Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions apps/docs/core/react/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ All props are passed directly to the `<SuperDocEditor>` component. Only `documen
Hide the toolbar.
</ParamField>

<ParamField path="contained" type="boolean" default="false">
Enable contained mode for fixed-height container embedding. When `true`, SuperDoc fits within its parent's height and scrolls internally.

Your wrapper must have a definite height (e.g., `height: 400px`, `flex: 1`, or a CSS class with a fixed height). Pass `style={{ height: '100%' }}` to propagate the height.

```jsx
<div style={{ height: 500 }}>
<SuperDocEditor
document={file}
documentMode="viewing"
contained
style={{ height: '100%' }}
/>
</div>
```
</ParamField>

<ParamField path="rulers" type="boolean">
Show or hide rulers. Uses SuperDoc default if not set.
</ParamField>
Expand Down
36 changes: 36 additions & 0 deletions apps/docs/core/superdoc/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,42 @@ new SuperDoc({
```
</ParamField>

<ParamField path="contained" type="boolean" default="false">
Enable contained mode for fixed-height container embedding. When `true`, SuperDoc fits within its parent's height and scrolls internally instead of expanding to the document's natural height. Works with DOCX, PDF, and HTML documents.

Use this when embedding SuperDoc inside a panel, sidebar, or any container with a fixed height (e.g., `height: 400px` or `flex: 1`).

<CodeGroup>

```javascript Usage
const superdoc = new SuperDoc({
selector: '#editor',
document: file,
contained: true,
});
```

```html Full Example
<!-- Parent must have a definite height -->
<div style="height: 500px;">
<div id="editor"></div>
</div>

<script type="module">
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

new SuperDoc({
selector: '#editor',
document: yourFile,
contained: true,
});
</script>
```

</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
4 changes: 4 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pre-commit:
root: "apps/docs/"
glob: "apps/docs/**/*.mdx"
run: pnpm run test:examples
react-type-check:
root: "packages/react/"
glob: "packages/react/**/*.{ts,tsx}"
run: pnpm run type-check
generate-all:
glob: "packages/document-api/src/contract/**/*.ts"
run: pnpm run generate:all && git add apps/docs/document-api/reference apps/docs/document-api/overview.mdx apps/docs/document-engine/sdks.mdx
Expand Down
17 changes: 15 additions & 2 deletions packages/react/src/SuperDocEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,25 @@ function SuperDocEditorInner(props: SuperDocEditorProps, ref: ForwardedRef<Super
const wrapperClassName = ['superdoc-wrapper', className].filter(Boolean).join(' ');
const hideWhenLoading: CSSProperties | undefined = isLoading ? { display: 'none' } : undefined;

// contained is a SuperDoc config option passed through restProps.
// It's defined via JSDoc in superdoc's types and may not appear in the generated .d.ts.
const isContained = Boolean((restProps as Record<string, unknown>).contained);

const wrapperStyle: CSSProperties = {
...style,
...(isContained && { display: 'flex', flexDirection: 'column' as const }),
};

return (
<div className={wrapperClassName} style={style}>
<div className={wrapperClassName} style={wrapperStyle}>
{!hideToolbar && (
<div ref={toolbarContainerRef} id={toolbarId} className='superdoc-toolbar-container' style={hideWhenLoading} />
)}
<div id={containerId} className='superdoc-editor-container' style={hideWhenLoading} />
<div
id={containerId}
className='superdoc-editor-container'
style={{ ...hideWhenLoading, ...(isContained && { flex: 1, minHeight: 0 }) }}
/>
{isLoading && !hasError && renderLoading && <div className='superdoc-loading-container'>{renderLoading()}</div>}
{hasError && <div className='superdoc-error-container'>Failed to load editor. Check console for details.</div>}
</div>
Expand Down
26 changes: 25 additions & 1 deletion packages/super-editor/src/components/SuperEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ const isWebLayout = computed(() => {
return props.options.viewOptions?.layout === 'web';
});

const isContained = computed(() => {
return Boolean(props.options.contained);
});

/**
* Reactive ruler visibility state.
* Uses a ref with a deep watcher to ensure proper reactivity when options.rulers changes.
Expand Down Expand Up @@ -1160,7 +1164,11 @@ onBeforeUnmount(() => {
</script>

<template>
<div class="super-editor-container" :class="{ 'web-layout': isWebLayout }" :style="containerStyle">
<div
class="super-editor-container"
:class="{ 'web-layout': isWebLayout, contained: isContained }"
:style="containerStyle"
>
<!-- Ruler: teleport to external container if specified, otherwise render inline (hidden in web layout) -->
<Teleport
v-if="options.rulerContainer && rulersVisible && !isWebLayout && !!activeEditor"
Expand Down Expand Up @@ -1294,4 +1302,20 @@ onBeforeUnmount(() => {
overflow: hidden;
position: relative;
}

/* Contained mode: fixed-height container embedding with internal scrolling.
* The super-editor-container becomes the scroll container (overflow: auto).
* The .super-editor overflow is changed from hidden to visible so content
* flows through to the scroll container. The visibleHost (.presentation-editor)
* stays overflow: visible per PresentationEditor's design — it is NOT the scroller.
*/
.super-editor-container.contained {
height: 100%;
min-height: 0;
overflow: auto;
}

.super-editor-container.contained .super-editor {
overflow: visible;
}
</style>
12 changes: 12 additions & 0 deletions packages/superdoc/src/SuperDoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ const editorOptions = (doc) => {
disableContextMenu: proxy.$superdoc.config.disableContextMenu,
jsonOverride: proxy.$superdoc.config.jsonOverride,
viewOptions: proxy.$superdoc.config.viewOptions,
contained: proxy.$superdoc.config.contained,
linkPopoverResolver: proxy.$superdoc.config.modules?.links?.popoverResolver,
layoutEngineOptions: useLayoutEngine
? {
Expand Down Expand Up @@ -1338,6 +1339,7 @@ const getPDFViewer = () => {
:class="{
'superdoc--with-sidebar': showCommentsSidebar,
'superdoc--web-layout': proxy.$superdoc.config.viewOptions?.layout === 'web',
'superdoc--contained': proxy.$superdoc.config.contained,
'high-contrast': isHighContrastMode,
}"
:style="superdocStyleVars"
Expand Down Expand Up @@ -1528,6 +1530,16 @@ const getPDFViewer = () => {
position: relative;
}

/* In contained mode, overlay layers must not take flow space.
* With height:100% resolved on .superdoc__document, this element's
* position:relative + height:100% takes the full container height,
* pushing .superdoc__sub-document out of view. */
.superdoc--contained .superdoc__comments-layer {
position: absolute;
width: 100%;
pointer-events: none;
}

.superdoc__right-sidebar {
width: 320px;
min-width: 320px;
Expand Down
15 changes: 15 additions & 0 deletions packages/superdoc/src/assets/styles/elements/superdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,18 @@
.superdoc--web-layout .superdoc__sub-document {
width: 100%;
}

/* Contained Mode - fixed-height container embedding with internal scrolling */
.superdoc--contained,
.superdoc--contained .superdoc__layers,
.superdoc--contained .superdoc__document,
.superdoc--contained .superdoc__sub-document {
height: 100%;
}

/* Sub-document scroll fallback for non-SuperEditor content (PDF, HTML).
* SuperEditor manages its own scroll container via .super-editor-container.contained,
* but PDF/HTML viewers don't — they need the sub-document to scroll. */
.superdoc--contained .superdoc__sub-document {
overflow: auto;
}
4 changes: 4 additions & 0 deletions packages/superdoc/src/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@
* @property {boolean} [warnOnUnsupportedContent] When true and no onUnsupportedContent callback is provided, emits a console.warn with unsupported items
* @property {boolean} [isDebug=false] Whether to enable debug mode
* @property {ViewOptions} [viewOptions] Document view options (OOXML ST_View compatible)
* @property {boolean} [contained] Enable contained mode for fixed-height container embedding.
* When true, SuperDoc propagates height through its DOM tree and adds internal scrolling,
* so multi-page documents scroll within the consumer's fixed-height container.
* Default behavior (false) lets the document expand to its natural height.
* @property {string} [cspNonce] Content Security Policy nonce for dynamically injected styles
* @property {string} [licenseKey] License key for organization identification
* @property {{ enabled: boolean, endpoint?: string, metadata?: Record<string, unknown>, licenseKey?: string }} [telemetry] Telemetry configuration
Expand Down
Loading