@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.
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/components/nk-ai-input-row.js +57 -0
- package/dist/components/nk-ai-msg.js +67 -0
- package/dist/components/nk-ai-thread.js +14 -0
- package/dist/components/nk-app.js +23 -0
- package/dist/components/nk-avatar-group.js +35 -0
- package/dist/components/nk-banner.js +30 -0
- package/dist/components/nk-block-host.js +35 -0
- package/dist/components/nk-board-view.js +129 -0
- package/dist/components/nk-breadcrumb.js +78 -0
- package/dist/components/nk-btn.js +107 -0
- package/dist/components/nk-callout.js +36 -0
- package/dist/components/nk-check.js +77 -0
- package/dist/components/nk-cmdk.js +221 -0
- package/dist/components/nk-code.js +66 -0
- package/dist/components/nk-comment.js +41 -0
- package/dist/components/nk-comments.js +57 -0
- package/dist/components/nk-danger-zone.js +27 -0
- package/dist/components/nk-database.js +100 -0
- package/dist/components/nk-divider.js +12 -0
- package/dist/components/nk-emoji-picker.js +112 -0
- package/dist/components/nk-empty.js +36 -0
- package/dist/components/nk-field.js +50 -0
- package/dist/components/nk-filter-bar.js +78 -0
- package/dist/components/nk-heading.js +29 -0
- package/dist/components/nk-input.js +108 -0
- package/dist/components/nk-kbd.js +15 -0
- package/dist/components/nk-member-list.js +30 -0
- package/dist/components/nk-member-row.js +46 -0
- package/dist/components/nk-mention.js +31 -0
- package/dist/components/nk-menu-item.js +68 -0
- package/dist/components/nk-menu.js +45 -0
- package/dist/components/nk-modal.js +166 -0
- package/dist/components/nk-model-card.js +89 -0
- package/dist/components/nk-page-actions.js +15 -0
- package/dist/components/nk-page-cover.js +35 -0
- package/dist/components/nk-page-title.js +71 -0
- package/dist/components/nk-page.js +71 -0
- package/dist/components/nk-pop.js +90 -0
- package/dist/components/nk-profile-row.js +25 -0
- package/dist/components/nk-progress.js +43 -0
- package/dist/components/nk-quote.js +32 -0
- package/dist/components/nk-radio.js +160 -0
- package/dist/components/nk-section-label.js +42 -0
- package/dist/components/nk-segmented.js +99 -0
- package/dist/components/nk-select.js +127 -0
- package/dist/components/nk-settings-pane.js +40 -0
- package/dist/components/nk-settings-user.js +36 -0
- package/dist/components/nk-sidebar.js +81 -0
- package/dist/components/nk-skeleton.js +31 -0
- package/dist/components/nk-slider.js +85 -0
- package/dist/components/nk-stat.js +40 -0
- package/dist/components/nk-stats.js +14 -0
- package/dist/components/nk-switch.js +69 -0
- package/dist/components/nk-synced.js +26 -0
- package/dist/components/nk-tab.js +54 -0
- package/dist/components/nk-table-view.js +109 -0
- package/dist/components/nk-tabs.js +82 -0
- package/dist/components/nk-tag.js +32 -0
- package/dist/components/nk-template-btn.js +43 -0
- package/dist/components/nk-textarea.js +95 -0
- package/dist/components/nk-theme-toggle.js +67 -0
- package/dist/components/nk-toast.js +51 -0
- package/dist/components/nk-todo.js +73 -0
- package/dist/components/nk-toggle.js +56 -0
- package/dist/components/nk-topbar.js +24 -0
- package/dist/components/nk-tree-item.js +155 -0
- package/dist/components/nk-tree.js +123 -0
- package/dist/components/nk-workspace-switcher.js +82 -0
- package/dist/components/shared/base-6gEcoatG.js +317 -0
- package/dist/components/shared/focus-DXrK9oLe.js +47 -0
- package/dist/components/shared/property-cell-Dn91uFkf.js +92 -0
- package/dist/notionkit-elements.esm.js +4639 -0
- package/dist/notionkit-elements.js +4718 -0
- package/dist/notionkit-elements.min.js +1 -0
- package/package.json +72 -0
- package/src/base.js +296 -0
- package/src/components/content/nk-callout.js +35 -0
- package/src/components/content/nk-code.js +51 -0
- package/src/components/content/nk-divider.js +11 -0
- package/src/components/content/nk-heading.js +28 -0
- package/src/components/content/nk-kbd.js +14 -0
- package/src/components/content/nk-progress.js +42 -0
- package/src/components/content/nk-quote.js +31 -0
- package/src/components/content/nk-tag.js +31 -0
- package/src/components/content/nk-todo.js +72 -0
- package/src/components/content/nk-toggle.js +55 -0
- package/src/components/data/nk-ai-input-row.js +56 -0
- package/src/components/data/nk-ai-msg.js +66 -0
- package/src/components/data/nk-ai-thread.js +13 -0
- package/src/components/data/nk-board-view.js +128 -0
- package/src/components/data/nk-comment.js +40 -0
- package/src/components/data/nk-comments.js +56 -0
- package/src/components/data/nk-database.js +99 -0
- package/src/components/data/nk-filter-bar.js +77 -0
- package/src/components/data/nk-table-view.js +108 -0
- package/src/components/forms/nk-btn.js +106 -0
- package/src/components/forms/nk-check.js +76 -0
- package/src/components/forms/nk-field.js +49 -0
- package/src/components/forms/nk-input.js +107 -0
- package/src/components/forms/nk-radio.js +159 -0
- package/src/components/forms/nk-select.js +126 -0
- package/src/components/forms/nk-slider.js +84 -0
- package/src/components/forms/nk-switch.js +68 -0
- package/src/components/forms/nk-textarea.js +94 -0
- package/src/components/overlays/nk-cmdk.js +201 -0
- package/src/components/overlays/nk-emoji-picker.js +94 -0
- package/src/components/overlays/nk-menu-item.js +67 -0
- package/src/components/overlays/nk-menu.js +44 -0
- package/src/components/overlays/nk-modal.js +165 -0
- package/src/components/overlays/nk-pop.js +89 -0
- package/src/components/overlays/nk-settings-pane.js +39 -0
- package/src/components/overlays/nk-settings-user.js +35 -0
- package/src/components/overlays/nk-toast.js +50 -0
- package/src/components/page/nk-avatar-group.js +34 -0
- package/src/components/page/nk-banner.js +29 -0
- package/src/components/page/nk-block-host.js +34 -0
- package/src/components/page/nk-danger-zone.js +26 -0
- package/src/components/page/nk-empty.js +35 -0
- package/src/components/page/nk-member-list.js +29 -0
- package/src/components/page/nk-member-row.js +45 -0
- package/src/components/page/nk-mention.js +30 -0
- package/src/components/page/nk-model-card.js +88 -0
- package/src/components/page/nk-page-actions.js +14 -0
- package/src/components/page/nk-page-cover.js +34 -0
- package/src/components/page/nk-page-title.js +70 -0
- package/src/components/page/nk-page.js +70 -0
- package/src/components/page/nk-profile-row.js +24 -0
- package/src/components/page/nk-segmented.js +98 -0
- package/src/components/page/nk-skeleton.js +30 -0
- package/src/components/page/nk-stat.js +39 -0
- package/src/components/page/nk-stats.js +13 -0
- package/src/components/page/nk-synced.js +25 -0
- package/src/components/page/nk-tab.js +53 -0
- package/src/components/page/nk-tabs.js +81 -0
- package/src/components/page/nk-template-btn.js +42 -0
- package/src/components/shell/nk-app.js +22 -0
- package/src/components/shell/nk-breadcrumb.js +77 -0
- package/src/components/shell/nk-section-label.js +41 -0
- package/src/components/shell/nk-sidebar.js +80 -0
- package/src/components/shell/nk-theme-toggle.js +66 -0
- package/src/components/shell/nk-topbar.js +23 -0
- package/src/components/shell/nk-tree-item.js +154 -0
- package/src/components/shell/nk-tree.js +122 -0
- package/src/components/shell/nk-workspace-switcher.js +81 -0
- package/src/index.js +88 -0
- package/src/util/emoji.js +17 -0
- package/src/util/focus.js +45 -0
- package/src/util/fuzzy.js +19 -0
- package/src/util/highlight.js +14 -0
- package/src/util/property-cell.js +90 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-database id="db" view="table" add-view>
|
|
4
|
+
// <nk-table-view name="table" label="▦ Table" count></nk-table-view>
|
|
5
|
+
// <nk-board-view name="board" label="▤ Board" group-by="status"></nk-board-view>
|
|
6
|
+
// </nk-database>
|
|
7
|
+
// db.columns = [{ key: 'name', label: 'Name', type: 'text', icon: '📄', title: true }, { key: 'status', type: 'select', options: [...] }, …];
|
|
8
|
+
// db.rows = [{ id: 1, icon: '🧭', name: '…', status: 'done', … }];
|
|
9
|
+
// → <div class="nk-database"><div class="nk-db-tabs">…</div>…active view…</div>
|
|
10
|
+
// The tabs come from the child views; data is pushed into every view.
|
|
11
|
+
class NkDatabase extends NkElement {
|
|
12
|
+
static get observedAttributes() { return ['view', 'add-view']; }
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
this._box = this.createElement('div', ['nk-database']);
|
|
16
|
+
this._tabs = this.createElement('div', ['nk-db-tabs'], { role: 'tablist' });
|
|
17
|
+
this._slot = document.createElement('slot');
|
|
18
|
+
this._box.append(this._tabs, this._slot);
|
|
19
|
+
this._wrapper.appendChild(this._box);
|
|
20
|
+
this._columns = []; this._rows = [];
|
|
21
|
+
this._sync();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get views() { return this._slot?.assignedElements().filter(el => /^nk-[a-z]+-view$/.test(el.localName)) ?? []; }
|
|
25
|
+
|
|
26
|
+
_sync() {
|
|
27
|
+
if (!this._tabs) return;
|
|
28
|
+
const views = this.views;
|
|
29
|
+
let current = this.getAttribute('view');
|
|
30
|
+
if (!views.some(v => v.name === current)) current = views[0]?.name ?? null;
|
|
31
|
+
this._tabs.replaceChildren();
|
|
32
|
+
for (const v of views) {
|
|
33
|
+
const tab = this.createElement('span', ['nk-db-tab'], { role: 'tab', 'data-view': v.name, tabindex: '0' });
|
|
34
|
+
tab.classList.toggle('active', v.name === current);
|
|
35
|
+
tab.setAttribute('aria-selected', v.name === current ? 'true' : 'false');
|
|
36
|
+
tab.append(document.createTextNode(v.getAttribute('label') || v.name));
|
|
37
|
+
const badge = v.hasAttribute('count') ? String(this._rows.length) : v.getAttribute('badge');
|
|
38
|
+
if (badge) { const b = this.createElement('span', ['badge']); b.textContent = badge; tab.appendChild(b); }
|
|
39
|
+
this._tabs.appendChild(tab);
|
|
40
|
+
v.hidden = v.name !== current;
|
|
41
|
+
// A method call, not a property write: a view that has not been upgraded
|
|
42
|
+
// yet would otherwise get an own `data` property that shadows its setter.
|
|
43
|
+
v.setData?.(this._columns, this._rows);
|
|
44
|
+
}
|
|
45
|
+
if (this.getBoolAttr('add-view')) {
|
|
46
|
+
const add = this.createElement('span', ['nk-db-tab'], { role: 'button', 'data-add': '', tabindex: '0' });
|
|
47
|
+
add.style.color = 'var(--nk-text-tertiary)';
|
|
48
|
+
add.textContent = '+';
|
|
49
|
+
this._tabs.appendChild(add);
|
|
50
|
+
}
|
|
51
|
+
this._tabs.style.display = views.length ? '' : 'none';
|
|
52
|
+
this._current = current;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Views upgrade after the database; each one asks for a resync once rendered. */
|
|
56
|
+
requestSync() {
|
|
57
|
+
if (this._syncQueued) return;
|
|
58
|
+
this._syncQueued = true;
|
|
59
|
+
queueMicrotask(() => { this._syncQueued = false; this._sync(); });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setupEvents() {
|
|
63
|
+
this._onClick = (e) => {
|
|
64
|
+
const tab = e.target.closest('[data-view]');
|
|
65
|
+
if (tab) { this.view = tab.dataset.view; return; }
|
|
66
|
+
if (e.target.closest('[data-add]')) this.emit('nk-action', { action: 'add-view' });
|
|
67
|
+
};
|
|
68
|
+
this._onKey = (e) => { if ((e.key === 'Enter' || e.key === ' ') && e.target.closest('[data-view],[data-add]')) { e.preventDefault(); e.target.click(); } };
|
|
69
|
+
this._onSlot = () => this._sync();
|
|
70
|
+
this._tabs.addEventListener('click', this._onClick);
|
|
71
|
+
this._tabs.addEventListener('keydown', this._onKey);
|
|
72
|
+
this._slot.addEventListener('slotchange', this._onSlot);
|
|
73
|
+
this._sync();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
teardownEvents() {
|
|
77
|
+
this._tabs?.removeEventListener('click', this._onClick);
|
|
78
|
+
this._tabs?.removeEventListener('keydown', this._onKey);
|
|
79
|
+
this._slot?.removeEventListener('slotchange', this._onSlot);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
onAttributeChanged(name) {
|
|
83
|
+
const before = this._current;
|
|
84
|
+
this._sync();
|
|
85
|
+
if (name === 'view' && this._current !== before) this.emit('nk-view-change', { view: this._current });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get columns() { return this._columns; }
|
|
89
|
+
set columns(v) { this._columns = Array.isArray(v) ? v : []; this._sync(); }
|
|
90
|
+
get rows() { return this._rows; }
|
|
91
|
+
set rows(v) { this._rows = Array.isArray(v) ? v : []; this._sync(); }
|
|
92
|
+
get view() { return this._current ?? this.getAttribute('view'); }
|
|
93
|
+
set view(v) { this.setAttribute('view', v); }
|
|
94
|
+
/** Re-renders every view after in-place row mutations. */
|
|
95
|
+
refresh() { this._sync(); }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
customElements.define('nk-database', NkDatabase);
|
|
99
|
+
|
|
100
|
+
export { NkDatabase };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-divider></nk-divider> → <hr class="nk-divider">
|
|
4
|
+
class NkDivider extends NkElement {
|
|
5
|
+
render() {
|
|
6
|
+
this._wrapper.appendChild(this.createElement('hr', ['nk-divider']));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
customElements.define('nk-divider', NkDivider);
|
|
11
|
+
|
|
12
|
+
export { NkDivider };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// Built-in emoji set for <nk-emoji-picker>: eight categories, names for
|
|
4
|
+
// search. Override with `picker.emojis = [{ char, name, cat }]`.
|
|
5
|
+
const C = (cat, icon, list) => list.map(([char, name]) => ({ char, name, cat, icon }));
|
|
6
|
+
const EMOJI_CATEGORIES = [
|
|
7
|
+
{ id: 'smileys', icon: '😀' }, { id: 'people', icon: '👋' }, { id: 'nature', icon: '🌿' }, { id: 'food', icon: '☕' },
|
|
8
|
+
{ id: 'activity', icon: '🎯' }, { id: 'travel', icon: '🚀' }, { id: 'objects', icon: '💡' }, { id: 'symbols', icon: '✅' },
|
|
9
|
+
];
|
|
10
|
+
const EMOJI = [
|
|
11
|
+
...C('smileys', '😀', [['😀', 'grinning'], ['😊', 'smile blush'], ['😂', 'joy laugh'], ['🙂', 'slight smile'], ['😉', 'wink'], ['😍', 'heart eyes love'], ['🤔', 'thinking'], ['😎', 'cool sunglasses'], ['🥳', 'party'], ['😴', 'sleep'], ['🤯', 'mind blown'], ['😅', 'sweat smile'], ['🙃', 'upside down'], ['😇', 'angel halo'], ['🤗', 'hug'], ['😢', 'cry sad']]),
|
|
12
|
+
...C('people', '👋', [['👋', 'wave hello'], ['👍', 'thumbs up'], ['👎', 'thumbs down'], ['🙏', 'pray thanks'], ['👏', 'clap'], ['🤝', 'handshake deal'], ['💪', 'strong'], ['👀', 'eyes look'], ['🧠', 'brain knowledge'], ['❤️', 'heart love'], ['👤', 'person user'], ['👥', 'people team members'], ['🧑💻', 'developer'], ['🙋', 'raise hand'], ['✍️', 'writing'], ['🫶', 'heart hands']]),
|
|
13
|
+
...C('nature', '🌿', [['🌿', 'leaf herb'], ['🌱', 'seedling'], ['🌳', 'tree'], ['🌸', 'blossom flower'], ['🌞', 'sun'], ['🌙', 'moon night'], ['⭐', 'star'], ['🔥', 'fire hot'], ['🌈', 'rainbow'], ['🌊', 'wave water'], ['🍀', 'clover luck'], ['🐝', 'bee'], ['🦋', 'butterfly'], ['🐢', 'turtle'], ['🌍', 'earth globe'], ['❄️', 'snow']]),
|
|
14
|
+
...C('food', '☕', [['☕', 'coffee'], ['🍵', 'tea'], ['🍎', 'apple'], ['🍕', 'pizza'], ['🍰', 'cake'], ['🍪', 'cookie'], ['🥐', 'croissant'], ['🍔', 'burger'], ['🍜', 'noodles'], ['🍺', 'beer'], ['🍷', 'wine'], ['🥗', 'salad'], ['🍫', 'chocolate'], ['🍓', 'strawberry'], ['🥑', 'avocado'], ['🍩', 'donut']]),
|
|
15
|
+
...C('activity', '🎯', [['🎯', 'target goal'], ['🏆', 'trophy win'], ['🎉', 'tada celebration'], ['🎨', 'art palette design'], ['🎙️', 'microphone podcast voice'], ['🎬', 'clapper movie'], ['🎮', 'game'], ['⚽', 'football'], ['🏃', 'run'], ['🧘', 'yoga calm'], ['🎵', 'music'], ['🎲', 'dice'], ['🏁', 'finish flag'], ['🎓', 'graduation learn'], ['🧩', 'puzzle piece'], ['🔁', 'repeat retro']]),
|
|
16
|
+
...C('travel', '🚀', [['🚀', 'rocket launch'], ['✈️', 'plane travel'], ['🚗', 'car'], ['🚲', 'bike'], ['🏠', 'house home'], ['🏢', 'office building'], ['🗺️', 'map'], ['🧭', 'compass'], ['⛰️', 'mountain'], ['🏖️', 'beach'], ['🚉', 'station'], ['🛰️', 'satellite'], ['🌆', 'city'], ['🏕️', 'camping'], ['⛵', 'sailboat'], ['🛫', 'departure']]),
|
|
17
|
+
...C('objects', '💡', [['💡', 'idea bulb'], ['📊', 'chart bar'], ['📈', 'chart up'], ['📅', 'calendar date'], ['📄', 'page document'], ['📁', 'folder'], ['🗃️', 'card box database'], ['📌', 'pin'], ['🔍', 'search'], ['🛠️', 'tools'], ['⚙️', 'gear settings'], ['🔔', 'bell notification'], ['💎', 'gem diamond'], ['📝', 'memo notes'], ['📎', 'paperclip files'], ['🔒', 'lock secure'], ['💬', 'speech comment'], ['📤', 'export outbox'], ['📥', 'inbox'], ['🗑️', 'trash bin'], ['🖥️', 'desktop'], ['📱', 'phone'], ['💻', 'laptop'], ['🧾', 'receipt']]),
|
|
18
|
+
...C('symbols', '✅', [['✅', 'check done'], ['✨', 'sparkles ai magic'], ['❌', 'cross no'], ['⚠️', 'warning'], ['ℹ️', 'info'], ['❓', 'question'], ['➕', 'plus add'], ['➡️', 'arrow right'], ['🔴', 'red circle'], ['🟢', 'green circle'], ['🟡', 'yellow circle'], ['🔵', 'blue circle'], ['⏳', 'hourglass wait'], ['♻️', 'recycle'], ['🆕', 'new'], ['💯', 'hundred']]),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// <nk-emoji-picker></nk-emoji-picker>
|
|
22
|
+
// → <div class="nk-pop"><input class="nk-emoji-search"><div class="nk-emoji-grid">…</div><div class="nk-emoji-cats">…</div></div>
|
|
23
|
+
// Built-in set with names for search; `picker.emojis = [{ char, name, cat }]`
|
|
24
|
+
// replaces it. A click fires nk-select { emoji }.
|
|
25
|
+
class NkEmojiPicker extends NkElement {
|
|
26
|
+
static get observedAttributes() { return ['placeholder', 'value']; }
|
|
27
|
+
|
|
28
|
+
render() {
|
|
29
|
+
this._box = this.createElement('div', ['nk-pop']);
|
|
30
|
+
this._search = this.createElement('input', ['nk-emoji-search'], { type: 'search', autocomplete: 'off', 'aria-label': 'Search emoji' });
|
|
31
|
+
this._grid = this.createElement('div', ['nk-emoji-grid'], { role: 'listbox' });
|
|
32
|
+
this._cats = this.createElement('div', ['nk-emoji-cats'], { role: 'tablist' });
|
|
33
|
+
this._box.append(this._search, this._grid, this._cats);
|
|
34
|
+
this._wrapper.appendChild(this._box);
|
|
35
|
+
this._emojis = EMOJI;
|
|
36
|
+
this._cat = EMOJI_CATEGORIES[0].id;
|
|
37
|
+
this._renderCats();
|
|
38
|
+
this._renderGrid();
|
|
39
|
+
this._syncAttrs();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_syncAttrs() { this._search.placeholder = this.getAttribute('placeholder') || 'Search…'; }
|
|
43
|
+
|
|
44
|
+
_renderCats() {
|
|
45
|
+
this._cats.replaceChildren();
|
|
46
|
+
const cats = this._categories();
|
|
47
|
+
for (const cat of cats) {
|
|
48
|
+
const el = this.createElement('span', [], { role: 'tab', 'data-cat': cat.id, title: cat.id });
|
|
49
|
+
el.textContent = cat.icon;
|
|
50
|
+
el.classList.toggle('active', cat.id === this._cat);
|
|
51
|
+
this._cats.appendChild(el);
|
|
52
|
+
}
|
|
53
|
+
this._cats.style.display = cats.length > 1 ? '' : 'none';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_categories() {
|
|
57
|
+
if (this._catList) return this._catList;
|
|
58
|
+
const seen = new Map();
|
|
59
|
+
for (const e of this._emojis) if (e.cat && !seen.has(e.cat)) seen.set(e.cat, { id: e.cat, icon: e.icon || EMOJI_CATEGORIES.find(c => c.id === e.cat)?.icon || e.char });
|
|
60
|
+
return [...seen.values()];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_renderGrid() {
|
|
64
|
+
const q = this._search.value.trim().toLowerCase();
|
|
65
|
+
const list = q ? this._emojis.filter(e => (e.name || '').toLowerCase().includes(q) || e.char === q) : this._emojis.filter(e => !e.cat || e.cat === this._cat);
|
|
66
|
+
this._grid.replaceChildren();
|
|
67
|
+
for (const e of list.slice(0, 64)) {
|
|
68
|
+
const s = this.createElement('span', [], { role: 'option', title: e.name || '', 'data-emoji': e.char });
|
|
69
|
+
s.textContent = e.char;
|
|
70
|
+
this._grid.appendChild(s);
|
|
71
|
+
}
|
|
72
|
+
for (const el of this._cats.children) el.classList.toggle('active', !q && el.dataset.cat === this._cat);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setupEvents() {
|
|
76
|
+
this._onInput = () => this._renderGrid();
|
|
77
|
+
this._onGrid = (e) => { const s = e.target.closest('[data-emoji]'); if (s) this.select(s.dataset.emoji); };
|
|
78
|
+
this._onCats = (e) => { const c = e.target.closest('[data-cat]'); if (!c) return; this._cat = c.dataset.cat; this._search.value = ''; this._renderGrid(); };
|
|
79
|
+
this._onKey = (e) => { if (e.key === 'Enter') { const first = this._grid.querySelector('[data-emoji]'); if (first) this.select(first.dataset.emoji); } };
|
|
80
|
+
this._search.addEventListener('input', this._onInput);
|
|
81
|
+
this._search.addEventListener('keydown', this._onKey);
|
|
82
|
+
this._grid.addEventListener('click', this._onGrid);
|
|
83
|
+
this._cats.addEventListener('click', this._onCats);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
teardownEvents() {
|
|
87
|
+
this._search?.removeEventListener('input', this._onInput);
|
|
88
|
+
this._search?.removeEventListener('keydown', this._onKey);
|
|
89
|
+
this._grid?.removeEventListener('click', this._onGrid);
|
|
90
|
+
this._cats?.removeEventListener('click', this._onCats);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
select(emoji) {
|
|
94
|
+
this.setAttribute('value', emoji);
|
|
95
|
+
this.emit('nk-select', { emoji, value: emoji });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onAttributeChanged() { this._syncAttrs(); }
|
|
99
|
+
focus(o) { this._search?.focus(o); }
|
|
100
|
+
|
|
101
|
+
/** Category strip override: [{ id, icon }]. Emojis without `cat` show in every category. */
|
|
102
|
+
get categories() { return this._categories(); }
|
|
103
|
+
set categories(list) { this._catList = Array.isArray(list) && list.length ? list : null; this._cat = this._categories()[0]?.id; this._renderCats(); this._renderGrid(); }
|
|
104
|
+
get emojis() { return this._emojis; }
|
|
105
|
+
set emojis(list) { this._emojis = Array.isArray(list) ? list : EMOJI; this._cat = this._categories()[0]?.id; this._renderCats(); this._renderGrid(); }
|
|
106
|
+
get value() { return this.getAttribute('value'); }
|
|
107
|
+
set value(v) { this.setAttribute('value', v); }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
customElements.define('nk-emoji-picker', NkEmojiPicker);
|
|
111
|
+
|
|
112
|
+
export { NkEmojiPicker };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-empty icon="🗂️" title="No entries yet" desc="Create the first one."><nk-btn variant="primary" small>+ New</nk-btn></nk-empty>
|
|
4
|
+
// → <div class="nk-empty"><div class="e-icon">🗂️</div><div class="e-title">…</div><div class="e-desc">…</div>…</div>
|
|
5
|
+
class NkEmpty extends NkElement {
|
|
6
|
+
static get observedAttributes() { return ['icon', 'title', 'desc']; }
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
const box = this.createElement('div', ['nk-empty']);
|
|
10
|
+
this._parts = {};
|
|
11
|
+
for (const [slot, cls] of [['icon', 'e-icon'], ['title', 'e-title'], ['desc', 'e-desc']]) {
|
|
12
|
+
const el = this.createElement('div', [cls]);
|
|
13
|
+
const s = this.createElement('slot', [], { name: slot });
|
|
14
|
+
s.appendChild(el);
|
|
15
|
+
box.appendChild(s);
|
|
16
|
+
this._parts[slot] = el;
|
|
17
|
+
}
|
|
18
|
+
box.appendChild(document.createElement('slot'));
|
|
19
|
+
this._wrapper.appendChild(box);
|
|
20
|
+
this._sync();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_sync() {
|
|
24
|
+
for (const [slot, el] of Object.entries(this._parts)) {
|
|
25
|
+
const v = this.getAttribute(slot);
|
|
26
|
+
el.textContent = v || '';
|
|
27
|
+
el.style.display = v ? '' : 'none';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onAttributeChanged() { this._sync(); }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
customElements.define('nk-empty', NkEmpty);
|
|
35
|
+
|
|
36
|
+
export { NkEmpty };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-field label="Display name" desc="Shown next to your comments">
|
|
4
|
+
// <nk-input value="Marcel"></nk-input>
|
|
5
|
+
// </nk-field>
|
|
6
|
+
// → <div class="nk-field"><div><div class="f-label">…</div><div class="f-desc">…</div></div>
|
|
7
|
+
// <div class="f-control">…</div></div>
|
|
8
|
+
// Rich label / description content goes through slot="label" / slot="desc"
|
|
9
|
+
// (twins `.nk-field ::slotted([slot="label"])` etc. in notionkit.css).
|
|
10
|
+
class NkField extends NkElement {
|
|
11
|
+
static get observedAttributes() { return ['label', 'desc']; }
|
|
12
|
+
|
|
13
|
+
render() {
|
|
14
|
+
const field = this.createElement('div', ['nk-field']);
|
|
15
|
+
const text = document.createElement('div');
|
|
16
|
+
this._label = this.createElement('div', ['f-label']);
|
|
17
|
+
const labelSlot = this.createElement('slot', [], { name: 'label' });
|
|
18
|
+
labelSlot.appendChild(this._label);
|
|
19
|
+
this._desc = this.createElement('div', ['f-desc']);
|
|
20
|
+
const descSlot = this.createElement('slot', [], { name: 'desc' });
|
|
21
|
+
descSlot.appendChild(this._desc);
|
|
22
|
+
text.appendChild(labelSlot);
|
|
23
|
+
text.appendChild(descSlot);
|
|
24
|
+
const control = this.createElement('div', ['f-control']);
|
|
25
|
+
control.appendChild(document.createElement('slot'));
|
|
26
|
+
field.appendChild(text);
|
|
27
|
+
field.appendChild(control);
|
|
28
|
+
this._wrapper.appendChild(field);
|
|
29
|
+
this._sync();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onAttributeChanged() { this._sync(); }
|
|
33
|
+
|
|
34
|
+
_sync() {
|
|
35
|
+
if (!this._label) return;
|
|
36
|
+
this._label.textContent = this.getAttribute('label') || '';
|
|
37
|
+
const desc = this.getAttribute('desc');
|
|
38
|
+
this._desc.textContent = desc || '';
|
|
39
|
+
this._desc.style.display = desc ? '' : 'none';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get label() { return this.getAttribute('label'); }
|
|
43
|
+
set label(v) { this.setAttribute('label', v); }
|
|
44
|
+
get desc() { return this.getAttribute('desc'); }
|
|
45
|
+
set desc(v) { v == null ? this.removeAttribute('desc') : this.setAttribute('desc', v); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
customElements.define('nk-field', NkField);
|
|
49
|
+
|
|
50
|
+
export { NkField };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-filter-bar search placeholder="Search rows …"></nk-filter-bar>
|
|
4
|
+
// bar.filters = [{ key: 'status', label: 'Status: Done', value: 'done' }];
|
|
5
|
+
// Composed from existing classes (nk-btn secondary small, nk-input, nk-tag):
|
|
6
|
+
// filter / sort buttons fire nk-action, chips remove on ×, search and chip
|
|
7
|
+
// changes fire nk-change { filters, search }. The wrapper is layout only.
|
|
8
|
+
class NkFilterBar extends NkElement {
|
|
9
|
+
static get observedAttributes() { return ['search', 'placeholder', 'no-filter', 'no-sort', 'value']; }
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
this._bar = document.createElement('div');
|
|
13
|
+
this._bar.style.cssText = 'display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:8px 0';
|
|
14
|
+
this._filterBtn = this.createElement('button', ['nk-btn', 'secondary', 'small'], { type: 'button', 'data-action': 'filter' });
|
|
15
|
+
this._filterBtn.textContent = '⚲ Filter';
|
|
16
|
+
this._sortBtn = this.createElement('button', ['nk-btn', 'secondary', 'small'], { type: 'button', 'data-action': 'sort' });
|
|
17
|
+
this._sortBtn.textContent = '↕ Sort';
|
|
18
|
+
this._chips = document.createElement('span');
|
|
19
|
+
this._chips.style.cssText = 'display:inline-flex;gap:4px;flex-wrap:wrap';
|
|
20
|
+
this._input = this.createElement('input', ['nk-input'], { type: 'search', autocomplete: 'off' });
|
|
21
|
+
this._input.style.marginLeft = 'auto';
|
|
22
|
+
this._bar.append(this._filterBtn, this._sortBtn, this._chips, document.createElement('slot'), this._input);
|
|
23
|
+
this._wrapper.appendChild(this._bar);
|
|
24
|
+
this._filters = [];
|
|
25
|
+
this._sync();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_sync() {
|
|
29
|
+
this._filterBtn.style.display = this.getBoolAttr('no-filter') ? 'none' : '';
|
|
30
|
+
this._sortBtn.style.display = this.getBoolAttr('no-sort') ? 'none' : '';
|
|
31
|
+
this._input.style.display = this.getBoolAttr('search') ? '' : 'none';
|
|
32
|
+
this._input.placeholder = this.getAttribute('placeholder') || 'Search …';
|
|
33
|
+
if (this.hasAttribute('value') && this._input.value !== this.getAttribute('value')) this._input.value = this.getAttribute('value');
|
|
34
|
+
this._chips.replaceChildren();
|
|
35
|
+
this._filters.forEach((f, i) => {
|
|
36
|
+
const chip = this.createElement('span', ['nk-tag', f.color || 'blue'], { 'data-index': i });
|
|
37
|
+
chip.style.cursor = 'pointer';
|
|
38
|
+
chip.textContent = `${f.label ?? `${f.key}: ${f.value}`} ×`;
|
|
39
|
+
chip.title = 'Remove filter';
|
|
40
|
+
this._chips.appendChild(chip);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setupEvents() {
|
|
45
|
+
this._onClick = (e) => {
|
|
46
|
+
const btn = e.target.closest('[data-action]');
|
|
47
|
+
if (btn) { this.emit('nk-action', { action: btn.dataset.action }); return; }
|
|
48
|
+
const chip = e.target.closest('[data-index]');
|
|
49
|
+
if (chip) { this._filters.splice(Number(chip.dataset.index), 1); this._sync(); this._emitChange(); }
|
|
50
|
+
};
|
|
51
|
+
this._onInput = () => { clearTimeout(this._debounce); this._debounce = setTimeout(() => this._emitChange(), 150); };
|
|
52
|
+
this._bar.addEventListener('click', this._onClick);
|
|
53
|
+
this._input.addEventListener('input', this._onInput);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
teardownEvents() {
|
|
57
|
+
this._bar?.removeEventListener('click', this._onClick);
|
|
58
|
+
this._input?.removeEventListener('input', this._onInput);
|
|
59
|
+
clearTimeout(this._debounce);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_emitChange() { this.emit('nk-change', { filters: [...this._filters], search: this._input.value }); }
|
|
63
|
+
onAttributeChanged() { this._sync(); }
|
|
64
|
+
|
|
65
|
+
get filters() { return this._filters; }
|
|
66
|
+
set filters(v) { this._filters = Array.isArray(v) ? v : []; this._sync(); }
|
|
67
|
+
get value() { return this._input?.value ?? ''; }
|
|
68
|
+
set value(v) { if (this._input) this._input.value = v ?? ''; }
|
|
69
|
+
/** Applies filters + search to rows: equality per filter key, substring search over every string field. */
|
|
70
|
+
apply(rows) {
|
|
71
|
+
const q = this.value.trim().toLowerCase();
|
|
72
|
+
return rows.filter(r => this._filters.every(f => r[f.key] === f.value) && (!q || Object.values(r).some(v => String(typeof v === 'object' && v ? v.name ?? '' : v ?? '').toLowerCase().includes(q))));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
customElements.define('nk-filter-bar', NkFilterBar);
|
|
77
|
+
|
|
78
|
+
export { NkFilterBar };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-heading level="2">Section</nk-heading> → <h2 class="nk-heading">
|
|
4
|
+
// The heading level is real markup (h1–h4), so the outline stays honest.
|
|
5
|
+
class NkHeading extends NkElement {
|
|
6
|
+
static get observedAttributes() { return ['level']; }
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
this._build();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_build() {
|
|
13
|
+
const level = Math.min(4, Math.max(1, Number(this.getAttribute('level')) || 2));
|
|
14
|
+
const h = this.createElement(`h${level}`, ['nk-heading']);
|
|
15
|
+
h.appendChild(document.createElement('slot'));
|
|
16
|
+
if (this._heading) this._heading.replaceWith(h);
|
|
17
|
+
else this._wrapper.appendChild(h);
|
|
18
|
+
this._heading = h;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onAttributeChanged() { this._build(); }
|
|
22
|
+
|
|
23
|
+
get level() { return Number(this.getAttribute('level')) || 2; }
|
|
24
|
+
set level(v) { this.setAttribute('level', v); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
customElements.define('nk-heading', NkHeading);
|
|
28
|
+
|
|
29
|
+
export { NkHeading };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { a as NkFormElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-input name="title" value="…" placeholder="…" wide></nk-input> → <input class="nk-input wide">
|
|
4
|
+
// Takes part in the surrounding <form> through ElementInternals.
|
|
5
|
+
const FORWARDED = ['type', 'placeholder', 'name', 'min', 'max', 'step', 'autocomplete', 'pattern', 'maxlength', 'minlength', 'inputmode', 'list', 'aria-label'];
|
|
6
|
+
const BOOLEANS = ['required', 'disabled', 'readonly'];
|
|
7
|
+
|
|
8
|
+
class NkInput extends NkFormElement {
|
|
9
|
+
static get observedAttributes() { return ['value', 'wide', ...FORWARDED, ...BOOLEANS]; }
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
this._input = this.createElement('input', ['nk-input']);
|
|
13
|
+
for (const attr of FORWARDED) this._forward(attr);
|
|
14
|
+
for (const attr of BOOLEANS) this._forward(attr);
|
|
15
|
+
this._input.value = this.getAttribute('value') ?? '';
|
|
16
|
+
this._defaultValue = this._input.value;
|
|
17
|
+
this._syncWide();
|
|
18
|
+
this._wrapper.appendChild(this._input);
|
|
19
|
+
this._syncFormValue();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_forward(attr) {
|
|
23
|
+
if (BOOLEANS.includes(attr)) {
|
|
24
|
+
this._input[attr === 'readonly' ? 'readOnly' : attr] = this.getBoolAttr(attr);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const v = this.getAttribute(attr);
|
|
28
|
+
if (v === null) this._input.removeAttribute(attr);
|
|
29
|
+
else this._input.setAttribute(attr, v);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_syncWide() {
|
|
33
|
+
this._input.classList.toggle('wide', this.getBoolAttr('wide'));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setupEvents() {
|
|
37
|
+
this._onInput = () => {
|
|
38
|
+
this._syncFormValue();
|
|
39
|
+
this.emit('nk-input', { value: this._input.value, name: this.name });
|
|
40
|
+
};
|
|
41
|
+
this._onChange = () => {
|
|
42
|
+
this._syncFormValue();
|
|
43
|
+
this.emit('nk-change', { value: this._input.value, name: this.name });
|
|
44
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
45
|
+
};
|
|
46
|
+
this._input.addEventListener('input', this._onInput);
|
|
47
|
+
this._input.addEventListener('change', this._onChange);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
teardownEvents() {
|
|
51
|
+
this._input?.removeEventListener('input', this._onInput);
|
|
52
|
+
this._input?.removeEventListener('change', this._onChange);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onAttributeChanged(name) {
|
|
56
|
+
if (!this._input) return;
|
|
57
|
+
if (name === 'value') { this._input.value = this.getAttribute('value') ?? ''; this._syncFormValue(); return; }
|
|
58
|
+
if (name === 'wide') { this._syncWide(); return; }
|
|
59
|
+
this._forward(name);
|
|
60
|
+
this._syncFormValue();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_syncFormValue() {
|
|
64
|
+
this.setFormValue(this._input.value);
|
|
65
|
+
this.syncValidityFrom(this._input);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
resetValue() {
|
|
69
|
+
this._input.value = this.getAttribute('value') ?? '';
|
|
70
|
+
this._syncFormValue();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
restoreValue(state) {
|
|
74
|
+
if (typeof state === 'string') { this._input.value = state; this._syncFormValue(); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
onFormDisabled(disabled) {
|
|
78
|
+
this._input.disabled = disabled || this.getBoolAttr('disabled');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
focus(options) { this._input?.focus(options); }
|
|
82
|
+
blur() { this._input?.blur(); }
|
|
83
|
+
select() { this._input?.select(); }
|
|
84
|
+
|
|
85
|
+
get value() { return this._input?.value ?? (this.getAttribute('value') ?? ''); }
|
|
86
|
+
set value(v) {
|
|
87
|
+
if (this._input) { this._input.value = v ?? ''; this._syncFormValue(); }
|
|
88
|
+
else this.setAttribute('value', v ?? '');
|
|
89
|
+
}
|
|
90
|
+
get name() { return this.getAttribute('name'); }
|
|
91
|
+
set name(v) { this.setAttribute('name', v); }
|
|
92
|
+
get type() { return this.getAttribute('type') || 'text'; }
|
|
93
|
+
set type(v) { this.setAttribute('type', v); }
|
|
94
|
+
get placeholder() { return this.getAttribute('placeholder'); }
|
|
95
|
+
set placeholder(v) { this.setAttribute('placeholder', v); }
|
|
96
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
97
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
98
|
+
get required() { return this.getBoolAttr('required'); }
|
|
99
|
+
set required(v) { this.setBoolAttr('required', v); }
|
|
100
|
+
get readonly() { return this.getBoolAttr('readonly'); }
|
|
101
|
+
set readonly(v) { this.setBoolAttr('readonly', v); }
|
|
102
|
+
get wide() { return this.getBoolAttr('wide'); }
|
|
103
|
+
set wide(v) { this.setBoolAttr('wide', v); }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
customElements.define('nk-input', NkInput);
|
|
107
|
+
|
|
108
|
+
export { NkInput };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-kbd>⌘</nk-kbd> <nk-kbd>K</nk-kbd> → <kbd class="nk-kbd">
|
|
4
|
+
class NkKbd extends NkElement {
|
|
5
|
+
|
|
6
|
+
render() {
|
|
7
|
+
const kbd = this.createElement('kbd', ['nk-kbd']);
|
|
8
|
+
kbd.appendChild(document.createElement('slot'));
|
|
9
|
+
this._wrapper.appendChild(kbd);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
customElements.define('nk-kbd', NkKbd);
|
|
14
|
+
|
|
15
|
+
export { NkKbd };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-member-list><nk-member-row …></nk-member-row>…</nk-member-list>
|
|
4
|
+
// → <div class="nk-member-list">…</div>; the last row gets `last` (no border).
|
|
5
|
+
class NkMemberList extends NkElement {
|
|
6
|
+
render() {
|
|
7
|
+
const box = this.createElement('div', ['nk-member-list']);
|
|
8
|
+
this._slot = document.createElement('slot');
|
|
9
|
+
box.appendChild(this._slot);
|
|
10
|
+
this._wrapper.appendChild(box);
|
|
11
|
+
this._mark();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_mark() {
|
|
15
|
+
const rows = this._slot.assignedElements().filter(el => el.localName === 'nk-member-row');
|
|
16
|
+
rows.forEach((row, i) => { if (i === rows.length - 1) row.setAttribute('last', ''); else row.removeAttribute('last'); });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setupEvents() {
|
|
20
|
+
this._onSlot = () => this._mark();
|
|
21
|
+
this._slot.addEventListener('slotchange', this._onSlot);
|
|
22
|
+
this._mark();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
teardownEvents() { this._slot?.removeEventListener('slotchange', this._onSlot); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
customElements.define('nk-member-list', NkMemberList);
|
|
29
|
+
|
|
30
|
+
export { NkMemberList };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { N as NkElement } from './shared/base-6gEcoatG.js';
|
|
2
|
+
|
|
3
|
+
// <nk-member-row name="Sara Lindt" mail="sara@…" avatar="SL" color="#448361">
|
|
4
|
+
// <nk-select slot="role" compact>…</nk-select>
|
|
5
|
+
// </nk-member-row>
|
|
6
|
+
// → <div class="nk-member-row"><span class="mini-avatar">SL</span><div>Sara Lindt<div class="m-mail">…</div></div><select …></div>
|
|
7
|
+
class NkMemberRow extends NkElement {
|
|
8
|
+
static get observedAttributes() { return ['name', 'mail', 'avatar', 'color', 'last']; }
|
|
9
|
+
|
|
10
|
+
render() {
|
|
11
|
+
this._row = this.createElement('div', ['nk-member-row']);
|
|
12
|
+
this._avatar = this.createElement('span', ['mini-avatar']);
|
|
13
|
+
const avatarSlot = this.createElement('slot', [], { name: 'avatar' });
|
|
14
|
+
avatarSlot.appendChild(this._avatar);
|
|
15
|
+
const text = document.createElement('div');
|
|
16
|
+
this._name = document.createTextNode('');
|
|
17
|
+
this._mail = this.createElement('div', ['m-mail']);
|
|
18
|
+
text.append(this._name, this._mail);
|
|
19
|
+
// `.nk-member-row .nk-select { margin-left: auto }` cannot reach a select
|
|
20
|
+
// inside another shadow root, and a display:contents host takes no margin –
|
|
21
|
+
// so the role slot sits in a box that carries that rule.
|
|
22
|
+
const role = document.createElement('div');
|
|
23
|
+
role.style.marginLeft = 'auto';
|
|
24
|
+
role.appendChild(this.createElement('slot', [], { name: 'role' }));
|
|
25
|
+
this._row.append(avatarSlot, text, role, document.createElement('slot'));
|
|
26
|
+
this._wrapper.appendChild(this._row);
|
|
27
|
+
this._sync();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_sync() {
|
|
31
|
+
this._name.data = this.getAttribute('name') || '';
|
|
32
|
+
const mail = this.getAttribute('mail');
|
|
33
|
+
this._mail.textContent = mail || '';
|
|
34
|
+
this._mail.style.display = mail ? '' : 'none';
|
|
35
|
+
const avatar = this.getAttribute('avatar');
|
|
36
|
+
this._avatar.textContent = avatar || (this.getAttribute('name') || '').split(/\s+/).map(w => w[0]).join('').slice(0, 2).toUpperCase();
|
|
37
|
+
this._avatar.style.background = this.getAttribute('color') || 'var(--nk-text-tertiary)';
|
|
38
|
+
this._row.classList.toggle('last', this.getBoolAttr('last'));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onAttributeChanged() { this._sync(); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
customElements.define('nk-member-row', NkMemberRow);
|
|
45
|
+
|
|
46
|
+
export { NkMemberRow };
|