@kubex/zinc 1.1.164 → 1.1.165
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 +124 -74
- package/dist/vscode.html-custom-data.json +11 -11
- package/dist/web-types.json +23 -23
- package/dist/zn.d.ts +9 -0
- package/dist/zn.min.js +240 -239
- package/package.json +1 -1
- package/src/components/remarkd-editor/list-continuation.ts +10 -4
- package/src/components/remarkd-editor/remarkd-editor.component.ts +29 -2
- package/src/components/remarkd-editor/remarkd-editor.test.ts +41 -0
package/package.json
CHANGED
|
@@ -22,6 +22,13 @@ function nextCheckbox(box: string): string {
|
|
|
22
22
|
return box === '[_] ' || box === '[*] ' ? '[_] ' : '[ ] ';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/** Whether `line` is a list marker with nothing after it — `- `, `2. `, `- [ ] `, or a bare `::`. */
|
|
26
|
+
export function isEmptyListItem(line: string): boolean {
|
|
27
|
+
if (line.trim() === '::') return true;
|
|
28
|
+
const item = LIST_ITEM.exec(line);
|
|
29
|
+
return !!item && line.slice(item[0].length).trim() === '';
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
/**
|
|
26
33
|
* What Enter does on a list line: continues the list with the next marker, or ends it by
|
|
27
34
|
* clearing the marker of an item that has no text. Null when Enter should just insert a
|
|
@@ -36,15 +43,14 @@ export function continueList(value: string, start: number, end: number): TextEdi
|
|
|
36
43
|
const line = value.slice(lineStart, lineEnd);
|
|
37
44
|
|
|
38
45
|
if (insideVerbatim(value.slice(0, lineStart))) return null;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
if (isEmptyListItem(line)) {
|
|
47
|
+
return {value: value.slice(0, lineStart) + value.slice(lineEnd), caret: lineStart};
|
|
48
|
+
}
|
|
42
49
|
|
|
43
50
|
const item = LIST_ITEM.exec(line);
|
|
44
51
|
const definition = item ? null : DEFINITION.exec(line);
|
|
45
52
|
const marker = item?.[0] ?? definition?.[0];
|
|
46
53
|
if (!marker || start - lineStart < marker.length) return null;
|
|
47
|
-
if (item && line.slice(marker.length).trim() === '') return clearLine;
|
|
48
54
|
|
|
49
55
|
if (definition) {
|
|
50
56
|
// The caret goes before `::` — the term is typed first.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
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
|
+
import {continueList, isEmptyListItem} from './list-continuation';
|
|
4
4
|
import {type CSSResultGroup, html, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
|
|
5
5
|
import {defaultValue} from "../../internal/default-value";
|
|
6
6
|
import {FormControlController} from "../../internal/form";
|
|
@@ -562,6 +562,11 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
562
562
|
* delimited containers (``` ==== !!!! .... ---- ____ **** ////) as single blocks.
|
|
563
563
|
*/
|
|
564
564
|
private splitBlocks(source: string): string[] {
|
|
565
|
+
return this.scanBlocks(source).blocks;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** `splitBlocks`, plus whether the source ends inside an unclosed container, where a blank line does not split. */
|
|
569
|
+
private scanBlocks(source: string): { blocks: string[]; open: boolean } {
|
|
565
570
|
const lines = source.replace(/\r\n/g, '\n').split('\n');
|
|
566
571
|
const blocks: string[] = [];
|
|
567
572
|
let current: string[] = [];
|
|
@@ -612,7 +617,7 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
612
617
|
current.push(line);
|
|
613
618
|
}
|
|
614
619
|
push();
|
|
615
|
-
return blocks;
|
|
620
|
+
return {blocks, open: fence !== null || depth > 0};
|
|
616
621
|
}
|
|
617
622
|
|
|
618
623
|
private fenceMarker(line: string): string | null {
|
|
@@ -961,6 +966,14 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
961
966
|
e.preventDefault();
|
|
962
967
|
this.activateAction(shortcutAction);
|
|
963
968
|
} else if (this.isPlainEnter(e)) {
|
|
969
|
+
const finished = this.finishedDraft(input.value, input.selectionStart, input.selectionEnd);
|
|
970
|
+
if (finished !== null) {
|
|
971
|
+
e.preventDefault();
|
|
972
|
+
this.editingDraft = finished;
|
|
973
|
+
this.suppressBlurCommit = true;
|
|
974
|
+
this.insertDraftBlock(this.commitEdit());
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
964
977
|
const edit = continueList(input.value, input.selectionStart, input.selectionEnd);
|
|
965
978
|
if (!edit) return;
|
|
966
979
|
e.preventDefault();
|
|
@@ -979,6 +992,20 @@ export default class ZnRemarkdEditor extends ZincElement implements ZincFormCont
|
|
|
979
992
|
}
|
|
980
993
|
};
|
|
981
994
|
|
|
995
|
+
/**
|
|
996
|
+
* The draft to commit when Enter should finish the block — pressed on a trailing blank line or
|
|
997
|
+
* empty list item, outside any container — with that line dropped. Null otherwise.
|
|
998
|
+
*/
|
|
999
|
+
private finishedDraft(value: string, start: number, end: number): string | null {
|
|
1000
|
+
if (start !== end || value.slice(start).trim() !== '') return null;
|
|
1001
|
+
const lineStart = value.lastIndexOf('\n', start - 1) + 1;
|
|
1002
|
+
const line = value.slice(lineStart);
|
|
1003
|
+
if (line.trim() !== '' && !isEmptyListItem(line)) return null;
|
|
1004
|
+
const before = value.slice(0, lineStart);
|
|
1005
|
+
if (before.trim() === '' || this.scanBlocks(before).open) return null;
|
|
1006
|
+
return before.trimEnd();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
982
1009
|
private isPlainEnter(e: KeyboardEvent): boolean {
|
|
983
1010
|
return e.key === 'Enter' && !e.isComposing && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey;
|
|
984
1011
|
}
|
|
@@ -595,6 +595,47 @@ Second"></zn-remarkd-editor>`);
|
|
|
595
595
|
expect(enter(input).defaultPrevented).to.be.false;
|
|
596
596
|
});
|
|
597
597
|
|
|
598
|
+
/** Opens the first block and replaces its draft with `draft`, caret at the end. */
|
|
599
|
+
async function editDraft(draft: string) {
|
|
600
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
601
|
+
<zn-remarkd-editor value="placeholder"></zn-remarkd-editor>`);
|
|
602
|
+
el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
|
|
603
|
+
await el.updateComplete;
|
|
604
|
+
return {el, input: typeInBlock(el, draft)};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
it('should finish the block on Enter at an empty trailing list item', async () => {
|
|
608
|
+
const {el, input} = await editDraft('- a\n- ');
|
|
609
|
+
expect(enter(input).defaultPrevented).to.be.true;
|
|
610
|
+
await el.updateComplete;
|
|
611
|
+
|
|
612
|
+
expect(el.value).to.equal('- a');
|
|
613
|
+
const draft = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
614
|
+
expect(draft.value, 'a fresh block is opened below').to.equal('');
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('should finish the block on a double Enter after a paragraph', async () => {
|
|
618
|
+
const {el, input} = await editDraft('hello\n');
|
|
619
|
+
expect(enter(input).defaultPrevented).to.be.true;
|
|
620
|
+
await el.updateComplete;
|
|
621
|
+
|
|
622
|
+
expect(el.value).to.equal('hello');
|
|
623
|
+
expect(el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!.value).to.equal('');
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('should keep a blank line inside an open container', async () => {
|
|
627
|
+
const {input} = await editDraft('```\ncode\n');
|
|
628
|
+
expect(enter(input).defaultPrevented).to.be.false;
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('should clear a mid-list empty item without finishing the block', async () => {
|
|
632
|
+
const {el, input} = await editDraft('- a\n- \n- b');
|
|
633
|
+
input.setSelectionRange(6, 6);
|
|
634
|
+
expect(enter(input).defaultPrevented).to.be.true;
|
|
635
|
+
await el.updateComplete;
|
|
636
|
+
expect(input.value).to.equal('- a\n\n- b');
|
|
637
|
+
});
|
|
638
|
+
|
|
598
639
|
it('should continue a list on Enter in the raw source view', async () => {
|
|
599
640
|
const el = await fixture<ZnRemarkdEditor>(html`
|
|
600
641
|
<zn-remarkd-editor allow-raw value="- [ ] first"></zn-remarkd-editor>`);
|