@rimelight/ui 0.0.46 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/icons-DPnFQCts.d.mts +72 -0
  2. package/dist/icons.d.mts +1 -1
  3. package/dist/index.d.mts +5 -6
  4. package/dist/preset.d.mts +1 -1
  5. package/dist/preset.mjs +73 -1
  6. package/dist/resolver.d.mts +4 -5
  7. package/dist/resolver.mjs +2 -4
  8. package/dist/shortcuts.d.mts +10 -11
  9. package/dist/stores.d.mts +3 -4
  10. package/dist/{types-DqAgCNcm.d.mts → types-DWmKMcYC.d.mts} +1 -1
  11. package/dist/types.d.mts +1 -1
  12. package/package.json +8 -6
  13. package/src/components/3d-hover/RLS3dHover.tsx +21 -0
  14. package/src/components/accordion/RLSAccordion.tsx +112 -0
  15. package/src/components/app/RLSApp.tsx +21 -0
  16. package/src/components/aspect-ratio/RLSAspectRatio.tsx +30 -0
  17. package/src/components/audio/RLSAudio.tsx +27 -0
  18. package/src/components/avatar/RLSAvatar.tsx +142 -0
  19. package/src/components/avatar/avatar.ts +22 -21
  20. package/src/components/avatar-group/RLSAvatarGroup.tsx +163 -0
  21. package/src/components/badge/RLSBadge.tsx +1 -1
  22. package/src/components/badge/RLVBadge.vue +5 -3
  23. package/src/components/banner/RLABanner.astro +1 -1
  24. package/src/components/banner/RLSBanner.tsx +151 -0
  25. package/src/components/breadcrumb/RLSBreadcrumb.tsx +194 -0
  26. package/src/components/browser-mockup/RLSBrowserMockup.tsx +21 -0
  27. package/src/components/button/RLSButton.tsx +53 -38
  28. package/src/components/button/RLVButton.vue +44 -32
  29. package/src/components/calendar/RLSCalendar.tsx +26 -0
  30. package/src/components/callout/RLSCallout.tsx +151 -0
  31. package/src/components/card/RLSCard.tsx +4 -0
  32. package/src/components/card/RLVCard.vue +4 -0
  33. package/src/components/card/card.ts +10 -0
  34. package/src/components/carousel/RLACarousel.astro +14 -1
  35. package/src/components/carousel/RLSCarousel.tsx +260 -0
  36. package/src/components/chart/RLSChart.tsx +299 -0
  37. package/src/components/chat/RLSChat.tsx +27 -0
  38. package/src/components/chat-message/RLSChatMessage.tsx +27 -0
  39. package/src/components/chat-messages/RLSChatMessages.tsx +27 -0
  40. package/src/components/chat-palette/RLSChatPalette.tsx +27 -0
  41. package/src/components/chat-prompt/RLSChatPrompt.tsx +27 -0
  42. package/src/components/chat-prompt-submit/RLSChatPromptSubmit.tsx +27 -0
  43. package/src/components/chat-reasoning/RLSChatReasoning.tsx +27 -0
  44. package/src/components/chat-shimmer/RLSChatShimmer.tsx +27 -0
  45. package/src/components/chat-tool/RLSChatTool.tsx +27 -0
  46. package/src/components/checkbox/RLACheckbox.astro +1 -0
  47. package/src/components/checkbox/RLSCheckbox.tsx +1 -1
  48. package/src/components/checkbox/checkbox.theme.ts +2 -2
  49. package/src/components/chip/RLSChip.tsx +65 -0
  50. package/src/components/collapsible/RLSCollapsible.tsx +89 -0
  51. package/src/components/color-area/RLSColorArea.tsx +554 -0
  52. package/src/components/color-field/RLSColorField.tsx +236 -0
  53. package/src/components/color-picker/RLSColorPicker.tsx +26 -0
  54. package/src/components/color-slider/RLSColorSlider.tsx +373 -0
  55. package/src/components/command-palette/RLSCommandPalette.tsx +26 -0
  56. package/src/components/compare/RLSCompare.tsx +26 -0
  57. package/src/components/confirm/RLSConfirm.tsx +173 -0
  58. package/src/components/container/RLSContainer.tsx +24 -0
  59. package/src/components/context-menu/RLSContextMenu.tsx +26 -0
  60. package/src/components/copy-markdown/RLSCopyMarkdown.tsx +57 -0
  61. package/src/components/dashboard-group/RLSDashboardGroup.tsx +29 -0
  62. package/src/components/dashboard-navbar/RLSDashboardNavbar.tsx +116 -0
  63. package/src/components/dashboard-panel/RLSDashboardPanel.tsx +84 -0
  64. package/src/components/dashboard-resize-handle/RLSDashboardResizeHandle.tsx +35 -0
  65. package/src/components/dashboard-search/RLADashboardSearch.astro +1 -1
  66. package/src/components/dashboard-search/RLSDashboardSearch.tsx +125 -0
  67. package/src/components/dashboard-search-button/RLSDashboardSearchButton.tsx +82 -0
  68. package/src/components/dashboard-sidebar/RLADashboardSidebar.astro +4 -4
  69. package/src/components/dashboard-sidebar/RLSDashboardSidebar.tsx +184 -0
  70. package/src/components/dashboard-sidebar-collapse/RLSDashboardSidebarCollapse.tsx +46 -0
  71. package/src/components/dashboard-sidebar-toggle/RLSDashboardSidebarToggle.tsx +35 -0
  72. package/src/components/dashboard-toolbar/RLSDashboardToolbar.tsx +49 -0
  73. package/src/components/date/RLSDate.tsx +34 -0
  74. package/src/components/dock/RLSDock.tsx +26 -0
  75. package/src/components/drawer/RLSDrawer.tsx +177 -0
  76. package/src/components/dropdown-menu/RLADropdownMenu.astro +250 -241
  77. package/src/components/dropdown-menu/RLSDropdownMenu.tsx +183 -86
  78. package/src/components/editor/RLSEditor.tsx +26 -0
  79. package/src/components/empty/RLSEmpty.tsx +132 -0
  80. package/src/components/error/RLSError.tsx +26 -0
  81. package/src/components/field-group/RLSFieldGroup.tsx +46 -0
  82. package/src/components/fieldset/RLSFieldset.tsx +26 -0
  83. package/src/components/file-upload/RLAFileUpload.astro +1 -1
  84. package/src/components/file-upload/RLSFileUpload.tsx +163 -0
  85. package/src/components/floating-action/RLSFloatingAction.tsx +26 -0
  86. package/src/components/footer/RLSFooter.tsx +56 -0
  87. package/src/components/form/RLSForm.tsx +34 -0
  88. package/src/components/gallery/RLSGallery.tsx +21 -0
  89. package/src/components/gamepad/RLSGamepad.tsx +269 -0
  90. package/src/components/grid/RLSGrid.tsx +29 -0
  91. package/src/components/head/RLAHead.astro +9 -0
  92. package/src/components/head/RLSHead.tsx +42 -0
  93. package/src/components/head/UIHead.astro +3 -0
  94. package/src/components/header/HeaderLayer.astro +1 -1
  95. package/src/components/header/RLSHeader.tsx +87 -0
  96. package/src/components/hover-card/RLSHoverCard.tsx +72 -0
  97. package/src/components/icon/RLSIcon.tsx +3 -1
  98. package/src/components/image/RLAImage.astro +447 -138
  99. package/src/components/image/RLSImage.tsx +545 -0
  100. package/src/components/image/image.theme.ts +19 -3
  101. package/src/components/image/image.ts +31 -1
  102. package/src/components/input/RLAInput.astro +62 -51
  103. package/src/components/input/RLSInput.tsx +1 -1
  104. package/src/components/input-date/RLSInputDate.tsx +92 -0
  105. package/src/components/input-menu/RLAInputMenu.astro +3 -3
  106. package/src/components/input-menu/RLSInputMenu.tsx +199 -0
  107. package/src/components/input-number/RLSInputNumber.tsx +184 -0
  108. package/src/components/input-pin/RLSInputPin.tsx +185 -0
  109. package/src/components/input-rating/RLAInputRating.astro +8 -8
  110. package/src/components/input-rating/RLSInputRating.tsx +198 -0
  111. package/src/components/input-rating/input-rating.ts +4 -0
  112. package/src/components/input-tags/RLSInputTags.tsx +94 -0
  113. package/src/components/input-time/RLSInputTime.tsx +92 -0
  114. package/src/components/kbd/RLSKbd.tsx +37 -0
  115. package/src/components/keyboard/RLSKeyboard.tsx +476 -0
  116. package/src/components/knob/RLSKnob.tsx +27 -0
  117. package/src/components/label/RLSLabel.tsx +27 -0
  118. package/src/components/layout-grid/RLALayoutGrid.astro +29 -29
  119. package/src/components/layout-grid/RLSLayoutGrid.tsx +402 -0
  120. package/src/components/layout-grid/layout-grid.ts +25 -1
  121. package/src/components/link/RLALink.astro +5 -5
  122. package/src/components/link/RLSLink.tsx +6 -5
  123. package/src/components/link/link.ts +21 -12
  124. package/src/components/link-group/RLSLinkGroup.tsx +76 -0
  125. package/src/components/listbox/RLSListbox.tsx +27 -0
  126. package/src/components/locale-selector/RLALocaleSelector.astro +110 -102
  127. package/src/components/locale-selector/RLSLocaleSelector.tsx +178 -0
  128. package/src/components/locale-selector/locale-selector.ts +2 -7
  129. package/src/components/logo/RLALogo.astro +153 -102
  130. package/src/components/logo/RLSLogo.tsx +129 -0
  131. package/src/components/main/RLSMain.tsx +40 -0
  132. package/src/components/marquee/RLSMarquee.tsx +80 -0
  133. package/src/components/meter/RLSMeter.tsx +27 -0
  134. package/src/components/modal/RLAModal.astro +1 -1
  135. package/src/components/modal/RLSModal.tsx +109 -72
  136. package/src/components/navigation-menu/RLANavigationMenu.astro +6 -6
  137. package/src/components/navigation-menu/RLSNavigationMenu.tsx +228 -0
  138. package/src/components/navigation-menu/navigation-menu.ts +2 -2
  139. package/src/components/package-managers/RLSPackageManagers.tsx +204 -0
  140. package/src/components/page/RLSPage.tsx +21 -0
  141. package/src/components/page-aside/RLSPageAside.tsx +21 -0
  142. package/src/components/page-body/RLSPageBody.tsx +21 -0
  143. package/src/components/page-header/RLSPageHeader.tsx +21 -0
  144. package/src/components/page-section/RLSPageSection.tsx +201 -0
  145. package/src/components/pagination/RLSPagination.tsx +114 -0
  146. package/src/components/password/RLSPassword.tsx +27 -0
  147. package/src/components/phone-mockup/RLSPhoneMockup.tsx +27 -0
  148. package/src/components/placeholder/RLSPlaceholder.tsx +47 -0
  149. package/src/components/popover/RLAPopover.astro +2 -2
  150. package/src/components/popover/RLSPopover.tsx +124 -0
  151. package/src/components/post/RLSPost.tsx +134 -0
  152. package/src/components/pricing-plan/RLSPricingPlan.tsx +26 -0
  153. package/src/components/pricing-table/RLSPricingTable.tsx +26 -0
  154. package/src/components/progress/RLSProgress.tsx +53 -0
  155. package/src/components/prose/RLSProse.tsx +26 -0
  156. package/src/components/qr-code/RLSQrCode.tsx +26 -0
  157. package/src/components/quote/RLAQuote.astro +10 -6
  158. package/src/components/quote/RLSQuote.tsx +37 -0
  159. package/src/components/quote/quote.theme.ts +5 -2
  160. package/src/components/radio-group/RLSRadioGroup.tsx +37 -0
  161. package/src/components/rating/RLSRating.tsx +27 -0
  162. package/src/components/scroll-area/RLAScrollArea.astro +1 -1
  163. package/src/components/scroll-area/RLSScrollArea.tsx +210 -0
  164. package/src/components/scroll-to-top/RLAScrollToTop.astro +5 -4
  165. package/src/components/scroll-to-top/RLSScrollToTop.tsx +145 -0
  166. package/src/components/search/RLASearch.astro +6 -6
  167. package/src/components/search/RLSSearch.tsx +237 -0
  168. package/src/components/select/RLASelect.astro +67 -4
  169. package/src/components/select/RLSSelect.tsx +107 -39
  170. package/src/components/select/RLVSelect.vue +50 -1
  171. package/src/components/select/select.theme.ts +30 -10
  172. package/src/components/select/select.ts +20 -1
  173. package/src/components/separator/RLSSeparator.tsx +92 -0
  174. package/src/components/share/RLSShare.tsx +66 -0
  175. package/src/components/shortcuts/RLAShortcuts.astro +2 -2
  176. package/src/components/shortcuts/RLSShortcuts.tsx +393 -0
  177. package/src/components/sidebar/RLASidebar.astro +1 -1
  178. package/src/components/sidebar/RLSSidebar.tsx +271 -0
  179. package/src/components/skeleton/RLSSkeleton.tsx +21 -0
  180. package/src/components/slideover/RLASlideover.astro +1 -1
  181. package/src/components/slideover/RLSSlideover.tsx +159 -0
  182. package/src/components/slider/RLSSlider.tsx +112 -0
  183. package/src/components/speed-dial/RLSSpeedDial.tsx +27 -0
  184. package/src/components/spinner/RLSSpinner.tsx +22 -0
  185. package/src/components/splitter/RLASplitter.astro +1 -1
  186. package/src/components/splitter/RLSSplitter.tsx +178 -0
  187. package/src/components/spoiler/RLSSpoiler.tsx +26 -0
  188. package/src/components/stepper/RLAStepper.astro +9 -9
  189. package/src/components/stepper/RLSStepper.tsx +205 -0
  190. package/src/components/steps/RLSSteps.tsx +108 -0
  191. package/src/components/sticky-surface/RLSStickySurface.tsx +63 -0
  192. package/src/components/surround/RLSSurround.tsx +57 -0
  193. package/src/components/switch/RLSSwitch.tsx +56 -0
  194. package/src/components/table/RLATable.astro +1044 -579
  195. package/src/components/table/RLSTable.tsx +893 -0
  196. package/src/components/table/table.theme.ts +47 -16
  197. package/src/components/table/table.ts +104 -66
  198. package/src/components/table-of-contents/RLSTableOfContents.tsx +196 -0
  199. package/src/components/tabs/RLATabs.astro +8 -8
  200. package/src/components/tabs/RLSTabs.tsx +298 -0
  201. package/src/components/textarea/RLATextarea.astro +1 -0
  202. package/src/components/textarea/RLSTextarea.tsx +3 -1
  203. package/src/components/theme-selector/RLAThemeSelector.astro +85 -56
  204. package/src/components/theme-selector/RLSThemeSelector.tsx +131 -0
  205. package/src/components/theme-selector/theme-selector.theme.ts +5 -7
  206. package/src/components/theme-selector/theme-selector.ts +11 -1
  207. package/src/components/timeline/RLSTimeline.tsx +171 -0
  208. package/src/components/toast/RLAToast.astro +2 -2
  209. package/src/components/toast/RLSToast.tsx +179 -0
  210. package/src/components/toaster/RLSToaster.tsx +82 -0
  211. package/src/components/tooltip/RLSTooltip.tsx +48 -0
  212. package/src/components/tour/RLSTour.tsx +26 -0
  213. package/src/components/tree/RLSTree.tsx +26 -0
  214. package/src/components/user/RLSUser.tsx +26 -0
  215. package/src/components/video/RLAVideo.astro +4 -1
  216. package/src/components/video/RLSVideo.tsx +158 -0
  217. package/src/components/watermark/RLSWatermark.tsx +27 -0
  218. package/src/components/window-mockup/RLSWindowMockup.tsx +27 -0
  219. package/src/preset.ts +73 -1
  220. package/src/resolver.ts +6 -1
  221. package/src/virtual.d.ts +6 -0
  222. package/dist/icons--RzV1RTU.d.mts +0 -72
@@ -0,0 +1,185 @@
1
+ import { createMemo, createSignal, splitProps, For, type Component, type JSX } from "solid-js"
2
+ import type { InputPinProps } from "./input-pin"
3
+ import { inputPinTheme } from "./input-pin.theme"
4
+ import { inputTheme } from "../input/input.theme"
5
+
6
+ export interface RLSInputPinProps extends InputPinProps {
7
+ children?: JSX.Element
8
+ length?: number
9
+ value?: string
10
+ defaultValue?: string
11
+ name?: string
12
+ disabled?: boolean
13
+ required?: boolean
14
+ type?: "text" | "numeric" | "alphanumeric"
15
+ size?: "sm" | "md" | "lg"
16
+ onInput?: (e: InputEvent & { currentTarget: HTMLInputElement }) => void
17
+ onChange?: (e: Event & { currentTarget: HTMLInputElement }) => void
18
+ onKeyDown?: (e: KeyboardEvent & { currentTarget: HTMLInputElement }) => void
19
+ onComplete?: (value: string) => void
20
+ }
21
+
22
+ const RLSInputPin: Component<RLSInputPinProps> = (allProps) => {
23
+ const [props, rest] = splitProps(allProps, [
24
+ "children",
25
+ "length",
26
+ "value",
27
+ "defaultValue",
28
+ "name",
29
+ "disabled",
30
+ "required",
31
+ "type",
32
+ "size",
33
+ "ui",
34
+ "class",
35
+ "onInput",
36
+ "onChange",
37
+ "onKeyDown",
38
+ "onComplete"
39
+ ])
40
+
41
+ const resolvedClasses = createMemo(() =>
42
+ inputPinTheme({
43
+ ui: props.ui,
44
+ class: props.class
45
+ })
46
+ )
47
+
48
+ const cellsClasses = createMemo(() =>
49
+ inputTheme({
50
+ variant: "default",
51
+ size: props.size ?? "md",
52
+ error: false,
53
+ ui: props.ui
54
+ })
55
+ )
56
+
57
+ const length = () => props.length ?? 4
58
+ const pinType = () => props.type ?? "numeric"
59
+
60
+ const initial = (props.value ?? props.defaultValue ?? "").split("")
61
+ const cells = createMemo(() => Array.from({ length: length() }))
62
+
63
+ const [current, setCurrent] = createSignal<string>(props.value ?? props.defaultValue ?? "")
64
+
65
+ let pinRefs: Array<HTMLInputElement | undefined> = []
66
+
67
+ const setCellRef = (el: HTMLInputElement | undefined, index: number) => {
68
+ pinRefs[index] = el
69
+ }
70
+
71
+ const focusCell = (index: number) => {
72
+ const el = pinRefs[index]
73
+ if (el) {
74
+ el.focus()
75
+ el.select()
76
+ }
77
+ }
78
+
79
+ const handleInput = (e: InputEvent & { currentTarget: HTMLInputElement }, index: number) => {
80
+ const input = e.currentTarget
81
+ let digit = input.value
82
+ if (pinType() === "numeric") {
83
+ digit = digit.replace(/[^0-9]/g, "")
84
+ } else if (pinType() === "alphanumeric") {
85
+ digit = digit.replace(/[^0-9a-zA-Z]/g, "")
86
+ } else {
87
+ digit = digit.replace(/[^0-9]/g, "")
88
+ }
89
+ digit = digit.slice(-1)
90
+ input.value = digit
91
+ const next = current().slice(0, index) + digit + current().slice(index + 1)
92
+ setCurrent(next)
93
+ props.onInput?.(e)
94
+ props.onChange?.(e)
95
+ if (digit) {
96
+ if (index < length() - 1) {
97
+ focusCell(index + 1)
98
+ } else if (next.length === length()) {
99
+ props.onComplete?.(next)
100
+ }
101
+ }
102
+ }
103
+
104
+ const handleKeyDown = (e: KeyboardEvent & { currentTarget: HTMLInputElement }, index: number) => {
105
+ props.onKeyDown?.(e)
106
+ const input = e.currentTarget
107
+ if (e.key === "Backspace" && input.value === "") {
108
+ e.preventDefault()
109
+ if (index > 0) focusCell(index - 1)
110
+ } else if (e.key === "ArrowLeft") {
111
+ e.preventDefault()
112
+ if (index > 0) focusCell(index - 1)
113
+ } else if (e.key === "ArrowRight") {
114
+ e.preventDefault()
115
+ if (index < length() - 1) focusCell(index + 1)
116
+ }
117
+ }
118
+
119
+ const handlePaste = (e: ClipboardEvent, index: number) => {
120
+ const pasted = (e.clipboardData?.getData("text") ?? "")
121
+ .replace(pinType() === "numeric" ? /[^0-9]/g : /[^0-9a-zA-Z]/g, "")
122
+ .slice(0, length())
123
+ if (!pasted) return
124
+ e.preventDefault()
125
+ const next = current().split("")
126
+ for (let i = 0; i < pasted.length; i++) {
127
+ const char = pasted[i]
128
+ if (char !== undefined && index + i < length()) next[index + i] = char
129
+ }
130
+ const joined = next.join("")
131
+ setCurrent(joined)
132
+ const endIndex = Math.min(index + pasted.length, length() - 1)
133
+ const endEl = pinRefs[endIndex]
134
+ if (endEl) {
135
+ endEl.value = joined[endIndex] ?? ""
136
+ endEl.focus()
137
+ }
138
+ if (joined.length === length()) props.onComplete?.(joined)
139
+ }
140
+
141
+ return (
142
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
143
+ <div
144
+ role="group"
145
+ aria-label="One-time code"
146
+ class="flex items-center gap-2"
147
+ data-slot="group"
148
+ >
149
+ <For each={cells()}>
150
+ {(_, index) => (
151
+ <div
152
+ class={`${cellsClasses().root} w-12 h-12 flex items-center justify-center rounded-md`}
153
+ data-slot="cell"
154
+ >
155
+ <input
156
+ ref={(el) => setCellRef(el, index())}
157
+ type="text"
158
+ name={props.name ? `${props.name}-${index()}` : undefined}
159
+ inputMode={pinType() === "numeric" ? "numeric" : "text"}
160
+ autocomplete="one-time-code"
161
+ maxLength={1}
162
+ value={initial[index()] ?? ""}
163
+ disabled={props.disabled}
164
+ required={props.required}
165
+ aria-label={`Digit ${index() + 1}`}
166
+ class={`${cellsClasses().input} w-full text-center`}
167
+ data-slot="input"
168
+ onInput={(e) =>
169
+ handleInput(e as InputEvent & { currentTarget: HTMLInputElement }, index())
170
+ }
171
+ onKeyDown={(e) =>
172
+ handleKeyDown(e as KeyboardEvent & { currentTarget: HTMLInputElement }, index())
173
+ }
174
+ onPaste={(e) => handlePaste(e, index())}
175
+ />
176
+ </div>
177
+ )}
178
+ </For>
179
+ </div>
180
+ {props.children}
181
+ </div>
182
+ )
183
+ }
184
+
185
+ export default RLSInputPin
@@ -117,12 +117,12 @@ const ratingItems = Array.from({ length }, (_, idx) => {
117
117
  const input = this.querySelector('.rating-hidden-input') as HTMLInputElement | null
118
118
  const items = this.querySelectorAll('.input-rating-item') as NodeListOf<HTMLElement>
119
119
 
120
- const step = parseFloat(this.dataset.step || '1')
121
- const length = parseInt(this.dataset.length || '5')
122
- const clearable = this.dataset.clearable === 'true'
123
- const hoverable = this.dataset.hoverable === 'true'
124
- const disabled = this.dataset.disabled === 'true'
125
- const readonly = this.dataset.readonly === 'true'
120
+ const step = parseFloat(this.dataset["step"] || '1')
121
+ const length = parseInt(this.dataset["length"] || '5')
122
+ const clearable = this.dataset["clearable"] === 'true'
123
+ const hoverable = this.dataset["hoverable"] === 'true'
124
+ const disabled = this.dataset["disabled"] === 'true'
125
+ const readonly = this.dataset["readonly"] === 'true'
126
126
 
127
127
  if (disabled || readonly) return
128
128
 
@@ -130,7 +130,7 @@ const ratingItems = Array.from({ length }, (_, idx) => {
130
130
 
131
131
  function updateVisuals(val: number) {
132
132
  items.forEach((item) => {
133
- const idx = parseInt(item.dataset.index || '0')
133
+ const idx = parseInt(item.dataset["index"] || '0')
134
134
  const indicator = item.querySelector('.rating-item-indicator') as HTMLElement | null
135
135
  if (indicator) {
136
136
  indicator.style.width = getWidthForVal(val, idx)
@@ -153,7 +153,7 @@ const ratingItems = Array.from({ length }, (_, idx) => {
153
153
  const clickListeners = new Map<HTMLElement, (e: MouseEvent) => void>()
154
154
 
155
155
  items.forEach((item) => {
156
- const idx = parseInt(item.dataset.index || '0')
156
+ const idx = parseInt(item.dataset["index"] || '0')
157
157
 
158
158
  const onMouseMove = (event: MouseEvent) => {
159
159
  const val = getValueFromEvent(event, item, idx)
@@ -0,0 +1,198 @@
1
+ import { createMemo, createSignal, splitProps, For, type Component } from "solid-js"
2
+ import type { InputRatingProps } from "./input-rating"
3
+ import { inputRatingTheme } from "./input-rating.theme"
4
+ import RLSIcon from "../icon/RLSIcon"
5
+
6
+ export interface RLSInputRatingProps extends InputRatingProps {
7
+ onValueChange?: (value: number) => void
8
+ }
9
+
10
+ const RLSInputRating: Component<RLSInputRatingProps> = (allProps) => {
11
+ const [props, rest] = splitProps(allProps, [
12
+ "id",
13
+ "readonly",
14
+ "icon",
15
+ "emptyIcon",
16
+ "color",
17
+ "size",
18
+ "orientation",
19
+ "step",
20
+ "disabled",
21
+ "required",
22
+ "name",
23
+ "modelValue",
24
+ "defaultValue",
25
+ "value",
26
+ "length",
27
+ "clearable",
28
+ "hoverable",
29
+ "ui",
30
+ "class",
31
+ "onValueChange"
32
+ ])
33
+
34
+ const resolvedClasses = createMemo(() =>
35
+ inputRatingTheme({
36
+ color: props.color ?? "primary",
37
+ size: props.size ?? "md",
38
+ orientation: props.orientation ?? "horizontal",
39
+ readonly: !!props.readonly,
40
+ disabled: !!props.disabled,
41
+ ui: props.ui,
42
+ class: props.class
43
+ })
44
+ )
45
+
46
+ const step = () => props.step ?? 1
47
+ const length = () => props.length ?? 5
48
+ const itemSize = () => {
49
+ const map: Record<string, string> = {
50
+ xs: "0.75rem",
51
+ sm: "1rem",
52
+ md: "1.25rem",
53
+ lg: "1.5rem",
54
+ xl: "1.75rem"
55
+ }
56
+ return map[props.size ?? "md"] ?? "1.25rem"
57
+ }
58
+
59
+ const [internalValue, setInternalValue] = createSignal<number>(
60
+ props.value !== undefined
61
+ ? props.value
62
+ : props.modelValue !== undefined
63
+ ? props.modelValue
64
+ : (props.defaultValue ?? 0)
65
+ )
66
+ const value = () =>
67
+ props.value !== undefined
68
+ ? props.value
69
+ : props.modelValue !== undefined
70
+ ? props.modelValue
71
+ : internalValue()
72
+
73
+ const [hoverValue, setHoverValue] = createSignal<number | null>(null)
74
+ const display = (): number => (hoverValue() !== null ? hoverValue()! : value())
75
+
76
+ const items = createMemo(() => Array.from({ length: length() }, (_, idx) => idx + 1))
77
+
78
+ const getWidthForVal = (val: number, itemIndex: number) => {
79
+ if (val >= itemIndex) return "100%"
80
+ if (val <= itemIndex - 1) return "0%"
81
+ return `${(val - (itemIndex - 1)) * 100}%`
82
+ }
83
+
84
+ const getValueFromEvent = (e: MouseEvent, el: HTMLElement, index: number) => {
85
+ const rect = el.getBoundingClientRect()
86
+ const x = (e.clientX - rect.left) / rect.width
87
+ const steppedX = Math.ceil(x / step()) * step()
88
+ return Math.min(Math.max(index - 1 + steppedX, 0), length())
89
+ }
90
+
91
+ const handleMouseMove = (e: MouseEvent, el: HTMLElement, index: number) => {
92
+ if (props.disabled || props.readonly) return
93
+ if (props.hoverable) setHoverValue(getValueFromEvent(e, el, index))
94
+ }
95
+
96
+ const handleMouseLeave = () => {
97
+ setHoverValue(null)
98
+ }
99
+
100
+ const handleClick = (e: MouseEvent, el: HTMLElement, index: number) => {
101
+ if (props.disabled || props.readonly) return
102
+ const val = getValueFromEvent(e, el, index)
103
+ let finalVal = val
104
+ if (props.clearable && value() === val) finalVal = 0
105
+ if (props.value === undefined && props.modelValue === undefined) setInternalValue(finalVal)
106
+ props.onValueChange?.(finalVal)
107
+ }
108
+
109
+ const handleKeyDown = (e: KeyboardEvent) => {
110
+ if (props.disabled || props.readonly) return
111
+ let newVal = value()
112
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
113
+ newVal = Math.min(value() + step(), length())
114
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
115
+ newVal = Math.max(value() - step(), 0)
116
+ } else if (e.key === "Home") {
117
+ newVal = 0
118
+ } else if (e.key === "End") {
119
+ newVal = length()
120
+ } else {
121
+ return
122
+ }
123
+ e.preventDefault()
124
+ if (props.value === undefined && props.modelValue === undefined) setInternalValue(newVal)
125
+ props.onValueChange?.(newVal)
126
+ }
127
+
128
+ return (
129
+ <div
130
+ class={resolvedClasses().root}
131
+ data-slot="root"
132
+ data-step={step()}
133
+ data-length={length()}
134
+ data-clearable={props.clearable ? "true" : "false"}
135
+ data-hoverable={props.hoverable ? "true" : "false"}
136
+ data-disabled={props.disabled ? "true" : "false"}
137
+ data-readonly={props.readonly ? "true" : "false"}
138
+ id={props.id}
139
+ role="slider"
140
+ aria-valuenow={value()}
141
+ aria-valuemin={0}
142
+ aria-valuemax={length()}
143
+ aria-readonly={props.readonly ? "true" : "false"}
144
+ aria-disabled={props.disabled ? "true" : "false"}
145
+ tabIndex={props.disabled || props.readonly ? undefined : 0}
146
+ onKeyDown={handleKeyDown}
147
+ {...rest}
148
+ >
149
+ <input
150
+ type="hidden"
151
+ name={props.name}
152
+ value={value()}
153
+ required={props.required}
154
+ disabled={props.disabled || props.readonly}
155
+ class="rating-hidden-input"
156
+ />
157
+
158
+ <For each={items()}>
159
+ {(i) => (
160
+ <div
161
+ class={`input-rating-item ${resolvedClasses().item}`}
162
+ data-slot="item"
163
+ data-index={i}
164
+ style={{ "--item-size": itemSize(), "width": itemSize(), "height": itemSize() }}
165
+ onMouseMove={(e) => handleMouseMove(e, e.currentTarget, i)}
166
+ onMouseLeave={handleMouseLeave}
167
+ onClick={(e) => handleClick(e, e.currentTarget, i)}
168
+ >
169
+ <div class={resolvedClasses().emptyIcon} data-slot="emptyIcon">
170
+ <RLSIcon
171
+ name={props.emptyIcon || props.icon || "star"}
172
+ size={props.size ?? "md"}
173
+ class={resolvedClasses().icon}
174
+ aria-hidden="true"
175
+ />
176
+ </div>
177
+
178
+ <div
179
+ class={`rating-item-indicator ${resolvedClasses().indicator}`}
180
+ data-slot="indicator"
181
+ data-state={display() >= i - 0.99 ? "active" : "inactive"}
182
+ style={{ width: getWidthForVal(display(), i) }}
183
+ >
184
+ <RLSIcon
185
+ name={props.icon || "star"}
186
+ size={props.size ?? "md"}
187
+ class={resolvedClasses().icon}
188
+ aria-hidden="true"
189
+ />
190
+ </div>
191
+ </div>
192
+ )}
193
+ </For>
194
+ </div>
195
+ )
196
+ }
197
+
198
+ export default RLSInputRating
@@ -71,6 +71,10 @@ export interface InputRatingProps {
71
71
  * The name of the field, submitted with form data.
72
72
  */
73
73
  name?: string
74
+ /**
75
+ * The controlled rating value.
76
+ */
77
+ value?: number
74
78
  /**
75
79
  * The controlled rating value. Can be bound with v-model.
76
80
  */
@@ -0,0 +1,94 @@
1
+ import { createMemo, createSignal, For, Show, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { InputTagsProps, TagItem } from "./input-tags"
3
+ import { inputTagsTheme } from "./input-tags.theme"
4
+
5
+ export interface RLSInputTagsProps extends InputTagsProps {
6
+ children?: JSX.Element
7
+ onAddTag?: (tag: string) => void
8
+ onRemoveTag?: (tag: TagItem) => void
9
+ }
10
+
11
+ const RLSInputTags: Component<RLSInputTagsProps> = (allProps) => {
12
+ const [props, rest] = splitProps(allProps, [
13
+ "items",
14
+ "displayLimit",
15
+ "estimatedTagWidth",
16
+ "showInput",
17
+ "name",
18
+ "placeholder",
19
+ "ui",
20
+ "class",
21
+ "children",
22
+ "onAddTag",
23
+ "onRemoveTag"
24
+ ])
25
+
26
+ const [inputValue, setInputValue] = createSignal("")
27
+ const limit = () => props.displayLimit ?? 4
28
+
29
+ const normalizedItems = createMemo<TagItem[]>(() =>
30
+ (props.items ?? []).map((item) =>
31
+ typeof item === "string" ? { label: item, value: item } : item
32
+ )
33
+ )
34
+
35
+ const renderedItems = () => normalizedItems().slice(0, limit())
36
+ const overflowCount = () => Math.max(0, normalizedItems().length - limit())
37
+
38
+ const handleKeyDown: JSX.EventHandler<HTMLInputElement, KeyboardEvent> = (e) => {
39
+ if (e.key === "Enter") {
40
+ e.preventDefault()
41
+ const val = inputValue().trim()
42
+ if (val) {
43
+ props.onAddTag?.(val)
44
+ setInputValue("")
45
+ }
46
+ }
47
+ }
48
+
49
+ const resolvedClasses = createMemo(() =>
50
+ inputTagsTheme({
51
+ ui: props.ui,
52
+ class: props.class
53
+ })
54
+ )
55
+
56
+ return (
57
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
58
+ <Show when={normalizedItems().length > 0} fallback={props.children}>
59
+ <For each={renderedItems()}>
60
+ {(item) => (
61
+ <span class={resolvedClasses().tag} data-slot="tag">
62
+ <span class={resolvedClasses().tagLabel}>{item.label}</span>
63
+ <Show when={props.name}>
64
+ <input type="hidden" name={props.name} value={item.value ?? item.label} />
65
+ </Show>
66
+ </span>
67
+ )}
68
+ </For>
69
+
70
+ <Show when={overflowCount() > 0}>
71
+ <span class={resolvedClasses().more} data-slot="more" aria-hidden="true">
72
+ +{overflowCount()} more
73
+ </span>
74
+ </Show>
75
+ </Show>
76
+
77
+ <Show when={props.showInput}>
78
+ <input
79
+ type="text"
80
+ name={props.name ? `${props.name}__input` : undefined}
81
+ placeholder={props.placeholder || "Add tag…"}
82
+ value={inputValue()}
83
+ onInput={(e) => setInputValue(e.currentTarget.value)}
84
+ onKeyDown={handleKeyDown}
85
+ class={resolvedClasses().input}
86
+ data-slot="input"
87
+ autocomplete="off"
88
+ />
89
+ </Show>
90
+ </div>
91
+ )
92
+ }
93
+
94
+ export default RLSInputTags
@@ -0,0 +1,92 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { InputTimeProps } from "./input-time"
3
+ import { inputTimeTheme } from "./input-time.theme"
4
+ import { inputTheme } from "../input/input.theme"
5
+
6
+ export interface RLSInputTimeProps extends InputTimeProps {
7
+ children?: JSX.Element
8
+ value?: string
9
+ defaultValue?: string
10
+ name?: string
11
+ placeholder?: string
12
+ disabled?: boolean
13
+ readonly?: boolean
14
+ required?: boolean
15
+ min?: string
16
+ max?: string
17
+ step?: number
18
+ size?: "sm" | "md" | "lg"
19
+ onInput?: (e: InputEvent & { currentTarget: HTMLInputElement }) => void
20
+ onChange?: (e: Event & { currentTarget: HTMLInputElement }) => void
21
+ onBlur?: (e: FocusEvent & { currentTarget: HTMLInputElement }) => void
22
+ onFocus?: (e: FocusEvent & { currentTarget: HTMLInputElement }) => void
23
+ onKeyDown?: (e: KeyboardEvent & { currentTarget: HTMLInputElement }) => void
24
+ }
25
+
26
+ const RLSInputTime: Component<RLSInputTimeProps> = (allProps) => {
27
+ const [props, rest] = splitProps(allProps, [
28
+ "children",
29
+ "value",
30
+ "defaultValue",
31
+ "name",
32
+ "placeholder",
33
+ "disabled",
34
+ "readonly",
35
+ "required",
36
+ "min",
37
+ "max",
38
+ "step",
39
+ "size",
40
+ "ui",
41
+ "class",
42
+ "onInput",
43
+ "onChange",
44
+ "onBlur",
45
+ "onFocus",
46
+ "onKeyDown"
47
+ ])
48
+
49
+ const resolvedClasses = createMemo(() =>
50
+ inputTimeTheme({
51
+ ui: props.ui,
52
+ class: props.class
53
+ })
54
+ )
55
+
56
+ const inputClasses = createMemo(() =>
57
+ inputTheme({
58
+ variant: "default",
59
+ size: props.size ?? "md",
60
+ error: false,
61
+ ui: props.ui
62
+ })
63
+ )
64
+
65
+ return (
66
+ <div class={`${resolvedClasses().root} ${inputClasses().root}`} data-slot="root">
67
+ <input
68
+ type="time"
69
+ name={props.name}
70
+ value={props.value !== undefined ? props.value : (props.defaultValue ?? "")}
71
+ placeholder={props.placeholder}
72
+ disabled={props.disabled}
73
+ readonly={props.readonly}
74
+ required={props.required}
75
+ min={props.min}
76
+ max={props.max}
77
+ step={props.step}
78
+ class={inputClasses().input}
79
+ data-slot="input"
80
+ onInput={props.onInput}
81
+ onChange={props.onChange}
82
+ onBlur={props.onBlur}
83
+ onFocus={props.onFocus}
84
+ onKeyDown={props.onKeyDown}
85
+ {...rest}
86
+ />
87
+ {props.children}
88
+ </div>
89
+ )
90
+ }
91
+
92
+ export default RLSInputTime
@@ -0,0 +1,37 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { KbdProps } from "./kbd"
3
+ import { kbdTheme } from "./kbd.theme"
4
+
5
+ export interface RLSKbdProps extends KbdProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSKbd: Component<RLSKbdProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, [
11
+ "color",
12
+ "variant",
13
+ "size",
14
+ "theme",
15
+ "ui",
16
+ "class",
17
+ "children"
18
+ ])
19
+
20
+ const classes = createMemo(() =>
21
+ kbdTheme({
22
+ color: props.color ?? "neutral",
23
+ variant: props.variant ?? "outline",
24
+ size: props.size ?? "md",
25
+ ui: props.ui,
26
+ class: props.class
27
+ })
28
+ )
29
+
30
+ return (
31
+ <kbd class={classes()["root"]} data-slot="root" data-theme={props.theme} {...rest}>
32
+ {props.children}
33
+ </kbd>
34
+ )
35
+ }
36
+
37
+ export default RLSKbd