sveltacular 1.0.18 → 1.0.21

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 (42) hide show
  1. package/README.md +1 -2
  2. package/dist/forms/bool-box/bool-box.svelte +392 -13
  3. package/dist/forms/bool-box/bool-box.svelte.d.ts +2 -0
  4. package/dist/forms/bool-box/index.d.ts +2 -0
  5. package/dist/forms/bool-box/index.js +2 -0
  6. package/dist/forms/date-box/date-box.svelte +104 -53
  7. package/dist/forms/date-box/date-box.svelte.d.ts +8 -3
  8. package/dist/forms/dimension-box/dimension-box.svelte +15 -50
  9. package/dist/forms/file-box/file-box.svelte +7 -25
  10. package/dist/forms/form-input-wrapper/form-input-wrapper.svelte +203 -0
  11. package/dist/forms/form-input-wrapper/form-input-wrapper.svelte.d.ts +16 -0
  12. package/dist/forms/form-input-wrapper/index.d.ts +2 -0
  13. package/dist/forms/form-input-wrapper/index.js +2 -0
  14. package/dist/forms/index.d.ts +0 -1
  15. package/dist/forms/index.js +0 -1
  16. package/dist/forms/list-box/list-box.svelte +26 -9
  17. package/dist/forms/list-box/list-box.svelte.d.ts +3 -0
  18. package/dist/forms/money-box/money-box.svelte +104 -65
  19. package/dist/forms/money-box/money-box.svelte.d.ts +6 -0
  20. package/dist/forms/number-box/number-box.svelte +93 -49
  21. package/dist/forms/number-box/number-box.svelte.d.ts +6 -0
  22. package/dist/forms/number-range-box/number-range-box.svelte +22 -58
  23. package/dist/forms/phone-box/phone-box.svelte +101 -38
  24. package/dist/forms/phone-box/phone-box.svelte.d.ts +6 -1
  25. package/dist/forms/slider/slider.svelte +13 -6
  26. package/dist/forms/slider/slider.svelte.d.ts +6 -2
  27. package/dist/forms/tag-box/tag-box.svelte +5 -3
  28. package/dist/forms/text-area/text-area.svelte +22 -2
  29. package/dist/forms/text-area/text-area.svelte.d.ts +4 -0
  30. package/dist/forms/text-box/text-box.svelte +97 -131
  31. package/dist/forms/text-box/text-box.svelte.d.ts +7 -2
  32. package/dist/forms/time-box/time-box.svelte +106 -37
  33. package/dist/forms/time-box/time-box.svelte.d.ts +10 -3
  34. package/dist/forms/url-box/url-box.svelte +26 -5
  35. package/dist/forms/url-box/url-box.svelte.d.ts +7 -1
  36. package/dist/generic/theme-provider/theme-provider-demo.svelte +7 -2
  37. package/dist/navigation/dropdown-button/dropdown-button.svelte +102 -3
  38. package/dist/navigation/dropdown-button/dropdown-manager.svelte.d.ts +47 -0
  39. package/dist/navigation/dropdown-button/dropdown-manager.svelte.js +47 -0
  40. package/dist/tables/table-cell.svelte +2 -0
  41. package/dist/tables/table-row.svelte +1 -0
  42. package/package.json +1 -1
@@ -2,25 +2,36 @@
2
2
  import { untrack } from 'svelte';
3
3
  import { uniqueId, type FormFieldSizeOptions } from '../../index.js';
4
4
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
5
+ import FormInputWrapper from '../form-input-wrapper';
5
6
 
6
7
  let {
7
8
  value = $bindable('' as string | null),
8
9
  size = 'md' as FormFieldSizeOptions,
10
+ placeholder = '',
9
11
  onChange = undefined,
12
+ onCheckChanged = undefined,
10
13
  label = undefined,
11
14
  helperText = undefined,
12
15
  feedback = undefined,
13
16
  disabled = false,
14
- required = false
17
+ required = false,
18
+ readonly = false,
19
+ nullable = false,
20
+ nullText = ''
15
21
  }: {
16
22
  value?: string | null;
17
23
  size?: FormFieldSizeOptions;
18
- onChange?: ((value: string) => void) | undefined;
24
+ placeholder?: string;
25
+ onChange?: ((value: string | null) => void) | undefined;
26
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
19
27
  label?: string;
20
28
  helperText?: string;
21
29
  feedback?: FormFieldFeedback;
22
30
  disabled?: boolean;
23
31
  required?: boolean;
32
+ readonly?: boolean;
33
+ nullable?: boolean;
34
+ nullText?: string;
24
35
  } = $props();
25
36
 
26
37
  const id = uniqueId();
@@ -65,8 +76,9 @@
65
76
  };
66
77
 
67
78
  const publishChange = () => {
68
- value = getCombinedValue();
69
- onChange?.(value);
79
+ const currentValue = !nullable || isChecked ? getCombinedValue() : null;
80
+ value = currentValue;
81
+ onChange?.(currentValue);
70
82
  return value;
71
83
  };
72
84
 
@@ -139,9 +151,57 @@
139
151
  }
140
152
  };
141
153
 
154
+ // Track whether the nullable checkbox is checked (i.e., whether field has a value)
155
+ let isChecked = $state(untrack(() => !!value));
156
+
157
+ // Remember the last non-null value so we can restore it when re-checking
158
+ let lastValue = $state<string | undefined>(undefined);
159
+
160
+ // Derive the actual disabled state: disabled prop OR (nullable and unchecked)
161
+ let inputDisabled = $derived(disabled || (nullable && !isChecked));
162
+
163
+ let showInput = $derived(!nullable || isChecked);
164
+
165
+ let effectiveNullText = $derived(nullText || '(___) ___-____');
166
+
167
+ const checkChanged = () => {
168
+ if (nullable) {
169
+ if (isChecked) {
170
+ // Restore last value if available, otherwise use empty string
171
+ const restoreValue = lastValue || '';
172
+ setValue(restoreValue);
173
+ } else {
174
+ // Store current value before clearing
175
+ const currentValue = getCombinedValue();
176
+ if (currentValue) {
177
+ lastValue = currentValue;
178
+ }
179
+ areaCode = '';
180
+ localExt = '';
181
+ lastFour = '';
182
+ value = null;
183
+ }
184
+ }
185
+ onCheckChanged?.(isChecked);
186
+ };
187
+
142
188
  // Set the initial value
143
189
  setValue(value ?? '');
144
190
 
191
+ $effect(() => {
192
+ if (!value) {
193
+ // Use untrack to prevent writes to isChecked/value from triggering this effect again
194
+ untrack(() => {
195
+ if (nullable) isChecked = false;
196
+ });
197
+ } else {
198
+ // Initialize lastValue if we have an initial value
199
+ if (!lastValue) {
200
+ lastValue = value;
201
+ }
202
+ }
203
+ });
204
+
145
205
  $effect(() => {
146
206
  // Only trigger when the phone number parts change
147
207
  const hasValue = areaCode || localExt || lastFour;
@@ -155,19 +215,26 @@
155
215
  </script>
156
216
 
157
217
  <FormField {size} {label} id="{id}-areaCode" {required} {disabled} {helperText} {feedback}>
158
- <div class="input">
218
+ <FormInputWrapper
219
+ {disabled}
220
+ {nullable}
221
+ nullText={effectiveNullText}
222
+ onCheckChanged={checkChanged}
223
+ >
159
224
  <span class="areaCode segment">
160
225
  <span>(</span>
161
226
  <input
162
227
  id="{id}-areaCode"
163
228
  type="text"
229
+ {placeholder}
164
230
  oninput={valueChanged}
165
231
  onkeyup={keyUp}
166
232
  onchange={valueChanged}
167
233
  bind:value={areaCode}
168
234
  name="areaCode"
169
235
  data-maxlength="3"
170
- {disabled}
236
+ disabled={inputDisabled}
237
+ {readonly}
171
238
  {required}
172
239
  />
173
240
  <span>)</span>
@@ -176,13 +243,15 @@
176
243
  <input
177
244
  id="{id}-localExt"
178
245
  type="text"
246
+ {placeholder}
179
247
  oninput={valueChanged}
180
248
  onchange={valueChanged}
181
249
  onkeyup={keyUp}
182
250
  bind:value={localExt}
183
251
  name="localExt"
184
252
  data-maxlength="3"
185
- {disabled}
253
+ disabled={inputDisabled}
254
+ {readonly}
186
255
  {required}
187
256
  />
188
257
  </span>
@@ -191,55 +260,43 @@
191
260
  <input
192
261
  id="{id}-lastFour"
193
262
  type="text"
263
+ {placeholder}
194
264
  oninput={valueChanged}
195
265
  onchange={valueChanged}
196
266
  onkeyup={keyUp}
197
267
  bind:value={lastFour}
198
268
  name="lastFour"
199
269
  data-maxlength="4"
200
- {disabled}
270
+ disabled={inputDisabled}
271
+ {readonly}
201
272
  {required}
202
- /></span
203
- >
204
- </div>
273
+ />
274
+ </span>
275
+ </FormInputWrapper>
205
276
  </FormField>
206
277
 
207
- <style>.input {
208
- display: flex;
209
- align-items: center;
210
- justify-content: flex-start;
211
- position: relative;
212
- width: 100%;
213
- height: 100%;
214
- border-radius: var(--radius-md);
215
- border: var(--border-thin) solid var(--form-input-border);
216
- background-color: var(--form-input-bg);
217
- color: var(--form-input-fg);
218
- font-size: var(--font-md);
219
- font-weight: 500;
220
- line-height: 2rem;
221
- padding-left: var(--spacing-base);
222
- gap: var(--spacing-sm);
223
- transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out);
224
- user-select: none;
225
- white-space: nowrap;
226
- }
227
- .input .segment {
278
+ <style>.segment {
228
279
  position: relative;
229
280
  display: flex;
230
281
  align-items: center;
231
282
  justify-content: center;
283
+ gap: var(--spacing-sm);
232
284
  }
233
- .input .areaCode {
285
+
286
+ .areaCode {
234
287
  flex-basis: 100px;
288
+ padding-left: var(--spacing-base);
235
289
  }
236
- .input .localExt {
290
+
291
+ .localExt {
237
292
  flex-basis: 80px;
238
293
  }
239
- .input .lastFour {
294
+
295
+ .lastFour {
240
296
  flex-basis: 140px;
241
297
  }
242
- .input input {
298
+
299
+ input {
243
300
  background-color: transparent;
244
301
  border: none;
245
302
  line-height: 2rem;
@@ -251,10 +308,16 @@
251
308
  text-align: center;
252
309
  color: inherit;
253
310
  }
254
- .input input:focus {
311
+ input:focus {
255
312
  outline: none;
256
313
  }
257
- .input input:focus-visible {
314
+ input:focus-visible {
258
315
  outline: 2px solid var(--focus-ring, #007bff);
259
316
  outline-offset: 2px;
317
+ }
318
+ input:disabled {
319
+ cursor: not-allowed;
320
+ }
321
+ input:read-only {
322
+ cursor: default;
260
323
  }</style>
@@ -3,12 +3,17 @@ import { type FormFieldFeedback } from '../form-field/form-field.svelte';
3
3
  type $$ComponentProps = {
4
4
  value?: string | null;
5
5
  size?: FormFieldSizeOptions;
6
- onChange?: ((value: string) => void) | undefined;
6
+ placeholder?: string;
7
+ onChange?: ((value: string | null) => void) | undefined;
8
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
7
9
  label?: string;
8
10
  helperText?: string;
9
11
  feedback?: FormFieldFeedback;
10
12
  disabled?: boolean;
11
13
  required?: boolean;
14
+ readonly?: boolean;
15
+ nullable?: boolean;
16
+ nullText?: string;
12
17
  };
13
18
  declare const PhoneBox: import("svelte").Component<$$ComponentProps, {}, "value">;
14
19
  type PhoneBox = ReturnType<typeof PhoneBox>;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { uniqueId } from '../../helpers/unique-id.js';
3
- import FormField from '../form-field/form-field.svelte';
4
- import type { ComponentSize } from '../../types/size.js';
3
+ import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
4
+ import type { FormFieldSizeOptions } from '../../types/form.js';
5
5
 
6
6
  const id = uniqueId();
7
7
 
@@ -11,24 +11,30 @@
11
11
  max = 100,
12
12
  step = 1,
13
13
  disabled = false,
14
- size = 'full' as ComponentSize,
14
+ required = false,
15
+ size = 'full' as FormFieldSizeOptions,
15
16
  showTooltip = true,
16
17
  showValue = true,
17
18
  formatValue = undefined,
18
19
  onChange = undefined,
19
- label = undefined
20
+ label = undefined,
21
+ helperText = undefined,
22
+ feedback = undefined
20
23
  }: {
21
24
  value?: number;
22
25
  min?: number;
23
26
  max?: number;
24
27
  step?: number;
25
28
  disabled?: boolean;
26
- size?: ComponentSize;
29
+ required?: boolean;
30
+ size?: FormFieldSizeOptions;
27
31
  showTooltip?: boolean;
28
32
  showValue?: boolean;
29
33
  formatValue?: ((value: number) => string) | undefined;
30
34
  onChange?: ((value: number) => void) | undefined;
31
35
  label?: string;
36
+ helperText?: string;
37
+ feedback?: FormFieldFeedback;
32
38
  } = $props();
33
39
 
34
40
  let isDragging = $state(false);
@@ -52,7 +58,7 @@
52
58
  let displayValue = $derived(formatValue ? formatValue(value) : String(value));
53
59
  </script>
54
60
 
55
- <FormField {size} {label} {id} {disabled}>
61
+ <FormField {size} {label} {id} {disabled} {required} {helperText} {feedback}>
56
62
  <div class="slider-wrapper">
57
63
  <div class="slider-track-container">
58
64
  <input
@@ -63,6 +69,7 @@
63
69
  {max}
64
70
  {step}
65
71
  {disabled}
72
+ {required}
66
73
  bind:value
67
74
  oninput={handleInput}
68
75
  onmousedown={handleMouseDown}
@@ -1,16 +1,20 @@
1
- import type { ComponentSize } from '../../types/size.js';
1
+ import { type FormFieldFeedback } from '../form-field/form-field.svelte';
2
+ import type { FormFieldSizeOptions } from '../../types/form.js';
2
3
  type $$ComponentProps = {
3
4
  value?: number;
4
5
  min?: number;
5
6
  max?: number;
6
7
  step?: number;
7
8
  disabled?: boolean;
8
- size?: ComponentSize;
9
+ required?: boolean;
10
+ size?: FormFieldSizeOptions;
9
11
  showTooltip?: boolean;
10
12
  showValue?: boolean;
11
13
  formatValue?: ((value: number) => string) | undefined;
12
14
  onChange?: ((value: number) => void) | undefined;
13
15
  label?: string;
16
+ helperText?: string;
17
+ feedback?: FormFieldFeedback;
14
18
  };
15
19
  declare const Slider: import("svelte").Component<$$ComponentProps, {}, "value">;
16
20
  type Slider = ReturnType<typeof Slider>;
@@ -403,7 +403,7 @@
403
403
  justify-content: flex-start;
404
404
  position: relative;
405
405
  width: 100%;
406
- height: 100%;
406
+ min-height: 2.125rem;
407
407
  border-radius: var(--radius-md);
408
408
  border: var(--border-thin) solid var(--form-input-border);
409
409
  background-color: var(--form-input-bg);
@@ -411,6 +411,7 @@
411
411
  font-size: var(--font-md);
412
412
  font-weight: 500;
413
413
  line-height: 2rem;
414
+ padding: 0;
414
415
  transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out), fill var(--transition-base) var(--ease-in-out), stroke var(--transition-base) var(--ease-in-out), box-shadow var(--transition-base) var(--ease-in-out);
415
416
  user-select: none;
416
417
  white-space: nowrap;
@@ -433,11 +434,12 @@
433
434
  background-color: transparent;
434
435
  border: none;
435
436
  line-height: 2rem;
437
+ height: 2rem;
436
438
  font-size: var(--font-md);
437
439
  width: 100%;
438
440
  flex-grow: 1;
439
- padding-left: var(--spacing-base);
440
- padding-right: var(--spacing-base);
441
+ padding: 0 var(--spacing-base);
442
+ margin: 0;
441
443
  }
442
444
  .tag-box .input-container .input input:focus {
443
445
  outline: none;
@@ -24,7 +24,11 @@
24
24
  showCharacterCount = false,
25
25
  maxlength = undefined,
26
26
  minlength = undefined,
27
- pattern = undefined
27
+ pattern = undefined,
28
+ onChange = undefined,
29
+ onInput = undefined,
30
+ onFocus = undefined,
31
+ onBlur = undefined
28
32
  }: {
29
33
  size?: FormFieldSizeOptions;
30
34
  value?: string | null;
@@ -44,6 +48,10 @@
44
48
  maxlength?: number | undefined;
45
49
  minlength?: number | undefined;
46
50
  pattern?: string | undefined;
51
+ onChange?: ((value: string) => void) | undefined;
52
+ onInput?: ((value: string) => void) | undefined;
53
+ onFocus?: ((e: FocusEvent) => void) | undefined;
54
+ onBlur?: ((e: FocusEvent) => void) | undefined;
47
55
  } = $props();
48
56
 
49
57
  let textareaElement: HTMLTextAreaElement | null = $state(null);
@@ -76,6 +84,15 @@
76
84
  textareaElement.style.height = `${newHeight}px`;
77
85
  };
78
86
 
87
+ const handleInput = (e: Event) => {
88
+ handleAutoResize();
89
+ onInput?.(value || '');
90
+ };
91
+
92
+ const handleChange = (e: Event) => {
93
+ onChange?.(value || '');
94
+ };
95
+
79
96
  // Run auto-resize when value changes
80
97
  $effect(() => {
81
98
  if (value !== null && value !== undefined) {
@@ -113,7 +130,10 @@
113
130
  {minlength}
114
131
  aria-busy={isLoading}
115
132
  data-auto-resize={autoResize}
116
- oninput={handleAutoResize}
133
+ oninput={handleInput}
134
+ onchange={handleChange}
135
+ onfocus={onFocus}
136
+ onblur={onBlur}
117
137
  ></textarea>
118
138
  {#if showCharacterCount && maxlength}
119
139
  <div class="character-count {characterLimitClass}">
@@ -19,6 +19,10 @@ type $$ComponentProps = {
19
19
  maxlength?: number | undefined;
20
20
  minlength?: number | undefined;
21
21
  pattern?: string | undefined;
22
+ onChange?: ((value: string) => void) | undefined;
23
+ onInput?: ((value: string) => void) | undefined;
24
+ onFocus?: ((e: FocusEvent) => void) | undefined;
25
+ onBlur?: ((e: FocusEvent) => void) | undefined;
22
26
  };
23
27
  declare const TextArea: import("svelte").Component<$$ComponentProps, {}, "value">;
24
28
  type TextArea = ReturnType<typeof TextArea>;