bitboss-ui 3.0.0-beta.21 → 3.0.0-beta.23
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/README.md +55 -3
- package/bin/bitboss-ui-mcp.mjs +419 -9
- package/bin/bitboss-ui.mjs +293 -22
- package/dist/ai/BbAccordion.md +21 -23
- package/dist/ai/BbAlert.md +6 -12
- package/dist/ai/BbAvatar.md +60 -24
- package/dist/ai/BbBadge.md +12 -20
- package/dist/ai/BbBaseButton.md +14 -16
- package/dist/ai/BbBaseCheckboxIcon.md +6 -10
- package/dist/ai/BbBaseColorInput.md +1 -0
- package/dist/ai/BbBaseDatePickerInput.md +1 -0
- package/dist/ai/BbBaseNumberInput.md +1 -0
- package/dist/ai/BbBaseRadioIcon.md +6 -10
- package/dist/ai/BbBaseSelect.md +1 -0
- package/dist/ai/BbBaseSwitchIcon.md +5 -9
- package/dist/ai/BbBaseTag.md +1 -0
- package/dist/ai/BbBaseTextInput.md +2 -1
- package/dist/ai/BbBaseTextarea.md +1 -0
- package/dist/ai/BbBaseTimePickerInput.md +1 -0
- package/dist/ai/BbBreadcrumbs.md +23 -34
- package/dist/ai/BbButton.md +35 -47
- package/dist/ai/BbCheckbox.md +21 -37
- package/dist/ai/BbCheckboxGroup.md +13 -13
- package/dist/ai/BbCollapsible.md +14 -26
- package/dist/ai/BbColorInput.md +27 -23
- package/dist/ai/BbColorPalette.md +23 -35
- package/dist/ai/BbConfirm.md +17 -32
- package/dist/ai/BbDatePickerInput.md +29 -21
- package/dist/ai/BbDialog.md +159 -62
- package/dist/ai/BbDropdown.md +21 -25
- package/dist/ai/BbDropdownButton.md +13 -30
- package/dist/ai/BbDropzone.md +9 -11
- package/dist/ai/BbIcon.md +44 -33
- package/dist/ai/BbIndicator.md +2 -2
- package/dist/ai/BbNumberInput.md +32 -20
- package/dist/ai/BbOffCanvas.md +42 -42
- package/dist/ai/BbPagination.md +21 -23
- package/dist/ai/BbPopover.md +15 -17
- package/dist/ai/BbProgress.md +40 -51
- package/dist/ai/BbRadio.md +9 -9
- package/dist/ai/BbRadioGroup.md +7 -11
- package/dist/ai/BbRating.md +8 -12
- package/dist/ai/BbSelect.md +46 -36
- package/dist/ai/BbSelectPopover.md +19 -31
- package/dist/ai/BbSlider.md +15 -19
- package/dist/ai/BbSmoothHeight.md +16 -31
- package/dist/ai/BbSpinner.md +118 -74
- package/dist/ai/BbSwitch.md +25 -18
- package/dist/ai/BbSwitchGroup.md +9 -14
- package/dist/ai/BbTable.md +102 -55
- package/dist/ai/BbTabs.md +59 -82
- package/dist/ai/BbTag.md +26 -23
- package/dist/ai/BbTextInput.md +63 -27
- package/dist/ai/BbTextarea.md +42 -34
- package/dist/ai/BbTimePickerInput.md +14 -0
- package/dist/ai/BbToast.md +14 -34
- package/dist/ai/BbTooltip.md +22 -20
- package/dist/ai/BbTree.md +46 -72
- package/dist/ai/CommonInputWrapper.md +1 -0
- package/dist/ai/changelog.json +170 -62
- package/dist/ai/components.json +1678 -11
- package/dist/ai/composables/useConfirm.md +5 -2
- package/dist/ai/composables/useToast.md +4 -2
- package/dist/ai/guides/agent-contract.md +7 -4
- package/dist/ai/guides/ai-router.md +37 -22
- package/dist/ai/guides/component-picker.md +23 -18
- package/dist/ai/guides/design-language.md +151 -21
- package/dist/ai/guides/design-tokens.md +29 -4
- package/dist/ai/guides/icons-policy.md +46 -21
- package/dist/ai/guides/installation-and-plugin-setup.md +286 -17
- package/dist/ai/guides/migration/components/bb-rating.md +26 -1
- package/dist/ai/guides/migration/components/bb-tooltip.md +18 -0
- package/dist/ai/guides/migration/v2-to-v3.md +50 -23
- package/dist/ai/index.md +1 -1
- package/dist/ai/recipes/inertia/approvals-inbox.md +3 -3
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/inertia/onboarding.md +2 -2
- package/dist/ai/recipes/inertia/ownership-atlas.md +6 -6
- package/dist/ai/recipes/inertia/record-form.md +5 -5
- package/dist/ai/recipes/inertia/records-workspace.md +4 -4
- package/dist/ai/recipes/inertia/upload-center.md +6 -6
- package/dist/ai/recipes/inertia/wizard-form.md +5 -5
- package/dist/ai/recipes/nuxt/approvals-inbox.md +3 -3
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/nuxt/onboarding.md +2 -2
- package/dist/ai/recipes/nuxt/record-form.md +5 -5
- package/dist/ai/recipes/nuxt/records-workspace.md +4 -4
- package/dist/ai/recipes/nuxt/upload-center.md +6 -6
- package/dist/ai/recipes/nuxt/wizard-form.md +5 -5
- package/dist/ai/recipes/vue/approvals-inbox.md +3 -3
- package/dist/ai/recipes/vue/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/vue/onboarding.md +2 -2
- package/dist/ai/recipes/vue/record-form.md +10 -10
- package/dist/ai/recipes/vue/records-workspace.md +4 -4
- package/dist/ai/recipes/vue/upload-center.md +6 -6
- package/dist/ai/recipes/vue/wizard-form.md +5 -5
- package/dist/ai/source/BbBadge.md +1 -1
- package/dist/ai/source/BbBaseCheckbox.md +1 -1
- package/dist/ai/source/BbBaseColorInput.md +7 -0
- package/dist/ai/source/BbBaseDatePicker.md +5 -5
- package/dist/ai/source/BbBaseDatePickerInput.md +9 -2
- package/dist/ai/source/BbBaseNumberInput.md +7 -0
- package/dist/ai/source/BbBaseRadio.md +1 -1
- package/dist/ai/source/BbBaseRating.md +5 -5
- package/dist/ai/source/BbBaseSelect.md +8 -1
- package/dist/ai/source/BbBaseSlider.md +5 -5
- package/dist/ai/source/BbBaseSwitch.md +1 -1
- package/dist/ai/source/BbBaseTag.md +7 -0
- package/dist/ai/source/BbBaseTextInput.md +7 -0
- package/dist/ai/source/BbBaseTextarea.md +7 -0
- package/dist/ai/source/BbBaseTimePickerInput.md +8 -1
- package/dist/ai/source/BbBreadcrumbs.md +20 -1
- package/dist/ai/source/BbButton.md +2 -2
- package/dist/ai/source/BbColorInput.md +14 -0
- package/dist/ai/source/BbDatePickerInput.md +14 -0
- package/dist/ai/source/BbDialog.md +39 -8
- package/dist/ai/source/BbDropdown.md +1 -1
- package/dist/ai/source/BbDropzone.md +5 -5
- package/dist/ai/source/BbIcon.md +23 -10
- package/dist/ai/source/BbNumberInput.md +14 -0
- package/dist/ai/source/BbOffCanvas.md +50 -28
- package/dist/ai/source/BbPagination.md +2 -2
- package/dist/ai/source/BbPopover.md +12 -1
- package/dist/ai/source/BbSelect.md +14 -0
- package/dist/ai/source/BbSelectPopover.md +45 -7
- package/dist/ai/source/BbSpinner.md +2 -2
- package/dist/ai/source/BbTable.md +11 -11
- package/dist/ai/source/BbTag.md +14 -0
- package/dist/ai/source/BbTextInput.md +14 -0
- package/dist/ai/source/BbTextarea.md +14 -0
- package/dist/ai/source/BbTimePickerInput.md +14 -0
- package/dist/ai/source/BbTooltip.md +57 -10
- package/dist/ai/source/ChipsBox.md +1 -1
- package/dist/ai/source/CommaBox.md +1 -1
- package/dist/ai/source/CommonInputWrapper.md +128 -33
- package/dist/ai/source/GuidesComponent.md +14 -7
- package/dist/ai/source/OptionsContainer.md +1 -1
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseColorInput/types.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbBaseDatePickerInput/types.d.ts +5 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +1 -1
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseNumberInput/types.d.ts +5 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseSelect/types.d.ts +5 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseTag/BbBaseTag.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseTag/types.d.ts +5 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseTextInput/types.d.ts +5 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/BbBaseTextarea/types.d.ts +5 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbBaseTimePickerInput/types.d.ts +5 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +71 -71
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbColorInput/types.d.ts +12 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/BbDatePickerInput/types.d.ts +12 -0
- package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +102 -97
- package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +58 -58
- package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +33 -35
- package/dist/components/BbIcon/iconCache.d.ts +30 -0
- package/dist/components/BbIcon/iconCache.js +21 -3
- package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbNumberInput/types.d.ts +12 -0
- package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +57 -56
- package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +138 -138
- package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/BbSelect/types.d.ts +12 -0
- package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +43 -43
- package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbSpinner/types.d.ts +1 -1
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/BbTable/BbTableDataRow.js +2 -2
- package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbTag/types.d.ts +12 -0
- package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbTextInput/types.d.ts +12 -0
- package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/BbTextarea/types.d.ts +12 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/BbTimePickerInput/types.d.ts +12 -0
- package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +62 -62
- package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +81 -78
- package/dist/components/BbTooltip/types.d.ts +8 -1
- package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.d.ts +1 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/CommonInputWrapper/types.d.ts +10 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/composables/useConfirm.d.ts +22 -0
- package/dist/composables/useConfirm.js +43 -40
- package/dist/composables/useSafeAreaInternal.js +1 -1
- package/dist/composables/useToast.d.ts +16 -0
- package/dist/composables/useToast.js +20 -16
- package/dist/composables/useViewportWidth.d.ts +20 -6
- package/dist/composables/useViewportWidth.js +8 -7
- package/dist/icon-scan.d.ts +276 -0
- package/dist/icon-scan.js +485 -0
- package/dist/icons-whitelist.d.ts +5 -4
- package/dist/index.d.ts +2 -0
- package/dist/input-variants.d.ts +18 -0
- package/dist/llms-full.txt +2173 -1537
- package/dist/llms-medium.txt +504 -82
- package/dist/llms.txt +1 -1
- package/dist/nuxt-module.d.ts +1 -0
- package/dist/nuxt.js +10 -7
- package/dist/plugin.js +1 -0
- package/dist/runtime/nuxt-plugin.js +2 -1
- package/dist/styles.css +1 -1
- package/dist/types/Config.d.ts +18 -0
- package/dist/types/InputVariant.d.ts +22 -0
- package/dist/utils/cssLayers.d.ts +91 -0
- package/dist/utils/cssLayers.js +43 -0
- package/dist/utils/injectLibraryStyles.d.ts +3 -1
- package/dist/utils/injectLibraryStyles.js +24 -23
- package/dist/validated/BbColorInput.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbNumberInput.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbTag.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbTextInput.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbTextarea.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/validated/index.d.ts +1 -0
- package/dist/vite-plugin.d.ts +89 -12
- package/dist/vite.js +331 -281
- package/llms.txt +1 -1
- package/package.json +7 -5
- package/scripts/lib/ensure-manifest.mjs +28 -5
- package/scripts/lib/eslint-plugin.d.ts +35 -0
- package/scripts/lib/eslint-plugin.mjs +299 -3
- package/scripts/lib/hand-roll-hints.mjs +50 -6
- package/scripts/lib/html-attributes.mjs +42 -0
- package/scripts/lib/mcp-config.mjs +38 -34
- package/scripts/lib/public-type-exports.mjs +246 -0
- package/scripts/lib/validate-bb-markup.mjs +205 -12
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
:prepend:icon="props['prepend:icon']"
|
|
22
22
|
:readonly="props.readonly"
|
|
23
23
|
:reserve-append-space="props.clearable"
|
|
24
|
+
:variant="props.variant"
|
|
24
25
|
@click:clear="$emit('update:modelValue', null)"
|
|
25
26
|
>
|
|
26
27
|
<template #prepend-outer><slot name="prepend-outer"></slot></template>
|
|
@@ -258,6 +259,7 @@ defineExpose({
|
|
|
258
259
|
## Types
|
|
259
260
|
|
|
260
261
|
```ts
|
|
262
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
261
263
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
262
264
|
import type { IconType } from '@/types/Icon';
|
|
263
265
|
import type { MaskInputOptions } from 'maska';
|
|
@@ -296,6 +298,11 @@ export type BaseTextInputProps = {
|
|
|
296
298
|
*/
|
|
297
299
|
compact?: boolean;
|
|
298
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Visual variant of the field box, forwarded to `CommonInputWrapper`.
|
|
303
|
+
*/
|
|
304
|
+
variant?: InputVariantType;
|
|
305
|
+
|
|
299
306
|
/**
|
|
300
307
|
* Disable all interactions with the input.
|
|
301
308
|
*/
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
:prepend:icon="props['prepend:icon']"
|
|
25
25
|
:readonly="props.readonly"
|
|
26
26
|
:reserve-append-space="clearable"
|
|
27
|
+
:variant="props.variant"
|
|
27
28
|
@click:clear="$emit('update:modelValue', null)"
|
|
28
29
|
>
|
|
29
30
|
<template #prepend-outer><slot name="prepend-outer"></slot></template>
|
|
@@ -204,6 +205,7 @@ function resetTextareaHeight() {
|
|
|
204
205
|
## Types
|
|
205
206
|
|
|
206
207
|
```ts
|
|
208
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
207
209
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
208
210
|
import type { IconType } from '@/types/Icon';
|
|
209
211
|
|
|
@@ -247,6 +249,11 @@ export type BaseTextareaProps = {
|
|
|
247
249
|
*/
|
|
248
250
|
compact?: boolean;
|
|
249
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Visual variant of the field box, forwarded to `CommonInputWrapper`.
|
|
254
|
+
*/
|
|
255
|
+
variant?: InputVariantType;
|
|
256
|
+
|
|
250
257
|
/**
|
|
251
258
|
* Disable all interactions with the textarea.
|
|
252
259
|
*/
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
:loading="props.loading"
|
|
26
26
|
:prepend:icon="props['prepend:icon']"
|
|
27
27
|
:readonly="props.readonly"
|
|
28
|
+
:variant="props.variant"
|
|
28
29
|
@click:clear="onClickClear"
|
|
29
30
|
@focusin="onFocusIn"
|
|
30
31
|
>
|
|
@@ -129,7 +130,7 @@
|
|
|
129
130
|
|
|
130
131
|
<span
|
|
131
132
|
:id="descriptionId"
|
|
132
|
-
class="bb-base-time-picker-input__accessible-description sr-only"
|
|
133
|
+
class="bb-base-time-picker-input__accessible-description bb-sr-only"
|
|
133
134
|
>{{ formatHint }}</span
|
|
134
135
|
>
|
|
135
136
|
<template v-if="$slots.suffix" #suffix><slot name="suffix" /></template>
|
|
@@ -822,6 +823,7 @@ onBeforeUnmount(() => {
|
|
|
822
823
|
## Types
|
|
823
824
|
|
|
824
825
|
```ts
|
|
826
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
825
827
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
826
828
|
import type { IconType } from '@/types/Icon';
|
|
827
829
|
import type { BbOffCanvasProps } from '../BbOffCanvas/types';
|
|
@@ -877,6 +879,11 @@ export type BaseTimePickerInputProps = {
|
|
|
877
879
|
/** Applies the compact density styles to the control. */
|
|
878
880
|
compact?: boolean;
|
|
879
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Visual variant of the field box, forwarded to `CommonInputWrapper`.
|
|
884
|
+
*/
|
|
885
|
+
variant?: InputVariantType;
|
|
886
|
+
|
|
880
887
|
/** Disables the input, clock trigger, and popover. */
|
|
881
888
|
disabled?: boolean;
|
|
882
889
|
|
|
@@ -174,9 +174,28 @@ const props = withDefaults(defineProps<BbBreadcrumbsProps>(), {
|
|
|
174
174
|
defineSlots<BbBreadcrumbsSlots>();
|
|
175
175
|
const { t } = useLocale();
|
|
176
176
|
const logger = useLogger();
|
|
177
|
-
const FLEX_BASIS_OFFSET_WIDTH = 5;
|
|
178
177
|
const SAFETY_MARGIN_WIDTH = 20;
|
|
179
178
|
const COLLAPSE_BUFFER = 1;
|
|
179
|
+
/**
|
|
180
|
+
* Slack added to `--flex-basis` on top of the width the items actually need.
|
|
181
|
+
*
|
|
182
|
+
* It is DERIVED, not tuned: the basis is the width this component asks the flex
|
|
183
|
+
* row for, and `recompute()` must agree that everything fits at exactly that
|
|
184
|
+
* width. Substituting `availableWidth = estimatedRequiredWidth` into
|
|
185
|
+
* `effectiveAvailableWidth` cancels the offset and the safety margin and leaves
|
|
186
|
+
* `FLEX_BASIS_OFFSET_WIDTH - COLLAPSE_BUFFER`, which then has to clear
|
|
187
|
+
* `isNearThreshold`'s `SAFETY_MARGIN_WIDTH / 2` band.
|
|
188
|
+
*
|
|
189
|
+
* At the old hand-picked `5` it did not: the slack came to `5 - 1 = 4`, inside
|
|
190
|
+
* the 10px band, so at its own basis the component always declared one item
|
|
191
|
+
* overflowed. That flipped `--flex-grow` to `1`, the row grew it, everything
|
|
192
|
+
* then fitted, `--flex-grow` went back to `0`, it shrank to the basis, and the
|
|
193
|
+
* cycle repeated every frame — a self-inflicted resize→recompute loop that only
|
|
194
|
+
* showed up once an app stopped overriding `flex-basis` (Tailwind v4 puts
|
|
195
|
+
* utilities in `@layer utilities`, which lose to our unlayered rule).
|
|
196
|
+
*/
|
|
197
|
+
const COLLAPSE_BUFFER_AND_BAND = COLLAPSE_BUFFER + SAFETY_MARGIN_WIDTH / 2;
|
|
198
|
+
const FLEX_BASIS_OFFSET_WIDTH = COLLAPSE_BUFFER_AND_BAND + 1;
|
|
180
199
|
const FONT_WIDTH = 8.5;
|
|
181
200
|
|
|
182
201
|
const slots = useSlots();
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
class="bb-button__prepend-icon"
|
|
52
52
|
:icon="props['prepend:icon']"
|
|
53
53
|
/>
|
|
54
|
-
<span class="bb-button__content" :class="{ 'sr-only': !!icon }"
|
|
54
|
+
<span class="bb-button__content" :class="{ 'bb-sr-only': !!icon }"
|
|
55
55
|
><slot>{{ text }}</slot></span
|
|
56
56
|
>
|
|
57
|
-
<span v-if="loading" class="bb-button__loading-label sr-only">{{
|
|
57
|
+
<span v-if="loading" class="bb-button__loading-label bb-sr-only">{{
|
|
58
58
|
t('common.loadingText')
|
|
59
59
|
}}</span>
|
|
60
60
|
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
:readonly="readonly"
|
|
52
52
|
:required="required"
|
|
53
53
|
:swatches="swatches"
|
|
54
|
+
:variant="variant"
|
|
54
55
|
v-bind="eventListeners"
|
|
55
56
|
>
|
|
56
57
|
<template #append-outer><slot name="append-outer" /></template>
|
|
@@ -139,6 +140,7 @@ const eventListeners = {
|
|
|
139
140
|
## Types
|
|
140
141
|
|
|
141
142
|
```ts
|
|
143
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
142
144
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
143
145
|
import type { InputDirection } from '@/types/InputDirection';
|
|
144
146
|
import type { IconType } from '@/types/Icon';
|
|
@@ -182,6 +184,18 @@ export type BbColorInputProps = {
|
|
|
182
184
|
* Displays the component in a compact version.
|
|
183
185
|
*/
|
|
184
186
|
compact?: boolean;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Visual variant of the field box — the same names and tokens as the
|
|
190
|
+
* `BbButton` variants. Colours only: height, padding and border width are
|
|
191
|
+
* identical across variants, so a form never reflows when one changes.
|
|
192
|
+
* `'ghost'` has no border in any state (errors and warnings show through
|
|
193
|
+
* the icon and the messages); every variant keeps the focus ring.
|
|
194
|
+
* Register extra names with the plugin's `inputVariants` option.
|
|
195
|
+
*
|
|
196
|
+
* @defaultValue `'outline'`
|
|
197
|
+
*/
|
|
198
|
+
variant?: InputVariantType;
|
|
185
199
|
/**
|
|
186
200
|
* Descriptive text displayed below the label and above the input. Unlike the hint it is always
|
|
187
201
|
* visible, and it is linked to the input via `aria-describedby` (after any
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
:transition-duration="transitionDuration"
|
|
65
65
|
:type="type"
|
|
66
66
|
:utc="utc"
|
|
67
|
+
:variant="variant"
|
|
67
68
|
v-bind="eventListeners"
|
|
68
69
|
>
|
|
69
70
|
<template #prepend-outer><slot name="prepend-outer"></slot></template>
|
|
@@ -158,6 +159,7 @@ const eventListeners = {
|
|
|
158
159
|
## Types
|
|
159
160
|
|
|
160
161
|
```ts
|
|
162
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
161
163
|
import type { DatePickerType } from '@/types/DatePickerType';
|
|
162
164
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
163
165
|
import type { InputDirection } from '@/types/InputDirection';
|
|
@@ -227,6 +229,18 @@ export type BbDatePickerInputProps = {
|
|
|
227
229
|
*/
|
|
228
230
|
compact?: boolean;
|
|
229
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Visual variant of the field box — the same names and tokens as the
|
|
234
|
+
* `BbButton` variants. Colours only: height, padding and border width are
|
|
235
|
+
* identical across variants, so a form never reflows when one changes.
|
|
236
|
+
* `'ghost'` has no border in any state (errors and warnings show through
|
|
237
|
+
* the icon and the messages); every variant keeps the focus ring.
|
|
238
|
+
* Register extra names with the plugin's `inputVariants` option.
|
|
239
|
+
*
|
|
240
|
+
* @defaultValue `'outline'`
|
|
241
|
+
*/
|
|
242
|
+
variant?: InputVariantType;
|
|
243
|
+
|
|
230
244
|
/**
|
|
231
245
|
* Direction of the layout of the component. Can either be a predefined value or a pattern separated by a space like `xx xxxxx`.
|
|
232
246
|
*/
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
'bb-dialog--mobile': isMobile,
|
|
51
51
|
'bb-dialog--deny-close': denyClose,
|
|
52
52
|
'bb-dialog--covered': isCovered,
|
|
53
|
+
'bb-dialog--stacked': isStackedAbove,
|
|
53
54
|
}"
|
|
54
55
|
:inert="(!model && !isVisible) || isCovered"
|
|
55
56
|
:style="{
|
|
@@ -430,6 +431,20 @@ const isCovered = computed(() => {
|
|
|
430
431
|
return list[list.length - 1] !== stackId;
|
|
431
432
|
});
|
|
432
433
|
|
|
434
|
+
/**
|
|
435
|
+
* True when this dialog sits ABOVE another member of its stack. A stack paints
|
|
436
|
+
* ONE backdrop, owned by its bottom member; everyone above rides on it with a
|
|
437
|
+
* transparent backdrop of their own (see `--stacked` in index.css). Two things
|
|
438
|
+
* fall out: swapping members never fades a backdrop, so the page behind never
|
|
439
|
+
* blinks mid-crossfade, and N stacked dialogs cost one blur pass instead of N.
|
|
440
|
+
*/
|
|
441
|
+
const isStackedAbove = computed(() => {
|
|
442
|
+
if (!props.stack) return false;
|
|
443
|
+
const list = stacks.value[props.stack];
|
|
444
|
+
if (!list || !list.includes(stackId)) return false;
|
|
445
|
+
return list[0] !== stackId;
|
|
446
|
+
});
|
|
447
|
+
|
|
433
448
|
let releaseTopLayer: (() => void) | null = null;
|
|
434
449
|
const toggleDialog = async (value: boolean) => {
|
|
435
450
|
if (value) {
|
|
@@ -460,13 +475,21 @@ const toggleDialog = async (value: boolean) => {
|
|
|
460
475
|
emit('shown');
|
|
461
476
|
} else {
|
|
462
477
|
emit('hide');
|
|
478
|
+
// Leave the stack NOW, not after the fade. The member beneath uncovers
|
|
479
|
+
// at once and crossfades in under this one's fade-out, so it is fully
|
|
480
|
+
// visible one transition after the close — not two, with a dead window
|
|
481
|
+
// where nothing was on screen (measured 2026-09-10: 250ms of blank, the
|
|
482
|
+
// parent finishing at ~520ms). Safe to do early: while this dialog is
|
|
483
|
+
// still natively open, the HTML modal algorithm keeps everything outside
|
|
484
|
+
// it inert regardless of our classes, and Escape stays owned through the
|
|
485
|
+
// overlay stack until the listener is removed below.
|
|
486
|
+
partStack();
|
|
463
487
|
const currentOpenedTimes = openedTimes.value;
|
|
464
488
|
await wait(props.transitionDuration);
|
|
465
|
-
// Reopened during the transition — keep it open
|
|
466
|
-
//
|
|
489
|
+
// Reopened during the transition — keep it open. The open path already
|
|
490
|
+
// re-joined the stack (on top); the top-layer host was never released.
|
|
467
491
|
if (currentOpenedTimes !== openedTimes.value) return;
|
|
468
492
|
removeEscapeListener();
|
|
469
|
-
partStack();
|
|
470
493
|
dialog.value?.close();
|
|
471
494
|
// Release the top-layer host only after close(): releasing at close-start
|
|
472
495
|
// would re-home body toasts under a still-modal dialog, freezing them for
|
|
@@ -842,14 +865,22 @@ export type { BbDialogSlots as BaseDialogSlots };
|
|
|
842
865
|
animation: bb-dialog-deny-bounce 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
843
866
|
}
|
|
844
867
|
|
|
845
|
-
/* A dialog covered by a later sibling of the same stack fades out
|
|
846
|
-
only the top-most member of the stack stays visible.
|
|
868
|
+
/* A dialog covered by a later sibling of the same stack fades its BOX out —
|
|
869
|
+
only the top-most member of the stack stays visible. Its backdrop stays:
|
|
870
|
+
the bottom member owns the stack's one backdrop (below). */
|
|
847
871
|
&.bb-dialog--covered {
|
|
848
872
|
opacity: 0;
|
|
873
|
+
}
|
|
849
874
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
875
|
+
/* A member above the bottom of its stack rides on the bottom member's
|
|
876
|
+
backdrop and paints none of its own. Swapping members therefore never
|
|
877
|
+
fades a backdrop — no brightness blink mid-crossfade — and the stack costs
|
|
878
|
+
one blur pass however deep it goes. Must follow `--open`, which sets the
|
|
879
|
+
backdrop to 1 at equal specificity. */
|
|
880
|
+
&.bb-dialog--stacked::backdrop {
|
|
881
|
+
-webkit-backdrop-filter: none;
|
|
882
|
+
backdrop-filter: none;
|
|
883
|
+
opacity: 0;
|
|
853
884
|
}
|
|
854
885
|
|
|
855
886
|
&::backdrop {
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
menu container: `role="menu"` admits only items and groups, and a
|
|
95
95
|
reader in menu mode walks items — so the presentational row inside
|
|
96
96
|
is skipped and an empty search would announce nothing. -->
|
|
97
|
-
<span class="sr-only" role="status">{{ statusText }}</span>
|
|
97
|
+
<span class="bb-sr-only" role="status">{{ statusText }}</span>
|
|
98
98
|
<div v-if="$slots.footer" class="bb-dropdown__footer">
|
|
99
99
|
<slot
|
|
100
100
|
:disabled="disabled"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
:aria-label="dropzoneAriaLabel"
|
|
42
42
|
:aria-labelledby="ariaLabelledby"
|
|
43
43
|
:autofocus="autofocus"
|
|
44
|
-
class="sr-only"
|
|
44
|
+
class="bb-sr-only"
|
|
45
45
|
:disabled="disabled"
|
|
46
46
|
:multiple="multiple"
|
|
47
47
|
type="file"
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
half of that composed name ("<your copy> Select files"), so the
|
|
55
55
|
name still says the control takes files. Without it the localized
|
|
56
56
|
`aria-label` is the whole name and this span would be dead
|
|
57
|
-
sr-only chatter on every dropzone. -->
|
|
58
|
-
<span v-if="hasSlotLabel" :id="defaultLabelId" class="sr-only">{{
|
|
57
|
+
bb-sr-only chatter on every dropzone. -->
|
|
58
|
+
<span v-if="hasSlotLabel" :id="defaultLabelId" class="bb-sr-only">{{
|
|
59
59
|
dropzoneAriaLabel
|
|
60
60
|
}}</span>
|
|
61
61
|
</label>
|
|
@@ -330,7 +330,7 @@ const errorContainerId = `${containerId}_errors`;
|
|
|
330
330
|
const warningContainerId = `${containerId}_warnings`;
|
|
331
331
|
/** Handed to the slot as `labelId`; the consumer puts it on their label node. */
|
|
332
332
|
const slotLabelId = `${containerId}_label`;
|
|
333
|
-
/** Our sr-only copy of the localized instruction, appended to that name. */
|
|
333
|
+
/** Our bb-sr-only copy of the localized instruction, appended to that name. */
|
|
334
334
|
const defaultLabelId = `${containerId}_default_label`;
|
|
335
335
|
|
|
336
336
|
const dragging = ref(false);
|
|
@@ -394,7 +394,7 @@ onUpdated(syncSlotLabel);
|
|
|
394
394
|
|
|
395
395
|
/**
|
|
396
396
|
* `aria-labelledby` wins over `aria-label`, so the localized instruction has to
|
|
397
|
-
* be re-supplied as a referenced node — hence the sr-only span. Consumer copy
|
|
397
|
+
* be re-supplied as a referenced node — hence the bb-sr-only span. Consumer copy
|
|
398
398
|
* first: WCAG 2.5.3 (Label in Name) wants the visible label at the START of the
|
|
399
399
|
* accessible name so speech input can match what the user reads.
|
|
400
400
|
*/
|
package/dist/ai/source/BbIcon.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import {
|
|
19
19
|
computed,
|
|
20
20
|
inject,
|
|
21
|
-
|
|
21
|
+
shallowRef,
|
|
22
22
|
onServerPrefetch,
|
|
23
23
|
onBeforeMount,
|
|
24
24
|
watch,
|
|
@@ -36,8 +36,10 @@ import { useConfig } from '@/composables/useConfig';
|
|
|
36
36
|
import {
|
|
37
37
|
cacheKeyFor as cacheKey,
|
|
38
38
|
EMPTY_ICON,
|
|
39
|
+
MISSING_ICON,
|
|
39
40
|
parseIcon,
|
|
40
41
|
parsedIcons,
|
|
42
|
+
resolveRegistryIcon,
|
|
41
43
|
type ParsedIcon,
|
|
42
44
|
} from './iconCache';
|
|
43
45
|
import type { BbIconProps, BbIconSize } from './types';
|
|
@@ -52,7 +54,10 @@ if (process.env.NODE_ENV !== 'production')
|
|
|
52
54
|
warnRemovedAttrs('BbIcon', removedPropsFor('BbIcon'));
|
|
53
55
|
|
|
54
56
|
const logger = useLogger();
|
|
55
|
-
|
|
57
|
+
// Shallow: the registry is a (possibly Proxy-wrapped) map of loaders — there is
|
|
58
|
+
// nothing to track inside it, and a deep reactive wrapper would proxy the
|
|
59
|
+
// plugin's own Proxy on every lookup.
|
|
60
|
+
const icons = shallowRef<IconRegistry>();
|
|
56
61
|
|
|
57
62
|
const loadIcons = () => {
|
|
58
63
|
// Prefer the typed Symbol key; fall back to the legacy string key for one
|
|
@@ -102,7 +107,10 @@ const cacheKeyFor = (value: string) => cacheKey(value, isSvgPayload(value));
|
|
|
102
107
|
* Seeded from the module cache: an icon this page has already resolved renders
|
|
103
108
|
* complete in the FIRST render pass, with no loader promise and no regex.
|
|
104
109
|
*/
|
|
105
|
-
|
|
110
|
+
// Shallow on purpose: a parsed icon is immutable, and the placeholder is
|
|
111
|
+
// recognised by IDENTITY (`=== MISSING_ICON`) — a deep ref would hand back a
|
|
112
|
+
// reactive proxy of it and the comparison would never hold.
|
|
113
|
+
const parsed = shallowRef<ParsedIcon | null>(
|
|
106
114
|
parsedIcons.get(cacheKeyFor(props.icon)) ?? null
|
|
107
115
|
);
|
|
108
116
|
|
|
@@ -136,13 +144,13 @@ const getIcon = async () => {
|
|
|
136
144
|
logger.throw('Icons have not been loaded');
|
|
137
145
|
}
|
|
138
146
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
parsed.value =
|
|
147
|
+
// Never throws: a name that does not resolve yields the built-in
|
|
148
|
+
// placeholder (and one console.error per name per page), so the component
|
|
149
|
+
// tree around the icon stays mounted and the gap is visible where the icon
|
|
150
|
+
// should be — in dev exactly as in production.
|
|
151
|
+
const resolved = await resolveRegistryIcon(key, icons.value!);
|
|
152
|
+
// The prop may have moved on while the loader was in flight.
|
|
153
|
+
if (cacheKeyFor(props.icon) === key) parsed.value = resolved;
|
|
146
154
|
};
|
|
147
155
|
|
|
148
156
|
onServerPrefetch(async () => {
|
|
@@ -217,6 +225,11 @@ const iconAttributes = computed(() => {
|
|
|
217
225
|
// Icons should never be focusable or keyboard navigable since they're not interactive
|
|
218
226
|
base['focusable'] = 'false';
|
|
219
227
|
|
|
228
|
+
// The placeholder stands in for a name that did not resolve: sized and
|
|
229
|
+
// coloured exactly like the icon it replaces, and findable in the DOM (and
|
|
230
|
+
// in tests) by the name that was asked for.
|
|
231
|
+
if (parsed.value === MISSING_ICON) base['data-bb-icon-missing'] = props.icon;
|
|
232
|
+
|
|
220
233
|
return base;
|
|
221
234
|
});
|
|
222
235
|
</script>
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
:readonly="readonly"
|
|
56
56
|
:required="required"
|
|
57
57
|
:step="step"
|
|
58
|
+
:variant="variant"
|
|
58
59
|
v-bind="eventListeners"
|
|
59
60
|
>
|
|
60
61
|
<template #append-outer="data"
|
|
@@ -146,6 +147,7 @@ const eventListeners = {
|
|
|
146
147
|
## Types
|
|
147
148
|
|
|
148
149
|
```ts
|
|
150
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
149
151
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
150
152
|
import type { InputDirection } from '@/types/InputDirection';
|
|
151
153
|
import type { IconType } from '@/types/Icon';
|
|
@@ -180,6 +182,18 @@ export type BbNumberInputProps = {
|
|
|
180
182
|
*/
|
|
181
183
|
compact?: boolean;
|
|
182
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Visual variant of the field box — the same names and tokens as the
|
|
187
|
+
* `BbButton` variants. Colours only: height, padding and border width are
|
|
188
|
+
* identical across variants, so a form never reflows when one changes.
|
|
189
|
+
* `'ghost'` has no border in any state (errors and warnings show through
|
|
190
|
+
* the icon and the messages); every variant keeps the focus ring.
|
|
191
|
+
* Register extra names with the plugin's `inputVariants` option.
|
|
192
|
+
*
|
|
193
|
+
* @defaultValue `'outline'`
|
|
194
|
+
*/
|
|
195
|
+
variant?: InputVariantType;
|
|
196
|
+
|
|
183
197
|
/**
|
|
184
198
|
* Descriptive text displayed below the label and above the input. Unlike the hint it is always
|
|
185
199
|
* visible, and it is linked to the input via `aria-describedby` (after any
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
'bb-offcanvas--swipe-exit': swipeExiting,
|
|
29
29
|
'bb-offcanvas--instant-stack': instantStack,
|
|
30
30
|
'bb-offcanvas--covered': isCovered,
|
|
31
|
+
'bb-offcanvas--stacked': isStackedAbove,
|
|
31
32
|
'bb-offcanvas--has-handle': draggable,
|
|
32
33
|
'bb-offcanvas--no-header': !renderHeader(),
|
|
33
34
|
[`bb-offcanvas--${props.side}`]: true,
|
|
@@ -333,13 +334,21 @@ const toggleDialog = async (value: boolean) => {
|
|
|
333
334
|
emit('shown');
|
|
334
335
|
} else {
|
|
335
336
|
emit('hide');
|
|
337
|
+
// Leave the stack NOW, not after the slide-out. The parents' push offsets
|
|
338
|
+
// derive from the stack registry, so releasing it here lets them slide
|
|
339
|
+
// back in lockstep with this panel leaving — instead of staying pushed
|
|
340
|
+
// for the whole transition and only then starting their own (2026-09-10).
|
|
341
|
+
// Safe to do early: Escape ownership lives in the overlay escape stack,
|
|
342
|
+
// not in `openOrder`, and stays with this panel until the listener is
|
|
343
|
+
// removed below; while still natively open, the modal algorithm keeps
|
|
344
|
+
// everything outside it inert regardless of our classes.
|
|
345
|
+
leaveStack();
|
|
336
346
|
const currentOpenedTimes = openedTimes.value;
|
|
337
347
|
await wait(props.transitionDuration);
|
|
338
|
-
// Reopened during the transition — keep it open
|
|
339
|
-
//
|
|
348
|
+
// Reopened during the transition — keep it open. The open path already
|
|
349
|
+
// re-entered the stack (on top); the top-layer host was never released.
|
|
340
350
|
if (currentOpenedTimes !== openedTimes.value) return;
|
|
341
351
|
removeEscapeListener();
|
|
342
|
-
leaveStack();
|
|
343
352
|
dialog.value?.close();
|
|
344
353
|
// Release the top-layer host only after close(): releasing at close-start
|
|
345
354
|
// would re-home body toasts under a still-modal panel, freezing them for
|
|
@@ -522,20 +531,25 @@ const leaveStack = () => {
|
|
|
522
531
|
};
|
|
523
532
|
|
|
524
533
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
534
|
+
* `covered`: another open offcanvas sits on top of this one. A covered panel
|
|
535
|
+
* is inert, yields Escape to the front-most panel and drops its `will-change`
|
|
536
|
+
* layer (it cannot be dragged). Backdrops are NOT keyed off this — see
|
|
537
|
+
* `isStackedAbove`.
|
|
529
538
|
*/
|
|
530
|
-
const BLURRED_LAYERS = 2;
|
|
531
539
|
const isCovered = computed(() => {
|
|
532
540
|
const index = openOrder.value.indexOf(stackId);
|
|
533
|
-
return
|
|
534
|
-
isVisible.value &&
|
|
535
|
-
index !== -1 &&
|
|
536
|
-
index < openOrder.value.length - BLURRED_LAYERS
|
|
537
|
-
);
|
|
541
|
+
return isVisible.value && index !== -1 && index < openOrder.value.length - 1;
|
|
538
542
|
});
|
|
543
|
+
/**
|
|
544
|
+
* One backdrop per pile of open offcanvases, owned by the BOTTOM-most: every
|
|
545
|
+
* panel above it renders a transparent, unfiltered backdrop (`--stacked`). The
|
|
546
|
+
* owner never loses or regains its blur/dim while panels open and close above
|
|
547
|
+
* it, so a closing child's backdrop fade changes nothing on screen — no
|
|
548
|
+
* double-dim brightening to single-dim, and no blur snapping in on a deeper
|
|
549
|
+
* member as the pile shrinks. It also costs one blur pass however deep it goes.
|
|
550
|
+
* Mirrors `bb-dialog--stacked`.
|
|
551
|
+
*/
|
|
552
|
+
const isStackedAbove = computed(() => openOrder.value.indexOf(stackId) > 0);
|
|
539
553
|
|
|
540
554
|
// Re-register if the `stack` name, `direction` or `stackGap` changes while open,
|
|
541
555
|
// so the registry's bookkeeping (order, direction, size, gap) stays in sync.
|
|
@@ -1140,20 +1154,12 @@ export type BbOffCanvasSlots = {
|
|
|
1140
1154
|
--stack-transition-duration: 0s;
|
|
1141
1155
|
}
|
|
1142
1156
|
|
|
1143
|
-
/*
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
layer, since a covered panel can't be dragged. Must follow `--visible` to
|
|
1148
|
-
win the equal-specificity `will-change`. */
|
|
1157
|
+
/* A `covered` panel (another open panel sits on top) drops its `will-change`
|
|
1158
|
+
layer, since it can't be dragged. Must follow `--visible` to win the
|
|
1159
|
+
equal-specificity `will-change`. Its backdrop is untouched: which panel
|
|
1160
|
+
paints the backdrop is decided by `--stacked` below. */
|
|
1149
1161
|
&.bb-offcanvas--covered {
|
|
1150
1162
|
will-change: auto;
|
|
1151
|
-
|
|
1152
|
-
&::backdrop {
|
|
1153
|
-
-webkit-backdrop-filter: none;
|
|
1154
|
-
backdrop-filter: none;
|
|
1155
|
-
background-color: transparent;
|
|
1156
|
-
}
|
|
1157
1163
|
}
|
|
1158
1164
|
|
|
1159
1165
|
/* Stack pad: fills from the panel's anchored edge out to the screen edge, so a
|
|
@@ -1294,6 +1300,21 @@ export type BbOffCanvasSlots = {
|
|
|
1294
1300
|
}
|
|
1295
1301
|
}
|
|
1296
1302
|
}
|
|
1303
|
+
|
|
1304
|
+
/* One backdrop per pile of open panels, painted by the BOTTOM-most. Every
|
|
1305
|
+
panel above it is `--stacked`: no blur, no dim, so a child's backdrop
|
|
1306
|
+
fading out on close changes nothing on screen (two half-dims composite
|
|
1307
|
+
lighter than one; before this the pile brightened during the handoff),
|
|
1308
|
+
and N stacked panels cost one blur pass instead of N (stacked
|
|
1309
|
+
`backdrop-filter`s each reblur the whole scene and choke the GPU past a
|
|
1310
|
+
handful). Must follow `--open`, which sets opacity 1 at equal
|
|
1311
|
+
specificity. Mirrors `bb-dialog--stacked`. */
|
|
1312
|
+
&.bb-offcanvas--stacked::backdrop {
|
|
1313
|
+
-webkit-backdrop-filter: none;
|
|
1314
|
+
backdrop-filter: none;
|
|
1315
|
+
opacity: 0;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1297
1318
|
&.bb-offcanvas--deny-close {
|
|
1298
1319
|
animation-duration: 220ms;
|
|
1299
1320
|
animation-fill-mode: both;
|
|
@@ -1319,9 +1340,10 @@ export type BbOffCanvasSlots = {
|
|
|
1319
1340
|
backdrop-filter: blur(var(--bb-overlay-blur, 2px));
|
|
1320
1341
|
background-color: var(--bb-overlay);
|
|
1321
1342
|
opacity: 0;
|
|
1322
|
-
/* Also transition the dim: when
|
|
1323
|
-
|
|
1324
|
-
|
|
1343
|
+
/* Also transition the dim: when the backdrop owner closes under a panel
|
|
1344
|
+
that stays open, the new owner's dim fades in in step with the old one
|
|
1345
|
+
fading out, so the total stays roughly constant instead of flicking
|
|
1346
|
+
lighter/darker. */
|
|
1325
1347
|
transition:
|
|
1326
1348
|
opacity var(--transition-duration) var(--bb-ease),
|
|
1327
1349
|
background-color var(--transition-duration) var(--bb-ease);
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<li>
|
|
20
20
|
<BbBaseButton v-bind="previousAttributes">
|
|
21
21
|
<slot name="previous">
|
|
22
|
-
<span class="bb-pagination__page-label sr-only">{{
|
|
22
|
+
<span class="bb-pagination__page-label bb-sr-only">{{
|
|
23
23
|
t('pagination.previous').value
|
|
24
24
|
}}</span>
|
|
25
25
|
<svg
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
<li>
|
|
66
66
|
<BbBaseButton v-bind="nextAttributes">
|
|
67
67
|
<slot name="next">
|
|
68
|
-
<span class="bb-pagination__page-label sr-only">{{
|
|
68
|
+
<span class="bb-pagination__page-label bb-sr-only">{{
|
|
69
69
|
t('pagination.next').value
|
|
70
70
|
}}</span>
|
|
71
71
|
<svg
|
|
@@ -389,7 +389,18 @@ useIntersectionObserver(
|
|
|
389
389
|
([{ intersectionRatio }]) => {
|
|
390
390
|
// A modal sheet is detached from the activator's position, so it must not
|
|
391
391
|
// close just because the activator scrolls out of view.
|
|
392
|
-
|
|
392
|
+
//
|
|
393
|
+
// "Fully visible" is a tolerance, never `=== 1`. The ratio is
|
|
394
|
+
// intersection area over target area, and for an activator with a
|
|
395
|
+
// sub-pixel box (a bare <button> whose width is its text, 29.3px under
|
|
396
|
+
// the shipped reset) Chromium reports 0.9999… for an element that is
|
|
397
|
+
// entirely on screen. A strict `!== 1` then closed the popover in the
|
|
398
|
+
// same millisecond it opened — the observer's very first entry, delivered
|
|
399
|
+
// right after `showModal()` — for any consumer whose activator happened
|
|
400
|
+
// to measure fractionally (2026-09-09). 1% is 0.18px on an 18px button:
|
|
401
|
+
// rounding noise, never a real clip.
|
|
402
|
+
const fullyVisible = intersectionRatio >= 0.99;
|
|
403
|
+
if (!fullyVisible && isOpen.value && !useOffcanvas.value) close();
|
|
393
404
|
},
|
|
394
405
|
{ threshold: [0, 1] }
|
|
395
406
|
);
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
:selected-labels-fn="selectedLabelsFn"
|
|
79
79
|
:stash="stash"
|
|
80
80
|
:transition-duration="transitionDuration"
|
|
81
|
+
:variant="variant"
|
|
81
82
|
v-bind="eventListeners"
|
|
82
83
|
>
|
|
83
84
|
<template v-if="$slots.header" #header="data">
|
|
@@ -218,6 +219,7 @@ const eventListeners = {
|
|
|
218
219
|
## Types
|
|
219
220
|
|
|
220
221
|
```ts
|
|
222
|
+
import type { InputVariantType } from '@/types/InputVariant';
|
|
221
223
|
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
222
224
|
import type { InputDirection } from '@/types/InputDirection';
|
|
223
225
|
import type { ItemAccessor } from '@/types/ItemAccessor';
|
|
@@ -287,6 +289,18 @@ export type BbSelectProps<T> = {
|
|
|
287
289
|
*/
|
|
288
290
|
compact?: boolean;
|
|
289
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Visual variant of the field box — the same names and tokens as the
|
|
294
|
+
* `BbButton` variants. Colours only: height, padding and border width are
|
|
295
|
+
* identical across variants, so a form never reflows when one changes.
|
|
296
|
+
* `'ghost'` has no border in any state (errors and warnings show through
|
|
297
|
+
* the icon and the messages); every variant keeps the focus ring.
|
|
298
|
+
* Register extra names with the plugin's `inputVariants` option.
|
|
299
|
+
*
|
|
300
|
+
* @defaultValue `'outline'`
|
|
301
|
+
*/
|
|
302
|
+
variant?: InputVariantType;
|
|
303
|
+
|
|
290
304
|
/**
|
|
291
305
|
* Defines an array of dependencies that will trigger actions in the component upon change.
|
|
292
306
|
*/
|