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

Commit c85abc1

Browse files
feat: onexport callback (#21)
* feat: onexport callback * chore: add onexport callback to demo --------- Co-authored-by: Caio Pizzol <caiopizzol@icloud.com>
1 parent 10fa988 commit c85abc1

File tree

3 files changed

+132
-65
lines changed

3 files changed

+132
-65
lines changed

demo/src/App.tsx

Lines changed: 116 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@ import type {
44
SuperDocTemplateBuilderHandle,
55
TemplateField,
66
FieldDefinition,
7+
ExportEvent,
78
} from "@superdoc-dev/template-builder";
8-
import "superdoc/dist/style.css";
9+
import "superdoc/style.css";
910
import "./App.css";
1011

1112
const availableFields: FieldDefinition[] = [
12-
{ id: '1242142770', label: 'Agreement Date' },
13-
{ id: '1242142771', label: 'User Name' },
14-
{ id: '1242142772', label: 'Company Name' },
15-
{ id: '1242142773', label: 'Service Type' },
16-
{ id: '1242142774', label: 'Agreement Jurisdiction' },
17-
{ id: '1242142775', label: 'Company Address' },
18-
{ id: '1242142776', label: 'Signature' },
13+
{ id: "1242142770", label: "Agreement Date" },
14+
{ id: "1242142771", label: "User Name" },
15+
{ id: "1242142772", label: "Company Name" },
16+
{ id: "1242142773", label: "Service Type" },
17+
{ id: "1242142774", label: "Agreement Jurisdiction" },
18+
{ id: "1242142775", label: "Company Address" },
19+
{ id: "1242142776", label: "Signature" },
1920
];
2021

2122
export function App() {
@@ -34,42 +35,68 @@ export function App() {
3435
const log = useCallback((msg: string) => {
3536
const time = new Date().toLocaleTimeString();
3637
console.log(`[${time}] ${msg}`);
37-
setEvents(prev => [...prev.slice(-4), `${time} - ${msg}`]);
38+
setEvents((prev) => [...prev.slice(-4), `${time} - ${msg}`]);
3839
}, []);
3940

40-
const handleFieldsChange = useCallback((updatedFields: TemplateField[]) => {
41-
setFields(updatedFields);
42-
log(`Fields: ${updatedFields.length} total`);
43-
}, [log]);
41+
const handleFieldsChange = useCallback(
42+
(updatedFields: TemplateField[]) => {
43+
setFields(updatedFields);
44+
log(`Fields: ${updatedFields.length} total`);
45+
},
46+
[log],
47+
);
4448

45-
const handleFieldInsert = useCallback((field: TemplateField) => {
46-
log(`✓ Inserted: ${field.alias}`);
47-
}, [log]);
49+
const handleFieldInsert = useCallback(
50+
(field: TemplateField) => {
51+
log(`✓ Inserted: ${field.alias}`);
52+
},
53+
[log],
54+
);
4855

49-
const handleFieldDelete = useCallback((fieldId: string | number) => {
50-
log(`✗ Deleted: ${fieldId}`);
51-
}, [log]);
56+
const handleFieldDelete = useCallback(
57+
(fieldId: string | number) => {
58+
log(`✗ Deleted: ${fieldId}`);
59+
},
60+
[log],
61+
);
5262

53-
const handleFieldSelect = useCallback((field: TemplateField | null) => {
54-
if (field) {
55-
log(`Selected: ${field.alias}`);
56-
}
57-
}, [log]);
63+
const handleFieldSelect = useCallback(
64+
(field: TemplateField | null) => {
65+
if (field) {
66+
log(`Selected: ${field.alias}`);
67+
}
68+
},
69+
[log],
70+
);
5871

5972
const handleReady = useCallback(() => {
60-
log('✓ Template builder ready');
73+
log("✓ Template builder ready");
6174
if (importingRef.current) {
62-
log('📄 Document imported');
75+
log("📄 Document imported");
6376
importingRef.current = false;
6477
setImportError(null);
6578
setIsImporting(false);
6679
}
6780
}, [log]);
6881

6982
const handleTrigger = useCallback(() => {
70-
log('⌨ Trigger detected');
83+
log("⌨ Trigger detected");
7184
}, [log]);
7285

86+
const handleExport = useCallback(
87+
(event: ExportEvent) => {
88+
console.log("Export Event:", event);
89+
console.log("Fields:", JSON.stringify(event.fields, null, 2));
90+
log(`Exported ${event.fields.length} fields`);
91+
event.fields.forEach((f) => {
92+
console.log(
93+
` - ${f.alias} (id: ${f.id}, mode: ${f.mode}, group: ${f.group || "none"})`,
94+
);
95+
});
96+
},
97+
[log],
98+
);
99+
73100
const handleExportTemplate = useCallback(async () => {
74101
if (!builderRef.current) {
75102
return;
@@ -92,7 +119,7 @@ export function App() {
92119
}, [log]);
93120

94121
const handleKeyDown = (e: React.KeyboardEvent) => {
95-
if (e.key === 'Tab') {
122+
if (e.key === "Tab") {
96123
e.preventDefault();
97124
if (e.shiftKey) {
98125
builderRef.current?.previousField();
@@ -102,62 +129,85 @@ export function App() {
102129
}
103130
};
104131

105-
const documentConfig = useMemo(() => ({
106-
source: documentSource,
107-
mode: 'editing' as const
108-
}), [documentSource]);
132+
const documentConfig = useMemo(
133+
() => ({
134+
source: documentSource,
135+
mode: "editing" as const,
136+
}),
137+
[documentSource],
138+
);
109139

110140
const handleImportButtonClick = useCallback(() => {
111141
if (isImporting) return;
112142
fileInputRef.current?.click();
113143
}, [isImporting]);
114144

115-
const handleFileInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
116-
const file = event.target.files?.[0];
117-
event.target.value = "";
145+
const handleFileInputChange = useCallback(
146+
(event: React.ChangeEvent<HTMLInputElement>) => {
147+
const file = event.target.files?.[0];
148+
event.target.value = "";
118149

119-
if (!file) return;
150+
if (!file) return;
120151

121-
const extension = file.name.split('.').pop()?.toLowerCase();
122-
if (extension !== 'docx') {
123-
const message = 'Invalid file type. Please choose a .docx file.';
124-
setImportError(message);
125-
log('⚠️ ' + message);
126-
return;
127-
}
152+
const extension = file.name.split(".").pop()?.toLowerCase();
153+
if (extension !== "docx") {
154+
const message = "Invalid file type. Please choose a .docx file.";
155+
setImportError(message);
156+
log("⚠️ " + message);
157+
return;
158+
}
128159

129-
importingRef.current = true;
130-
setImportError(null);
131-
setIsImporting(true);
132-
setDocumentSource(file);
133-
log(`📥 Importing "${file.name}"`);
134-
}, [log]);
160+
importingRef.current = true;
161+
setImportError(null);
162+
setIsImporting(true);
163+
setDocumentSource(file);
164+
log(`📥 Importing "${file.name}"`);
165+
},
166+
[log],
167+
);
135168

136-
const fieldsConfig = useMemo(() => ({
137-
available: availableFields,
138-
allowCreate: true
139-
}), []);
169+
const fieldsConfig = useMemo(
170+
() => ({
171+
available: availableFields,
172+
allowCreate: true,
173+
}),
174+
[],
175+
);
140176

141-
const listConfig = useMemo(() => ({
142-
position: 'right' as const
143-
}), []);
177+
const listConfig = useMemo(
178+
() => ({
179+
position: "right" as const,
180+
}),
181+
[],
182+
);
144183

145184
return (
146185
<div className="demo" onKeyDown={handleKeyDown}>
147186
<header>
148187
<div className="header-content">
149188
<div className="header-left">
150189
<h1>
151-
<a href="https://www.npmjs.com/package/@superdoc-dev/template-builder" target="_blank" rel="noopener">
190+
<a
191+
href="https://www.npmjs.com/package/@superdoc-dev/template-builder"
192+
target="_blank"
193+
rel="noopener"
194+
>
152195
@superdoc-dev/template-builder
153196
</a>
154197
</h1>
155198
<p>
156-
React template builder from <a href="https://superdoc.dev" target="_blank" rel="noopener">SuperDoc</a>
199+
React template builder from{" "}
200+
<a href="https://superdoc.dev" target="_blank" rel="noopener">
201+
SuperDoc
202+
</a>
157203
</p>
158204
</div>
159205
<div className="header-nav">
160-
<a href="https://github.com/superdoc-dev/template-builder" target="_blank" rel="noopener">
206+
<a
207+
href="https://github.com/superdoc-dev/template-builder"
208+
target="_blank"
209+
rel="noopener"
210+
>
161211
GitHub
162212
</a>
163213
<a href="https://docs.superdoc.dev" target="_blank" rel="noopener">
@@ -170,7 +220,7 @@ export function App() {
170220
<div className="container">
171221
<div className="toolbar">
172222
<div className="toolbar-left">
173-
<span className="hint">Type {'{{'} to insert a field</span>
223+
<span className="hint">Type {"{{"} to insert a field</span>
174224
<span className="divider">|</span>
175225
<span className="hint">Tab/Shift+Tab to navigate</span>
176226
</div>
@@ -179,15 +229,15 @@ export function App() {
179229
type="file"
180230
accept=".docx"
181231
ref={fileInputRef}
182-
style={{ display: 'none' }}
232+
style={{ display: "none" }}
183233
onChange={handleFileInputChange}
184234
/>
185235
<button
186236
onClick={handleImportButtonClick}
187237
className="import-button"
188238
disabled={isImporting || isDownloading}
189239
>
190-
{isImporting ? 'Importing…' : 'Import File'}
240+
{isImporting ? "Importing…" : "Import File"}
191241
</button>
192242
<button
193243
onClick={handleExportTemplate}
@@ -217,6 +267,7 @@ export function App() {
217267
onFieldDelete={handleFieldDelete}
218268
onFieldSelect={handleFieldSelect}
219269
onFieldsChange={handleFieldsChange}
270+
onExport={handleExport}
220271
documentHeight="600px"
221272
/>
222273

@@ -225,11 +276,12 @@ export function App() {
225276
<div className="event-log">
226277
<div className="event-log-header">EVENT LOG</div>
227278
{events.map((evt, i) => (
228-
<div key={i} className="event-log-item">{evt}</div>
279+
<div key={i} className="event-log-item">
280+
{evt}
281+
</div>
229282
))}
230283
</div>
231284
)}
232-
233285
</div>
234286
</div>
235287
);

src/index.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ const SuperDocTemplateBuilder = forwardRef<
137137
onFieldsChange,
138138
onFieldSelect,
139139
onFieldCreate,
140+
onExport,
140141
className,
141142
style,
142143
documentHeight = "600px",
@@ -666,9 +667,16 @@ const SuperDocTemplateBuilder = forwardRef<
666667
triggerDownload,
667668
});
668669

670+
const editor = superdocRef.current?.activeEditor;
671+
if (editor) {
672+
const fields = getTemplateFieldsFromEditor(editor);
673+
const blob = triggerDownload ? undefined : (result as Blob);
674+
onExport?.({ fields, blob, fileName });
675+
}
676+
669677
return result;
670678
},
671-
[],
679+
[onExport],
672680
);
673681

674682
useImperativeHandle(ref, () => ({

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export interface TriggerEvent {
2323
cleanup: () => void;
2424
}
2525

26+
export interface ExportEvent {
27+
fields: TemplateField[];
28+
blob?: Blob;
29+
fileName: string;
30+
}
31+
2632
export interface FieldMenuProps {
2733
isVisible: boolean;
2834
position?: DOMRect;
@@ -116,6 +122,7 @@ export interface SuperDocTemplateBuilderProps {
116122
onFieldCreate?: (
117123
field: FieldDefinition,
118124
) => void | Promise<FieldDefinition | void>;
125+
onExport?: (event: ExportEvent) => void;
119126

120127
// UI
121128
className?: string;

0 commit comments

Comments
 (0)