/** * Centralized data attributes used across the Blok editor. * This is the single source of truth for all data-blok-* attributes. * * Exported as Blok.DATA_ATTR for users. * * Naming convention: * - Remove 'data-blok-' prefix and convert to camelCase * - Global attributes: hidden, disabled, focused * - Component prefixed: popover, popoverItem, toolbar, etc. */ export const DATA_ATTR = { // ============================================ // Core Element Identifiers // ============================================ /** Interface type identifier (blok, inline-toolbar, tooltip) */ interface: 'data-blok-interface', /** Block element wrapper */ element: 'data-blok-element', /** Block element content wrapper */ elementContent: 'data-blok-element-content', /** Editor wrapper container */ editor: 'data-blok-editor', /** Per-instance discriminator on the editor wrapper (a monotonic counter, as a * string). Two editors on one page share every other scope attribute, so this * is what lets a page-level stylesheet — Blok's own injected `style.fontSize` * sheet, or a host rule — address ONE editor. Public styling hook. */ instance: 'data-blok-instance', /** Redactor zone */ redactor: 'data-blok-redactor', /** Present on the editor wrapper once a `blocks.render()` batch has finished * inserting blocks into the DOM; removed while a re-render is in flight. * Acts as a stable render-readiness gate for consumers (e.g. E2E waits). */ rendered: 'data-blok-rendered', /** Blok version number stamped on the editor wrapper (e.g. '1.10.0', 'dev'). * Consumed by browser extensions to identify the running version. */ version: 'data-blok-version', // ============================================ // Block Identifiers // ============================================ /** Block unique identifier */ id: 'data-blok-id', /** Block component/tool type */ component: 'data-blok-component', /** Tool type attribute */ tool: 'data-blok-tool', /** Block nesting depth (derived from the parentId chain) */ depth: 'data-blok-depth', /** Flat list-nesting indentation level (0 = root); tool-agnostic, mirrors list depth */ indent: 'data-blok-indent', /** Header tool's heading level (1-6). Public styling hook — keyed by level rather * than by tag name, so a level remapped to a custom tag via `levelOverrides[n].tag` * (types/tools/header.d.ts) still matches its level's typography rules. */ headingLevel: 'data-blok-heading-level', // ============================================ // Global States // ============================================ /** Element is hidden from view */ hidden: 'data-blok-hidden', /** Element is disabled and non-interactive */ disabled: 'data-blok-disabled', /** Element is focused via keyboard navigation */ focused: 'data-blok-focused', /** Block is selected */ selected: 'data-blok-selected', /** Block is stretched */ stretched: 'data-blok-stretched', /** Editor or element is empty */ empty: 'data-blok-empty', /** Present on the editor wrapper while read-only mode is active. * Public styling hook — lets hosts key rules off the editing state * without JS. Deliberately does NOT collapse the gutter: plain * read-only still shows the block-hover copy-link control there, and * in-place readOnly.set() flips must not shift the layout. */ readonly: 'data-blok-readonly', /** Present on the editor wrapper while read-only mode hides ALL editor * controls (readOnly: { hideControls: true }). Public styling hook — * drives the gutter auto-collapse for genuinely chromeless read-only. */ controlsHidden: 'data-blok-controls-hidden', /** Present on the editor wrapper when config.hideToolbar is true. * Public styling hook — drives the gutter auto-collapse (the gutter * exists solely to house the toolbar's +/⠿ controls). */ toolbarHidden: 'data-blok-toolbar-hidden', /** Which gutter the floating block controls occupy: 'left' (default, * inline-start) or 'right' (inline-end). Written on the editor wrapper from * config.toolbarPosition and kept in sync by `toolbar.setPosition()`. * Public styling hook — drives the gutter swap and the actions-bar side. */ toolbarPosition: 'data-blok-toolbar-position', // ============================================ // Editor Modes // ============================================ /** Content alignment mode (left, center, right) */ contentAlign: 'data-blok-content-align', /** Right-to-left mode */ rtl: 'data-blok-rtl', /** Editor content width mode (present with value "full" for wide mode; absent = narrow) */ width: 'data-blok-width', /** Present on the editor wrapper when config.style.nativeSelection is true. * Public styling hook — disables Blok's ::selection repaint (preflight.css) * and re-points the fake-background highlight at the UA Highlight color * (colors.css), so selection falls back to native/host-defined colors. */ nativeSelection: 'data-blok-native-selection', /** Present on the editor wrapper while a cross-block TEXT selection is painted. * Suppresses the engine's own ::selection paint (main.css) so the * ::highlight() sub-ranges are the only thing drawn — Chromium and Firefox * paint such a range natively too and would otherwise double it up. */ crossSelection: 'data-blok-cross-selection', // ============================================ // Drag and Drop // ============================================ /** Block is being dragged */ dragging: 'data-blok-dragging', /** Multiple blocks being dragged */ draggingMulti: 'data-blok-dragging-multi', /** Block is being duplicated (Alt+drag) */ duplicating: 'data-blok-duplicating', /** Drag handle element */ dragHandle: 'data-blok-drag-handle', // ============================================ // Toolbar // ============================================ /** Toolbar element */ toolbar: 'data-blok-toolbar', /** The floating block-controls bar (plus button + drag/settings handle) * inside the toolbar. Public styling hook — the side it docks to is driven * from the wrapper's `data-blok-toolbar-position`. */ toolbarActions: 'data-blok-toolbar-actions', /** Settings toggler button */ settingsToggler: 'data-blok-settings-toggler', /** Toolbox is open */ toolboxOpened: 'data-blok-toolbox-opened', /** Block settings is open */ blockSettingsOpened: 'data-blok-block-settings-opened', /** Element is opened (generic) */ opened: 'data-blok-opened', // ============================================ // Popover Container // ============================================ /** Root popover element */ popover: 'data-blok-popover', /** Popover container wrapper */ popoverContainer: 'data-blok-popover-container', /** Popover items list */ popoverItems: 'data-blok-popover-items', /** Custom, engine-independent scrollbar thumb overlaid on the popover items */ popoverScrollbar: 'data-blok-popover-scrollbar', /** Stamped on the custom scrollbar thumb while it is being dragged (keeps it revealed) */ popoverScrollbarDragging: 'data-blok-dragging', /** Stamped on a scroll container while it is actively scrolling (reveals the auto-hidden scrollbar thumb) */ scrolling: 'data-blok-scrolling', /** Popover overlay element */ popoverOverlay: 'data-blok-popover-overlay', /** Popover custom content area */ popoverCustomContent: 'data-blok-popover-custom-content', /** Popover custom class */ popoverCustomClass: 'data-blok-popover-custom-class', /** Inline popover variant */ popoverInline: 'data-blok-popover-inline', /** Popover is open */ popoverOpened: 'data-blok-popover-opened', /** Popover opens upward */ popoverOpenTop: 'data-blok-popover-open-top', /** Popover opens leftward */ popoverOpenLeft: 'data-blok-popover-open-left', // ============================================ // Popover Nesting // ============================================ /** Nested popover indicator */ nested: 'data-blok-nested', /** Nesting level value */ nestedLevel: 'data-blok-nested-level', /** Group label for promoted search results from nested children */ promotedGroupLabel: 'data-blok-promoted-group-label', /** Group label for top-level matches in search results */ topLevelGroupLabel: 'data-blok-top-level-group-label', // ============================================ // Popover Header // ============================================ /** Header container */ popoverHeader: 'data-blok-popover-header', /** Header text element */ popoverHeaderText: 'data-blok-popover-header-text', /** Back button in nested popover */ popoverHeaderBackButton: 'data-blok-popover-header-back-button', // ============================================ // Popover Items // ============================================ /** Item container */ popoverItem: 'data-blok-popover-item', /** Item icon wrapper */ popoverItemIcon: 'data-blok-popover-item-icon', /** Chevron icon for nested items */ popoverItemIconChevronRight: 'data-blok-popover-item-icon-chevron-right', /** Item title text */ popoverItemTitle: 'data-blok-popover-item-title', /** Item secondary title */ popoverItemSecondaryTitle: 'data-blok-popover-item-secondary-title', /** Item is active/selected */ popoverItemActive: 'data-blok-popover-item-active', /** Item's child menu is currently open — keeps the trigger looking selected */ popoverItemChildrenOpen: 'data-blok-popover-item-children-open', /** Confirmation state */ popoverItemConfirmation: 'data-blok-popover-item-confirmation', /** Disable hover styling */ popoverItemNoHover: 'data-blok-popover-item-no-hover', /** Disable focus handling */ popoverItemNoFocus: 'data-blok-popover-item-no-focus', /** Destructive action item (e.g. delete) */ popoverItemDestructive: 'data-blok-popover-item-destructive', /** Separator item */ popoverItemSeparator: 'data-blok-popover-item-separator', /** Separator line element */ popoverItemSeparatorLine: 'data-blok-popover-item-separator-line', /** HTML-based item */ popoverItemHtml: 'data-blok-popover-item-html', /** Item has child menu */ hasChildren: 'data-blok-has-children', /** Item name identifier */ itemName: 'data-blok-item-name', /** No search results shown */ nothingFoundDisplayed: 'data-blok-nothing-found-displayed', // ============================================ // Overlay / Selection // ============================================ /** Selection overlay */ overlay: 'data-blok-overlay', /** Overlay container */ overlayContainer: 'data-blok-overlay-container', /** Selection rectangle */ overlayRectangle: 'data-blok-overlay-rectangle', /** Overlay is hidden */ overlayHidden: 'data-blok-overlay-hidden', /** Fake cursor indicator */ fakeCursor: 'data-blok-fake-cursor', /** Fake background for selection */ fakeBackground: 'data-blok-fake-background', // ============================================ // Scroll // ============================================ /** Auto-scroll zone (top/bottom) */ scrollZone: 'data-blok-scroll-zone', /** Scroll is locked */ scrollLocked: 'data-blok-scroll-locked', /** Hard scroll lock */ scrollLockedHard: 'data-blok-scroll-locked-hard', // ============================================ // Caret // ============================================ /** Shadow caret element */ shadowCaret: 'data-blok-shadow-caret', // ============================================ // Placeholders // ============================================ /** Placeholder text */ placeholder: 'data-blok-placeholder', /** Active placeholder text */ placeholderActive: 'data-blok-placeholder-active', // ============================================ // Columns Layout // ============================================ /** The columns row rendered by the column_list tool (the flex container). * Public styling hook — its direct `[data-blok-element]` children are the * column holders, whose shrink floor reads `--blok-column-min-width` and * whose gutter reads `--blok-column-gutter`. */ columns: 'data-blok-columns', /** A single column inside a columns row. */ column: 'data-blok-column', /** Drag-to-resize separator between two adjacent columns. Present only in * edit mode — the separators ARE the gutter there. */ columnResizer: 'data-blok-column-resizer', /** Present on a columns row whose gutter comes from the container's own * column-gap instead of from resizer elements. Set in read-only mode, where * no resizers are built — the discriminator between an editable row and a * published one. */ columnsStaticGutter: 'data-blok-columns-static-gutter', // ============================================ // Nested Blocks // ============================================ /** Container that hosts nested block holders (table cells, toggle/callout/header children). * Used as a universal guard: before moving a block holder via appendChild, * check `holder.closest([nestedBlocks])` — if truthy, the holder is already * claimed by another container and must not be stolen. */ nestedBlocks: 'data-blok-nested-blocks', // ============================================ // Mutation Tracking // ============================================ /** Element excluded from mutation tracking */ mutationFree: 'data-blok-mutation-free', // ============================================ // Keyboard Ownership // ============================================ /** Marks a subtree whose keyboard belongs to the Tool that rendered it, not to * the editor. Blok's block-level keydown/keyup handling stands down entirely * for events originating inside it — Escape, Tab, the arrows, "/" and the * Enter/Backspace/Delete structural keys all reach the element untouched. * * Blok already exempts native ``/`