@incursa/ui-kit 1.0.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/AI-AGENT-INSTRUCTIONS.md +49 -28
  2. package/LLMS.txt +58 -42
  3. package/README.md +181 -68
  4. package/dist/inc-design-language.css +655 -251
  5. package/dist/inc-design-language.css.map +1 -1
  6. package/dist/inc-design-language.js +520 -0
  7. package/dist/inc-design-language.min.css +1 -1
  8. package/dist/inc-design-language.min.css.map +1 -1
  9. package/dist/web-components/README.md +92 -0
  10. package/dist/web-components/RUNTIME-NOTES.md +40 -0
  11. package/dist/web-components/base-element.js +193 -0
  12. package/dist/web-components/components/feedback.js +1074 -0
  13. package/dist/web-components/components/forms.js +979 -0
  14. package/dist/web-components/components/layout.js +408 -0
  15. package/dist/web-components/components/navigation.js +854 -0
  16. package/dist/web-components/components/overlays.js +634 -0
  17. package/dist/web-components/controllers/focus.js +101 -0
  18. package/dist/web-components/controllers/overlay.js +128 -0
  19. package/dist/web-components/controllers/selection.js +145 -0
  20. package/dist/web-components/controllers/theme.js +173 -0
  21. package/dist/web-components/index.js +887 -0
  22. package/dist/web-components/package.json +3 -0
  23. package/dist/web-components/registry.js +74 -0
  24. package/dist/web-components/shared.js +186 -0
  25. package/dist/web-components/style.css +6 -0
  26. package/package.json +11 -2
  27. package/src/inc-design-language.js +520 -0
  28. package/src/inc-design-language.scss +692 -258
  29. package/src/web-components/README.md +92 -0
  30. package/src/web-components/RUNTIME-NOTES.md +40 -0
  31. package/src/web-components/base-element.js +193 -0
  32. package/src/web-components/components/feedback.js +1074 -0
  33. package/src/web-components/components/forms.js +979 -0
  34. package/src/web-components/components/layout.js +408 -0
  35. package/src/web-components/components/navigation.js +854 -0
  36. package/src/web-components/components/overlays.js +634 -0
  37. package/src/web-components/controllers/focus.js +101 -0
  38. package/src/web-components/controllers/overlay.js +128 -0
  39. package/src/web-components/controllers/selection.js +145 -0
  40. package/src/web-components/controllers/theme.js +173 -0
  41. package/src/web-components/index.js +887 -0
  42. package/src/web-components/package.json +3 -0
  43. package/src/web-components/registry.js +74 -0
  44. package/src/web-components/shared.js +186 -0
  45. package/src/web-components/style.css +6 -0
@@ -0,0 +1,634 @@
1
+ const INTERNAL_NODE = Symbol("inc-internal-overlay-node");
2
+
3
+ const HostElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
4
+
5
+ const DETAILS_SELECTOR = "details.inc-disclosure";
6
+ const DIALOG_SELECTOR = "dialog.inc-native-dialog";
7
+
8
+ function toBooleanAttribute(value, fallback = false) {
9
+ if (value === null || value === undefined) {
10
+ return fallback;
11
+ }
12
+
13
+ if (value === "" || value === true) {
14
+ return true;
15
+ }
16
+
17
+ if (value === false) {
18
+ return false;
19
+ }
20
+
21
+ const normalized = String(value).trim().toLowerCase();
22
+ return !(normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off");
23
+ }
24
+
25
+ function setBooleanAttribute(target, name, value) {
26
+ if (value) {
27
+ target.setAttribute(name, "");
28
+ return;
29
+ }
30
+
31
+ target.removeAttribute(name);
32
+ }
33
+
34
+ function emit(host, name, detail = {}) {
35
+ host.dispatchEvent(new CustomEvent(name, {
36
+ bubbles: true,
37
+ composed: true,
38
+ detail,
39
+ }));
40
+ }
41
+
42
+ function appendProjectedChildren(host, destinationMap, fallbackDestination, ignoredNodes = new Set()) {
43
+ const nodes = Array.from(host.childNodes);
44
+
45
+ for (const node of nodes) {
46
+ if (ignoredNodes.has(node)) {
47
+ continue;
48
+ }
49
+
50
+ if (node.nodeType === Node.TEXT_NODE && !node.textContent?.trim()) {
51
+ continue;
52
+ }
53
+
54
+ if (!(node instanceof HTMLElement)) {
55
+ fallbackDestination.append(node);
56
+ continue;
57
+ }
58
+
59
+ const slotName = (node.getAttribute("slot") || "").trim();
60
+ const destination = destinationMap.get(slotName) || fallbackDestination;
61
+ destination.append(node);
62
+ }
63
+ }
64
+
65
+ function getFocusableElements(container) {
66
+ const selector = [
67
+ "a[href]",
68
+ "button:not([disabled])",
69
+ "input:not([disabled]):not([type='hidden'])",
70
+ "select:not([disabled])",
71
+ "textarea:not([disabled])",
72
+ "[tabindex]:not([tabindex='-1'])",
73
+ ].join(",");
74
+
75
+ return Array.from(container.querySelectorAll(selector)).filter((element) => {
76
+ if (!(element instanceof HTMLElement)) {
77
+ return false;
78
+ }
79
+
80
+ if (element.hasAttribute("hidden") || element.getAttribute("aria-hidden") === "true") {
81
+ return false;
82
+ }
83
+
84
+ return element.offsetParent !== null || element === document.activeElement;
85
+ });
86
+ }
87
+
88
+ class IncDisclosureElement extends HostElement {
89
+ static get observedAttributes() {
90
+ return ["open", "summary", "toggleable"];
91
+ }
92
+
93
+ constructor() {
94
+ super();
95
+ this._isSyncing = false;
96
+ this._observer = null;
97
+ this._details = null;
98
+ this._summaryTitle = null;
99
+ this._content = null;
100
+
101
+ this._onToggle = this._onToggle.bind(this);
102
+ }
103
+
104
+ connectedCallback() {
105
+ this._ensureStructure();
106
+ this._syncFromAttributes();
107
+ this._projectChildren();
108
+
109
+ this._observer = new MutationObserver((mutations) => {
110
+ if (this._isSyncing) {
111
+ return;
112
+ }
113
+
114
+ const shouldProject = mutations.some((mutation) => {
115
+ if (mutation.type !== "childList") {
116
+ return false;
117
+ }
118
+
119
+ const changedNodes = [...mutation.addedNodes, ...mutation.removedNodes];
120
+ return changedNodes.some((node) => node !== this._details);
121
+ });
122
+
123
+ if (shouldProject) {
124
+ this._projectChildren();
125
+ }
126
+ });
127
+
128
+ this._observer.observe(this, { childList: true });
129
+ }
130
+
131
+ disconnectedCallback() {
132
+ this._observer?.disconnect();
133
+ this._observer = null;
134
+ this._details?.removeEventListener("toggle", this._onToggle);
135
+ }
136
+
137
+ attributeChangedCallback() {
138
+ this._syncFromAttributes();
139
+ }
140
+
141
+ open() {
142
+ this.setAttribute("open", "");
143
+ }
144
+
145
+ close() {
146
+ this.removeAttribute("open");
147
+ }
148
+
149
+ toggle(force) {
150
+ if (typeof force === "boolean") {
151
+ setBooleanAttribute(this, "open", force);
152
+ return;
153
+ }
154
+
155
+ setBooleanAttribute(this, "open", !this.hasAttribute("open"));
156
+ }
157
+
158
+ _ensureStructure() {
159
+ if (this._details?.isConnected) {
160
+ return;
161
+ }
162
+
163
+ const details = document.createElement("details");
164
+ const summary = document.createElement("summary");
165
+ const summaryTitle = document.createElement("span");
166
+ const content = document.createElement("div");
167
+
168
+ details.className = "inc-disclosure";
169
+ details.setAttribute("part", "surface");
170
+ details[INTERNAL_NODE] = true;
171
+
172
+ summary.className = "inc-disclosure__summary";
173
+ summary.setAttribute("part", "summary");
174
+
175
+ summaryTitle.className = "inc-disclosure__title";
176
+ summaryTitle.setAttribute("part", "title");
177
+ summary.append(summaryTitle);
178
+
179
+ content.className = "inc-disclosure__content";
180
+ content.setAttribute("part", "content");
181
+
182
+ details.append(summary, content);
183
+ this.append(details);
184
+
185
+ this._details = details;
186
+ this._summaryTitle = summaryTitle;
187
+ this._content = content;
188
+
189
+ details.addEventListener("toggle", this._onToggle);
190
+ }
191
+
192
+ _syncFromAttributes() {
193
+ if (!this._details) {
194
+ return;
195
+ }
196
+
197
+ const summaryText = this.getAttribute("summary");
198
+ if (summaryText) {
199
+ this._summaryTitle.textContent = summaryText;
200
+ } else if (!this._summaryTitle.querySelector(":scope > *")) {
201
+ this._summaryTitle.textContent = "";
202
+ }
203
+
204
+ const open = this.hasAttribute("open");
205
+ this._details.open = open;
206
+
207
+ const toggleable = toBooleanAttribute(this.getAttribute("toggleable"), true);
208
+ this._details.dataset.incToggleable = toggleable ? "true" : "false";
209
+ }
210
+
211
+ _projectChildren() {
212
+ if (!this._details || !this._summaryTitle || !this._content) {
213
+ return;
214
+ }
215
+
216
+ this._isSyncing = true;
217
+
218
+ this._summaryTitle.replaceChildren();
219
+ this._content.replaceChildren();
220
+
221
+ const destinations = new Map([
222
+ ["summary", this._summaryTitle],
223
+ ["content", this._content],
224
+ ["default", this._content],
225
+ ]);
226
+
227
+ appendProjectedChildren(this, destinations, this._content, new Set([this._details]));
228
+
229
+ this._isSyncing = false;
230
+ }
231
+
232
+ _onToggle() {
233
+ const open = this._details?.open === true;
234
+ setBooleanAttribute(this, "open", open);
235
+
236
+ emit(this, "toggle", { open });
237
+ emit(this, open ? "open" : "close", { open });
238
+ }
239
+ }
240
+
241
+ class IncDialogBaseElement extends HostElement {
242
+ static get observedAttributes() {
243
+ return ["open", "modal", "dismissible", "size", "label", "placement"];
244
+ }
245
+
246
+ constructor() {
247
+ super();
248
+ this._dialog = null;
249
+ this._surface = null;
250
+ this._header = null;
251
+ this._title = null;
252
+ this._body = null;
253
+ this._footer = null;
254
+ this._closeButton = null;
255
+ this._observer = null;
256
+ this._syncing = false;
257
+ this._lastTrigger = null;
258
+ this._tagType = "dialog";
259
+
260
+ this._onDialogClose = this._onDialogClose.bind(this);
261
+ this._onDialogCancel = this._onDialogCancel.bind(this);
262
+ this._onDialogPointerDown = this._onDialogPointerDown.bind(this);
263
+ }
264
+
265
+ connectedCallback() {
266
+ this._ensureStructure();
267
+ this._syncFromAttributes();
268
+ this._projectChildren();
269
+
270
+ this._observer = new MutationObserver((mutations) => {
271
+ if (this._syncing) {
272
+ return;
273
+ }
274
+
275
+ const shouldProject = mutations.some((mutation) => {
276
+ const changedNodes = [...mutation.addedNodes, ...mutation.removedNodes];
277
+ return changedNodes.some((node) => node !== this._dialog);
278
+ });
279
+
280
+ if (shouldProject) {
281
+ this._projectChildren();
282
+ }
283
+ });
284
+
285
+ this._observer.observe(this, { childList: true });
286
+ }
287
+
288
+ disconnectedCallback() {
289
+ this._observer?.disconnect();
290
+ this._observer = null;
291
+
292
+ if (this._dialog) {
293
+ this._dialog.removeEventListener("close", this._onDialogClose);
294
+ this._dialog.removeEventListener("cancel", this._onDialogCancel);
295
+ this._dialog.removeEventListener("click", this._onDialogPointerDown);
296
+ }
297
+ }
298
+
299
+ attributeChangedCallback() {
300
+ this._syncFromAttributes();
301
+ }
302
+
303
+ get open() {
304
+ return this.hasAttribute("open");
305
+ }
306
+
307
+ set open(value) {
308
+ setBooleanAttribute(this, "open", Boolean(value));
309
+ }
310
+
311
+ get modal() {
312
+ return toBooleanAttribute(this.getAttribute("modal"), true);
313
+ }
314
+
315
+ set modal(value) {
316
+ setBooleanAttribute(this, "modal", Boolean(value));
317
+ }
318
+
319
+ get dismissible() {
320
+ return toBooleanAttribute(this.getAttribute("dismissible"), true);
321
+ }
322
+
323
+ set dismissible(value) {
324
+ setBooleanAttribute(this, "dismissible", Boolean(value));
325
+ }
326
+
327
+ show() {
328
+ this._rememberTrigger();
329
+ this.setAttribute("open", "");
330
+ this._openDialog(false);
331
+ }
332
+
333
+ showModal() {
334
+ this._rememberTrigger();
335
+ this.setAttribute("open", "");
336
+ this._openDialog(true);
337
+ }
338
+
339
+ close(returnValue = "") {
340
+ if (!this._dialog) {
341
+ return;
342
+ }
343
+
344
+ if (this._dialog.open && typeof this._dialog.close === "function") {
345
+ this._dialog.close(returnValue);
346
+ } else {
347
+ this.removeAttribute("open");
348
+ }
349
+ }
350
+
351
+ dismiss(reason = "dismiss") {
352
+ if (!this.dismissible) {
353
+ return;
354
+ }
355
+
356
+ this.close(reason);
357
+ emit(this, "dismiss", { reason });
358
+ }
359
+
360
+ _ensureStructure() {
361
+ if (this._dialog?.isConnected) {
362
+ return;
363
+ }
364
+
365
+ const dialog = document.createElement("dialog");
366
+ const surface = document.createElement("div");
367
+ const header = document.createElement("div");
368
+ const title = document.createElement("div");
369
+ const body = document.createElement("div");
370
+ const footer = document.createElement("div");
371
+ const closeButton = document.createElement("button");
372
+
373
+ dialog.className = "inc-native-dialog";
374
+ dialog.setAttribute("part", "backdrop");
375
+ dialog[INTERNAL_NODE] = true;
376
+
377
+ surface.className = "inc-native-dialog__surface";
378
+ surface.setAttribute("part", "surface");
379
+
380
+ header.className = "inc-native-dialog__header";
381
+ header.setAttribute("part", "header");
382
+
383
+ title.className = "inc-native-dialog__title";
384
+ title.setAttribute("part", "title");
385
+
386
+ closeButton.type = "button";
387
+ closeButton.className = "inc-native-dialog__close";
388
+ closeButton.setAttribute("part", "close");
389
+ closeButton.setAttribute("aria-label", "Close");
390
+ closeButton.textContent = "x";
391
+ closeButton.addEventListener("click", () => this.dismiss("close-button"));
392
+
393
+ header.append(title, closeButton);
394
+
395
+ body.className = "inc-native-dialog__body";
396
+ body.setAttribute("part", "body");
397
+
398
+ footer.className = "inc-native-dialog__footer";
399
+ footer.setAttribute("part", "footer");
400
+
401
+ surface.append(header, body, footer);
402
+ dialog.append(surface);
403
+ this.append(dialog);
404
+
405
+ this._dialog = dialog;
406
+ this._surface = surface;
407
+ this._header = header;
408
+ this._title = title;
409
+ this._body = body;
410
+ this._footer = footer;
411
+ this._closeButton = closeButton;
412
+
413
+ dialog.addEventListener("close", this._onDialogClose);
414
+ dialog.addEventListener("cancel", this._onDialogCancel);
415
+ dialog.addEventListener("click", this._onDialogPointerDown);
416
+ }
417
+
418
+ _projectChildren() {
419
+ if (!this._title || !this._body || !this._footer || !this._header) {
420
+ return;
421
+ }
422
+
423
+ this._syncing = true;
424
+
425
+ this._title.replaceChildren();
426
+ this._body.replaceChildren();
427
+ this._footer.replaceChildren();
428
+
429
+ const destinations = new Map([
430
+ ["title", this._title],
431
+ ["header", this._header],
432
+ ["body", this._body],
433
+ ["footer", this._footer],
434
+ ["default", this._body],
435
+ ]);
436
+
437
+ appendProjectedChildren(this, destinations, this._body, new Set([this._dialog]));
438
+
439
+ if (!this._header.contains(this._closeButton)) {
440
+ this._header.append(this._closeButton);
441
+ }
442
+
443
+ if (!this._title.textContent?.trim()) {
444
+ const label = this.getAttribute("label");
445
+ if (label) {
446
+ this._title.textContent = label;
447
+ }
448
+ }
449
+
450
+ this._syncing = false;
451
+ }
452
+
453
+ _syncFromAttributes() {
454
+ if (!this._dialog) {
455
+ return;
456
+ }
457
+
458
+ const open = this.hasAttribute("open");
459
+ const dismissible = this.dismissible;
460
+
461
+ this._dialog.dataset.incDismissible = dismissible ? "true" : "false";
462
+ this._closeButton.hidden = !dismissible;
463
+
464
+ const size = this.getAttribute("size");
465
+ if (size) {
466
+ this._dialog.dataset.incSize = size;
467
+ } else {
468
+ delete this._dialog.dataset.incSize;
469
+ }
470
+
471
+ const label = this.getAttribute("label");
472
+ if (label) {
473
+ this._dialog.setAttribute("aria-label", label);
474
+ } else {
475
+ this._dialog.removeAttribute("aria-label");
476
+ }
477
+
478
+ const placement = this.getAttribute("placement");
479
+ if (placement) {
480
+ this._dialog.dataset.incPlacement = placement;
481
+ } else {
482
+ delete this._dialog.dataset.incPlacement;
483
+ }
484
+
485
+ if (this._tagType === "drawer") {
486
+ this._dialog.classList.add("inc-native-dialog--drawer");
487
+ } else {
488
+ this._dialog.classList.remove("inc-native-dialog--drawer");
489
+ }
490
+
491
+ if (open && !this._dialog.open) {
492
+ this._openDialog(this.modal);
493
+ }
494
+
495
+ if (!open && this._dialog.open) {
496
+ this._dialog.close();
497
+ }
498
+ }
499
+
500
+ _openDialog(asModal) {
501
+ if (!this._dialog || this._dialog.open) {
502
+ return;
503
+ }
504
+
505
+ try {
506
+ if (asModal && typeof this._dialog.showModal === "function") {
507
+ this._dialog.showModal();
508
+ } else if (typeof this._dialog.show === "function") {
509
+ this._dialog.show();
510
+ } else {
511
+ this._dialog.setAttribute("open", "");
512
+ }
513
+ } catch {
514
+ this._dialog.setAttribute("open", "");
515
+ }
516
+
517
+ const focusInitial = () => this._focusInitial();
518
+ if (typeof window.requestAnimationFrame === "function") {
519
+ window.requestAnimationFrame(focusInitial);
520
+ } else {
521
+ window.setTimeout(focusInitial, 0);
522
+ }
523
+ emit(this, "open", { modal: asModal });
524
+ }
525
+
526
+ _focusInitial() {
527
+ if (!this._dialog) {
528
+ return;
529
+ }
530
+
531
+ const explicit = this._dialog.querySelector("[data-inc-initial-focus]");
532
+ if (explicit instanceof HTMLElement) {
533
+ explicit.focus({ preventScroll: true });
534
+ return;
535
+ }
536
+
537
+ const focusables = getFocusableElements(this._dialog);
538
+ if (focusables[0]) {
539
+ focusables[0].focus({ preventScroll: true });
540
+ }
541
+ }
542
+
543
+ _rememberTrigger() {
544
+ const active = document.activeElement;
545
+ this._lastTrigger = active instanceof HTMLElement ? active : null;
546
+ }
547
+
548
+ _restoreFocus() {
549
+ if (!this._lastTrigger || !this._lastTrigger.isConnected) {
550
+ return;
551
+ }
552
+
553
+ this._lastTrigger.focus({ preventScroll: true });
554
+ }
555
+
556
+ _onDialogPointerDown(event) {
557
+ if (!this.dismissible || !this._dialog) {
558
+ return;
559
+ }
560
+
561
+ if (event.target === this._dialog) {
562
+ this.dismiss("backdrop");
563
+ }
564
+ }
565
+
566
+ _onDialogCancel(event) {
567
+ if (!this.dismissible) {
568
+ event.preventDefault();
569
+ return;
570
+ }
571
+
572
+ emit(this, "cancel", { reason: "escape" });
573
+ }
574
+
575
+ _onDialogClose() {
576
+ setBooleanAttribute(this, "open", this._dialog?.open === true);
577
+ emit(this, "close", { returnValue: this._dialog?.returnValue || "" });
578
+ this._restoreFocus();
579
+ }
580
+ }
581
+
582
+ class IncDialogElement extends IncDialogBaseElement {
583
+ constructor() {
584
+ super();
585
+ this._tagType = "dialog";
586
+ }
587
+ }
588
+
589
+ class IncDrawerElement extends IncDialogBaseElement {
590
+ constructor() {
591
+ super();
592
+ this._tagType = "drawer";
593
+ }
594
+
595
+ show() {
596
+ this._rememberTrigger();
597
+ this.setAttribute("open", "");
598
+ this._openDialog(this.modal);
599
+ }
600
+ }
601
+
602
+ function defineOverlayComponents(registry = globalThis.customElements) {
603
+ if (!registry) {
604
+ return;
605
+ }
606
+
607
+ if (!registry.get("inc-disclosure")) {
608
+ registry.define("inc-disclosure", IncDisclosureElement);
609
+ }
610
+
611
+ if (!registry.get("inc-dialog")) {
612
+ registry.define("inc-dialog", IncDialogElement);
613
+ }
614
+
615
+ if (!registry.get("inc-drawer")) {
616
+ registry.define("inc-drawer", IncDrawerElement);
617
+ }
618
+ }
619
+
620
+ const overlayComponentsApi = {
621
+ defineOverlayComponents,
622
+ IncDisclosureElement,
623
+ IncDialogElement,
624
+ IncDrawerElement,
625
+ };
626
+
627
+ if (typeof module !== "undefined" && module.exports) {
628
+ module.exports = overlayComponentsApi;
629
+ }
630
+
631
+ if (typeof globalThis !== "undefined") {
632
+ globalThis.IncWebComponents = globalThis.IncWebComponents || {};
633
+ globalThis.IncWebComponents.overlays = overlayComponentsApi;
634
+ }