mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-03-13 18:16:49 +08:00
Merge pull request #320 from webzard-io/feat/dialog-mask
EditorMask support components in dialog
This commit is contained in:
commit
bb6aba3f83
@ -96,17 +96,22 @@ export default implementRuntimeComponent({
|
||||
colorScheme: 'blue',
|
||||
},
|
||||
customStyle,
|
||||
elementRef,
|
||||
getElement,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [title, setTitle] = useState(customerTitle || '');
|
||||
const cancelRef = useRef(null);
|
||||
const contentRef = useRef<HTMLDivElement | null>(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
containerRef.current = document.getElementById(DIALOG_CONTAINER_ID);
|
||||
}, [containerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
getElement && contentRef.current && getElement(contentRef.current);
|
||||
}, [getElement, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeMethods({
|
||||
openDialog({ title }) {
|
||||
@ -152,7 +157,7 @@ export default implementRuntimeComponent({
|
||||
<AlertDialogContent
|
||||
className={`${customStyle?.content}`}
|
||||
{...(containerRef.current ? dialogContentProps : {})}
|
||||
ref={elementRef}
|
||||
ref={contentRef}
|
||||
>
|
||||
<AlertDialogHeader>{title}</AlertDialogHeader>
|
||||
<AlertDialogBody>{slotsElements.content}</AlertDialogBody>
|
||||
|
@ -5,6 +5,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { DropSlotMask } from './DropSlotMask';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { Box, Text } from '@chakra-ui/react';
|
||||
import { DIALOG_CONTAINER_ID } from '@sunmao-ui/runtime';
|
||||
|
||||
const outlineMaskTextStyle = css`
|
||||
position: absolute;
|
||||
@ -48,17 +49,40 @@ export const EditorMask: React.FC<Props> = observer((props: Props) => {
|
||||
const maskContainerRect = useRef<DOMRect>();
|
||||
const [coordinates, setCoordinates] = useState<Record<string, DOMRect>>({});
|
||||
const [coordinatesOffset, setCoordinatedOffset] = useState<[number, number]>([0, 0]);
|
||||
|
||||
// establish the coordinateSystem by getting all the rect of elements,
|
||||
// and recording the current scroll Offset
|
||||
// and the updating maskContainerRect, because maskContainer shares the same coordinates with app
|
||||
const updateCoordinateSystem = useCallback(
|
||||
(eleMap: Map<string, HTMLElement>) => {
|
||||
function isChild(child: HTMLElement, parent: HTMLElement) {
|
||||
let curr = child;
|
||||
while (curr.parentElement && !curr.parentElement.isSameNode(wrapperRef.current)) {
|
||||
if (curr.parentElement.isSameNode(parent)) {
|
||||
return true;
|
||||
}
|
||||
curr = curr.parentElement;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wrapperRef.current) return;
|
||||
const _rects: Record<string, DOMRect> = {};
|
||||
const modalContainerEle = document.getElementById(DIALOG_CONTAINER_ID)!;
|
||||
const modalEleMap = new Map<string, HTMLElement>();
|
||||
// detect if there are components in modal
|
||||
for (const id of eleMap.keys()) {
|
||||
const ele = eleMap.get(id);
|
||||
const rect = ele?.getBoundingClientRect();
|
||||
if (!rect) continue;
|
||||
const ele = eleMap.get(id)!;
|
||||
if (isChild(ele, modalContainerEle)) {
|
||||
modalEleMap.set(id, ele);
|
||||
}
|
||||
}
|
||||
|
||||
const foregroundEleMap = modalEleMap.size > 0 ? modalEleMap : eleMap;
|
||||
|
||||
for (const id of foregroundEleMap.keys()) {
|
||||
const ele = eleMap.get(id)!;
|
||||
const rect = ele.getBoundingClientRect();
|
||||
_rects[id] = rect;
|
||||
}
|
||||
maskContainerRect.current = maskContainerRef.current?.getBoundingClientRect();
|
||||
@ -187,6 +211,7 @@ export const EditorMask: React.FC<Props> = observer((props: Props) => {
|
||||
right="0"
|
||||
bottom="0"
|
||||
pointerEvents="none"
|
||||
zIndex="99999"
|
||||
ref={maskContainerRef}
|
||||
>
|
||||
{isDraggingNewComponent ? dragMask : hoverMask}
|
||||
|
@ -76,9 +76,10 @@ export const EditorMaskWrapper: React.FC<Props> = observer(props => {
|
||||
flex="1"
|
||||
overflow="auto"
|
||||
position="relative"
|
||||
// some components stop click event propagation, so here we should capture onClick
|
||||
onClickCapture={onClick}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onClick}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
onScroll={onScroll}
|
||||
|
@ -68,6 +68,7 @@ export const ComponentTree: React.FC<Props> = props => {
|
||||
isExpanded={isExpanded}
|
||||
isDropInOnly
|
||||
droppable={!isAncestorDragging && !isDragging}
|
||||
hasSlot={true}
|
||||
>
|
||||
<Text fontSize="sm" color="gray.500">
|
||||
Empty
|
||||
@ -120,6 +121,7 @@ export const ComponentTree: React.FC<Props> = props => {
|
||||
services={props.services}
|
||||
isExpanded={isExpanded}
|
||||
droppable={!isAncestorDragging && !isDragging}
|
||||
hasSlot={slots.length > 0}
|
||||
>
|
||||
<ComponentItemView
|
||||
id={component.id}
|
||||
|
@ -12,6 +12,7 @@ type Props = {
|
||||
isDropInOnly?: boolean;
|
||||
droppable: boolean;
|
||||
services: EditorServices;
|
||||
hasSlot: boolean;
|
||||
};
|
||||
|
||||
export const DropComponentWrapper: React.FC<Props> = props => {
|
||||
@ -23,6 +24,7 @@ export const DropComponentWrapper: React.FC<Props> = props => {
|
||||
isDropInOnly,
|
||||
isExpanded,
|
||||
droppable,
|
||||
hasSlot,
|
||||
} = props;
|
||||
const { registry, eventBus } = services;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@ -70,7 +72,7 @@ export const DropComponentWrapper: React.FC<Props> = props => {
|
||||
let targetParentId = parentId;
|
||||
let targetParentSlot = parentSlot;
|
||||
let targetId = componentId;
|
||||
if (dragDirection === 'next' && isExpanded) {
|
||||
if (dragDirection === 'next' && isExpanded && hasSlot) {
|
||||
targetParentId = componentId;
|
||||
targetParentSlot = 'content';
|
||||
targetId = undefined;
|
||||
|
@ -65,6 +65,7 @@ function Placeholder(props: { services: EditorServices }) {
|
||||
isDropInOnly
|
||||
isExpanded={false}
|
||||
droppable
|
||||
hasSlot={true}
|
||||
>
|
||||
<Text padding="2" border="2px dashed" color="gray.400" borderColor="gray.400">
|
||||
There is no components now. You can drag component into here to create it.
|
||||
|
@ -50,8 +50,11 @@ export function initSunmaoUIEditor(props: SunmaoUIEditorProps = {}) {
|
||||
const didUpdate = () => {
|
||||
eventBus.send('HTMLElementsUpdated');
|
||||
};
|
||||
const didDomUpdate = () => {
|
||||
eventBus.send('HTMLElementsUpdated');
|
||||
};
|
||||
|
||||
const ui = initSunmaoUI({ ...props.runtimeProps, hooks: { didMount, didUpdate } });
|
||||
const ui = initSunmaoUI({ ...props.runtimeProps, hooks: { didMount, didUpdate, didDomUpdate } });
|
||||
|
||||
const App = ui.App;
|
||||
const registry = ui.registry;
|
||||
|
@ -50,6 +50,7 @@ export const App: React.FC<AppProps> = props => {
|
||||
childrenMap={childrenMap}
|
||||
app={app}
|
||||
gridCallbacks={gridCallbacks}
|
||||
hooks={hooks}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -6,7 +6,7 @@ import { ImplWrapperProps, TraitResult } from '../../types';
|
||||
import { shallowCompareArray } from '../../utils/shallowCompareArray';
|
||||
|
||||
const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props, ref) => {
|
||||
const { component: c, app, children, services, childrenMap } = props;
|
||||
const { component: c, app, children, services, childrenMap, hooks } = props;
|
||||
const { registry, stateManager, globalHandlerMap, apiService, eleMap } = props.services;
|
||||
const childrenCache = new Map<RuntimeComponentSchema, React.ReactElement>();
|
||||
|
||||
@ -20,6 +20,7 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
const eleRef = useRef<HTMLElement>();
|
||||
const onRef = (ele: HTMLElement) => {
|
||||
eleMap.set(c.id, ele);
|
||||
hooks?.didDomUpdate && hooks?.didDomUpdate();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -20,6 +20,7 @@ export type GridCallbacks = {
|
||||
|
||||
export type ComponentParamsFromApp = {
|
||||
gridCallbacks?: GridCallbacks;
|
||||
hooks?: AppHooks;
|
||||
};
|
||||
|
||||
export type AppProps = {
|
||||
@ -27,10 +28,13 @@ export type AppProps = {
|
||||
services: UIServices;
|
||||
debugStore?: boolean;
|
||||
debugEvent?: boolean;
|
||||
hooks?: AppHooks;
|
||||
} & ComponentParamsFromApp;
|
||||
|
||||
export type AppHooks = {
|
||||
// after app first render
|
||||
didMount?: () => void;
|
||||
// app updates after schema changes
|
||||
didUpdate?: () => void;
|
||||
// app updates after dom change(dose not include updates from schema change)
|
||||
didDomUpdate?: () => void;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user