@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,27 @@
1
+ import { createMemo, splitProps, type Component, type JSX } from "solid-js"
2
+ import type { ListboxProps } from "./listbox"
3
+ import { listboxTheme } from "./listbox.theme"
4
+
5
+ export interface RLSListboxProps extends ListboxProps {
6
+ children?: JSX.Element
7
+ }
8
+
9
+ const RLSListbox: Component<RLSListboxProps> = (allProps) => {
10
+ const [props, rest] = splitProps(allProps, ["ui", "class", "children"])
11
+
12
+ const resolvedClasses = createMemo(() =>
13
+ listboxTheme({
14
+ ui: props.ui,
15
+ class: props.class,
16
+ ...allProps
17
+ })
18
+ )
19
+
20
+ return (
21
+ <div class={resolvedClasses().root} data-slot="root" {...rest}>
22
+ {props.children}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default RLSListbox
@@ -1,102 +1,110 @@
1
- ---
2
- import type { LocaleSelectorProps } from "./locale-selector"
3
- import { localeSelectorTheme } from "./locale-selector.theme"
4
- import { getRelativeLocaleUrlList, getRelativeLocaleUrl } from "astro:i18n"
5
- import RLASelect from "../select/RLASelect.astro"
6
-
7
- const {
8
- locales,
9
- currentLocale: currentLocaleProp,
10
- size = "md",
11
- theme,
12
- ...rest
13
- } = Astro.props as LocaleSelectorProps;
14
-
15
- const classes = localeSelectorTheme(Astro.props);
16
-
17
- // Resolve the active locale: explicit prop → Astro.currentLocale → 'en'
18
- const currentLocale = currentLocaleProp ?? Astro.currentLocale ?? 'en';
19
-
20
- // Normalise locales: accept strings, LocaleItem without href, or LocaleItem with href.
21
- // When no locales are supplied, fall back to Astro i18n auto-detection.
22
- const processedLocales = locales && locales.length > 0
23
- ? locales.map(item => typeof item === 'string' ? { code: item, label: item, href: undefined } : item)
24
- : [...new Map(getRelativeLocaleUrlList().map(url => {
25
- const locale = url.split('/')[1] || 'en';
26
- return [locale, { code: locale, label: locale, href: undefined }];
27
- })).values()];
28
-
29
- function getLocaleDisplayName(localeCode: string): string {
30
- try {
31
- const formatter = new Intl.DisplayNames([localeCode], { type: 'language' });
32
- const nativeName = formatter.of(localeCode);
33
-
34
- return nativeName
35
- ? nativeName.charAt(0).toUpperCase() + nativeName.slice(1)
36
- : localeCode.toUpperCase();
37
- } catch (e) {
38
- return localeCode.toUpperCase();
39
- }
40
- }
41
-
42
- const items = processedLocales.map((item) => {
43
- // If the caller supplied an explicit href, use it directly.
44
- // Otherwise fall back to Astro's getRelativeLocaleUrl, preserving the
45
- // current path and query string.
46
- let targetUrl: string;
47
- if (item.href !== undefined && item.href !== null) {
48
- targetUrl = item.href + Astro.url.search;
49
- } else {
50
- const basePath = Astro.url.pathname.replace(/^\/[^/]+/, '') || '/';
51
- targetUrl = getRelativeLocaleUrl(item.code, basePath) + Astro.url.search;
52
- }
53
-
54
- return {
55
- label: item.label !== item.code ? item.label : getLocaleDisplayName(item.code),
56
- value: targetUrl,
57
- code: item.code
58
- };
59
- });
60
-
61
- const currentLocaleUrl = items.find(item => item.code === currentLocale)?.value || "";
62
- ---
63
-
64
- <rla-locale-selector data-theme={theme} {...rest}>
65
- <div class:list={[classes.root]} data-locale-selector>
66
- {Astro.slots.has("label") && (
67
- <label class:list={[classes.label]}>
68
- <slot name="label" />
69
- </label>
70
- )}
71
-
72
- <div class:list={[classes.wrapper]}>
73
- <RLASelect
74
- items={items}
75
- value={currentLocaleUrl}
76
- size={size}
77
- theme={theme}
78
- />
79
- </div>
80
- </div>
81
- </rla-locale-selector>
82
-
83
- <script>
84
- class RLALocaleSelector extends HTMLElement {
85
- connectedCallback() {
86
- this.style.display = 'contents';
87
- this.addEventListener('change', (event) => {
88
- const target = event.target as HTMLSelectElement;
89
- if (target && target.tagName === 'SELECT') {
90
- const targetUrl = target.value;
91
- if (targetUrl) {
92
- window.location.href = targetUrl;
93
- }
94
- }
95
- });
96
- }
97
- }
98
-
99
- if (!customElements.get('rla-locale-selector')) {
100
- customElements.define('rla-locale-selector', RLALocaleSelector);
101
- }
102
- </script>
1
+ ---
2
+ import type { LocaleSelectorProps, LocaleItem } from "./locale-selector"
3
+ import { localeSelectorTheme } from "./locale-selector.theme"
4
+ import RLASelect from "../select/RLASelect.astro"
5
+
6
+ const {
7
+ locales = [],
8
+ currentLocale = "en",
9
+ size = "md",
10
+ theme,
11
+ class: classAttr,
12
+ ...rest
13
+ } = Astro.props as LocaleSelectorProps;
14
+
15
+ const classes = localeSelectorTheme(Astro.props);
16
+
17
+ function getLocaleDisplayName(localeCode: string): string {
18
+ try {
19
+ const formatter = new Intl.DisplayNames([localeCode], { type: 'language' });
20
+ const nativeName = formatter.of(localeCode);
21
+ return nativeName
22
+ ? nativeName.charAt(0).toUpperCase() + nativeName.slice(1)
23
+ : localeCode.toUpperCase();
24
+ } catch {
25
+ return localeCode.toUpperCase();
26
+ }
27
+ }
28
+
29
+ // Normalize locales: string or LocaleItem
30
+ const processedLocales: LocaleItem[] = locales.map(item =>
31
+ typeof item === 'string'
32
+ ? { code: item, label: getLocaleDisplayName(item) }
33
+ : { ...item, label: item.label || getLocaleDisplayName(item.code) }
34
+ );
35
+
36
+ const items = processedLocales.map((item) => {
37
+ let targetUrl: string;
38
+ if (item.href !== undefined && item.href !== null) {
39
+ targetUrl = item.href + (item.href.includes("?") ? "" : Astro.url.search);
40
+ } else {
41
+ // Derive the known locale codes from the props list so we can detect
42
+ // whether the first URL segment is a locale prefix or a real path segment.
43
+ const knownCodes = new Set(processedLocales.map((l) => l.code));
44
+
45
+ const pathname = Astro.url.pathname;
46
+ const segments = pathname.split("/").filter(Boolean);
47
+ const firstIsLocale = segments.length > 0 && knownCodes.has(segments[0]!);
48
+
49
+ if (firstIsLocale) {
50
+ // Replace existing locale prefix with the target locale.
51
+ segments[0] = item.code;
52
+ targetUrl = `/${segments.join("/")}${pathname.endsWith("/") ? "/" : ""}${Astro.url.search}`;
53
+ } else if (segments.length > 0) {
54
+ // No locale prefix in the current URL — prepend the target locale.
55
+ targetUrl = `/${item.code}/${segments.join("/")}${pathname.endsWith("/") ? "/" : ""}${Astro.url.search}`;
56
+ } else {
57
+ // Root path.
58
+ targetUrl = `/${item.code}/${Astro.url.search}`;
59
+ }
60
+ }
61
+
62
+ return {
63
+ label: item.label,
64
+ value: targetUrl,
65
+ code: item.code
66
+ };
67
+ });
68
+
69
+ const currentLocaleUrl = items.find(item => item.code === currentLocale)?.value || "";
70
+ ---
71
+
72
+ <rla-locale-selector data-theme={theme} {...rest}>
73
+ <div class:list={[classes.root, classAttr]} data-locale-selector>
74
+ {Astro.slots.has("label") && (
75
+ <label class:list={[classes.label]}>
76
+ <slot name="label" />
77
+ </label>
78
+ )}
79
+
80
+ <div class:list={[classes.wrapper]}>
81
+ <RLASelect
82
+ items={items}
83
+ value={currentLocaleUrl}
84
+ size={size}
85
+ theme={theme}
86
+ />
87
+ </div>
88
+ </div>
89
+ </rla-locale-selector>
90
+
91
+ <script>
92
+ class RLALocaleSelector extends HTMLElement {
93
+ connectedCallback() {
94
+ this.style.display = 'contents';
95
+ this.addEventListener('change', (event) => {
96
+ const target = event.target as HTMLSelectElement;
97
+ if (target && target.tagName === 'SELECT') {
98
+ const targetUrl = target.value;
99
+ if (targetUrl) {
100
+ window.location.href = targetUrl;
101
+ }
102
+ }
103
+ });
104
+ }
105
+ }
106
+
107
+ if (!customElements.get('rla-locale-selector')) {
108
+ customElements.define('rla-locale-selector', RLALocaleSelector);
109
+ }
110
+ </script>
@@ -0,0 +1,178 @@
1
+ import {
2
+ createMemo,
3
+ createSignal,
4
+ splitProps,
5
+ onMount,
6
+ onCleanup,
7
+ For,
8
+ Show,
9
+ type Component,
10
+ type JSX
11
+ } from "solid-js"
12
+ import type { LocaleSelectorProps, LocaleItem } from "./locale-selector"
13
+ import { localeSelectorTheme } from "./locale-selector.theme"
14
+ import RLSIcon from "../icon/RLSIcon"
15
+
16
+ export interface RLSLocaleSelectorProps extends LocaleSelectorProps {
17
+ children?: JSX.Element
18
+ labelSlot?: JSX.Element
19
+ onSelect?: (item: LocaleItem) => void
20
+ }
21
+
22
+ const getLocaleDisplayName = (localeCode: string): string => {
23
+ try {
24
+ const formatter = new Intl.DisplayNames([localeCode], { type: "language" })
25
+ const nativeName = formatter.of(localeCode)
26
+ return nativeName
27
+ ? nativeName.charAt(0).toUpperCase() + nativeName.slice(1)
28
+ : localeCode.toUpperCase()
29
+ } catch {
30
+ return localeCode.toUpperCase()
31
+ }
32
+ }
33
+
34
+ const RLSLocaleSelector: Component<RLSLocaleSelectorProps> = (allProps) => {
35
+ const [props, rest] = splitProps(allProps, [
36
+ "locales",
37
+ "currentLocale",
38
+ "variant",
39
+ "size",
40
+ "theme",
41
+ "ui",
42
+ "class",
43
+ "children",
44
+ "labelSlot",
45
+ "onSelect"
46
+ ])
47
+
48
+ const [isOpen, setIsOpen] = createSignal(false)
49
+ let containerRef: HTMLDivElement | undefined
50
+
51
+ onMount(() => {
52
+ const handleOutsideClick = (e: MouseEvent) => {
53
+ if (isOpen() && containerRef && !containerRef.contains(e.target as Node)) {
54
+ setIsOpen(false)
55
+ }
56
+ }
57
+ document.addEventListener("click", handleOutsideClick)
58
+ onCleanup(() => document.removeEventListener("click", handleOutsideClick))
59
+ })
60
+
61
+ const currentCode = (): string => props.currentLocale ?? "en"
62
+
63
+ const processedLocales = createMemo<LocaleItem[]>(() =>
64
+ (props.locales ?? []).map((item) =>
65
+ typeof item === "string"
66
+ ? { code: item, label: getLocaleDisplayName(item) }
67
+ : { ...item, label: item.label || getLocaleDisplayName(item.code) }
68
+ )
69
+ )
70
+
71
+ const items = createMemo(() =>
72
+ processedLocales().map((item) => {
73
+ let targetUrl: string
74
+ const search = typeof window !== "undefined" ? window.location.search : ""
75
+ const pathname = typeof window !== "undefined" ? window.location.pathname : "/"
76
+ if (item.href !== undefined && item.href !== null) {
77
+ targetUrl = item.href + (item.href.includes("?") ? "" : search)
78
+ } else {
79
+ const knownCodes = new Set(processedLocales().map((l) => l.code))
80
+ const segments = pathname.split("/").filter(Boolean)
81
+ const firstIsLocale = segments.length > 0 && knownCodes.has(segments[0]!)
82
+
83
+ if (firstIsLocale) {
84
+ segments[0] = item.code
85
+ targetUrl = `/${segments.join("/")}${pathname.endsWith("/") ? "/" : ""}${search}`
86
+ } else if (segments.length > 0) {
87
+ targetUrl = `/${item.code}/${segments.join("/")}${pathname.endsWith("/") ? "/" : ""}${search}`
88
+ } else {
89
+ targetUrl = `/${item.code}/${search}`
90
+ }
91
+ }
92
+ return { label: item.label, value: targetUrl, code: item.code }
93
+ })
94
+ )
95
+
96
+ const currentItem = createMemo(() => items().find((item) => item.code === currentCode()))
97
+
98
+ const resolvedClasses = createMemo(() =>
99
+ localeSelectorTheme({
100
+ variant: props.variant ?? "default",
101
+ size: props.size ?? "md",
102
+ theme: props.theme,
103
+ ui: props.ui,
104
+ class: props.class
105
+ })
106
+ )
107
+
108
+ const selectLocale = (item: { label: string; value: string; code: string }) => {
109
+ setIsOpen(false)
110
+ props.onSelect?.({ label: item.label, code: item.code, href: item.value })
111
+ if (item.value) {
112
+ window.location.href = item.value
113
+ }
114
+ }
115
+
116
+ return (
117
+ <div
118
+ ref={(el) => (containerRef = el)}
119
+ class={resolvedClasses().root}
120
+ data-slot="root"
121
+ data-theme={props.theme}
122
+ {...rest}
123
+ >
124
+ <Show when={props.labelSlot}>
125
+ <label class={resolvedClasses().label} data-slot="label">
126
+ {props.labelSlot}
127
+ </label>
128
+ </Show>
129
+
130
+ <div class={resolvedClasses().wrapper} data-slot="wrapper">
131
+ <button
132
+ type="button"
133
+ class={`${resolvedClasses().select} flex items-center justify-between gap-2 text-left`}
134
+ data-slot="trigger"
135
+ aria-haspopup="listbox"
136
+ aria-expanded={isOpen() ? "true" : "false"}
137
+ onClick={() => setIsOpen(!isOpen())}
138
+ >
139
+ <span class="truncate">{currentItem()?.label || currentCode()}</span>
140
+ <span class={resolvedClasses().iconWrapper}>
141
+ <RLSIcon name="chevronDown" size="sm" aria-hidden="true" />
142
+ </span>
143
+ </button>
144
+
145
+ <Show when={isOpen()}>
146
+ <div
147
+ class="absolute z-50 mt-1 w-full overflow-hidden rounded-lg border border-default bg-default py-1 shadow-xl"
148
+ data-slot="options"
149
+ role="listbox"
150
+ >
151
+ <For each={items()}>
152
+ {(item) => {
153
+ const selected = item.code === currentCode()
154
+ return (
155
+ <button
156
+ type="button"
157
+ class={`${resolvedClasses().option} flex w-full items-center justify-between gap-2 px-3 py-2 text-left text-sm transition-colors hover:bg-accented/50 ${selected ? "font-medium" : ""}`}
158
+ data-slot="option"
159
+ role="option"
160
+ aria-selected={selected ? "true" : "false"}
161
+ onClick={() => selectLocale(item)}
162
+ >
163
+ <span class="truncate">{item.label}</span>
164
+ <Show when={selected}>
165
+ <RLSIcon name="check" size="xs" class="shrink-0" aria-hidden="true" />
166
+ </Show>
167
+ </button>
168
+ )
169
+ }}
170
+ </For>
171
+ </div>
172
+ </Show>
173
+ </div>
174
+ </div>
175
+ )
176
+ }
177
+
178
+ export default RLSLocaleSelector
@@ -4,13 +4,8 @@ export interface LocaleItem {
4
4
  /**
5
5
  * The URL to navigate to when this locale is selected.
6
6
  *
7
- * Supply this to make the component fully routing-agnostic: it will use `href` directly and will
8
- * not call any Astro i18n APIs. The caller is responsible for constructing URLs using whatever
9
- * strategy their project uses — `getRelativeLocaleUrl`, `@rimelight/i18n`, a custom helper, or a
10
- * plain string.
11
- *
12
- * When omitted the component falls back to computing the URL via Astro's built-in
13
- * `getRelativeLocaleUrl` helper (requires Astro i18n to be configured).
7
+ * Supply this to make the component fully routing-agnostic: it will use `href` directly. When
8
+ * omitted, the component falls back to updating the first URL segment to match the locale code.
14
9
  */
15
10
  href?: string
16
11
  }