drab 5.0.1 → 5.1.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.
Files changed (112) hide show
  1. package/animate/define.d.ts +1 -2
  2. package/animate/define.iife.js +1 -1
  3. package/animate/define.js +2 -1
  4. package/animate/index.d.ts +4 -6
  5. package/animate/index.iife.js +1 -1
  6. package/animate/index.js +138 -1
  7. package/{index-0rvQCq2U.d.ts → base/copy/index.d.ts} +2 -5
  8. package/base/copy/index.js +25 -0
  9. package/base/define.d.ts +1 -2
  10. package/base/define.iife.js +1 -1
  11. package/base/define.js +2 -1
  12. package/base/index.d.ts +8 -6
  13. package/base/index.iife.js +1 -1
  14. package/base/index.js +131 -1
  15. package/breakpoint/define.d.ts +1 -2
  16. package/breakpoint/define.iife.js +1 -1
  17. package/breakpoint/define.js +2 -1
  18. package/breakpoint/index.d.ts +5 -7
  19. package/breakpoint/index.iife.js +1 -1
  20. package/breakpoint/index.js +55 -1
  21. package/contextmenu/define.d.ts +1 -2
  22. package/contextmenu/define.iife.js +1 -1
  23. package/contextmenu/define.js +2 -1
  24. package/contextmenu/index.d.ts +4 -8
  25. package/contextmenu/index.iife.js +1 -1
  26. package/contextmenu/index.js +71 -1
  27. package/copy/define.d.ts +1 -2
  28. package/copy/define.iife.js +1 -1
  29. package/copy/define.js +2 -1
  30. package/copy/index.d.ts +4 -8
  31. package/copy/index.iife.js +1 -1
  32. package/copy/index.js +13 -1
  33. package/define/index.iife.js +9 -9
  34. package/define.d.ts +1 -0
  35. package/define.js +4 -0
  36. package/details/define.d.ts +1 -2
  37. package/details/define.iife.js +1 -1
  38. package/details/define.js +2 -1
  39. package/details/index.d.ts +4 -8
  40. package/details/index.iife.js +1 -1
  41. package/details/index.js +46 -1
  42. package/dialog/define.d.ts +1 -2
  43. package/dialog/define.iife.js +1 -1
  44. package/dialog/define.js +2 -1
  45. package/dialog/index.d.ts +4 -8
  46. package/dialog/index.iife.js +1 -1
  47. package/dialog/index.js +44 -1
  48. package/editor/define.d.ts +1 -2
  49. package/editor/define.iife.js +4 -4
  50. package/editor/define.js +2 -1
  51. package/editor/index.d.ts +5 -8
  52. package/editor/index.iife.js +4 -4
  53. package/editor/index.js +480 -1
  54. package/fullscreen/define.d.ts +1 -2
  55. package/fullscreen/define.iife.js +1 -1
  56. package/fullscreen/define.js +2 -1
  57. package/fullscreen/index.d.ts +4 -7
  58. package/fullscreen/index.iife.js +1 -1
  59. package/fullscreen/index.js +46 -1
  60. package/index.d.ts +15 -15
  61. package/index.iife.js +9 -9
  62. package/index.js +15 -1
  63. package/package.json +14 -4
  64. package/popover/define.d.ts +1 -2
  65. package/popover/define.iife.js +1 -1
  66. package/popover/define.js +2 -1
  67. package/popover/index.d.ts +4 -8
  68. package/popover/index.iife.js +1 -1
  69. package/popover/index.js +74 -1
  70. package/share/define.d.ts +1 -2
  71. package/share/define.iife.js +1 -1
  72. package/share/define.js +2 -1
  73. package/share/index.d.ts +4 -8
  74. package/share/index.iife.js +1 -1
  75. package/share/index.js +34 -1
  76. package/tablesort/define.d.ts +1 -2
  77. package/tablesort/define.iife.js +1 -1
  78. package/tablesort/define.js +2 -1
  79. package/tablesort/index.d.ts +4 -7
  80. package/tablesort/index.iife.js +1 -1
  81. package/tablesort/index.js +109 -1
  82. package/types/index.d.ts +11 -0
  83. package/types/index.js +1 -0
  84. package/wakelock/define.d.ts +1 -0
  85. package/wakelock/define.iife.js +1 -0
  86. package/wakelock/define.js +2 -0
  87. package/wakelock/index.d.ts +32 -0
  88. package/wakelock/index.iife.js +1 -0
  89. package/wakelock/index.js +88 -0
  90. package/youtube/define.d.ts +1 -2
  91. package/youtube/define.iife.js +1 -1
  92. package/youtube/define.js +2 -1
  93. package/youtube/index.d.ts +4 -7
  94. package/youtube/index.iife.js +1 -1
  95. package/youtube/index.js +56 -1
  96. package/chunk-3ASUSP4D.js +0 -1
  97. package/chunk-4WBITBA6.js +0 -1
  98. package/chunk-5JV4T7GM.js +0 -1
  99. package/chunk-7F7CQUEG.js +0 -1
  100. package/chunk-AWUCZCAJ.js +0 -1
  101. package/chunk-BCS5E3IH.js +0 -1
  102. package/chunk-DKKPV7QP.js +0 -1
  103. package/chunk-F5BEEHGA.js +0 -1
  104. package/chunk-GPET75FT.js +0 -9
  105. package/chunk-H4RNP4O4.js +0 -1
  106. package/chunk-LHXOPRVC.js +0 -1
  107. package/chunk-MXKU7AKV.js +0 -1
  108. package/chunk-VEVFQB5N.js +0 -1
  109. package/chunk-XAP2U34A.js +0 -1
  110. package/define/index.d.ts +0 -2
  111. package/define/index.js +0 -1
  112. package/index-J5TqSvEl.d.ts +0 -12
@@ -1,9 +1,9 @@
1
- "use strict";(()=>{var c=class extends HTMLElement{#n=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let s=this.querySelector(this.getAttribute("content")??"[data-content]");if(s instanceof t)return s;throw new Error("Content not found")}swapContent(t=!0,s=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let e=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),t&&setTimeout(()=>this.getContent().replaceChildren(...e),s)}}safeListener(t,s,n=document.body,e={}){e.signal=this.#n.signal,n.addEventListener(t,s,e)}triggerListener(t,s=this.event){for(let n of this.getTrigger())n.addEventListener(s,t)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#n.abort()}};var d=class extends c{#n=[];keyPairs={"(":")","{":"}","[":"]","<":">",'"':'"',"`":"`"};constructor(){super();for(let t of this.#o)t.type==="wrap"&&(this.keyPairs[t.value]=t.value)}get textArea(){return this.getContent(HTMLTextAreaElement)}get text(){return this.textArea.value}set text(t){this.textArea.value=t}get#o(){let t=[];for(let s of this.getTrigger())t.push(this.#l(s));return t}get#h(){let t=this.text.split("```"),s=0;for(let[n,e]of t.entries())if(s+=e.length+3,this.#e<s)return n;return 0}get#t(){return this.textArea.selectionEnd}get#e(){return this.textArea.selectionStart}#s(t,s){this.textArea.setSelectionRange(t,s)}#l(t){let s=t.dataset.type,n=t.dataset.value,e=t.dataset.key??void 0;return{type:s,value:n,key:e}}async#c(t,s,n){if(t.type==="inline")this.text=`${this.text.slice(0,n)}${t.value}${this.text.slice(n)}`;else if(t.type==="wrap")this.text=v(this.text,t.value,s),this.text=v(this.text,this.keyPairs[t.value],n+t.value.length),t.value.length<2&&this.#n.push(t.value);else if(t.type==="block"){let{lines:e,lineNumber:i}=this.#r(),r=t.value.at(0);r&&e[i]?.startsWith(r)?e[i]=t.value.trim()+e[i]:e[i]=t.value+e[i],this.text=e.join(`
2
- `)}}async#u(t,s,n){let e=0,i=0;if(/[a-z]/i.test(t)){for(let r=n;r<this.text.length;r++)if(this.text[r]?.match(/[a-z]/i))e?i=r+1:e=r;else if(e)break}else e=s+t.length,i=n+t.length;this.#s(e,i),this.textArea.focus()}async#i(t){let s=this.#t,n=this.#e;await this.#c(t,n,s),this.#u(t.value,n,s)}#g(t){if(t){let s=[];this.#o.forEach(e=>{e.type==="block"&&s.push(e.value)});for(let e=0;e<s.length;e++){let i=s[e];if(i&&t.startsWith(i))return i}let n=m(t);if(n)return`${n}. `}return""}#r(){let t=this.text.split(`
1
+ "use strict";(()=>{var c=class extends HTMLElement{#n=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let s=this.querySelector(this.getAttribute("content")??"[data-content]");if(s instanceof t)return s;throw new Error("Content not found")}swapContent(t=!0,s=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let e=Array.from(this.getContent().childNodes),i=[];n instanceof HTMLTemplateElement?(i.push(n.content.cloneNode(!0)),n.content.replaceChildren(...e)):(i.push(...n.childNodes),n.replaceChildren(...e)),this.getContent().replaceChildren(...i),t&&setTimeout(()=>this.swapContent(!1),s)}}safeListener(t,s,n=document.body,e={}){e.signal=this.#n.signal,n.addEventListener(t,s,e)}triggerListener(t,s=this.event){for(let n of this.getTrigger())n.addEventListener(s,t)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}destroy(){}disconnectedCallback(){this.destroy(),this.#n.abort()}};var d=class extends c{#n=[];keyPairs={"(":")","{":"}","[":"]","<":">",'"':'"',"`":"`"};constructor(){super();for(let t of this.#o)t.type==="wrap"&&(this.keyPairs[t.value]=t.value)}get textArea(){return this.getContent(HTMLTextAreaElement)}get text(){return this.textArea.value}set text(t){this.textArea.value=t}get#o(){let t=[];for(let s of this.getTrigger())t.push(this.#l(s));return t}get#h(){let t=this.text.split("```"),s=0;for(let[n,e]of t.entries())if(s+=e.length+3,this.#e<s)return n;return 0}get#t(){return this.textArea.selectionEnd}get#e(){return this.textArea.selectionStart}#s(t,s){this.textArea.setSelectionRange(t,s)}#l(t){let s=t.dataset.type,n=t.dataset.value,e=t.dataset.key??void 0;return{type:s,value:n,key:e}}async#c(t,s,n){if(t.type==="inline")this.text=`${this.text.slice(0,n)}${t.value}${this.text.slice(n)}`;else if(t.type==="wrap")this.text=v(this.text,t.value,s),this.text=v(this.text,this.keyPairs[t.value],n+t.value.length),t.value.length<2&&this.#n.push(t.value);else if(t.type==="block"){let{lines:e,lineNumber:i}=this.#r(),r=t.value.at(0);r&&e[i]?.startsWith(r)?e[i]=t.value.trim()+e[i]:e[i]=t.value+e[i],this.text=e.join(`
2
+ `)}}async#u(t,s,n){let e=0,i=0;if(/[a-z]/i.test(t)){for(let r=n;r<this.text.length;r++)if(this.text[r]?.match(/[a-z]/i))e?i=r+1:e=r;else if(e)break}else e=s+t.length,i=n+t.length;this.#s(e,i),this.textArea.focus()}async#i(t){let s=this.#t,n=this.#e;await this.#c(t,n,s),this.#u(t.value,n,s)}#f(t){if(t){let s=[];this.#o.forEach(e=>{e.type==="block"&&s.push(e.value)});for(let e=0;e<s.length;e++){let i=s[e];if(i&&t.startsWith(i))return i}let n=m(t);if(n)return`${n}. `}return""}#r(){let t=this.text.split(`
3
3
  `),s=0;for(let n=0;n<t.length;n++){let e=t.at(n)?.length??0;if(s++,s+=e,s>this.#t)return{lines:t,lineNumber:n,columnNumber:this.#t-(s-e-1)}}return{lines:t,lineNumber:0,columnNumber:0}}#a(t,s=!1){let{lines:n}=this.#r();for(let e=t+1;e<n.length;e++){let i=n[e];if(i){let r=m(i);if(r){let o;if(s)if(r>1)o=r-1;else break;else o=r+1;n[e]=i.slice(String(r).length),n[e]=String(o)+n[e]}else break}}this.text=n.join(`
4
4
  `)}mount(){this.textArea.addEventListener("keydown",async t=>{let s=["ArrowUp","ArrowDown","Delete"],n=this.text[this.#t]??"";if(s.includes(t.key))this.#n=[];else if(t.key==="Backspace"){let e=this.text[this.#e-1];if(e&&e in this.keyPairs&&n===this.keyPairs[e]){t.preventDefault();let i=this.#e-1,r=this.#t-1;this.text=u(this.text,i),this.text=u(this.text,r),setTimeout(()=>{this.#s(i,r)},0),this.#n.pop()}if(e===`
5
- `&&this.#e===this.#t){t.preventDefault();let i=this.#e-1,{lineNumber:r}=this.#r();this.#a(r,!0),this.text=u(this.text,i),setTimeout(async()=>{this.#s(i,i)},0)}}else if(t.key==="Tab")this.#h%2!==0&&(t.preventDefault(),await this.#i({type:"inline",value:" "}));else if(t.key==="Enter"){let{lines:e,lineNumber:i,columnNumber:r}=this.#r(),o=e.at(i),a=this.#g(o),h=a,g=m(a);if(g&&(a=`${g+1}. `),a&&h.length<r)t.preventDefault(),g&&this.#a(i),await this.#i({type:"inline",value:`
6
- ${a}`});else if(a&&h.length===r){t.preventDefault();let p=this.#t,y=p-h.length;for(let f=0;f<h.length;f++)this.text=u(this.text,p-(f+1));setTimeout(async()=>{this.#s(y,y),this.textArea.focus(),await this.#i({type:"inline",value:`
5
+ `&&this.#e===this.#t){t.preventDefault();let i=this.#e-1,{lineNumber:r}=this.#r();this.#a(r,!0),this.text=u(this.text,i),setTimeout(async()=>{this.#s(i,i)},0)}}else if(t.key==="Tab")this.#h%2!==0&&(t.preventDefault(),await this.#i({type:"inline",value:" "}));else if(t.key==="Enter"){let{lines:e,lineNumber:i,columnNumber:r}=this.#r(),o=e.at(i),a=this.#f(o),h=a,f=m(a);if(f&&(a=`${f+1}. `),a&&h.length<r)t.preventDefault(),f&&this.#a(i),await this.#i({type:"inline",value:`
6
+ ${a}`});else if(a&&h.length===r){t.preventDefault();let p=this.#t,y=p-h.length;for(let g=0;g<h.length;g++)this.text=u(this.text,p-(g+1));setTimeout(async()=>{this.#s(y,y),this.textArea.focus(),await this.#i({type:"inline",value:`
7
7
  `})},0)}}else{let e=Object.values(this.keyPairs).includes(n),i=this.#e!==this.#t;if((t.ctrlKey||t.metaKey)&&this.#e===this.#t&&(t.key==="c"||t.key==="x")){t.preventDefault();let{lines:r,lineNumber:o,columnNumber:a}=this.#r();if(await navigator.clipboard.writeText(`${o===0&&t.key==="x"?"":`
8
8
  `}${r[o]}`),t.key==="x"){let h=this.#e-a;r.splice(o,1),this.text=r.join(`
9
9
  `),setTimeout(()=>{this.#s(h,h)},0)}}if((t.ctrlKey||t.metaKey)&&t.key){let r=this.#o.find(o=>o.key===t.key);r&&this.#i(r)}else e&&(n===t.key||t.key==="ArrowRight")&&this.#n.length&&!i?(t.preventDefault(),this.#s(this.#e+1,this.#t+1),this.#n.pop()):t.key in this.keyPairs&&(t.preventDefault(),await this.#i({type:"wrap",value:t.key}),this.#n.push(t.key))}}),this.textArea.addEventListener("dblclick",()=>{this.#e!==this.#t&&(this.text[this.#e]===" "&&this.#s(this.#e+1,this.#t),this.text[this.#t-1]===" "&&this.#s(this.#e,this.#t-1))}),this.textArea.addEventListener("click",()=>this.#n=[]);for(let t of this.getTrigger())t.addEventListener(this.event,()=>{this.#i(this.#l(t))})}},m=l=>{let t=l.match(/^(\d+)\./);return t?Number(t[1]):null},v=(l,t,s)=>l.slice(0,s)+t+l.slice(s),u=(l,t)=>l.slice(0,t)+l.slice(t+1);})();
package/editor/index.js CHANGED
@@ -1 +1,480 @@
1
- import{a}from"../chunk-GPET75FT.js";import"../chunk-MXKU7AKV.js";export{a as Editor};
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
+ };
@@ -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 i=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()&&"disabled"in e&&(e.disabled=!0)}};customElements.define("drab-fullscreen",i);})();
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 n=this.querySelector(this.getAttribute("content")??"[data-content]");if(n instanceof e)return n;throw new Error("Content not found")}swapContent(e=!0,n=800){let t=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(t){let r=Array.from(this.getContent().childNodes),i=[];t instanceof HTMLTemplateElement?(i.push(t.content.cloneNode(!0)),t.content.replaceChildren(...r)):(i.push(...t.childNodes),t.replaceChildren(...r)),this.getContent().replaceChildren(...i),e&&setTimeout(()=>this.swapContent(!1),n)}}safeListener(e,n,t=document.body,r={}){r.signal=this.#e.signal,t.addEventListener(e,n,r)}triggerListener(e,n=this.event){for(let t of this.getTrigger())t.addEventListener(n,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}destroy(){}disconnectedCallback(){this.destroy(),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()&&"disabled"in e&&(e.disabled=!0)}};customElements.define("drab-fullscreen",o);})();
@@ -1 +1,2 @@
1
- import{a as e}from"../chunk-5JV4T7GM.js";import"../chunk-MXKU7AKV.js";customElements.define("drab-fullscreen",e);
1
+ import { Fullscreen } from "./index.js";
2
+ customElements.define("drab-fullscreen", Fullscreen);
@@ -1,13 +1,12 @@
1
- import { Base } from '../base/index.js';
2
- import { A as Attributes } from '../index-J5TqSvEl.js';
3
-
4
- type FullscreenAttributes = Attributes<Fullscreen>;
1
+ import { Base } from "../base/index.js";
2
+ import type { Attributes } from "../types/index.js";
3
+ export type FullscreenAttributes = Attributes<Fullscreen>;
5
4
  /**
6
5
  * Toggles the `documentElement` or `content` element to fullscreen mode.
7
6
  *
8
7
  * Disables the `trigger` if fullscreen is not supported.
9
8
  */
10
- declare class Fullscreen extends Base {
9
+ export declare class Fullscreen extends Base {
11
10
  constructor();
12
11
  /**
13
12
  * @returns `true` if fullscreen is currently enabled.
@@ -21,5 +20,3 @@ declare class Fullscreen extends Base {
21
20
  toggle(): void;
22
21
  mount(): void;
23
22
  }
24
-
25
- export { Fullscreen, type FullscreenAttributes };
@@ -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 i=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()&&"disabled"in e&&(e.disabled=!0)}};})();
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 n=this.querySelector(this.getAttribute("content")??"[data-content]");if(n instanceof e)return n;throw new Error("Content not found")}swapContent(e=!0,n=800){let t=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(t){let r=Array.from(this.getContent().childNodes),o=[];t instanceof HTMLTemplateElement?(o.push(t.content.cloneNode(!0)),t.content.replaceChildren(...r)):(o.push(...t.childNodes),t.replaceChildren(...r)),this.getContent().replaceChildren(...o),e&&setTimeout(()=>this.swapContent(!1),n)}}safeListener(e,n,t=document.body,r={}){r.signal=this.#e.signal,t.addEventListener(e,n,r)}triggerListener(e,n=this.event){for(let t of this.getTrigger())t.addEventListener(n,e)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}destroy(){}disconnectedCallback(){this.destroy(),this.#e.abort()}};var i=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()&&"disabled"in e&&(e.disabled=!0)}};})();
@@ -1 +1,46 @@
1
- import{a}from"../chunk-5JV4T7GM.js";import"../chunk-MXKU7AKV.js";export{a as Fullscreen};
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
+ // BREAKING TODO: make private like in WakeLock
22
+ return Boolean(document.documentElement.requestFullscreen);
23
+ }
24
+ /** Enables or disables fullscreen mode based on the current state. */
25
+ toggle() {
26
+ if (this.isFullscreen()) {
27
+ document.exitFullscreen();
28
+ }
29
+ else {
30
+ try {
31
+ this.getContent(HTMLElement).requestFullscreen();
32
+ }
33
+ catch {
34
+ document.documentElement.requestFullscreen();
35
+ }
36
+ }
37
+ }
38
+ mount() {
39
+ this.triggerListener(() => this.toggle());
40
+ for (const trigger of this.getTrigger()) {
41
+ if (!this.fullscreenSupported() && "disabled" in trigger) {
42
+ trigger.disabled = true;
43
+ }
44
+ }
45
+ }
46
+ }
package/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- export { Base } from './base/index.js';
2
- export { Animate, AnimateAttributes } from './animate/index.js';
3
- export { Breakpoint, BreakpointAttributes } from './breakpoint/index.js';
4
- export { ContextMenu, ContextMenuAttributes } from './contextmenu/index.js';
5
- export { Copy, CopyAttributes } from './copy/index.js';
6
- export { Details, DetailsAttributes } from './details/index.js';
7
- export { Dialog, DialogAttributes } from './dialog/index.js';
8
- export { Editor, EditorAttributes } from './editor/index.js';
9
- export { Fullscreen, FullscreenAttributes } from './fullscreen/index.js';
10
- export { Popover, PopoverAttributes } from './popover/index.js';
11
- export { Share, ShareAttributes } from './share/index.js';
12
- export { TableSort, TableSortAttributes } from './tablesort/index.js';
13
- export { YouTube, YouTubeAttributes } from './youtube/index.js';
14
- import './index-J5TqSvEl.js';
15
- import './index-0rvQCq2U.js';
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 { WakeLock, type WakeLockAttributes } from "./wakelock/index.js";
14
+ import { YouTube, type YouTubeAttributes } from "./youtube/index.js";
15
+ export { Base, Animate, AnimateAttributes, Breakpoint, BreakpointAttributes, ContextMenu, ContextMenuAttributes, Copy, CopyAttributes, Details, DetailsAttributes, Dialog, DialogAttributes, Editor, EditorAttributes, Fullscreen, FullscreenAttributes, Popover, PopoverAttributes, Share, ShareAttributes, TableSort, TableSortAttributes, WakeLock, WakeLockAttributes, YouTube, YouTubeAttributes, };