@spaethtech/svelte-ui 0.1.10 → 0.3.1-dev.3.e16df24
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 +180 -42
- package/dist/components/Alert.svelte +119 -0
- package/dist/components/Alert.svelte.d.ts +29 -0
- package/dist/components/Badge/Badge.svelte +142 -69
- package/dist/components/Badge/Badge.svelte.d.ts +26 -6
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/Badge/index.js +1 -1
- package/dist/components/Banner.svelte +133 -0
- package/dist/components/Banner.svelte.d.ts +31 -0
- package/dist/components/Button.svelte +240 -0
- package/dist/components/Button.svelte.d.ts +33 -0
- package/dist/components/ButtonDropdown.svelte +145 -0
- package/dist/components/ButtonDropdown.svelte.d.ts +31 -0
- package/dist/components/Calendar.svelte +259 -0
- package/dist/components/Calendar.svelte.d.ts +27 -0
- package/dist/components/Card.svelte +78 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CardBody.svelte +41 -0
- package/dist/components/CardBody.svelte.d.ts +17 -0
- package/dist/components/CardFooter.svelte +49 -0
- package/dist/components/CardFooter.svelte.d.ts +16 -0
- package/dist/components/CardHeader.svelte +49 -0
- package/dist/components/CardHeader.svelte.d.ts +16 -0
- package/dist/components/Checkbox.svelte +131 -0
- package/dist/components/Checkbox.svelte.d.ts +16 -0
- package/dist/components/DataTable.svelte +518 -0
- package/dist/components/DataTable.svelte.d.ts +74 -0
- package/dist/components/DatePicker.svelte +312 -0
- package/dist/components/DatePicker.svelte.d.ts +32 -0
- package/dist/components/DateTimeInput.svelte +93 -0
- package/dist/components/DateTimeInput.svelte.d.ts +20 -0
- package/dist/components/Dialog.svelte +132 -0
- package/dist/components/Dialog.svelte.d.ts +24 -0
- package/dist/components/{EmailInput/EmailInput.svelte → EmailInput.svelte} +17 -8
- package/dist/components/EmailInput.svelte.d.ts +21 -0
- package/dist/components/Input.svelte +369 -0
- package/dist/components/Input.svelte.d.ts +35 -0
- package/dist/components/{List/List.svelte → List.svelte} +194 -133
- package/dist/components/{List/List.svelte.d.ts → List.svelte.d.ts} +9 -1
- package/dist/components/Menu.svelte +117 -0
- package/dist/components/Menu.svelte.d.ts +20 -0
- package/dist/components/NotesEditor.svelte +127 -0
- package/dist/components/NotesEditor.svelte.d.ts +17 -0
- package/dist/components/{NumberInput/NumberInput.svelte → NumberInput.svelte} +146 -105
- package/dist/components/{NumberInput/NumberInput.svelte.d.ts → NumberInput.svelte.d.ts} +11 -4
- package/dist/components/PasswordInput.svelte +52 -0
- package/dist/components/PasswordInput.svelte.d.ts +20 -0
- package/dist/components/Popup.svelte +140 -0
- package/dist/components/Popup.svelte.d.ts +31 -0
- package/dist/components/Query.svelte +284 -0
- package/dist/components/Query.svelte.d.ts +15 -0
- package/dist/components/{Rating/Rating.svelte → Rating.svelte} +19 -10
- package/dist/components/Rating.svelte.d.ts +13 -0
- package/dist/components/{SearchInput/SearchInput.svelte → SearchInput.svelte} +19 -8
- package/dist/components/{SearchInput/SearchInput.svelte.d.ts → SearchInput.svelte.d.ts} +9 -2
- package/dist/components/{Select/Select.svelte → Select.svelte} +48 -76
- package/dist/components/{Select/Select.svelte.d.ts → Select.svelte.d.ts} +11 -1
- package/dist/components/SideBarMenu/SideBarMenu.svelte +724 -0
- package/dist/components/SideBarMenu/SideBarMenu.svelte.d.ts +109 -0
- package/dist/components/SideBarMenu/index.d.ts +2 -0
- package/dist/components/SideBarMenu/index.js +1 -0
- package/dist/components/TabStrip/TabStrip.svelte +384 -0
- package/dist/components/TabStrip/TabStrip.svelte.d.ts +50 -0
- package/dist/components/TabStrip/index.d.ts +3 -0
- package/dist/components/TabStrip/index.js +2 -0
- package/dist/components/{TextArea/TextArea.svelte → TextArea.svelte} +143 -112
- package/dist/components/TextArea.svelte.d.ts +38 -0
- package/dist/components/ThemeSelector.svelte +81 -0
- package/dist/components/ThemeSelector.svelte.d.ts +10 -0
- package/dist/components/TimePicker.svelte +148 -0
- package/dist/components/TimePicker.svelte.d.ts +28 -0
- package/dist/components/TimeRangeInput.svelte +203 -0
- package/dist/components/TimeRangeInput.svelte.d.ts +29 -0
- package/dist/components/TimeSpinner.svelte +202 -0
- package/dist/components/TimeSpinner.svelte.d.ts +26 -0
- package/dist/components/Toast/Toaster.svelte +51 -0
- package/dist/components/Toast/Toaster.svelte.d.ts +12 -0
- package/dist/components/Toast/toast.svelte.d.ts +29 -0
- package/dist/components/Toast/toast.svelte.js +27 -0
- package/dist/components/Toggle.svelte +93 -0
- package/dist/components/Toggle.svelte.d.ts +15 -0
- package/dist/data/dataset.d.ts +42 -0
- package/dist/data/dataset.js +3 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/data/query/ast.d.ts +62 -0
- package/dist/data/query/ast.js +12 -0
- package/dist/data/query/index.d.ts +3 -0
- package/dist/data/query/index.js +3 -0
- package/dist/data/query/lexer.d.ts +33 -0
- package/dist/data/query/lexer.js +225 -0
- package/dist/data/query/parser.d.ts +11 -0
- package/dist/data/query/parser.js +252 -0
- package/dist/data/table/grid.svelte.d.ts +57 -0
- package/dist/data/table/grid.svelte.js +139 -0
- package/dist/data/table/index.d.ts +2 -0
- package/dist/data/table/index.js +2 -0
- package/dist/data/table/types.d.ts +79 -0
- package/dist/index.d.ts +48 -22
- package/dist/index.js +45 -21
- package/dist/positioning/anchored.d.ts +61 -0
- package/dist/positioning/anchored.js +122 -0
- package/dist/positioning/tooltip.d.ts +41 -0
- package/dist/positioning/tooltip.js +147 -0
- package/dist/theme.css +205 -0
- package/dist/types/breakpoints.d.ts +24 -0
- package/dist/types/breakpoints.js +13 -0
- package/dist/types/responsive.d.ts +32 -0
- package/dist/types/responsive.js +54 -0
- package/dist/types/sizes.d.ts +10 -3
- package/dist/types/time.d.ts +19 -0
- package/dist/types/time.js +10 -0
- package/dist/types/variants.d.ts +8 -1
- package/dist/types/variants.js +16 -1
- package/package.json +93 -63
- package/dist/components/Button/Button.svelte +0 -172
- package/dist/components/Button/Button.svelte.d.ts +0 -32
- package/dist/components/Button/index.d.ts +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/Dialog/Dialog.svelte +0 -101
- package/dist/components/Dialog/Dialog.svelte.d.ts +0 -18
- package/dist/components/Dialog/index.d.ts +0 -1
- package/dist/components/Dialog/index.js +0 -1
- package/dist/components/ElementManager/ElementManager.svelte +0 -397
- package/dist/components/ElementManager/ElementManager.svelte.d.ts +0 -43
- package/dist/components/ElementManager/index.d.ts +0 -1
- package/dist/components/ElementManager/index.js +0 -1
- package/dist/components/EmailInput/EmailInput.svelte.d.ts +0 -14
- package/dist/components/EmailInput/index.d.ts +0 -1
- package/dist/components/EmailInput/index.js +0 -1
- package/dist/components/Icon/Icon.svelte +0 -74
- package/dist/components/Icon/Icon.svelte.d.ts +0 -13
- package/dist/components/Icon/index.d.ts +0 -1
- package/dist/components/Icon/index.js +0 -1
- package/dist/components/Input/Input.svelte +0 -268
- package/dist/components/Input/Input.svelte.d.ts +0 -18
- package/dist/components/Input/index.d.ts +0 -1
- package/dist/components/Input/index.js +0 -1
- package/dist/components/List/index.d.ts +0 -1
- package/dist/components/List/index.js +0 -1
- package/dist/components/ListItem/ListItem.svelte +0 -175
- package/dist/components/ListItem/ListItem.svelte.d.ts +0 -24
- package/dist/components/ListItem/index.d.ts +0 -2
- package/dist/components/ListItem/index.js +0 -2
- package/dist/components/ListView/ListView.svelte +0 -463
- package/dist/components/ListView/ListView.svelte.d.ts +0 -37
- package/dist/components/ListView/index.d.ts +0 -2
- package/dist/components/ListView/index.js +0 -2
- package/dist/components/NodeGraph/BaseNode.d.ts +0 -57
- package/dist/components/NodeGraph/BaseNode.js +0 -30
- package/dist/components/NodeGraph/Edge.svelte +0 -60
- package/dist/components/NodeGraph/Edge.svelte.d.ts +0 -16
- package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +0 -82
- package/dist/components/NodeGraph/ExpressionEvaluator.js +0 -152
- package/dist/components/NodeGraph/Node.svelte +0 -100
- package/dist/components/NodeGraph/Node.svelte.d.ts +0 -10
- package/dist/components/NodeGraph/NodeGraph.svelte +0 -52
- package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +0 -14
- package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +0 -80
- package/dist/components/NodeGraph/NodeInstance.svelte.js +0 -241
- package/dist/components/NodeGraph/NodePort.svelte +0 -75
- package/dist/components/NodeGraph/NodePort.svelte.d.ts +0 -11
- package/dist/components/NodeGraph/decorators.d.ts +0 -81
- package/dist/components/NodeGraph/decorators.js +0 -148
- package/dist/components/NodeGraph/index.d.ts +0 -9
- package/dist/components/NodeGraph/index.js +0 -9
- package/dist/components/NodeGraph/types.d.ts +0 -94
- package/dist/components/NodeGraph/types.js +0 -33
- package/dist/components/NotesEditor/NotesEditor.svelte +0 -203
- package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +0 -9
- package/dist/components/NotesEditor/index.d.ts +0 -1
- package/dist/components/NotesEditor/index.js +0 -1
- package/dist/components/NumberInput/index.d.ts +0 -1
- package/dist/components/NumberInput/index.js +0 -1
- package/dist/components/PasswordInput/PasswordInput.svelte +0 -41
- package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +0 -13
- package/dist/components/PasswordInput/index.d.ts +0 -1
- package/dist/components/PasswordInput/index.js +0 -1
- package/dist/components/Popup/index.d.ts +0 -1
- package/dist/components/Popup/index.js +0 -1
- package/dist/components/Rating/Rating.svelte.d.ts +0 -7
- package/dist/components/Rating/index.d.ts +0 -1
- package/dist/components/Rating/index.js +0 -1
- package/dist/components/ScrollView/ScrollView.svelte +0 -285
- package/dist/components/ScrollView/ScrollView.svelte.d.ts +0 -19
- package/dist/components/ScrollView/index.d.ts +0 -1
- package/dist/components/ScrollView/index.js +0 -1
- package/dist/components/SearchInput/index.d.ts +0 -1
- package/dist/components/SearchInput/index.js +0 -1
- package/dist/components/Select/index.d.ts +0 -1
- package/dist/components/Select/index.js +0 -1
- package/dist/components/TextArea/TextArea.svelte.d.ts +0 -19
- package/dist/components/TextArea/index.d.ts +0 -1
- package/dist/components/TextArea/index.js +0 -1
- package/dist/components/ThemeSelector/ThemeSelector.svelte +0 -64
- package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +0 -3
- package/dist/components/ThemeSelector/index.d.ts +0 -1
- package/dist/components/ThemeSelector/index.js +0 -1
- package/dist/components/TooltipHandler/TooltipHandler.svelte +0 -593
- package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +0 -10
- package/dist/components/TooltipHandler/index.d.ts +0 -1
- package/dist/components/TooltipHandler/index.js +0 -1
- package/dist/styles/sizes.d.ts +0 -78
- package/dist/styles/sizes.js +0 -120
- package/dist/styles/variants.d.ts +0 -106
- package/dist/styles/variants.js +0 -194
- package/dist/types/ManagerTypes.d.ts +0 -42
- /package/dist/{types/ManagerTypes.js → data/table/types.js} +0 -0
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { untrack } from "svelte";
|
|
3
|
-
import type { Snippet } from "svelte";
|
|
4
|
-
import ScrollView from "../ScrollView/ScrollView.svelte";
|
|
5
|
-
|
|
6
|
-
type Option = Record<string, any> & {
|
|
7
|
-
value: string;
|
|
8
|
-
label: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
options?: Option[];
|
|
13
|
-
value?: string | string[]; // Bindable selected value - string[] when multiSelect is true
|
|
14
|
-
multiSelect?: boolean; // Enable multi-select mode
|
|
15
|
-
showCheckboxes?: boolean; // Show checkboxes for selection (implies multiSelect)
|
|
16
|
-
maxVisibleItems?: number; // When not set, grows to fit all items
|
|
17
|
-
itemHeight?: number;
|
|
18
|
-
minHeight?: number; // Minimum height in pixels (default 32px)
|
|
19
|
-
class?: string;
|
|
20
|
-
containerClass?: string;
|
|
21
|
-
disabled?: boolean;
|
|
22
|
-
children?: Snippet;
|
|
23
|
-
// API integration
|
|
24
|
-
url?: string;
|
|
25
|
-
searchParam?: string;
|
|
26
|
-
transform?: (apiResponse: any) => Option[];
|
|
27
|
-
debounceMs?: number;
|
|
28
|
-
maxResults?: number;
|
|
29
|
-
headers?: Record<string, string>;
|
|
30
|
-
searchTerm?: string;
|
|
31
|
-
showLoading?: boolean;
|
|
32
|
-
loadingText?: string;
|
|
33
|
-
showError?: boolean;
|
|
34
|
-
errorText?: string;
|
|
35
|
-
emptyText?: string;
|
|
36
|
-
onError?: (error: Error) => void;
|
|
37
|
-
onLoadStart?: () => void;
|
|
38
|
-
onLoadEnd?: () => void;
|
|
39
|
-
onSelection?: (option: Option | Option[]) => void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let {
|
|
43
|
-
options = [],
|
|
44
|
-
value = $bindable(),
|
|
45
|
-
multiSelect = false,
|
|
46
|
-
showCheckboxes = false,
|
|
47
|
-
maxVisibleItems, // Optional - when not set, grows to fit all items
|
|
48
|
-
itemHeight, // Will be auto-detected: 40px if secondaryText found, otherwise 32px
|
|
49
|
-
minHeight = 32, // Should match var(--ui-height) for standard UI element height
|
|
50
|
-
class: className = "",
|
|
51
|
-
containerClass = "",
|
|
52
|
-
disabled = false,
|
|
53
|
-
children,
|
|
54
|
-
// API integration props
|
|
55
|
-
url,
|
|
56
|
-
searchParam,
|
|
57
|
-
transform,
|
|
58
|
-
debounceMs = 300,
|
|
59
|
-
maxResults,
|
|
60
|
-
headers = {},
|
|
61
|
-
searchTerm = $bindable(""),
|
|
62
|
-
showLoading = true,
|
|
63
|
-
loadingText = "Loading...",
|
|
64
|
-
showError = true,
|
|
65
|
-
errorText = "Failed to load data",
|
|
66
|
-
emptyText = "No data",
|
|
67
|
-
onError,
|
|
68
|
-
onLoadStart,
|
|
69
|
-
onLoadEnd,
|
|
70
|
-
onSelection,
|
|
71
|
-
}: Props = $props();
|
|
72
|
-
|
|
73
|
-
// showCheckboxes implies multiSelect
|
|
74
|
-
const effectiveMultiSelect = $derived(multiSelect || showCheckboxes);
|
|
75
|
-
|
|
76
|
-
// Initialize value based on multiSelect mode
|
|
77
|
-
$effect(() => {
|
|
78
|
-
if (value === undefined) {
|
|
79
|
-
value = effectiveMultiSelect ? [] : "";
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// Helper functions for multi-select
|
|
84
|
-
function isSelected(optionValue: string): boolean {
|
|
85
|
-
if (effectiveMultiSelect) {
|
|
86
|
-
return Array.isArray(value) && value.includes(optionValue);
|
|
87
|
-
}
|
|
88
|
-
return value === optionValue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function toggleSelection(optionValue: string) {
|
|
92
|
-
if (effectiveMultiSelect && Array.isArray(value)) {
|
|
93
|
-
if (value.includes(optionValue)) {
|
|
94
|
-
value = value.filter((v) => v !== optionValue);
|
|
95
|
-
} else {
|
|
96
|
-
value = [...value, optionValue];
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
value = optionValue;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// API integration state
|
|
104
|
-
let isLoading = $state(false);
|
|
105
|
-
let hasError = $state(false);
|
|
106
|
-
let currentError = $state<Error | null>(null);
|
|
107
|
-
let apiOptions = $state<Option[]>([]);
|
|
108
|
-
let lastSearchTerm = $state("");
|
|
109
|
-
let abortController = $state<AbortController | null>(null);
|
|
110
|
-
|
|
111
|
-
// Combine static options with API options
|
|
112
|
-
const allOptions = $derived(() => {
|
|
113
|
-
if (url && apiOptions.length > 0) {
|
|
114
|
-
return apiOptions;
|
|
115
|
-
}
|
|
116
|
-
return options;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Calculate height - grows to fit all items when maxVisibleItems is not set
|
|
120
|
-
const calculatedHeight = $derived(() => {
|
|
121
|
-
if (maxVisibleItems !== undefined) {
|
|
122
|
-
// Fixed height mode - use maxVisibleItems
|
|
123
|
-
return maxVisibleItems * effectiveItemHeight;
|
|
124
|
-
} else {
|
|
125
|
-
// Auto-grow mode - use actual item count from options or children
|
|
126
|
-
const opts = allOptions();
|
|
127
|
-
if (opts.length > 0) {
|
|
128
|
-
return opts.length * effectiveItemHeight;
|
|
129
|
-
}
|
|
130
|
-
// When using children (no options), use counted rendered items
|
|
131
|
-
if (renderedItemCount > 0) {
|
|
132
|
-
return renderedItemCount * effectiveItemHeight;
|
|
133
|
-
}
|
|
134
|
-
// Fallback to minHeight
|
|
135
|
-
return minHeight;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const maxHeight = $derived(() => {
|
|
140
|
-
return Math.max(calculatedHeight(), minHeight);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Check if any options have description/secondaryText fields (available before rendering)
|
|
144
|
-
// TODO: Enable this auto-detection when options prop pattern is fully implemented
|
|
145
|
-
// const hasSecondaryText = $derived(() => {
|
|
146
|
-
// const opts = allOptions();
|
|
147
|
-
// return opts.some(
|
|
148
|
-
// (opt) =>
|
|
149
|
-
// opt.description !== undefined ||
|
|
150
|
-
// opt.secondaryText !== undefined,
|
|
151
|
-
// );
|
|
152
|
-
// });
|
|
153
|
-
|
|
154
|
-
// Determine default height: 32px for now, will auto-detect 48px when secondaryText support is added
|
|
155
|
-
const defaultItemHeight = $derived(32);
|
|
156
|
-
|
|
157
|
-
// Use itemHeight prop if provided, otherwise use detected default
|
|
158
|
-
const effectiveItemHeight = $derived(itemHeight ?? defaultItemHeight);
|
|
159
|
-
|
|
160
|
-
let listElement = $state<HTMLDivElement>();
|
|
161
|
-
let focusedIndex = $state(-1);
|
|
162
|
-
let keyboardCursor = $state(-1);
|
|
163
|
-
let measuredItemHeight = $state<number | null>(null);
|
|
164
|
-
let renderedItemCount = $state(0);
|
|
165
|
-
|
|
166
|
-
// Automatically measure item height and count from rendered items
|
|
167
|
-
$effect(() => {
|
|
168
|
-
if (listElement) {
|
|
169
|
-
const firstItem = listElement.querySelector('[role="option"]');
|
|
170
|
-
if (firstItem) {
|
|
171
|
-
measuredItemHeight = firstItem.getBoundingClientRect().height;
|
|
172
|
-
}
|
|
173
|
-
// Count rendered items for auto-height mode
|
|
174
|
-
const items = listElement.querySelectorAll('[role="option"]');
|
|
175
|
-
renderedItemCount = items.length;
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
function selectOption(optionValue: string, event: MouseEvent) {
|
|
180
|
-
if (disabled) return;
|
|
181
|
-
|
|
182
|
-
event.stopPropagation();
|
|
183
|
-
|
|
184
|
-
const clickedIndex = allOptions().findIndex(
|
|
185
|
-
(option) => option.value === optionValue,
|
|
186
|
-
);
|
|
187
|
-
if (clickedIndex >= 0) {
|
|
188
|
-
keyboardCursor = clickedIndex;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
toggleSelection(optionValue);
|
|
192
|
-
|
|
193
|
-
const selectedOption = allOptions().find(
|
|
194
|
-
(option) => option.value === optionValue,
|
|
195
|
-
);
|
|
196
|
-
if (selectedOption && onSelection && value !== undefined) {
|
|
197
|
-
if (effectiveMultiSelect && Array.isArray(value)) {
|
|
198
|
-
const currentValue = value; // Capture to avoid undefined in closure
|
|
199
|
-
const selectedOptions = allOptions().filter((opt) =>
|
|
200
|
-
currentValue.includes(opt.value),
|
|
201
|
-
);
|
|
202
|
-
onSelection(selectedOptions);
|
|
203
|
-
} else {
|
|
204
|
-
onSelection(selectedOption);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function handleListFocus() {
|
|
210
|
-
if (keyboardCursor < 0 && allOptions().length > 0) {
|
|
211
|
-
keyboardCursor = 0;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (keyboardCursor >= 0) {
|
|
215
|
-
focusedIndex = keyboardCursor;
|
|
216
|
-
|
|
217
|
-
if (listElement) {
|
|
218
|
-
const itemTop = keyboardCursor * effectiveItemHeight;
|
|
219
|
-
const itemBottom = itemTop + effectiveItemHeight;
|
|
220
|
-
const viewTop = listElement.scrollTop;
|
|
221
|
-
const currentMaxHeight = maxHeight();
|
|
222
|
-
const viewBottom = viewTop + currentMaxHeight;
|
|
223
|
-
|
|
224
|
-
if (itemTop < viewTop || itemBottom > viewBottom) {
|
|
225
|
-
listElement.scrollTop = Math.max(
|
|
226
|
-
0,
|
|
227
|
-
itemTop - (currentMaxHeight - effectiveItemHeight) / 2,
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
focusedIndex = -1;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function handleListBlur() {
|
|
237
|
-
focusedIndex = -1;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// API fetch function
|
|
241
|
-
async function fetchOptions(searchQuery: string) {
|
|
242
|
-
if (!url) {
|
|
243
|
-
apiOptions = [];
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (searchParam && !searchQuery.trim()) {
|
|
248
|
-
apiOptions = [];
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (abortController) {
|
|
253
|
-
abortController.abort();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
abortController = new AbortController();
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
isLoading = true;
|
|
260
|
-
onLoadStart?.();
|
|
261
|
-
|
|
262
|
-
const apiUrl = new URL(url, window.location.origin);
|
|
263
|
-
if (searchParam && searchQuery.trim()) {
|
|
264
|
-
apiUrl.searchParams.set(searchParam, searchQuery);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const response = await fetch(apiUrl.toString(), {
|
|
268
|
-
signal: abortController.signal,
|
|
269
|
-
headers: {
|
|
270
|
-
"Content-Type": "application/json",
|
|
271
|
-
...headers,
|
|
272
|
-
},
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
if (!response.ok) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
`HTTP ${response.status}: ${response.statusText}`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const data = await response.json();
|
|
282
|
-
|
|
283
|
-
let transformedOptions = transform ? transform(data) : data;
|
|
284
|
-
|
|
285
|
-
if (maxResults && transformedOptions.length > maxResults) {
|
|
286
|
-
transformedOptions = transformedOptions.slice(0, maxResults);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
apiOptions = transformedOptions;
|
|
290
|
-
lastSearchTerm = searchQuery;
|
|
291
|
-
} catch (error) {
|
|
292
|
-
if (error instanceof Error && error.name !== "AbortError") {
|
|
293
|
-
console.error("ListView API fetch error:", error);
|
|
294
|
-
hasError = true;
|
|
295
|
-
currentError = error;
|
|
296
|
-
onError?.(error);
|
|
297
|
-
apiOptions = [];
|
|
298
|
-
}
|
|
299
|
-
} finally {
|
|
300
|
-
isLoading = false;
|
|
301
|
-
onLoadEnd?.();
|
|
302
|
-
abortController = null;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// API fetch effect for search functionality
|
|
307
|
-
$effect(() => {
|
|
308
|
-
if (url && searchParam) {
|
|
309
|
-
if (searchTerm.trim() && searchTerm !== lastSearchTerm) {
|
|
310
|
-
const timeoutId = setTimeout(() => {
|
|
311
|
-
fetchOptions(searchTerm);
|
|
312
|
-
}, debounceMs);
|
|
313
|
-
|
|
314
|
-
return () => clearTimeout(timeoutId);
|
|
315
|
-
} else if (!searchTerm.trim()) {
|
|
316
|
-
apiOptions = [];
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// Initial API fetch for static data (no search) - run once on mount
|
|
322
|
-
$effect(() => {
|
|
323
|
-
if (url && !searchParam) {
|
|
324
|
-
untrack(() => fetchOptions(""));
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Clear error state when search term changes
|
|
329
|
-
$effect(() => {
|
|
330
|
-
if (searchTerm !== lastSearchTerm) {
|
|
331
|
-
hasError = false;
|
|
332
|
-
currentError = null;
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Handle keyboard navigation within list
|
|
337
|
-
function handleListKeydown(event: KeyboardEvent) {
|
|
338
|
-
if (disabled) return;
|
|
339
|
-
|
|
340
|
-
if (event.key === "Tab") {
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
345
|
-
event.preventDefault();
|
|
346
|
-
event.stopPropagation();
|
|
347
|
-
|
|
348
|
-
if (focusedIndex === -1) {
|
|
349
|
-
const startIndex = keyboardCursor >= 0 ? keyboardCursor : 0;
|
|
350
|
-
|
|
351
|
-
if (event.key === "ArrowDown") {
|
|
352
|
-
focusedIndex = Math.min(
|
|
353
|
-
startIndex + 1,
|
|
354
|
-
allOptions().length - 1,
|
|
355
|
-
);
|
|
356
|
-
} else {
|
|
357
|
-
focusedIndex = Math.max(startIndex - 1, 0);
|
|
358
|
-
}
|
|
359
|
-
} else {
|
|
360
|
-
if (event.key === "ArrowDown") {
|
|
361
|
-
focusedIndex = Math.min(
|
|
362
|
-
focusedIndex + 1,
|
|
363
|
-
allOptions().length - 1,
|
|
364
|
-
);
|
|
365
|
-
} else {
|
|
366
|
-
focusedIndex = Math.max(focusedIndex - 1, 0);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
keyboardCursor = focusedIndex;
|
|
371
|
-
|
|
372
|
-
if (listElement && maxVisibleItems !== undefined) {
|
|
373
|
-
const itemTop = focusedIndex * itemHeight;
|
|
374
|
-
const itemBottom = itemTop + itemHeight;
|
|
375
|
-
const viewTop = listElement.scrollTop;
|
|
376
|
-
const currentMaxHeight = maxHeight();
|
|
377
|
-
const viewBottom = viewTop + currentMaxHeight;
|
|
378
|
-
|
|
379
|
-
if (itemTop < viewTop) {
|
|
380
|
-
listElement.scrollTop = itemTop;
|
|
381
|
-
} else if (itemBottom > viewBottom) {
|
|
382
|
-
listElement.scrollTop = itemBottom - currentMaxHeight;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
} else if (event.key === " ") {
|
|
386
|
-
event.preventDefault();
|
|
387
|
-
event.stopPropagation();
|
|
388
|
-
if (focusedIndex >= 0 && focusedIndex < allOptions().length) {
|
|
389
|
-
const selectedOption = allOptions()[focusedIndex];
|
|
390
|
-
|
|
391
|
-
toggleSelection(selectedOption.value);
|
|
392
|
-
|
|
393
|
-
if (onSelection && value !== undefined) {
|
|
394
|
-
if (effectiveMultiSelect && Array.isArray(value)) {
|
|
395
|
-
const currentValue = value; // Capture to avoid undefined in closure
|
|
396
|
-
const selectedOptions = allOptions().filter((opt) =>
|
|
397
|
-
currentValue.includes(opt.value),
|
|
398
|
-
);
|
|
399
|
-
onSelection(selectedOptions);
|
|
400
|
-
} else {
|
|
401
|
-
onSelection(selectedOption);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
</script>
|
|
408
|
-
|
|
409
|
-
<ScrollView
|
|
410
|
-
class={className}
|
|
411
|
-
style="--list-item-height: {effectiveItemHeight}px; height: {maxHeight()}px;"
|
|
412
|
-
{disabled}
|
|
413
|
-
showScrollbar={maxVisibleItems !== undefined}
|
|
414
|
-
bind:view={listElement}
|
|
415
|
-
>
|
|
416
|
-
<div
|
|
417
|
-
class="flex flex-col"
|
|
418
|
-
onkeydown={handleListKeydown}
|
|
419
|
-
onfocus={handleListFocus}
|
|
420
|
-
onblur={handleListBlur}
|
|
421
|
-
tabindex={disabled ? -1 : 0}
|
|
422
|
-
role="listbox"
|
|
423
|
-
aria-label="List of options"
|
|
424
|
-
aria-busy={isLoading}
|
|
425
|
-
aria-disabled={disabled}
|
|
426
|
-
aria-multiselectable={effectiveMultiSelect}
|
|
427
|
-
>
|
|
428
|
-
{#if isLoading && showLoading}
|
|
429
|
-
<div
|
|
430
|
-
class="flex items-center justify-center py-4 [color:var(--color-secondary)]"
|
|
431
|
-
>
|
|
432
|
-
<div
|
|
433
|
-
class="animate-spin rounded-full h-4 w-4 border-b-2 [border-color:var(--color-secondary)] mr-2"
|
|
434
|
-
></div>
|
|
435
|
-
{loadingText}
|
|
436
|
-
</div>
|
|
437
|
-
{:else if hasError && showError}
|
|
438
|
-
<div
|
|
439
|
-
class="flex items-center justify-center py-4 [color:var(--color-danger)]"
|
|
440
|
-
>
|
|
441
|
-
<span class="text-sm [color:var(--color-danger)]"
|
|
442
|
-
>{errorText}</span
|
|
443
|
-
>
|
|
444
|
-
</div>
|
|
445
|
-
{:else if children}
|
|
446
|
-
{@render children()}
|
|
447
|
-
{:else if allOptions().length === 0}
|
|
448
|
-
<div
|
|
449
|
-
class="flex items-center justify-center [color:var(--color-secondary)] select-none"
|
|
450
|
-
style="height: {effectiveItemHeight}px;"
|
|
451
|
-
>
|
|
452
|
-
<span class="px-4 text-sm [color:var(--color-secondary)]"
|
|
453
|
-
>{emptyText}</span
|
|
454
|
-
>
|
|
455
|
-
</div>
|
|
456
|
-
{:else}
|
|
457
|
-
<div class="text-sm [color:var(--color-secondary)] px-4 py-2">
|
|
458
|
-
No children provided. Use ListView with ListItem components or
|
|
459
|
-
provide a children snippet.
|
|
460
|
-
</div>
|
|
461
|
-
{/if}
|
|
462
|
-
</div>
|
|
463
|
-
</ScrollView>
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from "svelte";
|
|
2
|
-
type Option = Record<string, any> & {
|
|
3
|
-
value: string;
|
|
4
|
-
label: string;
|
|
5
|
-
};
|
|
6
|
-
interface Props {
|
|
7
|
-
options?: Option[];
|
|
8
|
-
value?: string | string[];
|
|
9
|
-
multiSelect?: boolean;
|
|
10
|
-
showCheckboxes?: boolean;
|
|
11
|
-
maxVisibleItems?: number;
|
|
12
|
-
itemHeight?: number;
|
|
13
|
-
minHeight?: number;
|
|
14
|
-
class?: string;
|
|
15
|
-
containerClass?: string;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
children?: Snippet;
|
|
18
|
-
url?: string;
|
|
19
|
-
searchParam?: string;
|
|
20
|
-
transform?: (apiResponse: any) => Option[];
|
|
21
|
-
debounceMs?: number;
|
|
22
|
-
maxResults?: number;
|
|
23
|
-
headers?: Record<string, string>;
|
|
24
|
-
searchTerm?: string;
|
|
25
|
-
showLoading?: boolean;
|
|
26
|
-
loadingText?: string;
|
|
27
|
-
showError?: boolean;
|
|
28
|
-
errorText?: string;
|
|
29
|
-
emptyText?: string;
|
|
30
|
-
onError?: (error: Error) => void;
|
|
31
|
-
onLoadStart?: () => void;
|
|
32
|
-
onLoadEnd?: () => void;
|
|
33
|
-
onSelection?: (option: Option | Option[]) => void;
|
|
34
|
-
}
|
|
35
|
-
declare const ListView: import("svelte").Component<Props, {}, "value" | "searchTerm">;
|
|
36
|
-
type ListView = ReturnType<typeof ListView>;
|
|
37
|
-
export default ListView;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BaseNode - Abstract base class for all node definitions
|
|
3
|
-
*
|
|
4
|
-
* All custom nodes must extend this class and implement the execute() method.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* @Node({
|
|
9
|
-
* title: 'Math Operation',
|
|
10
|
-
* color: '#10b981',
|
|
11
|
-
* executeInEditor: true
|
|
12
|
-
* })
|
|
13
|
-
* export class MathNode extends BaseNode {
|
|
14
|
-
* @Input({ label: 'A', dataType: 'number' })
|
|
15
|
-
* a: number = 0;
|
|
16
|
-
*
|
|
17
|
-
* @Input({ label: 'B', dataType: 'number' })
|
|
18
|
-
* b: number = 0;
|
|
19
|
-
*
|
|
20
|
-
* @Output({ label: 'Result', dataType: 'number' })
|
|
21
|
-
* result: number = 0;
|
|
22
|
-
*
|
|
23
|
-
* execute() {
|
|
24
|
-
* this.result = this.a + this.b;
|
|
25
|
-
* }
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export declare abstract class BaseNode {
|
|
30
|
-
/**
|
|
31
|
-
* Execute the node's logic
|
|
32
|
-
*
|
|
33
|
-
* - For data nodes: Update output properties based on input properties
|
|
34
|
-
* - For flow nodes: Perform side effects and optionally return the flow output to follow
|
|
35
|
-
*
|
|
36
|
-
* @returns void for normal execution, or string (flow output key) for branching nodes
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```typescript
|
|
40
|
-
* // Data node
|
|
41
|
-
* execute() {
|
|
42
|
-
* this.result = this.a + this.b;
|
|
43
|
-
* }
|
|
44
|
-
*
|
|
45
|
-
* // Flow node
|
|
46
|
-
* execute() {
|
|
47
|
-
* console.log(this.message);
|
|
48
|
-
* }
|
|
49
|
-
*
|
|
50
|
-
* // Branch node (multiple flow outputs)
|
|
51
|
-
* execute() {
|
|
52
|
-
* return this.condition ? 'onTrue' : 'onFalse';
|
|
53
|
-
* }
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
abstract execute(): void | string;
|
|
57
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BaseNode - Abstract base class for all node definitions
|
|
3
|
-
*
|
|
4
|
-
* All custom nodes must extend this class and implement the execute() method.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* @Node({
|
|
9
|
-
* title: 'Math Operation',
|
|
10
|
-
* color: '#10b981',
|
|
11
|
-
* executeInEditor: true
|
|
12
|
-
* })
|
|
13
|
-
* export class MathNode extends BaseNode {
|
|
14
|
-
* @Input({ label: 'A', dataType: 'number' })
|
|
15
|
-
* a: number = 0;
|
|
16
|
-
*
|
|
17
|
-
* @Input({ label: 'B', dataType: 'number' })
|
|
18
|
-
* b: number = 0;
|
|
19
|
-
*
|
|
20
|
-
* @Output({ label: 'Result', dataType: 'number' })
|
|
21
|
-
* result: number = 0;
|
|
22
|
-
*
|
|
23
|
-
* execute() {
|
|
24
|
-
* this.result = this.a + this.b;
|
|
25
|
-
* }
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export class BaseNode {
|
|
30
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
interface Props {
|
|
3
|
-
from: { x: number; y: number };
|
|
4
|
-
to: { x: number; y: number };
|
|
5
|
-
selected?: boolean;
|
|
6
|
-
color?: string;
|
|
7
|
-
animated?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let {
|
|
11
|
-
from,
|
|
12
|
-
to,
|
|
13
|
-
selected = false,
|
|
14
|
-
color = "var(--color-border)",
|
|
15
|
-
animated = false,
|
|
16
|
-
}: Props = $props();
|
|
17
|
-
|
|
18
|
-
// Calculate SVG path for bezier curve
|
|
19
|
-
const path = $derived.by(() => {
|
|
20
|
-
const dx = to.x - from.x;
|
|
21
|
-
const dy = to.y - from.y;
|
|
22
|
-
|
|
23
|
-
// Control point offset for smooth curves
|
|
24
|
-
const offset = Math.min(Math.abs(dx) * 0.5, 100);
|
|
25
|
-
|
|
26
|
-
return `M ${from.x} ${from.y} C ${from.x + offset} ${from.y}, ${to.x - offset} ${to.y}, ${to.x} ${to.y}`;
|
|
27
|
-
});
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<svg
|
|
31
|
-
class="edge absolute inset-0 pointer-events-none"
|
|
32
|
-
style="overflow: visible;"
|
|
33
|
-
>
|
|
34
|
-
<path
|
|
35
|
-
d={path}
|
|
36
|
-
fill="none"
|
|
37
|
-
stroke={selected ? "var(--color-primary)" : color}
|
|
38
|
-
stroke-width={selected ? "3" : "2"}
|
|
39
|
-
class:animated
|
|
40
|
-
/>
|
|
41
|
-
</svg>
|
|
42
|
-
|
|
43
|
-
<style>
|
|
44
|
-
.edge path {
|
|
45
|
-
transition:
|
|
46
|
-
stroke 0.2s,
|
|
47
|
-
stroke-width 0.2s;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.edge path.animated {
|
|
51
|
-
stroke-dasharray: 5, 5;
|
|
52
|
-
animation: dash 1s linear infinite;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@keyframes dash {
|
|
56
|
-
to {
|
|
57
|
-
stroke-dashoffset: -10;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
</style>
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
interface Props {
|
|
2
|
-
from: {
|
|
3
|
-
x: number;
|
|
4
|
-
y: number;
|
|
5
|
-
};
|
|
6
|
-
to: {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
};
|
|
10
|
-
selected?: boolean;
|
|
11
|
-
color?: string;
|
|
12
|
-
animated?: boolean;
|
|
13
|
-
}
|
|
14
|
-
declare const Edge: import("svelte").Component<Props, {}, "">;
|
|
15
|
-
type Edge = ReturnType<typeof Edge>;
|
|
16
|
-
export default Edge;
|