@xmesh/system-design 0.0.1
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/README.md +472 -0
- package/assets/brand-lockup-dark.svg +9 -0
- package/assets/brand-lockup-light.svg +9 -0
- package/assets/brand-mark.svg +9 -0
- package/colors_and_type.css +11 -0
- package/dist/lit/components/alert/index.css +201 -0
- package/dist/lit/components/alert/index.d.ts +25 -0
- package/dist/lit/components/alert/index.js +191 -0
- package/dist/lit/components/app-bar/index.css +80 -0
- package/dist/lit/components/app-bar/index.d.ts +19 -0
- package/dist/lit/components/app-bar/index.js +120 -0
- package/dist/lit/components/artifact/index.css +166 -0
- package/dist/lit/components/artifact/index.d.ts +37 -0
- package/dist/lit/components/artifact/index.js +294 -0
- package/dist/lit/components/autocomplete/index.css +171 -0
- package/dist/lit/components/autocomplete/index.d.ts +47 -0
- package/dist/lit/components/autocomplete/index.js +404 -0
- package/dist/lit/components/avatar/index.css +62 -0
- package/dist/lit/components/avatar/index.d.ts +19 -0
- package/dist/lit/components/avatar/index.js +112 -0
- package/dist/lit/components/avatar-group/index.css +60 -0
- package/dist/lit/components/avatar-group/index.d.ts +19 -0
- package/dist/lit/components/avatar-group/index.js +97 -0
- package/dist/lit/components/badge/index.css +72 -0
- package/dist/lit/components/badge/index.d.ts +18 -0
- package/dist/lit/components/badge/index.js +115 -0
- package/dist/lit/components/brand-mark/index.css +109 -0
- package/dist/lit/components/brand-mark/index.d.ts +24 -0
- package/dist/lit/components/brand-mark/index.js +116 -0
- package/dist/lit/components/breadcrumbs/index.css +91 -0
- package/dist/lit/components/breadcrumbs/index.d.ts +19 -0
- package/dist/lit/components/breadcrumbs/index.js +104 -0
- package/dist/lit/components/bubble/index.css +182 -0
- package/dist/lit/components/bubble/index.d.ts +72 -0
- package/dist/lit/components/bubble/index.js +617 -0
- package/dist/lit/components/button/index.css +342 -0
- package/dist/lit/components/button/index.d.ts +32 -0
- package/dist/lit/components/button/index.js +202 -0
- package/dist/lit/components/card/index.css +99 -0
- package/dist/lit/components/card/index.d.ts +20 -0
- package/dist/lit/components/card/index.js +133 -0
- package/dist/lit/components/chat/index.css +292 -0
- package/dist/lit/components/chat/index.d.ts +74 -0
- package/dist/lit/components/chat/index.js +589 -0
- package/dist/lit/components/checkbox/index.css +126 -0
- package/dist/lit/components/checkbox/index.d.ts +21 -0
- package/dist/lit/components/checkbox/index.js +138 -0
- package/dist/lit/components/chip/index.css +145 -0
- package/dist/lit/components/chip/index.d.ts +30 -0
- package/dist/lit/components/chip/index.js +230 -0
- package/dist/lit/components/chip-group/index.css +19 -0
- package/dist/lit/components/chip-group/index.d.ts +24 -0
- package/dist/lit/components/chip-group/index.js +171 -0
- package/dist/lit/components/code/index.css +42 -0
- package/dist/lit/components/code/index.d.ts +12 -0
- package/dist/lit/components/code/index.js +68 -0
- package/dist/lit/components/composer/index.css +548 -0
- package/dist/lit/components/composer/index.d.ts +67 -0
- package/dist/lit/components/composer/index.js +713 -0
- package/dist/lit/components/data-table/index.css +166 -0
- package/dist/lit/components/data-table/index.d.ts +55 -0
- package/dist/lit/components/data-table/index.js +390 -0
- package/dist/lit/components/dialog/index.css +124 -0
- package/dist/lit/components/dialog/index.d.ts +24 -0
- package/dist/lit/components/dialog/index.js +199 -0
- package/dist/lit/components/divider/index.css +27 -0
- package/dist/lit/components/divider/index.d.ts +13 -0
- package/dist/lit/components/divider/index.js +67 -0
- package/dist/lit/components/empty-state/index.css +69 -0
- package/dist/lit/components/empty-state/index.d.ts +21 -0
- package/dist/lit/components/empty-state/index.js +123 -0
- package/dist/lit/components/expansion-panel/index.css +120 -0
- package/dist/lit/components/expansion-panel/index.d.ts +22 -0
- package/dist/lit/components/expansion-panel/index.js +174 -0
- package/dist/lit/components/field/index.css +223 -0
- package/dist/lit/components/field/index.d.ts +106 -0
- package/dist/lit/components/field/index.js +388 -0
- package/dist/lit/components/file-input/index.css +257 -0
- package/dist/lit/components/file-input/index.d.ts +30 -0
- package/dist/lit/components/file-input/index.js +298 -0
- package/dist/lit/components/form/index.css +29 -0
- package/dist/lit/components/form/index.d.ts +38 -0
- package/dist/lit/components/form/index.js +192 -0
- package/dist/lit/components/grid/index.css +53 -0
- package/dist/lit/components/grid/index.d.ts +14 -0
- package/dist/lit/components/grid/index.js +82 -0
- package/dist/lit/components/kbd/index.css +35 -0
- package/dist/lit/components/kbd/index.d.ts +11 -0
- package/dist/lit/components/kbd/index.js +43 -0
- package/dist/lit/components/list/index.css +15 -0
- package/dist/lit/components/list/index.d.ts +28 -0
- package/dist/lit/components/list/index.js +188 -0
- package/dist/lit/components/list-item/index.css +119 -0
- package/dist/lit/components/list-item/index.d.ts +20 -0
- package/dist/lit/components/list-item/index.js +127 -0
- package/dist/lit/components/menu/index.css +94 -0
- package/dist/lit/components/menu/index.d.ts +47 -0
- package/dist/lit/components/menu/index.js +386 -0
- package/dist/lit/components/navigation-drawer/index.css +114 -0
- package/dist/lit/components/navigation-drawer/index.d.ts +29 -0
- package/dist/lit/components/navigation-drawer/index.js +218 -0
- package/dist/lit/components/overlay/index.css +171 -0
- package/dist/lit/components/overlay/index.d.ts +65 -0
- package/dist/lit/components/overlay/index.js +566 -0
- package/dist/lit/components/pagination/index.css +102 -0
- package/dist/lit/components/pagination/index.d.ts +22 -0
- package/dist/lit/components/pagination/index.js +184 -0
- package/dist/lit/components/primitives/index.css +504 -0
- package/dist/lit/components/primitives/index.d.ts +25 -0
- package/dist/lit/components/primitives/index.js +283 -0
- package/dist/lit/components/progress/index.css +143 -0
- package/dist/lit/components/progress/index.d.ts +23 -0
- package/dist/lit/components/progress/index.js +180 -0
- package/dist/lit/components/radio-group/index.css +178 -0
- package/dist/lit/components/radio-group/index.d.ts +35 -0
- package/dist/lit/components/radio-group/index.js +292 -0
- package/dist/lit/components/select/index.css +151 -0
- package/dist/lit/components/select/index.d.ts +50 -0
- package/dist/lit/components/select/index.js +390 -0
- package/dist/lit/components/sidebar-item/index.css +133 -0
- package/dist/lit/components/sidebar-item/index.d.ts +20 -0
- package/dist/lit/components/sidebar-item/index.js +105 -0
- package/dist/lit/components/skeleton/index.css +81 -0
- package/dist/lit/components/skeleton/index.d.ts +19 -0
- package/dist/lit/components/skeleton/index.js +119 -0
- package/dist/lit/components/slider/index.css +171 -0
- package/dist/lit/components/slider/index.d.ts +36 -0
- package/dist/lit/components/slider/index.js +302 -0
- package/dist/lit/components/snackbar/index.css +279 -0
- package/dist/lit/components/snackbar/index.d.ts +33 -0
- package/dist/lit/components/snackbar/index.js +195 -0
- package/dist/lit/components/stack/index.css +41 -0
- package/dist/lit/components/stack/index.d.ts +20 -0
- package/dist/lit/components/stack/index.js +103 -0
- package/dist/lit/components/switch/index.css +126 -0
- package/dist/lit/components/switch/index.d.ts +17 -0
- package/dist/lit/components/switch/index.js +116 -0
- package/dist/lit/components/table/index.css +85 -0
- package/dist/lit/components/table/index.d.ts +25 -0
- package/dist/lit/components/table/index.js +139 -0
- package/dist/lit/components/tabs/index.css +116 -0
- package/dist/lit/components/tabs/index.d.ts +49 -0
- package/dist/lit/components/tabs/index.js +320 -0
- package/dist/lit/components/text-field/index.css +90 -0
- package/dist/lit/components/text-field/index.d.ts +17 -0
- package/dist/lit/components/text-field/index.js +101 -0
- package/dist/lit/components/textarea/index.css +55 -0
- package/dist/lit/components/textarea/index.d.ts +26 -0
- package/dist/lit/components/textarea/index.js +124 -0
- package/dist/lit/components/tooltip/index.css +37 -0
- package/dist/lit/components/tooltip/index.d.ts +31 -0
- package/dist/lit/components/tooltip/index.js +196 -0
- package/dist/lit/components/validation/index.css +386 -0
- package/dist/lit/components/validation/index.d.ts +45 -0
- package/dist/lit/components/validation/index.js +318 -0
- package/dist/lit/index.d.ts +50 -0
- package/dist/lit/index.js +59 -0
- package/package.json +81 -0
- package/styles/README.md +346 -0
- package/styles/_elevation.css +24 -0
- package/styles/_fonts.css +6 -0
- package/styles/_layout.css +37 -0
- package/styles/_primitives.css +154 -0
- package/styles/_scroll.css +75 -0
- package/styles/_semantic.css +146 -0
- package/styles/_space.css +61 -0
- package/styles/_type.css +139 -0
- package/styles/_xmesh-extensions.css +232 -0
- package/styles/index.css +44 -0
- package/styles/md3/_color.css +102 -0
- package/styles/md3/_elevation.css +26 -0
- package/styles/md3/_motion.css +35 -0
- package/styles/md3/_shape.css +22 -0
- package/styles/md3/_state.css +22 -0
- package/styles/md3/_type.css +111 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
sidebar-item/index.ts — Lit port of components/sidebar-item/index.jsx.
|
|
3
|
+
|
|
4
|
+
<xm-sidebar-item> — chat-list row in the sidebar.
|
|
5
|
+
|
|
6
|
+
Properties:
|
|
7
|
+
title string — row label
|
|
8
|
+
layout "row" | "stacked" (default "row")
|
|
9
|
+
active boolean
|
|
10
|
+
hover boolean — preview-only force state
|
|
11
|
+
showDot boolean — render the leading accent dot in row layout
|
|
12
|
+
collapsed boolean — render the 4px stripe rail (icon-only mode)
|
|
13
|
+
|
|
14
|
+
Click events:
|
|
15
|
+
Listen for native `click` on the element. The component renders a
|
|
16
|
+
role="button" div that fires the standard click event; consumers can
|
|
17
|
+
do `el.addEventListener("click", …)` or `@click` from a parent.
|
|
18
|
+
|
|
19
|
+
Light DOM. Existing components/sidebar-item/index.css owns all visuals.
|
|
20
|
+
*/
|
|
21
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
22
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
23
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
24
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
25
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
26
|
+
};
|
|
27
|
+
import { LitElement, html } from "lit";
|
|
28
|
+
import { customElement, property } from "lit/decorators.js";
|
|
29
|
+
let XmSidebarItem = class XmSidebarItem extends LitElement {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(...arguments);
|
|
32
|
+
this.title = "";
|
|
33
|
+
this.layout = "row";
|
|
34
|
+
this.active = false;
|
|
35
|
+
this.hover = false;
|
|
36
|
+
this.showDot = false;
|
|
37
|
+
this.collapsed = false;
|
|
38
|
+
}
|
|
39
|
+
createRenderRoot() {
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
connectedCallback() {
|
|
43
|
+
super.connectedCallback();
|
|
44
|
+
// Make the host transparent to flex layout so the inner .sidebar-item
|
|
45
|
+
// is what the parent panel measures — matches the React tree where
|
|
46
|
+
// <SidebarItem> renders the <div class="sidebar-item"> directly.
|
|
47
|
+
if (!this.style.display)
|
|
48
|
+
this.style.display = "contents";
|
|
49
|
+
}
|
|
50
|
+
render() {
|
|
51
|
+
if (this.collapsed) {
|
|
52
|
+
const cls = `sidebar-item sidebar-item--collapsed ${this.active ? "is-active" : ""}`
|
|
53
|
+
.replace(/\s+/g, " ").trim();
|
|
54
|
+
return html `
|
|
55
|
+
<div
|
|
56
|
+
class="${cls}"
|
|
57
|
+
role="button"
|
|
58
|
+
tabindex="0"
|
|
59
|
+
title="${this.title}"
|
|
60
|
+
></div>
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
const stateClass = [
|
|
64
|
+
this.active && "is-active",
|
|
65
|
+
this.hover && "is-hover",
|
|
66
|
+
this.showDot && "has-dot",
|
|
67
|
+
].filter(Boolean).join(" ");
|
|
68
|
+
const cls = `sidebar-item sidebar-item--${this.layout} ${stateClass}`
|
|
69
|
+
.replace(/\s+/g, " ").trim();
|
|
70
|
+
if (this.layout === "stacked") {
|
|
71
|
+
return html `
|
|
72
|
+
<div class="${cls}" role="button" tabindex="0" title="${this.title}">
|
|
73
|
+
<span class="sidebar-item__title">${this.title}</span>
|
|
74
|
+
</div>
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
return html `
|
|
78
|
+
<div class="${cls}" role="button" tabindex="0" title="${this.title}">
|
|
79
|
+
<span class="sidebar-item__dot" aria-hidden="${this.showDot ? "false" : "true"}"></span>
|
|
80
|
+
<span class="sidebar-item__title">${this.title}</span>
|
|
81
|
+
</div>
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
__decorate([
|
|
86
|
+
property({ type: String })
|
|
87
|
+
], XmSidebarItem.prototype, "title", void 0);
|
|
88
|
+
__decorate([
|
|
89
|
+
property({ type: String })
|
|
90
|
+
], XmSidebarItem.prototype, "layout", void 0);
|
|
91
|
+
__decorate([
|
|
92
|
+
property({ type: Boolean })
|
|
93
|
+
], XmSidebarItem.prototype, "active", void 0);
|
|
94
|
+
__decorate([
|
|
95
|
+
property({ type: Boolean })
|
|
96
|
+
], XmSidebarItem.prototype, "hover", void 0);
|
|
97
|
+
__decorate([
|
|
98
|
+
property({ type: Boolean, attribute: "show-dot" })
|
|
99
|
+
], XmSidebarItem.prototype, "showDot", void 0);
|
|
100
|
+
__decorate([
|
|
101
|
+
property({ type: Boolean })
|
|
102
|
+
], XmSidebarItem.prototype, "collapsed", void 0);
|
|
103
|
+
XmSidebarItem = __decorate([
|
|
104
|
+
customElement("xm-sidebar-item")
|
|
105
|
+
], XmSidebarItem);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Skeleton — gradient-free loading placeholder.
|
|
3
|
+
|
|
4
|
+
The pulse is a calm OPACITY animation on a neutral surface fill — a
|
|
5
|
+
color-mix between two surface-container tiers. There is NO shimmer /
|
|
6
|
+
sweep gradient anywhere (FR-147 / A10): the snackbar scrim is the
|
|
7
|
+
system's only sanctioned gradient. The animation honors
|
|
8
|
+
prefers-reduced-motion.
|
|
9
|
+
|
|
10
|
+
The fill is neutral chrome — no accent, no severity hue (AD-11). It
|
|
11
|
+
sits on the host surface family it's placed in; the surface-container
|
|
12
|
+
tiers read on both the desk and the inverse card stack, in both themes
|
|
13
|
+
(AD-13).
|
|
14
|
+
|
|
15
|
+
BEM block: `skeleton`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
|
|
16
|
+
Elements: __shape (one placeholder), __line (a text line). Modifiers:
|
|
17
|
+
--line / --block / --circle.
|
|
18
|
+
============================================ */
|
|
19
|
+
|
|
20
|
+
.skeleton {
|
|
21
|
+
display: block;
|
|
22
|
+
width: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Line variant stacks its text lines with a consistent gap. */
|
|
26
|
+
.skeleton--line {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
gap: var(--s-2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.skeleton__shape {
|
|
33
|
+
display: block;
|
|
34
|
+
/* Neutral fill — a color-mix between two surface-container tiers. This is
|
|
35
|
+
a flat fill, NOT a gradient: there is no shimmer sweep, no linear- /
|
|
36
|
+
radial-gradient. The pulse comes from the opacity animation below. */
|
|
37
|
+
background: color-mix(
|
|
38
|
+
in oklch,
|
|
39
|
+
var(--md-sys-color-surface-container-high) 60%,
|
|
40
|
+
var(--md-sys-color-surface-container-highest)
|
|
41
|
+
);
|
|
42
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
43
|
+
/* Calm pulse: alternate opacity, slow tempo, standard easing. */
|
|
44
|
+
animation: skeleton-pulse var(--md-sys-motion-duration-extra-long2)
|
|
45
|
+
var(--md-sys-motion-easing-standard) infinite alternate;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* A single text line — height from the body typescale, full width by default
|
|
49
|
+
(the element narrows the last line of a paragraph via inline width). */
|
|
50
|
+
.skeleton__line {
|
|
51
|
+
height: 0.85em;
|
|
52
|
+
width: 100%;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Block — arbitrary rectangle (card / image placeholder). */
|
|
56
|
+
.skeleton--block .skeleton__shape {
|
|
57
|
+
height: 120px;
|
|
58
|
+
border-radius: var(--md-sys-shape-corner-medium);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Circle — avatar / icon placeholder. */
|
|
62
|
+
.skeleton--circle .skeleton__shape {
|
|
63
|
+
width: 40px;
|
|
64
|
+
height: 40px;
|
|
65
|
+
border-radius: var(--md-sys-shape-corner-full);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@keyframes skeleton-pulse {
|
|
69
|
+
from {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 0.45;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
.skeleton__shape {
|
|
79
|
+
animation: none;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import type { TemplateResult } from "lit";
|
|
3
|
+
export type SkeletonVariant = "line" | "block" | "circle";
|
|
4
|
+
export declare class XmSkeleton extends LitElement {
|
|
5
|
+
variant: SkeletonVariant;
|
|
6
|
+
width: string;
|
|
7
|
+
height: string;
|
|
8
|
+
lines: number;
|
|
9
|
+
radius: string;
|
|
10
|
+
render(): TemplateResult;
|
|
11
|
+
private _renderShape;
|
|
12
|
+
private _renderLines;
|
|
13
|
+
private _shapeStyle;
|
|
14
|
+
}
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
"xm-skeleton": XmSkeleton;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
skeleton/index.ts — <xm-skeleton>, gradient-free loading placeholder.
|
|
3
|
+
|
|
4
|
+
Shows the structure of content while data loads. Three shapes via `variant`:
|
|
5
|
+
line — a text line (repeatable via `lines` for a paragraph)
|
|
6
|
+
block — a rectangular block (card / image placeholder)
|
|
7
|
+
circle — a circular avatar / icon placeholder
|
|
8
|
+
|
|
9
|
+
The loading PULSE is gradient-free (AC-2, the headline constraint): it is a
|
|
10
|
+
calm opacity animation on a neutral surface fill (a color-mix between two
|
|
11
|
+
surface-container tiers). There is NO shimmer / sweep gradient anywhere — the
|
|
12
|
+
snackbar scrim is the system's only sanctioned gradient. The pulse respects
|
|
13
|
+
`prefers-reduced-motion` (CSS disables the animation).
|
|
14
|
+
|
|
15
|
+
The placeholder is decorative: it carries `aria-hidden="true"`. The HOST that
|
|
16
|
+
owns the loading state carries `aria-busy` — the skeleton does not announce.
|
|
17
|
+
|
|
18
|
+
Authoring:
|
|
19
|
+
<xm-skeleton variant="line" lines="3"></xm-skeleton>
|
|
20
|
+
<xm-skeleton variant="block" width="100%" height="160px"></xm-skeleton>
|
|
21
|
+
<xm-skeleton variant="circle" width="40px"></xm-skeleton>
|
|
22
|
+
|
|
23
|
+
Properties:
|
|
24
|
+
variant "line" | "block" | "circle" (default "line")
|
|
25
|
+
width CSS length for the shape (block/circle/line)
|
|
26
|
+
height CSS length (block); line/circle derive their own height
|
|
27
|
+
lines number of text lines (line variant only; default 1)
|
|
28
|
+
radius CSS length override for corner rounding (block variant)
|
|
29
|
+
|
|
30
|
+
Shadow DOM. Depends only on tokens — no overlay, no primitives (AD-12).
|
|
31
|
+
Reused by xm-data-table loading rows (Epic 6, FR-161): a row of line/block
|
|
32
|
+
placeholders composes from these variants.
|
|
33
|
+
*/
|
|
34
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
35
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
36
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
37
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
38
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
39
|
+
};
|
|
40
|
+
import { LitElement, html } from "lit";
|
|
41
|
+
import { customElement, property } from "lit/decorators.js";
|
|
42
|
+
const SKELETON_CSS = new URL("../skeleton/index.css", import.meta.url).href;
|
|
43
|
+
let XmSkeleton = class XmSkeleton extends LitElement {
|
|
44
|
+
constructor() {
|
|
45
|
+
super(...arguments);
|
|
46
|
+
this.variant = "line";
|
|
47
|
+
this.width = "";
|
|
48
|
+
this.height = "";
|
|
49
|
+
this.lines = 1;
|
|
50
|
+
this.radius = "";
|
|
51
|
+
}
|
|
52
|
+
render() {
|
|
53
|
+
const v = this.variant === "block" || this.variant === "circle"
|
|
54
|
+
? this.variant
|
|
55
|
+
: "line";
|
|
56
|
+
const cls = `skeleton skeleton--${v}`;
|
|
57
|
+
return html `
|
|
58
|
+
<link rel="stylesheet" href="${SKELETON_CSS}" />
|
|
59
|
+
<style>
|
|
60
|
+
:host { display: block; }
|
|
61
|
+
:host([hidden]) { display: none; }
|
|
62
|
+
</style>
|
|
63
|
+
<div class="${cls}" aria-hidden="true">
|
|
64
|
+
${v === "line" ? this._renderLines() : this._renderShape()}
|
|
65
|
+
</div>
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
_renderShape() {
|
|
69
|
+
const style = this._shapeStyle();
|
|
70
|
+
return html `<span class="skeleton__shape" style=${style}></span>`;
|
|
71
|
+
}
|
|
72
|
+
_renderLines() {
|
|
73
|
+
const count = Math.max(1, Math.floor(this.lines) || 1);
|
|
74
|
+
const lines = Array.from({ length: count }, (_v, i) => {
|
|
75
|
+
// The last line of a paragraph runs short for a natural ragged edge.
|
|
76
|
+
const isLast = i === count - 1 && count > 1;
|
|
77
|
+
const w = this.width || (isLast ? "62%" : "100%");
|
|
78
|
+
return html `<span
|
|
79
|
+
class="skeleton__shape skeleton__line"
|
|
80
|
+
style="width:${w};"
|
|
81
|
+
></span>`;
|
|
82
|
+
});
|
|
83
|
+
return html `${lines}`;
|
|
84
|
+
}
|
|
85
|
+
_shapeStyle() {
|
|
86
|
+
const parts = [];
|
|
87
|
+
if (this.width)
|
|
88
|
+
parts.push(`width:${this.width};`);
|
|
89
|
+
if (this.variant === "circle") {
|
|
90
|
+
// A circle is square: height tracks width unless given explicitly.
|
|
91
|
+
parts.push(`height:${this.height || this.width || "40px"};`);
|
|
92
|
+
}
|
|
93
|
+
else if (this.height) {
|
|
94
|
+
parts.push(`height:${this.height};`);
|
|
95
|
+
}
|
|
96
|
+
if (this.radius)
|
|
97
|
+
parts.push(`border-radius:${this.radius};`);
|
|
98
|
+
return parts.join("");
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
__decorate([
|
|
102
|
+
property({ type: String })
|
|
103
|
+
], XmSkeleton.prototype, "variant", void 0);
|
|
104
|
+
__decorate([
|
|
105
|
+
property({ type: String })
|
|
106
|
+
], XmSkeleton.prototype, "width", void 0);
|
|
107
|
+
__decorate([
|
|
108
|
+
property({ type: String })
|
|
109
|
+
], XmSkeleton.prototype, "height", void 0);
|
|
110
|
+
__decorate([
|
|
111
|
+
property({ type: Number })
|
|
112
|
+
], XmSkeleton.prototype, "lines", void 0);
|
|
113
|
+
__decorate([
|
|
114
|
+
property({ type: String })
|
|
115
|
+
], XmSkeleton.prototype, "radius", void 0);
|
|
116
|
+
XmSkeleton = __decorate([
|
|
117
|
+
customElement("xm-skeleton")
|
|
118
|
+
], XmSkeleton);
|
|
119
|
+
export { XmSkeleton };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
xm-slider — draggable range slider on XmField (Story 2.8).
|
|
3
|
+
|
|
4
|
+
Extends XmField for chrome but renders its own layout (the bordered text-field
|
|
5
|
+
box is wrong for a slider): a label row (label + optional value), a track row
|
|
6
|
+
(rail + active fill + thumb), and the helper/error row. State machinery
|
|
7
|
+
(disabled / form-association / focus / ARIA) is inherited; this file styles the
|
|
8
|
+
track, fill, thumb, label, and value.
|
|
9
|
+
|
|
10
|
+
Surface & ink (AD-13): the slider rides the inverse-surface card tier — label +
|
|
11
|
+
value are inverse-on-surface ink. The active (filled) portion + the thumb are
|
|
12
|
+
the single coral accent (--md-sys-color-primary). The inactive rail, track
|
|
13
|
+
height, and thumb size are --xm-slider-* extensions (AD-10). Coral = the filled
|
|
14
|
+
value, never severity (AD-11). The error string carries severity via the warn
|
|
15
|
+
icon + copy.
|
|
16
|
+
|
|
17
|
+
Motion (NFR-19): fill/thumb position is layout-driven; the focus ring + hover
|
|
18
|
+
transition short3 standard, no bounce.
|
|
19
|
+
|
|
20
|
+
BEM block: `slider`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
|
|
21
|
+
============================================ */
|
|
22
|
+
|
|
23
|
+
.slider {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
gap: var(--s-2);
|
|
27
|
+
width: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ---------- Label row — field label + optional value ---------- */
|
|
31
|
+
.slider__label-row {
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: baseline;
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
gap: var(--s-2);
|
|
36
|
+
}
|
|
37
|
+
.slider__label {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: var(--s-1);
|
|
41
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
42
|
+
font:
|
|
43
|
+
var(--md-sys-typescale-label-large-weight)
|
|
44
|
+
var(--md-sys-typescale-label-large-size) /
|
|
45
|
+
var(--md-sys-typescale-label-large-line-height)
|
|
46
|
+
var(--md-sys-typescale-label-large-font);
|
|
47
|
+
}
|
|
48
|
+
.slider__label-text {
|
|
49
|
+
letter-spacing: 0;
|
|
50
|
+
}
|
|
51
|
+
.slider__required {
|
|
52
|
+
color: var(--md-sys-color-primary);
|
|
53
|
+
font-weight: 700;
|
|
54
|
+
line-height: 1;
|
|
55
|
+
}
|
|
56
|
+
.slider__value {
|
|
57
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
58
|
+
font:
|
|
59
|
+
var(--md-sys-typescale-label-medium-weight)
|
|
60
|
+
var(--md-sys-typescale-label-medium-size) /
|
|
61
|
+
var(--md-sys-typescale-label-medium-line-height)
|
|
62
|
+
var(--md-sys-typescale-label-medium-font);
|
|
63
|
+
font-variant-numeric: tabular-nums;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* ---------- Track — rail + active fill + thumb ---------- */
|
|
67
|
+
.slider__track {
|
|
68
|
+
position: relative;
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
height: var(--xm-slider-thumb-size);
|
|
72
|
+
/* Inset the interactive box by half a thumb so the thumb (positioned by its
|
|
73
|
+
center at left:fraction) never overflows the field width. The rail/fill and
|
|
74
|
+
the pointer math all share this same inset coordinate space. */
|
|
75
|
+
margin: 0 calc(var(--xm-slider-thumb-size) / 2);
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
touch-action: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.slider__rail {
|
|
81
|
+
position: absolute;
|
|
82
|
+
left: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
height: var(--xm-slider-track-height);
|
|
85
|
+
border-radius: var(--md-sys-shape-corner-full);
|
|
86
|
+
background: var(--xm-slider-track-inactive);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.slider__fill {
|
|
90
|
+
position: absolute;
|
|
91
|
+
left: 0;
|
|
92
|
+
height: var(--xm-slider-track-height);
|
|
93
|
+
border-radius: var(--md-sys-shape-corner-full);
|
|
94
|
+
background: var(--md-sys-color-primary);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.slider__thumb {
|
|
98
|
+
position: absolute;
|
|
99
|
+
top: 50%;
|
|
100
|
+
width: var(--xm-slider-thumb-size);
|
|
101
|
+
height: var(--xm-slider-thumb-size);
|
|
102
|
+
margin-left: calc(var(--xm-slider-thumb-size) / -2);
|
|
103
|
+
transform: translateY(-50%);
|
|
104
|
+
border-radius: var(--md-sys-shape-corner-full);
|
|
105
|
+
background: var(--md-sys-color-primary);
|
|
106
|
+
box-shadow: var(--xm-slider-thumb-shadow);
|
|
107
|
+
outline: none;
|
|
108
|
+
cursor: grab;
|
|
109
|
+
transition: box-shadow var(--md-sys-motion-duration-short3)
|
|
110
|
+
var(--md-sys-motion-easing-standard);
|
|
111
|
+
}
|
|
112
|
+
.slider__thumb:active {
|
|
113
|
+
cursor: grabbing;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Focus ring — the canonical 3px coral halo on the thumb. */
|
|
117
|
+
.slider__thumb:focus-visible {
|
|
118
|
+
box-shadow:
|
|
119
|
+
var(--xm-slider-thumb-shadow),
|
|
120
|
+
var(--xm-state-focus-ring);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ---------- Disabled — shared reduced emphasis ---------- */
|
|
124
|
+
.slider--disabled .slider__track {
|
|
125
|
+
cursor: not-allowed;
|
|
126
|
+
}
|
|
127
|
+
.slider--disabled .slider__thumb {
|
|
128
|
+
cursor: not-allowed;
|
|
129
|
+
box-shadow: none;
|
|
130
|
+
}
|
|
131
|
+
.slider--disabled .slider__rail,
|
|
132
|
+
.slider--disabled .slider__fill,
|
|
133
|
+
.slider--disabled .slider__thumb {
|
|
134
|
+
opacity: 0.45;
|
|
135
|
+
}
|
|
136
|
+
.slider--disabled .slider__label,
|
|
137
|
+
.slider--disabled .slider__value {
|
|
138
|
+
color: var(--xm-color-inverse-on-surface-disabled);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* ---------- Helper / error message row ----------
|
|
142
|
+
Severity is the warn icon + copy, not a color (rule 3a). */
|
|
143
|
+
.slider__message {
|
|
144
|
+
display: flex;
|
|
145
|
+
align-items: flex-start;
|
|
146
|
+
gap: var(--s-1);
|
|
147
|
+
min-height: 1em;
|
|
148
|
+
font:
|
|
149
|
+
var(--md-sys-typescale-body-small-weight)
|
|
150
|
+
var(--md-sys-typescale-body-small-size) /
|
|
151
|
+
var(--md-sys-typescale-body-small-line-height)
|
|
152
|
+
var(--md-sys-typescale-body-small-font);
|
|
153
|
+
}
|
|
154
|
+
.slider__message--helper {
|
|
155
|
+
color: var(--xm-color-inverse-on-surface-muted);
|
|
156
|
+
}
|
|
157
|
+
.slider__message--error {
|
|
158
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
159
|
+
}
|
|
160
|
+
.slider__error-icon {
|
|
161
|
+
display: inline-flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
flex-shrink: 0;
|
|
164
|
+
margin-top: 1px;
|
|
165
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
166
|
+
}
|
|
167
|
+
.slider__message-text {
|
|
168
|
+
flex: 1;
|
|
169
|
+
min-width: 0;
|
|
170
|
+
text-wrap: pretty;
|
|
171
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PropertyValues, TemplateResult } from "lit";
|
|
2
|
+
import { XmField } from "../field/index.js";
|
|
3
|
+
export declare class XmSlider extends XmField {
|
|
4
|
+
min: number;
|
|
5
|
+
max: number;
|
|
6
|
+
step: number;
|
|
7
|
+
/** Show the current numeric value as a label beside the track. */
|
|
8
|
+
showValue: boolean;
|
|
9
|
+
/** Optional unit appended to the displayed value (e.g. "%", "px"). */
|
|
10
|
+
unit: string;
|
|
11
|
+
private _track;
|
|
12
|
+
private _dragging;
|
|
13
|
+
connectedCallback(): void;
|
|
14
|
+
/** Typed live read; the inherited string `value` stays in sync. */
|
|
15
|
+
get valueAsNumber(): number;
|
|
16
|
+
private get _num();
|
|
17
|
+
private get _fraction();
|
|
18
|
+
private _clampSnap;
|
|
19
|
+
private _setValue;
|
|
20
|
+
private _commit;
|
|
21
|
+
private _valueFromClientX;
|
|
22
|
+
private _onPointerDown;
|
|
23
|
+
private _onPointerMove;
|
|
24
|
+
private _onPointerUp;
|
|
25
|
+
private _focusThumb;
|
|
26
|
+
private _onKeydown;
|
|
27
|
+
private _onBlur;
|
|
28
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
29
|
+
private get _valueText();
|
|
30
|
+
render(): TemplateResult;
|
|
31
|
+
}
|
|
32
|
+
declare global {
|
|
33
|
+
interface HTMLElementTagNameMap {
|
|
34
|
+
"xm-slider": XmSlider;
|
|
35
|
+
}
|
|
36
|
+
}
|