@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.
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +12 -2
- package/base.css +111 -0
- package/components/accordion/accordion.js +197 -0
- package/components/alert/alert.js +112 -0
- package/components/app-bar/app-bar.js +168 -0
- package/components/app-shell/app-shell.js +198 -0
- package/components/aspect-ratio/aspect-ratio.js +59 -0
- package/components/attachment/attachment.js +333 -0
- package/components/avatar/avatar.js +96 -0
- package/components/badge/badge.js +63 -0
- package/components/blockquote/blockquote.js +128 -0
- package/components/bottom-nav/bottom-nav.js +309 -0
- package/components/breadcrumbs/breadcrumbs.js +175 -0
- package/components/button/button.js +283 -0
- package/components/button-group/button-group.js +72 -0
- package/components/card/card.js +73 -0
- package/components/carousel/carousel.js +435 -0
- package/components/center/center.js +26 -0
- package/components/checkbox/checkbox.js +152 -0
- package/components/checkbox-group/checkbox-group.js +152 -0
- package/components/code/code.js +574 -0
- package/components/color-picker/color-picker.js +775 -0
- package/components/combobox/combobox.js +563 -0
- package/components/command-palette/command-palette.js +510 -0
- package/components/container/container.js +41 -0
- package/components/date-picker/date-picker.js +594 -0
- package/components/dialog/dialog.js +329 -0
- package/components/drawer/drawer.js +139 -0
- package/components/empty-state/empty-state.js +66 -0
- package/components/file-upload/file-upload.js +589 -0
- package/components/grid/grid.js +96 -0
- package/components/group/group.js +40 -0
- package/components/icon/icon.js +58 -0
- package/components/icon-button/icon-button.js +30 -0
- package/components/indicator/indicator.js +112 -0
- package/components/input/input.js +85 -0
- package/components/kbd/kbd.js +60 -0
- package/components/menu/menu.js +296 -0
- package/components/nav-link/nav-link.js +159 -0
- package/components/notification/notification.js +591 -0
- package/components/number-input/number-input.js +241 -0
- package/components/pagination/pagination.js +194 -0
- package/components/password-input/password-input.js +85 -0
- package/components/path-input/path-input.js +137 -0
- package/components/popover/popover.js +85 -0
- package/components/progress/progress.js +109 -0
- package/components/radio/radio.js +253 -0
- package/components/scroll-area/scroll-area.js +158 -0
- package/components/search/search.js +157 -0
- package/components/segmented-control/segmented-control.js +190 -0
- package/components/select/select.js +506 -0
- package/components/separator/separator.js +58 -0
- package/components/sidebar/sidebar.js +252 -0
- package/components/skeleton/skeleton.js +90 -0
- package/components/slider/slider.js +155 -0
- package/components/spinner/spinner.js +39 -0
- package/components/split/split.js +221 -0
- package/components/stack/stack.js +46 -0
- package/components/stat/stat.js +79 -0
- package/components/statusbar/statusbar.js +110 -0
- package/components/stepper/stepper.js +239 -0
- package/components/switch/switch.js +45 -0
- package/components/table/table.js +328 -0
- package/components/tabs/tabs.js +224 -0
- package/components/textarea/textarea.js +105 -0
- package/components/theme-toggle/theme-toggle.js +78 -0
- package/components/timeline/timeline.js +150 -0
- package/components/toggle/toggle.js +130 -0
- package/components/toggle-group/toggle-group.js +216 -0
- package/components/tooltip/tooltip.js +101 -0
- package/core/charts.js +107 -0
- package/core/color.js +110 -0
- package/core/core.js +556 -0
- package/core/dismiss.js +69 -0
- package/core/floating.js +195 -0
- package/core/focus.js +70 -0
- package/core/form.js +235 -0
- package/core/host.js +200 -0
- package/core/hotkey.js +40 -0
- package/core/icons.js +104 -0
- package/core/motion.js +73 -0
- package/core/position.js +91 -0
- package/core/raylang.js +167 -0
- package/core/scroll.js +58 -0
- package/core/styles.js +211 -0
- package/core.js +16 -0
- package/custom-elements.json +18545 -0
- package/index.js +74 -0
- package/package.json +312 -4
- package/ray-elements.d.ts +2570 -0
- package/tokens.css +796 -0
- package/tokens.json +2083 -0
- package/vscode.html-custom-data.json +5499 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// <ray-nav-link>: an item of a sidebar or navigation (Mantine's NavLink): icon,
|
|
2
|
+
// label, description, a badge on the right, active state, and nested
|
|
3
|
+
// ray-nav-links that collapse under it.
|
|
4
|
+
|
|
5
|
+
import { RayElement, css, define } from "../../core/core.js";
|
|
6
|
+
import { applyColor } from "../../core/styles.js";
|
|
7
|
+
|
|
8
|
+
const styles = css`
|
|
9
|
+
:host { display: block; }
|
|
10
|
+
.link {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--ray-space-3);
|
|
14
|
+
width: 100%;
|
|
15
|
+
min-height: 2.25rem;
|
|
16
|
+
padding: var(--ray-space-1) var(--ray-space-3);
|
|
17
|
+
font: inherit;
|
|
18
|
+
font-size: var(--ray-font-size-sm);
|
|
19
|
+
color: var(--ray-foreground);
|
|
20
|
+
text-align: left;
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
background: transparent;
|
|
23
|
+
border: 0;
|
|
24
|
+
border-radius: var(--ray-radius-sm);
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
.link:hover { background: var(--ray-accent); }
|
|
28
|
+
.link[data-active] { color: var(--_light-color); background: var(--_light); font-weight: var(--ray-font-weight-medium); }
|
|
29
|
+
.link[data-active]:hover { background: var(--_light-hover); }
|
|
30
|
+
:host([variant="filled"]) .link[data-active] { color: var(--_contrast); background: var(--_filled); }
|
|
31
|
+
:host([variant="filled"]) .link[data-active]:hover { background: var(--_filled-hover); }
|
|
32
|
+
:host([variant="subtle"]) .link[data-active] { background: transparent; }
|
|
33
|
+
:host([disabled]) .link { color: var(--ray-muted-foreground); cursor: not-allowed; background: transparent; opacity: 0.6; }
|
|
34
|
+
.body { display: flex; flex: 1; flex-direction: column; min-width: 0; }
|
|
35
|
+
.label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
36
|
+
.description { font-size: var(--ray-font-size-xs); color: var(--ray-muted-foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
37
|
+
.link[data-active] .description { color: inherit; opacity: 0.8; }
|
|
38
|
+
.description:empty { display: none; }
|
|
39
|
+
.section { display: inline-flex; flex: none; align-items: center; }
|
|
40
|
+
.section:not([data-filled]) { display: none; }
|
|
41
|
+
::slotted(svg) { width: 1rem; height: 1rem; }
|
|
42
|
+
.chevron { display: none; flex: none; color: var(--ray-muted-foreground); transition: transform var(--ray-duration-base) var(--ray-easing-standard); }
|
|
43
|
+
.chevron svg { width: 0.875rem; height: 0.875rem; }
|
|
44
|
+
:host([data-parent]) .chevron { display: inline-flex; }
|
|
45
|
+
:host([opened]) .chevron { transform: rotate(90deg); }
|
|
46
|
+
/* Children expand and collapse: a 0fr → 1fr grid row animates the height
|
|
47
|
+
in both directions without measuring it (like the accordion). */
|
|
48
|
+
.children {
|
|
49
|
+
display: grid;
|
|
50
|
+
grid-template-rows: 0fr;
|
|
51
|
+
opacity: 0;
|
|
52
|
+
transition: grid-template-rows var(--ray-duration-base) var(--ray-easing-standard), opacity var(--ray-duration-base) var(--ray-easing-standard);
|
|
53
|
+
}
|
|
54
|
+
:host([data-parent][opened]) .children { grid-template-rows: 1fr; opacity: 1; }
|
|
55
|
+
/* icon-only (a collapsed ray-sidebar): just the left section, centered;
|
|
56
|
+
the label stays as the accessible name and the tooltip. */
|
|
57
|
+
:host([icon-only]) .link { justify-content: center; gap: 0; padding-inline: 0; }
|
|
58
|
+
:host([icon-only]) .body, :host([icon-only]) .chevron, :host([icon-only]) .section.right { display: none; }
|
|
59
|
+
:host([icon-only]) .children { display: none; }
|
|
60
|
+
/* 2px of room so the focus ring of a child is not clipped. */
|
|
61
|
+
.children-inner { min-height: 0; overflow: hidden; margin: -2px 0; padding: 2px 0 2px var(--ray-space-5); }
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const INNER = `
|
|
65
|
+
<span class="section" part="left-section"><slot name="left"></slot></span>
|
|
66
|
+
<span class="body"><span class="label" part="label"></span><span class="description" part="description"></span></span>
|
|
67
|
+
<span class="section right" part="right-section"><slot name="right"></slot></span>
|
|
68
|
+
<span class="chevron" aria-hidden="true" data-ray-icon="chevron-right"></span>`;
|
|
69
|
+
|
|
70
|
+
export class RayNavLink extends RayElement {
|
|
71
|
+
static delegatesFocus = true;
|
|
72
|
+
static properties = {
|
|
73
|
+
label: { type: String },
|
|
74
|
+
description: { type: String },
|
|
75
|
+
href: { type: String, reflect: true },
|
|
76
|
+
active: { type: Boolean, reflect: true },
|
|
77
|
+
disabled: { type: Boolean, reflect: true },
|
|
78
|
+
opened: { type: Boolean, reflect: true },
|
|
79
|
+
variant: { type: String, reflect: true, default: "light" },
|
|
80
|
+
color: { type: String, reflect: true },
|
|
81
|
+
iconOnly: { type: Boolean, reflect: true },
|
|
82
|
+
};
|
|
83
|
+
static styles = [styles];
|
|
84
|
+
static template = `<div class="children" part="children" role="group"><div class="children-inner"><slot></slot></div></div>`;
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
super();
|
|
88
|
+
this._children = this.$(".children slot");
|
|
89
|
+
this._children.addEventListener("slotchange", () => this.requestUpdate());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Nested ray-nav-links. */
|
|
93
|
+
get links() {
|
|
94
|
+
return this._children.assignedElements().filter((el) => el.localName === "ray-nav-link");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
update() {
|
|
98
|
+
const parent = this.links.length > 0;
|
|
99
|
+
this.toggleAttribute("data-parent", parent);
|
|
100
|
+
// A parent toggles its children; a leaf with href is a link.
|
|
101
|
+
// A disabled link is a disabled button: an <a> without href is not a link.
|
|
102
|
+
const tag = this.href && !parent && !this.disabled ? "a" : "button";
|
|
103
|
+
if (!this._el || this._el.localName !== tag) this._render(tag);
|
|
104
|
+
const el = this._el;
|
|
105
|
+
el.querySelector(".label").textContent = this.label ?? "";
|
|
106
|
+
el.querySelector(".description").textContent = this.description ?? "";
|
|
107
|
+
el.toggleAttribute("data-active", Boolean(this.active));
|
|
108
|
+
applyColor(el, this.color);
|
|
109
|
+
if (tag === "a") {
|
|
110
|
+
if (this.disabled) el.removeAttribute("href");
|
|
111
|
+
else el.href = this.href;
|
|
112
|
+
} else {
|
|
113
|
+
el.disabled = Boolean(this.disabled);
|
|
114
|
+
}
|
|
115
|
+
if (this.active && !parent) el.setAttribute("aria-current", "page");
|
|
116
|
+
else el.removeAttribute("aria-current");
|
|
117
|
+
if (parent) el.setAttribute("aria-expanded", String(Boolean(this.opened)));
|
|
118
|
+
else el.removeAttribute("aria-expanded");
|
|
119
|
+
// Collapsed children: not focusable nor read (they are only 0px tall).
|
|
120
|
+
this.$(".children").inert = parent && !this.opened;
|
|
121
|
+
if (this.disabled) el.setAttribute("aria-disabled", "true");
|
|
122
|
+
else el.removeAttribute("aria-disabled");
|
|
123
|
+
// Icon only: the label (hidden) still names the link, and shows on hover.
|
|
124
|
+
if (this.iconOnly && this.label) {
|
|
125
|
+
el.setAttribute("aria-label", this.label);
|
|
126
|
+
el.title = this.label;
|
|
127
|
+
} else {
|
|
128
|
+
el.removeAttribute("aria-label");
|
|
129
|
+
el.removeAttribute("title");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_render(tag) {
|
|
134
|
+
this._el?.remove();
|
|
135
|
+
const el = document.createElement(tag);
|
|
136
|
+
el.className = "link focus-ring";
|
|
137
|
+
el.setAttribute("part", "link");
|
|
138
|
+
if (tag === "button") el.type = "button";
|
|
139
|
+
el.innerHTML = INNER;
|
|
140
|
+
for (const slot of el.querySelectorAll("slot[name]")) {
|
|
141
|
+
const sync = () => slot.parentElement.toggleAttribute("data-filled", slot.assignedNodes().length > 0);
|
|
142
|
+
slot.addEventListener("slotchange", sync);
|
|
143
|
+
}
|
|
144
|
+
el.addEventListener("click", (e) => {
|
|
145
|
+
if (this.disabled) {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (this.links.length > 0) {
|
|
150
|
+
this.opened = !this.opened;
|
|
151
|
+
this.emit("ray-toggle", { opened: this.opened });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
this.shadowRoot.prepend(el);
|
|
155
|
+
this._el = el;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
define("ray-nav-link", RayNavLink);
|