@marianmeres/stuic 2.0.0-next.4 → 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 +2 -2
  11. package/dist/components/Button/Button.svelte.d.ts +1 -1
  12. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +170 -0
  13. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte.d.ts +26 -0
  14. package/dist/components/ButtonGroupRadio/index.css +23 -0
  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 +308 -136
  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
@@ -112,9 +112,10 @@
112
112
  class={twMerge(
113
113
  "stuic-input",
114
114
  _classCommon,
115
- "mb-8 grid",
116
- hasLabel && labelLeft && labelLeftWidth === "normal" && "width-normal grid-cols-4",
117
- hasLabel && labelLeft && labelLeftWidth === "wide" && "width-wide grid-cols-3",
115
+ "mb-8",
116
+ hasLabel && labelLeft && "flex",
117
+ hasLabel && labelLeft && labelLeftWidth === "normal" && "width-normal",
118
+ hasLabel && labelLeft && labelLeftWidth === "wide" && "width-wide",
118
119
  classProp
119
120
  )}
120
121
  bind:clientWidth={width}
@@ -124,7 +125,7 @@
124
125
  class={twMerge(
125
126
  "label-box",
126
127
  _classCommon,
127
- "flex",
128
+ "flex flex-1",
128
129
  labelLeft ? "left items-start mt-2" : "items-end",
129
130
  classLabelBox
130
131
  )}
@@ -152,8 +153,8 @@
152
153
  class={twMerge(
153
154
  "input-box",
154
155
  _classCommon,
155
- hasLabel && labelLeft && labelLeftWidth === "normal" && "col-span-3",
156
- hasLabel && labelLeft && labelLeftWidth === "wide" && "col-span-2",
156
+ hasLabel && labelLeft && labelLeftWidth === "normal" && "flex-3",
157
+ hasLabel && labelLeft && labelLeftWidth === "wide" && "flex-2",
157
158
  classInputBox
158
159
  )}
159
160
  >
@@ -76,7 +76,9 @@
76
76
  bind:el={elBackdrop}
77
77
  bind:visible
78
78
  class={twMerge(
79
- "justify-center items-center bg-black/25 p-2 sm:p-4 md:p-[10vh] transition-all",
79
+ // "justify-center items-center bg-black/25 p-2 sm:p-4 md:p-[10vh] transition-all",
80
+ "justify-center items-center bg-black/25 transition-all",
81
+ "md:p-[10vh]",
80
82
  classBackdrop
81
83
  )}
82
84
  {focusTrap}
@@ -91,15 +93,18 @@
91
93
  aria-labelledby={labelledby}
92
94
  aria-describedby={describedby}
93
95
  class={twMerge(
94
- "overflow-x-hidden overflow-y-hidden w-full h-full max-w-3xl",
95
- // "overflow-y-auto w-full h-full max-w-3xl max-h-[66vh] border",
96
+ "overflow-x-hidden overflow-y-hidden flex flex-col",
97
+ "w-full max-w-3xl",
98
+ "h-dvh md:h-full",
96
99
  classInner
97
100
  )}
98
101
  >
99
102
  <div
100
103
  class={twMerge(
101
- "bg-white dark:bg-neutral-800 rounded-md flex flex-col overflow-hidden",
102
- "w-full max-h-full md:max-h-2/3",
104
+ "bg-white dark:bg-neutral-800",
105
+ "flex flex-col overflow-hidden",
106
+ "rounded-none md:rounded-md",
107
+ "w-full flex-1 md:max-h-2/3",
103
108
  classProp
104
109
  )}
105
110
  >
@@ -76,6 +76,31 @@
76
76
  () => !noClickOutsideClose && close()
77
77
  );
78
78
 
79
+ let _original: any = {};
80
+ $effect(() => {
81
+ // if (noScrollLock) return;
82
+ if (visible) {
83
+ _original = window.getComputedStyle(document.body);
84
+ const scrollY = window.scrollY;
85
+
86
+ document.body.style.position = "fixed";
87
+ document.body.style.top = `-${scrollY}px`;
88
+ document.body.style.width = "100%";
89
+ document.body.style.overflow = "hidden";
90
+ } else {
91
+ const scrollY = document.body.style.top;
92
+
93
+ document.body.style.position = _original.position;
94
+ document.body.style.position = "";
95
+ document.body.style.top = "";
96
+ document.body.style.width = "";
97
+ document.body.style.overflow = "";
98
+
99
+ // Restore scroll position
100
+ window.scrollTo(0, parseInt(scrollY || "0") * -1);
101
+ }
102
+ });
103
+
79
104
  // $inspect("Modal dialog mounted, is visible:", visible).with(clog);
80
105
  </script>
81
106
 
@@ -28,7 +28,7 @@
28
28
  posX: "right",
29
29
  posXMobile: "center",
30
30
  posY: "top",
31
- posYMobile: "bottom",
31
+ posYMobile: "top",
32
32
  };
33
33
 
34
34
  // prettier-ignore
@@ -23,7 +23,7 @@
23
23
  <div
24
24
  class={twMerge(
25
25
  "bar",
26
- "w-full h-full bg-progress-accent transition-[width] ease-linear duration-1000",
26
+ "w-full h-full bg-progress-accent transition-[width] ease-linear duration-100",
27
27
  classBar
28
28
  )}
29
29
  style="width:{Math.min(100, Math.max(0, progress))}%; {styleBar}"
@@ -0,0 +1,130 @@
1
+ <script module lang="ts">
2
+ export type SpinnerUnicodeVariant =
3
+ | "braille_bar"
4
+ | "braille_bar_dot"
5
+ | "braille_dot_circle"
6
+ | "braille_dot_bounce"
7
+ | "half_circle"
8
+ | "quarter_circle"
9
+ | "ascii"
10
+ | "bar_v"
11
+ | "bar_h"
12
+ | "shade"
13
+ | "arrows"
14
+ | "arrows2";
15
+
16
+ //
17
+ export function spinnerCreateBackAndForthCharFrames(
18
+ width: number,
19
+ hiChar: string,
20
+ loChar: string,
21
+ glue: string = "\u200A"
22
+ ): string[] {
23
+ if (width < 0) throw new TypeError("Expecting positive non-zero width");
24
+ if (width === 1) return [hiChar];
25
+
26
+ let frames: string[] = [];
27
+ let hiIdx = 0;
28
+ let inc = true;
29
+
30
+ const framesCount = 2 * width - 2;
31
+
32
+ for (let y = 0; y < framesCount; y++) {
33
+ let rowChars = [];
34
+ for (let x = 0; x < width; x++) {
35
+ rowChars.push(x == hiIdx ? hiChar : loChar);
36
+ }
37
+
38
+ if (inc) {
39
+ hiIdx++;
40
+ if (hiIdx === Math.ceil(framesCount / 2)) inc = false;
41
+ } else {
42
+ hiIdx--;
43
+ if (hiIdx === 0) inc = true;
44
+ }
45
+
46
+ frames.push(rowChars.join(glue));
47
+ }
48
+
49
+ return frames;
50
+ }
51
+ </script>
52
+
53
+ <script lang="ts">
54
+ import { onMount } from "svelte";
55
+ import { twMerge } from "../../utils/tw-merge.js";
56
+
57
+ let {
58
+ class: _class = "",
59
+ speed = 70,
60
+ variant = "braille_bar_dot",
61
+ reversed = false,
62
+ frames,
63
+ }: {
64
+ class?: string;
65
+ speed?: number;
66
+ variant?: SpinnerUnicodeVariant;
67
+ reversed?: boolean;
68
+ frames?: string[];
69
+ } = $props();
70
+
71
+ const braille_bar_dot = ["⣾", "⣷", "⣯", "⣟", "⡿", "⢿", "⣻", "⣽"];
72
+ const braille_bar = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
73
+ const braille_dot_circle = ["⠁", "⠈", "⠐", "⠠", "⢀", "⡀", "⠄", "⠂"];
74
+ const braille_dot_bounce = ["⠈", "⠐", "⠠", "⢀", "⠠", "⠐"];
75
+ const half_circle = ["◐", "◓", "◑", "◒"];
76
+ const quarter_circle = ["◴", "◷", "◶", "◵"];
77
+ const ascii = ["|", "/", "-", "\\"];
78
+ const bar_v = ["▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃"];
79
+ const bar_h = ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▉", "▋", "▌", "▍", "▎"];
80
+ const shade = ["░", "▒", "▓", "█", "▓", "▒"];
81
+ const arrows = ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"];
82
+ const arrows2 = ["⇠", "⇡", "⇢", "⇣"];
83
+
84
+ let _frames = $derived.by(() => {
85
+ if (frames) return frames;
86
+ const map: Record<SpinnerUnicodeVariant, string[]> = {
87
+ braille_bar,
88
+ braille_bar_dot,
89
+ braille_dot_circle,
90
+ braille_dot_bounce,
91
+ half_circle,
92
+ quarter_circle,
93
+ ascii,
94
+ bar_v,
95
+ bar_h,
96
+ shade,
97
+ arrows,
98
+ arrows2,
99
+ };
100
+
101
+ const _frames = map[variant] ?? braille_bar;
102
+ return reversed ? _frames.toReversed() : _frames;
103
+ });
104
+
105
+ let currentFrame = $state(0);
106
+ let timer: any;
107
+ let lastTime = 0;
108
+
109
+ onMount(() => {
110
+ const animate = (currentTime: number) => {
111
+ if (currentTime - lastTime >= speed) {
112
+ currentFrame = (currentFrame + 1) % _frames.length;
113
+ lastTime = currentTime;
114
+ }
115
+ timer = requestAnimationFrame(animate);
116
+ };
117
+
118
+ timer = requestAnimationFrame(animate);
119
+
120
+ return () => {
121
+ if (timer) {
122
+ cancelAnimationFrame(timer);
123
+ }
124
+ };
125
+ });
126
+ </script>
127
+
128
+ <span class={twMerge(`inline-block font-mono leading-none text-current text-xl`, _class)}>
129
+ {_frames[currentFrame]}
130
+ </span>
@@ -0,0 +1,12 @@
1
+ export type SpinnerUnicodeVariant = "braille_bar" | "braille_bar_dot" | "braille_dot_circle" | "braille_dot_bounce" | "half_circle" | "quarter_circle" | "ascii" | "bar_v" | "bar_h" | "shade" | "arrows" | "arrows2";
2
+ export declare function spinnerCreateBackAndForthCharFrames(width: number, hiChar: string, loChar: string, glue?: string): string[];
3
+ type $$ComponentProps = {
4
+ class?: string;
5
+ speed?: number;
6
+ variant?: SpinnerUnicodeVariant;
7
+ reversed?: boolean;
8
+ frames?: string[];
9
+ };
10
+ declare const SpinnerUnicode: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type SpinnerUnicode = ReturnType<typeof SpinnerUnicode>;
12
+ export default SpinnerUnicode;
@@ -1 +1,2 @@
1
1
  export { default as Spinner } from "./Spinner.svelte";
2
+ export { default as SpinnerUnicode, spinnerCreateBackAndForthCharFrames, type SpinnerUnicodeVariant, } from "./SpinnerUnicode.svelte";
@@ -1 +1,2 @@
1
1
  export { default as Spinner } from "./Spinner.svelte";
2
+ export { default as SpinnerUnicode, spinnerCreateBackAndForthCharFrames, } from "./SpinnerUnicode.svelte";
@@ -0,0 +1,261 @@
1
+ <script lang="ts" generics="T extends Item">
2
+ import { createClog } from "@marianmeres/clog";
3
+ import { ItemCollection, type Item } from "@marianmeres/item-collection";
4
+ import { Debounced, watch } from "runed";
5
+ import { twMerge } from "../../utils/tw-merge.js";
6
+ import { unaccent } from "../../utils/unaccent.js";
7
+ import Spinner from "../Spinner/Spinner.svelte";
8
+
9
+ const clog = createClog("TypeaheadInput");
10
+
11
+ interface Props {
12
+ input?: HTMLInputElement;
13
+ value: any;
14
+ placeholder?: string;
15
+ getOptions: (s: string, current: T[]) => Promise<T[]>;
16
+ renderOptionLabel?: (item: T) => string;
17
+ itemIdPropName?: string;
18
+ name?: string;
19
+ // consumer might need to differentiate between "value" and a "true submitted value"
20
+ // (eg. when hitting Enter)... so we have `onSubmit`. Note, that this is NOT a "form.onSubmit"
21
+ onSubmit?: (s: string) => void;
22
+ // when we hit backspace, and we are at cursor position 0... this is useful for
23
+ // consumer...
24
+ onDeleteRequest?: () => void;
25
+ //
26
+ class?: string;
27
+ classInput?: string;
28
+ noSpinner?: boolean;
29
+ // master disable flag not allowing to list all options on empty query
30
+ noListAllOnEmptyQ?: boolean;
31
+ // use empty string to disable hint
32
+ appendHint?: string;
33
+ }
34
+
35
+ let {
36
+ input = $bindable(),
37
+ value = $bindable(),
38
+ placeholder,
39
+ getOptions,
40
+ renderOptionLabel,
41
+ itemIdPropName = "id",
42
+ name = "text_input",
43
+ //
44
+ onSubmit,
45
+ onDeleteRequest,
46
+ //
47
+ class: classProp,
48
+ classInput = "",
49
+ noSpinner,
50
+ noListAllOnEmptyQ,
51
+ appendHint = " [tab]",
52
+ }: Props = $props();
53
+
54
+ let inputEl: HTMLInputElement = $state()!;
55
+ const randName = "name-" + Math.random().toString(36).slice(2);
56
+ let isFetching = $state(false); // not used currently
57
+ let previousKey = $state();
58
+
59
+ // special case flag to allow listing all available options when navigating with arrows
60
+ // even on empty query
61
+ let allowListAll = $state(false);
62
+
63
+ // ItemCollection of all possible candidates based on the current query (value)
64
+ const options = new ItemCollection<T>([], {
65
+ idPropName: itemIdPropName,
66
+ searchable: { getContent: (item) => _renderOptionLabel(item) },
67
+ allowNextPrevCycle: true,
68
+ sortFn: (a, b) => {
69
+ const _a = _renderOptionLabel(a).toLowerCase();
70
+ const _b = _renderOptionLabel(b).toLowerCase();
71
+ return _a.localeCompare(_b);
72
+ },
73
+ });
74
+
75
+ // deriving the actual suggestion from available candidates...
76
+ let available = $derived($options);
77
+ let suggestion: string = $derived.by(() => {
78
+ if (!available.active) return "";
79
+ // a little dance, since we need to be case insensitive
80
+ // otherwise we would just: `value = _renderOptionLabel(available.active)`
81
+ const suggestion = _renderOptionLabel(available.active);
82
+ return (value || "") + suggestion.slice(value?.length || 0);
83
+ });
84
+
85
+ let visibleSuggestion: string = $derived.by(() => {
86
+ if (
87
+ !suggestion ||
88
+ unaccent(suggestion.toLowerCase()) === unaccent(value?.toLowerCase())
89
+ ) {
90
+ return "";
91
+ }
92
+
93
+ return suggestion ? suggestion + appendHint : suggestion;
94
+ });
95
+
96
+ // helper
97
+ function _renderOptionLabel(item: T): string {
98
+ return renderOptionLabel?.(item) || `${item[itemIdPropName]}`;
99
+ }
100
+
101
+ // reset suggestion asap, even before the debounced search finishes (it feels better)
102
+ // the debounce will take over short after
103
+ watch([() => value], ([currQ], [oldQ]) => {
104
+ if (value === undefined) return;
105
+
106
+ // if we don't have a query or nothing is active, reset asap
107
+ if ((!allowListAll && !currQ) || !available.active) {
108
+ options.clear();
109
+ return;
110
+ }
111
+
112
+ // we need to be case insensitive
113
+ const suggestion = _renderOptionLabel(available.active);
114
+ if (!suggestion.toLowerCase().startsWith(currQ.toLowerCase())) {
115
+ options.clear();
116
+ return;
117
+ }
118
+ });
119
+
120
+ // do the query search
121
+ const debounced = new Debounced(() => value, 150);
122
+ watch([() => debounced.current, () => allowListAll], ([currQ, ala], [oldQ, _]) => {
123
+ // always start fresh
124
+ options.clear();
125
+
126
+ // no suggestion on empty input
127
+ if (!ala && !currQ) return;
128
+
129
+ isFetching = true;
130
+
131
+ getOptions(currQ, [])
132
+ .then((res) => {
133
+ // no options?
134
+ if (!res.length) return;
135
+
136
+ let found = res;
137
+ if (currQ) {
138
+ // so, here we have some candidate items... but, this is not a typical
139
+ // "word search", this is an exact, case-insensitive "string begins with",
140
+ // so we need to filter further...
141
+ found = res.filter((item) => {
142
+ const label = unaccent(_renderOptionLabel(item).toLowerCase());
143
+ return label.startsWith(unaccent(currQ.toLowerCase()));
144
+ });
145
+ }
146
+
147
+ // no exact "starts with" found?
148
+ if (!found.length) return;
149
+
150
+ // finally, this is where we pick the actual suggestion (by setting it as active)
151
+ options.addMany(found);
152
+ options.setActiveFirst();
153
+ })
154
+ .catch(clog.error)
155
+ .finally(() => (isFetching = false));
156
+ });
157
+
158
+ //
159
+ let _fixedInputClasses = $derived(
160
+ twMerge(
161
+ "form-input z-10 relative",
162
+ classInput,
163
+ "text-black",
164
+ "border-0 p-0 bg-transparent block w-full",
165
+ "focus:outline-0 focus:border-0 focus:ring-0",
166
+ "focus-visible:outline-0 focus-visible:border-0 focus-visible:ring-0"
167
+ )
168
+ );
169
+
170
+ function _on_submit(v: string) {
171
+ v = `${v || ""}`.trim();
172
+ if (v) onSubmit?.(v);
173
+ // reset this flag, next up/down arrow will switch it on again
174
+ allowListAll = false;
175
+ }
176
+
177
+ // $inspect({ isFetching, value, suggestion }).with(clog);
178
+ // $inspect({ previousKey }).with(clog);
179
+ </script>
180
+
181
+ <div class={twMerge("", classProp)}>
182
+ <div class="flex items-center">
183
+ <div class="relative inline-block flex-1">
184
+ <input
185
+ type="text"
186
+ bind:value
187
+ bind:this={inputEl}
188
+ {name}
189
+ class={twMerge(_fixedInputClasses)}
190
+ placeholder={suggestion ? "" : placeholder}
191
+ autocomplete="off"
192
+ onkeydown={(e) => {
193
+ // ignore on any modifier key
194
+ if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
195
+ return;
196
+ }
197
+
198
+ const cursorPos = inputEl.selectionStart ?? 0;
199
+
200
+ // also ignore ArrowRight if cursor is not at the end
201
+ if (value?.length) {
202
+ const maxPos = value.length;
203
+ // clog({ cursorPosition: pos, maxPos, value });
204
+ if (e.key === "ArrowRight" && cursorPos < maxPos) return;
205
+ }
206
+
207
+ // special case Tab handling - if we hit Enter just before, we want Tab
208
+ // to behave normally (so we are able to set value which HAS a suggestion
209
+ // but is NOT a suggestion - eg "New" vs "New York")
210
+ if (previousKey === "Enter" && e.key === "Tab") {
211
+ return;
212
+ }
213
+
214
+ // this acts as trigger for getOptions if empty value (`allowListAll` is watched)
215
+ allowListAll =
216
+ !value?.length &&
217
+ !noListAllOnEmptyQ &&
218
+ ["ArrowDown", "ArrowUp"].includes(e.key);
219
+
220
+ //
221
+ const suggestion = options.active ? _renderOptionLabel(options.active) : null;
222
+ if (e.key === "ArrowDown") {
223
+ options.setActiveNext();
224
+ e.preventDefault();
225
+ } else if (e.key === "ArrowUp") {
226
+ options.setActivePrevious();
227
+ e.preventDefault();
228
+ } else if (["ArrowRight", "Tab"].includes(e.key) && suggestion) {
229
+ if (e.key === "Tab" && value !== suggestion) {
230
+ e.preventDefault();
231
+ }
232
+ value = suggestion;
233
+ if (e.key === "Tab") _on_submit(value);
234
+ } else if (e.key === "Enter") {
235
+ options.clear();
236
+ _on_submit(value);
237
+ } else if (e.key === "Backspace" && cursorPos === 0) {
238
+ onDeleteRequest?.();
239
+ }
240
+
241
+ previousKey = e.key;
242
+ }}
243
+ onblur={() => _on_submit(value)}
244
+ />
245
+ <input
246
+ type="text"
247
+ bind:value={visibleSuggestion}
248
+ class={twMerge(
249
+ _fixedInputClasses,
250
+ "absolute inset-0 pointer-events-none opacity-40 z-0"
251
+ )}
252
+ name={randName}
253
+ tabindex="-1"
254
+ readonly
255
+ />
256
+ </div>
257
+ {#if !noSpinner && isFetching}
258
+ <Spinner class="size-5 opacity-50" />
259
+ {/if}
260
+ </div>
261
+ </div>
@@ -0,0 +1,40 @@
1
+ import { type Item } from "@marianmeres/item-collection";
2
+ declare function $$render<T extends Item>(): {
3
+ props: {
4
+ input?: HTMLInputElement;
5
+ value: any;
6
+ placeholder?: string;
7
+ getOptions: (s: string, current: T[]) => Promise<T[]>;
8
+ renderOptionLabel?: (item: T) => string;
9
+ itemIdPropName?: string;
10
+ name?: string;
11
+ onSubmit?: (s: string) => void;
12
+ onDeleteRequest?: () => void;
13
+ class?: string;
14
+ classInput?: string;
15
+ noSpinner?: boolean;
16
+ noListAllOnEmptyQ?: boolean;
17
+ appendHint?: string;
18
+ };
19
+ exports: {};
20
+ bindings: "value" | "input";
21
+ slots: {};
22
+ events: {};
23
+ };
24
+ declare class __sveltets_Render<T extends Item> {
25
+ props(): ReturnType<typeof $$render<T>>['props'];
26
+ events(): ReturnType<typeof $$render<T>>['events'];
27
+ slots(): ReturnType<typeof $$render<T>>['slots'];
28
+ bindings(): "value" | "input";
29
+ exports(): {};
30
+ }
31
+ interface $$IsomorphicComponent {
32
+ new <T extends Item>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
33
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
34
+ } & ReturnType<__sveltets_Render<T>['exports']>;
35
+ <T extends Item>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
36
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
37
+ }
38
+ declare const TypeaheadInput: $$IsomorphicComponent;
39
+ type TypeaheadInput<T extends Item> = InstanceType<typeof TypeaheadInput<T>>;
40
+ export default TypeaheadInput;
@@ -0,0 +1 @@
1
+ export { default as TypeaheadInput } from "./TypeaheadInput.svelte";
@@ -0,0 +1 @@
1
+ export { default as TypeaheadInput } from "./TypeaheadInput.svelte";
package/dist/index.css CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  @import "./actions/tooltip/index.css";
6
6
  @import "./components/Button/index.css";
7
+ @import "./components/ButtonGroupRadio/index.css";
7
8
  @import "./components/DismissibleMessage/index.css";
8
9
  @import "./components/Input/index.css";
9
10
  @import "./components/Notifications/index.css";
package/dist/index.d.ts CHANGED
@@ -3,7 +3,9 @@ export * from "./components/AnimatedElipsis/index.js";
3
3
  export * from "./components/AppShell/index.js";
4
4
  export * from "./components/Backdrop/index.js";
5
5
  export * from "./components/Button/index.js";
6
+ export * from "./components/ButtonGroupRadio/index.js";
6
7
  export * from "./components/ColorScheme/index.js";
8
+ export * from "./components/CommandMenu/index.js";
7
9
  export * from "./components/DismissibleMessage/index.js";
8
10
  export * from "./components/Drawer/index.js";
9
11
  export * from "./components/HoverExpandableWidth/index.js";
@@ -16,6 +18,7 @@ export * from "./components/Progress/index.js";
16
18
  export * from "./components/Spinner/index.js";
17
19
  export * from "./components/Switch/index.js";
18
20
  export * from "./components/TwCheck/index.js";
21
+ export * from "./components/TypeaheadInput/index.js";
19
22
  export * from "./components/X/index.js";
20
23
  export * from "./utils/index.js";
21
24
  export * from "./actions/index.js";
package/dist/index.js CHANGED
@@ -4,7 +4,9 @@ export * from "./components/AnimatedElipsis/index.js";
4
4
  export * from "./components/AppShell/index.js";
5
5
  export * from "./components/Backdrop/index.js";
6
6
  export * from "./components/Button/index.js";
7
+ export * from "./components/ButtonGroupRadio/index.js";
7
8
  export * from "./components/ColorScheme/index.js";
9
+ export * from "./components/CommandMenu/index.js";
8
10
  export * from "./components/DismissibleMessage/index.js";
9
11
  export * from "./components/Drawer/index.js";
10
12
  export * from "./components/HoverExpandableWidth/index.js";
@@ -17,6 +19,7 @@ export * from "./components/Progress/index.js";
17
19
  export * from "./components/Spinner/index.js";
18
20
  export * from "./components/Switch/index.js";
19
21
  export * from "./components/TwCheck/index.js";
22
+ export * from "./components/TypeaheadInput/index.js";
20
23
  export * from "./components/X/index.js";
21
24
  // utils
22
25
  export * from "./utils/index.js";
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export type DataAttributes = {
2
2
  [key: `data-${string}`]: string | number | boolean;
3
3
  };
4
+ export type TranslateFn = (k: string, values?: false | null | undefined | Record<string, string | number>, fallback?: string | boolean, i18nSpanWrap?: boolean) => string;
4
5
  export type TW_COLORS = "amber" | "blue" | "cyan" | "emerald" | "fuchsia" | "gray" | "green" | "indigo" | "lime" | "neutral" | "orange" | "pink" | "purple" | "red" | "rose" | "sky" | "slate" | "stone" | "teal" | "violet" | "yellow" | "zinc";
5
6
  /**
6
7
  DO NOTE REMOVE. FOOD FOR TW COMPILER...
@@ -0,0 +1 @@
1
+ export declare const escapeRegex: (str: string) => string;
@@ -0,0 +1 @@
1
+ export const escapeRegex = (str) => `${str}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@@ -0,0 +1,18 @@
1
+ /** Sugar, Ah, Honey, Honey... */
2
+ export declare class EventEmitter extends EventTarget {
3
+ #private;
4
+ constructor();
5
+ /** Overriding so we can use removeAll (via the abortController) later... */
6
+ addEventListener(type: string, listener: any, // fuck it (we would need 3 signatures)
7
+ options?: boolean | AddEventListenerOptions): void;
8
+ /** Yeah! */
9
+ removeAllListeners(): void;
10
+ /** Alias for dispatchEvent(CustomEvent) */
11
+ emit(eventName: string, detail?: any): void;
12
+ /** Alias for addEventListener */
13
+ on(eventName: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): () => void;
14
+ /** Alias for removeEventListener */
15
+ off(eventName: string, listener: EventListenerOrEventListenerObject): this;
16
+ /** Alias for addEventListener with once flag */
17
+ once(eventName: string, listener: EventListenerOrEventListenerObject): () => void;
18
+ }