bitboss-ui 3.0.0-beta.4 → 3.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -29
- package/dist/ai/BbBadge.md +10 -5
- package/dist/ai/BbSelect.md +8 -0
- package/dist/ai/BbTable.md +257 -51
- package/dist/ai/changelog.json +86 -2
- package/dist/ai/components.json +25 -15
- package/dist/ai/guides/design-language.md +14 -14
- package/dist/ai/guides/design-tokens.md +25 -18
- package/dist/ai/guides/installation-and-plugin-setup.md +1 -1
- package/dist/ai/guides/migration/components/bb-dialog.md +35 -29
- package/dist/ai/guides/migration/components/bb-table.md +406 -28
- package/dist/ai/guides/migration/v2-to-v3.md +175 -75
- package/dist/ai/index.md +1 -1
- package/dist/ai/recipes/inertia/layout-scaffold.md +5 -1
- package/dist/ai/recipes/inertia/ownership-atlas.md +41 -20
- package/dist/ai/recipes/inertia/records-workspace.md +26 -7
- package/dist/ai/recipes/nuxt/layout-scaffold.md +5 -1
- package/dist/ai/recipes/nuxt/records-workspace.md +26 -7
- package/dist/ai/recipes/vue/layout-scaffold.md +5 -1
- package/dist/ai/recipes/vue/records-workspace.md +26 -7
- package/dist/ai/source/BbBaseInputContainer.md +26 -0
- package/dist/ai/source/BbBreadcrumbs.md +31 -0
- package/dist/ai/source/BbDialog.md +15 -9
- package/dist/ai/source/BbOffCanvas.md +17 -11
- package/dist/ai/source/BbProgress.md +2 -2
- package/dist/ai/source/BbTable.md +837 -787
- package/dist/ai/source/CommonInputWrapper.md +4 -0
- package/dist/components/BbPagination/BbPagination.vue.d.ts +1 -1
- package/dist/components/BbTable/BbTable.vue.d.ts +5 -2
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +439 -484
- package/dist/components/BbTable/types.d.ts +83 -21
- package/dist/components/BbTable/utils.d.ts +18 -3
- package/dist/components/BbTable/utils.js +36 -28
- package/dist/index.d.ts +5 -1
- package/dist/index.js +68 -67
- package/dist/llms-full.txt +1065 -265
- package/dist/llms-medium.txt +15 -15
- package/dist/llms.txt +1 -1
- package/dist/styles.css +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/assets/svgs/sort.svg_raw.js +0 -4
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Migration v2→v3: BbTable'
|
|
3
|
-
summary: 'allowSelectAll inverted to disableSelectAll; loading no longer blanks populated tables and now inerts rows; #loading is first-load-only; #no-data fills a full-width
|
|
3
|
+
summary: 'The table renders as a CSS grid of divs with ARIA roles, not a <table> — every element selector (table/thead/tbody/tr/th/td) under .bb-table matches nothing, #thead/#tbody/#tfoot take role-complete div rows, --fill/--natural-width are gone, column widths drift within documented bounds, copy-to-spreadsheet and print header repetition degrade; allowSelectAll inverted to disableSelectAll; loading no longer blanks populated tables and now inerts rows; #loading is first-load-only; #no-data fills a full-width cell instead of replacing the row; row events drop the selected argument; columns gained sortable + rowClass, which collide with same-named consumer fields.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# BbTable — v2 → v3
|
|
7
7
|
|
|
8
8
|
## Changes
|
|
9
9
|
|
|
10
|
-
| v2
|
|
11
|
-
|
|
|
12
|
-
| `allowSelectAll?: boolean` (default `true`)
|
|
13
|
-
| refetch with rows → skeleton
|
|
14
|
-
| rows interactive while loading
|
|
15
|
-
| `#loading` slot fired on every load
|
|
16
|
-
| `#no-data` replaced the empty-state `<tr>`
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| `
|
|
20
|
-
|
|
|
10
|
+
| v2 | v3 | Kind |
|
|
11
|
+
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `allowSelectAll?: boolean` (default `true`) | `disableSelectAll?: boolean` (default `false`) | rename, polarity inverted |
|
|
13
|
+
| refetch with rows → skeleton | refetch keeps rows visible (dimmed + progress bar) | **behavior** — the skeleton only shows when there is nothing to display |
|
|
14
|
+
| rows interactive while loading | header + rows go `inert` while loading | **behavior** — opt out with `interactive-while-loading` |
|
|
15
|
+
| `#loading` slot fired on every load | fires on the **first load only** (it replaces the skeleton, so it follows the skeleton's rule) | **⚠ silent** for anyone styling refetches through it |
|
|
16
|
+
| `#no-data` replaced the empty-state `<tr>` | fills the table's own full-width cell (`aria-colspan` counted, centred) | **behavior** — pass content, not a row |
|
|
17
|
+
| `<table>` / `<thead>` / `<tbody>` / `<tr>` / `<th>` / `<td>` markup _(v2 and v3 ≤ beta.5)_ | a CSS grid of `<div>`s carrying `role="table\|rowgroup\|row\|columnheader\|cell"`; `.bb-table__table` / `__head` / `__body` / `__foot` are the new containers, every `.bb-table*` class survives | **⚠ silent** for element selectors, `#thead`/`#tbody`/`#tfoot` markup, copy/paste and print — see below |
|
|
18
|
+
| `--fill` / `--natural-width` published on `.bb-table` _(v3 ≤ beta.5)_ | **removed** — nothing publishes them | **⚠ silent** — see below |
|
|
19
|
+
| — | props `enforceCoherence` (prunes row-keyed models after each load; incompatible with pagination-via-`dependencies`), `expandedItems` + `#expand`, `sort`, `keyboardNavigation`, `highlighted`, `rowClass` | additive |
|
|
20
|
+
| — | **column** fields `sortable?: boolean`, `rowClass?: ColumnClasses` | additive, **collides** — see below |
|
|
21
|
+
| `useBbTableContext(id).total` _(v3 alphas ≤ 4)_ | `useBbTableContext(id).totalItems` | **hard rename** on the composable handle — see below |
|
|
22
|
+
| `@click:row` / `@contextmenu:row` / `@dblclick:row` handlers receive `(event, item, selected)` _(v2 and v3 ≤ beta.5)_ | `(event, item)` — `item` is now your raw record (was the internal row wrapper, record at `row.item`); the trailing `selected` boolean is gone | **payload slimmed** — see below |
|
|
23
|
+
| `--bb-table-*` custom properties | same names without the `bb-` prefix | **⚠ silent** for nested-table layouts — see below |
|
|
21
24
|
|
|
22
25
|
## Columns gained `sortable` and `rowClass` — check yours first
|
|
23
26
|
|
|
@@ -70,7 +73,8 @@ migration. Fix it at the type, not at the call sites:
|
|
|
70
73
|
`BbTable` coerces the field (`sortable: !!column.sortable` in `BbTable.vue`), so
|
|
71
74
|
**any** truthy value counts — including a sort-key string like
|
|
72
75
|
`'patients.created_at'`. The header stops rendering a plain label and renders a
|
|
73
|
-
`.bb-table-sort` button instead, and the
|
|
76
|
+
`.bb-table-sort` button instead, and the header cell (`.bb-table-header`,
|
|
77
|
+
`role="columnheader"`) gains `aria-sort="none"`. An app
|
|
74
78
|
that already ships its own sortable-header UI now draws two.
|
|
75
79
|
|
|
76
80
|
The damage is bounded: `sort` is a controlled model (`v-model:sort`), so the
|
|
@@ -88,8 +92,9 @@ const tableColumns = computed(() =>
|
|
|
88
92
|
|
|
89
93
|
Column `rowClass` is the same shape of trap without the type error, because
|
|
90
94
|
`ColumnClasses` already accepts `string`: a leftover `rowClass: 'w-40'` you
|
|
91
|
-
meant for the column typechecks fine and is now merged onto the whole row
|
|
92
|
-
|
|
95
|
+
meant for the column typechecks fine and is now merged onto the whole row
|
|
96
|
+
(`.bb-table-data__row`), alongside the table-level `rowClass` prop. A
|
|
97
|
+
non-`Classes` shape
|
|
93
98
|
(`rowClass?: { colspan: number }`) does error, with the same `undefined`
|
|
94
99
|
wording.
|
|
95
100
|
|
|
@@ -127,9 +132,13 @@ first-paint placeholder.
|
|
|
127
132
|
v2 handed the slot the whole empty-state `<tr>`, so anything that was not a
|
|
128
133
|
row — the usual `<div>` with a message and a "clear filters" button — landed
|
|
129
134
|
loose inside `<tbody>` and rendered left-aligned at content width instead of
|
|
130
|
-
spanning the table. v3
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
spanning the table. v3 owns the row/cell shell itself
|
|
136
|
+
(`.bb-table-no-data__row[role=row]` > `.bb-table-no-data__cell[role=cell]`,
|
|
137
|
+
spanning every column with `grid-column: 1 / -1` and an `aria-colspan` that
|
|
138
|
+
already counts the selection and actions columns) and puts the slot inside it,
|
|
139
|
+
so that same markup now centres across the full width. A v2 `<tr>`/`<td>`
|
|
140
|
+
passed here is not just redundant any more — it is table markup inside a
|
|
141
|
+
grid, which is why the diff below deletes it rather than keeping it.
|
|
133
142
|
|
|
134
143
|
```diff
|
|
135
144
|
<BbTable :items="rows" :columns="columns" selectable actions>
|
|
@@ -184,6 +193,372 @@ unrelated and does not change.
|
|
|
184
193
|
`BbPagination` consumers see the same rename through `table-id`; see
|
|
185
194
|
[BbPagination](./bb-pagination.md).
|
|
186
195
|
|
|
196
|
+
### Row events carry `(event, item)` — your raw record (v3 beta, 2026-08-26)
|
|
197
|
+
|
|
198
|
+
`click:row`, `contextmenu:row` and `dblclick:row` used to pass **the table's
|
|
199
|
+
internal row wrapper** (cells, classes, selection/highlight/expansion flags,
|
|
200
|
+
value hash — your record buried at `row.item`) plus a third `selected`
|
|
201
|
+
argument. Both are gone: the payload is the native event and **the record
|
|
202
|
+
exactly as you passed it in `items`**, nothing else. Selection state is
|
|
203
|
+
already yours through the `v-model` / `unselected-items` models, so the
|
|
204
|
+
handler read it from a second source of truth — and the owner wants row events
|
|
205
|
+
to carry the row, not a state dump.
|
|
206
|
+
|
|
207
|
+
```diff
|
|
208
|
+
- <BbTable @click:row="(event, row, selected) => open(row.item, selected)" />
|
|
209
|
+
+ <BbTable @click:row="(event, item) => open(item, selectedIds.includes(item.id))" />
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Two silent JavaScript failures and one loud one: `row.item` is now
|
|
213
|
+
`undefined` (the record has no `.item` field unless yours does), the third
|
|
214
|
+
parameter is simply `undefined`, and any handler typed against `BbTableEvents`
|
|
215
|
+
with three parameters is a **type error** under `vue-tsc` 3. Grep:
|
|
216
|
+
`rg '(click|contextmenu|dblclick):row' --type vue` and inspect every handler
|
|
217
|
+
for `.item` reads and a third parameter.
|
|
218
|
+
|
|
219
|
+
## The table is a CSS grid, not a `<table>` (v3 beta, 2026-08-26)
|
|
220
|
+
|
|
221
|
+
`BbTable` no longer renders a single `<table>` element. The substrate is a CSS
|
|
222
|
+
grid of `<div>`s: the root owns `grid-template-columns` (the column tracks
|
|
223
|
+
_are_ the width API — `columns[].width`, `fixed`, `inherit-column-widths` and
|
|
224
|
+
`snap` all resolve to tracks), and every row group and row is a `subgrid` of
|
|
225
|
+
it, so a cell lands on its column through two levels without a width of its
|
|
226
|
+
own. Nothing about the props, slots, events or models changed for this; what
|
|
227
|
+
changed is everything that touched the **markup** — CSS element selectors,
|
|
228
|
+
the markup you hand to `#thead` / `#tbody` / `#tfoot`, two published custom
|
|
229
|
+
properties, and a handful of things browsers do for real tables that they do
|
|
230
|
+
not do for grids (copy/paste, print, global element CSS).
|
|
231
|
+
|
|
232
|
+
### What renders now
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
.bb-table container (div, or fieldset when selectable) — unchanged: [data-bb-table-id], aria-busy, overflow-x: auto
|
|
236
|
+
legend.sr-only (selectable) — unchanged
|
|
237
|
+
.bb-table-caption[id] replaces <caption>; sr-only unless display-caption; sits BEFORE the grid root
|
|
238
|
+
.bb-table__table[role=table] grid root — aria-labelledby → the caption, aria-rowcount
|
|
239
|
+
.bb-table__head[role=rowgroup] replaces <thead>; goes inert while loading; hosts the busy bar
|
|
240
|
+
.bb-table-header-row[role=row]
|
|
241
|
+
.bb-table-header[role=columnheader] (+ --select / --actions / --edge-start / --edge-end, aria-sort)
|
|
242
|
+
.bb-table__body[role=rowgroup] replaces <tbody>
|
|
243
|
+
.bb-table-data__row[role=row] tabindex / aria-rowindex / aria-current / inert / row events — as before
|
|
244
|
+
.bb-table-data__cell[role=cell] (+ --select / --edge-start / --edge-end)
|
|
245
|
+
.bb-table__cell.bb-table__cell--actions[role=cell]
|
|
246
|
+
.bb-table-expand__row[role=row] id="expanded_{hash}" (the aria-controls target) — unchanged
|
|
247
|
+
.bb-table-expand__cell[role=cell] aria-colspan=N, grid-column: 1 / -1
|
|
248
|
+
.bb-table-no-data__row > .bb-table-no-data__cell[role=cell] aria-colspan=N, grid-column: 1 / -1
|
|
249
|
+
.bb-table-skeleton__row > .bb-table-skeleton__cell[role=cell]
|
|
250
|
+
.bb-table-loading__row > .bb-table-loading__cell[role=cell] > span.bb-table-loading__text[role=status][aria-live=polite]
|
|
251
|
+
.bb-table__foot replaces <tfoot>; always rendered, role=rowgroup only when #tfoot is provided
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Every `.bb-table*` class survives on the equivalent node** — including the
|
|
255
|
+
asymmetric `bb-table__cell--actions` and the caption's `bb-table-caption` (no
|
|
256
|
+
`__`). The one exception is the transient `bb-table--measuring` state class,
|
|
257
|
+
which was never documented and existed only to hide the old probe's
|
|
258
|
+
scrollbar flash: it is gone with the probe. Four container classes are new: `.bb-table__table`, `.bb-table__head`,
|
|
259
|
+
`.bb-table__body`, `.bb-table__foot`. Every row carries `role="row"`, every
|
|
260
|
+
header cell `role="columnheader"`, every body/foot cell `role="cell"`. The
|
|
261
|
+
role chain `table → rowgroup → row → columnheader|cell` is what assistive
|
|
262
|
+
technology reads as a table; without it the whole tree is a pile of generic
|
|
263
|
+
`div`s. `aria-colspan` replaces the deleted `colspan` on the three full-width
|
|
264
|
+
cells (expand, no-data, loading).
|
|
265
|
+
|
|
266
|
+
### Selector translation
|
|
267
|
+
|
|
268
|
+
Element selectors are the break. Nothing throws: a rule on `tbody td` matches
|
|
269
|
+
nothing, the browser applies nothing, and the page renders with the
|
|
270
|
+
override silently missing. Translate every element name under `.bb-table`:
|
|
271
|
+
|
|
272
|
+
| v2 element | v3 selector |
|
|
273
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
274
|
+
| `table` | `.bb-table__table` |
|
|
275
|
+
| `thead` | `.bb-table__head` |
|
|
276
|
+
| `tbody` | `.bb-table__body` |
|
|
277
|
+
| `tfoot` | `.bb-table__foot` |
|
|
278
|
+
| `caption` | `.bb-table-caption` |
|
|
279
|
+
| `tr` | `[role=row]` — or `.bb-table-data__row` for data rows only (the old bare `tr` matched every row kind: header, skeleton, no-data, expand, loading) |
|
|
280
|
+
| `th` | `[role=columnheader]` / `.bb-table-header` |
|
|
281
|
+
| `td` | `[role=cell]` / `.bb-table-data__cell` |
|
|
282
|
+
| `:first-child` / `:last-child` / `:not(.bb-table-expand__cell)` | **verbatim** — cells stay siblings inside their row |
|
|
283
|
+
| `td:nth-of-type(n)` | `:nth-child(n)` — the children are type-uniform now, so `nth-of-type` and `nth-child` mean the same thing |
|
|
284
|
+
| `colgroup` / `col` | **gone** — use `columns[].width` |
|
|
285
|
+
|
|
286
|
+
`[role=row]`, `[role=cell]` and `[role=columnheader]` are **supported styling
|
|
287
|
+
hooks**, not incidental markup: the role tree is pinned by the component's own
|
|
288
|
+
tests, so a future move to `role="grid"` would be a deliberate selector break,
|
|
289
|
+
not an accident. There are no `data-*` hooks and no shim classes — the roles
|
|
290
|
+
plus the four container classes are the whole translation.
|
|
291
|
+
|
|
292
|
+
**Specificity caveat.** `[role=cell]` is an attribute selector, specificity
|
|
293
|
+
(0,1,0); `td` was a type selector, (0,0,1). A translated rule is therefore
|
|
294
|
+
_stronger_ than the one it replaces, and an override that used to lose to a
|
|
295
|
+
library rule (or to another of your own rules) by one type-selector point can
|
|
296
|
+
now win. Diff any page where two rules targeted the same cell, and prefer the
|
|
297
|
+
class forms (`.bb-table-data__cell`) when you need the old ordering back.
|
|
298
|
+
|
|
299
|
+
The library's own body-cell rules are deliberately held at **(0,2,0)** — the
|
|
300
|
+
row step is wrapped in `:where()` — so a scoped override of yours that beat
|
|
301
|
+
the old `.bb-table tbody tr td` (0,1,3) at (0,2,0) or better keeps winning.
|
|
302
|
+
An override written at exactly (0,1,x) (one class plus type selectors) is
|
|
303
|
+
the only band that loses ground: give it one more class.
|
|
304
|
+
|
|
305
|
+
The one idiom that dominates real apps — a full-bleed table that re-adds the
|
|
306
|
+
page inset on its outermost cells, scoped with direct-child combinators so a
|
|
307
|
+
nested table inheriting the parent's tracks is not touched — translates like
|
|
308
|
+
this:
|
|
309
|
+
|
|
310
|
+
```diff
|
|
311
|
+
- .table-flush :deep(> table > thead > tr > th:first-child),
|
|
312
|
+
- .table-flush :deep(> table > tbody > tr > td:first-child:not(.bb-table-expand__cell)) {
|
|
313
|
+
+ .table-flush :deep(> .bb-table__table > .bb-table__head > [role='row'] > :first-child),
|
|
314
|
+
+ .table-flush :deep(> .bb-table__table > .bb-table__body > [role='row'] > :first-child:not(.bb-table-expand__cell)) {
|
|
315
|
+
padding-inline-start: var(--page-inset);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
- .table-flush :deep(> table > tbody > tr > td.bb-table-expand__cell) {
|
|
319
|
+
+ .table-flush :deep(> .bb-table__table > .bb-table__body > [role='row'] > .bb-table-expand__cell) {
|
|
320
|
+
padding-inline: 0;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The direct-child chain matters exactly as before: `.table-flush` lands on the
|
|
325
|
+
container, `.bb-table__table` is its direct child, and a nested table lives
|
|
326
|
+
inside `.bb-table-expand__cell`, so `>` at every step keeps the override off
|
|
327
|
+
the child's cells (whose padding is the alignment mechanism).
|
|
328
|
+
|
|
329
|
+
Grep for the hits: element names inside `:deep()`, inside `.bb-table` rules,
|
|
330
|
+
and — easy to miss — any **global** table CSS (`table { … }`, `td { padding }`,
|
|
331
|
+
`.prose table`), which reached the v2 markup and now reaches nothing (see
|
|
332
|
+
"Global element CSS" below).
|
|
333
|
+
|
|
334
|
+
### `#thead` / `#tbody` / `#tfoot` take role-complete rows
|
|
335
|
+
|
|
336
|
+
These three slots replace a whole row group, and the markup you pass into them
|
|
337
|
+
is now grid markup: a `<div role="row">` per row holding one
|
|
338
|
+
`<div role="columnheader">` (head) or `<div role="cell">` (body, foot) per
|
|
339
|
+
column — **never** a `<tr>` / `<th>` / `<td>`. A `<tr>` inside a grid is not
|
|
340
|
+
just wrong semantically: the `<tr>` is blockified into a single grid item and
|
|
341
|
+
the browser wraps its `<td>`s in an anonymous CSS table (under SSR the parser
|
|
342
|
+
drops the tags outright), so the cells never land on the tracks.
|
|
343
|
+
|
|
344
|
+
Rows are subgrid rows, so a cell takes the matching column track with no
|
|
345
|
+
width of its own. A cell that spans columns uses `grid-column`, not `colspan`:
|
|
346
|
+
the slot's `columnCount` (total rendered columns, selection and actions
|
|
347
|
+
included) is still handed to `#tfoot`, and it now feeds `grid-column: span N`
|
|
348
|
+
/ `grid-column: 1 / -1` instead of a `colspan` attribute. Add `aria-colspan`
|
|
349
|
+
on a spanning cell so column math stays right for assistive technology.
|
|
350
|
+
|
|
351
|
+
```diff
|
|
352
|
+
<template #tfoot="{ columnCount }">
|
|
353
|
+
- <tr>
|
|
354
|
+
- <td :colspan="columnCount - 1">Total</td>
|
|
355
|
+
- <td class="text-right">{{ total }}</td>
|
|
356
|
+
- </tr>
|
|
357
|
+
+ <div role="row">
|
|
358
|
+
+ <div
|
|
359
|
+
+ role="cell"
|
|
360
|
+
+ :aria-colspan="columnCount - 1"
|
|
361
|
+
+ :style="{ gridColumn: `span ${columnCount - 1}` }"
|
|
362
|
+
+ >
|
|
363
|
+
+ Total
|
|
364
|
+
+ </div>
|
|
365
|
+
+ <div role="cell" class="text-right">{{ total }}</div>
|
|
366
|
+
+ </div>
|
|
367
|
+
</template>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
A full-width row is `grid-column: 1 / -1` on its single cell (plus
|
|
371
|
+
`aria-colspan="columnCount"`). Keep the invariant the component keeps: no
|
|
372
|
+
role-bearing element between the row group and the row, and every child of a
|
|
373
|
+
row is a `cell` or `columnheader`. **Nothing lints this** — a slot ported to
|
|
374
|
+
`<div>`s without roles renders fine and ships a table screen readers cannot
|
|
375
|
+
read; the docs are the only guard, hence the emphasis.
|
|
376
|
+
|
|
377
|
+
Two side effects of replacing the header: the table measures its column
|
|
378
|
+
tracks off the header cells, so with a custom `#thead` it falls back to the
|
|
379
|
+
first row with one child per track (the first data row) — **a custom header
|
|
380
|
+
over an empty table publishes no tracks**, and a nested table cannot inherit
|
|
381
|
+
from it until rows arrive. And under `fixed-headers`, a `#thead` with several
|
|
382
|
+
rows now sticks as a block (all rows stack); the old cell-level sticky stacked
|
|
383
|
+
them on top of each other.
|
|
384
|
+
|
|
385
|
+
### `--fill` and `--natural-width` are removed, not renamed
|
|
386
|
+
|
|
387
|
+
Both were plumbing for the `table-layout: fixed` self-correction loop (a
|
|
388
|
+
measured natural width the table could never shrink below, and a fill
|
|
389
|
+
correction that compensated fixed-layout arithmetic). The grid has no such
|
|
390
|
+
loop: tracks are sized by the engine from the declarations, in one layout
|
|
391
|
+
pass, with no offscreen probe. Republishing the sum of the resolved tracks
|
|
392
|
+
under the same names would publish the _container_ width under a name
|
|
393
|
+
promising the _natural_ width — wrong by 2× in the panel's probe — so they
|
|
394
|
+
are dropped outright, the same treatment `--available-height` got. No consumer
|
|
395
|
+
outside the component's own stylesheet ever read them; a
|
|
396
|
+
`var(--natural-width)` in your CSS now resolves to its fallback.
|
|
397
|
+
|
|
398
|
+
### Column widths: what holds and what drifts
|
|
399
|
+
|
|
400
|
+
Frozen widths (`columns[].width`) hold **exactly**: a `120px` column is
|
|
401
|
+
120px. The overflow threshold (the container width at which the table starts
|
|
402
|
+
scrolling) and the wrap floor (the narrowest a column gets before its text
|
|
403
|
+
wraps) are **pixel-identical** to table auto layout — the panel measured 905px
|
|
404
|
+
against 905px. `fixed` still splits the width equally (`minmax(0, 1fr)` per
|
|
405
|
+
column). Everything else below is documented drift, each with the numbers
|
|
406
|
+
from the panel's probes (`plans/BBTABLE-GRID.md` § Tracks and § v2.1), so
|
|
407
|
+
you know what a screenshot diff is showing you:
|
|
408
|
+
|
|
409
|
+
- **Roomy containers share the surplus equally, not proportionally to
|
|
410
|
+
content.** Two auto columns that the table sized 748px / 52px now come out
|
|
411
|
+
544px / 256px. Short columns get wider; long ones narrower. Add a `width` to
|
|
412
|
+
any column whose proportion you relied on.
|
|
413
|
+
- **Constrained wrapping distributes differently.** When several wrappable
|
|
414
|
+
columns compete for a narrow container, tracks grow in equal increments up
|
|
415
|
+
to their max-content instead of proportionally — the longest column ends up
|
|
416
|
+
~22% narrower and its rows taller. The table starts scrolling at the same
|
|
417
|
+
width as before; only the split before that point moved.
|
|
418
|
+
- **Frozen + auto mixes drift ≤ ~19px per column** — the fixed-layout regime
|
|
419
|
+
where declared widths sat next to unfrozen ones.
|
|
420
|
+
- **All-frozen tables inflate their data columns exactly as before, but not
|
|
421
|
+
the structural ones.** When every column declares a width and the sum is
|
|
422
|
+
below the container, the surplus is still shared in proportion to the
|
|
423
|
+
declared widths (`[100, 200, 100]` at 600px → `[150, 300, 150]`, `%` too),
|
|
424
|
+
so the table keeps filling. The selection and actions columns stay at
|
|
425
|
+
content width instead of inflating with the rest.
|
|
426
|
+
- **A cell that is its own scroll container no longer floors the column.**
|
|
427
|
+
Under table auto layout an `overflow: hidden` / `auto` cell still
|
|
428
|
+
contributed its content width to the column. Under grid it does not — which
|
|
429
|
+
means native `text-overflow: ellipsis` finally works in a cell — **and the
|
|
430
|
+
classic table trick `max-width: 0` breaks**: the cell collapses to its
|
|
431
|
+
padding. Delete every `max-width: 0` you added to make ellipsis work.
|
|
432
|
+
- **Percentage widths in an overflowing table resolve against the grid's
|
|
433
|
+
min-content width**, not the container: a `30%` column that the table gave
|
|
434
|
+
181px gets 303px once the table overflows. Non-overflowing tables are
|
|
435
|
+
identical. (`width: fit-content` on the grid root keeps the root's box
|
|
436
|
+
covering its tracks only without `%` tracks under overflow — a `%`-wide
|
|
437
|
+
overflowing table is the one case where row backgrounds can paint short of
|
|
438
|
+
the scroll width.)
|
|
439
|
+
- **Expand-row content never widens the parent's columns.** A wide nested
|
|
440
|
+
table or a long unbroken string in `#expand` used to push an auto-layout
|
|
441
|
+
parent's columns apart (a 300px track became 500px in the probe); the grid
|
|
442
|
+
contains the full-width cells (`contain: inline-size`), so the parent's
|
|
443
|
+
tracks hold and the content overflows into the scrollport instead. Fixed
|
|
444
|
+
layout was already immune; auto layout was not; the grid always is.
|
|
445
|
+
- **A `snap` on a column whose table has no parent** used to fall into an
|
|
446
|
+
accidental equal split. It now sizes like any other unfrozen column
|
|
447
|
+
(`auto`).
|
|
448
|
+
- **Nested tables align by contract, at any depth.** A child's select/actions
|
|
449
|
+
cell is widened to the parent's region (`minmax(region, max-content)` — two
|
|
450
|
+
equal cells, controls at the row's end); a child without one folds the
|
|
451
|
+
region into its edge cell's padding. A child cell _wider_ than the parent's
|
|
452
|
+
is reclaimed from the adjacent content column only. What each table
|
|
453
|
+
publishes to its own children is the pure content rails plus the two
|
|
454
|
+
regions, so a grandchild aligns with the root — previously the middle
|
|
455
|
+
table's padding compensation was invisible to its children. The fill
|
|
456
|
+
column now only ever grows (`minmax(<its rail>, 1fr)`); it no longer
|
|
457
|
+
shrinks to absorb a wider actions column.
|
|
458
|
+
|
|
459
|
+
### Copy-to-spreadsheet degrades
|
|
460
|
+
|
|
461
|
+
Selecting rows in a real `<table>` and pasting into a spreadsheet gave one
|
|
462
|
+
cell per column. Chrome's selection serializer keys off the computed
|
|
463
|
+
`display`, not the element or its role, so a grid pastes **one value per
|
|
464
|
+
line** — the columns are lost. Accepted: the substrate is not going to carry a
|
|
465
|
+
hidden `<table>` for the clipboard. The sanctioned path is an app-level
|
|
466
|
+
"copy as TSV" action built from the **items model** (the same rows you gave
|
|
467
|
+
`items`, joined with tabs and newlines and written with `navigator.clipboard`)
|
|
468
|
+
— a recipe for it is queued (FOLLOWUPS Q7.4); do not scrape the DOM for it.
|
|
469
|
+
|
|
470
|
+
### Print: header repetition is gone
|
|
471
|
+
|
|
472
|
+
Browsers repeat a `<thead>` at the top of every printed page; there is no
|
|
473
|
+
grid equivalent, so a long table prints its header once. Data rows carry
|
|
474
|
+
`break-inside: avoid` so a row is never split across pages, which is the most
|
|
475
|
+
the substrate can do. No `@media print` rule exists in the library, so nothing
|
|
476
|
+
of yours is fighting one — but if a printed report depended on the repeated
|
|
477
|
+
header, that is a real regression to plan for (a print-specific view, or
|
|
478
|
+
paginated sections with their own headers).
|
|
479
|
+
|
|
480
|
+
### Global element CSS no longer reaches the table
|
|
481
|
+
|
|
482
|
+
Any CSS that reached `BbTable` through element names now misses it: a reset
|
|
483
|
+
(`table { border-collapse: … }`, `td, th { padding: 0 }`), Tailwind's
|
|
484
|
+
`.prose table` typography, a stray `td { vertical-align: top }` in a legacy
|
|
485
|
+
sheet. This cuts both ways. It is an **immunity** — the table no longer
|
|
486
|
+
inherits whatever global table styling the app carries — and it is a **silent
|
|
487
|
+
change** on every page where that global CSS was, knowingly or not, part of
|
|
488
|
+
the look. Diff those pages; then re-express what you actually wanted on
|
|
489
|
+
`.bb-table-data__cell` / `[role=cell]` scoped to a table.
|
|
490
|
+
|
|
491
|
+
### Browser floor
|
|
492
|
+
|
|
493
|
+
The table requires `grid-template-columns: subgrid`: **Chrome/Edge 117,
|
|
494
|
+
Firefox 71, Safari 16**. There is no `@supports` fallback and none is planned —
|
|
495
|
+
maintaining two substrates defeats the rewrite. Cell content is centred
|
|
496
|
+
vertically with `align-content` on a block container, which lands one notch
|
|
497
|
+
higher: **Chrome 123, Safari 17.4, Firefox 125**. Below that, cells still lay
|
|
498
|
+
out correctly but content sits at the top of the cell instead of the middle.
|
|
499
|
+
|
|
500
|
+
All of this sits inside the library-wide floor — **Chrome/Edge 114, Safari 17,
|
|
501
|
+
Firefox 125**, set by the native Popover API (see
|
|
502
|
+
`installation-and-plugin-setup.md` § Browser support) — so the only engine the
|
|
503
|
+
grid actually raises is Chrome/Edge, 114 → 117 (123 for centred cells).
|
|
504
|
+
|
|
505
|
+
### Do not override
|
|
506
|
+
|
|
507
|
+
- **Do not hide cells with `display: none`** (a `thClass`/`tdClass` of
|
|
508
|
+
`hidden`, the v2/early-v3 narrow-screen recipe). A hidden grid item leaves
|
|
509
|
+
its track in place: every following cell shifts one track over and the last
|
|
510
|
+
track collapses. Hide a column by removing it from `columns` at the
|
|
511
|
+
breakpoint, which removes the track.
|
|
512
|
+
- **`contain: inline-size` on `.bb-table-expand__cell`,
|
|
513
|
+
`.bb-table-no-data__cell` and `.bb-table-loading__cell`.** It is the only
|
|
514
|
+
thing keeping a full-width cell's content from resizing the column tracks —
|
|
515
|
+
`min-width: 0` and `width: 100%` wrappers do not work here, and with a nested
|
|
516
|
+
inheriting table inside the expand row the result without it is a feedback
|
|
517
|
+
loop. Removing it (or setting `contain: none` on a cell) is not a styling
|
|
518
|
+
choice.
|
|
519
|
+
- **`gap: 0` on `.bb-table__table`.** A gap set on the root cannot be undone
|
|
520
|
+
from a subgrid level; it opens gutters between every column.
|
|
521
|
+
- **`display: grid` on the root, row groups and rows**, and the
|
|
522
|
+
`grid-column: 1 / -1` on the full-width cells — the geometry.
|
|
523
|
+
- **`white-space: nowrap` on `.bb-table__cell--actions`** — the actions track
|
|
524
|
+
is `max-content`, and `nowrap` is what makes that the width of the controls
|
|
525
|
+
side by side rather than a wrapped stack.
|
|
526
|
+
- **`position: sticky` on `.bb-table__head`** under `fixed-headers`: sticky
|
|
527
|
+
lives on the row group, not on cells — a sticky cell inside the subgrid
|
|
528
|
+
cannot stick in any engine.
|
|
529
|
+
|
|
530
|
+
### Small semantic and visual changes
|
|
531
|
+
|
|
532
|
+
- **Pinned cells and the stuck header are opaque.** `fixed-columns` cells and
|
|
533
|
+
the `fixed-headers` band used to be transparent, so scrolled content showed
|
|
534
|
+
through them. They now paint the table's surface local `--bg` (default
|
|
535
|
+
`var(--bb-panel)`); a pinned cell also paints its row's hover/highlight
|
|
536
|
+
tint. On a non-panel surface set `.bb-table { --bg: … }` once. The
|
|
537
|
+
highlighted row's left accent bar moved from the row onto its first cell
|
|
538
|
+
(same position; an opaque pinned first column would have hidden it).
|
|
539
|
+
- **The sort glyph is one arrow.** The two-chevron `sort.svg` (with its
|
|
540
|
+
inner `sort-icon__asc` / `sort-icon__desc` paths) is gone; the sort button
|
|
541
|
+
renders an inline `<svg class="bb-table-sort__icon">` — a single arrow,
|
|
542
|
+
hidden at rest, faint on hover/focus, solid when sorted: up for ascending,
|
|
543
|
+
rotated down for descending (the AG Grid convention). Apps that copied the old glyph to rebuild
|
|
544
|
+
a header no longer need to (use the header prepend/append slots); any CSS
|
|
545
|
+
on `.sort-icon__asc` / `.sort-icon__desc` matches nothing.
|
|
546
|
+
- **Caption reading order.** The caption is now a `div` _before_ the table
|
|
547
|
+
role, named through `aria-labelledby`, rather than a `<caption>` inside it.
|
|
548
|
+
The accessible name is identical; a screen reader reading linearly meets the
|
|
549
|
+
caption text just before the table instead of as its first child.
|
|
550
|
+
- **The busy bar rides with a stuck header.** With `fixed-headers`, the
|
|
551
|
+
refetch progress bar is anchored to the sticky head row group, so it stays
|
|
552
|
+
at the header/body boundary while you scroll. It used to anchor to the
|
|
553
|
+
in-flow `<thead>` box and scroll away while the header cells stuck — the
|
|
554
|
+
documented intent, now the behavior.
|
|
555
|
+
- **Multi-row `#thead` heads stack when stuck** (above).
|
|
556
|
+
- **`aria-rowcount` now counts the header row**: it is `totalItems + 1`, so
|
|
557
|
+
the last row's `aria-rowindex` no longer exceeds the count. Anything
|
|
558
|
+
asserting the old value must add one.
|
|
559
|
+
- **Row heights change with the wrapping drift** — expect it in screenshot
|
|
560
|
+
diffs of narrow, text-heavy tables.
|
|
561
|
+
|
|
187
562
|
## The custom properties the table _publishes_ lost the `bb-` prefix
|
|
188
563
|
|
|
189
564
|
These are not theming inputs — they are values `BbTable` measures at runtime and
|
|
@@ -191,18 +566,21 @@ writes onto its own DOM, for the table's CSS and for descendants to read. You
|
|
|
191
566
|
reach for them for exactly one reason: aligning a nested table's columns to its
|
|
192
567
|
parent's. They all dropped the prefix.
|
|
193
568
|
|
|
194
|
-
| v2 | v3
|
|
195
|
-
| ------------------------------------------- |
|
|
196
|
-
| `--bb-table-{id}-track-{key}` | `--table-{id}-track-{key}`
|
|
197
|
-
| `--bb-table-offset-start` / `-end` | `--offset-start` / `--offset-end`
|
|
198
|
-
| `--bb-table-offset-internal-start` / `-end` | `--offset-internal-start` / `-internal-end`
|
|
199
|
-
| `--bb-table-offset-external-start` / `-end` | `--offset-external-start` / `-external-end`
|
|
200
|
-
| `--bb-table-fill` | `--fill`
|
|
201
|
-
| `--bb-table-natural-width` | `--natural-width`
|
|
202
|
-
| `--bb-table-cell-h` | `--cell-h`
|
|
569
|
+
| v2 | v3 |
|
|
570
|
+
| ------------------------------------------- | -------------------------------------------------- |
|
|
571
|
+
| `--bb-table-{id}-track-{key}` | `--table-{id}-track-{key}` |
|
|
572
|
+
| `--bb-table-offset-start` / `-end` | `--offset-start` / `--offset-end` |
|
|
573
|
+
| `--bb-table-offset-internal-start` / `-end` | `--offset-internal-start` / `-internal-end` |
|
|
574
|
+
| `--bb-table-offset-external-start` / `-end` | `--offset-external-start` / `-external-end` |
|
|
575
|
+
| `--bb-table-fill` | **removed** (was `--fill` in v3 ≤ beta.5) |
|
|
576
|
+
| `--bb-table-natural-width` | **removed** (was `--natural-width` in v3 ≤ beta.5) |
|
|
577
|
+
| `--bb-table-cell-h` | `--cell-h` |
|
|
203
578
|
|
|
204
579
|
Only the track bridge keeps a `table-` segment, because it crosses component
|
|
205
|
-
boundaries; the rest are plain locals on `.bb-table`. `--
|
|
580
|
+
boundaries; the rest are plain locals on `.bb-table`. `--fill` and
|
|
581
|
+
`--natural-width` were the fixed-layout self-correction values; the grid
|
|
582
|
+
substrate (next section) has no such loop, so they are gone rather than
|
|
583
|
+
republished under a name that would lie about what they hold. `--padding-x`,
|
|
206
584
|
`--padding-y` and `--actions-spacing` were already unprefixed in v2 and keep
|
|
207
585
|
their names (their _values_ tightened: `16px`/`8px` → `12px`/`6px`, and the cell
|
|
208
586
|
height `42px` → `36px`).
|