@spaethtech/svelte-ui 0.1.10

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 (116) hide show
  1. package/README.md +81 -0
  2. package/dist/components/Badge/Badge.svelte +80 -0
  3. package/dist/components/Badge/Badge.svelte.d.ts +14 -0
  4. package/dist/components/Badge/index.d.ts +1 -0
  5. package/dist/components/Badge/index.js +1 -0
  6. package/dist/components/Button/Button.svelte +172 -0
  7. package/dist/components/Button/Button.svelte.d.ts +32 -0
  8. package/dist/components/Button/index.d.ts +1 -0
  9. package/dist/components/Button/index.js +1 -0
  10. package/dist/components/Dialog/Dialog.svelte +101 -0
  11. package/dist/components/Dialog/Dialog.svelte.d.ts +18 -0
  12. package/dist/components/Dialog/index.d.ts +1 -0
  13. package/dist/components/Dialog/index.js +1 -0
  14. package/dist/components/ElementManager/ElementManager.svelte +397 -0
  15. package/dist/components/ElementManager/ElementManager.svelte.d.ts +43 -0
  16. package/dist/components/ElementManager/index.d.ts +1 -0
  17. package/dist/components/ElementManager/index.js +1 -0
  18. package/dist/components/EmailInput/EmailInput.svelte +47 -0
  19. package/dist/components/EmailInput/EmailInput.svelte.d.ts +14 -0
  20. package/dist/components/EmailInput/index.d.ts +1 -0
  21. package/dist/components/EmailInput/index.js +1 -0
  22. package/dist/components/Icon/Icon.svelte +74 -0
  23. package/dist/components/Icon/Icon.svelte.d.ts +13 -0
  24. package/dist/components/Icon/index.d.ts +1 -0
  25. package/dist/components/Icon/index.js +1 -0
  26. package/dist/components/Input/Input.svelte +268 -0
  27. package/dist/components/Input/Input.svelte.d.ts +18 -0
  28. package/dist/components/Input/index.d.ts +1 -0
  29. package/dist/components/Input/index.js +1 -0
  30. package/dist/components/List/List.svelte +580 -0
  31. package/dist/components/List/List.svelte.d.ts +38 -0
  32. package/dist/components/List/index.d.ts +1 -0
  33. package/dist/components/List/index.js +1 -0
  34. package/dist/components/ListItem/ListItem.svelte +175 -0
  35. package/dist/components/ListItem/ListItem.svelte.d.ts +24 -0
  36. package/dist/components/ListItem/index.d.ts +2 -0
  37. package/dist/components/ListItem/index.js +2 -0
  38. package/dist/components/ListView/ListView.svelte +463 -0
  39. package/dist/components/ListView/ListView.svelte.d.ts +37 -0
  40. package/dist/components/ListView/index.d.ts +2 -0
  41. package/dist/components/ListView/index.js +2 -0
  42. package/dist/components/NodeGraph/BaseNode.d.ts +57 -0
  43. package/dist/components/NodeGraph/BaseNode.js +30 -0
  44. package/dist/components/NodeGraph/Edge.svelte +60 -0
  45. package/dist/components/NodeGraph/Edge.svelte.d.ts +16 -0
  46. package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +82 -0
  47. package/dist/components/NodeGraph/ExpressionEvaluator.js +152 -0
  48. package/dist/components/NodeGraph/Node.svelte +100 -0
  49. package/dist/components/NodeGraph/Node.svelte.d.ts +10 -0
  50. package/dist/components/NodeGraph/NodeGraph.svelte +52 -0
  51. package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +14 -0
  52. package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +80 -0
  53. package/dist/components/NodeGraph/NodeInstance.svelte.js +241 -0
  54. package/dist/components/NodeGraph/NodePort.svelte +75 -0
  55. package/dist/components/NodeGraph/NodePort.svelte.d.ts +11 -0
  56. package/dist/components/NodeGraph/decorators.d.ts +81 -0
  57. package/dist/components/NodeGraph/decorators.js +148 -0
  58. package/dist/components/NodeGraph/index.d.ts +9 -0
  59. package/dist/components/NodeGraph/index.js +9 -0
  60. package/dist/components/NodeGraph/types.d.ts +94 -0
  61. package/dist/components/NodeGraph/types.js +33 -0
  62. package/dist/components/NotesEditor/NotesEditor.svelte +203 -0
  63. package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +9 -0
  64. package/dist/components/NotesEditor/index.d.ts +1 -0
  65. package/dist/components/NotesEditor/index.js +1 -0
  66. package/dist/components/NumberInput/NumberInput.svelte +353 -0
  67. package/dist/components/NumberInput/NumberInput.svelte.d.ts +26 -0
  68. package/dist/components/NumberInput/index.d.ts +1 -0
  69. package/dist/components/NumberInput/index.js +1 -0
  70. package/dist/components/PasswordInput/PasswordInput.svelte +41 -0
  71. package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +13 -0
  72. package/dist/components/PasswordInput/index.d.ts +1 -0
  73. package/dist/components/PasswordInput/index.js +1 -0
  74. package/dist/components/Popup/index.d.ts +1 -0
  75. package/dist/components/Popup/index.js +1 -0
  76. package/dist/components/Rating/Rating.svelte +77 -0
  77. package/dist/components/Rating/Rating.svelte.d.ts +7 -0
  78. package/dist/components/Rating/index.d.ts +1 -0
  79. package/dist/components/Rating/index.js +1 -0
  80. package/dist/components/ScrollView/ScrollView.svelte +285 -0
  81. package/dist/components/ScrollView/ScrollView.svelte.d.ts +19 -0
  82. package/dist/components/ScrollView/index.d.ts +1 -0
  83. package/dist/components/ScrollView/index.js +1 -0
  84. package/dist/components/SearchInput/SearchInput.svelte +50 -0
  85. package/dist/components/SearchInput/SearchInput.svelte.d.ts +15 -0
  86. package/dist/components/SearchInput/index.d.ts +1 -0
  87. package/dist/components/SearchInput/index.js +1 -0
  88. package/dist/components/Select/Select.svelte +278 -0
  89. package/dist/components/Select/Select.svelte.d.ts +35 -0
  90. package/dist/components/Select/index.d.ts +1 -0
  91. package/dist/components/Select/index.js +1 -0
  92. package/dist/components/TextArea/TextArea.svelte +734 -0
  93. package/dist/components/TextArea/TextArea.svelte.d.ts +19 -0
  94. package/dist/components/TextArea/index.d.ts +1 -0
  95. package/dist/components/TextArea/index.js +1 -0
  96. package/dist/components/ThemeSelector/ThemeSelector.svelte +64 -0
  97. package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +3 -0
  98. package/dist/components/ThemeSelector/index.d.ts +1 -0
  99. package/dist/components/ThemeSelector/index.js +1 -0
  100. package/dist/components/TooltipHandler/TooltipHandler.svelte +593 -0
  101. package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +10 -0
  102. package/dist/components/TooltipHandler/index.d.ts +1 -0
  103. package/dist/components/TooltipHandler/index.js +1 -0
  104. package/dist/index.d.ts +24 -0
  105. package/dist/index.js +26 -0
  106. package/dist/styles/sizes.d.ts +78 -0
  107. package/dist/styles/sizes.js +120 -0
  108. package/dist/styles/variants.d.ts +106 -0
  109. package/dist/styles/variants.js +194 -0
  110. package/dist/types/ManagerTypes.d.ts +42 -0
  111. package/dist/types/ManagerTypes.js +1 -0
  112. package/dist/types/sizes.d.ts +5 -0
  113. package/dist/types/sizes.js +1 -0
  114. package/dist/types/variants.d.ts +1 -0
  115. package/dist/types/variants.js +1 -0
  116. package/package.json +65 -0
@@ -0,0 +1,26 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value'> {
3
+ value: number | null;
4
+ class?: string;
5
+ inputClass?: string;
6
+ validate?: (value: number | null) => boolean | string;
7
+ valid?: boolean;
8
+ touched?: boolean;
9
+ element?: HTMLInputElement;
10
+ min?: number;
11
+ max?: number;
12
+ step?: number;
13
+ allowDecimals?: boolean;
14
+ decimalPlaces?: number;
15
+ thousandsSeparator?: boolean;
16
+ prefix?: string;
17
+ suffix?: string;
18
+ currency?: string;
19
+ decimalSeparator?: string;
20
+ thousandsChar?: string;
21
+ roundingMode?: 'round' | 'floor' | 'ceil' | 'trunc';
22
+ negativeMode?: 'minus' | 'parentheses' | 'both';
23
+ }
24
+ declare const NumberInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
25
+ type NumberInput = ReturnType<typeof NumberInput>;
26
+ export default NumberInput;
@@ -0,0 +1 @@
1
+ export { default } from './NumberInput.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './NumberInput.svelte';
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import Input from '../Input/Input.svelte';
3
+ import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
4
+ import type { HTMLInputAttributes } from 'svelte/elements';
5
+
6
+ interface Props extends Omit<HTMLInputAttributes, 'type'> {
7
+ value: string;
8
+ class?: string;
9
+ inputClass?: string;
10
+ validate?: (value: string) => boolean | string;
11
+ valid?: boolean;
12
+ touched?: boolean;
13
+ element?: HTMLInputElement;
14
+ }
15
+
16
+ let {
17
+ value = $bindable(),
18
+ valid = $bindable(true),
19
+ touched = $bindable(false),
20
+ element = $bindable(),
21
+ ...restProps
22
+ }: Props = $props();
23
+
24
+ let showPassword = $state(false);
25
+
26
+ function toggleVisibility() {
27
+ showPassword = !showPassword;
28
+ }
29
+ </script>
30
+
31
+ <Input
32
+ bind:value
33
+ bind:valid
34
+ bind:touched
35
+ bind:element
36
+ type={showPassword ? 'text' : 'password'}
37
+ icon={showPassword ? faEye : faEyeSlash}
38
+ iconClickable={true}
39
+ onIconClick={toggleVisibility}
40
+ {...restProps}
41
+ />
@@ -0,0 +1,13 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ interface Props extends Omit<HTMLInputAttributes, 'type'> {
3
+ value: string;
4
+ class?: string;
5
+ inputClass?: string;
6
+ validate?: (value: string) => boolean | string;
7
+ valid?: boolean;
8
+ touched?: boolean;
9
+ element?: HTMLInputElement;
10
+ }
11
+ declare const PasswordInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
12
+ type PasswordInput = ReturnType<typeof PasswordInput>;
13
+ export default PasswordInput;
@@ -0,0 +1 @@
1
+ export { default } from './PasswordInput.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './PasswordInput.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './Popup.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './Popup.svelte';
@@ -0,0 +1,77 @@
1
+ <script lang="ts">
2
+ export type RatingProps = {
3
+ average: number;
4
+ count: number;
5
+ };
6
+
7
+ let { average, count }: RatingProps = $props();
8
+
9
+ // Validate and sanitize the average rating
10
+ const validAverage =
11
+ isNaN(average) || average < 0 ? 0 : Math.min(average, 10);
12
+
13
+ // Convert TMDB 10-point scale to 5-star scale
14
+ const fiveStarRating = validAverage / 2;
15
+
16
+ // Calculate how many stars should be filled (ensure they're valid numbers)
17
+ const filledStars = Math.max(0, Math.min(5, Math.floor(fiveStarRating)));
18
+ const hasHalfStar = fiveStarRating % 1 >= 0.5;
19
+ const emptyStars = Math.max(0, 5 - filledStars - (hasHalfStar ? 1 : 0));
20
+ </script>
21
+
22
+ <div class="rating select-none">
23
+ <div class="stars">
24
+ {#each Array(filledStars) as _, i}
25
+ <span class="star filled">★</span>
26
+ {/each}
27
+ {#if hasHalfStar}
28
+ <span class="star half-filled">☆</span>
29
+ {/if}
30
+ {#each Array(emptyStars) as _, i}
31
+ <span class="star empty">☆</span>
32
+ {/each}
33
+ </div>
34
+ <span class="rating-value">{fiveStarRating.toFixed(1)}</span>
35
+ <span class="rating-count">({count})</span>
36
+ </div>
37
+
38
+ <style>
39
+ .rating {
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 0.5rem;
43
+ }
44
+
45
+ .stars {
46
+ display: flex;
47
+ gap: 0.1rem;
48
+ }
49
+
50
+ .star {
51
+ font-size: 1.2rem;
52
+ line-height: 1;
53
+ }
54
+
55
+ .star.filled {
56
+ color: #ffd700;
57
+ }
58
+
59
+ .star.half-filled {
60
+ color: #ffd700;
61
+ opacity: 0.6;
62
+ }
63
+
64
+ .star.empty {
65
+ color: var(--color-secondary);
66
+ }
67
+
68
+ .rating-value {
69
+ font-weight: 600;
70
+ color: var(--color-text);
71
+ }
72
+
73
+ .rating-count {
74
+ font-size: 0.9rem;
75
+ color: var(--color-secondary);
76
+ }
77
+ </style>
@@ -0,0 +1,7 @@
1
+ export type RatingProps = {
2
+ average: number;
3
+ count: number;
4
+ };
5
+ declare const Rating: import("svelte").Component<RatingProps, {}, "">;
6
+ type Rating = ReturnType<typeof Rating>;
7
+ export default Rating;
@@ -0,0 +1 @@
1
+ export { default } from './Rating.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './Rating.svelte';
@@ -0,0 +1,285 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ interface Props extends HTMLAttributes<HTMLDivElement> {
6
+ showScrollbar?: boolean;
7
+ scrollbarWidth?: number;
8
+ class?: string;
9
+ disabled?: boolean;
10
+ children: Snippet;
11
+ view?: HTMLDivElement;
12
+ onScroll?: (data: {
13
+ scrollTop: number;
14
+ thumbTop: number;
15
+ scrollHeight: number;
16
+ clientHeight: number;
17
+ }) => void;
18
+ }
19
+
20
+ let {
21
+ showScrollbar = true,
22
+ scrollbarWidth = 6,
23
+ class: className = "",
24
+ style = "",
25
+ disabled = false,
26
+ children,
27
+ view = $bindable(),
28
+ onScroll,
29
+ ...restProps
30
+ }: Props = $props();
31
+
32
+ let isDragging = $state(false);
33
+ let dragStartY = $state(0);
34
+ let dragStartScrollTop = $state(0);
35
+ let scrollTop = $state(0);
36
+ let previousScrollTop = $state(0);
37
+ let scrollHeight = $state(0);
38
+ let clientHeight = $state(0);
39
+ let isScrolling = $state(false);
40
+ let isHovering = $state(false);
41
+ let scrollTimeout = $state<ReturnType<typeof setTimeout>>();
42
+
43
+ // Inset from edges (0.25rem = 4px)
44
+ const inset = 4;
45
+
46
+ // Generate unique ID for aria-controls if not provided
47
+ const scrollableId =
48
+ restProps.id ?? `scrollview-${Math.random().toString(36).substr(2, 9)}`;
49
+
50
+ // Split classes: height/sizing/border/rounded classes go to wrapper, everything else to view
51
+ const wrapperClasses = $derived(
52
+ className
53
+ .match(
54
+ /\b(h-|min-h-|max-h-|flex-\d|flex-grow|flex-shrink|flex-none|flex-auto|basis-|border|rounded)\S*/g,
55
+ )
56
+ ?.join(" ") || "",
57
+ );
58
+ const viewClasses = $derived(
59
+ className
60
+ .split(/\s+/)
61
+ .filter((cls) => cls && !wrapperClasses.includes(cls))
62
+ .join(" "),
63
+ );
64
+
65
+ // Initialize dimensions when view is mounted
66
+ $effect(() => {
67
+ if (view) {
68
+ scrollTop = view.scrollTop;
69
+ scrollHeight = view.scrollHeight;
70
+ clientHeight = view.clientHeight;
71
+
72
+ // Fire initial callback if provided
73
+ if (onScroll) {
74
+ onScroll({ scrollTop, thumbTop, scrollHeight, clientHeight });
75
+ }
76
+ }
77
+ });
78
+
79
+ // Calculate thumb height based on view dimensions
80
+ const thumbHeight = $derived.by(() => {
81
+ if (scrollHeight === 0 || clientHeight === 0) return 20;
82
+ const visibleRatio = clientHeight / scrollHeight;
83
+ return Math.max(
84
+ 20,
85
+ Math.min(clientHeight - inset * 2, visibleRatio * clientHeight),
86
+ );
87
+ });
88
+
89
+ // Calculate thumb position based on scroll position
90
+ const thumbTop = $derived.by(() => {
91
+ if (scrollHeight === 0 || clientHeight === 0) return inset;
92
+ const maxScrollTop = scrollHeight - clientHeight;
93
+ if (maxScrollTop <= 0) return inset;
94
+ const scrollRatio = scrollTop / maxScrollTop;
95
+ const trackHeight = clientHeight - thumbHeight - inset * 2;
96
+ return inset + scrollRatio * trackHeight;
97
+ });
98
+
99
+ // Check if scrollbar is needed
100
+ const needsScrollbar = $derived(
101
+ showScrollbar &&
102
+ !disabled &&
103
+ scrollHeight > 0 &&
104
+ clientHeight > 0 &&
105
+ scrollHeight > clientHeight,
106
+ );
107
+
108
+ // Calculate scroll percentage for aria-valuenow
109
+ const scrollPercentage = $derived(() => {
110
+ if (!view) return 0;
111
+ const maxScrollTop = view.scrollHeight - view.clientHeight;
112
+ if (maxScrollTop <= 0) return 0;
113
+ return Math.round((scrollTop / maxScrollTop) * 100);
114
+ });
115
+
116
+ // Calculate opacity based on state
117
+ const thumbOpacity = $derived(isScrolling || isHovering ? "80%" : "30%");
118
+
119
+ // Handle scrollbar thumb mouse down
120
+ function handleThumbMouseDown(event: MouseEvent) {
121
+ if (!view) return;
122
+ event.preventDefault();
123
+ isDragging = true;
124
+ dragStartY = event.clientY;
125
+ dragStartScrollTop = view.scrollTop;
126
+
127
+ document.addEventListener("mousemove", handleThumbMouseMove);
128
+ document.addEventListener("mouseup", handleThumbMouseUp);
129
+ }
130
+
131
+ // Handle scrollbar thumb mouse move
132
+ function handleThumbMouseMove(event: MouseEvent) {
133
+ if (!isDragging || !view) return;
134
+
135
+ const deltaY = event.clientY - dragStartY;
136
+ const scrollRatio = deltaY / (view.clientHeight - thumbHeight);
137
+ const maxScrollTop = view.scrollHeight - view.clientHeight;
138
+
139
+ view.scrollTop = Math.max(
140
+ 0,
141
+ Math.min(
142
+ maxScrollTop,
143
+ dragStartScrollTop + scrollRatio * maxScrollTop,
144
+ ),
145
+ );
146
+ }
147
+
148
+ // Handle scrollbar thumb mouse up
149
+ function handleThumbMouseUp() {
150
+ isDragging = false;
151
+ document.removeEventListener("mousemove", handleThumbMouseMove);
152
+ document.removeEventListener("mouseup", handleThumbMouseUp);
153
+ }
154
+
155
+ // Handle keyboard events - prevent scroll chaining while preserving smooth scrolling
156
+ function handleKeyDown(event: KeyboardEvent) {
157
+ if (disabled) return;
158
+
159
+ // Only handle if this ScrollView has focus (not a child element)
160
+ if (event.target !== view) return;
161
+
162
+ // Check for scroll keys
163
+ const scrollKeys = [
164
+ "ArrowDown",
165
+ "ArrowUp",
166
+ "PageDown",
167
+ "PageUp",
168
+ "Home",
169
+ "End",
170
+ " ",
171
+ ];
172
+ if (!scrollKeys.includes(event.key)) return;
173
+
174
+ if (!view) return;
175
+
176
+ // ALWAYS prevent default and stop propagation to have full control
177
+ event.preventDefault();
178
+ event.stopPropagation();
179
+
180
+ // Calculate scroll amount based on key
181
+ const {
182
+ clientHeight: currentClientHeight,
183
+ scrollTop: currentScrollTop,
184
+ scrollHeight: currentScrollHeight,
185
+ } = view;
186
+ let scrollAmount = 0;
187
+
188
+ if (event.key === "ArrowDown") scrollAmount = 40;
189
+ else if (event.key === "ArrowUp") scrollAmount = -40;
190
+ else if (event.key === "PageDown")
191
+ scrollAmount = currentClientHeight * 0.9;
192
+ else if (event.key === "PageUp")
193
+ scrollAmount = -currentClientHeight * 0.9;
194
+ else if (event.key === " ")
195
+ scrollAmount = event.shiftKey
196
+ ? -currentClientHeight * 0.9
197
+ : currentClientHeight * 0.9;
198
+ else if (event.key === "Home") scrollAmount = -currentScrollTop;
199
+ else if (event.key === "End")
200
+ scrollAmount =
201
+ currentScrollHeight - currentClientHeight - currentScrollTop;
202
+
203
+ // Apply scroll instantly (smooth behavior feels laggy with keyboard)
204
+ view.scrollBy({ top: scrollAmount, behavior: "auto" });
205
+ }
206
+
207
+ // Handle scroll and update state
208
+ function handleScroll() {
209
+ if (!view) return;
210
+
211
+ // Update scroll state
212
+ previousScrollTop = scrollTop;
213
+ scrollTop = view.scrollTop;
214
+ scrollHeight = view.scrollHeight;
215
+ clientHeight = view.clientHeight;
216
+
217
+ // Show scrollbar during scrolling
218
+ isScrolling = true;
219
+
220
+ // Clear previous timeout
221
+ if (scrollTimeout) {
222
+ clearTimeout(scrollTimeout);
223
+ }
224
+
225
+ // Hide scrollbar after scrolling stops (500ms delay)
226
+ scrollTimeout = setTimeout(() => {
227
+ isScrolling = false;
228
+ }, 500);
229
+
230
+ // Fire onScroll callback
231
+ if (onScroll) {
232
+ onScroll({ scrollTop, thumbTop, scrollHeight, clientHeight });
233
+ }
234
+ }
235
+ </script>
236
+
237
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
238
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
239
+ <div
240
+ class="relative overflow-hidden p-0 flex-1 min-h-8 {wrapperClasses}"
241
+ {style}
242
+ {...restProps}
243
+ >
244
+ <div
245
+ bind:this={view}
246
+ id={scrollableId}
247
+ class="scrollview absolute inset-0 {disabled
248
+ ? 'overflow-y-hidden'
249
+ : 'overflow-y-auto'} overflow-x-hidden [scrollbar-width:none] [-ms-overflow-style:none] [overscroll-behavior:contain] {viewClasses} {disabled
250
+ ? '[background-color:color-mix(in_srgb,var(--color-text)_5%,transparent)] cursor-not-allowed'
251
+ : '[background-color:var(--color-background)]'} focus-visible:[outline:2px_solid_var(--color-primary)] focus-visible:[outline-offset:0px]"
252
+ onscroll={handleScroll}
253
+ onkeydown={handleKeyDown}
254
+ onmouseenter={() => (isHovering = true)}
255
+ onmouseleave={() => (isHovering = false)}
256
+ tabindex={disabled ? -1 : 0}
257
+ role="region"
258
+ aria-label="Scrollable content"
259
+ >
260
+ {@render children()}
261
+ </div>
262
+
263
+ <!-- Custom scrollbar thumb -->
264
+ {#if needsScrollbar}
265
+ <div
266
+ class="absolute rounded cursor-pointer pointer-events-auto z-10"
267
+ style="top: {thumbTop}px; right: {inset}px; height: {thumbHeight}px; width: {scrollbarWidth}px; background-color: color-mix(in srgb, var(--color-primary) {thumbOpacity}, transparent);"
268
+ onmousedown={handleThumbMouseDown}
269
+ role="scrollbar"
270
+ aria-controls={scrollableId}
271
+ aria-orientation="vertical"
272
+ aria-valuenow={scrollPercentage()}
273
+ aria-valuemin="0"
274
+ aria-valuemax="100"
275
+ tabindex="-1"
276
+ aria-label="Scroll thumb"
277
+ ></div>
278
+ {/if}
279
+ </div>
280
+
281
+ <style>
282
+ .scrollview::-webkit-scrollbar {
283
+ display: none;
284
+ }
285
+ </style>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ interface Props extends HTMLAttributes<HTMLDivElement> {
4
+ showScrollbar?: boolean;
5
+ scrollbarWidth?: number;
6
+ class?: string;
7
+ disabled?: boolean;
8
+ children: Snippet;
9
+ view?: HTMLDivElement;
10
+ onScroll?: (data: {
11
+ scrollTop: number;
12
+ thumbTop: number;
13
+ scrollHeight: number;
14
+ clientHeight: number;
15
+ }) => void;
16
+ }
17
+ declare const ScrollView: import("svelte").Component<Props, {}, "view">;
18
+ type ScrollView = ReturnType<typeof ScrollView>;
19
+ export default ScrollView;
@@ -0,0 +1 @@
1
+ export { default } from './ScrollView.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './ScrollView.svelte';
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ import Input from '../Input/Input.svelte';
3
+ import { faSearch, faTimes } from '@fortawesome/free-solid-svg-icons';
4
+ import type { HTMLInputAttributes } from 'svelte/elements';
5
+
6
+ interface Props extends Omit<HTMLInputAttributes, 'type'> {
7
+ value: string;
8
+ class?: string;
9
+ inputClass?: string;
10
+ validate?: (value: string) => boolean | string;
11
+ valid?: boolean;
12
+ touched?: boolean;
13
+ element?: HTMLInputElement;
14
+ onClear?: () => void;
15
+ clearable?: boolean;
16
+ }
17
+
18
+ let {
19
+ value = $bindable(),
20
+ valid = $bindable(true),
21
+ touched = $bindable(false),
22
+ element = $bindable(),
23
+ onClear,
24
+ clearable = true,
25
+ placeholder = 'Search...',
26
+ ...restProps
27
+ }: Props = $props();
28
+
29
+ function handleClear() {
30
+ value = '';
31
+ onClear?.();
32
+ }
33
+
34
+ // Show clear icon when there's content and clearable is true
35
+ let showClearIcon = $derived(clearable && value.length > 0);
36
+ </script>
37
+
38
+ <Input
39
+ bind:value
40
+ bind:valid
41
+ bind:touched
42
+ bind:element
43
+ type="search"
44
+ inputClass="[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none"
45
+ icon={showClearIcon ? faTimes : faSearch}
46
+ iconClickable={showClearIcon}
47
+ onIconClick={showClearIcon ? handleClear : undefined}
48
+ {placeholder}
49
+ {...restProps}
50
+ />
@@ -0,0 +1,15 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ interface Props extends Omit<HTMLInputAttributes, 'type'> {
3
+ value: string;
4
+ class?: string;
5
+ inputClass?: string;
6
+ validate?: (value: string) => boolean | string;
7
+ valid?: boolean;
8
+ touched?: boolean;
9
+ element?: HTMLInputElement;
10
+ onClear?: () => void;
11
+ clearable?: boolean;
12
+ }
13
+ declare const SearchInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
14
+ type SearchInput = ReturnType<typeof SearchInput>;
15
+ export default SearchInput;
@@ -0,0 +1 @@
1
+ export { default } from './SearchInput.svelte';
@@ -0,0 +1 @@
1
+ export { default } from './SearchInput.svelte';