@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.
- package/README.md +81 -0
- package/dist/components/Badge/Badge.svelte +80 -0
- package/dist/components/Badge/Badge.svelte.d.ts +14 -0
- package/dist/components/Badge/index.d.ts +1 -0
- package/dist/components/Badge/index.js +1 -0
- package/dist/components/Button/Button.svelte +172 -0
- package/dist/components/Button/Button.svelte.d.ts +32 -0
- package/dist/components/Button/index.d.ts +1 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/Dialog/Dialog.svelte +101 -0
- package/dist/components/Dialog/Dialog.svelte.d.ts +18 -0
- package/dist/components/Dialog/index.d.ts +1 -0
- package/dist/components/Dialog/index.js +1 -0
- package/dist/components/ElementManager/ElementManager.svelte +397 -0
- package/dist/components/ElementManager/ElementManager.svelte.d.ts +43 -0
- package/dist/components/ElementManager/index.d.ts +1 -0
- package/dist/components/ElementManager/index.js +1 -0
- package/dist/components/EmailInput/EmailInput.svelte +47 -0
- package/dist/components/EmailInput/EmailInput.svelte.d.ts +14 -0
- package/dist/components/EmailInput/index.d.ts +1 -0
- package/dist/components/EmailInput/index.js +1 -0
- package/dist/components/Icon/Icon.svelte +74 -0
- package/dist/components/Icon/Icon.svelte.d.ts +13 -0
- package/dist/components/Icon/index.d.ts +1 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Input/Input.svelte +268 -0
- package/dist/components/Input/Input.svelte.d.ts +18 -0
- package/dist/components/Input/index.d.ts +1 -0
- package/dist/components/Input/index.js +1 -0
- package/dist/components/List/List.svelte +580 -0
- package/dist/components/List/List.svelte.d.ts +38 -0
- package/dist/components/List/index.d.ts +1 -0
- package/dist/components/List/index.js +1 -0
- package/dist/components/ListItem/ListItem.svelte +175 -0
- package/dist/components/ListItem/ListItem.svelte.d.ts +24 -0
- package/dist/components/ListItem/index.d.ts +2 -0
- package/dist/components/ListItem/index.js +2 -0
- package/dist/components/ListView/ListView.svelte +463 -0
- package/dist/components/ListView/ListView.svelte.d.ts +37 -0
- package/dist/components/ListView/index.d.ts +2 -0
- package/dist/components/ListView/index.js +2 -0
- package/dist/components/NodeGraph/BaseNode.d.ts +57 -0
- package/dist/components/NodeGraph/BaseNode.js +30 -0
- package/dist/components/NodeGraph/Edge.svelte +60 -0
- package/dist/components/NodeGraph/Edge.svelte.d.ts +16 -0
- package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +82 -0
- package/dist/components/NodeGraph/ExpressionEvaluator.js +152 -0
- package/dist/components/NodeGraph/Node.svelte +100 -0
- package/dist/components/NodeGraph/Node.svelte.d.ts +10 -0
- package/dist/components/NodeGraph/NodeGraph.svelte +52 -0
- package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +14 -0
- package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +80 -0
- package/dist/components/NodeGraph/NodeInstance.svelte.js +241 -0
- package/dist/components/NodeGraph/NodePort.svelte +75 -0
- package/dist/components/NodeGraph/NodePort.svelte.d.ts +11 -0
- package/dist/components/NodeGraph/decorators.d.ts +81 -0
- package/dist/components/NodeGraph/decorators.js +148 -0
- package/dist/components/NodeGraph/index.d.ts +9 -0
- package/dist/components/NodeGraph/index.js +9 -0
- package/dist/components/NodeGraph/types.d.ts +94 -0
- package/dist/components/NodeGraph/types.js +33 -0
- package/dist/components/NotesEditor/NotesEditor.svelte +203 -0
- package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +9 -0
- package/dist/components/NotesEditor/index.d.ts +1 -0
- package/dist/components/NotesEditor/index.js +1 -0
- package/dist/components/NumberInput/NumberInput.svelte +353 -0
- package/dist/components/NumberInput/NumberInput.svelte.d.ts +26 -0
- package/dist/components/NumberInput/index.d.ts +1 -0
- package/dist/components/NumberInput/index.js +1 -0
- package/dist/components/PasswordInput/PasswordInput.svelte +41 -0
- package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +13 -0
- package/dist/components/PasswordInput/index.d.ts +1 -0
- package/dist/components/PasswordInput/index.js +1 -0
- package/dist/components/Popup/index.d.ts +1 -0
- package/dist/components/Popup/index.js +1 -0
- package/dist/components/Rating/Rating.svelte +77 -0
- package/dist/components/Rating/Rating.svelte.d.ts +7 -0
- package/dist/components/Rating/index.d.ts +1 -0
- package/dist/components/Rating/index.js +1 -0
- package/dist/components/ScrollView/ScrollView.svelte +285 -0
- package/dist/components/ScrollView/ScrollView.svelte.d.ts +19 -0
- package/dist/components/ScrollView/index.d.ts +1 -0
- package/dist/components/ScrollView/index.js +1 -0
- package/dist/components/SearchInput/SearchInput.svelte +50 -0
- package/dist/components/SearchInput/SearchInput.svelte.d.ts +15 -0
- package/dist/components/SearchInput/index.d.ts +1 -0
- package/dist/components/SearchInput/index.js +1 -0
- package/dist/components/Select/Select.svelte +278 -0
- package/dist/components/Select/Select.svelte.d.ts +35 -0
- package/dist/components/Select/index.d.ts +1 -0
- package/dist/components/Select/index.js +1 -0
- package/dist/components/TextArea/TextArea.svelte +734 -0
- package/dist/components/TextArea/TextArea.svelte.d.ts +19 -0
- package/dist/components/TextArea/index.d.ts +1 -0
- package/dist/components/TextArea/index.js +1 -0
- package/dist/components/ThemeSelector/ThemeSelector.svelte +64 -0
- package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +3 -0
- package/dist/components/ThemeSelector/index.d.ts +1 -0
- package/dist/components/ThemeSelector/index.js +1 -0
- package/dist/components/TooltipHandler/TooltipHandler.svelte +593 -0
- package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +10 -0
- package/dist/components/TooltipHandler/index.d.ts +1 -0
- package/dist/components/TooltipHandler/index.js +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +26 -0
- package/dist/styles/sizes.d.ts +78 -0
- package/dist/styles/sizes.js +120 -0
- package/dist/styles/variants.d.ts +106 -0
- package/dist/styles/variants.js +194 -0
- package/dist/types/ManagerTypes.d.ts +42 -0
- package/dist/types/ManagerTypes.js +1 -0
- package/dist/types/sizes.d.ts +5 -0
- package/dist/types/sizes.js +1 -0
- package/dist/types/variants.d.ts +1 -0
- package/dist/types/variants.js +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { slide } from 'svelte/transition';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
|
5
|
+
import Input from '../Input/Input.svelte';
|
|
6
|
+
import List from '../List/List.svelte';
|
|
7
|
+
|
|
8
|
+
type Option = Record<string, any> & {
|
|
9
|
+
value: string;
|
|
10
|
+
label: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
options = [],
|
|
15
|
+
value = $bindable(""),
|
|
16
|
+
multiSelect = false,
|
|
17
|
+
placeholder = "Select an option...",
|
|
18
|
+
maxVisibleItems = 10,
|
|
19
|
+
itemHeight = 32,
|
|
20
|
+
autoAdvance = false,
|
|
21
|
+
class: className = "",
|
|
22
|
+
inputClass = "",
|
|
23
|
+
dropdownClass = "",
|
|
24
|
+
itemClass = "",
|
|
25
|
+
itemSnippet,
|
|
26
|
+
methods = $bindable(),
|
|
27
|
+
// API integration props
|
|
28
|
+
url,
|
|
29
|
+
searchParam,
|
|
30
|
+
transform,
|
|
31
|
+
debounceMs = 300,
|
|
32
|
+
maxResults,
|
|
33
|
+
headers = {},
|
|
34
|
+
searchTerm = $bindable(""),
|
|
35
|
+
onError,
|
|
36
|
+
onLoadStart,
|
|
37
|
+
onLoadEnd,
|
|
38
|
+
onSelection,
|
|
39
|
+
disabled = false,
|
|
40
|
+
...restProps
|
|
41
|
+
}: {
|
|
42
|
+
options?: Option[];
|
|
43
|
+
value: string | string[];
|
|
44
|
+
multiSelect?: boolean;
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
maxVisibleItems?: number;
|
|
47
|
+
itemHeight?: number;
|
|
48
|
+
autoAdvance?: boolean;
|
|
49
|
+
class?: string;
|
|
50
|
+
inputClass?: string;
|
|
51
|
+
dropdownClass?: string;
|
|
52
|
+
itemClass?: string;
|
|
53
|
+
itemSnippet?: Snippet<[Option, number, boolean, boolean]>; // option, index, isSelected, isFocused
|
|
54
|
+
methods?: any;
|
|
55
|
+
// API integration
|
|
56
|
+
url?: string;
|
|
57
|
+
searchParam?: string;
|
|
58
|
+
transform?: (apiResponse: any) => Option[];
|
|
59
|
+
debounceMs?: number;
|
|
60
|
+
maxResults?: number;
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
searchTerm?: string;
|
|
63
|
+
onError?: (error: Error) => void;
|
|
64
|
+
onLoadStart?: () => void;
|
|
65
|
+
onLoadEnd?: () => void;
|
|
66
|
+
onSelection?: (option: Option) => void;
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
} & Record<string, any> = $props();
|
|
69
|
+
|
|
70
|
+
let isOpen = $state(false);
|
|
71
|
+
let inputElement = $state<HTMLInputElement>();
|
|
72
|
+
let listElement = $state<any>();
|
|
73
|
+
|
|
74
|
+
// Get display text for selected value
|
|
75
|
+
const selectedLabel = $derived(() => {
|
|
76
|
+
if (multiSelect && Array.isArray(value)) {
|
|
77
|
+
if (value.length === 0) return "";
|
|
78
|
+
if (value.length === 1) {
|
|
79
|
+
const selectedOption = options.find(option => option.value === value[0]);
|
|
80
|
+
return selectedOption?.label || "";
|
|
81
|
+
}
|
|
82
|
+
return `${value.length} items selected`;
|
|
83
|
+
} else {
|
|
84
|
+
const selectedOption = options.find(option => option.value === value);
|
|
85
|
+
return selectedOption?.label || "";
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Global dropdown manager - only one dropdown open at a time
|
|
90
|
+
let dropdownId = Math.random().toString(36).substring(2, 9);
|
|
91
|
+
let zIndex = $state(50);
|
|
92
|
+
|
|
93
|
+
function toggleDropdown(event?: MouseEvent | KeyboardEvent) {
|
|
94
|
+
event?.stopPropagation();
|
|
95
|
+
event?.preventDefault(); // Prevent text selection
|
|
96
|
+
if (disabled) return;
|
|
97
|
+
|
|
98
|
+
isOpen = !isOpen;
|
|
99
|
+
|
|
100
|
+
if (isOpen) {
|
|
101
|
+
// Set focus to the selected item in the list
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
if (listElement?.focus) {
|
|
104
|
+
listElement.focus();
|
|
105
|
+
}
|
|
106
|
+
}, 50);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleInputMouseDown(event: MouseEvent) {
|
|
111
|
+
event.preventDefault(); // Prevent text selection on mousedown
|
|
112
|
+
toggleDropdown(event);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function handleInputDoubleClick(event: MouseEvent) {
|
|
116
|
+
event.preventDefault(); // Prevent text selection on double-click
|
|
117
|
+
event.stopPropagation();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function handleSelection(option: Option) {
|
|
121
|
+
// In single-select mode, close dropdown. In multi-select mode, keep it open
|
|
122
|
+
if (!multiSelect) {
|
|
123
|
+
isOpen = false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (onSelection) {
|
|
127
|
+
onSelection(option);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!multiSelect && autoAdvance) {
|
|
131
|
+
// Move to next focusable element (only in single-select mode)
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
if (inputElement) {
|
|
134
|
+
const focusableElements = document.querySelectorAll(
|
|
135
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
136
|
+
);
|
|
137
|
+
const currentIndex = Array.from(focusableElements).indexOf(inputElement);
|
|
138
|
+
const nextElement = focusableElements[currentIndex + 1] as HTMLElement;
|
|
139
|
+
nextElement?.focus();
|
|
140
|
+
}
|
|
141
|
+
}, 50);
|
|
142
|
+
} else if (!multiSelect) {
|
|
143
|
+
inputElement?.focus();
|
|
144
|
+
}
|
|
145
|
+
// In multi-select mode, keep focus on the list for continued selection
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
149
|
+
if (event.key === 'Escape') {
|
|
150
|
+
isOpen = false;
|
|
151
|
+
inputElement?.focus();
|
|
152
|
+
} else if (event.key === 'Enter') {
|
|
153
|
+
if (isOpen) {
|
|
154
|
+
// Close dropdown and allow Enter to propagate for form submission
|
|
155
|
+
isOpen = false;
|
|
156
|
+
inputElement?.focus();
|
|
157
|
+
} else {
|
|
158
|
+
// Open dropdown when closed
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
toggleDropdown(event);
|
|
161
|
+
}
|
|
162
|
+
} else if (event.key === ' ') {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
toggleDropdown(event);
|
|
165
|
+
} else if (event.key === 'ArrowDown' && !isOpen) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
isOpen = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Expose methods to parent
|
|
172
|
+
$effect(() => {
|
|
173
|
+
methods = {
|
|
174
|
+
openDropdown: () => {
|
|
175
|
+
isOpen = true;
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
if (listElement?.focus) {
|
|
178
|
+
listElement.focus();
|
|
179
|
+
}
|
|
180
|
+
}, 50);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Close dropdown when clicking outside or when another dropdown opens
|
|
186
|
+
$effect(() => {
|
|
187
|
+
if (isOpen) {
|
|
188
|
+
// Increment z-index for newly opened dropdown
|
|
189
|
+
zIndex = 50 + Date.now() % 1000;
|
|
190
|
+
|
|
191
|
+
// Set this as the active dropdown
|
|
192
|
+
document.dispatchEvent(new CustomEvent('dropdown-opened', { detail: { id: dropdownId } }));
|
|
193
|
+
|
|
194
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
195
|
+
const target = event.target as Element;
|
|
196
|
+
if (!target.closest('.select-container')) {
|
|
197
|
+
isOpen = false;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const handleOtherDropdownOpen = (event: CustomEvent) => {
|
|
202
|
+
if (event.detail.id !== dropdownId) {
|
|
203
|
+
isOpen = false;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
document.addEventListener('click', handleClickOutside);
|
|
208
|
+
document.addEventListener('dropdown-opened', handleOtherDropdownOpen as EventListener);
|
|
209
|
+
|
|
210
|
+
return () => {
|
|
211
|
+
document.removeEventListener('click', handleClickOutside);
|
|
212
|
+
document.removeEventListener('dropdown-opened', handleOtherDropdownOpen as EventListener);
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
<div class="relative select-container {className}">
|
|
219
|
+
<Input
|
|
220
|
+
bind:element={inputElement}
|
|
221
|
+
value={selectedLabel()}
|
|
222
|
+
{placeholder}
|
|
223
|
+
readonly={true}
|
|
224
|
+
icon={faChevronDown}
|
|
225
|
+
iconPosition="right"
|
|
226
|
+
iconClickable={true}
|
|
227
|
+
onIconClick={toggleDropdown}
|
|
228
|
+
onmousedown={handleInputMouseDown}
|
|
229
|
+
ondblclick={handleInputDoubleClick}
|
|
230
|
+
onkeydown={handleKeydown}
|
|
231
|
+
{disabled}
|
|
232
|
+
class={inputClass}
|
|
233
|
+
inputClass={disabled ? "!cursor-default select-none" : "!cursor-pointer select-none"}
|
|
234
|
+
{...restProps}
|
|
235
|
+
/>
|
|
236
|
+
|
|
237
|
+
<!-- Always render List to maintain cache, show/hide with CSS -->
|
|
238
|
+
<div
|
|
239
|
+
class="absolute w-full {isOpen ? '' : 'opacity-0 pointer-events-none'}"
|
|
240
|
+
style="z-index: {isOpen ? zIndex : -1}; top: calc(100% + 0.25rem);"
|
|
241
|
+
>
|
|
242
|
+
<List
|
|
243
|
+
bind:this={listElement}
|
|
244
|
+
{options}
|
|
245
|
+
bind:value
|
|
246
|
+
{multiSelect}
|
|
247
|
+
{maxVisibleItems}
|
|
248
|
+
{itemHeight}
|
|
249
|
+
{itemSnippet}
|
|
250
|
+
onSelection={handleSelection}
|
|
251
|
+
class={dropdownClass}
|
|
252
|
+
{itemClass}
|
|
253
|
+
{url}
|
|
254
|
+
{searchParam}
|
|
255
|
+
{transform}
|
|
256
|
+
{debounceMs}
|
|
257
|
+
{maxResults}
|
|
258
|
+
{headers}
|
|
259
|
+
{searchTerm}
|
|
260
|
+
{onError}
|
|
261
|
+
{onLoadStart}
|
|
262
|
+
{onLoadEnd}
|
|
263
|
+
/>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<!-- Transition effect overlay -->
|
|
267
|
+
{#if isOpen}
|
|
268
|
+
<div
|
|
269
|
+
class="absolute w-full pointer-events-none"
|
|
270
|
+
style="z-index: {zIndex + 1}; top: calc(100% + 0.25rem);"
|
|
271
|
+
transition:slide={{ duration: 200 }}
|
|
272
|
+
>
|
|
273
|
+
<div style="height: {maxVisibleItems * itemHeight}px; opacity: 0;">
|
|
274
|
+
<!-- Invisible placeholder for slide transition -->
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
{/if}
|
|
278
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type Option = Record<string, any> & {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
type $$ComponentProps = {
|
|
7
|
+
options?: Option[];
|
|
8
|
+
value: string | string[];
|
|
9
|
+
multiSelect?: boolean;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
maxVisibleItems?: number;
|
|
12
|
+
itemHeight?: number;
|
|
13
|
+
autoAdvance?: boolean;
|
|
14
|
+
class?: string;
|
|
15
|
+
inputClass?: string;
|
|
16
|
+
dropdownClass?: string;
|
|
17
|
+
itemClass?: string;
|
|
18
|
+
itemSnippet?: Snippet<[Option, number, boolean, boolean]>;
|
|
19
|
+
methods?: any;
|
|
20
|
+
url?: string;
|
|
21
|
+
searchParam?: string;
|
|
22
|
+
transform?: (apiResponse: any) => Option[];
|
|
23
|
+
debounceMs?: number;
|
|
24
|
+
maxResults?: number;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
searchTerm?: string;
|
|
27
|
+
onError?: (error: Error) => void;
|
|
28
|
+
onLoadStart?: () => void;
|
|
29
|
+
onLoadEnd?: () => void;
|
|
30
|
+
onSelection?: (option: Option) => void;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
} & Record<string, any>;
|
|
33
|
+
declare const Select: import("svelte").Component<$$ComponentProps, {}, "value" | "searchTerm" | "methods">;
|
|
34
|
+
type Select = ReturnType<typeof Select>;
|
|
35
|
+
export default Select;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Select.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Select.svelte';
|