@livestorm/ui-vue 2.0.4 → 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 (89) hide show
  1. package/README.md +8 -5
  2. package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-LpdvGf5X.mjs +6093 -0
  3. package/dist/a11y-BhF_nl7L.mjs +159 -0
  4. package/dist/components.mjs +105 -96
  5. package/dist/index.mjs +68 -64
  6. package/dist/layouts.mjs +16 -16
  7. package/dist/types/index.d.ts +5 -1
  8. package/dist/types/src/components/UiActionBar/UiActionBar.vue.d.ts +3 -1
  9. package/dist/types/src/components/UiActionBar/UiActionBarItem.vue.d.ts +18 -3
  10. package/dist/types/src/components/UiActionBar/types.d.ts +3 -1
  11. package/dist/types/src/components/UiButton/UiButton.vue.d.ts +17 -13
  12. package/dist/types/src/components/UiButtonGroup/UiButtonGroupItem.vue.d.ts +3 -0
  13. package/dist/types/src/components/UiButtonGroup/types.d.ts +1 -1
  14. package/dist/types/src/components/UiDropdown/UiDropdownItem.vue.d.ts +6 -4
  15. package/dist/types/src/components/UiInputEmoji/UiInputEmoji.vue.d.ts +45 -0
  16. package/dist/types/src/components/UiInputEmoji/index.d.ts +1 -0
  17. package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +67 -0
  18. package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +81 -0
  19. package/dist/types/src/components/UiInputRadio/index.d.ts +3 -0
  20. package/dist/types/src/components/UiInputRadio/types.d.ts +19 -0
  21. package/dist/types/src/components/UiLoader/UiLoader.vue.d.ts +6 -5
  22. package/dist/types/src/components/UiLogo/UiLogo.vue.d.ts +9 -3
  23. package/dist/types/src/components/UiPage/UiPage.vue.d.ts +34 -0
  24. package/dist/types/src/components/UiProgress/UiProgress.vue.d.ts +1 -1
  25. package/dist/types/src/components/UiSwitch/UiSwitch.vue.d.ts +109 -0
  26. package/dist/types/src/components/UiSwitch/index.d.ts +2 -0
  27. package/dist/types/src/components/UiSwitch/types.d.ts +3 -0
  28. package/dist/types/src/components/UiTabs/UiTabs.vue.d.ts +5 -2
  29. package/dist/types/src/composables/useContentMode.d.ts +25 -0
  30. package/dist/types/src/composables/useIconSize.d.ts +11 -0
  31. package/dist/types/src/constants/sizes.d.ts +1 -1
  32. package/dist/ui-vue.css +1 -1
  33. package/dist/{useThemeAndModes-CUK56j0z.mjs → useThemeAndModes-BzYwSOEv.mjs} +1 -1
  34. package/dist/utils.mjs +154 -99
  35. package/index.d.ts +4 -0
  36. package/index.js +8 -0
  37. package/package.json +11 -3
  38. package/src/components/Toast/ToastMessage.vue +6 -6
  39. package/src/components/UiActionBar/UiActionBar.vue +11 -2
  40. package/src/components/UiActionBar/UiActionBarItem.vue +56 -32
  41. package/src/components/UiActionBar/types.ts +3 -1
  42. package/src/components/UiAvatar/UiAvatar.vue +1 -1
  43. package/src/components/UiBanner/UiBanner.vue +7 -7
  44. package/src/components/UiButton/UiButton.vue +9 -9
  45. package/src/components/UiButtonGroup/UiButtonGroup.vue +4 -0
  46. package/src/components/UiButtonGroup/UiButtonGroupItem.vue +14 -0
  47. package/src/components/UiButtonGroup/types.ts +1 -1
  48. package/src/components/UiCarousel/UiCarousel.vue +2 -2
  49. package/src/components/UiColorPicker/UiColorPicker.vue +5 -5
  50. package/src/components/UiDialog/MarketplaceHeaderExample.vue +2 -2
  51. package/src/components/UiDialog/UiDialogClose.vue +1 -1
  52. package/src/components/UiDropdown/UiDropdownContent.vue +2 -2
  53. package/src/components/UiDropdown/UiDropdownItem.vue +8 -1
  54. package/src/components/UiDropdown/UiDropdownTrigger.vue +6 -7
  55. package/src/components/UiFieldWrapper/UiFieldWrapper.vue +1 -1
  56. package/src/components/UiInputCheckbox/UiInputCheckbox.vue +2 -2
  57. package/src/components/UiInputEmoji/UiInputEmoji.vue +173 -0
  58. package/src/components/UiInputEmoji/index.ts +1 -0
  59. package/src/components/UiInputRadio/UiInputRadio.vue +374 -0
  60. package/src/components/UiInputRadio/UiInputRadioGroup.vue +156 -0
  61. package/src/components/UiInputRadio/index.ts +3 -0
  62. package/src/components/UiInputRadio/types.ts +25 -0
  63. package/src/components/UiLoader/UiLoader.vue +8 -10
  64. package/src/components/UiLogo/UiLogo.vue +41 -7
  65. package/src/components/UiMenu/UiMenuContent.vue +1 -1
  66. package/src/components/UiMenu/UiMenuGroup.vue +1 -1
  67. package/src/components/UiMenu/UiMenuItem.vue +1 -1
  68. package/src/components/UiMenu/UiMenuSubContent.vue +1 -1
  69. package/src/components/UiMenu/UiMenuSubTrigger.vue +2 -2
  70. package/src/components/UiPage/UiPage.vue +102 -29
  71. package/src/components/UiPageHeader/UiPageHeader.vue +29 -9
  72. package/src/components/UiRoomButton/UiRoomButton.vue +4 -4
  73. package/src/components/UiShell/UiShell.vue +1 -1
  74. package/src/components/UiSideMenu/UiSideMenu.vue +13 -10
  75. package/src/components/UiSideMenu/UiSideMenuItemContent.vue +2 -2
  76. package/src/components/UiStepper/UiStepperIndicator.vue +8 -11
  77. package/src/components/UiSwitch/UiSwitch.vue +359 -0
  78. package/src/components/UiSwitch/index.ts +2 -0
  79. package/src/components/UiSwitch/types.ts +5 -0
  80. package/src/components/UiTabs/UiTabs.vue +7 -4
  81. package/src/components/UiTextField/UiTextField.vue +6 -11
  82. package/src/components/UiTimeline/README.md +6 -6
  83. package/src/components/UiTimeline/UiTimelineItem.vue +3 -3
  84. package/src/composables/useContentMode.ts +53 -0
  85. package/src/composables/useIconSize.ts +28 -0
  86. package/src/constants/sizes.ts +1 -1
  87. package/src/global.d.ts +8 -0
  88. package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-BtG5oABt.mjs +0 -5454
  89. package/dist/a11y--y7uaY4j.mjs +0 -217
@@ -16,7 +16,7 @@ const forwarded = useForwardPropsEmits(props, emits)
16
16
  <DropdownMenuPortal>
17
17
  <DropdownMenuContent
18
18
  v-bind="{ ...forwarded, ...$attrs }"
19
- class="bg-surface-neutral-bg-main rounded-xl border border-borders-neutral-light shadow-lg p-1 z-50"
19
+ class="bg-surface-neutral-bg-main rounded-xl border border-borders-neutral-light shadow-lg p-1.5 flex flex-col gap-0.5 z-50"
20
20
  data-colors-semantic="light"
21
21
  >
22
22
  <slot />
@@ -10,7 +10,7 @@ defineProps<{
10
10
  <DropdownMenuGroup v-bind="$attrs">
11
11
  <DropdownMenuLabel
12
12
  v-if="label"
13
- class="text-content-legends-bold-md text-text-neutral-tertiary px-3 pt-3 pb-1"
13
+ class="text-content-legends-bold-md text-text-neutral-secondary px-3 pt-3 pb-1"
14
14
  >
15
15
  {{ label }}
16
16
  </DropdownMenuLabel>
@@ -11,7 +11,7 @@ const forwarded = useForwardPropsEmits(props, emits)
11
11
  <template>
12
12
  <DropdownMenuItem
13
13
  v-bind="{ ...forwarded, ...$attrs }"
14
- class="w-full flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-actions-primary-hover-overlay data-[highlighted]:bg-actions-primary-hover-overlay transition-colors text-left outline-none cursor-pointer text-content-text-md-regular text-text-neutral-base"
14
+ class="w-full flex items-center gap-2 min-h-10 px-3 py-1.5 rounded-lg hover:bg-actions-neutral-hover-overlay data-[highlighted]:bg-actions-neutral-hover-overlay transition-colors text-left outline-none cursor-pointer text-content-text-md-regular text-text-neutral-base"
15
15
  >
16
16
  <slot />
17
17
  </DropdownMenuItem>
@@ -14,7 +14,7 @@ const forwarded = useForwardPropsEmits(props, emits)
14
14
  <DropdownMenuPortal>
15
15
  <DropdownMenuSubContent
16
16
  v-bind="{ ...forwarded, ...$attrs }"
17
- class="bg-surface-neutral-bg-main rounded-xl border border-borders-neutral-light shadow-lg p-1 z-50"
17
+ class="bg-surface-neutral-bg-main rounded-xl border border-borders-neutral-light shadow-lg p-1.5 flex flex-col gap-0.5 z-50"
18
18
  data-colors-semantic="light"
19
19
  >
20
20
  <slot />
@@ -9,9 +9,9 @@ const forwarded = useForwardProps(props)
9
9
  <template>
10
10
  <DropdownMenuSubTrigger
11
11
  v-bind="{ ...forwarded, ...$attrs }"
12
- class="w-full flex items-center justify-between gap-3 px-3 py-2 rounded-lg hover:bg-actions-primary-hover-overlay data-[highlighted]:bg-actions-primary-hover-overlay data-[state=open]:bg-actions-primary-hover-overlay transition-colors text-left outline-none cursor-pointer"
12
+ class="w-full flex items-center justify-between gap-3 min-h-10 px-3 py-1.5 rounded-lg hover:bg-actions-neutral-hover-overlay data-[highlighted]:bg-actions-neutral-hover-overlay data-[state=open]:bg-actions-neutral-hover-overlay transition-colors text-left outline-none cursor-pointer"
13
13
  >
14
14
  <slot />
15
- <span class="i-ls-chevron-right w-4 h-4 text-text-neutral-tertiary shrink-0" />
15
+ <span class="i-ph-caret-right w-4 h-4 text-text-neutral-tertiary shrink-0" />
16
16
  </DropdownMenuSubTrigger>
17
17
  </template>
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { computed, onBeforeUnmount, onMounted, ref, watch, getCurrentInstance, type Slot } from 'vue'
2
+ import { computed, onBeforeUnmount, ref, watch, getCurrentInstance, type Slot } from 'vue'
3
3
  import { BREAKPOINT_LG } from '../../utils/breakpoints'
4
4
  import { useResponsive } from '../../utils/responsive'
5
- import useThemeAndModes from '../../composables/useThemeAndModes'
5
+ import { useContentMode } from '../../composables/useContentMode'
6
6
  import { useMobileMenu } from '../../composables/useMobileMenu'
7
7
  import UiLoader from '../UiLoader/UiLoader.vue'
8
8
 
@@ -36,6 +36,24 @@ import UiLoader from '../UiLoader/UiLoader.vue'
36
36
  // │ │ └─────────────────────────────────────────────────┘ │
37
37
  // └─┴─────────────────────────────────────────────────────┘
38
38
 
39
+ /**
40
+ * Headroom sizes, in px: the gap above the content card that lets the shell
41
+ * background show through. One map so the page frame has a single source of
42
+ * truth — retune here rather than at call sites.
43
+ */
44
+ const HEADROOM_GAP_PX = {
45
+ none: 0,
46
+ sm: 4,
47
+ md: 10,
48
+ lg: 16,
49
+ xl: 24,
50
+ } as const
51
+
52
+ type HeadroomGap = keyof typeof HEADROOM_GAP_PX
53
+
54
+ /** Used when the card sits flush at the top and no size is given. */
55
+ const DEFAULT_HEADROOM_GAP: HeadroomGap = 'md'
56
+
39
57
  const props = withDefaults(defineProps<{
40
58
  /**
41
59
  * Extra classes for the inner content `<section>`. Use to override the
@@ -50,9 +68,30 @@ const props = withDefaults(defineProps<{
50
68
  * menu row — e.g. "Home" — without reaching into useMobileMenu directly.
51
69
  */
52
70
  title?: string
71
+ /**
72
+ * The shape of the page inside the content card.
73
+ * - `document` (default): the card scrolls as a single column. The common
74
+ * dashboard page — lists, forms, detail views.
75
+ * - `app`: the card fills the available height and never scrolls itself;
76
+ * the page owns its own scroll regions. For editor-style pages (split
77
+ * panes, canvases) that would otherwise opt out of UiPage entirely.
78
+ */
79
+ variant?: 'document' | 'app'
80
+ /**
81
+ * Headroom above the content card, letting the shell background show
82
+ * through. Sizes map to px via `HEADROOM_GAP_PX`.
83
+ *
84
+ * Left unset, the card gets `DEFAULT_HEADROOM_GAP` only when it sits flush
85
+ * at the top — i.e. there is no page header or left slot already offsetting
86
+ * it. Naming a size applies it in either case, and `none` removes the gap
87
+ * on a page that would otherwise get the default.
88
+ */
89
+ headroomGap?: HeadroomGap
53
90
  }>(), {
54
91
  pageContentClasses: '',
55
92
  title: undefined,
93
+ variant: 'document',
94
+ headroomGap: undefined,
56
95
  })
57
96
 
58
97
  const slots = defineSlots<{
@@ -65,6 +104,12 @@ const slots = defineSlots<{
65
104
  default?(): Slot
66
105
  }>()
67
106
 
107
+ // An `app` page manages its own scrolling, so every level from the page
108
+ // content down to the card must stop scrolling and stop growing past the
109
+ // viewport — otherwise a split-pane editor gets a second, outer scrollbar.
110
+ const isApp = computed(() => props.variant === 'app')
111
+ const scrollClass = computed(() => (isApp.value ? 'overflow-hidden' : 'overflow-y-auto'))
112
+
68
113
  // Check for slot presence
69
114
  const bannerSlot = computed(() => !!slots.banner)
70
115
  const pageHeaderSlot = computed(() => !!slots.pageHeader)
@@ -73,6 +118,38 @@ const leftSlot = computed(() => !!slots.leftSlot)
73
118
  const headerSlot = computed(() => !!slots.header)
74
119
  const footerSlot = computed(() => !!slots.footer)
75
120
 
121
+ // Row track for the card grid. An `app` page needs its main row to take the
122
+ // leftover height and be allowed to shrink (minmax(0,1fr)), so nested
123
+ // overflow containers resolve against a real height instead of growing.
124
+ // Spelled out as literals: Tailwind only generates classes it can see as
125
+ // complete strings in the source.
126
+ const cardRowsClass = computed(() => {
127
+ if (!isApp.value) { return headerSlot.value ? 'grid-rows-[min-content]' : '' }
128
+ if (headerSlot.value && footerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)_min-content]' }
129
+ if (headerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)]' }
130
+ if (footerSlot.value) { return 'grid-rows-[minmax(0,1fr)_min-content]' }
131
+ return 'grid-rows-[minmax(0,1fr)]'
132
+ })
133
+
134
+ // A named size always wins; otherwise fall back to the default, and only when
135
+ // the card is flush at the top (a page header or left slot already provides
136
+ // the offset). Applied as an inline style rather than a utility so the value
137
+ // does not depend on Tailwind's `--spacing` fallback resolving to 0.25rem.
138
+ // A zero gap emits no style at all, so `none` and "already offset" produce
139
+ // identical markup.
140
+ //
141
+ // Padding on the clipping wrapper rather than a margin on the card: the card
142
+ // is `h-full`, so a margin would push a full-height box down inside a fixed
143
+ // height, overflowing by exactly the gap and clipping that much off the
144
+ // bottom. As padding it shrinks the box the card measures itself against.
145
+ const headroomGapStyle = computed(() => {
146
+ const size = props.headroomGap ?? (
147
+ !pageHeaderSlot.value && !leftSlot.value ? DEFAULT_HEADROOM_GAP : 'none'
148
+ )
149
+ const px = HEADROOM_GAP_PX[size]
150
+ return px ? { paddingTop: `${px}px` } : undefined
151
+ })
152
+
76
153
  // Feed the page title to UiShell's fallback mobile menu topbar. Watched so a
77
154
  // reactive title updates live, and cleared on unmount so it doesn't leak to
78
155
  // the next route.
@@ -92,23 +169,9 @@ const displayLeftSlot = computed(() => responsive.value.width >= BREAKPOINT_LG)
92
169
  const UiPage = ref<HTMLElement | undefined>()
93
170
  const PageContent = ref<HTMLElement | undefined>()
94
171
 
95
- const { addElementToWatch } = useThemeAndModes()
96
-
97
- // The page content historically forces light mode. Only lift that pin when
98
- // the internal theming tooling is enabled, so the stored content mode
99
- // (driven by the ThemePicker) can apply; for everyone else the content
100
- // stays light regardless of what localStorage may hold.
101
- const isInternalThemingEnabled = localStorage.getItem('ls-internal-theming') === 'true'
102
-
103
- onMounted(() => {
104
- addElementToWatch(
105
- 'content',
106
- {
107
- selector: '[data-name="ui-page-content"]',
108
- element: PageContent.value!,
109
- ...(isInternalThemingEnabled ? {} : { modeOverride: 'light' }),
110
- })
111
- })
172
+ // Follow the stored content mode via the shared composable (also used by the
173
+ // settings shell), so the page content's data-colors-semantic stays in sync.
174
+ const { contentMode } = useContentMode(PageContent, '[data-name="ui-page-content"]')
112
175
 
113
176
  </script>
114
177
 
@@ -132,7 +195,7 @@ onMounted(() => {
132
195
  v-if="pageHeaderSlot"
133
196
  ref="pageHeader"
134
197
  data-name="pageHeader"
135
- class="page-header p-4 md:p-8 md:pl-0 relative"
198
+ class="page-header p-4 md:px-8 md:py-6 md:pl-0 relative"
136
199
  >
137
200
  <slot name="pageHeader" />
138
201
  </section>
@@ -150,8 +213,11 @@ onMounted(() => {
150
213
  <section
151
214
  ref="PageContent"
152
215
  data-name="pageContent"
153
- class="page-content flex-1 overflow-y-auto w-full grid has-[>:nth-child(2)]:lg:grid-cols-[1fr_2fr] lg:gap-x-8 min-h-0"
154
- :data-colors-semantic="isInternalThemingEnabled ? undefined : 'light'"
216
+ :class="[
217
+ 'page-content flex-1 w-full grid has-[>:nth-child(2)]:lg:grid-cols-[1fr_2fr] lg:gap-x-8 min-h-0',
218
+ scrollClass,
219
+ ]"
220
+ :data-colors-semantic="contentMode"
155
221
  >
156
222
  <aside
157
223
  v-if="leftSlot && displayLeftSlot"
@@ -161,14 +227,15 @@ onMounted(() => {
161
227
  <slot name="leftSlot" />
162
228
  </aside>
163
229
 
164
- <div class="min-w-0 overflow-hidden rounded-tl-xl">
230
+ <div
231
+ class="min-w-0 overflow-hidden rounded-tl-xl"
232
+ :style="headroomGapStyle"
233
+ >
165
234
  <section
166
235
  :class="[
167
- 'content min-w-0 overflow-y-auto h-full bg-surface-neutral-bg-main grid shadow-shadows-drop-shadow-2xl rounded-tl-xl',
168
- // Nudge the content down only when it sits flush at the top — i.e.
169
- // no page header and no left slot to align against.
170
- !pageHeaderSlot && !leftSlot ? 'mt-1' : '',
171
- headerSlot ? 'grid-rows-[min-content]' : '',
236
+ 'content min-w-0 h-full bg-surface-neutral-bg-main grid shadow-shadows-drop-shadow-2xl rounded-tl-xl',
237
+ scrollClass,
238
+ cardRowsClass,
172
239
  props.pageContentClasses,
173
240
  ]"
174
241
  data-name="ui-shell-content"
@@ -183,7 +250,13 @@ onMounted(() => {
183
250
 
184
251
  <section
185
252
  data-name="main-content"
186
- class="route-content w-full min-w-0 flex flex-col"
253
+ :class="[
254
+ 'route-content w-full min-w-0 flex flex-col',
255
+ // Let the row shrink so an editor's own overflow containers
256
+ // resolve against a real height. Scoped to `app` so the 15
257
+ // existing document pages keep their current growth behaviour.
258
+ isApp ? 'min-h-0' : '',
259
+ ]"
187
260
  >
188
261
  <slot>
189
262
  <UiLoader />
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onBeforeUnmount, useSlots, watch, type Slot } from 'vue'
3
- import LivestormLogo from '@livestorm/ui-tokens/output/vectors/logos/livestorm-white.svg'
4
3
  import UiButton from '../UiButton/UiButton.vue'
4
+ import UiLogo from '../UiLogo/UiLogo.vue'
5
5
  import { useMobileMenu } from '../../composables/useMobileMenu'
6
6
  import type { RouteLocationRaw } from 'vue-router'
7
7
 
@@ -65,8 +65,15 @@ onBeforeUnmount(() => syncTrigger(false))
65
65
  </script>
66
66
 
67
67
  <template>
68
+ <!--
69
+ min-h-10 keeps the header a stable height whether or not a page fills the
70
+ action slots. Without it the row is sized by its tallest child — a bare
71
+ title is 32px but an action button is 40px — so the header, and the content
72
+ card below it, shifted by 8px between pages like Events and People.
73
+ A floor, not a fixed height: taller or wrapped content still grows.
74
+ -->
68
75
  <div
69
- class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center"
76
+ class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center min-h-10"
70
77
  >
71
78
  <div class="flex min-w-0 items-center gap-2">
72
79
  <div v-if="showLogo">
@@ -74,11 +81,13 @@ onBeforeUnmount(() => syncTrigger(false))
74
81
  :to="logoTo ? logoTo : '#'"
75
82
  class="grid items-center w-8 h-8"
76
83
  >
77
- <img
78
- :src="LivestormLogo"
79
- alt="Livestorm logo"
80
- class="w-6 h-6"
81
- >
84
+ <UiLogo
85
+ :height="1.5"
86
+ :width="1.5"
87
+ :themed="false"
88
+ :animated="false"
89
+ inverted="auto"
90
+ />
82
91
  </router-link>
83
92
  </div>
84
93
  <div
@@ -88,7 +97,7 @@ onBeforeUnmount(() => syncTrigger(false))
88
97
  <UiButton
89
98
  color="neutral"
90
99
  variant="tertiary"
91
- icon="i-ls-menu"
100
+ icon="i-ph-list"
92
101
  @click="toggle"
93
102
  />
94
103
  </div>
@@ -96,7 +105,7 @@ onBeforeUnmount(() => syncTrigger(false))
96
105
  <UiButton
97
106
  color="neutral"
98
107
  variant="tertiary"
99
- icon="i-ls-back"
108
+ icon="i-ph-arrow-left"
100
109
  />
101
110
  </div>
102
111
  <div
@@ -124,5 +133,16 @@ onBeforeUnmount(() => syncTrigger(false))
124
133
  >
125
134
  <slot name="slotB" />
126
135
  </div>
136
+
137
+ <!--
138
+ Teleport target for page-level actions, so a component deep inside the
139
+ page content (e.g. a settings section) can surface its primary action
140
+ here without threading props up. `contents` keeps the empty target out
141
+ of the flex layout, so pages that don't use it are unaffected.
142
+ -->
143
+ <div
144
+ id="ui-page-header-actions-teleport-target"
145
+ class="contents"
146
+ />
127
147
  </div>
128
148
  </template>
@@ -57,7 +57,7 @@ const isIconOnly = computed(() => !slots.default);
57
57
  >
58
58
  <svg
59
59
  v-if="loading"
60
- class="h-4 w-4 shrink-0 animate-spin"
60
+ class="size-5 shrink-0 animate-spin"
61
61
  xmlns="http://www.w3.org/2000/svg"
62
62
  fill="none"
63
63
  viewBox="0 0 24 24"
@@ -78,7 +78,7 @@ const isIconOnly = computed(() => !slots.default);
78
78
  </svg>
79
79
  <div
80
80
  v-else-if="icon"
81
- class="h-4 w-4 shrink-0"
81
+ class="size-5 shrink-0"
82
82
  :class="icon"
83
83
  />
84
84
  <slot />
@@ -117,7 +117,7 @@ const isIconOnly = computed(() => !slots.default);
117
117
  >
118
118
  <svg
119
119
  v-if="loading"
120
- class="h-4 w-4 shrink-0 animate-spin"
120
+ class="size-5 shrink-0 animate-spin"
121
121
  xmlns="http://www.w3.org/2000/svg"
122
122
  fill="none"
123
123
  viewBox="0 0 24 24"
@@ -138,7 +138,7 @@ const isIconOnly = computed(() => !slots.default);
138
138
  </svg>
139
139
  <div
140
140
  v-else-if="icon"
141
- class="h-4 w-4 shrink-0"
141
+ class="size-5 shrink-0"
142
142
  :class="icon"
143
143
  />
144
144
  <slot />
@@ -69,7 +69,7 @@ const fallbackLabel = computed(() => menuLabel.value ?? props.openMenuLabel)
69
69
  <UiButton
70
70
  color="neutral"
71
71
  variant="tertiary"
72
- icon="i-ls-menu"
72
+ icon="i-ph-list"
73
73
  :aria-label="fallbackLabel"
74
74
  @click="toggle"
75
75
  />
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import LivestormLogo from '@livestorm/ui-tokens/output/vectors/logos/livestorm-white.svg'
3
2
  import type { Slot } from 'vue'
4
3
  import type { SideMenuCollapsedItem, SideMenuItemProps, SideMenuMoreItem } from './types'
5
4
  import UiSideMenuItem from './UiSideMenuItem.vue'
5
+ import UiLogo from '../UiLogo/UiLogo.vue'
6
6
  import UiButton from '../UiButton/UiButton.vue'
7
7
  import UiPopover from '../UiPopover/UiPopover.vue'
8
8
  import UiPopoverTrigger from '../UiPopover/UiPopoverTrigger.vue'
@@ -189,17 +189,20 @@ function handleLogoClick (event: MouseEvent) {
189
189
  <!-- Top -->
190
190
  <div class="flex flex-col md:items-center gap-8">
191
191
  <div class="flex items-center justify-between">
192
- <img
192
+ <UiLogo
193
193
  v-if="showLivestormLogo"
194
- :src="LivestormLogo"
195
- :alt="logoAlt"
196
- class="w-10 h-10 ml-4 md:ml-0"
197
- role="img"
194
+ :height="2.5"
195
+ :width="2.5"
196
+ :themed="false"
197
+ :animated="false"
198
+ inverted="auto"
199
+ :label="logoAlt"
200
+ class="ml-4 md:ml-0"
198
201
  @click="handleLogoClick"
199
- >
202
+ />
200
203
  <div class="md:hidden">
201
204
  <UiButton
202
- icon="i-ls-x-stream"
205
+ icon="i-ph-x"
203
206
  variant="tertiary"
204
207
  color="neutral"
205
208
  size="lg"
@@ -289,7 +292,7 @@ function handleLogoClick (event: MouseEvent) {
289
292
  <UiPopoverTrigger class="w-full cursor-pointer">
290
293
  <UiSideMenuItem
291
294
  :label="translate('room_bottom-bar_controls-label_more')"
292
- icon="i-ls-more-horizontal"
295
+ icon="i-ph-dots-three"
293
296
  :is-active="false"
294
297
  :inside-interactive="true"
295
298
  :badge="moreBadge"
@@ -420,7 +423,7 @@ function handleLogoClick (event: MouseEvent) {
420
423
  <UiPopoverTrigger class="cursor-pointer">
421
424
  <UiSideMenuItem
422
425
  :label="translate('room_bottom-bar_controls-label_more')"
423
- icon="i-ls-more-vertical"
426
+ icon="i-ph-dots-three-vertical"
424
427
  :is-active="false"
425
428
  :inside-interactive="true"
426
429
  />
@@ -103,7 +103,7 @@ const textAlignClass = computed(() => {
103
103
  isActive ? 'bg-actions-neutral-active-overlay text-actions-neutral-idle' : 'text-actions-neutral-idle-alpha-strong',
104
104
  // Avatar box is 40px; on mobile `p-1` makes its total height (48px)
105
105
  // match icon items (`p-3` + 24px icon). Both are 40px on desktop.
106
- hasAvatarSlot ? 'p-1 md:p-0' : 'p-3',
106
+ hasAvatarSlot ? 'p-1 md:p-0' : 'p-2.5',
107
107
  ]"
108
108
  >
109
109
  <!-- Icon/avatar wrapper: anchors the notification badge to the icon's
@@ -119,7 +119,7 @@ const textAlignClass = computed(() => {
119
119
  <div
120
120
  v-if="icon"
121
121
  :class="[
122
- 'w-6 h-6 md:w-4 md:h-4',
122
+ 'w-6 h-6 md:w-5 md:h-5',
123
123
  icon
124
124
  ]"
125
125
  aria-hidden="true"
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { StepperIndicatorProps } from 'reka-ui'
3
3
  import { StepperIndicator } from 'reka-ui'
4
+ import { useIconSize } from '../../composables/useIconSize'
4
5
 
5
6
  type Size = 'sm' | 'md' | 'lg'
6
7
 
@@ -13,16 +14,12 @@ interface UiStepperIndicatorProps extends StepperIndicatorProps {
13
14
  const { size = 'md', showCheckmark = true, icon } = defineProps<UiStepperIndicatorProps>()
14
15
 
15
16
  const sizeClasses = {
16
- sm: 'size-6 text-content-text-md-regular',
17
- md: 'size-8 text-content-text-md-regular',
18
- lg: 'size-10 text-content-text-lg-regular',
17
+ sm: 'size-8 text-content-text-md-regular',
18
+ md: 'size-10 text-content-text-md-regular',
19
+ lg: 'size-12 text-content-text-lg-regular',
19
20
  }
20
21
 
21
- const iconSizeClasses = {
22
- sm: 'size-3',
23
- md: 'size-4',
24
- lg: 'size-5',
25
- }
22
+ const iconSizeClass = useIconSize(() => size)
26
23
  </script>
27
24
 
28
25
  <template>
@@ -34,13 +31,13 @@ const iconSizeClasses = {
34
31
  <span
35
32
  v-if="icon"
36
33
  class="stepper-custom-icon"
37
- :class="[iconSizeClasses[size], icon]"
34
+ :class="[iconSizeClass, icon]"
38
35
  />
39
36
 
40
37
  <span
41
38
  v-if="showCheckmark"
42
- class="stepper-checkmark i-ls-check"
43
- :class="iconSizeClasses[size]"
39
+ class="stepper-checkmark i-ph-check"
40
+ :class="iconSizeClass"
44
41
  />
45
42
 
46
43
  <span class="stepper-number">