@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
@@ -1,251 +1,274 @@
1
- ---
2
- import { Image, Picture, getImage } from "astro:assets"
3
- import { scv } from "css-variants"
4
- import { useUi, resolveClasses } from "../../utils"
5
- import type { ImageProps, ImageMetadataInfo } from "../../types"
6
- import imageThemeDefault, { createImageTheme } from "../../themes/image.theme"
7
- import { getUIConfig } from "virtual:rimelight-ui"
8
-
9
- const uiConfig = getUIConfig()
10
- const imageTheme = uiConfig.colors ? createImageTheme() : imageThemeDefault
11
-
12
- const img = scv(imageTheme)
13
-
14
- const {
15
- src,
16
- mobileSrc,
17
- desktopSrc,
18
- breakpoint,
19
- formats,
20
- alt = "",
21
- height,
22
- width,
23
- loading = "lazy",
24
- fit = "cover",
25
- metadata: preMetadata,
26
- open,
27
- ui: uiProp,
28
- class: className,
29
- metaSource = true,
30
- metaFormat = true,
31
- metaDimensions = true,
32
- metaSize = true,
33
- metaAlt = true,
34
- metaDownload = true,
35
- ...rest
36
- } = Astro.props as ImageProps
37
-
38
- const classes = resolveClasses(img, {}, useUi("image", uiProp), className)
39
-
40
- const isResponsive = !!(mobileSrc && desktopSrc)
41
- const isFormatFallback = !isResponsive && !!(formats && formats.length > 0)
42
- const breakpointVal = breakpoint ?? 768
43
-
44
- let mobileImg = null
45
- let desktopImg = null
46
-
47
- if (isResponsive) {
48
- mobileImg = await getImage({
49
- src: mobileSrc!,
50
- format: "webp",
51
- })
52
- desktopImg = await getImage({
53
- src: desktopSrc!,
54
- format: "webp",
55
- })
56
- }
57
-
58
- const activeSrc = isResponsive ? desktopSrc : src
59
- const isMetadata = typeof activeSrc !== "string" && activeSrc !== undefined
60
- const imgWidth = width ?? (isMetadata ? (activeSrc as any).width : undefined)
61
- const imgHeight = height ?? (isMetadata ? (activeSrc as any).height : undefined)
62
- const imgFormat = isMetadata ? (activeSrc as any).format : (preMetadata?.format ?? undefined)
63
- const rawName = activeSrc
64
- ? (typeof activeSrc === "string" ? activeSrc.split("/").pop() || "image" : (activeSrc as any).src.split("/").pop() || "image")
65
- : "image"
66
- const fileName = rawName.split("?")[0]
67
- const fileExtension = imgFormat?.toUpperCase() || (fileName.includes(".") ? fileName.split(".").pop()?.toUpperCase() : "IMG")
68
- const initialSize = preMetadata?.size ?? undefined
69
- ---
70
-
71
- <div class={classes.root} data-slot="root" {...rest}>
72
- <slot name="trigger">
73
- <button
74
- type="button"
75
- class={classes.trigger}
76
- data-image-trigger
77
- >
78
- {isResponsive && mobileImg && desktopImg ? (
79
- <picture>
80
- <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
81
- <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
82
- <img src={desktopImg.src} alt={alt} loading={loading} class="w-full h-full object-cover rounded-lg" />
83
- </picture>
84
- ) : isFormatFallback ? (
85
- <Picture src={src!} formats={formats!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class="w-full h-full object-cover rounded-lg" />
86
- ) : (
87
- <Image src={src!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} fit={fit} />
88
- )}
89
- </button>
90
- </slot>
91
-
92
- <dialog
93
- class={classes.dialog}
94
- data-image-dialog
95
- >
96
- <div class="flex flex-col bg-background text-foreground max-h-[90vh]">
97
- <div class="flex-1 min-h-0 flex items-center justify-center p-4 overflow-hidden">
98
- {isResponsive && mobileImg && desktopImg ? (
99
- <picture class="max-w-full max-h-[65vh] flex items-center justify-center overflow-hidden">
100
- <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
101
- <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
102
- <img src={desktopImg.src} alt={alt} class={classes.image} data-modal-img />
103
- </picture>
104
- ) : (
105
- <img
106
- src={typeof src === "string" ? src : src!.src}
107
- alt={alt}
108
- class={classes.image}
109
- data-modal-img
110
- />
111
- )}
112
- </div>
113
-
114
- <hr class="border-border mx-4 mb-3 shrink-0" />
115
-
116
- <div class={classes.fileInfo}>
117
- <div class="flex flex-col gap-xs text-sm">
118
- {
119
- metaSource && (
120
- <div class="flex flex-row gap-xs items-center">
121
- <span class="i-lucide-image size-4 shrink-0 text-muted-foreground" />
122
- <span class="text-muted-foreground">Source:</span>
123
- <span class="text-dimmed truncate max-w-[40vw]">{fileName}</span>
124
- </div>
125
- )
126
- }
127
- {
128
- metaFormat && (
129
- <div class="flex flex-row gap-xs items-center">
130
- <span class="i-lucide-file-question-mark size-4 shrink-0 text-muted-foreground" />
131
- <span class="text-muted-foreground">Format:</span>
132
- <span class="text-dimmed" data-metadata-format>{fileExtension || "IMG"}</span>
133
- </div>
134
- )
135
- }
136
- {
137
- metaDimensions && (
138
- <div class="flex flex-row gap-xs items-center">
139
- <span class="i-lucide-image-upscale size-4 shrink-0 text-muted-foreground" />
140
- <span class="text-muted-foreground">Dimensions:</span>
141
- <span class="text-dimmed">{(imgWidth || "?")} x {(imgHeight || "?")}</span>
142
- </div>
143
- )
144
- }
145
- {
146
- metaSize && (
147
- <div class="flex flex-row gap-xs items-center">
148
- <span class="i-lucide-hard-drive size-4 shrink-0 text-muted-foreground" />
149
- <span class="text-muted-foreground">Size:</span>
150
- <span class="text-dimmed" data-metadata-size>{initialSize !== undefined ? `${initialSize} bytes` : "fetching..."}</span>
151
- </div>
152
- )
153
- }
154
- {
155
- metaAlt && (
156
- <div class="flex flex-row gap-xs items-center">
157
- <span class="i-lucide-align-left size-4 shrink-0 text-muted-foreground" />
158
- <span class="text-muted-foreground">Alt:</span>
159
- <span class="text-dimmed truncate max-w-[30vw]" class:list={{ "italic opacity-50": !alt }}>{alt || "none"}</span>
160
- </div>
161
- )
162
- }
163
- </div>
164
-
165
- {
166
- metaDownload && (
167
- <button
168
- type="button"
169
- class={classes.download}
170
- data-image-download
171
- >
172
- <span class="i-lucide-download size-4 shrink-0" />
173
- Download Original
174
- </button>
175
- )
176
- }
177
- </div>
178
-
179
- <form method="dialog" class="absolute top-2 right-2">
180
- <button
181
- type="submit"
182
- class={classes.closeButton}
183
- aria-label="Close"
184
- >
185
- <span class="i-lucide-x size-4" />
186
- </button>
187
- </form>
188
- </div>
189
- </dialog>
190
- </div>
191
-
192
- <script>
193
- const triggers = document.querySelectorAll("[data-image-trigger]")
194
- for (const trigger of triggers) {
195
- const parent = trigger.closest(".group\\/image") || trigger.parentElement
196
- const dialog = parent?.querySelector("[data-image-dialog]")
197
- if (!dialog) continue
198
-
199
- const sizeEl = parent?.querySelector("[data-metadata-size]")
200
- const formatEl = parent?.querySelector("[data-metadata-format]")
201
- let fetched = false
202
-
203
- trigger.addEventListener("click", () => {
204
- if (!fetched) {
205
- fetched = true
206
- const imgEl = dialog.querySelector("[data-modal-img]")
207
- if (imgEl) {
208
- const src = imgEl.getAttribute("src")
209
- if (src) {
210
- fetch(src).then(r => r.blob()).then(blob => {
211
- if (sizeEl) sizeEl.textContent = (blob.size / 1024).toFixed(2) + " KB"
212
- if (formatEl && blob.type) {
213
- const mime = blob.type.split("/").pop().toUpperCase()
214
- if (mime && mime !== "OCTET-STREAM") formatEl.textContent = mime
215
- }
216
- }).catch(() => { if (sizeEl) sizeEl.textContent = "unavailable" })
217
- }
218
- }
219
- }
220
- dialog.showModal()
221
- })
222
-
223
- dialog.addEventListener("click", (e) => {
224
- if (e.target === dialog) dialog.close()
225
- })
226
-
227
- const downloadBtn = dialog.querySelector("[data-image-download]")
228
- if (downloadBtn) {
229
- downloadBtn.addEventListener("click", async () => {
230
- const img = dialog.querySelector("img")
231
- const imgSrc = img?.getAttribute("src")
232
- if (!imgSrc) return
233
- try {
234
- const response = await fetch(imgSrc)
235
- const blob = await response.blob()
236
- const url = URL.createObjectURL(blob)
237
- const link = document.createElement("a")
238
- link.href = url
239
- const srcAttr = (trigger.querySelector("img") || {}).getAttribute("src") || ""
240
- link.download = (srcAttr.split("/").pop() || "").split("?")[0] || `image-${Date.now()}`
241
- document.body.appendChild(link)
242
- link.click()
243
- document.body.removeChild(link)
244
- URL.revokeObjectURL(url)
245
- } catch (err) {
246
- console.error("Download failed", err)
247
- }
248
- })
249
- }
250
- }
251
- </script>
1
+ ---
2
+ import { getImage } from "astro:assets"
3
+ import type { ImageMetadata } from "astro"
4
+ import Image from "astro/components/Image.astro"
5
+ import Picture from "astro/components/Picture.astro"
6
+ import { scv } from "css-variants"
7
+ import { useUi, resolveClasses } from "../../utils"
8
+ import type { ImageProps } from "../../types"
9
+ import imageThemeDefault, { createImageTheme } from "../../themes/image.theme"
10
+ import { getUIConfig } from "virtual:rimelight-ui"
11
+
12
+ const uiConfig = getUIConfig()
13
+ const imageTheme = uiConfig.colors ? createImageTheme() : imageThemeDefault
14
+
15
+ const img = scv(imageTheme)
16
+
17
+ const {
18
+ src,
19
+ mobileSrc,
20
+ desktopSrc,
21
+ breakpoint,
22
+ formats,
23
+ alt = "",
24
+ height,
25
+ width,
26
+ loading = "lazy",
27
+ fit = "cover",
28
+ metadata: preMetadata,
29
+ open,
30
+ ui: uiProp,
31
+ class: className,
32
+ metaSource = true,
33
+ metaFormat = true,
34
+ metaDimensions = true,
35
+ metaSize = true,
36
+ metaAlt = true,
37
+ metaDownload = true,
38
+ ...rest
39
+ } = Astro.props as ImageProps
40
+
41
+ const classes = resolveClasses(img, {}, useUi("image", uiProp), className)
42
+
43
+ const isResponsive = !!(mobileSrc && desktopSrc)
44
+ const isFormatFallback = !isResponsive && !!(formats && formats.length > 0)
45
+ const breakpointVal = breakpoint ?? 768
46
+
47
+ let mobileImg = null
48
+ let desktopImg = null
49
+
50
+ if (isResponsive) {
51
+ mobileImg = await getImage({
52
+ src: mobileSrc!,
53
+ format: "webp",
54
+ })
55
+ desktopImg = await getImage({
56
+ src: desktopSrc!,
57
+ format: "webp",
58
+ })
59
+ }
60
+
61
+ const activeSrc = isResponsive ? desktopSrc : src
62
+ const isMetadata = typeof activeSrc !== "string" && activeSrc !== undefined
63
+ const imgWidth = width ?? (isMetadata ? (activeSrc as any).width : undefined)
64
+ const imgHeight = height ?? (isMetadata ? (activeSrc as any).height : undefined)
65
+ const imgFormat = isMetadata ? (activeSrc as any).format : (preMetadata?.format ?? undefined)
66
+ const rawName = activeSrc
67
+ ? (typeof activeSrc === "string" ? activeSrc.split("/").pop() || "image" : (activeSrc as any).src.split("/").pop() || "image")
68
+ : "image"
69
+ const fileName = rawName.split("?")[0]
70
+ const fileExtension = imgFormat?.toUpperCase() || (fileName.includes(".") ? fileName.split(".").pop()?.toUpperCase() : "IMG")
71
+ const initialSize = preMetadata?.size ?? undefined
72
+ ---
73
+
74
+ <rla-image {...rest}>
75
+ <div class={classes.root} data-slot="root">
76
+ <slot name="trigger">
77
+ <button
78
+ type="button"
79
+ class={classes.trigger}
80
+ data-image-trigger
81
+ >
82
+ {isResponsive && mobileImg && desktopImg ? (
83
+ <picture>
84
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
85
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
86
+ <img src={desktopImg.src} alt={alt} loading={loading} class="w-full h-full object-cover rounded-lg" />
87
+ </picture>
88
+ ) : isFormatFallback ? (
89
+ typeof src === "string" ? (
90
+ <Picture src={src} formats={formats!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class="w-full h-full object-cover rounded-lg" />
91
+ ) : (
92
+ <Picture src={src as ImageMetadata} formats={formats!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class="w-full h-full object-cover rounded-lg" />
93
+ )
94
+ ) : (
95
+ typeof src === "string" ? (
96
+ <Image src={src} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class={classes.triggerImage} />
97
+ ) : (
98
+ <Image src={src as ImageMetadata} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class={classes.triggerImage} />
99
+ )
100
+ )}
101
+ </button>
102
+ </slot>
103
+
104
+ <dialog
105
+ class={classes.dialog}
106
+ data-image-dialog
107
+ >
108
+ <div class="flex flex-col bg-background text-foreground max-h-[90vh]">
109
+ <div class="flex-1 min-h-0 flex items-center justify-center p-4 overflow-hidden">
110
+ {isResponsive && mobileImg && desktopImg ? (
111
+ <picture class="max-w-full max-h-[65vh] flex items-center justify-center overflow-hidden">
112
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
113
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
114
+ <img src={desktopImg.src} alt={alt} class={classes.image} data-modal-img />
115
+ </picture>
116
+ ) : (
117
+ <img
118
+ src={typeof src === "string" ? src : src!.src}
119
+ alt={alt}
120
+ class={classes.image}
121
+ data-modal-img
122
+ />
123
+ )}
124
+ </div>
125
+
126
+ <hr class="border-border mx-4 mb-3 shrink-0" />
127
+
128
+ <div class={classes.fileInfo}>
129
+ <div class="flex flex-col gap-xs text-sm">
130
+ {
131
+ metaSource && (
132
+ <div class="flex flex-row gap-xs items-center">
133
+ <span class="i-lucide-image size-4 shrink-0 text-muted-foreground" />
134
+ <span class="text-muted-foreground">Source:</span>
135
+ <span class="text-dimmed truncate max-w-[40vw]">{fileName}</span>
136
+ </div>
137
+ )
138
+ }
139
+ {
140
+ metaFormat && (
141
+ <div class="flex flex-row gap-xs items-center">
142
+ <span class="i-lucide-file-question-mark size-4 shrink-0 text-muted-foreground" />
143
+ <span class="text-muted-foreground">Format:</span>
144
+ <span class="text-dimmed" data-metadata-format>{fileExtension || "IMG"}</span>
145
+ </div>
146
+ )
147
+ }
148
+ {
149
+ metaDimensions && (
150
+ <div class="flex flex-row gap-xs items-center">
151
+ <span class="i-lucide-image-upscale size-4 shrink-0 text-muted-foreground" />
152
+ <span class="text-muted-foreground">Dimensions:</span>
153
+ <span class="text-dimmed">{(imgWidth || "?")} x {(imgHeight || "?")}</span>
154
+ </div>
155
+ )
156
+ }
157
+ {
158
+ metaSize && (
159
+ <div class="flex flex-row gap-xs items-center">
160
+ <span class="i-lucide-hard-drive size-4 shrink-0 text-muted-foreground" />
161
+ <span class="text-muted-foreground">Size:</span>
162
+ <span class="text-dimmed" data-metadata-size>{initialSize !== undefined ? `${initialSize} bytes` : "fetching..."}</span>
163
+ </div>
164
+ )
165
+ }
166
+ {
167
+ metaAlt && (
168
+ <div class="flex flex-row gap-xs items-center">
169
+ <span class="i-lucide-align-left size-4 shrink-0 text-muted-foreground" />
170
+ <span class="text-muted-foreground">Alt:</span>
171
+ <span class="text-dimmed truncate max-w-[30vw]" class:list={{ "italic opacity-50": !alt }}>{alt || "none"}</span>
172
+ </div>
173
+ )
174
+ }
175
+ </div>
176
+
177
+ {
178
+ metaDownload && (
179
+ <button
180
+ type="button"
181
+ class={classes.download}
182
+ data-image-download
183
+ >
184
+ <span class="i-lucide-download size-4 shrink-0" />
185
+ Download Original
186
+ </button>
187
+ )
188
+ }
189
+ </div>
190
+
191
+ <form method="dialog" class="absolute top-2 right-2">
192
+ <button
193
+ type="submit"
194
+ class={classes.closeButton}
195
+ aria-label="Close"
196
+ >
197
+ <span class="i-lucide-x size-4" />
198
+ </button>
199
+ </form>
200
+ </div>
201
+ </dialog>
202
+ </div>
203
+ </rla-image>
204
+
205
+ <script>
206
+ class RLAImage extends HTMLElement {
207
+ connectedCallback() {
208
+ this.style.display = 'contents';
209
+ const trigger = this.querySelector("[data-image-trigger]");
210
+ if (!trigger) return;
211
+
212
+ const dialog = this.querySelector("[data-image-dialog]") as HTMLDialogElement | null;
213
+ if (!dialog) return;
214
+
215
+ const sizeEl = this.querySelector("[data-metadata-size]");
216
+ const formatEl = this.querySelector("[data-metadata-format]");
217
+ let fetched = false;
218
+
219
+ trigger.addEventListener("click", () => {
220
+ if (!fetched) {
221
+ fetched = true;
222
+ const imgEl = dialog.querySelector("[data-modal-img]");
223
+ if (imgEl) {
224
+ const src = imgEl.getAttribute("src");
225
+ if (src) {
226
+ fetch(src).then(r => r.blob()).then(blob => {
227
+ if (sizeEl) sizeEl.textContent = (blob.size / 1024).toFixed(2) + " KB";
228
+ if (formatEl && blob.type) {
229
+ const mime = blob.type.split("/").pop()?.toUpperCase();
230
+ if (mime && mime !== "OCTET-STREAM") formatEl.textContent = mime;
231
+ }
232
+ return;
233
+ }).catch(() => { if (sizeEl) sizeEl.textContent = "unavailable"; });
234
+ }
235
+ }
236
+ }
237
+ dialog.showModal();
238
+ });
239
+
240
+ dialog.addEventListener("click", (e) => {
241
+ if (e.target === dialog) dialog.close();
242
+ });
243
+
244
+ const downloadBtn = dialog.querySelector("[data-image-download]");
245
+ if (downloadBtn) {
246
+ downloadBtn.addEventListener("click", async () => {
247
+ const img = dialog.querySelector("img");
248
+ const imgSrc = img?.getAttribute("src");
249
+ if (!imgSrc) return;
250
+ try {
251
+ const response = await fetch(imgSrc);
252
+ const blob = await response.blob();
253
+ const url = URL.createObjectURL(blob);
254
+ const link = document.createElement("a");
255
+ link.href = url;
256
+ const triggerImg = trigger.querySelector("img");
257
+ const srcAttr = triggerImg ? triggerImg.getAttribute("src") || "" : "";
258
+ link.download = (srcAttr.split("/").pop() || "").split("?")[0] || `image-${Date.now()}`;
259
+ document.body.appendChild(link);
260
+ link.click();
261
+ document.body.removeChild(link);
262
+ URL.revokeObjectURL(url);
263
+ } catch (err) {
264
+ console.error("Download failed", err);
265
+ }
266
+ });
267
+ }
268
+ }
269
+ }
270
+
271
+ if (!customElements.get('rla-image')) {
272
+ customElements.define('rla-image', RLAImage);
273
+ }
274
+ </script>
@@ -4,7 +4,10 @@ import { useUi, resolveClasses } from "../../utils"
4
4
  import type { InputProps } from "../../types"
5
5
  import inputThemeDefault, { createInputTheme } from "../../themes/input.theme"
6
6
 
7
- const inputTheme = inputThemeDefault
7
+ import { getUIConfig } from "virtual:rimelight-ui"
8
+
9
+ const uiConfig = getUIConfig()
10
+ const inputTheme = uiConfig.colors ? createInputTheme() : inputThemeDefault
8
11
  const input = scv(inputTheme)
9
12
 
10
13
  const {
@@ -18,6 +21,7 @@ const {
18
21
  error = false,
19
22
  leadingIcon,
20
23
  trailingIcon,
24
+ variant = "default",
21
25
  size = "md",
22
26
  color = "primary",
23
27
  theme,
@@ -26,10 +30,11 @@ const {
26
30
  ...rest
27
31
  } = Astro.props as InputProps
28
32
 
29
- const hasLeading = !!leadingIcon
30
- const hasTrailing = !!trailingIcon
33
+ const hasLeading = !!leadingIcon || Astro.slots.has("leading")
34
+ const hasTrailing = !!trailingIcon || Astro.slots.has("trailing")
31
35
 
32
36
  const classes = resolveClasses(input, {
37
+ variant,
33
38
  size,
34
39
  hasLeading,
35
40
  hasTrailing,
@@ -38,9 +43,9 @@ const classes = resolveClasses(input, {
38
43
  }, useUi("input", uiProp), className);
39
44
  ---
40
45
  <div class={classes.root} data-slot="root">
41
- {leadingIcon && (
42
- <span class={classes.leading} data-slot="leading-icon">
43
- <span class={leadingIcon} />
46
+ {(leadingIcon || Astro.slots.has("leading")) && (
47
+ <span class={classes.leading} data-slot="leading">
48
+ {leadingIcon ? <span class={leadingIcon} /> : <slot name="leading" />}
44
49
  </span>
45
50
  )}
46
51
  <input
@@ -54,9 +59,9 @@ const classes = resolveClasses(input, {
54
59
  required={required}
55
60
  {...rest}
56
61
  />
57
- {trailingIcon && (
58
- <span class={classes.trailing} data-slot="trailing-icon">
59
- <span class={trailingIcon} />
62
+ {(trailingIcon || Astro.slots.has("trailing")) && (
63
+ <span class={classes.trailing} data-slot="trailing">
64
+ {trailingIcon ? <span class={trailingIcon} /> : <slot name="trailing" />}
60
65
  </span>
61
66
  )}
62
67
  </div>