@lukso/web-components 1.156.0 → 1.157.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_commonjsHelpers-B85MJLTf.js +5 -0
- package/dist/_commonjsHelpers-CFO10eej.cjs +7 -0
- package/dist/axe-BK9JSROP.js +35093 -0
- package/dist/axe-C-H1UVi1.cjs +35097 -0
- package/dist/components/index.cjs +5 -5
- package/dist/components/index.js +5 -5
- package/dist/components/lukso-button/index.cjs +6 -6
- package/dist/components/lukso-button/index.js +4 -4
- package/dist/components/lukso-card/index.cjs +20 -20
- package/dist/components/lukso-card/index.js +8 -8
- package/dist/components/lukso-checkbox/index.cjs +3 -3
- package/dist/components/lukso-checkbox/index.js +3 -3
- package/dist/components/lukso-collapse/index.cjs +5 -5
- package/dist/components/lukso-collapse/index.js +4 -4
- package/dist/components/lukso-color-picker/index.cjs +6 -6
- package/dist/components/lukso-color-picker/index.js +5 -5
- package/dist/components/lukso-dropdown/index.cjs +4 -4
- package/dist/components/lukso-dropdown/index.js +4 -4
- package/dist/components/lukso-dropdown-option/index.cjs +2 -2
- package/dist/components/lukso-dropdown-option/index.js +2 -2
- package/dist/components/lukso-footer/index.cjs +2 -2
- package/dist/components/lukso-footer/index.js +2 -2
- package/dist/components/lukso-icon/index.cjs +41 -19
- package/dist/components/lukso-icon/index.d.ts +4 -4
- package/dist/components/lukso-icon/index.d.ts.map +1 -1
- package/dist/components/lukso-icon/index.js +41 -19
- package/dist/components/lukso-icon/lukso-icon.stories.d.ts +15 -1
- package/dist/components/lukso-icon/lukso-icon.stories.d.ts.map +1 -1
- package/dist/components/lukso-icon/vuesax/bold/warning-2.svg +3 -0
- package/dist/components/lukso-icon/vuesax/broken/warning-2.svg +5 -0
- package/dist/components/lukso-icon/vuesax/bulk/warning-2.svg +5 -0
- package/dist/components/lukso-icon/vuesax/linear/warning-2.svg +5 -0
- package/dist/components/lukso-icon/vuesax/outline/warning-2.svg +5 -0
- package/dist/components/lukso-icon/vuesax/twotone/warning-2.svg +5 -0
- package/dist/components/lukso-image/index.cjs +4 -4
- package/dist/components/lukso-image/index.js +4 -4
- package/dist/components/lukso-input/index.cjs +5 -5
- package/dist/components/lukso-input/index.js +4 -4
- package/dist/components/lukso-markdown/index.cjs +3 -2
- package/dist/components/lukso-markdown/index.d.ts.map +1 -1
- package/dist/components/lukso-markdown/index.js +3 -2
- package/dist/components/lukso-markdown-editor/index.cjs +858 -157
- package/dist/components/lukso-markdown-editor/index.d.ts +63 -1
- package/dist/components/lukso-markdown-editor/index.d.ts.map +1 -1
- package/dist/components/lukso-markdown-editor/index.js +857 -156
- package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts +65 -1
- package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts.map +1 -1
- package/dist/components/lukso-modal/index.cjs +2 -2
- package/dist/components/lukso-modal/index.js +2 -2
- package/dist/components/lukso-navbar/index.cjs +3 -3
- package/dist/components/lukso-navbar/index.js +3 -3
- package/dist/components/lukso-pagination/index.cjs +3 -3
- package/dist/components/lukso-pagination/index.js +3 -3
- package/dist/components/lukso-profile/index.cjs +3 -3
- package/dist/components/lukso-profile/index.js +3 -3
- package/dist/components/lukso-progress/index.cjs +7 -7
- package/dist/components/lukso-progress/index.js +4 -4
- package/dist/components/lukso-radio/index.cjs +6 -6
- package/dist/components/lukso-radio/index.js +4 -4
- package/dist/components/lukso-radio-group/index.cjs +3 -3
- package/dist/components/lukso-radio-group/index.js +3 -3
- package/dist/components/lukso-sanitize/index.cjs +30 -19
- package/dist/components/lukso-sanitize/index.js +30 -19
- package/dist/components/lukso-search/index.cjs +7 -7
- package/dist/components/lukso-search/index.js +7 -7
- package/dist/components/lukso-select/index.cjs +5 -5
- package/dist/components/lukso-select/index.js +5 -5
- package/dist/components/lukso-share/index.cjs +2 -2
- package/dist/components/lukso-share/index.js +2 -2
- package/dist/components/lukso-switch/index.cjs +3 -3
- package/dist/components/lukso-switch/index.js +3 -3
- package/dist/components/lukso-tag/index.cjs +3 -3
- package/dist/components/lukso-tag/index.js +3 -3
- package/dist/components/lukso-terms/index.cjs +3 -3
- package/dist/components/lukso-terms/index.js +3 -3
- package/dist/components/lukso-textarea/index.cjs +5 -5
- package/dist/components/lukso-textarea/index.js +4 -4
- package/dist/components/lukso-tooltip/index.cjs +3 -3
- package/dist/components/lukso-tooltip/index.js +3 -3
- package/dist/components/lukso-username/index.cjs +6 -6
- package/dist/components/lukso-username/index.js +6 -6
- package/dist/components/lukso-wizard/index.cjs +2 -2
- package/dist/components/lukso-wizard/index.js +2 -2
- package/dist/docs/Typography.stories.d.ts.map +1 -1
- package/dist/{index-QfURPzjM.cjs → index-1XmcSGot.cjs} +6 -6
- package/dist/{index-BvQ5tHy1.cjs → index-BhmMhjR9.cjs} +1 -1
- package/dist/{index-BR_I57SD.cjs → index-CJ6SmSwD.cjs} +4 -7
- package/dist/{index-g9LnjUjQ.js → index-CP2bxOlA.js} +3 -6
- package/dist/{index-DGEC4kLx.cjs → index-CgUaX5S2.cjs} +6 -6
- package/dist/index-DVrgVgp0.cjs +50 -0
- package/dist/{index-DI0bldib.js → index-Fy0gnibp.js} +1 -1
- package/dist/{index-CzJGpXul.js → index-Vj-gQHwJ.js} +5 -5
- package/dist/index-YQoO_Soq.js +41 -0
- package/dist/{index-bOsUG-kI.js → index-ldATebdp.js} +6 -6
- package/dist/index.cjs +6 -6
- package/dist/index.js +5 -5
- package/dist/{isAddress-7c5mkwjj.cjs → isAddress-DYM3mZP7.cjs} +1 -1
- package/dist/{isAddress-DyEmEF7O.js → isAddress-ZNhN82OL.js} +1 -1
- package/dist/{property-C7opy5D6.cjs → property-Df-2NJoK.cjs} +1 -1
- package/dist/{property-BohUfInC.js → property-Dsz7qIbu.js} +1 -1
- package/dist/shared/tailwind-element/index.cjs +1 -1
- package/dist/shared/tailwind-element/index.js +1 -1
- package/dist/{state-De1ciDiG.cjs → state-B2ymS5qU.cjs} +1 -1
- package/dist/{state-DJBtqv5G.js → state-CcocMKSJ.js} +1 -1
- package/dist/{style-map-BXvyZNeB.js → style-map-A9HLuIu5.js} +1 -1
- package/dist/{style-map-BjMzqR4L.cjs → style-map-CZiyZP71.cjs} +1 -1
- package/dist/styles/main.css +39 -0
- package/dist/styles/main.css.map +1 -1
- package/dist/{unsafe-html-Bs5tNJGm.js → unsafe-html-BTdQd0Aj.js} +1 -1
- package/dist/{unsafe-html-D9Z4CuNa.cjs → unsafe-html-CWvuBYgI.cjs} +1 -1
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/warning-2-97Si6IrM.cjs +7 -0
- package/dist/warning-2-BSM6W1Gm.js +3 -0
- package/dist/warning-2-BixGOAk9.js +3 -0
- package/dist/warning-2-BvOu0gS4.js +3 -0
- package/dist/warning-2-CZN1r-ol.cjs +7 -0
- package/dist/warning-2-CtlOo41U.js +3 -0
- package/dist/warning-2-Cv13JPGf.cjs +7 -0
- package/dist/warning-2-D2EzJ9aB.js +3 -0
- package/dist/warning-2-DMkszxQr.cjs +7 -0
- package/dist/warning-2-DdO_RDHZ.cjs +7 -0
- package/dist/warning-2-Dq9nReo1.cjs +7 -0
- package/dist/warning-2-DqcrFtKk.js +3 -0
- package/package.json +1 -1
- package/tools/__tests__/accessibility-checker.spec.d.ts +2 -0
- package/tools/__tests__/accessibility-checker.spec.d.ts.map +1 -0
- package/tools/accessibility-checker.d.ts +36 -0
- package/tools/accessibility-checker.d.ts.map +1 -0
- package/tools/index.cjs +32697 -1
- package/tools/index.d.ts +1 -0
- package/tools/index.d.ts.map +1 -1
- package/tools/index.js +32695 -2
- package/tools/sass/typography.scss +55 -1
- package/tools/styles/main.css +39 -0
- package/LICENSE +0 -21
- package/README.md +0 -146
- package/dist/cn-CNdKneQ1.cjs +0 -2578
- package/dist/cn-Cu9aP49j.js +0 -2575
- package/dist/index-Da-0kQ05.cjs +0 -50
- package/dist/index-wzuGnJOC.js +0 -41
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
|
|
5
|
-
const shared_tailwindElement_index = require('../../index-
|
|
6
|
-
const property = require('../../property-
|
|
7
|
-
const state = require('../../state-
|
|
5
|
+
const shared_tailwindElement_index = require('../../index-DVrgVgp0.cjs');
|
|
6
|
+
const property = require('../../property-Df-2NJoK.cjs');
|
|
7
|
+
const state = require('../../state-B2ymS5qU.cjs');
|
|
8
8
|
const query = require('../../query-EFiHeHdi.cjs');
|
|
9
9
|
const index = require('../../index-CaJky2qL.cjs');
|
|
10
10
|
require('../../tailwind-config.cjs');
|
|
11
|
-
const
|
|
11
|
+
const axe = require('../../axe-C-H1UVi1.cjs');
|
|
12
12
|
require('../lukso-textarea/index.cjs');
|
|
13
13
|
require('../lukso-markdown/index.cjs');
|
|
14
14
|
require('../lukso-switch/index.cjs');
|
|
@@ -19,6 +19,178 @@ require('../lukso-dropdown/index.cjs');
|
|
|
19
19
|
require('../lukso-dropdown-option/index.cjs');
|
|
20
20
|
require('../lukso-tooltip/index.cjs');
|
|
21
21
|
|
|
22
|
+
function mapAxeViolationToAccessibilityViolation(axeViolation) {
|
|
23
|
+
return {
|
|
24
|
+
id: axeViolation.id,
|
|
25
|
+
impact: axeViolation.impact,
|
|
26
|
+
description: axeViolation.description,
|
|
27
|
+
help: axeViolation.help,
|
|
28
|
+
helpUrl: axeViolation.helpUrl,
|
|
29
|
+
nodes: axeViolation.nodes?.map((node) => ({
|
|
30
|
+
html: node.html,
|
|
31
|
+
target: node.target,
|
|
32
|
+
failureSummary: node.failureSummary
|
|
33
|
+
})) || []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function checkAccessibility(element) {
|
|
37
|
+
if (!element) {
|
|
38
|
+
return {
|
|
39
|
+
violations: [],
|
|
40
|
+
hasViolations: false,
|
|
41
|
+
violationCount: 0
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const results = await axe.axe.run(element, {
|
|
46
|
+
resultTypes: ["violations"],
|
|
47
|
+
tags: ["wcag2a", "wcag2aa", "wcag21aa", "best-practice"],
|
|
48
|
+
rules: {
|
|
49
|
+
"color-contrast": { enabled: true },
|
|
50
|
+
"color-contrast-enhanced": { enabled: true },
|
|
51
|
+
"image-alt": { enabled: true },
|
|
52
|
+
"button-name": { enabled: true },
|
|
53
|
+
"link-name": { enabled: true },
|
|
54
|
+
label: { enabled: true },
|
|
55
|
+
"form-field-multiple-labels": { enabled: true }
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const violations = results.violations.map(
|
|
59
|
+
mapAxeViolationToAccessibilityViolation
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
violations,
|
|
63
|
+
hasViolations: violations.length > 0,
|
|
64
|
+
violationCount: violations.length
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.warn("Accessibility checking failed:", error);
|
|
68
|
+
return {
|
|
69
|
+
violations: [],
|
|
70
|
+
hasViolations: false,
|
|
71
|
+
violationCount: 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const getViolationSummary = (violations) => {
|
|
76
|
+
const summary = {
|
|
77
|
+
critical: 0,
|
|
78
|
+
serious: 0,
|
|
79
|
+
moderate: 0,
|
|
80
|
+
minor: 0,
|
|
81
|
+
total: violations.length
|
|
82
|
+
};
|
|
83
|
+
violations.forEach((violation) => {
|
|
84
|
+
if (violation.impact) {
|
|
85
|
+
summary[violation.impact]++;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return summary;
|
|
89
|
+
};
|
|
90
|
+
function getImpactStyling(impact) {
|
|
91
|
+
switch (impact) {
|
|
92
|
+
case "critical":
|
|
93
|
+
return {
|
|
94
|
+
colorClass: "text-red-600",
|
|
95
|
+
borderClass: "border-red-600",
|
|
96
|
+
label: "Critical"
|
|
97
|
+
};
|
|
98
|
+
case "serious":
|
|
99
|
+
return {
|
|
100
|
+
colorClass: "text-orange-600",
|
|
101
|
+
borderClass: "border-orange-600",
|
|
102
|
+
label: "Serious"
|
|
103
|
+
};
|
|
104
|
+
case "moderate":
|
|
105
|
+
return {
|
|
106
|
+
colorClass: "text-yellow-600",
|
|
107
|
+
borderClass: "border-yellow-600",
|
|
108
|
+
label: "Moderate"
|
|
109
|
+
};
|
|
110
|
+
case "minor":
|
|
111
|
+
return {
|
|
112
|
+
colorClass: "text-green-600",
|
|
113
|
+
borderClass: "border-green-600",
|
|
114
|
+
label: "Minor"
|
|
115
|
+
};
|
|
116
|
+
default:
|
|
117
|
+
return {
|
|
118
|
+
colorClass: "text-gray-500",
|
|
119
|
+
borderClass: "border-gray-500",
|
|
120
|
+
label: "Info"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const formatViolationsForTooltip = (violations) => {
|
|
125
|
+
if (violations.length === 0) {
|
|
126
|
+
return `
|
|
127
|
+
<div class="text-center text-green-600 font-semibold">
|
|
128
|
+
No accessibility violations found!
|
|
129
|
+
</div>
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
const summary = getViolationSummary(violations);
|
|
133
|
+
let tooltip = `
|
|
134
|
+
<div class="max-w-sm leading-relaxed p-1">
|
|
135
|
+
<div class="font-bold text-sm mb-1">
|
|
136
|
+
Accessibility Issues Found
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
tooltip += `
|
|
140
|
+
<div class="mb-2">
|
|
141
|
+
<div class="flex gap-3 flex-wrap">
|
|
142
|
+
`;
|
|
143
|
+
if (summary.critical > 0) {
|
|
144
|
+
tooltip += `<span class="text-red-600">🚨 ${summary.critical} Critical</span>`;
|
|
145
|
+
}
|
|
146
|
+
if (summary.serious > 0) {
|
|
147
|
+
tooltip += `<span class="text-orange-600">⚠️ ${summary.serious} Serious</span>`;
|
|
148
|
+
}
|
|
149
|
+
if (summary.moderate > 0) {
|
|
150
|
+
tooltip += `<span class="text-yellow-600">⚡ ${summary.moderate} Moderate</span>`;
|
|
151
|
+
}
|
|
152
|
+
if (summary.minor > 0) {
|
|
153
|
+
tooltip += `<span class="text-green-600">💡 ${summary.minor} Minor</span>`;
|
|
154
|
+
}
|
|
155
|
+
tooltip += `
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
`;
|
|
159
|
+
const sortedViolations = violations.sort((a, b) => {
|
|
160
|
+
const impactOrder = { critical: 0, serious: 1, moderate: 2, minor: 3 };
|
|
161
|
+
const aOrder = impactOrder[a.impact] ?? 4;
|
|
162
|
+
const bOrder = impactOrder[b.impact] ?? 4;
|
|
163
|
+
return aOrder - bOrder;
|
|
164
|
+
});
|
|
165
|
+
tooltip += `<div class="space-y-3">`;
|
|
166
|
+
sortedViolations.forEach((violation) => {
|
|
167
|
+
const impactStyling = getImpactStyling(violation.impact);
|
|
168
|
+
tooltip += `
|
|
169
|
+
<div class="p-2 bg-white border-l-4 ${impactStyling.borderClass} rounded">
|
|
170
|
+
<div class="flex items-center gap-1.5 mb-1.5">
|
|
171
|
+
<strong class="text-gray-800 text-xs leading-tight">${violation.help}</strong>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div class="flex items-center gap-1.5 mb-1">
|
|
175
|
+
<span class="text-xs ${impactStyling.colorClass} font-semibold">
|
|
176
|
+
${impactStyling.label}
|
|
177
|
+
</span>
|
|
178
|
+
<span class="text-xs text-gray-400">•</span>
|
|
179
|
+
<span class="text-xs text-gray-500">
|
|
180
|
+
${violation.nodes?.length || 0} element${(violation.nodes?.length || 0) !== 1 ? "s" : ""} affected
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
`;
|
|
185
|
+
});
|
|
186
|
+
tooltip += `
|
|
187
|
+
</div>
|
|
188
|
+
<div class="text-10 text-gray-500 mt-2">Check out the <a target="_blank" class="underline" href="https://www.w3.org/TR/WCAG21/">WCAG</a> guidelines why this is important.</div>
|
|
189
|
+
</div>
|
|
190
|
+
`;
|
|
191
|
+
return tooltip.trim();
|
|
192
|
+
};
|
|
193
|
+
|
|
22
194
|
const style = ":host {\n display: flex\n}";
|
|
23
195
|
|
|
24
196
|
var __defProp = Object.defineProperty;
|
|
@@ -31,6 +203,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
31
203
|
if (kind && result) __defProp(target, key, result);
|
|
32
204
|
return result;
|
|
33
205
|
};
|
|
206
|
+
const DEFAULT_PREVIEW_BACKGROUND_COLOR = "#f9f9f9";
|
|
34
207
|
exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindElement_index.TailwindStyledElement(style) {
|
|
35
208
|
constructor() {
|
|
36
209
|
super(...arguments);
|
|
@@ -48,13 +221,16 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
48
221
|
this.isPreview = false;
|
|
49
222
|
this.rows = 6;
|
|
50
223
|
this.placeholder = "";
|
|
224
|
+
this.previewBackgroundColor = DEFAULT_PREVIEW_BACKGROUND_COLOR;
|
|
51
225
|
this.savedSelectionForPreview = null;
|
|
52
226
|
this.isHeadingDropdownOpen = false;
|
|
53
227
|
this.isColorDropdownOpen = false;
|
|
54
228
|
this.isListDropdownOpen = false;
|
|
229
|
+
this.isAlignmentDropdownOpen = false;
|
|
55
230
|
this.headingTriggerId = "heading-dropdown-trigger";
|
|
56
231
|
this.colorTriggerId = "color-dropdown-trigger";
|
|
57
232
|
this.listTriggerId = "list-dropdown-trigger";
|
|
233
|
+
this.alignmentTriggerId = "alignment-dropdown-trigger";
|
|
58
234
|
this.currentSelection = { start: 0, end: 0 };
|
|
59
235
|
this.savedSelection = null;
|
|
60
236
|
this.defaultColor = "#374151";
|
|
@@ -65,10 +241,12 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
65
241
|
h1: false,
|
|
66
242
|
h2: false,
|
|
67
243
|
h3: false,
|
|
244
|
+
h4: false,
|
|
68
245
|
color: false,
|
|
69
246
|
activeColor: this.defaultColor,
|
|
70
247
|
unorderedList: false,
|
|
71
|
-
orderedList: false
|
|
248
|
+
orderedList: false,
|
|
249
|
+
alignment: "left"
|
|
72
250
|
};
|
|
73
251
|
// Undo/Redo state
|
|
74
252
|
this.undoStack = [];
|
|
@@ -78,6 +256,10 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
78
256
|
this.undoTimeout = null;
|
|
79
257
|
this.MAX_UNDO_STACK_SIZE = 100;
|
|
80
258
|
this.UNDO_SAVE_DELAY = 500;
|
|
259
|
+
this.accessibilityViolations = [];
|
|
260
|
+
this.hasAccessibilityViolations = false;
|
|
261
|
+
this.accessibilityCheckTimeout = null;
|
|
262
|
+
this.ACCESSIBILITY_CHECK_DELAY = 1e3;
|
|
81
263
|
this.handleOutsideClick = (event) => {
|
|
82
264
|
const target = event.target;
|
|
83
265
|
const isInsideThisComponent = this.contains(target) || this.shadowRoot?.contains(target);
|
|
@@ -91,14 +273,19 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
91
273
|
if (this.isListDropdownOpen) {
|
|
92
274
|
this.isListDropdownOpen = false;
|
|
93
275
|
}
|
|
276
|
+
if (this.isAlignmentDropdownOpen) {
|
|
277
|
+
this.isAlignmentDropdownOpen = false;
|
|
278
|
+
}
|
|
94
279
|
return;
|
|
95
280
|
}
|
|
96
281
|
const isInsideHeadingDropdown = this.shadowRoot?.getElementById("headingDropdown")?.contains(target);
|
|
97
282
|
const isInsideColorDropdown = this.shadowRoot?.getElementById("colorDropdown")?.contains(target);
|
|
98
283
|
const isInsideListDropdown = this.shadowRoot?.getElementById("listDropdown")?.contains(target);
|
|
284
|
+
const isInsideAlignmentDropdown = this.shadowRoot?.getElementById("alignmentDropdown")?.contains(target);
|
|
99
285
|
const isHeadingTrigger = this.shadowRoot?.getElementById(this.headingTriggerId)?.contains(target);
|
|
100
286
|
const isColorTrigger = this.shadowRoot?.getElementById(this.colorTriggerId)?.contains(target);
|
|
101
287
|
const isListTrigger = this.shadowRoot?.getElementById(this.listTriggerId)?.contains(target);
|
|
288
|
+
const isAlignmentTrigger = this.shadowRoot?.getElementById(this.alignmentTriggerId)?.contains(target);
|
|
102
289
|
if (!isInsideHeadingDropdown && !isHeadingTrigger && this.isHeadingDropdownOpen) {
|
|
103
290
|
this.isHeadingDropdownOpen = false;
|
|
104
291
|
}
|
|
@@ -108,18 +295,22 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
108
295
|
if (!isInsideListDropdown && !isListTrigger && this.isListDropdownOpen) {
|
|
109
296
|
this.isListDropdownOpen = false;
|
|
110
297
|
}
|
|
298
|
+
if (!isInsideAlignmentDropdown && !isAlignmentTrigger && this.isAlignmentDropdownOpen) {
|
|
299
|
+
this.isAlignmentDropdownOpen = false;
|
|
300
|
+
}
|
|
111
301
|
};
|
|
112
302
|
this.styles = index.ce({
|
|
113
303
|
slots: {
|
|
114
304
|
wrapper: "w-[inherit] grid gap-3",
|
|
115
305
|
header: "flex items-center justify-between gap-2 border border-neutral-90 rounded-12 px-3 py-2 bg-neutral-100",
|
|
116
306
|
toolbar: "flex flex-wrap items-center gap-1",
|
|
117
|
-
area: "",
|
|
307
|
+
area: "relative",
|
|
118
308
|
editor: "",
|
|
119
|
-
preview: "p-3",
|
|
309
|
+
preview: "p-3 border border-neutral-90 rounded-12 min-h-[158px]",
|
|
120
310
|
colorMenu: "relative",
|
|
121
311
|
headingMenu: "relative",
|
|
122
312
|
listMenu: "relative",
|
|
313
|
+
alignmentMenu: "relative",
|
|
123
314
|
label: "heading-inter-14-bold text-neutral-20",
|
|
124
315
|
description: "paragraph-inter-12-regular text-neutral-20",
|
|
125
316
|
divider: "w-[1px] h-4 bg-neutral-90"
|
|
@@ -233,7 +424,7 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
233
424
|
this.scheduleUndoStateSave();
|
|
234
425
|
}
|
|
235
426
|
this.value = newValue;
|
|
236
|
-
this.
|
|
427
|
+
this.emitChangeAndRefresh(event);
|
|
237
428
|
};
|
|
238
429
|
/**
|
|
239
430
|
* Textarea keyup handler.
|
|
@@ -284,7 +475,7 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
284
475
|
}
|
|
285
476
|
dispatchChange(event) {
|
|
286
477
|
this.updateComplete.then(() => {
|
|
287
|
-
const changeEvent = new CustomEvent("on-change", {
|
|
478
|
+
const changeEvent = new CustomEvent("on-markdown-change", {
|
|
288
479
|
detail: { value: this.value, event },
|
|
289
480
|
bubbles: false,
|
|
290
481
|
composed: true
|
|
@@ -292,6 +483,68 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
292
483
|
this.dispatchEvent(changeEvent);
|
|
293
484
|
});
|
|
294
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* Check accessibility violations on the live preview content
|
|
488
|
+
*/
|
|
489
|
+
async performAccessibilityCheck() {
|
|
490
|
+
if (!this.value.trim()) {
|
|
491
|
+
this.accessibilityViolations = [];
|
|
492
|
+
this.hasAccessibilityViolations = false;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
if (this.isPreview) {
|
|
497
|
+
const markdownEl = this.shadowRoot?.querySelector("lukso-markdown");
|
|
498
|
+
if (markdownEl) {
|
|
499
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
500
|
+
const sanitizeEl = markdownEl.shadowRoot?.querySelector("lukso-sanitize");
|
|
501
|
+
const proseDiv = sanitizeEl?.shadowRoot?.querySelector(
|
|
502
|
+
"div.prose"
|
|
503
|
+
);
|
|
504
|
+
if (proseDiv) {
|
|
505
|
+
const result = await checkAccessibility(proseDiv);
|
|
506
|
+
this.accessibilityViolations = result.violations;
|
|
507
|
+
this.hasAccessibilityViolations = result.hasViolations;
|
|
508
|
+
} else {
|
|
509
|
+
this.accessibilityViolations = [];
|
|
510
|
+
this.hasAccessibilityViolations = false;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
this.accessibilityViolations = [];
|
|
515
|
+
this.hasAccessibilityViolations = false;
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.warn("Accessibility checking failed:", error);
|
|
519
|
+
this.accessibilityViolations = [];
|
|
520
|
+
this.hasAccessibilityViolations = false;
|
|
521
|
+
}
|
|
522
|
+
this.requestUpdate();
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Schedule accessibility check with debouncing
|
|
526
|
+
*/
|
|
527
|
+
scheduleAccessibilityCheck() {
|
|
528
|
+
if (this.accessibilityCheckTimeout) {
|
|
529
|
+
clearTimeout(this.accessibilityCheckTimeout);
|
|
530
|
+
}
|
|
531
|
+
this.accessibilityCheckTimeout = window.setTimeout(() => {
|
|
532
|
+
this.performAccessibilityCheck();
|
|
533
|
+
this.accessibilityCheckTimeout = null;
|
|
534
|
+
}, this.ACCESSIBILITY_CHECK_DELAY);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Unified helper that ensures both active format state and change events are properly
|
|
538
|
+
* emitted after any value mutation. This replaces the scattered updateActiveFormats()
|
|
539
|
+
* and dispatchChange() calls throughout the codebase.
|
|
540
|
+
*
|
|
541
|
+
* @param event - Optional event that triggered the change
|
|
542
|
+
*/
|
|
543
|
+
emitChangeAndRefresh(event) {
|
|
544
|
+
this.updateActiveFormats();
|
|
545
|
+
this.dispatchChange(event);
|
|
546
|
+
this.scheduleAccessibilityCheck();
|
|
547
|
+
}
|
|
295
548
|
/**
|
|
296
549
|
* Utility to perform an operation with the current textarea selection.
|
|
297
550
|
*
|
|
@@ -332,7 +585,7 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
332
585
|
/**
|
|
333
586
|
* Apply or toggle heading formatting for the current line(s).
|
|
334
587
|
*
|
|
335
|
-
* @param level - 0 to remove heading, 1-
|
|
588
|
+
* @param level - 0 to remove heading, 1-4 for heading levels
|
|
336
589
|
*/
|
|
337
590
|
applyHeading(level) {
|
|
338
591
|
if (this.isReadonly || this.isDisabled) return;
|
|
@@ -378,9 +631,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
378
631
|
}
|
|
379
632
|
requestAnimationFrame(() => {
|
|
380
633
|
textarea.setSelectionRange(cursorPosition, cursorPosition);
|
|
381
|
-
this.
|
|
634
|
+
this.emitChangeAndRefresh();
|
|
382
635
|
});
|
|
383
|
-
this.dispatchChange();
|
|
384
636
|
});
|
|
385
637
|
}
|
|
386
638
|
/**
|
|
@@ -510,9 +762,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
510
762
|
const cursorPosition = before.length + (firstLineEnd === -1 ? transformed.length : firstLineEnd);
|
|
511
763
|
requestAnimationFrame(() => {
|
|
512
764
|
textarea.setSelectionRange(cursorPosition, cursorPosition);
|
|
513
|
-
this.
|
|
765
|
+
this.emitChangeAndRefresh();
|
|
514
766
|
});
|
|
515
|
-
this.dispatchChange();
|
|
516
767
|
});
|
|
517
768
|
}
|
|
518
769
|
/**
|
|
@@ -523,6 +774,155 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
523
774
|
if (this.activeFormats.orderedList) return "ordered";
|
|
524
775
|
return "none";
|
|
525
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Get the current alignment icon name based on activeFormats.
|
|
779
|
+
*/
|
|
780
|
+
getAlignmentIcon() {
|
|
781
|
+
switch (this.activeFormats.alignment) {
|
|
782
|
+
case "center":
|
|
783
|
+
return "textalign-center";
|
|
784
|
+
case "right":
|
|
785
|
+
return "textalign-right";
|
|
786
|
+
default:
|
|
787
|
+
return "textalign-left";
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Apply or toggle text alignment for the current line(s).
|
|
792
|
+
*
|
|
793
|
+
* @param alignment - 'left', 'center', or 'right'
|
|
794
|
+
*/
|
|
795
|
+
applyAlignment(alignment) {
|
|
796
|
+
if (this.isReadonly || this.isDisabled) return;
|
|
797
|
+
this.saveUndoStateBeforeChange();
|
|
798
|
+
this.withSelection((textarea, start, end, value) => {
|
|
799
|
+
const lineStart = value.lastIndexOf("\n", start - 1) + 1;
|
|
800
|
+
let lineEnd = value.indexOf("\n", end);
|
|
801
|
+
if (lineEnd === -1) lineEnd = value.length;
|
|
802
|
+
const before = value.slice(0, lineStart);
|
|
803
|
+
const selected = value.slice(lineStart, lineEnd);
|
|
804
|
+
const after = value.slice(lineEnd);
|
|
805
|
+
let transformed;
|
|
806
|
+
const currentAlignmentRegex = /<div style="text-align: (left|center|right);">(.*?)<\/div>/s;
|
|
807
|
+
const existingMatch = selected.match(currentAlignmentRegex);
|
|
808
|
+
if (existingMatch || this.hasNestedAlignment(selected)) {
|
|
809
|
+
if (existingMatch) {
|
|
810
|
+
const existingAlignment = existingMatch[1];
|
|
811
|
+
const innerContent = existingMatch[2];
|
|
812
|
+
if (existingAlignment === alignment) {
|
|
813
|
+
transformed = innerContent;
|
|
814
|
+
} else {
|
|
815
|
+
transformed = `<div style="text-align: ${alignment};">${innerContent}</div>`;
|
|
816
|
+
}
|
|
817
|
+
} else {
|
|
818
|
+
if (this.getNestedAlignment(selected) === alignment) {
|
|
819
|
+
transformed = this.removeNestedAlignment(selected);
|
|
820
|
+
} else {
|
|
821
|
+
transformed = this.replaceNestedAlignment(selected, alignment);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
} else {
|
|
825
|
+
if (alignment === "left") {
|
|
826
|
+
transformed = selected;
|
|
827
|
+
} else {
|
|
828
|
+
transformed = this.wrapContentWithAlignment(selected, alignment);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
this.value = before + transformed + after;
|
|
832
|
+
textarea.value = before + transformed + after;
|
|
833
|
+
const cursorPosition = before.length + transformed.length;
|
|
834
|
+
requestAnimationFrame(() => {
|
|
835
|
+
textarea.setSelectionRange(cursorPosition, cursorPosition);
|
|
836
|
+
this.emitChangeAndRefresh();
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Check if content has nested alignment divs inside formatting markers.
|
|
842
|
+
*/
|
|
843
|
+
hasNestedAlignment(content) {
|
|
844
|
+
const alignmentRegex = /<div style="text-align: (left|center|right);">/;
|
|
845
|
+
return alignmentRegex.test(content);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Get the alignment from nested alignment divs.
|
|
849
|
+
*/
|
|
850
|
+
getNestedAlignment(content) {
|
|
851
|
+
const alignmentMatch = content.match(
|
|
852
|
+
/<div style="text-align: (left|center|right);">/
|
|
853
|
+
);
|
|
854
|
+
return alignmentMatch ? alignmentMatch[1] : null;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Remove nested alignment divs from content.
|
|
858
|
+
*/
|
|
859
|
+
removeNestedAlignment(content) {
|
|
860
|
+
return content.replace(
|
|
861
|
+
/<div style="text-align: (left|center|right);">([^<]*?)<\/div>/g,
|
|
862
|
+
"$2"
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Replace nested alignment with a new alignment.
|
|
867
|
+
*/
|
|
868
|
+
replaceNestedAlignment(content, newAlignment) {
|
|
869
|
+
return content.replace(
|
|
870
|
+
/<div style="text-align: (left|center|right);">([^<]*?)<\/div>/g,
|
|
871
|
+
`<div style="text-align: ${newAlignment};">$2</div>`
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Wrap content with alignment div, ensuring proper nesting inside formatting markers.
|
|
876
|
+
* Examples:
|
|
877
|
+
* - **text** becomes **<div style="text-align: center;">text</div>**
|
|
878
|
+
* - *text* becomes *<div style="text-align: center;">text</div>*
|
|
879
|
+
* - # text becomes # <div style="text-align: center;">text</div>
|
|
880
|
+
*
|
|
881
|
+
* @param content - the content to wrap
|
|
882
|
+
* @param alignment - 'left', 'center' or 'right'
|
|
883
|
+
*/
|
|
884
|
+
wrapContentWithAlignment(content, alignment) {
|
|
885
|
+
const alignmentDiv = (innerContent) => `<div style="text-align: ${alignment};">${innerContent}</div>`;
|
|
886
|
+
const headingMatch = content.match(/^(#{1,6}\s+)(.*)$/);
|
|
887
|
+
if (headingMatch) {
|
|
888
|
+
const headingPrefix = headingMatch[1];
|
|
889
|
+
const headingText = headingMatch[2];
|
|
890
|
+
return headingPrefix + alignmentDiv(headingText);
|
|
891
|
+
}
|
|
892
|
+
const boldMatch = content.match(/^(\*\*)(.+?)(\*\*)$/s);
|
|
893
|
+
if (boldMatch) {
|
|
894
|
+
const innerText = boldMatch[2];
|
|
895
|
+
return `**${alignmentDiv(innerText)}**`;
|
|
896
|
+
}
|
|
897
|
+
const italicMatch = content.match(/^(\*)(.+?)(\*)$/s);
|
|
898
|
+
if (italicMatch) {
|
|
899
|
+
const innerText = italicMatch[2];
|
|
900
|
+
return `*${alignmentDiv(innerText)}*`;
|
|
901
|
+
}
|
|
902
|
+
const linkMatch = content.match(/^(\[)(.+?)(\]\([^)]+\))$/s);
|
|
903
|
+
if (linkMatch) {
|
|
904
|
+
const linkStart = linkMatch[1];
|
|
905
|
+
const linkText = linkMatch[2];
|
|
906
|
+
const linkEnd = linkMatch[3];
|
|
907
|
+
return linkStart + alignmentDiv(linkText) + linkEnd;
|
|
908
|
+
}
|
|
909
|
+
const colorMatch = content.match(
|
|
910
|
+
/^(<span style="color: [^"]+;">)(.+?)(<\/span>)$/s
|
|
911
|
+
);
|
|
912
|
+
if (colorMatch) {
|
|
913
|
+
const colorStart = colorMatch[1];
|
|
914
|
+
const colorText = colorMatch[2];
|
|
915
|
+
const colorEnd = colorMatch[3];
|
|
916
|
+
return colorStart + alignmentDiv(colorText) + colorEnd;
|
|
917
|
+
}
|
|
918
|
+
const listMatch = content.match(/^(\s*(?:[-*+]|\d+\.)\s+)(.*)$/);
|
|
919
|
+
if (listMatch) {
|
|
920
|
+
const listPrefix = listMatch[1];
|
|
921
|
+
const listText = listMatch[2];
|
|
922
|
+
return listPrefix + alignmentDiv(listText);
|
|
923
|
+
}
|
|
924
|
+
return alignmentDiv(content);
|
|
925
|
+
}
|
|
526
926
|
/**
|
|
527
927
|
* Toggle inline formatting by wrapping/unwrapping selection or current word.
|
|
528
928
|
*
|
|
@@ -549,9 +949,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
549
949
|
const selEnd2 = selStart2 + selected.length;
|
|
550
950
|
requestAnimationFrame(() => {
|
|
551
951
|
textarea.setSelectionRange(selStart2, selEnd2);
|
|
552
|
-
this.
|
|
952
|
+
this.emitChangeAndRefresh();
|
|
553
953
|
});
|
|
554
|
-
this.dispatchChange();
|
|
555
954
|
return;
|
|
556
955
|
}
|
|
557
956
|
const innerWrapped = selected.startsWith(wrapper) && selected.endsWith(wrapper);
|
|
@@ -566,9 +965,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
566
965
|
const selEnd2 = selStart2 + selected.length;
|
|
567
966
|
requestAnimationFrame(() => {
|
|
568
967
|
textarea.setSelectionRange(selStart2, selEnd2);
|
|
569
|
-
this.
|
|
968
|
+
this.emitChangeAndRefresh();
|
|
570
969
|
});
|
|
571
|
-
this.dispatchChange();
|
|
572
970
|
return;
|
|
573
971
|
}
|
|
574
972
|
const wrapped = `${wrapper}${selected || ""}${wrapper}`;
|
|
@@ -578,9 +976,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
578
976
|
const selEnd = selStart + (selected ? selected.length : 0);
|
|
579
977
|
requestAnimationFrame(() => {
|
|
580
978
|
textarea.setSelectionRange(selStart, selEnd);
|
|
581
|
-
this.
|
|
979
|
+
this.emitChangeAndRefresh();
|
|
582
980
|
});
|
|
583
|
-
this.dispatchChange();
|
|
584
981
|
});
|
|
585
982
|
}
|
|
586
983
|
/**
|
|
@@ -593,6 +990,11 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
593
990
|
this.enterPreview();
|
|
594
991
|
}
|
|
595
992
|
this.isPreview = !this.isPreview;
|
|
993
|
+
if (this.isPreview) {
|
|
994
|
+
requestAnimationFrame(() => {
|
|
995
|
+
this.scheduleAccessibilityCheck();
|
|
996
|
+
});
|
|
997
|
+
}
|
|
596
998
|
}
|
|
597
999
|
/**
|
|
598
1000
|
* Enter preview mode - save current state and remove keyboard listeners from textarea
|
|
@@ -651,9 +1053,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
651
1053
|
const newCursor = leftBracket2 + textOnly.length;
|
|
652
1054
|
requestAnimationFrame(() => {
|
|
653
1055
|
textarea.setSelectionRange(newCursor, newCursor);
|
|
654
|
-
this.
|
|
1056
|
+
this.emitChangeAndRefresh();
|
|
655
1057
|
});
|
|
656
|
-
this.dispatchChange();
|
|
657
1058
|
return;
|
|
658
1059
|
}
|
|
659
1060
|
}
|
|
@@ -673,9 +1074,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
673
1074
|
const newEnd = newStart + textOnly.length;
|
|
674
1075
|
requestAnimationFrame(() => {
|
|
675
1076
|
textarea.setSelectionRange(newStart, newEnd);
|
|
676
|
-
this.
|
|
1077
|
+
this.emitChangeAndRefresh();
|
|
677
1078
|
});
|
|
678
|
-
this.dispatchChange();
|
|
679
1079
|
return;
|
|
680
1080
|
}
|
|
681
1081
|
const leftBracket = value.lastIndexOf("[", s);
|
|
@@ -692,9 +1092,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
692
1092
|
const newCursor = leftBracket + textOnly.length;
|
|
693
1093
|
requestAnimationFrame(() => {
|
|
694
1094
|
textarea.setSelectionRange(newCursor, newCursor);
|
|
695
|
-
this.
|
|
1095
|
+
this.emitChangeAndRefresh();
|
|
696
1096
|
});
|
|
697
|
-
this.dispatchChange();
|
|
698
1097
|
return;
|
|
699
1098
|
}
|
|
700
1099
|
}
|
|
@@ -706,9 +1105,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
706
1105
|
requestAnimationFrame(() => {
|
|
707
1106
|
textarea.focus();
|
|
708
1107
|
textarea.setSelectionRange(cursorPosition, cursorPosition);
|
|
709
|
-
this.
|
|
1108
|
+
this.emitChangeAndRefresh();
|
|
710
1109
|
});
|
|
711
|
-
this.dispatchChange();
|
|
712
1110
|
});
|
|
713
1111
|
}
|
|
714
1112
|
/**
|
|
@@ -756,6 +1154,23 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
756
1154
|
);
|
|
757
1155
|
activeColor = beforeColorMatch?.[1] || selectedColorMatch?.[1] || "";
|
|
758
1156
|
}
|
|
1157
|
+
const alignmentRegex = /<div style="text-align: (left|center|right);">/;
|
|
1158
|
+
let alignment = "left";
|
|
1159
|
+
const alignmentMatch = currentLine.match(alignmentRegex);
|
|
1160
|
+
if (alignmentMatch) {
|
|
1161
|
+
alignment = alignmentMatch[1];
|
|
1162
|
+
} else {
|
|
1163
|
+
const beforeCurrentLine = this.value.slice(0, lineStart);
|
|
1164
|
+
const alignmentStartMatch = beforeCurrentLine.match(
|
|
1165
|
+
/.*<div style="text-align: (left|center|right);">[^<]*$/s
|
|
1166
|
+
);
|
|
1167
|
+
if (alignmentStartMatch) {
|
|
1168
|
+
const afterCurrentLine = this.value.slice(lineEnd);
|
|
1169
|
+
if (afterCurrentLine.includes("</div>")) {
|
|
1170
|
+
alignment = alignmentStartMatch[1];
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
759
1174
|
this.activeFormats = {
|
|
760
1175
|
bold: hasBoldWrap,
|
|
761
1176
|
italic: hasItalicWrap,
|
|
@@ -763,10 +1178,12 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
763
1178
|
h1: headingLevel === 1,
|
|
764
1179
|
h2: headingLevel === 2,
|
|
765
1180
|
h3: headingLevel === 3,
|
|
1181
|
+
h4: headingLevel === 4,
|
|
766
1182
|
color: hasColorWrap,
|
|
767
1183
|
activeColor,
|
|
768
1184
|
unorderedList: hasUnorderedList,
|
|
769
|
-
orderedList: hasOrderedList
|
|
1185
|
+
orderedList: hasOrderedList,
|
|
1186
|
+
alignment
|
|
770
1187
|
};
|
|
771
1188
|
}
|
|
772
1189
|
/**
|
|
@@ -884,9 +1301,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
884
1301
|
}
|
|
885
1302
|
requestAnimationFrame(() => {
|
|
886
1303
|
textarea.setSelectionRange(selStart2, selEnd2);
|
|
887
|
-
this.
|
|
1304
|
+
this.emitChangeAndRefresh();
|
|
888
1305
|
});
|
|
889
|
-
this.dispatchChange();
|
|
890
1306
|
return;
|
|
891
1307
|
}
|
|
892
1308
|
const colorRegex = /^<span style="color: ([^"]+)">(.*)<\/span>$/s;
|
|
@@ -905,9 +1321,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
905
1321
|
const selEnd2 = selStart2 + (existingColor === color ? innerText.length : innerText.length);
|
|
906
1322
|
requestAnimationFrame(() => {
|
|
907
1323
|
textarea.setSelectionRange(selStart2, selEnd2);
|
|
908
|
-
this.
|
|
1324
|
+
this.emitChangeAndRefresh();
|
|
909
1325
|
});
|
|
910
|
-
this.dispatchChange();
|
|
911
1326
|
return;
|
|
912
1327
|
}
|
|
913
1328
|
const newColorTagOpen = `<span style="color: ${color}">`;
|
|
@@ -918,9 +1333,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
918
1333
|
const selEnd = selStart + (selected ? selected.length : 4);
|
|
919
1334
|
requestAnimationFrame(() => {
|
|
920
1335
|
textarea.setSelectionRange(selStart, selEnd);
|
|
921
|
-
this.
|
|
1336
|
+
this.emitChangeAndRefresh();
|
|
922
1337
|
});
|
|
923
|
-
this.dispatchChange();
|
|
924
1338
|
});
|
|
925
1339
|
}
|
|
926
1340
|
/**
|
|
@@ -968,54 +1382,58 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
968
1382
|
return;
|
|
969
1383
|
}
|
|
970
1384
|
const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
|
|
971
|
-
const
|
|
972
|
-
let selected = value.slice(s, e);
|
|
973
|
-
const after = value.slice(e);
|
|
974
|
-
const colorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/gs;
|
|
975
|
-
selected = selected.replace(colorRegex, "$2");
|
|
976
|
-
const fullColorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/g;
|
|
1385
|
+
const colorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/g;
|
|
977
1386
|
let match;
|
|
978
|
-
let
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
);
|
|
983
|
-
const searchOffset = Math.max(0, s - 100);
|
|
984
|
-
match = fullColorRegex.exec(searchText);
|
|
1387
|
+
let newValue = value;
|
|
1388
|
+
let foundSpan = false;
|
|
1389
|
+
colorRegex.lastIndex = 0;
|
|
1390
|
+
match = colorRegex.exec(value);
|
|
985
1391
|
while (match !== null) {
|
|
986
|
-
const
|
|
987
|
-
const
|
|
1392
|
+
const fullMatchStart = match.index;
|
|
1393
|
+
const fullMatchEnd = match.index + match[0].length;
|
|
988
1394
|
const spanOpenTag = `<span style="color: ${match[1]}">`;
|
|
989
|
-
const contentStart =
|
|
990
|
-
const contentEnd =
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1395
|
+
const contentStart = fullMatchStart + spanOpenTag.length;
|
|
1396
|
+
const contentEnd = fullMatchEnd - 7;
|
|
1397
|
+
const innerContent = match[2];
|
|
1398
|
+
match = colorRegex.exec(value);
|
|
1399
|
+
const selectionOverlaps = s >= contentStart && s < contentEnd || e > contentStart && e <= contentEnd || s <= contentStart && e >= contentEnd;
|
|
1400
|
+
if (selectionOverlaps) {
|
|
1401
|
+
newValue = value.slice(0, fullMatchStart) + innerContent + value.slice(fullMatchEnd);
|
|
1402
|
+
foundSpan = true;
|
|
1403
|
+
this.value = newValue;
|
|
1404
|
+
textarea.value = newValue;
|
|
1405
|
+
let newStart = s;
|
|
1406
|
+
let newEnd = e;
|
|
1407
|
+
if (s >= fullMatchStart) {
|
|
1408
|
+
const spanOpenTagLength = spanOpenTag.length;
|
|
1409
|
+
if (s >= contentStart) {
|
|
1410
|
+
newStart = s - spanOpenTagLength;
|
|
1411
|
+
} else if (s >= fullMatchStart) {
|
|
1412
|
+
newStart = fullMatchStart;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
if (e >= fullMatchStart) {
|
|
1416
|
+
const spanOpenTagLength = spanOpenTag.length;
|
|
1417
|
+
if (e <= contentEnd) {
|
|
1418
|
+
newEnd = e - spanOpenTagLength;
|
|
1419
|
+
} else {
|
|
1420
|
+
newEnd = e - spanOpenTagLength - 7;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
999
1423
|
requestAnimationFrame(() => {
|
|
1000
|
-
textarea.setSelectionRange(
|
|
1001
|
-
|
|
1424
|
+
textarea.setSelectionRange(
|
|
1425
|
+
Math.max(0, newStart),
|
|
1426
|
+
Math.max(0, newEnd)
|
|
1427
|
+
);
|
|
1428
|
+
this.emitChangeAndRefresh();
|
|
1002
1429
|
});
|
|
1003
|
-
|
|
1004
|
-
foundMatch = true;
|
|
1005
|
-
break;
|
|
1430
|
+
return;
|
|
1006
1431
|
}
|
|
1007
|
-
match = fullColorRegex.exec(searchText);
|
|
1008
1432
|
}
|
|
1009
|
-
if (!
|
|
1010
|
-
this.value = before + selected + after;
|
|
1011
|
-
textarea.value = before + selected + after;
|
|
1012
|
-
const selStart = before.length;
|
|
1013
|
-
const selEnd = selStart + selected.length;
|
|
1433
|
+
if (!foundSpan) {
|
|
1014
1434
|
requestAnimationFrame(() => {
|
|
1015
|
-
textarea.setSelectionRange(selStart, selEnd);
|
|
1016
1435
|
this.updateActiveFormats();
|
|
1017
1436
|
});
|
|
1018
|
-
this.dispatchChange();
|
|
1019
1437
|
}
|
|
1020
1438
|
});
|
|
1021
1439
|
}
|
|
@@ -1026,6 +1444,7 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
1026
1444
|
if (this.activeFormats.h1) return 1;
|
|
1027
1445
|
if (this.activeFormats.h2) return 2;
|
|
1028
1446
|
if (this.activeFormats.h3) return 3;
|
|
1447
|
+
if (this.activeFormats.h4) return 4;
|
|
1029
1448
|
return 0;
|
|
1030
1449
|
}
|
|
1031
1450
|
/**
|
|
@@ -1093,10 +1512,14 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
1093
1512
|
const textarea = this.textareaEl?.shadowRoot?.querySelector(
|
|
1094
1513
|
"textarea"
|
|
1095
1514
|
);
|
|
1096
|
-
if (!textarea)
|
|
1515
|
+
if (!textarea) {
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1097
1518
|
const start = textarea.selectionStart ?? 0;
|
|
1098
1519
|
const end = textarea.selectionEnd ?? 0;
|
|
1099
|
-
if (start !== end)
|
|
1520
|
+
if (start !== end) {
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1100
1523
|
const value = this.value;
|
|
1101
1524
|
const lineStart = value.lastIndexOf("\n", start - 1) + 1;
|
|
1102
1525
|
let lineEnd = value.indexOf("\n", start);
|
|
@@ -1121,9 +1544,8 @@ exports.LuksoMarkdownEditor = class LuksoMarkdownEditor extends shared_tailwindE
|
|
|
1121
1544
|
const newCursor2 = before2.length;
|
|
1122
1545
|
requestAnimationFrame(() => {
|
|
1123
1546
|
textarea.setSelectionRange(newCursor2, newCursor2);
|
|
1124
|
-
this.
|
|
1547
|
+
this.emitChangeAndRefresh();
|
|
1125
1548
|
});
|
|
1126
|
-
this.dispatchChange();
|
|
1127
1549
|
return true;
|
|
1128
1550
|
}
|
|
1129
1551
|
const before = value.slice(0, start);
|
|
@@ -1134,11 +1556,8 @@ ${indent}${marker} `;
|
|
|
1134
1556
|
this.value = newValue;
|
|
1135
1557
|
textarea.value = newValue;
|
|
1136
1558
|
const newCursor = start + prefix.length;
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
this.updateActiveFormats();
|
|
1140
|
-
});
|
|
1141
|
-
this.dispatchChange();
|
|
1559
|
+
textarea.setSelectionRange(newCursor, newCursor);
|
|
1560
|
+
this.emitChangeAndRefresh();
|
|
1142
1561
|
return true;
|
|
1143
1562
|
}
|
|
1144
1563
|
if (orderedMatch) {
|
|
@@ -1154,30 +1573,39 @@ ${indent}${marker} `;
|
|
|
1154
1573
|
const newCursor2 = before2.length;
|
|
1155
1574
|
requestAnimationFrame(() => {
|
|
1156
1575
|
textarea.setSelectionRange(newCursor2, newCursor2);
|
|
1157
|
-
this.
|
|
1576
|
+
this.emitChangeAndRefresh();
|
|
1158
1577
|
});
|
|
1159
|
-
this.dispatchChange();
|
|
1160
1578
|
return true;
|
|
1161
1579
|
}
|
|
1162
|
-
const
|
|
1580
|
+
const currentNumber = parseInt(numberStr, 10);
|
|
1581
|
+
const nextNumber = currentNumber + 1;
|
|
1163
1582
|
const before = value.slice(0, start);
|
|
1164
1583
|
const after = value.slice(start);
|
|
1165
1584
|
const prefix = `
|
|
1166
1585
|
${indent}${nextNumber}. `;
|
|
1167
|
-
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
)
|
|
1173
|
-
|
|
1174
|
-
|
|
1586
|
+
let newValue = before + prefix + after;
|
|
1587
|
+
const lines = newValue.split("\n");
|
|
1588
|
+
const insertLineIndex = before.split("\n").length;
|
|
1589
|
+
const renumberStartIndex = insertLineIndex + 1;
|
|
1590
|
+
let currentNum = nextNumber + 1;
|
|
1591
|
+
const orderedRegex = /^(\s*)(\d+)\.\s+(.*)$/;
|
|
1592
|
+
for (let i = renumberStartIndex; i < lines.length; i++) {
|
|
1593
|
+
const line = lines[i];
|
|
1594
|
+
const match = line.match(orderedRegex);
|
|
1595
|
+
if (match && match[1] === indent) {
|
|
1596
|
+
const content2 = match[3];
|
|
1597
|
+
lines[i] = `${indent}${currentNum}. ${content2}`;
|
|
1598
|
+
currentNum++;
|
|
1599
|
+
} else if (match && match[1].length < indent.length) {
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
newValue = lines.join("\n");
|
|
1604
|
+
this.value = newValue;
|
|
1605
|
+
textarea.value = newValue;
|
|
1175
1606
|
const newCursor = start + prefix.length;
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
this.updateActiveFormats();
|
|
1179
|
-
});
|
|
1180
|
-
this.dispatchChange();
|
|
1607
|
+
textarea.setSelectionRange(newCursor, newCursor);
|
|
1608
|
+
this.emitChangeAndRefresh();
|
|
1181
1609
|
return true;
|
|
1182
1610
|
}
|
|
1183
1611
|
return false;
|
|
@@ -1190,10 +1618,14 @@ ${indent}${nextNumber}. `;
|
|
|
1190
1618
|
const textarea = this.textareaEl?.shadowRoot?.querySelector(
|
|
1191
1619
|
"textarea"
|
|
1192
1620
|
);
|
|
1193
|
-
if (!textarea)
|
|
1621
|
+
if (!textarea) {
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1194
1624
|
const start = textarea.selectionStart ?? 0;
|
|
1195
1625
|
const end = textarea.selectionEnd ?? 0;
|
|
1196
|
-
if (start !== end)
|
|
1626
|
+
if (start !== end) {
|
|
1627
|
+
return false;
|
|
1628
|
+
}
|
|
1197
1629
|
const value = this.value;
|
|
1198
1630
|
const lineStart = value.lastIndexOf("\n", start - 1) + 1;
|
|
1199
1631
|
let lineEnd = value.indexOf("\n", start);
|
|
@@ -1219,21 +1651,85 @@ ${indent}${nextNumber}. `;
|
|
|
1219
1651
|
} else if (orderedMatch) {
|
|
1220
1652
|
const currentIndent = orderedMatch[1] ?? "";
|
|
1221
1653
|
const content = orderedMatch[3] ?? "";
|
|
1222
|
-
|
|
1223
|
-
|
|
1654
|
+
if (content.trim() === "") {
|
|
1655
|
+
newLine = currentLine;
|
|
1656
|
+
const nestedLine = `${currentIndent}${indent}1. `;
|
|
1657
|
+
let newValue2 = before + newLine + "\n" + nestedLine + after;
|
|
1658
|
+
const parentIndent = currentIndent;
|
|
1659
|
+
const lines = newValue2.split("\n");
|
|
1660
|
+
const currentLineIndex = Math.floor(lineStart / (newValue2.indexOf("\n") + 1)) || newValue2.slice(0, lineStart).split("\n").length - 1;
|
|
1661
|
+
const currentLineMatch = lines[currentLineIndex]?.match(
|
|
1662
|
+
/^(\s*)(\d+)\.\s*(.*)$/
|
|
1663
|
+
);
|
|
1664
|
+
const nextExpectedNumber = currentLineMatch ? parseInt(currentLineMatch[2], 10) : 1;
|
|
1665
|
+
const orderedRegex = /^(\s*)(\d+)\.\s*(.*)$/;
|
|
1666
|
+
let currentNumber = nextExpectedNumber;
|
|
1667
|
+
for (let i = currentLineIndex + 2; i < lines.length; i++) {
|
|
1668
|
+
const line = lines[i];
|
|
1669
|
+
const orderedMatch2 = line.match(orderedRegex);
|
|
1670
|
+
if (orderedMatch2 && orderedMatch2[1] === parentIndent) {
|
|
1671
|
+
const content2 = orderedMatch2[3];
|
|
1672
|
+
lines[i] = `${parentIndent}${currentNumber}. ${content2}`;
|
|
1673
|
+
currentNumber++;
|
|
1674
|
+
} else if (orderedMatch2 && orderedMatch2[1].length < parentIndent.length) {
|
|
1675
|
+
break;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
newValue2 = lines.join("\n");
|
|
1679
|
+
const newCursor2 = lineStart + newLine.length + 1 + nestedLine.length;
|
|
1680
|
+
this.value = newValue2;
|
|
1681
|
+
textarea.value = newValue2;
|
|
1682
|
+
requestAnimationFrame(() => {
|
|
1683
|
+
textarea.setSelectionRange(newCursor2, newCursor2);
|
|
1684
|
+
this.emitChangeAndRefresh();
|
|
1685
|
+
});
|
|
1686
|
+
return true;
|
|
1687
|
+
} else {
|
|
1688
|
+
const newIndent = currentIndent + indent;
|
|
1689
|
+
let newNumber = 1;
|
|
1690
|
+
const beforeText = value.slice(0, lineStart);
|
|
1691
|
+
const lines = beforeText.split("\n");
|
|
1692
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1693
|
+
const line = lines[i];
|
|
1694
|
+
const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
|
|
1695
|
+
if (match && match[1] === newIndent) {
|
|
1696
|
+
newNumber = parseInt(match[2], 10) + 1;
|
|
1697
|
+
break;
|
|
1698
|
+
} else if (match && match[1].length < newIndent.length) {
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
newLine = `${newIndent}${newNumber}. ${content}`;
|
|
1703
|
+
newCursorOffset = indent.length;
|
|
1704
|
+
}
|
|
1224
1705
|
} else {
|
|
1225
1706
|
return false;
|
|
1226
1707
|
}
|
|
1227
|
-
|
|
1708
|
+
let newValue = before + newLine + after;
|
|
1709
|
+
if (orderedMatch) {
|
|
1710
|
+
const newIndent = (orderedMatch[1] ?? "") + indent;
|
|
1711
|
+
newValue = this.renumberOrderedListItems(
|
|
1712
|
+
newValue,
|
|
1713
|
+
lineStart + newLine.length,
|
|
1714
|
+
// Start renumbering after the current line
|
|
1715
|
+
newIndent
|
|
1716
|
+
// Use the new indentation level
|
|
1717
|
+
);
|
|
1718
|
+
const parentIndent = orderedMatch[1] ?? "";
|
|
1719
|
+
newValue = this.renumberOrderedListItems(
|
|
1720
|
+
newValue,
|
|
1721
|
+
lineStart,
|
|
1722
|
+
// Start from this line for parent level
|
|
1723
|
+
parentIndent
|
|
1724
|
+
// Use the parent level indentation
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1228
1727
|
this.value = newValue;
|
|
1229
1728
|
textarea.value = newValue;
|
|
1230
1729
|
const cursorInLine = start - lineStart;
|
|
1231
1730
|
const newCursor = lineStart + cursorInLine + newCursorOffset;
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
this.updateActiveFormats();
|
|
1235
|
-
});
|
|
1236
|
-
this.dispatchChange();
|
|
1731
|
+
textarea.setSelectionRange(newCursor, newCursor);
|
|
1732
|
+
this.emitChangeAndRefresh();
|
|
1237
1733
|
return true;
|
|
1238
1734
|
}
|
|
1239
1735
|
/**
|
|
@@ -1312,9 +1808,8 @@ ${indent}${nextNumber}. `;
|
|
|
1312
1808
|
);
|
|
1313
1809
|
requestAnimationFrame(() => {
|
|
1314
1810
|
textarea.setSelectionRange(newCursor, newCursor);
|
|
1315
|
-
this.
|
|
1811
|
+
this.emitChangeAndRefresh();
|
|
1316
1812
|
});
|
|
1317
|
-
this.dispatchChange();
|
|
1318
1813
|
return true;
|
|
1319
1814
|
}
|
|
1320
1815
|
/**
|
|
@@ -1441,32 +1936,52 @@ ${indent}${nextNumber}. `;
|
|
|
1441
1936
|
markerEndPosition = indent.length + numberStr.length + 2;
|
|
1442
1937
|
hasContent = content.trim().length > 0;
|
|
1443
1938
|
}
|
|
1444
|
-
|
|
1939
|
+
const isAtOrAfterMarker = cursorPositionInLine >= markerEndPosition;
|
|
1940
|
+
if (isAtOrAfterMarker && !hasContent) {
|
|
1445
1941
|
this.saveUndoStateBeforeChange();
|
|
1446
1942
|
const before = value.slice(0, lineStart);
|
|
1447
|
-
const after = value.slice(
|
|
1448
|
-
|
|
1449
|
-
)
|
|
1450
|
-
|
|
1943
|
+
const after = value.slice(lineEnd);
|
|
1944
|
+
let newValue;
|
|
1945
|
+
if (lineEnd === value.length) {
|
|
1946
|
+
newValue = before.endsWith("\n") ? before.slice(0, -1) : before;
|
|
1947
|
+
} else {
|
|
1948
|
+
newValue = before + after.slice(1);
|
|
1949
|
+
}
|
|
1451
1950
|
if (orderedMatch) {
|
|
1452
1951
|
const indent = orderedMatch[1] ?? "";
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1952
|
+
const lines = newValue.split("\n");
|
|
1953
|
+
const startLineIndex = Math.max(0, before.split("\n").length - 1);
|
|
1954
|
+
let nextNumber = 1;
|
|
1955
|
+
for (let i = startLineIndex - 1; i >= 0; i--) {
|
|
1956
|
+
const line = lines[i];
|
|
1957
|
+
const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
|
|
1958
|
+
if (match && match[1] === indent) {
|
|
1959
|
+
nextNumber = parseInt(match[2], 10) + 1;
|
|
1960
|
+
break;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
for (let i = startLineIndex; i < lines.length; i++) {
|
|
1964
|
+
const line = lines[i];
|
|
1965
|
+
const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
|
|
1966
|
+
if (match && match[1] === indent) {
|
|
1967
|
+
lines[i] = `${indent}${nextNumber}. ${match[3]}`;
|
|
1968
|
+
nextNumber++;
|
|
1969
|
+
} else if (match && match[1].length < indent.length) {
|
|
1970
|
+
break;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
newValue = lines.join("\n");
|
|
1458
1974
|
}
|
|
1459
1975
|
this.value = newValue;
|
|
1460
1976
|
textarea.value = newValue;
|
|
1461
1977
|
let newCursor = before.length;
|
|
1462
|
-
if (before.endsWith("\n")
|
|
1978
|
+
if (newCursor > 0 && before.endsWith("\n")) {
|
|
1463
1979
|
newCursor = before.length - 1;
|
|
1464
1980
|
}
|
|
1465
1981
|
requestAnimationFrame(() => {
|
|
1466
1982
|
textarea.setSelectionRange(newCursor, newCursor);
|
|
1467
|
-
this.
|
|
1983
|
+
this.emitChangeAndRefresh();
|
|
1468
1984
|
});
|
|
1469
|
-
this.dispatchChange();
|
|
1470
1985
|
return true;
|
|
1471
1986
|
}
|
|
1472
1987
|
return false;
|
|
@@ -1499,10 +2014,9 @@ ${indent}${nextNumber}. `;
|
|
|
1499
2014
|
previousState.selection.end
|
|
1500
2015
|
);
|
|
1501
2016
|
}
|
|
1502
|
-
this.
|
|
2017
|
+
this.emitChangeAndRefresh();
|
|
1503
2018
|
this.isUndoRedoAction = false;
|
|
1504
2019
|
});
|
|
1505
|
-
this.dispatchChange();
|
|
1506
2020
|
}
|
|
1507
2021
|
/**
|
|
1508
2022
|
* Perform a redo operation, reapplying a previously undone state.
|
|
@@ -1531,10 +2045,9 @@ ${indent}${nextNumber}. `;
|
|
|
1531
2045
|
nextState.selection.end
|
|
1532
2046
|
);
|
|
1533
2047
|
}
|
|
1534
|
-
this.
|
|
2048
|
+
this.emitChangeAndRefresh();
|
|
1535
2049
|
this.isUndoRedoAction = false;
|
|
1536
2050
|
});
|
|
1537
|
-
this.dispatchChange();
|
|
1538
2051
|
}
|
|
1539
2052
|
connectedCallback() {
|
|
1540
2053
|
super.connectedCallback();
|
|
@@ -1546,14 +2059,26 @@ ${indent}${nextNumber}. `;
|
|
|
1546
2059
|
this.saveInitialUndoState();
|
|
1547
2060
|
this.updateActiveFormats();
|
|
1548
2061
|
this.addKeyboardListeners();
|
|
2062
|
+
if (this.isPreview && this.value.trim()) {
|
|
2063
|
+
this.scheduleAccessibilityCheck();
|
|
2064
|
+
}
|
|
1549
2065
|
});
|
|
1550
2066
|
}
|
|
2067
|
+
updated(changedProperties) {
|
|
2068
|
+
super.updated(changedProperties);
|
|
2069
|
+
if (changedProperties.has("previewBackgroundColor") && this.isPreview && this.value.trim()) {
|
|
2070
|
+
this.scheduleAccessibilityCheck();
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
1551
2073
|
disconnectedCallback() {
|
|
1552
2074
|
super.disconnectedCallback();
|
|
1553
2075
|
document.removeEventListener("click", this.handleOutsideClick);
|
|
1554
2076
|
if (this.undoTimeout) {
|
|
1555
2077
|
clearTimeout(this.undoTimeout);
|
|
1556
2078
|
}
|
|
2079
|
+
if (this.accessibilityCheckTimeout) {
|
|
2080
|
+
clearTimeout(this.accessibilityCheckTimeout);
|
|
2081
|
+
}
|
|
1557
2082
|
this.removeKeyboardListeners();
|
|
1558
2083
|
this.savedSelectionForPreview = null;
|
|
1559
2084
|
}
|
|
@@ -1609,8 +2134,8 @@ ${indent}${nextNumber}. `;
|
|
|
1609
2134
|
}
|
|
1610
2135
|
toolbarTemplate() {
|
|
1611
2136
|
return shared_tailwindElement_index.x`
|
|
1612
|
-
<div class="flex items-center gap-
|
|
1613
|
-
<div class=${
|
|
2137
|
+
<div class="flex items-center gap-1">
|
|
2138
|
+
<div class=${axe.cn(this.styles().headingMenu())}>
|
|
1614
2139
|
<!-- Heading -->
|
|
1615
2140
|
<lukso-tooltip text="Heading options" placement="top">
|
|
1616
2141
|
<lukso-button
|
|
@@ -1619,6 +2144,7 @@ ${indent}${nextNumber}. `;
|
|
|
1619
2144
|
e.stopPropagation();
|
|
1620
2145
|
this.isColorDropdownOpen = false;
|
|
1621
2146
|
this.isListDropdownOpen = false;
|
|
2147
|
+
this.isAlignmentDropdownOpen = false;
|
|
1622
2148
|
this.isHeadingDropdownOpen = !this.isHeadingDropdownOpen;
|
|
1623
2149
|
}}
|
|
1624
2150
|
aria-expanded=${this.isHeadingDropdownOpen ? "true" : "false"}
|
|
@@ -1692,6 +2218,18 @@ ${indent}${nextNumber}. `;
|
|
|
1692
2218
|
>
|
|
1693
2219
|
Heading 3
|
|
1694
2220
|
</lukso-dropdown-option>
|
|
2221
|
+
<lukso-dropdown-option
|
|
2222
|
+
?is-selected=${this.getActiveHeadingLevel() === 4}
|
|
2223
|
+
@click=${(e) => {
|
|
2224
|
+
e.stopPropagation();
|
|
2225
|
+
this.restoreFocusAndSelection();
|
|
2226
|
+
this.applyHeading(4);
|
|
2227
|
+
this.isHeadingDropdownOpen = false;
|
|
2228
|
+
}}
|
|
2229
|
+
size="medium"
|
|
2230
|
+
>
|
|
2231
|
+
Heading 4
|
|
2232
|
+
</lukso-dropdown-option>
|
|
1695
2233
|
</lukso-dropdown>
|
|
1696
2234
|
</div>
|
|
1697
2235
|
|
|
@@ -1721,6 +2259,7 @@ ${indent}${nextNumber}. `;
|
|
|
1721
2259
|
this.restoreFocusAndSelection();
|
|
1722
2260
|
this.isHeadingDropdownOpen = false;
|
|
1723
2261
|
this.isColorDropdownOpen = false;
|
|
2262
|
+
this.isAlignmentDropdownOpen = false;
|
|
1724
2263
|
this.isListDropdownOpen = !this.isListDropdownOpen;
|
|
1725
2264
|
}}
|
|
1726
2265
|
aria-expanded=${this.isListDropdownOpen ? "true" : "false"}
|
|
@@ -1793,6 +2332,108 @@ ${indent}${nextNumber}. `;
|
|
|
1793
2332
|
this.activeFormats.link
|
|
1794
2333
|
)}
|
|
1795
2334
|
|
|
2335
|
+
<!-- Text Alignment -->
|
|
2336
|
+
<div class=${this.styles().alignmentMenu()}>
|
|
2337
|
+
<lukso-tooltip text="Text alignment" placement="top">
|
|
2338
|
+
<lukso-button
|
|
2339
|
+
id=${this.alignmentTriggerId}
|
|
2340
|
+
@click=${(e) => {
|
|
2341
|
+
e.stopPropagation();
|
|
2342
|
+
this.restoreFocusAndSelection();
|
|
2343
|
+
this.isHeadingDropdownOpen = false;
|
|
2344
|
+
this.isColorDropdownOpen = false;
|
|
2345
|
+
this.isListDropdownOpen = false;
|
|
2346
|
+
this.isAlignmentDropdownOpen = !this.isAlignmentDropdownOpen;
|
|
2347
|
+
}}
|
|
2348
|
+
aria-expanded=${this.isAlignmentDropdownOpen ? "true" : "false"}
|
|
2349
|
+
aria-label="Text alignment"
|
|
2350
|
+
variant="secondary"
|
|
2351
|
+
size="small"
|
|
2352
|
+
custom-class=${this.toolbarButton({
|
|
2353
|
+
active: this.activeFormats.alignment !== "left"
|
|
2354
|
+
})}
|
|
2355
|
+
is-icon
|
|
2356
|
+
>
|
|
2357
|
+
<lukso-icon
|
|
2358
|
+
name=${this.getAlignmentIcon()}
|
|
2359
|
+
size="small"
|
|
2360
|
+
pack="vuesax"
|
|
2361
|
+
variant="linear"
|
|
2362
|
+
></lukso-icon>
|
|
2363
|
+
</lukso-button>
|
|
2364
|
+
</lukso-tooltip>
|
|
2365
|
+
<lukso-dropdown
|
|
2366
|
+
id="alignmentDropdown"
|
|
2367
|
+
trigger-id=""
|
|
2368
|
+
size="medium"
|
|
2369
|
+
?is-open=${this.isAlignmentDropdownOpen}
|
|
2370
|
+
>
|
|
2371
|
+
<lukso-dropdown-option
|
|
2372
|
+
?is-selected=${this.activeFormats.alignment === "left"}
|
|
2373
|
+
@click=${(e) => {
|
|
2374
|
+
e.stopPropagation();
|
|
2375
|
+
this.restoreFocusAndSelection();
|
|
2376
|
+
this.applyAlignment("left");
|
|
2377
|
+
this.isAlignmentDropdownOpen = false;
|
|
2378
|
+
}}
|
|
2379
|
+
size="medium"
|
|
2380
|
+
aria-label="Align left"
|
|
2381
|
+
>
|
|
2382
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
2383
|
+
<lukso-icon
|
|
2384
|
+
name="textalign-left"
|
|
2385
|
+
size="small"
|
|
2386
|
+
pack="vuesax"
|
|
2387
|
+
variant="linear"
|
|
2388
|
+
></lukso-icon>
|
|
2389
|
+
Left
|
|
2390
|
+
</div>
|
|
2391
|
+
</lukso-dropdown-option>
|
|
2392
|
+
<lukso-dropdown-option
|
|
2393
|
+
?is-selected=${this.activeFormats.alignment === "center"}
|
|
2394
|
+
@click=${(e) => {
|
|
2395
|
+
e.stopPropagation();
|
|
2396
|
+
this.restoreFocusAndSelection();
|
|
2397
|
+
this.applyAlignment("center");
|
|
2398
|
+
this.isAlignmentDropdownOpen = false;
|
|
2399
|
+
}}
|
|
2400
|
+
size="medium"
|
|
2401
|
+
aria-label="Align center"
|
|
2402
|
+
>
|
|
2403
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
2404
|
+
<lukso-icon
|
|
2405
|
+
name="textalign-center"
|
|
2406
|
+
size="small"
|
|
2407
|
+
pack="vuesax"
|
|
2408
|
+
variant="linear"
|
|
2409
|
+
></lukso-icon>
|
|
2410
|
+
Center
|
|
2411
|
+
</div>
|
|
2412
|
+
</lukso-dropdown-option>
|
|
2413
|
+
<lukso-dropdown-option
|
|
2414
|
+
?is-selected=${this.activeFormats.alignment === "right"}
|
|
2415
|
+
@click=${(e) => {
|
|
2416
|
+
e.stopPropagation();
|
|
2417
|
+
this.restoreFocusAndSelection();
|
|
2418
|
+
this.applyAlignment("right");
|
|
2419
|
+
this.isAlignmentDropdownOpen = false;
|
|
2420
|
+
}}
|
|
2421
|
+
size="medium"
|
|
2422
|
+
aria-label="Align right"
|
|
2423
|
+
>
|
|
2424
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
2425
|
+
<lukso-icon
|
|
2426
|
+
name="textalign-right"
|
|
2427
|
+
size="small"
|
|
2428
|
+
pack="vuesax"
|
|
2429
|
+
variant="linear"
|
|
2430
|
+
></lukso-icon>
|
|
2431
|
+
Right
|
|
2432
|
+
</div>
|
|
2433
|
+
</lukso-dropdown-option>
|
|
2434
|
+
</lukso-dropdown>
|
|
2435
|
+
</div>
|
|
2436
|
+
|
|
1796
2437
|
<!-- Color -->
|
|
1797
2438
|
<div class=${this.styles().colorMenu()}>
|
|
1798
2439
|
<lukso-tooltip text="Text color" placement="top">
|
|
@@ -1803,6 +2444,7 @@ ${indent}${nextNumber}. `;
|
|
|
1803
2444
|
this.restoreFocusAndSelection();
|
|
1804
2445
|
this.isHeadingDropdownOpen = false;
|
|
1805
2446
|
this.isListDropdownOpen = false;
|
|
2447
|
+
this.isAlignmentDropdownOpen = false;
|
|
1806
2448
|
if (!this.isColorDropdownOpen) {
|
|
1807
2449
|
const ta = this.textareaEl?.shadowRoot?.querySelector("textarea");
|
|
1808
2450
|
if (ta) {
|
|
@@ -1848,6 +2490,7 @@ ${indent}${nextNumber}. `;
|
|
|
1848
2490
|
this.isColorDropdownOpen = false;
|
|
1849
2491
|
}}
|
|
1850
2492
|
type="button"
|
|
2493
|
+
aria-label="Clear color"
|
|
1851
2494
|
>
|
|
1852
2495
|
Clear
|
|
1853
2496
|
</button>` : shared_tailwindElement_index.E}
|
|
@@ -1859,6 +2502,7 @@ ${indent}${nextNumber}. `;
|
|
|
1859
2502
|
style="background-color: ${color}"
|
|
1860
2503
|
title=${color}
|
|
1861
2504
|
aria-pressed=${this.activeFormats.activeColor === color ? "true" : "false"}
|
|
2505
|
+
aria-label="Color ${color}"
|
|
1862
2506
|
@click=${(e) => {
|
|
1863
2507
|
e.stopPropagation();
|
|
1864
2508
|
this.selectColor(color);
|
|
@@ -1875,47 +2519,88 @@ ${indent}${nextNumber}. `;
|
|
|
1875
2519
|
</div>
|
|
1876
2520
|
`;
|
|
1877
2521
|
}
|
|
2522
|
+
accessibilityIndicatorTemplate() {
|
|
2523
|
+
if (!this.hasAccessibilityViolations || this.accessibilityViolations.length === 0 || !this.isPreview) {
|
|
2524
|
+
return shared_tailwindElement_index.E;
|
|
2525
|
+
}
|
|
2526
|
+
const tooltipText = formatViolationsForTooltip(this.accessibilityViolations);
|
|
2527
|
+
return shared_tailwindElement_index.x`
|
|
2528
|
+
<div
|
|
2529
|
+
class="accessibility-indicator has-violations absolute top-2 right-2 z-10"
|
|
2530
|
+
style="pointer-events: auto;"
|
|
2531
|
+
>
|
|
2532
|
+
<lukso-tooltip placement="left">
|
|
2533
|
+
<div slot="text">
|
|
2534
|
+
<div .innerHTML=${tooltipText}></div>
|
|
2535
|
+
</div>
|
|
2536
|
+
<div
|
|
2537
|
+
class="flex cursor-help"
|
|
2538
|
+
role="alert"
|
|
2539
|
+
aria-label="Accessibility violations found"
|
|
2540
|
+
>
|
|
2541
|
+
<lukso-icon
|
|
2542
|
+
name="warning-2"
|
|
2543
|
+
size="small"
|
|
2544
|
+
color="red-65"
|
|
2545
|
+
pack="vuesax"
|
|
2546
|
+
variant="linear"
|
|
2547
|
+
></lukso-icon>
|
|
2548
|
+
</div>
|
|
2549
|
+
</lukso-tooltip>
|
|
2550
|
+
</div>
|
|
2551
|
+
`;
|
|
2552
|
+
}
|
|
1878
2553
|
render() {
|
|
1879
2554
|
const { wrapper, header, toolbar, area, editor, preview } = this.styles({
|
|
1880
2555
|
isFullWidth: this.isFullWidth
|
|
1881
2556
|
});
|
|
2557
|
+
if (!this.previewBackgroundColor) {
|
|
2558
|
+
this.previewBackgroundColor = DEFAULT_PREVIEW_BACKGROUND_COLOR;
|
|
2559
|
+
}
|
|
1882
2560
|
return shared_tailwindElement_index.x`
|
|
1883
2561
|
<div class=${wrapper()}>
|
|
1884
2562
|
${this.labelTemplate()} ${this.descriptionTemplate()}
|
|
1885
2563
|
|
|
1886
|
-
<div class
|
|
1887
|
-
<div class=${
|
|
1888
|
-
|
|
2564
|
+
<div class="flex flex-col gap-2">
|
|
2565
|
+
<div class=${header()}>
|
|
2566
|
+
<div class=${toolbar()}>${this.toolbarTemplate()}</div>
|
|
2567
|
+
${this.buttonTemplate(
|
|
1889
2568
|
"eye",
|
|
1890
2569
|
() => this.togglePreview(),
|
|
1891
2570
|
"Toggle preview",
|
|
1892
2571
|
this.isPreview
|
|
1893
2572
|
)}
|
|
1894
|
-
|
|
2573
|
+
</div>
|
|
1895
2574
|
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2575
|
+
<div class=${area()}>
|
|
2576
|
+
${!this.isPreview ? shared_tailwindElement_index.x`<div class=${editor()}>
|
|
2577
|
+
<lukso-textarea
|
|
2578
|
+
.value=${this.value}
|
|
2579
|
+
name=${this.name ? this.name : shared_tailwindElement_index.E}
|
|
2580
|
+
size=${this.size ? this.size : shared_tailwindElement_index.E}
|
|
2581
|
+
rows=${this.rows ? this.rows : shared_tailwindElement_index.E}
|
|
2582
|
+
placeholder=${this.placeholder ? this.placeholder : shared_tailwindElement_index.E}
|
|
2583
|
+
error=${this.error ? this.error : shared_tailwindElement_index.E}
|
|
2584
|
+
?is-full-width=${true}
|
|
2585
|
+
?is-disabled=${this.isDisabled}
|
|
2586
|
+
?is-readonly=${this.isReadonly}
|
|
2587
|
+
?is-non-resizable=${this.isNonResizable}
|
|
2588
|
+
@on-input=${this.handleTextareaInput}
|
|
2589
|
+
@on-key-up=${this.handleTextareaKeyUp}
|
|
2590
|
+
@on-input-click=${this.handleTextareaClick}
|
|
2591
|
+
></lukso-textarea>
|
|
2592
|
+
${this.accessibilityIndicatorTemplate()}
|
|
2593
|
+
</div>` : shared_tailwindElement_index.x`<div
|
|
2594
|
+
class=${preview()}
|
|
2595
|
+
style="background-color: ${this.previewBackgroundColor};"
|
|
2596
|
+
>
|
|
2597
|
+
<lukso-markdown
|
|
2598
|
+
value=${this.value}
|
|
2599
|
+
prose-classes="prose prose-base prose-gray"
|
|
2600
|
+
></lukso-markdown>
|
|
2601
|
+
${this.accessibilityIndicatorTemplate()}
|
|
2602
|
+
</div>`}
|
|
2603
|
+
</div>
|
|
1919
2604
|
</div>
|
|
1920
2605
|
</div>
|
|
1921
2606
|
`;
|
|
@@ -1925,7 +2610,7 @@ __decorateClass([
|
|
|
1925
2610
|
property.n({ type: String })
|
|
1926
2611
|
], exports.LuksoMarkdownEditor.prototype, "value", 2);
|
|
1927
2612
|
__decorateClass([
|
|
1928
|
-
property.n({ type: String })
|
|
2613
|
+
property.n({ type: String, reflect: true })
|
|
1929
2614
|
], exports.LuksoMarkdownEditor.prototype, "name", 2);
|
|
1930
2615
|
__decorateClass([
|
|
1931
2616
|
property.n({ type: String })
|
|
@@ -1963,6 +2648,13 @@ __decorateClass([
|
|
|
1963
2648
|
__decorateClass([
|
|
1964
2649
|
property.n({ type: String })
|
|
1965
2650
|
], exports.LuksoMarkdownEditor.prototype, "placeholder", 2);
|
|
2651
|
+
__decorateClass([
|
|
2652
|
+
property.n({
|
|
2653
|
+
type: String,
|
|
2654
|
+
attribute: "preview-background-color",
|
|
2655
|
+
reflect: true
|
|
2656
|
+
})
|
|
2657
|
+
], exports.LuksoMarkdownEditor.prototype, "previewBackgroundColor", 2);
|
|
1966
2658
|
__decorateClass([
|
|
1967
2659
|
state.r()
|
|
1968
2660
|
], exports.LuksoMarkdownEditor.prototype, "savedSelectionForPreview", 2);
|
|
@@ -1975,6 +2667,9 @@ __decorateClass([
|
|
|
1975
2667
|
__decorateClass([
|
|
1976
2668
|
state.r()
|
|
1977
2669
|
], exports.LuksoMarkdownEditor.prototype, "isListDropdownOpen", 2);
|
|
2670
|
+
__decorateClass([
|
|
2671
|
+
state.r()
|
|
2672
|
+
], exports.LuksoMarkdownEditor.prototype, "isAlignmentDropdownOpen", 2);
|
|
1978
2673
|
__decorateClass([
|
|
1979
2674
|
state.r()
|
|
1980
2675
|
], exports.LuksoMarkdownEditor.prototype, "currentSelection", 2);
|
|
@@ -1984,6 +2679,12 @@ __decorateClass([
|
|
|
1984
2679
|
__decorateClass([
|
|
1985
2680
|
state.r()
|
|
1986
2681
|
], exports.LuksoMarkdownEditor.prototype, "activeFormats", 2);
|
|
2682
|
+
__decorateClass([
|
|
2683
|
+
state.r()
|
|
2684
|
+
], exports.LuksoMarkdownEditor.prototype, "accessibilityViolations", 2);
|
|
2685
|
+
__decorateClass([
|
|
2686
|
+
state.r()
|
|
2687
|
+
], exports.LuksoMarkdownEditor.prototype, "hasAccessibilityViolations", 2);
|
|
1987
2688
|
__decorateClass([
|
|
1988
2689
|
query.e("lukso-textarea")
|
|
1989
2690
|
], exports.LuksoMarkdownEditor.prototype, "textareaEl", 2);
|