@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
|
@@ -3,11 +3,14 @@ import { ToggleGroupRoot } from 'reka-ui'
|
|
|
3
3
|
import type { AcceptableValue } from 'reka-ui'
|
|
4
4
|
import { useDebounceFn, useResizeObserver } from '@vueuse/core'
|
|
5
5
|
import { computed, nextTick, onMounted, provide, ref, watch } from 'vue'
|
|
6
|
-
import type { ButtonGroupColor, ButtonGroupSize } from './types'
|
|
6
|
+
import type { ButtonGroupColor, ButtonGroupSize, ButtonGroupVariant } from './types'
|
|
7
7
|
import {
|
|
8
8
|
BUTTON_GROUP_COLOR_KEY,
|
|
9
|
+
BUTTON_GROUP_INNER_RADIUS,
|
|
10
|
+
BUTTON_GROUP_RAIL_RADIUS,
|
|
9
11
|
BUTTON_GROUP_REGISTER_KEY,
|
|
10
12
|
BUTTON_GROUP_SIZE_KEY,
|
|
13
|
+
BUTTON_GROUP_VARIANT_KEY,
|
|
11
14
|
} from './types'
|
|
12
15
|
|
|
13
16
|
interface UiButtonGroupProps {
|
|
@@ -16,6 +19,8 @@ interface UiButtonGroupProps {
|
|
|
16
19
|
disabled?: boolean
|
|
17
20
|
size?: ButtonGroupSize
|
|
18
21
|
color?: ButtonGroupColor
|
|
22
|
+
/** `strong` fills the selected item with the color; `light` raises it on the surface and writes its label in the color. */
|
|
23
|
+
variant?: ButtonGroupVariant
|
|
19
24
|
name?: string
|
|
20
25
|
orientation?: 'horizontal' | 'vertical'
|
|
21
26
|
/**
|
|
@@ -24,14 +29,18 @@ interface UiButtonGroupProps {
|
|
|
24
29
|
* Set to true to allow clearing the selection by clicking the active item.
|
|
25
30
|
*/
|
|
26
31
|
allowDeselect?: boolean
|
|
32
|
+
/** When false, the group hugs its items instead of filling its container. */
|
|
33
|
+
fullWidth?: boolean
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
const props = withDefaults(defineProps<UiButtonGroupProps>(), {
|
|
30
37
|
size: 'md',
|
|
31
38
|
color: 'neutral',
|
|
39
|
+
variant: 'strong',
|
|
32
40
|
orientation: 'horizontal',
|
|
33
41
|
disabled: false,
|
|
34
42
|
allowDeselect: false,
|
|
43
|
+
fullWidth: true,
|
|
35
44
|
modelValue: undefined,
|
|
36
45
|
defaultValue: undefined,
|
|
37
46
|
name: undefined,
|
|
@@ -43,6 +52,7 @@ const emit = defineEmits<{
|
|
|
43
52
|
|
|
44
53
|
provide(BUTTON_GROUP_SIZE_KEY, computed(() => props.size))
|
|
45
54
|
provide(BUTTON_GROUP_COLOR_KEY, computed(() => props.color))
|
|
55
|
+
provide(BUTTON_GROUP_VARIANT_KEY, computed(() => props.variant))
|
|
46
56
|
|
|
47
57
|
// --- Sliding indicator -----------------------------------------------------
|
|
48
58
|
// Each item registers its DOM node on mount. When the selected value or
|
|
@@ -85,12 +95,10 @@ function updateIndicator () {
|
|
|
85
95
|
return
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
const rootRect = rootEl.getBoundingClientRect()
|
|
89
|
-
const itemRect = activeEl.getBoundingClientRect()
|
|
90
98
|
indicatorStyle.value = {
|
|
91
|
-
transform: `translate(${
|
|
92
|
-
width: `${
|
|
93
|
-
height: `${
|
|
99
|
+
transform: `translate(${activeEl.offsetLeft}px, ${activeEl.offsetTop}px)`,
|
|
100
|
+
width: `${activeEl.offsetWidth}px`,
|
|
101
|
+
height: `${activeEl.offsetHeight}px`,
|
|
94
102
|
}
|
|
95
103
|
indicatorVisible.value = true
|
|
96
104
|
}
|
|
@@ -124,6 +132,7 @@ useResizeObserver(resizeTarget, debouncedUpdate)
|
|
|
124
132
|
|
|
125
133
|
const sizeClasses = computed(() => {
|
|
126
134
|
switch (props.size) {
|
|
135
|
+
case 'xs': return 'h-6 p-0.5 gap-0.5'
|
|
127
136
|
case 'sm': return 'h-8 p-0.5 gap-0.5'
|
|
128
137
|
case 'lg': return 'h-12 p-1 gap-1'
|
|
129
138
|
case 'md':
|
|
@@ -149,15 +158,12 @@ const INDICATOR_BG: Record<ButtonGroupColor, string> = {
|
|
|
149
158
|
'error': 'bg-actions-danger-idle',
|
|
150
159
|
}
|
|
151
160
|
|
|
152
|
-
const INDICATOR_RADIUS: Record<ButtonGroupSize, string> = {
|
|
153
|
-
sm: 'rounded-md',
|
|
154
|
-
md: 'rounded-lg',
|
|
155
|
-
lg: 'rounded-lg',
|
|
156
|
-
}
|
|
157
|
-
|
|
158
161
|
const railBgClass = computed(() => RAIL_BG[props.color])
|
|
159
|
-
const indicatorBgClass = computed(() =>
|
|
160
|
-
|
|
162
|
+
const indicatorBgClass = computed(() => props.variant === 'light'
|
|
163
|
+
? 'bg-surface-neutral-bg-main shadow-elevation-level-1'
|
|
164
|
+
: INDICATOR_BG[props.color])
|
|
165
|
+
const indicatorRadiusClass = computed(() => BUTTON_GROUP_INNER_RADIUS[props.size])
|
|
166
|
+
const railRadiusClass = computed(() => BUTTON_GROUP_RAIL_RADIUS[props.size])
|
|
161
167
|
|
|
162
168
|
function handleUpdate (value: AcceptableValue | AcceptableValue[]) {
|
|
163
169
|
const next = Array.isArray(value) ? value[0] : value
|
|
@@ -183,8 +189,8 @@ function handleUpdate (value: AcceptableValue | AcceptableValue[]) {
|
|
|
183
189
|
:name="name"
|
|
184
190
|
:roving-focus="true"
|
|
185
191
|
type="single"
|
|
186
|
-
class="relative inline-flex
|
|
187
|
-
:class="[sizeClasses, railBgClass, { 'flex-col h-auto': orientation === 'vertical' }]"
|
|
192
|
+
class="relative inline-flex items-stretch"
|
|
193
|
+
:class="[fullWidth ? 'w-full' : 'w-fit', sizeClasses, railRadiusClass, railBgClass, { 'flex-col h-auto': orientation === 'vertical' }]"
|
|
188
194
|
@update:model-value="handleUpdate"
|
|
189
195
|
>
|
|
190
196
|
<div
|
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ToggleGroupItem } from 'reka-ui'
|
|
3
3
|
import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
4
|
-
import
|
|
4
|
+
import { useIconSize } from '../../composables/useIconSize'
|
|
5
|
+
import type { ButtonGroupColor, ButtonGroupVariant } from './types'
|
|
5
6
|
import {
|
|
6
7
|
BUTTON_GROUP_COLOR_KEY,
|
|
8
|
+
BUTTON_GROUP_INNER_RADIUS,
|
|
7
9
|
BUTTON_GROUP_REGISTER_KEY,
|
|
8
10
|
BUTTON_GROUP_SIZE_KEY,
|
|
11
|
+
BUTTON_GROUP_VARIANT_KEY,
|
|
9
12
|
} from './types'
|
|
10
13
|
|
|
11
14
|
interface UiButtonGroupItemProps {
|
|
12
15
|
value: string
|
|
13
16
|
disabled?: boolean
|
|
14
17
|
ariaLabel?: string
|
|
18
|
+
/** Icon class (e.g. "i-ph-text-align-left") rendered before the slot, sized per group size. */
|
|
19
|
+
icon?: string
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
const props = withDefaults(defineProps<UiButtonGroupItemProps>(), {
|
|
18
23
|
disabled: false,
|
|
19
24
|
ariaLabel: undefined,
|
|
25
|
+
icon: undefined,
|
|
20
26
|
})
|
|
21
27
|
|
|
22
28
|
const size = inject(BUTTON_GROUP_SIZE_KEY, computed(() => 'md' as const))
|
|
23
29
|
const color = inject(BUTTON_GROUP_COLOR_KEY, computed<ButtonGroupColor>(() => 'neutral'))
|
|
30
|
+
const variant = inject(BUTTON_GROUP_VARIANT_KEY, computed<ButtonGroupVariant>(() => 'strong'))
|
|
24
31
|
const registry = inject(BUTTON_GROUP_REGISTER_KEY, null)
|
|
25
32
|
|
|
26
33
|
const itemRef = ref<InstanceType<typeof ToggleGroupItem> | null>(null)
|
|
@@ -55,13 +62,16 @@ onBeforeUnmount(() => {
|
|
|
55
62
|
// (rail height − rail padding) in horizontal mode, and drives height in vertical mode.
|
|
56
63
|
const sizeClasses = computed(() => {
|
|
57
64
|
switch (size.value) {
|
|
58
|
-
case '
|
|
59
|
-
case '
|
|
65
|
+
case 'xs': return 'px-2 py-0.5 gap-1 text-action-button-sm'
|
|
66
|
+
case 'sm': return 'px-2.5 py-1.5 gap-1 text-action-button-sm'
|
|
67
|
+
case 'lg': return 'px-6 py-2.5 gap-2 text-action-button-lg'
|
|
60
68
|
case 'md':
|
|
61
|
-
default: return 'px-4 py-2.5 gap-1.5
|
|
69
|
+
default: return 'px-4 py-2.5 gap-1.5 text-action-button-md'
|
|
62
70
|
}
|
|
63
71
|
})
|
|
64
72
|
|
|
73
|
+
const iconSizeClass = useIconSize(size)
|
|
74
|
+
|
|
65
75
|
// Per-color state classes. The selected fill is drawn by the parent's sliding
|
|
66
76
|
// indicator <div>, so items only set text color per state. Hover bg/text apply
|
|
67
77
|
// only to unselected items (data-[state=off]) so the hover tint never stacks
|
|
@@ -71,41 +81,56 @@ const COLOR_CLASSES: Record<ButtonGroupColor, string> = {
|
|
|
71
81
|
'text-actions-neutral-idle-alpha-strong',
|
|
72
82
|
'data-[state=off]:hover:bg-actions-neutral-hover-overlay',
|
|
73
83
|
'data-[state=off]:hover:text-actions-neutral-idle',
|
|
74
|
-
'data-[state=on]:text-actions-neutral-invert-idle',
|
|
75
84
|
].join(' '),
|
|
76
85
|
'neutral-invert': [
|
|
77
86
|
'text-actions-neutral-invert-idle-alpha-strong',
|
|
78
87
|
'data-[state=off]:hover:bg-actions-neutral-invert-hover-overlay',
|
|
79
88
|
'data-[state=off]:hover:text-actions-neutral-invert-idle',
|
|
80
|
-
'data-[state=on]:text-actions-neutral-idle',
|
|
81
89
|
].join(' '),
|
|
82
90
|
'primary': [
|
|
83
91
|
'text-actions-primary-idle-alpha-strong',
|
|
84
92
|
'data-[state=off]:hover:bg-actions-primary-hover-overlay',
|
|
85
93
|
'data-[state=off]:hover:text-actions-primary-idle',
|
|
86
|
-
'data-[state=on]:text-actions-neutral-invert-idle',
|
|
87
94
|
].join(' '),
|
|
88
95
|
'success': [
|
|
89
96
|
'text-actions-success-idle-alpha-strong',
|
|
90
97
|
'data-[state=off]:hover:bg-actions-success-hover-overlay',
|
|
91
98
|
'data-[state=off]:hover:text-actions-success-idle',
|
|
92
|
-
'data-[state=on]:text-actions-neutral-invert-idle',
|
|
93
99
|
].join(' '),
|
|
94
100
|
'warning': [
|
|
95
101
|
'text-actions-warning-idle-alpha-strong',
|
|
96
102
|
'data-[state=off]:hover:bg-actions-warning-hover-overlay',
|
|
97
103
|
'data-[state=off]:hover:text-actions-warning-idle',
|
|
98
|
-
'data-[state=on]:text-actions-neutral-invert-idle',
|
|
99
104
|
].join(' '),
|
|
100
105
|
'error': [
|
|
101
106
|
'text-actions-danger-idle-alpha-strong',
|
|
102
107
|
'data-[state=off]:hover:bg-actions-danger-hover-overlay',
|
|
103
108
|
'data-[state=off]:hover:text-actions-danger-idle',
|
|
104
|
-
'data-[state=on]:text-actions-neutral-invert-idle',
|
|
105
109
|
].join(' '),
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
const
|
|
112
|
+
const STRONG_SELECTED_TEXT: Record<ButtonGroupColor, string> = {
|
|
113
|
+
'neutral': 'data-[state=on]:text-actions-neutral-invert-idle',
|
|
114
|
+
'neutral-invert': 'data-[state=on]:text-actions-neutral-idle',
|
|
115
|
+
'primary': 'data-[state=on]:text-actions-neutral-invert-idle',
|
|
116
|
+
'success': 'data-[state=on]:text-actions-neutral-invert-idle',
|
|
117
|
+
'warning': 'data-[state=on]:text-actions-neutral-invert-idle',
|
|
118
|
+
'error': 'data-[state=on]:text-actions-neutral-invert-idle',
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const LIGHT_SELECTED_TEXT: Record<ButtonGroupColor, string> = {
|
|
122
|
+
'neutral': 'data-[state=on]:text-actions-neutral-idle',
|
|
123
|
+
'neutral-invert': 'data-[state=on]:text-actions-neutral-idle',
|
|
124
|
+
'primary': 'data-[state=on]:text-actions-primary-idle',
|
|
125
|
+
'success': 'data-[state=on]:text-actions-success-idle',
|
|
126
|
+
'warning': 'data-[state=on]:text-actions-warning-idle',
|
|
127
|
+
'error': 'data-[state=on]:text-actions-danger-idle',
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const colorClasses = computed(() => [
|
|
131
|
+
COLOR_CLASSES[color.value],
|
|
132
|
+
(variant.value === 'light' ? LIGHT_SELECTED_TEXT : STRONG_SELECTED_TEXT)[color.value],
|
|
133
|
+
])
|
|
109
134
|
</script>
|
|
110
135
|
|
|
111
136
|
<template>
|
|
@@ -114,12 +139,17 @@ const colorClasses = computed(() => COLOR_CLASSES[color.value])
|
|
|
114
139
|
:value="value"
|
|
115
140
|
:disabled="disabled"
|
|
116
141
|
:aria-label="ariaLabel"
|
|
117
|
-
class="relative z-10 flex-1 inline-flex items-center justify-center transition-colors cursor-pointer outline-none
|
|
118
|
-
focus-visible:
|
|
142
|
+
class="relative z-10 flex-1 inline-flex items-center justify-center whitespace-nowrap transition-colors cursor-pointer outline-none
|
|
143
|
+
focus-visible:focus-ring
|
|
119
144
|
data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed
|
|
120
145
|
data-[state=off]:data-[disabled]:hover:bg-transparent"
|
|
121
|
-
:class="[sizeClasses, colorClasses]"
|
|
146
|
+
:class="[sizeClasses, BUTTON_GROUP_INNER_RADIUS[size], colorClasses]"
|
|
122
147
|
>
|
|
148
|
+
<span
|
|
149
|
+
v-if="icon"
|
|
150
|
+
:class="[icon, iconSizeClass]"
|
|
151
|
+
class="shrink-0"
|
|
152
|
+
/>
|
|
123
153
|
<slot />
|
|
124
154
|
</ToggleGroupItem>
|
|
125
155
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComputedRef, InjectionKey } from 'vue'
|
|
2
2
|
|
|
3
|
-
export type ButtonGroupSize = 'sm' | 'md' | 'lg'
|
|
3
|
+
export type ButtonGroupSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
4
4
|
export type ButtonGroupColor =
|
|
5
5
|
| 'neutral'
|
|
6
6
|
| 'neutral-invert'
|
|
@@ -8,6 +8,7 @@ export type ButtonGroupColor =
|
|
|
8
8
|
| 'success'
|
|
9
9
|
| 'warning'
|
|
10
10
|
| 'error'
|
|
11
|
+
export type ButtonGroupVariant = 'strong' | 'light'
|
|
11
12
|
|
|
12
13
|
export interface ButtonGroupRegister {
|
|
13
14
|
register(value: string, el: HTMLElement): void
|
|
@@ -20,5 +21,23 @@ export const BUTTON_GROUP_SIZE_KEY: InjectionKey<ComputedRef<ButtonGroupSize>> =
|
|
|
20
21
|
export const BUTTON_GROUP_COLOR_KEY: InjectionKey<ComputedRef<ButtonGroupColor>> =
|
|
21
22
|
Symbol('ui-button-group-color')
|
|
22
23
|
|
|
24
|
+
export const BUTTON_GROUP_VARIANT_KEY: InjectionKey<ComputedRef<ButtonGroupVariant>> =
|
|
25
|
+
Symbol('ui-button-group-variant')
|
|
26
|
+
|
|
23
27
|
export const BUTTON_GROUP_REGISTER_KEY: InjectionKey<ButtonGroupRegister> =
|
|
24
28
|
Symbol('ui-button-group-register')
|
|
29
|
+
|
|
30
|
+
export const BUTTON_GROUP_RAIL_RADIUS: Record<ButtonGroupSize, string> = {
|
|
31
|
+
xs: 'rounded-button-xs',
|
|
32
|
+
sm: 'rounded-button-sm',
|
|
33
|
+
md: 'rounded-button-md',
|
|
34
|
+
lg: 'rounded-field-lg',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Inner radius stays concentric with the rail: rail radius minus rail padding.
|
|
38
|
+
export const BUTTON_GROUP_INNER_RADIUS: Record<ButtonGroupSize, string> = {
|
|
39
|
+
xs: 'rounded-[calc(var(--radius-button-xs)_-_2px)]',
|
|
40
|
+
sm: 'rounded-[calc(var(--radius-button-sm)_-_2px)]',
|
|
41
|
+
md: 'rounded-[calc(var(--radius-button-md)_-_2px)]',
|
|
42
|
+
lg: 'rounded-[calc(var(--radius-field-lg)_-_4px)]',
|
|
43
|
+
}
|
|
@@ -23,35 +23,40 @@ const props = withDefaults(defineProps<UiCardProps>(), {
|
|
|
23
23
|
disabled: false,
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
const baseCardClasses = 'bg-surface-neutral-
|
|
26
|
+
const baseCardClasses = 'bg-surface-neutral-bg-main'
|
|
27
27
|
|
|
28
28
|
const variantClasses = computed(() => {
|
|
29
29
|
if (props.selected) {
|
|
30
|
-
return 'bg-surface-primary-alpha-100
|
|
30
|
+
return 'bg-surface-primary-alpha-100 ring-2 ring-actions-primary-idle'
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
switch (props.variant) {
|
|
34
34
|
case 'success':
|
|
35
|
-
return 'bg-surface-success-alpha-200
|
|
35
|
+
return 'bg-surface-success-alpha-200'
|
|
36
36
|
case 'warning':
|
|
37
|
-
return 'bg-surface-warning-alpha-100
|
|
37
|
+
return 'bg-surface-warning-alpha-100'
|
|
38
38
|
case 'danger':
|
|
39
|
-
return 'bg-surface-danger-alpha-100
|
|
39
|
+
return 'bg-surface-danger-alpha-100'
|
|
40
40
|
case 'primary':
|
|
41
|
-
return 'bg-surface-primary-alpha-100
|
|
41
|
+
return 'bg-surface-primary-alpha-100'
|
|
42
42
|
case 'primary-fill':
|
|
43
|
-
return 'bg-surface-primary-bg-alpha
|
|
43
|
+
return 'bg-surface-primary-bg-alpha'
|
|
44
44
|
case 'transparent':
|
|
45
|
-
return 'bg-transparent
|
|
45
|
+
return 'bg-transparent'
|
|
46
46
|
case 'neutral':
|
|
47
|
-
return 'bg-surface-neutral-alpha-100 border border-borders-neutral-light'
|
|
48
47
|
case 'white':
|
|
49
|
-
return
|
|
48
|
+
return baseCardClasses
|
|
50
49
|
default:
|
|
51
50
|
return baseCardClasses
|
|
52
51
|
}
|
|
53
52
|
})
|
|
54
53
|
|
|
54
|
+
// A selected card is always a choice; a disabled one cannot be acted on.
|
|
55
|
+
const borderClasses = computed(() => {
|
|
56
|
+
const isInteractive = props.selected || (props.interactive && !props.disabled)
|
|
57
|
+
return isInteractive ? 'border border-container-interactive-border' : 'border border-container-border'
|
|
58
|
+
})
|
|
59
|
+
|
|
55
60
|
const paddingClasses = computed(() => {
|
|
56
61
|
// Based on Figma: default padding is 32px (p-8)
|
|
57
62
|
switch (props.padding) {
|
|
@@ -70,11 +75,7 @@ const paddingClasses = computed(() => {
|
|
|
70
75
|
|
|
71
76
|
const interactiveClasses = computed(() => {
|
|
72
77
|
if (!props.interactive || props.disabled) return ''
|
|
73
|
-
|
|
74
|
-
if (props.selected) {
|
|
75
|
-
return 'cursor-pointer transition-all duration-150 hover:shadow-shadows-drop-shadow-lg'
|
|
76
|
-
}
|
|
77
|
-
return 'cursor-pointer transition-all duration-150 hover:ring-1 hover:ring-shadow-neutral-100 hover:shadow-shadows-drop-shadow-lg'
|
|
78
|
+
return 'cursor-pointer transition-all duration-150 hover:bg-surface-neutral-bg-main hover:shadow-container-hover'
|
|
78
79
|
})
|
|
79
80
|
|
|
80
81
|
const disabledClasses = computed(() => {
|
|
@@ -86,23 +87,19 @@ const disabledClasses = computed(() => {
|
|
|
86
87
|
<template>
|
|
87
88
|
<div
|
|
88
89
|
:class="[
|
|
89
|
-
'rounded-
|
|
90
|
+
'rounded-container relative overflow-hidden',
|
|
90
91
|
variantClasses,
|
|
92
|
+
borderClasses,
|
|
93
|
+
'shadow-container',
|
|
91
94
|
paddingClasses,
|
|
92
95
|
interactiveClasses,
|
|
93
96
|
disabledClasses,
|
|
94
97
|
]"
|
|
95
98
|
>
|
|
96
|
-
<!-- Hover overlay for interactive cards -->
|
|
97
|
-
<div
|
|
98
|
-
v-if="interactive && !disabled"
|
|
99
|
-
class="absolute inset-0 bg-actions-neutral-hover-overlay rounded-xl opacity-0 hover:opacity-100 transition-opacity duration-150 pointer-events-none"
|
|
100
|
-
/>
|
|
101
|
-
|
|
102
99
|
<!-- Selected overlay -->
|
|
103
100
|
<div
|
|
104
101
|
v-if="selected"
|
|
105
|
-
class="absolute inset-0 bg-actions-primary-idle-alpha-light rounded-
|
|
102
|
+
class="absolute inset-0 bg-actions-primary-idle-alpha-light rounded-[inherit] pointer-events-none"
|
|
106
103
|
/>
|
|
107
104
|
|
|
108
105
|
<div class="relative z-10 h-full">
|
|
@@ -176,7 +176,7 @@ defineExpose({
|
|
|
176
176
|
variant="tertiary"
|
|
177
177
|
color="primary"
|
|
178
178
|
size="md"
|
|
179
|
-
icon="i-
|
|
179
|
+
icon="i-ph-caret-left"
|
|
180
180
|
label="Previous slide"
|
|
181
181
|
:disabled="!canScrollPrev"
|
|
182
182
|
@click="scrollPrev"
|
|
@@ -205,7 +205,7 @@ defineExpose({
|
|
|
205
205
|
variant="tertiary"
|
|
206
206
|
color="primary"
|
|
207
207
|
size="md"
|
|
208
|
-
icon="i-
|
|
208
|
+
icon="i-ph-caret-right"
|
|
209
209
|
label="Next slide"
|
|
210
210
|
:disabled="!canScrollNext"
|
|
211
211
|
@click="scrollNext"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject, provide, ref } from 'vue'
|
|
3
|
+
import UiInputCheckbox from '../UiInputCheckbox/UiInputCheckbox.vue'
|
|
4
|
+
import UiInputRadio from '../UiInputRadio/UiInputRadio.vue'
|
|
5
|
+
import { RADIO_GROUP_KEY } from '../UiInputRadio/types'
|
|
6
|
+
import UiSwitch from '../UiSwitch/UiSwitch.vue'
|
|
7
|
+
import type { RadioValue } from '../UiInputRadio/types'
|
|
8
|
+
import type { ChoiceControl, ChoiceValue, ControlPosition } from './types'
|
|
9
|
+
import { CHOICE_ROW_KEY } from './types'
|
|
10
|
+
import { useChoiceboxSurface } from '../../composables/useChoiceboxSurface'
|
|
11
|
+
|
|
12
|
+
interface UiChoiceProps {
|
|
13
|
+
/**
|
|
14
|
+
* The control rendered in the row
|
|
15
|
+
*/
|
|
16
|
+
control: ChoiceControl
|
|
17
|
+
/**
|
|
18
|
+
* The label text
|
|
19
|
+
*/
|
|
20
|
+
label: string
|
|
21
|
+
/**
|
|
22
|
+
* Supporting text under the label, announced with the control
|
|
23
|
+
*/
|
|
24
|
+
description?: string
|
|
25
|
+
/**
|
|
26
|
+
* Info tooltip next to the label
|
|
27
|
+
*/
|
|
28
|
+
labelInfo?: string
|
|
29
|
+
/**
|
|
30
|
+
* The value this row selects. Required when `control` is `radio`; `null` is a valid value.
|
|
31
|
+
* A radio row goes inside a UiInputRadioGroup, which owns the selection.
|
|
32
|
+
*/
|
|
33
|
+
value?: RadioValue
|
|
34
|
+
/**
|
|
35
|
+
* Switch and checkbox: whether the row is on. A radio reads its group instead.
|
|
36
|
+
*/
|
|
37
|
+
modelValue?: ChoiceValue
|
|
38
|
+
/**
|
|
39
|
+
* Whether the row is disabled
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Whether the row uses the small height
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
compact?: boolean
|
|
48
|
+
/**
|
|
49
|
+
* Which side of the text the control sits on
|
|
50
|
+
* @default 'end'
|
|
51
|
+
*/
|
|
52
|
+
controlPosition?: ControlPosition
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<UiChoiceProps>(), {
|
|
56
|
+
description: undefined,
|
|
57
|
+
labelInfo: undefined,
|
|
58
|
+
value: undefined,
|
|
59
|
+
modelValue: undefined,
|
|
60
|
+
disabled: false,
|
|
61
|
+
compact: false,
|
|
62
|
+
controlPosition: 'end',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const emit = defineEmits<{
|
|
66
|
+
'update:modelValue': [value: ChoiceValue]
|
|
67
|
+
}>()
|
|
68
|
+
|
|
69
|
+
defineSlots<{
|
|
70
|
+
/** Radio only: extra content under the text, such as a field the option configures */
|
|
71
|
+
content?(): unknown
|
|
72
|
+
/** Radio only: an icon or a logo before the text */
|
|
73
|
+
icon?(): unknown
|
|
74
|
+
}>()
|
|
75
|
+
|
|
76
|
+
const enclosingGroup = inject(RADIO_GROUP_KEY, undefined)
|
|
77
|
+
|
|
78
|
+
if (props.control === 'radio' && props.value === undefined) {
|
|
79
|
+
console.warn(`UiChoice: the radio row "${props.label}" needs a \`value\`: without one it never shows as selected`)
|
|
80
|
+
}
|
|
81
|
+
provide(CHOICE_ROW_KEY, true)
|
|
82
|
+
|
|
83
|
+
// A radio is also disabled by its group, as UiInputRadio decides
|
|
84
|
+
const isDisabled = computed(() => props.disabled || (props.control === 'radio' && (enclosingGroup?.disabled.value ?? false)))
|
|
85
|
+
|
|
86
|
+
const controlRef = ref<HTMLElement | null>(null)
|
|
87
|
+
|
|
88
|
+
const inErroredGroup = computed(() => props.control === 'radio' && (enclosingGroup?.error.value ?? false))
|
|
89
|
+
|
|
90
|
+
const isSelected = computed(() => {
|
|
91
|
+
if (props.control !== 'radio') {
|
|
92
|
+
return props.modelValue === true
|
|
93
|
+
}
|
|
94
|
+
return props.value !== undefined && enclosingGroup?.modelValue.value === props.value
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// `null` is a valid option value; a missing one falls back to an empty string
|
|
98
|
+
const radioValue = computed(() => props.value === undefined ? '' : props.value)
|
|
99
|
+
|
|
100
|
+
const isSwitch = computed(() => props.control === 'switch')
|
|
101
|
+
|
|
102
|
+
// A switch is a setting, not a choice: on, it keeps the idle surface
|
|
103
|
+
const surfaceClasses = useChoiceboxSurface(() => ({
|
|
104
|
+
control: props.control,
|
|
105
|
+
selected: isSelected.value && !isSwitch.value,
|
|
106
|
+
disabled: isDisabled.value,
|
|
107
|
+
error: inErroredGroup.value,
|
|
108
|
+
}))
|
|
109
|
+
|
|
110
|
+
const rowClasses = computed(() => [
|
|
111
|
+
'group/option flex items-center w-full text-content-text-md-regular',
|
|
112
|
+
props.compact ? 'min-h-field-sm px-3 py-1' : 'p-3',
|
|
113
|
+
surfaceClasses.value,
|
|
114
|
+
isDisabled.value ? 'cursor-not-allowed' : 'cursor-pointer',
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
const controlAtEnd = computed(() => props.controlPosition === 'end')
|
|
118
|
+
|
|
119
|
+
// The control handles clicks inside its own box; the row forwards the rest.
|
|
120
|
+
function onRowClick (event: MouseEvent) {
|
|
121
|
+
if (isDisabled.value || controlRef.value?.contains(event.target as Node)) {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
controlRef.value
|
|
125
|
+
?.querySelector<HTMLElement>('[role="radio"], [role="switch"], input[type="checkbox"]')
|
|
126
|
+
?.click()
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<template>
|
|
131
|
+
<div
|
|
132
|
+
data-testid="ui-choice"
|
|
133
|
+
:data-state="isSelected ? 'checked' : 'unchecked'"
|
|
134
|
+
:class="rowClasses"
|
|
135
|
+
@click="onRowClick"
|
|
136
|
+
>
|
|
137
|
+
<div
|
|
138
|
+
ref="controlRef"
|
|
139
|
+
:class="controlAtEnd || (control === 'radio' && $slots.content) ? 'w-full' : 'w-fit'"
|
|
140
|
+
>
|
|
141
|
+
<UiInputRadio
|
|
142
|
+
v-if="control === 'radio'"
|
|
143
|
+
:value="radioValue"
|
|
144
|
+
:label="label"
|
|
145
|
+
:description="description"
|
|
146
|
+
:label-info="labelInfo"
|
|
147
|
+
:disabled="disabled"
|
|
148
|
+
:control-position="controlPosition"
|
|
149
|
+
>
|
|
150
|
+
<template
|
|
151
|
+
v-if="$slots.icon"
|
|
152
|
+
#icon
|
|
153
|
+
>
|
|
154
|
+
<slot name="icon" />
|
|
155
|
+
</template>
|
|
156
|
+
<template
|
|
157
|
+
v-if="$slots.content"
|
|
158
|
+
#content
|
|
159
|
+
>
|
|
160
|
+
<slot name="content" />
|
|
161
|
+
</template>
|
|
162
|
+
</UiInputRadio>
|
|
163
|
+
<UiSwitch
|
|
164
|
+
v-else-if="control === 'switch'"
|
|
165
|
+
:model-value="modelValue === true"
|
|
166
|
+
:label="label"
|
|
167
|
+
:description="description"
|
|
168
|
+
:label-info="labelInfo"
|
|
169
|
+
:disabled="disabled"
|
|
170
|
+
:switch-position="controlAtEnd ? 'right' : 'left'"
|
|
171
|
+
:full-width="controlAtEnd"
|
|
172
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
173
|
+
/>
|
|
174
|
+
<UiInputCheckbox
|
|
175
|
+
v-else
|
|
176
|
+
:model-value="modelValue === true"
|
|
177
|
+
:label="label"
|
|
178
|
+
:description="description"
|
|
179
|
+
:label-info="labelInfo"
|
|
180
|
+
:disabled="disabled"
|
|
181
|
+
:control-position="controlPosition"
|
|
182
|
+
:full-width="controlAtEnd"
|
|
183
|
+
@update:model-value="emit('update:modelValue', $event as boolean)"
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type ChoiceControl = 'radio' | 'switch' | 'checkbox'
|
|
4
|
+
|
|
5
|
+
export type ChoiceValue = boolean
|
|
6
|
+
|
|
7
|
+
/** Which side of the text a radio, checkbox or switch sits on */
|
|
8
|
+
export type ControlPosition = 'start' | 'end'
|
|
9
|
+
|
|
10
|
+
/** Provided by UiChoice, which draws the option row around the control it wraps */
|
|
11
|
+
export const CHOICE_ROW_KEY: InjectionKey<boolean> = Symbol('ui-choice-row')
|
|
@@ -129,7 +129,7 @@ function onPickerChange(value: string): void {
|
|
|
129
129
|
<span
|
|
130
130
|
data-testid="ui-color-field-swatch"
|
|
131
131
|
aria-hidden="true"
|
|
132
|
-
class="block size-full rounded border border-borders-neutral-light"
|
|
132
|
+
class="block size-full rounded-check border border-borders-neutral-light"
|
|
133
133
|
:style="{ backgroundColor: currentHex }"
|
|
134
134
|
/>
|
|
135
135
|
</template>
|
|
@@ -140,7 +140,7 @@ function onPickerChange(value: string): void {
|
|
|
140
140
|
align="start"
|
|
141
141
|
:side-offset="8"
|
|
142
142
|
data-colors-semantic="light"
|
|
143
|
-
class="border-none shadow-none
|
|
143
|
+
class="border-none shadow-none"
|
|
144
144
|
@open-auto-focus.prevent
|
|
145
145
|
>
|
|
146
146
|
<UiColorPicker
|