@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
@@ -1,10 +1,10 @@
1
- import { T as TailwindStyledElement, E, x } from '../../index-wzuGnJOC.js';
2
- import { n, t } from '../../property-BohUfInC.js';
3
- import { r } from '../../state-DJBtqv5G.js';
1
+ import { T as TailwindStyledElement, E, x } from '../../index-YQoO_Soq.js';
2
+ import { n, t } from '../../property-Dsz7qIbu.js';
3
+ import { r } from '../../state-CcocMKSJ.js';
4
4
  import { e } from '../../query-CHb9Ft_d.js';
5
5
  import { c as ce } from '../../index-B9iart53.js';
6
6
  import '../../tailwind-config.js';
7
- import { c as cn } from '../../cn-Cu9aP49j.js';
7
+ import { a as axe, c as cn } from '../../axe-BK9JSROP.js';
8
8
  import '../lukso-textarea/index.js';
9
9
  import '../lukso-markdown/index.js';
10
10
  import '../lukso-switch/index.js';
@@ -15,6 +15,178 @@ import '../lukso-dropdown/index.js';
15
15
  import '../lukso-dropdown-option/index.js';
16
16
  import '../lukso-tooltip/index.js';
17
17
 
18
+ function mapAxeViolationToAccessibilityViolation(axeViolation) {
19
+ return {
20
+ id: axeViolation.id,
21
+ impact: axeViolation.impact,
22
+ description: axeViolation.description,
23
+ help: axeViolation.help,
24
+ helpUrl: axeViolation.helpUrl,
25
+ nodes: axeViolation.nodes?.map((node) => ({
26
+ html: node.html,
27
+ target: node.target,
28
+ failureSummary: node.failureSummary
29
+ })) || []
30
+ };
31
+ }
32
+ async function checkAccessibility(element) {
33
+ if (!element) {
34
+ return {
35
+ violations: [],
36
+ hasViolations: false,
37
+ violationCount: 0
38
+ };
39
+ }
40
+ try {
41
+ const results = await axe.run(element, {
42
+ resultTypes: ["violations"],
43
+ tags: ["wcag2a", "wcag2aa", "wcag21aa", "best-practice"],
44
+ rules: {
45
+ "color-contrast": { enabled: true },
46
+ "color-contrast-enhanced": { enabled: true },
47
+ "image-alt": { enabled: true },
48
+ "button-name": { enabled: true },
49
+ "link-name": { enabled: true },
50
+ label: { enabled: true },
51
+ "form-field-multiple-labels": { enabled: true }
52
+ }
53
+ });
54
+ const violations = results.violations.map(
55
+ mapAxeViolationToAccessibilityViolation
56
+ );
57
+ return {
58
+ violations,
59
+ hasViolations: violations.length > 0,
60
+ violationCount: violations.length
61
+ };
62
+ } catch (error) {
63
+ console.warn("Accessibility checking failed:", error);
64
+ return {
65
+ violations: [],
66
+ hasViolations: false,
67
+ violationCount: 0
68
+ };
69
+ }
70
+ }
71
+ const getViolationSummary = (violations) => {
72
+ const summary = {
73
+ critical: 0,
74
+ serious: 0,
75
+ moderate: 0,
76
+ minor: 0,
77
+ total: violations.length
78
+ };
79
+ violations.forEach((violation) => {
80
+ if (violation.impact) {
81
+ summary[violation.impact]++;
82
+ }
83
+ });
84
+ return summary;
85
+ };
86
+ function getImpactStyling(impact) {
87
+ switch (impact) {
88
+ case "critical":
89
+ return {
90
+ colorClass: "text-red-600",
91
+ borderClass: "border-red-600",
92
+ label: "Critical"
93
+ };
94
+ case "serious":
95
+ return {
96
+ colorClass: "text-orange-600",
97
+ borderClass: "border-orange-600",
98
+ label: "Serious"
99
+ };
100
+ case "moderate":
101
+ return {
102
+ colorClass: "text-yellow-600",
103
+ borderClass: "border-yellow-600",
104
+ label: "Moderate"
105
+ };
106
+ case "minor":
107
+ return {
108
+ colorClass: "text-green-600",
109
+ borderClass: "border-green-600",
110
+ label: "Minor"
111
+ };
112
+ default:
113
+ return {
114
+ colorClass: "text-gray-500",
115
+ borderClass: "border-gray-500",
116
+ label: "Info"
117
+ };
118
+ }
119
+ }
120
+ const formatViolationsForTooltip = (violations) => {
121
+ if (violations.length === 0) {
122
+ return `
123
+ <div class="text-center text-green-600 font-semibold">
124
+ No accessibility violations found!
125
+ </div>
126
+ `;
127
+ }
128
+ const summary = getViolationSummary(violations);
129
+ let tooltip = `
130
+ <div class="max-w-sm leading-relaxed p-1">
131
+ <div class="font-bold text-sm mb-1">
132
+ Accessibility Issues Found
133
+ </div>
134
+ `;
135
+ tooltip += `
136
+ <div class="mb-2">
137
+ <div class="flex gap-3 flex-wrap">
138
+ `;
139
+ if (summary.critical > 0) {
140
+ tooltip += `<span class="text-red-600">🚨 ${summary.critical} Critical</span>`;
141
+ }
142
+ if (summary.serious > 0) {
143
+ tooltip += `<span class="text-orange-600">⚠️ ${summary.serious} Serious</span>`;
144
+ }
145
+ if (summary.moderate > 0) {
146
+ tooltip += `<span class="text-yellow-600">⚡ ${summary.moderate} Moderate</span>`;
147
+ }
148
+ if (summary.minor > 0) {
149
+ tooltip += `<span class="text-green-600">💡 ${summary.minor} Minor</span>`;
150
+ }
151
+ tooltip += `
152
+ </div>
153
+ </div>
154
+ `;
155
+ const sortedViolations = violations.sort((a, b) => {
156
+ const impactOrder = { critical: 0, serious: 1, moderate: 2, minor: 3 };
157
+ const aOrder = impactOrder[a.impact] ?? 4;
158
+ const bOrder = impactOrder[b.impact] ?? 4;
159
+ return aOrder - bOrder;
160
+ });
161
+ tooltip += `<div class="space-y-3">`;
162
+ sortedViolations.forEach((violation) => {
163
+ const impactStyling = getImpactStyling(violation.impact);
164
+ tooltip += `
165
+ <div class="p-2 bg-white border-l-4 ${impactStyling.borderClass} rounded">
166
+ <div class="flex items-center gap-1.5 mb-1.5">
167
+ <strong class="text-gray-800 text-xs leading-tight">${violation.help}</strong>
168
+ </div>
169
+
170
+ <div class="flex items-center gap-1.5 mb-1">
171
+ <span class="text-xs ${impactStyling.colorClass} font-semibold">
172
+ ${impactStyling.label}
173
+ </span>
174
+ <span class="text-xs text-gray-400">•</span>
175
+ <span class="text-xs text-gray-500">
176
+ ${violation.nodes?.length || 0} element${(violation.nodes?.length || 0) !== 1 ? "s" : ""} affected
177
+ </span>
178
+ </div>
179
+ </div>
180
+ `;
181
+ });
182
+ tooltip += `
183
+ </div>
184
+ <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>
185
+ </div>
186
+ `;
187
+ return tooltip.trim();
188
+ };
189
+
18
190
  const style = ":host {\n display: flex\n}";
19
191
 
20
192
  var __defProp = Object.defineProperty;
@@ -27,6 +199,7 @@ var __decorateClass = (decorators, target, key, kind) => {
27
199
  if (kind && result) __defProp(target, key, result);
28
200
  return result;
29
201
  };
202
+ const DEFAULT_PREVIEW_BACKGROUND_COLOR = "#f9f9f9";
30
203
  let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
31
204
  constructor() {
32
205
  super(...arguments);
@@ -44,13 +217,16 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
44
217
  this.isPreview = false;
45
218
  this.rows = 6;
46
219
  this.placeholder = "";
220
+ this.previewBackgroundColor = DEFAULT_PREVIEW_BACKGROUND_COLOR;
47
221
  this.savedSelectionForPreview = null;
48
222
  this.isHeadingDropdownOpen = false;
49
223
  this.isColorDropdownOpen = false;
50
224
  this.isListDropdownOpen = false;
225
+ this.isAlignmentDropdownOpen = false;
51
226
  this.headingTriggerId = "heading-dropdown-trigger";
52
227
  this.colorTriggerId = "color-dropdown-trigger";
53
228
  this.listTriggerId = "list-dropdown-trigger";
229
+ this.alignmentTriggerId = "alignment-dropdown-trigger";
54
230
  this.currentSelection = { start: 0, end: 0 };
55
231
  this.savedSelection = null;
56
232
  this.defaultColor = "#374151";
@@ -61,10 +237,12 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
61
237
  h1: false,
62
238
  h2: false,
63
239
  h3: false,
240
+ h4: false,
64
241
  color: false,
65
242
  activeColor: this.defaultColor,
66
243
  unorderedList: false,
67
- orderedList: false
244
+ orderedList: false,
245
+ alignment: "left"
68
246
  };
69
247
  // Undo/Redo state
70
248
  this.undoStack = [];
@@ -74,6 +252,10 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
74
252
  this.undoTimeout = null;
75
253
  this.MAX_UNDO_STACK_SIZE = 100;
76
254
  this.UNDO_SAVE_DELAY = 500;
255
+ this.accessibilityViolations = [];
256
+ this.hasAccessibilityViolations = false;
257
+ this.accessibilityCheckTimeout = null;
258
+ this.ACCESSIBILITY_CHECK_DELAY = 1e3;
77
259
  this.handleOutsideClick = (event) => {
78
260
  const target = event.target;
79
261
  const isInsideThisComponent = this.contains(target) || this.shadowRoot?.contains(target);
@@ -87,14 +269,19 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
87
269
  if (this.isListDropdownOpen) {
88
270
  this.isListDropdownOpen = false;
89
271
  }
272
+ if (this.isAlignmentDropdownOpen) {
273
+ this.isAlignmentDropdownOpen = false;
274
+ }
90
275
  return;
91
276
  }
92
277
  const isInsideHeadingDropdown = this.shadowRoot?.getElementById("headingDropdown")?.contains(target);
93
278
  const isInsideColorDropdown = this.shadowRoot?.getElementById("colorDropdown")?.contains(target);
94
279
  const isInsideListDropdown = this.shadowRoot?.getElementById("listDropdown")?.contains(target);
280
+ const isInsideAlignmentDropdown = this.shadowRoot?.getElementById("alignmentDropdown")?.contains(target);
95
281
  const isHeadingTrigger = this.shadowRoot?.getElementById(this.headingTriggerId)?.contains(target);
96
282
  const isColorTrigger = this.shadowRoot?.getElementById(this.colorTriggerId)?.contains(target);
97
283
  const isListTrigger = this.shadowRoot?.getElementById(this.listTriggerId)?.contains(target);
284
+ const isAlignmentTrigger = this.shadowRoot?.getElementById(this.alignmentTriggerId)?.contains(target);
98
285
  if (!isInsideHeadingDropdown && !isHeadingTrigger && this.isHeadingDropdownOpen) {
99
286
  this.isHeadingDropdownOpen = false;
100
287
  }
@@ -104,18 +291,22 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
104
291
  if (!isInsideListDropdown && !isListTrigger && this.isListDropdownOpen) {
105
292
  this.isListDropdownOpen = false;
106
293
  }
294
+ if (!isInsideAlignmentDropdown && !isAlignmentTrigger && this.isAlignmentDropdownOpen) {
295
+ this.isAlignmentDropdownOpen = false;
296
+ }
107
297
  };
108
298
  this.styles = ce({
109
299
  slots: {
110
300
  wrapper: "w-[inherit] grid gap-3",
111
301
  header: "flex items-center justify-between gap-2 border border-neutral-90 rounded-12 px-3 py-2 bg-neutral-100",
112
302
  toolbar: "flex flex-wrap items-center gap-1",
113
- area: "",
303
+ area: "relative",
114
304
  editor: "",
115
- preview: "p-3",
305
+ preview: "p-3 border border-neutral-90 rounded-12 min-h-[158px]",
116
306
  colorMenu: "relative",
117
307
  headingMenu: "relative",
118
308
  listMenu: "relative",
309
+ alignmentMenu: "relative",
119
310
  label: "heading-inter-14-bold text-neutral-20",
120
311
  description: "paragraph-inter-12-regular text-neutral-20",
121
312
  divider: "w-[1px] h-4 bg-neutral-90"
@@ -229,7 +420,7 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
229
420
  this.scheduleUndoStateSave();
230
421
  }
231
422
  this.value = newValue;
232
- this.updateActiveFormats();
423
+ this.emitChangeAndRefresh(event);
233
424
  };
234
425
  /**
235
426
  * Textarea keyup handler.
@@ -280,7 +471,7 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
280
471
  }
281
472
  dispatchChange(event) {
282
473
  this.updateComplete.then(() => {
283
- const changeEvent = new CustomEvent("on-change", {
474
+ const changeEvent = new CustomEvent("on-markdown-change", {
284
475
  detail: { value: this.value, event },
285
476
  bubbles: false,
286
477
  composed: true
@@ -288,6 +479,68 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
288
479
  this.dispatchEvent(changeEvent);
289
480
  });
290
481
  }
482
+ /**
483
+ * Check accessibility violations on the live preview content
484
+ */
485
+ async performAccessibilityCheck() {
486
+ if (!this.value.trim()) {
487
+ this.accessibilityViolations = [];
488
+ this.hasAccessibilityViolations = false;
489
+ return;
490
+ }
491
+ try {
492
+ if (this.isPreview) {
493
+ const markdownEl = this.shadowRoot?.querySelector("lukso-markdown");
494
+ if (markdownEl) {
495
+ await new Promise((resolve) => setTimeout(resolve, 200));
496
+ const sanitizeEl = markdownEl.shadowRoot?.querySelector("lukso-sanitize");
497
+ const proseDiv = sanitizeEl?.shadowRoot?.querySelector(
498
+ "div.prose"
499
+ );
500
+ if (proseDiv) {
501
+ const result = await checkAccessibility(proseDiv);
502
+ this.accessibilityViolations = result.violations;
503
+ this.hasAccessibilityViolations = result.hasViolations;
504
+ } else {
505
+ this.accessibilityViolations = [];
506
+ this.hasAccessibilityViolations = false;
507
+ }
508
+ }
509
+ } else {
510
+ this.accessibilityViolations = [];
511
+ this.hasAccessibilityViolations = false;
512
+ }
513
+ } catch (error) {
514
+ console.warn("Accessibility checking failed:", error);
515
+ this.accessibilityViolations = [];
516
+ this.hasAccessibilityViolations = false;
517
+ }
518
+ this.requestUpdate();
519
+ }
520
+ /**
521
+ * Schedule accessibility check with debouncing
522
+ */
523
+ scheduleAccessibilityCheck() {
524
+ if (this.accessibilityCheckTimeout) {
525
+ clearTimeout(this.accessibilityCheckTimeout);
526
+ }
527
+ this.accessibilityCheckTimeout = window.setTimeout(() => {
528
+ this.performAccessibilityCheck();
529
+ this.accessibilityCheckTimeout = null;
530
+ }, this.ACCESSIBILITY_CHECK_DELAY);
531
+ }
532
+ /**
533
+ * Unified helper that ensures both active format state and change events are properly
534
+ * emitted after any value mutation. This replaces the scattered updateActiveFormats()
535
+ * and dispatchChange() calls throughout the codebase.
536
+ *
537
+ * @param event - Optional event that triggered the change
538
+ */
539
+ emitChangeAndRefresh(event) {
540
+ this.updateActiveFormats();
541
+ this.dispatchChange(event);
542
+ this.scheduleAccessibilityCheck();
543
+ }
291
544
  /**
292
545
  * Utility to perform an operation with the current textarea selection.
293
546
  *
@@ -328,7 +581,7 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
328
581
  /**
329
582
  * Apply or toggle heading formatting for the current line(s).
330
583
  *
331
- * @param level - 0 to remove heading, 1-3 for heading levels
584
+ * @param level - 0 to remove heading, 1-4 for heading levels
332
585
  */
333
586
  applyHeading(level) {
334
587
  if (this.isReadonly || this.isDisabled) return;
@@ -374,9 +627,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
374
627
  }
375
628
  requestAnimationFrame(() => {
376
629
  textarea.setSelectionRange(cursorPosition, cursorPosition);
377
- this.updateActiveFormats();
630
+ this.emitChangeAndRefresh();
378
631
  });
379
- this.dispatchChange();
380
632
  });
381
633
  }
382
634
  /**
@@ -506,9 +758,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
506
758
  const cursorPosition = before.length + (firstLineEnd === -1 ? transformed.length : firstLineEnd);
507
759
  requestAnimationFrame(() => {
508
760
  textarea.setSelectionRange(cursorPosition, cursorPosition);
509
- this.updateActiveFormats();
761
+ this.emitChangeAndRefresh();
510
762
  });
511
- this.dispatchChange();
512
763
  });
513
764
  }
514
765
  /**
@@ -519,6 +770,155 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
519
770
  if (this.activeFormats.orderedList) return "ordered";
520
771
  return "none";
521
772
  }
773
+ /**
774
+ * Get the current alignment icon name based on activeFormats.
775
+ */
776
+ getAlignmentIcon() {
777
+ switch (this.activeFormats.alignment) {
778
+ case "center":
779
+ return "textalign-center";
780
+ case "right":
781
+ return "textalign-right";
782
+ default:
783
+ return "textalign-left";
784
+ }
785
+ }
786
+ /**
787
+ * Apply or toggle text alignment for the current line(s).
788
+ *
789
+ * @param alignment - 'left', 'center', or 'right'
790
+ */
791
+ applyAlignment(alignment) {
792
+ if (this.isReadonly || this.isDisabled) return;
793
+ this.saveUndoStateBeforeChange();
794
+ this.withSelection((textarea, start, end, value) => {
795
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
796
+ let lineEnd = value.indexOf("\n", end);
797
+ if (lineEnd === -1) lineEnd = value.length;
798
+ const before = value.slice(0, lineStart);
799
+ const selected = value.slice(lineStart, lineEnd);
800
+ const after = value.slice(lineEnd);
801
+ let transformed;
802
+ const currentAlignmentRegex = /<div style="text-align: (left|center|right);">(.*?)<\/div>/s;
803
+ const existingMatch = selected.match(currentAlignmentRegex);
804
+ if (existingMatch || this.hasNestedAlignment(selected)) {
805
+ if (existingMatch) {
806
+ const existingAlignment = existingMatch[1];
807
+ const innerContent = existingMatch[2];
808
+ if (existingAlignment === alignment) {
809
+ transformed = innerContent;
810
+ } else {
811
+ transformed = `<div style="text-align: ${alignment};">${innerContent}</div>`;
812
+ }
813
+ } else {
814
+ if (this.getNestedAlignment(selected) === alignment) {
815
+ transformed = this.removeNestedAlignment(selected);
816
+ } else {
817
+ transformed = this.replaceNestedAlignment(selected, alignment);
818
+ }
819
+ }
820
+ } else {
821
+ if (alignment === "left") {
822
+ transformed = selected;
823
+ } else {
824
+ transformed = this.wrapContentWithAlignment(selected, alignment);
825
+ }
826
+ }
827
+ this.value = before + transformed + after;
828
+ textarea.value = before + transformed + after;
829
+ const cursorPosition = before.length + transformed.length;
830
+ requestAnimationFrame(() => {
831
+ textarea.setSelectionRange(cursorPosition, cursorPosition);
832
+ this.emitChangeAndRefresh();
833
+ });
834
+ });
835
+ }
836
+ /**
837
+ * Check if content has nested alignment divs inside formatting markers.
838
+ */
839
+ hasNestedAlignment(content) {
840
+ const alignmentRegex = /<div style="text-align: (left|center|right);">/;
841
+ return alignmentRegex.test(content);
842
+ }
843
+ /**
844
+ * Get the alignment from nested alignment divs.
845
+ */
846
+ getNestedAlignment(content) {
847
+ const alignmentMatch = content.match(
848
+ /<div style="text-align: (left|center|right);">/
849
+ );
850
+ return alignmentMatch ? alignmentMatch[1] : null;
851
+ }
852
+ /**
853
+ * Remove nested alignment divs from content.
854
+ */
855
+ removeNestedAlignment(content) {
856
+ return content.replace(
857
+ /<div style="text-align: (left|center|right);">([^<]*?)<\/div>/g,
858
+ "$2"
859
+ );
860
+ }
861
+ /**
862
+ * Replace nested alignment with a new alignment.
863
+ */
864
+ replaceNestedAlignment(content, newAlignment) {
865
+ return content.replace(
866
+ /<div style="text-align: (left|center|right);">([^<]*?)<\/div>/g,
867
+ `<div style="text-align: ${newAlignment};">$2</div>`
868
+ );
869
+ }
870
+ /**
871
+ * Wrap content with alignment div, ensuring proper nesting inside formatting markers.
872
+ * Examples:
873
+ * - **text** becomes **<div style="text-align: center;">text</div>**
874
+ * - *text* becomes *<div style="text-align: center;">text</div>*
875
+ * - # text becomes # <div style="text-align: center;">text</div>
876
+ *
877
+ * @param content - the content to wrap
878
+ * @param alignment - 'left', 'center' or 'right'
879
+ */
880
+ wrapContentWithAlignment(content, alignment) {
881
+ const alignmentDiv = (innerContent) => `<div style="text-align: ${alignment};">${innerContent}</div>`;
882
+ const headingMatch = content.match(/^(#{1,6}\s+)(.*)$/);
883
+ if (headingMatch) {
884
+ const headingPrefix = headingMatch[1];
885
+ const headingText = headingMatch[2];
886
+ return headingPrefix + alignmentDiv(headingText);
887
+ }
888
+ const boldMatch = content.match(/^(\*\*)(.+?)(\*\*)$/s);
889
+ if (boldMatch) {
890
+ const innerText = boldMatch[2];
891
+ return `**${alignmentDiv(innerText)}**`;
892
+ }
893
+ const italicMatch = content.match(/^(\*)(.+?)(\*)$/s);
894
+ if (italicMatch) {
895
+ const innerText = italicMatch[2];
896
+ return `*${alignmentDiv(innerText)}*`;
897
+ }
898
+ const linkMatch = content.match(/^(\[)(.+?)(\]\([^)]+\))$/s);
899
+ if (linkMatch) {
900
+ const linkStart = linkMatch[1];
901
+ const linkText = linkMatch[2];
902
+ const linkEnd = linkMatch[3];
903
+ return linkStart + alignmentDiv(linkText) + linkEnd;
904
+ }
905
+ const colorMatch = content.match(
906
+ /^(<span style="color: [^"]+;">)(.+?)(<\/span>)$/s
907
+ );
908
+ if (colorMatch) {
909
+ const colorStart = colorMatch[1];
910
+ const colorText = colorMatch[2];
911
+ const colorEnd = colorMatch[3];
912
+ return colorStart + alignmentDiv(colorText) + colorEnd;
913
+ }
914
+ const listMatch = content.match(/^(\s*(?:[-*+]|\d+\.)\s+)(.*)$/);
915
+ if (listMatch) {
916
+ const listPrefix = listMatch[1];
917
+ const listText = listMatch[2];
918
+ return listPrefix + alignmentDiv(listText);
919
+ }
920
+ return alignmentDiv(content);
921
+ }
522
922
  /**
523
923
  * Toggle inline formatting by wrapping/unwrapping selection or current word.
524
924
  *
@@ -545,9 +945,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
545
945
  const selEnd2 = selStart2 + selected.length;
546
946
  requestAnimationFrame(() => {
547
947
  textarea.setSelectionRange(selStart2, selEnd2);
548
- this.updateActiveFormats();
948
+ this.emitChangeAndRefresh();
549
949
  });
550
- this.dispatchChange();
551
950
  return;
552
951
  }
553
952
  const innerWrapped = selected.startsWith(wrapper) && selected.endsWith(wrapper);
@@ -562,9 +961,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
562
961
  const selEnd2 = selStart2 + selected.length;
563
962
  requestAnimationFrame(() => {
564
963
  textarea.setSelectionRange(selStart2, selEnd2);
565
- this.updateActiveFormats();
964
+ this.emitChangeAndRefresh();
566
965
  });
567
- this.dispatchChange();
568
966
  return;
569
967
  }
570
968
  const wrapped = `${wrapper}${selected || ""}${wrapper}`;
@@ -574,9 +972,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
574
972
  const selEnd = selStart + (selected ? selected.length : 0);
575
973
  requestAnimationFrame(() => {
576
974
  textarea.setSelectionRange(selStart, selEnd);
577
- this.updateActiveFormats();
975
+ this.emitChangeAndRefresh();
578
976
  });
579
- this.dispatchChange();
580
977
  });
581
978
  }
582
979
  /**
@@ -589,6 +986,11 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
589
986
  this.enterPreview();
590
987
  }
591
988
  this.isPreview = !this.isPreview;
989
+ if (this.isPreview) {
990
+ requestAnimationFrame(() => {
991
+ this.scheduleAccessibilityCheck();
992
+ });
993
+ }
592
994
  }
593
995
  /**
594
996
  * Enter preview mode - save current state and remove keyboard listeners from textarea
@@ -647,9 +1049,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
647
1049
  const newCursor = leftBracket2 + textOnly.length;
648
1050
  requestAnimationFrame(() => {
649
1051
  textarea.setSelectionRange(newCursor, newCursor);
650
- this.updateActiveFormats();
1052
+ this.emitChangeAndRefresh();
651
1053
  });
652
- this.dispatchChange();
653
1054
  return;
654
1055
  }
655
1056
  }
@@ -669,9 +1070,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
669
1070
  const newEnd = newStart + textOnly.length;
670
1071
  requestAnimationFrame(() => {
671
1072
  textarea.setSelectionRange(newStart, newEnd);
672
- this.updateActiveFormats();
1073
+ this.emitChangeAndRefresh();
673
1074
  });
674
- this.dispatchChange();
675
1075
  return;
676
1076
  }
677
1077
  const leftBracket = value.lastIndexOf("[", s);
@@ -688,9 +1088,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
688
1088
  const newCursor = leftBracket + textOnly.length;
689
1089
  requestAnimationFrame(() => {
690
1090
  textarea.setSelectionRange(newCursor, newCursor);
691
- this.updateActiveFormats();
1091
+ this.emitChangeAndRefresh();
692
1092
  });
693
- this.dispatchChange();
694
1093
  return;
695
1094
  }
696
1095
  }
@@ -702,9 +1101,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
702
1101
  requestAnimationFrame(() => {
703
1102
  textarea.focus();
704
1103
  textarea.setSelectionRange(cursorPosition, cursorPosition);
705
- this.updateActiveFormats();
1104
+ this.emitChangeAndRefresh();
706
1105
  });
707
- this.dispatchChange();
708
1106
  });
709
1107
  }
710
1108
  /**
@@ -752,6 +1150,23 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
752
1150
  );
753
1151
  activeColor = beforeColorMatch?.[1] || selectedColorMatch?.[1] || "";
754
1152
  }
1153
+ const alignmentRegex = /<div style="text-align: (left|center|right);">/;
1154
+ let alignment = "left";
1155
+ const alignmentMatch = currentLine.match(alignmentRegex);
1156
+ if (alignmentMatch) {
1157
+ alignment = alignmentMatch[1];
1158
+ } else {
1159
+ const beforeCurrentLine = this.value.slice(0, lineStart);
1160
+ const alignmentStartMatch = beforeCurrentLine.match(
1161
+ /.*<div style="text-align: (left|center|right);">[^<]*$/s
1162
+ );
1163
+ if (alignmentStartMatch) {
1164
+ const afterCurrentLine = this.value.slice(lineEnd);
1165
+ if (afterCurrentLine.includes("</div>")) {
1166
+ alignment = alignmentStartMatch[1];
1167
+ }
1168
+ }
1169
+ }
755
1170
  this.activeFormats = {
756
1171
  bold: hasBoldWrap,
757
1172
  italic: hasItalicWrap,
@@ -759,10 +1174,12 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
759
1174
  h1: headingLevel === 1,
760
1175
  h2: headingLevel === 2,
761
1176
  h3: headingLevel === 3,
1177
+ h4: headingLevel === 4,
762
1178
  color: hasColorWrap,
763
1179
  activeColor,
764
1180
  unorderedList: hasUnorderedList,
765
- orderedList: hasOrderedList
1181
+ orderedList: hasOrderedList,
1182
+ alignment
766
1183
  };
767
1184
  }
768
1185
  /**
@@ -880,9 +1297,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
880
1297
  }
881
1298
  requestAnimationFrame(() => {
882
1299
  textarea.setSelectionRange(selStart2, selEnd2);
883
- this.updateActiveFormats();
1300
+ this.emitChangeAndRefresh();
884
1301
  });
885
- this.dispatchChange();
886
1302
  return;
887
1303
  }
888
1304
  const colorRegex = /^<span style="color: ([^"]+)">(.*)<\/span>$/s;
@@ -901,9 +1317,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
901
1317
  const selEnd2 = selStart2 + (existingColor === color ? innerText.length : innerText.length);
902
1318
  requestAnimationFrame(() => {
903
1319
  textarea.setSelectionRange(selStart2, selEnd2);
904
- this.updateActiveFormats();
1320
+ this.emitChangeAndRefresh();
905
1321
  });
906
- this.dispatchChange();
907
1322
  return;
908
1323
  }
909
1324
  const newColorTagOpen = `<span style="color: ${color}">`;
@@ -914,9 +1329,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
914
1329
  const selEnd = selStart + (selected ? selected.length : 4);
915
1330
  requestAnimationFrame(() => {
916
1331
  textarea.setSelectionRange(selStart, selEnd);
917
- this.updateActiveFormats();
1332
+ this.emitChangeAndRefresh();
918
1333
  });
919
- this.dispatchChange();
920
1334
  });
921
1335
  }
922
1336
  /**
@@ -964,54 +1378,58 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
964
1378
  return;
965
1379
  }
966
1380
  const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
967
- const before = value.slice(0, s);
968
- let selected = value.slice(s, e);
969
- const after = value.slice(e);
970
- const colorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/gs;
971
- selected = selected.replace(colorRegex, "$2");
972
- const fullColorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/g;
1381
+ const colorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/g;
973
1382
  let match;
974
- let foundMatch = false;
975
- const searchText = value.slice(
976
- Math.max(0, s - 100),
977
- Math.min(value.length, e + 100)
978
- );
979
- const searchOffset = Math.max(0, s - 100);
980
- match = fullColorRegex.exec(searchText);
1383
+ let newValue = value;
1384
+ let foundSpan = false;
1385
+ colorRegex.lastIndex = 0;
1386
+ match = colorRegex.exec(value);
981
1387
  while (match !== null) {
982
- const matchStart = searchOffset + match.index;
983
- const matchEnd = searchOffset + match.index + match[0].length;
1388
+ const fullMatchStart = match.index;
1389
+ const fullMatchEnd = match.index + match[0].length;
984
1390
  const spanOpenTag = `<span style="color: ${match[1]}">`;
985
- const contentStart = searchOffset + match.index + spanOpenTag.length;
986
- const contentEnd = matchEnd - 7;
987
- if (contentStart <= s && e <= contentEnd) {
988
- const beforeContent = value.slice(contentStart, s);
989
- const afterContent = value.slice(e, contentEnd);
990
- const newContent = beforeContent + selected + afterContent;
991
- this.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
992
- textarea.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
993
- const selStart = matchStart + beforeContent.length;
994
- const selEnd = selStart + selected.length;
1391
+ const contentStart = fullMatchStart + spanOpenTag.length;
1392
+ const contentEnd = fullMatchEnd - 7;
1393
+ const innerContent = match[2];
1394
+ match = colorRegex.exec(value);
1395
+ const selectionOverlaps = s >= contentStart && s < contentEnd || e > contentStart && e <= contentEnd || s <= contentStart && e >= contentEnd;
1396
+ if (selectionOverlaps) {
1397
+ newValue = value.slice(0, fullMatchStart) + innerContent + value.slice(fullMatchEnd);
1398
+ foundSpan = true;
1399
+ this.value = newValue;
1400
+ textarea.value = newValue;
1401
+ let newStart = s;
1402
+ let newEnd = e;
1403
+ if (s >= fullMatchStart) {
1404
+ const spanOpenTagLength = spanOpenTag.length;
1405
+ if (s >= contentStart) {
1406
+ newStart = s - spanOpenTagLength;
1407
+ } else if (s >= fullMatchStart) {
1408
+ newStart = fullMatchStart;
1409
+ }
1410
+ }
1411
+ if (e >= fullMatchStart) {
1412
+ const spanOpenTagLength = spanOpenTag.length;
1413
+ if (e <= contentEnd) {
1414
+ newEnd = e - spanOpenTagLength;
1415
+ } else {
1416
+ newEnd = e - spanOpenTagLength - 7;
1417
+ }
1418
+ }
995
1419
  requestAnimationFrame(() => {
996
- textarea.setSelectionRange(selStart, selEnd);
997
- this.updateActiveFormats();
1420
+ textarea.setSelectionRange(
1421
+ Math.max(0, newStart),
1422
+ Math.max(0, newEnd)
1423
+ );
1424
+ this.emitChangeAndRefresh();
998
1425
  });
999
- this.dispatchChange();
1000
- foundMatch = true;
1001
- break;
1426
+ return;
1002
1427
  }
1003
- match = fullColorRegex.exec(searchText);
1004
1428
  }
1005
- if (!foundMatch) {
1006
- this.value = before + selected + after;
1007
- textarea.value = before + selected + after;
1008
- const selStart = before.length;
1009
- const selEnd = selStart + selected.length;
1429
+ if (!foundSpan) {
1010
1430
  requestAnimationFrame(() => {
1011
- textarea.setSelectionRange(selStart, selEnd);
1012
1431
  this.updateActiveFormats();
1013
1432
  });
1014
- this.dispatchChange();
1015
1433
  }
1016
1434
  });
1017
1435
  }
@@ -1022,6 +1440,7 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
1022
1440
  if (this.activeFormats.h1) return 1;
1023
1441
  if (this.activeFormats.h2) return 2;
1024
1442
  if (this.activeFormats.h3) return 3;
1443
+ if (this.activeFormats.h4) return 4;
1025
1444
  return 0;
1026
1445
  }
1027
1446
  /**
@@ -1089,10 +1508,14 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
1089
1508
  const textarea = this.textareaEl?.shadowRoot?.querySelector(
1090
1509
  "textarea"
1091
1510
  );
1092
- if (!textarea) return false;
1511
+ if (!textarea) {
1512
+ return false;
1513
+ }
1093
1514
  const start = textarea.selectionStart ?? 0;
1094
1515
  const end = textarea.selectionEnd ?? 0;
1095
- if (start !== end) return false;
1516
+ if (start !== end) {
1517
+ return false;
1518
+ }
1096
1519
  const value = this.value;
1097
1520
  const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1098
1521
  let lineEnd = value.indexOf("\n", start);
@@ -1117,9 +1540,8 @@ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
1117
1540
  const newCursor2 = before2.length;
1118
1541
  requestAnimationFrame(() => {
1119
1542
  textarea.setSelectionRange(newCursor2, newCursor2);
1120
- this.updateActiveFormats();
1543
+ this.emitChangeAndRefresh();
1121
1544
  });
1122
- this.dispatchChange();
1123
1545
  return true;
1124
1546
  }
1125
1547
  const before = value.slice(0, start);
@@ -1130,11 +1552,8 @@ ${indent}${marker} `;
1130
1552
  this.value = newValue;
1131
1553
  textarea.value = newValue;
1132
1554
  const newCursor = start + prefix.length;
1133
- requestAnimationFrame(() => {
1134
- textarea.setSelectionRange(newCursor, newCursor);
1135
- this.updateActiveFormats();
1136
- });
1137
- this.dispatchChange();
1555
+ textarea.setSelectionRange(newCursor, newCursor);
1556
+ this.emitChangeAndRefresh();
1138
1557
  return true;
1139
1558
  }
1140
1559
  if (orderedMatch) {
@@ -1150,30 +1569,39 @@ ${indent}${marker} `;
1150
1569
  const newCursor2 = before2.length;
1151
1570
  requestAnimationFrame(() => {
1152
1571
  textarea.setSelectionRange(newCursor2, newCursor2);
1153
- this.updateActiveFormats();
1572
+ this.emitChangeAndRefresh();
1154
1573
  });
1155
- this.dispatchChange();
1156
1574
  return true;
1157
1575
  }
1158
- const nextNumber = parseInt(numberStr, 10) + 1;
1576
+ const currentNumber = parseInt(numberStr, 10);
1577
+ const nextNumber = currentNumber + 1;
1159
1578
  const before = value.slice(0, start);
1160
1579
  const after = value.slice(start);
1161
1580
  const prefix = `
1162
1581
  ${indent}${nextNumber}. `;
1163
- const newValue = before + prefix + after;
1164
- const renumberedValue = this.renumberOrderedListItems(
1165
- newValue,
1166
- start + prefix.length,
1167
- indent
1168
- );
1169
- this.value = renumberedValue;
1170
- textarea.value = renumberedValue;
1582
+ let newValue = before + prefix + after;
1583
+ const lines = newValue.split("\n");
1584
+ const insertLineIndex = before.split("\n").length;
1585
+ const renumberStartIndex = insertLineIndex + 1;
1586
+ let currentNum = nextNumber + 1;
1587
+ const orderedRegex = /^(\s*)(\d+)\.\s+(.*)$/;
1588
+ for (let i = renumberStartIndex; i < lines.length; i++) {
1589
+ const line = lines[i];
1590
+ const match = line.match(orderedRegex);
1591
+ if (match && match[1] === indent) {
1592
+ const content2 = match[3];
1593
+ lines[i] = `${indent}${currentNum}. ${content2}`;
1594
+ currentNum++;
1595
+ } else if (match && match[1].length < indent.length) {
1596
+ break;
1597
+ }
1598
+ }
1599
+ newValue = lines.join("\n");
1600
+ this.value = newValue;
1601
+ textarea.value = newValue;
1171
1602
  const newCursor = start + prefix.length;
1172
- requestAnimationFrame(() => {
1173
- textarea.setSelectionRange(newCursor, newCursor);
1174
- this.updateActiveFormats();
1175
- });
1176
- this.dispatchChange();
1603
+ textarea.setSelectionRange(newCursor, newCursor);
1604
+ this.emitChangeAndRefresh();
1177
1605
  return true;
1178
1606
  }
1179
1607
  return false;
@@ -1186,10 +1614,14 @@ ${indent}${nextNumber}. `;
1186
1614
  const textarea = this.textareaEl?.shadowRoot?.querySelector(
1187
1615
  "textarea"
1188
1616
  );
1189
- if (!textarea) return false;
1617
+ if (!textarea) {
1618
+ return false;
1619
+ }
1190
1620
  const start = textarea.selectionStart ?? 0;
1191
1621
  const end = textarea.selectionEnd ?? 0;
1192
- if (start !== end) return false;
1622
+ if (start !== end) {
1623
+ return false;
1624
+ }
1193
1625
  const value = this.value;
1194
1626
  const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1195
1627
  let lineEnd = value.indexOf("\n", start);
@@ -1215,21 +1647,85 @@ ${indent}${nextNumber}. `;
1215
1647
  } else if (orderedMatch) {
1216
1648
  const currentIndent = orderedMatch[1] ?? "";
1217
1649
  const content = orderedMatch[3] ?? "";
1218
- newLine = `${currentIndent}${indent}1. ${content}`;
1219
- newCursorOffset = indent.length;
1650
+ if (content.trim() === "") {
1651
+ newLine = currentLine;
1652
+ const nestedLine = `${currentIndent}${indent}1. `;
1653
+ let newValue2 = before + newLine + "\n" + nestedLine + after;
1654
+ const parentIndent = currentIndent;
1655
+ const lines = newValue2.split("\n");
1656
+ const currentLineIndex = Math.floor(lineStart / (newValue2.indexOf("\n") + 1)) || newValue2.slice(0, lineStart).split("\n").length - 1;
1657
+ const currentLineMatch = lines[currentLineIndex]?.match(
1658
+ /^(\s*)(\d+)\.\s*(.*)$/
1659
+ );
1660
+ const nextExpectedNumber = currentLineMatch ? parseInt(currentLineMatch[2], 10) : 1;
1661
+ const orderedRegex = /^(\s*)(\d+)\.\s*(.*)$/;
1662
+ let currentNumber = nextExpectedNumber;
1663
+ for (let i = currentLineIndex + 2; i < lines.length; i++) {
1664
+ const line = lines[i];
1665
+ const orderedMatch2 = line.match(orderedRegex);
1666
+ if (orderedMatch2 && orderedMatch2[1] === parentIndent) {
1667
+ const content2 = orderedMatch2[3];
1668
+ lines[i] = `${parentIndent}${currentNumber}. ${content2}`;
1669
+ currentNumber++;
1670
+ } else if (orderedMatch2 && orderedMatch2[1].length < parentIndent.length) {
1671
+ break;
1672
+ }
1673
+ }
1674
+ newValue2 = lines.join("\n");
1675
+ const newCursor2 = lineStart + newLine.length + 1 + nestedLine.length;
1676
+ this.value = newValue2;
1677
+ textarea.value = newValue2;
1678
+ requestAnimationFrame(() => {
1679
+ textarea.setSelectionRange(newCursor2, newCursor2);
1680
+ this.emitChangeAndRefresh();
1681
+ });
1682
+ return true;
1683
+ } else {
1684
+ const newIndent = currentIndent + indent;
1685
+ let newNumber = 1;
1686
+ const beforeText = value.slice(0, lineStart);
1687
+ const lines = beforeText.split("\n");
1688
+ for (let i = lines.length - 1; i >= 0; i--) {
1689
+ const line = lines[i];
1690
+ const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1691
+ if (match && match[1] === newIndent) {
1692
+ newNumber = parseInt(match[2], 10) + 1;
1693
+ break;
1694
+ } else if (match && match[1].length < newIndent.length) {
1695
+ break;
1696
+ }
1697
+ }
1698
+ newLine = `${newIndent}${newNumber}. ${content}`;
1699
+ newCursorOffset = indent.length;
1700
+ }
1220
1701
  } else {
1221
1702
  return false;
1222
1703
  }
1223
- const newValue = before + newLine + after;
1704
+ let newValue = before + newLine + after;
1705
+ if (orderedMatch) {
1706
+ const newIndent = (orderedMatch[1] ?? "") + indent;
1707
+ newValue = this.renumberOrderedListItems(
1708
+ newValue,
1709
+ lineStart + newLine.length,
1710
+ // Start renumbering after the current line
1711
+ newIndent
1712
+ // Use the new indentation level
1713
+ );
1714
+ const parentIndent = orderedMatch[1] ?? "";
1715
+ newValue = this.renumberOrderedListItems(
1716
+ newValue,
1717
+ lineStart,
1718
+ // Start from this line for parent level
1719
+ parentIndent
1720
+ // Use the parent level indentation
1721
+ );
1722
+ }
1224
1723
  this.value = newValue;
1225
1724
  textarea.value = newValue;
1226
1725
  const cursorInLine = start - lineStart;
1227
1726
  const newCursor = lineStart + cursorInLine + newCursorOffset;
1228
- requestAnimationFrame(() => {
1229
- textarea.setSelectionRange(newCursor, newCursor);
1230
- this.updateActiveFormats();
1231
- });
1232
- this.dispatchChange();
1727
+ textarea.setSelectionRange(newCursor, newCursor);
1728
+ this.emitChangeAndRefresh();
1233
1729
  return true;
1234
1730
  }
1235
1731
  /**
@@ -1308,9 +1804,8 @@ ${indent}${nextNumber}. `;
1308
1804
  );
1309
1805
  requestAnimationFrame(() => {
1310
1806
  textarea.setSelectionRange(newCursor, newCursor);
1311
- this.updateActiveFormats();
1807
+ this.emitChangeAndRefresh();
1312
1808
  });
1313
- this.dispatchChange();
1314
1809
  return true;
1315
1810
  }
1316
1811
  /**
@@ -1437,32 +1932,52 @@ ${indent}${nextNumber}. `;
1437
1932
  markerEndPosition = indent.length + numberStr.length + 2;
1438
1933
  hasContent = content.trim().length > 0;
1439
1934
  }
1440
- if (cursorPositionInLine === markerEndPosition && !hasContent) {
1935
+ const isAtOrAfterMarker = cursorPositionInLine >= markerEndPosition;
1936
+ if (isAtOrAfterMarker && !hasContent) {
1441
1937
  this.saveUndoStateBeforeChange();
1442
1938
  const before = value.slice(0, lineStart);
1443
- const after = value.slice(
1444
- lineEnd === value.length ? lineEnd : lineEnd + 1
1445
- );
1446
- let newValue = before + after;
1939
+ const after = value.slice(lineEnd);
1940
+ let newValue;
1941
+ if (lineEnd === value.length) {
1942
+ newValue = before.endsWith("\n") ? before.slice(0, -1) : before;
1943
+ } else {
1944
+ newValue = before + after.slice(1);
1945
+ }
1447
1946
  if (orderedMatch) {
1448
1947
  const indent = orderedMatch[1] ?? "";
1449
- newValue = this.renumberOrderedListItems(
1450
- newValue,
1451
- before.length,
1452
- indent
1453
- );
1948
+ const lines = newValue.split("\n");
1949
+ const startLineIndex = Math.max(0, before.split("\n").length - 1);
1950
+ let nextNumber = 1;
1951
+ for (let i = startLineIndex - 1; i >= 0; i--) {
1952
+ const line = lines[i];
1953
+ const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1954
+ if (match && match[1] === indent) {
1955
+ nextNumber = parseInt(match[2], 10) + 1;
1956
+ break;
1957
+ }
1958
+ }
1959
+ for (let i = startLineIndex; i < lines.length; i++) {
1960
+ const line = lines[i];
1961
+ const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1962
+ if (match && match[1] === indent) {
1963
+ lines[i] = `${indent}${nextNumber}. ${match[3]}`;
1964
+ nextNumber++;
1965
+ } else if (match && match[1].length < indent.length) {
1966
+ break;
1967
+ }
1968
+ }
1969
+ newValue = lines.join("\n");
1454
1970
  }
1455
1971
  this.value = newValue;
1456
1972
  textarea.value = newValue;
1457
1973
  let newCursor = before.length;
1458
- if (before.endsWith("\n") && before.length > 1) {
1974
+ if (newCursor > 0 && before.endsWith("\n")) {
1459
1975
  newCursor = before.length - 1;
1460
1976
  }
1461
1977
  requestAnimationFrame(() => {
1462
1978
  textarea.setSelectionRange(newCursor, newCursor);
1463
- this.updateActiveFormats();
1979
+ this.emitChangeAndRefresh();
1464
1980
  });
1465
- this.dispatchChange();
1466
1981
  return true;
1467
1982
  }
1468
1983
  return false;
@@ -1495,10 +2010,9 @@ ${indent}${nextNumber}. `;
1495
2010
  previousState.selection.end
1496
2011
  );
1497
2012
  }
1498
- this.updateActiveFormats();
2013
+ this.emitChangeAndRefresh();
1499
2014
  this.isUndoRedoAction = false;
1500
2015
  });
1501
- this.dispatchChange();
1502
2016
  }
1503
2017
  /**
1504
2018
  * Perform a redo operation, reapplying a previously undone state.
@@ -1527,10 +2041,9 @@ ${indent}${nextNumber}. `;
1527
2041
  nextState.selection.end
1528
2042
  );
1529
2043
  }
1530
- this.updateActiveFormats();
2044
+ this.emitChangeAndRefresh();
1531
2045
  this.isUndoRedoAction = false;
1532
2046
  });
1533
- this.dispatchChange();
1534
2047
  }
1535
2048
  connectedCallback() {
1536
2049
  super.connectedCallback();
@@ -1542,14 +2055,26 @@ ${indent}${nextNumber}. `;
1542
2055
  this.saveInitialUndoState();
1543
2056
  this.updateActiveFormats();
1544
2057
  this.addKeyboardListeners();
2058
+ if (this.isPreview && this.value.trim()) {
2059
+ this.scheduleAccessibilityCheck();
2060
+ }
1545
2061
  });
1546
2062
  }
2063
+ updated(changedProperties) {
2064
+ super.updated(changedProperties);
2065
+ if (changedProperties.has("previewBackgroundColor") && this.isPreview && this.value.trim()) {
2066
+ this.scheduleAccessibilityCheck();
2067
+ }
2068
+ }
1547
2069
  disconnectedCallback() {
1548
2070
  super.disconnectedCallback();
1549
2071
  document.removeEventListener("click", this.handleOutsideClick);
1550
2072
  if (this.undoTimeout) {
1551
2073
  clearTimeout(this.undoTimeout);
1552
2074
  }
2075
+ if (this.accessibilityCheckTimeout) {
2076
+ clearTimeout(this.accessibilityCheckTimeout);
2077
+ }
1553
2078
  this.removeKeyboardListeners();
1554
2079
  this.savedSelectionForPreview = null;
1555
2080
  }
@@ -1605,7 +2130,7 @@ ${indent}${nextNumber}. `;
1605
2130
  }
1606
2131
  toolbarTemplate() {
1607
2132
  return x`
1608
- <div class="flex items-center gap-2">
2133
+ <div class="flex items-center gap-1">
1609
2134
  <div class=${cn(this.styles().headingMenu())}>
1610
2135
  <!-- Heading -->
1611
2136
  <lukso-tooltip text="Heading options" placement="top">
@@ -1615,6 +2140,7 @@ ${indent}${nextNumber}. `;
1615
2140
  e.stopPropagation();
1616
2141
  this.isColorDropdownOpen = false;
1617
2142
  this.isListDropdownOpen = false;
2143
+ this.isAlignmentDropdownOpen = false;
1618
2144
  this.isHeadingDropdownOpen = !this.isHeadingDropdownOpen;
1619
2145
  }}
1620
2146
  aria-expanded=${this.isHeadingDropdownOpen ? "true" : "false"}
@@ -1688,6 +2214,18 @@ ${indent}${nextNumber}. `;
1688
2214
  >
1689
2215
  Heading 3
1690
2216
  </lukso-dropdown-option>
2217
+ <lukso-dropdown-option
2218
+ ?is-selected=${this.getActiveHeadingLevel() === 4}
2219
+ @click=${(e) => {
2220
+ e.stopPropagation();
2221
+ this.restoreFocusAndSelection();
2222
+ this.applyHeading(4);
2223
+ this.isHeadingDropdownOpen = false;
2224
+ }}
2225
+ size="medium"
2226
+ >
2227
+ Heading 4
2228
+ </lukso-dropdown-option>
1691
2229
  </lukso-dropdown>
1692
2230
  </div>
1693
2231
 
@@ -1717,6 +2255,7 @@ ${indent}${nextNumber}. `;
1717
2255
  this.restoreFocusAndSelection();
1718
2256
  this.isHeadingDropdownOpen = false;
1719
2257
  this.isColorDropdownOpen = false;
2258
+ this.isAlignmentDropdownOpen = false;
1720
2259
  this.isListDropdownOpen = !this.isListDropdownOpen;
1721
2260
  }}
1722
2261
  aria-expanded=${this.isListDropdownOpen ? "true" : "false"}
@@ -1789,6 +2328,108 @@ ${indent}${nextNumber}. `;
1789
2328
  this.activeFormats.link
1790
2329
  )}
1791
2330
 
2331
+ <!-- Text Alignment -->
2332
+ <div class=${this.styles().alignmentMenu()}>
2333
+ <lukso-tooltip text="Text alignment" placement="top">
2334
+ <lukso-button
2335
+ id=${this.alignmentTriggerId}
2336
+ @click=${(e) => {
2337
+ e.stopPropagation();
2338
+ this.restoreFocusAndSelection();
2339
+ this.isHeadingDropdownOpen = false;
2340
+ this.isColorDropdownOpen = false;
2341
+ this.isListDropdownOpen = false;
2342
+ this.isAlignmentDropdownOpen = !this.isAlignmentDropdownOpen;
2343
+ }}
2344
+ aria-expanded=${this.isAlignmentDropdownOpen ? "true" : "false"}
2345
+ aria-label="Text alignment"
2346
+ variant="secondary"
2347
+ size="small"
2348
+ custom-class=${this.toolbarButton({
2349
+ active: this.activeFormats.alignment !== "left"
2350
+ })}
2351
+ is-icon
2352
+ >
2353
+ <lukso-icon
2354
+ name=${this.getAlignmentIcon()}
2355
+ size="small"
2356
+ pack="vuesax"
2357
+ variant="linear"
2358
+ ></lukso-icon>
2359
+ </lukso-button>
2360
+ </lukso-tooltip>
2361
+ <lukso-dropdown
2362
+ id="alignmentDropdown"
2363
+ trigger-id=""
2364
+ size="medium"
2365
+ ?is-open=${this.isAlignmentDropdownOpen}
2366
+ >
2367
+ <lukso-dropdown-option
2368
+ ?is-selected=${this.activeFormats.alignment === "left"}
2369
+ @click=${(e) => {
2370
+ e.stopPropagation();
2371
+ this.restoreFocusAndSelection();
2372
+ this.applyAlignment("left");
2373
+ this.isAlignmentDropdownOpen = false;
2374
+ }}
2375
+ size="medium"
2376
+ aria-label="Align left"
2377
+ >
2378
+ <div style="display: flex; align-items: center; gap: 8px;">
2379
+ <lukso-icon
2380
+ name="textalign-left"
2381
+ size="small"
2382
+ pack="vuesax"
2383
+ variant="linear"
2384
+ ></lukso-icon>
2385
+ Left
2386
+ </div>
2387
+ </lukso-dropdown-option>
2388
+ <lukso-dropdown-option
2389
+ ?is-selected=${this.activeFormats.alignment === "center"}
2390
+ @click=${(e) => {
2391
+ e.stopPropagation();
2392
+ this.restoreFocusAndSelection();
2393
+ this.applyAlignment("center");
2394
+ this.isAlignmentDropdownOpen = false;
2395
+ }}
2396
+ size="medium"
2397
+ aria-label="Align center"
2398
+ >
2399
+ <div style="display: flex; align-items: center; gap: 8px;">
2400
+ <lukso-icon
2401
+ name="textalign-center"
2402
+ size="small"
2403
+ pack="vuesax"
2404
+ variant="linear"
2405
+ ></lukso-icon>
2406
+ Center
2407
+ </div>
2408
+ </lukso-dropdown-option>
2409
+ <lukso-dropdown-option
2410
+ ?is-selected=${this.activeFormats.alignment === "right"}
2411
+ @click=${(e) => {
2412
+ e.stopPropagation();
2413
+ this.restoreFocusAndSelection();
2414
+ this.applyAlignment("right");
2415
+ this.isAlignmentDropdownOpen = false;
2416
+ }}
2417
+ size="medium"
2418
+ aria-label="Align right"
2419
+ >
2420
+ <div style="display: flex; align-items: center; gap: 8px;">
2421
+ <lukso-icon
2422
+ name="textalign-right"
2423
+ size="small"
2424
+ pack="vuesax"
2425
+ variant="linear"
2426
+ ></lukso-icon>
2427
+ Right
2428
+ </div>
2429
+ </lukso-dropdown-option>
2430
+ </lukso-dropdown>
2431
+ </div>
2432
+
1792
2433
  <!-- Color -->
1793
2434
  <div class=${this.styles().colorMenu()}>
1794
2435
  <lukso-tooltip text="Text color" placement="top">
@@ -1799,6 +2440,7 @@ ${indent}${nextNumber}. `;
1799
2440
  this.restoreFocusAndSelection();
1800
2441
  this.isHeadingDropdownOpen = false;
1801
2442
  this.isListDropdownOpen = false;
2443
+ this.isAlignmentDropdownOpen = false;
1802
2444
  if (!this.isColorDropdownOpen) {
1803
2445
  const ta = this.textareaEl?.shadowRoot?.querySelector("textarea");
1804
2446
  if (ta) {
@@ -1844,6 +2486,7 @@ ${indent}${nextNumber}. `;
1844
2486
  this.isColorDropdownOpen = false;
1845
2487
  }}
1846
2488
  type="button"
2489
+ aria-label="Clear color"
1847
2490
  >
1848
2491
  Clear
1849
2492
  </button>` : E}
@@ -1855,6 +2498,7 @@ ${indent}${nextNumber}. `;
1855
2498
  style="background-color: ${color}"
1856
2499
  title=${color}
1857
2500
  aria-pressed=${this.activeFormats.activeColor === color ? "true" : "false"}
2501
+ aria-label="Color ${color}"
1858
2502
  @click=${(e) => {
1859
2503
  e.stopPropagation();
1860
2504
  this.selectColor(color);
@@ -1871,47 +2515,88 @@ ${indent}${nextNumber}. `;
1871
2515
  </div>
1872
2516
  `;
1873
2517
  }
2518
+ accessibilityIndicatorTemplate() {
2519
+ if (!this.hasAccessibilityViolations || this.accessibilityViolations.length === 0 || !this.isPreview) {
2520
+ return E;
2521
+ }
2522
+ const tooltipText = formatViolationsForTooltip(this.accessibilityViolations);
2523
+ return x`
2524
+ <div
2525
+ class="accessibility-indicator has-violations absolute top-2 right-2 z-10"
2526
+ style="pointer-events: auto;"
2527
+ >
2528
+ <lukso-tooltip placement="left">
2529
+ <div slot="text">
2530
+ <div .innerHTML=${tooltipText}></div>
2531
+ </div>
2532
+ <div
2533
+ class="flex cursor-help"
2534
+ role="alert"
2535
+ aria-label="Accessibility violations found"
2536
+ >
2537
+ <lukso-icon
2538
+ name="warning-2"
2539
+ size="small"
2540
+ color="red-65"
2541
+ pack="vuesax"
2542
+ variant="linear"
2543
+ ></lukso-icon>
2544
+ </div>
2545
+ </lukso-tooltip>
2546
+ </div>
2547
+ `;
2548
+ }
1874
2549
  render() {
1875
2550
  const { wrapper, header, toolbar, area, editor, preview } = this.styles({
1876
2551
  isFullWidth: this.isFullWidth
1877
2552
  });
2553
+ if (!this.previewBackgroundColor) {
2554
+ this.previewBackgroundColor = DEFAULT_PREVIEW_BACKGROUND_COLOR;
2555
+ }
1878
2556
  return x`
1879
2557
  <div class=${wrapper()}>
1880
2558
  ${this.labelTemplate()} ${this.descriptionTemplate()}
1881
2559
 
1882
- <div class=${header()}>
1883
- <div class=${toolbar()}>${this.toolbarTemplate()}</div>
1884
- ${this.buttonTemplate(
2560
+ <div class="flex flex-col gap-2">
2561
+ <div class=${header()}>
2562
+ <div class=${toolbar()}>${this.toolbarTemplate()}</div>
2563
+ ${this.buttonTemplate(
1885
2564
  "eye",
1886
2565
  () => this.togglePreview(),
1887
2566
  "Toggle preview",
1888
2567
  this.isPreview
1889
2568
  )}
1890
- </div>
2569
+ </div>
1891
2570
 
1892
- <div class=${area()}>
1893
- ${!this.isPreview ? x`<div class=${editor()}>
1894
- <lukso-textarea
1895
- .value=${this.value}
1896
- name=${this.name ? this.name : E}
1897
- size=${this.size ? this.size : E}
1898
- rows=${this.rows ? this.rows : E}
1899
- placeholder=${this.placeholder ? this.placeholder : E}
1900
- error=${this.error ? this.error : E}
1901
- ?is-full-width=${true}
1902
- ?is-disabled=${this.isDisabled}
1903
- ?is-readonly=${this.isReadonly}
1904
- ?is-non-resizable=${this.isNonResizable}
1905
- @on-input=${this.handleTextareaInput}
1906
- @on-key-up=${this.handleTextareaKeyUp}
1907
- @on-input-click=${this.handleTextareaClick}
1908
- ></lukso-textarea>
1909
- </div>` : x`<div class=${preview()}>
1910
- <lukso-markdown
1911
- value=${this.value}
1912
- prose-classes="prose prose-base prose-gray"
1913
- ></lukso-markdown>
1914
- </div>`}
2571
+ <div class=${area()}>
2572
+ ${!this.isPreview ? x`<div class=${editor()}>
2573
+ <lukso-textarea
2574
+ .value=${this.value}
2575
+ name=${this.name ? this.name : E}
2576
+ size=${this.size ? this.size : E}
2577
+ rows=${this.rows ? this.rows : E}
2578
+ placeholder=${this.placeholder ? this.placeholder : E}
2579
+ error=${this.error ? this.error : E}
2580
+ ?is-full-width=${true}
2581
+ ?is-disabled=${this.isDisabled}
2582
+ ?is-readonly=${this.isReadonly}
2583
+ ?is-non-resizable=${this.isNonResizable}
2584
+ @on-input=${this.handleTextareaInput}
2585
+ @on-key-up=${this.handleTextareaKeyUp}
2586
+ @on-input-click=${this.handleTextareaClick}
2587
+ ></lukso-textarea>
2588
+ ${this.accessibilityIndicatorTemplate()}
2589
+ </div>` : x`<div
2590
+ class=${preview()}
2591
+ style="background-color: ${this.previewBackgroundColor};"
2592
+ >
2593
+ <lukso-markdown
2594
+ value=${this.value}
2595
+ prose-classes="prose prose-base prose-gray"
2596
+ ></lukso-markdown>
2597
+ ${this.accessibilityIndicatorTemplate()}
2598
+ </div>`}
2599
+ </div>
1915
2600
  </div>
1916
2601
  </div>
1917
2602
  `;
@@ -1921,7 +2606,7 @@ __decorateClass([
1921
2606
  n({ type: String })
1922
2607
  ], LuksoMarkdownEditor.prototype, "value", 2);
1923
2608
  __decorateClass([
1924
- n({ type: String })
2609
+ n({ type: String, reflect: true })
1925
2610
  ], LuksoMarkdownEditor.prototype, "name", 2);
1926
2611
  __decorateClass([
1927
2612
  n({ type: String })
@@ -1959,6 +2644,13 @@ __decorateClass([
1959
2644
  __decorateClass([
1960
2645
  n({ type: String })
1961
2646
  ], LuksoMarkdownEditor.prototype, "placeholder", 2);
2647
+ __decorateClass([
2648
+ n({
2649
+ type: String,
2650
+ attribute: "preview-background-color",
2651
+ reflect: true
2652
+ })
2653
+ ], LuksoMarkdownEditor.prototype, "previewBackgroundColor", 2);
1962
2654
  __decorateClass([
1963
2655
  r()
1964
2656
  ], LuksoMarkdownEditor.prototype, "savedSelectionForPreview", 2);
@@ -1971,6 +2663,9 @@ __decorateClass([
1971
2663
  __decorateClass([
1972
2664
  r()
1973
2665
  ], LuksoMarkdownEditor.prototype, "isListDropdownOpen", 2);
2666
+ __decorateClass([
2667
+ r()
2668
+ ], LuksoMarkdownEditor.prototype, "isAlignmentDropdownOpen", 2);
1974
2669
  __decorateClass([
1975
2670
  r()
1976
2671
  ], LuksoMarkdownEditor.prototype, "currentSelection", 2);
@@ -1980,6 +2675,12 @@ __decorateClass([
1980
2675
  __decorateClass([
1981
2676
  r()
1982
2677
  ], LuksoMarkdownEditor.prototype, "activeFormats", 2);
2678
+ __decorateClass([
2679
+ r()
2680
+ ], LuksoMarkdownEditor.prototype, "accessibilityViolations", 2);
2681
+ __decorateClass([
2682
+ r()
2683
+ ], LuksoMarkdownEditor.prototype, "hasAccessibilityViolations", 2);
1983
2684
  __decorateClass([
1984
2685
  e("lukso-textarea")
1985
2686
  ], LuksoMarkdownEditor.prototype, "textareaEl", 2);