Skip to content

Commit f67e225

Browse files
authored
docs(structured-content): add lock mode documentation (#2077)
* docs(structured-content): add lock mode documentation Document the w:lock support for structured content nodes: - Add lockMode attribute and lock modes section to extension docs - Add StructuredContentLockMode type definition - Add code examples for inserting and updating lock modes - Update interactive demo with lock/unlock buttons - Update template builder docs with lockMode in field definitions * docs(template-builder): revert lock mode additions Template builder does not support lockMode in FieldDefinition or TemplateField types yet. Remove premature documentation. * fix(docs): remove lockMode from interactive demo buttons lockMode is not yet available in the published editor version. Restore original block field and update buttons that work today. * docs(structured-content): add lock/unlock buttons to interactive demo Add buttons to lock and unlock both inline and block fields using updateStructuredContentById with lockMode attribute. * docs(structured-content): improve interactive demo example text Replace placeholder quote with a realistic service agreement template that demonstrates practical use of inline and block fields. * docs(structured-content): use sdtLocked on insert buttons Insert inline and block fields with sdtLocked by default. Add sdtContentLocked lock buttons for both field types and a single unlock-all button. * docs(structured-content): add live SDT fields to interactive demo Use onReady callback to auto-insert inline and block sdtLocked fields into the example document on load, replacing placeholder text with real structured content fields. Reorganize buttons into logical groups: update actions, lock mode actions, and delete. * fix(docs): remove lockMode from onReady SDT insertions lockMode is not available in the published version yet (PR #1939). Remove it from onReady field insertions so the demo works with the current superdoc@latest on unpkg. * fix(docs): remove lock/unlock buttons from interactive demo Lock mode buttons depend on PR #1939 which isn't published yet. Remove them until lockMode is available in superdoc@latest. * fix(docs): restore lockMode in demo — feature is published lockMode is available in superdoc@latest (v1.13.1). Restore sdtLocked on onReady field insertions and lock/unlock buttons. * Revert "fix(docs): restore lockMode in demo — feature is published" This reverts commit 53638cd. * docs(structured-content): add toggle lock buttons for inline and block * fix(docs): access node.attrs for lock toggle state detection getStructuredContentTags returns {node, pos} objects — attrs are on node.attrs, not directly on the object. * fix(docs): use setNodeAttribute for lock toggle to bypass filterTransaction updateStructuredContentById uses replaceWith which creates a ReplaceStep that the lock plugin blocks. Use tr.setNodeAttribute instead, which creates an AttrStep that the lock plugin explicitly skips. * docs(structured-content): address review feedback - Differentiate lock mode insert example from command section example (use sdtContentLocked/Account ID instead of duplicate sdtLocked/Customer Name) - Wrap enforcement details in Expandable for scannability - Add missing `group` to block insert attrs type - Replace raw tr.setNodeAttribute with updateStructuredContentById in demo * docs(structured-content): remove toggle lock buttons from demo
1 parent 0f58118 commit f67e225

2 files changed

Lines changed: 289 additions & 57 deletions

File tree

apps/docs/extensions/structured-content.mdx

Lines changed: 223 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,140 @@ Node attributes that can be set and retrieved:
4646
Display name for the block
4747
</ParamField>
4848

49+
<ParamField path="lockMode" type="StructuredContentLockMode" default="unlocked">
50+
Controls editing and deletion restrictions on the structured content node. See [Lock modes](#lock-modes) below.
51+
</ParamField>
52+
53+
## Lock modes
54+
55+
Structured content nodes support four lock modes based on the OOXML [`w:lock` element](https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.lock) (ISO/IEC 29500 §17.5.2.23). Lock modes control whether users can edit the content inside a field and whether they can delete the field wrapper itself.
56+
57+
| Lock mode | Wrapper | Content | Use case |
58+
|-----------|---------|---------|----------|
59+
| `unlocked` | Deletable | Editable | Default — no restrictions |
60+
| `sdtLocked` | Protected | Editable | Protect field structure, allow value changes |
61+
| `contentLocked` | Deletable | Read-only | Display a computed value users can remove |
62+
| `sdtContentLocked` | Protected | Read-only | Fully protected field (e.g., system-generated ID) |
63+
64+
Set the lock mode when inserting:
65+
66+
<CodeGroup>
67+
```javascript Usage
68+
editor.commands.insertStructuredContentInline({
69+
attrs: {
70+
id: '3',
71+
alias: 'Account ID',
72+
lockMode: 'sdtContentLocked',
73+
},
74+
text: 'ACC-00042',
75+
});
76+
```
77+
78+
```javascript Full Example
79+
import { SuperDoc } from 'superdoc';
80+
import 'superdoc/style.css';
81+
82+
const superdoc = new SuperDoc({
83+
selector: '#editor',
84+
document: yourFile,
85+
onReady: (superdoc) => {
86+
const editor = superdoc.activeEditor;
87+
editor.commands.insertStructuredContentInline({
88+
attrs: {
89+
id: '3',
90+
alias: 'Account ID',
91+
lockMode: 'sdtContentLocked',
92+
},
93+
text: 'ACC-00042',
94+
});
95+
},
96+
});
97+
```
98+
</CodeGroup>
99+
100+
Change the lock mode on an existing field:
101+
102+
<CodeGroup>
103+
```javascript Usage
104+
editor.commands.updateStructuredContentById('1', {
105+
attrs: { lockMode: 'contentLocked' },
106+
});
107+
```
108+
109+
```javascript Full Example
110+
import { SuperDoc } from 'superdoc';
111+
import 'superdoc/style.css';
112+
113+
const superdoc = new SuperDoc({
114+
selector: '#editor',
115+
document: yourFile,
116+
onReady: (superdoc) => {
117+
const editor = superdoc.activeEditor;
118+
editor.commands.updateStructuredContentById('1', {
119+
attrs: { lockMode: 'contentLocked' },
120+
});
121+
},
122+
});
123+
```
124+
</CodeGroup>
125+
126+
<Expandable title="How lock enforcement works">
127+
Lock modes are enforced at the editor plugin level using a three-layer defense:
128+
129+
1. **Key interception** — Delete, Backspace, and Cut are blocked before a transaction is created, preventing cursor jumps
130+
2. **Text input blocking** — Typing is silently blocked in content-locked nodes
131+
3. **Transaction filter** — Safety net that catches paste, drag-drop, and programmatic edits
132+
133+
Users can still place their cursor inside locked content and select text for copying. Only modifications are blocked.
134+
</Expandable>
135+
136+
<Info>
137+
Lock modes round-trip through DOCX. A document with `w:lock` elements in
138+
its SDT properties will import with the correct lock mode and export the
139+
`w:lock` element back to the saved file.
140+
</Info>
141+
49142
## Commands
50143

51144
### `insertStructuredContentInline`
52145

53-
Inserts a structured content inline at selection.
146+
Inserts a structured content inline at the current selection.
147+
148+
**Example:**
149+
150+
<CodeGroup>
151+
```javascript Usage
152+
editor.commands.insertStructuredContentInline({
153+
attrs: {
154+
id: '1',
155+
alias: 'Customer Name',
156+
lockMode: 'sdtLocked', // optional, defaults to 'unlocked'
157+
},
158+
text: 'John Doe',
159+
});
160+
```
161+
162+
```javascript Full Example
163+
import { SuperDoc } from 'superdoc';
164+
import 'superdoc/style.css';
165+
166+
const superdoc = new SuperDoc({
167+
selector: '#editor',
168+
document: yourFile,
169+
onReady: (superdoc) => {
170+
const editor = superdoc.activeEditor;
171+
editor.commands.insertStructuredContentInline({
172+
attrs: {
173+
id: '1',
174+
alias: 'Customer Name',
175+
lockMode: 'sdtLocked',
176+
},
177+
text: 'John Doe',
178+
});
179+
},
180+
});
181+
```
182+
</CodeGroup>
54183

55184
**Parameters:**
56185

@@ -62,7 +191,43 @@ Inserts a structured content inline at selection.
62191

63192
### `insertStructuredContentBlock`
64193

65-
Inserts a structured content block at selection.
194+
Inserts a structured content block at the current selection.
195+
196+
**Example:**
197+
198+
<CodeGroup>
199+
```javascript Usage
200+
editor.commands.insertStructuredContentBlock({
201+
attrs: {
202+
id: '2',
203+
alias: 'Terms Section',
204+
lockMode: 'sdtContentLocked', // optional, defaults to 'unlocked'
205+
},
206+
html: '<p>These terms are non-negotiable.</p>',
207+
});
208+
```
209+
210+
```javascript Full Example
211+
import { SuperDoc } from 'superdoc';
212+
import 'superdoc/style.css';
213+
214+
const superdoc = new SuperDoc({
215+
selector: '#editor',
216+
document: yourFile,
217+
onReady: (superdoc) => {
218+
const editor = superdoc.activeEditor;
219+
editor.commands.insertStructuredContentBlock({
220+
attrs: {
221+
id: '2',
222+
alias: 'Terms Section',
223+
lockMode: 'sdtContentLocked',
224+
},
225+
html: '<p>These terms are non-negotiable.</p>',
226+
});
227+
},
228+
});
229+
```
230+
</CodeGroup>
66231

67232
**Parameters:**
68233

@@ -78,6 +243,53 @@ Updates a single structured content field by its unique ID.
78243
IDs are unique identifiers, so this will update at most one field.
79244
If the updated node does not match the schema, it will not be updated.
80245

246+
Pass `attrs` alone to change attributes (like `lockMode`) without replacing content:
247+
248+
**Example:**
249+
250+
<CodeGroup>
251+
```javascript Usage
252+
// Update content
253+
editor.commands.updateStructuredContentById('1', {
254+
text: 'Jane Smith',
255+
});
256+
257+
// Update lock mode only (preserves content)
258+
editor.commands.updateStructuredContentById('1', {
259+
attrs: { lockMode: 'contentLocked' },
260+
});
261+
262+
// Update both content and attributes
263+
editor.commands.updateStructuredContentById('1', {
264+
text: 'New value',
265+
attrs: { alias: 'Updated Label', lockMode: 'sdtLocked' },
266+
});
267+
```
268+
269+
```javascript Full Example
270+
import { SuperDoc } from 'superdoc';
271+
import 'superdoc/style.css';
272+
273+
const superdoc = new SuperDoc({
274+
selector: '#editor',
275+
document: yourFile,
276+
onReady: (superdoc) => {
277+
const editor = superdoc.activeEditor;
278+
279+
// Update content
280+
editor.commands.updateStructuredContentById('1', {
281+
text: 'Jane Smith',
282+
});
283+
284+
// Update lock mode only (preserves content)
285+
editor.commands.updateStructuredContentById('1', {
286+
attrs: { lockMode: 'contentLocked' },
287+
});
288+
},
289+
});
290+
```
291+
</CodeGroup>
292+
81293
**Parameters:**
82294

83295
<ParamField path="id" type="string" required>
@@ -456,6 +668,12 @@ const superdoc = new SuperDoc({
456668

457669
## Types
458670

671+
### `StructuredContentLockMode`
672+
673+
```typescript
674+
type StructuredContentLockMode = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked'
675+
```
676+
459677
### `StructuredContentInlineInsert`
460678
461679
<Expandable title="Properties">
@@ -466,7 +684,7 @@ const superdoc = new SuperDoc({
466684
ProseMirror JSON
467685
</ResponseField>
468686
<ResponseField name="attrs" type="Object">
469-
Node attributes
687+
Node attributes (`id`, `alias`, `tag`, `lockMode`, `group`)
470688
</ResponseField>
471689
</Expandable>
472690
@@ -480,7 +698,7 @@ const superdoc = new SuperDoc({
480698
ProseMirror JSON
481699
</ResponseField>
482700
<ResponseField name="attrs" type="Object">
483-
Node attributes
701+
Node attributes (`id`, `alias`, `tag`, `lockMode`, `group`)
484702
</ResponseField>
485703
</Expandable>
486704
@@ -497,7 +715,7 @@ const superdoc = new SuperDoc({
497715
Replace content with ProseMirror JSON (overrides html)
498716
</ResponseField>
499717
<ResponseField name="attrs" type="Object">
500-
Update attributes only (preserves content)
718+
Update attributes only (preserves content). Supports `lockMode`, `alias`, `tag`.
501719
</ResponseField>
502720
</Expandable>
503721

0 commit comments

Comments
 (0)