@object-ui/components 17.5.0 → 17.7.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/CHANGELOG.md +10155 -0
- package/README.md +20 -10
- package/dist/custom/button-group.d.ts +2 -2
- package/dist/custom/character-count.d.ts +56 -0
- package/dist/custom/combobox.d.ts +2 -4
- package/dist/custom/config-field-renderer.d.ts +1 -1
- package/dist/custom/config-panel-renderer.d.ts +3 -3
- package/dist/custom/empty.d.ts +16 -4
- package/dist/custom/field.d.ts +1 -1
- package/dist/custom/filter-builder.d.ts +448 -36
- package/dist/custom/fullscreen-editor.d.ts +48 -2
- package/dist/custom/index.d.ts +27 -28
- package/dist/custom/item.d.ts +2 -2
- package/dist/custom/kbd.d.ts +1 -1
- package/dist/custom/mobile-dialog-content.d.ts +22 -1
- package/dist/custom/navigation-overlay.d.ts +105 -0
- package/dist/custom/refresh-indicator.d.ts +8 -2
- package/dist/custom/resizable.d.ts +1 -1
- package/dist/custom/view-states.d.ts +74 -1
- package/dist/debug/index.d.ts +2 -2
- package/dist/hooks/related-count-store.d.ts +26 -28
- package/dist/index.css +174 -364
- package/dist/index.d.ts +21 -21
- package/dist/index.js +7274 -6350
- package/dist/index.umd.cjs +12 -13
- package/dist/lib/close-label.d.ts +1 -2
- package/dist/lib/date-fns-locale.d.ts +41 -0
- package/dist/lib/form-control-dom-props.d.ts +77 -0
- package/dist/lib/lazy-icon.d.ts +32 -1
- package/dist/lib/lucide-record-icon-names.d.ts +30 -0
- package/dist/lib/slider-thumb.d.ts +86 -0
- package/dist/lib/utils.d.ts +113 -0
- package/dist/notifications/index.d.ts +5 -5
- package/dist/renderers/action/action-bar.d.ts +8 -36
- package/dist/renderers/action/action-button.d.ts +15 -4
- package/dist/renderers/action/action-group.d.ts +5 -4
- package/dist/renderers/action/action-icon.d.ts +12 -2
- package/dist/renderers/action/action-menu.d.ts +5 -2
- package/dist/renderers/action/auto-trigger.d.ts +19 -4
- package/dist/renderers/action/resolve-icon.d.ts +27 -2
- package/dist/renderers/action/static-params.d.ts +79 -0
- package/dist/renderers/action/visibility-gate.d.ts +11 -3
- package/dist/renderers/basic/elements.d.ts +6 -3
- package/dist/renderers/basic/html-elements.d.ts +8 -4
- package/dist/renderers/basic/readProps.d.ts +60 -0
- package/dist/renderers/complex/data-table.d.ts +31 -14
- package/dist/renderers/complex/dataTableBindDiagnostic.d.ts +187 -0
- package/dist/renderers/form/validationRuleKeys.d.ts +45 -0
- package/dist/renderers/layout/box.d.ts +8 -0
- package/dist/renderers/layout/containers.d.ts +1 -10
- package/dist/renderers/layout/react-page.d.ts +16 -2
- package/dist/renderers/navigation/sidebar.d.ts +31 -0
- package/dist/share/index.d.ts +2 -2
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/calendar.d.ts +3 -2
- package/dist/ui/carousel.d.ts +2 -2
- package/dist/ui/command.d.ts +8 -8
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/index.d.ts +46 -47
- package/dist/ui/input-otp.d.ts +2 -2
- package/dist/ui/pagination.d.ts +1 -1
- package/dist/ui/sheet.d.ts +1 -1
- package/dist/ui/sidebar.d.ts +4 -3
- package/dist/ui/slider.d.ts +2 -1
- package/package.json +17 -18
- package/dist/SchemaRenderer.d.ts +0 -4
- package/dist/custom/action-param-dialog.d.ts +0 -21
- package/dist/custom/export-progress-dialog.d.ts +0 -43
- package/dist/hooks/use-export-job.d.ts +0 -34
- package/dist/renderers/data-display/table.d.ts +0 -2
- package/dist/ui/chart.d.ts +0 -56
- package/dist/ui/toast.d.ts +0 -22
|
@@ -5,14 +5,24 @@ import { UIActionSchema } from '@object-ui/types';
|
|
|
5
5
|
* `locations`, `enabled` and `size` are all modern-only keys this renderer
|
|
6
6
|
* forwards, and the legacy `crud.ts` `ActionSchema` is `@deprecated` and pins
|
|
7
7
|
* `type: 'action'` where this renderer's own registry `inputs` declare
|
|
8
|
-
* `'script' | 'url' | 'modal' | 'flow' | 'api'`.
|
|
8
|
+
* `'script' | 'url' | 'modal' | 'flow' | 'api'`. `actionType` is the DECLARED
|
|
9
|
+
* input carrying that execution type (objectui#7415), for the same reason it is
|
|
10
|
+
* on `action:button`; `type` stays on the intersection as the SDUI envelope's
|
|
11
|
+
* component discriminator and is not read as an action type here.
|
|
9
12
|
*/
|
|
10
|
-
export interface
|
|
13
|
+
export interface ActionIconRendererProps {
|
|
11
14
|
schema: UIActionSchema & {
|
|
12
15
|
type: string;
|
|
13
16
|
className?: string;
|
|
17
|
+
actionType?: string;
|
|
14
18
|
};
|
|
15
19
|
className?: string;
|
|
16
20
|
context?: Record<string, any>;
|
|
21
|
+
/**
|
|
22
|
+
* The host-EVALUATED enablement verdict — see `ActionButtonRendererProps` for the
|
|
23
|
+
* mechanism (objectui#9131). Declared rather than left to the index
|
|
24
|
+
* signature, consumed by name below, never re-spread onto the DOM.
|
|
25
|
+
*/
|
|
26
|
+
disabled?: boolean;
|
|
17
27
|
[key: string]: any;
|
|
18
28
|
}
|
|
@@ -12,8 +12,11 @@ export interface ActionMenuSchema {
|
|
|
12
12
|
variant?: string;
|
|
13
13
|
/** Trigger size */
|
|
14
14
|
size?: string;
|
|
15
|
-
/** Visibility
|
|
16
|
-
visible?: string
|
|
15
|
+
/** Visibility predicate: a boolean, a CEL string, or a `{ dialect, source }` envelope */
|
|
16
|
+
visible?: boolean | string | {
|
|
17
|
+
dialect: string;
|
|
18
|
+
source?: string;
|
|
19
|
+
};
|
|
17
20
|
/** Custom CSS class */
|
|
18
21
|
className?: string;
|
|
19
22
|
[key: string]: any;
|
|
@@ -11,10 +11,25 @@
|
|
|
11
11
|
* another. Deliberately strict: only the literal `true` arms it.
|
|
12
12
|
*/
|
|
13
13
|
export declare function hasAutoTrigger(action: unknown): boolean;
|
|
14
|
+
/** The fields of an action this module reads. */
|
|
15
|
+
export interface AutoTriggerSubject {
|
|
16
|
+
autoTrigger?: unknown;
|
|
17
|
+
visible?: unknown;
|
|
18
|
+
name?: string;
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
14
21
|
/**
|
|
15
|
-
* Run `run` at most once, as soon as `
|
|
22
|
+
* Run `run` at most once, as soon as `action` asks to be auto-triggered — unless
|
|
23
|
+
* the action's own declared `visible` gate hides it, in which case the trigger
|
|
24
|
+
* is refused and reported instead (objectui#4191).
|
|
16
25
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
26
|
+
* @param action The action the renderer received (read: `autoTrigger`,
|
|
27
|
+
* `visible`, `name`, `label`).
|
|
28
|
+
* @param isVisible The renderer's own evaluated `visible` verdict — the same
|
|
29
|
+
* value its early return consults. Only consulted when the
|
|
30
|
+
* action DECLARES a gate, exactly like that early return.
|
|
31
|
+
* @param run The renderer's click path. It may change identity freely (it
|
|
32
|
+
* is rebuilt from `schema` on most renders); the ref is what
|
|
33
|
+
* makes this once-only, so depending on it is safe.
|
|
19
34
|
*/
|
|
20
|
-
export declare function useAutoTriggerOnce(
|
|
35
|
+
export declare function useAutoTriggerOnce(action: AutoTriggerSubject, isVisible: boolean, run: () => void | Promise<void>): void;
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import { LucideIcon } from 'lucide-react';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* The lookup this seam performs, exposed for DIAGNOSTICS only.
|
|
4
|
+
*
|
|
5
|
+
* `renderers/basic/icon.tsx` names both halves in its objectui#5631 warning
|
|
6
|
+
* (`lookup: "Home" -> "House"`), and that message is under a maintainer ruling
|
|
7
|
+
* 一字不动. Without this it would need its own copy of the tokeniser and the
|
|
8
|
+
* map to keep saying the same thing — which is precisely the duplication this
|
|
9
|
+
* card removed. ⛔ It decides nothing: no call site can change what resolves by
|
|
10
|
+
* reading it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function describeIconLookup(name: string): {
|
|
13
|
+
pascal: string;
|
|
14
|
+
key: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Resolve an authored Lucide icon name to its component.
|
|
18
|
+
*
|
|
19
|
+
* Accepts kebab-case, snake_case, space-separated and PascalCase spellings.
|
|
20
|
+
* Returns `null` when the name is absent or names no live glyph — deciding what
|
|
21
|
+
* to draw instead belongs to the caller (see the module docblock).
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ The component this returns draws its `<svg>` — with lucide's own classes,
|
|
24
|
+
* box and attributes — SYNCHRONOUSLY, and fills in the path data when the
|
|
25
|
+
* icon's module arrives (objectui#9251). So `svg.lucide-house` matches on the
|
|
26
|
+
* first frame and nothing reflows, while the 1,781 icon modules stay off the
|
|
27
|
+
* eager path. ⛔ A test that asserts on the PATHS inside the svg must await
|
|
28
|
+
* them (`findBy*` / `waitFor`); everything that asks which icon this is —
|
|
29
|
+
* including `null` vs not — is answered in the same tick, as before.
|
|
5
30
|
*/
|
|
6
31
|
export declare function resolveIcon(name: string | undefined): LucideIcon | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/** The two keys this module reads off an action node. */
|
|
9
|
+
export interface StaticParamsSubject {
|
|
10
|
+
params?: unknown;
|
|
11
|
+
properties?: unknown;
|
|
12
|
+
name?: unknown;
|
|
13
|
+
label?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** Reset the warn-once memo. Exported for tests. */
|
|
16
|
+
export declare function resetStaticParamsWarnings(): void;
|
|
17
|
+
/**
|
|
18
|
+
* The static values to forward as the runner's `ActionDef.params`: the node's
|
|
19
|
+
* `properties.params` when it is an object bag, otherwise `undefined`.
|
|
20
|
+
*
|
|
21
|
+
* A node-level OBJECT `params` that is not the hoisted `properties.params` is
|
|
22
|
+
* never returned; it triggers the development warning above.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readStaticParamValues(subject: StaticParamsSubject, where: string): Record<string, unknown> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Evaluates a config bag the way `SchemaRenderer` evaluates `properties`: the
|
|
27
|
+
* function `useConfigBagEvaluator()` (`@object-ui/react`) returns.
|
|
28
|
+
*/
|
|
29
|
+
export type ConfigBagEvaluator = (bag: unknown) => unknown;
|
|
30
|
+
/**
|
|
31
|
+
* A container MEMBER with its `properties` evaluated (objectui#10290).
|
|
32
|
+
*
|
|
33
|
+
* `action:bar`, `action:group` and `action:menu` draw their member actions
|
|
34
|
+
* themselves, so a member never passes through the `SchemaRenderer` memo that
|
|
35
|
+
* evaluates a node's `properties`. Its static values ride `properties.params`
|
|
36
|
+
* (objectui#10289), and they are templates (objectui#7867): on a record page,
|
|
37
|
+
* `"recordId": "${record.id}"` has to reach the handler as the record's id. The
|
|
38
|
+
* container evaluates the member's `properties` with the memo's own evaluator
|
|
39
|
+
* and scope (`evaluateBag`, from `useConfigBagEvaluator()`), once, where it
|
|
40
|
+
* composes or runs the member.
|
|
41
|
+
*
|
|
42
|
+
* Returns the member itself when it carries no `properties` bag. Only
|
|
43
|
+
* `properties` is evaluated. It is not copied onto the member (the memo's
|
|
44
|
+
* hoist), and the member's other keys are left as authored.
|
|
45
|
+
*/
|
|
46
|
+
export declare function withEvaluatedProperties<T extends object>(member: T, evaluateBag: ConfigBagEvaluator): T;
|
|
47
|
+
/**
|
|
48
|
+
* A container member's static values (objectui#10290): its `properties.params`,
|
|
49
|
+
* evaluated through {@link withEvaluatedProperties}, or `undefined`.
|
|
50
|
+
*
|
|
51
|
+
* For `action:group` / `action:menu` items, which run the member themselves.
|
|
52
|
+
* (`action:bar` hands an inline member to `action:button` / `action:icon` with
|
|
53
|
+
* its `properties` already evaluated, and `readStaticParamValues` reads it
|
|
54
|
+
* there.)
|
|
55
|
+
*/
|
|
56
|
+
export declare function readMemberStaticParamValues(member: StaticParamsSubject, evaluateBag: ConfigBagEvaluator): Record<string, unknown> | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The same ruling on a spec ACTION ENTRY (objectui#10462): `element:button`'s
|
|
59
|
+
* inline `action`, `action:group` / `action:menu` items and `page:header`'s
|
|
60
|
+
* actions. `params` is an entry's `ActionParam[]` input list and nothing else.
|
|
61
|
+
* (An `action:group` / `action:menu` item is also a container member, so its
|
|
62
|
+
* static values are read from `properties.params` and evaluated by the
|
|
63
|
+
* container through {@link readMemberStaticParamValues} (objectui#10290).
|
|
64
|
+
* `UIActionSchema` declares no `properties`, so on a container member this bag
|
|
65
|
+
* is read off the authored object, not off a declared key. `element:button`'s
|
|
66
|
+
* inline `action` and `page:header`'s actions do not read
|
|
67
|
+
* `properties.params`.)
|
|
68
|
+
*
|
|
69
|
+
* One type is still different: `api`. The objectstack#5777 window keeps the
|
|
70
|
+
* runner reading an object `params` as the request payload (with its own
|
|
71
|
+
* development warning, naming `bodyExtra`) until 18, so for `api` the object is
|
|
72
|
+
* returned unchanged. For every other type it is not returned, and a
|
|
73
|
+
* development build says so once per action.
|
|
74
|
+
*
|
|
75
|
+
* `type` is the executor the surface dispatches the entry as. Call this on the
|
|
76
|
+
* non-array branch only; an array `params` is the input list, forwarded as
|
|
77
|
+
* `actionParams`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readActionEntryParamValues(entry: StaticParamsSubject, type: unknown, where: string): Record<string, unknown> | undefined;
|
|
@@ -34,9 +34,13 @@
|
|
|
34
34
|
* scope (objectui#3848).
|
|
35
35
|
*
|
|
36
36
|
* The ruling: a gate is declared when normalization still leaves a condition to
|
|
37
|
-
* evaluate. `''`, a whitespace-only string
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* evaluate. `''`, a whitespace-only string and an empty-`source` envelope are
|
|
38
|
+
* therefore NOT declared, and one definition in core says so for the
|
|
39
|
+
* renderers, `SchemaRenderer` and the execution entry alike. (A non-predicate
|
|
40
|
+
* value — `0`, `{}`, an array, an `ast`-only envelope — was in that list until
|
|
41
|
+
* objectui#11358, which made it a DECLARED gate that cannot be evaluated: the
|
|
42
|
+
* normalizer keeps it, the evaluator faults on it, and each leg fails it in
|
|
43
|
+
* its key's fault direction — `visible` hidden, `disabled` greyed out.)
|
|
40
44
|
*
|
|
41
45
|
* The NAME is kept (`hasDeclaredVisibilityGate`) and this module keeps
|
|
42
46
|
* re-exporting it: the five member-action call sites — `action:button`,
|
|
@@ -68,6 +72,10 @@
|
|
|
68
72
|
* | `{ source: '' }` | shown → shown | GREY → on | on → on |
|
|
69
73
|
* | `' '` (whitespace) | HIDDEN → shown | on → on | GREY → on |
|
|
70
74
|
* | `0` / `{}` (not a predicate) | shown → shown | GREY → on | on → on |
|
|
75
|
+
*
|
|
76
|
+
* (The table is objectui#3850's measurement. Since objectui#11358 the `0` /
|
|
77
|
+
* `{}` row reads HIDDEN / GREY / on: declared and faulting, each leg's fault
|
|
78
|
+
* direction — pinned in `__tests__/action-empty-predicate-scope.test.tsx`.)
|
|
71
79
|
* | `true` / `false` / CEL / `${…}`| unchanged | unchanged | unchanged |
|
|
72
80
|
*
|
|
73
81
|
* The whitespace row is the one that moves on `visible`, and it moves toward the
|
|
@@ -15,12 +15,15 @@
|
|
|
15
15
|
* - element:divider (no-props separator)
|
|
16
16
|
* - ElementButtonProps -> element:button
|
|
17
17
|
*
|
|
18
|
-
* Heavier interactive elements (element:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Heavier interactive elements (element:form) live in their owning plugins and
|
|
19
|
+
* are left to those packages. element:record_picker — which writes its
|
|
20
|
+
* selection into a page variable — ships alongside in `./record-picker`.
|
|
21
21
|
*
|
|
22
22
|
* All props are read off `schema.properties` per the spec's
|
|
23
23
|
* `UIComponent.properties` convention; `schema.props` is also accepted
|
|
24
24
|
* as a fallback so authors transitioning between conventions keep working.
|
|
25
|
+
* `element:number` also reads the node-level `dataSource` binding: its
|
|
26
|
+
* `object` wins over the flat one, and its filter is AND-combined with the
|
|
27
|
+
* flat one (objectui#10909).
|
|
25
28
|
*/
|
|
26
29
|
export {};
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Safe native HTML element passthrough renderers (ADR: kind:'html').
|
|
9
9
|
*
|
|
10
|
-
* A `kind:'html'` page is authored as a constrained JSX
|
|
10
|
+
* A `kind:'html'` page is authored as a constrained JSX string that is
|
|
11
11
|
* PARSED (never executed) into the SDUI tree. For that tier to live up to its
|
|
12
12
|
* name, the everyday HTML tags an author reaches for — headings, paragraphs,
|
|
13
13
|
* lists, links, images, emphasis — must each resolve to a renderer (otherwise
|
|
14
|
-
* the parser flags them `unknown-component`). div / span / table /
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* the parser flags them `unknown-component`). div / span / table / label and
|
|
15
|
+
* the semantic sectioning tags are registered elsewhere; this module fills in
|
|
16
|
+
* the rest of the safe flow/inline set — `code` included (objectui#10756): the
|
|
17
|
+
* bare `code` key used to be the `field:code` widget's namespace fallback, so
|
|
18
|
+
* `<code>inline</code>` on an html page drew a code EDITOR and dropped its
|
|
19
|
+
* text. The widget now stands down from the bare key (`FIELD_TYPES_SKIP_FALLBACK`
|
|
20
|
+
* in `@object-ui/fields`) and stays reachable as `field:code`.
|
|
17
21
|
*
|
|
18
22
|
* Safety: these render real DOM, but the html tier never executes JS, so props
|
|
19
23
|
* are static literals from the parser. As defense-in-depth the passthrough
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The config bag an `element:*` renderer reads — THE one definition of it in
|
|
10
|
+
* `@object-ui/components` (objectui#6783).
|
|
11
|
+
*
|
|
12
|
+
* Per spec, element components carry their config in `schema.properties`;
|
|
13
|
+
* `schema.props` is tolerated as the legacy alias so JSON written either way
|
|
14
|
+
* works. `properties` wins a contested key — the 2026-08-18 cross-channel
|
|
15
|
+
* ruling (objectui#5123), whose spread order is preserved here verbatim and
|
|
16
|
+
* pinned by `packages/components/src/__tests__/alias-precedence-cross-channel.test.tsx`.
|
|
17
|
+
*
|
|
18
|
+
* ## The third channel (objectui#6783)
|
|
19
|
+
*
|
|
20
|
+
* This body used to be copied into five renderer modules — `elements.tsx`,
|
|
21
|
+
* `data-list.tsx`, `text-input.tsx`, `record-picker.tsx`,
|
|
22
|
+
* `metadata-viewer.tsx` — each spelling the two halves as
|
|
23
|
+
* `(schema?.properties ?? {}) as T`. `??` only replaces `null`/`undefined`, so
|
|
24
|
+
* a DEGENERATE bag (a string, a number, an array) went straight into the
|
|
25
|
+
* object spread and was re-read as its own indexed keys: for
|
|
26
|
+
* `properties: 'not-a-bag'` the bag a renderer received was
|
|
27
|
+
* `{ '0': 'n', '1': 'o', … '8': 'g' }` — nine keys nobody authored.
|
|
28
|
+
*
|
|
29
|
+
* That is the same hazard objectui#6752 and objectui#6760 closed in
|
|
30
|
+
* `packages/react/src/SchemaRenderer.tsx` on the two channels UPSTREAM of this
|
|
31
|
+
* one (the `props` evaluation memo plus `propsWithoutCanonicalKeys`, and the
|
|
32
|
+
* `properties` hoist). Those guards do not reach here, and are not meant to:
|
|
33
|
+
* what they buy is that the AUTHORED value's shape survives into the node, so
|
|
34
|
+
* a renderer downstream still receives `schema.properties === 'not-a-bag'` and
|
|
35
|
+
* gets to answer the question for itself. The three channels are in SERIES.
|
|
36
|
+
*
|
|
37
|
+
* ## Why it asks `isConfigBag` rather than carrying its own predicate
|
|
38
|
+
*
|
|
39
|
+
* Because a copy that DRIFTS produces no error — each spelling is a boolean
|
|
40
|
+
* expression, so two channels answering "is this a real config bag?"
|
|
41
|
+
* differently is silent by construction. objectui#6761 ended that inside
|
|
42
|
+
* `@object-ui/react` by converging six occurrences behind one exported
|
|
43
|
+
* definition and pinning it (`utils/configBag.pin.test.ts` fails on a seventh
|
|
44
|
+
* spelling); `@object-ui/components` depends on `@object-ui/react` — every one
|
|
45
|
+
* of the five modules above already imports from it — so the reachable answer
|
|
46
|
+
* here is to READ that definition, not to write the seventh spelling one
|
|
47
|
+
* package over where its pin cannot see it.
|
|
48
|
+
*
|
|
49
|
+
* ## What the guard buys, measured
|
|
50
|
+
*
|
|
51
|
+
* Not a rendered pixel, on today's tree: all five renderers read NAMED keys
|
|
52
|
+
* off this bag, and the one onward spread (`metadata-viewer`'s
|
|
53
|
+
* `<StateMachineView {...props} />`) hands them to components that destructure
|
|
54
|
+
* named fields, so the indexed keys were computed and then discarded. What it
|
|
55
|
+
* buys is the same thing objectui#6752 measured its own guard buys — the
|
|
56
|
+
* authored value's shape is not reinterpreted — one channel further down,
|
|
57
|
+
* which is exactly the property that stops mattering only until the first of
|
|
58
|
+
* these five spreads its bag onto a DOM element.
|
|
59
|
+
*/
|
|
60
|
+
export declare function readProps<T extends Record<string, any>>(schema: any): T;
|
|
@@ -40,26 +40,43 @@ type BuiltinRowPredicates = NonNullable<DataTableSchema['rowEditPredicates'] | D
|
|
|
40
40
|
export declare function isBuiltinRowActionVisible(predicates: Pick<BuiltinRowPredicates, 'visibleWhen'> | undefined, name: 'edit' | 'delete', row: any, scope: Record<string, unknown>): boolean;
|
|
41
41
|
/**
|
|
42
42
|
* Does this schema-driven custom row action render for THIS row? Same
|
|
43
|
-
* single-definition rule as the built-ins above
|
|
43
|
+
* single-definition rule as the built-ins above, and the ONE definition for
|
|
44
|
+
* every row menu: this file's (the related list's data table) and
|
|
45
|
+
* plugin-grid's `RowActionMenu` (its "⋮" items, its inline primary buttons and
|
|
46
|
+
* its "⋮" guard), which imports it from the `@object-ui/components` barrel
|
|
47
|
+
* rather than keeping a twin (objectui#11294).
|
|
44
48
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* rendered the item for everyone, and counted toward the "⋮" guard. This is the
|
|
50
|
-
* invariant objectui#3492 established for the selection bar (plugin-grid's
|
|
51
|
-
* `hasVisibilityGate`), and the same `!= null` posture the built-in `visibleWhen`
|
|
52
|
-
* gate above has always had. The boolean then decides in
|
|
53
|
-
* {@link evalRowActionVisibility}, which short-circuits it instead of handing it
|
|
54
|
-
* to the engine.
|
|
49
|
+
* Whether a gate is DECLARED at all is asked with `hasDeclaredVisibilityGate`,
|
|
50
|
+
* the action family's one definition (objectui#3812): the related list's
|
|
51
|
+
* toolbar, `action:*`, `DeclaredActionsBar` and the rest ask it too, so an
|
|
52
|
+
* action cannot show in a list's header and be missing from its rows.
|
|
55
53
|
*
|
|
56
|
-
* `
|
|
57
|
-
*
|
|
54
|
+
* - `visible: false` is a declared gate that excludes every row. Truthiness
|
|
55
|
+
* cannot answer the question: testing `!action.visible` read it as ungated
|
|
56
|
+
* and rendered the item for everyone (objectui#3758). The boolean then
|
|
57
|
+
* decides in {@link evalRowActionVisibility}, which short-circuits it instead
|
|
58
|
+
* of handing it to the engine.
|
|
59
|
+
* - A blank predicate is no gate, so the action shows: `''`, a whitespace-only
|
|
60
|
+
* string, and an envelope whose `source` is blank. The blank is still
|
|
61
|
+
* reported (ADR-0137 D4) through the family's one `[blank]` report, once per
|
|
62
|
+
* blank spelling rather than once per row. This test used to be
|
|
63
|
+
* `pred == null || pred === ''`, so a whitespace-only `visible` counted as
|
|
64
|
+
* declared, was evaluated, and failed closed: the action showed in a related
|
|
65
|
+
* list's toolbar and was missing from its rows (objectui#11294).
|
|
66
|
+
* - A value that is not a predicate at all (`0`, `{}`, an array, an
|
|
67
|
+
* `ast`-only envelope) IS a declared gate, one that cannot be evaluated
|
|
68
|
+
* (objectui#11358): the family's definition answers "declared", the row fold
|
|
69
|
+
* faults on it and fails closed, so the action is hidden and the fault is
|
|
70
|
+
* reported — as on every other member of the family.
|
|
71
|
+
*
|
|
72
|
+
* `fields` is the object's field definitions, for a caller that has them:
|
|
73
|
+
* plugin-grid passes them so a relation compares as its stored foreign key
|
|
74
|
+
* (see `evalRowPredicate`). This file's own callers pass none.
|
|
58
75
|
*/
|
|
59
76
|
export declare function isCustomRowActionVisible(action: {
|
|
60
77
|
name?: string;
|
|
61
78
|
visible?: unknown;
|
|
62
|
-
} | undefined, row: any, scope: Record<string, unknown
|
|
79
|
+
} | undefined, row: any, scope: Record<string, unknown>, fields?: unknown): boolean;
|
|
63
80
|
/** What the row overflow menu will actually render for ONE row. */
|
|
64
81
|
export interface DataTableRowMenuPlan {
|
|
65
82
|
/** The built-in Edit item renders for this row. */
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The diagnostics that say out loud what `data-table` has always done silently
|
|
10
|
+
* with a key the AUTHOR wrote.
|
|
11
|
+
*
|
|
12
|
+
* TWO questions, one channel. The file is named for the first because it came
|
|
13
|
+
* first (objectui#6575); it was never limited to it.
|
|
14
|
+
*
|
|
15
|
+
* 1. `bind` was authored — and `data-table` does not read `bind` at all
|
|
16
|
+
* (objectui#6575, maintainer ruling 2026-08-27, option A: 「同意」).
|
|
17
|
+
* 2. `data` was authored and is NOT an array — so the renderer dropped it to
|
|
18
|
+
* zero rows at `data-table.tsx`'s
|
|
19
|
+
* `Array.isArray(rawData) ? rawData : EMPTY_ROWS` (objectui#6665).
|
|
20
|
+
*
|
|
21
|
+
* They are deliberately two predicates asking two questions, not one widened
|
|
22
|
+
* predicate. The nodes that trip #2 carry no `bind` AT ALL, so #6575 staying
|
|
23
|
+
* silent on them is correct behaviour rather than a gap — the triage ruling on
|
|
24
|
+
* objectui#6665 is explicit about that, and widening #6575's question would
|
|
25
|
+
* have made its silence look like the defect. See {@link describeNonArrayData}.
|
|
26
|
+
*
|
|
27
|
+
* ## The first defect this names
|
|
28
|
+
*
|
|
29
|
+
* `bind` is the data-scope binding vocabulary: a path string resolved by
|
|
30
|
+
* `useDataScope()`. `list`, `tree-view` and the `object-*` plugin widgets read
|
|
31
|
+
* it. `DataTableRenderer` does NOT — it takes its rows from `data: rawData =
|
|
32
|
+
* EMPTY_ROWS` off the node, and the file calls no such hook.
|
|
33
|
+
*
|
|
34
|
+
* A `bind` on a `data-table` is nevertheless accepted by every gate: the TS
|
|
35
|
+
* side by `BaseSchema`'s `[key: string]: any`, the zod side by `BaseSchema`
|
|
36
|
+
* being `.passthrough()` (which `DataTableSchema.extend(…)` inherits). So the
|
|
37
|
+
* author gets a table drawing a correct-looking header over the "No results
|
|
38
|
+
* found" empty state, with no error, no warning and no diagnostic — the
|
|
39
|
+
* hardest failure shape for a human OR an AI author to self-check, because a
|
|
40
|
+
* rendered header reads as a success receipt.
|
|
41
|
+
*
|
|
42
|
+
* The platform was already paying a teaching cost for it rather than a
|
|
43
|
+
* diagnostic cost: `skills/objectui/rules/protocol.md` documents the pothole
|
|
44
|
+
* verbatim, and `skill-guide-data-table-binding.test.tsx` pins the behaviour.
|
|
45
|
+
* This module moves the warning from the docs to the console, where the author
|
|
46
|
+
* who did not read the docs is standing.
|
|
47
|
+
*
|
|
48
|
+
* ## What it deliberately does NOT do
|
|
49
|
+
*
|
|
50
|
+
* It changes NO behaviour. `data-table` still does not read `bind`, and the
|
|
51
|
+
* ruling is explicit that it must not start: making it a `useDataScope` reader
|
|
52
|
+
* (option B) is a separate published-surface question needing its own ruling.
|
|
53
|
+
* Refusing the key at parse (option C) stays blocked on the `.passthrough()`
|
|
54
|
+
* ceiling (objectui#5155 / objectui#6269). So the trap stops being silent; it
|
|
55
|
+
* does not stop being a trap.
|
|
56
|
+
*
|
|
57
|
+
* ⭐ The `data`-vs-`bind` PRECEDENCE half of that open question DISSOLVED at
|
|
58
|
+
* objectui#9308 rather than being answered. While `useDataScope` walked the
|
|
59
|
+
* injected adapter, `bind` and the node's own `data` were two spellings of one
|
|
60
|
+
* idea — "where this table's rows come from" — and a reader of both would have
|
|
61
|
+
* needed a rule. Since that ruling `bind` resolves a path in the ambient
|
|
62
|
+
* predicate scope and `data` is an inline array on the node: different
|
|
63
|
+
* channels, different questions, no precedence to decide. objectui#6575's
|
|
64
|
+
* ruling that `data-table` must not read `bind` is untouched by that, and this
|
|
65
|
+
* diagnostic's job is unchanged.
|
|
66
|
+
*
|
|
67
|
+
* ## Why a console warning, and only a console warning
|
|
68
|
+
*
|
|
69
|
+
* The same channel `plugin-grid`'s `columnSpellingDiagnostics.ts` uses for the
|
|
70
|
+
* identical shape of failure ("you declared something and the renderer dropped
|
|
71
|
+
* it"): a pure `describe…` function returning `string | null`, called from a
|
|
72
|
+
* `useEffect` keyed on the schema slice, one `console.warn`, no NODE_ENV
|
|
73
|
+
* branch. Deliberately the SAME shape rather than a second, differently-shaped
|
|
74
|
+
* one next to it. Rendering an in-table message instead would be user-facing
|
|
75
|
+
* copy needing all ten locale packs in `@object-ui/i18n`; a throw would take
|
|
76
|
+
* the surrounding page down for a defect that costs one table.
|
|
77
|
+
*
|
|
78
|
+
* The rate limit is the `useEffect` key, and that is enough HERE for a reason
|
|
79
|
+
* `visibilityDiagnostic.ts` does not have available: a node gate is evaluated
|
|
80
|
+
* once per row, so it needs a module-level dedupe `Set` to keep one authoring
|
|
81
|
+
* bug from printing N lines. A `data-table` is one node rendered once — the
|
|
82
|
+
* effect key is one line per mount per distinct `bind`, which is already the
|
|
83
|
+
* "one line per distinct authoring bug" ceiling that `Set` exists to buy.
|
|
84
|
+
*
|
|
85
|
+
* ## The message never asserts something it did not check
|
|
86
|
+
*
|
|
87
|
+
* A node can carry BOTH an inline `data` array and a `bind`. That table is not
|
|
88
|
+
* empty, so the "header over an empty body" consequence would be false there —
|
|
89
|
+
* and a diagnostic that overstates its own consequence teaches authors to
|
|
90
|
+
* distrust it. The two cases get two different consequence clauses, decided by
|
|
91
|
+
* looking at the rows the renderer actually resolved.
|
|
92
|
+
*/
|
|
93
|
+
/** Prefix for every `bind` line — the handle tests and greps hold. */
|
|
94
|
+
export declare const DATA_TABLE_BIND_DIAGNOSTIC_PREFIX = "[ObjectUI] DataTable bind:";
|
|
95
|
+
/** The key `data-table` really reads its rows from. */
|
|
96
|
+
export declare const DECLARED_ROWS_KEY = "data";
|
|
97
|
+
/** Where the offending node lives, for the first line of either message. */
|
|
98
|
+
export interface DataTableNodeAddress {
|
|
99
|
+
/** The schema node's `type` — `data-table`, or an alias that routes here. */
|
|
100
|
+
blockType?: unknown;
|
|
101
|
+
/** The node's `id`, when it has one. */
|
|
102
|
+
id?: unknown;
|
|
103
|
+
/** The table's authored caption — often the only human-readable name. */
|
|
104
|
+
caption?: unknown;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Was a `bind` authored on this node?
|
|
108
|
+
*
|
|
109
|
+
* `undefined` is absence — a destructuring default or an omitted key. Every
|
|
110
|
+
* other value, including `null` and the empty string, is something the author
|
|
111
|
+
* WROTE, and writing it bought nothing. Exported so the renderer's effect key
|
|
112
|
+
* and this judgement cannot drift apart: one predicate, two readers.
|
|
113
|
+
*/
|
|
114
|
+
export declare function hasAuthoredBind(bind: unknown): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* The message for a `data-table` node carrying a `bind`, or `null` when there
|
|
117
|
+
* is nothing to say.
|
|
118
|
+
*
|
|
119
|
+
* `rows` is what the renderer resolved for the body — passed in rather than
|
|
120
|
+
* re-derived, so the consequence sentence is measured against the same array
|
|
121
|
+
* the reader is looking at.
|
|
122
|
+
*
|
|
123
|
+
* Naming the ADDRESS is the point: which node, which path it spells, what the
|
|
124
|
+
* renderer did instead, and what to write to get the rows on screen. A message
|
|
125
|
+
* that only said something went wrong would leave the author where the silence
|
|
126
|
+
* did.
|
|
127
|
+
*/
|
|
128
|
+
export declare function describeIgnoredBind(bind: unknown, rows: unknown, address: DataTableNodeAddress): string | null;
|
|
129
|
+
/** Prefix for every non-array `data` line — the handle tests and greps hold. */
|
|
130
|
+
export declare const DATA_TABLE_DATA_DIAGNOSTIC_PREFIX = "[ObjectUI] DataTable data:";
|
|
131
|
+
/**
|
|
132
|
+
* Was `data` authored as something that is not an array?
|
|
133
|
+
*
|
|
134
|
+
* `undefined` is absence — an omitted key, or the renderer's destructuring
|
|
135
|
+
* default. Everything else the author WROTE, and `data-table.tsx` reduces every
|
|
136
|
+
* non-array value of it to `EMPTY_ROWS` without a word.
|
|
137
|
+
*
|
|
138
|
+
* ## Why this is not {@link hasAuthoredBind}'s question
|
|
139
|
+
*
|
|
140
|
+
* That one asks "was a `bind` authored?" — a key `data-table` does not read at
|
|
141
|
+
* all. This asks about `data`, the key it genuinely does read, and asks a
|
|
142
|
+
* SHAPE question about it rather than a presence one. The nodes that trip this
|
|
143
|
+
* carry no `bind`, so #6575's predicate is correctly silent on them; that
|
|
144
|
+
* silence is precisely why a second question had to be asked here instead of
|
|
145
|
+
* the first one being widened.
|
|
146
|
+
*
|
|
147
|
+
* ## Why the predicate is not keyed on the `${...}` shape
|
|
148
|
+
*
|
|
149
|
+
* The expression string is the spelling that was REPORTED, but it is not the
|
|
150
|
+
* defect. `Array.isArray(rawData) ? rawData : EMPTY_ROWS` swallows a number, an
|
|
151
|
+
* object, a `null` and a plain string exactly as silently, so a predicate that
|
|
152
|
+
* only caught `${...}` would leave the next non-array spelling to arrive as a
|
|
153
|
+
* fresh card. The shape only chooses the wording (see {@link TEMPLATE_SHAPE}).
|
|
154
|
+
*
|
|
155
|
+
* Exported so the renderer and this judgement cannot drift apart: one
|
|
156
|
+
* predicate, two readers.
|
|
157
|
+
*/
|
|
158
|
+
export declare function hasNonArrayAuthoredData(rawData: unknown): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* The message for a `data-table` node whose `data` is not an array, or `null`
|
|
161
|
+
* when there is nothing to say.
|
|
162
|
+
*
|
|
163
|
+
* ## Why no measured-consequence clause, unlike {@link describeIgnoredBind}
|
|
164
|
+
*
|
|
165
|
+
* That one has to look at the rows the renderer resolved, because a node can
|
|
166
|
+
* carry BOTH a `bind` and a real inline `data` array — "header over an empty
|
|
167
|
+
* body" would be false there. Here it cannot be: `data` is the ONLY row source
|
|
168
|
+
* `data-table` has, and this function is called exactly when that source is not
|
|
169
|
+
* an array, so the body is `EMPTY_ROWS` by construction. The consequence is
|
|
170
|
+
* stated because the predicate already established it, not asserted on faith.
|
|
171
|
+
*
|
|
172
|
+
* ## What it deliberately does NOT say
|
|
173
|
+
*
|
|
174
|
+
* It does not tell the author to move the expression under `properties`. The
|
|
175
|
+
* same expression IS evaluated there — that contrast is what makes this a
|
|
176
|
+
* defect rather than a design — but whether `properties` is an authoring
|
|
177
|
+
* channel for the `ui:*` / `page:*` namespaces is an open contract question,
|
|
178
|
+
* recorded in `skills/objectui/rules/protocol.md` rather than recommended by
|
|
179
|
+
* it. A console line is the wrong place to settle that, so the message teaches
|
|
180
|
+
* the one route the guides do teach: the host resolves the rows.
|
|
181
|
+
*
|
|
182
|
+
* It also changes NO behaviour. Making node-level `data` evaluate expressions
|
|
183
|
+
* is a behaviour change on a published component; the objectui#6665 triage
|
|
184
|
+
* ruling put that arm on the maintainer floor and dispatched only this one.
|
|
185
|
+
* The trap stops being silent; it does not stop being a trap.
|
|
186
|
+
*/
|
|
187
|
+
export declare function describeNonArrayData(rawData: unknown, address: DataTableNodeAddress): string | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The validation-rule names react-hook-form actually RUNS (objectui#5099).
|
|
10
|
+
*
|
|
11
|
+
* react-hook-form's field validator destructures a FIXED key set off the
|
|
12
|
+
* field descriptor and silently drops every other key. From the installed
|
|
13
|
+
* 7.85.0 bundle (`dist/index.cjs.js`, minified):
|
|
14
|
+
*
|
|
15
|
+
* const{ref:c,refs:f,required:m,maxLength:y,minLength:p,min:g,max:b,
|
|
16
|
+
* pattern:V,validate:A,name:x,valueAsNumber:S,mount:k}=e._f
|
|
17
|
+
*
|
|
18
|
+
* Of those twelve, seven are authorable validation rules — the rest are
|
|
19
|
+
* field-descriptor internals react-hook-form writes itself and an author
|
|
20
|
+
* never supplies through `FieldValidationRules`. Any rule name outside the
|
|
21
|
+
* seven — a misspelled `minlength`, an invented `email`, the numeric keys
|
|
22
|
+
* left by spreading an ARRAY into `validation` — is dropped without a trace:
|
|
23
|
+
* the field renders, the form looks validated, and the rule never runs. The
|
|
24
|
+
* form renderer reports such names loudly (dev-time `console.error`; see
|
|
25
|
+
* `form.tsx`).
|
|
26
|
+
*
|
|
27
|
+
* Both lists are PINNED against the installed react-hook-form bundle by
|
|
28
|
+
* `__tests__/rhf-recognized-rule-keys.test.ts`, which re-derives the
|
|
29
|
+
* destructured set from `dist/index.cjs.js` at test time. A react-hook-form
|
|
30
|
+
* bump that changes the set turns that test red — forcing a human to re-sort
|
|
31
|
+
* the new key into one of these two lists — instead of silently rotting the
|
|
32
|
+
* diagnostic.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Keys react-hook-form's validator destructures that are NOT authorable
|
|
36
|
+
* rules: descriptor internals the library maintains itself.
|
|
37
|
+
*/
|
|
38
|
+
export declare const RHF_DESCRIPTOR_NON_RULE_KEYS: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* The authorable rule names react-hook-form's validator reads. Exactly the
|
|
41
|
+
* declared key set of `FieldValidationRules` in `@object-ui/types` —
|
|
42
|
+
* declared = enforced; anything else in a field's `validation` object is a
|
|
43
|
+
* bug in the metadata, not a rule.
|
|
44
|
+
*/
|
|
45
|
+
export declare const RHF_RECOGNIZED_RULE_KEYS: ReadonlySet<string>;
|
|
@@ -17,13 +17,4 @@
|
|
|
17
17
|
* - PageContainerProps -> page:footer / page:sidebar / page:section
|
|
18
18
|
* (the thin wrappers; one shared `children` slot)
|
|
19
19
|
*/
|
|
20
|
-
|
|
21
|
-
* Strip dangling connectors that survive when a `titleFormat` interpolates
|
|
22
|
-
* with one side empty — e.g. `{number} - {name}` becomes `CTR-0001 -` when
|
|
23
|
-
* `name` is blank. Removes a trailing/leading hyphen / middle-dot / colon /
|
|
24
|
-
* slash / pipe (optionally surrounded by whitespace) and collapses
|
|
25
|
-
* adjacent whitespace into a single space. Idempotent.
|
|
26
|
-
*
|
|
27
|
-
* Exported for unit tests.
|
|
28
|
-
*/
|
|
29
|
-
export declare function cleanupTitleSeparators(s: string): string;
|
|
20
|
+
export {};
|