quasar-ui-danx 0.4.99 → 0.5.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 (90) hide show
  1. package/dist/danx.es.js +17884 -12732
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +192 -118
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +11 -2
  7. package/scripts/publish.sh +76 -0
  8. package/src/components/Utility/Code/CodeViewer.vue +31 -14
  9. package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
  10. package/src/components/Utility/Code/CodeViewerFooter.vue +1 -1
  11. package/src/components/Utility/Code/LanguageBadge.vue +278 -5
  12. package/src/components/Utility/Code/MarkdownContent.vue +160 -6
  13. package/src/components/Utility/Code/index.ts +3 -0
  14. package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
  15. package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
  16. package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
  17. package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
  18. package/src/components/Utility/Markdown/MarkdownEditor.vue +228 -0
  19. package/src/components/Utility/Markdown/MarkdownEditorContent.vue +235 -0
  20. package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
  21. package/src/components/Utility/Markdown/TablePopover.vue +420 -0
  22. package/src/components/Utility/Markdown/index.ts +11 -0
  23. package/src/components/Utility/Markdown/types.ts +27 -0
  24. package/src/components/Utility/index.ts +1 -0
  25. package/src/composables/index.ts +1 -0
  26. package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
  27. package/src/composables/markdown/features/useBlockquotes.ts +248 -0
  28. package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
  29. package/src/composables/markdown/features/useCodeBlocks.spec.ts +779 -0
  30. package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
  31. package/src/composables/markdown/features/useContextMenu.ts +444 -0
  32. package/src/composables/markdown/features/useFocusTracking.ts +116 -0
  33. package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
  34. package/src/composables/markdown/features/useHeadings.ts +290 -0
  35. package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
  36. package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
  37. package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
  38. package/src/composables/markdown/features/useLinks.spec.ts +369 -0
  39. package/src/composables/markdown/features/useLinks.ts +374 -0
  40. package/src/composables/markdown/features/useLists.spec.ts +834 -0
  41. package/src/composables/markdown/features/useLists.ts +747 -0
  42. package/src/composables/markdown/features/usePopoverManager.ts +181 -0
  43. package/src/composables/markdown/features/useTables.spec.ts +1601 -0
  44. package/src/composables/markdown/features/useTables.ts +1107 -0
  45. package/src/composables/markdown/index.ts +16 -0
  46. package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
  47. package/src/composables/markdown/useMarkdownEditor.ts +1068 -0
  48. package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
  49. package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
  50. package/src/composables/markdown/useMarkdownSelection.ts +219 -0
  51. package/src/composables/markdown/useMarkdownSync.ts +549 -0
  52. package/src/composables/useCodeViewerEditor.spec.ts +655 -0
  53. package/src/composables/useCodeViewerEditor.ts +174 -20
  54. package/src/helpers/formats/index.ts +1 -1
  55. package/src/helpers/formats/markdown/escapeHtml.ts +15 -0
  56. package/src/helpers/formats/markdown/escapeSequences.ts +60 -0
  57. package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
  58. package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
  59. package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +412 -0
  60. package/src/helpers/formats/markdown/index.ts +92 -0
  61. package/src/helpers/formats/markdown/linePatterns.spec.ts +495 -0
  62. package/src/helpers/formats/markdown/linePatterns.ts +172 -0
  63. package/src/helpers/formats/markdown/parseInline.ts +124 -0
  64. package/src/helpers/formats/markdown/render/index.ts +92 -0
  65. package/src/helpers/formats/markdown/render/renderFootnotes.ts +30 -0
  66. package/src/helpers/formats/markdown/render/renderList.ts +69 -0
  67. package/src/helpers/formats/markdown/render/renderTable.ts +38 -0
  68. package/src/helpers/formats/markdown/state.ts +58 -0
  69. package/src/helpers/formats/markdown/tokenize/extractDefinitions.ts +39 -0
  70. package/src/helpers/formats/markdown/tokenize/index.ts +139 -0
  71. package/src/helpers/formats/markdown/tokenize/parseBlockquote.ts +34 -0
  72. package/src/helpers/formats/markdown/tokenize/parseCodeBlock.ts +85 -0
  73. package/src/helpers/formats/markdown/tokenize/parseDefinitionList.ts +88 -0
  74. package/src/helpers/formats/markdown/tokenize/parseHeading.ts +65 -0
  75. package/src/helpers/formats/markdown/tokenize/parseHorizontalRule.ts +22 -0
  76. package/src/helpers/formats/markdown/tokenize/parseList.ts +119 -0
  77. package/src/helpers/formats/markdown/tokenize/parseParagraph.ts +59 -0
  78. package/src/helpers/formats/markdown/tokenize/parseTable.ts +70 -0
  79. package/src/helpers/formats/markdown/tokenize/parseTaskList.ts +47 -0
  80. package/src/helpers/formats/markdown/tokenize/utils.ts +25 -0
  81. package/src/helpers/formats/markdown/types.ts +63 -0
  82. package/src/styles/danx.scss +1 -0
  83. package/src/styles/themes/danx/markdown.scss +96 -0
  84. package/src/test/helpers/editorTestUtils.spec.ts +296 -0
  85. package/src/test/helpers/editorTestUtils.ts +253 -0
  86. package/src/test/helpers/index.ts +1 -0
  87. package/src/test/setup.test.ts +12 -0
  88. package/src/test/setup.ts +12 -0
  89. package/vitest.config.ts +19 -0
  90. package/src/helpers/formats/renderMarkdown.ts +0 -338
@@ -0,0 +1,290 @@
1
+ import { Ref } from "vue";
2
+ import { UseMarkdownSelectionReturn } from "../useMarkdownSelection";
3
+ import { detectHeadingPattern } from "../../../helpers/formats/markdown/linePatterns";
4
+
5
+ /**
6
+ * Options for useHeadings composable
7
+ */
8
+ export interface UseHeadingsOptions {
9
+ contentRef: Ref<HTMLElement | null>;
10
+ selection: UseMarkdownSelectionReturn;
11
+ onContentChange: () => void;
12
+ }
13
+
14
+ /**
15
+ * Return type for useHeadings composable
16
+ */
17
+ export interface UseHeadingsReturn {
18
+ /** Set heading level (0 = paragraph, 1-6 = h1-h6) */
19
+ setHeadingLevel: (level: 0 | 1 | 2 | 3 | 4 | 5 | 6) => void;
20
+ /** Increase heading level (P -> H6 -> H5 -> ... -> H1) */
21
+ increaseHeadingLevel: () => void;
22
+ /** Decrease heading level (H1 -> H2 -> ... -> H6 -> P) */
23
+ decreaseHeadingLevel: () => void;
24
+ /** Get current heading level (0 for paragraph) */
25
+ getCurrentHeadingLevel: () => number;
26
+ /** Check for heading pattern (e.g., "# ") and convert if matched */
27
+ checkAndConvertHeadingPattern: () => boolean;
28
+ }
29
+
30
+ /**
31
+ * Heading level hierarchy
32
+ * Level 0 = paragraph (P)
33
+ * Level 1-6 = H1-H6
34
+ */
35
+ type HeadingLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
36
+
37
+ /**
38
+ * Map tag names to heading levels
39
+ */
40
+ const TAG_TO_LEVEL: Record<string, HeadingLevel> = {
41
+ P: 0,
42
+ DIV: 0, // Browsers often create DIV instead of P when pressing Enter
43
+ H1: 1,
44
+ H2: 2,
45
+ H3: 3,
46
+ H4: 4,
47
+ H5: 5,
48
+ H6: 6
49
+ };
50
+
51
+ /**
52
+ * Map heading levels to tag names
53
+ */
54
+ const LEVEL_TO_TAG: Record<HeadingLevel, string> = {
55
+ 0: "P",
56
+ 1: "H1",
57
+ 2: "H2",
58
+ 3: "H3",
59
+ 4: "H4",
60
+ 5: "H5",
61
+ 6: "H6"
62
+ };
63
+
64
+ /**
65
+ * Convert a block element to a different heading level
66
+ * Preserves the element's content and attributes
67
+ */
68
+ function convertElement(element: Element, newTagName: string): Element {
69
+ const newElement = document.createElement(newTagName);
70
+
71
+ // Copy all child nodes
72
+ while (element.firstChild) {
73
+ newElement.appendChild(element.firstChild);
74
+ }
75
+
76
+ // Copy attributes (except structural ones)
77
+ for (const attr of Array.from(element.attributes)) {
78
+ if (attr.name !== "id") {
79
+ newElement.setAttribute(attr.name, attr.value);
80
+ }
81
+ }
82
+
83
+ // Replace in DOM
84
+ element.parentNode?.replaceChild(newElement, element);
85
+
86
+ return newElement;
87
+ }
88
+
89
+ /**
90
+ * Get the heading/paragraph element containing the cursor
91
+ */
92
+ function getTargetBlock(contentRef: Ref<HTMLElement | null>, selection: UseMarkdownSelectionReturn): Element | null {
93
+ const currentBlock = selection.getCurrentBlock();
94
+ if (!currentBlock) return null;
95
+
96
+ // Only operate on headings and paragraphs
97
+ const tagName = currentBlock.tagName;
98
+ if (tagName in TAG_TO_LEVEL) {
99
+ return currentBlock;
100
+ }
101
+
102
+ // If in a list item or other block, find if there's a parent heading/paragraph
103
+ // or try to get the direct child of contentRef
104
+ if (!contentRef.value) return null;
105
+
106
+ // Walk up to find a heading/paragraph or the direct child of contentRef
107
+ let current: Element | null = currentBlock;
108
+ while (current && current.parentElement !== contentRef.value) {
109
+ current = current.parentElement;
110
+ }
111
+
112
+ // Check if this direct child is a heading/paragraph
113
+ if (current && current.tagName in TAG_TO_LEVEL) {
114
+ return current;
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ /**
121
+ * Composable for heading-specific operations in markdown editor
122
+ */
123
+ export function useHeadings(options: UseHeadingsOptions): UseHeadingsReturn {
124
+ const { contentRef, selection, onContentChange } = options;
125
+
126
+ /**
127
+ * Get the current heading level of the block containing the cursor
128
+ * Returns 0 for paragraph, 1-6 for h1-h6, -1 if not in a heading/paragraph
129
+ */
130
+ function getCurrentHeadingLevel(): number {
131
+ const block = getTargetBlock(contentRef, selection);
132
+ if (!block) return -1;
133
+
134
+ return TAG_TO_LEVEL[block.tagName] ?? -1;
135
+ }
136
+
137
+ /**
138
+ * Set the heading level of the current block
139
+ * @param level 0 = paragraph, 1-6 = h1-h6
140
+ */
141
+ function setHeadingLevel(level: HeadingLevel): void {
142
+ const block = getTargetBlock(contentRef, selection);
143
+ if (!block) return;
144
+
145
+ const currentLevel = TAG_TO_LEVEL[block.tagName];
146
+ if (currentLevel === level) return; // Already at target level
147
+
148
+ // Save cursor position
149
+ const cursorPos = selection.saveCursorPosition();
150
+
151
+ // Convert the element
152
+ const newTagName = LEVEL_TO_TAG[level];
153
+ convertElement(block, newTagName);
154
+
155
+ // Restore cursor position
156
+ if (cursorPos) {
157
+ // After conversion, we need to recalculate since the element changed
158
+ // The block index should remain the same
159
+ selection.restoreCursorPosition(cursorPos);
160
+ }
161
+
162
+ // Notify of content change
163
+ onContentChange();
164
+ }
165
+
166
+ /**
167
+ * Increase heading level (make heading more prominent)
168
+ * P -> H6 -> H5 -> H4 -> H3 -> H2 -> H1
169
+ */
170
+ function increaseHeadingLevel(): void {
171
+ const currentLevel = getCurrentHeadingLevel();
172
+ if (currentLevel === -1) return;
173
+
174
+ let newLevel: HeadingLevel;
175
+ if (currentLevel === 0) {
176
+ // Paragraph -> H6
177
+ newLevel = 6;
178
+ } else if (currentLevel === 1) {
179
+ // H1 stays at H1 (most prominent)
180
+ return;
181
+ } else {
182
+ // H6 -> H5 -> ... -> H1
183
+ newLevel = (currentLevel - 1) as HeadingLevel;
184
+ }
185
+
186
+ setHeadingLevel(newLevel);
187
+ }
188
+
189
+ /**
190
+ * Decrease heading level (make heading less prominent)
191
+ * H1 -> H2 -> H3 -> H4 -> H5 -> H6 -> P
192
+ */
193
+ function decreaseHeadingLevel(): void {
194
+ const currentLevel = getCurrentHeadingLevel();
195
+ if (currentLevel === -1) return;
196
+
197
+ let newLevel: HeadingLevel;
198
+ if (currentLevel === 0) {
199
+ // Paragraph stays at paragraph (least prominent)
200
+ return;
201
+ } else if (currentLevel === 6) {
202
+ // H6 -> Paragraph
203
+ newLevel = 0;
204
+ } else {
205
+ // H1 -> H2 -> ... -> H6
206
+ newLevel = (currentLevel + 1) as HeadingLevel;
207
+ }
208
+
209
+ setHeadingLevel(newLevel);
210
+ }
211
+
212
+ /**
213
+ * Check if the current block contains a heading pattern (e.g., "# ", "## ")
214
+ * and convert it to the appropriate heading if detected.
215
+ * Only converts if:
216
+ * - The current block is a paragraph (not already a heading)
217
+ * - The pattern is at the start of the block
218
+ * - The pattern includes the trailing space
219
+ * @returns true if a pattern was detected and converted, false otherwise
220
+ */
221
+ function checkAndConvertHeadingPattern(): boolean {
222
+ const block = getTargetBlock(contentRef, selection);
223
+ if (!block) return false;
224
+
225
+ // Only convert paragraphs or divs - don't re-convert headings
226
+ if (block.tagName !== "P" && block.tagName !== "DIV") return false;
227
+
228
+ // Get the text content of the block
229
+ const textContent = block.textContent || "";
230
+
231
+ // Check for heading pattern
232
+ const pattern = detectHeadingPattern(textContent);
233
+ if (!pattern) return false;
234
+
235
+ // Pattern detected - convert to heading
236
+ const level = pattern.level as HeadingLevel;
237
+ const remainingContent = pattern.content;
238
+
239
+ // Create the new heading element
240
+ const newTagName = LEVEL_TO_TAG[level];
241
+ const newElement = document.createElement(newTagName);
242
+
243
+ // Set the content without the "# " prefix
244
+ newElement.textContent = remainingContent;
245
+
246
+ // Copy attributes (except structural ones)
247
+ for (const attr of Array.from(block.attributes)) {
248
+ if (attr.name !== "id") {
249
+ newElement.setAttribute(attr.name, attr.value);
250
+ }
251
+ }
252
+
253
+ // Replace in DOM
254
+ block.parentNode?.replaceChild(newElement, block);
255
+
256
+ // Position cursor at the end of the content (synchronously)
257
+ const sel = window.getSelection();
258
+ if (sel && newElement.firstChild) {
259
+ const range = document.createRange();
260
+ const textNode = newElement.firstChild;
261
+
262
+ if (textNode.nodeType === Node.TEXT_NODE) {
263
+ // Position cursor at the end of the text content
264
+ const offset = textNode.textContent?.length || 0;
265
+ range.setStart(textNode, offset);
266
+ range.collapse(true);
267
+ } else {
268
+ // Fallback: select end of element
269
+ range.selectNodeContents(newElement);
270
+ range.collapse(false);
271
+ }
272
+
273
+ sel.removeAllRanges();
274
+ sel.addRange(range);
275
+ }
276
+
277
+ // Notify of content change
278
+ onContentChange();
279
+
280
+ return true;
281
+ }
282
+
283
+ return {
284
+ setHeadingLevel,
285
+ increaseHeadingLevel,
286
+ decreaseHeadingLevel,
287
+ getCurrentHeadingLevel,
288
+ checkAndConvertHeadingPattern
289
+ };
290
+ }