bitboss-ui 2.1.113 → 2.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/BaseButton.md +448 -0
- package/dist/ai/BaseCheckbox.md +494 -0
- package/dist/ai/BaseCheckboxGroup.md +597 -0
- package/dist/ai/BaseColorInput.md +461 -0
- package/dist/ai/BaseDatePicker.md +739 -0
- package/dist/ai/BaseDatePickerInput.md +1517 -0
- package/dist/ai/BaseDialog.md +610 -0
- package/dist/ai/BaseInputContainer.md +570 -0
- package/dist/ai/BaseNumberInput.md +509 -0
- package/dist/ai/BaseRadio.md +405 -0
- package/dist/ai/BaseRadioGroup.md +535 -0
- package/dist/ai/BaseRating.md +489 -0
- package/dist/ai/BaseSelect.md +1720 -0
- package/dist/ai/BaseSlider.md +871 -0
- package/dist/ai/BaseSwitch.md +322 -0
- package/dist/ai/BaseSwitchGroup.md +298 -0
- package/dist/ai/BaseTag.md +624 -0
- package/dist/ai/BaseTextInput.md +392 -0
- package/dist/ai/BaseTextarea.md +398 -0
- package/dist/ai/BbAccordion.md +135 -0
- package/dist/ai/BbAlert.md +226 -0
- package/dist/ai/BbAvatar.md +200 -0
- package/dist/ai/BbBadge.md +185 -0
- package/dist/ai/BbBreadcrumbs.md +536 -0
- package/dist/ai/BbButton.md +687 -0
- package/dist/ai/BbCheckbox.md +280 -0
- package/dist/ai/BbCheckboxGroup.md +387 -0
- package/dist/ai/BbChip.md +148 -0
- package/dist/ai/BbCollapsible.md +119 -0
- package/dist/ai/BbColorInput.md +345 -0
- package/dist/ai/BbColorPalette.md +360 -0
- package/dist/ai/BbConfirm.md +160 -0
- package/dist/ai/BbDatePickerInput.md +414 -0
- package/dist/ai/BbDialog.md +135 -0
- package/dist/ai/BbDropdown.md +765 -0
- package/dist/ai/BbDropdownButton.md +629 -0
- package/dist/ai/BbDropzone.md +504 -0
- package/dist/ai/BbIcon.md +238 -0
- package/dist/ai/BbIntersection.md +121 -0
- package/dist/ai/BbNumberInput.md +372 -0
- package/dist/ai/BbOffCanvas.md +549 -0
- package/dist/ai/BbPagination.md +562 -0
- package/dist/ai/BbPopover.md +580 -0
- package/dist/ai/BbProgress.md +97 -0
- package/dist/ai/BbRadio.md +256 -0
- package/dist/ai/BbRadioGroup.md +373 -0
- package/dist/ai/BbRating.md +245 -0
- package/dist/ai/BbRatio.md +62 -0
- package/dist/ai/BbRows.md +307 -0
- package/dist/ai/BbSelect.md +562 -0
- package/dist/ai/BbSelectPopover.md +2010 -0
- package/dist/ai/BbSlider.md +274 -0
- package/dist/ai/BbSmoothHeight.md +167 -0
- package/dist/ai/BbSpinner.md +154 -0
- package/dist/ai/BbSwitch.md +151 -0
- package/dist/ai/BbSwitchGroup.md +237 -0
- package/dist/ai/BbTab.md +954 -0
- package/dist/ai/BbTable.md +1624 -0
- package/dist/ai/BbTag.md +315 -0
- package/dist/ai/BbTextInput.md +357 -0
- package/dist/ai/BbTextarea.md +277 -0
- package/dist/ai/BbToast.md +219 -0
- package/dist/ai/BbTooltip.md +353 -0
- package/dist/ai/BbTree.md +271 -0
- package/dist/ai/ChipsBox.md +211 -0
- package/dist/ai/ClearableButton.md +67 -0
- package/dist/ai/CommaBox.md +212 -0
- package/dist/ai/CommonInputInnerContainer.md +419 -0
- package/dist/ai/CommonInputOuterContainer.md +56 -0
- package/dist/ai/CommonPopover.md +446 -0
- package/dist/ai/ErrorIcon.md +61 -0
- package/dist/ai/FlatListBox.md +382 -0
- package/dist/ai/GroupedListBox.md +538 -0
- package/dist/ai/ListBox.md +234 -0
- package/dist/ai/OptionsContainer.md +257 -0
- package/dist/ai/index.md +124 -0
- package/dist/components/BaseButton/BaseButton.vue.d.ts +2 -163
- package/dist/components/BaseButton/types.d.ts +158 -0
- package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +4 -4
- package/dist/components/BaseCheckboxGroup/BaseCheckboxGroup.vue.d.ts +2 -2
- package/dist/components/BaseCheckboxGroup/types.d.ts +16 -9
- package/dist/components/BaseColorInput/BaseColorInput.vue.d.ts +12 -52
- package/dist/components/BaseDatePicker/BaseDatePicker.vue.d.ts +4 -76
- package/dist/components/BaseDatePicker/types.d.ts +100 -0
- package/dist/components/BaseDatePickerInput/BaseDatePickerInput.vue.d.ts +18 -315
- package/dist/components/BaseDatePickerInput/types.d.ts +206 -0
- package/dist/components/BaseDialog/BaseDialog.vue.d.ts +6 -156
- package/dist/components/BaseDialog/types.d.ts +180 -0
- package/dist/components/BaseInputContainer/BaseInputContainer.vue.d.ts +1 -107
- package/dist/components/BaseInputContainer/types.d.ts +126 -0
- package/dist/components/BaseNumberInput/BaseNumberInput.vue.d.ts +7 -170
- package/dist/components/BaseNumberInput/types.d.ts +191 -0
- package/dist/components/BaseRadio/BaseRadio.vue.d.ts +6 -119
- package/dist/components/BaseRadio/types.d.ts +173 -0
- package/dist/components/BaseRadioGroup/BaseRadioGroup.vue.d.ts +4 -274
- package/dist/components/BaseRadioGroup/types.d.ts +240 -0
- package/dist/components/BaseRating/BaseRating.vue.d.ts +5 -106
- package/dist/components/BaseRating/types.d.ts +144 -0
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +2 -363
- package/dist/components/BaseSelect/types.d.ts +457 -0
- package/dist/components/BaseSlider/BaseSlider.vue.d.ts +6 -178
- package/dist/components/BaseSlider/types.d.ts +201 -0
- package/dist/components/BaseSwitch/BaseSwitch.vue.d.ts +7 -35
- package/dist/components/BaseSwitch/types.d.ts +25 -0
- package/dist/components/BaseSwitchGroup/BaseSwitchGroup.vue.d.ts +5 -11
- package/dist/components/BaseSwitchGroup/types.d.ts +8 -0
- package/dist/components/BaseTag/BaseTag.vue.d.ts +27 -222
- package/dist/components/BaseTag/types.d.ts +136 -0
- package/dist/components/BaseTextInput/BaseTextInput.vue.d.ts +5 -141
- package/dist/components/BaseTextInput/types.d.ts +166 -0
- package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +5 -131
- package/dist/components/BaseTextarea/types.d.ts +151 -0
- package/dist/components/BbAccordion/BbAccordion.vue.d.ts +3 -51
- package/dist/components/BbAccordion/types.d.ts +32 -0
- package/dist/components/BbAlert/BbAlert.vue.d.ts +3 -50
- package/dist/components/BbAlert/types.d.ts +42 -0
- package/dist/components/BbAvatar/BbAvatar.vue.d.ts +3 -23
- package/dist/components/BbAvatar/types.d.ts +34 -0
- package/dist/components/BbBadge/BbBadge.vue.d.ts +3 -40
- package/dist/components/BbBadge/types.d.ts +30 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.d.ts +14 -178
- package/dist/components/BbBreadcrumbs/types.d.ts +109 -0
- package/dist/components/BbButton/BbButton.vue.d.ts +4 -163
- package/dist/components/BbButton/types.d.ts +159 -0
- package/dist/components/BbCheckbox/BbCheckbox.vue.d.ts +7 -165
- package/dist/components/BbCheckbox/types.d.ts +130 -0
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.d.ts +7 -324
- package/dist/components/BbCheckboxGroup/types.d.ts +189 -0
- package/dist/components/BbChip/BbChip.vue.d.ts +6 -28
- package/dist/components/BbChip/types.d.ts +23 -0
- package/dist/components/BbCollapsible/BbCollapsible.vue.d.ts +3 -24
- package/dist/components/BbCollapsible/types.d.ts +20 -0
- package/dist/components/BbColorInput/BbColorInput.vue.d.ts +10 -151
- package/dist/components/BbColorInput/types.d.ts +131 -0
- package/dist/components/BbColorPalette/BbColorPalette.vue.d.ts +2 -112
- package/dist/components/BbColorPalette/types.d.ts +127 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.d.ts +6 -212
- package/dist/components/BbDatePickerInput/types.d.ts +180 -0
- package/dist/components/BbDialog/BbDialog.vue.d.ts +2 -2
- package/dist/components/BbDialog/types.d.ts +1 -0
- package/dist/components/BbDropdown/BbDropdown.vue.d.ts +21 -247
- package/dist/components/BbDropdown/types.d.ts +147 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.d.ts +16 -209
- package/dist/components/BbDropdownButton/types.d.ts +114 -0
- package/dist/components/BbDropzone/BbDropzone.vue.d.ts +7 -86
- package/dist/components/BbDropzone/types.d.ts +67 -0
- package/dist/components/BbIcon/BbIcon.vue.d.ts +2 -26
- package/dist/components/BbIcon/types.d.ts +28 -0
- package/dist/components/BbIntersection/BbIntersection.vue.d.ts +3 -41
- package/dist/components/BbIntersection/types.d.ts +36 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue.d.ts +44 -175
- package/dist/components/BbNumberInput/types.d.ts +130 -0
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +5 -93
- package/dist/components/BbOffCanvas/types.d.ts +97 -0
- package/dist/components/BbPagination/BbPagination.vue.d.ts +4 -87
- package/dist/components/BbPagination/types.d.ts +80 -0
- package/dist/components/BbPopover/BbPopover.vue.d.ts +9 -135
- package/dist/components/BbPopover/types.d.ts +99 -0
- package/dist/components/BbProgress/BbProgress.vue.d.ts +2 -14
- package/dist/components/BbProgress/types.d.ts +20 -0
- package/dist/components/BbRadio/BbRadio.vue.d.ts +7 -150
- package/dist/components/BbRadio/types.d.ts +117 -0
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.d.ts +7 -322
- package/dist/components/BbRadioGroup/types.d.ts +182 -0
- package/dist/components/BbRating/BbRating.vue.d.ts +10 -113
- package/dist/components/BbRating/types.d.ts +105 -0
- package/dist/components/BbRatio/BbRatio.vue.d.ts +3 -18
- package/dist/components/BbRatio/types.d.ts +15 -0
- package/dist/components/BbSelect/BbSelect.vue.d.ts +7 -375
- package/dist/components/BbSelect/types.d.ts +351 -0
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.d.ts +1 -1
- package/dist/components/BbSelectPopover/types.d.ts +351 -0
- package/dist/components/BbSlider/BbSlider.vue.d.ts +10 -129
- package/dist/components/BbSlider/types.d.ts +123 -0
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.d.ts +2 -23
- package/dist/components/BbSmoothHeight/types.d.ts +24 -0
- package/dist/components/BbSpinner/BbSpinner.vue.d.ts +3 -5
- package/dist/components/BbSpinner/types.d.ts +8 -0
- package/dist/components/BbSwitch/BbSwitch.vue.d.ts +9 -65
- package/dist/components/BbSwitch/types.d.ts +29 -0
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.d.ts +7 -190
- package/dist/components/BbSwitchGroup/types.d.ts +81 -0
- package/dist/components/BbTab/BbTab.vue.d.ts +9 -247
- package/dist/components/BbTab/types.d.ts +186 -0
- package/dist/components/BbTag/BbTag.vue.d.ts +6 -156
- package/dist/components/BbTag/types.d.ts +158 -0
- package/dist/components/BbTextInput/BbTextInput.vue.d.ts +10 -152
- package/dist/components/BbTextInput/types.d.ts +137 -0
- package/dist/components/BbTextarea/BbTextarea.vue.d.ts +10 -142
- package/dist/components/BbTextarea/types.d.ts +123 -0
- package/dist/components/BbToast/BbToast.vue.d.ts +2 -6
- package/dist/components/BbToast/types.d.ts +8 -0
- package/dist/components/BbTooltip/BbTooltip.vue.d.ts +8 -65
- package/dist/components/BbTooltip/types.d.ts +55 -0
- package/dist/components/BbTree/BbTree.vue.d.ts +2 -65
- package/dist/components/BbTree/types.d.ts +69 -0
- package/dist/components/{ChipsBox.vue.d.ts → ChipsBox/ChipsBox.vue.d.ts} +5 -6
- package/dist/components/ChipsBox/types.d.ts +14 -0
- package/dist/components/{ClearableButton.vue.d.ts → ClearableButton/ClearableButton.vue.d.ts} +2 -0
- package/dist/components/ClearableButton/types.d.ts +3 -0
- package/dist/components/{CommaBox.vue.d.ts → CommaBox/CommaBox.vue.d.ts} +5 -6
- package/dist/components/CommaBox/types.d.ts +14 -0
- package/dist/components/CommonInputInnerContainer/CommonInputInnerContainer.vue.d.ts +25 -0
- package/dist/components/CommonInputInnerContainer/types.d.ts +47 -0
- package/dist/components/CommonInputOuterContainer/CommonInputOuterContainer.vue.d.ts +17 -0
- package/dist/components/CommonInputOuterContainer/types.d.ts +16 -0
- package/dist/components/{CommonPopover.vue.d.ts → CommonPopover/CommonPopover.vue.d.ts} +5 -30
- package/dist/components/CommonPopover/types.d.ts +43 -0
- package/dist/components/{ErrorIcon.vue.d.ts → ErrorIcon/ErrorIcon.vue.d.ts} +2 -0
- package/dist/components/ErrorIcon/types.d.ts +3 -0
- package/dist/components/FlatListBox/types.d.ts +97 -0
- package/dist/components/GroupedListBox/types.d.ts +118 -0
- package/dist/components/ListBox/ListBox.vue.d.ts +30 -0
- package/dist/components/ListBox/types.d.ts +133 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue.d.ts +13 -0
- package/dist/components/OptionsContainer/types.d.ts +96 -0
- package/dist/composables/useBbConfig.d.ts +1 -1
- package/dist/composables/useConfirm.d.ts +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +18 -18
- package/dist/index109.js +9 -9
- package/dist/index110.js +50 -49
- package/dist/index114.js +1 -1
- package/dist/index118.js +1 -1
- package/dist/index122.js +1 -0
- package/dist/index124.js +4 -4
- package/dist/index126.js +13 -13
- package/dist/index132.js +22 -19
- package/dist/index134.js +1 -1
- package/dist/index136.js +5 -5
- package/dist/index138.js +1 -1
- package/dist/index14.js +1 -1
- package/dist/index140.js +18 -17
- package/dist/index144.js +1 -1
- package/dist/index146.js +2 -2
- package/dist/index149.js +2 -2
- package/dist/index16.js +3 -3
- package/dist/index18.js +3 -3
- package/dist/index20.js +70 -59
- package/dist/index22.js +14 -14
- package/dist/index221.js +138 -2
- package/dist/index222.js +2 -138
- package/dist/index224.js +5 -34
- package/dist/index225.js +7 -32
- package/dist/index226.js +32 -26
- package/dist/index227.js +7 -0
- package/dist/index228.js +5 -5
- package/dist/index229.js +5 -8
- package/dist/index230.js +5 -7
- package/dist/index231.js +3 -2
- package/dist/index232.js +2 -9
- package/dist/index233.js +6 -13
- package/dist/index234.js +8 -3
- package/dist/index235.js +268 -2
- package/dist/index236.js +52 -11
- package/dist/index237.js +50 -6
- package/dist/index238.js +32 -3
- package/dist/index239.js +60 -3
- package/dist/index24.js +10 -10
- package/dist/index240.js +13 -2
- package/dist/index241.js +187 -17
- package/dist/index242.js +3 -12
- package/dist/index243.js +2 -51
- package/dist/index244.js +2 -18
- package/dist/index245.js +2 -12
- package/dist/index246.js +12 -16
- package/dist/index247.js +11 -28
- package/dist/index248.js +48 -15
- package/dist/index249.js +17 -4
- package/dist/index250.js +2 -2
- package/dist/index252.js +2 -2
- package/dist/index254.js +3 -135
- package/dist/index255.js +4 -0
- package/dist/index256.js +4 -107
- package/dist/index257.js +19 -12
- package/dist/index258.js +6 -2
- package/dist/index259.js +16 -7
- package/dist/index26.js +3 -3
- package/dist/index260.js +86 -7
- package/dist/index262.js +32 -0
- package/dist/index263.js +18 -5
- package/dist/index264.js +12 -5
- package/dist/index265.js +18 -5
- package/dist/index266.js +2 -5
- package/dist/index267.js +7 -5
- package/dist/index268.js +7 -5
- package/dist/index269.js +3 -67
- package/dist/index270.js +4 -33
- package/dist/index271.js +5 -2
- package/dist/index272.js +5 -2
- package/dist/index273.js +5 -3
- package/dist/index274.js +135 -4
- package/dist/index276.js +9 -6
- package/dist/index277.js +7 -11
- package/dist/index278.js +23 -5
- package/dist/index279.js +3 -5
- package/dist/index28.js +57 -55
- package/dist/index280.js +21 -266
- package/dist/index281.js +364 -43
- package/dist/index283.js +32 -31
- package/dist/index284.js +3 -60
- package/dist/index285.js +25 -4
- package/dist/index286.js +3 -20
- package/dist/index287.js +18 -5
- package/dist/index288.js +12 -373
- package/dist/index289.js +109 -0
- package/dist/index290.js +11 -6
- package/dist/index291.js +66 -15
- package/dist/index292.js +32 -10
- package/dist/index294.js +5 -8
- package/dist/index295.js +9 -20
- package/dist/index296.js +2 -8
- package/dist/index297.js +9 -23
- package/dist/index298.js +52 -24
- package/dist/index299.js +5 -188
- package/dist/index30.js +3 -3
- package/dist/index300.js +21 -3
- package/dist/index301.js +28 -3
- package/dist/index303.js +9 -0
- package/dist/index304.js +2 -7
- package/dist/index305.js +280 -3
- package/dist/index306.js +2 -2
- package/dist/index307.js +16 -5
- package/dist/index308.js +2 -7
- package/dist/index309.js +16 -3
- package/dist/index310.js +2 -3
- package/dist/index311.js +27 -3
- package/dist/index312.js +2 -2
- package/dist/index313.js +2 -28
- package/dist/index314.js +2 -17
- package/dist/index315.js +2 -4
- package/dist/index316.js +1 -1
- package/dist/index317.js +28 -3
- package/dist/index318.js +2 -280
- package/dist/index319.js +7 -2
- package/dist/index32.js +2 -2
- package/dist/index320.js +719 -125
- package/dist/index321.js +366 -2
- package/dist/index322.js +56 -14
- package/dist/index323.js +4 -2
- package/dist/index324.js +3 -16
- package/dist/index325.js +17 -2
- package/dist/index326.js +3 -16
- package/dist/index327.js +3 -2
- package/dist/index328.js +3 -19
- package/dist/index329.js +3 -2
- package/dist/index330.js +120 -22
- package/dist/index331.js +2 -2
- package/dist/index332.js +15 -2
- package/dist/index333.js +2 -2
- package/dist/index334.js +19 -2
- package/dist/index335.js +2 -2
- package/dist/index336.js +5 -2
- package/dist/index337.js +5 -3
- package/dist/index338.js +2 -4
- package/dist/index339.js +4 -719
- package/dist/index34.js +8 -8
- package/dist/index340.js +2 -366
- package/dist/index341.js +3 -57
- package/dist/index342.js +3 -6
- package/dist/index343.js +6 -5
- package/dist/index344.js +6 -34
- package/dist/index345.js +17 -127
- package/dist/index346.js +7 -396
- package/dist/index347.js +14 -199
- package/dist/index348.js +5 -259
- package/dist/index349.js +6 -227
- package/dist/index352.js +35 -2
- package/dist/index353.js +129 -2
- package/dist/index354.js +378 -114
- package/dist/index355.js +92 -6
- package/dist/index356.js +226 -17
- package/dist/index357.js +22 -9
- package/dist/index359.js +7 -5
- package/dist/index36.js +4 -4
- package/dist/index360.js +200 -7
- package/dist/index361.js +255 -18
- package/dist/index362.js +136 -0
- package/dist/index363.js +2 -93
- package/dist/index364.js +2 -441
- package/dist/index365.js +427 -114
- package/dist/index366.js +127 -46
- package/dist/index367.js +44 -67
- package/dist/index368.js +66 -516
- package/dist/index369.js +515 -45
- package/dist/index370.js +52 -0
- package/dist/index38.js +133 -131
- package/dist/index40.js +8 -8
- package/dist/index42.js +2 -2
- package/dist/index44.js +16 -15
- package/dist/index46.js +4 -4
- package/dist/index50.js +28 -25
- package/dist/index54.js +1 -1
- package/dist/index56.js +1 -1
- package/dist/index58.js +2 -2
- package/dist/index60.js +2 -2
- package/dist/index62.js +5 -5
- package/dist/index66.js +3 -1
- package/dist/index68.js +1 -1
- package/dist/index74.js +4 -4
- package/dist/index82.js +6 -6
- package/dist/index84.js +1 -1
- package/dist/index86.js +2 -2
- package/dist/index88.js +3 -3
- package/dist/index90.js +1 -1
- package/dist/index93.js +3 -3
- package/dist/index95.js +2 -2
- package/dist/index97.js +5 -5
- package/dist/index99.js +1 -1
- package/dist/utilities/functions/parseSize.d.ts +1 -1
- package/package.json +5 -3
- package/dist/components/CommonInputInnerContainer.vue.d.ts +0 -81
- package/dist/components/CommonInputOuterContainer.vue.d.ts +0 -41
- package/dist/components/FlatListBox.vue.d.ts +0 -119
- package/dist/components/GroupedListBox.vue.d.ts +0 -153
- package/dist/components/ListBox.vue.d.ts +0 -170
- package/dist/components/OptionsContainer.vue.d.ts +0 -172
- package/dist/index261.js +0 -88
- package/dist/index275.js +0 -25
- package/dist/index282.js +0 -54
- package/dist/index293.js +0 -5
- package/dist/index302.js +0 -55
- package/dist/index358.js +0 -17
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
# BbDropdown
|
|
2
|
+
|
|
3
|
+
## Template & Script
|
|
4
|
+
|
|
5
|
+
```vue
|
|
6
|
+
<template>
|
|
7
|
+
<slot
|
|
8
|
+
v-if="!hasExternalActivator"
|
|
9
|
+
name="activator"
|
|
10
|
+
v-bind="{
|
|
11
|
+
props: {
|
|
12
|
+
ref: setActivatorRef,
|
|
13
|
+
},
|
|
14
|
+
disabled,
|
|
15
|
+
open,
|
|
16
|
+
placement,
|
|
17
|
+
}"
|
|
18
|
+
></slot>
|
|
19
|
+
<CommonPopover
|
|
20
|
+
ref="popover"
|
|
21
|
+
v-model="open"
|
|
22
|
+
:anchor="effectiveAnchor"
|
|
23
|
+
:arrow-padding="arrowPadding"
|
|
24
|
+
:boundary="boundary"
|
|
25
|
+
class="bb-dropdown"
|
|
26
|
+
:class="{
|
|
27
|
+
[`bb-dropdown--${theme}`]: true,
|
|
28
|
+
}"
|
|
29
|
+
:eager="eager"
|
|
30
|
+
:offset="offset"
|
|
31
|
+
:hide-arrow="placement === 'cursor'"
|
|
32
|
+
:padding="padding"
|
|
33
|
+
:placement="placement === 'cursor' ? 'bottom-start' : placement"
|
|
34
|
+
:transition-duration="transitionDuration"
|
|
35
|
+
v-bind="$attrs"
|
|
36
|
+
>
|
|
37
|
+
<span
|
|
38
|
+
:id="`menu_${id}`"
|
|
39
|
+
:aria-labelledby="id"
|
|
40
|
+
aria-role="menu"
|
|
41
|
+
class="bb-dropdown__items-container"
|
|
42
|
+
:inert="!open"
|
|
43
|
+
:style="parsedWidth ? { width: parsedWidth } : undefined"
|
|
44
|
+
>
|
|
45
|
+
<slot
|
|
46
|
+
:disabled="disabled"
|
|
47
|
+
name="prepend"
|
|
48
|
+
:open="open"
|
|
49
|
+
:placement="placement"
|
|
50
|
+
></slot>
|
|
51
|
+
<template
|
|
52
|
+
v-for="({ original, ...item }, index) in mappedItems"
|
|
53
|
+
:key="item.key"
|
|
54
|
+
>
|
|
55
|
+
<slot
|
|
56
|
+
:disabled="item.disabled"
|
|
57
|
+
:index="index"
|
|
58
|
+
:item="items[index]"
|
|
59
|
+
:name="`item:prepend`"
|
|
60
|
+
:open="open"
|
|
61
|
+
:placement="placement"
|
|
62
|
+
:text="item.text"
|
|
63
|
+
></slot>
|
|
64
|
+
<BaseButton
|
|
65
|
+
:aria-role="'menuitem'"
|
|
66
|
+
class="bb-dropdown__item"
|
|
67
|
+
:class="{
|
|
68
|
+
'bb-dropdown__item--first': index === 0,
|
|
69
|
+
'bb-dropdown__item--last': index === items.length - 1,
|
|
70
|
+
}"
|
|
71
|
+
:data-dropdown-item="true"
|
|
72
|
+
v-bind="item"
|
|
73
|
+
>
|
|
74
|
+
<slot
|
|
75
|
+
:disabled="item.disabled"
|
|
76
|
+
:index="index"
|
|
77
|
+
:item="original"
|
|
78
|
+
:name="item.key"
|
|
79
|
+
:open="open"
|
|
80
|
+
:placement="placement"
|
|
81
|
+
:text="item.text"
|
|
82
|
+
><BbIcon
|
|
83
|
+
v-if="item['prepend:icon']"
|
|
84
|
+
class="item__prepend-icon"
|
|
85
|
+
:type="item['prepend:icon']" />
|
|
86
|
+
<span class="item__content">{{ item.text }}</span>
|
|
87
|
+
<BbIcon
|
|
88
|
+
v-if="item['append:icon']"
|
|
89
|
+
class="item__append-icon"
|
|
90
|
+
:type="item['append:icon']" /></slot></BaseButton
|
|
91
|
+
><slot
|
|
92
|
+
:disabled="item.disabled"
|
|
93
|
+
:index="index"
|
|
94
|
+
:item="items[index]"
|
|
95
|
+
:name="`item:append`"
|
|
96
|
+
:open="open"
|
|
97
|
+
:placement="placement"
|
|
98
|
+
:text="item.text"
|
|
99
|
+
></slot>
|
|
100
|
+
</template>
|
|
101
|
+
<slot
|
|
102
|
+
:disabled="disabled"
|
|
103
|
+
name="append"
|
|
104
|
+
:open="open"
|
|
105
|
+
:placement="placement"
|
|
106
|
+
></slot>
|
|
107
|
+
</span>
|
|
108
|
+
</CommonPopover>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
import { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue';
|
|
113
|
+
import { findRightIndex } from '@/utilities/functions/findRightIndex';
|
|
114
|
+
import { useId } from '@/composables/useId';
|
|
115
|
+
import { useUntil } from '@/composables/useUntil';
|
|
116
|
+
import { wait } from '@/utilities/functions/wait';
|
|
117
|
+
import BaseButton from '../BaseButton/BaseButton.vue';
|
|
118
|
+
import BbIcon from '../BbIcon/BbIcon.vue';
|
|
119
|
+
import CommonPopover from '../CommonPopover/CommonPopover.vue';
|
|
120
|
+
import selectors from 'focusable-selectors';
|
|
121
|
+
import type { ColumnName } from '@/types/ColumnName';
|
|
122
|
+
import { extractDomContainer } from '@/utilities/functions/extractDomContainer';
|
|
123
|
+
import { useElementSize, useIntersectionObserver } from '@vueuse/core';
|
|
124
|
+
import { parseWidthString } from '@/utilities/functions/parseWidthString';
|
|
125
|
+
import type {
|
|
126
|
+
BbDropdownProps,
|
|
127
|
+
BbDropdownSlots,
|
|
128
|
+
Item,
|
|
129
|
+
MappedItem,
|
|
130
|
+
DropdownTrigger,
|
|
131
|
+
} from './types';
|
|
132
|
+
export type {
|
|
133
|
+
BbDropdownProps,
|
|
134
|
+
BbDropdownSlots,
|
|
135
|
+
Item,
|
|
136
|
+
MappedItem,
|
|
137
|
+
DropdownTrigger,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
type InferredBodySlots = ColumnName<BbDropdownProps['items'][number]['key']>;
|
|
141
|
+
defineSlots<BbDropdownSlots>();
|
|
142
|
+
|
|
143
|
+
const props = withDefaults(defineProps<BbDropdownProps>(), {
|
|
144
|
+
arrowPadding: 10,
|
|
145
|
+
placement: 'bottom',
|
|
146
|
+
theme: 'default',
|
|
147
|
+
transitionDuration: 300,
|
|
148
|
+
trigger: 'click',
|
|
149
|
+
width: 'auto',
|
|
150
|
+
padding: 10,
|
|
151
|
+
offset: 3,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
defineOptions({
|
|
155
|
+
inheritAttrs: false,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const id = props.id ?? `bb_${useId().id.value}`;
|
|
159
|
+
const activator = ref<HTMLElement>();
|
|
160
|
+
const activatorDom = computed(() => extractDomContainer(activator.value));
|
|
161
|
+
const hasExternalActivator = computed(() => props.activator != null);
|
|
162
|
+
const popoverRef = useTemplateRef('popover');
|
|
163
|
+
const popoverDom = computed(() => extractDomContainer(popoverRef.value));
|
|
164
|
+
const open = ref(false);
|
|
165
|
+
const hasOpenedOnce = useUntil(open, { initialValue: props.eager });
|
|
166
|
+
|
|
167
|
+
const triggers = computed(() => {
|
|
168
|
+
const raw = props.trigger;
|
|
169
|
+
return new Set(Array.isArray(raw) ? raw : [raw]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const createVirtualElement = (x: number, y: number) => ({
|
|
173
|
+
getBoundingClientRect: () => ({
|
|
174
|
+
x,
|
|
175
|
+
y,
|
|
176
|
+
width: 0,
|
|
177
|
+
height: 0,
|
|
178
|
+
top: y,
|
|
179
|
+
left: x,
|
|
180
|
+
right: x,
|
|
181
|
+
bottom: y,
|
|
182
|
+
toJSON() {
|
|
183
|
+
return this;
|
|
184
|
+
},
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const cursorAnchor = ref<ReturnType<typeof createVirtualElement> | null>(null);
|
|
189
|
+
|
|
190
|
+
const effectiveAnchor = computed(() =>
|
|
191
|
+
props.placement === 'cursor' && cursorAnchor.value
|
|
192
|
+
? cursorAnchor.value
|
|
193
|
+
: activator.value
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const setActivatorRef = (reference: unknown) => {
|
|
197
|
+
const el = extractDomContainer(reference);
|
|
198
|
+
if (!el) return;
|
|
199
|
+
activator.value = el;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const { resume, pause } = useIntersectionObserver(
|
|
203
|
+
activatorDom,
|
|
204
|
+
async ([{ intersectionRatio }]) => {
|
|
205
|
+
if (intersectionRatio !== 1 && open.value) {
|
|
206
|
+
await closeDropdown();
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{ threshold: [0, 1], immediate: false }
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const mappedItems = computed(() => {
|
|
213
|
+
let firstFocusableIndex: number = props.items.findIndex((el) => !el.disabled);
|
|
214
|
+
let lastFocusableIndex: number = findRightIndex(
|
|
215
|
+
props.items,
|
|
216
|
+
(item) => !item.disabled
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return props.items.map((item, index) => {
|
|
220
|
+
const itemid = `${id}-item-${item.key}`;
|
|
221
|
+
const { onClick: handler, ...rest } = item;
|
|
222
|
+
|
|
223
|
+
/* If a click handler has been passed close the dropdown,
|
|
224
|
+
* return focus and run the handler
|
|
225
|
+
*/
|
|
226
|
+
const onClick = async (event: MouseEvent) => {
|
|
227
|
+
event.stopPropagation();
|
|
228
|
+
await closeDropdown();
|
|
229
|
+
const nextElement = document.querySelector<HTMLElement>(`#${id}`);
|
|
230
|
+
if (nextElement) nextElement.focus();
|
|
231
|
+
handler?.(event);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* On keydown
|
|
236
|
+
* arrow down move to the next focusable element if it's not the last
|
|
237
|
+
* arrow up move to the previous focusable element if it's not the first
|
|
238
|
+
* on tab search the next focusable element after the button that opens the dropdown
|
|
239
|
+
* if an element is found focus on that
|
|
240
|
+
* (the menu is attached to body so it's not in order)
|
|
241
|
+
*/
|
|
242
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
243
|
+
if (
|
|
244
|
+
!(event.target instanceof HTMLElement) ||
|
|
245
|
+
!event.target.dataset['dropdownItem'] ||
|
|
246
|
+
!popoverDom.value
|
|
247
|
+
) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (event.key === 'ArrowDown') {
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
if (index !== lastFocusableIndex) {
|
|
254
|
+
const option = popoverDom.value.querySelector(
|
|
255
|
+
selectors.map((selector) => `#${itemid} ~ ${selector}`).join(',')
|
|
256
|
+
);
|
|
257
|
+
if (option instanceof HTMLElement) {
|
|
258
|
+
option.focus();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} else if (event.key === 'ArrowUp') {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
if (index !== firstFocusableIndex) {
|
|
264
|
+
const indexOfPrevious = findRightIndex(
|
|
265
|
+
props.items,
|
|
266
|
+
(item, i) => i < index && !item.disabled
|
|
267
|
+
);
|
|
268
|
+
const options = Array.from(
|
|
269
|
+
popoverDom.value.querySelectorAll(`.bb-dropdown__item`)
|
|
270
|
+
);
|
|
271
|
+
const option = options[indexOfPrevious];
|
|
272
|
+
if (option instanceof HTMLElement) {
|
|
273
|
+
option.focus();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else if (event.key === 'Tab') {
|
|
277
|
+
if (index === lastFocusableIndex && !event.shiftKey) {
|
|
278
|
+
const focusableElements = Array.from(
|
|
279
|
+
document.querySelectorAll(selectors.join(','))
|
|
280
|
+
);
|
|
281
|
+
const indexOfButton = focusableElements.findIndex(
|
|
282
|
+
(el) => el.id === id
|
|
283
|
+
);
|
|
284
|
+
const nextElement = focusableElements[indexOfButton + 1];
|
|
285
|
+
if (nextElement instanceof HTMLElement) {
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
closeDropdown();
|
|
288
|
+
nextElement.focus();
|
|
289
|
+
}
|
|
290
|
+
} else if (index === firstFocusableIndex && event.shiftKey) {
|
|
291
|
+
const nextElement = document.querySelector<HTMLElement>(`#${id}`);
|
|
292
|
+
if (nextElement) {
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
nextElement.focus();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
...rest,
|
|
302
|
+
original: item,
|
|
303
|
+
key: item.key.split(/\W+/g).join('_').toLowerCase() as InferredBodySlots,
|
|
304
|
+
disabled: rest.disabled || props.disabled,
|
|
305
|
+
id: itemid,
|
|
306
|
+
onClick,
|
|
307
|
+
onKeydown,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const onClick = (event: MouseEvent) => {
|
|
313
|
+
if (props.disabled) return;
|
|
314
|
+
if (longPressTriggered) {
|
|
315
|
+
longPressTriggered = false;
|
|
316
|
+
event.preventDefault();
|
|
317
|
+
event.stopPropagation();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
if (props.placement === 'cursor') {
|
|
322
|
+
cursorAnchor.value = createVirtualElement(event.clientX, event.clientY);
|
|
323
|
+
}
|
|
324
|
+
open.value ? closeDropdown() : openDropdown();
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const onContextMenu = (event: MouseEvent) => {
|
|
328
|
+
if (props.disabled) return;
|
|
329
|
+
event.preventDefault();
|
|
330
|
+
event.stopPropagation();
|
|
331
|
+
if (props.placement === 'cursor') {
|
|
332
|
+
cursorAnchor.value = createVirtualElement(event.clientX, event.clientY);
|
|
333
|
+
if (open.value) return;
|
|
334
|
+
}
|
|
335
|
+
open.value ? closeDropdown() : openDropdown();
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// --- Long-press (mobile equivalent of contextMenu) ---
|
|
339
|
+
|
|
340
|
+
const LONG_PRESS_DELAY = 500;
|
|
341
|
+
const LONG_PRESS_MOVE_THRESHOLD = 10;
|
|
342
|
+
let longPressTimer: ReturnType<typeof setTimeout> | undefined;
|
|
343
|
+
let longPressOrigin: { x: number; y: number } | null = null;
|
|
344
|
+
let longPressTriggered = false;
|
|
345
|
+
|
|
346
|
+
const clearLongPress = () => {
|
|
347
|
+
clearTimeout(longPressTimer);
|
|
348
|
+
longPressOrigin = null;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
352
|
+
if (props.disabled || event.pointerType === 'mouse') return;
|
|
353
|
+
longPressTriggered = false;
|
|
354
|
+
longPressOrigin = { x: event.clientX, y: event.clientY };
|
|
355
|
+
longPressTimer = setTimeout(() => {
|
|
356
|
+
longPressTriggered = true;
|
|
357
|
+
if (props.placement === 'cursor' && longPressOrigin) {
|
|
358
|
+
cursorAnchor.value = createVirtualElement(
|
|
359
|
+
longPressOrigin.x,
|
|
360
|
+
longPressOrigin.y
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
longPressOrigin = null;
|
|
364
|
+
openDropdown();
|
|
365
|
+
}, LONG_PRESS_DELAY);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const onPointerMove = (event: PointerEvent) => {
|
|
369
|
+
if (!longPressOrigin) return;
|
|
370
|
+
const dx = event.clientX - longPressOrigin.x;
|
|
371
|
+
const dy = event.clientY - longPressOrigin.y;
|
|
372
|
+
if (Math.sqrt(dx * dx + dy * dy) > LONG_PRESS_MOVE_THRESHOLD) {
|
|
373
|
+
clearLongPress();
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const onPointerUp = () => {
|
|
378
|
+
clearLongPress();
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const onKeydown = async (event: KeyboardEvent) => {
|
|
382
|
+
if (props.disabled) return;
|
|
383
|
+
const firstFocusableIndex = props.items.findIndex((el) => !el.disabled);
|
|
384
|
+
const lastFocusableIndex = findRightIndex(
|
|
385
|
+
props.items,
|
|
386
|
+
(item) => !item.disabled
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const focusItem = (index: number) => {
|
|
390
|
+
if (!popoverDom.value || !mappedItems.value.length || index === -1) return;
|
|
391
|
+
const idToFocus = mappedItems.value[index].id;
|
|
392
|
+
const option = popoverDom.value.querySelector<HTMLElement>(`#${idToFocus}`);
|
|
393
|
+
option?.focus();
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
if (event.key === 'ArrowDown') {
|
|
397
|
+
event.preventDefault();
|
|
398
|
+
if (!open.value) await openDropdown();
|
|
399
|
+
focusItem(firstFocusableIndex);
|
|
400
|
+
} else if (event.key === 'ArrowUp') {
|
|
401
|
+
event.preventDefault();
|
|
402
|
+
if (!open.value) await openDropdown();
|
|
403
|
+
focusItem(lastFocusableIndex);
|
|
404
|
+
} else if (event.key === 'Tab' && open.value && !event.shiftKey) {
|
|
405
|
+
event.preventDefault();
|
|
406
|
+
focusItem(firstFocusableIndex);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const onOutsideInteraction = (event: Event) => {
|
|
411
|
+
const target = event.target;
|
|
412
|
+
if (!(target instanceof HTMLElement)) return;
|
|
413
|
+
|
|
414
|
+
const isOutside = !popoverDom.value?.contains(target);
|
|
415
|
+
if (isOutside) closeDropdown();
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const onEsc = (event: KeyboardEvent) => {
|
|
419
|
+
if (!open.value || event.key !== 'Escape') return;
|
|
420
|
+
|
|
421
|
+
event.stopImmediatePropagation();
|
|
422
|
+
event.preventDefault();
|
|
423
|
+
|
|
424
|
+
if (document.activeElement?.id !== id) {
|
|
425
|
+
const button = document.querySelector<HTMLElement>(`#${id}`);
|
|
426
|
+
button?.focus();
|
|
427
|
+
}
|
|
428
|
+
closeDropdown();
|
|
429
|
+
};
|
|
430
|
+
const openDropdown = async () => {
|
|
431
|
+
resume();
|
|
432
|
+
await wait(50);
|
|
433
|
+
open.value = true;
|
|
434
|
+
document.addEventListener('click', onOutsideInteraction, { passive: true });
|
|
435
|
+
document.addEventListener('contextmenu', onOutsideInteraction, {
|
|
436
|
+
passive: true,
|
|
437
|
+
});
|
|
438
|
+
document.addEventListener('focusin', onOutsideInteraction, { passive: true });
|
|
439
|
+
document.addEventListener('keydown', onEsc, { capture: true });
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const closeDropdown = async () => {
|
|
443
|
+
open.value = false;
|
|
444
|
+
pause();
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
watch(open, (value) => {
|
|
448
|
+
if (!value) {
|
|
449
|
+
document.removeEventListener('click', onOutsideInteraction);
|
|
450
|
+
document.removeEventListener('contextmenu', onOutsideInteraction);
|
|
451
|
+
document.removeEventListener('focusin', onOutsideInteraction);
|
|
452
|
+
document.removeEventListener('keydown', onEsc, { capture: true });
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
watch(
|
|
457
|
+
() => props.activator,
|
|
458
|
+
(val) => {
|
|
459
|
+
if (val) setActivatorRef(val);
|
|
460
|
+
},
|
|
461
|
+
{ immediate: true }
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
let cleanupActivator: (() => void) | null = null;
|
|
465
|
+
|
|
466
|
+
watch(
|
|
467
|
+
[activator, triggers] as const,
|
|
468
|
+
([el, active]) => {
|
|
469
|
+
if (cleanupActivator) {
|
|
470
|
+
cleanupActivator();
|
|
471
|
+
cleanupActivator = null;
|
|
472
|
+
}
|
|
473
|
+
if (!el) return;
|
|
474
|
+
|
|
475
|
+
el.id = id;
|
|
476
|
+
|
|
477
|
+
const removers: (() => void)[] = [];
|
|
478
|
+
|
|
479
|
+
if (active.has('click')) {
|
|
480
|
+
el.addEventListener('click', onClick);
|
|
481
|
+
removers.push(() => el.removeEventListener('click', onClick));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (active.has('contextMenu')) {
|
|
485
|
+
el.addEventListener('contextmenu', onContextMenu);
|
|
486
|
+
el.addEventListener('pointerdown', onPointerDown);
|
|
487
|
+
el.addEventListener('pointermove', onPointerMove);
|
|
488
|
+
el.addEventListener('pointerup', onPointerUp);
|
|
489
|
+
el.addEventListener('pointercancel', onPointerUp);
|
|
490
|
+
removers.push(() => {
|
|
491
|
+
el.removeEventListener('contextmenu', onContextMenu);
|
|
492
|
+
el.removeEventListener('pointerdown', onPointerDown);
|
|
493
|
+
el.removeEventListener('pointermove', onPointerMove);
|
|
494
|
+
el.removeEventListener('pointerup', onPointerUp);
|
|
495
|
+
el.removeEventListener('pointercancel', onPointerUp);
|
|
496
|
+
clearLongPress();
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
el.addEventListener('keydown', onKeydown);
|
|
501
|
+
removers.push(() => el.removeEventListener('keydown', onKeydown));
|
|
502
|
+
|
|
503
|
+
cleanupActivator = () => {
|
|
504
|
+
removers.forEach((fn) => fn());
|
|
505
|
+
el.removeAttribute('id');
|
|
506
|
+
el.removeAttribute('aria-expanded');
|
|
507
|
+
el.removeAttribute('aria-controls');
|
|
508
|
+
};
|
|
509
|
+
},
|
|
510
|
+
{ immediate: true }
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
// Update reactive ARIA attributes
|
|
514
|
+
watch(
|
|
515
|
+
[activator, open, hasOpenedOnce] as const,
|
|
516
|
+
([el, isOpen, opened]) => {
|
|
517
|
+
if (!el) return;
|
|
518
|
+
|
|
519
|
+
el.setAttribute('aria-expanded', opened ? String(isOpen) : 'false');
|
|
520
|
+
if (opened) {
|
|
521
|
+
el.setAttribute('aria-controls', `menu_${id}`);
|
|
522
|
+
} else {
|
|
523
|
+
el.removeAttribute('aria-controls');
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
{ immediate: true }
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
onBeforeUnmount(() => {
|
|
530
|
+
cleanupActivator?.();
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const { width: activatorWidth } = useElementSize(
|
|
534
|
+
activatorDom,
|
|
535
|
+
{ width: 0, height: 0 },
|
|
536
|
+
{
|
|
537
|
+
box: 'border-box',
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
const parsedWidth = computed(() => {
|
|
541
|
+
if (props.width === 'auto') return undefined;
|
|
542
|
+
if (props.width) {
|
|
543
|
+
const { value, unit } = parseWidthString(props.width);
|
|
544
|
+
if (unit === '%') {
|
|
545
|
+
if (!activatorDom.value) return '0px';
|
|
546
|
+
return `${activatorWidth.value * (value / 100)}px`;
|
|
547
|
+
}
|
|
548
|
+
return `${value}px`;
|
|
549
|
+
}
|
|
550
|
+
return '0px';
|
|
551
|
+
});
|
|
552
|
+
</script>
|
|
553
|
+
|
|
554
|
+
<style lang="postcss">
|
|
555
|
+
@import './index.css';
|
|
556
|
+
</style>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Types
|
|
560
|
+
|
|
561
|
+
```ts
|
|
562
|
+
import type { BaseButtonProps } from '../BaseButton/types';
|
|
563
|
+
import type { CommonProps } from '@/types/CommonProps';
|
|
564
|
+
import type { IconType } from '@/types/Icon';
|
|
565
|
+
|
|
566
|
+
export type DropdownTrigger = 'click' | 'contextMenu';
|
|
567
|
+
|
|
568
|
+
export type Item = Omit<BaseButtonProps, 'block' | 'tag' | 'type'> & {
|
|
569
|
+
/**
|
|
570
|
+
* Click handler of the item.
|
|
571
|
+
*/
|
|
572
|
+
onClick?: (...args: any[]) => any;
|
|
573
|
+
/**
|
|
574
|
+
* Key used to identify the element.
|
|
575
|
+
*/
|
|
576
|
+
key: string;
|
|
577
|
+
/**
|
|
578
|
+
* Icon to be displayed before the text.
|
|
579
|
+
*/
|
|
580
|
+
'prepend:icon'?: IconType;
|
|
581
|
+
/**
|
|
582
|
+
* Icon to be displayed after the text.
|
|
583
|
+
*/
|
|
584
|
+
'append:icon'?: IconType;
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
export type MappedItem = Item & {
|
|
588
|
+
/**
|
|
589
|
+
* Identifier of the element among its siblings.
|
|
590
|
+
*/
|
|
591
|
+
id: string;
|
|
592
|
+
/**
|
|
593
|
+
* Keydown event used to handle accessibility.
|
|
594
|
+
*/
|
|
595
|
+
onKeydown: (event: KeyboardEvent) => void;
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
export type BbDropdownProps = Pick<
|
|
599
|
+
CommonProps,
|
|
600
|
+
| 'arrowPadding'
|
|
601
|
+
| 'disabled'
|
|
602
|
+
| 'eager'
|
|
603
|
+
| 'id'
|
|
604
|
+
| 'offset'
|
|
605
|
+
| 'padding'
|
|
606
|
+
| 'transitionDuration'
|
|
607
|
+
> & {
|
|
608
|
+
/**
|
|
609
|
+
* Position of the dropdown relative to the activator.
|
|
610
|
+
* Use `'cursor'` to anchor at the mouse/touch coordinates (hides arrow).
|
|
611
|
+
*
|
|
612
|
+
* @defaultValue `'bottom'`
|
|
613
|
+
*/
|
|
614
|
+
placement?: CommonProps['placement'] | 'cursor';
|
|
615
|
+
/**
|
|
616
|
+
* External activator element or component ref.
|
|
617
|
+
* When provided, the activator slot is not rendered and event listeners
|
|
618
|
+
* are attached programmatically to the referenced element.
|
|
619
|
+
*/
|
|
620
|
+
activator?: HTMLElement | Record<string, any> | null;
|
|
621
|
+
/**
|
|
622
|
+
* Constrains the dropdown within a bounding element.
|
|
623
|
+
* Accepts an HTMLElement, a Vue component ref, or a CSS selector string.
|
|
624
|
+
*/
|
|
625
|
+
boundary?: HTMLElement | Record<string, any> | string | null;
|
|
626
|
+
/**
|
|
627
|
+
* Array used to render each dropdown button.
|
|
628
|
+
* They can act as `button`, as `a`, or as `router-link`
|
|
629
|
+
* as they are based on the functionality provided by `BaseButton`.
|
|
630
|
+
*/
|
|
631
|
+
items: Item[];
|
|
632
|
+
/**
|
|
633
|
+
* Name of the theme to apply to the component. It creates a CSS class with the name of the theme.
|
|
634
|
+
*
|
|
635
|
+
* @deprecated since the popover is local you should use a normal css class applied to the top level element
|
|
636
|
+
* @defaultValue `'default'`
|
|
637
|
+
*/
|
|
638
|
+
theme?: string;
|
|
639
|
+
/**
|
|
640
|
+
* Interaction(s) that open the dropdown.
|
|
641
|
+
* Accepts a single trigger or an array to combine them.
|
|
642
|
+
* - `'click'` — left-click / tap (default)
|
|
643
|
+
* - `'contextMenu'` — right-click on desktop, long-press on mobile
|
|
644
|
+
*
|
|
645
|
+
* @defaultValue `'click'`
|
|
646
|
+
*/
|
|
647
|
+
trigger?: DropdownTrigger | DropdownTrigger[];
|
|
648
|
+
/**
|
|
649
|
+
* Width of the dropdown in pixels, percentage, or 'auto' to fit content.
|
|
650
|
+
*
|
|
651
|
+
* @defaultValue `'auto'`
|
|
652
|
+
*/
|
|
653
|
+
width?: number | string;
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
/** Props spread onto the activator element to wire up the floating-UI anchor reference. */
|
|
657
|
+
export type BbDropdownActivatorProps = {
|
|
658
|
+
/** Ref callback to register the activator element. */
|
|
659
|
+
ref: (reference: unknown) => void;
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
/** Props exposed by the `activator` slot. */
|
|
663
|
+
export type BbDropdownActivatorSlotProps = {
|
|
664
|
+
/** Spread onto the trigger element to wire the floating-UI anchor reference. */
|
|
665
|
+
props: BbDropdownActivatorProps;
|
|
666
|
+
/** Whether the dropdown is globally disabled. */
|
|
667
|
+
disabled?: boolean;
|
|
668
|
+
/** Whether the dropdown panel is currently open. */
|
|
669
|
+
open: boolean;
|
|
670
|
+
/** The current placement of the dropdown panel. */
|
|
671
|
+
placement: BbDropdownProps['placement'];
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
/** Props exposed by the `prepend`, `append` slots. */
|
|
675
|
+
export type BbDropdownEdgeSlotProps = {
|
|
676
|
+
/** Whether the dropdown is globally disabled. */
|
|
677
|
+
disabled?: boolean;
|
|
678
|
+
/** Whether the dropdown panel is currently open. */
|
|
679
|
+
open: boolean;
|
|
680
|
+
/** The current placement of the dropdown panel. */
|
|
681
|
+
placement: BbDropdownProps['placement'];
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
/** Props exposed by the `item:prepend` and `item:append` slots, and named item slots. */
|
|
685
|
+
export type BbDropdownItemSlotProps = {
|
|
686
|
+
/** Whether this item is disabled. */
|
|
687
|
+
disabled?: MappedItem['disabled'];
|
|
688
|
+
/** The zero-based index of this item. */
|
|
689
|
+
index: number;
|
|
690
|
+
/** The raw item object from the `items` prop. */
|
|
691
|
+
item: BbDropdownProps['items'][number];
|
|
692
|
+
/** Whether the dropdown panel is currently open. */
|
|
693
|
+
open: boolean;
|
|
694
|
+
/** The current placement of the dropdown panel. */
|
|
695
|
+
placement: BbDropdownProps['placement'];
|
|
696
|
+
/** The resolved display text for this item. */
|
|
697
|
+
text: MappedItem['text'];
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
export type BbDropdownSlots = {
|
|
701
|
+
/**
|
|
702
|
+
* The element that triggers the dropdown to open. Spread `props` onto your trigger element.
|
|
703
|
+
*/
|
|
704
|
+
activator?: (props: BbDropdownActivatorSlotProps) => any;
|
|
705
|
+
/**
|
|
706
|
+
* Content rendered before the first item in the dropdown panel.
|
|
707
|
+
*/
|
|
708
|
+
prepend?: (props: BbDropdownEdgeSlotProps) => any;
|
|
709
|
+
/**
|
|
710
|
+
* Content rendered before each dropdown item's default button content.
|
|
711
|
+
*/
|
|
712
|
+
'item:prepend'?: (props: BbDropdownItemSlotProps) => any;
|
|
713
|
+
/**
|
|
714
|
+
* Content rendered after each dropdown item's default button content.
|
|
715
|
+
*/
|
|
716
|
+
'item:append'?: (props: BbDropdownItemSlotProps) => any;
|
|
717
|
+
/**
|
|
718
|
+
* Content rendered after the last item in the dropdown panel.
|
|
719
|
+
*/
|
|
720
|
+
append?: (props: BbDropdownEdgeSlotProps) => any;
|
|
721
|
+
} & {
|
|
722
|
+
/**
|
|
723
|
+
* Named slot for a specific item, identified by `item.key`. Replaces the item's default label content.
|
|
724
|
+
*/
|
|
725
|
+
[key: string]: (props: BbDropdownItemSlotProps) => any;
|
|
726
|
+
};
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
## Styles
|
|
730
|
+
|
|
731
|
+
```css
|
|
732
|
+
.bb-dropdown {
|
|
733
|
+
overflow: clip;
|
|
734
|
+
|
|
735
|
+
.bb-dropdown__items-container {
|
|
736
|
+
color: var(--bb-text);
|
|
737
|
+
display: inline-grid;
|
|
738
|
+
grid-template-columns: 1fr;
|
|
739
|
+
padding: 6px;
|
|
740
|
+
.bb-dropdown__item {
|
|
741
|
+
border-radius: var(--bb-radius);
|
|
742
|
+
column-gap: 6px;
|
|
743
|
+
display: inline-flex;
|
|
744
|
+
align-items: center;
|
|
745
|
+
padding: 6px 8px;
|
|
746
|
+
text-align: left;
|
|
747
|
+
min-width: 100px;
|
|
748
|
+
|
|
749
|
+
&:hover {
|
|
750
|
+
background-color: color-mix(in srgb, var(--bb-text) 5%, transparent);
|
|
751
|
+
}
|
|
752
|
+
&:focus {
|
|
753
|
+
background-color: color-mix(in srgb, var(--bb-text) 15%, transparent);
|
|
754
|
+
}
|
|
755
|
+
.item__prepend-icon,
|
|
756
|
+
.item__append-icon {
|
|
757
|
+
flex-shrink: 0;
|
|
758
|
+
}
|
|
759
|
+
.item__content {
|
|
760
|
+
flex: auto;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
```
|