@rimelight/ui 0.0.50 → 0.0.52

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimelight/ui",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "private": false,
5
5
  "description": "Rimelight Entertainment's UI Package",
6
6
  "homepage": "https://rimelight.com/docs",
@@ -54,6 +54,12 @@
54
54
  "publishConfig": {
55
55
  "access": "public"
56
56
  },
57
+ "scripts": {
58
+ "build": "vp pack",
59
+ "prepack": "pnpm run build",
60
+ "astro": "astro",
61
+ "check": "vp check --fix && astro check"
62
+ },
57
63
  "dependencies": {
58
64
  "@unocss/vite": "66.10.2",
59
65
  "unocss": "66.10.2"
@@ -63,7 +69,7 @@
63
69
  "@astrojs/solid-js": "7.0.2",
64
70
  "@astrojs/ts-plugin": "1.10.11",
65
71
  "@astrojs/vue": "7.0.2",
66
- "@rimelight/config": "0.0.8",
72
+ "@rimelight/config": "workspace:*",
67
73
  "@vitejs/plugin-vue": "6.0.8",
68
74
  "astro": "7.3.2",
69
75
  "solid-js": "1.9.15",
@@ -95,9 +101,5 @@
95
101
  "engines": {
96
102
  "node": ">=26.8.2"
97
103
  },
98
- "scripts": {
99
- "build": "vp pack",
100
- "astro": "astro",
101
- "check": "vp check --fix && astro check"
102
- }
103
- }
104
+ "packageManager": "pnpm@12.4.1"
105
+ }
@@ -1,250 +1,258 @@
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>
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
+ const {
7
+ items = [],
8
+ label,
9
+ icon,
10
+ size = "md",
11
+ color = "primary",
12
+ arrow = false,
13
+ content: contentProp = {},
14
+ disabled = false,
15
+ ...rest
16
+ } = Astro.props as DropdownMenuProps
17
+
18
+ const align = contentProp?.align || "start"
19
+ const side = contentProp?.side || "bottom"
20
+
21
+ const classes = dropdownMenuTheme(Astro.props)
22
+
23
+ // Standardize item groups: array of arrays vs array of items
24
+ const itemGroups: DropdownMenuItem[][] = Array.isArray(items) && items.length > 0 && Array.isArray(items[0])
25
+ ? (items as DropdownMenuItem[][])
26
+ : [items as DropdownMenuItem[]]
27
+ ---
28
+ <rla-dropdown-menu data-align={align} data-side={side} {...rest}>
29
+ <div class={classes.root} data-slot="root" data-dropdown-menu>
30
+ {Astro.slots.has("trigger") ? (
31
+ <div class="inline-flex cursor-pointer" data-slot="trigger" data-dropdown-trigger>
32
+ <slot name="trigger" />
33
+ </div>
34
+ ) : (
35
+ <button
36
+ type="button"
37
+ class:list={[
38
+ classes.trigger,
39
+ !label && !Astro.slots.has("default") && !Astro.slots.has("trigger") && "aspect-square !px-1.5 !py-1.5 inline-flex items-center justify-center shrink-0"
40
+ ]}
41
+ data-slot="trigger"
42
+ data-dropdown-trigger
43
+ disabled={disabled}
44
+ >
45
+ <slot>
46
+ {icon && <span class={icon + " shrink-0"} />}
47
+ {label && <span>{label}</span>}
48
+ {!icon && !label && <RLAIcon name="i-lucide-ellipsis-vertical" class="size-4" />}
49
+ </slot>
50
+ </button>
51
+ )}
52
+
53
+ <div
54
+ class={`${classes.content} hidden`}
55
+ data-slot="content"
56
+ data-dropdown-content
57
+ >
58
+ {arrow && <div class={classes.arrow} />}
59
+ <div class={classes.viewport}>
60
+ {itemGroups.map((group, groupIdx) => (
61
+ <div class={classes.group}>
62
+ {groupIdx > 0 && <div class={classes.separator} />}
63
+ {group.map((item) => {
64
+ if (item.type === "separator") {
65
+ return <div class={classes.separator} />
66
+ }
67
+ if (item.type === "label") {
68
+ return <div class={classes.label}>{item.label}</div>
69
+ }
70
+
71
+ const itemColorClass = item.color === "error"
72
+ ? "text-destructive hover:bg-destructive/10 hover:text-destructive"
73
+ : item.color === "primary"
74
+ ? "text-primary hover:bg-primary/10"
75
+ : ""
76
+
77
+ const ItemTag = item.to ? "a" : "button"
78
+
79
+ return (
80
+ <ItemTag
81
+ href={item.to}
82
+ target={item.target}
83
+ type={item.to ? undefined : "button"}
84
+ class={`${classes.item} ${itemColorClass} ${item.class || ""}`}
85
+ data-slot="item"
86
+ data-dropdown-item
87
+ data-item-type={item.type}
88
+ data-item-id={item["id"] || item["label"]}
89
+ data-item-action={item.label}
90
+ disabled={item.disabled}
91
+ >
92
+ {item.type === "checkbox" && (
93
+ <span class="shrink-0 size-4 flex items-center justify-center border border-default rounded bg-default" data-checkbox-box>
94
+ <RLAIcon name="i-lucide-check" class={`size-3 text-primary ${item.checked !== false ? "" : "hidden"}`} data-checkbox-check />
95
+ </span>
96
+ )}
97
+
98
+ {item.avatar && (
99
+ <img src={item.avatar.src} alt={item.avatar.alt || item.label || ""} class={classes.itemLeadingAvatar} />
100
+ )}
101
+
102
+ {item.icon && !item.avatar && (
103
+ <span class={`${item.icon} ${classes.itemLeadingIcon}`} />
104
+ )}
105
+
106
+ <span class={classes.itemLabel}>{item.label}</span>
107
+
108
+ {(item.shortcut || (item.kbds && item.kbds.length > 0)) && (
109
+ <span class={classes.itemTrailing}>
110
+ {item.shortcut && <span class={classes.shortcut}>{item.shortcut}</span>}
111
+ {item.kbds && item.kbds.map(kbd => (
112
+ <kbd class={classes.itemTrailingKbds}>{kbd}</kbd>
113
+ ))}
114
+ </span>
115
+ )}
116
+ </ItemTag>
117
+ )
118
+ })}
119
+ </div>
120
+ ))}
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </rla-dropdown-menu>
125
+
126
+ <script>
127
+ class RLADropdownMenu extends HTMLElement {
128
+ private dropdownCleanup: (() => void) | null = null;
129
+
130
+ connectedCallback() {
131
+ this.style.display = 'inline-block';
132
+ const trigger = this.querySelector("[data-dropdown-trigger]") as HTMLElement | null;
133
+ const content = this.querySelector("[data-dropdown-content]") as HTMLElement | null;
134
+
135
+ if (!trigger || !content) return;
136
+
137
+ const updatePosition = () => {
138
+ if (content.classList.contains("hidden")) return;
139
+ const rect = trigger.getBoundingClientRect();
140
+ const align = this.getAttribute("data-align") || "start";
141
+ const side = this.getAttribute("data-side") || "bottom";
142
+
143
+ content.style.position = "fixed";
144
+ content.style.zIndex = "999999";
145
+ content.style.margin = "0";
146
+
147
+ if (side === "top") {
148
+ content.style.top = `${rect.top - content.offsetHeight - 4}px`;
149
+ } else {
150
+ content.style.top = `${rect.bottom + 4}px`;
151
+ }
152
+
153
+ if (align === "end") {
154
+ content.style.right = `${window.innerWidth - rect.right}px`;
155
+ content.style.left = "auto";
156
+ } else if (align === "center") {
157
+ content.style.left = `${rect.left + (rect.width / 2) - (content.offsetWidth / 2)}px`;
158
+ content.style.right = "auto";
159
+ } else {
160
+ content.style.left = `${rect.left}px`;
161
+ content.style.right = "auto";
162
+ }
163
+ };
164
+
165
+ const toggleMenu = (e: MouseEvent) => {
166
+ e.stopPropagation();
167
+ e.preventDefault();
168
+ const isHidden = content.classList.contains("hidden");
169
+
170
+ document.querySelectorAll("[data-dropdown-content]").forEach(el => {
171
+ if (el !== content) el.classList.add("hidden");
172
+ });
173
+
174
+ if (isHidden) {
175
+ if (content.parentElement !== document.body) {
176
+ document.body.appendChild(content);
177
+ }
178
+ content.classList.remove("hidden");
179
+ updatePosition();
180
+ } else {
181
+ content.classList.add("hidden");
182
+ }
183
+ };
184
+
185
+ trigger.addEventListener("click", toggleMenu);
186
+
187
+ const onDocumentClick = (e: MouseEvent) => {
188
+ const target = e.target as Node | null;
189
+ if (!target) return;
190
+ if (trigger.contains(target) || content.contains(target)) return;
191
+ content.classList.add("hidden");
192
+ };
193
+ document.addEventListener("click", onDocumentClick);
194
+
195
+ const onScrollOrResize = () => {
196
+ if (!content.classList.contains("hidden")) {
197
+ updatePosition();
198
+ }
199
+ };
200
+ window.addEventListener("scroll", onScrollOrResize, true);
201
+ window.addEventListener("resize", onScrollOrResize);
202
+
203
+ const onItemClick = (e: MouseEvent) => {
204
+ const itemEl = (e.target as HTMLElement).closest("[data-dropdown-item]") as HTMLElement | null;
205
+ if (itemEl && !itemEl.hasAttribute("disabled")) {
206
+ const itemType = itemEl.getAttribute("data-item-type");
207
+ if (itemType === "checkbox") {
208
+ e.stopPropagation();
209
+ const checkIcon = itemEl.querySelector("[data-checkbox-check]");
210
+ const isCurrentlyChecked = checkIcon ? !checkIcon.classList.contains("hidden") : false;
211
+ const newChecked = !isCurrentlyChecked;
212
+ if (checkIcon) {
213
+ if (newChecked) {
214
+ checkIcon.classList.remove("hidden");
215
+ } else {
216
+ checkIcon.classList.add("hidden");
217
+ }
218
+ }
219
+ this.dispatchEvent(new CustomEvent("rla-dropdown-checkbox-change", {
220
+ detail: {
221
+ id: itemEl.getAttribute("data-item-id"),
222
+ action: itemEl.getAttribute("data-item-action"),
223
+ checked: newChecked,
224
+ event: e
225
+ },
226
+ bubbles: true
227
+ }));
228
+ } else {
229
+ content.classList.add("hidden");
230
+ }
231
+ }
232
+ };
233
+ content.addEventListener("click", onItemClick);
234
+
235
+ this.dropdownCleanup = () => {
236
+ trigger.removeEventListener("click", toggleMenu);
237
+ document.removeEventListener("click", onDocumentClick);
238
+ window.removeEventListener("scroll", onScrollOrResize, true);
239
+ window.removeEventListener("resize", onScrollOrResize);
240
+ content.removeEventListener("click", onItemClick);
241
+ if (content.parentElement === document.body) {
242
+ content.remove();
243
+ }
244
+ };
245
+ }
246
+
247
+ disconnectedCallback() {
248
+ if (this.dropdownCleanup) {
249
+ this.dropdownCleanup();
250
+ this.dropdownCleanup = null;
251
+ }
252
+ }
253
+ }
254
+
255
+ if (!customElements.get('rla-dropdown-menu')) {
256
+ customElements.define('rla-dropdown-menu', RLADropdownMenu);
257
+ }
258
+ </script>