@nectary/components 5.41.2 → 5.42.1
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/bundle.js +52 -37
- package/icon/index.js +9 -30
- package/package.json +1 -1
- package/rich-textarea/index.js +23 -1
- package/rich-textarea/types.d.ts +2 -1
- package/rich-textarea/utils.js +18 -5
- package/utils/markdown.js +2 -1
package/bundle.js
CHANGED
|
@@ -291,40 +291,19 @@ class Icon extends NectaryElement {
|
|
|
291
291
|
static get observedAttributes() {
|
|
292
292
|
return ["name", "icons-version"];
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
2. _matchNameToFont() → switches font-family to Sinch Icons Zero To D via
|
|
300
|
-
class
|
|
301
|
-
|
|
302
|
-
After:
|
|
303
|
-
1. _matchNameToFont() → sets the correct font-family class first
|
|
304
|
-
2. textContent = "fa-clone" → Safari renders the text using the correct font
|
|
305
|
-
from the start
|
|
306
|
-
|
|
307
|
-
Reasoning:
|
|
308
|
-
* Safari caches the ligature layout from the first paint. When
|
|
309
|
-
the text was set in Material Icons (which doesn't have these ligatures),
|
|
310
|
-
Safari calculated glyph positions for individual characters. When the font
|
|
311
|
-
then switched to Sinch Icons, Safari reused some of that cached layout
|
|
312
|
-
instead of fully recalculating — shifting certain glyphs to the right.
|
|
313
|
-
* _matchNameToFont() reads this.name via getAttribute(this,
|
|
314
|
-
'name'), which reads the HTML attribute — not textContent. The attribute is
|
|
315
|
-
already set when the callback fires (that's what triggers the callback), so
|
|
316
|
-
the method gets the correct name regardless of when textContent is set.
|
|
317
|
-
*/
|
|
294
|
+
// Font class is set before textContent to avoid a WebKit ligature caching
|
|
295
|
+
// bug: Safari caches glyph layout from the first paint, so if textContent
|
|
296
|
+
// lands while the default font (Material Icons) is active, the cached
|
|
297
|
+
// positions stick even after the font switches. Setting the class first
|
|
298
|
+
// ensures the correct font-family is active before any text renders.
|
|
318
299
|
attributeChangedCallback(name, _, newVal) {
|
|
319
300
|
switch (name) {
|
|
320
301
|
case "name": {
|
|
321
|
-
{
|
|
322
|
-
this
|
|
323
|
-
updateAttribute(this.#$icon, "aria-label", newVal);
|
|
324
|
-
if (getAttribute(this, "icons-version", "1") !== "1") {
|
|
325
|
-
this._matchNameToFont();
|
|
326
|
-
}
|
|
302
|
+
if (getAttribute(this, "icons-version", "1") !== "1") {
|
|
303
|
+
this._matchNameToFont();
|
|
327
304
|
}
|
|
305
|
+
this.#$icon.textContent = newVal;
|
|
306
|
+
updateAttribute(this.#$icon, "aria-label", newVal);
|
|
328
307
|
break;
|
|
329
308
|
}
|
|
330
309
|
case "icons-version": {
|
|
@@ -1549,7 +1528,8 @@ const regStrikethrough = new RegExp("(?<!\\\\)~~(?<strike>.+?)(?<!\\\\)~~");
|
|
|
1549
1528
|
const regButtonPlaceholder = new RegExp("(?<!\\\\)\\[\\[(?<button>[a-zA-Z0-9_-]+)\\]\\]");
|
|
1550
1529
|
const regLink = new RegExp("(?<!\\\\)!?\\[(?<linktext>[^\\]]*?)\\]\\((?<linkhref>[^)]+?)\\)(\\{(?<linkattrs>[^)]+?)\\})?");
|
|
1551
1530
|
const regChip = new RegExp("(?<!\\\\)\\{\\{(?<chip>[a-zA-Z0-9_-]+)\\}\\}");
|
|
1552
|
-
const
|
|
1531
|
+
const regEmojiSeq = "(?![0-9#*])\\p{Emoji}(?:\\uFE0F|\\p{Emoji_Modifier})?\\u20E3?";
|
|
1532
|
+
const regEmoji = new RegExp(`(?<emoji>\\p{RI}\\p{RI}|${regEmojiSeq}(?:\\u200D${regEmojiSeq})*)`, "u");
|
|
1553
1533
|
const regUList = /^(?<indent>[\t ]*?)[*+-][\t ]+(?<ultext>.*?)[\t ]*?$/;
|
|
1554
1534
|
const regOList = /^(?<indent>[\t ]*?)\d+\.[\t ]+(?<oltext>.*?)[\t ]*?$/;
|
|
1555
1535
|
const regEscapedChars = /\\(?<escaped>[\\\*_\[\]`~\{\}])/;
|
|
@@ -12237,11 +12217,18 @@ const assertListItem = ($n) => {
|
|
|
12237
12217
|
throw new Error(`Node is not a ListItem: ${$n?.nodeName}`);
|
|
12238
12218
|
}
|
|
12239
12219
|
};
|
|
12220
|
+
const BLOCK_CLASSNAME = "block";
|
|
12240
12221
|
const markListItemAsBlock = ($li) => {
|
|
12241
|
-
$li.classList.add(
|
|
12222
|
+
$li.classList.add(BLOCK_CLASSNAME);
|
|
12242
12223
|
};
|
|
12243
12224
|
const isListItemMarkedArBlock = ($li) => {
|
|
12244
|
-
return $li.classList.contains(
|
|
12225
|
+
return $li.classList.contains(BLOCK_CLASSNAME);
|
|
12226
|
+
};
|
|
12227
|
+
const markParagraphAsBlock = ($p) => {
|
|
12228
|
+
$p.classList.add(BLOCK_CLASSNAME);
|
|
12229
|
+
};
|
|
12230
|
+
const isParagraphMarkedAsBlock = ($p) => {
|
|
12231
|
+
return $p.classList.contains(BLOCK_CLASSNAME);
|
|
12245
12232
|
};
|
|
12246
12233
|
const MAX_LISTITEM_LEVEL = 4;
|
|
12247
12234
|
const removeListItemLevel = ($li) => {
|
|
@@ -13758,6 +13745,7 @@ const MD_ULISTITEM_TOKEN = "*";
|
|
|
13758
13745
|
const MD_OLISTITEM_TOKEN = "1.";
|
|
13759
13746
|
const MD_LISTITEM_JOIN = "\n";
|
|
13760
13747
|
const MD_PARAGRAPH_JOIN = "\n\n";
|
|
13748
|
+
const MD_SOFT_LINEBREAK = "\n";
|
|
13761
13749
|
const serializeTextReducer = (state, desc, i, descArray) => {
|
|
13762
13750
|
const { chunks } = state;
|
|
13763
13751
|
if (desc.isLink === true) {
|
|
@@ -13879,7 +13867,7 @@ const serializeRoot = ($root, range) => {
|
|
|
13879
13867
|
};
|
|
13880
13868
|
const flushParagraphChunks = () => {
|
|
13881
13869
|
if (paragraphChunks.length > 0) {
|
|
13882
|
-
chunks.push(
|
|
13870
|
+
chunks.push(paragraphChunks.join(MD_SOFT_LINEBREAK));
|
|
13883
13871
|
paragraphChunks.length = 0;
|
|
13884
13872
|
}
|
|
13885
13873
|
};
|
|
@@ -13897,6 +13885,9 @@ const serializeRoot = ($root, range) => {
|
|
|
13897
13885
|
} else {
|
|
13898
13886
|
assertTextBlock($child);
|
|
13899
13887
|
flushListChunks();
|
|
13888
|
+
if (isParagraph($child) && isParagraphMarkedAsBlock($child) && paragraphChunks.length > 0) {
|
|
13889
|
+
flushParagraphChunks();
|
|
13890
|
+
}
|
|
13900
13891
|
paragraphChunks.push(
|
|
13901
13892
|
serializeTextBlock($child, range)
|
|
13902
13893
|
);
|
|
@@ -14013,8 +14004,10 @@ const createParseVisitor = (doc) => {
|
|
|
14013
14004
|
},
|
|
14014
14005
|
paragraph() {
|
|
14015
14006
|
if (listsStack.length === 0) {
|
|
14016
|
-
$
|
|
14017
|
-
|
|
14007
|
+
const $p = createActuallyEmptyParagraph(doc);
|
|
14008
|
+
markParagraphAsBlock($p);
|
|
14009
|
+
$currentBlock = $p;
|
|
14010
|
+
$root.appendChild($p);
|
|
14018
14011
|
}
|
|
14019
14012
|
},
|
|
14020
14013
|
end() {
|
|
@@ -14041,7 +14034,7 @@ const setBrowserCaret = ({ startContainer, startOffset, endContainer, endOffset
|
|
|
14041
14034
|
range.setEnd(endContainer, endOffset);
|
|
14042
14035
|
selection.addRange(range);
|
|
14043
14036
|
};
|
|
14044
|
-
const templateHTML$p = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;box-sizing:border-box;background-color:var(--sinch-comp-textarea-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);overflow:hidden;--sinch-local-shape-radius:var(--sinch-comp-textarea-shape-radius)}#input-wrapper{position:relative;padding:8px 10px 8px 12px;box-sizing:border-box}#input{font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-initial);white-space:pre-wrap;overflow-wrap:break-word;border:none;outline:0}#placeholder{display:none;position:absolute;left:0;top:0;font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-placeholder);padding:8px 10px 8px 12px;pointer-events:none;user-select:none}#input.empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-textarea-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}:host([invalid]) #border{border-color:var(--sinch-comp-textarea-color-invalid-border-initial)}:host([disabled]){color:var(--sinch-comp-textarea-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-textarea-color-disabled-text-initial)}:host([disabled]) #border{border-color:var(--sinch-comp-textarea-color-disabled-border-initial)}:host(:not([disabled])) #input-wrapper:focus-within~#border{border-color:var(--sinch-comp-textarea-color-default-border-focus);border-width:2px}.oli,.p,.uli{margin:0}.oli.l0,.uli.l0{margin-left:6px}.oli.l1,.uli.l1{margin-left:36px}.oli.l2,.uli.l2{margin-left:64px}.oli.l3,.uli.l3{margin-left:92px}.oli.l4,.uli.l4{margin-left:120px}.uli.l0{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli.l1{counter-reset:list-1 list-2 list-3 list-4}.uli.l2{counter-reset:list-2 list-3 list-4}.uli.l3{counter-reset:list-3 list-4}.uli.l4{counter-reset:list-4}.oli.l0{counter-reset:list-1 list-2 list-3 list-4}.oli.l1{counter-reset:list-2 list-3 list-4}.oli.l2{counter-reset:list-3 list-4}.oli.l3{counter-reset:list-4}.oli.l0::before{counter-increment:list-0;content:counter(list-0,decimal) ". "}.oli.l1::before{counter-increment:list-1;content:counter(list-1,lower-alpha) ". "}.oli.l2::before{counter-increment:list-2;content:counter(list-2,lower-roman) ". "}.oli.l3::before{counter-increment:list-3;content:counter(list-3,decimal) ". "}.oli.l4::before{counter-increment:list-4;content:counter(list-4,lower-alpha) ". "}.oli.block,.oli:first-of-type,.p+.oli{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli::before{content:"\\25CF";display:inline-block;width:16px}.oli+.p,.oli.block,.p+.oli,.p+.uli,.uli+.p,.uli.block{margin-top:.5em}.c{font:var(--sinch-comp-code-tag-font-text);font-size:inherit;line-height:inherit;color:var(--sinch-comp-code-tag-color-default-text-initial);border:1px solid var(--sinch-comp-code-tag-color-default-border-initial);background-color:var(--sinch-comp-code-tag-color-default-background-initial);padding:0 .25em;border-radius:var(--sinch-comp-code-tag-shape-radius)}.l{font:var(--sinch-comp-link-default-font-initial);color:var(--sinch-comp-link-color-default-text-initial);text-decoration:underline}sinch-rich-textarea-chip{display:inline-flex;vertical-align:middle;user-select:none}.i{font-style:italic}.b{font-weight:700}.s{text-decoration:line-through}.e{background-repeat:no-repeat;background-position:50% 50%;background-size:contain;width:1em;height:1em;vertical-align:-.2em}#top-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:4px 4px 0}#top-wrapper.empty{display:none}#bottom-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:0 4px 4px}#bottom-wrapper.empty{display:none}</style><div id="wrapper"><div id="top-wrapper"><slot id="top" name="top"></slot></div><div id="input-wrapper"><div id="input" contenteditable="true" role="textbox" aria-multiline="true" suppresscontenteditablewarning autocapitalize="false" autocorrect="false" autosave="false" spellcheck="false"></div><div id="placeholder"></div></div><div id="border"></div><div id="bottom-wrapper"><slot id="bottom" name="bottom"></slot></div></div>';
|
|
14037
|
+
const templateHTML$p = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;box-sizing:border-box;background-color:var(--sinch-comp-textarea-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);overflow:hidden;--sinch-local-shape-radius:var(--sinch-comp-textarea-shape-radius)}#input-wrapper{position:relative;padding:8px 10px 8px 12px;box-sizing:border-box}#input{font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-initial);caret-color:var(--sinch-comp-textarea-color-default-text-initial);white-space:pre-wrap;overflow-wrap:break-word;border:none;outline:0}#placeholder{display:none;position:absolute;left:0;top:0;font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-placeholder);padding:8px 10px 8px 12px;pointer-events:none;user-select:none}#input.empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-textarea-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}:host([invalid]) #border{border-color:var(--sinch-comp-textarea-color-invalid-border-initial)}:host([disabled]){color:var(--sinch-comp-textarea-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-textarea-color-disabled-text-initial)}:host([disabled]) #border{border-color:var(--sinch-comp-textarea-color-disabled-border-initial)}:host(:not([disabled])) #input-wrapper:focus-within~#border{border-color:var(--sinch-comp-textarea-color-default-border-focus);border-width:2px}.oli,.p,.uli{margin:0}.oli.l0,.uli.l0{margin-left:6px}.oli.l1,.uli.l1{margin-left:36px}.oli.l2,.uli.l2{margin-left:64px}.oli.l3,.uli.l3{margin-left:92px}.oli.l4,.uli.l4{margin-left:120px}.uli.l0{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli.l1{counter-reset:list-1 list-2 list-3 list-4}.uli.l2{counter-reset:list-2 list-3 list-4}.uli.l3{counter-reset:list-3 list-4}.uli.l4{counter-reset:list-4}.oli.l0{counter-reset:list-1 list-2 list-3 list-4}.oli.l1{counter-reset:list-2 list-3 list-4}.oli.l2{counter-reset:list-3 list-4}.oli.l3{counter-reset:list-4}.oli.l0::before{counter-increment:list-0;content:counter(list-0,decimal) ". "}.oli.l1::before{counter-increment:list-1;content:counter(list-1,lower-alpha) ". "}.oli.l2::before{counter-increment:list-2;content:counter(list-2,lower-roman) ". "}.oli.l3::before{counter-increment:list-3;content:counter(list-3,decimal) ". "}.oli.l4::before{counter-increment:list-4;content:counter(list-4,lower-alpha) ". "}.oli.block,.oli:first-of-type,.p+.oli{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli::before{content:"\\25CF";display:inline-block;width:16px}.oli+.p,.oli.block,.p+.oli,.p+.uli,.uli+.p,.uli.block{margin-top:.5em}.c{font:var(--sinch-comp-code-tag-font-text);font-size:inherit;line-height:inherit;color:var(--sinch-comp-code-tag-color-default-text-initial);border:1px solid var(--sinch-comp-code-tag-color-default-border-initial);background-color:var(--sinch-comp-code-tag-color-default-background-initial);padding:0 .25em;border-radius:var(--sinch-comp-code-tag-shape-radius)}.l{font:var(--sinch-comp-link-default-font-initial);color:var(--sinch-comp-link-color-default-text-initial);text-decoration:underline}sinch-rich-textarea-chip{display:inline-flex;vertical-align:middle;user-select:none}.i{font-style:italic}.b{font-weight:700}.s{text-decoration:line-through}.e{background-repeat:no-repeat;background-position:50% 50%;background-size:contain;width:1em;height:1em;vertical-align:-.2em}#top-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:4px 4px 0}#top-wrapper.empty{display:none}#bottom-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:0 4px 4px}#bottom-wrapper.empty{display:none}</style><div id="wrapper"><div id="top-wrapper"><slot id="top" name="top"></slot></div><div id="input-wrapper"><div id="input" contenteditable="true" role="textbox" aria-multiline="true" suppresscontenteditablewarning autocapitalize="false" autocorrect="false" autosave="false" spellcheck="false"></div><div id="placeholder"></div></div><div id="border"></div><div id="bottom-wrapper"><slot id="bottom" name="bottom"></slot></div></div>';
|
|
14045
14038
|
const template$p = document.createElement("template");
|
|
14046
14039
|
template$p.innerHTML = templateHTML$p;
|
|
14047
14040
|
const SUPPORTS_SHADOW_SELECTION = typeof window.ShadowRoot.prototype.getSelection === "function";
|
|
@@ -14059,7 +14052,13 @@ class RichTextarea extends NectaryElement {
|
|
|
14059
14052
|
#cachedRange = null;
|
|
14060
14053
|
#lastSelectionInfo = null;
|
|
14061
14054
|
#prevDispatchedValue = null;
|
|
14055
|
+
#changeDebounce = null;
|
|
14062
14056
|
#parseVisitor;
|
|
14057
|
+
/* @internal
|
|
14058
|
+
* The chip resolver is used to resolve the color and icon of a chip.
|
|
14059
|
+
* It is used to resolve the color and icon of a chip.
|
|
14060
|
+
* It is used to resolve the color and icon of a chip.
|
|
14061
|
+
*/
|
|
14063
14062
|
#chipResolver = null;
|
|
14064
14063
|
constructor() {
|
|
14065
14064
|
super();
|
|
@@ -14123,6 +14122,7 @@ class RichTextarea extends NectaryElement {
|
|
|
14123
14122
|
}
|
|
14124
14123
|
disconnectedCallback() {
|
|
14125
14124
|
super.disconnectedCallback();
|
|
14125
|
+
this.#clearChangeDebounce();
|
|
14126
14126
|
this.#controller.abort();
|
|
14127
14127
|
this.#controller = null;
|
|
14128
14128
|
}
|
|
@@ -14448,9 +14448,23 @@ class RichTextarea extends NectaryElement {
|
|
|
14448
14448
|
if (result.range !== null) {
|
|
14449
14449
|
setBrowserCaret(result.range);
|
|
14450
14450
|
}
|
|
14451
|
+
this.#scheduleChangeDispatch();
|
|
14451
14452
|
}
|
|
14452
14453
|
this.#updateEditorEmptyClass();
|
|
14453
14454
|
}
|
|
14455
|
+
#clearChangeDebounce() {
|
|
14456
|
+
if (this.#changeDebounce !== null) {
|
|
14457
|
+
clearTimeout(this.#changeDebounce);
|
|
14458
|
+
this.#changeDebounce = null;
|
|
14459
|
+
}
|
|
14460
|
+
}
|
|
14461
|
+
#scheduleChangeDispatch() {
|
|
14462
|
+
this.#clearChangeDebounce();
|
|
14463
|
+
this.#changeDebounce = setTimeout(() => {
|
|
14464
|
+
this.#changeDebounce = null;
|
|
14465
|
+
this.#dispatchChangeEvent();
|
|
14466
|
+
}, 100);
|
|
14467
|
+
}
|
|
14454
14468
|
#onInputFocus = () => {
|
|
14455
14469
|
if (this.#cachedRange !== null) {
|
|
14456
14470
|
setBrowserCaret(this.#cachedRange);
|
|
@@ -14459,6 +14473,7 @@ class RichTextarea extends NectaryElement {
|
|
|
14459
14473
|
};
|
|
14460
14474
|
#onInputBlur = () => {
|
|
14461
14475
|
this.dispatchEvent(new CustomEvent("-blur"));
|
|
14476
|
+
this.#clearChangeDebounce();
|
|
14462
14477
|
this.#dispatchChangeEvent();
|
|
14463
14478
|
};
|
|
14464
14479
|
#dispatchChangeEvent() {
|
package/icon/index.js
CHANGED
|
@@ -14,40 +14,19 @@ class Icon extends NectaryElement {
|
|
|
14
14
|
static get observedAttributes() {
|
|
15
15
|
return ["name", "icons-version"];
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
2. _matchNameToFont() → switches font-family to Sinch Icons Zero To D via
|
|
23
|
-
class
|
|
24
|
-
|
|
25
|
-
After:
|
|
26
|
-
1. _matchNameToFont() → sets the correct font-family class first
|
|
27
|
-
2. textContent = "fa-clone" → Safari renders the text using the correct font
|
|
28
|
-
from the start
|
|
29
|
-
|
|
30
|
-
Reasoning:
|
|
31
|
-
* Safari caches the ligature layout from the first paint. When
|
|
32
|
-
the text was set in Material Icons (which doesn't have these ligatures),
|
|
33
|
-
Safari calculated glyph positions for individual characters. When the font
|
|
34
|
-
then switched to Sinch Icons, Safari reused some of that cached layout
|
|
35
|
-
instead of fully recalculating — shifting certain glyphs to the right.
|
|
36
|
-
* _matchNameToFont() reads this.name via getAttribute(this,
|
|
37
|
-
'name'), which reads the HTML attribute — not textContent. The attribute is
|
|
38
|
-
already set when the callback fires (that's what triggers the callback), so
|
|
39
|
-
the method gets the correct name regardless of when textContent is set.
|
|
40
|
-
*/
|
|
17
|
+
// Font class is set before textContent to avoid a WebKit ligature caching
|
|
18
|
+
// bug: Safari caches glyph layout from the first paint, so if textContent
|
|
19
|
+
// lands while the default font (Material Icons) is active, the cached
|
|
20
|
+
// positions stick even after the font switches. Setting the class first
|
|
21
|
+
// ensures the correct font-family is active before any text renders.
|
|
41
22
|
attributeChangedCallback(name, _, newVal) {
|
|
42
23
|
switch (name) {
|
|
43
24
|
case "name": {
|
|
44
|
-
{
|
|
45
|
-
this
|
|
46
|
-
updateAttribute(this.#$icon, "aria-label", newVal);
|
|
47
|
-
if (getAttribute(this, "icons-version", "1") !== "1") {
|
|
48
|
-
this._matchNameToFont();
|
|
49
|
-
}
|
|
25
|
+
if (getAttribute(this, "icons-version", "1") !== "1") {
|
|
26
|
+
this._matchNameToFont();
|
|
50
27
|
}
|
|
28
|
+
this.#$icon.textContent = newVal;
|
|
29
|
+
updateAttribute(this.#$icon, "aria-label", newVal);
|
|
51
30
|
break;
|
|
52
31
|
}
|
|
53
32
|
case "icons-version": {
|
package/package.json
CHANGED
package/rich-textarea/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isElementFocused } from "../utils/slot.js";
|
|
|
6
6
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
7
7
|
import { parseMarkdown } from "../utils/markdown.js";
|
|
8
8
|
import { createParseVisitor, getEndRange, formatList, handleEmojiMousedown, removeChip, setBrowserCaret, formatOutdent, formatIndent, formatInline, insertChip, insertLink, insertText, insertLineBreak, deleteContentBackward, serializeMarkdown, getSelectionInfo, isSelectionEqual, insertFromPaste, isEditorEmpty } from "./utils.js";
|
|
9
|
-
const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;box-sizing:border-box;background-color:var(--sinch-comp-textarea-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);overflow:hidden;--sinch-local-shape-radius:var(--sinch-comp-textarea-shape-radius)}#input-wrapper{position:relative;padding:8px 10px 8px 12px;box-sizing:border-box}#input{font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-initial);white-space:pre-wrap;overflow-wrap:break-word;border:none;outline:0}#placeholder{display:none;position:absolute;left:0;top:0;font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-placeholder);padding:8px 10px 8px 12px;pointer-events:none;user-select:none}#input.empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-textarea-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}:host([invalid]) #border{border-color:var(--sinch-comp-textarea-color-invalid-border-initial)}:host([disabled]){color:var(--sinch-comp-textarea-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-textarea-color-disabled-text-initial)}:host([disabled]) #border{border-color:var(--sinch-comp-textarea-color-disabled-border-initial)}:host(:not([disabled])) #input-wrapper:focus-within~#border{border-color:var(--sinch-comp-textarea-color-default-border-focus);border-width:2px}.oli,.p,.uli{margin:0}.oli.l0,.uli.l0{margin-left:6px}.oli.l1,.uli.l1{margin-left:36px}.oli.l2,.uli.l2{margin-left:64px}.oli.l3,.uli.l3{margin-left:92px}.oli.l4,.uli.l4{margin-left:120px}.uli.l0{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli.l1{counter-reset:list-1 list-2 list-3 list-4}.uli.l2{counter-reset:list-2 list-3 list-4}.uli.l3{counter-reset:list-3 list-4}.uli.l4{counter-reset:list-4}.oli.l0{counter-reset:list-1 list-2 list-3 list-4}.oli.l1{counter-reset:list-2 list-3 list-4}.oli.l2{counter-reset:list-3 list-4}.oli.l3{counter-reset:list-4}.oli.l0::before{counter-increment:list-0;content:counter(list-0,decimal) ". "}.oli.l1::before{counter-increment:list-1;content:counter(list-1,lower-alpha) ". "}.oli.l2::before{counter-increment:list-2;content:counter(list-2,lower-roman) ". "}.oli.l3::before{counter-increment:list-3;content:counter(list-3,decimal) ". "}.oli.l4::before{counter-increment:list-4;content:counter(list-4,lower-alpha) ". "}.oli.block,.oli:first-of-type,.p+.oli{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli::before{content:"\\25CF";display:inline-block;width:16px}.oli+.p,.oli.block,.p+.oli,.p+.uli,.uli+.p,.uli.block{margin-top:.5em}.c{font:var(--sinch-comp-code-tag-font-text);font-size:inherit;line-height:inherit;color:var(--sinch-comp-code-tag-color-default-text-initial);border:1px solid var(--sinch-comp-code-tag-color-default-border-initial);background-color:var(--sinch-comp-code-tag-color-default-background-initial);padding:0 .25em;border-radius:var(--sinch-comp-code-tag-shape-radius)}.l{font:var(--sinch-comp-link-default-font-initial);color:var(--sinch-comp-link-color-default-text-initial);text-decoration:underline}sinch-rich-textarea-chip{display:inline-flex;vertical-align:middle;user-select:none}.i{font-style:italic}.b{font-weight:700}.s{text-decoration:line-through}.e{background-repeat:no-repeat;background-position:50% 50%;background-size:contain;width:1em;height:1em;vertical-align:-.2em}#top-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:4px 4px 0}#top-wrapper.empty{display:none}#bottom-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:0 4px 4px}#bottom-wrapper.empty{display:none}</style><div id="wrapper"><div id="top-wrapper"><slot id="top" name="top"></slot></div><div id="input-wrapper"><div id="input" contenteditable="true" role="textbox" aria-multiline="true" suppresscontenteditablewarning autocapitalize="false" autocorrect="false" autosave="false" spellcheck="false"></div><div id="placeholder"></div></div><div id="border"></div><div id="bottom-wrapper"><slot id="bottom" name="bottom"></slot></div></div>';
|
|
9
|
+
const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;box-sizing:border-box;background-color:var(--sinch-comp-textarea-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);overflow:hidden;--sinch-local-shape-radius:var(--sinch-comp-textarea-shape-radius)}#input-wrapper{position:relative;padding:8px 10px 8px 12px;box-sizing:border-box}#input{font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-initial);caret-color:var(--sinch-comp-textarea-color-default-text-initial);white-space:pre-wrap;overflow-wrap:break-word;border:none;outline:0}#placeholder{display:none;position:absolute;left:0;top:0;font:var(--sinch-comp-textarea-font-input);color:var(--sinch-comp-textarea-color-default-text-placeholder);padding:8px 10px 8px 12px;pointer-events:none;user-select:none}#input.empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-textarea-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}:host([invalid]) #border{border-color:var(--sinch-comp-textarea-color-invalid-border-initial)}:host([disabled]){color:var(--sinch-comp-textarea-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-textarea-color-disabled-text-initial)}:host([disabled]) #border{border-color:var(--sinch-comp-textarea-color-disabled-border-initial)}:host(:not([disabled])) #input-wrapper:focus-within~#border{border-color:var(--sinch-comp-textarea-color-default-border-focus);border-width:2px}.oli,.p,.uli{margin:0}.oli.l0,.uli.l0{margin-left:6px}.oli.l1,.uli.l1{margin-left:36px}.oli.l2,.uli.l2{margin-left:64px}.oli.l3,.uli.l3{margin-left:92px}.oli.l4,.uli.l4{margin-left:120px}.uli.l0{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli.l1{counter-reset:list-1 list-2 list-3 list-4}.uli.l2{counter-reset:list-2 list-3 list-4}.uli.l3{counter-reset:list-3 list-4}.uli.l4{counter-reset:list-4}.oli.l0{counter-reset:list-1 list-2 list-3 list-4}.oli.l1{counter-reset:list-2 list-3 list-4}.oli.l2{counter-reset:list-3 list-4}.oli.l3{counter-reset:list-4}.oli.l0::before{counter-increment:list-0;content:counter(list-0,decimal) ". "}.oli.l1::before{counter-increment:list-1;content:counter(list-1,lower-alpha) ". "}.oli.l2::before{counter-increment:list-2;content:counter(list-2,lower-roman) ". "}.oli.l3::before{counter-increment:list-3;content:counter(list-3,decimal) ". "}.oli.l4::before{counter-increment:list-4;content:counter(list-4,lower-alpha) ". "}.oli.block,.oli:first-of-type,.p+.oli{counter-reset:list-0 list-1 list-2 list-3 list-4}.uli::before{content:"\\25CF";display:inline-block;width:16px}.oli+.p,.oli.block,.p+.oli,.p+.uli,.uli+.p,.uli.block{margin-top:.5em}.c{font:var(--sinch-comp-code-tag-font-text);font-size:inherit;line-height:inherit;color:var(--sinch-comp-code-tag-color-default-text-initial);border:1px solid var(--sinch-comp-code-tag-color-default-border-initial);background-color:var(--sinch-comp-code-tag-color-default-background-initial);padding:0 .25em;border-radius:var(--sinch-comp-code-tag-shape-radius)}.l{font:var(--sinch-comp-link-default-font-initial);color:var(--sinch-comp-link-color-default-text-initial);text-decoration:underline}sinch-rich-textarea-chip{display:inline-flex;vertical-align:middle;user-select:none}.i{font-style:italic}.b{font-weight:700}.s{text-decoration:line-through}.e{background-repeat:no-repeat;background-position:50% 50%;background-size:contain;width:1em;height:1em;vertical-align:-.2em}#top-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:4px 4px 0}#top-wrapper.empty{display:none}#bottom-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;padding:0 4px 4px}#bottom-wrapper.empty{display:none}</style><div id="wrapper"><div id="top-wrapper"><slot id="top" name="top"></slot></div><div id="input-wrapper"><div id="input" contenteditable="true" role="textbox" aria-multiline="true" suppresscontenteditablewarning autocapitalize="false" autocorrect="false" autosave="false" spellcheck="false"></div><div id="placeholder"></div></div><div id="border"></div><div id="bottom-wrapper"><slot id="bottom" name="bottom"></slot></div></div>';
|
|
10
10
|
const template = document.createElement("template");
|
|
11
11
|
template.innerHTML = templateHTML;
|
|
12
12
|
const SUPPORTS_SHADOW_SELECTION = typeof window.ShadowRoot.prototype.getSelection === "function";
|
|
@@ -24,7 +24,13 @@ class RichTextarea extends NectaryElement {
|
|
|
24
24
|
#cachedRange = null;
|
|
25
25
|
#lastSelectionInfo = null;
|
|
26
26
|
#prevDispatchedValue = null;
|
|
27
|
+
#changeDebounce = null;
|
|
27
28
|
#parseVisitor;
|
|
29
|
+
/* @internal
|
|
30
|
+
* The chip resolver is used to resolve the color and icon of a chip.
|
|
31
|
+
* It is used to resolve the color and icon of a chip.
|
|
32
|
+
* It is used to resolve the color and icon of a chip.
|
|
33
|
+
*/
|
|
28
34
|
#chipResolver = null;
|
|
29
35
|
constructor() {
|
|
30
36
|
super();
|
|
@@ -88,6 +94,7 @@ class RichTextarea extends NectaryElement {
|
|
|
88
94
|
}
|
|
89
95
|
disconnectedCallback() {
|
|
90
96
|
super.disconnectedCallback();
|
|
97
|
+
this.#clearChangeDebounce();
|
|
91
98
|
this.#controller.abort();
|
|
92
99
|
this.#controller = null;
|
|
93
100
|
}
|
|
@@ -413,9 +420,23 @@ class RichTextarea extends NectaryElement {
|
|
|
413
420
|
if (result.range !== null) {
|
|
414
421
|
setBrowserCaret(result.range);
|
|
415
422
|
}
|
|
423
|
+
this.#scheduleChangeDispatch();
|
|
416
424
|
}
|
|
417
425
|
this.#updateEditorEmptyClass();
|
|
418
426
|
}
|
|
427
|
+
#clearChangeDebounce() {
|
|
428
|
+
if (this.#changeDebounce !== null) {
|
|
429
|
+
clearTimeout(this.#changeDebounce);
|
|
430
|
+
this.#changeDebounce = null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
#scheduleChangeDispatch() {
|
|
434
|
+
this.#clearChangeDebounce();
|
|
435
|
+
this.#changeDebounce = setTimeout(() => {
|
|
436
|
+
this.#changeDebounce = null;
|
|
437
|
+
this.#dispatchChangeEvent();
|
|
438
|
+
}, 100);
|
|
439
|
+
}
|
|
419
440
|
#onInputFocus = () => {
|
|
420
441
|
if (this.#cachedRange !== null) {
|
|
421
442
|
setBrowserCaret(this.#cachedRange);
|
|
@@ -424,6 +445,7 @@ class RichTextarea extends NectaryElement {
|
|
|
424
445
|
};
|
|
425
446
|
#onInputBlur = () => {
|
|
426
447
|
this.dispatchEvent(new CustomEvent("-blur"));
|
|
448
|
+
this.#clearChangeDebounce();
|
|
427
449
|
this.#dispatchChangeEvent();
|
|
428
450
|
};
|
|
429
451
|
#dispatchChangeEvent() {
|
package/rich-textarea/types.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export type TSinchRichTextareaProps = {
|
|
|
24
24
|
'chip-color'?: TSinchTagColor;
|
|
25
25
|
/** Default icon for chips/tags */
|
|
26
26
|
'chip-icon'?: string;
|
|
27
|
+
/** Accessible label for the rich textarea */
|
|
27
28
|
'aria-label': string;
|
|
28
29
|
};
|
|
29
30
|
export type TSinchRichTextareaMethods = {
|
|
@@ -44,7 +45,7 @@ export type TSinchRichTextareaMethods = {
|
|
|
44
45
|
chipResolver: TChipResolver | null;
|
|
45
46
|
};
|
|
46
47
|
export type TSinchRichTextareaEvents = {
|
|
47
|
-
/** Change value handler */
|
|
48
|
+
/** Change value handler — fires while editing (debounced) and on blur */
|
|
48
49
|
'-change'?: (e: CustomEvent<string>) => void;
|
|
49
50
|
/** Focus handler */
|
|
50
51
|
'-focus'?: (e: CustomEvent<void>) => void;
|
package/rich-textarea/utils.js
CHANGED
|
@@ -55,11 +55,18 @@ const assertListItem = ($n) => {
|
|
|
55
55
|
throw new Error(`Node is not a ListItem: ${$n?.nodeName}`);
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
const BLOCK_CLASSNAME = "block";
|
|
58
59
|
const markListItemAsBlock = ($li) => {
|
|
59
|
-
$li.classList.add(
|
|
60
|
+
$li.classList.add(BLOCK_CLASSNAME);
|
|
60
61
|
};
|
|
61
62
|
const isListItemMarkedArBlock = ($li) => {
|
|
62
|
-
return $li.classList.contains(
|
|
63
|
+
return $li.classList.contains(BLOCK_CLASSNAME);
|
|
64
|
+
};
|
|
65
|
+
const markParagraphAsBlock = ($p) => {
|
|
66
|
+
$p.classList.add(BLOCK_CLASSNAME);
|
|
67
|
+
};
|
|
68
|
+
const isParagraphMarkedAsBlock = ($p) => {
|
|
69
|
+
return $p.classList.contains(BLOCK_CLASSNAME);
|
|
63
70
|
};
|
|
64
71
|
const MAX_LISTITEM_LEVEL = 4;
|
|
65
72
|
const removeListItemLevel = ($li) => {
|
|
@@ -1576,6 +1583,7 @@ const MD_ULISTITEM_TOKEN = "*";
|
|
|
1576
1583
|
const MD_OLISTITEM_TOKEN = "1.";
|
|
1577
1584
|
const MD_LISTITEM_JOIN = "\n";
|
|
1578
1585
|
const MD_PARAGRAPH_JOIN = "\n\n";
|
|
1586
|
+
const MD_SOFT_LINEBREAK = "\n";
|
|
1579
1587
|
const serializeTextReducer = (state, desc, i, descArray) => {
|
|
1580
1588
|
const { chunks } = state;
|
|
1581
1589
|
if (desc.isLink === true) {
|
|
@@ -1697,7 +1705,7 @@ const serializeRoot = ($root, range) => {
|
|
|
1697
1705
|
};
|
|
1698
1706
|
const flushParagraphChunks = () => {
|
|
1699
1707
|
if (paragraphChunks.length > 0) {
|
|
1700
|
-
chunks.push(
|
|
1708
|
+
chunks.push(paragraphChunks.join(MD_SOFT_LINEBREAK));
|
|
1701
1709
|
paragraphChunks.length = 0;
|
|
1702
1710
|
}
|
|
1703
1711
|
};
|
|
@@ -1715,6 +1723,9 @@ const serializeRoot = ($root, range) => {
|
|
|
1715
1723
|
} else {
|
|
1716
1724
|
assertTextBlock($child);
|
|
1717
1725
|
flushListChunks();
|
|
1726
|
+
if (isParagraph($child) && isParagraphMarkedAsBlock($child) && paragraphChunks.length > 0) {
|
|
1727
|
+
flushParagraphChunks();
|
|
1728
|
+
}
|
|
1718
1729
|
paragraphChunks.push(
|
|
1719
1730
|
serializeTextBlock($child, range)
|
|
1720
1731
|
);
|
|
@@ -1831,8 +1842,10 @@ const createParseVisitor = (doc) => {
|
|
|
1831
1842
|
},
|
|
1832
1843
|
paragraph() {
|
|
1833
1844
|
if (listsStack.length === 0) {
|
|
1834
|
-
$
|
|
1835
|
-
|
|
1845
|
+
const $p = createActuallyEmptyParagraph(doc);
|
|
1846
|
+
markParagraphAsBlock($p);
|
|
1847
|
+
$currentBlock = $p;
|
|
1848
|
+
$root.appendChild($p);
|
|
1836
1849
|
}
|
|
1837
1850
|
},
|
|
1838
1851
|
end() {
|
package/utils/markdown.js
CHANGED
|
@@ -11,7 +11,8 @@ const regStrikethrough = new RegExp("(?<!\\\\)~~(?<strike>.+?)(?<!\\\\)~~");
|
|
|
11
11
|
const regButtonPlaceholder = new RegExp("(?<!\\\\)\\[\\[(?<button>[a-zA-Z0-9_-]+)\\]\\]");
|
|
12
12
|
const regLink = new RegExp("(?<!\\\\)!?\\[(?<linktext>[^\\]]*?)\\]\\((?<linkhref>[^)]+?)\\)(\\{(?<linkattrs>[^)]+?)\\})?");
|
|
13
13
|
const regChip = new RegExp("(?<!\\\\)\\{\\{(?<chip>[a-zA-Z0-9_-]+)\\}\\}");
|
|
14
|
-
const
|
|
14
|
+
const regEmojiSeq = "(?![0-9#*])\\p{Emoji}(?:\\uFE0F|\\p{Emoji_Modifier})?\\u20E3?";
|
|
15
|
+
const regEmoji = new RegExp(`(?<emoji>\\p{RI}\\p{RI}|${regEmojiSeq}(?:\\u200D${regEmojiSeq})*)`, "u");
|
|
15
16
|
const regUList = /^(?<indent>[\t ]*?)[*+-][\t ]+(?<ultext>.*?)[\t ]*?$/;
|
|
16
17
|
const regOList = /^(?<indent>[\t ]*?)\d+\.[\t ]+(?<oltext>.*?)[\t ]*?$/;
|
|
17
18
|
const regEscapedChars = /\\(?<escaped>[\\\*_\[\]`~\{\}])/;
|