bitboss-ui 2.1.134 → 3.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +244 -21
- package/bin/bitboss-ui-mcp.mjs +557 -0
- package/bin/bitboss-ui.mjs +626 -0
- package/dist/ai/BbAccordion.md +522 -113
- package/dist/ai/BbAlert.md +377 -197
- package/dist/ai/BbAsterisk.md +46 -0
- package/dist/ai/BbAvatar.md +454 -178
- package/dist/ai/BbBadge.md +695 -225
- package/dist/ai/BbBadgeButton.md +54 -0
- package/dist/ai/BbBaseButton.md +489 -0
- package/dist/ai/BbBaseCheckbox.md +49 -0
- package/dist/ai/BbBaseCheckboxGroup.md +64 -0
- package/dist/ai/BbBaseCheckboxIcon.md +174 -0
- package/dist/ai/BbBaseColorInput.md +64 -0
- package/dist/ai/BbBaseColorPalette.md +39 -0
- package/dist/ai/BbBaseDatePicker.md +42 -0
- package/dist/ai/BbBaseDatePickerInput.md +73 -0
- package/dist/ai/BbBaseInputContainer.md +48 -0
- package/dist/ai/BbBaseNumberInput.md +71 -0
- package/dist/ai/BbBaseRadio.md +46 -0
- package/dist/ai/BbBaseRadioGroup.md +63 -0
- package/dist/ai/BbBaseRadioIcon.md +187 -0
- package/dist/ai/BbBaseRating.md +51 -0
- package/dist/ai/BbBaseSelect.md +88 -0
- package/dist/ai/BbBaseSlider.md +56 -0
- package/dist/ai/BbBaseSwitch.md +49 -0
- package/dist/ai/BbBaseSwitchGroup.md +64 -0
- package/dist/ai/BbBaseSwitchIcon.md +184 -0
- package/dist/ai/BbBaseTag.md +68 -0
- package/dist/ai/BbBaseTextInput.md +75 -0
- package/dist/ai/BbBaseTextarea.md +62 -0
- package/dist/ai/BbBaseTimePickerInput.md +62 -0
- package/dist/ai/BbBreadcrumbs.md +542 -503
- package/dist/ai/BbButton.md +767 -636
- package/dist/ai/BbCheckbox.md +572 -254
- package/dist/ai/BbCheckboxGroup.md +561 -361
- package/dist/ai/BbCollapsible.md +419 -103
- package/dist/ai/BbColorInput.md +529 -323
- package/dist/ai/BbColorPalette.md +565 -330
- package/dist/ai/BbConfirm.md +612 -136
- package/dist/ai/BbConfirmPortal.md +28 -0
- package/dist/ai/BbDatePicker.md +244 -0
- package/dist/ai/BbDatePickerInput.md +706 -388
- package/dist/ai/BbDialog.md +770 -99
- package/dist/ai/BbDropdown.md +1054 -763
- package/dist/ai/BbDropdownButton.md +511 -615
- package/dist/ai/BbDropdownGroup.md +34 -0
- package/dist/ai/BbDropzone.md +1148 -436
- package/dist/ai/BbForm.md +239 -0
- package/dist/ai/BbIcon.md +379 -212
- package/dist/ai/BbIndicator.md +283 -0
- package/dist/ai/BbNumberInput.md +713 -355
- package/dist/ai/BbOffCanvas.md +875 -498
- package/dist/ai/BbPagination.md +483 -535
- package/dist/ai/BbPopover.md +583 -546
- package/dist/ai/BbProgress.md +456 -69
- package/dist/ai/BbRadio.md +343 -229
- package/dist/ai/BbRadioGroup.md +426 -353
- package/dist/ai/BbRating.md +361 -226
- package/dist/ai/BbSelect.md +1380 -536
- package/dist/ai/BbSelectPopover.md +1240 -1579
- package/dist/ai/BbSlider.md +478 -257
- package/dist/ai/BbSmoothHeight.md +472 -139
- package/dist/ai/BbSpinner.md +718 -131
- package/dist/ai/BbSwitch.md +549 -119
- package/dist/ai/BbSwitchGroup.md +503 -214
- package/dist/ai/BbTable.md +1956 -2358
- package/dist/ai/BbTabs.md +1113 -0
- package/dist/ai/BbTabsList.md +29 -0
- package/dist/ai/BbTabsPanes.md +27 -0
- package/dist/ai/BbTabsRoot.md +57 -0
- package/dist/ai/BbTag.md +393 -292
- package/dist/ai/BbTextInput.md +852 -333
- package/dist/ai/BbTextarea.md +836 -255
- package/dist/ai/BbTimePicker.md +231 -0
- package/dist/ai/BbTimePickerInput.md +196 -0
- package/dist/ai/BbToast.md +624 -196
- package/dist/ai/BbToastPortal.md +28 -0
- package/dist/ai/BbTooltip.md +443 -346
- package/dist/ai/BbTree.md +984 -235
- package/dist/ai/ChipsBox.md +27 -199
- package/dist/ai/ClearableButton.md +21 -55
- package/dist/ai/CommaBox.md +25 -200
- package/dist/ai/CommonInputWrapper.md +42 -0
- package/dist/ai/CommonPopover.md +30 -510
- package/dist/ai/ErrorIcon.md +19 -56
- package/dist/ai/FlatListBox.md +35 -379
- package/dist/ai/GroupedListBox.md +37 -535
- package/dist/ai/GuidesComponent.md +21 -0
- package/dist/ai/ListBox.md +38 -231
- package/dist/ai/OptionsContainer.md +34 -253
- package/dist/ai/WarningIcon.md +21 -0
- package/dist/ai/components.json +15883 -0
- package/dist/ai/composables/useBbConfig.md +80 -0
- package/dist/ai/composables/useBbFormContext.md +110 -0
- package/dist/ai/composables/useBbTableContext.md +164 -0
- package/dist/ai/composables/useBbTabsContext.md +118 -0
- package/dist/ai/composables/useBroadcastChannelInstance.md +97 -0
- package/dist/ai/composables/useConfirm.md +306 -0
- package/dist/ai/composables/useCountdown.md +84 -0
- package/dist/ai/composables/useId.md +60 -0
- package/dist/ai/composables/useMobile.md +85 -0
- package/dist/ai/composables/useToast.md +230 -0
- package/dist/ai/guides/agent-contract.md +108 -0
- package/dist/ai/guides/ai-router.md +111 -0
- package/dist/ai/guides/app-layout.md +295 -0
- package/dist/ai/guides/coherence-playbook.md +136 -0
- package/dist/ai/guides/component-picker.md +195 -0
- package/dist/ai/guides/design-language.md +461 -0
- package/dist/ai/guides/design-tokens.md +424 -0
- package/dist/ai/guides/fetch-items-playbook.md +161 -0
- package/dist/ai/guides/floating-bottom-panel.md +191 -0
- package/dist/ai/guides/icons-policy.md +257 -0
- package/dist/ai/guides/inertia-helpers.md +1394 -0
- package/dist/ai/guides/inline-edit-playbook.md +258 -0
- package/dist/ai/guides/installation-and-plugin-setup.md +377 -0
- package/dist/ai/guides/migration/components/bb-alert.md +37 -0
- package/dist/ai/guides/migration/components/bb-avatar.md +24 -0
- package/dist/ai/guides/migration/components/bb-badge.md +110 -0
- package/dist/ai/guides/migration/components/bb-breadcrumbs.md +26 -0
- package/dist/ai/guides/migration/components/bb-button.md +55 -0
- package/dist/ai/guides/migration/components/bb-checkbox-group.md +54 -0
- package/dist/ai/guides/migration/components/bb-checkbox.md +45 -0
- package/dist/ai/guides/migration/components/bb-chip.md +35 -0
- package/dist/ai/guides/migration/components/bb-collapsible.md +26 -0
- package/dist/ai/guides/migration/components/bb-color-input.md +23 -0
- package/dist/ai/guides/migration/components/bb-color-palette.md +31 -0
- package/dist/ai/guides/migration/components/bb-date-picker-input.md +84 -0
- package/dist/ai/guides/migration/components/bb-dialog.md +46 -0
- package/dist/ai/guides/migration/components/bb-dropdown-button.md +31 -0
- package/dist/ai/guides/migration/components/bb-dropdown.md +36 -0
- package/dist/ai/guides/migration/components/bb-dropzone.md +27 -0
- package/dist/ai/guides/migration/components/bb-icon.md +27 -0
- package/dist/ai/guides/migration/components/bb-off-canvas.md +25 -0
- package/dist/ai/guides/migration/components/bb-offcanvas.md +54 -0
- package/dist/ai/guides/migration/components/bb-pagination.md +24 -0
- package/dist/ai/guides/migration/components/bb-popover.md +52 -0
- package/dist/ai/guides/migration/components/bb-rating.md +24 -0
- package/dist/ai/guides/migration/components/bb-select-popover.md +58 -0
- package/dist/ai/guides/migration/components/bb-select.md +68 -0
- package/dist/ai/guides/migration/components/bb-slider.md +25 -0
- package/dist/ai/guides/migration/components/bb-smooth-height.md +45 -0
- package/dist/ai/guides/migration/components/bb-spinner.md +23 -0
- package/dist/ai/guides/migration/components/bb-table.md +66 -0
- package/dist/ai/guides/migration/components/bb-tabs.md +47 -0
- package/dist/ai/guides/migration/components/bb-tag.md +29 -0
- package/dist/ai/guides/migration/components/bb-text-input.md +37 -0
- package/dist/ai/guides/migration/components/bb-toast.md +47 -0
- package/dist/ai/guides/migration/components/bb-tooltip.md +34 -0
- package/dist/ai/guides/migration/components/bb-tree.md +77 -0
- package/dist/ai/guides/migration/components/use-confirm.md +46 -0
- package/dist/ai/guides/migration/v2-to-v3.md +649 -0
- package/dist/ai/guides/options-items-playbook.md +222 -0
- package/dist/ai/guides/page-shell.md +314 -0
- package/dist/ai/guides/validated-forms.md +177 -0
- package/dist/ai/index.md +156 -32
- package/dist/ai/recipes/inertia/approvals-inbox.md +1598 -0
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +1733 -0
- package/dist/ai/recipes/inertia/onboarding.md +1765 -0
- package/dist/ai/recipes/inertia/ownership-atlas.md +1281 -0
- package/dist/ai/recipes/inertia/record-form.md +1442 -0
- package/dist/ai/recipes/inertia/records-workspace.md +1328 -0
- package/dist/ai/recipes/inertia/upload-center.md +1926 -0
- package/dist/ai/recipes/inertia/wizard-form.md +1477 -0
- package/dist/ai/recipes/nuxt/approvals-inbox.md +1561 -0
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +1690 -0
- package/dist/ai/recipes/nuxt/onboarding.md +1698 -0
- package/dist/ai/recipes/nuxt/record-form.md +1456 -0
- package/dist/ai/recipes/nuxt/records-workspace.md +1314 -0
- package/dist/ai/recipes/nuxt/upload-center.md +1847 -0
- package/dist/ai/recipes/nuxt/wizard-form.md +1486 -0
- package/dist/ai/recipes/vue/approvals-inbox.md +1479 -0
- package/dist/ai/recipes/vue/inline-edit-workspace.md +1581 -0
- package/dist/ai/recipes/vue/onboarding.md +1653 -0
- package/dist/ai/recipes/vue/record-form.md +1540 -0
- package/dist/ai/recipes/vue/records-workspace.md +1230 -0
- package/dist/ai/recipes/vue/upload-center.md +1806 -0
- package/dist/ai/recipes/vue/wizard-form.md +1418 -0
- package/dist/ai/source/BbAccordion.md +122 -0
- package/dist/ai/source/BbAlert.md +396 -0
- package/dist/ai/source/BbAsterisk.md +32 -0
- package/dist/ai/source/BbAvatar.md +256 -0
- package/dist/ai/source/BbBadge.md +560 -0
- package/dist/ai/source/BbBadgeButton.md +490 -0
- package/dist/ai/source/BbBaseButton.md +682 -0
- package/dist/ai/source/BbBaseCheckbox.md +459 -0
- package/dist/ai/source/BbBaseCheckboxGroup.md +554 -0
- package/dist/ai/source/BbBaseCheckboxIcon.md +201 -0
- package/dist/ai/source/BbBaseColorInput.md +413 -0
- package/dist/ai/source/BbBaseColorPalette.md +934 -0
- package/dist/ai/source/BbBaseDatePicker.md +1001 -0
- package/dist/ai/source/BbBaseDatePickerInput.md +1781 -0
- package/dist/ai/source/BbBaseInputContainer.md +1148 -0
- package/dist/ai/source/BbBaseNumberInput.md +615 -0
- package/dist/ai/source/BbBaseRadio.md +377 -0
- package/dist/ai/source/BbBaseRadioGroup.md +499 -0
- package/dist/ai/source/BbBaseRadioIcon.md +153 -0
- package/dist/ai/source/BbBaseRating.md +573 -0
- package/dist/ai/source/BbBaseSelect.md +895 -0
- package/dist/ai/source/BbBaseSlider.md +1085 -0
- package/dist/ai/source/BbBaseSwitch.md +246 -0
- package/dist/ai/source/BbBaseSwitchGroup.md +302 -0
- package/dist/ai/source/BbBaseSwitchIcon.md +206 -0
- package/dist/ai/source/BbBaseTag.md +688 -0
- package/dist/ai/source/BbBaseTextInput.md +511 -0
- package/dist/ai/source/BbBaseTextarea.md +480 -0
- package/dist/ai/source/BbBaseTimePickerInput.md +1013 -0
- package/dist/ai/source/BbBreadcrumbs.md +811 -0
- package/dist/ai/source/BbButton.md +990 -0
- package/dist/ai/source/BbCheckbox.md +319 -0
- package/dist/ai/source/BbCheckboxGroup.md +412 -0
- package/dist/ai/source/BbCollapsible.md +133 -0
- package/dist/ai/source/BbColorInput.md +315 -0
- package/dist/ai/source/BbColorPalette.md +361 -0
- package/dist/ai/source/BbConfirm.md +173 -0
- package/dist/ai/source/BbConfirmPortal.md +74 -0
- package/dist/ai/source/BbDatePicker.md +725 -0
- package/dist/ai/source/BbDatePickerInput.md +518 -0
- package/dist/ai/source/BbDialog.md +812 -0
- package/dist/ai/source/BbDropdown.md +2369 -0
- package/dist/ai/source/BbDropdownButton.md +313 -0
- package/dist/ai/source/BbDropdownGroup.md +1249 -0
- package/dist/ai/source/BbDropzone.md +749 -0
- package/dist/ai/source/BbForm.md +100 -0
- package/dist/ai/source/BbIcon.md +212 -0
- package/dist/ai/source/BbIndicator.md +298 -0
- package/dist/ai/source/BbNumberInput.md +365 -0
- package/dist/ai/source/BbOffCanvas.md +1260 -0
- package/dist/ai/source/BbPagination.md +829 -0
- package/dist/ai/source/BbPopover.md +853 -0
- package/dist/ai/source/BbProgress.md +140 -0
- package/dist/ai/source/BbRadio.md +294 -0
- package/dist/ai/source/BbRadioGroup.md +401 -0
- package/dist/ai/source/BbRating.md +362 -0
- package/dist/ai/source/BbSelect.md +681 -0
- package/dist/ai/source/BbSelectPopover.md +2209 -0
- package/dist/ai/source/BbSlider.md +371 -0
- package/dist/ai/source/BbSmoothHeight.md +261 -0
- package/dist/ai/source/BbSpinner.md +127 -0
- package/dist/ai/source/BbSwitch.md +175 -0
- package/dist/ai/source/BbSwitchGroup.md +240 -0
- package/dist/ai/source/BbTable.md +3348 -0
- package/dist/ai/source/BbTabs.md +403 -0
- package/dist/ai/source/BbTabsList.md +578 -0
- package/dist/ai/source/BbTabsPanes.md +368 -0
- package/dist/ai/source/BbTabsRoot.md +372 -0
- package/dist/ai/source/BbTag.md +383 -0
- package/dist/ai/source/BbTextInput.md +345 -0
- package/dist/ai/source/BbTextarea.md +330 -0
- package/dist/ai/source/BbTimePicker.md +591 -0
- package/dist/ai/source/BbTimePickerInput.md +367 -0
- package/dist/ai/source/BbToast.md +524 -0
- package/dist/ai/source/BbToastPortal.md +482 -0
- package/dist/ai/source/BbTooltip.md +508 -0
- package/dist/ai/source/BbTree.md +319 -0
- package/dist/ai/source/ChipsBox.md +201 -0
- package/dist/ai/source/ClearableButton.md +70 -0
- package/dist/ai/source/CommaBox.md +202 -0
- package/dist/ai/source/CommonInputWrapper.md +554 -0
- package/dist/ai/source/CommonPopover.md +606 -0
- package/dist/ai/source/ErrorIcon.md +43 -0
- package/dist/ai/source/FlatListBox.md +396 -0
- package/dist/ai/source/GroupedListBox.md +540 -0
- package/dist/ai/source/GuidesComponent.md +383 -0
- package/dist/ai/source/ListBox.md +238 -0
- package/dist/ai/source/OptionsContainer.md +191 -0
- package/dist/ai/source/WarningIcon.md +47 -0
- package/dist/alert-variants.d.ts +12 -0
- package/dist/assets/css/reset.css_inline.js +4 -0
- package/dist/assets/svgs/alert.svg_raw.js +4 -0
- package/dist/assets/svgs/calendar.svg_raw.js +4 -0
- package/dist/assets/svgs/check_stroke.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_down.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_right.svg_raw.js +4 -0
- package/dist/assets/svgs/clear.svg_raw.js +4 -0
- package/dist/assets/svgs/clock.svg_raw.js +4 -0
- package/dist/assets/svgs/error.svg_raw.js +4 -0
- package/dist/assets/svgs/search.svg_raw.js +4 -0
- package/dist/assets/svgs/sort.svg_raw.js +4 -0
- package/dist/assets/svgs/spinner.svg_raw.js +4 -0
- package/dist/assets/svgs/user.svg_raw.js +4 -0
- package/dist/badge-variants.d.ts +18 -0
- package/dist/button-variants.d.ts +15 -0
- package/dist/components/BbAccordion/BbAccordion.vue.d.ts +9 -3
- package/dist/components/BbAccordion/BbAccordion.vue.js +6 -0
- package/dist/components/BbAccordion/BbAccordion.vue_vue_type_script_setup_true_lang.js +66 -0
- package/dist/components/BbAccordion/types.d.ts +10 -7
- package/dist/components/BbAlert/BbAlert.vue.d.ts +12 -3
- package/dist/components/BbAlert/BbAlert.vue.js +6 -0
- package/dist/components/BbAlert/BbAlert.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbAlert/types.d.ts +51 -12
- package/dist/components/BbAsterisk/BbAsterisk.vue.d.ts +3 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue.js +6 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue_vue_type_script_setup_true_lang.js +13 -0
- package/dist/components/BbAvatar/BbAvatar.vue.d.ts +5 -0
- package/dist/components/BbAvatar/BbAvatar.vue.js +6 -0
- package/dist/components/BbAvatar/BbAvatar.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/components/BbAvatar/types.d.ts +4 -5
- package/dist/components/BbBadge/BadgeAppendIcon.vue.d.ts +3 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue.js +5 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue_vue_type_script_setup_true_lang.js +22 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.d.ts +15 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.js +5 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +34 -0
- package/dist/components/BbBadge/BbBadge.vue.d.ts +8 -1
- package/dist/components/BbBadge/BbBadge.vue.js +6 -0
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.d.ts +12 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.js +5 -0
- package/dist/components/BbBadge/BbBadgeButton.vue_vue_type_script_setup_true_lang.js +54 -0
- package/dist/components/BbBadge/badgeButtonContext.d.ts +27 -0
- package/dist/components/BbBadge/badgeButtonContext.js +5 -0
- package/dist/components/BbBadge/types.d.ts +61 -16
- package/dist/components/BbBaseButton/BbBaseButton.vue.d.ts +13 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue.js +6 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue_vue_type_script_setup_true_lang.js +171 -0
- package/dist/components/BbBaseButton/RouterComponent.vue.js +5 -0
- package/dist/components/BbBaseButton/RouterComponent.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/BbBaseButton/types.d.ts +191 -0
- package/dist/components/BbBaseButton/types.js +33 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.d.ts +40 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.js +6 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +145 -0
- package/dist/components/BbBaseCheckbox/types.d.ts +222 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.js +6 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseCheckboxGroup/types.d.ts +257 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.js +6 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue_vue_type_script_setup_true_lang.js +43 -0
- package/dist/components/BbBaseCheckboxIcon/types.d.ts +47 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.d.ts +42 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.js +6 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbBaseColorInput/types.d.ts +207 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.d.ts +13 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.js +6 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue_vue_type_script_setup_true_lang.js +395 -0
- package/dist/components/BbBaseColorPalette/types.d.ts +40 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue.js +6 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +300 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.d.ts +44 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/components/BbBaseDatePicker/typeahead.d.ts +112 -0
- package/dist/components/BbBaseDatePicker/typeahead.js +37 -0
- package/dist/components/BbBaseDatePicker/types.d.ts +97 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.d.ts +58 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.js +11 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +713 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue_vue_type_script_setup_true_lang.js +204 -0
- package/dist/components/BbBaseDatePickerInput/types.d.ts +309 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +17 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.js +6 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbBaseInputContainer/types.d.ts +185 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.d.ts +52 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.js +6 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue_vue_type_script_setup_true_lang.js +211 -0
- package/dist/components/BbBaseNumberInput/types.d.ts +211 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.d.ts +33 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.js +6 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +114 -0
- package/dist/components/BbBaseRadio/types.d.ts +179 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.js +6 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue_vue_type_script_setup_true_lang.js +172 -0
- package/dist/components/BbBaseRadioGroup/types.d.ts +215 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.js +6 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/BbBaseRadioIcon/types.d.ts +41 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.d.ts +38 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.js +6 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbBaseRating/types.d.ts +168 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.d.ts +19 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.js +6 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +307 -0
- package/dist/components/BbBaseSelect/types.d.ts +294 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.d.ts +35 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.js +6 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +324 -0
- package/dist/components/BbBaseSlider/types.d.ts +200 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.d.ts +35 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.js +6 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbBaseSwitch/types.d.ts +10 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.js +6 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseSwitchGroup/types.d.ts +8 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.js +6 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbBaseSwitchIcon/types.d.ts +48 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.d.ts +50 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.js +6 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue_vue_type_script_setup_true_lang.js +266 -0
- package/dist/components/BbBaseTag/types.d.ts +160 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.d.ts +45 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.js +6 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue_vue_type_script_setup_true_lang.js +200 -0
- package/dist/components/BbBaseTextInput/types.d.ts +209 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.d.ts +39 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.js +6 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue_vue_type_script_setup_true_lang.js +175 -0
- package/dist/components/BbBaseTextarea/types.d.ts +170 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.d.ts +27 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.js +6 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +517 -0
- package/dist/components/BbBaseTimePickerInput/types.d.ts +151 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.d.ts +0 -1
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.js +6 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +224 -0
- package/dist/components/BbBreadcrumbs/estimateBreadcrumbItemWidth.js +47 -0
- package/dist/components/BbBreadcrumbs/types.d.ts +33 -22
- package/dist/components/BbButton/BbButton.vue.d.ts +6 -5
- package/dist/components/BbButton/BbButton.vue.js +6 -0
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbButton/types.d.ts +48 -47
- package/dist/components/BbCheckbox/BbCheckbox.vue.d.ts +4 -3
- package/dist/components/BbCheckbox/BbCheckbox.vue.js +6 -0
- package/dist/components/BbCheckbox/BbCheckbox.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbCheckbox/types.d.ts +24 -10
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.d.ts +2 -13
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.js +6 -0
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbCheckboxGroup/types.d.ts +44 -23
- package/dist/components/BbCollapsible/BbCollapsible.vue.d.ts +2 -1
- package/dist/components/BbCollapsible/BbCollapsible.vue.js +6 -0
- package/dist/components/BbCollapsible/BbCollapsible.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/components/BbCollapsible/types.d.ts +10 -9
- package/dist/components/BbColorInput/BbColorInput.vue.d.ts +10 -1
- package/dist/components/BbColorInput/BbColorInput.vue.js +6 -0
- package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbColorInput/types.d.ts +37 -11
- package/dist/components/BbColorPalette/BbColorPalette.vue.d.ts +5 -8
- package/dist/components/BbColorPalette/BbColorPalette.vue.js +5 -0
- package/dist/components/BbColorPalette/BbColorPalette.vue_vue_type_script_setup_true_lang.js +125 -0
- package/dist/components/BbColorPalette/types.d.ts +66 -53
- package/dist/components/BbConfirm/BbConfirm.vue.js +6 -0
- package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +130 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue.d.ts +5 -6
- package/dist/components/BbConfirm/BbConfirmPortal.vue.js +5 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +37 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.js +6 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +319 -0
- package/dist/components/BbDatePicker/types.d.ts +155 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.d.ts +3 -1
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.js +6 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +179 -0
- package/dist/components/BbDatePickerInput/types.d.ts +127 -16
- package/dist/components/BbDialog/BbDialog.vue.d.ts +8 -6
- package/dist/components/BbDialog/BbDialog.vue.js +6 -0
- package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +214 -0
- package/dist/components/BbDialog/types.d.ts +146 -1
- package/dist/components/BbDialog/useDialogStack.d.ts +5 -0
- package/dist/components/BbDialog/useDialogStack.js +20 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.d.ts +18 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +116 -0
- package/dist/components/BbDropdown/BbDropdown.vue.d.ts +14 -7
- package/dist/components/BbDropdown/BbDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +560 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.d.ts +44 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.js +5 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.d.ts +27 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +464 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.d.ts +16 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.js +5 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue_vue_type_script_setup_true_lang.js +168 -0
- package/dist/components/BbDropdown/normalizeGroups.d.ts +18 -0
- package/dist/components/BbDropdown/normalizeGroups.js +46 -0
- package/dist/components/BbDropdown/types.d.ts +457 -45
- package/dist/components/BbDropdown/types.js +4 -0
- package/dist/components/BbDropdown/useDropdownContext.d.ts +89 -0
- package/dist/components/BbDropdown/useDropdownContext.js +9 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.d.ts +108 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.js +5 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.d.ts +3 -3
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.js +6 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +119 -0
- package/dist/components/BbDropdownButton/types.d.ts +45 -52
- package/dist/components/BbDropzone/BbDropzone.vue.d.ts +5 -4
- package/dist/components/BbDropzone/BbDropzone.vue.js +6 -0
- package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/components/BbDropzone/types.d.ts +55 -7
- package/dist/components/BbIcon/BbIcon.vue.d.ts +3 -2
- package/dist/components/BbIcon/BbIcon.vue.js +6 -0
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbIcon/types.d.ts +6 -19
- package/dist/components/BbIndicator/BbIndicator.vue.d.ts +14 -0
- package/dist/components/BbIndicator/BbIndicator.vue.js +6 -0
- package/dist/components/BbIndicator/BbIndicator.vue_vue_type_script_setup_true_lang.js +56 -0
- package/dist/components/BbIndicator/types.d.ts +49 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue.d.ts +12 -6
- package/dist/components/BbNumberInput/BbNumberInput.vue.js +6 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +194 -0
- package/dist/components/BbNumberInput/types.d.ts +31 -4
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +10 -10
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.js +6 -0
- package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/components/BbOffCanvas/types.d.ts +71 -39
- package/dist/components/BbOffCanvas/useOffCanvasStack.d.ts +25 -0
- package/dist/components/BbOffCanvas/useOffCanvasStack.js +68 -0
- package/dist/components/BbPagination/BbPagination.vue.d.ts +3 -4
- package/dist/components/BbPagination/BbPagination.vue.js +6 -0
- package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbPagination/paginationFit.d.ts +38 -0
- package/dist/components/BbPagination/paginationFit.js +13 -0
- package/dist/components/BbPagination/types.d.ts +51 -16
- package/dist/components/BbPopover/BbPopover.vue.d.ts +10 -15
- package/dist/components/BbPopover/BbPopover.vue.js +6 -0
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/components/BbPopover/escapeStack.d.ts +6 -0
- package/dist/components/BbPopover/escapeStack.js +10 -0
- package/dist/components/BbPopover/types.d.ts +84 -31
- package/dist/components/BbProgress/BbProgress.vue.d.ts +1 -0
- package/dist/components/BbProgress/BbProgress.vue.js +6 -0
- package/dist/components/BbProgress/BbProgress.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbProgress/types.d.ts +15 -1
- package/dist/components/BbRadio/BbRadio.vue.d.ts +5 -3
- package/dist/components/BbRadio/BbRadio.vue.js +6 -0
- package/dist/components/BbRadio/BbRadio.vue_vue_type_script_setup_true_lang.js +133 -0
- package/dist/components/BbRadio/types.d.ts +23 -9
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.d.ts +4 -14
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.js +6 -0
- package/dist/components/BbRadioGroup/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbRadioGroup/types.d.ts +41 -22
- package/dist/components/BbRating/BbRating.vue.d.ts +4 -1
- package/dist/components/BbRating/BbRating.vue.js +6 -0
- package/dist/components/BbRating/BbRating.vue_vue_type_script_setup_true_lang.js +150 -0
- package/dist/components/BbRating/types.d.ts +37 -10
- package/dist/components/BbSelect/BbSelect.vue.d.ts +2 -11
- package/dist/components/BbSelect/BbSelect.vue.js +6 -0
- package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +241 -0
- package/dist/components/BbSelect/types.d.ts +105 -59
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.d.ts +1 -10
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.js +6 -0
- package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +637 -0
- package/dist/components/BbSelectPopover/types.d.ts +189 -102
- package/dist/components/BbSlider/BbSlider.vue.d.ts +3 -6
- package/dist/components/BbSlider/BbSlider.vue.js +6 -0
- package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/components/BbSlider/types.d.ts +37 -13
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.d.ts +3 -1
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.js +6 -0
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue_vue_type_script_setup_true_lang.js +79 -0
- package/dist/components/BbSmoothHeight/types.d.ts +13 -2
- package/dist/components/BbSpinner/BbSpinner.vue.d.ts +1 -1
- package/dist/components/BbSpinner/BbSpinner.vue.js +6 -0
- package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbSpinner/types.d.ts +10 -3
- package/dist/components/BbSwitch/BbSwitch.vue.d.ts +5 -3
- package/dist/components/BbSwitch/BbSwitch.vue.js +6 -0
- package/dist/components/BbSwitch/BbSwitch.vue_vue_type_script_setup_true_lang.js +142 -0
- package/dist/components/BbSwitch/types.d.ts +3 -1
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.d.ts +4 -14
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.js +6 -0
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbSwitchGroup/types.d.ts +3 -22
- package/dist/components/BbTable/BbTable.vue.d.ts +53 -31
- package/dist/components/BbTable/BbTable.vue.js +6 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +889 -0
- package/dist/components/BbTable/types.d.ts +176 -37
- package/dist/components/BbTable/utils.d.ts +9 -1
- package/dist/components/BbTable/utils.js +53 -0
- package/dist/components/BbTabs/BbTabs.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabs.vue.js +6 -0
- package/dist/components/BbTabs/BbTabs.vue_vue_type_script_setup_true_lang.js +64 -0
- package/dist/components/BbTabs/BbTabsList.vue.d.ts +19 -0
- package/dist/components/BbTabs/BbTabsList.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsList.vue_vue_type_script_setup_true_lang.js +123 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.d.ts +16 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsPanes.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.js +5 -0
- package/dist/components/BbTabs/BbTabsRoot.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTabs/types.d.ts +264 -0
- package/dist/components/BbTag/BbTag.vue.d.ts +19 -6
- package/dist/components/BbTag/BbTag.vue.js +6 -0
- package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +187 -0
- package/dist/components/BbTag/types.d.ts +48 -17
- package/dist/components/BbTextInput/BbTextInput.vue.d.ts +10 -1
- package/dist/components/BbTextInput/BbTextInput.vue.js +6 -0
- package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +195 -0
- package/dist/components/BbTextInput/types.d.ts +24 -4
- package/dist/components/BbTextarea/BbTextarea.vue.d.ts +10 -1
- package/dist/components/BbTextarea/BbTextarea.vue.js +6 -0
- package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbTextarea/types.d.ts +26 -2
- package/dist/components/BbTimePicker/BbTimePicker.vue.d.ts +33 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue.js +6 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbTimePicker/types.d.ts +129 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.d.ts +25 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.js +6 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +157 -0
- package/dist/components/BbTimePickerInput/types.d.ts +165 -0
- package/dist/components/BbToast/BbToast.vue.d.ts +2 -3
- package/dist/components/BbToast/BbToast.vue.js +6 -0
- package/dist/components/BbToast/BbToast.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/BbToast/BbToastMessage.vue.d.ts +17 -8
- package/dist/components/BbToast/BbToastMessage.vue.js +5 -0
- package/dist/components/BbToast/BbToastMessage.vue_vue_type_script_setup_true_lang.js +170 -0
- package/dist/components/BbToast/BbToastPortal.vue.d.ts +30 -0
- package/dist/components/BbToast/BbToastPortal.vue.js +5 -0
- package/dist/components/BbToast/BbToastPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbToast/BbToastRegion.vue.d.ts +8 -0
- package/dist/components/BbToast/BbToastRegion.vue.js +5 -0
- package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/components/BbToast/types.d.ts +69 -5
- package/dist/components/BbTooltip/BbTooltip.vue.d.ts +4 -3
- package/dist/components/BbTooltip/BbTooltip.vue.js +6 -0
- package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/BbTooltip/types.d.ts +61 -11
- package/dist/components/BbTree/BbTree.vue.d.ts +26 -13
- package/dist/components/BbTree/BbTree.vue.js +6 -0
- package/dist/components/BbTree/BbTree.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTree/BbTreeLevel.vue.d.ts +19 -0
- package/dist/components/BbTree/BbTreeLevel.vue.js +5 -0
- package/dist/components/BbTree/BbTreeLevel.vue_vue_type_script_setup_true_lang.js +94 -0
- package/dist/components/BbTree/context.d.ts +17 -0
- package/dist/components/BbTree/context.js +4 -0
- package/dist/components/BbTree/types.d.ts +93 -42
- package/dist/components/ChipsBox/ChipsBox.vue.d.ts +2 -1
- package/dist/components/ChipsBox/ChipsBox.vue.js +5 -0
- package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +98 -0
- package/dist/components/ChipsBox/types.d.ts +12 -5
- package/dist/components/ClearableButton/ClearableButton.vue.d.ts +2 -1
- package/dist/components/ClearableButton/ClearableButton.vue.js +6 -0
- package/dist/components/ClearableButton/ClearableButton.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/ClearableButton/types.d.ts +7 -1
- package/dist/components/CloseIcon/CloseButton.vue.d.ts +5 -0
- package/dist/components/CloseIcon/CloseButton.vue.js +7 -0
- package/dist/components/CloseIcon/CloseButton.vue_vue_type_script_setup_true_lang.js +23 -0
- package/dist/components/CloseIcon/types.d.ts +2 -0
- package/dist/components/CommaBox/CommaBox.vue.d.ts +2 -1
- package/dist/components/CommaBox/CommaBox.vue.js +6 -0
- package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/components/CommaBox/types.d.ts +6 -7
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.d.ts +19 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.js +6 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue_vue_type_script_setup_true_lang.js +107 -0
- package/dist/components/CommonInputWrapper/types.d.ts +96 -0
- package/dist/components/CommonPopover/CommonPopover.vue.d.ts +3 -3
- package/dist/components/CommonPopover/CommonPopover.vue.js +6 -0
- package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/CommonPopover/types.d.ts +61 -16
- package/dist/components/ErrorIcon/ErrorIcon.vue.d.ts +2 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue.js +6 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/FlatListBox/FlatListBox.vue.d.ts +2 -5
- package/dist/components/FlatListBox/FlatListBox.vue.js +5 -0
- package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +167 -0
- package/dist/components/FlatListBox/types.d.ts +1 -1
- package/dist/components/GroupedListBox/GroupedListBox.vue.d.ts +2 -5
- package/dist/components/GroupedListBox/GroupedListBox.vue.js +5 -0
- package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +218 -0
- package/dist/components/ListBox/ListBox.vue.d.ts +3 -6
- package/dist/components/ListBox/ListBox.vue.js +5 -0
- package/dist/components/ListBox/ListBox.vue_vue_type_script_setup_true_lang.js +112 -0
- package/dist/components/ListBox/types.d.ts +1 -1
- package/dist/components/OptionsContainer/OptionsContainer.vue.d.ts +1 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue.js +5 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/components/OptionsContainer/types.d.ts +14 -24
- package/dist/components/Transitions/ScaleFade.vue.js +5 -0
- package/dist/components/Transitions/ScaleFade.vue_vue_type_script_setup_true_lang.js +47 -0
- package/dist/components/Transitions/Slide.vue.js +7 -0
- package/dist/components/Transitions/Slide.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/WarningIcon/WarningIcon.vue.d.ts +5 -0
- package/dist/components/WarningIcon/WarningIcon.vue.js +6 -0
- package/dist/components/WarningIcon/WarningIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/WarningIcon/types.d.ts +3 -0
- package/dist/composables/__testResets.d.ts +1 -0
- package/dist/composables/useActiveInactiveState.d.ts +4 -0
- package/dist/composables/useActiveInactiveState.js +21 -0
- package/dist/composables/useBaseOptions.d.ts +3 -3
- package/dist/composables/useBaseOptions.js +31 -0
- package/dist/composables/useBbConfig.d.ts +8 -37
- package/dist/composables/useBbConfig.js +5 -0
- package/dist/composables/useBbDropdownContext.d.ts +89 -0
- package/dist/composables/useBbDropdownContext.js +46 -0
- package/dist/composables/useBbFormContext.d.ts +90 -0
- package/dist/composables/useBbFormContext.js +69 -0
- package/dist/composables/useBbTableContext.d.ts +195 -0
- package/dist/composables/useBbTableContext.js +208 -0
- package/dist/composables/useBbTabsContext.d.ts +111 -0
- package/dist/composables/useBbTabsContext.js +219 -0
- package/dist/composables/useBreakpoint.d.ts +13 -0
- package/dist/composables/useBreakpoint.js +39 -0
- package/dist/composables/useBroadcastChannelInstance.d.ts +1 -3
- package/dist/composables/useBroadcastChannelInstance.js +39 -0
- package/dist/composables/useCancelableFunction.js +37 -0
- package/dist/composables/useCoherence.js +16 -0
- package/dist/composables/useConfirm.d.ts +257 -67
- package/dist/composables/useConfirm.js +122 -0
- package/dist/composables/useCountdown.js +42 -0
- package/dist/composables/useHashedWatcher.d.ts +17 -0
- package/dist/composables/useHashedWatcher.js +14 -0
- package/dist/composables/useId.d.ts +3 -2
- package/dist/composables/useIndexById.js +16 -0
- package/dist/composables/useInertiaPage.d.ts +10 -0
- package/dist/composables/useInertiaPage.js +8 -0
- package/dist/composables/useItemText.js +17 -0
- package/dist/composables/useItemValue.js +9 -0
- package/dist/composables/useItemsGetter.d.ts +1 -1
- package/dist/composables/useItemsGetter.js +30 -0
- package/dist/composables/useListboxFocus.js +66 -0
- package/dist/composables/useLocale.d.ts +1 -1
- package/dist/composables/useLocale.js +11 -0
- package/dist/composables/useLogger.d.ts +1 -1
- package/dist/composables/useLogger.js +10 -0
- package/dist/composables/useMinimalQueue.d.ts +1 -1
- package/dist/composables/useMobile.js +7 -0
- package/dist/composables/useOnFalsy.d.ts +2 -0
- package/dist/composables/useOnFalsy.js +9 -0
- package/dist/composables/useOnTruthy.d.ts +7 -0
- package/dist/composables/useOnTruthy.js +9 -0
- package/dist/composables/usePrefill.d.ts +2 -1
- package/dist/composables/usePrefill.js +24 -0
- package/dist/composables/useQuery.d.ts +15 -3
- package/dist/composables/useQuery.js +42 -0
- package/dist/composables/useRoute.js +11 -0
- package/dist/composables/useRouter.js +8 -0
- package/dist/composables/useSafeArea.d.ts +51 -0
- package/dist/composables/useSafeArea.js +16 -0
- package/dist/composables/useSegmentedFields.d.ts +103 -0
- package/dist/composables/useSegmentedFields.js +152 -0
- package/dist/composables/useTableWidthContext.d.ts +5 -0
- package/dist/composables/useTableWidthContext.js +42 -0
- package/dist/composables/useToast.d.ts +17 -73
- package/dist/composables/useToast.js +49 -0
- package/dist/composables/useTopLayer.d.ts +9 -0
- package/dist/composables/useTopLayer.js +7 -0
- package/dist/composables/useUntil.js +13 -0
- package/dist/composables/useViewportWidth.d.ts +17 -0
- package/dist/composables/useViewportWidth.js +6 -0
- package/dist/confirm-variants.d.ts +10 -0
- package/dist/deprecation/ai-deprecations.json.d.ts +784 -0
- package/dist/deprecation/ai-deprecations.json.js +4 -0
- package/dist/deprecation/removedProps.d.ts +3 -0
- package/dist/deprecation/removedProps.js +13 -0
- package/dist/directives/bbColor.d.ts +65 -0
- package/dist/directives/bbColor.js +24 -0
- package/dist/directives/bbDate.d.ts +66 -0
- package/dist/directives/bbDate.js +24 -0
- package/dist/directives/bbDropdown.d.ts +15 -11
- package/dist/directives/bbDropdown.js +23 -0
- package/dist/directives/bbTime.d.ts +64 -0
- package/dist/directives/bbTime.js +24 -0
- package/dist/directives/bbTooltip.d.ts +16 -11
- package/dist/directives/bbTooltip.js +24 -0
- package/dist/dropdown-item-variants.d.ts +10 -0
- package/dist/i18n/index.d.ts +51 -3
- package/dist/i18n/index.js +27 -0
- package/dist/i18n/locales/cs.json +115 -0
- package/dist/i18n/locales/da.json +115 -0
- package/dist/i18n/locales/de.json +115 -0
- package/dist/i18n/locales/el.json +115 -0
- package/dist/i18n/locales/en.json +115 -0
- package/dist/i18n/locales/es.json +115 -0
- package/dist/i18n/locales/fi.json +115 -0
- package/dist/i18n/locales/fr.json +115 -0
- package/dist/i18n/locales/hu.json +115 -0
- package/dist/i18n/locales/it.json +115 -0
- package/dist/i18n/locales/ja.json +115 -0
- package/dist/i18n/locales/ko.json +115 -0
- package/dist/i18n/locales/nb.json +115 -0
- package/dist/i18n/locales/nl.json +115 -0
- package/dist/i18n/locales/pl.json +115 -0
- package/dist/i18n/locales/pt.json +115 -0
- package/dist/i18n/locales/ro.json +115 -0
- package/dist/i18n/locales/ru.json +115 -0
- package/dist/i18n/locales/sv.json +115 -0
- package/dist/i18n/locales/tr.json +115 -0
- package/dist/i18n/locales/uk.json +115 -0
- package/dist/i18n/locales/zh-cn.json +115 -0
- package/dist/icons-whitelist.d.ts +8 -0
- package/dist/index.d.ts +94 -79
- package/dist/index.js +76 -82
- package/dist/indicator-variants.d.ts +13 -0
- package/dist/llms-full.txt +77073 -0
- package/dist/llms-medium.txt +1334 -0
- package/dist/llms.txt +103 -0
- package/dist/locale-blueprint.json +141 -0
- package/dist/nuxt-module.d.ts +3 -0
- package/dist/nuxt.js +19 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.js +22 -0
- package/dist/reset.css +1 -0
- package/dist/reset.d.ts +0 -0
- package/dist/runtime/nuxt-plugin.d.ts +2 -0
- package/dist/runtime/nuxt-plugin.js +24 -0
- package/dist/styles.css +2 -0
- package/dist/styles.d.ts +0 -0
- package/dist/toast-variants.d.ts +13 -0
- package/dist/tooltip-variants.d.ts +10 -0
- package/dist/types/ActionButton.d.ts +32 -0
- package/dist/types/AlertVariant.d.ts +16 -0
- package/dist/types/BadgeVariant.d.ts +16 -0
- package/dist/types/ButtonVariant.d.ts +17 -0
- package/dist/types/Classes.d.ts +1 -1
- package/dist/types/ColumnName.d.ts +1 -1
- package/dist/types/CommonProps.d.ts +66 -279
- package/dist/types/Config.d.ts +84 -0
- package/dist/types/ConfirmVariant.d.ts +15 -0
- package/dist/types/DropdownItemVariant.d.ts +18 -0
- package/dist/types/Icon.d.ts +5 -7
- package/dist/types/IndicatorVariant.d.ts +16 -0
- package/dist/types/InputDirection.d.ts +15 -0
- package/dist/types/ItemAccessor.d.ts +21 -0
- package/dist/types/Placement.d.ts +6 -0
- package/dist/types/ResponsiveSize.d.ts +20 -0
- package/dist/types/SizeScale.d.ts +8 -0
- package/dist/types/ToastVariant.d.ts +17 -0
- package/dist/types/TooltipVariant.d.ts +18 -0
- package/dist/utilities/functions/bytesToUnit.d.ts +2 -0
- package/dist/utilities/functions/bytesToUnit.js +18 -0
- package/dist/utilities/functions/clampPrecision.js +10 -0
- package/dist/utilities/functions/convertDateValue.d.ts +23 -10
- package/dist/utilities/functions/convertDateValue.js +12 -0
- package/dist/utilities/functions/deburr.js +5 -0
- package/dist/utilities/functions/empty.js +5 -0
- package/dist/utilities/functions/flattenTree.d.ts +1 -1
- package/dist/utilities/functions/hash.js +5 -0
- package/dist/utilities/functions/isDateDisabled.d.ts +1 -0
- package/dist/utilities/functions/isDateDisabled.js +20 -0
- package/dist/utilities/functions/isEqual.js +5 -0
- package/dist/utilities/functions/isNotEmpty.js +5 -0
- package/dist/utilities/functions/matchAnyKey.d.ts +1 -1
- package/dist/utilities/functions/matchAnyKey.js +15 -0
- package/dist/utilities/functions/parseSize.d.ts +1 -1
- package/dist/utilities/functions/parseSize.js +15 -0
- package/dist/utilities/functions/percentage.js +11 -0
- package/dist/utilities/functions/pipe.d.ts +1 -1
- package/dist/utilities/functions/reject.d.ts +25 -0
- package/dist/utilities/functions/reject.js +28 -0
- package/dist/utilities/functions/resolveEnumProp.d.ts +19 -0
- package/dist/utilities/functions/resolveEnumProp.js +5 -0
- package/dist/utilities/functions/slotKey.d.ts +13 -0
- package/dist/utilities/functions/slotKey.js +4 -0
- package/dist/utilities/functions/sort.d.ts +2 -2
- package/dist/utilities/functions/sort.js +19 -0
- package/dist/utilities/functions/throttle.js +5 -0
- package/dist/utilities/functions/waitFor.d.ts +9 -2
- package/dist/utilities/functions/waitFor.js +14 -0
- package/dist/utilities/functions/warnRemovedAttrs.d.ts +11 -0
- package/dist/utilities/functions/warnRemovedAttrs.js +20 -0
- package/dist/utilities/functions/when.js +5 -0
- package/dist/utilities/misc/iconsInjectionKey.d.ts +15 -0
- package/dist/utilities/misc/iconsInjectionKey.js +4 -0
- package/dist/utilities/misc/transitions.d.ts +5 -0
- package/dist/utilities/misc/transitions.js +8 -0
- package/dist/utils/injectLibraryStyles.d.ts +6 -0
- package/dist/utils/injectLibraryStyles.js +38 -0
- package/dist/validated/BbCheckbox.vue.d.ts +29 -0
- package/dist/validated/BbCheckbox.vue.js +5 -0
- package/dist/validated/BbCheckbox.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbCheckboxGroup.vue.d.ts +34 -0
- package/dist/validated/BbCheckboxGroup.vue.js +5 -0
- package/dist/validated/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +76 -0
- package/dist/validated/BbColorInput.vue.d.ts +36 -0
- package/dist/validated/BbColorInput.vue.js +5 -0
- package/dist/validated/BbColorInput.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/validated/BbDatePickerInput.vue.d.ts +40 -0
- package/dist/validated/BbDatePickerInput.vue.js +5 -0
- package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +87 -0
- package/dist/validated/BbDropzone.vue.d.ts +24 -0
- package/dist/validated/BbDropzone.vue.js +5 -0
- package/dist/validated/BbDropzone.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/validated/BbForm.vue.d.ts +28 -0
- package/dist/validated/BbForm.vue.js +5 -0
- package/dist/validated/BbForm.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/validated/BbNumberInput.vue.d.ts +36 -0
- package/dist/validated/BbNumberInput.vue.js +5 -0
- package/dist/validated/BbNumberInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbRadioGroup.vue.d.ts +34 -0
- package/dist/validated/BbRadioGroup.vue.js +5 -0
- package/dist/validated/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbRating.vue.d.ts +30 -0
- package/dist/validated/BbRating.vue.js +5 -0
- package/dist/validated/BbRating.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/validated/BbSelect.vue.d.ts +46 -0
- package/dist/validated/BbSelect.vue.js +5 -0
- package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/validated/BbSlider.vue.d.ts +36 -0
- package/dist/validated/BbSlider.vue.js +5 -0
- package/dist/validated/BbSlider.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/validated/BbSwitch.vue.d.ts +29 -0
- package/dist/validated/BbSwitch.vue.js +5 -0
- package/dist/validated/BbSwitch.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbSwitchGroup.vue.d.ts +34 -0
- package/dist/validated/BbSwitchGroup.vue.js +5 -0
- package/dist/validated/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +76 -0
- package/dist/validated/BbTag.vue.d.ts +36 -0
- package/dist/validated/BbTag.vue.js +5 -0
- package/dist/validated/BbTag.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextInput.vue.d.ts +36 -0
- package/dist/validated/BbTextInput.vue.js +5 -0
- package/dist/validated/BbTextInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextarea.vue.d.ts +36 -0
- package/dist/validated/BbTextarea.vue.js +5 -0
- package/dist/validated/BbTextarea.vue_vue_type_script_setup_true_lang.js +70 -0
- package/dist/validated/BbTimePickerInput.vue.d.ts +36 -0
- package/dist/validated/BbTimePickerInput.vue.js +5 -0
- package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/validated/index.d.ts +29 -0
- package/dist/validated/useValidatedField.d.ts +52 -0
- package/dist/validated/useValidatedField.js +51 -0
- package/dist/validated.js +21 -0
- package/dist/vite-plugin.d.ts +217 -0
- package/dist/vite.js +530 -0
- package/llms.txt +105 -0
- package/package.json +161 -59
- package/scripts/lib/__fixtures__/validated-manifest.json +65 -0
- package/scripts/lib/ensure-manifest.mjs +39 -0
- package/scripts/lib/eslint-plugin-autofix.node.test.mjs +109 -0
- package/scripts/lib/eslint-plugin.mjs +285 -0
- package/scripts/lib/eslint-plugin.node.test.mjs +161 -0
- package/scripts/lib/html-attributes.mjs +191 -0
- package/scripts/lib/mcp-config.mjs +126 -0
- package/scripts/lib/migrate-fix.mjs +115 -0
- package/scripts/lib/validate-bb-markup.mjs +421 -0
- package/dist/ai/BaseButton.md +0 -449
- package/dist/ai/BaseCheckbox.md +0 -512
- package/dist/ai/BaseCheckboxGroup.md +0 -597
- package/dist/ai/BaseColorInput.md +0 -461
- package/dist/ai/BaseDatePicker.md +0 -739
- package/dist/ai/BaseDatePickerInput.md +0 -1517
- package/dist/ai/BaseDialog.md +0 -610
- package/dist/ai/BaseInputContainer.md +0 -570
- package/dist/ai/BaseNumberInput.md +0 -509
- package/dist/ai/BaseRadio.md +0 -405
- package/dist/ai/BaseRadioGroup.md +0 -535
- package/dist/ai/BaseRating.md +0 -489
- package/dist/ai/BaseSelect.md +0 -1742
- package/dist/ai/BaseSlider.md +0 -871
- package/dist/ai/BaseSwitch.md +0 -322
- package/dist/ai/BaseSwitchGroup.md +0 -298
- package/dist/ai/BaseTag.md +0 -623
- package/dist/ai/BaseTextInput.md +0 -392
- package/dist/ai/BaseTextarea.md +0 -398
- package/dist/ai/BbChip.md +0 -147
- package/dist/ai/BbIntersection.md +0 -120
- package/dist/ai/BbRatio.md +0 -61
- package/dist/ai/BbRows.md +0 -307
- package/dist/ai/BbTab.md +0 -950
- package/dist/ai/CommonInputInnerContainer.md +0 -414
- package/dist/ai/CommonInputOuterContainer.md +0 -55
- package/dist/components/BaseButton/BaseButton.vue.d.ts +0 -14
- package/dist/components/BaseButton/types.d.ts +0 -159
- package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +0 -39
- package/dist/components/BaseCheckbox/types.d.ts +0 -213
- package/dist/components/BaseCheckboxGroup/BaseCheckboxGroup.vue.d.ts +0 -30
- package/dist/components/BaseCheckboxGroup/types.d.ts +0 -282
- package/dist/components/BaseColorInput/BaseColorInput.vue.d.ts +0 -35
- package/dist/components/BaseColorInput/ColorPalette.vue.d.ts +0 -13
- package/dist/components/BaseColorInput/types.d.ts +0 -233
- package/dist/components/BaseDatePicker/BaseDatePickerHeader.vue.d.ts +0 -15
- package/dist/components/BaseDatePicker/BaseDatePickerInputDaySelector.vue.d.ts +0 -58
- package/dist/components/BaseDatePicker/BaseDatePickerMonthSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/BaseDatePickerYearSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/types.d.ts +0 -100
- package/dist/components/BaseDatePickerInput/BaseDatePickerInput.vue.d.ts +0 -26
- package/dist/components/BaseDatePickerInput/types.d.ts +0 -206
- package/dist/components/BaseDialog/BaseDialog.vue.d.ts +0 -37
- package/dist/components/BaseDialog/types.d.ts +0 -180
- package/dist/components/BaseInputContainer/BaseInputContainer.vue.d.ts +0 -18
- package/dist/components/BaseInputContainer/types.d.ts +0 -126
- package/dist/components/BaseNumberInput/BaseNumberInput.vue.d.ts +0 -46
- package/dist/components/BaseNumberInput/types.d.ts +0 -191
- package/dist/components/BaseRadio/BaseRadio.vue.d.ts +0 -31
- package/dist/components/BaseRadio/types.d.ts +0 -173
- package/dist/components/BaseRadioGroup/BaseRadioGroup.vue.d.ts +0 -30
- package/dist/components/BaseRadioGroup/types.d.ts +0 -240
- package/dist/components/BaseRating/BaseRating.vue.d.ts +0 -35
- package/dist/components/BaseRating/types.d.ts +0 -144
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +0 -28
- package/dist/components/BaseSelect/types.d.ts +0 -470
- package/dist/components/BaseSlider/BaseSlider.vue.d.ts +0 -38
- package/dist/components/BaseSlider/types.d.ts +0 -201
- package/dist/components/BaseSwitch/BaseSwitch.vue.d.ts +0 -34
- package/dist/components/BaseSwitch/types.d.ts +0 -25
- package/dist/components/BaseSwitchGroup/BaseSwitchGroup.vue.d.ts +0 -30
- package/dist/components/BaseSwitchGroup/types.d.ts +0 -8
- package/dist/components/BaseTag/BaseTag.vue.d.ts +0 -37
- package/dist/components/BaseTag/types.d.ts +0 -136
- package/dist/components/BaseTextInput/BaseTextInput.vue.d.ts +0 -33
- package/dist/components/BaseTextInput/types.d.ts +0 -166
- package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +0 -31
- package/dist/components/BaseTextarea/types.d.ts +0 -151
- package/dist/components/BbChip/BbChip.vue.d.ts +0 -17
- package/dist/components/BbChip/types.d.ts +0 -23
- package/dist/components/BbIntersection/BbIntersection.vue.d.ts +0 -23
- package/dist/components/BbIntersection/types.d.ts +0 -36
- package/dist/components/BbRatio/BbRatio.vue.d.ts +0 -14
- package/dist/components/BbRatio/types.d.ts +0 -15
- package/dist/components/BbRows/BbRows.vue.d.ts +0 -62
- package/dist/components/BbRows/types.d.ts +0 -21
- package/dist/components/BbTab/BbTab.vue.d.ts +0 -27
- package/dist/components/BbTab/types.d.ts +0 -186
- package/dist/components/CommonInputInnerContainer/CommonInputInnerContainer.vue.d.ts +0 -15
- package/dist/components/CommonInputInnerContainer/types.d.ts +0 -47
- package/dist/components/CommonInputOuterContainer/CommonInputOuterContainer.vue.d.ts +0 -11
- package/dist/components/CommonInputOuterContainer/types.d.ts +0 -16
- package/dist/composables/useModalsState.d.ts +0 -5
- package/dist/composables/useQueue.d.ts +0 -58
- package/dist/composables/useWizard.d.ts +0 -85
- package/dist/i18n/en.d.ts +0 -67
- package/dist/i18n/it.d.ts +0 -67
- package/dist/index.css +0 -2
- package/dist/index100.js +0 -6
- package/dist/index101.js +0 -300
- package/dist/index102.js +0 -16
- package/dist/index103.js +0 -16
- package/dist/index104.js +0 -27
- package/dist/index109.js +0 -19
- package/dist/index11.js +0 -42
- package/dist/index110.js +0 -12
- package/dist/index111.js +0 -19
- package/dist/index112.js +0 -25
- package/dist/index114.js +0 -7
- package/dist/index115.js +0 -241
- package/dist/index116.js +0 -5
- package/dist/index117.js +0 -42
- package/dist/index118.js +0 -5
- package/dist/index119.js +0 -53
- package/dist/index120.js +0 -5
- package/dist/index121.js +0 -32
- package/dist/index122.js +0 -5
- package/dist/index124.js +0 -52
- package/dist/index125.js +0 -5
- package/dist/index126.js +0 -223
- package/dist/index128.js +0 -6
- package/dist/index129.js +0 -162
- package/dist/index13.js +0 -92
- package/dist/index130.js +0 -25
- package/dist/index131.js +0 -51
- package/dist/index132.js +0 -5
- package/dist/index133.js +0 -607
- package/dist/index135.js +0 -6
- package/dist/index136.js +0 -14
- package/dist/index137.js +0 -153
- package/dist/index139.js +0 -6
- package/dist/index14.js +0 -306
- package/dist/index140.js +0 -224
- package/dist/index141.js +0 -5
- package/dist/index143.js +0 -128
- package/dist/index145.js +0 -6
- package/dist/index146.js +0 -8
- package/dist/index147.js +0 -182
- package/dist/index149.js +0 -6
- package/dist/index15.js +0 -10
- package/dist/index151.js +0 -4
- package/dist/index152.js +0 -95
- package/dist/index154.js +0 -6
- package/dist/index155.js +0 -168
- package/dist/index157.js +0 -6
- package/dist/index158.js +0 -145
- package/dist/index16.js +0 -59
- package/dist/index160.js +0 -6
- package/dist/index161.js +0 -53
- package/dist/index163.js +0 -6
- package/dist/index164.js +0 -16
- package/dist/index166.js +0 -35
- package/dist/index168.js +0 -6
- package/dist/index169.js +0 -89
- package/dist/index171.js +0 -6
- package/dist/index172.js +0 -82
- package/dist/index174.js +0 -6
- package/dist/index175.js +0 -38
- package/dist/index176.js +0 -461
- package/dist/index177.js +0 -28
- package/dist/index178.js +0 -66
- package/dist/index18.js +0 -35
- package/dist/index180.js +0 -162
- package/dist/index181.js +0 -5
- package/dist/index182.js +0 -217
- package/dist/index183.js +0 -5
- package/dist/index184.js +0 -112
- package/dist/index185.js +0 -5
- package/dist/index186.js +0 -459
- package/dist/index188.js +0 -6
- package/dist/index19.js +0 -4
- package/dist/index192.js +0 -268
- package/dist/index194.js +0 -6
- package/dist/index195.js +0 -124
- package/dist/index197.js +0 -6
- package/dist/index198.js +0 -174
- package/dist/index2.js +0 -20
- package/dist/index20.js +0 -9
- package/dist/index200.js +0 -6
- package/dist/index201.js +0 -224
- package/dist/index203.js +0 -6
- package/dist/index204.js +0 -135
- package/dist/index206.js +0 -6
- package/dist/index207.js +0 -5
- package/dist/index208.js +0 -170
- package/dist/index21.js +0 -35
- package/dist/index210.js +0 -6
- package/dist/index211.js +0 -37
- package/dist/index213.js +0 -6
- package/dist/index214.js +0 -63
- package/dist/index216.js +0 -6
- package/dist/index217.js +0 -74
- package/dist/index219.js +0 -6
- package/dist/index22.js +0 -6
- package/dist/index220.js +0 -65
- package/dist/index222.js +0 -6
- package/dist/index223.js +0 -67
- package/dist/index225.js +0 -6
- package/dist/index226.js +0 -41
- package/dist/index228.js +0 -6
- package/dist/index229.js +0 -10
- package/dist/index23.js +0 -7
- package/dist/index230.js +0 -22
- package/dist/index232.js +0 -8
- package/dist/index233.js +0 -297
- package/dist/index235.js +0 -6
- package/dist/index236.js +0 -47
- package/dist/index237.js +0 -165
- package/dist/index239.js +0 -6
- package/dist/index24.js +0 -9
- package/dist/index241.js +0 -159
- package/dist/index243.js +0 -6
- package/dist/index244.js +0 -134
- package/dist/index246.js +0 -6
- package/dist/index247.js +0 -167
- package/dist/index249.js +0 -6
- package/dist/index25.js +0 -24
- package/dist/index250.js +0 -147
- package/dist/index252.js +0 -6
- package/dist/index253.js +0 -35
- package/dist/index255.js +0 -6
- package/dist/index256.js +0 -65
- package/dist/index258.js +0 -6
- package/dist/index259.js +0 -19
- package/dist/index260.js +0 -5
- package/dist/index261.js +0 -160
- package/dist/index263.js +0 -6
- package/dist/index264.js +0 -60
- package/dist/index266.js +0 -6
- package/dist/index267.js +0 -134
- package/dist/index269.js +0 -6
- package/dist/index27.js +0 -4
- package/dist/index271.js +0 -206
- package/dist/index273.js +0 -6
- package/dist/index274.js +0 -85
- package/dist/index275.js +0 -5
- package/dist/index276.js +0 -55
- package/dist/index277.js +0 -5
- package/dist/index278.js +0 -164
- package/dist/index28.js +0 -8
- package/dist/index280.js +0 -6
- package/dist/index281.js +0 -145
- package/dist/index283.js +0 -6
- package/dist/index284.js +0 -11
- package/dist/index285.js +0 -200
- package/dist/index287.js +0 -6
- package/dist/index288.js +0 -241
- package/dist/index29.js +0 -7
- package/dist/index290.js +0 -6
- package/dist/index291.js +0 -21
- package/dist/index293.js +0 -6
- package/dist/index294.js +0 -125
- package/dist/index296.js +0 -6
- package/dist/index297.js +0 -161
- package/dist/index299.js +0 -6
- package/dist/index30.js +0 -118
- package/dist/index300.js +0 -127
- package/dist/index302.js +0 -6
- package/dist/index303.js +0 -20
- package/dist/index305.js +0 -6
- package/dist/index306.js +0 -116
- package/dist/index307.js +0 -5
- package/dist/index308.js +0 -507
- package/dist/index31.js +0 -10
- package/dist/index310.js +0 -6
- package/dist/index311.js +0 -196
- package/dist/index313.js +0 -6
- package/dist/index314.js +0 -142
- package/dist/index316.js +0 -6
- package/dist/index317.js +0 -134
- package/dist/index319.js +0 -6
- package/dist/index32.js +0 -28
- package/dist/index320.js +0 -162
- package/dist/index322.js +0 -6
- package/dist/index323.js +0 -319
- package/dist/index325.js +0 -6
- package/dist/index326.js +0 -40
- package/dist/index327.js +0 -43
- package/dist/index328.js +0 -665
- package/dist/index33.js +0 -5
- package/dist/index330.js +0 -6
- package/dist/index331.js +0 -155
- package/dist/index333.js +0 -6
- package/dist/index334.js +0 -142
- package/dist/index336.js +0 -6
- package/dist/index337.js +0 -155
- package/dist/index339.js +0 -6
- package/dist/index34.js +0 -112
- package/dist/index340.js +0 -58
- package/dist/index341.js +0 -5
- package/dist/index342.js +0 -20
- package/dist/index344.js +0 -6
- package/dist/index345.js +0 -142
- package/dist/index347.js +0 -6
- package/dist/index349.js +0 -109
- package/dist/index351.js +0 -6
- package/dist/index353.js +0 -24
- package/dist/index354.js +0 -23
- package/dist/index355.js +0 -839
- package/dist/index356.js +0 -474
- package/dist/index357.js +0 -52
- package/dist/index358.js +0 -7
- package/dist/index36.js +0 -6
- package/dist/index38.js +0 -63
- package/dist/index39.js +0 -63
- package/dist/index40.js +0 -9
- package/dist/index42.js +0 -8
- package/dist/index43.js +0 -179
- package/dist/index44.js +0 -21
- package/dist/index46.js +0 -385
- package/dist/index48.js +0 -6
- package/dist/index49.js +0 -33
- package/dist/index5.js +0 -34
- package/dist/index50.js +0 -12
- package/dist/index51.js +0 -8
- package/dist/index52.js +0 -106
- package/dist/index53.js +0 -330
- package/dist/index54.js +0 -97
- package/dist/index55.js +0 -328
- package/dist/index56.js +0 -28
- package/dist/index57.js +0 -102
- package/dist/index58.js +0 -157
- package/dist/index6.js +0 -38
- package/dist/index60.js +0 -6
- package/dist/index61.js +0 -131
- package/dist/index62.js +0 -5
- package/dist/index65.js +0 -35
- package/dist/index66.js +0 -26
- package/dist/index68.js +0 -6
- package/dist/index69.js +0 -54
- package/dist/index7.js +0 -58
- package/dist/index71.js +0 -6
- package/dist/index72.js +0 -14
- package/dist/index74.js +0 -6
- package/dist/index75.js +0 -159
- package/dist/index77.js +0 -6
- package/dist/index78.js +0 -4
- package/dist/index79.js +0 -4
- package/dist/index80.js +0 -54
- package/dist/index81.js +0 -5
- package/dist/index82.js +0 -5
- package/dist/index83.js +0 -141
- package/dist/index85.js +0 -6
- package/dist/index86.js +0 -65
- package/dist/index88.js +0 -6
- package/dist/index89.js +0 -30
- package/dist/index9.js +0 -11
- package/dist/index90.js +0 -19
- package/dist/index91.js +0 -17
- package/dist/index92.js +0 -9
- package/dist/index93.js +0 -34
- package/dist/index95.js +0 -16
- package/dist/index96.js +0 -10
- package/dist/index97.js +0 -18
- package/dist/index98.js +0 -189
- package/dist/types/VoidHtmlElements.d.ts +0 -1
- package/dist/utilities/functions/add.d.ts +0 -1
- package/dist/utilities/functions/booleanishToBoolean.d.ts +0 -6
- package/dist/utilities/functions/capitalize.d.ts +0 -4
- package/dist/utilities/functions/cloneDeep.d.ts +0 -1
- package/dist/utilities/functions/debounce.d.ts +0 -1
- package/dist/utilities/functions/drop.d.ts +0 -1
- package/dist/utilities/functions/exceedsPrecision.d.ts +0 -1
- package/dist/utilities/functions/findRight.d.ts +0 -4
- package/dist/utilities/functions/findRightIndex.d.ts +0 -4
- package/dist/utilities/functions/flow.d.ts +0 -1
- package/dist/utilities/functions/getHueFromRGB.d.ts +0 -9
- package/dist/utilities/functions/groupBy.d.ts +0 -6
- package/dist/utilities/functions/gt.d.ts +0 -7
- package/dist/utilities/functions/hexToRGB.d.ts +0 -7
- package/dist/utilities/functions/hslToHex.d.ts +0 -1
- package/dist/utilities/functions/includes.d.ts +0 -1
- package/dist/utilities/functions/initial.d.ts +0 -1
- package/dist/utilities/functions/isCssColor.d.ts +0 -5
- package/dist/utilities/functions/lt.d.ts +0 -7
- package/dist/utilities/functions/makeUrl.d.ts +0 -1
- package/dist/utilities/functions/mapKeys.d.ts +0 -5
- package/dist/utilities/functions/mapValues.d.ts +0 -1
- package/dist/utilities/functions/matchesPropertyInsensitive.d.ts +0 -1
- package/dist/utilities/functions/max.d.ts +0 -2
- package/dist/utilities/functions/mean.d.ts +0 -4
- package/dist/utilities/functions/merge.d.ts +0 -11
- package/dist/utilities/functions/millisecondsTo.d.ts +0 -1
- package/dist/utilities/functions/negate.d.ts +0 -4
- package/dist/utilities/functions/normalizeItemGroups.d.ts +0 -13
- package/dist/utilities/functions/omit.d.ts +0 -2
- package/dist/utilities/functions/overEvery.d.ts +0 -4
- package/dist/utilities/functions/pick.d.ts +0 -1
- package/dist/utilities/functions/replace.d.ts +0 -1
- package/dist/utilities/functions/replaceInTree.d.ts +0 -4
- package/dist/utilities/functions/retry.d.ts +0 -5
- package/dist/utilities/functions/roundToTheNearest.d.ts +0 -8
- package/dist/utilities/functions/set.d.ts +0 -15
- package/dist/utilities/functions/stub.d.ts +0 -6
- package/dist/utilities/functions/transposePercent.d.ts +0 -9
- package/dist/utilities/functions/tryTo.d.ts +0 -2
- package/dist/utilities/functions/uniqBy.d.ts +0 -1
- /package/dist/{index64.js → _virtual/_plugin-vue_export-helper.js} +0 -0
- /package/dist/components/{BaseButton → BbBaseButton}/RouterComponent.vue.d.ts +0 -0
- /package/dist/components/{BaseDatePicker/BaseDatePicker.vue.d.ts → BbBaseDatePicker/BbBaseDatePicker.vue.d.ts} +0 -0
- /package/dist/{index165.js → composables/useArray.js} +0 -0
- /package/dist/{index17.js → composables/useFrameworkDetection.js} +0 -0
- /package/dist/{index12.js → composables/useId.js} +0 -0
- /package/dist/{index352.js → directives/createPopoverDirective.js} +0 -0
- /package/dist/{index107.js → utilities/functions/chunk.js} +0 -0
- /package/dist/{index8.js → utilities/functions/clamp.js} +0 -0
- /package/dist/{index150.js → utilities/functions/curry.js} +0 -0
- /package/dist/{index45.js → utilities/functions/extractDomContainer.js} +0 -0
- /package/dist/{index270.js → utilities/functions/findInTree.js} +0 -0
- /package/dist/{index348.js → utilities/functions/flattenTree.js} +0 -0
- /package/dist/{index41.js → utilities/functions/get.js} +0 -0
- /package/dist/{index189.js → utilities/functions/head.js} +0 -0
- /package/dist/{index108.js → utilities/functions/identity.js} +0 -0
- /package/dist/{index179.js → utilities/functions/indexBy.js} +0 -0
- /package/dist/{index37.js → utilities/functions/isHex.js} +0 -0
- /package/dist/{index94.js → utilities/functions/isNil.js} +0 -0
- /package/dist/{index26.js → utilities/functions/isNotNil.js} +0 -0
- /package/dist/{index105.js → utilities/functions/last.js} +0 -0
- /package/dist/{index142.js → utilities/functions/noop.js} +0 -0
- /package/dist/{index231.js → utilities/functions/parseWidthString.js} +0 -0
- /package/dist/{index240.js → utilities/functions/pickBy.js} +0 -0
- /package/dist/{index123.js → utilities/functions/range.js} +0 -0
- /package/dist/{index190.js → utilities/functions/snapToStep.js} +0 -0
- /package/dist/{index191.js → utilities/functions/transpose.js} +0 -0
- /package/dist/{index106.js → utilities/functions/validateDateFormat.js} +0 -0
- /package/dist/{index10.js → utilities/functions/wait.js} +0 -0
|
@@ -1,1669 +1,1330 @@
|
|
|
1
1
|
# BbSelectPopover
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Agent Contract
|
|
4
|
+
|
|
5
|
+
- Safety level: **Consumer API**
|
|
6
|
+
- Group: **Selection**
|
|
7
|
+
- Description: Provides popover-driven option selection and search.
|
|
8
|
+
- Usage rule: Prefer this component for consumer-facing implementations.
|
|
9
|
+
- Implementation source (only when the contract below is not enough): [source/BbSelectPopover.md](./source/BbSelectPopover.md)
|
|
10
|
+
|
|
11
|
+
## Usage & Guidelines
|
|
12
|
+
|
|
13
|
+
### When to use
|
|
14
|
+
|
|
15
|
+
`BbSelectPopover` is `BbSelect` with the inline form input removed: **you own the
|
|
16
|
+
trigger**, it owns search, filtering, keyboard navigation, single/multiple
|
|
17
|
+
selection and the virtualized options list. Reach for it when the control that
|
|
18
|
+
opens the picker is _not_ a form field — a status pill, an avatar stack, a role
|
|
19
|
+
badge, a toolbar button, a table-cell chip. It is the right tool for
|
|
20
|
+
command-palette and inline-edit patterns.
|
|
21
|
+
|
|
22
|
+
Prefer a sibling when the shape is different:
|
|
23
|
+
|
|
24
|
+
- You want a **labelled, bordered form control** with hint/error/description
|
|
25
|
+
states → `BbSelect` (`./BbSelect.md`). That is a field, not a trigger.
|
|
26
|
+
- A small, always-visible exclusive choice → `BbRadioGroup`; small static
|
|
27
|
+
multi-choice → `BbCheckboxGroup` / `BbSwitchGroup`.
|
|
28
|
+
- Values the user _invents_ → `BbTag`; a known-but-growable set →
|
|
29
|
+
`BbSelectPopover` (or `BbSelect`) with `option:add`.
|
|
30
|
+
|
|
31
|
+
Everything from the **Options and Items** and **Fetch Items** playbooks carries
|
|
32
|
+
over unchanged — this component shares `BbSelect`'s option engine. This guide
|
|
33
|
+
covers what is _specific_ to the popover: the activator, and the traps of
|
|
34
|
+
driving a trigger you render yourself.
|
|
35
|
+
|
|
36
|
+
### Map options with `item-text` / `item-value`
|
|
37
|
+
|
|
38
|
+
Pass your domain objects to `items` as they are and name the display/value
|
|
39
|
+
fields with `item-text` / `item-value` (dot-paths or extractor functions). The
|
|
40
|
+
`v-model` holds the resolved **value**, not the whole object. The value-identity
|
|
41
|
+
rules are the shared ones (see the Options and Items playbook), in brief:
|
|
42
|
+
|
|
43
|
+
- Primitive `items` (`string[]` / `number[]`) need no mapping props; for object
|
|
44
|
+
items always set both.
|
|
45
|
+
- Matching compares **value hashes** (deep structural equality), so object
|
|
46
|
+
values survive refetches — but prefer stable primitive ids.
|
|
47
|
+
- Options resolving to a **duplicate value are silently dropped** (first wins).
|
|
48
|
+
- Gate individual options with `selectable` — a boolean (`false` disables every
|
|
49
|
+
option) or a predicate `(item) => boolean`; rejected options render disabled
|
|
50
|
+
and keyboard navigation skips them. A `disabled` field on the item object is
|
|
51
|
+
inert domain data — feed it to the predicate
|
|
52
|
+
(`:selectable="(item) => !item.disabled"`) if it encodes availability.
|
|
53
|
+
|
|
54
|
+
### Wire up an activator
|
|
55
|
+
|
|
56
|
+
Provide the trigger through the `#activator` slot and **spread its `props` onto
|
|
57
|
+
your element** with `v-bind="props"` — that wires the popover reference and the
|
|
58
|
+
open/close handlers. The full slot scope:
|
|
59
|
+
|
|
60
|
+
- `props` — spread onto the focusable trigger element.
|
|
61
|
+
- `shown` — is the popover open (reflect it visually).
|
|
62
|
+
- `toggle` / `open` / `close` — imperative open-state control (all async).
|
|
63
|
+
- `clear` — empty the selection (and query).
|
|
64
|
+
- `focus` — focus the search field (or the listbox when writing is disabled).
|
|
65
|
+
- `query` — the current search string.
|
|
66
|
+
- `selectedOptions` — the resolved selected options (`.text` / `.value` /
|
|
67
|
+
`.item`) — **popover state** (see below).
|
|
68
|
+
- `text` — display text of the current selection — **popover state** (see below).
|
|
69
|
+
- `disabled` / `readonly` / `loading` — state mirrors for styling the trigger.
|
|
70
|
+
|
|
71
|
+
**The trigger must not depend on the popover's state — label it from your own
|
|
72
|
+
data.** The popover is an _optional_ editing affordance: it may never be opened,
|
|
73
|
+
and whether it has been opened must never change how the trigger reads. `text`
|
|
74
|
+
and `selectedOptions` resolve the model against **loaded options**, so with a
|
|
75
|
+
provider they stay empty until that provider first runs — by default, the first
|
|
76
|
+
open. A trigger fed from them renders blank and then flickers into truth; a
|
|
77
|
+
trigger fed from your own data is correct on first paint and stays that way.
|
|
78
|
+
|
|
79
|
+
The robust pattern — used in the example — is a `computed` that resolves the
|
|
80
|
+
label from the model against your own data:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const assigneeLabel = computed(
|
|
84
|
+
() => people.find((p) => p.id === assignee.value)?.name ?? 'Unassigned'
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
(Array `items` do resolve on mount, so `text` is populated from the first paint
|
|
89
|
+
there — but keep labelling from your own data anyway: it is the same amount of
|
|
90
|
+
code, and it does not change meaning the day the array becomes a provider.)
|
|
91
|
+
|
|
92
|
+
**Filter-pill assignee picker**
|
|
4
93
|
|
|
5
94
|
```vue
|
|
6
95
|
<template>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
name="activator"
|
|
10
|
-
v-bind="{
|
|
11
|
-
props: {
|
|
12
|
-
ref: setActivatorRef,
|
|
13
|
-
},
|
|
14
|
-
shown,
|
|
15
|
-
disabled,
|
|
16
|
-
readonly,
|
|
17
|
-
loading: computedLoading,
|
|
18
|
-
query,
|
|
19
|
-
selectedOptions,
|
|
20
|
-
text: triggerInputValue,
|
|
21
|
-
clear: onClear,
|
|
22
|
-
open,
|
|
23
|
-
close,
|
|
24
|
-
toggle,
|
|
25
|
-
}"
|
|
26
|
-
/>
|
|
27
|
-
<CommonPopover
|
|
28
|
-
v-model="shown"
|
|
29
|
-
v-bind="$attrs"
|
|
30
|
-
:anchor="activatorEl"
|
|
31
|
-
:arrow-padding="arrowPadding"
|
|
32
|
-
:boundary="boundary"
|
|
33
|
-
class="bb-select-popover"
|
|
34
|
-
:class="{
|
|
35
|
-
'bb-select-popover--errors': hasErrors,
|
|
36
|
-
'bb-select-popover--loading': computedLoading,
|
|
37
|
-
}"
|
|
38
|
-
:flip="flip"
|
|
39
|
-
:placement="placement"
|
|
40
|
-
:offset="offset"
|
|
41
|
-
:padding="padding"
|
|
42
|
-
:transition-duration="transitionDuration"
|
|
43
|
-
:hide-arrow="hideArrow"
|
|
44
|
-
:style="panelStyles"
|
|
96
|
+
<div
|
|
97
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
45
98
|
>
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Assignee</span>
|
|
100
|
+
<BbSelectPopover
|
|
101
|
+
v-model="assignee"
|
|
102
|
+
compact
|
|
103
|
+
item-text="fullName"
|
|
104
|
+
item-value="id"
|
|
105
|
+
:items="teammates"
|
|
106
|
+
width="240px"
|
|
49
107
|
>
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@focus.stop="onSearchInputFocus"
|
|
68
|
-
@input.stop="onSearchInputInput"
|
|
69
|
-
@keydown.stop.prevent.down="onArrowDown"
|
|
70
|
-
@keydown.stop.prevent.enter="onEnter"
|
|
71
|
-
@keydown.stop.prevent.esc="onEscape"
|
|
72
|
-
@keydown.stop.prevent.up="onArrowUp"
|
|
73
|
-
/>
|
|
74
|
-
<ErrorIcon
|
|
75
|
-
aria-hidden="true"
|
|
76
|
-
class="bb-select-popover__search-error-icon"
|
|
77
|
-
focusable="false"
|
|
78
|
-
/>
|
|
79
|
-
<BbSpinner
|
|
80
|
-
aria-hidden="true"
|
|
81
|
-
class="bb-select-popover__search-spinner"
|
|
82
|
-
focusable="false"
|
|
83
|
-
/>
|
|
84
|
-
<svg
|
|
85
|
-
class="bb-select-popover__search-icon"
|
|
86
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
87
|
-
viewBox="0 0 24 24"
|
|
88
|
-
aria-hidden="true"
|
|
89
|
-
>
|
|
90
|
-
<path
|
|
91
|
-
fill="none"
|
|
92
|
-
stroke="currentColor"
|
|
93
|
-
stroke-linecap="round"
|
|
94
|
-
stroke-linejoin="round"
|
|
95
|
-
stroke-width="2"
|
|
96
|
-
d="m21 21-4.343-4.343m0 0A8 8 0 1 0 5.343 5.343a8 8 0 0 0 11.314 11.314"
|
|
97
|
-
/>
|
|
98
|
-
</svg>
|
|
99
|
-
</div>
|
|
100
|
-
<button
|
|
101
|
-
v-else
|
|
102
|
-
ref="popoverFocusProxy"
|
|
103
|
-
class="bb-select-popover__focus-proxy sr-only"
|
|
104
|
-
type="button"
|
|
105
|
-
:aria-controls="`${id}_listbox`"
|
|
106
|
-
:aria-describedby="computedAriaDescribedby"
|
|
107
|
-
:aria-invalid="computedAriaInvalid"
|
|
108
|
-
:aria-label="searchInputAriaLabel"
|
|
109
|
-
tabindex="-1"
|
|
110
|
-
@focus="onPopoverFocusProxyFocus"
|
|
111
|
-
@keydown.stop.prevent.down="onArrowDown"
|
|
112
|
-
@keydown.stop.prevent.up="onArrowUp"
|
|
113
|
-
@keydown.stop.prevent.enter="onEnter"
|
|
114
|
-
@keydown.stop.prevent.esc="onEscape"
|
|
115
|
-
>
|
|
116
|
-
Focus options
|
|
117
|
-
</button>
|
|
118
|
-
<div
|
|
119
|
-
class="bb-select-popover__options-outer"
|
|
120
|
-
:aria-hidden="shown ? undefined : 'true'"
|
|
121
|
-
:inert="!shown"
|
|
122
|
-
>
|
|
123
|
-
<slot name="options:prepend:outer" :focus="focusPopoverContent" />
|
|
124
|
-
</div>
|
|
125
|
-
<ListBox
|
|
126
|
-
:id="`${id}_listbox`"
|
|
127
|
-
ref="optionsContainer"
|
|
128
|
-
:compact="compact"
|
|
129
|
-
:loading="computedLoading"
|
|
130
|
-
:loading-text="loadingText"
|
|
131
|
-
:multiple="multiple"
|
|
132
|
-
:no-data-text="noDataText"
|
|
133
|
-
:open="shown"
|
|
134
|
-
:options="groupBy ? undefined : filteredOptions"
|
|
135
|
-
:groups="groupBy ? groupedOptions : undefined"
|
|
136
|
-
:header-height="headerHeight"
|
|
137
|
-
:option-height="itemHeight"
|
|
138
|
-
@focused:change="onListboxFocusedChange"
|
|
139
|
-
@option:selected="onOptionSelected"
|
|
140
|
-
@option:unselected="onOptionUnselected"
|
|
141
|
-
>
|
|
142
|
-
<template #options:prepend
|
|
143
|
-
><slot name="options:prepend" :focus="focusPopoverContent"
|
|
144
|
-
/></template>
|
|
145
|
-
<template #loading><slot name="loading" :query="query" /></template>
|
|
146
|
-
<template #no-data
|
|
147
|
-
><slot name="no-data" :query="query" :focus="focusPopoverContent"
|
|
148
|
-
/></template>
|
|
149
|
-
<template #options:append
|
|
150
|
-
><slot name="options:append" :focus="focusPopoverContent"
|
|
151
|
-
/></template>
|
|
152
|
-
<template #option="data"
|
|
153
|
-
><slot :loading="computedLoading" name="option" v-bind="data"
|
|
154
|
-
/></template>
|
|
155
|
-
<template #group="data"><slot name="group" v-bind="data" /></template>
|
|
156
|
-
</ListBox>
|
|
157
|
-
<div
|
|
158
|
-
class="bb-select-popover__options-outer"
|
|
159
|
-
:aria-hidden="shown ? undefined : 'true'"
|
|
160
|
-
:inert="!shown"
|
|
161
|
-
>
|
|
162
|
-
<slot name="options:append:outer" :focus="focusPopoverContent" />
|
|
163
|
-
</div>
|
|
164
|
-
</CommonPopover>
|
|
108
|
+
<template #activator="{ props }">
|
|
109
|
+
<!-- The badge IS the trigger — Linear filter-pill grammar. Label
|
|
110
|
+
from our own state, never the slot's `text` (empty until first open
|
|
111
|
+
with a provider). -->
|
|
112
|
+
<BbBadge
|
|
113
|
+
append:icon="lucide:chevron-down"
|
|
114
|
+
prepend:icon="lucide:user"
|
|
115
|
+
size="lg"
|
|
116
|
+
variant="outline"
|
|
117
|
+
>
|
|
118
|
+
<BbBadgeButton v-bind="props">{{
|
|
119
|
+
assigneeLabel || 'Assign'
|
|
120
|
+
}}</BbBadgeButton>
|
|
121
|
+
</BbBadge>
|
|
122
|
+
</template>
|
|
123
|
+
</BbSelectPopover>
|
|
124
|
+
</div>
|
|
165
125
|
</template>
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { computed, ref } from 'vue';
|
|
128
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
166
129
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
import type { Option as BaseOption, GroupedOptions } from '@/types/Option';
|
|
171
|
-
import { computed, ref, nextTick, watch, onBeforeUnmount } from 'vue';
|
|
172
|
-
import { extractDomContainer } from '@/utilities/functions/extractDomContainer';
|
|
173
|
-
import { get } from '@/utilities/functions/get';
|
|
174
|
-
import { hash } from '@/utilities/functions/hash';
|
|
175
|
-
import { isNil } from '@/utilities/functions/isNil';
|
|
176
|
-
import { matchAnyKey } from '@/utilities/functions/matchAnyKey';
|
|
177
|
-
import { parseWidthString } from '@/utilities/functions/parseWidthString';
|
|
178
|
-
import { toRef } from 'vue';
|
|
179
|
-
import { useArray } from '@/composables/useArray';
|
|
180
|
-
import { useBaseOptions } from '@/composables/useBaseOptions';
|
|
181
|
-
import { useCoherence } from '@/composables/useCoherence';
|
|
182
|
-
import { useElementSize, useIntersectionObserver } from '@vueuse/core';
|
|
183
|
-
import { useHashedWatcher } from '@/composables/useHashedWatcher';
|
|
184
|
-
import { useId } from '@/composables/useId';
|
|
185
|
-
import { useIndexById } from '@/composables/useIndexById';
|
|
186
|
-
import { useItemsGetter } from '@/composables/useItemsGetter';
|
|
187
|
-
import { useMobile } from '@/composables/useMobile';
|
|
188
|
-
import { usePrefill } from '@/composables/usePrefill';
|
|
189
|
-
import { useUntil } from '@/composables/useUntil';
|
|
190
|
-
import { wait } from '@/utilities/functions/wait';
|
|
191
|
-
import { BbSpinner } from '@/index';
|
|
192
|
-
import CommonPopover from '../CommonPopover/CommonPopover.vue';
|
|
193
|
-
import ErrorIcon from '../ErrorIcon/ErrorIcon.vue';
|
|
194
|
-
import ListBox from '../ListBox/ListBox.vue';
|
|
195
|
-
import { waitFor } from '@/utilities/functions/waitFor';
|
|
196
|
-
import type {
|
|
197
|
-
BbSelectPopoverEvents,
|
|
198
|
-
BbSelectPopoverProps,
|
|
199
|
-
BbSelectPopoverSlots,
|
|
200
|
-
} from './types';
|
|
201
|
-
|
|
202
|
-
const props = withDefaults(defineProps<BbSelectPopoverProps<Item>>(), {
|
|
203
|
-
allowWriting: true,
|
|
204
|
-
arrowPadding: 10,
|
|
205
|
-
autocomplete: 'off',
|
|
206
|
-
depsDebounceTime: 0,
|
|
207
|
-
dependencies: () => [],
|
|
208
|
-
filterBy: () => [],
|
|
209
|
-
flip: true,
|
|
210
|
-
hideArrow: true,
|
|
211
|
-
max: Infinity,
|
|
212
|
-
modelValueDebounceTime: 0,
|
|
213
|
-
offset: 4,
|
|
214
|
-
padding: 10,
|
|
215
|
-
placement: 'bottom',
|
|
216
|
-
prefill: 'interaction',
|
|
217
|
-
queryDebounceTime: 500,
|
|
218
|
-
transitionDuration: 300,
|
|
219
|
-
width: '200',
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const emit = defineEmits<BbSelectPopoverEvents>();
|
|
223
|
-
|
|
224
|
-
defineSlots<BbSelectPopoverSlots<Item>>();
|
|
225
|
-
defineOptions({ inheritAttrs: false });
|
|
226
|
-
|
|
227
|
-
if (props.multiple && !Array.isArray(props.modelValue)) {
|
|
228
|
-
throw new Error('Multiple is set to "true" but modelValue is not an array.');
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* START ELEMENT REFERENCES
|
|
233
|
-
*/
|
|
234
|
-
const activatorEl = ref<HTMLElement>();
|
|
235
|
-
const optionsContainer = ref<any | null>(null);
|
|
236
|
-
const searchInput = ref<HTMLInputElement | null>(null);
|
|
237
|
-
const popoverFocusProxy = ref<HTMLButtonElement | null>(null);
|
|
238
|
-
const activeDescendantId = ref<string>();
|
|
239
|
-
const { width: activatorWidth } = useElementSize(
|
|
240
|
-
activatorEl,
|
|
241
|
-
{ width: 0, height: 0 },
|
|
242
|
-
{
|
|
243
|
-
box: 'border-box',
|
|
244
|
-
}
|
|
245
|
-
);
|
|
246
|
-
/**
|
|
247
|
-
* END ELEMENT REFERENCES
|
|
248
|
-
*/
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* START ACTIVATOR MANAGEMENT
|
|
252
|
-
*/
|
|
253
|
-
const hasExternalActivator = computed(() => props.activator != null);
|
|
254
|
-
|
|
255
|
-
const setActivatorRef = (reference: unknown) => {
|
|
256
|
-
const el = extractDomContainer(reference);
|
|
257
|
-
if (!el) return;
|
|
258
|
-
activatorEl.value = el;
|
|
130
|
+
type Teammate = {
|
|
131
|
+
id: string;
|
|
132
|
+
fullName: string;
|
|
259
133
|
};
|
|
260
134
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
);
|
|
268
|
-
/**
|
|
269
|
-
* END ACTIVATOR MANAGEMENT
|
|
270
|
-
*/
|
|
135
|
+
const teammates: Teammate[] = [
|
|
136
|
+
{ id: 'ada', fullName: 'Ada Lovelace' },
|
|
137
|
+
{ id: 'grace', fullName: 'Grace Hopper' },
|
|
138
|
+
{ id: 'alan', fullName: 'Alan Turing' },
|
|
139
|
+
{ id: 'maggie', fullName: 'Margaret Hamilton' },
|
|
140
|
+
];
|
|
271
141
|
|
|
272
|
-
const
|
|
273
|
-
const computedAllowWriting = computed(() => {
|
|
274
|
-
if (props.allowWriting === 'not-mobile') return !isMobile.value;
|
|
275
|
-
return !!props.allowWriting;
|
|
276
|
-
});
|
|
277
|
-
const inputmode = computed(() =>
|
|
278
|
-
props.allowWriting === 'not-mobile' && isMobile.value ? 'none' : undefined
|
|
279
|
-
);
|
|
280
|
-
const searchInputAriaLabel = computed(
|
|
281
|
-
() => props.searchInputAriaLabel ?? 'Search options'
|
|
282
|
-
);
|
|
142
|
+
const assignee = ref<string | null>(null);
|
|
283
143
|
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
const isInvalid = props.ariaInvalid ?? !!props.hasErrors;
|
|
287
|
-
return isInvalid ? 'true' : undefined;
|
|
288
|
-
});
|
|
289
|
-
const computedAriaDescribedby = computed(() => {
|
|
290
|
-
const describedBy = props.ariaDescribedby;
|
|
291
|
-
if (Array.isArray(describedBy)) {
|
|
292
|
-
const normalized = describedBy
|
|
293
|
-
.map((value) => value.trim())
|
|
294
|
-
.filter(Boolean)
|
|
295
|
-
.join(' ');
|
|
296
|
-
return normalized || undefined;
|
|
297
|
-
}
|
|
298
|
-
if (typeof describedBy === 'string') {
|
|
299
|
-
const normalized = describedBy.trim();
|
|
300
|
-
return normalized || undefined;
|
|
301
|
-
}
|
|
302
|
-
return undefined;
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
const { resume, pause } = useIntersectionObserver(
|
|
306
|
-
activatorEl,
|
|
307
|
-
async ([{ intersectionRatio }]) => {
|
|
308
|
-
if (intersectionRatio !== 1 && shown.value) {
|
|
309
|
-
await close();
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
{ threshold: [0, 1], immediate: false }
|
|
144
|
+
const assigneeLabel = computed(
|
|
145
|
+
() => teammates.find((t) => t.id === assignee.value)?.fullName ?? ''
|
|
313
146
|
);
|
|
147
|
+
</script>
|
|
148
|
+
```
|
|
314
149
|
|
|
315
|
-
|
|
316
|
-
if (props.width === 'auto') return 'auto';
|
|
317
|
-
if (props.width == null || props.width === '') {
|
|
318
|
-
return activatorWidth.value ? `${activatorWidth.value}px` : '320px';
|
|
319
|
-
}
|
|
150
|
+
### Multiple selection
|
|
320
151
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
} catch {
|
|
329
|
-
if (typeof props.width === 'string') return props.width.trim();
|
|
330
|
-
return activatorWidth.value ? `${activatorWidth.value}px` : '320px';
|
|
331
|
-
}
|
|
332
|
-
});
|
|
152
|
+
Add `multiple` and the model becomes an array — it **must be seeded as one**
|
|
153
|
+
(`ref<string[]>([])`); a non-array model **throws at setup**
|
|
154
|
+
(`"Multiple is set to \"true\" but modelValue is not an array."`). Cap the count
|
|
155
|
+
with `max`: at the cap, unselected options auto-disable while selected ones never
|
|
156
|
+
do, so the user can always deselect. Because the trigger is yours, _you_
|
|
157
|
+
summarise the selection — a count, a chip cluster, an avatar stack — from
|
|
158
|
+
`selectedOptions` or your own model.
|
|
333
159
|
|
|
334
|
-
|
|
335
|
-
'--bb-select-popover-width': normalizedWidth.value,
|
|
336
|
-
}));
|
|
160
|
+
**Reviewers filter pill with a live count**
|
|
337
161
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
162
|
+
```vue
|
|
163
|
+
<template>
|
|
164
|
+
<div
|
|
165
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
166
|
+
>
|
|
167
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Filters</span>
|
|
168
|
+
<BbSelectPopover
|
|
169
|
+
v-model="reviewers"
|
|
170
|
+
compact
|
|
171
|
+
item-text="name"
|
|
172
|
+
item-value="id"
|
|
173
|
+
:items="people"
|
|
174
|
+
:max="4"
|
|
175
|
+
multiple
|
|
176
|
+
width="240px"
|
|
177
|
+
>
|
|
178
|
+
<template #activator="{ props }">
|
|
179
|
+
<!-- Summarise the multi-select yourself — a live count on the pill. -->
|
|
180
|
+
<BbBadge
|
|
181
|
+
append:icon="lucide:chevron-down"
|
|
182
|
+
prepend:icon="lucide:users"
|
|
183
|
+
size="lg"
|
|
184
|
+
variant="outline"
|
|
185
|
+
>
|
|
186
|
+
<BbBadgeButton v-bind="props"
|
|
187
|
+
>Reviewers ({{ reviewers.length }})</BbBadgeButton
|
|
188
|
+
>
|
|
189
|
+
</BbBadge>
|
|
190
|
+
</template>
|
|
191
|
+
</BbSelectPopover>
|
|
192
|
+
</div>
|
|
193
|
+
</template>
|
|
194
|
+
<script setup lang="ts">
|
|
195
|
+
import { ref } from 'vue';
|
|
196
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
197
|
+
|
|
198
|
+
type Person = { id: string; name: string };
|
|
199
|
+
|
|
200
|
+
const people: Person[] = [
|
|
201
|
+
{ id: 'ada', name: 'Ada Lovelace' },
|
|
202
|
+
{ id: 'grace', name: 'Grace Hopper' },
|
|
203
|
+
{ id: 'alan', name: 'Alan Turing' },
|
|
204
|
+
{ id: 'katherine', name: 'Katherine Johnson' },
|
|
205
|
+
{ id: 'ken', name: 'Ken Thompson' },
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
// `multiple` requires an array model from the start.
|
|
209
|
+
const reviewers = ref<string[]>(['ada', 'grace']);
|
|
210
|
+
</script>
|
|
211
|
+
```
|
|
364
212
|
|
|
365
|
-
|
|
366
|
-
* Search input focus keeps the whole control in active state.
|
|
367
|
-
*/
|
|
368
|
-
const onSearchInputFocus = (event: FocusEvent) => {
|
|
369
|
-
emit('focus', event);
|
|
370
|
-
setActive();
|
|
371
|
-
};
|
|
213
|
+
### Custom option rows
|
|
372
214
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
215
|
+
The `#option` slot replaces each row — exposing the raw `item`, resolved `text` /
|
|
216
|
+
`value`, the row `index`, and `selected` / `focused` / `disabled` / `loading`
|
|
217
|
+
flags — so options can carry avatars, badges or secondary metadata. Rows are
|
|
218
|
+
fixed-height and virtualized: if your markup is taller than the default 40px
|
|
219
|
+
(compact 32px), set `item-height` to match — and `header-height` for grouped
|
|
220
|
+
headers.
|
|
377
221
|
|
|
378
|
-
|
|
379
|
-
emit('change', event);
|
|
380
|
-
};
|
|
222
|
+
**Option rows with avatar initials**
|
|
381
223
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
224
|
+
```vue
|
|
225
|
+
<template>
|
|
226
|
+
<div
|
|
227
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
228
|
+
>
|
|
229
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Owner</span>
|
|
230
|
+
<BbSelectPopover
|
|
231
|
+
v-model="owner"
|
|
232
|
+
compact
|
|
233
|
+
:item-height="40"
|
|
234
|
+
item-text="label"
|
|
235
|
+
item-value="value"
|
|
236
|
+
:items="people"
|
|
237
|
+
width="240px"
|
|
238
|
+
>
|
|
239
|
+
<template #activator="{ props }">
|
|
240
|
+
<BbBadge
|
|
241
|
+
append:icon="lucide:chevron-down"
|
|
242
|
+
prepend:icon="lucide:user"
|
|
243
|
+
size="lg"
|
|
244
|
+
variant="outline"
|
|
245
|
+
>
|
|
246
|
+
<BbBadgeButton v-bind="props">{{
|
|
247
|
+
ownerLabel || 'Set owner'
|
|
248
|
+
}}</BbBadgeButton>
|
|
249
|
+
</BbBadge>
|
|
250
|
+
</template>
|
|
251
|
+
<template #option="{ item, text, selected }">
|
|
252
|
+
<span class="flex w-full items-center gap-2">
|
|
253
|
+
<BbAvatar :alt="text" size="sm">{{ item.initials }}</BbAvatar>
|
|
254
|
+
<span class="min-w-0 flex-1 truncate">{{ text }}</span>
|
|
255
|
+
<BbIcon
|
|
256
|
+
v-if="selected"
|
|
257
|
+
class="shrink-0 text-[color:var(--bb-text-muted)]"
|
|
258
|
+
icon="lucide:check"
|
|
259
|
+
:size="14"
|
|
260
|
+
/>
|
|
261
|
+
</span>
|
|
262
|
+
</template>
|
|
263
|
+
</BbSelectPopover>
|
|
264
|
+
</div>
|
|
265
|
+
</template>
|
|
266
|
+
<script setup lang="ts">
|
|
267
|
+
import { computed, ref } from 'vue';
|
|
268
|
+
import {
|
|
269
|
+
BbAvatar,
|
|
270
|
+
BbBadge,
|
|
271
|
+
BbBadgeButton,
|
|
272
|
+
BbIcon,
|
|
273
|
+
BbSelectPopover,
|
|
274
|
+
} from 'bitboss-ui';
|
|
275
|
+
|
|
276
|
+
type Person = { label: string; value: string; initials: string };
|
|
277
|
+
|
|
278
|
+
const people: Person[] = [
|
|
279
|
+
{ label: 'Ada Lovelace', value: 'ada', initials: 'AL' },
|
|
280
|
+
{ label: 'Grace Hopper', value: 'grace', initials: 'GH' },
|
|
281
|
+
{ label: 'Alan Turing', value: 'alan', initials: 'AT' },
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
const owner = ref<string | null>(null);
|
|
285
|
+
const ownerLabel = computed(
|
|
286
|
+
() => people.find((p) => p.value === owner.value)?.label ?? ''
|
|
287
|
+
);
|
|
288
|
+
</script>
|
|
289
|
+
```
|
|
385
290
|
|
|
386
|
-
|
|
387
|
-
* Search on user typing inside the popover search field.
|
|
388
|
-
*/
|
|
389
|
-
const onSearchInputInput = (event: Event) => {
|
|
390
|
-
emit('input', event);
|
|
391
|
-
debouncedGetter(query.value, false, props.modelValue);
|
|
392
|
-
};
|
|
291
|
+
### Fetch options with a provider
|
|
393
292
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
293
|
+
When options come from a backend, pass a **function** to `items`. It receives
|
|
294
|
+
`(query, prefill, modelValue)` and returns `Item[]` or a `Promise<Item[]>` — the
|
|
295
|
+
full call matrix is the shared one in the Fetch Items playbook. The essentials:
|
|
397
296
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
: event.target instanceof Text
|
|
406
|
-
? event.target.parentElement
|
|
407
|
-
: null;
|
|
408
|
-
if (!target) return;
|
|
409
|
-
const insideActivator = activatorEl.value?.contains(target);
|
|
410
|
-
const insidePopover = !!target.closest('.bb-common-popover');
|
|
411
|
-
if (insideActivator || insidePopover) return;
|
|
412
|
-
await close();
|
|
413
|
-
setInactive();
|
|
414
|
-
};
|
|
297
|
+
- `prefill === false` means exactly one thing: **the user typed** — search then.
|
|
298
|
+
- Every component-initiated call (first load, dependency change, model change)
|
|
299
|
+
passes `prefill === true` and expects the current selection to stay resolvable
|
|
300
|
+
in the response — so return the selected items (by id) to keep the trigger
|
|
301
|
+
label and chips accurate.
|
|
302
|
+
- `modelValue` is your v-model as-is; the prop types it `any` — an array in
|
|
303
|
+
`multiple` mode, the raw model value (possibly `null`) in single mode.
|
|
415
304
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
305
|
+
Runtime guarantees you should not re-implement: **debouncing** (`query-debounce-time`
|
|
306
|
+
default 500ms; `deps-debounce-time` and `model-value-debounce-time` default 0),
|
|
307
|
+
**race protection** (out-of-order responses discarded), **loading** (in-flight
|
|
308
|
+
requests set the slot's `loading` flag automatically — the `loading` prop is only
|
|
309
|
+
for _external_ pending state), and **errors** (a rejected provider is logged and
|
|
310
|
+
the previous items kept — surface failures yourself via `useToast`). Enable
|
|
311
|
+
`stash` to accumulate picked options across searches so chosen values stay
|
|
312
|
+
resolvable even when later responses omit them.
|
|
422
313
|
|
|
423
|
-
|
|
424
|
-
* On first open the listbox is lazily mounted.
|
|
425
|
-
* Wait a few micro-cycles so imperative focus calls don't no-op.
|
|
426
|
-
*/
|
|
427
|
-
const waitForOptionsContainer = async () =>
|
|
428
|
-
waitFor(() => !!optionsContainer.value, 100);
|
|
314
|
+
**Async, debounced destination picker**
|
|
429
315
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
316
|
+
```vue
|
|
317
|
+
<template>
|
|
318
|
+
<div
|
|
319
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
320
|
+
>
|
|
321
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Trip</span>
|
|
322
|
+
<BbSelectPopover
|
|
323
|
+
v-model="destination"
|
|
324
|
+
compact
|
|
325
|
+
item-text="label"
|
|
326
|
+
item-value="value"
|
|
327
|
+
:items="searchCities"
|
|
328
|
+
loading-text="Searching…"
|
|
329
|
+
no-data-text="No city found"
|
|
330
|
+
:query-debounce-time="250"
|
|
331
|
+
width="240px"
|
|
332
|
+
>
|
|
333
|
+
<template #activator="{ props, loading }">
|
|
334
|
+
<!-- Badge `loading` reflects the provider's in-flight fetch. Label
|
|
335
|
+
from our own pool — never the slot's `text`. -->
|
|
336
|
+
<BbBadge
|
|
337
|
+
append:icon="lucide:chevron-down"
|
|
338
|
+
:loading="loading"
|
|
339
|
+
prepend:icon="lucide:map-pin"
|
|
340
|
+
size="lg"
|
|
341
|
+
variant="outline"
|
|
342
|
+
>
|
|
343
|
+
<BbBadgeButton v-bind="props">{{
|
|
344
|
+
destinationLabel || 'Add destination'
|
|
345
|
+
}}</BbBadgeButton>
|
|
346
|
+
</BbBadge>
|
|
347
|
+
</template>
|
|
348
|
+
</BbSelectPopover>
|
|
349
|
+
</div>
|
|
350
|
+
</template>
|
|
351
|
+
<script setup lang="ts">
|
|
352
|
+
import { computed, ref } from 'vue';
|
|
353
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
354
|
+
|
|
355
|
+
type City = { label: string; value: string };
|
|
356
|
+
|
|
357
|
+
const pool: City[] = [
|
|
358
|
+
{ label: 'Amsterdam', value: 'amsterdam' },
|
|
359
|
+
{ label: 'Athens', value: 'athens' },
|
|
360
|
+
{ label: 'Barcelona', value: 'barcelona' },
|
|
361
|
+
{ label: 'Copenhagen', value: 'copenhagen' },
|
|
362
|
+
{ label: 'Dublin', value: 'dublin' },
|
|
363
|
+
{ label: 'Prague', value: 'prague' },
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
const fetchFromApi = (predicate: (city: City) => boolean) =>
|
|
367
|
+
new Promise<City[]>((resolve) => {
|
|
368
|
+
setTimeout(() => resolve(pool.filter(predicate)), 250);
|
|
369
|
+
});
|
|
438
370
|
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
371
|
+
const searchCities = (
|
|
372
|
+
query: string,
|
|
373
|
+
prefill: boolean,
|
|
374
|
+
// The raw v-model — type it with your own model shape: a single value (or
|
|
375
|
+
// null) here, an array in multiple mode.
|
|
376
|
+
modelValue: string | null
|
|
377
|
+
): Promise<City[]> => {
|
|
378
|
+
// Prefill: resolve the current selection so the trigger label stays accurate.
|
|
379
|
+
if (prefill && modelValue) {
|
|
380
|
+
return fetchFromApi((city) => city.value === modelValue);
|
|
444
381
|
}
|
|
445
|
-
|
|
382
|
+
if (!query.trim()) return Promise.resolve([]);
|
|
383
|
+
const q = query.trim().toLowerCase();
|
|
384
|
+
return fetchFromApi((city) => city.label.toLowerCase().includes(q));
|
|
446
385
|
};
|
|
447
386
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (!canProcessKeyboardBindings.value) return;
|
|
455
|
-
if (shown.value) {
|
|
456
|
-
optionsContainer.value?.focusPrevious();
|
|
457
|
-
} else {
|
|
458
|
-
await open({ focusPopoverContent: false });
|
|
459
|
-
await waitForOptionsContainer();
|
|
460
|
-
await wait(props.transitionDuration);
|
|
461
|
-
focusLastSelectedOption();
|
|
462
|
-
}
|
|
463
|
-
};
|
|
387
|
+
const destination = ref<string | null>(null);
|
|
388
|
+
const destinationLabel = computed(
|
|
389
|
+
() => pool.find((c) => c.value === destination.value)?.label ?? ''
|
|
390
|
+
);
|
|
391
|
+
</script>
|
|
392
|
+
```
|
|
464
393
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
394
|
+
### Laziness, prefill and the trigger label
|
|
395
|
+
|
|
396
|
+
`prefill` controls **when the first load happens**: `'interaction'` (default) on
|
|
397
|
+
first open, `true` immediately on mount (and during SSR via `onServerPrefetch`),
|
|
398
|
+
`false` **search-first** — nothing is fetched until the user types a query.
|
|
399
|
+
Opening the panel is not a search, so it loads nothing; until the first query the
|
|
400
|
+
list shows a _"search to begin"_ hint instead of "no data".
|
|
401
|
+
|
|
402
|
+
Reach for `false` when the result set is too large or too expensive to browse and
|
|
403
|
+
the query is the only sensible entry point — a user picker over thousands of
|
|
404
|
+
rows. Note the panel is deliberately empty on open: that is the mode telling the
|
|
405
|
+
user to type.
|
|
406
|
+
|
|
407
|
+
**Search-first needs both a way to search and something to defer**, so `false`
|
|
408
|
+
falls back to loading on open when either is missing:
|
|
409
|
+
|
|
410
|
+
- **Writing is disabled** (`disable-writing`, including the per-surface
|
|
411
|
+
`'mobile'`/`'desktop'` forms) — there is no search field, so a user could never
|
|
412
|
+
trigger the load and the panel would stay empty forever.
|
|
413
|
+
- **The model is non-empty** — its text has to resolve for the selection to
|
|
414
|
+
render. (In-field `BbSelect` upgrades this to a mount prefill; see below.)
|
|
415
|
+
|
|
416
|
+
**Two further cases ignore `prefill` entirely and always load**, because deferring
|
|
417
|
+
would buy nothing or break a promise you made:
|
|
418
|
+
|
|
419
|
+
- **`items` is an array.** There is no request to defer, so laziness would only
|
|
420
|
+
leave the selection unresolved. Options resolve on mount, whatever `prefill`
|
|
421
|
+
says — free, and it makes `text`/`selectedOptions` and coherence work from the
|
|
422
|
+
first paint.
|
|
423
|
+
- **`enforce-coherence` over a non-empty model.** Validating a value is
|
|
424
|
+
impossible without options, so the popover loads and checks on that first load.
|
|
425
|
+
An empty model stays lazy — empty is always coherent, so there is nothing to
|
|
426
|
+
check.
|
|
427
|
+
|
|
428
|
+
Outside those two, a standalone popover with a **provider does not** auto-upgrade
|
|
429
|
+
a seeded model to an eager prefill the way in-field `BbSelect` does. That
|
|
430
|
+
laziness is deliberate: a fetch per instance must be something you ask for (see
|
|
431
|
+
_One popover per table row_).
|
|
432
|
+
|
|
433
|
+
**Do not reach for `prefill: true` to fix a trigger label.** Resolve the label
|
|
434
|
+
from your own state instead (above) — a trigger that needs a fetch to render is
|
|
435
|
+
a trigger that renders wrong until the fetch lands.
|
|
436
|
+
|
|
437
|
+
### Dependent fields and coherence
|
|
438
|
+
|
|
439
|
+
When one picker constrains another, list the upstream value in `dependencies`:
|
|
440
|
+
the provider re-runs whenever a dependency changes (value-hash compared, so array
|
|
441
|
+
identity churn doesn't refetch), debounced by `deps-debounce-time`. Add
|
|
442
|
+
`enforce-coherence` to auto-clean a now-invalid selection — multiple emits the
|
|
443
|
+
**filtered array**, single emits **`null`**, an already-empty selection stays
|
|
444
|
+
quiet. Because enforcement checks the merged result (fetched + stashed), a
|
|
445
|
+
provider that returns `[]` on a `prefill: true` call **wipes a valid selection** —
|
|
446
|
+
always resolve the current selection on component-initiated calls, or enable
|
|
447
|
+
`stash`. The full mechanics are in the Coherence playbook.
|
|
448
|
+
|
|
449
|
+
**You do not need `prefill: true` to make this work.** Asking for
|
|
450
|
+
`enforce-coherence` over a seeded model _is_ a request to validate it, and that
|
|
451
|
+
is impossible without options — so the popover loads on mount by itself and runs
|
|
452
|
+
the check on that first load. The filter bar below relies on exactly that: no
|
|
453
|
+
`prefill` prop anywhere, yet switching the project re-runs the milestone provider
|
|
454
|
+
and clears a milestone the new project lacks, while a still-valid one survives.
|
|
455
|
+
|
|
456
|
+
What that costs is honest and worth knowing: **`enforce-coherence` opts each
|
|
457
|
+
instance into its own load**. One filter, one request. Fifty rows with a provider
|
|
458
|
+
each, fifty requests. That is the trade you asked for by enabling it — and it is
|
|
459
|
+
the reason the table pattern below hands every row the _same array_ instead.
|
|
460
|
+
|
|
461
|
+
#### Never share one ref across instances
|
|
462
|
+
|
|
463
|
+
Independent popovers each with their own model and their own `enforce-coherence`
|
|
464
|
+
are fine: every instance judges **its own** value against **its own** options, so
|
|
465
|
+
they never interfere. What breaks is binding them all to **one** ref:
|
|
476
466
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
await wait(props.transitionDuration);
|
|
485
|
-
if (selectedOptions.value.length) {
|
|
486
|
-
focusFirstSelectedOption();
|
|
487
|
-
} else if (computedAllowWriting.value) {
|
|
488
|
-
focusSearchInput();
|
|
489
|
-
} else {
|
|
490
|
-
focusPopoverFocusProxy();
|
|
491
|
-
}
|
|
492
|
-
return;
|
|
493
|
-
} else if (optionsContainer.value?.getHighlighted()) {
|
|
494
|
-
optionsContainer.value.confirmOption();
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
if (!computedAllowWriting.value) return;
|
|
498
|
-
const text = query.value.trim();
|
|
499
|
-
if (isItemSelected(text)) return;
|
|
500
|
-
emit('option:add', text);
|
|
501
|
-
query.value = '';
|
|
502
|
-
};
|
|
467
|
+
```vue
|
|
468
|
+
<!-- WRONG: every row writes the same value -->
|
|
469
|
+
<script setup>
|
|
470
|
+
const status = ref(null); // ← one ref, shared by every row
|
|
471
|
+
</script>
|
|
472
|
+
<BbSelectPopover v-for="row in rows" v-model="status" enforce-coherence … />
|
|
473
|
+
```
|
|
503
474
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
};
|
|
475
|
+
Now every instance judges **the same** value against **its own** options, and
|
|
476
|
+
coherence's answer to a value it doesn't recognise is `null`. Pick a value only
|
|
477
|
+
one row's options contain and the others each emit `null` in turn — measured with
|
|
478
|
+
5 rows: the pick lands, four instances overwrite it, the model ends `null`. The
|
|
479
|
+
user watches their choice vanish, with nothing logged.
|
|
510
480
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const {
|
|
515
|
-
getter,
|
|
516
|
-
debouncedGetter,
|
|
517
|
-
items: internalItems,
|
|
518
|
-
loading,
|
|
519
|
-
} = useItemsGetter({
|
|
520
|
-
items: toRef(props, 'items'),
|
|
521
|
-
debounce: props.queryDebounceTime,
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
const {
|
|
525
|
-
array: innerStash,
|
|
526
|
-
add: addToStash,
|
|
527
|
-
remove: removeFromStash,
|
|
528
|
-
set: setStash,
|
|
529
|
-
} = useArray<BaseOption>();
|
|
530
|
-
|
|
531
|
-
const {
|
|
532
|
-
hasPrefilled,
|
|
533
|
-
isPrefilling,
|
|
534
|
-
canLoad,
|
|
535
|
-
prefill: prefillItems,
|
|
536
|
-
} = usePrefill({
|
|
537
|
-
trigger: computed(
|
|
538
|
-
() =>
|
|
539
|
-
props.prefill === true ||
|
|
540
|
-
(props.prefill === 'interaction' && hasInteracted.value)
|
|
541
|
-
),
|
|
542
|
-
currentValue: undefined,
|
|
543
|
-
multiple: false,
|
|
544
|
-
fn: async (isPrefill) => {
|
|
545
|
-
await getter(query.value, isPrefill, props.modelValue);
|
|
546
|
-
if (props.stash) {
|
|
547
|
-
addToStash(...options.value.map((o) => o.item));
|
|
548
|
-
}
|
|
549
|
-
},
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
const { data: modelValueIndexedByHash, get: isItemSelected } = useIndexById({
|
|
553
|
-
items: computed(() => [].concat(props.modelValue)),
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
const { options } = useBaseOptions({
|
|
557
|
-
disabled: toRef(props, 'disabled'),
|
|
558
|
-
items: computed(() => [...internalItems.value, ...innerStash.value]),
|
|
559
|
-
itemText: props.itemText,
|
|
560
|
-
itemValue: props.itemValue,
|
|
561
|
-
max: props.max,
|
|
562
|
-
selectable: true,
|
|
563
|
-
selectedIndexedByHash: modelValueIndexedByHash,
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
const { data: optionsIndexedByHash } = useIndexById({
|
|
567
|
-
items: options,
|
|
568
|
-
key: 'valueHash',
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
const { coherent, coherentValue } = useCoherence({
|
|
572
|
-
modelValue: toRef(props, 'modelValue'),
|
|
573
|
-
multiple: props.multiple,
|
|
574
|
-
iteratee: (item) => !!optionsIndexedByHash.value[hash(item)],
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
const selectedOptions = computed<BaseOption[]>(() =>
|
|
578
|
-
([] as any[])
|
|
579
|
-
.concat(props.modelValue)
|
|
580
|
-
.map((v) => optionsIndexedByHash.value[hash(v)])
|
|
581
|
-
.filter(Boolean)
|
|
582
|
-
);
|
|
481
|
+
The component cannot catch this for you: it receives a **value**, not a ref
|
|
482
|
+
identity, and two rows legitimately holding the same value is ordinary. Give
|
|
483
|
+
every row its own model.
|
|
583
484
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
485
|
+
**Toolbar filter pills — milestone follows project**
|
|
486
|
+
|
|
487
|
+
```vue
|
|
488
|
+
<template>
|
|
489
|
+
<div
|
|
490
|
+
class="flex max-w-md flex-wrap items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
491
|
+
>
|
|
492
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Filters</span>
|
|
493
|
+
<BbSelectPopover
|
|
494
|
+
v-model="project"
|
|
495
|
+
compact
|
|
496
|
+
disable-writing
|
|
497
|
+
item-text="name"
|
|
498
|
+
item-value="id"
|
|
499
|
+
:items="projects"
|
|
500
|
+
width="200px"
|
|
501
|
+
>
|
|
502
|
+
<template #activator="{ props }">
|
|
503
|
+
<BbBadge
|
|
504
|
+
append:icon="lucide:chevron-down"
|
|
505
|
+
prepend:icon="lucide:folder"
|
|
506
|
+
size="lg"
|
|
507
|
+
variant="outline"
|
|
508
|
+
>
|
|
509
|
+
<BbBadgeButton v-bind="props">{{ projectLabel }}</BbBadgeButton>
|
|
510
|
+
</BbBadge>
|
|
511
|
+
</template>
|
|
512
|
+
</BbSelectPopover>
|
|
513
|
+
<BbSelectPopover
|
|
514
|
+
v-model="milestone"
|
|
515
|
+
compact
|
|
516
|
+
:dependencies="[project]"
|
|
517
|
+
:deps-debounce-time="150"
|
|
518
|
+
enforce-coherence
|
|
519
|
+
item-text="title"
|
|
520
|
+
item-value="id"
|
|
521
|
+
:items="fetchMilestones"
|
|
522
|
+
no-data-text="No milestone matches"
|
|
523
|
+
width="220px"
|
|
524
|
+
>
|
|
525
|
+
<template #activator="{ props }">
|
|
526
|
+
<BbBadge
|
|
527
|
+
append:icon="lucide:chevron-down"
|
|
528
|
+
prepend:icon="lucide:flag"
|
|
529
|
+
size="lg"
|
|
530
|
+
variant="outline"
|
|
531
|
+
>
|
|
532
|
+
<BbBadgeButton v-bind="props">{{
|
|
533
|
+
milestoneLabel || 'Milestone'
|
|
534
|
+
}}</BbBadgeButton>
|
|
535
|
+
</BbBadge>
|
|
536
|
+
</template>
|
|
537
|
+
</BbSelectPopover>
|
|
538
|
+
</div>
|
|
539
|
+
</template>
|
|
540
|
+
<script setup lang="ts">
|
|
541
|
+
import { computed, ref } from 'vue';
|
|
542
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
543
|
+
|
|
544
|
+
type Project = { id: string; name: string };
|
|
545
|
+
type Milestone = { id: string; title: string; project: string };
|
|
546
|
+
|
|
547
|
+
const projects: Project[] = [
|
|
548
|
+
{ id: 'mobile', name: 'Mobile app' },
|
|
549
|
+
{ id: 'web', name: 'Web dashboard' },
|
|
550
|
+
{ id: 'design', name: 'Design system' },
|
|
551
|
+
];
|
|
552
|
+
|
|
553
|
+
const milestonePool: Milestone[] = [
|
|
554
|
+
{ id: 'ms-beta', title: 'Private beta', project: 'mobile' },
|
|
555
|
+
{ id: 'ms-launch', title: 'App Store launch', project: 'mobile' },
|
|
556
|
+
{ id: 'ms-ga', title: 'GA rollout', project: 'web' },
|
|
557
|
+
{ id: 'ms-audit', title: 'SOC 2 audit', project: 'web' },
|
|
558
|
+
{ id: 'ms-tokens', title: 'Token overhaul', project: 'design' },
|
|
559
|
+
];
|
|
560
|
+
|
|
561
|
+
const project = ref<string>('mobile');
|
|
562
|
+
const milestone = ref<string | null>('ms-beta');
|
|
563
|
+
|
|
564
|
+
// Trigger labels come from our own state — the slot's `text` is empty until
|
|
565
|
+
// the popover first matches the model against loaded items.
|
|
566
|
+
const projectLabel = computed(
|
|
567
|
+
() => projects.find((p) => p.id === project.value)?.name ?? 'Project'
|
|
604
568
|
);
|
|
605
|
-
|
|
606
|
-
() =>
|
|
607
|
-
async () => {
|
|
608
|
-
if (!canLoad.value) return;
|
|
609
|
-
await getter(query.value, true, props.modelValue);
|
|
610
|
-
if (props.enforceCoherence && !coherent.value) {
|
|
611
|
-
emit('update:modelValue', coherentValue.value);
|
|
612
|
-
optionsContainer.value?.blur();
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
{ debounce: props.depsDebounceTime }
|
|
569
|
+
const milestoneLabel = computed(
|
|
570
|
+
() => milestonePool.find((m) => m.id === milestone.value)?.title ?? ''
|
|
616
571
|
);
|
|
617
572
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
if (!props.multiple) {
|
|
637
|
-
close();
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
const onOptionUnselected = async (option: BaseOption) => {
|
|
642
|
-
removeFromStash((o) => o.valueHash !== option.valueHash);
|
|
643
|
-
if (props.multiple) {
|
|
644
|
-
const copy = { ...modelValueIndexedByHash.value };
|
|
645
|
-
delete copy[option.valueHash];
|
|
646
|
-
emit('update:modelValue', Object.values(copy));
|
|
647
|
-
} else {
|
|
648
|
-
emit('update:modelValue', null);
|
|
649
|
-
}
|
|
650
|
-
if (props.multiple && computedAllowWriting.value && shown.value) {
|
|
651
|
-
focusSearchInput();
|
|
652
|
-
} else {
|
|
653
|
-
focusActivator();
|
|
654
|
-
}
|
|
573
|
+
// Query-aware provider. Component-initiated calls (the mount load, project
|
|
574
|
+
// changes via `dependencies`) pass `prefill === true` and get the project's
|
|
575
|
+
// full list back, so a still-valid selection stays resolvable —
|
|
576
|
+
// `enforce-coherence` then clears only a milestone the new project lacks.
|
|
577
|
+
// No `prefill` prop needed: `enforce-coherence` over a seeded model loads on
|
|
578
|
+
// mount by itself, because validating a value requires having the options.
|
|
579
|
+
const fetchMilestones = (
|
|
580
|
+
query: string,
|
|
581
|
+
prefill: boolean,
|
|
582
|
+
_modelValue: string | null
|
|
583
|
+
): Promise<Milestone[]> => {
|
|
584
|
+
const inProject = milestonePool.filter((m) => m.project === project.value);
|
|
585
|
+
const q = query.trim().toLowerCase();
|
|
586
|
+
const matches = prefill
|
|
587
|
+
? inProject
|
|
588
|
+
: inProject.filter((m) => m.title.toLowerCase().includes(q));
|
|
589
|
+
return new Promise((resolve) => setTimeout(() => resolve(matches), 200));
|
|
655
590
|
};
|
|
591
|
+
</script>
|
|
592
|
+
```
|
|
656
593
|
|
|
657
|
-
|
|
658
|
-
* Filter options based on the current query.
|
|
659
|
-
*/
|
|
660
|
-
const filteredOptions = computed<BaseOption<Item>[]>(() => {
|
|
661
|
-
const queryMatchesASelectedOption = selectedOptions.value.some(
|
|
662
|
-
(option: BaseOption) => option.text === query.value
|
|
663
|
-
);
|
|
664
|
-
const shouldFilter =
|
|
665
|
-
(props.multiple && query.value) ||
|
|
666
|
-
(props.filterBy === 'not_stashed' && query.value) ||
|
|
667
|
-
(query.value && !queryMatchesASelectedOption && !props.multiple);
|
|
668
|
-
|
|
669
|
-
if (!shouldFilter || !props.filterBy) return options.value;
|
|
670
|
-
|
|
671
|
-
const stashedHashes =
|
|
672
|
-
props.filterBy === 'not_stashed'
|
|
673
|
-
? new Set(innerStash.value.map((o) => o.valueHash))
|
|
674
|
-
: null;
|
|
675
|
-
|
|
676
|
-
return options.value.filter((item: BaseOption) => {
|
|
677
|
-
let matchedAnything = false;
|
|
678
|
-
if (typeof props.filterBy === 'function') {
|
|
679
|
-
matchedAnything = props.filterBy(item.value, item.item, query.value);
|
|
680
|
-
} else if (props.filterBy === 'not_stashed' && query.value) {
|
|
681
|
-
matchedAnything = !stashedHashes!.has(item.valueHash);
|
|
682
|
-
} else if (Array.isArray(props.filterBy) && props.filterBy.length) {
|
|
683
|
-
matchedAnything = matchAnyKey(
|
|
684
|
-
item.item,
|
|
685
|
-
props.filterBy,
|
|
686
|
-
query.value.trim()
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
return matchedAnything || matchAnyKey(item, ['text'], query.value.trim());
|
|
690
|
-
});
|
|
691
|
-
});
|
|
594
|
+
### One popover per table row (inline edit)
|
|
692
595
|
|
|
693
|
-
|
|
596
|
+
The status-pill-in-a-table is the popover's signature use — and the structure of
|
|
597
|
+
the **page** decides whether it scales. Three rules, in priority order:
|
|
694
598
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
599
|
+
**1. Resolve the options once, for the whole page. Pass an array, not a provider.**
|
|
600
|
+
This is the load-bearing decision. A provider is per-instance, so 50 rows means
|
|
601
|
+
50 popovers each owning a fetch. Lift the fetch to the page, `await` it once, and
|
|
602
|
+
hand every row the same array — 50 rows, **zero** requests, because there is
|
|
603
|
+
nothing left to fetch. Array `items` are never lazy either: every row resolves on
|
|
604
|
+
mount, so selections and coherence work immediately with no `prefill` prop.
|
|
700
605
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
if (!groupIndex[groupId]) {
|
|
707
|
-
groupIndex[groupId] = {
|
|
708
|
-
id: groupId,
|
|
709
|
-
data: groupData ?? null,
|
|
710
|
-
options: [],
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
groupIndex[groupId].options.push(option);
|
|
714
|
-
}
|
|
715
|
-
return Object.values(groupIndex).filter((g) => g.options.length);
|
|
716
|
-
});
|
|
606
|
+
```ts
|
|
607
|
+
// Page level — once, not per row.
|
|
608
|
+
const statuses = ref<Status[]>([]);
|
|
609
|
+
onMounted(async () => (statuses.value = await api.statuses.list()));
|
|
610
|
+
```
|
|
717
611
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (shown.value) return;
|
|
729
|
-
hasInteracted.value = true;
|
|
730
|
-
resume();
|
|
731
|
-
shown.value = true;
|
|
732
|
-
|
|
733
|
-
if (!hasPrefilled.value && !isPrefilling.value) {
|
|
734
|
-
await prefillItems();
|
|
735
|
-
}
|
|
612
|
+
**2. Hold the edit off the row — a page-level draft map keyed by id.** Never
|
|
613
|
+
`v-model="item.status"`: writing straight back onto the slot's `item` mutates the
|
|
614
|
+
table's data, so any refetch (a filter change, a poll) silently discards the edit,
|
|
615
|
+
and a partially-dirty row has no clean before/after. Instead bind the value _down_
|
|
616
|
+
(`:model-value="valueOf(item, 'status')"`) and commit it _up_ into a `drafts` map
|
|
617
|
+
(`@update:model-value="setField(item, 'status', $event)"`). The map is the dirty
|
|
618
|
+
set **and** the save payload; the row object stays pristine. This is the table
|
|
619
|
+
case of the [inline-edit playbook](../../ai/guides/inline-edit-playbook.md) —
|
|
620
|
+
reach for autosave there instead when the domain wants every pick to persist on
|
|
621
|
+
the spot.
|
|
736
622
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
623
|
+
**3. Label the pill from your own state, not from the popover.**
|
|
624
|
+
`{{ labelFor(valueOf(item, 'status')) }}` resolved against your page-level array —
|
|
625
|
+
never the slot's `text`. The badge must read correctly on first paint for every
|
|
626
|
+
row, including the hundreds nobody ever opens.
|
|
740
627
|
|
|
741
|
-
|
|
628
|
+
**Inline status cell — soft badge editor**
|
|
742
629
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
630
|
+
```vue
|
|
631
|
+
<template>
|
|
632
|
+
<div class="flex max-w-xl flex-col gap-1.5">
|
|
633
|
+
<div
|
|
634
|
+
class="overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
|
|
635
|
+
>
|
|
636
|
+
<BbTable
|
|
637
|
+
caption="Tickets — edit statuses, then Save the batch"
|
|
638
|
+
:columns="columns"
|
|
639
|
+
compact
|
|
640
|
+
item-value="id"
|
|
641
|
+
:items="tickets"
|
|
642
|
+
>
|
|
643
|
+
<!-- One popover per row. Three rules keep it cheap and correct:
|
|
644
|
+
1. `:items="statuses"` is the SAME array for every row — resolved
|
|
645
|
+
once at page level, so N rows cost zero extra requests.
|
|
646
|
+
2. The edit is held in a page-level `drafts` map keyed by row id,
|
|
647
|
+
NEVER written back onto `item`. Bind the value down with
|
|
648
|
+
`valueOf` and commit up with `setField`; the row object stays
|
|
649
|
+
pristine, so a refetch (filter, poll) can't wipe an edit.
|
|
650
|
+
3. The pill renders from `valueOf(item, 'status')`, our own state —
|
|
651
|
+
never the slot's `text` — so it is correct on first paint
|
|
652
|
+
whether or not the popover is ever opened. -->
|
|
653
|
+
<template #status="{ item }">
|
|
654
|
+
<BbSelectPopover
|
|
655
|
+
compact
|
|
656
|
+
disable-writing
|
|
657
|
+
item-text="label"
|
|
658
|
+
item-value="value"
|
|
659
|
+
:items="statuses"
|
|
660
|
+
:model-value="valueOf(item, 'status')"
|
|
661
|
+
width="180px"
|
|
662
|
+
@update:model-value="setField(item, 'status', $event as Status)"
|
|
663
|
+
>
|
|
664
|
+
<template #activator="{ props, shown }">
|
|
665
|
+
<!-- Soft status pill: the badge IS the trigger. A dirty pill
|
|
666
|
+
(edited, not yet saved) keeps a deeper border until Save. -->
|
|
667
|
+
<BbBadge
|
|
668
|
+
append:icon="lucide:chevron-down"
|
|
669
|
+
:class="{
|
|
670
|
+
'status-trigger--editing': shown,
|
|
671
|
+
'status-trigger--dirty': isDirty(item),
|
|
672
|
+
}"
|
|
673
|
+
size="lg"
|
|
674
|
+
:variant="variantFor(valueOf(item, 'status'))"
|
|
675
|
+
>
|
|
676
|
+
<BbBadgeButton v-bind="props">{{
|
|
677
|
+
labelFor(valueOf(item, 'status'))
|
|
678
|
+
}}</BbBadgeButton>
|
|
679
|
+
</BbBadge>
|
|
680
|
+
</template>
|
|
681
|
+
</BbSelectPopover>
|
|
682
|
+
</template>
|
|
683
|
+
</BbTable>
|
|
684
|
+
</div>
|
|
751
685
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
686
|
+
<div class="flex items-center gap-2">
|
|
687
|
+
<BbButton :disabled="!dirtyCount" size="sm" @click="save">
|
|
688
|
+
Save{{ dirtyCount ? ` (${dirtyCount})` : '' }}
|
|
689
|
+
</BbButton>
|
|
690
|
+
<span class="text-xs text-gray-500">
|
|
691
|
+
Edits live in a draft map keyed by row id — the map is the save payload.
|
|
692
|
+
Rows are never mutated, so nothing is lost on a refetch.
|
|
693
|
+
</span>
|
|
694
|
+
</div>
|
|
695
|
+
</div>
|
|
696
|
+
</template>
|
|
697
|
+
<script setup lang="ts">
|
|
698
|
+
import { computed, reactive, ref } from 'vue';
|
|
699
|
+
import {
|
|
700
|
+
BbBadge,
|
|
701
|
+
BbBadgeButton,
|
|
702
|
+
BbButton,
|
|
703
|
+
BbSelectPopover,
|
|
704
|
+
BbTable,
|
|
705
|
+
} from 'bitboss-ui';
|
|
706
|
+
import type { BadgeVariantType, BbTableColumn } from 'bitboss-ui';
|
|
707
|
+
|
|
708
|
+
type Status = 'open' | 'in-progress' | 'blocked' | 'done';
|
|
709
|
+
type Ticket = { id: string; ref: string; title: string; status: Status };
|
|
710
|
+
type Patch = Partial<Pick<Ticket, 'status'>>;
|
|
762
711
|
|
|
763
|
-
const toggle = () => {
|
|
764
|
-
if (shown.value) return close();
|
|
765
|
-
return open();
|
|
766
|
-
};
|
|
767
712
|
/**
|
|
768
|
-
*
|
|
713
|
+
* The option list is resolved ONCE for the whole page and handed to every row as
|
|
714
|
+
* a plain array. With a provider per row, each popover would own its own fetch;
|
|
715
|
+
* as an array there is no request at all, and every row resolves on mount.
|
|
769
716
|
*/
|
|
717
|
+
const statuses: Array<{ value: Status; label: string }> = [
|
|
718
|
+
{ value: 'open', label: 'Open' },
|
|
719
|
+
{ value: 'in-progress', label: 'In progress' },
|
|
720
|
+
{ value: 'blocked', label: 'Blocked' },
|
|
721
|
+
{ value: 'done', label: 'Done' },
|
|
722
|
+
];
|
|
723
|
+
|
|
724
|
+
const tickets = ref<Ticket[]>([
|
|
725
|
+
{
|
|
726
|
+
id: 't-1',
|
|
727
|
+
ref: 'BB-104',
|
|
728
|
+
title: 'Popover drops focus on Esc',
|
|
729
|
+
status: 'open',
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
id: 't-2',
|
|
733
|
+
ref: 'BB-118',
|
|
734
|
+
title: 'Table header misaligns at 320px',
|
|
735
|
+
status: 'in-progress',
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
id: 't-3',
|
|
739
|
+
ref: 'BB-121',
|
|
740
|
+
title: 'Token audit for dark mode',
|
|
741
|
+
status: 'blocked',
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: 't-4',
|
|
745
|
+
ref: 'BB-130',
|
|
746
|
+
title: 'Ship the migration guide',
|
|
747
|
+
status: 'done',
|
|
748
|
+
},
|
|
749
|
+
]);
|
|
770
750
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const active = ref(false);
|
|
751
|
+
const columns: Array<BbTableColumn<Ticket>> = [
|
|
752
|
+
{ key: 'ref', label: 'Ref', width: 90 },
|
|
753
|
+
{ key: 'title', label: 'Title' },
|
|
754
|
+
{ key: 'status', label: 'Status', width: 140 },
|
|
755
|
+
];
|
|
777
756
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
};
|
|
781
|
-
const setInactive = () => {
|
|
782
|
-
if (!active.value) return;
|
|
783
|
-
active.value = false;
|
|
784
|
-
emit('inactive');
|
|
785
|
-
};
|
|
757
|
+
/* ---- Draft map: the edit lives here, never on the row -------------------- */
|
|
758
|
+
|
|
759
|
+
const drafts = reactive<Record<string, Patch>>({});
|
|
786
760
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
761
|
+
/**
|
|
762
|
+
* Read-through: the draft wins, the row is the fallback. Use a `key in` check,
|
|
763
|
+
* NOT `??` — a field legitimately drafted to a nullish value would otherwise
|
|
764
|
+
* fall through to the row.
|
|
765
|
+
*/
|
|
766
|
+
const valueOf = <K extends keyof Patch>(item: Ticket, key: K): Ticket[K] => {
|
|
767
|
+
const draft = drafts[item.id];
|
|
768
|
+
return draft && key in draft ? (draft[key] as Ticket[K]) : item[key];
|
|
794
769
|
};
|
|
795
770
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
771
|
+
/** One setter covers every field; re-picking the original un-dirties for free. */
|
|
772
|
+
const setField = <K extends keyof Patch>(
|
|
773
|
+
item: Ticket,
|
|
774
|
+
key: K,
|
|
775
|
+
value: Patch[K]
|
|
776
|
+
) => {
|
|
777
|
+
if (value === item[key]) {
|
|
778
|
+
delete drafts[item.id]?.[key];
|
|
779
|
+
if (drafts[item.id] && !Object.keys(drafts[item.id]).length) {
|
|
780
|
+
delete drafts[item.id];
|
|
801
781
|
}
|
|
802
|
-
|
|
803
|
-
}
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
(drafts[item.id] ??= {})[key] = value;
|
|
804
785
|
};
|
|
805
|
-
/**
|
|
806
|
-
* END ACTIVE STATE
|
|
807
|
-
*/
|
|
808
786
|
|
|
809
|
-
const
|
|
810
|
-
|
|
787
|
+
const isDirty = (item: Ticket) => drafts[item.id]?.status !== undefined;
|
|
788
|
+
const dirtyCount = computed(() => Object.keys(drafts).length);
|
|
789
|
+
|
|
790
|
+
/** The map IS the payload. Here it patches the local rows; a real app PATCHes. */
|
|
791
|
+
const save = () => {
|
|
792
|
+
tickets.value = tickets.value.map((row) =>
|
|
793
|
+
drafts[row.id] ? { ...row, ...drafts[row.id] } : row
|
|
794
|
+
);
|
|
795
|
+
Object.keys(drafts).forEach((id) => delete drafts[id]);
|
|
811
796
|
};
|
|
812
797
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
* to the activator element, following the BbDropdown pattern.
|
|
817
|
-
*/
|
|
818
|
-
let cleanupActivator: (() => void) | null = null;
|
|
819
|
-
|
|
820
|
-
watch(
|
|
821
|
-
activatorEl,
|
|
822
|
-
(el) => {
|
|
823
|
-
if (cleanupActivator) {
|
|
824
|
-
cleanupActivator();
|
|
825
|
-
cleanupActivator = null;
|
|
826
|
-
}
|
|
827
|
-
if (!el) return;
|
|
828
|
-
|
|
829
|
-
el.setAttribute('role', 'combobox');
|
|
830
|
-
el.setAttribute('aria-haspopup', 'listbox');
|
|
831
|
-
|
|
832
|
-
const handleClick = (event: MouseEvent) => {
|
|
833
|
-
emit('click', event);
|
|
834
|
-
if (props.disabled || props.readonly) return;
|
|
835
|
-
event.preventDefault();
|
|
836
|
-
toggle();
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
const handleKeydown = (event: KeyboardEvent) => {
|
|
840
|
-
switch (event.key) {
|
|
841
|
-
case 'ArrowDown':
|
|
842
|
-
event.preventDefault();
|
|
843
|
-
onArrowDown();
|
|
844
|
-
break;
|
|
845
|
-
case 'ArrowUp':
|
|
846
|
-
event.preventDefault();
|
|
847
|
-
onArrowUp();
|
|
848
|
-
break;
|
|
849
|
-
case 'Enter':
|
|
850
|
-
event.preventDefault();
|
|
851
|
-
onEnter();
|
|
852
|
-
break;
|
|
853
|
-
case 'Escape':
|
|
854
|
-
if (shown.value) {
|
|
855
|
-
event.preventDefault();
|
|
856
|
-
onEscape();
|
|
857
|
-
}
|
|
858
|
-
break;
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
const handleFocus = (event: FocusEvent) => {
|
|
863
|
-
emit('focus', event);
|
|
864
|
-
setActive();
|
|
865
|
-
};
|
|
866
|
-
|
|
867
|
-
const handleBlur = (event: FocusEvent) => {
|
|
868
|
-
emit('blur', event);
|
|
869
|
-
syncActiveStateOnBlur();
|
|
870
|
-
};
|
|
871
|
-
|
|
872
|
-
el.addEventListener('click', handleClick);
|
|
873
|
-
el.addEventListener('keydown', handleKeydown);
|
|
874
|
-
el.addEventListener('focus', handleFocus);
|
|
875
|
-
el.addEventListener('blur', handleBlur);
|
|
876
|
-
|
|
877
|
-
cleanupActivator = () => {
|
|
878
|
-
el.removeEventListener('click', handleClick);
|
|
879
|
-
el.removeEventListener('keydown', handleKeydown);
|
|
880
|
-
el.removeEventListener('focus', handleFocus);
|
|
881
|
-
el.removeEventListener('blur', handleBlur);
|
|
882
|
-
el.removeAttribute('role');
|
|
883
|
-
el.removeAttribute('aria-haspopup');
|
|
884
|
-
el.removeAttribute('aria-expanded');
|
|
885
|
-
el.removeAttribute('aria-controls');
|
|
886
|
-
el.removeAttribute('aria-invalid');
|
|
887
|
-
el.removeAttribute('aria-describedby');
|
|
888
|
-
el.removeAttribute('aria-activedescendant');
|
|
889
|
-
};
|
|
890
|
-
},
|
|
891
|
-
{ immediate: true }
|
|
892
|
-
);
|
|
798
|
+
/* Row labels come from our own state — stable regardless of popover state. */
|
|
799
|
+
const labelFor = (status: Status) =>
|
|
800
|
+
statuses.find((s) => s.value === status)?.label ?? '—';
|
|
893
801
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
computedAriaDescribedby,
|
|
901
|
-
activeDescendantId,
|
|
902
|
-
] as const,
|
|
903
|
-
([el, isShown, hasListBox, ariaInvalid, ariaDescribedby, descendantId]) => {
|
|
904
|
-
if (!el) return;
|
|
905
|
-
el.setAttribute('aria-expanded', String(isShown));
|
|
906
|
-
if (ariaInvalid != null) {
|
|
907
|
-
el.setAttribute('aria-invalid', ariaInvalid);
|
|
908
|
-
} else {
|
|
909
|
-
el.removeAttribute('aria-invalid');
|
|
910
|
-
}
|
|
911
|
-
if (hasListBox) {
|
|
912
|
-
el.setAttribute('aria-controls', `${id}_listbox`);
|
|
913
|
-
} else {
|
|
914
|
-
el.removeAttribute('aria-controls');
|
|
915
|
-
}
|
|
916
|
-
if (ariaDescribedby != null) {
|
|
917
|
-
el.setAttribute('aria-describedby', ariaDescribedby);
|
|
918
|
-
} else {
|
|
919
|
-
el.removeAttribute('aria-describedby');
|
|
920
|
-
}
|
|
921
|
-
if (isShown && descendantId) {
|
|
922
|
-
el.setAttribute('aria-activedescendant', descendantId);
|
|
923
|
-
} else {
|
|
924
|
-
el.removeAttribute('aria-activedescendant');
|
|
925
|
-
}
|
|
926
|
-
},
|
|
927
|
-
{ immediate: true }
|
|
928
|
-
);
|
|
802
|
+
const VARIANT: Record<Status, BadgeVariantType> = {
|
|
803
|
+
open: 'soft-gray',
|
|
804
|
+
'in-progress': 'soft-blue',
|
|
805
|
+
blocked: 'soft-red',
|
|
806
|
+
done: 'soft-green',
|
|
807
|
+
};
|
|
929
808
|
|
|
930
|
-
|
|
931
|
-
cleanupActivator?.();
|
|
932
|
-
document.removeEventListener('click', onOutsideInteraction);
|
|
933
|
-
document.removeEventListener('focusin', onOutsideInteraction);
|
|
934
|
-
});
|
|
935
|
-
/**
|
|
936
|
-
* END ACTIVATOR LISTENERS
|
|
937
|
-
*/
|
|
809
|
+
const variantFor = (status: Status) => VARIANT[status];
|
|
938
810
|
</script>
|
|
939
811
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
812
|
+
<!-- Soft status palette — canonical source: ai/guides/design-language.md § Using color.
|
|
813
|
+
Non-scoped so variant classes on the child badge receive the accents. -->
|
|
814
|
+
<style>
|
|
815
|
+
.bb-badge.bb-badge--soft-gray {
|
|
816
|
+
--accent: #64748b;
|
|
817
|
+
}
|
|
818
|
+
.bb-badge.bb-badge--soft-blue {
|
|
819
|
+
--accent: #2563eb;
|
|
820
|
+
}
|
|
821
|
+
.bb-badge.bb-badge--soft-green {
|
|
822
|
+
--accent: #16a34a;
|
|
823
|
+
}
|
|
824
|
+
.bb-badge.bb-badge--soft-red {
|
|
825
|
+
--accent: #dc2626;
|
|
826
|
+
}
|
|
827
|
+
.bb-badge.bb-badge--soft-gray,
|
|
828
|
+
.bb-badge.bb-badge--soft-blue,
|
|
829
|
+
.bb-badge.bb-badge--soft-green,
|
|
830
|
+
.bb-badge.bb-badge--soft-red {
|
|
831
|
+
--bg: color-mix(in oklab, var(--accent) 15%, var(--bb-panel));
|
|
832
|
+
--color: color-mix(in oklab, var(--accent) 80%, var(--bb-text));
|
|
833
|
+
--border-width: 1px;
|
|
834
|
+
--border-color: color-mix(in oklab, var(--accent) 25%, var(--bb-panel));
|
|
835
|
+
--ring: color-mix(in oklab, var(--accent) 45%, transparent);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/* Unsaved edit: hold a deeper border until the batch is saved. */
|
|
839
|
+
.status-trigger--dirty {
|
|
840
|
+
--border-width: 1px;
|
|
841
|
+
--border-color: color-mix(in oklab, var(--color) 60%, var(--bb-panel));
|
|
842
|
+
}
|
|
943
843
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
844
|
+
/* While open, deepen the badge's own hue — not a generic indigo ring. */
|
|
845
|
+
.status-trigger--editing {
|
|
846
|
+
--bg: color-mix(in oklab, var(--color) 25%, var(--bb-panel));
|
|
847
|
+
--border-color: color-mix(in oklab, var(--color) 45%, var(--bb-panel));
|
|
848
|
+
box-shadow: 0 0 0 var(--bb-ring-size) var(--ring);
|
|
849
|
+
}
|
|
850
|
+
</style>
|
|
851
|
+
```
|
|
951
852
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
853
|
+
**Why this shape and not the obvious one.** The instinct is to give each row a
|
|
854
|
+
provider and `enforce-coherence` so rows keep themselves honest. That inverts the
|
|
855
|
+
cost: coherence with a seeded model makes every row load, so the table's request
|
|
856
|
+
count tracks its row count, and each row can rewrite its own model from a
|
|
857
|
+
response you never see. The page-level array collapses all of it — one source,
|
|
858
|
+
one fetch, every row consistent by construction, nothing to prune because every
|
|
859
|
+
row already draws from the same list.
|
|
860
|
+
|
|
861
|
+
If rows genuinely need **different** option sets (per-type statuses, say),
|
|
862
|
+
resolve _those_ lists at page level too — one fetch per distinct set, keyed by
|
|
863
|
+
type — and pass the right array down. Still zero per-row requests. Reach for a
|
|
864
|
+
per-row provider only when the options truly depend on that row's identity and
|
|
865
|
+
cannot be batched, and then expect the request count to follow the row count.
|
|
866
|
+
|
|
867
|
+
Pruning stale values in a table is a **page-level** job, not a per-row one: when
|
|
868
|
+
the source list narrows, reconcile the rows yourself in one pass, where you can
|
|
869
|
+
show the user what changed (or ask them) instead of nulling values invisibly.
|
|
870
|
+
|
|
871
|
+
### Create options on the fly (`option:add`)
|
|
872
|
+
|
|
873
|
+
Pressing Enter in the search field with **no option highlighted** emits
|
|
874
|
+
`option:add` with the trimmed query (it can be `''`, and it won't fire if the
|
|
875
|
+
query already matches a selected option). The component creates nothing itself:
|
|
876
|
+
append the new item to `items` and select it. This is the idiomatic pattern for
|
|
877
|
+
closed-but-growable vocabularies — the same as `BbSelect`. The behaviour is gated
|
|
878
|
+
by the search field existing; `disable-writing` (which removes the field) also
|
|
879
|
+
disables it.
|
|
968
880
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
}
|
|
881
|
+
```ts
|
|
882
|
+
const roles = ref<string[]>(['Reviewer', 'Approver']);
|
|
883
|
+
const role = ref<string | null>(null);
|
|
884
|
+
|
|
885
|
+
const onAdd = (text: string) => {
|
|
886
|
+
const name = text.trim();
|
|
887
|
+
if (!name || roles.value.includes(name)) return;
|
|
888
|
+
roles.value.push(name);
|
|
889
|
+
role.value = name;
|
|
890
|
+
};
|
|
891
|
+
```
|
|
981
892
|
|
|
982
|
-
|
|
983
|
-
opacity: 1;
|
|
984
|
-
}
|
|
893
|
+
### Group options
|
|
985
894
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
895
|
+
`group-by` (a field path or a function) buckets options under headers; the
|
|
896
|
+
`#group` slot styles each header and receives `{ text, item, index, length,
|
|
897
|
+
disabled }` — `length` is the group size, `index` the list index of its first
|
|
898
|
+
option. Identical to `BbSelect`, anchored to your trigger.
|
|
991
899
|
|
|
992
|
-
|
|
993
|
-
.bb-select-popover__search-spinner {
|
|
994
|
-
opacity: 0;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
900
|
+
**Roles grouped by department**
|
|
997
901
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
902
|
+
```vue
|
|
903
|
+
<template>
|
|
904
|
+
<div
|
|
905
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
906
|
+
>
|
|
907
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Role</span>
|
|
908
|
+
<BbSelectPopover
|
|
909
|
+
v-model="role"
|
|
910
|
+
compact
|
|
911
|
+
group-by="department"
|
|
912
|
+
item-text="label"
|
|
913
|
+
item-value="value"
|
|
914
|
+
:items="roles"
|
|
915
|
+
width="240px"
|
|
916
|
+
>
|
|
917
|
+
<template #activator="{ props }">
|
|
918
|
+
<BbBadge
|
|
919
|
+
append:icon="lucide:chevron-down"
|
|
920
|
+
prepend:icon="lucide:briefcase"
|
|
921
|
+
size="lg"
|
|
922
|
+
variant="outline"
|
|
923
|
+
>
|
|
924
|
+
<BbBadgeButton v-bind="props">{{
|
|
925
|
+
roleLabel || 'Assign role'
|
|
926
|
+
}}</BbBadgeButton>
|
|
927
|
+
</BbBadge>
|
|
928
|
+
</template>
|
|
929
|
+
<template #group="{ text, length }">
|
|
930
|
+
<div
|
|
931
|
+
class="flex items-center justify-between px-2 text-xs font-medium text-gray-500"
|
|
932
|
+
>
|
|
933
|
+
<span>{{ text }}</span>
|
|
934
|
+
<span>{{ length }}</span>
|
|
935
|
+
</div>
|
|
936
|
+
</template>
|
|
937
|
+
</BbSelectPopover>
|
|
938
|
+
</div>
|
|
939
|
+
</template>
|
|
940
|
+
<script setup lang="ts">
|
|
941
|
+
import { computed, ref } from 'vue';
|
|
942
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
943
|
+
|
|
944
|
+
type Role = { label: string; value: string; department: string };
|
|
945
|
+
|
|
946
|
+
const roles: Role[] = [
|
|
947
|
+
{ label: 'Analyst', value: 'mkt-analyst', department: 'Marketing' },
|
|
948
|
+
{ label: 'Manager', value: 'mkt-manager', department: 'Marketing' },
|
|
949
|
+
{ label: 'Frontend', value: 'eng-fe', department: 'Engineering' },
|
|
950
|
+
{ label: 'Backend', value: 'eng-be', department: 'Engineering' },
|
|
951
|
+
];
|
|
952
|
+
|
|
953
|
+
const role = ref<string | null>(null);
|
|
954
|
+
const roleLabel = computed(
|
|
955
|
+
() => roles.find((r) => r.value === role.value)?.label ?? ''
|
|
956
|
+
);
|
|
957
|
+
</script>
|
|
958
|
+
```
|
|
1002
959
|
|
|
1003
|
-
|
|
1004
|
-
.bb-select-popover__search-error-icon {
|
|
1005
|
-
opacity: 0;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
960
|
+
### Panel header, footer and empty/loading states
|
|
1008
961
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
962
|
+
`#header` and `#footer` pin content above the search field and below the options
|
|
963
|
+
list, in **both** the desktop popover and the mobile adaptive sheet. Both receive
|
|
964
|
+
`{ close, clear, focus, query, selectedOptions }` — ideal for a title and a
|
|
965
|
+
"Done" action. Two more panel slots cover async states: `#loading` and
|
|
966
|
+
`#no-data` (both receive `{ query }`; `#no-data` also a `focus()` callback), or
|
|
967
|
+
their string equivalents `loading-text` / `no-data-text`.
|
|
1012
968
|
|
|
1013
|
-
|
|
1014
|
-
|
|
969
|
+
**A titled panel with a done action**
|
|
970
|
+
|
|
971
|
+
```vue
|
|
972
|
+
<template>
|
|
973
|
+
<div
|
|
974
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
975
|
+
>
|
|
976
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Destination</span>
|
|
977
|
+
<BbSelectPopover
|
|
978
|
+
v-model="city"
|
|
979
|
+
compact
|
|
980
|
+
item-text="label"
|
|
981
|
+
item-value="value"
|
|
982
|
+
:items="cities"
|
|
983
|
+
width="240px"
|
|
984
|
+
>
|
|
985
|
+
<template #activator="{ props }">
|
|
986
|
+
<BbBadge
|
|
987
|
+
append:icon="lucide:chevron-down"
|
|
988
|
+
prepend:icon="lucide:map-pin"
|
|
989
|
+
size="lg"
|
|
990
|
+
variant="outline"
|
|
991
|
+
>
|
|
992
|
+
<BbBadgeButton v-bind="props">{{
|
|
993
|
+
cityLabel || 'Add destination'
|
|
994
|
+
}}</BbBadgeButton>
|
|
995
|
+
</BbBadge>
|
|
996
|
+
</template>
|
|
997
|
+
<template #header>
|
|
998
|
+
<span class="text-sm font-medium">Popular destinations</span>
|
|
999
|
+
</template>
|
|
1000
|
+
<template #footer="{ close, clear }">
|
|
1001
|
+
<div class="flex w-full items-center gap-2">
|
|
1002
|
+
<BbButton class="mr-auto" variant="ghost" @click="clear"
|
|
1003
|
+
>Clear</BbButton
|
|
1004
|
+
>
|
|
1005
|
+
<BbButton variant="primary" @click="close">Done</BbButton>
|
|
1006
|
+
</div>
|
|
1007
|
+
</template>
|
|
1008
|
+
</BbSelectPopover>
|
|
1009
|
+
</div>
|
|
1010
|
+
</template>
|
|
1011
|
+
<script setup lang="ts">
|
|
1012
|
+
import { computed, ref } from 'vue';
|
|
1013
|
+
import { BbBadge, BbBadgeButton, BbButton, BbSelectPopover } from 'bitboss-ui';
|
|
1014
|
+
|
|
1015
|
+
type City = { label: string; value: string };
|
|
1016
|
+
|
|
1017
|
+
const cities: City[] = [
|
|
1018
|
+
{ label: 'Amsterdam', value: 'ams' },
|
|
1019
|
+
{ label: 'Berlin', value: 'ber' },
|
|
1020
|
+
{ label: 'Lisbon', value: 'lis' },
|
|
1021
|
+
{ label: 'Milan', value: 'mil' },
|
|
1022
|
+
];
|
|
1023
|
+
|
|
1024
|
+
const city = ref<string | null>(null);
|
|
1025
|
+
const cityLabel = computed(
|
|
1026
|
+
() => cities.find((c) => c.value === city.value)?.label ?? ''
|
|
1027
|
+
);
|
|
1028
|
+
</script>
|
|
1029
|
+
```
|
|
1015
1030
|
|
|
1016
|
-
|
|
1017
|
-
grid-template-rows: 1fr;
|
|
1018
|
-
opacity: 1;
|
|
1019
|
-
transition-delay: 0s, 00ms;
|
|
1031
|
+
### Search field and filtering
|
|
1020
1032
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
&.bb-listbox--no-data {
|
|
1028
|
-
.bb-listbox__outer-container {
|
|
1029
|
-
.bb-listbox__inner-container {
|
|
1030
|
-
.bb-listbox__no-data {
|
|
1031
|
-
display: block;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
&.bb-listbox--loading {
|
|
1037
|
-
.bb-listbox__outer-container {
|
|
1038
|
-
.bb-listbox__inner-container {
|
|
1039
|
-
.bb-listbox__loading {
|
|
1040
|
-
display: block;
|
|
1041
|
-
text-align: left;
|
|
1042
|
-
}
|
|
1043
|
-
.bb-listbox__loading {
|
|
1044
|
-
display: hidden;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1033
|
+
The panel carries a search field (role `searchbox`). Filtering follows the shared
|
|
1034
|
+
semantics (Options/Fetch playbooks): array items filter locally by
|
|
1035
|
+
case-insensitive **substring** against the display text; `filter-by: ['path', …]`
|
|
1036
|
+
searches the listed paths **instead of** the text (every path must exist on every
|
|
1037
|
+
item or matching throws); with a provider, the local substring filter **still
|
|
1038
|
+
applies on top of** the response, so keep server matches substring-compatible.
|
|
1049
1039
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
}
|
|
1040
|
+
`disable-writing` removes the search field entirely — `true` everywhere, or
|
|
1041
|
+
`'mobile'` / `'desktop'` to drop it on one platform only. It is also the
|
|
1042
|
+
supported way to disable typing/filtering (and, with it, `option:add`); use it
|
|
1043
|
+
when the option list is short enough to just scroll.
|
|
1055
1044
|
|
|
1056
|
-
|
|
1057
|
-
background-color: var(--bb-panel);
|
|
1058
|
-
border-color: var(--bb-primary);
|
|
1059
|
-
border-radius: var(--bb-radius);
|
|
1060
|
-
border-width: 0px;
|
|
1061
|
-
display: grid;
|
|
1062
|
-
grid-template-rows: 0fr;
|
|
1063
|
-
opacity: 0;
|
|
1064
|
-
transition-delay: calc(var(--transition-duration) * 0.25), 0s, 0s;
|
|
1065
|
-
transition-duration:
|
|
1066
|
-
var(--transition-duration), calc(var(--transition-duration) * 1.4),
|
|
1067
|
-
var(--transition-duration);
|
|
1068
|
-
transition-property: grid-template-rows, opacity, border;
|
|
1069
|
-
|
|
1070
|
-
> * {
|
|
1071
|
-
opacity: 0;
|
|
1072
|
-
transition-delay: 0s;
|
|
1073
|
-
transition-duration: calc(var(--transition-duration) * 2);
|
|
1074
|
-
transition-property: opacity;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
.bb-listbox__inner-container {
|
|
1078
|
-
overflow: auto;
|
|
1079
|
-
overflow-x: clip;
|
|
1080
|
-
text-align: left;
|
|
1081
|
-
/* Always show options up to the bottom of the screen, but not more than 6 options */
|
|
1082
|
-
max-height: min(
|
|
1083
|
-
calc(var(--option-h) * 6),
|
|
1084
|
-
calc(round(down, calc(100dvh - 130px), var(--option-h)) - 1px)
|
|
1085
|
-
);
|
|
1086
|
-
|
|
1087
|
-
.bb-listbox__loading,
|
|
1088
|
-
.bb-listbox__no-data {
|
|
1089
|
-
color: var(--bb-text);
|
|
1090
|
-
display: none;
|
|
1091
|
-
padding-bottom: var(--bb-select-option-py);
|
|
1092
|
-
padding-left: var(--bb-select-option-px);
|
|
1093
|
-
padding-right: var(--bb-select-option-px);
|
|
1094
|
-
padding-top: var(--bb-select-option-py);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
span[role='listbox'] {
|
|
1098
|
-
position: relative;
|
|
1099
|
-
width: 100%;
|
|
1100
|
-
display: block;
|
|
1101
|
-
|
|
1102
|
-
.bb-listbox__group {
|
|
1103
|
-
left: 0;
|
|
1104
|
-
position: absolute;
|
|
1105
|
-
top: 0;
|
|
1106
|
-
width: 100%;
|
|
1107
|
-
display: block;
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
.bb-listbox__group-header {
|
|
1111
|
-
display: flex;
|
|
1112
|
-
align-items: center;
|
|
1113
|
-
height: var(--group-header-height, 32px);
|
|
1114
|
-
padding-left: var(--bb-select-option-px);
|
|
1115
|
-
padding-right: var(--bb-select-option-px);
|
|
1116
|
-
|
|
1117
|
-
user-select: none;
|
|
1118
|
-
|
|
1119
|
-
.bb-listbox__group-header-label {
|
|
1120
|
-
display: flex;
|
|
1121
|
-
align-items: center;
|
|
1122
|
-
gap: 16px;
|
|
1123
|
-
flex: auto;
|
|
1124
|
-
text-align: left;
|
|
1125
|
-
overflow: hidden;
|
|
1126
|
-
text-overflow: ellipsis;
|
|
1127
|
-
white-space: nowrap;
|
|
1128
|
-
color: color-mix(in srgb, var(--bb-text) 50%, transparent);
|
|
1129
|
-
font-size: 0.75rem;
|
|
1130
|
-
font-weight: 600;
|
|
1131
|
-
letter-spacing: 0.025em;
|
|
1132
|
-
text-transform: uppercase;
|
|
1133
|
-
|
|
1134
|
-
&::after {
|
|
1135
|
-
content: '';
|
|
1136
|
-
display: block;
|
|
1137
|
-
width: 100%;
|
|
1138
|
-
height: 1px;
|
|
1139
|
-
background-color: var(--bb-border);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
.bb-listbox__option {
|
|
1145
|
-
left: 0;
|
|
1146
|
-
position: absolute;
|
|
1147
|
-
display: block;
|
|
1148
|
-
top: 0;
|
|
1149
|
-
width: 100%;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
.bb-listbox__group > .bb-listbox__option {
|
|
1153
|
-
position: static;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
.autocomplete-option {
|
|
1157
|
-
--bg-opacity: 0;
|
|
1158
|
-
--bg-base-color: var(--bb-text);
|
|
1159
|
-
align-items: center;
|
|
1160
|
-
background-color: color-mix(
|
|
1161
|
-
in srgb,
|
|
1162
|
-
var(--bg-base-color) calc(100% * var(--bg-opacity)),
|
|
1163
|
-
transparent
|
|
1164
|
-
);
|
|
1165
|
-
color: var(--bb-text);
|
|
1166
|
-
cursor: pointer;
|
|
1167
|
-
display: flex;
|
|
1168
|
-
flex-grow: 1;
|
|
1169
|
-
gap: 8px;
|
|
1170
|
-
height: var(--option-h);
|
|
1171
|
-
padding-left: var(--bb-select-option-px);
|
|
1172
|
-
padding-right: var(--bb-select-option-px);
|
|
1173
|
-
transition-duration: 250ms;
|
|
1174
|
-
transition-property: color, background-color;
|
|
1175
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
1176
|
-
width: 100%;
|
|
1177
|
-
|
|
1178
|
-
&:hover:not(.autocomplete-option--disabled) {
|
|
1179
|
-
--bg-opacity: 0.05;
|
|
1180
|
-
--bg-base-color: var(--bb-text);
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
&--focused {
|
|
1184
|
-
--bg-opacity: 0.15;
|
|
1185
|
-
--bg-base-color: var(--bb-text);
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
&--focused:hover:not(.autocomplete-option--disabled) {
|
|
1189
|
-
--bg-opacity: 0.2;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
&--selected {
|
|
1193
|
-
--bg-opacity: 0.1;
|
|
1194
|
-
--bg-base-color: var(--bb-primary) !important;
|
|
1195
|
-
color: var(--bb-primary);
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
&--selected:hover:not(.autocomplete-option--disabled) {
|
|
1199
|
-
--bg-opacity: 0.15;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
&--selected.autocomplete-option--focused {
|
|
1203
|
-
--bg-opacity: 0.2;
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
&--selected.autocomplete-option--focused:hover:not(
|
|
1207
|
-
.autocomplete-option--disabled
|
|
1208
|
-
) {
|
|
1209
|
-
--bg-opacity: 0.2;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
&--disabled {
|
|
1213
|
-
cursor: not-allowed;
|
|
1214
|
-
opacity: 0.5;
|
|
1215
|
-
|
|
1216
|
-
.autocomplete-option__checkbox.autocomplete-option__checkbox {
|
|
1217
|
-
background-color: var(--bb-panel-disabled);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
.autocomplete-option__checkbox {
|
|
1222
|
-
pointer-events: none;
|
|
1223
|
-
display: none;
|
|
1224
|
-
width: 16px;
|
|
1225
|
-
height: 16px;
|
|
1226
|
-
background-color: var(--bb-panel);
|
|
1227
|
-
border-color: var(--bb-border);
|
|
1228
|
-
border-radius: min(6px, var(--bb-radius));
|
|
1229
|
-
border-style: solid;
|
|
1230
|
-
border-width: 1px;
|
|
1231
|
-
margin-left: -6px;
|
|
1232
|
-
transition-duration: 300ms;
|
|
1233
|
-
transition-property:
|
|
1234
|
-
color, background-color, border-color, text-decoration-color,
|
|
1235
|
-
box-shadow;
|
|
1236
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
1237
|
-
|
|
1238
|
-
path {
|
|
1239
|
-
stroke: var(--bb-contrasting);
|
|
1240
|
-
stroke-dasharray: 105;
|
|
1241
|
-
stroke-dashoffset: 105;
|
|
1242
|
-
transition: stroke-dashoffset 0.3s;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
.autocomplete-option__label {
|
|
1247
|
-
display: block;
|
|
1248
|
-
flex: auto;
|
|
1249
|
-
text-align: left;
|
|
1250
|
-
overflow: hidden;
|
|
1251
|
-
text-overflow: ellipsis;
|
|
1252
|
-
white-space: nowrap;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
&.autocomplete-option--selected {
|
|
1256
|
-
.autocomplete-option__checkbox {
|
|
1257
|
-
background-color: var(--bb-primary);
|
|
1258
|
-
border-color: var(--bb-primary);
|
|
1259
|
-
|
|
1260
|
-
path {
|
|
1261
|
-
stroke-dashoffset: 0;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
</style>
|
|
1272
|
-
```
|
|
1045
|
+
**Scroll-only status pill**
|
|
1273
1046
|
|
|
1274
|
-
|
|
1047
|
+
```vue
|
|
1048
|
+
<template>
|
|
1049
|
+
<div
|
|
1050
|
+
class="flex max-w-sm items-center gap-1.5 rounded-[var(--bb-radius)] border p-2"
|
|
1051
|
+
>
|
|
1052
|
+
<span class="pr-1 text-xs font-medium text-gray-500">Status</span>
|
|
1053
|
+
<BbSelectPopover
|
|
1054
|
+
v-model="status"
|
|
1055
|
+
compact
|
|
1056
|
+
disable-writing
|
|
1057
|
+
:items="statuses"
|
|
1058
|
+
width="200px"
|
|
1059
|
+
>
|
|
1060
|
+
<template #activator="{ props, shown }">
|
|
1061
|
+
<BbBadge
|
|
1062
|
+
append:icon="lucide:chevron-down"
|
|
1063
|
+
:class="{ 'status-trigger--editing': shown }"
|
|
1064
|
+
size="lg"
|
|
1065
|
+
:variant="variantFor(status)"
|
|
1066
|
+
>
|
|
1067
|
+
<BbBadgeButton v-bind="props">{{
|
|
1068
|
+
status ?? 'Set status'
|
|
1069
|
+
}}</BbBadgeButton>
|
|
1070
|
+
</BbBadge>
|
|
1071
|
+
</template>
|
|
1072
|
+
</BbSelectPopover>
|
|
1073
|
+
</div>
|
|
1074
|
+
</template>
|
|
1075
|
+
<script setup lang="ts">
|
|
1076
|
+
import { BbBadge, BbBadgeButton, BbSelectPopover } from 'bitboss-ui';
|
|
1077
|
+
import type { BadgeVariantType } from 'bitboss-ui';
|
|
1078
|
+
import { ref } from 'vue';
|
|
1275
1079
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
import type { CommonProps } from '@/types/CommonProps';
|
|
1279
|
-
|
|
1280
|
-
export type BbSelectPopoverProps<Item> = {
|
|
1281
|
-
/**
|
|
1282
|
-
* External activator element or component ref.
|
|
1283
|
-
* When provided, the activator slot is not rendered and event listeners
|
|
1284
|
-
* are attached programmatically to the referenced element.
|
|
1285
|
-
*/
|
|
1286
|
-
activator?: HTMLElement | Record<string, any> | null;
|
|
1287
|
-
|
|
1288
|
-
/**
|
|
1289
|
-
* Controls whether the search input is shown inside the popover panel.
|
|
1290
|
-
* Use `'not-mobile'` to hide the search input on mobile.
|
|
1291
|
-
*
|
|
1292
|
-
* @defaultValue `false`
|
|
1293
|
-
*/
|
|
1294
|
-
allowWriting?: boolean | 'not-mobile';
|
|
1295
|
-
|
|
1296
|
-
/**
|
|
1297
|
-
* Browser autocomplete hint for the popover search input.
|
|
1298
|
-
*
|
|
1299
|
-
* @defaultValue `'off'`
|
|
1300
|
-
*/
|
|
1301
|
-
autocomplete?: InputHTMLAttributes['autocomplete'];
|
|
1302
|
-
|
|
1303
|
-
/**
|
|
1304
|
-
* Apply the compact density variant.
|
|
1305
|
-
*/
|
|
1306
|
-
compact?: boolean;
|
|
1307
|
-
|
|
1308
|
-
/**
|
|
1309
|
-
* Space the arrow keeps from popover edges. Useful with rounded corners.
|
|
1310
|
-
*
|
|
1311
|
-
* @defaultValue `10`
|
|
1312
|
-
*/
|
|
1313
|
-
arrowPadding?: CommonProps['arrowPadding'];
|
|
1314
|
-
|
|
1315
|
-
/**
|
|
1316
|
-
* Constrains the popover to a specific boundary element.
|
|
1317
|
-
*/
|
|
1318
|
-
boundary?: HTMLElement | Record<string, any> | string | null;
|
|
1319
|
-
|
|
1320
|
-
/**
|
|
1321
|
-
* Additional dependencies that trigger item reloading when changed.
|
|
1322
|
-
*
|
|
1323
|
-
* @defaultValue `[]`
|
|
1324
|
-
*/
|
|
1325
|
-
dependencies?: any[];
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* Debounce delay (ms) for dependency-triggered reloads.
|
|
1329
|
-
*
|
|
1330
|
-
* @defaultValue `0`
|
|
1331
|
-
*/
|
|
1332
|
-
depsDebounceTime?: number;
|
|
1333
|
-
|
|
1334
|
-
/**
|
|
1335
|
-
* Disables the component.
|
|
1336
|
-
*/
|
|
1337
|
-
disabled?: boolean;
|
|
1338
|
-
|
|
1339
|
-
/**
|
|
1340
|
-
* Marks the component as being in error state.
|
|
1341
|
-
*/
|
|
1342
|
-
hasErrors?: boolean;
|
|
1343
|
-
|
|
1344
|
-
/**
|
|
1345
|
-
* Explicit invalid state for accessibility semantics.
|
|
1346
|
-
* Falls back to `hasErrors` when not provided.
|
|
1347
|
-
*/
|
|
1348
|
-
ariaInvalid?: boolean;
|
|
1349
|
-
|
|
1350
|
-
/**
|
|
1351
|
-
* Space-separated IDREF(s) that describe this field for assistive technologies.
|
|
1352
|
-
* Accepts either a string or array of ids.
|
|
1353
|
-
*/
|
|
1354
|
-
ariaDescribedby?: string | string[];
|
|
1355
|
-
|
|
1356
|
-
/**
|
|
1357
|
-
* Enable or disable floating flip behavior.
|
|
1358
|
-
*
|
|
1359
|
-
* @defaultValue `true`
|
|
1360
|
-
*/
|
|
1361
|
-
flip?: boolean;
|
|
1362
|
-
|
|
1363
|
-
/**
|
|
1364
|
-
* Whether the floating arrow is hidden.
|
|
1365
|
-
*
|
|
1366
|
-
* @defaultValue `true`
|
|
1367
|
-
*/
|
|
1368
|
-
hideArrow?: boolean;
|
|
1369
|
-
|
|
1370
|
-
/**
|
|
1371
|
-
* Reset modelValue to `null` or empty array if it no longer matches available items.
|
|
1372
|
-
*/
|
|
1373
|
-
enforceCoherence?: boolean;
|
|
1374
|
-
|
|
1375
|
-
/**
|
|
1376
|
-
* Controls how options are filtered during search.
|
|
1377
|
-
*
|
|
1378
|
-
* @defaultValue `() => []`
|
|
1379
|
-
*/
|
|
1380
|
-
filterBy?:
|
|
1381
|
-
| string
|
|
1382
|
-
| string[]
|
|
1383
|
-
| false
|
|
1384
|
-
| 'not_stashed'
|
|
1385
|
-
| ((value: any, item: any, query: string | null) => boolean)
|
|
1386
|
-
| null;
|
|
1387
|
-
|
|
1388
|
-
/** Path to item property for grouping options. */
|
|
1389
|
-
groupBy?: string | ((item: Item) => string | number | symbol);
|
|
1390
|
-
|
|
1391
|
-
/**
|
|
1392
|
-
* Height of group headers in the listbox (px). Defaults to 24px (compact) or 32px.
|
|
1393
|
-
* Only applies when `groupBy` is set.
|
|
1394
|
-
*/
|
|
1395
|
-
headerHeight?: number;
|
|
1396
|
-
|
|
1397
|
-
/**
|
|
1398
|
-
* Explicit id for the component. Used to generate ids for listbox and options.
|
|
1399
|
-
*/
|
|
1400
|
-
id?: HTMLAttributes['id'];
|
|
1401
|
-
|
|
1402
|
-
/**
|
|
1403
|
-
* Array of items or function to load them asynchronously.
|
|
1404
|
-
* Functions receive `(query, prefill, modelValue)` and can return a promise.
|
|
1405
|
-
*
|
|
1406
|
-
* @defaultValue `[]`
|
|
1407
|
-
*/
|
|
1408
|
-
items:
|
|
1409
|
-
| Item[]
|
|
1410
|
-
| ((query: string, prefill: boolean, modelValue: any[]) => Promise<Item[]>)
|
|
1411
|
-
| ((query: string, prefill: boolean, modelValue: any[]) => Item[]);
|
|
1412
|
-
|
|
1413
|
-
/**
|
|
1414
|
-
* Height of the options in the listbox (px).
|
|
1415
|
-
*
|
|
1416
|
-
* @defaultValue `40`
|
|
1417
|
-
*/
|
|
1418
|
-
itemHeight?: number;
|
|
1419
|
-
|
|
1420
|
-
/**
|
|
1421
|
-
* Path to item property for display text or function to extract it.
|
|
1422
|
-
*/
|
|
1423
|
-
itemText?: string | ((item: Item) => string) | undefined;
|
|
1424
|
-
|
|
1425
|
-
/**
|
|
1426
|
-
* Path to item property for value or function to extract it.
|
|
1427
|
-
*/
|
|
1428
|
-
itemValue?: string | ((item: Item) => string) | undefined;
|
|
1429
|
-
|
|
1430
|
-
/**
|
|
1431
|
-
* Display the loading state styles.
|
|
1432
|
-
*/
|
|
1433
|
-
loading?: boolean;
|
|
1434
|
-
|
|
1435
|
-
/**
|
|
1436
|
-
* Message shown while items are being loaded.
|
|
1437
|
-
*/
|
|
1438
|
-
loadingText?: string;
|
|
1439
|
-
|
|
1440
|
-
/**
|
|
1441
|
-
* Maximum number of selectable items (limits selection when `multiple` is true).
|
|
1442
|
-
*
|
|
1443
|
-
* @defaultValue `Infinity`
|
|
1444
|
-
*/
|
|
1445
|
-
max?: number;
|
|
1446
|
-
|
|
1447
|
-
/**
|
|
1448
|
-
* v-model value. Single value for single select, array for multiple select.
|
|
1449
|
-
*/
|
|
1450
|
-
modelValue: any;
|
|
1451
|
-
|
|
1452
|
-
/**
|
|
1453
|
-
* Debounce delay (ms) for modelValue change handling.
|
|
1454
|
-
*
|
|
1455
|
-
* @defaultValue `0`
|
|
1456
|
-
*/
|
|
1457
|
-
modelValueDebounceTime?: number;
|
|
1458
|
-
|
|
1459
|
-
/**
|
|
1460
|
-
* Enable multiple item selection. When true, modelValue must be an array.
|
|
1461
|
-
*/
|
|
1462
|
-
multiple?: boolean;
|
|
1463
|
-
|
|
1464
|
-
/**
|
|
1465
|
-
* Message displayed when no items are available.
|
|
1466
|
-
*/
|
|
1467
|
-
noDataText?: string;
|
|
1468
|
-
|
|
1469
|
-
/**
|
|
1470
|
-
* Distance (px) between the activator and the popover.
|
|
1471
|
-
*
|
|
1472
|
-
* @defaultValue `4`
|
|
1473
|
-
*/
|
|
1474
|
-
offset?: number;
|
|
1475
|
-
|
|
1476
|
-
/**
|
|
1477
|
-
* Minimum page padding used by floating shift middleware.
|
|
1478
|
-
*
|
|
1479
|
-
* @defaultValue `0`
|
|
1480
|
-
*/
|
|
1481
|
-
padding?: CommonProps['padding'];
|
|
1482
|
-
|
|
1483
|
-
/**
|
|
1484
|
-
* Floating placement of the popover.
|
|
1485
|
-
*
|
|
1486
|
-
* @defaultValue `'bottom'`
|
|
1487
|
-
*/
|
|
1488
|
-
placement?: CommonProps['placement'];
|
|
1489
|
-
|
|
1490
|
-
/**
|
|
1491
|
-
* Controls when items are pre-loaded.
|
|
1492
|
-
* `'interaction'` loads on first user interaction, `true` loads immediately,
|
|
1493
|
-
* `false` loads only on explicit search/open flows.
|
|
1494
|
-
*
|
|
1495
|
-
* @defaultValue `'interaction'`
|
|
1496
|
-
*/
|
|
1497
|
-
prefill?: boolean | 'interaction';
|
|
1498
|
-
|
|
1499
|
-
/**
|
|
1500
|
-
* Debounce delay (ms) before triggering search queries after user input stops.
|
|
1501
|
-
*
|
|
1502
|
-
* @defaultValue `500`
|
|
1503
|
-
*/
|
|
1504
|
-
queryDebounceTime?: number;
|
|
1505
|
-
|
|
1506
|
-
/**
|
|
1507
|
-
* Prevents opening the popover while keeping the activator accessible.
|
|
1508
|
-
*/
|
|
1509
|
-
readonly?: boolean;
|
|
1510
|
-
|
|
1511
|
-
/**
|
|
1512
|
-
* Accessible label used by the search input rendered inside the popover.
|
|
1513
|
-
*
|
|
1514
|
-
* @defaultValue `'Search options'`
|
|
1515
|
-
*/
|
|
1516
|
-
searchInputAriaLabel?: string;
|
|
1517
|
-
|
|
1518
|
-
/**
|
|
1519
|
-
* Accumulate selected items across searches instead of replacing them.
|
|
1520
|
-
*/
|
|
1521
|
-
stash?: boolean;
|
|
1522
|
-
|
|
1523
|
-
/**
|
|
1524
|
-
* Transition duration (ms) for popover show/hide animations.
|
|
1525
|
-
*
|
|
1526
|
-
* @defaultValue `300`
|
|
1527
|
-
*/
|
|
1528
|
-
transitionDuration?: number;
|
|
1529
|
-
|
|
1530
|
-
/**
|
|
1531
|
-
* Width of the popover in pixels, percentage, or `'auto'`.
|
|
1532
|
-
*/
|
|
1533
|
-
width?: number | string;
|
|
1534
|
-
};
|
|
1080
|
+
const statuses = ['Draft', 'In review', 'Approved', 'Published'];
|
|
1081
|
+
type Status = (typeof statuses)[number];
|
|
1535
1082
|
|
|
1536
|
-
|
|
1537
|
-
/**
|
|
1538
|
-
* Emitted when the activator loses focus.
|
|
1539
|
-
*/
|
|
1540
|
-
(e: 'blur', event: FocusEvent): void;
|
|
1541
|
-
/**
|
|
1542
|
-
* Emitted when the value changes via native change event.
|
|
1543
|
-
*/
|
|
1544
|
-
(e: 'change', event: Event): void;
|
|
1545
|
-
/**
|
|
1546
|
-
* Emitted when the activator is clicked.
|
|
1547
|
-
*/
|
|
1548
|
-
(e: 'click', event: MouseEvent): void;
|
|
1549
|
-
/**
|
|
1550
|
-
* Emitted when the activator receives focus.
|
|
1551
|
-
*/
|
|
1552
|
-
(e: 'focus', event: FocusEvent): void;
|
|
1553
|
-
/**
|
|
1554
|
-
* Emitted when focus moves outside the entire component after it was active.
|
|
1555
|
-
*/
|
|
1556
|
-
(e: 'inactive'): void;
|
|
1557
|
-
/**
|
|
1558
|
-
* Emitted on native input events from the search input.
|
|
1559
|
-
*/
|
|
1560
|
-
(e: 'input', event: Event): void;
|
|
1561
|
-
/**
|
|
1562
|
-
* Emitted with the next selection value whenever selection changes.
|
|
1563
|
-
*/
|
|
1564
|
-
(e: 'update:modelValue', value: any): void;
|
|
1565
|
-
/**
|
|
1566
|
-
* Emitted when the user types a new value and confirms it (Enter key) when `allowWriting` is enabled.
|
|
1567
|
-
*/
|
|
1568
|
-
(e: 'option:add', text: string): void;
|
|
1569
|
-
};
|
|
1083
|
+
const status = ref<Status | null>(null);
|
|
1570
1084
|
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
};
|
|
1577
|
-
/** Whether the popover is currently open. */
|
|
1578
|
-
shown: boolean;
|
|
1579
|
-
/** Whether the select is disabled. */
|
|
1580
|
-
disabled: boolean | undefined;
|
|
1581
|
-
/** Whether the select is read-only. */
|
|
1582
|
-
readonly: boolean | undefined;
|
|
1583
|
-
/** Whether options are currently being fetched. */
|
|
1584
|
-
loading: boolean;
|
|
1585
|
-
/** The current search query string. */
|
|
1586
|
-
query: string;
|
|
1587
|
-
/** Clears the current selection and query. */
|
|
1588
|
-
clear: () => void;
|
|
1589
|
-
/** Opens the popover. */
|
|
1590
|
-
open: () => Promise<void>;
|
|
1591
|
-
/** Closes the popover. */
|
|
1592
|
-
close: () => Promise<void>;
|
|
1593
|
-
/** Toggles the popover open/closed. */
|
|
1594
|
-
toggle: () => Promise<void>;
|
|
1085
|
+
const VARIANT: Record<Status, BadgeVariantType> = {
|
|
1086
|
+
Draft: 'soft-gray',
|
|
1087
|
+
'In review': 'soft-amber',
|
|
1088
|
+
Approved: 'soft-blue',
|
|
1089
|
+
Published: 'soft-green',
|
|
1595
1090
|
};
|
|
1596
1091
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
disabled: boolean;
|
|
1601
|
-
/** Whether this option currently has keyboard focus. */
|
|
1602
|
-
focused: boolean;
|
|
1603
|
-
/** Zero-based index of this option in the list. */
|
|
1604
|
-
index: number;
|
|
1605
|
-
/** The raw item from the `items` prop. */
|
|
1606
|
-
item: Item;
|
|
1607
|
-
/** Whether items are still being fetched. */
|
|
1608
|
-
loading: boolean;
|
|
1609
|
-
/** Whether this option is currently selected. */
|
|
1610
|
-
selected: boolean;
|
|
1611
|
-
/** The resolved display text for this option. */
|
|
1612
|
-
text: string;
|
|
1613
|
-
/** The resolved value for this option. */
|
|
1614
|
-
value: any;
|
|
1615
|
-
};
|
|
1092
|
+
const variantFor = (value: Status | null): BadgeVariantType =>
|
|
1093
|
+
value ? VARIANT[value] : 'outline';
|
|
1094
|
+
</script>
|
|
1616
1095
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1096
|
+
<!-- Soft status palette — canonical source: ai/guides/design-language.md § Using color. -->
|
|
1097
|
+
<style>
|
|
1098
|
+
.bb-badge.bb-badge--soft-gray {
|
|
1099
|
+
--accent: #64748b;
|
|
1100
|
+
}
|
|
1101
|
+
.bb-badge.bb-badge--soft-blue {
|
|
1102
|
+
--accent: #2563eb;
|
|
1103
|
+
}
|
|
1104
|
+
.bb-badge.bb-badge--soft-green {
|
|
1105
|
+
--accent: #16a34a;
|
|
1106
|
+
}
|
|
1107
|
+
.bb-badge.bb-badge--soft-amber {
|
|
1108
|
+
--accent: #d97706;
|
|
1109
|
+
}
|
|
1110
|
+
.bb-badge.bb-badge--soft-gray,
|
|
1111
|
+
.bb-badge.bb-badge--soft-blue,
|
|
1112
|
+
.bb-badge.bb-badge--soft-green,
|
|
1113
|
+
.bb-badge.bb-badge--soft-amber {
|
|
1114
|
+
--bg: color-mix(in oklab, var(--accent) 15%, var(--bb-panel));
|
|
1115
|
+
--color: color-mix(in oklab, var(--accent) 80%, var(--bb-text));
|
|
1116
|
+
--border-width: 1px;
|
|
1117
|
+
--border-color: color-mix(in oklab, var(--accent) 25%, var(--bb-panel));
|
|
1118
|
+
--ring: color-mix(in oklab, var(--accent) 45%, transparent);
|
|
1119
|
+
}
|
|
1630
1120
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
* Content shown inside the options dropdown while items are loading.
|
|
1638
|
-
*/
|
|
1639
|
-
loading?: (props: { query: string }) => any;
|
|
1640
|
-
/**
|
|
1641
|
-
* Content shown when no options match the query or the list is empty.
|
|
1642
|
-
*/
|
|
1643
|
-
'no-data'?: (props: { query: string; focus: () => void }) => any;
|
|
1644
|
-
/**
|
|
1645
|
-
* Replaces the default rendering of each option row in the dropdown.
|
|
1646
|
-
*/
|
|
1647
|
-
option?: (props: BbSelectPopoverOptionSlotProps<Item>) => any;
|
|
1648
|
-
/**
|
|
1649
|
-
* Replaces the default group header rendered above grouped options.
|
|
1650
|
-
*/
|
|
1651
|
-
group?: (props: BbSelectPopoverGroupSlotProps<Item>) => any;
|
|
1652
|
-
/**
|
|
1653
|
-
* Content appended inside the options list, after the last option.
|
|
1654
|
-
*/
|
|
1655
|
-
'options:append'?: (props: { focus: () => void }) => any;
|
|
1656
|
-
/**
|
|
1657
|
-
* Content appended outside the options list container.
|
|
1658
|
-
*/
|
|
1659
|
-
'options:append:outer'?: (props: { focus: () => void }) => any;
|
|
1660
|
-
/**
|
|
1661
|
-
* Content prepended inside the options list, before the first option.
|
|
1662
|
-
*/
|
|
1663
|
-
'options:prepend'?: (props: { focus: () => void }) => any;
|
|
1664
|
-
/**
|
|
1665
|
-
* Content prepended outside the options list container.
|
|
1666
|
-
*/
|
|
1667
|
-
'options:prepend:outer'?: (props: { focus: () => void }) => any;
|
|
1668
|
-
};
|
|
1121
|
+
.status-trigger--editing {
|
|
1122
|
+
--bg: color-mix(in oklab, var(--color) 25%, var(--bb-panel));
|
|
1123
|
+
--border-color: color-mix(in oklab, var(--color) 45%, var(--bb-panel));
|
|
1124
|
+
box-shadow: 0 0 0 var(--bb-ring-size) var(--ring);
|
|
1125
|
+
}
|
|
1126
|
+
</style>
|
|
1669
1127
|
```
|
|
1128
|
+
|
|
1129
|
+
### Sizing and layout
|
|
1130
|
+
|
|
1131
|
+
- `width` sets the popover width — a number (px), a CSS string (`'240px'`,
|
|
1132
|
+
`'100%'`), or `'auto'`. Unlike `BbSelect` (whose panel always matches the
|
|
1133
|
+
control), here the trigger and panel are independent, so size the panel
|
|
1134
|
+
explicitly.
|
|
1135
|
+
- `compact` applies the dense density variant (32px options, 24px group headers).
|
|
1136
|
+
- `item-height` / `header-height` tune the virtualized row heights when you use
|
|
1137
|
+
taller custom `#option` / `#group` markup.
|
|
1138
|
+
- Placement props mirror the palette/dropdown float: `placement`, `offset`
|
|
1139
|
+
(default 4), `padding`, `boundary`, `disable-flip`, `transition-duration`. The
|
|
1140
|
+
panel is a frame, not a bubble — it never shows an arrow.
|
|
1141
|
+
|
|
1142
|
+
### Adaptive mobile sheet
|
|
1143
|
+
|
|
1144
|
+
With `adaptive` (default: global `config.adaptive`, which is `true`), the panel
|
|
1145
|
+
opens as a bottom off-canvas sheet on mobile viewports (below
|
|
1146
|
+
`config.mobileMaxWidth`) instead of a floating popover — same search, options and
|
|
1147
|
+
`#header`/`#footer` slots, plus a representation of the current selection between
|
|
1148
|
+
them. The surface is **latched per open cycle**: a mid-open resize never swaps
|
|
1149
|
+
popover ↔ sheet. `off-canvas-props` forwards extra `BbOffCanvas` props to the
|
|
1150
|
+
sheet (`size`, `draggable`, `fullscreen`, `stack`); `:adaptive="false"` keeps a
|
|
1151
|
+
floating popover everywhere.
|
|
1152
|
+
|
|
1153
|
+
### Events and v-model
|
|
1154
|
+
|
|
1155
|
+
- `update:modelValue` — the resolved value (single) or array (multiple), on pick,
|
|
1156
|
+
`clear`, coherence enforcement, and — in `multiple` mode only — deselect.
|
|
1157
|
+
A second click on the current option in single mode emits nothing.
|
|
1158
|
+
- `option:add` — Enter on a non-matching query with nothing highlighted (above).
|
|
1159
|
+
- `focus` / `blur` — the activator gaining/losing focus.
|
|
1160
|
+
- `input` / `change` — native events from the panel search field.
|
|
1161
|
+
- `click` — the activator was clicked.
|
|
1162
|
+
- `inactive` — focus has fully left the component (**trigger and panel**). Because
|
|
1163
|
+
focus legitimately moves into the panel while open, validate/react on
|
|
1164
|
+
`inactive`, not on `blur`.
|
|
1165
|
+
|
|
1166
|
+
### Config and platform
|
|
1167
|
+
|
|
1168
|
+
- **No teleport** — the popover renders **in the component's own subtree** (it is
|
|
1169
|
+
not portalled to `<body>`), so it inherits your CSS context and is directly
|
|
1170
|
+
queryable (handy for tests: the open panel is `.bb-listbox--open` on the same
|
|
1171
|
+
root, no portal hop).
|
|
1172
|
+
- **Global config** — `config.adaptive` (default for `adaptive`),
|
|
1173
|
+
`config.mobileMaxWidth` (the mobile threshold), `config.locale` (the search
|
|
1174
|
+
field's default accessible name and panel string defaults).
|
|
1175
|
+
- **SSR / Nuxt** — `prefill: true` providers also run in `onServerPrefetch`, so
|
|
1176
|
+
server-rendered pages hydrate with options loaded; keep providers isomorphic
|
|
1177
|
+
(no `window` access).
|
|
1178
|
+
|
|
1179
|
+
### Works well with
|
|
1180
|
+
|
|
1181
|
+
- `BbSelect` — the same engine as a labelled form field; swap when the trigger
|
|
1182
|
+
should be an inline input with hint/error states.
|
|
1183
|
+
- `BbTable` — popover triggers as inline-edit cells or toolbar filters; list
|
|
1184
|
+
their models in the table provider's `dependencies`.
|
|
1185
|
+
- `BbButton` — the natural activator (it forwards `props` and exposes a
|
|
1186
|
+
`loading` state to reflect fetches).
|
|
1187
|
+
- `useToast` — surface provider failures the component only logs.
|
|
1188
|
+
|
|
1189
|
+
### Gotchas and constraints
|
|
1190
|
+
|
|
1191
|
+
- `multiple` with a non-array model **throws at setup** — seed `ref([])`.
|
|
1192
|
+
- A seeded model shows **no trigger `text`** until the first load (no eager
|
|
1193
|
+
prefill upgrade, by design) — resolve the label from your own state. Array
|
|
1194
|
+
`items` resolve on mount, so this only bites providers.
|
|
1195
|
+
- The local substring filter also applies to provider results — server matches
|
|
1196
|
+
whose text doesn't contain the query are hidden.
|
|
1197
|
+
- `filter-by` is `string[]` only and matches exactly those paths; each must exist
|
|
1198
|
+
on every item or matching throws.
|
|
1199
|
+
- `prefill: false` is search-first: opening fetches nothing and the panel shows
|
|
1200
|
+
the "search to begin" hint until the user types. It quietly falls back to
|
|
1201
|
+
loading on open when writing is disabled, the model is non-empty, or `items` is
|
|
1202
|
+
an array.
|
|
1203
|
+
- `enforce-coherence` + a provider that drops the selection on component-initiated
|
|
1204
|
+
(`prefill === true`) calls = wiped model — and since the check now runs on the
|
|
1205
|
+
**first** load, that wipe happens at mount rather than later. Resolve the
|
|
1206
|
+
current selection in those responses, or enable `stash`.
|
|
1207
|
+
- `enforce-coherence` opts **each instance** into its own load. One field, one
|
|
1208
|
+
request; N provider-backed rows, N requests.
|
|
1209
|
+
- `option:add` can fire with an empty string — trim and guard.
|
|
1210
|
+
- **Clicking the selected option does not clear a single select** — it re-picks
|
|
1211
|
+
it and closes the panel. Emptying is the `clear` callback (activator/panel
|
|
1212
|
+
slot scope), never an accidental second click. Multiple mode still toggles.
|
|
1213
|
+
- **`null` as a real "All" option** works — it resolves and renders as the
|
|
1214
|
+
selection — but it overloads the value the component uses for _nothing
|
|
1215
|
+
selected_. So a `null` model stays lazy (nothing to resolve, no load, not even
|
|
1216
|
+
with `enforce-coherence`), `clear` is indistinguishable from picking "All", and
|
|
1217
|
+
an `enforce-coherence` prune emits `null` — i.e. a stale value **widens to
|
|
1218
|
+
"All"** rather than clearing. Fine if you want it; use a sentinel
|
|
1219
|
+
(`'all'`) if "everything" and "nothing" must stay distinct.
|
|
1220
|
+
|
|
1221
|
+
### Anti-patterns
|
|
1222
|
+
|
|
1223
|
+
- Reading `text` (or `selectedOptions`) for the trigger label instead of
|
|
1224
|
+
computing it from your own state — with a provider they are empty until the
|
|
1225
|
+
first load, so the trigger's appearance ends up depending on whether the panel
|
|
1226
|
+
was ever opened.
|
|
1227
|
+
- Binding many instances to **one shared ref**: each judges the same value against
|
|
1228
|
+
its own options and every instance that disagrees emits `null`, so the user's
|
|
1229
|
+
pick is overwritten by its neighbours. One model per row.
|
|
1230
|
+
- Giving every table row its own **provider**: N rows becomes N requests. Resolve
|
|
1231
|
+
the options once at page level and pass the same array down.
|
|
1232
|
+
- Reaching for `prefill: true` to populate a trigger label.
|
|
1233
|
+
- Rebuilding chips, counts or select-all in wrapper markup when `max`,
|
|
1234
|
+
`selectedOptions` and the panel slots cover it.
|
|
1235
|
+
- Hand-rolled debounce/loading refs around the provider.
|
|
1236
|
+
- `watch(filter, refetch)` when `dependencies` exists; blind `model = null`
|
|
1237
|
+
resets when `enforce-coherence` would preserve still-valid values.
|
|
1238
|
+
- Using it where an inline labelled field is wanted (that's `BbSelect`), or for a
|
|
1239
|
+
static three-option choice (that's `BbRadioGroup`).
|
|
1240
|
+
|
|
1241
|
+
### Accessibility
|
|
1242
|
+
|
|
1243
|
+
- The activator carries the accessible name — make it a real focusable element (a
|
|
1244
|
+
`<button>` or `BbButton`), and give icon-only triggers an `aria-label`.
|
|
1245
|
+
- The in-panel search field is labelled by `search-input-aria-label` (defaults to
|
|
1246
|
+
"Search options"); override it to match your domain.
|
|
1247
|
+
- The component manages listbox roles, `aria-activedescendant`, the full keyboard
|
|
1248
|
+
model and focus return to the trigger on close — do not reimplement them.
|
|
1249
|
+
- Because the trigger is yours, reflect the open state visually (the `shown` slot
|
|
1250
|
+
prop) so keyboard and low-vision users can see the popover is expanded.
|
|
1251
|
+
|
|
1252
|
+
## v-model
|
|
1253
|
+
|
|
1254
|
+
- `v-model` — type: `any`
|
|
1255
|
+
|
|
1256
|
+
## Props
|
|
1257
|
+
|
|
1258
|
+
| Prop | Type | Default | Required | Description |
|
|
1259
|
+
| --- | --- | --- | --- | --- |
|
|
1260
|
+
| `activator` | `Record<string, any> \| HTMLElement \| null \| undefined` | | | External activator element or component ref. When provided, the activator slot is not rendered and event listeners are attached programmatically to the referenced element. |
|
|
1261
|
+
| `adaptive` | `boolean \| undefined` | `undefined` | | When `true`, the panel opens as a bottom off-canvas sheet on mobile viewports instead of a floating popover; on desktop it stays a popover. The sheet reuses the same search field and options list, adding a representation of the current sele… |
|
|
1262
|
+
| `anchor` | `Record<string, any> \| HTMLElement \| null \| undefined` | `null` | | Override the element used for popover positioning and width measurement. Useful when the focusable activator is narrower than its visual container (e.g. a button inside a wrapper). Falls back to `activatorEl` when omitted. |
|
|
1263
|
+
| `ariaDescribedby` | `string \| string[] \| undefined` | | | Space-separated IDREF(s) that describe this field for assistive technologies. Accepts either a string or array of ids. |
|
|
1264
|
+
| `ariaInvalid` | `boolean \| undefined` | `undefined` | | Explicit invalid state for accessibility semantics. Falls back to `hasErrors` when not provided. |
|
|
1265
|
+
| `autocomplete` | `string \| undefined` | `"off"` | | Browser autocomplete hint for the popover search input. |
|
|
1266
|
+
| `boundary` | `string \| Record<string, any> \| HTMLElement \| null \| undefined` | | | Constrains the popover to a specific boundary element. |
|
|
1267
|
+
| `clearable` | `boolean \| undefined` | | | Shows a clear button inside the search field's icon stack: it takes the lens' place on hover/focus while there is a query or selection to clear, and resets both. Off by default. Leave it off when the component is wrapped by a control that a… |
|
|
1268
|
+
| `compact` | `boolean \| undefined` | | | Apply the compact density variant. |
|
|
1269
|
+
| `dependencies` | `unknown[] \| undefined` | `[]` | | Additional dependencies that trigger item reloading when changed. |
|
|
1270
|
+
| `depsDebounceTime` | `number \| undefined` | `0` | | Debounce delay (ms) for dependency-triggered reloads. |
|
|
1271
|
+
| `disabled` | `boolean \| undefined` | | | Disables the component. |
|
|
1272
|
+
| `disableFlip` | `boolean \| undefined` | | | Disables the automatic flip to the opposite side when the popover would overflow (flipping is on by default). |
|
|
1273
|
+
| `disableWriting` | `boolean \| "mobile" \| "desktop" \| undefined` | | | Controls whether the search input is shown inside the popover panel. Use `'mobile'` to hide the search input on mobile, `'desktop'` to hide it on desktop. |
|
|
1274
|
+
| `enforceCoherence` | `boolean \| undefined` | | | Reset modelValue to `null` or empty array if it no longer matches available items. |
|
|
1275
|
+
| `filterBy` | `string[] \| undefined` | `[]` | | Property paths used to filter options when the user types in the search input. When empty (default) options are matched against their display text. When non-empty the provided paths are searched instead of the display text. To disable filte… |
|
|
1276
|
+
| `groupBy` | `string \| ((item: Item) => string \| number \| symbol) \| undefined` | | | Path to item property for grouping options. |
|
|
1277
|
+
| `hasErrors` | `boolean \| undefined` | | | Marks the component as being in error state. |
|
|
1278
|
+
| `hasWarning` | `boolean \| undefined` | | | Marks the component as being in a warning state. Visually mirrors `hasErrors` but uses `--bb-warn` chrome; errors take priority when both are set. |
|
|
1279
|
+
| `headerHeight` | `number \| undefined` | | | Height of group headers in the listbox (px). Defaults to 24px (compact) or 32px. Only applies when `groupBy` is set. |
|
|
1280
|
+
| `id` | `string \| undefined` | | | Explicit id for the component. Used to generate ids for listbox and options. |
|
|
1281
|
+
| `itemHeight` | `number \| undefined` | | | Height of the options in the listbox (px). |
|
|
1282
|
+
| `items` | `Item[] \| ((query: string, prefill: boolean, modelValue: any) => Promise<Item[]>) \| ((query: string, prefill: boolean, modelValue: any) => Item[])` | | yes | Array of items or function to load them asynchronously. Functions receive `(query, prefill, modelValue)` and can return a promise. `modelValue` is the raw v-model: an array in `multiple` mode, the raw model value (possibly `null`) in single… |
|
|
1283
|
+
| `itemText` | `ItemAccessor<Item, string> \| undefined` | | | Path to item property for display text or function to extract it. |
|
|
1284
|
+
| `itemValue` | `ItemAccessor<Item> \| undefined` | | | Path to item property for value or function to extract it. |
|
|
1285
|
+
| `loading` | `boolean \| undefined` | | | Display the loading state styles. |
|
|
1286
|
+
| `loadingText` | `string \| undefined` | | | Message shown while items are being loaded. |
|
|
1287
|
+
| `max` | `number \| undefined` | `Infinity` | | Maximum number of selectable items (limits selection when `multiple` is true). |
|
|
1288
|
+
| `modelValue` | `any` | | yes | v-model value. Single value for single select, array for multiple select. |
|
|
1289
|
+
| `modelValueDebounceTime` | `number \| undefined` | `0` | | Debounce delay (ms) for modelValue change handling. |
|
|
1290
|
+
| `multiple` | `boolean \| undefined` | | | Enable multiple item selection. When true, modelValue must be an array. |
|
|
1291
|
+
| `noDataText` | `string \| undefined` | | | Message displayed when no items are available. |
|
|
1292
|
+
| `offCanvasProps` | `Partial<BbOffCanvasProps> \| undefined` | | | Extra props forwarded to the `BbOffCanvas` sheet when `adaptive` is active on mobile (e.g. `size`, `draggable`, `fullscreen`, `stack`). |
|
|
1293
|
+
| `offset` | `number \| undefined` | `4` | | Distance (px) between the activator and the popover. |
|
|
1294
|
+
| `padding` | `number \| undefined` | `10` | | Minimum page padding used by floating shift middleware. |
|
|
1295
|
+
| `placement` | `Placement \| undefined` | `"bottom"` | | Floating placement of the popover. |
|
|
1296
|
+
| `prefill` | `boolean \| "interaction" \| undefined` | `"interaction"` | | Controls when items are pre-loaded. `'interaction'` loads on the first user interaction (opening the popover), `true` loads immediately on mount (and during SSR via `onServerPrefetch`), `false` is *search-first*: nothing is fetched until th… |
|
|
1297
|
+
| `queryDebounceTime` | `number \| undefined` | `500` | | Debounce delay (ms) before triggering search queries after user input stops. |
|
|
1298
|
+
| `readonly` | `boolean \| undefined` | | | Prevents opening the popover while keeping the activator accessible. |
|
|
1299
|
+
| `searchInputAriaLabel` | `string \| undefined` | | | Accessible label used by the search input rendered inside the popover. Falls back to the localized `select.searchLabel` key when omitted. |
|
|
1300
|
+
| `selectable` | `boolean \| ((item: Item) => boolean) \| undefined` | `true` | | Defines whether options are selectable. Can be a global boolean that affects all options or a function that accepts an item and returns a boolean that only affects that item. Non-selectable options are rendered disabled. |
|
|
1301
|
+
| `stash` | `boolean \| undefined` | | | Accumulate selected items across searches instead of replacing them. |
|
|
1302
|
+
| `transitionDuration` | `number \| undefined` | `transitions.medium` | | Transition duration (ms) for popover show/hide animations. |
|
|
1303
|
+
| `width` | `string \| number \| undefined` | `"200"` | | Width of the popover in pixels, percentage, or `'auto'`. |
|
|
1304
|
+
|
|
1305
|
+
## Events
|
|
1306
|
+
|
|
1307
|
+
- `active` — `(e: "active"): void` — Emitted when the component becomes active (focus enters it).
|
|
1308
|
+
- `blur` — `(e: "blur", event: FocusEvent): void` — Emitted when the activator loses focus.
|
|
1309
|
+
- `change` — `(e: "change", event: Event): void` — Emitted when the value changes via native change event.
|
|
1310
|
+
- `click` — `(e: "click", event: MouseEvent): void` — Emitted when the activator is clicked.
|
|
1311
|
+
- `focus` — `(e: "focus", event: FocusEvent): void` — Emitted when the activator receives focus.
|
|
1312
|
+
- `inactive` — `(e: "inactive"): void` — Emitted when focus moves outside the entire component after it was active.
|
|
1313
|
+
- `input` — `(e: "input", event: Event): void` — Emitted on native input events from the search input.
|
|
1314
|
+
- `option:add` — `(e: "option:add", text: string): void` — Emitted when the user types a new value and confirms it (Enter key) while writing is enabled (i.e. `disableWriting` does not suppress the search input for the current surface).
|
|
1315
|
+
- `update:modelValue` — `(e: "update:modelValue", value: any): void` — Emitted with the next selection value whenever selection changes.
|
|
1316
|
+
|
|
1317
|
+
## Slots
|
|
1318
|
+
|
|
1319
|
+
- `activator` — scope: `BbSelectPopoverActivatorSlotProps` — Replaces the default trigger element that opens the select popover.
|
|
1320
|
+
- `footer` — scope: `BbSelectPopoverPanelSlotProps` — Content pinned to the bottom of the panel, below the options list. Rendered in both the desktop popover and the adaptive sheet.
|
|
1321
|
+
- `group` — scope: `BbSelectPopoverGroupSlotProps<Item>` — Replaces the default group header rendered above grouped options.
|
|
1322
|
+
- `header` — scope: `BbSelectPopoverPanelSlotProps` — Content pinned to the top of the panel, above the search field. Rendered in both the desktop popover and the adaptive sheet.
|
|
1323
|
+
- `loading` — scope: `{ query: string; }` — Content shown inside the options dropdown while items are loading.
|
|
1324
|
+
- `no-data` — scope: `{ query: string; focus: () => void; }` — Content shown when no options match the query or the list is empty.
|
|
1325
|
+
- `option` — scope: `BbSelectPopoverOptionSlotProps<Item>` — Replaces the default rendering of each option row in the dropdown.
|
|
1326
|
+
|
|
1327
|
+
## See Also
|
|
1328
|
+
|
|
1329
|
+
- [BbSelect](./BbSelect.md) — Selects one or many options from structured data.
|
|
1330
|
+
- [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
|