@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
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
</span>
|
|
17
17
|
|
|
18
18
|
<!-- Label content -->
|
|
19
|
-
<span
|
|
19
|
+
<span
|
|
20
|
+
v-if="$slots.default || label"
|
|
21
|
+
class="ui-tag-label"
|
|
22
|
+
>
|
|
20
23
|
<slot>{{ label }}</slot>
|
|
21
24
|
</span>
|
|
22
25
|
</span>
|
|
@@ -25,30 +28,9 @@
|
|
|
25
28
|
<script setup lang="ts">
|
|
26
29
|
import { computed } from 'vue'
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Size of the tag
|
|
31
|
-
*/
|
|
32
|
-
size?: 'xxs' | 'xs' | 'sm' | 'md'
|
|
33
|
-
/**
|
|
34
|
-
* Color variant of the tag
|
|
35
|
-
*/
|
|
36
|
-
color?: 'primary' | 'neutral' | 'danger' | 'warning' | 'success' | 'neutral-invert'
|
|
37
|
-
/**
|
|
38
|
-
* Variant of the tag
|
|
39
|
-
*/
|
|
40
|
-
variant?: 'light' | 'strong'
|
|
41
|
-
/**
|
|
42
|
-
* Label text for the tag
|
|
43
|
-
*/
|
|
44
|
-
label?: string
|
|
45
|
-
/**
|
|
46
|
-
* Whether to show hover effects
|
|
47
|
-
*/
|
|
48
|
-
withHoverEffect?: boolean
|
|
49
|
-
}
|
|
31
|
+
import type { UiTagProps } from './UiTag.types'
|
|
50
32
|
|
|
51
|
-
const props = withDefaults(defineProps<
|
|
33
|
+
const props = withDefaults(defineProps<UiTagProps>(), {
|
|
52
34
|
size: 'md',
|
|
53
35
|
color: 'neutral',
|
|
54
36
|
variant: 'light',
|
|
@@ -61,7 +43,7 @@ const tagClasses = computed(() => {
|
|
|
61
43
|
'ui-tag',
|
|
62
44
|
'inline-flex',
|
|
63
45
|
'items-center',
|
|
64
|
-
'rounded-
|
|
46
|
+
'rounded-full',
|
|
65
47
|
'transition-colors',
|
|
66
48
|
'duration-150',
|
|
67
49
|
'ease-in-out',
|
|
@@ -71,36 +53,36 @@ const tagClasses = computed(() => {
|
|
|
71
53
|
// Size classes
|
|
72
54
|
const sizeClasses = {
|
|
73
55
|
xxs: ['px-1.5', 'gap-1', 'text-content-badges-bold-md', 'h-4'],
|
|
74
|
-
xs: ['px-
|
|
75
|
-
sm: ['px-
|
|
76
|
-
md: ['px-
|
|
56
|
+
xs: ['px-2', 'py-1', 'gap-1', 'text-content-badges-bold-md', 'h-6'],
|
|
57
|
+
sm: ['px-3', 'py-1', 'gap-1', 'text-content-text-md-bold', 'h-8'],
|
|
58
|
+
md: ['px-4', 'py-1', 'gap-2', 'text-content-text-md-bold', 'h-10'],
|
|
77
59
|
}
|
|
78
60
|
|
|
79
61
|
// Color and variant classes
|
|
80
62
|
const colorVariantClasses = {
|
|
81
63
|
primary: {
|
|
82
|
-
light: ['bg-actions-primary-active-overlay', 'text-
|
|
83
|
-
strong: ['bg-actions-primary-idle-alpha-strong', 'text-
|
|
64
|
+
light: ['bg-actions-primary-active-overlay', 'text-text-primary-title'],
|
|
65
|
+
strong: ['bg-actions-primary-idle-alpha-strong', 'text-text-neutral-invert-base'],
|
|
84
66
|
},
|
|
85
67
|
neutral: {
|
|
86
|
-
light: ['bg-actions-neutral-active-overlay', 'text-
|
|
87
|
-
strong: ['bg-actions-neutral-idle-alpha-strong', 'text-
|
|
68
|
+
light: ['bg-actions-neutral-active-overlay', 'text-text-neutral-base'],
|
|
69
|
+
strong: ['bg-actions-neutral-idle-alpha-strong', 'text-text-neutral-invert-base'],
|
|
88
70
|
},
|
|
89
71
|
danger: {
|
|
90
|
-
light: ['bg-actions-danger-active-overlay', 'text-
|
|
91
|
-
strong: ['bg-actions-danger-idle-alpha-strong', 'text-
|
|
72
|
+
light: ['bg-actions-danger-active-overlay', 'text-text-danger-base'],
|
|
73
|
+
strong: ['bg-actions-danger-idle-alpha-strong', 'text-text-neutral-invert-base'],
|
|
92
74
|
},
|
|
93
75
|
warning: {
|
|
94
|
-
light: ['bg-actions-warning-active-overlay', 'text-
|
|
95
|
-
strong: ['bg-actions-warning-idle-alpha-strong', 'text-
|
|
76
|
+
light: ['bg-actions-warning-active-overlay', 'text-text-warning-base'],
|
|
77
|
+
strong: ['bg-actions-warning-idle-alpha-strong', 'text-text-neutral-invert-base'],
|
|
96
78
|
},
|
|
97
79
|
success: {
|
|
98
|
-
light: ['bg-actions-success-active-overlay', 'text-
|
|
99
|
-
strong: ['bg-actions-success-idle-alpha-strong', 'text-
|
|
80
|
+
light: ['bg-actions-success-active-overlay', 'text-text-success-base'],
|
|
81
|
+
strong: ['bg-actions-success-idle-alpha-strong', 'text-text-neutral-invert-base'],
|
|
100
82
|
},
|
|
101
83
|
'neutral-invert': {
|
|
102
|
-
light: ['bg-actions-neutral-invert-active-overlay', 'text-
|
|
103
|
-
strong: ['bg-actions-neutral-invert-idle-alpha-strong', 'text-
|
|
84
|
+
light: ['bg-actions-neutral-invert-active-overlay', 'text-text-neutral-invert-base'],
|
|
85
|
+
strong: ['bg-actions-neutral-invert-idle-alpha-strong', 'text-text-neutral-base'],
|
|
104
86
|
},
|
|
105
87
|
}
|
|
106
88
|
|
|
@@ -129,7 +111,7 @@ const iconClasses = computed(() => {
|
|
|
129
111
|
|
|
130
112
|
/* Focus ring */
|
|
131
113
|
.ui-tag:focus-visible {
|
|
132
|
-
@apply
|
|
114
|
+
@apply focus-ring;
|
|
133
115
|
}
|
|
134
116
|
|
|
135
117
|
/* Hover states - only apply when withHoverEffect is true */
|
|
@@ -182,6 +164,6 @@ const iconClasses = computed(() => {
|
|
|
182
164
|
}
|
|
183
165
|
|
|
184
166
|
.ui-tag-label {
|
|
185
|
-
@apply flex-1;
|
|
167
|
+
@apply flex-1 truncate;
|
|
186
168
|
}
|
|
187
169
|
</style>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { computed, nextTick, ref, useSlots } from 'vue'
|
|
3
3
|
import UiFieldWrapper from '../UiFieldWrapper/UiFieldWrapper.vue'
|
|
4
4
|
import { useIconSize } from '../../composables/useIconSize'
|
|
5
|
+
import { useControlShape } from '../../composables/useControlShape'
|
|
6
|
+
import { useFieldFocusEvents } from '../../composables/useFieldFocusEvents'
|
|
5
7
|
import type { TextFieldSize } from './types'
|
|
6
8
|
|
|
7
9
|
interface UiTextFieldProps {
|
|
@@ -9,6 +11,10 @@ interface UiTextFieldProps {
|
|
|
9
11
|
modelValue?: string
|
|
10
12
|
placeholder?: string
|
|
11
13
|
disabled?: boolean
|
|
14
|
+
/**
|
|
15
|
+
* The value can be selected and copied but not edited
|
|
16
|
+
*/
|
|
17
|
+
readonly?: boolean
|
|
12
18
|
loading?: boolean
|
|
13
19
|
error?: boolean
|
|
14
20
|
label?: string
|
|
@@ -28,6 +34,7 @@ interface UiTextFieldProps {
|
|
|
28
34
|
const props = withDefaults(defineProps<UiTextFieldProps>(), {
|
|
29
35
|
size: 'md',
|
|
30
36
|
disabled: false,
|
|
37
|
+
readonly: false,
|
|
31
38
|
loading: false,
|
|
32
39
|
error: false,
|
|
33
40
|
required: false,
|
|
@@ -49,22 +56,17 @@ const emit = defineEmits<{
|
|
|
49
56
|
|
|
50
57
|
const slots = useSlots()
|
|
51
58
|
const textFieldRef = ref<HTMLInputElement | null>(null)
|
|
52
|
-
const isFocused =
|
|
59
|
+
const { isFocused, handleFocus, handleBlur, handleKeydown, handleKeyup } = useFieldFocusEvents(emit)
|
|
53
60
|
|
|
54
61
|
const iconSizeClass = useIconSize(() => props.size)
|
|
62
|
+
const shape = useControlShape('field', () => props.size)
|
|
55
63
|
|
|
56
64
|
const hasLeftIcon = computed(() => !!slots.icon)
|
|
57
65
|
const hasRightAction = computed(() => !!slots['right-action'])
|
|
58
|
-
const showClearButton = computed(() => props.clearable && !!props.modelValue && !props.disabled && !props.loading)
|
|
66
|
+
const showClearButton = computed(() => props.clearable && !!props.modelValue && !props.disabled && !props.readonly && !props.loading)
|
|
59
67
|
|
|
60
68
|
const textFieldClasses = computed(() => {
|
|
61
|
-
const classes = ['ui-text-field-field']
|
|
62
|
-
|
|
63
|
-
// Size classes
|
|
64
|
-
if (props.size === 'sm') classes.push('h-8 text-content-text-md-regular')
|
|
65
|
-
if (props.size === 'md') classes.push('h-10 text-content-text-md-regular')
|
|
66
|
-
if (props.size === 'lg') classes.push('h-12 text-content-text-md-regular')
|
|
67
|
-
if (props.size === 'xl') classes.push('h-16 text-content-text-md-regular')
|
|
69
|
+
const classes = ['ui-text-field-field', shape.value.radius, shape.value.height, 'text-content-text-md-regular']
|
|
68
70
|
|
|
69
71
|
// State classes
|
|
70
72
|
if (props.error) {
|
|
@@ -90,24 +92,6 @@ function handleClear () {
|
|
|
90
92
|
textFieldRef.value?.focus()
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
function handleFocus (event: FocusEvent) {
|
|
94
|
-
isFocused.value = true
|
|
95
|
-
emit('focus', event)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function handleBlur (event: FocusEvent) {
|
|
99
|
-
isFocused.value = false
|
|
100
|
-
emit('blur', event)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function handleKeydown (event: KeyboardEvent) {
|
|
104
|
-
emit('keydown', event)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function handleKeyup (event: KeyboardEvent) {
|
|
108
|
-
emit('keyup', event)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
95
|
defineExpose({
|
|
112
96
|
focus: () => textFieldRef.value?.focus(),
|
|
113
97
|
blur: () => textFieldRef.value?.blur(),
|
|
@@ -164,11 +148,12 @@ defineExpose({
|
|
|
164
148
|
:value="modelValue"
|
|
165
149
|
:placeholder="placeholder"
|
|
166
150
|
:disabled="disabled || loading"
|
|
151
|
+
:readonly="readonly"
|
|
167
152
|
:autocomplete="autocomplete"
|
|
168
153
|
:aria-label="ariaLabel"
|
|
169
154
|
:aria-invalid="error || undefined"
|
|
170
155
|
:aria-describedby="ariaDescribedby || ((helperMessage || errorMessage) ? `${fieldId}-message` : undefined)"
|
|
171
|
-
class="ui-text-field-element text-text-neutral-base placeholder:text-
|
|
156
|
+
class="ui-text-field-element text-text-neutral-base placeholder:text-control-placeholder"
|
|
172
157
|
@input="handleInput"
|
|
173
158
|
@focus="handleFocus"
|
|
174
159
|
@blur="handleBlur"
|
|
@@ -217,12 +202,12 @@ defineExpose({
|
|
|
217
202
|
@reference '@livestorm/ui-tokens/output/supernova/index.css';
|
|
218
203
|
|
|
219
204
|
.ui-text-field-field {
|
|
220
|
-
@apply relative flex items-center gap-1.5 w-full
|
|
205
|
+
@apply relative flex items-center gap-1.5 w-full border transition-all;
|
|
221
206
|
@apply pl-3 pr-0;
|
|
222
207
|
}
|
|
223
208
|
|
|
224
209
|
.ui-text-field-field--default {
|
|
225
|
-
@apply bg-
|
|
210
|
+
@apply bg-control-field-bg border-control-field-border;
|
|
226
211
|
|
|
227
212
|
&:not(.ui-text-field-field--disabled) {
|
|
228
213
|
&:hover .ui-text-field-overlay {
|
|
@@ -230,7 +215,7 @@ defineExpose({
|
|
|
230
215
|
}
|
|
231
216
|
|
|
232
217
|
&.ui-text-field-field--focused {
|
|
233
|
-
@apply
|
|
218
|
+
@apply bg-control-field-bg-focus focus-ring;
|
|
234
219
|
|
|
235
220
|
.ui-text-field-overlay {
|
|
236
221
|
background: transparent;
|
|
@@ -240,7 +225,7 @@ defineExpose({
|
|
|
240
225
|
}
|
|
241
226
|
|
|
242
227
|
.ui-text-field-field--error {
|
|
243
|
-
@apply bg-actions-danger-idle-alpha-light border-
|
|
228
|
+
@apply bg-actions-danger-idle-alpha-light border-control-error-border;
|
|
244
229
|
|
|
245
230
|
&:not(.ui-text-field-field--disabled) {
|
|
246
231
|
&:hover .ui-text-field-overlay {
|
|
@@ -248,7 +233,7 @@ defineExpose({
|
|
|
248
233
|
}
|
|
249
234
|
|
|
250
235
|
&.ui-text-field-field--focused {
|
|
251
|
-
@apply
|
|
236
|
+
@apply focus-ring;
|
|
252
237
|
|
|
253
238
|
.ui-text-field-overlay {
|
|
254
239
|
background: transparent;
|
|
@@ -262,7 +247,7 @@ defineExpose({
|
|
|
262
247
|
}
|
|
263
248
|
|
|
264
249
|
.ui-text-field-overlay {
|
|
265
|
-
@apply absolute inset-
|
|
250
|
+
@apply absolute -inset-px rounded-[inherit] pointer-events-none transition-colors;
|
|
266
251
|
z-index: 1;
|
|
267
252
|
}
|
|
268
253
|
|
|
@@ -336,7 +321,7 @@ defineExpose({
|
|
|
336
321
|
}
|
|
337
322
|
|
|
338
323
|
.ui-text-field-actions {
|
|
339
|
-
@apply flex items-center justify-center px-3 h-full rounded-
|
|
324
|
+
@apply flex items-center justify-center px-3 h-full rounded-[inherit];
|
|
340
325
|
position: relative;
|
|
341
326
|
z-index: 2;
|
|
342
327
|
}
|
|
@@ -359,7 +344,7 @@ defineExpose({
|
|
|
359
344
|
padding: 0;
|
|
360
345
|
|
|
361
346
|
&:focus-visible {
|
|
362
|
-
@apply
|
|
347
|
+
@apply focus-ring rounded-row;
|
|
363
348
|
}
|
|
364
349
|
}
|
|
365
350
|
</style>
|
|
@@ -63,7 +63,7 @@ const maxWidthClass = computed(() => {
|
|
|
63
63
|
v-bind="{ ...forwarded, ...$attrs }"
|
|
64
64
|
:data-colors-semantic="props.colorsSemantic"
|
|
65
65
|
:class="[
|
|
66
|
-
'ui-tooltip-content py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-
|
|
66
|
+
'ui-tooltip-content py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-row text-center z-overlay shadow-shadows-drop-shadow-lg',
|
|
67
67
|
'data-[state=delayed-open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=delayed-open]:zoom-in-95',
|
|
68
68
|
maxWidthClass
|
|
69
69
|
]"
|
|
@@ -5,6 +5,9 @@ import { TooltipProvider } from 'reka-ui'
|
|
|
5
5
|
withDefaults(defineProps<TooltipProviderProps>(), {
|
|
6
6
|
delayDuration: 700,
|
|
7
7
|
skipDelayDuration: 300,
|
|
8
|
+
// A tap is how touch opens a tooltip, and the click that follows it would
|
|
9
|
+
// close it again straight away unless the trigger stops closing on click.
|
|
10
|
+
disableClosingTrigger: true,
|
|
8
11
|
})
|
|
9
12
|
</script>
|
|
10
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { TooltipTriggerProps } from 'reka-ui'
|
|
3
|
-
import { TooltipTrigger, useForwardProps } from 'reka-ui'
|
|
3
|
+
import { TooltipTrigger, injectTooltipRootContext, useForwardProps } from 'reka-ui'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<TooltipTriggerProps>()
|
|
6
6
|
|
|
@@ -8,11 +8,26 @@ const props = defineProps<TooltipTriggerProps>()
|
|
|
8
8
|
// consumed here and never reach TooltipTrigger, so `as-child` would be ignored
|
|
9
9
|
// and reka would wrap the slot in its own <button>.
|
|
10
10
|
const forwarded = useForwardProps(props)
|
|
11
|
+
|
|
12
|
+
const rootContext = injectTooltipRootContext()
|
|
13
|
+
|
|
14
|
+
// this is to open the tooltip on touch devices, which can't hover
|
|
15
|
+
// Bound to `pointerup` so a scroll that starts on the trigger does not open it.
|
|
16
|
+
function toggleOnTouch(event: PointerEvent) {
|
|
17
|
+
if (event.pointerType !== 'touch' || rootContext.disabled.value) return
|
|
18
|
+
|
|
19
|
+
if (rootContext.open.value) {
|
|
20
|
+
rootContext.onClose()
|
|
21
|
+
} else {
|
|
22
|
+
rootContext.onOpen()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
11
25
|
</script>
|
|
12
26
|
|
|
13
27
|
<template>
|
|
14
28
|
<TooltipTrigger
|
|
15
29
|
v-bind="{ ...forwarded, ...$attrs }"
|
|
30
|
+
@pointerup="toggleOnTouch"
|
|
16
31
|
>
|
|
17
32
|
<slot />
|
|
18
33
|
</TooltipTrigger>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does stored theming output apply?
|
|
3
|
+
*
|
|
4
|
+
* The theme picker (and its playground) is the only writer of deliberate
|
|
5
|
+
* theming choices: the stored modes in `themeAndMode` and the seed token map
|
|
6
|
+
* in `customThemeTokens`. A stored value alone is not a choice signal —
|
|
7
|
+
* `useStorage` persists the light/light defaults for every visitor — so
|
|
8
|
+
* stored theming is consumed only where the picker exists, and every surface
|
|
9
|
+
* ships its own defaults otherwise (the dashboard light/light, the room
|
|
10
|
+
* dark shell over light content).
|
|
11
|
+
*
|
|
12
|
+
* This is the single place that rule lives. `ls-internal-theming` itself
|
|
13
|
+
* gates the picker's visibility; it does not gate any rendering path.
|
|
14
|
+
* When a real user-facing theming choice ships (workspace setting, OS
|
|
15
|
+
* follow), its mechanism replaces this predicate.
|
|
16
|
+
*/
|
|
17
|
+
export function storedThemingApplies (): boolean {
|
|
18
|
+
return typeof localStorage !== 'undefined' && localStorage.getItem('ls-internal-theming') === 'true'
|
|
19
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed, type MaybeRefOrGetter, toValue } from 'vue'
|
|
2
2
|
|
|
3
3
|
// Single source of truth for the action-button visual language (background,
|
|
4
|
-
// text,
|
|
4
|
+
// text, hover/active overlays) keyed by `variant` × `color`. Both
|
|
5
5
|
// `UiButton` and `UiDropdownTrigger` consume this so a trigger styled as a
|
|
6
6
|
// tertiary button stays pixel-identical to a real `UiButton` without copying
|
|
7
7
|
// token names around.
|
|
@@ -81,16 +81,6 @@ const TEXT_MAP: Record<ButtonVariant, Record<ButtonColor, string>> = {
|
|
|
81
81
|
},
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// Border — only `secondary` draws one.
|
|
85
|
-
const BORDER_MAP: Record<ButtonColor, string[]> = {
|
|
86
|
-
'primary': ['border', 'border-borders-primary-strong'],
|
|
87
|
-
'neutral': ['border', 'border-borders-neutral-strong'],
|
|
88
|
-
'neutral-invert': ['border', 'border-borders-neutral-invert-strong'],
|
|
89
|
-
'success': ['border', 'border-borders-success-strong'],
|
|
90
|
-
'warning': ['border', 'border-borders-warning-strong'],
|
|
91
|
-
'error': ['border', 'border-borders-danger-strong'],
|
|
92
|
-
}
|
|
93
|
-
|
|
94
84
|
// Hover overlay tints keyed by `variant-color`, in both the `group-hover:`
|
|
95
85
|
// flavor (layered button overlay element) and the `before:` flavor
|
|
96
86
|
// (single-element trigger pseudo). Kept as literals for Tailwind extraction.
|
|
@@ -205,9 +195,14 @@ export function useButtonStyles(options: UseButtonStylesOptions) {
|
|
|
205
195
|
/** Text color class for the current variant × color. */
|
|
206
196
|
const textClass = computed(() => TEXT_MAP[variant.value][color.value])
|
|
207
197
|
|
|
208
|
-
/**
|
|
198
|
+
/** Soft border of the secondary fill layer; transparent except in the accessible look. */
|
|
209
199
|
const borderClasses = computed(() =>
|
|
210
|
-
variant.value === 'secondary' ?
|
|
200
|
+
variant.value === 'secondary' ? ['border', 'border-control-soft-border'] : [],
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
/** Tertiary labels are underlined in the accessible look. */
|
|
204
|
+
const decorationClass = computed(() =>
|
|
205
|
+
variant.value === 'tertiary' ? '[text-decoration-line:var(--control-link-decoration)] underline-offset-3' : '',
|
|
211
206
|
)
|
|
212
207
|
|
|
213
208
|
/**
|
|
@@ -236,6 +231,7 @@ export function useButtonStyles(options: UseButtonStylesOptions) {
|
|
|
236
231
|
bgClass,
|
|
237
232
|
textClass,
|
|
238
233
|
borderClasses,
|
|
234
|
+
decorationClass,
|
|
239
235
|
overlayClasses,
|
|
240
236
|
beforeHoverOverlayClass,
|
|
241
237
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
|
|
2
|
+
import type { ChoiceControl } from '../components/UiChoice/types'
|
|
3
|
+
|
|
4
|
+
interface ChoiceboxState {
|
|
5
|
+
control: ChoiceControl
|
|
6
|
+
selected: boolean
|
|
7
|
+
disabled: boolean
|
|
8
|
+
error?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Only the row's own control rings the surface: a control in the content slot keeps its own focus
|
|
12
|
+
const FOCUS_RING: Record<ChoiceControl, string> = {
|
|
13
|
+
radio: 'has-[[role=radio]:focus-visible]:focus-ring',
|
|
14
|
+
switch: 'has-[[role=switch]:focus-visible]:focus-ring',
|
|
15
|
+
checkbox: 'has-[[type=checkbox]:focus-visible]:focus-ring',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Border, fill, radius and focus ring of a UiChoice choicebox */
|
|
19
|
+
export function useChoiceboxSurface (state: MaybeRefOrGetter<ChoiceboxState>): ComputedRef<string[]> {
|
|
20
|
+
return computed(() => {
|
|
21
|
+
const { control, selected, disabled, error } = toValue(state)
|
|
22
|
+
const classes = ['rounded-row border', FOCUS_RING[control]]
|
|
23
|
+
|
|
24
|
+
if (error) {
|
|
25
|
+
classes.push('bg-actions-danger-idle-alpha-light', selected ? 'border-actions-danger-idle' : 'border-control-error-border')
|
|
26
|
+
} else if (disabled) {
|
|
27
|
+
classes.push('bg-control-choice-bg-disabled border-control-choice-border-disabled')
|
|
28
|
+
} else if (selected) {
|
|
29
|
+
classes.push('bg-control-choice-selected-bg border-control-choice-selected-border hover:bg-control-choice-selected-bg-hover')
|
|
30
|
+
} else {
|
|
31
|
+
classes.push('bg-control-choice-bg border-control-choice-border hover:bg-control-choice-bg-hover hover:border-control-choice-border-hover')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return classes
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { computed, onMounted, ref, type Ref } from 'vue'
|
|
1
|
+
import { computed, onMounted, onUnmounted, ref, type Ref } from 'vue'
|
|
2
2
|
import useThemeAndModes from './useThemeAndModes'
|
|
3
|
+
import { storedThemingApplies } from './storedThemingApplies'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Registers an element as a page "content" surface so the stored content mode
|
|
@@ -7,26 +8,29 @@ import useThemeAndModes from './useThemeAndModes'
|
|
|
7
8
|
* behaviour `UiPage` gives its page content, without pulling in the full page
|
|
8
9
|
* layout (and its `<main>`).
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Content ships light. The stored content mode applies where the picker
|
|
12
|
+
* exists (see storedThemingApplies); everyone else gets the shipped default
|
|
13
|
+
* regardless of what localStorage may hold.
|
|
13
14
|
*
|
|
14
|
-
* The
|
|
15
|
-
* localStorage), and server + client-hydration markup must be
|
|
16
|
-
*
|
|
15
|
+
* The predicate is read in onMounted, not at setup: setup also runs during
|
|
16
|
+
* SSR (no localStorage), and server + client-hydration markup must be
|
|
17
|
+
* identical — stored modes take over right after mount instead.
|
|
17
18
|
*
|
|
18
19
|
* @param element Template ref of the content element to watch.
|
|
19
20
|
* @param selector Unique key for the watch registry — one per logical surface
|
|
20
21
|
* (e.g. `[data-name="ui-page-content"]`).
|
|
21
|
-
* @returns `contentMode`, to bind to the element's `data-colors-semantic
|
|
22
|
-
* `isInternalThemingEnabled` for any mode-dependent styling.
|
|
22
|
+
* @returns `contentMode`, to bind to the element's `data-colors-semantic`.
|
|
23
23
|
*/
|
|
24
24
|
export function useContentMode (
|
|
25
25
|
element: Ref<HTMLElement | undefined>,
|
|
26
26
|
selector: string,
|
|
27
27
|
) {
|
|
28
|
-
const { addElementToWatch, currentContentMode } = useThemeAndModes()
|
|
29
|
-
const
|
|
28
|
+
const { addElementToWatch, removeElementToWatch, currentContentMode } = useThemeAndModes()
|
|
29
|
+
const storedApplies = ref(false)
|
|
30
|
+
|
|
31
|
+
// Captured at registration: Vue has already nulled the template ref by the
|
|
32
|
+
// time onUnmounted runs, and unregistering needs the element it registered.
|
|
33
|
+
let registered: HTMLElement | undefined
|
|
30
34
|
|
|
31
35
|
// Always resolve to a concrete mode, never undefined. useThemeAndModes also
|
|
32
36
|
// sets this attribute imperatively; if the binding resolved to undefined,
|
|
@@ -34,20 +38,29 @@ export function useContentMode (
|
|
|
34
38
|
// flag flip, and the content would silently inherit the *shell* mode from
|
|
35
39
|
// <body> — reading as "content mode resets to dark on every navigation".
|
|
36
40
|
const contentMode = computed(() => (
|
|
37
|
-
|
|
41
|
+
storedApplies.value ? currentContentMode.value : 'light'
|
|
38
42
|
))
|
|
39
43
|
|
|
40
44
|
onMounted(() => {
|
|
41
|
-
|
|
45
|
+
storedApplies.value = storedThemingApplies()
|
|
42
46
|
|
|
43
47
|
if (!element.value) { return }
|
|
44
48
|
|
|
49
|
+
registered = element.value
|
|
45
50
|
addElementToWatch('content', {
|
|
46
51
|
selector,
|
|
47
|
-
element:
|
|
48
|
-
...(
|
|
52
|
+
element: registered,
|
|
53
|
+
...(storedApplies.value ? {} : { modeOverride: 'light' }),
|
|
49
54
|
})
|
|
50
55
|
})
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
// The registry outlives this component, so a surface that unmounts without
|
|
58
|
+
// this stays referenced and keeps being written to.
|
|
59
|
+
onUnmounted(() => {
|
|
60
|
+
if (registered) {
|
|
61
|
+
removeElementToWatch('content', { selector, element: registered })
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return { contentMode }
|
|
53
66
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies the accessibility look on the app root: `data-accessibility="on"`.
|
|
3
|
+
* The control tokens read this attribute.
|
|
4
|
+
*/
|
|
5
|
+
export function applyControlModes (
|
|
6
|
+
{ accessibility }: { accessibility: boolean },
|
|
7
|
+
root: HTMLElement = document.body,
|
|
8
|
+
) {
|
|
9
|
+
if (accessibility) root.setAttribute('data-accessibility', 'on')
|
|
10
|
+
else root.removeAttribute('data-accessibility')
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
|
|
2
|
+
|
|
3
|
+
// NOTE: every class must be a complete literal string: Tailwind's static
|
|
4
|
+
// extractor can't see interpolated class names.
|
|
5
|
+
|
|
6
|
+
export type FieldSize = 'sm' | 'md' | 'lg' | 'xl'
|
|
7
|
+
type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
8
|
+
|
|
9
|
+
interface FieldShape { height: string, radius: string, chevron: string }
|
|
10
|
+
|
|
11
|
+
const FIELD_SHAPES: Record<FieldSize, FieldShape> = {
|
|
12
|
+
sm: { height: 'h-field-sm', radius: 'rounded-field-sm', chevron: 'size-3.5' },
|
|
13
|
+
md: { height: 'h-field-md', radius: 'rounded-field-md', chevron: 'size-4' },
|
|
14
|
+
lg: { height: 'h-field-lg', radius: 'rounded-field-lg', chevron: 'size-5' },
|
|
15
|
+
xl: { height: 'h-field-xl', radius: 'rounded-field-xl', chevron: 'size-6' },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const BUTTON_SHAPES: Record<ButtonSize, { height: string, square: string, radius: string }> = {
|
|
19
|
+
xs: { height: 'h-button-xs', square: 'size-button-xs', radius: 'rounded-button-xs' },
|
|
20
|
+
sm: { height: 'h-button-sm', square: 'size-button-sm', radius: 'rounded-button-sm' },
|
|
21
|
+
md: { height: 'h-button-md', square: 'size-button-md', radius: 'rounded-button-md' },
|
|
22
|
+
lg: { height: 'h-button-lg', square: 'size-button-lg', radius: 'rounded-button-lg' },
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ControlShapes {
|
|
26
|
+
field: { size: FieldSize, shape: FieldShape }
|
|
27
|
+
button: { size: ButtonSize, shape: { height: string, square: string, radius: string } }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useControlShape<K extends keyof ControlShapes> (kind: K, size: MaybeRefOrGetter<ControlShapes[K]['size']>): ComputedRef<ControlShapes[K]['shape']> {
|
|
31
|
+
return computed(() => (kind === 'field'
|
|
32
|
+
? FIELD_SHAPES[toValue(size) as FieldSize]
|
|
33
|
+
: BUTTON_SHAPES[toValue(size) as ButtonSize]) as ControlShapes[K]['shape'])
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function useFieldFocusEvents(emit: {
|
|
4
|
+
(event: 'focus', payload: FocusEvent): void
|
|
5
|
+
(event: 'blur', payload: FocusEvent): void
|
|
6
|
+
(event: 'keydown', payload: KeyboardEvent): void
|
|
7
|
+
(event: 'keyup', payload: KeyboardEvent): void
|
|
8
|
+
}) {
|
|
9
|
+
const isFocused = ref(false)
|
|
10
|
+
|
|
11
|
+
function handleFocus(event: FocusEvent) {
|
|
12
|
+
isFocused.value = true
|
|
13
|
+
emit('focus', event)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function handleBlur(event: FocusEvent) {
|
|
17
|
+
isFocused.value = false
|
|
18
|
+
emit('blur', event)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
22
|
+
emit('keydown', event)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function handleKeyup(event: KeyboardEvent) {
|
|
26
|
+
emit('keyup', event)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { isFocused, handleFocus, handleBlur, handleKeydown, handleKeyup }
|
|
30
|
+
}
|
|
@@ -26,3 +26,18 @@ export type IconSizeName = keyof typeof ICON_SIZES
|
|
|
26
26
|
export function useIconSize(size: MaybeRefOrGetter<IconSizeName>) {
|
|
27
27
|
return computed(() => ICON_SIZES[toValue(size)])
|
|
28
28
|
}
|
|
29
|
+
|
|
30
|
+
// A button's trailing icon (external link, chevron) sits one step below its
|
|
31
|
+
// leading icon so it reads as an affordance, not as content.
|
|
32
|
+
const TRAILING_ICON_SIZES = {
|
|
33
|
+
xs: 'size-2.5',
|
|
34
|
+
sm: 'size-3.5',
|
|
35
|
+
md: 'size-4',
|
|
36
|
+
lg: 'size-5',
|
|
37
|
+
xl: 'size-6',
|
|
38
|
+
} as const
|
|
39
|
+
|
|
40
|
+
/** Reactive trailing icon size class for a control's `size`. */
|
|
41
|
+
export function useTrailingIconSize(size: MaybeRefOrGetter<IconSizeName>) {
|
|
42
|
+
return computed(() => TRAILING_ICON_SIZES[toValue(size)])
|
|
43
|
+
}
|