@raylang/elements 0.0.0-stage → 0.4.0

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 (95) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +12 -2
  4. package/base.css +111 -0
  5. package/components/accordion/accordion.js +197 -0
  6. package/components/alert/alert.js +112 -0
  7. package/components/app-bar/app-bar.js +168 -0
  8. package/components/app-shell/app-shell.js +198 -0
  9. package/components/aspect-ratio/aspect-ratio.js +59 -0
  10. package/components/attachment/attachment.js +333 -0
  11. package/components/avatar/avatar.js +96 -0
  12. package/components/badge/badge.js +63 -0
  13. package/components/blockquote/blockquote.js +128 -0
  14. package/components/bottom-nav/bottom-nav.js +309 -0
  15. package/components/breadcrumbs/breadcrumbs.js +175 -0
  16. package/components/button/button.js +283 -0
  17. package/components/button-group/button-group.js +72 -0
  18. package/components/card/card.js +73 -0
  19. package/components/carousel/carousel.js +435 -0
  20. package/components/center/center.js +26 -0
  21. package/components/checkbox/checkbox.js +152 -0
  22. package/components/checkbox-group/checkbox-group.js +152 -0
  23. package/components/code/code.js +574 -0
  24. package/components/color-picker/color-picker.js +775 -0
  25. package/components/combobox/combobox.js +563 -0
  26. package/components/command-palette/command-palette.js +510 -0
  27. package/components/container/container.js +41 -0
  28. package/components/date-picker/date-picker.js +594 -0
  29. package/components/dialog/dialog.js +329 -0
  30. package/components/drawer/drawer.js +139 -0
  31. package/components/empty-state/empty-state.js +66 -0
  32. package/components/file-upload/file-upload.js +589 -0
  33. package/components/grid/grid.js +96 -0
  34. package/components/group/group.js +40 -0
  35. package/components/icon/icon.js +58 -0
  36. package/components/icon-button/icon-button.js +30 -0
  37. package/components/indicator/indicator.js +112 -0
  38. package/components/input/input.js +85 -0
  39. package/components/kbd/kbd.js +60 -0
  40. package/components/menu/menu.js +296 -0
  41. package/components/nav-link/nav-link.js +159 -0
  42. package/components/notification/notification.js +591 -0
  43. package/components/number-input/number-input.js +241 -0
  44. package/components/pagination/pagination.js +194 -0
  45. package/components/password-input/password-input.js +85 -0
  46. package/components/path-input/path-input.js +137 -0
  47. package/components/popover/popover.js +85 -0
  48. package/components/progress/progress.js +109 -0
  49. package/components/radio/radio.js +253 -0
  50. package/components/scroll-area/scroll-area.js +158 -0
  51. package/components/search/search.js +157 -0
  52. package/components/segmented-control/segmented-control.js +190 -0
  53. package/components/select/select.js +506 -0
  54. package/components/separator/separator.js +58 -0
  55. package/components/sidebar/sidebar.js +252 -0
  56. package/components/skeleton/skeleton.js +90 -0
  57. package/components/slider/slider.js +155 -0
  58. package/components/spinner/spinner.js +39 -0
  59. package/components/split/split.js +221 -0
  60. package/components/stack/stack.js +46 -0
  61. package/components/stat/stat.js +79 -0
  62. package/components/statusbar/statusbar.js +110 -0
  63. package/components/stepper/stepper.js +239 -0
  64. package/components/switch/switch.js +45 -0
  65. package/components/table/table.js +328 -0
  66. package/components/tabs/tabs.js +224 -0
  67. package/components/textarea/textarea.js +105 -0
  68. package/components/theme-toggle/theme-toggle.js +78 -0
  69. package/components/timeline/timeline.js +150 -0
  70. package/components/toggle/toggle.js +130 -0
  71. package/components/toggle-group/toggle-group.js +216 -0
  72. package/components/tooltip/tooltip.js +101 -0
  73. package/core/charts.js +107 -0
  74. package/core/color.js +110 -0
  75. package/core/core.js +556 -0
  76. package/core/dismiss.js +69 -0
  77. package/core/floating.js +195 -0
  78. package/core/focus.js +70 -0
  79. package/core/form.js +235 -0
  80. package/core/host.js +200 -0
  81. package/core/hotkey.js +40 -0
  82. package/core/icons.js +104 -0
  83. package/core/motion.js +73 -0
  84. package/core/position.js +91 -0
  85. package/core/raylang.js +167 -0
  86. package/core/scroll.js +58 -0
  87. package/core/styles.js +211 -0
  88. package/core.js +16 -0
  89. package/custom-elements.json +18545 -0
  90. package/index.js +74 -0
  91. package/package.json +312 -4
  92. package/ray-elements.d.ts +2570 -0
  93. package/tokens.css +796 -0
  94. package/tokens.json +2083 -0
  95. package/vscode.html-custom-data.json +5499 -0
@@ -0,0 +1,221 @@
1
+ // <ray-split>: two panes (slot="start", slot="end") with a draggable divider
2
+ // (ray-sublime's sidebar and docks). The start pane has the size; the end
3
+ // pane takes the rest.
4
+ //
5
+ // The divider is a focusable separator (WAI-ARIA window splitter): arrows
6
+ // move it (Shift: faster), Home/End go to min/max, Enter collapses the start
7
+ // pane. With `autosave="key"` the size survives reloads (localStorage).
8
+
9
+ import { RayElement, css, define } from "../../core/core.js";
10
+
11
+ const styles = css`
12
+ :host {
13
+ display: grid;
14
+ grid-template-columns: var(--_size) 0.375rem minmax(0, 1fr);
15
+ min-width: 0;
16
+ min-height: 0;
17
+ --_size: 50%;
18
+ }
19
+ :host([orientation="vertical"]) { grid-template-columns: none; grid-template-rows: var(--_size) 0.375rem minmax(0, 1fr); }
20
+ .pane { min-width: 0; min-height: 0; overflow: auto; }
21
+ :host([collapsed]) .start { visibility: hidden; }
22
+ .divider {
23
+ position: relative;
24
+ z-index: 1;
25
+ margin: 0 -0.25rem;
26
+ cursor: col-resize;
27
+ touch-action: none;
28
+ outline: none;
29
+ }
30
+ :host([orientation="vertical"]) .divider { margin: -0.25rem 0; cursor: row-resize; }
31
+ :host([disabled]) .divider { cursor: default; }
32
+ .divider::before {
33
+ content: "";
34
+ position: absolute;
35
+ top: 0;
36
+ bottom: 0;
37
+ left: 50%;
38
+ width: 1px;
39
+ background: var(--ray-border);
40
+ transform: translateX(-50%);
41
+ transition: background-color var(--ray-duration-fast) var(--ray-easing-standard), width var(--ray-duration-fast) var(--ray-easing-standard);
42
+ }
43
+ :host([orientation="vertical"]) .divider::before { top: 50%; bottom: auto; left: 0; right: 0; width: auto; height: 1px; transform: translateY(-50%); }
44
+ :host(:not([disabled])) .divider:hover::before,
45
+ .divider:focus-visible::before,
46
+ :host([data-dragging]) .divider::before { width: 3px; background: var(--ray-primary); }
47
+ :host([orientation="vertical"]:not([disabled])) .divider:hover::before,
48
+ :host([orientation="vertical"]) .divider:focus-visible::before,
49
+ :host([orientation="vertical"][data-dragging]) .divider::before { width: auto; height: 3px; }
50
+ :host([data-dragging]) { user-select: none; cursor: col-resize; }
51
+ :host([data-dragging][orientation="vertical"]) { cursor: row-resize; }
52
+ `;
53
+
54
+ const STEP = 10;
55
+ const BIG_STEP = 50;
56
+
57
+ export class RaySplit extends RayElement {
58
+ static properties = {
59
+ orientation: { type: String, reflect: true, default: "horizontal" },
60
+ position: { type: String, default: "50%" },
61
+ min: { type: Number, default: 0 },
62
+ max: { type: Number },
63
+ collapsed: { type: Boolean, reflect: true },
64
+ disabled: { type: Boolean, reflect: true },
65
+ autosave: { type: String },
66
+ };
67
+ static styles = [styles];
68
+ static template = `
69
+ <div class="pane start" part="start" id="start"><slot name="start"></slot></div>
70
+ <div class="divider" part="divider" role="separator" tabindex="0" aria-controls="start"></div>
71
+ <div class="pane end" part="end"><slot name="end"></slot></div>`;
72
+
73
+ constructor() {
74
+ super();
75
+ this._px = null;
76
+ this.divider = this.$(".divider");
77
+ this.divider.addEventListener("pointerdown", (e) => this._down(e));
78
+ this.divider.addEventListener("keydown", (e) => this._key(e));
79
+ this.divider.addEventListener("dblclick", () => this.toggle());
80
+ }
81
+
82
+ connectedCallback() {
83
+ super.connectedCallback();
84
+ if (this._restored) return;
85
+ this._restored = true;
86
+ if (this.autosave) {
87
+ try {
88
+ const saved = Number(localStorage.getItem(`ray-split:${this.autosave}`));
89
+ if (saved > 0) this._px = saved;
90
+ } catch {
91
+ // No storage: the default position is fine.
92
+ }
93
+ }
94
+ }
95
+
96
+ /** The start pane's current size in px. */
97
+ get size() {
98
+ return this.$(".start").getBoundingClientRect()[this._vertical() ? "height" : "width"];
99
+ }
100
+
101
+ /** Collapses or restores the start pane. */
102
+ toggle() {
103
+ this.collapsed = !this.collapsed;
104
+ this.emit("ray-resize", { size: this.collapsed ? 0 : this.size, collapsed: this.collapsed });
105
+ }
106
+
107
+ update(changed) {
108
+ // A later change of `position` wins over a dragged/restored size; the
109
+ // first update (position from the markup) does not.
110
+ if (changed.has("position") && this._updated) this._px = null;
111
+ this._updated = true;
112
+ const min = Math.max(0, this.min ?? 0);
113
+ const max = this.max ?? null;
114
+ let value;
115
+ if (this._px !== null) {
116
+ value = `${this._clamp(this._px)}px`;
117
+ } else {
118
+ // A bare number is px; anything else (30%, 20rem) is CSS, clamped by min/max in CSS.
119
+ const pos = /^\d+(\.\d+)?$/.test(this.position ?? "") ? `${this.position}px` : (this.position || "50%");
120
+ value = `clamp(${min}px, ${pos}, ${max === null ? "100%" : `${max}px`})`;
121
+ }
122
+ // Inline on the host (it beats any :host rule): collapsed is decided here too.
123
+ this.style.setProperty("--_size", this.collapsed ? "0px" : value);
124
+ const vertical = this._vertical();
125
+ // The separator line is perpendicular to the split direction.
126
+ this.divider.setAttribute("aria-orientation", vertical ? "horizontal" : "vertical");
127
+ this.divider.setAttribute("aria-label", this.getAttribute("aria-label") ?? "Resize");
128
+ this.divider.tabIndex = this.disabled ? -1 : 0;
129
+ if (this.disabled) this.divider.setAttribute("aria-disabled", "true");
130
+ else this.divider.removeAttribute("aria-disabled");
131
+ queueMicrotask(() => this._aria());
132
+ }
133
+
134
+ _vertical() {
135
+ return this.orientation === "vertical";
136
+ }
137
+
138
+ _total() {
139
+ const r = this.getBoundingClientRect();
140
+ return this._vertical() ? r.height : r.width;
141
+ }
142
+
143
+ _clamp(px) {
144
+ const total = this._total();
145
+ const max = Math.min(this.max ?? Infinity, total > 0 ? total : Infinity);
146
+ return Math.round(Math.max(this.min ?? 0, Math.min(px, max)));
147
+ }
148
+
149
+ _aria() {
150
+ const total = this._total();
151
+ const now = total > 0 ? Math.round((this.collapsed ? 0 : this.size) / total * 100) : 0;
152
+ this.divider.setAttribute("aria-valuemin", "0");
153
+ this.divider.setAttribute("aria-valuemax", "100");
154
+ this.divider.setAttribute("aria-valuenow", String(now));
155
+ }
156
+
157
+ _resizeTo(px, commit) {
158
+ this._px = this._clamp(px);
159
+ if (this.collapsed) this.collapsed = false;
160
+ this.requestUpdate();
161
+ if (commit) {
162
+ if (this.autosave) {
163
+ try {
164
+ localStorage.setItem(`ray-split:${this.autosave}`, String(this._px));
165
+ } catch {
166
+ // Not persisted: fine.
167
+ }
168
+ }
169
+ this.emit("ray-resize", { size: this._px, collapsed: false });
170
+ }
171
+ }
172
+
173
+ _down(e) {
174
+ if (this.disabled || e.button !== 0) return;
175
+ e.preventDefault();
176
+ this.divider.focus();
177
+ try {
178
+ this.divider.setPointerCapture(e.pointerId);
179
+ } catch {
180
+ // The pointer is already gone (e.g. a canceled touch): drag without capture.
181
+ }
182
+ this.setAttribute("data-dragging", "");
183
+ const rect = this.getBoundingClientRect();
184
+ const vertical = this._vertical();
185
+ const move = (ev) => this._resizeTo(vertical ? ev.clientY - rect.top : ev.clientX - rect.left, false);
186
+ const up = (ev) => {
187
+ this.divider.removeEventListener("pointermove", move);
188
+ this.divider.removeEventListener("pointerup", up);
189
+ this.divider.removeEventListener("pointercancel", up);
190
+ this.removeAttribute("data-dragging");
191
+ this._resizeTo(vertical ? ev.clientY - rect.top : ev.clientX - rect.left, true);
192
+ };
193
+ this.divider.addEventListener("pointermove", move);
194
+ this.divider.addEventListener("pointerup", up);
195
+ this.divider.addEventListener("pointercancel", up);
196
+ }
197
+
198
+ _key(e) {
199
+ if (this.disabled) return;
200
+ const vertical = this._vertical();
201
+ const step = e.shiftKey ? BIG_STEP : STEP;
202
+ const current = this.collapsed ? 0 : this.size;
203
+ const less = vertical ? "ArrowUp" : "ArrowLeft";
204
+ const more = vertical ? "ArrowDown" : "ArrowRight";
205
+ let next = null;
206
+ if (e.key === less) next = current - step;
207
+ else if (e.key === more) next = current + step;
208
+ else if (e.key === "Home") next = this.min ?? 0;
209
+ else if (e.key === "End") next = this.max ?? this._total();
210
+ else if (e.key === "Enter") {
211
+ e.preventDefault();
212
+ this.toggle();
213
+ return;
214
+ }
215
+ if (next === null) return;
216
+ e.preventDefault();
217
+ this._resizeTo(next, true);
218
+ }
219
+ }
220
+
221
+ define("ray-split", RaySplit);
@@ -0,0 +1,46 @@
1
+ // <ray-stack>: its children in a column, spaced by the space scale
2
+ // (Mantine's Stack). <ray-group>: the same in a row.
3
+
4
+ import { RayElement, css, define } from "../../core/core.js";
5
+ import { spaceValue } from "../../core/styles.js";
6
+
7
+ const ALIGN = { start: "flex-start", center: "center", end: "flex-end", stretch: "stretch", baseline: "baseline" };
8
+ const JUSTIFY = { start: "flex-start", center: "center", end: "flex-end", "space-between": "space-between", "space-around": "space-around", "space-evenly": "space-evenly" };
9
+
10
+ const styles = css`
11
+ :host {
12
+ --_gap: var(--ray-space-md);
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--_gap);
16
+ align-items: var(--_align, stretch);
17
+ justify-content: var(--_justify, flex-start);
18
+ min-width: 0;
19
+ }
20
+ :host([hidden]) { display: none; }
21
+ `;
22
+
23
+ /** Gap, align and justify onto the host's custom properties. */
24
+ export function applyFlex(el, { gap, align, justify }, alignDefault) {
25
+ const g = spaceValue(gap);
26
+ if (g) el.style.setProperty("--_gap", g);
27
+ else el.style.removeProperty("--_gap");
28
+ el.style.setProperty("--_align", ALIGN[align] ?? alignDefault);
29
+ el.style.setProperty("--_justify", JUSTIFY[justify] ?? "flex-start");
30
+ }
31
+
32
+ export class RayStack extends RayElement {
33
+ static properties = {
34
+ gap: { type: String, reflect: true, default: "md" },
35
+ align: { type: String, reflect: true, default: "stretch" },
36
+ justify: { type: String, reflect: true, default: "start" },
37
+ };
38
+ static styles = [styles];
39
+ static template = `<slot></slot>`;
40
+
41
+ update() {
42
+ applyFlex(this, this, "stretch");
43
+ }
44
+ }
45
+
46
+ define("ray-stack", RayStack);
@@ -0,0 +1,79 @@
1
+ // <ray-stat>: a metric tile — label, big value, change against a previous
2
+ // period and a note (the tiles of rayauth's dashboard).
3
+
4
+ import { RayElement, css, define } from "../../core/core.js";
5
+
6
+ const styles = css`
7
+ :host { display: block; }
8
+ .root {
9
+ display: grid;
10
+ gap: var(--ray-space-1);
11
+ padding: var(--ray-space-md);
12
+ color: var(--ray-card-foreground);
13
+ background: var(--ray-card);
14
+ border: 1px solid var(--ray-border);
15
+ border-radius: var(--ray-radius-default);
16
+ }
17
+ :host([plain]) .root { padding: 0; border: 0; background: transparent; }
18
+ .head { display: flex; align-items: center; justify-content: space-between; gap: var(--ray-space-2); }
19
+ .label { font-size: var(--ray-font-size-xs); font-weight: var(--ray-font-weight-semibold); text-transform: uppercase; letter-spacing: 0.04em; color: var(--ray-muted-foreground); }
20
+ .icon { display: inline-flex; color: var(--ray-muted-foreground); }
21
+ ::slotted(svg) { width: 1.25rem; height: 1.25rem; }
22
+ .line { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--ray-space-2); }
23
+ .value { font-size: var(--ray-font-size-2xl); font-weight: var(--ray-font-weight-bold); line-height: var(--ray-line-height-tight); font-variant-numeric: tabular-nums; }
24
+ .diff { display: inline-flex; align-items: center; gap: 0.125rem; font-size: var(--ray-font-size-sm); font-weight: var(--ray-font-weight-semibold); }
25
+ .diff[data-trend="up"] { color: var(--ray-success); }
26
+ .diff[data-trend="down"] { color: var(--ray-destructive); }
27
+ .diff[data-trend="flat"] { color: var(--ray-muted-foreground); }
28
+ :host([inverse]) .diff[data-trend="up"] { color: var(--ray-destructive); }
29
+ :host([inverse]) .diff[data-trend="down"] { color: var(--ray-success); }
30
+ .diff { --ray-icon-stroke: 2.5; }
31
+ .diff svg { width: 0.875rem; height: 0.875rem; }
32
+ .note { font-size: var(--ray-font-size-xs); color: var(--ray-muted-foreground); }
33
+ [hidden] { display: none !important; }
34
+ `;
35
+
36
+ // Icon of each trend (from the icon registry).
37
+ const ARROWS = { up: "arrow-up-right", down: "arrow-down-right", flat: "minus" };
38
+
39
+ export class RayStat extends RayElement {
40
+ static properties = {
41
+ label: { type: String },
42
+ value: { type: String },
43
+ diff: { type: Number },
44
+ diffUnit: { type: String, default: "%" },
45
+ note: { type: String },
46
+ inverse: { type: Boolean, reflect: true },
47
+ plain: { type: Boolean, reflect: true },
48
+ };
49
+ static styles = [styles];
50
+ static template = `
51
+ <div class="root" part="root">
52
+ <div class="head"><span class="label" part="label"></span><span class="icon" part="icon"><slot name="icon"></slot></span></div>
53
+ <div class="line"><span class="value" part="value"></span><span class="diff" part="diff" hidden></span></div>
54
+ <div class="note" part="note" hidden></div>
55
+ <slot></slot>
56
+ </div>`;
57
+
58
+ update() {
59
+ this.$(".label").textContent = this.label ?? "";
60
+ this.$(".value").textContent = this.value ?? "";
61
+ const diff = this.$(".diff");
62
+ const has = typeof this.diff === "number" && !Number.isNaN(this.diff);
63
+ diff.hidden = !has;
64
+ if (has) {
65
+ const trend = this.diff > 0 ? "up" : this.diff < 0 ? "down" : "flat";
66
+ diff.dataset.trend = trend;
67
+ const sign = this.diff > 0 ? "+" : "";
68
+ diff.innerHTML = `<span data-ray-icon="${ARROWS[trend]}" aria-hidden="true"></span>`;
69
+ this.paintIcons(diff);
70
+ diff.append(`${sign}${this.diff}${this.diffUnit ?? ""}`);
71
+ diff.setAttribute("aria-label", `${trend === "up" ? "Up" : trend === "down" ? "Down" : "No change"} ${Math.abs(this.diff)}${this.diffUnit ?? ""}`);
72
+ }
73
+ const note = this.$(".note");
74
+ note.hidden = !this.note;
75
+ note.textContent = this.note ?? "";
76
+ }
77
+ }
78
+
79
+ define("ray-stat", RayStat);
@@ -0,0 +1,110 @@
1
+ // <ray-statusbar> + <ray-statusbar-item>: the bar at the bottom of an editor
2
+ // window (ray-sublime): items on the left, items on the right (slot="right").
3
+ // An item with `action` is a button; the others are plain text.
4
+
5
+ import { RayElement, css, define } from "../../core/core.js";
6
+ import { applyColor } from "../../core/styles.js";
7
+
8
+ const barStyles = css`
9
+ :host {
10
+ display: flex;
11
+ align-items: stretch;
12
+ justify-content: space-between;
13
+ gap: var(--ray-space-4);
14
+ min-height: 1.5rem;
15
+ padding: 0 var(--ray-space-2);
16
+ font-size: var(--ray-font-size-xs);
17
+ color: var(--ray-muted-foreground);
18
+ background: var(--ray-muted);
19
+ border-top: 1px solid var(--ray-border);
20
+ user-select: none;
21
+ }
22
+ :host([variant="accent"]) {
23
+ color: var(--ray-primary-foreground);
24
+ background: var(--ray-primary);
25
+ border-top-color: transparent;
26
+ /* Inherited by the items (no :host-context() in WebKit/Firefox). */
27
+ --_item-hover: rgb(255 255 255 / 0.15);
28
+ --_item-hover-color: var(--ray-primary-foreground);
29
+ }
30
+ .side { display: flex; align-items: stretch; gap: 0.125rem; min-width: 0; }
31
+ `;
32
+
33
+ export class RayStatusbar extends RayElement {
34
+ static properties = {
35
+ variant: { type: String, reflect: true, default: "default" },
36
+ };
37
+ static styles = [barStyles];
38
+ static template = `<div class="side" part="left"><slot></slot></div><div class="side" part="right"><slot name="right"></slot></div>`;
39
+
40
+ constructor() {
41
+ super();
42
+ this.attachInternals().role = "group";
43
+ }
44
+
45
+ update() {
46
+ if (!this.hasAttribute("aria-label")) this.setAttribute("aria-label", "Status bar");
47
+ }
48
+ }
49
+
50
+ const itemStyles = css`
51
+ :host { display: inline-flex; min-width: 0; }
52
+ .item {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: var(--ray-space-1);
56
+ min-width: 0;
57
+ padding: 0 var(--ray-space-2);
58
+ font: inherit;
59
+ color: inherit;
60
+ white-space: nowrap;
61
+ background: transparent;
62
+ border: 0;
63
+ border-radius: var(--ray-radius-sm);
64
+ }
65
+ .label { overflow: hidden; text-overflow: ellipsis; }
66
+ button.item { cursor: pointer; }
67
+ button.item:hover { color: var(--_item-hover-color, var(--ray-foreground)); background: var(--_item-hover, var(--ray-accent)); }
68
+ :host([color]) .item { color: var(--_light-color); }
69
+ .icon { display: inline-flex; }
70
+ .icon:not([data-filled]) { display: none; }
71
+ ::slotted(svg) { width: 0.875rem; height: 0.875rem; }
72
+ `;
73
+
74
+ export class RayStatusbarItem extends RayElement {
75
+ static delegatesFocus = true;
76
+ static properties = {
77
+ action: { type: Boolean, reflect: true },
78
+ color: { type: String, reflect: true },
79
+ label: { type: String },
80
+ };
81
+ static styles = [itemStyles];
82
+
83
+ update() {
84
+ const tag = this.action ? "button" : "span";
85
+ if (!this._el || this._el.localName !== tag) {
86
+ this._el?.remove();
87
+ const el = document.createElement(tag);
88
+ el.className = "item focus-ring";
89
+ el.setAttribute("part", "item");
90
+ if (tag === "button") el.type = "button";
91
+ el.innerHTML = `<span class="icon"><slot name="icon"></slot></span><span class="label"><slot></slot></span>`;
92
+ const slot = el.querySelector('slot[name="icon"]');
93
+ slot.addEventListener("slotchange", () => slot.parentElement.toggleAttribute("data-filled", slot.assignedNodes().length > 0));
94
+ this.shadowRoot.append(el);
95
+ this._el = el;
96
+ }
97
+ if (this.color) applyColor(this, this.color);
98
+ // The label names a button whose content is only an icon or a short count.
99
+ if (this.label) {
100
+ this._el.setAttribute("aria-label", this.label);
101
+ this._el.title = this.label;
102
+ } else {
103
+ this._el.removeAttribute("aria-label");
104
+ this._el.removeAttribute("title");
105
+ }
106
+ }
107
+ }
108
+
109
+ define("ray-statusbar", RayStatusbar);
110
+ define("ray-statusbar-item", RayStatusbarItem);