import { n as useDataSourceRegistryConfig } from "./registry-context-DdfXs3K3.mjs";
import { createContext, memo, useCallback, useContext, useMemo, useRef } from "react";
import { useQueries } from "@tanstack/react-query";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
//#region ../core/src/data-source-api-context.ts
const DataSourceApiContext = createContext(null);
const DataSourceApiProvider = DataSourceApiContext.Provider;
/**
* Access the DataSourceApi adapter without throwing.
* Returns null when no DataSourceApiProvider ancestor exists.
* Use in isolated rendering contexts (Storybook, unit tests) where the
* full provider stack may not be wired.
*/
function useDataSourceApiOptional() {
	return useContext(DataSourceApiContext);
}
//#endregion
//#region ../widgets/src/contexts/RegistryContext.tsx
/**
* Context for providing the widget registry to all components in the tree.
* This eliminates prop drilling and makes the registry accessible anywhere.
*
* Default to undefined - registry must be provided by a RegistryProvider.
*/
const RegistryContext = createContext(void 0);
/**
* Provider component that makes the widget registry available to all descendants.
* A registry must be provided either via props or by nesting within another RegistryProvider.
*/
function RegistryProvider({ registry, children }) {
	const parentRegistry = useContext(RegistryContext);
	const contextValue = registry ?? parentRegistry;
	if (!contextValue) throw new Error("RegistryProvider requires a registry prop or must be nested within another RegistryProvider.");
	return /* @__PURE__ */ jsx(RegistryContext.Provider, {
		value: contextValue,
		children
	});
}
/**
* Hook to access the widget registry from anywhere in the component tree.
* Must be used within a RegistryProvider that has a registry prop.
*/
function useRegistry() {
	const registry = useContext(RegistryContext);
	if (!registry) throw new Error("useRegistry must be used within a RegistryProvider with a registry prop.");
	return registry;
}
//#endregion
//#region ../widgets/src/contexts/ScreenRendererContext.tsx
/**
* Context for providing an alternative ScreenRenderer component.
*
* This allows packages like portal-builder to inject their own ScreenRenderer
* (with edit mode, drag-and-drop, etc.) so that container widgets in portal-widgets
* use the richer renderer instead of the basic view-only one.
*
* Default is undefined — container widgets fall back to the local ScreenRenderer import.
*/
const ScreenRendererContext = createContext(void 0);
/**
* Hook to get the ScreenRenderer component from context.
* Returns undefined if no override has been provided.
*/
function useScreenRenderer() {
	return useContext(ScreenRendererContext);
}
//#endregion
//#region ../core/src/types/widget-schema.ts
/**
* Widget type names as a const object.
* This serves as the single source of truth for widget discriminants.
* Use `as const` for literal type inference (safety-as-const-deep-readonly rule).
*/
const WIDGET_TYPE_NAMES = {
	Alert: "AlertWidget",
	BulletList: "BulletListWidget",
	Calendar: "CalendarWidget",
	Card: "CardWidget",
	Carousel: "CarouselWidget",
	CatchUp: "CatchUpWidget",
	Chart: "ChartWidget",
	Container: "ContainerWidget",
	Embed: "EmbedWidget",
	Image: "ImageWidget",
	Layout: "LayoutWidget",
	Link: "LinkWidget",
	List: "ListWidget",
	MySite: "MySiteWidget",
	Nested: "NestedWidget",
	Points: "PointsWidget",
	QuickLinks: "QuickLinksWidget",
	QuickShare: "QuickShareWidget",
	RecentActivity: "RecentActivityWidget",
	Separator: "SeparatorWidget",
	Spacer: "SpacerWidget",
	Table: "TableWidget",
	Text: "TextWidget",
	ToDo: "ToDoWidget",
	Video: "VideoWidget"
};
/**
* Type predicate to check if a string is a known widget type name.
* Use for runtime validation of widget types.
*
* @example
* if (isWidgetTypeName(widget.type)) {
*   // TypeScript knows widget.type is WidgetTypeName
* }
*/
function isWidgetTypeName(type) {
	return Object.values(WIDGET_TYPE_NAMES).includes(type);
}
/**
* Type predicate to check if a widget has a specific type.
* Enables type-safe widget narrowing without `as` assertions.
*
* @example
* if (isWidgetType(widget, "LayoutWidget")) {
*   // TypeScript knows widget.type === "LayoutWidget"
*   // and widget.props is LayoutWidget props
* }
*/
function isWidgetType(widget, typeName) {
	return widget != null && widget.type === typeName;
}
/**
* Helper for exhaustive switch statements on widget types.
* Use in the default case to ensure all widget types are handled.
*
* @example
* switch (widget.type) {
*   case "AlertWidget": return handleAlert();
*   case "TextWidget": return handleText();
*   // ... all other widget types
*   default: return assertNever(widget.type, "widget type");
* }
*/
function assertNever(value, context) {
	const message = context ? `Unexpected ${context}: ${String(value)}` : `Unexpected value: ${String(value)}`;
	throw new Error(message);
}
/**
* Assertion function that throws if value is undefined.
* Narrows the type to exclude undefined.
*
* @example
* const widget = screen[0];
* assertDefined(widget, "widget at index 0");
* // TypeScript knows widget is defined here
*/
function assertDefined(value, name) {
	if (value == null) throw new Error(name ? `${name} is required` : "Value is required");
}
/** Lookup map for O(1) preset resolution */
const PRESET_MAP = new Map([
	{
		id: "rep-most-shared",
		name: "Most Shared Visits",
		description: "Content with the most share-link click-throughs",
		endpoint: "/v2025-06/reps/{rep_id}/most_shared?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}",
		resultPath: "resources",
		compatibleWidgets: [
			"CarouselWidget",
			"ListWidget",
			"NestedWidget",
			"TableWidget"
		],
		menuField: {
			key: "shareable_type",
			label: "Content Type",
			type: "select",
			options: [
				{
					label: "Products",
					value: "products"
				},
				{
					label: "Media",
					value: "media"
				},
				{
					label: "Pages",
					value: "pages"
				},
				{
					label: "Libraries",
					value: "libraries"
				},
				{
					label: "Enrollment Packs",
					value: "enrollment_packs"
				}
			],
			defaultValue: "products"
		},
		configFields: [{
			key: "limit",
			label: "Limit",
			type: "number",
			defaultValue: 10,
			min: 1,
			max: 20,
			description: "Maximum number of items to show"
		}, {
			key: "period",
			label: "Time Range",
			type: "select",
			options: [
				{
					label: "7 days",
					value: "7d"
				},
				{
					label: "30 days",
					value: "30d"
				},
				{
					label: "90 days",
					value: "90d"
				},
				{
					label: "1 year",
					value: "1y"
				},
				{
					label: "All time",
					value: "all"
				}
			],
			defaultValue: "all"
		}]
	},
	{
		id: "rep-most-viewed",
		name: "Most Viewed",
		description: "Most viewed content",
		endpoint: "/v2025-06/reps/{rep_id}/most_viewed?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}",
		resultPath: "resources",
		compatibleWidgets: [
			"CarouselWidget",
			"ListWidget",
			"NestedWidget",
			"TableWidget"
		],
		menuField: {
			key: "shareable_type",
			label: "Content Type",
			type: "select",
			options: [
				{
					label: "Products",
					value: "products"
				},
				{
					label: "Media",
					value: "media"
				},
				{
					label: "Pages",
					value: "pages"
				},
				{
					label: "Libraries",
					value: "libraries"
				},
				{
					label: "Enrollment Packs",
					value: "enrollment_packs"
				}
			],
			defaultValue: "products"
		},
		configFields: [{
			key: "limit",
			label: "Limit",
			type: "number",
			defaultValue: 10,
			min: 1,
			max: 20,
			description: "Maximum number of items to show"
		}, {
			key: "period",
			label: "Time Range",
			type: "select",
			options: [
				{
					label: "7 days",
					value: "7d"
				},
				{
					label: "30 days",
					value: "30d"
				},
				{
					label: "90 days",
					value: "90d"
				},
				{
					label: "1 year",
					value: "1y"
				},
				{
					label: "All time",
					value: "all"
				}
			],
			defaultValue: "all"
		}]
	},
	{
		id: "customer-orders",
		name: "Orders",
		description: "Customer order history",
		endpoint: "/v202506/orders?customer_id={customer_id}&page[limit]={limit}&status={status}",
		resultPath: "orders",
		transform: "toOrderTableProps",
		compatibleWidgets: ["TableWidget"],
		configFields: [{
			key: "limit",
			label: "Limit",
			type: "number",
			defaultValue: 10,
			min: 1,
			max: 50,
			description: "Maximum number of orders to show"
		}, {
			key: "status",
			label: "Status",
			type: "select",
			options: [
				{
					label: "All",
					value: "all"
				},
				{
					label: "Paid",
					value: "paid"
				},
				{
					label: "Unpaid",
					value: "unpaid"
				},
				{
					label: "Refunded",
					value: "refunded"
				}
			],
			defaultValue: "all"
		}]
	},
	{
		id: "customer-subscriptions",
		name: "Subscriptions",
		description: "Customer subscriptions",
		endpoint: "/subscriptions?customer_id={customer_id}&per_page={limit}&status={status}",
		resultPath: "subscriptions",
		transform: "toSubscriptionTableProps",
		compatibleWidgets: ["TableWidget"],
		configFields: [{
			key: "limit",
			label: "Limit",
			type: "number",
			defaultValue: 10,
			min: 1,
			max: 50,
			description: "Maximum number of subscriptions to show"
		}, {
			key: "status",
			label: "Status",
			type: "select",
			options: [
				{
					label: "All",
					value: "all"
				},
				{
					label: "Active",
					value: "active"
				},
				{
					label: "Paused",
					value: "paused"
				},
				{
					label: "Cancelled",
					value: "cancelled"
				}
			],
			defaultValue: "all"
		}]
	}
].map((p) => [p.id, p]));
/**
* Resolves a preset by ID, returning the current preset definition.
* Returns undefined if the preset ID is not recognized.
*/
function resolvePreset(presetId) {
	return PRESET_MAP.get(presetId);
}
/**
* If the source is an API source with a presetId, returns a copy with
* endpoint, resultPath, and transform resolved from the current preset.
* Falls back to stored values if the preset is unknown.
* Non-API sources are returned as-is.
*/
function resolveSource(source) {
	if (source.type !== "api" || !source.presetId) return source;
	const preset = resolvePreset(source.presetId);
	if (!preset) {
		console.warn(`[DataSource] Unknown preset "${source.presetId}", falling back to stored values`);
		return source;
	}
	return {
		...source,
		endpoint: preset.endpoint,
		resultPath: preset.resultPath,
		transform: preset.transform ?? source.transform
	};
}
//#endregion
//#region ../react/src/data-sources/use-widget-data.ts
/**
* Generates a cache key for the raw data fetch (endpoint/items identity only).
* Transforms and targetProps are NOT included — they are applied per-widget
* after reading from the shared cache.
*/
function getSourceKey(source) {
	if (source.type === "api") {
		const varsKey = source.variables ? `:${JSON.stringify(source.variables)}` : "";
		return `${source.presetId ?? source.endpoint}${varsKey}`;
	}
	if (source.type === "custom") return `custom:${(source.selectedItems?.map((item) => {
		return `${item.shareableType}:${item.id}`;
	}) ?? []).join(",")}`;
	if (source.type === "static") return `static:${source.staticType}:${source.selectedId}`;
	return "unknown";
}
/**
* Resolves cached raw data into widget props by applying each widget's own
* transforms and targetProps mapping.
*/
function resolvePropsFromQueries(queryResults, sources, registry, errorConfig) {
	if (sources.length === 0) return void 0;
	if (queryResults.some((q) => q.isLoading && !q.data)) return void 0;
	const hasError = !!queryResults.find((q) => q.error)?.error;
	if (hasError && errorConfig?.fallback) return errorConfig.fallback;
	if (hasError) return;
	const resolvedProps = {};
	for (let i = 0; i < queryResults.length; i++) {
		const query = queryResults[i];
		const source = sources[i];
		if (!query?.data || !source) continue;
		if (!source.targetProps) continue;
		let result = query.data;
		if (source.transform) {
			const transformer = registry.transformers[source.transform];
			if (transformer) result = transformer(result, source);
			else console.warn(`Transform "${source.transform}" not found in registry`);
		}
		if (result !== null && typeof result === "object" && !Array.isArray(result) && source.targetProps.length > 1) {
			const resultObj = result;
			for (const prop of source.targetProps) if (prop in resultObj) resolvedProps[prop] = resultObj[prop];
		} else for (const prop of source.targetProps) resolvedProps[prop] = result;
	}
	return resolvedProps;
}
/**
* Hook that fetches and resolves data sources for a widget using React Query.
*
* Raw API data is cached and shared across widgets with the same data source.
* Transforms and targetProps mapping are applied per-widget after cache reads,
* so multiple widgets can share one fetch but resolve data independently.
*/
function useWidgetData(widget, options) {
	const config = useDataSourceRegistryConfig();
	const registry = options?.registry ?? config.registry;
	const baseUrl = options?.baseUrl ?? config.baseUrl;
	const getApiHeaders = config.getApiHeaders;
	const variables = config.variables;
	const api = useDataSourceApiOptional() ?? void 0;
	const sources = useMemo(() => (widget.dataSource?.sources ?? []).map(resolveSource), [widget.dataSource?.sources]);
	const errorConfig = widget.dataSource?.error;
	const widgetId = widget.id ?? "unknown";
	const widgetType = widget.type;
	const queryResultsRef = useRef([]);
	const results = useQueries({ queries: sources.map((source) => ({
		queryKey: [
			"portal-widget-data",
			getSourceKey(source),
			baseUrl,
			variables
		],
		queryFn: async ({ signal }) => {
			const context = {
				widgetId,
				widgetType,
				signal,
				baseUrl,
				getApiHeaders,
				variables,
				api
			};
			const fetcher = registry.fetchers[source.type];
			if (!fetcher) throw new Error(`No fetcher registered for source type: ${source.type}`);
			return fetcher(source, context);
		},
		enabled: sources.length > 0,
		retry: errorConfig?.retryCount ?? 0,
		retryDelay: errorConfig?.retryDelay ?? 1e3,
		refetchInterval: source.refreshInterval || false
	})) });
	queryResultsRef.current = results;
	const isLoading = results.some((q) => q.isLoading);
	const failedQuery = results.find((q) => q.error);
	const error = failedQuery?.error instanceof Error ? failedQuery.error : failedQuery?.error ? new Error(String(failedQuery.error)) : null;
	const queryFingerprint = results.map((r) => `${r.dataUpdatedAt}:${r.isLoading}`).join(",");
	const resultsRef = useRef(results);
	resultsRef.current = results;
	return {
		data: useMemo(() => {
			return resolvePropsFromQueries(resultsRef.current.map((q) => ({
				data: q.data,
				isLoading: q.isLoading,
				error: q.error instanceof Error ? q.error : null
			})), sources, registry, errorConfig);
		}, [
			queryFingerprint,
			sources,
			registry,
			errorConfig
		]),
		isLoading,
		error,
		refetch: useCallback(() => {
			queryResultsRef.current.forEach((q) => q.refetch());
		}, [])
	};
}
//#endregion
//#region ../react/src/data-sources/ErrorState.tsx
function ErrorState() {
	return /* @__PURE__ */ jsxs("div", {
		className: "flex min-h-[120px] flex-col items-center justify-center p-6 text-center",
		children: [/* @__PURE__ */ jsx("p", {
			className: "text-lg font-semibold",
			children: "Something Went Wrong"
		}), /* @__PURE__ */ jsx("p", {
			className: "text-muted-foreground text-sm",
			children: "Please contact a company admin for help"
		})]
	});
}
//#endregion
//#region ../react/src/data-sources/DataAwareWidget.tsx
const EMPTY_OBJECT = {};
/**
* Wrapper component that resolves data sources before rendering the widget.
* Merges resolved data with static props.
*/
function DataAwareWidgetImpl({ widget, Component, additionalProps, loadingComponent, errorComponent, baseUrl }) {
	const stableAdditionalProps = additionalProps ?? EMPTY_OBJECT;
	const { data, isLoading, error } = useWidgetData(widget, { baseUrl });
	const mergedProps = useMemo(() => ({
		...widget.props,
		...data,
		...stableAdditionalProps
	}), [
		widget.props,
		data,
		stableAdditionalProps
	]);
	if (isLoading && widget.dataSource?.loading?.showSkeleton !== false) {
		if (loadingComponent) return /* @__PURE__ */ jsx(Fragment$1, { children: loadingComponent });
		return /* @__PURE__ */ jsx("div", {
			className: "bg-muted animate-pulse rounded-md",
			style: { minHeight: 100 },
			children: /* @__PURE__ */ jsx("span", {
				className: "sr-only",
				children: "Loading..."
			})
		});
	}
	if (error && !data) {
		if (errorComponent) return /* @__PURE__ */ jsx(Fragment$1, { children: errorComponent(error) });
		return /* @__PURE__ */ jsx(ErrorState, {});
	}
	return /* @__PURE__ */ jsx(Component, { ...mergedProps });
}
/**
* Memoized wrapper component that resolves data sources before rendering the widget.
* Prevents re-renders when parent re-renders but props haven't changed.
*/
const DataAwareWidget = memo(DataAwareWidgetImpl);
//#endregion
//#region ../widgets/src/core/ScreenRenderer.tsx
/**
* Internal component that uses the registry from context.
* This allows us to avoid prop drilling the registry.
*/
function ScreenRendererContent({ screen, containerWidgetTypes, className }) {
	const registry = useRegistry();
	const isContainerWidget = useCallback((widgetType) => containerWidgetTypes.includes(widgetType), [containerWidgetTypes]);
	const renderWidget = useCallback((widget, index, currentPath) => {
		const Component = registry[widget.type];
		if (!Component) {
			console.warn(`Widget type "${String(widget.type)}" not found in registry`);
			return null;
		}
		const widgetPath = [...currentPath, index];
		const additionalProps = isContainerWidget(widget.type) ? {
			widgetId: widget.id,
			widgetPath
		} : {};
		const props = {
			...widget.props,
			...additionalProps
		};
		if (widget.dataSource) return /* @__PURE__ */ jsx("div", {
			className: "h-full w-full overflow-x-hidden",
			children: /* @__PURE__ */ jsx(DataAwareWidget, {
				widget,
				Component
			})
		}, widget.id || index);
		return /* @__PURE__ */ jsx("div", {
			className: "h-full w-full overflow-x-hidden",
			children: /* @__PURE__ */ jsx(Component, { ...props })
		}, widget.id || index);
	}, [registry, isContainerWidget]);
	return /* @__PURE__ */ jsx("div", {
		className: className || "h-full w-full",
		children: screen?.map((item, index) => {
			if (!item) return null;
			return renderWidget(item, index, []);
		})
	});
}
/**
* ScreenRenderer - View-only component for rendering widget screens.
*
* This is a simplified version designed for the SDK that only handles
* rendering widgets without edit mode or drag-and-drop functionality.
*
* Features:
* - Static rendering of widgets
* - Registry context: No prop drilling for widget registry
* - Container widget support with path tracking
*
* Usage:
* ```tsx
* import { ScreenRenderer } from '@fluid-app/portal-widgets/core';
* import { WIDGET_REGISTRY } from '@fluid-app/portal-widgets/widgets';
*
* <ScreenRenderer
*   screen={widgets}
*   registry={WIDGET_REGISTRY}
* />
* ```
*/
function ScreenRenderer(props) {
	const { screen, registry, containerWidgetTypes = [WIDGET_TYPE_NAMES.Layout, WIDGET_TYPE_NAMES.Container], className } = props;
	return /* @__PURE__ */ jsx(RegistryProvider, {
		...registry !== void 0 ? { registry } : {},
		children: /* @__PURE__ */ jsx(ScreenRendererContent, {
			screen,
			containerWidgetTypes,
			className
		})
	});
}
//#endregion
export { assertNever as a, useScreenRenderer as c, DataSourceApiProvider as d, assertDefined as i, RegistryProvider as l, DataAwareWidget as n, isWidgetType as o, WIDGET_TYPE_NAMES as r, isWidgetTypeName as s, ScreenRenderer as t, useRegistry as u };

//# sourceMappingURL=ScreenRenderer-CuaL4ulD.mjs.map