@rimelight/ui 0.0.47 → 0.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/index.d.mts +4 -5
  2. package/dist/preset.mjs +73 -1
  3. package/dist/resolver.d.mts +3 -4
  4. package/dist/resolver.mjs +2 -4
  5. package/dist/shortcuts.d.mts +10 -11
  6. package/dist/stores.d.mts +3 -4
  7. package/package.json +8 -6
  8. package/src/components/3d-hover/RLS3dHover.tsx +21 -0
  9. package/src/components/accordion/RLSAccordion.tsx +112 -0
  10. package/src/components/app/RLSApp.tsx +21 -0
  11. package/src/components/aspect-ratio/RLSAspectRatio.tsx +30 -0
  12. package/src/components/audio/RLSAudio.tsx +27 -0
  13. package/src/components/avatar/RLSAvatar.tsx +142 -0
  14. package/src/components/avatar/avatar.ts +22 -21
  15. package/src/components/avatar-group/RLSAvatarGroup.tsx +163 -0
  16. package/src/components/badge/RLSBadge.tsx +1 -1
  17. package/src/components/badge/RLVBadge.vue +5 -3
  18. package/src/components/banner/RLSBanner.tsx +151 -0
  19. package/src/components/breadcrumb/RLSBreadcrumb.tsx +194 -0
  20. package/src/components/browser-mockup/RLSBrowserMockup.tsx +21 -0
  21. package/src/components/button/RLSButton.tsx +49 -40
  22. package/src/components/button/RLVButton.vue +44 -32
  23. package/src/components/calendar/RLSCalendar.tsx +26 -0
  24. package/src/components/callout/RLSCallout.tsx +151 -0
  25. package/src/components/card/RLSCard.tsx +4 -0
  26. package/src/components/card/RLVCard.vue +4 -0
  27. package/src/components/card/card.ts +10 -0
  28. package/src/components/carousel/RLACarousel.astro +14 -1
  29. package/src/components/carousel/RLSCarousel.tsx +260 -0
  30. package/src/components/chart/RLSChart.tsx +299 -0
  31. package/src/components/chat/RLSChat.tsx +27 -0
  32. package/src/components/chat-message/RLSChatMessage.tsx +27 -0
  33. package/src/components/chat-messages/RLSChatMessages.tsx +27 -0
  34. package/src/components/chat-palette/RLSChatPalette.tsx +27 -0
  35. package/src/components/chat-prompt/RLSChatPrompt.tsx +27 -0
  36. package/src/components/chat-prompt-submit/RLSChatPromptSubmit.tsx +27 -0
  37. package/src/components/chat-reasoning/RLSChatReasoning.tsx +27 -0
  38. package/src/components/chat-shimmer/RLSChatShimmer.tsx +27 -0
  39. package/src/components/chat-tool/RLSChatTool.tsx +27 -0
  40. package/src/components/checkbox/RLACheckbox.astro +1 -0
  41. package/src/components/checkbox/RLSCheckbox.tsx +1 -1
  42. package/src/components/checkbox/checkbox.theme.ts +2 -2
  43. package/src/components/chip/RLSChip.tsx +65 -0
  44. package/src/components/collapsible/RLSCollapsible.tsx +89 -0
  45. package/src/components/color-area/RLSColorArea.tsx +554 -0
  46. package/src/components/color-field/RLSColorField.tsx +236 -0
  47. package/src/components/color-picker/RLSColorPicker.tsx +26 -0
  48. package/src/components/color-slider/RLSColorSlider.tsx +373 -0
  49. package/src/components/command-palette/RLSCommandPalette.tsx +26 -0
  50. package/src/components/compare/RLSCompare.tsx +26 -0
  51. package/src/components/confirm/RLSConfirm.tsx +173 -0
  52. package/src/components/container/RLSContainer.tsx +24 -0
  53. package/src/components/context-menu/RLSContextMenu.tsx +26 -0
  54. package/src/components/copy-markdown/RLSCopyMarkdown.tsx +57 -0
  55. package/src/components/dashboard-group/RLSDashboardGroup.tsx +29 -0
  56. package/src/components/dashboard-navbar/RLSDashboardNavbar.tsx +116 -0
  57. package/src/components/dashboard-panel/RLSDashboardPanel.tsx +84 -0
  58. package/src/components/dashboard-resize-handle/RLSDashboardResizeHandle.tsx +35 -0
  59. package/src/components/dashboard-search/RLADashboardSearch.astro +1 -1
  60. package/src/components/dashboard-search/RLSDashboardSearch.tsx +125 -0
  61. package/src/components/dashboard-search-button/RLSDashboardSearchButton.tsx +82 -0
  62. package/src/components/dashboard-sidebar/RLSDashboardSidebar.tsx +184 -0
  63. package/src/components/dashboard-sidebar-collapse/RLSDashboardSidebarCollapse.tsx +46 -0
  64. package/src/components/dashboard-sidebar-toggle/RLSDashboardSidebarToggle.tsx +35 -0
  65. package/src/components/dashboard-toolbar/RLSDashboardToolbar.tsx +49 -0
  66. package/src/components/date/RLSDate.tsx +34 -0
  67. package/src/components/dock/RLSDock.tsx +26 -0
  68. package/src/components/drawer/RLSDrawer.tsx +177 -0
  69. package/src/components/dropdown-menu/RLADropdownMenu.astro +250 -241
  70. package/src/components/dropdown-menu/RLSDropdownMenu.tsx +183 -86
  71. package/src/components/editor/RLSEditor.tsx +26 -0
  72. package/src/components/empty/RLSEmpty.tsx +132 -0
  73. package/src/components/error/RLSError.tsx +26 -0
  74. package/src/components/field-group/RLSFieldGroup.tsx +46 -0
  75. package/src/components/fieldset/RLSFieldset.tsx +26 -0
  76. package/src/components/file-upload/RLSFileUpload.tsx +163 -0
  77. package/src/components/floating-action/RLSFloatingAction.tsx +26 -0
  78. package/src/components/footer/RLSFooter.tsx +56 -0
  79. package/src/components/form/RLSForm.tsx +34 -0
  80. package/src/components/gallery/RLSGallery.tsx +21 -0
  81. package/src/components/gamepad/RLSGamepad.tsx +269 -0
  82. package/src/components/grid/RLSGrid.tsx +29 -0
  83. package/src/components/head/RLAHead.astro +9 -0
  84. package/src/components/head/RLSHead.tsx +42 -0
  85. package/src/components/head/UIHead.astro +3 -0
  86. package/src/components/header/RLSHeader.tsx +87 -0
  87. package/src/components/hover-card/RLSHoverCard.tsx +72 -0
  88. package/src/components/icon/RLSIcon.tsx +3 -1
  89. package/src/components/image/RLAImage.astro +447 -138
  90. package/src/components/image/RLSImage.tsx +545 -0
  91. package/src/components/image/image.theme.ts +19 -3
  92. package/src/components/image/image.ts +31 -1
  93. package/src/components/input/RLAInput.astro +62 -51
  94. package/src/components/input/RLSInput.tsx +1 -1
  95. package/src/components/input-date/RLSInputDate.tsx +92 -0
  96. package/src/components/input-menu/RLSInputMenu.tsx +199 -0
  97. package/src/components/input-number/RLSInputNumber.tsx +184 -0
  98. package/src/components/input-pin/RLSInputPin.tsx +185 -0
  99. package/src/components/input-rating/RLSInputRating.tsx +198 -0
  100. package/src/components/input-rating/input-rating.ts +4 -0
  101. package/src/components/input-tags/RLSInputTags.tsx +94 -0
  102. package/src/components/input-time/RLSInputTime.tsx +92 -0
  103. package/src/components/kbd/RLSKbd.tsx +37 -0
  104. package/src/components/keyboard/RLSKeyboard.tsx +476 -0
  105. package/src/components/knob/RLSKnob.tsx +27 -0
  106. package/src/components/label/RLSLabel.tsx +27 -0
  107. package/src/components/layout-grid/RLSLayoutGrid.tsx +402 -0
  108. package/src/components/layout-grid/layout-grid.ts +25 -1
  109. package/src/components/link/RLSLink.tsx +2 -1
  110. package/src/components/link/link.ts +21 -12
  111. package/src/components/link-group/RLSLinkGroup.tsx +76 -0
  112. package/src/components/listbox/RLSListbox.tsx +27 -0
  113. package/src/components/locale-selector/RLALocaleSelector.astro +2 -1
  114. package/src/components/locale-selector/RLSLocaleSelector.tsx +178 -0
  115. package/src/components/logo/RLALogo.astro +69 -4
  116. package/src/components/logo/RLSLogo.tsx +129 -0
  117. package/src/components/main/RLSMain.tsx +40 -0
  118. package/src/components/marquee/RLSMarquee.tsx +80 -0
  119. package/src/components/meter/RLSMeter.tsx +27 -0
  120. package/src/components/modal/RLAModal.astro +1 -1
  121. package/src/components/modal/RLSModal.tsx +109 -72
  122. package/src/components/navigation-menu/RLSNavigationMenu.tsx +228 -0
  123. package/src/components/navigation-menu/navigation-menu.ts +2 -2
  124. package/src/components/package-managers/RLSPackageManagers.tsx +204 -0
  125. package/src/components/page/RLSPage.tsx +21 -0
  126. package/src/components/page-aside/RLSPageAside.tsx +21 -0
  127. package/src/components/page-body/RLSPageBody.tsx +21 -0
  128. package/src/components/page-header/RLSPageHeader.tsx +21 -0
  129. package/src/components/page-section/RLSPageSection.tsx +201 -0
  130. package/src/components/pagination/RLSPagination.tsx +114 -0
  131. package/src/components/password/RLSPassword.tsx +27 -0
  132. package/src/components/phone-mockup/RLSPhoneMockup.tsx +27 -0
  133. package/src/components/placeholder/RLSPlaceholder.tsx +47 -0
  134. package/src/components/popover/RLSPopover.tsx +124 -0
  135. package/src/components/post/RLSPost.tsx +134 -0
  136. package/src/components/pricing-plan/RLSPricingPlan.tsx +26 -0
  137. package/src/components/pricing-table/RLSPricingTable.tsx +26 -0
  138. package/src/components/progress/RLSProgress.tsx +53 -0
  139. package/src/components/prose/RLSProse.tsx +26 -0
  140. package/src/components/qr-code/RLSQrCode.tsx +26 -0
  141. package/src/components/quote/RLAQuote.astro +10 -6
  142. package/src/components/quote/RLSQuote.tsx +37 -0
  143. package/src/components/quote/quote.theme.ts +5 -2
  144. package/src/components/radio-group/RLSRadioGroup.tsx +37 -0
  145. package/src/components/rating/RLSRating.tsx +27 -0
  146. package/src/components/scroll-area/RLSScrollArea.tsx +210 -0
  147. package/src/components/scroll-to-top/RLAScrollToTop.astro +1 -0
  148. package/src/components/scroll-to-top/RLSScrollToTop.tsx +145 -0
  149. package/src/components/search/RLSSearch.tsx +237 -0
  150. package/src/components/select/RLASelect.astro +67 -4
  151. package/src/components/select/RLSSelect.tsx +107 -39
  152. package/src/components/select/RLVSelect.vue +50 -1
  153. package/src/components/select/select.theme.ts +30 -10
  154. package/src/components/select/select.ts +20 -1
  155. package/src/components/separator/RLSSeparator.tsx +92 -0
  156. package/src/components/share/RLSShare.tsx +66 -0
  157. package/src/components/shortcuts/RLSShortcuts.tsx +393 -0
  158. package/src/components/sidebar/RLSSidebar.tsx +271 -0
  159. package/src/components/skeleton/RLSSkeleton.tsx +21 -0
  160. package/src/components/slideover/RLASlideover.astro +1 -1
  161. package/src/components/slideover/RLSSlideover.tsx +159 -0
  162. package/src/components/slider/RLSSlider.tsx +112 -0
  163. package/src/components/speed-dial/RLSSpeedDial.tsx +27 -0
  164. package/src/components/spinner/RLSSpinner.tsx +22 -0
  165. package/src/components/splitter/RLSSplitter.tsx +178 -0
  166. package/src/components/spoiler/RLSSpoiler.tsx +26 -0
  167. package/src/components/stepper/RLSStepper.tsx +205 -0
  168. package/src/components/steps/RLSSteps.tsx +108 -0
  169. package/src/components/sticky-surface/RLSStickySurface.tsx +63 -0
  170. package/src/components/surround/RLSSurround.tsx +57 -0
  171. package/src/components/switch/RLSSwitch.tsx +56 -0
  172. package/src/components/table/RLATable.astro +1044 -579
  173. package/src/components/table/RLSTable.tsx +893 -0
  174. package/src/components/table/table.theme.ts +47 -16
  175. package/src/components/table/table.ts +104 -66
  176. package/src/components/table-of-contents/RLSTableOfContents.tsx +196 -0
  177. package/src/components/tabs/RLSTabs.tsx +298 -0
  178. package/src/components/textarea/RLATextarea.astro +1 -0
  179. package/src/components/textarea/RLSTextarea.tsx +3 -1
  180. package/src/components/theme-selector/RLAThemeSelector.astro +85 -56
  181. package/src/components/theme-selector/RLSThemeSelector.tsx +131 -0
  182. package/src/components/theme-selector/theme-selector.theme.ts +5 -7
  183. package/src/components/theme-selector/theme-selector.ts +11 -1
  184. package/src/components/timeline/RLSTimeline.tsx +171 -0
  185. package/src/components/toast/RLSToast.tsx +179 -0
  186. package/src/components/toaster/RLSToaster.tsx +82 -0
  187. package/src/components/tooltip/RLSTooltip.tsx +48 -0
  188. package/src/components/tour/RLSTour.tsx +26 -0
  189. package/src/components/tree/RLSTree.tsx +26 -0
  190. package/src/components/user/RLSUser.tsx +26 -0
  191. package/src/components/video/RLAVideo.astro +4 -1
  192. package/src/components/video/RLSVideo.tsx +158 -0
  193. package/src/components/watermark/RLSWatermark.tsx +27 -0
  194. package/src/components/window-mockup/RLSWindowMockup.tsx +27 -0
  195. package/src/preset.ts +73 -1
  196. package/src/resolver.ts +6 -1
  197. package/src/virtual.d.ts +6 -0
@@ -1,241 +1,250 @@
1
- ---
2
- import type { DropdownMenuProps, DropdownMenuItem } from "./dropdown-menu"
3
- import { dropdownMenuTheme } from "./dropdown-menu.theme"
4
- import RLAIcon from "../icon/RLAIcon.astro"
5
-
6
-
7
- const {
8
- items = [],
9
- label,
10
- icon,
11
- size = "md",
12
- color = "primary",
13
- arrow = false,
14
- content: contentProp = {},
15
- disabled = false,
16
- ...rest
17
- } = Astro.props as DropdownMenuProps
18
-
19
- const align = contentProp?.align || "start"
20
- const side = contentProp?.side || "bottom"
21
-
22
- const classes = dropdownMenuTheme(Astro.props)
23
-
24
- // Standardize item groups: array of arrays vs array of items
25
- const itemGroups: DropdownMenuItem[][] = Array.isArray(items) && items.length > 0 && Array.isArray(items[0])
26
- ? (items as DropdownMenuItem[][])
27
- : [items as DropdownMenuItem[]]
28
- ---
29
- <rla-dropdown-menu data-align={align} data-side={side} {...rest}>
30
- <div class={classes.root} data-slot="root" data-dropdown-menu>
31
- <button
32
- type="button"
33
- class={classes.trigger}
34
- data-slot="trigger"
35
- data-dropdown-trigger
36
- disabled={disabled}
37
- >
38
- <slot name="trigger">
39
- <slot>
40
- {icon && <span class={icon + " shrink-0"} />}
41
- {label && <span>{label}</span>}
42
- {!icon && !label && <RLAIcon name="i-lucide-ellipsis-vertical" class="size-4" />}
43
- </slot>
44
- </slot>
45
- </button>
46
-
47
- <div
48
- class={`${classes.content} hidden`}
49
- data-slot="content"
50
- data-dropdown-content
51
- >
52
-
53
- {arrow && <div class={classes.arrow} />}
54
- <div class={classes.viewport}>
55
- {itemGroups.map((group, groupIdx) => (
56
- <div class={classes.group}>
57
- {groupIdx > 0 && <div class={classes.separator} />}
58
- {group.map((item) => {
59
- if (item.type === "separator") {
60
- return <div class={classes.separator} />
61
- }
62
- if (item.type === "label") {
63
- return <div class={classes.label}>{item.label}</div>
64
- }
65
-
66
- const itemColorClass = item.color === "error"
67
- ? "text-destructive hover:bg-destructive/10 hover:text-destructive"
68
- : item.color === "primary"
69
- ? "text-primary hover:bg-primary/10"
70
- : ""
71
-
72
- const ItemTag = item.to ? "a" : "button"
73
-
74
- return (
75
- <ItemTag
76
- href={item.to}
77
- target={item.target}
78
- type={item.to ? undefined : "button"}
79
- class={`${classes.item} ${itemColorClass} ${item.class || ""}`}
80
- data-slot="item"
81
- data-dropdown-item
82
- data-item-type={item.type}
83
- data-item-id={item["id"] || item["label"]}
84
- data-item-action={item.label}
85
- disabled={item.disabled}
86
- >
87
- {item.type === "checkbox" && (
88
- <span class="shrink-0 size-4 flex items-center justify-center border border-default rounded bg-default" data-checkbox-box>
89
- <RLAIcon name="i-lucide-check" class={`size-3 text-primary ${item.checked !== false ? "" : "hidden"}`} data-checkbox-check />
90
- </span>
91
- )}
92
-
93
- {item.avatar && (
94
- <img src={item.avatar.src} alt={item.avatar.alt || item.label || ""} class={classes.itemLeadingAvatar} />
95
- )}
96
-
97
- {item.icon && !item.avatar && (
98
- <span class={`${item.icon} ${classes.itemLeadingIcon}`} />
99
- )}
100
-
101
- <span class={classes.itemLabel}>{item.label}</span>
102
-
103
- {(item.shortcut || (item.kbds && item.kbds.length > 0)) && (
104
- <span class={classes.itemTrailing}>
105
- {item.shortcut && <span class={classes.shortcut}>{item.shortcut}</span>}
106
- {item.kbds && item.kbds.map(kbd => (
107
- <kbd class={classes.itemTrailingKbds}>{kbd}</kbd>
108
- ))}
109
- </span>
110
- )}
111
- </ItemTag>
112
- )
113
- })}
114
- </div>
115
- ))}
116
- </div>
117
- </div>
118
- </div>
119
- </rla-dropdown-menu>
120
-
121
- <script>
122
- class RLADropdownMenu extends HTMLElement {
123
- private dropdownCleanup: (() => void) | null = null;
124
-
125
- connectedCallback() {
126
- this.style.display = 'inline-block';
127
- const trigger = this.querySelector("[data-dropdown-trigger]") as HTMLElement | null;
128
- const content = this.querySelector("[data-dropdown-content]") as HTMLElement | null;
129
-
130
- if (!trigger || !content) return;
131
-
132
- const updatePosition = () => {
133
- if (content.classList.contains("hidden")) return;
134
- const rect = trigger.getBoundingClientRect();
135
- const align = this.getAttribute("data-align") || "start";
136
- const side = this.getAttribute("data-side") || "bottom";
137
-
138
- content.style.position = "fixed";
139
- content.style.zIndex = "9999";
140
-
141
- if (side === "top") {
142
- content.style.top = `${rect.top - content.offsetHeight - 4}px`;
143
- } else {
144
- content.style.top = `${rect.bottom + 4}px`;
145
- }
146
-
147
- if (align === "end") {
148
- content.style.left = `${rect.right - content.offsetWidth}px`;
149
- } else if (align === "center") {
150
- content.style.left = `${rect.left + (rect.width / 2) - (content.offsetWidth / 2)}px`;
151
- } else {
152
- content.style.left = `${rect.left}px`;
153
- }
154
- };
155
-
156
- const toggleMenu = (e: MouseEvent) => {
157
- e.stopPropagation();
158
- const isHidden = content.classList.contains("hidden");
159
-
160
- document.querySelectorAll("[data-dropdown-content]").forEach(el => {
161
- if (el !== content) el.classList.add("hidden");
162
- });
163
-
164
- if (isHidden) {
165
- content.classList.remove("hidden");
166
- updatePosition();
167
- } else {
168
- content.classList.add("hidden");
169
- }
170
- };
171
-
172
- trigger.addEventListener("click", toggleMenu);
173
-
174
- const onDocumentClick = (e: MouseEvent) => {
175
- if (!this.contains(e.target as Node) && !content.contains(e.target as Node)) {
176
- content.classList.add("hidden");
177
- }
178
- };
179
- document.addEventListener("click", onDocumentClick);
180
-
181
- const onScrollOrResize = () => {
182
- if (!content.classList.contains("hidden")) {
183
- updatePosition();
184
- }
185
- };
186
- window.addEventListener("scroll", onScrollOrResize, true);
187
- window.addEventListener("resize", onScrollOrResize);
188
-
189
- const onItemClick = (e: MouseEvent) => {
190
- const itemEl = (e.target as HTMLElement).closest("[data-dropdown-item]") as HTMLElement | null;
191
- if (itemEl && !itemEl.hasAttribute("disabled")) {
192
- const itemType = itemEl.getAttribute("data-item-type");
193
- if (itemType === "checkbox") {
194
- e.stopPropagation();
195
- const checkIcon = itemEl.querySelector("[data-checkbox-check]");
196
- const isCurrentlyChecked = checkIcon ? !checkIcon.classList.contains("hidden") : false;
197
- const newChecked = !isCurrentlyChecked;
198
- if (checkIcon) {
199
- if (newChecked) {
200
- checkIcon.classList.remove("hidden");
201
- } else {
202
- checkIcon.classList.add("hidden");
203
- }
204
- }
205
- this.dispatchEvent(new CustomEvent("rla-dropdown-checkbox-change", {
206
- detail: {
207
- id: itemEl.getAttribute("data-item-id"),
208
- action: itemEl.getAttribute("data-item-action"),
209
- checked: newChecked,
210
- event: e
211
- },
212
- bubbles: true
213
- }));
214
- } else {
215
- content.classList.add("hidden");
216
- }
217
- }
218
- };
219
- content.addEventListener("click", onItemClick);
220
-
221
- this.dropdownCleanup = () => {
222
- trigger.removeEventListener("click", toggleMenu);
223
- document.removeEventListener("click", onDocumentClick);
224
- window.removeEventListener("scroll", onScrollOrResize, true);
225
- window.removeEventListener("resize", onScrollOrResize);
226
- content.removeEventListener("click", onItemClick);
227
- };
228
- }
229
-
230
- disconnectedCallback() {
231
- if (this.dropdownCleanup) {
232
- this.dropdownCleanup();
233
- this.dropdownCleanup = null;
234
- }
235
- }
236
- }
237
-
238
- if (!customElements.get('rla-dropdown-menu')) {
239
- customElements.define('rla-dropdown-menu', RLADropdownMenu);
240
- }
241
- </script>
1
+ ---
2
+ import type { DropdownMenuProps, DropdownMenuItem } from "./dropdown-menu"
3
+ import { dropdownMenuTheme } from "./dropdown-menu.theme"
4
+ import RLAIcon from "../icon/RLAIcon.astro"
5
+
6
+
7
+ const {
8
+ items = [],
9
+ label,
10
+ icon,
11
+ size = "md",
12
+ color = "primary",
13
+ arrow = false,
14
+ content: contentProp = {},
15
+ disabled = false,
16
+ ...rest
17
+ } = Astro.props as DropdownMenuProps
18
+
19
+ const align = contentProp?.align || "start"
20
+ const side = contentProp?.side || "bottom"
21
+
22
+ const classes = dropdownMenuTheme(Astro.props)
23
+
24
+ // Standardize item groups: array of arrays vs array of items
25
+ const itemGroups: DropdownMenuItem[][] = Array.isArray(items) && items.length > 0 && Array.isArray(items[0])
26
+ ? (items as DropdownMenuItem[][])
27
+ : [items as DropdownMenuItem[]]
28
+ ---
29
+ <rla-dropdown-menu data-align={align} data-side={side} {...rest}>
30
+ <div class={classes.root} data-slot="root" data-dropdown-menu>
31
+ <button
32
+ type="button"
33
+ class={classes.trigger}
34
+ data-slot="trigger"
35
+ data-dropdown-trigger
36
+ disabled={disabled}
37
+ >
38
+ <slot name="trigger">
39
+ <slot>
40
+ {icon && <span class={icon + " shrink-0"} />}
41
+ {label && <span>{label}</span>}
42
+ {!icon && !label && <RLAIcon name="i-lucide-ellipsis-vertical" class="size-4" />}
43
+ </slot>
44
+ </slot>
45
+ </button>
46
+
47
+ <div
48
+ class={`${classes.content} hidden`}
49
+ data-slot="content"
50
+ data-dropdown-content
51
+ >
52
+
53
+ {arrow && <div class={classes.arrow} />}
54
+ <div class={classes.viewport}>
55
+ {itemGroups.map((group, groupIdx) => (
56
+ <div class={classes.group}>
57
+ {groupIdx > 0 && <div class={classes.separator} />}
58
+ {group.map((item) => {
59
+ if (item.type === "separator") {
60
+ return <div class={classes.separator} />
61
+ }
62
+ if (item.type === "label") {
63
+ return <div class={classes.label}>{item.label}</div>
64
+ }
65
+
66
+ const itemColorClass = item.color === "error"
67
+ ? "text-destructive hover:bg-destructive/10 hover:text-destructive"
68
+ : item.color === "primary"
69
+ ? "text-primary hover:bg-primary/10"
70
+ : ""
71
+
72
+ const ItemTag = item.to ? "a" : "button"
73
+
74
+ return (
75
+ <ItemTag
76
+ href={item.to}
77
+ target={item.target}
78
+ type={item.to ? undefined : "button"}
79
+ class={`${classes.item} ${itemColorClass} ${item.class || ""}`}
80
+ data-slot="item"
81
+ data-dropdown-item
82
+ data-item-type={item.type}
83
+ data-item-id={item["id"] || item["label"]}
84
+ data-item-action={item.label}
85
+ disabled={item.disabled}
86
+ >
87
+ {item.type === "checkbox" && (
88
+ <span class="shrink-0 size-4 flex items-center justify-center border border-default rounded bg-default" data-checkbox-box>
89
+ <RLAIcon name="i-lucide-check" class={`size-3 text-primary ${item.checked !== false ? "" : "hidden"}`} data-checkbox-check />
90
+ </span>
91
+ )}
92
+
93
+ {item.avatar && (
94
+ <img src={item.avatar.src} alt={item.avatar.alt || item.label || ""} class={classes.itemLeadingAvatar} />
95
+ )}
96
+
97
+ {item.icon && !item.avatar && (
98
+ <span class={`${item.icon} ${classes.itemLeadingIcon}`} />
99
+ )}
100
+
101
+ <span class={classes.itemLabel}>{item.label}</span>
102
+
103
+ {(item.shortcut || (item.kbds && item.kbds.length > 0)) && (
104
+ <span class={classes.itemTrailing}>
105
+ {item.shortcut && <span class={classes.shortcut}>{item.shortcut}</span>}
106
+ {item.kbds && item.kbds.map(kbd => (
107
+ <kbd class={classes.itemTrailingKbds}>{kbd}</kbd>
108
+ ))}
109
+ </span>
110
+ )}
111
+ </ItemTag>
112
+ )
113
+ })}
114
+ </div>
115
+ ))}
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </rla-dropdown-menu>
120
+
121
+ <script>
122
+ class RLADropdownMenu extends HTMLElement {
123
+ private dropdownCleanup: (() => void) | null = null;
124
+
125
+ connectedCallback() {
126
+ this.style.display = 'inline-block';
127
+ const trigger = this.querySelector("[data-dropdown-trigger]") as HTMLElement | null;
128
+ const content = this.querySelector("[data-dropdown-content]") as HTMLElement | null;
129
+
130
+ if (!trigger || !content) return;
131
+
132
+ const updatePosition = () => {
133
+ if (content.classList.contains("hidden")) return;
134
+ const rect = trigger.getBoundingClientRect();
135
+ const align = this.getAttribute("data-align") || "start";
136
+ const side = this.getAttribute("data-side") || "bottom";
137
+
138
+ content.style.position = "fixed";
139
+ content.style.zIndex = "99999";
140
+
141
+ if (side === "top") {
142
+ content.style.top = `${rect.top - content.offsetHeight - 4}px`;
143
+ } else {
144
+ content.style.top = `${rect.bottom + 4}px`;
145
+ }
146
+
147
+ if (align === "end") {
148
+ content.style.right = `${window.innerWidth - rect.right}px`;
149
+ content.style.left = "auto";
150
+ } else if (align === "center") {
151
+ content.style.left = `${rect.left + (rect.width / 2) - (content.offsetWidth / 2)}px`;
152
+ content.style.right = "auto";
153
+ } else {
154
+ content.style.left = `${rect.left}px`;
155
+ content.style.right = "auto";
156
+ }
157
+ };
158
+
159
+ const toggleMenu = (e: MouseEvent) => {
160
+ e.stopPropagation();
161
+ const isHidden = content.classList.contains("hidden");
162
+
163
+ document.querySelectorAll("[data-dropdown-content]").forEach(el => {
164
+ if (el !== content) el.classList.add("hidden");
165
+ });
166
+
167
+ if (isHidden) {
168
+ if (content.parentElement !== document.body) {
169
+ document.body.appendChild(content);
170
+ }
171
+ content.classList.remove("hidden");
172
+ updatePosition();
173
+ } else {
174
+ content.classList.add("hidden");
175
+ }
176
+ };
177
+
178
+ trigger.addEventListener("click", toggleMenu);
179
+
180
+ const onDocumentClick = (e: MouseEvent) => {
181
+ if (!this.contains(e.target as Node) && !content.contains(e.target as Node)) {
182
+ content.classList.add("hidden");
183
+ }
184
+ };
185
+ document.addEventListener("click", onDocumentClick);
186
+
187
+ const onScrollOrResize = () => {
188
+ if (!content.classList.contains("hidden")) {
189
+ updatePosition();
190
+ }
191
+ };
192
+ window.addEventListener("scroll", onScrollOrResize, true);
193
+ window.addEventListener("resize", onScrollOrResize);
194
+
195
+ const onItemClick = (e: MouseEvent) => {
196
+ const itemEl = (e.target as HTMLElement).closest("[data-dropdown-item]") as HTMLElement | null;
197
+ if (itemEl && !itemEl.hasAttribute("disabled")) {
198
+ const itemType = itemEl.getAttribute("data-item-type");
199
+ if (itemType === "checkbox") {
200
+ e.stopPropagation();
201
+ const checkIcon = itemEl.querySelector("[data-checkbox-check]");
202
+ const isCurrentlyChecked = checkIcon ? !checkIcon.classList.contains("hidden") : false;
203
+ const newChecked = !isCurrentlyChecked;
204
+ if (checkIcon) {
205
+ if (newChecked) {
206
+ checkIcon.classList.remove("hidden");
207
+ } else {
208
+ checkIcon.classList.add("hidden");
209
+ }
210
+ }
211
+ this.dispatchEvent(new CustomEvent("rla-dropdown-checkbox-change", {
212
+ detail: {
213
+ id: itemEl.getAttribute("data-item-id"),
214
+ action: itemEl.getAttribute("data-item-action"),
215
+ checked: newChecked,
216
+ event: e
217
+ },
218
+ bubbles: true
219
+ }));
220
+ } else {
221
+ content.classList.add("hidden");
222
+ }
223
+ }
224
+ };
225
+ content.addEventListener("click", onItemClick);
226
+
227
+ this.dropdownCleanup = () => {
228
+ trigger.removeEventListener("click", toggleMenu);
229
+ document.removeEventListener("click", onDocumentClick);
230
+ window.removeEventListener("scroll", onScrollOrResize, true);
231
+ window.removeEventListener("resize", onScrollOrResize);
232
+ content.removeEventListener("click", onItemClick);
233
+ if (content.parentElement === document.body) {
234
+ content.remove();
235
+ }
236
+ };
237
+ }
238
+
239
+ disconnectedCallback() {
240
+ if (this.dropdownCleanup) {
241
+ this.dropdownCleanup();
242
+ this.dropdownCleanup = null;
243
+ }
244
+ }
245
+ }
246
+
247
+ if (!customElements.get('rla-dropdown-menu')) {
248
+ customElements.define('rla-dropdown-menu', RLADropdownMenu);
249
+ }
250
+ </script>