173 lines
5.0 KiB
JavaScript
173 lines
5.0 KiB
JavaScript
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
const require_ThemeContext = require('../theme/ThemeContext.cjs');
|
|
const require_A2UIProvider = require('../core/A2UIProvider.cjs');
|
|
let react = require("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 = require_A2UIProvider.useA2UIActions();
|
|
const theme = require_ThemeContext.useTheme();
|
|
const baseId = (0, react.useId)();
|
|
require_A2UIProvider.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 = (0, react.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 = (0, react.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 = (0, react.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 = (0, react.useCallback)((path, value) => {
|
|
actions.setData(node, path, value, surfaceId);
|
|
}, [
|
|
actions,
|
|
node,
|
|
surfaceId
|
|
]);
|
|
/**
|
|
* Get a value from the data model.
|
|
*/
|
|
const getValue = (0, react.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 = (0, react.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 = (0, react.useCallback)((prefix) => {
|
|
return `${prefix}${baseId}`;
|
|
}, [baseId]);
|
|
return (0, react.useMemo)(() => ({
|
|
theme,
|
|
resolveString,
|
|
resolveNumber,
|
|
resolveBoolean,
|
|
setValue,
|
|
getValue,
|
|
sendAction,
|
|
getUniqueId
|
|
}), [
|
|
theme,
|
|
resolveString,
|
|
resolveNumber,
|
|
resolveBoolean,
|
|
setValue,
|
|
getValue,
|
|
sendAction,
|
|
getUniqueId
|
|
]);
|
|
}
|
|
|
|
//#endregion
|
|
exports.useA2UIComponent = useA2UIComponent;
|
|
//# sourceMappingURL=useA2UIComponent.cjs.map
|