From 4cbd28d58d34a198c85b4212a22d92f58ec72ffb Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Tue, 7 Apr 2026 10:50:52 +0200 Subject: [PATCH] - fixed blue cell color after removing dynamic tag; --- .../ElementSettingSpreadsheet/index.js | 76 ++++++++++++++++++- .../BuilderElementSettings/index.js | 6 +- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingSpreadsheet/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingSpreadsheet/index.js index e03d5d9..52ebc16 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingSpreadsheet/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingSpreadsheet/index.js @@ -24,6 +24,11 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { const isRestoringRef = useRef(false); const mousePos = useRef({ x: 0, y: 0 }); const fileInputRef = useRef(null); + const taggedCellsRef = useRef(new Set()); + const saveTimerRef = useRef(null); + const setDataFnRef = useRef(setDataFn); + + useEffect(() => { setDataFnRef.current = setDataFn; }, [setDataFn]); const insertFieldTag = (ids, type, field) => { const api = univerAPIRef.current; @@ -63,7 +68,24 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { univerRef.current = univer; univerAPIRef.current = univerAPI; - // Restore tag cells whose display value was overwritten by raw tag on blur + // Initialise tagged-cell tracking from the loaded workbook data + const initialTagged = new Set(); + if (workbookData?.sheets) { + const sid = workbookData.sheetOrder?.[0]; + const sd = sid ? workbookData.sheets[sid] : Object.values(workbookData.sheets)[0]; + if (sd?.cellData) { + Object.entries(sd.cellData).forEach(([r, row]) => + Object.entries(row).forEach(([c, cell]) => { + if (typeof cell.f === 'string' && TAG_RE.test(cell.f)) + initialTagged.add(`${r}:${c}`); + }) + ); + } + } + taggedCellsRef.current = initialTagged; + + // Restore tag cells whose display value was overwritten by raw tag on blur; + // also clear background for cells whose tag formula was removed. const restoreTagCells = () => { if (isRestoringRef.current) return; const wb = univerAPIRef.current?.getActiveWorkbook(); @@ -76,6 +98,25 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { const sheetData = sheetId ? saved.sheets[sheetId] : Object.values(saved.sheets)[0]; if (!sheetData?.cellData) return; + // Build set of cells that currently have a tag formula + const currentlyTagged = new Set(); + Object.entries(sheetData.cellData).forEach(([r, row]) => + Object.entries(row).forEach(([c, cell]) => { + if (typeof cell.f === 'string' && TAG_RE.test(cell.f)) + currentlyTagged.add(`${r}:${c}`); + }) + ); + + // Cells that lost their tag formula → need background reset + const cellsToDecolor = []; + for (const key of taggedCellsRef.current) { + if (!currentlyTagged.has(key)) { + const [r, c] = key.split(':').map(Number); + cellsToDecolor.push({ r, c }); + } + } + + // Cells whose displayed value was overwritten by raw formula text → restore const restorations = []; Object.entries(sheetData.cellData).forEach(([r, row]) => { Object.entries(row).forEach(([c, cell]) => { @@ -94,21 +135,33 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { }); }); - if (restorations.length === 0) return; + if (restorations.length === 0 && cellsToDecolor.length === 0) { + taggedCellsRef.current = currentlyTagged; + return; + } isRestoringRef.current = true; restorations.forEach(({ r, c, v, f }) => { sheet.getRange(r, c, 1, 1).setValues([[{ v, f }]]); }); + cellsToDecolor.forEach(({ r, c }) => { + sheet.getRange(r, c, 1, 1).setBackground(''); + }); isRestoringRef.current = false; + taggedCellsRef.current = currentlyTagged; }; let restoreTimer; - // Disable adding new sheets; also schedule tag-cell restoration after each command + // Disable adding new sheets; auto-save on every command; schedule tag restoration univerAPI.addEvent(univerAPI.Event.BeforeCommandExecute, (event) => { if (event.id === 'sheet.command.insert-sheet') { event.cancel = true; } + clearTimeout(saveTimerRef.current); + saveTimerRef.current = setTimeout(() => { + const wb = univerAPIRef.current?.getActiveWorkbook(); + if (wb) setDataFnRef.current(name, wb.save()); + }, 300); clearTimeout(restoreTimer); restoreTimer = setTimeout(restoreTagCells, 80); }); @@ -179,6 +232,7 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { initializeUniver(workbook); return () => { + clearTimeout(saveTimerRef.current); if (univerRef.current) { univerRef.current.dispose(); univerRef.current = null; @@ -187,6 +241,22 @@ const ElementSettingSpreadsheet = ({ value, name, setDataFn }) => { }; }, []); // run once on mount + // Flush workbook data to parent immediately when the user clicks outside the spreadsheet. + // This runs in the capture phase — before the clicked element's own handler — so the + // parent's store is up to date by the time any outer "Salva" button handler reads it. + useEffect(() => { + const container = containerRef.current; + if (!container) return; + const flushOnExternalClick = (e) => { + if (container.contains(e.target)) return; + clearTimeout(saveTimerRef.current); + const wb = univerAPIRef.current?.getActiveWorkbook(); + if (wb) setDataFnRef.current(name, wb.save()); + }; + document.addEventListener('click', flushOnExternalClick, true); + return () => document.removeEventListener('click', flushOnExternalClick, true); + }, [name]); // name is stable ('template') + // Prevent page scroll while hovering over the spreadsheet useEffect(() => { const el = containerRef.current; diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js index b7ec16e..8c7f742 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js @@ -73,7 +73,11 @@ const BuilderElementSettings = ({ closeSettingsFn, callStatus, context }) => { const saveSettings = () => { let newActiveElementData = klona(activeElementData); - newActiveElementData = wrap(newActiveElementData).set(['settings'], settings).value(); + // Prefer the store value over React state: child components (e.g. ElementSettingSpreadsheet) + // write to the store synchronously, but React state updates are batched and may lag behind + // when saveSettings is called in the same event as the last setDataFn call. + const latestSettings = storeGet('chosenFieldSettings') || settings; + newActiveElementData = wrap(newActiveElementData).set(['settings'], latestSettings).value(); newActiveElementData = wrap(newActiveElementData).set(['validators'], validators).value(); newActiveElementData = wrap(newActiveElementData).set(['dynamicData'], dynamicData).value(); newActiveElementData = wrap(newActiveElementData).set(['criteria'], criteria).value();