@marianmeres/stuic 2.1.2 → 2.1.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.
@@ -27,7 +27,6 @@
27
27
  }
28
28
  });
29
29
 
30
- let value = $state();
31
30
  let isPending = $state(false);
32
31
  </script>
33
32
 
@@ -49,13 +48,13 @@
49
48
  noClickOutsideClose
50
49
  type={acp?.current?.type}
51
50
  class={twMerge(
52
- "max-w-xl justify-end max-h-[62vh] sm:max-h-[200px] border p-4 rounded-lg",
51
+ "max-w-xl justify-end max-h-[62vh] h-auto border p-4 rounded-lg",
53
52
  // different max-h based on not/existing content
54
- isTHCNotEmpty(acp?.current?.content) ? "sm:max-h-[200px]" : "sm:max-h-[150px]",
55
- acp?.current?.type === PROMPT && "sm:max-h-[250px]",
53
+ // isTHCNotEmpty(acp?.current?.content) ? "sm:max-h-[200px]" : "sm:max-h-[150px]",
54
+ // acp?.current?.type === PROMPT && "sm:max-h-[250px]",
56
55
  classProp
57
56
  )}
58
57
  >
59
- <Current bind:value bind:isPending {acp} />
58
+ <Current bind:isPending {acp} />
60
59
  </ModalDialog>
61
60
  {/if}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { tick } from "svelte";
2
3
  import { twMerge } from "../../utils/tw-merge.js";
3
4
  import Button from "../Button/Button.svelte";
4
5
  import FieldInput from "../Input/FieldInput.svelte";
@@ -16,7 +17,6 @@
16
17
 
17
18
  interface Props {
18
19
  acp?: AlertConfirmPromptStack;
19
- value?: any;
20
20
  isPending?: boolean;
21
21
  forceAsHtml?: boolean;
22
22
  class?: string;
@@ -35,7 +35,6 @@
35
35
 
36
36
  let {
37
37
  acp,
38
- value = $bindable(),
39
38
  isPending = $bindable(false),
40
39
  forceAsHtml = true,
41
40
  class: classProp,
@@ -62,22 +61,35 @@
62
61
  return out;
63
62
  });
64
63
 
65
- const createOnClick = (worker: CallableFunction) => async (e: Event) => {
66
- e.preventDefault();
67
- isPending = true;
68
- await Promise.resolve(worker(current.type === PROMPT ? value : true));
69
- isPending = false;
70
- value = null;
71
- };
64
+ let inputEl = $state<any>();
65
+ let okButtonEl = $state<any>();
66
+
67
+ const createOnClick =
68
+ (type: "cancel" | "ok" | "custom", worker: CallableFunction) =>
69
+ async (e: Event | null) => {
70
+ e?.preventDefault();
71
+
72
+ // trigger validate
73
+ if (inputEl && type === "ok" && current.type === PROMPT) {
74
+ inputEl.dispatchEvent(new Event("input", { bubbles: true }));
75
+ inputEl.dispatchEvent(new Event("change", { bubbles: true }));
76
+ if (!inputEl.checkValidity()) return;
77
+ }
78
+
79
+ //
80
+ isPending = true;
81
+ await Promise.resolve(worker(current.type === PROMPT ? current.value : true));
82
+ isPending = false;
83
+ };
72
84
 
73
85
  const debug = (c?: string, flag = 0) =>
74
86
  c && flag ? `outline outline-dashed ${c}` : "";
75
87
 
76
88
  // Default classes
77
89
 
78
- const _class = "p-1 sm:p-2 h-full flex flex-col overflow-hidden";
90
+ const _class = "p-1 sm:p-2 h-full flex flex-col min-h-[150px]"; // overflow-hidden
79
91
 
80
- const _classWrap = `flex-1 sm:flex sm:items-start overflow-hidden`;
92
+ const _classWrap = `flex-1 sm:flex sm:items-start `; // overflow-hidden
81
93
 
82
94
  const _classIconBox = `size-12 sm:size-10
83
95
  mt-1 mb-4 sm:my-0 sm:mr-5
@@ -87,7 +99,7 @@
87
99
  bg-neutral-950/10 text-neutral-950/80
88
100
  dark:bg-neutral-50/20 dark:text-neutral-50/80`;
89
101
 
90
- const _classContentBox = `mt-3 sm:mt-0 flex-1 overflow-hidden h-full flex flex-col`;
102
+ const _classContentBox = `mt-3 sm:mt-0 flex-1 h-full flex flex-col`; // overflow-hidden
91
103
 
92
104
  const _classTitle = `text-center sm:text-left text-base font-semibold leading-6`;
93
105
 
@@ -128,7 +140,10 @@
128
140
  <Thc thc={current.title} {forceAsHtml} />
129
141
  </h1>
130
142
 
131
- <div class="scrollable overflow-y-auto flex-1" style="scrollbar-width: thin;">
143
+ <div
144
+ class="scrollable overflow-y-auto flex-1 max-h-[30vh]"
145
+ style="scrollbar-width: thin;"
146
+ >
132
147
  {#if isTHCNotEmpty(current.content)}
133
148
  <div class={twMerge("content", _classContent, classContent)}>
134
149
  <Thc thc={current.content} {forceAsHtml} />
@@ -136,10 +151,11 @@
136
151
  {/if}
137
152
 
138
153
  {#if current.type === PROMPT}
139
- <div class={twMerge("input-box", "mt-1 p-2", classInputBox)}>
154
+ <div class={twMerge("input-box", "mt-3 p-1", classInputBox)}>
140
155
  {#if current?.promptFieldProps?.options?.length}
141
156
  <FieldSelect
142
- bind:value
157
+ bind:value={current.value}
158
+ bind:input={inputEl}
143
159
  class={twMerge("input", "m-0", classInput)}
144
160
  options={current.promptFieldProps.options}
145
161
  renderSize="sm"
@@ -148,11 +164,17 @@
148
164
  />
149
165
  {:else}
150
166
  <FieldInput
151
- bind:value
152
- class={twMerge("input", classInput)}
167
+ bind:value={current.value}
168
+ bind:input={inputEl}
169
+ class={twMerge("input", "m-0", classInput)}
153
170
  renderSize="sm"
154
171
  disabled={isPending}
155
172
  {...current?.promptFieldProps || {}}
173
+ onkeydown={(e) => {
174
+ if (e.key === "Enter" && inputEl.checkValidity()) {
175
+ okButtonEl?.focus()?.click(); // hm...
176
+ }
177
+ }}
156
178
  />
157
179
  {/if}
158
180
  </div>
@@ -166,7 +188,7 @@
166
188
  <Button
167
189
  class={twMerge("cancel", _classButton, classButton)}
168
190
  disabled={isPending}
169
- onclick={createOnClick(current.onCancel)}
191
+ onclick={createOnClick("cancel", current.onCancel)}
170
192
  >
171
193
  <Thc thc={current.labelCancel} {forceAsHtml} />
172
194
  </Button>
@@ -177,7 +199,7 @@
177
199
  <Button
178
200
  class={twMerge("custom", _classButton, classButton)}
179
201
  disabled={isPending}
180
- onclick={createOnClick(current.onCustom)}
202
+ onclick={createOnClick("custom", current.onCustom)}
181
203
  >
182
204
  <Thc thc={current.labelCustom} {forceAsHtml} />
183
205
  </Button>
@@ -188,7 +210,8 @@
188
210
  class={twMerge("ok", _classButton, classButton)}
189
211
  variant="primary"
190
212
  disabled={isPending}
191
- onclick={createOnClick(current.onOk)}
213
+ onclick={createOnClick("ok", current.onOk)}
214
+ bind:el={okButtonEl}
192
215
  >
193
216
  <Thc thc={current.labelOk} {forceAsHtml} />
194
217
  </Button>
@@ -1,7 +1,6 @@
1
1
  import { type AlertConfirmPromptStack } from "./alert-confirm-prompt-stack.svelte.js";
2
2
  interface Props {
3
3
  acp?: AlertConfirmPromptStack;
4
- value?: any;
5
4
  isPending?: boolean;
6
5
  forceAsHtml?: boolean;
7
6
  class?: string;
@@ -17,6 +16,6 @@ interface Props {
17
16
  classButton?: string;
18
17
  classSpinnerBox?: string;
19
18
  }
20
- declare const Current: import("svelte").Component<Props, {}, "value" | "isPending">;
19
+ declare const Current: import("svelte").Component<Props, {}, "isPending">;
21
20
  type Current = ReturnType<typeof Current>;
22
21
  export default Current;
@@ -76,6 +76,7 @@
76
76
  type SnippetWithId = Snippet<[{ id: string }]>;
77
77
 
78
78
  interface Props extends Record<string, any> {
79
+ trigger?: Snippet<[{ value: string; modal: Modal }]>;
79
80
  input?: HTMLInputElement;
80
81
  value: string;
81
82
  label?: SnippetWithId | THC;
@@ -135,9 +136,12 @@
135
136
  searchPlaceholder?: string;
136
137
  name: string;
137
138
  itemIdPropName?: string;
139
+ // for custom stuff...
140
+ onChange?: (value: string) => void;
138
141
  }
139
142
 
140
143
  let {
144
+ trigger,
141
145
  input = $bindable(),
142
146
  value = $bindable(), //
143
147
  label = "",
@@ -191,6 +195,7 @@
191
195
  searchPlaceholder,
192
196
  name,
193
197
  itemIdPropName = "id",
198
+ onChange,
194
199
  ...rest
195
200
  }: Props = $props();
196
201
 
@@ -344,7 +349,7 @@
344
349
 
345
350
  // "inner" submit
346
351
  function try_submit(force = false) {
347
- clog("try_submit", innerValue);
352
+ // clog("try_submit", innerValue);
348
353
  if (innerValue) {
349
354
  let found = have_option_label_like(_optionsColl.items, innerValue);
350
355
  if (!found && !allowUnknown) {
@@ -396,6 +401,7 @@
396
401
  _optionsColl.clear();
397
402
  modal.close();
398
403
  _dispatch_change_to_owner();
404
+ onChange?.(value);
399
405
  }
400
406
 
401
407
  // clears, closes, submits nothing
@@ -497,33 +503,36 @@
497
503
 
498
504
  <!-- must wrap both -->
499
505
  <div>
500
- <FieldLikeButton
501
- bind:value
502
- bind:input={parentHiddenInputEl}
503
- {name}
504
- class={classProp}
505
- {label}
506
- {description}
507
- {labelLeft}
508
- {labelAfter}
509
- {below}
510
- {labelLeftWidth}
511
- {labelLeftBreakpoint}
512
- {classLabel}
513
- {classLabelBox}
514
- {classInputBox}
515
- {classInputBoxWrap}
516
- {classDescBox}
517
- {classBelowBox}
518
- {style}
519
- validate={wrappedValidate}
520
- {required}
521
- {disabled}
522
- renderValue={(v) => {
523
- if (typeof renderValue === "function") return renderValue(v);
524
- // console.log(123123, "renderValue", v);
525
- // prettier-ignore
526
- try {
506
+ {#if trigger}
507
+ {@render trigger({ value, modal })}
508
+ {:else}
509
+ <FieldLikeButton
510
+ bind:value
511
+ bind:input={parentHiddenInputEl}
512
+ {name}
513
+ class={classProp}
514
+ {label}
515
+ {description}
516
+ {labelLeft}
517
+ {labelAfter}
518
+ {below}
519
+ {labelLeftWidth}
520
+ {labelLeftBreakpoint}
521
+ {classLabel}
522
+ {classLabelBox}
523
+ {classInputBox}
524
+ {classInputBoxWrap}
525
+ {classDescBox}
526
+ {classBelowBox}
527
+ {style}
528
+ validate={wrappedValidate}
529
+ {required}
530
+ {disabled}
531
+ renderValue={(v) => {
532
+ if (typeof renderValue === "function") return renderValue(v);
533
+ // console.log(123123, "renderValue", v);
534
+ // prettier-ignore
535
+ try {
527
536
  // defensive
528
537
  if (!v) v = "[]";
529
538
 
@@ -541,9 +550,10 @@
541
550
  clog.warn(e);
542
551
  return `${e}`; // either invalid json or not array...
543
552
  }
544
- }}
545
- onclick={modal?.open}
546
- />
553
+ }}
554
+ onclick={modal?.open}
555
+ />
556
+ {/if}
547
557
 
548
558
  <Modal
549
559
  bind:this={modal}
@@ -1,6 +1,7 @@
1
1
  import { ItemCollection, type Item } from "@marianmeres/item-collection";
2
2
  import { type Snippet } from "svelte";
3
3
  import { type ValidateOptions } from "../../actions/validate.svelte.js";
4
+ import Modal from "../Modal/Modal.svelte";
4
5
  import { NotificationsStack } from "../Notifications/index.js";
5
6
  import type { THC } from "../Thc/Thc.svelte";
6
7
  import type { TranslateFn } from "../../types.js";
@@ -12,6 +13,10 @@ type SnippetWithId = Snippet<[{
12
13
  id: string;
13
14
  }]>;
14
15
  interface Props extends Record<string, any> {
16
+ trigger?: Snippet<[{
17
+ value: string;
18
+ modal: Modal;
19
+ }]>;
15
20
  input?: HTMLInputElement;
16
21
  value: string;
17
22
  label?: SnippetWithId | THC;
@@ -59,6 +64,7 @@ interface Props extends Record<string, any> {
59
64
  searchPlaceholder?: string;
60
65
  name: string;
61
66
  itemIdPropName?: string;
67
+ onChange?: (value: string) => void;
62
68
  }
63
69
  declare const FieldOptions: import("svelte").Component<Props, {}, "value" | "input">;
64
70
  type FieldOptions = ReturnType<typeof FieldOptions>;
@@ -10,7 +10,8 @@
10
10
  text-base placeholder:text-base
11
11
  bg-transparent
12
12
  tracking-tight
13
- focus:outline-0 focus-visible:ring-0
13
+ focus:outline-none focus:ring-0
14
+ focus-visible:outline-none focus-visible:ring-0
14
15
  placeholder:tracking-tight
15
16
  placeholder:text-neutral-950/35 dark:placeholder:text-neutral-50/35
16
17
  text-neutral-950 dark:text-neutral-50;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -13,7 +13,9 @@
13
13
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
14
14
  "format": "prettier --write .",
15
15
  "lint": "prettier --check .",
16
- "test": "vitest --dir src/"
16
+ "test": "vitest --dir src/",
17
+ "svelte-check": "svelte-check",
18
+ "svelte-package": "svelte-package"
17
19
  },
18
20
  "files": [
19
21
  "dist",
@@ -57,7 +59,6 @@
57
59
  "vite": "^6.3.6",
58
60
  "vitest": "^3.2.4"
59
61
  },
60
- "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
61
62
  "dependencies": {
62
63
  "@marianmeres/clog": "^2.3.3",
63
64
  "@marianmeres/item-collection": "^1.2.19",