@rimelight/ui 0.0.47 → 0.0.49

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,163 @@
1
+ import { createMemo, createSignal, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { FileUploadProps } from "./file-upload"
3
+ import { fileUploadTheme } from "./file-upload.theme"
4
+ import RLSIcon from "../icon/RLSIcon"
5
+
6
+ export interface RLSFileUploadProps extends FileUploadProps {
7
+ onChange?: (e: Event & { currentTarget: HTMLInputElement }) => void
8
+ children?: JSX.Element
9
+ iconSlot?: JSX.Element
10
+ }
11
+
12
+ const RLSFileUpload: Component<RLSFileUploadProps> = (allProps) => {
13
+ const [props, rest] = splitProps(allProps, [
14
+ "accept",
15
+ "multiple",
16
+ "disabled",
17
+ "title",
18
+ "description",
19
+ "icon",
20
+ "color",
21
+ "ui",
22
+ "class",
23
+ "onChange",
24
+ "children",
25
+ "iconSlot"
26
+ ])
27
+
28
+ const resolvedClasses = createMemo(() =>
29
+ fileUploadTheme({
30
+ color: props.color,
31
+ disabled: !!props.disabled,
32
+ ui: props.ui,
33
+ class: props.class
34
+ })
35
+ )
36
+
37
+ const [dragging, setDragging] = createSignal(false)
38
+ const [fileLabel, setFileLabel] = createSignal("")
39
+
40
+ let inputRef: HTMLInputElement | undefined
41
+
42
+ const uploadTitle = () => props.title ?? "Drop files here or click to upload"
43
+
44
+ const openPicker = () => {
45
+ if (props.disabled) return
46
+ inputRef?.click()
47
+ }
48
+
49
+ const handleKeyDown = (e: KeyboardEvent) => {
50
+ if (props.disabled) return
51
+ if (e.key === "Enter" || e.key === " ") {
52
+ e.preventDefault()
53
+ inputRef?.click()
54
+ }
55
+ }
56
+
57
+ const showFiles = (files: FileList | null) => {
58
+ if (!files || files.length === 0) return
59
+ setFileLabel(files.length === 1 ? files[0]!.name : `${files.length} files selected`)
60
+ }
61
+
62
+ const handleInputChange = (e: Event & { currentTarget: HTMLInputElement }) => {
63
+ showFiles(e.currentTarget.files)
64
+ props.onChange?.(e)
65
+ }
66
+
67
+ const handleDragOver = (e: DragEvent) => {
68
+ e.preventDefault()
69
+ setDragging(true)
70
+ }
71
+
72
+ const handleDragLeave = () => {
73
+ setDragging(false)
74
+ }
75
+
76
+ const handleDrop = (e: DragEvent) => {
77
+ e.preventDefault()
78
+ setDragging(false)
79
+ const dt = e.dataTransfer
80
+ if (!dt) return
81
+ try {
82
+ const transfer = new DataTransfer()
83
+ Array.from(dt.files).forEach((f) => transfer.items.add(f))
84
+ if (inputRef) inputRef.files = transfer.files
85
+ } catch (err) {
86
+ void err
87
+ }
88
+ showFiles(dt.files)
89
+ }
90
+
91
+ return (
92
+ <div
93
+ class={resolvedClasses().root + (dragging() ? " border-primary bg-primary/5" : "")}
94
+ data-slot="root"
95
+ data-fileUpload
96
+ data-disabled={props.disabled ? "true" : undefined}
97
+ role="button"
98
+ tabIndex={props.disabled ? -1 : 0}
99
+ aria-label={uploadTitle()}
100
+ onClick={openPicker}
101
+ onKeyDown={handleKeyDown}
102
+ onDragOver={handleDragOver}
103
+ onDragLeave={handleDragLeave}
104
+ onDrop={handleDrop}
105
+ {...rest}
106
+ >
107
+ <input
108
+ type="file"
109
+ class={resolvedClasses().input}
110
+ accept={props.accept}
111
+ multiple={props.multiple}
112
+ disabled={props.disabled}
113
+ data-fileUpload-input
114
+ aria-hidden="true"
115
+ tabIndex={-1}
116
+ ref={(el) => (inputRef = el)}
117
+ onChange={handleInputChange}
118
+ />
119
+
120
+ <div class={resolvedClasses().content} data-slot="content">
121
+ {props.iconSlot ? (
122
+ props.iconSlot
123
+ ) : (
124
+ <RLSIcon
125
+ name={props.icon ?? "uploadCloud"}
126
+ class={resolvedClasses().icon}
127
+ aria-hidden="true"
128
+ />
129
+ )}
130
+
131
+ {props.children ? (
132
+ props.children
133
+ ) : (
134
+ <>
135
+ <p class={resolvedClasses().title} data-slot="title">
136
+ {uploadTitle()}
137
+ </p>
138
+ {props.description ? (
139
+ <p class={resolvedClasses().description} data-slot="description">
140
+ {props.description}
141
+ </p>
142
+ ) : null}
143
+ {props.accept ? (
144
+ <p class="mt-1 text-[10px] text-muted/70">
145
+ Accepted: <span class="font-mono">{props.accept}</span>
146
+ </p>
147
+ ) : null}
148
+ </>
149
+ )}
150
+
151
+ <div
152
+ class={`mt-3 text-xs text-muted ${fileLabel() ? "" : "hidden"}`}
153
+ data-fileUpload-filelist
154
+ aria-live="polite"
155
+ >
156
+ {fileLabel() || "\u00a0"}
157
+ </div>
158
+ </div>
159
+ </div>
160
+ )
161
+ }
162
+
163
+ export default RLSFileUpload
@@ -0,0 +1,26 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { FloatingActionProps } from "./floating-action"
3
+ import { floatingActionTheme } from "./floating-action.theme"
4
+
5
+ export interface RLSFloatingActionProps extends FloatingActionProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSFloatingAction: Component<RLSFloatingActionProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ floatingActionTheme({
14
+ ui: props.ui,
15
+ class: props.class
16
+ })
17
+ )
18
+
19
+ return (
20
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
21
+ {props.children}
22
+ </div>
23
+ )
24
+ }
25
+
26
+ export default RLSFloatingAction
@@ -0,0 +1,56 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { FooterProps } from "./footer"
3
+ import { footerTheme } from "./footer.theme"
4
+
5
+ export interface RLSFooterProps extends FooterProps {
6
+ left?: JSX.Element
7
+ center?: JSX.Element
8
+ right?: JSX.Element
9
+ children?: JSX.Element
10
+ }
11
+
12
+ const RLSFooter: Component<RLSFooterProps> = (allProps) => {
13
+ const [props, rest] = splitProps(allProps, [
14
+ "contain",
15
+ "theme",
16
+ "ui",
17
+ "class",
18
+ "left",
19
+ "center",
20
+ "right",
21
+ "children"
22
+ ])
23
+
24
+ const resolvedClasses = createMemo(() =>
25
+ footerTheme({
26
+ contain: props.contain,
27
+ theme: props.theme,
28
+ ui: props.ui,
29
+ class: props.class
30
+ })
31
+ )
32
+
33
+ return (
34
+ <footer
35
+ class={resolvedClasses().root}
36
+ data-slot="root"
37
+ data-contain={props.contain ? "true" : "false"}
38
+ data-theme={props.theme}
39
+ {...rest}
40
+ >
41
+ <div class={resolvedClasses().container} data-slot="container">
42
+ <div class={resolvedClasses().left} data-slot="left">
43
+ {props.left}
44
+ </div>
45
+ <div class={resolvedClasses().center} data-slot="center">
46
+ {props.center}
47
+ </div>
48
+ <div class={resolvedClasses().right} data-slot="right">
49
+ {props.right}
50
+ </div>
51
+ </div>
52
+ </footer>
53
+ )
54
+ }
55
+
56
+ export default RLSFooter
@@ -0,0 +1,34 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { FormProps } from "./form"
3
+ import { formTheme } from "./form.theme"
4
+
5
+ export interface RLSFormProps extends FormProps {
6
+ children?: JSX.Element
7
+ onSubmit?: (e: Event & { currentTarget: HTMLFormElement }) => void
8
+ }
9
+
10
+ const RLSForm: Component<RLSFormProps> = (allProps) => {
11
+ const [props, rest] = splitProps(allProps, [
12
+ "schema",
13
+ "state",
14
+ "ui",
15
+ "class",
16
+ "children",
17
+ "onSubmit"
18
+ ])
19
+
20
+ const resolvedClasses = createMemo(() =>
21
+ formTheme({
22
+ ui: props.ui,
23
+ class: props.class
24
+ })
25
+ )
26
+
27
+ return (
28
+ <form class={resolvedClasses().root} data-slot="root" onSubmit={props.onSubmit} {...rest}>
29
+ {props.children}
30
+ </form>
31
+ )
32
+ }
33
+
34
+ export default RLSForm
@@ -0,0 +1,21 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { GalleryProps } from "./gallery"
3
+ import { galleryTheme } from "./gallery.theme"
4
+
5
+ export interface RLSGalleryProps extends GalleryProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSGallery: Component<RLSGalleryProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() => galleryTheme({ ui: props.ui, class: props.class }))
13
+
14
+ return (
15
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
16
+ {props.children}
17
+ </div>
18
+ )
19
+ }
20
+
21
+ export default RLSGallery
@@ -0,0 +1,269 @@
1
+ import { For, Show, splitProps, type Component } from "solid-js"
2
+
3
+ interface HighlightGroup {
4
+ label: string
5
+ buttons: string[]
6
+ color: string
7
+ }
8
+
9
+ export interface RLSGamepadProps {
10
+ highlightedButtons?: string[]
11
+ groups?: HighlightGroup[]
12
+ highlightColor?: string
13
+ backgroundColor?: string
14
+ }
15
+
16
+ interface GamepadButton {
17
+ id: string
18
+ label?: string
19
+ x: number
20
+ y: number
21
+ r?: number
22
+ w?: number
23
+ h?: number
24
+ type?: string
25
+ }
26
+
27
+ const shoulderButtons: GamepadButton[] = [
28
+ { id: "L2", label: "LT", x: 65, y: 10, w: 80, h: 22 },
29
+ { id: "L1", label: "LB", x: 65, y: 42, w: 80, h: 22 },
30
+ { id: "R2", label: "RT", x: 255, y: 10, w: 80, h: 22 },
31
+ { id: "R1", label: "RB", x: 255, y: 42, w: 80, h: 22 }
32
+ ]
33
+
34
+ const mainButtons: GamepadButton[] = [
35
+ { id: "Y", label: "Y", x: 315, y: 125, r: 12 },
36
+ { id: "X", label: "X", x: 288, y: 152, r: 12 },
37
+ { id: "B", label: "B", x: 342, y: 152, r: 12 },
38
+ { id: "A", label: "A", x: 315, y: 179, r: 12 },
39
+ { id: "DPadUp", x: 85, y: 130, w: 20, h: 20, type: "dpad" },
40
+ { id: "DPadDown", x: 85, y: 174, w: 20, h: 20, type: "dpad" },
41
+ { id: "DPadLeft", x: 63, y: 152, w: 20, h: 20, type: "dpad" },
42
+ { id: "DPadRight", x: 107, y: 152, w: 20, h: 20, type: "dpad" },
43
+ { id: "LS", x: 155, y: 215, r: 24 },
44
+ { id: "RS", x: 245, y: 215, r: 24 },
45
+ { id: "View", label: "⧉", x: 145, y: 105, w: 16, h: 10 },
46
+ { id: "Menu", label: "≡", x: 239, y: 105, w: 16, h: 10 }
47
+ ]
48
+
49
+ const RLSGamepad: Component<RLSGamepadProps> = (allProps) => {
50
+ const [props] = splitProps(allProps, [
51
+ "highlightedButtons",
52
+ "groups",
53
+ "highlightColor",
54
+ "backgroundColor"
55
+ ])
56
+
57
+ const highlighted = (): string[] => props.highlightedButtons ?? []
58
+ const groups = (): HighlightGroup[] => props.groups ?? []
59
+ const highlightColor = (): string => props.highlightColor || "var(--rl-primary-500)"
60
+ const backgroundColor = (): string => props.backgroundColor || "transparent"
61
+
62
+ const getButtonHighlight = (buttonId: string): { active: boolean; color: string | undefined } => {
63
+ if (highlighted().includes(buttonId)) {
64
+ return { active: true, color: highlightColor() }
65
+ }
66
+ for (const group of groups()) {
67
+ if (group.buttons.includes(buttonId)) {
68
+ return { active: true, color: group.color }
69
+ }
70
+ }
71
+ return { active: false, color: undefined }
72
+ }
73
+
74
+ return (
75
+ <div class="gamepad-wrapper">
76
+ <div class="gamepad-container">
77
+ <svg viewBox="-20 -20 440 380" class="gamepad-svg">
78
+ <defs>
79
+ <linearGradient id="bodyGrad" x1="0%" y1="0%" x2="0%" y2="100%">
80
+ <stop offset="0%" stop-color="#2a2a40" stop-opacity="1" />
81
+ <stop offset="100%" stop-color="#1a1a2e" stop-opacity="1" />
82
+ </linearGradient>
83
+ </defs>
84
+
85
+ <Show when={backgroundColor() !== "transparent"}>
86
+ <rect x="-20" y="-20" width="440" height="360" fill={backgroundColor()} rx="24" />
87
+ </Show>
88
+
89
+ <For each={shoulderButtons}>
90
+ {(btn) => {
91
+ const highlight = getButtonHighlight(btn.id)
92
+ const w = btn.w || 0
93
+ const h = btn.h || 0
94
+ const color = highlight.active ? highlight.color || highlightColor() : "#1e1e30"
95
+ return (
96
+ <g class={`gp-btn shoulder ${highlight.active ? "active" : ""}`}>
97
+ <rect
98
+ x={btn.x}
99
+ y={btn.y}
100
+ width={w}
101
+ height={h}
102
+ rx="5"
103
+ fill={color}
104
+ stroke={highlight.active ? "#ffffff33" : "#33334d"}
105
+ stroke-width={highlight.active ? "1.5" : "1"}
106
+ />
107
+ {btn.label ? (
108
+ <text
109
+ x={btn.x + w / 2}
110
+ y={btn.y + h / 2}
111
+ text-anchor="middle"
112
+ dominant-baseline="central"
113
+ class="btn-label"
114
+ fill={highlight.active ? "#fff" : "#555570"}
115
+ >
116
+ {btn.label}
117
+ </text>
118
+ ) : null}
119
+ </g>
120
+ )
121
+ }}
122
+ </For>
123
+
124
+ <path
125
+ d="M120,85 L280,85 C320,85 340,95 350,115 C370,145 390,215 390,245 C390,285 360,315 320,315 C290,315 260,285 240,255 C220,245 180,245 160,255 C140,285 110,315 80,315 C40,315 10,285 10,245 C10,215 30,145 50,115 C60,95 80,85 120,85 Z"
126
+ fill="url(#bodyGrad)"
127
+ stroke="#33334d"
128
+ stroke-width="1"
129
+ />
130
+
131
+ <path
132
+ d="M145,90 L255,90 L262,150 L138,150 Z"
133
+ fill="#151525"
134
+ stroke="#33334d"
135
+ stroke-width="0.5"
136
+ />
137
+
138
+ <For each={mainButtons}>
139
+ {(btn) => {
140
+ const highlight = getButtonHighlight(btn.id)
141
+ const color = highlight.active ? highlight.color || highlightColor() : "#1e1e30"
142
+
143
+ if (btn.r) {
144
+ return (
145
+ <g class={`gp-btn ${highlight.active ? "active" : ""}`}>
146
+ <circle
147
+ cx={btn.x}
148
+ cy={btn.y}
149
+ r={btn.r}
150
+ fill={color}
151
+ stroke={highlight.active ? "#ffffff33" : "#33334d"}
152
+ stroke-width={highlight.active ? "1.5" : "1"}
153
+ />
154
+ {btn.label ? (
155
+ <text
156
+ x={btn.x}
157
+ y={btn.y}
158
+ text-anchor="middle"
159
+ dominant-baseline="central"
160
+ class="btn-label"
161
+ fill={highlight.active ? "#fff" : "#555570"}
162
+ >
163
+ {btn.label}
164
+ </text>
165
+ ) : null}
166
+ </g>
167
+ )
168
+ }
169
+
170
+ const w = btn.w || 0
171
+ const h = btn.h || 0
172
+ return (
173
+ <g class={`gp-btn ${highlight.active ? "active" : ""}`}>
174
+ <rect
175
+ x={btn.x}
176
+ y={btn.y}
177
+ width={w}
178
+ height={h}
179
+ rx="3"
180
+ fill={color}
181
+ stroke={highlight.active ? "#ffffff33" : "#33334d"}
182
+ stroke-width={highlight.active ? "1.5" : "1"}
183
+ />
184
+ {btn.label ? (
185
+ <text
186
+ x={btn.x + w / 2}
187
+ y={btn.y + h / 2}
188
+ text-anchor="middle"
189
+ dominant-baseline="central"
190
+ class="btn-label"
191
+ fill={highlight.active ? "#fff" : "#555570"}
192
+ >
193
+ {btn.label}
194
+ </text>
195
+ ) : null}
196
+ </g>
197
+ )
198
+ }}
199
+ </For>
200
+ </svg>
201
+ </div>
202
+
203
+ <Show when={groups().some((group) => group.label)}>
204
+ <div class="flex flex-wrap justify-center gap-x-6 gap-y-3 mt-10 w-full">
205
+ <For each={groups().filter((group) => group.label)}>
206
+ {(group) => (
207
+ <div class="flex items-center gap-2 m-0 p-0 h-[10px] text-[10px] text-[#777790] uppercase tracking-[0.1em] font-bold leading-[10px]">
208
+ <svg width="8" height="8" class="flex-shrink-0 block">
209
+ <rect width="8" height="8" rx="2" fill={group.color} />
210
+ </svg>
211
+ <span class="inline-block h-[10px] leading-[10px] m-0 p-0">{group.label}</span>
212
+ </div>
213
+ )}
214
+ </For>
215
+ </div>
216
+ </Show>
217
+
218
+ <style>{`
219
+ .gamepad-wrapper {
220
+ display: flex;
221
+ flex-direction: column;
222
+ align-items: center;
223
+ padding: 3rem;
224
+ border-radius: 1.5rem;
225
+ width: 100%;
226
+ max-width: 100%;
227
+ margin: 1.5rem 0;
228
+ box-sizing: border-box;
229
+ container-type: inline-size;
230
+ border: 1px solid #1a1a2e;
231
+ }
232
+ .gamepad-container {
233
+ width: 100%;
234
+ max-width: 27.5rem;
235
+ }
236
+ .gamepad-svg {
237
+ width: 100%;
238
+ height: auto;
239
+ }
240
+ .gp-btn rect,
241
+ .gp-btn circle {
242
+ fill: #1e1e30;
243
+ stroke: #33334d;
244
+ stroke-width: 1;
245
+ transition: all 0.2s ease;
246
+ }
247
+ .btn-label {
248
+ fill: #555570;
249
+ font-size: 0.625rem;
250
+ font-family: var(--font-sans, system-ui);
251
+ font-weight: 800;
252
+ pointer-events: none;
253
+ line-height: 1;
254
+ }
255
+ .gp-btn.active rect,
256
+ .gp-btn.active circle {
257
+ fill: var(--hl-color, #00c16a);
258
+ stroke: #ffffff33;
259
+ stroke-width: 1.5;
260
+ }
261
+ .gp-btn.active .btn-label {
262
+ fill: #fff;
263
+ }
264
+ `}</style>
265
+ </div>
266
+ )
267
+ }
268
+
269
+ export default RLSGamepad
@@ -0,0 +1,29 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { GridProps } from "./grid"
3
+ import { gridTheme } from "./grid.theme"
4
+
5
+ export interface RLSGridProps extends GridProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSGrid: Component<RLSGridProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["cols", "stagger", "gap", "ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ gridTheme({
14
+ cols: props.cols ?? 2,
15
+ gap: props.gap ?? "md",
16
+ stagger: props.stagger ?? false,
17
+ ui: props.ui,
18
+ class: props.class
19
+ })
20
+ )
21
+
22
+ return (
23
+ <div class={resolvedClasses().root} {...rest}>
24
+ {props.children}
25
+ </div>
26
+ )
27
+ }
28
+
29
+ export default RLSGrid
@@ -0,0 +1,9 @@
1
+ ---
2
+ import UIHead, { type UIHeadProps } from "./UIHead.astro"
3
+
4
+ export interface Props extends UIHeadProps {}
5
+ ---
6
+
7
+ <UIHead {...Astro.props}>
8
+ <slot />
9
+ </UIHead>
@@ -0,0 +1,42 @@
1
+ import { onMount, onCleanup, type Component, type JSX } from "solid-js"
2
+
3
+ export interface RLSHeadProps {
4
+ transitions?: boolean
5
+ children?: JSX.Element
6
+ }
7
+
8
+ const RLSHead: Component<RLSHeadProps> = (_allProps) => {
9
+ const getThemePreference = (): "dark" | "light" => {
10
+ if (typeof localStorage === "undefined") return "light"
11
+ const stored = localStorage.getItem("theme")
12
+ if (stored === "dark" || stored === "light") return stored
13
+ const dark = window.matchMedia("(prefers-color-scheme: dark)").matches
14
+ return dark ? "dark" : "light"
15
+ }
16
+
17
+ const applyTheme = () => {
18
+ if (typeof document === "undefined") return
19
+ document.documentElement.setAttribute("data-theme", getThemePreference())
20
+ }
21
+
22
+ onMount(() => {
23
+ applyTheme()
24
+
25
+ const mql = window.matchMedia("(prefers-color-scheme: dark)")
26
+ const onChange = () => {
27
+ if (localStorage.getItem("theme") === "system") applyTheme()
28
+ }
29
+
30
+ if (typeof mql.addEventListener === "function") {
31
+ mql.addEventListener("change", onChange)
32
+ onCleanup(() => mql.removeEventListener("change", onChange))
33
+ } else {
34
+ mql.addListener(onChange)
35
+ onCleanup(() => mql.removeListener(onChange))
36
+ }
37
+ })
38
+
39
+ return null
40
+ }
41
+
42
+ export default RLSHead
@@ -17,6 +17,9 @@ const {
17
17
  function getThemePreference() {
18
18
  var stored = localStorage.getItem('theme');
19
19
  if (stored === 'dark' || stored === 'light') return stored;
20
+ if (stored === 'system') {
21
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
22
+ }
20
23
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
21
24
  }
22
25