@livestorm/ui-vue 2.0.5 → 3.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.
Files changed (85) hide show
  1. package/README.md +6 -3
  2. package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-LpdvGf5X.mjs +6093 -0
  3. package/dist/components.mjs +105 -96
  4. package/dist/index.mjs +68 -64
  5. package/dist/layouts.mjs +15 -15
  6. package/dist/types/index.d.ts +5 -1
  7. package/dist/types/src/components/UiActionBar/UiActionBar.vue.d.ts +3 -1
  8. package/dist/types/src/components/UiActionBar/UiActionBarItem.vue.d.ts +18 -3
  9. package/dist/types/src/components/UiActionBar/types.d.ts +3 -1
  10. package/dist/types/src/components/UiButton/UiButton.vue.d.ts +17 -13
  11. package/dist/types/src/components/UiButtonGroup/UiButtonGroupItem.vue.d.ts +3 -0
  12. package/dist/types/src/components/UiButtonGroup/types.d.ts +1 -1
  13. package/dist/types/src/components/UiDropdown/UiDropdownItem.vue.d.ts +6 -4
  14. package/dist/types/src/components/UiInputEmoji/UiInputEmoji.vue.d.ts +45 -0
  15. package/dist/types/src/components/UiInputEmoji/index.d.ts +1 -0
  16. package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +67 -0
  17. package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +81 -0
  18. package/dist/types/src/components/UiInputRadio/index.d.ts +3 -0
  19. package/dist/types/src/components/UiInputRadio/types.d.ts +19 -0
  20. package/dist/types/src/components/UiLoader/UiLoader.vue.d.ts +6 -5
  21. package/dist/types/src/components/UiLogo/UiLogo.vue.d.ts +9 -3
  22. package/dist/types/src/components/UiPage/UiPage.vue.d.ts +34 -0
  23. package/dist/types/src/components/UiProgress/UiProgress.vue.d.ts +1 -1
  24. package/dist/types/src/components/UiSwitch/UiSwitch.vue.d.ts +109 -0
  25. package/dist/types/src/components/UiSwitch/index.d.ts +2 -0
  26. package/dist/types/src/components/UiSwitch/types.d.ts +3 -0
  27. package/dist/types/src/components/UiTabs/UiTabs.vue.d.ts +5 -2
  28. package/dist/types/src/composables/useContentMode.d.ts +25 -0
  29. package/dist/types/src/composables/useIconSize.d.ts +11 -0
  30. package/dist/types/src/constants/sizes.d.ts +1 -1
  31. package/dist/ui-vue.css +1 -1
  32. package/index.d.ts +4 -0
  33. package/index.js +8 -0
  34. package/package.json +6 -3
  35. package/src/components/Toast/ToastMessage.vue +6 -6
  36. package/src/components/UiActionBar/UiActionBar.vue +11 -2
  37. package/src/components/UiActionBar/UiActionBarItem.vue +56 -32
  38. package/src/components/UiActionBar/types.ts +3 -1
  39. package/src/components/UiAvatar/UiAvatar.vue +1 -1
  40. package/src/components/UiBanner/UiBanner.vue +7 -7
  41. package/src/components/UiButton/UiButton.vue +9 -9
  42. package/src/components/UiButtonGroup/UiButtonGroup.vue +2 -0
  43. package/src/components/UiButtonGroup/UiButtonGroupItem.vue +12 -0
  44. package/src/components/UiButtonGroup/types.ts +1 -1
  45. package/src/components/UiCarousel/UiCarousel.vue +2 -2
  46. package/src/components/UiColorPicker/UiColorPicker.vue +5 -5
  47. package/src/components/UiDialog/MarketplaceHeaderExample.vue +2 -2
  48. package/src/components/UiDialog/UiDialogClose.vue +1 -1
  49. package/src/components/UiDropdown/UiDropdownContent.vue +2 -2
  50. package/src/components/UiDropdown/UiDropdownItem.vue +8 -1
  51. package/src/components/UiDropdown/UiDropdownTrigger.vue +6 -7
  52. package/src/components/UiFieldWrapper/UiFieldWrapper.vue +1 -1
  53. package/src/components/UiInputCheckbox/UiInputCheckbox.vue +2 -2
  54. package/src/components/UiInputEmoji/UiInputEmoji.vue +173 -0
  55. package/src/components/UiInputEmoji/index.ts +1 -0
  56. package/src/components/UiInputRadio/UiInputRadio.vue +374 -0
  57. package/src/components/UiInputRadio/UiInputRadioGroup.vue +156 -0
  58. package/src/components/UiInputRadio/index.ts +3 -0
  59. package/src/components/UiInputRadio/types.ts +25 -0
  60. package/src/components/UiLoader/UiLoader.vue +8 -10
  61. package/src/components/UiLogo/UiLogo.vue +41 -7
  62. package/src/components/UiMenu/UiMenuContent.vue +1 -1
  63. package/src/components/UiMenu/UiMenuGroup.vue +1 -1
  64. package/src/components/UiMenu/UiMenuItem.vue +1 -1
  65. package/src/components/UiMenu/UiMenuSubContent.vue +1 -1
  66. package/src/components/UiMenu/UiMenuSubTrigger.vue +2 -2
  67. package/src/components/UiPage/UiPage.vue +102 -34
  68. package/src/components/UiPageHeader/UiPageHeader.vue +29 -9
  69. package/src/components/UiRoomButton/UiRoomButton.vue +4 -4
  70. package/src/components/UiShell/UiShell.vue +1 -1
  71. package/src/components/UiSideMenu/UiSideMenu.vue +13 -10
  72. package/src/components/UiSideMenu/UiSideMenuItemContent.vue +2 -2
  73. package/src/components/UiStepper/UiStepperIndicator.vue +8 -11
  74. package/src/components/UiSwitch/UiSwitch.vue +359 -0
  75. package/src/components/UiSwitch/index.ts +2 -0
  76. package/src/components/UiSwitch/types.ts +5 -0
  77. package/src/components/UiTabs/UiTabs.vue +7 -4
  78. package/src/components/UiTextField/UiTextField.vue +6 -11
  79. package/src/components/UiTimeline/README.md +6 -6
  80. package/src/components/UiTimeline/UiTimelineItem.vue +3 -3
  81. package/src/composables/useContentMode.ts +53 -0
  82. package/src/composables/useIconSize.ts +28 -0
  83. package/src/constants/sizes.ts +1 -1
  84. package/src/global.d.ts +8 -0
  85. package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-DJFdCn2R.mjs +0 -5456
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, inject } from 'vue'
2
+ import { computed, inject, useAttrs } from 'vue'
3
3
  import {
4
4
  TooltipProvider,
5
5
  TooltipRoot,
@@ -9,16 +9,21 @@ import {
9
9
  TooltipArrow,
10
10
  } from 'reka-ui'
11
11
  import type { RouteLocationRaw } from 'vue-router'
12
+ import { useIconSize } from '../../composables/useIconSize'
13
+ import type { ActionBarSize } from './types'
12
14
  import { ACTION_BAR_CONTEXT_KEY } from './types'
13
15
 
14
16
  interface UiActionBarItemProps {
15
- /** Icon class, e.g. `i-ls-trash`. */
17
+ /**
18
+ * Icon class, e.g. `i-ph-trash`. Ignored when the `icon` slot is filled —
19
+ * use the slot for glyphs no icon class covers (emoji, an avatar, a dot).
20
+ */
16
21
  icon?: string
17
22
  /** Visible/accessible label. Required so icon-only items stay accessible. */
18
23
  label: string
19
24
  /**
20
25
  * Optional icon class rendered next to the label inside the hover tooltip —
21
- * e.g. `i-ls-external-link` to flag that the action opens in a new tab.
26
+ * e.g. `i-ph-arrow-square-out` to flag that the action opens in a new tab.
22
27
  * Only shown for icon-only items (the tooltip is suppressed when
23
28
  * `showLabel` is set).
24
29
  */
@@ -55,13 +60,20 @@ const props = withDefaults(defineProps<UiActionBarItemProps>(), {
55
60
 
56
61
  const emit = defineEmits<{ click: [event: MouseEvent] }>()
57
62
 
63
+ // The trigger is rendered through `TooltipTrigger as-child`, which merges its
64
+ // own props over the child vnode — so normal attribute fallthrough never
65
+ // reaches the button. Read the consumer's `data-testid` explicitly, otherwise
66
+ // every item in a wrapped bar would share one indistinguishable test id.
67
+ const attrs = useAttrs()
68
+ const testId = computed(() => (attrs['data-testid'] as string | undefined) ?? 'ui-action-bar-item')
69
+
58
70
  const ctx = inject(ACTION_BAR_CONTEXT_KEY, null)
59
71
  const size = computed(() => ctx?.size.value ?? 'md')
60
72
  const orientation = computed(() => ctx?.orientation.value ?? 'horizontal')
61
73
 
62
74
  // Icon-only items get a tooltip; labeled items don't need one.
63
75
  const showTooltip = computed(() => !props.showLabel)
64
- const tooltipSide = computed(() => (orientation.value === 'vertical' ? 'right' : 'bottom'))
76
+ const tooltipSide = computed(() => ctx?.tooltipSide.value ?? (orientation.value === 'vertical' ? 'right' : 'bottom'))
65
77
 
66
78
  const component = computed(() => {
67
79
  if (props.disabled) return { is: 'button', props: { type: 'button', disabled: true } }
@@ -70,22 +82,30 @@ const component = computed(() => {
70
82
  return { is: 'button', props: { type: 'button' } }
71
83
  })
72
84
 
73
- const sizeClasses = computed(() => {
74
- const square = size.value === 'sm' ? 'size-8' : 'size-10'
75
- // Labeled items grow with their text instead of staying square.
76
- const padded = size.value === 'sm' ? 'h-8 px-2.5 gap-1' : 'h-10 px-3 gap-1.5'
77
- return props.showLabel ? padded : square
78
- })
85
+ // Per-size layout classes. `square` sizes icon-only items; labeled items use
86
+ // `padded` and grow with their text instead of staying square. xs reuses the
87
+ // sm text style — the design system defines no xs one.
88
+ const SIZE_CLASSES: Record<ActionBarSize, { square: string, padded: string, text: string }> = {
89
+ xs: { square: 'size-6', padded: 'h-6 px-2 gap-1', text: 'text-action-button-sm' },
90
+ sm: { square: 'size-8', padded: 'h-8 px-2.5 gap-1', text: 'text-action-button-sm' },
91
+ md: { square: 'size-10', padded: 'h-10 px-3 gap-1.5', text: 'text-action-button-md' },
92
+ }
79
93
 
80
- const iconSizeClass = computed(() => (size.value === 'sm' ? 'size-4' : 'size-5'))
81
- const textClass = computed(() => (size.value === 'sm' ? 'text-action-button-sm' : 'text-action-button-md'))
94
+ const sizeConfig = computed(() => SIZE_CLASSES[size.value])
95
+ const iconSizeClass = useIconSize(size)
82
96
 
83
- // Persistent pressed background when the item is an active toggle. Uses the
84
- // dedicated neutral "active" token so an active-but-not-hovered toggle stays
85
- // visually distinct from a merely hovered item.
86
- const stateClasses = computed(() => ({
87
- 'bg-actions-neutral-active-overlay': props.active === true,
88
- }))
97
+ // Everything bound on the item root: per-size layout and text classes, plus
98
+ // the persistent pressed background when the item is an active toggle (the
99
+ // dedicated neutral "active" token keeps an active-but-not-hovered toggle
100
+ // visually distinct from a merely hovered item).
101
+ const itemClasses = computed(() => [
102
+ props.showLabel ? sizeConfig.value.padded : sizeConfig.value.square,
103
+ sizeConfig.value.text,
104
+ {
105
+ 'bg-actions-neutral-active-overlay': props.active === true,
106
+ 'cursor-pointer': !props.disabled,
107
+ },
108
+ ])
89
109
 
90
110
  function onClick (event: MouseEvent) {
91
111
  if (props.disabled) {
@@ -113,20 +133,22 @@ function onClick (event: MouseEvent) {
113
133
  v-bind="component.props"
114
134
  :aria-label="label"
115
135
  :aria-pressed="active"
116
- data-testid="ui-action-bar-item"
136
+ :data-testid="testId"
117
137
  class="group inline-flex items-center justify-center rounded-lg shrink-0 outline-none transition-colors
118
138
  text-actions-neutral-idle
119
139
  hover:bg-actions-neutral-hover-overlay
120
140
  focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus-ring
121
141
  disabled:opacity-50 disabled:cursor-not-allowed"
122
- :class="[sizeClasses, textClass, stateClasses, { 'cursor-pointer': !disabled }]"
142
+ :class="itemClasses"
123
143
  @click="onClick"
124
144
  >
125
- <span
126
- v-if="icon"
127
- :class="[icon, iconSizeClass]"
128
- class="shrink-0"
129
- />
145
+ <slot name="icon">
146
+ <span
147
+ v-if="icon"
148
+ :class="[icon, iconSizeClass]"
149
+ class="shrink-0"
150
+ />
151
+ </slot>
130
152
  </component>
131
153
  </TooltipTrigger>
132
154
  <TooltipPortal>
@@ -160,20 +182,22 @@ function onClick (event: MouseEvent) {
160
182
  v-bind="component.props"
161
183
  :aria-label="label"
162
184
  :aria-pressed="active"
163
- data-testid="ui-action-bar-item"
185
+ :data-testid="testId"
164
186
  class="group inline-flex items-center justify-center rounded-lg shrink-0 outline-none transition-colors
165
187
  text-actions-neutral-idle
166
188
  hover:bg-actions-neutral-hover-overlay
167
189
  focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus-ring
168
190
  disabled:opacity-50 disabled:cursor-not-allowed"
169
- :class="[sizeClasses, textClass, stateClasses, { 'cursor-pointer': !disabled }]"
191
+ :class="itemClasses"
170
192
  @click="onClick"
171
193
  >
172
- <span
173
- v-if="icon"
174
- :class="[icon, iconSizeClass]"
175
- class="shrink-0"
176
- />
194
+ <slot name="icon">
195
+ <span
196
+ v-if="icon"
197
+ :class="[icon, iconSizeClass]"
198
+ class="shrink-0"
199
+ />
200
+ </slot>
177
201
  <span class="whitespace-nowrap">{{ label }}</span>
178
202
  </component>
179
203
  </template>
@@ -1,7 +1,7 @@
1
1
  import type { ComputedRef, InjectionKey } from 'vue'
2
2
 
3
3
  export type ActionBarOrientation = 'horizontal' | 'vertical'
4
- export type ActionBarSize = 'sm' | 'md'
4
+ export type ActionBarSize = 'xs' | 'sm' | 'md'
5
5
  export type ActionBarPosition =
6
6
  | 'top-left'
7
7
  | 'top-right'
@@ -9,10 +9,12 @@ export type ActionBarPosition =
9
9
  | 'center-right'
10
10
  | 'bottom-left'
11
11
  | 'bottom-right'
12
+ export type ActionBarTooltipSide = 'top' | 'right' | 'bottom' | 'left'
12
13
 
13
14
  export interface ActionBarContext {
14
15
  orientation: ComputedRef<ActionBarOrientation>
15
16
  size: ComputedRef<ActionBarSize>
17
+ tooltipSide: ComputedRef<ActionBarTooltipSide | undefined>
16
18
  }
17
19
 
18
20
  /**
@@ -80,7 +80,7 @@ const props = withDefaults(defineProps<Props>(), {
80
80
  variant: 'default',
81
81
  alt: undefined,
82
82
  color: null,
83
- icon: 'i-ls-user-fill',
83
+ icon: 'i-ph-user-fill',
84
84
  })
85
85
 
86
86
  const customStyle = computed(() => {
@@ -115,11 +115,11 @@ onMounted(() => {
115
115
  // Icon mapping based on color
116
116
  const iconClass = computed(() => {
117
117
  const colorMap = {
118
- neutral: 'i-ls-info',
119
- primary: 'i-ls-info',
120
- success: 'i-ls-check-circle',
121
- warning: 'i-ls-alert-triangle',
122
- danger: 'i-ls-alert-circle',
118
+ neutral: 'i-ph-info',
119
+ primary: 'i-ph-info',
120
+ success: 'i-ph-check-circle',
121
+ warning: 'i-ph-warning',
122
+ danger: 'i-ph-warning-circle',
123
123
  }
124
124
  return colorMap[props.color]
125
125
  })
@@ -295,7 +295,7 @@ function handleAction () {
295
295
  <!-- Icon -->
296
296
  <div
297
297
  v-if="showIcon"
298
- class="shrink-0 w-4 h-4"
298
+ class="shrink-0 size-5"
299
299
  :class="[iconClass, titleColorClass]"
300
300
  />
301
301
 
@@ -355,7 +355,7 @@ function handleAction () {
355
355
  :label="translate('general_close')"
356
356
  variant="tertiary"
357
357
  color="neutral"
358
- icon="i-ls-x"
358
+ icon="i-ph-x"
359
359
  class="shrink-0"
360
360
  data-testid="ui-banner-close"
361
361
  @click="handleClose"
@@ -86,6 +86,7 @@ import { computed, getCurrentInstance, watch } from 'vue'
86
86
  import { useSlotsAsComponents, RenderSlot } from '../../utils/slot'
87
87
  import { SIZES, type Size } from '../../constants'
88
88
  import { useButtonStyles, BUTTON_COLORS, type ButtonVariant, type ButtonColor } from '../../composables/useButtonStyles'
89
+ import { useIconSize } from '../../composables/useIconSize'
89
90
  import UiLoader from '../UiLoader/UiLoader.vue'
90
91
  import UiTooltip from '../UiTooltip/UiTooltip.vue'
91
92
  import type { RouteLocationRaw } from 'vue-router'
@@ -107,7 +108,7 @@ export default {
107
108
  default: 'cta',
108
109
  },
109
110
  size: {
110
- type: String,
111
+ type: String as () => Size,
111
112
  default: 'md',
112
113
  validator: (value: Size) => SIZES.includes(value),
113
114
  },
@@ -223,11 +224,7 @@ export default {
223
224
  const isLink = computed(() => !finalDisabled.value && (!!props.href || !!props.to))
224
225
 
225
226
  // Icon size classes
226
- const iconSizeClasses = computed(() => ({
227
- 'size-3': props.size === 'sm',
228
- 'size-4': props.size === 'md',
229
- 'size-5': props.size === 'lg',
230
- }))
227
+ const iconSizeClasses = useIconSize(() => props.size)
231
228
 
232
229
  // Label value
233
230
  const labelFinal = computed(() => {
@@ -237,8 +234,8 @@ export default {
237
234
 
238
235
  // Fallback: generate label from icon for icon-only buttons
239
236
  if (isSquare.value && props.icon && typeof props.icon === 'string') {
240
- // Extract readable name from icon class (e.g. "i-ls-settings" -> "settings")
241
- const iconName = props.icon.replace(/^i-ls-/, '').replace(/-/g, ' ')
237
+ // Extract readable name from icon class (e.g. "i-ph-gear" -> "gear")
238
+ const iconName = props.icon.replace(/^i-[a-z0-9]+-/, '').replace(/-/g, ' ')
242
239
  return iconName ? `${iconName} icon` : 'icon'
243
240
  }
244
241
 
@@ -258,17 +255,20 @@ export default {
258
255
  // Style: Dynamic button classes
259
256
  const buttonClasses = computed(() => [
260
257
  {
258
+ 'size-6': isSquare.value && props.size === 'xs',
261
259
  'size-8': isSquare.value && props.size === 'sm',
262
260
  'size-10': isSquare.value && props.size === 'md',
263
261
  'size-12': isSquare.value && props.size === 'lg',
264
262
  },
265
263
  { 'grid place-items-center': isSquare.value },
266
264
  {
265
+ // xs reuses the sm text style — the design system defines no xs one
266
+ 'h-6 text-action-button-sm': props.size === 'xs',
267
267
  'h-8 text-action-button-sm': props.size === 'sm',
268
268
  'h-10 text-action-button-md': props.size === 'md',
269
269
  'h-12 text-action-button-lg': props.size === 'lg',
270
270
  },
271
- props.size === 'sm' ? 'px-2.5' : props.size === 'lg' ? 'px-4' : 'px-3',
271
+ props.size === 'xs' ? 'px-2' : props.size === 'sm' ? 'px-2.5' : props.size === 'lg' ? 'px-4' : 'px-3',
272
272
  // Links should be inline-flex to prevent full width while maintaining flex centering
273
273
  { 'inline-flex': isLink.value },
274
274
  ])
@@ -124,6 +124,7 @@ useResizeObserver(resizeTarget, debouncedUpdate)
124
124
 
125
125
  const sizeClasses = computed(() => {
126
126
  switch (props.size) {
127
+ case 'xs': return 'h-6 p-0.5 gap-0.5'
127
128
  case 'sm': return 'h-8 p-0.5 gap-0.5'
128
129
  case 'lg': return 'h-12 p-1 gap-1'
129
130
  case 'md':
@@ -150,6 +151,7 @@ const INDICATOR_BG: Record<ButtonGroupColor, string> = {
150
151
  }
151
152
 
152
153
  const INDICATOR_RADIUS: Record<ButtonGroupSize, string> = {
154
+ xs: 'rounded-md',
153
155
  sm: 'rounded-md',
154
156
  md: 'rounded-lg',
155
157
  lg: 'rounded-lg',
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { ToggleGroupItem } from 'reka-ui'
3
3
  import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'
4
+ import { useIconSize } from '../../composables/useIconSize'
4
5
  import type { ButtonGroupColor } from './types'
5
6
  import {
6
7
  BUTTON_GROUP_COLOR_KEY,
@@ -12,11 +13,14 @@ interface UiButtonGroupItemProps {
12
13
  value: string
13
14
  disabled?: boolean
14
15
  ariaLabel?: string
16
+ /** Icon class (e.g. "i-ph-text-align-left") rendered before the slot, sized per group size. */
17
+ icon?: string
15
18
  }
16
19
 
17
20
  const props = withDefaults(defineProps<UiButtonGroupItemProps>(), {
18
21
  disabled: false,
19
22
  ariaLabel: undefined,
23
+ icon: undefined,
20
24
  })
21
25
 
22
26
  const size = inject(BUTTON_GROUP_SIZE_KEY, computed(() => 'md' as const))
@@ -55,6 +59,7 @@ onBeforeUnmount(() => {
55
59
  // (rail height − rail padding) in horizontal mode, and drives height in vertical mode.
56
60
  const sizeClasses = computed(() => {
57
61
  switch (size.value) {
62
+ case 'xs': return 'px-2 py-0.5 gap-1 rounded-md text-action-button-sm'
58
63
  case 'sm': return 'px-2.5 py-1.5 gap-1 rounded-md text-action-button-sm'
59
64
  case 'lg': return 'px-6 py-2.5 gap-2 rounded-lg text-action-button-lg'
60
65
  case 'md':
@@ -62,6 +67,8 @@ const sizeClasses = computed(() => {
62
67
  }
63
68
  })
64
69
 
70
+ const iconSizeClass = useIconSize(size)
71
+
65
72
  // Per-color state classes. The selected fill is drawn by the parent's sliding
66
73
  // indicator <div>, so items only set text color per state. Hover bg/text apply
67
74
  // only to unselected items (data-[state=off]) so the hover tint never stacks
@@ -120,6 +127,11 @@ const colorClasses = computed(() => COLOR_CLASSES[color.value])
120
127
  data-[state=off]:data-[disabled]:hover:bg-transparent"
121
128
  :class="[sizeClasses, colorClasses]"
122
129
  >
130
+ <span
131
+ v-if="icon"
132
+ :class="[icon, iconSizeClass]"
133
+ class="shrink-0"
134
+ />
123
135
  <slot />
124
136
  </ToggleGroupItem>
125
137
  </template>
@@ -1,6 +1,6 @@
1
1
  import type { ComputedRef, InjectionKey } from 'vue'
2
2
 
3
- export type ButtonGroupSize = 'sm' | 'md' | 'lg'
3
+ export type ButtonGroupSize = 'xs' | 'sm' | 'md' | 'lg'
4
4
  export type ButtonGroupColor =
5
5
  | 'neutral'
6
6
  | 'neutral-invert'
@@ -176,7 +176,7 @@ defineExpose({
176
176
  variant="tertiary"
177
177
  color="primary"
178
178
  size="md"
179
- icon="i-ls-chevron-left"
179
+ icon="i-ph-caret-left"
180
180
  label="Previous slide"
181
181
  :disabled="!canScrollPrev"
182
182
  @click="scrollPrev"
@@ -205,7 +205,7 @@ defineExpose({
205
205
  variant="tertiary"
206
206
  color="primary"
207
207
  size="md"
208
- icon="i-ls-chevron-right"
208
+ icon="i-ph-caret-right"
209
209
  label="Next slide"
210
210
  :disabled="!canScrollNext"
211
211
  @click="scrollNext"
@@ -54,8 +54,8 @@ const presetsLabel = computed<string | null>(() => {
54
54
  // Preset selector swatch sizes; the selection check icon scales to half the swatch.
55
55
  // maxPerRow is how many swatches (at that size + gap) fit the panel's content width.
56
56
  const SWATCH_SIZE_CLASSES: Record<UiColorPickerSwatchSize, { swatch: string; swatchPickerList: string; indicatorWrapper: string; indicator: string; maxPerRow: number }> = {
57
- sm: { swatch: 'size-6', swatchPickerList: 'gap-2', indicatorWrapper: 'ring-2', indicator: 'h-3 w-3', maxPerRow: 8 },
58
- lg: { swatch: 'size-12', swatchPickerList: 'gap-4', indicatorWrapper: 'ring-4', indicator: 'h-4 w-4', maxPerRow: 4 },
57
+ sm: { swatch: 'size-6', swatchPickerList: 'gap-2', indicatorWrapper: 'ring-2', indicator: 'size-3', maxPerRow: 8 },
58
+ lg: { swatch: 'size-12', swatchPickerList: 'gap-4', indicatorWrapper: 'ring-4', indicator: 'size-5', maxPerRow: 4 },
59
59
  }
60
60
  const swatchSizeClasses = computed(() => SWATCH_SIZE_CLASSES[props.presetConfig?.swatchSize ?? 'lg'])
61
61
 
@@ -328,7 +328,7 @@ watch(
328
328
  <!-- Mode switcher — its own bordered box; hidden when only one mode is available -->
329
329
  <div
330
330
  v-if="showSwitcher"
331
- class="h-full w-20 shrink-0"
331
+ class="h-full w-21 shrink-0"
332
332
  >
333
333
  <UiDropdown
334
334
  v-model="colorMode"
@@ -456,7 +456,7 @@ watch(
456
456
  class="absolute inset-0 flex items-center justify-center rounded-full bg-actions-neutral-hover-overlay opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100"
457
457
  >
458
458
  <div
459
- class="i-ls-check-circle-fill text-text-neutral-invert-tertiary"
459
+ class="i-ph-check-circle-fill text-text-neutral-invert-tertiary"
460
460
  :class="swatchSizeClasses.indicator"
461
461
  />
462
462
  </span>
@@ -472,7 +472,7 @@ watch(
472
472
  :class="swatchSizeClasses.indicatorWrapper"
473
473
  >
474
474
  <div
475
- class="i-ls-check-circle-fill text-text-neutral-invert-base"
475
+ class="i-ph-check-circle-fill text-text-neutral-invert-base"
476
476
  :class="swatchSizeClasses.indicator"
477
477
  />
478
478
  </span>
@@ -150,14 +150,14 @@ const goToImage = (index: number) => {
150
150
  class="absolute left-4 top-1/2 -translate-y-1/2 w-8 h-8 rounded-full bg-surface-neutral-bg-main/80 backdrop-blur-xs flex items-center justify-center hover:bg-surface-neutral-bg-main transition-colors"
151
151
  @click="prevImage"
152
152
  >
153
- <span class="i-ls-chevron-left w-4 h-4 text-text-neutral-base" />
153
+ <span class="i-ph-caret-left w-4 h-4 text-text-neutral-base" />
154
154
  </button>
155
155
  <button
156
156
  v-if="images.length > 1"
157
157
  class="absolute right-4 top-1/2 -translate-y-1/2 w-8 h-8 rounded-full bg-surface-neutral-bg-main/80 backdrop-blur-xs flex items-center justify-center hover:bg-surface-neutral-bg-main transition-colors"
158
158
  @click="nextImage"
159
159
  >
160
- <span class="i-ls-chevron-right w-4 h-4 text-text-neutral-base" />
160
+ <span class="i-ph-caret-right w-4 h-4 text-text-neutral-base" />
161
161
  </button>
162
162
 
163
163
  <!-- Dots indicator -->
@@ -21,7 +21,7 @@ withDefaults(defineProps<UiDialogCloseProps>(), {
21
21
  variant="tertiary"
22
22
  color="neutral"
23
23
  size="md"
24
- icon="i-ls-x-stream"
24
+ icon="i-ph-x"
25
25
  :aria-label="ariaLabel"
26
26
  :data-colors-semantic="mode"
27
27
  />
@@ -46,7 +46,7 @@ const slots = useSlots()
46
46
  v-if="showScrollButtons"
47
47
  class="flex items-center justify-center h-6 cursor-default text-text-neutral-base"
48
48
  >
49
- <div class="i-ls-chevron-up size-4" />
49
+ <div class="i-ph-caret-up size-4" />
50
50
  </SelectScrollUpButton>
51
51
 
52
52
  <SelectViewport class="flex flex-col gap-1">
@@ -57,7 +57,7 @@ const slots = useSlots()
57
57
  v-if="showScrollButtons"
58
58
  class="flex items-center justify-center h-6 cursor-default text-text-neutral-base"
59
59
  >
60
- <div class="i-ls-chevron-down size-4" />
60
+ <div class="i-ph-caret-down size-4" />
61
61
  </SelectScrollDownButton>
62
62
  </div>
63
63
 
@@ -48,8 +48,15 @@ const isMultiple = inject(UI_DROPDOWN_MULTIPLE_KEY, undefined)
48
48
  class="shrink-0 group-data-highlighted:text-actions-neutral-idle group-data-[state=checked]:text-actions-primary-idle"
49
49
  />
50
50
 
51
+ <div
52
+ v-if="$slots.leading"
53
+ class="shrink-0"
54
+ >
55
+ <slot name="leading" />
56
+ </div>
57
+
51
58
  <div class="flex flex-1 flex-col items-start justify-center min-w-0 py-1.5">
52
- <SelectItemText class="whitespace-nowrap text-content-text-md-regular text-actions-neutral-idle group-data-highlighted:text-actions-neutral-idle group-data-[state=checked]:text-actions-primary-idle">
59
+ <SelectItemText class="whitespace-nowrap text-content-text-md-medium text-actions-neutral-idle group-data-highlighted:text-actions-neutral-idle group-data-[state=checked]:text-actions-primary-idle">
53
60
  <slot name="title">
54
61
  {{ title }}
55
62
  </slot>
@@ -3,6 +3,7 @@ import type { SelectTriggerProps } from 'reka-ui'
3
3
  import { SelectIcon, SelectTrigger, SelectValue } from 'reka-ui'
4
4
  import { computed, useSlots } from 'vue'
5
5
  import { useButtonStyles } from '../../composables/useButtonStyles'
6
+ import { useIconSize } from '../../composables/useIconSize'
6
7
 
7
8
  type TSize = 'sm' | 'md' | 'lg' | 'xl'
8
9
  type TColor = 'neutral' | 'primary'
@@ -59,25 +60,21 @@ const sizeClasses = computed(() => {
59
60
  sm: {
60
61
  field: 'h-8 px-2 gap-1',
61
62
  text: 'text-action-button-sm tracking-[0.05px]',
62
- icon: 'size-3',
63
63
  padding: 'px-2',
64
64
  },
65
65
  md: {
66
66
  field: 'h-10 px-3 gap-1.5',
67
67
  text: 'text-action-button-md leading-[20px] tracking-[-0.04px]',
68
- icon: 'size-4',
69
68
  padding: 'px-3',
70
69
  },
71
70
  lg: {
72
71
  field: 'h-12 px-4 gap-2',
73
72
  text: 'text-action-button-lg leading-[20px] tracking-[-0.04px]',
74
- icon: 'size-4',
75
73
  padding: 'px-4',
76
74
  },
77
75
  xl: {
78
76
  field: 'h-16 px-4 gap-2',
79
77
  text: 'text-action-button-lg leading-[24px] tracking-[-0.04px]',
80
- icon: 'size-6',
81
78
  padding: 'px-4',
82
79
  },
83
80
  }
@@ -85,6 +82,8 @@ const sizeClasses = computed(() => {
85
82
  return sizes[props.size]
86
83
  })
87
84
 
85
+ const iconSizeClass = useIconSize(() => props.size)
86
+
88
87
  const fieldClasses = computed(() => {
89
88
  const base = [
90
89
  'focus-indicator-none',
@@ -204,7 +203,7 @@ const iconContainerClasses = computed(() => {
204
203
  isTertiary.value
205
204
  ? textClass.value
206
205
  : isPrimary.value ? 'text-actions-primary-idle' : 'text-text-neutral-tertiary',
207
- sizeClasses.value.icon,
206
+ iconSizeClass.value,
208
207
  ].join(' ')
209
208
  })
210
209
  </script>
@@ -231,8 +230,8 @@ const iconContainerClasses = computed(() => {
231
230
  :class="iconContainerClasses"
232
231
  >
233
232
  <div
234
- class="i-ls-chevron-down transition-transform duration-200 ease-in-out group-data-[state=open]:rotate-180"
235
- :class="sizeClasses.icon"
233
+ class="i-ph-caret-down transition-transform duration-200 ease-in-out group-data-[state=open]:rotate-180"
234
+ :class="iconSizeClass"
236
235
  />
237
236
  </SelectIcon>
238
237
  </SelectTrigger>
@@ -54,7 +54,7 @@ defineExpose({ fieldId })
54
54
  :content="labelInfo"
55
55
  >
56
56
  <div class="flex items-center justify-center size-4 text-text-neutral-secondary cursor-help">
57
- <span class="i-ls-info size-4" />
57
+ <span class="i-ph-info size-4" />
58
58
  </div>
59
59
  </UiTooltip>
60
60
  </div>
@@ -191,7 +191,7 @@ const iconClasses = computed(() => {
191
191
 
192
192
  // Icon type and color based on state
193
193
  if (props.indeterminate) {
194
- baseClasses.push('i-ls-minus text-actions-neutral-invert-idle opacity-100')
194
+ baseClasses.push('i-ph-minus text-actions-neutral-invert-idle opacity-100')
195
195
  } else if (isChecked.value) {
196
196
  // Checked: always visible with light color on primary/error background
197
197
  baseClasses.push('i-ls-checkbox-check text-actions-neutral-invert-idle opacity-100')
@@ -403,7 +403,7 @@ defineExpose({
403
403
  'text-text-primary-base': isChecked && !error,
404
404
  }"
405
405
  >
406
- <span class="i-ls-info-fill size-4" />
406
+ <span class="i-ph-info-fill size-4" />
407
407
  </div>
408
408
  </UiTooltip>
409
409
  </div>