Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Commit 66d06b9

Browse files
authored
feat: add template field management functions in SuperDocTemplateBuilder (#5)
1 parent 26ffd6f commit 66d06b9

1 file changed

Lines changed: 119 additions & 19 deletions

File tree

src/index.tsx

Lines changed: 119 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,59 @@ export { FieldMenu, FieldList };
1515

1616
type Editor = NonNullable<SuperDoc["activeEditor"]>;
1717

18+
const getTemplateFieldsFromEditor = (
19+
editor: Editor,
20+
): Types.TemplateField[] => {
21+
const structuredContentHelpers =
22+
(editor.helpers as any)?.structuredContentCommands;
23+
24+
if (!structuredContentHelpers?.getStructuredContentTags) {
25+
return [];
26+
}
27+
28+
const tags =
29+
structuredContentHelpers.getStructuredContentTags(editor.state) || [];
30+
31+
return tags
32+
.map((entry: any) => {
33+
const node = entry?.node ?? entry;
34+
const attrs = node?.attrs ?? {};
35+
36+
return {
37+
id: attrs.id,
38+
alias: attrs.alias || attrs.label || "",
39+
tag: attrs.tag,
40+
} as Types.TemplateField;
41+
})
42+
.filter((field: Types.TemplateField) => Boolean(field.id));
43+
};
44+
45+
const areTemplateFieldsEqual = (
46+
a: Types.TemplateField[],
47+
b: Types.TemplateField[],
48+
): boolean => {
49+
if (a === b) return true;
50+
if (a.length !== b.length) return false;
51+
52+
for (let index = 0; index < a.length; index += 1) {
53+
const left = a[index];
54+
const right = b[index];
55+
56+
if (!right) return false;
57+
58+
if (
59+
left.id !== right.id ||
60+
left.alias !== right.alias ||
61+
left.tag !== right.tag ||
62+
left.position !== right.position
63+
) {
64+
return false;
65+
}
66+
}
67+
68+
return true;
69+
};
70+
1871
const SuperDocTemplateBuilder = forwardRef<
1972
Types.SuperDocTemplateBuilderHandle,
2073
Types.SuperDocTemplateBuilderProps
@@ -173,21 +226,73 @@ const SuperDocTemplateBuilder = forwardRef<
173226

174227
const deleteField = useCallback(
175228
(id: string): boolean => {
176-
if (!superdocRef.current?.activeEditor) return false;
229+
const editor = superdocRef.current?.activeEditor;
177230

178-
const editor = superdocRef.current.activeEditor;
179-
const success = editor.commands.deleteStructuredContentById?.(id);
231+
if (!editor) {
232+
console.warn(
233+
"[SuperDocTemplateBuilder] deleteField called without active editor",
234+
);
180235

181-
if (success) {
236+
let removed = false;
182237
setTemplateFields((prev) => {
183-
const updated = prev.filter((f) => f.id !== id);
238+
if (!prev.some((field) => field.id === id)) return prev;
239+
240+
const updated = prev.filter((field) => field.id !== id);
241+
removed = true;
184242
onFieldsChange?.(updated);
185243
return updated;
186244
});
245+
246+
if (removed) {
247+
onFieldDelete?.(id);
248+
setSelectedFieldId((current) => (current === id ? null : current));
249+
}
250+
251+
return removed;
252+
}
253+
254+
let commandResult = false;
255+
try {
256+
commandResult =
257+
editor.commands.deleteStructuredContentById?.(id) ?? false;
258+
} catch (error) {
259+
console.error(
260+
"[SuperDocTemplateBuilder] Delete command failed:",
261+
error,
262+
);
263+
}
264+
265+
let documentFields = getTemplateFieldsFromEditor(editor);
266+
const fieldStillPresent = documentFields.some((field) => field.id === id);
267+
268+
if (!commandResult && fieldStillPresent) {
269+
documentFields = documentFields.filter((field) => field.id !== id);
270+
}
271+
272+
let removedFromState = false;
273+
274+
setTemplateFields((prev) => {
275+
if (areTemplateFieldsEqual(prev, documentFields)) {
276+
return prev;
277+
}
278+
279+
const prevHadField = prev.some((field) => field.id === id);
280+
const nextHasField = documentFields.some((field) => field.id === id);
281+
282+
if (prevHadField && !nextHasField) {
283+
removedFromState = true;
284+
}
285+
286+
onFieldsChange?.(documentFields);
287+
return documentFields;
288+
});
289+
290+
if (removedFromState) {
187291
onFieldDelete?.(id);
292+
setSelectedFieldId((current) => (current === id ? null : current));
188293
}
189294

190-
return success;
295+
return commandResult || removedFromState;
191296
},
192297
[onFieldDelete, onFieldsChange],
193298
);
@@ -210,21 +315,16 @@ const SuperDocTemplateBuilder = forwardRef<
210315
(editor: Editor) => {
211316
if (!editor) return;
212317

213-
const tags =
214-
editor.helpers.structuredContentCommands.getStructuredContentTags(
215-
editor.state,
216-
);
318+
const discovered = getTemplateFieldsFromEditor(editor);
217319

218-
const discovered: Types.TemplateField[] = tags
219-
.map(({ node }: any) => ({
220-
id: node.attrs.id,
221-
alias: node.attrs.alias || node.attrs.label || "",
222-
tag: node.attrs.tag,
223-
}))
224-
.filter((f: Types.TemplateField) => f.id);
320+
setTemplateFields((prev) => {
321+
if (areTemplateFieldsEqual(prev, discovered)) {
322+
return prev;
323+
}
225324

226-
setTemplateFields(discovered);
227-
onFieldsChange?.(discovered);
325+
onFieldsChange?.(discovered);
326+
return discovered;
327+
});
228328
},
229329
[onFieldsChange],
230330
);

0 commit comments

Comments
 (0)