@tai42/studio-sdk 6.1.1 → 7.0.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/completion-input.d.ts.map +1 -1
- package/dist/components/completion-input.js +19 -2
- package/dist/components/completion-input.js.map +1 -1
- package/dist/components/components.css +4 -3
- package/dist/components/explorer-view.d.ts.map +1 -1
- package/dist/components/explorer-view.js +11 -22
- package/dist/components/explorer-view.js.map +1 -1
- package/dist/components/field.d.ts +14 -1
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +5 -2
- package/dist/components/field.js.map +1 -1
- package/dist/components/open-target.d.ts +68 -0
- package/dist/components/open-target.d.ts.map +1 -0
- package/dist/components/open-target.js +48 -0
- package/dist/components/open-target.js.map +1 -0
- package/dist/expression/ExpressionField.d.ts +140 -0
- package/dist/expression/ExpressionField.d.ts.map +1 -0
- package/dist/expression/ExpressionField.js +88 -0
- package/dist/expression/ExpressionField.js.map +1 -0
- package/dist/expression/context.d.ts +50 -0
- package/dist/expression/context.d.ts.map +1 -0
- package/dist/expression/context.js +53 -0
- package/dist/expression/context.js.map +1 -0
- package/dist/expression/types.d.ts +157 -0
- package/dist/expression/types.d.ts.map +1 -0
- package/dist/expression/types.js +11 -0
- package/dist/expression/types.js.map +1 -0
- package/dist/hooks/useApi.d.ts +8 -0
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/plugin/registry.js +39 -1
- package/dist/plugin/registry.js.map +1 -1
- package/dist/plugin/types.d.ts +31 -0
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/schema-editor/SchemaEditor.d.ts.map +1 -1
- package/dist/schema-editor/SchemaEditor.js +8 -4
- package/dist/schema-editor/SchemaEditor.js.map +1 -1
- package/dist/schema-form/classify.d.ts +5 -3
- package/dist/schema-form/classify.d.ts.map +1 -1
- package/dist/schema-form/classify.js +42 -16
- package/dist/schema-form/classify.js.map +1 -1
- package/dist/schema-form/default-value.d.ts.map +1 -1
- package/dist/schema-form/default-value.js +4 -1
- package/dist/schema-form/default-value.js.map +1 -1
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +3 -3
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/json-field.d.ts +14 -0
- package/dist/schema-form/json-field.d.ts.map +1 -0
- package/dist/schema-form/json-field.js +104 -0
- package/dist/schema-form/json-field.js.map +1 -0
- package/dist/schema-form/types.d.ts +2 -2
- package/dist/schema-form/types.js +2 -2
- package/dist/schema-form/validate.d.ts.map +1 -1
- package/dist/schema-form/validate.js +11 -4
- package/dist/schema-form/validate.js.map +1 -1
- package/package.json +2 -2
- package/src/components/completion-input.tsx +19 -2
- package/src/components/components.css +4 -3
- package/src/components/explorer-view.tsx +16 -27
- package/src/components/field.tsx +20 -2
- package/src/components/open-target.ts +121 -0
- package/src/expression/ExpressionField.tsx +330 -0
- package/src/expression/context.tsx +83 -0
- package/src/expression/types.ts +164 -0
- package/src/index.ts +26 -0
- package/src/plugin/registry.ts +50 -1
- package/src/plugin/types.ts +32 -0
- package/src/schema-editor/SchemaEditor.tsx +8 -4
- package/src/schema-form/classify.ts +103 -18
- package/src/schema-form/default-value.ts +4 -1
- package/src/schema-form/field-node.tsx +7 -4
- package/src/schema-form/json-field.tsx +143 -0
- package/src/schema-form/types.ts +2 -2
- package/src/schema-form/validate.ts +10 -4
- package/dist/schema-form/unsupported-notice.d.ts +0 -18
- package/dist/schema-form/unsupported-notice.d.ts.map +0 -1
- package/dist/schema-form/unsupported-notice.js +0 -8
- package/dist/schema-form/unsupported-notice.js.map +0 -1
- package/src/schema-form/unsupported-notice.tsx +0 -39
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `validateAgainstSchema` — a loud, structured pre-submit check. It walks the
|
|
3
3
|
* schema against a value and returns a path-keyed `SchemaFormErrors` bag:
|
|
4
|
-
* required-presence, type/format mismatches, enum/const violations, and
|
|
5
|
-
*
|
|
4
|
+
* required-presence, type/format mismatches, enum/const violations, and the
|
|
5
|
+
* container constraint on a free-form `json` field. It never throws on a
|
|
6
6
|
* malformed value — a malformed value IS the error it reports.
|
|
7
7
|
*/
|
|
8
8
|
import { classifySchema } from './classify';
|
|
@@ -52,8 +52,15 @@ function walk(schema, value, path, root, errors, maxUploadBytes) {
|
|
|
52
52
|
}
|
|
53
53
|
const { model } = classified;
|
|
54
54
|
switch (model.kind) {
|
|
55
|
-
case '
|
|
56
|
-
|
|
55
|
+
case 'json':
|
|
56
|
+
// A free-form JSON field: any JSON is accepted, subject only to the
|
|
57
|
+
// container the schema commits to (mirrors the renderer's inline check).
|
|
58
|
+
if (model.jsonType === 'object' && !isPlainObject(value)) {
|
|
59
|
+
errors[path] = 'must be a JSON object';
|
|
60
|
+
}
|
|
61
|
+
else if (model.jsonType === 'array' && !Array.isArray(value)) {
|
|
62
|
+
errors[path] = 'must be a JSON array';
|
|
63
|
+
}
|
|
57
64
|
return;
|
|
58
65
|
case 'const':
|
|
59
66
|
if (value !== model.value)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAa7C,MAAM,QAAQ,GAAG,4BAA4B,CAAC;AAC9C,MAAM,OAAO,GAAG,qBAAqB,CAAC;AACtC,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IACzC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,KAAa;IAChD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,+BAA+B,CAAC;QAC5E,KAAK,KAAK,CAAC;QACX,KAAK,eAAe;YAClB,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC;gBACvC,OAAO,SAAS,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,qBAAqB,CAAC;YAC/B,CAAC;QACH,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC;QAClE,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC5D,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,mCAAmC,CAAC;QAC1C,KAAK,WAAW;YACd,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,SAAS,CAAC;QACnF;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CACX,MAAkB,EAClB,KAAc,EACd,IAAY,EACZ,IAAgB,EAChB,MAA8B,EAC9B,cAAkC;IAElC,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEhD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,QAAQ;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;IAC7B,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,aAAa;
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tai42/studio-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "The Studio plugin contract, shared UI components, and hooks that every feature builds on.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "tai42 <oss@tai42.ai>",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@radix-ui/react-select": "^2.3.2",
|
|
91
91
|
"@radix-ui/react-tabs": "^1.1.16",
|
|
92
92
|
"@radix-ui/react-tooltip": "^1.2.11",
|
|
93
|
-
"@tai42/api-client": "
|
|
93
|
+
"@tai42/api-client": "7.0.0"
|
|
94
94
|
},
|
|
95
95
|
"scripts": {
|
|
96
96
|
"build": "tsc -b tsconfig.build.json && node scripts/copy-assets.mjs",
|
|
@@ -30,10 +30,27 @@ export interface CompletionInputProps {
|
|
|
30
30
|
const wrapStyle: CSSProperties = { position: 'relative' };
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* The popup
|
|
34
|
-
*
|
|
33
|
+
* The popup FLOATS below the field: absolutely positioned within the
|
|
34
|
+
* `position: relative` wrapper (`top: 100%` = the field's bottom edge, stretched
|
|
35
|
+
* `left`/`right` to the field's width) so it is taken OUT OF FLOW — it neither
|
|
36
|
+
* displaces the content below the field nor gets laid out inside it. Its stacking
|
|
37
|
+
* comes from `tai-select-content`'s `--tai-z-popover` z-index, which was inert
|
|
38
|
+
* while the list was in-flow (z-index needs a positioned box) and is exactly what
|
|
39
|
+
* this `position` now activates, so the list clears a sibling control it overlaps.
|
|
40
|
+
*
|
|
41
|
+
* Absolute-in-wrapper rather than a portal on purpose: this is an inline form
|
|
42
|
+
* control (a `SchemaForm` string field). A portal would need anchor-rect popper
|
|
43
|
+
* plumbing, and it buys nothing here — the wrapper is a plain flow box, so the
|
|
44
|
+
* only container that could clip the list is a scrollable ancestor (e.g. the
|
|
45
|
+
* `Dialog` body), where an absolute list simply extends that ancestor's scroll
|
|
46
|
+
* area instead of being cut off. It is still a `<ul>`, so it also drops the marker
|
|
47
|
+
* a host without a CSS reset would draw.
|
|
35
48
|
*/
|
|
36
49
|
const listStyle: CSSProperties = {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
top: '100%',
|
|
52
|
+
left: 0,
|
|
53
|
+
right: 0,
|
|
37
54
|
marginTop: 'var(--tai-space-1)',
|
|
38
55
|
marginBottom: 0,
|
|
39
56
|
listStyle: 'none',
|
|
@@ -533,8 +533,9 @@
|
|
|
533
533
|
clears the dialog scrim that would otherwise cover and swallow it. The
|
|
534
534
|
height cap yields to the space the popper actually has, so a long list in a
|
|
535
535
|
short viewport still scrolls inside the screen; the fallback covers the
|
|
536
|
-
|
|
537
|
-
publish that measurement
|
|
536
|
+
completion suggestion list, which wears these classes without a Radix popper
|
|
537
|
+
to publish that measurement (it floats via its own absolute position, not a
|
|
538
|
+
popper — see `completion-input.tsx`). */
|
|
538
539
|
.tai-select-content {
|
|
539
540
|
z-index: var(--tai-z-popover);
|
|
540
541
|
/* Capped against the VIEWPORT as well as its own content, like every other
|
|
@@ -545,7 +546,7 @@
|
|
|
545
546
|
overflow-y: auto;
|
|
546
547
|
/* The cap is on the BORDER box, or it is not a cap at all: with the default
|
|
547
548
|
`content-box` the side padding and the 1 px boundary sit outside it. Radix
|
|
548
|
-
inline-styles `box-sizing: border-box` on its own popper, but the
|
|
549
|
+
inline-styles `box-sizing: border-box` on its own popper, but the completion
|
|
549
550
|
suggestion list wears this class as a plain `<ul>` with no Radix near it. */
|
|
550
551
|
box-sizing: border-box;
|
|
551
552
|
background: var(--tai-color-surface-raised);
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
* {@link matchesSelectedTags}, {@link UNTAGGED_TOKEN}) so every consuming screen
|
|
13
13
|
* shares one vocabulary/untagged-sentinel/OR-match rule rather than copying it.
|
|
14
14
|
*/
|
|
15
|
-
import { useCallback, useEffect, useState, type
|
|
15
|
+
import { useCallback, useEffect, useState, type ReactNode } from 'react';
|
|
16
16
|
|
|
17
17
|
import { EntityCardGrid } from './entity-card-grid';
|
|
18
18
|
import { childFolders, FolderBreadcrumb, FolderRow, type Folder } from './folder-nav';
|
|
19
19
|
import { SearchIcon } from './icons';
|
|
20
20
|
import { TextInput } from './inputs';
|
|
21
|
+
import { openTargetProps, type OpenTargetProps } from './open-target';
|
|
21
22
|
import { Button, Card, EmptyState } from './primitives';
|
|
22
23
|
import { ScrollRegion } from './scroll-region';
|
|
23
24
|
import { Select } from './select';
|
|
@@ -373,32 +374,20 @@ export function ExplorerView<T>({
|
|
|
373
374
|
tags.onChange(next);
|
|
374
375
|
};
|
|
375
376
|
|
|
376
|
-
// The pointer-open props for one item's row/card,
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
const openProps = (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const target = event.target;
|
|
391
|
-
if (
|
|
392
|
-
target instanceof Element &&
|
|
393
|
-
target.closest('button, a, [role="menu"], [role="menuitem"]') !== null
|
|
394
|
-
) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
onOpenItem(item);
|
|
398
|
-
},
|
|
399
|
-
style: { cursor: 'pointer' },
|
|
400
|
-
};
|
|
401
|
-
};
|
|
377
|
+
// The pointer-open props for one item's row/card, via the shared house pattern
|
|
378
|
+
// ({@link openTargetProps}): present only when `onOpenItem` is set, it yields to
|
|
379
|
+
// the item's own nested interactive elements (its name link, an actions kebab)
|
|
380
|
+
// and to a text-selection drag. The accessible activation path stays that nested
|
|
381
|
+
// name link, so no `keyboard` here — the row is never itself a focus stop.
|
|
382
|
+
const openProps = (item: T): OpenTargetProps =>
|
|
383
|
+
openTargetProps({
|
|
384
|
+
onOpen:
|
|
385
|
+
onOpenItem === undefined
|
|
386
|
+
? undefined
|
|
387
|
+
: () => {
|
|
388
|
+
onOpenItem(item);
|
|
389
|
+
},
|
|
390
|
+
});
|
|
402
391
|
|
|
403
392
|
const hasEntries = subfolders.length > 0 || filtered.length > 0;
|
|
404
393
|
|
package/src/components/field.tsx
CHANGED
|
@@ -91,6 +91,19 @@ export interface FieldProps {
|
|
|
91
91
|
* `group`.
|
|
92
92
|
*/
|
|
93
93
|
readonly group?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Render the label for ASSISTIVE TECH ONLY — visually hidden, but still the
|
|
96
|
+
* control's accessible name through the very same `<label for>` (or, in a group,
|
|
97
|
+
* the same `aria-labelledby`). The label element stays in the DOM behind
|
|
98
|
+
* `tai-visually-hidden`, so nothing about the accessible name changes; only its
|
|
99
|
+
* paint is dropped. It is the standard idiom for a dense grid of otherwise
|
|
100
|
+
* identical fields, where each row needs a UNIQUE, addressable name (a
|
|
101
|
+
* voice-control user can only say a name that exists) but there is no room for a
|
|
102
|
+
* visible label on every row — the same trade `RadioGroup`'s `visuallyHiddenLabel`
|
|
103
|
+
* and `ViewToggle` make. The label text still governs WCAG 2.5.3 (Label in Name),
|
|
104
|
+
* so a caller passes the real per-row name as `label`, not a decoration.
|
|
105
|
+
*/
|
|
106
|
+
readonly hideLabel?: boolean;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
export function Field({
|
|
@@ -101,6 +114,7 @@ export function Field({
|
|
|
101
114
|
style,
|
|
102
115
|
className,
|
|
103
116
|
group = false,
|
|
117
|
+
hideLabel = false,
|
|
104
118
|
}: FieldProps) {
|
|
105
119
|
const controlId = useId();
|
|
106
120
|
const labelId = `${controlId}-label`;
|
|
@@ -122,6 +136,10 @@ export function Field({
|
|
|
122
136
|
? { controlId: undefined, describedBy: undefined, invalid: error !== undefined }
|
|
123
137
|
: { controlId, describedBy, invalid: error !== undefined };
|
|
124
138
|
|
|
139
|
+
// The label keeps its `htmlFor`/`id` wiring either way — `hideLabel` only drops
|
|
140
|
+
// its PAINT, so the accessible name it gives the control is unchanged.
|
|
141
|
+
const labelClassName = hideLabel ? 'tai-field-label tai-visually-hidden' : 'tai-field-label';
|
|
142
|
+
|
|
125
143
|
return (
|
|
126
144
|
<FieldContext.Provider value={value}>
|
|
127
145
|
<div
|
|
@@ -129,11 +147,11 @@ export function Field({
|
|
|
129
147
|
style={style}
|
|
130
148
|
>
|
|
131
149
|
{group ? (
|
|
132
|
-
<span id={labelId} className=
|
|
150
|
+
<span id={labelId} className={labelClassName}>
|
|
133
151
|
{label}
|
|
134
152
|
</span>
|
|
135
153
|
) : (
|
|
136
|
-
<label id={labelId} htmlFor={controlId} className=
|
|
154
|
+
<label id={labelId} htmlFor={controlId} className={labelClassName}>
|
|
137
155
|
{label}
|
|
138
156
|
</label>
|
|
139
157
|
)}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `openTargetProps` — the ONE house pattern for a whole-row / whole-card "open"
|
|
3
|
+
* affordance.
|
|
4
|
+
*
|
|
5
|
+
* A navigable list entry (a table `<TR>`, a card `<div role="listitem">`) has a
|
|
6
|
+
* single natural destination: opening it. The industry-standard affordance is to
|
|
7
|
+
* make the WHOLE entry the click target — not only the name link buried in its
|
|
8
|
+
* first cell — while never HIJACKING the interactive elements nested inside it
|
|
9
|
+
* (the name link itself, an actions kebab, a Run button, an expandable preview).
|
|
10
|
+
* This helper is that affordance, extracted once so every surface spreads the
|
|
11
|
+
* same object rather than hand-rolling — and subtly diverging on — the yield
|
|
12
|
+
* rules.
|
|
13
|
+
*
|
|
14
|
+
* The contract, spread onto the entry element:
|
|
15
|
+
* - `cursor: pointer`, so the whole entry reads as activatable.
|
|
16
|
+
* - a click handler that opens the entry, EXCEPT when the click
|
|
17
|
+
* • landed on (or inside) a nested interactive element — a `button`, an
|
|
18
|
+
* `a`, or an inline `[role="menu"]`/`[role="menuitem"]` — which owns its
|
|
19
|
+
* own activation, or
|
|
20
|
+
* • is the tail of a press-drag that SELECTED text (a non-collapsed
|
|
21
|
+
* selection), which is a read gesture, not an open intent.
|
|
22
|
+
* - with `keyboard`, `tabIndex: 0` and an Enter/Space handler under the same
|
|
23
|
+
* yield rules, so the entry is operable without a pointer.
|
|
24
|
+
*
|
|
25
|
+
* Keyboard note: PREFER giving the entry a real nested interactive element (the
|
|
26
|
+
* name `AppLink`) as its accessible activation path and leaving `keyboard` off —
|
|
27
|
+
* the helper yields to that element, so it stays the one keyboard/AT target and
|
|
28
|
+
* the whole-entry click is a pure pointer convenience layered over it. Reach for
|
|
29
|
+
* `keyboard` only when the entry genuinely has no such nested control (a run row
|
|
30
|
+
* of timestamps and badges), where the entry itself must become the focus stop.
|
|
31
|
+
*
|
|
32
|
+
* Dependency-free: it reads only the DOM `event.target`, `window.getSelection()`,
|
|
33
|
+
* and returns plain React attributes — nothing to bundle, nothing to mock.
|
|
34
|
+
*/
|
|
35
|
+
import type { KeyboardEvent, MouseEvent } from 'react';
|
|
36
|
+
|
|
37
|
+
/** The nested interactive elements a whole-target open always yields to: a click
|
|
38
|
+
* or key that originates on one of these (or a descendant, e.g. an icon `<svg>`
|
|
39
|
+
* inside a button) is that element's own activation, never an open of the entry.
|
|
40
|
+
* The menu roles cover an INLINE menu; a portalled menu (Radix) never bubbles to
|
|
41
|
+
* the entry, so it needs no selector here. */
|
|
42
|
+
const NESTED_INTERACTIVE = 'button, a, [role="menu"], [role="menuitem"]';
|
|
43
|
+
|
|
44
|
+
export interface OpenTargetOptions {
|
|
45
|
+
/** Open the entry. `undefined` disables the affordance entirely — the helper
|
|
46
|
+
* returns empty props so the element stays inert (no cursor, no handlers),
|
|
47
|
+
* which lets a caller wire the whole thing behind an optional prop. */
|
|
48
|
+
readonly onOpen: (() => void) | undefined;
|
|
49
|
+
/** Extra selector(s) that also count as "nested interactive" for THIS surface,
|
|
50
|
+
* merged with the built-in set. Use it for a marked non-`button`/`a` subtree
|
|
51
|
+
* that owns its own clicks (e.g. an expandable preview keyed by a data
|
|
52
|
+
* attribute). Standard CSS selector syntax, comma-separated. */
|
|
53
|
+
readonly ignoreWithin?: string;
|
|
54
|
+
/** Make the entry itself keyboard-operable: a `tabIndex: 0` focus stop with
|
|
55
|
+
* Enter/Space activation. Leave off when a nested interactive element already
|
|
56
|
+
* carries the accessible path (see the keyboard note above) — adding it there
|
|
57
|
+
* would create a duplicate tab stop. */
|
|
58
|
+
readonly keyboard?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The React attributes {@link openTargetProps} yields — spread onto the entry
|
|
62
|
+
* element (`<TR>`, card `<div>`). Every field is optional so the disabled case
|
|
63
|
+
* is the empty object. */
|
|
64
|
+
export interface OpenTargetProps {
|
|
65
|
+
readonly onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
66
|
+
readonly onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
67
|
+
readonly tabIndex?: number;
|
|
68
|
+
readonly style?: { readonly cursor: 'pointer' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** True when the event originated on, or inside, an element the entry must yield
|
|
72
|
+
* to — a built-in nested-interactive element or a caller `ignoreWithin` match. */
|
|
73
|
+
function landedOnNestedInteractive(target: EventTarget | null, selector: string): boolean {
|
|
74
|
+
return target instanceof Element && target.closest(selector) !== null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build the whole-target open props for one entry. See the module doc for the
|
|
79
|
+
* contract; pass `onOpen: undefined` to get an inert entry (empty props).
|
|
80
|
+
*/
|
|
81
|
+
export function openTargetProps({
|
|
82
|
+
onOpen,
|
|
83
|
+
ignoreWithin,
|
|
84
|
+
keyboard = false,
|
|
85
|
+
}: OpenTargetOptions): OpenTargetProps {
|
|
86
|
+
if (onOpen === undefined) return {};
|
|
87
|
+
|
|
88
|
+
const selector =
|
|
89
|
+
ignoreWithin === undefined ? NESTED_INTERACTIVE : `${NESTED_INTERACTIVE}, ${ignoreWithin}`;
|
|
90
|
+
|
|
91
|
+
const props: {
|
|
92
|
+
onClick: (event: MouseEvent<HTMLElement>) => void;
|
|
93
|
+
onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
94
|
+
tabIndex?: number;
|
|
95
|
+
style: { readonly cursor: 'pointer' };
|
|
96
|
+
} = {
|
|
97
|
+
onClick: (event) => {
|
|
98
|
+
// A press-drag that selects the entry's text also fires a click; that is a
|
|
99
|
+
// read gesture, not an open intent, so an active (non-collapsed) selection
|
|
100
|
+
// suppresses the open. A keyboard Enter cannot leave a selection, so its
|
|
101
|
+
// handler skips this check.
|
|
102
|
+
if (window.getSelection()?.isCollapsed === false) return;
|
|
103
|
+
if (landedOnNestedInteractive(event.target, selector)) return;
|
|
104
|
+
onOpen();
|
|
105
|
+
},
|
|
106
|
+
style: { cursor: 'pointer' },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (keyboard) {
|
|
110
|
+
props.tabIndex = 0;
|
|
111
|
+
props.onKeyDown = (event) => {
|
|
112
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
113
|
+
if (landedOnNestedInteractive(event.target, selector)) return;
|
|
114
|
+
// Space would otherwise scroll the region; Enter/Space both activate.
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
onOpen();
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return props;
|
|
121
|
+
}
|