@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.
Files changed (175) hide show
  1. package/README.md +472 -0
  2. package/assets/brand-lockup-dark.svg +9 -0
  3. package/assets/brand-lockup-light.svg +9 -0
  4. package/assets/brand-mark.svg +9 -0
  5. package/colors_and_type.css +11 -0
  6. package/dist/lit/components/alert/index.css +201 -0
  7. package/dist/lit/components/alert/index.d.ts +25 -0
  8. package/dist/lit/components/alert/index.js +191 -0
  9. package/dist/lit/components/app-bar/index.css +80 -0
  10. package/dist/lit/components/app-bar/index.d.ts +19 -0
  11. package/dist/lit/components/app-bar/index.js +120 -0
  12. package/dist/lit/components/artifact/index.css +166 -0
  13. package/dist/lit/components/artifact/index.d.ts +37 -0
  14. package/dist/lit/components/artifact/index.js +294 -0
  15. package/dist/lit/components/autocomplete/index.css +171 -0
  16. package/dist/lit/components/autocomplete/index.d.ts +47 -0
  17. package/dist/lit/components/autocomplete/index.js +404 -0
  18. package/dist/lit/components/avatar/index.css +62 -0
  19. package/dist/lit/components/avatar/index.d.ts +19 -0
  20. package/dist/lit/components/avatar/index.js +112 -0
  21. package/dist/lit/components/avatar-group/index.css +60 -0
  22. package/dist/lit/components/avatar-group/index.d.ts +19 -0
  23. package/dist/lit/components/avatar-group/index.js +97 -0
  24. package/dist/lit/components/badge/index.css +72 -0
  25. package/dist/lit/components/badge/index.d.ts +18 -0
  26. package/dist/lit/components/badge/index.js +115 -0
  27. package/dist/lit/components/brand-mark/index.css +109 -0
  28. package/dist/lit/components/brand-mark/index.d.ts +24 -0
  29. package/dist/lit/components/brand-mark/index.js +116 -0
  30. package/dist/lit/components/breadcrumbs/index.css +91 -0
  31. package/dist/lit/components/breadcrumbs/index.d.ts +19 -0
  32. package/dist/lit/components/breadcrumbs/index.js +104 -0
  33. package/dist/lit/components/bubble/index.css +182 -0
  34. package/dist/lit/components/bubble/index.d.ts +72 -0
  35. package/dist/lit/components/bubble/index.js +617 -0
  36. package/dist/lit/components/button/index.css +342 -0
  37. package/dist/lit/components/button/index.d.ts +32 -0
  38. package/dist/lit/components/button/index.js +202 -0
  39. package/dist/lit/components/card/index.css +99 -0
  40. package/dist/lit/components/card/index.d.ts +20 -0
  41. package/dist/lit/components/card/index.js +133 -0
  42. package/dist/lit/components/chat/index.css +292 -0
  43. package/dist/lit/components/chat/index.d.ts +74 -0
  44. package/dist/lit/components/chat/index.js +589 -0
  45. package/dist/lit/components/checkbox/index.css +126 -0
  46. package/dist/lit/components/checkbox/index.d.ts +21 -0
  47. package/dist/lit/components/checkbox/index.js +138 -0
  48. package/dist/lit/components/chip/index.css +145 -0
  49. package/dist/lit/components/chip/index.d.ts +30 -0
  50. package/dist/lit/components/chip/index.js +230 -0
  51. package/dist/lit/components/chip-group/index.css +19 -0
  52. package/dist/lit/components/chip-group/index.d.ts +24 -0
  53. package/dist/lit/components/chip-group/index.js +171 -0
  54. package/dist/lit/components/code/index.css +42 -0
  55. package/dist/lit/components/code/index.d.ts +12 -0
  56. package/dist/lit/components/code/index.js +68 -0
  57. package/dist/lit/components/composer/index.css +548 -0
  58. package/dist/lit/components/composer/index.d.ts +67 -0
  59. package/dist/lit/components/composer/index.js +713 -0
  60. package/dist/lit/components/data-table/index.css +166 -0
  61. package/dist/lit/components/data-table/index.d.ts +55 -0
  62. package/dist/lit/components/data-table/index.js +390 -0
  63. package/dist/lit/components/dialog/index.css +124 -0
  64. package/dist/lit/components/dialog/index.d.ts +24 -0
  65. package/dist/lit/components/dialog/index.js +199 -0
  66. package/dist/lit/components/divider/index.css +27 -0
  67. package/dist/lit/components/divider/index.d.ts +13 -0
  68. package/dist/lit/components/divider/index.js +67 -0
  69. package/dist/lit/components/empty-state/index.css +69 -0
  70. package/dist/lit/components/empty-state/index.d.ts +21 -0
  71. package/dist/lit/components/empty-state/index.js +123 -0
  72. package/dist/lit/components/expansion-panel/index.css +120 -0
  73. package/dist/lit/components/expansion-panel/index.d.ts +22 -0
  74. package/dist/lit/components/expansion-panel/index.js +174 -0
  75. package/dist/lit/components/field/index.css +223 -0
  76. package/dist/lit/components/field/index.d.ts +106 -0
  77. package/dist/lit/components/field/index.js +388 -0
  78. package/dist/lit/components/file-input/index.css +257 -0
  79. package/dist/lit/components/file-input/index.d.ts +30 -0
  80. package/dist/lit/components/file-input/index.js +298 -0
  81. package/dist/lit/components/form/index.css +29 -0
  82. package/dist/lit/components/form/index.d.ts +38 -0
  83. package/dist/lit/components/form/index.js +192 -0
  84. package/dist/lit/components/grid/index.css +53 -0
  85. package/dist/lit/components/grid/index.d.ts +14 -0
  86. package/dist/lit/components/grid/index.js +82 -0
  87. package/dist/lit/components/kbd/index.css +35 -0
  88. package/dist/lit/components/kbd/index.d.ts +11 -0
  89. package/dist/lit/components/kbd/index.js +43 -0
  90. package/dist/lit/components/list/index.css +15 -0
  91. package/dist/lit/components/list/index.d.ts +28 -0
  92. package/dist/lit/components/list/index.js +188 -0
  93. package/dist/lit/components/list-item/index.css +119 -0
  94. package/dist/lit/components/list-item/index.d.ts +20 -0
  95. package/dist/lit/components/list-item/index.js +127 -0
  96. package/dist/lit/components/menu/index.css +94 -0
  97. package/dist/lit/components/menu/index.d.ts +47 -0
  98. package/dist/lit/components/menu/index.js +386 -0
  99. package/dist/lit/components/navigation-drawer/index.css +114 -0
  100. package/dist/lit/components/navigation-drawer/index.d.ts +29 -0
  101. package/dist/lit/components/navigation-drawer/index.js +218 -0
  102. package/dist/lit/components/overlay/index.css +171 -0
  103. package/dist/lit/components/overlay/index.d.ts +65 -0
  104. package/dist/lit/components/overlay/index.js +566 -0
  105. package/dist/lit/components/pagination/index.css +102 -0
  106. package/dist/lit/components/pagination/index.d.ts +22 -0
  107. package/dist/lit/components/pagination/index.js +184 -0
  108. package/dist/lit/components/primitives/index.css +504 -0
  109. package/dist/lit/components/primitives/index.d.ts +25 -0
  110. package/dist/lit/components/primitives/index.js +283 -0
  111. package/dist/lit/components/progress/index.css +143 -0
  112. package/dist/lit/components/progress/index.d.ts +23 -0
  113. package/dist/lit/components/progress/index.js +180 -0
  114. package/dist/lit/components/radio-group/index.css +178 -0
  115. package/dist/lit/components/radio-group/index.d.ts +35 -0
  116. package/dist/lit/components/radio-group/index.js +292 -0
  117. package/dist/lit/components/select/index.css +151 -0
  118. package/dist/lit/components/select/index.d.ts +50 -0
  119. package/dist/lit/components/select/index.js +390 -0
  120. package/dist/lit/components/sidebar-item/index.css +133 -0
  121. package/dist/lit/components/sidebar-item/index.d.ts +20 -0
  122. package/dist/lit/components/sidebar-item/index.js +105 -0
  123. package/dist/lit/components/skeleton/index.css +81 -0
  124. package/dist/lit/components/skeleton/index.d.ts +19 -0
  125. package/dist/lit/components/skeleton/index.js +119 -0
  126. package/dist/lit/components/slider/index.css +171 -0
  127. package/dist/lit/components/slider/index.d.ts +36 -0
  128. package/dist/lit/components/slider/index.js +302 -0
  129. package/dist/lit/components/snackbar/index.css +279 -0
  130. package/dist/lit/components/snackbar/index.d.ts +33 -0
  131. package/dist/lit/components/snackbar/index.js +195 -0
  132. package/dist/lit/components/stack/index.css +41 -0
  133. package/dist/lit/components/stack/index.d.ts +20 -0
  134. package/dist/lit/components/stack/index.js +103 -0
  135. package/dist/lit/components/switch/index.css +126 -0
  136. package/dist/lit/components/switch/index.d.ts +17 -0
  137. package/dist/lit/components/switch/index.js +116 -0
  138. package/dist/lit/components/table/index.css +85 -0
  139. package/dist/lit/components/table/index.d.ts +25 -0
  140. package/dist/lit/components/table/index.js +139 -0
  141. package/dist/lit/components/tabs/index.css +116 -0
  142. package/dist/lit/components/tabs/index.d.ts +49 -0
  143. package/dist/lit/components/tabs/index.js +320 -0
  144. package/dist/lit/components/text-field/index.css +90 -0
  145. package/dist/lit/components/text-field/index.d.ts +17 -0
  146. package/dist/lit/components/text-field/index.js +101 -0
  147. package/dist/lit/components/textarea/index.css +55 -0
  148. package/dist/lit/components/textarea/index.d.ts +26 -0
  149. package/dist/lit/components/textarea/index.js +124 -0
  150. package/dist/lit/components/tooltip/index.css +37 -0
  151. package/dist/lit/components/tooltip/index.d.ts +31 -0
  152. package/dist/lit/components/tooltip/index.js +196 -0
  153. package/dist/lit/components/validation/index.css +386 -0
  154. package/dist/lit/components/validation/index.d.ts +45 -0
  155. package/dist/lit/components/validation/index.js +318 -0
  156. package/dist/lit/index.d.ts +50 -0
  157. package/dist/lit/index.js +59 -0
  158. package/package.json +81 -0
  159. package/styles/README.md +346 -0
  160. package/styles/_elevation.css +24 -0
  161. package/styles/_fonts.css +6 -0
  162. package/styles/_layout.css +37 -0
  163. package/styles/_primitives.css +154 -0
  164. package/styles/_scroll.css +75 -0
  165. package/styles/_semantic.css +146 -0
  166. package/styles/_space.css +61 -0
  167. package/styles/_type.css +139 -0
  168. package/styles/_xmesh-extensions.css +232 -0
  169. package/styles/index.css +44 -0
  170. package/styles/md3/_color.css +102 -0
  171. package/styles/md3/_elevation.css +26 -0
  172. package/styles/md3/_motion.css +35 -0
  173. package/styles/md3/_shape.css +22 -0
  174. package/styles/md3/_state.css +22 -0
  175. package/styles/md3/_type.css +111 -0
@@ -0,0 +1,124 @@
1
+ /* ============================================
2
+ Dialog — a centered modal panel.
3
+
4
+ The panel chrome (inverse-surface fill, inverse-on-surface ink, hairline
5
+ border, corner-large radius, level5 elevation, the scrim ::backdrop, and the
6
+ centered modal positioning) is owned by the composed <xm-overlay> in the
7
+ `dialog` tier (modal mode). This file styles the panel's internal layout:
8
+ the header row + close control, the body, and the singular action row.
9
+
10
+ All ink is inverse-on-surface (AD-13) — the card-stack ink, matching the
11
+ theme-following panel surface (ADR 0006). Scrim is a flat alpha on
12
+ --md-sys-color-scrim (owned by the overlay); NO gradient (the snackbar scrim
13
+ is the only sanctioned one).
14
+
15
+ Entry/exit is opacity + a small translateY at short4 standard — no scale-in
16
+ reveal, no spring. The overlay owns the fade; this adds the lift.
17
+
18
+ BEM block: `dialog`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
19
+ Elements: __head, __header, __close, __body, __actions. Modifier: --static.
20
+ ============================================ */
21
+
22
+ .dialog {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--s-4);
26
+ min-width: 0;
27
+ color: var(--md-sys-color-inverse-on-surface);
28
+ padding: var(--s-5);
29
+ animation: dialog-in var(--md-sys-motion-duration-short4)
30
+ var(--md-sys-motion-easing-standard) both;
31
+ }
32
+ .dialog--static {
33
+ animation: none;
34
+ }
35
+
36
+ @keyframes dialog-in {
37
+ from {
38
+ opacity: 0;
39
+ transform: translateY(8px);
40
+ }
41
+ to {
42
+ opacity: 1;
43
+ transform: translateY(0);
44
+ }
45
+ }
46
+
47
+ .dialog__head {
48
+ display: flex;
49
+ align-items: flex-start;
50
+ justify-content: space-between;
51
+ gap: var(--s-3);
52
+ }
53
+ .dialog__head.is-empty {
54
+ /* No header text — keep the close control but drop the title row height. */
55
+ justify-content: flex-end;
56
+ }
57
+
58
+ .dialog__header {
59
+ font-family: var(--md-sys-typescale-title-large-font);
60
+ font-size: var(--md-sys-typescale-title-large-size);
61
+ line-height: var(--md-sys-typescale-title-large-line-height);
62
+ font-weight: var(--md-sys-typescale-title-large-weight);
63
+ letter-spacing: var(--md-sys-typescale-title-large-tracking);
64
+ color: var(--md-sys-color-inverse-on-surface);
65
+ text-wrap: pretty;
66
+ }
67
+
68
+ .dialog__close {
69
+ appearance: none;
70
+ border: none;
71
+ background: transparent;
72
+ color: color-mix(
73
+ in oklch,
74
+ var(--md-sys-color-inverse-on-surface) 70%,
75
+ transparent
76
+ );
77
+ width: 32px;
78
+ height: 32px;
79
+ border-radius: var(--md-sys-shape-corner-small);
80
+ cursor: pointer;
81
+ display: inline-flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ flex-shrink: 0;
85
+ margin: calc(-1 * var(--s-1)) calc(-1 * var(--s-1)) 0 0;
86
+ transition: background var(--md-sys-motion-duration-short3)
87
+ var(--md-sys-motion-easing-standard);
88
+ }
89
+ .dialog__close:hover {
90
+ background: color-mix(
91
+ in oklch,
92
+ var(--md-sys-color-inverse-on-surface) 10%,
93
+ transparent
94
+ );
95
+ color: var(--md-sys-color-inverse-on-surface);
96
+ }
97
+ .dialog__close:focus-visible {
98
+ outline: none;
99
+ box-shadow: var(--xm-state-focus-ring);
100
+ }
101
+
102
+ .dialog__body {
103
+ min-width: 0;
104
+ font-family: var(--md-sys-typescale-body-medium-font);
105
+ font-size: var(--md-sys-typescale-body-medium-size);
106
+ line-height: var(--md-sys-typescale-body-medium-line-height);
107
+ color: color-mix(
108
+ in oklch,
109
+ var(--md-sys-color-inverse-on-surface) 88%,
110
+ transparent
111
+ );
112
+ text-wrap: pretty;
113
+ }
114
+
115
+ .dialog__actions {
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: flex-end;
119
+ gap: var(--s-2);
120
+ flex-wrap: wrap;
121
+ }
122
+ .dialog__actions.is-empty {
123
+ display: none;
124
+ }
@@ -0,0 +1,24 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ export declare class XmDialog extends LitElement {
4
+ open: boolean;
5
+ noScrimClose: boolean;
6
+ static: boolean;
7
+ label: string;
8
+ private _hasHeader;
9
+ private _hasAction;
10
+ private _overlay;
11
+ private readonly _headerId;
12
+ private _wasOpen;
13
+ render(): TemplateResult;
14
+ protected updated(): void;
15
+ private _onOverlayClose;
16
+ private _onCloseButton;
17
+ private _onHeaderSlot;
18
+ private _onActionSlot;
19
+ }
20
+ declare global {
21
+ interface HTMLElementTagNameMap {
22
+ "xm-dialog": XmDialog;
23
+ }
24
+ }
@@ -0,0 +1,199 @@
1
+ /*
2
+ dialog/index.ts — <xm-dialog>, a centered modal dialog.
3
+
4
+ Composes the xm-overlay foundation (Story 1.4) for its scrim, modal stacking,
5
+ focus-trap, scroll-lock, and focus-restore — it does NOT hand-roll any of
6
+ these. The modal substrate is native <dialog>.showModal() (focus-trap +
7
+ background `inert` for free), surfaced through xm-overlay in the `dialog` tier
8
+ (the topmost tier: tooltip < menu < dialog). We drive the overlay through its
9
+ PUBLIC API only and never reach into its shadow root (AD-12).
10
+
11
+ Behaviour (FR-142 / FR-143):
12
+ • `open` is the single attribute controlling visibility — set it to show a
13
+ centered modal with a scrim; remove it to close
14
+ • Esc closes only this dialog (overlay's innermost-only handler
15
+ stopPropagations); closing also cascades-closes descendant popovers
16
+ • scrim-click closes by default, suppressed by `no-scrim-close`
17
+ • on close, focus restores to the trigger (delegated to the overlay)
18
+
19
+ Slots: `slot="header"` (title row → aria-labelledby), default (body), and the
20
+ singular `slot="action"` (trailing controls — one slot, many buttons; no
21
+ plural `actions`). Empty header/action rows collapse (alert's is-empty
22
+ pattern).
23
+
24
+ Event (AD-8): `close` — bare native-style name, bubbles + composed, structured
25
+ detail { reason }, fired on every close path.
26
+
27
+ Authoring:
28
+ <xm-dialog open>
29
+ <span slot="header">Delete this thread?</span>
30
+ This permanently removes the conversation and its artifacts.
31
+ <xm-button slot="action" variant="ghost">Cancel</xm-button>
32
+ <xm-button slot="action" variant="primary">Delete</xm-button>
33
+ </xm-dialog>
34
+
35
+ Properties:
36
+ open boolean (reflect) — the single visibility control
37
+ no-scrim-close boolean — a scrim click is a no-op (static backdrop)
38
+ static boolean — preview-only, suppress entry animation
39
+ label accessible name fallback when there is no header
40
+
41
+ Shadow DOM. Depends on xm-overlay + primitives (close icon) + tokens (AD-12).
42
+ */
43
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
44
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
45
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
46
+ 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;
47
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
48
+ };
49
+ import { LitElement, html, svg, nothing } from "lit";
50
+ import { customElement, property, query, state } from "lit/decorators.js";
51
+ const DIALOG_CSS = new URL("../dialog/index.css", import.meta.url).href;
52
+ let dialogSeq = 0;
53
+ const CLOSE_ICON = () => svg `
54
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none"
55
+ stroke="currentColor" stroke-width="2" stroke-linecap="round"
56
+ stroke-linejoin="round" class="ds-icon" aria-hidden="true">
57
+ <path d="M6 6l12 12M18 6L6 18" />
58
+ </svg>
59
+ `;
60
+ let XmDialog = class XmDialog extends LitElement {
61
+ constructor() {
62
+ super(...arguments);
63
+ this.open = false;
64
+ this.noScrimClose = false;
65
+ this.static = false;
66
+ this.label = "";
67
+ this._hasHeader = false;
68
+ this._hasAction = false;
69
+ this._headerId = `xm-dialog-${++dialogSeq}-header`;
70
+ this._wasOpen = false;
71
+ this._onOverlayClose = (e) => {
72
+ const detail = e.detail;
73
+ // Static backdrop: a scrim click must be a no-op. The overlay already
74
+ // closed itself, so re-open synchronously to keep the modal up.
75
+ if (detail?.reason === "backdrop" && this.noScrimClose) {
76
+ const ov = this._overlay;
77
+ this._wasOpen = true;
78
+ this.open = true;
79
+ ov?.show();
80
+ return;
81
+ }
82
+ this._wasOpen = false;
83
+ if (this.open)
84
+ this.open = false;
85
+ this.dispatchEvent(new CustomEvent("close", {
86
+ bubbles: true,
87
+ composed: true,
88
+ detail: { reason: detail?.reason ?? "api" },
89
+ }));
90
+ };
91
+ this._onCloseButton = () => {
92
+ const ov = this._overlay;
93
+ if (ov?.open)
94
+ ov.hide("api");
95
+ else {
96
+ this.open = false;
97
+ this._wasOpen = false;
98
+ this.dispatchEvent(new CustomEvent("close", {
99
+ bubbles: true,
100
+ composed: true,
101
+ detail: { reason: "api" },
102
+ }));
103
+ }
104
+ };
105
+ this._onHeaderSlot = (e) => {
106
+ const slot = e.target;
107
+ this._hasHeader = slot.assignedNodes({ flatten: true }).some((n) => {
108
+ if (n.nodeType === Node.TEXT_NODE)
109
+ return !!n.textContent?.trim();
110
+ return n.nodeType === Node.ELEMENT_NODE;
111
+ });
112
+ };
113
+ this._onActionSlot = (e) => {
114
+ const slot = e.target;
115
+ this._hasAction = slot.assignedElements().length > 0;
116
+ };
117
+ }
118
+ render() {
119
+ const cls = `dialog${this.static ? " dialog--static" : ""}`;
120
+ return html `
121
+ <link rel="stylesheet" href="${DIALOG_CSS}" />
122
+ <style>
123
+ :host { display: contents; }
124
+ </style>
125
+ <xm-overlay
126
+ mode="modal"
127
+ tier="dialog"
128
+ label=${this.label || nothing}
129
+ @xm-overlay-close=${this._onOverlayClose}
130
+ >
131
+ <div
132
+ class="${cls}"
133
+ role="dialog"
134
+ aria-modal="true"
135
+ aria-labelledby=${this._hasHeader ? this._headerId : nothing}
136
+ aria-label=${!this._hasHeader && this.label ? this.label : nothing}
137
+ >
138
+ <div class="dialog__head ${this._hasHeader ? "" : "is-empty"}">
139
+ <span class="dialog__header" id=${this._headerId}
140
+ ><slot name="header" @slotchange=${this._onHeaderSlot}></slot
141
+ ></span>
142
+ <button
143
+ type="button"
144
+ class="dialog__close"
145
+ aria-label="Close"
146
+ @click=${this._onCloseButton}
147
+ >
148
+ ${CLOSE_ICON()}
149
+ </button>
150
+ </div>
151
+ <div class="dialog__body"><slot></slot></div>
152
+ <div class="dialog__actions ${this._hasAction ? "" : "is-empty"}">
153
+ <slot name="action" @slotchange=${this._onActionSlot}></slot>
154
+ </div>
155
+ </div>
156
+ </xm-overlay>
157
+ `;
158
+ }
159
+ updated() {
160
+ // Drive the composed overlay from our single `open` control.
161
+ const ov = this._overlay;
162
+ if (!ov)
163
+ return;
164
+ if (this.open && !this._wasOpen) {
165
+ this._wasOpen = true;
166
+ ov.show();
167
+ }
168
+ else if (!this.open && this._wasOpen) {
169
+ this._wasOpen = false;
170
+ if (ov.open)
171
+ ov.hide("api");
172
+ }
173
+ }
174
+ };
175
+ __decorate([
176
+ property({ type: Boolean, reflect: true })
177
+ ], XmDialog.prototype, "open", void 0);
178
+ __decorate([
179
+ property({ type: Boolean, attribute: "no-scrim-close" })
180
+ ], XmDialog.prototype, "noScrimClose", void 0);
181
+ __decorate([
182
+ property({ type: Boolean })
183
+ ], XmDialog.prototype, "static", void 0);
184
+ __decorate([
185
+ property({ type: String })
186
+ ], XmDialog.prototype, "label", void 0);
187
+ __decorate([
188
+ state()
189
+ ], XmDialog.prototype, "_hasHeader", void 0);
190
+ __decorate([
191
+ state()
192
+ ], XmDialog.prototype, "_hasAction", void 0);
193
+ __decorate([
194
+ query("xm-overlay")
195
+ ], XmDialog.prototype, "_overlay", void 0);
196
+ XmDialog = __decorate([
197
+ customElement("xm-dialog")
198
+ ], XmDialog);
199
+ export { XmDialog };
@@ -0,0 +1,27 @@
1
+ /* ============================================
2
+ <xm-divider> — standalone hairline rule.
3
+
4
+ Exactly 1px using --md-sys-color-outline-variant (the default hairline
5
+ weight; the same rule a card's header/footer split uses). Never 2px —
6
+ 2px is reserved for the drag-active field state only. No thickness
7
+ variants, no status hue: a divider is always a neutral hairline that
8
+ traces its host surface and reads on both surface families (AD-13).
9
+
10
+ BEM: block `divider`; modifiers `--horizontal` / `--vertical`.
11
+ ============================================ */
12
+
13
+ .divider {
14
+ background: var(--md-sys-color-outline-variant);
15
+ border: 0;
16
+ }
17
+
18
+ .divider--horizontal {
19
+ width: 100%;
20
+ height: 1px;
21
+ }
22
+
23
+ .divider--vertical {
24
+ width: 1px;
25
+ height: 100%;
26
+ min-height: 1em;
27
+ }
@@ -0,0 +1,13 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ type DividerOrientation = "horizontal" | "vertical";
4
+ declare class XmDivider extends LitElement {
5
+ orientation: DividerOrientation;
6
+ render(): TemplateResult;
7
+ }
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ "xm-divider": XmDivider;
11
+ }
12
+ }
13
+ export {};
@@ -0,0 +1,67 @@
1
+ /*
2
+ divider/index.ts — <xm-divider>, the standalone hairline rule.
3
+
4
+ <xm-divider> — a 1px low-saturation separator. The standalone version of
5
+ the hairline rule that splits a card header from its body.
6
+
7
+ Authoring:
8
+ <xm-divider></xm-divider> horizontal (default)
9
+ <xm-divider orientation="vertical"></xm-divider>
10
+
11
+ Properties:
12
+ orientation "horizontal" | "vertical" (default "horizontal")
13
+
14
+ Surface / ink (AD-13): a divider is transparent except for its hairline.
15
+ It traces its host surface and reads as a neutral hairline against BOTH
16
+ surface families via the single --md-sys-color-outline-variant token,
17
+ which resolves per theme. Exactly 1px — 2px is a policy violation.
18
+
19
+ Shadow DOM; Lit from lit; sibling CSS resolved via
20
+ the built-file-relative new URL(...). BEM block `divider`.
21
+ */
22
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
23
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
24
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
25
+ 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;
26
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
27
+ };
28
+ import { LitElement, html } from "lit";
29
+ import { customElement, property } from "lit/decorators.js";
30
+ // Resolve CSS relative to the *built* file:
31
+ // lit/build/components/divider/index.js → ../divider/index.css.
32
+ const DIVIDER_CSS = new URL("../divider/index.css", import.meta.url).href;
33
+ let XmDivider = class XmDivider extends LitElement {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.orientation = "horizontal";
37
+ }
38
+ render() {
39
+ const orientation = this.orientation === "vertical" ? "vertical" : "horizontal";
40
+ return html `
41
+ <link rel="stylesheet" href="${DIVIDER_CSS}" />
42
+ <style>
43
+ :host {
44
+ display: block;
45
+ }
46
+ :host([orientation="vertical"]) {
47
+ display: inline-block;
48
+ align-self: stretch;
49
+ }
50
+ :host([hidden]) {
51
+ display: none;
52
+ }
53
+ </style>
54
+ <div
55
+ class="divider divider--${orientation}"
56
+ role="separator"
57
+ aria-orientation="${orientation}"
58
+ ></div>
59
+ `;
60
+ }
61
+ };
62
+ __decorate([
63
+ property({ type: String, reflect: true })
64
+ ], XmDivider.prototype, "orientation", void 0);
65
+ XmDivider = __decorate([
66
+ customElement("xm-divider")
67
+ ], XmDivider);
@@ -0,0 +1,69 @@
1
+ /* ============================================
2
+ Empty state — the shared no-data placeholder.
3
+
4
+ A centered, stacked column: line icon · sentence-case headline ·
5
+ secondary copy · optional action. The component is TRANSPARENT and
6
+ traces its host surface (AD-13): every ink derives from the inherited
7
+ `color` (the host's on-* ink), never a hard-pinned surface token — so
8
+ it pairs correctly whether it sits on the desk (`surface*` → on-surface)
9
+ or on a card (`inverse-surface` → inverse-on-surface), in both themes.
10
+
11
+ No imagery, no illustration, no emoji, no gradient — the only graphic
12
+ is the Lucide-style line icon (currentColor, no fill).
13
+
14
+ BEM block: `empty-state`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
15
+ ============================================ */
16
+
17
+ .empty-state {
18
+ display: flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ justify-content: center;
22
+ text-align: center;
23
+ gap: var(--s-2);
24
+ padding: var(--s-8) var(--s-6);
25
+ /* Trace the host: inherit the on-* ink the host already set on the
26
+ surface this empty-state sits on. No surface fill of its own. */
27
+ color: inherit;
28
+ }
29
+
30
+ .empty-state__icon {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ margin-bottom: var(--s-1);
35
+ /* Icon reads as a quiet marker — softened from the host ink via the
36
+ inherited currentColor, not a hard-pinned token. */
37
+ color: color-mix(in oklab, currentColor 64%, transparent);
38
+ }
39
+
40
+ .empty-state__heading {
41
+ font-family: var(--md-sys-typescale-title-medium-font);
42
+ font-size: var(--md-sys-typescale-title-medium-size);
43
+ line-height: var(--md-sys-typescale-title-medium-line-height);
44
+ font-weight: var(--md-sys-typescale-title-medium-weight);
45
+ letter-spacing: var(--md-sys-typescale-title-medium-tracking);
46
+ color: currentColor;
47
+ text-wrap: balance;
48
+ }
49
+ .empty-state__heading.is-empty { display: none; }
50
+
51
+ .empty-state__text {
52
+ max-width: 44ch;
53
+ font-family: var(--md-sys-typescale-body-medium-font);
54
+ font-size: var(--md-sys-typescale-body-medium-size);
55
+ line-height: var(--md-sys-typescale-body-medium-line-height);
56
+ /* Soft tier — derived from the host ink so it works on either surface
57
+ family. (on-surface-variant / inverse muted both read as ~70% ink.) */
58
+ color: color-mix(in oklab, currentColor 70%, transparent);
59
+ text-wrap: pretty;
60
+ }
61
+ .empty-state__text.is-empty { display: none; }
62
+
63
+ .empty-state__action {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ margin-top: var(--s-3);
68
+ }
69
+ .empty-state__action.is-empty { display: none; }
@@ -0,0 +1,21 @@
1
+ import { LitElement } from "lit";
2
+ import type { TemplateResult } from "lit";
3
+ export declare class XmEmptyState extends LitElement {
4
+ heading: string;
5
+ /** A primitives icon element name (e.g. "xm-search-icon"). Used only when no
6
+ slot="icon" content is supplied. */
7
+ icon: string;
8
+ private _hasIconSlot;
9
+ private _hasText;
10
+ private _hasAction;
11
+ render(): TemplateResult;
12
+ private _iconEl;
13
+ private _onIconSlot;
14
+ private _onDefaultSlot;
15
+ private _onActionSlot;
16
+ }
17
+ declare global {
18
+ interface HTMLElementTagNameMap {
19
+ "xm-empty-state": XmEmptyState;
20
+ }
21
+ }
@@ -0,0 +1,123 @@
1
+ /*
2
+ empty-state/index.ts — <xm-empty-state>, the shared no-data placeholder.
3
+
4
+ An empty view that reads as intentionally designed, not broken: a Lucide-style
5
+ line icon (from `primitives`, via slot or `icon` attribute), a sentence-case
6
+ headline, secondary copy (default slot), and an optional singular `slot="action"`
7
+ CTA. There is NO imagery / illustration / emoji — the only graphic is the line
8
+ icon, inked via currentColor from the host surface's on-* ink (AD-13).
9
+
10
+ This is the placeholder reused by xm-autocomplete (no-match, FR-120) and
11
+ xm-data-table (empty rows, FR-161); its API composes into both.
12
+
13
+ Authoring:
14
+ <xm-empty-state heading="No results found">
15
+ <xm-search-icon slot="icon" size="22"></xm-search-icon>
16
+ Try a different search term or clear the filters.
17
+ <xm-button slot="action" variant="outline" size="sm">Clear filters</xm-button>
18
+ </xm-empty-state>
19
+
20
+ Properties:
21
+ heading short title string (sentence case)
22
+ icon optional primitives icon NAME (e.g. "xm-search-icon"); used only
23
+ when no slot="icon" content is provided
24
+
25
+ Slots:
26
+ icon a primitives line-icon (overrides the `icon` attribute)
27
+ default secondary copy
28
+ action single trailing CTA (collapses when empty, mirroring xm-alert)
29
+
30
+ Shadow DOM. Loads its sibling CSS via the built-file-relative new URL(...)
31
+ pattern. Depends only on `primitives` + tokens — no overlay (AD-12).
32
+ */
33
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
34
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36
+ 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;
37
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
38
+ };
39
+ import { LitElement, html, nothing } from "lit";
40
+ import { customElement, property, state } from "lit/decorators.js";
41
+ const EMPTY_STATE_CSS = new URL("../empty-state/index.css", import.meta.url).href;
42
+ let XmEmptyState = class XmEmptyState extends LitElement {
43
+ constructor() {
44
+ super(...arguments);
45
+ this.heading = "";
46
+ /** A primitives icon element name (e.g. "xm-search-icon"). Used only when no
47
+ slot="icon" content is supplied. */
48
+ this.icon = "";
49
+ this._hasIconSlot = false;
50
+ this._hasText = false;
51
+ this._hasAction = false;
52
+ this._onIconSlot = (e) => {
53
+ const slot = e.target;
54
+ this._hasIconSlot = slot.assignedElements().length > 0;
55
+ };
56
+ this._onDefaultSlot = (e) => {
57
+ const slot = e.target;
58
+ const nodes = slot.assignedNodes({ flatten: true });
59
+ this._hasText = nodes.some((n) => {
60
+ if (n.nodeType === Node.TEXT_NODE)
61
+ return !!n.textContent?.trim();
62
+ if (n.nodeType === Node.ELEMENT_NODE)
63
+ return true;
64
+ return false;
65
+ });
66
+ };
67
+ this._onActionSlot = (e) => {
68
+ const slot = e.target;
69
+ this._hasAction = slot.assignedElements().length > 0;
70
+ };
71
+ }
72
+ render() {
73
+ const showFallbackIcon = !this._hasIconSlot && this.icon !== "";
74
+ return html `
75
+ <link rel="stylesheet" href="${EMPTY_STATE_CSS}" />
76
+ <style>
77
+ :host { display: block; }
78
+ :host([hidden]) { display: none; }
79
+ </style>
80
+ <div class="empty-state" role="status">
81
+ <span class="empty-state__icon" aria-hidden="true">
82
+ ${showFallbackIcon ? this._iconEl() : nothing}
83
+ <slot name="icon" @slotchange=${this._onIconSlot}></slot>
84
+ </span>
85
+ <span class="empty-state__heading ${this.heading ? "" : "is-empty"}"
86
+ >${this.heading}</span
87
+ >
88
+ <span class="empty-state__text ${this._hasText ? "" : "is-empty"}"
89
+ ><slot @slotchange=${this._onDefaultSlot}></slot
90
+ ></span>
91
+ <span class="empty-state__action ${this._hasAction ? "" : "is-empty"}"
92
+ ><slot name="action" @slotchange=${this._onActionSlot}></slot
93
+ ></span>
94
+ </div>
95
+ `;
96
+ }
97
+ // Render the named primitives icon by tag at 22px. The element inks via
98
+ // currentColor from .empty-state__icon, so no color is hard-pinned.
99
+ _iconEl() {
100
+ const el = document.createElement(this.icon);
101
+ el.setAttribute("size", "22");
102
+ return html `${el}`;
103
+ }
104
+ };
105
+ __decorate([
106
+ property({ type: String })
107
+ ], XmEmptyState.prototype, "heading", void 0);
108
+ __decorate([
109
+ property({ type: String })
110
+ ], XmEmptyState.prototype, "icon", void 0);
111
+ __decorate([
112
+ state()
113
+ ], XmEmptyState.prototype, "_hasIconSlot", void 0);
114
+ __decorate([
115
+ state()
116
+ ], XmEmptyState.prototype, "_hasText", void 0);
117
+ __decorate([
118
+ state()
119
+ ], XmEmptyState.prototype, "_hasAction", void 0);
120
+ XmEmptyState = __decorate([
121
+ customElement("xm-empty-state")
122
+ ], XmEmptyState);
123
+ export { XmEmptyState };