quasar-ui-danx 0.5.0 → 0.5.2

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 (81) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/danx.es.js +16119 -10641
  3. package/dist/danx.es.js.map +1 -1
  4. package/dist/danx.umd.js +202 -123
  5. package/dist/danx.umd.js.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +8 -1
  8. package/src/components/Utility/Buttons/ActionButton.vue +15 -5
  9. package/src/components/Utility/Code/CodeViewer.vue +41 -16
  10. package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
  11. package/src/components/Utility/Code/CodeViewerFooter.vue +3 -1
  12. package/src/components/Utility/Code/LanguageBadge.vue +278 -5
  13. package/src/components/Utility/Code/MarkdownContent.vue +31 -163
  14. package/src/components/Utility/Code/index.ts +3 -0
  15. package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
  16. package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
  17. package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
  18. package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
  19. package/src/components/Utility/Markdown/MarkdownEditor.vue +233 -0
  20. package/src/components/Utility/Markdown/MarkdownEditorContent.vue +296 -0
  21. package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
  22. package/src/components/Utility/Markdown/TablePopover.vue +420 -0
  23. package/src/components/Utility/Markdown/index.ts +11 -0
  24. package/src/components/Utility/Markdown/types.ts +27 -0
  25. package/src/components/Utility/Widgets/LabelPillWidget.vue +20 -0
  26. package/src/components/Utility/index.ts +1 -0
  27. package/src/composables/index.ts +1 -0
  28. package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
  29. package/src/composables/markdown/features/useBlockquotes.ts +248 -0
  30. package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
  31. package/src/composables/markdown/features/useCodeBlocks.spec.ts +805 -0
  32. package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
  33. package/src/composables/markdown/features/useContextMenu.ts +444 -0
  34. package/src/composables/markdown/features/useFocusTracking.ts +116 -0
  35. package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
  36. package/src/composables/markdown/features/useHeadings.ts +290 -0
  37. package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
  38. package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
  39. package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
  40. package/src/composables/markdown/features/useLinks.spec.ts +388 -0
  41. package/src/composables/markdown/features/useLinks.ts +374 -0
  42. package/src/composables/markdown/features/useLists.spec.ts +834 -0
  43. package/src/composables/markdown/features/useLists.ts +747 -0
  44. package/src/composables/markdown/features/usePopoverManager.ts +181 -0
  45. package/src/composables/markdown/features/useTables.spec.ts +1601 -0
  46. package/src/composables/markdown/features/useTables.ts +1107 -0
  47. package/src/composables/markdown/index.ts +16 -0
  48. package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
  49. package/src/composables/markdown/useMarkdownEditor.ts +1077 -0
  50. package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
  51. package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
  52. package/src/composables/markdown/useMarkdownSelection.ts +219 -0
  53. package/src/composables/markdown/useMarkdownSync.ts +549 -0
  54. package/src/composables/useCodeFormat.ts +17 -10
  55. package/src/composables/useCodeViewerEditor.spec.ts +655 -0
  56. package/src/composables/useCodeViewerEditor.ts +174 -20
  57. package/src/helpers/formats/highlightCSS.ts +236 -0
  58. package/src/helpers/formats/highlightHTML.ts +483 -0
  59. package/src/helpers/formats/highlightJavaScript.ts +346 -0
  60. package/src/helpers/formats/highlightSyntax.ts +15 -4
  61. package/src/helpers/formats/index.ts +3 -0
  62. package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
  63. package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
  64. package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +425 -0
  65. package/src/helpers/formats/markdown/index.ts +7 -0
  66. package/src/helpers/formats/markdown/linePatterns.spec.ts +498 -0
  67. package/src/helpers/formats/markdown/linePatterns.ts +172 -0
  68. package/src/styles/danx.scss +3 -3
  69. package/src/styles/index.scss +5 -5
  70. package/src/styles/themes/danx/code.scss +257 -1
  71. package/src/styles/themes/danx/index.scss +10 -10
  72. package/src/styles/themes/danx/markdown.scss +59 -0
  73. package/src/test/helpers/editorTestUtils.spec.ts +296 -0
  74. package/src/test/helpers/editorTestUtils.ts +253 -0
  75. package/src/test/helpers/index.ts +1 -0
  76. package/src/test/highlighters.test.ts +153 -0
  77. package/src/test/setup.test.ts +12 -0
  78. package/src/test/setup.ts +12 -0
  79. package/src/types/widgets.d.ts +2 -2
  80. package/vite.config.js +5 -1
  81. package/vitest.config.ts +19 -0
@@ -0,0 +1,296 @@
1
+ <template>
2
+ <div
3
+ ref="containerRef"
4
+ class="dx-markdown-editor-content dx-markdown-content"
5
+ :class="{ 'is-readonly': readonly, 'is-empty': isContentEmpty }"
6
+ :contenteditable="!readonly"
7
+ :data-placeholder="placeholder"
8
+ @input="onInput"
9
+ @keydown="$emit('keydown', $event)"
10
+ @blur="$emit('blur')"
11
+ @click="handleClick"
12
+ v-html="html"
13
+ />
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { nextTick, ref, watch } from "vue";
18
+
19
+ export interface MarkdownEditorContentProps {
20
+ html: string;
21
+ readonly?: boolean;
22
+ placeholder?: string;
23
+ }
24
+
25
+ const props = withDefaults(defineProps<MarkdownEditorContentProps>(), {
26
+ readonly: false,
27
+ placeholder: "Start typing..."
28
+ });
29
+
30
+ const emit = defineEmits<{
31
+ input: [];
32
+ keydown: [event: KeyboardEvent];
33
+ blur: [];
34
+ }>();
35
+
36
+ const containerRef = ref<HTMLElement | null>(null);
37
+ const isContentEmpty = ref(true);
38
+
39
+ /**
40
+ * Check if the editor content is empty by examining the actual DOM text content.
41
+ * This is needed because contenteditable changes the DOM directly without updating props.
42
+ */
43
+ function checkIfEmpty(): void {
44
+ if (containerRef.value) {
45
+ const textContent = containerRef.value.textContent?.trim() || "";
46
+ isContentEmpty.value = textContent.length === 0;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Handle input events - check if content is empty and emit the input event.
52
+ */
53
+ function onInput(): void {
54
+ checkIfEmpty();
55
+ emit("input");
56
+ }
57
+
58
+ // Watch for external HTML changes (e.g., from parent component)
59
+ watch(() => props.html, () => {
60
+ nextTick(() => checkIfEmpty());
61
+ }, { immediate: true });
62
+
63
+ /**
64
+ * Find the anchor element if the click target is inside one
65
+ */
66
+ function findLinkAncestor(node: Node | null): HTMLAnchorElement | null {
67
+ if (!node || !containerRef.value) return null;
68
+
69
+ let current: Node | null = node;
70
+ while (current && current !== containerRef.value) {
71
+ if (current.nodeType === Node.ELEMENT_NODE && (current as Element).tagName === "A") {
72
+ return current as HTMLAnchorElement;
73
+ }
74
+ current = current.parentNode;
75
+ }
76
+
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Handle clicks in the editor content.
82
+ * Ctrl+Click (or Cmd+Click on Mac) opens links in a new tab.
83
+ */
84
+ function handleClick(event: MouseEvent): void {
85
+ // Check if Ctrl (Windows/Linux) or Cmd (Mac) is held
86
+ const isModifierHeld = event.ctrlKey || event.metaKey;
87
+ if (!isModifierHeld) return;
88
+
89
+ // Find if the click was on or inside a link
90
+ const link = findLinkAncestor(event.target as Node);
91
+ if (!link) return;
92
+
93
+ const href = link.getAttribute("href");
94
+ if (!href) return;
95
+
96
+ // Prevent default behavior and open the link in a new tab
97
+ event.preventDefault();
98
+ event.stopPropagation();
99
+ window.open(href, "_blank", "noopener,noreferrer");
100
+ }
101
+
102
+ // Expose containerRef for parent component
103
+ defineExpose({ containerRef });
104
+ </script>
105
+
106
+ <style lang="scss">
107
+ .dx-markdown-editor-content {
108
+ min-height: 100px;
109
+ outline: none;
110
+ cursor: text;
111
+ border: 2px solid transparent;
112
+ border-radius: 0.375rem 0.375rem 0 0;
113
+ padding: 1rem;
114
+ background-color: #1e1e1e;
115
+ color: #d4d4d4;
116
+ transition: border-color 0.2s ease;
117
+ overflow: auto;
118
+
119
+ &:focus {
120
+ border-color: rgba(86, 156, 214, 0.6);
121
+ }
122
+
123
+ &:hover:not(:focus):not(.is-readonly) {
124
+ border-color: rgba(86, 156, 214, 0.3);
125
+ }
126
+
127
+ &.is-readonly {
128
+ cursor: default;
129
+ border-color: transparent;
130
+ }
131
+
132
+ // Placeholder styling when empty
133
+ &.is-empty::before {
134
+ content: attr(data-placeholder);
135
+ color: #6b7280;
136
+ pointer-events: none;
137
+ position: absolute;
138
+ }
139
+
140
+ &.is-empty {
141
+ position: relative;
142
+ }
143
+
144
+ // Caret color
145
+ caret-color: #d4d4d4;
146
+
147
+ // Link styling - show pointer cursor and hint for Ctrl+Click
148
+ a {
149
+ cursor: pointer;
150
+ position: relative;
151
+
152
+ &:hover::after {
153
+ content: "Ctrl+Click to open";
154
+ position: absolute;
155
+ bottom: 100%;
156
+ left: 50%;
157
+ transform: translateX(-50%);
158
+ background: #374151;
159
+ color: #d1d5db;
160
+ padding: 0.25rem 0.5rem;
161
+ border-radius: 0.25rem;
162
+ font-size: 0.75rem;
163
+ white-space: nowrap;
164
+ z-index: 10;
165
+ pointer-events: none;
166
+ opacity: 0;
167
+ animation: fadeIn 0.2s ease 0.5s forwards;
168
+ }
169
+ }
170
+
171
+ @keyframes fadeIn {
172
+ to {
173
+ opacity: 1;
174
+ }
175
+ }
176
+
177
+ // Alternating styles for nested ordered lists
178
+ // Level 1: decimal (1, 2, 3)
179
+ ol {
180
+ list-style-type: decimal;
181
+
182
+ // Level 2: lower-roman (i, ii, iii)
183
+ ol {
184
+ list-style-type: lower-roman;
185
+
186
+ // Level 3: lower-alpha (a, b, c)
187
+ ol {
188
+ list-style-type: lower-alpha;
189
+
190
+ // Level 4+: cycle back to decimal
191
+ ol {
192
+ list-style-type: decimal;
193
+
194
+ ol {
195
+ list-style-type: lower-roman;
196
+
197
+ ol {
198
+ list-style-type: lower-alpha;
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ // Alternating styles for nested unordered lists
207
+ // Level 1: disc
208
+ ul {
209
+ list-style-type: disc;
210
+
211
+ // Level 2: circle
212
+ ul {
213
+ list-style-type: circle;
214
+
215
+ // Level 3: square
216
+ ul {
217
+ list-style-type: square;
218
+
219
+ // Level 4+: cycle back to disc
220
+ ul {
221
+ list-style-type: disc;
222
+
223
+ ul {
224
+ list-style-type: circle;
225
+
226
+ ul {
227
+ list-style-type: square;
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ // Code block wrapper styling - distinct background to separate from editor content
236
+ .code-block-wrapper {
237
+ background: #0d1117;
238
+ border-radius: 0.375rem;
239
+ margin: 0.5rem 0;
240
+ border: 1px solid #30363d;
241
+
242
+ // Override CodeViewer backgrounds to be transparent so wrapper controls it
243
+ .dx-code-viewer {
244
+ .code-content {
245
+ background: transparent;
246
+ border-radius: 0.375rem 0.375rem 0 0;
247
+ }
248
+
249
+ .code-footer {
250
+ background: #161b22;
251
+ border-radius: 0 0 0.375rem 0.375rem;
252
+ }
253
+ }
254
+ }
255
+
256
+ // ==========================================
257
+ // LIGHT THEME VARIANT
258
+ // ==========================================
259
+ .dx-markdown-editor.theme-light & {
260
+ background-color: #f8fafc;
261
+ color: #1e293b;
262
+ caret-color: #1e293b;
263
+
264
+ &:focus {
265
+ border-color: rgba(14, 165, 233, 0.6);
266
+ }
267
+
268
+ &:hover:not(:focus):not(.is-readonly) {
269
+ border-color: rgba(14, 165, 233, 0.3);
270
+ }
271
+
272
+ // Placeholder styling - light theme
273
+ &.is-empty::before {
274
+ color: #94a3b8;
275
+ }
276
+
277
+ // Link tooltip - light theme
278
+ a:hover::after {
279
+ background: #e2e8f0;
280
+ color: #1e293b;
281
+ }
282
+
283
+ // Code block wrapper - light theme
284
+ .code-block-wrapper {
285
+ background: #f1f5f9;
286
+ border-color: #e2e8f0;
287
+
288
+ .dx-code-viewer {
289
+ .code-footer {
290
+ background: #e2e8f0;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ </style>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <div class="dx-markdown-editor-footer flex items-center justify-between px-2 py-1">
3
+ <span class="char-count text-xs text-gray-500">
4
+ {{ charCount.toLocaleString() }} chars
5
+ </span>
6
+ <button
7
+ class="hotkey-help-btn text-gray-500 hover:text-gray-300 transition-colors p-1 rounded"
8
+ title="Keyboard shortcuts (Ctrl+?)"
9
+ type="button"
10
+ @click="$emit('show-hotkeys')"
11
+ >
12
+ <KeyboardIcon class="w-4 h-4" />
13
+ </button>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { FaSolidKeyboard as KeyboardIcon } from "danx-icon";
19
+
20
+ export interface MarkdownEditorFooterProps {
21
+ charCount: number;
22
+ }
23
+
24
+ defineProps<MarkdownEditorFooterProps>();
25
+
26
+ defineEmits<{
27
+ "show-hotkeys": [];
28
+ }>();
29
+ </script>
30
+
31
+ <style lang="scss">
32
+ .dx-markdown-editor-footer {
33
+ background-color: #252526;
34
+ border-radius: 0 0 0.375rem 0.375rem;
35
+ flex-shrink: 0;
36
+
37
+ .hotkey-help-btn {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ background: transparent;
42
+ border: none;
43
+ cursor: pointer;
44
+
45
+ &:hover {
46
+ background-color: rgba(255, 255, 255, 0.1);
47
+ }
48
+ }
49
+ }
50
+ </style>