@rimelight/ui 0.0.34 → 0.0.36

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 +34 -41
  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
@@ -0,0 +1,413 @@
1
+ ---
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import colorSliderThemeDefault, { createColorSliderTheme } from "../../themes/color-slider.theme"
5
+ import { getUIConfig } from "virtual:rimelight-ui"
6
+
7
+ const uiConfig = getUIConfig()
8
+ const colorSliderTheme = uiConfig.colors ? createColorSliderTheme(uiConfig.colors) : colorSliderThemeDefault
9
+ const colorSlider = scv(colorSliderTheme)
10
+
11
+ export interface Props {
12
+ channel: "hue" | "saturation" | "red" | "green" | "blue" | "alpha" | "lightness" | "brightness"
13
+ colorSpace?: "hsl" | "rgb" | "hsb"
14
+ defaultValue?: string
15
+ dir?: "ltr" | "rtl"
16
+ disabled?: boolean
17
+ inverted?: boolean
18
+ modelValue?: string
19
+ name?: string
20
+ orientation?: "vertical" | "horizontal"
21
+ required?: boolean
22
+ step?: number
23
+ color?: any
24
+ theme?: any
25
+ ui?: any
26
+ class?: string
27
+ }
28
+
29
+ const {
30
+ channel,
31
+ colorSpace = "hsl",
32
+ defaultValue = "#000000",
33
+ dir = "ltr",
34
+ disabled = false,
35
+ inverted = false,
36
+ modelValue,
37
+ name,
38
+ orientation = "horizontal",
39
+ required,
40
+ step = 1,
41
+ color = "primary",
42
+ theme,
43
+ ui: uiProp,
44
+ class: className,
45
+ ...rest
46
+ } = Astro.props as Props
47
+
48
+ const classes = resolveClasses(colorSlider, {
49
+ color,
50
+ theme
51
+ }, useUi("colorSlider", uiProp), className)
52
+
53
+ const initialValue = modelValue || defaultValue
54
+ ---
55
+ <rla-color-slider
56
+ data-channel={channel}
57
+ data-color-space={colorSpace}
58
+ data-value={initialValue}
59
+ data-disabled={disabled ? "true" : "false"}
60
+ data-orientation={orientation}
61
+ data-inverted={inverted ? "true" : "false"}
62
+ data-step={step}
63
+ {...rest}
64
+ >
65
+ <div class={classes.root} data-slot="root" data-orientation={orientation}>
66
+ <div class={classes.track} data-slot="track">
67
+ <div class={classes.thumb} data-slot="thumb" role="slider" aria-label="Color Slider Thumb"></div>
68
+ </div>
69
+ {name && <input type="hidden" name={name} value={initialValue} data-main-input />}
70
+ </div>
71
+ </rla-color-slider>
72
+
73
+ <script>
74
+ import {
75
+ hexToRgb,
76
+ rgbToHex,
77
+ rgbToHsl,
78
+ hslToRgb,
79
+ rgbToHsb,
80
+ hsbToRgb,
81
+ getChannelRange,
82
+ clamp
83
+ } from "../../utils/color"
84
+
85
+ if (typeof HTMLElement !== 'undefined') {
86
+ class RLAColorSlider extends HTMLElement {
87
+ private cleanup: (() => void) | null = null
88
+ private channel: string = "hue"
89
+ private colorSpace: string = "hsl"
90
+ private value: string = "#000000"
91
+ private orientation: string = "horizontal"
92
+ private stepValue: number = 1
93
+
94
+ connectedCallback() {
95
+ this.style.display = 'contents'
96
+ this.channel = this.getAttribute("data-channel") || "hue"
97
+ this.colorSpace = this.getAttribute("data-color-space") || "hsl"
98
+ this.value = this.getAttribute("data-value") || "#000000"
99
+ this.orientation = this.getAttribute("data-orientation") || "horizontal"
100
+ this.stepValue = parseFloat(this.getAttribute("data-step") || "1")
101
+
102
+ const root = this.querySelector('[data-slot="root"]') as HTMLElement
103
+ const track = this.querySelector('[data-slot="track"]') as HTMLElement
104
+ const thumb = this.querySelector('[data-slot="thumb"]') as HTMLElement
105
+
106
+ if (!track || !thumb) return
107
+
108
+ const updateUI = () => {
109
+ const disabled = this.getAttribute("data-disabled") === "true"
110
+ if (disabled) {
111
+ root.classList.add("opacity-50", "pointer-events-none")
112
+ } else {
113
+ root.classList.remove("opacity-50", "pointer-events-none")
114
+ }
115
+
116
+ const rgb = hexToRgb(this.value)
117
+ let hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
118
+ let hsb = rgbToHsb(rgb[0], rgb[1], rgb[2])
119
+
120
+ const range = getChannelRange(this.channel)
121
+ let channelVal = 0
122
+
123
+ if (this.colorSpace === "hsl") {
124
+ const index = ["hue", "saturation", "lightness"].indexOf(this.channel)
125
+ channelVal = index !== -1 ? hsl[index]! : 0
126
+ } else if (this.colorSpace === "hsb") {
127
+ const index = ["hue", "saturation", "brightness"].indexOf(this.channel)
128
+ channelVal = index !== -1 ? hsb[index]! : 0
129
+ } else if (this.colorSpace === "rgb") {
130
+ const index = ["red", "green", "blue"].indexOf(this.channel)
131
+ channelVal = index !== -1 ? rgb[index]! : 0
132
+ }
133
+
134
+ let pct = ((channelVal - range.min) / (range.max - range.min)) * 100
135
+ if (this.getAttribute("data-inverted") === "true") {
136
+ pct = 100 - pct
137
+ }
138
+
139
+ if (this.orientation === "horizontal") {
140
+ thumb.style.left = `${pct}%`
141
+ thumb.style.top = "50%"
142
+ thumb.style.transform = "translate(-50%, -50%)"
143
+ } else {
144
+ thumb.style.left = "50%"
145
+ thumb.style.top = `${100 - pct}%`
146
+ thumb.style.transform = "translate(-50%, -50%)"
147
+ }
148
+
149
+ const dir = this.orientation === "horizontal" ? "to right" : "to top"
150
+ if (this.channel === "hue") {
151
+ track.style.background = `linear-gradient(${dir}, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)`
152
+ } else {
153
+ let startHex = "#000000"
154
+ let endHex = "#ffffff"
155
+
156
+ if (this.colorSpace === "hsl") {
157
+ const index = ["hue", "saturation", "lightness"].indexOf(this.channel)
158
+ if (index !== -1) {
159
+ let startHsl = [...hsl] as [number, number, number]
160
+ let endHsl = [...hsl] as [number, number, number]
161
+ startHsl[index] = range.min
162
+ endHsl[index] = range.max
163
+ const startRgb = hslToRgb(startHsl[0], startHsl[1], startHsl[2])
164
+ const endRgb = hslToRgb(endHsl[0], endHsl[1], endHsl[2])
165
+ startHex = rgbToHex(startRgb[0], startRgb[1], startRgb[2])
166
+ endHex = rgbToHex(endRgb[0], endRgb[1], endRgb[2])
167
+ }
168
+ } else if (this.colorSpace === "hsb") {
169
+ const index = ["hue", "saturation", "brightness"].indexOf(this.channel)
170
+ if (index !== -1) {
171
+ let startHsb = [...hsb] as [number, number, number]
172
+ let endHsb = [...hsb] as [number, number, number]
173
+ startHsb[index] = range.min
174
+ endHsb[index] = range.max
175
+ const startRgb = hsbToRgb(startHsb[0], startHsb[1], startHsb[2])
176
+ const endRgb = hsbToRgb(endHsb[0], endHsb[1], endHsb[2])
177
+ startHex = rgbToHex(startRgb[0], startRgb[1], startRgb[2])
178
+ endHex = rgbToHex(endRgb[0], endRgb[1], endRgb[2])
179
+ }
180
+ } else if (this.colorSpace === "rgb") {
181
+ const index = ["red", "green", "blue"].indexOf(this.channel)
182
+ if (index !== -1) {
183
+ let startRgb = [...rgb] as [number, number, number]
184
+ let endRgb = [...rgb] as [number, number, number]
185
+ startRgb[index] = range.min
186
+ endRgb[index] = range.max
187
+ startHex = rgbToHex(startRgb[0], startRgb[1], startRgb[2])
188
+ endHex = rgbToHex(endRgb[0], endRgb[1], endRgb[2])
189
+ }
190
+ }
191
+
192
+ track.style.background = `linear-gradient(${dir}, ${startHex}, ${endHex})`
193
+ }
194
+
195
+ thumb.style.backgroundColor = this.value
196
+
197
+ const mainInput = this.querySelector("[data-main-input]") as HTMLInputElement
198
+ if (mainInput) mainInput.value = this.value
199
+ }
200
+
201
+ updateUI()
202
+
203
+ const observer = new MutationObserver((mutations) => {
204
+ mutations.forEach((mutation) => {
205
+ if (mutation.type === "attributes") {
206
+ if (mutation.attributeName === "data-value") {
207
+ const newVal = this.getAttribute("data-value") || "#000000"
208
+ if (newVal !== this.value) {
209
+ this.value = newVal
210
+ updateUI()
211
+ }
212
+ }
213
+ if (mutation.attributeName === "data-disabled") {
214
+ updateUI()
215
+ }
216
+ }
217
+ })
218
+ })
219
+ observer.observe(this, { attributes: true })
220
+
221
+ let isDragging = false
222
+
223
+ const handlePointer = (e: PointerEvent) => {
224
+ if (this.getAttribute("data-disabled") === "true") return
225
+ const rect = track.getBoundingClientRect()
226
+ let pct = 0
227
+
228
+ if (this.orientation === "horizontal") {
229
+ pct = clamp((e.clientX - rect.left) / rect.width, 0, 1)
230
+ } else {
231
+ pct = 1 - clamp((e.clientY - rect.top) / rect.height, 0, 1)
232
+ }
233
+
234
+ if (this.getAttribute("data-inverted") === "true") {
235
+ pct = 1 - pct
236
+ }
237
+
238
+ const range = getChannelRange(this.channel)
239
+ const stepSize = this.stepValue
240
+ let targetVal = range.min + pct * (range.max - range.min)
241
+
242
+ targetVal = Math.round(targetVal / stepSize) * stepSize
243
+ targetVal = clamp(targetVal, range.min, range.max)
244
+
245
+ const rgb = hexToRgb(this.value)
246
+ let hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]) as number[]
247
+ let hsb = rgbToHsb(rgb[0], rgb[1], rgb[2]) as number[]
248
+ let newRgb = [...rgb] as number[]
249
+
250
+ if (this.colorSpace === "hsl") {
251
+ const index = ["hue", "saturation", "lightness"].indexOf(this.channel)
252
+ if (index !== -1) hsl[index] = targetVal
253
+ newRgb = hslToRgb(hsl[0]!, hsl[1]!, hsl[2]!)
254
+ } else if (this.colorSpace === "hsb") {
255
+ const index = ["hue", "saturation", "brightness"].indexOf(this.channel)
256
+ if (index !== -1) hsb[index] = targetVal
257
+ newRgb = hsbToRgb(hsb[0]!, hsb[1]!, hsb[2]!)
258
+ } else if (this.colorSpace === "rgb") {
259
+ const index = ["red", "green", "blue"].indexOf(this.channel)
260
+ if (index !== -1) newRgb[index] = targetVal
261
+ }
262
+
263
+ const newValue = rgbToHex(newRgb[0]!, newRgb[1]!, newRgb[2]!)
264
+ if (newValue !== this.value) {
265
+ this.value = newValue
266
+ this.setAttribute("data-value", newValue)
267
+ updateUI()
268
+
269
+ this.dispatchEvent(new CustomEvent("change", { detail: newValue }))
270
+ this.dispatchEvent(new CustomEvent("update:modelValue", { detail: newValue }))
271
+
272
+ const rootId = this.getAttribute("id")
273
+ if (rootId) {
274
+ document.querySelectorAll(`[data-color-link="${rootId}"]`).forEach((el) => {
275
+ el.setAttribute("data-value", newValue)
276
+ })
277
+ }
278
+ }
279
+ }
280
+
281
+ const onPointerDown = (e: PointerEvent) => {
282
+ isDragging = true
283
+ track.setPointerCapture(e.pointerId)
284
+ handlePointer(e)
285
+ }
286
+
287
+ const onPointerMove = (e: PointerEvent) => {
288
+ if (!isDragging) return
289
+ handlePointer(e)
290
+ }
291
+
292
+ const onPointerUp = (e: PointerEvent) => {
293
+ if (!isDragging) return
294
+ isDragging = false
295
+ track.releasePointerCapture(e.pointerId)
296
+ this.dispatchEvent(new CustomEvent("changeEnd", { detail: this.value }))
297
+ }
298
+
299
+ track.addEventListener("pointerdown", onPointerDown)
300
+ track.addEventListener("pointermove", onPointerMove)
301
+ track.addEventListener("pointerup", onPointerUp)
302
+
303
+ const onKeyDown = (e: KeyboardEvent) => {
304
+ if (this.getAttribute("data-disabled") === "true") return
305
+ const isShift = e.shiftKey
306
+ const stepMultiplier = isShift ? 10 : 1
307
+ const stepSize = this.stepValue * stepMultiplier
308
+
309
+ const range = getChannelRange(this.channel)
310
+ const rgb = hexToRgb(this.value)
311
+ let hsl = rgbToHsl(rgb[0], rgb[1], rgb[2])
312
+ let hsb = rgbToHsb(rgb[0], rgb[1], rgb[2])
313
+
314
+ let channelVal = 0
315
+ if (this.colorSpace === "hsl") {
316
+ const index = ["hue", "saturation", "lightness"].indexOf(this.channel)
317
+ channelVal = index !== -1 ? hsl[index]! : 0
318
+ } else if (this.colorSpace === "hsb") {
319
+ const index = ["hue", "saturation", "brightness"].indexOf(this.channel)
320
+ channelVal = index !== -1 ? hsb[index]! : 0
321
+ } else if (this.colorSpace === "rgb") {
322
+ const index = ["red", "green", "blue"].indexOf(this.channel)
323
+ channelVal = index !== -1 ? rgb[index]! : 0
324
+ }
325
+
326
+ let handled = false
327
+ let newVal = channelVal
328
+
329
+ if (this.orientation === "horizontal") {
330
+ if (e.key === "ArrowLeft") {
331
+ newVal = channelVal - stepSize
332
+ handled = true
333
+ } else if (e.key === "ArrowRight") {
334
+ newVal = channelVal + stepSize
335
+ handled = true
336
+ }
337
+ } else {
338
+ if (e.key === "ArrowDown") {
339
+ newVal = channelVal - stepSize
340
+ handled = true
341
+ } else if (e.key === "ArrowUp") {
342
+ newVal = channelVal + stepSize
343
+ handled = true
344
+ }
345
+ }
346
+
347
+ if (e.key === "Home") {
348
+ newVal = range.min
349
+ handled = true
350
+ } else if (e.key === "End") {
351
+ newVal = range.max
352
+ handled = true
353
+ } else if (e.key === "PageUp") {
354
+ newVal = channelVal + stepSize * 10
355
+ handled = true
356
+ } else if (e.key === "PageDown") {
357
+ newVal = channelVal - stepSize * 10
358
+ handled = true
359
+ }
360
+
361
+ if (handled) {
362
+ e.preventDefault()
363
+ newVal = clamp(newVal, range.min, range.max)
364
+
365
+ let newRgb = [...rgb] as number[]
366
+ if (this.colorSpace === "hsl") {
367
+ const index = ["hue", "saturation", "lightness"].indexOf(this.channel)
368
+ if (index !== -1) hsl[index] = newVal
369
+ newRgb = hslToRgb(hsl[0]!, hsl[1]!, hsl[2]!)
370
+ } else if (this.colorSpace === "hsb") {
371
+ const index = ["hue", "saturation", "brightness"].indexOf(this.channel)
372
+ if (index !== -1) hsb[index] = newVal
373
+ newRgb = hsbToRgb(hsb[0]!, hsb[1]!, hsb[2]!)
374
+ } else if (this.colorSpace === "rgb") {
375
+ const index = ["red", "green", "blue"].indexOf(this.channel)
376
+ if (index !== -1) newRgb[index] = newVal
377
+ }
378
+
379
+ const newValue = rgbToHex(newRgb[0]!, newRgb[1]!, newRgb[2]!)
380
+ if (newValue !== this.value) {
381
+ this.value = newValue
382
+ this.setAttribute("data-value", newValue)
383
+ updateUI()
384
+ this.dispatchEvent(new CustomEvent("change", { detail: newValue }))
385
+ }
386
+ }
387
+ }
388
+
389
+ thumb.setAttribute("tabindex", "0")
390
+ thumb.addEventListener("keydown", onKeyDown)
391
+
392
+ this.cleanup = () => {
393
+ track.removeEventListener("pointerdown", onPointerDown)
394
+ track.removeEventListener("pointermove", onPointerMove)
395
+ track.removeEventListener("pointerup", onPointerUp)
396
+ thumb.removeEventListener("keydown", onKeyDown)
397
+ observer.disconnect()
398
+ }
399
+ }
400
+
401
+ disconnectedCallback() {
402
+ if (this.cleanup) {
403
+ this.cleanup()
404
+ this.cleanup = null
405
+ }
406
+ }
407
+ }
408
+
409
+ if (!customElements.get("rla-color-slider")) {
410
+ customElements.define("rla-color-slider", RLAColorSlider)
411
+ }
412
+ }
413
+ </script>
@@ -17,7 +17,7 @@ const {
17
17
  ui: uiProp,
18
18
  class: className,
19
19
  ...rest
20
- } = Astro.props as ConfirmProps
20
+ } = Astro.props as ConfirmProps & { variant?: "default" | "destructive" }
21
21
 
22
22
  const classes = resolveClasses(alertDialog, {
23
23
  variant
@@ -33,12 +33,12 @@ const isDestructive = variant === "destructive"
33
33
  class={classes.trigger}
34
34
  data-slot="trigger"
35
35
  type="button"
36
- onclick={`document.getElementById('${dialogId}').showModal()`}
36
+ data-dialog-show={dialogId}
37
37
  >
38
38
  {triggerLabel}
39
39
  </RLAButton>
40
40
  ) : (
41
- <span onclick={`document.getElementById('${dialogId}').showModal()`}>
41
+ <span data-dialog-show={dialogId}>
42
42
  <slot name="trigger" />
43
43
  </span>
44
44
  )}
@@ -68,7 +68,7 @@ const isDestructive = variant === "destructive"
68
68
  variant="outline"
69
69
  color={isDestructive ? "error" : "neutral"}
70
70
  type="button"
71
- onclick={`document.getElementById('${dialogId}').close()`}
71
+ data-dialog-close={dialogId}
72
72
  >
73
73
  {cancelLabel}
74
74
  </RLAButton>
@@ -78,7 +78,7 @@ const isDestructive = variant === "destructive"
78
78
  color={isDestructive ? "error" : "primary"}
79
79
  variant={isDestructive ? "subtle" : "solid"}
80
80
  type="button"
81
- onclick={`document.getElementById('${dialogId}').close()`}
81
+ data-dialog-close={dialogId}
82
82
  >
83
83
  {actionLabel}
84
84
  </RLAButton>
@@ -89,19 +89,21 @@ const isDestructive = variant === "destructive"
89
89
 
90
90
  <script>
91
91
  (function() {
92
- if (window.__rlConfirm) return;
93
- var pendingResolve = null;
92
+ const win = window as unknown as Window & { rlConfirm?: () => Promise<boolean> };
93
+ if (win.rlConfirm) return;
94
+ var pendingResolve: ((value: boolean) => void) | null = null;
94
95
 
95
96
  document.addEventListener("click", function(e) {
96
- var btn = e.target.closest("[data-slot='cancel'], [data-slot='action']");
97
+ const target = e.target as HTMLElement | null;
98
+ var btn = target?.closest("[data-slot='cancel'], [data-slot='action']");
97
99
  if (!btn || !pendingResolve) return;
98
100
  pendingResolve(btn.getAttribute("data-slot") === "action");
99
101
  pendingResolve = null;
100
102
  }, true);
101
103
 
102
- window.__rlConfirm = function() {
103
- return new Promise(function(resolve) {
104
- var dialog = document.querySelector("[data-slot='alert-dialog-container'] dialog");
104
+ win.rlConfirm = function() {
105
+ return new Promise<boolean>(function(resolve) {
106
+ var dialog = document.querySelector("[data-slot='alert-dialog-container'] dialog") as HTMLDialogElement | null;
105
107
  if (!dialog) { resolve(false); return; }
106
108
  pendingResolve = resolve;
107
109
  dialog.addEventListener("close", function() {
@@ -0,0 +1,78 @@
1
+ ---
2
+ import RLAButton from "./RLAButton.astro"
3
+ import type { ButtonProps } from "../../types"
4
+
5
+ interface Props extends Omit<ButtonProps, "href" | "label" | "leadingIcon" | "trailingIcon" | "loading" | "loadingAuto" | "square" | "active" | "activeColor" | "activeVariant"> {
6
+ raw: string
7
+ }
8
+
9
+ const {
10
+ raw,
11
+ ...rest
12
+ } = Astro.props
13
+ ---
14
+
15
+ <rla-copy-markdown {...rest}>
16
+ <RLAButton
17
+ class="rl-copy-markdown"
18
+ color="neutral"
19
+ variant="outline"
20
+ size="sm"
21
+ leadingIcon="i-lucide-copy"
22
+ label="Copy Markdown"
23
+ data-raw={raw}
24
+ />
25
+ </rla-copy-markdown>
26
+
27
+ <script>
28
+ class RLACopyMarkdown extends HTMLElement {
29
+ private copyCleanup: (() => void) | null = null;
30
+
31
+ connectedCallback() {
32
+ this.style.display = 'contents';
33
+ const copyBtn = this.querySelector(".rl-copy-markdown") as HTMLButtonElement | null;
34
+ if (!copyBtn) return;
35
+
36
+ const onClick = async () => {
37
+ try {
38
+ copyBtn.disabled = true;
39
+ const markdown = copyBtn.getAttribute("data-raw");
40
+ if (!markdown) {
41
+ throw new Error("No raw markdown content provided to copy");
42
+ }
43
+ await navigator.clipboard.writeText(markdown);
44
+
45
+ const labelEl = copyBtn.querySelector('[data-slot="label"]');
46
+ if (labelEl) {
47
+ const origText = labelEl.textContent;
48
+ labelEl.textContent = "Copied!";
49
+ setTimeout(() => {
50
+ labelEl.textContent = origText;
51
+ }, 2000);
52
+ }
53
+ } catch (err) {
54
+ console.error("Failed to copy:", err);
55
+ } finally {
56
+ copyBtn.disabled = false;
57
+ }
58
+ };
59
+
60
+ copyBtn.addEventListener("click", onClick);
61
+
62
+ this.copyCleanup = () => {
63
+ copyBtn.removeEventListener("click", onClick);
64
+ };
65
+ }
66
+
67
+ disconnectedCallback() {
68
+ if (this.copyCleanup) {
69
+ this.copyCleanup();
70
+ this.copyCleanup = null;
71
+ }
72
+ }
73
+ }
74
+
75
+ if (!customElements.get('rla-copy-markdown')) {
76
+ customElements.define('rla-copy-markdown', RLACopyMarkdown);
77
+ }
78
+ </script>
@@ -0,0 +1,30 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types"
3
+
4
+ interface Props extends HTMLAttributes<"time"> {
5
+ date: Date | string | number
6
+ locale?: string
7
+ options?: Intl.DateTimeFormatOptions
8
+ }
9
+
10
+ const {
11
+ date,
12
+ locale = Astro.currentLocale || "en-US",
13
+ options = {
14
+ year: "numeric",
15
+ month: "short",
16
+ day: "numeric",
17
+ },
18
+ ...rest
19
+ } = Astro.props
20
+
21
+ const resolvedDate = date instanceof Date ? date : new Date(date)
22
+ const formatted = isNaN(resolvedDate.getTime())
23
+ ? ""
24
+ : resolvedDate.toLocaleDateString(locale, options)
25
+ const datetime = isNaN(resolvedDate.getTime()) ? "" : resolvedDate.toISOString()
26
+ ---
27
+
28
+ <time datetime={datetime} {...rest}>
29
+ {formatted}
30
+ </time>
@@ -32,12 +32,12 @@ const dialogId = `rla-drawer-${Math.random().toString(36).substring(2, 9)}`
32
32
  class={classes.trigger}
33
33
  data-slot="trigger"
34
34
  type="button"
35
- onclick={`document.getElementById('${dialogId}').showModal()`}
35
+ data-dialog-show={dialogId}
36
36
  >
37
37
  {triggerLabel}
38
38
  </button>
39
39
  ) : (
40
- <span onclick={`document.getElementById('${dialogId}').showModal()`}>
40
+ <span data-dialog-show={dialogId}>
41
41
  <slot name="trigger" />
42
42
  </span>
43
43
  )}
@@ -55,7 +55,7 @@ const dialogId = `rla-drawer-${Math.random().toString(36).substring(2, 9)}`
55
55
  class={classes.closeButton}
56
56
  data-slot="close-button"
57
57
  type="button"
58
- onclick={`document.getElementById('${dialogId}').close()`}
58
+ data-dialog-close={dialogId}
59
59
  aria-label="Close drawer"
60
60
  >
61
61
  <span class="i-lucide-x size-4" aria-hidden="true" />
@@ -76,3 +76,50 @@ const dialogId = `rla-drawer-${Math.random().toString(36).substring(2, 9)}`
76
76
  </div>
77
77
  </dialog>
78
78
  </div>
79
+
80
+ <script>
81
+ if (!(window as any).rlaDialogListenerActive) {
82
+ (window as any).rlaDialogListenerActive = true;
83
+ document.addEventListener('click', (e) => {
84
+ const target = e.target as HTMLElement | null;
85
+ if (!target) return;
86
+
87
+ const showTrigger = target.closest('[data-dialog-show]');
88
+ if (showTrigger) {
89
+ const dialogId = showTrigger.getAttribute('data-dialog-show');
90
+ if (dialogId) {
91
+ const dialog = document.getElementById(dialogId) as HTMLDialogElement | null;
92
+ if (dialog) {
93
+ if (typeof dialog.showModal === 'function') {
94
+ dialog.showModal();
95
+ } else if (typeof (dialog as any).showmodal === 'function') {
96
+ (dialog as any).showmodal();
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ const closeTrigger = target.closest('[data-dialog-close]');
103
+ if (closeTrigger) {
104
+ const dialogId = closeTrigger.getAttribute('data-dialog-close');
105
+ if (dialogId) {
106
+ const dialog = document.getElementById(dialogId) as HTMLDialogElement | null;
107
+ dialog?.close();
108
+ }
109
+ }
110
+
111
+ if (target.tagName === 'DIALOG' && target.getAttribute('data-close-on-click-outside') === 'true') {
112
+ const rect = target.getBoundingClientRect();
113
+ const isInModal = (
114
+ rect.top <= e.clientY &&
115
+ e.clientY <= rect.top + rect.height &&
116
+ rect.left <= e.clientX &&
117
+ e.clientX <= rect.left + rect.width
118
+ );
119
+ if (!isInModal) {
120
+ (target as HTMLDialogElement).close();
121
+ }
122
+ }
123
+ });
124
+ }
125
+ </script>