@jungherz-de/notionkit-elements 1.0.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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/dist/components/nk-ai-input-row.js +57 -0
  4. package/dist/components/nk-ai-msg.js +67 -0
  5. package/dist/components/nk-ai-thread.js +14 -0
  6. package/dist/components/nk-app.js +23 -0
  7. package/dist/components/nk-avatar-group.js +35 -0
  8. package/dist/components/nk-banner.js +30 -0
  9. package/dist/components/nk-block-host.js +35 -0
  10. package/dist/components/nk-board-view.js +129 -0
  11. package/dist/components/nk-breadcrumb.js +78 -0
  12. package/dist/components/nk-btn.js +107 -0
  13. package/dist/components/nk-callout.js +36 -0
  14. package/dist/components/nk-check.js +77 -0
  15. package/dist/components/nk-cmdk.js +221 -0
  16. package/dist/components/nk-code.js +66 -0
  17. package/dist/components/nk-comment.js +41 -0
  18. package/dist/components/nk-comments.js +57 -0
  19. package/dist/components/nk-danger-zone.js +27 -0
  20. package/dist/components/nk-database.js +100 -0
  21. package/dist/components/nk-divider.js +12 -0
  22. package/dist/components/nk-emoji-picker.js +112 -0
  23. package/dist/components/nk-empty.js +36 -0
  24. package/dist/components/nk-field.js +50 -0
  25. package/dist/components/nk-filter-bar.js +78 -0
  26. package/dist/components/nk-heading.js +29 -0
  27. package/dist/components/nk-input.js +108 -0
  28. package/dist/components/nk-kbd.js +15 -0
  29. package/dist/components/nk-member-list.js +30 -0
  30. package/dist/components/nk-member-row.js +46 -0
  31. package/dist/components/nk-mention.js +31 -0
  32. package/dist/components/nk-menu-item.js +68 -0
  33. package/dist/components/nk-menu.js +45 -0
  34. package/dist/components/nk-modal.js +166 -0
  35. package/dist/components/nk-model-card.js +89 -0
  36. package/dist/components/nk-page-actions.js +15 -0
  37. package/dist/components/nk-page-cover.js +35 -0
  38. package/dist/components/nk-page-title.js +71 -0
  39. package/dist/components/nk-page.js +71 -0
  40. package/dist/components/nk-pop.js +90 -0
  41. package/dist/components/nk-profile-row.js +25 -0
  42. package/dist/components/nk-progress.js +43 -0
  43. package/dist/components/nk-quote.js +32 -0
  44. package/dist/components/nk-radio.js +160 -0
  45. package/dist/components/nk-section-label.js +42 -0
  46. package/dist/components/nk-segmented.js +99 -0
  47. package/dist/components/nk-select.js +127 -0
  48. package/dist/components/nk-settings-pane.js +40 -0
  49. package/dist/components/nk-settings-user.js +36 -0
  50. package/dist/components/nk-sidebar.js +81 -0
  51. package/dist/components/nk-skeleton.js +31 -0
  52. package/dist/components/nk-slider.js +85 -0
  53. package/dist/components/nk-stat.js +40 -0
  54. package/dist/components/nk-stats.js +14 -0
  55. package/dist/components/nk-switch.js +69 -0
  56. package/dist/components/nk-synced.js +26 -0
  57. package/dist/components/nk-tab.js +54 -0
  58. package/dist/components/nk-table-view.js +109 -0
  59. package/dist/components/nk-tabs.js +82 -0
  60. package/dist/components/nk-tag.js +32 -0
  61. package/dist/components/nk-template-btn.js +43 -0
  62. package/dist/components/nk-textarea.js +95 -0
  63. package/dist/components/nk-theme-toggle.js +67 -0
  64. package/dist/components/nk-toast.js +51 -0
  65. package/dist/components/nk-todo.js +73 -0
  66. package/dist/components/nk-toggle.js +56 -0
  67. package/dist/components/nk-topbar.js +24 -0
  68. package/dist/components/nk-tree-item.js +155 -0
  69. package/dist/components/nk-tree.js +123 -0
  70. package/dist/components/nk-workspace-switcher.js +82 -0
  71. package/dist/components/shared/base-6gEcoatG.js +317 -0
  72. package/dist/components/shared/focus-DXrK9oLe.js +47 -0
  73. package/dist/components/shared/property-cell-Dn91uFkf.js +92 -0
  74. package/dist/notionkit-elements.esm.js +4639 -0
  75. package/dist/notionkit-elements.js +4718 -0
  76. package/dist/notionkit-elements.min.js +1 -0
  77. package/package.json +72 -0
  78. package/src/base.js +296 -0
  79. package/src/components/content/nk-callout.js +35 -0
  80. package/src/components/content/nk-code.js +51 -0
  81. package/src/components/content/nk-divider.js +11 -0
  82. package/src/components/content/nk-heading.js +28 -0
  83. package/src/components/content/nk-kbd.js +14 -0
  84. package/src/components/content/nk-progress.js +42 -0
  85. package/src/components/content/nk-quote.js +31 -0
  86. package/src/components/content/nk-tag.js +31 -0
  87. package/src/components/content/nk-todo.js +72 -0
  88. package/src/components/content/nk-toggle.js +55 -0
  89. package/src/components/data/nk-ai-input-row.js +56 -0
  90. package/src/components/data/nk-ai-msg.js +66 -0
  91. package/src/components/data/nk-ai-thread.js +13 -0
  92. package/src/components/data/nk-board-view.js +128 -0
  93. package/src/components/data/nk-comment.js +40 -0
  94. package/src/components/data/nk-comments.js +56 -0
  95. package/src/components/data/nk-database.js +99 -0
  96. package/src/components/data/nk-filter-bar.js +77 -0
  97. package/src/components/data/nk-table-view.js +108 -0
  98. package/src/components/forms/nk-btn.js +106 -0
  99. package/src/components/forms/nk-check.js +76 -0
  100. package/src/components/forms/nk-field.js +49 -0
  101. package/src/components/forms/nk-input.js +107 -0
  102. package/src/components/forms/nk-radio.js +159 -0
  103. package/src/components/forms/nk-select.js +126 -0
  104. package/src/components/forms/nk-slider.js +84 -0
  105. package/src/components/forms/nk-switch.js +68 -0
  106. package/src/components/forms/nk-textarea.js +94 -0
  107. package/src/components/overlays/nk-cmdk.js +201 -0
  108. package/src/components/overlays/nk-emoji-picker.js +94 -0
  109. package/src/components/overlays/nk-menu-item.js +67 -0
  110. package/src/components/overlays/nk-menu.js +44 -0
  111. package/src/components/overlays/nk-modal.js +165 -0
  112. package/src/components/overlays/nk-pop.js +89 -0
  113. package/src/components/overlays/nk-settings-pane.js +39 -0
  114. package/src/components/overlays/nk-settings-user.js +35 -0
  115. package/src/components/overlays/nk-toast.js +50 -0
  116. package/src/components/page/nk-avatar-group.js +34 -0
  117. package/src/components/page/nk-banner.js +29 -0
  118. package/src/components/page/nk-block-host.js +34 -0
  119. package/src/components/page/nk-danger-zone.js +26 -0
  120. package/src/components/page/nk-empty.js +35 -0
  121. package/src/components/page/nk-member-list.js +29 -0
  122. package/src/components/page/nk-member-row.js +45 -0
  123. package/src/components/page/nk-mention.js +30 -0
  124. package/src/components/page/nk-model-card.js +88 -0
  125. package/src/components/page/nk-page-actions.js +14 -0
  126. package/src/components/page/nk-page-cover.js +34 -0
  127. package/src/components/page/nk-page-title.js +70 -0
  128. package/src/components/page/nk-page.js +70 -0
  129. package/src/components/page/nk-profile-row.js +24 -0
  130. package/src/components/page/nk-segmented.js +98 -0
  131. package/src/components/page/nk-skeleton.js +30 -0
  132. package/src/components/page/nk-stat.js +39 -0
  133. package/src/components/page/nk-stats.js +13 -0
  134. package/src/components/page/nk-synced.js +25 -0
  135. package/src/components/page/nk-tab.js +53 -0
  136. package/src/components/page/nk-tabs.js +81 -0
  137. package/src/components/page/nk-template-btn.js +42 -0
  138. package/src/components/shell/nk-app.js +22 -0
  139. package/src/components/shell/nk-breadcrumb.js +77 -0
  140. package/src/components/shell/nk-section-label.js +41 -0
  141. package/src/components/shell/nk-sidebar.js +80 -0
  142. package/src/components/shell/nk-theme-toggle.js +66 -0
  143. package/src/components/shell/nk-topbar.js +23 -0
  144. package/src/components/shell/nk-tree-item.js +154 -0
  145. package/src/components/shell/nk-tree.js +122 -0
  146. package/src/components/shell/nk-workspace-switcher.js +81 -0
  147. package/src/index.js +88 -0
  148. package/src/util/emoji.js +17 -0
  149. package/src/util/focus.js +45 -0
  150. package/src/util/fuzzy.js +19 -0
  151. package/src/util/highlight.js +14 -0
  152. package/src/util/property-cell.js +90 -0
@@ -0,0 +1,31 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-mention type="person"><span slot="avatar" class="mini-avatar" style="background:#448361">SL</span>Sara Lindt</nk-mention>
4
+ // <nk-mention type="page">📄 Onboarding</nk-mention> <nk-mention type="date">📅 May 20</nk-mention>
5
+ // → <span class="nk-mention person">…</span>
6
+ const TYPES = ['person', 'page', 'date'];
7
+
8
+ class NkMention extends NkElement {
9
+ static get observedAttributes() { return ['type']; }
10
+
11
+ render() {
12
+ this._box = this.createElement('span', this._classes());
13
+ this._box.appendChild(this.createElement('slot', [], { name: 'avatar' }));
14
+ this._box.appendChild(document.createElement('slot'));
15
+ this._wrapper.appendChild(this._box);
16
+ }
17
+
18
+ _classes() {
19
+ const t = this.getAttribute('type');
20
+ return ['nk-mention', ...(TYPES.includes(t) ? [t] : [])];
21
+ }
22
+
23
+ onAttributeChanged() { if (this._box) this._box.className = this._classes().join(' '); }
24
+
25
+ get type() { return this.getAttribute('type'); }
26
+ set type(v) { v ? this.setAttribute('type', v) : this.removeAttribute('type'); }
27
+ }
28
+
29
+ customElements.define('nk-mention', NkMention);
30
+
31
+ export { NkMention };
@@ -0,0 +1,68 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-menu-item icon="✏️" shortcut="⌘E" value="rename">Rename</nk-menu-item>
4
+ // <nk-menu-item type="separator"></nk-menu-item> <nk-menu-item type="label">Danger</nk-menu-item>
5
+ // <nk-menu-item icon="🗑️" danger value="delete">Delete</nk-menu-item>
6
+ // → <div class="nk-menu-item danger"><span class="m-icon">🗑️</span>Delete<span class="m-shortcut">…</span></div>
7
+ class NkMenuItem extends NkElement {
8
+ static get observedAttributes() { return ['icon', 'shortcut', 'danger', 'value', 'type', 'disabled']; }
9
+
10
+ render() { this._build(); }
11
+
12
+ _build() {
13
+ const type = this.getAttribute('type');
14
+ let el;
15
+ if (type === 'separator') {
16
+ el = this.createElement('div', ['nk-menu-sep'], { role: 'separator' });
17
+ } else if (type === 'label') {
18
+ el = this.createElement('div', ['nk-menu-label']);
19
+ el.appendChild(document.createElement('slot'));
20
+ } else {
21
+ el = this.createElement('div', ['nk-menu-item'], { role: 'menuitem', tabindex: '-1' });
22
+ this._icon = this.createElement('span', ['m-icon']);
23
+ const iconSlot = this.createElement('slot', [], { name: 'icon' });
24
+ iconSlot.appendChild(this._icon);
25
+ this._shortcut = this.createElement('span', ['m-shortcut']);
26
+ el.append(iconSlot, document.createElement('slot'), this._shortcut);
27
+ }
28
+ if (this._el) { this._el.removeEventListener('click', this._onClick); this._el.replaceWith(el); } else this._wrapper.appendChild(el);
29
+ this._el = el;
30
+ if (this._onClick) el.addEventListener('click', this._onClick);
31
+ this._sync();
32
+ }
33
+
34
+ _sync() {
35
+ if (!this._el.classList.contains('nk-menu-item')) return;
36
+ const icon = this.getAttribute('icon');
37
+ this._icon.textContent = icon || '';
38
+ this._icon.style.display = icon ? '' : 'none';
39
+ const sc = this.getAttribute('shortcut');
40
+ this._shortcut.textContent = sc || '';
41
+ this._shortcut.style.display = sc ? '' : 'none';
42
+ this._el.classList.toggle('danger', this.getBoolAttr('danger'));
43
+ this._el.setAttribute('aria-disabled', this.getBoolAttr('disabled') ? 'true' : 'false');
44
+ }
45
+
46
+ setupEvents() {
47
+ this._onClick = () => this.select();
48
+ this._el.addEventListener('click', this._onClick);
49
+ }
50
+
51
+ teardownEvents() { this._el?.removeEventListener('click', this._onClick); }
52
+
53
+ select() {
54
+ if (!this._el.classList.contains('nk-menu-item') || this.getBoolAttr('disabled')) return;
55
+ this.emit('nk-select', { value: this.value, label: this.textContent.trim(), item: this });
56
+ }
57
+
58
+ onAttributeChanged(name) { name === 'type' ? this._build() : this._sync(); }
59
+ focus(o) { this._el?.focus(o); }
60
+ get selectable() { return this._el?.classList.contains('nk-menu-item') && !this.getBoolAttr('disabled'); }
61
+
62
+ get value() { return this.getAttribute('value') ?? this.textContent.trim(); }
63
+ set value(v) { this.setAttribute('value', v); }
64
+ }
65
+
66
+ customElements.define('nk-menu-item', NkMenuItem);
67
+
68
+ export { NkMenuItem };
@@ -0,0 +1,45 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-menu>
4
+ // <nk-menu-item type="label">Page</nk-menu-item>
5
+ // <nk-menu-item icon="✏️" shortcut="⌘E" value="rename">Rename</nk-menu-item>
6
+ // <nk-menu-item type="separator"></nk-menu-item>
7
+ // <nk-menu-item icon="🗑️" danger value="delete">Delete</nk-menu-item>
8
+ // </nk-menu>
9
+ // → <div class="nk-pop nk-menu" role="menu">…</div>
10
+ // ↑↓ move between items, Enter selects, nk-select bubbles from the item.
11
+ class NkMenu extends NkElement {
12
+ render() {
13
+ this._box = this.createElement('div', ['nk-pop', 'nk-menu'], { role: 'menu' });
14
+ this._box.appendChild(document.createElement('slot'));
15
+ this._wrapper.appendChild(this._box);
16
+ }
17
+
18
+ get items() { return [...this.querySelectorAll(':scope > nk-menu-item')].filter(i => i.selectable); }
19
+
20
+ setupEvents() {
21
+ this._onKey = (e) => {
22
+ const dir = { ArrowDown: 1, ArrowUp: -1 }[e.key];
23
+ const current = e.target.closest?.('nk-menu-item');
24
+ const items = this.items;
25
+ if (!items.length) return;
26
+ if (dir) {
27
+ e.preventDefault();
28
+ const i = items.indexOf(current);
29
+ items[(i + dir + items.length) % items.length].focus();
30
+ } else if ((e.key === 'Enter' || e.key === ' ') && current) {
31
+ e.preventDefault(); current.select();
32
+ }
33
+ };
34
+ this.addEventListener('keydown', this._onKey);
35
+ }
36
+
37
+ teardownEvents() { this.removeEventListener('keydown', this._onKey); }
38
+
39
+ /** Focuses the first item (called by a popover when it opens). */
40
+ focusFirst() { this.items[0]?.focus(); }
41
+ }
42
+
43
+ customElements.define('nk-menu', NkMenu);
44
+
45
+ export { NkMenu };
@@ -0,0 +1,166 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+ import { l as lockScroll, i as inertOutside, f as firstFocusable, u as unlockScroll } from './shared/focus-DXrK9oLe.js';
3
+
4
+ // <nk-modal id="settings">
5
+ // <nk-settings-user slot="user" name="…" mail="…"></nk-settings-user>
6
+ // <nk-settings-pane name="profile" group="Account" icon="👤" label="My profile" title="My profile" active>…</nk-settings-pane>
7
+ // <nk-settings-pane name="members" group="Workspace" icon="👥" label="Members" title="Members">…</nk-settings-pane>
8
+ // </nk-modal>
9
+ // → <div class="nk-modal-backdrop open"><div class="nk-modal" role="dialog">
10
+ // <nav class="nk-settings-nav">…user… section labels + tree rows…</nav>
11
+ // <div class="nk-settings-content">…panes…</div></div></div>
12
+ //
13
+ // The modal renders the nav rows itself from the panes' label/icon/group, so
14
+ // `.nk-settings-nav .nk-tree-item` and the 860px icon-rail rules match with
15
+ // no JS breakpoint. Escape and the backdrop close it; focus moves in and
16
+ // back out; the page behind is scroll-locked and inert. Put the element
17
+ // directly under <body>: a transformed ancestor would trap the fixed backdrop.
18
+ class NkModal extends NkElement {
19
+ static get observedAttributes() { return ['open', 'pane']; }
20
+
21
+ render() {
22
+ this._backdrop = this.createElement('div', ['nk-modal-backdrop']);
23
+ this._backdrop.inert = true;
24
+ this._box = this.createElement('div', ['nk-modal'], { role: 'dialog', 'aria-modal': 'true', tabindex: '-1' });
25
+ this._nav = this.createElement('nav', ['nk-settings-nav'], { 'aria-label': 'Settings' });
26
+ this._nav.appendChild(this.createElement('slot', [], { name: 'user' }));
27
+ this._rows = document.createElement('div');
28
+ this._nav.appendChild(this._rows);
29
+ this._nav.appendChild(this.createElement('slot', [], { name: 'nav' }));
30
+ this._content = this.createElement('div', ['nk-settings-content']);
31
+ this._slot = document.createElement('slot');
32
+ this._content.appendChild(this._slot);
33
+ this._box.append(this._nav, this._content);
34
+ this._backdrop.appendChild(this._box);
35
+ this._wrapper.appendChild(this._backdrop);
36
+ this._syncOpen();
37
+ this.requestNav();
38
+ }
39
+
40
+ get panes() { return [...this.querySelectorAll(':scope > nk-settings-pane')]; }
41
+
42
+ /** Rebuilds the nav from the panes (debounced – panes upgrade after the modal). */
43
+ requestNav() {
44
+ if (this._navQueued) return;
45
+ this._navQueued = true;
46
+ queueMicrotask(() => { this._navQueued = false; this._buildNav(); });
47
+ }
48
+
49
+ _buildNav() {
50
+ if (!this._rows) return;
51
+ const panes = this.panes;
52
+ const labelled = panes.filter(p => p.getAttribute('label'));
53
+ this._rows.replaceChildren();
54
+ this._nav.style.display = (labelled.length || this.querySelector(':scope > [slot="nav"], :scope > [slot="user"]')) ? '' : 'none';
55
+ let lastGroup = null;
56
+ for (const pane of labelled) {
57
+ const group = pane.getAttribute('group');
58
+ if (group && group !== lastGroup) {
59
+ const label = this.createElement('div', ['nk-section-label']);
60
+ label.textContent = group;
61
+ this._rows.appendChild(label);
62
+ lastGroup = group;
63
+ }
64
+ const row = this.createElement('div', ['nk-tree-item'], { role: 'tab', tabindex: '0', 'data-pane': pane.name });
65
+ const icon = this.createElement('span', ['icon']);
66
+ icon.textContent = pane.getAttribute('icon') || '';
67
+ const text = this.createElement('span', ['label']);
68
+ text.textContent = pane.getAttribute('label');
69
+ row.append(icon, text);
70
+ this._rows.appendChild(row);
71
+ }
72
+ this._syncActive();
73
+ }
74
+
75
+ /** Marks the active pane and its nav row without rebuilding the rows (keeps focus). */
76
+ _syncActive() {
77
+ const panes = this.panes;
78
+ let current = this.getAttribute('pane');
79
+ if (!panes.some(p => p.name === current)) current = (panes.find(p => p.hasAttribute('active')) || panes[0])?.name ?? null;
80
+ for (const row of this._rows.querySelectorAll('[data-pane]')) {
81
+ const on = row.dataset.pane === current;
82
+ row.classList.toggle('active', on);
83
+ row.setAttribute('aria-selected', on ? 'true' : 'false');
84
+ }
85
+ for (const pane of panes) pane.active = pane.name === current;
86
+ this._current = current;
87
+ }
88
+
89
+ _syncOpen() {
90
+ const open = this.getBoolAttr('open');
91
+ this._backdrop.classList.toggle('open', open);
92
+ this._backdrop.inert = !open;
93
+ if (open === this._wasOpen) return;
94
+ this._wasOpen = open;
95
+ if (open) {
96
+ this._returnFocus = document.activeElement;
97
+ lockScroll();
98
+ this._undoInert = inertOutside(this);
99
+ requestAnimationFrame(() => {
100
+ const target = this._rows.querySelector('.nk-tree-item.active') || firstFocusable(this) || this._box;
101
+ target.focus({ preventScroll: true });
102
+ });
103
+ } else {
104
+ unlockScroll();
105
+ this._undoInert?.(); this._undoInert = null;
106
+ const back = this._returnFocus;
107
+ if (back && typeof back.focus === 'function' && back.isConnected) back.focus({ preventScroll: true });
108
+ this._returnFocus = null;
109
+ }
110
+ }
111
+
112
+ setupEvents() {
113
+ this._onBackdrop = (e) => { if (e.target === this._backdrop) this.close(); };
114
+ this._onKey = (e) => { if (e.key === 'Escape' && this.getBoolAttr('open')) { e.stopPropagation(); this.close(); } };
115
+ this._onNavClick = (e) => { const row = e.target.closest('[data-pane]'); if (row) { row.focus(); this.pane = row.dataset.pane; } };
116
+ this._onNavKey = (e) => {
117
+ const row = e.target.closest?.('[data-pane]');
118
+ if (!row) return;
119
+ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.pane = row.dataset.pane; return; }
120
+ const dir = { ArrowDown: 1, ArrowUp: -1 }[e.key];
121
+ if (!dir) return;
122
+ const rows = [...this._rows.querySelectorAll('[data-pane]')];
123
+ const next = rows[(rows.indexOf(row) + dir + rows.length) % rows.length];
124
+ e.preventDefault(); next.focus(); this.pane = next.dataset.pane;
125
+ };
126
+ this._onSlot = () => this.requestNav();
127
+ this._backdrop.addEventListener('click', this._onBackdrop);
128
+ document.addEventListener('keydown', this._onKey);
129
+ this._rows.addEventListener('click', this._onNavClick);
130
+ this._rows.addEventListener('keydown', this._onNavKey);
131
+ this._slot.addEventListener('slotchange', this._onSlot);
132
+ }
133
+
134
+ teardownEvents() {
135
+ this._backdrop?.removeEventListener('click', this._onBackdrop);
136
+ document.removeEventListener('keydown', this._onKey);
137
+ this._rows?.removeEventListener('click', this._onNavClick);
138
+ this._rows?.removeEventListener('keydown', this._onNavKey);
139
+ this._slot?.removeEventListener('slotchange', this._onSlot);
140
+ if (this._wasOpen) { unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
141
+ }
142
+
143
+ onAttributeChanged(name) {
144
+ if (name === 'open') {
145
+ this._syncOpen();
146
+ this.emit('nk-toggle', { open: this.getBoolAttr('open') });
147
+ } else if (name === 'pane') {
148
+ const before = this._current;
149
+ this._syncActive();
150
+ if (this._current !== before) this.emit('nk-select', { value: this._current, label: this.panes.find(p => p.name === this._current)?.getAttribute('label') ?? this._current });
151
+ }
152
+ }
153
+
154
+ show(pane) { if (pane) this.setAttribute('pane', pane); this.setBoolAttr('open', true); }
155
+ close() { this.setBoolAttr('open', false); }
156
+ toggle() { this.setBoolAttr('open', !this.getBoolAttr('open')); }
157
+
158
+ get open() { return this.getBoolAttr('open'); }
159
+ set open(v) { this.setBoolAttr('open', v); }
160
+ get pane() { return this._current ?? this.getAttribute('pane'); }
161
+ set pane(v) { this.setAttribute('pane', v); }
162
+ }
163
+
164
+ customElements.define('nk-modal', NkModal);
165
+
166
+ export { NkModal };
@@ -0,0 +1,89 @@
1
+ import { a as NkFormElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-model-card name="model" value="gpt" title="Mona Pro" desc="Best for long documents" selected></nk-model-card>
4
+ // → <div class="nk-model-card selected"><div class="m-radio"></div><div><div class="m-name">…</div><div class="m-desc">…</div></div></div>
5
+ // Cards with the same `name` in the same tree behave like radios and submit
6
+ // the selected value with the form.
7
+ class NkModelCard extends NkFormElement {
8
+ static get observedAttributes() { return ['name', 'value', 'title', 'desc', 'selected', 'disabled']; }
9
+
10
+ render() {
11
+ this._card = this.createElement('div', ['nk-model-card'], { role: 'radio', tabindex: '0' });
12
+ this._card.appendChild(this.createElement('div', ['m-radio']));
13
+ const text = document.createElement('div');
14
+ this._name = this.createElement('div', ['m-name']);
15
+ const titleSlot = this.createElement('slot', [], { name: 'title' });
16
+ titleSlot.appendChild(this._name);
17
+ this._desc = this.createElement('div', ['m-desc']);
18
+ const descSlot = this.createElement('slot', [], { name: 'desc' });
19
+ descSlot.appendChild(this._desc);
20
+ text.append(titleSlot, descSlot);
21
+ this._card.appendChild(text);
22
+ this._wrapper.appendChild(this._card);
23
+ this._defaultSelected = this.getBoolAttr('selected');
24
+ this._sync();
25
+ if (this.getBoolAttr('selected')) this._unselectPeers();
26
+ }
27
+
28
+ _sync() {
29
+ const on = this.getBoolAttr('selected');
30
+ this._card.classList.toggle('selected', on);
31
+ this._card.setAttribute('aria-checked', on ? 'true' : 'false');
32
+ this._card.setAttribute('aria-disabled', this.getBoolAttr('disabled') ? 'true' : 'false');
33
+ this._name.textContent = this.getAttribute('title') || '';
34
+ const desc = this.getAttribute('desc');
35
+ this._desc.textContent = desc || '';
36
+ this._desc.style.display = desc ? '' : 'none';
37
+ this.setFormValue(on ? (this.getAttribute('value') || 'on') : null, on ? 'selected' : 'unselected');
38
+ }
39
+
40
+ _group() {
41
+ const name = this.getAttribute('name');
42
+ const root = this.getRootNode();
43
+ if (!name || typeof root?.querySelectorAll !== 'function') return [this];
44
+ return [...root.querySelectorAll('nk-model-card')].filter(el => el.getAttribute('name') === name);
45
+ }
46
+
47
+ _unselectPeers() {
48
+ for (const el of this._group()) if (el !== this && el.hasAttribute('selected')) el.removeAttribute('selected');
49
+ }
50
+
51
+ setupEvents() {
52
+ this._onClick = () => this.select();
53
+ this._onKey = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.select(); } };
54
+ this._card.addEventListener('click', this._onClick);
55
+ this._card.addEventListener('keydown', this._onKey);
56
+ }
57
+
58
+ teardownEvents() {
59
+ this._card?.removeEventListener('click', this._onClick);
60
+ this._card?.removeEventListener('keydown', this._onKey);
61
+ }
62
+
63
+ select() {
64
+ if (this.getBoolAttr('disabled') || this.getBoolAttr('selected')) return;
65
+ this.setBoolAttr('selected', true);
66
+ this.emit('nk-change', { value: this.value, name: this.name, checked: true });
67
+ this.emit('nk-select', { value: this.value, label: this.getAttribute('title') });
68
+ this.dispatchEvent(new Event('change', { bubbles: true }));
69
+ }
70
+
71
+ onAttributeChanged(name) {
72
+ this._sync();
73
+ if (name === 'selected' && this.getBoolAttr('selected')) this._unselectPeers();
74
+ }
75
+
76
+ resetValue() { this.setBoolAttr('selected', this._defaultSelected); }
77
+ focus(o) { this._card?.focus(o); }
78
+
79
+ get selected() { return this.getBoolAttr('selected'); }
80
+ set selected(v) { this.setBoolAttr('selected', v); }
81
+ get value() { return this.getAttribute('value') || 'on'; }
82
+ set value(v) { this.setAttribute('value', v); }
83
+ get name() { return this.getAttribute('name'); }
84
+ set name(v) { this.setAttribute('name', v); }
85
+ }
86
+
87
+ customElements.define('nk-model-card', NkModelCard);
88
+
89
+ export { NkModelCard };
@@ -0,0 +1,15 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-page-actions><span>👤 Ada</span><span>📅 12 May</span><span>🏷️ <nk-tag color="purple">Design</nk-tag></span></nk-page-actions>
4
+ // → <div class="nk-page-meta">…</div> – the meta row under the title
5
+ class NkPageActions extends NkElement {
6
+ render() {
7
+ const row = this.createElement('div', ['nk-page-meta']);
8
+ row.appendChild(document.createElement('slot'));
9
+ this._wrapper.appendChild(row);
10
+ }
11
+ }
12
+
13
+ customElements.define('nk-page-actions', NkPageActions);
14
+
15
+ export { NkPageActions };
@@ -0,0 +1,35 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-page-cover slot="cover"></nk-page-cover> → the token gradient
4
+ // <nk-page-cover slot="cover" src="cover.jpg"></nk-page-cover> → an image
5
+ class NkPageCover extends NkElement {
6
+ static get observedAttributes() { return ['src']; }
7
+
8
+ render() {
9
+ this._cover = this.createElement('div', ['nk-cover']);
10
+ this._wrapper.appendChild(this._cover);
11
+ this._sync();
12
+ }
13
+
14
+ _sync() {
15
+ const src = this.getAttribute('src');
16
+ if (src) {
17
+ this._cover.style.backgroundImage = `url("${src}")`;
18
+ this._cover.style.backgroundSize = 'cover';
19
+ this._cover.style.backgroundPosition = 'center';
20
+ } else {
21
+ this._cover.style.backgroundImage = '';
22
+ this._cover.style.backgroundSize = '';
23
+ this._cover.style.backgroundPosition = '';
24
+ }
25
+ }
26
+
27
+ onAttributeChanged() { this._sync(); }
28
+
29
+ get src() { return this.getAttribute('src'); }
30
+ set src(v) { v == null ? this.removeAttribute('src') : this.setAttribute('src', v); }
31
+ }
32
+
33
+ customElements.define('nk-page-cover', NkPageCover);
34
+
35
+ export { NkPageCover };
@@ -0,0 +1,71 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-page-title>NotionKit MVP</nk-page-title> → <h1 class="nk-page-title">
4
+ // <nk-page-title editable placeholder="Untitled">…</nk-page-title>
5
+ // With `editable` the text is copied into the shadow heading (contenteditable
6
+ // cannot edit slotted nodes) and nk-change fires on blur / Enter.
7
+ class NkPageTitle extends NkElement {
8
+ static get observedAttributes() { return ['editable', 'placeholder', 'value']; }
9
+ static get observesLightDom() { return true; }
10
+
11
+ render() {
12
+ this._h1 = this.createElement('h1', ['nk-page-title']);
13
+ this._slot = document.createElement('slot');
14
+ this._h1.appendChild(this._slot);
15
+ this._wrapper.appendChild(this._h1);
16
+ this._sync();
17
+ }
18
+
19
+ _sync() {
20
+ const editable = this.getBoolAttr('editable');
21
+ if (editable) {
22
+ if (this._slot.isConnected) this._slot.remove();
23
+ this._h1.setAttribute('contenteditable', 'plaintext-only');
24
+ this._h1.setAttribute('spellcheck', 'false');
25
+ this._h1.setAttribute('role', 'textbox');
26
+ this._h1.setAttribute('aria-label', 'Title');
27
+ const text = this.getAttribute('value') ?? this.textContent.trim();
28
+ if (this._h1.textContent !== text && document.activeElement !== this) this._h1.textContent = text;
29
+ this._h1.setAttribute('data-placeholder', this.getAttribute('placeholder') || '');
30
+ } else {
31
+ this._h1.removeAttribute('contenteditable');
32
+ this._h1.removeAttribute('role');
33
+ if (this.hasAttribute('value')) { if (this._slot.isConnected) this._slot.remove(); this._h1.textContent = this.getAttribute('value'); }
34
+ else if (!this._slot.isConnected) { this._h1.textContent = ''; this._h1.appendChild(this._slot); }
35
+ }
36
+ }
37
+
38
+ projectLightDom() { if (this._h1 && !this.hasAttribute('value')) this._sync(); }
39
+
40
+ setupEvents() {
41
+ this._committed = this.value;
42
+ this._onKey = (e) => { if (e.key === 'Enter') { e.preventDefault(); this._h1.blur(); } };
43
+ this._onBlur = () => this._commit();
44
+ this._h1.addEventListener('keydown', this._onKey);
45
+ this._h1.addEventListener('blur', this._onBlur);
46
+ }
47
+
48
+ teardownEvents() {
49
+ this._h1?.removeEventListener('keydown', this._onKey);
50
+ this._h1?.removeEventListener('blur', this._onBlur);
51
+ }
52
+
53
+ _commit() {
54
+ const value = this._h1.textContent.trim();
55
+ if (value === this._committed) return;
56
+ this._committed = value;
57
+ this.emit('nk-change', { value });
58
+ }
59
+
60
+ onAttributeChanged() { this._sync(); }
61
+
62
+ get value() { return this.getBoolAttr('editable') ? (this._h1?.textContent.trim() ?? '') : (this.getAttribute('value') ?? this.textContent.trim()); }
63
+ set value(v) { this.setAttribute('value', v); }
64
+ get editable() { return this.getBoolAttr('editable'); }
65
+ set editable(v) { this.setBoolAttr('editable', v); }
66
+ focus(o) { this._h1?.focus(o); }
67
+ }
68
+
69
+ customElements.define('nk-page-title', NkPageTitle);
70
+
71
+ export { NkPageTitle };
@@ -0,0 +1,71 @@
1
+ import { N as NkElement } from './shared/base-6gEcoatG.js';
2
+
3
+ // <nk-page icon="🚀" cover>
4
+ // <nk-page-title>NotionKit MVP</nk-page-title>
5
+ // <nk-page-actions>…</nk-page-actions>
6
+ // <p class="lead">…</p>
7
+ // …blocks…
8
+ // </nk-page>
9
+ // → <div class="nk-page-scroll"><div class="nk-cover"></div><div class="nk-page">
10
+ // <div class="nk-page-icon">🚀</div>…</div></div>
11
+ //
12
+ // The host is display:contents, so .nk-page-scroll is the flex child of
13
+ // .nk-main that scrolls. `narrow` drops the scroll wrapper for pages that are
14
+ // the document itself (landing / docs skeleton). The page icon is rendered
15
+ // here because its ::slotted twin is keyed on the parent (.nk-page).
16
+ class NkPage extends NkElement {
17
+ static get observedAttributes() { return ['icon', 'cover', 'narrow']; }
18
+
19
+ render() {
20
+ this._page = this.createElement('div', ['nk-page']);
21
+ this._icon = this.createElement('div', ['nk-page-icon'], { role: 'button', title: 'Change icon' });
22
+ const iconSlot = this.createElement('slot', [], { name: 'icon' });
23
+ iconSlot.appendChild(this._icon);
24
+ this._page.appendChild(iconSlot);
25
+ this._page.appendChild(document.createElement('slot'));
26
+ this._cover = this.createElement('div', ['nk-cover']);
27
+ this._coverSlot = this.createElement('slot', [], { name: 'cover' });
28
+ this._coverSlot.appendChild(this._cover);
29
+ this._build();
30
+ }
31
+
32
+ _build() {
33
+ this._wrapper.replaceChildren();
34
+ if (this.getBoolAttr('narrow')) {
35
+ this._wrapper.append(this._coverSlot, this._page);
36
+ } else {
37
+ const scroll = this.createElement('div', ['nk-page-scroll']);
38
+ scroll.append(this._coverSlot, this._page);
39
+ this._wrapper.appendChild(scroll);
40
+ }
41
+ this._sync();
42
+ }
43
+
44
+ _sync() {
45
+ const icon = this.getAttribute('icon');
46
+ this._icon.textContent = icon || '';
47
+ this._icon.style.display = icon ? '' : 'none';
48
+ this._cover.style.display = this.getBoolAttr('cover') ? '' : 'none';
49
+ }
50
+
51
+ setupEvents() {
52
+ this._onIcon = () => this.emit('nk-action', { action: 'icon', value: this.getAttribute('icon') });
53
+ this._icon.addEventListener('click', this._onIcon);
54
+ }
55
+
56
+ teardownEvents() { this._icon?.removeEventListener('click', this._onIcon); }
57
+
58
+ onAttributeChanged(name) {
59
+ if (name === 'narrow') this._build();
60
+ else this._sync();
61
+ }
62
+
63
+ get icon() { return this.getAttribute('icon'); }
64
+ set icon(v) { v == null ? this.removeAttribute('icon') : this.setAttribute('icon', v); }
65
+ get cover() { return this.getBoolAttr('cover'); }
66
+ set cover(v) { this.setBoolAttr('cover', v); }
67
+ }
68
+
69
+ customElements.define('nk-page', NkPage);
70
+
71
+ export { NkPage };