@marianmeres/stuic 2.0.0-next.5 → 2.0.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 (61) hide show
  1. package/dist/actions/file-dropzone.svelte.d.ts +8 -0
  2. package/dist/actions/file-dropzone.svelte.js +43 -0
  3. package/dist/actions/highlight-dragover.svelte.js +16 -3
  4. package/dist/actions/index.d.ts +2 -0
  5. package/dist/actions/index.js +2 -0
  6. package/dist/actions/resizable-width.svelte.d.ts +21 -0
  7. package/dist/actions/resizable-width.svelte.js +162 -0
  8. package/dist/actions/validate.svelte.js +13 -13
  9. package/dist/components/Backdrop/Backdrop.svelte +1 -1
  10. package/dist/components/Button/Button.svelte +1 -1
  11. package/dist/components/Button/Button.svelte.d.ts +1 -1
  12. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +47 -12
  13. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte.d.ts +3 -2
  14. package/dist/components/ButtonGroupRadio/index.css +11 -2
  15. package/dist/components/ButtonGroupRadio/index.d.ts +1 -0
  16. package/dist/components/ButtonGroupRadio/index.js +1 -0
  17. package/dist/components/CommandMenu/CommandMenu.svelte +365 -0
  18. package/dist/components/CommandMenu/CommandMenu.svelte.d.ts +25 -0
  19. package/dist/components/CommandMenu/index.d.ts +1 -0
  20. package/dist/components/CommandMenu/index.js +1 -0
  21. package/dist/components/Input/FieldInput.svelte +1 -0
  22. package/dist/components/Input/FieldLikeButton.svelte +16 -7
  23. package/dist/components/Input/FieldLikeButton.svelte.d.ts +1 -1
  24. package/dist/components/Input/FieldOptions.svelte +278 -120
  25. package/dist/components/Input/FieldOptions.svelte.d.ts +15 -8
  26. package/dist/components/Input/_internal/InputWrap.svelte +7 -6
  27. package/dist/components/Modal/Modal.svelte +10 -5
  28. package/dist/components/ModalDialog/ModalDialog.svelte +25 -0
  29. package/dist/components/Notifications/Notifications.svelte +1 -1
  30. package/dist/components/Progress/_internal/Bar.svelte +1 -1
  31. package/dist/components/Spinner/SpinnerUnicode.svelte +130 -0
  32. package/dist/components/Spinner/SpinnerUnicode.svelte.d.ts +12 -0
  33. package/dist/components/Spinner/index.d.ts +1 -0
  34. package/dist/components/Spinner/index.js +1 -0
  35. package/dist/components/TypeaheadInput/TypeaheadInput.svelte +261 -0
  36. package/dist/components/TypeaheadInput/TypeaheadInput.svelte.d.ts +40 -0
  37. package/dist/components/TypeaheadInput/index.d.ts +1 -0
  38. package/dist/components/TypeaheadInput/index.js +1 -0
  39. package/dist/index.css +1 -0
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.js +3 -0
  42. package/dist/types.d.ts +1 -0
  43. package/dist/utils/escape-regex.d.ts +1 -0
  44. package/dist/utils/escape-regex.js +1 -0
  45. package/dist/utils/event-emitter.d.ts +18 -0
  46. package/dist/utils/event-emitter.js +40 -0
  47. package/dist/utils/index.d.ts +5 -0
  48. package/dist/utils/index.js +5 -0
  49. package/dist/utils/is-plain-object.d.ts +2 -0
  50. package/dist/utils/is-plain-object.js +4 -0
  51. package/dist/utils/replace-map.d.ts +5 -0
  52. package/dist/utils/replace-map.js +22 -0
  53. package/dist/utils/seconds.d.ts +7 -0
  54. package/dist/utils/seconds.js +35 -0
  55. package/dist/utils/tw-merge.d.ts +2 -0
  56. package/dist/utils/tw-merge.js +4 -0
  57. package/dist/utils/unaccent.d.ts +6 -0
  58. package/dist/utils/unaccent.js +8 -0
  59. package/package.json +70 -66
  60. package/dist/components/ColResize/ColResize.svelte +0 -0
  61. 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, false));
29
- UNHIGH.forEach((name) => el.addEventListener(name, unhighlight, false));
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));
@@ -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";
@@ -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
- 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 anything generically reasonable here...
69
- "This field contains invalid data. Please review and try again."),
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={{
@@ -8,7 +8,7 @@
8
8
  border-button-border dark:border-button-border-dark
9
9
  rounded-md
10
10
  inline-flex items-center justify-center gap-x-2
11
- px-3 py-1.5
11
+ px-3 py-2
12
12
 
13
13
  hover:brightness-[1.05]
14
14
  active:brightness-[0.95]
@@ -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.5\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";
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, type Item } from "@marianmeres/item-collection";
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 ItemColl extends ItemCollection<{ id: string; option: FieldRadiosOption }> {}
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 (activeIndex !== undefined) out.setActiveIndex(activeIndex);
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
- p-1.5 rounded-lg inline-block space-x-2
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 CLS_ACTIVE = `
85
- shadow-[0px_0px_1px_1px_rgba(0_0_0_/_.6)]
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={i === 0 ? tabindex : undefined}
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 && [CLS_ACTIVE, classButtonActive].join(" ")
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 === "ArrowRight") {
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 === "ArrowLeft") {
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 ItemColl extends ItemCollection<{
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-neutral-100));
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-white));
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";