bitboss-ui 3.0.0-beta.20 → 3.0.0-beta.21
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/dist/ai/BbTable.md +206 -98
- package/dist/ai/changelog.json +21 -3
- package/dist/ai/components.json +40 -15
- package/dist/ai/guides/component-picker.md +18 -18
- package/dist/ai/guides/icons-policy.md +7 -3
- package/dist/ai/guides/installation-and-plugin-setup.md +11 -14
- package/dist/ai/guides/migration/components/bb-table.md +131 -17
- package/dist/ai/recipes/inertia/approvals-inbox.md +0 -1
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +0 -1
- package/dist/ai/recipes/inertia/ownership-atlas.md +8 -9
- package/dist/ai/recipes/inertia/record-form.md +0 -1
- package/dist/ai/recipes/inertia/records-workspace.md +0 -1
- package/dist/ai/recipes/inertia/upload-center.md +0 -1
- package/dist/ai/recipes/nuxt/approvals-inbox.md +0 -1
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +0 -1
- package/dist/ai/recipes/nuxt/record-form.md +0 -1
- package/dist/ai/recipes/nuxt/records-workspace.md +0 -1
- package/dist/ai/recipes/nuxt/upload-center.md +0 -1
- package/dist/ai/recipes/vue/approvals-inbox.md +0 -1
- package/dist/ai/recipes/vue/inline-edit-workspace.md +0 -1
- package/dist/ai/recipes/vue/records-workspace.md +0 -1
- package/dist/ai/recipes/vue/upload-center.md +0 -1
- package/dist/ai/source/BbTable.md +564 -173
- package/dist/components/BbTable/BbTable.vue.d.ts +2 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +865 -735
- package/dist/components/BbTable/types.d.ts +98 -46
- package/dist/components/BbTable/utils.d.ts +21 -16
- package/dist/components/BbTable/utils.js +8 -8
- package/dist/composables/useTableWidthContext.d.ts +24 -4
- package/dist/composables/useTableWidthContext.js +31 -28
- package/dist/deprecation/ai-deprecations.json.d.ts +22 -0
- package/dist/deprecation/ai-deprecations.json.js +1 -1
- package/dist/llms-full.txt +381 -173
- package/dist/llms-medium.txt +29 -32
- package/dist/vite.js +1 -1
- package/package.json +1 -1
- package/scripts/lib/eslint-plugin.mjs +9 -14
- package/scripts/lib/validate-bb-markup.mjs +0 -7
package/dist/ai/BbTable.md
CHANGED
|
@@ -69,7 +69,7 @@ needs is declared here, not in markup:
|
|
|
69
69
|
`(content, key, item) => Classes` for state-dependent styling. Table-level
|
|
70
70
|
`thClass` / `tdClass` / `rowClass` / `headerRowClass` apply everywhere; a
|
|
71
71
|
row's classes accumulate the table `rowClass` plus every column's.
|
|
72
|
-
- **`snap`**
|
|
72
|
+
- **`snap`** names the parent column a nested table's column sits under — see
|
|
73
73
|
Nested tables.
|
|
74
74
|
|
|
75
75
|
**Columns, formatters and a per-row class**
|
|
@@ -198,11 +198,11 @@ pre-mapping your items:
|
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
**`actions` and `select` are not columns.** Both are rendered by the table
|
|
201
|
-
itself
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
builds warn.
|
|
201
|
+
itself: the selection column from the `selectable` prop, the actions column
|
|
202
|
+
from the `#actions` slot — providing the slot is what creates the column,
|
|
203
|
+
there is no prop. Putting `{ key: 'actions' }` in `columns` adds an ordinary
|
|
204
|
+
data column that reads `item.actions`, so you get a stray empty cell next to
|
|
205
|
+
the real one. Dev builds warn.
|
|
206
206
|
|
|
207
207
|
### Custom cells and headers
|
|
208
208
|
|
|
@@ -225,7 +225,8 @@ Body cell scope: `content` (the formatted value), `item` (raw row), `value`
|
|
|
225
225
|
one-way state; the `toggle*` callbacks are the write path.
|
|
226
226
|
|
|
227
227
|
Header scope: `label`, `items` (the full list — handy for counts), `sortable`,
|
|
228
|
-
`sortOrder`, `toggleSort`, `
|
|
228
|
+
`sortOrder`, `toggleSort`, `hideColumn` (hides the column, see "Column
|
|
229
|
+
visibility"), `classes`.
|
|
229
230
|
|
|
230
231
|
**Add beside a header instead of replacing it** with
|
|
231
232
|
`#header:<key>:prepend` / `#header:<key>:append` — rendered before / after the
|
|
@@ -837,12 +838,14 @@ The model's contract is what makes a column panel trivial:
|
|
|
837
838
|
|
|
838
839
|
- Keys listed first render first; declared columns the array omits follow, in
|
|
839
840
|
declaration order (a column added to `columns` later renders last).
|
|
840
|
-
-
|
|
841
|
-
|
|
842
|
-
|
|
841
|
+
- A `hidden` column (next section) keeps its slot without rendering, and keys
|
|
842
|
+
that match no declared column render nothing but are **preserved in
|
|
843
|
+
place** — a column that exists only for some users comes back exactly where
|
|
844
|
+
it was.
|
|
843
845
|
- A user move relocates **exactly one key**; everything else, hidden keys
|
|
844
846
|
included, keeps its relative order. The first move on a partial (or empty)
|
|
845
|
-
model emits the completed array —
|
|
847
|
+
model emits the completed array — every declared key, hidden ones included
|
|
848
|
+
— so persist that and the model is complete from then on.
|
|
846
849
|
- A drop that changes nothing emits nothing; unbound, the table keeps the
|
|
847
850
|
order as per-mount state like `sort`.
|
|
848
851
|
|
|
@@ -853,8 +856,9 @@ The model's contract is what makes a column panel trivial:
|
|
|
853
856
|
<div class="flex max-w-2xl flex-col gap-3">
|
|
854
857
|
<!-- ONE order, two writers: the panel's buttons and the table's own
|
|
855
858
|
header drag / keyboard handles both write `v-model:order`.
|
|
856
|
-
|
|
857
|
-
|
|
859
|
+
Visibility is a flag ON the definition (`hidden`), so a hidden
|
|
860
|
+
column stays declared, keeps its slot in the order, and comes back
|
|
861
|
+
where it was. -->
|
|
858
862
|
<ul
|
|
859
863
|
aria-label="Columns"
|
|
860
864
|
class="flex flex-wrap gap-2 text-xs text-[color:var(--bb-text-muted)]"
|
|
@@ -867,7 +871,7 @@ The model's contract is what makes a column panel trivial:
|
|
|
867
871
|
<BbCheckbox
|
|
868
872
|
:label="column.label"
|
|
869
873
|
:model-value="!hidden.has(column.key)"
|
|
870
|
-
@update:model-value="
|
|
874
|
+
@update:model-value="setHidden(column.key, !$event)"
|
|
871
875
|
/>
|
|
872
876
|
<BbButton
|
|
873
877
|
:aria-label="`Move ${column.label} left`"
|
|
@@ -893,15 +897,30 @@ The model's contract is what makes a column panel trivial:
|
|
|
893
897
|
<BbTable
|
|
894
898
|
v-model:order="order"
|
|
895
899
|
caption="Invoices"
|
|
896
|
-
:columns="
|
|
900
|
+
:columns="columns"
|
|
897
901
|
compact
|
|
898
902
|
item-value="id"
|
|
899
903
|
:items="invoices"
|
|
900
904
|
reorderable
|
|
901
|
-
|
|
905
|
+
@hide:column="(key) => setHidden(key, true)"
|
|
906
|
+
>
|
|
907
|
+
<!-- A "hide" control beside every header: the slot's `hideColumn()` is
|
|
908
|
+
one-way (a hidden column has no header), so the panel above
|
|
909
|
+
is what brings a column back. -->
|
|
910
|
+
<template #header:append="{ hideColumn, label }">
|
|
911
|
+
<BbButton
|
|
912
|
+
:aria-label="`Hide ${label}`"
|
|
913
|
+
icon="lucide:eye-off"
|
|
914
|
+
size="xs"
|
|
915
|
+
variant="ghost"
|
|
916
|
+
@click="hideColumn"
|
|
917
|
+
/>
|
|
918
|
+
</template>
|
|
919
|
+
</BbTable>
|
|
902
920
|
</div>
|
|
903
921
|
<p class="text-xs text-[color:var(--bb-text-muted)]">
|
|
904
|
-
Order model: <code>{{ JSON.stringify(order) }}</code>
|
|
922
|
+
Order model: <code>{{ JSON.stringify(order) }}</code> · hidden:
|
|
923
|
+
<code>{{ JSON.stringify([...hidden]) }}</code>
|
|
905
924
|
</p>
|
|
906
925
|
</div>
|
|
907
926
|
</template>
|
|
@@ -923,8 +942,8 @@ const euro = new Intl.NumberFormat('en-IE', {
|
|
|
923
942
|
currency: 'EUR',
|
|
924
943
|
});
|
|
925
944
|
|
|
926
|
-
// The declaration: what a column IS. Never reordered.
|
|
927
|
-
const
|
|
945
|
+
// The declaration: what a column IS. Never reordered, never filtered.
|
|
946
|
+
const definitions: Array<BbTableColumn<Invoice>> = [
|
|
928
947
|
{ key: 'number', label: 'Invoice', width: 110 },
|
|
929
948
|
{ key: 'client', label: 'Client' },
|
|
930
949
|
{ key: 'issued', label: 'Issued', width: 110 },
|
|
@@ -968,33 +987,41 @@ const invoices: Invoice[] = [
|
|
|
968
987
|
},
|
|
969
988
|
];
|
|
970
989
|
|
|
971
|
-
// The state: render order (keys)
|
|
972
|
-
//
|
|
973
|
-
|
|
990
|
+
// The state, both persistable as-is: render order (keys) and the hidden set.
|
|
991
|
+
// A partial or empty order is fine — the first move writes back the
|
|
992
|
+
// complete array, hidden keys in their slots, because every column stays
|
|
993
|
+
// declared below.
|
|
994
|
+
const order = ref<string[]>([]);
|
|
974
995
|
const hidden = ref(new Set<string>());
|
|
975
996
|
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
const
|
|
979
|
-
|
|
997
|
+
// The definitions, with the persisted visibility mapped in. Same shape you
|
|
998
|
+
// would use for persisted widths (`width`) — the definition is the truth.
|
|
999
|
+
const columns = computed(() =>
|
|
1000
|
+
definitions.map((column) => ({
|
|
1001
|
+
...column,
|
|
1002
|
+
hidden: hidden.value.has(column.key),
|
|
1003
|
+
}))
|
|
980
1004
|
);
|
|
981
1005
|
|
|
982
|
-
// The panel lists every declared column in the order the model has them
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1006
|
+
// The panel lists every declared column in the order the model has them
|
|
1007
|
+
// (omitted keys follow in declaration order, exactly as the table renders).
|
|
1008
|
+
const panelColumns = computed(() => {
|
|
1009
|
+
const rank = (key: string) => {
|
|
1010
|
+
const index = order.value.indexOf(key);
|
|
1011
|
+
return index < 0 ? order.value.length : index;
|
|
1012
|
+
};
|
|
1013
|
+
return [...definitions].sort((a, b) => rank(a.key) - rank(b.key));
|
|
1014
|
+
});
|
|
988
1015
|
|
|
989
|
-
const
|
|
1016
|
+
const setHidden = (key: string, value: boolean) => {
|
|
990
1017
|
const next = new Set(hidden.value);
|
|
991
|
-
if (
|
|
992
|
-
else next.
|
|
1018
|
+
if (value) next.add(key);
|
|
1019
|
+
else next.delete(key);
|
|
993
1020
|
hidden.value = next;
|
|
994
1021
|
};
|
|
995
1022
|
|
|
996
1023
|
const move = (key: string, delta: 1 | -1) => {
|
|
997
|
-
const next =
|
|
1024
|
+
const next = panelColumns.value.map((column) => column.key);
|
|
998
1025
|
const from = next.indexOf(key);
|
|
999
1026
|
const to = from + delta;
|
|
1000
1027
|
if (from < 0 || to < 0 || to >= next.length) return;
|
|
@@ -1005,22 +1032,50 @@ const move = (key: string, delta: 1 | -1) => {
|
|
|
1005
1032
|
</script>
|
|
1006
1033
|
```
|
|
1007
1034
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1035
|
+
### Column visibility
|
|
1036
|
+
|
|
1037
|
+
`hidden: true` on a column definition hides it: no header, no cells, no grid
|
|
1038
|
+
track. The column stays **declared**, and that is the whole point — the order
|
|
1039
|
+
model is completed against every declared key, so a hidden column keeps its
|
|
1040
|
+
slot through any number of header drags, and it returns to that slot the
|
|
1041
|
+
moment the flag clears. Two rules follow:
|
|
1042
|
+
|
|
1043
|
+
- **Hidden means "exists, not shown".** A column panel, a persisted
|
|
1044
|
+
preference, a narrow-screen breakpoint all write `hidden` into the
|
|
1045
|
+
definition (a `computed` over your persisted set — the example above does
|
|
1046
|
+
exactly that). Filter `columns` only for a column that does **not exist**
|
|
1047
|
+
for this user (a custom field they don't have); its key, if a saved order
|
|
1048
|
+
still lists it, is preserved in place like any unknown key.
|
|
1049
|
+
- **There is no visibility model, on purpose** — the `width` shape, not the
|
|
1050
|
+
`sort` one. The definition is the single declared truth. Header slots
|
|
1051
|
+
(`#header:<key>`, `#header:<key>:append`, the table-wide `#header:append`)
|
|
1052
|
+
receive a `hideColumn()` callback for a "Hide column" control of your own; calling
|
|
1053
|
+
it hides the column for the mount and fires `@hide:column="(key) => …"`
|
|
1054
|
+
once. Persist by writing `hidden: true` back into the definition — the
|
|
1055
|
+
override matches it and stays; write `hidden: false` (the panel showing the
|
|
1056
|
+
column again) and the definition wins. Unbound, the hide lives for the
|
|
1057
|
+
mount, like an unbound resize. A hidden column has no header, so `hideColumn()`
|
|
1058
|
+
is one-way by nature: if you offer it, own the persistence and give the
|
|
1059
|
+
user a panel that shows columns again.
|
|
1060
|
+
|
|
1061
|
+
A nested table that shares its parent's keys shares its `hidden` flags too,
|
|
1062
|
+
the same way it shares `order` — a hidden parent column has no track for a
|
|
1063
|
+
child cell to land on.
|
|
1064
|
+
|
|
1065
|
+
**Pins travel with the column.** `fixed` is a field on the column
|
|
1066
|
+
definition (see "Pinned columns"), so a reorder moves the pin with the cell:
|
|
1067
|
+
drag the identity column to slot 3 and it is still sticky, now at slot 3.
|
|
1068
|
+
The offset is still positional — the pinned columns before it on the same
|
|
1069
|
+
side — so a pinned column dragged away from the edge sticks at `left: 0` and
|
|
1070
|
+
slides over the columns before it. Lock the pinned columns in place by
|
|
1071
|
+
normalizing the model in your `@update:order` handler when that matters.
|
|
1072
|
+
|
|
1073
|
+
**Nested tables.** A parent that uses `order` or `reorderable` is inherited
|
|
1074
|
+
by **position**: its columns can move, so the child's `snap` targets and
|
|
1075
|
+
same-key pairing are ignored there (dev builds warn) and the child aligns to
|
|
1076
|
+
the parent's rails slot by slot. Give both tables the same `v-model:order`
|
|
1077
|
+
when they share keys and they stay paired. Never reorder (or share the order
|
|
1078
|
+
with) a snapped child itself — dev builds warn.
|
|
1024
1079
|
|
|
1025
1080
|
Apply the model synchronously in your handler and persist afterwards: an
|
|
1026
1081
|
`await` before the write moves the cell in a later flush, after the table
|
|
@@ -1392,7 +1447,7 @@ fields.
|
|
|
1392
1447
|
Track open rows with `v-model:expanded-items` (an array of row values — set
|
|
1393
1448
|
`item-value` so expansion is stable across refetches) and render their detail
|
|
1394
1449
|
through the `#expand` slot, a full-width row below the main one. Trigger it
|
|
1395
|
-
from the `#actions` slot (
|
|
1450
|
+
from the `#actions` slot (the slot creates the actions column; label it with
|
|
1396
1451
|
`actions-text`): the scope hands you `toggleExpanded` — a **callback** — plus
|
|
1397
1452
|
the read-only `expanded` flag. Drive open/close through the callback; the
|
|
1398
1453
|
`expanded` prop is one-way state, not something to mutate.
|
|
@@ -1409,7 +1464,6 @@ the read-only `expanded` flag. Drive open/close through the callback; the
|
|
|
1409
1464
|
>
|
|
1410
1465
|
<BbTable
|
|
1411
1466
|
v-model:expanded-items="expanded"
|
|
1412
|
-
actions
|
|
1413
1467
|
actions-text="Details"
|
|
1414
1468
|
caption="Invoices"
|
|
1415
1469
|
:columns="columns"
|
|
@@ -1633,11 +1687,19 @@ above it. The mechanics:
|
|
|
1633
1687
|
- `inherit-column-widths` — `true` inherits from the nearest ancestor table;
|
|
1634
1688
|
a string targets a specific ancestor's `id` (useful when an intermediate
|
|
1635
1689
|
table has a different column count).
|
|
1636
|
-
- By default child
|
|
1637
|
-
column
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1690
|
+
- By default a child column pairs with the parent column of the **same key**,
|
|
1691
|
+
and a column the parent lacks flows into the next parent track no other
|
|
1692
|
+
column claimed. A column's `snap` names the parent column instead:
|
|
1693
|
+
`snap: 'client'` takes that track, `['client', 'amount']` spans from the
|
|
1694
|
+
start of one through the end of the other, and the parent's structural
|
|
1695
|
+
columns are targets by name (`'select'`, `'actions'` — `['name',
|
|
1696
|
+
'actions']` runs a column across the parent's actions cell too). A
|
|
1697
|
+
trailing `.digits` on a key in a pair is a fraction of that track
|
|
1698
|
+
(`['a', 'b.5']` = all of `a` plus half of `b`). Snaps and keys resolve
|
|
1699
|
+
against the parent's **rendered** columns: a target the parent hides (or
|
|
1700
|
+
never had) makes the column flow like an unsnapped one; a snap that lands
|
|
1701
|
+
before the previous column's cannot tile and flows too (dev builds warn);
|
|
1702
|
+
under a parent that reorders, snaps are ignored (see Column order).
|
|
1641
1703
|
- A child with **more** columns than the parent content-sizes the extras and
|
|
1642
1704
|
overflows into the parent's scroll (see below); with **fewer**, the leading
|
|
1643
1705
|
columns stay 1:1, the actions column inherits the parent's actions width,
|
|
@@ -1645,15 +1707,16 @@ above it. The mechanics:
|
|
|
1645
1707
|
both tables end flush (a right-aligned total lands under the parent's last
|
|
1646
1708
|
column). Give that last column a `width` or an explicit `[start, end]`
|
|
1647
1709
|
snap only if you want it to stop short — the leftover then joins the
|
|
1648
|
-
actions region instead.
|
|
1710
|
+
actions region instead. A column with nothing free left to flow into is an
|
|
1711
|
+
overflow column too.
|
|
1649
1712
|
- **An inheriting child does not scroll on its own — the parent's scrollport
|
|
1650
1713
|
is the only one.** That is what keeps the two grids locked together: a child
|
|
1651
1714
|
with its own scroll position would fall out of alignment the moment either
|
|
1652
1715
|
one moved, which is the whole thing `inherit-column-widths` exists to
|
|
1653
1716
|
prevent. One scrollbar moves both.
|
|
1654
1717
|
- **The parent's `selectable`/`actions` columns are handled for you** — the
|
|
1655
|
-
child's content columns
|
|
1656
|
-
needed for that), and the parent's select/actions _regions_ are reserved on
|
|
1718
|
+
child's content columns pair with the parent's content columns by key (no
|
|
1719
|
+
`snap` needed for that), and the parent's select/actions _regions_ are reserved on
|
|
1657
1720
|
each side: a child with its own select or actions column widens that cell
|
|
1658
1721
|
to the parent's (two equal cells, controls at the row's end, so the ⌃ sits
|
|
1659
1722
|
under the parent's); a child without one folds the region into its first/
|
|
@@ -1685,7 +1748,6 @@ above it. The mechanics:
|
|
|
1685
1748
|
>
|
|
1686
1749
|
<BbTable
|
|
1687
1750
|
v-model:expanded-items="expanded"
|
|
1688
|
-
actions
|
|
1689
1751
|
actions-text="Line items"
|
|
1690
1752
|
caption="Invoices"
|
|
1691
1753
|
:columns="columns"
|
|
@@ -1783,27 +1845,29 @@ const expanded = ref<string[]>([]);
|
|
|
1783
1845
|
|
|
1784
1846
|
#### Affixed actions on both tables
|
|
1785
1847
|
|
|
1786
|
-
A nested table can
|
|
1787
|
-
their own row actions — and both action columns pin to the
|
|
1848
|
+
A nested table can pin its own actions column with `fixed-actions` — child
|
|
1849
|
+
rows usually need their own row actions — and both action columns pin to the
|
|
1850
|
+
same visible edge:
|
|
1788
1851
|
|
|
1789
1852
|
```vue
|
|
1790
1853
|
<BbTable
|
|
1791
1854
|
id="programmes"
|
|
1792
|
-
actions
|
|
1793
1855
|
:columns="columns"
|
|
1794
|
-
|
|
1856
|
+
fixed-actions
|
|
1795
1857
|
:items="rows"
|
|
1796
1858
|
item-value="id"
|
|
1797
1859
|
>
|
|
1860
|
+
<template #actions="{ item }">…</template>
|
|
1798
1861
|
<template #expand="{ item }">
|
|
1799
1862
|
<BbTable
|
|
1800
|
-
actions
|
|
1801
1863
|
:columns="childColumns"
|
|
1802
|
-
|
|
1864
|
+
fixed-actions
|
|
1803
1865
|
inherit-column-widths="programmes"
|
|
1804
1866
|
:items="item.children"
|
|
1805
1867
|
item-value="id"
|
|
1806
|
-
|
|
1868
|
+
>
|
|
1869
|
+
<template #actions="{ item }">…</template>
|
|
1870
|
+
</BbTable>
|
|
1807
1871
|
</template>
|
|
1808
1872
|
</BbTable>
|
|
1809
1873
|
```
|
|
@@ -1812,9 +1876,10 @@ This works because the child shares the parent's scrollport. `position: sticky`
|
|
|
1812
1876
|
resolves against the nearest scrolling ancestor, and the child's own box is as
|
|
1813
1877
|
wide as the parent's **content** — so if it opened a scrollport of its own, the
|
|
1814
1878
|
child's actions would affix to an edge sitting out in the overflow, hundreds of
|
|
1815
|
-
pixels past what anyone can see, while the parent's pinned correctly.
|
|
1816
|
-
child's actions column
|
|
1817
|
-
above) so the two columns line up rather than merely both
|
|
1879
|
+
pixels past what anyone can see, while the parent's pinned correctly. The
|
|
1880
|
+
child's actions column inherits the parent's actions region by itself (the
|
|
1881
|
+
structural rule above), so the two columns line up rather than merely both
|
|
1882
|
+
being pinned.
|
|
1818
1883
|
|
|
1819
1884
|
A pinned cell is opaque — it paints the table's `--bg` surface because it
|
|
1820
1885
|
slides over its siblings — so a nested table sitting on a tinted expand row
|
|
@@ -2102,7 +2167,6 @@ open-in-new-tab for free.
|
|
|
2102
2167
|
class="max-w-2xl overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
|
|
2103
2168
|
>
|
|
2104
2169
|
<BbTable
|
|
2105
|
-
actions
|
|
2106
2170
|
actions-text="Actions"
|
|
2107
2171
|
caption="Invoices"
|
|
2108
2172
|
:columns="columns"
|
|
@@ -2438,10 +2502,8 @@ canonical palette lives in the
|
|
|
2438
2502
|
|
|
2439
2503
|
`compact` tightens row padding. `fixed-headers` makes the header stick to the
|
|
2440
2504
|
top of the nearest scroll container. `fixed` switches to an equal-width fixed
|
|
2441
|
-
layout.
|
|
2442
|
-
|
|
2443
|
-
objects; indices count rendered columns (a selection column is index 0), and
|
|
2444
|
-
an out-of-range index logs a console error and is skipped. When content can't
|
|
2505
|
+
layout. Pinned columns (next section) stick while the rest scroll
|
|
2506
|
+
horizontally. When content can't
|
|
2445
2507
|
fit, the table keeps every column at its measured minimum — its header label
|
|
2446
2508
|
included — and overflows its container instead of clipping; this is by
|
|
2447
2509
|
design, not a bug to work around. Note that truncating cells yourself
|
|
@@ -2492,13 +2554,51 @@ your wrapper actually sits on before sprinkling `min-width: 0` everywhere.)
|
|
|
2492
2554
|
</div>
|
|
2493
2555
|
```
|
|
2494
2556
|
|
|
2557
|
+
### Pinned columns
|
|
2558
|
+
|
|
2559
|
+
`fixed: 'left' | 'right'` on a column definition makes its cells
|
|
2560
|
+
`position: sticky` on that side — in place, at the column's own render
|
|
2561
|
+
position, like a spreadsheet's freeze panes. Pin a contiguous run from the
|
|
2562
|
+
edge and the offsets accumulate (the second left-pinned column sticks right
|
|
2563
|
+
after the first). The two structural columns have no definition to carry the
|
|
2564
|
+
field, so they pin through the table: `fixed-select` for the selection
|
|
2565
|
+
column and `fixed-actions` for the actions column (each has one sensible
|
|
2566
|
+
side, so both are booleans).
|
|
2567
|
+
|
|
2568
|
+
```vue
|
|
2569
|
+
<BbTable :columns="columns" fixed-actions fixed-select :items="rows" selectable>
|
|
2570
|
+
<template #actions="{ item }">…</template>
|
|
2571
|
+
</BbTable>
|
|
2572
|
+
```
|
|
2573
|
+
|
|
2574
|
+
```ts
|
|
2575
|
+
const columns = [
|
|
2576
|
+
{ key: 'name', label: 'Name', fixed: 'left' },
|
|
2577
|
+
{ key: 'region', label: 'Region' },
|
|
2578
|
+
{ key: 'amount', label: 'Amount', align: 'right' },
|
|
2579
|
+
];
|
|
2580
|
+
```
|
|
2581
|
+
|
|
2582
|
+
Three rules:
|
|
2583
|
+
|
|
2584
|
+
- **The pin is the column's.** Under `order` / `reorderable` it travels with
|
|
2585
|
+
the cell, and a `hidden` pinned column pins nothing. The offset is still
|
|
2586
|
+
positional, so a lone left-pinned column dragged to the middle sticks at
|
|
2587
|
+
`left: 0` and slides over the columns before it — by design; lock the
|
|
2588
|
+
pinned columns in place by normalizing `@update:order` if you need to.
|
|
2589
|
+
- **Pinned cells are opaque** — they paint the table's `--bg` (see Styling
|
|
2590
|
+
hooks), so a table on a tinted surface needs `--bg` set once.
|
|
2591
|
+
- **Nested tables pin against the ancestor scrollport.** A child that
|
|
2592
|
+
inherits the parent's tracks has no scrollport of its own, so both tables'
|
|
2593
|
+
pins land on the same visible edge (see Nested tables).
|
|
2594
|
+
|
|
2495
2595
|
### Virtual rows
|
|
2496
2596
|
|
|
2497
2597
|
`virtual` windows the body: only the rows in the vertical scrollport (plus a
|
|
2498
2598
|
few on each side) are mounted, and two spacer rows hold the scrollport at the
|
|
2499
2599
|
full list height, so 5,000 or 50,000 rows scroll at the cost of a few dozen.
|
|
2500
2600
|
Everything else is the same table — the rows stay in flow in the grid, so the
|
|
2501
|
-
stuck header,
|
|
2601
|
+
stuck header, pinned columns, nested tables, selection, highlight and the
|
|
2502
2602
|
keyboard grammar work untouched. One prop, no knobs.
|
|
2503
2603
|
|
|
2504
2604
|
- **Give it a scroller.** Bound the table's height (`max-height` / `height`
|
|
@@ -2556,7 +2656,6 @@ keyboard grammar work untouched. One prop, no knobs.
|
|
|
2556
2656
|
class="max-h-80"
|
|
2557
2657
|
:columns="columns"
|
|
2558
2658
|
compact
|
|
2559
|
-
:fixed-columns="[0]"
|
|
2560
2659
|
item-value="id"
|
|
2561
2660
|
:items="rows"
|
|
2562
2661
|
virtual
|
|
@@ -2576,7 +2675,7 @@ type Entry = {
|
|
|
2576
2675
|
};
|
|
2577
2676
|
|
|
2578
2677
|
const columns: Array<BbTableColumn<Entry>> = [
|
|
2579
|
-
{ key: 'number', label: 'Invoice' },
|
|
2678
|
+
{ key: 'number', label: 'Invoice', fixed: 'left' },
|
|
2580
2679
|
{ key: 'client', label: 'Client' },
|
|
2581
2680
|
{ key: 'issued', label: 'Issued' },
|
|
2582
2681
|
{ key: 'amount', label: 'Amount', align: 'right' },
|
|
@@ -2601,14 +2700,14 @@ leave the screen. Collapse instead of scroll:
|
|
|
2601
2700
|
|
|
2602
2701
|
- **Rank the columns.** Identity + status + the one number that matters stay;
|
|
2603
2702
|
everything else is secondary.
|
|
2604
|
-
- **
|
|
2605
|
-
over `useMobile()` (or your own `matchMedia`) that
|
|
2606
|
-
|
|
2607
|
-
`thClass`/`tdClass` + `display: none`:
|
|
2608
|
-
leaves its track in place and every
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
order array.
|
|
2703
|
+
- **Flag the secondary ones `hidden` at the breakpoint** — a `computed`
|
|
2704
|
+
over `useMobile()` (or your own `matchMedia`) that maps the definitions
|
|
2705
|
+
with `hidden: isMobile && secondary.has(key)`. A hidden column has no grid
|
|
2706
|
+
track. Do **not** hide cells with `thClass`/`tdClass` + `display: none`:
|
|
2707
|
+
the table is a grid, a hidden cell leaves its track in place and every
|
|
2708
|
+
following cell shifts one track over. The hidden column stays declared, so
|
|
2709
|
+
it keeps its slot in `order` and returns where it was when the screen
|
|
2710
|
+
widens — never filter the order array.
|
|
2612
2711
|
- **Stack what survives into the primary cell.** The identity slot renders the
|
|
2613
2712
|
name plus the hidden facts under it, at `--bb-text-muted`, so nothing is
|
|
2614
2713
|
actually lost.
|
|
@@ -2622,7 +2721,7 @@ records list.
|
|
|
2622
2721
|
**The table's surface is `--bg`** (default `var(--bb-panel)`), a local on
|
|
2623
2722
|
`.bb-table`. Cells are transparent at rest so the row can tint them, but
|
|
2624
2723
|
anything that slides over scrolled content — the stuck header band with
|
|
2625
|
-
`fixed-headers`, and `fixed
|
|
2724
|
+
`fixed-headers`, and pinned (`fixed`) cells — is opaque and paints `--bg` (a
|
|
2626
2725
|
pinned cell also picks up its row's hover/highlight tint, so it never reads
|
|
2627
2726
|
as a hole). When a table sits on a surface that is not the panel colour, set
|
|
2628
2727
|
the one knob and everything pinned follows:
|
|
@@ -2769,8 +2868,9 @@ reader.
|
|
|
2769
2868
|
- `BbPagination` — shared page ref, or zero-wiring via `id` + `table-id`.
|
|
2770
2869
|
- `useBbTableContext` (`./composables/useBbTableContext.md`) — bulk-action
|
|
2771
2870
|
toolbars, detail panels, "reset sort" buttons living outside the table.
|
|
2772
|
-
(Column order
|
|
2773
|
-
so it lives where `
|
|
2871
|
+
(Column order and visibility are not mirrored there: a column panel writes
|
|
2872
|
+
`v-model:order` and the `hidden` flags, so it lives where `columns` is
|
|
2873
|
+
built.)
|
|
2774
2874
|
- `BbButton` — per-row links (`href`/`to`) in cells or `#actions`;
|
|
2775
2875
|
`BbDropdown` for an overflow menu of row actions.
|
|
2776
2876
|
- `BbBadge` / `BbAvatar` — status and identity cells via slots.
|
|
@@ -2796,10 +2896,17 @@ reader.
|
|
|
2796
2896
|
`(columns, item)` with the mapped cells (in **render** order — look them up
|
|
2797
2897
|
by `key`, never by position) and the raw record, and whatever it returns
|
|
2798
2898
|
becomes the checkbox's label.
|
|
2799
|
-
- `fixed
|
|
2800
|
-
|
|
2899
|
+
- A pin (`column.fixed`) travels with its column under `order`, but its
|
|
2900
|
+
offset is positional: dragged away from the edge, a pinned column sticks
|
|
2901
|
+
at `left: 0` and slides over the columns before it (see Pinned columns).
|
|
2902
|
+
- The actions column has no prop: the `#actions` slot creates it. A
|
|
2903
|
+
conditional slot (`<template v-if="…" #actions>`) adds and removes the
|
|
2904
|
+
column live.
|
|
2801
2905
|
- A column panel that holds a partial `order` receives the completed
|
|
2802
|
-
array on the user's first move — its persisted list grows to every
|
|
2906
|
+
array on the user's first move — its persisted list grows to every declared
|
|
2907
|
+
key, `hidden` ones included. Hide with the flag, not by filtering
|
|
2908
|
+
`columns`: a filtered column is undeclared, so a partial model completes
|
|
2909
|
+
without it and the key is gone from the persisted order.
|
|
2803
2910
|
- Enabling select-all does not re-emit an empty `modelValue` (see the
|
|
2804
2911
|
select-all section) — in all-mode, read `select-all` +
|
|
2805
2912
|
`unselected-items`, not `modelValue`.
|
|
@@ -2877,7 +2984,6 @@ reader.
|
|
|
2877
2984
|
| Prop | Type | Default | Required | Description |
|
|
2878
2985
|
| --- | --- | --- | --- | --- |
|
|
2879
2986
|
| `accessibleLabel` | `((columns: MappedCell[], item: any) => string) \| undefined` | | | Function that accepts the columns and the current item as arguments and returns a label to be used for accessibility purposes. The cells arrive in RENDER order (they follow `order`, so the label reads like the row) — look a cell up by `key`… |
|
|
2880
|
-
| `actions` | `boolean \| undefined` | `false` | | Displays the actions column. |
|
|
2881
2987
|
| `actionsText` | `string \| undefined` | | | Label used in the header of the actions column. |
|
|
2882
2988
|
| `align` | `"left" \| "center" \| "right" \| undefined` | `"left"` | | Text alignment of the columns. |
|
|
2883
2989
|
| `caption` | `string \| undefined` | | | Caption that describes the content of the table. Used for accessibility purposes. |
|
|
@@ -2891,8 +2997,9 @@ reader.
|
|
|
2891
2997
|
| `enforceCoherence` | `boolean \| undefined` | `false` | | After every load (initial, `dependencies` refetch or a change of the `items` array) prunes the row-keyed models of values whose row is no longer in the result set: `modelValue`, `unselected-items`, `highlighted` and `expanded-items`. Values… |
|
|
2892
2998
|
| `expandedItems` | `any[] \| undefined` | `[]` | | Used by `v-model:expandedItems`. Array of the currently expanded items — toggled by the row expand control and by external writes (mirrors the `highlighted`/`sort` v-model surfaces). Drives the `#expand` slot rows and their `aria-expanded`/… |
|
|
2893
2999
|
| `fixed` | `boolean \| undefined` | `false` | | Splits the width equally among the columns that declare no `width` (each undeclared column becomes a `minmax(0, 1fr)` track) making each column take up the same amount of space. |
|
|
2894
|
-
| `
|
|
3000
|
+
| `fixedActions` | `boolean \| undefined` | `false` | | Pins the actions column (the one the `#actions` slot creates) to the right edge, `position: sticky`, in place. Data columns pin through `column.fixed`; this is the same feature for the structural column that has no definition to carry it. N… |
|
|
2895
3001
|
| `fixedHeaders` | `boolean \| undefined` | `false` | | Boolean that sets the headers as sticky to the top of the table. |
|
|
3002
|
+
| `fixedSelect` | `boolean \| undefined` | `false` | | Pins the selection column to the left edge, `position: sticky`, in place. Data columns pin through `column.fixed`; this is the same feature for the structural column that has no definition to carry it. No effect unless the table is `selecta… |
|
|
2896
3003
|
| `headerRowClass` | `Classes \| undefined` | | | Defines the classes to be passed to the header row. |
|
|
2897
3004
|
| `highlighted` | `any` | `undefined` | | Used by `v-model:highlighted`. A parallel, UI-intent state to selection — typically "the row whose details are open". A single item value (or `null`) — highlight is single by design. **Opt-in, and `undefined` is the opt-out.** Leave it unbo… |
|
|
2898
3005
|
| `id` | `string \| undefined` | | | Stable id for this table's width context. When omitted a unique id is generated. Nested tables use the nearest ancestor id to inherit widths. |
|
|
@@ -2933,6 +3040,7 @@ reader.
|
|
|
2933
3040
|
- `click:row` — `(e: "click:row", event: MouseEvent, item: any): void`
|
|
2934
3041
|
- `contextmenu:row` — `(e: "contextmenu:row", event: MouseEvent, item: any): void`
|
|
2935
3042
|
- `dblclick:row` — `(e: "dblclick:row", event: MouseEvent, item: any): void`
|
|
3043
|
+
- `hide:column` — `(e: "hide:column", key: string): void` — A column was hidden from its header (a `#header:<key>` / `#header:<key>:append` control calling the slot's `hideColumn()`). Fired once; the table keeps the column hidden for the mount, or until the column's `hidden` flag changes — write `hi…
|
|
2936
3044
|
- `item:selected` — `(e: "item:selected", value: any): void`
|
|
2937
3045
|
- `item:unselected` — `(e: "item:unselected", value: any): void`
|
|
2938
3046
|
- `resize:column` — `(e: "resize:column", key: string, width: number \| null): void` — A column was resized by the user (`resizable`): the new width in px, or `null` when the handle was double-clicked to reset the column to its declared width.
|
|
@@ -2946,7 +3054,7 @@ reader.
|
|
|
2946
3054
|
|
|
2947
3055
|
## Slots
|
|
2948
3056
|
|
|
2949
|
-
- `actions` — scope: `{ expanded?: boolean \| undefined; expandProps?: ExpandControllerProps \| undefined; toggleExpanded?: (() => void) \| undefined; highlighted?: boolean \| undefi…` — Content rendered in the actions cell for each row.
|
|
3057
|
+
- `actions` — scope: `{ expanded?: boolean \| undefined; expandProps?: ExpandControllerProps \| undefined; toggleExpanded?: (() => void) \| undefined; highlighted?: boolean \| undefi…` — Content rendered in the actions cell for each row. Providing this slot is what CREATES the actions column (there is no prop): the header cell (labelled by `actions-text`, replaceable through `#header:actions`) and one cell per row appear wi…
|
|
2950
3058
|
- `expand` — scope: `{ expanded: boolean; expandProps?: ExpandControllerProps \| undefined; toggleExpanded: () => void; highlighted?: boolean \| undefined; toggleHighlighted?: (() =…` — Full-width expandable content row rendered below each main row when expanded.
|
|
2951
3059
|
- `header:actions` — scope: `{ text: string; }` — Replaces the default actions header cell content.
|
|
2952
3060
|
- `header:append` — scope: `BbTableHeaderAffixSlotProps<Item>` — Content rendered after every data column's header cell content — the label + sort control, or the column's own `header:<key>` replacement. A column's `header:<key>:append` slot wins over this one for that column; they never stack. Branch on…
|
package/dist/ai/changelog.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"library": "bitboss-ui",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
4
|
+
"version": "3.0.0-beta.21",
|
|
5
5
|
"upgrade": "v2-to-v3",
|
|
6
6
|
"guide": "ai/guides/migration/v2-to-v3.md",
|
|
7
7
|
"summary": {
|
|
8
8
|
"renames": 9,
|
|
9
|
-
"behaviourBreaks":
|
|
9
|
+
"behaviourBreaks": 50
|
|
10
10
|
},
|
|
11
11
|
"breaking": [
|
|
12
12
|
{
|
|
@@ -306,10 +306,28 @@
|
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"kind": "behaviour",
|
|
309
|
-
"description": "BbTable: hiding a column with thClass/tdClass + display: none (the v2/early-v3 narrow-screen recipe) no longer works — a hidden grid item keeps its track and shifts the following cells;
|
|
309
|
+
"description": "BbTable: hiding a column with thClass/tdClass + display: none (the v2/early-v3 narrow-screen recipe) no longer works — a hidden grid item keeps its track and shifts the following cells; set hidden: true on the column instead (filtering it out of columns was the beta.20 doctrine and lost the key from a partial order — DECISIONS § Column visibility, 2026-09-07)",
|
|
310
310
|
"ruling": "grid substrate, plans/BBTABLE-GRID.md",
|
|
311
311
|
"guide": "components/bb-table.md § Do not override; guide § Narrow screens"
|
|
312
312
|
},
|
|
313
|
+
{
|
|
314
|
+
"kind": "behaviour",
|
|
315
|
+
"description": "BbTable actions prop removed — the #actions slot creates the column (a prop without the slot rendered an empty column; now it renders nothing)",
|
|
316
|
+
"ruling": "DECISIONS § Column pins, 2026-09-07",
|
|
317
|
+
"guide": "components/bb-table.md § The #actions slot creates the actions column"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"kind": "behaviour",
|
|
321
|
+
"description": "BbTable fixedColumns (rendered-slot indices) removed — fixed: 'left' \\",
|
|
322
|
+
"ruling": "'right' on the column definition + fixed-select / fixed-actions; pins follow the column through a reorder, sticky in place as before",
|
|
323
|
+
"guide": "DECISIONS § Column pins, 2026-09-07"
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"kind": "behaviour",
|
|
327
|
+
"description": "BbTable column snap takes the PARENT's column key ('select' / 'actions' by name, inclusive pairs, 'key.5' fractions) instead of a gridline index; --table-{id}-track-{index} became --table-{id}-track-{key} (slot-name spelling). Numeric snaps are ignored at runtime",
|
|
328
|
+
"ruling": "DECISIONS § Keyed snap, 2026-09-07",
|
|
329
|
+
"guide": "components/bb-table.md § snap names the parent column"
|
|
330
|
+
},
|
|
313
331
|
{
|
|
314
332
|
"kind": "behaviour",
|
|
315
333
|
"description": "BbTable highlight is opt-in: with highlighted unbound (or bound as undefined) clicking a row no longer applies bb-table-data__row--highlighted / aria-current — the table used to drive defineModel's local fallback, an unreadable state the app could not clear. Bind ref(null), not ref(); a context seed counts. click:row is unaffected",
|