@rimelight/ui 0.0.23 → 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 (65) hide show
  1. package/package.json +8 -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 -85
  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 +125 -50
  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/nuxt-types.ts +3 -0
  25. package/src/plugins/index.ts +5 -2
  26. package/src/plugins/starlightAddons/index.ts +3 -64
  27. package/src/plugins/starlightDocsApi/components/ApiEmits.astro +46 -0
  28. package/src/plugins/starlightDocsApi/components/ApiProps.astro +47 -0
  29. package/src/plugins/starlightDocsApi/components/ApiSlots.astro +50 -0
  30. package/src/plugins/starlightDocsApi/components/ApiTheme.astro +27 -0
  31. package/src/plugins/starlightDocsApi/extract-vue.ts +286 -0
  32. package/src/plugins/starlightDocsApi/index.ts +265 -0
  33. package/src/plugins/starlightDocsApi/types.ts +40 -0
  34. package/src/plugins/starlightDocsApi/utils.ts +45 -0
  35. package/src/themes/button.theme.ts +179 -23
  36. package/src/themes/footer.theme.ts +4 -6
  37. package/src/themes/header.theme.ts +4 -28
  38. package/src/themes/icon.theme.ts +4 -2
  39. package/src/themes/index.ts +15 -20
  40. package/src/themes/logo.theme.ts +4 -2
  41. package/src/themes/placeholder.theme.ts +4 -6
  42. package/src/themes/scroll-to-top.theme.ts +4 -6
  43. package/src/themes/section.theme.ts +185 -0
  44. package/src/types/componentVariant.ts +10 -0
  45. package/src/types/index.ts +1 -0
  46. package/src/utils/defineTheme.ts +11 -0
  47. package/src/utils/index.ts +3 -0
  48. package/src/utils/resolveClasses.ts +21 -0
  49. package/src/utils/useUi.ts +19 -0
  50. package/src/composables/useUi.ts +0 -27
  51. package/src/plugins/starlightAddons/Sidebar.astro +0 -89
  52. package/src/plugins/starlightAddons/data.ts +0 -39
  53. package/src/plugins/starlightAddons/libs/config.ts +0 -107
  54. package/src/plugins/starlightAddons/libs/content.ts +0 -20
  55. package/src/plugins/starlightAddons/libs/i18n.ts +0 -51
  56. package/src/plugins/starlightAddons/libs/locals.ts +0 -12
  57. package/src/plugins/starlightAddons/libs/pathname.ts +0 -22
  58. package/src/plugins/starlightAddons/libs/plugin.ts +0 -9
  59. package/src/plugins/starlightAddons/libs/sidebar.ts +0 -183
  60. package/src/plugins/starlightAddons/libs/vite.ts +0 -46
  61. package/src/plugins/starlightAddons/locals.d.ts +0 -14
  62. package/src/plugins/starlightAddons/middleware.ts +0 -132
  63. package/src/plugins/starlightAddons/overrides/Sidebar.astro +0 -8
  64. package/src/plugins/starlightAddons/schema.ts +0 -13
  65. package/src/plugins/starlightAddons/virtual.d.ts +0 -13
@@ -1,53 +1,71 @@
1
1
  <script setup lang="ts">
2
- import { scv, cx } from "css-variants"
3
- import { twMerge } from "tailwind-merge"
4
- import defu from "defu"
5
- import { buttonTheme } from "../../themes/button.theme.ts"
6
- import { getUIConfig } from "virtual:rimelight-ui"
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
7
 
8
- const button = scv({
9
- slots: [...buttonTheme.slots],
10
- base: buttonTheme.base,
11
- variants: buttonTheme.variants,
12
- compoundVariants: [...buttonTheme.compoundVariants],
13
- defaultVariants: buttonTheme.defaultVariants,
14
- classNameResolver: (...args) => twMerge(cx(...args))
15
- })
8
+ const button = scv(buttonTheme)
9
+ type ButtonVariants = ComponentVariants<typeof buttonTheme>
16
10
 
17
11
  export interface RLVButtonProps {
18
12
  /**
19
- * Button variant.
20
- *
21
- * @default "solid"
13
+ * The visual style variant of the button
22
14
  */
23
- variant?: keyof typeof buttonTheme.variants.variant
15
+ variant?: ButtonVariants["variant"]
24
16
  /**
25
- * Button color.
26
- *
27
- * @default "primary"
17
+ * The color scheme applied to the button
28
18
  */
29
- color?: keyof typeof buttonTheme.variants.color
19
+ color?: ButtonVariants["color"]
30
20
  /**
31
- * Button size.
32
- *
33
- * @default "md"
21
+ * The size scale of the button
34
22
  */
35
- size?: keyof typeof buttonTheme.variants.size
23
+ size?: ButtonVariants["size"]
36
24
  /**
37
- * When provided, renders as <a> instead of <button>.
25
+ * When provided, renders the button as an anchor link
38
26
  */
39
27
  href?: string
40
28
  /**
41
- * UI overrides for individual slots, derived from the button theme.
29
+ * The text label displayed inside the button
42
30
  */
43
- ui?: Partial<Record<keyof typeof buttonTheme.base, string>>
31
+ label?: string
44
32
  /**
45
- * External class applied to the root element.
33
+ * Icon name displayed before the label
46
34
  */
47
- class?: string
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"]
48
64
  /**
49
- * Standard HTML attributes for <button>
65
+ * Component-specific CSS class overrides
50
66
  */
67
+ ui?: Partial<Record<string, string>>
68
+ class?: string
51
69
  [key: string]: unknown
52
70
  }
53
71
 
@@ -57,30 +75,68 @@ const {
57
75
  size = "md",
58
76
  class: className,
59
77
  ui: uiProp,
60
- href
78
+ href,
79
+ label,
80
+ leadingIcon,
81
+ trailingIcon,
82
+ loading = false,
83
+ square = false,
84
+ block = false,
85
+ active = false,
86
+ activeColor,
87
+ activeVariant
61
88
  } = defineProps<RLVButtonProps>()
62
89
 
63
- defineSlots<{
64
- /**
65
- * The default slot for the button content (e.g., text, icons).
66
- */
67
- default(props: {}): any
68
- }>()
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
+ })
69
101
 
70
- const globalConfig = getUIConfig()
102
+ const showTrailingIcon = computed(() => {
103
+ if (loading) return "i-lucide-loader-circle"
104
+ return trailingIcon
105
+ })
71
106
 
72
- const mergedUI = defu(
73
- uiProp ?? {},
74
- (globalConfig.button as Record<string, unknown>) ?? {}
75
- ) as Record<keyof typeof buttonTheme.base, string | undefined>
107
+ const leadingIconClass = computed(() => {
108
+ return loading && isLeading.value ? "animate-spin" : ""
109
+ })
76
110
 
77
- const { root } = button({
78
- variant,
79
- color,
80
- size
111
+ const trailingIconClass = computed(() => {
112
+ return loading && isTrailing.value ? "animate-spin" : ""
81
113
  })
82
114
 
83
- // Determine whether to render <a> or <button>
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
+
84
140
  const Tag = href ? "a" : "button"
85
141
  </script>
86
142
 
@@ -89,9 +145,28 @@ const Tag = href ? "a" : "button"
89
145
  v-bind="$attrs"
90
146
  :is="Tag"
91
147
  :href="href"
92
- :class="twMerge(root, mergedUI.root, className)"
148
+ :class="resolvedClasses.root"
93
149
  data-slot="rlv-button"
94
150
  >
95
- <slot />
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>
96
171
  </component>
97
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
@@ -1,51 +1,19 @@
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 defu from "defu"
6
- import { logoTheme } from "../../themes/logo.theme.ts"
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import logoTheme from "../../themes/logo.theme"
7
6
  import { getUIConfig } from "virtual:rimelight-ui"
8
7
 
9
- const logo = scv({
10
- slots: [...logoTheme.slots],
11
- base: logoTheme.base,
12
- variants: logoTheme.variants,
13
- defaultVariants: logoTheme.defaultVariants,
14
- classNameResolver: (...args) => twMerge(cx(...args))
15
- })
8
+ const logo = scv(logoTheme)
16
9
 
17
10
  export interface RLVLogoProps {
18
- /**
19
- * The variant of the logo to display.
20
- *
21
- * @default "logomark"
22
- */
23
11
  variant?: string
24
- /**
25
- * Override the color mode.
26
- */
27
12
  mode?: "color" | "white" | "black"
28
- /**
29
- * The URL to link to.
30
- *
31
- * @default "/"
32
- */
33
13
  href?: string
34
- /**
35
- * UI overrides for individual slots, derived from the logo theme.
36
- */
37
- ui?: Partial<Record<keyof typeof logoTheme.base, string>>
38
- /**
39
- * External class — applied to the root element.
40
- */
14
+ ui?: Partial<Record<string, string>>
41
15
  class?: string
42
- /**
43
- * The alt text for the logo image.
44
- */
45
16
  alt?: string
46
- /**
47
- * Standard HTML attributes for <a>
48
- */
49
17
  [key: string]: unknown
50
18
  }
51
19
 
@@ -55,30 +23,15 @@ const props = withDefaults(defineProps<RLVLogoProps>(), {
55
23
  })
56
24
 
57
25
  defineSlots<{
58
- /**
59
- * The default slot for the logo content (e.g., text, custom elements).
60
- */
61
26
  default(props: {}): any
62
27
  }>()
63
28
 
64
- const globalConfig = getUIConfig()
65
-
66
- const mergedUI = computed(
67
- () =>
68
- defu(props.ui ?? {}, (globalConfig.logo as Record<string, unknown>) ?? {}) as Record<
69
- keyof typeof logoTheme.base,
70
- string | undefined
71
- >
29
+ const resolvedClasses = computed(() =>
30
+ resolveClasses(logo, {}, useUi("logo", props.ui), props.class)
72
31
  )
73
32
 
74
- const resolvedClasses = computed(() => {
75
- const { root } = logo()
76
- return {
77
- root: twMerge(root, mergedUI.value.root, props.class)
78
- }
79
- })
80
-
81
33
  const logoSrc = computed<string | null>(() => {
34
+ const globalConfig = getUIConfig()
82
35
  const rcLogos = globalConfig.logos as Record<string, any>
83
36
  let src: any = null
84
37
 
@@ -114,7 +67,6 @@ const Tag = computed(() => (props.href ? "a" : "div"))
114
67
  data-slot="rlv-logo"
115
68
  >
116
69
  <template v-if="logoSrc">
117
- <!-- Using mode='svg' pattern is not strictly needed for UnoCSS icons -->
118
70
  <span v-if="isIcon" :class="[logoSrc, 'size-full block shrink-0']" aria-hidden="true" />
119
71
  <img v-else :src="logoSrc" :alt="alt || ''" class="size-full block object-contain shrink-0" />
120
72
  </template>