@livestorm/ui-vue 3.0.0 → 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 +3 -3
- 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 -231
- package/dist/i18n-B8QOMuFD.mjs +46 -0
- package/dist/index.mjs +4 -91
- 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 +19 -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/types.d.ts +2 -0
- 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 +95 -35
- package/dist/types/src/components/UiButtonGroup/UiButtonGroup.vue.d.ts +8 -2
- package/dist/types/src/components/UiButtonGroup/types.d.ts +4 -0
- 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 +8 -5
- 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/UiInputRadio/RadioGlyph.vue.d.ts +19 -0
- package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +21 -8
- package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +6 -1
- package/dist/types/src/components/UiInputRadio/types.d.ts +1 -1
- 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/UiSpinnerIcon.vue.d.ts +3 -0
- 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 +79 -15
- 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/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 +10 -5
- 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 +3 -0
- 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 +7 -9
- 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 +2 -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/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 +18 -0
- package/index.js +36 -0
- package/package.json +26 -21
- package/src/components/Toast/LayoutToasts.vue +19 -11
- package/src/components/Toast/ToastMessage.vue +125 -68
- package/src/components/Toast/toastDSV2.ts +238 -67
- package/src/components/UiActionBar/UiActionBar.vue +4 -3
- package/src/components/UiActionBar/UiActionBarItem.vue +9 -6
- package/src/components/UiActionBar/types.ts +13 -0
- package/src/components/UiAvatar/UiAvatar.vue +8 -2
- package/src/components/UiAvatarList/UiAvatarList.vue +51 -20
- package/src/components/UiBadge/UiBadge.vue +1 -1
- package/src/components/UiBanner/UiBanner.vue +71 -21
- package/src/components/UiBreadcrumb/UiBreadcrumb.vue +1 -1
- package/src/components/UiButton/UiButton.vue +92 -25
- package/src/components/UiButtonGroup/UiButtonGroup.vue +21 -17
- package/src/components/UiButtonGroup/UiButtonGroupItem.vue +33 -15
- package/src/components/UiButtonGroup/types.ts +19 -0
- package/src/components/UiCard/UiCard.vue +20 -23
- 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 +11 -8
- 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/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 +38 -18
- package/src/components/UiDropdown/UiDropdownSeparator.vue +1 -1
- package/src/components/UiDropdown/UiDropdownTrigger.vue +138 -43
- 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 +9 -3
- 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 +18 -95
- package/src/components/UiInputRadio/RadioGlyph.vue +45 -0
- package/src/components/UiInputRadio/UiInputRadio.vue +177 -169
- package/src/components/UiInputRadio/UiInputRadioGroup.vue +15 -5
- package/src/components/UiInputRadio/types.ts +1 -2
- 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 +4 -20
- package/src/components/UiLoader/UiSpinnerIcon.vue +21 -0
- 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 +1 -1
- package/src/components/UiNestedOptions/UiNestedOptions.vue +6 -0
- package/src/components/UiPage/UiPage.vue +190 -32
- 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 +10 -41
- package/src/components/UiRoomCountdown/UiRoomCountdown.vue +376 -0
- package/src/components/UiShell/UiShell.vue +9 -3
- package/src/components/UiSideMenu/UiSideMenu.vue +155 -18
- package/src/components/UiSideMenu/UiSideMenuItemContent.vue +20 -16
- package/src/components/UiStepper/UiStepper.vue +3 -16
- package/src/components/UiStepper/UiStepperDescription.vue +1 -13
- package/src/components/UiStepper/UiStepperIndicator.vue +11 -48
- 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 +44 -28
- 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 +56 -61
- 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 +21 -36
- 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 +29 -16
- 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 +15 -0
- package/src/composables/usePullGesture.ts +142 -0
- package/src/composables/useThemeAndModes.ts +28 -1
- package/src/css/body.css +16 -1
- package/src/css/fonts.css +39 -5
- package/src/global.d.ts +36 -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-LpdvGf5X.mjs +0 -6093
- 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/UiInputEmoji/index.d.ts +0 -1
- package/dist/types/src/components/UiInputRadio/index.d.ts +0 -3
- 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/UiSwitch/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/UiInputEmoji/index.ts +0 -1
- package/src/components/UiInputRadio/index.ts +0 -3
- 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/UiSwitch/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
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactiveOmit, useMutationObserver, useScroll } from '@vueuse/core'
|
|
3
|
+
import type { DialogContentEmits, DialogContentProps } from 'reka-ui'
|
|
4
|
+
import {
|
|
5
|
+
DialogClose,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogPortal,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
useForwardPropsEmits,
|
|
10
|
+
VisuallyHidden,
|
|
11
|
+
} from 'reka-ui'
|
|
12
|
+
import { computed, onMounted, ref, useAttrs, useSlots } from 'vue'
|
|
13
|
+
import { useTranslate } from '../../utils/i18n'
|
|
14
|
+
import UiButton from '../UiButton/UiButton.vue'
|
|
15
|
+
|
|
16
|
+
export type UiPaneSize = 'sm' | 'lg'
|
|
17
|
+
export type UiPaneOrientation = 'horizontal' | 'vertical'
|
|
18
|
+
|
|
19
|
+
export interface UiPaneContentProps extends DialogContentProps {
|
|
20
|
+
/**
|
|
21
|
+
* Header title, also used as the pane's accessible name
|
|
22
|
+
*/
|
|
23
|
+
title?: string
|
|
24
|
+
/**
|
|
25
|
+
* Density of the header, padding and title
|
|
26
|
+
* @values sm, lg
|
|
27
|
+
*/
|
|
28
|
+
size?: UiPaneSize
|
|
29
|
+
/**
|
|
30
|
+
* Layout of the footer actions: in a row, or stacked full width
|
|
31
|
+
* @values horizontal, vertical
|
|
32
|
+
*/
|
|
33
|
+
orientation?: UiPaneOrientation
|
|
34
|
+
hideClose?: boolean
|
|
35
|
+
closeButtonAriaLabel?: string
|
|
36
|
+
/**
|
|
37
|
+
* Removes the body padding, for full-bleed content
|
|
38
|
+
*/
|
|
39
|
+
noPadding?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* When false, a click outside or Escape no longer closes the pane:
|
|
42
|
+
* only the close button or the consumer's own actions do
|
|
43
|
+
*/
|
|
44
|
+
dismissible?: boolean
|
|
45
|
+
/**
|
|
46
|
+
* Any CSS width, for content with a fixed width of its own (an email's 600px column).
|
|
47
|
+
* Still capped to the viewport.
|
|
48
|
+
*/
|
|
49
|
+
width?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Attributes belong on the pane itself, not on the portal root.
|
|
53
|
+
defineOptions({ inheritAttrs: false })
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<UiPaneContentProps>(), {
|
|
56
|
+
title: undefined,
|
|
57
|
+
size: 'lg',
|
|
58
|
+
orientation: 'horizontal',
|
|
59
|
+
hideClose: false,
|
|
60
|
+
closeButtonAriaLabel: undefined,
|
|
61
|
+
noPadding: false,
|
|
62
|
+
dismissible: true,
|
|
63
|
+
width: undefined,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const emits = defineEmits<DialogContentEmits>()
|
|
67
|
+
|
|
68
|
+
// Our own props must not reach DialogContent, where `title` would become a native tooltip.
|
|
69
|
+
const contentProps = reactiveOmit(props, 'title', 'size', 'orientation', 'hideClose', 'closeButtonAriaLabel', 'noPadding', 'dismissible', 'width')
|
|
70
|
+
const forwarded = useForwardPropsEmits(contentProps, emits)
|
|
71
|
+
|
|
72
|
+
const slots = useSlots()
|
|
73
|
+
const attrs = useAttrs()
|
|
74
|
+
const translate = useTranslate()
|
|
75
|
+
|
|
76
|
+
const closeAriaLabel = computed(() => props.closeButtonAriaLabel ?? translate('general_close'))
|
|
77
|
+
const accessibleTitle = computed(() => (attrs['aria-label'] as string) || translate('general_dialog_title'))
|
|
78
|
+
|
|
79
|
+
// Non-modal, the page stays usable: only a click outside or Escape dismisses the pane.
|
|
80
|
+
// Focus leaving it (tabbing back to the page, a menu restoring focus to its trigger) must not.
|
|
81
|
+
function keepOpenOnFocusOutside (event: Event) {
|
|
82
|
+
event.preventDefault()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function preventUnlessDismissible (event: Event) {
|
|
86
|
+
if (!props.dismissible) event.preventDefault()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Portaled to the body so no stacking context of the page can cover it, the pane takes the
|
|
90
|
+
// color mode of the place it is declared, as if it rendered there.
|
|
91
|
+
const anchor = ref<HTMLElement | null>(null)
|
|
92
|
+
const colorModeHost = ref<HTMLElement | null>(null)
|
|
93
|
+
const colorMode = ref<string>()
|
|
94
|
+
|
|
95
|
+
function syncColorMode () {
|
|
96
|
+
colorMode.value = colorModeHost.value?.getAttribute('data-colors-semantic') ?? undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
onMounted(() => {
|
|
100
|
+
colorModeHost.value = anchor.value?.closest<HTMLElement>('[data-colors-semantic]') ?? null
|
|
101
|
+
syncColorMode()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
useMutationObserver(colorModeHost, syncColorMode, { attributeFilter: ['data-colors-semantic'] })
|
|
105
|
+
|
|
106
|
+
const hasHeader = computed(() => !!props.title || !!slots.header || !props.hideClose)
|
|
107
|
+
|
|
108
|
+
const body = ref<HTMLElement | null>(null)
|
|
109
|
+
const { y: bodyScrollTop } = useScroll(body)
|
|
110
|
+
|
|
111
|
+
const isLarge = computed(() => props.size === 'lg')
|
|
112
|
+
const spacing = computed(() => isLarge.value ? 'p-6' : 'p-4')
|
|
113
|
+
|
|
114
|
+
const bodyClasses = computed(() => {
|
|
115
|
+
if (props.noPadding) return ''
|
|
116
|
+
|
|
117
|
+
return [
|
|
118
|
+
isLarge.value ? 'px-6 pb-6' : 'px-4 pb-4',
|
|
119
|
+
!hasHeader.value && (isLarge.value ? 'pt-6' : 'pt-4'),
|
|
120
|
+
]
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const footerClasses = computed(() => props.orientation === 'vertical'
|
|
124
|
+
? 'flex flex-col gap-2 *:w-full'
|
|
125
|
+
: 'flex items-center justify-end gap-2')
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<span
|
|
130
|
+
ref="anchor"
|
|
131
|
+
hidden
|
|
132
|
+
/>
|
|
133
|
+
<DialogPortal>
|
|
134
|
+
<!-- The pane has no description slot; opt out of reka's one unless the consumer points to its own. -->
|
|
135
|
+
<DialogContent
|
|
136
|
+
data-testid="ui-pane-content"
|
|
137
|
+
v-bind="{ 'aria-describedby': undefined, ...forwarded, ...$attrs }"
|
|
138
|
+
:class="[
|
|
139
|
+
'ui-pane-content fixed top-3 right-3 bottom-3 z-pane flex flex-col overflow-hidden',
|
|
140
|
+
!width && 'w-[560px]',
|
|
141
|
+
'max-w-[calc(100vw-1.5rem)]',
|
|
142
|
+
'bg-surface-neutral-300 shadow-shadows-drop-shadow-2xl rounded-container',
|
|
143
|
+
'focus:outline-hidden focus-indicator-none',
|
|
144
|
+
]"
|
|
145
|
+
:style="width ? { width } : undefined"
|
|
146
|
+
:data-colors-semantic="colorMode"
|
|
147
|
+
@focus-outside="keepOpenOnFocusOutside"
|
|
148
|
+
@pointer-down-outside="preventUnlessDismissible"
|
|
149
|
+
@escape-key-down="preventUnlessDismissible"
|
|
150
|
+
>
|
|
151
|
+
<VisuallyHidden
|
|
152
|
+
v-if="!title"
|
|
153
|
+
as-child
|
|
154
|
+
>
|
|
155
|
+
<DialogTitle>{{ accessibleTitle }}</DialogTitle>
|
|
156
|
+
</VisuallyHidden>
|
|
157
|
+
|
|
158
|
+
<div
|
|
159
|
+
v-if="hasHeader"
|
|
160
|
+
data-testid="ui-pane-header"
|
|
161
|
+
:class="['relative z-1 flex min-h-10 shrink-0 items-center box-content', spacing, isLarge ? 'gap-6' : 'gap-4', bodyScrollTop > 0 && 'shadow-elevation-level-1']"
|
|
162
|
+
>
|
|
163
|
+
<DialogTitle
|
|
164
|
+
v-if="title"
|
|
165
|
+
:class="['min-w-0 break-words text-text-neutral-base', isLarge ? 'text-title-md-bold' : 'text-title-sm-bold']"
|
|
166
|
+
>
|
|
167
|
+
{{ title }}
|
|
168
|
+
</DialogTitle>
|
|
169
|
+
|
|
170
|
+
<div
|
|
171
|
+
v-if="$slots.header"
|
|
172
|
+
class="flex min-w-0 flex-1 items-center"
|
|
173
|
+
>
|
|
174
|
+
<slot name="header" />
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<!-- Not UiDialogClose: it pins itself to light, and the pane follows the mode around it -->
|
|
178
|
+
<DialogClose
|
|
179
|
+
v-if="!hideClose"
|
|
180
|
+
as-child
|
|
181
|
+
>
|
|
182
|
+
<UiButton
|
|
183
|
+
variant="tertiary"
|
|
184
|
+
color="neutral"
|
|
185
|
+
size="md"
|
|
186
|
+
icon="i-ph-x"
|
|
187
|
+
data-testid="ui-pane-close"
|
|
188
|
+
class="ml-auto shrink-0"
|
|
189
|
+
:aria-label="closeAriaLabel"
|
|
190
|
+
/>
|
|
191
|
+
</DialogClose>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div
|
|
195
|
+
ref="body"
|
|
196
|
+
data-testid="ui-pane-body"
|
|
197
|
+
:class="['min-h-0 flex-1 overflow-y-auto', bodyClasses]"
|
|
198
|
+
>
|
|
199
|
+
<slot />
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div
|
|
203
|
+
v-if="$slots.footer"
|
|
204
|
+
data-testid="ui-pane-footer"
|
|
205
|
+
:class="['shrink-0 border-t border-divider', spacing, footerClasses]"
|
|
206
|
+
>
|
|
207
|
+
<slot name="footer" />
|
|
208
|
+
</div>
|
|
209
|
+
</DialogContent>
|
|
210
|
+
</DialogPortal>
|
|
211
|
+
</template>
|
|
212
|
+
|
|
213
|
+
<style scoped>
|
|
214
|
+
/* Own keyframes, like UiDialogContent: reka waits for `animationend` before unmounting. */
|
|
215
|
+
@keyframes ui-pane-in {
|
|
216
|
+
from { opacity: 0; transform: translateX(1rem); }
|
|
217
|
+
to { opacity: 1; transform: translateX(0); }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@keyframes ui-pane-out {
|
|
221
|
+
from { opacity: 1; transform: translateX(0); }
|
|
222
|
+
to { opacity: 0; transform: translateX(1rem); }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.ui-pane-content[data-state='open'] {
|
|
226
|
+
animation: ui-pane-in 200ms ease-out both;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.ui-pane-content[data-state='closed'] {
|
|
230
|
+
animation: ui-pane-out 150ms ease-in both;
|
|
231
|
+
}
|
|
232
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogTriggerProps } from 'reka-ui'
|
|
3
|
+
import { DialogTrigger, useForwardProps } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<DialogTriggerProps>()
|
|
6
|
+
|
|
7
|
+
const forwarded = useForwardProps(props)
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<DialogTrigger v-bind="{ ...forwarded, ...$attrs }">
|
|
12
|
+
<slot />
|
|
13
|
+
</DialogTrigger>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { default as UiPane } from './UiPane.vue'
|
|
2
|
+
export { default as UiPaneTrigger } from './UiPaneTrigger.vue'
|
|
3
|
+
export { default as UiPaneContent } from './UiPaneContent.vue'
|
|
4
|
+
export type { UiPaneContentProps, UiPaneOrientation, UiPaneSize } from './UiPaneContent.vue'
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
DialogClose,
|
|
8
|
+
type DialogRootProps,
|
|
9
|
+
type DialogRootEmits,
|
|
10
|
+
type DialogContentProps,
|
|
11
|
+
type DialogContentEmits,
|
|
12
|
+
} from 'reka-ui'
|
|
@@ -12,7 +12,7 @@ const forwarded = useForwardPropsEmits(props, emits)
|
|
|
12
12
|
<PopoverPortal>
|
|
13
13
|
<PopoverContent
|
|
14
14
|
v-bind="{ ...forwarded, ...$attrs }"
|
|
15
|
-
class="rounded-
|
|
15
|
+
class="rounded-container shadow-shadows-drop-shadow-lg border border-container-border ui-popover-content z-overlay"
|
|
16
16
|
>
|
|
17
17
|
<slot />
|
|
18
18
|
</PopoverContent>
|
|
@@ -20,7 +20,27 @@ const forwarded = useForwardPropsEmits(props, emits)
|
|
|
20
20
|
</template>
|
|
21
21
|
|
|
22
22
|
<style>
|
|
23
|
+
/* Unscoped: reka gives the scope id to its popper wrapper, not this content. */
|
|
23
24
|
.ui-popover-content {
|
|
24
25
|
transform-origin: var(--reka-popover-content-transform-origin, center);
|
|
25
26
|
}
|
|
27
|
+
|
|
28
|
+
/* Local keyframes: reka waits for the closing animation before unmounting. */
|
|
29
|
+
@keyframes ui-popover-scale-in {
|
|
30
|
+
from { opacity: 0; transform: scale(0.6); }
|
|
31
|
+
to { opacity: 1; transform: scale(1); }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@keyframes ui-popover-scale-out {
|
|
35
|
+
from { opacity: 1; transform: scale(1); }
|
|
36
|
+
to { opacity: 0; transform: scale(0.6); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:where(.ui-popover-content[data-state='open']) {
|
|
40
|
+
animation: ui-popover-scale-in 150ms ease-out both;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ui-popover-content[data-state='closed'] {
|
|
44
|
+
animation: ui-popover-scale-out 100ms ease-in both;
|
|
45
|
+
}
|
|
26
46
|
</style>
|
|
@@ -66,11 +66,11 @@ const sizeClass = computed(() => {
|
|
|
66
66
|
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
67
67
|
|
|
68
68
|
.ui-progress-root {
|
|
69
|
-
@apply relative w-full bg-actions-neutral-active-overlay overflow-hidden rounded-
|
|
69
|
+
@apply relative w-full bg-actions-neutral-active-overlay overflow-hidden rounded-full;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.ui-progress-indicator {
|
|
73
|
-
@apply h-full w-full rounded-
|
|
73
|
+
@apply h-full w-full rounded-full transition-transform duration-[660ms];
|
|
74
74
|
transition-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
|
+
import type { ComponentPublicInstance } from 'vue'
|
|
4
|
+
import { RadioGroupItem, RadioGroupRoot } from 'reka-ui'
|
|
5
|
+
import type { AcceptableValue, RadioGroupItemEmits } from 'reka-ui'
|
|
6
|
+
import type { RatingSize } from './types'
|
|
7
|
+
|
|
8
|
+
interface UiRatingProps {
|
|
9
|
+
/**
|
|
10
|
+
* The selected star, from 1 to `max`. `null` means no selection.
|
|
11
|
+
* @default null
|
|
12
|
+
*/
|
|
13
|
+
modelValue?: number | null
|
|
14
|
+
/**
|
|
15
|
+
* Number of stars
|
|
16
|
+
* @default 5
|
|
17
|
+
*/
|
|
18
|
+
max?: number
|
|
19
|
+
/**
|
|
20
|
+
* Caption under each star, in star order. The caption is also the star's
|
|
21
|
+
* accessible name; a star without one is named by its number.
|
|
22
|
+
*/
|
|
23
|
+
labels?: string[]
|
|
24
|
+
/**
|
|
25
|
+
* Size of the stars
|
|
26
|
+
* @default 'md'
|
|
27
|
+
*/
|
|
28
|
+
size?: RatingSize
|
|
29
|
+
/**
|
|
30
|
+
* Whether the rating only displays its value. The stars stay focusable so
|
|
31
|
+
* the value can be read, but cannot be changed.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
readonly?: boolean
|
|
35
|
+
/**
|
|
36
|
+
* Whether the rating is disabled
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
disabled?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* Whether a rating is required
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
required?: boolean
|
|
45
|
+
/**
|
|
46
|
+
* Name attribute, submitted with the form via a hidden input
|
|
47
|
+
*/
|
|
48
|
+
name?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const props = withDefaults(defineProps<UiRatingProps>(), {
|
|
52
|
+
modelValue: null,
|
|
53
|
+
max: 5,
|
|
54
|
+
labels: undefined,
|
|
55
|
+
size: 'md',
|
|
56
|
+
readonly: false,
|
|
57
|
+
disabled: false,
|
|
58
|
+
required: false,
|
|
59
|
+
name: undefined,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const emit = defineEmits<{
|
|
63
|
+
'update:modelValue': [value: number]
|
|
64
|
+
}>()
|
|
65
|
+
|
|
66
|
+
const STAR_SIZE_CLASSES: Record<RatingSize, string> = {
|
|
67
|
+
sm: 'size-4',
|
|
68
|
+
md: 'size-6',
|
|
69
|
+
lg: 'size-8',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type StarState = 'empty' | 'preview' | 'selected'
|
|
73
|
+
type StarFill = 'empty' | 'selected'
|
|
74
|
+
|
|
75
|
+
const STAR_FILL_CLASSES: Record<StarFill, string> = {
|
|
76
|
+
empty: 'i-ph-star text-text-neutral-tertiary',
|
|
77
|
+
selected: 'i-ph-star-fill text-actions-primary-idle',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The pressed overlay stands in for the hover one, too faint on a glyph. It darkens
|
|
81
|
+
// the empty outline and lightens the primary fill, as it does on a primary button.
|
|
82
|
+
const STAR_OVERLAY_CLASSES: Record<StarFill, string> = {
|
|
83
|
+
empty: 'i-ph-star-fill text-actions-neutral-pressed-overlay',
|
|
84
|
+
selected: 'i-ph-star-fill text-actions-neutral-invert-pressed-overlay',
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Vertical padding keeps every star inside a hit area of at least 32px high
|
|
88
|
+
const ITEM_PADDING_CLASSES: Record<RatingSize, string> = {
|
|
89
|
+
sm: 'py-2',
|
|
90
|
+
md: 'py-1',
|
|
91
|
+
lg: 'py-1',
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Every star gets the same fixed column, wider with captions so their length
|
|
95
|
+
// never changes the star spacing. Only sm trades the 32px hit width for density.
|
|
96
|
+
const ITEM_WIDTH_CLASSES: Record<RatingSize, { plain: string, captioned: string }> = {
|
|
97
|
+
sm: { plain: 'w-6', captioned: 'w-12' },
|
|
98
|
+
md: { plain: 'w-8', captioned: 'w-12' },
|
|
99
|
+
lg: { plain: 'w-10', captioned: 'w-15' },
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Mirror the value locally so the stars follow a selection even when the
|
|
103
|
+
// consumer does not bind v-model (uncontrolled usage)
|
|
104
|
+
const currentValue = ref<number | null>(props.modelValue)
|
|
105
|
+
|
|
106
|
+
watch(() => props.modelValue, (value) => {
|
|
107
|
+
currentValue.value = value
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const hoveredValue = ref<number | null>(null)
|
|
111
|
+
|
|
112
|
+
const isInteractive = computed(() => !props.readonly && !props.disabled)
|
|
113
|
+
|
|
114
|
+
// A rating locked under the pointer drops the preview straight away
|
|
115
|
+
const previewValue = computed(() => isInteractive.value ? hoveredValue.value : null)
|
|
116
|
+
|
|
117
|
+
const hasCaptions = computed(() => (props.labels?.length ?? 0) > 0)
|
|
118
|
+
|
|
119
|
+
const rootClasses = computed(() => [
|
|
120
|
+
'flex w-fit gap-1',
|
|
121
|
+
{ 'opacity-50': props.disabled },
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
const cursorClass = computed(() => {
|
|
125
|
+
if (props.disabled) {
|
|
126
|
+
return 'cursor-not-allowed'
|
|
127
|
+
}
|
|
128
|
+
if (props.readonly) {
|
|
129
|
+
return 'cursor-default'
|
|
130
|
+
}
|
|
131
|
+
return 'cursor-pointer'
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const itemClasses = computed(() => [
|
|
135
|
+
'group/star flex flex-col items-center gap-2 rounded-md',
|
|
136
|
+
ITEM_PADDING_CLASSES[props.size],
|
|
137
|
+
ITEM_WIDTH_CLASSES[props.size][hasCaptions.value ? 'captioned' : 'plain'],
|
|
138
|
+
cursorClass.value,
|
|
139
|
+
])
|
|
140
|
+
|
|
141
|
+
function starFill(value: number): StarFill {
|
|
142
|
+
return value <= (currentValue.value ?? 0) ? 'selected' : 'empty'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Past the selection only the added stars preview; within it, the stars up to
|
|
146
|
+
// the pointer do. The rest of the selection stays as it is.
|
|
147
|
+
function starState(value: number): StarState {
|
|
148
|
+
const selected = currentValue.value ?? 0
|
|
149
|
+
const preview = previewValue.value
|
|
150
|
+
|
|
151
|
+
if (preview !== null) {
|
|
152
|
+
const previewFrom = preview > selected ? selected + 1 : 1
|
|
153
|
+
if (value >= previewFrom && value <= preview) {
|
|
154
|
+
return 'preview'
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return starFill(value)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function starClasses(value: number): string[] {
|
|
161
|
+
return [STAR_SIZE_CLASSES[props.size], STAR_FILL_CLASSES[starFill(value)]]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function overlayClasses(value: number): string[] {
|
|
165
|
+
return [STAR_SIZE_CLASSES[props.size], STAR_OVERLAY_CLASSES[starFill(value)]]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// An empty caption counts as a missing one, so the star keeps its number as a name
|
|
169
|
+
function labelFor(value: number): string | undefined {
|
|
170
|
+
return props.labels?.[value - 1] || undefined
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// A star without a caption keeps its number as a visually hidden name
|
|
174
|
+
function captionClasses(value: number): string {
|
|
175
|
+
if (!labelFor(value)) {
|
|
176
|
+
return 'sr-only'
|
|
177
|
+
}
|
|
178
|
+
return 'text-content-legends-medium-md text-center wrap-break-word text-text-neutral-secondary group-data-[state=checked]/star:text-text-primary-base'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function onUpdateModelValue(value: AcceptableValue) {
|
|
182
|
+
currentValue.value = value as number
|
|
183
|
+
emit('update:modelValue', value as number)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function onSelect(event: RadioGroupItemEmits['select'][0]) {
|
|
187
|
+
if (props.readonly) {
|
|
188
|
+
event.preventDefault()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function onMouseEnter(value: number) {
|
|
193
|
+
if (isInteractive.value) {
|
|
194
|
+
hoveredValue.value = value
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function onMouseLeave() {
|
|
199
|
+
hoveredValue.value = null
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// RadioGroupRoot is a generic component, so InstanceType<typeof RadioGroupRoot> can't be used
|
|
203
|
+
const rootRef = ref<ComponentPublicInstance | null>(null)
|
|
204
|
+
|
|
205
|
+
// Focuses the selected star, or the first one when nothing is selected
|
|
206
|
+
function focus() {
|
|
207
|
+
const root = rootRef.value?.$el as HTMLElement | undefined
|
|
208
|
+
const star = root?.querySelector<HTMLElement>('[role="radio"][aria-checked="true"]') ??
|
|
209
|
+
root?.querySelector<HTMLElement>('[role="radio"]')
|
|
210
|
+
star?.focus()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
defineExpose({ focus })
|
|
214
|
+
</script>
|
|
215
|
+
|
|
216
|
+
<template>
|
|
217
|
+
<RadioGroupRoot
|
|
218
|
+
ref="rootRef"
|
|
219
|
+
:model-value="currentValue"
|
|
220
|
+
:disabled="disabled"
|
|
221
|
+
:required="required"
|
|
222
|
+
:name="name"
|
|
223
|
+
:loop="false"
|
|
224
|
+
:aria-readonly="readonly || undefined"
|
|
225
|
+
:class="rootClasses"
|
|
226
|
+
@update:model-value="onUpdateModelValue"
|
|
227
|
+
@mouseleave="onMouseLeave"
|
|
228
|
+
>
|
|
229
|
+
<RadioGroupItem
|
|
230
|
+
v-for="value in max"
|
|
231
|
+
:key="value"
|
|
232
|
+
:value="value"
|
|
233
|
+
:class="itemClasses"
|
|
234
|
+
@select="onSelect"
|
|
235
|
+
@mouseenter="onMouseEnter(value)"
|
|
236
|
+
>
|
|
237
|
+
<!-- The star under the pointer grows a little so the hovered one stands out of the preview -->
|
|
238
|
+
<span
|
|
239
|
+
aria-hidden="true"
|
|
240
|
+
class="relative motion-safe:transition-transform"
|
|
241
|
+
:class="[STAR_SIZE_CLASSES[size], { 'scale-110': previewValue === value }]"
|
|
242
|
+
>
|
|
243
|
+
<span
|
|
244
|
+
data-testid="ui-rating-star"
|
|
245
|
+
:data-state="starState(value)"
|
|
246
|
+
:class="starClasses(value)"
|
|
247
|
+
/>
|
|
248
|
+
<span
|
|
249
|
+
v-if="starState(value) === 'preview'"
|
|
250
|
+
data-testid="ui-rating-star-overlay"
|
|
251
|
+
class="absolute inset-0"
|
|
252
|
+
:class="overlayClasses(value)"
|
|
253
|
+
/>
|
|
254
|
+
</span>
|
|
255
|
+
<span :class="captionClasses(value)">{{ labelFor(value) ?? value }}</span>
|
|
256
|
+
</RadioGroupItem>
|
|
257
|
+
</RadioGroupRoot>
|
|
258
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type RatingSize = 'sm' | 'md' | 'lg'
|
|
@@ -4,6 +4,7 @@ import UiTooltipProvider from "../UiTooltip/UiTooltipProvider.vue";
|
|
|
4
4
|
import UiTooltip from "../UiTooltip/UiTooltip.vue";
|
|
5
5
|
import UiTooltipTrigger from "../UiTooltip/UiTooltipTrigger.vue";
|
|
6
6
|
import UiTooltipContent from "../UiTooltip/UiTooltipContent.vue";
|
|
7
|
+
import UiSpinnerIcon from "../UiLoader/UiSpinnerIcon.vue";
|
|
7
8
|
|
|
8
9
|
defineOptions({ inheritAttrs: false });
|
|
9
10
|
|
|
@@ -14,6 +15,7 @@ withDefaults(
|
|
|
14
15
|
icon?: string;
|
|
15
16
|
tooltip?: string;
|
|
16
17
|
tooltipSide?: "top" | "bottom" | "left" | "right";
|
|
18
|
+
tooltipDisabled?: boolean;
|
|
17
19
|
disabled?: boolean;
|
|
18
20
|
active?: boolean;
|
|
19
21
|
loading?: boolean;
|
|
@@ -22,6 +24,7 @@ withDefaults(
|
|
|
22
24
|
variant: "ghost",
|
|
23
25
|
color: "neutral",
|
|
24
26
|
tooltipSide: "top",
|
|
27
|
+
tooltipDisabled: false,
|
|
25
28
|
},
|
|
26
29
|
);
|
|
27
30
|
|
|
@@ -31,7 +34,7 @@ const isIconOnly = computed(() => !slots.default);
|
|
|
31
34
|
|
|
32
35
|
<template>
|
|
33
36
|
<UiTooltipProvider v-if="tooltip">
|
|
34
|
-
<UiTooltip>
|
|
37
|
+
<UiTooltip :disabled="tooltipDisabled">
|
|
35
38
|
<UiTooltipTrigger as-child>
|
|
36
39
|
<button
|
|
37
40
|
v-bind="$attrs"
|
|
@@ -55,27 +58,10 @@ const isIconOnly = computed(() => !slots.default);
|
|
|
55
58
|
]"
|
|
56
59
|
:disabled="disabled || loading"
|
|
57
60
|
>
|
|
58
|
-
<
|
|
61
|
+
<UiSpinnerIcon
|
|
59
62
|
v-if="loading"
|
|
60
63
|
class="size-5 shrink-0 animate-spin"
|
|
61
|
-
|
|
62
|
-
fill="none"
|
|
63
|
-
viewBox="0 0 24 24"
|
|
64
|
-
>
|
|
65
|
-
<circle
|
|
66
|
-
class="opacity-20"
|
|
67
|
-
cx="12"
|
|
68
|
-
cy="12"
|
|
69
|
-
r="10"
|
|
70
|
-
stroke="currentColor"
|
|
71
|
-
stroke-width="4"
|
|
72
|
-
/>
|
|
73
|
-
<path
|
|
74
|
-
class="opacity-100"
|
|
75
|
-
fill="currentColor"
|
|
76
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
77
|
-
/>
|
|
78
|
-
</svg>
|
|
64
|
+
/>
|
|
79
65
|
<div
|
|
80
66
|
v-else-if="icon"
|
|
81
67
|
class="size-5 shrink-0"
|
|
@@ -115,27 +101,10 @@ const isIconOnly = computed(() => !slots.default);
|
|
|
115
101
|
]"
|
|
116
102
|
:disabled="disabled || loading"
|
|
117
103
|
>
|
|
118
|
-
<
|
|
104
|
+
<UiSpinnerIcon
|
|
119
105
|
v-if="loading"
|
|
120
106
|
class="size-5 shrink-0 animate-spin"
|
|
121
|
-
|
|
122
|
-
fill="none"
|
|
123
|
-
viewBox="0 0 24 24"
|
|
124
|
-
>
|
|
125
|
-
<circle
|
|
126
|
-
class="opacity-20"
|
|
127
|
-
cx="12"
|
|
128
|
-
cy="12"
|
|
129
|
-
r="10"
|
|
130
|
-
stroke="currentColor"
|
|
131
|
-
stroke-width="4"
|
|
132
|
-
/>
|
|
133
|
-
<path
|
|
134
|
-
class="opacity-100"
|
|
135
|
-
fill="currentColor"
|
|
136
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
137
|
-
/>
|
|
138
|
-
</svg>
|
|
107
|
+
/>
|
|
139
108
|
<div
|
|
140
109
|
v-else-if="icon"
|
|
141
110
|
class="size-5 shrink-0"
|
|
@@ -156,7 +125,7 @@ const isIconOnly = computed(() => !slots.default);
|
|
|
156
125
|
}
|
|
157
126
|
|
|
158
127
|
.ui-room-button--ghost.ui-room-button--neutral:not(:disabled):hover {
|
|
159
|
-
background: rgba(245, 247, 252, 0.1);
|
|
128
|
+
background: var(--color-actions-neutral-hover-overlay, rgba(245, 247, 252, 0.1));
|
|
160
129
|
}
|
|
161
130
|
|
|
162
131
|
.ui-room-button--ghost.ui-room-button--danger {
|
|
@@ -182,7 +151,7 @@ const isIconOnly = computed(() => !slots.default);
|
|
|
182
151
|
}
|
|
183
152
|
|
|
184
153
|
.ui-room-button--glass.ui-room-button--neutral:not(:disabled):hover {
|
|
185
|
-
background: rgba(245, 247, 252, 0.1);
|
|
154
|
+
background: var(--color-actions-neutral-hover-overlay, rgba(245, 247, 252, 0.1));
|
|
186
155
|
}
|
|
187
156
|
|
|
188
157
|
.ui-room-button--ghost.ui-room-button--primary {
|