@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.
Files changed (140) hide show
  1. package/dist/_commonjsHelpers-B85MJLTf.js +5 -0
  2. package/dist/_commonjsHelpers-CFO10eej.cjs +7 -0
  3. package/dist/axe-BK9JSROP.js +35093 -0
  4. package/dist/axe-C-H1UVi1.cjs +35097 -0
  5. package/dist/components/index.cjs +5 -5
  6. package/dist/components/index.js +5 -5
  7. package/dist/components/lukso-button/index.cjs +6 -6
  8. package/dist/components/lukso-button/index.js +4 -4
  9. package/dist/components/lukso-card/index.cjs +20 -20
  10. package/dist/components/lukso-card/index.js +8 -8
  11. package/dist/components/lukso-checkbox/index.cjs +3 -3
  12. package/dist/components/lukso-checkbox/index.js +3 -3
  13. package/dist/components/lukso-collapse/index.cjs +5 -5
  14. package/dist/components/lukso-collapse/index.js +4 -4
  15. package/dist/components/lukso-color-picker/index.cjs +6 -6
  16. package/dist/components/lukso-color-picker/index.js +5 -5
  17. package/dist/components/lukso-dropdown/index.cjs +4 -4
  18. package/dist/components/lukso-dropdown/index.js +4 -4
  19. package/dist/components/lukso-dropdown-option/index.cjs +2 -2
  20. package/dist/components/lukso-dropdown-option/index.js +2 -2
  21. package/dist/components/lukso-footer/index.cjs +2 -2
  22. package/dist/components/lukso-footer/index.js +2 -2
  23. package/dist/components/lukso-icon/index.cjs +41 -19
  24. package/dist/components/lukso-icon/index.d.ts +4 -4
  25. package/dist/components/lukso-icon/index.d.ts.map +1 -1
  26. package/dist/components/lukso-icon/index.js +41 -19
  27. package/dist/components/lukso-icon/lukso-icon.stories.d.ts +15 -1
  28. package/dist/components/lukso-icon/lukso-icon.stories.d.ts.map +1 -1
  29. package/dist/components/lukso-icon/vuesax/bold/warning-2.svg +3 -0
  30. package/dist/components/lukso-icon/vuesax/broken/warning-2.svg +5 -0
  31. package/dist/components/lukso-icon/vuesax/bulk/warning-2.svg +5 -0
  32. package/dist/components/lukso-icon/vuesax/linear/warning-2.svg +5 -0
  33. package/dist/components/lukso-icon/vuesax/outline/warning-2.svg +5 -0
  34. package/dist/components/lukso-icon/vuesax/twotone/warning-2.svg +5 -0
  35. package/dist/components/lukso-image/index.cjs +4 -4
  36. package/dist/components/lukso-image/index.js +4 -4
  37. package/dist/components/lukso-input/index.cjs +5 -5
  38. package/dist/components/lukso-input/index.js +4 -4
  39. package/dist/components/lukso-markdown/index.cjs +3 -2
  40. package/dist/components/lukso-markdown/index.d.ts.map +1 -1
  41. package/dist/components/lukso-markdown/index.js +3 -2
  42. package/dist/components/lukso-markdown-editor/index.cjs +858 -157
  43. package/dist/components/lukso-markdown-editor/index.d.ts +63 -1
  44. package/dist/components/lukso-markdown-editor/index.d.ts.map +1 -1
  45. package/dist/components/lukso-markdown-editor/index.js +857 -156
  46. package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts +65 -1
  47. package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts.map +1 -1
  48. package/dist/components/lukso-modal/index.cjs +2 -2
  49. package/dist/components/lukso-modal/index.js +2 -2
  50. package/dist/components/lukso-navbar/index.cjs +3 -3
  51. package/dist/components/lukso-navbar/index.js +3 -3
  52. package/dist/components/lukso-pagination/index.cjs +3 -3
  53. package/dist/components/lukso-pagination/index.js +3 -3
  54. package/dist/components/lukso-profile/index.cjs +3 -3
  55. package/dist/components/lukso-profile/index.js +3 -3
  56. package/dist/components/lukso-progress/index.cjs +7 -7
  57. package/dist/components/lukso-progress/index.js +4 -4
  58. package/dist/components/lukso-radio/index.cjs +6 -6
  59. package/dist/components/lukso-radio/index.js +4 -4
  60. package/dist/components/lukso-radio-group/index.cjs +3 -3
  61. package/dist/components/lukso-radio-group/index.js +3 -3
  62. package/dist/components/lukso-sanitize/index.cjs +30 -19
  63. package/dist/components/lukso-sanitize/index.js +30 -19
  64. package/dist/components/lukso-search/index.cjs +7 -7
  65. package/dist/components/lukso-search/index.js +7 -7
  66. package/dist/components/lukso-select/index.cjs +5 -5
  67. package/dist/components/lukso-select/index.js +5 -5
  68. package/dist/components/lukso-share/index.cjs +2 -2
  69. package/dist/components/lukso-share/index.js +2 -2
  70. package/dist/components/lukso-switch/index.cjs +3 -3
  71. package/dist/components/lukso-switch/index.js +3 -3
  72. package/dist/components/lukso-tag/index.cjs +3 -3
  73. package/dist/components/lukso-tag/index.js +3 -3
  74. package/dist/components/lukso-terms/index.cjs +3 -3
  75. package/dist/components/lukso-terms/index.js +3 -3
  76. package/dist/components/lukso-textarea/index.cjs +5 -5
  77. package/dist/components/lukso-textarea/index.js +4 -4
  78. package/dist/components/lukso-tooltip/index.cjs +3 -3
  79. package/dist/components/lukso-tooltip/index.js +3 -3
  80. package/dist/components/lukso-username/index.cjs +6 -6
  81. package/dist/components/lukso-username/index.js +6 -6
  82. package/dist/components/lukso-wizard/index.cjs +2 -2
  83. package/dist/components/lukso-wizard/index.js +2 -2
  84. package/dist/docs/Typography.stories.d.ts.map +1 -1
  85. package/dist/{index-QfURPzjM.cjs → index-1XmcSGot.cjs} +6 -6
  86. package/dist/{index-BvQ5tHy1.cjs → index-BhmMhjR9.cjs} +1 -1
  87. package/dist/{index-BR_I57SD.cjs → index-CJ6SmSwD.cjs} +4 -7
  88. package/dist/{index-g9LnjUjQ.js → index-CP2bxOlA.js} +3 -6
  89. package/dist/{index-DGEC4kLx.cjs → index-CgUaX5S2.cjs} +6 -6
  90. package/dist/index-DVrgVgp0.cjs +50 -0
  91. package/dist/{index-DI0bldib.js → index-Fy0gnibp.js} +1 -1
  92. package/dist/{index-CzJGpXul.js → index-Vj-gQHwJ.js} +5 -5
  93. package/dist/index-YQoO_Soq.js +41 -0
  94. package/dist/{index-bOsUG-kI.js → index-ldATebdp.js} +6 -6
  95. package/dist/index.cjs +6 -6
  96. package/dist/index.js +5 -5
  97. package/dist/{isAddress-7c5mkwjj.cjs → isAddress-DYM3mZP7.cjs} +1 -1
  98. package/dist/{isAddress-DyEmEF7O.js → isAddress-ZNhN82OL.js} +1 -1
  99. package/dist/{property-C7opy5D6.cjs → property-Df-2NJoK.cjs} +1 -1
  100. package/dist/{property-BohUfInC.js → property-Dsz7qIbu.js} +1 -1
  101. package/dist/shared/tailwind-element/index.cjs +1 -1
  102. package/dist/shared/tailwind-element/index.js +1 -1
  103. package/dist/{state-De1ciDiG.cjs → state-B2ymS5qU.cjs} +1 -1
  104. package/dist/{state-DJBtqv5G.js → state-CcocMKSJ.js} +1 -1
  105. package/dist/{style-map-BXvyZNeB.js → style-map-A9HLuIu5.js} +1 -1
  106. package/dist/{style-map-BjMzqR4L.cjs → style-map-CZiyZP71.cjs} +1 -1
  107. package/dist/styles/main.css +39 -0
  108. package/dist/styles/main.css.map +1 -1
  109. package/dist/{unsafe-html-Bs5tNJGm.js → unsafe-html-BTdQd0Aj.js} +1 -1
  110. package/dist/{unsafe-html-D9Z4CuNa.cjs → unsafe-html-CWvuBYgI.cjs} +1 -1
  111. package/dist/vitest.config.d.ts.map +1 -1
  112. package/dist/warning-2-97Si6IrM.cjs +7 -0
  113. package/dist/warning-2-BSM6W1Gm.js +3 -0
  114. package/dist/warning-2-BixGOAk9.js +3 -0
  115. package/dist/warning-2-BvOu0gS4.js +3 -0
  116. package/dist/warning-2-CZN1r-ol.cjs +7 -0
  117. package/dist/warning-2-CtlOo41U.js +3 -0
  118. package/dist/warning-2-Cv13JPGf.cjs +7 -0
  119. package/dist/warning-2-D2EzJ9aB.js +3 -0
  120. package/dist/warning-2-DMkszxQr.cjs +7 -0
  121. package/dist/warning-2-DdO_RDHZ.cjs +7 -0
  122. package/dist/warning-2-Dq9nReo1.cjs +7 -0
  123. package/dist/warning-2-DqcrFtKk.js +3 -0
  124. package/package.json +1 -1
  125. package/tools/__tests__/accessibility-checker.spec.d.ts +2 -0
  126. package/tools/__tests__/accessibility-checker.spec.d.ts.map +1 -0
  127. package/tools/accessibility-checker.d.ts +36 -0
  128. package/tools/accessibility-checker.d.ts.map +1 -0
  129. package/tools/index.cjs +32697 -1
  130. package/tools/index.d.ts +1 -0
  131. package/tools/index.d.ts.map +1 -1
  132. package/tools/index.js +32695 -2
  133. package/tools/sass/typography.scss +55 -1
  134. package/tools/styles/main.css +39 -0
  135. package/LICENSE +0 -21
  136. package/README.md +0 -146
  137. package/dist/cn-CNdKneQ1.cjs +0 -2578
  138. package/dist/cn-Cu9aP49j.js +0 -2575
  139. package/dist/index-Da-0kQ05.cjs +0 -50
  140. 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-Da-0kQ05.cjs');
6
- const property = require('../../property-C7opy5D6.cjs');
7
- const state = require('../../state-De1ciDiG.cjs');
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 cn = require('../../cn-CNdKneQ1.cjs');
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.updateActiveFormats();
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-3 for heading levels
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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 before = value.slice(0, s);
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 foundMatch = false;
979
- const searchText = value.slice(
980
- Math.max(0, s - 100),
981
- Math.min(value.length, e + 100)
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 matchStart = searchOffset + match.index;
987
- const matchEnd = searchOffset + match.index + match[0].length;
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 = searchOffset + match.index + spanOpenTag.length;
990
- const contentEnd = matchEnd - 7;
991
- if (contentStart <= s && e <= contentEnd) {
992
- const beforeContent = value.slice(contentStart, s);
993
- const afterContent = value.slice(e, contentEnd);
994
- const newContent = beforeContent + selected + afterContent;
995
- this.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
996
- textarea.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
997
- const selStart = matchStart + beforeContent.length;
998
- const selEnd = selStart + selected.length;
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(selStart, selEnd);
1001
- this.updateActiveFormats();
1424
+ textarea.setSelectionRange(
1425
+ Math.max(0, newStart),
1426
+ Math.max(0, newEnd)
1427
+ );
1428
+ this.emitChangeAndRefresh();
1002
1429
  });
1003
- this.dispatchChange();
1004
- foundMatch = true;
1005
- break;
1430
+ return;
1006
1431
  }
1007
- match = fullColorRegex.exec(searchText);
1008
1432
  }
1009
- if (!foundMatch) {
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) return false;
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) return false;
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.updateActiveFormats();
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
- requestAnimationFrame(() => {
1138
- textarea.setSelectionRange(newCursor, newCursor);
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.updateActiveFormats();
1576
+ this.emitChangeAndRefresh();
1158
1577
  });
1159
- this.dispatchChange();
1160
1578
  return true;
1161
1579
  }
1162
- const nextNumber = parseInt(numberStr, 10) + 1;
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
- const newValue = before + prefix + after;
1168
- const renumberedValue = this.renumberOrderedListItems(
1169
- newValue,
1170
- start + prefix.length,
1171
- indent
1172
- );
1173
- this.value = renumberedValue;
1174
- textarea.value = renumberedValue;
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
- requestAnimationFrame(() => {
1177
- textarea.setSelectionRange(newCursor, newCursor);
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) return false;
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) return false;
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
- newLine = `${currentIndent}${indent}1. ${content}`;
1223
- newCursorOffset = indent.length;
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
- const newValue = before + newLine + after;
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
- requestAnimationFrame(() => {
1233
- textarea.setSelectionRange(newCursor, newCursor);
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.updateActiveFormats();
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
- if (cursorPositionInLine === markerEndPosition && !hasContent) {
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
- lineEnd === value.length ? lineEnd : lineEnd + 1
1449
- );
1450
- let newValue = before + after;
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
- newValue = this.renumberOrderedListItems(
1454
- newValue,
1455
- before.length,
1456
- indent
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") && before.length > 1) {
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.updateActiveFormats();
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.updateActiveFormats();
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.updateActiveFormats();
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-2">
1613
- <div class=${cn.cn(this.styles().headingMenu())}>
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=${header()}>
1887
- <div class=${toolbar()}>${this.toolbarTemplate()}</div>
1888
- ${this.buttonTemplate(
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
- </div>
2573
+ </div>
1895
2574
 
1896
- <div class=${area()}>
1897
- ${!this.isPreview ? shared_tailwindElement_index.x`<div class=${editor()}>
1898
- <lukso-textarea
1899
- .value=${this.value}
1900
- name=${this.name ? this.name : shared_tailwindElement_index.E}
1901
- size=${this.size ? this.size : shared_tailwindElement_index.E}
1902
- rows=${this.rows ? this.rows : shared_tailwindElement_index.E}
1903
- placeholder=${this.placeholder ? this.placeholder : shared_tailwindElement_index.E}
1904
- error=${this.error ? this.error : shared_tailwindElement_index.E}
1905
- ?is-full-width=${true}
1906
- ?is-disabled=${this.isDisabled}
1907
- ?is-readonly=${this.isReadonly}
1908
- ?is-non-resizable=${this.isNonResizable}
1909
- @on-input=${this.handleTextareaInput}
1910
- @on-key-up=${this.handleTextareaKeyUp}
1911
- @on-input-click=${this.handleTextareaClick}
1912
- ></lukso-textarea>
1913
- </div>` : shared_tailwindElement_index.x`<div class=${preview()}>
1914
- <lukso-markdown
1915
- value=${this.value}
1916
- prose-classes="prose prose-base prose-gray"
1917
- ></lukso-markdown>
1918
- </div>`}
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);