@industream/flowmaker-flowbox-ui-components 0.0.15 → 0.0.17

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.
@@ -68,6 +68,22 @@
68
68
  let client: DataCatalogClient | null = null;
69
69
  let clientUrl = '';
70
70
 
71
+ /**
72
+ * Deduplicate entries by id — keep only the first occurrence.
73
+ */
74
+ function deduplicateEntries(items: CatalogEntry[]): CatalogEntry[] {
75
+ const seen = new Set<string>();
76
+ const result: CatalogEntry[] = [];
77
+ for (const item of items) {
78
+ const id = item.id;
79
+ if (!seen.has(id)) {
80
+ seen.add(id);
81
+ result.push(item);
82
+ }
83
+ }
84
+ return result;
85
+ }
86
+
71
87
  function getClient(apiUrl: string): DataCatalogClient {
72
88
  if (client && clientUrl === apiUrl) return client;
73
89
  client = new DataCatalogClient({ baseUrl: apiUrl });
@@ -76,7 +92,7 @@
76
92
  }
77
93
 
78
94
  $effect(() => {
79
- if (dcApiUrl && sourceConnectionId) {
95
+ if (dcApiUrl) {
80
96
  loadInitial(dcApiUrl, sourceConnectionId);
81
97
  } else {
82
98
  entries = [];
@@ -90,7 +106,7 @@
90
106
  * If ≤ MAX_ENTRIES items, stay in client mode.
91
107
  * If more, switch to server search mode.
92
108
  */
93
- async function loadInitial(apiUrl: string, connId: string) {
109
+ async function loadInitial(apiUrl: string, connId?: string) {
94
110
  // Cancel any in-flight search
95
111
  searchAbort?.abort();
96
112
  searchAbort = null;
@@ -103,18 +119,19 @@
103
119
  try {
104
120
  const dc = getClient(apiUrl);
105
121
  const result = await dc.catalogEntries.search('%', {
106
- sourceConnectionIds: [connId],
122
+ sourceConnectionIds: connId ? [connId] : [],
107
123
  limit: MAX_ENTRIES + 1
108
124
  });
109
125
  const items = result.items ?? [];
126
+ const uniqueItems = deduplicateEntries(items);
110
127
 
111
- if (items.length > MAX_ENTRIES) {
128
+ if (uniqueItems.length > MAX_ENTRIES) {
112
129
  // Too many — enter server search mode, don't show entries
113
130
  tooMany = true;
114
131
  entries = [];
115
132
  } else {
116
133
  tooMany = false;
117
- entries = items;
134
+ entries = uniqueItems;
118
135
  }
119
136
  } catch (err: any) {
120
137
  error = err.message ?? 'Failed to load catalog entries';
@@ -129,7 +146,7 @@
129
146
  * Cancels previous in-flight request via AbortSignal.
130
147
  */
131
148
  function handleServerSearch(query: string) {
132
- if (!dcApiUrl || !sourceConnectionId) return;
149
+ if (!dcApiUrl) return;
133
150
 
134
151
  // Cancel previous in-flight search
135
152
  searchAbort?.abort();
@@ -147,12 +164,13 @@
147
164
 
148
165
  const dc = getClient(dcApiUrl);
149
166
  dc.catalogEntries.search(query, {
150
- sourceConnectionIds: [sourceConnectionId],
167
+ sourceConnectionIds: sourceConnectionId ? [sourceConnectionId] : [],
151
168
  limit: MAX_ENTRIES
152
169
  }, abort.signal)
153
170
  .then((result) => {
154
171
  if (abort.signal.aborted) return;
155
- entries = result.items ?? [];
172
+ const items = result.items ?? [];
173
+ entries = deduplicateEntries(items);
156
174
  searching = false;
157
175
  })
158
176
  .catch((err) => {
@@ -212,7 +212,7 @@
212
212
  }
213
213
  @keyframes spin { to { transform: rotate(360deg); } }
214
214
  .table-container {
215
- max-height: 400px; overflow-y: auto;
215
+ max-height: 400px; overflow-y: auto; overflow-x: auto;
216
216
  border: 1px solid var(--cds-border-subtle-01, #393939);
217
217
  }
218
218
  .entry-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
@@ -242,6 +242,8 @@
242
242
  code {
243
243
  font-family: 'IBM Plex Mono', monospace; font-size: 0.75rem;
244
244
  color: var(--cds-text-secondary, #c6c6c6);
245
+ display: inline-block; max-width: 100%;
246
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
245
247
  }
246
248
  .pill, .label-pill {
247
249
  display: inline-block; padding: 0.0625rem 0.375rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@industream/flowmaker-flowbox-ui-components",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Reusable Svelte components for FlowMaker FlowBox UI",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",
@@ -68,6 +68,22 @@
68
68
  let client: DataCatalogClient | null = null;
69
69
  let clientUrl = '';
70
70
 
71
+ /**
72
+ * Deduplicate entries by id — keep only the first occurrence.
73
+ */
74
+ function deduplicateEntries(items: CatalogEntry[]): CatalogEntry[] {
75
+ const seen = new Set<string>();
76
+ const result: CatalogEntry[] = [];
77
+ for (const item of items) {
78
+ const id = item.id;
79
+ if (!seen.has(id)) {
80
+ seen.add(id);
81
+ result.push(item);
82
+ }
83
+ }
84
+ return result;
85
+ }
86
+
71
87
  function getClient(apiUrl: string): DataCatalogClient {
72
88
  if (client && clientUrl === apiUrl) return client;
73
89
  client = new DataCatalogClient({ baseUrl: apiUrl });
@@ -76,7 +92,7 @@
76
92
  }
77
93
 
78
94
  $effect(() => {
79
- if (dcApiUrl && sourceConnectionId) {
95
+ if (dcApiUrl) {
80
96
  loadInitial(dcApiUrl, sourceConnectionId);
81
97
  } else {
82
98
  entries = [];
@@ -90,7 +106,7 @@
90
106
  * If ≤ MAX_ENTRIES items, stay in client mode.
91
107
  * If more, switch to server search mode.
92
108
  */
93
- async function loadInitial(apiUrl: string, connId: string) {
109
+ async function loadInitial(apiUrl: string, connId?: string) {
94
110
  // Cancel any in-flight search
95
111
  searchAbort?.abort();
96
112
  searchAbort = null;
@@ -103,18 +119,19 @@
103
119
  try {
104
120
  const dc = getClient(apiUrl);
105
121
  const result = await dc.catalogEntries.search('%', {
106
- sourceConnectionIds: [connId],
122
+ sourceConnectionIds: connId ? [connId] : [],
107
123
  limit: MAX_ENTRIES + 1
108
124
  });
109
125
  const items = result.items ?? [];
126
+ const uniqueItems = deduplicateEntries(items);
110
127
 
111
- if (items.length > MAX_ENTRIES) {
128
+ if (uniqueItems.length > MAX_ENTRIES) {
112
129
  // Too many — enter server search mode, don't show entries
113
130
  tooMany = true;
114
131
  entries = [];
115
132
  } else {
116
133
  tooMany = false;
117
- entries = items;
134
+ entries = uniqueItems;
118
135
  }
119
136
  } catch (err: any) {
120
137
  error = err.message ?? 'Failed to load catalog entries';
@@ -129,7 +146,7 @@
129
146
  * Cancels previous in-flight request via AbortSignal.
130
147
  */
131
148
  function handleServerSearch(query: string) {
132
- if (!dcApiUrl || !sourceConnectionId) return;
149
+ if (!dcApiUrl) return;
133
150
 
134
151
  // Cancel previous in-flight search
135
152
  searchAbort?.abort();
@@ -147,12 +164,13 @@
147
164
 
148
165
  const dc = getClient(dcApiUrl);
149
166
  dc.catalogEntries.search(query, {
150
- sourceConnectionIds: [sourceConnectionId],
167
+ sourceConnectionIds: sourceConnectionId ? [sourceConnectionId] : [],
151
168
  limit: MAX_ENTRIES
152
169
  }, abort.signal)
153
170
  .then((result) => {
154
171
  if (abort.signal.aborted) return;
155
- entries = result.items ?? [];
172
+ const items = result.items ?? [];
173
+ entries = deduplicateEntries(items);
156
174
  searching = false;
157
175
  })
158
176
  .catch((err) => {
@@ -212,7 +212,7 @@
212
212
  }
213
213
  @keyframes spin { to { transform: rotate(360deg); } }
214
214
  .table-container {
215
- max-height: 400px; overflow-y: auto;
215
+ max-height: 400px; overflow-y: auto; overflow-x: auto;
216
216
  border: 1px solid var(--cds-border-subtle-01, #393939);
217
217
  }
218
218
  .entry-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
@@ -242,6 +242,8 @@
242
242
  code {
243
243
  font-family: 'IBM Plex Mono', monospace; font-size: 0.75rem;
244
244
  color: var(--cds-text-secondary, #c6c6c6);
245
+ display: inline-block; max-width: 100%;
246
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
245
247
  }
246
248
  .pill, .label-pill {
247
249
  display: inline-block; padding: 0.0625rem 0.375rem;