From dc70d7eb1fa7cd571a9a0c5d2e1bdf450daf30d4 Mon Sep 17 00:00:00 2001 From: HerbertJulio Date: Mon, 22 Jun 2026 09:32:18 -0300 Subject: [PATCH 1/4] [ENG-46087] feat(webkit): replace data-table with composable Table and Paginator Add the composition + data-driven Table and the Paginator under data/, replacing the legacy data-table component. - Table: compound API (Table.Header/Body/Row/Cell/...), data-driven mode via the @tanstack/vue-table engine (sorting/selection/pagination/global filter), frozen columns + sticky header, grow-to-fill sizing, per-column `resizable`, and an action column auto-pinned to the trailing edge. - Paginator: pagination-button / paginator-info / paginator-page-size parts. - Remove the legacy data/data-table component; add data/table + data/paginator exports (the data/data-table export is dropped, no alias kept). - Specs and Storybook stories for Table and Paginator. --- .specs/data-table.md | 197 ----- .specs/paginator.md | 210 +++++ .specs/table.md | 303 +++++++ .../stories/webkit/data/DataTable.stories.js | 416 --------- .../stories/webkit/data/Paginator.stories.js | 259 ++++++ .../src/stories/webkit/data/Table.stories.js | 804 ++++++++++++++++++ packages/webkit/package.json | 35 +- .../composables/use-data-table-context.js | 14 - .../composables/use-data-table-filter.js | 33 - .../composables/use-data-table-pagination.js | 50 -- .../composables/use-data-table-selection.js | 51 -- .../composables/use-data-table-sort.js | 37 - .../data/data-table/data-table-actions.vue | 30 - .../data-table/data-table-batch-actions.vue | 62 -- .../data/data-table/data-table-breadcrumb.vue | 66 -- .../data-table/data-table-column-selector.vue | 100 --- .../data/data-table/data-table-column.vue | 78 -- .../data/data-table/data-table-export.vue | 57 -- .../data-table/data-table-filter-chips.vue | 72 -- .../data/data-table/data-table-filter.vue | 161 ---- .../data-table/data-table-inline-create.vue | 86 -- .../data-table-last-modified-popup.vue | 60 -- .../data-table/data-table-position-input.vue | 58 -- .../data-table/data-table-refresh-button.vue | 48 -- .../data-table/data-table-review-changes.vue | 61 -- .../data-table/data-table-row-actions.vue | 126 --- .../data/data-table/data-table-search.vue | 80 -- .../data/data-table/data-table-toolbar.vue | 57 -- .../data-table/data-table-view-all-footer.vue | 47 - .../data-table/data-table-view-toggle.vue | 67 -- .../components/data/data-table/data-table.vue | 612 ------------- .../data/data-table/injection-key.ts | 87 -- .../components/data/data-table/package.json | 11 - .../data-table/utils/format-filter-value.js | 7 - .../src/components/data/paginator/index.ts | 30 + .../data/paginator/injection-key.ts | 8 + .../components/data/paginator/package.json | 11 + .../paginator/pagination-button/package.json | 11 + .../pagination-button/pagination-button.vue | 84 ++ .../paginator/paginator-info/package.json | 11 + .../paginator-info/paginator-info.vue | 33 + .../paginator-page-size/package.json | 11 + .../paginator-page-size.vue | 75 ++ .../components/data/paginator/paginator.vue | 198 +++++ .../webkit/src/components/data/table/index.ts | 50 ++ .../components/data/table/injection-key.ts | 28 + .../src/components/data/table/package.json | 11 + .../data/table/table-body/package.json | 11 + .../data/table/table-body/table-body.vue | 36 + .../data/table/table-caption/package.json | 11 + .../table/table-caption/table-caption.vue | 34 + .../data/table/table-cell/package.json | 11 + .../data/table/table-cell/table-cell.vue | 88 ++ .../data/table/table-footer/package.json | 11 + .../data/table/table-footer/table-footer.vue | 34 + .../data/table/table-head-cell/package.json | 11 + .../table/table-head-cell/table-head-cell.vue | 124 +++ .../data/table/table-header/package.json | 11 + .../data/table/table-header/table-header.vue | 60 ++ .../data/table/table-row/package.json | 11 + .../data/table/table-row/table-row.vue | 54 ++ .../data/table/table-search/package.json | 11 + .../data/table/table-search/table-search.vue | 48 ++ .../data/table/table-sort-button/package.json | 11 + .../table-sort-button/table-sort-button.vue | 66 ++ .../data/table/table-toolbar/package.json | 11 + .../table/table-toolbar/table-toolbar.vue | 33 + .../src/components/data/table/table.vue | 783 +++++++++++++++++ pnpm-lock.yaml | 20 + 69 files changed, 3643 insertions(+), 2850 deletions(-) delete mode 100644 .specs/data-table.md create mode 100644 .specs/paginator.md create mode 100644 .specs/table.md delete mode 100644 apps/storybook/src/stories/webkit/data/DataTable.stories.js create mode 100644 apps/storybook/src/stories/webkit/data/Paginator.stories.js create mode 100644 apps/storybook/src/stories/webkit/data/Table.stories.js delete mode 100644 packages/webkit/src/components/data/data-table/composables/use-data-table-context.js delete mode 100644 packages/webkit/src/components/data/data-table/composables/use-data-table-filter.js delete mode 100644 packages/webkit/src/components/data/data-table/composables/use-data-table-pagination.js delete mode 100644 packages/webkit/src/components/data/data-table/composables/use-data-table-selection.js delete mode 100644 packages/webkit/src/components/data/data-table/composables/use-data-table-sort.js delete mode 100644 packages/webkit/src/components/data/data-table/data-table-actions.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-batch-actions.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-breadcrumb.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-column-selector.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-column.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-export.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-filter-chips.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-filter.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-inline-create.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-last-modified-popup.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-position-input.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-refresh-button.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-review-changes.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-row-actions.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-search.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-toolbar.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-view-all-footer.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table-view-toggle.vue delete mode 100644 packages/webkit/src/components/data/data-table/data-table.vue delete mode 100644 packages/webkit/src/components/data/data-table/injection-key.ts delete mode 100644 packages/webkit/src/components/data/data-table/package.json delete mode 100644 packages/webkit/src/components/data/data-table/utils/format-filter-value.js create mode 100644 packages/webkit/src/components/data/paginator/index.ts create mode 100644 packages/webkit/src/components/data/paginator/injection-key.ts create mode 100644 packages/webkit/src/components/data/paginator/package.json create mode 100644 packages/webkit/src/components/data/paginator/pagination-button/package.json create mode 100644 packages/webkit/src/components/data/paginator/pagination-button/pagination-button.vue create mode 100644 packages/webkit/src/components/data/paginator/paginator-info/package.json create mode 100644 packages/webkit/src/components/data/paginator/paginator-info/paginator-info.vue create mode 100644 packages/webkit/src/components/data/paginator/paginator-page-size/package.json create mode 100644 packages/webkit/src/components/data/paginator/paginator-page-size/paginator-page-size.vue create mode 100644 packages/webkit/src/components/data/paginator/paginator.vue create mode 100644 packages/webkit/src/components/data/table/index.ts create mode 100644 packages/webkit/src/components/data/table/injection-key.ts create mode 100644 packages/webkit/src/components/data/table/package.json create mode 100644 packages/webkit/src/components/data/table/table-body/package.json create mode 100644 packages/webkit/src/components/data/table/table-body/table-body.vue create mode 100644 packages/webkit/src/components/data/table/table-caption/package.json create mode 100644 packages/webkit/src/components/data/table/table-caption/table-caption.vue create mode 100644 packages/webkit/src/components/data/table/table-cell/package.json create mode 100644 packages/webkit/src/components/data/table/table-cell/table-cell.vue create mode 100644 packages/webkit/src/components/data/table/table-footer/package.json create mode 100644 packages/webkit/src/components/data/table/table-footer/table-footer.vue create mode 100644 packages/webkit/src/components/data/table/table-head-cell/package.json create mode 100644 packages/webkit/src/components/data/table/table-head-cell/table-head-cell.vue create mode 100644 packages/webkit/src/components/data/table/table-header/package.json create mode 100644 packages/webkit/src/components/data/table/table-header/table-header.vue create mode 100644 packages/webkit/src/components/data/table/table-row/package.json create mode 100644 packages/webkit/src/components/data/table/table-row/table-row.vue create mode 100644 packages/webkit/src/components/data/table/table-search/package.json create mode 100644 packages/webkit/src/components/data/table/table-search/table-search.vue create mode 100644 packages/webkit/src/components/data/table/table-sort-button/package.json create mode 100644 packages/webkit/src/components/data/table/table-sort-button/table-sort-button.vue create mode 100644 packages/webkit/src/components/data/table/table-toolbar/package.json create mode 100644 packages/webkit/src/components/data/table/table-toolbar/table-toolbar.vue create mode 100644 packages/webkit/src/components/data/table/table.vue diff --git a/.specs/data-table.md b/.specs/data-table.md deleted file mode 100644 index 82a2d17c..00000000 --- a/.specs/data-table.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -name: data-table -category: data -structure: composition -status: implemented -spec_version: 1 -created: 2026-05-22 -last_updated: 2026-05-22 -checksum: 3dcb1a858556ea3dbb0ecc50481128baea0d457069fc8da30a031db7c79d4033 ---- - - -# DataTable — Component Spec - -## Purpose - -Composition-based data table for listing, filtering, sorting, and acting on tabular records. Mirrors the `core/list-data-table` API (toolbar + column slots) using webkit primitives instead of PrimeVue. Supports headerless list rows (workloads layout) and full column-header grid mode. No TanStack Table dependency. - -## Sub-components - -- `data-table.vue` — Root shell, table engine, empty/loading logic, CSV export. -- `data-table-column.vue` — Column registration with `#header` and `#body` scoped slots. -- `data-table-toolbar.vue` — Two-line toolbar with `first-line` and `second-line` slots. -- `data-table-search.vue` — Debounced search input bound to global filter. -- `data-table-actions.vue` — Trailing toolbar action cluster slot wrapper. -- `data-table-export.vue` — Export/CSV trigger button. -- `data-table-column-selector.vue` — Toggle column visibility panel. -- `data-table-filter.vue` — Anchored filter panel with field picker and `filter-field` slot. -- `data-table-filter-chips.vue` — Removable applied-filter chips row. -- `data-table-row-actions.vue` — Per-row kebab menu or inline action. -- `data-table-batch-actions.vue` — Bulk selection action bar. -- `data-table-breadcrumb.vue` — Path navigation above the table. -- `data-table-inline-create.vue` — Inline create row form. -- `data-table-position-input.vue` — Row order position input. -- `data-table-refresh-button.vue` — Table refresh control. -- `data-table-review-changes.vue` — Pending reorder/edit review footer. -- `data-table-view-toggle.vue` — View mode switcher. -- `data-table-view-all-footer.vue` — View-all footer CTA. -- `data-table-last-modified-popup.vue` — Hover popup for last-modified metadata. - -## Props - -| Prop | Type | Default | Required | JSDoc | -|---|---|---|---|---| -| `data` | `Record[]` | `[]` | no | Row records to render. | -| `dataKey` | `string` | `'id'` | no | Unique row identifier field. | -| `columns` | `ColumnDefinition[]` | `[]` | no | Column metadata for skeleton, export, and column selector. | -| `loading` | `boolean` | `false` | no | Shows loading skeleton rows when true. | -| `totalRecords` | `number` | `0` | no | Total record count for lazy pagination. | -| `lazy` | `boolean` | `false` | no | When true, pagination and sort emit events for server fetch. | -| `first` | `number` | `0` | no | Zero-based index of the first row on the current page. | -| `hideHeader` | `boolean` | `false` | no | Hides column headers; renders headerless list rows. | -| `rowHover` | `boolean` | `true` | no | Applies hover surface token on rows. | -| `scrollable` | `boolean` | `false` | no | Wraps the table body in a scroll area. | -| `scrollHeight` | `string` | `''` | no | Max height when scrollable is true. | -| `showGridlines` | `boolean` | `false` | no | Shows row/column divider borders. | -| `filters` | `Record` | `{}` | no | Active filter state including global search. | -| `sortField` | `string` | `''` | no | Current sort field name. | -| `sortOrder` | `number` | `1` | no | Sort direction: 1 ascending, -1 descending. | -| `sortMode` | `'single' \| 'multiple'` | `'single'` | no | Single or multi-column sort mode. | -| `globalFilterFields` | `string[]` | `[]` | no | Fields included in global text search. | -| `searchValue` | `string` | `''` | no | Bound search input value for empty-state logic. | -| `appliedFilters` | `FilterDefinition[]` | `[]` | no | Applied filter chips shown below the toolbar. | -| `paginator` | `boolean` | `false` | no | Enables pagination controls. | -| `rows` | `number` | `10` | no | Rows per page. | -| `rowsPerPageOptions` | `number[]` | `[10, 25, 50, 100]` | no | Page size options in the paginator. | -| `selection` | `Record \| Record[] \| null` | `null` | no | Selected row or rows. | -| `selectable` | `boolean` | `false` | no | Enables row selection checkboxes. | -| `editMode` | `'row' \| 'cell'` | `'row'` | no | Row or cell edit mode. | -| `editingRows` | `Record[]` | `[]` | no | Rows currently in edit state. | -| `exportFilename` | `string` | `'export'` | no | Default CSV export filename. | -| `exportFunction` | `(options: { data: Record[]; fields: string[] }) => void \| null` | `null` | no | Custom export handler; null uses built-in CSV. | -| `emptyListMessage` | `string` | `'No data available'` | no | In-table empty message when filtered with no rows. | -| `emptyBlock` | `EmptyBlockConfig` | `{ title: 'No data has been created', description: 'No data has been created.', createButtonLabel: 'Create' }` | no | Full-page empty block configuration. | -| `notShowEmptyBlock` | `boolean` | `false` | no | Always renders the table shell even when data is empty. | -| `hasEmptyBlockSlot` | `boolean` | `false` | no | Uses the emptyBlock slot instead of the default block. | -| `skeletonRows` | `number` | `5` | no | Skeleton placeholder row count while loading. | -| `frozenValue` | `Record[]` | `[]` | no | Frozen rows pinned above the scroll body. | -| `resizableColumns` | `boolean` | `false` | no | Reserved; column resize deferred in v1. | -| `rowClass` | `(row: Record) => string \| null` | `null` | no | Returns extra classes for a row. | -| `expandableRowGroups` | `boolean` | `false` | no | Enables expandable row groups. | -| `rowGroupMode` | `'subheader' \| 'rowspan'` | `'subheader'` | no | Row group rendering mode. | -| `groupRowsBy` | `string` | `''` | no | Field used to group rows. | -| `expandedRowGroups` | `string[]` | `[]` | no | Currently expanded group keys. | - -## Events - -| Event | Payload | Notes | -|---|---|---| -| `update:filters` | `value: Record` | v-model:filters. | -| `update:sortField` | `value: string` | v-model:sortField. | -| `update:sortOrder` | `value: number` | v-model:sortOrder. | -| `update:selection` | `value: Record \| Record[] \| null` | v-model:selection. | -| `update:editingRows` | `value: Record[]` | v-model:editingRows. | -| `update:expandedRowGroups` | `value: string[]` | v-model:expandedRowGroups. | -| `update:first` | `value: number` | v-model:first. | -| `page` | `event: PageEvent` | Fires on page change. | -| `sort` | `event: SortEvent` | Fires on sort change. | -| `filter` | `event: Record` | Fires when filters change. | -| `row-click` | `event: { data: Record; originalEvent: MouseEvent }` | Fires on row click. | -| `row-reorder` | `event: RowOrderingResult` | Fires when rows are reordered. | -| `row-edit-save` | `event: RowEditEvent` | Fires when a row edit is saved. | -| `row-edit-cancel` | `event: RowEditEvent` | Fires when a row edit is cancelled. | -| `click-to-create` | `void` | Fires from the empty block create action. | - -## Slots - -| Slot | Scope | Notes | -|---|---|---| -| `default` | — | DataTableColumn children. | -| `header` | — | Toolbar area; typically DataTableToolbar. | -| `footer` | — | Table footer content. | -| `empty` | — | Custom in-table empty state. | -| `emptyBlock` | — | Full-page empty block replacement. | -| `emptyBlockButton` | — | Trailing action in the empty block. | -| `illustration` | — | Illustration in the empty block. | -| `loading` | — | Custom loading overlay. | -| `expansion` | `{ data: Record; index: number }` | Row expansion content. | -| `groupheader` | `{ data: Record }` | Row group header content. | -| `groupfooter` | `{ data: Record }` | Row group footer content. | - -## States - -- Visual states: `default`, `hover`, `focus-visible`, `active`, `disabled`, `loading` -- `data-state` values: `selected` | `unselected` on rows when selectable -- `data-disabled` mirrors disabled controls in toolbar sub-components -- `data-loading` mirrors the root `loading` prop - -## Motion & Animations - -| Trigger | Animation / Transition | Token | Reduced-motion fallback | -|---|---|---|---| -| row hover | `transition-colors duration-150 ease-out` | inline | `motion-reduce:transition-none` | -| filter chip remove | `transition-opacity duration-150 ease-out` | inline | `motion-reduce:transition-none` | -| loading skeleton | `animate-blink` | semantic | `motion-reduce:animate-none` | - -## Tokens - -| Region | Token (DESIGN.md) | -|---|---| -| typography (primary cell) | `.text-body-sm` | -| typography (secondary cell) | `.text-body-xs` | -| typography (header) | `.text-body-sm` | -| surface | `var(--bg-surface)` | -| surface hover | `var(--bg-surface-hover)` | -| text | `var(--text-default)` | -| text muted | `var(--text-muted)` | -| border | `var(--border-default)` | -| spacing | `var(--spacing-3)` | -| spacing row | `var(--spacing-4)` | -| shape | `var(--shape-elements)` | -| ring | `var(--ring-color)` | - -## Theme gaps - -| Figma variable | Temporary primitive | Follow-up | -|---|---|---| -| _none_ | — | — | - -## Accessibility (WCAG 2.1 AA) - -- Visible focus: `focus-visible:ring-2 focus-visible:ring-[var(--ring-color)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-canvas)]` -- Keyboard map: `Tab` focuses interactive controls; `Enter`/`Space` activates buttons and sortable headers; `Escape` closes filter panel and row menus; arrow keys navigate paginator when focused. -- ARIA: root uses `role="table"` or `role="grid"`; header cells use `scope="col"`; selectable rows use `aria-selected`; sortable headers use `aria-sort`. -- Contrast ≥4.5:1 (text) / ≥3:1 (large + icons), including disabled state. -- `motion-reduce:transition-none motion-reduce:transform-none` on animated states. -- Touch target ≥40×40 px on toolbar and row action controls. - -## Stories (Storybook) - -- Default — basic three-column table with headers. -- WithFilters — workloads-like layout (filter toggle, search, chips, headerless rows); justified because filter/search/chip interaction is the primary UX differentiator. -- WithLazyLoad — server-driven pagination with lazy prop; justified because lazy + totalRecords is a distinct data-fetch path. -- Empty — empty block vs in-table empty; justified because root visibility logic is non-trivial. - -## Constraints — DO NOT - - - -- Do not add props beyond the Props table above. If you need a prop that is not listed, emit `BLOCKED: missing prop ` and stop — do not invent. -- Do not add events beyond the Events table above. Same rule for slots and sub-components. -- Do not invent imports. Every `@aziontech/webkit/*` path must exist in `packages/webkit/package.json#exports`. Every relative import must resolve to a real file. Every npm package must be installed. -- Do not use HEX/RGB/HSL colors, Tailwind palette names (e.g. `bg-blue-500`), raw typography classes (e.g. `text-sm`), `any`, `@ts-ignore`, or `class` inside `defineProps`. -- Do not install or import positioning/animation libraries (`@floating-ui/*`, `popper.js`, `tippy.js`, `gsap`, `framer-motion`, `motion`, `@vueuse/motion`, `@formkit/auto-animate`, drag-drop runtimes, scroll virtualization libs). Use CSS + Vue primitives (``, ``). See `.claude/rules/dependencies.md`. -- Do not improvise animations. Every `animate-*` / `transition-*` class must come from `packages/theme/src/tokens/semantic/animations.js`; every motion-bearing class pairs with `motion-reduce:*` on the same class string; no component-local `@keyframes`. -- Do not create class presets in JavaScript (`const kindClasses = {...}`, `const sharedClasses = [...]`, `const sizeClasses = {...}`, `const rootClasses = computed(...)`). Variants live on `data-*` attributes consumed by Tailwind `data-[attr=value]:`. All utilities live inline on the root element's `class` attribute. No `