@marianmeres/stuic 2.3.2 → 2.4.0
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/README.md +0 -1
- package/dist/README.md +17 -11
- package/dist/actions/autogrow.svelte.js +4 -4
- package/dist/actions/autoscroll.d.ts +2 -2
- package/dist/actions/autoscroll.js +17 -8
- package/dist/actions/file-dropzone.svelte.d.ts +1 -1
- package/dist/actions/file-dropzone.svelte.js +1 -1
- package/dist/actions/focus-trap.js +33 -24
- package/dist/actions/highlight-dragover.svelte.js +6 -5
- package/dist/actions/on-submit-validity-check.svelte.js +2 -2
- package/dist/actions/resizable-width.svelte.d.ts +1 -1
- package/dist/actions/resizable-width.svelte.js +7 -5
- package/dist/actions/tooltip/index.css +2 -7
- package/dist/actions/tooltip/tooltip.svelte.js +5 -4
- package/dist/actions/trim.svelte.d.ts +1 -1
- package/dist/actions/trim.svelte.js +2 -2
- package/dist/actions/validate.svelte.d.ts +4 -4
- package/dist/actions/validate.svelte.js +9 -9
- package/dist/components/AlertConfirmPrompt/alert-confirm-prompt-stack.svelte.d.ts +7 -6
- package/dist/components/AlertConfirmPrompt/alert-confirm-prompt-stack.svelte.js +1 -2
- package/dist/components/AlertConfirmPrompt/index.d.ts +1 -1
- package/dist/components/AlertConfirmPrompt/index.js +1 -1
- package/dist/components/AnimatedElipsis/index.d.ts +1 -1
- package/dist/components/AnimatedElipsis/index.js +1 -1
- package/dist/components/Button/index.d.ts +1 -1
- package/dist/components/Button/index.js +1 -1
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +4 -1
- package/dist/components/ButtonGroupRadio/index.d.ts +1 -1
- package/dist/components/ButtonGroupRadio/index.js +1 -1
- package/dist/components/ColorScheme/index.d.ts +2 -2
- package/dist/components/ColorScheme/index.js +2 -2
- package/dist/components/CommandMenu/index.d.ts +1 -1
- package/dist/components/CommandMenu/index.js +1 -1
- package/dist/components/DismissibleMessage/index.d.ts +1 -1
- package/dist/components/DismissibleMessage/index.js +1 -1
- package/dist/components/HoverExpandableWidth/index.d.ts +1 -1
- package/dist/components/HoverExpandableWidth/index.js +1 -1
- package/dist/components/Input/FieldAssets.svelte +7 -3
- package/dist/components/Input/FieldLikeButton.svelte +1 -1
- package/dist/components/Input/FieldOptions.svelte +1 -1
- package/dist/components/Input/index.d.ts +7 -7
- package/dist/components/Input/index.js +7 -7
- package/dist/components/KbdShortcut/index.d.ts +1 -1
- package/dist/components/KbdShortcut/index.js +1 -1
- package/dist/components/ModalDialog/index.d.ts +1 -1
- package/dist/components/ModalDialog/index.js +1 -1
- package/dist/components/Notifications/index.d.ts +1 -1
- package/dist/components/Notifications/index.js +1 -1
- package/dist/components/Notifications/notifications-stack.svelte.d.ts +5 -5
- package/dist/components/Notifications/notifications-stack.svelte.js +8 -7
- package/dist/components/SlidingPanels/index.d.ts +1 -1
- package/dist/components/SlidingPanels/index.js +1 -1
- package/dist/components/Spinner/index.d.ts +1 -1
- package/dist/components/Spinner/index.js +1 -1
- package/dist/components/Switch/Switch.svelte +5 -2
- package/dist/components/Switch/index.d.ts +1 -1
- package/dist/components/Switch/index.js +1 -1
- package/dist/components/TypeaheadInput/index.d.ts +1 -1
- package/dist/components/TypeaheadInput/index.js +1 -1
- package/dist/utils/body-scroll-locker.js +4 -3
- package/dist/utils/breakpoint.svelte.js +0 -2
- package/dist/utils/colors.js +3 -3
- package/dist/utils/debounce.d.ts +1 -1
- package/dist/utils/debounce.js +1 -2
- package/dist/utils/escape-regex.js +1 -1
- package/dist/utils/event-emitter.d.ts +2 -3
- package/dist/utils/event-emitter.js +1 -2
- package/dist/utils/event-modifiers.d.ts +4 -4
- package/dist/utils/event-modifiers.js +4 -6
- package/dist/utils/get-file-type-label.js +1 -1
- package/dist/utils/is-image.js +2 -2
- package/dist/utils/is-nullish.d.ts +1 -1
- package/dist/utils/is-plain-object.d.ts +1 -1
- package/dist/utils/is-plain-object.js +4 -1
- package/dist/utils/maybe-json-parse.d.ts +1 -1
- package/dist/utils/maybe-json-parse.js +1 -1
- package/dist/utils/maybe-json-stringify.d.ts +1 -1
- package/dist/utils/move-array-item.d.ts +1 -1
- package/dist/utils/preload-img.js +2 -1
- package/dist/utils/sleep.d.ts +1 -1
- package/dist/utils/storage-abstraction.d.ts +13 -13
- package/dist/utils/storage-abstraction.js +2 -0
- package/dist/utils/svg-circle.js +2 -1
- package/dist/utils/switch.svelte.d.ts +1 -1
- package/dist/utils/switch.svelte.js +1 -1
- package/dist/utils/throttle.d.ts +1 -1
- package/dist/utils/throttle.js +7 -8
- package/dist/utils/to-integer.d.ts +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
package/dist/README.md
CHANGED
|
@@ -12,21 +12,22 @@ npm install @marianmeres/stuic
|
|
|
12
12
|
|
|
13
13
|
```svelte
|
|
14
14
|
<script>
|
|
15
|
-
|
|
15
|
+
import { Button, Modal } from "@marianmeres/stuic";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
let modal;
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<Button onclick={() => modal.open()}>Open Modal</Button>
|
|
21
21
|
|
|
22
22
|
<Modal bind:this={modal}>
|
|
23
|
-
|
|
23
|
+
<p>Hello from Modal!</p>
|
|
24
24
|
</Modal>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Components
|
|
28
28
|
|
|
29
29
|
### Layout & Overlays
|
|
30
|
+
|
|
30
31
|
- **AppShell** - Application layout wrapper with header, sidebar, main content
|
|
31
32
|
- **Backdrop** - Fullscreen overlay with transitions
|
|
32
33
|
- **Modal** - Dialog overlay with focus trap and scroll lock
|
|
@@ -34,6 +35,7 @@ npm install @marianmeres/stuic
|
|
|
34
35
|
- **Drawer** - Slide-in panel from screen edges
|
|
35
36
|
|
|
36
37
|
### Forms & Inputs
|
|
38
|
+
|
|
37
39
|
- **FieldInput** - Text input with label, validation, description
|
|
38
40
|
- **FieldTextarea** - Multiline text input with autogrow support
|
|
39
41
|
- **FieldSelect** - Native select dropdown
|
|
@@ -46,12 +48,14 @@ npm install @marianmeres/stuic
|
|
|
46
48
|
- **Fieldset** - Group of form fields with legend
|
|
47
49
|
|
|
48
50
|
### Buttons & Controls
|
|
51
|
+
|
|
49
52
|
- **Button** - Styled button with variants (primary, secondary, ghost)
|
|
50
53
|
- **ButtonGroupRadio** - Radio-style button group
|
|
51
54
|
- **Switch** - Toggle switch component
|
|
52
55
|
- **X** - Close/dismiss button icon
|
|
53
56
|
|
|
54
57
|
### Feedback & Notifications
|
|
58
|
+
|
|
55
59
|
- **Notifications** - Toast notification system
|
|
56
60
|
- **AlertConfirmPrompt** - Modal dialogs for alerts, confirms, and prompts
|
|
57
61
|
- **DismissibleMessage** - Closable message banner
|
|
@@ -59,11 +63,13 @@ npm install @marianmeres/stuic
|
|
|
59
63
|
- **Spinner** - Loading spinner animation
|
|
60
64
|
|
|
61
65
|
### Navigation & Interaction
|
|
66
|
+
|
|
62
67
|
- **CommandMenu** - Keyboard-driven command palette
|
|
63
68
|
- **TypeaheadInput** - Autocomplete text input
|
|
64
69
|
- **KbdShortcut** - Keyboard shortcut display
|
|
65
70
|
|
|
66
71
|
### Utilities
|
|
72
|
+
|
|
67
73
|
- **ColorScheme** - Dark/light mode management
|
|
68
74
|
- **Thc** - Render text, HTML, or component dynamically
|
|
69
75
|
- **SlidingPanels** - Animated panel transitions
|
|
@@ -86,7 +92,7 @@ npm install @marianmeres/stuic
|
|
|
86
92
|
## Utilities
|
|
87
93
|
|
|
88
94
|
```ts
|
|
89
|
-
import { debounce, throttle, twMerge, localStorageState } from
|
|
95
|
+
import { debounce, throttle, twMerge, localStorageState } from "@marianmeres/stuic";
|
|
90
96
|
```
|
|
91
97
|
|
|
92
98
|
- **debounce/throttle** - Function rate limiting
|
|
@@ -103,10 +109,10 @@ Components use CSS custom properties for theming. Override in your CSS:
|
|
|
103
109
|
|
|
104
110
|
```css
|
|
105
111
|
:root {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
--color-button-bg: theme("colors.blue.500");
|
|
113
|
+
--color-button-text: white;
|
|
114
|
+
--color-input-accent: theme("colors.blue.500");
|
|
115
|
+
/* ... */
|
|
110
116
|
}
|
|
111
117
|
```
|
|
112
118
|
|
|
@@ -117,10 +123,10 @@ See `src/lib/theme.css` for all available custom properties.
|
|
|
117
123
|
All components export their Props types:
|
|
118
124
|
|
|
119
125
|
```ts
|
|
120
|
-
import type { ButtonProps, ModalProps } from
|
|
126
|
+
import type { ButtonProps, ModalProps } from "@marianmeres/stuic";
|
|
121
127
|
|
|
122
128
|
const buttonConfig: Partial<ButtonProps> = {
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
variant: "primary",
|
|
130
|
+
size: "lg",
|
|
125
131
|
};
|
|
126
132
|
```
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
export function autogrow(el, fn) {
|
|
26
26
|
let lastValue = el.value;
|
|
27
27
|
$effect(() => {
|
|
28
|
-
|
|
28
|
+
const { enabled = true, max = 250, immediate = true, value } = fn?.() || {};
|
|
29
29
|
if (!enabled)
|
|
30
30
|
return;
|
|
31
|
-
function set_height(
|
|
31
|
+
function set_height() {
|
|
32
32
|
// console.log(123, el.value);
|
|
33
33
|
if (enabled) {
|
|
34
34
|
el.style.height = "auto"; // Reset height to auto to correctly calculate scrollHeight
|
|
@@ -38,14 +38,14 @@ export function autogrow(el, fn) {
|
|
|
38
38
|
// eventlistener strategy (we're not passing value)
|
|
39
39
|
if (value === undefined) {
|
|
40
40
|
if (immediate)
|
|
41
|
-
set_height(
|
|
41
|
+
set_height();
|
|
42
42
|
el.addEventListener("input", set_height);
|
|
43
43
|
el.addEventListener("blur", set_height);
|
|
44
44
|
}
|
|
45
45
|
// strategy with provided value
|
|
46
46
|
else {
|
|
47
47
|
if (value !== lastValue) {
|
|
48
|
-
set_height(
|
|
48
|
+
set_height();
|
|
49
49
|
lastValue = value;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -12,8 +12,8 @@ interface StoreLike<T> extends StoreReadable<T> {
|
|
|
12
12
|
* Options for the autoscroll action.
|
|
13
13
|
*/
|
|
14
14
|
export type AutoscrollOptions = ScrollOptions & {
|
|
15
|
-
dependencies?: StoreReadable<
|
|
16
|
-
logger?: (...args:
|
|
15
|
+
dependencies?: StoreReadable<unknown>[];
|
|
16
|
+
logger?: (...args: unknown[]) => void;
|
|
17
17
|
newScrollableContentSignal?: StoreLike<boolean>;
|
|
18
18
|
shouldScrollThresholdPx?: number;
|
|
19
19
|
startScrollTimeout?: number;
|
|
@@ -43,16 +43,19 @@ export function autoscroll(node, options = {
|
|
|
43
43
|
startScrollTimeout: DEFAULTS.startScrollTimeout,
|
|
44
44
|
}) {
|
|
45
45
|
// use "smooth" by default
|
|
46
|
-
options.behavior ??=
|
|
46
|
+
options.behavior ??= "smooth";
|
|
47
47
|
options.shouldScrollThresholdPx ??= DEFAULTS.shouldScrollThresholdPx;
|
|
48
48
|
options.startScrollTimeout ??= DEFAULTS.startScrollTimeout;
|
|
49
49
|
const { behavior, shouldScrollThresholdPx, dependencies, logger, newScrollableContentSignal, startScrollTimeout, } = options || {};
|
|
50
50
|
let origScrollHeight = 0;
|
|
51
|
-
const log = (...args) =>
|
|
51
|
+
const log = (...args) => {
|
|
52
|
+
if (typeof logger === "function")
|
|
53
|
+
logger.apply(null, [...args]);
|
|
54
|
+
};
|
|
52
55
|
const shouldScroll = () => {
|
|
53
56
|
const { scrollTop, clientHeight } = node;
|
|
54
57
|
const result = origScrollHeight - scrollTop - clientHeight < shouldScrollThresholdPx;
|
|
55
|
-
log(
|
|
58
|
+
log("shouldScroll?", result, { scrollTop, origScrollHeight, clientHeight });
|
|
56
59
|
return result;
|
|
57
60
|
};
|
|
58
61
|
const scroll = () => {
|
|
@@ -62,17 +65,23 @@ export function autoscroll(node, options = {
|
|
|
62
65
|
};
|
|
63
66
|
// for when children change sizes
|
|
64
67
|
const resizeObserver = new ResizeObserver(() => {
|
|
65
|
-
log(
|
|
66
|
-
shouldScroll()
|
|
68
|
+
log("observed resize...");
|
|
69
|
+
if (shouldScroll())
|
|
70
|
+
scroll();
|
|
67
71
|
});
|
|
68
72
|
// for when children
|
|
69
73
|
const mutationObserver = new MutationObserver(() => {
|
|
70
|
-
log(
|
|
71
|
-
shouldScroll()
|
|
74
|
+
log("observed mutation...");
|
|
75
|
+
if (shouldScroll()) {
|
|
76
|
+
scroll();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
newScrollableContentSignal?.set(true);
|
|
80
|
+
}
|
|
72
81
|
origScrollHeight = node.scrollHeight;
|
|
73
82
|
});
|
|
74
83
|
const unsubs = dependencies?.map((dep) => dep.subscribe((v) => {
|
|
75
|
-
log(
|
|
84
|
+
log("dependency update...", v);
|
|
76
85
|
setTimeout(scroll, startScrollTimeout);
|
|
77
86
|
})) ?? [];
|
|
78
87
|
// observe size of all children
|
|
@@ -5,7 +5,7 @@ interface FileDropzoneOptions {
|
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
inputEl: HTMLInputElement;
|
|
7
7
|
allowClick?: boolean;
|
|
8
|
-
processFiles?: (files: FileList | null, wasDrop?: boolean) =>
|
|
8
|
+
processFiles?: (files: FileList | null, wasDrop?: boolean) => void | Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* A Svelte action that turns any element into a file drop zone.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
export function fileDropzone(el, fn) {
|
|
42
42
|
$effect(() => {
|
|
43
|
-
|
|
43
|
+
const { enabled = true, allowClick = true, inputEl, processFiles, } = fn?.() || {};
|
|
44
44
|
if (!enabled)
|
|
45
45
|
return;
|
|
46
46
|
if (!inputEl) {
|
|
@@ -32,34 +32,36 @@ const defaults = { enabled: true, autoFocusFirst: true };
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
export function focusTrap(node, options = {}) {
|
|
35
|
-
let
|
|
35
|
+
let enabled;
|
|
36
|
+
const { enabled: _enabled, autoFocusFirst } = { ...defaults, ...(options || {}) };
|
|
37
|
+
enabled = _enabled ?? true;
|
|
36
38
|
const focusableSelectors = [
|
|
37
|
-
|
|
39
|
+
"[contentEditable=true]",
|
|
38
40
|
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
"button:not([disabled])",
|
|
42
|
+
"input:not([disabled])",
|
|
43
|
+
"select:not([disabled])",
|
|
44
|
+
"textarea:not([disabled])",
|
|
43
45
|
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
"a[href]",
|
|
47
|
+
"area[href]",
|
|
48
|
+
"details",
|
|
49
|
+
"iframe",
|
|
48
50
|
// see more below on tabindexes
|
|
49
51
|
'[tabindex]:not([tabindex^="-"])',
|
|
50
|
-
].join(
|
|
52
|
+
].join(",");
|
|
51
53
|
let first;
|
|
52
54
|
let last;
|
|
53
55
|
// When the first element is selected, shift+tab pressed, jump to the last selectable item.
|
|
54
56
|
function onFirstElemKeydown(e) {
|
|
55
|
-
if (e.shiftKey && e.code ===
|
|
57
|
+
if (e.shiftKey && e.code === "Tab") {
|
|
56
58
|
e.preventDefault();
|
|
57
59
|
last.focus();
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
// When the last item selected, tab pressed, jump to the first selectable item.
|
|
61
63
|
function onLastElemKeydown(e) {
|
|
62
|
-
if (!e.shiftKey && e.code ===
|
|
64
|
+
if (!e.shiftKey && e.code === "Tab") {
|
|
63
65
|
e.preventDefault();
|
|
64
66
|
first.focus();
|
|
65
67
|
}
|
|
@@ -68,15 +70,15 @@ export function focusTrap(node, options = {}) {
|
|
|
68
70
|
if (enabled === false)
|
|
69
71
|
return;
|
|
70
72
|
let maxTabindex = 0;
|
|
71
|
-
|
|
73
|
+
const focusable = [...node.querySelectorAll(focusableSelectors)]
|
|
72
74
|
// filter negative tabindexes (afaik there is no :not([disabled] OR [tabindex^="-"]))
|
|
73
75
|
.filter((e) => {
|
|
74
76
|
// reusing loop for a side job here... see sort below
|
|
75
|
-
maxTabindex = Math.max(maxTabindex, parseInt(e.getAttribute(
|
|
77
|
+
maxTabindex = Math.max(maxTabindex, parseInt(e.getAttribute("tabindex") || "0"));
|
|
76
78
|
//
|
|
77
|
-
if (e.getAttribute(
|
|
79
|
+
if (e.getAttribute("disabled") === "")
|
|
78
80
|
return false;
|
|
79
|
-
if ((e.getAttribute(
|
|
81
|
+
if ((e.getAttribute("tabindex") || "").startsWith("-"))
|
|
80
82
|
return false;
|
|
81
83
|
return true;
|
|
82
84
|
})
|
|
@@ -84,8 +86,8 @@ export function focusTrap(node, options = {}) {
|
|
|
84
86
|
// but must increase zero to max + 1 first, because browsers focus zeros as last...
|
|
85
87
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
|
|
86
88
|
.sort((e1, e2) => {
|
|
87
|
-
const a = parseInt(e1.getAttribute(
|
|
88
|
-
const b = parseInt(e2.getAttribute(
|
|
89
|
+
const a = parseInt(e1.getAttribute("tabindex") || "0") || maxTabindex + 1;
|
|
90
|
+
const b = parseInt(e2.getAttribute("tabindex") || "0") || maxTabindex + 1;
|
|
89
91
|
return a - b;
|
|
90
92
|
});
|
|
91
93
|
if (focusable.length) {
|
|
@@ -95,14 +97,16 @@ export function focusTrap(node, options = {}) {
|
|
|
95
97
|
if (!fromObserver && autoFocusFirst)
|
|
96
98
|
first.focus();
|
|
97
99
|
// Listen for keydown on first & last element
|
|
98
|
-
first.addEventListener(
|
|
99
|
-
last.addEventListener(
|
|
100
|
+
first.addEventListener("keydown", onFirstElemKeydown);
|
|
101
|
+
last.addEventListener("keydown", onLastElemKeydown);
|
|
100
102
|
}
|
|
101
103
|
};
|
|
102
104
|
queryElements(false);
|
|
103
105
|
function cleanup() {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if (first)
|
|
107
|
+
first.removeEventListener("keydown", onFirstElemKeydown);
|
|
108
|
+
if (last)
|
|
109
|
+
last.removeEventListener("keydown", onLastElemKeydown);
|
|
106
110
|
}
|
|
107
111
|
// When children of node are changed (added or removed)
|
|
108
112
|
const observer = new MutationObserver((mutations, observer) => {
|
|
@@ -117,7 +121,12 @@ export function focusTrap(node, options = {}) {
|
|
|
117
121
|
return {
|
|
118
122
|
update(options = {}) {
|
|
119
123
|
enabled = !!options?.enabled;
|
|
120
|
-
|
|
124
|
+
if (enabled) {
|
|
125
|
+
queryElements(false);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
cleanup();
|
|
129
|
+
}
|
|
121
130
|
},
|
|
122
131
|
destroy() {
|
|
123
132
|
cleanup();
|
|
@@ -32,15 +32,16 @@ export function highlightDragover(el, fn) {
|
|
|
32
32
|
// // Trigger change event for any listeners
|
|
33
33
|
// el.dispatchEvent(new Event("change"));
|
|
34
34
|
// }
|
|
35
|
-
function prevent(e) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
35
|
+
// function prevent(e: DragEvent) {
|
|
36
|
+
// e.preventDefault();
|
|
37
|
+
// // e.stopPropagation();
|
|
38
|
+
// }
|
|
39
39
|
const HIGH = ["dragenter", "dragover"];
|
|
40
40
|
const UNHIGH = ["dragleave", "drop"];
|
|
41
41
|
// const ALL = [...HIGH, ...UNHIGH];
|
|
42
42
|
$effect(() => {
|
|
43
|
-
|
|
43
|
+
const { enabled = true } = fn?.() || {};
|
|
44
|
+
let { classes = ["dragover"] } = fn?.() || {};
|
|
44
45
|
if (!enabled)
|
|
45
46
|
return;
|
|
46
47
|
if (!Array.isArray(classes))
|
|
@@ -47,9 +47,9 @@ export function onSubmitValidityCheck(node) {
|
|
|
47
47
|
e.preventDefault();
|
|
48
48
|
// this will disable all other onsubmit listeners...
|
|
49
49
|
e.stopImmediatePropagation();
|
|
50
|
-
|
|
50
|
+
const invalid = [];
|
|
51
51
|
for (let i = 0; i < node.elements?.length; i++) {
|
|
52
|
-
|
|
52
|
+
const el = node.elements[i];
|
|
53
53
|
if (typeof el.checkValidity === "function") {
|
|
54
54
|
// hm... radios are tricky, as triggering change automatically checks the last
|
|
55
55
|
// input (last radio input), which is not desired
|
|
@@ -16,7 +16,7 @@ export interface ResizableWidthOptions {
|
|
|
16
16
|
units: "px" | "%";
|
|
17
17
|
container: number;
|
|
18
18
|
}) => void;
|
|
19
|
-
debug?: (...args:
|
|
19
|
+
debug?: (...args: unknown[]) => void;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* A Svelte action that makes an element's width resizable via drag handle.
|
|
@@ -73,7 +73,8 @@ export function resizableWidth(el, fn) {
|
|
|
73
73
|
return handle;
|
|
74
74
|
}
|
|
75
75
|
$effect(() => {
|
|
76
|
-
|
|
76
|
+
const { enabled = true, initial: initialValue = 0, min = 0, max = 0, units = "px", key, storage = "session", handleClass = "", handleDragClass = "", onResize, debug, } = fn?.() || {};
|
|
77
|
+
let initial = initialValue;
|
|
77
78
|
const _debug = (...args) => debug?.("[resizable-width]", ...args);
|
|
78
79
|
_debug("$effect");
|
|
79
80
|
if (!enabled)
|
|
@@ -108,7 +109,8 @@ export function resizableWidth(el, fn) {
|
|
|
108
109
|
value = Math.max(min, value);
|
|
109
110
|
if (max)
|
|
110
111
|
value = Math.min(max, value);
|
|
111
|
-
_initial !== value
|
|
112
|
+
if (_initial !== value)
|
|
113
|
+
_debug("clamped", value, units);
|
|
112
114
|
return value;
|
|
113
115
|
};
|
|
114
116
|
let width;
|
|
@@ -132,7 +134,7 @@ export function resizableWidth(el, fn) {
|
|
|
132
134
|
e.preventDefault(); // prevent scrolling on touch devices
|
|
133
135
|
isResizing = true;
|
|
134
136
|
//
|
|
135
|
-
const clientX =
|
|
137
|
+
const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
|
|
136
138
|
startX = clientX;
|
|
137
139
|
startWidth = parseInt(getComputedStyle(el).width, 10);
|
|
138
140
|
containerW = container.offsetWidth;
|
|
@@ -145,9 +147,9 @@ export function resizableWidth(el, fn) {
|
|
|
145
147
|
return;
|
|
146
148
|
e.preventDefault(); // prevent scrolling on touch devices
|
|
147
149
|
//
|
|
148
|
-
const clientX =
|
|
150
|
+
const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
|
|
149
151
|
const deltaX = clientX - startX;
|
|
150
|
-
|
|
152
|
+
const width = startWidth + deltaX;
|
|
151
153
|
set_width_px(width);
|
|
152
154
|
}
|
|
153
155
|
function resize_stop() {
|
|
@@ -41,13 +41,8 @@
|
|
|
41
41
|
/* position-area is set via inline style based on position param */
|
|
42
42
|
/* fallbacks ensure tooltip stays within viewport */
|
|
43
43
|
position-try-fallbacks:
|
|
44
|
-
--tt-top-span-right,
|
|
45
|
-
--tt-
|
|
46
|
-
flip-block,
|
|
47
|
-
--tt-bottom-span-right,
|
|
48
|
-
--tt-bottom-span-left,
|
|
49
|
-
--tt-left,
|
|
50
|
-
--tt-right;
|
|
44
|
+
--tt-top-span-right, --tt-top-span-left, flip-block, --tt-bottom-span-right,
|
|
45
|
+
--tt-bottom-span-left, --tt-left, --tt-right;
|
|
51
46
|
|
|
52
47
|
&.tt-block {
|
|
53
48
|
display: block;
|
|
@@ -103,7 +103,8 @@ export function tooltip(anchorEl, fn) {
|
|
|
103
103
|
anchorEl.setAttribute("aria-describedby", id);
|
|
104
104
|
anchorEl.setAttribute("aria-expanded", "false");
|
|
105
105
|
const debug = (...args) => {
|
|
106
|
-
|
|
106
|
+
if (do_debug)
|
|
107
|
+
console.debug("[tooltip]", rnd, ...args);
|
|
107
108
|
};
|
|
108
109
|
function ensure_tooltip() {
|
|
109
110
|
debug("ensure_tooltip()", content, classTooltip);
|
|
@@ -132,7 +133,7 @@ export function tooltip(anchorEl, fn) {
|
|
|
132
133
|
// update position-area in case it changed
|
|
133
134
|
tooltipEl.style.setProperty("position-area", POSITION_MAP[position] || "top");
|
|
134
135
|
if (classTooltip) {
|
|
135
|
-
|
|
136
|
+
const old = tooltipEl.className;
|
|
136
137
|
tooltipEl.className = ""; // reset
|
|
137
138
|
tooltipEl.classList.add(...twMerge(old, classTooltip).split(/\s/));
|
|
138
139
|
}
|
|
@@ -201,12 +202,12 @@ export function tooltip(anchorEl, fn) {
|
|
|
201
202
|
}
|
|
202
203
|
// "reactive" params re/set
|
|
203
204
|
$effect(() => {
|
|
204
|
-
|
|
205
|
+
const { enabled: _enabled, content: _content, position: _position, debug: debugParam, class: _classTooltip, onShow, onHide, } = fn?.() || {};
|
|
205
206
|
// re/assign new params
|
|
206
207
|
do_debug = !!debugParam;
|
|
207
208
|
on_show = onShow;
|
|
208
209
|
on_hide = onHide;
|
|
209
|
-
content = _content
|
|
210
|
+
content = _content || anchorEl.getAttribute("aria-label");
|
|
210
211
|
classTooltip = _classTooltip;
|
|
211
212
|
enabled = _enabled ?? true;
|
|
212
213
|
position = _position || "top";
|
|
@@ -24,8 +24,8 @@ export function trim(el, fn) {
|
|
|
24
24
|
// the node has been mounted in the DOM
|
|
25
25
|
$effect(() => {
|
|
26
26
|
// setup goes here
|
|
27
|
-
|
|
28
|
-
function trim(
|
|
27
|
+
const { enabled, setValue } = fn?.() || { enabled: true };
|
|
28
|
+
function trim() {
|
|
29
29
|
if (enabled && typeof el.value === "string") {
|
|
30
30
|
el.value = el.value.trim();
|
|
31
31
|
setValue?.(el.value);
|
|
@@ -71,14 +71,14 @@ export interface ValidationResult {
|
|
|
71
71
|
valid: boolean;
|
|
72
72
|
message: string;
|
|
73
73
|
}
|
|
74
|
-
type ReasonTranslate = (reason: keyof ValidityStateFlags, value:
|
|
74
|
+
type ReasonTranslate = (reason: keyof ValidityStateFlags, value: unknown, fallback: string) => string;
|
|
75
75
|
/**
|
|
76
76
|
* Options for the validate action.
|
|
77
77
|
*/
|
|
78
78
|
export interface ValidateOptions {
|
|
79
79
|
enabled?: boolean;
|
|
80
|
-
context?: Record<string,
|
|
81
|
-
customValidator?: (value:
|
|
80
|
+
context?: Record<string, unknown>;
|
|
81
|
+
customValidator?: (value: unknown, context: Record<string, unknown> | undefined, el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => string | undefined;
|
|
82
82
|
on?: "input" | "change";
|
|
83
83
|
setValidationResult?: (res: ValidationResult) => void;
|
|
84
84
|
t?: false | ReasonTranslate;
|
|
@@ -148,6 +148,6 @@ export interface ValidateOptions {
|
|
|
148
148
|
*/
|
|
149
149
|
export declare function validate(el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, fn?: () => boolean | ValidateOptions): void;
|
|
150
150
|
export declare namespace validate {
|
|
151
|
-
var t: null;
|
|
151
|
+
var t: ReasonTranslate | null;
|
|
152
152
|
}
|
|
153
153
|
export {};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { tick } from "svelte";
|
|
2
|
-
import { waitForNextRepaint, waitForTwoRepaints } from "../utils/paint.js";
|
|
3
1
|
/**
|
|
4
2
|
* Creates a ValidityStateLike object for testing/mocking validation states.
|
|
5
3
|
*
|
|
@@ -133,8 +131,8 @@ const KNOWN_REASONS = [
|
|
|
133
131
|
export function validate(el, fn) {
|
|
134
132
|
$effect(() => {
|
|
135
133
|
//
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
const fnResult = fn?.() ?? {};
|
|
135
|
+
const { enabled, context, customValidator, on = "change", setValidationResult, t, } = typeof fnResult === "boolean" ? { enabled: !!fnResult } : fnResult;
|
|
138
136
|
//
|
|
139
137
|
const _t = (reason, value, fallback) => {
|
|
140
138
|
// explicit false
|
|
@@ -187,10 +185,13 @@ export function validate(el, fn) {
|
|
|
187
185
|
el.addEventListener(on, _doValidate);
|
|
188
186
|
//
|
|
189
187
|
let _touchCount = 0;
|
|
190
|
-
const onFocus = (
|
|
188
|
+
const onFocus = () => _touchCount++;
|
|
191
189
|
el.addEventListener("focus", onFocus);
|
|
192
190
|
// also validate on first blur
|
|
193
|
-
const onBlur = (
|
|
191
|
+
const onBlur = () => {
|
|
192
|
+
if (_touchCount === 1)
|
|
193
|
+
_doValidate();
|
|
194
|
+
};
|
|
194
195
|
el.addEventListener("blur", onBlur);
|
|
195
196
|
return () => {
|
|
196
197
|
el.removeEventListener(on, _doValidate);
|
|
@@ -199,6 +200,5 @@ export function validate(el, fn) {
|
|
|
199
200
|
};
|
|
200
201
|
});
|
|
201
202
|
}
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
validate.t = t;
|
|
203
|
+
// Global translation function - can be set by consumers
|
|
204
|
+
validate.t = null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Component } from "svelte";
|
|
1
2
|
import type { THC } from "../Thc/Thc.svelte";
|
|
2
3
|
/**
|
|
3
4
|
* Types of alert/confirm/prompt dialogs.
|
|
@@ -12,13 +13,13 @@ export declare enum AlertConfirmPromptType {
|
|
|
12
13
|
*/
|
|
13
14
|
export type AlertConfirmPromptVariant = "info" | "success" | "warn" | "error";
|
|
14
15
|
/** Callback type for OK button click. */
|
|
15
|
-
export type FnOnOK = (value: any) =>
|
|
16
|
+
export type FnOnOK = (value: any) => void;
|
|
16
17
|
/** Callback type for Cancel button click. */
|
|
17
|
-
export type FnOnCancel = (value: false) =>
|
|
18
|
+
export type FnOnCancel = (value: false) => void;
|
|
18
19
|
/** Callback type for Escape key press. */
|
|
19
20
|
export type FnOnEscape = () => void;
|
|
20
21
|
/** Callback type for custom button click. */
|
|
21
|
-
export type FnOnCustom = (value: any) =>
|
|
22
|
+
export type FnOnCustom = (value: any) => void;
|
|
22
23
|
/**
|
|
23
24
|
* Configuration object for an alert/confirm/prompt dialog.
|
|
24
25
|
*/
|
|
@@ -38,9 +39,9 @@ export interface AlertConfirmPromptObj extends Record<string, any> {
|
|
|
38
39
|
variant: AlertConfirmPromptVariant;
|
|
39
40
|
iconFn: (() => string) | boolean;
|
|
40
41
|
forceAsHtml?: boolean;
|
|
41
|
-
CmpButtonOk?:
|
|
42
|
-
CmpButtonCancel?:
|
|
43
|
-
CmpButtonCustom?:
|
|
42
|
+
CmpButtonOk?: Component;
|
|
43
|
+
CmpButtonCancel?: Component;
|
|
44
|
+
CmpButtonCustom?: Component;
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
47
|
* A reactive stack manager for alert, confirm, and prompt dialogs.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./alert-confirm-prompt-stack.svelte.js";
|
|
2
|
-
export { default as AlertConfirmPrompt, type Props as AlertConfirmPromptProps } from "./AlertConfirmPrompt.svelte";
|
|
2
|
+
export { default as AlertConfirmPrompt, type Props as AlertConfirmPromptProps, } from "./AlertConfirmPrompt.svelte";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./alert-confirm-prompt-stack.svelte.js";
|
|
2
|
-
export { default as AlertConfirmPrompt } from "./AlertConfirmPrompt.svelte";
|
|
2
|
+
export { default as AlertConfirmPrompt, } from "./AlertConfirmPrompt.svelte";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as AnimatedEllipsis, type Props as AnimatedEllipsisProps } from "./AnimatedEllipsis.svelte";
|
|
1
|
+
export { default as AnimatedEllipsis, type Props as AnimatedEllipsisProps, } from "./AnimatedEllipsis.svelte";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as AnimatedEllipsis } from "./AnimatedEllipsis.svelte";
|
|
1
|
+
export { default as AnimatedEllipsis, } from "./AnimatedEllipsis.svelte";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as Button, type Props as ButtonProps } from
|
|
1
|
+
export { default as Button, type Props as ButtonProps } from "./Button.svelte";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as Button } from
|
|
1
|
+
export { default as Button } from "./Button.svelte";
|
|
@@ -30,7 +30,10 @@
|
|
|
30
30
|
|
|
31
31
|
<script lang="ts">
|
|
32
32
|
import { ItemCollection } from "@marianmeres/item-collection";
|
|
33
|
-
type ItemCollectionItem = {
|
|
33
|
+
type ItemCollectionItem = {
|
|
34
|
+
id: string;
|
|
35
|
+
option: import("../Input/types.js").FieldRadiosOption;
|
|
36
|
+
};
|
|
34
37
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
35
38
|
import Button from "../Button/Button.svelte";
|
|
36
39
|
//
|