@marianmeres/stuic 2.0.0-next.5 → 2.0.3
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/actions/file-dropzone.svelte.d.ts +8 -0
- package/dist/actions/file-dropzone.svelte.js +43 -0
- package/dist/actions/highlight-dragover.svelte.js +16 -3
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/resizable-width.svelte.d.ts +21 -0
- package/dist/actions/resizable-width.svelte.js +162 -0
- package/dist/actions/validate.svelte.js +13 -13
- package/dist/components/Backdrop/Backdrop.svelte +1 -1
- package/dist/components/Button/Button.svelte +1 -1
- package/dist/components/Button/Button.svelte.d.ts +1 -1
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +47 -12
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte.d.ts +3 -2
- package/dist/components/ButtonGroupRadio/index.css +11 -2
- package/dist/components/ButtonGroupRadio/index.d.ts +1 -0
- package/dist/components/ButtonGroupRadio/index.js +1 -0
- package/dist/components/CommandMenu/CommandMenu.svelte +365 -0
- package/dist/components/CommandMenu/CommandMenu.svelte.d.ts +25 -0
- package/dist/components/CommandMenu/index.d.ts +1 -0
- package/dist/components/CommandMenu/index.js +1 -0
- package/dist/components/Input/FieldInput.svelte +1 -0
- package/dist/components/Input/FieldLikeButton.svelte +16 -7
- package/dist/components/Input/FieldLikeButton.svelte.d.ts +1 -1
- package/dist/components/Input/FieldOptions.svelte +278 -120
- package/dist/components/Input/FieldOptions.svelte.d.ts +15 -8
- package/dist/components/Input/_internal/InputWrap.svelte +7 -6
- package/dist/components/Modal/Modal.svelte +10 -5
- package/dist/components/ModalDialog/ModalDialog.svelte +25 -0
- package/dist/components/Notifications/Notifications.svelte +1 -1
- package/dist/components/Progress/_internal/Bar.svelte +1 -1
- package/dist/components/Spinner/SpinnerUnicode.svelte +130 -0
- package/dist/components/Spinner/SpinnerUnicode.svelte.d.ts +12 -0
- package/dist/components/Spinner/index.d.ts +1 -0
- package/dist/components/Spinner/index.js +1 -0
- package/dist/components/TypeaheadInput/TypeaheadInput.svelte +261 -0
- package/dist/components/TypeaheadInput/TypeaheadInput.svelte.d.ts +40 -0
- package/dist/components/TypeaheadInput/index.d.ts +1 -0
- package/dist/components/TypeaheadInput/index.js +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +1 -0
- package/dist/utils/escape-regex.d.ts +1 -0
- package/dist/utils/escape-regex.js +1 -0
- package/dist/utils/event-emitter.d.ts +18 -0
- package/dist/utils/event-emitter.js +40 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/is-plain-object.d.ts +2 -0
- package/dist/utils/is-plain-object.js +4 -0
- package/dist/utils/replace-map.d.ts +5 -0
- package/dist/utils/replace-map.js +22 -0
- package/dist/utils/seconds.d.ts +7 -0
- package/dist/utils/seconds.js +35 -0
- package/dist/utils/tw-merge.d.ts +2 -0
- package/dist/utils/tw-merge.js +4 -0
- package/dist/utils/unaccent.d.ts +6 -0
- package/dist/utils/unaccent.js +8 -0
- package/package.json +70 -66
- package/dist/components/ColResize/ColResize.svelte +0 -0
- package/dist/components/ColResize/ColResize.svelte.d.ts +0 -26
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface FileDropzoneOptions {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
inputEl: HTMLInputElement;
|
|
4
|
+
allowClick?: boolean;
|
|
5
|
+
processFiles?: (files: FileList | null) => any | Promise<any>;
|
|
6
|
+
}
|
|
7
|
+
export declare function fileDropzone(el: HTMLElement, fn?: () => FileDropzoneOptions): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function fileDropzone(el, fn) {
|
|
2
|
+
$effect(() => {
|
|
3
|
+
let { enabled = true, allowClick = true, inputEl, processFiles, } = fn?.() || {};
|
|
4
|
+
if (!enabled)
|
|
5
|
+
return;
|
|
6
|
+
if (!inputEl) {
|
|
7
|
+
console.warn("Missing inputEl instance, can't continue...");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
function preventDefault(e) {
|
|
11
|
+
e.preventDefault();
|
|
12
|
+
// e.stopPropagation();
|
|
13
|
+
}
|
|
14
|
+
function handle_drop(e) {
|
|
15
|
+
handle_files(e?.dataTransfer?.files ?? null);
|
|
16
|
+
}
|
|
17
|
+
function handle_change(e) {
|
|
18
|
+
if (e.target instanceof HTMLInputElement) {
|
|
19
|
+
handle_files(e.target.files);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function handle_files(files) {
|
|
23
|
+
processFiles?.(files);
|
|
24
|
+
}
|
|
25
|
+
function handle_click() {
|
|
26
|
+
allowClick && inputEl.click();
|
|
27
|
+
}
|
|
28
|
+
// over/drop are critical, enter/leave I'm not sure
|
|
29
|
+
const PREVENT = ["dragenter", "dragover", "dragleave", "drop"];
|
|
30
|
+
PREVENT.forEach((name) => el.addEventListener(name, preventDefault));
|
|
31
|
+
//
|
|
32
|
+
el.addEventListener("drop", handle_drop);
|
|
33
|
+
el.addEventListener("click", handle_click);
|
|
34
|
+
inputEl.addEventListener("change", handle_change);
|
|
35
|
+
//
|
|
36
|
+
return () => {
|
|
37
|
+
PREVENT.forEach((name) => el.removeEventListener(name, preventDefault));
|
|
38
|
+
el.removeEventListener("drop", handle_drop);
|
|
39
|
+
el.removeEventListener("click", handle_click);
|
|
40
|
+
inputEl.removeEventListener("change", handle_change);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -11,7 +11,7 @@ export function highlightDragover(el, fn) {
|
|
|
11
11
|
// }
|
|
12
12
|
function prevent(e) {
|
|
13
13
|
e.preventDefault();
|
|
14
|
-
e.stopPropagation();
|
|
14
|
+
// e.stopPropagation();
|
|
15
15
|
}
|
|
16
16
|
const HIGH = ["dragenter", "dragover"];
|
|
17
17
|
const UNHIGH = ["dragleave", "drop"];
|
|
@@ -22,13 +22,26 @@ export function highlightDragover(el, fn) {
|
|
|
22
22
|
return;
|
|
23
23
|
if (!Array.isArray(classes))
|
|
24
24
|
classes = [classes];
|
|
25
|
+
// allow strings
|
|
26
|
+
classes = classes.reduce((m, c) => {
|
|
27
|
+
m = [
|
|
28
|
+
...m,
|
|
29
|
+
...c
|
|
30
|
+
.split(/\s/)
|
|
31
|
+
.map((v) => v.trim())
|
|
32
|
+
.filter(Boolean),
|
|
33
|
+
];
|
|
34
|
+
return m;
|
|
35
|
+
}, []);
|
|
36
|
+
// el.addEventListener("drop", prevent);
|
|
25
37
|
const highlight = () => el.classList.add(...classes);
|
|
26
38
|
const unhighlight = () => el.classList.remove(...classes);
|
|
27
39
|
// ALL.forEach((name: any) => el.addEventListener(name, prevent, false));
|
|
28
|
-
HIGH.forEach((name) => el.addEventListener(name, highlight
|
|
29
|
-
UNHIGH.forEach((name) => el.addEventListener(name, unhighlight
|
|
40
|
+
HIGH.forEach((name) => el.addEventListener(name, highlight));
|
|
41
|
+
UNHIGH.forEach((name) => el.addEventListener(name, unhighlight));
|
|
30
42
|
// el.addEventListener("drop", handle_drop, false);
|
|
31
43
|
return () => {
|
|
44
|
+
// el.removeEventListener("drop", prevent);
|
|
32
45
|
// ALL.forEach((name: any) => el.removeEventListener(name, prevent));
|
|
33
46
|
HIGH.forEach((name) => el.removeEventListener(name, highlight));
|
|
34
47
|
UNHIGH.forEach((name) => el.removeEventListener(name, unhighlight));
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./autogrow.svelte.js";
|
|
2
2
|
export * from "./autoscroll.js";
|
|
3
|
+
export * from "./file-dropzone.svelte.js";
|
|
3
4
|
export * from "./focus-trap.js";
|
|
4
5
|
export * from "./highlight-dragover.svelte.js";
|
|
5
6
|
export * from "./on-submit-validity-check.svelte.js";
|
|
7
|
+
export * from "./resizable-width.svelte.js";
|
|
6
8
|
export * from "./tooltip/tooltip.svelte.js";
|
|
7
9
|
export * from "./trim.svelte.js";
|
|
8
10
|
export * from "./validate.svelte.js";
|
package/dist/actions/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./autogrow.svelte.js";
|
|
2
2
|
export * from "./autoscroll.js";
|
|
3
|
+
export * from "./file-dropzone.svelte.js";
|
|
3
4
|
export * from "./focus-trap.js";
|
|
4
5
|
export * from "./highlight-dragover.svelte.js";
|
|
5
6
|
export * from "./on-submit-validity-check.svelte.js";
|
|
7
|
+
export * from "./resizable-width.svelte.js";
|
|
6
8
|
export * from "./tooltip/tooltip.svelte.js";
|
|
7
9
|
export * from "./trim.svelte.js";
|
|
8
10
|
export * from "./validate.svelte.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ResizableWidthOptions {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
initial?: number;
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
units?: "px" | "%";
|
|
7
|
+
key?: string | number | null | undefined;
|
|
8
|
+
storage?: "local" | "session";
|
|
9
|
+
handleClass?: string;
|
|
10
|
+
handleDragClass?: string;
|
|
11
|
+
onResize?: (info: {
|
|
12
|
+
width: number;
|
|
13
|
+
units: "px" | "%";
|
|
14
|
+
container: number;
|
|
15
|
+
}) => void;
|
|
16
|
+
debug?: (...args: any[]) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Note: units should not be changed on the fly...
|
|
20
|
+
*/
|
|
21
|
+
export declare function resizableWidth(el: HTMLDivElement, fn?: () => ResizableWidthOptions): void;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { localStorageState, sessionStorageState, } from "../utils/persistent-state.svelte.js";
|
|
2
|
+
import { twMerge } from "../utils/tw-merge.js";
|
|
3
|
+
/**
|
|
4
|
+
* Note: units should not be changed on the fly...
|
|
5
|
+
*/
|
|
6
|
+
export function resizableWidth(el, fn) {
|
|
7
|
+
const DEFAULT_HANDLE_CLS = [
|
|
8
|
+
"group",
|
|
9
|
+
"absolute top-0 right-0 bottom-0",
|
|
10
|
+
"w-[1px]",
|
|
11
|
+
"bg-black/20 hover:bg-black/30",
|
|
12
|
+
"dark:bg-white/10 dark:hover:bg-white/20",
|
|
13
|
+
"transition-colors duration-200",
|
|
14
|
+
"touch-none cursor-ew-resize",
|
|
15
|
+
].join(" ");
|
|
16
|
+
const DEFAULT_DRAG_HANDLE_CLS = [
|
|
17
|
+
"absolute h-[20px] w-[9px]",
|
|
18
|
+
"-translate-x-[4px] top-1/2 -translate-y-1/2",
|
|
19
|
+
"rounded border border-black/20 dark:border-white/20",
|
|
20
|
+
"bg-gray-300 group-hover:bg-gray-400",
|
|
21
|
+
"dark:bg-gray-600 dark:group-hover:bg-gray-500",
|
|
22
|
+
"transition-colors duration-200",
|
|
23
|
+
"touch-none cursor-ew-resize",
|
|
24
|
+
].join(" ");
|
|
25
|
+
function create_handle(el, handleClass, handleDragClass) {
|
|
26
|
+
const handle = document.createElement("div");
|
|
27
|
+
handle.setAttribute("data-handle", "true");
|
|
28
|
+
const dragHandle = document.createElement("div");
|
|
29
|
+
dragHandle.classList.add(...twMerge(DEFAULT_DRAG_HANDLE_CLS, handleDragClass).split(" "));
|
|
30
|
+
handle.appendChild(dragHandle);
|
|
31
|
+
el.appendChild(handle);
|
|
32
|
+
//
|
|
33
|
+
handle.classList.add(...twMerge(DEFAULT_HANDLE_CLS, handleClass).split(" "));
|
|
34
|
+
return handle;
|
|
35
|
+
}
|
|
36
|
+
$effect(() => {
|
|
37
|
+
let { enabled = true, initial = 0, min = 0, max = 0, units = "px", key, storage = "session", handleClass = "", handleDragClass = "", onResize, debug, } = fn?.() || {};
|
|
38
|
+
const _debug = (...args) => debug?.("[resizable-width]", ...args);
|
|
39
|
+
_debug("$effect");
|
|
40
|
+
if (!enabled)
|
|
41
|
+
return;
|
|
42
|
+
// initialize ////////////////////////////////////////////////////////////////////
|
|
43
|
+
//
|
|
44
|
+
let isResizing = false;
|
|
45
|
+
let startX = 0;
|
|
46
|
+
let startWidth = 0;
|
|
47
|
+
let containerW = undefined;
|
|
48
|
+
//
|
|
49
|
+
const handle = create_handle(el, handleClass, handleDragClass);
|
|
50
|
+
const container = el.parentElement;
|
|
51
|
+
// do we have a storage? if so, adjust the initial value...
|
|
52
|
+
const initialBackup = initial;
|
|
53
|
+
const _storage = get_storage(storage, key, initial);
|
|
54
|
+
if (_storage)
|
|
55
|
+
initial = _storage.current ?? initial;
|
|
56
|
+
// handlers/workers/helpers //////////////////////////////////////////////////////
|
|
57
|
+
function set_width(pxOrPercent) {
|
|
58
|
+
if (pxOrPercent) {
|
|
59
|
+
_debug(`set_width(${pxOrPercent})`);
|
|
60
|
+
set_width_px(units === "%" ? container.offsetWidth * (pxOrPercent / 100) : pxOrPercent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function set_width_px(widthPx) {
|
|
64
|
+
_debug(`set_width_px(${widthPx})`);
|
|
65
|
+
containerW ??= container.offsetWidth;
|
|
66
|
+
const clamp = (value) => {
|
|
67
|
+
const _initial = value;
|
|
68
|
+
if (min)
|
|
69
|
+
value = Math.max(min, value);
|
|
70
|
+
if (max)
|
|
71
|
+
value = Math.min(max, value);
|
|
72
|
+
_initial !== value && _debug("clamped", value, units);
|
|
73
|
+
return value;
|
|
74
|
+
};
|
|
75
|
+
let width;
|
|
76
|
+
if (units === "%") {
|
|
77
|
+
const widthPercent = Math.min(100, (widthPx / containerW) * 100); // convert to % (with 100 max)
|
|
78
|
+
width = clamp(widthPercent);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
width = clamp(widthPx);
|
|
82
|
+
}
|
|
83
|
+
el.style.width = `${width}${units}`;
|
|
84
|
+
_debug("new width", width, units);
|
|
85
|
+
const info = { width, units, container: containerW };
|
|
86
|
+
onResize?.(info);
|
|
87
|
+
// maybe save to storage
|
|
88
|
+
if (_storage)
|
|
89
|
+
_storage.current = width;
|
|
90
|
+
return info;
|
|
91
|
+
}
|
|
92
|
+
function resize_start(e) {
|
|
93
|
+
e.preventDefault(); // prevent scrolling on touch devices
|
|
94
|
+
isResizing = true;
|
|
95
|
+
//
|
|
96
|
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
97
|
+
startX = clientX;
|
|
98
|
+
startWidth = parseInt(getComputedStyle(el).width, 10);
|
|
99
|
+
containerW = container.offsetWidth;
|
|
100
|
+
//
|
|
101
|
+
document.body.style.cursor = "ew-resize";
|
|
102
|
+
document.body.style.userSelect = "none";
|
|
103
|
+
}
|
|
104
|
+
function resize(e) {
|
|
105
|
+
if (!isResizing)
|
|
106
|
+
return;
|
|
107
|
+
e.preventDefault(); // prevent scrolling on touch devices
|
|
108
|
+
//
|
|
109
|
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
110
|
+
const deltaX = clientX - startX;
|
|
111
|
+
let width = startWidth + deltaX;
|
|
112
|
+
set_width_px(width);
|
|
113
|
+
}
|
|
114
|
+
function resize_stop() {
|
|
115
|
+
if (isResizing) {
|
|
116
|
+
isResizing = false;
|
|
117
|
+
containerW = undefined;
|
|
118
|
+
//
|
|
119
|
+
document.body.style.cursor = "";
|
|
120
|
+
document.body.style.userSelect = "";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function on_dblclick() {
|
|
124
|
+
set_width(initialBackup);
|
|
125
|
+
}
|
|
126
|
+
// initial styles ////////////////////////////////////////////////////////////////
|
|
127
|
+
el.style.position = "relative"; // so the handle will work
|
|
128
|
+
set_width(initial);
|
|
129
|
+
// listeners /////////////////////////////////////////////////////////////////////
|
|
130
|
+
// handle
|
|
131
|
+
handle.addEventListener("dblclick", on_dblclick);
|
|
132
|
+
handle.addEventListener("selectstart", (e) => e.preventDefault()); // prevent text selection during resize
|
|
133
|
+
// mouse
|
|
134
|
+
handle.addEventListener("mousedown", resize_start);
|
|
135
|
+
document.addEventListener("mousemove", resize);
|
|
136
|
+
document.addEventListener("mouseup", resize_stop);
|
|
137
|
+
// touch
|
|
138
|
+
handle.addEventListener("touchstart", resize_start, { passive: false });
|
|
139
|
+
document.addEventListener("touchmove", resize, { passive: false });
|
|
140
|
+
document.addEventListener("touchend", resize_stop);
|
|
141
|
+
document.addEventListener("touchcancel", resize_stop);
|
|
142
|
+
// cleanup ///////////////////////////////////////////////////////////////////////
|
|
143
|
+
return () => {
|
|
144
|
+
// mouse
|
|
145
|
+
document.removeEventListener("mousemove", resize);
|
|
146
|
+
document.removeEventListener("mouseup", resize_stop);
|
|
147
|
+
// touch
|
|
148
|
+
document.removeEventListener("touchmove", resize);
|
|
149
|
+
document.removeEventListener("touchend", resize_stop);
|
|
150
|
+
document.removeEventListener("touchcancel", resize_stop);
|
|
151
|
+
// will also remove it's own event listeners
|
|
152
|
+
handle.remove();
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// helpers ///////////////////////////////////////////////////////////////////////////////
|
|
157
|
+
function get_storage(type, key, initialValue) {
|
|
158
|
+
if (key) {
|
|
159
|
+
return (type === "session" ? sessionStorageState : localStorageState)(`resizable-width-${key}`, initialValue);
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
@@ -55,20 +55,20 @@ export function validate(el, fn) {
|
|
|
55
55
|
// console.log(1111, validityState, el);
|
|
56
56
|
// hm... Uncaught Svelte error: state_unsafe_mutation...
|
|
57
57
|
// the `tick` await helps, but I'm not really sure I understand the internals...
|
|
58
|
-
tick().then(() => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
58
|
+
// tick().then(() => {
|
|
59
|
+
setValidationResult?.({
|
|
60
|
+
validity: validityState,
|
|
61
|
+
reasons,
|
|
62
|
+
valid: validityState?.valid,
|
|
63
|
+
// use translate fn for first reason (if fn provided and allowed),
|
|
64
|
+
// otherwise fallback to native msg
|
|
65
|
+
message: _t(reasons?.[0], el.value, el.validationMessage ||
|
|
66
|
+
// PROBLEM: hidden inputs do not report validationMessage-s even
|
|
67
|
+
// if correctly reported as invalid. So all we can do, is
|
|
68
|
+
// put only something generic here...
|
|
69
|
+
"This field is invalid. Please review and try again."),
|
|
71
70
|
});
|
|
71
|
+
// });
|
|
72
72
|
};
|
|
73
73
|
el.addEventListener(on, _doValidate);
|
|
74
74
|
//
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
bind:this={el}
|
|
140
140
|
role="presentation"
|
|
141
141
|
tabindex="-1"
|
|
142
|
-
class={twMerge("fixed inset-0 flex z-10", classProp)}
|
|
142
|
+
class={twMerge("fixed inset-0 flex z-10 h-dvh", classProp)}
|
|
143
143
|
in:fade={{ duration: fadeInDuration }}
|
|
144
144
|
out:fade={{ duration: fadeOutDuration }}
|
|
145
145
|
use:focusTrapAction={{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const BUTTON_STUIC_BASE_CLASSES = "\n\t\tbg-button-bg text-button-text \n\t\tdark:bg-button-bg-dark dark:text-button-text-dark\n\t\tfont-mono text-sm text-center \n\t\tleading-none\n\t\tborder-1\n\t\tborder-button-border dark:border-button-border-dark\n\t\trounded-md\n\t\tinline-flex items-center justify-center gap-x-2\n\t\tpx-3 py-
|
|
1
|
+
export declare const BUTTON_STUIC_BASE_CLASSES = "\n\t\tbg-button-bg text-button-text \n\t\tdark:bg-button-bg-dark dark:text-button-text-dark\n\t\tfont-mono text-sm text-center \n\t\tleading-none\n\t\tborder-1\n\t\tborder-button-border dark:border-button-border-dark\n\t\trounded-md\n\t\tinline-flex items-center justify-center gap-x-2\n\t\tpx-3 py-2\n\n\t\thover:brightness-[1.05]\n\t\tactive:brightness-[0.95]\n\t\tdisabled:hover:brightness-100\n\n\t\tfocus:brightness-[1.05] \n\t\tfocus:border-button-border-focus focus:dark:border-button-border-focus-dark\n\n\t\t focus:outline-4 focus:outline-black/10 focus:dark:outline-white/20\n\t\tfocus-visible:outline-4 focus-visible:outline-black/10 focus-visible:dark:outline-white/20\n\t";
|
|
2
2
|
export declare const BUTTON_STUIC_PRESET_CLASSES: any;
|
|
3
3
|
import type { Snippet } from "svelte";
|
|
4
4
|
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ItemCollection
|
|
3
|
-
import { getId } from "../../utils/get-id.js";
|
|
2
|
+
import { ItemCollection } from "@marianmeres/item-collection";
|
|
4
3
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
5
|
-
import type { FieldRadiosOption } from "../Input/types.js";
|
|
6
4
|
import Button from "../Button/Button.svelte";
|
|
5
|
+
import type { FieldRadiosOption } from "../Input/types.js";
|
|
7
6
|
//
|
|
8
7
|
import "./index.css";
|
|
9
8
|
|
|
10
|
-
interface
|
|
9
|
+
interface ItemCollectionType {
|
|
10
|
+
id: string;
|
|
11
|
+
option: FieldRadiosOption;
|
|
12
|
+
}
|
|
13
|
+
interface ItemColl extends ItemCollection<ItemCollectionType> {}
|
|
11
14
|
|
|
12
15
|
interface Props {
|
|
13
16
|
value?: string;
|
|
@@ -58,7 +61,12 @@
|
|
|
58
61
|
{}
|
|
59
62
|
);
|
|
60
63
|
|
|
61
|
-
if (
|
|
64
|
+
if (value !== undefined) {
|
|
65
|
+
const index = out.items.findIndex((item: ItemCollectionType) => {
|
|
66
|
+
return value === (item?.option.value ?? item.option.label);
|
|
67
|
+
});
|
|
68
|
+
if (index > -1) out.setActiveIndex(index);
|
|
69
|
+
} else if (activeIndex !== undefined) out.setActiveIndex(activeIndex);
|
|
62
70
|
|
|
63
71
|
return out;
|
|
64
72
|
});
|
|
@@ -70,20 +78,44 @@
|
|
|
70
78
|
});
|
|
71
79
|
});
|
|
72
80
|
|
|
81
|
+
const rounded = "rounded-md";
|
|
82
|
+
const roundedBtn = "rounded-md";
|
|
73
83
|
//
|
|
74
84
|
const CLS = `
|
|
75
|
-
|
|
85
|
+
stuic-button-group
|
|
86
|
+
${rounded}
|
|
87
|
+
w-full
|
|
88
|
+
py-1.5 px-1.5 inline-block space-x-1
|
|
76
89
|
bg-button-group-bg text-button-group-text
|
|
77
90
|
dark:bg-button-group-bg-dark dark:text-button-group-text-dark
|
|
78
91
|
border-1
|
|
79
92
|
border-button-group-border dark:border-button-group-border-dark
|
|
93
|
+
flex justify-between
|
|
94
|
+
|
|
95
|
+
focus-within:border-button-group-accent focus-within:dark:border-button-group-accent-dark
|
|
96
|
+
focus-within:ring-button-group-accent/20 focus-within:dark:ring-button-group-accent-dark/20
|
|
97
|
+
focus-within:ring-4
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
const CLS_BUTTON = `
|
|
101
|
+
${rounded}
|
|
102
|
+
w-full inline-block
|
|
103
|
+
bg-transparent text-button-group-text dark:text-button-group-text-dark
|
|
104
|
+
hover:bg-transparent hover:text-button-group-text hover:dark:text-button-group-text-dark
|
|
105
|
+
outline-none focus:outline-none
|
|
80
106
|
`;
|
|
81
107
|
|
|
82
108
|
// we need some active indication by default... use just something subtle here, in the wild
|
|
83
109
|
// this will be styled with classButtonActive
|
|
84
|
-
const
|
|
85
|
-
|
|
110
|
+
const CLS_BUTTON_ACTIVE = `
|
|
111
|
+
shadow-none
|
|
112
|
+
bg-button-group-bg-active dark:bg-button-group-bg-active-dark
|
|
113
|
+
text-button-group-text-active dark:text-button-group-text-active-dark
|
|
114
|
+
hover:bg-button-group-bg-active hover:dark:bg-button-group-bg-active
|
|
115
|
+
hover:text-button-group-text-active hover:dark:text-button-group-text-active-dark
|
|
116
|
+
${roundedBtn}
|
|
86
117
|
`;
|
|
118
|
+
// shadow-[0px_0px_1px_1px_rgba(0_0_0_/_.6)]
|
|
87
119
|
|
|
88
120
|
let els = $state<Record<number, HTMLButtonElement>>({});
|
|
89
121
|
|
|
@@ -104,13 +136,16 @@
|
|
|
104
136
|
>
|
|
105
137
|
{#each coll.items as item, i}
|
|
106
138
|
<Button
|
|
107
|
-
tabindex={
|
|
139
|
+
tabindex={$coll.activeIndex === i ? tabindex : -1}
|
|
108
140
|
class={twMerge(
|
|
109
141
|
"border-none shadow-none",
|
|
142
|
+
CLS_BUTTON,
|
|
110
143
|
classButton,
|
|
111
|
-
$coll.activeIndex === i && [
|
|
144
|
+
$coll.activeIndex === i && [CLS_BUTTON_ACTIVE, classButtonActive].join(" ")
|
|
112
145
|
)}
|
|
146
|
+
{disabled}
|
|
113
147
|
{size}
|
|
148
|
+
type="button"
|
|
114
149
|
role="radio"
|
|
115
150
|
aria-checked={$coll.activeIndex === i}
|
|
116
151
|
onclick={async () => {
|
|
@@ -118,10 +153,10 @@
|
|
|
118
153
|
}}
|
|
119
154
|
bind:el={els[i]}
|
|
120
155
|
onkeydown={async (e) => {
|
|
121
|
-
if (e.key
|
|
156
|
+
if (["ArrowRight", "ArrowDown"].includes(e.key)) {
|
|
122
157
|
await maybe_activate(Math.min(i + 1, coll.size - 1), coll);
|
|
123
158
|
}
|
|
124
|
-
if (e.key
|
|
159
|
+
if (["ArrowLeft", "ArrowUp"].includes(e.key)) {
|
|
125
160
|
await maybe_activate(Math.max(0, i - 1), coll);
|
|
126
161
|
}
|
|
127
162
|
}}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ItemCollection } from "@marianmeres/item-collection";
|
|
2
2
|
import type { FieldRadiosOption } from "../Input/types.js";
|
|
3
3
|
import "./index.css";
|
|
4
|
-
interface
|
|
4
|
+
interface ItemCollectionType {
|
|
5
5
|
id: string;
|
|
6
6
|
option: FieldRadiosOption;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
interface ItemColl extends ItemCollection<ItemCollectionType> {
|
|
8
9
|
}
|
|
9
10
|
interface Props {
|
|
10
11
|
value?: string;
|
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
/* prettier-ignore */
|
|
5
5
|
@theme inline {
|
|
6
|
-
--color-button-group-bg: var(--color-button-group-bg, var(--color-
|
|
6
|
+
--color-button-group-bg: var(--color-button-group-bg, var(--color-white));
|
|
7
7
|
--color-button-group-bg-dark: var(--color-button-group-bg-dark, var(--color-neutral-600));
|
|
8
8
|
|
|
9
9
|
--color-button-group-text: var(--color-button-group-text, var(--color-black));
|
|
10
|
-
--color-button-group-text-dark: var(--color-button-group-text-dark, var(--color-
|
|
10
|
+
--color-button-group-text-dark: var(--color-button-group-text-dark, var(--color-neutral-300));
|
|
11
11
|
|
|
12
12
|
--color-button-group-border: var(--color-button-group-border, var(--color-neutral-300));
|
|
13
13
|
--color-button-group-border-dark: var(--color-button-group-border-dark, var(--color-neutral-800));
|
|
14
|
+
|
|
15
|
+
--color-button-group-accent: var(--color-button-group-accent, var(--color-red-600));
|
|
16
|
+
--color-button-group-accent-dark: var(--color-button-group-accent-dark, var(--color-red-400));
|
|
17
|
+
|
|
18
|
+
--color-button-group-bg-active: var(--color-button-group-bg-active, var(--color-neutral-500));
|
|
19
|
+
--color-button-group-bg-active-dark: var(--color-button-group-bg-active-dark, var(--color-neutral-500));
|
|
20
|
+
|
|
21
|
+
--color-button-group-text-active: var(--color-button-group-text-active, var(--color-white));
|
|
22
|
+
--color-button-group-text-active-dark: var(--color-button-group-text-active-dark, var(--color-white));
|
|
14
23
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ButtonGroupRadio } from "./ButtonGroupRadio.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ButtonGroupRadio } from "./ButtonGroupRadio.svelte";
|