@object-ui/react 17.6.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 +3766 -0
- package/README.md +294 -38
- package/dist/SchemaRenderer.d.ts +38 -7
- package/dist/SchemaRenderer.js +1218 -87
- package/dist/context/ActionContext.js +40 -1
- package/dist/context/DiscussionContext.d.ts +12 -0
- package/dist/context/DiscussionContext.js +2 -0
- package/dist/context/DrillNavigationContext.d.ts +13 -0
- package/dist/context/InlineEditContext.d.ts +81 -1
- package/dist/context/InlineEditContext.js +8 -2
- package/dist/context/RecordContext.d.ts +48 -5
- package/dist/context/RecordContext.js +10 -1
- package/dist/context/RelatedRecordActionsContext.d.ts +5 -1
- package/dist/context/SchemaRendererContext.d.ts +54 -2
- package/dist/context/SchemaRendererContext.js +38 -5
- package/dist/context/ThemeContext.js +1 -1
- package/dist/context/actionRunnerTranslator.d.ts +27 -0
- package/dist/context/actionRunnerTranslator.js +37 -0
- package/dist/element-data-source/ElementDataSourceGate.d.ts +88 -1
- package/dist/element-data-source/ElementDataSourceGate.js +326 -13
- package/dist/hooks/index.d.ts +3 -1
- package/dist/hooks/index.js +10 -1
- package/dist/hooks/useActionEngine.js +46 -3
- package/dist/hooks/useActionRunner.js +4 -0
- package/dist/hooks/useActionTextLocalizer.d.ts +21 -8
- package/dist/hooks/useActionTextLocalizer.js +33 -8
- package/dist/hooks/useConfigBagEvaluator.d.ts +30 -0
- package/dist/hooks/useConfigBagEvaluator.js +39 -0
- package/dist/hooks/useDensityMode.d.ts +6 -2
- package/dist/hooks/useDensityMode.js +6 -2
- package/dist/hooks/useDiscovery.d.ts +1 -10
- package/dist/hooks/useETagCache.js +25 -4
- package/dist/hooks/useElementDataSource.d.ts +15 -2
- package/dist/hooks/useElementDataSource.js +24 -7
- package/dist/hooks/useExpression.d.ts +52 -59
- package/dist/hooks/useExpression.js +58 -90
- package/dist/hooks/useFilterScope.d.ts +22 -2
- package/dist/hooks/useFilterScope.js +29 -4
- package/dist/hooks/useGlobalUndo.d.ts +10 -0
- package/dist/hooks/useGlobalUndo.js +112 -46
- package/dist/hooks/useNavigationOverlay.d.ts +76 -11
- package/dist/hooks/useNavigationOverlay.js +119 -9
- package/dist/hooks/useOffline.js +65 -11
- package/dist/hooks/usePageAssignment.d.ts +8 -5
- package/dist/hooks/usePageAssignment.js +37 -16
- package/dist/hooks/useRecordSearch.d.ts +34 -1
- package/dist/hooks/useRecordSearch.js +42 -7
- package/dist/hooks/useResolvedFilter.d.ts +46 -0
- package/dist/hooks/useResolvedFilter.js +130 -0
- package/dist/hooks/useSchemaPersistence.d.ts +7 -0
- package/dist/hooks/useSchemaPersistence.js +144 -5
- package/dist/hooks/useSettledSchema.d.ts +123 -0
- package/dist/hooks/useSettledSchema.js +186 -0
- package/dist/hooks/useViewData.d.ts +7 -2
- package/dist/index.d.ts +8 -1
- package/dist/index.js +45 -1
- package/dist/schema-input.d.ts +38 -8
- package/dist/schema-input.js +39 -7
- package/dist/utils/configBag.d.ts +68 -0
- package/dist/utils/configBag.js +70 -0
- package/dist/utils/error-message.d.ts +1 -1
- package/dist/utils/error-message.js +1 -1
- package/dist/utils/inlineAria.d.ts +61 -0
- package/dist/utils/inlineAria.js +70 -0
- package/dist/utils/nonGridRowCeiling.d.ts +44 -0
- package/dist/utils/nonGridRowCeiling.js +78 -0
- package/dist/utils/paramsBag.d.ts +28 -0
- package/dist/utils/paramsBag.js +146 -0
- package/dist/utils/propsBagDiagnostic.d.ts +196 -0
- package/dist/utils/propsBagDiagnostic.js +421 -0
- package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
- package/dist/utils/refusedDataPropDiagnostic.js +94 -0
- package/dist/utils/unevaluatedExpression.d.ts +42 -1
- package/dist/utils/unevaluatedExpression.js +74 -11
- package/dist/utils/visibilityDiagnostic.d.ts +271 -19
- package/dist/utils/visibilityDiagnostic.js +311 -44
- package/package.json +10 -10
- package/dist/hooks/useClientNotifications.d.ts +0 -47
- package/dist/hooks/useClientNotifications.js +0 -162
- package/dist/spec-bridge/SpecBridge.d.ts +0 -28
- package/dist/spec-bridge/SpecBridge.js +0 -56
- package/dist/spec-bridge/bridges/form-view.d.ts +0 -158
- package/dist/spec-bridge/bridges/form-view.js +0 -170
- package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
- package/dist/spec-bridge/bridges/list-view.js +0 -268
- package/dist/spec-bridge/index.d.ts +0 -11
- package/dist/spec-bridge/index.js +0 -10
- package/dist/spec-bridge/types.d.ts +0 -55
- package/dist/spec-bridge/types.js +0 -8
package/dist/index.d.ts
CHANGED
|
@@ -10,10 +10,17 @@ export * from './schema-input.js';
|
|
|
10
10
|
export * from './hooks/index.js';
|
|
11
11
|
export * from './context/index.js';
|
|
12
12
|
export * from './LazyPluginLoader.js';
|
|
13
|
-
export * from './spec-bridge/index.js';
|
|
14
13
|
export * from './data-invalidation.js';
|
|
15
14
|
export * from './element-data-source/ElementDataSourceGate.js';
|
|
16
15
|
export { resolveKeyedI18nLabel } from './utils/i18n.js';
|
|
16
|
+
export { resolveInlineAriaProps } from './utils/inlineAria.js';
|
|
17
|
+
export { isConfigBag } from './utils/configBag.js';
|
|
18
|
+
export { reportUnresolvableVisibilityPredicate, formatUnresolvableVisibilityMessage, UNRESOLVABLE_VISIBILITY_PREFIX, UNRESOLVABLE_ENABLEMENT_PREFIX, __resetVisibilityPredicateWarnings, } from './utils/visibilityDiagnostic.js';
|
|
19
|
+
export type { PredicateScopeTier } from './utils/visibilityDiagnostic.js';
|
|
20
|
+
export type { PredicateGateKind } from './utils/visibilityDiagnostic.js';
|
|
17
21
|
export { extractWriteErrorMessage, isPermissionError, extractFieldErrors, classifyLoadError, declaredUserMessage } from './utils/error-message.js';
|
|
18
22
|
export type { WriteFieldError, LoadErrorKind } from './utils/error-message.js';
|
|
19
23
|
export { I18nProvider, useObjectTranslation, useSafeTranslate, useObjectLabel, useSafeFieldLabel, useI18nContext, createI18n, getDirection, getAvailableLanguages, formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, type I18nConfig, type I18nProviderProps, type TranslationKeys, type DateFormatOptions, type CurrencyFormatOptions, type NumberFormatOptions, } from '@object-ui/i18n';
|
|
24
|
+
export { NON_GRID_ROW_CEILING, applyNonGridRowCeiling } from '@object-ui/core';
|
|
25
|
+
export type { NonGridCeilingResult } from '@object-ui/core';
|
|
26
|
+
export { NonGridRowCeilingNote } from './utils/nonGridRowCeiling.js';
|
package/dist/index.js
CHANGED
|
@@ -10,15 +10,59 @@ export * from './schema-input.js';
|
|
|
10
10
|
export * from './hooks/index.js'; // will be empty for now
|
|
11
11
|
export * from './context/index.js'; // will be empty for now
|
|
12
12
|
export * from './LazyPluginLoader.js';
|
|
13
|
-
export * from './spec-bridge/index.js';
|
|
14
13
|
export * from './data-invalidation.js';
|
|
15
14
|
// PageComponentSchema.dataSource — mapping the spec's per-element data binding
|
|
16
15
|
// onto the schema keys each object-bound block reads (objectstack#6953).
|
|
17
16
|
export * from './element-data-source/ElementDataSourceGate.js';
|
|
18
17
|
// i18n utilities
|
|
19
18
|
export { resolveKeyedI18nLabel } from './utils/i18n.js';
|
|
19
|
+
// The spec's NESTED `aria` bag (`AriaPropsSchema`) → `aria-label` /
|
|
20
|
+
// `aria-describedby` / `role`, with `ariaLabel` resolved as an inline locale
|
|
21
|
+
// map (objectui#11051). Exported for the renderers in `@object-ui/components`,
|
|
22
|
+
// which depends on this package, so the mapping is written once. It is not the
|
|
23
|
+
// flat keyed reader `SchemaRenderer` applies to every node; objectui#4580 Q2-B
|
|
24
|
+
// keeps the two vocabularies apart.
|
|
25
|
+
export { resolveInlineAriaProps } from './utils/inlineAria.js';
|
|
26
|
+
// "Is this a real config bag?" — the ONE definition of that question
|
|
27
|
+
// (objectui#6761), exported at the package entry for the same reason the node-gate
|
|
28
|
+
// reporter above is (objectui#6038): a surface in `@object-ui/components`, which
|
|
29
|
+
// depends on this package, asks it too. `element:*` renderers there read their
|
|
30
|
+
// config out of `{ ...schema.props, ...schema.properties }`, the third channel of
|
|
31
|
+
// the degenerate-bag hazard objectui#6752 / objectui#6760 closed here
|
|
32
|
+
// (objectui#6783). A second copy one package over would be a spelling that
|
|
33
|
+
// `utils/configBag.pin.test.ts` — which scans `packages/react/src` — cannot see,
|
|
34
|
+
// which is the drift that pin exists to stop.
|
|
35
|
+
export { isConfigBag } from './utils/configBag.js';
|
|
36
|
+
// Node-gate predicate diagnostics. Exported at the package entry (objectui#6038)
|
|
37
|
+
// so every surface that evaluates a node `visibleWhen` reports a fault through
|
|
38
|
+
// ONE reporter and ONE dedupe `Set` — `page:tabs` item predicates live in
|
|
39
|
+
// `@object-ui/components`, which depends on this package. A second copy of the
|
|
40
|
+
// reporter would mean a second rate limit, and the same broken predicate would
|
|
41
|
+
// then be entitled to one line per package instead of one line, which is the
|
|
42
|
+
// property the 2026-08-25 ruling asked for.
|
|
43
|
+
export { reportUnresolvableVisibilityPredicate, formatUnresolvableVisibilityMessage, UNRESOLVABLE_VISIBILITY_PREFIX,
|
|
44
|
+
// The ENABLEMENT gate's opening line (objectui#6445). Exported beside its
|
|
45
|
+
// sibling for the reason that one is: an app filtering these out of its
|
|
46
|
+
// console transport filters by the constant, and a second line it cannot
|
|
47
|
+
// name is a second thing to hard-code. Not a second reporter — the emit,
|
|
48
|
+
// the dedupe `Set` and the reset above are still the only ones.
|
|
49
|
+
UNRESOLVABLE_ENABLEMENT_PREFIX, __resetVisibilityPredicateWarnings, } from './utils/visibilityDiagnostic.js';
|
|
20
50
|
// Write-error surfacing utilities (shared by drag-write plugins so a failed
|
|
21
51
|
// PATCH — e.g. an RLS 403 — is never silently swallowed).
|
|
22
52
|
export { extractWriteErrorMessage, isPermissionError, extractFieldErrors, classifyLoadError, declaredUserMessage } from './utils/error-message.js';
|
|
23
53
|
// Built-in i18n support
|
|
24
54
|
export { I18nProvider, useObjectTranslation, useSafeTranslate, useObjectLabel, useSafeFieldLabel, useI18nContext, createI18n, getDirection, getAvailableLanguages, formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, } from '@object-ui/i18n';
|
|
55
|
+
// The platform row ceiling for NON-GRID visualisations — gantt, calendar, map
|
|
56
|
+
// and tree (objectui#7210, maintainer ruling a′). One constant across the
|
|
57
|
+
// four, because a per-plugin copy would be four constants wearing one name,
|
|
58
|
+
// which is the thing the ruling ruled out.
|
|
59
|
+
//
|
|
60
|
+
// HOME: `@object-ui/core`, beside the `extractRecords` it wraps (objectui#7508,
|
|
61
|
+
// maintainer ruling A′). This entry re-exports the three names it published
|
|
62
|
+
// first, the same way it re-exports `@object-ui/i18n` above, and keeps only
|
|
63
|
+
// the React half — `NonGridRowCeilingNote`, which takes the
|
|
64
|
+
// `NonGridCeilingResult` and nothing else. The probe row's `+ 1` lives in
|
|
65
|
+
// core's `nonGridRowCeilingQuery()` and nowhere else; the constant that used
|
|
66
|
+
// to carry it here is deleted, not re-exported.
|
|
67
|
+
export { NON_GRID_ROW_CEILING, applyNonGridRowCeiling } from '@object-ui/core';
|
|
68
|
+
export { NonGridRowCeilingNote } from './utils/nonGridRowCeiling.js';
|
package/dist/schema-input.d.ts
CHANGED
|
@@ -5,23 +5,53 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { SchemaNode } from '@object-ui/types';
|
|
9
9
|
import type { SchemaRendererProps } from './SchemaRenderer.js';
|
|
10
10
|
/**
|
|
11
11
|
* Narrow a loosely-typed metadata node onto {@link SchemaRendererProps.schema}.
|
|
12
12
|
*
|
|
13
13
|
* `@object-ui/types` declares `SchemaNode` as
|
|
14
|
-
* `
|
|
15
|
-
* metadata plumbing (page regions, header-bar actions, detail tabs, view
|
|
14
|
+
* `DeclaredNode | string | number | boolean | null | undefined` (its object
|
|
15
|
+
* arm the declared-node union, objectui#11466), and a lot of metadata plumbing (page regions, header-bar actions, detail tabs, view
|
|
16
16
|
* configs) is typed with it. `SchemaRenderer` deliberately does NOT declare the
|
|
17
17
|
* `number` / `boolean` members — nobody should be invited to author them — so
|
|
18
18
|
* forwarding such a value needs one honest step in between.
|
|
19
19
|
*
|
|
20
20
|
* This is that step, and it is a TOTAL function rather than a cast: it maps the
|
|
21
|
-
* two primitive members onto
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* two primitive members onto whatever `SchemaRenderer` would itself render for
|
|
22
|
+
* them. So it changes no behaviour and tells no lie — the value a caller
|
|
23
|
+
* forwards renders identically whether or not it passes through here.
|
|
24
|
+
*
|
|
25
|
+
* ## Why the mapping has TWO legs and not one
|
|
26
|
+
*
|
|
27
|
+
* Because the renderer's own narrowing has two, in a fixed order: the
|
|
28
|
+
* `!evaluatedSchema` leg returns nothing and runs FIRST, and only below it does
|
|
29
|
+
* the defensive branch return the value's `String` form. So the falsy
|
|
30
|
+
* primitives — `0`, `-0`, `NaN`, `false` — never reach that branch at all; they
|
|
31
|
+
* render as nothing. `!node` is exactly that set for the two members this
|
|
32
|
+
* function converts, so the ternary below IS the renderer's order, restated for
|
|
33
|
+
* a value that has to cross the type boundary as a string.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ The single-leg `String(node)` that stood here until objectui#8908 was the
|
|
36
|
+
* lie this paragraph used to tell. `String(0)` and `String(false)` are the
|
|
37
|
+
* NON-EMPTY strings `'0'` and `'false'`, which are truthy, so they sailed past
|
|
38
|
+
* the renderer's first leg and printed themselves — while the same two values
|
|
39
|
+
* handed to `SchemaRenderer` directly render nothing, pinned since
|
|
40
|
+
* objectui#4548 in `packages/react/src/__tests__/SchemaRenderer.primitiveSchema.test.tsx`.
|
|
41
|
+
* The shipped `empty` renderer, whose `action` slot gates on nullish rather than
|
|
42
|
+
* truthiness, printed a stray `0` for `{ type: 'empty', action: 0 }` because of
|
|
43
|
+
* it. ⛔ Do not "simplify" the two legs back into one.
|
|
44
|
+
*
|
|
45
|
+
* ## Why the falsy leg returns nothing rather than the value itself
|
|
46
|
+
*
|
|
47
|
+
* Passing `0` / `false` through UNCHANGED would widen this function's return
|
|
48
|
+
* type to admit `number` / `boolean`, and that is not a free choice: the return
|
|
49
|
+
* type is pinned exactly equal to `SchemaRendererProps['schema']` by
|
|
50
|
+
* `packages/react/src/__tests__/SchemaRenderer.propsResolution.test.ts`, which
|
|
51
|
+
* also pins that the prop admits NEITHER member (objectui#4548 ruling Q2). A
|
|
52
|
+
* widened return would therefore be a TS2322 at every call site that forwards
|
|
53
|
+
* it straight into `<SchemaRenderer schema={…} />`, which is all of them. The
|
|
54
|
+
* nullish leg is the spelling that keeps the guarantee and the type both.
|
|
25
55
|
*
|
|
26
56
|
* The bridge is PERMANENT — not scaffolding awaiting a merge. The two competing
|
|
27
57
|
* repo-wide `SchemaNode` spellings ARE reconciled (objectui#4580, PR #4608):
|
|
@@ -39,4 +69,4 @@ import type { SchemaRendererProps } from './SchemaRenderer.js';
|
|
|
39
69
|
* PR #4621 routed all five through here. An earlier revision of this paragraph
|
|
40
70
|
* said the reconciliation was still pending and invited exactly that edit.
|
|
41
71
|
*/
|
|
42
|
-
export declare function toRenderableSchema(node:
|
|
72
|
+
export declare function toRenderableSchema(node: SchemaNode): SchemaRendererProps['schema'];
|
package/dist/schema-input.js
CHANGED
|
@@ -9,17 +9,47 @@
|
|
|
9
9
|
* Narrow a loosely-typed metadata node onto {@link SchemaRendererProps.schema}.
|
|
10
10
|
*
|
|
11
11
|
* `@object-ui/types` declares `SchemaNode` as
|
|
12
|
-
* `
|
|
13
|
-
* metadata plumbing (page regions, header-bar actions, detail tabs, view
|
|
12
|
+
* `DeclaredNode | string | number | boolean | null | undefined` (its object
|
|
13
|
+
* arm the declared-node union, objectui#11466), and a lot of metadata plumbing (page regions, header-bar actions, detail tabs, view
|
|
14
14
|
* configs) is typed with it. `SchemaRenderer` deliberately does NOT declare the
|
|
15
15
|
* `number` / `boolean` members — nobody should be invited to author them — so
|
|
16
16
|
* forwarding such a value needs one honest step in between.
|
|
17
17
|
*
|
|
18
18
|
* This is that step, and it is a TOTAL function rather than a cast: it maps the
|
|
19
|
-
* two primitive members onto
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* two primitive members onto whatever `SchemaRenderer` would itself render for
|
|
20
|
+
* them. So it changes no behaviour and tells no lie — the value a caller
|
|
21
|
+
* forwards renders identically whether or not it passes through here.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the mapping has TWO legs and not one
|
|
24
|
+
*
|
|
25
|
+
* Because the renderer's own narrowing has two, in a fixed order: the
|
|
26
|
+
* `!evaluatedSchema` leg returns nothing and runs FIRST, and only below it does
|
|
27
|
+
* the defensive branch return the value's `String` form. So the falsy
|
|
28
|
+
* primitives — `0`, `-0`, `NaN`, `false` — never reach that branch at all; they
|
|
29
|
+
* render as nothing. `!node` is exactly that set for the two members this
|
|
30
|
+
* function converts, so the ternary below IS the renderer's order, restated for
|
|
31
|
+
* a value that has to cross the type boundary as a string.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ The single-leg `String(node)` that stood here until objectui#8908 was the
|
|
34
|
+
* lie this paragraph used to tell. `String(0)` and `String(false)` are the
|
|
35
|
+
* NON-EMPTY strings `'0'` and `'false'`, which are truthy, so they sailed past
|
|
36
|
+
* the renderer's first leg and printed themselves — while the same two values
|
|
37
|
+
* handed to `SchemaRenderer` directly render nothing, pinned since
|
|
38
|
+
* objectui#4548 in `packages/react/src/__tests__/SchemaRenderer.primitiveSchema.test.tsx`.
|
|
39
|
+
* The shipped `empty` renderer, whose `action` slot gates on nullish rather than
|
|
40
|
+
* truthiness, printed a stray `0` for `{ type: 'empty', action: 0 }` because of
|
|
41
|
+
* it. ⛔ Do not "simplify" the two legs back into one.
|
|
42
|
+
*
|
|
43
|
+
* ## Why the falsy leg returns nothing rather than the value itself
|
|
44
|
+
*
|
|
45
|
+
* Passing `0` / `false` through UNCHANGED would widen this function's return
|
|
46
|
+
* type to admit `number` / `boolean`, and that is not a free choice: the return
|
|
47
|
+
* type is pinned exactly equal to `SchemaRendererProps['schema']` by
|
|
48
|
+
* `packages/react/src/__tests__/SchemaRenderer.propsResolution.test.ts`, which
|
|
49
|
+
* also pins that the prop admits NEITHER member (objectui#4548 ruling Q2). A
|
|
50
|
+
* widened return would therefore be a TS2322 at every call site that forwards
|
|
51
|
+
* it straight into `<SchemaRenderer schema={…} />`, which is all of them. The
|
|
52
|
+
* nullish leg is the spelling that keeps the guarantee and the type both.
|
|
23
53
|
*
|
|
24
54
|
* The bridge is PERMANENT — not scaffolding awaiting a merge. The two competing
|
|
25
55
|
* repo-wide `SchemaNode` spellings ARE reconciled (objectui#4580, PR #4608):
|
|
@@ -38,5 +68,7 @@
|
|
|
38
68
|
* said the reconciliation was still pending and invited exactly that edit.
|
|
39
69
|
*/
|
|
40
70
|
export function toRenderableSchema(node) {
|
|
41
|
-
return typeof node === 'number' || typeof node === 'boolean'
|
|
71
|
+
return typeof node === 'number' || typeof node === 'boolean'
|
|
72
|
+
? (node ? String(node) : undefined)
|
|
73
|
+
: node;
|
|
42
74
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
* Is this value a real config bag — an object, and not an array pretending to
|
|
10
|
+
* be one? (objectui#6761)
|
|
11
|
+
*
|
|
12
|
+
* This is the ONE definition of that question in `@object-ui/react`, and it
|
|
13
|
+
* lives here rather than in whichever module happened to need it first,
|
|
14
|
+
* because every module that asks it is a CONSUMER of the same authored shape:
|
|
15
|
+
* the `properties` / `props` bags a node may carry. `typeof null === 'object'`
|
|
16
|
+
* is covered by the truthiness test.
|
|
17
|
+
*
|
|
18
|
+
* ## Why one definition, when all the copies agreed
|
|
19
|
+
*
|
|
20
|
+
* They agreed; that was never the cost. The cost is that disagreement between
|
|
21
|
+
* them produces NO error — each copy is a boolean expression, and a copy that
|
|
22
|
+
* drifts answers a different question on ONE channel while every other channel
|
|
23
|
+
* keeps the old answer. That is the same failure mode `hasDeclaredPredicate`
|
|
24
|
+
* (`@object-ui/core`) was created to end for "is a gate DECLARED?", which had
|
|
25
|
+
* three spellings with three different SCOPES before objectui#3850 —
|
|
26
|
+
* `disabled: null` greying a control out on one path and not another
|
|
27
|
+
* (objectui#3862).
|
|
28
|
+
*
|
|
29
|
+
* ## The spellings this replaces, measured on `b98352a15`
|
|
30
|
+
*
|
|
31
|
+
* Six occurrences, four spellings, all in `packages/react/src`:
|
|
32
|
+
*
|
|
33
|
+
* 1. `SchemaRenderer`'s module-private `isConfigBag` — itself already the
|
|
34
|
+
* merge of the `properties` evaluation guard and `propsWithoutCanonicalKeys`
|
|
35
|
+
* (objectui#6752, which unified those two because it had to touch both,
|
|
36
|
+
* and filed the rest as objectui#6761 rather than smuggling it in);
|
|
37
|
+
* 2. `SchemaRenderer`'s `winningVisibilityKey`, inline, spelled with
|
|
38
|
+
* `!= null` where the others spell truthiness;
|
|
39
|
+
* 3. `utils/propsBagDiagnostic.ts`'s module-private twin (objectui#6708);
|
|
40
|
+
* 4. `utils/unevaluatedExpression.ts`'s `scanBag` early return, spelled
|
|
41
|
+
* NEGATED (objectui#4795);
|
|
42
|
+
* 5. `utils/unevaluatedExpression.ts`'s `hoisted`, in the same file as (4)
|
|
43
|
+
* and spelled positively — this one is not in objectui#6761's inventory
|
|
44
|
+
* of five, which counted the file once; six occurrences is the count
|
|
45
|
+
* measured on this branch's base, and it was six on the card's base
|
|
46
|
+
* (`b76ca6764`) too.
|
|
47
|
+
*
|
|
48
|
+
* `!= null` (2) and truthiness (1, 3, 5) cannot disagree HERE, and the
|
|
49
|
+
* difference is worth naming rather than smoothing over: they part company
|
|
50
|
+
* only on a falsy value, and every falsy value except `document.all` fails
|
|
51
|
+
* `typeof === 'object'` in the very next conjunct. The texts differ; the
|
|
52
|
+
* answers cannot.
|
|
53
|
+
*
|
|
54
|
+
* ## What this is NOT
|
|
55
|
+
*
|
|
56
|
+
* Not a general "is this a plain object?" utility, and deliberately not shared
|
|
57
|
+
* with the two sites that ask the same SHAPE question about a data ROW —
|
|
58
|
+
* `SchemaRenderer`'s `boundRecord` scope entry and
|
|
59
|
+
* `usePredicateRecordContext` (`hooks/useExpression.ts`). Those answer "did a
|
|
60
|
+
* row bind?", and their answer has its own pinned meaning: binding NOTHING
|
|
61
|
+
* rather than an empty row is what keeps a host-supplied `record` from being
|
|
62
|
+
* shadowed. Should "config bag" ever narrow (rejecting a class instance, say),
|
|
63
|
+
* the row sites must NOT follow — merging them would make that a single edit
|
|
64
|
+
* with two rulings behind it. The pin in `configBag.pin.test.ts` names both
|
|
65
|
+
* sites with this reason, so the next reader finds a decision rather than an
|
|
66
|
+
* oversight.
|
|
67
|
+
*/
|
|
68
|
+
export declare function isConfigBag(value: unknown): value is Record<string, unknown>;
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
* Is this value a real config bag — an object, and not an array pretending to
|
|
10
|
+
* be one? (objectui#6761)
|
|
11
|
+
*
|
|
12
|
+
* This is the ONE definition of that question in `@object-ui/react`, and it
|
|
13
|
+
* lives here rather than in whichever module happened to need it first,
|
|
14
|
+
* because every module that asks it is a CONSUMER of the same authored shape:
|
|
15
|
+
* the `properties` / `props` bags a node may carry. `typeof null === 'object'`
|
|
16
|
+
* is covered by the truthiness test.
|
|
17
|
+
*
|
|
18
|
+
* ## Why one definition, when all the copies agreed
|
|
19
|
+
*
|
|
20
|
+
* They agreed; that was never the cost. The cost is that disagreement between
|
|
21
|
+
* them produces NO error — each copy is a boolean expression, and a copy that
|
|
22
|
+
* drifts answers a different question on ONE channel while every other channel
|
|
23
|
+
* keeps the old answer. That is the same failure mode `hasDeclaredPredicate`
|
|
24
|
+
* (`@object-ui/core`) was created to end for "is a gate DECLARED?", which had
|
|
25
|
+
* three spellings with three different SCOPES before objectui#3850 —
|
|
26
|
+
* `disabled: null` greying a control out on one path and not another
|
|
27
|
+
* (objectui#3862).
|
|
28
|
+
*
|
|
29
|
+
* ## The spellings this replaces, measured on `b98352a15`
|
|
30
|
+
*
|
|
31
|
+
* Six occurrences, four spellings, all in `packages/react/src`:
|
|
32
|
+
*
|
|
33
|
+
* 1. `SchemaRenderer`'s module-private `isConfigBag` — itself already the
|
|
34
|
+
* merge of the `properties` evaluation guard and `propsWithoutCanonicalKeys`
|
|
35
|
+
* (objectui#6752, which unified those two because it had to touch both,
|
|
36
|
+
* and filed the rest as objectui#6761 rather than smuggling it in);
|
|
37
|
+
* 2. `SchemaRenderer`'s `winningVisibilityKey`, inline, spelled with
|
|
38
|
+
* `!= null` where the others spell truthiness;
|
|
39
|
+
* 3. `utils/propsBagDiagnostic.ts`'s module-private twin (objectui#6708);
|
|
40
|
+
* 4. `utils/unevaluatedExpression.ts`'s `scanBag` early return, spelled
|
|
41
|
+
* NEGATED (objectui#4795);
|
|
42
|
+
* 5. `utils/unevaluatedExpression.ts`'s `hoisted`, in the same file as (4)
|
|
43
|
+
* and spelled positively — this one is not in objectui#6761's inventory
|
|
44
|
+
* of five, which counted the file once; six occurrences is the count
|
|
45
|
+
* measured on this branch's base, and it was six on the card's base
|
|
46
|
+
* (`b76ca6764`) too.
|
|
47
|
+
*
|
|
48
|
+
* `!= null` (2) and truthiness (1, 3, 5) cannot disagree HERE, and the
|
|
49
|
+
* difference is worth naming rather than smoothing over: they part company
|
|
50
|
+
* only on a falsy value, and every falsy value except `document.all` fails
|
|
51
|
+
* `typeof === 'object'` in the very next conjunct. The texts differ; the
|
|
52
|
+
* answers cannot.
|
|
53
|
+
*
|
|
54
|
+
* ## What this is NOT
|
|
55
|
+
*
|
|
56
|
+
* Not a general "is this a plain object?" utility, and deliberately not shared
|
|
57
|
+
* with the two sites that ask the same SHAPE question about a data ROW —
|
|
58
|
+
* `SchemaRenderer`'s `boundRecord` scope entry and
|
|
59
|
+
* `usePredicateRecordContext` (`hooks/useExpression.ts`). Those answer "did a
|
|
60
|
+
* row bind?", and their answer has its own pinned meaning: binding NOTHING
|
|
61
|
+
* rather than an empty row is what keeps a host-supplied `record` from being
|
|
62
|
+
* shadowed. Should "config bag" ever narrow (rejecting a class instance, say),
|
|
63
|
+
* the row sites must NOT follow — merging them would make that a single edit
|
|
64
|
+
* with two rulings behind it. The pin in `configBag.pin.test.ts` names both
|
|
65
|
+
* sites with this reason, so the next reader finds a decision rather than an
|
|
66
|
+
* oversight.
|
|
67
|
+
*/
|
|
68
|
+
export function isConfigBag(value) {
|
|
69
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
70
|
+
}
|
|
@@ -16,7 +16,7 @@ export declare function extractWriteErrorMessage(err: unknown): string | null;
|
|
|
16
16
|
* The refusal text the PRODUCER explicitly marked as addressed to the end user,
|
|
17
17
|
* or `null` when the refusal carries no marking.
|
|
18
18
|
*
|
|
19
|
-
* `userMessage` is the opt-in channel added by objectstack
|
|
19
|
+
* `userMessage` is the opt-in channel added by objectstack `79c46da90` (maintainer
|
|
20
20
|
* ruling 2026-08-19 on objectui#5210), declared on `ApiErrorSchema` /
|
|
21
21
|
* `EnhancedApiErrorSchema` in the pinned `@objectstack/spec`.
|
|
22
22
|
* Three properties of the contract decide this reader's whole shape:
|
|
@@ -74,7 +74,7 @@ export function extractWriteErrorMessage(err) {
|
|
|
74
74
|
* The refusal text the PRODUCER explicitly marked as addressed to the end user,
|
|
75
75
|
* or `null` when the refusal carries no marking.
|
|
76
76
|
*
|
|
77
|
-
* `userMessage` is the opt-in channel added by objectstack
|
|
77
|
+
* `userMessage` is the opt-in channel added by objectstack `79c46da90` (maintainer
|
|
78
78
|
* ruling 2026-08-19 on objectui#5210), declared on `ApiErrorSchema` /
|
|
79
79
|
* `EnhancedApiErrorSchema` in the pinned `@objectstack/spec`.
|
|
80
80
|
* Three properties of the contract decide this reader's whole shape:
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
import type { AriaProps } from '@objectstack/spec/ui';
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the spec's NESTED `aria` bag to the DOM attributes it declares
|
|
11
|
+
* (objectui#11051).
|
|
12
|
+
*
|
|
13
|
+
* The bag is `@objectstack/spec`'s `AriaPropsSchema`, the `aria` member a
|
|
14
|
+
* block's own props schema carries (`element:text`, `element:button`,
|
|
15
|
+
* `element:image` and `element:number` among them). It declares three keys,
|
|
16
|
+
* and each maps to one attribute:
|
|
17
|
+
*
|
|
18
|
+
* `ariaLabel` → `aria-label`, resolved with the spec's own
|
|
19
|
+
* `resolveI18nLabel` against `locale`: a plain string is
|
|
20
|
+
* used as written, and an inline locale map
|
|
21
|
+
* (`{ en: 'Order total', 'zh-CN': '订单合计' }`) gives the
|
|
22
|
+
* entry for the viewer's language;
|
|
23
|
+
* `ariaDescribedBy` → `aria-describedby`;
|
|
24
|
+
* `role` → `role`.
|
|
25
|
+
*
|
|
26
|
+
* A key that resolves to nothing, or to an empty string, is left out of the
|
|
27
|
+
* result instead of being written as an empty or `undefined` attribute. A key
|
|
28
|
+
* the bag does not declare is not read: the spec refuses such keys when the
|
|
29
|
+
* document is parsed, so this function adds no alias for them.
|
|
30
|
+
*
|
|
31
|
+
* ## Two vocabularies, two readers (objectui#4580 Q2-B)
|
|
32
|
+
*
|
|
33
|
+
* This is NOT `SchemaRenderer`'s `resolveAriaProps`. That one reads the FLAT
|
|
34
|
+
* node keys (`BaseSchema.ariaLabel` / `ariaDescribedBy` / `role`) and resolves
|
|
35
|
+
* `ariaLabel` in objectui's KEYED vocabulary (`{ key, defaultValue?, params? }`,
|
|
36
|
+
* via `resolveKeyedI18nLabel`). The nested bag uses the INLINE locale map.
|
|
37
|
+
* Neither resolver accepts the other's shape: the keyed one returns `undefined`
|
|
38
|
+
* for a locale map, and the spec's one treats `key` / `defaultValue` as locale
|
|
39
|
+
* tags. So the two readers stay separate, as objectui#4580 Q2-B ruled.
|
|
40
|
+
*
|
|
41
|
+
* ## Why a pure function and not a hook
|
|
42
|
+
*
|
|
43
|
+
* The caller passes the locale; this function reads no React context. That
|
|
44
|
+
* lets a renderer call it anywhere in its body, including after an early
|
|
45
|
+
* return, and lets code outside React call it too. Renderers pass
|
|
46
|
+
* `useDisplayLocale()` from `@object-ui/i18n`, the locale every
|
|
47
|
+
* `resolveI18nLabel` read site in this repo resolves against (objectui#4580
|
|
48
|
+
* revised Q1-A). Like the spec's resolver, `locale` is a required parameter
|
|
49
|
+
* that may be `undefined` (which resolves as `en`), so a caller cannot forget
|
|
50
|
+
* it by accident.
|
|
51
|
+
*
|
|
52
|
+
* @param aria - The authored bag, taken directly from the node's config. A
|
|
53
|
+
* missing bag, or a value that is not an object, gives `{}`.
|
|
54
|
+
* @param locale - BCP-47 tag of the viewer's language, e.g. `zh-CN`.
|
|
55
|
+
* @returns The attributes to spread onto the element that carries the name.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveInlineAriaProps(aria: AriaProps | null | undefined, locale: string | undefined): {
|
|
58
|
+
'aria-label'?: string;
|
|
59
|
+
'aria-describedby'?: string;
|
|
60
|
+
role?: string;
|
|
61
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
import { resolveI18nLabel as resolveInlineI18nLabel } from '@objectstack/spec/ui';
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the spec's NESTED `aria` bag to the DOM attributes it declares
|
|
11
|
+
* (objectui#11051).
|
|
12
|
+
*
|
|
13
|
+
* The bag is `@objectstack/spec`'s `AriaPropsSchema`, the `aria` member a
|
|
14
|
+
* block's own props schema carries (`element:text`, `element:button`,
|
|
15
|
+
* `element:image` and `element:number` among them). It declares three keys,
|
|
16
|
+
* and each maps to one attribute:
|
|
17
|
+
*
|
|
18
|
+
* `ariaLabel` → `aria-label`, resolved with the spec's own
|
|
19
|
+
* `resolveI18nLabel` against `locale`: a plain string is
|
|
20
|
+
* used as written, and an inline locale map
|
|
21
|
+
* (`{ en: 'Order total', 'zh-CN': '订单合计' }`) gives the
|
|
22
|
+
* entry for the viewer's language;
|
|
23
|
+
* `ariaDescribedBy` → `aria-describedby`;
|
|
24
|
+
* `role` → `role`.
|
|
25
|
+
*
|
|
26
|
+
* A key that resolves to nothing, or to an empty string, is left out of the
|
|
27
|
+
* result instead of being written as an empty or `undefined` attribute. A key
|
|
28
|
+
* the bag does not declare is not read: the spec refuses such keys when the
|
|
29
|
+
* document is parsed, so this function adds no alias for them.
|
|
30
|
+
*
|
|
31
|
+
* ## Two vocabularies, two readers (objectui#4580 Q2-B)
|
|
32
|
+
*
|
|
33
|
+
* This is NOT `SchemaRenderer`'s `resolveAriaProps`. That one reads the FLAT
|
|
34
|
+
* node keys (`BaseSchema.ariaLabel` / `ariaDescribedBy` / `role`) and resolves
|
|
35
|
+
* `ariaLabel` in objectui's KEYED vocabulary (`{ key, defaultValue?, params? }`,
|
|
36
|
+
* via `resolveKeyedI18nLabel`). The nested bag uses the INLINE locale map.
|
|
37
|
+
* Neither resolver accepts the other's shape: the keyed one returns `undefined`
|
|
38
|
+
* for a locale map, and the spec's one treats `key` / `defaultValue` as locale
|
|
39
|
+
* tags. So the two readers stay separate, as objectui#4580 Q2-B ruled.
|
|
40
|
+
*
|
|
41
|
+
* ## Why a pure function and not a hook
|
|
42
|
+
*
|
|
43
|
+
* The caller passes the locale; this function reads no React context. That
|
|
44
|
+
* lets a renderer call it anywhere in its body, including after an early
|
|
45
|
+
* return, and lets code outside React call it too. Renderers pass
|
|
46
|
+
* `useDisplayLocale()` from `@object-ui/i18n`, the locale every
|
|
47
|
+
* `resolveI18nLabel` read site in this repo resolves against (objectui#4580
|
|
48
|
+
* revised Q1-A). Like the spec's resolver, `locale` is a required parameter
|
|
49
|
+
* that may be `undefined` (which resolves as `en`), so a caller cannot forget
|
|
50
|
+
* it by accident.
|
|
51
|
+
*
|
|
52
|
+
* @param aria - The authored bag, taken directly from the node's config. A
|
|
53
|
+
* missing bag, or a value that is not an object, gives `{}`.
|
|
54
|
+
* @param locale - BCP-47 tag of the viewer's language, e.g. `zh-CN`.
|
|
55
|
+
* @returns The attributes to spread onto the element that carries the name.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveInlineAriaProps(aria, locale) {
|
|
58
|
+
if (!aria || typeof aria !== 'object')
|
|
59
|
+
return {};
|
|
60
|
+
const out = {};
|
|
61
|
+
const label = resolveInlineI18nLabel(aria.ariaLabel, locale);
|
|
62
|
+
if (label)
|
|
63
|
+
out['aria-label'] = label;
|
|
64
|
+
if (typeof aria.ariaDescribedBy === 'string' && aria.ariaDescribedBy) {
|
|
65
|
+
out['aria-describedby'] = aria.ariaDescribedBy;
|
|
66
|
+
}
|
|
67
|
+
if (typeof aria.role === 'string' && aria.role)
|
|
68
|
+
out.role = aria.role;
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
import React from 'react';
|
|
9
|
+
import type { NonGridCeilingResult } from '@object-ui/core';
|
|
10
|
+
/**
|
|
11
|
+
* The loud footnote a non-grid view shows when it drew only the first
|
|
12
|
+
* `NON_GRID_ROW_CEILING` rows of a larger result set (objectui#7210).
|
|
13
|
+
*
|
|
14
|
+
* Placement and tone follow objectui#7148's chart footnote — a `role="note"`
|
|
15
|
+
* line in muted small type directly under the visualisation, naming BOTH
|
|
16
|
+
* numbers, because the count is the half a reader cannot recover from the
|
|
17
|
+
* picture. A truncated schedule renders as a healthy, confident schedule of a
|
|
18
|
+
* fraction of itself; "some rows are missing" leaves it indistinguishable from
|
|
19
|
+
* a complete one, and `2,000 of 40,000` is the bit that was missing.
|
|
20
|
+
*
|
|
21
|
+
* Two sentences because there are two conditions: a known total states the
|
|
22
|
+
* fact with both numbers; an adapter that reported no `total` still gets a
|
|
23
|
+
* DEFINITE sentence (the probe row proves more rows exist), it simply cannot
|
|
24
|
+
* name how many.
|
|
25
|
+
*
|
|
26
|
+
* ## It takes the RESULT, and nothing else (objectui#7508, ruling A′)
|
|
27
|
+
*
|
|
28
|
+
* The only prop is the `NonGridCeilingResult` that `applyNonGridRowCeiling`
|
|
29
|
+
* returned. Every number the note prints is read off that one object — the
|
|
30
|
+
* drawn count is `result.rows.length`, the total is `result.total`, the
|
|
31
|
+
* verdict is `result.truncated` — so a footnote whose numbers disagree with
|
|
32
|
+
* the rows actually drawn is not something a caller can write. The earlier
|
|
33
|
+
* shape took `drawn` / `total` / `truncated` as three loose props, and every
|
|
34
|
+
* call site hand-wrote `drawn={NON_GRID_ROW_CEILING}` next to a `total` from
|
|
35
|
+
* somewhere else: an incoherent pair was expressible and nothing refused it.
|
|
36
|
+
* ⛔ No deprecated path keeps those props; there were no released consumers.
|
|
37
|
+
*
|
|
38
|
+
* Renders `null` when nothing was truncated, so a caller holding a result can
|
|
39
|
+
* mount it unconditionally and gains no wrapper element on the healthy path.
|
|
40
|
+
*/
|
|
41
|
+
export declare function NonGridRowCeilingNote({ result, }: {
|
|
42
|
+
/** What `applyNonGridRowCeiling` returned for the rows being drawn. */
|
|
43
|
+
result: NonGridCeilingResult;
|
|
44
|
+
}): React.JSX.Element | null;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useObjectTranslation, en } from '@object-ui/i18n';
|
|
3
|
+
// The non-React half of the non-grid row ceiling — `NON_GRID_ROW_CEILING`,
|
|
4
|
+
// `nonGridRowCeilingQuery`, `applyNonGridRowCeiling` and `NonGridCeilingResult`
|
|
5
|
+
// — lives in `@object-ui/core` beside the `extractRecords` it wraps
|
|
6
|
+
// (objectui#7508, ruling A′). Only the footnote is React, so only the footnote
|
|
7
|
+
// is here.
|
|
8
|
+
/**
|
|
9
|
+
* The loud footnote a non-grid view shows when it drew only the first
|
|
10
|
+
* `NON_GRID_ROW_CEILING` rows of a larger result set (objectui#7210).
|
|
11
|
+
*
|
|
12
|
+
* Placement and tone follow objectui#7148's chart footnote — a `role="note"`
|
|
13
|
+
* line in muted small type directly under the visualisation, naming BOTH
|
|
14
|
+
* numbers, because the count is the half a reader cannot recover from the
|
|
15
|
+
* picture. A truncated schedule renders as a healthy, confident schedule of a
|
|
16
|
+
* fraction of itself; "some rows are missing" leaves it indistinguishable from
|
|
17
|
+
* a complete one, and `2,000 of 40,000` is the bit that was missing.
|
|
18
|
+
*
|
|
19
|
+
* Two sentences because there are two conditions: a known total states the
|
|
20
|
+
* fact with both numbers; an adapter that reported no `total` still gets a
|
|
21
|
+
* DEFINITE sentence (the probe row proves more rows exist), it simply cannot
|
|
22
|
+
* name how many.
|
|
23
|
+
*
|
|
24
|
+
* ## It takes the RESULT, and nothing else (objectui#7508, ruling A′)
|
|
25
|
+
*
|
|
26
|
+
* The only prop is the `NonGridCeilingResult` that `applyNonGridRowCeiling`
|
|
27
|
+
* returned. Every number the note prints is read off that one object — the
|
|
28
|
+
* drawn count is `result.rows.length`, the total is `result.total`, the
|
|
29
|
+
* verdict is `result.truncated` — so a footnote whose numbers disagree with
|
|
30
|
+
* the rows actually drawn is not something a caller can write. The earlier
|
|
31
|
+
* shape took `drawn` / `total` / `truncated` as three loose props, and every
|
|
32
|
+
* call site hand-wrote `drawn={NON_GRID_ROW_CEILING}` next to a `total` from
|
|
33
|
+
* somewhere else: an incoherent pair was expressible and nothing refused it.
|
|
34
|
+
* ⛔ No deprecated path keeps those props; there were no released consumers.
|
|
35
|
+
*
|
|
36
|
+
* Renders `null` when nothing was truncated, so a caller holding a result can
|
|
37
|
+
* mount it unconditionally and gains no wrapper element on the healthy path.
|
|
38
|
+
*/
|
|
39
|
+
export function NonGridRowCeilingNote({ result, }) {
|
|
40
|
+
// ⚠️ A HOOK AT RENDER, never a module-scope `createSafeTranslation(...)`
|
|
41
|
+
// factory, and that is a correctness constraint rather than taste. This
|
|
42
|
+
// module is re-exported from `@object-ui/react`'s entry, so anything at its
|
|
43
|
+
// module scope runs on IMPORT for every consumer that touches the barrel —
|
|
44
|
+
// and a factory call there throws inside any test that partially mocks
|
|
45
|
+
// `@object-ui/i18n` with an object literal instead of `importOriginal`,
|
|
46
|
+
// before a single assertion runs. Two such files went red in CI on exactly
|
|
47
|
+
// that, and the population is repo-wide rather than knowable from here, so
|
|
48
|
+
// the call moved to where it cannot fire at import time. `en` is likewise
|
|
49
|
+
// dereferenced HERE, at render, not in a module-scope constant.
|
|
50
|
+
//
|
|
51
|
+
// `useObjectTranslation` is the one that interpolates on the provider-less
|
|
52
|
+
// path too (objectui#6219), so `{{shown}}` / `{{total}}` are filled whether
|
|
53
|
+
// or not the host mounted an `I18nProvider`. The `defaultValue` is read from
|
|
54
|
+
// the `en` pack rather than retyped: both sit in the same eagerly-loaded
|
|
55
|
+
// chunk, so a hand copy would ship identical bytes twice, and reading the
|
|
56
|
+
// pack makes an inline default that disagrees with `en` unrepresentable
|
|
57
|
+
// instead of merely policed.
|
|
58
|
+
const { t } = useObjectTranslation();
|
|
59
|
+
if (!result.truncated)
|
|
60
|
+
return null;
|
|
61
|
+
const shown = result.rows.length;
|
|
62
|
+
const text = typeof result.total === 'number'
|
|
63
|
+
? t('common.rowCeilingNote', {
|
|
64
|
+
shown,
|
|
65
|
+
total: result.total,
|
|
66
|
+
defaultValue: en.common.rowCeilingNote,
|
|
67
|
+
})
|
|
68
|
+
: t('common.rowCeilingNoteUnknownTotal', {
|
|
69
|
+
shown,
|
|
70
|
+
defaultValue: en.common.rowCeilingNoteUnknownTotal,
|
|
71
|
+
});
|
|
72
|
+
// `shrink-0` is the note's own, not a caller's: under a `flex-1` view pane in
|
|
73
|
+
// a fixed-height host a plain sibling can be clipped out of view — the
|
|
74
|
+
// construction objectui#7148's `ChartFootnote` measured, and the reason the
|
|
75
|
+
// gantt used to pass this exact class in. Outside a flex container it does
|
|
76
|
+
// nothing, so all four views take it and no caller needs a styling prop.
|
|
77
|
+
return (_jsx("p", { role: "note", "data-row-ceiling-note": "non-grid", className: "shrink-0 px-1 py-1 text-xs text-muted-foreground", children: text }));
|
|
78
|
+
}
|