- fixed blue cell color after removing dynamic tag;

This commit is contained in:
Vitalii Kiiko
2026-04-07 10:50:52 +02:00
parent 2804dd897a
commit 4cbd28d58d
2 changed files with 78 additions and 4 deletions

View File

@@ -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;

View File

@@ -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();