@bfrs/agentic-components 0.3.6 → 0.3.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.
@@ -36,7 +36,30 @@ import "@bfrs/agentic-components/fonts.css";
36
36
  Import components by name:
37
37
 
38
38
  ```tsx
39
- import { BfrsProvider, Button, Input, Slider, ColorPicker, ColorSwatchGroup, OptionCardGroup, NumberStepper, SuggestInput, FileDropzone, RevealField, RevealAndCopy, Modal, DataTable, DateRangePicker, ToastProvider, useToast } from "@bfrs/agentic-components";
39
+ import {
40
+ BfrsProvider,
41
+ Button,
42
+ FormField,
43
+ Input,
44
+ Slider,
45
+ ColorPicker,
46
+ ColorSwatchGroup,
47
+ OptionCardGroup,
48
+ SelectableChipGroup,
49
+ NumberStepper,
50
+ SuggestInput,
51
+ FileDropzone,
52
+ RevealField,
53
+ RevealAndCopy,
54
+ Modal,
55
+ DataTable,
56
+ TableColumnVisibility,
57
+ TableSaveView,
58
+ TableToolbar,
59
+ DateRangePicker,
60
+ ToastProvider,
61
+ useToast
62
+ } from "@bfrs/agentic-components";
40
63
  ```
41
64
 
42
65
  React consumers should wrap BFRS surfaces in `BfrsProvider`; the provider owns `--bfrs-*` variables, color scheme, and portal inheritance.
@@ -119,7 +142,7 @@ Registered tags cover the documented component surface, including
119
142
  `bfrs-button-group`, `bfrs-toast-provider`, `bfrs-table-toolbar`,
120
143
  `bfrs-table-empty-state`, `bfrs-table-error-state`, `bfrs-table-skeleton`,
121
144
  `bfrs-table-row-actions`, `bfrs-table-bulk-actions`, and
122
- `bfrs-table-column-visibility` in addition to the existing component tags.
145
+ `bfrs-table-column-visibility`, and `bfrs-table-save-view` in addition to the existing component tags.
123
146
  Existing form and pattern tags include `bfrs-date-range-picker`,
124
147
  `bfrs-multi-select`, `bfrs-step-progress-card`, `bfrs-data-table`,
125
148
  `bfrs-modal`, `bfrs-tabs`, and the rest of the documented `bfrs-*` surface.
@@ -164,7 +187,7 @@ Use attributes for simple props: `variant`, `size`, `tone`, `label`, `loading`,
164
187
 
165
188
  Common component events: `(value-change)`, `(checked-change)`, `(open-change)`, `(reveal-change)`, `(copy)`, `(copy-error)`, `(close)`, `(confirm)`, `(cancel)`, `(cell-action)`, `(cell-event)`, `(row-click)`, `(sort-change)`, `(page-change)`, `(page-size-change)`, `(action-select)`, `(dropdown-select)`, `(date-range-change)`, `(search-change)`, `(open-filters)`, `(apply)`, `(reset)`, and `(submit)`. Payloads are available on `$event.detail`.
166
189
 
167
- For validated forms, keep client validation, API validation, and submit side effects in the consuming app. In React, pass field errors through `FormField errorText` and set `Input error`. In Angular, use `bfrs-input` with `[attr.error]` and `[attr.error-message]`, listen to `(value-change)`, and manage success/failure notifications with `bfrs-toast-manager`.
190
+ For validated forms, keep client validation, API validation, and submit side effects in the consuming app. In React, wrap controls in `FormField`, pass field errors through `errorText`, and set the control's `error` prop when supported. In Angular, wrap controls that do not render their own label in `bfrs-form-field`; pass `error-text` and `helper-text` there, listen to the control's `(value-change)`, and manage success/failure notifications with `bfrs-toast-manager`.
168
191
 
169
192
  ```html
170
193
  <bfrs-select
@@ -398,7 +421,7 @@ Square button for icon-only actions. Always provide `aria-label`.
398
421
 
399
422
  ### Forms
400
423
 
401
- > **Rule:** Always wrap form controls in `FormField` — it handles label, helper text, error text, and aria wiring automatically.
424
+ > **Rule:** Always wrap form controls in `FormField` — it handles label, helper text, error text, consistent spacing, and aria wiring automatically.
402
425
 
403
426
  ---
404
427
 
@@ -406,6 +429,8 @@ Square button for icon-only actions. Always provide `aria-label`.
406
429
 
407
430
  Wraps any form control with label, helper, and error text. Connects them via `aria-describedby`.
408
431
 
432
+ The label always renders on its own block row with a `6px` vertical gap before the control. This applies consistently to full-width controls such as `Input` and inline controls such as `NumberStepper`; do not add manual margins between the label and control.
433
+
409
434
  ```tsx
410
435
  <FormField
411
436
  label="Email address"
@@ -415,6 +440,28 @@ Wraps any form control with label, helper, and error text. Connects them via `ar
415
440
  >
416
441
  <Input type="email" value={email} onChange={…} />
417
442
  </FormField>
443
+
444
+ <FormField label="Quantity">
445
+ <NumberStepper
446
+ value={quantity}
447
+ min={1}
448
+ max={10}
449
+ onValueChange={setQuantity}
450
+ />
451
+ </FormField>
452
+ ```
453
+
454
+ Angular / Custom Elements:
455
+
456
+ ```html
457
+ <bfrs-form-field label="Quantity" helper-text="Choose between 1 and 10 items.">
458
+ <bfrs-number-stepper
459
+ [value]="quantity"
460
+ min="1"
461
+ max="10"
462
+ (value-change)="quantity = $event.detail.value">
463
+ </bfrs-number-stepper>
464
+ </bfrs-form-field>
418
465
  ```
419
466
 
420
467
  | Prop | Type | Default |
@@ -548,6 +595,7 @@ const [range, setRange] = useState<DateRangeValue>({
548
595
  | `maxRangeDays` | `number` | — |
549
596
  | `months` | `1 \| 2` | `2` |
550
597
  | `open` / `onOpenChange` | controlled popover state | — |
598
+ | `defaultOpen` | `boolean` | `false` |
551
599
 
552
600
  Keep API date formatting, route updates, and business limits in the consuming app.
553
601
 
@@ -580,6 +628,7 @@ Combobox with search. Use when options list is long (10+).
580
628
  | `disabled` | `boolean` | `false` |
581
629
  | `error` | `boolean` | `false` |
582
630
  | `clearable` | `boolean` | `true` |
631
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` |
583
632
 
584
633
  ---
585
634
 
@@ -621,6 +670,7 @@ Angular custom element:
621
670
  | `error` | `boolean` | `false` |
622
671
  | `clearable` | `boolean` | `true` |
623
672
  | `maxVisibleValues` | `number` | `2` |
673
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` |
624
674
 
625
675
  ---
626
676
 
@@ -657,25 +707,73 @@ Angular: `<bfrs-selectable-chip-group multiple [props]="{ options, value }" (val
657
707
 
658
708
  #### `NumberStepper`
659
709
 
660
- Quantity input rendered as decrement, value, increment controls.
710
+ Quantity input rendered as decrement, value, increment controls. It is an inline-width control, so use `FormField` for its visible label and consistent label-to-control spacing.
661
711
 
662
712
  ```tsx
663
- <NumberStepper value={quantity} min={1} max={10} onValueChange={setQuantity} />
713
+ <FormField label="Quantity">
714
+ <NumberStepper
715
+ value={quantity}
716
+ min={1}
717
+ max={10}
718
+ step={1}
719
+ onValueChange={setQuantity}
720
+ />
721
+ </FormField>
664
722
  ```
665
723
 
666
- Angular: `<bfrs-number-stepper [value]="quantity" min="1" max="10" (value-change)="quantity = $event.detail.value"></bfrs-number-stepper>`.
724
+ | Prop | Type | Default |
725
+ |------|------|---------|
726
+ | `value` | `number` | — |
727
+ | `defaultValue` | `number` | `0` |
728
+ | `onValueChange` | `(value: number) => void` | — |
729
+ | `min` | `number` | — |
730
+ | `max` | `number` | — |
731
+ | `step` | `number` | `1` |
732
+ | `size` | `"sm" \| "md"` | `"md"` |
733
+ | `error` | `boolean` | `false` |
734
+ | `disabled` | `boolean` | `false` |
735
+
736
+ Angular:
737
+
738
+ ```html
739
+ <bfrs-form-field label="Quantity">
740
+ <bfrs-number-stepper
741
+ [value]="quantity"
742
+ min="1"
743
+ max="10"
744
+ step="1"
745
+ (value-change)="quantity = $event.detail.value">
746
+ </bfrs-number-stepper>
747
+ </bfrs-form-field>
748
+ ```
667
749
 
668
750
  ---
669
751
 
670
752
  #### `SuggestInput`
671
753
 
672
- Free-text input with async-friendly suggestions dropdown. Use when the user may type arbitrary text; use `SearchableSelect` when the final value must be from a bounded option list.
754
+ Free-text input with async-friendly suggestions dropdown. Use when the user may type arbitrary text; use `SearchableSelect` when the final value must be from a bounded option list. Wrap it in `FormField` when it needs a visible label, helper text, or validation message.
673
755
 
674
756
  ```tsx
675
- <SuggestInput value={city} suggestions={citySuggestions} onValueChange={setCity} onSuggestionSelect={setSelectedCity} />
757
+ <FormField label="City">
758
+ <SuggestInput
759
+ value={city}
760
+ suggestions={citySuggestions}
761
+ onValueChange={setCity}
762
+ onSuggestionSelect={setSelectedCity}
763
+ />
764
+ </FormField>
676
765
  ```
677
766
 
678
- Angular: `<bfrs-suggest-input [props]="{ suggestions, value }" (value-change)="value = $event.detail.value"></bfrs-suggest-input>`.
767
+ Angular:
768
+
769
+ ```html
770
+ <bfrs-form-field label="City">
771
+ <bfrs-suggest-input
772
+ [props]="{ suggestions: citySuggestions, value: city }"
773
+ (value-change)="city = $event.detail.value">
774
+ </bfrs-suggest-input>
775
+ </bfrs-form-field>
776
+ ```
679
777
 
680
778
  ---
681
779
 
@@ -703,6 +801,82 @@ Angular: `<bfrs-reveal-field label="Phone" value="9876543210"></bfrs-reveal-fiel
703
801
 
704
802
  ---
705
803
 
804
+ #### Additional form controls composition
805
+
806
+ Use `FormField` around controls that do not own a label. This keeps Quantity and City aligned in the same grid and preserves the standard `6px` label-to-control gap without demo-specific margins.
807
+
808
+ ```tsx
809
+ <Stack gap={5}>
810
+ <OptionCardGroup
811
+ value={fulfillmentMode}
812
+ onValueChange={setFulfillmentMode}
813
+ options={fulfillmentOptions}
814
+ />
815
+
816
+ <SelectableChipGroup
817
+ multiple
818
+ value={channels}
819
+ options={channelOptions}
820
+ addAction={{ label: "Add" }}
821
+ onValueChange={setChannels}
822
+ />
823
+
824
+ <Grid columns={2} gap={4}>
825
+ <FormField label="Quantity">
826
+ <NumberStepper
827
+ value={quantity}
828
+ min={1}
829
+ max={10}
830
+ onValueChange={setQuantity}
831
+ />
832
+ </FormField>
833
+
834
+ <FormField label="City">
835
+ <SuggestInput
836
+ value={city}
837
+ suggestions={citySuggestions}
838
+ onValueChange={setCity}
839
+ />
840
+ </FormField>
841
+ </Grid>
842
+
843
+ <FileDropzone
844
+ title="Upload invoice"
845
+ onFilesChange={setFiles}
846
+ />
847
+
848
+ <RevealField
849
+ label="Masked account field"
850
+ value="9876543210"
851
+ allowCopy
852
+ />
853
+ </Stack>
854
+ ```
855
+
856
+ Angular / Custom Elements:
857
+
858
+ ```html
859
+ <bfrs-grid columns="2" gap="4">
860
+ <bfrs-form-field label="Quantity">
861
+ <bfrs-number-stepper
862
+ [value]="quantity"
863
+ min="1"
864
+ max="10"
865
+ (value-change)="quantity = $event.detail.value">
866
+ </bfrs-number-stepper>
867
+ </bfrs-form-field>
868
+
869
+ <bfrs-form-field label="City">
870
+ <bfrs-suggest-input
871
+ [props]="{ suggestions: citySuggestions, value: city }"
872
+ (value-change)="city = $event.detail.value">
873
+ </bfrs-suggest-input>
874
+ </bfrs-form-field>
875
+ </bfrs-grid>
876
+ ```
877
+
878
+ ---
879
+
706
880
  #### `Slider`
707
881
 
708
882
  Controlled numeric range control. Use instead of raw `<input type="range">`.
@@ -915,6 +1089,12 @@ Small inline status label.
915
1089
  <Badge tone="warning">Pending</Badge>
916
1090
  <Badge tone="danger">Blocked</Badge>
917
1091
  <Badge count={3} aria-label="3 active filters" />
1092
+
1093
+ {/* Sizes: xs | sm (default) | md | lg */}
1094
+ <Badge size="xs" tone="success">xs</Badge>
1095
+ <Badge size="sm" tone="success">sm</Badge>
1096
+ <Badge size="md" tone="success">md</Badge>
1097
+ <Badge size="lg" tone="success">lg</Badge>
918
1098
  ```
919
1099
 
920
1100
  | Prop | Type | Default |
@@ -941,6 +1121,12 @@ Removable tag. Use in filter bars and multi-select UIs.
941
1121
  >
942
1122
  Status: Active
943
1123
  </Chip>
1124
+
1125
+ {/* Sizes: xs | sm (default) | md | lg */}
1126
+ <Chip size="xs" tone="primary">xs</Chip>
1127
+ <Chip size="sm" tone="primary">sm</Chip>
1128
+ <Chip size="md" tone="primary">md</Chip>
1129
+ <Chip size="lg" tone="primary">lg</Chip>
944
1130
  ```
945
1131
 
946
1132
  | Prop | Type | Default |
@@ -949,10 +1135,13 @@ Removable tag. Use in filter bars and multi-select UIs.
949
1135
  | `tone` | `"neutral" \| "primary" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` |
950
1136
  | `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"sm"` |
951
1137
  | `leftIcon` | `ReactNode` | — |
1138
+ | `rightIcon` | `ReactNode` | — |
952
1139
  | `removable` | `boolean` | `false` |
953
1140
  | `onRemove` | `() => void` | — |
954
1141
  | `removeLabel` | `string` | `"Remove"` |
955
1142
 
1143
+ Angular: use `left-icon="package"` / `right-icon="arrow-right"` for named Phosphor icons, or use named slots `<span slot="left-icon">…</span>`.
1144
+
956
1145
  ---
957
1146
 
958
1147
  #### `Alert`
@@ -967,11 +1156,18 @@ Contextual message banner. Auto-selects an icon based on tone.
967
1156
  >
968
1157
  Upgrade to keep access to all features.
969
1158
  </Alert>
1159
+
1160
+ {/* Sizes: xs | sm | md (default) | lg */}
1161
+ <Alert size="xs" tone="info" title="XSmall">Ultra-compact inline alert.</Alert>
1162
+ <Alert size="sm" tone="info" title="Small">Compact alert for dense layouts.</Alert>
1163
+ <Alert size="md" tone="info" title="Medium">Default alert size.</Alert>
1164
+ <Alert size="lg" tone="info" title="Large">Spacious alert for prominent messages.</Alert>
970
1165
  ```
971
1166
 
972
1167
  | Prop | Type | Default |
973
1168
  |------|------|---------|
974
1169
  | `tone` | `"info" \| "success" \| "warning" \| "danger"` | `"info"` |
1170
+ | `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` |
975
1171
  | `title` | `ReactNode` | — |
976
1172
  | `action` | `ReactNode` | — |
977
1173
  | `icon` | `ReactNode` | auto (from tone) |
@@ -1130,6 +1326,11 @@ Centered modal. Use for confirmations, forms, and focused tasks.
1130
1326
  | `size` | `"sm" \| "md" \| "lg" \| "xl" \| "full"` | `"md"` |
1131
1327
  | `open` | `boolean` | — |
1132
1328
  | `onOpenChange` | `(open: boolean) => void` | — |
1329
+ | `contentClassName` | `string` | — |
1330
+ | `headerClassName` | `string` | — |
1331
+ | `footerClassName` | `string` | — |
1332
+
1333
+ Angular: `content-class-name`, `header-class-name`, `footer-class-name` attributes.
1133
1334
 
1134
1335
  `Dialog` is a deprecated compatibility alias. Use `Modal` in new code.
1135
1336
 
@@ -1160,6 +1361,11 @@ Slide-out panel. Use for detail views, settings, and secondary flows.
1160
1361
  | `description` | `ReactNode` | — |
1161
1362
  | `open` | `boolean` | — |
1162
1363
  | `onOpenChange` | `(open: boolean) => void` | — |
1364
+ | `contentClassName` | `string` | — |
1365
+ | `headerClassName` | `string` | — |
1366
+ | `footerClassName` | `string` | — |
1367
+
1368
+ Angular: `content-class-name`, `header-class-name`, `footer-class-name` attributes.
1163
1369
 
1164
1370
  ---
1165
1371
 
@@ -1204,6 +1410,7 @@ Floating label on hover. Provide `content` as the tooltip text.
1204
1410
  | `children` | `ReactNode` | **required** |
1205
1411
  | `side` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` |
1206
1412
  | `delayDuration` | `number` | `350` |
1413
+ | `disabled` | `boolean` | `false` |
1207
1414
 
1208
1415
  ---
1209
1416
 
@@ -1228,6 +1435,43 @@ Floating content panel. Use for filters, pickers, and inline forms.
1228
1435
 
1229
1436
  ---
1230
1437
 
1438
+ #### `FilterDrawer`
1439
+
1440
+ Slide-out drawer pre-wired for filter forms. Manages its own Apply / Reset footer; the consumer owns filter state and the apply logic.
1441
+
1442
+ ```tsx
1443
+ <FilterDrawer
1444
+ title="Filter shipments"
1445
+ open={filtersOpen}
1446
+ onOpenChange={setFiltersOpen}
1447
+ onApply={handleApply}
1448
+ onReset={handleReset}
1449
+ activeFiltersCount={activeCount}
1450
+ footerActions={<Button variant="ghost">Save as preset</Button>}
1451
+ >
1452
+ <StatusFilter />
1453
+ <DateFilter />
1454
+ </FilterDrawer>
1455
+ ```
1456
+
1457
+ | Prop | Type | Default |
1458
+ |------|------|---------|
1459
+ | `open` | `boolean` | — |
1460
+ | `onOpenChange` | `(open: boolean) => void` | — |
1461
+ | `onApply` | `() => void` | — |
1462
+ | `onReset` | `() => void` | — |
1463
+ | `title` | `ReactNode` | `"Filters"` |
1464
+ | `description` | `ReactNode` | — |
1465
+ | `applyLabel` | `ReactNode` | `"Apply filters"` |
1466
+ | `resetLabel` | `ReactNode` | `"Reset all"` |
1467
+ | `activeFiltersCount` | `number` | `0` |
1468
+ | `applying` | `boolean` | `false` |
1469
+ | `footerActions` | `ReactNode` | — |
1470
+
1471
+ Angular: `(apply)`, `(reset)`, `(close)` events; `<span slot="footer-actions">…</span>` for extra footer content.
1472
+
1473
+ ---
1474
+
1231
1475
  ### Data Display
1232
1476
 
1233
1477
  ---
@@ -1259,12 +1503,13 @@ User avatar with image, initials fallback, and online status.
1259
1503
 
1260
1504
  #### `TablePagination`
1261
1505
 
1262
- Table page navigation. Handles ellipsis, edge pages, and optional page-size controls.
1506
+ Table page navigation. Handles ellipsis, edge pages, optional page-size controls, and two navigation variants: `indexed` (Prev, page indexes like `1 2 3 ... 10`, Next) and `simple` (Prev / Next only).
1263
1507
 
1264
1508
  ```tsx
1265
1509
  <TablePagination
1266
1510
  page={currentPage}
1267
1511
  totalPages={totalPages}
1512
+ variant="indexed"
1268
1513
  pageSize={pageSize}
1269
1514
  onPageChange={setCurrentPage}
1270
1515
  onPageSizeChange={setPageSize}
@@ -1276,8 +1521,14 @@ Table page navigation. Handles ellipsis, edge pages, and optional page-size cont
1276
1521
  | `page` | `number` | **required** |
1277
1522
  | `totalPages` | `number` | **required** |
1278
1523
  | `onPageChange` | `(page: number) => void` | **required** |
1524
+ | `variant` | `"indexed" \| "simple"` | `"indexed"` |
1279
1525
  | `pageSize` | `number` | — |
1280
1526
  | `onPageSizeChange` | `(pageSize: number) => void` | — |
1527
+ | `itemsPerPageLabel` | `string` | `"Items per page:"` |
1528
+
1529
+ Angular: `items-per-page-label` attribute.
1530
+
1531
+ > Pass `pagination={{ page, totalPages, variant: "simple", onPageChange }}` to `DataTable` when the built-in footer should show only Prev and Next.
1281
1532
 
1282
1533
  ---
1283
1534
 
@@ -1320,6 +1571,11 @@ Generic data table with sorting, loading skeleton, row actions, selection, pagin
1320
1571
  | `density` | `"compact" \| "default" \| "comfortable"` | `"default"` |
1321
1572
  | `emptyTitle` | `ReactNode` | `"No records found"` |
1322
1573
  | `emptyDescription` | `ReactNode` | — |
1574
+ | `defaultSorting` | `{ columnId: string; direction: "asc" \| "desc" } \| null` | `null` |
1575
+ | `maxHeight` | `number \| string` | — |
1576
+ | `stickyHeader` | `boolean` | `true` |
1577
+
1578
+ Angular: pass `sorting` as JSON attribute `[sorting]='{"columnId":"name","direction":"asc"}'`; `default-sorting`, `max-height`, `sticky-header` attributes are fully supported. `show-all-label` and `(show-all)` event are supported on `bfrs-table-column-visibility`.
1323
1579
 
1324
1580
  `DataTableColumn<T>`:
1325
1581
  ```ts
@@ -1337,6 +1593,33 @@ Generic data table with sorting, loading skeleton, row actions, selection, pagin
1337
1593
 
1338
1594
  When `loading` is true, `DataTable` renders built-in table-row skeleton placeholders rather than a spinner. The consumer app only owns the loading boolean.
1339
1595
 
1596
+ Use `TableSaveView` with `TableColumnVisibility` when the user changes columns or filters and needs to save the draft as a named view. It opens the "Save this view" modal, collects the view name, summarizes settings like hidden columns, and calls `onSave({ name, visibleColumnIds, hiddenColumnCount, filtersCount, appliedSettingLabels })`; persistence remains app-owned.
1597
+
1598
+ ```tsx
1599
+ <TableToolbar
1600
+ title="Orders"
1601
+ actions={
1602
+ <div className="bfrs-flex bfrs-items-center bfrs-gap-2">
1603
+ {dirty && (
1604
+ <TableSaveView
1605
+ columns={visibilityColumns}
1606
+ visibleColumnIds={visibleColumnIds}
1607
+ onSave={({ visibleColumnIds: nextSavedIds }) => setSavedColumnIds(nextSavedIds)}
1608
+ />
1609
+ )}
1610
+ <TableColumnVisibility
1611
+ columns={visibilityColumns}
1612
+ visibleColumnIds={visibleColumnIds}
1613
+ onVisibleColumnIdsChange={setVisibleColumnIds}
1614
+ onReset={() => setVisibleColumnIds(savedColumnIds)}
1615
+ />
1616
+ </div>
1617
+ }
1618
+ />
1619
+ ```
1620
+
1621
+ Angular custom-element consumers can use `<bfrs-table-save-view>` and listen for `(save-view)` with the same payload.
1622
+
1340
1623
  Custom-element DataTable action cells must be declarative because Angular/HTML cannot pass React `cell` functions through JSON. Use `cellType: "button"` for one inline button, `cellType: "buttons"` for multiple inline buttons, or `cellType: "actions"` for an action menu. Action clicks emit `cell-action` with `{ actionId, columnId, row, rowId, rowIndex }` and do not trigger `row-click`.
1341
1624
 
1342
1625
  ```html
@@ -1429,6 +1712,68 @@ not rendered. Listed custom-element events are re-emitted as `cell-event` with
1429
1712
 
1430
1713
  ---
1431
1714
 
1715
+ #### `TableColumnVisibility`
1716
+
1717
+ Column show/hide control used inside `TableToolbar`. Emits `visible-column-ids-change` and exposes a "Show all" reset button.
1718
+
1719
+ ```tsx
1720
+ <TableColumnVisibility
1721
+ columns={visibilityColumns}
1722
+ visibleColumnIds={visibleColumnIds}
1723
+ onVisibleColumnIdsChange={setVisibleColumnIds}
1724
+ onReset={() => setVisibleColumnIds(savedColumnIds)}
1725
+ showAllLabel="Show all columns"
1726
+ />
1727
+ ```
1728
+
1729
+ | Prop | Type | Default |
1730
+ |------|------|---------|
1731
+ | `columns` | `{ id: string; label: string }[]` | **required** |
1732
+ | `visibleColumnIds` | `string[]` | — |
1733
+ | `onVisibleColumnIdsChange` | `(ids: string[]) => void` | — |
1734
+ | `onReset` | `() => void` | — |
1735
+ | `onShowAll` | `() => void` | — |
1736
+ | `showAllLabel` | `string` | `"Show all"` |
1737
+ | `resetLabel` | `string` | `"Reset"` |
1738
+ | `label` | `ReactNode` | `"Columns"` |
1739
+
1740
+ Angular: `show-all-label` attribute; `(show-all)` event.
1741
+
1742
+ ---
1743
+
1744
+ #### `MetricCard`
1745
+
1746
+ KPI display card with trend indicator, optional subtitle, description, icon slot, and action slot.
1747
+
1748
+ ```tsx
1749
+ <MetricCard
1750
+ title="Total shipments"
1751
+ value="12,480"
1752
+ trendValue="+8.2%"
1753
+ trendDirection="up"
1754
+ subtitle="vs last month"
1755
+ icon={<Icon icon={Package} />}
1756
+ action={<Button size="sm" variant="ghost">View all</Button>}
1757
+ />
1758
+ ```
1759
+
1760
+ | Prop | Type | Default |
1761
+ |------|------|---------|
1762
+ | `title` | `ReactNode` | `"Metric"` |
1763
+ | `value` | `ReactNode` | — |
1764
+ | `subtitle` | `ReactNode` | — |
1765
+ | `description` | `ReactNode` | — |
1766
+ | `trendValue` | `ReactNode` | — |
1767
+ | `trendDirection` | `"up" \| "down" \| "neutral"` | `"neutral"` |
1768
+ | `icon` | `ReactNode` | — |
1769
+ | `action` | `ReactNode` | — |
1770
+ | `loading` | `boolean` | `false` |
1771
+ | `compact` | `boolean` | `false` |
1772
+
1773
+ Angular: `icon="package"` for a named Phosphor icon; use `<span slot="action">…</span>` for the action slot.
1774
+
1775
+ ---
1776
+
1432
1777
  ### Navigation
1433
1778
 
1434
1779
  ---
@@ -1742,7 +2087,7 @@ Grouped expandable sections. Use `type="single"` (default) to keep one panel ope
1742
2087
 
1743
2088
  For `type="single"`, `value`/`defaultValue`/`onValueChange` use a string; for `type="multiple"` they use a string array.
1744
2089
 
1745
- Angular: `<bfrs-accordion type="single" [props]="{ items: sections }" (value-change)="openSection = $event.detail.value"></bfrs-accordion>`. Pass each item's `content` as a string in custom-element usage.
2090
+ Angular: `<bfrs-accordion type="single" value="pickup" [props]="{ items: sections }" (value-change)="openSection = $event.detail.value"></bfrs-accordion>`. The `value` attribute supports controlled mode; use a JSON array string for `type="multiple"`. Pass each item's `content` as a string in custom-element usage.
1746
2091
 
1747
2092
  ---
1748
2093
 
@@ -1954,7 +2299,7 @@ Step-by-step progress indicator. Calculates `done` / `active` / `pending` state
1954
2299
  loading={isLoading}
1955
2300
  sorting={sorting}
1956
2301
  onSortingChange={setSorting}
1957
- pagination={{ page, totalPages, onPageChange: setPage }}
2302
+ pagination={{ page, totalPages, variant: "indexed", onPageChange: setPage }}
1958
2303
  />
1959
2304
  </Card>
1960
2305
  </Container>
@@ -2002,6 +2347,6 @@ Step-by-step progress indicator. Calculates `done` / `active` / `pending` state
2002
2347
 
2003
2348
  - Do not use raw Tailwind classes for spacing, color, or typography — use `Stack`, `Text`, and component props instead
2004
2349
  - Do not build custom modals, drawers, or dropdowns — use `Modal`, `ConfirmDialog`, `Drawer`, and `ActionMenu`
2005
- - Do not render form labels manually — use `FormField` which wires aria attributes automatically
2350
+ - Do not render form labels or add label margins manually — use `FormField`, which keeps labels on their own row, applies the standard gap, and wires aria attributes automatically
2006
2351
  - Do not use `<h1>`–`<h6>` or `<p>` directly — use `<Text variant="h1">` etc.
2007
2352
  - Do not add `loading` spinners manually — all interactive components accept a `loading` prop
@@ -3,6 +3,7 @@ import { ActionMenuItem } from '../../navigation/ActionMenu';
3
3
  export type SortDirection = "asc" | "desc";
4
4
  export type TableDensity = "compact" | "default" | "comfortable";
5
5
  export type TableAlign = "left" | "center" | "right";
6
+ export type TablePaginationVariant = "indexed" | "simple";
6
7
  export type DataTableSorting = {
7
8
  columnId: string;
8
9
  direction: SortDirection;
@@ -45,6 +46,8 @@ export type DataTableLeadingColumn<Row> = {
45
46
  export type DataTablePagination = {
46
47
  page: number;
47
48
  totalPages: number;
49
+ /** "indexed" shows Prev, page numbers, and Next. "simple" shows only Prev and Next. */
50
+ variant?: TablePaginationVariant;
48
51
  pageSize?: number;
49
52
  totalItems?: number;
50
53
  pageSizeOptions?: number[];
@@ -169,5 +172,9 @@ export type TableColumnVisibilityProps = {
169
172
  label?: ReactNode;
170
173
  resetLabel?: ReactNode;
171
174
  onReset?: () => void;
175
+ /** Label for the "Show all" footer button. Defaults to "Show all". */
176
+ showAllLabel?: ReactNode;
177
+ /** Override the "Show all" action. Defaults to making every column visible. */
178
+ onShowAll?: () => void;
172
179
  className?: string;
173
180
  };
@@ -1,2 +1,2 @@
1
1
  import { TableColumnVisibilityProps } from './DataTable.types';
2
- export declare function TableColumnVisibility({ columns, visibleColumnIds, onVisibleColumnIdsChange, label, resetLabel, onReset, className }: TableColumnVisibilityProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function TableColumnVisibility({ columns, visibleColumnIds, onVisibleColumnIdsChange, label, resetLabel, onReset, showAllLabel, onShowAll, className }: TableColumnVisibilityProps): import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,2 @@
1
1
  import { TablePaginationProps } from './DataTable.types';
2
- export declare function TablePagination({ page, totalPages, pageSize, totalItems, pageSizeOptions, onPageChange, onPageSizeChange, itemLabel, itemsPerPageLabel, className, pageSizeControl, showPageNavigation }: TablePaginationProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function TablePagination({ page, totalPages, variant, pageSize, totalItems, pageSizeOptions, onPageChange, onPageSizeChange, itemLabel, itemsPerPageLabel, className, pageSizeControl, showPageNavigation }: TablePaginationProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { ReactNode } from 'react';
2
+ import { TableColumnVisibilityColumn } from './DataTable.types';
3
+ export type TableSaveViewPayload = {
4
+ name: string;
5
+ visibleColumnIds: string[];
6
+ hiddenColumnCount: number;
7
+ filtersCount: number;
8
+ appliedSettingLabels: string[];
9
+ };
10
+ export type TableSaveViewProps = {
11
+ open?: boolean;
12
+ defaultOpen?: boolean;
13
+ onOpenChange?: (open: boolean) => void;
14
+ onClose?: () => void;
15
+ trigger?: ReactNode;
16
+ triggerLabel?: ReactNode;
17
+ title?: ReactNode;
18
+ description?: ReactNode;
19
+ viewName?: string;
20
+ defaultViewName?: string;
21
+ onViewNameChange?: (viewName: string) => void;
22
+ viewNameLabel?: ReactNode;
23
+ viewNamePlaceholder?: string;
24
+ appliedLabel?: ReactNode;
25
+ appliedSettings?: ReactNode;
26
+ appliedSettingLabels?: string[];
27
+ columns?: TableColumnVisibilityColumn[];
28
+ visibleColumnIds?: string[];
29
+ hiddenColumnCount?: number;
30
+ filtersCount?: number;
31
+ cancelLabel?: ReactNode;
32
+ saveLabel?: ReactNode;
33
+ onCancel?: () => void;
34
+ onSave?: (payload: TableSaveViewPayload) => void | Promise<void>;
35
+ loading?: boolean;
36
+ disabled?: boolean;
37
+ closeOnSave?: boolean;
38
+ closeOnOverlayClick?: boolean;
39
+ closeOnEscape?: boolean;
40
+ preventClose?: boolean;
41
+ className?: string;
42
+ triggerClassName?: string;
43
+ };
44
+ export declare function TableSaveView({ open, defaultOpen, onOpenChange, onClose, trigger, triggerLabel, title, description, viewName, defaultViewName, onViewNameChange, viewNameLabel, viewNamePlaceholder, appliedLabel, appliedSettings, appliedSettingLabels, columns, visibleColumnIds, hiddenColumnCount, filtersCount, cancelLabel, saveLabel, onCancel, onSave, loading, disabled, closeOnSave, closeOnOverlayClick, closeOnEscape, preventClose, className, triggerClassName }: TableSaveViewProps): import("react/jsx-runtime").JSX.Element;
@@ -8,3 +8,4 @@ export * from './TableErrorState';
8
8
  export * from './TableRowActions';
9
9
  export * from './TableBulkActions';
10
10
  export * from './TableColumnVisibility';
11
+ export * from './TableSaveView';
@@ -2,6 +2,7 @@ import { HTMLAttributes, ReactNode } from 'react';
2
2
  import { VariantProps } from 'class-variance-authority';
3
3
  declare const alertVariants: (props?: ({
4
4
  tone?: "success" | "warning" | "danger" | "info" | null | undefined;
5
+ size?: "xs" | "sm" | "md" | "lg" | null | undefined;
5
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
7
  export type AlertProps = Omit<HTMLAttributes<HTMLDivElement>, "title"> & VariantProps<typeof alertVariants> & {
7
8
  title?: ReactNode;
@@ -10,6 +11,7 @@ export type AlertProps = Omit<HTMLAttributes<HTMLDivElement>, "title"> & Variant
10
11
  };
11
12
  export declare const Alert: import('react').ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "title"> & VariantProps<(props?: ({
12
13
  tone?: "success" | "warning" | "danger" | "info" | null | undefined;
14
+ size?: "xs" | "sm" | "md" | "lg" | null | undefined;
13
15
  } & import('class-variance-authority/types').ClassProp) | undefined) => string> & {
14
16
  title?: ReactNode;
15
17
  action?: ReactNode;