@nextgensoftwares/folio-react
React UI: the paginated editor view, toolbar, menus, rulers, page setup and theme editor. Peer dependencies: react and react-dom ≥ 18.
import { FolioStyles, FolioToolbar, FolioView } from '@nextgensoftwares/folio-react';
import '@nextgensoftwares/folio-react/styles.css'; // or render <FolioStyles /> onceEditorOptions and FolioEditor are re-exported (as types) from @nextgensoftwares/folio-editor for convenience.
App
A complete editor app; guide: UI kit & theming.
const app = createFolioApp({ doc, measurer: fonts, plugins });
<FolioApp app={app} title={title} onTitleChange={setTitle} status="saved" />| Export | Description |
|---|---|
FolioApp, FolioAppProps | app bar (title, status, people, actions, theme), toolbar, page canvas, left/right panels (slots navigation / sidebar + a Settings tab from settings), status bar (statusbar slot + page, words, zoom), optional dock; responsive (drawers by its own width), light/dark |
createFolioApp(options), CreateFolioAppOptions | { doc, measurer, plugins?, schema?, layout?, renderMath?, reflow? } → FolioAppSetup: plugins composed once, incremental print/reflow layout (createLayoutSwitch), the editor |
FolioAppSetup, AppComposed | { editor, composed, metrics?, renderMath?, bodySize?, layouts? }; composed is Pick<ComposedPlugins, 'ui' | 'painters' | 'input' | 'pageLayers' | 'colors'> (partial) |
AppPanelOptions, AppDock | { slot?, label?, width?, defaultOpen? } for left/right; { title, open, onClose, children, width?, label? } for dock |
SaveStatus | 'saved' | 'saving' | 'unsaved' | 'offline' | 'error' | { label, tone?, title? } |
AppPerson | { name, color?, image?, status? } |
FolioAppBar, FolioAppButton | the app bar; its icon button (icon, label, onClick, pressed?) |
FolioAppTitle, SaveIndicator, FolioMark | editable title (title, onChange?, placeholder?); save-state dot + label; the Folio logo |
FolioAvatars, FolioAvatarsProps, personColor(name), initials(name) | overlapping avatars with a "+N" chip (people, max, size); a stable colour and initials for a name |
FolioAppPanel, AppPanelProps | a side panel: slot tabs (+ Settings sections) or fixed children; docked or a drawer |
FolioAppStatusBar, APP_STATUS_CELLS, AppStatusProps | the status bar; built-in cells app.page and app.words (slot items, hide or replace by id); cell props { layout, view } |
FolioTooltips | styled tooltips for every title inside .folio-ui (one listener set per page) |
useAppWidth(ref), useAppSize(ref), appSize(width), AppSize, APP_BREAKPOINTS | the app's own width and its class (wide ≥ 1200, medium ≥ 900, narrow ≥ 600, phone) |
usePanels(width, defaults, dock, onDockClose?, widths?), PanelState, PanelSide, MIN_MAIN | which panels dock (while the page keeps MIN_MAIN = 820 px) and which drawer is open |
View
<FolioView>
The paginated editor surface: virtualized pages, caret, selection, presence, input capture, context and slash menus.
| Prop | Type | Description |
|---|---|---|
editor | FolioEditor | null | the editor (null = read-only layout) |
layout | DocumentLayout | null | shown read-only when there's no editor |
painters | Record<string, unknown> | composePlugins(...).painters plus host painters; keep stable |
ui | { contextMenu?, slash?, slots?, arrangement?, surface?, surfaces? } | composePlugins(...).ui: plugin menu items, the overlay slot, routed surfaces (bubble) and the arrangement (hide/move/arrange) |
input | ViewInput | plugin paste/drop handlers (composePlugins(...).input) |
renderMath | MathRenderer | LaTeX → HTML (e.g. KaTeX renderToString) |
metrics | FontMetrics | ascent/descent per font, the same numbers layout used |
bodySize | number | font size for display math (default 16) |
zoom | ZoomMode | a factor or 'fit-width' (default 1) |
autoFit | boolean | shrink the host's initial numeric zoom so the page fits narrow containers, never scrolling sideways (default true); a zoom the user changed is honoured exactly |
onZoomChange | (zoom: ZoomMode) => void | enables Ctrl/⌘+wheel, pinch and Ctrl/⌘ + − 0 zoom, anchored at the pointer; see Zoom |
zoomGestures | boolean | gestures off while keeping onZoomChange (default true) |
layouts | LayoutSwitch | print / reflow switching (the editor's layout must be layouts.layout); see Responsive & mobile |
layoutMode | LayoutMode | overrides the switch's mode for this view |
mobileBar | boolean | MobileBarOptions | the phone formatting bar in the reflow column (default on for phones) |
compactWidth | number | below this container width (default 560 px; 0 disables) sticky rulers are hidden and data-compact is set |
touchSelect | boolean | touch: tap places the caret, long-press selects a word, drag extends, release opens the context menu (default true) |
appearance | PageAppearance | radius, shadow, gap, border, page colour, background |
remotes | readonly RemotePresence[] | collaborators' selections to draw |
placeholderPages | number | skeleton pages (faux text lines, shimmer) after the laid-out ones while streaming; arriving pages fade in |
scrollToPage | number | scroll to this 1-based page whenever it changes |
showBoxes, readOnly, contextMenu, slashMenu | boolean | debug boxes, read-only mode, enable menus |
pageLabel | (page) => ReactNode | small label under each page |
pageDecoration | (page) => ReactNode | drawn behind each page's content (guides, watermarks); keep stable |
pageLayers | readonly PageLayer[] | composePlugins(...).pageLayers: drawn under/over each visible page through the painter registry; keep the array stable |
colors | ColorAdapter | null | composePlugins(...).colors (dark mode, document themes): every painter colour, the page, canvas, caret, selection and remote cursors go through it; images/video get its mediaFilter. Pages repaint once when it notifies; display only (stored colours and exports never change) |
layerVariables | Record<string, string> | for page layers (shallow-compared) |
layerTarget | LayerTarget | target painted (default 'screen'; a print view passes 'print') |
stickyTop, stickyLeft | ReactNode | pinned rulers, aligned with the page column |
top | ReactNode | sticky area above the pages (toolbar, status bar) |
display | DisplayMode | 'scroll' (default), 'page' (one page at a time) or 'spread' (two side by side); see Reader & protection |
firstPageAlone, readingDirection | boolean, 'ltr' | 'rtl' | spread pairing (first page alone by default); order of spreads, arrow keys and swipes |
prefetch, pageTransition, pageButtons | number, 'slide' | 'fade' | 'none', boolean | paged modes: neighbours kept mounted (1), page-turn animation, side buttons |
onPageChange | (index) => void | the first visible page (0-based), in every mode |
pageAccess | PageAccess | which pages may be seen (ranges, budget, onPageView); locked pages render as placeholders, never mounted. UX only |
pageComponent | ComponentType<PageViewProps> | draws each page instead of PageView (e.g. @nextgensoftwares/folio-plugin-protect's canvas pages); keep stable |
className, style | container |
FolioViewHandle (via ref) extends ViewportApi: scrollTo(page, y?, { margin? }) (0-based page), viewportTop(), subscribe(fn), plus focus(), zoom() (after fit-width / auto-fit), compact(), slotHeight(), pageTop(index) (exact scroller offset of a page, also when sections give pages other sizes) and element().
| Export | Description |
|---|---|
FolioViewProps, FolioViewHandle, ViewportApi | types above |
InputCapture, InputHandle | the focused invisible textarea that turns keyboard, IME and clipboard input into transactions |
pasteInto(editor, { html?, text? }) | apply clipboard content at the selection |
usePageLayers(layers, totalPages, variables?, target?) | subscribes to live layers; returns a PageLayersState that changes only when a layer notifies or the page count changes (pages re-render, their content doesn't) |
PageLayers, PageLayersState | one page's layer fragments for one z, in a non-selectable, pointer-events: none, aria-hidden box |
pageWindow(scrollTop, viewHeight, slotHeight, pages, slots, overscan?) | O(1) page slots intersecting the viewport |
pageHeights(pages), pageStack(heights, slotH, zoom, gap), stackWindow(...), PageStack | mixed page sizes (sections): heights (null when uniform, the O(1) path), prefix-sum slot tops (top, at, end) and the binary-searched window |
fitWidthZoom(containerWidth, pageWidth, min?, max?) | zoom that fits a page to the container |
effectiveZoom(zoom, containerWidth, pageWidth, autoFit?), ResponsiveViewProps, COMPACT_WIDTH | the zoom the view actually uses (fit-width, or a factor shrunk to fit); the responsive props |
PAGE_PADDING | padding around the page column (px) |
PageView, PageViewProps, PointerKind | the built-in page component and the props every pageComponent receives |
usePaged(args), PagedState, DisplayViewProps | paged-mode state and its ViewportApi; the display props |
DisplayMode, DisplayOptions, NavAction | 'scroll' | 'page' | 'spread'; { firstPageAlone? }; 'next' | 'prev' | 'first' | 'last' | null |
shownPages, mountedPages, stepPage, spreadStart, clampPage, viewCount, applyNav | pure navigation: pages shown/mounted for a view, moves by pages or spreads |
keyAction(key, { rtl?, editing? }), swipeAction(dx, dy, ms, { rtl?, min? }) | keys and swipes → navigation |
Page access
| Export | Description |
|---|---|
PageAccess, PageRanges, PageStatus, LockReason, LockedPageProps | the window (allowed, maxVisible, onPageView, budgetKey, navigation, lockedPage, lockedAction, printLayout); statuses 'open' | 'outside' | 'budget' | 'pending' |
inWindow(allowed, index), accessTarget(isAllowed, navigation, from, to, total) | static window test; navigation target ('skip' to the nearest allowed page, or 'lock') |
PageBudget | distinct-page budget with host approval (request, status, subscribe) |
usePageAccess(access, layout), AccessView, accessApi(api, view, total), visibleIndices(...) | the window resolved for a layout; a ViewportApi whose scrollTo follows it; on-screen pages |
reflowUnits(screen, print) | screen page → print pages holding its blocks |
LockedPage, renderLocked(access, props) | the built-in placeholder; the host's lockedPage or it |
Reader
| Export | Description |
|---|---|
FolioReaderBar, FolioReaderBarProps | page X of Y, go to page, previous/next, slider, zoom (fit width / fit page), display-mode switch, built from reader slot items |
READER_ITEMS, ReaderSlotProps | built-in items reader.prev, reader.page, reader.next, reader.slider, reader.zoom, reader.mode; slot components get { reader } |
useReader(options), ReaderControls, ReaderOptions, ReaderTarget | reader state over any target with scrollTo / viewportTop / subscribe (a FolioView or image-pager ref) |
Types
| Type | Description |
|---|---|
PainterProps | { fragment: Fragment; page: PageLayout; editor: FolioEditor | null } |
FragmentPainter | ComponentType<PainterProps>; looked up by custom type, media:<type>, rect:<role>, then kind |
FontMetrics | (font: FontSpec) => { ascent; descent } |
MathRenderer | (latex: string, display: boolean) => string |
PageAppearance | { radius?, shadow?: 'none' | 'sm' | 'md' | 'lg' | string, gap?, border?, pageColor?, background? } |
RemotePresence | { name, color, anchor, head } |
ZoomMode | number | 'fit-width' |
ViewInput | { paste?(editor, data), drop?(editor, data, at) } |
Context and hooks
| Export | Description |
|---|---|
PaintContext, PaintEnv, usePaintEnv() | paint environment (metrics, math renderer, body size, debug boxes, color(c, role, on?), mediaFilter, readOnly from <FolioView readOnly>: painters hide editing controls) for custom painters |
PaintColor, identityColor | (color, role: ColorRole, on?: string) => string: a painter asks for the display colour of a document colour (on = the display background it sits on); identity when no colour adapter is active |
backdropAt(page, x, y, color, lines?) | the display colour painted under a page point (highlight / inline code behind text, else the topmost filled rect: code box, cell fill), or null for the page |
caretColorOn(background, color) | a caret colour that stays visible (≥ 3:1) on that background: the adapter's caret, else black/white. The view colours its caret this way, and tints selections over dark fills |
useDocColors(colors), DocColors | the view's hook: { color, mediaFilter, active }, re-evaluated only when the adapter notifies |
docColorVars(doc, appearance) | CSS variables (--folio-page-bg, --folio-canvas, --folio-page-fg, --folio-caret, --folio-selection, --folio-media-filter, dark page shadow) and whether the page is dark (.folio-doc-dark) |
defaultMetrics | 0.8 / 0.2 ascent/descent |
registerFocus(editor, fn), focusEditor(editor) | hand keyboard focus back to the editor after chrome interactions |
useEditorVersion(editor) | re-render on every editor change |
useDeferredEditorVersion(editor) | low-priority version for chrome: availability checks run after the keystroke painted |
Print / reflow layout and mobile
See Responsive & mobile. <FolioView> takes layouts, layoutMode and mobileBar (ReflowViewProps).
| Export | Description |
|---|---|
createLayoutSwitch(options), LayoutSwitch, LayoutSwitchOptions | owns an editor's layout across print and reflow: layout (pass it to FolioEditor), mode / setMode / subscribe, viewFor(width, mode?, screen?), setView(view), configure(options), printPageOf(block); progressive: progressive, window / setWindow, warm(doc, indices), finish(). One cache + previous per view (print + 2 reflow widths) |
LayoutView, LayoutFn, viewKey(view) | { mode: 'print' } or { mode: 'reflow', width, height? } (height = e-reader screen pages) |
ScreenPages | { height, spread?, gap? }: reflow cut into screen pages for page/spread display |
isReflowLayout(layout) | was this layout made for a reflow view |
screenTheme(theme, width, height) | the e-reader page theme (reflow theme with the document's pagination rules) |
LayoutMode, ReflowOptions, resolveLayoutMode(mode, width, pageWidth, o?, screen?) | 'print' | 'reflow' | 'auto' and the auto rule (breakpoint 720, minZoom 0.75, minBodySize, sheetHeight, floatMinWidth) |
REFLOW_BREAKPOINT, REFLOW_MIN_ZOOM | the defaults |
reflowWidth(containerWidth) | sheet width for a container (minus the view's padding) |
printLayoutItem(layouts, o?), printLayoutSlot(layouts), PrintLayoutToggle, isPrintLayout, togglePrintLayout | "Print layout" on/off as a toolbar item, a reader-bar slot item, a component |
screenPageSlot(layouts), screenPageInfo(editor, layouts, page) | "Screen 37 of ≈412 · print p. 112" in the reader bar |
MobileBar, MobileBarOptions | the phone formatting bar docked above the keyboard (essentials, plus GroupOptions) |
useKeyboardInset(enabled), keyboardInset(window), scrollForCaret(...) | on-screen keyboard tracking (visualViewport) and keeping the caret above it |
ReflowViewProps | layouts, layoutMode, mobileBar |
Zoom
<FolioView onZoomChange zoomGestures> (ZoomViewProps): Ctrl/⌘+wheel, trackpad and touch pinch, Ctrl/⌘ + − 0; CSS-scaled while the gesture runs, committed and re-anchored after 150ms.
| Export | Description |
|---|---|
ZoomControl, ZoomControlProps | − slider + and a percentage button with presets (50–200%, page width, whole page, two pages) |
useZoom(options), ZoomOptions, ZoomState, ZoomChoice | headless zoom state: zoom, percent, set(choice), zoomIn, zoomOut, reset, limits |
clampZoom(z), stepZoom(z, dir), fitPageZoom(viewW, viewH, pageW, pageH, across?) | 25%–400%, Word/Docs steps, fit a page (or two) |
ZOOM_MIN, ZOOM_MAX, ZOOM_PRESETS, ZOOM_STEPS | limits and levels |
zoomAnchor(geometry, pointer, z0, z1), ZoomGeometry | scroll position keeping the point under the pointer still |
zoomKeyAction(event), wheelFactor(deltaY, deltaMode) | the keyboard map and wheel → factor |
ZoomViewProps | onZoomChange, zoomGestures |
Painters
| Export | Description |
|---|---|
BUILTIN_PAINTERS | line, marker, rect, media, math |
createPainterRegistry(painters) | lookup built once per painter map; invalid entries dropped with a warning |
PainterRegistry | { get(fragment) } |
isComponent(x) | can React render it as a component? |
LinePainter, MarkerPainter, fontStyle(font) | text painting (baselines from metrics) |
RectPainter, MediaPainter, MathPainter, MissingPainter | built-in box painters |
rectFillRole(role) | the ColorRole a rect's fill plays: code boxes → codeBackground, quote borders/rules → border, cells and the rest → fill |
safeSrc(src) | only URLs that can't run script |
fragmentBox(f) | { left, top, width, height } of a fragment |
Menus
| Export | Description |
|---|---|
Menu, MenuHandle | keyboard-navigable menu of UIItems with group separators and submenus |
Popover, Anchor, placeBox, keepFocus | floating layer in a portal; placement that flips/clamps into the viewport; focus-preserving mousedown |
ContextMenu, ContextMenuState | right-click menu: built-in edit items plus plugin items |
SlashMenu | "/" menu filtered as you type (keys arrive through the handle; focus stays in the editor) |
builtinContextItems() | cut, copy, paste, delete, select all, table submenu |
builtinSlashItems(), slashTrigger(state), slashQuery(state, slash), SlashState | slash menu items and trigger detection |
isCommand, asCommand | tell commands from actions (by arity), or mark a command explicitly |
isVisible, isEnabled, isActive | evaluate when / enabled / active safely (errors reported, not thrown) |
runItem(item, ctx) | run an item against the live state |
visibleItems, groupItems, mergeItems, filterItems | drop malformed/hidden items, split by group, merge by id (later wins), fuzzy filter |
uiContext(editor, at?) | { editor, state: editor.uiState(), at } |
Toolbar and formatting
| Export | Description |
|---|---|
FolioToolbar, FolioToolbarProps | built-in formatting groups plus plugin items; re-renders at low priority. Props: editor, ui (composePlugins(...).ui: toolbar items, toolbar slot groups, arrangement), items (overrides ui.toolbar), extra, contextBar, className and GroupOptions |
ToolbarSlotProps | { state, run } passed to a toolbar slot component (which replaces a built-in group with the same id) |
builtinGroups(editor, state, run, options), BuiltinGroup, GroupOptions | the built-in groups (history, style, font, size, marks, color, script, link, align, lists, direction, insert, clear); options: fonts, defaultFontSize, blockStyles, textPalette, highlightPalette, renderMath, hide |
Toolbar, ToolbarGroup, fitGroups | single-row toolbar; groups that don't fit move into a "more" popover |
ToolButton, Separator, Dropdown, OptionList, Option, ItemButton | toolbar primitives (never steal focus) |
ContextBar | contextual row: items routed to contextBar (props editor, items = ui.surface('contextBar')) while any is visible, else table tools in a table, size/alignment for a selected image |
ColorPicker, TEXT_PALETTE, HIGHLIGHT_PALETTE | colour dropdown with palettes and a custom colour |
BordersControl, BORDER_PRESETS, SHADING_PALETTE | "Borders and shading" for the selected paragraphs (in the toolbar's align group): border presets (none, box, top, bottom, top and bottom, left bar) and a shading colour, set as the borders / shading attrs |
StylePicker, DEFAULT_BLOCK_STYLES, BlockStyle | paragraph/heading style picker |
FontFamilyPicker, FontSizeControl | family dropdown; Docs-style − [size] + |
CapsControl, CASE_OPTIONS, SPACING_OPTIONS, caseOf(style), casePatch(value) | letter case (all caps, small caps, all small caps) and character spacing dropdown in the script group; caseOf/casePatch map a textStyle to and from a case option |
LinkControl, normalizeHref | link editor; normalizes input to a safe href |
MathControl | deprecated: LaTeX input with preview (the built-in Insert-group control). Hidden when a toolbar item math.insert exists (@nextgensoftwares/folio-plugin-math), or with math={false} |
TablePicker | hover grid to insert a table |
TableStyleGallery | ({ state, run, theme? }): toolbar dropdown with a Word-like gallery of table styles (default grid, the built-ins and the accent styles from TABLE_STYLE_GALLERY), the look switches (header row, first/last column, banded rows/columns) and header repeat; disabled outside a table |
StylePreview | ({ name: string | null, look: Required<TableLook>, theme }): the 4×3 miniature of a table style under a look (fills, borders, header) that a gallery swatch shows; null = the default grid |
TablePanel, TablePanelProps | the inspector's "Table" tab (props editor, theme? for default padding): style and look, width, alignment, indent, header repeat, borders and padding, then CellFields and CellBorderFields; every control runs a table command. Shows a hint when the caret is not in a table |
tablePanelItem(options?) | { slot?, order?, theme? } → a UISlotItem (id table, default slot sidebar, order 20) that shows TablePanel while the caret is in a table |
CellFields | ({ state, run, defaultPad }): shading, padding (falling back to defaultPad), vertical alignment of the selected cells and the width of their columns; renders nothing outside a cell |
CellBorderFields | ({ state, run }): per-side borders of the selected cells: which sides (CellBorderSides), line style, width, colour, with Apply and Clear (back to the table's / its style's borders) |
cellAt(state) | the attrs of the table cell at the selection head, or null |
indent(editor, dir), canIndent(state, dir), shiftIndent(dir) | Word-like indent: nest list items, else shift paddingInlineStart |
insertJSON(json), insertTable(rows?, cols?), tableJSON(rows?, cols?, headerRow?) | insert nodes from JSON |
clearFormatting, deleteSelection | commands |
setHighlight(color), highlightAt(state), safeColor(v), toPt(size, fallback?), stepSize(pt, dir), FONT_SIZES | text-style helpers |
Routed surfaces and the bubble bar
| Export | Description |
|---|---|
FolioBubbleBar, FolioBubbleBarProps | <FolioBubbleBar editor ui? items? surface? root? label? className?>: floating toolbar above the selected node / text selection with the items routed to surface (default "bubble"); flips below, follows scroll/zoom, clamps to the viewport, hides while typing/dragging; Alt+F10 focuses it, arrows move, Escape returns. <FolioView> mounts one when ui.surfaces() includes "bubble" |
SurfaceSource | { surface?(name), arrangement? }: what the bubble reads from ComposedUI |
SurfaceItem | <SurfaceItem item ctx surface close?>: the item's render control (error-isolated) or an ItemButton |
UIRenderProps | { ctx, item, surface, close }: props of a UIItem.render component |
renderOf(item) | the item's render component, or null |
bubblePosition(target, size, viewport, gap?, margin?), BubblePlacement, Box, unionBox(boxes) | pure placement: centred above, flip below, clamp; null when the target is out of view |
useBubbleTarget(editor, root, inside), selectionBox(editor, root), BubbleRoot, isTypingKey(e) | the selection's viewport box on mounted pages (null while typing/dragging); typing-key test |
Guide: Routing controls to any surface.
Slots
| Export | Description |
|---|---|
FolioSlot, FolioSlotProps | <FolioSlot ui name editor props? builtins? wrap?>: renders a slot's visible, arranged items, each in an error boundary |
SlotTabs, SlotTabsProps | tabbed panel whose tabs are slot items: ui, name, editor, props, builtins, tab, onTab, label, empty, className |
TabStrip, TabStripProps, TabStripTab | panel tabs that never clip: every label when they fit, then icon-only inactive tabs (with tooltips), then a "more" menu; the active tab always stays visible. Props tabs (TabStripTab = { id, label, icon? }), current (id), onSelect(id), label (accessible name of the tablist), panelId? (aria-controls), className?. Roving tabindex: arrows (mirrored in RTL) and Home/End move and select |
fitTabs(sizes, active, avail, gap, more) | the strip's fit rule as a pure function: sizes are each tab's TabSize, avail/gap/more px (available width, between tabs, the "more" button) → TabFit. Tries all labels, then icon-only inactive tabs, then as many as fit (active first) with the rest overflowing; Infinity avail shows all |
TabSize, TabFit | { full, compact }: a tab's px width with its label and icon-only (equal without an icon); { shown: number[], overflow: number[], compact: boolean }: indices shown and moved to the "more" menu, and whether inactive tabs show only their icon |
useSlot(ui, name, editor, builtins?) | visible, arranged items of a slot (low-priority re-evaluation) |
renderSlotItem(item, editor, props?) | one item's component inside an error boundary |
slotVisible(item, ctx) | when(ctx), a throw counts as hidden |
SlotProps<P> | P & { editor, item }: what slot components receive |
SlotSource | { slots?, arrangement? }: the part of ComposedUI slots read |
ViewOverlay, OverlaySlotProps, PageRect | <FolioView> renders the overlay slot (when a plugin contributes to it) with { view, editor }: view.clientRect(pageRect) (viewport box of a page rect, null when its page isn't mounted), view.bounds() (the scroll area), view.subscribe(fn) (scroll/zoom), view.focus(). For floating UI anchored to content, e.g. @nextgensoftwares/folio-plugin-math's equation editor |
Guide: Customizing the UI.
Clipboard progress
| Export | Description |
|---|---|
ClipboardProgress | ({ editor }): the built-in toast for large copy/cut/paste tasks (progress, cancel, retry actions); mounted by the view's input layer unless clipboardTasks(editor).configure({ ui: false }) |
useClipboardTask(editor) | the current ClipboardTask (or null), re-rendering as it progresses: build your own progress UI |
clipboardTaskLabel(task) | the toast's text ("Copying 3,120 pages… 45%", "Pasting…", ...) |
Icons and styles
| Export | Description |
|---|---|
Icon | inline SVG icon (decorative; label the control) |
ICONS, iconPaths(name) | Folio's 24×24 stroke icon set; a name or a raw M… path |
FolioStyles, folioCss | inject the UI CSS (or import @nextgensoftwares/folio-react/styles.css) |
setFolioTheme(theme, root?), FolioTheme | 'light' | 'dark' | 'system' UI theme (pages stay paper-white) |
Inspector controls
Restrained, token-driven form controls for settings panels (the playground's rails, plugins' settings slot sections, the page-setup panel and the theme editor all use them). They follow the light/dark UI theme through the --folio-* tokens, stack their label above the control in panels narrower than ~230 px, and grow touch targets on coarse pointers. Wrap a panel in className="folio-controls" to give plain <select>/<input>/<textarea> inside it the same look.
import { Field, Section, Switch, Slider, Segmented, NumberInput, ColorInput, FileButton } from '@nextgensoftwares/folio-react';
<Section title="Watermark">
<Field label="Show"><Switch checked={on} onChange={setOn} /></Field>
<Field label="Opacity"><Slider min={0} max={1} step={0.01} value={o} format={(v) => `${Math.round(v * 100)}%`} onChange={setO} /></Field>
<Field label="Copies" group><Segmented value={mode} options={['one', 'count', 'tiled']} onChange={setMode} /></Field>
<Field label="Font size"><NumberInput value={size} unit="px" min={4} max={400} onChange={setSize} /></Field>
<Field label="Colour" group><ColorInput value={color} onChange={setColor} /></Field>
<Field label="Image" group><FileButton accept="image/*" onFile={upload}>Upload…</FileButton></Field>
</Section>| Export | Description |
|---|---|
Field, FieldProps | a labelled row: label, hint?, stacked?; a <label> around one control, group for several (role="group"), or htmlFor |
Section, SectionProps | uppercase header + body; collapsible <details> (open) or a plain <section> (collapsible={false}); actions on the right |
Inline | a row of controls |
TextInput, TextInputProps | text field (value, onChange(value) per keystroke: for local state and live previews) |
CommitInput, CommitInputProps | text field that commits instead: onCommit(text) on Enter, blur and unmount (a popover closing on an outside click), only when changed; Escape reverts (a second Escape reaches the popover; onCancel). Use it for anything that writes to the document |
useCommitField, CommitFieldProps | the same draft/commit cycle for a custom <input> (NumberInput, ColorInput, LengthInput, the font size box use it) |
CommitFieldOptions | { onCancel? }: useCommitField's third argument; onCancel runs after Escape reverted an edit (e.g. to close a popover) |
Select, SelectProps, SelectOption | styled native select; options are values or { value, label, disabled? } |
NumberInput, NumberInputProps | number with a unit suffix; commits on Enter/blur/unmount, ↑/↓ steps (Shift ×10), clamps to min/max, Escape reverts |
Switch, SwitchProps | role="switch" toggle |
Segmented, SegmentedProps | a radiogroup of a few choices (arrow keys move); block stretches it |
Slider, SliderProps | range with a filled track and a formatted readout (null hides it) |
ColorInput, ColorInputProps, toHex6 | swatch (opens the platform picker) + editable hex; toHex6 normalises #rgb(a)/#rrggbbaa for the picker |
Button, ButtonProps | variant: 'default', 'primary', 'ghost', 'danger' |
FileButton, FileButtonProps | a real button over a hidden file input; onFile(file), shows the chosen name |
Page setup
| Export | Description |
|---|---|
PageSetupPanel, PageSetupPanelProps | paper size, orientation, margins, header/footer bands, optional page look; controlled (value, onChange, unit, onUnitChange, look, onLookChange) |
PageSetupDialog, PageSetupDialogProps | modal: edits a draft, onApply(page, section?) once; with section (SectionApply: scope, columns) it adds Columns and "Apply to: This section / Whole document" |
ColumnsFields, ColumnsFieldsProps | number of columns, spacing, line between |
applySectionSetup(patch, scope), SectionSetupPatch, SetupScope | command: page setup / columns for the caret's section ('section', giving it an id) or every section ('document': section overrides dropped, document columns set); needs plugin-header-footer's schema (canSetSections(state)) |
caretSection(state) | the caret's section: index, block, key ('' = document level, null = no id yet), own |
sectionSetupOf(editor, theme) | what the dialog shows for the caret's section (value, section) |
applyPageSetup(editor, page, section, setDocumentPage) | applies a dialog result: the host's theme page for the whole document, else the caret's section |
pageSetupOf(page, theme, doc?) | a laid-out page's setup (its section's size and margins) for rulers |
PageLookFields, PagePreview, LengthInput, LengthInputProps | building blocks |
HorizontalRuler, HorizontalRulerProps | Word-style ruler that follows the caret's page (its section's size and margins; in newspaper columns it counts from the caret's column, and margin drags change that section): theme, zoom, unit, onMarginsChange, editor (indent markers, which may go into the margin for negative indents; tab stops: the selector box at the start picks left/center/right/decimal, a click adds a stop, drag moves, drag off removes, double-click picks the leader; default stops are ticked), guideExtent |
VerticalRuler, VerticalRulerProps | page, zoom, unit, onMarginsChange, offset, guideExtent |
RULER_THICKNESS | ruler size (px) |
PageLook, DEFAULT_PAGE_LOOK, PAGE_SHADOWS, pageLookStyle(look, zoom?), pageGuides(page), sanitizePageLook(input) | display-only page look |
PageSetup, Margins, Orientation, PageSizeId, MarginPresetId | types (PageSetup = Theme['page']) |
PAGE_SIZES, MARGIN_PRESETS, PAGE_LIMITS | presets and limits |
matchPageSize, matchMarginPreset, orientationOf | recognise presets |
applyPageSize, applyOrientation, applyMarginPreset, sanitizePageSetup, samePageSetup | transforms and validation |
rulerTicks, RULER_STEPS, snapDistance, startDistance, RulerUnit, Tick | ruler math |
dragMargin, MarginSide, dragIndent, indentMarkers, indentAttrs, IndentGeometry, IndentMarker | margin and indent drags (Word's first-line / hanging / left markers) |
caretParagraph(editor, theme), CaretParagraph | the caret's paragraph geometry from its laid-out line |
LengthUnit, LENGTH_UNITS, isLengthUnit, toPx, fromPx, roundIn, formatLength, parseLengthInput, clamp, pxToPt, ptToPx | units |
Theme editor
| Export | Description |
|---|---|
ThemeEditor, ThemeEditorProps, ThemeEditorTab | controlled typography editor: value, onChange, presets, fonts, commitDelay (400), preview, initialTab (presets, text, headings, blocks, json) |
ThemePreview | approximate instant CSS preview |
ThemeIO | export (download/copy) and validated import |
PresetList, THEME_PRESETS, ThemePreset, applyThemePreset(current, id, presets?) | presets (classic book, modern, academic, arabic academic); page setup kept |
validateTheme(input, base?), parseThemeJson(text, base?), ThemeValidation | untrusted theme data → { theme, errors, warnings, fatal }; never throws |
serializeTheme(theme, name?), THEME_FORMAT, THEME_FORMAT_VERSION | versioned JSON envelope (folio-theme, v1) |
isCssColor, isFontFamily | validators |
useThemeDraft(value, onChange, delay?) | debounced draft state for theme forms |