@rimelight/ui 0.0.34 → 0.0.35

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 (185) hide show
  1. package/README.md +1 -0
  2. package/package.json +19 -24
  3. package/src/components/astro/RLAAccordion.astro +94 -18
  4. package/src/components/astro/RLAAvatar.astro +135 -68
  5. package/src/components/astro/RLABadge.astro +4 -2
  6. package/src/components/astro/RLABanner.astro +138 -23
  7. package/src/components/astro/RLABreadcrumb.astro +13 -0
  8. package/src/components/astro/RLAButton.astro +8 -4
  9. package/src/components/astro/RLACallout.astro +112 -0
  10. package/src/components/astro/RLACard.astro +6 -5
  11. package/src/components/astro/RLACarousel.astro +104 -75
  12. package/src/components/astro/RLAChart.astro +4 -1
  13. package/src/components/astro/RLAChip.astro +11 -5
  14. package/src/components/astro/RLACollapsible.astro +4 -1
  15. package/src/components/astro/RLAColorArea.astro +432 -0
  16. package/src/components/astro/RLAColorField.astro +300 -0
  17. package/src/components/astro/RLAColorSlider.astro +413 -0
  18. package/src/components/astro/RLAConfirm.astro +13 -11
  19. package/src/components/astro/RLACopyMarkdown.astro +78 -0
  20. package/src/components/astro/RLADate.astro +30 -0
  21. package/src/components/astro/RLADrawer.astro +50 -3
  22. package/src/components/astro/RLADropdownMenu.astro +78 -51
  23. package/src/components/astro/RLAFieldGroup.astro +4 -1
  24. package/src/components/astro/RLAFileUpload.astro +114 -100
  25. package/src/components/astro/RLAFooter.astro +2 -1
  26. package/src/components/astro/RLAFormField.astro +10 -6
  27. package/src/components/astro/RLAGrid.astro +31 -39
  28. package/src/components/astro/RLAHead.astro +139 -39
  29. package/src/components/astro/RLAHeader.astro +2 -1
  30. package/src/components/astro/RLAImage.astro +274 -251
  31. package/src/components/astro/RLAInput.astro +14 -9
  32. package/src/components/astro/RLAInputMenu.astro +119 -83
  33. package/src/components/astro/RLAKbd.astro +5 -5
  34. package/src/components/astro/RLALayoutGrid.astro +395 -0
  35. package/src/components/astro/RLALink.astro +23 -1
  36. package/src/components/astro/RLALinkGroup.astro +99 -99
  37. package/src/components/astro/RLALocaleSelector.astro +29 -35
  38. package/src/components/astro/RLALogo.astro +117 -90
  39. package/src/components/astro/RLAModal.astro +48 -18
  40. package/src/components/astro/RLANavigationMenu.astro +381 -326
  41. package/src/components/astro/{RLADocsPackageManagers.astro → RLAPackageManagers.astro} +81 -87
  42. package/src/components/astro/RLAPageSection.astro +4 -2
  43. package/src/components/astro/RLAPopover.astro +4 -3
  44. package/src/components/astro/RLAPost.astro +11 -2
  45. package/src/components/astro/RLAScrollArea.astro +4 -1
  46. package/src/components/astro/RLAScrollToTop.astro +200 -172
  47. package/src/components/astro/RLASearch.astro +282 -0
  48. package/src/components/astro/RLASelect.astro +78 -55
  49. package/src/components/astro/RLASeparator.astro +11 -6
  50. package/src/components/astro/RLAShare.astro +60 -0
  51. package/src/components/astro/RLAShortcuts.astro +429 -0
  52. package/src/components/astro/RLASidebar.astro +158 -212
  53. package/src/components/astro/RLASlideover.astro +47 -17
  54. package/src/components/astro/RLASplitter.astro +194 -141
  55. package/src/components/astro/RLASteps.astro +118 -71
  56. package/src/components/astro/RLASurround.astro +39 -0
  57. package/src/components/astro/RLATable.astro +4 -1
  58. package/src/components/astro/RLATableOfContents.astro +109 -23
  59. package/src/components/astro/RLATabs.astro +147 -133
  60. package/src/components/astro/RLATextarea.astro +4 -1
  61. package/src/components/astro/RLAThemeSelector.astro +105 -76
  62. package/src/components/astro/RLAToast.astro +35 -21
  63. package/src/components/vue/RLVBanner.vue +230 -0
  64. package/src/components/vue/RLVButton.vue +3 -2
  65. package/src/components/vue/RLVCheckbox.vue +4 -2
  66. package/src/components/vue/RLVFooter.vue +3 -2
  67. package/src/components/vue/RLVHeader.vue +3 -2
  68. package/src/components/vue/RLVIcon.vue +4 -2
  69. package/src/components/vue/RLVInput.vue +5 -4
  70. package/src/components/vue/RLVLogo.vue +25 -3
  71. package/src/components/vue/RLVPlaceholder.vue +3 -2
  72. package/src/components/vue/RLVPost.vue +8 -2
  73. package/src/components/vue/RLVProgress.vue +60 -0
  74. package/src/components/vue/RLVScrollToTop.vue +65 -52
  75. package/src/components/vue/RLVSection.vue +14 -13
  76. package/src/components/vue/RLVToast.vue +17 -4
  77. package/src/composables/index.ts +1 -0
  78. package/src/composables/useHeaderStore.ts +1 -0
  79. package/src/composables/useShortcuts.ts +11 -0
  80. package/src/config/security.ts +43 -36
  81. package/src/config/site.config.ts +1 -0
  82. package/src/docs/components/ApiChangelog.astro +147 -0
  83. package/src/{plugins/starlightDocsApi → docs}/components/ApiEmits.astro +4 -4
  84. package/src/{plugins/starlightDocsApi → docs}/components/ApiProps.astro +4 -4
  85. package/src/{plugins/starlightDocsApi → docs}/components/ApiSlots.astro +4 -4
  86. package/src/docs/components/ApiTheme.astro +100 -0
  87. package/src/docs/extractAll.ts +94 -0
  88. package/src/docs/extractors/extractAstro.ts +69 -0
  89. package/src/docs/extractors/extractTheme.ts +56 -0
  90. package/src/{plugins/starlightDocsApi/extract-vue.ts → docs/extractors/extractVue.ts} +164 -15
  91. package/src/{plugins/starlightDocsApi → docs}/utils.ts +1 -2
  92. package/src/env.d.ts +0 -12
  93. package/src/integrations/ui.ts +35 -19
  94. package/src/middleware/security.ts +75 -19
  95. package/src/presets/index.ts +118 -293
  96. package/src/styles/index.css +0 -2
  97. package/src/themes/accordion.theme.ts +29 -3
  98. package/src/themes/aspect-ratio.theme.ts +1 -1
  99. package/src/themes/avatar-group.theme.ts +1 -1
  100. package/src/themes/avatar.theme.ts +2 -2
  101. package/src/themes/badge.theme.ts +2 -3
  102. package/src/themes/banner.theme.ts +40 -4
  103. package/src/themes/breadcrumb.theme.ts +1 -1
  104. package/src/themes/button.theme.ts +21 -22
  105. package/src/themes/{alert.theme.ts → callout.theme.ts} +12 -39
  106. package/src/themes/card.theme.ts +17 -9
  107. package/src/themes/carousel.theme.ts +1 -1
  108. package/src/themes/checkbox.theme.ts +1 -2
  109. package/src/themes/chip.theme.ts +2 -3
  110. package/src/themes/color-area.theme.ts +33 -0
  111. package/src/themes/color-field.theme.ts +32 -0
  112. package/src/themes/color-slider.theme.ts +34 -0
  113. package/src/themes/confirm.theme.ts +1 -1
  114. package/src/themes/container.theme.ts +1 -1
  115. package/src/themes/drawer.theme.ts +1 -1
  116. package/src/themes/footer.theme.ts +2 -2
  117. package/src/themes/grid.theme.ts +38 -0
  118. package/src/themes/header.theme.ts +4 -3
  119. package/src/themes/hover-card.theme.ts +1 -1
  120. package/src/themes/image.theme.ts +11 -1
  121. package/src/themes/input.theme.ts +7 -1
  122. package/src/themes/kbd.theme.ts +2 -3
  123. package/src/themes/label.theme.ts +1 -1
  124. package/src/themes/layout-grid.theme.ts +16 -0
  125. package/src/themes/link-group.theme.ts +0 -4
  126. package/src/themes/marquee.theme.ts +1 -1
  127. package/src/themes/modal.theme.ts +1 -1
  128. package/src/themes/navigation-menu.theme.ts +2 -2
  129. package/src/themes/page-section.theme.ts +4 -2
  130. package/src/themes/pagination.theme.ts +1 -1
  131. package/src/themes/pin-input.theme.ts +1 -1
  132. package/src/themes/popover.theme.ts +1 -1
  133. package/src/themes/post.theme.ts +23 -3
  134. package/src/themes/progress.theme.ts +1 -2
  135. package/src/themes/scroll-to-top.theme.ts +2 -3
  136. package/src/themes/search.theme.ts +64 -0
  137. package/src/themes/separator.theme.ts +1 -1
  138. package/src/themes/slideover.theme.ts +1 -1
  139. package/src/themes/slider.theme.ts +1 -2
  140. package/src/themes/spinner.theme.ts +1 -2
  141. package/src/themes/{megamenu.theme.ts → steps.theme.ts} +3 -3
  142. package/src/themes/sticky-surface.theme.ts +1 -1
  143. package/src/themes/switch.theme.ts +1 -2
  144. package/src/themes/table-of-contents.theme.ts +21 -2
  145. package/src/themes/timeline.theme.ts +7 -2
  146. package/src/themes/toast.theme.ts +1 -1
  147. package/src/types/components/accordion.ts +35 -2
  148. package/src/types/components/avatar.ts +16 -0
  149. package/src/types/components/banner.ts +42 -0
  150. package/src/types/components/button.ts +3 -4
  151. package/src/types/components/{alert.ts → callout.ts} +6 -6
  152. package/src/types/components/color-area.ts +19 -0
  153. package/src/types/components/color-field.ts +21 -0
  154. package/src/types/components/color-slider.ts +20 -0
  155. package/src/types/components/grid.ts +23 -0
  156. package/src/types/components/input.ts +4 -0
  157. package/src/types/components/layout-grid.ts +145 -0
  158. package/src/types/components/link-group.ts +4 -2
  159. package/src/types/components/link.ts +15 -0
  160. package/src/types/components/logo.ts +3 -3
  161. package/src/types/components/post.ts +12 -0
  162. package/src/types/components/search.ts +29 -0
  163. package/src/types/components/sidebar.ts +2 -6
  164. package/src/types/components/{megamenu.ts → steps.ts} +7 -1
  165. package/src/types/components/table-of-contents.ts +22 -0
  166. package/src/types/components/toast.ts +4 -0
  167. package/src/types/docs.ts +40 -0
  168. package/src/types/index.ts +151 -1
  169. package/src/types/theme.ts +6 -6
  170. package/src/utils/color.ts +165 -0
  171. package/src/utils/docs.ts +260 -110
  172. package/src/utils/headerStack.ts +24 -4
  173. package/src/utils/index.ts +2 -0
  174. package/src/utils/shortcuts.ts +322 -0
  175. package/src/components/TableOfContents.astro +0 -107
  176. package/src/components/astro/RLAAlert.astro +0 -93
  177. package/src/components/astro/RLAChangelog.astro +0 -23
  178. package/src/components/astro/RLAMegamenu.astro +0 -23
  179. package/src/plugins/index.ts +0 -2
  180. package/src/plugins/starlightDocsApi/components/ApiTheme.astro +0 -27
  181. package/src/plugins/starlightDocsApi/index.ts +0 -272
  182. package/src/styles/prism.css +0 -42
  183. package/src/themes/changelog.theme.ts +0 -13
  184. package/src/types/components/index.ts +0 -134
  185. /package/src/{plugins/starlightDocsApi → docs}/types.ts +0 -0
@@ -7,16 +7,38 @@ const {
7
7
  href,
8
8
  variant = "link",
9
9
  color = "neutral",
10
+ external,
11
+ isButton = false,
12
+ ariaLabel,
13
+ externalIcon,
14
+ target,
15
+ rel,
16
+ trailingIcon,
10
17
  ...rest
11
18
  } = Astro.props as LinkProps
12
19
 
13
20
  const linkHref = href || (typeof to === 'string' ? to : undefined);
21
+
22
+ const isExternal = !!(external || (linkHref && (linkHref.startsWith('http://') || linkHref.startsWith('https://') || linkHref.startsWith('//'))));
23
+
24
+ const linkVariant = isButton && variant === "link" ? "solid" : variant;
25
+ const linkTarget = target || (isExternal ? "_blank" : undefined);
26
+ const linkRel = rel || (isExternal ? "noopener noreferrer" : undefined);
27
+
28
+ const showExternalIcon = isExternal && !trailingIcon ? (externalIcon || "i-lucide-external-link") : trailingIcon;
14
29
  ---
15
30
  <RLAButton
16
- variant={variant as any}
31
+ variant={linkVariant as any}
17
32
  color={color as any}
18
33
  href={linkHref}
34
+ target={linkTarget}
35
+ rel={linkRel}
36
+ aria-label={ariaLabel}
37
+ trailingIcon={showExternalIcon}
19
38
  {...rest}
20
39
  >
21
40
  <slot />
41
+ {isExternal && !ariaLabel && (
42
+ <span class="sr-only"> (opens in a new tab)</span>
43
+ )}
22
44
  </RLAButton>
@@ -1,100 +1,100 @@
1
- ---
2
- import { scv } from "css-variants"
3
- import { useUi, resolveClasses } from "../../utils"
4
- import type { LinkGroupProps } from "../../types"
5
- import linkGroupThemeDefault, { createLinkGroupTheme } from "../../themes/link-group.theme"
6
- import { getUIConfig } from "virtual:rimelight-ui"
7
-
8
- import RLALink from "./RLALink.astro"
9
-
10
- const uiConfig = getUIConfig()
11
- const linkGroupTheme = uiConfig.colors ? createLinkGroupTheme() : linkGroupThemeDefault
12
-
13
- const linkGroup = scv(linkGroupTheme)
14
-
15
- const {
16
- title,
17
- label,
18
- leadingIcon,
19
- trailingIcon,
20
- href,
21
- to,
22
- links = [],
23
- theme,
24
- ui: uiProp,
25
- class: className,
26
- ...rest
27
- } = Astro.props as LinkGroupProps
28
-
29
- const classes = resolveClasses(linkGroup, { theme }, useUi("linkGroup", uiProp), className);
30
-
31
- const headingLabel = label || (typeof title === "object" ? title.label : (typeof title === "string" ? title : undefined));
32
- const headingLeadingIcon = leadingIcon || (typeof title === "object" ? title.leadingIcon : undefined);
33
- const headingTrailingIcon = trailingIcon || (typeof title === "object" ? title.trailingIcon : undefined);
34
- const headingHref = href || (typeof title === "object" ? title.href : undefined);
35
- const headingTo = to || (typeof title === "object" ? (title as any).to : undefined);
36
-
37
- const linksProp = links ?? [];
38
- const normalizedLinks = Array.isArray(linksProp) ? linksProp : [linksProp];
39
-
40
- // Prefix utility classes in classes.link with `!` to cleanly override conflicting text colors in RLAButton's base classes.
41
- // For default or light themes, do not force base text classes so RLAButton's own light/dark mode styles can adapt naturally.
42
- const isPinnedDark = theme === "flat" || theme === "dark";
43
- const overrideLinkClass = classes.link
44
- ? classes.link
45
- .split(" ")
46
- .map(cls => {
47
- if (!cls) return "";
48
- if (cls.includes(":")) {
49
- const parts = cls.split(":");
50
- const state = parts.slice(0, -1).join(":");
51
- const baseCls = parts[parts.length - 1];
52
- return `${state}:!${baseCls}`;
53
- }
54
- if (cls.startsWith("text-") && !isPinnedDark) {
55
- return cls;
56
- }
57
- return `!${cls}`;
58
- })
59
- .filter(Boolean)
60
- .join(" ")
61
- : "";
62
- ---
63
- <div class={classes.root} data-slot="root" {...rest}>
64
- {headingLabel && (
65
- <h4 class={classes.title} data-slot="title">
66
- {headingHref || headingTo ? (
67
- <a href={headingHref || (typeof headingTo === 'string' ? headingTo : undefined)} class="hover:underline flex items-center gap-1.5">
68
- {headingLeadingIcon && <span class={headingLeadingIcon} />}
69
- <span>{headingLabel}</span>
70
- {headingTrailingIcon && <span class={headingTrailingIcon} />}
71
- </a>
72
- ) : (
73
- <span class="flex items-center gap-1.5">
74
- {headingLeadingIcon && <span class={headingLeadingIcon} />}
75
- <span>{headingLabel}</span>
76
- {headingTrailingIcon && <span class={headingTrailingIcon} />}
77
- </span>
78
- )}
79
- </h4>
80
- )}
81
-
82
- <ul class={classes.list} data-slot="list">
83
- <slot>
84
- {normalizedLinks.map((link) => (
85
- <li class={classes.item} data-slot="item">
86
- <RLALink
87
- variant={link.variant ?? "ghost"}
88
- color={link.color ?? "neutral"}
89
- theme={link.theme ?? theme}
90
- class:list={[overrideLinkClass, "w-full !justify-start"]}
91
- data-slot="link"
92
- {...link}
93
- >
94
- {link.label}
95
- </RLALink>
96
- </li>
97
- ))}
98
- </slot>
99
- </ul>
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { LinkGroupProps } from "../../types"
5
+ import linkGroupThemeDefault, { createLinkGroupTheme } from "../../themes/link-group.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ import RLALink from "./RLALink.astro"
9
+
10
+ const uiConfig = getUIConfig()
11
+ const linkGroupTheme = uiConfig.colors ? createLinkGroupTheme() : linkGroupThemeDefault
12
+
13
+ const linkGroup = scv(linkGroupTheme)
14
+
15
+ const {
16
+ title,
17
+ label,
18
+ leadingIcon,
19
+ trailingIcon,
20
+ href,
21
+ to,
22
+ links = [],
23
+ theme,
24
+ ui: uiProp,
25
+ class: className,
26
+ ...rest
27
+ } = Astro.props as LinkGroupProps
28
+
29
+ const classes = resolveClasses(linkGroup, { theme }, useUi("linkGroup", uiProp), className);
30
+
31
+ const headingLabel = label || (typeof title === "object" ? title.label : (typeof title === "string" ? title : undefined));
32
+ const headingLeadingIcon = leadingIcon || (typeof title === "object" ? title.leadingIcon : undefined);
33
+ const headingTrailingIcon = trailingIcon || (typeof title === "object" ? title.trailingIcon : undefined);
34
+ const headingHref = href || (typeof title === "object" ? title.href : undefined);
35
+ const headingTo = to || (typeof title === "object" ? (title as any).to : undefined);
36
+
37
+ const linksProp = links ?? [];
38
+ const normalizedLinks = Array.isArray(linksProp) ? linksProp : [linksProp];
39
+
40
+ // Prefix utility classes in classes.link with `!` to cleanly override conflicting text colors in RLAButton's base classes.
41
+ // For default or light themes, do not force base text classes so RLAButton's own light/dark mode styles can adapt naturally.
42
+ const isPinnedDark = theme === "dark";
43
+ const overrideLinkClass = classes.link
44
+ ? classes.link
45
+ .split(" ")
46
+ .map(cls => {
47
+ if (!cls) return "";
48
+ if (cls.includes(":")) {
49
+ const parts = cls.split(":");
50
+ const state = parts.slice(0, -1).join(":");
51
+ const baseCls = parts[parts.length - 1];
52
+ return `${state}:!${baseCls}`;
53
+ }
54
+ if (cls.startsWith("text-") && !isPinnedDark) {
55
+ return cls;
56
+ }
57
+ return `!${cls}`;
58
+ })
59
+ .filter(Boolean)
60
+ .join(" ")
61
+ : "";
62
+ ---
63
+ <div class={classes.root} data-slot="root" {...rest}>
64
+ {headingLabel && (
65
+ <h4 class={classes.title} data-slot="title">
66
+ {headingHref || headingTo ? (
67
+ <a href={headingHref || (typeof headingTo === 'string' ? headingTo : undefined)} class="hover:underline flex items-center gap-1.5">
68
+ {headingLeadingIcon && <span class={headingLeadingIcon} />}
69
+ <span>{headingLabel}</span>
70
+ {headingTrailingIcon && <span class={headingTrailingIcon} />}
71
+ </a>
72
+ ) : (
73
+ <span class="flex items-center gap-1.5">
74
+ {headingLeadingIcon && <span class={headingLeadingIcon} />}
75
+ <span>{headingLabel}</span>
76
+ {headingTrailingIcon && <span class={headingTrailingIcon} />}
77
+ </span>
78
+ )}
79
+ </h4>
80
+ )}
81
+
82
+ <ul class={classes.list} data-slot="list">
83
+ <slot>
84
+ {normalizedLinks.map((link) => (
85
+ <li class={classes.item} data-slot="item">
86
+ <RLALink
87
+ variant={link.variant ?? "ghost"}
88
+ color={link.color ?? "neutral"}
89
+ theme={link.theme ?? theme}
90
+ class:list={[overrideLinkClass, "w-full !justify-start"]}
91
+ data-slot="link"
92
+ {...link}
93
+ >
94
+ {link.label}
95
+ </RLALink>
96
+ </li>
97
+ ))}
98
+ </slot>
99
+ </ul>
100
100
  </div>
@@ -81,45 +81,38 @@ const overrideTriggerClass = classes.select
81
81
 
82
82
  const gapClass = size === "sm" ? "gap-1.5" : size === "lg" ? "gap-3" : "gap-2";
83
83
 
84
- const isDefault = variant === "default";
85
- const triggerDarkModeOverrides = isDefault
86
- ? "dark:!bg-neutral-800 dark:!text-white dark:hover:!bg-neutral-700"
87
- : "";
88
-
89
84
  const selectUi = {
90
85
  root: "!w-fit !inline-flex",
91
- trigger: `flex items-center justify-between !w-fit ${gapClass} ${overrideTriggerClass} ${triggerDarkModeOverrides}`,
92
- content: "!min-w-0 !w-full !bg-white !border-gray-200 !text-gray-800 dark:!bg-neutral-900 dark:!border-neutral-800 dark:!text-white",
93
- item: "!text-gray-700 hover:!bg-gray-100 hover:!text-gray-900 dark:!text-neutral-300 dark:hover:!bg-neutral-800 dark:hover:!text-white"
86
+ trigger: `flex items-center justify-between !w-fit ${gapClass} ${overrideTriggerClass}`,
87
+ content: "!min-w-0 !w-full !bg-[var(--popover)] !border-[var(--border)] !text-[var(--popover-foreground)]",
88
+ item: "!text-[var(--foreground)] hover:!bg-[var(--accent)] hover:!text-[var(--accent-foreground)]"
94
89
  };
95
90
  ---
96
91
 
97
- <div class:list={[classes.root]} data-locale-selector {...rest}>
98
- {Astro.slots.has("label") && (
99
- <label class:list={[classes.label]}>
100
- <slot name="label" />
101
- </label>
102
- )}
103
-
104
- <div class:list={[classes.wrapper]}>
105
- <RLASelect
106
- items={items}
107
- value={currentLocaleUrl}
108
- size={size}
109
- ui={selectUi}
110
- />
92
+ <rla-locale-selector {...rest}>
93
+ <div class:list={[classes.root]} data-locale-selector>
94
+ {Astro.slots.has("label") && (
95
+ <label class:list={[classes.label]}>
96
+ <slot name="label" />
97
+ </label>
98
+ )}
99
+
100
+ <div class:list={[classes.wrapper]}>
101
+ <RLASelect
102
+ items={items}
103
+ value={currentLocaleUrl}
104
+ size={size}
105
+ ui={selectUi}
106
+ />
107
+ </div>
111
108
  </div>
112
- </div>
109
+ </rla-locale-selector>
113
110
 
114
111
  <script>
115
- const initializeLocaleSelector = () => {
116
- const selectors = document.querySelectorAll('[data-locale-selector]');
117
-
118
- selectors.forEach(selector => {
119
- if (selector.hasAttribute('data-initialized')) return;
120
- selector.setAttribute('data-initialized', 'true');
121
-
122
- const hiddenInput = selector.querySelector('[data-select-hidden-input]') as HTMLInputElement;
112
+ class RLALocaleSelector extends HTMLElement {
113
+ connectedCallback() {
114
+ this.style.display = 'contents';
115
+ const hiddenInput = this.querySelector('[data-select-hidden-input]') as HTMLInputElement | null;
123
116
  if (!hiddenInput) return;
124
117
 
125
118
  hiddenInput.addEventListener('change', (event) => {
@@ -129,9 +122,10 @@ const selectUi = {
129
122
  window.location.href = targetUrl;
130
123
  }
131
124
  });
132
- });
133
- };
125
+ }
126
+ }
134
127
 
135
- document.addEventListener('DOMContentLoaded', initializeLocaleSelector);
136
- document.addEventListener('astro:page-load', initializeLocaleSelector);
128
+ if (!customElements.get('rla-locale-selector')) {
129
+ customElements.define('rla-locale-selector', RLALocaleSelector);
130
+ }
137
131
  </script>
@@ -1,90 +1,117 @@
1
- ---
2
- import { scv } from "css-variants"
3
- import { useUi, resolveClasses } from "../../utils"
4
- import type { LogoProps } from "../../types"
5
- import logoThemeDefault, { createLogoTheme } from "../../themes/logo.theme"
6
- import { getUIConfig } from "virtual:rimelight-ui"
7
-
8
- const uiConfig = getUIConfig()
9
- const logoTheme = uiConfig.colors ? createLogoTheme() : logoThemeDefault
10
-
11
- const logo = scv(logoTheme)
12
-
13
- const {
14
- variant = "logomark",
15
- mode,
16
- theme,
17
- href = "/",
18
- alt,
19
- ui: uiProp,
20
- class: className,
21
- ...rest
22
- } = Astro.props as LogoProps;
23
-
24
- const classes = resolveClasses(logo, {}, useUi("logo", uiProp), className);
25
-
26
- const globalConfig = getUIConfig();
27
- const rcLogos = globalConfig.logos as Record<string, any>;
28
-
29
- let logoSrc: string | null = null;
30
- let darkSrc: string | null = null;
31
- let lightSrc: string | null = null;
32
-
33
- if (rcLogos && typeof rcLogos === "object") {
34
- let src = rcLogos[variant];
35
- if (src && typeof src === "object") {
36
- if (theme === "flat" && src.flat) {
37
- logoSrc = src.flat;
38
- } else if (mode) {
39
- logoSrc = src[mode] || src.color;
40
- } else {
41
- // No mode, no flat — auto-detect, use color as default
42
- logoSrc = src.color || src.black || src.white;
43
- darkSrc = src.white || src.color;
44
- lightSrc = src.black || src.color;
45
- }
46
- } else if (typeof src === "string") {
47
- logoSrc = src;
48
- }
49
- }
50
-
51
- const startsWithUrlPrefix = logoSrc && (logoSrc.startsWith("http") || logoSrc.startsWith("/") || logoSrc.startsWith("."));
52
- const isIcon = logoSrc && !startsWithUrlPrefix && (logoSrc.includes(":") || logoSrc.startsWith("i-"));
53
- const hasAutoMode = darkSrc && lightSrc && darkSrc !== lightSrc && !mode && theme !== "flat";
54
- const autoId = hasAutoMode ? `rla-logo-${Math.random().toString(36).slice(2, 8)}` : undefined;
55
- ---
56
-
57
- <a
58
- class={classes.root}
59
- href={href}
60
- aria-label={alt || variant}
61
- data-slot="root"
62
- {...rest}
63
- >
64
- {logoSrc ? (
65
- isIcon ? (
66
- <span class:list={[logoSrc, "size-full block shrink-0"]} aria-hidden="true" />
67
- ) : hasAutoMode ? (
68
- <>
69
- <img
70
- src={lightSrc}
71
- alt={alt || ""}
72
- class="size-full block object-contain shrink-0 dark:hidden"
73
- />
74
- <img
75
- src={darkSrc}
76
- alt={alt || ""}
77
- class="size-full hidden object-contain shrink-0 dark:block"
78
- />
79
- </>
80
- ) : (
81
- <img
82
- src={logoSrc}
83
- alt={alt || ""}
84
- class="size-full block object-contain shrink-0"
85
- />
86
- )
87
- ) : (
88
- <slot />
89
- )}
90
- </a>
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { LogoProps } from "../../types"
5
+ import logoThemeDefault, { createLogoTheme } from "../../themes/logo.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ const uiConfig = getUIConfig()
9
+ const logoTheme = uiConfig.colors ? createLogoTheme() : logoThemeDefault
10
+
11
+ const logo = scv(logoTheme)
12
+
13
+ const {
14
+ variant = "logomark",
15
+ mode,
16
+ theme,
17
+ href = "/",
18
+ alt,
19
+ ui: uiProp,
20
+ class: className,
21
+ ...rest
22
+ } = Astro.props as LogoProps;
23
+
24
+ let resolvedHref = href;
25
+ if (href === "/") {
26
+ try {
27
+ const { getRelativeLocaleUrl } = await import("@rimelight/i18n");
28
+ resolvedHref = Astro.currentLocale
29
+ ? getRelativeLocaleUrl(Astro.currentLocale, "/")
30
+ : getRelativeLocaleUrl("/");
31
+ } catch {
32
+ if (Astro.currentLocale) {
33
+ resolvedHref = `/${Astro.currentLocale}/`;
34
+ }
35
+ }
36
+ }
37
+
38
+ const classes = resolveClasses(logo, {}, useUi("logo", uiProp), className);
39
+
40
+ const globalConfig = getUIConfig();
41
+ const rcLogos = globalConfig.logos as Record<string, any>;
42
+
43
+ let logoSrc: string | null = null;
44
+ let darkSrc: string | null = null;
45
+ let lightSrc: string | null = null;
46
+
47
+ const logoFiles = import.meta.glob<{ default: any }>("/src/assets/**/*.{svg,png,jpg,jpeg,webp,gif}", { eager: true });
48
+
49
+ function resolveLogo(pathStr: string | null | undefined): any {
50
+ if (!pathStr) return null;
51
+ let lookupPath = pathStr;
52
+ if (lookupPath.startsWith("./")) {
53
+ lookupPath = lookupPath.substring(1);
54
+ } else if (!lookupPath.startsWith("/")) {
55
+ lookupPath = "/" + lookupPath;
56
+ }
57
+ const file = logoFiles[lookupPath];
58
+ if (file) {
59
+ return file.default?.src || file.default;
60
+ }
61
+ return pathStr;
62
+ }
63
+
64
+ if (rcLogos && typeof rcLogos === "object") {
65
+ let src = rcLogos[variant];
66
+ if (src && typeof src === "object") {
67
+ if (mode) {
68
+ logoSrc = resolveLogo(src[mode] || src.color);
69
+ } else {
70
+ logoSrc = resolveLogo(src.color || src.black || src.white);
71
+ darkSrc = resolveLogo(src.white || src.color);
72
+ lightSrc = resolveLogo(src.black || src.color);
73
+ }
74
+ } else if (typeof src === "string") {
75
+ logoSrc = resolveLogo(src);
76
+ }
77
+ }
78
+
79
+ const startsWithUrlPrefix = logoSrc && (logoSrc.startsWith("http") || logoSrc.startsWith("/") || logoSrc.startsWith("."));
80
+ const isIcon = logoSrc && !startsWithUrlPrefix && (logoSrc.includes(":") || logoSrc.startsWith("i-"));
81
+ const hasAutoMode = darkSrc && lightSrc && darkSrc !== lightSrc && !mode;
82
+ ---
83
+
84
+ <a
85
+ class={classes.root}
86
+ href={resolvedHref}
87
+ aria-label={alt || variant}
88
+ data-slot="root"
89
+ {...rest}
90
+ >
91
+ {logoSrc ? (
92
+ isIcon ? (
93
+ <span class:list={[logoSrc, "size-full block shrink-0"]} aria-hidden="true" />
94
+ ) : hasAutoMode ? (
95
+ <>
96
+ <img
97
+ src={lightSrc}
98
+ alt={alt || ""}
99
+ class="size-full block object-contain shrink-0 dark:hidden"
100
+ />
101
+ <img
102
+ src={darkSrc}
103
+ alt={alt || ""}
104
+ class="size-full hidden object-contain shrink-0 dark:block"
105
+ />
106
+ </>
107
+ ) : (
108
+ <img
109
+ src={logoSrc}
110
+ alt={alt || ""}
111
+ class="size-full block object-contain shrink-0"
112
+ />
113
+ )
114
+ ) : (
115
+ <slot />
116
+ )}
117
+ </a>
@@ -22,7 +22,7 @@ const {
22
22
  ui: uiProp,
23
23
  class: className,
24
24
  ...rest
25
- } = Astro.props as ModalProps
25
+ } = Astro.props as ModalProps & { size?: "sm" | "md" | "lg" | "xl" }
26
26
 
27
27
  const classes = resolveClasses(modal, {
28
28
  size
@@ -36,12 +36,12 @@ const modalId = `rla-modal-${Math.random().toString(36).substring(2, 9)}`
36
36
  class={classes.trigger}
37
37
  data-slot="trigger"
38
38
  type="button"
39
- onclick={`document.getElementById('${modalId}').showmodal()`}
39
+ data-dialog-show={modalId}
40
40
  >
41
41
  {triggerLabel}
42
42
  </button>
43
43
  ) : (
44
- <span onclick={`document.getElementById('${modalId}').showmodal()`}>
44
+ <span data-dialog-show={modalId}>
45
45
  <slot name="trigger" />
46
46
  </span>
47
47
  )}
@@ -59,7 +59,7 @@ const modalId = `rla-modal-${Math.random().toString(36).substring(2, 9)}`
59
59
  class={classes.closeButton}
60
60
  data-slot="close-button"
61
61
  type="button"
62
- onclick={`document.getElementById('${modalId}').close()`}
62
+ data-dialog-close={modalId}
63
63
  aria-label="Close modal"
64
64
  >
65
65
  <span class="i-lucide-x size-4" aria-hidden="true" />
@@ -81,19 +81,49 @@ const modalId = `rla-modal-${Math.random().toString(36).substring(2, 9)}`
81
81
  </dialog>
82
82
  </div>
83
83
 
84
- <script is:inline>
85
- document.addEventListener('click', (e) => {
86
- if (e.target && e.target.tagName === 'DIALOG' && e.target.getAttribute('data-close-on-click-outside') === 'true') {
87
- const rect = e.target.getBoundingClientRect();
88
- const isInModal = (
89
- rect.top <= e.clientY &&
90
- e.clientY <= rect.top + rect.height &&
91
- rect.left <= e.clientX &&
92
- e.clientX <= rect.left + rect.width
93
- );
94
- if (!isInModal) {
95
- e.target.close();
84
+ <script>
85
+ if (!(window as any).rlaDialogListenerActive) {
86
+ (window as any).rlaDialogListenerActive = true;
87
+ document.addEventListener('click', (e) => {
88
+ const target = e.target as HTMLElement | null;
89
+ if (!target) return;
90
+
91
+ const showTrigger = target.closest('[data-dialog-show]');
92
+ if (showTrigger) {
93
+ const dialogId = showTrigger.getAttribute('data-dialog-show');
94
+ if (dialogId) {
95
+ const dialog = document.getElementById(dialogId) as HTMLDialogElement | null;
96
+ if (dialog) {
97
+ if (typeof dialog.showModal === 'function') {
98
+ dialog.showModal();
99
+ } else if (typeof (dialog as any).showmodal === 'function') {
100
+ (dialog as any).showmodal();
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ const closeTrigger = target.closest('[data-dialog-close]');
107
+ if (closeTrigger) {
108
+ const dialogId = closeTrigger.getAttribute('data-dialog-close');
109
+ if (dialogId) {
110
+ const dialog = document.getElementById(dialogId) as HTMLDialogElement | null;
111
+ dialog?.close();
112
+ }
113
+ }
114
+
115
+ if (target.tagName === 'DIALOG' && target.getAttribute('data-close-on-click-outside') === 'true') {
116
+ const rect = target.getBoundingClientRect();
117
+ const isInModal = (
118
+ rect.top <= e.clientY &&
119
+ e.clientY <= rect.top + rect.height &&
120
+ rect.left <= e.clientX &&
121
+ e.clientX <= rect.left + rect.width
122
+ );
123
+ if (!isInModal) {
124
+ (target as HTMLDialogElement).close();
125
+ }
96
126
  }
97
- }
98
- });
127
+ });
128
+ }
99
129
  </script>