@judo/core
v0.1.1
Published
Core React contexts and hooks for JUDO UI Runtime
Readme
@judo/core
Core React contexts and hooks for JUDO UI Runtime
Purpose
Central state management layer providing application context, navigation, data binding, validation, dispatch, runtime configuration, customizations, and refresh signaling. This is the heart of the React runtime — it bridges model definitions to live React component trees.
Architecture Layer
Layer 3 (Infrastructure) — consumed by components, app-shell, and actions packages.
Dependencies
@judo/model-api— model type definitions@judo/model-loader— model registry@judo/actions— action types (used byDispatchContext)react ^19— React (peer)
File Structure
src/
├── index.ts # Barrel re-export
├── contexts/
│ ├── application-context.tsx # Application/actor context
│ ├── navigation-context.tsx # Page stack + dialog navigation
│ ├── data-context.tsx # DataStore provider
│ ├── dispatch-context.tsx # Action dispatch provider
│ ├── page-context.tsx # Current page metadata
│ ├── refresh-signal-context.tsx # Post-operation refresh signal
│ ├── runtime-config-context.tsx # Feature flags / runtime config
│ ├── validation-context.tsx # Form-level validation state
│ ├── customizations-context.tsx # Component overrides, interceptors, page customizations
│ ├── mui-pro-context.tsx # MUI X Pro component access
│ ├── selector-selection-context.tsx # Selector dialog selection sharing
│ └── index.ts # Barrel re-export for contexts
├── hooks/
│ ├── use-visual-binding.ts # Visual element ↔ data store binding (incl. hiddenBy/enabledBy/requiredBy)
│ ├── use-element-visibility.ts # hiddenBy runtime evaluation for any VisualElement
│ ├── use-element-disabled.ts # enabledBy runtime evaluation for any VisualElement
│ ├── use-validation.ts # Per-element validation + useFormValidation
│ ├── use-data-selector.ts # Selector-based data subscriptions
│ ├── use-edit-mode.ts # Edit/dirty mode detection
│ ├── use-visual-element.ts # Visual element lookup utility
│ ├── use-debounced-callback.ts # Debounced callback for typeahead/autocomplete
│ └── index.ts # Barrel re-export for hooks
├── store/
│ ├── data-store.ts # DataStore class (external store)
│ ├── selectors.ts # Selector factory functions
│ └── index.ts # Barrel re-export for store
└── utils/
├── deep-equal.ts # Deep equality comparison
└── index.ts # Barrel re-export for utilsExports Summary
Context Providers (11)
| Provider | Key Props | Purpose |
| --------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| ApplicationProvider | registry | Subscribes to ModelRegistry via useSyncExternalStore. Exposes active application + actor switching. |
| NavigationProvider | initialPage?, navigate?, getRouteForPage? | Page stack navigation, dialog open/close with callbacks, React Router integration. |
| DataProvider | store? | Creates/wraps a DataStore instance in React context. |
| ValidationProvider | — | Manages field errors (Map) and touched state (Set). |
| PageProvider | pageDefinition, transferId?, params?, isDialog? | Current page metadata. |
| RuntimeConfigProvider | config? | Feature flags (guards, forceShowTotalCount, headerFilters) and MUI Pro license key. |
| DispatchProvider | dispatch | Action dispatch function for the component tree. |
| RefreshSignalProvider | signal | Page-level refresh signal. ViewContainerRenderer increments after successful operations; embedded components watch and re-dispatch refresh logic. |
| CustomizationsProvider | customizations? | Component overrides, interceptors, page-scoped customization bundles. HMR support via ref + version counter. |
| MuiProProvider | isProEnabled, components? | Provides Pro variants of MUI X components (e.g., DataGridPro). Activated when MUI Pro license is set. |
| SelectorSelectionProvider | ownerTransfer? | Shares selection state (via mutable ref) between table and dialog footer in selector dialogs. |
Each provider exports a mandatory hook (useX() — throws if outside provider) and an optional hook (useXOptional() — returns null), except RefreshSignalProvider which provides only useRefreshSignal() (returns 0 when outside provider).
DataStore
| Export | Kind | Description |
| ---------------------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
| TransferData | interface | Shape of transfer data objects with JUDO internal properties (__signedIdentifier, __identifier, etc.). |
| TransferState | interface | Per-transfer tracked state: data, originalData, isDirty, isLoading, error. |
| DataStoreState | interface | Full store state: transfers map + version counter. |
| DataSelector<T> | type | Selector function: (state: DataStoreState) => T. |
| createEmptyTransferState() | function | Factory returning a new empty TransferState. |
| DataStore (class) | class | External store (not React state) with selector-based subscriptions. |
DataStore methods:
| Method | Description |
| ----------------------------------------------- | --------------------------------------------------------------------------------------- |
| subscribe(selector, callback) | Subscribe to a data slice. Only fires when selected value changes. Returns unsubscribe. |
| getSnapshot(selector) | Returns current value for a selector. |
| getState() | Returns full state (debug/test). |
| getTransfer(transferId) | Get transfer state by ID. |
| setTransferData(transferId, data) | Sets transfer data & originalData, resets dirty/loading/error. |
| updateTransferField(transferId, field, value) | Updates one field, recalculates isDirty. Creates transfer if absent. |
| updateTransferFields(transferId, updates) | Batch field updates, recalculates isDirty. |
| setTransferLoading(transferId, loading) | Toggle loading state. |
| setTransferError(transferId, error) | Set error, clears loading. |
| resetTransfer(transferId) | Reverts data to originalData, clears dirty. |
| clearTransfer(transferId) | Removes transfer from store. |
| clearAll() | Clears all transfers. |
Selector Factories
| Function | Description |
| ---------------------------------------- | ----------------------------------------------- |
| selectTransfer(transferId) | Selector for entire transfer state. |
| selectTransferField(transferId, field) | Selector for a single field value. |
| selectTransferLoading(transferId) | Selector for loading state. |
| selectTransferError(transferId) | Selector for error state. |
| selectTransferDirty(transferId) | Selector for dirty state. |
| selectTransferIds | Direct selector returning all transfer IDs. |
| selectVersion | Direct selector returning store version number. |
React Hooks
| Hook | Description |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| useApplication() / useApplicationOptional() | Access application context (ApplicationContextType). |
| useNavigation() / useNavigationOptional() | Access navigation context (page stack, dialogs). |
| useDataStore() / useDataStoreOptional() | Access DataStore instance. |
| useValidationContext() / useValidationContextOptional() | Access validation context (field errors, touched state). |
| usePageContext() / usePageContextOptional() | Access current page metadata. |
| useRuntimeConfig() / useRuntimeConfigOptional() | Access runtime configuration (returns RuntimeConfigContextType). |
| useDispatch() / useDispatchOptional() | Access action dispatch function. |
| useCustomizations() / useCustomizationsOptional() | Access customizations context (component overrides, interceptors, page customizations). |
| useMuiPro() / useMuiProOptional() | Access MUI Pro component context. |
| useSelectorSelection() / useSelectorSelectionOptional() | Access selector dialog selection state. |
| useVisualBinding(element, attributeType?) | Core binding hook. Subscribes to DataStore for attribute data. Resolves label, hidden, disabled, required, readOnly. Integrates hiddenBy, enabledBy, requiredBy dynamic behavior and validation for errors. |
| useElementVisibility(element) | Evaluates runtime hidden state of any VisualElement via hiddenBy dynamic data, static hidden, and customization overrides. Safe outside DataProvider. |
| useElementDisabled(element) | Evaluates runtime disabled state of any VisualElement via enabledBy dynamic data, static disabled, and customization overrides. enabledBy fully controls disabled when present. Safe outside DataProvider. |
| useValidation(element) | Per-element validation. Returns ValidationResult with error (only for touched fields), touch/setError/clearError methods. |
| useFormValidation() | Alias for useValidationContext() — throws if outside ValidationProvider. |
| useDataSelector(selector) | Generic selector subscription via useSyncExternalStore. |
| useTransferState(transferId) | Convenience hook for full transfer state. |
| useTransferField(transferId, field) | Convenience hook for single field value. |
| useTransferLoading(transferId) | Convenience hook for transfer loading state. |
| useTransferDirty(transferId) | Convenience hook for transfer dirty state. |
| useEditMode() | Derives edit mode from PageContext transfer ID + DataStore dirty state. No parameters — reads context directly. |
| useRefreshSignal() | Returns current page-level refresh signal (0 when outside provider). |
| useComponentOverride(element) | Returns custom component for element by sourceId, or null. |
| useComponentInterceptor(element) | Returns intercepted component for element by @type, or null. |
| useSubThemeProvider(element) | Returns the SubThemeHook for an element's subTheme property, or null. |
| usePageActionOverrides(page, action) | Returns action lifecycle overrides for an action on a page. |
| useVisualPropertyOverrides(element) | Returns visual property overrides for an element. |
| useResolvedPageActionOverrides() | Returns all resolved action overrides for current page. |
| useActionLifecycleOverrides(actionSourceId) | Returns lifecycle overrides for a specific action by sourceId. |
| useTypeaheadProvider(elementSourceId) | Returns the typeahead provider for a text input, resolved from page-scoped PageCustomization. |
| useTableRowHighlighting(elementSourceId) | Returns the TableRowHighlightConfig[] for a table element, resolved from page-scoped PageCustomization. |
| useEnumOptionFilter(elementSourceId) | Returns a wrapped filter function for enum input options. Subscribes to transfer data changes for reactive re-evaluation. |
| useDateValidationProps(elementSourceId) | Returns DateValidationProps for a date/datetime input. Subscribes to transfer data changes for reactive re-evaluation. |
| useColumnCustomizers() | Returns all column customizer functions for the current page from PageCustomization.columnCustomizers. |
| useItemContainerConfig(elementSourceId) | Returns ItemContainerConfig for a table element from PageCustomization.itemContainerConfigs. |
| useDebouncedCallback(callback, delay?) | Returns a debounced version of the callback (default 300ms). Used by autocomplete/typeahead. |
| getVisualElement(registry, sourceId) | Utility function (not a hook) — looks up a VisualElement by sourceId from model registry. |
Additional Exported Components
| Export | Kind | Description |
| ----------------------------- | --------- | -------------------------------------------------------------------------------------------------------- |
| PageActionOverridesProvider | component | Calls registered page action hook and provides resolved overrides to descendants. Inside PageProvider. |
| VisualPropertiesProvider | component | Calls registered visual properties hook and provides overrides to descendants. Inside PageProvider. |
Context Types
| Type | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ApplicationContextType | application, applications, actorName, switchActor, switchActorWithNavigation |
| NavigationContextType | Extends NavigationState. Methods: navigateTo, goBack, openDialog, closeDialog, replacePage, clearStack, dialog, canGoBack |
| PageStackEntry | pageDefinition, params?, scrollPosition? |
| NavigationState | pageStack, currentPage, dialogStack |
| DialogState | pageDefinition, size, params? |
| DialogCloseResultType | "cancelled" \| "created" \| "updated" \| "deleted" \| "selected" \| "submitted" |
| DialogCloseResult | Discriminated union for each result type (e.g., { type: "created"; isEager: boolean; data?: unknown }) |
| DialogCloseCallback | (result?: DialogCloseResult) => void |
| PageContextType | pageDefinition, transferId, params, isDialog |
| ValidationContextType | Methods: getError, setError, setErrors, touch, isTouched, clearErrors, clearError, validateAll, getAllErrors, isValid |
| ValidationState | errors: Map<string, string>, touched: Set<string> |
| RuntimeConfig | features?: FeaturesConfig, muiProLicenseKey? |
| FeaturesConfig | guards?, forceShowTotalCountForLazyTables?, headerFilters? |
| RuntimeConfigContextType | { config: RuntimeConfig } |
| DispatchFn | (action: Action) => Promise<unknown> (uses Action from @judo/actions) |
| DispatchContextType | { dispatch: DispatchFn } |
| CustomizationsContextType | version, getPageCustomization, getComponent, getComponentInterceptor, getSubThemeProvider, getRedirectHandler, getCustomRoutes, getMenuCustomizer, getFooterText, getHeroComponent, getSettingsPage, getGuestComponent |
| MuiProContextType | isProEnabled, getComponent(name) |
| SelectorSelectionContextType | selectedTransfersRef (mutable ref), ownerTransfer? |
| VisualBindingResult | value, onChange, error, hidden, disabled, required, label, readOnly, isLoading, isDirty |
| ValidationResult | error, isTouched, touch(), setError(), clearError() |
| EditModeResult | isEditMode, transferId |
Utilities
| Export | Kind | Description |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| deepEqual(a, b) | function | Recursive deep equality comparison. Handles references, null/undefined, primitives, Arrays, Dates, and plain Objects. Used by DataStore for dirty tracking and selector cache stability. |
| DEFAULT_DEBOUNCE_MS | constant | Default debounce delay (300ms) used by useDebouncedCallback. |
Key Patterns
- Mandatory + Optional hook pairs: Every context exports both
useX()(throws) anduseXOptional()(returns null) - External store with selector-based subscriptions:
DataStoreis a plain class; hooks subscribe viauseSyncExternalStore+ selector functions for fine-grained re-renders - Referential stability via deep-equal cache:
useVisualBindingand dynamic behavior hooks use ref-based caches withdeepEqualto ensure referential stability foruseSyncExternalStore - Model-driven binding:
useVisualBindingbridgesVisualElementtoDataStoreby resolvingattributeType.nameas the data field key - Dynamic behavior via
*Byreferences:hiddenBy/enabledBy/requiredByproperties onVisualElementreferenceAttributeTypeinstances — hooks subscribe to the DataStore for those boolean fields and evaluate runtime visibility/disabled/required state enabledByvshiddenByasymmetry:enabledByfully replaces the staticdisabledvalue when present;hiddenByonly overrides to hidden (never clears statichidden=true)- Dialog lifecycle with callbacks:
openDialogaccepts anonClose?: DialogCloseCallbackcallback; dialog close callbacks stored in a ref-based stack (dialogCloseCallbackStackRef) - React Router integration:
NavigationProvideroptionally acceptsnavigateandgetRouteForPageprops useSyncExternalStorethroughout:ApplicationProvider, all data hooks, dynamic behavior hooks, and customization data-reactive hooks use React 18+ tear-free external store pattern- RefreshSignalProvider: Post-operation refresh signaling — ViewContainerRenderer increments a counter after operations; embedded components watch and re-dispatch their refresh logic
- Customizations with HMR support:
CustomizationsProviderstores config in a ref and uses a version counter; when customizations identity changes, version bumps and consumers re-render - Selector selection via mutable ref:
SelectorSelectionProvideruses auseReffor selection state to avoid re-renders; value only read at dispatch time
