@treeui/vue 0.15.0 → 0.17.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 (78) hide show
  1. package/dist/components/TAccordion.cjs.map +1 -1
  2. package/dist/components/TAccordion.js.map +1 -1
  3. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs +1 -1
  4. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  5. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
  6. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/components/TAccordionItem.cjs.map +1 -1
  8. package/dist/components/TAccordionItem.js.map +1 -1
  9. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs +1 -1
  10. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  11. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/TCard.cjs.map +1 -1
  14. package/dist/components/TCard.js.map +1 -1
  15. package/dist/components/TCard.vue_vue_type_script_setup_true_lang.cjs +1 -1
  16. package/dist/components/TCard.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  17. package/dist/components/TCard.vue_vue_type_script_setup_true_lang.js +2 -2
  18. package/dist/components/TCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/TDropdown.cjs.map +1 -1
  20. package/dist/components/TDropdown.js.map +1 -1
  21. package/dist/components/TDropdown.vue.d.ts +6 -2
  22. package/dist/components/TDropdown.vue.d.ts.map +1 -1
  23. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs +1 -1
  24. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  25. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js +31 -24
  26. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js.map +1 -1
  27. package/dist/components/TFlag.cjs +2 -0
  28. package/dist/components/TFlag.cjs.map +1 -0
  29. package/dist/components/TFlag.js +7 -0
  30. package/dist/components/TFlag.js.map +1 -0
  31. package/dist/components/TFlag.vue.d.ts +20 -0
  32. package/dist/components/TFlag.vue.d.ts.map +1 -0
  33. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs +2 -0
  34. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  35. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js +58 -0
  36. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js.map +1 -0
  37. package/dist/components/TIcon.vue.d.ts +1 -1
  38. package/dist/components/TLanguageSelect.cjs +2 -0
  39. package/dist/components/TLanguageSelect.cjs.map +1 -0
  40. package/dist/components/TLanguageSelect.js +7 -0
  41. package/dist/components/TLanguageSelect.js.map +1 -0
  42. package/dist/components/TLanguageSelect.vue.d.ts +103 -0
  43. package/dist/components/TLanguageSelect.vue.d.ts.map +1 -0
  44. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs +2 -0
  45. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  46. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js +299 -0
  47. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
  48. package/dist/components/TMarkdownEditor.cjs.map +1 -1
  49. package/dist/components/TMarkdownEditor.js.map +1 -1
  50. package/dist/components/TMarkdownEditor.vue.d.ts +2 -0
  51. package/dist/components/TMarkdownEditor.vue.d.ts.map +1 -1
  52. package/dist/components/TMarkdownEditor.vue_vue_type_script_setup_true_lang.cjs +9 -9
  53. package/dist/components/TMarkdownEditor.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  54. package/dist/components/TMarkdownEditor.vue_vue_type_script_setup_true_lang.js +117 -96
  55. package/dist/components/TMarkdownEditor.vue_vue_type_script_setup_true_lang.js.map +1 -1
  56. package/dist/components/TPopover.cjs.map +1 -1
  57. package/dist/components/TPopover.js.map +1 -1
  58. package/dist/components/TPopover.vue.d.ts +4 -2
  59. package/dist/components/TPopover.vue.d.ts.map +1 -1
  60. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs +1 -1
  61. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  62. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js +10 -5
  63. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/accordion.cjs.map +1 -1
  65. package/dist/components/accordion.d.ts +4 -4
  66. package/dist/components/accordion.d.ts.map +1 -1
  67. package/dist/components/accordion.js.map +1 -1
  68. package/dist/components/index.d.ts +13 -1
  69. package/dist/components/index.d.ts.map +1 -1
  70. package/dist/index.cjs +1 -1
  71. package/dist/index.js +80 -78
  72. package/dist/plugin.cjs +1 -1
  73. package/dist/plugin.cjs.map +1 -1
  74. package/dist/plugin.d.ts.map +1 -1
  75. package/dist/plugin.js +146 -142
  76. package/dist/plugin.js.map +1 -1
  77. package/dist/style.css +1 -1
  78. package/package.json +2 -2
@@ -4,20 +4,20 @@ var d = {
4
4
  class: "t-md-editor__toolbar",
5
5
  role: "toolbar",
6
6
  "aria-label": "Markdown formatting"
7
- }, f = { class: "t-md-editor__toolbar-group" }, p = ["disabled"], ee = ["disabled"], m = ["disabled"], te = { class: "t-md-editor__toolbar-group" }, ne = ["disabled"], h = ["disabled"], g = ["disabled"], _ = { class: "t-md-editor__toolbar-group" }, v = ["disabled"], y = ["disabled"], b = ["disabled"], x = { class: "t-md-editor__toolbar-group" }, S = ["disabled"], C = ["disabled"], w = ["disabled"], re = ["disabled"], T = { class: "t-md-editor__body" }, E = { class: "t-md-editor__pane t-md-editor__pane--write" }, D = [
7
+ }, f = { class: "t-md-editor__toolbar-group" }, p = ["disabled"], m = ["disabled"], h = ["disabled"], g = { class: "t-md-editor__toolbar-group" }, _ = ["disabled"], v = ["disabled"], ee = ["disabled"], te = { class: "t-md-editor__toolbar-group" }, y = ["disabled"], b = ["disabled"], x = ["disabled"], S = { class: "t-md-editor__toolbar-group" }, C = ["disabled"], w = ["disabled"], T = ["disabled"], E = ["disabled"], ne = { class: "t-md-editor__body" }, re = { class: "t-md-editor__pane t-md-editor__pane--write" }, ie = [
8
8
  "value",
9
9
  "placeholder",
10
10
  "disabled",
11
11
  "aria-label"
12
- ], O = {
12
+ ], ae = {
13
13
  class: "t-md-editor__pane t-md-editor__pane--preview",
14
14
  "aria-label": "Markdown preview"
15
- }, k = ["innerHTML"], A = {
15
+ }, D = ["innerHTML"], O = {
16
16
  key: 0,
17
17
  class: "t-md-editor__upload-bar",
18
18
  role: "status",
19
19
  "aria-live": "polite"
20
- }, j = /*@__PURE__*/ i({
20
+ }, k = "\0", A = /*@__PURE__*/ i({
21
21
  inheritAttrs: !1,
22
22
  __name: "TMarkdownEditor",
23
23
  props: {
@@ -31,6 +31,10 @@ var d = {
31
31
  uploadImage: {
32
32
  type: Function,
33
33
  default: void 0
34
+ },
35
+ sanitize: {
36
+ type: Function,
37
+ default: void 0
34
38
  }
35
39
  },
36
40
  emits: [
@@ -38,41 +42,44 @@ var d = {
38
42
  "image-upload",
39
43
  "image-upload-error"
40
44
  ],
41
- setup(i, { emit: j }) {
42
- let M = i, N = j, P = u(), F = c(null), I = c(!1), L = e(() => [
45
+ setup(i, { emit: A }) {
46
+ let j = i, M = A, N = u(), P = c(null), F = c(!1), I = e(() => [
43
47
  "t-md-editor",
44
- `t-md-editor--${M.size}`,
48
+ `t-md-editor--${j.size}`,
45
49
  {
46
- "is-disabled": M.disabled,
47
- "is-uploading": I.value
50
+ "is-disabled": j.disabled,
51
+ "is-uploading": F.value
48
52
  },
49
- P.class
50
- ]), R = e(() => P.style), z = e(() => {
51
- let { class: e, style: t, ...n } = P;
53
+ N.class
54
+ ]), L = e(() => N.style), R = e(() => {
55
+ let { class: e, style: t, ...n } = N;
52
56
  return n;
53
- }), B = e(() => `${M.modelValue ?? ""}`), V = (e) => {
54
- N("update:modelValue", e.target.value);
55
- }, H = (e, t = "") => {
56
- let n = F.value;
57
+ }), z = e(() => `${j.modelValue ?? ""}`), B = (e) => {
58
+ M("update:modelValue", e.target.value);
59
+ }, V = (e, t = "") => {
60
+ let n = P.value;
57
61
  if (!n) return;
58
- let r = n.selectionStart, i = n.selectionEnd, a = B.value, s = a.slice(r, i), c = a.slice(0, r) + e + s + t + a.slice(i);
59
- N("update:modelValue", c), o(() => {
62
+ let r = n.selectionStart, i = n.selectionEnd, a = z.value, s = a.slice(r, i), c = a.slice(0, r) + e + s + t + a.slice(i);
63
+ M("update:modelValue", c), o(() => {
60
64
  n.focus();
61
65
  let i = s ? r + e.length + s.length + t.length : r + e.length;
62
66
  n.setSelectionRange(i, i);
63
67
  });
64
- }, U = (e) => {
65
- let t = "#".repeat(e) + " ", n = F.value;
68
+ }, H = (e) => {
69
+ let t = "#".repeat(e) + " ", n = P.value;
66
70
  if (!n) return;
67
- let r = n.selectionStart, i = B.value, a = i.lastIndexOf("\n", r - 1) + 1, s = i.indexOf("\n", r), c = s === -1 ? i.length : s, l = t + i.slice(a, c).replace(/^#{1,6}\s*/, ""), u = i.slice(0, a) + l + i.slice(c);
68
- N("update:modelValue", u), o(() => {
71
+ let r = n.selectionStart, i = z.value, a = i.lastIndexOf("\n", r - 1) + 1, s = i.indexOf("\n", r), c = s === -1 ? i.length : s, l = t + i.slice(a, c).replace(/^#{1,6}\s*/, ""), u = i.slice(0, a) + l + i.slice(c);
72
+ M("update:modelValue", u), o(() => {
69
73
  n.focus(), n.setSelectionRange(a + l.length, a + l.length);
70
74
  });
71
- }, W = () => H("**", "**"), G = () => H("_", "_"), K = () => H("`", "`"), q = () => H("\n```\n", "\n```\n"), J = () => H("[", "](url)"), Y = () => H("\n- "), X = () => H("\n1. "), ie = () => H("\n> "), ae = () => H("\n---\n"), oe = e(() => se(B.value));
72
- function Z(e) {
75
+ }, U = () => V("**", "**"), W = () => V("_", "_"), G = () => V("`", "`"), oe = () => V("\n```\n", "\n```\n"), K = () => V("[", "](url)"), q = () => V("\n- "), se = () => V("\n1. "), ce = () => V("\n> "), le = () => V("\n---\n"), ue = e(() => {
76
+ let e = de(z.value);
77
+ return j.sanitize ? j.sanitize(e) : e;
78
+ });
79
+ function J(e) {
73
80
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
74
81
  }
75
- function se(e) {
82
+ function de(e) {
76
83
  if (!e) return "";
77
84
  let t = e.split("\n"), n = [], r = !1, i = !1, a = !1;
78
85
  for (let e = 0; e < t.length; e++) {
@@ -82,7 +89,7 @@ var d = {
82
89
  continue;
83
90
  }
84
91
  if (r) {
85
- n.push(Z(o)), n.push("\n");
92
+ n.push(J(o)), n.push("\n");
86
93
  continue;
87
94
  }
88
95
  let s = /^[-*+]\s/.test(o.trim()), c = /^\d+\.\s/.test(o.trim());
@@ -93,36 +100,50 @@ var d = {
93
100
  let l = o.match(/^(#{1,6})\s+(.+)$/);
94
101
  if (l) {
95
102
  let e = l[1].length;
96
- n.push(`<h${e} class="t-md-editor__heading t-md-editor__heading--${e}">${Q(Z(l[2]))}</h${e}>`);
103
+ n.push(`<h${e} class="t-md-editor__heading t-md-editor__heading--${e}">${Q(J(l[2]))}</h${e}>`);
97
104
  continue;
98
105
  }
99
106
  if (o.trim().startsWith("> ")) {
100
- n.push(`<blockquote class="t-md-editor__blockquote">${Q(Z(o.trim().slice(2)))}</blockquote>`);
107
+ n.push(`<blockquote class="t-md-editor__blockquote">${Q(J(o.trim().slice(2)))}</blockquote>`);
101
108
  continue;
102
109
  }
103
110
  if (s) {
104
- i ||= (n.push("<ul class=\"t-md-editor__list\">"), !0), n.push(`<li>${Q(Z(o.trim().slice(2)))}</li>`);
111
+ i ||= (n.push("<ul class=\"t-md-editor__list\">"), !0), n.push(`<li>${Q(J(o.trim().slice(2)))}</li>`);
105
112
  continue;
106
113
  }
107
114
  if (c) {
108
115
  a ||= (n.push("<ol class=\"t-md-editor__list t-md-editor__list--ordered\">"), !0);
109
116
  let e = o.trim().replace(/^\d+\.\s/, "");
110
- n.push(`<li>${Q(Z(e))}</li>`);
117
+ n.push(`<li>${Q(J(e))}</li>`);
111
118
  continue;
112
119
  }
113
120
  if (o.trim() === "") {
114
121
  n.push("<br />");
115
122
  continue;
116
123
  }
117
- n.push(`<p class="t-md-editor__paragraph">${Q(Z(o))}</p>`);
124
+ n.push(`<p class="t-md-editor__paragraph">${Q(J(o))}</p>`);
118
125
  }
119
126
  return r && n.push("</code></pre>"), i && n.push("</ul>"), a && n.push("</ol>"), n.join("\n");
120
127
  }
128
+ let Y = /^[a-z][a-z0-9+.-]*:/, fe = /^(https?|mailto|tel):/, pe = /^https?:/, me = /^data:image\/(png|jpe?g|gif|webp|avif|bmp|x-icon|vnd\.microsoft\.icon)[;,]/, X = (e) => Number.isFinite(e) && e >= 0 && e <= 1114111 ? String.fromCodePoint(e) : "";
129
+ function Z(e) {
130
+ return e.replace(/&quot;/g, "\"").replace(/&#039;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&#x([0-9a-f]+);?/gi, (e, t) => X(parseInt(t, 16))).replace(/&#(\d+);?/g, (e, t) => X(parseInt(t, 10))).replace(/[\x00-\x20\x7f]/g, "").toLowerCase();
131
+ }
132
+ function he(e) {
133
+ let t = Z(e);
134
+ return !Y.test(t) || fe.test(t);
135
+ }
136
+ function ge(e) {
137
+ let t = Z(e);
138
+ return !Y.test(t) || pe.test(t) || me.test(t);
139
+ }
140
+ let _e = /\x00(\d+)\x00/g;
121
141
  function Q(e) {
122
- return e = e.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, "<img src=\"$2\" alt=\"$1\" class=\"t-md-editor__image\" loading=\"lazy\" />"), e = e.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>"), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/_(.+?)_/g, "<em>$1</em>"), e = e.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>"), e = e.replace(/`(.+?)`/g, "<code class=\"t-md-editor__inline-code\">$1</code>"), e = e.replace(/~~(.+?)~~/g, "<del>$1</del>"), e;
142
+ let t = [], n = (e) => (t.push(e), `${k}${t.length - 1}${k}`), r = (e) => n(`<span class="t-md-editor__blocked-link">${e}</span>`);
143
+ return e = e.replace(/\x00/g, ""), e = e.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (e, t, i) => ge(i) ? n(`<img src="${i}" alt="${t}" class="t-md-editor__image" loading="lazy" />`) : r(e)), e = e.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (e, t, i) => he(i) ? `${n(`<a href="${i}" target="_blank" rel="noopener noreferrer">`)}${t}${n("</a>")}` : r(e)), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/_(.+?)_/g, "<em>$1</em>"), e = e.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>"), e = e.replace(/`(.+?)`/g, "<code class=\"t-md-editor__inline-code\">$1</code>"), e = e.replace(/~~(.+?)~~/g, "<del>$1</del>"), e.replace(_e, (e, n) => t[Number(n)]);
123
144
  }
124
- let ce = async (e) => {
125
- if (M.disabled) return;
145
+ let ve = async (e) => {
146
+ if (j.disabled) return;
126
147
  let t = e.clipboardData?.items;
127
148
  if (t) {
128
149
  for (let n of t) if (n.type.startsWith("image/")) {
@@ -132,47 +153,47 @@ var d = {
132
153
  return;
133
154
  }
134
155
  }
135
- }, le = async (e) => {
136
- if (M.disabled) return;
156
+ }, ye = async (e) => {
157
+ if (j.disabled) return;
137
158
  let t = e.dataTransfer?.files;
138
159
  if (t) for (let n of t) n.type.startsWith("image/") && (e.preventDefault(), await $(n));
139
- }, ue = (e) => {
140
- M.disabled || e.preventDefault();
160
+ }, be = (e) => {
161
+ j.disabled || e.preventDefault();
141
162
  }, $ = async (e) => {
142
- if (!M.uploadImage) {
143
- H(`![${e.name}]()`);
163
+ if (!j.uploadImage) {
164
+ V(`![${e.name}]()`);
144
165
  return;
145
166
  }
146
167
  let t = `![Uploading ${e.name}...]()`;
147
- H(t), I.value = !0;
168
+ V(t), F.value = !0;
148
169
  try {
149
- let n = M.uploadImage(e), r = n instanceof Promise ? await n : n, i = B.value.replace(t, `![${e.name}](${r})`);
150
- N("update:modelValue", i), N("image-upload", e, r);
170
+ let n = j.uploadImage(e), r = n instanceof Promise ? await n : n, i = z.value.replace(t, `![${e.name}](${r})`);
171
+ M("update:modelValue", i), M("image-upload", e, r);
151
172
  } catch (n) {
152
- let r = B.value.replace(t, "");
153
- N("update:modelValue", r), N("image-upload-error", e, n instanceof Error ? n : Error(String(n)));
173
+ let r = z.value.replace(t, "");
174
+ M("update:modelValue", r), M("image-upload-error", e, n instanceof Error ? n : Error(String(n)));
154
175
  } finally {
155
- I.value = !1;
176
+ F.value = !1;
156
177
  }
157
- }, de = (e) => {
158
- if (!M.disabled && (e.metaKey || e.ctrlKey)) switch (e.key) {
178
+ }, xe = (e) => {
179
+ if (!j.disabled && (e.metaKey || e.ctrlKey)) switch (e.key) {
159
180
  case "b":
160
- e.preventDefault(), W();
181
+ e.preventDefault(), U();
161
182
  break;
162
183
  case "i":
163
- e.preventDefault(), G();
184
+ e.preventDefault(), W();
164
185
  break;
165
186
  case "k":
166
- e.preventDefault(), J();
187
+ e.preventDefault(), K();
167
188
  break;
168
189
  case "e":
169
- e.preventDefault(), K();
190
+ e.preventDefault(), G();
170
191
  break;
171
192
  }
172
193
  };
173
- return (e, o) => (s(), n("div", a(z.value, {
174
- class: L.value,
175
- style: R.value
194
+ return (e, o) => (s(), n("div", a(R.value, {
195
+ class: I.value,
196
+ style: L.value
176
197
  }), [
177
198
  r("div", d, [
178
199
  r("div", f, [
@@ -181,135 +202,135 @@ var d = {
181
202
  class: "t-md-editor__toolbar-btn",
182
203
  title: "Heading 1 (H1)",
183
204
  disabled: i.disabled,
184
- onClick: o[0] ||= (e) => U(1)
205
+ onClick: o[0] ||= (e) => H(1)
185
206
  }, " H1 ", 8, p),
186
207
  r("button", {
187
208
  type: "button",
188
209
  class: "t-md-editor__toolbar-btn",
189
210
  title: "Heading 2 (H2)",
190
211
  disabled: i.disabled,
191
- onClick: o[1] ||= (e) => U(2)
192
- }, " H2 ", 8, ee),
212
+ onClick: o[1] ||= (e) => H(2)
213
+ }, " H2 ", 8, m),
193
214
  r("button", {
194
215
  type: "button",
195
216
  class: "t-md-editor__toolbar-btn",
196
217
  title: "Heading 3 (H3)",
197
218
  disabled: i.disabled,
198
- onClick: o[2] ||= (e) => U(3)
199
- }, " H3 ", 8, m)
219
+ onClick: o[2] ||= (e) => H(3)
220
+ }, " H3 ", 8, h)
200
221
  ]),
201
222
  o[7] ||= r("div", {
202
223
  class: "t-md-editor__toolbar-divider",
203
224
  role: "separator"
204
225
  }, null, -1),
205
- r("div", te, [
226
+ r("div", g, [
206
227
  r("button", {
207
228
  type: "button",
208
229
  class: "t-md-editor__toolbar-btn",
209
230
  title: "Bold (Ctrl+B)",
210
231
  disabled: i.disabled,
211
- onClick: W
212
- }, [...o[4] ||= [r("strong", null, "B", -1)]], 8, ne),
232
+ onClick: U
233
+ }, [...o[4] ||= [r("strong", null, "B", -1)]], 8, _),
213
234
  r("button", {
214
235
  type: "button",
215
236
  class: "t-md-editor__toolbar-btn",
216
237
  title: "Italic (Ctrl+I)",
217
238
  disabled: i.disabled,
218
- onClick: G
219
- }, [...o[5] ||= [r("em", null, "I", -1)]], 8, h),
239
+ onClick: W
240
+ }, [...o[5] ||= [r("em", null, "I", -1)]], 8, v),
220
241
  r("button", {
221
242
  type: "button",
222
243
  class: "t-md-editor__toolbar-btn",
223
244
  title: "Strikethrough",
224
245
  disabled: i.disabled,
225
- onClick: o[3] ||= (e) => H("~~", "~~")
226
- }, [...o[6] ||= [r("del", null, "S", -1)]], 8, g)
246
+ onClick: o[3] ||= (e) => V("~~", "~~")
247
+ }, [...o[6] ||= [r("del", null, "S", -1)]], 8, ee)
227
248
  ]),
228
249
  o[8] ||= r("div", {
229
250
  class: "t-md-editor__toolbar-divider",
230
251
  role: "separator"
231
252
  }, null, -1),
232
- r("div", _, [
253
+ r("div", te, [
233
254
  r("button", {
234
255
  type: "button",
235
256
  class: "t-md-editor__toolbar-btn",
236
257
  title: "Inline code (Ctrl+E)",
237
258
  disabled: i.disabled,
238
- onClick: K
239
- }, " </> ", 8, v),
259
+ onClick: G
260
+ }, " </> ", 8, y),
240
261
  r("button", {
241
262
  type: "button",
242
263
  class: "t-md-editor__toolbar-btn",
243
264
  title: "Code block",
244
265
  disabled: i.disabled,
245
- onClick: q
246
- }, " { } ", 8, y),
266
+ onClick: oe
267
+ }, " { } ", 8, b),
247
268
  r("button", {
248
269
  type: "button",
249
270
  class: "t-md-editor__toolbar-btn",
250
271
  title: "Link (Ctrl+K)",
251
272
  disabled: i.disabled,
252
- onClick: J
253
- }, " 🔗 ", 8, b)
273
+ onClick: K
274
+ }, " 🔗 ", 8, x)
254
275
  ]),
255
276
  o[9] ||= r("div", {
256
277
  class: "t-md-editor__toolbar-divider",
257
278
  role: "separator"
258
279
  }, null, -1),
259
- r("div", x, [
280
+ r("div", S, [
260
281
  r("button", {
261
282
  type: "button",
262
283
  class: "t-md-editor__toolbar-btn",
263
284
  title: "Unordered list",
264
285
  disabled: i.disabled,
265
- onClick: Y
266
- }, " •\xA0List ", 8, S),
286
+ onClick: q
287
+ }, " •\xA0List ", 8, C),
267
288
  r("button", {
268
289
  type: "button",
269
290
  class: "t-md-editor__toolbar-btn",
270
291
  title: "Ordered list",
271
292
  disabled: i.disabled,
272
- onClick: X
273
- }, " 1.\xA0List ", 8, C),
293
+ onClick: se
294
+ }, " 1.\xA0List ", 8, w),
274
295
  r("button", {
275
296
  type: "button",
276
297
  class: "t-md-editor__toolbar-btn",
277
298
  title: "Blockquote",
278
299
  disabled: i.disabled,
279
- onClick: ie
280
- }, " > ", 8, w),
300
+ onClick: ce
301
+ }, " > ", 8, T),
281
302
  r("button", {
282
303
  type: "button",
283
304
  class: "t-md-editor__toolbar-btn",
284
305
  title: "Horizontal rule",
285
306
  disabled: i.disabled,
286
- onClick: ae
287
- }, " ― ", 8, re)
307
+ onClick: le
308
+ }, " ― ", 8, E)
288
309
  ]),
289
310
  l(e.$slots, "toolbar")
290
311
  ]),
291
- r("div", T, [r("div", E, [r("textarea", {
312
+ r("div", ne, [r("div", re, [r("textarea", {
292
313
  ref_key: "textareaRef",
293
- ref: F,
314
+ ref: P,
294
315
  class: "t-md-editor__textarea",
295
- value: B.value,
316
+ value: z.value,
296
317
  placeholder: i.placeholder,
297
318
  disabled: i.disabled,
298
319
  "aria-label": i.placeholder,
299
- onInput: V,
300
- onPaste: ce,
301
- onDrop: le,
302
- onDragover: ue,
303
- onKeydown: de
304
- }, null, 40, D)]), r("div", O, [r("div", {
320
+ onInput: B,
321
+ onPaste: ve,
322
+ onDrop: ye,
323
+ onDragover: be,
324
+ onKeydown: xe
325
+ }, null, 40, ie)]), r("div", ae, [r("div", {
305
326
  class: "t-md-editor__preview",
306
- innerHTML: oe.value
307
- }, null, 8, k)])]),
308
- I.value ? (s(), n("div", A, " Uploading image… ")) : t("", !0)
327
+ innerHTML: ue.value
328
+ }, null, 8, D)])]),
329
+ F.value ? (s(), n("div", O, " Uploading image… ")) : t("", !0)
309
330
  ], 16));
310
331
  }
311
332
  });
312
333
  //#endregion
313
- export { j as default };
334
+ export { A as default };
314
335
 
315
336
  //# sourceMappingURL=TMarkdownEditor.vue_vue_type_script_setup_true_lang.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TMarkdownEditor.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/TMarkdownEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, useAttrs, nextTick } from 'vue';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n size?: TSize;\n disabled?: boolean;\n placeholder?: string;\n uploadImage?: (file: File) => Promise<string>;\n }>(),\n {\n modelValue: '',\n size: 'md',\n disabled: false,\n placeholder: 'Write your markdown here...',\n uploadImage: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'image-upload': [file: File, url: string];\n 'image-upload-error': [file: File, error: Error];\n}>();\n\ndefineSlots<{\n toolbar?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\nconst isUploading = ref(false);\n\nconst rootClasses = computed(() => [\n 't-md-editor',\n `t-md-editor--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-uploading': isUploading.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst editorAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst stringValue = computed(() => `${props.modelValue ?? ''}`);\n\nconst onInput = (event: Event) => {\n emit('update:modelValue', (event.target as HTMLTextAreaElement).value);\n};\n\n/* ──────── Toolbar Actions ──────── */\n\nconst insertAtCursor = (before: string, after = '') => {\n const el = textareaRef.value;\n if (!el) return;\n\n const start = el.selectionStart;\n const end = el.selectionEnd;\n const text = stringValue.value;\n const selected = text.slice(start, end);\n\n const newText = text.slice(0, start) + before + selected + after + text.slice(end);\n emit('update:modelValue', newText);\n\n nextTick(() => {\n el.focus();\n const cursorPos = selected\n ? start + before.length + selected.length + after.length\n : start + before.length;\n el.setSelectionRange(cursorPos, cursorPos);\n });\n};\n\nconst insertHeading = (level: number) => {\n const prefix = '#'.repeat(level) + ' ';\n const el = textareaRef.value;\n if (!el) return;\n\n const start = el.selectionStart;\n const text = stringValue.value;\n\n // Find start of current line\n const lineStart = text.lastIndexOf('\\n', start - 1) + 1;\n const lineEnd = text.indexOf('\\n', start);\n const actualEnd = lineEnd === -1 ? text.length : lineEnd;\n const line = text.slice(lineStart, actualEnd);\n\n // Strip existing heading markers\n const stripped = line.replace(/^#{1,6}\\s*/, '');\n const newLine = prefix + stripped;\n\n const newText = text.slice(0, lineStart) + newLine + text.slice(actualEnd);\n emit('update:modelValue', newText);\n\n nextTick(() => {\n el.focus();\n el.setSelectionRange(lineStart + newLine.length, lineStart + newLine.length);\n });\n};\n\nconst insertBold = () => insertAtCursor('**', '**');\nconst insertItalic = () => insertAtCursor('_', '_');\nconst insertCode = () => insertAtCursor('`', '`');\nconst insertCodeBlock = () => insertAtCursor('\\n```\\n', '\\n```\\n');\nconst insertLink = () => insertAtCursor('[', '](url)');\nconst insertList = () => insertAtCursor('\\n- ');\nconst insertOrderedList = () => insertAtCursor('\\n1. ');\nconst insertQuote = () => insertAtCursor('\\n> ');\nconst insertHr = () => insertAtCursor('\\n---\\n');\n\n/* ──────── Markdown → HTML rendering ──────── */\n\nconst renderedHtml = computed(() => {\n return markdownToHtml(stringValue.value);\n});\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n}\n\nfunction markdownToHtml(md: string): string {\n if (!md) return '';\n\n const lines = md.split('\\n');\n const html: string[] = [];\n let inCodeBlock = false;\n let inList = false;\n let inOrderedList = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Code blocks\n if (line.trim().startsWith('```')) {\n if (inCodeBlock) {\n html.push('</code></pre>');\n inCodeBlock = false;\n } else {\n inCodeBlock = true;\n html.push('<pre class=\"t-md-editor__code-block\"><code>');\n }\n continue;\n }\n\n if (inCodeBlock) {\n html.push(escapeHtml(line));\n html.push('\\n');\n continue;\n }\n\n // Close open lists if needed\n const isUnorderedListItem = /^[-*+]\\s/.test(line.trim());\n const isOrderedListItem = /^\\d+\\.\\s/.test(line.trim());\n\n if (inList && !isUnorderedListItem) {\n html.push('</ul>');\n inList = false;\n }\n if (inOrderedList && !isOrderedListItem) {\n html.push('</ol>');\n inOrderedList = false;\n }\n\n // Horizontal rule\n if (/^(-{3,}|_{3,}|\\*{3,})$/.test(line.trim())) {\n html.push('<hr class=\"t-md-editor__hr\" />');\n continue;\n }\n\n // Headings\n const headingMatch = line.match(/^(#{1,6})\\s+(.+)$/);\n if (headingMatch) {\n const level = headingMatch[1].length;\n html.push(`<h${level} class=\"t-md-editor__heading t-md-editor__heading--${level}\">${inlineMarkdown(escapeHtml(headingMatch[2]))}</h${level}>`);\n continue;\n }\n\n // Blockquote\n if (line.trim().startsWith('> ')) {\n html.push(`<blockquote class=\"t-md-editor__blockquote\">${inlineMarkdown(escapeHtml(line.trim().slice(2)))}</blockquote>`);\n continue;\n }\n\n // Unordered list\n if (isUnorderedListItem) {\n if (!inList) {\n html.push('<ul class=\"t-md-editor__list\">');\n inList = true;\n }\n html.push(`<li>${inlineMarkdown(escapeHtml(line.trim().slice(2)))}</li>`);\n continue;\n }\n\n // Ordered list\n if (isOrderedListItem) {\n if (!inOrderedList) {\n html.push('<ol class=\"t-md-editor__list t-md-editor__list--ordered\">');\n inOrderedList = true;\n }\n const content = line.trim().replace(/^\\d+\\.\\s/, '');\n html.push(`<li>${inlineMarkdown(escapeHtml(content))}</li>`);\n continue;\n }\n\n // Empty line\n if (line.trim() === '') {\n html.push('<br />');\n continue;\n }\n\n // Paragraph\n html.push(`<p class=\"t-md-editor__paragraph\">${inlineMarkdown(escapeHtml(line))}</p>`);\n }\n\n // Close any open blocks\n if (inCodeBlock) html.push('</code></pre>');\n if (inList) html.push('</ul>');\n if (inOrderedList) html.push('</ol>');\n\n return html.join('\\n');\n}\n\nfunction inlineMarkdown(text: string): string {\n // Images: ![alt](url)\n text = text.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, '<img src=\"$2\" alt=\"$1\" class=\"t-md-editor__image\" loading=\"lazy\" />');\n // Links: [text](url)\n text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>');\n // Bold: **text**\n text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n // Italic: _text_ or *text*\n text = text.replace(/_(.+?)_/g, '<em>$1</em>');\n text = text.replace(/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, '<em>$1</em>');\n // Inline code: `text`\n text = text.replace(/`(.+?)`/g, '<code class=\"t-md-editor__inline-code\">$1</code>');\n // Strikethrough: ~~text~~\n text = text.replace(/~~(.+?)~~/g, '<del>$1</del>');\n return text;\n}\n\n/* ──────── Image paste / drop handling ──────── */\n\nconst handlePaste = async (event: ClipboardEvent) => {\n if (props.disabled) return;\n\n const items = event.clipboardData?.items;\n if (!items) return;\n\n for (const item of items) {\n if (item.type.startsWith('image/')) {\n event.preventDefault();\n const file = item.getAsFile();\n if (file) await processImageUpload(file);\n return;\n }\n }\n};\n\nconst handleDrop = async (event: DragEvent) => {\n if (props.disabled) return;\n\n const files = event.dataTransfer?.files;\n if (!files) return;\n\n for (const file of files) {\n if (file.type.startsWith('image/')) {\n event.preventDefault();\n await processImageUpload(file);\n }\n }\n};\n\nconst handleDragOver = (event: DragEvent) => {\n if (props.disabled) return;\n event.preventDefault();\n};\n\nconst processImageUpload = async (file: File) => {\n if (!props.uploadImage) {\n // Fallback: insert a placeholder with the file name\n insertAtCursor(`![${file.name}]()`);\n return;\n }\n\n // Insert uploading placeholder\n const placeholder = `![Uploading ${file.name}...]()`;\n insertAtCursor(placeholder);\n isUploading.value = true;\n\n try {\n const url = props.uploadImage(file);\n const resolvedUrl = url instanceof Promise ? await url : url;\n\n // Replace placeholder with actual URL\n const currentValue = stringValue.value;\n const newValue = currentValue.replace(placeholder, `![${file.name}](${resolvedUrl})`);\n emit('update:modelValue', newValue);\n emit('image-upload', file, resolvedUrl);\n } catch (err) {\n // Remove placeholder on error\n const currentValue = stringValue.value;\n const newValue = currentValue.replace(placeholder, '');\n emit('update:modelValue', newValue);\n emit('image-upload-error', file, err instanceof Error ? err : new Error(String(err)));\n } finally {\n isUploading.value = false;\n }\n};\n\n/* ──────── Keyboard shortcuts ──────── */\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n const mod = event.metaKey || event.ctrlKey;\n if (!mod) return;\n\n switch (event.key) {\n case 'b':\n event.preventDefault();\n insertBold();\n break;\n case 'i':\n event.preventDefault();\n insertItalic();\n break;\n case 'k':\n event.preventDefault();\n insertLink();\n break;\n case 'e':\n event.preventDefault();\n insertCode();\n break;\n }\n};\n\n</script>\n\n<template>\n <div\n v-bind=\"editorAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <!-- Toolbar -->\n <div\n class=\"t-md-editor__toolbar\"\n role=\"toolbar\"\n aria-label=\"Markdown formatting\"\n >\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 1 (H1)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(1)\"\n >\n H1\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 2 (H2)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(2)\"\n >\n H2\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 3 (H3)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(3)\"\n >\n H3\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Bold (Ctrl+B)\"\n :disabled=\"disabled\"\n @click=\"insertBold\"\n >\n <strong>B</strong>\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Italic (Ctrl+I)\"\n :disabled=\"disabled\"\n @click=\"insertItalic\"\n >\n <em>I</em>\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Strikethrough\"\n :disabled=\"disabled\"\n @click=\"insertAtCursor('~~', '~~')\"\n >\n <del>S</del>\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Inline code (Ctrl+E)\"\n :disabled=\"disabled\"\n @click=\"insertCode\"\n >\n &lt;/&gt;\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Code block\"\n :disabled=\"disabled\"\n @click=\"insertCodeBlock\"\n >\n { }\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Link (Ctrl+K)\"\n :disabled=\"disabled\"\n @click=\"insertLink\"\n >\n 🔗\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Unordered list\"\n :disabled=\"disabled\"\n @click=\"insertList\"\n >\n •&nbsp;List\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Ordered list\"\n :disabled=\"disabled\"\n @click=\"insertOrderedList\"\n >\n 1.&nbsp;List\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Blockquote\"\n :disabled=\"disabled\"\n @click=\"insertQuote\"\n >\n &gt;\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Horizontal rule\"\n :disabled=\"disabled\"\n @click=\"insertHr\"\n >\n ―\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n </div>\n\n <!-- Editor body -->\n <div class=\"t-md-editor__body\">\n <!-- Write pane -->\n <div\n class=\"t-md-editor__pane t-md-editor__pane--write\"\n >\n <textarea\n ref=\"textareaRef\"\n class=\"t-md-editor__textarea\"\n :value=\"stringValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"placeholder\"\n @input=\"onInput\"\n @paste=\"handlePaste\"\n @drop=\"handleDrop\"\n @dragover=\"handleDragOver\"\n @keydown=\"handleKeydown\"\n />\n </div>\n\n <!-- Preview pane -->\n <div\n class=\"t-md-editor__pane t-md-editor__pane--preview\"\n aria-label=\"Markdown preview\"\n >\n <!-- eslint-disable vue/no-v-html -->\n <div\n class=\"t-md-editor__preview\"\n v-html=\"renderedHtml\"\n />\n <!-- eslint-enable vue/no-v-html -->\n </div>\n </div>\n\n <!-- Upload indicator -->\n <div\n v-if=\"isUploading\"\n class=\"t-md-editor__upload-bar\"\n role=\"status\"\n aria-live=\"polite\"\n >\n Uploading image…\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TMarkdownEditor.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/TMarkdownEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, useAttrs, nextTick } from 'vue';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n size?: TSize;\n disabled?: boolean;\n placeholder?: string;\n uploadImage?: (file: File) => Promise<string>;\n sanitize?: (html: string) => string;\n }>(),\n {\n modelValue: '',\n size: 'md',\n disabled: false,\n placeholder: 'Write your markdown here...',\n uploadImage: undefined,\n sanitize: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'image-upload': [file: File, url: string];\n 'image-upload-error': [file: File, error: Error];\n}>();\n\ndefineSlots<{\n toolbar?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\nconst isUploading = ref(false);\n\nconst rootClasses = computed(() => [\n 't-md-editor',\n `t-md-editor--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-uploading': isUploading.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst editorAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst stringValue = computed(() => `${props.modelValue ?? ''}`);\n\nconst onInput = (event: Event) => {\n emit('update:modelValue', (event.target as HTMLTextAreaElement).value);\n};\n\n/* ──────── Toolbar Actions ──────── */\n\nconst insertAtCursor = (before: string, after = '') => {\n const el = textareaRef.value;\n if (!el) return;\n\n const start = el.selectionStart;\n const end = el.selectionEnd;\n const text = stringValue.value;\n const selected = text.slice(start, end);\n\n const newText = text.slice(0, start) + before + selected + after + text.slice(end);\n emit('update:modelValue', newText);\n\n nextTick(() => {\n el.focus();\n const cursorPos = selected\n ? start + before.length + selected.length + after.length\n : start + before.length;\n el.setSelectionRange(cursorPos, cursorPos);\n });\n};\n\nconst insertHeading = (level: number) => {\n const prefix = '#'.repeat(level) + ' ';\n const el = textareaRef.value;\n if (!el) return;\n\n const start = el.selectionStart;\n const text = stringValue.value;\n\n // Find start of current line\n const lineStart = text.lastIndexOf('\\n', start - 1) + 1;\n const lineEnd = text.indexOf('\\n', start);\n const actualEnd = lineEnd === -1 ? text.length : lineEnd;\n const line = text.slice(lineStart, actualEnd);\n\n // Strip existing heading markers\n const stripped = line.replace(/^#{1,6}\\s*/, '');\n const newLine = prefix + stripped;\n\n const newText = text.slice(0, lineStart) + newLine + text.slice(actualEnd);\n emit('update:modelValue', newText);\n\n nextTick(() => {\n el.focus();\n el.setSelectionRange(lineStart + newLine.length, lineStart + newLine.length);\n });\n};\n\nconst insertBold = () => insertAtCursor('**', '**');\nconst insertItalic = () => insertAtCursor('_', '_');\nconst insertCode = () => insertAtCursor('`', '`');\nconst insertCodeBlock = () => insertAtCursor('\\n```\\n', '\\n```\\n');\nconst insertLink = () => insertAtCursor('[', '](url)');\nconst insertList = () => insertAtCursor('\\n- ');\nconst insertOrderedList = () => insertAtCursor('\\n1. ');\nconst insertQuote = () => insertAtCursor('\\n> ');\nconst insertHr = () => insertAtCursor('\\n---\\n');\n\n/* ──────── Markdown → HTML rendering ──────── */\n\nconst renderedHtml = computed(() => {\n const html = markdownToHtml(stringValue.value);\n // Escape hatch: the built-in rendering escapes HTML and allowlists URL\n // schemes, but consumers with a stricter policy can pass their own\n // sanitizer (DOMPurify or similar) instead of forking the component.\n return props.sanitize ? props.sanitize(html) : html;\n});\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n}\n\nfunction markdownToHtml(md: string): string {\n if (!md) return '';\n\n const lines = md.split('\\n');\n const html: string[] = [];\n let inCodeBlock = false;\n let inList = false;\n let inOrderedList = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Code blocks\n if (line.trim().startsWith('```')) {\n if (inCodeBlock) {\n html.push('</code></pre>');\n inCodeBlock = false;\n } else {\n inCodeBlock = true;\n html.push('<pre class=\"t-md-editor__code-block\"><code>');\n }\n continue;\n }\n\n if (inCodeBlock) {\n html.push(escapeHtml(line));\n html.push('\\n');\n continue;\n }\n\n // Close open lists if needed\n const isUnorderedListItem = /^[-*+]\\s/.test(line.trim());\n const isOrderedListItem = /^\\d+\\.\\s/.test(line.trim());\n\n if (inList && !isUnorderedListItem) {\n html.push('</ul>');\n inList = false;\n }\n if (inOrderedList && !isOrderedListItem) {\n html.push('</ol>');\n inOrderedList = false;\n }\n\n // Horizontal rule\n if (/^(-{3,}|_{3,}|\\*{3,})$/.test(line.trim())) {\n html.push('<hr class=\"t-md-editor__hr\" />');\n continue;\n }\n\n // Headings\n const headingMatch = line.match(/^(#{1,6})\\s+(.+)$/);\n if (headingMatch) {\n const level = headingMatch[1].length;\n html.push(`<h${level} class=\"t-md-editor__heading t-md-editor__heading--${level}\">${inlineMarkdown(escapeHtml(headingMatch[2]))}</h${level}>`);\n continue;\n }\n\n // Blockquote\n if (line.trim().startsWith('> ')) {\n html.push(`<blockquote class=\"t-md-editor__blockquote\">${inlineMarkdown(escapeHtml(line.trim().slice(2)))}</blockquote>`);\n continue;\n }\n\n // Unordered list\n if (isUnorderedListItem) {\n if (!inList) {\n html.push('<ul class=\"t-md-editor__list\">');\n inList = true;\n }\n html.push(`<li>${inlineMarkdown(escapeHtml(line.trim().slice(2)))}</li>`);\n continue;\n }\n\n // Ordered list\n if (isOrderedListItem) {\n if (!inOrderedList) {\n html.push('<ol class=\"t-md-editor__list t-md-editor__list--ordered\">');\n inOrderedList = true;\n }\n const content = line.trim().replace(/^\\d+\\.\\s/, '');\n html.push(`<li>${inlineMarkdown(escapeHtml(content))}</li>`);\n continue;\n }\n\n // Empty line\n if (line.trim() === '') {\n html.push('<br />');\n continue;\n }\n\n // Paragraph\n html.push(`<p class=\"t-md-editor__paragraph\">${inlineMarkdown(escapeHtml(line))}</p>`);\n }\n\n // Close any open blocks\n if (inCodeBlock) html.push('</code></pre>');\n if (inList) html.push('</ul>');\n if (inOrderedList) html.push('</ol>');\n\n return html.join('\\n');\n}\n\n/* ──────── URL safety ──────── */\n\n// A URL carries a scheme only when a colon follows a valid scheme name.\n// Anything else (`/path`, `./path`, `#anchor`, `?q=1`) is relative and cannot\n// introduce a new scheme, so it is safe. `//host/path` is protocol-relative:\n// it keeps the page's protocol but points at another host, so it can navigate\n// off-origin. That is allowed — it is a link, not script — and `rel=\"noopener\n// noreferrer\"` on the anchor covers the tab-nabbing side of it.\nconst HAS_SCHEME = /^[a-z][a-z0-9+.-]*:/;\n// Navigable schemes. `javascript:`, `vbscript:`, `data:` and friends are\n// rejected — everything reaching inlineMarkdown is already HTML-escaped, so\n// the URL scheme is the only remaining way to get script into the preview.\nconst SAFE_LINK_SCHEME = /^(https?|mailto|tel):/;\nconst SAFE_IMAGE_SCHEME = /^https?:/;\n// Inline raster images are useful for pasted screenshots. `image/svg+xml` is\n// deliberately excluded: SVG is a document format that can carry script and\n// external references, not just pixels.\nconst SAFE_IMAGE_DATA_URL = /^data:image\\/(png|jpe?g|gif|webp|avif|bmp|x-icon|vnd\\.microsoft\\.icon)[;,]/;\n\nconst fromCodePoint = (code: number) =>\n Number.isFinite(code) && code >= 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '';\n\n// Reduce a URL to the form a browser would act on before testing its scheme:\n// decode character references, drop control characters and whitespace, and\n// lowercase. This defeats `JaVaScRiPt:`, ` javascript:`, `java\\tscript:`,\n// `java\\x00script:` and `&#106;avascript:` style evasion.\nfunction normalizeUrl(url: string): string {\n return url\n .replace(/&quot;/g, '\"')\n .replace(/&#039;/g, \"'\")\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&amp;/g, '&')\n .replace(/&#x([0-9a-f]+);?/gi, (_match, hex: string) => fromCodePoint(parseInt(hex, 16)))\n .replace(/&#(\\d+);?/g, (_match, dec: string) => fromCodePoint(parseInt(dec, 10)))\n // eslint-disable-next-line no-control-regex -- matching control characters is the point\n .replace(/[\\x00-\\x20\\x7f]/g, '')\n .toLowerCase();\n}\n\nfunction isSafeLinkUrl(url: string): boolean {\n const normalized = normalizeUrl(url);\n return !HAS_SCHEME.test(normalized) || SAFE_LINK_SCHEME.test(normalized);\n}\n\nfunction isSafeImageUrl(url: string): boolean {\n const normalized = normalizeUrl(url);\n if (!HAS_SCHEME.test(normalized)) return true;\n return SAFE_IMAGE_SCHEME.test(normalized) || SAFE_IMAGE_DATA_URL.test(normalized);\n}\n\n// Placeholder delimiter for generated markup. NUL is stripped from the input\n// before any token is created, so a placeholder can never collide with user\n// text, and it survives the emphasis passes because none of them treat it as\n// a delimiter.\nconst TOKEN = '\\x00';\n// eslint-disable-next-line no-control-regex -- matching control characters is the point\nconst TOKEN_PATTERN = /\\x00(\\d+)\\x00/g;\n\nfunction inlineMarkdown(text: string): string {\n const tokens: string[] = [];\n // Park generated markup outside the emphasis passes below. Without this,\n // `_(.+?)_` eats the underscore in the generated target=\"_blank\" and any\n // underscore inside a URL.\n const token = (html: string) => {\n tokens.push(html);\n return `${TOKEN}${tokens.length - 1}${TOKEN}`;\n };\n const inert = (source: string) => token(`<span class=\"t-md-editor__blocked-link\">${source}</span>`);\n\n // eslint-disable-next-line no-control-regex -- matching control characters is the point\n text = text.replace(/\\x00/g, '');\n\n // Images: ![alt](url) — alt is a plain-text attribute, so the whole tag is parked.\n text = text.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, (match, alt: string, url: string) =>\n isSafeImageUrl(url)\n ? token(`<img src=\"${url}\" alt=\"${alt}\" class=\"t-md-editor__image\" loading=\"lazy\" />`)\n : inert(match),\n );\n // Links: [text](url) — only the tags are parked so the label still formats.\n text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (match, label: string, url: string) =>\n isSafeLinkUrl(url)\n ? `${token(`<a href=\"${url}\" target=\"_blank\" rel=\"noopener noreferrer\">`)}${label}${token('</a>')}`\n : inert(match),\n );\n // Bold: **text**\n text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n // Italic: _text_ or *text*\n text = text.replace(/_(.+?)_/g, '<em>$1</em>');\n text = text.replace(/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, '<em>$1</em>');\n // Inline code: `text`\n text = text.replace(/`(.+?)`/g, '<code class=\"t-md-editor__inline-code\">$1</code>');\n // Strikethrough: ~~text~~\n text = text.replace(/~~(.+?)~~/g, '<del>$1</del>');\n\n return text.replace(TOKEN_PATTERN, (_match, index: string) => tokens[Number(index)]);\n}\n\n/* ──────── Image paste / drop handling ──────── */\n\nconst handlePaste = async (event: ClipboardEvent) => {\n if (props.disabled) return;\n\n const items = event.clipboardData?.items;\n if (!items) return;\n\n for (const item of items) {\n if (item.type.startsWith('image/')) {\n event.preventDefault();\n const file = item.getAsFile();\n if (file) await processImageUpload(file);\n return;\n }\n }\n};\n\nconst handleDrop = async (event: DragEvent) => {\n if (props.disabled) return;\n\n const files = event.dataTransfer?.files;\n if (!files) return;\n\n for (const file of files) {\n if (file.type.startsWith('image/')) {\n event.preventDefault();\n await processImageUpload(file);\n }\n }\n};\n\nconst handleDragOver = (event: DragEvent) => {\n if (props.disabled) return;\n event.preventDefault();\n};\n\nconst processImageUpload = async (file: File) => {\n if (!props.uploadImage) {\n // Fallback: insert a placeholder with the file name\n insertAtCursor(`![${file.name}]()`);\n return;\n }\n\n // Insert uploading placeholder\n const placeholder = `![Uploading ${file.name}...]()`;\n insertAtCursor(placeholder);\n isUploading.value = true;\n\n try {\n const url = props.uploadImage(file);\n const resolvedUrl = url instanceof Promise ? await url : url;\n\n // Replace placeholder with actual URL\n const currentValue = stringValue.value;\n const newValue = currentValue.replace(placeholder, `![${file.name}](${resolvedUrl})`);\n emit('update:modelValue', newValue);\n emit('image-upload', file, resolvedUrl);\n } catch (err) {\n // Remove placeholder on error\n const currentValue = stringValue.value;\n const newValue = currentValue.replace(placeholder, '');\n emit('update:modelValue', newValue);\n emit('image-upload-error', file, err instanceof Error ? err : new Error(String(err)));\n } finally {\n isUploading.value = false;\n }\n};\n\n/* ──────── Keyboard shortcuts ──────── */\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n const mod = event.metaKey || event.ctrlKey;\n if (!mod) return;\n\n switch (event.key) {\n case 'b':\n event.preventDefault();\n insertBold();\n break;\n case 'i':\n event.preventDefault();\n insertItalic();\n break;\n case 'k':\n event.preventDefault();\n insertLink();\n break;\n case 'e':\n event.preventDefault();\n insertCode();\n break;\n }\n};\n\n</script>\n\n<template>\n <div\n v-bind=\"editorAttrs\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n >\n <!-- Toolbar -->\n <div\n class=\"t-md-editor__toolbar\"\n role=\"toolbar\"\n aria-label=\"Markdown formatting\"\n >\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 1 (H1)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(1)\"\n >\n H1\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 2 (H2)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(2)\"\n >\n H2\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Heading 3 (H3)\"\n :disabled=\"disabled\"\n @click=\"insertHeading(3)\"\n >\n H3\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Bold (Ctrl+B)\"\n :disabled=\"disabled\"\n @click=\"insertBold\"\n >\n <strong>B</strong>\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Italic (Ctrl+I)\"\n :disabled=\"disabled\"\n @click=\"insertItalic\"\n >\n <em>I</em>\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Strikethrough\"\n :disabled=\"disabled\"\n @click=\"insertAtCursor('~~', '~~')\"\n >\n <del>S</del>\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Inline code (Ctrl+E)\"\n :disabled=\"disabled\"\n @click=\"insertCode\"\n >\n &lt;/&gt;\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Code block\"\n :disabled=\"disabled\"\n @click=\"insertCodeBlock\"\n >\n { }\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Link (Ctrl+K)\"\n :disabled=\"disabled\"\n @click=\"insertLink\"\n >\n 🔗\n </button>\n </div>\n\n <div\n class=\"t-md-editor__toolbar-divider\"\n role=\"separator\"\n />\n\n <div class=\"t-md-editor__toolbar-group\">\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Unordered list\"\n :disabled=\"disabled\"\n @click=\"insertList\"\n >\n •&nbsp;List\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Ordered list\"\n :disabled=\"disabled\"\n @click=\"insertOrderedList\"\n >\n 1.&nbsp;List\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Blockquote\"\n :disabled=\"disabled\"\n @click=\"insertQuote\"\n >\n &gt;\n </button>\n <button\n type=\"button\"\n class=\"t-md-editor__toolbar-btn\"\n title=\"Horizontal rule\"\n :disabled=\"disabled\"\n @click=\"insertHr\"\n >\n ―\n </button>\n </div>\n\n <slot name=\"toolbar\" />\n </div>\n\n <!-- Editor body -->\n <div class=\"t-md-editor__body\">\n <!-- Write pane -->\n <div\n class=\"t-md-editor__pane t-md-editor__pane--write\"\n >\n <textarea\n ref=\"textareaRef\"\n class=\"t-md-editor__textarea\"\n :value=\"stringValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"placeholder\"\n @input=\"onInput\"\n @paste=\"handlePaste\"\n @drop=\"handleDrop\"\n @dragover=\"handleDragOver\"\n @keydown=\"handleKeydown\"\n />\n </div>\n\n <!-- Preview pane -->\n <div\n class=\"t-md-editor__pane t-md-editor__pane--preview\"\n aria-label=\"Markdown preview\"\n >\n <!-- eslint-disable vue/no-v-html -->\n <div\n class=\"t-md-editor__preview\"\n v-html=\"renderedHtml\"\n />\n <!-- eslint-enable vue/no-v-html -->\n </div>\n </div>\n\n <!-- Upload indicator -->\n <div\n v-if=\"isUploading\"\n class=\"t-md-editor__upload-bar\"\n role=\"status\"\n aria-live=\"polite\"\n >\n Uploading image…\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TPopover.cjs","names":[],"sources":["../../src/components/TPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { createId, focusFirst, isEscapeKey } from '@treeui/utils';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TTooltipSide } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n side?: TTooltipSide;\n align?: 'start' | 'center' | 'end';\n id?: string;\n }>(),\n {\n open: undefined,\n defaultOpen: false,\n disabled: false,\n side: 'bottom',\n align: 'center',\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean }): unknown;\n default(): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst contentId = props.id ?? createId('t-popover');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-popover',\n {\n 'is-open': isOpen.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst contentClasses = computed(() => [\n 't-popover__content',\n `t-popover__content--${props.side}`,\n `t-popover__content--align-${props.align}`,\n]);\n\nconst openPopover = () => {\n if (props.disabled) return;\n setValue(true);\n nextTick(() => {\n if (contentRef.value) {\n if (!focusFirst(contentRef.value)) {\n contentRef.value.focus();\n }\n }\n });\n};\n\nconst closePopover = (restoreFocus = false) => {\n setValue(false);\n if (restoreFocus) {\n nextTick(() => triggerRef.value?.focus());\n }\n};\n\nconst togglePopover = () => {\n if (isOpen.value) {\n closePopover();\n } else {\n openPopover();\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closePopover();\n }\n};\n\nconst onContentKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closePopover(true);\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closePopover();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <div\n class=\"t-popover__anchor\"\n @click=\"togglePopover\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n >\n <button\n ref=\"triggerRef\"\n type=\"button\"\n class=\"t-popover__trigger\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? contentId : undefined\"\n aria-haspopup=\"dialog\"\n v-bind=\"triggerAttrs\"\n />\n </slot>\n </div>\n <transition name=\"t-popover-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"contentId\"\n ref=\"contentRef\"\n role=\"dialog\"\n :class=\"contentClasses\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n tabindex=\"-1\"\n @keydown=\"onContentKeydown\"\n >\n <slot />\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TPopover.cjs","names":[],"sources":["../../src/components/TPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { createId, focusFirst, isEscapeKey } from '@treeui/utils';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TTooltipSide } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n side?: TTooltipSide;\n align?: 'start' | 'center' | 'end';\n id?: string;\n }>(),\n {\n open: undefined,\n defaultOpen: false,\n disabled: false,\n side: 'bottom',\n align: 'center',\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean; contentId: string }): unknown;\n default(): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst contentId = props.id ?? createId('t-popover');\nconst rootRef = ref<HTMLElement | null>(null);\n// Bound to the anchor wrapper, not the fallback button: the fallback does not\n// render when a consumer supplies a `trigger` slot, so a ref on it would be\n// null exactly when focus restoration matters most. focusFirst() resolves the\n// real trigger either way. Mirrors TDropdown's trigger-wrapper ref.\nconst triggerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-popover',\n {\n 'is-open': isOpen.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst contentClasses = computed(() => [\n 't-popover__content',\n `t-popover__content--${props.side}`,\n `t-popover__content--align-${props.align}`,\n]);\n\nconst openPopover = () => {\n if (props.disabled) return;\n setValue(true);\n nextTick(() => {\n if (contentRef.value) {\n if (!focusFirst(contentRef.value)) {\n contentRef.value.focus();\n }\n }\n });\n};\n\nconst closePopover = (restoreFocus = false) => {\n setValue(false);\n if (restoreFocus) {\n nextTick(() => {\n if (triggerRef.value) focusFirst(triggerRef.value);\n });\n }\n};\n\nconst togglePopover = () => {\n if (isOpen.value) {\n closePopover();\n } else {\n openPopover();\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closePopover();\n }\n};\n\nconst onContentKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closePopover(true);\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closePopover();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <div\n ref=\"triggerRef\"\n class=\"t-popover__anchor\"\n @click=\"togglePopover\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n :content-id=\"contentId\"\n >\n <button\n type=\"button\"\n class=\"t-popover__trigger\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? contentId : undefined\"\n aria-haspopup=\"dialog\"\n v-bind=\"triggerAttrs\"\n />\n </slot>\n </div>\n <transition name=\"t-popover-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"contentId\"\n ref=\"contentRef\"\n role=\"dialog\"\n :class=\"contentClasses\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n tabindex=\"-1\"\n @keydown=\"onContentKeydown\"\n >\n <slot />\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"TPopover.js","names":[],"sources":["../../src/components/TPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { createId, focusFirst, isEscapeKey } from '@treeui/utils';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TTooltipSide } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n side?: TTooltipSide;\n align?: 'start' | 'center' | 'end';\n id?: string;\n }>(),\n {\n open: undefined,\n defaultOpen: false,\n disabled: false,\n side: 'bottom',\n align: 'center',\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean }): unknown;\n default(): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst contentId = props.id ?? createId('t-popover');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-popover',\n {\n 'is-open': isOpen.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst contentClasses = computed(() => [\n 't-popover__content',\n `t-popover__content--${props.side}`,\n `t-popover__content--align-${props.align}`,\n]);\n\nconst openPopover = () => {\n if (props.disabled) return;\n setValue(true);\n nextTick(() => {\n if (contentRef.value) {\n if (!focusFirst(contentRef.value)) {\n contentRef.value.focus();\n }\n }\n });\n};\n\nconst closePopover = (restoreFocus = false) => {\n setValue(false);\n if (restoreFocus) {\n nextTick(() => triggerRef.value?.focus());\n }\n};\n\nconst togglePopover = () => {\n if (isOpen.value) {\n closePopover();\n } else {\n openPopover();\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closePopover();\n }\n};\n\nconst onContentKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closePopover(true);\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closePopover();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <div\n class=\"t-popover__anchor\"\n @click=\"togglePopover\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n >\n <button\n ref=\"triggerRef\"\n type=\"button\"\n class=\"t-popover__trigger\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? contentId : undefined\"\n aria-haspopup=\"dialog\"\n v-bind=\"triggerAttrs\"\n />\n </slot>\n </div>\n <transition name=\"t-popover-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"contentId\"\n ref=\"contentRef\"\n role=\"dialog\"\n :class=\"contentClasses\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n tabindex=\"-1\"\n @keydown=\"onContentKeydown\"\n >\n <slot />\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TPopover.js","names":[],"sources":["../../src/components/TPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { createId, focusFirst, isEscapeKey } from '@treeui/utils';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TTooltipSide } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst props = withDefaults(\n defineProps<{\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n side?: TTooltipSide;\n align?: 'start' | 'center' | 'end';\n id?: string;\n }>(),\n {\n open: undefined,\n defaultOpen: false,\n disabled: false,\n side: 'bottom',\n align: 'center',\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean; contentId: string }): unknown;\n default(): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst contentId = props.id ?? createId('t-popover');\nconst rootRef = ref<HTMLElement | null>(null);\n// Bound to the anchor wrapper, not the fallback button: the fallback does not\n// render when a consumer supplies a `trigger` slot, so a ref on it would be\n// null exactly when focus restoration matters most. focusFirst() resolves the\n// real trigger either way. Mirrors TDropdown's trigger-wrapper ref.\nconst triggerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-popover',\n {\n 'is-open': isOpen.value,\n 'is-disabled': props.disabled,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst contentClasses = computed(() => [\n 't-popover__content',\n `t-popover__content--${props.side}`,\n `t-popover__content--align-${props.align}`,\n]);\n\nconst openPopover = () => {\n if (props.disabled) return;\n setValue(true);\n nextTick(() => {\n if (contentRef.value) {\n if (!focusFirst(contentRef.value)) {\n contentRef.value.focus();\n }\n }\n });\n};\n\nconst closePopover = (restoreFocus = false) => {\n setValue(false);\n if (restoreFocus) {\n nextTick(() => {\n if (triggerRef.value) focusFirst(triggerRef.value);\n });\n }\n};\n\nconst togglePopover = () => {\n if (isOpen.value) {\n closePopover();\n } else {\n openPopover();\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closePopover();\n }\n};\n\nconst onContentKeydown = (event: KeyboardEvent) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closePopover(true);\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closePopover();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <div\n ref=\"triggerRef\"\n class=\"t-popover__anchor\"\n @click=\"togglePopover\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n :content-id=\"contentId\"\n >\n <button\n type=\"button\"\n class=\"t-popover__trigger\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? contentId : undefined\"\n aria-haspopup=\"dialog\"\n v-bind=\"triggerAttrs\"\n />\n </slot>\n </div>\n <transition name=\"t-popover-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"contentId\"\n ref=\"contentRef\"\n role=\"dialog\"\n :class=\"contentClasses\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n tabindex=\"-1\"\n @keydown=\"onContentKeydown\"\n >\n <slot />\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}