Skip to content

Commit

Permalink
feat: useErdEditorAttachElement
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Nov 18, 2023
1 parent ad061ef commit 2e91739
Show file tree
Hide file tree
Showing 20 changed files with 504 additions and 107 deletions.
3 changes: 2 additions & 1 deletion packages/erd-editor/.storybook/ThemeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const ThemeProvider: FC<ThemeProviderProps> = props => () =>
.theme=${createTheme({
grayColor: props.grayColor,
accentColor: props.accentColor,
})(props.appearance)}
appearance: props.appearance,
})}
/>
${props.story}
`;
Expand Down
33 changes: 17 additions & 16 deletions packages/erd-editor/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import './preview.css';

import { kebabCase } from 'lodash-es';
import type { Preview } from '@storybook/html';
import { render, html, addCSSHost } from '@dineug/r-html';

Expand All @@ -10,39 +10,40 @@ import {
AccentColor,
} from '../src/themes/radix-ui-theme';

import { themeToTokensString } from '../src/themes/tokens';
import { themeToTokensString, Theme } from '../src/themes/tokens';
import ThemeProvider from './ThemeProvider';

const themeToGlobalTokensString = (theme: Theme) =>
Object.keys(theme)
.map(key => `--erd-editor-${kebabCase(key)}: ${Reflect.get(theme, key)};`)
.join('\n');

const withThemeProvider = (Story, context) => {
const fragment = document.createDocumentFragment();
const div = document.createElement('div');
const shadowRoot = div.attachShadow({ mode: 'open' });
const themeOptions = {
grayColor: context.globals.grayColor,
accentColor: context.globals.accentColor,
appearance: context.globals.appearance,
};

addCSSHost(shadowRoot);

render(
shadowRoot,
html`
<${ThemeProvider}
.story=${Story()}
.appearance=${context.globals.appearance}
.grayColor=${context.globals.grayColor}
.accentColor=${context.globals.accentColor}
/>
`
html`<${ThemeProvider} .story=${Story()} ...${themeOptions} />`
);

render(
fragment,
html`
<style>
${context.parameters.css}
:root {
${themeToTokensString(
createTheme({
grayColor: context.globals.grayColor,
accentColor: context.globals.accentColor,
})(context.globals.appearance)
)};
${themeToGlobalTokensString(createTheme(themeOptions))}
${themeToTokensString(createTheme(themeOptions))}
}
</style>
${div}
Expand Down
45 changes: 45 additions & 0 deletions packages/erd-editor/src/components/erd-editor/ErdEditor.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import './ErdEditor';

import { html, render } from '@dineug/r-html';
import type { Meta, StoryObj } from '@storybook/html';

import type { ErdEditorProps } from './ErdEditor';

const meta = {
title: 'ErdEditor',
render: args => {
const fragment = document.createDocumentFragment();
render(fragment, html`<erd-editor ...${args} />`);
return fragment;
},
argTypes: {
readonly: {
type: 'boolean',
},
},
parameters: {
css: `
body {
padding: 0 !important;
margin: 0 !important;
}
#storybook-root {
height: 100vh;
}
#storybook-root > div {
height: 100%;
}
`,
},
} satisfies Meta<ErdEditorProps>;

export default meta;
type Story = StoryObj<ErdEditorProps>;

export const Normal: Story = {
args: {
readonly: false,
},
};
82 changes: 52 additions & 30 deletions packages/erd-editor/src/components/erd-editor/ErdEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,67 @@ import {
defineCustomElement,
FC,
html,
observable,
onMounted,
ref,
useProvider,
} from '@dineug/r-html';
import { fromEvent, merge, throttleTime } from 'rxjs';
import { fromEvent, throttleTime } from 'rxjs';

import { appContext, createAppContext } from '@/components/appContext';
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 { DatabaseVendor } from '@/constants/database';
import { TOOLBAR_HEIGHT } from '@/constants/layout';
import { changeViewportAction } from '@/engine/modules/editor/atom.actions';
import { useKeyBindingMap } from '@/hooks/useKeyBindingMap';
import { useUnmounted } from '@/hooks/useUnmounted';
import { AccentColor, createTheme, GrayColor } from '@/themes/radix-ui-theme';
import { ThemeOptions } from '@/themes/radix-ui-theme';
import { Theme as ThemeType } from '@/themes/tokens';
import { focusEvent, forceFocusEvent } from '@/utils/internalEvents';
import { KeyBindingMap, KeyBindingName } from '@/utils/keyboard-shortcut';
import { createText } from '@/utils/text';

import * as styles from './ErdEditor.styles';
import { useErdEditorAttachElement } from './useErdEditorAttachElement';

declare global {
interface HTMLElementTagNameMap {
'erd-editor': ErdEditorElement;
}
}

export type ErdEditorProps = {};
export type ErdEditorProps = {
readonly: boolean;
systemDarkMode: boolean;
};

export interface ErdEditorElement extends ErdEditorProps, HTMLElement {}
export interface ErdEditorElement extends ErdEditorProps, HTMLElement {
value: string;
focus: () => void;
blur: () => void;
clear: () => void;
setInitialValue: (value: string) => void;
setPresetTheme: (themeOptions: Partial<ThemeOptions>) => void;
setTheme: (theme: Partial<ThemeType>) => void;
setKeyBindingMap: (
keyBindingMap: Partial<
Omit<
KeyBindingMap,
| typeof KeyBindingName.edit
| typeof KeyBindingName.stop
| typeof KeyBindingName.find
| typeof KeyBindingName.undo
| typeof KeyBindingName.redo
| typeof KeyBindingName.zoomIn
| typeof KeyBindingName.zoomOut
>
>
) => void;
setSchemaSQL: (value: string) => void;
getSchemaSQL: (databaseVendor?: DatabaseVendor) => string;
}

const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {
const text = createText();
Expand All @@ -43,52 +73,39 @@ const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {
const root = createRef<HTMLDivElement>();
useKeyBindingMap(ctx, root);

const { theme } = useErdEditorAttachElement({
props,
ctx,
app: appContextValue,
root,
});
const { store, keydown$ } = appContextValue;
const { addUnsubscribe } = useUnmounted();

const state = observable(
{
theme: createTheme({
grayColor: GrayColor.slate,
accentColor: AccentColor.indigo,
})('dark'),
},
{ shallow: true }
);

const handleKeydown = (event: KeyboardEvent) => {
const { keydown$ } = appContextValue;
keydown$.next(event);
};

const handleFocus = () => {
const $root = root.value;
setTimeout(() => {
if (document.activeElement !== ctx) {
$root.focus();
ctx.focus();
}
}, 1);
};

onMounted(() => {
const $root = root.value;
handleFocus();
ctx.focus();
addUnsubscribe(
merge(
fromEvent($root, 'mousedown'),
fromEvent($root, 'touchstart'),
fromEvent(ctx, focusEvent.type)
)
fromEvent(ctx, focusEvent.type)
.pipe(throttleTime(50))
.subscribe(handleFocus),
fromEvent(ctx, forceFocusEvent.type).subscribe(() => {
$root.focus();
})
fromEvent(ctx, forceFocusEvent.type).subscribe(ctx.focus)
);
});

onMounted(() => {
const $root = root.value;
const { store } = appContextValue;
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
Expand All @@ -106,7 +123,7 @@ const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {

return () => html`
<${GlobalStyles} />
<${Theme} .theme=${state.theme} />
<${Theme} .theme=${theme} />
<div
${ref(root)}
class=${styles.root}
Expand All @@ -123,5 +140,10 @@ const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {
};

defineCustomElement('erd-editor', {
shadow: 'closed',
observedProps: {
readonly: Boolean,
systemDarkMode: Boolean,
},
render: ErdEditor,
});
Loading

0 comments on commit 2e91739

Please sign in to comment.