@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
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the unified createComponent builder API.
|
|
3
|
+
*
|
|
4
|
+
* This provides a fluent builder interface for creating bindx components
|
|
5
|
+
* with support for both implicit (JSX-analyzed) and explicit (selector-based)
|
|
6
|
+
* entity props, scalar props, and optional role constraints.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ReactNode, ComponentType } from 'react'
|
|
10
|
+
import type {
|
|
11
|
+
EntityRef,
|
|
12
|
+
EntityAccessor,
|
|
13
|
+
EntityAccessorBase,
|
|
14
|
+
FluentFragment,
|
|
15
|
+
SelectionBuilder,
|
|
16
|
+
AnyBrand,
|
|
17
|
+
SelectionMeta,
|
|
18
|
+
EntityDef,
|
|
19
|
+
ResolveEntity,
|
|
20
|
+
} from '@contember/bindx'
|
|
21
|
+
import type { SelectionProvider } from './types.js'
|
|
22
|
+
import type { Condition } from './conditions.js'
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Symbols (re-exported from createComponent.ts)
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export declare const COMPONENT_MARKER: unique symbol
|
|
29
|
+
export declare const COMPONENT_BRAND: unique symbol
|
|
30
|
+
export declare const COMPONENT_SELECTIONS: unique symbol
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Entity Prop Configuration
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for a single entity prop.
|
|
38
|
+
* Can be implicit (selection from JSX) or explicit (selection from selector).
|
|
39
|
+
* Stores the entity type directly (not entity name).
|
|
40
|
+
*/
|
|
41
|
+
export interface EntityPropConfig<
|
|
42
|
+
TEntity extends object = object,
|
|
43
|
+
TSelected extends object = object,
|
|
44
|
+
TIsImplicit extends boolean = boolean,
|
|
45
|
+
> {
|
|
46
|
+
readonly __entity: TEntity
|
|
47
|
+
readonly isImplicit: TIsImplicit
|
|
48
|
+
readonly __selected: TSelected
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates an implicit entity prop config type.
|
|
53
|
+
*/
|
|
54
|
+
export type ImplicitEntityConfig<TEntity extends object> =
|
|
55
|
+
EntityPropConfig<TEntity, TEntity, true>
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates an explicit entity prop config type.
|
|
59
|
+
*/
|
|
60
|
+
export type ExplicitEntityConfig<TEntity extends object, TSelected extends object> =
|
|
61
|
+
EntityPropConfig<TEntity, TSelected, false>
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Interface Entity Prop Configuration
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Configuration for an interface-based entity prop.
|
|
69
|
+
* Unlike EntityPropConfig, this doesn't require a specific entity type.
|
|
70
|
+
* Instead, it constrains the TSelected type to extend TInterface.
|
|
71
|
+
*/
|
|
72
|
+
export interface InterfaceEntityPropConfig<
|
|
73
|
+
TInterface extends object = object,
|
|
74
|
+
TIsImplicit extends boolean = boolean,
|
|
75
|
+
> {
|
|
76
|
+
readonly isInterface: true
|
|
77
|
+
readonly isImplicit: TIsImplicit
|
|
78
|
+
readonly __interface: TInterface
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates an implicit interface entity prop config type.
|
|
83
|
+
*/
|
|
84
|
+
export type ImplicitInterfaceEntityConfig<TInterface extends object> =
|
|
85
|
+
InterfaceEntityPropConfig<TInterface, true>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates an explicit interface entity prop config type.
|
|
89
|
+
*/
|
|
90
|
+
export type ExplicitInterfaceEntityConfig<TInterface extends object> =
|
|
91
|
+
InterfaceEntityPropConfig<TInterface, false>
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Builder State
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Union type for all entity prop config types.
|
|
99
|
+
*/
|
|
100
|
+
export type AnyEntityPropConfig = EntityPropConfig | InterfaceEntityPropConfig
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Accumulated builder state (type-level).
|
|
104
|
+
* Grows as you chain builder methods.
|
|
105
|
+
*/
|
|
106
|
+
export interface ComponentBuilderState<
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
108
|
+
TEntityProps extends Record<string, AnyEntityPropConfig> = {},
|
|
109
|
+
TScalarProps extends object = object,
|
|
110
|
+
TRoles extends readonly string[] = readonly string[],
|
|
111
|
+
> {
|
|
112
|
+
readonly __entityProps: TEntityProps
|
|
113
|
+
readonly __scalarProps: TScalarProps
|
|
114
|
+
readonly __roles: TRoles
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// State Update Helpers
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Add an implicit entity to builder state.
|
|
123
|
+
* Entity type comes directly from the EntityDef.
|
|
124
|
+
*/
|
|
125
|
+
export type AddImplicitEntity<
|
|
126
|
+
TState extends ComponentBuilderState,
|
|
127
|
+
TPropName extends string,
|
|
128
|
+
TEntity extends object,
|
|
129
|
+
> = ComponentBuilderState<
|
|
130
|
+
TState['__entityProps'] & {
|
|
131
|
+
readonly [K in TPropName]: ImplicitEntityConfig<TEntity>
|
|
132
|
+
},
|
|
133
|
+
TState['__scalarProps'],
|
|
134
|
+
TState['__roles']
|
|
135
|
+
>
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Add an explicit entity to builder state.
|
|
139
|
+
* Entity type comes directly from the EntityDef.
|
|
140
|
+
*/
|
|
141
|
+
export type AddExplicitEntity<
|
|
142
|
+
TState extends ComponentBuilderState,
|
|
143
|
+
TPropName extends string,
|
|
144
|
+
TEntity extends object,
|
|
145
|
+
TSelected extends object,
|
|
146
|
+
> = ComponentBuilderState<
|
|
147
|
+
TState['__entityProps'] & {
|
|
148
|
+
readonly [K in TPropName]: ExplicitEntityConfig<TEntity, TSelected>
|
|
149
|
+
},
|
|
150
|
+
TState['__scalarProps'],
|
|
151
|
+
TState['__roles']
|
|
152
|
+
>
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Add an implicit interface entity to builder state.
|
|
156
|
+
*/
|
|
157
|
+
export type AddImplicitInterfaceEntity<
|
|
158
|
+
TState extends ComponentBuilderState,
|
|
159
|
+
TPropName extends string,
|
|
160
|
+
TInterface extends object,
|
|
161
|
+
> = ComponentBuilderState<
|
|
162
|
+
TState['__entityProps'] & {
|
|
163
|
+
readonly [K in TPropName]: ImplicitInterfaceEntityConfig<TInterface>
|
|
164
|
+
},
|
|
165
|
+
TState['__scalarProps'],
|
|
166
|
+
TState['__roles']
|
|
167
|
+
>
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Add an explicit interface entity to builder state.
|
|
171
|
+
*/
|
|
172
|
+
export type AddExplicitInterfaceEntity<
|
|
173
|
+
TState extends ComponentBuilderState,
|
|
174
|
+
TPropName extends string,
|
|
175
|
+
TInterface extends object,
|
|
176
|
+
> = ComponentBuilderState<
|
|
177
|
+
TState['__entityProps'] & {
|
|
178
|
+
readonly [K in TPropName]: ExplicitInterfaceEntityConfig<TInterface>
|
|
179
|
+
},
|
|
180
|
+
TState['__scalarProps'],
|
|
181
|
+
TState['__roles']
|
|
182
|
+
>
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Add multiple interface entities to builder state.
|
|
186
|
+
* Each key in TInterfaces becomes a prop.
|
|
187
|
+
*/
|
|
188
|
+
export type AddInterfaces<
|
|
189
|
+
TState extends ComponentBuilderState,
|
|
190
|
+
TInterfaces extends Record<string, object>,
|
|
191
|
+
> = ComponentBuilderState<
|
|
192
|
+
TState['__entityProps'] & {
|
|
193
|
+
readonly [K in keyof TInterfaces]: InterfaceEntityPropConfig<TInterfaces[K]>
|
|
194
|
+
},
|
|
195
|
+
TState['__scalarProps'],
|
|
196
|
+
TState['__roles']
|
|
197
|
+
>
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Type for selectors parameter in interfaces() method.
|
|
201
|
+
* Maps prop names to selector functions.
|
|
202
|
+
*/
|
|
203
|
+
export type InterfaceSelectorsMap<TInterfaces extends Record<string, object>> = {
|
|
204
|
+
readonly [K in keyof TInterfaces]?: (
|
|
205
|
+
e: SelectionBuilder<TInterfaces[K]>,
|
|
206
|
+
) => SelectionBuilder<TInterfaces[K], TInterfaces[K], object>
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set scalar props in builder state.
|
|
211
|
+
*/
|
|
212
|
+
export type SetScalarProps<
|
|
213
|
+
TState extends ComponentBuilderState,
|
|
214
|
+
TNewScalarProps extends object,
|
|
215
|
+
> = ComponentBuilderState<
|
|
216
|
+
TState['__entityProps'],
|
|
217
|
+
TNewScalarProps,
|
|
218
|
+
TState['__roles']
|
|
219
|
+
>
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Props Building
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Build EntityAccessor props from entity config.
|
|
227
|
+
*
|
|
228
|
+
* For explicit selection (selector provided): Returns EntityAccessor with full access
|
|
229
|
+
* - Allows direct field access: `entity.fieldName.value`
|
|
230
|
+
* - Allows relation access: `entity.relation.length`, `entity.relation.$entity`
|
|
231
|
+
*
|
|
232
|
+
* For implicit selection (no selector): Returns EntityAccessorBase with restricted access
|
|
233
|
+
* - Blocks direct field access to enforce declarative patterns
|
|
234
|
+
* - Users must use components: `<Field field={entity.fieldName} />`
|
|
235
|
+
* - Users must use condition DSL: `<If condition={cond.hasItems(entity.relation)} />`
|
|
236
|
+
*/
|
|
237
|
+
export type BuildEntityProps<
|
|
238
|
+
TEntityProps extends Record<string, AnyEntityPropConfig>,
|
|
239
|
+
TRoles extends readonly string[],
|
|
240
|
+
> = {
|
|
241
|
+
readonly [K in keyof TEntityProps]: TEntityProps[K] extends EntityPropConfig<
|
|
242
|
+
infer TEntity,
|
|
243
|
+
infer TSelected,
|
|
244
|
+
infer TIsImplicit
|
|
245
|
+
>
|
|
246
|
+
? TIsImplicit extends true
|
|
247
|
+
// Implicit selection -> restricted EntityAccessorBase (no .value, .length access)
|
|
248
|
+
? EntityAccessorBase<TEntity, TSelected, AnyBrand, string>
|
|
249
|
+
// Explicit selection -> full EntityAccessor (full access)
|
|
250
|
+
: EntityAccessor<TEntity, TSelected, AnyBrand, string>
|
|
251
|
+
: TEntityProps[K] extends InterfaceEntityPropConfig<infer TInterface, infer TIsImplicit>
|
|
252
|
+
? TIsImplicit extends true
|
|
253
|
+
// Implicit interface -> restricted
|
|
254
|
+
? EntityAccessorBase<TInterface, TInterface, AnyBrand, string>
|
|
255
|
+
// Explicit interface -> full access
|
|
256
|
+
: EntityAccessor<TInterface, TInterface, AnyBrand, string>
|
|
257
|
+
: never
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Build complete props type from builder state.
|
|
262
|
+
*/
|
|
263
|
+
export type BuildProps<TState extends ComponentBuilderState> =
|
|
264
|
+
TState['__scalarProps'] &
|
|
265
|
+
BuildEntityProps<TState['__entityProps'], TState['__roles']>
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Build fragment properties ($propName) from entity config.
|
|
269
|
+
*/
|
|
270
|
+
export type BuildFragmentProps<
|
|
271
|
+
TEntityProps extends Record<string, AnyEntityPropConfig>,
|
|
272
|
+
TRoles extends readonly string[],
|
|
273
|
+
> = {
|
|
274
|
+
readonly [K in keyof TEntityProps as `$${K & string}`]: TEntityProps[K] extends EntityPropConfig<
|
|
275
|
+
infer TEntity,
|
|
276
|
+
infer TSelected,
|
|
277
|
+
infer _TIsImplicit
|
|
278
|
+
>
|
|
279
|
+
? FluentFragment<TEntity, TSelected, AnyBrand>
|
|
280
|
+
: TEntityProps[K] extends InterfaceEntityPropConfig<infer TInterface, infer _TIsImplicit>
|
|
281
|
+
? FluentFragment<TInterface, TInterface, AnyBrand>
|
|
282
|
+
: never
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// Result Component Types
|
|
287
|
+
// ============================================================================
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Metadata for entity prop selections.
|
|
291
|
+
*/
|
|
292
|
+
export interface SelectionPropMeta {
|
|
293
|
+
readonly selection: SelectionMeta
|
|
294
|
+
readonly fragment: FluentFragment<unknown, object>
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Base component type with markers.
|
|
299
|
+
*/
|
|
300
|
+
export type BindxComponentBase<TProps extends object> = ComponentType<TProps> &
|
|
301
|
+
SelectionProvider & {
|
|
302
|
+
readonly [COMPONENT_MARKER]: true
|
|
303
|
+
readonly [COMPONENT_SELECTIONS]: Map<string, SelectionPropMeta>
|
|
304
|
+
readonly __componentRoles?: readonly string[]
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Complete bindx component type with fragment properties.
|
|
309
|
+
*/
|
|
310
|
+
export type BindxComponent<
|
|
311
|
+
TState extends ComponentBuilderState,
|
|
312
|
+
> = BindxComponentBase<BuildProps<TState>> &
|
|
313
|
+
BuildFragmentProps<TState['__entityProps'], TState['__roles']>
|
|
314
|
+
|
|
315
|
+
// ============================================================================
|
|
316
|
+
// Builder Interface
|
|
317
|
+
// ============================================================================
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Fluent builder for creating bindx components.
|
|
321
|
+
*
|
|
322
|
+
* @typeParam TState - Accumulated builder state
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```typescript
|
|
326
|
+
* createComponent()
|
|
327
|
+
* .entity('article', schema.Article) // implicit
|
|
328
|
+
* .entity('author', schema.Author, e => e.id().name()) // explicit
|
|
329
|
+
* .props<{ className?: string }>()
|
|
330
|
+
* .render(({ article, author, className }) => ...)
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
export interface ComponentBuilder<
|
|
334
|
+
TState extends ComponentBuilderState = ComponentBuilderState,
|
|
335
|
+
> {
|
|
336
|
+
/**
|
|
337
|
+
* Add an entity prop with implicit selection (collected from JSX).
|
|
338
|
+
* Entity type is resolved based on the builder's roles via ResolveEntity.
|
|
339
|
+
*
|
|
340
|
+
* @param propName - Name of the prop
|
|
341
|
+
* @param entity - EntityDef reference
|
|
342
|
+
*/
|
|
343
|
+
entity<TPropName extends string, TRoleMap extends Record<string, object>>(
|
|
344
|
+
propName: TPropName,
|
|
345
|
+
entity: EntityDef<TRoleMap>,
|
|
346
|
+
): ComponentBuilder<AddImplicitEntity<TState, TPropName, ResolveEntity<TRoleMap, TState['__roles'][number]>>>
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Add an entity prop with explicit selection (from selector function).
|
|
350
|
+
* Entity type is resolved based on the builder's roles via ResolveEntity.
|
|
351
|
+
*
|
|
352
|
+
* @param propName - Name of the prop
|
|
353
|
+
* @param entity - EntityDef reference
|
|
354
|
+
* @param selector - Selection builder function
|
|
355
|
+
*/
|
|
356
|
+
entity<
|
|
357
|
+
TPropName extends string,
|
|
358
|
+
TRoleMap extends Record<string, object>,
|
|
359
|
+
TSelected extends object,
|
|
360
|
+
>(
|
|
361
|
+
propName: TPropName,
|
|
362
|
+
entity: EntityDef<TRoleMap>,
|
|
363
|
+
selector: (e: SelectionBuilder<ResolveEntity<TRoleMap, TState['__roles'][number]>>) => SelectionBuilder<ResolveEntity<TRoleMap, TState['__roles'][number]>, TSelected, object>,
|
|
364
|
+
): ComponentBuilder<AddExplicitEntity<TState, TPropName, ResolveEntity<TRoleMap, TState['__roles'][number]>, TSelected>>
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Add interface-based entity props that accept any EntityRef with matching fields.
|
|
368
|
+
* Each key in TInterfaces becomes a prop name, and the value defines required fields.
|
|
369
|
+
*
|
|
370
|
+
* Props without selectors use implicit mode (selection collected from JSX).
|
|
371
|
+
* Props with selectors use explicit mode (selection from selector function).
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* interface HasName { name: string }
|
|
376
|
+
* interface HasArchivedAt { archivedAt: string | null }
|
|
377
|
+
*
|
|
378
|
+
* // Implicit mode - selection from JSX
|
|
379
|
+
* const NameCard = createComponent()
|
|
380
|
+
* .interfaces<{ item: HasName }>()
|
|
381
|
+
* .render(({ item }) => <span>{item.fields.name.value}</span>)
|
|
382
|
+
*
|
|
383
|
+
* // Explicit mode - selection from selector
|
|
384
|
+
* const ArchiveIndicator = createComponent()
|
|
385
|
+
* .interfaces<{ item: HasArchivedAt }>({
|
|
386
|
+
* item: e => e.archivedAt(),
|
|
387
|
+
* })
|
|
388
|
+
* .render(({ item }) => <span>{item.data?.archivedAt ? 'Archived' : 'Active'}</span>)
|
|
389
|
+
*
|
|
390
|
+
* // Mixed mode - some implicit, some explicit
|
|
391
|
+
* const Card = createComponent()
|
|
392
|
+
* .interfaces<{ item: HasName; status: HasArchivedAt }>({
|
|
393
|
+
* status: e => e.archivedAt(), // explicit
|
|
394
|
+
* // item has no selector -> implicit from JSX
|
|
395
|
+
* })
|
|
396
|
+
* .render(({ item, status }) => ...)
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
interfaces<TInterfaces extends Record<string, object>>(
|
|
400
|
+
selectors?: InterfaceSelectorsMap<TInterfaces>,
|
|
401
|
+
): ComponentBuilder<AddInterfaces<TState, TInterfaces>>
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Define additional scalar (non-entity) props.
|
|
405
|
+
* This is a type-only operation - no runtime effect.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```typescript
|
|
409
|
+
* .props<{ className?: string; onClick?: () => void }>()
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
props<TNewScalarProps extends object>(): ComponentBuilder<SetScalarProps<TState, TNewScalarProps>>
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Add a condition that must be true for the component to render.
|
|
416
|
+
* If the condition is false, the component renders null.
|
|
417
|
+
*
|
|
418
|
+
* Fields used in the condition are automatically collected for the GraphQL query.
|
|
419
|
+
*
|
|
420
|
+
* @param conditionFn - Function that receives entity props and returns a Condition
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```typescript
|
|
424
|
+
* createComponent()
|
|
425
|
+
* .entity('task', schema.Task)
|
|
426
|
+
* .if(({ task }) => cond.isTruthy(task.project.company.canSeeTimeEstimates))
|
|
427
|
+
* .render(({ task }) => (
|
|
428
|
+
* // Only renders if canSeeTimeEstimates is truthy
|
|
429
|
+
* <div>...</div>
|
|
430
|
+
* ))
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
if(conditionFn: (props: BuildEntityProps<TState['__entityProps'], TState['__roles']>) => Condition): ComponentBuilder<TState>
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Build the component with the render function.
|
|
437
|
+
*
|
|
438
|
+
* @param renderFn - React render function receiving typed props
|
|
439
|
+
* @returns Bindx component with fragment properties
|
|
440
|
+
*/
|
|
441
|
+
render(renderFn: (props: BuildProps<TState>) => ReactNode): BindxComponent<TState>
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ============================================================================
|
|
445
|
+
// Factory Types
|
|
446
|
+
// ============================================================================
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Initial builder state with empty entity props and scalar props.
|
|
450
|
+
* Note: Uses {} instead of Record<string, never> to avoid index signature issues
|
|
451
|
+
* when combining with scalar props.
|
|
452
|
+
*/
|
|
453
|
+
export type InitialBuilderState<
|
|
454
|
+
TRoles extends readonly string[] = readonly string[],
|
|
455
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
456
|
+
> = ComponentBuilderState<{}, object, TRoles>
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Options for createComponent.
|
|
460
|
+
*/
|
|
461
|
+
export interface CreateComponentOptions<TRoles extends readonly string[] = readonly string[]> {
|
|
462
|
+
readonly roles?: TRoles
|
|
463
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backwards compatibility exports for createComponent.
|
|
3
|
+
* These are used by other parts of the codebase that haven't been migrated yet.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentType } from 'react'
|
|
7
|
+
import type { SelectionPropMeta } from './componentBuilder.types.js'
|
|
8
|
+
import { COMPONENT_MARKER, COMPONENT_SELECTIONS } from './componentBuilder.js'
|
|
9
|
+
import { BINDX_COMPONENT } from './types.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Assigns $propName fragment properties to a component.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export function assignFragmentProperties(
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
component: ComponentType<any>,
|
|
18
|
+
selectionsMap: Map<string, SelectionPropMeta>,
|
|
19
|
+
): void {
|
|
20
|
+
const result = component as unknown as Record<string, unknown>
|
|
21
|
+
for (const [propName, meta] of selectionsMap) {
|
|
22
|
+
result[`$${propName}`] = meta.fragment
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Assigns standard bindx component markers to a component.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export function assignComponentMarkers(
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
component: ComponentType<any>,
|
|
33
|
+
selectionsMap: Map<string, SelectionPropMeta>,
|
|
34
|
+
): void {
|
|
35
|
+
const comp = component as unknown as Record<symbol, unknown>
|
|
36
|
+
comp[BINDX_COMPONENT] = true
|
|
37
|
+
comp[COMPONENT_MARKER] = true
|
|
38
|
+
comp[COMPONENT_SELECTIONS] = selectionsMap
|
|
39
|
+
}
|