Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .changeset/pagination-enabled-option.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@platejs/pagination": minor
---

Add an `enabled` option (default `true`) to toggle pagination at runtime. When `false`, the React layer skips layout recompute and renders no page-break overlay; the document is never affected either way. Toggle with `editor.setOption(BasePaginationPlugin, 'enabled', next)`.
10 changes: 10 additions & 0 deletions packages/pagination/src/lib/BasePaginationPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import { invalidateLayoutRegistry, shouldInvalidateLayout } from './registry';
export type PaginationViewMode = 'continuous' | 'paged';

export type PaginationOptions = {
/**
* Whether pagination is active. When `false`, the React layer skips layout
* recompute and renders no page-break overlay, leaving the editor untouched;
* the document is never mutated either way. Toggle at runtime with
* `editor.setOption(BasePaginationPlugin, 'enabled', next)`.
*
* @default true
*/
enabled: boolean;
page: PageSpec;
margins: PageMargins;
policies: LayoutPolicies;
Expand All @@ -36,6 +45,7 @@ export type PaginationConfig = PluginConfig<'pagination', PaginationOptions>;
// (cheapest, fully native edit surface — paged is the high-fidelity opt-in).
const DEFAULT_OPTIONS: PaginationOptions = {
atomicTypes: [],
enabled: true,
keepWithNextTypes: [],
margins: { bottomPx: 96, leftPx: 96, rightPx: 96, topPx: 96 },
page: { heightPx: 1123, preset: 'a4', widthPx: 794 },
Expand Down
17 changes: 17 additions & 0 deletions packages/pagination/src/lib/__tests__/BasePaginationPlugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ function editorWithPagination() {
}

describe('BasePaginationPlugin', () => {
it('enables pagination by default', () => {
const editor = editorWithPagination();

expect(editor.getOptions(BasePaginationPlugin).enabled).toBe(true);
});

it('can be disabled via options', () => {
const editor = createSlateEditor({
plugins: [
BasePaginationPlugin.configure({ options: { enabled: false } }),
],
value: [{ children: [{ text: 'hello' }], type: 'p' }],
});

expect(editor.getOptions(BasePaginationPlugin).enabled).toBe(false);
});

it('invalidates the layout registry on a content edit', () => {
const editor = editorWithPagination();
getLayoutRegistry(editor).dirty = false; // simulate a fresh build
Expand Down
8 changes: 7 additions & 1 deletion packages/pagination/src/react/PaginationPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ const labelStyle: React.CSSProperties = {
*/
const PaginationBreakLines: EditableSiblingComponent = () => {
const editor = useEditorRef();
const enabled = usePluginOption(PaginationPlugin, 'enabled');
const breaks = usePluginOption(PaginationPlugin, 'breaks');

const editable = editor.api.toDOMNode(editor);
if (!editable || breaks.length === 0) return null;
if (!enabled || !editable || breaks.length === 0) return null;
Comment on lines 72 to +73

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The enabled check should ideally occur before calling editor.api.toDOMNode(editor) to avoid unnecessary DOM lookups when pagination is disabled.

Suggested change
const editable = editor.api.toDOMNode(editor);
if (!editable || breaks.length === 0) return null;
if (!enabled || !editable || breaks.length === 0) return null;
if (!enabled) return null;
const editable = editor.api.toDOMNode(editor);
if (!editable || breaks.length === 0) return null;


const style = getComputedStyle(editable);
const padLeft = Number.parseFloat(style.paddingLeft) || 0;
Expand Down Expand Up @@ -136,6 +137,7 @@ export const PaginationPlugin = toPlatePlugin(BasePaginationPlugin, {
render: { afterEditable: PaginationBreakLines },
useHooks: ({ editor, setOption }) => {
const [, forceRecompute] = useState(0);
const enabled = usePluginOption(PaginationPlugin, 'enabled');

// Recompute when the layout registry is dirty (content edits via the base
// plugin's apply override; selection-only changes leave it clean). Runs in a
Expand All @@ -144,7 +146,11 @@ export const PaginationPlugin = toPlatePlugin(BasePaginationPlugin, {
// an extra frame later. setOption re-renders the overlay via usePluginOption.
// (The residual delay on first load is the editor's hydration time: the SSR
// content is on screen before the client can measure the DOM to place lines.)
// Skipped entirely while disabled; toggling `enabled` re-renders here (the
// subscribed option above), so re-enabling recomputes from the dirty registry.
useIsomorphicLayoutEffect(() => {
if (!enabled) return;

const registry = getLayoutRegistry(editor);
if (!registry.dirty && registry.output) return;

Expand Down