rdesign/frontend/node_modules/@copilotkit/a2ui-renderer/dist/react-renderer/hooks/useA2UIComponent.mjs

172 lines
4.7 KiB
JavaScript

import { useTheme } from "../theme/ThemeContext.mjs";
import { useA2UIActions, useA2UIState } from "../core/A2UIProvider.mjs";
import { useCallback, useId, useMemo } from "react";
//#region src/react-renderer/hooks/useA2UIComponent.ts
/**
* Base hook for A2UI components. Provides data binding, theme access,
* and action dispatching.
*
* @param node - The component node from the A2UI message processor
* @param surfaceId - The surface ID this component belongs to
* @returns Object with theme, data binding helpers, and action dispatcher
*
* @example
* ```tsx
* function TextField({ node, surfaceId }: A2UIComponentProps<Types.TextFieldNode>) {
* const { theme, resolveString, setValue } = useA2UIComponent(node, surfaceId);
*
* const label = resolveString(node.properties.label);
* const value = resolveString(node.properties.text) ?? '';
*
* return (
* <div className={classMapToString(theme.components.TextField.container)}>
* <label>{label}</label>
* <input
* value={value}
* onChange={(e) => setValue(node.properties.text?.path!, e.target.value)}
* />
* </div>
* );
* }
* ```
*/
function useA2UIComponent(node, surfaceId) {
const actions = useA2UIActions();
const theme = useTheme();
const baseId = useId();
useA2UIState();
/**
* Resolve a StringValue to its actual string value.
* Checks literalString, literal, then path in that order.
* Note: This reads from data model via stable actions reference.
*/
const resolveString = useCallback((value) => {
if (!value) return null;
if (typeof value !== "object") return null;
if (value.literalString !== void 0) return value.literalString;
if (value.literal !== void 0) return String(value.literal);
if (value.path) {
const data = actions.getData(node, value.path, surfaceId);
return data !== null ? String(data) : null;
}
return null;
}, [
actions,
node,
surfaceId
]);
/**
* Resolve a NumberValue to its actual number value.
*/
const resolveNumber = useCallback((value) => {
if (!value) return null;
if (typeof value !== "object") return null;
if (value.literalNumber !== void 0) return value.literalNumber;
if (value.literal !== void 0) return Number(value.literal);
if (value.path) {
const data = actions.getData(node, value.path, surfaceId);
return data !== null ? Number(data) : null;
}
return null;
}, [
actions,
node,
surfaceId
]);
/**
* Resolve a BooleanValue to its actual boolean value.
*/
const resolveBoolean = useCallback((value) => {
if (!value) return null;
if (typeof value !== "object") return null;
if (value.literalBoolean !== void 0) return value.literalBoolean;
if (value.literal !== void 0) return Boolean(value.literal);
if (value.path) {
const data = actions.getData(node, value.path, surfaceId);
return data !== null ? Boolean(data) : null;
}
return null;
}, [
actions,
node,
surfaceId
]);
/**
* Set a value in the data model for two-way binding.
*/
const setValue = useCallback((path, value) => {
actions.setData(node, path, value, surfaceId);
}, [
actions,
node,
surfaceId
]);
/**
* Get a value from the data model.
*/
const getValue = useCallback((path) => {
return actions.getData(node, path, surfaceId);
}, [
actions,
node,
surfaceId
]);
/**
* Dispatch a user action to the server.
* Resolves all context bindings before dispatching.
*/
const sendAction = useCallback((action) => {
const actionContext = {};
if (action.context) {
for (const item of action.context) if (item.value.literalString !== void 0) actionContext[item.key] = item.value.literalString;
else if (item.value.literalNumber !== void 0) actionContext[item.key] = item.value.literalNumber;
else if (item.value.literalBoolean !== void 0) actionContext[item.key] = item.value.literalBoolean;
else if (item.value.path) {
const resolvedPath = actions.resolvePath(item.value.path, node.dataContextPath);
actionContext[item.key] = actions.getData(node, resolvedPath, surfaceId);
}
}
actions.dispatch({ userAction: {
name: action.name,
sourceComponentId: node.id,
surfaceId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
context: actionContext
} });
}, [
actions,
node,
surfaceId
]);
/**
* Generate a unique ID for accessibility purposes.
* Uses React's useId() for SSR and Concurrent Mode compatibility.
*/
const getUniqueId = useCallback((prefix) => {
return `${prefix}${baseId}`;
}, [baseId]);
return useMemo(() => ({
theme,
resolveString,
resolveNumber,
resolveBoolean,
setValue,
getValue,
sendAction,
getUniqueId
}), [
theme,
resolveString,
resolveNumber,
resolveBoolean,
setValue,
getValue,
sendAction,
getUniqueId
]);
}
//#endregion
export { useA2UIComponent };
//# sourceMappingURL=useA2UIComponent.mjs.map