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,369 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { useLinks } from "./useLinks";
3
+ import { createTestEditor, TestEditorResult } from "../../../test/helpers/editorTestUtils";
4
+
5
+ describe("useLinks", () => {
6
+ let editor: TestEditorResult;
7
+ let onContentChange: ReturnType<typeof vi.fn>;
8
+
9
+ afterEach(() => {
10
+ if (editor) {
11
+ editor.destroy();
12
+ }
13
+ vi.restoreAllMocks();
14
+ });
15
+
16
+ function createLinks() {
17
+ return useLinks({
18
+ contentRef: editor.contentRef,
19
+ onContentChange
20
+ });
21
+ }
22
+
23
+ describe("insertLink", () => {
24
+ describe("with selected text", () => {
25
+ beforeEach(() => {
26
+ onContentChange = vi.fn();
27
+ });
28
+
29
+ it("wraps selected text in anchor tag with prompted URL", () => {
30
+ editor = createTestEditor("<p>Hello world</p>");
31
+ vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
32
+ const links = createLinks();
33
+ editor.selectInBlock(0, 0, 5); // Select "Hello"
34
+
35
+ links.insertLink();
36
+
37
+ expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
38
+ expect(onContentChange).toHaveBeenCalled();
39
+ });
40
+
41
+ it("wraps middle text in anchor tag", () => {
42
+ editor = createTestEditor("<p>Hello beautiful world</p>");
43
+ vi.spyOn(window, "prompt").mockReturnValue("https://test.com");
44
+ const links = createLinks();
45
+ editor.selectInBlock(0, 6, 15); // Select "beautiful"
46
+
47
+ links.insertLink();
48
+
49
+ expect(editor.getHtml()).toBe('<p>Hello <a href="https://test.com">beautiful</a> world</p>');
50
+ });
51
+
52
+ it("trims whitespace from URL", () => {
53
+ editor = createTestEditor("<p>Hello world</p>");
54
+ vi.spyOn(window, "prompt").mockReturnValue(" https://example.com ");
55
+ const links = createLinks();
56
+ editor.selectInBlock(0, 0, 5);
57
+
58
+ links.insertLink();
59
+
60
+ expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
61
+ });
62
+
63
+ it("does nothing when user cancels prompt", () => {
64
+ editor = createTestEditor("<p>Hello world</p>");
65
+ vi.spyOn(window, "prompt").mockReturnValue(null);
66
+ const links = createLinks();
67
+ editor.selectInBlock(0, 0, 5);
68
+
69
+ links.insertLink();
70
+
71
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
72
+ expect(onContentChange).not.toHaveBeenCalled();
73
+ });
74
+
75
+ it("does nothing when user enters empty URL", () => {
76
+ editor = createTestEditor("<p>Hello world</p>");
77
+ vi.spyOn(window, "prompt").mockReturnValue("");
78
+ const links = createLinks();
79
+ editor.selectInBlock(0, 0, 5);
80
+
81
+ links.insertLink();
82
+
83
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
84
+ expect(onContentChange).not.toHaveBeenCalled();
85
+ });
86
+
87
+ it("does nothing when user enters whitespace-only URL", () => {
88
+ editor = createTestEditor("<p>Hello world</p>");
89
+ vi.spyOn(window, "prompt").mockReturnValue(" ");
90
+ const links = createLinks();
91
+ editor.selectInBlock(0, 0, 5);
92
+
93
+ links.insertLink();
94
+
95
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
96
+ expect(onContentChange).not.toHaveBeenCalled();
97
+ });
98
+
99
+ it("selects the wrapped content after wrapping", () => {
100
+ editor = createTestEditor("<p>Hello world</p>");
101
+ vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
102
+ const links = createLinks();
103
+ editor.selectInBlock(0, 0, 5);
104
+
105
+ links.insertLink();
106
+
107
+ const selection = window.getSelection();
108
+ expect(selection?.toString()).toBe("Hello");
109
+ });
110
+ });
111
+
112
+ describe("without selection (cursor only)", () => {
113
+ beforeEach(() => {
114
+ onContentChange = vi.fn();
115
+ });
116
+
117
+ it("inserts link with URL as text when no selection", () => {
118
+ editor = createTestEditor("<p>Hello world</p>");
119
+ vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
120
+ const links = createLinks();
121
+ editor.setCursorInBlock(0, 6); // After "Hello "
122
+
123
+ links.insertLink();
124
+
125
+ expect(editor.getHtml()).toContain('<a href="https://example.com">https://example.com</a>');
126
+ expect(onContentChange).toHaveBeenCalled();
127
+ });
128
+
129
+ it("does nothing when user cancels prompt", () => {
130
+ editor = createTestEditor("<p>Hello world</p>");
131
+ vi.spyOn(window, "prompt").mockReturnValue(null);
132
+ const links = createLinks();
133
+ editor.setCursorInBlock(0, 6);
134
+
135
+ links.insertLink();
136
+
137
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
138
+ expect(onContentChange).not.toHaveBeenCalled();
139
+ });
140
+
141
+ it("does nothing when user enters empty URL", () => {
142
+ editor = createTestEditor("<p>Hello world</p>");
143
+ vi.spyOn(window, "prompt").mockReturnValue("");
144
+ const links = createLinks();
145
+ editor.setCursorInBlock(0, 6);
146
+
147
+ links.insertLink();
148
+
149
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
150
+ expect(onContentChange).not.toHaveBeenCalled();
151
+ });
152
+
153
+ it("selects the inserted link text after insertion", () => {
154
+ editor = createTestEditor("<p>Hello world</p>");
155
+ vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
156
+ const links = createLinks();
157
+ editor.setCursorInBlock(0, 6);
158
+
159
+ links.insertLink();
160
+
161
+ const selection = window.getSelection();
162
+ expect(selection?.toString()).toBe("https://example.com");
163
+ });
164
+ });
165
+
166
+ describe("editing existing link", () => {
167
+ beforeEach(() => {
168
+ onContentChange = vi.fn();
169
+ });
170
+
171
+ it("prompts with current URL prefilled when cursor is in link", () => {
172
+ editor = createTestEditor('<p><a href="https://old.com">Hello</a> world</p>');
173
+ const promptSpy = vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
174
+ const links = createLinks();
175
+ const linkEl = editor.container.querySelector("a");
176
+ editor.setCursor(linkEl!.firstChild!, 3); // Inside "Hello"
177
+
178
+ links.insertLink();
179
+
180
+ expect(promptSpy).toHaveBeenCalledWith("Edit link URL:", "https://old.com");
181
+ });
182
+
183
+ it("updates URL when user provides new URL", () => {
184
+ editor = createTestEditor('<p><a href="https://old.com">Hello</a> world</p>');
185
+ vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
186
+ const links = createLinks();
187
+ const linkEl = editor.container.querySelector("a");
188
+ editor.setCursor(linkEl!.firstChild!, 3);
189
+
190
+ links.insertLink();
191
+
192
+ expect(editor.getHtml()).toBe('<p><a href="https://new.com">Hello</a> world</p>');
193
+ expect(onContentChange).toHaveBeenCalled();
194
+ });
195
+
196
+ it("removes link when user enters empty URL", () => {
197
+ editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
198
+ vi.spyOn(window, "prompt").mockReturnValue("");
199
+ const links = createLinks();
200
+ const linkEl = editor.container.querySelector("a");
201
+ editor.setCursor(linkEl!.firstChild!, 3);
202
+
203
+ links.insertLink();
204
+
205
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
206
+ expect(onContentChange).toHaveBeenCalled();
207
+ });
208
+
209
+ it("does nothing when user cancels prompt on existing link", () => {
210
+ editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
211
+ vi.spyOn(window, "prompt").mockReturnValue(null);
212
+ const links = createLinks();
213
+ const linkEl = editor.container.querySelector("a");
214
+ editor.setCursor(linkEl!.firstChild!, 3);
215
+
216
+ links.insertLink();
217
+
218
+ expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
219
+ expect(onContentChange).not.toHaveBeenCalled();
220
+ });
221
+
222
+ it("handles link with empty href attribute", () => {
223
+ editor = createTestEditor('<p><a href="">Hello</a> world</p>');
224
+ const promptSpy = vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
225
+ const links = createLinks();
226
+ const linkEl = editor.container.querySelector("a");
227
+ editor.setCursor(linkEl!.firstChild!, 3);
228
+
229
+ links.insertLink();
230
+
231
+ expect(promptSpy).toHaveBeenCalledWith("Edit link URL:", "");
232
+ expect(editor.getHtml()).toBe('<p><a href="https://new.com">Hello</a> world</p>');
233
+ });
234
+ });
235
+ });
236
+
237
+ describe("isInLink", () => {
238
+ beforeEach(() => {
239
+ onContentChange = vi.fn();
240
+ });
241
+
242
+ it("returns true when cursor is inside a link", () => {
243
+ editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
244
+ const links = createLinks();
245
+ const linkEl = editor.container.querySelector("a");
246
+ editor.setCursor(linkEl!.firstChild!, 3);
247
+
248
+ expect(links.isInLink()).toBe(true);
249
+ });
250
+
251
+ it("returns false when cursor is outside a link", () => {
252
+ editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
253
+ const links = createLinks();
254
+ editor.setCursorInBlock(0, 8); // In " world"
255
+
256
+ expect(links.isInLink()).toBe(false);
257
+ });
258
+
259
+ it("returns false when there are no links in content", () => {
260
+ editor = createTestEditor("<p>Hello world</p>");
261
+ const links = createLinks();
262
+ editor.setCursorInBlock(0, 5);
263
+
264
+ expect(links.isInLink()).toBe(false);
265
+ });
266
+
267
+ it("returns false when contentRef is null", () => {
268
+ editor = createTestEditor("<p>Hello world</p>");
269
+ const links = useLinks({
270
+ contentRef: { value: null },
271
+ onContentChange
272
+ });
273
+
274
+ expect(links.isInLink()).toBe(false);
275
+ });
276
+
277
+ it("returns false when no selection exists", () => {
278
+ editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
279
+ const links = createLinks();
280
+
281
+ // Clear any selection
282
+ window.getSelection()?.removeAllRanges();
283
+
284
+ expect(links.isInLink()).toBe(false);
285
+ });
286
+ });
287
+
288
+ describe("edge cases", () => {
289
+ beforeEach(() => {
290
+ onContentChange = vi.fn();
291
+ });
292
+
293
+ it("does nothing when contentRef is null", () => {
294
+ editor = createTestEditor("<p>Hello world</p>");
295
+ const links = useLinks({
296
+ contentRef: { value: null },
297
+ onContentChange
298
+ });
299
+
300
+ links.insertLink();
301
+
302
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
303
+ expect(onContentChange).not.toHaveBeenCalled();
304
+ });
305
+
306
+ it("does nothing when selection is outside content area", () => {
307
+ editor = createTestEditor("<p>Hello world</p>");
308
+ const links = createLinks();
309
+
310
+ // Create a selection outside the editor
311
+ const externalDiv = document.createElement("div");
312
+ externalDiv.textContent = "External text";
313
+ document.body.appendChild(externalDiv);
314
+
315
+ const range = document.createRange();
316
+ range.selectNodeContents(externalDiv);
317
+ const sel = window.getSelection();
318
+ sel?.removeAllRanges();
319
+ sel?.addRange(range);
320
+
321
+ links.insertLink();
322
+
323
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
324
+ expect(onContentChange).not.toHaveBeenCalled();
325
+
326
+ externalDiv.remove();
327
+ });
328
+
329
+ it("does nothing when no selection exists", () => {
330
+ editor = createTestEditor("<p>Hello world</p>");
331
+ const links = createLinks();
332
+
333
+ // Clear any selection
334
+ window.getSelection()?.removeAllRanges();
335
+
336
+ links.insertLink();
337
+
338
+ expect(editor.getHtml()).toBe("<p>Hello world</p>");
339
+ expect(onContentChange).not.toHaveBeenCalled();
340
+ });
341
+
342
+ it("handles nested elements within selection", () => {
343
+ editor = createTestEditor("<p><strong>Hello</strong> world</p>");
344
+ vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
345
+ const links = createLinks();
346
+ editor.selectInBlock(0, 0, 5); // Select "Hello" (inside strong)
347
+
348
+ links.insertLink();
349
+
350
+ // The link should wrap the strong element
351
+ expect(editor.getHtml()).toContain("https://example.com");
352
+ expect(onContentChange).toHaveBeenCalled();
353
+ });
354
+ });
355
+
356
+ describe("return type", () => {
357
+ beforeEach(() => {
358
+ onContentChange = vi.fn();
359
+ editor = createTestEditor("<p>test</p>");
360
+ });
361
+
362
+ it("returns insertLink and isInLink functions", () => {
363
+ const links = createLinks();
364
+
365
+ expect(typeof links.insertLink).toBe("function");
366
+ expect(typeof links.isInLink).toBe("function");
367
+ });
368
+ });
369
+ });