@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,142 @@
|
|
|
1
|
+
import { computed, ref, toValue, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
|
|
3
|
+
interface UsePullGestureOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Pointer travel (px, ascending) at which each stage is reached. An empty
|
|
6
|
+
* list disables the gesture.
|
|
7
|
+
*/
|
|
8
|
+
stages: MaybeRefOrGetter<number[]>
|
|
9
|
+
/**
|
|
10
|
+
* Travel (px) up to which the element tracks the pointer 1:1; resistance
|
|
11
|
+
* builds beyond it. Defaults to the last stage.
|
|
12
|
+
*/
|
|
13
|
+
resistFrom?: MaybeRefOrGetter<number | undefined>
|
|
14
|
+
/**
|
|
15
|
+
* Visible travel the pulled element tends toward once resisting. Defaults to
|
|
16
|
+
* five times the last stage.
|
|
17
|
+
*/
|
|
18
|
+
maxOffset?: MaybeRefOrGetter<number | undefined>
|
|
19
|
+
/** Called on release when at least one stage was reached. */
|
|
20
|
+
onRelease?: (stage: number) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Point {
|
|
24
|
+
x: number
|
|
25
|
+
y: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const REST: Point = { x: 0, y: 0 }
|
|
29
|
+
|
|
30
|
+
// Travel (px) under which a press still counts as a click, not a drag.
|
|
31
|
+
const DRAG_SLOP = 4
|
|
32
|
+
|
|
33
|
+
// Detent at each stage: the element holds still for this much travel past
|
|
34
|
+
// the threshold, then catches up at double speed over the same distance.
|
|
35
|
+
const DETENT = 8
|
|
36
|
+
|
|
37
|
+
function detentLag (distance: number, stages: number[]) {
|
|
38
|
+
return stages.reduce(
|
|
39
|
+
(lag, threshold) => lag + Math.max(0, DETENT - Math.abs(distance - threshold - DETENT)),
|
|
40
|
+
0,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Free travel up to `free`, then an iOS-style rubber band lets the visible
|
|
45
|
+
// travel approach `max` asymptotically.
|
|
46
|
+
function resist (distance: number, free: number, max: number) {
|
|
47
|
+
if (distance <= free) return distance
|
|
48
|
+
const tail = max - free
|
|
49
|
+
if (tail <= 0) return free
|
|
50
|
+
const beyond = distance - free
|
|
51
|
+
return free + (tail * beyond) / (beyond + tail)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pull gesture in any direction with staged thresholds: the element follows
|
|
56
|
+
* the pointer through the stages and resists beyond the last one. Bind the
|
|
57
|
+
* returned pointer handlers to the pulled element and apply `offset` as its
|
|
58
|
+
* translation; on release the state snaps back to rest.
|
|
59
|
+
*/
|
|
60
|
+
export function usePullGesture (options: UsePullGestureOptions) {
|
|
61
|
+
const isPulling = ref(false)
|
|
62
|
+
const delta = ref<Point>(REST)
|
|
63
|
+
let pointerId: number | null = null
|
|
64
|
+
let start: Point = REST
|
|
65
|
+
let didDrag = false
|
|
66
|
+
|
|
67
|
+
const stages = computed(() => toValue(options.stages))
|
|
68
|
+
const enabled = computed(() => stages.value.length > 0)
|
|
69
|
+
const lastStage = computed(() => stages.value[stages.value.length - 1] ?? 0)
|
|
70
|
+
const resistFrom = computed(() => toValue(options.resistFrom) ?? lastStage.value)
|
|
71
|
+
const maxOffset = computed(() => toValue(options.maxOffset) ?? lastStage.value * 5)
|
|
72
|
+
const distance = computed(() => Math.hypot(delta.value.x, delta.value.y))
|
|
73
|
+
const stage = computed(() => stages.value.filter(threshold => distance.value >= threshold).length)
|
|
74
|
+
const offset = computed<Point>(() => {
|
|
75
|
+
if (distance.value === 0) return REST
|
|
76
|
+
const eased = distance.value - detentLag(distance.value, stages.value)
|
|
77
|
+
const ratio = resist(eased, resistFrom.value, maxOffset.value) / distance.value
|
|
78
|
+
return { x: delta.value.x * ratio, y: delta.value.y * ratio }
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
function reset () {
|
|
82
|
+
isPulling.value = false
|
|
83
|
+
delta.value = REST
|
|
84
|
+
pointerId = null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onPointerDown (event: PointerEvent) {
|
|
88
|
+
if (!enabled.value || !event.isPrimary || event.button !== 0) return
|
|
89
|
+
pointerId = event.pointerId
|
|
90
|
+
start = { x: event.clientX, y: event.clientY }
|
|
91
|
+
didDrag = false
|
|
92
|
+
isPulling.value = true
|
|
93
|
+
// Keep receiving move/up events once the pointer leaves the element.
|
|
94
|
+
;(event.currentTarget as Element | null)?.setPointerCapture?.(event.pointerId)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function track (event: PointerEvent) {
|
|
98
|
+
delta.value = { x: event.clientX - start.x, y: event.clientY - start.y }
|
|
99
|
+
if (distance.value > DRAG_SLOP) didDrag = true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function onPointerMove (event: PointerEvent) {
|
|
103
|
+
if (!isPulling.value || event.pointerId !== pointerId) return
|
|
104
|
+
track(event)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function onPointerUp (event: PointerEvent) {
|
|
108
|
+
if (!isPulling.value || event.pointerId !== pointerId) return
|
|
109
|
+
// A fast drag can end past the last delivered move: judge the release position.
|
|
110
|
+
track(event)
|
|
111
|
+
const reached = stage.value
|
|
112
|
+
reset()
|
|
113
|
+
if (reached > 0) options.onRelease?.(reached)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function onPointerCancel (event: PointerEvent) {
|
|
117
|
+
if (event.pointerId !== pointerId) return
|
|
118
|
+
reset()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Whether the press that just ended was a drag. Lets a click handler ignore
|
|
123
|
+
* the click the browser fires after a drag; the next press starts clean.
|
|
124
|
+
*/
|
|
125
|
+
function consumeDrag () {
|
|
126
|
+
const wasDrag = didDrag
|
|
127
|
+
didDrag = false
|
|
128
|
+
return wasDrag
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
enabled,
|
|
133
|
+
isPulling,
|
|
134
|
+
stage,
|
|
135
|
+
offset,
|
|
136
|
+
onPointerDown,
|
|
137
|
+
onPointerMove,
|
|
138
|
+
onPointerUp,
|
|
139
|
+
onPointerCancel,
|
|
140
|
+
consumeDrag,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useStorage } from '@vueuse/core'
|
|
2
|
+
import { storedThemingApplies } from './storedThemingApplies'
|
|
2
3
|
import { computed, getCurrentInstance, ref, watch } from 'vue'
|
|
3
4
|
import {
|
|
4
5
|
THEME_LIST,
|
|
@@ -124,10 +125,35 @@ export default function useThemeAndModes (
|
|
|
124
125
|
list.value.set(selector, { element, modeOverride })
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Drops an element from the watch list. The registries outlive every
|
|
130
|
+
* component that registers with them, so an element that unmounts without
|
|
131
|
+
* this stays referenced — and keeps being written to — for the life of the
|
|
132
|
+
* page.
|
|
133
|
+
*
|
|
134
|
+
* The element is part of the descriptor because a selector is a surface, not
|
|
135
|
+
* an instance: where two instances of one surface overlap (a route leaving
|
|
136
|
+
* while its replacement mounts), the leaving one must not drop the entry the
|
|
137
|
+
* incoming one just registered.
|
|
138
|
+
* @param listType Which registry the element was added to.
|
|
139
|
+
* @param descriptor The selector it was registered under, and the element itself.
|
|
140
|
+
* @returns void
|
|
141
|
+
*/
|
|
142
|
+
function removeElementToWatch (
|
|
143
|
+
listType: 'mode' | 'content',
|
|
144
|
+
{ selector, element }: { selector: string, element: HTMLElement }
|
|
145
|
+
) {
|
|
146
|
+
const list = listType === 'mode' ? modeTemplateRefsToWatch : contentTemplateRefsToWatch
|
|
147
|
+
if (list.value.get(selector)?.element === element) {
|
|
148
|
+
list.value.delete(selector)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
127
152
|
return {
|
|
128
153
|
initOnAppMounted,
|
|
129
154
|
registerWatchers,
|
|
130
155
|
addElementToWatch,
|
|
156
|
+
removeElementToWatch,
|
|
131
157
|
updateContentMode,
|
|
132
158
|
|
|
133
159
|
updateTheme,
|
|
@@ -214,7 +240,8 @@ const DYNAMIC_TOKENS_STORAGE_KEY = 'customThemeTokens'
|
|
|
214
240
|
*/
|
|
215
241
|
function applyStoredDynamicTokens () {
|
|
216
242
|
try {
|
|
217
|
-
|
|
243
|
+
// Seed tokens are picker output — same consumption rule as stored modes.
|
|
244
|
+
if (!storedThemingApplies()) return
|
|
218
245
|
|
|
219
246
|
const stored = localStorage.getItem(DYNAMIC_TOKENS_STORAGE_KEY)
|
|
220
247
|
if (!stored) return
|
package/src/css/body.css
CHANGED
|
@@ -14,6 +14,21 @@
|
|
|
14
14
|
line-height: 1.5;
|
|
15
15
|
font-weight: 400;
|
|
16
16
|
color: var(--color-grey-700);
|
|
17
|
+
font-feature-settings: 'cv01', 'cv05', 'cv09', 'cv11', 'ss03';
|
|
18
|
+
font-optical-sizing: auto;
|
|
19
|
+
font-kerning: normal;
|
|
20
|
+
font-synthesis: none;
|
|
21
|
+
text-rendering: optimizeLegibility;
|
|
22
|
+
-webkit-font-smoothing: antialiased;
|
|
23
|
+
-moz-osx-font-smoothing: grayscale;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
h1, h2, h3, h4, h5, h6 {
|
|
27
|
+
text-wrap: balance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
p {
|
|
31
|
+
text-wrap: pretty;
|
|
17
32
|
}
|
|
18
33
|
}
|
|
19
34
|
|
|
@@ -34,5 +49,5 @@
|
|
|
34
49
|
https://css-tricks.com/almanac/selectors/f/focus-visible/#aa-how-do-browsers-determine-when-something-is-focus-visible
|
|
35
50
|
*/
|
|
36
51
|
*:focus-visible:not(input, .base-input, textarea, .v-popper__popper, .focus-indicator-none, [contenteditable]) {
|
|
37
|
-
@apply
|
|
52
|
+
@apply focus-ring;
|
|
38
53
|
}
|
package/src/css/fonts.css
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Inter
|
|
3
|
-
purpose: a
|
|
4
|
-
|
|
2
|
+
Inter Variable, served from the inter-ui package (OFL-1.1) - kept OUT of
|
|
3
|
+
the base stylesheet (./style) on purpose: loading a font must stay the
|
|
4
|
+
consumer's choice. Apps that want the quick path import this file once:
|
|
5
5
|
|
|
6
6
|
import '@livestorm/ui-vue/fonts'
|
|
7
7
|
|
|
8
|
-
The
|
|
8
|
+
The Latin subsets come last so the browser tries them first; it downloads a
|
|
9
|
+
full file only for text they do not cover. Their unicode-range is the
|
|
10
|
+
subset's own character map.
|
|
9
11
|
*/
|
|
10
|
-
@
|
|
12
|
+
@font-face {
|
|
13
|
+
font-family: 'Inter';
|
|
14
|
+
font-style: normal;
|
|
15
|
+
font-weight: 100 900;
|
|
16
|
+
font-display: swap;
|
|
17
|
+
src: url('inter-ui/variable/InterVariable.woff2') format('woff2');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@font-face {
|
|
21
|
+
font-family: 'Inter';
|
|
22
|
+
font-style: italic;
|
|
23
|
+
font-weight: 100 900;
|
|
24
|
+
font-display: swap;
|
|
25
|
+
src: url('inter-ui/variable/InterVariable-Italic.woff2') format('woff2');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@font-face {
|
|
29
|
+
font-family: 'Inter';
|
|
30
|
+
font-style: normal;
|
|
31
|
+
font-weight: 100 900;
|
|
32
|
+
font-display: swap;
|
|
33
|
+
src: url('inter-ui/variable-latin/InterVariable-subset.woff2') format('woff2');
|
|
34
|
+
unicode-range: U+0000, U+0020-007E, U+00A0-00AC, U+00AE-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-200B, U+2010-2027, U+202F-2055, U+2057, U+205F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+FEFF;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@font-face {
|
|
38
|
+
font-family: 'Inter';
|
|
39
|
+
font-style: italic;
|
|
40
|
+
font-weight: 100 900;
|
|
41
|
+
font-display: swap;
|
|
42
|
+
src: url('inter-ui/variable-latin/InterVariable-Italic-subset.woff2') format('woff2');
|
|
43
|
+
unicode-range: U+0000, U+0020-007E, U+00A0-00AC, U+00AE-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-200B, U+2010-2027, U+202F-2055, U+2057, U+205F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+FEFF;
|
|
44
|
+
}
|
package/src/global.d.ts
CHANGED
|
@@ -13,18 +13,26 @@ import type UiButtonGroup from './components/UiButtonGroup/UiButtonGroup.vue'
|
|
|
13
13
|
import type UiButtonGroupItem from './components/UiButtonGroup/UiButtonGroupItem.vue'
|
|
14
14
|
import type UiCard from './components/UiCard/UiCard.vue'
|
|
15
15
|
import type UiCarousel from './components/UiCarousel/UiCarousel.vue'
|
|
16
|
+
import type UiChoice from './components/UiChoice/UiChoice.vue'
|
|
16
17
|
import type UiColorField from './components/UiColorField/UiColorField.vue'
|
|
17
18
|
import type UiColorPicker from './components/UiColorPicker/UiColorPicker.vue'
|
|
19
|
+
import type UiCombobox from './components/UiCombobox/UiCombobox.vue'
|
|
20
|
+
import type UiDataTable from './components/UiDataTable/UiDataTable.vue'
|
|
18
21
|
import type UiDialog from './components/UiDialog/UiDialog.vue'
|
|
19
22
|
import type UiDialogClose from './components/UiDialog/UiDialogClose.vue'
|
|
20
23
|
import type UiDialogContent from './components/UiDialog/UiDialogContent.vue'
|
|
21
24
|
import type UiDialogFooter from './components/UiDialog/UiDialogFooter.vue'
|
|
22
25
|
import type UiDialogTrigger from './components/UiDialog/UiDialogTrigger.vue'
|
|
26
|
+
import type UiDrawer from './components/UiDrawer/UiDrawer.vue'
|
|
27
|
+
import type UiDrawerContent from './components/UiDrawer/UiDrawerContent.vue'
|
|
28
|
+
import type UiDrawerTrigger from './components/UiDrawer/UiDrawerTrigger.vue'
|
|
23
29
|
import type UiDropdown from './components/UiDropdown/UiDropdown.vue'
|
|
24
30
|
import type UiDropdownContent from './components/UiDropdown/UiDropdownContent.vue'
|
|
31
|
+
import type UiDropdownGroup from './components/UiDropdown/UiDropdownGroup.vue'
|
|
25
32
|
import type UiDropdownItem from './components/UiDropdown/UiDropdownItem.vue'
|
|
26
33
|
import type UiDropdownSeparator from './components/UiDropdown/UiDropdownSeparator.vue'
|
|
27
34
|
import type UiDropdownTrigger from './components/UiDropdown/UiDropdownTrigger.vue'
|
|
35
|
+
import type UiEmojiPickerList from './components/UiEmojiPickerList/UiEmojiPickerList.vue'
|
|
28
36
|
import type UiEmptyState from './components/UiEmptyState/UiEmptyState.vue'
|
|
29
37
|
import type UiFieldWrapper from './components/UiFieldWrapper/UiFieldWrapper.vue'
|
|
30
38
|
import type UiFormField from './components/UiForm/UiFormField.vue'
|
|
@@ -39,6 +47,7 @@ import type UiLabel from './components/UiLabel/UiLabel.vue'
|
|
|
39
47
|
import type UiList from './components/UiList/UiList.vue'
|
|
40
48
|
import type UiListItem from './components/UiListItem/UiListItem.vue'
|
|
41
49
|
import type UiLoader from './components/UiLoader/UiLoader.vue'
|
|
50
|
+
import type UiSpinnerIcon from './components/UiLoader/UiSpinnerIcon.vue'
|
|
42
51
|
import type UiLogo from './components/UiLogo/UiLogo.vue'
|
|
43
52
|
import type UiMenu from './components/UiMenu/UiMenu.vue'
|
|
44
53
|
import type UiMenuContent from './components/UiMenu/UiMenuContent.vue'
|
|
@@ -50,14 +59,21 @@ import type UiMenuSub from './components/UiMenu/UiMenuSub.vue'
|
|
|
50
59
|
import type UiMenuSubContent from './components/UiMenu/UiMenuSubContent.vue'
|
|
51
60
|
import type UiMenuSubTrigger from './components/UiMenu/UiMenuSubTrigger.vue'
|
|
52
61
|
import type UiMenuTrigger from './components/UiMenu/UiMenuTrigger.vue'
|
|
62
|
+
import type UiNestedOptions from './components/UiNestedOptions/UiNestedOptions.vue'
|
|
53
63
|
import type UiPage from './components/UiPage/UiPage.vue'
|
|
54
64
|
import type UiPageHeader from './components/UiPageHeader/UiPageHeader.vue'
|
|
65
|
+
import type UiPagination from './components/UiPagination/UiPagination.vue'
|
|
66
|
+
import type UiPane from './components/UiPane/UiPane.vue'
|
|
67
|
+
import type UiPaneContent from './components/UiPane/UiPaneContent.vue'
|
|
68
|
+
import type UiPaneTrigger from './components/UiPane/UiPaneTrigger.vue'
|
|
55
69
|
import type UiPopover from './components/UiPopover/UiPopover.vue'
|
|
56
70
|
import type UiPopoverAnchor from './components/UiPopover/UiPopoverAnchor.vue'
|
|
57
71
|
import type UiPopoverContent from './components/UiPopover/UiPopoverContent.vue'
|
|
58
72
|
import type UiPopoverTrigger from './components/UiPopover/UiPopoverTrigger.vue'
|
|
59
73
|
import type UiProgress from './components/UiProgress/UiProgress.vue'
|
|
74
|
+
import type UiRating from './components/UiRating/UiRating.vue'
|
|
60
75
|
import type UiRoomButton from './components/UiRoomButton/UiRoomButton.vue'
|
|
76
|
+
import type UiRoomCountdown from './components/UiRoomCountdown/UiRoomCountdown.vue'
|
|
61
77
|
import type UiShell from './components/UiShell/UiShell.vue'
|
|
62
78
|
import type UiSideMenu from './components/UiSideMenu/UiSideMenu.vue'
|
|
63
79
|
import type UiSideMenuBottom from './components/UiSideMenu/UiSideMenuBottom.vue'
|
|
@@ -72,6 +88,8 @@ import type UiStepperSeparator from './components/UiStepper/UiStepperSeparator.v
|
|
|
72
88
|
import type UiStepperTitle from './components/UiStepper/UiStepperTitle.vue'
|
|
73
89
|
import type UiSwitch from './components/UiSwitch/UiSwitch.vue'
|
|
74
90
|
import type UiTable from './components/UiTable/UiTable.vue'
|
|
91
|
+
import type UiTableActionsCell from './components/UiTable/UiTableActionsCell.vue'
|
|
92
|
+
import type UiTableActionsHeaderCell from './components/UiTable/UiTableActionsHeaderCell.vue'
|
|
75
93
|
import type UiTableBody from './components/UiTable/UiTableBody.vue'
|
|
76
94
|
import type UiTableCell from './components/UiTable/UiTableCell.vue'
|
|
77
95
|
import type UiTableHeader from './components/UiTable/UiTableHeader.vue'
|
|
@@ -110,18 +128,26 @@ declare module 'vue' {
|
|
|
110
128
|
UiButtonGroupItem: typeof UiButtonGroupItem
|
|
111
129
|
UiCard: typeof UiCard
|
|
112
130
|
UiCarousel: typeof UiCarousel
|
|
131
|
+
UiChoice: typeof UiChoice
|
|
113
132
|
UiColorField: typeof UiColorField
|
|
114
133
|
UiColorPicker: typeof UiColorPicker
|
|
134
|
+
UiCombobox: typeof UiCombobox
|
|
135
|
+
UiDataTable: typeof UiDataTable
|
|
115
136
|
UiDialog: typeof UiDialog
|
|
116
137
|
UiDialogClose: typeof UiDialogClose
|
|
117
138
|
UiDialogContent: typeof UiDialogContent
|
|
118
139
|
UiDialogFooter: typeof UiDialogFooter
|
|
119
140
|
UiDialogTrigger: typeof UiDialogTrigger
|
|
141
|
+
UiDrawer: typeof UiDrawer
|
|
142
|
+
UiDrawerContent: typeof UiDrawerContent
|
|
143
|
+
UiDrawerTrigger: typeof UiDrawerTrigger
|
|
120
144
|
UiDropdown: typeof UiDropdown
|
|
121
145
|
UiDropdownContent: typeof UiDropdownContent
|
|
146
|
+
UiDropdownGroup: typeof UiDropdownGroup
|
|
122
147
|
UiDropdownItem: typeof UiDropdownItem
|
|
123
148
|
UiDropdownSeparator: typeof UiDropdownSeparator
|
|
124
149
|
UiDropdownTrigger: typeof UiDropdownTrigger
|
|
150
|
+
UiEmojiPickerList: typeof UiEmojiPickerList
|
|
125
151
|
UiEmptyState: typeof UiEmptyState
|
|
126
152
|
UiFieldWrapper: typeof UiFieldWrapper
|
|
127
153
|
UiFormField: typeof UiFormField
|
|
@@ -136,6 +162,7 @@ declare module 'vue' {
|
|
|
136
162
|
UiList: typeof UiList
|
|
137
163
|
UiListItem: typeof UiListItem
|
|
138
164
|
UiLoader: typeof UiLoader
|
|
165
|
+
UiSpinnerIcon: typeof UiSpinnerIcon
|
|
139
166
|
UiLogo: typeof UiLogo
|
|
140
167
|
UiMenu: typeof UiMenu
|
|
141
168
|
UiMenuContent: typeof UiMenuContent
|
|
@@ -147,14 +174,21 @@ declare module 'vue' {
|
|
|
147
174
|
UiMenuSubContent: typeof UiMenuSubContent
|
|
148
175
|
UiMenuSubTrigger: typeof UiMenuSubTrigger
|
|
149
176
|
UiMenuTrigger: typeof UiMenuTrigger
|
|
177
|
+
UiNestedOptions: typeof UiNestedOptions
|
|
150
178
|
UiPage: typeof UiPage
|
|
151
179
|
UiPageHeader: typeof UiPageHeader
|
|
180
|
+
UiPagination: typeof UiPagination
|
|
181
|
+
UiPane: typeof UiPane
|
|
182
|
+
UiPaneContent: typeof UiPaneContent
|
|
183
|
+
UiPaneTrigger: typeof UiPaneTrigger
|
|
152
184
|
UiPopover: typeof UiPopover
|
|
153
185
|
UiPopoverAnchor: typeof UiPopoverAnchor
|
|
154
186
|
UiPopoverContent: typeof UiPopoverContent
|
|
155
187
|
UiPopoverTrigger: typeof UiPopoverTrigger
|
|
156
188
|
UiProgress: typeof UiProgress
|
|
189
|
+
UiRating: typeof UiRating
|
|
157
190
|
UiRoomButton: typeof UiRoomButton
|
|
191
|
+
UiRoomCountdown: typeof UiRoomCountdown
|
|
158
192
|
UiShell: typeof UiShell
|
|
159
193
|
UiSideMenu: typeof UiSideMenu
|
|
160
194
|
UiSideMenuBottom: typeof UiSideMenuBottom
|
|
@@ -169,6 +203,8 @@ declare module 'vue' {
|
|
|
169
203
|
UiStepperTitle: typeof UiStepperTitle
|
|
170
204
|
UiSwitch: typeof UiSwitch
|
|
171
205
|
UiTable: typeof UiTable
|
|
206
|
+
UiTableActionsCell: typeof UiTableActionsCell
|
|
207
|
+
UiTableActionsHeaderCell: typeof UiTableActionsHeaderCell
|
|
172
208
|
UiTableBody: typeof UiTableBody
|
|
173
209
|
UiTableCell: typeof UiTableCell
|
|
174
210
|
UiTableHeader: typeof UiTableHeader
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { parse, converter, clampChroma, formatHex } from 'culori'
|
|
2
|
+
|
|
3
|
+
// --- OKLCH lightness & chroma targets ---
|
|
4
|
+
// Calibrated to reproduce the static ui-tokens palette exactly at the
|
|
5
|
+
// Livestorm seed (#3073ff = brand 500), pinned by colorScale.spec.ts.
|
|
6
|
+
// l = target lightness, cf = chroma factor (x seed chroma), hOff = hue
|
|
7
|
+
// offset, so other seeds keep the palette's hue drift.
|
|
8
|
+
//
|
|
9
|
+
// 500 is a fixed rung like every other step (its l happens to be the
|
|
10
|
+
// Livestorm seed's own). The seed only contributes hue and chroma, otherwise
|
|
11
|
+
// a light or dark seed would break the light-to-dark ordering of the ramp.
|
|
12
|
+
|
|
13
|
+
export const BRAND_SCALE = {
|
|
14
|
+
100: { l: 0.9823, cf: 0.0376, hOff: 8.79 },
|
|
15
|
+
200: { l: 0.9577, cf: 0.0906, hOff: 4.75 },
|
|
16
|
+
300: { l: 0.9290, cf: 0.1542, hOff: 1.04 },
|
|
17
|
+
400: { l: 0.7250, cf: 0.6476, hOff: 0.44 },
|
|
18
|
+
500: { l: 0.5961, cf: 1, hOff: 0 },
|
|
19
|
+
600: { l: 0.4428, cf: 0.9451, hOff: 0.66 },
|
|
20
|
+
700: { l: 0.3137, cf: 0.7551, hOff: 0.89 },
|
|
21
|
+
800: { l: 0.2729, cf: 0.6492, hOff: 0.79 },
|
|
22
|
+
900: { l: 0.2412, cf: 0.5471, hOff: 0.34 },
|
|
23
|
+
} as const
|
|
24
|
+
|
|
25
|
+
export const NEUTRAL_SCALE = {
|
|
26
|
+
lightBg: { l: 0.9904, cf: 0.0206, hOff: -4.21 },
|
|
27
|
+
100: { l: 0.9760, cf: 0.0318, hOff: 6.01 },
|
|
28
|
+
200: { l: 0.9549, cf: 0.0452, hOff: 4.82 },
|
|
29
|
+
300: { l: 0.9278, cf: 0.0646, hOff: 5.95 },
|
|
30
|
+
400: { l: 0.7065, cf: 0.2241, hOff: 5.20 },
|
|
31
|
+
500: { l: 0.5686, cf: 0.3199, hOff: 4.13 },
|
|
32
|
+
600: { l: 0.4480, cf: 0.3622, hOff: 3.67 },
|
|
33
|
+
// 650/675: dark elevated-surface primitives (surface-neutral-elevated-200/100)
|
|
34
|
+
// from the 2026-09 Supernova sync; calibrated like the rest of the ramp.
|
|
35
|
+
650: { l: 0.4091, cf: 0.3771, hOff: 3.65 },
|
|
36
|
+
675: { l: 0.3584, cf: 0.3331, hOff: 2.93 },
|
|
37
|
+
700: { l: 0.3109, cf: 0.3019, hOff: 2.71 },
|
|
38
|
+
800: { l: 0.2609, cf: 0.2425, hOff: 3.52 },
|
|
39
|
+
900: { l: 0.2219, cf: 0.1955, hOff: 4.56 },
|
|
40
|
+
darkBg: { l: 0.1847, cf: 0.1470, hOff: 4.16 },
|
|
41
|
+
} as const
|
|
42
|
+
|
|
43
|
+
export const ALPHA_STEPS = [0.04, 0.08, 0.12, 0.2, 0.32, 0.52, 0.72, 0.84, 0.92] as const
|
|
44
|
+
|
|
45
|
+
// --- Types ---
|
|
46
|
+
|
|
47
|
+
export interface ScaleStep {
|
|
48
|
+
l: number
|
|
49
|
+
cf: number
|
|
50
|
+
hOff: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface OklchColor {
|
|
54
|
+
mode: 'oklch'
|
|
55
|
+
l: number
|
|
56
|
+
c: number
|
|
57
|
+
h: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface BrandScale {
|
|
61
|
+
100: string; 200: string; 300: string
|
|
62
|
+
400: string; 500: string; 600: string
|
|
63
|
+
700: string; 800: string; 900: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface NeutralScale {
|
|
67
|
+
lightBg: string; darkBg: string
|
|
68
|
+
100: string; 200: string; 300: string
|
|
69
|
+
400: string; 500: string; 600: string
|
|
70
|
+
650: string; 675: string
|
|
71
|
+
700: string; 800: string; 900: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// --- Color math ---
|
|
75
|
+
|
|
76
|
+
const toOklch = converter('oklch')
|
|
77
|
+
const toRgb = converter('rgb')
|
|
78
|
+
|
|
79
|
+
export function seedToOklch(hex: string): OklchColor {
|
|
80
|
+
const parsed = parse(hex)
|
|
81
|
+
if (!parsed) throw new Error(`Invalid color: ${hex}`)
|
|
82
|
+
const oklch = toOklch(parsed)
|
|
83
|
+
return {
|
|
84
|
+
mode: 'oklch',
|
|
85
|
+
l: oklch.l ?? 0,
|
|
86
|
+
c: oklch.c ?? 0,
|
|
87
|
+
h: oklch.h ?? 0,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Recover the seed (hue + chroma) whose brand rung `step` renders as `hex` —
|
|
92
|
+
// the inverse of stepOklch: undo the rung's chroma factor and hue offset.
|
|
93
|
+
// Generation reads only hue + chroma, so lightness is carried through but
|
|
94
|
+
// irrelevant. NOTE: this round-trips losslessly only for rungs whose forward
|
|
95
|
+
// transform stays in sRGB gamut — the cf≈1 rungs (400/500/600). Light/dark
|
|
96
|
+
// rungs clamp on the way out and cannot be inverted exactly, hence the narrow
|
|
97
|
+
// step type.
|
|
98
|
+
export function seedFromBrandStep(hex: string, step: 400 | 500 | 600): OklchColor {
|
|
99
|
+
const seed = seedToOklch(hex)
|
|
100
|
+
const { cf, hOff } = BRAND_SCALE[step]
|
|
101
|
+
return { mode: 'oklch', l: seed.l, c: seed.c / cf, h: seed.h - hOff }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function clampedOklch(seed: OklchColor, l: number, cf: number, hue: number = seed.h): OklchColor {
|
|
105
|
+
const clamped = clampChroma({ mode: 'oklch', l, c: seed.c * cf, h: hue }, 'oklch')
|
|
106
|
+
return { mode: 'oklch', l: clamped.l, c: clamped.c, h: clamped.h ?? hue }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function oklchToHex(color: OklchColor): string {
|
|
110
|
+
return formatHex(color)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// sRGB string forms matching the ui-tokens convention (rgb / rgba). Applied
|
|
114
|
+
// app tokens use these so they line up exactly with the static palette and the
|
|
115
|
+
// hex swatch previews, instead of round-tripping through the browser's own
|
|
116
|
+
// oklch pipeline (which can quantize a rung differently).
|
|
117
|
+
function rgbChannels(color: OklchColor): [number, number, number] {
|
|
118
|
+
const { r, g, b } = toRgb(color)
|
|
119
|
+
const to255 = (v: number) => Math.round(Math.min(1, Math.max(0, v)) * 255)
|
|
120
|
+
return [to255(r), to255(g), to255(b)]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function oklchToRgb(color: OklchColor): string {
|
|
124
|
+
const [r, g, b] = rgbChannels(color)
|
|
125
|
+
return `rgb(${r}, ${g}, ${b})`
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function oklchToRgba(color: OklchColor, alpha: number): string {
|
|
129
|
+
const [r, g, b] = rgbChannels(color)
|
|
130
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// --- Scale generators ---
|
|
134
|
+
// One pair of helpers drives every ramp (brand, neutral, legacy, email): a
|
|
135
|
+
// scale lives in its {l, cf, hOff} table and is applied to the seed here, so
|
|
136
|
+
// there is a single place that turns a table into colors.
|
|
137
|
+
|
|
138
|
+
export function stepOklch(seed: OklchColor, target: ScaleStep): OklchColor {
|
|
139
|
+
return clampedOklch(seed, target.l, target.cf, seed.h + target.hOff)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Object.keys() widens to string[]; the cast restores the table's key type.
|
|
143
|
+
export function scaleToOklch<K extends PropertyKey>(
|
|
144
|
+
seed: OklchColor,
|
|
145
|
+
table: Record<K, ScaleStep>,
|
|
146
|
+
): Record<K, OklchColor> {
|
|
147
|
+
const out = {} as Record<K, OklchColor>
|
|
148
|
+
for (const key of Object.keys(table) as unknown as K[]) {
|
|
149
|
+
out[key] = stepOklch(seed, table[key])
|
|
150
|
+
}
|
|
151
|
+
return out
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function scaleToHex<K extends PropertyKey>(
|
|
155
|
+
seed: OklchColor,
|
|
156
|
+
table: Record<K, ScaleStep>,
|
|
157
|
+
): Record<K, string> {
|
|
158
|
+
const out = {} as Record<K, string>
|
|
159
|
+
for (const key of Object.keys(table) as unknown as K[]) {
|
|
160
|
+
out[key] = oklchToHex(stepOklch(seed, table[key]))
|
|
161
|
+
}
|
|
162
|
+
return out
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const generateBrandOklch = (seed: OklchColor): Record<keyof BrandScale, OklchColor> =>
|
|
166
|
+
scaleToOklch(seed, BRAND_SCALE)
|
|
167
|
+
|
|
168
|
+
export const generateNeutralOklch = (seed: OklchColor): Record<keyof NeutralScale, OklchColor> =>
|
|
169
|
+
scaleToOklch(seed, NEUTRAL_SCALE)
|
|
170
|
+
|
|
171
|
+
export const generateBrandScale = (seed: OklchColor): BrandScale => scaleToHex(seed, BRAND_SCALE)
|
|
172
|
+
|
|
173
|
+
export const generateNeutralScale = (seed: OklchColor): NeutralScale => scaleToHex(seed, NEUTRAL_SCALE)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { scaleToOklch, scaleToHex, type OklchColor } from './colorScale'
|
|
2
|
+
|
|
3
|
+
// --- Legacy OKLCH targets ---
|
|
4
|
+
// Calibrated to reproduce the static legacy palette (--color-livestorm-*,
|
|
5
|
+
// --color-grey-*) exactly at the Livestorm seed (#3073ff). Same l/cf/hOff
|
|
6
|
+
// scheme as colorScale.ts.
|
|
7
|
+
|
|
8
|
+
const LEGACY_BRAND_SCALE = {
|
|
9
|
+
100: { l: 0.9673, cf: 0.0697, hOff: 7.45 },
|
|
10
|
+
200: { l: 0.9247, cf: 0.1567, hOff: 5.10 },
|
|
11
|
+
300: { l: 0.8398, cf: 0.3526, hOff: 3.60 },
|
|
12
|
+
400: { l: 0.7232, cf: 0.6433, hOff: 3.08 },
|
|
13
|
+
// no 500 in the legacy palette
|
|
14
|
+
600: { l: 0.5316, cf: 1.1786, hOff: 0.66 },
|
|
15
|
+
700: { l: 0.4428, cf: 0.9453, hOff: 0.66 },
|
|
16
|
+
800: { l: 0.3595, cf: 0.8656, hOff: 1.67 },
|
|
17
|
+
} as const
|
|
18
|
+
|
|
19
|
+
const LEGACY_NEUTRAL_SCALE = {
|
|
20
|
+
100: { l: 0.9759, cf: 0.0132, hOff: 2.00 },
|
|
21
|
+
200: { l: 0.9469, cf: 0.0269, hOff: -22.72 },
|
|
22
|
+
300: { l: 0.8605, cf: 0.0702, hOff: -27.13 },
|
|
23
|
+
600: { l: 0.5258, cf: 0.1230, hOff: -22.05 },
|
|
24
|
+
700: { l: 0.4634, cf: 0.1139, hOff: -20.73 },
|
|
25
|
+
900: { l: 0.2831, cf: 0.0610, hOff: -33.07 },
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
// --- Types ---
|
|
29
|
+
|
|
30
|
+
type LegacyBrandStep = 100 | 200 | 300 | 400 | 600 | 700 | 800
|
|
31
|
+
type LegacyNeutralStep = 100 | 200 | 300 | 600 | 700 | 900
|
|
32
|
+
|
|
33
|
+
interface LegacyBrandScale {
|
|
34
|
+
100: string; 200: string; 300: string; 400: string
|
|
35
|
+
600: string; 700: string; 800: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface LegacyNeutralScale {
|
|
39
|
+
100: string; 200: string; 300: string
|
|
40
|
+
600: string; 700: string; 900: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const LEGACY_BRAND_STEPS: readonly LegacyBrandStep[] = [100, 200, 300, 400, 600, 700, 800] as const
|
|
44
|
+
export const LEGACY_NEUTRAL_STEPS: readonly LegacyNeutralStep[] = [100, 200, 300, 600, 700, 900] as const
|
|
45
|
+
|
|
46
|
+
// --- Generators ---
|
|
47
|
+
|
|
48
|
+
export const generateLegacyBrandOklch = (seed: OklchColor): Record<LegacyBrandStep, OklchColor> =>
|
|
49
|
+
scaleToOklch(seed, LEGACY_BRAND_SCALE)
|
|
50
|
+
|
|
51
|
+
export const generateLegacyNeutralOklch = (seed: OklchColor): Record<LegacyNeutralStep, OklchColor> =>
|
|
52
|
+
scaleToOklch(seed, LEGACY_NEUTRAL_SCALE)
|
|
53
|
+
|
|
54
|
+
export const generateLegacyBrandScale = (seed: OklchColor): LegacyBrandScale =>
|
|
55
|
+
scaleToHex(seed, LEGACY_BRAND_SCALE)
|
|
56
|
+
|
|
57
|
+
export const generateLegacyNeutralScale = (seed: OklchColor): LegacyNeutralScale =>
|
|
58
|
+
scaleToHex(seed, LEGACY_NEUTRAL_SCALE)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { seedToOklch, generateBrandScale } from './colorScale'
|
|
2
|
+
|
|
3
|
+
const PRESET_COLORS = [
|
|
4
|
+
'#9b6dd7', '#4829c7', '#0b42c3', '#1e7fb8',
|
|
5
|
+
'#1ba8a0', '#0e7a5e', '#2d8c4e', '#5a7a11',
|
|
6
|
+
'#8a7b12', '#b0780e', '#d05a3a', '#a12244',
|
|
7
|
+
'#c41560', '#d436a8', '#9b21c4', '#6321a8',
|
|
8
|
+
] as const
|
|
9
|
+
|
|
10
|
+
interface PresetSwatch {
|
|
11
|
+
seed: string
|
|
12
|
+
light: string
|
|
13
|
+
dark: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const presetSwatches: PresetSwatch[] = PRESET_COLORS.map((hex) => {
|
|
17
|
+
const brand = generateBrandScale(seedToOklch(hex))
|
|
18
|
+
return { seed: hex, light: brand[400], dark: brand[600] }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export interface BrandedPalette extends PresetSwatch {
|
|
22
|
+
name: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Branded workspace palettes from the Email Templates V2 figma, mirrored by
|
|
26
|
+
// @livestorm/ui-tokens (supernova: base = Livestorm, customer1/2/3).
|
|
27
|
+
// seed = brand-500, light = brand-400, dark = brand-600.
|
|
28
|
+
export const brandedPalettes: BrandedPalette[] = [
|
|
29
|
+
{ name: 'Livestorm', seed: '#3073ff', light: '#76a4ff', dark: '#0b42c3' },
|
|
30
|
+
{ name: 'Customer 1', seed: '#d04134', light: '#e5837d', dark: '#9e2114' },
|
|
31
|
+
{ name: 'Customer 2', seed: '#1a8290', light: '#29b0c2', dark: '#115d67' },
|
|
32
|
+
{ name: 'Customer 3', seed: '#9a51e2', light: '#bd8aef', dark: '#7420c9' },
|
|
33
|
+
]
|