@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,393 @@
1
+ import uiConfig from "virtual:rimelight/ui"
2
+ import {
3
+ createEffect,
4
+ createMemo,
5
+ createSignal,
6
+ For,
7
+ onMount,
8
+ onCleanup,
9
+ Show,
10
+ splitProps,
11
+ type Component
12
+ } from "solid-js"
13
+ import { activeShortcutsStore, defineShortcuts, type RegisteredShortcut } from "../../shortcuts"
14
+ import RLSIcon from "../icon/RLSIcon"
15
+
16
+ export interface RLSShortcutsProps {
17
+ categories?: Record<string, string>
18
+ }
19
+
20
+ interface CategorySection {
21
+ key: string
22
+ label: string
23
+ items: RegisteredShortcut[]
24
+ }
25
+
26
+ const modalId = `rla-shortcuts-${Math.random().toString(36).substring(2, 9)}`
27
+
28
+ const RLSShortcuts: Component<RLSShortcutsProps> = (allProps) => {
29
+ const [props] = splitProps(allProps, ["categories"])
30
+
31
+ const configCategories = (): Record<string, { label: string }> =>
32
+ uiConfig.shortcuts?.categories || {}
33
+
34
+ const resolvedCategories = createMemo<Record<string, string>>(() => {
35
+ const configKeys = Object.keys(configCategories())
36
+ const allKeys = [...new Set([...configKeys, ...Object.keys(props.categories || {})])]
37
+ const resolved: Record<string, string> = {}
38
+ allKeys.forEach((key) => {
39
+ resolved[key] = props.categories?.[key] || configCategories()[key]?.label || key
40
+ })
41
+ return resolved
42
+ })
43
+
44
+ const categoryOrder = createMemo(() => Object.keys(configCategories()))
45
+
46
+ const [isOpen, setIsOpen] = createSignal(false)
47
+ const [shortcuts, setShortcuts] = createSignal<RegisteredShortcut[]>([])
48
+ let dialogRef: HTMLDialogElement | undefined
49
+
50
+ const openHelp = () => setIsOpen(true)
51
+ const closeHelp = () => setIsOpen(false)
52
+
53
+ onMount(() => {
54
+ const unsubscribe = activeShortcutsStore.subscribe((list) => {
55
+ setShortcuts([...list])
56
+ })
57
+ onCleanup(unsubscribe)
58
+ })
59
+
60
+ createEffect(() => {
61
+ const dialog = dialogRef
62
+ if (!dialog) return
63
+ if (isOpen()) {
64
+ if (!dialog.open) dialog.showModal()
65
+ document.body.style.overflow = "hidden"
66
+ } else {
67
+ if (dialog.open) dialog.close()
68
+ document.body.style.overflow = ""
69
+ }
70
+ })
71
+
72
+ onMount(() => {
73
+ const dialog = dialogRef
74
+ if (!dialog) return
75
+
76
+ const onDialogClick = (e: MouseEvent) => {
77
+ const rect = dialog.getBoundingClientRect()
78
+ const isInDialog =
79
+ rect.top <= e.clientY &&
80
+ e.clientY <= rect.top + rect.height &&
81
+ rect.left <= e.clientX &&
82
+ e.clientX <= rect.left + rect.width
83
+ if (!isInDialog) closeHelp()
84
+ }
85
+ dialog.addEventListener("click", onDialogClick)
86
+ onCleanup(() => dialog.removeEventListener("click", onDialogClick))
87
+ })
88
+
89
+ onMount(() => {
90
+ const qShortcutCleanup = defineShortcuts({
91
+ "?": {
92
+ handler: () => {
93
+ if (dialogRef?.open) {
94
+ closeHelp()
95
+ } else {
96
+ openHelp()
97
+ }
98
+ },
99
+ label: "Show Keyboard Shortcuts",
100
+ description: "Display this shortcuts help cheat sheet modal",
101
+ category: "system",
102
+ order: 0
103
+ }
104
+ })
105
+ onCleanup(qShortcutCleanup)
106
+ })
107
+
108
+ createEffect(() => {
109
+ if (!isOpen()) return
110
+ const cleanup = defineShortcuts({
111
+ "escape": {
112
+ handler: () => closeHelp(),
113
+ usingInput: true
114
+ },
115
+ "?": {
116
+ handler: () => closeHelp(),
117
+ usingInput: true
118
+ }
119
+ })
120
+ onCleanup(cleanup)
121
+ })
122
+
123
+ const sections = createMemo<CategorySection[]>(() => {
124
+ const list = shortcuts()
125
+ if (list.length === 0) return []
126
+
127
+ const grouped: Record<string, RegisteredShortcut[]> = {}
128
+ list.forEach((shortcut) => {
129
+ const cat = (shortcut.category || "system").toLowerCase()
130
+ const arr = grouped[cat]
131
+ if (arr) {
132
+ arr.push(shortcut)
133
+ } else {
134
+ grouped[cat] = [shortcut]
135
+ }
136
+ })
137
+
138
+ const sortedKeys = Object.keys(grouped).sort((keyA, keyB) => {
139
+ const indexA = categoryOrder().indexOf(keyA)
140
+ const indexB = categoryOrder().indexOf(keyB)
141
+
142
+ if (indexA !== -1 && indexB !== -1) return indexA - indexB
143
+ if (indexA !== -1) return -1
144
+ if (indexB !== -1) return 1
145
+
146
+ const labelA = resolvedCategories()[keyA] || keyA
147
+ const labelB = resolvedCategories()[keyB] || keyB
148
+ return labelA.localeCompare(labelB)
149
+ })
150
+
151
+ return sortedKeys.map((key) => {
152
+ const group = grouped[key] || []
153
+ group.sort((a, b) => {
154
+ const orderA = a.order ?? -1
155
+ const orderB = b.order ?? -1
156
+ if (orderA !== orderB) return orderB - orderA
157
+ const labelA = a.label || a.id
158
+ const labelB = b.label || b.id
159
+ return labelA.localeCompare(labelB)
160
+ })
161
+ return { key, label: resolvedCategories()[key] || key, items: group }
162
+ })
163
+ })
164
+
165
+ return (
166
+ <>
167
+ <dialog
168
+ ref={(el) => (dialogRef = el)}
169
+ id={modalId}
170
+ class="rla-shortcuts-dialog"
171
+ tabIndex={-1}
172
+ aria-labelledby={`${modalId}-title`}
173
+ >
174
+ <div class="rla-shortcuts-content">
175
+ <div class="rla-shortcuts-header">
176
+ <h3 id={`${modalId}-title`} class="rla-shortcuts-title">
177
+ Keyboard Shortcuts
178
+ </h3>
179
+ <button
180
+ type="button"
181
+ class="rla-shortcuts-close"
182
+ aria-label="Close shortcuts help"
183
+ onClick={closeHelp}
184
+ >
185
+ <RLSIcon name="close" class="size-4" />
186
+ </button>
187
+ </div>
188
+
189
+ <div class="rla-shortcuts-body">
190
+ <Show
191
+ when={shortcuts().length > 0}
192
+ fallback={
193
+ <div class="rla-shortcuts-empty">No active shortcuts found on this page.</div>
194
+ }
195
+ >
196
+ <For each={sections()}>
197
+ {(section) => (
198
+ <div class="rla-shortcuts-category-section">
199
+ <h4 class="rla-shortcuts-category-title">{section.label}</h4>
200
+ <div class="rla-shortcuts-grid">
201
+ <For each={section.items}>
202
+ {(shortcut) => (
203
+ <div class="rla-shortcuts-row">
204
+ <div class="rla-shortcuts-info">
205
+ <span class="rla-shortcuts-name">
206
+ {shortcut.label || shortcut.id}
207
+ </span>
208
+ <Show when={shortcut.description}>
209
+ <span class="rla-shortcuts-desc">{shortcut.description}</span>
210
+ </Show>
211
+ </div>
212
+ <div class="rla-shortcuts-keys">
213
+ <For each={shortcut.keys}>
214
+ {(key) => <kbd class="rla-shortcuts-kbd">{key}</kbd>}
215
+ </For>
216
+ </div>
217
+ </div>
218
+ )}
219
+ </For>
220
+ </div>
221
+ </div>
222
+ )}
223
+ </For>
224
+ </Show>
225
+ </div>
226
+
227
+ <div class="rla-shortcuts-footer">
228
+ <span>
229
+ Press <kbd class="rla-shortcuts-kbd-footer">?</kbd> to close
230
+ </span>
231
+ </div>
232
+ </div>
233
+ </dialog>
234
+
235
+ <style>{`
236
+ .rla-shortcuts-dialog {
237
+ border: 1px solid #27272a !important;
238
+ background: #18181b !important;
239
+ color: #fff !important;
240
+ border-radius: 12px;
241
+ padding: 0;
242
+ max-width: 520px;
243
+ width: 100%;
244
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
245
+ outline: none;
246
+ position: fixed;
247
+ left: 50%;
248
+ top: 50%;
249
+ transform: translate(-50%, -50%);
250
+ z-index: 50;
251
+ margin: 0;
252
+ }
253
+ .rla-shortcuts-dialog::backdrop {
254
+ background: rgba(0, 0, 0, 0.65);
255
+ backdrop-filter: blur(4px);
256
+ -webkit-backdrop-filter: blur(4px);
257
+ }
258
+ .rla-shortcuts-content {
259
+ display: flex;
260
+ flex-direction: column;
261
+ }
262
+ .rla-shortcuts-header {
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: space-between;
266
+ padding: 16px 20px;
267
+ border-bottom: 1px solid #27272a;
268
+ }
269
+ .rla-shortcuts-title {
270
+ margin: 0 !important;
271
+ font-size: 1.125rem !important;
272
+ font-weight: 600 !important;
273
+ color: #ffffff !important;
274
+ }
275
+ .rla-shortcuts-close {
276
+ background: transparent;
277
+ border: none;
278
+ color: var(--rl-neutral-400, #a3a3a3);
279
+ cursor: pointer;
280
+ padding: 6px;
281
+ border-radius: 6px;
282
+ display: flex;
283
+ align-items: center;
284
+ justify-content: center;
285
+ transition: color 0.15s, background-color 0.15s;
286
+ }
287
+ .rla-shortcuts-close:hover {
288
+ color: #fff;
289
+ background-color: var(--rl-neutral-800, #262626);
290
+ }
291
+ .rla-shortcuts-close:focus {
292
+ outline: none !important;
293
+ }
294
+ .rla-shortcuts-close:focus-visible {
295
+ outline: 2px solid var(--rl-primary-500, #3b82f6) !important;
296
+ }
297
+ .rla-shortcuts-body {
298
+ padding: 20px;
299
+ max-height: 380px;
300
+ overflow-y: auto;
301
+ display: flex;
302
+ flex-direction: column;
303
+ gap: 24px;
304
+ }
305
+ .rla-shortcuts-category-section {
306
+ display: flex;
307
+ flex-direction: column;
308
+ gap: 8px;
309
+ }
310
+ .rla-shortcuts-category-title {
311
+ margin: 0;
312
+ font-size: 0.75rem;
313
+ text-transform: uppercase;
314
+ letter-spacing: 0.05em;
315
+ color: var(--rl-primary-500, #3b82f6);
316
+ font-weight: 700;
317
+ }
318
+ .rla-shortcuts-grid {
319
+ display: flex;
320
+ flex-direction: column;
321
+ gap: 12px;
322
+ }
323
+ .rla-shortcuts-row {
324
+ display: flex;
325
+ align-items: center;
326
+ justify-content: space-between;
327
+ gap: 16px;
328
+ padding-bottom: 8px;
329
+ border-bottom: 1px solid rgba(38, 38, 38, 0.4);
330
+ }
331
+ .rla-shortcuts-info {
332
+ display: flex;
333
+ flex-direction: column;
334
+ gap: 2px;
335
+ }
336
+ .rla-shortcuts-name {
337
+ font-size: 0.875rem;
338
+ font-weight: 500;
339
+ color: #f5f5f5;
340
+ }
341
+ .rla-shortcuts-desc {
342
+ font-size: 0.75rem;
343
+ color: var(--rl-neutral-400, #a3a3a3);
344
+ }
345
+ .rla-shortcuts-keys {
346
+ display: flex;
347
+ align-items: center;
348
+ gap: 4px;
349
+ }
350
+ .rla-shortcuts-kbd {
351
+ display: inline-flex;
352
+ align-items: center;
353
+ justify-content: center;
354
+ font-family: inherit;
355
+ font-size: 0.75rem;
356
+ font-weight: 600;
357
+ background-color: #27272a !important;
358
+ border: 1px solid #3f3f46 !important;
359
+ border-radius: 4px;
360
+ padding: 2px 6px;
361
+ min-width: 20px;
362
+ color: #a3a3a3 !important;
363
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
364
+ }
365
+ .rla-shortcuts-empty {
366
+ font-size: 0.875rem;
367
+ color: var(--rl-neutral-500, #737373);
368
+ text-align: center;
369
+ padding: 16px 0;
370
+ }
371
+ .rla-shortcuts-footer {
372
+ padding: 12px 20px;
373
+ background-color: #09090b !important;
374
+ border-top: 1px solid #27272a;
375
+ font-size: 0.75rem;
376
+ color: var(--rl-neutral-400, #a3a3a3);
377
+ display: flex;
378
+ justify-content: flex-end;
379
+ }
380
+ .rla-shortcuts-kbd-footer {
381
+ background-color: #27272a !important;
382
+ border: 1px solid #3f3f46 !important;
383
+ border-radius: 3px;
384
+ padding: 1px 4px;
385
+ font-size: 0.7rem;
386
+ color: #a3a3a3 !important;
387
+ }
388
+ `}</style>
389
+ </>
390
+ )
391
+ }
392
+
393
+ export default RLSShortcuts
@@ -0,0 +1,271 @@
1
+ import { createMemo, For, Show, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { SidebarItem, SidebarProps } from "./sidebar"
3
+ import { sidebarTheme } from "./sidebar.theme"
4
+ import RLSBadge from "../badge/RLSBadge"
5
+ import RLSIcon from "../icon/RLSIcon"
6
+
7
+ export interface RLSSidebarProps extends SidebarProps {
8
+ children?: JSX.Element
9
+ header?: JSX.Element
10
+ headerSlot?: JSX.Element
11
+ footer?: JSX.Element
12
+ footerSlot?: JSX.Element
13
+ _depth?: number
14
+ }
15
+
16
+ function hasActiveChild(item: SidebarItem): boolean {
17
+ if (item.active) return true
18
+ return (item.children ?? []).some((c) => hasActiveChild(c))
19
+ }
20
+
21
+ function normalizeBadge(badge: any) {
22
+ if (!badge) return null
23
+ if (typeof badge === "string") {
24
+ return { text: badge, color: "primary" as const, variant: "soft" as const, class: "" }
25
+ }
26
+ let color = badge.color || "primary"
27
+ let badgeVariant = badge.variant || "soft"
28
+ if (badge.variant) {
29
+ const v = String(badge.variant).toLowerCase()
30
+ if (v === "tip" || v === "success") {
31
+ color = "success"
32
+ badgeVariant = "soft"
33
+ } else if (v === "caution" || v === "warning") {
34
+ color = "warning"
35
+ badgeVariant = "soft"
36
+ } else if (v === "error") {
37
+ color = "error"
38
+ badgeVariant = "soft"
39
+ } else if (v === "note" || v === "default") {
40
+ color = "primary"
41
+ badgeVariant = "soft"
42
+ }
43
+ }
44
+ return {
45
+ text: badge.text || badge.label || "",
46
+ color,
47
+ variant: badgeVariant as "solid" | "outline" | "soft" | "ghost",
48
+ class: badge.class || ""
49
+ }
50
+ }
51
+
52
+ const RLSSidebarItems: Component<{
53
+ items: SidebarItem[]
54
+ t: (key: string) => string
55
+ _depth: number
56
+ themeClasses: any
57
+ }> = (props) => {
58
+ const indentClass = () => (props._depth === 0 ? "ml-6" : props._depth === 1 ? "ml-4" : "ml-3")
59
+ const textSizeClass = () => (props._depth === 0 ? "" : "text-xs")
60
+
61
+ return (
62
+ <div class="flex flex-col gap-0.5">
63
+ <For each={props.items}>
64
+ {(item) => {
65
+ const hasChildren = () => (item.children?.length ?? 0) > 0
66
+ const isActive = () => item.active || hasActiveChild(item)
67
+ const b = () => normalizeBadge(item.badge)
68
+
69
+ return (
70
+ <div
71
+ class={props._depth === 1 ? props.themeClasses.item : ""}
72
+ data-slot={props._depth === 1 ? "item" : undefined}
73
+ >
74
+ <Show
75
+ when={hasChildren()}
76
+ fallback={
77
+ <a
78
+ href={item.href ?? "#"}
79
+ class={`${
80
+ props._depth === 1
81
+ ? props.themeClasses.itemLink
82
+ : `flex items-center gap-1.5 w-full px-2 py-1 rounded-md text-muted hover:text-highlighted hover:bg-muted transition-colors ${textSizeClass()}`
83
+ } ${isActive() ? " text-highlighted bg-muted" : ""}`}
84
+ aria-current={isActive() ? "page" : undefined}
85
+ >
86
+ {item.icon && (
87
+ <span
88
+ class={`${
89
+ props._depth === 1 ? props.themeClasses.itemIcon : "size-3.5 shrink-0"
90
+ } ${item.icon}`}
91
+ aria-hidden="true"
92
+ />
93
+ )}
94
+ <span data-slot="item-text" class="truncate flex-1">
95
+ {props.t(item.label || "")}
96
+ </span>
97
+ {b() && (
98
+ <RLSBadge
99
+ variant={b()!.variant}
100
+ color={b()!.color as any}
101
+ size="sm"
102
+ class={`ml-auto select-none ${b()!.class}`}
103
+ data-slot="item-badge"
104
+ >
105
+ {props.t(b()!.text)}
106
+ </RLSBadge>
107
+ )}
108
+ </a>
109
+ }
110
+ >
111
+ <details open={isActive()} class="group/details">
112
+ <summary
113
+ class={`${
114
+ props._depth === 1
115
+ ? props.themeClasses.itemLink
116
+ : `flex items-center gap-1.5 w-full px-2 py-1 rounded-md text-muted hover:text-highlighted hover:bg-muted transition-colors ${textSizeClass()}`
117
+ } ${isActive() ? " text-highlighted bg-muted" : ""} list-none cursor-pointer`}
118
+ data-slot="item-summary"
119
+ >
120
+ {item.icon && (
121
+ <span
122
+ class={`${
123
+ props._depth === 1 ? props.themeClasses.itemIcon : "size-3.5 shrink-0"
124
+ } ${item.icon}`}
125
+ aria-hidden="true"
126
+ />
127
+ )}
128
+ <span data-slot="item-text" class="truncate flex-1">
129
+ {props.t(item.label || "")}
130
+ </span>
131
+ {b() && (
132
+ <RLSBadge
133
+ variant={b()!.variant}
134
+ color={b()!.color as any}
135
+ size="sm"
136
+ class={`ml-auto select-none ${b()!.class}`}
137
+ data-slot="item-badge"
138
+ >
139
+ {props.t(b()!.text)}
140
+ </RLSBadge>
141
+ )}
142
+ <RLSIcon
143
+ name="i-rl-chevronRight"
144
+ class="size-3 ml-auto transition-transform group-open/details:rotate-90"
145
+ />
146
+ </summary>
147
+ <div class={`mt-0.5 ${indentClass()} flex flex-col gap-0.5`}>
148
+ <RLSSidebarItems
149
+ items={item.children ?? []}
150
+ t={props.t}
151
+ _depth={props._depth + 1}
152
+ themeClasses={props.themeClasses}
153
+ />
154
+ </div>
155
+ </details>
156
+ </Show>
157
+ </div>
158
+ )
159
+ }}
160
+ </For>
161
+ </div>
162
+ )
163
+ }
164
+
165
+ const RLSSidebar: Component<RLSSidebarProps> = (allProps) => {
166
+ const [props, rest] = splitProps(allProps, [
167
+ "items",
168
+ "collapsible",
169
+ "variant",
170
+ "logo",
171
+ "t",
172
+ "_depth",
173
+ "ui",
174
+ "class",
175
+ "children",
176
+ "header",
177
+ "headerSlot",
178
+ "footer",
179
+ "footerSlot"
180
+ ])
181
+
182
+ const t = () => props.t ?? ((key: string) => key)
183
+ const depth = () => props._depth ?? 0
184
+ const items = () => props.items ?? []
185
+ const isRoot = () => depth() === 0
186
+
187
+ const resolvedClasses = createMemo(() =>
188
+ sidebarTheme({
189
+ collapsible: props.collapsible,
190
+ variant: props.variant,
191
+ ui: props.ui,
192
+ class: props.class
193
+ })
194
+ )
195
+
196
+ let sidebarRef: HTMLElement | null = null
197
+
198
+ const toggleSidebar = () => {
199
+ if (sidebarRef) {
200
+ sidebarRef.classList.toggle("-translate-x-full")
201
+ }
202
+ }
203
+
204
+ const headerContent = () =>
205
+ props.header !== undefined ? (
206
+ props.header
207
+ ) : props.headerSlot !== undefined ? (
208
+ props.headerSlot
209
+ ) : props.logo ? (
210
+ <span data-slot="sidebar-logo" class="flex items-center gap-2">
211
+ <RLSIcon name="i-rl-layers" class="size-5 text-primary" />
212
+ <span class="truncate">{props.logo}</span>
213
+ </span>
214
+ ) : null
215
+
216
+ const footerContent = () => (props.footer !== undefined ? props.footer : props.footerSlot)
217
+
218
+ return (
219
+ <Show
220
+ when={isRoot()}
221
+ fallback={
222
+ <RLSSidebarItems
223
+ items={items()}
224
+ t={t()}
225
+ _depth={depth()}
226
+ themeClasses={resolvedClasses()}
227
+ />
228
+ }
229
+ >
230
+ <aside
231
+ ref={(el) => (sidebarRef = el)}
232
+ class={resolvedClasses().root}
233
+ data-slot="root"
234
+ data-sidebar
235
+ {...rest}
236
+ >
237
+ <div class={resolvedClasses().header} data-slot="header">
238
+ {headerContent()}
239
+ </div>
240
+
241
+ <div class={resolvedClasses().content} data-slot="content">
242
+ {props.children !== undefined ? (
243
+ props.children
244
+ ) : (
245
+ <RLSSidebarItems items={items()} t={t()} _depth={1} themeClasses={resolvedClasses()} />
246
+ )}
247
+ </div>
248
+
249
+ {footerContent() !== undefined && (
250
+ <div class={resolvedClasses().footer} data-slot="footer">
251
+ {footerContent()}
252
+ </div>
253
+ )}
254
+ </aside>
255
+
256
+ <Show when={props.collapsible === "offcanvas"}>
257
+ <button
258
+ type="button"
259
+ data-sidebar-toggle
260
+ class="fixed top-4 left-4 z-50 flex size-9 items-center justify-center rounded-md bg-default border border-default shadow-sm hover:bg-muted transition-colors"
261
+ aria-label="Toggle sidebar"
262
+ onClick={toggleSidebar}
263
+ >
264
+ <RLSIcon name="i-rl-panelLeft" class="size-4" />
265
+ </button>
266
+ </Show>
267
+ </Show>
268
+ )
269
+ }
270
+
271
+ export default RLSSidebar
@@ -0,0 +1,21 @@
1
+ import { createMemo, splitProps, type Component } from "solid-js"
2
+ import type { SkeletonProps } from "./skeleton"
3
+ import { skeletonTheme } from "./skeleton.theme"
4
+
5
+ export interface RLSSkeletonProps extends SkeletonProps {}
6
+
7
+ const RLSSkeleton: Component<RLSSkeletonProps> = (allProps) => {
8
+ const [props, rest] = splitProps(allProps, ["shape", "ui", "class"])
9
+
10
+ const classes = createMemo(() =>
11
+ skeletonTheme({
12
+ shape: props.shape ?? "rounded",
13
+ ui: props.ui,
14
+ class: props.class
15
+ })
16
+ )
17
+
18
+ return <div class={classes()["root"]} data-slot="root" {...rest} />
19
+ }
20
+
21
+ export default RLSSkeleton
@@ -18,7 +18,7 @@ const classes = slideoverTheme(Astro.props)
18
18
 
19
19
  const dialogId = `rla-slideover-${Math.random().toString(36).substring(2, 9)}`
20
20
  ---
21
- <div class={classes.root} data-slot="slideover-container">
21
+ <div class={classes.root} data-slot="root">
22
22
  {triggerLabel ? (
23
23
  <button
24
24
  class={classes.trigger}