@rimelight/ui 0.0.30 → 0.0.31

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 (152) hide show
  1. package/package.json +1 -1
  2. package/src/components/astro/RLAAlert.astro +60 -0
  3. package/src/components/astro/RLAAlertDialog.astro +86 -0
  4. package/src/components/astro/RLAAspectRatio.astro +23 -0
  5. package/src/components/astro/RLAAvatar.astro +56 -0
  6. package/src/components/astro/RLAAvatarGroup.astro +19 -0
  7. package/src/components/astro/RLABadge.astro +31 -0
  8. package/src/components/astro/RLABreadcrumb.astro +44 -0
  9. package/src/components/astro/RLACard.astro +74 -0
  10. package/src/components/astro/RLACarousel.astro +71 -0
  11. package/src/components/astro/RLAChart.astro +215 -0
  12. package/src/components/astro/RLACheckbox.astro +51 -0
  13. package/src/components/astro/RLACollapsible.astro +38 -0
  14. package/src/components/astro/RLACombobox.astro +131 -0
  15. package/src/components/astro/RLADialog.astro +95 -0
  16. package/src/components/astro/RLADrawer.astro +74 -0
  17. package/src/components/astro/RLADropdownMenu.astro +82 -0
  18. package/src/components/astro/RLADropzone.astro +134 -0
  19. package/src/components/astro/RLAField.astro +43 -0
  20. package/src/components/{Head.astro → astro/RLAHead.astro} +181 -199
  21. package/src/components/astro/RLAHoverCard.astro +31 -0
  22. package/src/components/astro/RLAInput.astro +60 -0
  23. package/src/components/astro/RLAInputGroup.astro +35 -0
  24. package/src/components/astro/RLAKbd.astro +22 -0
  25. package/src/components/astro/RLALabel.astro +23 -0
  26. package/src/components/astro/RLALocaleSelector.astro +110 -0
  27. package/src/components/astro/RLAMarquee.astro +32 -0
  28. package/src/components/astro/RLANativeSelect.astro +51 -0
  29. package/src/components/astro/RLANavigationMenu.astro +136 -0
  30. package/src/components/astro/RLAPagination.astro +94 -0
  31. package/src/components/astro/RLAPopover.astro +31 -0
  32. package/src/components/astro/RLAProgress.astro +43 -0
  33. package/src/components/astro/RLAResizable.astro +177 -0
  34. package/src/components/astro/RLAScrollArea.astro +157 -0
  35. package/src/components/astro/RLASelect.astro +109 -0
  36. package/src/components/astro/RLASeparator.astro +27 -0
  37. package/src/components/astro/RLASheet.astro +72 -0
  38. package/src/components/astro/RLASidebar.astro +135 -0
  39. package/src/components/astro/RLASkeleton.astro +20 -0
  40. package/src/components/astro/RLASlider.astro +53 -0
  41. package/src/components/astro/RLASpinner.astro +25 -0
  42. package/src/components/astro/RLAStickySurface.astro +27 -0
  43. package/src/components/astro/RLASwitch.astro +49 -0
  44. package/src/components/astro/RLATable.astro +60 -0
  45. package/src/components/astro/RLATabs.astro +92 -0
  46. package/src/components/astro/RLATextarea.astro +42 -0
  47. package/src/components/astro/RLAThemeSelector.astro +73 -0
  48. package/src/components/astro/RLAToast.astro +107 -0
  49. package/src/components/astro/RLAToggle.astro +38 -0
  50. package/src/components/astro/RLAToggleGroup.astro +30 -0
  51. package/src/components/astro/RLATooltip.astro +31 -0
  52. package/src/integrations/ui.ts +1 -0
  53. package/src/themes/alert.theme.ts +51 -0
  54. package/src/themes/alertDialog.theme.ts +33 -0
  55. package/src/themes/aspectRatio.theme.ts +11 -0
  56. package/src/themes/avatar.theme.ts +28 -0
  57. package/src/themes/avatarGroup.theme.ts +10 -0
  58. package/src/themes/badge.theme.ts +61 -0
  59. package/src/themes/breadcrumb.theme.ts +15 -0
  60. package/src/themes/card.theme.ts +34 -0
  61. package/src/themes/carousel.theme.ts +32 -0
  62. package/src/themes/chart.theme.ts +21 -0
  63. package/src/themes/checkbox.theme.ts +42 -0
  64. package/src/themes/collapsible.theme.ts +22 -0
  65. package/src/themes/combobox.theme.ts +28 -0
  66. package/src/themes/dialog.theme.ts +37 -0
  67. package/src/themes/drawer.theme.ts +44 -0
  68. package/src/themes/dropdownMenu.theme.ts +27 -0
  69. package/src/themes/dropzone.theme.ts +24 -0
  70. package/src/themes/field.theme.ts +24 -0
  71. package/src/themes/hoverCard.theme.ts +27 -0
  72. package/src/themes/input.theme.ts +36 -0
  73. package/src/themes/inputGroup.theme.ts +23 -0
  74. package/src/themes/kbd.theme.ts +24 -0
  75. package/src/themes/label.theme.ts +20 -0
  76. package/src/themes/localeSelector.theme.ts +50 -0
  77. package/src/themes/marquee.theme.ts +27 -0
  78. package/src/themes/nativeSelect.theme.ts +24 -0
  79. package/src/themes/navigationMenu.theme.ts +19 -0
  80. package/src/themes/pagination.theme.ts +26 -0
  81. package/src/themes/popover.theme.ts +28 -0
  82. package/src/themes/progress.theme.ts +26 -0
  83. package/src/themes/resizable.theme.ts +22 -0
  84. package/src/themes/scrollArea.theme.ts +24 -0
  85. package/src/themes/section.theme.ts +1 -2
  86. package/src/themes/select.theme.ts +27 -0
  87. package/src/themes/separator.theme.ts +19 -0
  88. package/src/themes/sheet.theme.ts +43 -0
  89. package/src/themes/sidebar.theme.ts +33 -0
  90. package/src/themes/skeleton.theme.ts +20 -0
  91. package/src/themes/slider.theme.ts +28 -0
  92. package/src/themes/spinner.theme.ts +30 -0
  93. package/src/themes/stickySurface.theme.ts +10 -0
  94. package/src/themes/switch.theme.ts +59 -0
  95. package/src/themes/table.theme.ts +36 -0
  96. package/src/themes/tabs.theme.ts +44 -0
  97. package/src/themes/textarea.theme.ts +26 -0
  98. package/src/themes/themeSelector.theme.ts +16 -0
  99. package/src/themes/toast.theme.ts +25 -0
  100. package/src/themes/toggle.theme.ts +42 -0
  101. package/src/themes/toggleGroup.theme.ts +21 -0
  102. package/src/themes/tooltip.theme.ts +27 -0
  103. package/src/types/components/alert-dialog.ts +35 -0
  104. package/src/types/components/alert.ts +31 -0
  105. package/src/types/components/aspect-ratio.ts +15 -0
  106. package/src/types/components/avatar.ts +47 -0
  107. package/src/types/components/badge.ts +27 -0
  108. package/src/types/components/breadcrumb.ts +24 -0
  109. package/src/types/components/card.ts +31 -0
  110. package/src/types/components/carousel.ts +25 -0
  111. package/src/types/components/chart.ts +38 -0
  112. package/src/types/components/checkbox.ts +35 -0
  113. package/src/types/components/collapsible.ts +31 -0
  114. package/src/types/components/combobox.ts +43 -0
  115. package/src/types/components/dialog.ts +39 -0
  116. package/src/types/components/drawer.ts +31 -0
  117. package/src/types/components/dropdown-menu.ts +42 -0
  118. package/src/types/components/dropzone.ts +39 -0
  119. package/src/types/components/field.ts +31 -0
  120. package/src/types/components/hover-card.ts +19 -0
  121. package/src/types/components/index.ts +47 -0
  122. package/src/types/components/input-group.ts +23 -0
  123. package/src/types/components/input.ts +59 -0
  124. package/src/types/components/kbd.ts +15 -0
  125. package/src/types/components/label.ts +19 -0
  126. package/src/types/components/localeSelector.ts +33 -0
  127. package/src/types/components/marquee.ts +23 -0
  128. package/src/types/components/native-select.ts +39 -0
  129. package/src/types/components/navigation-menu.ts +27 -0
  130. package/src/types/components/pagination.ts +23 -0
  131. package/src/types/components/popover.ts +23 -0
  132. package/src/types/components/progress.ts +27 -0
  133. package/src/types/components/resizable.ts +19 -0
  134. package/src/types/components/scroll-area.ts +23 -0
  135. package/src/types/components/select.ts +43 -0
  136. package/src/types/components/separator.ts +19 -0
  137. package/src/types/components/sheet.ts +31 -0
  138. package/src/types/components/sidebar.ts +36 -0
  139. package/src/types/components/skeleton.ts +15 -0
  140. package/src/types/components/slider.ts +35 -0
  141. package/src/types/components/spinner.ts +19 -0
  142. package/src/types/components/sticky-surface.ts +19 -0
  143. package/src/types/components/switch.ts +35 -0
  144. package/src/types/components/table.ts +31 -0
  145. package/src/types/components/tabs.ts +43 -0
  146. package/src/types/components/textarea.ts +51 -0
  147. package/src/types/components/themeSelector.ts +15 -0
  148. package/src/types/components/toast.ts +35 -0
  149. package/src/types/components/toggle-group.ts +23 -0
  150. package/src/types/components/toggle.ts +27 -0
  151. package/src/types/components/tooltip.ts +23 -0
  152. package/src/components/ThemeToggle.astro +0 -57
@@ -0,0 +1,157 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { ScrollAreaProps } from "../../types"
5
+ import scrollAreaThemeDefault, { createScrollAreaTheme } from "../../themes/scrollArea.theme"
6
+
7
+ const scrollAreaTheme = scrollAreaThemeDefault
8
+ const scrollArea = scv(scrollAreaTheme)
9
+
10
+ const {
11
+ orientation = "vertical",
12
+ scrollHideDelay = 600,
13
+ height,
14
+ ui: uiProp,
15
+ class: className,
16
+ ...rest
17
+ } = Astro.props as ScrollAreaProps
18
+
19
+ const classes = resolveClasses(scrollArea, {
20
+ orientation
21
+ }, useUi("scrollArea", uiProp), className)
22
+
23
+ const heightStyle = height
24
+ ? `height:${typeof height === "number" ? `${height}px` : height}`
25
+ : undefined
26
+ ---
27
+
28
+ <div
29
+ class={classes.root}
30
+ data-slot="root"
31
+ data-scroll-area
32
+ data-orientation={orientation}
33
+ data-hide-delay={scrollHideDelay}
34
+ style={heightStyle}
35
+ {...rest}
36
+ >
37
+ {/* Scrollable viewport */}
38
+ <div
39
+ class={classes.viewport}
40
+ data-slot="viewport"
41
+ data-scroll-viewport
42
+ >
43
+ <slot />
44
+ </div>
45
+
46
+ {/* Vertical scrollbar */}
47
+ {(orientation === "vertical" || orientation === "both") && (
48
+ <div
49
+ class={classes.scrollbar + " opacity-0 transition-opacity"}
50
+ data-slot="scrollbar"
51
+ data-scroll-bar="vertical"
52
+ aria-hidden="true"
53
+ >
54
+ <div class={classes.thumb} data-scroll-thumb="vertical" />
55
+ </div>
56
+ )}
57
+
58
+ {/* Horizontal scrollbar */}
59
+ {(orientation === "horizontal" || orientation === "both") && (
60
+ <div
61
+ class={"absolute bottom-0 left-0 right-0 h-2.5 flex-row flex p-0.5 bg-transparent transition-colors duration-150 ease-out hover:bg-muted/10 opacity-0 transition-opacity"}
62
+ data-slot="scrollbar"
63
+ data-scroll-bar="horizontal"
64
+ aria-hidden="true"
65
+ >
66
+ <div class={classes.thumb} data-scroll-thumb="horizontal" />
67
+ </div>
68
+ )}
69
+ </div>
70
+
71
+ <script>
72
+ document.querySelectorAll("[data-scroll-area]").forEach(area => {
73
+ const viewport = area.querySelector("[data-scroll-viewport]") as HTMLElement | null;
74
+ if (!viewport) return;
75
+
76
+ const hideDelay = parseInt((area as HTMLElement).dataset.hideDelay ?? "600", 10);
77
+
78
+ const vBar = area.querySelector("[data-scroll-bar='vertical']") as HTMLElement | null;
79
+ const hBar = area.querySelector("[data-scroll-bar='horizontal']") as HTMLElement | null;
80
+ const vThumb = area.querySelector("[data-scroll-thumb='vertical']") as HTMLElement | null;
81
+ const hThumb = area.querySelector("[data-scroll-thumb='horizontal']") as HTMLElement | null;
82
+
83
+ let hideTimer: ReturnType<typeof setTimeout>;
84
+
85
+ const showBars = () => {
86
+ vBar?.classList.replace("opacity-0", "opacity-100");
87
+ hBar?.classList.replace("opacity-0", "opacity-100");
88
+ };
89
+
90
+ const hideBars = () => {
91
+ hideTimer = setTimeout(() => {
92
+ vBar?.classList.replace("opacity-100", "opacity-0");
93
+ hBar?.classList.replace("opacity-100", "opacity-0");
94
+ }, hideDelay);
95
+ };
96
+
97
+ const updateThumbs = () => {
98
+ if (vThumb) {
99
+ const ratio = viewport.clientHeight / viewport.scrollHeight;
100
+ const thumbH = Math.max(ratio * (vBar?.clientHeight ?? 0), 20);
101
+ const thumbTop = (viewport.scrollTop / viewport.scrollHeight) * (vBar?.clientHeight ?? 0);
102
+ vThumb.style.height = `${thumbH}px`;
103
+ vThumb.style.transform = `translateY(${thumbTop}px)`;
104
+ }
105
+ if (hThumb) {
106
+ const ratio = viewport.clientWidth / viewport.scrollWidth;
107
+ const thumbW = Math.max(ratio * (hBar?.clientWidth ?? 0), 20);
108
+ const thumbLeft = (viewport.scrollLeft / viewport.scrollWidth) * (hBar?.clientWidth ?? 0);
109
+ hThumb.style.width = `${thumbW}px`;
110
+ hThumb.style.transform = `translateX(${thumbLeft}px)`;
111
+ }
112
+ };
113
+
114
+ viewport.addEventListener("scroll", () => {
115
+ clearTimeout(hideTimer);
116
+ showBars();
117
+ updateThumbs();
118
+ hideBars();
119
+ });
120
+
121
+ // Drag-to-scroll for custom thumbs
122
+ const makeDraggable = (
123
+ thumb: HTMLElement | null,
124
+ scrollProp: "scrollTop" | "scrollLeft",
125
+ clientProp: "clientY" | "clientX",
126
+ sizeProp: "scrollHeight" | "scrollWidth",
127
+ clientSizeProp: "clientHeight" | "clientWidth"
128
+ ) => {
129
+ if (!thumb) return;
130
+ let startPos = 0;
131
+ let startScroll = 0;
132
+
133
+ thumb.addEventListener("mousedown", (e) => {
134
+ e.preventDefault();
135
+ startPos = (e as MouseEvent)[clientProp];
136
+ startScroll = viewport[scrollProp];
137
+
138
+ const onMove = (ev: MouseEvent) => {
139
+ const delta = ev[clientProp] - startPos;
140
+ const ratio = viewport[sizeProp] / viewport[clientSizeProp];
141
+ viewport[scrollProp] = startScroll + delta * ratio;
142
+ };
143
+ const onUp = () => {
144
+ document.removeEventListener("mousemove", onMove);
145
+ document.removeEventListener("mouseup", onUp);
146
+ };
147
+ document.addEventListener("mousemove", onMove);
148
+ document.addEventListener("mouseup", onUp);
149
+ });
150
+ };
151
+
152
+ makeDraggable(vThumb, "scrollTop", "clientY", "scrollHeight", "clientHeight");
153
+ makeDraggable(hThumb, "scrollLeft", "clientX", "scrollWidth", "clientWidth");
154
+
155
+ updateThumbs();
156
+ });
157
+ </script>
@@ -0,0 +1,109 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SelectProps } from "../../types"
5
+ import selectThemeDefault, { createSelectTheme } from "../../themes/select.theme"
6
+
7
+ const selectTheme = selectThemeDefault
8
+ const select = scv(selectTheme)
9
+
10
+ const {
11
+ items = [],
12
+ value = "",
13
+ placeholder = "Select item...",
14
+ disabled = false,
15
+ name,
16
+ label,
17
+ size = "md",
18
+ color = "primary",
19
+ ui: uiProp,
20
+ class: className,
21
+ ...rest
22
+ } = Astro.props as SelectProps
23
+
24
+ const classes = resolveClasses(select, {
25
+ size
26
+ }, useUi("select", uiProp), className);
27
+
28
+ const normalizedItems = items.map(item => {
29
+ if (typeof item === "string") {
30
+ return { label: item, value: item }
31
+ }
32
+ return item
33
+ })
34
+
35
+ const selectedItem = normalizedItems.find(item => item.value === value)
36
+ const initialLabel = selectedItem ? selectedItem.label : ""
37
+ ---
38
+ <div class={classes.root} data-slot="root" data-disabled={disabled ? "" : undefined}>
39
+ {label && <label class={classes.label} data-slot="label">{label}</label>}
40
+
41
+ <div class="relative w-full" data-select-wrapper>
42
+ <button
43
+ type="button"
44
+ class={classes.trigger}
45
+ data-slot="trigger"
46
+ disabled={disabled}
47
+ data-select-trigger
48
+ {...rest}
49
+ >
50
+ <span data-select-value>{initialLabel || placeholder}</span>
51
+ <span class="i-lucide-chevron-down opacity-50" />
52
+ </button>
53
+
54
+ <div class={classes.content + " hidden"} data-select-dropdown data-slot="content">
55
+ <div class="py-1" data-select-items>
56
+ {normalizedItems.map(item => (
57
+ <div
58
+ class={classes.item}
59
+ data-slot="item"
60
+ data-value={item.value}
61
+ data-label={item.label}
62
+ data-select-item
63
+ >
64
+ <span class={classes.itemText}>{item.label}</span>
65
+ <span class={classes.itemIcon + " hidden i-lucide-check"} data-check-icon />
66
+ </div>
67
+ ))}
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <input type="hidden" name={name} value={value} data-select-hidden-input />
72
+ </div>
73
+
74
+ <script>
75
+ document.querySelectorAll("[data-select-wrapper]").forEach(wrapper => {
76
+ const trigger = wrapper.querySelector("[data-select-trigger]") as HTMLButtonElement;
77
+ const dropdown = wrapper.querySelector("[data-select-dropdown]") as HTMLDivElement;
78
+ const items = wrapper.querySelectorAll("[data-select-item]");
79
+ const valueSpan = wrapper.querySelector("[data-select-value]");
80
+ const hiddenInput = wrapper.closest("[data-slot=root]")?.querySelector("[data-select-hidden-input]") as HTMLInputElement;
81
+
82
+ if (!trigger || !dropdown) return;
83
+
84
+ trigger.addEventListener("click", (e) => {
85
+ e.stopPropagation();
86
+ dropdown.classList.toggle("hidden");
87
+ });
88
+
89
+ items.forEach(item => {
90
+ item.addEventListener("click", (e) => {
91
+ e.stopPropagation();
92
+ const val = (item as HTMLElement).dataset.value || "";
93
+ const label = (item as HTMLElement).dataset.label || "";
94
+
95
+ if (hiddenInput) hiddenInput.value = val;
96
+ if (valueSpan) valueSpan.textContent = label;
97
+
98
+ items.forEach(i => i.querySelector("[data-check-icon]")?.classList.add("hidden"));
99
+ item.querySelector("[data-check-icon]")?.classList.remove("hidden");
100
+
101
+ dropdown.classList.add("hidden");
102
+ });
103
+ });
104
+
105
+ document.addEventListener("click", () => {
106
+ dropdown.classList.add("hidden");
107
+ });
108
+ });
109
+ </script>
@@ -0,0 +1,27 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SeparatorProps } from "../../types"
5
+ import separatorTheme from "../../themes/separator.theme"
6
+
7
+ const separator = scv(separatorTheme)
8
+
9
+ const {
10
+ orientation = "horizontal",
11
+ decorative = true,
12
+ ui: uiProp,
13
+ class: className,
14
+ ...rest
15
+ } = Astro.props as SeparatorProps
16
+
17
+ const classes = resolveClasses(separator, {
18
+ orientation
19
+ }, useUi("separator", uiProp), className)
20
+ ---
21
+ <div
22
+ class={classes.root}
23
+ data-slot="root"
24
+ role={decorative ? "none" : "separator"}
25
+ aria-orientation={decorative ? undefined : orientation}
26
+ {...rest}
27
+ />
@@ -0,0 +1,72 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SheetProps } from "../../types"
5
+ import sheetTheme from "../../themes/sheet.theme"
6
+
7
+ const sheet = scv(sheetTheme)
8
+
9
+ const {
10
+ side = "right",
11
+ title,
12
+ description,
13
+ triggerLabel,
14
+ ui: uiProp,
15
+ class: className,
16
+ ...rest
17
+ } = Astro.props as SheetProps
18
+
19
+ const classes = resolveClasses(sheet, {
20
+ side
21
+ }, useUi("sheet", uiProp), className)
22
+
23
+ const dialogId = `rla-sheet-${Math.random().toString(36).substring(2, 9)}`
24
+ ---
25
+ <div class={classes.root} data-slot="sheet-container">
26
+ {triggerLabel ? (
27
+ <button
28
+ class={classes.trigger}
29
+ data-slot="trigger"
30
+ type="button"
31
+ onclick={`document.getElementById('${dialogId}').showModal()`}
32
+ >
33
+ {triggerLabel}
34
+ </button>
35
+ ) : (
36
+ <span onclick={`document.getElementById('${dialogId}').showModal()`}>
37
+ <slot name="trigger" />
38
+ </span>
39
+ )}
40
+
41
+ <dialog
42
+ id={dialogId}
43
+ class={classes.content}
44
+ data-slot="content"
45
+ data-close-on-click-outside="true"
46
+ {...rest}
47
+ >
48
+ <button
49
+ class={classes.closeButton}
50
+ data-slot="close-button"
51
+ type="button"
52
+ onclick={`document.getElementById('${dialogId}').close()`}
53
+ aria-label="Close sheet"
54
+ >
55
+ <span class="i-lucide-x size-4" aria-hidden="true" />
56
+ </button>
57
+
58
+ <div class={classes.header} data-slot="header">
59
+ {title && <h3 class={classes.title} data-slot="title">{title}</h3>}
60
+ <slot name="title" />
61
+
62
+ {description && <p class={classes.description} data-slot="description">{description}</p>}
63
+ <slot name="description" />
64
+ </div>
65
+
66
+ <slot />
67
+
68
+ <div class={classes.footer} data-slot="footer">
69
+ <slot name="footer" />
70
+ </div>
71
+ </dialog>
72
+ </div>
@@ -0,0 +1,135 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SidebarProps, SidebarItem } from "../../types"
5
+ import sidebarThemeDefault, { createSidebarTheme } from "../../themes/sidebar.theme"
6
+
7
+ const sidebarTheme = sidebarThemeDefault
8
+ const sidebar = scv(sidebarTheme)
9
+
10
+ const {
11
+ items = [],
12
+ side = "left",
13
+ collapsible = "none",
14
+ logo,
15
+ ui: uiProp,
16
+ class: className,
17
+ ...rest
18
+ } = Astro.props as SidebarProps
19
+
20
+ const classes = resolveClasses(sidebar, {
21
+ side,
22
+ collapsible
23
+ }, useUi("sidebar", uiProp), className)
24
+
25
+ // Recursive helper to render sidebar items with children
26
+ const hasActiveChild = (item: SidebarItem): boolean => {
27
+ if (item.active) return true
28
+ return (item.children ?? []).some(c => hasActiveChild(c))
29
+ }
30
+ ---
31
+
32
+ <aside class={classes.root} data-slot="root" data-sidebar {...rest}>
33
+ {/* Header */}
34
+ <div class={classes.header} data-slot="header">
35
+ <slot name="header">
36
+ {logo && (
37
+ <span data-slot="sidebar-logo" class="flex items-center gap-2">
38
+ <span class="i-lucide-layers size-5 text-primary" />
39
+ <span class="truncate">{logo}</span>
40
+ </span>
41
+ )}
42
+ </slot>
43
+ </div>
44
+
45
+ {/* Body */}
46
+ <div class={classes.content} data-slot="content">
47
+ <slot>
48
+ {items.map((item) => {
49
+ const hasChildren = item.children && item.children.length > 0
50
+ const isActive = item.active || hasActiveChild(item)
51
+ return (
52
+ <div class={classes.item} data-slot="item">
53
+ {hasChildren ? (
54
+ <details open={isActive} class="group/details">
55
+ <summary
56
+ class={classes.itemLink + (isActive ? " text-foreground bg-muted" : "") + " list-none cursor-pointer"}
57
+ data-slot="item-summary"
58
+ >
59
+ {item.icon && (
60
+ <span
61
+ class={classes.itemIcon + " " + item.icon}
62
+ aria-hidden="true"
63
+ />
64
+ )}
65
+ <span data-slot="item-text" class="truncate flex-1">{item.label}</span>
66
+ {item.badge !== undefined && (
67
+ <span class={classes.itemBadge} data-slot="item-badge">{item.badge}</span>
68
+ )}
69
+ <span class="i-lucide-chevron-right size-3.5 ml-auto transition-transform group-open/details:rotate-90" />
70
+ </summary>
71
+ <div class="mt-0.5 ml-6 flex flex-col gap-0.5">
72
+ {item.children!.map(child => (
73
+ <a
74
+ href={child.href ?? "#"}
75
+ class={classes.itemLink + (child.active ? " text-foreground bg-muted" : "") + " text-xs pl-2"}
76
+ >
77
+ {child.icon && <span class={classes.itemIcon + " " + child.icon} aria-hidden="true" />}
78
+ <span data-slot="item-text" class="truncate flex-1">{child.label}</span>
79
+ {child.badge !== undefined && (
80
+ <span class={classes.itemBadge} data-slot="item-badge">{child.badge}</span>
81
+ )}
82
+ </a>
83
+ ))}
84
+ </div>
85
+ </details>
86
+ ) : (
87
+ <a
88
+ href={item.href ?? "#"}
89
+ class={classes.itemLink + (isActive ? " text-foreground bg-muted" : "")}
90
+ aria-current={isActive ? "page" : undefined}
91
+ >
92
+ {item.icon && (
93
+ <span
94
+ class={classes.itemIcon + " " + item.icon}
95
+ aria-hidden="true"
96
+ />
97
+ )}
98
+ <span data-slot="item-text" class="truncate flex-1">{item.label}</span>
99
+ {item.badge !== undefined && (
100
+ <span class={classes.itemBadge} data-slot="item-badge">{item.badge}</span>
101
+ )}
102
+ </a>
103
+ )}
104
+ </div>
105
+ )
106
+ })}
107
+ </slot>
108
+ </div>
109
+
110
+ {/* Footer */}
111
+ <div class={classes.footer} data-slot="footer">
112
+ <slot name="footer" />
113
+ </div>
114
+ </aside>
115
+
116
+ {collapsible === "offcanvas" && (
117
+ <button
118
+ type="button"
119
+ data-sidebar-toggle
120
+ class="fixed top-4 left-4 z-50 flex size-9 items-center justify-center rounded-md bg-background border border-border shadow-sm hover:bg-muted transition-colors"
121
+ aria-label="Toggle sidebar"
122
+ >
123
+ <span class="i-lucide-panel-left size-4" />
124
+ </button>
125
+ )}
126
+
127
+ <script>
128
+ document.querySelectorAll("[data-sidebar-toggle]").forEach(toggle => {
129
+ toggle.addEventListener("click", () => {
130
+ const sidebar = document.querySelector("[data-sidebar]");
131
+ if (!sidebar) return;
132
+ sidebar.classList.toggle("-translate-x-full");
133
+ });
134
+ });
135
+ </script>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SkeletonProps } from "../../types"
5
+ import skeletonTheme from "../../themes/skeleton.theme"
6
+
7
+ const skeleton = scv(skeletonTheme)
8
+
9
+ const {
10
+ shape = "rounded",
11
+ ui: uiProp,
12
+ class: className,
13
+ ...rest
14
+ } = Astro.props as SkeletonProps
15
+
16
+ const classes = resolveClasses(skeleton, {
17
+ shape
18
+ }, useUi("skeleton", uiProp), className)
19
+ ---
20
+ <div class={classes.root} data-slot="root" {...rest} />
@@ -0,0 +1,53 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SliderProps } from "../../types"
5
+ import sliderThemeDefault, { createSliderTheme } from "../../themes/slider.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ const uiConfig = getUIConfig()
9
+ const sliderTheme = uiConfig.colors ? createSliderTheme(uiConfig.colors) : sliderThemeDefault
10
+ const slider = scv(sliderTheme)
11
+
12
+ const {
13
+ value = 0,
14
+ min = 0,
15
+ max = 100,
16
+ step = 1,
17
+ name,
18
+ color = "primary",
19
+ ui: uiProp,
20
+ class: className,
21
+ ...rest
22
+ } = Astro.props as SliderProps
23
+
24
+ const classes = resolveClasses(slider, {
25
+ color
26
+ }, useUi("slider", uiProp), className)
27
+
28
+ const percentage = Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100))
29
+ ---
30
+ <div class={classes.root} data-slot="slider-root" {...rest}>
31
+ <div class={classes.track} data-slot="track">
32
+ <div
33
+ class={classes.range}
34
+ data-slot="range"
35
+ style={{ left: "0%", right: `${100 - percentage}%` }}
36
+ />
37
+ </div>
38
+ <input
39
+ type="range"
40
+ min={min}
41
+ max={max}
42
+ step={step}
43
+ value={value}
44
+ name={name}
45
+ class="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
46
+ data-slot="input"
47
+ />
48
+ <div
49
+ class={classes.thumb}
50
+ data-slot="thumb"
51
+ style={{ left: `calc(${percentage}% - 9px)` }}
52
+ />
53
+ </div>
@@ -0,0 +1,25 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SpinnerProps } from "../../types"
5
+ import spinnerThemeDefault, { createSpinnerTheme } from "../../themes/spinner.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ const uiConfig = getUIConfig()
9
+ const spinnerTheme = uiConfig.colors ? createSpinnerTheme(uiConfig.colors) : spinnerThemeDefault
10
+ const spinner = scv(spinnerTheme)
11
+
12
+ const {
13
+ size = "md",
14
+ color = "current",
15
+ ui: uiProp,
16
+ class: className,
17
+ ...rest
18
+ } = Astro.props as SpinnerProps
19
+
20
+ const classes = resolveClasses(spinner, {
21
+ size,
22
+ color
23
+ }, useUi("spinner", uiProp), className)
24
+ ---
25
+ <div class={classes.root} data-slot="root" {...rest} />
@@ -0,0 +1,27 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { StickySurfaceProps } from "../../types"
5
+ import stickySurfaceTheme from "../../themes/stickySurface.theme"
6
+
7
+ const stickySurface = scv(stickySurfaceTheme)
8
+
9
+ const {
10
+ offsetTop = 0,
11
+ zIndex = 10,
12
+ ui: uiProp,
13
+ class: className,
14
+ ...rest
15
+ } = Astro.props as StickySurfaceProps
16
+
17
+ const classes = resolveClasses(stickySurface, {}, useUi("stickySurface", uiProp), className)
18
+ const topOffset = `${offsetTop}px`
19
+ ---
20
+ <div
21
+ class={classes.root}
22
+ data-slot="root"
23
+ style={{ top: topOffset, zIndex }}
24
+ {...rest}
25
+ >
26
+ <slot />
27
+ </div>
@@ -0,0 +1,49 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { SwitchProps } from "../../types"
5
+ import switchThemeDefault, { createSwitchTheme } from "../../themes/switch.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ const uiConfig = getUIConfig()
9
+ const switchTheme = uiConfig.colors ? createSwitchTheme(uiConfig.colors) : switchThemeDefault
10
+
11
+ const switchFn = scv(switchTheme)
12
+
13
+ const {
14
+ checked = false,
15
+ disabled = false,
16
+ name,
17
+ label,
18
+ color = "primary",
19
+ size = "md",
20
+ ui: uiProp,
21
+ class: className,
22
+ ...rest
23
+ } = Astro.props as SwitchProps
24
+
25
+ const classes = resolveClasses(switchFn, {
26
+ color,
27
+ size,
28
+ checked
29
+ }, useUi("switch", uiProp), className);
30
+ ---
31
+ <label class={classes.root} data-slot="root">
32
+ <input
33
+ type="checkbox"
34
+ class={classes.input}
35
+ checked={checked}
36
+ disabled={disabled}
37
+ name={name}
38
+ {...rest}
39
+ />
40
+ <div class={classes.track} data-slot="track">
41
+ <div class={classes.thumb} data-slot="thumb" />
42
+ </div>
43
+ {label !== undefined && label !== null && (
44
+ <span class={classes.label} data-slot="label">
45
+ {label}
46
+ </span>
47
+ )}
48
+ {!label && <slot />}
49
+ </label>