Merge pull request #320 from webzard-io/feat/dialog-mask

EditorMask support components in dialog
This commit is contained in:
yz-yu 2022-03-02 10:36:57 +08:00 committed by GitHub
commit bb6aba3f83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 55 additions and 10 deletions

View File

@ -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>

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -50,6 +50,7 @@ export const App: React.FC<AppProps> = props => {
childrenMap={childrenMap}
app={app}
gridCallbacks={gridCallbacks}
hooks={hooks}
/>
);
})}

View File

@ -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(() => {

View File

@ -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;
};