@rimelight/ui 0.0.24 → 0.0.25

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 (64) hide show
  1. package/package.json +7 -3
  2. package/src/components/Head.astro +199 -156
  3. package/src/components/astro/RLAAccordion.astro +22 -0
  4. package/src/components/astro/RLAButton.astro +104 -118
  5. package/src/components/astro/RLAFooter.astro +14 -40
  6. package/src/components/astro/RLAHeader.astro +18 -78
  7. package/src/components/astro/RLAIcon.astro +11 -36
  8. package/src/components/astro/RLALogo.astro +9 -44
  9. package/src/components/astro/RLAPlaceholder.astro +8 -33
  10. package/src/components/astro/RLAQuote.astro +15 -0
  11. package/src/components/astro/RLAScrollToTop.astro +171 -221
  12. package/src/components/astro/RLASection.astro +181 -0
  13. package/src/components/vue/RLVButton.vue +172 -139
  14. package/src/components/vue/RLVFooter.vue +12 -49
  15. package/src/components/vue/RLVHeader.vue +15 -98
  16. package/src/components/vue/RLVIcon.vue +11 -37
  17. package/src/components/vue/RLVLogo.vue +8 -56
  18. package/src/components/vue/RLVPlaceholder.vue +11 -33
  19. package/src/components/vue/RLVScrollToTop.vue +17 -88
  20. package/src/components/vue/RLVSection.vue +98 -0
  21. package/src/composables/index.ts +0 -1
  22. package/src/env.d.ts +5 -0
  23. package/src/integrations/ui.ts +2 -6
  24. package/src/plugins/index.ts +5 -2
  25. package/src/plugins/starlightAddons/index.ts +3 -64
  26. package/src/plugins/starlightDocsApi/components/ApiEmits.astro +46 -0
  27. package/src/plugins/starlightDocsApi/components/ApiProps.astro +47 -0
  28. package/src/plugins/starlightDocsApi/components/ApiSlots.astro +50 -0
  29. package/src/plugins/starlightDocsApi/components/ApiTheme.astro +27 -0
  30. package/src/plugins/starlightDocsApi/extract-vue.ts +286 -0
  31. package/src/plugins/starlightDocsApi/index.ts +265 -0
  32. package/src/plugins/starlightDocsApi/types.ts +40 -0
  33. package/src/plugins/starlightDocsApi/utils.ts +45 -0
  34. package/src/themes/button.theme.ts +29 -14
  35. package/src/themes/footer.theme.ts +4 -6
  36. package/src/themes/header.theme.ts +4 -28
  37. package/src/themes/icon.theme.ts +4 -2
  38. package/src/themes/index.ts +15 -20
  39. package/src/themes/logo.theme.ts +4 -2
  40. package/src/themes/placeholder.theme.ts +4 -6
  41. package/src/themes/scroll-to-top.theme.ts +4 -6
  42. package/src/themes/section.theme.ts +185 -0
  43. package/src/types/componentVariant.ts +10 -0
  44. package/src/types/index.ts +1 -0
  45. package/src/utils/defineTheme.ts +11 -0
  46. package/src/utils/index.ts +3 -0
  47. package/src/utils/resolveClasses.ts +21 -0
  48. package/src/utils/useUi.ts +19 -0
  49. package/src/composables/useUi.ts +0 -27
  50. package/src/plugins/starlightAddons/Sidebar.astro +0 -89
  51. package/src/plugins/starlightAddons/data.ts +0 -39
  52. package/src/plugins/starlightAddons/libs/config.ts +0 -107
  53. package/src/plugins/starlightAddons/libs/content.ts +0 -20
  54. package/src/plugins/starlightAddons/libs/i18n.ts +0 -51
  55. package/src/plugins/starlightAddons/libs/locals.ts +0 -12
  56. package/src/plugins/starlightAddons/libs/pathname.ts +0 -22
  57. package/src/plugins/starlightAddons/libs/plugin.ts +0 -9
  58. package/src/plugins/starlightAddons/libs/sidebar.ts +0 -183
  59. package/src/plugins/starlightAddons/libs/vite.ts +0 -46
  60. package/src/plugins/starlightAddons/locals.d.ts +0 -14
  61. package/src/plugins/starlightAddons/middleware.ts +0 -132
  62. package/src/plugins/starlightAddons/overrides/Sidebar.astro +0 -8
  63. package/src/plugins/starlightAddons/schema.ts +0 -13
  64. package/src/plugins/starlightAddons/virtual.d.ts +0 -13
@@ -1,139 +1,172 @@
1
- <script setup lang="ts">
2
- import { computed, useSlots } from "vue"
3
- import { scv, cx } from "css-variants"
4
- import { twMerge } from "tailwind-merge"
5
- import defu from "defu"
6
- import { buttonTheme } from "../../themes/button.theme.ts"
7
- import { getUIConfig } from "virtual:rimelight-ui"
8
-
9
- const button = scv({
10
- slots: [...buttonTheme.slots],
11
- base: buttonTheme.base,
12
- variants: buttonTheme.variants,
13
- compoundVariants: [...buttonTheme.compoundVariants],
14
- defaultVariants: buttonTheme.defaultVariants,
15
- classNameResolver: (...args) => twMerge(cx(...args))
16
- })
17
-
18
- export interface RLVButtonProps {
19
- variant?: keyof typeof buttonTheme.variants.variant
20
- color?: keyof typeof buttonTheme.variants.color
21
- size?: keyof typeof buttonTheme.variants.size
22
- href?: string
23
- label?: string
24
- leadingIcon?: string
25
- trailingIcon?: string
26
- loading?: boolean
27
- square?: boolean
28
- block?: boolean
29
- active?: boolean
30
- activeColor?: keyof typeof buttonTheme.variants.color
31
- activeVariant?: keyof typeof buttonTheme.variants.variant
32
- ui?: Partial<Record<string, string>>
33
- class?: string
34
- [key: string]: unknown
35
- }
36
-
37
- const {
38
- variant = "solid",
39
- color = "primary",
40
- size = "md",
41
- class: className,
42
- ui: uiProp,
43
- href,
44
- label,
45
- leadingIcon,
46
- trailingIcon,
47
- loading = false,
48
- square = false,
49
- block = false,
50
- active = false,
51
- activeColor,
52
- activeVariant
53
- } = defineProps<RLVButtonProps>()
54
-
55
- const slots = useSlots()
56
-
57
- const isLeading = computed(() => !!(leadingIcon || slots.leading) || loading)
58
- const isTrailing = computed(() => !!(trailingIcon || slots.trailing) || (loading && !isLeading.value))
59
-
60
- const showLeadingIcon = computed(() => {
61
- if (loading) return "i-lucide-loader-circle"
62
- return leadingIcon
63
- })
64
-
65
- const showTrailingIcon = computed(() => {
66
- if (loading) return "i-lucide-loader-circle"
67
- return trailingIcon
68
- })
69
-
70
- const leadingIconClass = computed(() => {
71
- return loading && isLeading.value ? "animate-spin" : ""
72
- })
73
-
74
- const trailingIconClass = computed(() => {
75
- return loading && isTrailing.value ? "animate-spin" : ""
76
- })
77
-
78
- defineSlots<{
79
- default(props: {}): any
80
- leading(props: { ui: Record<string, string> }): any
81
- trailing(props: { ui: Record<string, string> }): any
82
- }>()
83
-
84
- const globalConfig = getUIConfig()
85
-
86
- const mergedUI = defu(
87
- uiProp ?? {},
88
- (globalConfig.button as Record<string, unknown>) ?? {}
89
- ) as Record<string, string | undefined>
90
-
91
- const resolved = computed(() => button({
92
- variant: active && activeVariant ? activeVariant : variant,
93
- color: active && activeColor ? activeColor : color,
94
- size,
95
- block,
96
- square: square || (!label && !slots.default),
97
- leading: isLeading.value,
98
- trailing: isTrailing.value,
99
- loading,
100
- active
101
- }))
102
-
103
- const Tag = href ? "a" : "button"
104
- </script>
105
-
106
- <template>
107
- <component
108
- v-bind="$attrs"
109
- :is="Tag"
110
- :href="href"
111
- :class="twMerge(resolved.root, mergedUI.root, className)"
112
- data-slot="rlv-button"
113
- >
114
- <slot name="leading" :ui="resolved">
115
- <span
116
- v-if="isLeading"
117
- :class="twMerge(resolved.leadingIcon, mergedUI.leadingIcon)"
118
- data-slot="leading-icon"
119
- >
120
- <span :class="[showLeadingIcon, leadingIconClass]" aria-hidden="true" />
121
- </span>
122
- </slot>
123
-
124
- <span v-if="label !== undefined && label !== null" :class="twMerge(resolved.label, mergedUI.label)" data-slot="label">
125
- {{ label }}
126
- </span>
127
- <slot v-else />
128
-
129
- <slot name="trailing" :ui="resolved">
130
- <span
131
- v-if="isTrailing"
132
- :class="twMerge(resolved.trailingIcon, mergedUI.trailingIcon)"
133
- data-slot="trailing-icon"
134
- >
135
- <span :class="[showTrailingIcon, trailingIconClass]" aria-hidden="true" />
136
- </span>
137
- </slot>
138
- </component>
139
- </template>
1
+ <script setup lang="ts">
2
+ import { computed, useSlots } from "vue"
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import type { ComponentVariants } from "../../types"
6
+ import buttonTheme from "../../themes/button.theme"
7
+
8
+ const button = scv(buttonTheme)
9
+ type ButtonVariants = ComponentVariants<typeof buttonTheme>
10
+
11
+ export interface RLVButtonProps {
12
+ /**
13
+ * The visual style variant of the button
14
+ */
15
+ variant?: ButtonVariants["variant"]
16
+ /**
17
+ * The color scheme applied to the button
18
+ */
19
+ color?: ButtonVariants["color"]
20
+ /**
21
+ * The size scale of the button
22
+ */
23
+ size?: ButtonVariants["size"]
24
+ /**
25
+ * When provided, renders the button as an anchor link
26
+ */
27
+ href?: string
28
+ /**
29
+ * The text label displayed inside the button
30
+ */
31
+ label?: string
32
+ /**
33
+ * Icon name displayed before the label
34
+ */
35
+ leadingIcon?: string
36
+ /**
37
+ * Icon name displayed after the label
38
+ */
39
+ trailingIcon?: string
40
+ /**
41
+ * Shows a loading spinner animation
42
+ */
43
+ loading?: boolean
44
+ /**
45
+ * Renders a square button with equal padding
46
+ */
47
+ square?: boolean
48
+ /**
49
+ * Makes the button stretch to full width
50
+ */
51
+ block?: boolean
52
+ /**
53
+ * Applies active state styling
54
+ */
55
+ active?: boolean
56
+ /**
57
+ * Color used when button is in active state
58
+ */
59
+ activeColor?: ButtonVariants["color"]
60
+ /**
61
+ * Variant used when button is in active state
62
+ */
63
+ activeVariant?: ButtonVariants["variant"]
64
+ /**
65
+ * Component-specific CSS class overrides
66
+ */
67
+ ui?: Partial<Record<string, string>>
68
+ class?: string
69
+ [key: string]: unknown
70
+ }
71
+
72
+ const {
73
+ variant = "solid",
74
+ color = "primary",
75
+ size = "md",
76
+ class: className,
77
+ ui: uiProp,
78
+ href,
79
+ label,
80
+ leadingIcon,
81
+ trailingIcon,
82
+ loading = false,
83
+ square = false,
84
+ block = false,
85
+ active = false,
86
+ activeColor,
87
+ activeVariant
88
+ } = defineProps<RLVButtonProps>()
89
+
90
+ const slots = useSlots()
91
+
92
+ const isLeading = computed(() => !!(leadingIcon || slots.leading) || loading)
93
+ const isTrailing = computed(
94
+ () => !!(trailingIcon || slots.trailing) || (loading && !isLeading.value)
95
+ )
96
+
97
+ const showLeadingIcon = computed(() => {
98
+ if (loading) return "i-lucide-loader-circle"
99
+ return leadingIcon
100
+ })
101
+
102
+ const showTrailingIcon = computed(() => {
103
+ if (loading) return "i-lucide-loader-circle"
104
+ return trailingIcon
105
+ })
106
+
107
+ const leadingIconClass = computed(() => {
108
+ return loading && isLeading.value ? "animate-spin" : ""
109
+ })
110
+
111
+ const trailingIconClass = computed(() => {
112
+ return loading && isTrailing.value ? "animate-spin" : ""
113
+ })
114
+
115
+ defineSlots<{
116
+ default(props: { ui: Record<string, string> }): any
117
+ leading(props: { ui: Record<string, string> }): any
118
+ trailing(props: { ui: Record<string, string> }): any
119
+ }>()
120
+
121
+ const resolvedClasses = computed(() =>
122
+ resolveClasses(
123
+ button,
124
+ {
125
+ variant: active && activeVariant ? activeVariant : variant,
126
+ color: active && activeColor ? activeColor : color,
127
+ size,
128
+ block,
129
+ square: square || (!label && !slots.default),
130
+ leading: isLeading.value,
131
+ trailing: isTrailing.value,
132
+ loading,
133
+ active
134
+ },
135
+ useUi("button", uiProp),
136
+ className
137
+ )
138
+ )
139
+
140
+ const Tag = href ? "a" : "button"
141
+ </script>
142
+
143
+ <template>
144
+ <component
145
+ v-bind="$attrs"
146
+ :is="Tag"
147
+ :href="href"
148
+ :class="resolvedClasses.root"
149
+ data-slot="rlv-button"
150
+ >
151
+ <slot name="leading" :ui="resolvedClasses">
152
+ <span v-if="isLeading" :class="resolvedClasses.leadingIcon" data-slot="leading-icon">
153
+ <span :class="[showLeadingIcon, leadingIconClass]" aria-hidden="true" />
154
+ </span>
155
+ </slot>
156
+
157
+ <span
158
+ v-if="label !== undefined && label !== null"
159
+ :class="resolvedClasses.label"
160
+ data-slot="label"
161
+ >
162
+ {{ label }}
163
+ </span>
164
+ <slot v-else />
165
+
166
+ <slot name="trailing" :ui="resolvedClasses">
167
+ <span v-if="isTrailing" :class="resolvedClasses.trailingIcon" data-slot="trailing-icon">
168
+ <span :class="[showTrailingIcon, trailingIconClass]" aria-hidden="true" />
169
+ </span>
170
+ </slot>
171
+ </component>
172
+ </template>
@@ -1,71 +1,34 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue"
3
- import { scv, cx } from "css-variants"
4
- import { twMerge } from "tailwind-merge"
5
- import { useUi } from "../../composables"
6
- import { footerTheme } from "../../themes/footer.theme.ts"
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import type { ComponentVariants } from "../../types"
6
+ import footerTheme from "../../themes/footer.theme"
7
7
 
8
- const footer = scv({
9
- slots: [...footerTheme.slots],
10
- base: footerTheme.base,
11
- variants: footerTheme.variants,
12
- defaultVariants: footerTheme.defaultVariants,
13
- classNameResolver: (...args) => twMerge(cx(...args))
14
- })
8
+ const footer = scv(footerTheme)
9
+ type FooterVariants = ComponentVariants<typeof footerTheme>
15
10
 
16
11
  export interface RLVFooterProps {
17
- /**
18
- * Whether to contain the footer content in a max-width container.
19
- *
20
- * @default false
21
- */
22
- contain?: boolean
23
- /**
24
- * UI overrides for individual slots, derived from the footer theme.
25
- */
26
- ui?: Partial<Record<keyof typeof footerTheme.base, string>>
27
- /**
28
- * External class — applied to the root element.
29
- */
12
+ contain?: FooterVariants["contain"]
13
+ ui?: Partial<Record<string, string>>
30
14
  class?: string
31
15
  }
32
16
 
33
17
  const { contain = false, class: className, ui: uiProp } = defineProps<RLVFooterProps>()
34
18
 
35
19
  defineSlots<{
36
- /**
37
- * Content to display on the left side of the footer.
38
- */
39
20
  left(props: {}): any
40
- /**
41
- * Content to display in the center of the footer.
42
- */
43
21
  center(props: {}): any
44
- /**
45
- * Content to display on the right side of the footer.
46
- */
47
22
  right(props: {}): any
48
23
  }>()
49
24
 
50
- const mergedUI = useUi("footer", uiProp as Record<string, unknown> | undefined)
51
-
52
- const resolvedClasses = computed(() => {
53
- const classes = footer({
54
- contain
55
- })
56
-
57
- return {
58
- root: twMerge(classes.root, mergedUI.value.root as string | undefined),
59
- container: twMerge(classes.container, mergedUI.value.container as string | undefined),
60
- left: twMerge(classes.left, mergedUI.value.left as string | undefined),
61
- center: twMerge(classes.center, mergedUI.value.center as string | undefined),
62
- right: twMerge(classes.right, mergedUI.value.right as string | undefined)
63
- }
64
- })
25
+ const resolvedClasses = computed(() =>
26
+ resolveClasses(footer, { contain }, useUi("footer", uiProp), className)
27
+ )
65
28
  </script>
66
29
 
67
30
  <template>
68
- <footer :class="[resolvedClasses.root, className]" data-slot="rlv-footer" v-bind="$attrs">
31
+ <footer :class="[resolvedClasses.root]" data-slot="rlv-footer" v-bind="$attrs">
69
32
  <div :class="resolvedClasses.container">
70
33
  <div :class="resolvedClasses.left" data-slot="footer-left">
71
34
  <slot name="left" />
@@ -1,64 +1,21 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onMounted, onUnmounted, ref } from "vue"
3
- import { scv, cx } from "css-variants"
4
- import { twMerge } from "tailwind-merge"
5
- import { useUi } from "../../composables"
6
- import { headerTheme } from "../../themes/header.theme.ts"
7
- import { getHeaderStack } from "../../utils/headerStack.ts"
8
-
9
- // ── Style engine ─────────────────────────────────────────────────────────────
10
-
11
- const header = scv({
12
- slots: [...headerTheme.slots],
13
- base: headerTheme.base,
14
- variants: headerTheme.variants,
15
- defaultVariants: headerTheme.defaultVariants,
16
- classNameResolver: (...args) => twMerge(cx(...args))
17
- })
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import type { ComponentVariants } from "../../types"
6
+ import headerTheme from "../../themes/header.theme"
7
+ import { getHeaderStack } from "../../utils/headerStack"
18
8
 
19
- // ── Props ─────────────────────────────────────────────────────────────────────
9
+ const header = scv(headerTheme)
10
+ type HeaderVariants = ComponentVariants<typeof headerTheme>
20
11
 
21
12
  export interface RLVHeaderProps {
22
- /**
23
- * Whether to contain the header content in a max-width container.
24
- *
25
- * @default true
26
- */
27
- contain?: boolean
28
- /**
29
- * Sticky positioning when `fixed` is false.
30
- *
31
- * @default true
32
- */
33
- sticky?: boolean
34
- /**
35
- * Switches to `position: fixed; left: 0; right: 0` (layer mode). When true, `top` and
36
- * `stackIndex` control the exact position.
37
- *
38
- * @default false
39
- */
40
- fixed?: boolean
41
- /**
42
- * Controls z-index when `fixed` is true: `z-index = 100 - stackIndex`. Lower index = higher in
43
- * the visual stack.
44
- *
45
- * @default 0
46
- */
13
+ contain?: HeaderVariants["contain"]
14
+ sticky?: HeaderVariants["sticky"]
15
+ fixed?: HeaderVariants["fixed"]
47
16
  stackIndex?: number
48
- /**
49
- * When true, hides the header on scroll-down and shows it on scroll-up. Uses a passive `scroll`
50
- * event listener — no external dependencies. Only meaningful when `fixed` is true.
51
- *
52
- * @default false
53
- */
54
17
  hideOnScroll?: boolean
55
- /**
56
- * UI overrides for individual slots, derived from the header theme.
57
- */
58
- ui?: Partial<Record<keyof typeof headerTheme.base, string>>
59
- /**
60
- * External class — applied to the root element.
61
- */
18
+ ui?: Partial<Record<string, string>>
62
19
  class?: string
63
20
  }
64
21
 
@@ -72,42 +29,15 @@ const {
72
29
  ui: uiProp
73
30
  } = defineProps<RLVHeaderProps>()
74
31
 
75
- // ── Slots ─────────────────────────────────────────────────────────────────────
76
-
77
32
  defineSlots<{
78
- /**
79
- * Content to display on the left side of the header (e.g., Logo).
80
- */
81
33
  left(props: {}): any
82
- /**
83
- * Content to display in the center of the header (e.g., Navigation Links).
84
- */
85
34
  center(props: {}): any
86
- /**
87
- * Content to display on the right side of the header (e.g., User Menu, Theme Toggle).
88
- */
89
35
  right(props: {}): any
90
36
  }>()
91
37
 
92
- // ── UI merge ──────────────────────────────────────────────────────────────────
93
-
94
- const mergedUI = useUi("header", uiProp as Record<string, unknown> | undefined)
95
-
96
- // ── Resolved classes ──────────────────────────────────────────────────────────
97
-
98
- const resolvedClasses = computed(() => {
99
- const classes = header({ contain, sticky, fixed })
100
- return {
101
- root: twMerge(classes.root, mergedUI.value.root as string | undefined),
102
- content: twMerge(classes.content, mergedUI.value.content as string | undefined),
103
- container: twMerge(classes.container, mergedUI.value.container as string | undefined),
104
- left: twMerge(classes.left, mergedUI.value.left as string | undefined),
105
- center: twMerge(classes.center, mergedUI.value.center as string | undefined),
106
- right: twMerge(classes.right, mergedUI.value.right as string | undefined)
107
- }
108
- })
109
-
110
- // ── Fixed-mode state ──────────────────────────────────────────────────────────
38
+ const resolvedClasses = computed(() =>
39
+ resolveClasses(header, { contain, sticky, fixed }, useUi("header", uiProp), className)
40
+ )
111
41
 
112
42
  const id = `rlv-header-${Math.random().toString(36).slice(2, 8)}`
113
43
  const rootRef = ref<HTMLElement | null>(null)
@@ -115,9 +45,6 @@ const contentRef = ref<HTMLElement | null>(null)
115
45
  const naturalHeight = ref(0)
116
46
  const isVisible = ref(true)
117
47
 
118
- /**
119
- * Inline style applied to the root element in fixed mode.
120
- */
121
48
  const fixedStyle = computed(() => {
122
49
  if (!fixed) return {}
123
50
  return {
@@ -125,8 +52,6 @@ const fixedStyle = computed(() => {
125
52
  }
126
53
  })
127
54
 
128
- // ── Lifecycle: height measurement + scroll listener ───────────────────────────
129
-
130
55
  let resizeObserver: ResizeObserver | null = null
131
56
  let scrollHandler: (() => void) | null = null
132
57
 
@@ -139,7 +64,6 @@ onMounted(() => {
139
64
  stack.register(id, stackIndex, naturalHeight.value, isVisible.value, rootRef.value)
140
65
  }
141
66
 
142
- // Measure natural height via ResizeObserver
143
67
  if (contentRef.value) {
144
68
  naturalHeight.value = contentRef.value.getBoundingClientRect().height
145
69
  resizeObserver = new ResizeObserver((entries) => {
@@ -152,14 +76,12 @@ onMounted(() => {
152
76
  resizeObserver.observe(contentRef.value)
153
77
  }
154
78
 
155
- // Raw scroll listener for hideOnScroll
156
79
  if (hideOnScroll) {
157
80
  let localLastScrollY = window.scrollY
158
81
  scrollHandler = () => {
159
82
  const current = window.scrollY
160
83
  const diff = current - localLastScrollY
161
84
 
162
- // Ignore tiny jitter
163
85
  if (Math.abs(diff) < 10) return
164
86
 
165
87
  const newVisible = current <= 50 ? true : diff <= 0
@@ -188,16 +110,11 @@ onUnmounted(() => {
188
110
  <header
189
111
  ref="rootRef"
190
112
  :id="id"
191
- :class="[resolvedClasses.root, className]"
113
+ :class="[resolvedClasses.root]"
192
114
  :style="fixedStyle"
193
115
  data-slot="rlv-header"
194
116
  v-bind="$attrs"
195
117
  >
196
- <!--
197
- content wrapper: always rendered, but only meaningful in fixed mode.
198
- The ResizeObserver targets this element so the `<header>` root's padding/border
199
- don't interfere with the height measurement.
200
- -->
201
118
  <div ref="contentRef" :class="resolvedClasses.content">
202
119
  <div :class="resolvedClasses.container">
203
120
  <div :class="resolvedClasses.left" data-slot="header-left">
@@ -1,51 +1,25 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue"
3
- import { scv, cx } from "css-variants"
4
- import { twMerge } from "tailwind-merge"
5
- import { useUi } from "../../composables"
6
- import { iconTheme } from "../../themes/icon.theme"
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import type { ComponentVariants } from "../../types"
6
+ import iconTheme from "../../themes/icon.theme"
7
7
 
8
- const icon = scv({
9
- slots: [...iconTheme.slots],
10
- base: iconTheme.base,
11
- variants: iconTheme.variants,
12
- defaultVariants: iconTheme.defaultVariants,
13
- classNameResolver: (...args) => twMerge(cx(...args))
14
- })
8
+ const icon = scv(iconTheme)
9
+ type IconVariants = ComponentVariants<typeof iconTheme>
15
10
 
16
11
  export interface RLVIconProps {
17
- /**
18
- * Icon name following the UnoCSS icons format. Can be a string like "i-lucide-home" or a Vue
19
- * component.
20
- *
21
- * @example
22
- * "i-lucide-home"
23
- */
24
12
  name: string
25
- /**
26
- * Size variant for the icon.
27
- */
28
- size?: keyof typeof iconTheme.variants.size
29
- /**
30
- * External class applied to the root element.
31
- */
13
+ size?: IconVariants["size"]
32
14
  class?: string
33
- /**
34
- * UI overrides for individual slots.
35
- */
36
- ui?: Partial<Record<keyof typeof iconTheme.base, string>>
15
+ ui?: Partial<Record<string, string>>
37
16
  }
38
17
 
39
18
  const { name, size = "md", class: className, ui: uiProp } = defineProps<RLVIconProps>()
40
19
 
41
- const mergedUI = useUi("icon", uiProp as Record<string, unknown> | undefined)
42
-
43
- const resolvedClasses = computed(() => {
44
- const { root } = icon({ size })
45
- return {
46
- root: twMerge(root, mergedUI.value.root, className)
47
- }
48
- })
20
+ const resolvedClasses = computed(() =>
21
+ resolveClasses(icon, { size }, useUi("icon", uiProp), className)
22
+ )
49
23
 
50
24
  defineSlots<{
51
25
  default(props: {}): any