Skip to content

Latest commit

 

History

History
1580 lines (1279 loc) · 88.4 KB

File metadata and controls

1580 lines (1279 loc) · 88.4 KB

Transition Progress

This file tracks the progress of the TableBerg architecture transition from block editor primitives (InnerBlocks) to a single block with faux blocks.

Tasks

Add frontend rendering via PHP

  • Status: In Progress
  • Notes:
    • Core table/cell PHP rendering infrastructure now exists and is being expanded element-by-element
    • Text and Button element renderers are integrated in CellRenderer
    • Image, List, Icon, Star Rating, and Custom HTML element PHP renderers are now added and wired in
    • Cell ribbon frontend rendering is now wired in from cellStyles.ribbon and per-cell ribbon overrides
    • Element/ribbon renderer coverage is complete
    • Frontend DataTable behavior is now complete (sorting, pagination, search)

Figure out pro version extension strategy

  • Status: Not started
  • Notes:

Convert Freemius to a Composer dependency

  • Status: Complete
  • Notes: Freemius SDK now installed via freemius/wordpress-sdk (v2.13.0). Bundled directories removed from both free and pro packages.

Add DataTable support

  • Status: Complete
  • Notes:
    • Column sorting: Editor-side and frontend implementation complete
    • Search: Editor-side and frontend implementation complete
    • Pagination: Editor-side and frontend implementation complete
    • All features treated as free during development — will separate pro features after all are done

Add dynamic data support (ACF, WooCommerce fields, other meta fields)

  • Status: Complete (editor side) — will revisit after PHP rendering is added
  • Notes:
    • Core infrastructure implemented (bindings stored at element level)
    • Backend auto-detects source: known post field keys are resolved directly, everything else is treated as post meta
    • REST API endpoints: /dynamic-data/preview (takes key + optional postId) and /dynamic-data/meta-keys
    • Editor UI (DynamicDataPanel) integrated into text and button elements
    • Collapsible binding cards showing attribute + field summary
    • Remaining: Frontend PHP rendering (blocked on PHP render task), Pro sources (ACF, WooCommerce)

Add remaining elements

  • Status: Complete
  • Notes: All elements implemented: Image, Star Rating, List, Icon, and Custom HTML. Cell ribbon frontend rendering is also implemented.

Add support for converting <v1 tables

  • Status: Complete
  • Notes:
    • Added a PHP-side TableBlockMigratorV0ToV1 that reads the legacy tableberg/cell inner block tree and converts it into transient v1 attrs before the existing v1 to v2 migration runs
    • Legacy top-level table attrs now map into v1 tableConfig, structure, data, and cellStyles, including row/column sizing, search, responsive settings, merged-cell spans, per-cell styles, and ribbons
    • Legacy child blocks are migrated into current cell elements: paragraph to text, button, image, core list, styled list, icon, star rating, custom HTML, and ribbon; dynamic-field wrappers are unwrapped to their child content during migration
    • Follow-up fix: TableBlockMigratorV1ToV2 now strips legacy cellStyles.cells before merging cell defaults so per-cell overrides do not leak into cellDefaults.styles

Remove all dangerouslySetInnerHTML usages

  • Status: Not started
  • Notes: Search the codebase for all occurrences of dangerouslySetInnerHTML and replace with safer alternatives (React components, sanitized HTML via dedicated utilities, etc.)

Consider Element Context for CellCoords and ElementIndex

In Progress:

  • Elements currently need to receive cellCoords and elementIndex as props, which is verbose and error-prone
  • Create an ElementContext (React Context) that provides:
    • cellCoords: { rowIndex, columnIndex } - position of the parent cell
    • elementIndex: number - position within the cell's elements array
  • Wrap element rendering in this context so child components can access coords via hook
  • Benefits:
    • Cleaner component signatures (no prop drilling)
    • Easier to add new attributes that need cell position info
    • Consistent access pattern across all elements
  • Implementation approach:
    • Create ElementContext provider in element renderer
    • useElementContext hook for consuming components
    • Update TextElement, ButtonElement, and future elements to use the context

Make block work inside non-iframed editor

  • Status: Complete
  • Notes: Tableberg now works inside the non-iframed editor as well as the iframed editor.

Mock/replicate block editor native features for table elements

  • Status: In progress
  • Notes: Non-iframed editor support is now in place, and the remaining work is to continue closing the gaps between faux table elements and native block editor behavior, including:
    • Block selection and focus states
    • Block mover controls
    • Block toolbar (for selected elements)
    • Block wrapper controls (alignment, dimensions, etc.)
    • Block inserter integration (drag and drop)
    • Block deletion and duplication
    • Nested block support (if applicable)
    • Block navigation/listing
    • Responsive preview controls
    • Any other iframe-specific functionality

Session Log

Record progress, decisions, and blockers here after each session.

2026-04-23: Sorting Control Shows All Columns (Editor)

Completed:

  • Updated the editor-side Column Sorting inspector control in packages/tableberg/src/controls.tsx so it no longer depends on a selected column
  • The panel now shows sorting toggles and sort-type selectors for every table column in one place
  • Refined the panel UI to use the same collapsible card pattern as DynamicDataPanel, with one summary row per column
  • Kept existing restrictions intact: sorting still requires a header row and remains unavailable for merged tables or for individual columns that contain cells with multiple elements

Validation:

  • pnpm --filter tableberg check:types passes

2026-04-20: List View Faux Element Drag Reorder Fix (Editor)

Completed:

  • Fixed faux element List View drag-and-drop reorder syncing in packages/tableberg/src/hooks/block-editor-compat/useSyncInnerBlocksShim.ts
  • After detecting a Gutenberg-side inner block reorder, the shim now immediately updates faux block elementIndex attributes to match the new block positions before the next sync pass
  • This prevents stale shim attributes from being interpreted as a second reorder request, which previously caused the actual cell element order to revert instead of persisting

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-15: Table Attr Shape Refactor (Editor + Renderer Parsing)

Completed:

  • Refactored the persisted table attrs in packages/tableberg/src/attributes.ts to the flatter top-level shape:
    • table
    • rows
    • columns
    • cells
    • bindings
    • cellDefaults
  • Moved row/column counts into table.rows / table.cols
  • Switched persisted cell storage to a record keyed by "row,col" and added helpers to derive the existing editor/runtime compatibility slices (structure, data, cellStyles) from the new attrs shape
  • Added the inverse mapping so store mutations that still operate on the compatibility slices now write back to the new persisted attrs shape
  • Changed element bindings to use per-element binding ID references plus a top-level bindings registry for batched resolution
  • Reworked the editor dynamic data panel to create/update/remove binding definitions through the shared store while still editing the selected element's binding refs
  • Updated the PHP table attr parser in packages/tableberg/renderer/Table/TableAttrs.php to accept the new persisted attrs shape and hydrate the existing renderer-facing structure / data / tableConfig / cellStyles objects from it
  • Added backward-compatible legacy defaults in packages/tableberg/renderer/Table/Defaults.php so existing renderer attr classes continue to parse against the derived compatibility structure

Follow-up:

  • Updated the PHP renderer to read the new persisted attrs shape directly instead of reconstructing legacy structure / data / tableConfig / cellStyles objects first
  • packages/tableberg/renderer/Table/TableAttrs.php now parses table, rows, columns, cells, bindings, and cellDefaults directly
  • packages/tableberg/renderer/Table/TableRenderer.php now reads row/column counts, column configs, cell defaults, and per-cell data directly from that new shape
  • Bumped the attr schema version to 2 and added an editor-side migration path from v1 attrs (structure / data / tableConfig / cellStyles) to the new v2 attrs shape before the table store initializes
  • Replaced the editor-side migration path with a PHP REST-response migrator in packages/tableberg/includes/Block_Attrs_Migrator.php, since Gutenberg strips unknown attrs before block edit JS receives them
  • Moved the PHP migration entry point into packages/tableberg/renderer/Migrations/ and split it into a versioned pipeline:
    • BlockContentMigrator handles REST-response rewriting for editor loads
    • TableBlockMigrator dispatches per-version migrations for tableberg/table
    • TableBlockMigratorV0ToV1 and TableBlockMigratorV1ToV2 run sequentially when needed

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l packages/tableberg/renderer/Table/TableAttrs.php
  • php -l packages/tableberg/renderer/Table/Defaults.php

2026-04-04: Legacy InnerBlocks Migration (Renderer)

Completed:

  • Replaced the placeholder packages/tableberg/renderer/Migrations/TableBlockMigratorV0ToV1.php with a real legacy migrator for pre-v1 Tableberg tables
  • Updated packages/tableberg/renderer/Migrations/BlockContentMigrator.php and packages/tableberg/renderer/Migrations/TableBlockMigrator.php so v0 migrations receive the parsed table block, not just the attr object, which lets the migrator read legacy tableberg/cell inner blocks and their child blocks
  • Added legacy v0 to v1 mapping for:
    • table config: header/footer flags, sticky header, width/alignment, search, responsive breakpoints, row/column counts
    • structure: merged cell spans, row heights, column widths
    • cell defaults: legacy padding, inner borders, border radius, zero/default element gaps
    • per-cell overrides: legacy row/column/cell backgrounds, borders, border radii, horizontal layout, wrapping, ribbons
    • cell content: paragraph to text, core list to list, styled list to styled list data, button, image, icon, star rating, custom HTML, ribbon, and dynamic-field wrapper unwrapping
  • Hardened packages/tableberg/renderer/Migrations/TableBlockMigratorV1ToV2.php so the transient legacy cellStyles.cells array is not copied into final cellDefaults.styles

Decisions:

  • Unknown legacy child blocks now fall back to custom-html when they have saved HTML, instead of being dropped silently
  • Legacy row/column styling is expanded to per-cell overrides during migration because the current attr model no longer stores row/column presentation separately
  • Legacy gradient-only cell/row/column backgrounds are not persisted when there is no flat color equivalent, since the current cell style schema only stores backgroundColor

Validation:

  • php -l packages/tableberg/renderer/Migrations/TableBlockMigrator.php
  • php -l packages/tableberg/renderer/Migrations/BlockContentMigrator.php
  • php -l packages/tableberg/renderer/Migrations/TableBlockMigratorV0ToV1.php
  • php -l packages/tableberg/renderer/Migrations/TableBlockMigratorV1ToV2.php
  • Targeted php -r migration smoke test covering legacy header attrs, paragraph/button conversion, ribbon extraction, and wrap/default handling

Follow-up:

  • Fixed legacy core/paragraph migration to read saved block HTML from parsed inner blocks when attrs.content is absent, preserving rich text markup during paragraph to text-element conversion
  • Fixed legacy core/list migration to read parsed core/list-item inner blocks instead of depending only on serialized list HTML, preserving nested list items during list-element conversion
  • Changed legacy inner-border migration to materialize explicit per-cell borders, including legacy innerBorderType and hideCellOutsideBorders behavior, instead of storing inner borders only in cellDefaults.styles.border
  • Fixed legacy border defaults so missing enableInnerBorder still behaves like the old block default (true), and missing innerBorder config still falls back to 1px solid #000000
  • Added legacy paragraph-to-text migration support for styles.backgroundColor now that the text element supports background color
  • Added a legacy v0 padding fallback so tables with no explicit cellPadding migrate to 10px cell padding instead of inheriting the newer 20 preset default

2026-04-05: Text Element Background Color

Completed:

  • Added styles.backgroundColor to the text element editor schema and inspector controls
  • Applied text element background color in both editor rendering and frontend PHP rendering so it persists outside Gutenberg
  • Exposed the new style as a bindable text element attribute for dynamic data usage

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l packages/tableberg/renderer/Text/TextAttrs.php
  • php -l packages/tableberg/renderer/Text/TextRenderer.php
  • php -l packages/tableberg/renderer/Text/Defaults.php

2026-04-05: Button Element Width Control

Completed:

  • Restored legacy-style button width behavior for the current button element by applying custom widths on an outer wrapper instead of the button node itself
  • Reused the old 25/50/75/100 percent width calculations so button sizing behaves correctly alongside block gaps and horizontal cell layouts
  • Matched the same width handling in the PHP button renderer so frontend output stays consistent with the editor

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l packages/tableberg/renderer/Button/ButtonRenderer.php

2026-03-12: Faux Element Block Registration Consolidation (Editor)

Completed:

  • Consolidated faux element block registration into a single shared module at packages/tableberg/src/faux-blocks/index.tsx
  • Moved faux element selection bridging into the shared faux element edit component so all faux element blocks now:
    • sync useBlockProps() into useFauxBlockPropsStore
    • resolve their parent tableberg/table block
    • reselect the parent table block when the faux element is selected
    • sync the active Tableberg element via setSelectedElement([row, col], elementIndex)
  • Updated packages/tableberg/src/index.tsx to register faux element blocks through the new shared module instead of importing one registration file per element
  • Removed the duplicated per-element faux block registration files under packages/tableberg/src/elements/*/block/index.tsx
  • Removed the now-unused legacy upsell shim registrations under packages/tableberg/src/upsells-blocks/
  • Follow-up refactor: moved faux element shim metadata and registration into a dedicated packages/tableberg/src/faux-blocks/ directory so faux block assets no longer sit inside the real element source directories or collide with the main elements module
  • Follow-up refactor: moved the faux cell shim registration and metadata into packages/tableberg/src/faux-blocks/cell/ for consistency with the faux element block layout

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-11: Faux Block useBlockProps Bridging for Actual Cells / Elements (Editor)

Completed:

  • Added a dedicated editor-only faux block props store at packages/tableberg/src/store/faux-block-props.tsx so hidden shim blocks can publish their useBlockProps() output without mixing ephemeral editor state into the main table data store
  • Wrapped block edit rendering with the new FauxBlockPropsProvider in packages/tableberg/src/index.tsx
  • Updated faux block edit components to call useBlockProps() and sync those props into the new store via useEffect:
    • packages/tableberg/src/cell/block/index.tsx
    • packages/tableberg/src/elements/*/block/index.tsx
  • Added helper hooks in packages/tableberg/src/hooks/useFauxBlockProps.ts and exported them from packages/tableberg/src/hooks/index.ts
  • Updated packages/tableberg/src/cell/index.tsx so:
    • unselected actual cells use the stored faux cell block props
    • unselected actual elements use the stored faux element block props
    • selected actual elements continue using the Tableberg block's local useBlockProps() behavior
    • wrapper className/style merging preserves both block-editor-provided props and Tableberg layout styles
  • Follow-up refactor: replaced shim cellIndex attrs with cellCoords objects ({ row, col }) across cell and faux element block metadata and sync logic, which removes editor bridge dependence on structure.cols and makes shim identity match the real table coordinate model more directly

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-11: Remaining Faux Element Block Registration (Editor)

Completed:

  • Added hidden faux block registrations for the remaining free cell elements so Gutenberg can represent them in the shim tree:
    • packages/tableberg/src/elements/image/block/*
    • packages/tableberg/src/elements/list/block/*
    • packages/tableberg/src/elements/icon/block/*
    • packages/tableberg/src/elements/star-rating/block/*
    • packages/tableberg/src/elements/custom-html/block/*
  • Registered the new faux element blocks from packages/tableberg/src/index.tsx
  • Expanded packages/tableberg/src/cell/block/block.json allowedBlocks so faux cell blocks accept all current free element block types
  • Updated packages/tableberg/src/hooks/block-editor-compat/useSyncInnerBlocksShim.ts so shim syncing maps each element type to its matching faux block name instead of falling back to text

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-11: Incremental Faux Inner Block Sync (Editor)

Completed:

  • Updated packages/tableberg/src/hooks/block-editor-compat/useSyncInnerBlocksShim.ts so faux inner blocks are synced incrementally instead of rebuilding the entire inner block tree on every change
  • Table cell shim blocks are now matched by cellIndex, then inserted, moved, or removed as table structure changes
  • Element shim blocks inside each faux cell are now matched by block type and position, then inserted, moved, removed, and attribute-synced instead of replacing the whole cell subtree
  • Zero-row or zero-column states now remove any existing faux inner blocks instead of leaving stale shim blocks behind
  • Added hidden InnerBlocks mounts for the table and faux cell blocks so Gutenberg List View can operate on the shim tree
  • List View reordering of faux element blocks now updates the corresponding cell element order in table data instead of snapping back on the next sync

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-10: Cell / Element Options Menu Cleanup + Element Actions (Editor)

Completed:

  • Cleaned up the block toolbar options menu for Tableberg faux cell/element blocks:
    • disabled lock, renaming, reusable, and visibility supports in faux block metadata:
      • packages/tableberg/src/cell/block/block.json
      • packages/tableberg/src/elements/text/block/block.json
      • packages/tableberg/src/elements/button/block/block.json
    • added editor-side menu shim in packages/tableberg/src/hooks/block-editor-compat/useBlockSettingsMenuShim.ts to hide unsupported or misleading options when a Tableberg cell/element is selected:
      • Group / Ungroup
      • Lock / Unlock
      • Rename
      • Create pattern
      • Hide / Show
      • Add note
    • intercepts the native Copy, Cut, Duplicate, Delete, Add before, and Add after menu items when an element is selected so they operate on Tableberg elements instead of faux inner blocks
  • Extracted shared clipboard helpers into packages/tableberg/src/hooks/block-editor-compat/elementClipboard.ts and reused them in both keyboard shortcut handling and native menu interception
  • Added new store actions in packages/tableberg/src/store/index.tsx:
    • duplicateElementInCell(coord, elementIndex) to clone the selected element immediately after itself
    • insertElementInCell(coord, elementIndex, element) to support native Add before / Add after menu actions with default text elements
  • Wired the new menu shim into packages/tableberg/src/index.tsx
  • Follow-up fix:
    • changed the shim to bind capture-phase handlers directly to the rendered menu items inside the popover instead of intercepting at document level, so native menu actions are reliably overridden before Gutenberg block actions run
    • hardened the shim to read the latest selected element/cell state from refs at interaction time so opening the options menu cannot tear down element-action interception during selection churn
    • fixed shim activation so it remains enabled while faux cell/element blocks are selected (the table block itself is not always isSelected in that state)
    • fixed menu hide/show handling so options are restored when no faux selection is active and hidden again when a faux selection is present
    • moved native menu action execution to mousedown/keyboard activation instead of relying on click, because popover mutations can happen between press and click and detach click-time interception
    • removed the native block settings menu shim entirely; Tableberg no longer modifies Gutenberg's native options menu for faux cell/element selections
    • added a dedicated element toolbar options dropdown (ElementOptionsButton) that uses ToolbarDropdownMenu and provides Tableberg-owned Copy, Cut, Duplicate, Add before, Add after, and Delete actions without depending on Gutenberg's native block settings popover
    • wired the new toolbar options button into free element toolbars: text, button, list, image, icon, custom HTML, and star rating

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-08: Element Cut / Copy / Paste Shortcuts (Editor)

Completed:

  • Added editor-side element clipboard shortcut handling in packages/tableberg/src/hooks/useElementClipboard.ts
    • supports Ctrl/Cmd+C to copy the selected element
    • supports Ctrl/Cmd+X to copy then remove the selected element
    • supports Ctrl/Cmd+V to:
      • replace the selected element when an element is selected
      • append the pasted element to the end of the first selected cell when only a cell is selected
  • Clipboard payloads now use a cheap-to-detect text prefix (TABLEBERG_ELEMENT:) before JSON-serialized element data
  • Added parsing/validation guards so only supported Tableberg cell elements are accepted from clipboard payloads
  • Added in-memory clipboard fallback so the shortcuts still work when the browser clipboard API is unavailable or blocked
  • Avoided intercepting native text clipboard behavior while editing RichText/contenteditable/input/textarea fields
  • Wired the new hook into block edit rendering in packages/tableberg/src/index.tsx and exported it from packages/tableberg/src/hooks/index.ts

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-08: Table Elements Section in Global Block Inserter (Editor)

Completed:

  • Added a new editor-side inserter extension that shows a Table elements section inside the global inserter Blocks browse panel when:
    • the global inserter is open
    • the current block context is the active Tableberg block (or one of its faux inner blocks)
    • a target exists via selected element cell or selected cell
  • Implemented new component at packages/tableberg/src/components/TableElementsInserterExtension/index.tsx:
    • detects active Tableberg context via core/block-editor selected block + parent chain
    • tracks inserter open state via core/editor
    • resolves insertion target as:
      • selected element's containing cell, otherwise
      • first selected cell
    • uses a MutationObserver + portal mount into the inserter Blocks tab container (.block-editor-inserter__block-list) so the section appears in the native inserter UI
    • reuses existing Tableberg element item definitions (cellInserterItems) and list UI
    • inserts elements via existing store action (addElementToCell) and element factory (createElement)
  • Wired extension rendering into block edit in packages/tableberg/src/index.tsx.
  • Added editor styles for the new inserter section in packages/tableberg/src/components/TableElementsInserterExtension/style.scss and imported into packages/tableberg/src/editor.scss.
  • Follow-up polish:
    • updated mount behavior to prepend the section at the top of the Blocks inserter panel via a dedicated mount node prepended to .block-editor-inserter__block-list
    • expanded useClickOutside chrome exclusions in packages/tableberg/src/hooks/useClickOutside.ts to treat inserter sidebar/menu/popover clicks as inside-editor interactions, preventing selection clear before element insert
    • expanded useClickOutside exclusions again to include top document-tools inserter toggle chrome (.editor-document-tools, .edit-post-header-toolbar, and inserter toggle class variants) so using the global +/x inserter button does not clear selected cell/element

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-08: Focus Newly Inserted Cell Element (Editor)

Completed:

  • Updated element insertion flow so a newly inserted cell element is selected immediately after insertion
  • Modified addElementToCell in packages/tableberg/src/store/index.tsx to:
    • compute the inserted element index for both existing and newly created cell data entries
    • set selectedElement to the inserted element's cell/index
    • clear selectedRibbonCell on insert so element focus is unambiguous
  • Kept cell selection behavior unchanged (selectedCells is preserved)

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-07: Responsive Controls + Frontend Responsive Behavior (Editor + PHP + View Script)

Completed:

  • Added responsive table config types/defaults to editor attributes:
    • tableConfig.responsive.tablet
    • tableConfig.responsive.mobile
    • Breakpoint fields: enabled, maxWidth, mode, direction, stackCount, headerAsCol
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
  • Added new responsive controls panel in inspector:
    • New component: packages/tableberg/src/components/ResponsiveControl/index.tsx
    • New editor styles: packages/tableberg/src/components/ResponsiveControl/style.scss
    • Imported via packages/tableberg/src/editor.scss
    • Added controls:
      • Device switcher (Desktop / Tablet / Mobile) using WordPress preview device APIs
      • Enable responsive rule
      • Max width (px)
      • Responsive mode (scroll / stack)
      • Stack-only options:
        • Transform rows to columns (direction row/col)
        • Show first column (headerAsCol)
        • Items per stack row (stackCount)
    • Wired into main controls rendering in packages/tableberg/src/controls.tsx
  • Added PHP parsing support for responsive config:
    • New parser classes in packages/tableberg/renderer/Table/TableAttrs.php:
      • ResponsiveBreakpointConfig
      • ResponsiveConfig
    • Wired into TableConfig::from_array() as $tableConfig->responsive
  • Added frontend data attributes output for responsive behavior:
    • packages/tableberg/renderer/Table/TableRenderer.php now outputs responsive metadata when any breakpoint is enabled:
      • data-tableberg-responsive
      • data-tableberg-rows, data-tableberg-cols
      • Per-device attrs for tablet/mobile (enabled, width, mode, direction, count, header)
  • Implemented frontend responsive behavior in view script:
    • New file: packages/tableberg/src/frontend/responsive.ts
    • Supports responsive modes:
      • scroll (horizontal table scrolling)
      • stack with direction: row
      • stack with direction: col
    • Handles resize transitions with revive logic when leaving responsive mode
    • Preserves base row/cell data attributes for rebuild/revive flow
    • Added init in packages/tableberg/src/view.ts
  • Added frontend responsive styles:
    • New file: packages/tableberg/src/frontend/responsive.scss
    • Imported in packages/tableberg/src/style.scss
  • Follow-up: added editor-side responsive visual preview wiring in packages/tableberg/src/table/index.tsx:
    • Reads Gutenberg preview device (Desktop / Tablet / Mobile)
    • Applies responsive preview classes in editor table wrapper based on active breakpoint mode/direction
    • Uses CSS preview styles for scroll + stack presentation during editor device preview
  • Follow-up: replaced stack editor preview approximation with actual stacked-row rendering in packages/tableberg/src/table/index.tsx:
    • Items Per Stack Row now controls real sub-row chunking in editor preview
    • Transform Rows to Columns now previews via transpose-before-stack behavior
    • Show First Column in Every Stack Row now repeats first cell per generated sub-row in preview
  • Follow-up: aligned frontend stack semantics in packages/tableberg/src/frontend/responsive.ts:
    • stackCount interpreted as max cells per generated stacked row
    • when first-column repeat is enabled, first column is included within that max (minimum effective row size of 2)
  • Follow-up: renamed responsive breakpoint fields for clarity:
    • replaced direction (row/col) with boolean transpose
    • replaced headerAsCol with repeatFirstCol
    • updated editor types/defaults, block defaults, PHP defaults/parser, renderer data attrs, and frontend parser/logic
    • backward compatibility: legacy direction / headerAsCol are still read as fallbacks

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l passes for:
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Table/Defaults.php

2026-03-08: Fix Merge Cells in Non-Iframed Editor Mode

Completed:

  • Fixed selection clearing race in non-iframed editor mode that prevented cell merging from working when clicking toolbar controls
  • Updated packages/tableberg/src/hooks/useClickOutside.ts to ignore clicks inside editor chrome containers:
    • .block-editor-block-toolbar
    • .block-editor-block-popover
    • .components-popover
    • .interface-interface-skeleton__sidebar
  • Kept outside-click behavior for true outside clicks while preserving cell selection during toolbar/sidebar interactions
  • Hardened hook event handling by:
    • early return when ownerDocument is unavailable
    • handling non-Element event targets safely
    • using a stable ownerDocument reference for add/remove listener symmetry

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-08: Inserted Row Cells Initialize with Text Element (Editor)

Completed:

  • Updated row insertion flow so every newly created cell in an inserted row starts with a blank Text element instead of an empty elements array
  • Extended insertRowAt in packages/tableberg/src/table-structure.ts with an optional default element parameter
  • Added store wiring in packages/tableberg/src/store/index.tsx so insertRow passes createElement("text") into insertRowAt
  • Kept merged-cell behavior intact: cells covered by row spans are still skipped and not overwritten

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-06: Row Height (Editor + PHP Frontend)

Completed:

  • Added row-level config support in table structure:
    • structure.rowConfigs[*].height (optional string)
    • Added RowConfig type and rowConfigs map in packages/tableberg/src/attributes.ts
  • Added editor store actions in packages/tableberg/src/store/index.tsx:
    • setRowHeight(row, height)
    • getRowConfig(row)
    • Mirrors column-width cleanup behavior by removing empty row config objects
  • Added inspector UI in packages/tableberg/src/controls.tsx:
    • New Row Height panel
    • Requires a single selected cell to target its row
    • Height Mode toggle (Auto / Fixed)
    • Row Height size control shown in Fixed mode (default 50px)
    • Follow-up: grouped row and column sizing controls into a shared Column & Row Dimensions panel
  • Updated editor cell rendering in packages/tableberg/src/cell/index.tsx:
    • Applies row height as height + min-height when configured
    • Skips applying row height on cells with rowSpan > 1
  • Updated frontend PHP parsing/rendering:
    • packages/tableberg/renderer/Table/TableAttrs.php parses structure.rowConfigs via new RowConfig class
    • packages/tableberg/renderer/Table/TableRenderer.php resolves per-row height and passes it into cell render context
    • packages/tableberg/renderer/Cell/CellRenderContext.php now carries row height
    • packages/tableberg/renderer/Cell/CellRenderer.php applies height + min-height when rowSpan === 1

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Cell/CellRenderContext.php
    • packages/tableberg/renderer/Cell/CellRenderer.php
  • pnpm --filter tableberg check:types passes

2026-03-06: Block Spacing / Element Spacing (Editor + PHP Frontend)

Completed:

  • Added new cell style field elementGap (string) for spacing between elements within a cell
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
    • Default value is now var(--wp--preset--spacing--20)
  • Added editor UI control in packages/tableberg/src/controls.tsx:
    • New Element Spacing control in the Cell Elements panel via SpacingControlSingle
    • Scope behavior matches other cell-style controls:
      • applies to selected cells when a selection exists
      • otherwise applies as the global cell style default
  • Updated editor cell rendering in packages/tableberg/src/cell/index.tsx:
    • resolves effective elementGap from per-cell override -> global cell style -> defaults
    • passes spacing into CellElementRenderer
    • applies spacing via flex gap on .tableberg-cell-elements
  • Updated PHP frontend rendering/parsing:
    • packages/tableberg/renderer/Table/TableAttrs.php now parses cellStyles.elementGap
    • packages/tableberg/renderer/Table/TableRenderer.php now includes elementGap in global cell render context
    • packages/tableberg/renderer/Cell/CellRenderContext.php now carries resolved elementGap
    • packages/tableberg/renderer/Cell/CellRenderer.php now:
      • applies wrapper gap style to rendered cell elements
      • supports per-cell elementGap override via overrideStyles

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l passes for updated renderer files

2026-03-06: Left-Side Table Edit Toolbar + Row/Column Actions (Editor)

Completed:

  • Added new editor-side table edit toolbar component at packages/tableberg/src/components/TableEditToolbar/index.tsx
    • Toolbar is vertical and floats to the left of the table, top-aligned
    • Shown when the table block is selected, a cell is selected, and sort preview mode is off
    • Added block-toolbar toggles to independently show/hide:
      • Row/column edit controls (insert/delete)
      • Duplicate/move controls
    • Includes controls:
      • Insert row before / after
      • Delete row
      • Insert column before / after
      • Delete column
      • Duplicate row / duplicate column
      • Move row up / down
      • Move column left / right
    • Duplicate/move controls are currently disabled when the table has merged cells
  • Added toolbar styles in packages/tableberg/src/components/TableEditToolbar/style.scss and imported in packages/tableberg/src/editor.scss
  • Added hover preview behavior for toolbar row/column operations:
    • Hovering insert/duplicate row/column actions now shows insertion guides between rows/columns (Block Editor-style, single-entry animation)
    • Duplicate hover also highlights the source row/column being copied
    • Hovering delete row/column actions now tints the affected row/column red
    • Edge insertion (after last row/column) shows the same animated guide on the outer boundary
    • Implemented via new tableEditPreview store state and cell preview classes/styles in packages/tableberg/src/cell/toolbar-preview.scss
  • Updated table-edit toolbar button tooltips for better readability/interaction:
    • Replaced default per-button tooltip behavior with a custom anchored tooltip layer
    • Added short hide grace period to reduce flicker when moving across toolbar buttons
    • Applied stronger tooltip text weight via tableberg-edit-toolbar-tooltip
    • Slightly increased toolbar button hit area to reduce accidental hover loss
  • Integrated toolbar into block edit rendering in packages/tableberg/src/index.tsx
    • Added tableberg-editor-shell wrapper class for positioning context
    • Renders toolbar within the table figure when conditions match
  • Added new structure mutation helpers in packages/tableberg/src/table-structure.ts:
    • insertRowAt, deleteRowAt, insertColumnAt, deleteColumnAt
    • Handles merged cell span adjustments (rowSpan / colSpan) and coordinate shifting
    • Updates row/column config indices when inserting/deleting
  • Added new store actions in packages/tableberg/src/store/index.tsx:
    • insertRow, deleteRow, insertColumn, deleteColumn
    • Applies structure/data/style updates through the new helpers
    • Resets element/ribbon selection and moves cell selection to a valid resulting cell

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-06: Bulk Element Font Options Controls (Editor)

Completed:

  • Added new inspector panel Element Font Options in packages/tableberg/src/controls.tsx directly after the Cell Elements panel
  • Added three stateless action controls that open popovers (no persisted control state/value display):
    • Set elements' text color
    • Set elements' link color
    • Set elements' font size
  • Updated dropdown actions to use a custom Reset button label:
    • disabled built-in picker clear/reset actions
    • added always-enabled Reset action that applies element default values (#000000, "", 1.38rem) for the relevant option across scoped cells
  • Updated UI styling for the new controls:
    • trigger rows now visually match color control rows (without color indicators)
    • removed spacing between the three font option rows (stacked contiguous controls)
    • custom Reset action now uses the same clear-action visual style as WordPress picker clear buttons
  • Implemented bulk apply behavior for typography-related elements only:
    • Text element (styles.textColor, styles.linkColor, styles.fontSize)
    • List element (styles.textColor, styles.linkColor, styles.fontSize)
    • Star rating element (reviewTextColor, reviewTextLinkColor, reviewTextFontSize)
  • Scope behavior matches existing helper controls:
    • applies to selected cells when a selection exists
    • otherwise applies to all cells in the table

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-06: Toggle Block Migration (Pro -> Free)

Completed:

  • Migrated toggle block source from pro package into free package at packages/tableberg/src/blocks/toggle
    • Added block.json, index.tsx, style.scss, editor.scss, and frontend.ts
  • Added block-local asset declarations in toggle metadata (packages/tableberg/src/blocks/toggle/block.json):
    • editorScript: file:./index.tsx
    • editorStyle: file:./index.css
    • style: file:./index.css
    • viewScript: file:./frontend.ts
  • Added server-side registration + rendering for free toggle block:
    • Added packages/tableberg/renderer/ToggleBlockRenderer.php with toggle render callback
    • Registered build/blocks/toggle/block.json in packages/tableberg/tableberg.php with ToggleBlockRenderer::render_toggle_block
  • Removed global toggle imports from free table block entrypoints (index.tsx, style.scss, editor.scss, view.ts) so toggle assets are handled by toggle block metadata
  • Updated migrated toggle code to align with free package APIs:
    • switched spacing helper import to shared package (@tableberg/shared/utils/styling-helpers)
    • adapted ColorControl props to use value / onChange
    • removed stray o; statement from tab removal handler

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-05: Implicit Cell Elements Alignment + Per-Element Align Migration (Editor + PHP Frontend)

Completed:

  • Reworked cell elements alignment architecture to be implicit (no persisted cellStyles.justification):
    • Removed justification from table attribute defaults/schema:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
    • Updated editor cell rendering (packages/tableberg/src/cell/index.tsx) to:
      • stop reading/storing cell-level justification
      • apply per-element flex alignment wrappers (display:flex + justify-content)
      • derive horizontal group alignment from child element alignment when all elements match
    • Updated frontend cell renderer (packages/tableberg/renderer/Cell/CellRenderer.php) with matching per-element flex wrappers and implicit horizontal alignment behavior
  • Added new alignment utilities in packages/tableberg/src/alignment.ts:
    • canonical element alignment model (left / center / right)
    • element alignment read/write helpers across all element types
  • Applied destructive schema updates (no backward compatibility layer):
    • removed all legacy alignment fallbacks and migration transforms
    • store now consumes/syncs canonical attrs directly
  • Converted cell alignment control in inspector (packages/tableberg/src/controls.tsx) to implicit behavior:
    • value is derived from selected/all child elements
    • mixed child alignments show indeterminate state
    • changing control updates child element alignments (no cell style writes)
    • removed space-between from alignment options
  • Added/standardized element-level alignment support and controls:
    • Text: added align attr/default + toolbar alignment control (packages/tableberg/src/elements/text/index.tsx)
    • List: added align attr/default + toolbar alignment control (packages/tableberg/src/elements/list/index.tsx)
    • Custom HTML: added align attr/default + toolbar alignment control (packages/tableberg/src/elements/custom-html/index.tsx, packages/tableberg/src/elements/custom-html/controls.tsx)
    • Button: moved alignment from styles.align to top-level align; rendering now uses flex justify-content (packages/tableberg/src/elements/button/index.tsx, packages/tableberg/src/elements/button/controls.tsx)
    • Icon: renamed styles.justify to styles.align (packages/tableberg/src/elements/icon/index.tsx, packages/tableberg/src/elements/icon/controls.tsx)
    • Star Rating: renamed starAlign to top-level align (packages/tableberg/src/elements/star-rating/index.tsx, packages/tableberg/src/elements/star-rating/controls.tsx)
    • Image: removed class-based internal alignment in favor of cell/element flex alignment wrappers (packages/tableberg/src/elements/image/index.tsx, packages/tableberg/src/elements/image/style.scss)
  • Updated PHP element attrs/defaults for alignment schema parity:
    • renderer/Text/*, renderer/List/*, renderer/CustomHtml/*
    • renderer/Button/* (align moved top-level)
    • renderer/Icon/* (styles.align canonical)
    • renderer/StarRating/* (align canonical)

Validation:

  • pnpm --filter tableberg check:types passes
  • php -l passes for all updated renderer PHP files

2026-03-05: Element Delete Button in Selected Element Toolbar (Editor)

Completed:

  • Added element deletion action in editor store:
    • removeElementFromCell(coord, elementIndex) in packages/tableberg/src/store/index.tsx
    • Removes the element from the target cell and drops empty cell element arrays from data.cells
    • Clears selectedElement when the deleted element was selected (and reindexes selection if needed)
  • Added shared toolbar delete control:
    • New packages/tableberg/src/components/ElementDeleteButton.tsx
    • Uses WordPress toolbar button + trash icon and localized label Delete element
  • Wired delete button into all element toolbars shown on selection:
    • packages/tableberg/src/elements/text/index.tsx
    • packages/tableberg/src/elements/button/controls.tsx
    • packages/tableberg/src/elements/image/controls.tsx
    • packages/tableberg/src/elements/list/index.tsx
    • packages/tableberg/src/elements/star-rating/controls.tsx
    • packages/tableberg/src/elements/icon/controls.tsx
    • packages/tableberg/src/elements/custom-html/controls.tsx
    • Updated corresponding element control invocations to pass cellCoords + elementIndex

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-05: Cell Orientation, Justification, Wrap, Vertical Alignment (Editor + PHP Frontend)

Completed:

  • Added new cell-level style defaults and schema fields:
    • cellStyles.orientation (vertical | horizontal, default vertical)
    • cellStyles.justification (left | center | right | space-between, default left)
    • cellStyles.wrap (wrap | nowrap, default wrap)
    • cellStyles.verticalAlign (top | middle | bottom, default middle)
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
  • Added inspector UI in packages/tableberg/src/controls.tsx:
    • New panel: Cell Elements
    • Elements Orientation icon toggle (Vertical / Horizontal)
    • Elements Alignment icon toggle
    • Elements Allow Wrap toggle
    • Elements Vertical Alignment icon toggle (Top / Middle / Bottom)
  • Updated editor cell rendering in packages/tableberg/src/cell/index.tsx:
    • Applies vertical-align to table cells
    • Adds flex-based cell element layout support for both orientations
    • In vertical orientation, alignment controls remain active and map correctly to vertical/horizontal axes
    • In vertical orientation, space-between is excluded from Elements Alignment (unsupported on cross-axis)
  • Updated frontend PHP rendering/parsing:
    • packages/tableberg/renderer/Table/TableAttrs.php parses the new cell style fields
    • packages/tableberg/renderer/Table/TableRenderer.php passes new global cell style fields into render context
    • packages/tableberg/renderer/Cell/CellRenderContext.php now carries orientation/justification/wrap/verticalAlign
    • packages/tableberg/renderer/Cell/CellRenderer.php applies:
      • cell vertical-align
      • horizontal element wrapper layout styles
      • per-cell override handling for new fields
  • Hardened control value fallback in packages/tableberg/src/hooks/useCellStyleControl.ts so missing legacy style keys fall back to control defaults

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/Defaults.php
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Cell/CellRenderContext.php
    • packages/tableberg/renderer/Cell/CellRenderer.php
  • pnpm --filter tableberg check:types passes

2026-03-04: Table Outer Border (Editor + PHP Frontend)

Completed:

  • Added new table-level border config defaults/parsing:
    • tableConfig.tableBorder (top/right/bottom/left, default empty strings)
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
  • Added editor controls in packages/tableberg/src/controls.tsx:
    • Table Border via shared BorderControl
    • Controls update/reset tableConfig.tableBorder
  • Updated editor table rendering in packages/tableberg/src/table/index.tsx:
    • Applies outer border styles (border-top/right/bottom/left) to the <table> element
  • Updated PHP frontend rendering/parsing:
    • packages/tableberg/renderer/Table/TableAttrs.php now parses table border config
    • packages/tableberg/renderer/Table/TableRenderer.php now outputs matching table border inline styles
  • Fixed border conflict when table border is set and only one cell spacing axis is 0:
    • Added table border-side marker classes on rendered table markup (editor + PHP frontend)
    • Extended packages/tableberg/src/table-spacing.scss to suppress conflicting outer cell borders against table outer border on zero-spacing axis
  • Follow-up simplification:
    • Removed table border radius setting from editor, attributes, and frontend rendering

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Table/Defaults.php
  • pnpm --filter tableberg check:types passes

2026-03-04: Sticky Header (Editor + PHP Frontend)

Completed:

  • Added new table-level sticky config defaults/parsing:
    • tableConfig.stickyHeader (boolean, default false)
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
      • packages/tableberg/renderer/Table/TableAttrs.php
  • Added editor controls in packages/tableberg/src/controls.tsx:
    • Moved sticky option into Header & Footer Settings
    • Renamed sticky control to Sticky Header
  • Updated editor rendering:
    • packages/tableberg/src/cell/index.tsx now applies sticky styles (position: sticky, top, z-index) to top-row header cells when sticky header is enabled
    • Sticky behavior now applies to header cells in the top row
    • Sticky cells now fall back to white background (#fff) when no background color is configured
  • Updated frontend PHP rendering:
    • packages/tableberg/renderer/Table/TableRenderer.php now passes sticky config into cell render context
    • packages/tableberg/renderer/Cell/CellRenderContext.php now carries sticky header config
    • packages/tableberg/renderer/Cell/CellRenderer.php now applies sticky inline styles and white background fallback for sticky cells

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Cell/CellRenderContext.php
    • packages/tableberg/renderer/Cell/CellRenderer.php
    • packages/tableberg/renderer/Table/Defaults.php
  • pnpm --filter tableberg check:types passes

2026-03-04: Cell Spacing (Editor + PHP Frontend)

Completed:

  • Added table-level cell spacing support in table config defaults/parsing:
    • tableConfig.cellSpacing.horizontal (string, default "0")
    • tableConfig.cellSpacing.vertical (string, default "0")
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
      • packages/tableberg/renderer/Table/TableAttrs.php
  • Added editor controls in the Dimensions panel (packages/tableberg/src/controls.tsx):
    • Cell Spacing now uses shared SpacingControl from @tableberg/components
    • Control maps left/right to horizontal spacing and top/bottom to vertical spacing
    • Reset behavior returns both values to 0
  • Updated editor table rendering in packages/tableberg/src/table/index.tsx:
    • Applies border-collapse: separate when either spacing value is non-zero
    • Applies border-spacing: <horizontal> <vertical> when spacing is enabled
    • Keeps border-collapse: collapse when both spacing values are zero
  • Updated PHP frontend rendering in packages/tableberg/renderer/Table/TableRenderer.php:
    • Mirrors editor behavior for border-collapse and border-spacing
    • Uses direct "0" checks for spacing axis values
    • Added spacing-axis classes on table markup to support border conflict fixes
    • Added CSS rules in packages/tableberg/src/table-spacing.scss to prevent doubled borders when only one spacing axis is 0

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/Defaults.php
  • pnpm --filter tableberg check:types passes

2026-03-04: Table Caption Support (Editor + PHP Frontend)

Completed:

  • Added table-level caption support to table config defaults and parsing:
    • tableConfig.caption (string, default "")
    • Synced defaults in:
      • packages/tableberg/src/attributes.ts
      • packages/tableberg/src/block.json
      • packages/tableberg/renderer/Table/Defaults.php
      • packages/tableberg/renderer/Table/TableAttrs.php
  • Added editor caption UI:
    • New TableCaption component in packages/tableberg/src/components/TableCaption/index.tsx
    • Uses RichText with figcaption for formatted caption content
    • Caption visibility behavior:
      • auto-shows when caption has content
      • auto-hides when empty and block is deselected
    • Added toolbar caption toggle in packages/tableberg/src/controls.tsx (Add caption / Remove caption)
  • Updated editor markup to use semantic figure/caption structure:
    • packages/tableberg/src/index.tsx now renders the table block inside <figure> and renders caption below table content
    • Added showCaption transient state + setShowCaption action in packages/tableberg/src/store/index.tsx to control editor caption visibility
  • Updated frontend PHP rendering to output semantic figure/caption structure:
    • packages/tableberg/renderer/Table/TableRenderer.php now wraps output in <figure class="wp-block-tableberg">
    • Renders <figcaption class="tableberg-table-caption wp-element-caption"> when caption is not empty
    • Caption content is sanitized via StringAttr::asHtml()
  • Added caption styles in packages/tableberg/src/components/TableCaption/style.scss and imported into both editor.scss and style.scss

Validation:

  • php -l passes for:
    • packages/tableberg/renderer/Table/TableRenderer.php
    • packages/tableberg/renderer/Table/TableAttrs.php
    • packages/tableberg/renderer/Table/Defaults.php
  • pnpm --filter tableberg check:types passes

2026-03-04: Table Background Helper Controls (Editor Only)

Completed:

  • Added new color sidebar helper controls in packages/tableberg/src/controls.tsx:
    • Header Background Color
    • Even Row Background Color
    • Odd Row Background Color
    • Footer Background Color
    • Row Background Color
    • Column Background Color
  • Implemented helper control behavior without adding new attributes:
    • Controls infer their value from existing per-cell/background-effective values
    • Mixed target values show as unset in the control
    • Setting a helper control updates background color on the appropriate cells via existing per-cell style overrides
    • Target cell resolution now uses data.cells (not structure.cells) so helper controls work for normal (unmerged) tables where structure.cells is sparse/default-span-only
  • Refactored helper control logic out of controls.tsx into new hook packages/tableberg/src/hooks/useBackgroundColorHelpers.ts
    • controls.tsx now consumes generated control props from the hook
    • helper targeting/inference behavior remains unchanged; refactor is organizational only
  • Row/column helper controls are selection-driven:
    • shown when cells are selected
    • row control targets all cells in selected row(s)
    • column control targets all cells in selected column(s)
  • Header/footer helper controls are shown only when their respective table sections are enabled
  • Even/odd helper controls now target visible data rows (respecting search and pagination), with the first visible data row treated as odd

Validation:

  • pnpm --filter tableberg check:types passes

2026-03-02: Table Width + Alignment (Editor + PHP Frontend)

Completed:

  • Added table-level width and alignment support to table config defaults and parsing:
    • tableConfig.tableWidth (auto / wide / full / custom string, default auto)
    • tableConfig.tableAlignment (left / center / right, default left)
    • Removed width/alignment normalization and legacy value remapping; renderer/editor now use the configured values directly
  • Added editor controls in packages/tableberg/src/controls.tsx:
    • Table Width control via SizeControl (default 350px)
    • Toolbar alignment control now includes only left / center / right
    • Added sidebar width mode control (auto / fixed / wide / full) above table width
    • Table width control is shown only when width mode is fixed
    • Toolbar alignment control is disabled when width mode is auto, wide, or full
    • Fixed mode selection now works for legacy blocks by normalizing width first (custom width no longer gets forced back to auto)
  • Updated editor table rendering in packages/tableberg/src/table/index.tsx:
    • Applies table width as inline width + max-width when set
    • Applies left/center/right alignment class to wrapper (justify-table-*)
  • Added alignment styles in packages/tableberg/src/table-width.scss:
    • left/center/right alignment behavior
    • wide/full now follows Gutenberg alignment model via alignwide / alignfull classes
  • Updated PHP renderer parsing and output:
    • packages/tableberg/renderer/Table/TableAttrs.php now parses tableWidth and tableAlignment
    • packages/tableberg/renderer/Table/TableRenderer.php now maps wide/full to alignwide / alignfull and applies table width style inline only for left/center/right
  • Updated editor block wrapper in packages/tableberg/src/index.tsx:
    • applies Gutenberg-style alignwide / alignfull class + data-align when table width is wide/full
  • Updated editor table rendering in packages/tableberg/src/table/index.tsx:
    • applies custom tableWidth only when table width is a fixed custom value
  • Synced defaults:
    • packages/tableberg/src/attributes.ts
    • packages/tableberg/src/block.json
    • packages/tableberg/renderer/Table/Defaults.php

Validation:

  • php -l passes for updated table renderer files
  • pnpm --filter tableberg check:types passes

2026-03-02: Fixed Width Cells (Editor + PHP Frontend)

Completed:

  • Added fixed-width column support to table attributes:
    • tableConfig.fixedColumnWidths (boolean)
    • structure.columns[*].width (string)
  • Added editor controls in packages/tableberg/src/controls.tsx:
    • Equal width columns toggle (table-level)
    • Per-column Width Mode control (Auto / Fixed) when equal-width mode is disabled
    • Column Width input shown only for Fixed mode, with default 150px (never indeterminate)
  • Added new store action in packages/tableberg/src/store/index.tsx:
    • setColumnWidth(column, width) with cleanup logic to remove empty per-column config objects
  • Updated editor cell rendering in packages/tableberg/src/cell/index.tsx:
    • Applies computed column width (width + min-width) to all non-colspan cells
    • In fixed mode, computes equal width as 100 / cols percent
  • Updated PHP renderer parsing and output:
    • packages/tableberg/renderer/Table/TableAttrs.php now parses fixedColumnWidths and column width
    • ColumnConfig::from_array now supports nullable sortable during general column parsing (prevents width-only columns from becoming sortable by default)
    • packages/tableberg/renderer/Table/TableRenderer.php computes per-cell column width and passes it to render context
    • packages/tableberg/renderer/Cell/CellRenderContext.php and CellRenderer.php now carry and apply cell width (width + min-width) for non-colspan cells
  • Synced defaults:
    • packages/tableberg/src/block.json
    • packages/tableberg/src/attributes.ts
    • packages/tableberg/renderer/Table/Defaults.php
    • fixedColumnWidths now defaults to true

Validation:

  • php -l passes for updated renderer files
  • pnpm --filter tableberg check:types run completed without reporting new type errors

2026-03-02: Frontend Search (View Script + PHP Data Attrs)

Completed:

  • Added frontend search metadata output from packages/tableberg/renderer/Table/TableRenderer.php:
    • data-tableberg-search-enabled
    • data-tableberg-search-placeholder
    • data-tableberg-search-position
    • data-tableberg-search-highlight-color
  • Implemented frontend search behavior in packages/tableberg/src/frontend/search.ts:
    • renders search UI above each enabled table
    • filters data rows by text element content only (for now) while preserving header/footer
    • tracks per-row match state (data-tableberg-search-match)
    • emits tableberg:search-changed event after each filter update
    • highlights matching search terms inside matched rows using mark.tableberg-search-highlight
  • Tagged rendered text elements with .tableberg-text-element in packages/tableberg/renderer/Text/TextRenderer.php so frontend search/highlight can target text elements explicitly
  • Integrated search initialization in packages/tableberg/src/view.ts
  • Updated frontend pagination in packages/tableberg/src/frontend/pagination.ts to:
    • paginate only search-matched rows
    • reset to page 1 when search changes
    • listen to both sorting and search events for re-rendering
  • Enabled frontend search styles in packages/tableberg/src/style.scss and added row hide/highlight styles in packages/tableberg/src/components/SearchInput/style.scss

Validation:

  • php -l passes for packages/tableberg/renderer/Table/TableRenderer.php
  • pnpm --filter tableberg check:types still fails due pre-existing unrelated type errors:
    • packages/components/editor/ColorDropdown.tsx (__experimentalColorGradientControl export)
    • packages/tableberg/src/components/RibbonControl/index.tsx (CellCoords export)

Next:

  • DataTable frontend behavior is complete; next work can focus on post-transition tasks and pro/free separation

2026-02-26: Frontend Pagination (View Script + PHP Data Attrs)

Completed:

  • Added frontend pagination config output in packages/tableberg/renderer/Table/TableRenderer.php via data-tableberg-pagination
  • Implemented frontend pagination behavior in packages/tableberg/src/frontend/pagination.ts:
    • reads pagination config from rendered table attrs
    • renders pagination controls below each table
    • supports page number buttons, prev/next arrows, and ellipsis behavior
    • paginates only data rows while preserving header/footer rows
    • skips pagination when row-spanning cells are present
  • Integrated sorting + pagination initialization in packages/tableberg/src/view.ts
  • Added sorting-to-pagination synchronization using a shared table event (tableberg:rows-changed) in packages/tableberg/src/frontend/sorting.ts
  • Enabled frontend pagination styles by importing packages/tableberg/src/components/PaginationNavigation/style.scss in packages/tableberg/src/style.scss

Validation:

  • php -l passes for packages/tableberg/renderer/Table/TableRenderer.php
  • pnpm --filter tableberg check:types still fails due pre-existing unrelated type errors:
    • packages/components/editor/ColorDropdown.tsx (__experimentalColorGradientControl export)
    • packages/tableberg/src/components/RibbonControl/index.tsx (CellCoords export)

Next:

  • Implement frontend search behavior

2026-02-26: Frontend Column Sorting (View Script + PHP Data Attrs)

Completed:

  • Added block frontend script entry via viewScript in packages/tableberg/src/block.json
  • Implemented frontend table sorting in packages/tableberg/src/view.ts:
    • initialization for rendered tables with sortable column metadata
    • click and keyboard (Enter/Space) sorting on sortable header cells
    • sort cycle: ascending -> descending -> original row order
    • sort-type-aware parsing for text, number, and date
    • active sort indicator + aria-sort updates
  • Extended PHP table rendering to expose sorting metadata and hooks:
    • table-level data attrs (data-tableberg-sortable, data-tableberg-header, data-tableberg-footer, data-tableberg-columns)
    • per-row data-tableberg-row
    • per-sortable-header attrs (data-sortable, keyboard/focus attrs) and indicator markup
    • added sortableType support to CellRenderContext and CellRenderer
  • Added frontend sortable-header/indicator styles in packages/tableberg/src/cell/style.scss

Validation:

  • php -l passes for updated renderer files
  • pnpm --filter tableberg check:types still fails due pre-existing unrelated type errors:
    • packages/components/editor/ColorDropdown.tsx (__experimentalColorGradientControl export)
    • packages/tableberg/src/components/RibbonControl/index.tsx (CellCoords export)

Next:

  • Implement frontend search behavior
  • Implement frontend pagination behavior

2026-02-25: Renderer Stabilization (Ribbon + Attr Helpers)

Completed:

  • Added getBoolOrNull helper in packages/tableberg/renderer/utils.php for typed boolean extraction
  • Fixed ribbon parser autoload issue by moving RibbonConfigAttrs to its own PSR-4 file:
    • Ribbon/RibbonConfigAttrs.php
  • Kept ribbon attribute classes in the same module file:
    • Ribbon/RibbonConfigAttrs.php (RibbonAttrs, RibbonIconAttrs, RibbonStyleAttrs)
  • Simplified ribbon defaults parsing to use get*OrNull helpers directly in from_array methods (removed redundant fallback literals)
  • Confirmed renderer syntax validity after refactor (php -l on updated ribbon files)

2026-02-24: PHP Frontend Renderer Progress (Text, Button, Image)

Completed:

  • Continued server-side frontend rendering work in packages/tableberg/renderer
  • Integrated ImageRenderer into cell element rendering flow (CellRenderer now handles image)
  • Finalized initial image renderer module structure:
    • Image/Defaults.php
    • Image/ImageAttrs.php
    • Image/ImageRenderer.php
  • Aligned image rendering with editor attributes:
    • alignment wrapper classes (left/center/right)
    • size slug URL fallback to original media URL
    • aspect ratio / scale / width / height / border / border radius style output
    • optional linked image with _blank rel handling
    • optional caption rendering
  • Fixed namespaced WordPress escaping calls in image renderer (\esc_url, \esc_attr)

Next:

  • Continue implementing the remaining element renderers in PHP and wire them into CellRenderer
  • Add frontend scripts for search/sort/pagination after element renderer coverage is sufficient

2026-02-24: PHP Frontend Renderer Progress (List)

Completed:

  • Added ListRenderer module structure in packages/tableberg/renderer/List
    • List/Defaults.php
    • List/ListAttrs.php
    • List/ListRenderer.php
  • Implemented list attribute parsing with defaults for:
    • nested list items (content, indentLevel)
    • list config (listType, listStyle)
    • icon data (iconName, svg.viewBox, svg.path)
    • style values (spacing, typography, colors)
  • Implemented frontend list HTML rendering for both modes:
    • basic list rendering (ul/ol) with nested tree generation based on indent levels
    • styled list rendering with icon/decimal marker output and inline style parity
  • Wired list element rendering into CellRenderer (name === "list") so frontend output now renders list content instead of placeholder element markup

Next:

  • Continue implementing the remaining element renderers in PHP and wire them into CellRenderer
  • Add frontend scripts for search/sort/pagination after element renderer coverage is sufficient

2026-02-24: PHP Frontend Renderer Progress (Icon)

Completed:

  • Added IconRenderer module structure in packages/tableberg/renderer/Icon
    • Icon/Defaults.php
    • Icon/IconAttrs.php
    • Icon/IconRenderer.php
  • Implemented icon attribute parsing with defaults for:
    • icon source (iconName, optional SVG data, optional URL)
    • size and behavior (paragraph/char)
    • link config (url, target)
    • style values (justify, rotation, colors/hover colors, spacing, border, border radius)
  • Implemented frontend icon HTML rendering with editor parity:
    • SVG icon rendering and URL image rendering
    • icon link wrapping with _blank rel handling
    • hover CSS custom property output (--tableberg-icon-color-hover, --tableberg-icon-bg-hover)
    • behavior class support (tableberg-icon-as-char)
  • Wired icon element rendering into CellRenderer (name === "icon") so frontend output now renders icon content instead of placeholder element markup
  • Refactored duplicated SVG icon attribute objects into shared SvgIconAttrs in renderer/Attrs, reused by both List and Icon renderers

Next:

  • Continue implementing the remaining element renderers in PHP and wire them into CellRenderer
  • Add frontend scripts for search/sort/pagination after element renderer coverage is sufficient

2026-02-24: PHP Frontend Renderer Progress (Star Rating)

Completed:

  • Added StarRatingRenderer module structure in packages/tableberg/renderer/StarRating
    • StarRating/Defaults.php
    • StarRating/StarRatingAttrs.php
    • StarRating/StarRatingRenderer.php
  • Implemented star-rating attribute parsing with defaults for:
    • star config (starCount, starSize, starColor, selectedStars)
    • review text config (enableText, reviewText, reviewTextAlign, reviewTextColor)
    • star alignment (starAlign)
  • Implemented frontend star-rating HTML rendering with editor parity:
    • fractional star fill rendering via SVG masks
    • alignment classes (tableberg-stars-center, tableberg-stars-right)
    • optional review text rendering with sanitized HTML + inline text alignment/color styles
  • Wired star-rating element rendering into CellRenderer (name === "star-rating") so frontend output now renders star-rating content instead of placeholder element markup

Next:

  • Continue implementing the remaining element renderers in PHP and wire them into CellRenderer
  • Add frontend scripts for search/sort/pagination after element renderer coverage is sufficient

2026-02-24: PHP Frontend Renderer Progress (Custom HTML, Ribbon)

Completed:

  • Added CustomHtmlRenderer module structure in packages/tableberg/renderer/CustomHtml

    • CustomHtml/Defaults.php
    • CustomHtml/CustomHtmlAttrs.php
    • CustomHtml/CustomHtmlRenderer.php
  • Implemented custom HTML attribute parsing and frontend rendering using sanitized HTML output (StringAttr::asHtml())

  • Wired custom HTML element rendering into CellRenderer (name === "custom-html")

  • Added ribbon frontend renderer module in packages/tableberg/renderer/Ribbon

    • Ribbon/Defaults.php
    • Ribbon/RibbonConfigAttrs.php
    • Ribbon/RibbonRenderer.php
  • Implemented ribbon parsing and rendering for all ribbon types:

    • badge
    • bookmark
    • corner
    • side
    • icon
  • Added shared cell ribbon resolution in rendering context:

    • global ribbon config from cellStyles.ribbon
    • per-cell ribbon override from cellStyles.cells[*][1].ribbon
    • merged override behavior before render
  • Wired ribbon HTML output into cell frontend markup so rendered table cells include ribbon overlays

Next:

  • Add frontend scripts for search/sort/pagination now that element/ribbon renderer coverage is complete

2026-02-18: Custom HTML Element Implementation

Completed:

  • Added CustomHtmlElement with features from old pro version:

    • HTML code editing via PlainText input
    • Live preview rendering in sandboxed iframe
    • HTML/Preview toggle buttons in toolbar
    • Dynamic data binding support for HTML content
    • Auto-switch to preview mode when content is bound
  • Updated infrastructure:

    • Added CustomHtmlElementType to CellElement union in attributes.ts
    • Updated elements/index.ts exports and createElement function
    • Updated CellChildAutoCompleter.tsx to enable custom-html
    • Updated cell-inserter/items.ts to enable custom-html
    • Updated Cell component to render CustomHtmlElement
    • Updated bindable-attributes.ts to support custom-html element
    • Added SCSS styles for custom-html element

Architecture decisions:

  • CustomHtmlElement follows established element pattern (ElementRendererProps, controls separation, store integration)
  • Preview uses sandboxed iframe with pointer-events: none to allow selection clicks to pass through
  • Editor styles are injected into iframe for consistent rendering
  • HTML editor shown by default; preview only when user clicks Preview button or content is bound
  • Integrates with sortPreviewMode (shows preview during sort preview)
  • Single bindable attribute: content (HTML string)

2026-02-17: Icon Element Implementation

Completed:

  • Added IconElement with full feature parity from old pro version:

    • Icon selection from built-in library (WordPress icons, Font Awesome, social icons)
    • URL import for custom SVG/image icons
    • Icon size control (HeightControl)
    • Rotation (-180 to 180 degrees)
    • Justify/alignment (left, center, right)
    • Behavior mode: block-level ("paragraph") or inline ("char")
    • Link support with URL and target (_blank/_self)
    • Color controls with normal/hover states
    • Background color/gradient with normal/hover states
    • Padding and margin spacing controls
    • Border and border-radius controls
    • Dynamic data bindings for: linkUrl, color, background
  • Updated infrastructure:

    • Added IconElementType to CellElement union in attributes.ts
    • Updated elements/index.ts exports and createElement function
    • Updated CellChildAutoCompleter.tsx to enable icon
    • Updated cell-inserter/items.ts to enable icon
    • Updated Cell component to render IconElement
    • Updated bindable-attributes.ts to support icon element
    • Added SCSS styles for icon element

Architecture decisions:

  • IconElement follows the established element pattern (ElementRendererProps, controls separation, store integration)
  • Reuses existing @tableberg/components/icon-library components (Icon, IconPickerMini)
  • Icon data structure supports both library icons (with SVG path data) and URL-based custom icons
  • Hover states use CSS custom properties (--tableberg-icon-color-hover, --tableberg-icon-bg-hover)
  • Integrates with sortPreviewMode (selection disabled during sort preview)
  • Supports dynamic data bindings following established pattern

2026-02-12: Search Implementation with Highlighting

Completed:

  • Added search feature with full editor functionality (frontend deferred until PHP rendering is done)
  • New types in attributes.ts:
    • SearchConfig: { enabled, placeholder, highlightColor }
    • Added search?: SearchConfig to TableConfig
  • Created search.ts with utilities:
    • filterRowsBySearch() - filters rows by search term across all cell content
    • getMatchingRowCount() - returns count of matching data rows
    • highlightSearchMatches() - wraps matching text in <mark> tags with custom background color
  • Updated store with:
    • Search state: searchTerm (transient, not persisted)
    • Actions: setSearchTerm, setSearchConfig, getFilteredRowIndices
  • New UI components:
    • SearchInput - search input with icon, clear button, and result count (rendered above table)
    • SearchControl - inspector panel with:
      • Enable search toggle
      • Placeholder text customization
      • Highlight color picker (uses @tableberg/components ColorControl with theme colors)
  • Updated PrimaryTable to integrate search → sort → paginate pipeline:
    1. Filter rows by search term
    2. Sort the filtered rows (if sorting enabled)
    3. Paginate the sorted/filtered rows (if pagination enabled)
  • Updated PaginationNavigation to accept filteredRowCount prop for correct page calculation when search is active
  • Added search highlighting to TextElement and ButtonElement:
    • Highlights matching text with configurable background color
    • Highlighting only active when highlightColor is set (non-empty)
    • Shows highlighted (read-only) view when search is active with highlight color
    • RichText editing disabled during active search with highlighting to preserve highlights

Architecture decisions:

  • Search config stored in tableConfig.search (table-level setting)
  • searchTerm is transient state (not persisted to block attributes) - resets on page reload
  • Search is case-insensitive and matches any part of cell content
  • All cell elements' content attribute is searched (text, button labels, etc.)
  • Header and footer rows are always preserved in results
  • Search resets pagination to first page when term changes
  • No restrictions on search with merged cells or row-spanning cells (unlike sorting/pagination)
  • Highlight uses <mark> elements with inline background-color style
  • Highlight is disabled when highlightColor is empty (no separate toggle)
  • Uses theme color palette via @tableberg/components ColorControl

Deferred:

  • Frontend search script - will be implemented alongside PHP rendering task
    • PHP needs to output data-tableberg-search attribute with config
    • Frontend JS will render search input and handle filtering/highlighting

2026-02-04: Dynamic Data Simplification

Completed:

  • Removed localStorage caching from useDynamicData — react-query handles all caching
  • Removed bindingsFingerprint — pass bindings object directly to query keys
  • Removed PHP Resolver class and Sources (SourceInterface, PostFieldSource, PostMetaSource, ElementRenderer)
  • Consolidated all dynamic data logic into RestController with procedural code
  • Removed source parameter entirely — backend auto-detects post field vs post meta by checking key against known post field list
  • Renamed /dynamic-data/keys endpoint to /dynamic-data/meta-keys (post field keys are static and known client-side)
  • Removed /dynamic-data/sources endpoint
  • Simplified BindingSource type: removed source field, now just { key, postId?, fallback? }
  • Removed type field from BindableAttribute (was never used) — now just { path, label }
  • Standardized dynamic data values to always be string | null (cast to string on PHP side)
  • Reworked DynamicDataPanel UI:
    • Only shows currently bound attributes (not all bindable ones)
    • "Add Binding" button opens a blank form with attribute selector
    • Bound attributes show as collapsible cards with attribute label + field summary
    • Attribute dropdown shown in both new and existing binding forms
    • Forms submit on Enter
  • Switched fetchAvailableMetaKeys to use useQuery for caching

Architecture decisions:

  • No need for source abstraction — post field keys are a fixed known set, anything else is post meta
  • Dynamic data always returns strings (PHP casts non-null values)
  • BindableAttribute.type removed — the consumer already knows what it expects for each path

2026-02-02: Dynamic Data Feature Implementation

Completed:

  • Created bindings architecture with parallel bindings object on elements
  • TypeScript types: BindingSource, ElementBindings, BindableAttribute
  • PHP infrastructure:
    • DynamicData\RestController - REST API endpoints for editor preview
  • Editor components:
    • DynamicDataPanel - Inspector sidebar panel for configuring bindings
    • useDynamicData hook for fetching preview values
    • Integrated into TextElement and ButtonElement controls
  • Store updated with updateSelectedElementBindings and getSelectedElement

Architecture decisions:

  • Bindings stored at element level (self-contained, easy to copy/paste)
  • Parallel to attributes (static value preserved as fallback)
  • Nested path support (e.g., link.url, styles.backgroundColor)

Pending:

  • Frontend PHP rendering needs to be implemented first (separate task)
  • Pro sources (ACF, WooCommerce) to be added later
  • Deprecate old Pro DynamicField block and migrate

2026-02-09: Pagination Implementation

Completed:

  • Added pagination feature with full editor functionality (frontend deferred until PHP rendering is done)
  • New types in attributes.ts:
    • PaginationConfig: { enabled, pageSize, showPageNumbers, showPrevNext }
    • Added pagination?: PaginationConfig to TableStyles
  • Created pagination.ts with utilities:
    • tableHasRowSpanningCells() - detects rows with rowSpan > 1
    • isPaginationAllowed() - checks if pagination can be enabled (no row-spanning cells)
    • getTotalPages() - calculates total pages excluding header/footer
    • getPagedRowIndices() - returns row indices for current page (header/footer always included)
    • defaultPaginationConfig - default config values
  • Updated store with:
    • Pagination state: currentPage
    • Actions: setCurrentPage, setPaginationConfig, getPaginationConfig, isPaginationAllowed
  • New UI components:
    • PaginationNavigation - navigation controls below table (prev/next buttons, page numbers with ellipsis)
    • PaginationControl - inspector panel with enable toggle, page size input with suggestions (5, 10, 20, 30), navigation style toggles
  • Updated PrimaryTable to render paginated rows when pagination is enabled
  • Added sort indicator chevrons in header cells when sorting is enabled (visible even outside preview mode)

Architecture decisions:

  • Pagination config stored in tableStyles.pagination (table-level setting)
  • Pagination allowed only if table has no row-spanning cells (colSpan OK, rowSpan not OK)
  • Header row (if enabled) always shown on all pages
  • Footer row (if enabled) always shown on all pages
  • Pagination and sorting work together: sort first, then paginate
  • No preview mode for pagination - controls are always visible and functional when enabled
  • At least one of showPageNumbers or showPrevNext must be enabled (toggles are disabled when they're the only option)

Deferred:

  • Frontend pagination script - will be implemented alongside PHP rendering task
    • PHP needs to output data-tableberg-pagination attribute with config
    • Frontend JS will render page navigation controls and handle page switching

2026-02-07: Column Sorting Implementation

Completed:

  • Added column sorting feature (editor preview only - frontend deferred until PHP rendering is done)
  • New types in attributes.ts:
    • SortableType: 'text' | 'number' | 'date'
    • ColumnConfig: { sortable?: SortableType }
    • Added columns?: Record<number, ColumnConfig> to Structure
  • Created sorting.ts with utilities:
    • getColumnsWithMergedCells() - detects columns with merged cells
    • getColumnsWithMultipleElements() - detects columns with multi-element cells
    • isColumnSortable() - checks if column can be sorted
    • sortRowsByColumn() - performs the actual sort
  • Updated store with:
    • Sort preview state: sortPreviewMode, previewSortColumn, previewSortOrder
    • Actions: enterSortPreviewMode, exitSortPreviewMode, togglePreviewSort, getSortedRowIndices
    • Column config actions: setColumnSortable, getColumnConfig, isColumnSortableAllowed
  • New UI components:
    • SortPreviewBanner - banner shown during preview mode
    • ColumnSortingControl - inspector panel for configuring column sorting
  • Updated PrimaryTable to render sorted rows in preview mode with clickable sort indicators

Architecture decisions:

  • Sorting config stored in structure.columns (not tableStyles)
  • Column is sortable only if it has no merged cells AND no cells with multiple elements
  • Editor preview mode is temporary - exiting reverts to original order
  • No global "enable sorting" toggle - sorting is enabled per-column via the "Sort as" dropdown

Deferred:

  • Frontend sorting script - will be implemented alongside PHP rendering task
    • PHP needs to output data attributes (data-tableberg-columns, data-tableberg-header, data-tableberg-footer)
    • Frontend JS will read config and add click-to-sort on header cells

2026-02-12: Button Element Completion & Dynamic Bindings Helper

Completed:

  • Created mergeAttrsWithDefaultsAndApplyBindings helper in packages/shared/utils/merge-attrs-with-bindings.ts
    • Combined function that merges partial attrs with defaults, then applies dynamic binding values
    • Supports dot-notation paths for bindings (e.g., link.url, styles.backgroundColor)
    • Uses shallow-cloning along the path for immutability
    • Accepts optional defaultFallback for when preview is null and no per-binding fallback is set
    • Removed separate merge-attrs-with-defaults.ts file
  • Updated ButtonElement to consume all bindable attributes:
    • content - button text
    • link.url - link URL
    • id - HTML anchor (newly added)
    • styles.backgroundColor - background color
    • styles.textColor - text color
  • Added sortPreviewMode guard to ButtonElement (matching TextElement behavior)
    • Prevents element selection during sort preview
    • Renders as static <div> when in sort preview mode
  • Added UI feedback for dynamically-bound controls:
    • Link toolbar button disabled with tooltip when link.url is bound
    • Color control labels show "(dynamic)" suffix when bound
    • HTML anchor help text changes when id is bound
  • Refactored TextElement to use mergeAttrsWithDefaultsAndApplyBindings for consistency

Architecture decisions:

  • Single combined function mergeAttrsWithDefaultsAndApplyBindings handles both defaults and bindings in one call
  • Default fallback "(No data)" is applied to all bound attributes when preview is null and no fallback configured (even for non-text paths like URL/color — browser handles invalid values gracefully)
  • Binding detection variables (linkUrlIsBound, textColorIsBound, etc.) added to controls for UI feedback
  • contentIsBound vs hasDynamicBindings distinction: former controls RichText/div rendering, latter controls CSS class

2026-02-13: Image and Star Rating Elements

Completed:

  • Added ImageElement with full feature parity from old version:

    • Media upload/selection from WordPress media library
    • Image resize with ResizableBox (drag handles)
    • Aspect ratio control (Original, 1:1, 4:3, 3:4, 3:2, 2:3, 16:9, 9:16)
    • Scale options (cover, contain) when aspect ratio is set
    • Width and height controls (px units)
    • Resolution/size slug selection (thumbnail, medium, large, full)
    • Alt text support
    • Alignment (left, center, right)
    • Caption support with toggle
    • Link support with target options
    • Border and border radius controls
    • Dynamic data bindings for: media.url, alt, href, caption
  • Added StarRatingElement with full feature parity from old version:

    • Star count (5-10 range)
    • Star size (10-50px)
    • Star color with theme color picker
    • Selected stars value (0.1 step increments, supports half stars)
    • Interactive star selection (click to toggle between full/half)
    • Star alignment (left, center, right)
    • Optional review text with RichText editing
    • Review text alignment (left, center, right, justify)
    • Review text color
    • Dynamic data bindings for: selectedStars, reviewText
  • Updated infrastructure:

    • Added new element types to CellElement union in attributes.ts
    • Updated elements/index.ts exports
    • Updated CellChildAutoCompleter.tsx to enable image and star-rating
    • Updated cell-inserter/items.ts to enable image and star-rating
    • Updated Cell component to render new elements
    • Updated bindable-attributes.ts to support new elements
    • Added SCSS styles for both elements

Architecture decisions:

  • Both elements follow the established pattern (ElementRendererProps, controls separation, store integration)
  • Image element uses WordPress MediaUpload and ResizableBox for native WP experience
  • Star rating uses SVG-based Star component from @tableberg/shared/icons for fractional star rendering
  • Both elements integrate with sortPreviewMode (selection disabled during sort preview)
  • Both support dynamic data bindings following established pattern