bitboss-ui 3.0.0-beta.49 → 3.0.0-beta.50

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/BbForm.md CHANGED
@@ -866,7 +866,8 @@ the save (nothing spins forever), and the error goes to
866
866
  save, for anything else you show meanwhile.
867
867
 
868
868
  To keep the fields editable during the save, set `disable-submit-lock` on the
869
- form.
869
+ form. The submit button still spins while the save is pending: the prop only
870
+ lifts the read-only lock.
870
871
 
871
872
  ### Saving in place: `snapshot()`
872
873
 
@@ -1425,7 +1426,7 @@ marker stays consistent with the rules.
1425
1426
 
1426
1427
  | Prop | Type | Default | Required | Description |
1427
1428
  | --- | --- | --- | --- | --- |
1428
- | `disableSubmitLock` | `boolean \| undefined` | `false` | | Keep the fields editable while a save runs. By default, while a `@submit` that returned a promise is pending, every field joined to the form is read-only (never disabled: values stay readable and focusable) and every `type="submit"` BbButton inside the form shows its spinner; both end when the promise settles. A `@submit` that returns nothing never locks. |
1429
+ | `disableSubmitLock` | `boolean \| undefined` | `false` | | Keep the fields editable while a save runs. By default, while a `@submit` that returned a promise is pending, every field joined to the form is read-only (never disabled: values stay readable and focusable); it ends when the promise settles. A `@submit` that returns nothing never locks. The `type="submit"` BbButtons inside the form show their spinner while the save is pending either way: this prop does not stop that. |
1429
1430
  | `fieldErrors` | `Record<string, unknown> \| null \| undefined` | | | A server's validation errors, shown on the fields they name, for as long as they are in the object: `form.errors` from Inertia's `useForm()`, `$page.props.errors`, a Laravel 422 body you kept in a `ref`. Keys are field paths (`email`, `address.city`, `address[city]`, `lines.0.qty`); a value is a message or a list of them; a nested object reads as dot paths. `null`, `undefined` and anything that is not a message show nothing. A key no field on the form has is in `unmatchedErrors`. The page owns them: editing the field does not hide its message, the next object you pass does. They never block a submit, and a field's own rule errors show first. When new messages arrive after a submit, the first field that has one takes focus. For a one-off message the form drops once the user edits the field, call `setErrors` instead. |
1430
1431
  | `id` | `string \| undefined` | | | Explicit id, so anything outside the form can read and drive it through `useBbFormContext(id)` — a dialog footer's submit button, a page toolbar, a leave guard. It is also the `<form>`'s own id, so a native `<button form="id" type="submit">` outside it submits it. Omit it when the form is self-contained: the slot scope already carries the same state for chrome that lives inside. |
1431
1432
  | `keepValuesOnUnmount` | `boolean \| Ref<boolean, boolean> \| undefined` | | | Keep a field's value (and its place in `@submit`) when it unmounts — the `v-if` wizard step you left, a collapsed section. Off, an unmounted field drops out of the values. |
@@ -380,7 +380,8 @@ symbol alone does nothing. Clearing the last digit leaves the field empty (no
380
380
  lone `$`), so the placeholder and `required` keep working. A pasted amount,
381
381
  symbol and all (`$1,234.56`, `1.234,56 €`, `1,234 kg`), is read as the number;
382
382
  another currency's symbol is dropped and the amount kept, with no conversion.
383
- The unit follows the number's plural (`1 hr`, `2 hrs`), and at rest a screen
383
+ The unit follows the number's plural (`1 day`, `2 days`; Greek `1 κιλό`,
384
+ `2 κιλά`), and at rest a screen
384
385
  reader hears the amount spelled out (`1,234.50 US dollars`, `20 kilograms`).
385
386
  Percent is written the locale's way: `15%` in English and Italian, `15 %` in
386
387
  German and French, `%15` in Turkish. `unit` is typed: `kilowatt` and
@@ -737,12 +738,13 @@ Parts: `root` (the container), `label`, `description`, `message` (the errors / w
737
738
  States: `errors`, `warnings` (never beside errors), `loading`, `disabled`, `readonly`, `hasValue` (written `pt:box:has-value`). A name outside that list is a type error and an
738
739
  ESLint error.
739
740
 
740
- The currency line item above, restyled through those parts: muted affixes on
741
- `pt:prefix` / `pt:suffix`, tabular figures on `pt:input`, a primary edge on
741
+ The currency line item above, restyled through those parts: the `$` comes
742
+ from `currency` (it is text, so `pt:input` styles it), a muted note on
743
+ `pt:suffix`, tabular figures on `pt:input`, a primary edge on
742
744
  `pt:box:has-value` (`0` still counts). The stepper buttons in the slot ring
743
745
  are yours, not a part.
744
746
 
745
- **pt:prefix / pt:suffix / pt:input — the invoice unit price**
747
+ **pt:suffix / pt:input — the invoice unit price**
746
748
 
747
749
  ```vue
748
750
  <template>
@@ -752,24 +754,22 @@ are yours, not a part.
752
754
  <div>
753
755
  <p class="text-sm font-medium">Line item · INV-2048</p>
754
756
  <p class="text-xs text-(--bb-text-muted)">
755
- Currency stays in the chrome; the model is a clean number.
757
+ The dollar sign sits in the text; the model is a clean number.
756
758
  </p>
757
759
  </div>
758
760
  <BbNumberInput
759
761
  v-model="price"
760
762
  compact
763
+ currency="USD"
761
764
  label="Unit price"
762
- :max-precision="2"
763
765
  :min="0"
764
766
  name="unit-price"
765
767
  pt:box:has-value="border-(--bb-primary)"
766
768
  pt:input="tabular-nums"
767
- pt:prefix="text-(--bb-text-muted)"
768
769
  pt:suffix="text-(--bb-text-muted)"
769
770
  :step="0.5"
770
771
  >
771
- <template #prefix>$</template>
772
- <template #suffix>USD</template>
772
+ <template #suffix>per unit</template>
773
773
  </BbNumberInput>
774
774
  </div>
775
775
  </template>
@@ -779,8 +779,9 @@ import { ref } from 'vue';
779
779
  import { BbNumberInput } from 'bitboss-ui';
780
780
 
781
781
  // Same currency line item as `PriceCurrencyAffixes.vue`, restyled through
782
- // passthrough: muted affixes on `pt:prefix` / `pt:suffix`, tabular figures
783
- // on `pt:input`, a primary edge on `pt:box:has-value` (`0` counts).
782
+ // passthrough: the `$` comes from `currency` (inside the text, never in
783
+ // `#prefix`), a muted note on `pt:suffix`, tabular figures on `pt:input`, a
784
+ // primary edge on `pt:box:has-value` (`0` counts).
784
785
  const price = ref<number | null>(49.99);
785
786
  </script>
786
787
  ```
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 3,
3
3
  "library": "bitboss-ui",
4
- "version": "3.0.0-beta.49",
4
+ "version": "3.0.0-beta.50",
5
5
  "upgrade": "v2-to-v3",
6
6
  "guide": "ai/guides/migration/v2-to-v3.md",
7
7
  "legend": {
@@ -60,7 +60,8 @@
60
60
  "3.0.0-beta.46",
61
61
  "3.0.0-beta.47",
62
62
  "3.0.0-beta.48",
63
- "3.0.0-beta.49"
63
+ "3.0.0-beta.49",
64
+ "3.0.0-beta.50"
64
65
  ],
65
66
  "unpublished": [
66
67
  "3.0.0-alpha.2",
@@ -5806,12 +5807,12 @@
5806
5807
  {
5807
5808
  "id": "f320",
5808
5809
  "kind": "behaviour",
5809
- "summary": "BbForm locks while a promise `@submit` runs: joined fields read-only, `type=submit` buttons spin; opt out with `disable-submit-lock`",
5810
+ "summary": "BbForm locks while a promise `@submit` runs: joined fields read-only, `type=submit` buttons spin; `disable-submit-lock` keeps the fields editable (the buttons still spin)",
5810
5811
  "components": [
5811
5812
  "BbForm",
5812
5813
  "BbButton"
5813
5814
  ],
5814
- "description": "While a @submit that returned a promise is pending, every field joined to the BbForm is read-only (never disabled; aria-readonly) and every type=\"submit\" BbButton inside spins; both end when it settles. A sync submit never locks. Opt out with disable-submit-lock. A useBbFormField handle gains readonly",
5815
+ "description": "While a @submit that returned a promise is pending, every field joined to the BbForm is read-only (never disabled; aria-readonly) and every type=\"submit\" BbButton inside spins; both end when it settles. A sync submit never locks. disable-submit-lock keeps the fields editable; the submit buttons spin while the save is pending either way (owner, readonly-fix). A useBbFormField handle gains readonly",
5815
5816
  "ruling": "owner 2026-10-07 (Q60.17)",
5816
5817
  "guide": "v2-to-v3.md § 6 item 53 + BbForm.guide.md § While it saves",
5817
5818
  "firstReleasedIn": "3.0.0-beta.49"
@@ -5859,7 +5860,7 @@
5859
5860
  "components": [
5860
5861
  "BbNumberInput"
5861
5862
  ],
5862
- "description": "BbNumberInput currency (new, ISO 4217) / unit (new, an Intl unit) paint the symbol or unit INSIDE the editable text, the locale's way ($1,234.50, 1234,50 €, CHF-1’234.56, 20 kg, 15%): event.target.value, pt:input listeners and a consumer maxlength / pattern see the shell too (a maxlength counts it); Home / End move the caret to the number's edges, never the value; the unit follows the number's plural (1 hr, 2 hrs) and at rest aria-valuetext is the spoken amount (1,234.50 US dollars). unit is typed (BbNumberUnit: kilowatt, square-meter are type errors). The model stays the number (unit=\"percent\" showing 15% is 15); currency sets the decimals unless min-precision / max-precision are set; disable-formatting ignores both",
5863
+ "description": "BbNumberInput currency (new, ISO 4217) / unit (new, an Intl unit) paint the symbol or unit INSIDE the editable text, the locale's way ($1,234.50, 1234,50 €, CHF-1’234.56, 20 kg, 15%): event.target.value, pt:input listeners and a consumer maxlength / pattern see the shell too (a maxlength counts it); Home / End move the caret to the number's edges, never the value; the unit follows the number's plural (1 day, 2 days; el 1 κιλό, 2 κιλά) and at rest aria-valuetext is the spoken amount (1,234.50 US dollars). unit is typed (BbNumberUnit: kilowatt, square-meter are type errors). The model stays the number (unit=\"percent\" showing 15% is 15); currency sets the decimals unless min-precision / max-precision are set; disable-formatting ignores both",
5863
5864
  "ruling": "owner 2026-10-07 (Q60.25b)",
5864
5865
  "guide": "v2-to-v3.md § 6 item 55 + BbNumberInput.guide.md § Currency & unit fields",
5865
5866
  "firstReleasedIn": "3.0.0-beta.49"
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "bitboss-ui",
4
- "version": "3.0.0-beta.49",
5
- "generatedAt": "2026-10-07T21:07:12.711Z",
4
+ "version": "3.0.0-beta.50",
5
+ "generatedAt": "2026-10-07T22:12:47.592Z",
6
6
  "legend": {
7
7
  "referenceOnly": "true = internal building block, documented so trees and sources can be read. Never write it in a template; most are not exported.",
8
8
  "types[].usage": "api = the type of a prop, event or slot value you pass; wrapper = a component Props/Events/Slots type (its component entry lists every field); advanced = everything else.",
@@ -491,7 +491,7 @@
491
491
  {"name":"BbDropdownButton","group":"Overlay","description":"A button with primary action and all other actions collected in a dropdown.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbDropdownButton.md","source":"ai/source/BbDropdownButton.md","props":[{"name":"adaptive","type":"boolean | undefined","required":false,"default":"`config.adaptive` (`true`)","description":"On mobile viewports (below `config.mobileMaxWidth`), render the dropdown as a\nbottom-anchored, auto-sized off-canvas sheet that **navigates** through nested\nsubmenus (drilling one level at a time with a back control) instead of showing\nhover flyouts. Slots and selection behave exactly as on desktop.\n\nWhen unset, falls back to the global `config.adaptive`."},{"name":"append:icon","type":"string | undefined","required":false,"description":"Icon to be added on the right of the text."},{"name":"block","type":"boolean | undefined","required":false,"default":"false","description":"Displays the component as full width."},{"name":"compact","type":"boolean | undefined","required":false,"default":"false","description":"Apply the compact density variant — the same ladder `BbSelect` uses, on\nboth surfaces: flyout rows go 28px → 24px, sheet rows 44px → 36px. The\nlabel size does not change; density here is height, as it is for a select.\n\nA menu row is a touch target in a way a select option is not quite — a\nmis-tap fires the neighbouring COMMAND rather than picking the wrong value\n— so the compact sheet stops at 36px and never follows the flyout down to\n24px. That is the same floor the select's sheet holds."},{"name":"disableAutoLoading","type":"boolean | undefined","required":false,"default":"false","description":"Disables the automatic loading state that tracks async actions\n(enabled by default). Only affects the main action button: the split\ntoggle half still mirrors the dropdown's internal async-action loading\nstate."},{"name":"disabled","type":"boolean | undefined","required":false,"default":"false","description":"Disables the component"},{"name":"eager","type":"boolean | undefined","required":false,"default":"false","description":"Renders the menu panel at mount instead of on the first open (the phone\nsheet's too), and keeps its rows after a close. Without it the rows mount\non each open and go once the menu has closed."},{"name":"id","type":"string | undefined","required":false,"description":"Names this dropdown: the activator's id (unless it already has one),\nthe menu's `menu_<id>`, and the key `useBbDropdownContext(id)` and a\ncompanion `BbDropdownGroup` pair by. Generated when omitted."},{"name":"items","type":"BbDropdownItem[]","required":true,"default":"[]","description":"Array used to render each dropdown button.\nThey can act as `button`, as `a`, or as `router-link`\nas they are based on the functionality provided by `BbBaseButton`.\n\nPass a flat array for a single group, nest arrays for divider-separated\ngroups, or pass {@link InternalGroup} objects for titled / selectable sections.\nAn item may carry its own `items` to open a submenu. The shapes can be mixed."},{"name":"loading","type":"boolean | undefined","required":false,"default":"false","description":"Triggers a loading indicator."},{"name":"offCanvasProps","type":"Partial<BbOffCanvasProps> | undefined","required":false,"description":"Props for the phone sheet. A `pt` inside it, object or colon keys, styles\nonly that sheet.\n\nOverrides forwarded to the underlying `BbOffCanvas` when `adaptive` renders\nthe sheet on mobile. Merged over the adaptive defaults\n(`side: 'bottom'`, `size: 'auto'`, `draggable: true`). Ignored when\n`adaptive` is `false`.\n\nA `pt` inside it styles ONLY the sheet, in `BbOffCanvas`'s part words\n(`root`, `header`, `title`, `description`, `content`, `footer`, `close`;\nstate `open`), object (`{ pt: { header: '…' } }`) or colon keys\n(`{ 'pt:header': '…' }`) alike. It merges after what this component\nforwards to its sheet, so it wins a conflict and drops nothing — see the\npassthrough guide § Inside the sheet."},{"name":"offset","type":"number | undefined","required":false,"default":"4","description":"Space the floating container will maintain from the activator."},{"name":"padding","type":"number | undefined","required":false,"default":"10","description":"Minimum empty space to keep from the edge of the page"},{"name":"placement","type":"Placement | \"cursor\" | undefined","required":false,"default":"'bottom'","values":["top","bottom","left","right","top-start","top-end","right-start","right-end","bottom-start","bottom-end","left-start","left-end","cursor"],"description":"Position of the dropdown relative to the activator.\nUse `'cursor'` to anchor at the mouse/touch coordinates.\n(No arrow is drawn either way — arrows are tooltip-only in v3.)"},{"name":"prepend:icon","type":"string | undefined","required":false,"description":"Icon to be added on the left of the text."},{"name":"pt","type":"BbDropdownButtonPtMap | undefined","required":false,"description":"Passthrough, object form: the same keys as the `pt:*` attributes\nwithout the prefix. See the `pt:<part>` row."},{"name":"pt:footer","type":"PtValue | undefined","required":false},{"name":"pt:footer:disabled","type":"PtValue | undefined","required":false},{"name":"pt:footer:loading","type":"PtValue | undefined","required":false},{"name":"pt:footer:open","type":"PtValue | undefined","required":false},{"name":"pt:header","type":"PtValue | undefined","required":false},{"name":"pt:header:disabled","type":"PtValue | undefined","required":false},{"name":"pt:header:loading","type":"PtValue | undefined","required":false},{"name":"pt:header:open","type":"PtValue | undefined","required":false},{"name":"pt:icon","type":"PtValue | undefined","required":false},{"name":"pt:icon:disabled","type":"PtValue | undefined","required":false},{"name":"pt:icon:loading","type":"PtValue | undefined","required":false},{"name":"pt:icon:open","type":"PtValue | undefined","required":false},{"name":"pt:input","type":"PtValue | undefined","required":false},{"name":"pt:input:disabled","type":"PtValue | undefined","required":false},{"name":"pt:input:loading","type":"PtValue | undefined","required":false},{"name":"pt:input:open","type":"PtValue | undefined","required":false},{"name":"pt:item","type":"PtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownButtonPtState>) => PtItemResult) | undefined","required":false},{"name":"pt:item:disabled","type":"PtValue | undefined","required":false},{"name":"pt:item:loading","type":"PtValue | undefined","required":false},{"name":"pt:item:open","type":"PtValue | undefined","required":false},{"name":"pt:item:selected","type":"PtValue | undefined","required":false},{"name":"pt:itemDescription","type":"PtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownButtonPtState>) => PtItemResult) | undefined","required":false},{"name":"pt:itemDescription:disabled","type":"PtValue | undefined","required":false},{"name":"pt:itemDescription:loading","type":"PtValue | undefined","required":false},{"name":"pt:itemDescription:open","type":"PtValue | undefined","required":false},{"name":"pt:itemDescription:selected","type":"PtValue | undefined","required":false},{"name":"pt:itemIcon","type":"PtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownButtonPtState>) => PtItemResult) | undefined","required":false},{"name":"pt:itemIcon:disabled","type":"PtValue | undefined","required":false},{"name":"pt:itemIcon:loading","type":"PtValue | undefined","required":false},{"name":"pt:itemIcon:open","type":"PtValue | undefined","required":false},{"name":"pt:itemIcon:selected","type":"PtValue | undefined","required":false},{"name":"pt:itemText","type":"PtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownButtonPtState>) => PtItemResult) | undefined","required":false},{"name":"pt:itemText:disabled","type":"PtValue | undefined","required":false},{"name":"pt:itemText:loading","type":"PtValue | undefined","required":false},{"name":"pt:itemText:open","type":"PtValue | undefined","required":false},{"name":"pt:itemText:selected","type":"PtValue | undefined","required":false},{"name":"pt:main","type":"PtValue | undefined","required":false},{"name":"pt:main:disabled","type":"PtValue | undefined","required":false},{"name":"pt:main:loading","type":"PtValue | undefined","required":false},{"name":"pt:main:open","type":"PtValue | undefined","required":false},{"name":"pt:panel","type":"PtValue | undefined","required":false},{"name":"pt:panel:disabled","type":"PtValue | undefined","required":false},{"name":"pt:panel:loading","type":"PtValue | undefined","required":false},{"name":"pt:panel:open","type":"PtValue | undefined","required":false},{"name":"pt:root","type":"PtValue | undefined","required":false},{"name":"pt:root:disabled","type":"PtValue | undefined","required":false},{"name":"pt:root:loading","type":"PtValue | undefined","required":false},{"name":"pt:root:open","type":"PtValue | undefined","required":false},{"name":"pt:sheet","type":"PtValue | undefined","required":false},{"name":"pt:sheet:disabled","type":"PtValue | undefined","required":false},{"name":"pt:sheet:loading","type":"PtValue | undefined","required":false},{"name":"pt:sheet:open","type":"PtValue | undefined","required":false},{"name":"pt:spinner","type":"PtValue | undefined","required":false},{"name":"pt:spinner:disabled","type":"PtValue | undefined","required":false},{"name":"pt:spinner:loading","type":"PtValue | undefined","required":false},{"name":"pt:spinner:open","type":"PtValue | undefined","required":false},{"name":"pt:text","type":"PtValue | undefined","required":false},{"name":"pt:text:disabled","type":"PtValue | undefined","required":false},{"name":"pt:text:loading","type":"PtValue | undefined","required":false},{"name":"pt:text:open","type":"PtValue | undefined","required":false},{"name":"pt:toggle","type":"PtValue | undefined","required":false},{"name":"pt:toggle:disabled","type":"PtValue | undefined","required":false},{"name":"pt:toggle:loading","type":"PtValue | undefined","required":false},{"name":"pt:toggle:open","type":"PtValue | undefined","required":false},{"name":"size","type":"Responsive<Sizes> | undefined","required":false,"default":"\"md\"","description":"Sets the size of the button. When the value is a string, it will be applied to all screen sizes.\nOtherwise, it will be applied to the screen size specified by the object."},{"name":"toggle:icon","type":"string | undefined","required":false,"description":"Replaces the chevron shown inside the dropdown toggle button.\nA constrained alternative to a slot: only the toggle's icon changes, so\nthe button keeps its semantics. Named after the part it sits in (the\n`toggle`), the way `prepend:icon` / `append:icon` name theirs."},{"name":"transitionDuration","type":"number | undefined","required":false,"default":"150","description":"How long the transition has to last in milliseconds. Menus use the fast\ntransition (150) rather than the medium one (250) the other popovers\ndefault to: a menu is opened to pick one command and is gone again, so\nit answers faster than a panel the user reads or edits in."},{"name":"variant","type":"ButtonVariantType | undefined","required":false,"default":"\"primary\"","values":["outline","primary","destructive","secondary","none","ghost","link"],"description":"Visual variant shared by both buttons of the split control.\n\n`'none'` is a blank starting point: the button keeps its structure and\nbehaviour but takes no look, so your own classes don't fight a built-in\none. You then own hover, focus and disabled. Fine for a one-off; keep\ninline styling short, and register a real variant when the look recurs:\nthe plugin's `buttonVariants` option types the name, and you style\n`.bb-button--<name>` (the library ships no CSS for it)."},{"name":"width","type":"string | number | undefined","required":false,"default":"'auto'","description":"Width of the dropdown in pixels, percentage, or 'auto' to fit content."}],"events":[{"name":"hidden","signature":"(): void","description":"Emitted once the menu has finished closing."},{"name":"hide","signature":"(): void","description":"Emitted as soon as the menu starts to close."},{"name":"item:selected","signature":"(value: any): void","description":"A row of a selectable group was selected — the menu's own event,\nre-emitted with the same payload (the item's resolved value)."},{"name":"item:unselected","signature":"(value: any): void","description":"A row of a selectable group was unselected (same payload as above)."},{"name":"show","signature":"(): void","description":"Emitted as soon as the menu (the popover, or the sheet on a phone) starts to open."},{"name":"shown","signature":"(): void","description":"Emitted once the menu is open and its open transition has finished."}],"slots":[{"name":"default","type":"any","description":"Companion content forwarded into the dropdown (e.g. `<BbDropdownGroup>`)."},{"name":"empty","type":"BbDropdownPlaceholderSlotProps","fields":["groupId?: string","text: string","error?: unknown"],"description":"Replaces the default \"no data\" placeholder row shown in a pipeline group\nthat resolved to no items. Rendered once per empty group. Forwarded to the\nunderlying `BbDropdown`."},{"name":"footer","type":"BbDropdownButtonEdgeSlotProps","fields":["disabled?: boolean","open: boolean","placement: Placement | \"cursor\""],"description":"Content pinned to the bottom of the dropdown panel, outside the scroll area."},{"name":"header","type":"BbDropdownButtonEdgeSlotProps","fields":["disabled?: boolean","open: boolean","placement: Placement | \"cursor\""],"description":"Content pinned to the top of the dropdown panel, outside the scroll area."},{"name":"item:append","type":"BbDropdownButtonItemSlotProps","fields":["disabled?: boolean","index: number","item: InternalItem","open: boolean","placement: Placement | \"cursor\"","label: string"],"description":"The trailing region of every menu row, replacing its `append:icon`; the\nsubmenu chevron and the check on a selected row take its place\n(BbDropdown's rules)."},{"name":"item:prepend","type":"BbDropdownButtonItemSlotProps","fields":["disabled?: boolean","index: number","item: InternalItem","open: boolean","placement: Placement | \"cursor\"","label: string"],"description":"The leading region of every menu row, replacing its `prepend:icon`\n(BbDropdown's rules)."},{"name":"loading","type":"BbDropdownPlaceholderSlotProps","fields":["groupId?: string","text: string","error?: unknown"],"description":"Replaces the default loading placeholder row shown in a pipeline group\nwhile its async items load. Rendered once per loading group. Forwarded to\nthe underlying `BbDropdown`."},{"name":"<value>","type":"BbDropdownItemSlotProps","description":"One row's content (label and description), by its value; in a fetched group, by its resolved `itemValue`.","dynamic":true},{"name":"<value>:prepend","type":"BbDropdownItemSlotProps","description":"One row's leading region; replaces its `prepend:icon`.","dynamic":true},{"name":"<value>:append","type":"BbDropdownItemSlotProps","description":"One row's trailing region; replaces its `append:icon` and its submenu chevron. The check takes its place on the selected row.","dynamic":true},{"name":"<groupId>:item","type":"BbDropdownItemSlotProps","description":"The content of every row of that group, and no other; a row's own `#<value>` still wins.","dynamic":true},{"name":"<groupId>:item:prepend","type":"BbDropdownItemSlotProps","description":"The leading region of every row of that group — e.g. an avatar per record, drawn from `item.meta`, while other rows keep their `prepend:icon`.","dynamic":true},{"name":"<groupId>:item:append","type":"BbDropdownItemSlotProps","description":"The trailing region of every row of that group; replaces their `append:icon`. The check takes its place on the selected row.","dynamic":true},{"name":"<groupId>","type":"BbDropdownItemSlotProps","description":"That group's title line (the group is passed as `item`, `groupTitle` is `true`).","dynamic":true},{"name":"<groupId>:prepend","type":"BbDropdownItemSlotProps","description":"The leading region of that group's title line.","dynamic":true},{"name":"<groupId>:append","type":"BbDropdownItemSlotProps","description":"The trailing region of that group's title line.","dynamic":true}],"exposed":[],"models":[],"pt":{"parts":[{"name":"root","description":"The group wrapper around the main button and the toggle."},{"name":"main","description":"The main (action) button, the left half — handed to it as its own `pt:root`."},{"name":"toggle","description":"The toggle button that opens the menu, the right half — handed to it as its own `pt:root`. Pairs with `toggle:icon`."},{"name":"icon","description":"The main button's content icons — handed to it as its own `pt:icon`. The toggle's chevron is chrome."},{"name":"spinner","description":"The main button's loading spinner."},{"name":"text","description":"The main button's label span."},{"name":"panel","description":"The menu surface (the flyout box on desktop, the sheet itself on a phone) — handed to the `BbDropdown`."},{"name":"sheet","description":"The bottom sheet the menu becomes on a phone only."},{"name":"header","description":"The band around your `#header` slot: at the top of the menu on desktop, the sheet's header on a phone — handed to the `BbDropdown`."},{"name":"footer","description":"The band around your `#footer` slot: at the bottom of the menu on desktop, the sheet's footer on a phone — handed to the `BbDropdown`."},{"name":"input","description":"A searchable group's search field in the menu — a broadcast, every searchable group's field — handed to the `BbDropdown` as its own `pt:input`."},{"name":"item","description":"One menu entry, submenus included — a broadcast resolved per entry. Also takes a per-item function, called with the entry."},{"name":"itemText","description":"An entry's label (its `label`) — resolved with that entry's states. A per-entry `#<value>` slot replaces it. Also takes a per-item function."},{"name":"itemIcon","description":"An entry's prepend / append icons — resolved with that entry's states. Also takes a per-item function."},{"name":"itemDescription","description":"An entry's muted second line (its `description`) — resolved with that entry's states. A per-entry `#<value>` slot replaces it. Also takes a per-item function."}],"states":[{"name":"selected","description":"Per node, on the entry parts only (`item`, `itemText`, `itemIcon`, `itemDescription`): a selectable group's checked entry. `pt:root:selected` is a type error, not a silent no-op."},{"name":"disabled","description":"Component-wide while the control is `disabled`; also per node, on the entry parts: a menu entry that is disabled."},{"name":"open","description":"Component-wide: the menu is open."},{"name":"loading","description":"Component-wide: `loading` is on — the main button shows its spinner."}]},"tree":{"forwards":[{"to":"BbButton","mapping":"{ main: 'root', icon: 'icon', spinner: 'spinner', text: 'text' }"},{"to":"BbDropdown","mapping":"{ panel: 'panel', sheet: 'sheet', header: 'header', footer: 'footer', input: 'input', item: 'item', itemText: 'itemText', itemIcon: 'itemIcon', itemDescription: 'itemDescription' }"},{"to":"BbButton","mapping":"{ toggle: 'root' }"}],"nodes":[{"name":"BbButton","kind":"public","vocabulary":"BbButton","bindings":[{"part":"root","target":"BbBaseButton"},{"part":"spinner","target":"BbSpinner"},{"part":"icon","target":"BbIcon"},{"part":"text","target":"span.bb-button__text"}],"cssProps":[{"name":"--h-xs","documented":true,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--icon-size-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--px-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--fs-xs","documented":false,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--r-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--gap-xs","documented":false,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--h-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--icon-size-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--px-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--fs-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--r-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--gap-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--h-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--icon-size-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--px-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--fs-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--r-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--gap-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--h-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--icon-size-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--px-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--fs-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--r-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--gap-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--h-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--icon-size-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--px-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--fs-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--r-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--gap-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--h-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--icon-size-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--px-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--fs-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--r-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--gap-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--h","documented":true,"readBy":["bb-button","bb-button--icon"]},{"name":"--icon-size","documented":true,"readBy":["bb-button__text","bb-spinner","bb-button__icon","bb-button__prepend-icon","bb-button__append-icon"]},{"name":"--px","documented":true,"readBy":["bb-button","bb-button__prepend-icon","bb-button__append-icon"]},{"name":"--fs","documented":true,"readBy":["bb-button"]},{"name":"--r","documented":true,"readBy":["bb-button"]},{"name":"--gap","documented":true,"readBy":["bb-button","bb-button__text","bb-spinner"]},{"name":"--bg","documented":true,"readBy":["bb-button"]},{"name":"--bg-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--bg-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--fg","documented":true,"readBy":["bb-button"]},{"name":"--border-color","documented":true,"readBy":["bb-button"]},{"name":"--ring-color","documented":true,"readBy":["bb-button"]},{"name":"--ring-size","documented":true,"readBy":["bb-button"]},{"name":"--bw","documented":true,"readBy":["bb-button"]},{"name":"--fg-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--border-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--fg-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--border-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--bg-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--fg-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--border-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--opacity-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--elev-tier","documented":true,"readBy":["bb-button--primary","bb-button--secondary","bb-button--outline","bb-button--destructive"]},{"name":"--elev","documented":true,"readBy":["bb-button"]}],"children":[{"name":"BbIcon","kind":"public","cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]},{"name":"BbBaseButton","kind":"public","vocabulary":"BbBaseButton","bindings":[{"part":"root","target":"RouterComponent"},{"part":"root","target":"a"},{"part":"root","target":"component"}],"children":[{"name":"RouterComponent","kind":"internal"}]},{"name":"BbSpinner","kind":"public","children":[{"name":"BbIcon","kind":"public","repeated":true,"cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]}]}]},{"name":"BbDropdown","kind":"public","vocabulary":"BbDropdown","bindings":[{"part":"panel","target":"CommonPopover"},{"part":"root","target":"CommonPopover"},{"part":"header","target":"div.bb-dropdown__header"},{"part":"list","target":"span.bb-dropdown__list"},{"part":"footer","target":"div.bb-dropdown__footer"}],"cssProps":[{"name":"--bg","documented":false,"readBy":["bb-dropdown__group-search"]},{"name":"--menu-inset","documented":true,"readBy":["bb-common-popover__bubble","bb-dropdown__group-search","bb-dropdown__list","bb-dropdown__placeholder--failed","bb-dropdown__divider"]}],"children":[{"name":"CommonPopover","kind":"internal"},{"name":"BbDropdownList","kind":"internal","children":[{"name":"BbBadge","kind":"public","vocabulary":"BbBadge","bindings":[{"part":"spinner","target":"BbSpinner"},{"part":"icon","target":"BbIcon"},{"part":"clear","target":"button.bb-badge__clear"}],"cssProps":[{"name":"--bg","documented":true,"readBy":["bb-badge"]},{"name":"--fg","documented":true,"readBy":["bb-badge","bb-badge__clear"]},{"name":"--bw","documented":true,"readBy":["bb-badge","bb-badge__clear"]},{"name":"--border-color","documented":false,"readBy":["bb-badge"]},{"name":"--ring-color","documented":true,"readBy":["bb-badge","bb-badge__clear","bb-badge__body","bb-badge--highlighted"]},{"name":"--ring-size","documented":true,"readBy":["bb-badge","bb-badge__clear","bb-badge__body","bb-badge--highlighted"]},{"name":"--min-size-md","documented":true,"readBy":["bb-badge"]},{"name":"--min-size","documented":true,"readBy":["bb-badge"]},{"name":"--elev","documented":true,"readBy":["bb-badge","bb-badge__clear","bb-badge__body","bb-badge--highlighted"]},{"name":"--r","documented":true,"readBy":["bb-badge","bb-badge__body","bb-badge__trailing","bb-badge__clear"]},{"name":"--fs","documented":true,"readBy":["bb-badge"]},{"name":"--icon-size","documented":true,"readBy":["bb-badge__icon","bb-badge__prepend-icon","bb-badge__append-icon","bb-badge__clear"]},{"name":"--gap","documented":true,"readBy":["bb-badge","bb-badge__body","bb-badge__trailing","bb-badge__clear"]},{"name":"--px","documented":true,"readBy":["bb-badge","bb-badge--icon","bb-badge__prepend-icon","bb-badge__append-icon","bb-badge--clearable"]},{"name":"--clear-size","documented":true,"readBy":["bb-badge__clear","bb-icon"]},{"name":"--bg-disabled","documented":true,"readBy":["bb-badge__body","bb-base-button--disabled","bb-badge--loading"]},{"name":"--fg-disabled","documented":true,"readBy":["bb-badge__body","bb-base-button--disabled","bb-badge--loading"]},{"name":"--border-disabled","documented":true,"readBy":["bb-badge__body","bb-base-button--disabled","bb-badge--loading"]},{"name":"--pad-left","documented":true,"readBy":["bb-badge__body"]},{"name":"--pad-right","documented":false,"readBy":["bb-badge__body","bb-badge__clear"]},{"name":"--size","documented":false}],"children":[{"name":"BbIcon","kind":"public","repeated":true,"cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]},{"name":"BbSpinner","kind":"public","repeated":true},{"name":"BadgeBodyContent","kind":"internal","children":[{"name":"BbIcon","kind":"public","repeated":true,"cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]},{"name":"BbSpinner","kind":"public","repeated":true}]}]},{"name":"BbIcon","kind":"public","repeated":true,"cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]},{"name":"ErrorIcon","kind":"internal","cssProps":[{"name":"--accent","documented":false,"readBy":["bb-error-icon"]}],"children":[{"name":"BbIcon","kind":"public","repeated":true,"cssProps":[{"name":"--w","documented":true,"readBy":["bb-icon"]},{"name":"--size","documented":false,"readBy":["bb-icon"]}]}]},{"name":"CommonPopover","kind":"internal","repeated":true},{"name":"BbDropdownList","kind":"internal","repeated":true}]},{"name":"AdaptiveDropdown","kind":"internal","forwards":[{"to":"BbOffCanvas","mapping":"{ header: 'header', footer: 'footer', panel: 'root', sheet: 'root' }"}],"children":[{"name":"BbOffCanvas","kind":"public","vocabulary":"BbOffCanvas","bindings":[{"part":"header","target":"div.bb-offcanvas__header"},{"part":"title","target":"span.bb-offcanvas__title"},{"part":"description","target":"p.bb-offcanvas__description"},{"part":"close","target":"CloseButton"},{"part":"content","target":"div.bb-offcanvas__content"},{"part":"footer","target":"div.bb-offcanvas__footer"}],"cssProps":[{"name":"--translate-x","documented":false,"readBy":["bb-offcanvas"]},{"name":"--translate-y","documented":false,"readBy":["bb-offcanvas"]},{"name":"--translate","documented":false,"readBy":["bb-offcanvas"]},{"name":"--stack-x","documented":true,"readBy":["bb-offcanvas"]},{"name":"--stack-y","documented":false,"readBy":["bb-offcanvas"]},{"name":"--stack-fill","documented":true,"readBy":["bb-offcanvas","bb-offcanvas--right","bb-offcanvas--left","bb-offcanvas--top","bb-offcanvas--bottom"]},{"name":"--stack-transition-duration","documented":true,"readBy":["bb-offcanvas"]},{"name":"--px","documented":true,"readBy":["bb-offcanvas__header","bb-close-button","bb-offcanvas__content","bb-offcanvas__footer"]},{"name":"--py","documented":false,"readBy":["bb-offcanvas__footer","bb-offcanvas__content","bb-offcanvas__header","bb-close-button"]},{"name":"--handle-w","documented":true,"readBy":["bb-offcanvas__handle"]},{"name":"--handle-h","documented":true,"readBy":["bb-offcanvas__handle"]},{"name":"--handle-bg","documented":true,"readBy":["bb-offcanvas__handle"]},{"name":"--transition-timing-function","documented":false,"readBy":["bb-offcanvas","bb-offcanvas--open"]},{"name":"--bg","documented":false,"readBy":["bb-offcanvas"]},{"name":"--fg","documented":false,"readBy":["bb-offcanvas"]},{"name":"--safe-top","documented":true,"readBy":["bb-offcanvas","bb-offcanvas--left","bb-offcanvas--right","bb-offcanvas--top","bb-offcanvas--fullscreen"]},{"name":"--safe-right","documented":false,"readBy":["bb-offcanvas","bb-offcanvas--right","bb-offcanvas--top","bb-offcanvas--bottom","bb-offcanvas--fullscreen"]},{"name":"--safe-bottom","documented":false,"readBy":["bb-offcanvas","bb-offcanvas--left","bb-offcanvas--right","bb-offcanvas--bottom","bb-offcanvas--fullscreen"]},{"name":"--safe-left","documented":false,"readBy":["bb-offcanvas","bb-offcanvas--left","bb-offcanvas--top","bb-offcanvas--bottom","bb-offcanvas--fullscreen"]},{"name":"--inset-top","documented":false,"readBy":["bb-offcanvas__content","bb-offcanvas__header"]},{"name":"--inset-right","documented":false,"readBy":["bb-offcanvas__header","bb-offcanvas__content","bb-offcanvas__footer"]},{"name":"--inset-bottom","documented":false,"readBy":["bb-offcanvas__footer","bb-offcanvas__content"]},{"name":"--inset-left","documented":false,"readBy":["bb-offcanvas__header","bb-offcanvas__content","bb-offcanvas__footer"]},{"name":"--close-size","documented":true,"readBy":["bb-close-button"]}],"children":[{"name":"CloseButton","kind":"internal","cssProps":[{"name":"--size","documented":true,"readBy":["bb-close-button"]},{"name":"--p","documented":true,"readBy":["bb-close-button"]}]}]},{"name":"BbSmoothHeight","kind":"public"},{"name":"BbButton","kind":"public","repeated":true,"cssProps":[{"name":"--h-xs","documented":true,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--icon-size-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--px-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--fs-xs","documented":false,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--r-xs","documented":false,"readBy":["bb-button","bb-button--xs"]},{"name":"--gap-xs","documented":false,"readBy":["bb-button","bb-button--xs","bb-button--sm-xs","bb-button--md-xs","bb-button--lg-xs","bb-button--xl-xs","bb-button--2xl-xs"]},{"name":"--h-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--icon-size-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--px-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--fs-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--r-sm","documented":false,"readBy":["bb-button","bb-button--sm"]},{"name":"--gap-sm","documented":false,"readBy":["bb-button","bb-button--sm","bb-button--sm-sm","bb-button--md-sm","bb-button--lg-sm","bb-button--xl-sm","bb-button--2xl-sm"]},{"name":"--h-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--icon-size-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--px-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--fs-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--r-md","documented":false,"readBy":["bb-button","bb-button--md"]},{"name":"--gap-md","documented":false,"readBy":["bb-button","bb-button--md","bb-button--sm-md","bb-button--md-md","bb-button--lg-md","bb-button--xl-md","bb-button--2xl-md"]},{"name":"--h-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--icon-size-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--px-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--fs-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--r-lg","documented":false,"readBy":["bb-button","bb-button--lg"]},{"name":"--gap-lg","documented":false,"readBy":["bb-button","bb-button--lg","bb-button--sm-lg","bb-button--md-lg","bb-button--lg-lg","bb-button--xl-lg","bb-button--2xl-lg"]},{"name":"--h-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--icon-size-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--px-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--fs-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--r-xl","documented":false,"readBy":["bb-button","bb-button--xl"]},{"name":"--gap-xl","documented":false,"readBy":["bb-button","bb-button--xl","bb-button--sm-xl","bb-button--md-xl","bb-button--lg-xl","bb-button--xl-xl","bb-button--2xl-xl"]},{"name":"--h-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--icon-size-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--px-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--fs-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--r-2xl","documented":false,"readBy":["bb-button","bb-button--2xl"]},{"name":"--gap-2xl","documented":false,"readBy":["bb-button","bb-button--2xl","bb-button--sm-2xl","bb-button--md-2xl","bb-button--lg-2xl","bb-button--xl-2xl","bb-button--2xl-2xl"]},{"name":"--h","documented":true,"readBy":["bb-button","bb-button--icon"]},{"name":"--icon-size","documented":true,"readBy":["bb-button__text","bb-spinner","bb-button__icon","bb-button__prepend-icon","bb-button__append-icon"]},{"name":"--px","documented":true,"readBy":["bb-button","bb-button__prepend-icon","bb-button__append-icon"]},{"name":"--fs","documented":true,"readBy":["bb-button"]},{"name":"--r","documented":true,"readBy":["bb-button"]},{"name":"--gap","documented":true,"readBy":["bb-button","bb-button__text","bb-spinner"]},{"name":"--bg","documented":true,"readBy":["bb-button"]},{"name":"--bg-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--bg-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--fg","documented":true,"readBy":["bb-button"]},{"name":"--border-color","documented":true,"readBy":["bb-button"]},{"name":"--ring-color","documented":true,"readBy":["bb-button"]},{"name":"--ring-size","documented":true,"readBy":["bb-button"]},{"name":"--bw","documented":true,"readBy":["bb-button"]},{"name":"--fg-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--border-hover","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--fg-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--border-pressed","documented":true,"readBy":["bb-button","bb-base-button--disabled"]},{"name":"--bg-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--fg-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--border-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--opacity-disabled","documented":true,"readBy":["bb-button","bb-base-button--disabled","bb-button--loading"]},{"name":"--elev-tier","documented":true,"readBy":["bb-button--primary","bb-button--secondary","bb-button--outline","bb-button--destructive"]},{"name":"--elev","documented":true,"readBy":["bb-button"]}]},{"name":"BbDropdownList","kind":"internal","repeated":true}]},{"name":"DropdownPipelineResolver","kind":"internal"}]}]},"css":{"classes":{"bb-dropdown-button":["","--block","__chevron","__main","__toggle"]},"locals":["--color-gray-200"]},"attrTargets":["span"],"removed":[{"name":"arrowPadding","new":null,"since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"remove"}},{"name":"autoLoading","new":"disableAutoLoading","since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"invert"}},{"name":"theme","new":"variant","since":"3.0.0-beta.15","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"rename"}},{"name":"right:icon","new":"toggle:icon","since":"3.0.0-beta.35","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"rename"}},{"name":"no-data","new":"empty","surface":"slot","since":"3.0.0-beta.41","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"rename"}},{"name":"items[].key","new":"items[].value","surface":"item","hint":"a row's identity: its `v-model` value (any type, compared by hash) and its `#<value>` slots; `check --fix` renames it","since":"3.0.0-beta.49","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"rename"}},{"name":"items[].text","new":"items[].label","surface":"item","hint":"a row's text; `check --fix` renames it","since":"3.0.0-beta.49","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"rename"}},{"name":"items[group].key","new":"items[group].id","surface":"item","hint":"a group's identity, the same word as `<BbDropdownGroup id>`; `check --fix` renames it","since":"3.0.0-beta.49","migration":"ai/guides/migration/components/bb-dropdown-button.md","silent":true,"fix":{"kind":"rename"}}],"renamed":[{"from":"autoLoading","to":"disableAutoLoading","since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-dropdown-button.md","fix":{"kind":"invert"}},{"from":"theme","to":"variant","since":"3.0.0-beta.15","migration":"ai/guides/migration/components/bb-dropdown-button.md","fix":{"kind":"rename"}},{"from":"right:icon","to":"toggle:icon","since":"3.0.0-beta.35","migration":"ai/guides/migration/components/bb-dropdown-button.md","fix":{"kind":"rename"}},{"from":"no-data","to":"empty","surface":"slot","since":"3.0.0-beta.41","migration":"ai/guides/migration/v2-to-v3.md","fix":{"kind":"rename"}}]},
492
492
  {"name":"BbDropdownGroup","group":"Overlay","description":"Renderless v-model companion for a group declared in BbDropdown.items, bound by id.","referenceOnly":false,"hasGuide":false,"contract":"ai/BbDropdownGroup.md","source":"ai/source/BbDropdownGroup.md","props":[{"name":"id","type":"string","required":true,"description":"Identifier of the group to bind to — must match the `id` of a group\ndeclared in the host `BbDropdown`'s `items`."},{"name":"modelValue","type":"any","required":false,"description":"The group's selection: one value for a single group, an array for a\n`multiple` one. Read by the owning BbDropdown through the group's `id`."}],"events":[{"name":"item:selected","signature":"(value: any): void","description":"The user picked a row, at the click, a pick you then refuse included —\nwith the row's value (its `item-value` for pipeline groups, else its\n`value`), symmetric with `BbTable`. Your own `v-model` changes fire none."},{"name":"item:unselected","signature":"(value: any): void","description":"The user unpicked a row (or a single pick replaced it), with its resolved value."},{"name":"update:modelValue","signature":"(value: any): void","description":"The value a user change asks for (a pick, a chip ×, a coherence prune).\nWith a listener the group is controlled: the rows tick when you write the\nvalue, and ignoring it leaves them as they are."}],"slots":[{"name":"default","type":"{}"}],"exposed":[],"models":[{"name":"modelValue","type":"any"}]},
493
493
  {"name":"BbDropzone","group":"Inputs","description":"Supports drag-and-drop file input flows.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbDropzone.md","source":"ai/source/BbDropzone.md","props":[{"name":"accept","type":"string[] | undefined","required":false,"description":"Array of MIME types (`'image/png'`), wildcards (`'image/*'`), extensions\n(`'.png'`) or bare tokens (`'png'`) to filter selectable files. A bare\ntoken upgrades to its extension instantly and is enriched with its MIME\ntype from the lazily-imported `mime` database; validation of files that\narrive before the database resolves is queued until the rules are\ncomplete."},{"name":"autofocus","type":"Booleanish | undefined","required":false,"description":"Sets autofocus on page load."},{"name":"disabled","type":"boolean | undefined","required":false,"default":"false","description":"Disables the component"},{"name":"errors","type":"string | string[] | undefined","required":false,"description":"Can be a string or an array of string containing the messages to display.\nThey render in an `aria-live=\"polite\"` region (announced when they appear)\nand, while the list is non-empty, are referenced FIRST from the control's\n`aria-describedby`, so the reason the field is invalid is re-read whenever\nthe control regains focus."},{"name":"formless","type":"boolean | undefined","required":false,"default":"false","description":"Keep this control out of the enclosing `BbForm` entirely: not submitted,\nnot dirty, not reset, not validated. For a control that is not form data\n(a filter, a \"show advanced\" switch)."},{"name":"hasErrors","type":"boolean | undefined","required":false,"default":"false","description":"Define if the component should be in an error state.\nIt usually attaches a CSS class for styling purposes."},{"name":"hasWarnings","type":"boolean | undefined","required":false,"default":"false","description":"Define if the component should be in a warning state.\nSame chrome as errors with `--bb-warn`; errors take priority."},{"name":"id","type":"string | undefined","required":false,"description":"The `id` of the native control; the label's `for` and the description\nand message ids derive from it. Generated when omitted."},{"name":"maxFiles","type":"number | undefined","required":false,"description":"Maximum number of acceptable files."},{"name":"maxSize","type":"number | undefined","required":false,"description":"Max size of the files in bytes."},{"name":"modelValue","type":"File | File[] | null | undefined","required":false,"description":"Used by v-model."},{"name":"multiple","type":"boolean | undefined","required":false,"default":"false","description":"Allows the selection of multiple items."},{"name":"name","type":"string | undefined","required":false,"description":"Defines the name of the input."},{"name":"pt","type":"PtMap<BbDropzonePtPart, BbDropzonePtState> | undefined","required":false,"description":"Passthrough, object form: the same keys as the `pt:*` attributes\nwithout the prefix. See the `pt:<part>` row."},{"name":"pt:content","type":"PtValue | undefined","required":false},{"name":"pt:content:active","type":"PtValue | undefined","required":false},{"name":"pt:content:disabled","type":"PtValue | undefined","required":false},{"name":"pt:content:errors","type":"PtValue | undefined","required":false},{"name":"pt:content:readonly","type":"PtValue | undefined","required":false},{"name":"pt:content:warnings","type":"PtValue | undefined","required":false},{"name":"pt:message","type":"PtValue | undefined","required":false},{"name":"pt:message:active","type":"PtValue | undefined","required":false},{"name":"pt:message:disabled","type":"PtValue | undefined","required":false},{"name":"pt:message:errors","type":"PtValue | undefined","required":false},{"name":"pt:message:readonly","type":"PtValue | undefined","required":false},{"name":"pt:message:warnings","type":"PtValue | undefined","required":false},{"name":"pt:root","type":"PtValue | undefined","required":false},{"name":"pt:root:active","type":"PtValue | undefined","required":false},{"name":"pt:root:disabled","type":"PtValue | undefined","required":false},{"name":"pt:root:errors","type":"PtValue | undefined","required":false},{"name":"pt:root:readonly","type":"PtValue | undefined","required":false},{"name":"pt:root:warnings","type":"PtValue | undefined","required":false},{"name":"readonly","type":"boolean | undefined","required":false,"default":"false","description":"Shows the files without letting the user change them: drop, the file\npicker and the `open()` slot prop do nothing, while the input stays\nfocusable and a `name`d zone still submits its files (unlike `disabled`).\nYour slot renders the list, so hide your remove buttons with the\n`readonly` slot prop."},{"name":"rules","type":"RuleExpression<File | File[] | null>","required":false,"description":"vee-validate rules for this field. Only inside a `BbForm`."},{"name":"tag","type":"LayoutTag | undefined","required":false,"default":"\"div\"","values":["div","span"],"description":"Element rendered as the component's wrapper."},{"name":"validationLabel","type":"string | undefined","required":false,"description":"The field's name in validation messages (`{field}`): \"Email is not\nvalid\". Defaults to the `label` (or `legend`), then \"This field\"; the\nfield's `name` never shows. Set it when the label is not a good noun for\na sentence (an icon-only label, a question) or is hidden. Only inside a\n`BbForm`."},{"name":"validationMode","type":"ValidationMode | undefined","required":false,"values":["aggressive","balanced","lazy","submit-only","submit-live"],"description":"When this field checks itself while the user works: `aggressive` (every\nchange), `balanced` (on leave, then every change while it shows an\nerror), `lazy` (on leave only), `submit-only` (on submit only) or\n`submit-live` (on submit, then every change while it shows an error).\n\"Leave\" is blur for a text-like control, and focus leaving the whole\ncontrol for a picker or a group; a checkbox or switch commits on click.\nUnset, the enclosing `BbForm`'s `validation-mode` applies, then the app's\n`formControls.validationMode` (`'balanced'`). Only inside a `BbForm`."},{"name":"warnings","type":"string | string[] | undefined","required":false,"description":"Warning messages to display beneath the dropzone, in their own `aria-live`\nregion and amber (`--bb-text-warn`). A string or array; a non-empty list\nimplies the warning state. Errors suppress warnings when both are set.\nWhile displayed they are also referenced from the control's\n`aria-describedby`, after any errors and before the description/hint."}],"events":[{"name":"blur","signature":"(event: FocusEvent): void","description":"Emitted when the file input loses focus. Forwards the original DOM `FocusEvent`."},{"name":"error","signature":"(data: DropZoneError): void","description":"Emitted when a file validation error occurs (e.g. size, type, uniqueness, max count)."},{"name":"focus","signature":"(event: FocusEvent): void","description":"Emitted when the file input receives focus. Forwards the original DOM `FocusEvent`."},{"name":"update:modelValue","signature":"(value: File | File[] | null): void","description":"Emitted when the selected files change. Payload is the new file or file array."}],"slots":[{"name":"default","type":"BbDropzoneDefaultSlotProps","fields":["dragging: boolean","focused: boolean","id: string","labelId: string","open: () => void","readonly: boolean"],"description":"The drop zone area content. Renders inside the `<label>` that captures drag\nand file input events. It is a region, not a label: its text does not name\nthe input unless you mark one node with the `labelId` slot prop."}],"exposed":[],"models":[{"name":"modelValue","type":"File | File[] | null | undefined"}],"pt":{"parts":[{"name":"root","description":"The zone."},{"name":"content","description":"The drop region: the label that wraps the file input and your slot."},{"name":"message","description":"The errors / warnings region that holds a message — one part, on whichever is showing (errors win); an empty region never gets it."}],"states":[{"name":"readonly","description":"Component-wide: the zone is `readonly` — the files stay shown and submitted, the input stays focusable, nothing can be added."},{"name":"disabled","description":"Component-wide: the zone is `disabled`."},{"name":"warnings","description":"Component-wide: the zone shows warnings and no errors."},{"name":"errors","description":"Component-wide: the zone shows errors."},{"name":"active","description":"Component-wide: a drag is over the zone."}]},"tree":{"bindings":[{"part":"content","target":"label.bb-dropzone__content"},{"part":"message","target":"span.bb-dropzone__message"}],"nodes":[{"name":"BbSmoothHeight","kind":"public"}]},"css":{"classes":{"bb-dropzone":["","--disabled","--dragging","--errors","--focused","--readonly","--warnings","__content","__error","__errors","__message","__submittable-input","__submittable-label","__warning","__warnings"]},"locals":["--color-gray-200"]},"removed":[{"name":"hasWarning","new":"hasWarnings","since":"3.0.0-beta.26","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"rename"}},{"name":"vid","new":null,"hint":"a display name (v2: `vid=\"Email address\"`) is now `validation-label`; a field path (3.0 betas: `vid=\"address.city\"`) is now `name` (brackets for a native post: `address[city]`). `check --fix` picks by the value","since":"3.0.0-beta.41","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"byValue","display":"validationLabel","key":"name"}},{"name":"validateOn","new":"validationMode","hint":"an array of events became one mode (`aggressive` | `balanced` | `lazy` | `submit-only` | `submit-live`). The old defaults (`['blur']`, `['inactive']`, a toggle's `['update:modelValue', 'change', 'blur']`) are `balanced`, the new default: delete the prop. A text field on `['update:modelValue']` is `validation-mode=\"aggressive\"`. Set it once on `<BbForm validation-mode>` instead of per field","since":"3.0.0-beta.41","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"manual"}}],"renamed":[{"from":"hasWarning","to":"hasWarnings","since":"3.0.0-beta.26","migration":"ai/guides/migration/v2-to-v3.md","fix":{"kind":"rename"}},{"from":"validateOn","to":"validationMode","since":"3.0.0-beta.41","migration":"ai/guides/migration/v2-to-v3.md","fix":{"kind":"manual"}}]},
494
- {"name":"BbForm","group":"Inputs","description":"Form host for the form controls inside it: owns the vee-validate form (useForm), validates every field on submit, and emits `submit` with the collected values only when the form is valid. `schema` takes any Standard Schema (Zod, Valibot, ArkType, Yup) as it is; a failed submit focuses the first invalid field on screen and emits `invalid`. Server errors (a 422): `setErrors(errors)` from the slot or `useBbFormContext(id)` puts each on its field.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbForm.md","source":"ai/source/BbForm.md","props":[{"name":"disableSubmitLock","type":"boolean | undefined","required":false,"default":"false","description":"Keep the fields editable while a save runs. By default, while a\n`@submit` that returned a promise is pending, every field joined to the\nform is read-only (never disabled: values stay readable and focusable)\nand every `type=\"submit\"` BbButton inside the form shows its spinner;\nboth end when the promise settles. A `@submit` that returns nothing\nnever locks."},{"name":"fieldErrors","type":"Record<string, unknown> | null | undefined","required":false,"description":"A server's validation errors, shown on the fields they name, for as long\nas they are in the object: `form.errors` from Inertia's `useForm()`,\n`$page.props.errors`, a Laravel 422 body you kept in a `ref`. Keys are\nfield paths (`email`, `address.city`, `address[city]`, `lines.0.qty`);\na value is a message or a list of them; a nested object reads as dot\npaths. `null`, `undefined` and anything that is not a message show\nnothing. A key no field on the form has is in `unmatchedErrors`.\n\nThe page owns them: editing the field does not hide its message, the\nnext object you pass does. They never block a submit, and a field's own\nrule errors show first. When new messages arrive after a submit, the\nfirst field that has one takes focus. For a one-off message the form\ndrops once the user edits the field, call `setErrors` instead."},{"name":"id","type":"string | undefined","required":false,"description":"Explicit id, so anything outside the form can read and drive it through\n`useBbFormContext(id)` — a dialog footer's submit button, a page toolbar,\na leave guard. It is also the `<form>`'s own id, so a native\n`<button form=\"id\" type=\"submit\">` outside it submits it. Omit it when the form is self-contained: the slot scope\nalready carries the same state for chrome that lives inside."},{"name":"keepValuesOnUnmount","type":"boolean | Ref<boolean, boolean> | undefined","required":false,"description":"Keep a field's value (and its place in `@submit`) when it unmounts —\nthe `v-if` wizard step you left, a collapsed section. Off, an unmounted\nfield drops out of the values."},{"name":"model","type":"Record<string, any> | undefined","required":false,"description":"The object the fields edit: any reactive object — `reactive()`, `ref`,\nPinia state, Inertia `useForm()` — written in place, never replaced;\nwatch it deep. Bound once, one way (`:model=\"draft\"`, not `v-model`):\nthe form never hands back a new object, so there is nothing to assign.\nA field without its own `v-model` starts from the value at its path (its\n`name`, else its slugged label), writes every change back there (nested\npaths created: `lines[0][qty]` → `lines[0].qty`), and follows the page's\nown writes (`draft.email = 'x'`, `form.reset()`). Keys no field owns are\nnever touched. Nothing is written on mount. A field's own `v-model` wins\nover it."},{"name":"schema","type":"MaybeRef<S | undefined>","required":false,"description":"One schema for the whole form: any Standard Schema — Zod (3.24+, 4),\nValibot, ArkType, Yup, Joi, Effect — passed as it is, no adapter (the\nvee-validate option underneath is `validationSchema`). Errors land on the field\nwhose `name` is the issue path (`address.city` or `address[city]`,\n`items.0.qty`), and `@submit` receives the schema's output (transforms\napplied) typed from it (Joi has no static types: there it is `any`), and\nthe required asterisk comes from the schema (every library above). A vee-validate `toTypedSchema(...)` or rules\nobject works too.\n\nReactive: pass a `computed` to validate a wizard one step at a time. It\nmust be set when the form mounts; it can change, not appear later. While\na schema is set, per-field `rules` are ignored (vee-validate's design)."},{"name":"validationMode","type":"ValidationMode | undefined","required":false,"description":"When the fields inside check themselves while the user works:\n`aggressive`, `balanced`, `lazy`, `submit-only` or `submit-live` (see\n`ValidationMode`). A field's own `validation-mode` wins. Unset, the app's\n`formControls.validationMode` applies (`'balanced'` by default)."}],"events":[{"name":"invalid","signature":"(payload: BbFormInvalidPayload): void","description":"Fired when a submit fails validation, after the form has focused the\nfirst invalid field that is on screen. `errors` also holds paths with no\nfield showing, so a wizard can jump to the step that has one."},{"name":"submit","signature":"(values: BbFormSubmitValues<S>): void","description":"Fired only when every field passes. Carries the values — with a schema,\nits parsed OUTPUT (transforms and coercions applied), typed from it."}],"slots":[{"name":"default","type":"{ touched: boolean; dirty: boolean; valid: boolean; pending: boolean; initialValues?: Partial<GenericObject> | undefined; errors: Record<string, string[]>; fields: Record<string, BbFormFieldEntry>; isSubmitting: boolean; removeField: (path: string) => void; resetForm: (state?: Partial<FormState<GenericObject>> | undefined, opts?: Partial<ResetFormOpts> | undefined) => void; setErrors: (serverErrors: BbFormServerErrors, options?: BbFormSetErrorsOptions) => void; snapshot: (next?: Record<string, unknown> | undefined) => void; submitCount: number; unmatchedErrors: Record<string, string[]>; validate: (opts?: Partial<ValidationOptions> | undefined) => Promise<FormValidationResult<GenericObject, GenericObject>>; values: GenericObject; }"}],"exposed":[{"name":"$el","type":"HTMLFormElement | null","description":"The rendered element."}],"models":[],"attrTargets":["form"],"removed":[{"name":"modelValue","new":"model","since":"3.0.0-beta.42","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"rename"}}],"renamed":[{"from":"modelValue","to":"model","since":"3.0.0-beta.42","migration":"ai/guides/migration/v2-to-v3.md","fix":{"kind":"rename"}}]},
494
+ {"name":"BbForm","group":"Inputs","description":"Form host for the form controls inside it: owns the vee-validate form (useForm), validates every field on submit, and emits `submit` with the collected values only when the form is valid. `schema` takes any Standard Schema (Zod, Valibot, ArkType, Yup) as it is; a failed submit focuses the first invalid field on screen and emits `invalid`. Server errors (a 422): `setErrors(errors)` from the slot or `useBbFormContext(id)` puts each on its field.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbForm.md","source":"ai/source/BbForm.md","props":[{"name":"disableSubmitLock","type":"boolean | undefined","required":false,"default":"false","description":"Keep the fields editable while a save runs. By default, while a\n`@submit` that returned a promise is pending, every field joined to the\nform is read-only (never disabled: values stay readable and focusable);\nit ends when the promise settles. A `@submit` that returns nothing never\nlocks. The `type=\"submit\"` BbButtons inside the form show their spinner\nwhile the save is pending either way: this prop does not stop that."},{"name":"fieldErrors","type":"Record<string, unknown> | null | undefined","required":false,"description":"A server's validation errors, shown on the fields they name, for as long\nas they are in the object: `form.errors` from Inertia's `useForm()`,\n`$page.props.errors`, a Laravel 422 body you kept in a `ref`. Keys are\nfield paths (`email`, `address.city`, `address[city]`, `lines.0.qty`);\na value is a message or a list of them; a nested object reads as dot\npaths. `null`, `undefined` and anything that is not a message show\nnothing. A key no field on the form has is in `unmatchedErrors`.\n\nThe page owns them: editing the field does not hide its message, the\nnext object you pass does. They never block a submit, and a field's own\nrule errors show first. When new messages arrive after a submit, the\nfirst field that has one takes focus. For a one-off message the form\ndrops once the user edits the field, call `setErrors` instead."},{"name":"id","type":"string | undefined","required":false,"description":"Explicit id, so anything outside the form can read and drive it through\n`useBbFormContext(id)` — a dialog footer's submit button, a page toolbar,\na leave guard. It is also the `<form>`'s own id, so a native\n`<button form=\"id\" type=\"submit\">` outside it submits it. Omit it when the form is self-contained: the slot scope\nalready carries the same state for chrome that lives inside."},{"name":"keepValuesOnUnmount","type":"boolean | Ref<boolean, boolean> | undefined","required":false,"description":"Keep a field's value (and its place in `@submit`) when it unmounts —\nthe `v-if` wizard step you left, a collapsed section. Off, an unmounted\nfield drops out of the values."},{"name":"model","type":"Record<string, any> | undefined","required":false,"description":"The object the fields edit: any reactive object — `reactive()`, `ref`,\nPinia state, Inertia `useForm()` — written in place, never replaced;\nwatch it deep. Bound once, one way (`:model=\"draft\"`, not `v-model`):\nthe form never hands back a new object, so there is nothing to assign.\nA field without its own `v-model` starts from the value at its path (its\n`name`, else its slugged label), writes every change back there (nested\npaths created: `lines[0][qty]` → `lines[0].qty`), and follows the page's\nown writes (`draft.email = 'x'`, `form.reset()`). Keys no field owns are\nnever touched. Nothing is written on mount. A field's own `v-model` wins\nover it."},{"name":"schema","type":"MaybeRef<S | undefined>","required":false,"description":"One schema for the whole form: any Standard Schema — Zod (3.24+, 4),\nValibot, ArkType, Yup, Joi, Effect — passed as it is, no adapter (the\nvee-validate option underneath is `validationSchema`). Errors land on the field\nwhose `name` is the issue path (`address.city` or `address[city]`,\n`items.0.qty`), and `@submit` receives the schema's output (transforms\napplied) typed from it (Joi has no static types: there it is `any`), and\nthe required asterisk comes from the schema (every library above). A vee-validate `toTypedSchema(...)` or rules\nobject works too.\n\nReactive: pass a `computed` to validate a wizard one step at a time. It\nmust be set when the form mounts; it can change, not appear later. While\na schema is set, per-field `rules` are ignored (vee-validate's design)."},{"name":"validationMode","type":"ValidationMode | undefined","required":false,"description":"When the fields inside check themselves while the user works:\n`aggressive`, `balanced`, `lazy`, `submit-only` or `submit-live` (see\n`ValidationMode`). A field's own `validation-mode` wins. Unset, the app's\n`formControls.validationMode` applies (`'balanced'` by default)."}],"events":[{"name":"invalid","signature":"(payload: BbFormInvalidPayload): void","description":"Fired when a submit fails validation, after the form has focused the\nfirst invalid field that is on screen. `errors` also holds paths with no\nfield showing, so a wizard can jump to the step that has one."},{"name":"submit","signature":"(values: BbFormSubmitValues<S>): void","description":"Fired only when every field passes. Carries the values — with a schema,\nits parsed OUTPUT (transforms and coercions applied), typed from it."}],"slots":[{"name":"default","type":"{ touched: boolean; dirty: boolean; valid: boolean; pending: boolean; initialValues?: Partial<GenericObject> | undefined; errors: Record<string, string[]>; fields: Record<string, BbFormFieldEntry>; isSubmitting: boolean; removeField: (path: string) => void; resetForm: (state?: Partial<FormState<GenericObject>> | undefined, opts?: Partial<ResetFormOpts> | undefined) => void; setErrors: (serverErrors: BbFormServerErrors, options?: BbFormSetErrorsOptions) => void; snapshot: (next?: Record<string, unknown> | undefined) => void; submitCount: number; unmatchedErrors: Record<string, string[]>; validate: (opts?: Partial<ValidationOptions> | undefined) => Promise<FormValidationResult<GenericObject, GenericObject>>; values: GenericObject; }"}],"exposed":[{"name":"$el","type":"HTMLFormElement | null","description":"The rendered element."}],"models":[],"attrTargets":["form"],"removed":[{"name":"modelValue","new":"model","since":"3.0.0-beta.42","migration":"ai/guides/migration/v2-to-v3.md","silent":true,"fix":{"kind":"rename"}}],"renamed":[{"from":"modelValue","to":"model","since":"3.0.0-beta.42","migration":"ai/guides/migration/v2-to-v3.md","fix":{"kind":"rename"}}]},
495
495
  {"name":"BbHoverCard","group":"Overlay","description":"A rich preview card that opens on hover or keyboard focus (a person, an event, a link) — BbPopover's panel without the click; may hold commands that also exist elsewhere; also the v-bb-hover-card directive.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbHoverCard.md","source":"ai/source/BbHoverCard.md","props":[{"name":"activator","type":"ElementOrComponentRef | undefined","required":false,"description":"External activator element or component ref.\nWhen provided, the activator slot is not rendered and the hover / focus\nlisteners are attached programmatically to the referenced element."},{"name":"boundary","type":"ElementOrComponentRefOrSelector | undefined","required":false,"description":"Constrains the card within a bounding element.\nWhen provided, the card will not overflow this container's bounds (e.g. a sidebar).\nAccepts an HTMLElement, a Vue component ref, or a CSS selector string."},{"name":"delay","type":"number | undefined","required":false,"default":"350","description":"The delay in milliseconds before the card is shown: how long the pointer\nrests on the activator, or keyboard focus stays on it. BbTooltip's name;\na different default, because a card that opened on every pass of the\npointer would be in the way. Closing has a fixed grace instead (see the\nguide § Timing and the safe zone)."},{"name":"disabled","type":"boolean | undefined","required":false,"default":"false","description":"Disables the component. Disabling an open card closes it."},{"name":"eager","type":"boolean | undefined","required":false,"default":"false","description":"Renders the card's content at mount instead of on the first open."},{"name":"id","type":"string | undefined","required":false,"description":"The activator's id (the card is `bbhc_<id>`, and is labelled by it once it\nholds a control). Generated when omitted."},{"name":"modelValue","type":"boolean | undefined","required":false,"default":"false","description":"Used by v-model to open / close the card. A card with no `v-model` starts\nclosed and is driven by hover and keyboard focus."},{"name":"offset","type":"number | undefined","required":false,"default":"4","description":"Space the floating container will maintain from the activator."},{"name":"padding","type":"number | undefined","required":false,"default":"10","description":"Minimum empty space to keep from the edge of the page."},{"name":"placement","type":"Placement | undefined","required":false,"default":"\"bottom\"","values":["top","bottom","left","right","top-start","top-end","right-start","right-end","bottom-start","bottom-end","left-start","left-end"],"description":"Position of the card relative to the activator."},{"name":"pt","type":"PtMap<BbHoverCardPtPart, \"open\"> | undefined","required":false,"description":"Passthrough, object form: the same keys as the `pt:*` attributes\nwithout the prefix. See the `pt:<part>` row."},{"name":"pt:content","type":"PtValue | undefined","required":false},{"name":"pt:content:open","type":"PtValue | undefined","required":false},{"name":"pt:footer","type":"PtValue | undefined","required":false},{"name":"pt:footer:open","type":"PtValue | undefined","required":false},{"name":"pt:header","type":"PtValue | undefined","required":false},{"name":"pt:header:open","type":"PtValue | undefined","required":false},{"name":"pt:panel","type":"PtValue | undefined","required":false},{"name":"pt:panel:open","type":"PtValue | undefined","required":false},{"name":"pt:root","type":"PtValue | undefined","required":false},{"name":"pt:root:open","type":"PtValue | undefined","required":false},{"name":"scrollable","type":"boolean | undefined","required":false,"default":"false","description":"Caps the card height to the available viewport space and enables internal scrolling."},{"name":"transitionDuration","type":"number | undefined","required":false,"default":"250","description":"How long the transition has to last in milliseconds."},{"name":"width","type":"string | number | undefined","required":false,"description":"Width of the card. Accepts pixels or percentage (relative to activator width)."}],"events":[{"name":"hidden","signature":"(): void","description":"Emitted after the card is fully closed."},{"name":"hide","signature":"(): void","description":"Emitted as soon as the card starts to close (model becomes false)."},{"name":"show","signature":"(): void","description":"Emitted as soon as the card starts to open (model becomes true)."},{"name":"shown","signature":"(): void","description":"Emitted after the card is fully opened. Focus never moves on open."},{"name":"update:modelValue","signature":"(value: boolean): void","description":"Emitted with the new value whenever `v-model` changes."}],"slots":[{"name":"activator","type":"BbHoverCardActivatorSlotProps","fields":["disabled: boolean","open: boolean","placement: Placement","props: { 'aria-expanded'?: boolean | undefined; 'aria-controls'?: string | undefined; id: string; ref: (reference: unknown) => void; onPointerenter: (event: PointerEvent) => void; onPointerleave: (event: PointerEvent) => void; onFocusin: (event: FocusEvent) => void; onFocusout: (event: FocusEvent) => void; onKeydown: (event: KeyboardEvent) => void; onPointerdown: (event: PointerEvent) => void; onContextmenu: (event: MouseEvent) => void; }"],"description":"The element the card describes. Spread `props` onto it; it must be\nfocusable (a link or a button) so a keyboard user can open the card too."},{"name":"default","type":"BbHoverCardDefaultSlotProps","fields":["close: () => void","disabled: boolean","open: boolean","placement: Placement"],"description":"The card body."},{"name":"footer","type":"BbHoverCardDefaultSlotProps","fields":["close: () => void","disabled: boolean","open: boolean","placement: Placement"],"description":"Content pinned to the bottom of the card, outside the scroll region."},{"name":"header","type":"BbHoverCardDefaultSlotProps","fields":["close: () => void","disabled: boolean","open: boolean","placement: Placement"],"description":"Content pinned to the top of the card, outside the scroll region."}],"exposed":[],"models":[{"name":"modelValue","type":"boolean | undefined"}],"pt":{"parts":[{"name":"root","description":"The floating element — an invisible positioning wrapper; a `class` on the component lands here, and so does `role=\"group\"` once the card holds a control."},{"name":"panel","description":"The surface you see — the bordered box that paints the card and lays out its bands."},{"name":"header","description":"The header band."},{"name":"content","description":"The body band."},{"name":"footer","description":"The footer band."}],"states":[{"name":"open","description":"Component-wide: the card is open."}]},"tree":{"bindings":[{"part":"panel","target":"CommonPopover"},{"part":"root","target":"CommonPopover"},{"part":"header","target":"span.bb-hover-card__header"},{"part":"content","target":"span.bb-hover-card__content"},{"part":"footer","target":"span.bb-hover-card__footer"}],"nodes":[{"name":"CommonPopover","kind":"internal"}]},"css":{"classes":{"bb-hover-card":["","__content","__footer","__header","__panel","__panel--scrollable"]},"locals":["--color-gray-200","--max-h"]},"attrTargets":["root"]},
496
496
  {"name":"BbHoverCardPortal","group":"Overlay","description":"Registers named slot content that `v-bb-hover-card=\"'name'\"` (or `{ portal: 'name' }`) renders in the card; slot `{ props, close }`.","referenceOnly":false,"hasGuide":false,"contract":"ai/BbHoverCardPortal.md","source":"ai/source/BbHoverCardPortal.md","props":[{"name":"name","type":"string","required":true,"description":"The name a `v-bb-hover-card` passes as its value (`\"'name'\"`) or as `portal: 'name'`."}],"events":[],"slots":[{"name":"default","type":"BbHoverCardPortalSlotProps","fields":["props: P","close: () => void"],"description":"The card's body. `props` is the directive's `portalProps`; `close` closes the card."}],"exposed":[],"models":[]},
497
497
  {"name":"BbIcon","group":"Data Display","description":"Renders an icon either from the local icon folder or from a provider.","referenceOnly":false,"hasGuide":true,"contract":"ai/BbIcon.md","source":"ai/source/BbIcon.md","props":[{"name":"icon","type":"string","required":true,"description":"Name of a registered icon to use, or a full raw `<svg>` string\n(e.g. an SVG file imported with `?raw`)."},{"name":"label","type":"string | undefined","required":false,"description":"Label of the icon for accessibility."},{"name":"pt","type":"PtMap<\"root\", never> | undefined","required":false,"description":"Passthrough, object form: `{ root: '…' }`. See the `pt:<part>` row."},{"name":"pt:root","type":"PtValue | undefined","required":false},{"name":"size","type":"number | CssSize | keyof Sizes | undefined","required":false,"default":"\"md\"","description":"Defines the component size.\n\nAccepted values:\n- A key from the provided size map (`keyof T`), e.g. `'sm' | 'md' | 'lg'`\n- A custom CSS size string (e.g. `'20px'`, `'1.5rem'`, `'2em'`)\n- A number, typically interpreted as pixels by size parsers"}],"events":[],"slots":[],"exposed":[],"models":[],"cssProps":[{"name":"--w","value":"initial","selector":".bb-icon","description":"`--w` is set inline by BbIcon from the size prop. The `initial` default seals it: without it an ancestor's `--w` (e.g. a dropdown's width bridge) would inherit in and size the icon. The `--size` indirection stays so higher-specificity rules (e.g. `.bb-badge .bb-icon`) can re-point it."},{"name":"--size","value":"var(--w)","selector":".bb-icon"}],"pt":{"parts":[{"name":"root","description":"The `<svg>` itself; a `class` on the component lands here too. It is `pointer-events: none`, so a pointer listener here never fires."}],"states":[]},"css":{"classes":{"bb-icon":[""]},"locals":["--color-gray-200","--size","--w"]},"attrTargets":["svg"],"removed":[{"name":"color","new":null,"since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-icon.md","silent":true,"fix":{"kind":"manual"}},{"name":"type","new":"icon","since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-icon.md","silent":false,"fix":{"kind":"rename"}}],"renamed":[{"from":"type","to":"icon","since":"3.0.0-alpha.1","migration":"ai/guides/migration/components/bb-icon.md","fix":{"kind":"rename"}}]},
@@ -958,7 +958,8 @@ Global locale for the library's built-in strings (empty states, close/clear
958
958
  labels, screen-reader announcements, confirm buttons, …) and for the formats
959
959
  around them (dates, numbers, week start). The list is fixed and typed (see
960
960
  [Locales](#locales) below): a language (`it`, `de`, …), a region (`en-GB`,
961
- `de-CH`, `fr-CA`, …), or `pt-BR` / `pt-PT` / `zh-CN` / `zh-TW`. Shipping ≠
961
+ `de-CH`, `fr-CA`, …), or a language with its own dictionaries per
962
+ region: `es-MX`, `pt-BR` / `pt-PT`, `zh-CN` / `zh-TW` / `zh-HK`. Shipping ≠
962
963
  bundling: only the **enabled** locales enter your build (next option). Runtime
963
964
  switching: `useBbConfig().setConfig({ locale })` — to a locale you enabled.
964
965
 
@@ -1816,7 +1816,7 @@ Nothing here fails to compile — these change what existing code does.
1816
1816
  A `:loading="isSubmitting"` on the submit button is now redundant (drop
1817
1817
  it); a submit button outside the form still binds `loading` itself. A
1818
1818
  page that wants the fields editable during the save sets
1819
- `disable-submit-lock` on the form.
1819
+ `disable-submit-lock` on the form (the submit button still spins).
1820
1820
 
1821
1821
  54. **A set of `group` toggle buttons is one Tab stop** (since
1822
1822
  3.0.0-beta.49). Adjacent `group` `BbButton`s that each bind a `v-model`
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": 1,
3
- "version": "3.0.0-beta.49",
3
+ "version": "3.0.0-beta.50",
4
4
  "canon": "bb-canon/1",
5
5
  "folder": "inertia-helpers",
6
6
  "helpers": {
@@ -7,7 +7,7 @@
7
7
  "contract": "ai/BbForm.md",
8
8
  "source": "ai/source/BbForm.md",
9
9
  "props": [
10
- {"name":"disableSubmitLock","type":"boolean | undefined","default":"false","description":"Keep the fields editable while a save runs. By default, while a\n`@submit` that returned a promise is pending, every field joined to the\nform is read-only (never disabled: values stay readable and focusable)\nand every `type=\"submit\"` BbButton inside the form shows its spinner;\nboth end when the promise settles. A `@submit` that returns nothing\nnever locks."},
10
+ {"name":"disableSubmitLock","type":"boolean | undefined","default":"false","description":"Keep the fields editable while a save runs. By default, while a\n`@submit` that returned a promise is pending, every field joined to the\nform is read-only (never disabled: values stay readable and focusable);\nit ends when the promise settles. A `@submit` that returns nothing never\nlocks. The `type=\"submit\"` BbButtons inside the form show their spinner\nwhile the save is pending either way: this prop does not stop that."},
11
11
  {"name":"fieldErrors","type":"Record<string, unknown> | null | undefined","description":"A server's validation errors, shown on the fields they name, for as long\nas they are in the object: `form.errors` from Inertia's `useForm()`,\n`$page.props.errors`, a Laravel 422 body you kept in a `ref`. Keys are\nfield paths (`email`, `address.city`, `address[city]`, `lines.0.qty`);\na value is a message or a list of them; a nested object reads as dot\npaths. `null`, `undefined` and anything that is not a message show\nnothing. A key no field on the form has is in `unmatchedErrors`.\n\nThe page owns them: editing the field does not hide its message, the\nnext object you pass does. They never block a submit, and a field's own\nrule errors show first. When new messages arrive after a submit, the\nfirst field that has one takes focus. For a one-off message the form\ndrops once the user edits the field, call `setErrors` instead."},
12
12
  {"name":"id","type":"string | undefined","description":"Explicit id, so anything outside the form can read and drive it through\n`useBbFormContext(id)` — a dialog footer's submit button, a page toolbar,\na leave guard. It is also the `<form>`'s own id, so a native\n`<button form=\"id\" type=\"submit\">` outside it submits it. Omit it when the form is self-contained: the slot scope\nalready carries the same state for chrome that lives inside."},
13
13
  {"name":"keepValuesOnUnmount","type":"boolean | Ref<boolean, boolean> | undefined","description":"Keep a field's value (and its place in `@submit`) when it unmounts —\nthe `v-if` wizard step you left, a collapsed section. Off, an unmounted\nfield drops out of the values."},
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "bitboss-ui",
4
- "version": "3.0.0-beta.49",
5
- "generatedAt": "2026-10-07T21:07:12.711Z",
4
+ "version": "3.0.0-beta.50",
5
+ "generatedAt": "2026-10-07T22:12:47.592Z",
6
6
  "about": "The bitboss-ui API for agents: open components/<Name>.json for one component (every prop, event, slot, v-model, pt part and state, removed/renamed name, and the public types its API uses; one member per line). components/<Name>.tree.json has its component tree and CSS internals. The machine file with everything in one place is ../components.json.",
7
7
  "reading": {
8
8
  "components[]": "one row per component; description = its first sentence; file = its API file; tree: true = components/<Name>.tree.json exists (component tree, css classes, CSS custom properties).",
@@ -146,15 +146,15 @@ const forwardedAttrs = computed(() => {
146
146
  const internalLoading = ref(0);
147
147
 
148
148
  // Q60.17: a `type="submit"` button inside a BbForm spins while the form's
149
- // save runs (unless the form sets `disable-submit-lock`). Other buttons in
150
- // the form (Cancel, a row action) are untouched.
149
+ // save runs, `disable-submit-lock` or not (that keeps the fields editable
150
+ // only). Other buttons in the form (Cancel, a row action) are untouched.
151
151
  const formLock = inject(BB_FORM_SUBMIT_LOCK, null);
152
152
 
153
153
  const loading = computed(
154
154
  () =>
155
155
  props.loading ||
156
156
  (!!internalLoading.value && !props.disableAutoLoading) ||
157
- (props.type === 'submit' && !!formLock?.locked())
157
+ (props.type === 'submit' && !!formLock?.saving())
158
158
  );
159
159
 
160
160
  /** Latches on the first load; gates the spinner's <Transition> — see the template. */
@@ -177,10 +177,10 @@ const props = defineProps<{
177
177
  /**
178
178
  * Keep the fields editable while a save runs. By default, while a
179
179
  * `@submit` that returned a promise is pending, every field joined to the
180
- * form is read-only (never disabled: values stay readable and focusable)
181
- * and every `type="submit"` BbButton inside the form shows its spinner;
182
- * both end when the promise settles. A `@submit` that returns nothing
183
- * never locks.
180
+ * form is read-only (never disabled: values stay readable and focusable);
181
+ * it ends when the promise settles. A `@submit` that returns nothing never
182
+ * locks. The `type="submit"` BbButtons inside the form show their spinner
183
+ * while the save is pending either way: this prop does not stop that.
184
184
  */
185
185
  disableSubmitLock?: boolean;
186
186
  }>();
@@ -667,7 +667,10 @@ const instance = getCurrentInstance();
667
667
  /** Q60.17: saves still pending (a second submit can overlap the first). */
668
668
  const savesInFlight = ref(0);
669
669
  const submitLocked = () => savesInFlight.value > 0 && !props.disableSubmitLock;
670
- provide(BB_FORM_SUBMIT_LOCK, { locked: submitLocked });
670
+ provide(BB_FORM_SUBMIT_LOCK, {
671
+ locked: submitLocked,
672
+ saving: () => savesInFlight.value > 0,
673
+ });
671
674
 
672
675
  type SubmitListener = (values: Record<string, unknown>) => unknown;
673
676