@neptune.fintech/web-ui 1.0.0 → 2.1.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 (46) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +25 -3
  3. package/dist/components/actions.d.ts +43 -0
  4. package/dist/components/actions.d.ts.map +1 -0
  5. package/dist/components/actions.js +279 -0
  6. package/dist/components/actions.js.map +1 -0
  7. package/dist/components/button.d.ts +1 -1
  8. package/dist/components/button.d.ts.map +1 -1
  9. package/dist/components/button.js +9 -1
  10. package/dist/components/button.js.map +1 -1
  11. package/dist/components/containers.d.ts +54 -0
  12. package/dist/components/containers.d.ts.map +1 -0
  13. package/dist/components/containers.js +298 -0
  14. package/dist/components/containers.js.map +1 -0
  15. package/dist/components/feedback.d.ts +42 -0
  16. package/dist/components/feedback.d.ts.map +1 -0
  17. package/dist/components/feedback.js +241 -0
  18. package/dist/components/feedback.js.map +1 -0
  19. package/dist/components/inputs.d.ts +8 -1
  20. package/dist/components/inputs.d.ts.map +1 -1
  21. package/dist/components/inputs.js +57 -3
  22. package/dist/components/inputs.js.map +1 -1
  23. package/dist/components/layout.d.ts +68 -0
  24. package/dist/components/layout.d.ts.map +1 -0
  25. package/dist/components/layout.js +359 -0
  26. package/dist/components/layout.js.map +1 -0
  27. package/dist/components/nav-rail.d.ts +22 -0
  28. package/dist/components/nav-rail.d.ts.map +1 -0
  29. package/dist/components/nav-rail.js +120 -0
  30. package/dist/components/nav-rail.js.map +1 -0
  31. package/dist/components/selection.d.ts +51 -0
  32. package/dist/components/selection.d.ts.map +1 -0
  33. package/dist/components/selection.js +377 -0
  34. package/dist/components/selection.js.map +1 -0
  35. package/dist/index.d.ts +7 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +7 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/register.d.ts.map +1 -1
  40. package/dist/register.js +38 -0
  41. package/dist/register.js.map +1 -1
  42. package/dist/theme/applyTheme.d.ts +1 -1
  43. package/dist/theme/applyTheme.js +2 -2
  44. package/dist/theme/applyTheme.js.map +1 -1
  45. package/package.json +2 -2
  46. package/styles/themes.css +17 -17
@@ -0,0 +1,298 @@
1
+ // © 2026 Neptune.Fintech (neptune.ly) · Neptune Odyssey Community License v1.0
2
+ // Neptune Odyssey — overlay containers
3
+ // <npt-dialog>, <npt-bottom-sheet>, <npt-menu> + <npt-menu-item>.
4
+ // Custom-property driven only; logical layout → mirrors in RTL.
5
+ import { NptElement, css, html, A11Y } from "./base.js";
6
+ const FOCUSABLE = 'a[href],button:not([disabled]),textarea,input,select,[tabindex]:not([tabindex="-1"]),npt-button,npt-icon-button';
7
+ /**
8
+ * <npt-dialog [open] headline="Confirm">
9
+ * …content… <span slot="actions">…</span>
10
+ * </npt-dialog>
11
+ * Scrim + centred surface. ESC and backdrop close; focus-trap-lite.
12
+ */
13
+ export class NptDialog extends NptElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.onClick = (e) => {
17
+ const t = e.target;
18
+ if (t.classList.contains("scrim") || t.closest("[data-close]"))
19
+ this.close();
20
+ };
21
+ this.onKey = (e) => {
22
+ if (e.key === "Escape") {
23
+ e.preventDefault();
24
+ this.close();
25
+ }
26
+ else if (e.key === "Tab") {
27
+ const items = Array.from(this.querySelectorAll(FOCUSABLE)).filter((el) => el.offsetParent !== null || el === document.activeElement);
28
+ if (items.length === 0)
29
+ return;
30
+ const first = items[0];
31
+ const last = items[items.length - 1];
32
+ const active = this.getRootNode().activeElement;
33
+ if (e.shiftKey && active === first) {
34
+ e.preventDefault();
35
+ last?.focus();
36
+ }
37
+ else if (!e.shiftKey && active === last) {
38
+ e.preventDefault();
39
+ first?.focus();
40
+ }
41
+ }
42
+ };
43
+ }
44
+ attributeChangedCallback(name) {
45
+ if (this.isConnected)
46
+ this.update();
47
+ if (name === "open" && this.hasAttribute("open"))
48
+ this.focusFirst();
49
+ }
50
+ connectedCallback() {
51
+ super.connectedCallback();
52
+ this.root.addEventListener("click", this.onClick);
53
+ this.addEventListener("keydown", this.onKey);
54
+ }
55
+ disconnectedCallback() {
56
+ this.root.removeEventListener("click", this.onClick);
57
+ this.removeEventListener("keydown", this.onKey);
58
+ }
59
+ /** Close the overlay and emit a `close` event. */
60
+ close() {
61
+ this.removeAttribute("open");
62
+ this.dispatchEvent(new CustomEvent("close", { bubbles: true }));
63
+ }
64
+ focusFirst() {
65
+ queueMicrotask(() => {
66
+ const el = this.querySelector(FOCUSABLE) ?? this.root.querySelector(".surface");
67
+ el?.focus?.();
68
+ });
69
+ }
70
+ styles() {
71
+ return css `
72
+ ${A11Y}
73
+ :host {
74
+ display: none;
75
+ }
76
+ :host([open]) {
77
+ display: block;
78
+ }
79
+ .scrim {
80
+ position: fixed;
81
+ inset: 0;
82
+ z-index: var(--npt-z-modal, 70);
83
+ background: color-mix(in oklab, var(--md-sys-color-scrim) 40%, transparent);
84
+ display: grid;
85
+ place-items: center;
86
+ padding: var(--npt-space-4, 16px);
87
+ }
88
+ .surface {
89
+ inline-size: min(560px, 100%);
90
+ max-block-size: 90vh;
91
+ overflow: auto;
92
+ background: var(--md-sys-color-surface-container-high);
93
+ color: var(--md-sys-color-on-surface);
94
+ border-radius: var(--npt-corner-xl, 32px);
95
+ padding: var(--npt-space-6, 24px);
96
+ box-shadow: var(--npt-elevation-5, 0 28px 60px rgba(0, 0, 0, 0.3));
97
+ box-sizing: border-box;
98
+ }
99
+ .headline {
100
+ font-family: var(--npt-font-display);
101
+ font-size: var(--npt-text-headline, 28px);
102
+ font-weight: var(--npt-display-weight, 700);
103
+ margin: 0 0 var(--npt-space-4, 16px);
104
+ }
105
+ .content {
106
+ font-family: var(--npt-font-text);
107
+ font-size: var(--npt-text-body-lg, 16px);
108
+ color: var(--md-sys-color-on-surface-variant);
109
+ }
110
+ .actions {
111
+ display: flex;
112
+ justify-content: flex-end;
113
+ gap: var(--npt-space-2, 8px);
114
+ margin-block-start: var(--npt-space-6, 24px);
115
+ }
116
+ `;
117
+ }
118
+ render() {
119
+ const headline = (this.getAttribute("headline") ?? "").replace(/[<>]/g, "");
120
+ return html `<div class="scrim" part="scrim">
121
+ <div class="surface" part="surface" role="dialog" aria-modal="true" aria-label="${headline}" tabindex="-1">
122
+ ${headline ? html `<h2 class="headline">${headline}</h2>` : ""}
123
+ <div class="content"><slot></slot></div>
124
+ <div class="actions"><slot name="actions"></slot></div>
125
+ </div>
126
+ </div>`;
127
+ }
128
+ }
129
+ NptDialog.observedAttributes = ["open", "headline"];
130
+ /**
131
+ * <npt-bottom-sheet [open]>…content…</npt-bottom-sheet>
132
+ * Bottom-anchored sheet with a drag affordance + scrim. Backdrop/ESC close.
133
+ */
134
+ export class NptBottomSheet extends NptElement {
135
+ constructor() {
136
+ super(...arguments);
137
+ this.onClick = (e) => {
138
+ const t = e.target;
139
+ if (t.classList.contains("scrim") || t.closest("[data-close]"))
140
+ this.close();
141
+ };
142
+ this.onKey = (e) => {
143
+ if (e.key === "Escape") {
144
+ e.preventDefault();
145
+ this.close();
146
+ }
147
+ };
148
+ }
149
+ attributeChangedCallback() {
150
+ if (this.isConnected)
151
+ this.update();
152
+ }
153
+ connectedCallback() {
154
+ super.connectedCallback();
155
+ this.root.addEventListener("click", this.onClick);
156
+ this.addEventListener("keydown", this.onKey);
157
+ }
158
+ disconnectedCallback() {
159
+ this.root.removeEventListener("click", this.onClick);
160
+ this.removeEventListener("keydown", this.onKey);
161
+ }
162
+ /** Close the sheet and emit a `close` event. */
163
+ close() {
164
+ this.removeAttribute("open");
165
+ this.dispatchEvent(new CustomEvent("close", { bubbles: true }));
166
+ }
167
+ styles() {
168
+ return css `
169
+ ${A11Y}
170
+ :host {
171
+ display: none;
172
+ }
173
+ :host([open]) {
174
+ display: block;
175
+ }
176
+ .scrim {
177
+ position: fixed;
178
+ inset: 0;
179
+ z-index: var(--npt-z-modal, 70);
180
+ background: color-mix(in oklab, var(--md-sys-color-scrim) 40%, transparent);
181
+ display: flex;
182
+ align-items: flex-end;
183
+ justify-content: center;
184
+ }
185
+ .sheet {
186
+ inline-size: min(640px, 100%);
187
+ max-block-size: 90vh;
188
+ overflow: auto;
189
+ background: var(--md-sys-color-surface-container-low);
190
+ color: var(--md-sys-color-on-surface);
191
+ border-start-start-radius: var(--npt-corner-xl, 32px);
192
+ border-start-end-radius: var(--npt-corner-xl, 32px);
193
+ padding: var(--npt-space-4, 16px) var(--npt-space-6, 24px) var(--npt-space-6, 24px);
194
+ box-shadow: var(--npt-elevation-5, 0 28px 60px rgba(0, 0, 0, 0.3));
195
+ box-sizing: border-box;
196
+ }
197
+ .grip {
198
+ inline-size: 32px;
199
+ block-size: 4px;
200
+ border-radius: var(--npt-corner-full, 999px);
201
+ background: var(--md-sys-color-outline-variant);
202
+ margin: 0 auto var(--npt-space-4, 16px);
203
+ }
204
+ `;
205
+ }
206
+ render() {
207
+ return html `<div class="scrim" part="scrim">
208
+ <div class="sheet" part="sheet" role="dialog" aria-modal="true">
209
+ <div class="grip" part="grip" aria-hidden="true"></div>
210
+ <slot></slot>
211
+ </div>
212
+ </div>`;
213
+ }
214
+ }
215
+ NptBottomSheet.observedAttributes = ["open"];
216
+ /**
217
+ * <npt-menu [open]> with <npt-menu-item> children.
218
+ * Anchored popover. Place inside a positioned ancestor for anchoring.
219
+ */
220
+ export class NptMenu extends NptElement {
221
+ attributeChangedCallback() {
222
+ if (this.isConnected)
223
+ this.update();
224
+ }
225
+ styles() {
226
+ return css `
227
+ ${A11Y}
228
+ :host {
229
+ display: none;
230
+ position: absolute;
231
+ inset-block-start: 100%;
232
+ inset-inline-start: 0;
233
+ z-index: var(--npt-z-overlay, 60);
234
+ }
235
+ :host([open]) {
236
+ display: block;
237
+ }
238
+ .menu {
239
+ min-inline-size: 180px;
240
+ padding-block: var(--npt-space-2, 8px);
241
+ background: var(--md-sys-color-surface-container);
242
+ color: var(--md-sys-color-on-surface);
243
+ border-radius: var(--npt-corner-sm, 12px);
244
+ box-shadow: var(--npt-elevation-3, 0 8px 20px rgba(0, 0, 0, 0.2));
245
+ }
246
+ `;
247
+ }
248
+ render() {
249
+ return html `<div class="menu" part="menu" role="menu"><slot></slot></div>`;
250
+ }
251
+ }
252
+ NptMenu.observedAttributes = ["open"];
253
+ /** <npt-menu-item [disabled]>Settings</npt-menu-item> */
254
+ export class NptMenuItem extends NptElement {
255
+ attributeChangedCallback() {
256
+ if (this.isConnected)
257
+ this.update();
258
+ }
259
+ styles() {
260
+ return css `
261
+ ${A11Y}
262
+ :host {
263
+ display: block;
264
+ }
265
+ .item {
266
+ inline-size: 100%;
267
+ min-height: 48px;
268
+ display: flex;
269
+ align-items: center;
270
+ gap: var(--npt-space-3, 12px);
271
+ padding-inline: var(--npt-space-4, 16px);
272
+ border: none;
273
+ background: transparent;
274
+ color: inherit;
275
+ text-align: start;
276
+ cursor: pointer;
277
+ font-family: var(--npt-font-text);
278
+ font-size: var(--npt-text-body-lg, 16px);
279
+ transition: background-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
280
+ }
281
+ .item:hover:not(:disabled) {
282
+ background: var(--md-sys-color-surface-container-highest);
283
+ }
284
+ .item:disabled {
285
+ cursor: not-allowed;
286
+ opacity: 0.38;
287
+ }
288
+ `;
289
+ }
290
+ render() {
291
+ const disabled = this.hasAttribute("disabled") ? "disabled" : "";
292
+ return html `<button class="item" part="item" role="menuitem" ${disabled}>
293
+ <slot name="leading"></slot><slot></slot>
294
+ </button>`;
295
+ }
296
+ }
297
+ NptMenuItem.observedAttributes = ["disabled"];
298
+ //# sourceMappingURL=containers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containers.js","sourceRoot":"","sources":["../../src/components/containers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { NptElement } from "./base.js";
2
+ /**
3
+ * <npt-progress variant="linear|circular" value="60" [indeterminate]></npt-progress>
4
+ * Omit value (or set [indeterminate]) for the indeterminate state.
5
+ */
6
+ export declare class NptProgress extends NptElement {
7
+ static observedAttributes: string[];
8
+ attributeChangedCallback(): void;
9
+ protected styles(): string;
10
+ protected render(): string;
11
+ }
12
+ /**
13
+ * <npt-snackbar message="Saved" [open]>…optional action slot…</npt-snackbar>
14
+ * Inverse-surface toast. Provide an action via the default slot.
15
+ */
16
+ export declare class NptSnackbar extends NptElement {
17
+ static observedAttributes: string[];
18
+ attributeChangedCallback(): void;
19
+ protected styles(): string;
20
+ protected render(): string;
21
+ }
22
+ /**
23
+ * <npt-tooltip label="Copy"><npt-icon-button>⧉</npt-icon-button></npt-tooltip>
24
+ * Wraps a trigger; reveals the label on hover/focus.
25
+ */
26
+ export declare class NptTooltip extends NptElement {
27
+ static observedAttributes: string[];
28
+ attributeChangedCallback(): void;
29
+ protected styles(): string;
30
+ protected render(): string;
31
+ }
32
+ /**
33
+ * <npt-banner text="Update available">…actions slot…</npt-banner>
34
+ * Surface-level inline message with an actions slot.
35
+ */
36
+ export declare class NptBanner extends NptElement {
37
+ static observedAttributes: string[];
38
+ attributeChangedCallback(): void;
39
+ protected styles(): string;
40
+ protected render(): string;
41
+ }
42
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/components/feedback.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAmB,MAAM,WAAW,CAAC;AAKxD;;;GAGG;AACH,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,kBAAkB,WAAkD;IAE3E,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAsD1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAuB3B;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,kBAAkB,WAAuB;IAEhD,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAgC1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAO3B;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,UAAU;IACxC,MAAM,CAAC,kBAAkB,WAAa;IAEtC,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAgC1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAK3B;AAED;;;GAGG;AACH,qBAAa,SAAU,SAAQ,UAAU;IACvC,MAAM,CAAC,kBAAkB,WAAY;IAErC,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAiC1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAQ3B"}
@@ -0,0 +1,241 @@
1
+ // © 2026 Neptune.Fintech (neptune.ly) · Neptune Odyssey Community License v1.0
2
+ // Neptune Odyssey — communication & feedback
3
+ // <npt-progress>, <npt-snackbar>, <npt-tooltip>, <npt-banner>.
4
+ // Custom-property driven only; logical layout → mirrors in RTL.
5
+ import { NptElement, css, html, A11Y } from "./base.js";
6
+ const esc = (v) => (v ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
7
+ /**
8
+ * <npt-progress variant="linear|circular" value="60" [indeterminate]></npt-progress>
9
+ * Omit value (or set [indeterminate]) for the indeterminate state.
10
+ */
11
+ export class NptProgress extends NptElement {
12
+ attributeChangedCallback() {
13
+ if (this.isConnected)
14
+ this.update();
15
+ }
16
+ styles() {
17
+ return css `
18
+ ${A11Y}
19
+ :host {
20
+ display: block;
21
+ }
22
+ .track {
23
+ block-size: 4px;
24
+ inline-size: 100%;
25
+ border-radius: var(--npt-corner-full, 999px);
26
+ background: var(--md-sys-color-surface-container-highest);
27
+ overflow: hidden;
28
+ }
29
+ .bar {
30
+ block-size: 100%;
31
+ background: var(--md-sys-color-primary);
32
+ border-radius: var(--npt-corner-full, 999px);
33
+ transition: inline-size var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
34
+ }
35
+ :host([indeterminate]) .bar,
36
+ :host(:not([value])) .bar {
37
+ inline-size: 40%;
38
+ animation: slide 1.4s var(--npt-ease-standard, ease) infinite;
39
+ }
40
+ @keyframes slide {
41
+ 0% { transform: translateX(-120%); }
42
+ 100% { transform: translateX(320%); }
43
+ }
44
+ .ring {
45
+ inline-size: 48px;
46
+ block-size: 48px;
47
+ transform: rotate(-90deg);
48
+ }
49
+ .ring circle {
50
+ fill: none;
51
+ stroke-width: 4;
52
+ }
53
+ .ring .bg {
54
+ stroke: var(--md-sys-color-surface-container-highest);
55
+ }
56
+ .ring .fg {
57
+ stroke: var(--md-sys-color-primary);
58
+ stroke-linecap: round;
59
+ transition: stroke-dashoffset var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
60
+ }
61
+ :host([indeterminate]) .ring {
62
+ animation: spin 1.2s linear infinite;
63
+ }
64
+ @keyframes spin {
65
+ to { transform: rotate(270deg); }
66
+ }
67
+ `;
68
+ }
69
+ render() {
70
+ const variant = this.getAttribute("variant") || "linear";
71
+ const indeterminate = this.hasAttribute("indeterminate") || !this.hasAttribute("value");
72
+ const value = Math.max(0, Math.min(100, Number(this.getAttribute("value") ?? 0)));
73
+ const label = esc(this.getAttribute("label")) || "progress";
74
+ const aria = indeterminate
75
+ ? html `role="progressbar" aria-label="${label}"`
76
+ : html `role="progressbar" aria-label="${label}" aria-valuenow="${value}" aria-valuemin="0" aria-valuemax="100"`;
77
+ if (variant === "circular") {
78
+ const r = 20;
79
+ const circ = 2 * Math.PI * r;
80
+ const offset = indeterminate ? circ * 0.7 : circ * (1 - value / 100);
81
+ return html `<svg class="ring" part="ring" viewBox="0 0 48 48" ${aria}>
82
+ <circle class="bg" cx="24" cy="24" r="${r}"></circle>
83
+ <circle class="fg" cx="24" cy="24" r="${r}"
84
+ stroke-dasharray="${circ}" stroke-dashoffset="${offset}"></circle>
85
+ </svg>`;
86
+ }
87
+ const width = indeterminate ? "40%" : `${value}%`;
88
+ return html `<div class="track" part="track" ${aria}>
89
+ <div class="bar" style="inline-size:${width}"></div>
90
+ </div>`;
91
+ }
92
+ }
93
+ NptProgress.observedAttributes = ["variant", "value", "indeterminate", "label"];
94
+ /**
95
+ * <npt-snackbar message="Saved" [open]>…optional action slot…</npt-snackbar>
96
+ * Inverse-surface toast. Provide an action via the default slot.
97
+ */
98
+ export class NptSnackbar extends NptElement {
99
+ attributeChangedCallback() {
100
+ if (this.isConnected)
101
+ this.update();
102
+ }
103
+ styles() {
104
+ return css `
105
+ ${A11Y}
106
+ :host {
107
+ display: none;
108
+ }
109
+ :host([open]) {
110
+ display: block;
111
+ }
112
+ .bar {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: var(--npt-space-4, 16px);
116
+ min-height: 48px;
117
+ padding-inline: var(--npt-space-4, 16px);
118
+ padding-block: var(--npt-space-3, 12px);
119
+ border-radius: var(--npt-corner-xs, 8px);
120
+ background: var(--md-sys-color-inverse-surface);
121
+ color: var(--md-sys-color-inverse-on-surface);
122
+ box-shadow: var(--npt-elevation-3, 0 8px 20px rgba(0, 0, 0, 0.2));
123
+ font-family: var(--npt-font-text);
124
+ font-size: var(--npt-text-body, 14px);
125
+ }
126
+ .msg {
127
+ flex: 1 1 auto;
128
+ }
129
+ ::slotted(*) {
130
+ color: var(--md-sys-color-inverse-primary);
131
+ }
132
+ `;
133
+ }
134
+ render() {
135
+ const message = esc(this.getAttribute("message"));
136
+ return html `<div class="bar" part="bar" role="status" aria-live="polite">
137
+ <span class="msg">${message}</span>
138
+ <slot></slot>
139
+ </div>`;
140
+ }
141
+ }
142
+ NptSnackbar.observedAttributes = ["message", "open"];
143
+ /**
144
+ * <npt-tooltip label="Copy"><npt-icon-button>⧉</npt-icon-button></npt-tooltip>
145
+ * Wraps a trigger; reveals the label on hover/focus.
146
+ */
147
+ export class NptTooltip extends NptElement {
148
+ attributeChangedCallback() {
149
+ if (this.isConnected)
150
+ this.update();
151
+ }
152
+ styles() {
153
+ return css `
154
+ ${A11Y}
155
+ :host {
156
+ display: inline-flex;
157
+ position: relative;
158
+ }
159
+ .tip {
160
+ position: absolute;
161
+ inset-block-end: calc(100% + var(--npt-space-2, 8px));
162
+ inset-inline-start: 50%;
163
+ transform: translateX(-50%);
164
+ white-space: nowrap;
165
+ padding-inline: var(--npt-space-3, 12px);
166
+ padding-block: var(--npt-space-1, 4px);
167
+ border-radius: var(--npt-corner-xs, 8px);
168
+ background: var(--md-sys-color-inverse-surface);
169
+ color: var(--md-sys-color-inverse-on-surface);
170
+ font-family: var(--npt-font-text);
171
+ font-size: var(--npt-text-caption, 12px);
172
+ opacity: 0;
173
+ pointer-events: none;
174
+ transition: opacity var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
175
+ z-index: var(--npt-z-toast, 80);
176
+ }
177
+ :host(:hover) .tip,
178
+ :host(:focus-within) .tip {
179
+ opacity: 1;
180
+ }
181
+ `;
182
+ }
183
+ render() {
184
+ const label = esc(this.getAttribute("label"));
185
+ return html `<slot></slot>
186
+ <span class="tip" part="tip" role="tooltip">${label}</span>`;
187
+ }
188
+ }
189
+ NptTooltip.observedAttributes = ["label"];
190
+ /**
191
+ * <npt-banner text="Update available">…actions slot…</npt-banner>
192
+ * Surface-level inline message with an actions slot.
193
+ */
194
+ export class NptBanner extends NptElement {
195
+ attributeChangedCallback() {
196
+ if (this.isConnected)
197
+ this.update();
198
+ }
199
+ styles() {
200
+ return css `
201
+ ${A11Y}
202
+ :host {
203
+ display: block;
204
+ }
205
+ .banner {
206
+ display: flex;
207
+ align-items: center;
208
+ flex-wrap: wrap;
209
+ gap: var(--npt-space-4, 16px);
210
+ padding-inline: var(--npt-space-4, 16px);
211
+ padding-block: var(--npt-space-3, 12px);
212
+ background: var(--md-sys-color-surface-container);
213
+ color: var(--md-sys-color-on-surface);
214
+ border-bottom: 1px solid var(--md-sys-color-outline-variant);
215
+ font-family: var(--npt-font-text);
216
+ font-size: var(--npt-text-body, 14px);
217
+ }
218
+ .leading {
219
+ display: inline-flex;
220
+ }
221
+ .text {
222
+ flex: 1 1 200px;
223
+ }
224
+ .actions {
225
+ display: inline-flex;
226
+ gap: var(--npt-space-2, 8px);
227
+ margin-inline-start: auto;
228
+ }
229
+ `;
230
+ }
231
+ render() {
232
+ const text = esc(this.getAttribute("text"));
233
+ return html `<div class="banner" part="banner" role="status">
234
+ <span class="leading"><slot name="leading"></slot></span>
235
+ <span class="text">${text}</span>
236
+ <span class="actions"><slot></slot></span>
237
+ </div>`;
238
+ }
239
+ }
240
+ NptBanner.observedAttributes = ["text"];
241
+ //# sourceMappingURL=feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/components/feedback.ts"],"names":[],"mappings":"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"}
@@ -10,10 +10,17 @@ export declare class NptTextField extends NptElement {
10
10
  protected styles(): string;
11
11
  protected render(): string;
12
12
  }
13
- /** <npt-chip [selected]>Label</npt-chip> */
13
+ /**
14
+ * <npt-chip variant="assist|filter|input|suggestion" [selected]>Label</npt-chip>
15
+ * `filter` shows a leading ✓ when [selected]; `input` shows a removable ✕ that
16
+ * dispatches a `remove` event. Defaults to the assist/filter treatment.
17
+ */
14
18
  export declare class NptChip extends NptElement {
15
19
  static observedAttributes: string[];
16
20
  attributeChangedCallback(): void;
21
+ connectedCallback(): void;
22
+ disconnectedCallback(): void;
23
+ private onClick;
17
24
  protected styles(): string;
18
25
  protected render(): string;
19
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/components/inputs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAmB,MAAM,WAAW,CAAC;AAKxD;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,MAAM,CAAC,kBAAkB,WAAsD;IAE/E,wBAAwB,IAAI,IAAI;IAIhC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,SAAS,CAAC,MAAM,IAAI,MAAM;IAgD1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAkB3B;AAED,4CAA4C;AAC5C,qBAAa,OAAQ,SAAQ,UAAU;IACrC,MAAM,CAAC,kBAAkB,WAAgB;IAEzC,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IA2B1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAI3B;AAED,oEAAoE;AACpE,qBAAa,QAAS,SAAQ,UAAU;IACtC,MAAM,CAAC,kBAAkB,WAAY;IAErC,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAmC1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAG3B"}
1
+ {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/components/inputs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAmB,MAAM,WAAW,CAAC;AAKxD;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,MAAM,CAAC,kBAAkB,WAAsD;IAE/E,wBAAwB,IAAI,IAAI;IAIhC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,SAAS,CAAC,MAAM,IAAI,MAAM;IAgD1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAkB3B;AAED;;;;GAIG;AACH,qBAAa,OAAQ,SAAQ,UAAU;IACrC,MAAM,CAAC,kBAAkB,WAAuC;IAEhE,wBAAwB,IAAI,IAAI;IAIvB,iBAAiB,IAAI,IAAI;IAKlC,oBAAoB,IAAI,IAAI;IAI5B,OAAO,CAAC,OAAO,CAKb;IAEF,SAAS,CAAC,MAAM,IAAI,MAAM;IAqD1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAY3B;AAED,oEAAoE;AACpE,qBAAa,QAAS,SAAQ,UAAU;IACtC,MAAM,CAAC,kBAAkB,WAAY;IAErC,wBAAwB,IAAI,IAAI;IAIhC,SAAS,CAAC,MAAM,IAAI,MAAM;IAmC1B,SAAS,CAAC,MAAM,IAAI,MAAM;CAG3B"}