@nectary/components 5.41.2 → 5.42.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/bundle.js +50 -36
- 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/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": {
|
|
@@ -12237,11 +12216,18 @@ const assertListItem = ($n) => {
|
|
|
12237
12216
|
throw new Error(`Node is not a ListItem: ${$n?.nodeName}`);
|
|
12238
12217
|
}
|
|
12239
12218
|
};
|
|
12219
|
+
const BLOCK_CLASSNAME = "block";
|
|
12240
12220
|
const markListItemAsBlock = ($li) => {
|
|
12241
|
-
$li.classList.add(
|
|
12221
|
+
$li.classList.add(BLOCK_CLASSNAME);
|
|
12242
12222
|
};
|
|
12243
12223
|
const isListItemMarkedArBlock = ($li) => {
|
|
12244
|
-
return $li.classList.contains(
|
|
12224
|
+
return $li.classList.contains(BLOCK_CLASSNAME);
|
|
12225
|
+
};
|
|
12226
|
+
const markParagraphAsBlock = ($p) => {
|
|
12227
|
+
$p.classList.add(BLOCK_CLASSNAME);
|
|
12228
|
+
};
|
|
12229
|
+
const isParagraphMarkedAsBlock = ($p) => {
|
|
12230
|
+
return $p.classList.contains(BLOCK_CLASSNAME);
|
|
12245
12231
|
};
|
|
12246
12232
|
const MAX_LISTITEM_LEVEL = 4;
|
|
12247
12233
|
const removeListItemLevel = ($li) => {
|
|
@@ -13758,6 +13744,7 @@ const MD_ULISTITEM_TOKEN = "*";
|
|
|
13758
13744
|
const MD_OLISTITEM_TOKEN = "1.";
|
|
13759
13745
|
const MD_LISTITEM_JOIN = "\n";
|
|
13760
13746
|
const MD_PARAGRAPH_JOIN = "\n\n";
|
|
13747
|
+
const MD_SOFT_LINEBREAK = "\n";
|
|
13761
13748
|
const serializeTextReducer = (state, desc, i, descArray) => {
|
|
13762
13749
|
const { chunks } = state;
|
|
13763
13750
|
if (desc.isLink === true) {
|
|
@@ -13879,7 +13866,7 @@ const serializeRoot = ($root, range) => {
|
|
|
13879
13866
|
};
|
|
13880
13867
|
const flushParagraphChunks = () => {
|
|
13881
13868
|
if (paragraphChunks.length > 0) {
|
|
13882
|
-
chunks.push(
|
|
13869
|
+
chunks.push(paragraphChunks.join(MD_SOFT_LINEBREAK));
|
|
13883
13870
|
paragraphChunks.length = 0;
|
|
13884
13871
|
}
|
|
13885
13872
|
};
|
|
@@ -13897,6 +13884,9 @@ const serializeRoot = ($root, range) => {
|
|
|
13897
13884
|
} else {
|
|
13898
13885
|
assertTextBlock($child);
|
|
13899
13886
|
flushListChunks();
|
|
13887
|
+
if (isParagraph($child) && isParagraphMarkedAsBlock($child) && paragraphChunks.length > 0) {
|
|
13888
|
+
flushParagraphChunks();
|
|
13889
|
+
}
|
|
13900
13890
|
paragraphChunks.push(
|
|
13901
13891
|
serializeTextBlock($child, range)
|
|
13902
13892
|
);
|
|
@@ -14013,8 +14003,10 @@ const createParseVisitor = (doc) => {
|
|
|
14013
14003
|
},
|
|
14014
14004
|
paragraph() {
|
|
14015
14005
|
if (listsStack.length === 0) {
|
|
14016
|
-
$
|
|
14017
|
-
|
|
14006
|
+
const $p = createActuallyEmptyParagraph(doc);
|
|
14007
|
+
markParagraphAsBlock($p);
|
|
14008
|
+
$currentBlock = $p;
|
|
14009
|
+
$root.appendChild($p);
|
|
14018
14010
|
}
|
|
14019
14011
|
},
|
|
14020
14012
|
end() {
|
|
@@ -14041,7 +14033,7 @@ const setBrowserCaret = ({ startContainer, startOffset, endContainer, endOffset
|
|
|
14041
14033
|
range.setEnd(endContainer, endOffset);
|
|
14042
14034
|
selection.addRange(range);
|
|
14043
14035
|
};
|
|
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>';
|
|
14036
|
+
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
14037
|
const template$p = document.createElement("template");
|
|
14046
14038
|
template$p.innerHTML = templateHTML$p;
|
|
14047
14039
|
const SUPPORTS_SHADOW_SELECTION = typeof window.ShadowRoot.prototype.getSelection === "function";
|
|
@@ -14059,7 +14051,13 @@ class RichTextarea extends NectaryElement {
|
|
|
14059
14051
|
#cachedRange = null;
|
|
14060
14052
|
#lastSelectionInfo = null;
|
|
14061
14053
|
#prevDispatchedValue = null;
|
|
14054
|
+
#changeDebounce = null;
|
|
14062
14055
|
#parseVisitor;
|
|
14056
|
+
/* @internal
|
|
14057
|
+
* The chip resolver is used to resolve the color and icon of a chip.
|
|
14058
|
+
* It 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
|
+
*/
|
|
14063
14061
|
#chipResolver = null;
|
|
14064
14062
|
constructor() {
|
|
14065
14063
|
super();
|
|
@@ -14123,6 +14121,7 @@ class RichTextarea extends NectaryElement {
|
|
|
14123
14121
|
}
|
|
14124
14122
|
disconnectedCallback() {
|
|
14125
14123
|
super.disconnectedCallback();
|
|
14124
|
+
this.#clearChangeDebounce();
|
|
14126
14125
|
this.#controller.abort();
|
|
14127
14126
|
this.#controller = null;
|
|
14128
14127
|
}
|
|
@@ -14448,9 +14447,23 @@ class RichTextarea extends NectaryElement {
|
|
|
14448
14447
|
if (result.range !== null) {
|
|
14449
14448
|
setBrowserCaret(result.range);
|
|
14450
14449
|
}
|
|
14450
|
+
this.#scheduleChangeDispatch();
|
|
14451
14451
|
}
|
|
14452
14452
|
this.#updateEditorEmptyClass();
|
|
14453
14453
|
}
|
|
14454
|
+
#clearChangeDebounce() {
|
|
14455
|
+
if (this.#changeDebounce !== null) {
|
|
14456
|
+
clearTimeout(this.#changeDebounce);
|
|
14457
|
+
this.#changeDebounce = null;
|
|
14458
|
+
}
|
|
14459
|
+
}
|
|
14460
|
+
#scheduleChangeDispatch() {
|
|
14461
|
+
this.#clearChangeDebounce();
|
|
14462
|
+
this.#changeDebounce = setTimeout(() => {
|
|
14463
|
+
this.#changeDebounce = null;
|
|
14464
|
+
this.#dispatchChangeEvent();
|
|
14465
|
+
}, 100);
|
|
14466
|
+
}
|
|
14454
14467
|
#onInputFocus = () => {
|
|
14455
14468
|
if (this.#cachedRange !== null) {
|
|
14456
14469
|
setBrowserCaret(this.#cachedRange);
|
|
@@ -14459,6 +14472,7 @@ class RichTextarea extends NectaryElement {
|
|
|
14459
14472
|
};
|
|
14460
14473
|
#onInputBlur = () => {
|
|
14461
14474
|
this.dispatchEvent(new CustomEvent("-blur"));
|
|
14475
|
+
this.#clearChangeDebounce();
|
|
14462
14476
|
this.#dispatchChangeEvent();
|
|
14463
14477
|
};
|
|
14464
14478
|
#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() {
|