@warkypublic/svelix 0.1.46 → 0.2.1

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 (129) hide show
  1. package/README.md +93 -33
  2. package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
  3. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
  4. package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
  5. package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
  6. package/dist/components/Boxer/Boxer.svelte +492 -602
  7. package/dist/components/Boxer/BoxerTarget.svelte +6 -46
  8. package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
  9. package/dist/components/Boxer/types.d.ts +0 -1
  10. package/dist/components/Button.svelte +19 -43
  11. package/dist/components/ButtonPreview.svelte +2 -11
  12. package/dist/components/CardGrid/CardGrid.svelte +489 -614
  13. package/dist/components/CardGrid/CardGrid.svelte.d.ts +4 -0
  14. package/dist/components/CardGrid/CardGridFilterPanel.svelte +111 -90
  15. package/dist/components/CardGrid/DefaultCard.svelte +86 -97
  16. package/dist/components/CardGrid/DefaultCard.svelte.d.ts +1 -0
  17. package/dist/components/CardGrid/ImageCardStory.svelte +87 -36
  18. package/dist/components/CardGrid/cardGrid.d.ts +24 -0
  19. package/dist/components/CardGrid/cardGrid.js +20 -0
  20. package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
  21. package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
  22. package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
  23. package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
  24. package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
  25. package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
  26. package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
  27. package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
  28. package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
  29. package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
  30. package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
  31. package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
  32. package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
  33. package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
  34. package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
  35. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
  36. package/dist/components/Former/Former.svelte +306 -424
  37. package/dist/components/Former/FormerButtonArea.svelte +17 -68
  38. package/dist/components/Former/FormerDrawer.svelte +16 -80
  39. package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
  40. package/dist/components/Former/FormerModal.svelte +16 -74
  41. package/dist/components/Former/FormerModalPreview.svelte +99 -89
  42. package/dist/components/Former/FormerPreview.svelte +76 -78
  43. package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
  44. package/dist/components/Former/formerOverlayHelpers.svelte.d.ts +11 -0
  45. package/dist/components/Former/formerOverlayHelpers.svelte.js +38 -0
  46. package/dist/components/Former/index.d.ts +1 -0
  47. package/dist/components/Former/index.js +1 -0
  48. package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -49
  49. package/dist/components/FormerControllers/CtrlFieldShell.svelte +15 -0
  50. package/dist/components/FormerControllers/CtrlFieldShell.svelte.d.ts +12 -0
  51. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +285 -457
  52. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
  53. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
  54. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
  55. package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -47
  56. package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
  57. package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
  58. package/dist/components/FormerControllers/NativeSelectCtrl.svelte +7 -39
  59. package/dist/components/FormerControllers/NumberInputCtrl.svelte +21 -43
  60. package/dist/components/FormerControllers/PasswordInputCtrl.svelte +8 -33
  61. package/dist/components/FormerControllers/SwitchCtrl.svelte +4 -20
  62. package/dist/components/FormerControllers/TextAreaCtrl.svelte +7 -34
  63. package/dist/components/FormerControllers/TextInputCtrl.svelte +7 -34
  64. package/dist/components/FormerControllers/ctrlLoading.svelte.d.ts +8 -0
  65. package/dist/components/FormerControllers/ctrlLoading.svelte.js +43 -0
  66. package/dist/components/FormerControllers/index.d.ts +2 -0
  67. package/dist/components/FormerControllers/index.js +2 -0
  68. package/dist/components/FormerControllers/utils.d.ts +1 -0
  69. package/dist/components/FormerControllers/utils.js +3 -0
  70. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
  71. package/dist/components/Gridler/components/Gridler.svelte +344 -442
  72. package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
  73. package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
  74. package/dist/components/Gridler/components/GridlerFull.svelte +521 -841
  75. package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
  76. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
  77. package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
  78. package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
  79. package/dist/components/Gridler/types.d.ts +1 -1
  80. package/dist/components/Gridler/utils/serverPaging.d.ts +1 -0
  81. package/dist/components/Gridler/utils/serverPaging.js +7 -0
  82. package/dist/components/Icons/IconAdd.svelte +1 -3
  83. package/dist/components/Icons/IconAlertCircle.svelte +1 -3
  84. package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
  85. package/dist/components/Icons/IconCamera.svelte +1 -3
  86. package/dist/components/Icons/IconClose.svelte +1 -3
  87. package/dist/components/Icons/IconEdit.svelte +1 -3
  88. package/dist/components/Icons/IconFilter.svelte +1 -3
  89. package/dist/components/Icons/IconSearch.svelte +1 -3
  90. package/dist/components/Icons/IconSort.svelte +1 -3
  91. package/dist/components/Icons/IconTrash.svelte +1 -3
  92. package/dist/components/Portal/Portal.svelte +14 -38
  93. package/dist/components/Screenshot/Screenshot.svelte +7 -13
  94. package/dist/components/Svark/Svark.svelte +680 -852
  95. package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
  96. package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
  97. package/dist/components/Svark/SvarkPager.svelte +2 -28
  98. package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
  99. package/dist/components/Svark/SvarkTopBar.svelte +9 -74
  100. package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
  101. package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
  102. package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
  103. package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
  104. package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
  105. package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
  106. package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
  107. package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
  108. package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
  109. package/dist/components/VTree/VTree.svelte +454 -431
  110. package/dist/components/VTree/VTree.utils.d.ts +23 -0
  111. package/dist/components/VTree/VTree.utils.js +73 -0
  112. package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
  113. package/dist/components/VTree/VTreeControlledSelectionDemo.svelte +20 -0
  114. package/dist/components/VTree/VTreeControlledSelectionDemo.svelte.d.ts +18 -0
  115. package/dist/components/VTree/VTreeCustomizationDemo.svelte +23 -0
  116. package/dist/components/VTree/VTreeCustomizationDemo.svelte.d.ts +18 -0
  117. package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
  118. package/dist/components/VTree/VTreeInteractionDemo.svelte +29 -0
  119. package/dist/components/VTree/VTreeInteractionDemo.svelte.d.ts +18 -0
  120. package/dist/components/VTree/VTreeMultiSelectDemo.svelte +27 -0
  121. package/dist/components/VTree/VTreeMultiSelectDemo.svelte.d.ts +18 -0
  122. package/dist/components/VTree/VTreeRow.svelte +77 -115
  123. package/dist/components/VTree/VTreeRow.svelte.d.ts +34 -18
  124. package/dist/components/VTree/VTreeSearch.svelte +6 -13
  125. package/dist/components/VTree/VTreeVirtualViewport.svelte +54 -143
  126. package/dist/components/VTree/VTreeVirtualViewport.svelte.d.ts +35 -18
  127. package/dist/components/VTree/types.d.ts +39 -1
  128. package/llm/COMPONENT_GUIDE.md +25 -0
  129. package/package.json +51 -50
@@ -1,59 +1,25 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
-
4
- export interface Props {
5
- children?: Snippet;
6
- disabled?: boolean;
7
- loading?: boolean;
8
- onclick?: () => Promise<void>;
9
- sid?: string;
10
- tooltip?: string;
11
- variant?: 'filled' | 'ghost' | 'outlined';
12
- }
13
-
14
- const {
15
- children,
16
- disabled,
17
- loading: externalLoading = false,
18
- onclick,
19
- sid,
20
- tooltip,
21
- variant = 'ghost',
22
- }: Props = $props();
23
-
24
- let internalLoading = $state(false);
25
- const isLoading = $derived(internalLoading || externalLoading);
26
-
27
- const variantClass = $derived(
28
- variant === 'outlined'
29
- ? 'preset-outlined-primary-500'
30
- : variant === 'filled'
31
- ? 'preset-filled-primary-500'
32
- : 'preset-tonal-primary'
33
- );
34
-
35
- async function handleClick() {
36
- if (onclick && !isLoading) {
37
- internalLoading = true;
38
- try {
39
- await onclick();
40
- } finally {
41
- internalLoading = false;
42
- }
43
- }
44
- }
1
+ <script lang="ts">import { createClickLoading, createClickPulse } from "./ctrlLoading.svelte";
2
+ const { children, disabled, loading: externalLoading = false, onclick, sid, tooltip, variant = "ghost" } = $props();
3
+ const clickLoading = createClickLoading(() => externalLoading);
4
+ const variantClass = $derived(variant === "outlined" ? "preset-outlined-primary-500" : variant === "filled" ? "preset-filled-primary-500" : "preset-tonal-primary");
5
+ const clickPulse = createClickPulse();
6
+ function handleClick() {
7
+ clickPulse.trigger();
8
+ clickLoading.run(onclick);
9
+ }
45
10
  </script>
46
11
 
47
12
  <button
48
13
  aria-label={tooltip ?? sid ?? 'Action'}
49
- class="btn-icon btn-sm {variantClass}"
14
+ class="btn-icon btn-sm {variantClass} transition-transform duration-150"
15
+ class:scale-90={clickPulse.pressed}
50
16
  data-sid={sid}
51
- disabled={disabled || isLoading}
17
+ disabled={disabled || clickLoading.isLoading}
52
18
  title={tooltip}
53
19
  type="button"
54
20
  onclick={handleClick}
55
21
  >
56
- {#if isLoading}
22
+ {#if clickLoading.isLoading}
57
23
  <span class="animate-spin">⟳</span>
58
24
  {:else if children}
59
25
  {@render children()}
@@ -1,44 +1,15 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import IconAlertCircle from '../Icons/IconAlertCircle.svelte';
4
- import IconAlertTriangle from '../Icons/IconAlertTriangle.svelte';
5
-
6
- export interface Props {
7
- children?: Snippet;
8
- error?: string;
9
- label?: string | Snippet;
10
- labelWidth?: string;
11
- leftSection?: Snippet;
12
- promptArea?: Snippet;
13
- required?: boolean;
14
- rightSection?: Snippet;
15
- tooltip?: string;
16
- value?: unknown;
17
- }
18
-
19
- const {
20
- children,
21
- error,
22
- label,
23
- labelWidth = '10rem',
24
- leftSection,
25
- promptArea,
26
- required,
27
- rightSection,
28
- tooltip,
29
- value,
30
- }: Props = $props();
31
-
32
- function isValueEmpty(v: unknown): boolean {
33
- if (v === null || v === undefined) return true;
34
- if (typeof v === 'number') return false;
35
- if (typeof v === 'string') return v.trim() === '';
36
- return false;
37
- }
38
-
39
- const isEmpty = $derived(isValueEmpty(value));
40
- const showErrorIcon = $derived(!!error);
41
- const showWarningIcon = $derived(!error && !!required && isEmpty);
1
+ <script lang="ts">import IconAlertCircle from "../Icons/IconAlertCircle.svelte";
2
+ import IconAlertTriangle from "../Icons/IconAlertTriangle.svelte";
3
+ const { children, error, label, labelWidth = "10rem", leftSection, promptArea, required, rightSection, tooltip, value } = $props();
4
+ function isValueEmpty(v) {
5
+ if (v === null || v === undefined) return true;
6
+ if (typeof v === "number") return false;
7
+ if (typeof v === "string") return v.trim() === "";
8
+ return false;
9
+ }
10
+ const isEmpty = $derived(isValueEmpty(value));
11
+ const showErrorIcon = $derived(!!error);
12
+ const showWarningIcon = $derived(!error && !!required && isEmpty);
42
13
  </script>
43
14
 
44
15
  <div class="flex flex-col gap-0" title={tooltip}>
@@ -1,29 +1,7 @@
1
- <script lang="ts">
2
- import InlineWrapper from './InlineWrapper.svelte';
3
- import TextInputCtrl from './TextInputCtrl.svelte';
4
- import ButtonCtrl from './ButtonCtrl.svelte';
5
-
6
- export interface Props {
7
- error?: string;
8
- label?: string;
9
- labelWidth?: string;
10
- required?: boolean;
11
- tooltip?: string;
12
- value?: string;
13
- withLeftSection?: boolean;
14
- withRightSection?: boolean;
15
- }
16
-
17
- let {
18
- error,
19
- label = 'Username',
20
- labelWidth,
21
- required,
22
- tooltip,
23
- value = $bindable(''),
24
- withLeftSection = false,
25
- withRightSection = false,
26
- }: Props = $props();
1
+ <script lang="ts">import InlineWrapper from "./InlineWrapper.svelte";
2
+ import TextInputCtrl from "./TextInputCtrl.svelte";
3
+ import ButtonCtrl from "./ButtonCtrl.svelte";
4
+ let { error, label = "Username", labelWidth, required, tooltip, value = $bindable(""), withLeftSection = false, withRightSection = false } = $props();
27
5
  </script>
28
6
 
29
7
  {#snippet leftSection()}
@@ -1,43 +1,14 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- interface SelectOption {
5
- label: string;
6
- value: string;
7
- }
8
-
9
- export interface Props extends CtrlBaseProps {
10
- onchange?: (value: string) => void;
11
- options?: Array<SelectOption | string>;
12
- value?: string;
13
- }
14
-
15
- let {
16
- disabled,
17
- error,
18
- label,
19
- name,
20
- onchange,
21
- options = [],
22
- placeholder,
23
- required,
24
- sid,
25
- tooltip,
26
- value = $bindable(''),
27
- }: Props = $props();
28
-
29
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { disabled, error, label, name, onchange, options = [], placeholder, required, sid, tooltip, value = $bindable("") } = $props();
4
+ const id = $derived(makeFieldId(name, sid));
30
5
  </script>
31
6
 
32
- <div class="flex flex-col gap-1" title={tooltip}>
33
- {#if label}
34
- <label class="label text-sm font-medium" for={id}>
35
- {label}{#if required}<span class="text-error-500">*</span>{/if}
36
- </label>
37
- {/if}
7
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
38
8
  <select
39
9
  {id}
40
10
  {disabled}
11
+ {name}
41
12
  {required}
42
13
  bind:value
43
14
  class="select"
@@ -55,7 +26,4 @@
55
26
  {/if}
56
27
  {/each}
57
28
  </select>
58
- {#if error}
59
- <p class="text-error-500 text-xs">{error}</p>
60
- {/if}
61
- </div>
29
+ </CtrlFieldShell>
@@ -1,51 +1,32 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- export interface Props extends CtrlBaseProps {
5
- max?: number;
6
- min?: number;
7
- onchange?: (value: number | undefined) => void;
8
- step?: number;
9
- value?: number | undefined;
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { disabled, error, label, max, min, name, onchange, placeholder, required, sid, step, tooltip, value = $bindable(undefined) } = $props();
4
+ const id = $derived(makeFieldId(name, sid));
5
+ function handleInput(e) {
6
+ const target = e.target;
7
+ const raw = target.value;
8
+ if (raw === "") {
9
+ value = undefined;
10
+ onchange?.(undefined);
11
+ return;
10
12
  }
11
-
12
- let {
13
- disabled,
14
- error,
15
- label,
16
- max,
17
- min,
18
- name,
19
- onchange,
20
- placeholder,
21
- required,
22
- sid,
23
- step,
24
- tooltip,
25
- value = $bindable(undefined),
26
- }: Props = $props();
27
-
28
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
29
-
30
- function handleInput(e: Event) {
31
- const target = e.target as HTMLInputElement;
32
- const parsed = target.value !== '' ? Number(target.value) : undefined;
33
- value = parsed;
34
- onchange?.(parsed);
13
+ const parsed = Number(raw);
14
+ // Intermediate typed states (e.g. "-", "1e") parse to NaN; ignore until valid.
15
+ if (Number.isNaN(parsed)) {
16
+ return;
35
17
  }
18
+ value = parsed;
19
+ onchange?.(parsed);
20
+ }
36
21
  </script>
37
22
 
38
- <div class="flex flex-col gap-1" title={tooltip}>
39
- {#if label}
40
- <label class="label text-sm font-medium" for={id}>
41
- {label}{#if required}<span class="text-error-500">*</span>{/if}
42
- </label>
43
- {/if}
23
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
44
24
  <input
45
25
  {id}
46
26
  {disabled}
47
27
  {max}
48
28
  {min}
29
+ {name}
49
30
  {placeholder}
50
31
  {required}
51
32
  {step}
@@ -55,7 +36,4 @@
55
36
  value={value ?? ''}
56
37
  oninput={handleInput}
57
38
  />
58
- {#if error}
59
- <p class="text-error-500 text-xs">{error}</p>
60
- {/if}
61
- </div>
39
+ </CtrlFieldShell>
@@ -1,38 +1,16 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- export interface Props extends CtrlBaseProps {
5
- onchange?: (value: string) => void;
6
- value?: string;
7
- }
8
-
9
- let {
10
- disabled,
11
- error,
12
- label,
13
- name,
14
- onchange,
15
- placeholder,
16
- required,
17
- sid,
18
- tooltip,
19
- value = $bindable(''),
20
- }: Props = $props();
21
-
22
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
23
- let showPassword = $state(false);
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { disabled, error, label, name, onchange, placeholder, required, sid, tooltip, value = $bindable("") } = $props();
4
+ const id = $derived(makeFieldId(name, sid));
5
+ let showPassword = $state(false);
24
6
  </script>
25
7
 
26
- <div class="flex flex-col gap-1" title={tooltip}>
27
- {#if label}
28
- <label class="label text-sm font-medium" for={id}>
29
- {label}{#if required}<span class="text-error-500">*</span>{/if}
30
- </label>
31
- {/if}
8
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
32
9
  <div class="flex gap-0">
33
10
  <input
34
11
  {id}
35
12
  {disabled}
13
+ {name}
36
14
  {placeholder}
37
15
  {required}
38
16
  bind:value
@@ -51,7 +29,4 @@
51
29
  {showPassword ? '🙈' : '👁'}
52
30
  </button>
53
31
  </div>
54
- {#if error}
55
- <p class="text-error-500 text-xs">{error}</p>
56
- {/if}
57
- </div>
32
+ </CtrlFieldShell>
@@ -1,29 +1,13 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- export interface Props extends Omit<CtrlBaseProps, 'placeholder'> {
5
- checked?: boolean;
6
- onchange?: (checked: boolean) => void;
7
- }
8
-
9
- let {
10
- checked = $bindable(false),
11
- disabled,
12
- error,
13
- label,
14
- name,
15
- onchange,
16
- sid,
17
- tooltip,
18
- }: Props = $props();
19
-
20
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
1
+ <script lang="ts">import { makeFieldId } from "./utils";
2
+ let { checked = $bindable(false), disabled, error, label, name, onchange, sid, tooltip } = $props();
3
+ const id = $derived(makeFieldId(name, sid));
21
4
  </script>
22
5
 
23
6
  <div class="flex items-center gap-2" title={tooltip}>
24
7
  <input
25
8
  {id}
26
9
  {disabled}
10
+ {name}
27
11
  bind:checked
28
12
  class="checkbox"
29
13
  type="checkbox"
@@ -1,38 +1,14 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- export interface Props extends CtrlBaseProps {
5
- onchange?: (value: string) => void;
6
- rows?: number;
7
- value?: string;
8
- }
9
-
10
- let {
11
- disabled,
12
- error,
13
- label,
14
- name,
15
- onchange,
16
- placeholder,
17
- required,
18
- rows = 4,
19
- sid,
20
- tooltip,
21
- value = $bindable(''),
22
- }: Props = $props();
23
-
24
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { disabled, error, label, name, onchange, placeholder, required, rows = 4, sid, tooltip, value = $bindable("") } = $props();
4
+ const id = $derived(makeFieldId(name, sid));
25
5
  </script>
26
6
 
27
- <div class="flex flex-col gap-1" title={tooltip}>
28
- {#if label}
29
- <label class="label text-sm font-medium" for={id}>
30
- {label}{#if required}<span class="text-error-500">*</span>{/if}
31
- </label>
32
- {/if}
7
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
33
8
  <textarea
34
9
  {id}
35
10
  {disabled}
11
+ {name}
36
12
  {placeholder}
37
13
  {required}
38
14
  {rows}
@@ -41,7 +17,4 @@
41
17
  class:textarea-error={!!error}
42
18
  oninput={() => onchange?.(value)}
43
19
  ></textarea>
44
- {#if error}
45
- <p class="text-error-500 text-xs">{error}</p>
46
- {/if}
47
- </div>
20
+ </CtrlFieldShell>
@@ -1,38 +1,14 @@
1
- <script lang="ts">
2
- import type { CtrlBaseProps } from './types';
3
-
4
- export interface Props extends CtrlBaseProps {
5
- onchange?: (value: string) => void;
6
- type?: string;
7
- value?: string;
8
- }
9
-
10
- let {
11
- disabled,
12
- error,
13
- label,
14
- name,
15
- onchange,
16
- placeholder,
17
- required,
18
- sid,
19
- tooltip,
20
- type = 'text',
21
- value = $bindable(''),
22
- }: Props = $props();
23
-
24
- const id = $derived(`field_${name ?? ''}_${sid ?? ''}`);
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { disabled, error, label, name, onchange, placeholder, required, sid, tooltip, type = "text", value = $bindable("") } = $props();
4
+ const id = $derived(makeFieldId(name, sid));
25
5
  </script>
26
6
 
27
- <div class="flex flex-col gap-1" title={tooltip}>
28
- {#if label}
29
- <label class="label text-sm font-medium" for={id}>
30
- {label}{#if required}<span class="text-error-500">*</span>{/if}
31
- </label>
32
- {/if}
7
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
33
8
  <input
34
9
  {id}
35
10
  {disabled}
11
+ {name}
36
12
  {placeholder}
37
13
  {required}
38
14
  {type}
@@ -41,7 +17,4 @@
41
17
  class:input-error={!!error}
42
18
  oninput={() => onchange?.(value)}
43
19
  />
44
- {#if error}
45
- <p class="text-error-500 text-xs">{error}</p>
46
- {/if}
47
- </div>
20
+ </CtrlFieldShell>
@@ -0,0 +1,8 @@
1
+ export declare function createClickLoading(getExternalLoading: () => boolean | undefined): {
2
+ readonly isLoading: boolean;
3
+ run: (action?: () => Promise<void>) => Promise<void>;
4
+ };
5
+ export declare function createClickPulse(durationMs?: number): {
6
+ readonly pressed: boolean;
7
+ trigger: () => void;
8
+ };
@@ -0,0 +1,43 @@
1
+ export function createClickLoading(getExternalLoading) {
2
+ let internalLoading = $state(false);
3
+ const isLoading = $derived(internalLoading || (getExternalLoading() ?? false));
4
+ async function run(action) {
5
+ if (!action || isLoading)
6
+ return;
7
+ internalLoading = true;
8
+ try {
9
+ await action();
10
+ }
11
+ finally {
12
+ internalLoading = false;
13
+ }
14
+ }
15
+ return {
16
+ get isLoading() {
17
+ return isLoading;
18
+ },
19
+ run,
20
+ };
21
+ }
22
+ // Guarantees a visible "click" pulse even when the action resolves faster than
23
+ // a human can perceive the loading spinner — decoupled from actual duration.
24
+ export function createClickPulse(durationMs = 200) {
25
+ let pressed = $state(false);
26
+ let timeout;
27
+ function trigger() {
28
+ pressed = true;
29
+ clearTimeout(timeout);
30
+ timeout = setTimeout(() => {
31
+ pressed = false;
32
+ }, durationMs);
33
+ }
34
+ $effect(() => {
35
+ return () => clearTimeout(timeout);
36
+ });
37
+ return {
38
+ get pressed() {
39
+ return pressed;
40
+ },
41
+ trigger,
42
+ };
43
+ }
@@ -1,4 +1,5 @@
1
1
  export { default as ButtonCtrl } from './ButtonCtrl.svelte';
2
+ export { default as CtrlFieldShell } from './CtrlFieldShell.svelte';
2
3
  export { DateTimeCtrl } from './DateTimeCtrl/index';
3
4
  export { default as IconButtonCtrl } from './IconButtonCtrl.svelte';
4
5
  export { default as InlineWrapper } from './InlineWrapper.svelte';
@@ -8,5 +9,6 @@ export { default as PasswordInputCtrl } from './PasswordInputCtrl.svelte';
8
9
  export { default as SwitchCtrl } from './SwitchCtrl.svelte';
9
10
  export { default as TextAreaCtrl } from './TextAreaCtrl.svelte';
10
11
  export { default as TextInputCtrl } from './TextInputCtrl.svelte';
12
+ export { makeFieldId } from './utils';
11
13
  export type { DateTimeConstraints, DateTimeMode, DateTimeParts } from './DateTimeCtrl/DateTimeCtrl.utils';
12
14
  export * from './types';
@@ -1,4 +1,5 @@
1
1
  export { default as ButtonCtrl } from './ButtonCtrl.svelte';
2
+ export { default as CtrlFieldShell } from './CtrlFieldShell.svelte';
2
3
  export { DateTimeCtrl } from './DateTimeCtrl/index';
3
4
  export { default as IconButtonCtrl } from './IconButtonCtrl.svelte';
4
5
  export { default as InlineWrapper } from './InlineWrapper.svelte';
@@ -8,4 +9,5 @@ export { default as PasswordInputCtrl } from './PasswordInputCtrl.svelte';
8
9
  export { default as SwitchCtrl } from './SwitchCtrl.svelte';
9
10
  export { default as TextAreaCtrl } from './TextAreaCtrl.svelte';
10
11
  export { default as TextInputCtrl } from './TextInputCtrl.svelte';
12
+ export { makeFieldId } from './utils';
11
13
  export * from './types';
@@ -0,0 +1 @@
1
+ export declare function makeFieldId(name?: string, sid?: string): string;
@@ -0,0 +1,3 @@
1
+ export function makeFieldId(name, sid) {
2
+ return `field_${name ?? ''}_${sid ?? ''}`;
3
+ }