@tappify/extension-sdk 2.0.0 → 2.1.0-beta.1
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 +208 -0
- package/dist/chunk-2CY5HVY7.mjs +59 -0
- package/dist/chunk-2CY5HVY7.mjs.map +1 -0
- package/dist/{chunk-2DBSVKAU.cjs → chunk-6HETNZJY.cjs} +1 -1
- package/dist/chunk-6HETNZJY.cjs.map +1 -0
- package/dist/{chunk-PYJMJF44.cjs → chunk-7YKH4LZN.cjs} +9 -2
- package/dist/chunk-7YKH4LZN.cjs.map +1 -0
- package/dist/{chunk-RMUWRESK.cjs → chunk-A74HCO6T.cjs} +31 -18
- package/dist/chunk-A74HCO6T.cjs.map +1 -0
- package/dist/chunk-BPCGXXMD.cjs +781 -0
- package/dist/chunk-BPCGXXMD.cjs.map +1 -0
- package/dist/chunk-EB5XWQAU.cjs +132 -0
- package/dist/chunk-EB5XWQAU.cjs.map +1 -0
- package/dist/{chunk-5AAPD5WV.cjs → chunk-GAUZYBXE.cjs} +11 -11
- package/dist/chunk-GAUZYBXE.cjs.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +10 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/{chunk-ZT35X7KL.mjs → chunk-KZ5NGFUP.mjs} +22 -9
- package/dist/chunk-KZ5NGFUP.mjs.map +1 -0
- package/dist/chunk-NN263PGQ.cjs +70 -0
- package/dist/chunk-NN263PGQ.cjs.map +1 -0
- package/dist/{chunk-3YWVYA42.cjs → chunk-O357OJXL.cjs} +14 -18
- package/dist/chunk-O357OJXL.cjs.map +1 -0
- package/dist/chunk-ONJMPO3P.cjs +59 -0
- package/dist/chunk-ONJMPO3P.cjs.map +1 -0
- package/dist/{chunk-OPPYEA7F.cjs → chunk-PRCZMOLD.cjs} +3 -3
- package/dist/chunk-PRCZMOLD.cjs.map +1 -0
- package/dist/chunk-Q7SFCCGT.cjs +10 -0
- package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
- package/dist/{chunk-QHM7XNRK.mjs → chunk-QA5AL3VZ.mjs} +2 -2
- package/dist/{chunk-QHM7XNRK.mjs.map → chunk-QA5AL3VZ.mjs.map} +1 -1
- package/dist/chunk-QJMZRV7A.mjs +132 -0
- package/dist/chunk-QJMZRV7A.mjs.map +1 -0
- package/dist/{chunk-CSTYBKZC.mjs → chunk-QXD7WVKK.mjs} +1 -1
- package/dist/chunk-QXD7WVKK.mjs.map +1 -0
- package/dist/{chunk-4C5Z3CIK.mjs → chunk-RBIQT75L.mjs} +2 -2
- package/dist/chunk-RBIQT75L.mjs.map +1 -0
- package/dist/{chunk-MNDVYPEP.mjs → chunk-SGCYEEXD.mjs} +9 -2
- package/dist/chunk-SGCYEEXD.mjs.map +1 -0
- package/dist/{chunk-BDCX6LRQ.mjs → chunk-SGEYDV3K.mjs} +3 -3
- package/dist/chunk-SGEYDV3K.mjs.map +1 -0
- package/dist/{chunk-IGK2AP6J.mjs → chunk-SLGOHIBL.mjs} +13 -17
- package/dist/chunk-SLGOHIBL.mjs.map +1 -0
- package/dist/chunk-SWG7CNTZ.mjs +781 -0
- package/dist/chunk-SWG7CNTZ.mjs.map +1 -0
- package/dist/{chunk-7KIKOH5H.cjs → chunk-VV7ELNRW.cjs} +3 -3
- package/dist/chunk-VV7ELNRW.cjs.map +1 -0
- package/dist/chunk-Z43DPNZ6.mjs +70 -0
- package/dist/chunk-Z43DPNZ6.mjs.map +1 -0
- package/dist/client-CV1qzQgx.d.cts +25 -0
- package/dist/client-CnlSHDWE.d.ts +25 -0
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +40 -7
- package/dist/codegen.d.ts +40 -7
- package/dist/codegen.mjs.map +1 -1
- package/dist/{constants-DJ_KgZpN.d.cts → constants-BAimnqOK.d.cts} +41 -8
- package/dist/{constants-DJ_KgZpN.d.ts → constants-BAimnqOK.d.ts} +41 -8
- package/dist/{context-Ml7k_V14.d.ts → context-Cr93B05o.d.ts} +5 -4
- package/dist/{context-Dt6xh_5t.d.cts → context-DJ3REtdT.d.cts} +5 -4
- package/dist/{errors-COsBh7kd.d.cts → errors-Cqh0wrqt.d.cts} +3 -2
- package/dist/{errors-COsBh7kd.d.ts → errors-Cqh0wrqt.d.ts} +3 -2
- package/dist/host.cjs +5 -4
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts +5 -5
- package/dist/host.d.ts +5 -5
- package/dist/host.mjs +4 -3
- package/dist/host.mjs.map +1 -1
- package/dist/index.cjs +9 -656
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -627
- package/dist/index.d.ts +69 -627
- package/dist/index.mjs +5 -652
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.cjs +138 -85
- package/dist/manifest.cjs.map +1 -1
- package/dist/manifest.d.cts +93 -12
- package/dist/manifest.d.ts +93 -12
- package/dist/manifest.mjs +125 -72
- package/dist/manifest.mjs.map +1 -1
- package/dist/server.cjs +15 -66
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +6 -6
- package/dist/server.d.ts +6 -6
- package/dist/server.mjs +10 -61
- package/dist/server.mjs.map +1 -1
- package/dist/testing/index.cjs +73 -750
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +30 -11
- package/dist/testing/index.d.ts +30 -11
- package/dist/testing/index.mjs +89 -766
- package/dist/testing/index.mjs.map +1 -1
- package/dist/testing/jest.cjs +6 -5
- package/dist/testing/jest.cjs.map +1 -1
- package/dist/testing/jest.mjs +3 -2
- package/dist/testing/jest.mjs.map +1 -1
- package/dist/testing/vitest.cjs +6 -5
- package/dist/testing/vitest.cjs.map +1 -1
- package/dist/testing/vitest.d.cts +10 -0
- package/dist/testing/vitest.d.ts +10 -0
- package/dist/testing/vitest.mjs +3 -2
- package/dist/testing/vitest.mjs.map +1 -1
- package/dist/{types-DrpQSv_2.d.cts → types-BYGcp5pZ.d.ts} +4 -3
- package/dist/{types-DDAGLpov.d.ts → types-I1JOZyI_.d.cts} +4 -3
- package/dist/ui/styles.css +740 -0
- package/dist/ui.cjs +1018 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +231 -0
- package/dist/ui.d.ts +231 -0
- package/dist/ui.mjs +1018 -0
- package/dist/ui.mjs.map +1 -0
- package/dist/vite-preview-bootstrap.cjs +66 -0
- package/dist/vite-preview-bootstrap.cjs.map +1 -0
- package/dist/vite-preview-bootstrap.d.cts +15 -0
- package/dist/vite-preview-bootstrap.d.ts +15 -0
- package/dist/vite-preview-bootstrap.mjs +66 -0
- package/dist/vite-preview-bootstrap.mjs.map +1 -0
- package/dist/vite-preview.cjs +876 -0
- package/dist/vite-preview.cjs.map +1 -0
- package/dist/vite-preview.d.cts +9 -0
- package/dist/vite-preview.d.ts +9 -0
- package/dist/vite-preview.mjs +876 -0
- package/dist/vite-preview.mjs.map +1 -0
- package/dist/vite.cjs +316 -59
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +51 -8
- package/dist/vite.d.ts +51 -8
- package/dist/vite.mjs +308 -53
- package/dist/vite.mjs.map +1 -1
- package/dist/work-events-DB3WR468.d.ts +547 -0
- package/dist/work-events-Dxe7Djdy.d.cts +547 -0
- package/package.json +19 -2
- package/schema/extension-v2.json +432 -121
- package/dist/chunk-2DBSVKAU.cjs.map +0 -1
- package/dist/chunk-3YWVYA42.cjs.map +0 -1
- package/dist/chunk-4C5Z3CIK.mjs.map +0 -1
- package/dist/chunk-5AAPD5WV.cjs.map +0 -1
- package/dist/chunk-7KIKOH5H.cjs.map +0 -1
- package/dist/chunk-BDCX6LRQ.mjs.map +0 -1
- package/dist/chunk-CSTYBKZC.mjs.map +0 -1
- package/dist/chunk-IGK2AP6J.mjs.map +0 -1
- package/dist/chunk-MNDVYPEP.mjs.map +0 -1
- package/dist/chunk-OPPYEA7F.cjs.map +0 -1
- package/dist/chunk-PYJMJF44.cjs.map +0 -1
- package/dist/chunk-RMUWRESK.cjs.map +0 -1
- package/dist/chunk-ZT35X7KL.mjs.map +0 -1
- package/dist/handler-CZIlZ5vu.d.ts +0 -227
- package/dist/handler-kU9Jf36K.d.cts +0 -227
- package/dist/styles.css +0 -367
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export { u as useTap } from './context-
|
|
2
|
-
export { T as TAP_ERROR_CODES, a as TapError, b as TapErrorCode, c as TapServerError } from './errors-
|
|
3
|
-
import { T as Tap, a as TapFilters, b as TapSize, c as TapHostEvents, d as TapQuery, e as TapResult, f as TapProcedureName, g as TapProcedureInput, h as TapProcedureOutput, i as TapSingletonName, j as TapSingletonDocument } from './types-
|
|
4
|
-
export { A as ActionId, k as ActionInput, P as PromptId, l as PromptInput, m as TapActionMap, n as TapActionRun, o as TapApp, p as TapChangeKey, q as TapChatCard, r as TapCollection, s as TapComparisonSide, t as TapConfirmOptions, u as TapCrashIssue, v as TapCrashesResult, w as TapCredentialValues, x as TapCredentials, y as TapDateRange, z as TapFormatters, B as TapHostEventMap, C as TapInternals, D as TapKeyword, E as TapKeywordsResult, F as TapListing, G as TapListingResult, H as TapPageProps, I as TapPath, J as TapPlatform, K as TapPlatformFilter, L as TapProcedureMap, M as TapProcedures, N as TapProjectResult, O as TapPromptMap, Q as TapRelease, R as TapRevenueResult, S as TapReview, U as TapReviewsResult, V as TapRowActionProps, W as TapSeriesMetric, X as TapSeriesPoint, Y as TapSeriesResult, Z as TapSettings, _ as TapSettingsProps, $ as TapSettingsValues, a0 as TapSingleton, a1 as TapStorage, a2 as TapStorageMap, a3 as TapTabProps, a4 as TapTableColumn, a5 as TapTelemetryEvents, a6 as TapToastTone, a7 as TapToolMap, a8 as TapWebhookMap, a9 as TapWidgetProps, aa as TelemetryEvent, ab as ToolId, ac as ToolInput, ad as WebhookName, ae as WebhookPayload } from './types-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import { J as JsonSchema } from './constants-DJ_KgZpN.js';
|
|
8
|
-
export { D as DataQueryKind, H as HostEventName, a as HostPage, S as ScopeKey, b as SlotId } from './constants-DJ_KgZpN.js';
|
|
1
|
+
export { u as useTap } from './context-Cr93B05o.js';
|
|
2
|
+
export { T as TAP_ERROR_CODES, a as TapError, b as TapErrorCode, c as TapServerError } from './errors-Cqh0wrqt.js';
|
|
3
|
+
import { T as Tap, a as TapFilters, b as TapSize, c as TapHostEvents, d as TapQuery, e as TapResult, f as TapProcedureName, g as TapProcedureInput, h as TapProcedureOutput, i as TapSingletonName, j as TapSingletonDocument } from './types-BYGcp5pZ.js';
|
|
4
|
+
export { A as ActionId, k as ActionInput, P as PromptId, l as PromptInput, m as TapActionMap, n as TapActionRun, o as TapApp, p as TapChangeKey, q as TapChatCard, r as TapCollection, s as TapComparisonSide, t as TapConfirmOptions, u as TapCrashIssue, v as TapCrashesResult, w as TapCredentialValues, x as TapCredentials, y as TapDateRange, z as TapFormatters, B as TapHostEventMap, C as TapInternals, D as TapKeyword, E as TapKeywordsResult, F as TapListing, G as TapListingResult, H as TapPageProps, I as TapPath, J as TapPlatform, K as TapPlatformFilter, L as TapProcedureMap, M as TapProcedures, N as TapProjectResult, O as TapPromptMap, Q as TapRelease, R as TapRevenueResult, S as TapReview, U as TapReviewsResult, V as TapRowActionProps, W as TapSeriesMetric, X as TapSeriesPoint, Y as TapSeriesResult, Z as TapSettings, _ as TapSettingsProps, $ as TapSettingsValues, a0 as TapSingleton, a1 as TapStorage, a2 as TapStorageMap, a3 as TapTabProps, a4 as TapTableColumn, a5 as TapTelemetryEvents, a6 as TapToastTone, a7 as TapToolMap, a8 as TapWebhookMap, a9 as TapWidgetProps, aa as TelemetryEvent, ab as ToolId, ac as ToolInput, ad as WebhookName, ae as WebhookPayload } from './types-BYGcp5pZ.js';
|
|
5
|
+
export { D as DataQueryKind, H as HostEventName, a as HostPage, S as ScopeKey, b as SlotId } from './constants-BAimnqOK.js';
|
|
6
|
+
import 'react';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* Returns the install token, the signed-in teammate, the workspace id, the
|
|
@@ -18,12 +16,13 @@ export { D as DataQueryKind, H as HostEventName, a as HostPage, S as ScopeKey, b
|
|
|
18
16
|
*
|
|
19
17
|
* @example
|
|
20
18
|
* ```tsx
|
|
21
|
-
* import {
|
|
19
|
+
* import { useTapAuth } from '@tappify/extension-sdk';
|
|
20
|
+
* import { EmptyState } from '@tappify/extension-sdk/ui';
|
|
22
21
|
*
|
|
23
22
|
* function RevenuePanel() {
|
|
24
23
|
* const auth = useTapAuth();
|
|
25
24
|
* if (!auth.can('revenue:read')) {
|
|
26
|
-
* return <
|
|
25
|
+
* return <EmptyState title="Revenue is not shared with this extension" />;
|
|
27
26
|
* }
|
|
28
27
|
* return <span>Signed in as {auth.user.id}</span>;
|
|
29
28
|
* }
|
|
@@ -109,17 +108,30 @@ declare function useTapTheme(): Tap['theme'];
|
|
|
109
108
|
*
|
|
110
109
|
* @example
|
|
111
110
|
* ```tsx
|
|
112
|
-
* import {
|
|
111
|
+
* import { useTapSize } from '@tappify/extension-sdk';
|
|
112
|
+
* import {
|
|
113
|
+
* Table,
|
|
114
|
+
* TableBody,
|
|
115
|
+
* TableCell,
|
|
116
|
+
* TableHead,
|
|
117
|
+
* TableHeader,
|
|
118
|
+
* TableRow,
|
|
119
|
+
* } from '@tappify/extension-sdk/ui';
|
|
113
120
|
*
|
|
114
121
|
* function Keywords({ rows }: { rows: { term: string }[] }) {
|
|
115
122
|
* const size = useTapSize();
|
|
116
123
|
* const shown = size === 'slot' ? rows.slice(0, 3) : rows;
|
|
117
124
|
* return (
|
|
118
|
-
* <
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
125
|
+
* <Table>
|
|
126
|
+
* <TableHeader>
|
|
127
|
+
* <TableRow variant="header"><TableHead>Term</TableHead></TableRow>
|
|
128
|
+
* </TableHeader>
|
|
129
|
+
* <TableBody>
|
|
130
|
+
* {shown.map(row => (
|
|
131
|
+
* <TableRow key={row.term}><TableCell>{row.term}</TableCell></TableRow>
|
|
132
|
+
* ))}
|
|
133
|
+
* </TableBody>
|
|
134
|
+
* </Table>
|
|
123
135
|
* );
|
|
124
136
|
* }
|
|
125
137
|
* ```
|
|
@@ -182,14 +194,15 @@ declare function useTapContext(): Record<string, unknown>;
|
|
|
182
194
|
*
|
|
183
195
|
* @example
|
|
184
196
|
* ```tsx
|
|
185
|
-
* import {
|
|
197
|
+
* import { useTapState } from '@tappify/extension-sdk';
|
|
198
|
+
* import { Button } from '@tappify/extension-sdk/ui';
|
|
186
199
|
*
|
|
187
200
|
* function StepPicker() {
|
|
188
201
|
* const [step, setStep] = useTapState<string>('selectedStep');
|
|
189
202
|
* return (
|
|
190
|
-
* <
|
|
203
|
+
* <Button onClick={() => setStep('install')}>
|
|
191
204
|
* {step ?? 'Pick a step'}
|
|
192
|
-
* </
|
|
205
|
+
* </Button>
|
|
193
206
|
* );
|
|
194
207
|
* }
|
|
195
208
|
* ```
|
|
@@ -325,7 +338,8 @@ declare function procedurePrefix(installId: string, name: string): unknown[];
|
|
|
325
338
|
*
|
|
326
339
|
* @example
|
|
327
340
|
* ```tsx
|
|
328
|
-
* import {
|
|
341
|
+
* import { useTapFilters, useTapQuery } from '@tappify/extension-sdk';
|
|
342
|
+
* import { Metric } from '@tappify/extension-sdk/ui';
|
|
329
343
|
*
|
|
330
344
|
* function Downloads() {
|
|
331
345
|
* const filters = useTapFilters();
|
|
@@ -335,25 +349,41 @@ declare function procedurePrefix(installId: string, name: string): unknown[];
|
|
|
335
349
|
* );
|
|
336
350
|
* const points = downloads.data?.points ?? [];
|
|
337
351
|
* const total = points.reduce((sum, point) => sum + point.value, 0);
|
|
338
|
-
* return <
|
|
352
|
+
* return <Metric label="Downloads" value={total} />;
|
|
339
353
|
* }
|
|
340
354
|
* ```
|
|
341
355
|
*
|
|
342
356
|
* @example
|
|
343
357
|
* ```tsx
|
|
344
|
-
* import {
|
|
358
|
+
* import { useTapQuery } from '@tappify/extension-sdk';
|
|
359
|
+
* import {
|
|
360
|
+
* Table,
|
|
361
|
+
* TableBody,
|
|
362
|
+
* TableCell,
|
|
363
|
+
* TableHead,
|
|
364
|
+
* TableHeader,
|
|
365
|
+
* TableRow,
|
|
366
|
+
* } from '@tappify/extension-sdk/ui';
|
|
345
367
|
*
|
|
346
368
|
* function Keywords() {
|
|
347
369
|
* const keywords = useTapQuery({ kind: 'keywords' });
|
|
348
370
|
* return (
|
|
349
|
-
* <
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
371
|
+
* <Table>
|
|
372
|
+
* <TableHeader>
|
|
373
|
+
* <TableRow variant="header">
|
|
374
|
+
* <TableHead>Term</TableHead>
|
|
375
|
+
* <TableHead>Rank</TableHead>
|
|
376
|
+
* </TableRow>
|
|
377
|
+
* </TableHeader>
|
|
378
|
+
* <TableBody>
|
|
379
|
+
* {(keywords.data?.keywords ?? []).map(keyword => (
|
|
380
|
+
* <TableRow key={keyword.id}>
|
|
381
|
+
* <TableCell>{keyword.term}</TableCell>
|
|
382
|
+
* <TableCell>{keyword.position}</TableCell>
|
|
383
|
+
* </TableRow>
|
|
384
|
+
* ))}
|
|
385
|
+
* </TableBody>
|
|
386
|
+
* </Table>
|
|
357
387
|
* );
|
|
358
388
|
* }
|
|
359
389
|
* ```
|
|
@@ -374,7 +404,8 @@ declare function useTapQuery<Q extends TapQuery>(query: Q, options?: TapQueryOpt
|
|
|
374
404
|
*
|
|
375
405
|
* @example
|
|
376
406
|
* ```tsx
|
|
377
|
-
* import {
|
|
407
|
+
* import { useTapFilters, useTapServer } from '@tappify/extension-sdk';
|
|
408
|
+
* import { Button, ErrorState } from '@tappify/extension-sdk/ui';
|
|
378
409
|
*
|
|
379
410
|
* function Summary() {
|
|
380
411
|
* const filters = useTapFilters();
|
|
@@ -384,9 +415,9 @@ declare function useTapQuery<Q extends TapQuery>(query: Q, options?: TapQueryOpt
|
|
|
384
415
|
* { invalidateOn: ['settings.changed'] },
|
|
385
416
|
* );
|
|
386
417
|
* if (summary.error !== null) {
|
|
387
|
-
* return <
|
|
418
|
+
* return <ErrorState title="The summary did not load" description={summary.error.message} />;
|
|
388
419
|
* }
|
|
389
|
-
* return <
|
|
420
|
+
* return <Button loading={summary.isLoading} onClick={summary.refetch}>Refresh</Button>;
|
|
390
421
|
* }
|
|
391
422
|
* ```
|
|
392
423
|
*/
|
|
@@ -406,611 +437,22 @@ declare function useTapServer<N extends TapProcedureName>(name: N, input: TapPro
|
|
|
406
437
|
*
|
|
407
438
|
* @example
|
|
408
439
|
* ```tsx
|
|
409
|
-
* import {
|
|
440
|
+
* import { useTapStorage } from '@tappify/extension-sdk';
|
|
441
|
+
* import { Button } from '@tappify/extension-sdk/ui';
|
|
410
442
|
*
|
|
411
443
|
* function CompactToggle() {
|
|
412
444
|
* const preferences = useTapStorage('preferences');
|
|
413
445
|
* return (
|
|
414
|
-
* <
|
|
446
|
+
* <Button
|
|
415
447
|
* loading={preferences.isLoading}
|
|
416
448
|
* onClick={() => void preferences.patch({ compact: true })}
|
|
417
449
|
* >
|
|
418
450
|
* Compact
|
|
419
|
-
* </
|
|
451
|
+
* </Button>
|
|
420
452
|
* );
|
|
421
453
|
* }
|
|
422
454
|
* ```
|
|
423
455
|
*/
|
|
424
456
|
declare function useTapStorage<C extends TapSingletonName>(collection: C): TapStorageResult<TapSingletonDocument<C>>;
|
|
425
457
|
|
|
426
|
-
|
|
427
|
-
* Anything `cn` accepts: a class string, a number, an array of these, a map of
|
|
428
|
-
* class name to condition, or a value to drop.
|
|
429
|
-
*/
|
|
430
|
-
type ClassValue = ClassValue$1;
|
|
431
|
-
/**
|
|
432
|
-
* Joins class names, dropping the ones that are `false`, `null` or `undefined`.
|
|
433
|
-
*
|
|
434
|
-
* @remarks
|
|
435
|
-
* This is `clsx`, re-exported so a vendor surface needs no class-name dependency
|
|
436
|
-
* of its own. It does not merge conflicting utility classes.
|
|
437
|
-
*
|
|
438
|
-
* @example
|
|
439
|
-
* ```tsx
|
|
440
|
-
* import { cn } from '@tappify/extension-sdk';
|
|
441
|
-
*
|
|
442
|
-
* function Row({ compact }: { compact: boolean }) {
|
|
443
|
-
* return <div className={cn('starter-row', compact && 'starter-row--compact')} />;
|
|
444
|
-
* }
|
|
445
|
-
* ```
|
|
446
|
-
*/
|
|
447
|
-
declare function cn(...values: ClassValue[]): string;
|
|
448
|
-
|
|
449
|
-
/** The props `TapButton` takes, on top of the native button attributes. */
|
|
450
|
-
interface TapButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
451
|
-
/** Defaults to `primary`. */
|
|
452
|
-
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
|
|
453
|
-
/** Defaults to `md`. */
|
|
454
|
-
size?: 'sm' | 'md';
|
|
455
|
-
/** Disables the button and marks it `aria-busy`. */
|
|
456
|
-
loading?: boolean;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Renders a button in the host's styling, in one of four variants.
|
|
460
|
-
*
|
|
461
|
-
* @remarks
|
|
462
|
-
* Draws on `--tap-accent` and `--tap-accent-fg` for `primary`, `--bg-raised`,
|
|
463
|
-
* `--fg-default` and `--divider` for `secondary`, `--fg-muted` for `ghost` and
|
|
464
|
-
* `--tap-danger` for `danger`, and takes its radius from `--tap-radius`. The
|
|
465
|
-
* element defaults to `type="button"`, so a button inside a `TapForm` does not
|
|
466
|
-
* submit it unless you pass `type="submit"`. `loading` disables the button, so a
|
|
467
|
-
* click cannot land twice.
|
|
468
|
-
*
|
|
469
|
-
* @example
|
|
470
|
-
* ```tsx
|
|
471
|
-
* import { TapButton } from '@tappify/extension-sdk';
|
|
472
|
-
*
|
|
473
|
-
* function ExportRow({ busy, onExport }: { busy: boolean; onExport: () => void }) {
|
|
474
|
-
* return (
|
|
475
|
-
* <TapButton variant="secondary" size="sm" loading={busy} onClick={onExport}>
|
|
476
|
-
* Export
|
|
477
|
-
* </TapButton>
|
|
478
|
-
* );
|
|
479
|
-
* }
|
|
480
|
-
* ```
|
|
481
|
-
*/
|
|
482
|
-
declare function TapButton({ variant, size, loading, disabled, className, children, ...rest }: TapButtonProps): ReactElement;
|
|
483
|
-
/** The props `TapCard` takes. */
|
|
484
|
-
interface TapCardProps {
|
|
485
|
-
/** Rendered in the header, which appears only with a `title` or an `action`. */
|
|
486
|
-
title?: ReactNode;
|
|
487
|
-
/** Sits at the end of the header, for a button or a link. */
|
|
488
|
-
action?: ReactNode;
|
|
489
|
-
/** Rendered under a divider below the body. */
|
|
490
|
-
footer?: ReactNode;
|
|
491
|
-
className?: string;
|
|
492
|
-
children: ReactNode;
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Renders the bordered surface a widget, tab or page section sits in.
|
|
496
|
-
*
|
|
497
|
-
* @remarks
|
|
498
|
-
* Renders as a `section` on `--bg-raised` with a `--divider` border and the
|
|
499
|
-
* `--tap-radius` corner, and the footer in `--fg-muted`. The header appears only
|
|
500
|
-
* when `title` or `action` is given, and the body carries its own padding, so
|
|
501
|
-
* nest content directly rather than wrapping it in another padded box. A widget
|
|
502
|
-
* belongs in one card.
|
|
503
|
-
*
|
|
504
|
-
* @example
|
|
505
|
-
* ```tsx
|
|
506
|
-
* import { TapButton, TapCard, TapStat } from '@tappify/extension-sdk';
|
|
507
|
-
*
|
|
508
|
-
* function InstallSummary() {
|
|
509
|
-
* return (
|
|
510
|
-
* <TapCard
|
|
511
|
-
* title="Install summary"
|
|
512
|
-
* action={<TapButton variant="ghost" size="sm">Compact</TapButton>}
|
|
513
|
-
* footer={<span>Last seven days</span>}
|
|
514
|
-
* >
|
|
515
|
-
* <TapStat label="Installs" value="1,260" delta={0.087} />
|
|
516
|
-
* </TapCard>
|
|
517
|
-
* );
|
|
518
|
-
* }
|
|
519
|
-
* ```
|
|
520
|
-
*/
|
|
521
|
-
declare function TapCard({ title, action, footer, className, children, }: TapCardProps): ReactElement;
|
|
522
|
-
/** The props `TapPageHeader` takes. */
|
|
523
|
-
interface TapPageHeaderProps {
|
|
524
|
-
title: ReactNode;
|
|
525
|
-
description?: ReactNode;
|
|
526
|
-
/** Laid out in a row at the end of the header. */
|
|
527
|
-
actions?: ReactNode;
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Renders the title, description and actions at the top of a tab, a page or a
|
|
531
|
-
* settings panel.
|
|
532
|
-
*
|
|
533
|
-
* @remarks
|
|
534
|
-
* The title renders as an `h2`, because the host owns the page's `h1`. The
|
|
535
|
-
* description follows `--fg-muted` and the rule under the header follows
|
|
536
|
-
* `--divider`. Under 360 pixels of mount width the actions stack under the title.
|
|
537
|
-
* A widget in a slot uses `TapCard`'s own header instead.
|
|
538
|
-
*
|
|
539
|
-
* @example
|
|
540
|
-
* ```tsx
|
|
541
|
-
* import { TapButton, TapPageHeader } from '@tappify/extension-sdk';
|
|
542
|
-
*
|
|
543
|
-
* function FunnelsHeader() {
|
|
544
|
-
* return (
|
|
545
|
-
* <TapPageHeader
|
|
546
|
-
* title="Funnels"
|
|
547
|
-
* description="Every step from impression to install."
|
|
548
|
-
* actions={<TapButton size="sm">New funnel</TapButton>}
|
|
549
|
-
* />
|
|
550
|
-
* );
|
|
551
|
-
* }
|
|
552
|
-
* ```
|
|
553
|
-
*/
|
|
554
|
-
declare function TapPageHeader({ title, description, actions, }: TapPageHeaderProps): ReactElement;
|
|
555
|
-
/** The props `TapSkeleton` takes. */
|
|
556
|
-
interface TapSkeletonProps {
|
|
557
|
-
/** How many bars to draw. Defaults to one. */
|
|
558
|
-
lines?: number;
|
|
559
|
-
/** A CSS width for every bar. Left out, each bar is 12% shorter than the last. */
|
|
560
|
-
width?: string;
|
|
561
|
-
className?: string;
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Renders the placeholder bars that stand in for content while a read is in
|
|
565
|
-
* flight.
|
|
566
|
-
*
|
|
567
|
-
* @remarks
|
|
568
|
-
* The bars are `aria-hidden`, so a screen reader is not told about them, and they
|
|
569
|
-
* take their tone from `--bg-subtle`. The pulse stops under
|
|
570
|
-
* `prefers-reduced-motion`. Render this while `isLoading` is `true`, in place of
|
|
571
|
-
* the content rather than beside it.
|
|
572
|
-
*
|
|
573
|
-
* @example
|
|
574
|
-
* ```tsx
|
|
575
|
-
* import { TapSkeleton, TapStat, useTapQuery } from '@tappify/extension-sdk';
|
|
576
|
-
*
|
|
577
|
-
* function Keywords() {
|
|
578
|
-
* const keywords = useTapQuery({ kind: 'keywords' });
|
|
579
|
-
* if (keywords.isLoading) return <TapSkeleton lines={3} />;
|
|
580
|
-
* return <TapStat label="Tracked" value={keywords.data?.keywords.length ?? 0} />;
|
|
581
|
-
* }
|
|
582
|
-
* ```
|
|
583
|
-
*/
|
|
584
|
-
declare function TapSkeleton({ lines, width, className, }: TapSkeletonProps): ReactElement;
|
|
585
|
-
/** The props `TapEmptyState` takes. */
|
|
586
|
-
interface TapEmptyStateProps {
|
|
587
|
-
title: string;
|
|
588
|
-
description?: string;
|
|
589
|
-
/** Rendered under the text, for the one thing the owner can do next. */
|
|
590
|
-
action?: ReactNode;
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Renders the centred message that stands in for content when there is nothing to
|
|
594
|
-
* show.
|
|
595
|
-
*
|
|
596
|
-
* @remarks
|
|
597
|
-
* Centres its text in `--fg-muted` with the title in `--fg-default`. Use it for an
|
|
598
|
-
* empty result or a surface the install has not been granted; a failure belongs in
|
|
599
|
-
* `TapErrorState`, which carries a retry and announces itself as an alert.
|
|
600
|
-
*
|
|
601
|
-
* @example
|
|
602
|
-
* ```tsx
|
|
603
|
-
* import { TapEmptyState, useTapQuery } from '@tappify/extension-sdk';
|
|
604
|
-
*
|
|
605
|
-
* function Reviews() {
|
|
606
|
-
* const reviews = useTapQuery({
|
|
607
|
-
* kind: 'reviews',
|
|
608
|
-
* range: { from: '2026-09-01', to: '2026-09-08' },
|
|
609
|
-
* });
|
|
610
|
-
* if (reviews.data?.total === 0) {
|
|
611
|
-
* return <TapEmptyState title="No reviews in this range" />;
|
|
612
|
-
* }
|
|
613
|
-
* return <span>{reviews.data?.total ?? 0} reviews</span>;
|
|
614
|
-
* }
|
|
615
|
-
* ```
|
|
616
|
-
*/
|
|
617
|
-
declare function TapEmptyState({ title, description, action, }: TapEmptyStateProps): ReactElement;
|
|
618
|
-
/** The props `TapErrorState` takes. */
|
|
619
|
-
interface TapErrorStateProps {
|
|
620
|
-
title: string;
|
|
621
|
-
/** The failure's own message, which is what a caller shows the owner. */
|
|
622
|
-
description?: string;
|
|
623
|
-
/** An error code, rendered small in `--font-mono` under the description. */
|
|
624
|
-
code?: string;
|
|
625
|
-
/** Renders a Retry button, and is called by it. */
|
|
626
|
-
onRetry?: () => void;
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* Renders the centred failure message a surface shows in place of its content.
|
|
630
|
-
*
|
|
631
|
-
* @remarks
|
|
632
|
-
* Carries `role="alert"`, so the host announces it, and draws the title in
|
|
633
|
-
* `--tap-danger`. The Retry button appears only with `onRetry`; wire it to the
|
|
634
|
-
* `refetch` of every read the surface needs. Pass the failure's own `message` as
|
|
635
|
-
* the description — an error from `useTapServer` carries the text your server sent
|
|
636
|
-
* the owner — and the `code` of a `TapError` or `TapServerError` as `code`.
|
|
637
|
-
*
|
|
638
|
-
* @example
|
|
639
|
-
* ```tsx
|
|
640
|
-
* import { TapErrorState, useTapQuery } from '@tappify/extension-sdk';
|
|
641
|
-
*
|
|
642
|
-
* function Crashes() {
|
|
643
|
-
* const crashes = useTapQuery({
|
|
644
|
-
* kind: 'crashes',
|
|
645
|
-
* range: { from: '2026-09-01', to: '2026-09-08' },
|
|
646
|
-
* });
|
|
647
|
-
* if (crashes.error !== null) {
|
|
648
|
-
* return (
|
|
649
|
-
* <TapErrorState
|
|
650
|
-
* title="Crashes did not load"
|
|
651
|
-
* description={crashes.error.message}
|
|
652
|
-
* onRetry={crashes.refetch}
|
|
653
|
-
* />
|
|
654
|
-
* );
|
|
655
|
-
* }
|
|
656
|
-
* return <span>{crashes.data?.issues.length ?? 0} issues</span>;
|
|
657
|
-
* }
|
|
658
|
-
* ```
|
|
659
|
-
*/
|
|
660
|
-
declare function TapErrorState({ title, description, code, onRetry, }: TapErrorStateProps): ReactElement;
|
|
661
|
-
/** The props `TapStat` takes. */
|
|
662
|
-
interface TapStatProps {
|
|
663
|
-
label: string;
|
|
664
|
-
value: ReactNode;
|
|
665
|
-
/** A share of the previous period, so `0.087` renders as `+9%`. */
|
|
666
|
-
delta?: number;
|
|
667
|
-
/** A line of context under the number. */
|
|
668
|
-
hint?: string;
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Renders one labelled number, with an optional change against the previous
|
|
672
|
-
* period.
|
|
673
|
-
*
|
|
674
|
-
* @remarks
|
|
675
|
-
* The label is uppercased in `--fg-muted` and the number uses tabular figures, so
|
|
676
|
-
* a row of stats stays aligned as values change. `delta` is a ratio, not a
|
|
677
|
-
* percentage: it renders as whole percent with a plus in front when it is above
|
|
678
|
-
* zero, and is coloured `--tap-danger` below zero and `--tap-accent` at or above
|
|
679
|
-
* it. Format `value`
|
|
680
|
-
* yourself with `tap.format.number` or `tap.format.currency`, so it follows the
|
|
681
|
-
* owner's locale.
|
|
682
|
-
*
|
|
683
|
-
* @example
|
|
684
|
-
* ```tsx
|
|
685
|
-
* import { TapStat, useTap } from '@tappify/extension-sdk';
|
|
686
|
-
*
|
|
687
|
-
* function Installs({ installs, delta }: { installs: number; delta: number }) {
|
|
688
|
-
* const tap = useTap();
|
|
689
|
-
* return (
|
|
690
|
-
* <TapStat
|
|
691
|
-
* label="Installs"
|
|
692
|
-
* value={tap.format.number(installs)}
|
|
693
|
-
* delta={delta}
|
|
694
|
-
* hint="Against the previous seven days"
|
|
695
|
-
* />
|
|
696
|
-
* );
|
|
697
|
-
* }
|
|
698
|
-
* ```
|
|
699
|
-
*/
|
|
700
|
-
declare function TapStat({ label, value, delta, hint, }: TapStatProps): ReactElement;
|
|
701
|
-
/**
|
|
702
|
-
* The `TapInput` props for a single-line control, on top of the native input
|
|
703
|
-
* attributes minus `size`.
|
|
704
|
-
*/
|
|
705
|
-
interface TapSingleLineInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
706
|
-
/** Rendered as a `label` bound to the control by id. */
|
|
707
|
-
label?: string;
|
|
708
|
-
/** Marks the control invalid and renders the message under it. */
|
|
709
|
-
error?: string;
|
|
710
|
-
/** A line of guidance under the control, referenced by `aria-describedby`. */
|
|
711
|
-
hint?: string;
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* The `TapInput` props for a multiline control, on top of the native textarea
|
|
715
|
-
* attributes.
|
|
716
|
-
*/
|
|
717
|
-
interface TapTextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
718
|
-
/** Rendered as a `label` bound to the control by id. */
|
|
719
|
-
label?: string;
|
|
720
|
-
/** Marks the control invalid and renders the message under it. */
|
|
721
|
-
error?: string;
|
|
722
|
-
/** A line of guidance under the control, referenced by `aria-describedby`. */
|
|
723
|
-
hint?: string;
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* The props `TapInput` takes: the single-line set, or the textarea set behind
|
|
727
|
-
* `multiline`.
|
|
728
|
-
*/
|
|
729
|
-
type TapInputProps = ({
|
|
730
|
-
multiline?: false;
|
|
731
|
-
} & TapSingleLineInputProps) | ({
|
|
732
|
-
multiline: true;
|
|
733
|
-
} & TapTextareaProps);
|
|
734
|
-
/**
|
|
735
|
-
* Renders a labelled text control with its hint and error, as an input or, with
|
|
736
|
-
* `multiline`, a textarea.
|
|
737
|
-
*
|
|
738
|
-
* @remarks
|
|
739
|
-
* Generates an id when none is passed and binds the label, the hint and the error
|
|
740
|
-
* to the control, so the field is announced whole; an `error` also sets
|
|
741
|
-
* `aria-invalid` and the `--tap-danger` border. The textarea starts at four rows
|
|
742
|
-
* and is resizable in height. Pass `type="checkbox"` with
|
|
743
|
-
* `className="tap-control--checkbox"` for a boolean, which is what `TapForm`
|
|
744
|
-
* does.
|
|
745
|
-
*
|
|
746
|
-
* @example
|
|
747
|
-
* ```tsx
|
|
748
|
-
* import { TapInput } from '@tappify/extension-sdk';
|
|
749
|
-
*
|
|
750
|
-
* function TermField({ term, onTerm }: { term: string; onTerm: (next: string) => void }) {
|
|
751
|
-
* return (
|
|
752
|
-
* <TapInput
|
|
753
|
-
* label="Keyword"
|
|
754
|
-
* hint="One term per row in the report."
|
|
755
|
-
* value={term}
|
|
756
|
-
* onChange={event => onTerm(event.target.value)}
|
|
757
|
-
* />
|
|
758
|
-
* );
|
|
759
|
-
* }
|
|
760
|
-
* ```
|
|
761
|
-
*/
|
|
762
|
-
declare function TapInput(props: TapInputProps): ReactElement;
|
|
763
|
-
/** The props `TapSelect` takes, on top of the native select attributes. */
|
|
764
|
-
interface TapSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
765
|
-
/** Rendered as a `label` bound to the control by id. */
|
|
766
|
-
label?: string;
|
|
767
|
-
/** Marks the control invalid and renders the message under it. */
|
|
768
|
-
error?: string;
|
|
769
|
-
/** A line of guidance under the control, referenced by `aria-describedby`. */
|
|
770
|
-
hint?: string;
|
|
771
|
-
/** Rendered in order; the component adds no empty option of its own. */
|
|
772
|
-
options: {
|
|
773
|
-
value: string;
|
|
774
|
-
label: string;
|
|
775
|
-
}[];
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Renders a labelled select over a list of options, with its hint and error.
|
|
779
|
-
*
|
|
780
|
-
* @remarks
|
|
781
|
-
* Carries the same label, hint, error and `aria-describedby` wiring as
|
|
782
|
-
* `TapInput`. Add your own empty option when the field starts unset — `TapForm`
|
|
783
|
-
* puts a `Choose one` option in front of a schema's enum values.
|
|
784
|
-
*
|
|
785
|
-
* @example
|
|
786
|
-
* ```tsx
|
|
787
|
-
* import { TapSelect } from '@tappify/extension-sdk';
|
|
788
|
-
*
|
|
789
|
-
* function PlatformPicker({ value, onPick }: { value: string; onPick: (next: string) => void }) {
|
|
790
|
-
* return (
|
|
791
|
-
* <TapSelect
|
|
792
|
-
* label="Platform"
|
|
793
|
-
* value={value}
|
|
794
|
-
* options={[
|
|
795
|
-
* { value: 'ios', label: 'iOS' },
|
|
796
|
-
* { value: 'android', label: 'Android' },
|
|
797
|
-
* ]}
|
|
798
|
-
* onChange={event => onPick(event.target.value)}
|
|
799
|
-
* />
|
|
800
|
-
* );
|
|
801
|
-
* }
|
|
802
|
-
* ```
|
|
803
|
-
*/
|
|
804
|
-
declare function TapSelect({ label, error, hint, options, className, id, ...rest }: TapSelectProps): ReactElement;
|
|
805
|
-
/** One column of a `TapTable`. */
|
|
806
|
-
interface TapTableColumnDef<T> {
|
|
807
|
-
/** The key read from each row when there is no `render`. */
|
|
808
|
-
key: string;
|
|
809
|
-
label: string;
|
|
810
|
-
/** `end` right-aligns the column and gives it tabular figures. */
|
|
811
|
-
align?: 'start' | 'end';
|
|
812
|
-
/** Draws the cell. Without it the cell shows the row's own string or number. */
|
|
813
|
-
render?(row: T): ReactNode;
|
|
814
|
-
}
|
|
815
|
-
/** The props `TapTable` takes. */
|
|
816
|
-
interface TapTableProps<T> {
|
|
817
|
-
columns: TapTableColumnDef<T>[];
|
|
818
|
-
rows: T[];
|
|
819
|
-
/** Returns a stable key per row, which React uses for reconciliation. */
|
|
820
|
-
rowKey: (row: T) => string;
|
|
821
|
-
/** Rendered in place of the whole table when `rows` is empty. */
|
|
822
|
-
empty?: ReactNode;
|
|
823
|
-
/** Makes each row clickable and focusable, and is called on click or Enter. */
|
|
824
|
-
onRowClick?: (row: T) => void;
|
|
825
|
-
}
|
|
826
|
-
/**
|
|
827
|
-
* Renders rows under column headers, scrolling sideways inside the mount rather
|
|
828
|
-
* than widening it.
|
|
829
|
-
*
|
|
830
|
-
* @remarks
|
|
831
|
-
* A cell with no `render` shows the row's own value under `key` when that value is
|
|
832
|
-
* a string, number, boolean or bigint, and nothing otherwise, so an object or an
|
|
833
|
-
* array needs a `render`. With `empty` and no rows, the table is replaced whole by
|
|
834
|
-
* that node. With `onRowClick` each row becomes focusable and answers Enter and
|
|
835
|
-
* Space as well as a click, and hovers on `--bg-subtle`; the headers and rules
|
|
836
|
-
* follow `--fg-muted` and `--divider`.
|
|
837
|
-
*
|
|
838
|
-
* @example
|
|
839
|
-
* ```tsx
|
|
840
|
-
* import { TapEmptyState, TapTable, useTapQuery } from '@tappify/extension-sdk';
|
|
841
|
-
*
|
|
842
|
-
* function Keywords() {
|
|
843
|
-
* const keywords = useTapQuery({ kind: 'keywords' });
|
|
844
|
-
* return (
|
|
845
|
-
* <TapTable
|
|
846
|
-
* columns={[
|
|
847
|
-
* { key: 'term', label: 'Term' },
|
|
848
|
-
* { key: 'position', label: 'Rank', align: 'end' },
|
|
849
|
-
* ]}
|
|
850
|
-
* rows={keywords.data?.keywords ?? []}
|
|
851
|
-
* rowKey={keyword => keyword.id}
|
|
852
|
-
* empty={<TapEmptyState title="No keywords tracked yet" />}
|
|
853
|
-
* />
|
|
854
|
-
* );
|
|
855
|
-
* }
|
|
856
|
-
* ```
|
|
857
|
-
*/
|
|
858
|
-
declare function TapTable<T>({ columns, rows, rowKey, empty, onRowClick, }: TapTableProps<T>): ReactElement;
|
|
859
|
-
|
|
860
|
-
/** The props `TapDialog` takes. */
|
|
861
|
-
interface TapDialogProps {
|
|
862
|
-
/** The dialog renders nothing while this is `false`. */
|
|
863
|
-
open: boolean;
|
|
864
|
-
/** Rendered in the header, and used as the dialog's accessible name. */
|
|
865
|
-
title: string;
|
|
866
|
-
/** Called by Escape, by a click on the scrim, and by the host's close event. */
|
|
867
|
-
onClose: () => void;
|
|
868
|
-
/** Rendered under a divider at the end, for the dialog's buttons. */
|
|
869
|
-
footer?: ReactNode;
|
|
870
|
-
children: ReactNode;
|
|
871
|
-
/** The id of your own heading, when the accessible name is not the title. */
|
|
872
|
-
labelledBy?: string;
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Renders a modal dialog into the mount's own portal node, so it covers the
|
|
876
|
-
* surface without leaving the extension.
|
|
877
|
-
*
|
|
878
|
-
* @remarks
|
|
879
|
-
* Rendered outside a Tappify mount it throws `TAP_OUTSIDE_HOST`, from the `useTap`
|
|
880
|
-
* above it. Inside a mount whose shadow root has no portal node yet, opening it
|
|
881
|
-
* throws `TapError` with code `TAP_NO_PORTAL`; `renderWithTap` provides that node
|
|
882
|
-
* in tests. While open it traps Tab
|
|
883
|
-
* inside itself, moves focus to the first focusable element and returns focus
|
|
884
|
-
* where it was on close, and answers Escape and a click on the scrim with
|
|
885
|
-
* `onClose`. The scrim follows `--tap-scrim` and the panel `--bg-raised`,
|
|
886
|
-
* `--divider` and `--tap-radius`; the panel is at most 520 pixels wide. The
|
|
887
|
-
* component keeps no open state of its own.
|
|
888
|
-
*
|
|
889
|
-
* @example
|
|
890
|
-
* ```tsx
|
|
891
|
-
* import { TapButton, TapDialog } from '@tappify/extension-sdk';
|
|
892
|
-
* import { useState } from 'react';
|
|
893
|
-
*
|
|
894
|
-
* function ExportDialog() {
|
|
895
|
-
* const [open, setOpen] = useState(false);
|
|
896
|
-
* return (
|
|
897
|
-
* <>
|
|
898
|
-
* <TapButton onClick={() => setOpen(true)}>Export</TapButton>
|
|
899
|
-
* <TapDialog
|
|
900
|
-
* open={open}
|
|
901
|
-
* title="Export keywords"
|
|
902
|
-
* onClose={() => setOpen(false)}
|
|
903
|
-
* footer={<TapButton onClick={() => setOpen(false)}>Done</TapButton>}
|
|
904
|
-
* >
|
|
905
|
-
* <span>The report covers the range in the filter bar.</span>
|
|
906
|
-
* </TapDialog>
|
|
907
|
-
* </>
|
|
908
|
-
* );
|
|
909
|
-
* }
|
|
910
|
-
* ```
|
|
911
|
-
*/
|
|
912
|
-
declare function TapDialog({ open, title, onClose, footer, children, labelledBy, }: TapDialogProps): ReactElement | null;
|
|
913
|
-
|
|
914
|
-
/** The control a schema property maps to. */
|
|
915
|
-
type TapFormFieldKind = 'text' | 'textarea' | 'date' | 'number' | 'boolean' | 'enum';
|
|
916
|
-
/** One control `TapForm` renders, as `formFields` reads it off a schema. */
|
|
917
|
-
interface TapFormField {
|
|
918
|
-
/** The property name in the schema, and the key in the submitted value. */
|
|
919
|
-
name: string;
|
|
920
|
-
/** The property's `title`, or the name spaced and capitalised. */
|
|
921
|
-
label: string;
|
|
922
|
-
kind: TapFormFieldKind;
|
|
923
|
-
/** Whether the schema lists the property under `required`. */
|
|
924
|
-
required: boolean;
|
|
925
|
-
/** The property's `description`. */
|
|
926
|
-
hint?: string;
|
|
927
|
-
/** Set for an `enum` property: one option per string value. */
|
|
928
|
-
options?: {
|
|
929
|
-
value: string;
|
|
930
|
-
label: string;
|
|
931
|
-
}[];
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Reads a JSON Schema object and returns the list of controls `TapForm` renders
|
|
935
|
-
* from it.
|
|
936
|
-
*
|
|
937
|
-
* @remarks
|
|
938
|
-
* One field per declared property, in the order the schema lists them; a schema
|
|
939
|
-
* with no `properties` object returns an empty list, and a property that is not an
|
|
940
|
-
* object is skipped. An `enum` becomes a select and keeps only its string values;
|
|
941
|
-
* otherwise the kind comes from `type` — `boolean`, `number` and `integer` — and
|
|
942
|
-
* then from `format`, where `textarea` and `date` are recognised and anything else
|
|
943
|
-
* falls back to a single-line text control. Nested objects and arrays get a text
|
|
944
|
-
* control, so render those yourself. Call it when you want the fields without the
|
|
945
|
-
* form.
|
|
946
|
-
*
|
|
947
|
-
* @example
|
|
948
|
-
* ```ts
|
|
949
|
-
* import { formFields } from '@tappify/extension-sdk';
|
|
950
|
-
*
|
|
951
|
-
* const fields = formFields({
|
|
952
|
-
* type: 'object',
|
|
953
|
-
* properties: { refreshMinutes: { type: 'integer', title: 'Refresh minutes' } },
|
|
954
|
-
* required: ['refreshMinutes'],
|
|
955
|
-
* });
|
|
956
|
-
* const names = fields.map(field => field.name);
|
|
957
|
-
* ```
|
|
958
|
-
*/
|
|
959
|
-
declare function formFields(schema: JsonSchema): TapFormField[];
|
|
960
|
-
/** The props `TapForm` takes. */
|
|
961
|
-
interface TapFormProps<T> {
|
|
962
|
-
/** The object schema to render, one control per declared property. */
|
|
963
|
-
schema: JsonSchema;
|
|
964
|
-
/** The value the fields start at; later changes to it do not reset the form. */
|
|
965
|
-
value?: Partial<T>;
|
|
966
|
-
/** Defaults to `Submit`. */
|
|
967
|
-
submitLabel?: string;
|
|
968
|
-
/** Defaults to `Cancel`. */
|
|
969
|
-
cancelLabel?: string;
|
|
970
|
-
/** Marks the submit button busy and disables it while a submit is in flight. */
|
|
971
|
-
busy?: boolean;
|
|
972
|
-
/** Called with the cleaned value once validation passes. */
|
|
973
|
-
onSubmit: (value: T) => void | Promise<void>;
|
|
974
|
-
/** Renders a cancel button next to submit, and is called by it. */
|
|
975
|
-
onCancel?: () => void;
|
|
976
|
-
}
|
|
977
|
-
/**
|
|
978
|
-
* Renders a JSON Schema as a form in the host's styling, validates it on submit
|
|
979
|
-
* and hands back the value.
|
|
980
|
-
*
|
|
981
|
-
* @remarks
|
|
982
|
-
* Point it at the same schema file the manifest references, so the form and the
|
|
983
|
-
* handler change together. Submit drops the fields left blank, then checks what
|
|
984
|
-
* remains against the whole schema, so a `minimum`, `pattern` or `maxLength`
|
|
985
|
-
* lands under the control it belongs to; a required field left empty is reported
|
|
986
|
-
* without reaching the schema. A schema this SDK's Zod version cannot compile
|
|
987
|
-
* still renders and still enforces required fields. `onSubmit` runs only when
|
|
988
|
-
* nothing failed, and the form holds its own values, so a change to `value` after
|
|
989
|
-
* the first render does not reset it. A form with no `value` has nothing to infer
|
|
990
|
-
* `T` from, so annotate the handler's parameter.
|
|
991
|
-
*
|
|
992
|
-
* @example
|
|
993
|
-
* ```tsx
|
|
994
|
-
* import { TapForm, type TapSettingsProps } from '@tappify/extension-sdk';
|
|
995
|
-
*
|
|
996
|
-
* const schema = {
|
|
997
|
-
* type: 'object',
|
|
998
|
-
* properties: { refreshMinutes: { type: 'integer', title: 'Refresh minutes' } },
|
|
999
|
-
* required: ['refreshMinutes'],
|
|
1000
|
-
* };
|
|
1001
|
-
*
|
|
1002
|
-
* function Settings({ values, onChange }: TapSettingsProps) {
|
|
1003
|
-
* return (
|
|
1004
|
-
* <TapForm
|
|
1005
|
-
* schema={schema}
|
|
1006
|
-
* value={values}
|
|
1007
|
-
* submitLabel="Save"
|
|
1008
|
-
* onSubmit={next => onChange(next)}
|
|
1009
|
-
* />
|
|
1010
|
-
* );
|
|
1011
|
-
* }
|
|
1012
|
-
* ```
|
|
1013
|
-
*/
|
|
1014
|
-
declare function TapForm<T>({ schema, value, submitLabel, cancelLabel, busy, onSubmit, onCancel, }: TapFormProps<T>): ReactElement;
|
|
1015
|
-
|
|
1016
|
-
export { type ClassValue, Tap, TapButton, type TapButtonProps, TapCard, type TapCardProps, TapDialog, type TapDialogProps, TapEmptyState, type TapEmptyStateProps, TapErrorState, type TapErrorStateProps, TapFilters, TapForm, type TapFormField, type TapFormFieldKind, type TapFormProps, TapHostEvents, TapInput, type TapInputProps, TapPageHeader, type TapPageHeaderProps, TapProcedureInput, TapProcedureName, TapProcedureOutput, TapQuery, type TapQueryOptions, type TapQueryResult, TapResult, TapSelect, type TapSelectProps, type TapSingleLineInputProps, TapSingletonDocument, TapSingletonName, TapSize, TapSkeleton, type TapSkeletonProps, TapStat, type TapStatProps, type TapStorageResult, TapTable, type TapTableColumnDef, type TapTableProps, type TapTextareaProps, cn, dataQueryKey, formFields, installPrefix, procedurePrefix, procedureQueryKey, storageQueryKey, useTapAuth, useTapContext, useTapFilters, useTapParams, useTapProject, useTapQuery, useTapServer, useTapSize, useTapState, useTapStorage, useTapTheme };
|
|
458
|
+
export { Tap, TapFilters, TapHostEvents, TapProcedureInput, TapProcedureName, TapProcedureOutput, TapQuery, type TapQueryOptions, type TapQueryResult, TapResult, TapSingletonDocument, TapSingletonName, TapSize, type TapStorageResult, dataQueryKey, installPrefix, procedurePrefix, procedureQueryKey, storageQueryKey, useTapAuth, useTapContext, useTapFilters, useTapParams, useTapProject, useTapQuery, useTapServer, useTapSize, useTapState, useTapStorage, useTapTheme };
|