Skip to content

Commit

Permalink
feat: visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Nov 25, 2023
1 parent c65cee2 commit 20d8d2c
Show file tree
Hide file tree
Showing 9 changed files with 648 additions and 26 deletions.
8 changes: 7 additions & 1 deletion packages/erd-editor/src/components/erd/Erd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
changeColorAllAction$,
unselectAllAction$,
} from '@/engine/modules/editor/generator.actions';
import { Viewport } from '@/engine/modules/editor/state';
import {
streamScrollToAction,
streamZoomLevelAction,
Expand Down Expand Up @@ -51,6 +52,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
colorPickerShow: false,
colorPickerX: 0,
colorPickerY: 0,
colorPickerViewport: null as Viewport | null,
colorPickerInitialColor: '',
});
useErdShortcut(ctx);
Expand Down Expand Up @@ -157,15 +159,18 @@ const Erd: FC<ErdProps> = (props, ctx) => {
};

onMounted(() => {
const { emitter } = app.value;
const { store, emitter } = app.value;
const $root = root.value;

addUnsubscribe(
emitter.on({
openColorPicker: ({ payload: { x, y, color } }) => {
const { editor } = store.state;
const rect = $root.getBoundingClientRect();

state.colorPickerX = x - rect.x;
state.colorPickerY = y - rect.y;
state.colorPickerViewport = editor.viewport;
state.colorPickerInitialColor = color;
state.colorPickerShow = true;
},
Expand Down Expand Up @@ -224,6 +229,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
color=${state.colorPickerInitialColor}
x=${state.colorPickerX}
y=${state.colorPickerY}
viewport=${state.colorPickerViewport}
.onChange=${handleChangeColorPicker}
/>
`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { createRef, FC, html, onMounted, ref } from '@dineug/r-html';
import {
createRef,
FC,
html,
observable,
onMounted,
ref,
} from '@dineug/r-html';
// @ts-ignore
import ColorPickerUI from '@easylogic/colorpicker';

import { Viewport } from '@/engine/modules/editor/state';
import { useUnmounted } from '@/hooks/useUnmounted';

import * as styles from './ColorPicker.styles';
Expand All @@ -10,13 +18,18 @@ export type ColorPickerProps = {
x: number;
y: number;
color: string;
viewport?: Viewport;
onChange?: (color: string) => void;
onLastUpdate?: (color: string) => void;
};

const ColorPicker: FC<ColorPickerProps> = (props, ctx) => {
const container = createRef<HTMLDivElement>();
const { addUnsubscribe } = useUnmounted();
const state = observable({
x: props.x,
y: props.y,
});

onMounted(() => {
const $container = container.value;
Expand All @@ -33,23 +46,41 @@ const ColorPicker: FC<ColorPickerProps> = (props, ctx) => {
},
});

if (props.viewport) {
const rect = $container.getBoundingClientRect();
const width = props.x + rect.width;
const height = props.y + rect.height;

if (props.viewport.width < width) {
const x = props.viewport.width - rect.width;
if (0 <= x) {
state.x = x;
}
}
if (props.viewport.height < height) {
const y = props.viewport.height - rect.height;
if (0 <= y) {
state.y = y;
}
}
}

addUnsubscribe(() => {
colorPicker.destroy();
$container.removeChild(colorPicker.$root.el);
});
});

return () =>
html`
<div
class=${['color-picker', styles.container]}
style=${{
top: `${props.y}px`,
left: `${props.x}px`,
}}
${ref(container)}
></div>
`;
return () => html`
<div
class=${['color-picker', styles.container]}
style=${{
top: `${state.y}px`,
left: `${state.x}px`,
}}
${ref(container)}
></div>
`;
};

export default ColorPicker;
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
import { css } from '@dineug/r-html';

export const root = css`
position: relative;
height: 100%;
overflow: auto;
background-color: var(--canvas-background);
`;
129 changes: 116 additions & 13 deletions packages/erd-editor/src/components/visualization/Visualization.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,125 @@
import { FC, html } from '@dineug/r-html';
import {
create,
drag,
forceLink,
forceManyBody,
forceSimulation,
forceX,
forceY,
scaleOrdinal,
schemeCategory10,
} from 'd3';
import { FC, html, observable, onBeforeMount, watch } from '@dineug/r-html';

import { useAppContext } from '@/components/appContext';
import Table from '@/components/visualization/table/Table';
import { useUnmounted } from '@/hooks/useUnmounted';
import { Table as TableType } from '@/internal-types';
import { query } from '@/utils/collection/query';

import { createVisualization } from './createVisualization';
import * as styles from './Visualization.styles';

const HEIGHT = 1200;
const MARGIN = 20;

export type VisualizationProps = {};

type VisualizationState = {
preview: boolean;
drag: boolean;
table?: TableType | null;
columnId: string | null;
x: number;
y: number;
};

const Visualization: FC<VisualizationProps> = (props, ctx) => {
return () => html``;
const app = useAppContext(ctx);
const { addUnsubscribe } = useUnmounted();
const state = observable<VisualizationState>({
preview: false,
drag: false,
table: null,
columnId: null,
x: 0,
y: 0,
});

let d3SVG: ReturnType<typeof createVisualization> | null = null;

const setViewBox = () => {
const { store } = app.value;
const {
editor: { viewport },
} = store.state;

d3SVG?.attr('viewBox', [
-viewport.width / 2,
-HEIGHT / 2,
viewport.width,
HEIGHT,
]);
};

onBeforeMount(() => {
const { store } = app.value;
const { editor } = store.state;

d3SVG = createVisualization(store.state, {
onDragStart: () => {
state.drag = true;
},
onDragEnd: () => {
state.drag = false;
},
onStartPreview: (
event: MouseEvent,
tableId: string | null,
columnId: string | null
) => {
if (!tableId) return;

const { store } = app.value;
const { collections } = store.state;
const table = query(collections)
.collection('tableEntities')
.selectById(tableId);
if (!table) return;

state.columnId = columnId;
state.table = table;
state.x = event.clientX;
state.y = event.clientY;
state.preview = true;
},
onEndPreview: () => {
state.preview = false;
},
});

setViewBox();

addUnsubscribe(
watch(editor.viewport).subscribe(propName => {
if (propName !== 'width') return;
setViewBox();
}),
() => {
d3SVG?.remove();
d3SVG = null;
}
);
});

return () => {
const showPreview = state.table && !state.drag && state.preview;

return html`
<div class=${['scrollbar', styles.root]}>
${d3SVG?.node()}
${showPreview
? html`
<${Table}
table=${state.table}
columnId=${state.columnId}
x=${state.x + MARGIN}
y=${state.y}
/>
`
: null}
</div>
`;
};
};

export default Visualization;
Loading

0 comments on commit 20d8d2c

Please sign in to comment.