Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 2x 4x 4x 4x 2x | import { createContext, useContext, ReactNode } from 'react';
import type { Field } from '@quiqr/types';
/**
* Reference to a file in a bundle (used by bundle-manager fields)
*/
export interface FileReference {
src: string;
filename?: string;
thumbnail?: string;
}
/**
* Extended field config with runtime properties added during form initialization.
* The compositeKey is computed to create a unique path identifier for each field instance.
*
* Uses intersection type since Field is a discriminated union that can't be extended.
*/
export type FieldConfig = Field & {
compositeKey: string;
};
/**
* Form metadata used across the form system
*/
export interface FormMeta {
siteKey: string;
workspaceKey: string;
collectionKey: string;
collectionItemKey: string;
prompt_templates: string[];
pageUrl: string;
}
/**
* Main form context value interface.
*
* The form uses nested state to match YAML/frontmatter structure.
* Resources (files) are separated for easier management but merged when saving.
*/
export interface FormContextValue {
// Nested document state (matches YAML structure)
document: Record<string, unknown>;
// Path-based value access
getValueAtPath: <T>(path: string) => T | undefined;
setValueAtPath: (path: string, value: unknown, debounce?: number) => void;
clearValueAtPath: (path: string) => void;
// Separated resources (for bundle-manager fields)
// Keyed by compositeKey
resources: Record<string, FileReference[]>;
getResources: (key: string) => FileReference[];
setResources: (key: string, files: FileReference[], markDirty?: boolean) => void;
// Field configs indexed by compositeKey (preprocessed once at init)
fieldConfigs: Map<string, FieldConfig>;
getFieldConfig: (compositeKey: string) => FieldConfig | undefined;
// Form state
isDirty: boolean;
isSubmitting: boolean;
// Form metadata
meta: FormMeta;
// Per-field cache (for expensive computations)
getCache: (compositeKey: string) => Record<string, unknown>;
// For rendering nested fields (accordion items, etc.)
renderFields: (parentPath: string, fields: Field[]) => ReactNode;
// Save handler
saveForm: () => Promise<void>;
}
const FormContext = createContext<FormContextValue | null>(null);
/**
* Hook to access the form context.
* Must be used within a FormProvider.
*/
export function useFormContext(): FormContextValue {
const ctx = useContext(FormContext);
Iif (!ctx) {
throw new Error('useFormContext must be used within a FormProvider');
}
return ctx;
}
/**
* Consumer component for class components that can't use hooks
*/
export const FormContextConsumer = FormContext.Consumer;
export { FormContext };
export default FormContext;
|