@jungherz-de/notionkit-elements 1.16.0 → 1.17.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/CHANGELOG.md +17 -0
- package/README.md +64 -6
- package/SKILL.md +59 -53
- package/dist/components/base.js +102 -3
- package/dist/components/nk-ai-input-row.js +6 -3
- package/dist/components/nk-board-view.js +2 -2
- package/dist/components/nk-breadcrumb.js +2 -1
- package/dist/components/nk-calendar-view.js +8 -5
- package/dist/components/nk-calendar.js +10 -7
- package/dist/components/nk-cmdk.js +6 -2
- package/dist/components/nk-comments.js +5 -3
- package/dist/components/nk-copy-field.js +6 -3
- package/dist/components/nk-emoji-picker.js +6 -2
- package/dist/components/nk-filter-bar.js +10 -8
- package/dist/components/nk-gallery-view.js +29 -7
- package/dist/components/nk-image-picker.js +7 -3
- package/dist/components/nk-list-view.js +3 -2
- package/dist/components/nk-modal.js +3 -1
- package/dist/components/nk-page-title.js +2 -1
- package/dist/components/nk-page.js +3 -1
- package/dist/components/nk-peek.js +6 -3
- package/dist/components/nk-sidebar.js +5 -2
- package/dist/components/nk-steps.js +20 -4
- package/dist/components/nk-synced.js +2 -1
- package/dist/components/nk-table-view.js +3 -2
- package/dist/components/nk-theme-toggle.js +5 -2
- package/dist/components/nk-tree-item.js +3 -1
- package/dist/components/shared/{property-cell-BSVcETss.js → property-cell-DcsfnUNF.js} +15 -5
- package/dist/notionkit-elements.esm.js +265 -70
- package/dist/notionkit-elements.esm.min.js +1 -1
- package/dist/notionkit-elements.esm.min.js.map +1 -1
- package/dist/notionkit-elements.js +267 -69
- package/dist/notionkit-elements.min.js +1 -1
- package/package.json +3 -3
- package/src/base.js +32 -2
- package/src/components/data/nk-ai-input-row.js +6 -3
- package/src/components/data/nk-board-view.js +2 -2
- package/src/components/data/nk-calendar-view.js +7 -4
- package/src/components/data/nk-comments.js +5 -3
- package/src/components/data/nk-filter-bar.js +10 -8
- package/src/components/data/nk-gallery-view.js +29 -7
- package/src/components/data/nk-list-view.js +2 -1
- package/src/components/data/nk-table-view.js +2 -1
- package/src/components/forms/nk-calendar.js +10 -7
- package/src/components/forms/nk-copy-field.js +6 -3
- package/src/components/forms/nk-image-picker.js +7 -3
- package/src/components/overlays/nk-cmdk.js +6 -2
- package/src/components/overlays/nk-emoji-picker.js +6 -2
- package/src/components/overlays/nk-modal.js +3 -1
- package/src/components/overlays/nk-peek.js +6 -3
- package/src/components/page/nk-page-title.js +2 -1
- package/src/components/page/nk-page.js +3 -1
- package/src/components/page/nk-steps.js +20 -4
- package/src/components/page/nk-synced.js +2 -1
- package/src/components/shell/nk-breadcrumb.js +2 -1
- package/src/components/shell/nk-sidebar.js +5 -2
- package/src/components/shell/nk-theme-toggle.js +5 -2
- package/src/components/shell/nk-tree-item.js +3 -1
- package/src/index.js +5 -1
- package/src/util/property-cell.js +14 -4
- package/src/util/strings.js +73 -0
|
@@ -11,8 +11,8 @@ class NkAiInputRow extends NkElement {
|
|
|
11
11
|
this._row = this.createElement('div', ['nk-ai-input-row']);
|
|
12
12
|
this._icon = document.createElement('span');
|
|
13
13
|
this._icon.style.fontSize = '14px';
|
|
14
|
-
this._input = this.createElement('input', [], { type: 'text'
|
|
15
|
-
this._btn = this.createElement('button', ['nk-ai-send'], { type: 'button'
|
|
14
|
+
this._input = this.createElement('input', [], { type: 'text' });
|
|
15
|
+
this._btn = this.createElement('button', ['nk-ai-send'], { type: 'button' });
|
|
16
16
|
this._btn.textContent = '↑';
|
|
17
17
|
this._row.append(this._icon, this._input, this._btn);
|
|
18
18
|
this._wrapper.appendChild(this._row);
|
|
@@ -20,7 +20,9 @@ class NkAiInputRow extends NkElement {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
_sync() {
|
|
23
|
-
this._input.placeholder = this.getAttribute('placeholder') || '
|
|
23
|
+
this._input.placeholder = this.getAttribute('placeholder') || this.str('askPlaceholder');
|
|
24
|
+
this._input.setAttribute('aria-label', this.str('message'));
|
|
25
|
+
this._btn.setAttribute('aria-label', this.str('send'));
|
|
24
26
|
if (this.hasAttribute('value') && this._input.value !== this.getAttribute('value')) this._input.value = this.getAttribute('value');
|
|
25
27
|
this._input.disabled = this._btn.disabled = this.getBoolAttr('disabled');
|
|
26
28
|
const icon = this.getAttribute('icon') ?? '✨';
|
|
@@ -48,6 +50,7 @@ class NkAiInputRow extends NkElement {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
onAttributeChanged() { this._sync(); }
|
|
53
|
+
onStringsChanged() { this._sync(); }
|
|
51
54
|
focus(o) { this._input?.focus(o); }
|
|
52
55
|
get value() { return this._input?.value ?? ''; }
|
|
53
56
|
set value(v) { if (this._input) this._input.value = v ?? ''; }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NkElement } from './base.js';
|
|
2
|
-
import { t as tagFor, a as textOf, f as formatDate, r as renderPropertyCell } from './shared/property-cell-
|
|
2
|
+
import { t as tagFor, a as textOf, f as formatPercent, b as formatDate, r as renderPropertyCell } from './shared/property-cell-DcsfnUNF.js';
|
|
3
3
|
import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
4
4
|
import './shared/avatar-CzPZN3uP.js';
|
|
5
5
|
import './shared/dates-a32DcW1l.js';
|
|
@@ -58,7 +58,7 @@ class NkBoardView extends NkElement {
|
|
|
58
58
|
const v = row[c.key];
|
|
59
59
|
if (v === undefined || v === null || v === '') continue;
|
|
60
60
|
const s = document.createElement('span');
|
|
61
|
-
if (c.type === 'progress') s.textContent = `▰ ${v}
|
|
61
|
+
if (c.type === 'progress') s.textContent = `▰ ${formatPercent(c, v)}`;
|
|
62
62
|
else if (c.type === 'date') s.textContent = `📅 ${formatDate(c, v)}`;
|
|
63
63
|
else s.appendChild(renderPropertyCell(c, v, row));
|
|
64
64
|
meta.appendChild(s);
|
|
@@ -17,7 +17,7 @@ class NkBreadcrumb extends NkElement {
|
|
|
17
17
|
static get observesLightDom() { return true; }
|
|
18
18
|
|
|
19
19
|
render() {
|
|
20
|
-
this._nav = this.createElement('nav', ['nk-breadcrumb'], { 'aria-label': '
|
|
20
|
+
this._nav = this.createElement('nav', ['nk-breadcrumb'], { 'aria-label': this.str('breadcrumb') });
|
|
21
21
|
this._wrapper.appendChild(this._nav);
|
|
22
22
|
this.projectLightDom();
|
|
23
23
|
}
|
|
@@ -69,6 +69,7 @@ class NkBreadcrumb extends NkElement {
|
|
|
69
69
|
teardownEvents() { this._nav?.removeEventListener('click', this._onClick); }
|
|
70
70
|
|
|
71
71
|
onAttributeChanged() { this.projectLightDom(); }
|
|
72
|
+
onStringsChanged() { this._nav.setAttribute('aria-label', this.str('breadcrumb')); }
|
|
72
73
|
|
|
73
74
|
get separator() { return this.getAttribute('separator') ?? '/'; }
|
|
74
75
|
set separator(v) { this.setAttribute('separator', v); }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NkElement } from './base.js';
|
|
2
|
-
import { a as textOf } from './shared/property-cell-
|
|
2
|
+
import { a as textOf } from './shared/property-cell-DcsfnUNF.js';
|
|
3
3
|
import { C as CHEVRONS, i as isoOf, r as resolveLocale, f as firstWeekday, m as monthGrid, p as parseDay, w as weekLabel, a as weekdayNames, b as isoWeek, s as stepMonth } from './shared/dates-a32DcW1l.js';
|
|
4
4
|
import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
5
5
|
import './shared/avatar-CzPZN3uP.js';
|
|
@@ -12,7 +12,8 @@ import './shared/avatar-CzPZN3uP.js';
|
|
|
12
12
|
// default today's), weeks (the ISO week in front of each row), week-start
|
|
13
13
|
// (0 = Sunday … 6 = Saturday; default from the locale), weekend (weekdays
|
|
14
14
|
// washed like the days of other months, "6,0"), today, locale and the texts
|
|
15
|
-
// today-label, prev-label, next-label, week-label
|
|
15
|
+
// today-label, prev-label, next-label, week-label – without them English
|
|
16
|
+
// or German after the language (1.17.0).
|
|
16
17
|
// Standalone: view.columns = […]; view.rows = […]. Inside <nk-database> the
|
|
17
18
|
// database pushes the data. A card fires nk-select { row, id, value } like a
|
|
18
19
|
// row of the table, Today and ‹ › nk-month { month }.
|
|
@@ -68,9 +69,9 @@ class NkCalendarView extends NkElement {
|
|
|
68
69
|
}
|
|
69
70
|
this._box.classList.toggle('weeks', weeks);
|
|
70
71
|
this._title.textContent = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(g.first);
|
|
71
|
-
this._todayBtn.textContent = this.getAttribute('today-label') || '
|
|
72
|
-
this._prev.setAttribute('aria-label', this.getAttribute('prev-label') || '
|
|
73
|
-
this._next.setAttribute('aria-label', this.getAttribute('next-label') || '
|
|
72
|
+
this._todayBtn.textContent = this.getAttribute('today-label') || this.str('today');
|
|
73
|
+
this._prev.setAttribute('aria-label', this.getAttribute('prev-label') || this.str('previousMonth'));
|
|
74
|
+
this._next.setAttribute('aria-label', this.getAttribute('next-label') || this.str('nextMonth'));
|
|
74
75
|
|
|
75
76
|
const cell = (cls, text) => { const el = this.createElement('div', [cls]); el.textContent = text; return el; };
|
|
76
77
|
const nodes = [];
|
|
@@ -116,6 +117,8 @@ class NkCalendarView extends NkElement {
|
|
|
116
117
|
|
|
117
118
|
_rowById(id) { return this._rows.find(r => String(r.id) === String(id)); }
|
|
118
119
|
|
|
120
|
+
onStringsChanged() { this._render(); }
|
|
121
|
+
|
|
119
122
|
onAttributeChanged(name) {
|
|
120
123
|
if (name === 'month') this._month = this._attrMonth() ?? this._month;
|
|
121
124
|
this._render();
|
|
@@ -22,8 +22,9 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
22
22
|
// default from the locale, Monday where the browser cannot say), weeks,
|
|
23
23
|
// weekend (weekdays not worked, "6,0" for Saturday and Sunday), range, time,
|
|
24
24
|
// clearable, label and the texts today-label, prev-label, next-label,
|
|
25
|
-
// week-label, time-label, clear-label
|
|
26
|
-
// range has
|
|
25
|
+
// week-label, time-label, clear-label – without them English or German
|
|
26
|
+
// after the language (1.17.0). `time` belongs to single days; a range has
|
|
27
|
+
// none.
|
|
27
28
|
// days (property, or JSON attribute): { 'YYYY-MM-DD': { off, label, marks } }
|
|
28
29
|
// – `off` greys a holiday, `label` names the day (tooltip and screen
|
|
29
30
|
// reader), `marks` puts up to three dots in the nine colours on it.
|
|
@@ -152,9 +153,9 @@ class NkCalendar extends NkFormElement {
|
|
|
152
153
|
const label = this.getAttribute('label');
|
|
153
154
|
if (label) this._cal.setAttribute('aria-label', label); else this._cal.removeAttribute('aria-label');
|
|
154
155
|
this._title.textContent = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(g.first);
|
|
155
|
-
this._todayBtn.textContent = this.getAttribute('today-label') || '
|
|
156
|
-
this._prev.setAttribute('aria-label', this.getAttribute('prev-label') || '
|
|
157
|
-
this._next.setAttribute('aria-label', this.getAttribute('next-label') || '
|
|
156
|
+
this._todayBtn.textContent = this.getAttribute('today-label') || this.str('today');
|
|
157
|
+
this._prev.setAttribute('aria-label', this.getAttribute('prev-label') || this.str('previousMonth'));
|
|
158
|
+
this._next.setAttribute('aria-label', this.getAttribute('next-label') || this.str('nextMonth'));
|
|
158
159
|
|
|
159
160
|
// The one tab stop: the focused day, else the chosen one, else the 1st.
|
|
160
161
|
const cells = Array.from({ length: 42 }, (_, i) => isoOf(g.day(i)));
|
|
@@ -191,8 +192,8 @@ class NkCalendar extends NkFormElement {
|
|
|
191
192
|
|
|
192
193
|
const time = this._hasTime, clearable = this.getBoolAttr('clearable');
|
|
193
194
|
this._timeInput.value = this._time;
|
|
194
|
-
this._timeInput.setAttribute('aria-label', this.getAttribute('time-label') || '
|
|
195
|
-
this._clear.textContent = this.getAttribute('clear-label') || '
|
|
195
|
+
this._timeInput.setAttribute('aria-label', this.getAttribute('time-label') || this.str('time'));
|
|
196
|
+
this._clear.textContent = this.getAttribute('clear-label') || this.str('clear');
|
|
196
197
|
this._foot.replaceChildren(...[time && this._timeInput, clearable && this._clear].filter(Boolean));
|
|
197
198
|
if (time || clearable) this._cal.appendChild(this._foot); else this._foot.remove();
|
|
198
199
|
this._syncForm();
|
|
@@ -302,6 +303,8 @@ class NkCalendar extends NkFormElement {
|
|
|
302
303
|
document.removeEventListener('click', this._onOutside, true);
|
|
303
304
|
}
|
|
304
305
|
|
|
306
|
+
onStringsChanged() { if (this._grid) this._draw(); }
|
|
307
|
+
|
|
305
308
|
onAttributeChanged(name, _old, value) {
|
|
306
309
|
if (name === 'value') { this._readValue(value); this._draw(); return; }
|
|
307
310
|
if (name === 'month') { this.month = value; return; }
|
|
@@ -38,7 +38,7 @@ class NkCmdk extends NkElement {
|
|
|
38
38
|
render() {
|
|
39
39
|
this._backdrop = this.createElement('div', ['nk-cmdk-backdrop']);
|
|
40
40
|
this._backdrop.inert = true;
|
|
41
|
-
this._box = this.createElement('div', ['nk-cmdk'], { role: 'dialog', 'aria-modal': 'true'
|
|
41
|
+
this._box = this.createElement('div', ['nk-cmdk'], { role: 'dialog', 'aria-modal': 'true' });
|
|
42
42
|
const row = this.createElement('div', ['nk-cmdk-input-row']);
|
|
43
43
|
const glass = document.createElement('span');
|
|
44
44
|
glass.style.fontSize = '15px';
|
|
@@ -63,7 +63,11 @@ class NkCmdk extends NkElement {
|
|
|
63
63
|
this._syncOpen();
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
_syncAttrs() {
|
|
66
|
+
_syncAttrs() {
|
|
67
|
+
this._input.placeholder = this.getAttribute('placeholder') || this.str('commandPlaceholder');
|
|
68
|
+
this._box.setAttribute('aria-label', this.str('commandPalette'));
|
|
69
|
+
}
|
|
70
|
+
onStringsChanged() { this._syncAttrs(); }
|
|
67
71
|
|
|
68
72
|
/** Filtered, flat list in group order, best matches first within a group. */
|
|
69
73
|
results() {
|
|
@@ -13,7 +13,7 @@ class NkComments extends NkElement {
|
|
|
13
13
|
const box = this.createElement('div', ['nk-comments']);
|
|
14
14
|
box.appendChild(document.createElement('slot'));
|
|
15
15
|
this._row = this.createElement('div', ['nk-comment-input']);
|
|
16
|
-
this._input = this.createElement('input', ['nk-input'], { type: 'text'
|
|
16
|
+
this._input = this.createElement('input', ['nk-input'], { type: 'text' });
|
|
17
17
|
this._btn = this.createElement('button', ['nk-btn', 'primary', 'small'], { type: 'button' });
|
|
18
18
|
this._row.append(this._input, this._btn);
|
|
19
19
|
box.appendChild(this._row);
|
|
@@ -22,8 +22,9 @@ class NkComments extends NkElement {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
_sync() {
|
|
25
|
-
this._input.placeholder = this.getAttribute('placeholder') || '
|
|
26
|
-
this.
|
|
25
|
+
this._input.placeholder = this.getAttribute('placeholder') || this.str('commentPlaceholder');
|
|
26
|
+
this._input.setAttribute('aria-label', this.str('comment'));
|
|
27
|
+
this._btn.textContent = this.getAttribute('send-label') || this.str('send');
|
|
27
28
|
this._row.style.display = this.getBoolAttr('no-input') ? 'none' : '';
|
|
28
29
|
this._input.disabled = this._btn.disabled = this.getBoolAttr('disabled');
|
|
29
30
|
}
|
|
@@ -48,6 +49,7 @@ class NkComments extends NkElement {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
onAttributeChanged() { this._sync(); }
|
|
52
|
+
onStringsChanged() { this._sync(); }
|
|
51
53
|
focus(o) { this._input?.focus(o); }
|
|
52
54
|
get value() { return this._input?.value ?? ''; }
|
|
53
55
|
set value(v) { if (this._input) this._input.value = v ?? ''; }
|
|
@@ -11,7 +11,8 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
11
11
|
// ⌘C. `secret` masks it behind a Show/Hide toggle; Copy still copies the
|
|
12
12
|
// real value. `value` set as a property is not reflected, so a key never
|
|
13
13
|
// lands in the markup. Fires nk-action { action: 'copy', value, ok }. The
|
|
14
|
-
// texts
|
|
14
|
+
// texts come in English or German after the language (1.17.0, setStrings()
|
|
15
|
+
// for others); copy-label, copied-label, show-label, hide-label set them here.
|
|
15
16
|
class NkCopyField extends NkElement {
|
|
16
17
|
static get observedAttributes() { return ['value', 'mono', 'wrap', 'wide', 'secret', 'copy-label', 'copied-label', 'show-label', 'hide-label']; }
|
|
17
18
|
|
|
@@ -35,9 +36,9 @@ class NkCopyField extends NkElement {
|
|
|
35
36
|
// but a button that is not there cannot be tabbed to either.
|
|
36
37
|
if (secret && !this._show.isConnected) this._copy.before(this._show);
|
|
37
38
|
if (!secret) this._show.remove();
|
|
38
|
-
this._show.textContent = this._revealed ? (this.getAttribute('hide-label') || '
|
|
39
|
+
this._show.textContent = this._revealed ? (this.getAttribute('hide-label') || this.str('hide')) : (this.getAttribute('show-label') || this.str('show'));
|
|
39
40
|
this._show.setAttribute('aria-pressed', String(!!this._revealed));
|
|
40
|
-
this._copy.textContent = this._copied ? (this.getAttribute('copied-label') || '
|
|
41
|
+
this._copy.textContent = this._copied ? (this.getAttribute('copied-label') || this.str('copied')) : (this.getAttribute('copy-label') || this.str('copy'));
|
|
41
42
|
this._copy.classList.toggle('copied', !!this._copied);
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -54,6 +55,8 @@ class NkCopyField extends NkElement {
|
|
|
54
55
|
clearTimeout(this._timer);
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
onStringsChanged() { this._sync(); }
|
|
59
|
+
|
|
57
60
|
onAttributeChanged(name, _old, value) {
|
|
58
61
|
if (name === 'value') this._val = value ?? '';
|
|
59
62
|
this._sync();
|
|
@@ -28,7 +28,7 @@ class NkEmojiPicker extends NkElement {
|
|
|
28
28
|
|
|
29
29
|
render() {
|
|
30
30
|
this._box = this.createElement('div', ['nk-pop']);
|
|
31
|
-
this._search = this.createElement('input', ['nk-emoji-search'], { type: 'search', autocomplete: 'off'
|
|
31
|
+
this._search = this.createElement('input', ['nk-emoji-search'], { type: 'search', autocomplete: 'off' });
|
|
32
32
|
this._grid = this.createElement('div', ['nk-emoji-grid'], { role: 'listbox' });
|
|
33
33
|
this._cats = this.createElement('div', ['nk-emoji-cats'], { role: 'tablist' });
|
|
34
34
|
this._box.append(this._search, this._grid, this._cats);
|
|
@@ -40,7 +40,11 @@ class NkEmojiPicker extends NkElement {
|
|
|
40
40
|
this._syncAttrs();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
_syncAttrs() {
|
|
43
|
+
_syncAttrs() {
|
|
44
|
+
this._search.placeholder = this.getAttribute('placeholder') || this.str('emojiPlaceholder');
|
|
45
|
+
this._search.setAttribute('aria-label', this.str('searchEmoji'));
|
|
46
|
+
}
|
|
47
|
+
onStringsChanged() { this._syncAttrs(); }
|
|
44
48
|
|
|
45
49
|
_renderCats() {
|
|
46
50
|
this._cats.replaceChildren();
|
|
@@ -13,9 +13,10 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
13
13
|
// nk-action { action: 'add' } – each with the clicked button as `anchor`, to
|
|
14
14
|
// open a menu under it. × removes the filter; every change to filters or
|
|
15
15
|
// search fires nk-change { filters, search }. In slot="filters" of
|
|
16
|
-
// <nk-database> the row sits right under the view tabs. The texts
|
|
17
|
-
//
|
|
18
|
-
//
|
|
16
|
+
// <nk-database> the row sits right under the view tabs. The texts come in
|
|
17
|
+
// English or German after the language (1.17.0, setStrings() for others);
|
|
18
|
+
// filter-label, sort-label, add-label, remove-label (the × reads
|
|
19
|
+
// "<remove-label>: <pill>") and placeholder set them here.
|
|
19
20
|
class NkFilterBar extends NkElement {
|
|
20
21
|
static get observedAttributes() { return ['search', 'placeholder', 'no-filter', 'no-sort', 'value', 'add', 'add-label', 'filter-label', 'sort-label', 'remove-label']; }
|
|
21
22
|
|
|
@@ -33,18 +34,18 @@ class NkFilterBar extends NkElement {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
_sync() {
|
|
36
|
-
this._filterBtn.textContent = this.getAttribute('filter-label') || '
|
|
37
|
+
this._filterBtn.textContent = this.getAttribute('filter-label') || this.str('filter');
|
|
37
38
|
this._filterBtn.hidden = this.getBoolAttr('no-filter');
|
|
38
39
|
this._filterBtn.classList.toggle('active', this._filters.length > 0);
|
|
39
|
-
this._sortBtn.textContent = this.getAttribute('sort-label') || '
|
|
40
|
+
this._sortBtn.textContent = this.getAttribute('sort-label') || this.str('sort');
|
|
40
41
|
this._sortBtn.hidden = this.getBoolAttr('no-sort');
|
|
41
|
-
this._addBtn.textContent = this.getAttribute('add-label') || '
|
|
42
|
+
this._addBtn.textContent = this.getAttribute('add-label') || this.str('addFilter');
|
|
42
43
|
this._addBtn.hidden = !this.getBoolAttr('add');
|
|
43
44
|
this._input.hidden = !this.getBoolAttr('search');
|
|
44
|
-
this._input.placeholder = this.getAttribute('placeholder') || '
|
|
45
|
+
this._input.placeholder = this.getAttribute('placeholder') || this.str('search');
|
|
45
46
|
if (this.hasAttribute('value') && this._input.value !== this.getAttribute('value')) this._input.value = this.getAttribute('value');
|
|
46
47
|
for (const old of this._row.querySelectorAll('[data-pill]')) old.remove();
|
|
47
|
-
const removeLabel = this.getAttribute('remove-label') || '
|
|
48
|
+
const removeLabel = this.getAttribute('remove-label') || this.str('removeFilter');
|
|
48
49
|
this._pillStart.after(...this._filters.map((f, i) => {
|
|
49
50
|
const pill = this.createElement('span', ['nk-filter-pill', 'active'], { 'data-pill': i });
|
|
50
51
|
const label = this.createElement('button', [], { type: 'button', 'data-edit': i });
|
|
@@ -78,6 +79,7 @@ class NkFilterBar extends NkElement {
|
|
|
78
79
|
|
|
79
80
|
_emitChange() { this.emit('nk-change', { filters: [...this._filters], search: this._input.value }); }
|
|
80
81
|
onAttributeChanged() { this._sync(); }
|
|
82
|
+
onStringsChanged() { this._sync(); }
|
|
81
83
|
|
|
82
84
|
// A copy each way: the caller's array is never changed – removing a pill
|
|
83
85
|
// only reports the new list in nk-change.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NkElement } from './base.js';
|
|
2
|
-
import { a as textOf, f as formatDate, r as renderPropertyCell } from './shared/property-cell-
|
|
2
|
+
import { a as textOf, f as formatPercent, b as formatDate, r as renderPropertyCell } from './shared/property-cell-DcsfnUNF.js';
|
|
3
3
|
import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
4
4
|
import './shared/avatar-CzPZN3uP.js';
|
|
5
5
|
import './shared/dates-a32DcW1l.js';
|
|
@@ -16,13 +16,20 @@ import './shared/dates-a32DcW1l.js';
|
|
|
16
16
|
// <nk-database> the database pushes the data. A card fires nk-select
|
|
17
17
|
// { row, id, value } on a click, Enter or Space, like a row of the table;
|
|
18
18
|
// the add card nk-action { action: 'new-row' }.
|
|
19
|
+
// `href-key` (1.17.0) names the row field with the card's address: a row
|
|
20
|
+
// with one gets a link, <a class="nk-card" href>, in a .card-item that
|
|
21
|
+
// carries the list item's role – to open a course in a new tab or copy its
|
|
22
|
+
// address. A plain click, or Enter, fires the same nk-select; cancelling it
|
|
23
|
+
// keeps the browser from following the link. A middle click or one with
|
|
24
|
+
// Cmd, Ctrl, Shift or Alt fires nothing and does what a link does.
|
|
19
25
|
// → <div class="nk-gallery" role="list"><div class="nk-card" role="listitem" tabindex="0">
|
|
20
26
|
// <div class="nk-cover"><img src="…" alt=""></div><div class="card-title">🧭 …</div><div class="card-meta">…</div></div>
|
|
27
|
+
// <div class="card-item" role="listitem"><a class="nk-card" href="/courses/safety">…</a></div>
|
|
21
28
|
// <div class="nk-new-row">+ New page</div></div>
|
|
22
29
|
const SIZES = ['small', 'large'];
|
|
23
30
|
|
|
24
31
|
class NkGalleryView extends NkElement {
|
|
25
|
-
static get observedAttributes() { return ['name', 'label', 'badge', 'count', 'cover-key', 'no-cover', 'size', 'fit', 'title-key', 'meta-keys', 'new-row', 'new-row-label']; }
|
|
32
|
+
static get observedAttributes() { return ['name', 'label', 'badge', 'count', 'cover-key', 'no-cover', 'size', 'fit', 'title-key', 'meta-keys', 'href-key', 'new-row', 'new-row-label']; }
|
|
26
33
|
|
|
27
34
|
render() {
|
|
28
35
|
this._grid = this.createElement('div', ['nk-gallery'], { role: 'list' });
|
|
@@ -48,9 +55,13 @@ class NkGalleryView extends NkElement {
|
|
|
48
55
|
this._grid.className = ['nk-gallery', SIZES.includes(size) && size, this.getBoolAttr('fit') && 'fit'].filter(Boolean).join(' ');
|
|
49
56
|
const title = this._titleColumn(), meta = this._metaColumns();
|
|
50
57
|
const coverKey = this.getAttribute('cover-key') || 'cover', covers = !this.getBoolAttr('no-cover');
|
|
58
|
+
const hrefKey = this.getAttribute('href-key');
|
|
51
59
|
this._grid.replaceChildren();
|
|
52
60
|
for (const row of this._rows) {
|
|
53
|
-
const
|
|
61
|
+
const link = hrefKey ? row[hrefKey] : null, href = link && typeof link === 'object' ? link.href : link;
|
|
62
|
+
const card = href
|
|
63
|
+
? this.createElement('a', ['nk-card'], { href: String(href), 'data-id': row.id ?? '' })
|
|
64
|
+
: this.createElement('div', ['nk-card'], { role: 'listitem', tabindex: '0', 'data-id': row.id ?? '' });
|
|
54
65
|
if (covers) {
|
|
55
66
|
const cover = this.createElement('div', ['nk-cover']);
|
|
56
67
|
if (row[coverKey]) cover.appendChild(this.createElement('img', [], { src: row[coverKey], alt: '' }));
|
|
@@ -63,17 +74,21 @@ class NkGalleryView extends NkElement {
|
|
|
63
74
|
const v = row[c.key];
|
|
64
75
|
if (v === undefined || v === null || v === '') continue;
|
|
65
76
|
const s = document.createElement('span');
|
|
66
|
-
if (c.type === 'progress') s.textContent = `▰ ${v}
|
|
77
|
+
if (c.type === 'progress') s.textContent = `▰ ${formatPercent(c, v)}`;
|
|
67
78
|
else if (c.type === 'date') s.textContent = `📅 ${formatDate(c, v)}`;
|
|
68
79
|
else s.appendChild(renderPropertyCell(c, v, row));
|
|
69
80
|
m.appendChild(s);
|
|
70
81
|
}
|
|
71
82
|
card.append(t, m);
|
|
72
|
-
|
|
83
|
+
if (href) {
|
|
84
|
+
const item = this.createElement('div', ['card-item'], { role: 'listitem' });
|
|
85
|
+
item.appendChild(card);
|
|
86
|
+
this._grid.appendChild(item);
|
|
87
|
+
} else this._grid.appendChild(card);
|
|
73
88
|
}
|
|
74
89
|
if (this.getBoolAttr('new-row')) {
|
|
75
90
|
const add = this.createElement('div', ['nk-new-row'], { role: 'button', tabindex: '0', 'data-add': '' });
|
|
76
|
-
add.textContent = this.getAttribute('new-row-label') || '
|
|
91
|
+
add.textContent = this.getAttribute('new-row-label') || this.str('newPage');
|
|
77
92
|
this._grid.appendChild(add);
|
|
78
93
|
}
|
|
79
94
|
}
|
|
@@ -82,10 +97,16 @@ class NkGalleryView extends NkElement {
|
|
|
82
97
|
this._onClick = (e) => {
|
|
83
98
|
if (e.target.closest('[data-add]')) { this.emit('nk-action', { action: 'new-row' }); return; }
|
|
84
99
|
const card = e.target.closest('.nk-card');
|
|
85
|
-
if (card)
|
|
100
|
+
if (!card) return;
|
|
101
|
+
// The link contract: a plain left click is a selection, any other one the browser's.
|
|
102
|
+
const link = card.localName === 'a';
|
|
103
|
+
if (link && (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)) return;
|
|
104
|
+
const row = this._rowById(card.dataset.id);
|
|
105
|
+
if (!this.emit('nk-select', { row, id: row?.id, value: row?.id }) && link) e.preventDefault();
|
|
86
106
|
};
|
|
87
107
|
this._onKey = (e) => {
|
|
88
108
|
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
109
|
+
if (e.target.localName === 'a') return; // a link card: Enter is the browser's click, Space scrolls
|
|
89
110
|
if (e.target.matches?.('.nk-card, [data-add]')) { e.preventDefault(); e.target.click(); }
|
|
90
111
|
};
|
|
91
112
|
this._grid.addEventListener('click', this._onClick);
|
|
@@ -99,6 +120,7 @@ class NkGalleryView extends NkElement {
|
|
|
99
120
|
|
|
100
121
|
_rowById(id) { return this._rows.find(r => String(r.id) === String(id)); }
|
|
101
122
|
onAttributeChanged() { this._render(); }
|
|
123
|
+
onStringsChanged() { this._render(); }
|
|
102
124
|
|
|
103
125
|
get name() { return this.getAttribute('name') || 'gallery'; }
|
|
104
126
|
get columns() { return this._columns; }
|
|
@@ -21,7 +21,9 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
21
21
|
// Events: nk-change { dataUrl, width, height, size } after a pick, and on
|
|
22
22
|
// remove with an empty dataUrl; nk-error { message, name } when the
|
|
23
23
|
// file cannot be decoded (HEIC outside Safari, a broken file).
|
|
24
|
-
// Texts:
|
|
24
|
+
// Texts: English or German after the language (1.17.0, setStrings() for
|
|
25
|
+
// others); choose-label, change-label, remove-label set them here.
|
|
26
|
+
// label names the group.
|
|
25
27
|
|
|
26
28
|
function dataUrlBytes(url) {
|
|
27
29
|
const b64 = url.slice(url.indexOf(',') + 1);
|
|
@@ -63,8 +65,8 @@ class NkImagePicker extends NkElement {
|
|
|
63
65
|
}
|
|
64
66
|
const label = this.getAttribute('label');
|
|
65
67
|
if (label) this._row.setAttribute('aria-label', label); else this._row.removeAttribute('aria-label');
|
|
66
|
-
this._choose.textContent = this._src ? (this.getAttribute('change-label') || '
|
|
67
|
-
this._remove.textContent = this.getAttribute('remove-label') || '
|
|
68
|
+
this._choose.textContent = this._src ? (this.getAttribute('change-label') || this.str('changeImage')) : (this.getAttribute('choose-label') || this.str('uploadImage'));
|
|
69
|
+
this._remove.textContent = this.getAttribute('remove-label') || this.str('remove');
|
|
68
70
|
this._remove.hidden = !this._src;
|
|
69
71
|
this._input.accept = this.getAttribute('accept') || 'image/*';
|
|
70
72
|
}
|
|
@@ -112,6 +114,8 @@ class NkImagePicker extends NkElement {
|
|
|
112
114
|
this._remove?.removeEventListener('click', this._onRemove);
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
onStringsChanged() { this._sync(); }
|
|
118
|
+
|
|
115
119
|
onAttributeChanged(name, _old, value) {
|
|
116
120
|
if (name === 'src') this._src = value || '';
|
|
117
121
|
this._sync();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NkElement } from './base.js';
|
|
2
|
-
import { a as textOf,
|
|
2
|
+
import { a as textOf, b as formatDate, r as renderPropertyCell } from './shared/property-cell-DcsfnUNF.js';
|
|
3
3
|
import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
4
4
|
import './shared/avatar-CzPZN3uP.js';
|
|
5
5
|
import './shared/dates-a32DcW1l.js';
|
|
@@ -56,7 +56,7 @@ class NkListView extends NkElement {
|
|
|
56
56
|
item.append(icon, t, m);
|
|
57
57
|
this._list.appendChild(item);
|
|
58
58
|
}
|
|
59
|
-
this._newRow.textContent = this.getAttribute('new-row-label') || '
|
|
59
|
+
this._newRow.textContent = this.getAttribute('new-row-label') || this.str('newPage');
|
|
60
60
|
this._newRow.style.display = this.getBoolAttr('new-row') ? '' : 'none';
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -81,6 +81,7 @@ class NkListView extends NkElement {
|
|
|
81
81
|
|
|
82
82
|
_rowById(id) { return this._rows.find(r => String(r.id) === String(id)); }
|
|
83
83
|
onAttributeChanged() { this._render(); }
|
|
84
|
+
onStringsChanged() { this._render(); }
|
|
84
85
|
|
|
85
86
|
get name() { return this.getAttribute('name') || 'list'; }
|
|
86
87
|
get columns() { return this._columns; }
|
|
@@ -23,7 +23,7 @@ class NkModal extends NkElement {
|
|
|
23
23
|
this._backdrop = this.createElement('div', ['nk-modal-backdrop']);
|
|
24
24
|
this._backdrop.inert = true;
|
|
25
25
|
this._box = this.createElement('div', ['nk-modal'], { role: 'dialog', 'aria-modal': 'true', tabindex: '-1' });
|
|
26
|
-
this._nav = this.createElement('nav', ['nk-settings-nav'], { 'aria-label': '
|
|
26
|
+
this._nav = this.createElement('nav', ['nk-settings-nav'], { 'aria-label': this.str('settings') });
|
|
27
27
|
this._nav.appendChild(this.createElement('slot', [], { name: 'user' }));
|
|
28
28
|
this._rows = document.createElement('div');
|
|
29
29
|
this._nav.appendChild(this._rows);
|
|
@@ -141,6 +141,8 @@ class NkModal extends NkElement {
|
|
|
141
141
|
if (this._wasOpen) { unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
onStringsChanged() { this._nav.setAttribute('aria-label', this.str('settings')); }
|
|
145
|
+
|
|
144
146
|
onAttributeChanged(name) {
|
|
145
147
|
if (name === 'open') {
|
|
146
148
|
this._syncOpen();
|
|
@@ -24,7 +24,7 @@ class NkPageTitle extends NkElement {
|
|
|
24
24
|
this._h1.setAttribute('contenteditable', 'plaintext-only');
|
|
25
25
|
this._h1.setAttribute('spellcheck', 'false');
|
|
26
26
|
this._h1.setAttribute('role', 'textbox');
|
|
27
|
-
this._h1.setAttribute('aria-label', '
|
|
27
|
+
this._h1.setAttribute('aria-label', this.str('title'));
|
|
28
28
|
const text = this.getAttribute('value') ?? this.textContent.trim();
|
|
29
29
|
if (this._h1.textContent !== text && document.activeElement !== this) this._h1.textContent = text;
|
|
30
30
|
this._h1.setAttribute('data-placeholder', this.getAttribute('placeholder') || '');
|
|
@@ -59,6 +59,7 @@ class NkPageTitle extends NkElement {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
onAttributeChanged() { this._sync(); }
|
|
62
|
+
onStringsChanged() { if (this._h1.hasAttribute('role')) this._h1.setAttribute('aria-label', this.str('title')); }
|
|
62
63
|
|
|
63
64
|
get value() { return this.getBoolAttr('editable') ? (this._h1?.textContent.trim() ?? '') : (this.getAttribute('value') ?? this.textContent.trim()); }
|
|
64
65
|
set value(v) { this.setAttribute('value', v); }
|
|
@@ -20,7 +20,7 @@ class NkPage extends NkElement {
|
|
|
20
20
|
|
|
21
21
|
render() {
|
|
22
22
|
this._page = this.createElement('div', ['nk-page']);
|
|
23
|
-
this._icon = this.createElement('div', ['nk-page-icon'], { role: 'button', title: '
|
|
23
|
+
this._icon = this.createElement('div', ['nk-page-icon'], { role: 'button', title: this.str('changeIcon') });
|
|
24
24
|
this._iconSlot = this.createElement('slot', [], { name: 'icon' });
|
|
25
25
|
this._iconSlot.appendChild(this._icon);
|
|
26
26
|
this._page.appendChild(this._iconSlot);
|
|
@@ -76,6 +76,8 @@ class NkPage extends NkElement {
|
|
|
76
76
|
this._iconSlot?.removeEventListener('slotchange', this._onCover);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
onStringsChanged() { this._icon.title = this.str('changeIcon'); }
|
|
80
|
+
|
|
79
81
|
onAttributeChanged(name) {
|
|
80
82
|
if (name === 'narrow') this._build();
|
|
81
83
|
else this._sync();
|
|
@@ -27,7 +27,8 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
27
27
|
// Notion: from `min` (380) to `max` (all but 320px of the window). The width
|
|
28
28
|
// is the token --nk-peek-width, set on :root; `width` (px) sets it too, and
|
|
29
29
|
// nk-resize { width } fires when a drag or a key ends – the moment to keep
|
|
30
|
-
// it. resize-label names the edge (default "Resize"
|
|
30
|
+
// it. resize-label names the edge (default "Resize", in German "Breite
|
|
31
|
+
// ändern").
|
|
31
32
|
// `inset`: while it is open on the desktop, the page's <nk-app> gets
|
|
32
33
|
// `peek-inset` – its main column makes room instead of lying under the peek.
|
|
33
34
|
const CLOSE_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 17 5-5-5-5M13 17l5-5-5-5"/></svg>';
|
|
@@ -74,7 +75,7 @@ class NkPeek extends NkElement {
|
|
|
74
75
|
|
|
75
76
|
_syncResize() {
|
|
76
77
|
this._handle.hidden = !this.getBoolAttr('resizable');
|
|
77
|
-
this._handle.setAttribute('aria-label', this.getAttribute('resize-label') || '
|
|
78
|
+
this._handle.setAttribute('aria-label', this.getAttribute('resize-label') || this.str('resize'));
|
|
78
79
|
this._handle.setAttribute('aria-valuemin', this._min);
|
|
79
80
|
this._handle.setAttribute('aria-valuenow', Math.round(this.width));
|
|
80
81
|
}
|
|
@@ -91,7 +92,7 @@ class NkPeek extends NkElement {
|
|
|
91
92
|
_syncLabels() {
|
|
92
93
|
const label = this.getAttribute('label');
|
|
93
94
|
if (label) this._box.setAttribute('aria-label', label); else this._box.removeAttribute('aria-label');
|
|
94
|
-
this._close.setAttribute('aria-label', this.getAttribute('close-label') || '
|
|
95
|
+
this._close.setAttribute('aria-label', this.getAttribute('close-label') || this.str('close'));
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
_syncOpen() {
|
|
@@ -176,6 +177,8 @@ class NkPeek extends NkElement {
|
|
|
176
177
|
this._wasOpen = false;
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
onStringsChanged() { this._syncResize(); this._syncLabels(); }
|
|
181
|
+
|
|
179
182
|
onAttributeChanged(name, _old, value) {
|
|
180
183
|
if (name === 'width') { this._setWidth(Number(value)); return; }
|
|
181
184
|
if (name === 'inset') { this._syncInset(); return; }
|
|
@@ -24,7 +24,8 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
24
24
|
// brings it back; ⌘\ or Ctrl+\ toggles it (on a phone the drawer).
|
|
25
25
|
// `collapsed` is the state, collapse() / expand() / toggleCollapsed() the
|
|
26
26
|
// API, nk-collapse { collapsed } the moment to keep it. collapse-label names
|
|
27
|
-
// the « (default "Close sidebar"
|
|
27
|
+
// the « (default "Close sidebar", in German "Seitenleiste schließen") and is
|
|
28
|
+
// its tooltip for <nk-tooltip>.
|
|
28
29
|
const COLLAPSE_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m18 17-5-5 5-5M11 17l-5-5 5-5"/></svg>';
|
|
29
30
|
const phone = () => matchMedia('(max-width: 860px)').matches;
|
|
30
31
|
|
|
@@ -54,7 +55,7 @@ class NkSidebar extends NkElement {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
_syncCollapse() {
|
|
57
|
-
const label = this.getAttribute('collapse-label') || '
|
|
58
|
+
const label = this.getAttribute('collapse-label') || this.str('closeSidebar');
|
|
58
59
|
this._collapse.hidden = !this.getBoolAttr('collapsible');
|
|
59
60
|
this._collapse.setAttribute('aria-label', label);
|
|
60
61
|
this._collapse.dataset.tooltip = label;
|
|
@@ -106,6 +107,8 @@ class NkSidebar extends NkElement {
|
|
|
106
107
|
document.removeEventListener('keydown', this._onKey);
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
onStringsChanged() { this._syncCollapse(); }
|
|
111
|
+
|
|
109
112
|
onAttributeChanged(name) {
|
|
110
113
|
if (name === 'open') {
|
|
111
114
|
this._syncOpen();
|