From 8599785ee87627f01fc0136bc30d551e9ea33f5d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 8 Mar 2026 22:39:43 +0200 Subject: [PATCH] refactor(spreadsheet): use multiple modules --- apps/client/src/widgets/note_types.tsx | 2 +- .../{ => spreadsheet}/Spreadsheet.css | 0 .../type_widgets/spreadsheet/Spreadsheet.tsx | 98 +++++++++++++++++ .../persistence.tsx} | 103 +----------------- 4 files changed, 104 insertions(+), 99 deletions(-) rename apps/client/src/widgets/type_widgets/{ => spreadsheet}/Spreadsheet.css (100%) create mode 100644 apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.tsx rename apps/client/src/widgets/type_widgets/{Spreadsheet.tsx => spreadsheet/persistence.tsx} (61%) diff --git a/apps/client/src/widgets/note_types.tsx b/apps/client/src/widgets/note_types.tsx index 687bfdfe9c..8899c73100 100644 --- a/apps/client/src/widgets/note_types.tsx +++ b/apps/client/src/widgets/note_types.tsx @@ -143,7 +143,7 @@ export const TYPE_MAPPINGS: Record = { isFullHeight: true }, spreadsheet: { - view: () => import("./type_widgets/Spreadsheet"), + view: () => import("./type_widgets/Spreadsheet/Spreadsheet"), className: "note-detail-spreadsheet", printable: true, isFullHeight: true diff --git a/apps/client/src/widgets/type_widgets/Spreadsheet.css b/apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.css similarity index 100% rename from apps/client/src/widgets/type_widgets/Spreadsheet.css rename to apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.css diff --git a/apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.tsx b/apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.tsx new file mode 100644 index 0000000000..84a81839bc --- /dev/null +++ b/apps/client/src/widgets/type_widgets/spreadsheet/Spreadsheet.tsx @@ -0,0 +1,98 @@ +import "@univerjs/preset-sheets-core/lib/index.css"; +import "./Spreadsheet.css"; + +import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'; +import sheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US'; +import { UniverSheetsFindReplacePreset } from '@univerjs/preset-sheets-find-replace'; +import sheetsFindReplaceEnUS from '@univerjs/preset-sheets-find-replace/locales/en-US'; +import { UniverSheetsNotePreset } from '@univerjs/preset-sheets-note'; +import sheetsNoteEnUS from '@univerjs/preset-sheets-note/locales/en-US'; +import { createUniver, FUniver, LocaleType, mergeLocales } from '@univerjs/presets'; +import { MutableRef, useEffect, useRef } from "preact/hooks"; + +import { useColorScheme, useNoteLabelBoolean, useTriliumEvent } from "../../react/hooks"; +import { TypeWidgetProps } from "../type_widget"; +import usePersistence from "./persistence"; + +export default function Spreadsheet(props: TypeWidgetProps) { + const [ readOnly ] = useNoteLabelBoolean(props.note, "readOnly"); + + // Use readOnly as key to force full remount (and data reload) when it changes. + return ; +} + +function SpreadsheetEditor({ note, noteContext, readOnly }: TypeWidgetProps & { readOnly: boolean }) { + const containerRef = useRef(null); + const apiRef = useRef(); + + useInitializeSpreadsheet(containerRef, apiRef, readOnly); + useDarkMode(apiRef); + usePersistence(note, noteContext, apiRef, containerRef, readOnly); + useSearchIntegration(apiRef); + + // Focus the spreadsheet when the note is focused. + useTriliumEvent("focusOnDetail", () => { + const focusable = containerRef.current?.querySelector('[data-u-comp="editor"]'); + if (focusable instanceof HTMLElement) { + focusable.focus(); + } + }); + + return
; +} + +function useInitializeSpreadsheet(containerRef: MutableRef, apiRef: MutableRef, readOnly: boolean) { + useEffect(() => { + if (!containerRef.current) return; + + const { univerAPI } = createUniver({ + locale: LocaleType.EN_US, + locales: { + [LocaleType.EN_US]: mergeLocales( + sheetsCoreEnUS, + sheetsFindReplaceEnUS, + sheetsNoteEnUS, + ), + }, + presets: [ + UniverSheetsCorePreset({ + container: containerRef.current, + toolbar: !readOnly, + contextMenu: !readOnly, + formulaBar: !readOnly, + footer: readOnly ? false : undefined, + menu: { + "sheet.contextMenu.permission": { hidden: true }, + "sheet-permission.operation.openPanel": { hidden: true }, + "sheet.command.add-range-protection-from-toolbar": { hidden: true }, + }, + }), + UniverSheetsFindReplacePreset(), + UniverSheetsNotePreset() + ] + }); + apiRef.current = univerAPI; + return () => univerAPI.dispose(); + }, [ apiRef, containerRef, readOnly ]); +} + +function useDarkMode(apiRef: MutableRef) { + const colorScheme = useColorScheme(); + + // React to dark mode. + useEffect(() => { + const univerAPI = apiRef.current; + if (!univerAPI) return; + univerAPI.toggleDarkMode(colorScheme === 'dark'); + }, [ colorScheme, apiRef ]); +} + +function useSearchIntegration(apiRef: MutableRef) { + useTriliumEvent("findInText", () => { + const univerAPI = apiRef.current; + if (!univerAPI) return; + + // Open find/replace panel and populate the search term. + univerAPI.executeCommand("ui.operation.open-find-dialog"); + }); +} diff --git a/apps/client/src/widgets/type_widgets/Spreadsheet.tsx b/apps/client/src/widgets/type_widgets/spreadsheet/persistence.tsx similarity index 61% rename from apps/client/src/widgets/type_widgets/Spreadsheet.tsx rename to apps/client/src/widgets/type_widgets/spreadsheet/persistence.tsx index 2325e4641a..b3717c9ace 100644 --- a/apps/client/src/widgets/type_widgets/Spreadsheet.tsx +++ b/apps/client/src/widgets/type_widgets/spreadsheet/persistence.tsx @@ -1,19 +1,9 @@ -import "@univerjs/preset-sheets-core/lib/index.css"; -import "./Spreadsheet.css"; - -import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'; -import sheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US'; -import { UniverSheetsFindReplacePreset } from '@univerjs/preset-sheets-find-replace'; -import sheetsFindReplaceEnUS from '@univerjs/preset-sheets-find-replace/locales/en-US'; -import { UniverSheetsNotePreset } from '@univerjs/preset-sheets-note'; -import sheetsNoteEnUS from '@univerjs/preset-sheets-note/locales/en-US'; -import { CommandType, createUniver, FUniver, IDisposable, IWorkbookData, LocaleType, mergeLocales } from '@univerjs/presets'; +import { CommandType, FUniver, IDisposable, IWorkbookData } from "@univerjs/presets"; import { MutableRef, useEffect, useRef } from "preact/hooks"; -import NoteContext from "../../components/note_context"; -import FNote from "../../entities/fnote"; -import { SavedData, useColorScheme, useEditorSpacedUpdate, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks"; -import { TypeWidgetProps } from "./type_widget"; +import NoteContext from "../../../components/note_context"; +import FNote from "../../../entities/fnote"; +import { SavedData, useEditorSpacedUpdate } from "../../react/hooks"; interface PersistedData { version: number; @@ -28,80 +18,7 @@ interface SpreadsheetViewState { scrollCol?: number; } -export default function Spreadsheet(props: TypeWidgetProps) { - const [ readOnly ] = useNoteLabelBoolean(props.note, "readOnly"); - - // Use readOnly as key to force full remount (and data reload) when it changes. - return ; -} - -function SpreadsheetEditor({ note, noteContext, readOnly }: TypeWidgetProps & { readOnly: boolean }) { - const containerRef = useRef(null); - const apiRef = useRef(); - - useInitializeSpreadsheet(containerRef, apiRef, readOnly); - useDarkMode(apiRef); - usePersistence(note, noteContext, apiRef, containerRef, readOnly); - useSearchIntegration(apiRef); - - // Focus the spreadsheet when the note is focused. - useTriliumEvent("focusOnDetail", () => { - const focusable = containerRef.current?.querySelector('[data-u-comp="editor"]'); - if (focusable instanceof HTMLElement) { - focusable.focus(); - } - }); - - return
; -} - -function useInitializeSpreadsheet(containerRef: MutableRef, apiRef: MutableRef, readOnly: boolean) { - useEffect(() => { - if (!containerRef.current) return; - - const { univerAPI } = createUniver({ - locale: LocaleType.EN_US, - locales: { - [LocaleType.EN_US]: mergeLocales( - sheetsCoreEnUS, - sheetsFindReplaceEnUS, - sheetsNoteEnUS, - ), - }, - presets: [ - UniverSheetsCorePreset({ - container: containerRef.current, - toolbar: !readOnly, - contextMenu: !readOnly, - formulaBar: !readOnly, - footer: readOnly ? false : undefined, - menu: { - "sheet.contextMenu.permission": { hidden: true }, - "sheet-permission.operation.openPanel": { hidden: true }, - "sheet.command.add-range-protection-from-toolbar": { hidden: true }, - }, - }), - UniverSheetsFindReplacePreset(), - UniverSheetsNotePreset() - ] - }); - apiRef.current = univerAPI; - return () => univerAPI.dispose(); - }, [ apiRef, containerRef, readOnly ]); -} - -function useDarkMode(apiRef: MutableRef) { - const colorScheme = useColorScheme(); - - // React to dark mode. - useEffect(() => { - const univerAPI = apiRef.current; - if (!univerAPI) return; - univerAPI.toggleDarkMode(colorScheme === 'dark'); - }, [ colorScheme, apiRef ]); -} - -function usePersistence(note: FNote, noteContext: NoteContext | null | undefined, apiRef: MutableRef, containerRef: MutableRef, readOnly: boolean) { +export default function usePersistence(note: FNote, noteContext: NoteContext | null | undefined, apiRef: MutableRef, containerRef: MutableRef, readOnly: boolean) { const changeListener = useRef(null); const pendingContent = useRef(null); @@ -275,13 +192,3 @@ function usePersistence(note: FNote, noteContext: NoteContext | null | undefined }; }, []); } - -function useSearchIntegration(apiRef: MutableRef) { - useTriliumEvent("findInText", () => { - const univerAPI = apiRef.current; - if (!univerAPI) return; - - // Open find/replace panel and populate the search term. - univerAPI.executeCommand("ui.operation.open-find-dialog"); - }); -}