mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-04-06 21:40:23 +08:00
render border
This commit is contained in:
parent
90cd805437
commit
5d182f9a0a
@ -55,6 +55,7 @@ export default implementRuntimeComponent({
|
||||
colorScheme,
|
||||
isLoading,
|
||||
customStyle,
|
||||
$onRef,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
@ -69,6 +70,12 @@ export default implementRuntimeComponent({
|
||||
});
|
||||
}, [subscribeMethods]);
|
||||
|
||||
useEffect(() => {
|
||||
if ($onRef && ref.current) {
|
||||
$onRef(ref.current);
|
||||
}
|
||||
}, [$onRef]);
|
||||
|
||||
return (
|
||||
<BaseButton
|
||||
className={css`
|
||||
|
@ -42,7 +42,7 @@ export default implementRuntimeComponent({
|
||||
methods: {},
|
||||
events: [],
|
||||
},
|
||||
})(({ direction, wrap, align, justify, spacing, slotsElements, customStyle }) => {
|
||||
})(({ direction, wrap, align, justify, spacing, slotsElements, customStyle, $ref }) => {
|
||||
return (
|
||||
<BaseHStack
|
||||
height="full"
|
||||
@ -55,6 +55,7 @@ export default implementRuntimeComponent({
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
ref={$ref}
|
||||
{...{ direction, wrap, align, justify, spacing }}
|
||||
>
|
||||
{slotsElements.content}
|
||||
|
@ -102,6 +102,7 @@ export default implementRuntimeComponent({
|
||||
subscribeMethods,
|
||||
defaultValue,
|
||||
customStyle,
|
||||
$ref,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(defaultValue || ''); // TODO: pin input
|
||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
@ -125,9 +126,9 @@ export default implementRuntimeComponent({
|
||||
},
|
||||
});
|
||||
}, [defaultValue, subscribeMethods]);
|
||||
|
||||
console.log('input', $ref);
|
||||
return (
|
||||
<InputGroup size={size} background="white">
|
||||
<InputGroup size={size} background="white" ref={$ref as any}>
|
||||
{left ? (
|
||||
left.type === 'addon' ? (
|
||||
<InputLeftAddon>{left.children}</InputLeftAddon>
|
||||
|
@ -42,7 +42,7 @@ export default implementRuntimeComponent({
|
||||
methods: {},
|
||||
events: [],
|
||||
},
|
||||
})(({ direction, wrap, align, justify, spacing, slotsElements, customStyle }) => {
|
||||
})(({ direction, wrap, align, justify, spacing, slotsElements, customStyle, $ref }) => {
|
||||
return (
|
||||
<BaseVStack
|
||||
width="full"
|
||||
@ -55,6 +55,7 @@ export default implementRuntimeComponent({
|
||||
className={css`
|
||||
${customStyle?.content}
|
||||
`}
|
||||
ref={$ref}
|
||||
{...{ direction, wrap, align, justify, spacing }}
|
||||
>
|
||||
{slotsElements.content}
|
||||
|
115
packages/editor/src/components/ComponentContainerMask.tsx
Normal file
115
packages/editor/src/components/ComponentContainerMask.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { EditorServices } from '../types';
|
||||
|
||||
const MaskWrapperStyle = css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
`;
|
||||
|
||||
const outlineMaskTextStyle = css`
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0px;
|
||||
padding: 0 4px;
|
||||
font-size: 14px;
|
||||
font-weight: black;
|
||||
color: white;
|
||||
&.hover {
|
||||
background-color: black;
|
||||
}
|
||||
&.select {
|
||||
background-color: red;
|
||||
}
|
||||
&.idle,
|
||||
&.drag {
|
||||
display: none;
|
||||
}
|
||||
&.top {
|
||||
top: -4px;
|
||||
transform: translateY(-100%);
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
}
|
||||
&.bottom {
|
||||
bottom: -4px;
|
||||
transform: translateY(100%);
|
||||
border-radius: 0px 0px 3px 3px;
|
||||
}
|
||||
`;
|
||||
|
||||
const outlineMaskStyle = css`
|
||||
position: absolute;
|
||||
border: 1px solid;
|
||||
pointer-events: none;
|
||||
/* create a bfc */
|
||||
transform: translate3d(0, 0, 0);
|
||||
z-index: 10;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
&.idle {
|
||||
display: none;
|
||||
}
|
||||
&.hover {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
&.select {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
&.drag {
|
||||
border-color: orange;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
services: EditorServices;
|
||||
eleMap: Map<string, HTMLElement>;
|
||||
};
|
||||
|
||||
export const ComponentContainerMask: React.FC<Props> = props => {
|
||||
const { eleMap } = props;
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
// const { hoverComponentId } = editorStore;'
|
||||
const rects = Array.from(eleMap.keys()).map(id => {
|
||||
const ele = eleMap.get(id);
|
||||
return {
|
||||
id,
|
||||
rect: ele?.getBoundingClientRect(),
|
||||
};
|
||||
});
|
||||
|
||||
const borders = useMemo(() => {
|
||||
if (!wrapperRef.current) {
|
||||
return null;
|
||||
}
|
||||
const wrapperRect = wrapperRef.current.getBoundingClientRect();
|
||||
return rects.map(({ id, rect }) => {
|
||||
console.log('rect', rect)
|
||||
console.log('wrapperRect', wrapperRect)
|
||||
const style = {
|
||||
top: (rect?.top || 0) - wrapperRect.top - 2,
|
||||
left: (rect?.left || 0) - wrapperRect.left - 2,
|
||||
height: (rect?.height || 0) + 4,
|
||||
width: (rect?.width || 0) + 4,
|
||||
};
|
||||
return (
|
||||
<div key={id} className={cx([outlineMaskStyle, 'hover'])} style={style}>
|
||||
<span className={cx([outlineMaskTextStyle, 'hover'])}>{id}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, [rects]);
|
||||
|
||||
return (
|
||||
<div className={MaskWrapperStyle} ref={wrapperRef}>
|
||||
{borders}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -12,7 +12,6 @@ import { StructureTree } from './StructureTree';
|
||||
import { ComponentList } from './ComponentsList';
|
||||
import { EditorHeader } from './EditorHeader';
|
||||
import { KeyboardEventWrapper } from './KeyboardEventWrapper';
|
||||
import { useComponentWrapper } from './ComponentWrapper';
|
||||
import { StateViewer, SchemaEditor } from './CodeEditor';
|
||||
import { Explorer } from './Explorer';
|
||||
import { genOperation } from '../operations';
|
||||
@ -21,18 +20,20 @@ import ErrorBoundary from './ErrorBoundary';
|
||||
import { PreviewModal } from './PreviewModal';
|
||||
import { WarningArea } from './WarningArea';
|
||||
import { EditorServices } from '../types';
|
||||
import { ComponentContainerMask } from './ComponentContainerMask';
|
||||
|
||||
type ReturnOfInit = ReturnType<typeof initSunmaoUI>;
|
||||
|
||||
type Props = {
|
||||
App: ReturnOfInit['App'];
|
||||
eleMap: ReturnOfInit['eleMap'];
|
||||
registry: ReturnOfInit['registry'];
|
||||
stateStore: ReturnOfInit['stateManager']['store'];
|
||||
services: EditorServices;
|
||||
};
|
||||
|
||||
export const Editor: React.FC<Props> = observer(
|
||||
({ App, registry, stateStore, services }) => {
|
||||
({ App, registry, stateStore, eleMap, services }) => {
|
||||
const { eventBus, editorStore } = services;
|
||||
const {
|
||||
components,
|
||||
@ -52,6 +53,10 @@ export const Editor: React.FC<Props> = observer(
|
||||
const [isError, setIsError] = useState<boolean>(false);
|
||||
const [store, setStore] = useState(stateStore);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('eleMap', eleMap);
|
||||
}, [eleMap]);
|
||||
|
||||
useEffect(() => {
|
||||
watch(store, newValue => {
|
||||
setStore(JSON.parse(JSON.stringify(newValue)));
|
||||
@ -103,9 +108,9 @@ export const Editor: React.FC<Props> = observer(
|
||||
};
|
||||
}, [components]);
|
||||
|
||||
const ComponentWrapper = useMemo(() => {
|
||||
return useComponentWrapper(services);
|
||||
}, [services]);
|
||||
// const ComponentWrapper = useMemo(() => {
|
||||
// return useComponentWrapper(services);
|
||||
// }, [services]);
|
||||
|
||||
const appComponent = useMemo(() => {
|
||||
return (
|
||||
@ -115,11 +120,11 @@ export const Editor: React.FC<Props> = observer(
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
gridCallbacks={gridCallbacks}
|
||||
componentWrapper={ComponentWrapper}
|
||||
// componentWrapper={ComponentWrapper}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}, [App, ComponentWrapper, app, gridCallbacks, recoverKey]);
|
||||
}, [App, app, gridCallbacks, recoverKey]);
|
||||
|
||||
const renderMain = () => {
|
||||
const appBox = (
|
||||
@ -138,8 +143,9 @@ export const Editor: React.FC<Props> = observer(
|
||||
height="full"
|
||||
position="absolute"
|
||||
/>
|
||||
<Box width="full" overflow="auto">
|
||||
<Box width="full" overflow="auto" position="relative">
|
||||
{appComponent}
|
||||
<ComponentContainerMask services={services} eleMap={eleMap} />
|
||||
</Box>
|
||||
<WarningArea services={services} />
|
||||
</Box>
|
||||
|
@ -75,6 +75,7 @@ export function initSunmaoUIEditor(props: SunmaoUIEditorProps = {}) {
|
||||
<ChakraProvider theme={editorTheme}>
|
||||
<_Editor
|
||||
App={App}
|
||||
eleMap={ui.eleMap}
|
||||
registry={registry}
|
||||
stateStore={stateManager.store}
|
||||
services={services}
|
||||
|
@ -25,7 +25,7 @@ export const App: React.FC<AppProps> = props => {
|
||||
const runtimeAppSchemaManager = useRef(new RuntimeAppSchemaManager());
|
||||
const app = runtimeAppSchemaManager.current.update(options);
|
||||
initStateAndMethod(services.registry, services.stateManager, app.spec.components);
|
||||
|
||||
|
||||
const { childrenMap, topLevelComponents } = resolveChildrenMap(app.spec.components);
|
||||
return (
|
||||
<div className="App" style={{ height: '100vh', overflow: 'auto' }}>
|
||||
|
@ -14,7 +14,7 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
services,
|
||||
childrenMap,
|
||||
} = props;
|
||||
const { registry, stateManager, globalHandlerMap, apiService } = props.services;
|
||||
const { registry, stateManager, globalHandlerMap, apiService, eleMap } = props.services;
|
||||
const childrenCache = new Map<RuntimeComponentSchema, React.ReactElement>();
|
||||
|
||||
const Impl = registry.getComponent(c.parsedType.version, c.parsedType.name).impl;
|
||||
@ -24,6 +24,23 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
}
|
||||
|
||||
const handlerMap = useRef(globalHandlerMap.get(c.id)!);
|
||||
const eleRef = useRef<HTMLElement>();
|
||||
const onRef = (ele: HTMLElement) => {
|
||||
console.log('onRef', ele);
|
||||
eleMap.set(c.id, ele);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (eleRef.current) {
|
||||
eleMap.set(c.id, eleRef.current);
|
||||
console.log('挂载了', c.id, eleRef);
|
||||
}
|
||||
return () => {
|
||||
console.log('卸载了', c.id);
|
||||
eleMap.delete(c.id);
|
||||
};
|
||||
}, [c.id, eleMap]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (s: { componentId: string; name: string; parameters?: any }) => {
|
||||
if (s.componentId !== c.id) {
|
||||
@ -164,7 +181,6 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const C = unmount ? null : (
|
||||
<Impl
|
||||
key={c.id}
|
||||
@ -173,6 +189,8 @@ const _ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>((props,
|
||||
slotsElements={genSlotsElements()}
|
||||
mergeState={mergeState}
|
||||
subscribeMethods={subscribeMethods}
|
||||
$ref={eleRef}
|
||||
$onRef={onRef}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -15,13 +15,15 @@ export function initSunmaoUI(props: SunmaoUIRuntimeProps = {}) {
|
||||
const globalHandlerMap = initGlobalHandlerMap();
|
||||
const apiService = initApiService();
|
||||
const registry = initRegistry(apiService);
|
||||
const eleMap = new Map<string, HTMLElement>();
|
||||
|
||||
return {
|
||||
App: genApp({ registry, stateManager, globalHandlerMap, apiService }),
|
||||
App: genApp({ registry, stateManager, globalHandlerMap, apiService, eleMap }),
|
||||
stateManager,
|
||||
registry,
|
||||
globalHandlerMap,
|
||||
apiService,
|
||||
eleMap,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ export type UIServices = {
|
||||
stateManager: StateManager;
|
||||
globalHandlerMap: GlobalHandlerMap;
|
||||
apiService: ApiService;
|
||||
eleMap: Map<string, HTMLElement>;
|
||||
};
|
||||
|
||||
export type ComponentWrapperProps = {
|
||||
|
@ -25,6 +25,8 @@ export type ComponentImplProps<
|
||||
TraitResult<KStyleSlot, KEvent>['props'] &
|
||||
RuntimeFunctions<TState, TMethods> & {
|
||||
slotsElements: Record<KSlot, React.ReactElement[] | React.ReactElement>;
|
||||
$ref?: React.Ref<any>;
|
||||
$onRef?: (ele: HTMLElement) => void;
|
||||
};
|
||||
|
||||
export type ComponentImpl<
|
||||
|
Loading…
x
Reference in New Issue
Block a user