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.
- package/dist/danx.es.js +17884 -12732
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +192 -118
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +11 -2
- package/scripts/publish.sh +76 -0
- package/src/components/Utility/Code/CodeViewer.vue +31 -14
- package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
- package/src/components/Utility/Code/CodeViewerFooter.vue +1 -1
- package/src/components/Utility/Code/LanguageBadge.vue +278 -5
- package/src/components/Utility/Code/MarkdownContent.vue +160 -6
- package/src/components/Utility/Code/index.ts +3 -0
- package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
- package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
- package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
- package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
- package/src/components/Utility/Markdown/MarkdownEditor.vue +228 -0
- package/src/components/Utility/Markdown/MarkdownEditorContent.vue +235 -0
- package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
- package/src/components/Utility/Markdown/TablePopover.vue +420 -0
- package/src/components/Utility/Markdown/index.ts +11 -0
- package/src/components/Utility/Markdown/types.ts +27 -0
- package/src/components/Utility/index.ts +1 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
- package/src/composables/markdown/features/useBlockquotes.ts +248 -0
- package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
- package/src/composables/markdown/features/useCodeBlocks.spec.ts +779 -0
- package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
- package/src/composables/markdown/features/useContextMenu.ts +444 -0
- package/src/composables/markdown/features/useFocusTracking.ts +116 -0
- package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
- package/src/composables/markdown/features/useHeadings.ts +290 -0
- package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
- package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
- package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
- package/src/composables/markdown/features/useLinks.spec.ts +369 -0
- package/src/composables/markdown/features/useLinks.ts +374 -0
- package/src/composables/markdown/features/useLists.spec.ts +834 -0
- package/src/composables/markdown/features/useLists.ts +747 -0
- package/src/composables/markdown/features/usePopoverManager.ts +181 -0
- package/src/composables/markdown/features/useTables.spec.ts +1601 -0
- package/src/composables/markdown/features/useTables.ts +1107 -0
- package/src/composables/markdown/index.ts +16 -0
- package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
- package/src/composables/markdown/useMarkdownEditor.ts +1068 -0
- package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
- package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
- package/src/composables/markdown/useMarkdownSelection.ts +219 -0
- package/src/composables/markdown/useMarkdownSync.ts +549 -0
- package/src/composables/useCodeViewerEditor.spec.ts +655 -0
- package/src/composables/useCodeViewerEditor.ts +174 -20
- package/src/helpers/formats/index.ts +1 -1
- package/src/helpers/formats/markdown/escapeHtml.ts +15 -0
- package/src/helpers/formats/markdown/escapeSequences.ts +60 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +412 -0
- package/src/helpers/formats/markdown/index.ts +92 -0
- package/src/helpers/formats/markdown/linePatterns.spec.ts +495 -0
- package/src/helpers/formats/markdown/linePatterns.ts +172 -0
- package/src/helpers/formats/markdown/parseInline.ts +124 -0
- package/src/helpers/formats/markdown/render/index.ts +92 -0
- package/src/helpers/formats/markdown/render/renderFootnotes.ts +30 -0
- package/src/helpers/formats/markdown/render/renderList.ts +69 -0
- package/src/helpers/formats/markdown/render/renderTable.ts +38 -0
- package/src/helpers/formats/markdown/state.ts +58 -0
- package/src/helpers/formats/markdown/tokenize/extractDefinitions.ts +39 -0
- package/src/helpers/formats/markdown/tokenize/index.ts +139 -0
- package/src/helpers/formats/markdown/tokenize/parseBlockquote.ts +34 -0
- package/src/helpers/formats/markdown/tokenize/parseCodeBlock.ts +85 -0
- package/src/helpers/formats/markdown/tokenize/parseDefinitionList.ts +88 -0
- package/src/helpers/formats/markdown/tokenize/parseHeading.ts +65 -0
- package/src/helpers/formats/markdown/tokenize/parseHorizontalRule.ts +22 -0
- package/src/helpers/formats/markdown/tokenize/parseList.ts +119 -0
- package/src/helpers/formats/markdown/tokenize/parseParagraph.ts +59 -0
- package/src/helpers/formats/markdown/tokenize/parseTable.ts +70 -0
- package/src/helpers/formats/markdown/tokenize/parseTaskList.ts +47 -0
- package/src/helpers/formats/markdown/tokenize/utils.ts +25 -0
- package/src/helpers/formats/markdown/types.ts +63 -0
- package/src/styles/danx.scss +1 -0
- package/src/styles/themes/danx/markdown.scss +96 -0
- package/src/test/helpers/editorTestUtils.spec.ts +296 -0
- package/src/test/helpers/editorTestUtils.ts +253 -0
- package/src/test/helpers/index.ts +1 -0
- package/src/test/setup.test.ts +12 -0
- package/src/test/setup.ts +12 -0
- package/vitest.config.ts +19 -0
- 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
|
+
}
|