@kubex/zinc 1.1.162 → 1.1.164
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/dist/custom-elements.json +133 -78
- package/dist/vscode.html-custom-data.json +12 -12
- package/dist/web-types.json +30 -27
- package/dist/zn.d.ts +38 -2
- package/dist/zn.min.js +324 -270
- package/package.json +1 -1
- package/src/components/menu/menu.scss +21 -3
- package/src/components/menu-item/menu-item.component.ts +1 -1
- package/src/components/menu-item/menu-item.scss +4 -3
- package/src/components/menu-item/submenu-controller.ts +20 -15
- package/src/components/popup/popup.component.ts +3 -1
- package/src/components/remarkd-editor/actions.ts +36 -9
- package/src/components/remarkd-editor/list-continuation.test.ts +67 -0
- package/src/components/remarkd-editor/list-continuation.ts +73 -0
- package/src/components/remarkd-editor/remarkd-editor.component.ts +116 -38
- package/src/components/remarkd-editor/remarkd-editor.scss +5 -0
- package/src/components/remarkd-editor/remarkd-editor.test.ts +149 -31
- package/src/events/events.ts +1 -0
- package/src/events/zn-reposition.ts +7 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {ACTION_GROUPS, EDITOR_ACTIONS, slashItems} from './actions';
|
|
1
|
+
import {ACTION_GROUPS, EDITOR_ACTIONS, matchesShortcut, shortcutLabel, slashItems} from './actions';
|
|
2
2
|
import {classMap} from "lit/directives/class-map.js";
|
|
3
|
+
import {continueList} from './list-continuation';
|
|
3
4
|
import {type CSSResultGroup, html, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
|
|
4
5
|
import {defaultValue} from "../../internal/default-value";
|
|
5
6
|
import {FormControlController} from "../../internal/form";
|
|
@@ -279,7 +280,8 @@ function renderConditionalParts(parts: ConditionalPart[]): TemplateResult {
|
|
|
279
280
|
${parts.map(part => {
|
|
280
281
|
if (part.kind === 'nested') return renderConditionalWrapper(part.label, part.parts);
|
|
281
282
|
const text = part.lines.join('\n').trim();
|
|
282
|
-
return text ? html
|
|
283
|
+
return text ? html`
|
|
284
|
+
<div class="remarkd-editor__rendered--parsed">${unsafeHTML(safeParse(text))}</div>` : '';
|
|
283
285
|
})}
|
|
284
286
|
</div>`;
|
|
285
287
|
}
|
|
@@ -953,7 +955,17 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
953
955
|
// The slash menu claims navigation, Enter and Escape in the capture phase.
|
|
954
956
|
if (this.slashController.open) return;
|
|
955
957
|
|
|
956
|
-
|
|
958
|
+
const shortcutAction = EDITOR_ACTIONS.find(action =>
|
|
959
|
+
action.shortcut && matchesShortcut(e, action.shortcut) && this.actionAvailable(action));
|
|
960
|
+
if (shortcutAction) {
|
|
961
|
+
e.preventDefault();
|
|
962
|
+
this.activateAction(shortcutAction);
|
|
963
|
+
} else if (this.isPlainEnter(e)) {
|
|
964
|
+
const edit = continueList(input.value, input.selectionStart, input.selectionEnd);
|
|
965
|
+
if (!edit) return;
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
this.applyDraftEdit(input, edit.value, edit.caret, edit.caret);
|
|
968
|
+
} else if (e.key === 'Enter' && e.shiftKey) {
|
|
957
969
|
e.preventDefault();
|
|
958
970
|
this.suppressBlurCommit = true;
|
|
959
971
|
const next = this.commitEdit();
|
|
@@ -967,6 +979,21 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
967
979
|
}
|
|
968
980
|
};
|
|
969
981
|
|
|
982
|
+
private isPlainEnter(e: KeyboardEvent): boolean {
|
|
983
|
+
return e.key === 'Enter' && !e.isComposing && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
private handleRawKeydown = (e: KeyboardEvent) => {
|
|
987
|
+
if (!this.isPlainEnter(e)) return;
|
|
988
|
+
const input = e.target as HTMLTextAreaElement;
|
|
989
|
+
const edit = continueList(input.value, input.selectionStart, input.selectionEnd);
|
|
990
|
+
if (!edit) return;
|
|
991
|
+
e.preventDefault();
|
|
992
|
+
input.value = edit.value;
|
|
993
|
+
input.setSelectionRange(edit.caret, edit.caret);
|
|
994
|
+
this.handleRawInput(e);
|
|
995
|
+
};
|
|
996
|
+
|
|
970
997
|
/** Whether the block being edited is nothing but the slash command. */
|
|
971
998
|
private isSlashBlock(): boolean {
|
|
972
999
|
const value = this.shadowRoot?.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')?.value ?? '';
|
|
@@ -1232,7 +1259,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1232
1259
|
fetch(url, {headers: {Accept: 'application/json'}})
|
|
1233
1260
|
.then(res => {
|
|
1234
1261
|
if (!res.ok) throw new Error(`link search failed: ${res.status}`);
|
|
1235
|
-
return res.json() as Promise<{items?: LinkOption[]}>;
|
|
1262
|
+
return res.json() as Promise<{ items?: LinkOption[] }>;
|
|
1236
1263
|
})
|
|
1237
1264
|
.then(data => {
|
|
1238
1265
|
if (token !== this.linkSearchToken) return;
|
|
@@ -1329,7 +1356,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1329
1356
|
this.includeRequest = fetch(this.includeUrl, {headers: {Accept: 'application/json'}})
|
|
1330
1357
|
.then(res => {
|
|
1331
1358
|
if (!res.ok) throw new Error(`include list request failed: ${res.status}`);
|
|
1332
|
-
return res.json() as Promise<{items?: IncludeOption[]}>;
|
|
1359
|
+
return res.json() as Promise<{ items?: IncludeOption[] }>;
|
|
1333
1360
|
})
|
|
1334
1361
|
.then(data => {
|
|
1335
1362
|
const items = data.items ?? [];
|
|
@@ -1367,7 +1394,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1367
1394
|
fetch(url, {headers: {Accept: 'application/json'}})
|
|
1368
1395
|
.then(res => {
|
|
1369
1396
|
if (!res.ok) throw new Error(`link resolve failed: ${res.status}`);
|
|
1370
|
-
return res.json() as Promise<{items?: LinkOption[]}>;
|
|
1397
|
+
return res.json() as Promise<{ items?: LinkOption[] }>;
|
|
1371
1398
|
})
|
|
1372
1399
|
.then(data => {
|
|
1373
1400
|
const known = new Map((data.items ?? []).map(item => [item.ref, item]));
|
|
@@ -1403,7 +1430,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1403
1430
|
/**
|
|
1404
1431
|
* Applies an inline mark to the open block's textarea: wraps the selection, toggles the
|
|
1405
1432
|
* mark off when it is already wrapped, or inserts a selected placeholder when there is
|
|
1406
|
-
* no selection.
|
|
1433
|
+
* no selection.
|
|
1407
1434
|
*/
|
|
1408
1435
|
private applyInline(mark: InlineMark) {
|
|
1409
1436
|
const input = this.shadowRoot?.querySelector<HTMLTextAreaElement>('.remarkd-editor__input');
|
|
@@ -1450,10 +1477,15 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1450
1477
|
caret = [start + mark.before.length, start + mark.before.length + body.length];
|
|
1451
1478
|
}
|
|
1452
1479
|
|
|
1453
|
-
this.
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1480
|
+
this.applyDraftEdit(input, next, caret[0], caret[1]);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
/** Writes a programmatic edit to the open block's textarea. Goes through `editingDraft` so `zn-input` still fires. */
|
|
1484
|
+
private applyDraftEdit(input: HTMLTextAreaElement, value: string, selectionStart: number, selectionEnd: number) {
|
|
1485
|
+
this.editingDraft = value;
|
|
1486
|
+
input.value = value;
|
|
1487
|
+
input.setSelectionRange(selectionStart, selectionEnd);
|
|
1488
|
+
this.editShell = this.computeEditShell(value);
|
|
1457
1489
|
this.autosize(input);
|
|
1458
1490
|
input.focus({preventScroll: true});
|
|
1459
1491
|
this.emit('zn-input');
|
|
@@ -1529,7 +1561,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1529
1561
|
private renderImageControls() {
|
|
1530
1562
|
const data = this.imageEdit;
|
|
1531
1563
|
if (!data) return '';
|
|
1532
|
-
const aligns: {value: ImageBlockData['align']; icon: string; label: string}[] = [
|
|
1564
|
+
const aligns: { value: ImageBlockData['align']; icon: string; label: string }[] = [
|
|
1533
1565
|
{value: '', icon: 'align-left@lu', label: 'Align left'},
|
|
1534
1566
|
{value: 'center', icon: 'align-center@lu', label: 'Align center'},
|
|
1535
1567
|
{value: 'right', icon: 'align-right@lu', label: 'Align right'},
|
|
@@ -1558,7 +1590,8 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1558
1590
|
<span>Alignment</span>
|
|
1559
1591
|
<zn-button-group>
|
|
1560
1592
|
${aligns.map(align => html`
|
|
1561
|
-
<zn-button
|
|
1593
|
+
<zn-button icon-button="small"
|
|
1594
|
+
icon=${align.icon}
|
|
1562
1595
|
?plain=${data.align !== align.value}
|
|
1563
1596
|
tooltip=${align.label}
|
|
1564
1597
|
@click=${() => this.updateImageEdit({align: align.value})}></zn-button>`)}
|
|
@@ -1581,12 +1614,15 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1581
1614
|
</label>
|
|
1582
1615
|
</div>
|
|
1583
1616
|
<div class="remarkd-editor__image-buttons">
|
|
1584
|
-
<zn-button
|
|
1585
|
-
<zn-button
|
|
1617
|
+
<zn-button color="primary" size="small" @click=${this.saveImageEdit}>Save</zn-button>
|
|
1618
|
+
<zn-button color="secondary" size="small" @click=${this.closeImageEdit}>Cancel</zn-button>
|
|
1586
1619
|
<span class="remarkd-editor__image-buttons-spacer"></span>
|
|
1587
|
-
<zn-button
|
|
1588
|
-
|
|
1589
|
-
|
|
1620
|
+
<zn-button icon-button="small"
|
|
1621
|
+
plain
|
|
1622
|
+
icon="code@lu"
|
|
1623
|
+
tooltip="Edit source"
|
|
1624
|
+
@click=${this.editImageSource}></zn-button>
|
|
1625
|
+
<zn-button icon-button="small" plain icon="trash-2@lu" color="error"
|
|
1590
1626
|
tooltip="Delete image" @click=${this.deleteImageBlock}></zn-button>
|
|
1591
1627
|
</div>
|
|
1592
1628
|
</div>`;
|
|
@@ -1744,12 +1780,19 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1744
1780
|
@pointerdown=${this.handleHandlePointerDown}>
|
|
1745
1781
|
<zn-icon src="grip-vertical@lu" size="16"></zn-icon>
|
|
1746
1782
|
</span>
|
|
1747
|
-
<zn-button
|
|
1783
|
+
<zn-button icon-button="small"
|
|
1784
|
+
plain
|
|
1785
|
+
icon="plus@lu"
|
|
1786
|
+
icon-size="16"
|
|
1748
1787
|
tooltip="Add block below"
|
|
1749
1788
|
@click=${() => this.insertDraftBlock(index + 1)}></zn-button>
|
|
1750
1789
|
</div>
|
|
1751
1790
|
<zn-button class="remarkd-editor__delete"
|
|
1752
|
-
|
|
1791
|
+
icon-button="small"
|
|
1792
|
+
plain
|
|
1793
|
+
icon="x@lu"
|
|
1794
|
+
icon-size="16"
|
|
1795
|
+
color="error"
|
|
1753
1796
|
tooltip="Delete block"
|
|
1754
1797
|
@click=${() => this.deleteBlock(index)}></zn-button>`}
|
|
1755
1798
|
<div part="rendered"
|
|
@@ -1808,8 +1851,12 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1808
1851
|
/** A single toolbar action button, shared by the toolbar bar and the overflow menu. */
|
|
1809
1852
|
private renderAction(action: EditorAction) {
|
|
1810
1853
|
return html`
|
|
1811
|
-
<zn-button
|
|
1812
|
-
|
|
1854
|
+
<zn-button icon-button="small"
|
|
1855
|
+
plain
|
|
1856
|
+
icon=${action.icon}
|
|
1857
|
+
icon-size="18"
|
|
1858
|
+
label=${action.label}
|
|
1859
|
+
tooltip=${action.shortcut ? `${action.label} (${shortcutLabel(action.shortcut)})` : action.label}
|
|
1813
1860
|
?disabled=${this.isActionDisabled(action)}
|
|
1814
1861
|
@mousedown=${(e: MouseEvent) => e.preventDefault()}
|
|
1815
1862
|
@click=${() => this.activateAction(action)}></zn-button>`;
|
|
@@ -1823,6 +1870,8 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1823
1870
|
@click=${() => this.activateAction(action)}>
|
|
1824
1871
|
<zn-icon slot="prefix" src=${action.icon} size="18"></zn-icon>
|
|
1825
1872
|
${action.label}
|
|
1873
|
+
${action.shortcut ? html`
|
|
1874
|
+
<span slot="suffix" class="remarkd-editor__shortcut">${shortcutLabel(action.shortcut)}</span>` : ''}
|
|
1826
1875
|
</zn-menu-item>`;
|
|
1827
1876
|
}
|
|
1828
1877
|
|
|
@@ -1869,17 +1918,31 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1869
1918
|
// the textarea out from under it.
|
|
1870
1919
|
if (!this.linkPickerOpen) this.suppressBlurCommit = false;
|
|
1871
1920
|
}}>
|
|
1872
|
-
<zn-button slot="trigger"
|
|
1873
|
-
icon-
|
|
1921
|
+
<zn-button slot="trigger"
|
|
1922
|
+
icon-button="small"
|
|
1923
|
+
plain
|
|
1924
|
+
icon="ellipsis@lu"
|
|
1925
|
+
icon-size="18"
|
|
1926
|
+
tooltip="More"
|
|
1874
1927
|
@mousedown=${(e: MouseEvent) => e.preventDefault()}></zn-button>
|
|
1875
1928
|
<zn-menu>
|
|
1876
|
-
${overflowed.
|
|
1929
|
+
${overflowed.map(entry => html`
|
|
1930
|
+
<zn-menu-item>
|
|
1931
|
+
<zn-icon slot="prefix" src=${entry.actions[0].icon} size="18"></zn-icon>
|
|
1932
|
+
${entry.group.label}
|
|
1933
|
+
<zn-menu slot="submenu">
|
|
1934
|
+
${entry.actions.map(action => this.renderMenuAction(action))}
|
|
1935
|
+
</zn-menu>
|
|
1936
|
+
</zn-menu-item>`)}
|
|
1877
1937
|
</zn-menu>
|
|
1878
1938
|
</zn-dropdown>` : ''}`;
|
|
1879
1939
|
})()}
|
|
1880
1940
|
${this.allowRaw ? html`
|
|
1881
|
-
<zn-button part="raw-toggle"
|
|
1882
|
-
|
|
1941
|
+
<zn-button part="raw-toggle"
|
|
1942
|
+
class="remarkd-editor__raw-toggle"
|
|
1943
|
+
icon-button="small"
|
|
1944
|
+
icon="code-xml@lu"
|
|
1945
|
+
icon-size="18"
|
|
1883
1946
|
?plain=${!this.rawMode}
|
|
1884
1947
|
tooltip=${this.rawMode ? 'Show blocks' : 'Show raw source'}
|
|
1885
1948
|
@click=${this.toggleRawMode}></zn-button>` : ''}
|
|
@@ -1915,6 +1978,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1915
1978
|
spellcheck="false"
|
|
1916
1979
|
.value=${this.value}
|
|
1917
1980
|
@input=${this.handleRawInput}
|
|
1981
|
+
@keydown=${this.handleRawKeydown}
|
|
1918
1982
|
@blur=${this.commitRaw}></textarea>`;
|
|
1919
1983
|
}
|
|
1920
1984
|
|
|
@@ -1944,12 +2008,17 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1944
2008
|
@input=${(e: Event) => {
|
|
1945
2009
|
this.includeQuery = (e.target as HTMLInputElement).value;
|
|
1946
2010
|
}}>
|
|
1947
|
-
<zn-button
|
|
1948
|
-
|
|
2011
|
+
<zn-button icon-button="small"
|
|
2012
|
+
plain
|
|
2013
|
+
icon="x@lu"
|
|
2014
|
+
tooltip="Cancel"
|
|
2015
|
+
@click=${this.closeIncludePicker}></zn-button>
|
|
1949
2016
|
</div>
|
|
1950
2017
|
${this.includeOptions === null
|
|
1951
|
-
? html
|
|
1952
|
-
|
|
2018
|
+
? html`
|
|
2019
|
+
<div class="remarkd-editor__include-picker-empty">${
|
|
2020
|
+
this.includeLoadFailed ? 'Could not load Includes' : 'Loading…'}
|
|
2021
|
+
</div>`
|
|
1953
2022
|
: items.length
|
|
1954
2023
|
? items.map(item => html`
|
|
1955
2024
|
<button type="button" class="remarkd-editor__include-option"
|
|
@@ -1959,7 +2028,8 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1959
2028
|
<span class="remarkd-editor__include-option-meta">${item.scope ?? ''}${
|
|
1960
2029
|
item.languages ? ` · ${item.languages}` : ' · No content'}</span>
|
|
1961
2030
|
</button>`)
|
|
1962
|
-
: html
|
|
2031
|
+
: html`
|
|
2032
|
+
<div class="remarkd-editor__include-picker-empty">No Includes found</div>`}
|
|
1963
2033
|
</div>`;
|
|
1964
2034
|
}
|
|
1965
2035
|
|
|
@@ -1975,12 +2045,17 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1975
2045
|
this.linkQuery = (e.target as HTMLInputElement).value;
|
|
1976
2046
|
this.searchLinks(this.linkQuery);
|
|
1977
2047
|
}}>
|
|
1978
|
-
<zn-button
|
|
1979
|
-
|
|
2048
|
+
<zn-button icon-button="small"
|
|
2049
|
+
plain
|
|
2050
|
+
icon="x@lu"
|
|
2051
|
+
tooltip="Cancel"
|
|
2052
|
+
@click=${this.closeLinkPicker}></zn-button>
|
|
1980
2053
|
</div>
|
|
1981
2054
|
${this.linkResults === null
|
|
1982
|
-
? html
|
|
1983
|
-
|
|
2055
|
+
? html`
|
|
2056
|
+
<div class="remarkd-editor__link-picker-empty">${
|
|
2057
|
+
this.linkSearchFailed ? 'Could not search for articles' : 'Searching…'}
|
|
2058
|
+
</div>`
|
|
1984
2059
|
: this.linkResults.length
|
|
1985
2060
|
? this.linkResults.map(item => html`
|
|
1986
2061
|
<button type="button" class="remarkd-editor__link-option"
|
|
@@ -1990,7 +2065,8 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
1990
2065
|
[kinds[item.kind] ?? item.kind, item.context, item.status === 'published' ? '' : item.status]
|
|
1991
2066
|
.filter(Boolean).join(' · ')}</span>
|
|
1992
2067
|
</button>`)
|
|
1993
|
-
: html
|
|
2068
|
+
: html`
|
|
2069
|
+
<div class="remarkd-editor__link-picker-empty">No articles found</div>`}
|
|
1994
2070
|
</div>`;
|
|
1995
2071
|
}
|
|
1996
2072
|
|
|
@@ -2024,7 +2100,9 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
2024
2100
|
accept="image/*"
|
|
2025
2101
|
droparea
|
|
2026
2102
|
@zn-change=${this.handleImagePicked}></zn-file>
|
|
2027
|
-
<zn-button
|
|
2103
|
+
<zn-button icon-button="small"
|
|
2104
|
+
plain
|
|
2105
|
+
icon="x@lu"
|
|
2028
2106
|
tooltip="Cancel"
|
|
2029
2107
|
@click=${this.closeImagePicker}></zn-button>
|
|
2030
2108
|
</div>`;
|