@tai42/studio-sdk 0.11.0 → 0.13.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.
- package/dist/components/code-block.d.ts.map +1 -1
- package/dist/components/code-block.js +26 -13
- package/dist/components/code-block.js.map +1 -1
- package/dist/components/components.css +20 -0
- package/dist/components/copy-field.d.ts.map +1 -1
- package/dist/components/copy-field.js +8 -73
- package/dist/components/copy-field.js.map +1 -1
- package/dist/components/date-range-picker.d.ts +73 -0
- package/dist/components/date-range-picker.d.ts.map +1 -0
- package/dist/components/date-range-picker.js +146 -0
- package/dist/components/date-range-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +19 -1
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +16 -13
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dirty-guard.d.ts +28 -0
- package/dist/components/dirty-guard.d.ts.map +1 -0
- package/dist/components/dirty-guard.js +93 -0
- package/dist/components/dirty-guard.js.map +1 -0
- package/dist/components/extension-picker.d.ts.map +1 -1
- package/dist/components/extension-picker.js +7 -0
- package/dist/components/extension-picker.js.map +1 -1
- package/dist/components/form-dialog.d.ts +45 -0
- package/dist/components/form-dialog.d.ts.map +1 -0
- package/dist/components/form-dialog.js +71 -0
- package/dist/components/form-dialog.js.map +1 -0
- package/dist/components/json-tree.d.ts +7 -1
- package/dist/components/json-tree.d.ts.map +1 -1
- package/dist/components/json-tree.js +300 -13
- package/dist/components/json-tree.js.map +1 -1
- package/dist/components/markdown.d.ts +60 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +435 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/tabs.d.ts +3 -1
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +2 -2
- package/dist/components/tabs.js.map +1 -1
- package/dist/hooks/useApi.d.ts +56 -28
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/hooks/useClipboardCopy.d.ts +26 -0
- package/dist/hooks/useClipboardCopy.d.ts.map +1 -0
- package/dist/hooks/useClipboardCopy.js +72 -0
- package/dist/hooks/useClipboardCopy.js.map +1 -0
- package/dist/hooks/useSystemKinds.d.ts +55 -0
- package/dist/hooks/useSystemKinds.d.ts.map +1 -0
- package/dist/hooks/useSystemKinds.js +78 -0
- package/dist/hooks/useSystemKinds.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/navigation/context.d.ts +13 -1
- package/dist/navigation/context.d.ts.map +1 -1
- package/dist/navigation/context.js +205 -7
- package/dist/navigation/context.js.map +1 -1
- package/dist/navigation/index.d.ts +2 -2
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +1 -1
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/types.d.ts +11 -0
- package/dist/navigation/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/code-block.tsx +72 -9
- package/src/components/components.css +20 -0
- package/src/components/copy-field.tsx +25 -81
- package/src/components/date-range-picker.tsx +256 -0
- package/src/components/dialog.tsx +59 -18
- package/src/components/dirty-guard.tsx +153 -0
- package/src/components/extension-picker.tsx +12 -0
- package/src/components/form-dialog.tsx +133 -0
- package/src/components/json-tree.tsx +477 -42
- package/src/components/markdown.tsx +552 -0
- package/src/components/tabs.tsx +10 -1
- package/src/hooks/useClipboardCopy.ts +105 -0
- package/src/hooks/useSystemKinds.tsx +97 -0
- package/src/index.ts +20 -0
- package/src/navigation/context.tsx +245 -7
- package/src/navigation/index.ts +2 -0
- package/src/navigation/types.ts +12 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AA2BD,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,+BA+C3D"}
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
* `CodeBlock` — escaped, monospaced preformatted text for tool results and string
|
|
4
|
-
* payloads, on the design system's terminal ground (`tai-code-block`). The content
|
|
5
|
-
* is a React text child inside `<pre><code>`, so it is escaped by React and
|
|
6
|
-
* rendered verbatim; this component is NEVER an HTML sink (no
|
|
7
|
-
* `dangerouslySetInnerHTML`).
|
|
8
|
-
*
|
|
9
|
-
* The `<pre>` IS the scrolling box, so it carries the region attributes itself
|
|
10
|
-
* rather than sitting inside a `ScrollRegion` wrapper that would add a second
|
|
11
|
-
* scroller: a long line makes it a named keyboard target, a short one leaves it
|
|
12
|
-
* an ordinary block.
|
|
13
|
-
*/
|
|
2
|
+
import { CheckIcon, CopyIcon, XCircleIcon } from './icons';
|
|
14
3
|
import { useOverflowRegion } from './scroll-region';
|
|
4
|
+
import { COPIED_LABEL, useClipboardCopy } from '../hooks/useClipboardCopy';
|
|
15
5
|
/** The region's name when no language caption supplies one. */
|
|
16
6
|
const DEFAULT_LABEL = 'Code';
|
|
7
|
+
/** The button's resting accessible name; whichever face shows is also its name. */
|
|
8
|
+
const COPY_LABEL = 'Copy code';
|
|
9
|
+
/** Shown when the browser offers no clipboard at all (any non-secure context). */
|
|
10
|
+
const NO_CLIPBOARD = 'This browser will not write to the clipboard here. Select the code and copy it.';
|
|
11
|
+
/** Shown when the write is offered and refused. */
|
|
12
|
+
function writeFailed(reason) {
|
|
13
|
+
const detail = reason instanceof Error ? reason.message : String(reason);
|
|
14
|
+
return `Copy failed: ${detail}. Select the code and copy it.`;
|
|
15
|
+
}
|
|
16
|
+
/** The frame the copy button is pinned to, over the top-right of the code. */
|
|
17
|
+
const frameStyle = { position: 'relative' };
|
|
18
|
+
const copyButtonStyle = {
|
|
19
|
+
position: 'absolute',
|
|
20
|
+
top: 'var(--tai-space-2)',
|
|
21
|
+
right: 'var(--tai-space-2)',
|
|
22
|
+
};
|
|
17
23
|
export function CodeBlock({ code, language }) {
|
|
18
24
|
// No consumer ref: the measurement's own callback ref is the only thing that
|
|
19
25
|
// needs the `<pre>`, and this component publishes no `ref` prop.
|
|
20
26
|
const region = useOverflowRegion(undefined, language ?? DEFAULT_LABEL);
|
|
21
|
-
|
|
27
|
+
const { copied, error: copyError, announcement, copy, } = useClipboardCopy({
|
|
28
|
+
noClipboard: NO_CLIPBOARD,
|
|
29
|
+
writeFailed,
|
|
30
|
+
});
|
|
31
|
+
const handleCopy = () => {
|
|
32
|
+
void copy(() => code);
|
|
33
|
+
};
|
|
34
|
+
return (_jsxs("div", { className: "tai-stack tai-stack-2", children: [language !== undefined ? _jsx("span", { className: "tai-label", children: language }) : null, _jsxs("div", { style: frameStyle, children: [_jsx("pre", { className: "tai-code-block", ...region, children: _jsx("code", { children: code }) }), _jsx("button", { type: "button", className: "tai-icon-btn", style: copyButtonStyle, "aria-label": copied ? COPIED_LABEL : COPY_LABEL, onClick: handleCopy, children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children: announcement })] }), copyError !== undefined ? (_jsxs("span", { role: "alert", className: "tai-field-error", children: [_jsx(XCircleIcon, {}), copyError] })) : null] }));
|
|
22
35
|
}
|
|
23
36
|
//# sourceMappingURL=code-block.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":";AAiBA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAQ3E,+DAA+D;AAC/D,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,mFAAmF;AACnF,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B,kFAAkF;AAClF,MAAM,YAAY,GAChB,iFAAiF,CAAC;AAEpF,mDAAmD;AACnD,SAAS,WAAW,CAAC,MAAe;IAClC,MAAM,MAAM,GAAG,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;AAChE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,GAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAE3D,MAAM,eAAe,GAAkB;IACrC,QAAQ,EAAE,UAAU;IACpB,GAAG,EAAE,oBAAoB;IACzB,KAAK,EAAE,oBAAoB;CAC5B,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAkB;IAC1D,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,IAAI,aAAa,CAAC,CAAC;IAEvE,MAAM,EACJ,MAAM,EACN,KAAK,EAAE,SAAS,EAChB,YAAY,EACZ,IAAI,GACL,GAAG,gBAAgB,CAAC;QACnB,WAAW,EAAE,YAAY;QACzB,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,GAAS,EAAE;QAC5B,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,uBAAuB,aACnC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,WAAW,YAAE,QAAQ,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC9E,eAAK,KAAK,EAAE,UAAU,aACpB,cAAK,SAAS,EAAC,gBAAgB,KAAK,MAAM,YACxC,yBAAO,IAAI,GAAQ,GACf,EACN,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,cAAc,EACxB,KAAK,EAAE,eAAe,gBACV,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EAC9C,OAAO,EAAE,UAAU,YAElB,MAAM,CAAC,CAAC,CAAC,KAAC,SAAS,KAAG,CAAC,CAAC,CAAC,KAAC,QAAQ,KAAG,GAC/B,EACT,4BAAgB,QAAQ,EAAC,SAAS,EAAC,qBAAqB,YACrD,YAAY,GACR,IACH,EACL,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CACzB,gBAAM,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,iBAAiB,aAC5C,KAAC,WAAW,KAAG,EACd,SAAS,IACL,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -1316,6 +1316,26 @@
|
|
|
1316
1316
|
color: var(--tai-color-text);
|
|
1317
1317
|
}
|
|
1318
1318
|
|
|
1319
|
+
/* Fullscreen variant: fills the viewport instead of centring at a fixed size.
|
|
1320
|
+
Composes with `.tai-dialog`, overriding only geometry and edge chrome. */
|
|
1321
|
+
.tai-dialog-fullscreen {
|
|
1322
|
+
top: 0;
|
|
1323
|
+
left: 0;
|
|
1324
|
+
transform: none;
|
|
1325
|
+
/* `border-box` so `100vw` is the outer edge and never overflows; declared here,
|
|
1326
|
+
not inherited from `.tai-dialog`. */
|
|
1327
|
+
box-sizing: border-box;
|
|
1328
|
+
width: 100vw;
|
|
1329
|
+
max-width: none;
|
|
1330
|
+
/* `dvh` is the live viewport with a `vh` fallback for browsers below the `dvh` floor. */
|
|
1331
|
+
height: 100vh;
|
|
1332
|
+
height: 100dvh;
|
|
1333
|
+
max-height: none;
|
|
1334
|
+
padding: 0;
|
|
1335
|
+
border: 0;
|
|
1336
|
+
border-radius: 0;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1319
1339
|
.tai-dialog-title {
|
|
1320
1340
|
margin: 0 0 var(--tai-space-2);
|
|
1321
1341
|
font-size: var(--tai-text-section);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-field.d.ts","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"copy-field.d.ts","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"AAsBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAoCD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAuB,EAAE,KAAK,EAAE,EAAE,cAAc,+BA0D3F"}
|
|
@@ -1,41 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* `CopyField` — a show-once box for a freshly-minted secret (an API key or a
|
|
4
|
-
* minted backup key). It renders the value in a monospace, break-all,
|
|
5
|
-
* select-all box beside a copy button that writes the value to the clipboard and
|
|
6
|
-
* shows a transient "Copied" affordance. It is PRESENTATIONAL: the caller owns
|
|
7
|
-
* the value; this component fetches nothing and never re-reads the secret.
|
|
8
|
-
*
|
|
9
|
-
* The button holds BOTH states stacked in one grid cell, so the widest of them
|
|
10
|
-
* sets the width once and the flip never reflows the row. Only the active state
|
|
11
|
-
* is exposed to assistive tech, and that state's own words are the button's
|
|
12
|
-
* accessible name: a constant `aria-label` would leave a button reading "Copied"
|
|
13
|
-
* named "Copy", which fails WCAG 2.5.3 (Label in Name) and leaves a voice-control
|
|
14
|
-
* user naming a control they cannot see. The polite live region beside the button
|
|
15
|
-
* is what actually announces the flip.
|
|
16
|
-
*
|
|
17
|
-
* A clipboard write can FAIL — a permissions policy blocks it, or the page is
|
|
18
|
-
* not in a secure context, where `navigator.clipboard` does not exist at all.
|
|
19
|
-
* Either way the value is a secret shown once, so the failure is rendered as a
|
|
20
|
-
* visible `role="alert"` telling the reader to copy it by hand: throwing it at
|
|
21
|
-
* the console would leave the button looking inert and the secret unrecoverable.
|
|
22
|
-
*
|
|
23
|
-
* SAFETY: the value and caption render as TEXT (React escapes them) — never an
|
|
24
|
-
* HTML sink. The value is never logged, and neither is a failure — the alert
|
|
25
|
-
* names the reason, never the value. Pinned by a test.
|
|
26
|
-
*/
|
|
27
|
-
import { useEffect, useRef, useState } from 'react';
|
|
28
2
|
import { CheckIcon, CopyIcon, XCircleIcon } from './icons';
|
|
29
|
-
|
|
30
|
-
/** The button's
|
|
3
|
+
import { COPIED_LABEL, useClipboardCopy } from '../hooks/useClipboardCopy';
|
|
4
|
+
/** The button's resting face; whichever face is exposed is also its accessible name. */
|
|
31
5
|
const COPY_LABEL = 'Copy';
|
|
32
|
-
const COPIED_LABEL = 'Copied';
|
|
33
|
-
/** Announced once, and worded so it is never mistaken for the visible label. */
|
|
34
|
-
const COPIED_ANNOUNCEMENT = 'Copied to clipboard';
|
|
35
|
-
/** The clipboard this browser actually offers, or `undefined` when it offers none. */
|
|
36
|
-
function clipboardOf(host) {
|
|
37
|
-
return host.clipboard;
|
|
38
|
-
}
|
|
39
6
|
/** Shown when the browser offers no clipboard at all (any non-secure context). */
|
|
40
7
|
const NO_CLIPBOARD = 'This browser will not write to the clipboard here. Select the value above and copy it.';
|
|
41
8
|
/** Shown when the write is offered and refused. */
|
|
@@ -62,45 +29,13 @@ const stateStyle = {
|
|
|
62
29
|
gap: 'var(--tai-space-2)',
|
|
63
30
|
};
|
|
64
31
|
export function CopyField({ value, caption, idPrefix = 'copy-field', label }) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// component goes away — the caller's dialog closes on the same click. Clearing
|
|
70
|
-
// the timer alone would not cover that: at unmount there is no timer yet, and
|
|
71
|
-
// the resolution that follows would start one nothing is left to clear. This
|
|
72
|
-
// flag is what the resolution checks before it touches state at all.
|
|
73
|
-
const mounted = useRef(true);
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
mounted.current = true;
|
|
76
|
-
return () => {
|
|
77
|
-
mounted.current = false;
|
|
78
|
-
if (resetTimer.current !== null)
|
|
79
|
-
clearTimeout(resetTimer.current);
|
|
80
|
-
};
|
|
81
|
-
}, []);
|
|
32
|
+
const { copied, error: copyError, announcement, copy, } = useClipboardCopy({
|
|
33
|
+
noClipboard: NO_CLIPBOARD,
|
|
34
|
+
writeFailed,
|
|
35
|
+
});
|
|
82
36
|
const handleCopy = () => {
|
|
83
|
-
|
|
84
|
-
if (clipboard === undefined) {
|
|
85
|
-
setCopyError(NO_CLIPBOARD);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
setCopyError(undefined);
|
|
89
|
-
void clipboard.writeText(value).then(() => {
|
|
90
|
-
if (!mounted.current)
|
|
91
|
-
return;
|
|
92
|
-
setCopied(true);
|
|
93
|
-
if (resetTimer.current !== null)
|
|
94
|
-
clearTimeout(resetTimer.current);
|
|
95
|
-
resetTimer.current = setTimeout(() => {
|
|
96
|
-
setCopied(false);
|
|
97
|
-
}, COPIED_RESET_MS);
|
|
98
|
-
}, (error) => {
|
|
99
|
-
if (!mounted.current)
|
|
100
|
-
return;
|
|
101
|
-
setCopyError(writeFailed(error));
|
|
102
|
-
});
|
|
37
|
+
void copy(() => value);
|
|
103
38
|
};
|
|
104
|
-
return (_jsxs("div", { "data-testid": idPrefix, className: "tai-stack tai-stack-2", children: [label !== undefined ? _jsx("span", { className: "tai-field-label", children: label }) : null, _jsxs("div", { className: "tai-row", children: [_jsx("code", { className: "tai-code", style: valueStyle, children: value }), _jsx("button", { type: "button", className: "tai-btn tai-btn-secondary", "data-testid": `${idPrefix}-copy`, onClick: handleCopy, children: _jsxs("span", { style: statesStyle, children: [_jsxs("span", { "aria-hidden": copied, style: { ...stateStyle, visibility: copied ? 'hidden' : 'visible' }, children: [_jsx(CopyIcon, {}), COPY_LABEL] }), _jsxs("span", { "aria-hidden": !copied, style: { ...stateStyle, visibility: copied ? 'visible' : 'hidden' }, children: [_jsx(CheckIcon, {}), COPIED_LABEL] })] }) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children:
|
|
39
|
+
return (_jsxs("div", { "data-testid": idPrefix, className: "tai-stack tai-stack-2", children: [label !== undefined ? _jsx("span", { className: "tai-field-label", children: label }) : null, _jsxs("div", { className: "tai-row", children: [_jsx("code", { className: "tai-code", style: valueStyle, children: value }), _jsx("button", { type: "button", className: "tai-btn tai-btn-secondary", "data-testid": `${idPrefix}-copy`, onClick: handleCopy, children: _jsxs("span", { style: statesStyle, children: [_jsxs("span", { "aria-hidden": copied, style: { ...stateStyle, visibility: copied ? 'hidden' : 'visible' }, children: [_jsx(CopyIcon, {}), COPY_LABEL] }), _jsxs("span", { "aria-hidden": !copied, style: { ...stateStyle, visibility: copied ? 'visible' : 'hidden' }, children: [_jsx(CheckIcon, {}), COPIED_LABEL] })] }) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children: announcement })] }), copyError !== undefined ? (_jsxs("span", { role: "alert", className: "tai-field-error", children: [_jsx(XCircleIcon, {}), copyError] })) : null, caption !== undefined ? _jsx("span", { className: "tai-field-hint", children: caption }) : null] }));
|
|
105
40
|
}
|
|
106
41
|
//# sourceMappingURL=copy-field.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-field.js","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"copy-field.js","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DateRangePicker` — the compact time-window control observability screens share.
|
|
3
|
+
* It emits ONE of two shapes:
|
|
4
|
+
*
|
|
5
|
+
* - a relative token from a PRESET chip, `{ kind: 'relative', token }` — the backend
|
|
6
|
+
* resolves `\d+[hdw]` tokens, so a preset stays a token rather than freezing to an
|
|
7
|
+
* instant at click time;
|
|
8
|
+
* - an ABSOLUTE range from the two native `datetime-local` inputs,
|
|
9
|
+
* `{ kind: 'absolute', from, to }` with ISO instants — native inputs keep the
|
|
10
|
+
* control inside the platform date UI, no third-party calendar.
|
|
11
|
+
*
|
|
12
|
+
* Absolute ranges are normalized by {@link normalizeCustomRange}; the active window
|
|
13
|
+
* is rendered by {@link formatRangeLabel}, always with the YEAR present.
|
|
14
|
+
*/
|
|
15
|
+
import { type ReactNode } from 'react';
|
|
16
|
+
/** A relative preset: a `\d+[hdw]` token the backend resolves, plus its chip label. */
|
|
17
|
+
export interface DateRangePreset {
|
|
18
|
+
readonly token: string;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
}
|
|
21
|
+
/** The presets a picker offers when the caller names none. */
|
|
22
|
+
export declare const DEFAULT_DATE_RANGE_PRESETS: readonly DateRangePreset[];
|
|
23
|
+
/**
|
|
24
|
+
* A named time window: either a relative token the backend resolves against its
|
|
25
|
+
* own clock, or an absolute pair of ISO instants.
|
|
26
|
+
*/
|
|
27
|
+
export type DateRangeValue = {
|
|
28
|
+
readonly kind: 'relative';
|
|
29
|
+
readonly token: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: 'absolute';
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function formatRangeLabel(value: DateRangeValue, presets?: readonly DateRangePreset[]): string;
|
|
36
|
+
/**
|
|
37
|
+
* A raw from/to pair resolved to a clean absolute range. Order matters: SWAP the
|
|
38
|
+
* reversed pair first (earlier instant becomes start), THEN cap both ends at `now` —
|
|
39
|
+
* capping first can leave a future instant on the post-swap end. The end is
|
|
40
|
+
* INCLUSIVE of its minute, extended to `:59.999`, so `23:59` reaches the day's end.
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeCustomRange(fromInput: string, toInput: string, now: Date): {
|
|
43
|
+
readonly from: string;
|
|
44
|
+
readonly to: string;
|
|
45
|
+
};
|
|
46
|
+
export interface DateRangePickerProps {
|
|
47
|
+
readonly value: DateRangeValue;
|
|
48
|
+
readonly onValueChange: (value: DateRangeValue) => void;
|
|
49
|
+
/** The preset chips to offer. Defaults to {@link DEFAULT_DATE_RANGE_PRESETS}. */
|
|
50
|
+
readonly presets?: readonly DateRangePreset[];
|
|
51
|
+
/** The control group's accessible name. */
|
|
52
|
+
readonly 'aria-label'?: string;
|
|
53
|
+
readonly disabled?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The clock the absolute-range clamp reads. Defaults to the wall clock; a caller
|
|
56
|
+
* (or a test) injects a fixed instant to pin the "no future" boundary.
|
|
57
|
+
*/
|
|
58
|
+
readonly now?: () => Date;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The range control: a row of preset chips over the two `datetime-local` inputs and
|
|
62
|
+
* an Apply action, with the active window echoed as text below.
|
|
63
|
+
*
|
|
64
|
+
* Controlled: the caller owns `value` and receives the next window through
|
|
65
|
+
* `onValueChange` — a preset click emits its token immediately, while the custom
|
|
66
|
+
* inputs hold a working draft until Apply commits the normalized absolute range.
|
|
67
|
+
*
|
|
68
|
+
* The drafts track the controlled value, re-seeded on a NEW absolute `value` (keyed
|
|
69
|
+
* on `from`/`to`), so edits against an unchanged value are preserved but a genuinely
|
|
70
|
+
* different value replaces an in-progress draft.
|
|
71
|
+
*/
|
|
72
|
+
export declare function DateRangePicker({ value, onValueChange, presets, 'aria-label': ariaLabel, disabled, now, }: DateRangePickerProps): ReactNode;
|
|
73
|
+
//# sourceMappingURL=date-range-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range-picker.d.ts","sourceRoot":"","sources":["../../src/components/date-range-picker.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMrE,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,EAAE,SAAS,eAAe,EAKhE,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAgC9E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,SAAS,eAAe,EAA+B,GAC/D,MAAM,CAQR;AAuBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,IAAI,GACR;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAiBhD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACxD,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,OAAoC,EACpC,YAAY,EAAE,SAAwB,EACtC,QAAgB,EAChB,GAAsB,GACvB,EAAE,oBAAoB,GAAG,SAAS,CAmFlC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `DateRangePicker` — the compact time-window control observability screens share.
|
|
4
|
+
* It emits ONE of two shapes:
|
|
5
|
+
*
|
|
6
|
+
* - a relative token from a PRESET chip, `{ kind: 'relative', token }` — the backend
|
|
7
|
+
* resolves `\d+[hdw]` tokens, so a preset stays a token rather than freezing to an
|
|
8
|
+
* instant at click time;
|
|
9
|
+
* - an ABSOLUTE range from the two native `datetime-local` inputs,
|
|
10
|
+
* `{ kind: 'absolute', from, to }` with ISO instants — native inputs keep the
|
|
11
|
+
* control inside the platform date UI, no third-party calendar.
|
|
12
|
+
*
|
|
13
|
+
* Absolute ranges are normalized by {@link normalizeCustomRange}; the active window
|
|
14
|
+
* is rendered by {@link formatRangeLabel}, always with the YEAR present.
|
|
15
|
+
*/
|
|
16
|
+
import { useState } from 'react';
|
|
17
|
+
import { Field } from './field';
|
|
18
|
+
import { TextInput } from './inputs';
|
|
19
|
+
import { Button } from './primitives';
|
|
20
|
+
/** The presets a picker offers when the caller names none. */
|
|
21
|
+
export const DEFAULT_DATE_RANGE_PRESETS = [
|
|
22
|
+
{ token: '24h', label: 'Last 24 hours' },
|
|
23
|
+
{ token: '7d', label: 'Last 7 days' },
|
|
24
|
+
{ token: '30d', label: 'Last 30 days' },
|
|
25
|
+
{ token: '90d', label: 'Last 90 days' },
|
|
26
|
+
];
|
|
27
|
+
const RELATIVE_TOKEN = /^(\d+)([hdw])$/;
|
|
28
|
+
const UNIT_NOUN = { h: 'hour', d: 'day', w: 'week' };
|
|
29
|
+
/**
|
|
30
|
+
* A relative token as a readable phrase (`7d` → `Last 7 days`). A token outside the
|
|
31
|
+
* `\d+[hdw]` grammar raises rather than rendering a silent placeholder.
|
|
32
|
+
*/
|
|
33
|
+
function relativePhrase(token) {
|
|
34
|
+
const match = RELATIVE_TOKEN.exec(token);
|
|
35
|
+
if (match === null) {
|
|
36
|
+
throw new Error(`Invalid relative range token: ${token}`);
|
|
37
|
+
}
|
|
38
|
+
const amount = Number(match[1]);
|
|
39
|
+
const noun = UNIT_NOUN[match[2] ?? ''] ?? '';
|
|
40
|
+
return `Last ${String(amount)} ${noun}${amount === 1 ? '' : 's'}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The active window as human text: a relative token as its preset label (or a
|
|
44
|
+
* derived phrase), an absolute range as both instants with the YEAR present.
|
|
45
|
+
*/
|
|
46
|
+
const ABSOLUTE_FORMAT = new Intl.DateTimeFormat(undefined, {
|
|
47
|
+
year: 'numeric',
|
|
48
|
+
month: 'short',
|
|
49
|
+
day: 'numeric',
|
|
50
|
+
hour: '2-digit',
|
|
51
|
+
minute: '2-digit',
|
|
52
|
+
});
|
|
53
|
+
export function formatRangeLabel(value, presets = DEFAULT_DATE_RANGE_PRESETS) {
|
|
54
|
+
if (value.kind === 'relative') {
|
|
55
|
+
const preset = presets.find((candidate) => candidate.token === value.token);
|
|
56
|
+
return preset?.label ?? relativePhrase(value.token);
|
|
57
|
+
}
|
|
58
|
+
const from = new Date(value.from);
|
|
59
|
+
const to = new Date(value.to);
|
|
60
|
+
return `${ABSOLUTE_FORMAT.format(from)} – ${ABSOLUTE_FORMAT.format(to)}`;
|
|
61
|
+
}
|
|
62
|
+
/** A `datetime-local` string parsed as a local instant, or a loud error on garbage. */
|
|
63
|
+
function parseLocalInput(value, field) {
|
|
64
|
+
const parsed = new Date(value);
|
|
65
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
66
|
+
throw new Error(`Invalid ${field} datetime: ${value}`);
|
|
67
|
+
}
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
function pad(value) {
|
|
71
|
+
return String(value).padStart(2, '0');
|
|
72
|
+
}
|
|
73
|
+
/** A local instant as the `YYYY-MM-DDTHH:mm` a `datetime-local` input reads. */
|
|
74
|
+
function toLocalInputValue(date) {
|
|
75
|
+
return (`${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
|
|
76
|
+
`T${pad(date.getHours())}:${pad(date.getMinutes())}`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A raw from/to pair resolved to a clean absolute range. Order matters: SWAP the
|
|
80
|
+
* reversed pair first (earlier instant becomes start), THEN cap both ends at `now` —
|
|
81
|
+
* capping first can leave a future instant on the post-swap end. The end is
|
|
82
|
+
* INCLUSIVE of its minute, extended to `:59.999`, so `23:59` reaches the day's end.
|
|
83
|
+
*/
|
|
84
|
+
export function normalizeCustomRange(fromInput, toInput, now) {
|
|
85
|
+
let start = parseLocalInput(fromInput, 'from');
|
|
86
|
+
let end = parseLocalInput(toInput, 'to');
|
|
87
|
+
if (start.getTime() > end.getTime()) {
|
|
88
|
+
[start, end] = [end, start];
|
|
89
|
+
}
|
|
90
|
+
// Inclusive end: extend the selected minute to its final millisecond.
|
|
91
|
+
end = new Date(end);
|
|
92
|
+
end.setSeconds(59, 999);
|
|
93
|
+
const nowMs = now.getTime();
|
|
94
|
+
if (start.getTime() > nowMs)
|
|
95
|
+
start = new Date(nowMs);
|
|
96
|
+
if (end.getTime() > nowMs)
|
|
97
|
+
end = new Date(nowMs);
|
|
98
|
+
return { from: start.toISOString(), to: end.toISOString() };
|
|
99
|
+
}
|
|
100
|
+
const CUSTOM_ROW_STYLE = { alignItems: 'flex-end' };
|
|
101
|
+
const FIELD_STYLE = { flex: 1 };
|
|
102
|
+
/**
|
|
103
|
+
* The range control: a row of preset chips over the two `datetime-local` inputs and
|
|
104
|
+
* an Apply action, with the active window echoed as text below.
|
|
105
|
+
*
|
|
106
|
+
* Controlled: the caller owns `value` and receives the next window through
|
|
107
|
+
* `onValueChange` — a preset click emits its token immediately, while the custom
|
|
108
|
+
* inputs hold a working draft until Apply commits the normalized absolute range.
|
|
109
|
+
*
|
|
110
|
+
* The drafts track the controlled value, re-seeded on a NEW absolute `value` (keyed
|
|
111
|
+
* on `from`/`to`), so edits against an unchanged value are preserved but a genuinely
|
|
112
|
+
* different value replaces an in-progress draft.
|
|
113
|
+
*/
|
|
114
|
+
export function DateRangePicker({ value, onValueChange, presets = DEFAULT_DATE_RANGE_PRESETS, 'aria-label': ariaLabel = 'Date range', disabled = false, now = () => new Date(), }) {
|
|
115
|
+
const nextFrom = value.kind === 'absolute' ? value.from : null;
|
|
116
|
+
const nextTo = value.kind === 'absolute' ? value.to : null;
|
|
117
|
+
const [fromDraft, setFromDraft] = useState(() => nextFrom !== null ? toLocalInputValue(new Date(nextFrom)) : '');
|
|
118
|
+
const [toDraft, setToDraft] = useState(() => nextTo !== null ? toLocalInputValue(new Date(nextTo)) : '');
|
|
119
|
+
// The absolute ends that last seeded the drafts (null while a preset is active).
|
|
120
|
+
// Comparing against the incoming ends re-seeds only when the caller swaps in a
|
|
121
|
+
// different absolute value, leaving drafts typed against an unchanged value alone.
|
|
122
|
+
const [seeded, setSeeded] = useState(() => ({
|
|
123
|
+
from: nextFrom,
|
|
124
|
+
to: nextTo,
|
|
125
|
+
}));
|
|
126
|
+
if (seeded.from !== nextFrom || seeded.to !== nextTo) {
|
|
127
|
+
setSeeded({ from: nextFrom, to: nextTo });
|
|
128
|
+
if (nextFrom !== null && nextTo !== null) {
|
|
129
|
+
setFromDraft(toLocalInputValue(new Date(nextFrom)));
|
|
130
|
+
setToDraft(toLocalInputValue(new Date(nextTo)));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const activeToken = value.kind === 'relative' ? value.token : null;
|
|
134
|
+
const canApply = !disabled && fromDraft !== '' && toDraft !== '';
|
|
135
|
+
const applyCustom = () => {
|
|
136
|
+
onValueChange({ kind: 'absolute', ...normalizeCustomRange(fromDraft, toDraft, now()) });
|
|
137
|
+
};
|
|
138
|
+
return (_jsxs("div", { role: "group", "aria-label": ariaLabel, className: "tai-stack tai-stack-3", children: [_jsx("div", { className: "tai-row", children: presets.map((preset) => (_jsx("button", { type: "button", className: "tai-chip", "aria-pressed": activeToken === preset.token, onClick: () => {
|
|
139
|
+
onValueChange({ kind: 'relative', token: preset.token });
|
|
140
|
+
}, disabled: disabled, children: preset.label }, preset.token))) }), _jsxs("div", { className: "tai-row", style: CUSTOM_ROW_STYLE, children: [_jsx(Field, { label: "From", style: FIELD_STYLE, children: _jsx(TextInput, { type: "datetime-local", value: fromDraft, onChange: (event) => {
|
|
141
|
+
setFromDraft(event.target.value);
|
|
142
|
+
}, disabled: disabled }) }), _jsx(Field, { label: "To", style: FIELD_STYLE, children: _jsx(TextInput, { type: "datetime-local", value: toDraft, onChange: (event) => {
|
|
143
|
+
setToDraft(event.target.value);
|
|
144
|
+
}, disabled: disabled }) }), _jsx(Button, { type: "button", variant: "primary", onClick: applyCustom, disabled: !canApply, children: "Apply range" })] }), _jsx("p", { role: "status", className: "tai-muted", style: { margin: 0 }, children: formatRangeLabel(value, presets) })] }));
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=date-range-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range-picker.js","sourceRoot":"","sources":["../../src/components/date-range-picker.tsx"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,24 @@ export interface DialogProps {
|
|
|
16
16
|
readonly open?: boolean;
|
|
17
17
|
readonly defaultOpen?: boolean;
|
|
18
18
|
readonly onOpenChange?: (open: boolean) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Fill the viewport edge to edge instead of centring a fixed-size panel. The
|
|
21
|
+
* overlay, the focus trap and every other Radix behaviour are unchanged.
|
|
22
|
+
*/
|
|
23
|
+
readonly fullscreen?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* A class merged onto `RadixDialog.Content` after the surface classes, for a
|
|
26
|
+
* host that must reach the content element itself — e.g. a plugin hanging its
|
|
27
|
+
* CSS-scoping root on the panel so its styles apply inside the Radix portal.
|
|
28
|
+
*/
|
|
29
|
+
readonly contentClassName?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Drop the forced visual `Title` and the `tai-stack` children wrapper, rendering
|
|
32
|
+
* `children` directly for a chrome-free content mode. The `title` is still
|
|
33
|
+
* required and still names the dialog — it renders visually-hidden — so the
|
|
34
|
+
* accessible name never depends on the visible chrome.
|
|
35
|
+
*/
|
|
36
|
+
readonly chromeless?: boolean;
|
|
19
37
|
}
|
|
20
|
-
export declare function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, }: DialogProps): import("react").JSX.Element;
|
|
38
|
+
export declare function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, fullscreen, contentClassName, chromeless, }: DialogProps): import("react").JSX.Element;
|
|
21
39
|
//# sourceMappingURL=dialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,UAAkB,EAClB,gBAAgB,EAChB,UAAkB,GACnB,EAAE,WAAW,+BAkDb"}
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* `Dialog` — a design-system wrapper over Radix Dialog: an accessible modal (role
|
|
4
|
-
* `dialog`, focus trap, Escape to close) labelled by its title. Modality
|
|
5
|
-
*
|
|
6
|
-
* by an `aria-modal` attribute — the panel ships none.
|
|
4
|
+
* `dialog`, focus trap, Escape to close) labelled by its title. Modality comes from
|
|
5
|
+
* the focus trap and Radix marking the page inert, not an `aria-modal` attribute.
|
|
7
6
|
*
|
|
8
|
-
* Radix owns every modal behaviour
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `useModalFocusReturn`, which stands down whenever a trigger IS rendered.
|
|
7
|
+
* Radix owns every modal behaviour (focus trap, scroll lock, Escape, focus return
|
|
8
|
+
* to the trigger); this component adds only the design-system surface (`tai-overlay`
|
|
9
|
+
* scrim, `tai-dialog` panel). The one gap Radix leaves — focus return for a dialog
|
|
10
|
+
* with no trigger — is filled by `useModalFocusReturn`, which stands down when a
|
|
11
|
+
* trigger IS rendered. The `fullscreen`, `contentClassName` and `chromeless` opt-ins
|
|
12
|
+
* (see the props) reshape the panel without giving up any Radix behaviour.
|
|
15
13
|
*/
|
|
16
14
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
17
15
|
import { assertSlotElement } from '../element-slot';
|
|
18
16
|
import { useModalFocusReturn } from './modal-focus';
|
|
19
|
-
export function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, }) {
|
|
17
|
+
export function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, fullscreen = false, contentClassName, chromeless = false, }) {
|
|
20
18
|
const focusReturn = useModalFocusReturn(trigger !== undefined);
|
|
21
19
|
if (trigger !== undefined)
|
|
22
20
|
assertSlotElement(trigger, 'Dialog `trigger`');
|
|
23
|
-
|
|
21
|
+
const contentClasses = ['tai-dialog'];
|
|
22
|
+
if (fullscreen)
|
|
23
|
+
contentClasses.push('tai-dialog-fullscreen');
|
|
24
|
+
if (contentClassName !== undefined)
|
|
25
|
+
contentClasses.push(contentClassName);
|
|
26
|
+
return (_jsxs(RadixDialog.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger !== undefined ? _jsx(RadixDialog.Trigger, { asChild: true, children: trigger }) : null, _jsxs(RadixDialog.Portal, { children: [_jsx(RadixDialog.Overlay, { className: "tai-overlay" }), _jsxs(RadixDialog.Content, { className: contentClasses.join(' '), ...focusReturn, ...(description === undefined ? { 'aria-describedby': undefined } : {}), children: [_jsx(RadixDialog.Title, { className: chromeless ? 'tai-visually-hidden' : 'tai-dialog-title', children: title }), chromeless ? (_jsxs(_Fragment, { children: [description !== undefined ? (_jsx(RadixDialog.Description, { className: "tai-visually-hidden", children: description })) : null, children] })) : (_jsxs("div", { className: "tai-stack", children: [description !== undefined ? (_jsx(RadixDialog.Description, { className: "tai-muted", children: description })) : null, children] }))] })] })] }));
|
|
24
27
|
}
|
|
25
28
|
//# sourceMappingURL=dialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAuCpD,MAAM,UAAU,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,UAAU,GAAG,KAAK,EAClB,gBAAgB,EAChB,UAAU,GAAG,KAAK,GACN;IACZ,MAAM,WAAW,GAAG,mBAAmB,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;IAC/D,IAAI,OAAO,KAAK,SAAS;QAAE,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAE1E,MAAM,cAAc,GAAG,CAAC,YAAY,CAAC,CAAC;IACtC,IAAI,UAAU;QAAE,cAAc,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC7D,IAAI,gBAAgB,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE1E,OAAO,CACL,MAAC,WAAW,CAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,aAC/E,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAC,WAAW,CAAC,OAAO,IAAC,OAAO,kBAAE,OAAO,GAAuB,CAAC,CAAC,CAAC,IAAI,EAC5F,MAAC,WAAW,CAAC,MAAM,eACjB,KAAC,WAAW,CAAC,OAAO,IAAC,SAAS,EAAC,aAAa,GAAG,EAK/C,MAAC,WAAW,CAAC,OAAO,IAClB,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,KAC/B,WAAW,KACX,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAIxE,KAAC,WAAW,CAAC,KAAK,IAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,kBAAkB,YAClF,KAAK,GACY,EACnB,UAAU,CAAC,CAAC,CAAC,CACZ,8BACG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,KAAC,WAAW,CAAC,WAAW,IAAC,SAAS,EAAC,qBAAqB,YACrD,WAAW,GACY,CAC3B,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACR,CACJ,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,WAAW,aACvB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,KAAC,WAAW,CAAC,WAAW,IAAC,SAAS,EAAC,WAAW,YAC3C,WAAW,GACY,CAC3B,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACL,CACP,IACmB,IACH,IACJ,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unsaved-changes guard for tabbed editors. `Tabs` unmount inactive panels, so a
|
|
3
|
+
* tab switch tears an editor down and drops its edits. One registry closes the gap:
|
|
4
|
+
* editors report dirtiness via {@link useRegisterDirty}; {@link GuardedTabs} holds a
|
|
5
|
+
* switch away from a dirty editor behind a confirm; the same registry arms
|
|
6
|
+
* {@link useNavigationGuard}, so a route navigation confirms through the SAME dialog
|
|
7
|
+
* and a full-page unload raises the native prompt. The dialog is one shared
|
|
8
|
+
* {@link ConfirmDialog} driven by a pending-promise resolver, so the guard's
|
|
9
|
+
* `Promise<boolean>` handler and the tab switch both settle against it.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactNode } from 'react';
|
|
12
|
+
import type { TabItem } from './tabs';
|
|
13
|
+
/**
|
|
14
|
+
* Report an editor's dirty state to the enclosing {@link GuardedTabs}. A no-op
|
|
15
|
+
* outside a provider (an editor rendered standalone, e.g. in isolation tests) so
|
|
16
|
+
* the hook is always safe to call.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useRegisterDirty(dirty: boolean): void;
|
|
19
|
+
/**
|
|
20
|
+
* A drop-in replacement for {@link Tabs} that guards a switch away from a dirty
|
|
21
|
+
* editor. Wraps the tabs in a {@link DirtyGuardProvider}, so an editor rendered in
|
|
22
|
+
* any panel can arm the guard through {@link useRegisterDirty}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function GuardedTabs({ items, defaultValue, }: {
|
|
25
|
+
readonly items: readonly TabItem[];
|
|
26
|
+
readonly defaultValue?: string;
|
|
27
|
+
}): ReactNode;
|
|
28
|
+
//# sourceMappingURL=dirty-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dirty-guard.d.ts","sourceRoot":"","sources":["../../src/components/dirty-guard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AA0EtC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CASrD;AAwBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,YAAY,GACb,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,GAAG,SAAS,CAMZ"}
|