@contember/bindx-react 0.1.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/batching/QueryBatcher.d.ts +31 -0
- package/dist/batching/QueryBatcher.d.ts.map +1 -0
- package/dist/batching/index.d.ts +2 -0
- package/dist/batching/index.d.ts.map +1 -0
- package/dist/components/EntityScope.d.ts +28 -0
- package/dist/components/EntityScope.d.ts.map +1 -0
- package/dist/dataview/DataGrid.d.ts +45 -0
- package/dist/dataview/DataGrid.d.ts.map +1 -0
- package/dist/dataview/DataGridPagination.d.ts +24 -0
- package/dist/dataview/DataGridPagination.d.ts.map +1 -0
- package/dist/dataview/DataGridToolbar.d.ts +16 -0
- package/dist/dataview/DataGridToolbar.d.ts.map +1 -0
- package/dist/dataview/DataViewContext.d.ts +32 -0
- package/dist/dataview/DataViewContext.d.ts.map +1 -0
- package/dist/dataview/columns.d.ts +115 -0
- package/dist/dataview/columns.d.ts.map +1 -0
- package/dist/dataview/contextHooks.d.ts +48 -0
- package/dist/dataview/contextHooks.d.ts.map +1 -0
- package/dist/dataview/dataAttribute.d.ts +6 -0
- package/dist/dataview/dataAttribute.d.ts.map +1 -0
- package/dist/dataview/export.d.ts +46 -0
- package/dist/dataview/export.d.ts.map +1 -0
- package/dist/dataview/filterComponents.d.ts +127 -0
- package/dist/dataview/filterComponents.d.ts.map +1 -0
- package/dist/dataview/filterContext.d.ts +10 -0
- package/dist/dataview/filterContext.d.ts.map +1 -0
- package/dist/dataview/filterHooks.d.ts +74 -0
- package/dist/dataview/filterHooks.d.ts.map +1 -0
- package/dist/dataview/filterScope.d.ts +54 -0
- package/dist/dataview/filterScope.d.ts.map +1 -0
- package/dist/dataview/filterWrappers.d.ts +65 -0
- package/dist/dataview/filterWrappers.d.ts.map +1 -0
- package/dist/dataview/index.d.ts +21 -0
- package/dist/dataview/index.d.ts.map +1 -0
- package/dist/dataview/infiniteLoad.d.ts +32 -0
- package/dist/dataview/infiniteLoad.d.ts.map +1 -0
- package/dist/dataview/loaderComponents.d.ts +80 -0
- package/dist/dataview/loaderComponents.d.ts.map +1 -0
- package/dist/dataview/pagingComponents.d.ts +50 -0
- package/dist/dataview/pagingComponents.d.ts.map +1 -0
- package/dist/dataview/renderCellValue.d.ts +9 -0
- package/dist/dataview/renderCellValue.d.ts.map +1 -0
- package/dist/dataview/selectionComponents.d.ts +62 -0
- package/dist/dataview/selectionComponents.d.ts.map +1 -0
- package/dist/dataview/sortingComponents.d.ts +39 -0
- package/dist/dataview/sortingComponents.d.ts.map +1 -0
- package/dist/dataview/stateStorage.d.ts +19 -0
- package/dist/dataview/stateStorage.d.ts.map +1 -0
- package/dist/dataview/useDataView.d.ts +58 -0
- package/dist/dataview/useDataView.d.ts.map +1 -0
- package/dist/dataview/useDataViewState.d.ts +89 -0
- package/dist/dataview/useDataViewState.d.ts.map +1 -0
- package/dist/hooks/BackendAdapterContext.d.ts +110 -0
- package/dist/hooks/BackendAdapterContext.d.ts.map +1 -0
- package/dist/hooks/ContemberBindxProvider.d.ts +67 -0
- package/dist/hooks/ContemberBindxProvider.d.ts.map +1 -0
- package/dist/hooks/createBindx.d.ts +39 -0
- package/dist/hooks/createBindx.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useBindxEvents.d.ts +83 -0
- package/dist/hooks/useBindxEvents.d.ts.map +1 -0
- package/dist/hooks/useEntity.d.ts +123 -0
- package/dist/hooks/useEntity.d.ts.map +1 -0
- package/dist/hooks/useEntityBeforePersist.d.ts +22 -0
- package/dist/hooks/useEntityBeforePersist.d.ts.map +1 -0
- package/dist/hooks/useEntityCore.d.ts +52 -0
- package/dist/hooks/useEntityCore.d.ts.map +1 -0
- package/dist/hooks/useEntityImpl.d.ts +105 -0
- package/dist/hooks/useEntityImpl.d.ts.map +1 -0
- package/dist/hooks/useEntityList.d.ts +102 -0
- package/dist/hooks/useEntityList.d.ts.map +1 -0
- package/dist/hooks/useEntityListCore.d.ts +49 -0
- package/dist/hooks/useEntityListCore.d.ts.map +1 -0
- package/dist/hooks/useEntityListImpl.d.ts +70 -0
- package/dist/hooks/useEntityListImpl.d.ts.map +1 -0
- package/dist/hooks/useErrors.d.ts +72 -0
- package/dist/hooks/useErrors.d.ts.map +1 -0
- package/dist/hooks/usePersist.d.ts +115 -0
- package/dist/hooks/usePersist.d.ts.map +1 -0
- package/dist/hooks/useSelectionCollection.d.ts +41 -0
- package/dist/hooks/useSelectionCollection.d.ts.map +1 -0
- package/dist/hooks/useSelectionCollectionForList.d.ts +40 -0
- package/dist/hooks/useSelectionCollectionForList.d.ts.map +1 -0
- package/dist/hooks/useStableSelectionMeta.d.ts +11 -0
- package/dist/hooks/useStableSelectionMeta.d.ts.map +1 -0
- package/dist/hooks/useStoreSubscription.d.ts +19 -0
- package/dist/hooks/useStoreSubscription.d.ts.map +1 -0
- package/dist/hooks/useUndo.d.ts +45 -0
- package/dist/hooks/useUndo.d.ts.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/jsx/SelectionMeta.d.ts +2 -0
- package/dist/jsx/SelectionMeta.d.ts.map +1 -0
- package/dist/jsx/analyzer.d.ts +22 -0
- package/dist/jsx/analyzer.d.ts.map +1 -0
- package/dist/jsx/collectorProxy.d.ts +14 -0
- package/dist/jsx/collectorProxy.d.ts.map +1 -0
- package/dist/jsx/componentBuilder.d.ts +51 -0
- package/dist/jsx/componentBuilder.d.ts.map +1 -0
- package/dist/jsx/componentBuilder.types.d.ts +268 -0
- package/dist/jsx/componentBuilder.types.d.ts.map +1 -0
- package/dist/jsx/componentBuilderCompat.d.ts +17 -0
- package/dist/jsx/componentBuilderCompat.d.ts.map +1 -0
- package/dist/jsx/componentFactory.d.ts +39 -0
- package/dist/jsx/componentFactory.d.ts.map +1 -0
- package/dist/jsx/components/Entity.d.ts +82 -0
- package/dist/jsx/components/Entity.d.ts.map +1 -0
- package/dist/jsx/components/EntityList.d.ts +48 -0
- package/dist/jsx/components/EntityList.d.ts.map +1 -0
- package/dist/jsx/components/Field.d.ts +27 -0
- package/dist/jsx/components/Field.d.ts.map +1 -0
- package/dist/jsx/components/HasMany.d.ts +34 -0
- package/dist/jsx/components/HasMany.d.ts.map +1 -0
- package/dist/jsx/components/HasOne.d.ts +25 -0
- package/dist/jsx/components/HasOne.d.ts.map +1 -0
- package/dist/jsx/components/HasRole.d.ts +51 -0
- package/dist/jsx/components/HasRole.d.ts.map +1 -0
- package/dist/jsx/components/If.d.ts +51 -0
- package/dist/jsx/components/If.d.ts.map +1 -0
- package/dist/jsx/components/Show.d.ts +33 -0
- package/dist/jsx/components/Show.d.ts.map +1 -0
- package/dist/jsx/components/index.d.ts +8 -0
- package/dist/jsx/components/index.d.ts.map +1 -0
- package/dist/jsx/conditions.d.ts +310 -0
- package/dist/jsx/conditions.d.ts.map +1 -0
- package/dist/jsx/createComponent.d.ts +44 -0
- package/dist/jsx/createComponent.d.ts.map +1 -0
- package/dist/jsx/index.d.ts +19 -0
- package/dist/jsx/index.d.ts.map +1 -0
- package/dist/jsx/inlineProxy.d.ts +14 -0
- package/dist/jsx/inlineProxy.d.ts.map +1 -0
- package/dist/jsx/legacyTypes.d.ts +26 -0
- package/dist/jsx/legacyTypes.d.ts.map +1 -0
- package/dist/jsx/proxy.d.ts +2 -0
- package/dist/jsx/proxy.d.ts.map +1 -0
- package/dist/jsx/proxyShared.d.ts +14 -0
- package/dist/jsx/proxyShared.d.ts.map +1 -0
- package/dist/jsx/runtimeProxy.d.ts +11 -0
- package/dist/jsx/runtimeProxy.d.ts.map +1 -0
- package/dist/jsx/standaloneCreateComponent.d.ts +30 -0
- package/dist/jsx/standaloneCreateComponent.d.ts.map +1 -0
- package/dist/jsx/types.d.ts +109 -0
- package/dist/jsx/types.d.ts.map +1 -0
- package/dist/jsx/withCollector.d.ts +39 -0
- package/dist/jsx/withCollector.d.ts.map +1 -0
- package/dist/roles/RoleContext.d.ts +31 -0
- package/dist/roles/RoleContext.d.ts.map +1 -0
- package/dist/roles/index.d.ts +2 -0
- package/dist/roles/index.d.ts.map +1 -0
- package/package.json +34 -0
- package/src/batching/QueryBatcher.ts +114 -0
- package/src/batching/index.ts +1 -0
- package/src/components/EntityScope.tsx +43 -0
- package/src/hooks/BackendAdapterContext.tsx +214 -0
- package/src/hooks/ContemberBindxProvider.tsx +158 -0
- package/src/hooks/index.ts +61 -0
- package/src/hooks/useBindxEvents.ts +229 -0
- package/src/hooks/useEntity.ts +489 -0
- package/src/hooks/useEntityBeforePersist.ts +32 -0
- package/src/hooks/useEntityList.ts +444 -0
- package/src/hooks/useErrors.ts +150 -0
- package/src/hooks/usePersist.ts +377 -0
- package/src/hooks/useSelectionCollection.ts +122 -0
- package/src/hooks/useStoreSubscription.ts +51 -0
- package/src/hooks/useUndo.ts +83 -0
- package/src/index.ts +357 -0
- package/src/jsx/SelectionMeta.ts +2 -0
- package/src/jsx/analyzer.ts +261 -0
- package/src/jsx/collectorProxy.ts +282 -0
- package/src/jsx/componentBuilder.ts +180 -0
- package/src/jsx/componentBuilder.types.ts +463 -0
- package/src/jsx/componentBuilderCompat.ts +39 -0
- package/src/jsx/componentFactory.ts +400 -0
- package/src/jsx/components/Entity.tsx +383 -0
- package/src/jsx/components/EntityList.tsx +147 -0
- package/src/jsx/components/Field.tsx +79 -0
- package/src/jsx/components/HasMany.tsx +109 -0
- package/src/jsx/components/HasOne.tsx +78 -0
- package/src/jsx/components/HasRole.tsx +95 -0
- package/src/jsx/components/If.tsx +127 -0
- package/src/jsx/components/Show.tsx +56 -0
- package/src/jsx/components/index.ts +20 -0
- package/src/jsx/conditions.ts +472 -0
- package/src/jsx/createComponent.ts +175 -0
- package/src/jsx/index.ts +135 -0
- package/src/jsx/legacyTypes.ts +30 -0
- package/src/jsx/proxy.ts +1 -0
- package/src/jsx/proxyShared.ts +42 -0
- package/src/jsx/standaloneCreateComponent.ts +47 -0
- package/src/jsx/types.ts +192 -0
- package/src/jsx/withCollector.ts +45 -0
- package/src/roles/RoleContext.tsx +57 -0
- package/src/roles/index.ts +1 -0
package/src/jsx/index.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export {
|
|
3
|
+
BINDX_COMPONENT,
|
|
4
|
+
FIELD_REF_META,
|
|
5
|
+
type FieldRefMeta,
|
|
6
|
+
// Full types
|
|
7
|
+
type FieldRef,
|
|
8
|
+
type HasManyRef,
|
|
9
|
+
type HasOneRef,
|
|
10
|
+
type HasOneAccessor,
|
|
11
|
+
type EntityRef,
|
|
12
|
+
type EntityAccessor,
|
|
13
|
+
// Base types
|
|
14
|
+
type FieldRefBase,
|
|
15
|
+
type HasManyRefBase,
|
|
16
|
+
type HasOneRefBase,
|
|
17
|
+
type HasOneAccessorBase,
|
|
18
|
+
type EntityRefBase,
|
|
19
|
+
type EntityAccessorBase,
|
|
20
|
+
// Field mapping types
|
|
21
|
+
type EntityFields,
|
|
22
|
+
type SelectedEntityFields,
|
|
23
|
+
type SelectedEntityFieldsBase,
|
|
24
|
+
// Component props
|
|
25
|
+
type FieldProps,
|
|
26
|
+
type HasManyProps,
|
|
27
|
+
type HasOneProps,
|
|
28
|
+
type IfProps,
|
|
29
|
+
type EntityComponentProps,
|
|
30
|
+
type SelectionMeta,
|
|
31
|
+
type SelectionFieldMeta,
|
|
32
|
+
type HasManyComponentOptions,
|
|
33
|
+
type SelectionProvider,
|
|
34
|
+
type AnyBrand,
|
|
35
|
+
} from './types.js'
|
|
36
|
+
|
|
37
|
+
// Selection metadata
|
|
38
|
+
export {
|
|
39
|
+
SelectionMetaCollector,
|
|
40
|
+
mergeSelections,
|
|
41
|
+
createEmptySelection,
|
|
42
|
+
} from './SelectionMeta.js'
|
|
43
|
+
|
|
44
|
+
// Proxy creation
|
|
45
|
+
export {
|
|
46
|
+
createCollectorProxy,
|
|
47
|
+
} from './proxy.js'
|
|
48
|
+
|
|
49
|
+
// JSX Analyzer
|
|
50
|
+
export {
|
|
51
|
+
analyzeJsx,
|
|
52
|
+
collectSelection,
|
|
53
|
+
convertToQuerySelection,
|
|
54
|
+
debugSelection,
|
|
55
|
+
} from './analyzer.js'
|
|
56
|
+
|
|
57
|
+
// Components
|
|
58
|
+
export { Field, FieldWithMeta } from './components/Field.js'
|
|
59
|
+
export { HasMany, HasManyWithMeta } from './components/HasMany.js'
|
|
60
|
+
export { HasOne, HasOneWithMeta } from './components/HasOne.js'
|
|
61
|
+
export { If, IfWithMeta } from './components/If.js'
|
|
62
|
+
export { Show, ShowWithMeta, type ShowProps } from './components/Show.js'
|
|
63
|
+
export { Entity, type EntityProps } from './components/Entity.js'
|
|
64
|
+
export { EntityList, type EntityListProps } from './components/EntityList.js'
|
|
65
|
+
export { HasRole, type HasRoleProps } from './components/HasRole.js'
|
|
66
|
+
|
|
67
|
+
// Condition DSL for <If> component
|
|
68
|
+
export {
|
|
69
|
+
cond,
|
|
70
|
+
isCondition,
|
|
71
|
+
evaluateCondition,
|
|
72
|
+
collectConditionFields,
|
|
73
|
+
CONDITION_META,
|
|
74
|
+
type Condition,
|
|
75
|
+
type ConditionMeta,
|
|
76
|
+
} from './conditions.js'
|
|
77
|
+
|
|
78
|
+
// Component builder (unified API)
|
|
79
|
+
export {
|
|
80
|
+
isBindxComponent,
|
|
81
|
+
mergeFragments,
|
|
82
|
+
COMPONENT_MARKER,
|
|
83
|
+
COMPONENT_BRAND,
|
|
84
|
+
COMPONENT_SELECTIONS,
|
|
85
|
+
createComponentBuilder,
|
|
86
|
+
ComponentBuilderImpl,
|
|
87
|
+
getComponentBrand,
|
|
88
|
+
setBrandValidation,
|
|
89
|
+
validateBrand,
|
|
90
|
+
} from './createComponent.js'
|
|
91
|
+
|
|
92
|
+
// Standalone createComponent function
|
|
93
|
+
export { createComponent } from './standaloneCreateComponent.js'
|
|
94
|
+
|
|
95
|
+
// withCollector — attach staticRender to a component for selection collection
|
|
96
|
+
export { withCollector } from './withCollector.js'
|
|
97
|
+
|
|
98
|
+
export type {
|
|
99
|
+
SelectionPropMeta,
|
|
100
|
+
BindxComponentBase,
|
|
101
|
+
BindxComponent,
|
|
102
|
+
ComponentBuilder,
|
|
103
|
+
ComponentBuilderState,
|
|
104
|
+
CreateComponentOptions,
|
|
105
|
+
// Interface types
|
|
106
|
+
InterfaceEntityPropConfig,
|
|
107
|
+
ImplicitInterfaceEntityConfig,
|
|
108
|
+
ExplicitInterfaceEntityConfig,
|
|
109
|
+
AddInterfaces,
|
|
110
|
+
InterfaceSelectorsMap,
|
|
111
|
+
AnyEntityPropConfig,
|
|
112
|
+
// Entity prop config types
|
|
113
|
+
EntityPropConfig,
|
|
114
|
+
ImplicitEntityConfig,
|
|
115
|
+
ExplicitEntityConfig,
|
|
116
|
+
// State helpers
|
|
117
|
+
AddImplicitEntity,
|
|
118
|
+
AddExplicitEntity,
|
|
119
|
+
AddImplicitInterfaceEntity,
|
|
120
|
+
AddExplicitInterfaceEntity,
|
|
121
|
+
SetScalarProps,
|
|
122
|
+
// Props building
|
|
123
|
+
BuildEntityProps,
|
|
124
|
+
BuildProps,
|
|
125
|
+
BuildFragmentProps,
|
|
126
|
+
InitialBuilderState,
|
|
127
|
+
} from './createComponent.js'
|
|
128
|
+
|
|
129
|
+
// Legacy type exports for backwards compatibility
|
|
130
|
+
export type {
|
|
131
|
+
EntityPropKeys,
|
|
132
|
+
EntityFromProp,
|
|
133
|
+
SelectionFromProp,
|
|
134
|
+
ImplicitFragmentProperties,
|
|
135
|
+
} from './legacyTypes.js'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy type exports for backwards compatibility.
|
|
3
|
+
* These types were previously exported from createComponent.ts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { EntityRef, FluentFragment } from '@contember/bindx'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract keys from props type where value is EntityRef<any, any>
|
|
10
|
+
*/
|
|
11
|
+
export type EntityPropKeys<P> = {
|
|
12
|
+
[K in keyof P]: P[K] extends EntityRef<infer _T, infer _S> ? K : never
|
|
13
|
+
}[keyof P]
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract the full entity type from an EntityRef prop
|
|
17
|
+
*/
|
|
18
|
+
export type EntityFromProp<P, K extends keyof P> = P[K] extends EntityRef<infer T, infer _S> ? T : never
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Extract the selection type from an EntityRef prop
|
|
22
|
+
*/
|
|
23
|
+
export type SelectionFromProp<P, K extends keyof P> = P[K] extends EntityRef<infer _T, infer S> ? S : never
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Fragment properties for implicit mode - $propName for each entity prop
|
|
27
|
+
*/
|
|
28
|
+
export type ImplicitFragmentProperties<P> = {
|
|
29
|
+
[K in EntityPropKeys<P> as `$${K & string}`]: FluentFragment<EntityFromProp<P, K>, SelectionFromProp<P, K>>
|
|
30
|
+
}
|
package/src/jsx/proxy.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCollectorProxy } from './collectorProxy.js'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type EntityRef,
|
|
3
|
+
type EntityAccessor,
|
|
4
|
+
type EntityFields,
|
|
5
|
+
} from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Known properties on entity accessor that should NOT be treated as field access.
|
|
9
|
+
* Properties are accessed with $ prefix for consistency with EntityHandle.
|
|
10
|
+
* Used by collector proxy.
|
|
11
|
+
*/
|
|
12
|
+
export const ENTITY_ACCESSOR_PROPERTIES = new Set([
|
|
13
|
+
'id',
|
|
14
|
+
'$fields', '$data', '$isDirty', '$persistedId', '$isNew',
|
|
15
|
+
'$errors', '$hasError', '$addError', '$clearErrors', '$clearAllErrors',
|
|
16
|
+
'$on', '$intercept', '$onPersisted', '$interceptPersisting',
|
|
17
|
+
'__entityType', '__entityName',
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Wraps an EntityRef in a Proxy that supports direct field access.
|
|
22
|
+
* - `entity.fieldName` is equivalent to `entity.$fields.fieldName`
|
|
23
|
+
* - Known accessor properties pass through to the target
|
|
24
|
+
*/
|
|
25
|
+
export function wrapEntityRefWithFieldAccessProxy<T>(ref: EntityRef<T>): EntityAccessor<T> {
|
|
26
|
+
return new Proxy(ref, {
|
|
27
|
+
get(target, prop) {
|
|
28
|
+
// Symbols - pass through
|
|
29
|
+
if (typeof prop !== 'string') {
|
|
30
|
+
return Reflect.get(target, prop)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Known accessor properties - pass through
|
|
34
|
+
if (ENTITY_ACCESSOR_PROPERTIES.has(prop)) {
|
|
35
|
+
return Reflect.get(target, prop)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Otherwise, treat as field access
|
|
39
|
+
return target.$fields[prop as keyof EntityFields<T>]
|
|
40
|
+
},
|
|
41
|
+
}) as EntityAccessor<T>
|
|
42
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone createComponent function.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the old createBindx()-returned createComponent, this function
|
|
5
|
+
* does not need a schema-bound closure. The SchemaRegistry is passed as null
|
|
6
|
+
* and components resolve it from context at render time if needed.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ComponentBuilder,
|
|
11
|
+
ComponentBuilderState,
|
|
12
|
+
CreateComponentOptions,
|
|
13
|
+
} from './componentBuilder.types.js'
|
|
14
|
+
import { createComponentBuilder } from './componentBuilder.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a component builder for defining bindx components.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createComponent } from '@contember/bindx-react'
|
|
22
|
+
* import { schema } from './generated'
|
|
23
|
+
*
|
|
24
|
+
* const ArticleCard = createComponent()
|
|
25
|
+
* .entity('article', schema.Article, e => e.title().author(a => a.name()))
|
|
26
|
+
* .props<{ className?: string }>()
|
|
27
|
+
* .render(({ article, className }) => (
|
|
28
|
+
* <div className={className}>
|
|
29
|
+
* <h2>{article.$data?.title}</h2>
|
|
30
|
+
* <span>{article.$data?.author?.name}</span>
|
|
31
|
+
* </div>
|
|
32
|
+
* ))
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
36
|
+
export function createComponent(): ComponentBuilder<ComponentBuilderState<{}, object, readonly string[]>>
|
|
37
|
+
export function createComponent<const TRoles extends readonly string[]>(
|
|
38
|
+
options: CreateComponentOptions<TRoles>,
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
40
|
+
): ComponentBuilder<ComponentBuilderState<{}, object, TRoles>>
|
|
41
|
+
export function createComponent(options?: CreateComponentOptions<readonly string[]>): ComponentBuilder<ComponentBuilderState> {
|
|
42
|
+
const roles = options?.roles ?? []
|
|
43
|
+
return createComponentBuilder(
|
|
44
|
+
null,
|
|
45
|
+
roles,
|
|
46
|
+
)
|
|
47
|
+
}
|
package/src/jsx/types.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import type {
|
|
3
|
+
SelectionMeta,
|
|
4
|
+
SelectionFieldMeta,
|
|
5
|
+
EntityFields,
|
|
6
|
+
FieldRefMeta,
|
|
7
|
+
ScalarKeys,
|
|
8
|
+
HasManyKeys,
|
|
9
|
+
HasOneKeys,
|
|
10
|
+
SelectedEntityFields,
|
|
11
|
+
SelectedEntityFieldsBase,
|
|
12
|
+
// Full types
|
|
13
|
+
FieldRef,
|
|
14
|
+
HasManyRef,
|
|
15
|
+
HasOneRef,
|
|
16
|
+
HasOneAccessor,
|
|
17
|
+
EntityRef,
|
|
18
|
+
EntityAccessor,
|
|
19
|
+
// Base types (for component props - accept both)
|
|
20
|
+
FieldRefBase,
|
|
21
|
+
HasManyRefBase,
|
|
22
|
+
HasOneRefBase,
|
|
23
|
+
HasOneAccessorBase,
|
|
24
|
+
EntityRefBase,
|
|
25
|
+
EntityAccessorBase,
|
|
26
|
+
AnyBrand,
|
|
27
|
+
} from '@contember/bindx'
|
|
28
|
+
import { Condition } from './conditions.js'
|
|
29
|
+
|
|
30
|
+
// Re-export unified types for backwards compatibility
|
|
31
|
+
export type { SelectionMeta, SelectionFieldMeta }
|
|
32
|
+
|
|
33
|
+
// Re-export from @contember/bindx
|
|
34
|
+
export { FIELD_REF_META } from '@contember/bindx'
|
|
35
|
+
export type {
|
|
36
|
+
EntityFields,
|
|
37
|
+
FieldRefMeta,
|
|
38
|
+
ScalarKeys,
|
|
39
|
+
HasManyKeys,
|
|
40
|
+
HasOneKeys,
|
|
41
|
+
SelectedEntityFields,
|
|
42
|
+
SelectedEntityFieldsBase,
|
|
43
|
+
// Full types
|
|
44
|
+
FieldRef,
|
|
45
|
+
HasManyRef,
|
|
46
|
+
HasOneRef,
|
|
47
|
+
HasOneAccessor,
|
|
48
|
+
EntityRef,
|
|
49
|
+
EntityAccessor,
|
|
50
|
+
// Base types
|
|
51
|
+
FieldRefBase,
|
|
52
|
+
HasManyRefBase,
|
|
53
|
+
HasOneRefBase,
|
|
54
|
+
HasOneAccessorBase,
|
|
55
|
+
EntityRefBase,
|
|
56
|
+
EntityAccessorBase,
|
|
57
|
+
AnyBrand,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Marker symbol for identifying bindx components
|
|
62
|
+
*/
|
|
63
|
+
export const BINDX_COMPONENT = Symbol('BINDX_COMPONENT')
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Symbol for direct scope access on collector proxies.
|
|
67
|
+
* Replaces NESTED_SELECTION_REF - carries the SelectionScope directly.
|
|
68
|
+
* Used when passing relation entities to nested createComponent components.
|
|
69
|
+
*/
|
|
70
|
+
export const SCOPE_REF = Symbol('SCOPE_REF')
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Props for Field component.
|
|
75
|
+
* Accepts FieldRefBase, so both FieldRef (explicit) and FieldRefBase (implicit) work.
|
|
76
|
+
*/
|
|
77
|
+
export interface FieldProps<T> {
|
|
78
|
+
field: FieldRefBase<T>
|
|
79
|
+
children?: (accessor: FieldRef<T>) => ReactNode
|
|
80
|
+
format?: (value: T | null) => ReactNode
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for HasMany relation
|
|
85
|
+
*/
|
|
86
|
+
export interface HasManyComponentOptions {
|
|
87
|
+
filter?: unknown
|
|
88
|
+
orderBy?: unknown
|
|
89
|
+
limit?: number
|
|
90
|
+
offset?: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Props for HasMany component.
|
|
95
|
+
* Selection-aware: children callback receives EntityAccessor with direct field access.
|
|
96
|
+
* Accepts HasManyRefBase, so both HasManyRef (explicit) and HasManyRefBase (implicit) work.
|
|
97
|
+
*
|
|
98
|
+
* @typeParam TEntity - The full entity type
|
|
99
|
+
* @typeParam TSelected - The selected subset of fields (defaults to TEntity for backwards compatibility)
|
|
100
|
+
* @typeParam TBrand - Component brand type for validation (defaults to AnyBrand)
|
|
101
|
+
* @typeParam TEntityName - Entity name as string literal for type narrowing
|
|
102
|
+
* @typeParam TSchema - Schema for entity name lookup in nested relations
|
|
103
|
+
*/
|
|
104
|
+
export interface HasManyProps<
|
|
105
|
+
TEntity,
|
|
106
|
+
TSelected = TEntity,
|
|
107
|
+
TBrand extends AnyBrand = AnyBrand,
|
|
108
|
+
TEntityName extends string = string,
|
|
109
|
+
TSchema extends Record<string, object> = Record<string, object>,
|
|
110
|
+
> {
|
|
111
|
+
field: HasManyRefBase<TEntity, TSelected, TBrand, TEntityName, TSchema>
|
|
112
|
+
children: (item: EntityAccessor<TEntity, TSelected, TBrand, TEntityName, TSchema>, index: number) => ReactNode
|
|
113
|
+
filter?: unknown
|
|
114
|
+
orderBy?: unknown
|
|
115
|
+
limit?: number
|
|
116
|
+
offset?: number
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Props for HasOne component.
|
|
121
|
+
* Selection-aware: children callback receives EntityAccessor with direct field access.
|
|
122
|
+
* Accepts HasOneRefBase, so both HasOneRef (explicit) and HasOneRefBase (implicit) work.
|
|
123
|
+
*
|
|
124
|
+
* @typeParam TEntity - The full entity type
|
|
125
|
+
* @typeParam TSelected - The selected subset of fields (defaults to TEntity for backwards compatibility)
|
|
126
|
+
* @typeParam TBrand - Component brand type for validation (defaults to AnyBrand)
|
|
127
|
+
* @typeParam TEntityName - Entity name as string literal for type narrowing
|
|
128
|
+
* @typeParam TSchema - Schema for entity name lookup in nested relations
|
|
129
|
+
*/
|
|
130
|
+
export interface HasOneProps<
|
|
131
|
+
TEntity,
|
|
132
|
+
TSelected = TEntity,
|
|
133
|
+
TBrand extends AnyBrand = AnyBrand,
|
|
134
|
+
TEntityName extends string = string,
|
|
135
|
+
TSchema extends Record<string, object> = Record<string, object>,
|
|
136
|
+
> {
|
|
137
|
+
field: HasOneRefBase<TEntity, TSelected, TBrand, TEntityName, TSchema>
|
|
138
|
+
children: (entity: EntityAccessor<TEntity, TSelected, TBrand, TEntityName, TSchema>) => ReactNode
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Props for Entity component.
|
|
143
|
+
* Entity name is preserved for proper HasRole type narrowing.
|
|
144
|
+
* Children receive EntityAccessor with direct field access.
|
|
145
|
+
*/
|
|
146
|
+
export interface EntityComponentProps<TSchema extends Record<string, object>, K extends keyof TSchema & string> {
|
|
147
|
+
name: K
|
|
148
|
+
id: string
|
|
149
|
+
children: (entity: EntityAccessor<TSchema[K], TSchema[K], import('@contember/bindx').AnyBrand, K, TSchema>) => ReactNode
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Props for If conditional component.
|
|
154
|
+
*
|
|
155
|
+
* Supports three types of conditions:
|
|
156
|
+
* 1. Boolean literals: `condition={true}` or `condition={someBoolean}`
|
|
157
|
+
* 2. FieldRef<boolean>: `condition={task.isActive}` - field value is used
|
|
158
|
+
* 3. Condition objects: `condition={cond.hasItems(task.developers)}` - composable DSL
|
|
159
|
+
*
|
|
160
|
+
* Using the Condition DSL is recommended for implicit selection mode as it ensures
|
|
161
|
+
* all referenced fields are collected for the GraphQL query.
|
|
162
|
+
*/
|
|
163
|
+
export interface IfProps {
|
|
164
|
+
/**
|
|
165
|
+
* Condition to evaluate. Can be:
|
|
166
|
+
* - A boolean literal
|
|
167
|
+
* - A FieldRef<boolean>
|
|
168
|
+
* - A Condition object from the condition builders (hasItems, isEmpty, eq, and, or, not, etc.)
|
|
169
|
+
*/
|
|
170
|
+
condition: boolean | FieldRef<boolean> | Condition
|
|
171
|
+
then: ReactNode
|
|
172
|
+
else?: ReactNode
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Interface for components that can provide selection info
|
|
177
|
+
*/
|
|
178
|
+
export interface SelectionProvider {
|
|
179
|
+
getSelection(
|
|
180
|
+
props: unknown,
|
|
181
|
+
collectNested: (children: ReactNode) => SelectionMeta,
|
|
182
|
+
): SelectionFieldMeta | SelectionFieldMeta[] | null
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Re-export component types
|
|
186
|
+
export type {
|
|
187
|
+
EntityPropKeys,
|
|
188
|
+
EntityFromProp,
|
|
189
|
+
SelectionFromProp,
|
|
190
|
+
ImplicitFragmentProperties,
|
|
191
|
+
} from './legacyTypes.js'
|
|
192
|
+
export { COMPONENT_MARKER, COMPONENT_SELECTIONS } from './componentBuilder.js'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Attaches a static render function to a component for selection collection.
|
|
5
|
+
*
|
|
6
|
+
* During the collection phase, the analyzer calls `staticRender(props)` instead of
|
|
7
|
+
* analyzing runtime children. Props contain collector proxies, so field accesses
|
|
8
|
+
* are tracked automatically. The returned JSX is analyzed for nested components.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // Simple: children is a render prop receiving has-one entity
|
|
13
|
+
* export const SelectField = withCollector(
|
|
14
|
+
* function SelectField({ field, children, ... }) { ... },
|
|
15
|
+
* (props) => props.children(props.field.$entity)
|
|
16
|
+
* )
|
|
17
|
+
*
|
|
18
|
+
* // Composing with HasMany for iteration
|
|
19
|
+
* export const DefaultRepeater = withCollector(
|
|
20
|
+
* function DefaultRepeater({ field, children, ... }) { ... },
|
|
21
|
+
* (props) => (
|
|
22
|
+
* <HasMany field={props.field}>
|
|
23
|
+
* {item => props.children(item, collectionItemInfo)}
|
|
24
|
+
* </HasMany>
|
|
25
|
+
* )
|
|
26
|
+
* )
|
|
27
|
+
*
|
|
28
|
+
* // Programmatic field access (no JSX needed)
|
|
29
|
+
* export const Uploader = withCollector(
|
|
30
|
+
* function Uploader({ field, fileType }) { ... },
|
|
31
|
+
* (props) => {
|
|
32
|
+
* const entity = props.field.$entity
|
|
33
|
+
* for (const ext of props.fileType.extractors) entity[ext.fieldName]
|
|
34
|
+
* return null
|
|
35
|
+
* }
|
|
36
|
+
* )
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function withCollector<TComponent extends (...args: never[]) => ReactNode>(
|
|
40
|
+
component: TComponent,
|
|
41
|
+
staticRender: (props: Parameters<TComponent>[0]) => ReactNode,
|
|
42
|
+
): TComponent {
|
|
43
|
+
(component as TComponent & { staticRender: typeof staticRender }).staticRender = staticRender
|
|
44
|
+
return component
|
|
45
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role provider context for runtime role checking.
|
|
3
|
+
*
|
|
4
|
+
* HasRole uses this to determine if the current user has the required roles.
|
|
5
|
+
* Without a RoleProvider, HasRole always renders (no gating).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { createContext, useContext, type ReactNode, type ReactElement } from 'react'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Context
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
type HasRoleFn = (role: string) => boolean
|
|
15
|
+
|
|
16
|
+
const RoleContext = createContext<HasRoleFn | null>(null)
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Provider
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface RoleProviderProps {
|
|
23
|
+
children: ReactNode
|
|
24
|
+
/** Function that checks if the current user has a given role */
|
|
25
|
+
hasRole: HasRoleFn
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Provides runtime role checking for HasRole components.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <RoleProvider hasRole={role => currentUser.roles.includes(role)}>
|
|
34
|
+
* <App />
|
|
35
|
+
* </RoleProvider>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function RoleProvider({ children, hasRole }: RoleProviderProps): ReactElement {
|
|
39
|
+
return <RoleContext.Provider value={hasRole}>{children}</RoleContext.Provider>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Hook
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns a function to check if the current user has a given role.
|
|
48
|
+
* If no RoleProvider is present, returns a function that always returns true.
|
|
49
|
+
*/
|
|
50
|
+
export function useHasRole(): HasRoleFn {
|
|
51
|
+
const hasRole = useContext(RoleContext)
|
|
52
|
+
return hasRole ?? alwaysTrue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function alwaysTrue(): boolean {
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RoleProvider, useHasRole, type RoleProviderProps } from './RoleContext.js'
|