@tangle-network/sandbox-ui 0.91.4 → 0.91.5

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.
@@ -93,9 +93,15 @@ interface IntegrationsPanelProps {
93
93
  featuredIds?: string[];
94
94
  /** Initial sort mode. Defaults to "featured". */
95
95
  defaultSort?: IntegrationSort;
96
+ /**
97
+ * Tiles the loading skeleton draws. A skeleton exists to reserve the space
98
+ * the real content will take, so pass the catalog size you expect when the
99
+ * consumer knows it; the default fills two rows at the widest breakpoint.
100
+ */
101
+ skeletonCount?: number;
96
102
  className?: string;
97
103
  }
98
- declare function IntegrationsPanel({ catalog, connections, healthByConnectionId, isLoading, error, onConnect, onDisconnect, getManageHref, onManage, emptyCatalogLabel, featuredIds, defaultSort, className, }: IntegrationsPanelProps): React.JSX.Element;
104
+ declare function IntegrationsPanel({ catalog, connections, healthByConnectionId, isLoading, error, onConnect, onDisconnect, getManageHref, onManage, emptyCatalogLabel, featuredIds, defaultSort, skeletonCount, className, }: IntegrationsPanelProps): React.JSX.Element;
99
105
 
100
106
  /**
101
107
  * Endpoint contract expected on the consumer app's server (which
@@ -64,6 +64,60 @@ var DEFAULT_FEATURED_IDS = [
64
64
  function defaultConnectorOf(provider) {
65
65
  return provider.connectors?.[0]?.connectorId ?? provider.providerId;
66
66
  }
67
+ var TILE_GRID_CLASS = "grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6";
68
+ var DEFAULT_SKELETON_COUNT = 12;
69
+ var TILE_BOX_CLASS = "flex min-w-0 aspect-square flex-col items-center justify-center gap-2 rounded-xl border p-3 text-center";
70
+ function IntegrationsToolbar({
71
+ query,
72
+ onQueryChange,
73
+ sort,
74
+ onSortChange
75
+ }) {
76
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [
77
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2 rounded-lg border border-border bg-card px-3 py-2", children: [
78
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
79
+ /* @__PURE__ */ jsx(
80
+ "input",
81
+ {
82
+ type: "text",
83
+ value: query,
84
+ onChange: (e) => onQueryChange(e.target.value),
85
+ placeholder: "Search integrations...",
86
+ autoFocus: true,
87
+ "data-testid": "integration-search",
88
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
89
+ }
90
+ )
91
+ ] }),
92
+ /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ role: "tablist",
96
+ "aria-label": "Sort integrations",
97
+ className: "flex shrink-0 items-center gap-1 rounded-lg border border-border bg-card p-1",
98
+ children: [
99
+ ["featured", "Featured"],
100
+ ["alpha", "A\u2013Z"]
101
+ ].map(([value, label]) => /* @__PURE__ */ jsx(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ role: "tab",
106
+ "aria-selected": sort === value,
107
+ onClick: () => onSortChange(value),
108
+ "data-testid": `sort-${value}`,
109
+ className: cn(
110
+ "rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
111
+ sort === value ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"
112
+ ),
113
+ children: label
114
+ },
115
+ value
116
+ ))
117
+ }
118
+ )
119
+ ] });
120
+ }
67
121
  var LOGO_SIZE = 48;
68
122
  function buildConnectionIndex(connections) {
69
123
  const index = /* @__PURE__ */ new Map();
@@ -109,6 +163,7 @@ function IntegrationsPanel({
109
163
  emptyCatalogLabel = "No integrations available yet.",
110
164
  featuredIds = DEFAULT_FEATURED_IDS,
111
165
  defaultSort = "featured",
166
+ skeletonCount = DEFAULT_SKELETON_COUNT,
112
167
  className
113
168
  }) {
114
169
  const [query, setQuery] = React.useState("");
@@ -205,22 +260,38 @@ function IntegrationsPanel({
205
260
  ] }) }) });
206
261
  }
207
262
  if (isLoading && catalog.length === 0) {
208
- return /* @__PURE__ */ jsx(
209
- "div",
210
- {
211
- className: cn(
212
- "grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6",
213
- className
214
- ),
215
- children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsx(
216
- "div",
217
- {
218
- className: "aspect-square animate-pulse rounded-xl border border-border bg-muted/40"
219
- },
220
- i
221
- ))
222
- }
223
- );
263
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-testid": "integrations-skeleton", children: [
264
+ /* @__PURE__ */ jsx(
265
+ IntegrationsToolbar,
266
+ {
267
+ query,
268
+ onQueryChange: setQuery,
269
+ sort,
270
+ onSortChange: setSort
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsx("div", { className: TILE_GRID_CLASS, children: Array.from({ length: skeletonCount }).map((_, i) => /* @__PURE__ */ jsxs(
274
+ "div",
275
+ {
276
+ "aria-hidden": true,
277
+ className: cn(
278
+ TILE_BOX_CLASS,
279
+ "animate-pulse border-border bg-muted/40"
280
+ ),
281
+ children: [
282
+ /* @__PURE__ */ jsx(
283
+ "div",
284
+ {
285
+ className: "shrink-0 rounded-2xl bg-muted",
286
+ style: { width: LOGO_SIZE, height: LOGO_SIZE }
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsx("div", { className: "flex h-8 w-full items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "h-2.5 w-3/4 rounded bg-muted" }) })
290
+ ]
291
+ },
292
+ i
293
+ )) })
294
+ ] });
224
295
  }
225
296
  if (catalog.length === 0) {
226
297
  return /* @__PURE__ */ jsx(
@@ -233,51 +304,16 @@ function IntegrationsPanel({
233
304
  );
234
305
  }
235
306
  const disconnectAccountLabel = disconnectTarget?.accountDisplay;
236
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
237
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [
238
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2 rounded-lg border border-border bg-card px-3 py-2", children: [
239
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
240
- /* @__PURE__ */ jsx(
241
- "input",
242
- {
243
- type: "text",
244
- value: query,
245
- onChange: (e) => setQuery(e.target.value),
246
- placeholder: "Search integrations...",
247
- autoFocus: true,
248
- "data-testid": "integration-search",
249
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
250
- }
251
- )
252
- ] }),
253
- /* @__PURE__ */ jsx(
254
- "div",
255
- {
256
- role: "tablist",
257
- "aria-label": "Sort integrations",
258
- className: "flex shrink-0 items-center gap-1 rounded-lg border border-border bg-card p-1",
259
- children: [
260
- ["featured", "Featured"],
261
- ["alpha", "A\u2013Z"]
262
- ].map(([value, label]) => /* @__PURE__ */ jsx(
263
- "button",
264
- {
265
- type: "button",
266
- role: "tab",
267
- "aria-selected": sort === value,
268
- onClick: () => setSort(value),
269
- "data-testid": `sort-${value}`,
270
- className: cn(
271
- "rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
272
- sort === value ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"
273
- ),
274
- children: label
275
- },
276
- value
277
- ))
278
- }
279
- )
280
- ] }),
307
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-testid": "integrations-panel", children: [
308
+ /* @__PURE__ */ jsx(
309
+ IntegrationsToolbar,
310
+ {
311
+ query,
312
+ onQueryChange: setQuery,
313
+ sort,
314
+ onSortChange: setSort
315
+ }
316
+ ),
281
317
  connectError ? /* @__PURE__ */ jsx(
282
318
  "div",
283
319
  {
@@ -293,7 +329,7 @@ function IntegrationsPanel({
293
329
  title: "No matches",
294
330
  description: `No integrations match "${query.trim()}".`
295
331
  }
296
- ) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6", children: visible.map((provider) => {
332
+ ) : /* @__PURE__ */ jsx("div", { className: TILE_GRID_CLASS, children: visible.map((provider) => {
297
333
  const connectorId = defaultConnectorOf(provider);
298
334
  const live = connectionIndex.get(
299
335
  `${provider.providerId}:${connectorId}`
@@ -310,7 +346,8 @@ function IntegrationsPanel({
310
346
  "data-connected": "true",
311
347
  title: live.accountDisplay ? `${name} \u2014 ${live.accountDisplay}` : name,
312
348
  className: cn(
313
- "group relative flex min-w-0 aspect-square flex-col items-center justify-center gap-2 rounded-xl border p-3 text-center",
349
+ TILE_BOX_CLASS,
350
+ "group relative",
314
351
  "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)]"
315
352
  ),
316
353
  children: [
@@ -446,7 +483,8 @@ function IntegrationsPanel({
446
483
  },
447
484
  title: provider.description ? provider.description : `Connect ${name}`,
448
485
  className: cn(
449
- "group flex min-w-0 aspect-square flex-col items-center justify-center gap-2 rounded-xl border border-border bg-card p-3 text-center transition-all",
486
+ TILE_BOX_CLASS,
487
+ "group border-border bg-card transition-all",
450
488
  "hover:border-primary/40 hover:bg-accent/40 hover:shadow-sm focus:outline-none focus-visible:border-primary/50 focus-visible:ring-2 focus-visible:ring-primary/20"
451
489
  ),
452
490
  children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.91.4",
3
+ "version": "0.91.5",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",