@d34dman/flowdrop 0.0.1 → 0.0.2
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 +307 -215
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +30 -30
- package/dist/api/client.d.ts +24 -1
- package/dist/api/client.js +55 -38
- package/dist/api/enhanced-client.d.ts +46 -0
- package/dist/api/enhanced-client.js +211 -0
- package/dist/clients/ApiClient.d.ts +19 -23
- package/dist/clients/ApiClient.js +36 -34
- package/dist/components/App.svelte +1299 -230
- package/dist/components/App.svelte.d.ts +21 -1
- package/dist/components/CanvasBanner.svelte +50 -44
- package/dist/components/CanvasBanner.svelte.d.ts +5 -19
- package/dist/components/ConfigForm.svelte +555 -0
- package/dist/components/ConfigForm.svelte.d.ts +32 -0
- package/dist/components/ConfigModal.svelte +261 -0
- package/dist/components/ConfigModal.svelte.d.ts +31 -0
- package/dist/components/ConfigSidebar.svelte +934 -0
- package/dist/components/ConfigSidebar.svelte.d.ts +51 -0
- package/dist/components/ConnectionLine.svelte +32 -0
- package/dist/components/ConnectionLine.svelte.d.ts +3 -0
- package/dist/components/GatewayNode.svelte +471 -0
- package/dist/components/GatewayNode.svelte.d.ts +15 -0
- package/dist/components/LoadingSpinner.svelte +23 -23
- package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
- package/dist/components/Logo.svelte +82 -0
- package/dist/components/Logo.svelte.d.ts +26 -0
- package/dist/components/LogsSidebar.svelte +565 -0
- package/dist/components/LogsSidebar.svelte.d.ts +34 -0
- package/dist/components/MarkdownDisplay.svelte +28 -0
- package/dist/components/MarkdownDisplay.svelte.d.ts +7 -0
- package/dist/components/Navbar.svelte +663 -0
- package/dist/components/Navbar.svelte.d.ts +21 -0
- package/dist/components/NodeSidebar.svelte +629 -488
- package/dist/components/NodeSidebar.svelte.d.ts +1 -2
- package/dist/components/NodeStatusOverlay.svelte +327 -0
- package/dist/components/NodeStatusOverlay.svelte.d.ts +11 -0
- package/dist/components/NotesNode.svelte +566 -0
- package/dist/components/NotesNode.svelte.d.ts +43 -0
- package/dist/components/PipelineStatus.svelte +331 -0
- package/dist/components/PipelineStatus.svelte.d.ts +18 -0
- package/dist/components/SimpleNode.svelte +447 -0
- package/dist/components/SimpleNode.svelte.d.ts +24 -0
- package/dist/components/SquareNode.svelte +346 -0
- package/dist/components/SquareNode.svelte.d.ts +24 -0
- package/dist/components/StatusIcon.svelte +112 -0
- package/dist/components/StatusIcon.svelte.d.ts +10 -0
- package/dist/components/StatusLabel.svelte +33 -0
- package/dist/components/StatusLabel.svelte.d.ts +7 -0
- package/dist/components/ToolNode.svelte +385 -0
- package/dist/components/ToolNode.svelte.d.ts +36 -0
- package/dist/components/UniversalNode.svelte +126 -0
- package/dist/components/UniversalNode.svelte.d.ts +15 -0
- package/dist/components/WorkflowEditor.svelte +871 -528
- package/dist/components/WorkflowEditor.svelte.d.ts +15 -5
- package/dist/components/WorkflowNode.svelte +428 -542
- package/dist/components/WorkflowNode.svelte.d.ts +7 -3
- package/dist/config/apiConfig.d.ts +33 -0
- package/dist/config/apiConfig.js +39 -0
- package/dist/config/defaultPortConfig.d.ts +6 -0
- package/dist/config/defaultPortConfig.js +192 -0
- package/dist/config/demo.d.ts +58 -0
- package/dist/config/demo.js +142 -0
- package/dist/config/endpoints.d.ts +106 -0
- package/dist/config/endpoints.js +128 -0
- package/dist/data/samples.d.ts +38 -4
- package/dist/data/samples.js +2789 -737
- package/dist/examples/adapter-usage.d.ts +4 -4
- package/dist/examples/adapter-usage.js +21 -26
- package/dist/examples/api-client-usage.d.ts +6 -6
- package/dist/examples/api-client-usage.js +55 -54
- package/dist/index.d.ts +23 -15
- package/dist/index.js +23 -15
- package/dist/mocks/app-environment.d.ts +8 -0
- package/dist/mocks/app-environment.js +16 -0
- package/dist/mocks/app-forms.d.ts +2 -0
- package/dist/mocks/app-forms.js +21 -0
- package/dist/mocks/app-navigation.d.ts +5 -0
- package/dist/mocks/app-navigation.js +34 -0
- package/dist/mocks/app-stores.d.ts +14 -0
- package/dist/mocks/app-stores.js +26 -0
- package/dist/services/api.d.ts +13 -3
- package/dist/services/api.js +91 -36
- package/dist/services/globalSave.d.ts +20 -0
- package/dist/services/globalSave.js +165 -0
- package/dist/services/nodeExecutionService.d.ts +63 -0
- package/dist/services/nodeExecutionService.js +261 -0
- package/dist/services/portConfigApi.d.ts +14 -0
- package/dist/services/portConfigApi.js +69 -0
- package/dist/services/toastService.d.ts +147 -0
- package/dist/services/toastService.js +235 -0
- package/dist/services/workflowStorage.d.ts +2 -2
- package/dist/services/workflowStorage.js +10 -10
- package/dist/stores/workflowStore.d.ts +53 -0
- package/dist/stores/workflowStore.js +264 -0
- package/dist/styles/base.css +896 -363
- package/dist/svelte-app.d.ts +52 -5
- package/dist/svelte-app.js +128 -6
- package/dist/types/config.d.ts +291 -0
- package/dist/types/config.js +4 -0
- package/dist/types/index.d.ts +231 -19
- package/dist/types/index.js +1 -1
- package/dist/utils/colors.d.ts +67 -33
- package/dist/utils/colors.js +183 -118
- package/dist/utils/config.d.ts +41 -0
- package/dist/utils/config.js +248 -0
- package/dist/utils/connections.d.ts +40 -3
- package/dist/utils/connections.js +115 -44
- package/dist/utils/icons.d.ts +1 -1
- package/dist/utils/icons.js +71 -70
- package/dist/utils/nodeStatus.d.ts +53 -0
- package/dist/utils/nodeStatus.js +183 -0
- package/dist/utils/nodeTypes.d.ts +57 -0
- package/dist/utils/nodeTypes.js +109 -0
- package/dist/utils/nodeWrapper.d.ts +39 -0
- package/dist/utils/nodeWrapper.js +62 -0
- package/package.json +129 -97
- package/dist/components/Node.svelte +0 -38
- package/dist/components/Node.svelte.d.ts +0 -4
|
@@ -5,496 +5,637 @@
|
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
7
|
<script lang="ts">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return nodes;
|
|
180
|
-
}
|
|
8
|
+
import type { NodeMetadata, NodeCategory } from '../types/index.js';
|
|
9
|
+
import LoadingSpinner from './LoadingSpinner.svelte';
|
|
10
|
+
import Icon from '@iconify/svelte';
|
|
11
|
+
import { getNodeIcon, getCategoryIcon } from '../utils/icons.js';
|
|
12
|
+
import { getCategoryColorToken } from '../utils/colors.js';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
nodes: NodeMetadata[];
|
|
16
|
+
selectedCategory?: NodeCategory;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let props: Props = $props();
|
|
20
|
+
let searchInput = $state('');
|
|
21
|
+
let selectedCategory = $state(props.selectedCategory || 'all');
|
|
22
|
+
|
|
23
|
+
let filteredNodes = $derived(getFilteredNodes());
|
|
24
|
+
let categories = $derived(getCategories());
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get all unique categories from node types
|
|
28
|
+
*/
|
|
29
|
+
function getCategories(): NodeCategory[] {
|
|
30
|
+
const nodes = props.nodes || [];
|
|
31
|
+
if (nodes.length === 0) return [];
|
|
32
|
+
const categories = new Set<NodeCategory>();
|
|
33
|
+
nodes.forEach((node) => categories.add(node.category));
|
|
34
|
+
return Array.from(categories).sort();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Filter node types based on search query and selected category
|
|
39
|
+
*/
|
|
40
|
+
function getFilteredNodes(): NodeMetadata[] {
|
|
41
|
+
// Use actual node types from props
|
|
42
|
+
let filtered = props.nodes || [];
|
|
43
|
+
|
|
44
|
+
// Filter by category
|
|
45
|
+
if (selectedCategory !== 'all') {
|
|
46
|
+
filtered = filtered.filter((node) => node.category === selectedCategory);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Filter by search query
|
|
50
|
+
if (searchInput.trim()) {
|
|
51
|
+
const query = searchInput.toLowerCase();
|
|
52
|
+
filtered = filtered.filter(
|
|
53
|
+
(node) =>
|
|
54
|
+
node.name.toLowerCase().includes(query) ||
|
|
55
|
+
node.description.toLowerCase().includes(query) ||
|
|
56
|
+
node.tags?.some((tag) => tag.toLowerCase().includes(query))
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Create a new array and sort it to avoid mutating the original
|
|
61
|
+
return [...filtered].sort((a, b) => a.name.localeCompare(b.name));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Handle node type drag start - creates a new node instance
|
|
66
|
+
*/
|
|
67
|
+
function handleNodeDragStart(event: DragEvent, nodeType: NodeMetadata): void {
|
|
68
|
+
if (!event.dataTransfer) return;
|
|
69
|
+
|
|
70
|
+
// Extract initial config from configSchema with proper null checks
|
|
71
|
+
let initialConfig = {};
|
|
72
|
+
if (
|
|
73
|
+
nodeType.configSchema &&
|
|
74
|
+
typeof nodeType.configSchema === 'object' &&
|
|
75
|
+
nodeType.configSchema.properties &&
|
|
76
|
+
typeof nodeType.configSchema.properties === 'object'
|
|
77
|
+
) {
|
|
78
|
+
// JSON Schema format - extract defaults
|
|
79
|
+
Object.entries(nodeType.configSchema.properties).forEach(([key, prop]) => {
|
|
80
|
+
if (prop && typeof prop === 'object' && 'default' in prop) {
|
|
81
|
+
initialConfig[key] = prop.default;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Create a new node instance from the node type
|
|
87
|
+
const newNodeData = {
|
|
88
|
+
type: 'node',
|
|
89
|
+
nodeType: nodeType.id,
|
|
90
|
+
nodeData: {
|
|
91
|
+
label: nodeType.name,
|
|
92
|
+
config: initialConfig,
|
|
93
|
+
metadata: nodeType
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const jsonData = JSON.stringify(newNodeData);
|
|
98
|
+
|
|
99
|
+
// Set the data that SvelteFlow will receive
|
|
100
|
+
event.dataTransfer.setData('application/json', jsonData);
|
|
101
|
+
event.dataTransfer.setData('text/plain', nodeType.name);
|
|
102
|
+
event.dataTransfer.effectAllowed = 'copy';
|
|
103
|
+
|
|
104
|
+
// Set drag image
|
|
105
|
+
if (event.target) {
|
|
106
|
+
const rect = (event.target as HTMLElement).getBoundingClientRect();
|
|
107
|
+
event.dataTransfer.setDragImage(event.target as HTMLElement, rect.width / 2, rect.height / 2);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Handle search input change
|
|
113
|
+
*/
|
|
114
|
+
function handleSearchChange(): void {
|
|
115
|
+
// Search is handled reactively through the derived filteredNodes
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handle node click
|
|
120
|
+
*/
|
|
121
|
+
function handleNodeClick(nodeType: NodeMetadata): void {
|
|
122
|
+
// Handle node click - could be used for preview or configuration
|
|
123
|
+
console.log('Node clicked:', nodeType.name);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get category display name
|
|
128
|
+
*/
|
|
129
|
+
function getCategoryDisplayName(category: NodeCategory): string {
|
|
130
|
+
const names: Record<NodeCategory, string> = {
|
|
131
|
+
inputs: 'Inputs',
|
|
132
|
+
outputs: 'Outputs',
|
|
133
|
+
prompts: 'Prompts',
|
|
134
|
+
models: 'Models',
|
|
135
|
+
processing: 'Processing',
|
|
136
|
+
logic: 'Logic',
|
|
137
|
+
data: 'Data',
|
|
138
|
+
tools: 'Tools',
|
|
139
|
+
helpers: 'Helpers',
|
|
140
|
+
'vector stores': 'Vector Stores',
|
|
141
|
+
embeddings: 'Embeddings',
|
|
142
|
+
memories: 'Memories',
|
|
143
|
+
agents: 'Agents',
|
|
144
|
+
ai: 'AI',
|
|
145
|
+
bundles: 'Bundles'
|
|
146
|
+
};
|
|
147
|
+
return names[category] || category;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get node types for category
|
|
152
|
+
*/
|
|
153
|
+
function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
|
|
154
|
+
const nodes = props.nodes || [];
|
|
155
|
+
return [...nodes]
|
|
156
|
+
.filter((node) => node.category === category)
|
|
157
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get filtered nodes for category
|
|
162
|
+
*/
|
|
163
|
+
function getFilteredNodesForCategory(category: NodeCategory): NodeMetadata[] {
|
|
164
|
+
let nodes = getNodesForCategory(category);
|
|
165
|
+
|
|
166
|
+
// Filter by search query
|
|
167
|
+
if (searchInput.trim()) {
|
|
168
|
+
const query = searchInput.toLowerCase();
|
|
169
|
+
nodes = nodes.filter(
|
|
170
|
+
(node) =>
|
|
171
|
+
node.name.toLowerCase().includes(query) ||
|
|
172
|
+
node.description.toLowerCase().includes(query) ||
|
|
173
|
+
node.tags?.some((tag) => tag.toLowerCase().includes(query))
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return nodes;
|
|
178
|
+
}
|
|
181
179
|
</script>
|
|
182
180
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
181
|
+
<!-- Components Sidebar - Always Visible -->
|
|
182
|
+
<div
|
|
183
|
+
class="flowdrop-sidebar flowdrop-sidebar--container"
|
|
184
|
+
role="complementary"
|
|
185
|
+
aria-label="Components sidebar"
|
|
186
|
+
>
|
|
187
|
+
<!-- Header -->
|
|
188
|
+
<div class="flowdrop-sidebar__header">
|
|
189
|
+
<div class="flowdrop-sidebar__title">
|
|
190
|
+
<h2 class="flowdrop-text--lg flowdrop-font--bold">Components</h2>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<!-- Search Section -->
|
|
195
|
+
<div class="flowdrop-sidebar__search">
|
|
196
|
+
<div class="flowdrop-join flowdrop-w--full">
|
|
197
|
+
<div class="flowdrop-join__item flowdrop-flex--1">
|
|
198
|
+
<input
|
|
199
|
+
type="text"
|
|
200
|
+
placeholder="Search components..."
|
|
201
|
+
class="flowdrop-input flowdrop-join__item flowdrop-w--full"
|
|
202
|
+
bind:value={searchInput}
|
|
203
|
+
oninput={handleSearchChange}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
<button class="flowdrop-btn flowdrop-join__item" aria-label="Search components">
|
|
207
|
+
<Icon icon="mdi:magnify" class="flowdrop-icon" />
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- Node Types List -->
|
|
213
|
+
<div class="flowdrop-sidebar__content">
|
|
214
|
+
{#if props.nodes?.length === 0}
|
|
215
|
+
<!-- No node types available -->
|
|
216
|
+
<div class="flowdrop-hero">
|
|
217
|
+
<div class="flowdrop-hero__content">
|
|
218
|
+
<div class="flowdrop-hero__icon">📦</div>
|
|
219
|
+
<h3 class="flowdrop-hero__title">No node types available</h3>
|
|
220
|
+
<p class="flowdrop-hero__description">Node type definitions will appear here</p>
|
|
221
|
+
<div class="flowdrop-mb--4">
|
|
222
|
+
<LoadingSpinner size="md" text="Loading from server..." />
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
{:else if searchInput.trim()}
|
|
227
|
+
<!-- Search Results -->
|
|
228
|
+
<div class="flowdrop-p--4">
|
|
229
|
+
<div class="flowdrop-divider">
|
|
230
|
+
<h3 class="flowdrop-divider__text">Search Results</h3>
|
|
231
|
+
</div>
|
|
232
|
+
{#if filteredNodes.length === 0}
|
|
233
|
+
<div class="flowdrop-hero">
|
|
234
|
+
<div class="flowdrop-hero__content">
|
|
235
|
+
<div class="flowdrop-hero__icon">🔍</div>
|
|
236
|
+
<h3 class="flowdrop-hero__title">No components found</h3>
|
|
237
|
+
<p class="flowdrop-hero__description">Try adjusting your search</p>
|
|
238
|
+
{#if props.nodes?.length === 0}
|
|
239
|
+
<div class="flowdrop-mb--4">
|
|
240
|
+
<LoadingSpinner size="sm" text="Loading components..." />
|
|
241
|
+
</div>
|
|
242
|
+
{/if}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
{:else}
|
|
246
|
+
<div class="flowdrop-node-list">
|
|
247
|
+
{#each filteredNodes as nodeType (nodeType.id)}
|
|
248
|
+
<div
|
|
249
|
+
class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
|
|
250
|
+
draggable="true"
|
|
251
|
+
ondragstart={(e) => handleNodeDragStart(e, nodeType)}
|
|
252
|
+
role="button"
|
|
253
|
+
tabindex="0"
|
|
254
|
+
>
|
|
255
|
+
<div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
|
|
256
|
+
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
|
|
257
|
+
<!-- Node Type Icon -->
|
|
258
|
+
<div
|
|
259
|
+
class="flowdrop-node-icon"
|
|
260
|
+
style="background-color: {getCategoryColorToken(nodeType.category)}"
|
|
261
|
+
>
|
|
262
|
+
<Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- Node Type Info - Icon and Title only -->
|
|
266
|
+
<h4
|
|
267
|
+
class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
|
|
268
|
+
>
|
|
269
|
+
{nodeType.name}
|
|
270
|
+
</h4>
|
|
271
|
+
</div>
|
|
272
|
+
<p
|
|
273
|
+
class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
|
|
274
|
+
>
|
|
275
|
+
{nodeType.description}
|
|
276
|
+
</p>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
{/each}
|
|
280
|
+
</div>
|
|
281
|
+
{/if}
|
|
282
|
+
</div>
|
|
283
|
+
{:else}
|
|
284
|
+
<!-- Show categories with details when no search is active -->
|
|
285
|
+
<div class="flowdrop-p--4">
|
|
286
|
+
<!-- Category-specific details -->
|
|
287
|
+
<div class="flowdrop-category-list">
|
|
288
|
+
{#each categories as category (category)}
|
|
289
|
+
{@const categoryNodes = getFilteredNodesForCategory(category)}
|
|
290
|
+
{#if categoryNodes.length > 0}
|
|
291
|
+
<details class="flowdrop-details">
|
|
292
|
+
<summary class="flowdrop-details__summary">
|
|
293
|
+
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
|
|
294
|
+
<span
|
|
295
|
+
class="flowdrop-node-icon"
|
|
296
|
+
style="background-color: {getCategoryColorToken(category)}"
|
|
297
|
+
>
|
|
298
|
+
<Icon icon={getCategoryIcon(category)} />
|
|
299
|
+
</span>
|
|
300
|
+
<span>{getCategoryDisplayName(category)}</span>
|
|
301
|
+
</div>
|
|
302
|
+
<div class="flowdrop-badge flowdrop-badge--secondary">{categoryNodes.length}</div>
|
|
303
|
+
</summary>
|
|
304
|
+
<div class="flowdrop-details__content">
|
|
305
|
+
<div class="flowdrop-node-list">
|
|
306
|
+
{#each categoryNodes as nodeType (nodeType.id)}
|
|
307
|
+
<div
|
|
308
|
+
class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
|
|
309
|
+
draggable="true"
|
|
310
|
+
ondragstart={(e) => handleNodeDragStart(e, nodeType)}
|
|
311
|
+
onclick={() => handleNodeClick(nodeType)}
|
|
312
|
+
role="button"
|
|
313
|
+
tabindex="0"
|
|
314
|
+
onkeydown={(e) => {
|
|
315
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
316
|
+
e.preventDefault();
|
|
317
|
+
handleNodeClick(nodeType);
|
|
318
|
+
}
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
<div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
|
|
322
|
+
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
|
|
323
|
+
<!-- Node Type Icon -->
|
|
324
|
+
<div
|
|
325
|
+
class="flowdrop-node-icon"
|
|
326
|
+
style="background-color: {getCategoryColorToken(nodeType.category)}"
|
|
327
|
+
>
|
|
328
|
+
<Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<!-- Node Type Info - Icon and Title only -->
|
|
332
|
+
<h4
|
|
333
|
+
class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
|
|
334
|
+
>
|
|
335
|
+
{nodeType.name}
|
|
336
|
+
</h4>
|
|
337
|
+
</div>
|
|
338
|
+
<p
|
|
339
|
+
class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
|
|
340
|
+
>
|
|
341
|
+
{nodeType.description}
|
|
342
|
+
</p>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
{/each}
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</details>
|
|
349
|
+
{/if}
|
|
350
|
+
{/each}
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
{/if}
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<!-- Footer -->
|
|
357
|
+
<div class="flowdrop-sidebar__footer">
|
|
358
|
+
<div class="flowdrop-flex flowdrop-gap--4">
|
|
359
|
+
<div class="flowdrop-flex flowdrop-gap--4">
|
|
360
|
+
{#if props.nodes?.length === 0}
|
|
361
|
+
<span class="flowdrop-text--xs flowdrop-text--gray">Loading components...</span>
|
|
362
|
+
{:else}
|
|
363
|
+
<span class="flowdrop-text--xs flowdrop-text--gray"
|
|
364
|
+
>Total: {props.nodes?.length || 0} components</span
|
|
365
|
+
>
|
|
366
|
+
<span class="flowdrop-text--xs flowdrop-text--gray">Showing: {filteredNodes.length}</span>
|
|
367
|
+
{/if}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
359
371
|
</div>
|
|
360
372
|
|
|
361
373
|
<style>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
374
|
+
/* Components Sidebar - Always Visible */
|
|
375
|
+
.flowdrop-sidebar {
|
|
376
|
+
width: 320px;
|
|
377
|
+
height: calc(100vh - var(--flowdrop-navbar-height, 60px)); /* Account for navbar height */
|
|
378
|
+
background-color: #ffffff;
|
|
379
|
+
border-right: 1px solid #e5e7eb;
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|
383
|
+
flex-shrink: 0;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.flowdrop-sidebar--container {
|
|
387
|
+
display: flex;
|
|
388
|
+
flex-direction: column;
|
|
389
|
+
height: 100%;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.flowdrop-sidebar__header {
|
|
393
|
+
background-color: #f8fafc;
|
|
394
|
+
border-bottom: 1px solid #e5e7eb;
|
|
395
|
+
padding: 0.75rem 1rem;
|
|
396
|
+
display: flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
justify-content: space-between;
|
|
399
|
+
flex-shrink: 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.flowdrop-sidebar__title {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* Close button styles removed - no longer needed */
|
|
408
|
+
|
|
409
|
+
.flowdrop-sidebar__title h2 {
|
|
410
|
+
font-size: 1rem;
|
|
411
|
+
font-weight: 600;
|
|
412
|
+
margin: 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.flowdrop-sidebar__search {
|
|
416
|
+
padding: 0.75rem 1rem;
|
|
417
|
+
background-color: #ffffff;
|
|
418
|
+
border-bottom: 1px solid #e5e7eb;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.flowdrop-sidebar__content {
|
|
422
|
+
flex: 1;
|
|
423
|
+
overflow-y: scroll; /* Changed from auto to scroll to always show scrollbar */
|
|
424
|
+
scrollbar-width: thin;
|
|
425
|
+
scrollbar-color: #d1d5db #f3f4f6;
|
|
426
|
+
padding-bottom: 4rem; /* Add padding to ensure content is scrollable above footer */
|
|
427
|
+
min-height: 0; /* Allow flex item to shrink below content size */
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.flowdrop-sidebar__content::-webkit-scrollbar {
|
|
431
|
+
width: 8px;
|
|
432
|
+
display: block; /* Ensure scrollbar is always visible */
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.flowdrop-sidebar__content::-webkit-scrollbar-track {
|
|
436
|
+
background: #f3f4f6;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.flowdrop-sidebar__content::-webkit-scrollbar-thumb {
|
|
440
|
+
background: #d1d5db;
|
|
441
|
+
border-radius: 4px;
|
|
442
|
+
min-height: 20px; /* Ensure thumb has minimum height for visibility */
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.flowdrop-sidebar__content::-webkit-scrollbar-thumb:hover {
|
|
446
|
+
background: #9ca3af;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.flowdrop-sidebar__footer {
|
|
450
|
+
background-color: rgba(255, 255, 255, 0.8);
|
|
451
|
+
backdrop-filter: blur(8px);
|
|
452
|
+
border-top: 1px solid #e5e7eb;
|
|
453
|
+
padding: 0.5rem 0.75rem;
|
|
454
|
+
flex-shrink: 0; /* Prevent footer from shrinking */
|
|
455
|
+
position: relative;
|
|
456
|
+
z-index: 10; /* Ensure footer stays on top */
|
|
457
|
+
height: 40px;
|
|
458
|
+
display: flex;
|
|
459
|
+
align-items: center;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.flowdrop-node-list {
|
|
463
|
+
display: flex;
|
|
464
|
+
flex-direction: column;
|
|
465
|
+
gap: 0.375rem;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.flowdrop-node-item {
|
|
469
|
+
cursor: grab;
|
|
470
|
+
transition: all 0.2s ease-in-out;
|
|
471
|
+
border-radius: 0.375rem;
|
|
472
|
+
border: 1px solid transparent;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.flowdrop-node-item:hover {
|
|
476
|
+
border-color: #d1d5db;
|
|
477
|
+
background-color: #f9fafb;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.flowdrop-node-item:active {
|
|
481
|
+
cursor: grabbing;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.flowdrop-node-icon {
|
|
485
|
+
width: 2rem;
|
|
486
|
+
height: 2rem;
|
|
487
|
+
border-radius: 0.375rem;
|
|
488
|
+
color: #ffffff;
|
|
489
|
+
font-size: 0.875rem;
|
|
490
|
+
display: flex;
|
|
491
|
+
align-items: center;
|
|
492
|
+
justify-content: center;
|
|
493
|
+
flex-shrink: 0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.flowdrop-category-list {
|
|
497
|
+
display: flex;
|
|
498
|
+
flex-direction: column;
|
|
499
|
+
gap: 0.375rem;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.flowdrop-items--center {
|
|
503
|
+
align-items: center;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.flowdrop-truncate {
|
|
507
|
+
overflow: hidden;
|
|
508
|
+
text-overflow: ellipsis;
|
|
509
|
+
white-space: nowrap;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.flowdrop-card__body h4 {
|
|
513
|
+
margin: 0;
|
|
514
|
+
line-height: 1;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.flowdrop-p--4 {
|
|
518
|
+
padding: 1rem;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.flowdrop-py--1 {
|
|
522
|
+
padding-top: 0.25rem;
|
|
523
|
+
padding-bottom: 0.25rem;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.flowdrop-ml--0 {
|
|
527
|
+
margin-left: 0;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Form Elements - Matching App Design System */
|
|
531
|
+
.flowdrop-input {
|
|
532
|
+
padding: 0.625rem 0.75rem;
|
|
533
|
+
border: 1px solid #d1d5db;
|
|
534
|
+
border-radius: 0.375rem;
|
|
535
|
+
font-size: 0.875rem;
|
|
536
|
+
color: #111827;
|
|
537
|
+
background-color: #ffffff;
|
|
538
|
+
transition:
|
|
539
|
+
border-color 0.2s ease-in-out,
|
|
540
|
+
box-shadow 0.2s ease-in-out;
|
|
541
|
+
width: 100%;
|
|
542
|
+
height: 2.5rem;
|
|
543
|
+
box-sizing: border-box;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.flowdrop-input:focus {
|
|
547
|
+
outline: none;
|
|
548
|
+
border-color: #6b7280;
|
|
549
|
+
box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.1);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.flowdrop-input::placeholder {
|
|
553
|
+
color: #9ca3af;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.flowdrop-btn {
|
|
557
|
+
padding: 0.625rem 0.75rem;
|
|
558
|
+
border-radius: 0.375rem;
|
|
559
|
+
font-size: 0.875rem;
|
|
560
|
+
font-weight: 500;
|
|
561
|
+
cursor: pointer;
|
|
562
|
+
transition: all 0.2s ease-in-out;
|
|
563
|
+
border: 1px solid #d1d5db;
|
|
564
|
+
display: flex;
|
|
565
|
+
align-items: center;
|
|
566
|
+
justify-content: center;
|
|
567
|
+
gap: 0.5rem;
|
|
568
|
+
background-color: #f9fafb;
|
|
569
|
+
color: #374151;
|
|
570
|
+
height: 2.5rem;
|
|
571
|
+
min-width: 2.5rem;
|
|
572
|
+
box-sizing: border-box;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.flowdrop-btn:hover {
|
|
576
|
+
background-color: #f3f4f6;
|
|
577
|
+
border-color: #9ca3af;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.flowdrop-btn:active {
|
|
581
|
+
background-color: #e5e7eb;
|
|
582
|
+
border-color: #6b7280;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.flowdrop-btn:focus {
|
|
586
|
+
outline: none;
|
|
587
|
+
box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.1);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.flowdrop-btn:disabled {
|
|
591
|
+
background-color: #9ca3af;
|
|
592
|
+
border-color: #9ca3af;
|
|
593
|
+
cursor: not-allowed;
|
|
594
|
+
opacity: 0.6;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/* Join component styles - Seamless integration */
|
|
598
|
+
.flowdrop-join {
|
|
599
|
+
display: flex;
|
|
600
|
+
width: 100%;
|
|
601
|
+
border-radius: 0.375rem;
|
|
602
|
+
overflow: hidden;
|
|
603
|
+
border: 1px solid #d1d5db;
|
|
604
|
+
background-color: #ffffff;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.flowdrop-join__item {
|
|
608
|
+
border: none;
|
|
609
|
+
border-radius: 0;
|
|
610
|
+
background-color: transparent;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.flowdrop-join__item:first-child {
|
|
614
|
+
border-right: 1px solid #d1d5db;
|
|
615
|
+
flex: 1;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.flowdrop-join__item:last-child {
|
|
619
|
+
border-left: none;
|
|
620
|
+
background-color: #f9fafb;
|
|
621
|
+
color: #374151;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.flowdrop-join__item:last-child:hover {
|
|
625
|
+
background-color: #f3f4f6;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.flowdrop-join:focus-within {
|
|
629
|
+
border-color: #6b7280;
|
|
630
|
+
box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.1);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Utility classes */
|
|
634
|
+
.flowdrop-w--full {
|
|
635
|
+
width: 100%;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.flowdrop-flex--1 {
|
|
639
|
+
flex: 1;
|
|
640
|
+
}
|
|
641
|
+
</style>
|