bitboss-ui 3.0.0-beta.44 → 3.0.0-beta.46
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/bin/bitboss-ui.mjs +6 -1
- package/dist/ai/BbDialog.md +12 -10
- package/dist/ai/BbDropdownButton.md +4 -0
- package/dist/ai/BbDropzone.md +2 -2
- package/dist/ai/BbForm.md +2 -2
- package/dist/ai/BbOffCanvas.md +3 -3
- package/dist/ai/BbPopover.md +1 -1
- package/dist/ai/BbRadioGroup.md +1 -1
- package/dist/ai/BbRating.md +1 -1
- package/dist/ai/BbSelect.md +2 -1
- package/dist/ai/BbSelectPopover.md +1 -1
- package/dist/ai/BbTable.md +27 -11
- package/dist/ai/BbTabs.md +4 -4
- package/dist/ai/BbTextInput.md +13 -11
- package/dist/ai/BbTextarea.md +8 -7
- package/dist/ai/changelog.json +18 -3
- package/dist/ai/components.json +16 -15
- package/dist/ai/guides/fetch-items-playbook.md +1 -1
- package/dist/ai/guides/installation-and-plugin-setup.md +31 -5
- package/dist/ai/guides/migration/v2-to-v3.md +10 -0
- package/dist/ai/guides/validation-libraries.md +7 -4
- package/dist/ai/manifest/components/BbCheckboxGroup.json +1 -1
- package/dist/ai/manifest/components/BbRadioGroup.json +2 -2
- package/dist/ai/manifest/components/BbSelect.json +1 -1
- package/dist/ai/manifest/components/BbSelectPopover.json +2 -2
- package/dist/ai/manifest/components/BbSwitchGroup.json +1 -1
- package/dist/ai/manifest/components/BbTable.json +1 -1
- package/dist/ai/manifest/components/BbTable.tree.json +11 -1
- package/dist/ai/manifest/components/BbTextInput.json +4 -3
- package/dist/ai/manifest/components/BbTextarea.json +2 -1
- package/dist/ai/manifest/index.json +2 -2
- package/dist/ai/manifest/meta.json +1 -0
- package/dist/ai/manifest/types.advanced.json +4 -5
- package/dist/ai/manifest/types.api.json +5 -3
- package/dist/ai/recipes/inertia/approvals-inbox.md +8 -6
- package/dist/ai/recipes/nuxt/approvals-inbox.md +8 -6
- package/dist/ai/recipes/vue/approvals-inbox.md +8 -6
- package/dist/ai/source/BbDropdownButton.md +14 -2
- package/dist/ai/source/BbSmoothHeight.md +5 -0
- package/dist/ai/source/BbTable.md +70 -8
- package/dist/ai/source/BbTextInput.md +44 -12
- package/dist/ai/source/BbTextarea.md +45 -13
- package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +52 -50
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +739 -737
- package/dist/components/BbTable/utils.js +11 -4
- package/dist/components/BbTextInput/BbTextInput.vue.d.ts +31 -39
- package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +19 -18
- package/dist/components/BbTextInput/types.d.ts +17 -4
- package/dist/components/BbTextarea/BbTextarea.vue.d.ts +31 -37
- package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +40 -39
- package/dist/components/BbTextarea/types.d.ts +17 -4
- package/dist/composables/useSegmentedFields.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/llms-full.txt +154 -83
- package/dist/llms-medium.txt +31 -5
- package/dist/project-dts.d.ts +8 -0
- package/dist/project-dts.js +6 -3
- package/dist/styles.css +2 -2
- package/dist/types/Config.d.ts +26 -8
- package/dist/types/ItemsProvider.d.ts +13 -10
- package/dist/utils/versionCheck.js +1 -1
- package/dist/vite-plugin.d.ts +6 -4
- package/dist/vite.js +182 -181
- package/package.json +1 -1
- package/scripts/lib/eslint-disable.mjs +2 -0
- package/scripts/lib/eslint-plugin.d.ts +2 -0
- package/scripts/lib/eslint-plugin.mjs +117 -0
- package/scripts/lib/text-null-value.mjs +260 -0
- package/scripts/lib/validate-bb-markup.mjs +4 -0
package/bin/bitboss-ui.mjs
CHANGED
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
projectLocalBbComponents,
|
|
77
77
|
} from '../scripts/lib/local-components.mjs';
|
|
78
78
|
import { applyBitbossFixes } from '../scripts/lib/check-fix.mjs';
|
|
79
|
+
import { stringControlsFor } from '../scripts/lib/text-null-value.mjs';
|
|
79
80
|
import { cssClassFindings } from '../scripts/lib/css-class-check.mjs';
|
|
80
81
|
import {
|
|
81
82
|
cssLocalFindings,
|
|
@@ -1044,7 +1045,11 @@ async function checkCommand(
|
|
|
1044
1045
|
const content = readText(file);
|
|
1045
1046
|
const { findings: fileFindings } = file.endsWith('.md')
|
|
1046
1047
|
? validateMarkdown(content, manifest, validateOptions)
|
|
1047
|
-
: validateVueSnippet(content, manifest,
|
|
1048
|
+
: validateVueSnippet(content, manifest, {
|
|
1049
|
+
...validateOptions,
|
|
1050
|
+
// The app's null-value policy, from the project bitboss-ui.d.ts.
|
|
1051
|
+
stringControls: stringControlsFor(file),
|
|
1052
|
+
});
|
|
1048
1053
|
// Q46.9: a finding the author silenced for ESLint (`eslint-disable*`,
|
|
1049
1054
|
// usually with a reason) is silenced here too, or `check` cannot gate
|
|
1050
1055
|
// CI next to a lint step that passes. `.md` lines are fence-relative,
|
package/dist/ai/BbDialog.md
CHANGED
|
@@ -99,7 +99,7 @@ back to `false`, which is the same thing.
|
|
|
99
99
|
<template #footer>
|
|
100
100
|
<div class="flex justify-end gap-2">
|
|
101
101
|
<BbButton variant="ghost" @click="open = false">Cancel</BbButton>
|
|
102
|
-
<BbButton :disabled="!name
|
|
102
|
+
<BbButton :disabled="!name?.trim()" variant="primary" @click="create">
|
|
103
103
|
Create
|
|
104
104
|
</BbButton>
|
|
105
105
|
</div>
|
|
@@ -112,7 +112,7 @@ import { ref } from 'vue';
|
|
|
112
112
|
import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
|
|
113
113
|
|
|
114
114
|
const open = ref(false);
|
|
115
|
-
const name = ref('');
|
|
115
|
+
const name = ref<string | null>('');
|
|
116
116
|
|
|
117
117
|
// Re-seed on `@show`, not on close — a dismissed dialog may be reopened.
|
|
118
118
|
const reset = () => {
|
|
@@ -442,7 +442,7 @@ const roles = [
|
|
|
442
442
|
];
|
|
443
443
|
|
|
444
444
|
const open = ref(false);
|
|
445
|
-
const email = ref('');
|
|
445
|
+
const email = ref<string | null>('');
|
|
446
446
|
const role = ref('member');
|
|
447
447
|
|
|
448
448
|
// Re-seed on `@show`, not on close — a dismissed dialog may be reopened.
|
|
@@ -452,7 +452,9 @@ const reset = () => {
|
|
|
452
452
|
};
|
|
453
453
|
|
|
454
454
|
// Primary action stays disabled until the email is plausible.
|
|
455
|
-
const canSend = computed(() =>
|
|
455
|
+
const canSend = computed(() =>
|
|
456
|
+
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value ?? '')
|
|
457
|
+
);
|
|
456
458
|
|
|
457
459
|
const send = () => {
|
|
458
460
|
// Send the invite here, then close only on success.
|
|
@@ -600,7 +602,7 @@ element.
|
|
|
600
602
|
<div class="flex justify-end gap-2">
|
|
601
603
|
<!-- Keep a visible way out — never trap the user. -->
|
|
602
604
|
<BbButton variant="ghost" @click="open = false">Skip</BbButton>
|
|
603
|
-
<BbButton :disabled="!company
|
|
605
|
+
<BbButton :disabled="!company?.trim()" variant="primary" @click="save">
|
|
604
606
|
Save
|
|
605
607
|
</BbButton>
|
|
606
608
|
</div>
|
|
@@ -613,8 +615,8 @@ import { ref } from 'vue';
|
|
|
613
615
|
import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
|
|
614
616
|
|
|
615
617
|
const open = ref(false);
|
|
616
|
-
const displayName = ref('');
|
|
617
|
-
const company = ref('');
|
|
618
|
+
const displayName = ref<string | null>('');
|
|
619
|
+
const company = ref<string | null>('');
|
|
618
620
|
|
|
619
621
|
// Re-seed on `@show` so a dismissed step reopens clean.
|
|
620
622
|
const reset = () => {
|
|
@@ -676,7 +678,7 @@ automatically; close only once the work resolves:
|
|
|
676
678
|
Cancel
|
|
677
679
|
</BbButton>
|
|
678
680
|
<!-- Returning the promise lets BbButton run its own loading state. -->
|
|
679
|
-
<BbButton :disabled="!draft
|
|
681
|
+
<BbButton :disabled="!draft?.trim()" variant="primary" @click="save">
|
|
680
682
|
Rename
|
|
681
683
|
</BbButton>
|
|
682
684
|
</div>
|
|
@@ -692,7 +694,7 @@ import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
|
|
|
692
694
|
const projectName = ref('acme-storefront');
|
|
693
695
|
|
|
694
696
|
const open = ref(false);
|
|
695
|
-
const draft = ref('');
|
|
697
|
+
const draft = ref<string | null>('');
|
|
696
698
|
const saving = ref(false);
|
|
697
699
|
|
|
698
700
|
// Re-seed on `@show` so a dismissed rename never resurfaces half-typed.
|
|
@@ -706,7 +708,7 @@ const save = async () => {
|
|
|
706
708
|
try {
|
|
707
709
|
// Simulated request — replace with your API call.
|
|
708
710
|
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
709
|
-
projectName.value = draft.value.trim();
|
|
711
|
+
projectName.value = (draft.value ?? '').trim();
|
|
710
712
|
// Work first, then close: the transition runs while your UI refreshes.
|
|
711
713
|
open.value = false;
|
|
712
714
|
} finally {
|
|
@@ -65,6 +65,10 @@ descendants: a group, a nested array or an item with its own `items` (a
|
|
|
65
65
|
submenu) has no single action for the button, so the component throws (a
|
|
66
66
|
`TypeError`) instead of guessing.
|
|
67
67
|
|
|
68
|
+
A computed `items` list that comes down to one entry can stay on this
|
|
69
|
+
component: with nothing after the main action and no default slot, there is no
|
|
70
|
+
menu, so the chevron goes and the control renders exactly like a `BbButton`.
|
|
71
|
+
|
|
68
72
|
**Save / Save as draft**
|
|
69
73
|
|
|
70
74
|
```vue
|
package/dist/ai/BbDropzone.md
CHANGED
|
@@ -1269,7 +1269,7 @@ import { ref } from 'vue';
|
|
|
1269
1269
|
import { BbDropzone, BbButton, BbIcon, BbTextInput } from 'bitboss-ui';
|
|
1270
1270
|
import type { DropZoneError } from 'bitboss-ui';
|
|
1271
1271
|
|
|
1272
|
-
const title = ref('');
|
|
1272
|
+
const title = ref<string | null>('');
|
|
1273
1273
|
const documents = ref<File[]>([]);
|
|
1274
1274
|
const errors = ref<string[]>([]);
|
|
1275
1275
|
const submitted = ref<{ field: string; fileCount: number } | null>(null);
|
|
@@ -1298,7 +1298,7 @@ const remove = (file: File) => {
|
|
|
1298
1298
|
model — assemble a FormData and hand it to your request layer. */
|
|
1299
1299
|
const submit = () => {
|
|
1300
1300
|
const body = new FormData();
|
|
1301
|
-
body.append('title', title.value);
|
|
1301
|
+
body.append('title', title.value ?? '');
|
|
1302
1302
|
documents.value.forEach((file) => body.append('documents[]', file));
|
|
1303
1303
|
submitted.value = { field: 'documents[]', fileCount: documents.value.length };
|
|
1304
1304
|
};
|
package/dist/ai/BbForm.md
CHANGED
|
@@ -310,8 +310,8 @@ Four things to know:
|
|
|
310
310
|
- **An emptied text field holds `null` by default**, not `''`. So
|
|
311
311
|
`z.string().min(1, 'Required')` alone answers an emptied field with the
|
|
312
312
|
library's own "expected string, received null". Two ways out:
|
|
313
|
-
- **Set the plugin option `formControls: { textInputNullValue: '',
|
|
314
|
-
textareaNullValue: '' }`.** Emptied fields then emit `''` and the short
|
|
313
|
+
- **Set the plugin option `formControls: { textInputNullValue: 'string',
|
|
314
|
+
textareaNullValue: 'string' }`.** Emptied fields then emit `''` and the short
|
|
315
315
|
schema shows your message as written. Start text values as `''` too (a
|
|
316
316
|
field nobody touched keeps what your page gave it).
|
|
317
317
|
- **Or keep `null` and put the message on the type as well:**
|
package/dist/ai/BbOffCanvas.md
CHANGED
|
@@ -216,7 +216,7 @@ header and footer stay pinned.
|
|
|
216
216
|
<div class="flex justify-end gap-2">
|
|
217
217
|
<BbButton variant="ghost" @click="open = false">Cancel</BbButton>
|
|
218
218
|
<BbButton
|
|
219
|
-
:disabled="!email
|
|
219
|
+
:disabled="!email?.trim()"
|
|
220
220
|
variant="primary"
|
|
221
221
|
@click="open = false"
|
|
222
222
|
>
|
|
@@ -232,7 +232,7 @@ import { ref } from 'vue';
|
|
|
232
232
|
import { BbButton, BbOffCanvas, BbTextInput } from 'bitboss-ui';
|
|
233
233
|
|
|
234
234
|
const open = ref(false);
|
|
235
|
-
const email = ref('');
|
|
235
|
+
const email = ref<string | null>('');
|
|
236
236
|
</script>
|
|
237
237
|
```
|
|
238
238
|
|
|
@@ -951,7 +951,7 @@ import { ref } from 'vue';
|
|
|
951
951
|
import { BbButton, BbOffCanvas, BbTextInput } from 'bitboss-ui';
|
|
952
952
|
|
|
953
953
|
const open = ref(false);
|
|
954
|
-
const reviewer = ref('');
|
|
954
|
+
const reviewer = ref<string | null>('');
|
|
955
955
|
</script>
|
|
956
956
|
```
|
|
957
957
|
|
package/dist/ai/BbPopover.md
CHANGED
package/dist/ai/BbRadioGroup.md
CHANGED
|
@@ -888,7 +888,7 @@ for a preset that fits any row type (never `<any>`), not an imported
|
|
|
888
888
|
| `itemLayout` | `FieldLayout \| undefined` | `'auto'` | | How each row's input and label sit together — the keywords of `layout`: `'auto'`, `'vertical'` (the input above the label), `'horizontal'` (50/50), `'label-fill'` (the label takes the row's free space — pair it with `list-layout="vertical-stretch"` and `item-reverse` for a settings list), `'input-fill'`, or a two-word ratio pattern like `'xx xxxxx'`, read in on-screen order. |
|
|
889
889
|
| `itemProps` | `BbOptionGroupItemProps<T> \| undefined` | | | ADDITIONAL row fields resolved from each item — never its text or value (those stay on `itemText` / `itemValue`). Option groups accept one field: - `description` — a muted line under the option label, beside the control; it may wrap. Either an object with a path or getter per field (`{ description: 'email' }`) or a function returning the fields (`(item) => ({ description: item.email })`). Without it no field renders — an item's own `description` is never read implicitly — and an empty value renders nothing for that item. The description is announced as the option's description, never part of its name. To disable individual options use `selectable`. |
|
|
890
890
|
| `itemReverse` | `boolean \| undefined` | `false` | | Swaps each row's input and label from their natural order (input first), in every `item-layout`. `reverse` swaps the legend and the options instead. |
|
|
891
|
-
| `items` | `T[] \| ((context:
|
|
891
|
+
| `items` | `T[] \| ((context: BbRadioGroupItemsContext) => T[] \| Promise<T[]>)` | | yes | The options: an array, or a provider function called with ONE context object and returning the rows, sync or async. The context carries: - `reason` — why this call happened: `'initial'` (the first load, on mount), `'dependencies'` (a `dependencies` entry or the `items` function changed; a retry arrives this way) or `'model'` (the model changed from outside and the options cannot resolve it). The group has no search field, so it never passes `query`; - `modelValue` — the `v-model` as-is; - `signal` — an `AbortSignal`, aborted when a newer call starts or the group is disposed; pass it to every request. A provider signals a failure by THROWING, never by returning `[]`: the rows are cleared, nothing is pruned, and `#empty` receives the `error` (`failed-text` is shown). Zero-argument providers keep working. Annotate the parameter `BbRadioGroupItemsContext`. |
|
|
892
892
|
| `itemText` | `ItemAccessor<T, string> \| undefined` | `JSON.stringify(item)` | | Defines a path that returns a property of the object to use as text or a function that returns a string. |
|
|
893
893
|
| `itemValue` | `ItemAccessor<T> \| undefined` | `the whole item` | | Defines a path that returns a property of the object to use as value or a function that returns any value. |
|
|
894
894
|
| `labelAlign` | `'top'` \| `'center'` \| `'bottom'` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
|
package/dist/ai/BbRating.md
CHANGED
|
@@ -273,7 +273,7 @@ import { ref } from 'vue';
|
|
|
273
273
|
import { BbButton, BbRating, BbTextarea } from 'bitboss-ui';
|
|
274
274
|
|
|
275
275
|
const score = ref<number | null>(null);
|
|
276
|
-
const feedback = ref('');
|
|
276
|
+
const feedback = ref<string | null>('');
|
|
277
277
|
const errors = ref<string | undefined>();
|
|
278
278
|
const sent = ref(false);
|
|
279
279
|
|
package/dist/ai/BbSelect.md
CHANGED
|
@@ -765,7 +765,8 @@ a `Promise<T[]>`. Annotate the parameter `BbSelectItemsContext`:
|
|
|
765
765
|
Branch on `reason !== 'search'`, never on one non-search value, so any call
|
|
766
766
|
the select fires lands on the right side.
|
|
767
767
|
- `modelValue` — your v-model as-is (an array in multiple mode, the raw value —
|
|
768
|
-
possibly `null` — otherwise), typed `any
|
|
768
|
+
possibly `null` — otherwise), typed `any` by default. Pass your model type to
|
|
769
|
+
the context type to check it: `BbSelectItemsContext<string[]>`.
|
|
769
770
|
- `signal` — an `AbortSignal`, aborted when a newer call starts or the select
|
|
770
771
|
is disposed. Pass it to every request.
|
|
771
772
|
|
|
@@ -1792,7 +1792,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1792
1792
|
| `id` | `string \| undefined` | | | Seeds the ids the popover sets: its listbox is `<id>_listbox` (the activator's `aria-controls`) and each option's id derives from it. Generated when omitted. |
|
|
1793
1793
|
| `itemHeight` | `number \| undefined` | `28` | | Height of the options in the listbox (px). Defaults to 28px (24px compact; 44px / 36px on mobile viewports). An explicit value wins on every viewport. |
|
|
1794
1794
|
| `itemProps` | `BbSelectItemProps<Item> \| undefined` | | | ADDITIONAL row fields resolved from each item — never its text or value (those stay on `itemText` / `itemValue`). Accepted fields: - `description` — a muted line under the option label. Setting it gives every row a fixed two-line height (the list is virtualized); the description stays on one line and ellipsizes. Announced as the option's description, never part of its name; matched by the search with the text when `filterBy` is empty. - `prepend:icon` — an icon before the label. - `append:icon` — an icon after the label; on the selected row the check takes its place. Either an object with a path or getter per field (`{ description: 'email', 'prepend:icon': 'icon' }`) or a function returning the fields (`(item) => ({ description: item.email })`). Without it no field renders — nothing is read off the item implicitly — and an empty value renders nothing for that item. Never shown in the trigger or the chips. To disable individual options use `selectable`. |
|
|
1795
|
-
| `items` | `Item[] \| ((context:
|
|
1795
|
+
| `items` | `Item[] \| ((context: BbSelectPopoverItemsContext) => Item[] \| Promise<Item[]>)` | `[]` | yes | The options: an array, or a provider function called with ONE context object and returning the rows, sync or async. The context carries: - `query` — what the user typed, `''` until they type; - `reason` — why this call happened: `'search'` (the user typed, and nothing else), `'initial'` (the first load, on mount or on the first open), `'dependencies'` (a `dependencies` entry or the `items` function changed; a retry arrives this way), `'model'` (the model changed from outside and the options cannot resolve it), `'reopen'` (the panel reopened over a searched list). To resolve the selection by id, branch on `reason !== 'search'`; - `modelValue` — the raw v-model: an array in `multiple` mode, the raw model value (possibly `null`) in single mode; - `signal` — an `AbortSignal`, aborted when a newer call starts or the component is disposed; pass it to every request. A provider signals a failure by THROWING, never by returning `[]`: the rows are cleared, nothing is pruned, and `#empty` receives the `error` (`failed-text` is shown). A newer call aborts the one in flight, and the aborted call's rejection or resolution is dropped silently. Zero-argument providers keep working. Annotate the parameter `BbSelectPopoverItemsContext`. |
|
|
1796
1796
|
| `itemText` | `ItemAccessor<Item, string> \| undefined` | `JSON.stringify(item)` | | Path to item property for display text or function to extract it. |
|
|
1797
1797
|
| `itemValue` | `ItemAccessor<Item> \| undefined` | `the whole item` | | Path to item property for value or function to extract it. |
|
|
1798
1798
|
| `loading` | `boolean \| undefined` | `false` | | Display the loading state styles. |
|
package/dist/ai/BbTable.md
CHANGED
|
@@ -323,8 +323,8 @@ reach for it when a single phone cut is all the table needs.
|
|
|
323
323
|
**When a table does scroll, it says so.** The edge with content hidden past
|
|
324
324
|
it fades what sits against it (the root carries `bb-table--overflow-left` /
|
|
325
325
|
`bb-table--overflow-right` while it does), so a reader sees at rest that the
|
|
326
|
-
table goes on. With pinned columns on that side the
|
|
327
|
-
|
|
326
|
+
table goes on. With pinned columns on that side the pinned column casts a
|
|
327
|
+
thin shadow over the content scrolled under it instead. Nothing to set up. Keep
|
|
328
328
|
the sticky/pinned identity column for the case where the full grid really
|
|
329
329
|
must stay reachable (a spreadsheet-shaped tool), not as the default for a
|
|
330
330
|
records list.
|
|
@@ -849,13 +849,13 @@ const catalogue: Invoice[] = [
|
|
|
849
849
|
{ id: '4', number: 'INV-1044', client: 'Umbrella', amount: '€2,750' },
|
|
850
850
|
];
|
|
851
851
|
|
|
852
|
-
const search = ref('');
|
|
852
|
+
const search = ref<string | null>('');
|
|
853
853
|
|
|
854
854
|
// Real apps forward the query to the backend; here we filter a local catalogue
|
|
855
855
|
// after a short simulated round-trip.
|
|
856
856
|
const fetchInvoices = async (): Promise<Invoice[]> => {
|
|
857
857
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
858
|
-
const term = search.value.trim().toLowerCase();
|
|
858
|
+
const term = (search.value ?? '').trim().toLowerCase();
|
|
859
859
|
if (!term) return catalogue;
|
|
860
860
|
return catalogue.filter(
|
|
861
861
|
(invoice) =>
|
|
@@ -3111,13 +3111,25 @@ the one knob and everything pinned follows:
|
|
|
3111
3111
|
}
|
|
3112
3112
|
```
|
|
3113
3113
|
|
|
3114
|
-
**The scroll cue
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
element and takes no click).
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3114
|
+
**The scroll cue.** While content is hidden past an edge the root carries
|
|
3115
|
+
`bb-table--overflow-left` / `bb-table--overflow-right`. On a side with no
|
|
3116
|
+
pinned column a `mask-image` on the root fades that edge (to 30% over 20px,
|
|
3117
|
+
never to nothing; it paints no element and takes no click). Turn it off with
|
|
3118
|
+
`.my-table.bb-table { mask-image: none; }`. A mask clips whatever the root
|
|
3119
|
+
paints outside its own box, so give a scrolling table a `border`, never an
|
|
3120
|
+
outer `box-shadow`. A mask also fades row tints in its band, which is why a
|
|
3121
|
+
side WITH a pinned column (the root carries `bb-table--pinned-left` /
|
|
3122
|
+
`bb-table--pinned-right`) is not masked: its innermost pinned cell casts a
|
|
3123
|
+
strip over the scrolled content instead, so a hovered or highlighted row keeps
|
|
3124
|
+
its tint up to the pinned cell. Size or tint the strip with two knobs on the
|
|
3125
|
+
root (`--cue-w: 0px` turns it off):
|
|
3126
|
+
|
|
3127
|
+
```css
|
|
3128
|
+
.my-table.bb-table {
|
|
3129
|
+
--cue-w: 12px; /* default 8px */
|
|
3130
|
+
--cue: rgb(0 0 0 / 0.15); /* the strip's dark end; it fades to transparent */
|
|
3131
|
+
}
|
|
3132
|
+
```
|
|
3121
3133
|
|
|
3122
3134
|
#### The table as a card
|
|
3123
3135
|
|
|
@@ -3902,6 +3914,10 @@ Set these on the element, or on a class you put on it, to retune this component
|
|
|
3902
3914
|
| `--fade-r` | `0px` | |
|
|
3903
3915
|
| `--fade-a-l` | `1` | |
|
|
3904
3916
|
| `--fade-a-r` | `1` | |
|
|
3917
|
+
| `--cue-l` | `0px` | |
|
|
3918
|
+
| `--cue-r` | `0px` | |
|
|
3919
|
+
| `--cue-w` | `8px` | |
|
|
3920
|
+
| `--cue` | `rgb(0 0 0 / 0.1)` | |
|
|
3905
3921
|
|
|
3906
3922
|
## Component tree
|
|
3907
3923
|
|
package/dist/ai/BbTabs.md
CHANGED
|
@@ -418,7 +418,7 @@ nothing when you flip back:
|
|
|
418
418
|
</template>
|
|
419
419
|
<template #preview>
|
|
420
420
|
<div class="grid gap-1.5 pt-3 text-sm whitespace-pre-line">
|
|
421
|
-
<template v-if="draft
|
|
421
|
+
<template v-if="draft?.trim()">
|
|
422
422
|
<p class="font-medium">{{ previewTitle }}</p>
|
|
423
423
|
<p class="text-(--bb-text-muted)">{{ previewBody }}</p>
|
|
424
424
|
</template>
|
|
@@ -442,17 +442,17 @@ const tabs: Array<BbTabsItem<Mode>> = [
|
|
|
442
442
|
{ key: 'preview', label: 'Preview' },
|
|
443
443
|
];
|
|
444
444
|
|
|
445
|
-
const draft = ref(
|
|
445
|
+
const draft = ref<string | null>(
|
|
446
446
|
'## Billing retries\n\nVerified on staging — invoice totals now match the ledger.'
|
|
447
447
|
);
|
|
448
448
|
|
|
449
449
|
const previewTitle = computed(() => {
|
|
450
|
-
const heading = draft.value.match(/^##\s+(.+)$/m);
|
|
450
|
+
const heading = (draft.value ?? '').match(/^##\s+(.+)$/m);
|
|
451
451
|
return heading?.[1] ?? 'Untitled';
|
|
452
452
|
});
|
|
453
453
|
|
|
454
454
|
const previewBody = computed(() =>
|
|
455
|
-
draft.value.replace(/^##\s+.+$/m, '').trim()
|
|
455
|
+
(draft.value ?? '').replace(/^##\s+.+$/m, '').trim()
|
|
456
456
|
);
|
|
457
457
|
</script>
|
|
458
458
|
```
|
package/dist/ai/BbTextInput.md
CHANGED
|
@@ -62,7 +62,7 @@ Pick a sibling when the shape of the data is different:
|
|
|
62
62
|
- calendar dates → `BbDatePickerInput`;
|
|
63
63
|
- choosing from a known set of options → `BbSelect` / `BbRadioGroup`.
|
|
64
64
|
|
|
65
|
-
`v-model` binds a `string | null` — an emptied field emits `null` by default, or `''` when the plugin option `formControls`
|
|
65
|
+
`v-model` binds a `string | null` — an emptied field emits `null` by default, or `''` when the plugin option `formControls` sets the `'string'` policy (see the installation guide § `formControls`). One field can differ from the app with `null-value="string"` or `null-value="null"`; the `update:modelValue` payload types to match (`string` with `'string'`).
|
|
66
66
|
`label` is required (hide it visually with `hideLabel`, never drop it).
|
|
67
67
|
|
|
68
68
|
### Label & label modes
|
|
@@ -233,7 +233,7 @@ name. The slot alone is enough: no `description` prop needed.
|
|
|
233
233
|
import { ref } from 'vue';
|
|
234
234
|
import { BbTextInput } from 'bitboss-ui';
|
|
235
235
|
|
|
236
|
-
const subdomain = ref('');
|
|
236
|
+
const subdomain = ref<string | null>('');
|
|
237
237
|
</script>
|
|
238
238
|
```
|
|
239
239
|
|
|
@@ -403,8 +403,9 @@ const apiKey = 'sk_live_51Hn3x8Kj2';
|
|
|
403
403
|
### Clearable & loading
|
|
404
404
|
|
|
405
405
|
`clearable` reveals a clear button while the field has a value and is hovered
|
|
406
|
-
or focused; clicking it emits `update:modelValue` with `null`
|
|
407
|
-
input. There is no separate `clear` event — watch
|
|
406
|
+
or focused; clicking it emits `update:modelValue` with the empty value (`null`
|
|
407
|
+
by default) and refocuses the input. There is no separate `clear` event — watch
|
|
408
|
+
the model for the empty value. The
|
|
408
409
|
button is suppressed on `disabled`/`readonly` fields.
|
|
409
410
|
|
|
410
411
|
`loading` shows a spinner in the append position. It is **visual only** — the
|
|
@@ -416,7 +417,7 @@ The classic pairing is a signup handle checker: debounce keystrokes, flip
|
|
|
416
417
|
`loading` while the (simulated) availability call runs, then land the result
|
|
417
418
|
in `errors` or `hint`. Because the field stays editable during the check, the
|
|
418
419
|
handler tags each request and drops stale responses. Watching the model —
|
|
419
|
-
rather than listening to `input` — means the clear button's
|
|
420
|
+
rather than listening to `input` — means the clear button's empty value also resets
|
|
420
421
|
the status.
|
|
421
422
|
|
|
422
423
|
**Async username check**
|
|
@@ -501,7 +502,7 @@ Pass a [maska](https://beholdr.github.io/maska/v3) config to `mask` to format
|
|
|
501
502
|
as the user types — card numbers, phone numbers, license keys. By default
|
|
502
503
|
`v-model` receives the _unmasked_ value (`'4242424242424242'` for the card
|
|
503
504
|
below); set `emitMasked` to persist the formatted string instead. A fully
|
|
504
|
-
emptied masked field emits the empty value (`null` by default, `''` with `
|
|
505
|
+
emptied masked field emits the empty value (`null` by default, `''` with the `'string'` policy), like every other state of this component.
|
|
505
506
|
|
|
506
507
|
The mask is live-reactive: changing the `mask` options at runtime reformats the
|
|
507
508
|
current value, and toggling `emitMasked` re-emits the model in the new format.
|
|
@@ -611,7 +612,7 @@ const revealed = ref(false);
|
|
|
611
612
|
|
|
612
613
|
### Events & v-model
|
|
613
614
|
|
|
614
|
-
- `update:modelValue` — every value change; `string`, or the empty value when emptied (`null`
|
|
615
|
+
- `update:modelValue` — every value change; `string`, or the empty value when emptied (the `null-value` policy, else `formControls`, else `'null'`; `'string'` emits `''`)
|
|
615
616
|
(with a mask: the unmasked/masked string per `emitMasked`).
|
|
616
617
|
- `focus` / `blur` — forwarded `FocusEvent`s; they also drive the hint's
|
|
617
618
|
show-on-focus behavior.
|
|
@@ -1194,7 +1195,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1194
1195
|
|
|
1195
1196
|
### Gotchas & anti-patterns
|
|
1196
1197
|
|
|
1197
|
-
- An emptied field emits `null` by default (`''` with the `formControls` plugin option) — write `value ?? ''` when feeding
|
|
1198
|
+
- An emptied field emits `null` by default (`''` with `null-value="string"` or the `formControls` plugin option) — write `value ?? ''` when feeding
|
|
1198
1199
|
APIs that require strings.
|
|
1199
1200
|
- `layout` is ignored under `labelMode="floating"`/`"inside"` — those modes
|
|
1200
1201
|
embed the label in the field and force the vertical layout (see _Density &
|
|
@@ -1256,7 +1257,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1256
1257
|
| `hint` | `string \| undefined` | | | Text box to be displayed near the input, usually to indicate instructions. |
|
|
1257
1258
|
| `id` | `string \| undefined` | | | The `id` of the native control; the label's `for` and the description and message ids derive from it. Generated when omitted. |
|
|
1258
1259
|
| `inputAlign` | `'top'` \| `'center'` \| `'bottom'` | | | Vertical position of the control in a side-by-side row; not `inputPosition`, the horizontal one. Omitted, the control's first line meets the label's. |
|
|
1259
|
-
| `inputMode` | `"text" \| "none" \| "search" \| "
|
|
1260
|
+
| `inputMode` | `"text" \| "none" \| "search" \| "tel" \| "url" \| "email" \| "numeric" \| "decimal" \| undefined` | | | The inputmode of the input. |
|
|
1260
1261
|
| `label` | `string` | | yes | Text content of the label of the element. |
|
|
1261
1262
|
| `labelAlign` | `'top'` \| `'center'` \| `'bottom'` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
|
|
1262
1263
|
| `labelMode` | `"outside" \| "floating" \| "inside" \| undefined` | | | Label rendering mode. |
|
|
@@ -1266,6 +1267,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1266
1267
|
| `mask` | `MaskInputOptions \| undefined` | | | The mask to be applied to the input. Please visit https://beholdr.github.io/maska/v3 for syntax examples. |
|
|
1267
1268
|
| `modelValue` | `string \| null \| undefined` | | | Used by v-model. |
|
|
1268
1269
|
| `name` | `string \| undefined` | | | Defines the name of the input. |
|
|
1270
|
+
| `nullValue` | `E \| undefined` | | | What the field emits once the user empties it (typing it empty, the clear button, a mask with nothing in it): `'null'` emits `null`, `'string'` emits `''`. Unset, the app's `formControls.textInputNullValue` applies (`'null'` by default). Also types the `update:modelValue` payload: with `'string'` it is `string`. |
|
|
1269
1271
|
| `persistentHint` | `boolean \| undefined` | `false` | | Keeps the hint displayed. |
|
|
1270
1272
|
| `placeholder` | `string \| undefined` | | | String displayed when there's no data. |
|
|
1271
1273
|
| `prepend:icon` | `string \| undefined` | | | Name of the icon to be added at the start of the input. |
|
|
@@ -1273,7 +1275,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1273
1275
|
| `required` | `boolean \| undefined` | `false` | | Sets the input as required. |
|
|
1274
1276
|
| `reverse` | `boolean \| undefined` | `false` | | Reverses the layout. Applicable in every direction the order of the label and the input is swapped. |
|
|
1275
1277
|
| `rules` | `RuleExpression<string \| null>` | | | vee-validate rules for this field. Only inside a `BbForm`. |
|
|
1276
|
-
| `type` | `"text" \| "search" \| "
|
|
1278
|
+
| `type` | `"text" \| "search" \| "tel" \| "url" \| "email" \| "password" \| undefined` | `"text"` | | Type of the input. Restricted to the textual input types this component supports — use `BbNumberInput` for numbers and `BbDatePickerInput` for dates. |
|
|
1277
1279
|
| `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; the field's `name` never shows. Set it when the label is not a good noun for a sentence (an icon-only label, a question) or is hidden. Only inside a `BbForm`. |
|
|
1278
1280
|
| `validationMode` | `'aggressive'` \| `'balanced'` \| `'lazy'` \| `'submit-only'` \| `'submit-live'` | | | When this field checks itself while the user works: `aggressive` (every change), `balanced` (on leave, then every change while it shows an error), `lazy` (on leave only), `submit-only` (on submit only) or `submit-live` (on submit, then every change while it shows an error). "Leave" is blur for a text-like control, and focus leaving the whole control for a picker or a group; a checkbox or switch commits on click. Unset, the enclosing `BbForm`'s `validation-mode` applies, then the app's `formControls.validationMode` (`'balanced'`). Only inside a `BbForm`. |
|
|
1279
1281
|
| `variant` | `'outline'` \| `'secondary'` \| `'none'` \| `'ghost'` | `'outline'` | | Visual variant of the field box — the same names and tokens as the `BbButton` variants. Colours only: height, padding and border width are identical across variants, so a form never reflows when one changes. `'ghost'` has no border in any state (errors and warnings show through the icon and the messages); every variant keeps the focus ring. Register extra names with the plugin's `inputVariants` option. `'none'` is a blank starting point: the field keeps its structure and behaviour but takes no look, so your own classes don't fight a built-in one. You then own hover, focus, disabled and the error / warning states. Fine for a one-off; keep inline styling short, and register a real variant (`inputVariants`) when the look recurs. |
|
|
@@ -1326,7 +1328,7 @@ States are listed in precedence order: when two are on at once and their entries
|
|
|
1326
1328
|
- `mousedown` — `(event: MouseEvent): void` — Emitted when a pointing device button is pressed over the input. Forwards the original DOM `MouseEvent`.
|
|
1327
1329
|
- `mouseup` — `(event: MouseEvent): void` — Emitted when a pointing device button is released over the input. Forwards the original DOM `MouseEvent`.
|
|
1328
1330
|
- `paste` — `(event: ClipboardEvent): void` — Emitted when content is pasted into the input. Forwards the original DOM `ClipboardEvent`.
|
|
1329
|
-
- `update:modelValue` — `(value: string \|
|
|
1331
|
+
- `update:modelValue` — `(value: string \| NullValueOf<E>): void` — Emitted when the value changes. Carries the new string, or the empty value (`null-value`, else the app's, `null` by default) when emptied. When `mask` is set, emits the masked or unmasked string depending on `emitMasked`.
|
|
1330
1332
|
|
|
1331
1333
|
## Slots
|
|
1332
1334
|
|
package/dist/ai/BbTextarea.md
CHANGED
|
@@ -63,7 +63,7 @@ Pick a sibling when the shape of the data is different:
|
|
|
63
63
|
- a calendar date → `BbDatePickerInput`;
|
|
64
64
|
- a value from a known set → `BbSelect` / `BbRadioGroup`.
|
|
65
65
|
|
|
66
|
-
`v-model` binds a `string | null` — an emptied field emits `null` by default, or `''` when the plugin option `formControls`
|
|
66
|
+
`v-model` binds a `string | null` — an emptied field emits `null` by default, or `''` when the plugin option `formControls` sets the `'string'` policy (see the installation guide § `formControls`). One field can differ from the app with `null-value="string"` or `null-value="null"`; the `update:modelValue` payload types to match (`string` with `'string'`).
|
|
67
67
|
`label` is required (hide it visually with `hideLabel`, never drop it).
|
|
68
68
|
|
|
69
69
|
### Label
|
|
@@ -608,9 +608,9 @@ const snippet = 'npx bitboss-ui ai-init';
|
|
|
608
608
|
### Clearable & loading
|
|
609
609
|
|
|
610
610
|
`clearable` reveals a clear button while the field has a value and is hovered or
|
|
611
|
-
focused; clicking it emits `update:modelValue` with `null`
|
|
612
|
-
`disabled`/`readonly` fields. There is no separate
|
|
613
|
-
model for
|
|
611
|
+
focused; clicking it emits `update:modelValue` with the empty value (`null` by
|
|
612
|
+
default) and is suppressed on `disabled`/`readonly` fields. There is no separate
|
|
613
|
+
`clear` event — watch the model for the empty value.
|
|
614
614
|
|
|
615
615
|
`loading` shows a spinner in the append position. It is **visual only** — the
|
|
616
616
|
field stays editable, so pair it with your own guard if the value must not
|
|
@@ -803,7 +803,7 @@ const notes = ref<string | null>(null);
|
|
|
803
803
|
|
|
804
804
|
### Events & v-model
|
|
805
805
|
|
|
806
|
-
- `update:modelValue` — every value change; `string`, or the empty value when emptied (`null`
|
|
806
|
+
- `update:modelValue` — every value change; `string`, or the empty value when emptied (the `null-value` policy, else `formControls`, else `'null'`; `'string'` emits `''`).
|
|
807
807
|
- `focus` / `blur` — forwarded `FocusEvent`s; they also drive the hint's
|
|
808
808
|
show-on-focus behavior.
|
|
809
809
|
- `input` — every keystroke (raw DOM event); `change` — on commit (blur), the
|
|
@@ -979,7 +979,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
979
979
|
floor; the field starts there and never shrinks below it.
|
|
980
980
|
- Avoid `labelMode="floating"` with `placeholder` — the resting label and the
|
|
981
981
|
placeholder occupy the same spot.
|
|
982
|
-
- An emptied field emits `null` by default (`''` with the `formControls` plugin option) — write `value ?? ''` when feeding
|
|
982
|
+
- An emptied field emits `null` by default (`''` with `null-value="string"` or the `formControls` plugin option) — write `value ?? ''` when feeding
|
|
983
983
|
APIs that require strings.
|
|
984
984
|
- `counter` counts what `maxlength` counts: UTF-16 code units, so an emoji
|
|
985
985
|
counts 2, and a line break counts 1 in the browser but 2 once a form
|
|
@@ -1044,6 +1044,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
|
|
|
1044
1044
|
| `loading` | `boolean \| undefined` | `false` | | Sets the component in a loading state, usually triggering some visual styles. |
|
|
1045
1045
|
| `modelValue` | `string \| null \| undefined` | | | Used by v-model. |
|
|
1046
1046
|
| `name` | `string \| undefined` | | | Defines the name of the input. |
|
|
1047
|
+
| `nullValue` | `E \| undefined` | | | What the field emits once the user empties it (typing it empty, the clear button): `'null'` emits `null`, `'string'` emits `''`. Unset, the app's `formControls.textareaNullValue` applies (`'null'` by default). Also types the `update:modelValue` payload: with `'string'` it is `string`. |
|
|
1047
1048
|
| `persistentHint` | `boolean \| undefined` | `false` | | Keeps the hint displayed. |
|
|
1048
1049
|
| `placeholder` | `string \| undefined` | | | String displayed when there's no data. |
|
|
1049
1050
|
| `prepend:icon` | `string \| undefined` | | | Name of the icon to be added at the start of the input. |
|
|
@@ -1104,7 +1105,7 @@ States are listed in precedence order: when two are on at once and their entries
|
|
|
1104
1105
|
- `mousedown` — `(event: MouseEvent): void` — Emitted when a pointing device button is pressed over the textarea. Forwards the original DOM `MouseEvent`.
|
|
1105
1106
|
- `mouseup` — `(event: MouseEvent): void` — Emitted when a pointing device button is released over the textarea. Forwards the original DOM `MouseEvent`.
|
|
1106
1107
|
- `paste` — `(event: ClipboardEvent): void` — Native clipboard paste. Forwards the original DOM `ClipboardEvent`.
|
|
1107
|
-
- `update:modelValue` — `(value: string \|
|
|
1108
|
+
- `update:modelValue` — `(value: string \| NullValueOf<E>): void` — Emitted when the value changes. Carries the new text, or the empty value (`null-value`, else the app's, `null` by default) when emptied.
|
|
1108
1109
|
|
|
1109
1110
|
## Slots
|
|
1110
1111
|
|
package/dist/ai/changelog.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 3,
|
|
3
3
|
"library": "bitboss-ui",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
4
|
+
"version": "3.0.0-beta.46",
|
|
5
5
|
"upgrade": "v2-to-v3",
|
|
6
6
|
"guide": "ai/guides/migration/v2-to-v3.md",
|
|
7
7
|
"legend": {
|
|
@@ -55,7 +55,9 @@
|
|
|
55
55
|
"3.0.0-beta.41",
|
|
56
56
|
"3.0.0-beta.42",
|
|
57
57
|
"3.0.0-beta.43",
|
|
58
|
-
"3.0.0-beta.44"
|
|
58
|
+
"3.0.0-beta.44",
|
|
59
|
+
"3.0.0-beta.45",
|
|
60
|
+
"3.0.0-beta.46"
|
|
59
61
|
],
|
|
60
62
|
"unpublished": [
|
|
61
63
|
"3.0.0-alpha.2",
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
],
|
|
77
79
|
"summary": {
|
|
78
80
|
"renames": 213,
|
|
79
|
-
"behaviourBreaks":
|
|
81
|
+
"behaviourBreaks": 169,
|
|
80
82
|
"bySurface": {
|
|
81
83
|
"prop": 15,
|
|
82
84
|
"option": 1,
|
|
@@ -5540,6 +5542,19 @@
|
|
|
5540
5542
|
"ruling": "owner 2026-10-01 (popover-in-dialog close)",
|
|
5541
5543
|
"guide": "v2-to-v3.md § 6 item 41 + BbDialog.guide.md / BbOffCanvas.guide.md § Lifecycle events",
|
|
5542
5544
|
"firstReleasedIn": "3.0.0-beta.44"
|
|
5545
|
+
},
|
|
5546
|
+
{
|
|
5547
|
+
"id": "f302",
|
|
5548
|
+
"kind": "behaviour",
|
|
5549
|
+
"summary": "`formControls.textInputNullValue` / `textareaNullValue` values `null` / `''` → `'null'` / `'string'`; an old `''` falls back to `null`",
|
|
5550
|
+
"components": [
|
|
5551
|
+
"BbTextInput",
|
|
5552
|
+
"BbTextarea"
|
|
5553
|
+
],
|
|
5554
|
+
"description": "formControls.textInputNullValue / textareaNullValue take a policy instead of the value: 'null' (the default) emits null, 'string' emits '' (it was null / ''). An old '' now falls back to null silently; TypeScript flags it in vite.config / app.use. The new per-field null-value prop takes the same values, and the Vite option also types update:modelValue (string with 'string') through the project bitboss-ui.d.ts",
|
|
5555
|
+
"ruling": "owner 2026-10-05 (null-value policy)",
|
|
5556
|
+
"guide": "v2-to-v3.md § 6 item 42 + installation-and-plugin-setup.md § formControls",
|
|
5557
|
+
"firstReleasedIn": "3.0.0-beta.46"
|
|
5543
5558
|
}
|
|
5544
5559
|
]
|
|
5545
5560
|
}
|