Skip to content

Commit

Permalink
feat: minimap
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Nov 10, 2023
1 parent 4943267 commit e00cbcc
Show file tree
Hide file tree
Showing 21 changed files with 469 additions and 81 deletions.
26 changes: 2 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,12 @@

> Entity-Relationship Diagram Editor
[![npm version](https://img.shields.io/npm/v/vuerd.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/vuerd) [![VS Marketplace version](https://vsmarketplacebadge.apphb.com/version-short/dineug.vuerd-vscode.svg?style=flat-square&color=blue&logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=dineug.vuerd-vscode) [![APM](https://img.shields.io/apm/v/vuerd-atom?color=blue&style=flat-square&logo=atom)](https://atom.io/packages/vuerd-atom) [![GitHub](https://img.shields.io/github/license/vuerd/vuerd?style=flat-square&color=blue)](https://github.com/vuerd/vuerd/blob/master/LICENSE) [![PRs](https://img.shields.io/badge/PRs-welcome-blue?style=flat-square)](https://github.com/vuerd/vuerd/pulls) [![CI](https://img.shields.io/github/workflow/status/vuerd/vuerd/CI?label=CI&logo=github&style=flat-square)](https://github.com/vuerd/vuerd/actions)

## ERD

![vuerd](https://github.com/vuerd/vuerd/blob/master/img/vuerd-erd.gif?raw=true)

## Packages

| Package | Version |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [vuerd](https://github.com/vuerd/vuerd/tree/master/packages/vuerd) | [![npm version](https://img.shields.io/npm/v/vuerd.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/vuerd) |
| [@vuerd/sql-ddl-parser](https://github.com/vuerd/vuerd/tree/master/packages/sql-ddl-parser) | [![npm version](https://img.shields.io/npm/v/@vuerd/sql-ddl-parser.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/@vuerd/sql-ddl-parser) |
| [@vuerd/lit-observable](https://github.com/vuerd/vuerd/tree/master/packages/lit-observable) | [![npm version](https://img.shields.io/npm/v/@vuerd/lit-observable.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/@vuerd/lit-observable) |
| [@vuerd/plugin-generate-template](https://github.com/vuerd/vuerd/tree/master/packages/plugin-generate-template) | [![npm version](https://img.shields.io/npm/v/@vuerd/plugin-generate-template.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/@vuerd/plugin-generate-template) |
| [vuerd-app](https://github.com/vuerd/vuerd/tree/master/packages/vuerd-app) | |
| [vuerd-vscode](https://github.com/vuerd/vuerd/tree/master/packages/vuerd-vscode) | [![VS Marketplace version](https://vsmarketplacebadge.apphb.com/version-short/dineug.vuerd-vscode.svg?style=flat-square&color=blue&logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=dineug.vuerd-vscode) |
| [vuerd-atom](https://github.com/vuerd/vuerd-atom) | [![APM](https://img.shields.io/apm/v/vuerd-atom?color=blue&style=flat-square&logo=atom)](https://atom.io/packages/vuerd-atom) |
![vuerd](https://github.com/dineug/erd-editor/blob/main/img/vuerd-erd.gif?raw=true)

## Document

- [Playground](https://vuerd.github.io)
- [Import SQL DDL support syntax](https://github.com/vuerd/vuerd/blob/master/packages/sql-ddl-parser/src/SQL_DDL_Test_Case.md)
- [Import SQL DDL support syntax](https://github.com/dineug/erd-editor/blob/master/packages/sql-ddl-parser/src/SQL_DDL_Test_Case.md)
- [vscode extension](https://marketplace.visualstudio.com/items?itemName=dineug.vuerd-vscode)
- [atom extension](https://atom.io/packages/vuerd-atom)

## Contributors

Thanks to all the people who contribute.
<a href="https://github.com/vuerd/vuerd/graphs/contributors">
<img src="https://contrib.rocks/image?repo=vuerd/vuerd" />
</a>
3 changes: 1 addition & 2 deletions packages/erd-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,12 @@
"@storybook/html": "^7.4.0",
"@storybook/testing-library": "^0.2.0",
"@types/color": "^3.0.3",
"@types/dom-to-image": "^2.6.5",
"@types/lodash-es": "^4.17.8",
"@types/luxon": "^3.3.1",
"@types/ua-parser-js": "^0.7.37",
"@types/uuid": "^9.0.2",
"color": "^4.2.3",
"dom-to-image": "^2.6.0",
"html-to-image": "^1.11.11",
"lodash-es": "^4.17.21",
"luxon": "^3.3.0",
"rollup-plugin-visualizer": "^5.9.2",
Expand Down
5 changes: 4 additions & 1 deletion packages/erd-editor/src/components/erd-editor/ErdEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Erd from '@/components/erd/Erd';
import GlobalStyles from '@/components/global-styles/GlobalStyles';
import Theme from '@/components/theme/Theme';
import Toolbar from '@/components/toolbar/Toolbar';
import { TOOLBAR_HEIGHT } from '@/constants/layout';
import { changeViewportAction } from '@/engine/modules/editor/atom.actions';
import { useKeyBindingMap } from '@/hooks/useKeyBindingMap';
import { useUnmounted } from '@/hooks/useUnmounted';
Expand Down Expand Up @@ -88,7 +89,9 @@ const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
store.dispatch(changeViewportAction({ width, height }));
store.dispatch(
changeViewportAction({ width, height: height - TOOLBAR_HEIGHT })
);
}
});
resizeObserver.observe($root);
Expand Down
12 changes: 8 additions & 4 deletions packages/erd-editor/src/components/erd/Erd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import DragSelect from '@/components/erd/drag-select/DragSelect';
import ErdContextMenu, {
ErdContextMenuType,
} from '@/components/erd/erd-context-menu/ErdContextMenu';
import Minimap from '@/components/erd/minimap/Minimap';
import { useContextMenuRootProvider } from '@/components/primitives/context-menu/context-menu-root/contextMenuRootContext';
import { unselectAllAction$ } from '@/engine/modules/editor/generator.actions';
import {
streamScrollToAction,
streamZoomLevelAction,
} from '@/engine/modules/settings/atom.actions';
import { isMouseEvent } from '@/utils/domEvent';
import { drag$, DragMove } from '@/utils/globalEventObservable';
import { isMod } from '@/utils/keyboard-shortcut';

Expand All @@ -23,6 +25,7 @@ export type ErdProps = {};
const Erd: FC<ErdProps> = (props, ctx) => {
const contextMenu = useContextMenuRootProvider(ctx);
const root = createRef<HTMLDivElement>();
const canvas = createRef<HTMLDivElement>();
const app = useAppContext(ctx);
const state = observable({
dragSelect: false,
Expand Down Expand Up @@ -61,7 +64,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
resetScroll();
};

const handleDragSelect = (event: DragEvent) => {
const handleDragSelect = (event: MouseEvent | TouchEvent) => {
const el = event.target as HTMLElement | null;
if (!el) return;

Expand All @@ -74,7 +77,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
const { store } = app.value;
store.dispatch(unselectAllAction$());

if (event.type === 'mousedown' && isMod(event)) {
if (isMouseEvent(event) && isMod(event)) {
const { x, y } = root.value.getBoundingClientRect();
state.dragSelect = true;
state.dragSelectX = event.clientX - x;
Expand All @@ -99,7 +102,8 @@ const Erd: FC<ErdProps> = (props, ctx) => {
@touchstart=${handleDragSelect}
@wheel=${handleWheel}
>
<${Canvas} />
<${Canvas} canvas=${canvas} />
<${Minimap} />
${state.dragSelect
? html`
<${DragSelect}
Expand All @@ -112,7 +116,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
: null}
<${ErdContextMenu}
type=${ErdContextMenuType.ERD}
root=${root}
root=${canvas}
.onClose=${handleContextmenuClose}
/>
</div>
Expand Down
59 changes: 32 additions & 27 deletions packages/erd-editor/src/components/erd/canvas/Canvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cache, FC, html, repeat } from '@dineug/r-html';
import { cache, FC, html, Ref, ref, repeat } from '@dineug/r-html';

import { useAppContext } from '@/components/context';
import Memo from '@/components/erd/canvas/memo/Memo';
Expand All @@ -7,7 +7,9 @@ import { query } from '@/utils/collection/query';

import * as styles from './Canvas.styles';

export type CanvasProps = {};
export type CanvasProps = {
canvas: Ref<HTMLDivElement>;
};

const Canvas: FC<CanvasProps> = (props, ctx) => {
const app = useAppContext(ctx);
Expand All @@ -28,31 +30,34 @@ const Canvas: FC<CanvasProps> = (props, ctx) => {
.collection('memoEntities')
.selectByIds(memoIds);

return html`<div
class=${styles.root}
style=${{
width: `${width}px`,
height: `${height}px`,
'min-width': `${width}px`,
'min-height': `${height}px`,
transform: `translate(${scrollLeft}px, ${scrollTop}px) scale(${zoomLevel})`,
}}
>
${cache(
zoomLevel > 0.7
? html`${repeat(
tables,
table => table.id,
table => html`<${Table} table=${table} />`
)}`
: null
)}
${repeat(
memos,
memo => memo.id,
memo => html`<${Memo} memo=${memo} />`
)}
</div>`;
return html`
<div
class=${styles.root}
${ref(props.canvas)}
style=${{
width: `${width}px`,
height: `${height}px`,
'min-width': `${width}px`,
'min-height': `${height}px`,
transform: `translate(${scrollLeft}px, ${scrollTop}px) scale(${zoomLevel})`,
}}
>
${cache(
zoomLevel > 0.7
? html`${repeat(
tables,
table => table.id,
table => html`<${Table} table=${table} />`
)}`
: null
)}
${repeat(
memos,
memo => memo.id,
memo => html`<${Memo} memo=${memo} />`
)}
</div>
`;
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { exportJSON, exportPNG, exportSQLDDL } from '@/utils/file/exportFile';
export function createExportMenus(
app: AppContext,
onClose: () => void,
root: Element
root: HTMLElement
) {
const { store } = app;
const databaseName = store.state.settings.databaseName;
Expand Down
16 changes: 16 additions & 0 deletions packages/erd-editor/src/components/erd/minimap/Minimap.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { css } from '@dineug/r-html';

export const minimap = css`
position: absolute;
overflow: hidden;
background-color: var(--canvas-boundary-background);
`;

export const border = css`
position: absolute;
box-sizing: content-box;
pointer-events: none;
border: 1px solid var(--minimap-border);
box-shadow: 0 1px 6px var(--minimap-shadow);
background-color: transparent;
`;
144 changes: 144 additions & 0 deletions packages/erd-editor/src/components/erd/minimap/Minimap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { createRef, FC, html, ref, repeat } from '@dineug/r-html';

import { useAppContext } from '@/components/context';
import * as CanvasStyle from '@/components/erd/canvas/Canvas.styles';
import Memo from '@/components/erd/minimap/memo/Memo';
import Table from '@/components/erd/minimap/table/Table';
import Viewport from '@/components/erd/minimap/viewport/Viewport';
import { MINIMAP_MARGIN, MINIMAP_SIZE } from '@/constants/layout';
import { scrollToAction } from '@/engine/modules/settings/atom.actions';
import { query } from '@/utils/collection/query';
import { isMouseEvent } from '@/utils/domEvent';

import * as styles from './Minimap.styles';

const BORDER = 1;

export type MinimapProps = {};

const Minimap: FC<MinimapProps> = (props, ctx) => {
const app = useAppContext(ctx);
const minimap = createRef<HTMLDivElement>();

const getRatio = () => {
const { store } = app.value;
const {
settings: { width },
} = store.state;
return MINIMAP_SIZE / width;
};

const styleMap = () => {
const { store } = app.value;
const {
settings: { width, height },
} = store.state;
const ratio = getRatio();
const x = (-1 * width) / 2 + (width * ratio) / 2;
const y = (-1 * height) / 2 + (height * ratio) / 2;
const right = x + MINIMAP_MARGIN;
const top = y + MINIMAP_MARGIN;

return {
transform: `scale(${ratio}, ${ratio})`,
width: `${width}px`,
height: `${height}px`,
right: `${right}px`,
top: `${top}px`,
};
};

const borderStyleMap = () => {
const margin = MINIMAP_MARGIN - BORDER;
return {
width: `${MINIMAP_SIZE}px`,
height: `${MINIMAP_SIZE}px`,
right: `${margin}px`,
top: `${margin}px`,
};
};

const handleMove = (event: MouseEvent | TouchEvent) => {
event.stopPropagation();
const { store } = app.value;
const {
editor: { viewport },
} = store.state;
const ratio = getRatio();
const $minimap = minimap.value;
const rect = $minimap.getBoundingClientRect();
const clientX = isMouseEvent(event)
? event.clientX
: event.touches[0].clientX;
const clientY = isMouseEvent(event)
? event.clientY
: event.touches[0].clientY;

const x = clientX - rect.x;
const y = clientY - rect.y;
const absoluteX = x / ratio;
const absoluteY = y / ratio;
const scrollLeft = absoluteX - viewport.width / 2;
const scrollTop = absoluteY - viewport.height / 2;

store.dispatch(
scrollToAction({
scrollLeft: -1 * scrollLeft,
scrollTop: -1 * scrollTop,
})
);
};

return () => {
const { store } = app.value;
const {
settings: { width, height, zoomLevel, show },
doc: { tableIds, memoIds },
collections,
} = store.state;

const tables = query(collections)
.collection('tableEntities')
.selectByIds(tableIds);

const memos = query(collections)
.collection('memoEntities')
.selectByIds(memoIds);

return html`
<div
class=${styles.minimap}
style=${styleMap()}
${ref(minimap)}
@mousedown=${handleMove}
@touchstart=${handleMove}
>
<div
class=${CanvasStyle.root}
style=${{
width: `${width}px`,
height: `${height}px`,
'min-width': `${width}px`,
'min-height': `${height}px`,
transform: `scale(${zoomLevel})`,
}}
>
${repeat(
tables,
table => table.id,
table => html`<${Table} table=${table} />`
)}
${repeat(
memos,
memo => memo.id,
memo => html`<${Memo} memo=${memo} />`
)}
</div>
</div>
<div class=${styles.border} style=${borderStyleMap()}></div>
<${Viewport} />
`;
};
};

export default Minimap;
Loading

0 comments on commit e00cbcc

Please sign in to comment.