drab 5.0.0 → 5.0.2
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/README.md +0 -5
- package/animate/define.d.ts +1 -2
- package/animate/define.iife.js +1 -1
- package/animate/define.js +2 -1
- package/animate/index.d.ts +8 -5
- package/animate/index.iife.js +1 -1
- package/animate/index.js +138 -1
- package/{index-SyRipepB.d.ts → base/copy/index.d.ts} +4 -5
- package/base/copy/index.js +25 -0
- package/base/define.d.ts +1 -2
- package/base/define.js +2 -1
- package/base/index.d.ts +1 -3
- package/base/index.js +108 -1
- package/breakpoint/define.d.ts +1 -2
- package/breakpoint/define.js +2 -1
- package/breakpoint/index.d.ts +7 -5
- package/breakpoint/index.js +55 -1
- package/contextmenu/define.d.ts +1 -2
- package/contextmenu/define.iife.js +1 -1
- package/contextmenu/define.js +2 -1
- package/contextmenu/index.d.ts +4 -6
- package/contextmenu/index.iife.js +1 -1
- package/contextmenu/index.js +71 -1
- package/copy/define.d.ts +1 -2
- package/copy/define.js +2 -1
- package/copy/index.d.ts +4 -6
- package/copy/index.iife.js +1 -1
- package/copy/index.js +13 -1
- package/define/index.iife.js +9 -9
- package/define.d.ts +1 -0
- package/define.js +4 -0
- package/details/define.d.ts +1 -2
- package/details/define.iife.js +1 -1
- package/details/define.js +2 -1
- package/details/index.d.ts +4 -6
- package/details/index.iife.js +1 -1
- package/details/index.js +46 -1
- package/dialog/define.d.ts +1 -2
- package/dialog/define.iife.js +1 -1
- package/dialog/define.js +2 -1
- package/dialog/index.d.ts +4 -6
- package/dialog/index.iife.js +1 -1
- package/dialog/index.js +44 -1
- package/editor/define.d.ts +1 -2
- package/editor/define.js +2 -1
- package/editor/index.d.ts +5 -6
- package/editor/index.iife.js +7 -7
- package/editor/index.js +480 -1
- package/fullscreen/define.d.ts +1 -2
- package/fullscreen/define.iife.js +1 -1
- package/fullscreen/define.js +2 -1
- package/fullscreen/index.d.ts +4 -5
- package/fullscreen/index.iife.js +1 -1
- package/fullscreen/index.js +45 -1
- package/index.d.ts +14 -14
- package/index.iife.js +9 -9
- package/index.js +14 -1
- package/package.json +6 -5
- package/popover/define.d.ts +1 -2
- package/popover/define.iife.js +1 -1
- package/popover/define.js +2 -1
- package/popover/index.d.ts +4 -6
- package/popover/index.iife.js +1 -1
- package/popover/index.js +74 -1
- package/share/define.d.ts +1 -2
- package/share/define.js +2 -1
- package/share/index.d.ts +5 -7
- package/share/index.js +34 -1
- package/tablesort/define.d.ts +1 -2
- package/tablesort/define.iife.js +1 -1
- package/tablesort/define.js +2 -1
- package/tablesort/index.d.ts +4 -5
- package/tablesort/index.iife.js +1 -1
- package/tablesort/index.js +109 -1
- package/types/index.d.ts +11 -0
- package/types/index.js +1 -0
- package/youtube/define.d.ts +1 -2
- package/youtube/define.js +2 -1
- package/youtube/index.d.ts +4 -5
- package/youtube/index.iife.js +1 -1
- package/youtube/index.js +56 -1
- package/chunk-57VEEUFG.js +0 -1
- package/chunk-5JV4T7GM.js +0 -1
- package/chunk-6HYPZWQ4.js +0 -1
- package/chunk-7F7CQUEG.js +0 -1
- package/chunk-7KU2PRW5.js +0 -1
- package/chunk-7S6DTKGH.js +0 -1
- package/chunk-G5WFHYNX.js +0 -1
- package/chunk-GPET75FT.js +0 -9
- package/chunk-IQJQPZUL.js +0 -1
- package/chunk-JMJUWKN2.js +0 -1
- package/chunk-MXKU7AKV.js +0 -1
- package/chunk-T7RZI3ZL.js +0 -1
- package/chunk-TSTTUEAF.js +0 -1
- package/chunk-VEVFQB5N.js +0 -1
- package/define/index.d.ts +0 -2
- package/define/index.js +0 -1
package/editor/index.js
CHANGED
@@ -1 +1,480 @@
|
|
1
|
-
import{
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
/**
|
3
|
+
* Enhances the `textarea` element with controls to add content and keyboard shortcuts. Compared to other WYSIWYG editors, the `text` value is just a `string`, so you can easily store it in a database or manipulate it without learning a separate API.
|
4
|
+
*
|
5
|
+
* `data-value`
|
6
|
+
*
|
7
|
+
* Set the value of the text to be inserted using the `data-value` attribute on the `trigger`.
|
8
|
+
*
|
9
|
+
* `data-type`
|
10
|
+
*
|
11
|
+
* Set the `data-type` attribute of the `trigger` to specify how the content should be inserted into the `textarea`.
|
12
|
+
*
|
13
|
+
* - `block` will be inserted at the beginning of the selected line.
|
14
|
+
* - `wrap` will be inserted before and after the current selection.
|
15
|
+
* - `inline` will be inserted at the current selection.
|
16
|
+
*
|
17
|
+
* `data-key`
|
18
|
+
*
|
19
|
+
* Add a `ctrl`/`meta` keyboard shortcut for the content based on the `data-key` attribute.
|
20
|
+
*
|
21
|
+
* Other features:
|
22
|
+
*
|
23
|
+
* - Automatically adds closing characters for `keyPairs`. For example, when typing `(`, `)` will be inserted and typed over when reached. All content with `data-type="wrap"` is also added to `keyPairs`.
|
24
|
+
* - Highlights the first word of the text inserted if it contains letters.
|
25
|
+
* - Automatically increments/decrements ordered lists.
|
26
|
+
* - Adds the starting character to the next line for `block` content.
|
27
|
+
* - On double click, highlight is corrected to only highlight the current word without space around it.
|
28
|
+
* - `tab` key will indent and not change focus if the selection is within a code block (three backticks).
|
29
|
+
* - When text is highlighted and a `wrap` character `keyPair` is typed, the highlighted text will be wrapped with the character instead of removing it. For example, if a word is highlighted and the `"` character is typed, the work will be surrounded by `"`s.
|
30
|
+
*/
|
31
|
+
export class Editor extends Base {
|
32
|
+
/** Array of keyPair characters that have been opened. */
|
33
|
+
#openChars = [];
|
34
|
+
/** The characters that will be automatically closed when typed. */
|
35
|
+
keyPairs = {
|
36
|
+
"(": ")",
|
37
|
+
"{": "}",
|
38
|
+
"[": "]",
|
39
|
+
"<": ">",
|
40
|
+
'"': '"',
|
41
|
+
"`": "`",
|
42
|
+
};
|
43
|
+
constructor() {
|
44
|
+
super();
|
45
|
+
// add any `type: "wrap"` values from `contentElements` to `keyPairs`
|
46
|
+
for (const element of this.#contentElements) {
|
47
|
+
if (element.type === "wrap") {
|
48
|
+
this.keyPairs[element.value] = element.value;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
/** The `content`, expects an `HTMLTextAreaElement`. */
|
53
|
+
get textArea() {
|
54
|
+
return this.getContent(HTMLTextAreaElement);
|
55
|
+
}
|
56
|
+
/** The current `value` of the `textarea`. */
|
57
|
+
get text() {
|
58
|
+
return this.textArea.value;
|
59
|
+
}
|
60
|
+
set text(value) {
|
61
|
+
this.textArea.value = value;
|
62
|
+
}
|
63
|
+
/** An array of `ContentElement`s derived from each `trigger`'s data attributes. */
|
64
|
+
get #contentElements() {
|
65
|
+
const contentElements = [];
|
66
|
+
for (const trigger of this.getTrigger()) {
|
67
|
+
contentElements.push(this.#getContentElement(trigger));
|
68
|
+
}
|
69
|
+
return contentElements;
|
70
|
+
}
|
71
|
+
/**
|
72
|
+
* - splits the content by "```" and finds the current index
|
73
|
+
* of the selectionStart
|
74
|
+
*
|
75
|
+
* @returns current codeblock (index) of selectionStart
|
76
|
+
*/
|
77
|
+
get #currentBlock() {
|
78
|
+
const blocks = this.text.split("```");
|
79
|
+
let totalChars = 0;
|
80
|
+
for (const [i, block] of blocks.entries()) {
|
81
|
+
totalChars += block.length + 3;
|
82
|
+
if (this.#selectionStart < totalChars) {
|
83
|
+
return i;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return 0;
|
87
|
+
}
|
88
|
+
/** Gets the end position of the selection */
|
89
|
+
get #selectionEnd() {
|
90
|
+
return this.textArea.selectionEnd;
|
91
|
+
}
|
92
|
+
/** Gets the start position of the selection. */
|
93
|
+
get #selectionStart() {
|
94
|
+
return this.textArea.selectionStart;
|
95
|
+
}
|
96
|
+
/** Sets the current cursor selection in the `textarea` */
|
97
|
+
#setSelectionRange(start, end) {
|
98
|
+
this.textArea.setSelectionRange(start, end);
|
99
|
+
}
|
100
|
+
/**
|
101
|
+
* @param trigger The trigger html element.
|
102
|
+
* @returns The ContentElement based on the `trigger`'s attributes.
|
103
|
+
*/
|
104
|
+
#getContentElement(trigger) {
|
105
|
+
const type = trigger.dataset.type;
|
106
|
+
const value = trigger.dataset.value;
|
107
|
+
const key = trigger.dataset.key ?? undefined;
|
108
|
+
return { type, value, key };
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* - Inserts text into the `textarea` based on the `display` property of
|
112
|
+
* the `ContentElement`.
|
113
|
+
*
|
114
|
+
* @param el the content element
|
115
|
+
* @param selectionStart current start position the selection
|
116
|
+
* @param selectionEnd current end position of the selection
|
117
|
+
*/
|
118
|
+
async #insertText(el, selectionStart, selectionEnd) {
|
119
|
+
if (el.type === "inline") {
|
120
|
+
// insert at current position
|
121
|
+
this.text = `${this.text.slice(0, selectionEnd)}${el.value}${this.text.slice(selectionEnd)}`;
|
122
|
+
}
|
123
|
+
else if (el.type === "wrap") {
|
124
|
+
this.text = insertChar(this.text, el.value, selectionStart);
|
125
|
+
this.text = insertChar(this.text, this.keyPairs[el.value], selectionEnd + el.value.length);
|
126
|
+
// if single char, add to opened
|
127
|
+
if (el.value.length < 2)
|
128
|
+
this.#openChars.push(el.value);
|
129
|
+
}
|
130
|
+
else if (el.type === "block") {
|
131
|
+
const { lines, lineNumber } = this.#getLineInfo();
|
132
|
+
const firstChar = el.value.at(0);
|
133
|
+
// add the string to the beginning of the line
|
134
|
+
if (firstChar && lines[lineNumber]?.startsWith(firstChar)) {
|
135
|
+
// avoids `# # # `, instead adds trimmed => `### `
|
136
|
+
lines[lineNumber] = el.value.trim() + lines[lineNumber];
|
137
|
+
}
|
138
|
+
else {
|
139
|
+
lines[lineNumber] = el.value + lines[lineNumber];
|
140
|
+
}
|
141
|
+
this.text = lines.join("\n");
|
142
|
+
}
|
143
|
+
}
|
144
|
+
/**
|
145
|
+
* - Sets the caret position after text is inserted based on
|
146
|
+
* the length of the text.
|
147
|
+
* - Highlights text if the content contains any letters.
|
148
|
+
*
|
149
|
+
* @param text
|
150
|
+
* @param selectionStart current start position the selection
|
151
|
+
* @param selectionEnd current end position of the selection
|
152
|
+
*/
|
153
|
+
async #setCaretPosition(text, selectionStart, selectionEnd) {
|
154
|
+
let startPos = 0;
|
155
|
+
let endPos = 0;
|
156
|
+
if (/[a-z]/i.test(text)) {
|
157
|
+
// if string contains letters, highlight the first word
|
158
|
+
for (let i = selectionEnd; i < this.text.length; i++) {
|
159
|
+
if (this.text[i]?.match(/[a-z]/i)) {
|
160
|
+
if (!startPos) {
|
161
|
+
startPos = i;
|
162
|
+
}
|
163
|
+
else {
|
164
|
+
endPos = i + 1;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
else if (startPos) {
|
168
|
+
break;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
// leave the cursor in place
|
174
|
+
startPos = selectionStart + text.length;
|
175
|
+
endPos = selectionEnd + text.length;
|
176
|
+
}
|
177
|
+
this.#setSelectionRange(startPos, endPos);
|
178
|
+
this.textArea.focus();
|
179
|
+
}
|
180
|
+
/**
|
181
|
+
* - Inserts the text and then sets the caret position
|
182
|
+
* based on the `ContentElement` selected.
|
183
|
+
*
|
184
|
+
* @param el selected content element
|
185
|
+
*/
|
186
|
+
async #addContent(el) {
|
187
|
+
const selectionEnd = this.#selectionEnd;
|
188
|
+
const selectionStart = this.#selectionStart;
|
189
|
+
await this.#insertText(el, selectionStart, selectionEnd);
|
190
|
+
this.#setCaretPosition(el.value, selectionStart, selectionEnd);
|
191
|
+
}
|
192
|
+
/**
|
193
|
+
* - checks if there is a block element or a number
|
194
|
+
* at the beginning of the string
|
195
|
+
*
|
196
|
+
* @param str
|
197
|
+
* @returns what is found, or the empty string
|
198
|
+
*/
|
199
|
+
#getRepeat(str) {
|
200
|
+
if (str) {
|
201
|
+
const blockStrings = [];
|
202
|
+
this.#contentElements.forEach((el) => {
|
203
|
+
if (el.type === "block")
|
204
|
+
blockStrings.push(el.value);
|
205
|
+
});
|
206
|
+
for (let i = 0; i < blockStrings.length; i++) {
|
207
|
+
const repeatString = blockStrings[i];
|
208
|
+
if (repeatString && str.startsWith(repeatString)) {
|
209
|
+
return repeatString;
|
210
|
+
}
|
211
|
+
}
|
212
|
+
const repeatNum = startsWithNumberAndPeriod(str);
|
213
|
+
if (repeatNum)
|
214
|
+
return `${repeatNum}. `;
|
215
|
+
}
|
216
|
+
return "";
|
217
|
+
}
|
218
|
+
/**
|
219
|
+
* @returns lines as an array, current line number, current column number
|
220
|
+
*
|
221
|
+
* @example
|
222
|
+
*
|
223
|
+
* ```js
|
224
|
+
* const { lines, lineNumber, columnNumber } = getLineInfo();
|
225
|
+
* ```
|
226
|
+
*/
|
227
|
+
#getLineInfo() {
|
228
|
+
const lines = this.text.split("\n");
|
229
|
+
let characterCount = 0;
|
230
|
+
for (let i = 0; i < lines.length; i++) {
|
231
|
+
const lineLength = lines.at(i)?.length ?? 0;
|
232
|
+
// for each line
|
233
|
+
characterCount++; // account for removed "\n" due to .split()
|
234
|
+
characterCount += lineLength;
|
235
|
+
// find the line that the cursor is on
|
236
|
+
if (characterCount > this.#selectionEnd) {
|
237
|
+
return {
|
238
|
+
lines,
|
239
|
+
lineNumber: i,
|
240
|
+
columnNumber: this.#selectionEnd - (characterCount - lineLength - 1),
|
241
|
+
};
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return { lines, lineNumber: 0, columnNumber: 0 };
|
245
|
+
}
|
246
|
+
/**
|
247
|
+
* - Increments/decrements the start of following lines if they are numbers
|
248
|
+
*
|
249
|
+
* Prevents this:
|
250
|
+
*
|
251
|
+
* ```
|
252
|
+
* 1. presses enter here when two items in list
|
253
|
+
* 2.
|
254
|
+
* 2.
|
255
|
+
* ```
|
256
|
+
*
|
257
|
+
* Instead:
|
258
|
+
*
|
259
|
+
* ```
|
260
|
+
* 1.
|
261
|
+
* 2.
|
262
|
+
* 3.
|
263
|
+
* ```
|
264
|
+
*
|
265
|
+
* @param currentLineNumber
|
266
|
+
* @param decrement if following lines should be decremented instead of incremented
|
267
|
+
*/
|
268
|
+
#correctFollowing(currentLineNumber, decrement = false) {
|
269
|
+
const { lines } = this.#getLineInfo();
|
270
|
+
for (let i = currentLineNumber + 1; i < lines.length; i++) {
|
271
|
+
const line = lines[i];
|
272
|
+
if (line) {
|
273
|
+
const num = startsWithNumberAndPeriod(line);
|
274
|
+
if (!num) {
|
275
|
+
break;
|
276
|
+
}
|
277
|
+
else {
|
278
|
+
let newNum;
|
279
|
+
if (decrement) {
|
280
|
+
if (num > 1) {
|
281
|
+
newNum = num - 1;
|
282
|
+
}
|
283
|
+
else {
|
284
|
+
break;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
else {
|
288
|
+
newNum = num + 1;
|
289
|
+
}
|
290
|
+
lines[i] = line.slice(String(num).length); // remove number from start
|
291
|
+
lines[i] = String(newNum) + lines[i];
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
this.text = lines.join("\n");
|
296
|
+
}
|
297
|
+
mount() {
|
298
|
+
this.textArea.addEventListener("keydown", async (e) => {
|
299
|
+
// these keys will reset the type over for characters like "
|
300
|
+
const resetKeys = ["ArrowUp", "ArrowDown", "Delete"];
|
301
|
+
const nextChar = this.text[this.#selectionEnd] ?? "";
|
302
|
+
if (resetKeys.includes(e.key)) {
|
303
|
+
// reset
|
304
|
+
this.#openChars = [];
|
305
|
+
}
|
306
|
+
else if (e.key === "Backspace") {
|
307
|
+
const prevChar = this.text[this.#selectionStart - 1];
|
308
|
+
if (prevChar &&
|
309
|
+
prevChar in this.keyPairs &&
|
310
|
+
nextChar === this.keyPairs[prevChar]) {
|
311
|
+
// remove both characters if the next one is the match of the prev
|
312
|
+
e.preventDefault();
|
313
|
+
const start = this.#selectionStart - 1;
|
314
|
+
const end = this.#selectionEnd - 1;
|
315
|
+
this.text = removeChar(this.text, start);
|
316
|
+
this.text = removeChar(this.text, end);
|
317
|
+
setTimeout(() => {
|
318
|
+
this.#setSelectionRange(start, end);
|
319
|
+
}, 0);
|
320
|
+
this.#openChars.pop();
|
321
|
+
}
|
322
|
+
if (prevChar === "\n" && this.#selectionStart === this.#selectionEnd) {
|
323
|
+
// see `correctFollowing`
|
324
|
+
e.preventDefault();
|
325
|
+
const newPos = this.#selectionStart - 1;
|
326
|
+
const { lineNumber } = this.#getLineInfo();
|
327
|
+
this.#correctFollowing(lineNumber, true);
|
328
|
+
this.text = removeChar(this.text, newPos);
|
329
|
+
setTimeout(async () => {
|
330
|
+
this.#setSelectionRange(newPos, newPos);
|
331
|
+
}, 0);
|
332
|
+
}
|
333
|
+
}
|
334
|
+
else if (e.key === "Tab") {
|
335
|
+
if (this.#currentBlock % 2 !== 0) {
|
336
|
+
// if caret is inside of a codeblock, indent
|
337
|
+
e.preventDefault();
|
338
|
+
await this.#addContent({
|
339
|
+
type: "inline",
|
340
|
+
value: "\t",
|
341
|
+
});
|
342
|
+
}
|
343
|
+
}
|
344
|
+
else if (e.key === "Enter") {
|
345
|
+
// autocomplete start of next line if block or number
|
346
|
+
const { lines, lineNumber, columnNumber } = this.#getLineInfo();
|
347
|
+
const currentLine = lines.at(lineNumber);
|
348
|
+
let repeat = this.#getRepeat(currentLine);
|
349
|
+
const original = repeat;
|
350
|
+
const num = startsWithNumberAndPeriod(repeat);
|
351
|
+
// line starts with number and period? - increment
|
352
|
+
if (num)
|
353
|
+
repeat = `${num + 1}. `;
|
354
|
+
if (repeat && original.length < columnNumber) {
|
355
|
+
e.preventDefault();
|
356
|
+
if (num)
|
357
|
+
this.#correctFollowing(lineNumber);
|
358
|
+
await this.#addContent({
|
359
|
+
type: "inline",
|
360
|
+
value: `\n${repeat}`,
|
361
|
+
});
|
362
|
+
}
|
363
|
+
else if (repeat && original.length === columnNumber) {
|
364
|
+
// remove if the repeat and caret at the end of the original
|
365
|
+
e.preventDefault();
|
366
|
+
// have to set a placeholder since `this.#selectionEnd` will change
|
367
|
+
// as characters are being removed
|
368
|
+
const originalSelectionEnd = this.#selectionEnd;
|
369
|
+
// go back the the length of the original
|
370
|
+
const newPos = originalSelectionEnd - original.length;
|
371
|
+
// for each character in the original
|
372
|
+
for (let i = 0; i < original.length; i++) {
|
373
|
+
this.text = removeChar(this.text, originalSelectionEnd - (i + 1));
|
374
|
+
}
|
375
|
+
setTimeout(async () => {
|
376
|
+
this.#setSelectionRange(newPos, newPos);
|
377
|
+
this.textArea.focus();
|
378
|
+
await this.#addContent({
|
379
|
+
type: "inline",
|
380
|
+
value: `\n`,
|
381
|
+
});
|
382
|
+
}, 0);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
else {
|
386
|
+
const nextCharIsClosing = Object.values(this.keyPairs).includes(nextChar);
|
387
|
+
const highlighted = this.#selectionStart !== this.#selectionEnd;
|
388
|
+
if (e.ctrlKey || e.metaKey) {
|
389
|
+
if (this.#selectionStart === this.#selectionEnd) {
|
390
|
+
// no selection
|
391
|
+
if (e.key === "c" || e.key === "x") {
|
392
|
+
// copy or cut entire line
|
393
|
+
e.preventDefault();
|
394
|
+
const { lines, lineNumber, columnNumber } = this.#getLineInfo();
|
395
|
+
await navigator.clipboard.writeText(`${lineNumber === 0 && e.key === "x" ? "" : "\n"}${lines[lineNumber]}`);
|
396
|
+
if (e.key === "x") {
|
397
|
+
const newPos = this.#selectionStart - columnNumber;
|
398
|
+
lines.splice(lineNumber, 1);
|
399
|
+
this.text = lines.join("\n");
|
400
|
+
setTimeout(() => {
|
401
|
+
this.#setSelectionRange(newPos, newPos);
|
402
|
+
}, 0);
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
406
|
+
}
|
407
|
+
if ((e.ctrlKey || e.metaKey) && e.key) {
|
408
|
+
// keyboard shortcut
|
409
|
+
const matchedEl = this.#contentElements.find((el) => el.key === e.key);
|
410
|
+
if (matchedEl)
|
411
|
+
this.#addContent(matchedEl);
|
412
|
+
}
|
413
|
+
else if (nextCharIsClosing &&
|
414
|
+
(nextChar === e.key || e.key === "ArrowRight") &&
|
415
|
+
this.#openChars.length &&
|
416
|
+
!highlighted) {
|
417
|
+
// type over the next character instead of inserting
|
418
|
+
e.preventDefault();
|
419
|
+
this.#setSelectionRange(this.#selectionStart + 1, this.#selectionEnd + 1);
|
420
|
+
this.#openChars.pop();
|
421
|
+
}
|
422
|
+
else if (e.key in this.keyPairs) {
|
423
|
+
e.preventDefault();
|
424
|
+
await this.#addContent({
|
425
|
+
type: "wrap",
|
426
|
+
value: e.key,
|
427
|
+
});
|
428
|
+
this.#openChars.push(e.key);
|
429
|
+
}
|
430
|
+
}
|
431
|
+
});
|
432
|
+
// trims the selection if there is an extra space around it
|
433
|
+
this.textArea.addEventListener("dblclick", () => {
|
434
|
+
if (this.#selectionStart !== this.#selectionEnd) {
|
435
|
+
if (this.text[this.#selectionStart] === " ") {
|
436
|
+
this.#setSelectionRange(this.#selectionStart + 1, this.#selectionEnd);
|
437
|
+
}
|
438
|
+
if (this.text[this.#selectionEnd - 1] === " ") {
|
439
|
+
this.#setSelectionRange(this.#selectionStart, this.#selectionEnd - 1);
|
440
|
+
}
|
441
|
+
}
|
442
|
+
});
|
443
|
+
// reset #openChars on click since the cursor has changed position
|
444
|
+
this.textArea.addEventListener("click", () => (this.#openChars = []));
|
445
|
+
for (const trigger of this.getTrigger()) {
|
446
|
+
trigger.addEventListener(this.event, () => {
|
447
|
+
this.#addContent(this.#getContentElement(trigger));
|
448
|
+
});
|
449
|
+
}
|
450
|
+
}
|
451
|
+
}
|
452
|
+
/**
|
453
|
+
* @param str
|
454
|
+
* @returns the number, if the string starts with a number and a period
|
455
|
+
*/
|
456
|
+
const startsWithNumberAndPeriod = (str) => {
|
457
|
+
const result = str.match(/^(\d+)\./);
|
458
|
+
return result ? Number(result[1]) : null;
|
459
|
+
};
|
460
|
+
/**
|
461
|
+
* - insert character into string at index
|
462
|
+
*
|
463
|
+
* @param str string to insert into
|
464
|
+
* @param char characters to insert into `str`
|
465
|
+
* @param index where to insert the characters
|
466
|
+
* @returns the new string
|
467
|
+
*/
|
468
|
+
const insertChar = (str, char, index) => {
|
469
|
+
return str.slice(0, index) + char + str.slice(index);
|
470
|
+
};
|
471
|
+
/**
|
472
|
+
* - remove char from string at index
|
473
|
+
*
|
474
|
+
* @param str string to remove the character from
|
475
|
+
* @param index index of character to remove
|
476
|
+
* @returns the new string
|
477
|
+
*/
|
478
|
+
const removeChar = (str, index) => {
|
479
|
+
return str.slice(0, index) + str.slice(index + 1);
|
480
|
+
};
|
package/fullscreen/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var s=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(e){this.setAttribute("event",e)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(e=HTMLElement){let t=this.querySelector(this.getAttribute("content")??"[data-content]");if(t instanceof e)return t;throw new Error("Content not found")}swapContent(e=!0,t=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let r=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),e&&setTimeout(()=>this.getContent().replaceChildren(...r),t)}}safeListener(e,t,n=document.body,r={}){r.signal=this.#e.signal,n.addEventListener(e,t,r)}triggerListener(e,t=this.event){for(let n of this.getTrigger())n.addEventListener(t,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var o=class extends s{constructor(){super()}isFullscreen(){return document.fullscreenElement!==null}fullscreenSupported(){return!!document.documentElement.requestFullscreen}toggle(){if(this.isFullscreen())document.exitFullscreen();else try{this.getContent(HTMLElement).requestFullscreen()}catch{document.documentElement.requestFullscreen()}}mount(){this.triggerListener(()=>this.toggle());for(let e of this.getTrigger())!this.fullscreenSupported()&&
|
1
|
+
"use strict";(()=>{var s=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(e){this.setAttribute("event",e)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(e=HTMLElement){let t=this.querySelector(this.getAttribute("content")??"[data-content]");if(t instanceof e)return t;throw new Error("Content not found")}swapContent(e=!0,t=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let r=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),e&&setTimeout(()=>this.getContent().replaceChildren(...r),t)}}safeListener(e,t,n=document.body,r={}){r.signal=this.#e.signal,n.addEventListener(e,t,r)}triggerListener(e,t=this.event){for(let n of this.getTrigger())n.addEventListener(t,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var o=class extends s{constructor(){super()}isFullscreen(){return document.fullscreenElement!==null}fullscreenSupported(){return!!document.documentElement.requestFullscreen}toggle(){if(this.isFullscreen())document.exitFullscreen();else try{this.getContent(HTMLElement).requestFullscreen()}catch{document.documentElement.requestFullscreen()}}mount(){this.triggerListener(()=>this.toggle());for(let e of this.getTrigger())!this.fullscreenSupported()&&e instanceof HTMLButtonElement&&(e.disabled=!0)}};customElements.define("drab-fullscreen",o);})();
|
package/fullscreen/define.js
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import { Fullscreen } from "./index.js";
|
2
|
+
customElements.define("drab-fullscreen", Fullscreen);
|
package/fullscreen/index.d.ts
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
import { Base } from
|
2
|
-
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
import type { Attributes } from "../types/index.js";
|
3
|
+
export type FullscreenAttributes = Attributes<Fullscreen>;
|
3
4
|
/**
|
4
5
|
* Toggles the `documentElement` or `content` element to fullscreen mode.
|
5
6
|
*
|
6
7
|
* Disables the `trigger` if fullscreen is not supported.
|
7
8
|
*/
|
8
|
-
declare class Fullscreen extends Base {
|
9
|
+
export declare class Fullscreen extends Base {
|
9
10
|
constructor();
|
10
11
|
/**
|
11
12
|
* @returns `true` if fullscreen is currently enabled.
|
@@ -19,5 +20,3 @@ declare class Fullscreen extends Base {
|
|
19
20
|
toggle(): void;
|
20
21
|
mount(): void;
|
21
22
|
}
|
22
|
-
|
23
|
-
export { Fullscreen };
|
package/fullscreen/index.iife.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var s=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(e){this.setAttribute("event",e)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(e=HTMLElement){let t=this.querySelector(this.getAttribute("content")??"[data-content]");if(t instanceof e)return t;throw new Error("Content not found")}swapContent(e=!0,t=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let r=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),e&&setTimeout(()=>this.getContent().replaceChildren(...r),t)}}safeListener(e,t,n=document.body,r={}){r.signal=this.#e.signal,n.addEventListener(e,t,r)}triggerListener(e,t=this.event){for(let n of this.getTrigger())n.addEventListener(t,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var
|
1
|
+
"use strict";(()=>{var s=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(e){this.setAttribute("event",e)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(e=HTMLElement){let t=this.querySelector(this.getAttribute("content")??"[data-content]");if(t instanceof e)return t;throw new Error("Content not found")}swapContent(e=!0,t=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let r=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),e&&setTimeout(()=>this.getContent().replaceChildren(...r),t)}}safeListener(e,t,n=document.body,r={}){r.signal=this.#e.signal,n.addEventListener(e,t,r)}triggerListener(e,t=this.event){for(let n of this.getTrigger())n.addEventListener(t,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var o=class extends s{constructor(){super()}isFullscreen(){return document.fullscreenElement!==null}fullscreenSupported(){return!!document.documentElement.requestFullscreen}toggle(){if(this.isFullscreen())document.exitFullscreen();else try{this.getContent(HTMLElement).requestFullscreen()}catch{document.documentElement.requestFullscreen()}}mount(){this.triggerListener(()=>this.toggle());for(let e of this.getTrigger())!this.fullscreenSupported()&&e instanceof HTMLButtonElement&&(e.disabled=!0)}};})();
|
package/fullscreen/index.js
CHANGED
@@ -1 +1,45 @@
|
|
1
|
-
import{
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
/**
|
3
|
+
* Toggles the `documentElement` or `content` element to fullscreen mode.
|
4
|
+
*
|
5
|
+
* Disables the `trigger` if fullscreen is not supported.
|
6
|
+
*/
|
7
|
+
export class Fullscreen extends Base {
|
8
|
+
constructor() {
|
9
|
+
super();
|
10
|
+
}
|
11
|
+
/**
|
12
|
+
* @returns `true` if fullscreen is currently enabled.
|
13
|
+
*/
|
14
|
+
isFullscreen() {
|
15
|
+
return document.fullscreenElement !== null;
|
16
|
+
}
|
17
|
+
/**
|
18
|
+
* @returns `true` if fullscreen is supported.
|
19
|
+
*/
|
20
|
+
fullscreenSupported() {
|
21
|
+
return Boolean(document.documentElement.requestFullscreen);
|
22
|
+
}
|
23
|
+
/** Enables or disables fullscreen mode based on the current state. */
|
24
|
+
toggle() {
|
25
|
+
if (this.isFullscreen()) {
|
26
|
+
document.exitFullscreen();
|
27
|
+
}
|
28
|
+
else {
|
29
|
+
try {
|
30
|
+
this.getContent(HTMLElement).requestFullscreen();
|
31
|
+
}
|
32
|
+
catch {
|
33
|
+
document.documentElement.requestFullscreen();
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
mount() {
|
38
|
+
this.triggerListener(() => this.toggle());
|
39
|
+
for (const trigger of this.getTrigger()) {
|
40
|
+
if (!this.fullscreenSupported() && trigger instanceof HTMLButtonElement) {
|
41
|
+
trigger.disabled = true;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
package/index.d.ts
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
import { Base } from "./base/index.js";
|
2
|
+
import { Animate, type AnimateAttributes } from "./animate/index.js";
|
3
|
+
import { Breakpoint, type BreakpointAttributes } from "./breakpoint/index.js";
|
4
|
+
import { ContextMenu, type ContextMenuAttributes } from "./contextmenu/index.js";
|
5
|
+
import { Copy, type CopyAttributes } from "./copy/index.js";
|
6
|
+
import { Details, type DetailsAttributes } from "./details/index.js";
|
7
|
+
import { Dialog, type DialogAttributes } from "./dialog/index.js";
|
8
|
+
import { Editor, type EditorAttributes } from "./editor/index.js";
|
9
|
+
import { Fullscreen, type FullscreenAttributes } from "./fullscreen/index.js";
|
10
|
+
import { Popover, type PopoverAttributes } from "./popover/index.js";
|
11
|
+
import { Share, type ShareAttributes } from "./share/index.js";
|
12
|
+
import { TableSort, type TableSortAttributes } from "./tablesort/index.js";
|
13
|
+
import { YouTube, type YouTubeAttributes } from "./youtube/index.js";
|
14
|
+
export { Base, Animate, AnimateAttributes, Breakpoint, BreakpointAttributes, ContextMenu, ContextMenuAttributes, Copy, CopyAttributes, Details, DetailsAttributes, Dialog, DialogAttributes, Editor, EditorAttributes, Fullscreen, FullscreenAttributes, Popover, PopoverAttributes, Share, ShareAttributes, TableSort, TableSortAttributes, YouTube, YouTubeAttributes, };
|