@contember/bindx 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/adapter/ContemberAdapter.d.ts +40 -0
- package/dist/adapter/ContemberAdapter.d.ts.map +1 -0
- package/dist/adapter/MockAdapter.d.ts +81 -0
- package/dist/adapter/MockAdapter.d.ts.map +1 -0
- package/dist/adapter/MockQueryEngine.d.ts +43 -0
- package/dist/adapter/MockQueryEngine.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +5 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/types.d.ts +184 -0
- package/dist/adapter/types.d.ts.map +1 -0
- package/dist/brand/ComponentBrand.d.ts +25 -0
- package/dist/brand/ComponentBrand.d.ts.map +1 -0
- package/dist/contember/index.d.ts +10 -0
- package/dist/contember/index.d.ts.map +1 -0
- package/dist/core/ActionDispatcher.d.ts +64 -0
- package/dist/core/ActionDispatcher.d.ts.map +1 -0
- package/dist/core/Disposable.d.ts +57 -0
- package/dist/core/Disposable.d.ts.map +1 -0
- package/dist/core/EntityLoader.d.ts +80 -0
- package/dist/core/EntityLoader.d.ts.map +1 -0
- package/dist/core/PersistenceManager.d.ts +126 -0
- package/dist/core/PersistenceManager.d.ts.map +1 -0
- package/dist/core/SelectionResolver.d.ts +21 -0
- package/dist/core/SelectionResolver.d.ts.map +1 -0
- package/dist/core/actionClassification.d.ts +48 -0
- package/dist/core/actionClassification.d.ts.map +1 -0
- package/dist/core/actions.d.ts +301 -0
- package/dist/core/actions.d.ts.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/dataview/filterHandlers.d.ts +18 -0
- package/dist/dataview/filterHandlers.d.ts.map +1 -0
- package/dist/dataview/index.d.ts +3 -0
- package/dist/dataview/index.d.ts.map +1 -0
- package/dist/dataview/types.d.ts +164 -0
- package/dist/dataview/types.d.ts.map +1 -0
- package/dist/errors/UnfetchedFieldError.d.ts +11 -0
- package/dist/errors/UnfetchedFieldError.d.ts.map +1 -0
- package/dist/errors/errorPathResolver.d.ts +52 -0
- package/dist/errors/errorPathResolver.d.ts.map +1 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/pathMapper.d.ts +42 -0
- package/dist/errors/pathMapper.d.ts.map +1 -0
- package/dist/errors/types.d.ts +131 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/events/EventEmitter.d.ts +97 -0
- package/dist/events/EventEmitter.d.ts.map +1 -0
- package/dist/events/eventFactory.d.ts +40 -0
- package/dist/events/eventFactory.d.ts.map +1 -0
- package/dist/events/index.d.ts +15 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/types.d.ts +264 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/handles/BaseHandle.d.ts +64 -0
- package/dist/handles/BaseHandle.d.ts.map +1 -0
- package/dist/handles/EntityHandle.d.ts +208 -0
- package/dist/handles/EntityHandle.d.ts.map +1 -0
- package/dist/handles/FieldHandle.d.ts +111 -0
- package/dist/handles/FieldHandle.d.ts.map +1 -0
- package/dist/handles/HasManyListHandle.d.ts +166 -0
- package/dist/handles/HasManyListHandle.d.ts.map +1 -0
- package/dist/handles/HasOneHandle.d.ts +168 -0
- package/dist/handles/HasOneHandle.d.ts.map +1 -0
- package/dist/handles/PlaceholderHandle.d.ts +117 -0
- package/dist/handles/PlaceholderHandle.d.ts.map +1 -0
- package/dist/handles/index.d.ts +8 -0
- package/dist/handles/index.d.ts.map +1 -0
- package/dist/handles/proxyFactory.d.ts +66 -0
- package/dist/handles/proxyFactory.d.ts.map +1 -0
- package/dist/handles/types.d.ts +430 -0
- package/dist/handles/types.d.ts.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/internal.d.ts +11 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/persistence/BatchPersister.d.ts +142 -0
- package/dist/persistence/BatchPersister.d.ts.map +1 -0
- package/dist/persistence/ChangeRegistry.d.ts +87 -0
- package/dist/persistence/ChangeRegistry.d.ts.map +1 -0
- package/dist/persistence/ContemberSchemaMutationAdapter.d.ts +16 -0
- package/dist/persistence/ContemberSchemaMutationAdapter.d.ts.map +1 -0
- package/dist/persistence/MutationCollector.d.ts +96 -0
- package/dist/persistence/MutationCollector.d.ts.map +1 -0
- package/dist/persistence/MutationSchemaProvider.d.ts +30 -0
- package/dist/persistence/MutationSchemaProvider.d.ts.map +1 -0
- package/dist/persistence/index.d.ts +7 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/types.d.ts +189 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/roles/RoleSchemaRegistry.d.ts +77 -0
- package/dist/roles/RoleSchemaRegistry.d.ts.map +1 -0
- package/dist/roles/index.d.ts +11 -0
- package/dist/roles/index.d.ts.map +1 -0
- package/dist/roles/types.d.ts +242 -0
- package/dist/roles/types.d.ts.map +1 -0
- package/dist/schema/ContemberSchema.d.ts +98 -0
- package/dist/schema/ContemberSchema.d.ts.map +1 -0
- package/dist/schema/SchemaLoader.d.ts +28 -0
- package/dist/schema/SchemaLoader.d.ts.map +1 -0
- package/dist/schema/SchemaRegistry.d.ts +95 -0
- package/dist/schema/SchemaRegistry.d.ts.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/roles.d.ts +62 -0
- package/dist/schema/roles.d.ts.map +1 -0
- package/dist/schema/types.d.ts +179 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/selection/SelectionMetaCollector.d.ts +32 -0
- package/dist/selection/SelectionMetaCollector.d.ts.map +1 -0
- package/dist/selection/SelectionScope.d.ts +77 -0
- package/dist/selection/SelectionScope.d.ts.map +1 -0
- package/dist/selection/buildQuery.d.ts +45 -0
- package/dist/selection/buildQuery.d.ts.map +1 -0
- package/dist/selection/createFragment.d.ts +29 -0
- package/dist/selection/createFragment.d.ts.map +1 -0
- package/dist/selection/createSelectionBuilder.d.ts +18 -0
- package/dist/selection/createSelectionBuilder.d.ts.map +1 -0
- package/dist/selection/index.d.ts +8 -0
- package/dist/selection/index.d.ts.map +1 -0
- package/dist/selection/queryTypes.d.ts +84 -0
- package/dist/selection/queryTypes.d.ts.map +1 -0
- package/dist/selection/types.d.ts +238 -0
- package/dist/selection/types.d.ts.map +1 -0
- package/dist/store/DirtyTracker.d.ts +26 -0
- package/dist/store/DirtyTracker.d.ts.map +1 -0
- package/dist/store/EntityMetaStore.d.ts +52 -0
- package/dist/store/EntityMetaStore.d.ts.map +1 -0
- package/dist/store/EntitySnapshotStore.d.ts +63 -0
- package/dist/store/EntitySnapshotStore.d.ts.map +1 -0
- package/dist/store/ErrorStore.d.ts +82 -0
- package/dist/store/ErrorStore.d.ts.map +1 -0
- package/dist/store/RelationStore.d.ts +175 -0
- package/dist/store/RelationStore.d.ts.map +1 -0
- package/dist/store/SnapshotStore.d.ts +143 -0
- package/dist/store/SnapshotStore.d.ts.map +1 -0
- package/dist/store/SubscriptionManager.d.ts +90 -0
- package/dist/store/SubscriptionManager.d.ts.map +1 -0
- package/dist/store/TouchedStore.d.ts +24 -0
- package/dist/store/TouchedStore.d.ts.map +1 -0
- package/dist/store/entityId.d.ts +29 -0
- package/dist/store/entityId.d.ts.map +1 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/snapshots.d.ts +144 -0
- package/dist/store/snapshots.d.ts.map +1 -0
- package/dist/undo/UndoManager.d.ts +120 -0
- package/dist/undo/UndoManager.d.ts.map +1 -0
- package/dist/undo/index.d.ts +3 -0
- package/dist/undo/index.d.ts.map +1 -0
- package/dist/undo/types.d.ts +74 -0
- package/dist/undo/types.d.ts.map +1 -0
- package/dist/utils/aliasGenerator.d.ts +25 -0
- package/dist/utils/aliasGenerator.d.ts.map +1 -0
- package/dist/utils/deepEqual.d.ts +6 -0
- package/dist/utils/deepEqual.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +29 -0
- package/src/adapter/ContemberAdapter.ts +239 -0
- package/src/adapter/MockAdapter.ts +394 -0
- package/src/adapter/MockQueryEngine.ts +236 -0
- package/src/adapter/index.ts +21 -0
- package/src/adapter/types.ts +199 -0
- package/src/brand/ComponentBrand.ts +28 -0
- package/src/contember/index.ts +17 -0
- package/src/core/ActionDispatcher.ts +450 -0
- package/src/core/Disposable.ts +126 -0
- package/src/core/EntityLoader.ts +169 -0
- package/src/core/SelectionResolver.ts +78 -0
- package/src/core/actionClassification.ts +196 -0
- package/src/core/actions.ts +491 -0
- package/src/core/index.ts +20 -0
- package/src/dataview/filterHandlers.ts +443 -0
- package/src/dataview/index.ts +36 -0
- package/src/dataview/types.ts +220 -0
- package/src/errors/UnfetchedFieldError.ts +18 -0
- package/src/errors/errorPathResolver.ts +239 -0
- package/src/errors/index.ts +40 -0
- package/src/errors/pathMapper.ts +40 -0
- package/src/errors/types.ts +223 -0
- package/src/events/EventEmitter.ts +447 -0
- package/src/events/eventFactory.ts +430 -0
- package/src/events/index.ts +89 -0
- package/src/events/types.ts +374 -0
- package/src/handles/BaseHandle.ts +100 -0
- package/src/handles/EntityHandle.ts +538 -0
- package/src/handles/FieldHandle.ts +268 -0
- package/src/handles/HasManyListHandle.ts +500 -0
- package/src/handles/HasOneHandle.ts +500 -0
- package/src/handles/PlaceholderHandle.ts +321 -0
- package/src/handles/index.ts +37 -0
- package/src/handles/proxyFactory.ts +218 -0
- package/src/handles/types.ts +743 -0
- package/src/index.ts +384 -0
- package/src/internal.ts +19 -0
- package/src/persistence/BatchPersister.ts +911 -0
- package/src/persistence/ChangeRegistry.ts +150 -0
- package/src/persistence/ContemberSchemaMutationAdapter.ts +57 -0
- package/src/persistence/MutationCollector.ts +567 -0
- package/src/persistence/MutationSchemaProvider.ts +33 -0
- package/src/persistence/index.ts +23 -0
- package/src/persistence/types.ts +220 -0
- package/src/schema/ContemberSchema.ts +132 -0
- package/src/schema/SchemaLoader.ts +93 -0
- package/src/schema/SchemaRegistry.ts +286 -0
- package/src/schema/index.ts +51 -0
- package/src/schema/roles.ts +83 -0
- package/src/schema/types.ts +238 -0
- package/src/selection/SelectionMetaCollector.ts +122 -0
- package/src/selection/SelectionScope.ts +208 -0
- package/src/selection/buildQuery.ts +134 -0
- package/src/selection/createFragment.ts +50 -0
- package/src/selection/createSelectionBuilder.ts +283 -0
- package/src/selection/index.ts +31 -0
- package/src/selection/queryTypes.ts +135 -0
- package/src/selection/types.ts +320 -0
- package/src/store/DirtyTracker.ts +117 -0
- package/src/store/EntityMetaStore.ts +143 -0
- package/src/store/EntitySnapshotStore.ts +260 -0
- package/src/store/ErrorStore.ts +251 -0
- package/src/store/RelationStore.ts +696 -0
- package/src/store/SnapshotStore.ts +765 -0
- package/src/store/SubscriptionManager.ts +241 -0
- package/src/store/TouchedStore.ts +42 -0
- package/src/store/entityId.ts +43 -0
- package/src/store/index.ts +2 -0
- package/src/store/snapshots.ts +203 -0
- package/src/undo/UndoManager.ts +418 -0
- package/src/undo/index.ts +9 -0
- package/src/undo/types.ts +84 -0
- package/src/utils/aliasGenerator.ts +56 -0
- package/src/utils/deepEqual.ts +33 -0
- package/src/utils/index.ts +2 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { EntityRelatedHandle } from './BaseHandle.js'
|
|
2
|
+
import type { ActionDispatcher } from '../core/ActionDispatcher.js'
|
|
3
|
+
import type { SnapshotStore } from '../store/SnapshotStore.js'
|
|
4
|
+
import { addFieldError, clearFieldErrors, setField } from '../core/actions.js'
|
|
5
|
+
import { FIELD_REF_META, type FieldRef, type FieldRefMeta, type InputProps, type Unsubscribe } from './types.js'
|
|
6
|
+
import { deepEqual } from '../utils/deepEqual.js'
|
|
7
|
+
import { createClientError, type ErrorInput, type FieldError } from '../errors/types.js'
|
|
8
|
+
import type {
|
|
9
|
+
FieldChangedEvent,
|
|
10
|
+
FieldChangingEvent,
|
|
11
|
+
EventListener,
|
|
12
|
+
Interceptor,
|
|
13
|
+
} from '../events/types.js'
|
|
14
|
+
import { createAliasProxy } from './proxyFactory.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* FieldHandle provides stable access to a scalar field.
|
|
18
|
+
*
|
|
19
|
+
* Key characteristics:
|
|
20
|
+
* - Stable identity (same instance across renders)
|
|
21
|
+
* - Reads data from SnapshotStore snapshots
|
|
22
|
+
* - Dispatches actions for mutations
|
|
23
|
+
* - Provides inputProps for form integration
|
|
24
|
+
* - Implements FieldRef interface for JSX compatibility
|
|
25
|
+
*/
|
|
26
|
+
export class FieldHandle<T = unknown> extends EntityRelatedHandle implements FieldRef<T> {
|
|
27
|
+
private _cachedInputProps: InputProps<T> | null = null
|
|
28
|
+
private _cachedInputPropsValue: T | null | undefined = undefined
|
|
29
|
+
|
|
30
|
+
private readonly _boundSetValue = (value: T | null): void => this.setValue(value)
|
|
31
|
+
|
|
32
|
+
private constructor(
|
|
33
|
+
entityType: string,
|
|
34
|
+
entityId: string,
|
|
35
|
+
private readonly fieldPath: string[],
|
|
36
|
+
store: SnapshotStore,
|
|
37
|
+
dispatcher: ActionDispatcher,
|
|
38
|
+
private readonly _enumName?: string,
|
|
39
|
+
private readonly _columnType?: string,
|
|
40
|
+
) {
|
|
41
|
+
super(entityType, entityId, store, dispatcher)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static create<T = unknown>(
|
|
45
|
+
entityType: string,
|
|
46
|
+
entityId: string,
|
|
47
|
+
fieldPath: string[],
|
|
48
|
+
store: SnapshotStore,
|
|
49
|
+
dispatcher: ActionDispatcher,
|
|
50
|
+
enumName?: string,
|
|
51
|
+
columnType?: string,
|
|
52
|
+
): FieldHandle<T> {
|
|
53
|
+
return createAliasProxy(new FieldHandle<T>(entityType, entityId, fieldPath, store, dispatcher, enumName, columnType))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* JSX field reference metadata for collection phase.
|
|
58
|
+
* Implements FieldRef interface.
|
|
59
|
+
*/
|
|
60
|
+
get [FIELD_REF_META](): FieldRefMeta {
|
|
61
|
+
return {
|
|
62
|
+
entityType: this.entityType,
|
|
63
|
+
entityId: this.entityId,
|
|
64
|
+
path: this.fieldPath,
|
|
65
|
+
fieldName: this.fieldName,
|
|
66
|
+
isArray: false,
|
|
67
|
+
isRelation: false,
|
|
68
|
+
enumName: this._enumName,
|
|
69
|
+
columnType: this._columnType,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets the current field value.
|
|
75
|
+
*/
|
|
76
|
+
get value(): T | null {
|
|
77
|
+
const data = this.getEntityData()
|
|
78
|
+
if (!data) return null
|
|
79
|
+
return getNestedValue(data, this.fieldPath) as T | null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets the server value (for dirty tracking).
|
|
84
|
+
*/
|
|
85
|
+
get serverValue(): T | null {
|
|
86
|
+
const serverData = this.getServerData()
|
|
87
|
+
if (!serverData) return null
|
|
88
|
+
return getNestedValue(serverData, this.fieldPath) as T | null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Checks if the field has been modified.
|
|
93
|
+
*/
|
|
94
|
+
get isDirty(): boolean {
|
|
95
|
+
return !deepEqual(this.value, this.serverValue)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Checks if the field has been touched (interacted with by the user).
|
|
100
|
+
* Useful for showing errors only after user interaction.
|
|
101
|
+
*/
|
|
102
|
+
get isTouched(): boolean {
|
|
103
|
+
return this.store.isFieldTouched(this.entityType, this.entityId, this.fieldName)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Marks the field as touched.
|
|
108
|
+
* Call this on blur or other interaction events.
|
|
109
|
+
*/
|
|
110
|
+
touch(): void {
|
|
111
|
+
this.assertNotDisposed()
|
|
112
|
+
this.store.setFieldTouched(this.entityType, this.entityId, this.fieldName, true)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Sets the field value.
|
|
117
|
+
* Also clears non-sticky client errors.
|
|
118
|
+
*/
|
|
119
|
+
setValue(value: T | null): void {
|
|
120
|
+
this.assertNotDisposed()
|
|
121
|
+
// Clear non-sticky errors when value changes
|
|
122
|
+
this.store.clearNonStickyFieldErrors(this.entityType, this.entityId, this.fieldName)
|
|
123
|
+
this.dispatcher.dispatch(
|
|
124
|
+
setField(this.entityType, this.entityId, this.fieldPath, value),
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Gets the list of errors on this field.
|
|
130
|
+
*/
|
|
131
|
+
get errors(): readonly FieldError[] {
|
|
132
|
+
return this.store.getFieldErrors(this.entityType, this.entityId, this.fieldName)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Checks if this field has any errors.
|
|
137
|
+
*/
|
|
138
|
+
get hasError(): boolean {
|
|
139
|
+
return this.errors.length > 0
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Adds a client-side error to this field.
|
|
144
|
+
*/
|
|
145
|
+
addError(error: ErrorInput): void {
|
|
146
|
+
this.assertNotDisposed()
|
|
147
|
+
this.dispatcher.dispatch(
|
|
148
|
+
addFieldError(this.entityType, this.entityId, this.fieldName, createClientError(error)),
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clears all errors from this field.
|
|
154
|
+
*/
|
|
155
|
+
clearErrors(): void {
|
|
156
|
+
this.assertNotDisposed()
|
|
157
|
+
this.dispatcher.dispatch(
|
|
158
|
+
clearFieldErrors(this.entityType, this.entityId, this.fieldName),
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets input binding props for form integration.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```tsx
|
|
167
|
+
* <input {...field.inputProps} />
|
|
168
|
+
* // or with custom handler
|
|
169
|
+
* <input
|
|
170
|
+
* value={field.inputProps.value ?? ''}
|
|
171
|
+
* onChange={e => field.inputProps.onChange(e.target.value)}
|
|
172
|
+
* />
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
get inputProps(): InputProps<T> {
|
|
176
|
+
const currentValue = this.value
|
|
177
|
+
if (this._cachedInputProps === null || currentValue !== this._cachedInputPropsValue) {
|
|
178
|
+
this._cachedInputPropsValue = currentValue
|
|
179
|
+
this._cachedInputProps = {
|
|
180
|
+
value: currentValue,
|
|
181
|
+
setValue: this._boundSetValue,
|
|
182
|
+
onChange: this._boundSetValue,
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return this._cachedInputProps
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets the field path.
|
|
190
|
+
*/
|
|
191
|
+
get path(): readonly string[] {
|
|
192
|
+
return this.fieldPath
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Gets the field name (last segment of path).
|
|
197
|
+
*/
|
|
198
|
+
get fieldName(): string {
|
|
199
|
+
return this.fieldPath[this.fieldPath.length - 1] ?? ''
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Creates a nested field handle for a subfield.
|
|
204
|
+
*/
|
|
205
|
+
nested<K extends keyof NonNullable<T>>(
|
|
206
|
+
key: K,
|
|
207
|
+
): FieldHandle<NonNullable<T>[K]> {
|
|
208
|
+
return FieldHandle.create<NonNullable<T>[K]>(
|
|
209
|
+
this.entityType,
|
|
210
|
+
this.entityId,
|
|
211
|
+
[...this.fieldPath, key as string],
|
|
212
|
+
this.store,
|
|
213
|
+
this.dispatcher,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ==================== Event Subscriptions ====================
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Subscribe to field value changes.
|
|
221
|
+
* The listener is called after the value has changed.
|
|
222
|
+
*/
|
|
223
|
+
onChange(listener: EventListener<FieldChangedEvent>): Unsubscribe {
|
|
224
|
+
const emitter = this.dispatcher.getEventEmitter()
|
|
225
|
+
return emitter.onField(
|
|
226
|
+
'field:changed',
|
|
227
|
+
this.entityType,
|
|
228
|
+
this.entityId,
|
|
229
|
+
this.fieldName,
|
|
230
|
+
listener,
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Intercept field value changes.
|
|
236
|
+
* The interceptor can cancel or modify the change.
|
|
237
|
+
*/
|
|
238
|
+
onChanging(interceptor: Interceptor<FieldChangingEvent>): Unsubscribe {
|
|
239
|
+
const emitter = this.dispatcher.getEventEmitter()
|
|
240
|
+
return emitter.interceptField(
|
|
241
|
+
'field:changing',
|
|
242
|
+
this.entityType,
|
|
243
|
+
this.entityId,
|
|
244
|
+
this.fieldName,
|
|
245
|
+
interceptor,
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ==================== Helper Functions ====================
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Gets a nested value from an object using a path array.
|
|
255
|
+
*/
|
|
256
|
+
function getNestedValue(obj: Record<string, unknown>, path: string[]): unknown {
|
|
257
|
+
let current: unknown = obj
|
|
258
|
+
|
|
259
|
+
for (const key of path) {
|
|
260
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
261
|
+
return undefined
|
|
262
|
+
}
|
|
263
|
+
current = (current as Record<string, unknown>)[key]
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return current
|
|
267
|
+
}
|
|
268
|
+
|