@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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { SideMenuItemProps } from './types'
|
|
3
3
|
import { computed, useSlots } from 'vue'
|
|
4
4
|
import { useForwardExpose } from 'reka-ui'
|
|
5
|
+
import UiBadge from '../UiBadge/UiBadge.vue'
|
|
5
6
|
|
|
6
7
|
// Visual body of a side menu item (icon, label, badge, interactive overlay).
|
|
7
8
|
// Extracted from UiSideMenuItem so the latter can optionally wrap this in a
|
|
@@ -41,12 +42,17 @@ const props = withDefaults(defineProps<SideMenuItemProps & {
|
|
|
41
42
|
alwaysShowLabel: false,
|
|
42
43
|
})
|
|
43
44
|
|
|
44
|
-
// Normalise the badge into either a display string (count pill) or null (dot)
|
|
45
|
-
// when shown. `showBadge` gates rendering so 0/false stay hidden.
|
|
46
45
|
const showBadge = computed(() => props.badge === true || (typeof props.badge === 'number' && props.badge > 0))
|
|
47
|
-
const badgeCount = computed(() =>
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const badgeCount = computed(() => typeof props.badge === 'number' ? props.badge : undefined)
|
|
47
|
+
|
|
48
|
+
// An icon sits inside the item's 10px padding, whereas a 40px avatar fills the
|
|
49
|
+
// box on its own: the avatar variant needs offsets 10px smaller to land the
|
|
50
|
+
// badge on the same corner of the item.
|
|
51
|
+
const badgePositionClass = computed(() => {
|
|
52
|
+
if (badgeCount.value !== undefined) {
|
|
53
|
+
return hasAvatarSlot.value ? '-top-1 -right-1' : '-top-3.5 -right-3.5'
|
|
54
|
+
}
|
|
55
|
+
return hasAvatarSlot.value ? 'top-0 right-0' : '-top-2.5 -right-2.5'
|
|
50
56
|
})
|
|
51
57
|
|
|
52
58
|
const textAlignClass = computed(() => {
|
|
@@ -88,7 +94,7 @@ const textAlignClass = computed(() => {
|
|
|
88
94
|
<button
|
|
89
95
|
v-else
|
|
90
96
|
type="button"
|
|
91
|
-
class="block w-full h-full absolute inset-0 z-20 transition-all duration-150 cursor-pointer focus:outline-none focus-visible:
|
|
97
|
+
class="block w-full h-full absolute inset-0 z-20 transition-all duration-150 cursor-pointer focus:outline-none focus-visible:focus-ring rounded-xl"
|
|
92
98
|
:class="{ 'pointer-events-none': insideInteractive }"
|
|
93
99
|
:tabindex="insideInteractive ? -1 : undefined"
|
|
94
100
|
:aria-hidden="insideInteractive ? 'true' : undefined"
|
|
@@ -103,7 +109,7 @@ const textAlignClass = computed(() => {
|
|
|
103
109
|
isActive ? 'bg-actions-neutral-active-overlay text-actions-neutral-idle' : 'text-actions-neutral-idle-alpha-strong',
|
|
104
110
|
// Avatar box is 40px; on mobile `p-1` makes its total height (48px)
|
|
105
111
|
// match icon items (`p-3` + 24px icon). Both are 40px on desktop.
|
|
106
|
-
hasAvatarSlot ? 'p-1 md:p-0' : 'p-
|
|
112
|
+
hasAvatarSlot ? 'p-1 md:p-0' : 'p-2.5',
|
|
107
113
|
]"
|
|
108
114
|
>
|
|
109
115
|
<!-- Icon/avatar wrapper: anchors the notification badge to the icon's
|
|
@@ -119,23 +125,21 @@ const textAlignClass = computed(() => {
|
|
|
119
125
|
<div
|
|
120
126
|
v-if="icon"
|
|
121
127
|
:class="[
|
|
122
|
-
'w-6 h-6 md:w-
|
|
128
|
+
'w-6 h-6 md:w-5 md:h-5',
|
|
123
129
|
icon
|
|
124
130
|
]"
|
|
125
131
|
aria-hidden="true"
|
|
126
132
|
/>
|
|
127
|
-
<!-- Notification badge, pushed out toward the
|
|
128
|
-
|
|
129
|
-
<div
|
|
133
|
+
<!-- Notification badge, pushed out toward the item's top-right corner. -->
|
|
134
|
+
<UiBadge
|
|
130
135
|
v-if="showBadge"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
:count="badgeCount"
|
|
137
|
+
color="danger"
|
|
138
|
+
variant="strong"
|
|
139
|
+
class="absolute z-30 pointer-events-none"
|
|
140
|
+
:class="badgePositionClass"
|
|
135
141
|
aria-hidden="true"
|
|
136
|
-
|
|
137
|
-
<span v-if="badgeCount">{{ badgeCount }}</span>
|
|
138
|
-
</div>
|
|
142
|
+
/>
|
|
139
143
|
</div>
|
|
140
144
|
<div
|
|
141
145
|
class="pl-4 flex md:hidden text-content-text-md-regular font-bold truncate w-full items-center md:justify-center md:text-center"
|
|
@@ -18,23 +18,10 @@ provide('stepperOrientation', toRef(props, 'orientation'))
|
|
|
18
18
|
<StepperRoot
|
|
19
19
|
v-bind="{ ...forwarded, ...$attrs }"
|
|
20
20
|
class="ui-stepper"
|
|
21
|
-
:class="
|
|
22
|
-
'ui-stepper--horizontal'
|
|
23
|
-
'ui-stepper--vertical'
|
|
24
|
-
}"
|
|
21
|
+
:class="isHorizontal
|
|
22
|
+
? 'ui-stepper--horizontal flex items-start gap-8'
|
|
23
|
+
: 'ui-stepper--vertical [&>*:not(:last-child)]:mb-2'"
|
|
25
24
|
>
|
|
26
25
|
<slot />
|
|
27
26
|
</StepperRoot>
|
|
28
27
|
</template>
|
|
29
|
-
|
|
30
|
-
<style>
|
|
31
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
32
|
-
|
|
33
|
-
.ui-stepper--horizontal {
|
|
34
|
-
@apply flex items-start gap-8;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.ui-stepper--vertical > *:not(:last-child) {
|
|
38
|
-
@apply mb-2;
|
|
39
|
-
}
|
|
40
|
-
</style>
|
|
@@ -8,20 +8,8 @@ defineProps<StepperDescriptionProps>()
|
|
|
8
8
|
<template>
|
|
9
9
|
<StepperDescription
|
|
10
10
|
v-bind="$attrs"
|
|
11
|
-
|
|
12
|
-
'ui-stepper-description',
|
|
13
|
-
'transition-all duration-200',
|
|
14
|
-
]"
|
|
11
|
+
class="ui-stepper-description block max-w-60 truncate text-content-text-md-regular text-text-neutral-tertiary transition-all duration-200"
|
|
15
12
|
>
|
|
16
13
|
<slot />
|
|
17
14
|
</StepperDescription>
|
|
18
15
|
</template>
|
|
19
|
-
|
|
20
|
-
<style scoped>
|
|
21
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
22
|
-
|
|
23
|
-
.ui-stepper-description {
|
|
24
|
-
@apply max-w-60 truncate;
|
|
25
|
-
@apply text-content-text-md-regular text-text-neutral-tertiary block;
|
|
26
|
-
}
|
|
27
|
-
</style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { StepperIndicatorProps } from 'reka-ui'
|
|
3
3
|
import { StepperIndicator } from 'reka-ui'
|
|
4
|
+
import { useIconSize } from '../../composables/useIconSize'
|
|
4
5
|
|
|
5
6
|
type Size = 'sm' | 'md' | 'lg'
|
|
6
7
|
|
|
@@ -13,81 +14,40 @@ interface UiStepperIndicatorProps extends StepperIndicatorProps {
|
|
|
13
14
|
const { size = 'md', showCheckmark = true, icon } = defineProps<UiStepperIndicatorProps>()
|
|
14
15
|
|
|
15
16
|
const sizeClasses = {
|
|
16
|
-
sm: 'size-
|
|
17
|
-
md: 'size-
|
|
18
|
-
lg: 'size-
|
|
17
|
+
sm: 'size-8 text-content-text-md-regular',
|
|
18
|
+
md: 'size-10 text-content-text-md-regular',
|
|
19
|
+
lg: 'size-12 text-content-text-lg-regular',
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
sm: 'size-3',
|
|
23
|
-
md: 'size-4',
|
|
24
|
-
lg: 'size-5',
|
|
25
|
-
}
|
|
22
|
+
const iconSizeClass = useIconSize(() => size)
|
|
26
23
|
</script>
|
|
27
24
|
|
|
28
25
|
<template>
|
|
29
26
|
<StepperIndicator
|
|
30
27
|
v-bind="$attrs"
|
|
31
|
-
class="stepper-indicator"
|
|
28
|
+
class="stepper-indicator flex items-center justify-center shrink-0 rounded-full font-semibold transition-all duration-200 bg-surface-neutral-alpha-200 text-text-neutral-base group-data-[state=active]/step:bg-surface-primary-bg-alpha group-data-[state=active]/step:text-text-neutral-invert-base"
|
|
32
29
|
:class="sizeClasses[size]"
|
|
33
30
|
>
|
|
34
31
|
<span
|
|
35
32
|
v-if="icon"
|
|
36
|
-
class="stepper-custom-icon"
|
|
37
|
-
:class="[
|
|
33
|
+
class="stepper-custom-icon block"
|
|
34
|
+
:class="[iconSizeClass, icon, { 'group-data-[state=completed]/step:hidden': showCheckmark }]"
|
|
35
|
+
data-testid="ui-stepper-indicator-icon"
|
|
38
36
|
/>
|
|
39
37
|
|
|
40
38
|
<span
|
|
41
39
|
v-if="showCheckmark"
|
|
42
|
-
class="stepper-checkmark i-
|
|
43
|
-
:class="
|
|
40
|
+
class="stepper-checkmark i-ph-check hidden group-data-[state=completed]/step:block"
|
|
41
|
+
:class="iconSizeClass"
|
|
42
|
+
data-testid="ui-stepper-indicator-checkmark"
|
|
44
43
|
/>
|
|
45
44
|
|
|
46
|
-
<span
|
|
45
|
+
<span
|
|
46
|
+
class="stepper-number"
|
|
47
|
+
:class="{ hidden: icon, 'group-data-[state=completed]/step:hidden': showCheckmark }"
|
|
48
|
+
data-testid="ui-stepper-indicator-number"
|
|
49
|
+
>
|
|
47
50
|
<slot />
|
|
48
51
|
</span>
|
|
49
52
|
</StepperIndicator>
|
|
50
53
|
</template>
|
|
51
|
-
|
|
52
|
-
<style>
|
|
53
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
54
|
-
|
|
55
|
-
.stepper-indicator {
|
|
56
|
-
@apply flex items-center justify-center shrink-0;
|
|
57
|
-
@apply rounded-full;
|
|
58
|
-
@apply transition-all duration-200 font-semibold;
|
|
59
|
-
@apply bg-surface-neutral-alpha-200 text-text-neutral-base;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
[data-state="active"] .stepper-indicator {
|
|
63
|
-
@apply bg-surface-primary-bg-alpha text-text-neutral-invert-base;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.stepper-custom-icon {
|
|
67
|
-
@apply block;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
[data-state="completed"] .stepper-custom-icon {
|
|
71
|
-
@apply hidden;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.stepper-custom-icon ~ .stepper-number {
|
|
75
|
-
@apply hidden;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
[data-state="completed"] .stepper-custom-icon ~ .stepper-number {
|
|
79
|
-
@apply block;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.stepper-checkmark {
|
|
83
|
-
@apply hidden;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
[data-state="completed"] .stepper-checkmark {
|
|
87
|
-
@apply block;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
[data-state="completed"] .stepper-number {
|
|
91
|
-
@apply hidden;
|
|
92
|
-
}
|
|
93
|
-
</style>
|
|
@@ -16,15 +16,8 @@ withDefaults(defineProps<UiStepperItemProps>(), {
|
|
|
16
16
|
v-bind="$attrs"
|
|
17
17
|
:step="step"
|
|
18
18
|
:disabled="disabled"
|
|
19
|
-
class="ui-stepper-item"
|
|
19
|
+
class="ui-stepper-item group/step relative"
|
|
20
20
|
>
|
|
21
21
|
<slot />
|
|
22
22
|
</StepperItem>
|
|
23
23
|
</template>
|
|
24
|
-
|
|
25
|
-
<style>
|
|
26
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
27
|
-
.ui-stepper-item {
|
|
28
|
-
@apply relative;
|
|
29
|
-
}
|
|
30
|
-
</style>
|
|
@@ -28,22 +28,11 @@ const orientation = computed(() => props.orientation || unref(injectedOrientatio
|
|
|
28
28
|
>
|
|
29
29
|
<StepperSeparator
|
|
30
30
|
v-bind="$attrs"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'w-px min-h-8': orientation === 'vertical',
|
|
37
|
-
}
|
|
38
|
-
]"
|
|
31
|
+
class="ui-stepper-separator bg-surface-neutral-alpha-400 transition-colors duration-200"
|
|
32
|
+
:class="{
|
|
33
|
+
'h-px w-full min-w-8': orientation === 'horizontal',
|
|
34
|
+
'w-px min-h-8': orientation === 'vertical',
|
|
35
|
+
}"
|
|
39
36
|
/>
|
|
40
37
|
</div>
|
|
41
38
|
</template>
|
|
42
|
-
|
|
43
|
-
<style scoped>
|
|
44
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
45
|
-
|
|
46
|
-
.ui-stepper-separator {
|
|
47
|
-
@apply bg-surface-neutral-alpha-400;
|
|
48
|
-
}
|
|
49
|
-
</style>
|
|
@@ -8,17 +8,8 @@ defineProps<StepperTitleProps>()
|
|
|
8
8
|
<template>
|
|
9
9
|
<StepperTitle
|
|
10
10
|
v-bind="$attrs"
|
|
11
|
-
|
|
12
|
-
'ui-stepper-title',
|
|
13
|
-
]"
|
|
11
|
+
class="ui-stepper-title text-content-text-lg-bold text-text-neutral-secondary"
|
|
14
12
|
>
|
|
15
13
|
<slot />
|
|
16
14
|
</StepperTitle>
|
|
17
15
|
</template>
|
|
18
|
-
|
|
19
|
-
<style scoped>
|
|
20
|
-
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
21
|
-
.ui-stepper-title {
|
|
22
|
-
@apply text-content-text-lg-bold text-content-text-lg-bold text-text-neutral-secondary;
|
|
23
|
-
}
|
|
24
|
-
</style>
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject, ref } from 'vue'
|
|
3
|
+
import type { ComponentPublicInstance } from 'vue'
|
|
4
|
+
import { Label, SwitchRoot, SwitchThumb, useId } from 'reka-ui'
|
|
5
|
+
import type { SwitchColor, SwitchPosition, SwitchSize } from './types'
|
|
6
|
+
import UiTooltip from '../UiTooltip/UiTooltip.vue'
|
|
7
|
+
import { CHOICE_ROW_KEY } from '../UiChoice/types'
|
|
8
|
+
|
|
9
|
+
interface UiSwitchProps {
|
|
10
|
+
/**
|
|
11
|
+
* Whether the switch is on
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
modelValue?: boolean
|
|
15
|
+
/**
|
|
16
|
+
* The label text
|
|
17
|
+
*/
|
|
18
|
+
label?: string
|
|
19
|
+
/**
|
|
20
|
+
* Description text below the label
|
|
21
|
+
*/
|
|
22
|
+
description?: string
|
|
23
|
+
/**
|
|
24
|
+
* Info tooltip for the label
|
|
25
|
+
*/
|
|
26
|
+
labelInfo?: string
|
|
27
|
+
/**
|
|
28
|
+
* Icon class rendered between the switch and the text (e.g. `i-ph-bell`)
|
|
29
|
+
*/
|
|
30
|
+
icon?: string
|
|
31
|
+
/**
|
|
32
|
+
* Whether the switch is disabled
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
disabled?: boolean
|
|
36
|
+
/**
|
|
37
|
+
* Whether the switch has an error
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
error?: boolean
|
|
41
|
+
/**
|
|
42
|
+
* Color of the on state (error takes precedence)
|
|
43
|
+
* @default 'primary'
|
|
44
|
+
*/
|
|
45
|
+
color?: SwitchColor
|
|
46
|
+
/**
|
|
47
|
+
* Size of the switch
|
|
48
|
+
* @default 'sm'
|
|
49
|
+
*/
|
|
50
|
+
size?: SwitchSize
|
|
51
|
+
/**
|
|
52
|
+
* Which side of the text the switch sits on
|
|
53
|
+
* @default 'left'
|
|
54
|
+
*/
|
|
55
|
+
switchPosition?: SwitchPosition
|
|
56
|
+
/**
|
|
57
|
+
* Whether to show a background behind the switch and its text
|
|
58
|
+
* @default false
|
|
59
|
+
*/
|
|
60
|
+
showBg?: boolean
|
|
61
|
+
/**
|
|
62
|
+
* Whether the switch stretches to fill its container, which spreads the
|
|
63
|
+
* text and the switch to opposite edges
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
fullWidth?: boolean
|
|
67
|
+
/**
|
|
68
|
+
* Switch name attribute (renders a hidden input for native forms)
|
|
69
|
+
*/
|
|
70
|
+
name?: string
|
|
71
|
+
/**
|
|
72
|
+
* Switch id attribute
|
|
73
|
+
*/
|
|
74
|
+
id?: string
|
|
75
|
+
/**
|
|
76
|
+
* Accessible name for the switch, for switches rendered without a label
|
|
77
|
+
*/
|
|
78
|
+
ariaLabel?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const props = withDefaults(defineProps<UiSwitchProps>(), {
|
|
82
|
+
modelValue: false,
|
|
83
|
+
label: undefined,
|
|
84
|
+
description: undefined,
|
|
85
|
+
labelInfo: undefined,
|
|
86
|
+
icon: undefined,
|
|
87
|
+
disabled: false,
|
|
88
|
+
error: false,
|
|
89
|
+
color: 'primary',
|
|
90
|
+
size: 'sm',
|
|
91
|
+
switchPosition: 'left',
|
|
92
|
+
showBg: false,
|
|
93
|
+
fullWidth: false,
|
|
94
|
+
name: undefined,
|
|
95
|
+
id: undefined,
|
|
96
|
+
ariaLabel: undefined,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const emit = defineEmits<{
|
|
100
|
+
'update:modelValue': [value: boolean]
|
|
101
|
+
'focus': [event: FocusEvent]
|
|
102
|
+
'blur': [event: FocusEvent]
|
|
103
|
+
}>()
|
|
104
|
+
|
|
105
|
+
const generatedId = useId()
|
|
106
|
+
const inputId = computed(() => props.id ?? generatedId)
|
|
107
|
+
|
|
108
|
+
const isChecked = computed(() => props.modelValue)
|
|
109
|
+
const inChoiceRow = inject(CHOICE_ROW_KEY, false)
|
|
110
|
+
|
|
111
|
+
// Design palette: primary | warning, with error mapping onto the danger tokens
|
|
112
|
+
const palette = computed(() => props.error ? 'danger' : props.color === 'warning' ? 'warning' : 'primary')
|
|
113
|
+
|
|
114
|
+
const descriptionId = computed(() => props.description ? `${inputId.value}-description` : undefined)
|
|
115
|
+
|
|
116
|
+
// Inside UiChoice a disabled switch greys out instead of fading with the row
|
|
117
|
+
const greyedOut = computed(() => inChoiceRow && props.disabled && !props.error)
|
|
118
|
+
|
|
119
|
+
const trackClasses = computed(() => {
|
|
120
|
+
const baseClasses = [
|
|
121
|
+
'relative shrink-0 inline-flex items-center rounded-full',
|
|
122
|
+
'border p-[3px] transition-colors',
|
|
123
|
+
'focus:outline-hidden',
|
|
124
|
+
'cursor-pointer disabled:cursor-not-allowed',
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
// sm: 40x24 track / 16px thumb, xs: 32x20 track / 12px thumb
|
|
128
|
+
baseClasses.push(props.size === 'xs' ? 'h-5 w-8' : 'h-6 w-10')
|
|
129
|
+
|
|
130
|
+
if (greyedOut.value) {
|
|
131
|
+
baseClasses.push('border-transparent', isChecked.value ? 'bg-surface-neutral-alpha-400' : 'bg-surface-neutral-alpha-200')
|
|
132
|
+
} else if (isChecked.value) {
|
|
133
|
+
const checkedClasses = {
|
|
134
|
+
primary: 'bg-control-switch-on border-transparent',
|
|
135
|
+
warning: 'bg-actions-warning-idle-alpha-strong border-actions-warning-idle-alpha-strong',
|
|
136
|
+
danger: 'bg-actions-danger-idle-alpha-strong border-actions-danger-idle-alpha-strong',
|
|
137
|
+
}
|
|
138
|
+
baseClasses.push(checkedClasses[palette.value])
|
|
139
|
+
if (!props.disabled) {
|
|
140
|
+
// On warning/danger the border lightens on hover (primary keeps its border)
|
|
141
|
+
const checkedHoverClasses = {
|
|
142
|
+
primary: '',
|
|
143
|
+
warning: 'group-hover:border-borders-warning-default',
|
|
144
|
+
danger: 'group-hover:border-borders-danger-default',
|
|
145
|
+
}
|
|
146
|
+
baseClasses.push(checkedHoverClasses[palette.value])
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
const uncheckedBorderClasses = {
|
|
150
|
+
primary: 'border-control-off-border',
|
|
151
|
+
warning: 'border-borders-warning-strong',
|
|
152
|
+
danger: 'border-control-error-border',
|
|
153
|
+
}
|
|
154
|
+
baseClasses.push('bg-control-off', uncheckedBorderClasses[palette.value])
|
|
155
|
+
if (!props.disabled) {
|
|
156
|
+
// Border thickens on hover; padding shrinks in step so the thumb
|
|
157
|
+
// keeps its exact position and travel distance
|
|
158
|
+
baseClasses.push('group-hover:border-2 group-hover:p-0.5')
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// The row draws the focus ring, or the choicebox around it
|
|
163
|
+
baseClasses.push('focus-indicator-none')
|
|
164
|
+
|
|
165
|
+
return baseClasses
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const trackHoverOverlayClasses = computed(() => {
|
|
169
|
+
const overlayClasses = {
|
|
170
|
+
primary: isChecked.value ? 'bg-actions-primary-hover-overlay' : 'bg-actions-neutral-hover-overlay',
|
|
171
|
+
warning: 'bg-actions-warning-hover-overlay',
|
|
172
|
+
danger: 'bg-actions-danger-hover-overlay',
|
|
173
|
+
}
|
|
174
|
+
return overlayClasses[palette.value]
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
const thumbClasses = computed(() => {
|
|
178
|
+
const baseClasses = ['pointer-events-none block rounded-full transition-transform']
|
|
179
|
+
|
|
180
|
+
if (props.size === 'xs') {
|
|
181
|
+
baseClasses.push('size-3 data-[state=checked]:translate-x-3')
|
|
182
|
+
} else {
|
|
183
|
+
baseClasses.push('size-4 data-[state=checked]:translate-x-4')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
baseClasses.push(isChecked.value
|
|
187
|
+
? 'bg-actions-neutral-invert-idle'
|
|
188
|
+
: 'bg-control-off-knob')
|
|
189
|
+
|
|
190
|
+
return baseClasses
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const innerWrapperClasses = computed(() => {
|
|
194
|
+
const classes = ['group relative flex items-center rounded-field-md', inChoiceRow && props.switchPosition === 'right' ? 'gap-6' : 'gap-2']
|
|
195
|
+
|
|
196
|
+
if (props.switchPosition === 'right') {
|
|
197
|
+
classes.push('flex-row-reverse')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (props.disabled) {
|
|
201
|
+
classes.push(inChoiceRow ? 'cursor-not-allowed' : 'opacity-50 cursor-not-allowed')
|
|
202
|
+
} else {
|
|
203
|
+
classes.push('cursor-pointer')
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (inChoiceRow) {
|
|
207
|
+
return classes
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!props.showBg) {
|
|
211
|
+
classes.push('isolate before:absolute before:-inset-x-3 before:-inset-y-1.5 before:-z-10 before:rounded-field-md before:pointer-events-none has-[[role=switch]:focus-visible]:before:focus-ring')
|
|
212
|
+
return classes
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
classes.push('p-2 shadow-[0px_1px_2px_0px_var(--color-shadow-neutral-100)] has-[[role=switch]:focus-visible]:focus-ring')
|
|
216
|
+
classes.push(props.error ? 'bg-actions-danger-idle-alpha-light' : 'bg-actions-neutral-idle-alpha-light')
|
|
217
|
+
|
|
218
|
+
return classes
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
const labelClasses = computed(() => ({
|
|
222
|
+
'text-content-text-md-medium': inChoiceRow,
|
|
223
|
+
'text-content-text-md-regular': !inChoiceRow,
|
|
224
|
+
'text-text-danger-base': props.error,
|
|
225
|
+
'text-text-neutral-base': !props.error && !greyedOut.value,
|
|
226
|
+
'text-text-neutral-tertiary': greyedOut.value,
|
|
227
|
+
'cursor-not-allowed': props.disabled,
|
|
228
|
+
'cursor-pointer': !props.disabled,
|
|
229
|
+
}))
|
|
230
|
+
|
|
231
|
+
// Text hugs the switch when the component sizes to its content, but stays at
|
|
232
|
+
// the leading edge when it stretches, so the row reads label-left/switch-right.
|
|
233
|
+
const textAlignsToSwitch = computed(() => props.switchPosition === 'right' && !props.fullWidth)
|
|
234
|
+
|
|
235
|
+
const descriptionClasses = computed(() => ({
|
|
236
|
+
'text-right': textAlignsToSwitch.value,
|
|
237
|
+
'text-text-danger-tertiary': props.error,
|
|
238
|
+
'text-text-neutral-secondary': !props.error && !greyedOut.value,
|
|
239
|
+
'text-text-neutral-tertiary': greyedOut.value,
|
|
240
|
+
'cursor-not-allowed': props.disabled,
|
|
241
|
+
'cursor-pointer': !props.disabled,
|
|
242
|
+
}))
|
|
243
|
+
|
|
244
|
+
// SwitchRoot is a generic component, so InstanceType<typeof SwitchRoot> can't be used
|
|
245
|
+
const switchRef = ref<ComponentPublicInstance | null>(null)
|
|
246
|
+
|
|
247
|
+
function switchElement (): HTMLElement | undefined {
|
|
248
|
+
return (switchRef.value?.$el as HTMLElement | undefined) ?? undefined
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Make the whole wrapper (description, icon, background) toggle the switch.
|
|
252
|
+
// Clicks on the button itself or on the label are skipped: the button handles
|
|
253
|
+
// its own clicks and the label forwards to the button natively via `for`.
|
|
254
|
+
function onWrapperClick (event: MouseEvent) {
|
|
255
|
+
if (props.disabled) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const target = event.target as HTMLElement
|
|
260
|
+
const switchEl = switchElement()
|
|
261
|
+
|
|
262
|
+
if (!switchEl || target === switchEl || switchEl.contains(target)) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
if (target.closest('label')) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
switchEl.click()
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
defineExpose({
|
|
273
|
+
focus: () => switchElement()?.focus(),
|
|
274
|
+
blur: () => switchElement()?.blur(),
|
|
275
|
+
})
|
|
276
|
+
</script>
|
|
277
|
+
|
|
278
|
+
<template>
|
|
279
|
+
<div
|
|
280
|
+
class="ui-switch-wrapper"
|
|
281
|
+
:class="fullWidth ? 'w-full' : 'w-fit'"
|
|
282
|
+
>
|
|
283
|
+
<div
|
|
284
|
+
:class="innerWrapperClasses"
|
|
285
|
+
@click="onWrapperClick"
|
|
286
|
+
>
|
|
287
|
+
<SwitchRoot
|
|
288
|
+
:id="inputId"
|
|
289
|
+
ref="switchRef"
|
|
290
|
+
:model-value="modelValue"
|
|
291
|
+
:disabled="disabled"
|
|
292
|
+
:name="name"
|
|
293
|
+
:class="trackClasses"
|
|
294
|
+
:aria-label="ariaLabel"
|
|
295
|
+
:aria-invalid="error || undefined"
|
|
296
|
+
:aria-describedby="descriptionId"
|
|
297
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
298
|
+
@focus="emit('focus', $event)"
|
|
299
|
+
@blur="emit('blur', $event)"
|
|
300
|
+
>
|
|
301
|
+
<SwitchThumb :class="thumbClasses" />
|
|
302
|
+
|
|
303
|
+
<div
|
|
304
|
+
v-if="!disabled"
|
|
305
|
+
class="absolute inset-0 rounded-full opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
|
|
306
|
+
:class="trackHoverOverlayClasses"
|
|
307
|
+
/>
|
|
308
|
+
</SwitchRoot>
|
|
309
|
+
|
|
310
|
+
<!-- optional leading icon -->
|
|
311
|
+
<slot name="icon">
|
|
312
|
+
<span
|
|
313
|
+
v-if="icon"
|
|
314
|
+
class="size-4 shrink-0"
|
|
315
|
+
:class="[icon, error ? 'text-text-danger-base' : 'text-text-neutral-secondary']"
|
|
316
|
+
/>
|
|
317
|
+
</slot>
|
|
318
|
+
|
|
319
|
+
<div
|
|
320
|
+
v-if="label || description || $slots.label"
|
|
321
|
+
class="flex flex-col min-w-0 flex-1"
|
|
322
|
+
:class="{ 'items-end': textAlignsToSwitch, 'gap-1': inChoiceRow }"
|
|
323
|
+
>
|
|
324
|
+
<div
|
|
325
|
+
class="flex items-center gap-2"
|
|
326
|
+
:class="{ 'flex-row-reverse': textAlignsToSwitch }"
|
|
327
|
+
>
|
|
328
|
+
<Label
|
|
329
|
+
:for="inputId"
|
|
330
|
+
:class="labelClasses"
|
|
331
|
+
>
|
|
332
|
+
<slot name="label">
|
|
333
|
+
{{ label }}
|
|
334
|
+
</slot>
|
|
335
|
+
</Label>
|
|
336
|
+
|
|
337
|
+
<!-- info tooltip -->
|
|
338
|
+
<UiTooltip
|
|
339
|
+
v-if="labelInfo"
|
|
340
|
+
:content="labelInfo"
|
|
341
|
+
>
|
|
342
|
+
<!-- stays neutral even in error state, per design -->
|
|
343
|
+
<div
|
|
344
|
+
class="flex items-center justify-center size-4 cursor-help shrink-0 text-text-neutral-tertiary"
|
|
345
|
+
data-testid="ui-switch-label-info"
|
|
346
|
+
@click.stop
|
|
347
|
+
>
|
|
348
|
+
<span class="i-ph-info-fill size-4" />
|
|
349
|
+
</div>
|
|
350
|
+
</UiTooltip>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<span
|
|
354
|
+
v-if="description"
|
|
355
|
+
:id="descriptionId"
|
|
356
|
+
class="text-content-text-md-regular"
|
|
357
|
+
:class="descriptionClasses"
|
|
358
|
+
>
|
|
359
|
+
{{ description }}
|
|
360
|
+
</span>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<!-- Background hover overlay -->
|
|
364
|
+
<div
|
|
365
|
+
v-if="showBg && !disabled"
|
|
366
|
+
class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none rounded-[inherit]"
|
|
367
|
+
:class="{
|
|
368
|
+
'bg-actions-danger-hover-overlay': error,
|
|
369
|
+
'bg-actions-primary-hover-overlay': isChecked && !error,
|
|
370
|
+
'bg-actions-neutral-hover-overlay': !isChecked && !error,
|
|
371
|
+
}"
|
|
372
|
+
/>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</template>
|