@nectary/components 2.6.1 → 2.7.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.
@@ -0,0 +1,464 @@
1
+ import { getEmojiBaseUrl } from '../emoji/utils';
2
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, getReactEventHandler, isElementFocused, NectaryElement, parseMarkdown, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
3
+ 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}.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 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>';
4
+ import { createParseVisitor, deleteContentBackward, formatIndent, formatInline, formatList, formatOutdent, getEndRange, getSelectionInfo, handleEmojiMousedown, insertFromPaste, insertLineBreak, insertLink, insertText, isEditorEmpty, isSelectionEqual, serializeMarkdown, setBrowserCaret } from './utils';
5
+ const template = document.createElement('template');
6
+ template.innerHTML = templateHTML;
7
+ const SUPPORTS_SHADOW_SELECTION = typeof window.ShadowRoot.prototype.getSelection === 'function';
8
+ defineCustomElement('sinch-rich-textarea', class extends NectaryElement {
9
+ #$input;
10
+ #$placeholder;
11
+ #controller = null;
12
+ #sh;
13
+ #pendingRangePolyfill = false;
14
+ #$bottomSlot;
15
+ #$bottomWrapper;
16
+ #$topSlot;
17
+ #$topWrapper;
18
+ #rangePolyfill = null;
19
+ #cachedRange = null;
20
+ #lastSelectionInfo = null;
21
+ #prevDispatchedValue = null;
22
+ #parseVisitor;
23
+ constructor() {
24
+ super();
25
+ const shadowRoot = this.attachShadow({
26
+ delegatesFocus: true
27
+ });
28
+ shadowRoot.appendChild(template.content.cloneNode(true));
29
+ this.#sh = shadowRoot;
30
+ this.#$input = shadowRoot.querySelector('#input');
31
+ this.#$placeholder = shadowRoot.querySelector('#placeholder');
32
+ this.#$topSlot = shadowRoot.querySelector('#top');
33
+ this.#$topWrapper = shadowRoot.querySelector('#top-wrapper');
34
+ this.#$bottomSlot = shadowRoot.querySelector('#bottom');
35
+ this.#$bottomWrapper = shadowRoot.querySelector('#bottom-wrapper');
36
+ Object.defineProperty(this.#$input, 'ownerDocument', {
37
+ value: shadowRoot
38
+ });
39
+ if (typeof shadowRoot.createElement !== 'function') {
40
+ Object.defineProperty(shadowRoot, 'createElement', {
41
+ value: document.createElement.bind(shadowRoot.ownerDocument)
42
+ });
43
+ }
44
+ Object.defineProperty(shadowRoot, 'createTextNode', {
45
+ value: document.createTextNode.bind(shadowRoot.ownerDocument)
46
+ });
47
+ Object.defineProperty(shadowRoot, 'createElementNS', {
48
+ value: document.createElementNS.bind(shadowRoot.ownerDocument)
49
+ });
50
+ Object.defineProperty(shadowRoot, 'createDocumentFragment', {
51
+ value: document.createDocumentFragment.bind(shadowRoot.ownerDocument)
52
+ });
53
+ this.#parseVisitor = createParseVisitor(shadowRoot);
54
+ }
55
+ connectedCallback() {
56
+ super.connectedCallback();
57
+ this.#controller = new AbortController();
58
+ const options = {
59
+ signal: this.#controller.signal
60
+ };
61
+ this.role = 'textbox';
62
+ this.ariaMultiLine = 'true';
63
+ this.#$input.addEventListener('beforeinput', this.#onBeforeInput, options);
64
+ this.#$input.addEventListener('keydown', this.#onKeydown, options);
65
+ this.#$input.addEventListener('mousedown', this.#onMouseDown, options);
66
+ this.#$input.addEventListener('focus', this.#onInputFocus, options);
67
+ this.#$input.addEventListener('blur', this.#onInputBlur, options);
68
+ this.#$input.addEventListener('dragstart', this.#onDragStart, options);
69
+ this.#$input.addEventListener('cut', this.#onCut, options);
70
+ this.#$input.addEventListener('copy', this.#onCopy, options);
71
+ this.#$input.addEventListener('paste', this.#onPaste, options);
72
+ this.#$bottomSlot.addEventListener('slotchange', this.#onBottomSlotChange, options);
73
+ this.#$topSlot.addEventListener('slotchange', this.#onTopSlotChange, options);
74
+ this.addEventListener('-change', this.#onChangeReactHandler, options);
75
+ this.addEventListener('-focus', this.#onFocusReactHandler, options);
76
+ this.addEventListener('-blur', this.#onBlurReactHandler, options);
77
+ this.addEventListener('-selection', this.#onSelectionReactHandler, options);
78
+ document.addEventListener('selectionchange', this.#onSelectionChange, options);
79
+ this.#parseVisitor.updateEmojiBaseUrl(getEmojiBaseUrl(this));
80
+ this.#onTopSlotChange();
81
+ this.#onBottomSlotChange();
82
+ this.#onValueChange(this.value);
83
+ this.#cachedRange = getEndRange(this.#$input);
84
+ this.#updateEditorEmptyClass();
85
+ }
86
+ disconnectedCallback() {
87
+ super.disconnectedCallback();
88
+ this.#controller.abort();
89
+ this.#controller = null;
90
+ }
91
+ static get observedAttributes() {
92
+ return ['value', 'placeholder'];
93
+ }
94
+ attributeChangedCallback(name, oldVal, newVal) {
95
+ switch (name) {
96
+ case 'value':
97
+ {
98
+ if (this.isConnected) {
99
+ this.#onValueChange(newVal);
100
+ }
101
+ break;
102
+ }
103
+ case 'placeholder':
104
+ {
105
+ this.#$placeholder.textContent = newVal;
106
+ updateAttribute(this, 'aria-placeholder', newVal);
107
+ break;
108
+ }
109
+ }
110
+ }
111
+ set value(value) {
112
+ updateAttribute(this, 'value', value);
113
+ }
114
+ get value() {
115
+ return getAttribute(this, 'value', '');
116
+ }
117
+ set placeholder(value) {
118
+ updateAttribute(this, 'placeholder', value);
119
+ }
120
+ get placeholder() {
121
+ return getAttribute(this, 'placeholder');
122
+ }
123
+ set disabled(isDisabled) {
124
+ updateBooleanAttribute(this, 'disabled', isDisabled);
125
+ }
126
+ get disabled() {
127
+ return getBooleanAttribute(this, 'disabled');
128
+ }
129
+ set rows(value) {
130
+ updateAttribute(this, 'rows', value);
131
+ }
132
+ get rows() {
133
+ return getIntegerAttribute(this, 'rows', 0);
134
+ }
135
+ get focusable() {
136
+ return true;
137
+ }
138
+ focus() {
139
+ this.#$input.focus();
140
+ }
141
+ blur() {
142
+ this.#$input.blur();
143
+ }
144
+ insertText(value) {
145
+ const res = this.#handleInput('insertText', this.#getCurrentRange(), value);
146
+ this.#handleActionResult(res);
147
+ }
148
+ insertLink(text, href) {
149
+ const res = this.#handleInput('insertLink', this.#getCurrentRange(), text, href);
150
+ this.#handleActionResult(res);
151
+ }
152
+ formatItalic() {
153
+ const res = this.#handleInput('formatItalic', this.#getCurrentRange());
154
+ this.#handleActionResult(res);
155
+ }
156
+ formatBold() {
157
+ const res = this.#handleInput('formatBold', this.#getCurrentRange());
158
+ this.#handleActionResult(res);
159
+ }
160
+ formatStrikethrough() {
161
+ const res = this.#handleInput('formatStrikeThrough', this.#getCurrentRange());
162
+ this.#handleActionResult(res);
163
+ }
164
+ formatCodeTag() {
165
+ const res = this.#handleInput('formatCodeTag', this.#getCurrentRange());
166
+ this.#handleActionResult(res);
167
+ }
168
+ formatOrderedList() {
169
+ const res = formatList(true, this.#getCurrentRange());
170
+ this.#handleActionResult(res);
171
+ }
172
+ formatUnorderedList() {
173
+ const res = formatList(false, this.#getCurrentRange());
174
+ this.#handleActionResult(res);
175
+ }
176
+ #getSelectionRange() {
177
+ if (SUPPORTS_SHADOW_SELECTION) {
178
+ const selection = this.#sh.getSelection();
179
+ if (selection === null || selection.rangeCount === 0) {
180
+ return null;
181
+ }
182
+ return selection.getRangeAt(0);
183
+ }
184
+ {
185
+ const selection = document.getSelection();
186
+ if (selection === null || selection.rangeCount === 0) {
187
+ return null;
188
+ }
189
+ const range = selection.getRangeAt(0);
190
+ if (this.#$input.contains(range.startContainer)) {
191
+ return range;
192
+ }
193
+ }
194
+ this.#pendingRangePolyfill = true;
195
+ this.#rangePolyfill = null;
196
+ document.execCommand('indent');
197
+ this.#pendingRangePolyfill = false;
198
+ return this.#rangePolyfill;
199
+ }
200
+ #onMouseDown = e => {
201
+ this.#handleActionResult(handleEmojiMousedown(e.target));
202
+ };
203
+ #onKeydown = e => {
204
+ if (e.shiftKey) {
205
+ switch (e.key) {
206
+ case 'Tab':
207
+ {
208
+ const res = this.#handleInput('formatOutdent', this.#getSelectionRange());
209
+ if (res.prevent) {
210
+ e.preventDefault();
211
+ }
212
+ this.#handleActionResult(res);
213
+ break;
214
+ }
215
+ }
216
+ } else {
217
+ switch (e.key) {
218
+ case 'Tab':
219
+ {
220
+ const res = this.#handleInput('formatIndent', this.#getSelectionRange());
221
+ if (res.prevent) {
222
+ e.preventDefault();
223
+ }
224
+ this.#handleActionResult(res);
225
+ break;
226
+ }
227
+ }
228
+ }
229
+ if (e.metaKey || e.ctrlKey) {
230
+ switch (e.key) {
231
+ case 'b':
232
+ {
233
+ const res = this.#handleInput('formatBold', this.#getSelectionRange());
234
+ if (res.prevent) {
235
+ e.preventDefault();
236
+ }
237
+ this.#handleActionResult(res);
238
+ break;
239
+ }
240
+ case 'i':
241
+ {
242
+ const res = this.#handleInput('formatItalic', this.#getSelectionRange());
243
+ if (res.prevent) {
244
+ e.preventDefault();
245
+ }
246
+ this.#handleActionResult(res);
247
+ break;
248
+ }
249
+ }
250
+ if (e.shiftKey) {
251
+ switch (e.key) {
252
+ case 'x':
253
+ {
254
+ const res = this.#handleInput('formatStrikeThrough', this.#getSelectionRange());
255
+ if (res.prevent) {
256
+ e.preventDefault();
257
+ }
258
+ this.#handleActionResult(res);
259
+ break;
260
+ }
261
+ case 'c':
262
+ {
263
+ const res = this.#handleInput('formatCodeTag', this.#getSelectionRange());
264
+ if (res.prevent) {
265
+ e.preventDefault();
266
+ }
267
+ this.#handleActionResult(res);
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ };
274
+ #onBeforeInput = e => {
275
+ const [range] = e.getTargetRanges();
276
+ if (this.#pendingRangePolyfill) {
277
+ this.#pendingRangePolyfill = false;
278
+ this.#rangePolyfill = range;
279
+ e.preventDefault();
280
+ e.stopImmediatePropagation();
281
+ return;
282
+ }
283
+ const handleResult = this.#handleInput(e.inputType, range, e.data);
284
+ if (handleResult.prevent) {
285
+ e.preventDefault();
286
+ }
287
+ this.#handleActionResult(handleResult);
288
+ };
289
+ #handleInput(inputType, range, text, href) {
290
+ if (range === null) {
291
+ return {
292
+ prevent: true,
293
+ range: null
294
+ };
295
+ }
296
+ switch (inputType) {
297
+ case 'deleteContent':
298
+ case 'deleteContentForward':
299
+ case 'deleteWordForward':
300
+ case 'deleteContentBackward':
301
+ case 'deleteWordBackward':
302
+ {
303
+ return deleteContentBackward(this.#$input, range);
304
+ }
305
+ case 'insertLineBreak':
306
+ case 'insertParagraph':
307
+ {
308
+ return insertLineBreak(range);
309
+ }
310
+ case 'insertReplacementText':
311
+ case 'insertText':
312
+ {
313
+ return insertText(this.#$input, text, range);
314
+ }
315
+ case 'insertLink':
316
+ {
317
+ return insertLink(this.#$input, text, href, range);
318
+ }
319
+ case 'formatItalic':
320
+ case 'formatBold':
321
+ case 'formatStrikeThrough':
322
+ case 'formatCodeTag':
323
+ {
324
+ return formatInline(inputType, range);
325
+ }
326
+ case 'formatIndent':
327
+ {
328
+ return formatIndent(range);
329
+ }
330
+ case 'formatOutdent':
331
+ {
332
+ return formatOutdent(range);
333
+ }
334
+ default:
335
+ {
336
+ return {
337
+ prevent: true,
338
+ range: null
339
+ };
340
+ }
341
+ }
342
+ }
343
+ #handleActionResult(result) {
344
+ if (result.prevent) {
345
+ this.#cachedRange = result.range;
346
+ if (result.range !== null) {
347
+ setBrowserCaret(result.range);
348
+ }
349
+ }
350
+ this.#updateEditorEmptyClass();
351
+ }
352
+ #onInputFocus = () => {
353
+ if (this.#cachedRange !== null) {
354
+ setBrowserCaret(this.#cachedRange);
355
+ }
356
+ this.dispatchEvent(new CustomEvent('-focus'));
357
+ };
358
+ #onInputBlur = () => {
359
+ this.dispatchEvent(new CustomEvent('-blur'));
360
+ this.#dispatchChangeEvent();
361
+ };
362
+ #dispatchChangeEvent() {
363
+ const value = serializeMarkdown(this.#$input, null);
364
+ this.#prevDispatchedValue = value;
365
+ if (value !== this.value) {
366
+ this.dispatchEvent(new CustomEvent('-change', {
367
+ detail: value
368
+ }));
369
+ }
370
+ }
371
+ #getCurrentRange = () => {
372
+ if (isElementFocused(this.#$input)) {
373
+ this.#cachedRange = this.#getSelectionRange();
374
+ }
375
+ if (this.#cachedRange === null) {
376
+ this.#cachedRange = getEndRange(this.#$input);
377
+ }
378
+ return this.#cachedRange;
379
+ };
380
+ #onSelectionChange = () => {
381
+ if (isElementFocused(this.#$input)) {
382
+ this.#cachedRange = this.#getSelectionRange();
383
+ }
384
+ if (this.#cachedRange === null) {
385
+ return;
386
+ }
387
+ const selectionInfo = getSelectionInfo(this.#cachedRange);
388
+ if (!isSelectionEqual(this.#lastSelectionInfo, selectionInfo)) {
389
+ this.#lastSelectionInfo = selectionInfo;
390
+ this.dispatchEvent(new CustomEvent('-selection', {
391
+ detail: selectionInfo
392
+ }));
393
+ }
394
+ };
395
+ #onValueChange(value) {
396
+ if (value === this.#prevDispatchedValue) {
397
+ return;
398
+ }
399
+ if (value === null || value.length === 0) {
400
+ this.#$input.innerHTML = '';
401
+ this.#cachedRange = getEndRange(this.#$input);
402
+ this.#prevDispatchedValue = value;
403
+ this.#updateEditorEmptyClass();
404
+ return;
405
+ }
406
+ this.#cachedRange = null;
407
+ this.#prevDispatchedValue = value;
408
+ this.#$input.replaceChildren(parseMarkdown(value, this.#parseVisitor.createVisitor()));
409
+ this.#updateEditorEmptyClass();
410
+ }
411
+ #onCopy = e => {
412
+ e.preventDefault();
413
+ if (this.#cachedRange === null) {
414
+ return;
415
+ }
416
+ if (e.clipboardData != null) {
417
+ e.clipboardData.setData('text/plain', serializeMarkdown(this.#$input, this.#cachedRange));
418
+ }
419
+ };
420
+ #onCut = e => {
421
+ e.preventDefault();
422
+ if (this.#cachedRange === null) {
423
+ return;
424
+ }
425
+ if (e.clipboardData != null) {
426
+ e.clipboardData.setData('text/plain', serializeMarkdown(this.#$input, this.#cachedRange));
427
+ }
428
+ this.#handleActionResult(deleteContentBackward(this.#$input, this.#cachedRange));
429
+ };
430
+ #onPaste = e => {
431
+ e.preventDefault();
432
+ const pasteValue = e.clipboardData?.getData('text/plain') ?? '';
433
+ if (pasteValue.length === 0 || this.#cachedRange === null) {
434
+ return;
435
+ }
436
+ this.#handleActionResult(insertFromPaste(pasteValue, this.#cachedRange, this.#parseVisitor.createVisitor()));
437
+ };
438
+ #updateEditorEmptyClass() {
439
+ setClass(this.#$input, 'empty', isEditorEmpty(this.#$input));
440
+ }
441
+ #onBottomSlotChange = () => {
442
+ const isEmpty = this.#$bottomSlot.assignedElements().length === 0;
443
+ setClass(this.#$bottomWrapper, 'empty', isEmpty);
444
+ };
445
+ #onTopSlotChange = () => {
446
+ const isEmpty = this.#$topSlot.assignedElements().length === 0;
447
+ setClass(this.#$topWrapper, 'empty', isEmpty);
448
+ };
449
+ #onDragStart = e => {
450
+ e.preventDefault();
451
+ };
452
+ #onChangeReactHandler = e => {
453
+ getReactEventHandler(this, 'on-change')?.(e);
454
+ };
455
+ #onFocusReactHandler = () => {
456
+ getReactEventHandler(this, 'on-focus')?.();
457
+ };
458
+ #onBlurReactHandler = () => {
459
+ getReactEventHandler(this, 'on-blur')?.();
460
+ };
461
+ #onSelectionReactHandler = e => {
462
+ getReactEventHandler(this, 'on-selection')?.(e);
463
+ };
464
+ });
@@ -0,0 +1,48 @@
1
+ import type { TSinchElementReact } from '../types';
2
+ export type TRichTextareaSelection = {
3
+ italic: boolean;
4
+ bold: boolean;
5
+ strikethrough: boolean;
6
+ codetag: boolean;
7
+ link: boolean;
8
+ ulist: boolean;
9
+ olist: boolean;
10
+ };
11
+ export type TSinchRichTextareaElement = HTMLElement & {
12
+ /** Value */
13
+ value: string;
14
+ /** Text that appears in the text field when it has no value set */
15
+ placeholder: string | null;
16
+ insertText(value: string): void;
17
+ insertLink(text: string, href: string): void;
18
+ formatItalic(): void;
19
+ formatBold(): void;
20
+ formatStrikethrough(): void;
21
+ formatCodeTag(): void;
22
+ formatOrderedList(): void;
23
+ formatUnorderedList(): void;
24
+ /** Change value event */
25
+ addEventListener(type: '-change', listener: (e: CustomEvent<string>) => void): void;
26
+ /** Focus event */
27
+ addEventListener(type: '-focus', listener: (e: CustomEvent<void>) => void): void;
28
+ /** Blur event */
29
+ addEventListener(type: '-blur', listener: (e: CustomEvent<void>) => void): void;
30
+ addEventListener(type: '-selection', listener: (e: CustomEvent<TRichTextareaSelection>) => void): void;
31
+ /** Value */
32
+ setAttribute(name: 'value', value: string): void;
33
+ /** Text that appears in the text field when it has no value set */
34
+ setAttribute(name: 'placeholder', value: string): void;
35
+ };
36
+ export type TSinchRichTextareaReact = TSinchElementReact<TSinchRichTextareaElement> & {
37
+ /** Value */
38
+ value: string;
39
+ /** Text that appears in the text field when it has no value set */
40
+ placeholder?: string;
41
+ 'aria-label': string;
42
+ /** Change value handler */
43
+ 'on-change'?: (e: CustomEvent<string>) => void;
44
+ /** Focus handler */
45
+ 'on-focus'?: (e: CustomEvent<void>) => void;
46
+ /** Blur handler */
47
+ 'on-blur'?: (e: CustomEvent<void>) => void;
48
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import type { TRichTextareaSelection } from './types';
2
+ import type { TMarkdownParseVisitor } from '../utils';
3
+ export interface TRichTextareaRoot extends HTMLElement {
4
+ nodeName: 'DIV';
5
+ }
6
+ export type TRange = {
7
+ endContainer: Node;
8
+ endOffset: number;
9
+ startContainer: Node;
10
+ startOffset: number;
11
+ };
12
+ export type TRichTextareaFormatInputType = 'formatItalic' | 'formatBold' | 'formatStrikeThrough' | 'formatCodeTag';
13
+ export declare const formatInline: (formatType: TRichTextareaFormatInputType, range: TRange) => TActionResult;
14
+ export declare const formatIndent: (range: TRange) => TActionResult;
15
+ export declare const formatOutdent: (range: TRange) => TActionResult;
16
+ export declare const formatList: (isOrdered: boolean, range: TRange) => TActionResult;
17
+ export type TActionResult = {
18
+ prevent: false;
19
+ } | {
20
+ range: TRange | null;
21
+ prevent: true;
22
+ };
23
+ export declare const deleteContentBackward: ($root: TRichTextareaRoot, range: TRange) => TActionResult;
24
+ export declare const insertLink: ($root: TRichTextareaRoot, text: string, href: string, range: TRange) => TActionResult;
25
+ export declare const insertLineBreak: (range: TRange) => TActionResult;
26
+ export declare const insertText: ($root: TRichTextareaRoot, data: string | null, range: TRange) => TActionResult;
27
+ export declare const insertFromPaste: (data: string, range: Readonly<TRange>, visitor: TMarkdownParseVisitor) => TActionResult;
28
+ export declare const handleEmojiMousedown: ($n: Node) => TActionResult;
29
+ export declare const getBeginRange: ($root: TRichTextareaRoot) => TRange;
30
+ export declare const getEndRange: ($root: TRichTextareaRoot) => TRange;
31
+ export declare const getSelectionInfo: (range: TRange) => TRichTextareaSelection;
32
+ export declare const isSelectionEqual: (a: TRichTextareaSelection | null, b: TRichTextareaSelection | null) => boolean;
33
+ export declare const isEditorEmpty: ($root: TRichTextareaRoot) => boolean;
34
+ export declare const serializeMarkdown: ($root: TRichTextareaRoot, range: Readonly<TRange> | null) => string;
35
+ export declare const createParseVisitor: (doc: Document) => {
36
+ updateEmojiBaseUrl(url: string | null): void;
37
+ createVisitor(): TMarkdownParseVisitor;
38
+ };
39
+ export declare const setBrowserCaret: ({ startContainer, startOffset, endContainer, endOffset }: TRange) => void;