Skip to content

Commit

Permalink
feat: keyboard focus
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Oct 29, 2023
1 parent 8311186 commit 6017ccc
Show file tree
Hide file tree
Showing 14 changed files with 668 additions and 30 deletions.
25 changes: 25 additions & 0 deletions packages/erd-editor/src/components/erd-editor/ErdEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ref,
useProvider,
} from '@dineug/r-html';
import { fromEvent, merge, throttleTime } from 'rxjs';

import { appContext, createAppContext } from '@/components/context';
import Erd from '@/components/erd/Erd';
Expand All @@ -18,6 +19,7 @@ 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 { InternalEventType } from '@/utils/internalEvents';
import { createText } from '@/utils/text';

import * as styles from './ErdEditor.styles';
Expand Down Expand Up @@ -57,6 +59,29 @@ const ErdEditor: FC<ErdEditorProps, ErdEditorElement> = (props, ctx) => {
keydown$.next(event);
};

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

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

onMounted(() => {
const $root = root.value;
const { store } = appContextValue;
Expand Down
5 changes: 4 additions & 1 deletion packages/erd-editor/src/components/erd/Erd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ const Erd: FC<ErdProps> = (props, ctx) => {
const el = event.target as HTMLElement | null;
if (!el) return;

const canDrag = !el.closest('.table') && !el.closest('.memo');
const canDrag =
!el.closest('.table') &&
!el.closest('.memo') &&
!el.closest('.edit-input');
if (!canDrag) return;

const { store } = app.value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const root = css`
fill: transparent;
color: transparent;
padding: 0 ${TABLE_PADDING}px;
transition: background-color 0.15s;
&:hover {
fill: var(--foreground);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,6 @@ const Column: FC<ColumnProps> = (props, ctx) => {
title=${simpleShortcutToString(
keyBindingMap.removeColumn[0]?.shortcut
)}
useTransition=${true}
.onClick=${handleRemoveColumn}
/>
</div>
Expand Down
79 changes: 74 additions & 5 deletions packages/erd-editor/src/components/erd/useErdShortcut.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { onMounted } from '@dineug/r-html';
import { nextTick, onMounted } from '@dineug/r-html';

import { useAppContext } from '@/components/context';
import { selectAllAction } from '@/engine/modules/editor/atom.actions';
import { removeSelectedAction$ } from '@/engine/modules/editor/generator.actions';
import {
editTableAction,
editTableEndAction,
focusMoveTableAction,
selectAllAction,
} from '@/engine/modules/editor/atom.actions';
import {
focusMoveTableAction$,
removeSelectedAction$,
} from '@/engine/modules/editor/generator.actions';
import { hasMoveKeys, MoveKey } from '@/engine/modules/editor/state';
import { addMemoAction$ } from '@/engine/modules/memo/generator.actions';
import { streamZoomLevelAction } from '@/engine/modules/settings/atom.actions';
import { addTableAction$ } from '@/engine/modules/table/generator.actions';
import { addColumnAction$ } from '@/engine/modules/tableColumn/generator.actions';
import {
addColumnAction$,
isToggleColumnTypes,
toggleColumnValueAction$,
} from '@/engine/modules/tableColumn/generator.actions';
import { useUnmounted } from '@/hooks/useUnmounted';
import { KeyBindingName } from '@/utils/keyboard-shortcut';

Expand All @@ -15,10 +28,66 @@ export function useErdShortcut(ctx: Parameters<typeof useAppContext>[0]) {
const { addUnsubscribe } = useUnmounted();

onMounted(() => {
const { store, shortcut$ } = app.value;
const { store, shortcut$, keydown$ } = app.value;

addUnsubscribe(
keydown$.subscribe(event => {
const { editor } = store.state;

if (
editor.focusTable &&
!editor.focusTable.edit &&
event.key !== MoveKey.Tab &&
hasMoveKeys(event.key)
) {
store.dispatch(
focusMoveTableAction({
moveKey: event.key,
shiftKey: event.shiftKey,
})
);
}

if (editor.focusTable && event.key === MoveKey.Tab) {
event.preventDefault();
store.dispatch(focusMoveTableAction$(event.key, event.shiftKey));

nextTick(() => {
if (
!editor.focusTable ||
isToggleColumnTypes(editor.focusTable.focusType)
) {
return;
}

store.dispatch(editTableAction());
});
}
}),
shortcut$.subscribe(action => {
const { editor } = store.state;

if (editor.focusTable && action === KeyBindingName.edit) {
const focusTable = editor.focusTable;

if (focusTable.edit) {
store.dispatch(editTableEndAction());
} else if (
focusTable.columnId &&
isToggleColumnTypes(focusTable.focusType)
) {
store.dispatch(
toggleColumnValueAction$(
focusTable.focusType,
focusTable.tableId,
focusTable.columnId
)
);
} else {
store.dispatch(editTableAction());
}
}

switch (action) {
case KeyBindingName.edit:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createRef, FC, html, onBeforeMount, ref, watch } from '@dineug/r-html';

import { useUnmounted } from '@/hooks/useUnmounted';
import { lastCursorFocus } from '@/utils/focus';
import { focusEvent } from '@/utils/internalEvents';

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

Expand Down Expand Up @@ -31,6 +32,11 @@ const EditInput: FC<EditInputProps> = (props, ctx) => {
};
};

const handleBlur = (event: FocusEvent) => {
props.onBlur?.(event);
ctx.host.dispatchEvent(focusEvent());
};

onBeforeMount(() => {
addUnsubscribe(
watch(props).subscribe(propName => {
Expand All @@ -40,6 +46,13 @@ const EditInput: FC<EditInputProps> = (props, ctx) => {
}

lastCursorFocus($input);
}),
watch(props).subscribe(propName => {
if (propName !== 'edit') return;

if (!props.edit) {
ctx.host.dispatchEvent(focusEvent());
}
})
);
});
Expand All @@ -60,7 +73,7 @@ const EditInput: FC<EditInputProps> = (props, ctx) => {
title=${props.title}
.value=${props.value ?? ''}
@input=${props.onInput}
@blur=${props.onBlur}
@blur=${handleBlur}
@keyup=${props.onKeyup}
/>
`
Expand Down
11 changes: 6 additions & 5 deletions packages/erd-editor/src/engine/modules/editor/atom.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { query } from '@/utils/collection/query';

import { ActionMap, ActionType, ReducerType } from './actions';
import { FocusType, MoveKey, SelectType } from './state';
import { arrowDown, arrowLeft, arrowRight, arrowUp } from './utils/focus';
import {
appendSelectColumns,
appendSelectRangeColumns,
Expand Down Expand Up @@ -238,19 +239,19 @@ const focusMoveTable: ReducerType<typeof ActionType.focusMoveTable> = (

switch (payload.moveKey) {
case MoveKey.ArrowUp:
// arrowUp
arrowUp(state, payload);
break;
case MoveKey.ArrowDown:
// arrowDown
arrowDown(state, payload);
break;
case MoveKey.ArrowLeft:
// arrowLeft
arrowLeft(state, payload);
break;
case MoveKey.ArrowRight:
// arrowRight
arrowRight(state, payload);
break;
case MoveKey.Tab:
// shiftKey ? arrowLeft : arrowRight
payload.shiftKey ? arrowLeft(state, payload) : arrowRight(state, payload);
break;
}
};
Expand Down
54 changes: 46 additions & 8 deletions packages/erd-editor/src/engine/modules/editor/generator.actions.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import { isEmpty } from 'lodash-es';

import { GeneratorAction } from '@/engine/generator.actions';
import {
clearAction,
focusTableEndAction,
loadJsonAction,
selectAction,
unselectAllAction,
} from '@/engine/modules/editor/atom.actions';
import { SelectType } from '@/engine/modules/editor/state';
import { moveMemoAction } from '@/engine/modules/memo/atom.actions';
import { removeMemoAction$ } from '@/engine/modules/memo/generator.actions';
import { moveTableAction } from '@/engine/modules/table/atom.actions';
import { removeTableAction$ } from '@/engine/modules/table/generator.actions';
import { addColumnAction$ } from '@/engine/modules/tableColumn/generator.actions';
import { Point } from '@/internal-types';
import { calcMemoHeight, calcMemoWidth } from '@/utils/calcMemo';
import { calcTableHeight, calcTableWidths } from '@/utils/calcTable';
import { query } from '@/utils/collection/query';

import {
clearAction,
focusMoveTableAction,
focusTableEndAction,
loadJsonAction,
selectAction,
unselectAllAction,
} from './atom.actions';
import { MoveKey, SelectType } from './state';
import {
isColumns,
isLastColumn,
isLastRowColumn,
isLastTable,
isTableFocusType,
} from './utils/focus';

type SelectTypeIds = {
tableIds: string[];
memoIds: string[];
Expand Down Expand Up @@ -134,9 +144,37 @@ export const unselectAllAction$ = (): GeneratorAction =>
yield focusTableEndAction();
};

export const focusMoveTableAction$ = (
moveKey: MoveKey,
shiftKey: boolean
): GeneratorAction =>
function* (state) {
const {
editor: { focusTable },
} = state;
if (!focusTable) return;

if (
moveKey === MoveKey.Tab &&
!shiftKey &&
((isTableFocusType(focusTable.focusType) &&
isLastTable(state) &&
!isColumns(state)) ||
(!isTableFocusType(focusTable.focusType) &&
isLastColumn(state) &&
isLastRowColumn(state)))
) {
yield addColumnAction$(focusTable.tableId);
} else {
yield focusMoveTableAction({ moveKey, shiftKey });
}
};

export const actions$ = {
loadJsonAction$,
moveAllAction$,
removeSelectedAction$,
dragSelectAction$,
unselectAllAction$,
focusMoveTableAction$,
};
3 changes: 3 additions & 0 deletions packages/erd-editor/src/engine/modules/editor/state.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { arrayHas } from '@dineug/shared';

import { DEFAULT_HEIGHT, DEFAULT_WIDTH } from '@/constants/layout';
import { ValuesType } from '@/internal-types';

Expand Down Expand Up @@ -50,6 +52,7 @@ export const MoveKey = {
Tab: 'Tab',
};
export type MoveKey = ValuesType<typeof MoveKey>;
export const hasMoveKeys = arrayHas(Object.values(MoveKey));

export const createEditor = (): Editor => ({
selectedMap: {},
Expand Down
Loading

0 comments on commit 6017ccc

Please sign in to comment.