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

Commit 5dd71db

Browse files
chore: promote main to stable
2 parents 1d0b502 + 75c4184 commit 5dd71db

10 files changed

Lines changed: 709 additions & 33 deletions

File tree

.github/workflows/release-package.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ jobs:
6565
echo "Main is already up to date with stable; skipping PR."
6666
exit 0
6767
fi
68-
pr_number=$(gh pr create --base main --head stable --title "chore: sync stable release to main" --body "Automated PR to merge the latest stable changes back into main." --json number --jq '.number')
68+
pr_url=$(gh pr create --base main --head stable --title "chore: sync stable release to main" --body "Automated PR to merge the latest stable changes back into main.")
69+
pr_number=$(echo "$pr_url" | grep -oE '[0-9]+$')
6970
echo "Created PR #$pr_number to sync stable into main."
7071
else
7172
echo "PR #$pr_number already open to sync stable into main."

LICENSE

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,19 @@ function TemplateEditor() {
1818
return (
1919
<SuperDocTemplateBuilder
2020
document={{
21-
source: "template.docx",
22-
mode: "editing"
21+
source: 'template.docx',
22+
mode: 'editing',
2323
}}
24-
2524
fields={{
2625
available: [
2726
{ id: '1324567890', label: 'Customer Name', category: 'Contact' },
2827
{ id: '1324567891', label: 'Invoice Date', category: 'Invoice' },
29-
{ id: '1324567892', label: 'Amount', category: 'Invoice' }
30-
]
28+
{ id: '1324567892', label: 'Amount', category: 'Invoice' },
29+
],
3130
}}
32-
3331
onTrigger={(event) => {
3432
console.log('User typed trigger at', event.position);
3533
}}
36-
3734
onFieldInsert={(field) => {
3835
console.log('Field inserted:', field.alias);
3936
}}
@@ -58,7 +55,7 @@ function TemplateEditor() {
5855

5956
- **🎯 Trigger Detection** - Type `{{` (customizable) to insert fields
6057
- **📝 Field Management** - Insert, update, delete, and navigate fields
61-
- **🔍 Field Discovery** - Automatically finds existing fields in documents
58+
- **🔍 Field Discovery** - Automatically finds existing fields in documents
6259
- **🎨 UI Agnostic** - Bring your own menus, panels, and components
6360
- **📄 SDT Based** - Uses structured content tags for Word compatibility
6461
- **⚡ Simple API** - Clear callbacks for trigger events and field changes
@@ -74,32 +71,32 @@ function TemplateEditor() {
7471
source: File | Blob | string,
7572
mode: 'editing' | 'viewing'
7673
}}
77-
74+
7875
// Field configuration
7976
fields={{
8077
available: FieldDefinition[], // Fields user can insert
8178
initial: TemplateField[] // Pre-existing fields
8279
}}
83-
80+
8481
// UI components (optional)
8582
menu={{
8683
trigger: '{{', // Trigger pattern
8784
component: CustomFieldMenu // Custom menu component
8885
}}
89-
86+
9087
list={{
9188
position: 'left' | 'right', // Sidebar position
9289
component: CustomFieldList // Custom list component
9390
}}
94-
91+
9592
// Toolbar (optional)
9693
toolbar={true} // Render built-in toolbar container
9794
// toolbar="#my-toolbar" // Mount into existing element
9895
// toolbar={{ // Configure built-in toolbar
9996
// toolbarGroups: ['center'],
10097
// excludeItems: ['italic', 'bold'],
10198
// }}
102-
99+
103100
// Event handlers
104101
onReady={() => {}}
105102
onTrigger={(event) => {}}
@@ -126,8 +123,8 @@ ref.current.deleteField(fieldId);
126123

127124
// Navigation
128125
ref.current.selectField(fieldId);
129-
ref.current.nextField(); // Tab behavior
130-
ref.current.previousField(); // Shift+Tab behavior
126+
ref.current.nextField(); // Tab behavior
127+
ref.current.previousField(); // Shift+Tab behavior
131128

132129
// Get data
133130
const fields = ref.current.getFields();
@@ -141,10 +138,10 @@ const template = await ref.current.exportTemplate();
141138
```jsx
142139
const CustomFieldMenu = ({ isVisible, position, availableFields, onSelect, onClose }) => {
143140
if (!isVisible) return null;
144-
141+
145142
return (
146143
<div style={{ position: 'fixed', left: position?.left, top: position?.top }}>
147-
{availableFields.map(field => (
144+
{availableFields.map((field) => (
148145
<button key={field.id} onClick={() => onSelect(field)}>
149146
{field.label}
150147
</button>
@@ -162,8 +159,8 @@ const CustomFieldList = ({ fields, onSelect, onDelete, selectedFieldId }) => {
162159
return (
163160
<div>
164161
<h3>Fields ({fields.length})</h3>
165-
{fields.map(field => (
166-
<div
162+
{fields.map((field) => (
163+
<div
167164
key={field.id}
168165
onClick={() => onSelect(field)}
169166
style={{ background: selectedFieldId === field.id ? '#blue' : '#gray' }}
@@ -184,7 +181,7 @@ Enable Tab/Shift+Tab navigation:
184181
```jsx
185182
function TemplateEditor() {
186183
const ref = useRef();
187-
184+
188185
const handleKeyDown = (e) => {
189186
if (e.key === 'Tab') {
190187
e.preventDefault();
@@ -195,7 +192,7 @@ function TemplateEditor() {
195192
}
196193
}
197194
};
198-
195+
199196
return (
200197
<div onKeyDown={handleKeyDown}>
201198
<SuperDocTemplateBuilder ref={ref} {...props} />
@@ -219,7 +216,7 @@ const handleDownload = async () => {
219216

220217
// Or with custom filename
221218
await ref.current?.exportTemplate({
222-
fileName: 'invoice-template.docx'
219+
fileName: 'invoice-template.docx',
223220
});
224221
};
225222
```
@@ -233,7 +230,7 @@ const handleSave = async () => {
233230
// Get the blob without triggering download
234231
const blob = await ref.current?.exportTemplate({
235232
fileName: 'invoice-template.docx',
236-
triggerDownload: false
233+
triggerDownload: false,
237234
});
238235

239236
if (blob) {
@@ -243,7 +240,7 @@ const handleSave = async () => {
243240

244241
await fetch('/api/templates', {
245242
method: 'POST',
246-
body: formData
243+
body: formData,
247244
});
248245
}
249246
};
@@ -262,6 +259,7 @@ exportTemplate(config?: ExportConfig): Promise<void | Blob>
262259
```
263260
264261
**Return value:**
262+
265263
- `Promise<void>` when `triggerDownload: true` (download happens automatically)
266264
- `Promise<Blob>` when `triggerDownload: false` (returns the docx data)
267265
@@ -276,12 +274,12 @@ import type {
276274
FieldDefinition,
277275
TriggerEvent,
278276
ExportConfig,
279-
SuperDocTemplateBuilderHandle
277+
SuperDocTemplateBuilderHandle,
280278
} from '@superdoc-dev/template-builder';
281279

282280
const ref = useRef<SuperDocTemplateBuilderHandle>(null);
283281
```
284282
285283
## License
286284
287-
MIT
285+
AGPLv3

demo/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const availableFields: FieldDefinition[] = [
1616
{ id: '1242142773', label: 'Service Type' },
1717
{ id: '1242142774', label: 'Agreement Jurisdiction' },
1818
{ id: '1242142775', label: 'Company Address' },
19-
{ id: '1242142776', label: 'Signature' },
19+
{ id: '1242142776', label: 'Signature', mode: 'block' },
2020
];
2121

2222
export function App() {

demo/tsconfig.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ESNext",
5+
"moduleResolution": "bundler",
6+
"jsx": "react-jsx",
7+
"strict": true,
8+
"noEmit": true,
9+
"skipLibCheck": true
10+
},
11+
"include": ["src"]
12+
}

demo/vite.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ import react from '@vitejs/plugin-react';
44
export default defineConfig({
55
plugins: [react()],
66
base: '/',
7+
resolve: {
8+
dedupe: ['react', 'react-dom'],
9+
},
710
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"react"
4747
],
4848
"author": "SuperDoc Team",
49-
"license": "MIT",
49+
"license": "AGPLv3",
5050
"bugs": {
5151
"url": "https://github.com/superdoc-dev/template-builder/issues"
5252
},

src/defaults/FieldMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const FieldMenu: React.FC<FieldMenuProps> = ({
6161
const newField: FieldDefinition = {
6262
id: `custom_${Date.now()}`,
6363
label: trimmedName,
64-
metadata: { mode: fieldMode },
64+
mode: fieldMode,
6565
};
6666

6767
try {
@@ -416,7 +416,7 @@ export const FieldMenu: React.FC<FieldMenuProps> = ({
416416
flexShrink: 0,
417417
}}
418418
>
419-
{field.metadata?.mode || 'inline'}
419+
{field.mode || 'inline'}
420420
</span>
421421
</div>
422422
))}

src/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,13 +491,13 @@ const SuperDocTemplateBuilder = forwardRef<
491491
menuTriggerFromRef.current = null;
492492
resetMenuFilter();
493493

494-
const mode = (field.metadata?.mode as 'inline' | 'block') || 'inline';
494+
const mode = field.mode || 'inline';
495495

496496
if (field.id.startsWith('custom_') && onFieldCreate) {
497497
const createdField = await onFieldCreate(field);
498498

499499
if (createdField) {
500-
const createdMode = (createdField.metadata?.mode as 'inline' | 'block') || mode;
500+
const createdMode = createdField.mode || mode;
501501
insertFieldInternal(createdMode, {
502502
alias: createdField.label,
503503
metadata: createdField.metadata,

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface FieldDefinition {
55
label: string;
66
defaultValue?: string;
77
metadata?: Record<string, any>;
8+
mode?: 'inline' | 'block';
89
group?: string;
910
}
1011

0 commit comments

Comments
 (0)