@tappify/extension-sdk 2.1.0-beta.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 (137) hide show
  1. package/README.md +18 -54
  2. package/dist/{chunk-6HETNZJY.cjs → chunk-2DBSVKAU.cjs} +1 -1
  3. package/dist/{chunk-6HETNZJY.cjs.map → chunk-2DBSVKAU.cjs.map} +1 -1
  4. package/dist/{chunk-O357OJXL.cjs → chunk-3YWVYA42.cjs} +18 -14
  5. package/dist/chunk-3YWVYA42.cjs.map +1 -0
  6. package/dist/{chunk-GAUZYBXE.cjs → chunk-5AAPD5WV.cjs} +11 -11
  7. package/dist/chunk-5AAPD5WV.cjs.map +1 -0
  8. package/dist/{chunk-SGEYDV3K.mjs → chunk-BDCX6LRQ.mjs} +3 -3
  9. package/dist/chunk-BDCX6LRQ.mjs.map +1 -0
  10. package/dist/{chunk-KZ5NGFUP.mjs → chunk-C3YNVHBP.mjs} +7 -20
  11. package/dist/chunk-C3YNVHBP.mjs.map +1 -0
  12. package/dist/{chunk-QXD7WVKK.mjs → chunk-CSTYBKZC.mjs} +1 -1
  13. package/dist/chunk-CSTYBKZC.mjs.map +1 -0
  14. package/dist/{chunk-SWG7CNTZ.mjs → chunk-CTSZ5D7S.mjs} +5 -9
  15. package/dist/chunk-CTSZ5D7S.mjs.map +1 -0
  16. package/dist/chunk-EW54H7CR.cjs +722 -0
  17. package/dist/chunk-EW54H7CR.cjs.map +1 -0
  18. package/dist/chunk-EX3NFXDK.mjs +722 -0
  19. package/dist/chunk-EX3NFXDK.mjs.map +1 -0
  20. package/dist/{chunk-A74HCO6T.cjs → chunk-G7XZSN2Q.cjs} +18 -31
  21. package/dist/chunk-G7XZSN2Q.cjs.map +1 -0
  22. package/dist/{chunk-RBIQT75L.mjs → chunk-GG3IX52W.mjs} +2 -2
  23. package/dist/{chunk-SLGOHIBL.mjs → chunk-IGK2AP6J.mjs} +17 -13
  24. package/dist/chunk-IGK2AP6J.mjs.map +1 -0
  25. package/dist/{chunk-VV7ELNRW.cjs → chunk-JMUK3K47.cjs} +3 -3
  26. package/dist/{chunk-VV7ELNRW.cjs.map → chunk-JMUK3K47.cjs.map} +1 -1
  27. package/dist/{chunk-SGCYEEXD.mjs → chunk-MNDVYPEP.mjs} +2 -9
  28. package/dist/chunk-MNDVYPEP.mjs.map +1 -0
  29. package/dist/{chunk-BPCGXXMD.cjs → chunk-NFUPPCX4.cjs} +18 -22
  30. package/dist/chunk-NFUPPCX4.cjs.map +1 -0
  31. package/dist/{chunk-PRCZMOLD.cjs → chunk-OPPYEA7F.cjs} +3 -3
  32. package/dist/{chunk-PRCZMOLD.cjs.map → chunk-OPPYEA7F.cjs.map} +1 -1
  33. package/dist/{chunk-7YKH4LZN.cjs → chunk-PYJMJF44.cjs} +2 -9
  34. package/dist/chunk-PYJMJF44.cjs.map +1 -0
  35. package/dist/{chunk-QA5AL3VZ.mjs → chunk-QHM7XNRK.mjs} +2 -2
  36. package/dist/{chunk-QA5AL3VZ.mjs.map → chunk-QHM7XNRK.mjs.map} +1 -1
  37. package/dist/codegen.cjs.map +1 -1
  38. package/dist/codegen.d.cts +7 -40
  39. package/dist/codegen.d.ts +7 -40
  40. package/dist/codegen.mjs.map +1 -1
  41. package/dist/{constants-BAimnqOK.d.cts → constants-DJ_KgZpN.d.cts} +8 -41
  42. package/dist/{constants-BAimnqOK.d.ts → constants-DJ_KgZpN.d.ts} +8 -41
  43. package/dist/{context-Cr93B05o.d.ts → context-Dt6xh_5t.d.cts} +4 -5
  44. package/dist/{context-DJ3REtdT.d.cts → context-Ml7k_V14.d.ts} +4 -5
  45. package/dist/{errors-Cqh0wrqt.d.ts → errors-COsBh7kd.d.cts} +2 -3
  46. package/dist/{errors-Cqh0wrqt.d.cts → errors-COsBh7kd.d.ts} +2 -3
  47. package/dist/handler-CZIlZ5vu.d.ts +227 -0
  48. package/dist/handler-kU9Jf36K.d.cts +227 -0
  49. package/dist/host.cjs +4 -4
  50. package/dist/host.d.cts +4 -4
  51. package/dist/host.d.ts +4 -4
  52. package/dist/host.mjs +3 -3
  53. package/dist/index.cjs +34 -5
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +627 -69
  56. package/dist/index.d.ts +627 -69
  57. package/dist/index.mjs +33 -4
  58. package/dist/manifest.cjs +23 -132
  59. package/dist/manifest.cjs.map +1 -1
  60. package/dist/manifest.d.cts +12 -93
  61. package/dist/manifest.d.ts +12 -93
  62. package/dist/manifest.mjs +9 -118
  63. package/dist/manifest.mjs.map +1 -1
  64. package/dist/server.cjs +66 -14
  65. package/dist/server.cjs.map +1 -1
  66. package/dist/server.d.cts +6 -6
  67. package/dist/server.d.ts +6 -6
  68. package/dist/server.mjs +61 -9
  69. package/dist/server.mjs.map +1 -1
  70. package/dist/styles.css +367 -0
  71. package/dist/testing/index.cjs +13 -72
  72. package/dist/testing/index.cjs.map +1 -1
  73. package/dist/testing/index.d.cts +10 -29
  74. package/dist/testing/index.d.ts +10 -29
  75. package/dist/testing/index.mjs +13 -72
  76. package/dist/testing/index.mjs.map +1 -1
  77. package/dist/testing/jest.cjs +5 -5
  78. package/dist/testing/jest.mjs +2 -2
  79. package/dist/testing/vitest.cjs +5 -5
  80. package/dist/testing/vitest.cjs.map +1 -1
  81. package/dist/testing/vitest.d.cts +0 -10
  82. package/dist/testing/vitest.d.ts +0 -10
  83. package/dist/testing/vitest.mjs +2 -2
  84. package/dist/testing/vitest.mjs.map +1 -1
  85. package/dist/{types-BYGcp5pZ.d.ts → types-DDAGLpov.d.ts} +3 -4
  86. package/dist/{types-I1JOZyI_.d.cts → types-DrpQSv_2.d.cts} +3 -4
  87. package/dist/vite-preview.cjs +125 -294
  88. package/dist/vite-preview.cjs.map +1 -1
  89. package/dist/vite-preview.d.cts +24 -6
  90. package/dist/vite-preview.d.ts +24 -6
  91. package/dist/vite-preview.mjs +118 -287
  92. package/dist/vite-preview.mjs.map +1 -1
  93. package/dist/vite.cjs +23 -133
  94. package/dist/vite.cjs.map +1 -1
  95. package/dist/vite.d.cts +7 -40
  96. package/dist/vite.d.ts +7 -40
  97. package/dist/vite.mjs +17 -127
  98. package/dist/vite.mjs.map +1 -1
  99. package/package.json +2 -12
  100. package/schema/extension-v2.json +121 -432
  101. package/dist/chunk-2CY5HVY7.mjs +0 -59
  102. package/dist/chunk-2CY5HVY7.mjs.map +0 -1
  103. package/dist/chunk-7YKH4LZN.cjs.map +0 -1
  104. package/dist/chunk-A74HCO6T.cjs.map +0 -1
  105. package/dist/chunk-BPCGXXMD.cjs.map +0 -1
  106. package/dist/chunk-EB5XWQAU.cjs +0 -132
  107. package/dist/chunk-EB5XWQAU.cjs.map +0 -1
  108. package/dist/chunk-GAUZYBXE.cjs.map +0 -1
  109. package/dist/chunk-KZ5NGFUP.mjs.map +0 -1
  110. package/dist/chunk-O357OJXL.cjs.map +0 -1
  111. package/dist/chunk-ONJMPO3P.cjs +0 -59
  112. package/dist/chunk-ONJMPO3P.cjs.map +0 -1
  113. package/dist/chunk-QJMZRV7A.mjs +0 -132
  114. package/dist/chunk-QJMZRV7A.mjs.map +0 -1
  115. package/dist/chunk-QXD7WVKK.mjs.map +0 -1
  116. package/dist/chunk-SGCYEEXD.mjs.map +0 -1
  117. package/dist/chunk-SGEYDV3K.mjs.map +0 -1
  118. package/dist/chunk-SLGOHIBL.mjs.map +0 -1
  119. package/dist/chunk-SWG7CNTZ.mjs.map +0 -1
  120. package/dist/client-CV1qzQgx.d.cts +0 -25
  121. package/dist/client-CnlSHDWE.d.ts +0 -25
  122. package/dist/ui/styles.css +0 -740
  123. package/dist/ui.cjs +0 -1018
  124. package/dist/ui.cjs.map +0 -1
  125. package/dist/ui.d.cts +0 -231
  126. package/dist/ui.d.ts +0 -231
  127. package/dist/ui.mjs +0 -1018
  128. package/dist/ui.mjs.map +0 -1
  129. package/dist/vite-preview-bootstrap.cjs +0 -66
  130. package/dist/vite-preview-bootstrap.cjs.map +0 -1
  131. package/dist/vite-preview-bootstrap.d.cts +0 -15
  132. package/dist/vite-preview-bootstrap.d.ts +0 -15
  133. package/dist/vite-preview-bootstrap.mjs +0 -66
  134. package/dist/vite-preview-bootstrap.mjs.map +0 -1
  135. package/dist/work-events-DB3WR468.d.ts +0 -547
  136. package/dist/work-events-Dxe7Djdy.d.cts +0 -547
  137. /package/dist/{chunk-RBIQT75L.mjs.map → chunk-GG3IX52W.mjs.map} +0 -0
@@ -0,0 +1,722 @@
1
+ import {
2
+ dataQueryKey,
3
+ installPrefix,
4
+ procedurePrefix,
5
+ procedureQueryKey,
6
+ storageQueryKey,
7
+ useTapAuth,
8
+ useTapContext,
9
+ useTapFilters,
10
+ useTapParams,
11
+ useTapProject,
12
+ useTapQuery,
13
+ useTapServer,
14
+ useTapSize,
15
+ useTapState,
16
+ useTapStorage,
17
+ useTapTheme
18
+ } from "./chunk-BDCX6LRQ.mjs";
19
+ import {
20
+ useTap
21
+ } from "./chunk-QHM7XNRK.mjs";
22
+ import {
23
+ TAP_ERROR_CODES,
24
+ TapError,
25
+ TapServerError
26
+ } from "./chunk-CSTYBKZC.mjs";
27
+ import {
28
+ __export
29
+ } from "./chunk-J5LGTIGS.mjs";
30
+
31
+ // src/index.ts
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
+ TAP_ERROR_CODES: () => TAP_ERROR_CODES,
35
+ TapButton: () => TapButton,
36
+ TapCard: () => TapCard,
37
+ TapDialog: () => TapDialog,
38
+ TapEmptyState: () => TapEmptyState,
39
+ TapError: () => TapError,
40
+ TapErrorState: () => TapErrorState,
41
+ TapForm: () => TapForm,
42
+ TapInput: () => TapInput,
43
+ TapPageHeader: () => TapPageHeader,
44
+ TapSelect: () => TapSelect,
45
+ TapServerError: () => TapServerError,
46
+ TapSkeleton: () => TapSkeleton,
47
+ TapStat: () => TapStat,
48
+ TapTable: () => TapTable,
49
+ cn: () => cn,
50
+ dataQueryKey: () => dataQueryKey,
51
+ formFields: () => formFields,
52
+ installPrefix: () => installPrefix,
53
+ procedurePrefix: () => procedurePrefix,
54
+ procedureQueryKey: () => procedureQueryKey,
55
+ storageQueryKey: () => storageQueryKey,
56
+ useTap: () => useTap,
57
+ useTapAuth: () => useTapAuth,
58
+ useTapContext: () => useTapContext,
59
+ useTapFilters: () => useTapFilters,
60
+ useTapParams: () => useTapParams,
61
+ useTapProject: () => useTapProject,
62
+ useTapQuery: () => useTapQuery,
63
+ useTapServer: () => useTapServer,
64
+ useTapSize: () => useTapSize,
65
+ useTapState: () => useTapState,
66
+ useTapStorage: () => useTapStorage,
67
+ useTapTheme: () => useTapTheme
68
+ });
69
+
70
+ // src/client/ui/cn.ts
71
+ import clsx from "clsx";
72
+ function cn(...values) {
73
+ return clsx(values);
74
+ }
75
+
76
+ // src/client/ui/primitives.tsx
77
+ import {
78
+ useId
79
+ } from "react";
80
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
81
+ function TapButton({
82
+ variant = "primary",
83
+ size = "md",
84
+ loading = false,
85
+ disabled,
86
+ className,
87
+ children,
88
+ ...rest
89
+ }) {
90
+ return /* @__PURE__ */ jsx(
91
+ "button",
92
+ {
93
+ type: "button",
94
+ ...rest,
95
+ disabled: disabled === true || loading,
96
+ "aria-busy": loading || void 0,
97
+ className: cn(
98
+ "tap-btn",
99
+ `tap-btn--${variant}`,
100
+ size === "sm" && "tap-btn--sm",
101
+ className
102
+ ),
103
+ children
104
+ }
105
+ );
106
+ }
107
+ function TapCard({
108
+ title,
109
+ action,
110
+ footer,
111
+ className,
112
+ children
113
+ }) {
114
+ return /* @__PURE__ */ jsxs("section", { className: cn("tap-card", className), children: [
115
+ (title !== void 0 || action !== void 0) && /* @__PURE__ */ jsxs("header", { className: "tap-card__head", children: [
116
+ /* @__PURE__ */ jsx("span", { children: title }),
117
+ action
118
+ ] }),
119
+ /* @__PURE__ */ jsx("div", { className: "tap-card__body", children }),
120
+ footer !== void 0 && /* @__PURE__ */ jsx("footer", { className: "tap-card__foot", children: footer })
121
+ ] });
122
+ }
123
+ function TapPageHeader({
124
+ title,
125
+ description,
126
+ actions
127
+ }) {
128
+ return /* @__PURE__ */ jsxs("div", { className: "tap-page-header", children: [
129
+ /* @__PURE__ */ jsxs("div", { children: [
130
+ /* @__PURE__ */ jsx("h2", { className: "tap-page-header__title", children: title }),
131
+ description !== void 0 && /* @__PURE__ */ jsx("p", { className: "tap-page-header__description", children: description })
132
+ ] }),
133
+ actions !== void 0 && /* @__PURE__ */ jsx("div", { className: "tap-row", children: actions })
134
+ ] });
135
+ }
136
+ function TapSkeleton({
137
+ lines = 1,
138
+ width,
139
+ className
140
+ }) {
141
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx(
142
+ "span",
143
+ {
144
+ className: cn("tap-skeleton", className),
145
+ style: { width: width ?? `${String(100 - index * 12)}%` }
146
+ },
147
+ index
148
+ )) });
149
+ }
150
+ function TapEmptyState({
151
+ title,
152
+ description,
153
+ action
154
+ }) {
155
+ return /* @__PURE__ */ jsxs("div", { className: "tap-state", children: [
156
+ /* @__PURE__ */ jsx("span", { className: "tap-state__title", children: title }),
157
+ description !== void 0 && /* @__PURE__ */ jsx("span", { children: description }),
158
+ action
159
+ ] });
160
+ }
161
+ function TapErrorState({
162
+ title,
163
+ description,
164
+ code,
165
+ onRetry
166
+ }) {
167
+ return /* @__PURE__ */ jsxs("div", { className: "tap-state tap-state--error", role: "alert", children: [
168
+ /* @__PURE__ */ jsx("span", { className: "tap-state__title", children: title }),
169
+ description !== void 0 && /* @__PURE__ */ jsx("span", { children: description }),
170
+ code !== void 0 && /* @__PURE__ */ jsx("span", { className: "tap-state__code", children: code }),
171
+ onRetry !== void 0 && /* @__PURE__ */ jsx(TapButton, { variant: "secondary", size: "sm", onClick: onRetry, children: "Retry" })
172
+ ] });
173
+ }
174
+ function TapStat({
175
+ label,
176
+ value,
177
+ delta,
178
+ hint
179
+ }) {
180
+ const percent = delta === void 0 ? void 0 : `${delta > 0 ? "+" : ""}${String(Math.round(delta * 100))}%`;
181
+ return /* @__PURE__ */ jsxs("div", { className: "tap-stat", children: [
182
+ /* @__PURE__ */ jsx("span", { className: "tap-stat__label", children: label }),
183
+ /* @__PURE__ */ jsx("span", { className: "tap-stat__value", children: value }),
184
+ percent !== void 0 && /* @__PURE__ */ jsx(
185
+ "span",
186
+ {
187
+ className: cn(
188
+ delta !== void 0 && delta < 0 ? "tap-stat__delta--down" : "tap-stat__delta--up"
189
+ ),
190
+ children: percent
191
+ }
192
+ ),
193
+ hint !== void 0 && /* @__PURE__ */ jsx("span", { className: "tap-field__hint", children: hint })
194
+ ] });
195
+ }
196
+ function FieldFrame({
197
+ id,
198
+ label,
199
+ hint,
200
+ error,
201
+ hintId,
202
+ errorId,
203
+ children
204
+ }) {
205
+ return /* @__PURE__ */ jsxs("div", { className: "tap-field", children: [
206
+ label !== void 0 && /* @__PURE__ */ jsx("label", { className: "tap-field__label", htmlFor: id, children: label }),
207
+ children,
208
+ hint !== void 0 && /* @__PURE__ */ jsx("span", { className: "tap-field__hint", id: hintId, children: hint }),
209
+ error !== void 0 && /* @__PURE__ */ jsx("span", { className: "tap-field__error", id: errorId, children: error })
210
+ ] });
211
+ }
212
+ function describedByOf(fieldId, hint, error) {
213
+ const value = cn(
214
+ hint !== void 0 && `${fieldId}-hint`,
215
+ error !== void 0 && `${fieldId}-error`
216
+ );
217
+ return value === "" ? void 0 : value;
218
+ }
219
+ function TapSingleLineInput({
220
+ label,
221
+ error,
222
+ hint,
223
+ className,
224
+ id,
225
+ ...rest
226
+ }) {
227
+ const generated = useId();
228
+ const fieldId = id ?? generated;
229
+ return /* @__PURE__ */ jsx(
230
+ FieldFrame,
231
+ {
232
+ id: fieldId,
233
+ label,
234
+ hint,
235
+ error,
236
+ hintId: `${fieldId}-hint`,
237
+ errorId: `${fieldId}-error`,
238
+ children: /* @__PURE__ */ jsx(
239
+ "input",
240
+ {
241
+ ...rest,
242
+ id: fieldId,
243
+ className: cn(
244
+ "tap-control",
245
+ error !== void 0 && "tap-control--invalid",
246
+ className
247
+ ),
248
+ "aria-invalid": error !== void 0 || void 0,
249
+ "aria-describedby": describedByOf(fieldId, hint, error)
250
+ }
251
+ )
252
+ }
253
+ );
254
+ }
255
+ function TapTextarea({
256
+ label,
257
+ error,
258
+ hint,
259
+ className,
260
+ id,
261
+ rows,
262
+ ...rest
263
+ }) {
264
+ const generated = useId();
265
+ const fieldId = id ?? generated;
266
+ return /* @__PURE__ */ jsx(
267
+ FieldFrame,
268
+ {
269
+ id: fieldId,
270
+ label,
271
+ hint,
272
+ error,
273
+ hintId: `${fieldId}-hint`,
274
+ errorId: `${fieldId}-error`,
275
+ children: /* @__PURE__ */ jsx(
276
+ "textarea",
277
+ {
278
+ ...rest,
279
+ id: fieldId,
280
+ rows: rows ?? 4,
281
+ className: cn(
282
+ "tap-control",
283
+ error !== void 0 && "tap-control--invalid",
284
+ className
285
+ ),
286
+ "aria-invalid": error !== void 0 || void 0,
287
+ "aria-describedby": describedByOf(fieldId, hint, error)
288
+ }
289
+ )
290
+ }
291
+ );
292
+ }
293
+ function TapInput(props) {
294
+ if (props.multiline === true) {
295
+ const { multiline: _multiline2, ...rest2 } = props;
296
+ return /* @__PURE__ */ jsx(TapTextarea, { ...rest2 });
297
+ }
298
+ const { multiline: _multiline, ...rest } = props;
299
+ return /* @__PURE__ */ jsx(TapSingleLineInput, { ...rest });
300
+ }
301
+ function TapSelect({
302
+ label,
303
+ error,
304
+ hint,
305
+ options,
306
+ className,
307
+ id,
308
+ ...rest
309
+ }) {
310
+ const generated = useId();
311
+ const fieldId = id ?? generated;
312
+ return /* @__PURE__ */ jsx(
313
+ FieldFrame,
314
+ {
315
+ id: fieldId,
316
+ label,
317
+ hint,
318
+ error,
319
+ hintId: `${fieldId}-hint`,
320
+ errorId: `${fieldId}-error`,
321
+ children: /* @__PURE__ */ jsx(
322
+ "select",
323
+ {
324
+ ...rest,
325
+ id: fieldId,
326
+ className: cn(
327
+ "tap-control",
328
+ error !== void 0 && "tap-control--invalid",
329
+ className
330
+ ),
331
+ "aria-invalid": error !== void 0 || void 0,
332
+ "aria-describedby": describedByOf(fieldId, hint, error),
333
+ children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
334
+ }
335
+ )
336
+ }
337
+ );
338
+ }
339
+ function cellText(row, key) {
340
+ if (row === null || typeof row !== "object") return "";
341
+ const value = Reflect.get(row, key);
342
+ if (typeof value === "string") return value;
343
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
344
+ return String(value);
345
+ }
346
+ return "";
347
+ }
348
+ function TapTable({
349
+ columns,
350
+ rows,
351
+ rowKey,
352
+ empty,
353
+ onRowClick
354
+ }) {
355
+ if (rows.length === 0 && empty !== void 0) {
356
+ return /* @__PURE__ */ jsx(Fragment, { children: empty });
357
+ }
358
+ return /* @__PURE__ */ jsx("div", { className: "tap-table-wrap", children: /* @__PURE__ */ jsxs("table", { className: "tap-table", children: [
359
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx(
360
+ "th",
361
+ {
362
+ className: cn(column.align === "end" && "tap-table__end"),
363
+ scope: "col",
364
+ children: column.label
365
+ },
366
+ column.key
367
+ )) }) }),
368
+ /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsx(
369
+ "tr",
370
+ {
371
+ "data-clickable": onRowClick !== void 0,
372
+ tabIndex: onRowClick === void 0 ? void 0 : 0,
373
+ onClick: onRowClick === void 0 ? void 0 : () => onRowClick(row),
374
+ onKeyDown: onRowClick === void 0 ? void 0 : (event) => {
375
+ if (event.key !== "Enter" && event.key !== " ") return;
376
+ event.preventDefault();
377
+ onRowClick(row);
378
+ },
379
+ children: columns.map((column) => /* @__PURE__ */ jsx(
380
+ "td",
381
+ {
382
+ className: cn(column.align === "end" && "tap-table__end"),
383
+ children: column.render ? column.render(row) : cellText(row, column.key)
384
+ },
385
+ column.key
386
+ ))
387
+ },
388
+ rowKey(row)
389
+ )) })
390
+ ] }) });
391
+ }
392
+
393
+ // src/client/ui/TapDialog.tsx
394
+ import {
395
+ useCallback,
396
+ useEffect,
397
+ useId as useId2,
398
+ useRef
399
+ } from "react";
400
+ import { createPortal } from "react-dom";
401
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
402
+ var DIALOG_CLOSE_EVENT = "tap:dialog-close";
403
+ var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
404
+ function activeElementNear(node) {
405
+ const root = node?.getRootNode();
406
+ return root instanceof ShadowRoot ? root.activeElement : document.activeElement;
407
+ }
408
+ function TapDialog({
409
+ open,
410
+ title,
411
+ onClose,
412
+ footer,
413
+ children,
414
+ labelledBy
415
+ }) {
416
+ const tap = useTap();
417
+ const dialogRef = useRef(null);
418
+ const titleId = useId2();
419
+ const focusables = useCallback(() => {
420
+ const root = dialogRef.current;
421
+ if (root === null) return [];
422
+ return Array.from(root.querySelectorAll(FOCUSABLE));
423
+ }, []);
424
+ useEffect(() => {
425
+ if (!open) return;
426
+ const previous = activeElementNear(dialogRef.current);
427
+ focusables()[0]?.focus();
428
+ return () => {
429
+ if (previous instanceof HTMLElement) previous.focus();
430
+ };
431
+ }, [open, focusables]);
432
+ useEffect(() => {
433
+ const portal = tap.__portal;
434
+ if (!open || portal === null) return;
435
+ const close = () => {
436
+ onClose();
437
+ };
438
+ portal.addEventListener(DIALOG_CLOSE_EVENT, close);
439
+ return () => {
440
+ portal.removeEventListener(DIALOG_CLOSE_EVENT, close);
441
+ };
442
+ }, [open, tap, onClose]);
443
+ if (!open) return null;
444
+ if (tap.__portal === null) {
445
+ throw new TapError(
446
+ "TAP_NO_PORTAL",
447
+ "TapDialog needs the host portal node, which the mount sets inside its shadow root. Render this dialog from a contribution entry, or use renderWithTap() in tests."
448
+ );
449
+ }
450
+ const onKeyDown = (event) => {
451
+ if (event.key === "Escape") {
452
+ event.stopPropagation();
453
+ event.preventDefault();
454
+ onClose();
455
+ return;
456
+ }
457
+ if (event.key !== "Tab") return;
458
+ const items = focusables();
459
+ if (items.length === 0) return;
460
+ const first = items[0];
461
+ const last = items[items.length - 1];
462
+ const active = activeElementNear(dialogRef.current);
463
+ if (event.shiftKey && active === first) {
464
+ event.preventDefault();
465
+ last.focus();
466
+ return;
467
+ }
468
+ if (!event.shiftKey && active === last) {
469
+ event.preventDefault();
470
+ first.focus();
471
+ }
472
+ };
473
+ return createPortal(
474
+ /* @__PURE__ */ jsx2(
475
+ "div",
476
+ {
477
+ className: "tap-dialog-scrim",
478
+ onKeyDown,
479
+ onMouseDown: (event) => {
480
+ if (event.target === event.currentTarget) onClose();
481
+ },
482
+ children: /* @__PURE__ */ jsxs2(
483
+ "div",
484
+ {
485
+ ref: dialogRef,
486
+ className: "tap-dialog",
487
+ role: "dialog",
488
+ "aria-modal": "true",
489
+ "aria-labelledby": labelledBy ?? titleId,
490
+ children: [
491
+ /* @__PURE__ */ jsx2("header", { className: "tap-dialog__head", id: titleId, children: title }),
492
+ /* @__PURE__ */ jsx2("div", { className: "tap-dialog__body", children }),
493
+ footer !== void 0 && /* @__PURE__ */ jsx2("footer", { className: "tap-dialog__foot", children: footer })
494
+ ]
495
+ }
496
+ )
497
+ }
498
+ ),
499
+ tap.__portal
500
+ );
501
+ }
502
+
503
+ // src/client/ui/TapForm.tsx
504
+ import {
505
+ useMemo,
506
+ useState
507
+ } from "react";
508
+ import { z } from "zod";
509
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
510
+ function humanise(name) {
511
+ const spaced = name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
512
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
513
+ }
514
+ function readString(source, key) {
515
+ const value = source[key];
516
+ return typeof value === "string" ? value : void 0;
517
+ }
518
+ function kindOf(property) {
519
+ if (Array.isArray(property.enum)) return "enum";
520
+ const type = readString(property, "type");
521
+ if (type === "boolean") return "boolean";
522
+ if (type === "number" || type === "integer") return "number";
523
+ const format = readString(property, "format");
524
+ if (format === "textarea") return "textarea";
525
+ if (format === "date") return "date";
526
+ return "text";
527
+ }
528
+ function formFields(schema) {
529
+ const properties = schema.properties;
530
+ if (typeof properties !== "object" || properties === null) return [];
531
+ const required = new Set(
532
+ Array.isArray(schema.required) ? schema.required.filter(
533
+ (name) => typeof name === "string"
534
+ ) : []
535
+ );
536
+ return Object.entries(properties).flatMap(([name, raw]) => {
537
+ if (typeof raw !== "object" || raw === null) return [];
538
+ const property = { ...raw };
539
+ const kind = kindOf(property);
540
+ const field = {
541
+ name,
542
+ label: readString(property, "title") ?? humanise(name),
543
+ kind,
544
+ required: required.has(name)
545
+ };
546
+ const hint = readString(property, "description");
547
+ if (hint !== void 0) field.hint = hint;
548
+ if (kind === "enum" && Array.isArray(property.enum)) {
549
+ field.options = property.enum.filter((option) => typeof option === "string").map((option) => ({ value: option, label: option }));
550
+ }
551
+ return [field];
552
+ });
553
+ }
554
+ function requiredErrors(fields, values) {
555
+ const errors = {};
556
+ for (const field of fields) {
557
+ const value = values[field.name];
558
+ if (field.required && (value === void 0 || value === "")) {
559
+ errors[field.name] = `${field.label} is required.`;
560
+ }
561
+ }
562
+ return errors;
563
+ }
564
+ function compile(schema) {
565
+ try {
566
+ return z.fromJSONSchema(schema);
567
+ } catch {
568
+ return null;
569
+ }
570
+ }
571
+ function validate(fields, compiled, values) {
572
+ const errors = requiredErrors(fields, values);
573
+ if (compiled === null) return errors;
574
+ const result = compiled.safeParse(values);
575
+ if (result.success) return errors;
576
+ for (const issue of result.error.issues) {
577
+ const key = issue.path[0];
578
+ if (typeof key === "string" && errors[key] === void 0) {
579
+ errors[key] = issue.message;
580
+ }
581
+ }
582
+ return errors;
583
+ }
584
+ function clean(fields, values) {
585
+ const output = {};
586
+ for (const field of fields) {
587
+ const value = values[field.name];
588
+ if (value === void 0 || value === "") continue;
589
+ output[field.name] = value;
590
+ }
591
+ return output;
592
+ }
593
+ function TapForm({
594
+ schema,
595
+ value,
596
+ submitLabel = "Submit",
597
+ cancelLabel = "Cancel",
598
+ busy = false,
599
+ onSubmit,
600
+ onCancel
601
+ }) {
602
+ const fields = useMemo(() => formFields(schema), [schema]);
603
+ const compiled = useMemo(() => compile(schema), [schema]);
604
+ const [values, setValues] = useState(() => ({ ...value }));
605
+ const [errors, setErrors] = useState({});
606
+ const set = (name, next) => {
607
+ setValues((current) => ({ ...current, [name]: next }));
608
+ setErrors((current) => {
609
+ if (current[name] === void 0) return current;
610
+ const { [name]: _cleared, ...rest } = current;
611
+ return rest;
612
+ });
613
+ };
614
+ const submit = (event) => {
615
+ event.preventDefault();
616
+ const candidate = clean(fields, values);
617
+ const found = validate(fields, compiled, candidate);
618
+ setErrors(found);
619
+ if (Object.keys(found).length > 0) return;
620
+ void onSubmit(candidate);
621
+ };
622
+ return /* @__PURE__ */ jsxs3("form", { className: "tap-form", onSubmit: submit, noValidate: true, children: [
623
+ fields.map((field) => {
624
+ const raw = values[field.name];
625
+ if (field.kind === "enum") {
626
+ return /* @__PURE__ */ jsx3(
627
+ TapSelect,
628
+ {
629
+ label: field.label,
630
+ hint: field.hint,
631
+ error: errors[field.name],
632
+ required: field.required,
633
+ value: typeof raw === "string" ? raw : "",
634
+ options: [
635
+ { value: "", label: "Choose one" },
636
+ ...field.options ?? []
637
+ ],
638
+ onChange: (event) => {
639
+ set(field.name, event.target.value);
640
+ }
641
+ },
642
+ field.name
643
+ );
644
+ }
645
+ if (field.kind === "boolean") {
646
+ return /* @__PURE__ */ jsx3(
647
+ TapInput,
648
+ {
649
+ type: "checkbox",
650
+ label: field.label,
651
+ hint: field.hint,
652
+ error: errors[field.name],
653
+ className: "tap-control--checkbox",
654
+ checked: raw === true,
655
+ onChange: (event) => {
656
+ set(field.name, event.target.checked);
657
+ }
658
+ },
659
+ field.name
660
+ );
661
+ }
662
+ if (field.kind === "textarea") {
663
+ return /* @__PURE__ */ jsx3(
664
+ TapInput,
665
+ {
666
+ multiline: true,
667
+ label: field.label,
668
+ hint: field.hint,
669
+ error: errors[field.name],
670
+ required: field.required,
671
+ value: typeof raw === "string" ? raw : "",
672
+ onChange: (event) => {
673
+ set(field.name, event.target.value);
674
+ }
675
+ },
676
+ field.name
677
+ );
678
+ }
679
+ return /* @__PURE__ */ jsx3(
680
+ TapInput,
681
+ {
682
+ type: field.kind === "number" ? "number" : field.kind === "date" ? "date" : "text",
683
+ label: field.label,
684
+ hint: field.hint,
685
+ error: errors[field.name],
686
+ required: field.required,
687
+ value: typeof raw === "string" || typeof raw === "number" ? String(raw) : "",
688
+ onChange: (event) => {
689
+ set(
690
+ field.name,
691
+ field.kind === "number" ? event.target.value === "" ? "" : Number(event.target.value) : event.target.value
692
+ );
693
+ }
694
+ },
695
+ field.name
696
+ );
697
+ }),
698
+ /* @__PURE__ */ jsxs3("div", { className: "tap-form__actions", children: [
699
+ onCancel !== void 0 && /* @__PURE__ */ jsx3(TapButton, { variant: "secondary", onClick: onCancel, children: cancelLabel }),
700
+ /* @__PURE__ */ jsx3(TapButton, { type: "submit", loading: busy, children: submitLabel })
701
+ ] })
702
+ ] });
703
+ }
704
+
705
+ export {
706
+ cn,
707
+ TapButton,
708
+ TapCard,
709
+ TapPageHeader,
710
+ TapSkeleton,
711
+ TapEmptyState,
712
+ TapErrorState,
713
+ TapStat,
714
+ TapInput,
715
+ TapSelect,
716
+ TapTable,
717
+ TapDialog,
718
+ formFields,
719
+ TapForm,
720
+ src_exports
721
+ };
722
+ //# sourceMappingURL=chunk-EX3NFXDK.mjs.map