@rimelight/ui 0.0.46 → 0.0.48

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 (222) hide show
  1. package/dist/icons-DPnFQCts.d.mts +72 -0
  2. package/dist/icons.d.mts +1 -1
  3. package/dist/index.d.mts +5 -6
  4. package/dist/preset.d.mts +1 -1
  5. package/dist/preset.mjs +73 -1
  6. package/dist/resolver.d.mts +4 -5
  7. package/dist/resolver.mjs +2 -4
  8. package/dist/shortcuts.d.mts +10 -11
  9. package/dist/stores.d.mts +3 -4
  10. package/dist/{types-DqAgCNcm.d.mts → types-DWmKMcYC.d.mts} +1 -1
  11. package/dist/types.d.mts +1 -1
  12. package/package.json +8 -6
  13. package/src/components/3d-hover/RLS3dHover.tsx +21 -0
  14. package/src/components/accordion/RLSAccordion.tsx +112 -0
  15. package/src/components/app/RLSApp.tsx +21 -0
  16. package/src/components/aspect-ratio/RLSAspectRatio.tsx +30 -0
  17. package/src/components/audio/RLSAudio.tsx +27 -0
  18. package/src/components/avatar/RLSAvatar.tsx +142 -0
  19. package/src/components/avatar/avatar.ts +22 -21
  20. package/src/components/avatar-group/RLSAvatarGroup.tsx +163 -0
  21. package/src/components/badge/RLSBadge.tsx +1 -1
  22. package/src/components/badge/RLVBadge.vue +5 -3
  23. package/src/components/banner/RLABanner.astro +1 -1
  24. package/src/components/banner/RLSBanner.tsx +151 -0
  25. package/src/components/breadcrumb/RLSBreadcrumb.tsx +194 -0
  26. package/src/components/browser-mockup/RLSBrowserMockup.tsx +21 -0
  27. package/src/components/button/RLSButton.tsx +53 -38
  28. package/src/components/button/RLVButton.vue +44 -32
  29. package/src/components/calendar/RLSCalendar.tsx +26 -0
  30. package/src/components/callout/RLSCallout.tsx +151 -0
  31. package/src/components/card/RLSCard.tsx +4 -0
  32. package/src/components/card/RLVCard.vue +4 -0
  33. package/src/components/card/card.ts +10 -0
  34. package/src/components/carousel/RLACarousel.astro +14 -1
  35. package/src/components/carousel/RLSCarousel.tsx +260 -0
  36. package/src/components/chart/RLSChart.tsx +299 -0
  37. package/src/components/chat/RLSChat.tsx +27 -0
  38. package/src/components/chat-message/RLSChatMessage.tsx +27 -0
  39. package/src/components/chat-messages/RLSChatMessages.tsx +27 -0
  40. package/src/components/chat-palette/RLSChatPalette.tsx +27 -0
  41. package/src/components/chat-prompt/RLSChatPrompt.tsx +27 -0
  42. package/src/components/chat-prompt-submit/RLSChatPromptSubmit.tsx +27 -0
  43. package/src/components/chat-reasoning/RLSChatReasoning.tsx +27 -0
  44. package/src/components/chat-shimmer/RLSChatShimmer.tsx +27 -0
  45. package/src/components/chat-tool/RLSChatTool.tsx +27 -0
  46. package/src/components/checkbox/RLACheckbox.astro +1 -0
  47. package/src/components/checkbox/RLSCheckbox.tsx +1 -1
  48. package/src/components/checkbox/checkbox.theme.ts +2 -2
  49. package/src/components/chip/RLSChip.tsx +65 -0
  50. package/src/components/collapsible/RLSCollapsible.tsx +89 -0
  51. package/src/components/color-area/RLSColorArea.tsx +554 -0
  52. package/src/components/color-field/RLSColorField.tsx +236 -0
  53. package/src/components/color-picker/RLSColorPicker.tsx +26 -0
  54. package/src/components/color-slider/RLSColorSlider.tsx +373 -0
  55. package/src/components/command-palette/RLSCommandPalette.tsx +26 -0
  56. package/src/components/compare/RLSCompare.tsx +26 -0
  57. package/src/components/confirm/RLSConfirm.tsx +173 -0
  58. package/src/components/container/RLSContainer.tsx +24 -0
  59. package/src/components/context-menu/RLSContextMenu.tsx +26 -0
  60. package/src/components/copy-markdown/RLSCopyMarkdown.tsx +57 -0
  61. package/src/components/dashboard-group/RLSDashboardGroup.tsx +29 -0
  62. package/src/components/dashboard-navbar/RLSDashboardNavbar.tsx +116 -0
  63. package/src/components/dashboard-panel/RLSDashboardPanel.tsx +84 -0
  64. package/src/components/dashboard-resize-handle/RLSDashboardResizeHandle.tsx +35 -0
  65. package/src/components/dashboard-search/RLADashboardSearch.astro +1 -1
  66. package/src/components/dashboard-search/RLSDashboardSearch.tsx +125 -0
  67. package/src/components/dashboard-search-button/RLSDashboardSearchButton.tsx +82 -0
  68. package/src/components/dashboard-sidebar/RLADashboardSidebar.astro +4 -4
  69. package/src/components/dashboard-sidebar/RLSDashboardSidebar.tsx +184 -0
  70. package/src/components/dashboard-sidebar-collapse/RLSDashboardSidebarCollapse.tsx +46 -0
  71. package/src/components/dashboard-sidebar-toggle/RLSDashboardSidebarToggle.tsx +35 -0
  72. package/src/components/dashboard-toolbar/RLSDashboardToolbar.tsx +49 -0
  73. package/src/components/date/RLSDate.tsx +34 -0
  74. package/src/components/dock/RLSDock.tsx +26 -0
  75. package/src/components/drawer/RLSDrawer.tsx +177 -0
  76. package/src/components/dropdown-menu/RLADropdownMenu.astro +250 -241
  77. package/src/components/dropdown-menu/RLSDropdownMenu.tsx +183 -86
  78. package/src/components/editor/RLSEditor.tsx +26 -0
  79. package/src/components/empty/RLSEmpty.tsx +132 -0
  80. package/src/components/error/RLSError.tsx +26 -0
  81. package/src/components/field-group/RLSFieldGroup.tsx +46 -0
  82. package/src/components/fieldset/RLSFieldset.tsx +26 -0
  83. package/src/components/file-upload/RLAFileUpload.astro +1 -1
  84. package/src/components/file-upload/RLSFileUpload.tsx +163 -0
  85. package/src/components/floating-action/RLSFloatingAction.tsx +26 -0
  86. package/src/components/footer/RLSFooter.tsx +56 -0
  87. package/src/components/form/RLSForm.tsx +34 -0
  88. package/src/components/gallery/RLSGallery.tsx +21 -0
  89. package/src/components/gamepad/RLSGamepad.tsx +269 -0
  90. package/src/components/grid/RLSGrid.tsx +29 -0
  91. package/src/components/head/RLAHead.astro +9 -0
  92. package/src/components/head/RLSHead.tsx +42 -0
  93. package/src/components/head/UIHead.astro +3 -0
  94. package/src/components/header/HeaderLayer.astro +1 -1
  95. package/src/components/header/RLSHeader.tsx +87 -0
  96. package/src/components/hover-card/RLSHoverCard.tsx +72 -0
  97. package/src/components/icon/RLSIcon.tsx +3 -1
  98. package/src/components/image/RLAImage.astro +447 -138
  99. package/src/components/image/RLSImage.tsx +545 -0
  100. package/src/components/image/image.theme.ts +19 -3
  101. package/src/components/image/image.ts +31 -1
  102. package/src/components/input/RLAInput.astro +62 -51
  103. package/src/components/input/RLSInput.tsx +1 -1
  104. package/src/components/input-date/RLSInputDate.tsx +92 -0
  105. package/src/components/input-menu/RLAInputMenu.astro +3 -3
  106. package/src/components/input-menu/RLSInputMenu.tsx +199 -0
  107. package/src/components/input-number/RLSInputNumber.tsx +184 -0
  108. package/src/components/input-pin/RLSInputPin.tsx +185 -0
  109. package/src/components/input-rating/RLAInputRating.astro +8 -8
  110. package/src/components/input-rating/RLSInputRating.tsx +198 -0
  111. package/src/components/input-rating/input-rating.ts +4 -0
  112. package/src/components/input-tags/RLSInputTags.tsx +94 -0
  113. package/src/components/input-time/RLSInputTime.tsx +92 -0
  114. package/src/components/kbd/RLSKbd.tsx +37 -0
  115. package/src/components/keyboard/RLSKeyboard.tsx +476 -0
  116. package/src/components/knob/RLSKnob.tsx +27 -0
  117. package/src/components/label/RLSLabel.tsx +27 -0
  118. package/src/components/layout-grid/RLALayoutGrid.astro +29 -29
  119. package/src/components/layout-grid/RLSLayoutGrid.tsx +402 -0
  120. package/src/components/layout-grid/layout-grid.ts +25 -1
  121. package/src/components/link/RLALink.astro +5 -5
  122. package/src/components/link/RLSLink.tsx +6 -5
  123. package/src/components/link/link.ts +21 -12
  124. package/src/components/link-group/RLSLinkGroup.tsx +76 -0
  125. package/src/components/listbox/RLSListbox.tsx +27 -0
  126. package/src/components/locale-selector/RLALocaleSelector.astro +110 -102
  127. package/src/components/locale-selector/RLSLocaleSelector.tsx +178 -0
  128. package/src/components/locale-selector/locale-selector.ts +2 -7
  129. package/src/components/logo/RLALogo.astro +153 -102
  130. package/src/components/logo/RLSLogo.tsx +129 -0
  131. package/src/components/main/RLSMain.tsx +40 -0
  132. package/src/components/marquee/RLSMarquee.tsx +80 -0
  133. package/src/components/meter/RLSMeter.tsx +27 -0
  134. package/src/components/modal/RLAModal.astro +1 -1
  135. package/src/components/modal/RLSModal.tsx +109 -72
  136. package/src/components/navigation-menu/RLANavigationMenu.astro +6 -6
  137. package/src/components/navigation-menu/RLSNavigationMenu.tsx +228 -0
  138. package/src/components/navigation-menu/navigation-menu.ts +2 -2
  139. package/src/components/package-managers/RLSPackageManagers.tsx +204 -0
  140. package/src/components/page/RLSPage.tsx +21 -0
  141. package/src/components/page-aside/RLSPageAside.tsx +21 -0
  142. package/src/components/page-body/RLSPageBody.tsx +21 -0
  143. package/src/components/page-header/RLSPageHeader.tsx +21 -0
  144. package/src/components/page-section/RLSPageSection.tsx +201 -0
  145. package/src/components/pagination/RLSPagination.tsx +114 -0
  146. package/src/components/password/RLSPassword.tsx +27 -0
  147. package/src/components/phone-mockup/RLSPhoneMockup.tsx +27 -0
  148. package/src/components/placeholder/RLSPlaceholder.tsx +47 -0
  149. package/src/components/popover/RLAPopover.astro +2 -2
  150. package/src/components/popover/RLSPopover.tsx +124 -0
  151. package/src/components/post/RLSPost.tsx +134 -0
  152. package/src/components/pricing-plan/RLSPricingPlan.tsx +26 -0
  153. package/src/components/pricing-table/RLSPricingTable.tsx +26 -0
  154. package/src/components/progress/RLSProgress.tsx +53 -0
  155. package/src/components/prose/RLSProse.tsx +26 -0
  156. package/src/components/qr-code/RLSQrCode.tsx +26 -0
  157. package/src/components/quote/RLAQuote.astro +10 -6
  158. package/src/components/quote/RLSQuote.tsx +37 -0
  159. package/src/components/quote/quote.theme.ts +5 -2
  160. package/src/components/radio-group/RLSRadioGroup.tsx +37 -0
  161. package/src/components/rating/RLSRating.tsx +27 -0
  162. package/src/components/scroll-area/RLAScrollArea.astro +1 -1
  163. package/src/components/scroll-area/RLSScrollArea.tsx +210 -0
  164. package/src/components/scroll-to-top/RLAScrollToTop.astro +5 -4
  165. package/src/components/scroll-to-top/RLSScrollToTop.tsx +145 -0
  166. package/src/components/search/RLASearch.astro +6 -6
  167. package/src/components/search/RLSSearch.tsx +237 -0
  168. package/src/components/select/RLASelect.astro +67 -4
  169. package/src/components/select/RLSSelect.tsx +107 -39
  170. package/src/components/select/RLVSelect.vue +50 -1
  171. package/src/components/select/select.theme.ts +30 -10
  172. package/src/components/select/select.ts +20 -1
  173. package/src/components/separator/RLSSeparator.tsx +92 -0
  174. package/src/components/share/RLSShare.tsx +66 -0
  175. package/src/components/shortcuts/RLAShortcuts.astro +2 -2
  176. package/src/components/shortcuts/RLSShortcuts.tsx +393 -0
  177. package/src/components/sidebar/RLASidebar.astro +1 -1
  178. package/src/components/sidebar/RLSSidebar.tsx +271 -0
  179. package/src/components/skeleton/RLSSkeleton.tsx +21 -0
  180. package/src/components/slideover/RLASlideover.astro +1 -1
  181. package/src/components/slideover/RLSSlideover.tsx +159 -0
  182. package/src/components/slider/RLSSlider.tsx +112 -0
  183. package/src/components/speed-dial/RLSSpeedDial.tsx +27 -0
  184. package/src/components/spinner/RLSSpinner.tsx +22 -0
  185. package/src/components/splitter/RLASplitter.astro +1 -1
  186. package/src/components/splitter/RLSSplitter.tsx +178 -0
  187. package/src/components/spoiler/RLSSpoiler.tsx +26 -0
  188. package/src/components/stepper/RLAStepper.astro +9 -9
  189. package/src/components/stepper/RLSStepper.tsx +205 -0
  190. package/src/components/steps/RLSSteps.tsx +108 -0
  191. package/src/components/sticky-surface/RLSStickySurface.tsx +63 -0
  192. package/src/components/surround/RLSSurround.tsx +57 -0
  193. package/src/components/switch/RLSSwitch.tsx +56 -0
  194. package/src/components/table/RLATable.astro +1044 -579
  195. package/src/components/table/RLSTable.tsx +893 -0
  196. package/src/components/table/table.theme.ts +47 -16
  197. package/src/components/table/table.ts +104 -66
  198. package/src/components/table-of-contents/RLSTableOfContents.tsx +196 -0
  199. package/src/components/tabs/RLATabs.astro +8 -8
  200. package/src/components/tabs/RLSTabs.tsx +298 -0
  201. package/src/components/textarea/RLATextarea.astro +1 -0
  202. package/src/components/textarea/RLSTextarea.tsx +3 -1
  203. package/src/components/theme-selector/RLAThemeSelector.astro +85 -56
  204. package/src/components/theme-selector/RLSThemeSelector.tsx +131 -0
  205. package/src/components/theme-selector/theme-selector.theme.ts +5 -7
  206. package/src/components/theme-selector/theme-selector.ts +11 -1
  207. package/src/components/timeline/RLSTimeline.tsx +171 -0
  208. package/src/components/toast/RLAToast.astro +2 -2
  209. package/src/components/toast/RLSToast.tsx +179 -0
  210. package/src/components/toaster/RLSToaster.tsx +82 -0
  211. package/src/components/tooltip/RLSTooltip.tsx +48 -0
  212. package/src/components/tour/RLSTour.tsx +26 -0
  213. package/src/components/tree/RLSTree.tsx +26 -0
  214. package/src/components/user/RLSUser.tsx +26 -0
  215. package/src/components/video/RLAVideo.astro +4 -1
  216. package/src/components/video/RLSVideo.tsx +158 -0
  217. package/src/components/watermark/RLSWatermark.tsx +27 -0
  218. package/src/components/window-mockup/RLSWindowMockup.tsx +27 -0
  219. package/src/preset.ts +73 -1
  220. package/src/resolver.ts +6 -1
  221. package/src/virtual.d.ts +6 -0
  222. package/dist/icons--RzV1RTU.d.mts +0 -72
@@ -225,7 +225,7 @@ if (needsOverflow && renderedListItems.length > 1) {
225
225
 
226
226
  const list = container.querySelector("[data-slot=list]") as HTMLElement | null;
227
227
  const indicator = container.querySelector("[data-slot=indicator]") as HTMLElement | null;
228
- const orientation = this.dataset.orientation || "horizontal";
228
+ const orientation = this.dataset["orientation"] || "horizontal";
229
229
  const isVertical = orientation === "vertical";
230
230
 
231
231
  const updateIndicator = (activeTrigger: HTMLElement | null) => {
@@ -263,7 +263,7 @@ if (needsOverflow && renderedListItems.length > 1) {
263
263
  }
264
264
  };
265
265
 
266
- const containerValue = container.dataset.value;
266
+ const containerValue = container.dataset["value"];
267
267
  const triggers = container.querySelectorAll("[role=tab]");
268
268
  const panels = container.querySelectorAll("[role=tabpanel]");
269
269
  const popoverItems = container.querySelectorAll("[data-popover-item]");
@@ -273,7 +273,7 @@ if (needsOverflow && renderedListItems.length > 1) {
273
273
  const syncState = (targetValue: string | undefined) => {
274
274
  triggers.forEach(trigger => {
275
275
  const el = trigger as HTMLElement;
276
- const val = el.dataset.value;
276
+ const val = el.dataset["value"];
277
277
  const isActive = val === targetValue;
278
278
  el.setAttribute("data-state", isActive ? "active" : "inactive");
279
279
  if (isActive) activeTrigger = el;
@@ -281,7 +281,7 @@ if (needsOverflow && renderedListItems.length > 1) {
281
281
 
282
282
  popoverItems.forEach(item => {
283
283
  const el = item as HTMLElement;
284
- const val = el.dataset.tabOverflowValue;
284
+ const val = el.dataset["tabOverflowValue"];
285
285
  if (val === targetValue) {
286
286
  el.setAttribute("data-active", "true");
287
287
  el.classList.add("bg-muted/80", "font-medium");
@@ -292,7 +292,7 @@ if (needsOverflow && renderedListItems.length > 1) {
292
292
  });
293
293
 
294
294
  panels.forEach(panel => {
295
- const val = (panel as HTMLElement).dataset.value;
295
+ const val = (panel as HTMLElement).dataset["value"];
296
296
  if (val === targetValue) {
297
297
  panel.removeAttribute("hidden");
298
298
  } else {
@@ -319,7 +319,7 @@ if (needsOverflow && renderedListItems.length > 1) {
319
319
  // Watch for browser Find-in-Page auto-expands
320
320
  panels.forEach(panel => {
321
321
  const onBeforeMatch = () => {
322
- const targetValue = (panel as HTMLElement).dataset.value;
322
+ const targetValue = (panel as HTMLElement).dataset["value"];
323
323
  const trigger = container.querySelector(`[role=tab][data-value="${targetValue}"]`) as HTMLButtonElement | null;
324
324
  if (trigger) {
325
325
  trigger.click();
@@ -335,7 +335,7 @@ if (needsOverflow && renderedListItems.length > 1) {
335
335
  const trigger = triggerNode as HTMLButtonElement;
336
336
  const onClick = () => {
337
337
  if (trigger.disabled) return;
338
- const targetValue = trigger.dataset.value;
338
+ const targetValue = trigger.dataset["value"];
339
339
  syncState(targetValue);
340
340
  updateIndicator(trigger);
341
341
  };
@@ -346,7 +346,7 @@ if (needsOverflow && renderedListItems.length > 1) {
346
346
  popoverItems.forEach(itemNode => {
347
347
  const item = itemNode as HTMLButtonElement;
348
348
  const onClick = () => {
349
- const targetValue = item.dataset.tabOverflowValue;
349
+ const targetValue = item.dataset["tabOverflowValue"];
350
350
  syncState(targetValue);
351
351
  const activeTrig = container.querySelector(`[role=tab][data-value="${targetValue}"]`) as HTMLElement | null;
352
352
  updateIndicator(activeTrig);
@@ -0,0 +1,298 @@
1
+ import {
2
+ createEffect,
3
+ createMemo,
4
+ createSignal,
5
+ For,
6
+ onCleanup,
7
+ onMount,
8
+ Show,
9
+ splitProps,
10
+ type Component,
11
+ type JSX
12
+ } from "solid-js"
13
+ import type { TabItem, TabsProps } from "./tabs"
14
+ import { tabsTheme } from "./tabs.theme"
15
+ import RLSBadge from "../badge/RLSBadge"
16
+ import RLSIcon from "../icon/RLSIcon"
17
+
18
+ export interface RLSTabsProps<T extends TabItem = TabItem> extends TabsProps<T> {
19
+ children?: JSX.Element
20
+ onChange?: (value: string) => void
21
+ }
22
+
23
+ const RLSTabs: Component<RLSTabsProps> = (allProps) => {
24
+ const [props, rest] = splitProps(allProps, [
25
+ "items",
26
+ "value",
27
+ "defaultValue",
28
+ "variant",
29
+ "orientation",
30
+ "size",
31
+ "color",
32
+ "content",
33
+ "unmountOnHide",
34
+ "valueKey",
35
+ "labelKey",
36
+ "maxTabs",
37
+ "estimatedTabWidth",
38
+ "onChange",
39
+ "class",
40
+ "ui",
41
+ "children"
42
+ ])
43
+
44
+ const items = () => props.items ?? []
45
+ const valueKey = () => props.valueKey ?? "value"
46
+ const labelKey = () => props.labelKey ?? "label"
47
+ const orientation = () => props.orientation ?? "horizontal"
48
+ const size = () => props.size ?? "md"
49
+ const showContent = () => props.content ?? true
50
+
51
+ const normalizedVariant = createMemo(() => {
52
+ const v = props.variant ?? "pill"
53
+ if (v === "solid" || v === "pills") return "pill"
54
+ if (v === "link") return "underline"
55
+ return v
56
+ })
57
+
58
+ const initialVal = createMemo(() => {
59
+ if (props.value !== undefined) return props.value
60
+ if (props.defaultValue !== undefined) return props.defaultValue
61
+ const list = items()
62
+ if (list.length > 0) {
63
+ return (list[0]?.[valueKey()] ?? list[0]?.value ?? "0") as string
64
+ }
65
+ return ""
66
+ })
67
+
68
+ const [internalValue, setInternalValue] = createSignal(initialVal())
69
+
70
+ createEffect(() => {
71
+ if (props.value !== undefined) {
72
+ setInternalValue(props.value)
73
+ }
74
+ })
75
+
76
+ const currentValue = () => (props.value !== undefined ? props.value : internalValue())
77
+
78
+ const handleSelect = (val: string) => {
79
+ if (props.value === undefined) {
80
+ setInternalValue(val)
81
+ }
82
+ props.onChange?.(val)
83
+ }
84
+
85
+ const resolvedClasses = createMemo(() =>
86
+ tabsTheme({
87
+ variant: normalizedVariant(),
88
+ orientation: orientation(),
89
+ size: size(),
90
+ color: props.color,
91
+ ui: props.ui,
92
+ class: props.class
93
+ })
94
+ )
95
+
96
+ let listRef: HTMLDivElement | null = null
97
+ let indicatorRef: HTMLDivElement | null = null
98
+ const [popoverOpen, setPopoverOpen] = createSignal(false)
99
+
100
+ const updateIndicator = () => {
101
+ const list = listRef
102
+ const indicator = indicatorRef
103
+ if (!list || !indicator) return
104
+ const activeEl = list.querySelector(`[role=tab][data-state=active]`) as HTMLElement | null
105
+ if (!activeEl || getComputedStyle(activeEl).display === "none") {
106
+ indicator.style.display = "none"
107
+ return
108
+ }
109
+
110
+ const listRect = list.getBoundingClientRect()
111
+ const triggerRect = activeEl.getBoundingClientRect()
112
+
113
+ if (triggerRect.width === 0 && triggerRect.height === 0) {
114
+ indicator.style.display = "none"
115
+ return
116
+ }
117
+
118
+ indicator.style.display = "block"
119
+ const isVert = orientation() === "vertical"
120
+
121
+ if (isVert) {
122
+ const top = triggerRect.top - listRect.top + list.scrollTop
123
+ const height = triggerRect.height
124
+ indicator.style.transform = `translateY(${top}px)`
125
+ indicator.style.height = `${height}px`
126
+ } else {
127
+ const left = triggerRect.left - listRect.left + list.scrollLeft
128
+ const width = triggerRect.width
129
+ indicator.style.transform = `translateX(${left}px)`
130
+ indicator.style.width = `${width}px`
131
+ }
132
+ }
133
+
134
+ onMount(() => {
135
+ updateIndicator()
136
+ if (typeof ResizeObserver !== "undefined" && listRef) {
137
+ const ro = new ResizeObserver(() => updateIndicator())
138
+ ro.observe(listRef)
139
+ onCleanup(() => ro.disconnect())
140
+ }
141
+ })
142
+
143
+ createEffect(() => {
144
+ currentValue()
145
+ requestAnimationFrame(() => updateIndicator())
146
+ })
147
+
148
+ const effectiveMax = () =>
149
+ props.maxTabs !== undefined ? Math.max(1, props.maxTabs) : items().length
150
+ const renderedListItems = () => items().slice(0, effectiveMax())
151
+ const overflowItems = () => items().slice(effectiveMax())
152
+ const needsOverflow = () => items().length > 1 && orientation() === "horizontal"
153
+
154
+ return (
155
+ <div
156
+ class={resolvedClasses().root}
157
+ data-slot="root"
158
+ data-orientation={orientation()}
159
+ data-variant={normalizedVariant()}
160
+ data-value={currentValue()}
161
+ {...rest}
162
+ >
163
+ <div
164
+ ref={(el) => (listRef = el)}
165
+ class={resolvedClasses().list}
166
+ data-slot="list"
167
+ role="tablist"
168
+ >
169
+ <div
170
+ ref={(el) => (indicatorRef = el)}
171
+ class={resolvedClasses().indicator}
172
+ data-slot="indicator"
173
+ style={{ display: "none" }}
174
+ />
175
+
176
+ <For each={renderedListItems()}>
177
+ {(item, index) => {
178
+ const itemVal = () => (item[valueKey()] ?? item.value ?? String(index())) as string
179
+ const itemLabel = () => item[labelKey()] ?? item.label
180
+ const active = () => itemVal() === currentValue()
181
+
182
+ return (
183
+ <button
184
+ type="button"
185
+ class={resolvedClasses().trigger}
186
+ data-slot="trigger"
187
+ role="tab"
188
+ data-value={itemVal()}
189
+ data-state={active() ? "active" : "inactive"}
190
+ disabled={item.disabled}
191
+ onClick={() => handleSelect(itemVal())}
192
+ >
193
+ {item.icon && (
194
+ <RLSIcon
195
+ name={item.icon}
196
+ class={resolvedClasses().leadingIcon}
197
+ data-slot="leadingIcon"
198
+ />
199
+ )}
200
+ {itemLabel() && (
201
+ <span class={resolvedClasses().label} data-slot="label">
202
+ {itemLabel()}
203
+ </span>
204
+ )}
205
+ {item.badge !== undefined && item.badge !== null && (
206
+ <RLSBadge
207
+ size="sm"
208
+ variant="soft"
209
+ color="neutral"
210
+ class={resolvedClasses().trailingBadge}
211
+ data-slot="trailingBadge"
212
+ >
213
+ {typeof item.badge === "object" ? item.badge.label : item.badge}
214
+ </RLSBadge>
215
+ )}
216
+ </button>
217
+ )
218
+ }}
219
+ </For>
220
+
221
+ <Show when={needsOverflow() && overflowItems().length > 0}>
222
+ <div class="relative inline-flex items-center ml-auto">
223
+ <button
224
+ type="button"
225
+ class={`${resolvedClasses().trigger} rla-tb-overflow-btn`}
226
+ data-slot="overflow-trigger"
227
+ aria-label="More tabs"
228
+ onClick={() => setPopoverOpen(!popoverOpen())}
229
+ >
230
+ <span class="rla-tb-label">+{overflowItems().length} more</span>
231
+ <RLSIcon name="chevronDown" class="ml-1 size-3.5 opacity-70" />
232
+ </button>
233
+
234
+ <Show when={popoverOpen()}>
235
+ <div
236
+ class="absolute right-0 top-full mt-1 z-50 rounded-lg border border-default bg-default p-1 shadow-lg backdrop-blur-sm min-w-[10rem] flex flex-col gap-0.5"
237
+ onClick={() => setPopoverOpen(false)}
238
+ >
239
+ <For each={overflowItems()}>
240
+ {(item, index) => {
241
+ const itemVal = () =>
242
+ (item[valueKey()] ?? item.value ?? String(effectiveMax() + index())) as string
243
+ const itemLabel = () => item[labelKey()] ?? item.label
244
+ const active = () => itemVal() === currentValue()
245
+
246
+ return (
247
+ <button
248
+ type="button"
249
+ class={`w-full text-left px-3 py-1.5 text-sm rounded-md transition-colors flex items-center justify-between hover:bg-muted focus-visible:outline-none cursor-pointer ${
250
+ active() ? "bg-muted/80 font-medium" : ""
251
+ }`}
252
+ onClick={() => handleSelect(itemVal())}
253
+ >
254
+ <span class="truncate">{itemLabel()}</span>
255
+ {active() && <RLSIcon name="check" class="size-4 opacity-100" />}
256
+ </button>
257
+ )
258
+ }}
259
+ </For>
260
+ </div>
261
+ </Show>
262
+ </div>
263
+ </Show>
264
+ </div>
265
+
266
+ <Show when={showContent()}>
267
+ <div class="flex-1">
268
+ {props.children ? (
269
+ props.children
270
+ ) : (
271
+ <For each={items()}>
272
+ {(item, index) => {
273
+ const itemVal = () => (item[valueKey()] ?? item.value ?? String(index())) as string
274
+ const active = () => itemVal() === currentValue()
275
+
276
+ return (
277
+ <Show when={props.unmountOnHide === false || active()}>
278
+ <div
279
+ class={resolvedClasses().content}
280
+ data-slot="content"
281
+ role="tabpanel"
282
+ data-value={itemVal()}
283
+ style={{ display: active() ? undefined : "none" }}
284
+ >
285
+ {item.content}
286
+ </div>
287
+ </Show>
288
+ )
289
+ }}
290
+ </For>
291
+ )}
292
+ </div>
293
+ </Show>
294
+ </div>
295
+ )
296
+ }
297
+
298
+ export default RLSTabs
@@ -34,6 +34,7 @@ const classes = textareaTheme({
34
34
  -->
35
35
  <textarea
36
36
  class={classes.textarea}
37
+ data-slot="textarea"
37
38
  placeholder={placeholder}
38
39
  disabled={disabled}
39
40
  readonly={readonly}
@@ -20,6 +20,7 @@ const RLSTextarea: Component<RLSTextareaProps> = (allProps) => {
20
20
  "name",
21
21
  "required",
22
22
  "rows",
23
+ "autoGrow",
23
24
  "error",
24
25
  "size",
25
26
  "color",
@@ -47,7 +48,8 @@ const RLSTextarea: Component<RLSTextareaProps> = (allProps) => {
47
48
  <textarea
48
49
  ref={props.ref}
49
50
  name={props.name}
50
- rows={props.rows ?? 3}
51
+ rows={props.autoGrow ? undefined : (props.rows ?? 4)}
52
+ style={props.autoGrow ? "field-sizing: content; min-height: unset;" : undefined}
51
53
  value={props.value !== undefined ? props.value : ""}
52
54
  placeholder={props.placeholder}
53
55
  disabled={props.disabled}
@@ -1,95 +1,124 @@
1
1
  ---
2
2
  import type { ThemeSelectorProps } from "./theme-selector"
3
3
  import { themeSelectorTheme } from "./theme-selector.theme"
4
- import RLAButton from "../button/RLAButton.astro"
4
+ import RLASelect from "../select/RLASelect.astro"
5
5
  import RLAIcon from "../icon/RLAIcon.astro"
6
6
 
7
-
8
-
9
7
  const {
8
+ mode = "select",
10
9
  size = "md",
10
+ theme,
11
+ class: classAttr,
11
12
  ...rest
12
13
  } = Astro.props as ThemeSelectorProps
13
14
 
14
15
  const classes = themeSelectorTheme(Astro.props);
16
+
17
+ const items = [
18
+ { label: "System", value: "system", icon: "i-lucide-laptop" },
19
+ { label: "Light", value: "light", icon: "i-rl-sun" },
20
+ { label: "Dark", value: "dark", icon: "i-rl-moon" }
21
+ ];
15
22
  ---
16
23
 
17
- <rla-theme-selector {...rest}>
18
- <RLAButton
19
- size={size}
20
- class:list={["theme-toggle-button", classes.root]}
21
- variant="ghost"
22
- square
23
- aria-label="Toggle theme"
24
- >
25
- <RLAIcon name="i-rl-sun" class={classes.sun} />
26
- <RLAIcon name="i-rl-moon" class={classes.moon} />
27
- </RLAButton>
24
+ <rla-theme-selector data-mode={mode} data-theme={theme} {...rest}>
25
+ <div class:list={[classes.root, classAttr]} data-theme-selector>
26
+ <div class:list={[classes.wrapper]}>
27
+ <RLASelect
28
+ items={items}
29
+ defaultValue="system"
30
+ size={size}
31
+ theme={theme}
32
+ variant={mode === "icon" ? "icon" : "default"}
33
+ aria-label="Toggle theme"
34
+ class={classes.select}
35
+ >
36
+ <span slot="leading" class="flex items-center">
37
+ <RLAIcon name="i-lucide-laptop" class="rla-theme-icon-system size-4" style="display: block;" />
38
+ <RLAIcon name="i-rl-sun" class="rla-theme-icon-light size-4" style="display: none;" />
39
+ <RLAIcon name="i-rl-moon" class="rla-theme-icon-dark size-4" style="display: none;" />
40
+ </span>
41
+ </RLASelect>
42
+ </div>
43
+ </div>
28
44
  </rla-theme-selector>
29
45
 
30
- <style is:global>
31
- .rla-theme-sun { display: none; }
32
- [data-theme="dark"] .rla-theme-sun { display: block; }
33
- .rla-theme-moon { display: block; }
34
- [data-theme="dark"] .rla-theme-moon { display: none; }
35
- </style>
36
-
37
46
  <script>
38
47
  import { defineShortcuts } from "../../shortcuts"
39
48
 
40
- function applyStoredTheme() {
41
- const stored = localStorage.getItem('theme');
42
- if (stored === 'dark' || stored === 'light') {
43
- document.documentElement.setAttribute('data-theme', stored);
44
- }
45
- }
49
+ class RLAThemeSelector extends HTMLElement {
50
+ private cleanup: (() => void) | null = null;
51
+ private select: HTMLSelectElement | null = null;
46
52
 
47
- applyStoredTheme();
53
+ private getPreference(): string {
54
+ const stored = localStorage.getItem('theme');
55
+ return stored === 'system' || stored === 'light' || stored === 'dark' ? stored : 'system';
56
+ }
48
57
 
49
- function onToggle() {
50
- const willBeDark = document.documentElement.getAttribute('data-theme') !== 'dark';
51
- document.documentElement.setAttribute('data-theme', willBeDark ? 'dark' : 'light');
52
- localStorage.setItem('theme', willBeDark ? 'dark' : 'light');
58
+ connectedCallback() {
59
+ this.style.display = 'contents';
60
+ const pref = this.getPreference();
53
61
 
54
- document.querySelectorAll('.theme-toggle-button').forEach(btn => {
55
- btn.setAttribute('data-state', willBeDark ? 'on' : 'off');
56
- btn.setAttribute('aria-pressed', willBeDark.toString());
57
- });
58
- }
62
+ const select = this.querySelector('select');
63
+ this.select = select;
64
+ if (select) {
65
+ select.value = pref;
66
+ select.dispatchEvent(new Event('change', { bubbles: true }));
67
+ }
59
68
 
60
- class RLAThemeSelector extends HTMLElement {
61
- private buttonCleanup: (() => void) | null = null;
69
+ const updateTheme = (next: string) => {
70
+ this.setAttribute('data-theme-preference', next);
71
+
72
+ const sysIcons = this.querySelectorAll('.rla-theme-icon-system');
73
+ const lightIcons = this.querySelectorAll('.rla-theme-icon-light');
74
+ const darkIcons = this.querySelectorAll('.rla-theme-icon-dark');
75
+ sysIcons.forEach(el => ((el as HTMLElement).style.display = next === 'system' ? 'block' : 'none'));
76
+ lightIcons.forEach(el => ((el as HTMLElement).style.display = next === 'light' ? 'block' : 'none'));
77
+ darkIcons.forEach(el => ((el as HTMLElement).style.display = next === 'dark' ? 'block' : 'none'));
78
+
79
+ const resolved = next === 'system'
80
+ ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
81
+ : next;
82
+ document.documentElement.setAttribute('data-theme', resolved);
83
+ localStorage.setItem('theme', next);
84
+ };
62
85
 
63
- connectedCallback() {
64
- this.style.display = 'contents';
65
- const button = this.querySelector('.theme-toggle-button');
66
- if (!button) return;
86
+ updateTheme(pref);
67
87
 
68
- const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
69
- button.setAttribute('data-state', isDark ? 'on' : 'off');
70
- button.setAttribute('aria-pressed', isDark.toString());
88
+ const onChange = (event: Event) => {
89
+ const target = event.target as HTMLSelectElement | null;
90
+ if (target && target.tagName === 'SELECT' && target.value) {
91
+ updateTheme(target.value);
92
+ }
93
+ };
94
+ this.addEventListener('change', onChange);
71
95
 
72
- button.addEventListener('click', onToggle);
96
+ const cycle = () => {
97
+ const current = localStorage.getItem('theme') || 'system';
98
+ const next = current === 'system' ? 'light' : current === 'light' ? 'dark' : 'system';
99
+ if (this.select) this.select.value = next;
100
+ updateTheme(next);
101
+ };
73
102
 
74
103
  const cleanupShortcut = defineShortcuts({
75
104
  meta_alt_t: {
76
- handler: onToggle,
77
- label: "Toggle Theme",
78
- description: "Switch between light and dark theme mode",
105
+ handler: cycle,
106
+ label: "Cycle Theme",
107
+ description: "Cycle between system, light, and dark theme mode",
79
108
  category: "system"
80
109
  }
81
110
  });
82
111
 
83
- this.buttonCleanup = () => {
84
- button.removeEventListener('click', onToggle);
112
+ this.cleanup = () => {
113
+ this.removeEventListener('change', onChange);
85
114
  cleanupShortcut();
86
115
  };
87
116
  }
88
117
 
89
118
  disconnectedCallback() {
90
- if (this.buttonCleanup) {
91
- this.buttonCleanup();
92
- this.buttonCleanup = null;
119
+ if (this.cleanup) {
120
+ this.cleanup();
121
+ this.cleanup = null;
93
122
  }
94
123
  }
95
124
  }
@@ -97,4 +126,4 @@ const classes = themeSelectorTheme(Astro.props);
97
126
  if (!customElements.get('rla-theme-selector')) {
98
127
  customElements.define('rla-theme-selector', RLAThemeSelector);
99
128
  }
100
- </script>
129
+ </script>