@livestorm/ui-vue 2.0.5 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/components-vite.js +2 -2
- package/dist/UiButton-uyW8EHRJ.mjs +602 -0
- package/dist/UiTimelineItem-EiOEi1yF.mjs +7293 -0
- package/dist/UiTooltip-DZcPD6_N.mjs +158 -0
- package/dist/a11y-CaZxRWzo.mjs +6 -0
- package/dist/breakpoints.mjs +4 -9
- package/dist/bus.mjs +5 -5
- package/dist/components.mjs +236 -222
- package/dist/i18n-B8QOMuFD.mjs +46 -0
- package/dist/index.mjs +4 -87
- package/dist/integrations.mjs +3 -14
- package/dist/layouts.mjs +228 -236
- package/dist/responsive.mjs +31 -36
- package/dist/slot.mjs +11 -17
- package/dist/toastDSV2-D3xDZPe5.mjs +105 -0
- package/dist/types/index.d.ts +23 -1
- package/dist/types/src/components/Toast/ToastMessage.vue.d.ts +17 -7
- package/dist/types/src/components/Toast/toastDSV2.d.ts +89 -64
- package/dist/types/src/components/UiActionBar/UiActionBar.vue.d.ts +3 -1
- package/dist/types/src/components/UiActionBar/UiActionBarItem.vue.d.ts +18 -3
- package/dist/types/src/components/UiActionBar/types.d.ts +5 -1
- package/dist/types/src/components/UiAvatar/UiAvatar.vue.d.ts +4 -2
- package/dist/types/src/components/UiAvatarList/UiAvatarList.vue.d.ts +3 -2
- package/dist/types/src/components/UiBanner/UiBanner.vue.d.ts +13 -1
- package/dist/types/src/components/UiButton/UiButton.vue.d.ts +102 -38
- package/dist/types/src/components/UiButtonGroup/UiButtonGroup.vue.d.ts +8 -2
- package/dist/types/src/components/UiButtonGroup/UiButtonGroupItem.vue.d.ts +3 -0
- package/dist/types/src/components/UiButtonGroup/types.d.ts +5 -1
- package/dist/types/src/components/UiChoice/UiChoice.vue.d.ts +71 -0
- package/dist/types/src/components/UiChoice/index.d.ts +2 -0
- package/dist/types/src/components/UiChoice/types.d.ts +7 -0
- package/dist/types/src/components/UiColorField/UiColorField.vue.d.ts +1 -1
- package/dist/types/src/components/UiColorPicker/UiColorPicker.vue.d.ts +2 -2
- package/dist/types/src/components/UiCombobox/UiCombobox.types.d.ts +47 -0
- package/dist/types/src/components/UiCombobox/UiCombobox.vue.d.ts +40 -0
- package/dist/types/src/components/UiCombobox/index.d.ts +2 -0
- package/dist/types/src/components/UiDataTable/UiDataTable.types.d.ts +46 -0
- package/dist/types/src/components/UiDataTable/UiDataTable.vue.d.ts +136 -0
- package/dist/types/src/components/UiDataTable/index.d.ts +5 -0
- package/dist/types/src/components/UiDrawer/UiDrawer.vue.d.ts +28 -0
- package/dist/types/src/components/UiDrawer/UiDrawerContent.vue.d.ts +18 -0
- package/dist/types/src/components/UiDrawer/UiDrawerTrigger.vue.d.ts +14 -0
- package/dist/types/src/components/UiDropdown/DropdownItemCheckbox.vue.d.ts +3 -0
- package/dist/types/src/components/UiDropdown/UiDropdownContent.vue.d.ts +10 -0
- package/dist/types/src/components/UiDropdown/UiDropdownGroup.vue.d.ts +17 -0
- package/dist/types/src/components/UiDropdown/UiDropdownItem.vue.d.ts +9 -4
- package/dist/types/src/components/UiDropdown/UiDropdownTrigger.vue.d.ts +21 -5
- package/dist/types/src/components/UiDropdown/index.d.ts +1 -0
- package/dist/types/src/components/UiEmojiPickerList/UiEmojiPickerList.vue.d.ts +22 -0
- package/dist/types/src/components/UiEmptyState/UiEmptyState.vue.d.ts +8 -3
- package/dist/types/src/components/UiFieldWrapper/UiFieldWrapper.vue.d.ts +2 -2
- package/dist/types/src/components/UiInputCheckbox/CheckboxGlyph.vue.d.ts +17 -0
- package/dist/types/src/components/UiInputCheckbox/UiInputCheckbox.vue.d.ts +35 -4
- package/dist/types/src/components/UiInputEmoji/UiInputEmoji.vue.d.ts +45 -0
- package/dist/types/src/components/UiInputRadio/RadioGlyph.vue.d.ts +19 -0
- package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +80 -0
- package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +86 -0
- package/dist/types/src/components/UiInputRadio/types.d.ts +19 -0
- package/dist/types/src/components/UiInputSelect/UiInputSelect.vue.d.ts +5 -5
- package/dist/types/src/components/UiInputTextarea/UiInputTextarea.vue.d.ts +5 -5
- package/dist/types/src/components/UiListItem/UiListItem.vue.d.ts +18 -4
- package/dist/types/src/components/UiLoader/UiLoader.vue.d.ts +6 -5
- package/dist/types/src/components/UiLoader/UiSpinnerIcon.vue.d.ts +3 -0
- package/dist/types/src/components/UiLogo/UiLogo.vue.d.ts +9 -3
- package/dist/types/src/components/UiMenu/UiMenuContent.vue.d.ts +15 -2
- package/dist/types/src/components/UiMenu/UiMenuItem.vue.d.ts +9 -3
- package/dist/types/src/components/UiNestedOptions/UiNestedOptions.vue.d.ts +13 -0
- package/dist/types/src/components/UiPage/UiPage.vue.d.ts +106 -8
- package/dist/types/src/components/UiPagination/UiPagination.vue.d.ts +36 -0
- package/dist/types/src/components/UiPagination/index.d.ts +2 -0
- package/dist/types/src/components/UiPagination/types.d.ts +4 -0
- package/dist/types/src/components/UiPane/UiPane.vue.d.ts +22 -0
- package/dist/types/src/components/UiPane/UiPaneContent.vue.d.ts +75 -0
- package/dist/types/src/components/UiPane/UiPaneTrigger.vue.d.ts +14 -0
- package/dist/types/src/components/UiPane/index.d.ts +5 -0
- package/dist/types/src/components/UiProgress/UiProgress.vue.d.ts +1 -1
- package/dist/types/src/components/UiRating/UiRating.vue.d.ts +62 -0
- package/dist/types/src/components/UiRating/types.d.ts +1 -0
- package/dist/types/src/components/UiRoomButton/UiRoomButton.vue.d.ts +5 -3
- package/dist/types/src/components/UiRoomCountdown/UiRoomCountdown.vue.d.ts +32 -0
- package/dist/types/src/components/UiShell/UiShell.vue.d.ts +3 -0
- package/dist/types/src/components/UiSideMenu/UiSideMenu.vue.d.ts +22 -1
- package/dist/types/src/components/UiSideMenu/UiSideMenuItemContent.vue.d.ts +2 -2
- package/dist/types/src/components/UiSwitch/UiSwitch.vue.d.ts +114 -0
- package/dist/types/src/components/UiSwitch/types.d.ts +3 -0
- package/dist/types/src/components/UiTable/UiTable.vue.d.ts +13 -4
- package/dist/types/src/components/UiTable/UiTableActionsCell.vue.d.ts +49 -0
- package/dist/types/src/components/UiTable/UiTableActionsHeaderCell.vue.d.ts +3 -0
- package/dist/types/src/components/UiTable/UiTableBody.vue.d.ts +11 -1
- package/dist/types/src/components/UiTable/UiTableCell.vue.d.ts +16 -1
- package/dist/types/src/components/UiTable/UiTableHeaderCell.vue.d.ts +31 -2
- package/dist/types/src/components/UiTable/UiTableRow.vue.d.ts +32 -2
- package/dist/types/src/components/UiTable/context.d.ts +3 -0
- package/dist/types/src/components/UiTable/index.d.ts +4 -0
- package/dist/types/src/components/UiTabs/UiTabs.vue.d.ts +8 -2
- package/dist/types/src/components/UiTag/UiTag.types.d.ts +25 -0
- package/dist/types/src/components/UiTag/UiTag.vue.d.ts +5 -26
- package/dist/types/src/components/UiTag/index.d.ts +2 -1
- package/dist/types/src/components/UiTextField/UiTextField.vue.d.ts +10 -5
- package/dist/types/src/components/UiTooltip/UiTooltipProvider.vue.d.ts +1 -0
- package/dist/types/src/components/UiTooltip/UiTooltipTrigger.vue.d.ts +2 -2
- package/dist/types/src/composables/storedThemingApplies.d.ts +17 -0
- package/dist/types/src/composables/useButtonStyles.d.ts +1 -0
- package/dist/types/src/composables/useChoiceboxSurface.d.ts +11 -0
- package/dist/types/src/composables/useContentMode.d.ts +23 -0
- package/dist/types/src/composables/useControlModes.d.ts +7 -0
- package/dist/types/src/composables/useControlShape.d.ts +24 -0
- package/dist/types/src/composables/useFieldFocusEvents.d.ts +12 -0
- package/dist/types/src/composables/useIconSize.d.ts +13 -0
- package/dist/types/src/composables/usePullGesture.d.ts +42 -0
- package/dist/types/src/composables/useThemeAndModes.d.ts +4 -0
- package/dist/types/src/constants/sizes.d.ts +1 -1
- package/dist/types/src/theming/colorScale.d.ts +165 -0
- package/dist/types/src/theming/legacyColorScale.d.ts +27 -0
- package/dist/types/src/theming/presetColors.d.ts +11 -0
- package/dist/types/src/theming/primitiveTokens.d.ts +2 -0
- package/dist/types/src/utils/effectiveOpacity.d.ts +2 -0
- package/dist/types/src/utils/numbers.d.ts +1 -2
- package/dist/types/src/utils/tokenColor.d.ts +2 -0
- package/dist/ui-vue.css +2 -1
- package/dist/utils.mjs +165 -194
- package/index.d.ts +22 -0
- package/index.js +44 -0
- package/package.json +28 -20
- package/src/components/Toast/LayoutToasts.vue +19 -11
- package/src/components/Toast/ToastMessage.vue +130 -73
- package/src/components/Toast/toastDSV2.ts +238 -67
- package/src/components/UiActionBar/UiActionBar.vue +15 -5
- package/src/components/UiActionBar/UiActionBarItem.vue +65 -38
- package/src/components/UiActionBar/types.ts +16 -1
- package/src/components/UiAvatar/UiAvatar.vue +9 -3
- package/src/components/UiAvatarList/UiAvatarList.vue +51 -20
- package/src/components/UiBadge/UiBadge.vue +1 -1
- package/src/components/UiBanner/UiBanner.vue +77 -27
- package/src/components/UiBreadcrumb/UiBreadcrumb.vue +1 -1
- package/src/components/UiButton/UiButton.vue +96 -29
- package/src/components/UiButtonGroup/UiButtonGroup.vue +22 -16
- package/src/components/UiButtonGroup/UiButtonGroupItem.vue +44 -14
- package/src/components/UiButtonGroup/types.ts +20 -1
- package/src/components/UiCard/UiCard.vue +20 -23
- package/src/components/UiCarousel/UiCarousel.vue +2 -2
- package/src/components/UiChoice/UiChoice.vue +187 -0
- package/src/components/UiChoice/index.ts +2 -0
- package/src/components/UiChoice/types.ts +11 -0
- package/src/components/UiColorField/UiColorField.vue +2 -2
- package/src/components/UiColorPicker/UiColorPicker.vue +16 -13
- package/src/components/UiCombobox/UiCombobox.types.ts +52 -0
- package/src/components/UiCombobox/UiCombobox.vue +296 -0
- package/src/components/UiCombobox/index.ts +2 -0
- package/src/components/UiDataTable/UiDataTable.types.ts +47 -0
- package/src/components/UiDataTable/UiDataTable.vue +422 -0
- package/src/components/UiDataTable/index.ts +12 -0
- package/src/components/UiDialog/UiDialogClose.vue +1 -1
- package/src/components/UiDialog/UiDialogContent.vue +22 -7
- package/src/components/UiDrawer/UiDrawer.vue +16 -0
- package/src/components/UiDrawer/UiDrawerContent.vue +42 -0
- package/src/components/UiDrawer/UiDrawerTrigger.vue +17 -0
- package/src/components/UiDropdown/DropdownItemCheckbox.vue +10 -0
- package/src/components/UiDropdown/UiDropdownContent.vue +32 -21
- package/src/components/UiDropdown/UiDropdownGroup.vue +23 -0
- package/src/components/UiDropdown/UiDropdownItem.vue +45 -18
- package/src/components/UiDropdown/UiDropdownSeparator.vue +1 -1
- package/src/components/UiDropdown/UiDropdownTrigger.vue +139 -45
- package/src/components/UiDropdown/index.ts +1 -0
- package/src/components/UiEmojiPickerList/UiEmojiPickerList.vue +122 -0
- package/src/components/UiEmptyState/UiEmptyState.vue +16 -4
- package/src/components/UiFieldWrapper/UiFieldWrapper.vue +10 -4
- package/src/components/UiHorizontalSeparator/UiHorizontalSeparator.vue +2 -2
- package/src/components/UiInputCheckbox/CheckboxGlyph.vue +78 -0
- package/src/components/UiInputCheckbox/UiInputCheckbox.vue +213 -177
- package/src/components/UiInputEmoji/UiInputEmoji.vue +96 -0
- package/src/components/UiInputRadio/RadioGlyph.vue +45 -0
- package/src/components/UiInputRadio/UiInputRadio.vue +382 -0
- package/src/components/UiInputRadio/UiInputRadioGroup.vue +166 -0
- package/src/components/UiInputRadio/types.ts +24 -0
- package/src/components/UiInputSelect/UiInputSelect.vue +9 -7
- package/src/components/UiInputTextarea/UiInputTextarea.vue +20 -35
- package/src/components/UiLabel/UiLabel.vue +1 -1
- package/src/components/UiListItem/UiListItem.vue +55 -9
- package/src/components/UiLoader/UiLoader.vue +11 -29
- package/src/components/UiLoader/UiSpinnerIcon.vue +21 -0
- package/src/components/UiLogo/UiLogo.vue +41 -7
- package/src/components/UiMenu/UiMenuContent.vue +24 -3
- package/src/components/UiMenu/UiMenuGroup.vue +1 -1
- package/src/components/UiMenu/UiMenuItem.vue +14 -3
- package/src/components/UiMenu/UiMenuSubContent.vue +1 -1
- package/src/components/UiMenu/UiMenuSubTrigger.vue +2 -2
- package/src/components/UiNestedOptions/UiNestedOptions.vue +6 -0
- package/src/components/UiPage/UiPage.vue +273 -47
- package/src/components/UiPageHeader/UiPageHeader.vue +29 -9
- package/src/components/UiPagination/UiPagination.vue +99 -0
- package/src/components/UiPagination/index.ts +2 -0
- package/src/components/UiPagination/types.ts +28 -0
- package/src/components/UiPane/UiPane.vue +20 -0
- package/src/components/UiPane/UiPaneContent.vue +232 -0
- package/src/components/UiPane/UiPaneTrigger.vue +14 -0
- package/src/components/UiPane/index.ts +12 -0
- package/src/components/UiPopover/UiPopoverContent.vue +21 -1
- package/src/components/UiProgress/UiProgress.vue +2 -2
- package/src/components/UiRating/UiRating.vue +258 -0
- package/src/components/UiRating/types.ts +1 -0
- package/src/components/UiRoomButton/UiRoomButton.vue +14 -45
- package/src/components/UiRoomCountdown/UiRoomCountdown.vue +376 -0
- package/src/components/UiShell/UiShell.vue +10 -4
- package/src/components/UiSideMenu/UiSideMenu.vue +158 -18
- package/src/components/UiSideMenu/UiSideMenuItemContent.vue +22 -18
- package/src/components/UiStepper/UiStepper.vue +3 -16
- package/src/components/UiStepper/UiStepperDescription.vue +1 -13
- package/src/components/UiStepper/UiStepperIndicator.vue +17 -57
- package/src/components/UiStepper/UiStepperItem.vue +1 -8
- package/src/components/UiStepper/UiStepperSeparator.vue +5 -16
- package/src/components/UiStepper/UiStepperTitle.vue +1 -10
- package/src/components/UiSwitch/UiSwitch.vue +375 -0
- package/src/components/UiSwitch/types.ts +5 -0
- package/src/components/UiTable/UiTable.vue +98 -8
- package/src/components/UiTable/UiTableActionsCell.vue +135 -0
- package/src/components/UiTable/UiTableActionsHeaderCell.vue +19 -0
- package/src/components/UiTable/UiTableBody.vue +16 -1
- package/src/components/UiTable/UiTableCell.vue +33 -16
- package/src/components/UiTable/UiTableHeader.vue +6 -5
- package/src/components/UiTable/UiTableHeaderCell.vue +61 -9
- package/src/components/UiTable/UiTableRow.vue +46 -10
- package/src/components/UiTable/context.ts +5 -0
- package/src/components/UiTable/index.ts +4 -0
- package/src/components/UiTabs/UiTabs.vue +61 -63
- package/src/components/UiTag/UiTag.types.ts +28 -0
- package/src/components/UiTag/UiTag.vue +24 -42
- package/src/components/UiTag/index.ts +2 -0
- package/src/components/UiTextField/UiTextField.vue +27 -47
- package/src/components/UiTimeline/README.md +6 -6
- package/src/components/UiTimeline/UiTimelineItem.vue +3 -3
- package/src/components/UiTooltip/UiTooltipContent.vue +1 -1
- package/src/components/UiTooltip/UiTooltipProvider.vue +3 -0
- package/src/components/UiTooltip/UiTooltipTrigger.vue +16 -1
- package/src/composables/storedThemingApplies.ts +19 -0
- package/src/composables/useButtonStyles.ts +9 -13
- package/src/composables/useChoiceboxSurface.ts +36 -0
- package/src/composables/useContentMode.ts +66 -0
- package/src/composables/useControlModes.ts +11 -0
- package/src/composables/useControlShape.ts +34 -0
- package/src/composables/useFieldFocusEvents.ts +30 -0
- package/src/composables/useIconSize.ts +43 -0
- package/src/composables/usePullGesture.ts +142 -0
- package/src/composables/useThemeAndModes.ts +28 -1
- package/src/constants/sizes.ts +1 -1
- package/src/css/body.css +16 -1
- package/src/css/fonts.css +39 -5
- package/src/global.d.ts +44 -0
- package/src/theming/colorScale.ts +173 -0
- package/src/theming/legacyColorScale.ts +58 -0
- package/src/theming/presetColors.ts +33 -0
- package/src/theming/primitiveTokens.ts +71 -0
- package/src/utils/effectiveOpacity.ts +8 -0
- package/src/utils/i18n.ts +6 -0
- package/src/utils/numbers.ts +1 -1
- package/src/utils/responsive.d.ts +14 -0
- package/src/utils/tokenColor.ts +9 -0
- package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-DJFdCn2R.mjs +0 -5456
- package/dist/a11y-BhF_nl7L.mjs +0 -159
- package/dist/i18n-Cktw9hIu.mjs +0 -49
- package/dist/toastDSV2-wMh-UMzW.mjs +0 -74
- package/dist/types/src/components/UiAvatar/index.d.ts +0 -1
- package/dist/types/src/components/UiAvatarList/index.d.ts +0 -1
- package/dist/types/src/components/UiBadge/index.d.ts +0 -1
- package/dist/types/src/components/UiBanner/index.d.ts +0 -2
- package/dist/types/src/components/UiButtonGroup/index.d.ts +0 -3
- package/dist/types/src/components/UiCarousel/index.d.ts +0 -2
- package/dist/types/src/components/UiColorField/index.d.ts +0 -2
- package/dist/types/src/components/UiDialog/MarketplaceHeaderExample.vue.d.ts +0 -28
- package/dist/types/src/components/UiDialog/index.d.ts +0 -7
- package/dist/types/src/components/UiFieldWrapper/index.d.ts +0 -1
- package/dist/types/src/components/UiInputCheckbox/index.d.ts +0 -2
- package/dist/types/src/components/UiInputSelect/index.d.ts +0 -2
- package/dist/types/src/components/UiInputTextarea/index.d.ts +0 -2
- package/dist/types/src/components/UiMenu/index.d.ts +0 -10
- package/dist/types/src/components/UiProgress/index.d.ts +0 -2
- package/dist/types/src/components/UiTabs/index.d.ts +0 -2
- package/dist/types/src/components/UiTimeline/index.d.ts +0 -2
- package/dist/types/src/components/UiTooltip/index.d.ts +0 -4
- package/dist/types/src/mixins/DynamicAction.d.ts +0 -30
- package/dist/useThemeAndModes-BzYwSOEv.mjs +0 -108
- package/src/components/UiAvatar/index.js +0 -1
- package/src/components/UiAvatarList/index.js +0 -1
- package/src/components/UiBadge/index.ts +0 -2
- package/src/components/UiBanner/index.ts +0 -2
- package/src/components/UiButtonGroup/index.ts +0 -3
- package/src/components/UiCarousel/index.ts +0 -2
- package/src/components/UiColorField/index.ts +0 -2
- package/src/components/UiDialog/MarketplaceHeaderExample.vue +0 -217
- package/src/components/UiDialog/index.ts +0 -18
- package/src/components/UiFieldWrapper/index.ts +0 -1
- package/src/components/UiInputCheckbox/index.ts +0 -2
- package/src/components/UiInputSelect/index.ts +0 -2
- package/src/components/UiInputTextarea/index.ts +0 -2
- package/src/components/UiMenu/index.ts +0 -10
- package/src/components/UiProgress/index.ts +0 -2
- package/src/components/UiTabs/index.ts +0 -5
- package/src/components/UiTag/index.js +0 -1
- package/src/components/UiTimeline/index.ts +0 -2
- package/src/components/UiTooltip/index.js +0 -4
- package/src/mixins/DynamicAction.js +0 -61
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { ref } from 'vue'
|
|
2
|
-
import type { Component } from 'vue'
|
|
1
|
+
import { markRaw, ref } from 'vue'
|
|
2
|
+
import type { Component, Ref } from 'vue'
|
|
3
3
|
import type { RouteLocationRaw } from 'vue-router'
|
|
4
4
|
import { createEventHook, type EventHook } from '@vueuse/core'
|
|
5
5
|
|
|
6
6
|
import type { ThemeColorType } from '../../theme'
|
|
7
|
+
import type { Size } from '../../constants'
|
|
7
8
|
|
|
8
9
|
let uid = 1
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* How many toasts render at once. It lives here rather than in `LayoutToasts`
|
|
13
|
+
* because the store schedules the close timers, and only the toasts actually
|
|
14
|
+
* on screen may run one.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_ITEMS_DISPLAYED = 3
|
|
17
|
+
|
|
10
18
|
export type ToastType = 'alert'|'warning'|'info'|'success'
|
|
11
19
|
|
|
12
20
|
const DEFAULT_TIMER_DURATION_PER_TYPE: Record<Extract<ToastType, 'success'|'info'>, number> = {
|
|
@@ -14,14 +22,39 @@ const DEFAULT_TIMER_DURATION_PER_TYPE: Record<Extract<ToastType, 'success'|'info
|
|
|
14
22
|
info: 7 * 1000,
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Identity shown in place of the status icon. Size and variant are fixed by
|
|
27
|
+
* the design, so only the identity-bearing `UiAvatar` props are exposed.
|
|
28
|
+
*/
|
|
29
|
+
export interface ToastAvatar {
|
|
30
|
+
src?: string | null
|
|
31
|
+
firstName?: string | null
|
|
32
|
+
lastName?: string | null
|
|
33
|
+
customText?: string | null
|
|
34
|
+
color?: string | null
|
|
35
|
+
alt?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Every field a toast understands, all optional. `openToast` narrows this to
|
|
40
|
+
* `ToastOptions`, which additionally demands something to display.
|
|
41
|
+
*/
|
|
42
|
+
export interface ToastBaseOptions {
|
|
43
|
+
message?: string
|
|
44
|
+
/**
|
|
45
|
+
* Rendered in place of `message`, inside the default layout — `component`
|
|
46
|
+
* replaces the whole body instead. Markup, and sanitizing it, stays with
|
|
47
|
+
* the caller.
|
|
48
|
+
*/
|
|
49
|
+
messageComponent?: Component
|
|
50
|
+
messageComponentProps?: Record<string, unknown>
|
|
19
51
|
id?: PropertyKey
|
|
20
52
|
title?: string
|
|
21
53
|
info?: string
|
|
22
54
|
type?: ThemeColorType
|
|
55
|
+
avatar?: ToastAvatar
|
|
23
56
|
time?: number | boolean // if time is explicitly false, the toast will not auto-close
|
|
24
|
-
|
|
57
|
+
progressIndeterminate?: boolean
|
|
25
58
|
customIcon?: string
|
|
26
59
|
confirmLabel?: string
|
|
27
60
|
confirmHref?: string
|
|
@@ -29,6 +62,7 @@ export interface ToastOptions {
|
|
|
29
62
|
confirmTo?: RouteLocationRaw
|
|
30
63
|
confirmIcon?: string
|
|
31
64
|
confirmColor?: ThemeColorType
|
|
65
|
+
actionsSize?: Size
|
|
32
66
|
cancelLabel?: string
|
|
33
67
|
locked?: boolean
|
|
34
68
|
closable?: boolean
|
|
@@ -36,116 +70,253 @@ export interface ToastOptions {
|
|
|
36
70
|
componentProps?: Record<string, unknown>
|
|
37
71
|
}
|
|
38
72
|
|
|
73
|
+
/**
|
|
74
|
+
* A toast has to show something. Requiring one of the three content fields
|
|
75
|
+
* here means an empty toast is a type error rather than an empty white box.
|
|
76
|
+
*/
|
|
77
|
+
export type ToastContent =
|
|
78
|
+
| { message: string }
|
|
79
|
+
| { messageComponent: Component }
|
|
80
|
+
| { title: string }
|
|
81
|
+
| { component: Component }
|
|
82
|
+
|
|
83
|
+
export type ToastOptions = ToastBaseOptions & ToastContent
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The caller's options as the store keeps them: `time` has been resolved to
|
|
87
|
+
* milliseconds, so nothing downstream has to handle the `true` shorthand.
|
|
88
|
+
*/
|
|
89
|
+
type StoredToastOptions = Omit<ToastBaseOptions, 'time'> & { time?: number }
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A toast on screen: what the caller asked for, kept in `options`, plus the
|
|
93
|
+
* bookkeeping the store needs alongside it. They stay apart so `LayoutToasts`
|
|
94
|
+
* can hand the component the options without the timer handle and the event
|
|
95
|
+
* hooks falling through onto the DOM.
|
|
96
|
+
*/
|
|
39
97
|
interface ToastItem {
|
|
40
98
|
id: PropertyKey
|
|
41
|
-
|
|
42
|
-
title?: string
|
|
43
|
-
info?: string
|
|
44
|
-
type?: ThemeColorType
|
|
45
|
-
icon?: string
|
|
46
|
-
time?: number
|
|
47
|
-
locked?: boolean
|
|
48
|
-
progessIndeterminate?: boolean
|
|
99
|
+
options: StoredToastOptions
|
|
49
100
|
closeTimer: any
|
|
101
|
+
// The pointer is resting on this toast, so it must not be given a timer
|
|
102
|
+
// back when the visible window is recomputed.
|
|
103
|
+
paused: boolean
|
|
104
|
+
// When the current run of the close timer started. The progress bar animation
|
|
105
|
+
// is component state, so without this it has no way to observe a restart.
|
|
106
|
+
timerStartedAt: number
|
|
50
107
|
confirmEventHook: EventHook
|
|
51
108
|
cancelEventHook: EventHook
|
|
52
109
|
}
|
|
53
110
|
|
|
54
|
-
|
|
111
|
+
/**
|
|
112
|
+
* `true` picks the per-type default; a number is used as-is; `false` (or
|
|
113
|
+
* unset) means no auto-close — normalized to undefined so the timer is never
|
|
114
|
+
* started, and so nothing downstream has to divide a boolean by 1000.
|
|
115
|
+
*/
|
|
116
|
+
function resolveTime(time: ToastBaseOptions['time'], type: ThemeColorType): number | undefined {
|
|
117
|
+
const typeWithTimer = type === 'success' || type === 'info'
|
|
118
|
+
|
|
119
|
+
return time === true
|
|
120
|
+
? (typeWithTimer ? DEFAULT_TIMER_DURATION_PER_TYPE[type] : undefined)
|
|
121
|
+
: (typeof time === 'number' ? time : undefined)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Cast past `UnwrapRef`, which mangles the `Component` in `options` into a
|
|
125
|
+
// shape that no longer matches ToastItem. Nothing here holds a nested ref, so
|
|
126
|
+
// the unwrapped type and the declared one are the same at runtime.
|
|
127
|
+
const toasts = ref<ToastItem[]>([]) as Ref<ToastItem[]>
|
|
55
128
|
|
|
56
|
-
export function useToasts
|
|
129
|
+
export function useToasts() {
|
|
57
130
|
return {
|
|
58
131
|
toasts,
|
|
59
|
-
closeToast,
|
|
60
132
|
openToast,
|
|
133
|
+
updateToast,
|
|
134
|
+
restartToast,
|
|
135
|
+
closeToast,
|
|
61
136
|
cancelCloseTimer,
|
|
62
137
|
}
|
|
63
138
|
}
|
|
64
139
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
id = item.id
|
|
73
|
-
Object.assign(item, {
|
|
74
|
-
...options,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const restartTimer = options.id && Object.keys(options).length === 1
|
|
78
|
-
if (restartTimer) {
|
|
79
|
-
startTimer(item)
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
id = options.id ?? uid++
|
|
83
|
-
|
|
84
|
-
const type = options.type || 'info'
|
|
85
|
-
const typeWithTimer = type === 'success' || type === 'info'
|
|
86
|
-
// `true` picks the per-type default; a number is used as-is; `false`
|
|
87
|
-
// (or unset) means no auto-close — normalized to undefined so the timer
|
|
88
|
-
// is never started.
|
|
89
|
-
const time = options.time === true
|
|
90
|
-
? (typeWithTimer ? DEFAULT_TIMER_DURATION_PER_TYPE[type] : undefined)
|
|
91
|
-
: (typeof options.time === 'number' ? options.time : undefined)
|
|
92
|
-
item = {
|
|
93
|
-
...options,
|
|
94
|
-
id,
|
|
95
|
-
closeTimer: null,
|
|
96
|
-
confirmEventHook,
|
|
97
|
-
cancelEventHook,
|
|
98
|
-
time,
|
|
99
|
-
type,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
toasts.value.push(item)
|
|
140
|
+
/** What a caller gets back for the toast it just opened or patched. */
|
|
141
|
+
interface ToastHandle {
|
|
142
|
+
id: PropertyKey
|
|
143
|
+
close: () => void
|
|
144
|
+
onConfirm: EventHook['on']
|
|
145
|
+
onCancel: EventHook['on']
|
|
146
|
+
}
|
|
103
147
|
|
|
104
|
-
|
|
105
|
-
}
|
|
148
|
+
function toastHandle(item: ToastItem): ToastHandle {
|
|
149
|
+
const { id } = item
|
|
106
150
|
|
|
107
151
|
return {
|
|
108
152
|
id,
|
|
109
153
|
close: () => {
|
|
110
154
|
closeToast(id)
|
|
111
155
|
},
|
|
112
|
-
|
|
113
|
-
|
|
156
|
+
// Read off the item, so a patched toast hands back the hooks it was
|
|
157
|
+
// opened with rather than a fresh pair nothing would ever trigger.
|
|
158
|
+
onConfirm: item.confirmEventHook.on,
|
|
159
|
+
onCancel: item.cancelEventHook.on,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The store is a deep ref, so a component definition kept in it would be
|
|
165
|
+
* proxied on every render — Vue warns about exactly that.
|
|
166
|
+
*/
|
|
167
|
+
function withRawComponents<T extends ToastBaseOptions>(options: T): T {
|
|
168
|
+
const { component, messageComponent } = options
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
...options,
|
|
172
|
+
...(component && { component: markRaw(component) }),
|
|
173
|
+
...(messageComponent && { messageComponent: markRaw(messageComponent) }),
|
|
114
174
|
}
|
|
115
175
|
}
|
|
116
176
|
|
|
117
|
-
|
|
177
|
+
function applyPatch(item: ToastItem, patch: Omit<ToastBaseOptions, 'id'>) {
|
|
178
|
+
const { time, ...rest } = patch
|
|
179
|
+
Object.assign(item.options, withRawComponents(rest))
|
|
180
|
+
|
|
181
|
+
// Patching goes through the same normalization as opening, so the store only
|
|
182
|
+
// ever holds milliseconds and the progress bar never divides a boolean.
|
|
183
|
+
if (time !== undefined) {
|
|
184
|
+
item.options.time = resolveTime(time, item.options.type ?? 'info')
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Opens a toast. An `id` is an identity, so opening one that is already on
|
|
190
|
+
* screen patches it in place instead of stacking a second copy — that is what
|
|
191
|
+
* the room's per-user and per-link toasts rely on. It does not restart the
|
|
192
|
+
* close timer; call `restartToast` for that.
|
|
193
|
+
*/
|
|
194
|
+
export function openToast(options: ToastOptions): ToastHandle {
|
|
195
|
+
const open = toasts.value.find(item => item.id === options.id)
|
|
196
|
+
if (open) {
|
|
197
|
+
// Re-opening is a fresh occurrence of the same identity, and the caller is
|
|
198
|
+
// about to bind the handlers for it. Without dropping the previous ones,
|
|
199
|
+
// every re-open leaves another subscriber on hooks nothing ever clears.
|
|
200
|
+
open.confirmEventHook.clear()
|
|
201
|
+
open.cancelEventHook.clear()
|
|
202
|
+
applyPatch(open, options)
|
|
203
|
+
|
|
204
|
+
return toastHandle(open)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const type = options.type || 'info'
|
|
208
|
+
const id = options.id ?? uid++
|
|
209
|
+
const item: ToastItem = {
|
|
210
|
+
id,
|
|
211
|
+
options: {
|
|
212
|
+
...withRawComponents(options),
|
|
213
|
+
id,
|
|
214
|
+
type,
|
|
215
|
+
time: resolveTime(options.time, type),
|
|
216
|
+
},
|
|
217
|
+
closeTimer: null,
|
|
218
|
+
paused: false,
|
|
219
|
+
timerStartedAt: 0,
|
|
220
|
+
// Created with the toast, not on every call, so the hooks outlive a patch.
|
|
221
|
+
confirmEventHook: createEventHook<void>(),
|
|
222
|
+
cancelEventHook: createEventHook<void>(),
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
toasts.value.push(item)
|
|
226
|
+
syncTimers()
|
|
227
|
+
|
|
228
|
+
return toastHandle(item)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Patches an open toast. Unlike `openToast` it needs no content, so a caller
|
|
233
|
+
* can change one field. Returns undefined if the toast has already closed.
|
|
234
|
+
*/
|
|
235
|
+
export function updateToast(id: PropertyKey, patch: Omit<ToastBaseOptions, 'id'>): ToastHandle | undefined {
|
|
236
|
+
const item = toasts.value.find(item => item.id === id)
|
|
237
|
+
if (!item) return
|
|
238
|
+
|
|
239
|
+
applyPatch(item, patch)
|
|
240
|
+
|
|
241
|
+
return toastHandle(item)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Runs the close timer again from zero, e.g. when the pointer leaves. */
|
|
245
|
+
export function restartToast(id: PropertyKey) {
|
|
246
|
+
const item = toasts.value.find(item => item.id === id)
|
|
247
|
+
if (!item) return
|
|
248
|
+
|
|
249
|
+
item.paused = false
|
|
250
|
+
startTimer(item)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function closeToast(id: PropertyKey) {
|
|
118
254
|
const item = toasts.value.find(item => item.id === id)
|
|
119
255
|
if (item) {
|
|
120
|
-
|
|
256
|
+
stopTimer(item)
|
|
121
257
|
removeToast(id)
|
|
258
|
+
// Closing one frees a slot, so whatever was queued behind it can start
|
|
259
|
+
// counting down now that it is on screen.
|
|
260
|
+
syncTimers()
|
|
122
261
|
}
|
|
123
262
|
}
|
|
124
263
|
|
|
125
|
-
export function cancelCloseTimer
|
|
264
|
+
export function cancelCloseTimer(id: PropertyKey) {
|
|
126
265
|
const item = toasts.value.find(item => item.id === id)
|
|
127
266
|
if (item) {
|
|
128
|
-
|
|
267
|
+
item.paused = true
|
|
268
|
+
stopTimer(item)
|
|
129
269
|
}
|
|
130
270
|
}
|
|
131
271
|
|
|
132
|
-
export function resetToasts
|
|
272
|
+
export function resetToasts() {
|
|
273
|
+
toasts.value.forEach(stopTimer)
|
|
133
274
|
toasts.value = []
|
|
134
275
|
}
|
|
135
276
|
|
|
136
|
-
function removeToast
|
|
277
|
+
function removeToast(id: PropertyKey) {
|
|
137
278
|
const index = toasts.value.findIndex(item => item.id === id)
|
|
138
279
|
if (index !== -1) {
|
|
139
280
|
toasts.value.splice(index, 1)
|
|
140
281
|
}
|
|
141
282
|
}
|
|
142
283
|
|
|
143
|
-
|
|
144
|
-
|
|
284
|
+
/**
|
|
285
|
+
* Gives a close timer to every toast on screen and takes it away from the ones
|
|
286
|
+
* queued behind them. Without this a queued toast counts down while nothing is
|
|
287
|
+
* rendered and can be removed before it is ever seen.
|
|
288
|
+
*/
|
|
289
|
+
function syncTimers(): void {
|
|
290
|
+
toasts.value.forEach((item, index) => {
|
|
291
|
+
const onScreen = index < MAX_ITEMS_DISPLAYED
|
|
292
|
+
|
|
293
|
+
if (!onScreen || item.paused) {
|
|
294
|
+
stopTimer(item)
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Guarded so resettling the window never restarts a countdown that is
|
|
299
|
+
// already running, which would also replay the progress bar.
|
|
300
|
+
if (!item.closeTimer) startTimer(item)
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function stopTimer(item: ToastItem): void {
|
|
305
|
+
clearTimeout(item.closeTimer)
|
|
306
|
+
item.closeTimer = null
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function startTimer(item: ToastItem): void {
|
|
310
|
+
// Idempotent: without this a restart would orphan the previous timeout,
|
|
311
|
+
// which would go on to close the toast early.
|
|
312
|
+
stopTimer(item)
|
|
313
|
+
|
|
314
|
+
if (item.options.time) {
|
|
315
|
+
item.timerStartedAt = performance.now()
|
|
145
316
|
item.closeTimer = setTimeout(
|
|
146
317
|
() => {
|
|
147
318
|
closeToast(item.id)
|
|
148
|
-
}, item.time
|
|
319
|
+
}, item.options.time
|
|
149
320
|
)
|
|
150
321
|
}
|
|
151
322
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, provide } from 'vue'
|
|
3
|
-
import type { ActionBarOrientation, ActionBarPosition, ActionBarSize } from './types'
|
|
4
|
-
import { ACTION_BAR_CONTEXT_KEY } from './types'
|
|
3
|
+
import type { ActionBarOrientation, ActionBarPosition, ActionBarSize, ActionBarTooltipSide } from './types'
|
|
4
|
+
import { ACTION_BAR_CONTEXT_KEY, ACTION_BAR_RAIL_RADIUS } from './types'
|
|
5
5
|
|
|
6
6
|
interface UiActionBarProps {
|
|
7
7
|
/**
|
|
@@ -26,6 +26,7 @@ interface UiActionBarProps {
|
|
|
26
26
|
* Accessible label for the toolbar landmark. Defaults to "Actions".
|
|
27
27
|
*/
|
|
28
28
|
ariaLabel?: string
|
|
29
|
+
tooltipSide?: ActionBarTooltipSide
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const props = withDefaults(defineProps<UiActionBarProps>(), {
|
|
@@ -34,17 +35,26 @@ const props = withDefaults(defineProps<UiActionBarProps>(), {
|
|
|
34
35
|
position: undefined,
|
|
35
36
|
offset: 3,
|
|
36
37
|
ariaLabel: 'Actions',
|
|
38
|
+
tooltipSide: undefined,
|
|
37
39
|
})
|
|
38
40
|
|
|
39
41
|
provide(ACTION_BAR_CONTEXT_KEY, {
|
|
40
42
|
orientation: computed<ActionBarOrientation>(() => props.orientation),
|
|
41
43
|
size: computed<ActionBarSize>(() => props.size),
|
|
44
|
+
tooltipSide: computed<ActionBarTooltipSide | undefined>(() => props.tooltipSide),
|
|
42
45
|
})
|
|
43
46
|
|
|
44
47
|
const isVertical = computed(() => props.orientation === 'vertical')
|
|
45
48
|
|
|
46
49
|
// Inner padding follows the design tokens (space-0-5 = 2px).
|
|
47
|
-
const
|
|
50
|
+
const SIZE_CLASSES: Record<ActionBarSize, string> = {
|
|
51
|
+
xs: 'p-0.5 gap-0.5',
|
|
52
|
+
sm: 'p-0.5 gap-0.5',
|
|
53
|
+
md: 'p-1 gap-0.5',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sizeClasses = computed(() => SIZE_CLASSES[props.size])
|
|
57
|
+
const railRadiusClass = computed(() => ACTION_BAR_RAIL_RADIUS[props.size])
|
|
48
58
|
|
|
49
59
|
// When `position` is set, anchor the bar to an edge of its positioned
|
|
50
60
|
// ancestor. Offsets are inline styles (rem) so any `offset` value works
|
|
@@ -77,8 +87,8 @@ const positionStyle = computed<Record<string, string>>(() => {
|
|
|
77
87
|
:aria-label="ariaLabel"
|
|
78
88
|
:aria-orientation="orientation"
|
|
79
89
|
data-testid="ui-action-bar"
|
|
80
|
-
class="inline-flex items-center bg-surface-neutral-bg-main
|
|
81
|
-
:class="[sizeClasses, { 'flex-col': isVertical, 'absolute z-10': position }]"
|
|
90
|
+
class="inline-flex items-center bg-surface-neutral-bg-main border border-container-border shadow-lg"
|
|
91
|
+
:class="[sizeClasses, railRadiusClass, { 'flex-col': isVertical, 'absolute z-10': position }]"
|
|
82
92
|
:style="positionStyle"
|
|
83
93
|
>
|
|
84
94
|
<slot />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, inject } from 'vue'
|
|
2
|
+
import { computed, inject, useAttrs } from 'vue'
|
|
3
3
|
import {
|
|
4
4
|
TooltipProvider,
|
|
5
5
|
TooltipRoot,
|
|
@@ -9,16 +9,21 @@ import {
|
|
|
9
9
|
TooltipArrow,
|
|
10
10
|
} from 'reka-ui'
|
|
11
11
|
import type { RouteLocationRaw } from 'vue-router'
|
|
12
|
-
import {
|
|
12
|
+
import { useIconSize } from '../../composables/useIconSize'
|
|
13
|
+
import type { ActionBarSize } from './types'
|
|
14
|
+
import { ACTION_BAR_CONTEXT_KEY, ACTION_BAR_ITEM_RADIUS } from './types'
|
|
13
15
|
|
|
14
16
|
interface UiActionBarItemProps {
|
|
15
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Icon class, e.g. `i-ph-trash`. Ignored when the `icon` slot is filled —
|
|
19
|
+
* use the slot for glyphs no icon class covers (emoji, an avatar, a dot).
|
|
20
|
+
*/
|
|
16
21
|
icon?: string
|
|
17
22
|
/** Visible/accessible label. Required so icon-only items stay accessible. */
|
|
18
23
|
label: string
|
|
19
24
|
/**
|
|
20
25
|
* Optional icon class rendered next to the label inside the hover tooltip —
|
|
21
|
-
* e.g. `i-
|
|
26
|
+
* e.g. `i-ph-arrow-square-out` to flag that the action opens in a new tab.
|
|
22
27
|
* Only shown for icon-only items (the tooltip is suppressed when
|
|
23
28
|
* `showLabel` is set).
|
|
24
29
|
*/
|
|
@@ -55,13 +60,20 @@ const props = withDefaults(defineProps<UiActionBarItemProps>(), {
|
|
|
55
60
|
|
|
56
61
|
const emit = defineEmits<{ click: [event: MouseEvent] }>()
|
|
57
62
|
|
|
63
|
+
// The trigger is rendered through `TooltipTrigger as-child`, which merges its
|
|
64
|
+
// own props over the child vnode — so normal attribute fallthrough never
|
|
65
|
+
// reaches the button. Read the consumer's `data-testid` explicitly, otherwise
|
|
66
|
+
// every item in a wrapped bar would share one indistinguishable test id.
|
|
67
|
+
const attrs = useAttrs()
|
|
68
|
+
const testId = computed(() => (attrs['data-testid'] as string | undefined) ?? 'ui-action-bar-item')
|
|
69
|
+
|
|
58
70
|
const ctx = inject(ACTION_BAR_CONTEXT_KEY, null)
|
|
59
71
|
const size = computed(() => ctx?.size.value ?? 'md')
|
|
60
72
|
const orientation = computed(() => ctx?.orientation.value ?? 'horizontal')
|
|
61
73
|
|
|
62
74
|
// Icon-only items get a tooltip; labeled items don't need one.
|
|
63
75
|
const showTooltip = computed(() => !props.showLabel)
|
|
64
|
-
const tooltipSide = computed(() => (orientation.value === 'vertical' ? 'right' : 'bottom'))
|
|
76
|
+
const tooltipSide = computed(() => ctx?.tooltipSide.value ?? (orientation.value === 'vertical' ? 'right' : 'bottom'))
|
|
65
77
|
|
|
66
78
|
const component = computed(() => {
|
|
67
79
|
if (props.disabled) return { is: 'button', props: { type: 'button', disabled: true } }
|
|
@@ -70,22 +82,33 @@ const component = computed(() => {
|
|
|
70
82
|
return { is: 'button', props: { type: 'button' } }
|
|
71
83
|
})
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
85
|
+
// Per-size layout classes. `square` sizes icon-only items; labeled items use
|
|
86
|
+
// `padded` and grow with their text instead of staying square. xs reuses the
|
|
87
|
+
// sm text style — the design system defines no xs one.
|
|
88
|
+
const SIZE_CLASSES: Record<ActionBarSize, { square: string, padded: string, text: string }> = {
|
|
89
|
+
xs: { square: 'size-6', padded: 'h-6 px-2 gap-1', text: 'text-action-button-sm' },
|
|
90
|
+
sm: { square: 'size-8', padded: 'h-8 px-2.5 gap-1', text: 'text-action-button-sm' },
|
|
91
|
+
md: { square: 'size-10', padded: 'h-10 px-3 gap-1.5', text: 'text-action-button-md' },
|
|
92
|
+
}
|
|
79
93
|
|
|
80
|
-
const
|
|
81
|
-
const
|
|
94
|
+
const sizeConfig = computed(() => SIZE_CLASSES[size.value])
|
|
95
|
+
const iconSizeClass = useIconSize(size)
|
|
82
96
|
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
// Everything bound on the item root: per-size layout and text classes, plus
|
|
98
|
+
// the persistent pressed background when the item is an active toggle (the
|
|
99
|
+
// dedicated neutral "active" token keeps an active-but-not-hovered toggle
|
|
100
|
+
// visually distinct from a merely hovered item).
|
|
101
|
+
const itemClasses = computed(() => [
|
|
102
|
+
props.showLabel ? sizeConfig.value.padded : sizeConfig.value.square,
|
|
103
|
+
sizeConfig.value.text,
|
|
104
|
+
ACTION_BAR_ITEM_RADIUS[size.value],
|
|
105
|
+
// Labeled items in a vertical rail stretch so their icons align.
|
|
106
|
+
props.showLabel && orientation.value === 'vertical' ? 'w-full justify-start' : 'justify-center',
|
|
107
|
+
{
|
|
108
|
+
'bg-actions-neutral-active-overlay': props.active === true,
|
|
109
|
+
'cursor-pointer': !props.disabled,
|
|
110
|
+
},
|
|
111
|
+
])
|
|
89
112
|
|
|
90
113
|
function onClick (event: MouseEvent) {
|
|
91
114
|
if (props.disabled) {
|
|
@@ -113,20 +136,22 @@ function onClick (event: MouseEvent) {
|
|
|
113
136
|
v-bind="component.props"
|
|
114
137
|
:aria-label="label"
|
|
115
138
|
:aria-pressed="active"
|
|
116
|
-
data-testid="
|
|
117
|
-
class="group inline-flex items-center
|
|
139
|
+
:data-testid="testId"
|
|
140
|
+
class="group inline-flex items-center shrink-0 outline-none transition-colors
|
|
118
141
|
text-actions-neutral-idle
|
|
119
142
|
hover:bg-actions-neutral-hover-overlay
|
|
120
|
-
focus-visible:
|
|
143
|
+
focus-visible:focus-ring
|
|
121
144
|
disabled:opacity-50 disabled:cursor-not-allowed"
|
|
122
|
-
:class="
|
|
145
|
+
:class="itemClasses"
|
|
123
146
|
@click="onClick"
|
|
124
147
|
>
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
<slot name="icon">
|
|
149
|
+
<span
|
|
150
|
+
v-if="icon"
|
|
151
|
+
:class="[icon, iconSizeClass]"
|
|
152
|
+
class="shrink-0"
|
|
153
|
+
/>
|
|
154
|
+
</slot>
|
|
130
155
|
</component>
|
|
131
156
|
</TooltipTrigger>
|
|
132
157
|
<TooltipPortal>
|
|
@@ -134,7 +159,7 @@ function onClick (event: MouseEvent) {
|
|
|
134
159
|
:side="tooltipSide"
|
|
135
160
|
:side-offset="8"
|
|
136
161
|
data-colors-semantic="light"
|
|
137
|
-
class="inline-flex items-center justify-center gap-1.5 py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-
|
|
162
|
+
class="inline-flex items-center justify-center gap-1.5 py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-row text-center z-overlay shadow-shadows-drop-shadow-lg
|
|
138
163
|
data-[state=delayed-open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=delayed-open]:zoom-in-95"
|
|
139
164
|
>
|
|
140
165
|
{{ label }}
|
|
@@ -160,20 +185,22 @@ function onClick (event: MouseEvent) {
|
|
|
160
185
|
v-bind="component.props"
|
|
161
186
|
:aria-label="label"
|
|
162
187
|
:aria-pressed="active"
|
|
163
|
-
data-testid="
|
|
164
|
-
class="group inline-flex items-center
|
|
188
|
+
:data-testid="testId"
|
|
189
|
+
class="group inline-flex items-center shrink-0 outline-none transition-colors
|
|
165
190
|
text-actions-neutral-idle
|
|
166
191
|
hover:bg-actions-neutral-hover-overlay
|
|
167
|
-
focus-visible:
|
|
192
|
+
focus-visible:focus-ring
|
|
168
193
|
disabled:opacity-50 disabled:cursor-not-allowed"
|
|
169
|
-
:class="
|
|
194
|
+
:class="itemClasses"
|
|
170
195
|
@click="onClick"
|
|
171
196
|
>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
197
|
+
<slot name="icon">
|
|
198
|
+
<span
|
|
199
|
+
v-if="icon"
|
|
200
|
+
:class="[icon, iconSizeClass]"
|
|
201
|
+
class="shrink-0"
|
|
202
|
+
/>
|
|
203
|
+
</slot>
|
|
177
204
|
<span class="whitespace-nowrap">{{ label }}</span>
|
|
178
205
|
</component>
|
|
179
206
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComputedRef, InjectionKey } from 'vue'
|
|
2
2
|
|
|
3
3
|
export type ActionBarOrientation = 'horizontal' | 'vertical'
|
|
4
|
-
export type ActionBarSize = 'sm' | 'md'
|
|
4
|
+
export type ActionBarSize = 'xs' | 'sm' | 'md'
|
|
5
5
|
export type ActionBarPosition =
|
|
6
6
|
| 'top-left'
|
|
7
7
|
| 'top-right'
|
|
@@ -9,10 +9,12 @@ export type ActionBarPosition =
|
|
|
9
9
|
| 'center-right'
|
|
10
10
|
| 'bottom-left'
|
|
11
11
|
| 'bottom-right'
|
|
12
|
+
export type ActionBarTooltipSide = 'top' | 'right' | 'bottom' | 'left'
|
|
12
13
|
|
|
13
14
|
export interface ActionBarContext {
|
|
14
15
|
orientation: ComputedRef<ActionBarOrientation>
|
|
15
16
|
size: ComputedRef<ActionBarSize>
|
|
17
|
+
tooltipSide: ComputedRef<ActionBarTooltipSide | undefined>
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -22,3 +24,16 @@ export interface ActionBarContext {
|
|
|
22
24
|
*/
|
|
23
25
|
export const ACTION_BAR_CONTEXT_KEY: InjectionKey<ActionBarContext> =
|
|
24
26
|
Symbol('ui-action-bar-context')
|
|
27
|
+
|
|
28
|
+
export const ACTION_BAR_RAIL_RADIUS: Record<ActionBarSize, string> = {
|
|
29
|
+
xs: 'rounded-button-sm',
|
|
30
|
+
sm: 'rounded-button-sm',
|
|
31
|
+
md: 'rounded-field-lg',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Item radius stays concentric with the rail: rail radius minus rail padding and border.
|
|
35
|
+
export const ACTION_BAR_ITEM_RADIUS: Record<ActionBarSize, string> = {
|
|
36
|
+
xs: 'rounded-[calc(var(--radius-button-sm)_-_3px)]',
|
|
37
|
+
sm: 'rounded-[calc(var(--radius-button-sm)_-_3px)]',
|
|
38
|
+
md: 'rounded-[calc(var(--radius-field-lg)_-_5px)]',
|
|
39
|
+
}
|