@sveltia/ui 0.36.1 → 0.37.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/components/util/popup.svelte +16 -29
- package/dist/services/popup.svelte.d.ts +11 -12
- package/dist/services/popup.svelte.js +51 -44
- package/package.json +61 -53
- package/dist/components/text-editor/constants.test.d.ts +0 -1
- package/dist/components/text-editor/constants.test.js +0 -98
- package/dist/components/text-editor/markdown.test.d.ts +0 -1
- package/dist/components/text-editor/markdown.test.js +0 -84
- package/dist/components/text-editor/store.svelte.test.d.ts +0 -1
- package/dist/components/text-editor/store.svelte.test.js +0 -229
- package/dist/components/text-editor/transformers/hr.test.d.ts +0 -1
- package/dist/components/text-editor/transformers/hr.test.js +0 -106
- package/dist/components/text-editor/transformers/table.test.d.ts +0 -1
- package/dist/components/text-editor/transformers/table.test.js +0 -28
- package/dist/services/group.test.d.ts +0 -1
- package/dist/services/group.test.js +0 -1215
- package/dist/services/i18n.test.d.ts +0 -1
- package/dist/services/i18n.test.js +0 -18
- package/dist/services/popup.test.d.ts +0 -1
- package/dist/services/popup.test.js +0 -641
- package/dist/services/select.test.d.ts +0 -1
- package/dist/services/select.test.js +0 -69
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
<script>
|
|
6
6
|
import { sleep } from '@sveltia/utils/misc';
|
|
7
7
|
import { onMount } from 'svelte';
|
|
8
|
-
import { writable } from 'svelte/store';
|
|
9
8
|
import { activatePopup } from '../../services/popup.svelte.js';
|
|
10
9
|
import Modal from './modal.svelte';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* @import { Snippet } from 'svelte';
|
|
14
|
-
* @import { Writable } from 'svelte/store';
|
|
15
13
|
* @import { ModalProps, PopupPosition } from '../../typedefs';
|
|
16
14
|
*/
|
|
17
15
|
|
|
@@ -78,23 +76,11 @@
|
|
|
78
76
|
*/
|
|
79
77
|
let contentType = $state();
|
|
80
78
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
79
|
+
* @type {{ style: { inset: string | undefined, zIndex: number | undefined, minWidth: string |
|
|
80
|
+
* undefined, maxWidth: string | undefined, height: string | undefined }, open: boolean,
|
|
81
|
+
* checkPosition: () => void } | undefined}
|
|
83
82
|
*/
|
|
84
|
-
let
|
|
85
|
-
writable({
|
|
86
|
-
inset: undefined,
|
|
87
|
-
zIndex: undefined,
|
|
88
|
-
width: undefined,
|
|
89
|
-
height: undefined,
|
|
90
|
-
}),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* @type {{ style: Writable<Record<string, any>>, open: Writable<boolean>, checkPosition:
|
|
95
|
-
* () => void } | undefined}
|
|
96
|
-
*/
|
|
97
|
-
let popupInstance = undefined;
|
|
83
|
+
let popupInstance = $state();
|
|
98
84
|
let hoveredTimeout = 0;
|
|
99
85
|
|
|
100
86
|
/**
|
|
@@ -103,15 +89,16 @@
|
|
|
103
89
|
const init = () => {
|
|
104
90
|
popupInstance = activatePopup(anchor, dialogElement, position, positionBaseElement);
|
|
105
91
|
|
|
106
|
-
style = popupInstance.style;
|
|
107
|
-
popupInstance.open.subscribe((_open) => {
|
|
108
|
-
open = _open;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
92
|
contentType = anchor?.getAttribute('aria-haspopup') ?? undefined;
|
|
112
93
|
initialized = true;
|
|
113
94
|
};
|
|
114
95
|
|
|
96
|
+
$effect(() => {
|
|
97
|
+
if (popupInstance) {
|
|
98
|
+
open = popupInstance.open;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
115
102
|
$effect(() => {
|
|
116
103
|
if (parentDialogElement && !dialogElement && content) {
|
|
117
104
|
dialogElement = parentDialogElement;
|
|
@@ -145,12 +132,12 @@
|
|
|
145
132
|
role="none"
|
|
146
133
|
class="content {className} {contentType}"
|
|
147
134
|
class:touch
|
|
148
|
-
style:inset={
|
|
149
|
-
style:z-index={
|
|
150
|
-
style:min-width={
|
|
151
|
-
style:max-width={
|
|
152
|
-
style:max-height={
|
|
153
|
-
style:visibility={
|
|
135
|
+
style:inset={popupInstance?.style.inset}
|
|
136
|
+
style:z-index={popupInstance?.style.zIndex}
|
|
137
|
+
style:min-width={popupInstance?.style.minWidth}
|
|
138
|
+
style:max-width={popupInstance?.style.maxWidth}
|
|
139
|
+
style:max-height={popupInstance?.style.height}
|
|
140
|
+
style:visibility={popupInstance?.style.inset ? undefined : 'hidden'}
|
|
154
141
|
onmouseenter={() => {
|
|
155
142
|
hovered = true;
|
|
156
143
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export function activatePopup(...args: any[]): Popup;
|
|
2
2
|
/**
|
|
3
|
-
* @import { Writable } from 'svelte/store';
|
|
4
3
|
* @import { PopupPosition } from '../typedefs';
|
|
5
4
|
*/
|
|
6
5
|
/**
|
|
@@ -16,23 +15,23 @@ declare class Popup {
|
|
|
16
15
|
* will be the `anchorElement`.
|
|
17
16
|
*/
|
|
18
17
|
constructor(anchorElement: HTMLButtonElement, popupElement: HTMLDialogElement, position: PopupPosition, positionBaseElement?: HTMLElement | undefined);
|
|
19
|
-
open: Writable<boolean>;
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* zIndex: number | undefined,
|
|
24
|
-
* minWidth: string | undefined,
|
|
25
|
-
* maxWidth: string | undefined,
|
|
26
|
-
* height: string | undefined,
|
|
27
|
-
* }>}
|
|
19
|
+
* Open or close the popup, running side effects synchronously.
|
|
20
|
+
* @param {boolean} value `true` to open, `false` to close.
|
|
28
21
|
*/
|
|
29
|
-
|
|
22
|
+
set open(value: boolean);
|
|
23
|
+
/**
|
|
24
|
+
* Whether the popup is open.
|
|
25
|
+
* @returns {boolean} `true` if the popup is open.
|
|
26
|
+
*/
|
|
27
|
+
get open(): boolean;
|
|
28
|
+
style: {
|
|
30
29
|
inset: string | undefined;
|
|
31
30
|
zIndex: number | undefined;
|
|
32
31
|
minWidth: string | undefined;
|
|
33
32
|
maxWidth: string | undefined;
|
|
34
33
|
height: string | undefined;
|
|
35
|
-
}
|
|
34
|
+
};
|
|
36
35
|
observer: IntersectionObserver;
|
|
37
36
|
anchorElement: HTMLButtonElement;
|
|
38
37
|
popupElement: HTMLDialogElement;
|
|
@@ -58,7 +57,7 @@ declare class Popup {
|
|
|
58
57
|
* Hide the popup immediately (when the anchor is being hidden).
|
|
59
58
|
*/
|
|
60
59
|
hideImmediately(): Promise<void>;
|
|
60
|
+
#private;
|
|
61
61
|
}
|
|
62
|
-
import type { Writable } from 'svelte/store';
|
|
63
62
|
import type { PopupPosition } from '../typedefs';
|
|
64
63
|
export {};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { generateElementId } from '@sveltia/utils/element';
|
|
2
2
|
import { sleep } from '@sveltia/utils/misc';
|
|
3
3
|
import { on } from 'svelte/events';
|
|
4
|
-
import { get, writable } from 'svelte/store';
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
|
-
* @import { Writable } from 'svelte/store';
|
|
8
5
|
* @import { PopupPosition } from '../typedefs';
|
|
9
6
|
*/
|
|
10
7
|
|
|
@@ -12,24 +9,46 @@ import { get, writable } from 'svelte/store';
|
|
|
12
9
|
* Implement the popup handler.
|
|
13
10
|
*/
|
|
14
11
|
class Popup {
|
|
15
|
-
open =
|
|
12
|
+
#open = $state(false);
|
|
16
13
|
|
|
17
14
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* zIndex: number | undefined,
|
|
21
|
-
* minWidth: string | undefined,
|
|
22
|
-
* maxWidth: string | undefined,
|
|
23
|
-
* height: string | undefined,
|
|
24
|
-
* }>}
|
|
15
|
+
* Whether the popup is open.
|
|
16
|
+
* @returns {boolean} `true` if the popup is open.
|
|
25
17
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
get open() {
|
|
19
|
+
return this.#open;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Open or close the popup, running side effects synchronously.
|
|
24
|
+
* @param {boolean} value `true` to open, `false` to close.
|
|
25
|
+
*/
|
|
26
|
+
set open(value) {
|
|
27
|
+
this.#open = value;
|
|
28
|
+
|
|
29
|
+
if (value) {
|
|
30
|
+
this.checkPosition();
|
|
31
|
+
} else if (this.anchorElement.getAttribute('aria-expanded') === 'true') {
|
|
32
|
+
this.anchorElement.focus();
|
|
33
|
+
this.anchorElement.removeAttribute('aria-controls');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.anchorElement.setAttribute('aria-expanded', String(value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
style = $state(
|
|
40
|
+
/**
|
|
41
|
+
* @type {{ inset: string | undefined, zIndex: number | undefined, minWidth: string | undefined,
|
|
42
|
+
* maxWidth: string | undefined, height: string | undefined }}
|
|
43
|
+
*/
|
|
44
|
+
({
|
|
45
|
+
inset: undefined,
|
|
46
|
+
zIndex: undefined,
|
|
47
|
+
minWidth: undefined,
|
|
48
|
+
maxWidth: undefined,
|
|
49
|
+
height: undefined,
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
33
52
|
|
|
34
53
|
observer = new IntersectionObserver((entries) => {
|
|
35
54
|
entries.forEach(({ intersectionRect, rootBounds }) => {
|
|
@@ -120,16 +139,14 @@ class Popup {
|
|
|
120
139
|
height: height ? `${Math.round(height)}px` : 'auto',
|
|
121
140
|
};
|
|
122
141
|
|
|
123
|
-
const current = get(this.style);
|
|
124
|
-
|
|
125
142
|
if (
|
|
126
|
-
style.inset !==
|
|
127
|
-
style.zIndex !==
|
|
128
|
-
style.minWidth !==
|
|
129
|
-
style.maxWidth !==
|
|
130
|
-
style.height !==
|
|
143
|
+
style.inset !== this.style.inset ||
|
|
144
|
+
style.zIndex !== this.style.zIndex ||
|
|
145
|
+
style.minWidth !== this.style.minWidth ||
|
|
146
|
+
style.maxWidth !== this.style.maxWidth ||
|
|
147
|
+
style.height !== this.style.height
|
|
131
148
|
) {
|
|
132
|
-
this.style
|
|
149
|
+
this.style = style;
|
|
133
150
|
}
|
|
134
151
|
});
|
|
135
152
|
});
|
|
@@ -151,10 +168,11 @@ class Popup {
|
|
|
151
168
|
|
|
152
169
|
this.anchorElement.setAttribute('aria-controls', this.id);
|
|
153
170
|
this.popupElement.setAttribute('id', this.id);
|
|
171
|
+
this.anchorElement.setAttribute('aria-expanded', 'false');
|
|
154
172
|
|
|
155
173
|
on(anchorElement, 'click', () => {
|
|
156
174
|
if (!this.isDisabled && !this.isReadOnly) {
|
|
157
|
-
this.open
|
|
175
|
+
this.open = !this.open;
|
|
158
176
|
}
|
|
159
177
|
});
|
|
160
178
|
|
|
@@ -165,7 +183,7 @@ class Popup {
|
|
|
165
183
|
if (!this.isDisabled && !this.isReadOnly && ['Enter', ' '].includes(key) && !hasModifier) {
|
|
166
184
|
event.preventDefault();
|
|
167
185
|
event.stopPropagation();
|
|
168
|
-
this.open
|
|
186
|
+
this.open = !this.open;
|
|
169
187
|
}
|
|
170
188
|
});
|
|
171
189
|
|
|
@@ -176,7 +194,7 @@ class Popup {
|
|
|
176
194
|
});
|
|
177
195
|
|
|
178
196
|
new IntersectionObserver(([entry]) => {
|
|
179
|
-
if (!entry.isIntersecting &&
|
|
197
|
+
if (!entry.isIntersecting && this.open) {
|
|
180
198
|
this.hideImmediately();
|
|
181
199
|
}
|
|
182
200
|
}).observe(this.anchorElement);
|
|
@@ -189,10 +207,10 @@ class Popup {
|
|
|
189
207
|
const target = /** @type {HTMLElement} */ (event.target);
|
|
190
208
|
|
|
191
209
|
if (
|
|
192
|
-
|
|
210
|
+
this.open &&
|
|
193
211
|
(target === this.popupElement || target.matches('[role^="menuitem"], [role="option"]'))
|
|
194
212
|
) {
|
|
195
|
-
this.open
|
|
213
|
+
this.open = false;
|
|
196
214
|
}
|
|
197
215
|
});
|
|
198
216
|
|
|
@@ -203,21 +221,10 @@ class Popup {
|
|
|
203
221
|
if (key === 'Escape' && !hasModifier) {
|
|
204
222
|
event.preventDefault();
|
|
205
223
|
event.stopPropagation();
|
|
206
|
-
this.open
|
|
224
|
+
this.open = false;
|
|
207
225
|
}
|
|
208
226
|
});
|
|
209
227
|
|
|
210
|
-
this.open.subscribe((open) => {
|
|
211
|
-
if (open) {
|
|
212
|
-
this.checkPosition();
|
|
213
|
-
} else if (this.anchorElement.getAttribute('aria-expanded') === 'true') {
|
|
214
|
-
this.anchorElement.focus();
|
|
215
|
-
this.anchorElement.removeAttribute('aria-controls');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
this.anchorElement.setAttribute('aria-expanded', String(open));
|
|
219
|
-
});
|
|
220
|
-
|
|
221
228
|
// Update the popup width when the base element is resized
|
|
222
229
|
new ResizeObserver(() => {
|
|
223
230
|
cancelAnimationFrame(this._rafId);
|
|
@@ -254,7 +261,7 @@ class Popup {
|
|
|
254
261
|
*/
|
|
255
262
|
async hideImmediately() {
|
|
256
263
|
this.popupElement.hidden = true;
|
|
257
|
-
this.open
|
|
264
|
+
this.open = false;
|
|
258
265
|
await sleep(50);
|
|
259
266
|
this.popupElement.hidden = false;
|
|
260
267
|
}
|
package/package.json
CHANGED
|
@@ -1,101 +1,109 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltia/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "0.37.1",
|
|
4
|
+
"description": "A collection of Svelte components and utilities for building user interfaces.",
|
|
6
5
|
"repository": {
|
|
7
6
|
"type": "git",
|
|
8
|
-
"url": "github
|
|
7
|
+
"url": "git+https://github.com/sveltia/sveltia-ui.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Kohei Yoshino",
|
|
12
|
+
"url": "https://github.com/kyoshino"
|
|
13
|
+
},
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"svelte": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
9
22
|
},
|
|
23
|
+
"svelte": "./dist/index.js",
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
">4.0": {
|
|
26
|
+
"index": [
|
|
27
|
+
"./dist/index.d.ts"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"!dist/**/*.test.*"
|
|
34
|
+
],
|
|
10
35
|
"dependencies": {
|
|
11
|
-
"@lexical/code": "^0.
|
|
12
|
-
"@lexical/code-prism": "^0.
|
|
13
|
-
"@lexical/dragon": "^0.
|
|
14
|
-
"@lexical/extension": "^0.
|
|
15
|
-
"@lexical/history": "^0.
|
|
16
|
-
"@lexical/link": "^0.
|
|
17
|
-
"@lexical/list": "^0.
|
|
18
|
-
"@lexical/markdown": "^0.
|
|
19
|
-
"@lexical/rich-text": "^0.
|
|
20
|
-
"@lexical/selection": "^0.
|
|
21
|
-
"@lexical/table": "^0.
|
|
22
|
-
"@lexical/utils": "^0.
|
|
23
|
-
"@sveltia/i18n": "^0.
|
|
36
|
+
"@lexical/code": "^0.43.0",
|
|
37
|
+
"@lexical/code-prism": "^0.43.0",
|
|
38
|
+
"@lexical/dragon": "^0.43.0",
|
|
39
|
+
"@lexical/extension": "^0.43.0",
|
|
40
|
+
"@lexical/history": "^0.43.0",
|
|
41
|
+
"@lexical/link": "^0.43.0",
|
|
42
|
+
"@lexical/list": "^0.43.0",
|
|
43
|
+
"@lexical/markdown": "^0.43.0",
|
|
44
|
+
"@lexical/rich-text": "^0.43.0",
|
|
45
|
+
"@lexical/selection": "^0.43.0",
|
|
46
|
+
"@lexical/table": "^0.43.0",
|
|
47
|
+
"@lexical/utils": "^0.43.0",
|
|
48
|
+
"@sveltia/i18n": "^1.0.2",
|
|
24
49
|
"@sveltia/utils": "^0.10.0",
|
|
25
|
-
"lexical": "^0.
|
|
50
|
+
"lexical": "^0.43.0",
|
|
26
51
|
"prismjs": "^1.30.0",
|
|
27
52
|
"yaml": "^2.8.3"
|
|
28
53
|
},
|
|
29
|
-
"peerDependencies": {
|
|
30
|
-
"svelte": "^5.0.0"
|
|
31
|
-
},
|
|
32
54
|
"devDependencies": {
|
|
33
55
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
34
|
-
"@sveltejs/kit": "^2.
|
|
56
|
+
"@sveltejs/kit": "^2.57.0",
|
|
35
57
|
"@sveltejs/package": "^2.5.7",
|
|
36
58
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
37
|
-
"@vitest/coverage-v8": "^4.1.
|
|
38
|
-
"cspell": "^
|
|
59
|
+
"@vitest/coverage-v8": "^4.1.3",
|
|
60
|
+
"cspell": "^10.0.0",
|
|
39
61
|
"eslint": "^9.39.4",
|
|
40
|
-
"eslint-config-airbnb-extended": "^3.0
|
|
62
|
+
"eslint-config-airbnb-extended": "^3.1.0",
|
|
41
63
|
"eslint-config-prettier": "^10.1.8",
|
|
42
64
|
"eslint-plugin-import": "^2.32.0",
|
|
43
65
|
"eslint-plugin-jsdoc": "^62.9.0",
|
|
66
|
+
"eslint-plugin-package-json": "^0.91.1",
|
|
44
67
|
"eslint-plugin-svelte": "^3.17.0",
|
|
45
68
|
"globals": "^17.4.0",
|
|
46
69
|
"happy-dom": "^20.8.9",
|
|
47
|
-
"oxlint": "^1.
|
|
48
|
-
"postcss": "^8.5.
|
|
70
|
+
"oxlint": "^1.59.0",
|
|
71
|
+
"postcss": "^8.5.9",
|
|
49
72
|
"postcss-html": "^1.8.1",
|
|
50
73
|
"prettier": "^3.8.1",
|
|
51
74
|
"prettier-plugin-svelte": "^3.5.1",
|
|
52
75
|
"sass": "^1.99.0",
|
|
53
76
|
"stylelint": "^17.6.0",
|
|
54
|
-
"stylelint-config-recommended-scss": "^17.0.
|
|
77
|
+
"stylelint-config-recommended-scss": "^17.0.1",
|
|
55
78
|
"stylelint-scss": "^7.0.0",
|
|
56
|
-
"svelte": "^5.55.
|
|
79
|
+
"svelte": "^5.55.2",
|
|
57
80
|
"svelte-check": "^4.4.6",
|
|
58
81
|
"svelte-preprocess": "^6.0.3",
|
|
59
82
|
"tslib": "^2.8.1",
|
|
60
|
-
"vite": "^8.0.
|
|
61
|
-
"vitest": "^4.1.
|
|
83
|
+
"vite": "^8.0.7",
|
|
84
|
+
"vitest": "^4.1.3"
|
|
62
85
|
},
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"types": "./dist/index.d.ts",
|
|
66
|
-
"svelte": "./dist/index.js",
|
|
67
|
-
"default": "./dist/index.js"
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"files": [
|
|
71
|
-
"dist"
|
|
72
|
-
],
|
|
73
|
-
"svelte": "./dist/index.js",
|
|
74
|
-
"typesVersions": {
|
|
75
|
-
">4.0": {
|
|
76
|
-
"index": [
|
|
77
|
-
"./dist/index.d.ts"
|
|
78
|
-
]
|
|
79
|
-
}
|
|
86
|
+
"peerDependencies": {
|
|
87
|
+
"svelte": "^5.0.0"
|
|
80
88
|
},
|
|
81
89
|
"publishConfig": {
|
|
82
90
|
"access": "public",
|
|
83
91
|
"provenance": true
|
|
84
92
|
},
|
|
85
93
|
"scripts": {
|
|
86
|
-
"dev": "vite dev",
|
|
87
94
|
"build": "svelte-kit sync && svelte-package",
|
|
88
95
|
"build:watch": "svelte-kit sync && svelte-package --watch",
|
|
89
|
-
"preview": "vite preview",
|
|
90
|
-
"format": "prettier --write .",
|
|
91
96
|
"check": "pnpm run '/^check:.*/'",
|
|
92
97
|
"check:audit": "pnpm audit",
|
|
93
98
|
"check:cspell": "cspell --no-progress",
|
|
94
|
-
"check:svelte": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
|
|
95
|
-
"check:prettier": "prettier --check .",
|
|
96
99
|
"check:eslint": "eslint .",
|
|
97
100
|
"check:oxlint": "oxlint .",
|
|
101
|
+
"check:prettier": "prettier --check .",
|
|
98
102
|
"check:stylelint": "stylelint '**/*.{css,scss,svelte}'",
|
|
103
|
+
"check:svelte": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
|
|
104
|
+
"dev": "vite dev",
|
|
105
|
+
"format": "prettier --write .",
|
|
106
|
+
"preview": "vite preview",
|
|
99
107
|
"test": "vitest run",
|
|
100
108
|
"test:coverage": "vitest run --coverage",
|
|
101
109
|
"test:watch": "vitest"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
AVAILABLE_BUTTONS,
|
|
4
|
-
BLOCK_BUTTON_TYPES,
|
|
5
|
-
IMAGE_COMPONENT_IDS,
|
|
6
|
-
INLINE_BUTTON_TYPES,
|
|
7
|
-
TEXT_FORMAT_BUTTON_TYPES,
|
|
8
|
-
} from './constants.js';
|
|
9
|
-
|
|
10
|
-
describe('AVAILABLE_BUTTONS', () => {
|
|
11
|
-
it('should contain all expected button keys', () => {
|
|
12
|
-
const expectedKeys = [
|
|
13
|
-
'bold',
|
|
14
|
-
'italic',
|
|
15
|
-
'strikethrough',
|
|
16
|
-
'code',
|
|
17
|
-
'link',
|
|
18
|
-
'paragraph',
|
|
19
|
-
'heading-1',
|
|
20
|
-
'heading-2',
|
|
21
|
-
'heading-3',
|
|
22
|
-
'heading-4',
|
|
23
|
-
'heading-5',
|
|
24
|
-
'heading-6',
|
|
25
|
-
'bulleted-list',
|
|
26
|
-
'numbered-list',
|
|
27
|
-
'blockquote',
|
|
28
|
-
'code-block',
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
expectedKeys.forEach((key) => {
|
|
32
|
-
expect(AVAILABLE_BUTTONS).toHaveProperty(key);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should mark inline buttons correctly', () => {
|
|
37
|
-
expect(AVAILABLE_BUTTONS.bold.inline).toBe(true);
|
|
38
|
-
expect(AVAILABLE_BUTTONS.italic.inline).toBe(true);
|
|
39
|
-
expect(AVAILABLE_BUTTONS.link.inline).toBe(true);
|
|
40
|
-
expect(AVAILABLE_BUTTONS.paragraph.inline).toBe(false);
|
|
41
|
-
expect(AVAILABLE_BUTTONS['heading-1'].inline).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should have a labelKey and icon for each button', () => {
|
|
45
|
-
Object.values(AVAILABLE_BUTTONS).forEach(({ labelKey, icon }) => {
|
|
46
|
-
expect(typeof labelKey).toBe('string');
|
|
47
|
-
expect(labelKey.length).toBeGreaterThan(0);
|
|
48
|
-
expect(typeof icon).toBe('string');
|
|
49
|
-
expect(icon.length).toBeGreaterThan(0);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('TEXT_FORMAT_BUTTON_TYPES', () => {
|
|
55
|
-
it('should include bold, italic, strikethrough, code', () => {
|
|
56
|
-
expect(TEXT_FORMAT_BUTTON_TYPES).toEqual(['bold', 'italic', 'strikethrough', 'code']);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('INLINE_BUTTON_TYPES', () => {
|
|
61
|
-
it('should include all text format types plus link', () => {
|
|
62
|
-
expect(INLINE_BUTTON_TYPES).toContain('bold');
|
|
63
|
-
expect(INLINE_BUTTON_TYPES).toContain('italic');
|
|
64
|
-
expect(INLINE_BUTTON_TYPES).toContain('strikethrough');
|
|
65
|
-
expect(INLINE_BUTTON_TYPES).toContain('code');
|
|
66
|
-
expect(INLINE_BUTTON_TYPES).toContain('link');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should be a superset of TEXT_FORMAT_BUTTON_TYPES', () => {
|
|
70
|
-
TEXT_FORMAT_BUTTON_TYPES.forEach((type) => {
|
|
71
|
-
expect(INLINE_BUTTON_TYPES).toContain(type);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('BLOCK_BUTTON_TYPES', () => {
|
|
77
|
-
it('should include paragraph and all heading levels', () => {
|
|
78
|
-
expect(BLOCK_BUTTON_TYPES).toContain('paragraph');
|
|
79
|
-
|
|
80
|
-
for (let i = 1; i <= 6; i += 1) {
|
|
81
|
-
expect(BLOCK_BUTTON_TYPES).toContain(`heading-${i}`);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should include list, blockquote, and code-block types', () => {
|
|
86
|
-
expect(BLOCK_BUTTON_TYPES).toContain('bulleted-list');
|
|
87
|
-
expect(BLOCK_BUTTON_TYPES).toContain('numbered-list');
|
|
88
|
-
expect(BLOCK_BUTTON_TYPES).toContain('blockquote');
|
|
89
|
-
expect(BLOCK_BUTTON_TYPES).toContain('code-block');
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('IMAGE_COMPONENT_IDS', () => {
|
|
94
|
-
it('should include image and linked-image', () => {
|
|
95
|
-
expect(IMAGE_COMPONENT_IDS).toContain('image');
|
|
96
|
-
expect(IMAGE_COMPONENT_IDS).toContain('linked-image');
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { increaseListIndentation, splitMultilineFormatting } from './markdown.js';
|
|
3
|
-
|
|
4
|
-
describe('splitMultilineFormatting', () => {
|
|
5
|
-
it('should split italic formatting across lines', () => {
|
|
6
|
-
expect(splitMultilineFormatting(' _foo\nbar_ ')).toBe(' _foo_\n_bar_ ');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('should split bold formatting across lines', () => {
|
|
10
|
-
expect(splitMultilineFormatting(' **foo\nbar** ')).toBe(' **foo**\n**bar** ');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should split strikethrough formatting across lines', () => {
|
|
14
|
-
expect(splitMultilineFormatting(' ~~foo\nbar~~ ')).toBe(' ~~foo~~\n~~bar~~ ');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should split code formatting across lines', () => {
|
|
18
|
-
expect(splitMultilineFormatting(' `foo\nbar` ')).toBe(' `foo`\n`bar` ');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should handle multiple formatting types', () => {
|
|
22
|
-
expect(
|
|
23
|
-
splitMultilineFormatting(' _italic\ntext_ **bold\ntext** ~~strike\nthrough~~ '),
|
|
24
|
-
).toBe(' _italic_\n_text_ **bold**\n**text** ~~strike~~\n~~through~~ ');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should not affect properly formatted single-line text', () => {
|
|
28
|
-
expect(splitMultilineFormatting('_italic_ **bold** ~~strike~~ `code`')).toBe(
|
|
29
|
-
'_italic_ **bold** ~~strike~~ `code`',
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should only split when surrounded by whitespace', () => {
|
|
34
|
-
expect(splitMultilineFormatting('_foo\nbar_')).toBe('_foo\nbar_');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should handle formatting at different indentation levels', () => {
|
|
38
|
-
expect(splitMultilineFormatting(' _foo\nbar_ ')).toBe(' _foo_\n_bar_ ');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('increaseListIndentation', () => {
|
|
43
|
-
it('should double indentation for bullet lists', () => {
|
|
44
|
-
expect(increaseListIndentation(' - item')).toBe(' - item');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should double indentation for numbered lists', () => {
|
|
48
|
-
expect(increaseListIndentation(' 1. item')).toBe(' 1. item');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should handle different list markers', () => {
|
|
52
|
-
expect(increaseListIndentation(' - a\n + b\n * c')).toBe(' - a\n + b\n * c');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should double different indentation levels', () => {
|
|
56
|
-
expect(increaseListIndentation(' - level 1\n - level 2')).toBe(
|
|
57
|
-
' - level 1\n - level 2',
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should not affect non-list content', () => {
|
|
62
|
-
expect(increaseListIndentation('regular text')).toBe('regular text');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should not affect lists without preceding spaces', () => {
|
|
66
|
-
expect(increaseListIndentation('- item')).toBe('- item');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should handle mixed content', () => {
|
|
70
|
-
expect(increaseListIndentation('paragraph\n - item\nmore text')).toBe(
|
|
71
|
-
'paragraph\n - item\nmore text',
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should handle lists with various indentation levels', () => {
|
|
76
|
-
expect(increaseListIndentation(' - a\n - b\n - c')).toBe(
|
|
77
|
-
' - a\n - b\n - c',
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should return unchanged string if no matching lists', () => {
|
|
82
|
-
expect(increaseListIndentation('paragraph with - no list')).toBe('paragraph with - no list');
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|