@box-kite/react 2.0.1 → 2.1.0

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.
Files changed (100) hide show
  1. package/.claude/rules/box-kite-rules.md +16 -0
  2. package/.claude/skills/box-kite/SKILL.md +16 -0
  3. package/.claude/skills/box-kite/references/components.md +8 -3
  4. package/.claude/skills/box-kite/references/patterns.md +23 -0
  5. package/.cursor/rules/box-kite.mdc +16 -0
  6. package/AGENTS.md +19 -0
  7. package/BOX_AI_CONTEXT.md +562 -1
  8. package/BOX_KITE_AI_CONTEXT.md +562 -1
  9. package/README.md +39 -7
  10. package/catalog.cjs +1 -0
  11. package/catalog.d.ts +8 -0
  12. package/catalog.mjs +2333 -0
  13. package/client.cjs +1 -1
  14. package/client.mjs +40 -42
  15. package/components/agent.cjs +1 -0
  16. package/components/agent.d.ts +215 -0
  17. package/components/agent.mjs +405 -0
  18. package/components/chart.d.ts +1 -0
  19. package/components/dashboard.cjs +1 -0
  20. package/components/dashboard.d.ts +121 -0
  21. package/components/dashboard.mjs +690 -0
  22. package/components/dataGrid/components/dataGridBody.d.ts +2 -1
  23. package/components/dataGrid/components/dataGridDetailRow.d.ts +13 -2
  24. package/components/dataGrid/components/dataGridGroupRow.d.ts +13 -2
  25. package/components/dataGrid/components/dataGridRow.d.ts +13 -2
  26. package/components/dataGrid/contracts/dataGridContract.d.ts +66 -2
  27. package/components/dataGrid/models/cellModel.d.ts +3 -0
  28. package/components/dataGrid/models/editModel.d.ts +27 -0
  29. package/components/dataGrid/models/exportModel.d.ts +2 -1
  30. package/components/dataGrid/models/gridModel.d.ts +3 -0
  31. package/components/dataGrid/models/rangeModel.d.ts +119 -0
  32. package/components/dataGrid/models/viewportModel.d.ts +23 -2
  33. package/components/dataGrid/useGridNavigation.d.ts +10 -2
  34. package/components/dataGrid.cjs +4 -1
  35. package/components/dataGrid.d.ts +3 -0
  36. package/components/dataGrid.mjs +1056 -765
  37. package/components/markdown.cjs +1 -0
  38. package/components/markdown.d.ts +55 -0
  39. package/components/markdown.mjs +179 -0
  40. package/components/semantics.d.ts +29 -29
  41. package/components/skeleton.cjs +1 -0
  42. package/components/skeleton.d.ts +34 -0
  43. package/components/skeleton.mjs +25 -0
  44. package/dashboardSchema.cjs +1 -0
  45. package/dashboardSchema.mjs +78 -0
  46. package/docs/a11y.md +2 -3
  47. package/docs/catalog.md +451 -0
  48. package/docs/components.md +4 -0
  49. package/docs/index.md +3 -0
  50. package/docs/interop.md +237 -0
  51. package/docs/mcp.md +69 -0
  52. package/docs/props.md +3 -3
  53. package/interop.cjs +1 -0
  54. package/interop.d.ts +51 -0
  55. package/interop.mjs +454 -0
  56. package/mcp/src/data.d.ts +88 -0
  57. package/mcp/src/data.test.d.ts +1 -0
  58. package/mcp/src/engine.d.ts +23 -0
  59. package/mcp/src/index.d.ts +1 -0
  60. package/mcp/src/search.d.ts +26 -0
  61. package/mcp/src/search.test.d.ts +1 -0
  62. package/mcp/src/server.d.ts +18 -0
  63. package/mcp/src/server.test.d.ts +1 -0
  64. package/mcp/src/styles.d.ts +27 -0
  65. package/mcp/src/styles.test.d.ts +1 -0
  66. package/mcp/src/tools.d.ts +18 -0
  67. package/mcp/src/vocabulary.d.ts +41 -0
  68. package/package.json +124 -17
  69. package/react/spec/renderSpec.d.ts +51 -0
  70. package/react/spec/specBoundary.d.ts +25 -0
  71. package/react/spec/specRegistry.d.ts +42 -0
  72. package/react/spec/specRenderer.d.ts +10 -0
  73. package/registry/blocks/dashboard-shell/dashboard-shell.d.ts +22 -0
  74. package/registry/blocks/dashboard-shell/dashboard-stats.d.ts +13 -0
  75. package/registry/blocks/data-grid/invoice-rows.d.ts +15 -0
  76. package/registry/blocks/data-grid/invoices-grid.d.ts +1 -0
  77. package/registry/blocks/settings-form/settings-form.d.ts +17 -0
  78. package/spec.cjs +2 -0
  79. package/spec.d.ts +12 -0
  80. package/spec.mjs +382 -0
  81. package/specData.cjs +1 -0
  82. package/specData.mjs +69 -0
  83. package/specDefs.cjs +1 -0
  84. package/specDefs.mjs +33 -0
  85. package/specSchema.cjs +1 -0
  86. package/specSchema.mjs +88 -0
  87. package/utils/agent/agentUtils.d.ts +81 -0
  88. package/utils/catalog/catalogContracts.d.ts +19 -0
  89. package/utils/catalog/catalogManifest.d.ts +8 -0
  90. package/utils/dashboard/dashboardSchema.d.ts +18 -0
  91. package/utils/dashboard/dashboardUtils.d.ts +97 -0
  92. package/utils/interop/a2uiInterop.d.ts +82 -0
  93. package/utils/interop/assistantUiInterop.d.ts +34 -0
  94. package/utils/interop/toolInterop.d.ts +30 -0
  95. package/utils/spec/specData.d.ts +19 -0
  96. package/utils/spec/specDefs.d.ts +11 -0
  97. package/utils/spec/specProps.d.ts +15 -0
  98. package/utils/spec/specSchema.d.ts +23 -0
  99. package/utils/spec/specTypes.d.ts +71 -0
  100. package/utils/spec/specValidate.d.ts +10 -0
@@ -0,0 +1,405 @@
1
+ "use client";
2
+ import e from "../box.mjs";
3
+ import { a as t, i as n } from "../behavior.mjs";
4
+ import { t as r } from "../identity.mjs";
5
+ import { useState as i } from "react";
6
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ //#region src/utils/agent/agentUtils.ts
8
+ var c;
9
+ (function(e) {
10
+ let t = {
11
+ pending: "Pending",
12
+ running: "Running",
13
+ success: "Done",
14
+ error: "Failed"
15
+ };
16
+ function n(e) {
17
+ return t[e] ?? t.pending;
18
+ }
19
+ e.statusLabel = n;
20
+ let r = {
21
+ approved: "Approved",
22
+ rejected: "Rejected"
23
+ };
24
+ function i(e) {
25
+ return r[e] ?? "";
26
+ }
27
+ e.decisionLabel = i;
28
+ let a = e.VALUE_LIMIT = 2e4;
29
+ function o(e) {
30
+ if (typeof e == "string") return e;
31
+ try {
32
+ let t = JSON.stringify(e, s, 2);
33
+ return typeof t == "string" ? t : String(e);
34
+ } catch {
35
+ return String(e);
36
+ }
37
+ }
38
+ function s(e, t) {
39
+ return typeof t == "bigint" ? `${t}` : typeof t == "function" ? "[Function]" : t;
40
+ }
41
+ function c(e, t = a) {
42
+ let n = o(e);
43
+ return n.length <= t ? {
44
+ text: n,
45
+ truncated: 0
46
+ } : {
47
+ text: n.slice(0, t),
48
+ truncated: n.length - t
49
+ };
50
+ }
51
+ e.formatValue = c;
52
+ function l(e) {
53
+ return e != null;
54
+ }
55
+ e.hasValue = l;
56
+ function u(e) {
57
+ return `${Math.trunc(e)}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
58
+ }
59
+ e.formatCount = u;
60
+ function d(e) {
61
+ if (!Number.isFinite(e) || e < 0) return "";
62
+ if (e < 1e3) return `${Math.round(e)}ms`;
63
+ let t = e / 1e3;
64
+ if (t < 60) return `${t < 10 ? Math.round(t * 10) / 10 : Math.round(t)}s`;
65
+ let n = Math.floor(t / 60);
66
+ return `${n}m ${Math.round(t - n * 60)}s`;
67
+ }
68
+ e.formatDuration = d;
69
+ let f = e.STREAM_WINDOW = 8;
70
+ function p(e) {
71
+ return {
72
+ settled: e,
73
+ segments: [],
74
+ nextKey: 0
75
+ };
76
+ }
77
+ e.initialStream = p;
78
+ function m(e) {
79
+ return e.segments.reduce((e, t) => e + t.text, e.settled);
80
+ }
81
+ e.streamText = m;
82
+ function h(e, t, n = f) {
83
+ let r = m(e);
84
+ if (t === r) return e;
85
+ if (!t.startsWith(r)) return {
86
+ settled: t,
87
+ segments: [],
88
+ nextKey: e.nextKey
89
+ };
90
+ let i = [...e.segments, {
91
+ key: e.nextKey,
92
+ text: t.slice(r.length)
93
+ }], a = Math.max(0, i.length - Math.max(0, n));
94
+ return {
95
+ settled: i.slice(0, a).reduce((e, t) => e + t.text, e.settled),
96
+ segments: i.slice(a),
97
+ nextKey: e.nextKey + 1
98
+ };
99
+ }
100
+ e.advanceStream = h;
101
+ })(c ||= {});
102
+ var l = c;
103
+ //#endregion
104
+ //#region src/components/agent.tsx
105
+ function u({ part: t, open: n }) {
106
+ return /* @__PURE__ */ o(e, {
107
+ tag: "span",
108
+ component: t,
109
+ variant: { open: n },
110
+ props: { "aria-hidden": !0 }
111
+ });
112
+ }
113
+ var d = {
114
+ section: "toolCall.section",
115
+ label: "toolCall.label",
116
+ value: "toolCall.value",
117
+ truncated: "toolCall.truncated"
118
+ }, f = {
119
+ section: "approval.section",
120
+ label: "approval.label",
121
+ value: "approval.value",
122
+ truncated: "approval.truncated"
123
+ };
124
+ function p({ parts: t, label: n, value: r, limit: i }) {
125
+ let { text: a, truncated: c } = l.formatValue(r, i);
126
+ return /* @__PURE__ */ s(e, {
127
+ component: t.section,
128
+ children: [
129
+ /* @__PURE__ */ o(e, {
130
+ component: t.label,
131
+ children: n
132
+ }),
133
+ /* @__PURE__ */ o(e, {
134
+ tag: "pre",
135
+ component: t.value,
136
+ children: a
137
+ }),
138
+ c > 0 && /* @__PURE__ */ o(e, {
139
+ component: t.truncated,
140
+ children: `${l.formatCount(c)} more characters, not shown`
141
+ })
142
+ ]
143
+ });
144
+ }
145
+ function m(i) {
146
+ let { name: c, description: f, status: m = "pending", input: h, output: g, error: _, open: v, defaultOpen: y = !1, onOpenChange: b, collapsible: x = !0, inputLabel: S = "Input", outputLabel: C = "Output", errorLabel: w = "Error", valueLimit: T, children: E, props: D, ...O } = i, k = r("toolcall"), A = `${k}-header`, j = `${k}-body`, [M, N] = n({
147
+ value: v,
148
+ defaultValue: y,
149
+ onChange: b
150
+ }), P = t((e) => N((e) => !e, {
151
+ reason: "trigger",
152
+ event: e
153
+ })), F = [
154
+ {
155
+ key: "input",
156
+ label: S,
157
+ value: h
158
+ },
159
+ {
160
+ key: "output",
161
+ label: C,
162
+ value: g
163
+ },
164
+ {
165
+ key: "error",
166
+ label: w,
167
+ value: _
168
+ }
169
+ ].filter((e) => l.hasValue(e.value)), I = x && (F.length > 0 || E !== void 0), L = I && M, R = /* @__PURE__ */ s(a, { children: [
170
+ /* @__PURE__ */ s(e, {
171
+ tag: "span",
172
+ component: "toolCall.summary",
173
+ children: [/* @__PURE__ */ o(e, {
174
+ tag: "span",
175
+ component: "toolCall.name",
176
+ children: c
177
+ }), f !== void 0 && /* @__PURE__ */ o(e, {
178
+ tag: "span",
179
+ component: "toolCall.description",
180
+ children: f
181
+ })]
182
+ }),
183
+ /* @__PURE__ */ s(e, {
184
+ tag: "span",
185
+ component: "toolCall.status",
186
+ variant: m,
187
+ children: [/* @__PURE__ */ o(e, {
188
+ tag: "span",
189
+ component: "toolCall.status.dot",
190
+ variant: { running: m === "running" },
191
+ props: { "aria-hidden": !0 }
192
+ }), l.statusLabel(m)]
193
+ }),
194
+ I && /* @__PURE__ */ o(u, {
195
+ part: "toolCall.arrow",
196
+ open: L
197
+ })
198
+ ] });
199
+ return /* @__PURE__ */ s(e, {
200
+ component: "toolCall",
201
+ ...O,
202
+ props: {
203
+ "aria-busy": m === "running" || void 0,
204
+ ...D
205
+ },
206
+ children: [I ? /* @__PURE__ */ o(e, {
207
+ tag: "button",
208
+ component: "toolCall.header",
209
+ variant: { interactive: !0 },
210
+ id: A,
211
+ props: {
212
+ type: "button",
213
+ "aria-expanded": L,
214
+ "aria-controls": j,
215
+ onClick: P
216
+ },
217
+ children: R
218
+ }) : /* @__PURE__ */ o(e, {
219
+ component: "toolCall.header",
220
+ id: A,
221
+ children: R
222
+ }), I && /* @__PURE__ */ o(e, {
223
+ component: "toolCall.track",
224
+ variant: { closed: !L },
225
+ children: /* @__PURE__ */ o(e, {
226
+ component: "toolCall.clip",
227
+ children: /* @__PURE__ */ s(e, {
228
+ component: "toolCall.body",
229
+ id: j,
230
+ children: [F.map((e) => /* @__PURE__ */ o(p, {
231
+ parts: d,
232
+ label: e.label,
233
+ value: e.value,
234
+ limit: T
235
+ }, e.key)), E]
236
+ })
237
+ })
238
+ })]
239
+ });
240
+ }
241
+ var h = m;
242
+ h.displayName = "ToolCallCard";
243
+ function g(t) {
244
+ let { title: i, description: a, input: c, inputLabel: u = "Request", decision: d, defaultDecision: m = null, onDecisionChange: h, busy: g = !1, approveLabel: _ = "Approve", rejectLabel: v = "Reject", autoFocus: y = !1, valueLimit: b, children: x, props: S, ...C } = t, w = `${r("approval")}-title`, [T, E] = n({
245
+ value: d,
246
+ defaultValue: m,
247
+ onChange: h
248
+ }), D = (e, t) => (n) => E(e, {
249
+ reason: t,
250
+ event: n
251
+ });
252
+ return /* @__PURE__ */ s(e, {
253
+ component: "approval",
254
+ ...C,
255
+ variant: [C.variant, {
256
+ approved: T === "approved",
257
+ rejected: T === "rejected"
258
+ }],
259
+ props: {
260
+ role: "group",
261
+ "aria-labelledby": w,
262
+ "aria-busy": g || void 0,
263
+ ...S
264
+ },
265
+ children: [
266
+ /* @__PURE__ */ o(e, {
267
+ component: "approval.title",
268
+ id: w,
269
+ children: i
270
+ }),
271
+ a !== void 0 && /* @__PURE__ */ o(e, {
272
+ component: "approval.description",
273
+ children: a
274
+ }),
275
+ l.hasValue(c) && /* @__PURE__ */ o(p, {
276
+ parts: f,
277
+ label: u,
278
+ value: c,
279
+ limit: b
280
+ }),
281
+ x,
282
+ /* @__PURE__ */ s(e, {
283
+ component: "approval.footer",
284
+ children: [T === null && /* @__PURE__ */ s(e, {
285
+ component: "approval.actions",
286
+ children: [/* @__PURE__ */ o(e, {
287
+ tag: "button",
288
+ component: "approval.button",
289
+ variant: { reject: !0 },
290
+ disabled: g,
291
+ props: {
292
+ type: "button",
293
+ autoFocus: y,
294
+ onClick: D("rejected", "reject")
295
+ },
296
+ children: v
297
+ }), /* @__PURE__ */ o(e, {
298
+ tag: "button",
299
+ component: "approval.button",
300
+ variant: { approve: !0 },
301
+ disabled: g,
302
+ props: {
303
+ type: "button",
304
+ onClick: D("approved", "approve")
305
+ },
306
+ children: _
307
+ })]
308
+ }), /* @__PURE__ */ o(e, {
309
+ component: "approval.decision",
310
+ variant: {
311
+ approved: T === "approved",
312
+ rejected: T === "rejected"
313
+ },
314
+ props: { role: "status" },
315
+ children: T === null ? null : l.decisionLabel(T)
316
+ })]
317
+ })
318
+ ]
319
+ });
320
+ }
321
+ var _ = g;
322
+ _.displayName = "ApprovalCard";
323
+ function v(i) {
324
+ let { label: a, children: c, open: d, defaultOpen: f = !1, onOpenChange: p, streaming: m = !1, duration: h, props: g, ..._ } = i, v = r("reasoning"), y = `${v}-trigger`, b = `${v}-body`, [x, S] = n({
325
+ value: d,
326
+ defaultValue: f,
327
+ onChange: p
328
+ }), C = t((e) => S((e) => !e, {
329
+ reason: "trigger",
330
+ event: e
331
+ })), w = h === void 0 ? "" : l.formatDuration(h);
332
+ return /* @__PURE__ */ s(e, {
333
+ component: "reasoning",
334
+ ..._,
335
+ props: {
336
+ "aria-busy": m || void 0,
337
+ ...g
338
+ },
339
+ children: [/* @__PURE__ */ s(e, {
340
+ tag: "button",
341
+ component: "reasoning.trigger",
342
+ variant: { streaming: m },
343
+ id: y,
344
+ props: {
345
+ type: "button",
346
+ "aria-expanded": x,
347
+ "aria-controls": b,
348
+ onClick: C
349
+ },
350
+ children: [
351
+ /* @__PURE__ */ o(u, {
352
+ part: "reasoning.arrow",
353
+ open: x
354
+ }),
355
+ a ?? (m ? "Thinking…" : "Reasoning"),
356
+ !m && w !== "" && /* @__PURE__ */ o(e, {
357
+ tag: "span",
358
+ component: "reasoning.duration",
359
+ children: `Thought for ${w}`
360
+ })
361
+ ]
362
+ }), /* @__PURE__ */ o(e, {
363
+ component: "reasoning.track",
364
+ variant: { closed: !x },
365
+ children: /* @__PURE__ */ o(e, {
366
+ component: "reasoning.clip",
367
+ children: /* @__PURE__ */ o(e, {
368
+ component: "reasoning.body",
369
+ id: b,
370
+ children: c
371
+ })
372
+ })
373
+ })]
374
+ });
375
+ }
376
+ var y = v;
377
+ y.displayName = "Reasoning";
378
+ function b(t) {
379
+ let { text: n, streaming: r = !1, window: a = l.STREAM_WINDOW, caret: c = !0, props: u, ...d } = t, [f, p] = i(() => l.initialStream(n)), m = l.advanceStream(f, n, a);
380
+ return m !== f && p(m), /* @__PURE__ */ s(e, {
381
+ component: "streamingText",
382
+ ...d,
383
+ props: {
384
+ "aria-busy": r || void 0,
385
+ ...u
386
+ },
387
+ children: [
388
+ m.settled,
389
+ m.segments.map((t) => /* @__PURE__ */ o(e, {
390
+ tag: "span",
391
+ component: "streamingText.segment",
392
+ children: t.text
393
+ }, t.key)),
394
+ r && c && /* @__PURE__ */ o(e, {
395
+ tag: "span",
396
+ component: "streamingText.caret",
397
+ props: { "aria-hidden": !0 }
398
+ })
399
+ ]
400
+ });
401
+ }
402
+ var x = b;
403
+ x.displayName = "StreamingText";
404
+ //#endregion
405
+ export { l as AgentUtils, _ as ApprovalCard, y as Reasoning, x as StreamingText, h as ToolCallCard };
@@ -15,6 +15,7 @@ import { SvgProps } from './svg';
15
15
  * `children` narrowed to SVG content — a chart's children are a label in a ring, never a render prop.
16
16
  */
17
17
  type ChartProps = Omit<SvgProps, 'viewBox' | 'variant' | 'children'> & {
18
+ /** What is drawn on top of the shape: the number inside a ring, a gradient in a `<Defs>`. SVG content, not a render prop. */
18
19
  children?: React.ReactNode;
19
20
  };
20
21
  /** A paint value: a colour token, or the `url(#…)`/`var(--…)` a gradient or a theme provides. */
@@ -0,0 +1 @@
1
+ "use client";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../platform.cjs"),t=require("../box.cjs"),n=require("../behavior.cjs"),r=require("../dashboardSchema.cjs"),i=require("./svg.cjs"),a=require("./visuallyHidden.cjs");let o=require("react"),s=require("react/jsx-runtime");var c;(function(e){let t=e.VERSION=1,n=e.MIN_COLUMNS=1,i=e.MAX_COLUMNS=24;function a(e,t,n){return Math.min(Math.max(e,t),n)}function o(e,t){return typeof e==`number`&&Number.isFinite(e)?Math.round(e):t}function s(e,t){return e.x<t.x+t.w&&e.x+e.w>t.x&&e.y<t.y+t.h&&e.y+e.h>t.y}e.overlaps=s;function c(e,t){return e.find(e=>e.id!==t.id&&s(e,t))}function l(e,t){let n=Math.min(e.maxW??t,t),r=a(o(e.w,1),Math.max(1,Math.min(e.minW??1,n)),Math.max(1,n)),i=a(o(e.h,1),Math.max(1,e.minH??1),Math.max(1,e.maxH??2**53-1));return{...e,w:r,h:i,x:a(o(e.x,0),0,t-r),y:Math.max(o(e.y,0),0)}}function u(e,t){return e.y-t.y||e.x-t.x}function d(e,t){let r=a(o(t,12),n,i),s=e.map(e=>l(e,r)),d=e=>+!e.fixed,f=[...s].sort((e,t)=>d(e)-d(t)||u(e,t)),p=[];for(let e of f){if(e.fixed){p.push(e);continue}let t={...e};for(;t.y>0&&!c(p,{...t,y:t.y-1});)t.y--;let n=c(p,t);for(;n;)t.y=n.y+n.h,n=c(p,t);p.push(t)}return p.sort(u)}e.resolve=d;function f(e){let r=a(o(e.columns,12),n,i);return{version:t,columns:r,items:d(e.items,r)}}e.normalize=f;function p(e,t){return e.items.find(e=>e.id===t)}e.itemOf=p;function m(e){return new Map(e.items.map((e,t)=>[e.id,t]))}e.ranks=m;function h(e,t){let n=n=>!s(t,n)&&!e.some(e=>e.id!==n.id&&e.id!==t.id&&s(e,n));return e.map(e=>{if(e.id===t.id)return t;if(e.fixed===!0||!s(e,t))return e;let r={...e,y:Math.max(t.y-e.h,0)};return n(r)?r:{...e,y:t.y+t.h}})}function g(e,t,r,s){let c=p(e,t);if(!c||c.fixed)return e;let u=l({...c,x:r,y:s},a(o(e.columns,12),n,i));return f({...e,items:h(e.items,u)})}e.moveTo=g;function _(e,t,n,r){let i=p(e,t);if(!i||i.fixed)return e;let a=e.items.map(e=>e.id===t?{...e,w:n,h:r}:e);return f({...e,items:a})}e.resizeTo=_;function v(e,t){let n=e.items.reduce((e,t)=>Math.max(e,t.y+t.h),0);return f({...e,items:[...e.items,{x:0,y:n,...t}]})}e.add=v;function y(e,t){return f({...e,items:e.items.filter(e=>e.id!==t)})}e.remove=y;function b(e,r){let s=a(o(r,12),n,i);if(s===e.columns)return f(e);let c=s/e.columns,l=e=>e===void 0?void 0:a(Math.max(1,Math.round(e*c)),1,s),u=e.items.map(e=>{let t=a(Math.max(1,Math.round(e.w*c)),1,s);return{...e,w:t,x:a(Math.floor(e.x*c),0,s-t),minW:l(e.minW),maxW:l(e.maxW)}});return f({version:t,columns:s,items:u})}e.project=b;function x(e,t){return e===t?!0:e.columns!==t.columns||e.items.length!==t.items.length?!1:e.items.every((e,n)=>{let r=t.items[n];return e.id===r.id&&e.x===r.x&&e.y===r.y&&e.w===r.w&&e.h===r.h})}e.same=x;function S(e,t,n){return{columns:n.x>0?Math.round(e/n.x):0,rows:n.y>0?Math.round(t/n.y):0}}e.cellsMoved=S;function C(e){return typeof e==`string`&&e.length>0}function w(e){return typeof e==`number`&&Number.isFinite(e)}function T(e,t,n){if(typeof e!=`object`||!e||Array.isArray(e)){n.push({code:`invalid-item`,path:t,message:`A layout item has to be an object.`});return}let r=e;if(!C(r.id)){n.push({code:`invalid-item`,path:`${t}.id`,message:`A layout item needs an id naming the widget it places.`});return}if(!w(r.x)||!w(r.y)||!w(r.w)||!w(r.h)){n.push({code:`invalid-item`,path:t,message:`Item "${r.id}" needs numeric x, y, w and h.`});return}let i=[`minW`,`minH`,`maxW`,`maxH`].reduce((e,t)=>(w(r[t])&&(e[t]=Math.round(r[t])),e),{});return{id:r.id,x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.w),h:Math.round(r.h),...i,...r.fixed===!0?{fixed:!0}:{}}}function E(e,r=12){let o=[],s={version:t,columns:r,items:[]};if(typeof e!=`object`||!e||Array.isArray(e))return o.push({code:`not-an-object`,path:``,message:`A layout has to be an object.`}),{layout:f(s),issues:o};let c=e;c.version!==t&&o.push({code:`version`,path:`version`,message:`Layout version ${String(c.version)} was read as version ${t}.`});let l=w(c.columns)?a(Math.round(c.columns),n,i):r;if(w(c.columns)||o.push({code:`columns`,path:`columns`,message:`No column count, so the layout was read in ${r}.`}),!Array.isArray(c.items))return o.push({code:`items`,path:`items`,message:`A layout needs an items array.`}),{layout:f({...s,columns:l}),issues:o};let u=new Set,d=c.items.reduce((e,t,n)=>{let r=T(t,`items.${n}`,o);return r?u.has(r.id)?(o.push({code:`duplicate-id`,path:`items.${n}.id`,message:`Two items are called "${r.id}"; the second was dropped.`}),e):(u.add(r.id),e.push(r),e):e},[]);return{layout:f({version:t,columns:l,items:d}),issues:o}}e.parse=E,e.SCHEMA=r.t})(c||={});var l=c,u=[`xs`,`sm`,`md`,`lg`,`xl`,`xxl`],d=[`h1`,`h2`,`h3`,`h4`,`h5`,`h6`],f=(0,o.createContext)(null);function p(e,t,n){return Math.min(Math.max(e,t),n)}function m(e){if(typeof e==`number`)return[{count:p(Math.round(e),l.MIN_COLUMNS,l.MAX_COLUMNS)}];let t=u.filter(t=>typeof e[t]==`number`).map(t=>({size:t,count:p(Math.round(e[t]),l.MIN_COLUMNS,l.MAX_COLUMNS)}));return t.length>0?t:[{count:12}]}function h(e){return{gridColumnStart:e.x+1,gridColumnEnd:e.x+1+e.w,gridRowStart:e.y+1,gridRowEnd:e.y+1+e.h}}function g(e){return`max${e[0].toUpperCase()}${e.slice(1)}`}function _(e,t){let n=t?-1:1;if(e===`ArrowLeft`)return{x:-n,y:0};if(e===`ArrowRight`)return{x:n,y:0};if(e===`ArrowUp`)return{x:0,y:-1};if(e===`ArrowDown`)return{x:0,y:1}}function v(e){return`column ${e.x+1}, row ${e.y+1}`}function y(e){return`${e.w} ${e.w===1?`column`:`columns`} by ${e.h} ${e.h===1?`row`:`rows`}`}function b(r){let{children:i,layout:c,defaultLayout:d,onLayoutChange:p,onLayoutCommit:b,columns:x={xs:1,md:6,xxl:12},rowHeight:S=24,editable:C=!1,label:w,labelledBy:T,props:E,...D}=r,O=(0,o.useRef)(null),[k,A]=(0,o.useState)(null),[j,M]=(0,o.useState)(null),[N,P]=(0,o.useState)(``),F=`${(0,o.useId)()}-dashboard`,[I,L]=n.i({value:c,defaultValue:d??{version:l.VERSION,columns:12,items:[]},onChange:p}),R=(0,o.useMemo)(()=>l.normalize(I),[I]),z=n.o(R),B=n.a(b),V=typeof x==`number`?String(x):u.map(e=>x[e]??``).join(),H=(0,o.useMemo)(()=>m(x),[V]),U=H[H.length-1],W=(0,o.useMemo)(()=>{let e=l.ranks(R),t=new Map;for(let n of H){let r=n.count===U.count?R:l.project(R,n.count),i=U.count/n.count;for(let a of r.items){let r=t.get(a.id)??{order:e.get(a.id)??0,fixed:a.fixed===!0},o=h({...a,x:Math.round(a.x*i),w:Math.round((a.x+a.w)*i)-Math.round(a.x*i)});n===H[0]?r.placement=o:r.query={...r.query,[n.size]:o},t.set(a.id,r)}}return t},[R,H,U.count]),G=n.a((e,t)=>{l.same(e,z.current)||L(e,t)}),ee=(e,t)=>{let n=e.getBoundingClientRect(),r=O.current,i=r&&typeof getComputedStyle==`function`?getComputedStyle(r):void 0,a=Number.parseFloat(i?.columnGap??``)||0,o=Number.parseFloat(i?.rowGap??``)||0;return{x:(n.width+a)/t.w,y:(n.height+o)/t.h}},te=e=>e instanceof Element?e.closest(`[data-widget-id]`):null,K=(e,t,n,r)=>{let i=e.kind===`move`?l.moveTo(z.current,e.id,t,n):l.resizeTo(z.current,e.id,t,n);return G(i,{reason:e.kind,event:r}),i},q=n.a((t,n,r,i)=>{let a=l.itemOf(z.current,n),o=te(t.currentTarget);!C||t.button!==0||t.defaultPrevented||!a||a.fixed||!o||(t.preventDefault(),t.currentTarget.setPointerCapture?.(t.pointerId),t.currentTarget.focus(),A({id:n,kind:i,name:r,origin:a,before:z.current,pointer:{id:t.pointerId,x:t.clientX,y:t.clientY,step:ee(o,a),rtl:e.u(o)}}))}),J=n.a((e,t)=>{if(!k?.pointer||k.id!==t)return;let{x:n,y:r,step:i,rtl:a}=k.pointer,o={x:e.clientX-n,y:e.clientY-r},s=l.cellsMoved(a?-o.x:o.x,o.y,i),c=k.origin,u=k.kind===`move`?K(k,c.x+s.columns,c.y+s.rows,e):K(k,c.w+s.columns,c.h+s.rows,e),d=l.itemOf(u,t);if(k.kind!==`move`||!d)return;let f={x:(d.x-c.x)*i.x*(a?-1:1),y:(d.y-c.y)*i.y};M({translate:`${Math.round(o.x-f.x)}px ${Math.round(o.y-f.y)}px`})}),Y=n.a((e,t)=>{if(!k?.pointer||k.id!==t)return;let n=l.itemOf(z.current,t);e.currentTarget.releasePointerCapture?.(k.pointer.id),A(null),M(null),B(z.current,{reason:k.kind,event:e}),n&&P(k.kind===`move`?`${k.name} moved to ${v(n)}.`:`${k.name} resized to ${y(n)}.`)}),X=n.a((e,t)=>{G(e.before,{reason:e.kind,event:t}),A(null),P(`${e.name} put back at ${v(e.origin)}.`)}),Z=n.a((t,n,r,i)=>{if(!C||t.defaultPrevented)return;let a=k&&k.id===n&&k.kind===i&&!k.pointer?k:void 0,o=l.itemOf(z.current,n);if(!o)return;if(!a){if(t.key!==`Enter`&&t.key!==` `||o.fixed)return;t.preventDefault(),A({id:n,kind:i,name:r,origin:o,before:z.current}),P(i===`move`?`${r} picked up at ${v(o)}. Use the arrow keys to move it.`:`${r} held for resizing at ${y(o)}. Use the arrow keys to resize it.`);return}if(t.key===`Escape`){t.preventDefault(),X(a,t);return}if(t.key===`Enter`||t.key===` `){t.preventDefault(),A(null),B(z.current,{reason:i,event:t}),P(i===`move`?`${r} dropped at ${v(o)}.`:`${r} resized to ${y(o)}.`);return}let s=_(t.key,e.u(t.currentTarget));if(!s)return;t.preventDefault();let c=i===`move`?K(a,o.x+s.x,o.y+s.y,t):K(a,o.w+s.x,o.h+s.y,t),u=l.itemOf(c,n);u&&P(i===`move`?`${r}, ${v(u)}.`:`${r}, ${y(u)}.`)}),Q=n.a((e,t,n)=>{k&&k.id===t&&k.kind===n&&!k.pointer&&X(k,e)}),ne=(0,o.useMemo)(()=>({editable:C,placementOf:e=>W.get(e),heldOf:e=>k&&k.id===e?k:void 0,offsetOf:e=>k&&k.id===e&&j?j:void 0,outsideAuthoring:U.size?{[g(U.size)]:{display:`none`}}:void 0,instructionsId:F,handleProps:(e,t,n)=>({type:`button`,"aria-label":n===`move`?`Move ${t}`:`Resize ${t}`,"aria-pressed":!!(k&&k.id===e&&k.kind===n),"aria-describedby":F,onPointerDown:r=>q(r,e,t,n),onPointerMove:t=>J(t,e),onPointerUp:t=>Y(t,e),onPointerCancel:t=>Y(t,e),onKeyDown:r=>Z(r,e,t,n),onBlur:t=>Q(t,e,n)})}),[C,W,k,j,U.size,F,q,J,Y,Z,Q]),$=k?W.get(k.id):void 0;return(0,s.jsxs)(f.Provider,{value:ne,children:[(0,s.jsxs)(t.default,{ref:O,component:`dashboard`,...D,variant:[D.variant,{dragging:k!==null}],vars:{"dashboard-row":`${S/4}rem`},gridTemplateColumns:U.count,props:{role:`list`,"aria-label":w,"aria-labelledby":T,...E},children:[i,$&&(0,s.jsx)(t.default,{component:`dashboard.placeholder`,order:$.order,cq:$.query,...$.placement,props:{role:`presentation`,"aria-hidden":!0}})]}),(0,s.jsx)(a.default,{props:{role:`status`},children:N}),(0,s.jsx)(a.default,{id:F,children:`Press Enter or Space to pick the widget up, the arrow keys to move or resize it, Enter to drop it and Escape to put it back.`})]})}function x({kind:e}){return e===`resize`?(0,s.jsx)(i.Svg,{viewBox:`0 0 24 24`,width:`1rem`,height:`1rem`,rtl:{flip:`xAxis`},props:{fill:`none`},children:(0,s.jsx)(i.Path,{d:`M20 10 10 20M20 16l-4 4`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`})}):(0,s.jsxs)(i.Svg,{viewBox:`0 0 24 24`,width:`1rem`,height:`1rem`,children:[[8,12,16].map(e=>(0,s.jsx)(i.Circle,{cx:9,cy:e,r:1.4,fill:`currentColor`},e)),[8,12,16].map(e=>(0,s.jsx)(i.Circle,{cx:15,cy:e,r:1.4,fill:`currentColor`},e))]})}function S(){return(0,s.jsx)(i.Svg,{viewBox:`0 0 24 24`,width:`1rem`,height:`1rem`,props:{fill:`none`},children:(0,s.jsx)(i.Path,{d:`M20 12a8 8 0 1 1-2.6-5.9M20 4v4h-4`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`})})}function C(e){let{id:n,children:r,title:i,name:a,description:c,actions:l,level:u=3,loading:p=!1,error:m,empty:h,onRefresh:g,refreshLabel:_,props:v,...y}=e,b=(0,o.useContext)(f),C=n===void 0?void 0:b?.placementOf(n),w=n===void 0?void 0:b?.heldOf(n),T=a??(typeof i==`string`?i:void 0)??n??`Widget`,E=b?.editable===!0&&C!==void 0&&!C.fixed,D=e=>E&&n!==void 0?(0,s.jsx)(t.default,{component:`widget.handle`,tag:`button`,variant:{editable:!0,grabbed:w?.kind===e,corner:e===`resize`},cq:b?.outsideAuthoring,props:b?.handleProps(n,T,e),children:(0,s.jsx)(x,{kind:e})}):null,O=()=>p?(0,s.jsx)(t.default,{component:`widget.skeleton`,props:{"aria-hidden":!0},children:[80,100,60].map(e=>(0,s.jsx)(t.default,{component:`widget.skeleton.bar`,width:`${e}%`},e))}):m?(0,s.jsxs)(t.default,{component:`widget.message`,variant:{error:!0},props:{role:`status`},children:[m,g&&(0,s.jsx)(t.default,{tag:`button`,component:`widget.retry`,props:{type:`button`,onClick:g},children:`Try again`})]}):h?(0,s.jsx)(t.default,{component:`widget.message`,children:h===!0?`Nothing to show yet.`:h}):r,k=i!==void 0||c!==void 0||l!==void 0||g!==void 0||E;return(0,s.jsxs)(t.default,{component:`widget`,...y,variant:[y.variant,{dragging:w?.pointer!==void 0,grabbed:w!==void 0&&w.pointer===void 0}],order:C?.order,cq:C?.query,...C?.placement,style:{...n===void 0?void 0:b?.offsetOf(n),...y.style},props:{role:C?`listitem`:void 0,"aria-busy":p||void 0,"data-widget-id":n,...v},children:[k&&(0,s.jsxs)(t.default,{component:`widget.header`,children:[D(`move`),(0,s.jsxs)(t.default,{component:`widget.label`,children:[i!==void 0&&(0,s.jsx)(t.default,{component:`widget.title`,tag:d[u-1],children:i}),c!==void 0&&(0,s.jsx)(t.default,{component:`widget.description`,children:c})]}),g&&(0,s.jsx)(t.default,{component:`widget.handle`,tag:`button`,variant:{editable:!0},props:{type:`button`,"aria-label":_??`Refresh ${T}`,onClick:g},children:(0,s.jsx)(S,{})}),l!==void 0&&(0,s.jsx)(t.default,{component:`widget.actions`,children:l})]}),(0,s.jsx)(t.default,{component:`widget.body`,children:O()}),D(`resize`)]})}var w=b;w.displayName=`DashboardGrid`;var T=C;T.displayName=`Widget`,exports.DashboardUtils=l,exports.Widget=T,exports.default=w;
@@ -0,0 +1,121 @@
1
+ import { ReactNode } from 'react';
2
+ import { BoxProps } from '../box';
3
+ import { ChangeHandler } from '../react/a11y/useControllableState';
4
+ import { ComponentsAndVariants } from '@box-kite/core/types';
5
+ import { default as DashboardUtils, DashboardItem, DashboardLayout } from '../utils/dashboard/dashboardUtils';
6
+ export type { DashboardItem, DashboardLayout };
7
+ export { DashboardUtils };
8
+ /**
9
+ * Why a layout changed. The *device* is not a reason here — a drag and an arrow key both move a widget,
10
+ * and `details.event` says which one did it; what a consumer acts on differently is move against resize.
11
+ */
12
+ export type DashboardReason = 'move' | 'resize' | 'imperative';
13
+ /** The container sizes columns are declared against — the `cq` scale, since a dashboard queries its own width. */
14
+ export type DashboardSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
15
+ /** One column count, or one per container size. The largest is the space the layout is written in. */
16
+ export type DashboardColumns = number | Partial<Record<DashboardSize, number>>;
17
+ type DashboardBoxProps<TKey extends keyof ComponentsAndVariants> = Omit<BoxProps<'div', TKey>, 'onChange'>;
18
+ export interface DashboardGridProps<TKey extends keyof ComponentsAndVariants = 'dashboard'> extends DashboardBoxProps<TKey> {
19
+ /** The widgets. Each `<Widget id>` is placed by the layout item of that id; one with no item is left to flow. */
20
+ children?: ReactNode;
21
+ /** Controlled layout. Leave it out and the grid owns it. */
22
+ layout?: DashboardLayout;
23
+ /** What it starts as. The layout is normalized on the way in, so a generated one need not be tidy. */
24
+ defaultLayout?: DashboardLayout;
25
+ /** Fires on every cell a drag crosses and every arrow key, with the whole normalized layout. */
26
+ onLayoutChange?: ChangeHandler<DashboardLayout, DashboardReason>;
27
+ /** Fires once when the interaction ends — the place for the write a per-cell callback is too noisy for. */
28
+ onLayoutCommit?: ChangeHandler<DashboardLayout, DashboardReason>;
29
+ /**
30
+ * How many columns, at one width or at several: `12`, or `{ xs: 1, md: 6, xxl: 12 }` — the default —
31
+ * which is one column until the dashboard is 28rem wide, six from there and twelve from 42rem. Each is
32
+ * a projection of the one layout, computed rather than measured, so the browser picks between classes;
33
+ * the smallest count is also what a container narrower than every size named here gets.
34
+ */
35
+ columns?: DashboardColumns;
36
+ /** The height of one row, on the ÷4 spacing scale. Default `24` — 6rem. */
37
+ rowHeight?: number;
38
+ /** Whether widgets can be moved and resized. The handles exist only in edit mode, and only where the layout's own columns are being rendered. */
39
+ editable?: boolean;
40
+ /** What the set of widgets is called. A list needs a name where a page holds more than one. */
41
+ label?: string;
42
+ /** The same, naming an element that already says it. */
43
+ labelledBy?: string;
44
+ }
45
+ export interface WidgetProps<TKey extends keyof ComponentsAndVariants = 'widget'> extends BoxProps<'div', TKey> {
46
+ /** Which layout item this widget is. Left out, it is chrome with no place — a card. */
47
+ id?: string;
48
+ /** What it shows. */
49
+ children?: ReactNode;
50
+ /** The title bar's heading. */
51
+ title?: ReactNode;
52
+ /** What the widget is called where a control has to say so — the handles' labels and the announcements. Defaults to a string `title`, then to `id`. */
53
+ name?: string;
54
+ /** A line under the title, for the question the widget answers rather than the noun it is named after. */
55
+ description?: ReactNode;
56
+ /** The end of the title bar: a menu, a filter, a link out. */
57
+ actions?: ReactNode;
58
+ /** The heading level the title renders at. Default `3`, since a dashboard is a section of a page. */
59
+ level?: 2 | 3 | 4 | 5 | 6;
60
+ /** Bars where the content will be, and `aria-busy` while they are showing. */
61
+ loading?: boolean;
62
+ /** What went wrong, shown in place of the content. With `onRefresh` it comes with a retry. */
63
+ error?: ReactNode;
64
+ /** There is nothing to show: `true` for the default line, or the words to use instead. */
65
+ empty?: boolean | ReactNode;
66
+ /** Fetch it again. A button in the title bar, and the retry an error state offers. */
67
+ onRefresh?: () => void;
68
+ /** The refresh button's accessible name. Default `Refresh <name>`. */
69
+ refreshLabel?: string;
70
+ }
71
+ interface WidgetType {
72
+ <TKey extends keyof ComponentsAndVariants = 'widget'>(props: WidgetProps<TKey>): React.ReactNode;
73
+ displayName?: string;
74
+ }
75
+ /**
76
+ * A dashboard the people using it can rearrange, and a model can write.
77
+ *
78
+ * ```tsx
79
+ * <DashboardGrid layout={layout} onLayoutChange={setLayout} editable>
80
+ * <Widget id="revenue" title="Revenue">…</Widget>
81
+ * <Widget id="orders" title="Orders" loading>…</Widget>
82
+ * </DashboardGrid>
83
+ * ```
84
+ *
85
+ * **The layout is the artifact**, and it is plain JSON: a version, a column count and one `{ id, x, y, w, h }`
86
+ * per widget, in cells. That is what a model emits under `DashboardUtils.SCHEMA`, what a drag reports back
87
+ * and what a host stores — persistence is the app's, because only the app knows whether a dashboard belongs
88
+ * to a user, a team or a URL.
89
+ *
90
+ * **Nothing is measured to lay it out.** A widget's place is `grid-column`/`grid-row`, which is a class, so
91
+ * a layout of any size costs no inline style and no `ResizeObserver`; the narrower arrangements are
92
+ * *projections* of the same layout, computed at render and chosen between by container queries. The one
93
+ * inline style in the component is the translate that keeps a dragged widget under the pointer — a value
94
+ * per frame, which a class would make a rule per frame that is never freed.
95
+ *
96
+ * **The layout is compacted upward**, so a widget cannot be parked in mid-air and two dashboards holding
97
+ * the same widgets in the same places compare equal. A drop below a neighbour rises to meet it.
98
+ *
99
+ * **An arrangement can only be edited in the space it is written in.** Where the grid is rendering a
100
+ * projection the handles are not there at all — hidden by the same container query that chose the
101
+ * projection — because an edit made in six columns is not a layout in twelve, and writing one back would
102
+ * quietly replace the other.
103
+ *
104
+ * @a11y The grid is a `role="list"` named by `label`, and each placed widget a `role="listitem"` — so a
105
+ * reader is told how many widgets there are and which one this is, which a set of `region` landmarks
106
+ * would drown out.
107
+ * @a11y Moving and resizing are a **grab**: Enter or Space on a handle picks the widget up, the arrows
108
+ * move it, Enter drops it and Escape puts it back. Every step is announced in a polite live region that
109
+ * is there before there is anything to say.
110
+ * @a11y The handles are real buttons with `aria-pressed` for the grabbed state and an `aria-describedby`
111
+ * naming the instructions, rather than an HTML5 drag, which no screen reader drives.
112
+ * @keyboard Enter, Space — Picks a widget up from either handle, and drops it again.
113
+ * @keyboard Arrows — One cell, along the reading axis: in a right-to-left page ArrowLeft moves a widget
114
+ * to the *right*. On the resize handle they add and remove a column or a row.
115
+ * @keyboard Escape — Puts the widget back where it was picked up from, and the layout with it.
116
+ */
117
+ declare function DashboardGridImpl<TKey extends keyof ComponentsAndVariants = 'dashboard'>(props: DashboardGridProps<TKey>): import("react").JSX.Element;
118
+ declare const DashboardGrid: typeof DashboardGridImpl;
119
+ declare const Widget: WidgetType;
120
+ export { Widget };
121
+ export default DashboardGrid;