@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
@@ -1,51 +1,62 @@
1
- ---
2
- import type { InputProps } from "./input"
3
- import { inputTheme } from "./input.theme"
4
-
5
-
6
-
7
- const {
8
- type = "text",
9
- value = "",
10
- placeholder,
11
- disabled = false,
12
- readonly = false,
13
- name,
14
- required = false,
15
- error = false,
16
- leadingIcon,
17
- trailingIcon,
18
- theme,
19
- ...rest
20
- } = Astro.props as InputProps
21
-
22
-
23
-
24
- const classes = inputTheme({
25
- ...Astro.props,
26
- error: !!error
27
- });
28
- ---
29
- <div class={classes.root} data-slot="root" data-theme={theme}>
30
- {(leadingIcon || Astro.slots.has("leading")) && (
31
- <span class={classes.leading} data-slot="leading">
32
- {leadingIcon ? <span class={leadingIcon} /> : <slot name="leading" />}
33
- </span>
34
- )}
35
- <input
36
- type={type}
37
- class={classes.input}
38
- value={value}
39
- placeholder={placeholder}
40
- disabled={disabled}
41
- readonly={readonly}
42
- name={name}
43
- required={required}
44
- {...rest}
45
- />
46
- {(trailingIcon || Astro.slots.has("trailing")) && (
47
- <span class={classes.trailing} data-slot="trailing">
48
- {trailingIcon ? <span class={trailingIcon} /> : <slot name="trailing" />}
49
- </span>
50
- )}
51
- </div>
1
+ ---
2
+ import type { InputProps } from "./input"
3
+ import { inputTheme } from "./input.theme"
4
+ import RLAIcon from "../icon/RLAIcon.astro"
5
+
6
+ const {
7
+ type = "text",
8
+ value = "",
9
+ placeholder,
10
+ disabled = false,
11
+ readonly = false,
12
+ name,
13
+ required = false,
14
+ error = false,
15
+ leadingIcon,
16
+ trailingIcon,
17
+ size = "md",
18
+ variant = "default",
19
+ theme,
20
+ class: className,
21
+ ui,
22
+ ...rest
23
+ } = Astro.props as InputProps
24
+
25
+ const hasLeading = !!(leadingIcon || Astro.slots.has("leading"))
26
+ const hasTrailing = !!(trailingIcon || Astro.slots.has("trailing"))
27
+
28
+ const classes = inputTheme({
29
+ variant,
30
+ size,
31
+ hasLeading,
32
+ hasTrailing,
33
+ error: !!error,
34
+ theme,
35
+ ui,
36
+ class: className
37
+ })
38
+ ---
39
+ <div class={classes.root} data-slot="root" data-theme={theme}>
40
+ {(leadingIcon || Astro.slots.has("leading")) && (
41
+ <span class={classes.leading} data-slot="leading">
42
+ {leadingIcon ? <RLAIcon name={leadingIcon} size="sm" aria-hidden="true" /> : <slot name="leading" />}
43
+ </span>
44
+ )}
45
+ <input
46
+ type={type}
47
+ class={classes.input}
48
+ data-slot="input"
49
+ value={value}
50
+ placeholder={placeholder}
51
+ disabled={disabled}
52
+ readonly={readonly}
53
+ name={name}
54
+ required={required}
55
+ {...rest}
56
+ />
57
+ {(trailingIcon || Astro.slots.has("trailing")) && (
58
+ <span class={classes.trailing} data-slot="trailing">
59
+ {trailingIcon ? <RLAIcon name={trailingIcon} size="sm" aria-hidden="true" /> : <slot name="trailing" />}
60
+ </span>
61
+ )}
62
+ </div>
@@ -59,7 +59,7 @@ const RLSInput: Component<RLSInputProps> = (allProps) => {
59
59
  )
60
60
 
61
61
  return (
62
- <div class={resolvedClasses().root} data-slot="root">
62
+ <div class={resolvedClasses().root} data-slot="root" data-theme={props.theme}>
63
63
  {props.leading ? (
64
64
  props.leading
65
65
  ) : props.leadingIcon ? (
@@ -0,0 +1,92 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { InputDateProps } from "./input-date"
3
+ import { inputDateTheme } from "./input-date.theme"
4
+ import { inputTheme } from "../input/input.theme"
5
+
6
+ export interface RLSInputDateProps extends InputDateProps {
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 RLSInputDate: Component<RLSInputDateProps> = (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
+ inputDateTheme({
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="date"
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 RLSInputDate
@@ -0,0 +1,199 @@
1
+ import {
2
+ createMemo,
3
+ createSignal,
4
+ splitProps,
5
+ For,
6
+ Show,
7
+ onMount,
8
+ onCleanup,
9
+ createEffect,
10
+ type Component
11
+ } from "solid-js"
12
+ import type { InputMenuProps } from "./input-menu"
13
+ import { inputMenuTheme } from "./input-menu.theme"
14
+ import RLSIcon from "../icon/RLSIcon"
15
+
16
+ export interface RLSInputMenuProps extends InputMenuProps {
17
+ defaultValue?: string
18
+ onValueChange?: (value: string) => void
19
+ }
20
+
21
+ type InputMenuItem = { label: string; value: string; [key: string]: any }
22
+
23
+ const RLSInputMenu: Component<RLSInputMenuProps> = (allProps) => {
24
+ const [props, rest] = splitProps(allProps, [
25
+ "items",
26
+ "value",
27
+ "defaultValue",
28
+ "placeholder",
29
+ "disabled",
30
+ "name",
31
+ "label",
32
+ "size",
33
+ "color",
34
+ "ui",
35
+ "class",
36
+ "onValueChange"
37
+ ])
38
+
39
+ const resolvedClasses = createMemo(() =>
40
+ inputMenuTheme({
41
+ size: props.size ?? "md",
42
+ color: props.color,
43
+ ui: props.ui,
44
+ class: props.class
45
+ })
46
+ )
47
+
48
+ const normalizedItems = createMemo<InputMenuItem[]>(() =>
49
+ (props.items ?? []).map((item) =>
50
+ typeof item === "string" ? { label: item, value: item } : item
51
+ )
52
+ )
53
+
54
+ const [selectedValue, setSelectedValue] = createSignal<string>(
55
+ props.value !== undefined
56
+ ? String(props.value)
57
+ : props.defaultValue !== undefined
58
+ ? String(props.defaultValue)
59
+ : ""
60
+ )
61
+ const currentValue = () => (props.value !== undefined ? String(props.value) : selectedValue())
62
+
63
+ const selectedItem = createMemo(() =>
64
+ normalizedItems().find((item) => String(item.value) === String(currentValue()))
65
+ )
66
+
67
+ const visibleLabel = createMemo(() => {
68
+ if (props.placeholder && (currentValue() === "" || currentValue() === "undefined")) {
69
+ return props.placeholder
70
+ }
71
+ return selectedItem()?.label ?? props.placeholder ?? "Select item..."
72
+ })
73
+
74
+ const [open, setOpen] = createSignal(false)
75
+ const [query, setQuery] = createSignal("")
76
+
77
+ let searchRef: HTMLInputElement | undefined
78
+ let hiddenRef: HTMLInputElement | undefined
79
+
80
+ const filteredItems = createMemo(() => {
81
+ const q = query().toLowerCase().trim()
82
+ if (!q) return normalizedItems()
83
+ return normalizedItems().filter((item) => item.label.toLowerCase().includes(q))
84
+ })
85
+
86
+ const selectItem = (value: string, _label: string) => {
87
+ if (props.value === undefined) setSelectedValue(value)
88
+ if (hiddenRef) hiddenRef.value = value
89
+ props.onValueChange?.(value)
90
+ setQuery("")
91
+ setOpen(false)
92
+ }
93
+
94
+ const handleTriggerClick = (e: MouseEvent) => {
95
+ e.stopPropagation()
96
+ if (props.disabled) return
97
+ setOpen(!open())
98
+ }
99
+
100
+ const handleItemClick = (e: MouseEvent, item: InputMenuItem) => {
101
+ e.stopPropagation()
102
+ selectItem(item.value, item.label)
103
+ }
104
+
105
+ createEffect(() => {
106
+ if (open()) searchRef?.focus()
107
+ })
108
+
109
+ onMount(() => {
110
+ const handleDocumentClick = () => {
111
+ setOpen(false)
112
+ }
113
+ document.addEventListener("click", handleDocumentClick)
114
+ onCleanup(() => document.removeEventListener("click", handleDocumentClick))
115
+ })
116
+
117
+ return (
118
+ <div
119
+ class={resolvedClasses().root}
120
+ data-slot="root"
121
+ data-disabled={props.disabled ? "" : undefined}
122
+ {...rest}
123
+ >
124
+ {props.label ? (
125
+ <label class={resolvedClasses().label} data-slot="label">
126
+ {props.label}
127
+ </label>
128
+ ) : null}
129
+
130
+ <div class="relative w-full" data-inputMenu-wrapper>
131
+ <button
132
+ type="button"
133
+ class={resolvedClasses().trigger}
134
+ data-slot="trigger"
135
+ disabled={props.disabled}
136
+ data-inputMenu-trigger
137
+ onClick={handleTriggerClick}
138
+ >
139
+ <span data-inputMenu-value>{visibleLabel()}</span>
140
+ <RLSIcon name="i-rl-chevronsUpDown" class="opacity-50" aria-hidden="true" />
141
+ </button>
142
+
143
+ <Show when={open()}>
144
+ <div
145
+ class={`${resolvedClasses().content} absolute z-50`}
146
+ data-inputMenu-dropdown
147
+ data-slot="content"
148
+ >
149
+ <input
150
+ type="text"
151
+ class={resolvedClasses().input}
152
+ placeholder="Search..."
153
+ data-inputMenu-search
154
+ ref={(el) => (searchRef = el)}
155
+ onInput={(e) => setQuery(e.currentTarget.value)}
156
+ />
157
+ <div class="py-1" data-inputMenu-items>
158
+ <For each={filteredItems()}>
159
+ {(item) => (
160
+ <div
161
+ class={resolvedClasses().item}
162
+ data-slot="item"
163
+ data-value={item.value}
164
+ data-label={item.label}
165
+ data-inputMenu-item
166
+ data-state={
167
+ String(item.value) === String(currentValue()) ? "selected" : undefined
168
+ }
169
+ onClick={(e) => handleItemClick(e, item)}
170
+ >
171
+ <span class={resolvedClasses().itemText}>{item.label}</span>
172
+ <RLSIcon
173
+ name="i-rl-check"
174
+ class={`${resolvedClasses().itemIcon} ${
175
+ String(item.value) === String(currentValue()) ? "" : "hidden"
176
+ }`}
177
+ data-check-icon
178
+ aria-hidden="true"
179
+ />
180
+ </div>
181
+ )}
182
+ </For>
183
+ </div>
184
+ </div>
185
+ </Show>
186
+ </div>
187
+
188
+ <input
189
+ type="hidden"
190
+ name={props.name}
191
+ value={currentValue()}
192
+ data-inputMenu-hidden-input
193
+ ref={(el) => (hiddenRef = el)}
194
+ />
195
+ </div>
196
+ )
197
+ }
198
+
199
+ export default RLSInputMenu
@@ -0,0 +1,184 @@
1
+ import { createMemo, createSignal, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { InputNumberProps } from "./input-number"
3
+ import { inputNumberTheme } from "./input-number.theme"
4
+ import { inputTheme } from "../input/input.theme"
5
+ import RLSIcon from "../icon/RLSIcon"
6
+
7
+ export interface RLSInputNumberProps extends InputNumberProps {
8
+ children?: JSX.Element
9
+ value?: number
10
+ defaultValue?: number
11
+ min?: number
12
+ max?: number
13
+ step?: number
14
+ name?: string
15
+ placeholder?: string
16
+ disabled?: boolean
17
+ readonly?: boolean
18
+ required?: boolean
19
+ size?: "sm" | "md" | "lg"
20
+ onInput?: (e: InputEvent & { currentTarget: HTMLInputElement }) => void
21
+ onChange?: (e: Event & { currentTarget: HTMLInputElement }) => void
22
+ onBlur?: (e: FocusEvent & { currentTarget: HTMLInputElement }) => void
23
+ onFocus?: (e: FocusEvent & { currentTarget: HTMLInputElement }) => void
24
+ onKeyDown?: (e: KeyboardEvent & { currentTarget: HTMLInputElement }) => void
25
+ onValueChange?: (value: number) => void
26
+ }
27
+
28
+ const RLSInputNumber: Component<RLSInputNumberProps> = (allProps) => {
29
+ const [props, rest] = splitProps(allProps, [
30
+ "children",
31
+ "value",
32
+ "defaultValue",
33
+ "min",
34
+ "max",
35
+ "step",
36
+ "name",
37
+ "placeholder",
38
+ "disabled",
39
+ "readonly",
40
+ "required",
41
+ "size",
42
+ "ui",
43
+ "class",
44
+ "onInput",
45
+ "onChange",
46
+ "onBlur",
47
+ "onFocus",
48
+ "onKeyDown",
49
+ "onValueChange"
50
+ ])
51
+
52
+ const resolvedClasses = createMemo(() =>
53
+ inputNumberTheme({
54
+ ui: props.ui,
55
+ class: props.class
56
+ })
57
+ )
58
+
59
+ const inputClasses = createMemo(() =>
60
+ inputTheme({
61
+ variant: "default",
62
+ size: props.size ?? "md",
63
+ error: false,
64
+ ui: props.ui
65
+ })
66
+ )
67
+
68
+ const clamp = (value: number) =>
69
+ Math.min(
70
+ props.max ?? Number.POSITIVE_INFINITY,
71
+ Math.max(props.min ?? Number.NEGATIVE_INFINITY, value)
72
+ )
73
+
74
+ const [internalValue, setInternalValue] = createSignal<number | undefined>(props.defaultValue)
75
+ const boundValue = () => (props.value !== undefined ? props.value : internalValue())
76
+
77
+ let inputRef: HTMLInputElement | undefined
78
+
79
+ const updateValue = (next: number) => {
80
+ const clamped = clamp(next)
81
+ if (props.value === undefined) setInternalValue(clamped)
82
+ props.onValueChange?.(clamped)
83
+ }
84
+
85
+ const stepBy = (dir: 1 | -1) => {
86
+ if (props.disabled || props.readonly) return
87
+ const raw = inputRef ? Number(inputRef.value) : Number(boundValue())
88
+ const base = Number.isNaN(raw) ? Number(boundValue() || 0) : raw
89
+ updateValue((Number.isNaN(base) ? 0 : base) + dir * (props.step ?? 1))
90
+ if (inputRef)
91
+ inputRef.value = String(clamp((Number.isNaN(base) ? 0 : base) + dir * (props.step ?? 1)))
92
+ }
93
+
94
+ const changeEvent = (el: HTMLInputElement): Event & { currentTarget: HTMLInputElement } => {
95
+ const ev = new Event("change", { bubbles: true })
96
+ Object.defineProperty(ev, "currentTarget", { value: el })
97
+ return ev as Event & { currentTarget: HTMLInputElement }
98
+ }
99
+
100
+ const commit = () => {
101
+ const el = inputRef
102
+ if (!el) return
103
+ const raw = Number(el.value)
104
+ if (el.value !== "" && !Number.isNaN(raw)) {
105
+ const clamped = clamp(raw)
106
+ if (props.value === undefined) setInternalValue(clamped)
107
+ if (clamped !== raw) el.value = String(clamped)
108
+ props.onValueChange?.(clamped)
109
+ }
110
+ props.onChange?.(changeEvent(el))
111
+ }
112
+
113
+ const handleWheel = (e: WheelEvent) => {
114
+ if (props.disabled || props.readonly) return
115
+ e.preventDefault()
116
+ stepBy(e.deltaY < 0 ? 1 : -1)
117
+ }
118
+
119
+ const handleKeyDown = (e: KeyboardEvent & { currentTarget: HTMLInputElement }) => {
120
+ props.onKeyDown?.(e)
121
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") {
122
+ e.preventDefault()
123
+ stepBy(e.key === "ArrowUp" ? 1 : -1)
124
+ } else if (e.key === "Enter") {
125
+ e.preventDefault()
126
+ commit()
127
+ }
128
+ }
129
+
130
+ return (
131
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
132
+ <div class={`relative w-full ${inputClasses().root}`}>
133
+ <input
134
+ ref={(el) => (inputRef = el)}
135
+ type="number"
136
+ name={props.name}
137
+ value={props.value !== undefined ? props.value : ""}
138
+ placeholder={props.placeholder}
139
+ min={props.min}
140
+ max={props.max}
141
+ step={props.step}
142
+ disabled={props.disabled}
143
+ readonly={props.readonly}
144
+ required={props.required}
145
+ class={`${inputClasses().input} pr-11 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`}
146
+ data-slot="input"
147
+ onInput={props.onInput}
148
+ onChange={props.onChange}
149
+ onBlur={commit}
150
+ onFocus={props.onFocus}
151
+ onKeyDown={handleKeyDown}
152
+ onWheel={handleWheel}
153
+ />
154
+ <div class="absolute right-1 top-1/2 flex -translate-y-1/2 flex-col">
155
+ <button
156
+ type="button"
157
+ tabIndex={-1}
158
+ aria-label="Increment"
159
+ class="flex size-6 items-center justify-center rounded-sm text-muted hover:bg-muted/60 hover:text-highlighted disabled:cursor-not-allowed disabled:opacity-50"
160
+ data-slot="increment"
161
+ disabled={props.disabled || props.readonly}
162
+ onClick={() => stepBy(1)}
163
+ >
164
+ <RLSIcon name="arrowUp" size="xs" aria-hidden="true" />
165
+ </button>
166
+ <button
167
+ type="button"
168
+ tabIndex={-1}
169
+ aria-label="Decrement"
170
+ class="flex size-6 items-center justify-center rounded-sm text-muted hover:bg-muted/60 hover:text-highlighted disabled:cursor-not-allowed disabled:opacity-50"
171
+ data-slot="decrement"
172
+ disabled={props.disabled || props.readonly}
173
+ onClick={() => stepBy(-1)}
174
+ >
175
+ <RLSIcon name="arrowDown" size="xs" aria-hidden="true" />
176
+ </button>
177
+ </div>
178
+ </div>
179
+ {props.children}
180
+ </div>
181
+ )
182
+ }
183
+
184
+ export default RLSInputNumber