Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7bad8f0
feat(superdoc): add layout-change event for responsive fit-to-contain…
mattConnHarbour May 28, 2026
f168de5
fix(superdoc): wait for layout ready before capturing base document w…
mattConnHarbour May 28, 2026
fd9f34b
test(superdoc): add tests for layout-change event
mattConnHarbour May 28, 2026
f174110
feat(superdoc): zoom modes with viewport metrics and fit-width (SD-3294)
caio-pizzol Jun 5, 2026
ac41f85
feat(react): zoom and viewport callback props (SD-3294)
caio-pizzol Jun 5, 2026
e2c93be
docs(superdoc): zoom modes, viewport-change event, fit-width config (…
caio-pizzol Jun 5, 2026
6472895
feat(superdoc): observable zoom-mode transitions and format-aware vie…
caio-pizzol Jun 5, 2026
74d41d5
feat(ui): zoom domain, useSuperDocZoom, and zoom-fit-width command (S…
caio-pizzol Jun 5, 2026
62476fb
Merge remote-tracking branch 'upstream/main' into matthew/sd-3294-lay…
mattConnHarbour Jun 5, 2026
cbe8261
Merge remote-tracking branch 'origin/matthew/sd-3294-layout-change-ev…
caio-pizzol Jun 5, 2026
4ae1d4f
test(superdoc): cover pdf normalization, widest-page, and fit-width c…
caio-pizzol Jun 5, 2026
90b768d
chore: refresh host-event prose and export snapshots after base merge…
caio-pizzol Jun 5, 2026
4192222
chore: restore base-side bytes the merge format hook rewrote (SD-3294)
caio-pizzol Jun 5, 2026
96ab4f8
fix(superdoc): key viewport-change dedup on fit, not raw available wi…
caio-pizzol Jun 5, 2026
d643fb9
fix(superdoc): blocker round for zoom modes from review (SD-3294)
caio-pizzol Jun 5, 2026
34077b0
fix(ui): zoom hygiene for mode-only emissions (SD-3294)
caio-pizzol Jun 5, 2026
4e14994
test(superdoc): cover viewport fit zoom edges
caio-pizzol Jun 5, 2026
f260c69
fix(superdoc): land the always-latest metrics store the prior message…
caio-pizzol Jun 5, 2026
3b4d535
docs(superdoc): width-source wording reflects laid-out pages first (S…
caio-pizzol Jun 5, 2026
1f6e463
docs(superdoc): tighten fit-width zoom docs
caio-pizzol Jun 5, 2026
09d00a6
test(superdoc): declare esModule interop on the async PdfViewer mock …
caio-pizzol Jun 5, 2026
1938ebb
Merge pull request #3659 from superdoc-dev/caio/sd-3294-fit-to-contai…
caio-pizzol Jun 5, 2026
6b2f044
Merge branch 'main' into matthew/sd-3294-layout-change-event
caio-pizzol Jun 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/docs/advanced/headless-toolbar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ Snapshot values match the format you pass to `execute()`. What you read is what
| `redo` | — | — |
| `ruler` | — | — |
| `zoom` | number (e.g. `125`) | number |
| `zoom-fit-width` | none | none |
| `document-mode` | `'editing'` \| `'suggesting'` \| `'viewing'` | mode string |

### Track changes
Expand Down
13 changes: 13 additions & 0 deletions apps/docs/editor/custom-ui/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ await ui.document.export({ exportType: ['docx'], commentsType: 'external', trigg
await ui.document.replaceFile(file);
```

### `ui.zoom`

Zoom state, viewport metrics, and the two mutations. The snapshot updates on value changes, mode-only transitions, and viewport metric updates.

```ts
ui.zoom.getSnapshot(); // { mode, value, fitZoom, min, max, metrics }
ui.zoom.observe((snapshot) => {});
ui.zoom.set(125); // numeric zoom; switches the host to manual mode
ui.zoom.setMode('fit-width'); // continuous fit to the available width
```

In React, `useSuperDocZoom()` returns the same snapshot plus bound `set` / `setMode` actions. The toolbar registry also exposes a `zoom-fit-width` toggle command for custom toolbars.

### `ui.selection`

Live slice, capture, restore, painted geometry.
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/editor/custom-ui/toolbar-and-commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Common ids you'll wire to buttons:
| Style | `linked-style`, `clear-formatting`, `copy-format` |
| History | `undo`, `redo` |
| Tracked changes | `track-changes-accept-selection`, `track-changes-reject-selection` |
| View | `ruler`, `zoom`, `document-mode` |
| View | `ruler`, `zoom`, `zoom-fit-width`, `document-mode` |
| Tables | `table-insert`, `table-add-row-before`, `table-add-row-after`, `table-delete-row`, `table-add-column-before`, `table-add-column-after`, `table-delete-column`, `table-merge-cells`, `table-split-cell`, `table-delete` |
| Insert | `image` |

Expand Down
82 changes: 82 additions & 0 deletions apps/docs/editor/superdoc/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,68 @@ new SuperDoc({
</CodeGroup>
</ParamField>

<ParamField path="zoom" type="Object">
Zoom behavior for the document. Use `mode: 'fit-width'` to keep DOCX and PDF documents fitted to the available container width. Calling `setZoom()` switches back to manual zoom.

<Expandable title="properties" defaultOpen>
<ParamField path="zoom.initial" type="number" default="100">
Initial zoom level as a percentage. In `fit-width` mode, this is the paint zoom until the first fit computes.
</ParamField>
<ParamField path="zoom.mode" type="'manual' | 'fit-width'" default="'manual'">
Starting zoom mode. `'manual'` holds the current value. `'fit-width'` keeps the document fitted to the container.
</ParamField>
<ParamField path="zoom.fitWidth" type="Object">
Bounds and padding for the applied fit-width zoom.
<Expandable title="properties">
<ParamField path="zoom.fitWidth.min" type="number" default="10">
Lower bound for the applied zoom percentage.
</ParamField>
<ParamField path="zoom.fitWidth.max" type="number" default="100">
Upper bound for the applied zoom percentage. The default never enlarges the document past its natural size; raise it to let wide containers scale the page up.
</ParamField>
<ParamField path="zoom.fitWidth.padding" type="number" default="0">
Horizontal padding in pixels reserved inside the available width before computing the fit.
</ParamField>
</Expandable>
</ParamField>
</Expandable>

For custom behavior, listen to [`viewport-change`](/editor/superdoc/events#viewport-change) and call `setZoom()` yourself.

<CodeGroup>

```javascript Usage
const superdoc = new SuperDoc({
selector: '#editor',
document: file,
zoom: {
mode: 'fit-width',
fitWidth: { min: 35, max: 100, padding: 24 },
},
});
```

```javascript Full Example
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const superdoc = new SuperDoc({
selector: '#editor',
document: yourFile,
zoom: {
initial: 100,
mode: 'fit-width',
fitWidth: { min: 35, max: 100, padding: 24 },
},
onZoomChange: ({ zoom, mode }) => {
console.log(`Zoom is now ${zoom}% (${mode})`);
},
});
```

</CodeGroup>
</ParamField>

<ParamField path="layoutMode" type="string" deprecated>
<Warning>**Removed in v1.0**: Use `viewOptions.layout` instead. `'paginated'` → `'print'`, `'responsive'` → `'web'`.</Warning>
</ParamField>
Expand Down Expand Up @@ -684,6 +746,26 @@ All handlers are optional functions in the configuration:
```
</ParamField>

<ParamField path="onZoomChange" type="function">
Called when zoom changes from `setZoom()`, the toolbar zoom control, or `fit-width` mode.

```javascript
onZoomChange: ({ zoom, mode }) => {
setZoomIndicator(zoom, mode);
}
```
</ParamField>

<ParamField path="onViewportChange" type="function">
Called when the fit-width calculation changes. Pixel-level width jitter is deduped. `getViewportMetrics()` always reads the latest measurements.

```javascript
onViewportChange: ({ availableWidth, documentWidth, fitZoom }) => {
updateFitIndicator({ availableWidth, documentWidth, fitZoom });
}
```
</ParamField>

<ParamField path="onTrackedChangeBubbleAccept" type="function">
Custom handler for accepting tracked changes from comment bubbles. Replaces default accept behavior when provided.

Expand Down
43 changes: 38 additions & 5 deletions apps/docs/editor/superdoc/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,12 @@ superdoc.on('pagination-update', ({ totalPages, superdoc }) => {

### `zoomChange`

When the zoom level changes via `setZoom()`.
When the zoom level changes, from any source: `setZoom()`, the toolbar zoom control, or [`fit-width` mode](/editor/superdoc/configuration#param-zoom). The payload carries the value and the mode that produced it. Also available as the `onZoomChange` config callback.

<CodeGroup>
```javascript Usage
superdoc.on('zoomChange', ({ zoom }) => {
console.log(`Zoom: ${zoom}%`);
superdoc.on('zoomChange', ({ zoom, mode }) => {
console.log(`Zoom: ${zoom}% (${mode})`);
});
```

Expand All @@ -458,8 +458,41 @@ const superdoc = new SuperDoc({
document: yourFile,
});

superdoc.on('zoomChange', ({ zoom }) => {
console.log(`Zoom: ${zoom}%`);
superdoc.on('zoomChange', ({ zoom, mode }) => {
console.log(`Zoom: ${zoom}% (${mode})`);
});
```
</CodeGroup>

### `viewport-change`

When the fit-width calculation changes. Pixel-level width changes that do not affect the rounded fit are deduped. `getViewportMetrics()` always reads the latest measurements.

- `availableWidth` - container width in pixels, minus the comments sidebar when visible
- `documentWidth` - the widest document page width in pixels at 100% zoom (zoom-independent; DOCX from laid-out pages with page-styles fallback, PDF from rendered pages)
- `fitZoom` - the unclamped zoom percentage that fits the page into the available width

HTML documents reflow to the container, so an HTML-only instance reports no metrics.

For most use cases, prefer [`zoom.mode: 'fit-width'`](/editor/superdoc/configuration#param-zoom). Subscribe to this event only when you want to apply custom zoom behavior.

<CodeGroup>
```javascript Usage
superdoc.on('viewport-change', ({ fitZoom }) => {
superdoc.setZoom(Math.min(100, Math.max(35, fitZoom)));
});
```

```javascript Full Example
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const superdoc = new SuperDoc({
selector: '#editor',
document: yourFile,
onViewportChange: ({ availableWidth, documentWidth, fitZoom }) => {
console.log(`Need ${fitZoom}% to fit ${documentWidth}px into ${availableWidth}px`);
},
});
```
</CodeGroup>
Expand Down
97 changes: 94 additions & 3 deletions apps/docs/editor/superdoc/methods.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ const superdoc = new SuperDoc({

### `setZoom`

Set the zoom level for all documents. Propagates to all presentation editors, PDF viewers, and whiteboard layers.
Set an explicit zoom level and switch zoom mode to `manual`. Use `setZoomMode('fit-width')` to turn automatic fitting back on.

<ParamField path="percent" type="number" required>
Zoom level as a percentage (e.g., `100`, `150`, `200`). Must be a positive finite number.
Expand All @@ -560,15 +560,106 @@ const superdoc = new SuperDoc({
onReady: ({ superdoc }) => {
superdoc.setZoom(150);

superdoc.on('zoomChange', ({ zoom }) => {
console.log(`Zoom changed to ${zoom}%`);
superdoc.on('zoomChange', ({ zoom, mode }) => {
console.log(`Zoom changed to ${zoom}% (${mode})`);
});
},
});
```

</CodeGroup>

### `setZoomMode`

Switch between manual zoom and automatic fit-width zoom. `'fit-width'` keeps DOCX and PDF documents fitted to the available container width, using the bounds from [`zoom.fitWidth`](/editor/superdoc/configuration#param-zoom). Calling `setZoom()` switches back to `'manual'`.

<ParamField path="mode" type="'manual' | 'fit-width'" required>
The zoom mode to switch to.
</ParamField>

<CodeGroup>

```javascript Usage
superdoc.setZoomMode('fit-width');
```

```javascript Full Example
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const superdoc = new SuperDoc({
selector: '#editor',
document: yourFile,
onReady: ({ superdoc }) => {
superdoc.setZoomMode('fit-width');

superdoc.on('zoomChange', ({ zoom, mode }) => {
console.log(`Zoom: ${zoom}% (${mode})`);
});
},
});
```

</CodeGroup>

### `getZoomState`

Get the current zoom mode, value, latest fit calculation, and effective fit bounds.

**Returns:** `SuperDocZoomState` - `{ mode, value, fitZoom, min, max }`. `fitZoom` is `null` before the first viewport measurement.

<CodeGroup>

```javascript Usage
const { mode, value, fitZoom } = superdoc.getZoomState();
```

```javascript Full Example
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const superdoc = new SuperDoc({
selector: '#editor',
document: yourFile,
onReady: ({ superdoc }) => {
const state = superdoc.getZoomState();
console.log(`Mode: ${state.mode}, value: ${state.value}%`);
},
});
```

</CodeGroup>

### `getViewportMetrics`

Get the latest fit-width measurements. Use this when you need custom zoom behavior instead of `zoom.mode: 'fit-width'`.

**Returns:** `SuperDocViewportMetrics | null` - `{ availableWidth, documentWidth, fitZoom }`, or `null` until the first measurement (editors still mounting).

<CodeGroup>

```javascript Usage
const metrics = superdoc.getViewportMetrics();
```

```javascript Full Example
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

const superdoc = new SuperDoc({
selector: '#editor',
document: yourFile,
onReady: ({ superdoc }) => {
const metrics = superdoc.getViewportMetrics();
if (metrics) {
superdoc.setZoom(Math.min(100, metrics.fitZoom));
}
},
});
```

</CodeGroup>

### `focus`

Focus the active editor or first available.
Expand Down
17 changes: 17 additions & 0 deletions apps/docs/getting-started/frameworks/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ function App() {
<SuperDocEditor document={file} role="editor" />
```

### Responsive zoom

Pass `zoom` with `mode: 'fit-width'` to keep the document fitted to its container as it resizes. SuperDoc observes the container for you; no resize listeners needed. Calling `setZoom()` (or the user picking a percentage in the toolbar) switches back to manual mode.

```jsx
<SuperDocEditor
document={file}
zoom={{
mode: 'fit-width',
fitWidth: { min: 35, max: 100, padding: 24 },
}}
onZoomChange={({ zoom, mode }) => console.log(`Zoom: ${zoom}% (${mode})`)}
/>
```

For custom behavior, listen to `onViewportChange` instead and apply your own zoom with `getInstance().setZoom()`. See [zoom configuration](/editor/superdoc/configuration#param-zoom).

## Handle file uploads

```jsx
Expand Down
Loading
Loading