@jungherz-de/notionkit-elements 1.1.0 → 1.2.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 (84) hide show
  1. package/README.md +10 -10
  2. package/dist/components/nk-ai-input-row.js +1 -1
  3. package/dist/components/nk-ai-msg.js +1 -1
  4. package/dist/components/nk-ai-thread.js +1 -1
  5. package/dist/components/nk-app.js +1 -1
  6. package/dist/components/nk-avatar-group.js +1 -1
  7. package/dist/components/nk-banner.js +1 -1
  8. package/dist/components/nk-block-host.js +1 -1
  9. package/dist/components/nk-board-view.js +1 -1
  10. package/dist/components/nk-breadcrumb.js +1 -1
  11. package/dist/components/nk-btn.js +1 -1
  12. package/dist/components/nk-callout.js +1 -1
  13. package/dist/components/nk-check.js +9 -3
  14. package/dist/components/nk-cmdk.js +1 -1
  15. package/dist/components/nk-code.js +1 -1
  16. package/dist/components/nk-comment.js +1 -1
  17. package/dist/components/nk-comments.js +1 -1
  18. package/dist/components/nk-danger-zone.js +1 -1
  19. package/dist/components/nk-database.js +1 -1
  20. package/dist/components/nk-divider.js +1 -1
  21. package/dist/components/nk-emoji-picker.js +1 -1
  22. package/dist/components/nk-empty.js +1 -1
  23. package/dist/components/nk-field.js +45 -7
  24. package/dist/components/nk-fields.js +22 -0
  25. package/dist/components/nk-filter-bar.js +1 -1
  26. package/dist/components/nk-heading.js +1 -1
  27. package/dist/components/nk-input.js +1 -1
  28. package/dist/components/nk-kbd.js +1 -1
  29. package/dist/components/nk-member-list.js +1 -1
  30. package/dist/components/nk-member-row.js +1 -1
  31. package/dist/components/nk-mention.js +1 -1
  32. package/dist/components/nk-menu-item.js +1 -1
  33. package/dist/components/nk-menu.js +1 -1
  34. package/dist/components/nk-modal.js +1 -1
  35. package/dist/components/nk-model-card.js +1 -1
  36. package/dist/components/nk-page-actions.js +1 -1
  37. package/dist/components/nk-page-cover.js +1 -1
  38. package/dist/components/nk-page-title.js +1 -1
  39. package/dist/components/nk-page.js +1 -1
  40. package/dist/components/nk-pop.js +1 -1
  41. package/dist/components/nk-profile-row.js +1 -1
  42. package/dist/components/nk-progress.js +1 -1
  43. package/dist/components/nk-quote.js +1 -1
  44. package/dist/components/nk-radio.js +9 -3
  45. package/dist/components/nk-section-label.js +1 -1
  46. package/dist/components/nk-segmented.js +1 -1
  47. package/dist/components/nk-select.js +5 -2
  48. package/dist/components/nk-settings-pane.js +1 -1
  49. package/dist/components/nk-settings-user.js +1 -1
  50. package/dist/components/nk-sidebar.js +1 -1
  51. package/dist/components/nk-skeleton.js +1 -1
  52. package/dist/components/nk-slider.js +1 -1
  53. package/dist/components/nk-stat.js +1 -1
  54. package/dist/components/nk-stats.js +1 -1
  55. package/dist/components/nk-switch.js +32 -4
  56. package/dist/components/nk-synced.js +1 -1
  57. package/dist/components/nk-tab-bar-item.js +1 -1
  58. package/dist/components/nk-tab-bar.js +1 -1
  59. package/dist/components/nk-tab.js +1 -1
  60. package/dist/components/nk-table-view.js +1 -1
  61. package/dist/components/nk-tabs.js +1 -1
  62. package/dist/components/nk-tag.js +1 -1
  63. package/dist/components/nk-template-btn.js +1 -1
  64. package/dist/components/nk-textarea.js +1 -1
  65. package/dist/components/nk-theme-toggle.js +1 -1
  66. package/dist/components/nk-toast.js +1 -1
  67. package/dist/components/nk-todo.js +1 -1
  68. package/dist/components/nk-toggle.js +1 -1
  69. package/dist/components/nk-topbar.js +1 -1
  70. package/dist/components/nk-tree-item.js +1 -1
  71. package/dist/components/nk-tree.js +1 -1
  72. package/dist/components/nk-workspace-switcher.js +1 -1
  73. package/dist/components/shared/{base-BavgHsS-.js → base-EVtzqKmc.js} +2 -2
  74. package/dist/notionkit-elements.esm.js +117 -17
  75. package/dist/notionkit-elements.js +117 -16
  76. package/dist/notionkit-elements.min.js +1 -1
  77. package/package.json +3 -3
  78. package/src/components/forms/nk-check.js +8 -2
  79. package/src/components/forms/nk-field.js +44 -6
  80. package/src/components/forms/nk-fields.js +21 -0
  81. package/src/components/forms/nk-radio.js +8 -2
  82. package/src/components/forms/nk-select.js +4 -1
  83. package/src/components/forms/nk-switch.js +31 -3
  84. package/src/index.js +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungherz-de/notionkit-elements",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Vanilla JavaScript Web Components wrapping the NotionKit CSS foundation – <nk-*> custom elements with Shadow DOM, automatic theme sync and native form participation.",
5
5
  "license": "MIT",
6
6
  "author": "Jungherz GmbH <info@jungherz.com> (https://www.jungherz.com)",
@@ -58,10 +58,10 @@
58
58
  "dark-mode"
59
59
  ],
60
60
  "peerDependencies": {
61
- "@jungherz-de/notionkit": ">=1.2.0"
61
+ "@jungherz-de/notionkit": ">=1.3.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@jungherz-de/notionkit": "^1.2.0",
64
+ "@jungherz-de/notionkit": "^1.3.0",
65
65
  "@rollup/plugin-node-resolve": "^15.0.0",
66
66
  "@rollup/plugin-terser": "^0.4.0",
67
67
  "rollup": "^4.0.0",
@@ -3,15 +3,18 @@ import { NkFormElement } from '../../base.js';
3
3
  // <nk-check name="mail" value="weekly" checked>Weekly digest</nk-check>
4
4
  // → <label class="nk-check"><input type="checkbox"> Weekly digest</label>
5
5
  class NkCheck extends NkFormElement {
6
- static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'indeterminate', 'required']; }
6
+ static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'text', 'indeterminate', 'required']; }
7
7
 
8
8
  render() {
9
9
  const label = this.createElement('label', ['nk-check']);
10
10
  this._input = this.createElement('input', [], { type: 'checkbox' });
11
+ const slot = document.createElement('slot');
12
+ this._text = document.createTextNode('');
13
+ slot.appendChild(this._text);
11
14
  this._apply();
12
15
  this._defaultChecked = this.getBoolAttr('checked');
13
16
  label.appendChild(this._input);
14
- label.appendChild(document.createElement('slot'));
17
+ label.appendChild(slot);
15
18
  this._wrapper.appendChild(label);
16
19
  this._syncFormValue();
17
20
  }
@@ -24,6 +27,7 @@ class NkCheck extends NkFormElement {
24
27
  const name = this.getAttribute('name');
25
28
  name ? this._input.setAttribute('name', name) : this._input.removeAttribute('name');
26
29
  this._input.value = this.getAttribute('value') || 'on';
30
+ if (this._text) this._text.data = this.getAttribute('text') ?? '';
27
31
  }
28
32
 
29
33
  setupEvents() {
@@ -68,6 +72,8 @@ class NkCheck extends NkFormElement {
68
72
  set disabled(v) { this.setBoolAttr('disabled', v); }
69
73
  get name() { return this.getAttribute('name'); }
70
74
  set name(v) { this.setAttribute('name', v); }
75
+ get text() { return this.getAttribute('text'); }
76
+ set text(v) { v == null ? this.removeAttribute('text') : this.setAttribute('text', v); }
71
77
  get value() { return this.getAttribute('value') || 'on'; }
72
78
  set value(v) { this.setAttribute('value', v); }
73
79
  }
@@ -7,11 +7,19 @@ import { NkElement } from '../../base.js';
7
7
  // <div class="f-control">…</div></div>
8
8
  // Rich label / description content goes through slot="label" / slot="desc"
9
9
  // (twins `.nk-field ::slotted([slot="label"])` etc. in notionkit.css).
10
+ //
11
+ // `stacked` puts the label above a full-width control, `compact` shrinks the
12
+ // label and drops the row padding; inside <nk-fields> both are on by
13
+ // default. A stacked field sets `wide` on its nk-input / nk-textarea /
14
+ // nk-select, since the stylesheet's `.nk-field.stacked .nk-input` cannot
15
+ // reach into the control's shadow root.
16
+ const WIDE = ['nk-input', 'nk-textarea', 'nk-select'];
17
+
10
18
  class NkField extends NkElement {
11
- static get observedAttributes() { return ['label', 'desc']; }
19
+ static get observedAttributes() { return ['label', 'desc', 'stacked', 'compact']; }
12
20
 
13
21
  render() {
14
- const field = this.createElement('div', ['nk-field']);
22
+ this._field = this.createElement('div', ['nk-field']);
15
23
  const text = document.createElement('div');
16
24
  this._label = this.createElement('div', ['f-label']);
17
25
  const labelSlot = this.createElement('slot', [], { name: 'label' });
@@ -22,21 +30,51 @@ class NkField extends NkElement {
22
30
  text.appendChild(labelSlot);
23
31
  text.appendChild(descSlot);
24
32
  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);
33
+ this._slot = document.createElement('slot');
34
+ control.appendChild(this._slot);
35
+ this._field.appendChild(text);
36
+ this._field.appendChild(control);
37
+ this._wrapper.appendChild(this._field);
38
+ this._widened = new Set();
29
39
  this._sync();
30
40
  }
31
41
 
42
+ setupEvents() {
43
+ this._onSlot = () => this._syncWide();
44
+ this._slot.addEventListener('slotchange', this._onSlot);
45
+ this._sync();
46
+ }
47
+
48
+ teardownEvents() {
49
+ this._slot?.removeEventListener('slotchange', this._onSlot);
50
+ }
51
+
32
52
  onAttributeChanged() { this._sync(); }
33
53
 
54
+ get stacked() { return this.getBoolAttr('stacked') || !!this.closest('nk-fields'); }
55
+ set stacked(v) { this.setBoolAttr('stacked', v); }
56
+ get compact() { return this.getBoolAttr('compact') || !!this.closest('nk-fields'); }
57
+ set compact(v) { this.setBoolAttr('compact', v); }
58
+
34
59
  _sync() {
35
60
  if (!this._label) return;
36
61
  this._label.textContent = this.getAttribute('label') || '';
37
62
  const desc = this.getAttribute('desc');
38
63
  this._desc.textContent = desc || '';
39
64
  this._desc.style.display = desc ? '' : 'none';
65
+ this._field.classList.toggle('stacked', this.stacked);
66
+ this._field.classList.toggle('compact', this.compact);
67
+ this._syncWide();
68
+ }
69
+
70
+ _syncWide() {
71
+ if (!this._slot) return;
72
+ const stacked = this.stacked;
73
+ for (const el of this._slot.assignedElements()) {
74
+ if (!WIDE.includes(el.localName)) continue;
75
+ if (stacked && !el.hasAttribute('wide')) { el.setAttribute('wide', ''); this._widened.add(el); }
76
+ else if (!stacked && this._widened.has(el)) { el.removeAttribute('wide'); this._widened.delete(el); }
77
+ }
40
78
  }
41
79
 
42
80
  get label() { return this.getAttribute('label'); }
@@ -0,0 +1,21 @@
1
+ import { NkElement } from '../../base.js';
2
+
3
+ // <nk-fields>
4
+ // <nk-field label="Name"><nk-input name="name"></nk-input></nk-field>
5
+ // <nk-field label="Email"><nk-input name="email"></nk-input></nk-field>
6
+ // </nk-fields>
7
+ // → <div class="nk-fields">…</div>
8
+ // A grid of short fields side by side, wrapping as the width allows. Every
9
+ // nk-field inside renders itself stacked and compact (12px label above a
10
+ // full-width control) – the class markup does the same through
11
+ // `.nk-fields > .nk-field`, which cannot cross the shadow boundary here.
12
+ class NkFields extends NkElement {
13
+ render() {
14
+ const grid = this.createElement('div', ['nk-fields']);
15
+ grid.appendChild(document.createElement('slot'));
16
+ this._wrapper.appendChild(grid);
17
+ }
18
+ }
19
+
20
+ customElements.define('nk-fields', NkFields);
21
+ export { NkFields };
@@ -29,17 +29,20 @@ function syncGroupTabIndex(group) {
29
29
  }
30
30
 
31
31
  class NkRadio extends NkFormElement {
32
- static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'required']; }
32
+ static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'text', 'required']; }
33
33
 
34
34
  render() {
35
35
  const label = this.createElement('label', ['nk-check']);
36
36
  this._input = this.createElement('input', [], { type: 'radio' });
37
+ const slot = document.createElement('slot');
38
+ this._text = document.createTextNode('');
39
+ slot.appendChild(this._text);
37
40
  // `name` on the inner input is inert (it is alone in its tree); it stays
38
41
  // as a devtools courtesy. The group above is the real one.
39
42
  this._apply();
40
43
  this._defaultChecked = this.getBoolAttr('checked');
41
44
  label.appendChild(this._input);
42
- label.appendChild(document.createElement('slot'));
45
+ label.appendChild(slot);
43
46
  this._wrapper.appendChild(label);
44
47
  this._syncFormValue();
45
48
  // Elements upgrade in document order, so the last `checked` one in the
@@ -55,6 +58,7 @@ class NkRadio extends NkFormElement {
55
58
  const name = this.getAttribute('name');
56
59
  name ? this._input.setAttribute('name', name) : this._input.removeAttribute('name');
57
60
  this._input.value = this.getAttribute('value') || '';
61
+ if (this._text) this._text.data = this.getAttribute('text') ?? '';
58
62
  }
59
63
 
60
64
  setupEvents() {
@@ -151,6 +155,8 @@ class NkRadio extends NkFormElement {
151
155
  set disabled(v) { this.setBoolAttr('disabled', v); }
152
156
  get name() { return this.getAttribute('name'); }
153
157
  set name(v) { this.setAttribute('name', v); }
158
+ get text() { return this.getAttribute('text'); }
159
+ set text(v) { v == null ? this.removeAttribute('text') : this.setAttribute('text', v); }
154
160
  get value() { return this.getAttribute('value') || ''; }
155
161
  set value(v) { this.setAttribute('value', v); }
156
162
  }
@@ -9,7 +9,7 @@ import { NkFormElement } from '../../base.js';
9
9
  // MutationObserver keeps the copy in step when a framework swaps them; the
10
10
  // empty string is a valid value; the live selection survives a rebuild.
11
11
  class NkSelect extends NkFormElement {
12
- static get observedAttributes() { return ['value', 'name', 'disabled', 'required', 'compact', 'aria-label']; }
12
+ static get observedAttributes() { return ['value', 'name', 'disabled', 'required', 'compact', 'wide', 'aria-label']; }
13
13
  static get observesLightDom() { return true; }
14
14
 
15
15
  render() {
@@ -30,6 +30,7 @@ class NkSelect extends NkFormElement {
30
30
  this._select.disabled = this.getBoolAttr('disabled') || !!this._formDisabled;
31
31
  this._select.required = this.getBoolAttr('required');
32
32
  this._select.classList.toggle('compact', this.getBoolAttr('compact'));
33
+ this._select.classList.toggle('wide', this.getBoolAttr('wide'));
33
34
  }
34
35
 
35
36
  projectLightDom() {
@@ -120,6 +121,8 @@ class NkSelect extends NkFormElement {
120
121
  set required(v) { this.setBoolAttr('required', v); }
121
122
  get compact() { return this.getBoolAttr('compact'); }
122
123
  set compact(v) { this.setBoolAttr('compact', v); }
124
+ get wide() { return this.getBoolAttr('wide'); }
125
+ set wide(v) { this.setBoolAttr('wide', v); }
123
126
  }
124
127
 
125
128
  customElements.define('nk-select', NkSelect);
@@ -3,14 +3,28 @@ import { NkFormElement } from '../../base.js';
3
3
  // <nk-switch name="notify" checked></nk-switch>
4
4
  // → <button class="nk-switch" role="switch" aria-checked="true">
5
5
  // notionkit.css styles the state through [aria-checked]; the toggling is here.
6
+ //
7
+ // <nk-switch checked>Waiting</nk-switch> (or text="Waiting")
8
+ // → <label class="nk-switch-label"><button class="nk-switch" …></button><span>Waiting</span></label>
9
+ // Visible text beside the switch, for several switches in one row where no
10
+ // nk-field row names them. Without text the label wrapper is layout-
11
+ // transparent (display: contents), so the bare button stays the bare button.
6
12
  class NkSwitch extends NkFormElement {
7
- static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'label']; }
13
+ static get observedAttributes() { return ['checked', 'disabled', 'name', 'value', 'label', 'text']; }
8
14
 
9
15
  render() {
16
+ this._row = this.createElement('label', ['nk-switch-label']);
10
17
  this._btn = this.createElement('button', ['nk-switch'], { type: 'button', role: 'switch' });
18
+ this._text = document.createElement('span');
19
+ this._slot = document.createElement('slot');
20
+ this._textNode = document.createTextNode('');
21
+ this._slot.appendChild(this._textNode);
22
+ this._text.appendChild(this._slot);
23
+ this._row.append(this._btn, this._text);
11
24
  this._defaultChecked = this.getBoolAttr('checked');
12
25
  this._apply();
13
- this._wrapper.appendChild(this._btn);
26
+ this._syncText();
27
+ this._wrapper.appendChild(this._row);
14
28
  this._syncFormValue();
15
29
  }
16
30
 
@@ -21,13 +35,24 @@ class NkSwitch extends NkFormElement {
21
35
  label ? this._btn.setAttribute('aria-label', label) : this._btn.removeAttribute('aria-label');
22
36
  }
23
37
 
38
+ _syncText() {
39
+ this._textNode.data = this.getAttribute('text') ?? '';
40
+ const hasText = !!(this.getAttribute('text') || this._slot.assignedNodes().some(n => n.nodeType === Node.ELEMENT_NODE || n.data.trim()));
41
+ this._text.hidden = !hasText;
42
+ this._row.style.display = hasText ? '' : 'contents';
43
+ }
44
+
24
45
  setupEvents() {
25
46
  this._onClick = () => this.toggle();
47
+ this._onSlot = () => this._syncText();
26
48
  this._btn.addEventListener('click', this._onClick);
49
+ this._slot.addEventListener('slotchange', this._onSlot);
50
+ this._syncText();
27
51
  }
28
52
 
29
53
  teardownEvents() {
30
54
  this._btn?.removeEventListener('click', this._onClick);
55
+ this._slot?.removeEventListener('slotchange', this._onSlot);
31
56
  }
32
57
 
33
58
  toggle() {
@@ -37,8 +62,9 @@ class NkSwitch extends NkFormElement {
37
62
  this.dispatchEvent(new Event('change', { bubbles: true }));
38
63
  }
39
64
 
40
- onAttributeChanged() {
65
+ onAttributeChanged(name) {
41
66
  if (!this._btn) return;
67
+ if (name === 'text') { this._syncText(); return; }
42
68
  this._apply();
43
69
  this._syncFormValue();
44
70
  }
@@ -62,6 +88,8 @@ class NkSwitch extends NkFormElement {
62
88
  set name(v) { this.setAttribute('name', v); }
63
89
  get value() { return this.getAttribute('value') || 'on'; }
64
90
  set value(v) { this.setAttribute('value', v); }
91
+ get text() { return this.getAttribute('text'); }
92
+ set text(v) { v == null ? this.removeAttribute('text') : this.setAttribute('text', v); }
65
93
  }
66
94
 
67
95
  customElements.define('nk-switch', NkSwitch);
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ export { NkCheck } from './components/forms/nk-check.js';
16
16
  export { NkRadio } from './components/forms/nk-radio.js';
17
17
  export { NkSlider } from './components/forms/nk-slider.js';
18
18
  export { NkField } from './components/forms/nk-field.js';
19
+ export { NkFields } from './components/forms/nk-fields.js';
19
20
 
20
21
  // Wave 1 – content
21
22
  export { NkTag } from './components/content/nk-tag.js';