@rimelight/ui 0.0.47 → 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 (197) hide show
  1. package/dist/index.d.mts +4 -5
  2. package/dist/preset.mjs +73 -1
  3. package/dist/resolver.d.mts +3 -4
  4. package/dist/resolver.mjs +2 -4
  5. package/dist/shortcuts.d.mts +10 -11
  6. package/dist/stores.d.mts +3 -4
  7. package/package.json +8 -6
  8. package/src/components/3d-hover/RLS3dHover.tsx +21 -0
  9. package/src/components/accordion/RLSAccordion.tsx +112 -0
  10. package/src/components/app/RLSApp.tsx +21 -0
  11. package/src/components/aspect-ratio/RLSAspectRatio.tsx +30 -0
  12. package/src/components/audio/RLSAudio.tsx +27 -0
  13. package/src/components/avatar/RLSAvatar.tsx +142 -0
  14. package/src/components/avatar/avatar.ts +22 -21
  15. package/src/components/avatar-group/RLSAvatarGroup.tsx +163 -0
  16. package/src/components/badge/RLSBadge.tsx +1 -1
  17. package/src/components/badge/RLVBadge.vue +5 -3
  18. package/src/components/banner/RLSBanner.tsx +151 -0
  19. package/src/components/breadcrumb/RLSBreadcrumb.tsx +194 -0
  20. package/src/components/browser-mockup/RLSBrowserMockup.tsx +21 -0
  21. package/src/components/button/RLSButton.tsx +49 -40
  22. package/src/components/button/RLVButton.vue +44 -32
  23. package/src/components/calendar/RLSCalendar.tsx +26 -0
  24. package/src/components/callout/RLSCallout.tsx +151 -0
  25. package/src/components/card/RLSCard.tsx +4 -0
  26. package/src/components/card/RLVCard.vue +4 -0
  27. package/src/components/card/card.ts +10 -0
  28. package/src/components/carousel/RLACarousel.astro +14 -1
  29. package/src/components/carousel/RLSCarousel.tsx +260 -0
  30. package/src/components/chart/RLSChart.tsx +299 -0
  31. package/src/components/chat/RLSChat.tsx +27 -0
  32. package/src/components/chat-message/RLSChatMessage.tsx +27 -0
  33. package/src/components/chat-messages/RLSChatMessages.tsx +27 -0
  34. package/src/components/chat-palette/RLSChatPalette.tsx +27 -0
  35. package/src/components/chat-prompt/RLSChatPrompt.tsx +27 -0
  36. package/src/components/chat-prompt-submit/RLSChatPromptSubmit.tsx +27 -0
  37. package/src/components/chat-reasoning/RLSChatReasoning.tsx +27 -0
  38. package/src/components/chat-shimmer/RLSChatShimmer.tsx +27 -0
  39. package/src/components/chat-tool/RLSChatTool.tsx +27 -0
  40. package/src/components/checkbox/RLACheckbox.astro +1 -0
  41. package/src/components/checkbox/RLSCheckbox.tsx +1 -1
  42. package/src/components/checkbox/checkbox.theme.ts +2 -2
  43. package/src/components/chip/RLSChip.tsx +65 -0
  44. package/src/components/collapsible/RLSCollapsible.tsx +89 -0
  45. package/src/components/color-area/RLSColorArea.tsx +554 -0
  46. package/src/components/color-field/RLSColorField.tsx +236 -0
  47. package/src/components/color-picker/RLSColorPicker.tsx +26 -0
  48. package/src/components/color-slider/RLSColorSlider.tsx +373 -0
  49. package/src/components/command-palette/RLSCommandPalette.tsx +26 -0
  50. package/src/components/compare/RLSCompare.tsx +26 -0
  51. package/src/components/confirm/RLSConfirm.tsx +173 -0
  52. package/src/components/container/RLSContainer.tsx +24 -0
  53. package/src/components/context-menu/RLSContextMenu.tsx +26 -0
  54. package/src/components/copy-markdown/RLSCopyMarkdown.tsx +57 -0
  55. package/src/components/dashboard-group/RLSDashboardGroup.tsx +29 -0
  56. package/src/components/dashboard-navbar/RLSDashboardNavbar.tsx +116 -0
  57. package/src/components/dashboard-panel/RLSDashboardPanel.tsx +84 -0
  58. package/src/components/dashboard-resize-handle/RLSDashboardResizeHandle.tsx +35 -0
  59. package/src/components/dashboard-search/RLADashboardSearch.astro +1 -1
  60. package/src/components/dashboard-search/RLSDashboardSearch.tsx +125 -0
  61. package/src/components/dashboard-search-button/RLSDashboardSearchButton.tsx +82 -0
  62. package/src/components/dashboard-sidebar/RLSDashboardSidebar.tsx +184 -0
  63. package/src/components/dashboard-sidebar-collapse/RLSDashboardSidebarCollapse.tsx +46 -0
  64. package/src/components/dashboard-sidebar-toggle/RLSDashboardSidebarToggle.tsx +35 -0
  65. package/src/components/dashboard-toolbar/RLSDashboardToolbar.tsx +49 -0
  66. package/src/components/date/RLSDate.tsx +34 -0
  67. package/src/components/dock/RLSDock.tsx +26 -0
  68. package/src/components/drawer/RLSDrawer.tsx +177 -0
  69. package/src/components/dropdown-menu/RLADropdownMenu.astro +250 -241
  70. package/src/components/dropdown-menu/RLSDropdownMenu.tsx +183 -86
  71. package/src/components/editor/RLSEditor.tsx +26 -0
  72. package/src/components/empty/RLSEmpty.tsx +132 -0
  73. package/src/components/error/RLSError.tsx +26 -0
  74. package/src/components/field-group/RLSFieldGroup.tsx +46 -0
  75. package/src/components/fieldset/RLSFieldset.tsx +26 -0
  76. package/src/components/file-upload/RLSFileUpload.tsx +163 -0
  77. package/src/components/floating-action/RLSFloatingAction.tsx +26 -0
  78. package/src/components/footer/RLSFooter.tsx +56 -0
  79. package/src/components/form/RLSForm.tsx +34 -0
  80. package/src/components/gallery/RLSGallery.tsx +21 -0
  81. package/src/components/gamepad/RLSGamepad.tsx +269 -0
  82. package/src/components/grid/RLSGrid.tsx +29 -0
  83. package/src/components/head/RLAHead.astro +9 -0
  84. package/src/components/head/RLSHead.tsx +42 -0
  85. package/src/components/head/UIHead.astro +3 -0
  86. package/src/components/header/RLSHeader.tsx +87 -0
  87. package/src/components/hover-card/RLSHoverCard.tsx +72 -0
  88. package/src/components/icon/RLSIcon.tsx +3 -1
  89. package/src/components/image/RLAImage.astro +447 -138
  90. package/src/components/image/RLSImage.tsx +545 -0
  91. package/src/components/image/image.theme.ts +19 -3
  92. package/src/components/image/image.ts +31 -1
  93. package/src/components/input/RLAInput.astro +62 -51
  94. package/src/components/input/RLSInput.tsx +1 -1
  95. package/src/components/input-date/RLSInputDate.tsx +92 -0
  96. package/src/components/input-menu/RLSInputMenu.tsx +199 -0
  97. package/src/components/input-number/RLSInputNumber.tsx +184 -0
  98. package/src/components/input-pin/RLSInputPin.tsx +185 -0
  99. package/src/components/input-rating/RLSInputRating.tsx +198 -0
  100. package/src/components/input-rating/input-rating.ts +4 -0
  101. package/src/components/input-tags/RLSInputTags.tsx +94 -0
  102. package/src/components/input-time/RLSInputTime.tsx +92 -0
  103. package/src/components/kbd/RLSKbd.tsx +37 -0
  104. package/src/components/keyboard/RLSKeyboard.tsx +476 -0
  105. package/src/components/knob/RLSKnob.tsx +27 -0
  106. package/src/components/label/RLSLabel.tsx +27 -0
  107. package/src/components/layout-grid/RLSLayoutGrid.tsx +402 -0
  108. package/src/components/layout-grid/layout-grid.ts +25 -1
  109. package/src/components/link/RLSLink.tsx +2 -1
  110. package/src/components/link/link.ts +21 -12
  111. package/src/components/link-group/RLSLinkGroup.tsx +76 -0
  112. package/src/components/listbox/RLSListbox.tsx +27 -0
  113. package/src/components/locale-selector/RLALocaleSelector.astro +2 -1
  114. package/src/components/locale-selector/RLSLocaleSelector.tsx +178 -0
  115. package/src/components/logo/RLALogo.astro +69 -4
  116. package/src/components/logo/RLSLogo.tsx +129 -0
  117. package/src/components/main/RLSMain.tsx +40 -0
  118. package/src/components/marquee/RLSMarquee.tsx +80 -0
  119. package/src/components/meter/RLSMeter.tsx +27 -0
  120. package/src/components/modal/RLAModal.astro +1 -1
  121. package/src/components/modal/RLSModal.tsx +109 -72
  122. package/src/components/navigation-menu/RLSNavigationMenu.tsx +228 -0
  123. package/src/components/navigation-menu/navigation-menu.ts +2 -2
  124. package/src/components/package-managers/RLSPackageManagers.tsx +204 -0
  125. package/src/components/page/RLSPage.tsx +21 -0
  126. package/src/components/page-aside/RLSPageAside.tsx +21 -0
  127. package/src/components/page-body/RLSPageBody.tsx +21 -0
  128. package/src/components/page-header/RLSPageHeader.tsx +21 -0
  129. package/src/components/page-section/RLSPageSection.tsx +201 -0
  130. package/src/components/pagination/RLSPagination.tsx +114 -0
  131. package/src/components/password/RLSPassword.tsx +27 -0
  132. package/src/components/phone-mockup/RLSPhoneMockup.tsx +27 -0
  133. package/src/components/placeholder/RLSPlaceholder.tsx +47 -0
  134. package/src/components/popover/RLSPopover.tsx +124 -0
  135. package/src/components/post/RLSPost.tsx +134 -0
  136. package/src/components/pricing-plan/RLSPricingPlan.tsx +26 -0
  137. package/src/components/pricing-table/RLSPricingTable.tsx +26 -0
  138. package/src/components/progress/RLSProgress.tsx +53 -0
  139. package/src/components/prose/RLSProse.tsx +26 -0
  140. package/src/components/qr-code/RLSQrCode.tsx +26 -0
  141. package/src/components/quote/RLAQuote.astro +10 -6
  142. package/src/components/quote/RLSQuote.tsx +37 -0
  143. package/src/components/quote/quote.theme.ts +5 -2
  144. package/src/components/radio-group/RLSRadioGroup.tsx +37 -0
  145. package/src/components/rating/RLSRating.tsx +27 -0
  146. package/src/components/scroll-area/RLSScrollArea.tsx +210 -0
  147. package/src/components/scroll-to-top/RLAScrollToTop.astro +1 -0
  148. package/src/components/scroll-to-top/RLSScrollToTop.tsx +145 -0
  149. package/src/components/search/RLSSearch.tsx +237 -0
  150. package/src/components/select/RLASelect.astro +67 -4
  151. package/src/components/select/RLSSelect.tsx +107 -39
  152. package/src/components/select/RLVSelect.vue +50 -1
  153. package/src/components/select/select.theme.ts +30 -10
  154. package/src/components/select/select.ts +20 -1
  155. package/src/components/separator/RLSSeparator.tsx +92 -0
  156. package/src/components/share/RLSShare.tsx +66 -0
  157. package/src/components/shortcuts/RLSShortcuts.tsx +393 -0
  158. package/src/components/sidebar/RLSSidebar.tsx +271 -0
  159. package/src/components/skeleton/RLSSkeleton.tsx +21 -0
  160. package/src/components/slideover/RLASlideover.astro +1 -1
  161. package/src/components/slideover/RLSSlideover.tsx +159 -0
  162. package/src/components/slider/RLSSlider.tsx +112 -0
  163. package/src/components/speed-dial/RLSSpeedDial.tsx +27 -0
  164. package/src/components/spinner/RLSSpinner.tsx +22 -0
  165. package/src/components/splitter/RLSSplitter.tsx +178 -0
  166. package/src/components/spoiler/RLSSpoiler.tsx +26 -0
  167. package/src/components/stepper/RLSStepper.tsx +205 -0
  168. package/src/components/steps/RLSSteps.tsx +108 -0
  169. package/src/components/sticky-surface/RLSStickySurface.tsx +63 -0
  170. package/src/components/surround/RLSSurround.tsx +57 -0
  171. package/src/components/switch/RLSSwitch.tsx +56 -0
  172. package/src/components/table/RLATable.astro +1044 -579
  173. package/src/components/table/RLSTable.tsx +893 -0
  174. package/src/components/table/table.theme.ts +47 -16
  175. package/src/components/table/table.ts +104 -66
  176. package/src/components/table-of-contents/RLSTableOfContents.tsx +196 -0
  177. package/src/components/tabs/RLSTabs.tsx +298 -0
  178. package/src/components/textarea/RLATextarea.astro +1 -0
  179. package/src/components/textarea/RLSTextarea.tsx +3 -1
  180. package/src/components/theme-selector/RLAThemeSelector.astro +85 -56
  181. package/src/components/theme-selector/RLSThemeSelector.tsx +131 -0
  182. package/src/components/theme-selector/theme-selector.theme.ts +5 -7
  183. package/src/components/theme-selector/theme-selector.ts +11 -1
  184. package/src/components/timeline/RLSTimeline.tsx +171 -0
  185. package/src/components/toast/RLSToast.tsx +179 -0
  186. package/src/components/toaster/RLSToaster.tsx +82 -0
  187. package/src/components/tooltip/RLSTooltip.tsx +48 -0
  188. package/src/components/tour/RLSTour.tsx +26 -0
  189. package/src/components/tree/RLSTree.tsx +26 -0
  190. package/src/components/user/RLSUser.tsx +26 -0
  191. package/src/components/video/RLAVideo.astro +4 -1
  192. package/src/components/video/RLSVideo.tsx +158 -0
  193. package/src/components/watermark/RLSWatermark.tsx +27 -0
  194. package/src/components/window-mockup/RLSWindowMockup.tsx +27 -0
  195. package/src/preset.ts +73 -1
  196. package/src/resolver.ts +6 -1
  197. package/src/virtual.d.ts +6 -0
@@ -0,0 +1,260 @@
1
+ import {
2
+ children,
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 { CarouselProps, CarouselSlide } from "./carousel"
14
+ import { carouselTheme } from "./carousel.theme"
15
+ import RLSIcon from "../icon/RLSIcon"
16
+
17
+ export interface RLSCarouselProps extends CarouselProps {
18
+ children?: JSX.Element
19
+ }
20
+
21
+ const SLIDE_TRANSITION = "transform 500ms cubic-bezier(0.16, 1, 0.3, 1)"
22
+
23
+ const RLSCarousel: Component<RLSCarouselProps> = (allProps) => {
24
+ const [props, rest] = splitProps(allProps, ["slides", "autoplay", "ui", "class", "children"])
25
+
26
+ const [index, setIndex] = createSignal(0)
27
+ const [dragOffset, setDragOffset] = createSignal(0)
28
+ const [isDragging, setIsDragging] = createSignal(false)
29
+
30
+ const childrenContent = children(() => props.children)
31
+
32
+ const slideCount = createMemo(() => {
33
+ if (props.slides && props.slides.length > 0) return props.slides.length
34
+ return childrenContent.toArray().length
35
+ })
36
+
37
+ let viewportRef: HTMLDivElement | undefined
38
+ let containerRef: HTMLDivElement | undefined
39
+ let startX = 0
40
+ let startY = 0
41
+ let hasDragged = false
42
+ let intervalId: ReturnType<typeof setInterval> | undefined
43
+ let lastBtnClickTime = 0
44
+
45
+ const resolvedClasses = createMemo(() =>
46
+ carouselTheme({
47
+ active: false,
48
+ ui: props.ui,
49
+ class: props.class
50
+ })
51
+ )
52
+
53
+ const dotActiveClasses = createMemo(() =>
54
+ carouselTheme({
55
+ active: true,
56
+ ui: props.ui,
57
+ class: props.class
58
+ })
59
+ )
60
+
61
+ const carouselId = `rla-carousel-${Math.random().toString(36).substring(2, 9)}`
62
+ const dots = createMemo(() => Array.from({ length: slideCount() }, (_, i) => i))
63
+
64
+ const goTo = (n: number) => {
65
+ const count = slideCount()
66
+ if (count === 0) return
67
+ setIndex(((n % count) + count) % count)
68
+ }
69
+
70
+ const next = () => goTo(index() + 1)
71
+ const prev = () => goTo(index() - 1)
72
+
73
+ const startAutoplay = () => {
74
+ if (props.autoplay && !intervalId) {
75
+ intervalId = setInterval(() => goTo(index() + 1), 5000)
76
+ }
77
+ }
78
+
79
+ const stopAutoplay = () => {
80
+ if (intervalId) {
81
+ clearInterval(intervalId)
82
+ intervalId = undefined
83
+ }
84
+ }
85
+
86
+ onMount(() => {
87
+ startAutoplay()
88
+ onCleanup(stopAutoplay)
89
+ })
90
+
91
+ const handleBtnClick = (action: () => void) => {
92
+ const now = Date.now()
93
+ if (now - lastBtnClickTime < 500) return
94
+ lastBtnClickTime = now
95
+ stopAutoplay()
96
+ action()
97
+ startAutoplay()
98
+ }
99
+
100
+ const handlePointerDown = (e: PointerEvent) => {
101
+ if (!viewportRef) return
102
+ stopAutoplay()
103
+ startX = e.clientX
104
+ startY = e.clientY
105
+ hasDragged = false
106
+ setIsDragging(true)
107
+ try {
108
+ viewportRef.setPointerCapture(e.pointerId)
109
+ } catch {}
110
+ }
111
+
112
+ const handlePointerMove = (e: PointerEvent) => {
113
+ if (!isDragging() || !viewportRef || !containerRef) return
114
+ const deltaX = e.clientX - startX
115
+ const deltaY = e.clientY - startY
116
+
117
+ if (!hasDragged) {
118
+ if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 5) {
119
+ setIsDragging(false)
120
+ try {
121
+ viewportRef.releasePointerCapture(e.pointerId)
122
+ } catch {}
123
+ return
124
+ }
125
+ if (Math.abs(deltaX) > 5) {
126
+ hasDragged = true
127
+ }
128
+ }
129
+
130
+ if (hasDragged) {
131
+ e.preventDefault()
132
+ const width = viewportRef.clientWidth
133
+ setDragOffset((deltaX / width) * 100)
134
+ }
135
+ }
136
+
137
+ const handlePointerUp = (e: PointerEvent) => {
138
+ if (!isDragging() || !viewportRef) return
139
+ setIsDragging(false)
140
+ setDragOffset(0)
141
+ try {
142
+ viewportRef.releasePointerCapture(e.pointerId)
143
+ } catch {}
144
+
145
+ if (hasDragged) {
146
+ const deltaX = e.clientX - startX
147
+ const width = viewportRef.clientWidth
148
+ const threshold = width * 0.15
149
+ if (deltaX < -threshold) {
150
+ next()
151
+ } else if (deltaX > threshold) {
152
+ prev()
153
+ }
154
+ }
155
+ hasDragged = false
156
+ startAutoplay()
157
+ }
158
+
159
+ const renderSlide = (slide: CarouselSlide) => (
160
+ <div
161
+ class={`${resolvedClasses().slide} rla-carousel-slide`}
162
+ data-slot="slide"
163
+ style="container-type: scroll-state; container-name: rla-slide;"
164
+ >
165
+ <div class="relative w-full aspect-video">
166
+ {slide.imageSrc ? (
167
+ <img
168
+ src={slide.imageSrc}
169
+ alt={slide.title || ""}
170
+ class="w-full h-full object-cover rounded-lg"
171
+ draggable="false"
172
+ />
173
+ ) : null}
174
+ {slide.title || slide.description ? (
175
+ <div class="absolute bottom-4 left-4 right-4 p-4 rounded-lg bg-black/60 backdrop-blur-sm text-white">
176
+ {slide.title ? <h4 class="text-sm font-semibold">{slide.title}</h4> : null}
177
+ {slide.description ? <p class="text-xs opacity-95 mt-1">{slide.description}</p> : null}
178
+ </div>
179
+ ) : null}
180
+ </div>
181
+ </div>
182
+ )
183
+
184
+ return (
185
+ <div data-autoplay={props.autoplay ? "true" : "false"} style="display: contents" {...rest}>
186
+ <div id={carouselId} class={resolvedClasses().root} data-slot="root">
187
+ <div
188
+ ref={(el) => (viewportRef = el)}
189
+ class={resolvedClasses().viewport}
190
+ data-slot="viewport"
191
+ onPointerDown={handlePointerDown}
192
+ onPointerMove={handlePointerMove}
193
+ onPointerUp={handlePointerUp}
194
+ onPointerCancel={handlePointerUp}
195
+ >
196
+ <div
197
+ ref={(el) => (containerRef = el)}
198
+ class={resolvedClasses().container}
199
+ data-slot="container"
200
+ style={{
201
+ transform: `translate3d(${-index() * 100 + dragOffset()}%, 0px, 0px)`,
202
+ transition: isDragging() && hasDragged ? "none" : SLIDE_TRANSITION
203
+ }}
204
+ >
205
+ {props.slides && props.slides.length > 0 ? (
206
+ <For each={props.slides}>{(slide) => renderSlide(slide)}</For>
207
+ ) : (
208
+ childrenContent.toArray().map((child) => (
209
+ <div
210
+ class={`${resolvedClasses().slide} rla-carousel-slide`}
211
+ data-slot="slide"
212
+ style="container-type: scroll-state; container-name: rla-slide;"
213
+ >
214
+ {child}
215
+ </div>
216
+ ))
217
+ )}
218
+ </div>
219
+ </div>
220
+
221
+ <button
222
+ class={resolvedClasses().buttonPrev}
223
+ data-slot="prev-button"
224
+ type="button"
225
+ aria-label="Previous slide"
226
+ onClick={() => handleBtnClick(prev)}
227
+ >
228
+ <RLSIcon name="i-rl-chevronLeft" class="size-5" aria-hidden="true" />
229
+ </button>
230
+ <button
231
+ class={resolvedClasses().buttonNext}
232
+ data-slot="next-button"
233
+ type="button"
234
+ aria-label="Next slide"
235
+ onClick={() => handleBtnClick(next)}
236
+ >
237
+ <RLSIcon name="i-rl-chevronRight" class="size-5" aria-hidden="true" />
238
+ </button>
239
+
240
+ <Show when={dots().length > 0}>
241
+ <div class={resolvedClasses().dots} data-slot="dots">
242
+ <For each={dots()}>
243
+ {(i) => (
244
+ <button
245
+ type="button"
246
+ class={index() === i ? dotActiveClasses().dot : resolvedClasses().dot}
247
+ data-slot="dot"
248
+ aria-label={`Go to slide ${i + 1}`}
249
+ onClick={() => handleBtnClick(() => goTo(i))}
250
+ />
251
+ )}
252
+ </For>
253
+ </div>
254
+ </Show>
255
+ </div>
256
+ </div>
257
+ )
258
+ }
259
+
260
+ export default RLSCarousel
@@ -0,0 +1,299 @@
1
+ import { createMemo, For, Show, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChartProps, ChartDataPoint } from "./chart"
3
+ import { chartTheme } from "./chart.theme"
4
+
5
+ export interface RLSChartProps extends ChartProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const palette = [
10
+ "hsl(221 83% 53%)",
11
+ "hsl(262 83% 58%)",
12
+ "hsl(142 76% 36%)",
13
+ "hsl(32 95% 44%)",
14
+ "hsl(0 84% 60%)",
15
+ "hsl(187 85% 43%)"
16
+ ]
17
+
18
+ const PIE_R = 80
19
+ const PIE_CX = 100
20
+ const PIE_CY = 100
21
+ const DONUT_INNER_R = 45
22
+ const LINE_W = 400
23
+ const LINE_H = 160
24
+ const LINE_PAD = 32
25
+
26
+ const polarToCartesian = (cx: number, cy: number, r: number, angleDeg: number) => {
27
+ const rad = (angleDeg - 90) * (Math.PI / 180)
28
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) }
29
+ }
30
+
31
+ const describeArc = (cx: number, cy: number, r: number, start: number, end: number) => {
32
+ const s = polarToCartesian(cx, cy, r, start)
33
+ const e = polarToCartesian(cx, cy, r, end)
34
+ const largeArc = end - start > 180 ? 1 : 0
35
+ return `M ${s.x} ${s.y} A ${r} ${r} 0 ${largeArc} 1 ${e.x} ${e.y}`
36
+ }
37
+
38
+ const RLSChart: Component<RLSChartProps> = (allProps) => {
39
+ const [props, rest] = splitProps(allProps, [
40
+ "type",
41
+ "data",
42
+ "title",
43
+ "height",
44
+ "color",
45
+ "ui",
46
+ "class",
47
+ "children"
48
+ ])
49
+
50
+ const data = createMemo(() => props.data ?? [])
51
+ const chartType = () => props.type ?? "bar"
52
+
53
+ const resolvedClasses = createMemo(() =>
54
+ chartTheme({
55
+ color: props.color ?? "primary",
56
+ ui: props.ui,
57
+ class: props.class
58
+ })
59
+ )
60
+
61
+ const max = createMemo(() => {
62
+ const items = data()
63
+ return items.length > 0 ? Math.max(...items.map((d) => d.value)) : 1
64
+ })
65
+
66
+ const total = createMemo(() => data().reduce((sum, d) => sum + d.value, 0))
67
+
68
+ const getColor = (item: ChartDataPoint, index: number) => {
69
+ const res = item.color ?? palette[index % palette.length]
70
+ return res ?? "#000000"
71
+ }
72
+
73
+ const pieSlices = createMemo(() => {
74
+ let cursor = 0
75
+ return data().map((item, i) => {
76
+ const angle = (item.value / (total() || 1)) * 360
77
+ const start = cursor
78
+ const end = cursor + angle
79
+ cursor = end
80
+ return { item, i, start, end }
81
+ })
82
+ })
83
+
84
+ const linePoints = createMemo(() =>
85
+ data().map((d, i) => {
86
+ const x =
87
+ data().length < 2
88
+ ? LINE_PAD
89
+ : LINE_PAD + (i / (data().length - 1)) * (LINE_W - LINE_PAD * 2)
90
+ const y = LINE_H - LINE_PAD - (d.value / (max() || 1)) * (LINE_H - LINE_PAD * 2)
91
+ return { x, y, d, i }
92
+ })
93
+ )
94
+
95
+ const linePath = createMemo(() =>
96
+ linePoints()
97
+ .map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`)
98
+ .join(" ")
99
+ )
100
+
101
+ const areaPath = createMemo(() => {
102
+ const pts = linePoints()
103
+ if (pts.length === 0) return ""
104
+ const last = pts[pts.length - 1]!
105
+ const first = pts[0]!
106
+ return linePath() + ` L ${last.x} ${LINE_H - LINE_PAD}` + ` L ${first.x} ${LINE_H - LINE_PAD} Z`
107
+ })
108
+
109
+ const gridLines = [0, 0.25, 0.5, 0.75, 1]
110
+
111
+ const id = `chart-${Math.random().toString(36).substring(2, 9)}`
112
+ const height = props.height ?? 280
113
+
114
+ return (
115
+ <>
116
+ <style>{`
117
+ .${id} { min-height: ${height}px; }
118
+ .${id} [data-slot="container"] { height: ${height - 60}px; gap: 6px; align-items: flex-end; }
119
+ .${id} .rla-chart-line-wrap { height: ${height - 60}px; }
120
+ .${id} .rla-chart-pie-wrap { height: ${height - 60}px; }
121
+ `}</style>
122
+
123
+ <div class={`${resolvedClasses().root} ${id}`} data-slot="root" {...rest}>
124
+ {props.title ? (
125
+ <div class={resolvedClasses().title} data-slot="title">
126
+ {props.title}
127
+ </div>
128
+ ) : null}
129
+
130
+ {chartType() === "bar" ? (
131
+ <div class={resolvedClasses().container} data-slot="container">
132
+ <For each={data()}>
133
+ {(item, i) => {
134
+ const pct = max() > 0 ? (item.value / max()) * 100 : 0
135
+ return (
136
+ <div class="flex flex-col items-center gap-1 flex-1 group">
137
+ <span class="text-[10px] text-muted font-mono opacity-0 group-hover:opacity-100 transition-opacity">
138
+ {item.value}
139
+ </span>
140
+ <div
141
+ class={`${resolvedClasses().bar} h-full w-full overflow-hidden !bg-transparent`}
142
+ title={`${item.label}: ${item.value}`}
143
+ >
144
+ <svg
145
+ class="h-full w-full overflow-visible"
146
+ viewBox="0 0 20 100"
147
+ preserveAspectRatio="none"
148
+ >
149
+ <rect
150
+ x="0"
151
+ y={100 - pct}
152
+ width="20"
153
+ height={pct}
154
+ fill={getColor(item, i())}
155
+ rx="2"
156
+ />
157
+ </svg>
158
+ </div>
159
+ <span class={resolvedClasses().axis}>{item.label}</span>
160
+ </div>
161
+ )
162
+ }}
163
+ </For>
164
+ </div>
165
+ ) : null}
166
+
167
+ {chartType() === "line" || chartType() === "area" ? (
168
+ <div class="relative w-full rla-chart-line-wrap">
169
+ <svg
170
+ class={resolvedClasses().svg}
171
+ viewBox={`0 0 ${LINE_W} ${LINE_H}`}
172
+ preserveAspectRatio="none"
173
+ aria-label={props.title ?? "Line chart"}
174
+ >
175
+ <For each={gridLines}>
176
+ {(t) => {
177
+ const y = LINE_PAD + t * (LINE_H - LINE_PAD * 2)
178
+ return (
179
+ <line
180
+ x1={LINE_PAD}
181
+ y1={y}
182
+ x2={LINE_W - LINE_PAD}
183
+ y2={y}
184
+ stroke="currentColor"
185
+ stroke-opacity={0.08}
186
+ />
187
+ )
188
+ }}
189
+ </For>
190
+
191
+ <Show when={chartType() === "area"}>
192
+ <path d={areaPath()} fill={palette[0] ?? "currentColor"} fill-opacity={0.15} />
193
+ </Show>
194
+
195
+ <path
196
+ d={linePath()}
197
+ class={resolvedClasses().line}
198
+ stroke={palette[0] ?? "currentColor"}
199
+ stroke-width={2.5}
200
+ />
201
+
202
+ <For each={linePoints()}>
203
+ {(p) => (
204
+ <circle
205
+ cx={p.x}
206
+ cy={p.y}
207
+ r={4}
208
+ fill={getColor(p.d, p.i)}
209
+ stroke="white"
210
+ stroke-width={1.5}
211
+ >
212
+ <title>
213
+ {p.d.label}: {p.d.value}
214
+ </title>
215
+ </circle>
216
+ )}
217
+ </For>
218
+
219
+ <For each={linePoints()}>
220
+ {(p) => (
221
+ <text x={p.x} y={LINE_H - 4} text-anchor="middle" class={resolvedClasses().axis}>
222
+ {p.d.label}
223
+ </text>
224
+ )}
225
+ </For>
226
+ </svg>
227
+ </div>
228
+ ) : null}
229
+
230
+ {chartType() === "pie" || chartType() === "donut" ? (
231
+ <div class="flex items-center gap-6 justify-center rla-chart-pie-wrap">
232
+ <svg
233
+ viewBox="0 0 200 200"
234
+ class="h-full w-auto max-h-48"
235
+ aria-label={props.title ?? "Pie chart"}
236
+ >
237
+ <For each={pieSlices()}>
238
+ {({ item, i, start, end }) => {
239
+ const isFullCircle = Math.abs(end - start - 360) < 0.01
240
+ const d = isFullCircle
241
+ ? `M ${PIE_CX} ${PIE_CY - PIE_R} A ${PIE_R} ${PIE_R} 0 1 1 ${PIE_CX - 0.001} ${PIE_CY - PIE_R} Z`
242
+ : describeArc(PIE_CX, PIE_CY, PIE_R, start, end) + ` L ${PIE_CX} ${PIE_CY} Z`
243
+ return (
244
+ <path
245
+ d={d}
246
+ fill={getColor(item, i)}
247
+ class="transition-opacity hover:opacity-85 cursor-pointer"
248
+ >
249
+ <title>
250
+ {item.label}: {item.value}
251
+ </title>
252
+ </path>
253
+ )
254
+ }}
255
+ </For>
256
+ <Show when={chartType() === "donut"}>
257
+ <circle cx={PIE_CX} cy={PIE_CY} r={DONUT_INNER_R} fill="var(--rl-neutral-50)" />
258
+ </Show>
259
+ </svg>
260
+
261
+ <div class="flex flex-col gap-1.5">
262
+ <For each={data()}>
263
+ {(item, i) => (
264
+ <div class="flex items-center gap-2 text-xs text-muted">
265
+ <svg class="size-3 shrink-0" viewBox="0 0 10 10">
266
+ <rect width="10" height="10" rx="2" fill={getColor(item, i())} />
267
+ </svg>
268
+ <span>{item.label}</span>
269
+ <span class="ml-1 font-mono text-highlighted">{item.value}</span>
270
+ </div>
271
+ )}
272
+ </For>
273
+ </div>
274
+ </div>
275
+ ) : null}
276
+
277
+ {props.children}
278
+
279
+ {(chartType() === "bar" || chartType() === "line" || chartType() === "area") &&
280
+ data().length > 0 ? (
281
+ <div class={resolvedClasses().legend} data-slot="legend">
282
+ <For each={data()}>
283
+ {(item, i) => (
284
+ <div class="flex items-center gap-1.5">
285
+ <svg class="size-2.5 shrink-0" viewBox="0 0 10 10">
286
+ <circle cx="5" cy="5" r="5" fill={getColor(item, i())} />
287
+ </svg>
288
+ <span>{item.label}</span>
289
+ </div>
290
+ )}
291
+ </For>
292
+ </div>
293
+ ) : null}
294
+ </div>
295
+ </>
296
+ )
297
+ }
298
+
299
+ export default RLSChart
@@ -0,0 +1,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChatProps } from "./chat"
3
+ import { chatTheme } from "./chat.theme"
4
+
5
+ export interface RLSChatProps extends ChatProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSChat: Component<RLSChatProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ chatTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSChat
@@ -0,0 +1,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChatMessageProps } from "./chat-message"
3
+ import { chatMessageTheme } from "./chat-message.theme"
4
+
5
+ export interface RLSChatMessageProps extends ChatMessageProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSChatMessage: Component<RLSChatMessageProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ chatMessageTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSChatMessage
@@ -0,0 +1,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChatMessagesProps } from "./chat-messages"
3
+ import { chatMessagesTheme } from "./chat-messages.theme"
4
+
5
+ export interface RLSChatMessagesProps extends ChatMessagesProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSChatMessages: Component<RLSChatMessagesProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ chatMessagesTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSChatMessages
@@ -0,0 +1,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChatPaletteProps } from "./chat-palette"
3
+ import { chatPaletteTheme } from "./chat-palette.theme"
4
+
5
+ export interface RLSChatPaletteProps extends ChatPaletteProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSChatPalette: Component<RLSChatPaletteProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ chatPaletteTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSChatPalette
@@ -0,0 +1,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ChatPromptProps } from "./chat-prompt"
3
+ import { chatPromptTheme } from "./chat-prompt.theme"
4
+
5
+ export interface RLSChatPromptProps extends ChatPromptProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSChatPrompt: Component<RLSChatPromptProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ chatPromptTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSChatPrompt