bitboss-ui 3.0.0-beta.10 → 3.0.0-beta.14
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/BbBaseSelect.md +1 -0
- package/dist/ai/BbSelect.md +87 -4
- package/dist/ai/BbSelectPopover.md +9 -17
- package/dist/ai/BbTable.md +113 -6
- package/dist/ai/changelog.json +1 -1
- package/dist/ai/components.json +18 -2
- package/dist/ai/source/BbBadge.md +52 -13
- package/dist/ai/source/BbBaseSelect.md +71 -16
- package/dist/ai/source/BbButton.md +39 -23
- package/dist/ai/source/BbDropdown.md +5 -1
- package/dist/ai/source/BbIcon.md +43 -34
- package/dist/ai/source/BbSelect.md +28 -0
- package/dist/ai/source/BbSelectPopover.md +23 -3
- package/dist/ai/source/BbTable.md +1962 -334
- package/dist/ai/source/CommonPopover.md +6 -0
- package/dist/ai/source/FlatListBox.md +11 -5
- package/dist/ai/source/GroupedListBox.md +7 -5
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +22 -16
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +33 -12
- package/dist/components/BbBadge/badgeButtonContext.d.ts +21 -8
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +74 -65
- package/dist/components/BbBaseSelect/types.d.ts +22 -0
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +84 -82
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +40 -40
- package/dist/components/BbIcon/iconCache.d.ts +33 -0
- package/dist/components/BbIcon/iconCache.js +17 -0
- package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/BbSelect/types.d.ts +24 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +982 -672
- package/dist/components/BbTable/BbTableDataRow.d.ts +162 -0
- package/dist/components/BbTable/BbTableDataRow.js +136 -0
- package/dist/components/BbTable/BbTableExpandRow.d.ts +73 -0
- package/dist/components/BbTable/BbTableExpandRow.js +52 -0
- package/dist/components/BbTable/types.d.ts +26 -0
- package/dist/components/BbTable/utils.d.ts +17 -1
- package/dist/components/BbTable/utils.js +23 -18
- package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/composables/useBaseOptions.js +20 -10
- package/dist/directives/bbDropdown.d.ts +5 -0
- package/dist/directives/bbDropdown.js +32 -10
- package/dist/directives/createPopoverDirective.d.ts +62 -0
- package/dist/directives/createPopoverDirective.js +63 -23
- package/dist/llms-full.txt +210 -27
- package/dist/styles.css +1 -1
- package/dist/validated/BbSelect.vue.d.ts +2 -0
- package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -0
- package/package.json +1 -1
- package/dist/components/BbBadge/BadgeAppendIcon.vue.d.ts +0 -3
- package/dist/components/BbBadge/BadgeAppendIcon.vue.js +0 -5
- package/dist/components/BbBadge/BadgeAppendIcon.vue_vue_type_script_setup_true_lang.js +0 -22
package/dist/ai/BbBaseSelect.md
CHANGED
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
|
|
77
77
|
- `append` — scope: `BaseSelectActivatorSlotProps`
|
|
78
78
|
- `append-outer` — scope: `BaseSelectOuterSlotProps`
|
|
79
|
+
- `chip` — scope: `BaseSelectChipSlotProps<Item>` — Replaces the default badge rendered for each selection in `multiple` mode.
|
|
79
80
|
- `footer` — scope: `BaseSelectPanelSlotProps` — Content pinned to the bottom of the panel, below the options list.
|
|
80
81
|
- `group` — scope: `BaseSelectGroupSlotProps<Item>`
|
|
81
82
|
- `header` — scope: `BaseSelectPanelSlotProps` — Content pinned to the top of the panel, above the search field.
|
package/dist/ai/BbSelect.md
CHANGED
|
@@ -1068,10 +1068,12 @@ const role = ref<string | null>(null);
|
|
|
1068
1068
|
|
|
1069
1069
|
### Custom option rows
|
|
1070
1070
|
|
|
1071
|
-
The `#option` slot replaces the
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1071
|
+
The `#option` slot replaces the row's **text** — use it for avatars, secondary
|
|
1072
|
+
text or metadata. It exposes the raw `item`, the resolved `text` and `value`,
|
|
1073
|
+
plus `selected` / `focused` / `disabled` / `loading` / `hasErrors` flags and the
|
|
1074
|
+
row `index`. The selection check is not yours to redraw: it renders outside the
|
|
1075
|
+
slot, so a custom row keeps it (`selected` is there for rows that want to show
|
|
1076
|
+
the state some _other_ way, not for drawing a second check). Rows are fixed-height (the list is virtualized): if
|
|
1075
1077
|
your markup is taller than the default 28px (24px compact; 44px / 36px inside
|
|
1076
1078
|
the adaptive sheet), set `item-height` to match — and `header-height` for
|
|
1077
1079
|
grouped headers (defaults 32px / 24px compact; 36px / 32px in the sheet). The
|
|
@@ -1148,6 +1150,86 @@ const assignee = ref<string | null>(null);
|
|
|
1148
1150
|
</script>
|
|
1149
1151
|
```
|
|
1150
1152
|
|
|
1153
|
+
### Custom chips
|
|
1154
|
+
|
|
1155
|
+
In `multiple` mode each selection renders as a badge. The `#chip` slot replaces
|
|
1156
|
+
that badge outright — a colored label, an avatar plus a name, anything — and
|
|
1157
|
+
receives `{ item, text, value, disabled, deselect }`. `item` is the raw entry
|
|
1158
|
+
from `items` (where a per-selection color or avatar lives), `disabled` is `true`
|
|
1159
|
+
while the control is `disabled` or `readonly` (render no remove affordance —
|
|
1160
|
+
`deselect` refuses anyway), and `deselect` drops just this value from the model.
|
|
1161
|
+
|
|
1162
|
+
Custom chips need none of the library's class names: the chips row supplies the
|
|
1163
|
+
spacing and keeps them from shrinking. The slot is not called in `comma` mode
|
|
1164
|
+
(there are no chips to replace) nor for the collapsed "+N" summary that appears
|
|
1165
|
+
once the chips outgrow the field — keep chips compact, or raise
|
|
1166
|
+
`max-selected-labels`.
|
|
1167
|
+
|
|
1168
|
+
**Labels as colored chips**
|
|
1169
|
+
|
|
1170
|
+
```vue
|
|
1171
|
+
<template>
|
|
1172
|
+
<div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-3">
|
|
1173
|
+
<BbSelect
|
|
1174
|
+
v-model="tags"
|
|
1175
|
+
clearable
|
|
1176
|
+
item-text="label"
|
|
1177
|
+
item-value="value"
|
|
1178
|
+
:items="tagItems"
|
|
1179
|
+
label="Labels"
|
|
1180
|
+
multiple
|
|
1181
|
+
placeholder="Add labels"
|
|
1182
|
+
>
|
|
1183
|
+
<template #option="{ item, text }">
|
|
1184
|
+
<span class="flex w-full items-center gap-2">
|
|
1185
|
+
<span
|
|
1186
|
+
class="size-2 shrink-0 rounded-full"
|
|
1187
|
+
:style="{ background: item.color }"
|
|
1188
|
+
></span>
|
|
1189
|
+
<span class="min-w-0 flex-1 truncate">{{ text }}</span>
|
|
1190
|
+
</span>
|
|
1191
|
+
</template>
|
|
1192
|
+
<!-- The chip slot replaces the default badge for each selection. It
|
|
1193
|
+
hands over the raw `item` (here: the label's own color), the resolved
|
|
1194
|
+
`text` / `value`, a `disabled` flag that is true while the control is
|
|
1195
|
+
disabled or readonly, and `deselect` — which drops just this value. -->
|
|
1196
|
+
<template #chip="{ item, text, disabled, deselect }">
|
|
1197
|
+
<BbBadge
|
|
1198
|
+
:clearable="!disabled"
|
|
1199
|
+
size="lg"
|
|
1200
|
+
:style="{
|
|
1201
|
+
'--bg': `color-mix(in oklab, ${item.color} 15%, transparent)`,
|
|
1202
|
+
'--color': 'var(--bb-text)',
|
|
1203
|
+
}"
|
|
1204
|
+
@click:clear="deselect"
|
|
1205
|
+
>
|
|
1206
|
+
<span
|
|
1207
|
+
class="size-2 rounded-full"
|
|
1208
|
+
:style="{ background: item.color }"
|
|
1209
|
+
></span>
|
|
1210
|
+
{{ text }}
|
|
1211
|
+
</BbBadge>
|
|
1212
|
+
</template>
|
|
1213
|
+
</BbSelect>
|
|
1214
|
+
</div>
|
|
1215
|
+
</template>
|
|
1216
|
+
<script setup lang="ts">
|
|
1217
|
+
import { ref } from 'vue';
|
|
1218
|
+
import { BbBadge, BbSelect } from 'bitboss-ui';
|
|
1219
|
+
|
|
1220
|
+
type Label = { label: string; value: string; color: string };
|
|
1221
|
+
|
|
1222
|
+
const tagItems: Label[] = [
|
|
1223
|
+
{ label: 'Bug', value: 'bug', color: '#ef4444' },
|
|
1224
|
+
{ label: 'Enhancement', value: 'enhancement', color: '#0ea5e9' },
|
|
1225
|
+
{ label: 'Documentation', value: 'documentation', color: '#8b5cf6' },
|
|
1226
|
+
{ label: 'Good first issue', value: 'good-first-issue', color: '#22c55e' },
|
|
1227
|
+
];
|
|
1228
|
+
|
|
1229
|
+
const tags = ref<string[]>(['bug']);
|
|
1230
|
+
</script>
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1151
1233
|
### Slots around the input
|
|
1152
1234
|
|
|
1153
1235
|
Beyond the panel slots, the field itself is extensible:
|
|
@@ -1424,6 +1506,7 @@ Platform notes:
|
|
|
1424
1506
|
|
|
1425
1507
|
- `append` — scope: `{ query: string; focus: () => void; close: () => Promise<void>; }` — Content appended inside the input field, after the value area.
|
|
1426
1508
|
- `append-outer` — scope: `{ query: string; close: () => Promise<void>; }` — Content appended outside the input field wrapper.
|
|
1509
|
+
- `chip` — scope: `BbSelectChipSlotProps<T>` — Replaces the default badge rendered for each selection in `multiple` mode. Not called in `comma` mode, nor for the collapsed "+N" summary.
|
|
1427
1510
|
- `footer` — scope: `BbSelectPanelSlotProps` — Content pinned to the bottom of the options panel, below the options list. Rendered in both the desktop popover and the adaptive sheet.
|
|
1428
1511
|
- `group` — scope: `BbSelectGroupSlotProps<T>` — Replaces the default group header rendered above grouped options.
|
|
1429
1512
|
- `header` — scope: `BbSelectPanelSlotProps` — Content pinned to the top of the options panel, above the search field. Rendered in both the desktop popover and the adaptive sheet.
|
|
@@ -216,9 +216,11 @@ const reviewers = ref<string[]>(['ada', 'grace']);
|
|
|
216
216
|
|
|
217
217
|
### Custom option rows
|
|
218
218
|
|
|
219
|
-
The `#option` slot replaces each row — exposing the raw `item`,
|
|
220
|
-
`value`, the row `index`, and `selected` / `focused` /
|
|
221
|
-
flags — so options can carry avatars, badges or secondary
|
|
219
|
+
The `#option` slot replaces each row's **text** — exposing the raw `item`,
|
|
220
|
+
resolved `text` / `value`, the row `index`, and `selected` / `focused` /
|
|
221
|
+
`disabled` / `loading` flags — so options can carry avatars, badges or secondary
|
|
222
|
+
metadata. The selection check renders outside the slot and survives any custom
|
|
223
|
+
markup, so there is no need to redraw it from `selected`. Rows are
|
|
222
224
|
fixed-height and virtualized: if your markup is taller than the default 28px
|
|
223
225
|
(24px compact; 44px / 36px inside the adaptive sheet), set `item-height` to
|
|
224
226
|
match — and `header-height` for grouped headers (32px / 24px compact; 36px /
|
|
@@ -258,16 +260,12 @@ height wins everywhere.
|
|
|
258
260
|
}}</BbBadgeButton>
|
|
259
261
|
</BbBadge>
|
|
260
262
|
</template>
|
|
261
|
-
|
|
263
|
+
<!-- The slot replaces the row's TEXT only: the selection check keeps
|
|
264
|
+
rendering outside it, so custom rows never have to redraw it. -->
|
|
265
|
+
<template #option="{ item, text }">
|
|
262
266
|
<span class="flex w-full items-center gap-2">
|
|
263
267
|
<BbAvatar :alt="text" size="32">{{ item.initials }}</BbAvatar>
|
|
264
268
|
<span class="min-w-0 flex-1 truncate">{{ text }}</span>
|
|
265
|
-
<BbIcon
|
|
266
|
-
v-if="selected"
|
|
267
|
-
class="shrink-0 text-[color:var(--bb-text-muted)]"
|
|
268
|
-
icon="lucide:check"
|
|
269
|
-
:size="14"
|
|
270
|
-
/>
|
|
271
269
|
</span>
|
|
272
270
|
</template>
|
|
273
271
|
</BbSelectPopover>
|
|
@@ -275,13 +273,7 @@ height wins everywhere.
|
|
|
275
273
|
</template>
|
|
276
274
|
<script setup lang="ts">
|
|
277
275
|
import { computed, ref } from 'vue';
|
|
278
|
-
import {
|
|
279
|
-
BbAvatar,
|
|
280
|
-
BbBadge,
|
|
281
|
-
BbBadgeButton,
|
|
282
|
-
BbIcon,
|
|
283
|
-
BbSelectPopover,
|
|
284
|
-
} from 'bitboss-ui';
|
|
276
|
+
import { BbAvatar, BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
285
277
|
|
|
286
278
|
type Person = { label: string; value: string; initials: string };
|
|
287
279
|
|
package/dist/ai/BbTable.md
CHANGED
|
@@ -31,12 +31,14 @@ that state to your data source (client-side or via `dependencies`-driven
|
|
|
31
31
|
refetch). This keeps it equally at home in a client-only screen and a
|
|
32
32
|
server-paginated one.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
**Every row in `items` mounts to the DOM unless you say `virtual`.** A few
|
|
35
|
+
hundred rows is fine for a client-only screen. Past roughly 1,000 rows,
|
|
36
|
+
either page it — feed `BbTable` the current slice as `items` and pass `page`
|
|
37
|
+
/ `per-page` / `total-items` (see § Pagination) so the server, not the DOM,
|
|
38
|
+
holds the rest — or, when the whole dataset legitimately lives in the
|
|
39
|
+
browser (local filtering, a spreadsheet-shaped tool), add `virtual` and let
|
|
40
|
+
the table window its rows (see § Virtual rows). The two compose: a large
|
|
41
|
+
page can be windowed.
|
|
40
42
|
|
|
41
43
|
### Columns: the full grammar
|
|
42
44
|
|
|
@@ -2490,6 +2492,107 @@ your wrapper actually sits on before sprinkling `min-width: 0` everywhere.)
|
|
|
2490
2492
|
</div>
|
|
2491
2493
|
```
|
|
2492
2494
|
|
|
2495
|
+
### Virtual rows
|
|
2496
|
+
|
|
2497
|
+
`virtual` windows the body: only the rows in the vertical scrollport (plus a
|
|
2498
|
+
few on each side) are mounted, and two spacer rows hold the scrollport at the
|
|
2499
|
+
full list height, so 5,000 or 50,000 rows scroll at the cost of a few dozen.
|
|
2500
|
+
Everything else is the same table — the rows stay in flow in the grid, so the
|
|
2501
|
+
stuck header, `fixed-columns`, nested tables, selection, highlight and the
|
|
2502
|
+
keyboard grammar work untouched. One prop, no knobs.
|
|
2503
|
+
|
|
2504
|
+
- **Give it a scroller.** Bound the table's height (`max-height` / `height`
|
|
2505
|
+
on the table, or a flex item with `min-height: 0`) and the table itself
|
|
2506
|
+
scrolls its rows; left unbounded it grows with the page and the page (or
|
|
2507
|
+
the nearest scrolling ancestor) is the scroller. `virtual` implies a stuck
|
|
2508
|
+
header — a windowed body without column names is a scroller, not a table.
|
|
2509
|
+
- **Columns are sized once.** A windowed body only ever holds a few rows, so
|
|
2510
|
+
`auto` tracks would move as rows come and go. The first rendered batch is
|
|
2511
|
+
measured and its widths become floors (`minmax(px, 1fr)`): they still share
|
|
2512
|
+
surplus, later rows never widen them — and the cells of those columns are
|
|
2513
|
+
single-line and **clip with an ellipsis** (like a resized column) so a
|
|
2514
|
+
longer value that shows up later never spills into its neighbour. Want a
|
|
2515
|
+
column to wrap (prose, notes)? Declare a `width` on it: a declared width
|
|
2516
|
+
wins as always and keeps wrapping. A resize overrides as always, and an
|
|
2517
|
+
empty→non-empty transition (a refetch) re-measures from the new first
|
|
2518
|
+
batch.
|
|
2519
|
+
- **A fast wheel never shows blank space.** A band of skeleton rows is kept
|
|
2520
|
+
rendered beyond the window on both sides (cheap: empty cells at the
|
|
2521
|
+
measured row height), so a wheel that the compositor scrolls ahead of the
|
|
2522
|
+
main thread reveals skeletons, not blank spacer, and they turn into real
|
|
2523
|
+
rows as the window reaches them. While the scroll is fast, rows never
|
|
2524
|
+
mounted before stay skeletons and are swapped for real rows as soon as
|
|
2525
|
+
it slows; rows you have already seen stay real. A moderate scroll never
|
|
2526
|
+
sees a skeleton.
|
|
2527
|
+
- **Dynamic heights are fine.** Every mounted row is measured (wrapping
|
|
2528
|
+
cells, a "show more", an expand row that opens a nested table three levels
|
|
2529
|
+
deep), measurements are cached by row so a row seen once stays right, and a
|
|
2530
|
+
height change _above_ the viewport moves the scroll offset with it so what
|
|
2531
|
+
you are reading does not jump.
|
|
2532
|
+
- **Focus is never lost to an unmount.** The row (or expand row) that owns
|
|
2533
|
+
focus stays mounted however far it scrolls — a control inside a nested
|
|
2534
|
+
table included — and is released on blur. Arrow keys, Home/End and
|
|
2535
|
+
PageUp/PageDown work across the whole list (the target row is scrolled in,
|
|
2536
|
+
mounted, then focused).
|
|
2537
|
+
- **Nested tables are not windowed.** A nested table inside an expand row is
|
|
2538
|
+
one tall row to the virtualizer; give a huge nested dataset its own page.
|
|
2539
|
+
`virtual` is ignored on a table that inherits its widths, and under a
|
|
2540
|
+
`#tbody` slot.
|
|
2541
|
+
- **Rows keep their absolute position for AT** (`aria-rowcount` /
|
|
2542
|
+
`aria-rowindex`), page or no page — a virtual table is one page unless you
|
|
2543
|
+
say otherwise.
|
|
2544
|
+
|
|
2545
|
+
**5,000 rows in a 320px scroller**
|
|
2546
|
+
|
|
2547
|
+
```vue
|
|
2548
|
+
<template>
|
|
2549
|
+
<!-- Bounding the table's height is what makes it the scroller of its own
|
|
2550
|
+
rows; `virtual` windows them and implies the stuck header. -->
|
|
2551
|
+
<div
|
|
2552
|
+
class="max-w-2xl overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
|
|
2553
|
+
>
|
|
2554
|
+
<BbTable
|
|
2555
|
+
caption="Ledger"
|
|
2556
|
+
class="max-h-80"
|
|
2557
|
+
:columns="columns"
|
|
2558
|
+
compact
|
|
2559
|
+
:fixed-columns="[0]"
|
|
2560
|
+
item-value="id"
|
|
2561
|
+
:items="rows"
|
|
2562
|
+
virtual
|
|
2563
|
+
/>
|
|
2564
|
+
</div>
|
|
2565
|
+
</template>
|
|
2566
|
+
<script setup lang="ts">
|
|
2567
|
+
import { BbTable } from 'bitboss-ui';
|
|
2568
|
+
import type { BbTableColumn } from 'bitboss-ui';
|
|
2569
|
+
|
|
2570
|
+
type Entry = {
|
|
2571
|
+
id: number;
|
|
2572
|
+
number: string;
|
|
2573
|
+
client: string;
|
|
2574
|
+
issued: string;
|
|
2575
|
+
amount: string;
|
|
2576
|
+
};
|
|
2577
|
+
|
|
2578
|
+
const columns: Array<BbTableColumn<Entry>> = [
|
|
2579
|
+
{ key: 'number', label: 'Invoice' },
|
|
2580
|
+
{ key: 'client', label: 'Client' },
|
|
2581
|
+
{ key: 'issued', label: 'Issued' },
|
|
2582
|
+
{ key: 'amount', label: 'Amount', align: 'right' },
|
|
2583
|
+
];
|
|
2584
|
+
|
|
2585
|
+
const clients = ['Northwind Traders', 'Globex', 'Initech', 'Umbrella', 'Hooli'];
|
|
2586
|
+
const rows: Entry[] = Array.from({ length: 5000 }, (_, i) => ({
|
|
2587
|
+
id: i,
|
|
2588
|
+
number: `INV-${10000 + i}`,
|
|
2589
|
+
client: clients[i % clients.length],
|
|
2590
|
+
issued: `2026-${String(1 + (i % 12)).padStart(2, '0')}-${String(1 + (i % 28)).padStart(2, '0')}`,
|
|
2591
|
+
amount: `€${((i * 37) % 9000) + 100}`,
|
|
2592
|
+
}));
|
|
2593
|
+
</script>
|
|
2594
|
+
```
|
|
2595
|
+
|
|
2493
2596
|
### Narrow screens: hide columns, don't scroll everything
|
|
2494
2597
|
|
|
2495
2598
|
An ops table with ten columns is unusable on a phone as a horizontal scroller —
|
|
@@ -2551,6 +2654,9 @@ through element names, which match nothing:
|
|
|
2551
2654
|
| focused header | `.bb-table-header[data-column-key]:focus-visible` (`reorderable` only; inset ring, the header row is one roving tab stop) | `columnheader` |
|
|
2552
2655
|
| dragged column | `.bb-table-header--dragging` on the header; `.bb-table__head--dragging` on the head group during a drag | — |
|
|
2553
2656
|
| clipped header | `.bb-table-header--clip` (the track is a length or an equal split, so the label ellipsizes instead of flooring the column) | `columnheader` |
|
|
2657
|
+
| virtual table | `.bb-table--virtual` on the container (`overflow: auto`, the scroller once its height is bounded) | — |
|
|
2658
|
+
| spacer row | `.bb-table-spacer__row` (a `virtual` body's stand-in for unmounted rows, `aria-hidden`; never style its height) | — (`aria-hidden`) |
|
|
2659
|
+
| skeleton row | `.bb-table-placeholder__row` > `.bb-table-placeholder__cell` (a `virtual` body's skeleton band and fast-scroll stand-in) | `row` > `cell` (`aria-hidden`) |
|
|
2554
2660
|
| drag chip | `.bb-table-column-ghost` (a `popover="manual"` in the top layer; paints `--bb-panel`, not the table's `--bg`) | — (`aria-hidden`) |
|
|
2555
2661
|
| caption | `.bb-table-caption` (before the root, `aria-labelledby`) | — |
|
|
2556
2662
|
|
|
@@ -2817,6 +2923,7 @@ reader.
|
|
|
2817
2923
|
| `totalItems` | `string \| number \| undefined` | | | Total number of items there is. Also flows into the shared table context, where a `BbPagination` paired through `id` derives its page count from it. When omitted, an array `items` table infers it from `items.length`; an explicit value (or a… |
|
|
2818
2924
|
| `totalPages` | `string \| number \| undefined` | | | Seeds the page count shown by a `BbPagination` paired through `id`. A shortcut for when the server reports a page count rather than a row count: the table renders nothing from it, it only flows into the shared table context. A known total a… |
|
|
2819
2925
|
| `unselectedItems` | `any[] \| undefined` | `[]` | | Items that are not selected. |
|
|
2926
|
+
| `virtual` | `boolean \| undefined` | | | Windows the body: only the rows in (and just around) the vertical scrollport are mounted, so a client-side dataset of any size scrolls at a constant cost. The table itself is the scroller as soon as you bound its height (`max-height` / `hei… |
|
|
2820
2927
|
|
|
2821
2928
|
## Events
|
|
2822
2929
|
|
package/dist/ai/changelog.json
CHANGED
package/dist/ai/components.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"library": "bitboss-ui",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
5
|
-
"generatedAt": "2026-08-
|
|
4
|
+
"version": "3.0.0-beta.14",
|
|
5
|
+
"generatedAt": "2026-08-28T14:01:44.030Z",
|
|
6
6
|
"composables": [
|
|
7
7
|
{
|
|
8
8
|
"name": "useBbConfig",
|
|
@@ -3506,6 +3506,11 @@
|
|
|
3506
3506
|
"name": "append-outer",
|
|
3507
3507
|
"type": "BaseSelectOuterSlotProps"
|
|
3508
3508
|
},
|
|
3509
|
+
{
|
|
3510
|
+
"name": "chip",
|
|
3511
|
+
"type": "BaseSelectChipSlotProps<Item>",
|
|
3512
|
+
"description": "Replaces the default badge rendered for each selection in `multiple` mode."
|
|
3513
|
+
},
|
|
3509
3514
|
{
|
|
3510
3515
|
"name": "footer",
|
|
3511
3516
|
"type": "BaseSelectPanelSlotProps",
|
|
@@ -10923,6 +10928,11 @@
|
|
|
10923
10928
|
"type": "{ query: string; close: () => Promise<void>; }",
|
|
10924
10929
|
"description": "Content appended outside the input field wrapper."
|
|
10925
10930
|
},
|
|
10931
|
+
{
|
|
10932
|
+
"name": "chip",
|
|
10933
|
+
"type": "BbSelectChipSlotProps<T>",
|
|
10934
|
+
"description": "Replaces the default badge rendered for each selection in `multiple` mode.\nNot called in `comma` mode, nor for the collapsed \"+N\" summary."
|
|
10935
|
+
},
|
|
10926
10936
|
{
|
|
10927
10937
|
"name": "footer",
|
|
10928
10938
|
"type": "BbSelectPanelSlotProps",
|
|
@@ -12894,6 +12904,12 @@
|
|
|
12894
12904
|
"required": false,
|
|
12895
12905
|
"default": "[]",
|
|
12896
12906
|
"description": "Items that are not selected."
|
|
12907
|
+
},
|
|
12908
|
+
{
|
|
12909
|
+
"name": "virtual",
|
|
12910
|
+
"type": "boolean | undefined",
|
|
12911
|
+
"required": false,
|
|
12912
|
+
"description": "Windows the body: only the rows in (and just around) the vertical\nscrollport are mounted, so a client-side dataset of any size scrolls at\na constant cost. The table itself is the scroller as soon as you bound\nits height (`max-height` / `height`, or a flex item with\n`min-height: 0`); left unbounded, the nearest scrolling ancestor or the\npage scrolls it. The header sticks to that scroller. Column widths are\nmeasured once from the first rendered batch and then hold — later rows\nnever widen them. Expand rows and nested tables stay fully rendered\ninside their row (a nested table is never windowed); the row that owns\nfocus stays mounted even when scrolled out of view. Ignored by a table\nthat inherits its widths from a parent, and under a `#tbody` slot."
|
|
12897
12913
|
}
|
|
12898
12914
|
],
|
|
12899
12915
|
"events": [
|
|
@@ -19,7 +19,17 @@
|
|
|
19
19
|
The append icon joins it here so the two can share the hover swap; on a
|
|
20
20
|
non-clearable badge it renders inside the body instead. -->
|
|
21
21
|
<span v-if="props.clearable" class="bb-badge__trailing">
|
|
22
|
-
<
|
|
22
|
+
<template v-if="!props.icon">
|
|
23
|
+
<BbSpinner
|
|
24
|
+
v-if="props.loading && spinnerTrails()"
|
|
25
|
+
class="bb-badge__append-icon"
|
|
26
|
+
/>
|
|
27
|
+
<BbIcon
|
|
28
|
+
v-else-if="props['append:icon']"
|
|
29
|
+
class="bb-badge__append-icon"
|
|
30
|
+
:icon="props['append:icon']"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
23
33
|
<button
|
|
24
34
|
:id="`${badgeId}__clear`"
|
|
25
35
|
:aria-label="props.clearableLabel || t('badge.clearLabel').value"
|
|
@@ -45,7 +55,7 @@ import { useLogger } from '@/composables/useLogger';
|
|
|
45
55
|
import { removedPropsFor } from '@/deprecation/removedProps';
|
|
46
56
|
import { warnRemovedAttrs } from '@/utilities/functions/warnRemovedAttrs';
|
|
47
57
|
import BbIcon from '../BbIcon/BbIcon.vue';
|
|
48
|
-
import
|
|
58
|
+
import BbSpinner from '../BbSpinner/BbSpinner.vue';
|
|
49
59
|
import BadgeBodyContent from './BadgeBodyContent.vue';
|
|
50
60
|
import BbBadgeButton from './BbBadgeButton.vue';
|
|
51
61
|
import { provideBadgeHost } from './badgeButtonContext';
|
|
@@ -80,9 +90,7 @@ const normalizedSize = computed(() => {
|
|
|
80
90
|
});
|
|
81
91
|
|
|
82
92
|
/** The spinner overlays the append slot only when the append icon is the sole icon. */
|
|
83
|
-
const spinnerTrails =
|
|
84
|
-
() => !!props['append:icon'] && !props['prepend:icon']
|
|
85
|
-
);
|
|
93
|
+
const spinnerTrails = () => !!props['append:icon'] && !props['prepend:icon'];
|
|
86
94
|
|
|
87
95
|
/* ── BbBadgeButton detection ─────────────────────────────── */
|
|
88
96
|
|
|
@@ -106,15 +114,46 @@ const scanForButton = (nodes: unknown): boolean =>
|
|
|
106
114
|
const detectButton = () => scanForButton(slots.default?.({}));
|
|
107
115
|
|
|
108
116
|
// Published to the subtree: BadgeBodyContent — ours or the one inside a
|
|
109
|
-
// slotted BbBadgeButton — renders the leading side from this state.
|
|
117
|
+
// slotted BbBadgeButton — renders the leading side from this state. Plain
|
|
118
|
+
// getters, not `computed()`: see `HostValue`. Every entry is a bare prop read,
|
|
119
|
+
// so there is nothing for a computed to memoize — and seven reactive effects
|
|
120
|
+
// per badge is a real per-row tax in a table cell that carries three of them.
|
|
110
121
|
provideBadgeHost({
|
|
111
|
-
icon:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
icon: {
|
|
123
|
+
get value() {
|
|
124
|
+
return props.icon;
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
prependIcon: {
|
|
128
|
+
get value() {
|
|
129
|
+
return props['prepend:icon'];
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
appendIcon: {
|
|
133
|
+
get value() {
|
|
134
|
+
return props['append:icon'];
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
loading: {
|
|
138
|
+
get value() {
|
|
139
|
+
return !!props.loading;
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
spinnerLeads: {
|
|
143
|
+
get value() {
|
|
144
|
+
return !!props.loading && !spinnerTrails();
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
spinnerTrails: {
|
|
148
|
+
get value() {
|
|
149
|
+
return !!props.loading && spinnerTrails();
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
clearable: {
|
|
153
|
+
get value() {
|
|
154
|
+
return !!props.clearable;
|
|
155
|
+
},
|
|
156
|
+
},
|
|
118
157
|
});
|
|
119
158
|
|
|
120
159
|
const badgeClasses = computed(() => [
|
|
@@ -115,7 +115,11 @@
|
|
|
115
115
|
v-if="usesOverflowCollapse || !isCollapsed"
|
|
116
116
|
ref="chipsContainerRef"
|
|
117
117
|
class="bb-base-select__chips-row"
|
|
118
|
-
:class="{
|
|
118
|
+
:class="{
|
|
119
|
+
'bb-base-select__chips-row--hidden': isCollapsed,
|
|
120
|
+
'bb-base-select__chips-row--chips':
|
|
121
|
+
!comma && !!selectedOptions.length,
|
|
122
|
+
}"
|
|
119
123
|
>
|
|
120
124
|
<template v-if="comma">
|
|
121
125
|
<span
|
|
@@ -127,19 +131,40 @@
|
|
|
127
131
|
</span>
|
|
128
132
|
</template>
|
|
129
133
|
<!-- One badge per selection: removable (clearable) when
|
|
130
|
-
deselection is allowed, non-removable otherwise.
|
|
134
|
+
deselection is allowed, non-removable otherwise. The `chip`
|
|
135
|
+
slot replaces the badge outright (the badge below is its
|
|
136
|
+
fallback), so a consumer can render their own chip — a colored
|
|
137
|
+
tag, an avatar row — without reaching for `prepend`. No wrapper
|
|
138
|
+
element around it: the row itself supplies the spacing (see
|
|
139
|
+
`--chips` in index.css), so custom content needs none of our
|
|
140
|
+
class names to sit correctly. -->
|
|
131
141
|
<template v-else>
|
|
132
|
-
|
|
142
|
+
<!-- `v-for` on a `<template>`, not on the `<slot>` itself: a
|
|
143
|
+
`key` bound on a slot outlet is passed through as a slot
|
|
144
|
+
PROP. The template renders no element of its own. -->
|
|
145
|
+
<template
|
|
133
146
|
v-for="item in selectedOptions"
|
|
134
147
|
:key="hash(item.value)"
|
|
135
|
-
class="bb-base-select__chip"
|
|
136
|
-
:clearable="canDeselectChips"
|
|
137
|
-
size="lg"
|
|
138
|
-
variant="secondary"
|
|
139
|
-
@click:clear.stop="onChipUnselected(item)"
|
|
140
148
|
>
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
<slot
|
|
150
|
+
:deselect="() => onChipUnselected(item)"
|
|
151
|
+
:disabled="!canDeselectChips"
|
|
152
|
+
:item="item.item"
|
|
153
|
+
name="chip"
|
|
154
|
+
:text="item.text"
|
|
155
|
+
:value="item.value"
|
|
156
|
+
>
|
|
157
|
+
<BbBadge
|
|
158
|
+
class="bb-base-select__chip"
|
|
159
|
+
:clearable="canDeselectChips"
|
|
160
|
+
size="lg"
|
|
161
|
+
variant="secondary"
|
|
162
|
+
@click:clear.stop="onChipUnselected(item)"
|
|
163
|
+
>
|
|
164
|
+
{{ item.text }}
|
|
165
|
+
</BbBadge>
|
|
166
|
+
</slot>
|
|
167
|
+
</template>
|
|
143
168
|
</template>
|
|
144
169
|
</span>
|
|
145
170
|
<span v-if="isCollapsed" class="bb-base-select__max-reached">
|
|
@@ -769,6 +794,27 @@ export type BaseSelectPanelSlotProps = {
|
|
|
769
794
|
selectedOptions: BbSelectResolvedOption[];
|
|
770
795
|
};
|
|
771
796
|
|
|
797
|
+
/**
|
|
798
|
+
* Props exposed by the `chip` slot — called once per selected value in
|
|
799
|
+
* `multiple` mode, in place of the default badge. Not called in `comma` mode
|
|
800
|
+
* (there are no chips there) nor for the collapsed "+N" summary.
|
|
801
|
+
*/
|
|
802
|
+
export type BaseSelectChipSlotProps<Item = any> = {
|
|
803
|
+
/** The selected item, exactly as it was supplied in `items`. */
|
|
804
|
+
item: Item;
|
|
805
|
+
/** The item's resolved display text (what the default badge prints). */
|
|
806
|
+
text: string;
|
|
807
|
+
/** The item's resolved value — what sits in the v-model. */
|
|
808
|
+
value: any;
|
|
809
|
+
/**
|
|
810
|
+
* `true` while the control is disabled or readonly: the chip must not offer
|
|
811
|
+
* a way to remove itself, and `deselect` is a no-op.
|
|
812
|
+
*/
|
|
813
|
+
disabled: boolean;
|
|
814
|
+
/** Drops this value from the selection. No-op while `disabled`. */
|
|
815
|
+
deselect: () => void;
|
|
816
|
+
};
|
|
817
|
+
|
|
772
818
|
export type BaseSelectSlots<Item = any> = {
|
|
773
819
|
/** Content pinned to the top of the panel, above the search field. */
|
|
774
820
|
header?: (props: BaseSelectPanelSlotProps) => any;
|
|
@@ -784,6 +830,8 @@ export type BaseSelectSlots<Item = any> = {
|
|
|
784
830
|
'no-data'?: (props: { query: string; focus: () => void }) => any;
|
|
785
831
|
option?: (props: BaseSelectOptionSlotProps<Item>) => any;
|
|
786
832
|
group?: (props: BaseSelectGroupSlotProps<Item>) => any;
|
|
833
|
+
/** Replaces the default badge rendered for each selection in `multiple` mode. */
|
|
834
|
+
chip?: (props: BaseSelectChipSlotProps<Item>) => any;
|
|
787
835
|
};
|
|
788
836
|
```
|
|
789
837
|
|
|
@@ -885,12 +933,19 @@ export type BaseSelectSlots<Item = any> = {
|
|
|
885
933
|
pointer-events: none;
|
|
886
934
|
visibility: hidden;
|
|
887
935
|
}
|
|
888
|
-
}
|
|
889
936
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
937
|
+
/* Chips mode with at least one selection — never comma mode, which
|
|
938
|
+
separates with its own `,` and wants no gap. The `chip` slot lets a
|
|
939
|
+
consumer render their own element in place of our badge, so the layout
|
|
940
|
+
a chip needs (no shrinking, the gap between chips, and the negative
|
|
941
|
+
inset coupling below) lives HERE, on the row, rather than on a
|
|
942
|
+
`__chip` class custom content would have to know to apply. */
|
|
943
|
+
&--chips {
|
|
944
|
+
gap: 0.25rem;
|
|
945
|
+
|
|
946
|
+
> * {
|
|
947
|
+
flex-shrink: 0;
|
|
948
|
+
}
|
|
894
949
|
}
|
|
895
950
|
}
|
|
896
951
|
|
|
@@ -927,7 +982,7 @@ export type BaseSelectSlots<Item = any> = {
|
|
|
927
982
|
}
|
|
928
983
|
/* If there is no prepend icon, add a negative margin to the chips row so it smoothly couples with the border */
|
|
929
984
|
&:not(:has(.common-input-wrapper__prepend-icon)) {
|
|
930
|
-
.bb-base-select__chips-row
|
|
985
|
+
.bb-base-select__chips-row--chips {
|
|
931
986
|
margin-left: calc(
|
|
932
987
|
var(--bb-input-px) * var(--compensation-multiplier) * -1
|
|
933
988
|
);
|