@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,145 @@
1
+ import { createMemo, createSignal, splitProps, onMount, onCleanup, type Component } from "solid-js"
2
+ import type { ScrollToTopProps } from "./scroll-to-top"
3
+ import { scrollToTopTheme } from "./scroll-to-top.theme"
4
+ import { trackScroll, scrollToTop } from "./scroll"
5
+
6
+ export interface RLSScrollToTopProps extends ScrollToTopProps {}
7
+
8
+ const SCROLL_TO_TOP_CSS = `
9
+ .rla-scroll-to-top-inner {
10
+ opacity: 0;
11
+ pointer-events: none;
12
+ transition: opacity 0.3s ease-in-out;
13
+ }
14
+ .rla-scroll-to-top-inner.js-visible {
15
+ opacity: 1;
16
+ pointer-events: auto;
17
+ }
18
+ .progress-circle-base {
19
+ position: relative;
20
+ transform: translateZ(0);
21
+ }
22
+ .gauge-track {
23
+ position: absolute;
24
+ inset: 0;
25
+ border-radius: 50%;
26
+ border-style: solid;
27
+ }
28
+ .gauge-progress {
29
+ position: absolute;
30
+ inset: 0;
31
+ border-radius: 50%;
32
+ background: conic-gradient(
33
+ from 0deg,
34
+ var(--gauge-color, var(--rl-primary-500)) 0deg,
35
+ var(--gauge-color, var(--rl-primary-500)) calc(var(--gauge-progress, 0) * 3.6deg),
36
+ transparent calc(var(--gauge-progress, 0) * 3.6deg)
37
+ );
38
+ -webkit-mask: radial-gradient(
39
+ circle closest-side,
40
+ transparent calc(100% - var(--gauge-stroke-width, 6px)),
41
+ #000 calc(100% - var(--gauge-stroke-width, 6px))
42
+ );
43
+ mask: radial-gradient(
44
+ circle closest-side,
45
+ transparent calc(100% - var(--gauge-stroke-width, 6px)),
46
+ #000 calc(100% - var(--gauge-stroke-width, 6px))
47
+ );
48
+ }
49
+ `
50
+
51
+ const RLSScrollToTop: Component<RLSScrollToTopProps> = (allProps) => {
52
+ const [props, rest] = splitProps(allProps, [
53
+ "color",
54
+ "progressWidth",
55
+ "duration",
56
+ "threshold",
57
+ "showProgress",
58
+ "theme",
59
+ "ui",
60
+ "class"
61
+ ])
62
+
63
+ const [isVisible, setIsVisible] = createSignal(false)
64
+ const [scrollPercentage, setScrollPercentage] = createSignal(0)
65
+
66
+ onMount(() => {
67
+ const unsubscribe = trackScroll({ threshold: props.threshold ?? 200 }, (state) => {
68
+ setIsVisible(state.isVisible)
69
+ setScrollPercentage(state.scrollPercentage)
70
+ })
71
+ onCleanup(unsubscribe)
72
+ })
73
+
74
+ const resolvedClasses = createMemo(() =>
75
+ scrollToTopTheme({
76
+ color: props.color ?? "primary",
77
+ ui: props.ui,
78
+ class: props.class
79
+ })
80
+ )
81
+
82
+ const color = () => props.color ?? "primary"
83
+ const progressWidth = () => props.progressWidth ?? 6
84
+ const gaugeBorderWidth = () => progressWidth() * 0.5
85
+
86
+ const progressStyle = () =>
87
+ ({
88
+ "--gauge-stroke-width": `${gaugeBorderWidth()}px`,
89
+ "--gauge-color": `var(--rl-${color()}-500)`,
90
+ "--gauge-progress": String(scrollPercentage())
91
+ }) as Record<string, string>
92
+
93
+ const trackStyle = () =>
94
+ ({
95
+ "border-width": `${gaugeBorderWidth()}px`,
96
+ "border-color": `var(--rl-${color()}-900)`,
97
+ "background-color": "#000"
98
+ }) as Record<string, string>
99
+
100
+ return (
101
+ <div
102
+ class={`rla-scroll-to-top-inner scroll-to-top-inner fixed bottom-6 right-6 z-50 ${resolvedClasses().root} ${
103
+ isVisible() ? "js-visible" : ""
104
+ }`}
105
+ data-slot="root"
106
+ data-theme={props.theme}
107
+ {...rest}
108
+ >
109
+ <button
110
+ type="button"
111
+ class={resolvedClasses().button}
112
+ aria-label="Scroll to top"
113
+ data-scroll-to-top-button
114
+ onClick={scrollToTop}
115
+ >
116
+ <div class={resolvedClasses().progressBase}>
117
+ <div class="gauge-track" style={trackStyle()} />
118
+ {props.showProgress ? (
119
+ <div class="gauge-progress" data-gauge-progress style={progressStyle()} />
120
+ ) : null}
121
+ <div class={resolvedClasses().iconContainer}>
122
+ <svg
123
+ xmlns="http://www.w3.org/2000/svg"
124
+ width="24"
125
+ height="24"
126
+ viewBox="0 0 24 24"
127
+ fill="none"
128
+ stroke="currentColor"
129
+ stroke-width="2"
130
+ stroke-linecap="round"
131
+ stroke-linejoin="round"
132
+ class={resolvedClasses().icon}
133
+ >
134
+ <path d="m5 12 7-7 7 7" />
135
+ <path d="M12 19V5" />
136
+ </svg>
137
+ </div>
138
+ </div>
139
+ </button>
140
+ <style>{SCROLL_TO_TOP_CSS}</style>
141
+ </div>
142
+ )
143
+ }
144
+
145
+ export default RLSScrollToTop
@@ -0,0 +1,237 @@
1
+ import {
2
+ createMemo,
3
+ createSignal,
4
+ For,
5
+ onCleanup,
6
+ onMount,
7
+ Show,
8
+ splitProps,
9
+ type Component,
10
+ type JSX
11
+ } from "solid-js"
12
+ import type { SearchProps } from "./search"
13
+ import { searchTheme } from "./search.theme"
14
+ import RLSIcon from "../icon/RLSIcon"
15
+ import RLSInput from "../input/RLSInput"
16
+ import RLSKbd from "../kbd/RLSKbd"
17
+ import RLSButton from "../button/RLSButton"
18
+ import { defineShortcuts } from "../../shortcuts"
19
+
20
+ export interface SearchResultItem {
21
+ title?: string
22
+ url?: string
23
+ sourceType?: string
24
+ snippet?: string
25
+ [key: string]: any
26
+ }
27
+
28
+ export interface RLSSearchProps extends SearchProps {
29
+ children?: JSX.Element
30
+ triggerSlot?: JSX.Element
31
+ }
32
+
33
+ const RLSSearch: Component<RLSSearchProps> = (allProps) => {
34
+ const [props, rest] = splitProps(allProps, [
35
+ "endpoint",
36
+ "placeholder",
37
+ "messages",
38
+ "ui",
39
+ "class",
40
+ "children",
41
+ "triggerSlot"
42
+ ])
43
+
44
+ const endpoint = () => props.endpoint ?? "/api/search"
45
+ const searchPlaceholder = () =>
46
+ props.messages?.searchPlaceholder || props.placeholder || "Search..."
47
+ const noResultsLabel = () => props.messages?.noResultsLabel || "No results found"
48
+ const footerCloseHint = () => props.messages?.footerCloseHint || "Escape to close"
49
+ const footerBackendHint = () => props.messages?.footerBackendHint || "Postgres Full-Text Search"
50
+
51
+ const [isOpen, setIsOpen] = createSignal(false)
52
+ const [query, setQuery] = createSignal("")
53
+ const [results, setResults] = createSignal<SearchResultItem[]>([])
54
+ const [hasSearched, setHasSearched] = createSignal(false)
55
+ const [loading, setLoading] = createSignal(false)
56
+
57
+ let inputRef: HTMLInputElement | null = null
58
+ let debounceTimer: ReturnType<typeof setTimeout> | undefined
59
+
60
+ const openModal = () => {
61
+ setIsOpen(true)
62
+ setTimeout(() => inputRef?.focus(), 50)
63
+ }
64
+
65
+ const closeModal = () => {
66
+ setIsOpen(false)
67
+ setQuery("")
68
+ setResults([])
69
+ setHasSearched(false)
70
+ if (debounceTimer) clearTimeout(debounceTimer)
71
+ }
72
+
73
+ onMount(() => {
74
+ const metaKCleanup = defineShortcuts({
75
+ meta_k: {
76
+ handler: () => openModal(),
77
+ label: "Open Search",
78
+ description: "Open the global site search modal",
79
+ category: "navigation"
80
+ }
81
+ })
82
+
83
+ const onKeyDown = (e: KeyboardEvent) => {
84
+ if (e.key === "Escape" && isOpen()) {
85
+ closeModal()
86
+ }
87
+ }
88
+
89
+ window.addEventListener("keydown", onKeyDown)
90
+
91
+ onCleanup(() => {
92
+ metaKCleanup()
93
+ window.removeEventListener("keydown", onKeyDown)
94
+ if (debounceTimer) clearTimeout(debounceTimer)
95
+ })
96
+ })
97
+
98
+ const handleInput = (val: string) => {
99
+ setQuery(val)
100
+ if (debounceTimer) clearTimeout(debounceTimer)
101
+
102
+ const trimmed = val.trim()
103
+ if (!trimmed) {
104
+ setResults([])
105
+ setHasSearched(false)
106
+ setLoading(false)
107
+ return
108
+ }
109
+
110
+ setLoading(true)
111
+ debounceTimer = setTimeout(async () => {
112
+ try {
113
+ const res = await fetch(`${endpoint()}?q=${encodeURIComponent(trimmed)}`)
114
+ if (!res.ok) throw new Error(`HTTP status ${res.status}`)
115
+ const data = await res.json()
116
+ setResults(Array.isArray(data) ? data : [])
117
+ setHasSearched(true)
118
+ } catch (err) {
119
+ console.error("[RLSSearch] Search query failed:", err)
120
+ setResults([])
121
+ setHasSearched(true)
122
+ } finally {
123
+ setLoading(false)
124
+ }
125
+ }, 200)
126
+ }
127
+
128
+ const resolvedClasses = createMemo(() =>
129
+ searchTheme({
130
+ ui: props.ui,
131
+ class: props.class
132
+ })
133
+ )
134
+
135
+ return (
136
+ <div class={`${resolvedClasses().root} rla-search-wrapper`} data-slot="root" {...rest}>
137
+ {props.triggerSlot ? (
138
+ <div class="cursor-pointer" onClick={openModal}>
139
+ {props.triggerSlot}
140
+ </div>
141
+ ) : props.children ? (
142
+ <div class="cursor-pointer" onClick={openModal}>
143
+ {props.children}
144
+ </div>
145
+ ) : (
146
+ <RLSButton
147
+ variant="ghost"
148
+ color="neutral"
149
+ square
150
+ aria-label="Open Search"
151
+ onClick={openModal}
152
+ >
153
+ <RLSIcon name="i-rl-search" class="w-5 h-5" />
154
+ </RLSButton>
155
+ )}
156
+
157
+ <Show when={isOpen()}>
158
+ <div
159
+ class={resolvedClasses().modal}
160
+ style={{ display: "flex" }}
161
+ data-slot="modal"
162
+ onClick={(e) => {
163
+ if (e.target === e.currentTarget) closeModal()
164
+ }}
165
+ >
166
+ <div class={resolvedClasses().dialog} data-slot="dialog">
167
+ <div class={resolvedClasses().header} data-slot="header">
168
+ <RLSInput
169
+ ref={(el) => (inputRef = el)}
170
+ variant="ghost"
171
+ type="text"
172
+ value={query()}
173
+ placeholder={searchPlaceholder()}
174
+ autocomplete="off"
175
+ onInput={(e) => handleInput(e.currentTarget.value)}
176
+ leading={<RLSIcon name="i-rl-search" class={resolvedClasses().headerIcon} />}
177
+ trailing={
178
+ <div class="flex items-center gap-2">
179
+ <RLSKbd size="sm">Esc</RLSKbd>
180
+ <button
181
+ type="button"
182
+ class={resolvedClasses().close}
183
+ aria-label="Close search"
184
+ onClick={closeModal}
185
+ >
186
+ <RLSIcon name="i-rl-close" class={resolvedClasses().closeIcon} />
187
+ </button>
188
+ </div>
189
+ }
190
+ ui={{
191
+ root: "relative flex items-center w-full",
192
+ input:
193
+ "pl-8 pr-20 py-1.5 text-base outline-none text-white placeholder-neutral-500",
194
+ leading: "absolute left-0 text-neutral-400 flex items-center",
195
+ trailing: "absolute right-0 flex items-center gap-2"
196
+ }}
197
+ />
198
+ </div>
199
+
200
+ <div class={resolvedClasses().results} data-slot="results">
201
+ <For each={results()}>
202
+ {(item) => (
203
+ <a
204
+ href={item.url || "#"}
205
+ class={resolvedClasses().resultItem}
206
+ onClick={closeModal}
207
+ >
208
+ <div class={resolvedClasses().resultHeader}>
209
+ <span class={resolvedClasses().resultTitle}>{item.title || "Untitled"}</span>
210
+ {item.sourceType && (
211
+ <span class={resolvedClasses().resultBadge}>{item.sourceType}</span>
212
+ )}
213
+ </div>
214
+ {item.snippet && <p class={resolvedClasses().resultSnippet}>{item.snippet}</p>}
215
+ </a>
216
+ )}
217
+ </For>
218
+ </div>
219
+
220
+ <Show when={hasSearched() && results().length === 0 && !loading()}>
221
+ <div class={resolvedClasses().empty} style={{ display: "block" }} data-slot="empty">
222
+ {noResultsLabel()}
223
+ </div>
224
+ </Show>
225
+
226
+ <div class={resolvedClasses().footer} data-slot="footer">
227
+ <span>{footerCloseHint()}</span>
228
+ <span>{footerBackendHint()}</span>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </Show>
233
+ </div>
234
+ )
235
+ }
236
+
237
+ export default RLSSearch
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import type { SelectProps, SelectItem } from "./select"
3
3
  import { selectTheme } from "./select.theme"
4
+ import RLAButton from "../button/RLAButton.astro"
4
5
  import RLAIcon from "../icon/RLAIcon.astro"
5
6
 
6
7
  const {
@@ -12,14 +13,24 @@ const {
12
13
  required = false,
13
14
  name,
14
15
  size = "md",
16
+ variant = "default",
17
+ buttonVariant = "ghost",
18
+ buttonColor = "neutral",
15
19
  multiple,
20
+ leadingIcon,
16
21
  ui: _ui,
17
22
  class: classAttr,
18
23
  theme,
19
24
  ...rest
20
25
  } = Astro.props as SelectProps
21
26
 
22
- const classes = selectTheme({ size, class: classAttr })
27
+ // Legacy prop values ("outline", "soft", "subtle", "ghost", "none") were never
28
+ // implemented by the theme; normalize them to the default look.
29
+ const resolvedVariant = variant === "default" || variant === "icon" ? variant : "default"
30
+
31
+ const classes = selectTheme({ size, variant: resolvedVariant, class: classAttr })
32
+
33
+ const hasLeading = !!(leadingIcon || Astro.slots.has("leading"))
23
34
 
24
35
  const currentValue = value !== undefined ? value : defaultValue !== undefined ? defaultValue : undefined
25
36
  const currentValues = Array.isArray(currentValue) ? currentValue.map(String) : undefined
@@ -43,15 +54,53 @@ const normalizedItems = (items || []).map((item: SelectItem) => {
43
54
  avatar: i.avatar
44
55
  }
45
56
  })
57
+
58
+ const selectedItem =
59
+ currentValue !== undefined && !Array.isArray(currentValue)
60
+ ? normalizedItems.find((item) => String(item.value) === String(currentValue))
61
+ : undefined
62
+ const currentLabel =
63
+ placeholder && (currentValue === undefined || (Array.isArray(currentValue) && currentValues?.length === 0))
64
+ ? placeholder
65
+ : selectedItem?.label ?? ""
46
66
  ---
47
67
 
48
68
  <div class={classes.root} data-slot="root" data-theme={theme}>
69
+ {resolvedVariant === "icon" ? (
70
+ <RLAButton
71
+ variant={buttonVariant}
72
+ color={buttonColor}
73
+ size={size}
74
+ theme={theme}
75
+ square
76
+ disabled={disabled}
77
+ leadingIcon={leadingIcon}
78
+ aria-hidden="true"
79
+ tabindex={-1}
80
+ type="button"
81
+ data-slot="trigger"
82
+ >
83
+ <slot name="leading" />
84
+ </RLAButton>
85
+ ) : hasLeading ? (
86
+ <span class={classes.leading} data-slot="leading">
87
+ {leadingIcon ? <RLAIcon name={leadingIcon} class="size-4" /> : <slot name="leading" />}
88
+ </span>
89
+ ) : null}
90
+
91
+ {resolvedVariant !== "icon" && (
92
+ <span class={classes.label} data-slot="label" aria-hidden="true">
93
+ {currentLabel}
94
+ </span>
95
+ )}
96
+
49
97
  <select
50
98
  class={classes.select}
51
99
  name={name}
52
100
  disabled={disabled}
53
101
  required={required}
54
102
  multiple={multiple}
103
+ aria-label={currentLabel}
55
104
  data-slot="select"
56
105
  {...rest}
57
106
  >
@@ -71,9 +120,11 @@ const normalizedItems = (items || []).map((item: SelectItem) => {
71
120
  ))}
72
121
  </select>
73
122
 
74
- <span class={classes.icon} data-slot="icon">
75
- <RLAIcon name="i-lucide-chevron-down" class="size-4" />
76
- </span>
123
+ {resolvedVariant !== "icon" && (
124
+ <span class={classes.icon} data-slot="icon">
125
+ <RLAIcon name="i-lucide-chevron-down" class="size-4" />
126
+ </span>
127
+ )}
77
128
  </div>
78
129
 
79
130
  <style is:global>
@@ -117,3 +168,15 @@ const normalizedItems = (items || []).map((item: SelectItem) => {
117
168
  font-weight: 500;
118
169
  }
119
170
  </style>
171
+
172
+ <script>
173
+ document.addEventListener("change", (event) => {
174
+ const target = event.target as HTMLSelectElement | null
175
+ if (!target || target.dataset.slot !== "select") return
176
+ const root = target.closest('[data-slot="root"]')
177
+ const label = root?.querySelector('[data-slot="label"]')
178
+ if (label) {
179
+ label.textContent = target.options[target.selectedIndex]?.text ?? ""
180
+ }
181
+ })
182
+ </script>