diff --git a/apps/nextjs-app/src/features/app/blocks/view/grid/GridViewBase.tsx b/apps/nextjs-app/src/features/app/blocks/view/grid/GridViewBase.tsx index b22ed2206..f0b2c5aa5 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/grid/GridViewBase.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/grid/GridViewBase.tsx @@ -57,6 +57,7 @@ import { isEqual, keyBy, uniqueId, groupBy } from 'lodash'; import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useHotkeys } from 'react-hotkeys-hook'; import { usePrevious, useMount, useClickAway } from 'react-use'; import { ExpandRecordContainer } from '@/features/app/components/ExpandRecordContainer'; import type { IExpandRecordContainerRef } from '@/features/app/components/ExpandRecordContainer/types'; @@ -301,7 +302,8 @@ export const GridViewBase: React.FC = (props: IGridViewProps) => const selectColumns = extract(start, end, columns); const indexedColumns = keyBy(selectColumns, 'id'); const selectFields = fields.filter((field) => indexedColumns[field.id]); - openHeaderMenu({ position, fields: selectFields }); + const onSelectionClear = () => gridRef.current?.setSelection(emptySelection); + openHeaderMenu({ position, fields: selectFields, onSelectionClear }); } }, [ @@ -614,6 +616,16 @@ export const GridViewBase: React.FC = (props: IGridViewProps) => useScrollFrameRate(gridRef.current?.scrollBy); + useHotkeys( + ['mod+f', 'mod+k'], + () => { + gridRef.current?.setSelection(emptySelection); + }, + { + enableOnFormTags: ['input', 'select', 'textarea'], + } + ); + return (
{isReadyToRender && !isLoading ? ( diff --git a/apps/nextjs-app/src/features/app/blocks/view/grid/components/FieldMenu.tsx b/apps/nextjs-app/src/features/app/blocks/view/grid/components/FieldMenu.tsx index 34eaa6aa1..6d5ce0354 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/grid/components/FieldMenu.tsx +++ b/apps/nextjs-app/src/features/app/blocks/view/grid/components/FieldMenu.tsx @@ -46,7 +46,7 @@ export const FieldMenu = () => { const { t } = useTranslation(tableConfig.i18nNamespaces); const allFields = useFields({ withHidden: true, withDenied: true }); const fieldSettingRef = useRef(null); - const fields = headerMenu?.fields; + const { fields, onSelectionClear } = headerMenu ?? {}; const menuFieldPermission = useMemo(() => { if (!fields?.length || !fieldsPermission) { @@ -210,6 +210,7 @@ export const FieldMenu = () => { return; } await onClick(); + onSelectionClear?.(); closeHeaderMenu(); }} > @@ -249,6 +250,7 @@ export const FieldMenu = () => { return; } await onClick(); + onSelectionClear?.(); closeHeaderMenu(); }} > diff --git a/apps/nextjs-app/src/features/app/blocks/view/grid/store/type.ts b/apps/nextjs-app/src/features/app/blocks/view/grid/store/type.ts index e23081f02..8f58b8073 100644 --- a/apps/nextjs-app/src/features/app/blocks/view/grid/store/type.ts +++ b/apps/nextjs-app/src/features/app/blocks/view/grid/store/type.ts @@ -4,6 +4,7 @@ import type { IFieldInstance, Record } from '@teable/sdk/model'; export interface IHeaderMenu { fields: IFieldInstance[]; position: IPosition; + onSelectionClear?: () => void; } export interface IRecordMenu { diff --git a/packages/sdk/src/components/editor/long-text/Editor.tsx b/packages/sdk/src/components/editor/long-text/Editor.tsx index a4d4c0eb3..c9e04d7cd 100644 --- a/packages/sdk/src/components/editor/long-text/Editor.tsx +++ b/packages/sdk/src/components/editor/long-text/Editor.tsx @@ -10,7 +10,7 @@ const LongTextEditorBase: ForwardRefRenderFunction, ITextEdit props, ref ) => { - const { value, onChange, className, readonly } = props; + const { value, onChange, className, readonly, saveOnBlur = true } = props; const [text, setText] = useState(value || ''); const inputRef = useRef(null); @@ -39,7 +39,7 @@ const LongTextEditorBase: ForwardRefRenderFunction, ITextEdit minRows={2} maxRows={10} readOnly={readonly} - onBlur={saveValue} + onBlur={() => saveOnBlur && saveValue()} onChange={onChangeInner} /> ); diff --git a/packages/sdk/src/components/editor/number/Editor.tsx b/packages/sdk/src/components/editor/number/Editor.tsx index 8af22c425..459989f22 100644 --- a/packages/sdk/src/components/editor/number/Editor.tsx +++ b/packages/sdk/src/components/editor/number/Editor.tsx @@ -13,7 +13,7 @@ export const NumberEditorBase: ForwardRefRenderFunction, INum props, ref ) => { - const { value, options, onChange, className, readonly, style } = props; + const { value, options, onChange, className, readonly, style, saveOnBlur = true } = props; const { formatting } = options; const inputRef = useRef(null); const [formatStr, setFormatStr] = useState( @@ -47,7 +47,7 @@ export const NumberEditorBase: ForwardRefRenderFunction, INum className={cn('h-10 sm:h-8', className)} value={formatStr || ''} onChange={onChangeInner} - onBlur={saveValue} + onBlur={() => saveOnBlur && saveValue()} disabled={readonly} /> ); diff --git a/packages/sdk/src/components/editor/text/Editor.tsx b/packages/sdk/src/components/editor/text/Editor.tsx index 794a7daaf..56fdda0bb 100644 --- a/packages/sdk/src/components/editor/text/Editor.tsx +++ b/packages/sdk/src/components/editor/text/Editor.tsx @@ -12,7 +12,7 @@ interface ITextEditor extends ICellEditor { } const TextEditorBase: ForwardRefRenderFunction, ITextEditor> = (props, ref) => { - const { value, options, onChange, className, readonly, style } = props; + const { value, options, onChange, className, readonly, style, saveOnBlur = true } = props; const [text, setText] = useState(value || ''); const inputRef = useRef(null); const showAs = options.showAs; @@ -56,7 +56,7 @@ const TextEditorBase: ForwardRefRenderFunction, ITextEditor> className={cn('h-10 sm:h-8', className)} value={text} onChange={onChangeInner} - onBlur={saveValue} + onBlur={() => saveOnBlur && saveValue()} disabled={readonly} /> {showAs && ( diff --git a/packages/sdk/src/components/editor/type.ts b/packages/sdk/src/components/editor/type.ts index 7d4468cee..d30bccd22 100644 --- a/packages/sdk/src/components/editor/type.ts +++ b/packages/sdk/src/components/editor/type.ts @@ -5,9 +5,10 @@ export interface ICellEditor { className?: string; style?: React.CSSProperties; value?: T; - onChange?: (value?: T) => void; readonly?: boolean; + saveOnBlur?: boolean; context?: ICellEditorContext; + onChange?: (value?: T) => void; } export interface IEditorRef { diff --git a/packages/sdk/src/components/grid-enhancements/editor/GridNumberEditor.tsx b/packages/sdk/src/components/grid-enhancements/editor/GridNumberEditor.tsx index 02be4edcb..afa121f9c 100644 --- a/packages/sdk/src/components/grid-enhancements/editor/GridNumberEditor.tsx +++ b/packages/sdk/src/components/grid-enhancements/editor/GridNumberEditor.tsx @@ -50,6 +50,7 @@ const GridNumberEditorBase: ForwardRefRenderFunction< style={{ border: `2px solid ${cellLineColorActived}`, ...style, ...attachStyle }} options={options} onChange={saveValue} + saveOnBlur={false} /> ); }; diff --git a/packages/sdk/src/components/grid-enhancements/editor/GridSelectEditor.tsx b/packages/sdk/src/components/grid-enhancements/editor/GridSelectEditor.tsx index 9e22b4b74..64509fdde 100644 --- a/packages/sdk/src/components/grid-enhancements/editor/GridSelectEditor.tsx +++ b/packages/sdk/src/components/grid-enhancements/editor/GridSelectEditor.tsx @@ -21,7 +21,7 @@ const GridSelectEditorBase: ForwardRefRenderFunction< IEditorRef, IWrapperEditorProps & IEditorProps > = (props, ref) => { - const { field, record, rect, style, isEditing } = props; + const { field, record, rect, style, isEditing, setEditing } = props; const tableId = useTableId(); const defaultFocusRef = useRef(null); const editorRef = useRef>(null); @@ -59,6 +59,7 @@ const GridSelectEditorBase: ForwardRefRenderFunction< const onChange = (value?: string[] | string) => { record.updateCell(fieldId, isMultiple && value?.length === 0 ? null : value); + if (!isMultiple) setEditing?.(false); }; const onOptionAdd = useCallback( diff --git a/packages/sdk/src/components/grid/components/editor/EditorContainer.tsx b/packages/sdk/src/components/grid/components/editor/EditorContainer.tsx index f650d07c8..75c7bebff 100644 --- a/packages/sdk/src/components/grid/components/editor/EditorContainer.tsx +++ b/packages/sdk/src/components/grid/components/editor/EditorContainer.tsx @@ -2,9 +2,8 @@ import { clamp } from 'lodash'; import type { CSSProperties, ForwardRefRenderFunction } from 'react'; import { useEffect, useRef, useMemo, useImperativeHandle, forwardRef } from 'react'; -import { HotkeysProvider } from 'react-hotkeys-hook'; import type { IGridTheme } from '../../configs'; -import { GRID_HOTKEY_SCOPE, useKeyboardSelection } from '../../hooks'; +import { useKeyboardSelection } from '../../hooks'; import type { IInteractionLayerProps } from '../../InteractionLayer'; import type { IActiveCellBound, ICellItem, IRectangle, IScrollState } from '../../interface'; import type { CombinedSelection } from '../../managers'; @@ -117,9 +116,14 @@ export const EditorContainerBase: ForwardRefRenderFunction< if ((cellContent as ICell).type === CellType.Loading) return; if (!activeCell || isEditing) return; editorRef.current?.setValue?.(cellContent.data); - requestAnimationFrame(() => (editorRef.current || defaultFocusRef.current)?.focus?.()); }, [cellContent, activeCell, isEditing]); + useEffect(() => { + if ((cellType as CellType) === CellType.Loading) return; + if (!activeCell) return; + requestAnimationFrame(() => (editorRef.current || defaultFocusRef.current)?.focus?.()); + }, [cellType, activeCell, isEditing]); + useKeyboardSelection({ editorRef, isEditing, @@ -271,24 +275,22 @@ export const EditorContainerBase: ForwardRefRenderFunction< }; return ( - -
-
- {EditorRenderer} - -
+
+
+ {EditorRenderer} +
- +
); }; diff --git a/packages/sdk/src/components/grid/hooks/useKeyboardSelection.ts b/packages/sdk/src/components/grid/hooks/useKeyboardSelection.ts index a94769cf8..7aae5938a 100644 --- a/packages/sdk/src/components/grid/hooks/useKeyboardSelection.ts +++ b/packages/sdk/src/components/grid/hooks/useKeyboardSelection.ts @@ -17,8 +17,6 @@ interface ISelectionKeyboardProps editorRef: React.MutableRefObject; } -export const GRID_HOTKEY_SCOPE = 'grid-hotkey-scope'; - export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { const { isEditing, @@ -44,7 +42,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: !isEditing && selection.type !== SelectionRegionType.None, - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -105,7 +102,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell && !isEditing), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -126,7 +122,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -142,7 +137,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell && !isEditing), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -159,7 +153,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell && !isEditing), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -192,7 +185,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -204,7 +196,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } ); @@ -217,7 +208,6 @@ export const useKeyboardSelection = (props: ISelectionKeyboardProps) => { }, { enabled: Boolean(activeCell && !isEditing), - scopes: GRID_HOTKEY_SCOPE, enableOnFormTags: ['input', 'select', 'textarea'], } );