@fluid-app/portal-sdk 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/{AppNavigationContext-DrayF_RG.cjs → AppNavigationContext-DAcrNgXZ.cjs} +1 -1
  2. package/dist/{AppNavigationContext-DrayF_RG.cjs.map → AppNavigationContext-DAcrNgXZ.cjs.map} +1 -1
  3. package/dist/{AppNavigationContext-2nkMoO8F.mjs → AppNavigationContext-v_y8OYHo.mjs} +1 -1
  4. package/dist/{AppNavigationContext-2nkMoO8F.mjs.map → AppNavigationContext-v_y8OYHo.mjs.map} +1 -1
  5. package/dist/{ContactsScreen-GPOgZ-Wi.cjs → ContactsScreen-BL5Jlaz5.cjs} +2 -2
  6. package/dist/{ContactsScreen-GPOgZ-Wi.cjs.map → ContactsScreen-BL5Jlaz5.cjs.map} +1 -1
  7. package/dist/{ContactsScreen-BicHZ37M.mjs → ContactsScreen-CcNUC8xu.mjs} +2 -2
  8. package/dist/{ContactsScreen-BicHZ37M.mjs.map → ContactsScreen-CcNUC8xu.mjs.map} +1 -1
  9. package/dist/{CoreScreenPlaceholder-Cl_zuOBC.cjs → CoreScreenPlaceholder-B64M78iR.cjs} +1 -1
  10. package/dist/{CoreScreenPlaceholder-Cl_zuOBC.cjs.map → CoreScreenPlaceholder-B64M78iR.cjs.map} +1 -1
  11. package/dist/{CoreScreenPlaceholder-Cdyl97Wo.mjs → CoreScreenPlaceholder-DVVLc-OM.mjs} +1 -1
  12. package/dist/{CoreScreenPlaceholder-Cdyl97Wo.mjs.map → CoreScreenPlaceholder-DVVLc-OM.mjs.map} +1 -1
  13. package/dist/{CustomersScreen-Brz5zLkq.mjs → CustomersScreen-SuHGSUdb.mjs} +2 -2
  14. package/dist/{CustomersScreen-Brz5zLkq.mjs.map → CustomersScreen-SuHGSUdb.mjs.map} +1 -1
  15. package/dist/{CustomersScreen-CK1jJhvM.cjs → CustomersScreen-zl_vRzcJ.cjs} +2 -2
  16. package/dist/{CustomersScreen-CK1jJhvM.cjs.map → CustomersScreen-zl_vRzcJ.cjs.map} +1 -1
  17. package/dist/{FluidProvider-PqLUzyvQ.cjs → FluidProvider-BmkSwcgV.cjs} +3 -3
  18. package/dist/{FluidProvider-PqLUzyvQ.cjs.map → FluidProvider-BmkSwcgV.cjs.map} +1 -1
  19. package/dist/{FluidProvider-Bp1sNJnr.mjs → FluidProvider-x96kqsgN.mjs} +3 -3
  20. package/dist/{FluidProvider-Bp1sNJnr.mjs.map → FluidProvider-x96kqsgN.mjs.map} +1 -1
  21. package/dist/LayoutWidget-Dc_wvaLg.mjs.map +1 -1
  22. package/dist/LayoutWidget-fOBC-EML.cjs.map +1 -1
  23. package/dist/{MessagingScreen-9VsNB4fn.mjs → MessagingScreen-BGzfLD8k.mjs} +4 -4
  24. package/dist/{MessagingScreen-C-kF4Z5h.cjs → MessagingScreen-CRLd91tP.cjs} +4 -4
  25. package/dist/{MessagingScreen-C-kF4Z5h.cjs.map → MessagingScreen-CRLd91tP.cjs.map} +1 -1
  26. package/dist/{MessagingScreen-CcxyIep3.cjs → MessagingScreen-DIZ72Tg0.cjs} +4 -4
  27. package/dist/{MessagingScreen-7pC6SdRs.mjs → MessagingScreen-We1B2pka.mjs} +4 -4
  28. package/dist/{MessagingScreen-7pC6SdRs.mjs.map → MessagingScreen-We1B2pka.mjs.map} +1 -1
  29. package/dist/OrdersScreen-Bu-ENmH6.cjs +133 -0
  30. package/dist/OrdersScreen-Bu-ENmH6.cjs.map +1 -0
  31. package/dist/OrdersScreen-TSXDDHZe.cjs +33 -0
  32. package/dist/OrdersScreen-afRAHf07.mjs +126 -0
  33. package/dist/OrdersScreen-afRAHf07.mjs.map +1 -0
  34. package/dist/{ProductsScreen-BKDPaw5H.mjs → ProductsScreen-B-oWUzvD.mjs} +5 -5
  35. package/dist/{ProductsScreen-DwcIB1Tp.mjs → ProductsScreen-ChvK61hX.mjs} +5 -5
  36. package/dist/{ProductsScreen-DwcIB1Tp.mjs.map → ProductsScreen-ChvK61hX.mjs.map} +1 -1
  37. package/dist/{ProductsScreen-DeXWGZXc.cjs → ProductsScreen-CkE2nyuw.cjs} +5 -5
  38. package/dist/{ProductsScreen-DeXWGZXc.cjs.map → ProductsScreen-CkE2nyuw.cjs.map} +1 -1
  39. package/dist/{ProductsScreen-7t1oNY_N.cjs → ProductsScreen-PCg91SbX.cjs} +5 -5
  40. package/dist/ProfileScreen-B6Dp7RLa.cjs +2856 -0
  41. package/dist/ProfileScreen-B6Dp7RLa.cjs.map +1 -0
  42. package/dist/ProfileScreen-B83tzedh.mjs +2849 -0
  43. package/dist/ProfileScreen-B83tzedh.mjs.map +1 -0
  44. package/dist/{AccountScreen-B0p9WJoI.cjs → ProfileScreen-CH3B-IQz.cjs} +5 -5
  45. package/dist/{ShareablesScreen-B-kEPPCb.cjs → ShareablesScreen-Bqj6dtOM.cjs} +5 -5
  46. package/dist/{ShareablesScreen-B-kEPPCb.cjs.map → ShareablesScreen-Bqj6dtOM.cjs.map} +1 -1
  47. package/dist/{ShareablesScreen-Cx87emjz.cjs → ShareablesScreen-BvJIBZvI.cjs} +5 -5
  48. package/dist/{ShareablesScreen-8gKrJQn3.mjs → ShareablesScreen-ChS517hq.mjs} +5 -5
  49. package/dist/{ShareablesScreen-8gKrJQn3.mjs.map → ShareablesScreen-ChS517hq.mjs.map} +1 -1
  50. package/dist/{ShareablesScreen-ClC3YfjZ.mjs → ShareablesScreen-CqVj81Ol.mjs} +5 -5
  51. package/dist/{ShopScreen-BstU0BQe.cjs → ShopScreen-CjoTGnCJ.cjs} +4 -4
  52. package/dist/{ShopScreen-BstU0BQe.cjs.map → ShopScreen-CjoTGnCJ.cjs.map} +1 -1
  53. package/dist/{ShopScreen-Ds4MVzZT.cjs → ShopScreen-DS4p47Ry.cjs} +3 -3
  54. package/dist/{ShopScreen-BMCb4k-O.mjs → ShopScreen-KeU6x3PT.mjs} +4 -4
  55. package/dist/{ShopScreen-BMCb4k-O.mjs.map → ShopScreen-KeU6x3PT.mjs.map} +1 -1
  56. package/dist/SubscriptionsScreen-CHn_Q0zf.cjs +1288 -0
  57. package/dist/SubscriptionsScreen-CHn_Q0zf.cjs.map +1 -0
  58. package/dist/SubscriptionsScreen-D5_eJwBP.mjs +1281 -0
  59. package/dist/SubscriptionsScreen-D5_eJwBP.mjs.map +1 -0
  60. package/dist/SubscriptionsScreen-DGJ_YeX3.cjs +33 -0
  61. package/dist/{TableWidget-D-r0EQ_2.cjs → TableWidget-2ttj9H5g.cjs} +10 -7
  62. package/dist/TableWidget-2ttj9H5g.cjs.map +1 -0
  63. package/dist/{TableWidget-D_6Qi3m8.mjs → TableWidget-BbjQhI2A.mjs} +10 -7
  64. package/dist/TableWidget-BbjQhI2A.mjs.map +1 -0
  65. package/dist/{TableWidget-1Bp4ZHK8.cjs → TableWidget-eVjxq7Lv.cjs} +1 -1
  66. package/dist/{es-kNOrmozy.cjs → es-BtechuHV.cjs} +1 -1
  67. package/dist/{es-kNOrmozy.cjs.map → es-BtechuHV.cjs.map} +1 -1
  68. package/dist/{es-BL8VBBDa.mjs → es-DxWiENwN.mjs} +1 -1
  69. package/dist/{es-BL8VBBDa.mjs.map → es-DxWiENwN.mjs.map} +1 -1
  70. package/dist/index.cjs +85 -91
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +40 -8
  73. package/dist/index.d.cts.map +1 -1
  74. package/dist/index.d.mts +40 -8
  75. package/dist/index.d.mts.map +1 -1
  76. package/dist/index.mjs +80 -88
  77. package/dist/index.mjs.map +1 -1
  78. package/dist/{products-Bu0IiW0A.mjs → products-BAFSvGSa.mjs} +2 -2
  79. package/dist/{products-Bu0IiW0A.mjs.map → products-BAFSvGSa.mjs.map} +1 -1
  80. package/dist/{products-CzuNSVSG.cjs → products-CH5kIItT.cjs} +2 -2
  81. package/dist/{products-CzuNSVSG.cjs.map → products-CH5kIItT.cjs.map} +1 -1
  82. package/dist/registries-4OQmN6DO.cjs.map +1 -1
  83. package/dist/registries-Qk8P123O.mjs.map +1 -1
  84. package/dist/src-BZEkCfd4.mjs +973 -0
  85. package/dist/src-BZEkCfd4.mjs.map +1 -0
  86. package/dist/src-DXC-Jw6i.cjs +1009 -0
  87. package/dist/src-DXC-Jw6i.cjs.map +1 -0
  88. package/dist/{src-MZ0kePDW.cjs → src-vxm9rMYT.cjs} +3 -3
  89. package/dist/{src-MZ0kePDW.cjs.map → src-vxm9rMYT.cjs.map} +1 -1
  90. package/dist/{src-BFKgg3tf.mjs → src-wpQFW94i.mjs} +3 -3
  91. package/dist/{src-BFKgg3tf.mjs.map → src-wpQFW94i.mjs.map} +1 -1
  92. package/dist/use-account-clients-DYTyFvdN.mjs +134 -0
  93. package/dist/use-account-clients-DYTyFvdN.mjs.map +1 -0
  94. package/dist/use-account-clients-n2a8bdSP.cjs +182 -0
  95. package/dist/use-account-clients-n2a8bdSP.cjs.map +1 -0
  96. package/dist/{use-fluid-api-CTRIRz1H.cjs → use-fluid-api-DvJuEWRf.cjs} +2 -2
  97. package/dist/{use-fluid-api-CTRIRz1H.cjs.map → use-fluid-api-DvJuEWRf.cjs.map} +1 -1
  98. package/dist/{use-fluid-api-dCyPDaGZ.mjs → use-fluid-api-Zq9sZ5vk.mjs} +2 -2
  99. package/dist/{use-fluid-api-dCyPDaGZ.mjs.map → use-fluid-api-Zq9sZ5vk.mjs.map} +1 -1
  100. package/package.json +6 -6
  101. package/dist/AccountScreen-BigIbqV5.mjs +0 -5309
  102. package/dist/AccountScreen-BigIbqV5.mjs.map +0 -1
  103. package/dist/AccountScreen-DTv1-206.cjs +0 -5322
  104. package/dist/AccountScreen-DTv1-206.cjs.map +0 -1
  105. package/dist/OrdersScreen-DGt-CTBS.mjs +0 -24
  106. package/dist/OrdersScreen-DGt-CTBS.mjs.map +0 -1
  107. package/dist/OrdersScreen-Dy-JChVQ.cjs +0 -41
  108. package/dist/OrdersScreen-Dy-JChVQ.cjs.map +0 -1
  109. package/dist/TableWidget-D-r0EQ_2.cjs.map +0 -1
  110. package/dist/TableWidget-D_6Qi3m8.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutWidget-Dc_wvaLg.mjs","names":["ScreenRenderer","DefaultScreenRenderer"],"sources":["../../widgets/src/contexts/RegistryContext.tsx","../../widgets/src/contexts/ScreenRendererContext.tsx","../../core/src/types/widget-schema.ts","../../core/src/types/field-types.ts","../../core/src/widget-utils/widget-utils.ts","../../widgets/src/core/ScreenRenderer.tsx","../../widgets/src/widgets/LayoutWidget.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base props interface for widget components.\n * All widgets receive at least these props from the ScreenRenderer.\n */\nexport interface WidgetBaseProps {\n readonly widget: WidgetSchema;\n readonly path?: readonly number[];\n}\n\n/**\n * Generic widget component type.\n * Widgets receive WidgetBaseProps and may have additional props.\n */\nexport type WidgetComponent<P extends WidgetBaseProps = WidgetBaseProps> =\n ComponentType<P>;\n\n/**\n * Registry mapping widget type names to their components.\n * The keys are widget type strings (e.g., \"TextWidget\", \"ButtonWidget\").\n * Using WidgetComponent for the value type provides better type safety than `any`.\n */\nexport type WidgetRegistry = Readonly<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Widget props vary by type\n Record<string, WidgetComponent<any>>\n>;\n\n/**\n * Context for providing the widget registry to all components in the tree.\n * This eliminates prop drilling and makes the registry accessible anywhere.\n *\n * Default to undefined - registry must be provided by a RegistryProvider.\n */\nexport const RegistryContext: React.Context<WidgetRegistry | undefined> =\n createContext<WidgetRegistry | undefined>(undefined);\n\nexport interface RegistryProviderProps {\n registry?: WidgetRegistry;\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the widget registry available to all descendants.\n * A registry must be provided either via props or by nesting within another RegistryProvider.\n */\nexport function RegistryProvider({\n registry,\n children,\n}: RegistryProviderProps): React.JSX.Element {\n // If no registry provided, inherit from parent provider\n const parentRegistry = useContext(RegistryContext);\n const contextValue = registry ?? parentRegistry;\n\n if (!contextValue) {\n throw new Error(\n \"RegistryProvider requires a registry prop or must be nested within another RegistryProvider.\",\n );\n }\n\n return (\n <RegistryContext.Provider value={contextValue}>\n {children}\n </RegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the widget registry from anywhere in the component tree.\n * Must be used within a RegistryProvider that has a registry prop.\n */\nexport function useRegistry(): WidgetRegistry {\n const registry = useContext(RegistryContext);\n\n if (!registry) {\n throw new Error(\n \"useRegistry must be used within a RegistryProvider with a registry prop.\",\n );\n }\n\n return registry;\n}\n","import React, { createContext, useContext, type ComponentType } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { WidgetRegistry } from \"./RegistryContext\";\n\n/**\n * Props that any ScreenRenderer must accept.\n * This is the minimal interface used by container widgets (e.g. LayoutWidget)\n * to render their children.\n */\nexport interface ScreenRendererComponentProps {\n screen: WidgetSchema[];\n registry?: WidgetRegistry;\n className?: string;\n}\n\n/**\n * Context for providing an alternative ScreenRenderer component.\n *\n * This allows packages like portal-builder to inject their own ScreenRenderer\n * (with edit mode, drag-and-drop, etc.) so that container widgets in portal-widgets\n * use the richer renderer instead of the basic view-only one.\n *\n * Default is undefined — container widgets fall back to the local ScreenRenderer import.\n */\nexport const ScreenRendererContext: React.Context<\n ComponentType<ScreenRendererComponentProps> | undefined\n> = createContext<ComponentType<ScreenRendererComponentProps> | undefined>(\n undefined,\n);\n\n/**\n * Hook to get the ScreenRenderer component from context.\n * Returns undefined if no override has been provided.\n */\nexport function useScreenRenderer():\n | ComponentType<ScreenRendererComponentProps>\n | undefined {\n return useContext(ScreenRendererContext);\n}\n","import type { ComponentType } from \"react\";\nimport type { DataSourceConfig } from \"../data-sources/types\";\n\n/**\n * Widget type names as a const object.\n * This serves as the single source of truth for widget discriminants.\n * Use `as const` for literal type inference (safety-as-const-deep-readonly rule).\n */\nexport const WIDGET_TYPE_NAMES = {\n Alert: \"AlertWidget\",\n Calendar: \"CalendarWidget\",\n Carousel: \"CarouselWidget\",\n CatchUp: \"CatchUpWidget\",\n Chart: \"ChartWidget\",\n Container: \"ContainerWidget\",\n Embed: \"EmbedWidget\",\n Image: \"ImageWidget\",\n Layout: \"LayoutWidget\",\n List: \"ListWidget\",\n MySite: \"MySiteWidget\",\n Nested: \"NestedWidget\",\n QuickShare: \"QuickShareWidget\",\n RecentActivity: \"RecentActivityWidget\",\n Spacer: \"SpacerWidget\",\n Table: \"TableWidget\",\n Text: \"TextWidget\",\n ToDo: \"ToDoWidget\",\n Video: \"VideoWidget\",\n} as const;\n\n/**\n * Union of all known widget type names.\n * Derived from WIDGET_TYPE_NAMES to avoid duplication (deriving-typeof-for-object-keys rule).\n */\nexport type WidgetTypeName =\n (typeof WIDGET_TYPE_NAMES)[keyof typeof WIDGET_TYPE_NAMES];\n\n/**\n * Legacy alias for backwards compatibility.\n * Prefer using WidgetTypeName for new code when you need the union type.\n */\nexport type WidgetType = string;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type WidgetRegistry = Record<WidgetType, ComponentType<any>>;\n\n/**\n * Base widget schema with loose typing for runtime data.\n * Use TypedWidgetSchema<T> when you have a known registry for better type safety.\n */\nexport type WidgetSchema = {\n readonly type: WidgetType;\n readonly props: Readonly<Record<string, unknown>>;\n readonly id?: string; // Optional unique identifier for drag-and-drop\n /** Optional data source configuration for data-bound widgets */\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n};\n\n/**\n * Type-safe widget schema based on registry.\n * Uses discriminated unions - the `type` field serves as discriminant.\n * When narrowed (e.g., `if (widget.type === \"AlertWidget\")`),\n * TypeScript automatically knows the correct props type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TypedWidgetSchema<T extends Record<string, ComponentType<any>>> = {\n [K in keyof T]: {\n readonly type: K;\n readonly props: Readonly<React.ComponentProps<T[K]>>;\n readonly id?: string;\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n };\n}[keyof T];\n\n/**\n * Widget path in the tree - array of indices.\n * Readonly tuple to prevent accidental mutation.\n */\nexport type WidgetPath = readonly number[];\n\n// ============================================================================\n// Type Guards and Assertion Functions\n// ============================================================================\n\n/**\n * Type predicate to check if a string is a known widget type name.\n * Use for runtime validation of widget types.\n *\n * @example\n * if (isWidgetTypeName(widget.type)) {\n * // TypeScript knows widget.type is WidgetTypeName\n * }\n */\nexport function isWidgetTypeName(type: string): type is WidgetTypeName {\n // Type assertion required in type guard: Object.values() returns string[], but we\n // need to check against WidgetTypeName values. The assertion is safe because we're\n // checking membership, and the return type predicate ensures correct narrowing.\n return Object.values(WIDGET_TYPE_NAMES).includes(type as WidgetTypeName);\n}\n\n/**\n * Type predicate to check if a widget has a specific type.\n * Enables type-safe widget narrowing without `as` assertions.\n *\n * @example\n * if (isWidgetType(widget, \"LayoutWidget\")) {\n * // TypeScript knows widget.type === \"LayoutWidget\"\n * // and widget.props is LayoutWidget props\n * }\n */\nexport function isWidgetType<T extends WidgetTypeName>(\n widget: WidgetSchema | null | undefined,\n typeName: T,\n): widget is WidgetSchema & { readonly type: T } {\n return widget != null && widget.type === typeName;\n}\n\n/**\n * Helper for exhaustive switch statements on widget types.\n * Use in the default case to ensure all widget types are handled.\n *\n * @example\n * switch (widget.type) {\n * case \"AlertWidget\": return handleAlert();\n * case \"TextWidget\": return handleText();\n * // ... all other widget types\n * default: return assertNever(widget.type, \"widget type\");\n * }\n */\nexport function assertNever(value: never, context?: string): never {\n const message = context\n ? `Unexpected ${context}: ${String(value)}`\n : `Unexpected value: ${String(value)}`;\n throw new Error(message);\n}\n\n/**\n * Assertion function that throws if value is undefined.\n * Narrows the type to exclude undefined.\n *\n * @example\n * const widget = screen[0];\n * assertDefined(widget, \"widget at index 0\");\n * // TypeScript knows widget is defined here\n */\nexport function assertDefined<T>(\n value: T | undefined | null,\n name?: string,\n): asserts value is T {\n if (value == null) {\n throw new Error(name ? `${name} is required` : \"Value is required\");\n }\n}\n","import type { ShareableItem } from \"./shareable-item\";\n\n// ============================================================================\n// Color Options - Derive type from constant for single source of truth\n// ============================================================================\n\n/**\n * Color options constant - single source of truth for color values.\n * Use COLOR_OPTIONS.primary instead of \"primary\" for type-safe comparisons.\n */\nexport const COLOR_OPTIONS = {\n background: \"background\",\n foreground: \"foreground\",\n primary: \"primary\",\n secondary: \"secondary\",\n accent: \"accent\",\n muted: \"muted\",\n destructive: \"destructive\",\n} as const;\n\n/**\n * Union type of all color options, derived from COLOR_OPTIONS constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type ColorOptions = (typeof COLOR_OPTIONS)[keyof typeof COLOR_OPTIONS];\n\n// ============================================================================\n// Size Options - Derive types from constants for single source of truth\n// ============================================================================\n\nexport const FONT_SIZE_OPTIONS = {\n \"2xl\": \"2xl\",\n xl: \"xl\",\n lg: \"lg\",\n md: \"md\",\n sm: \"sm\",\n xs: \"xs\",\n} as const;\n\nexport type FontSizeOptions =\n (typeof FONT_SIZE_OPTIONS)[keyof typeof FONT_SIZE_OPTIONS];\n\nexport const BORDER_RADIUS_OPTIONS = {\n none: \"none\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n full: \"full\",\n} as const;\n\nexport type BorderRadiusOptions =\n (typeof BORDER_RADIUS_OPTIONS)[keyof typeof BORDER_RADIUS_OPTIONS];\n\n/** Padding values - numeric, so we use a tuple for derivation */\nexport const PADDING_VALUES = [0, 2, 4, 6, 8, 10] as const;\nexport type PaddingOptions = (typeof PADDING_VALUES)[number];\n\nexport const BUTTON_SIZE_OPTIONS = {\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type ButtonSizeOptions =\n (typeof BUTTON_SIZE_OPTIONS)[keyof typeof BUTTON_SIZE_OPTIONS];\n\nexport const GAP_OPTIONS = {\n none: \"none\",\n xs: \"xs\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type GapOptions = (typeof GAP_OPTIONS)[keyof typeof GAP_OPTIONS];\n\n// ============================================================================\n// Alignment Options - Derive from constants\n// ============================================================================\n\nexport const VERTICAL_ALIGN_OPTIONS = {\n top: \"top\",\n center: \"center\",\n bottom: \"bottom\",\n} as const;\n\nexport type VerticalAlign =\n (typeof VERTICAL_ALIGN_OPTIONS)[keyof typeof VERTICAL_ALIGN_OPTIONS];\n\nexport const HORIZONTAL_ALIGN_OPTIONS = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n} as const;\n\nexport type HorizontalAlign =\n (typeof HORIZONTAL_ALIGN_OPTIONS)[keyof typeof HORIZONTAL_ALIGN_OPTIONS];\n\nexport type AlignOptions = {\n vertical?: VerticalAlign;\n horizontal?: HorizontalAlign;\n};\n\n// ============================================================================\n// Background Options - Derive from constant\n// ============================================================================\n\nexport const BACKGROUND_TYPES = {\n solid: \"solid\",\n image: \"image\",\n} as const;\n\nexport type BackgroundType =\n (typeof BACKGROUND_TYPES)[keyof typeof BACKGROUND_TYPES];\nexport interface BackgroundValue {\n type: BackgroundType;\n color?: ColorOptions;\n resource?: ShareableItem;\n}\n\n// ============================================================================\n// Section Layout - Derive type from config keys (single source of truth)\n// ============================================================================\n\n/**\n * Section layout configuration - single source of truth for layout types.\n * SectionLayoutType is derived from these keys to prevent drift.\n */\nexport const SECTION_LAYOUT_CONFIG: {\n readonly \"single-column\": {\n readonly columns: 1;\n readonly widths: readonly [\"1fr\"];\n readonly gridClasses: \"\";\n };\n readonly \"2c-equal\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-2\";\n };\n readonly \"2c-left-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[2fr_1fr]\";\n };\n readonly \"2c-right-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"2fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr]\";\n };\n readonly \"2c-left-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"3fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_3fr]\";\n };\n readonly \"2c-right-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"3fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[3fr_1fr]\";\n };\n readonly \"3c-equal\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-3\";\n };\n readonly \"3c-middle-wider\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\";\n };\n} = {\n \"single-column\": { columns: 1, widths: [\"1fr\"], gridClasses: \"\" },\n \"2c-equal\": {\n columns: 2,\n widths: [\"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-2\",\n },\n \"2c-left-wider\": {\n columns: 2,\n widths: [\"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[2fr_1fr]\",\n },\n \"2c-right-wider\": {\n columns: 2,\n widths: [\"1fr\", \"2fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr]\",\n },\n \"2c-left-narrow\": {\n columns: 2,\n widths: [\"1fr\", \"3fr\"],\n gridClasses: \"@md:grid-cols-[1fr_3fr]\",\n },\n \"2c-right-narrow\": {\n columns: 2,\n widths: [\"3fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[3fr_1fr]\",\n },\n \"3c-equal\": {\n columns: 3,\n widths: [\"1fr\", \"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-3\",\n },\n \"3c-middle-wider\": {\n columns: 3,\n widths: [\"1fr\", \"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\",\n },\n} as const satisfies Record<\n string,\n {\n readonly columns: number;\n readonly widths: readonly string[];\n readonly gridClasses: string;\n }\n>;\n\n/**\n * Union type of all section layout types, derived from SECTION_LAYOUT_CONFIG keys.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type SectionLayoutType = keyof typeof SECTION_LAYOUT_CONFIG;\n\n/** @deprecated Use SECTION_LAYOUT_CONFIG instead */\nexport const sectionLayoutConfig: typeof SECTION_LAYOUT_CONFIG =\n SECTION_LAYOUT_CONFIG;\n","import type { WidgetSchema, WidgetPath } from \"../types\";\nimport { WIDGET_TYPE_NAMES, isWidgetType } from \"../types\";\n\n/**\n * Generates a unique ID for a widget\n */\nexport function generateWidgetId(type?: string): string {\n const uuid = crypto.randomUUID();\n return type ? `${type}-${uuid}` : uuid;\n}\n\n/**\n * Converts a widget path to a unique string identifier\n */\nexport function pathToId(path: WidgetPath): string {\n return path.join(\"-\");\n}\n\n/**\n * Converts a path string back to a WidgetPath array\n */\nexport function idToPath(id: string): WidgetPath {\n return id.split(\"-\").map((segment) => parseInt(segment, 10));\n}\n\n/**\n * Ensures all widgets in an array have valid IDs\n * Note: Handles null values for sparse arrays (e.g., grid layouts with empty cells)\n */\nexport function ensureWidgetIds(\n widgets: readonly (WidgetSchema | null)[],\n): (WidgetSchema | null)[] {\n return widgets?.map((widget, index) => {\n if (!widget) return null;\n return {\n ...widget,\n id: widget.id || `widget-${index}-${crypto.randomUUID()}`,\n };\n });\n}\n\n/**\n * Deep clones a widget and regenerates all IDs (including nested children)\n */\nexport function deepCloneWidget(widget: WidgetSchema): WidgetSchema {\n // Handle LayoutWidget children recursively\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (\n isWidgetType(widget, WIDGET_TYPE_NAMES.Layout) &&\n Array.isArray(widget.props.children)\n ) {\n // Build cloned widget with recursively cloned children\n return {\n type: widget.type,\n props: {\n ...widget.props,\n children: (\n widget.props.children as readonly (WidgetSchema | null)[]\n ).map((child) => (child ? deepCloneWidget(child) : null)),\n },\n id: generateWidgetId(widget.type),\n };\n }\n\n // Clone non-container widget\n return {\n type: widget.type,\n props: { ...widget.props },\n id: generateWidgetId(widget.type),\n };\n}\n\n/**\n * Deep clones an array of widgets and regenerates all IDs\n */\nexport function deepCloneWidgets(\n widgets: readonly WidgetSchema[],\n): WidgetSchema[] {\n return widgets.map((widget) => deepCloneWidget(widget));\n}\n\n/**\n * Gets a widget at a specific path in the widget tree\n */\nexport function getWidgetByPath(\n screen: readonly (WidgetSchema | null)[],\n path: WidgetPath,\n): WidgetSchema | null {\n if (path.length === 0 || path[0] === undefined) return null;\n\n let current: WidgetSchema | null = screen[path[0]] ?? null;\n\n for (let i = 1; i < path.length; i++) {\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (!isWidgetType(current, WIDGET_TYPE_NAMES.Layout)) return null;\n\n const children = (current.props.children ??\n []) as readonly (WidgetSchema | null)[];\n const index = path[i];\n if (index === undefined) return null;\n current = children[index] ?? null;\n }\n\n return current;\n}\n\n/**\n * Groups children by column index.\n * Accepts nullable children (sparse arrays from grid layouts) and filters them out.\n */\nexport function groupChildrenByColumn(\n children: readonly (WidgetSchema | null)[],\n columnCount: number,\n): WidgetSchema[][] {\n // Use explicit type annotation to avoid never[] inference (narrowing-empty-array-type rule)\n const columns: WidgetSchema[][] = Array.from(\n { length: columnCount },\n (): WidgetSchema[] => [],\n );\n\n // Filter out nulls first, then process (truthiness narrowing rule)\n // This avoids the narrowing-callback-scope issue since we work with non-null array\n const nonNullChildren = children.filter(\n (child): child is WidgetSchema => child !== null,\n );\n\n for (const child of nonNullChildren) {\n // Default to column 0 if no columnIndex specified\n const colIndex = Math.min(child.columnIndex ?? 0, columnCount - 1);\n const column = columns[colIndex];\n if (column) {\n column.push(child);\n }\n }\n\n return columns;\n}\n","import type { ComponentType } from \"react\";\nimport { useCallback } from \"react\";\nimport type React from \"react\";\nimport type {\n WidgetSchema,\n TypedWidgetSchema,\n WidgetPath,\n} from \"@fluid-app/portal-core/types\";\nimport { WIDGET_TYPE_NAMES } from \"@fluid-app/portal-core/types\";\nimport { RegistryProvider, useRegistry } from \"../contexts/RegistryContext\";\n\nexport interface ScreenRendererProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n> {\n /** Array of widget schemas to render */\n screen: TypedWidgetSchema<T>[] | WidgetSchema[];\n /** Widget registry mapping type names to components */\n registry?: T;\n /** Container widgets (like LayoutWidget) receive these additional props */\n containerWidgetTypes?: string[];\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\ninterface ScreenRendererContentProps {\n screen: WidgetSchema[];\n containerWidgetTypes: string[];\n className?: string | undefined;\n}\n\n/**\n * Internal component that uses the registry from context.\n * This allows us to avoid prop drilling the registry.\n */\nfunction ScreenRendererContent({\n screen,\n containerWidgetTypes,\n className,\n}: ScreenRendererContentProps) {\n const registry = useRegistry();\n\n // Check if a widget type is a container\n const isContainerWidget = useCallback(\n (widgetType: string) => containerWidgetTypes.includes(widgetType),\n [containerWidgetTypes],\n );\n\n // Render widgets recursively with path tracking\n const renderWidget = useCallback(\n (widget: WidgetSchema, index: number, currentPath: WidgetPath) => {\n const Component = registry[widget.type];\n if (!Component) {\n console.warn(\n `Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n const widgetPath = [...currentPath, index];\n\n // Build additional props for container widgets\n const additionalProps = isContainerWidget(widget.type)\n ? {\n widgetId: widget.id,\n widgetPath,\n }\n : {};\n\n const props = {\n ...widget.props,\n ...additionalProps,\n };\n\n return (\n <div\n key={widget.id || index}\n className=\"h-full w-full overflow-x-hidden\"\n >\n <Component {...props} />\n </div>\n );\n },\n [registry, isContainerWidget],\n );\n\n return (\n <div className={className || \"h-full w-full\"}>\n {screen?.map((item, index) => {\n // Skip null items (empty grid cells)\n if (!item) return null;\n return renderWidget(item, index, []);\n })}\n </div>\n );\n}\n\n/**\n * ScreenRenderer - View-only component for rendering widget screens.\n *\n * This is a simplified version designed for the SDK that only handles\n * rendering widgets without edit mode or drag-and-drop functionality.\n *\n * Features:\n * - Static rendering of widgets\n * - Registry context: No prop drilling for widget registry\n * - Container widget support with path tracking\n *\n * Usage:\n * ```tsx\n * import { ScreenRenderer } from '@fluid-app/portal-widgets/core';\n * import { WIDGET_REGISTRY } from '@fluid-app/portal-widgets/widgets';\n *\n * <ScreenRenderer\n * screen={widgets}\n * registry={WIDGET_REGISTRY}\n * />\n * ```\n */\nexport function ScreenRenderer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n>(props: ScreenRendererProps<T>): React.JSX.Element {\n const {\n screen,\n registry,\n containerWidgetTypes = [\n WIDGET_TYPE_NAMES.Layout,\n WIDGET_TYPE_NAMES.Container,\n ],\n className,\n } = props;\n\n // Conditionally pass registry prop to satisfy exactOptionalPropertyTypes\n // When registry is undefined, we omit it entirely and let RegistryProvider inherit from parent\n const registryProviderProps = registry !== undefined ? { registry } : {};\n\n return (\n <RegistryProvider {...registryProviderProps}>\n {/* Type assertion required: TypedWidgetSchema<T>[] is a subtype of WidgetSchema[]\n when T's components accept props compatible with WidgetSchema props.\n This enables type-safe widget rendering with custom registries while allowing\n the internal renderer to work with the base WidgetSchema type. */}\n <ScreenRendererContent\n screen={screen as WidgetSchema[]}\n containerWidgetTypes={containerWidgetTypes}\n className={className}\n />\n </RegistryProvider>\n );\n}\n","import { ScreenRenderer as DefaultScreenRenderer } from \"../core/ScreenRenderer\";\nimport { useRegistry } from \"../contexts/RegistryContext\";\nimport { useScreenRenderer } from \"../contexts/ScreenRendererContext\";\nimport type {\n TypedWidgetSchema,\n WidgetSchema,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n SectionLayoutType,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport { sectionLayoutConfig } from \"@fluid-app/portal-core/types\";\nimport type { ComponentType } from \"react\";\nimport type React from \"react\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getGapField,\n getPaddingField,\n gapValues,\n} from \"../core/fields\";\nimport { groupChildrenByColumn } from \"@fluid-app/portal-core/widget-utils\";\n\nconst DEFAULT_BACKGROUND: BackgroundValue = {\n type: \"solid\",\n color: \"background\",\n};\nconst DEFAULT_CHILDREN: (WidgetSchema | null)[] = [];\n\ntype LayoutProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>> = Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ComponentType<any>\n >,\n> = {\n // Section layout type (masonry-style column configuration)\n sectionLayout?: SectionLayoutType;\n\n // Legacy props (deprecated, kept for backward compatibility)\n type?: \"flex\" | \"grid\";\n columns?: number;\n rows?: number;\n direction?: string;\n justify?: string;\n align?: string;\n wrap?: boolean;\n\n // Spacing\n gap?: number; // deprecated, use gapSize instead\n gapSize?: GapOptions;\n\n // Design\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Content - widgets with columnIndex property for masonry layout\n children: (TypedWidgetSchema<T> | null)[] | (WidgetSchema | null)[];\n\n // Widget registry (passed down from parent renderer)\n registry?: T;\n\n // Styling\n className?: string;\n minHeight?: number;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const LayoutWidget = <T extends Record<string, ComponentType<any>>>({\n sectionLayout = \"single-column\",\n gap = 4,\n gapSize,\n background = DEFAULT_BACKGROUND,\n padding = 0,\n borderRadius = \"md\",\n children = DEFAULT_CHILDREN,\n registry,\n className = \"\",\n minHeight,\n}: LayoutProps<T>): React.JSX.Element => {\n // Get registry from context if not provided as prop\n const contextRegistry = useRegistry();\n const effectiveRegistry = registry || contextRegistry;\n\n // Use ScreenRenderer from context (e.g. portal-builder's), fall back to local\n const ContextScreenRenderer = useScreenRenderer();\n const ScreenRenderer = ContextScreenRenderer ?? DefaultScreenRenderer;\n\n // Extract background values\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n // Get layout configuration\n const layoutConfig = sectionLayoutConfig[sectionLayout];\n const columnCount = layoutConfig.columns;\n\n // Calculate gap size\n const gapSizeValue = gapSize ? gapValues[gapSize] : gap;\n\n // Build CSS classes for masonry layout with responsive columns\n const layoutClasses = [\n \"grid\",\n \"grid-cols-1\", // Mobile: single column\n layoutConfig.gridClasses, // Desktop (@md+): configured columns\n `bg-${backgroundColor} p-${padding} rounded-${borderRadius}`,\n `gap-${gapSizeValue}`,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n // Container style\n const containerStyle: React.CSSProperties = {\n ...(minHeight ? { minHeight: `${minHeight}px` } : {}),\n alignItems: \"start\", // Masonry-style: columns don't stretch to match height\n backgroundImage,\n };\n\n // If no registry is available, we can't render the children\n if (!effectiveRegistry) {\n console.warn(\"Layout widget: No registry provided, cannot render children\");\n return <div className={layoutClasses} style={containerStyle} />;\n }\n\n // Group children by column\n const columnGroups = groupChildrenByColumn(\n children as WidgetSchema[],\n columnCount,\n );\n\n // Regular render mode - render each column as a flex column\n return (\n <div className=\"@container\">\n <div className={layoutClasses} style={containerStyle}>\n {columnGroups.map((columnChildren, colIndex) => (\n <ScreenRenderer\n key={colIndex}\n screen={columnChildren}\n registry={effectiveRegistry}\n className={`flex flex-col gap-${gapSizeValue}`}\n />\n ))}\n </div>\n </div>\n );\n};\n\nexport const layoutWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"LayoutWidget\",\n displayName: \"Layout\",\n fields: [\n // Layout Configuration - Visual selector matching Figma design\n {\n key: \"sectionLayout\",\n label: \"Layout Type\",\n type: \"sectionLayoutSelect\",\n description: \"Column layout configuration\",\n defaultValue: \"single-column\",\n group: \"Layout\",\n },\n\n // Design\n getGapField({\n key: \"gapSize\",\n label: \"Gap\",\n description: \"Gap between columns and widgets\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the layout container\",\n defaultValue: \"background\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding for the layout container\",\n defaultValue: 4,\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the layout container\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n ],\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LayoutWidget-Dc_wvaLg.mjs","names":["ScreenRenderer","DefaultScreenRenderer"],"sources":["../../widgets/src/contexts/RegistryContext.tsx","../../widgets/src/contexts/ScreenRendererContext.tsx","../../core/src/types/widget-schema.ts","../../core/src/types/field-types.ts","../../core/src/widget-utils/widget-utils.ts","../../widgets/src/core/ScreenRenderer.tsx","../../widgets/src/widgets/LayoutWidget.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base props interface for widget components.\n * All widgets receive at least these props from the ScreenRenderer.\n */\nexport interface WidgetBaseProps {\n readonly widget: WidgetSchema;\n readonly path?: readonly number[];\n}\n\n/**\n * Generic widget component type.\n * Widgets receive WidgetBaseProps and may have additional props.\n */\nexport type WidgetComponent<P extends WidgetBaseProps = WidgetBaseProps> =\n ComponentType<P>;\n\n/**\n * Registry mapping widget type names to their components.\n * The keys are widget type strings (e.g., \"TextWidget\", \"ButtonWidget\").\n * Using WidgetComponent for the value type provides better type safety than `any`.\n */\nexport type WidgetRegistry = Readonly<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Widget props vary by type\n Record<string, WidgetComponent<any>>\n>;\n\n/**\n * Context for providing the widget registry to all components in the tree.\n * This eliminates prop drilling and makes the registry accessible anywhere.\n *\n * Default to undefined - registry must be provided by a RegistryProvider.\n */\nexport const RegistryContext: React.Context<WidgetRegistry | undefined> =\n createContext<WidgetRegistry | undefined>(undefined);\n\nexport interface RegistryProviderProps {\n registry?: WidgetRegistry;\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the widget registry available to all descendants.\n * A registry must be provided either via props or by nesting within another RegistryProvider.\n */\nexport function RegistryProvider({\n registry,\n children,\n}: RegistryProviderProps): React.JSX.Element {\n // If no registry provided, inherit from parent provider\n const parentRegistry = useContext(RegistryContext);\n const contextValue = registry ?? parentRegistry;\n\n if (!contextValue) {\n throw new Error(\n \"RegistryProvider requires a registry prop or must be nested within another RegistryProvider.\",\n );\n }\n\n return (\n <RegistryContext.Provider value={contextValue}>\n {children}\n </RegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the widget registry from anywhere in the component tree.\n * Must be used within a RegistryProvider that has a registry prop.\n */\nexport function useRegistry(): WidgetRegistry {\n const registry = useContext(RegistryContext);\n\n if (!registry) {\n throw new Error(\n \"useRegistry must be used within a RegistryProvider with a registry prop.\",\n );\n }\n\n return registry;\n}\n","import React, { createContext, useContext, type ComponentType } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { WidgetRegistry } from \"./RegistryContext\";\n\n/**\n * Props that any ScreenRenderer must accept.\n * This is the minimal interface used by container widgets (e.g. LayoutWidget)\n * to render their children.\n */\nexport interface ScreenRendererComponentProps {\n screen: WidgetSchema[];\n registry?: WidgetRegistry;\n className?: string;\n}\n\n/**\n * Context for providing an alternative ScreenRenderer component.\n *\n * This allows packages like portal-builder to inject their own ScreenRenderer\n * (with edit mode, drag-and-drop, etc.) so that container widgets in portal-widgets\n * use the richer renderer instead of the basic view-only one.\n *\n * Default is undefined — container widgets fall back to the local ScreenRenderer import.\n */\nexport const ScreenRendererContext: React.Context<\n ComponentType<ScreenRendererComponentProps> | undefined\n> = createContext<ComponentType<ScreenRendererComponentProps> | undefined>(\n undefined,\n);\n\n/**\n * Hook to get the ScreenRenderer component from context.\n * Returns undefined if no override has been provided.\n */\nexport function useScreenRenderer():\n | ComponentType<ScreenRendererComponentProps>\n | undefined {\n return useContext(ScreenRendererContext);\n}\n","import type { ComponentType } from \"react\";\nimport type { DataSourceConfig } from \"../data-sources/types\";\n\n/**\n * Widget type names as a const object.\n * This serves as the single source of truth for widget discriminants.\n * Use `as const` for literal type inference (safety-as-const-deep-readonly rule).\n */\nexport const WIDGET_TYPE_NAMES = {\n Alert: \"AlertWidget\",\n Calendar: \"CalendarWidget\",\n Carousel: \"CarouselWidget\",\n CatchUp: \"CatchUpWidget\",\n Chart: \"ChartWidget\",\n Container: \"ContainerWidget\",\n Embed: \"EmbedWidget\",\n Image: \"ImageWidget\",\n Layout: \"LayoutWidget\",\n List: \"ListWidget\",\n MySite: \"MySiteWidget\",\n Nested: \"NestedWidget\",\n QuickShare: \"QuickShareWidget\",\n RecentActivity: \"RecentActivityWidget\",\n Spacer: \"SpacerWidget\",\n Table: \"TableWidget\",\n Text: \"TextWidget\",\n ToDo: \"ToDoWidget\",\n Video: \"VideoWidget\",\n} as const;\n\n/**\n * Union of all known widget type names.\n * Derived from WIDGET_TYPE_NAMES to avoid duplication (deriving-typeof-for-object-keys rule).\n */\nexport type WidgetTypeName =\n (typeof WIDGET_TYPE_NAMES)[keyof typeof WIDGET_TYPE_NAMES];\n\n/**\n * Legacy alias for backwards compatibility.\n * Prefer using WidgetTypeName for new code when you need the union type.\n */\nexport type WidgetType = string;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type WidgetRegistry = Record<WidgetType, ComponentType<any>>;\n\n/**\n * Base widget schema with loose typing for runtime data.\n * Use TypedWidgetSchema<T> when you have a known registry for better type safety.\n */\nexport type WidgetSchema = {\n readonly type: WidgetType;\n readonly props: Readonly<Record<string, unknown>>;\n readonly id?: string; // Optional unique identifier for drag-and-drop\n /** Optional data source configuration for data-bound widgets */\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n};\n\n/**\n * Type-safe widget schema based on registry.\n * Uses discriminated unions - the `type` field serves as discriminant.\n * When narrowed (e.g., `if (widget.type === \"AlertWidget\")`),\n * TypeScript automatically knows the correct props type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TypedWidgetSchema<T extends Record<string, ComponentType<any>>> = {\n [K in keyof T]: {\n readonly type: K;\n readonly props: Readonly<React.ComponentProps<T[K]>>;\n readonly id?: string;\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n };\n}[keyof T];\n\n/**\n * Widget path in the tree - array of indices.\n * Readonly tuple to prevent accidental mutation.\n */\nexport type WidgetPath = readonly number[];\n\n// ============================================================================\n// Type Guards and Assertion Functions\n// ============================================================================\n\n/**\n * Type predicate to check if a string is a known widget type name.\n * Use for runtime validation of widget types.\n *\n * @example\n * if (isWidgetTypeName(widget.type)) {\n * // TypeScript knows widget.type is WidgetTypeName\n * }\n */\nexport function isWidgetTypeName(type: string): type is WidgetTypeName {\n // Type assertion required in type guard: Object.values() returns string[], but we\n // need to check against WidgetTypeName values. The assertion is safe because we're\n // checking membership, and the return type predicate ensures correct narrowing.\n return Object.values(WIDGET_TYPE_NAMES).includes(type as WidgetTypeName);\n}\n\n/**\n * Type predicate to check if a widget has a specific type.\n * Enables type-safe widget narrowing without `as` assertions.\n *\n * @example\n * if (isWidgetType(widget, \"LayoutWidget\")) {\n * // TypeScript knows widget.type === \"LayoutWidget\"\n * // and widget.props is LayoutWidget props\n * }\n */\nexport function isWidgetType<T extends WidgetTypeName>(\n widget: WidgetSchema | null | undefined,\n typeName: T,\n): widget is WidgetSchema & { readonly type: T } {\n return widget != null && widget.type === typeName;\n}\n\n/**\n * Helper for exhaustive switch statements on widget types.\n * Use in the default case to ensure all widget types are handled.\n *\n * @example\n * switch (widget.type) {\n * case \"AlertWidget\": return handleAlert();\n * case \"TextWidget\": return handleText();\n * // ... all other widget types\n * default: return assertNever(widget.type, \"widget type\");\n * }\n */\nexport function assertNever(value: never, context?: string): never {\n const message = context\n ? `Unexpected ${context}: ${String(value)}`\n : `Unexpected value: ${String(value)}`;\n throw new Error(message);\n}\n\n/**\n * Assertion function that throws if value is undefined.\n * Narrows the type to exclude undefined.\n *\n * @example\n * const widget = screen[0];\n * assertDefined(widget, \"widget at index 0\");\n * // TypeScript knows widget is defined here\n */\nexport function assertDefined<T>(\n value: T | undefined | null,\n name?: string,\n): asserts value is T {\n if (value == null) {\n throw new Error(name ? `${name} is required` : \"Value is required\");\n }\n}\n","import type { ShareableItem } from \"./shareable-item\";\n\n// ============================================================================\n// Color Options - Derive type from constant for single source of truth\n// ============================================================================\n\n/**\n * Color options constant - single source of truth for color values.\n * Use COLOR_OPTIONS.primary instead of \"primary\" for type-safe comparisons.\n */\nexport const COLOR_OPTIONS = {\n background: \"background\",\n foreground: \"foreground\",\n primary: \"primary\",\n secondary: \"secondary\",\n accent: \"accent\",\n muted: \"muted\",\n destructive: \"destructive\",\n transparent: \"transparent\",\n} as const;\n\n/**\n * Union type of all color options, derived from COLOR_OPTIONS constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type ColorOptions = (typeof COLOR_OPTIONS)[keyof typeof COLOR_OPTIONS];\n\n// ============================================================================\n// Size Options - Derive types from constants for single source of truth\n// ============================================================================\n\nexport const FONT_SIZE_OPTIONS = {\n \"2xl\": \"2xl\",\n xl: \"xl\",\n lg: \"lg\",\n md: \"md\",\n sm: \"sm\",\n xs: \"xs\",\n} as const;\n\nexport type FontSizeOptions =\n (typeof FONT_SIZE_OPTIONS)[keyof typeof FONT_SIZE_OPTIONS];\n\nexport const BORDER_RADIUS_OPTIONS = {\n none: \"none\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n full: \"full\",\n} as const;\n\nexport type BorderRadiusOptions =\n (typeof BORDER_RADIUS_OPTIONS)[keyof typeof BORDER_RADIUS_OPTIONS];\n\n/** Padding values - numeric, so we use a tuple for derivation */\nexport const PADDING_VALUES = [0, 2, 4, 6, 8, 10] as const;\nexport type PaddingOptions = (typeof PADDING_VALUES)[number];\n\nexport const BUTTON_SIZE_OPTIONS = {\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type ButtonSizeOptions =\n (typeof BUTTON_SIZE_OPTIONS)[keyof typeof BUTTON_SIZE_OPTIONS];\n\nexport const GAP_OPTIONS = {\n none: \"none\",\n xs: \"xs\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type GapOptions = (typeof GAP_OPTIONS)[keyof typeof GAP_OPTIONS];\n\n// ============================================================================\n// Alignment Options - Derive from constants\n// ============================================================================\n\nexport const VERTICAL_ALIGN_OPTIONS = {\n top: \"top\",\n center: \"center\",\n bottom: \"bottom\",\n} as const;\n\nexport type VerticalAlign =\n (typeof VERTICAL_ALIGN_OPTIONS)[keyof typeof VERTICAL_ALIGN_OPTIONS];\n\nexport const HORIZONTAL_ALIGN_OPTIONS = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n} as const;\n\nexport type HorizontalAlign =\n (typeof HORIZONTAL_ALIGN_OPTIONS)[keyof typeof HORIZONTAL_ALIGN_OPTIONS];\n\nexport type AlignOptions = {\n vertical?: VerticalAlign;\n horizontal?: HorizontalAlign;\n};\n\n// ============================================================================\n// Background Options - Derive from constant\n// ============================================================================\n\nexport const BACKGROUND_TYPES = {\n solid: \"solid\",\n image: \"image\",\n} as const;\n\nexport type BackgroundType =\n (typeof BACKGROUND_TYPES)[keyof typeof BACKGROUND_TYPES];\nexport interface BackgroundValue {\n type: BackgroundType;\n color?: ColorOptions;\n resource?: ShareableItem;\n}\n\n// ============================================================================\n// Section Layout - Derive type from config keys (single source of truth)\n// ============================================================================\n\n/**\n * Section layout configuration - single source of truth for layout types.\n * SectionLayoutType is derived from these keys to prevent drift.\n */\nexport const SECTION_LAYOUT_CONFIG: {\n readonly \"single-column\": {\n readonly columns: 1;\n readonly widths: readonly [\"1fr\"];\n readonly gridClasses: \"\";\n };\n readonly \"2c-equal\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-2\";\n };\n readonly \"2c-left-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[2fr_1fr]\";\n };\n readonly \"2c-right-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"2fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr]\";\n };\n readonly \"2c-left-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"3fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_3fr]\";\n };\n readonly \"2c-right-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"3fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[3fr_1fr]\";\n };\n readonly \"3c-equal\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-3\";\n };\n readonly \"3c-middle-wider\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\";\n };\n} = {\n \"single-column\": { columns: 1, widths: [\"1fr\"], gridClasses: \"\" },\n \"2c-equal\": {\n columns: 2,\n widths: [\"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-2\",\n },\n \"2c-left-wider\": {\n columns: 2,\n widths: [\"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[2fr_1fr]\",\n },\n \"2c-right-wider\": {\n columns: 2,\n widths: [\"1fr\", \"2fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr]\",\n },\n \"2c-left-narrow\": {\n columns: 2,\n widths: [\"1fr\", \"3fr\"],\n gridClasses: \"@md:grid-cols-[1fr_3fr]\",\n },\n \"2c-right-narrow\": {\n columns: 2,\n widths: [\"3fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[3fr_1fr]\",\n },\n \"3c-equal\": {\n columns: 3,\n widths: [\"1fr\", \"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-3\",\n },\n \"3c-middle-wider\": {\n columns: 3,\n widths: [\"1fr\", \"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\",\n },\n} as const satisfies Record<\n string,\n {\n readonly columns: number;\n readonly widths: readonly string[];\n readonly gridClasses: string;\n }\n>;\n\n/**\n * Union type of all section layout types, derived from SECTION_LAYOUT_CONFIG keys.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type SectionLayoutType = keyof typeof SECTION_LAYOUT_CONFIG;\n\n/** @deprecated Use SECTION_LAYOUT_CONFIG instead */\nexport const sectionLayoutConfig: typeof SECTION_LAYOUT_CONFIG =\n SECTION_LAYOUT_CONFIG;\n","import type { WidgetSchema, WidgetPath } from \"../types\";\nimport { WIDGET_TYPE_NAMES, isWidgetType } from \"../types\";\n\n/**\n * Generates a unique ID for a widget\n */\nexport function generateWidgetId(type?: string): string {\n const uuid = crypto.randomUUID();\n return type ? `${type}-${uuid}` : uuid;\n}\n\n/**\n * Converts a widget path to a unique string identifier\n */\nexport function pathToId(path: WidgetPath): string {\n return path.join(\"-\");\n}\n\n/**\n * Converts a path string back to a WidgetPath array\n */\nexport function idToPath(id: string): WidgetPath {\n return id.split(\"-\").map((segment) => parseInt(segment, 10));\n}\n\n/**\n * Ensures all widgets in an array have valid IDs\n * Note: Handles null values for sparse arrays (e.g., grid layouts with empty cells)\n */\nexport function ensureWidgetIds(\n widgets: readonly (WidgetSchema | null)[],\n): (WidgetSchema | null)[] {\n return widgets?.map((widget, index) => {\n if (!widget) return null;\n return {\n ...widget,\n id: widget.id || `widget-${index}-${crypto.randomUUID()}`,\n };\n });\n}\n\n/**\n * Deep clones a widget and regenerates all IDs (including nested children)\n */\nexport function deepCloneWidget(widget: WidgetSchema): WidgetSchema {\n // Handle LayoutWidget children recursively\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (\n isWidgetType(widget, WIDGET_TYPE_NAMES.Layout) &&\n Array.isArray(widget.props.children)\n ) {\n // Build cloned widget with recursively cloned children\n return {\n type: widget.type,\n props: {\n ...widget.props,\n children: (\n widget.props.children as readonly (WidgetSchema | null)[]\n ).map((child) => (child ? deepCloneWidget(child) : null)),\n },\n id: generateWidgetId(widget.type),\n };\n }\n\n // Clone non-container widget\n return {\n type: widget.type,\n props: { ...widget.props },\n id: generateWidgetId(widget.type),\n };\n}\n\n/**\n * Deep clones an array of widgets and regenerates all IDs\n */\nexport function deepCloneWidgets(\n widgets: readonly WidgetSchema[],\n): WidgetSchema[] {\n return widgets.map((widget) => deepCloneWidget(widget));\n}\n\n/**\n * Gets a widget at a specific path in the widget tree\n */\nexport function getWidgetByPath(\n screen: readonly (WidgetSchema | null)[],\n path: WidgetPath,\n): WidgetSchema | null {\n if (path.length === 0 || path[0] === undefined) return null;\n\n let current: WidgetSchema | null = screen[path[0]] ?? null;\n\n for (let i = 1; i < path.length; i++) {\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (!isWidgetType(current, WIDGET_TYPE_NAMES.Layout)) return null;\n\n const children = (current.props.children ??\n []) as readonly (WidgetSchema | null)[];\n const index = path[i];\n if (index === undefined) return null;\n current = children[index] ?? null;\n }\n\n return current;\n}\n\n/**\n * Groups children by column index.\n * Accepts nullable children (sparse arrays from grid layouts) and filters them out.\n */\nexport function groupChildrenByColumn(\n children: readonly (WidgetSchema | null)[],\n columnCount: number,\n): WidgetSchema[][] {\n // Use explicit type annotation to avoid never[] inference (narrowing-empty-array-type rule)\n const columns: WidgetSchema[][] = Array.from(\n { length: columnCount },\n (): WidgetSchema[] => [],\n );\n\n // Filter out nulls first, then process (truthiness narrowing rule)\n // This avoids the narrowing-callback-scope issue since we work with non-null array\n const nonNullChildren = children.filter(\n (child): child is WidgetSchema => child !== null,\n );\n\n for (const child of nonNullChildren) {\n // Default to column 0 if no columnIndex specified\n const colIndex = Math.min(child.columnIndex ?? 0, columnCount - 1);\n const column = columns[colIndex];\n if (column) {\n column.push(child);\n }\n }\n\n return columns;\n}\n","import type { ComponentType } from \"react\";\nimport { useCallback } from \"react\";\nimport type React from \"react\";\nimport type {\n WidgetSchema,\n TypedWidgetSchema,\n WidgetPath,\n} from \"@fluid-app/portal-core/types\";\nimport { WIDGET_TYPE_NAMES } from \"@fluid-app/portal-core/types\";\nimport { RegistryProvider, useRegistry } from \"../contexts/RegistryContext\";\n\nexport interface ScreenRendererProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n> {\n /** Array of widget schemas to render */\n screen: TypedWidgetSchema<T>[] | WidgetSchema[];\n /** Widget registry mapping type names to components */\n registry?: T;\n /** Container widgets (like LayoutWidget) receive these additional props */\n containerWidgetTypes?: string[];\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\ninterface ScreenRendererContentProps {\n screen: WidgetSchema[];\n containerWidgetTypes: string[];\n className?: string | undefined;\n}\n\n/**\n * Internal component that uses the registry from context.\n * This allows us to avoid prop drilling the registry.\n */\nfunction ScreenRendererContent({\n screen,\n containerWidgetTypes,\n className,\n}: ScreenRendererContentProps) {\n const registry = useRegistry();\n\n // Check if a widget type is a container\n const isContainerWidget = useCallback(\n (widgetType: string) => containerWidgetTypes.includes(widgetType),\n [containerWidgetTypes],\n );\n\n // Render widgets recursively with path tracking\n const renderWidget = useCallback(\n (widget: WidgetSchema, index: number, currentPath: WidgetPath) => {\n const Component = registry[widget.type];\n if (!Component) {\n console.warn(\n `Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n const widgetPath = [...currentPath, index];\n\n // Build additional props for container widgets\n const additionalProps = isContainerWidget(widget.type)\n ? {\n widgetId: widget.id,\n widgetPath,\n }\n : {};\n\n const props = {\n ...widget.props,\n ...additionalProps,\n };\n\n return (\n <div\n key={widget.id || index}\n className=\"h-full w-full overflow-x-hidden\"\n >\n <Component {...props} />\n </div>\n );\n },\n [registry, isContainerWidget],\n );\n\n return (\n <div className={className || \"h-full w-full\"}>\n {screen?.map((item, index) => {\n // Skip null items (empty grid cells)\n if (!item) return null;\n return renderWidget(item, index, []);\n })}\n </div>\n );\n}\n\n/**\n * ScreenRenderer - View-only component for rendering widget screens.\n *\n * This is a simplified version designed for the SDK that only handles\n * rendering widgets without edit mode or drag-and-drop functionality.\n *\n * Features:\n * - Static rendering of widgets\n * - Registry context: No prop drilling for widget registry\n * - Container widget support with path tracking\n *\n * Usage:\n * ```tsx\n * import { ScreenRenderer } from '@fluid-app/portal-widgets/core';\n * import { WIDGET_REGISTRY } from '@fluid-app/portal-widgets/widgets';\n *\n * <ScreenRenderer\n * screen={widgets}\n * registry={WIDGET_REGISTRY}\n * />\n * ```\n */\nexport function ScreenRenderer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n>(props: ScreenRendererProps<T>): React.JSX.Element {\n const {\n screen,\n registry,\n containerWidgetTypes = [\n WIDGET_TYPE_NAMES.Layout,\n WIDGET_TYPE_NAMES.Container,\n ],\n className,\n } = props;\n\n // Conditionally pass registry prop to satisfy exactOptionalPropertyTypes\n // When registry is undefined, we omit it entirely and let RegistryProvider inherit from parent\n const registryProviderProps = registry !== undefined ? { registry } : {};\n\n return (\n <RegistryProvider {...registryProviderProps}>\n {/* Type assertion required: TypedWidgetSchema<T>[] is a subtype of WidgetSchema[]\n when T's components accept props compatible with WidgetSchema props.\n This enables type-safe widget rendering with custom registries while allowing\n the internal renderer to work with the base WidgetSchema type. */}\n <ScreenRendererContent\n screen={screen as WidgetSchema[]}\n containerWidgetTypes={containerWidgetTypes}\n className={className}\n />\n </RegistryProvider>\n );\n}\n","import { ScreenRenderer as DefaultScreenRenderer } from \"../core/ScreenRenderer\";\nimport { useRegistry } from \"../contexts/RegistryContext\";\nimport { useScreenRenderer } from \"../contexts/ScreenRendererContext\";\nimport type {\n TypedWidgetSchema,\n WidgetSchema,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n SectionLayoutType,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport { sectionLayoutConfig } from \"@fluid-app/portal-core/types\";\nimport type { ComponentType } from \"react\";\nimport type React from \"react\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getGapField,\n getPaddingField,\n gapValues,\n} from \"../core/fields\";\nimport { groupChildrenByColumn } from \"@fluid-app/portal-core/widget-utils\";\n\nconst DEFAULT_BACKGROUND: BackgroundValue = {\n type: \"solid\",\n color: \"background\",\n};\nconst DEFAULT_CHILDREN: (WidgetSchema | null)[] = [];\n\ntype LayoutProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>> = Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ComponentType<any>\n >,\n> = {\n // Section layout type (masonry-style column configuration)\n sectionLayout?: SectionLayoutType;\n\n // Legacy props (deprecated, kept for backward compatibility)\n type?: \"flex\" | \"grid\";\n columns?: number;\n rows?: number;\n direction?: string;\n justify?: string;\n align?: string;\n wrap?: boolean;\n\n // Spacing\n gap?: number; // deprecated, use gapSize instead\n gapSize?: GapOptions;\n\n // Design\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Content - widgets with columnIndex property for masonry layout\n children: (TypedWidgetSchema<T> | null)[] | (WidgetSchema | null)[];\n\n // Widget registry (passed down from parent renderer)\n registry?: T;\n\n // Styling\n className?: string;\n minHeight?: number;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const LayoutWidget = <T extends Record<string, ComponentType<any>>>({\n sectionLayout = \"single-column\",\n gap = 4,\n gapSize,\n background = DEFAULT_BACKGROUND,\n padding = 0,\n borderRadius = \"md\",\n children = DEFAULT_CHILDREN,\n registry,\n className = \"\",\n minHeight,\n}: LayoutProps<T>): React.JSX.Element => {\n // Get registry from context if not provided as prop\n const contextRegistry = useRegistry();\n const effectiveRegistry = registry || contextRegistry;\n\n // Use ScreenRenderer from context (e.g. portal-builder's), fall back to local\n const ContextScreenRenderer = useScreenRenderer();\n const ScreenRenderer = ContextScreenRenderer ?? DefaultScreenRenderer;\n\n // Extract background values\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n // Get layout configuration\n const layoutConfig = sectionLayoutConfig[sectionLayout];\n const columnCount = layoutConfig.columns;\n\n // Calculate gap size\n const gapSizeValue = gapSize ? gapValues[gapSize] : gap;\n\n // Build CSS classes for masonry layout with responsive columns\n const layoutClasses = [\n \"grid\",\n \"grid-cols-1\", // Mobile: single column\n layoutConfig.gridClasses, // Desktop (@md+): configured columns\n `bg-${backgroundColor} p-${padding} rounded-${borderRadius}`,\n `gap-${gapSizeValue}`,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n // Container style\n const containerStyle: React.CSSProperties = {\n ...(minHeight ? { minHeight: `${minHeight}px` } : {}),\n alignItems: \"start\", // Masonry-style: columns don't stretch to match height\n backgroundImage,\n };\n\n // If no registry is available, we can't render the children\n if (!effectiveRegistry) {\n console.warn(\"Layout widget: No registry provided, cannot render children\");\n return <div className={layoutClasses} style={containerStyle} />;\n }\n\n // Group children by column\n const columnGroups = groupChildrenByColumn(\n children as WidgetSchema[],\n columnCount,\n );\n\n // Regular render mode - render each column as a flex column\n return (\n <div className=\"@container\">\n <div className={layoutClasses} style={containerStyle}>\n {columnGroups.map((columnChildren, colIndex) => (\n <ScreenRenderer\n key={colIndex}\n screen={columnChildren}\n registry={effectiveRegistry}\n className={`flex flex-col gap-${gapSizeValue}`}\n />\n ))}\n </div>\n </div>\n );\n};\n\nexport const layoutWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"LayoutWidget\",\n displayName: \"Layout\",\n fields: [\n // Layout Configuration - Visual selector matching Figma design\n {\n key: \"sectionLayout\",\n label: \"Layout Type\",\n type: \"sectionLayoutSelect\",\n description: \"Column layout configuration\",\n defaultValue: \"single-column\",\n group: \"Layout\",\n },\n\n // Design\n getGapField({\n key: \"gapSize\",\n label: \"Gap\",\n description: \"Gap between columns and widgets\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the layout container\",\n defaultValue: \"background\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding for the layout container\",\n defaultValue: 4,\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the layout container\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n ],\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutWidget-fOBC-EML.cjs","names":["ScreenRenderer","DefaultScreenRenderer","gapValues","getGapField","getPaddingField","getBorderRadiusField"],"sources":["../../widgets/src/contexts/RegistryContext.tsx","../../widgets/src/contexts/ScreenRendererContext.tsx","../../core/src/types/widget-schema.ts","../../core/src/types/field-types.ts","../../core/src/widget-utils/widget-utils.ts","../../widgets/src/core/ScreenRenderer.tsx","../../widgets/src/widgets/LayoutWidget.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base props interface for widget components.\n * All widgets receive at least these props from the ScreenRenderer.\n */\nexport interface WidgetBaseProps {\n readonly widget: WidgetSchema;\n readonly path?: readonly number[];\n}\n\n/**\n * Generic widget component type.\n * Widgets receive WidgetBaseProps and may have additional props.\n */\nexport type WidgetComponent<P extends WidgetBaseProps = WidgetBaseProps> =\n ComponentType<P>;\n\n/**\n * Registry mapping widget type names to their components.\n * The keys are widget type strings (e.g., \"TextWidget\", \"ButtonWidget\").\n * Using WidgetComponent for the value type provides better type safety than `any`.\n */\nexport type WidgetRegistry = Readonly<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Widget props vary by type\n Record<string, WidgetComponent<any>>\n>;\n\n/**\n * Context for providing the widget registry to all components in the tree.\n * This eliminates prop drilling and makes the registry accessible anywhere.\n *\n * Default to undefined - registry must be provided by a RegistryProvider.\n */\nexport const RegistryContext: React.Context<WidgetRegistry | undefined> =\n createContext<WidgetRegistry | undefined>(undefined);\n\nexport interface RegistryProviderProps {\n registry?: WidgetRegistry;\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the widget registry available to all descendants.\n * A registry must be provided either via props or by nesting within another RegistryProvider.\n */\nexport function RegistryProvider({\n registry,\n children,\n}: RegistryProviderProps): React.JSX.Element {\n // If no registry provided, inherit from parent provider\n const parentRegistry = useContext(RegistryContext);\n const contextValue = registry ?? parentRegistry;\n\n if (!contextValue) {\n throw new Error(\n \"RegistryProvider requires a registry prop or must be nested within another RegistryProvider.\",\n );\n }\n\n return (\n <RegistryContext.Provider value={contextValue}>\n {children}\n </RegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the widget registry from anywhere in the component tree.\n * Must be used within a RegistryProvider that has a registry prop.\n */\nexport function useRegistry(): WidgetRegistry {\n const registry = useContext(RegistryContext);\n\n if (!registry) {\n throw new Error(\n \"useRegistry must be used within a RegistryProvider with a registry prop.\",\n );\n }\n\n return registry;\n}\n","import React, { createContext, useContext, type ComponentType } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { WidgetRegistry } from \"./RegistryContext\";\n\n/**\n * Props that any ScreenRenderer must accept.\n * This is the minimal interface used by container widgets (e.g. LayoutWidget)\n * to render their children.\n */\nexport interface ScreenRendererComponentProps {\n screen: WidgetSchema[];\n registry?: WidgetRegistry;\n className?: string;\n}\n\n/**\n * Context for providing an alternative ScreenRenderer component.\n *\n * This allows packages like portal-builder to inject their own ScreenRenderer\n * (with edit mode, drag-and-drop, etc.) so that container widgets in portal-widgets\n * use the richer renderer instead of the basic view-only one.\n *\n * Default is undefined — container widgets fall back to the local ScreenRenderer import.\n */\nexport const ScreenRendererContext: React.Context<\n ComponentType<ScreenRendererComponentProps> | undefined\n> = createContext<ComponentType<ScreenRendererComponentProps> | undefined>(\n undefined,\n);\n\n/**\n * Hook to get the ScreenRenderer component from context.\n * Returns undefined if no override has been provided.\n */\nexport function useScreenRenderer():\n | ComponentType<ScreenRendererComponentProps>\n | undefined {\n return useContext(ScreenRendererContext);\n}\n","import type { ComponentType } from \"react\";\nimport type { DataSourceConfig } from \"../data-sources/types\";\n\n/**\n * Widget type names as a const object.\n * This serves as the single source of truth for widget discriminants.\n * Use `as const` for literal type inference (safety-as-const-deep-readonly rule).\n */\nexport const WIDGET_TYPE_NAMES = {\n Alert: \"AlertWidget\",\n Calendar: \"CalendarWidget\",\n Carousel: \"CarouselWidget\",\n CatchUp: \"CatchUpWidget\",\n Chart: \"ChartWidget\",\n Container: \"ContainerWidget\",\n Embed: \"EmbedWidget\",\n Image: \"ImageWidget\",\n Layout: \"LayoutWidget\",\n List: \"ListWidget\",\n MySite: \"MySiteWidget\",\n Nested: \"NestedWidget\",\n QuickShare: \"QuickShareWidget\",\n RecentActivity: \"RecentActivityWidget\",\n Spacer: \"SpacerWidget\",\n Table: \"TableWidget\",\n Text: \"TextWidget\",\n ToDo: \"ToDoWidget\",\n Video: \"VideoWidget\",\n} as const;\n\n/**\n * Union of all known widget type names.\n * Derived from WIDGET_TYPE_NAMES to avoid duplication (deriving-typeof-for-object-keys rule).\n */\nexport type WidgetTypeName =\n (typeof WIDGET_TYPE_NAMES)[keyof typeof WIDGET_TYPE_NAMES];\n\n/**\n * Legacy alias for backwards compatibility.\n * Prefer using WidgetTypeName for new code when you need the union type.\n */\nexport type WidgetType = string;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type WidgetRegistry = Record<WidgetType, ComponentType<any>>;\n\n/**\n * Base widget schema with loose typing for runtime data.\n * Use TypedWidgetSchema<T> when you have a known registry for better type safety.\n */\nexport type WidgetSchema = {\n readonly type: WidgetType;\n readonly props: Readonly<Record<string, unknown>>;\n readonly id?: string; // Optional unique identifier for drag-and-drop\n /** Optional data source configuration for data-bound widgets */\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n};\n\n/**\n * Type-safe widget schema based on registry.\n * Uses discriminated unions - the `type` field serves as discriminant.\n * When narrowed (e.g., `if (widget.type === \"AlertWidget\")`),\n * TypeScript automatically knows the correct props type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TypedWidgetSchema<T extends Record<string, ComponentType<any>>> = {\n [K in keyof T]: {\n readonly type: K;\n readonly props: Readonly<React.ComponentProps<T[K]>>;\n readonly id?: string;\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n };\n}[keyof T];\n\n/**\n * Widget path in the tree - array of indices.\n * Readonly tuple to prevent accidental mutation.\n */\nexport type WidgetPath = readonly number[];\n\n// ============================================================================\n// Type Guards and Assertion Functions\n// ============================================================================\n\n/**\n * Type predicate to check if a string is a known widget type name.\n * Use for runtime validation of widget types.\n *\n * @example\n * if (isWidgetTypeName(widget.type)) {\n * // TypeScript knows widget.type is WidgetTypeName\n * }\n */\nexport function isWidgetTypeName(type: string): type is WidgetTypeName {\n // Type assertion required in type guard: Object.values() returns string[], but we\n // need to check against WidgetTypeName values. The assertion is safe because we're\n // checking membership, and the return type predicate ensures correct narrowing.\n return Object.values(WIDGET_TYPE_NAMES).includes(type as WidgetTypeName);\n}\n\n/**\n * Type predicate to check if a widget has a specific type.\n * Enables type-safe widget narrowing without `as` assertions.\n *\n * @example\n * if (isWidgetType(widget, \"LayoutWidget\")) {\n * // TypeScript knows widget.type === \"LayoutWidget\"\n * // and widget.props is LayoutWidget props\n * }\n */\nexport function isWidgetType<T extends WidgetTypeName>(\n widget: WidgetSchema | null | undefined,\n typeName: T,\n): widget is WidgetSchema & { readonly type: T } {\n return widget != null && widget.type === typeName;\n}\n\n/**\n * Helper for exhaustive switch statements on widget types.\n * Use in the default case to ensure all widget types are handled.\n *\n * @example\n * switch (widget.type) {\n * case \"AlertWidget\": return handleAlert();\n * case \"TextWidget\": return handleText();\n * // ... all other widget types\n * default: return assertNever(widget.type, \"widget type\");\n * }\n */\nexport function assertNever(value: never, context?: string): never {\n const message = context\n ? `Unexpected ${context}: ${String(value)}`\n : `Unexpected value: ${String(value)}`;\n throw new Error(message);\n}\n\n/**\n * Assertion function that throws if value is undefined.\n * Narrows the type to exclude undefined.\n *\n * @example\n * const widget = screen[0];\n * assertDefined(widget, \"widget at index 0\");\n * // TypeScript knows widget is defined here\n */\nexport function assertDefined<T>(\n value: T | undefined | null,\n name?: string,\n): asserts value is T {\n if (value == null) {\n throw new Error(name ? `${name} is required` : \"Value is required\");\n }\n}\n","import type { ShareableItem } from \"./shareable-item\";\n\n// ============================================================================\n// Color Options - Derive type from constant for single source of truth\n// ============================================================================\n\n/**\n * Color options constant - single source of truth for color values.\n * Use COLOR_OPTIONS.primary instead of \"primary\" for type-safe comparisons.\n */\nexport const COLOR_OPTIONS = {\n background: \"background\",\n foreground: \"foreground\",\n primary: \"primary\",\n secondary: \"secondary\",\n accent: \"accent\",\n muted: \"muted\",\n destructive: \"destructive\",\n} as const;\n\n/**\n * Union type of all color options, derived from COLOR_OPTIONS constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type ColorOptions = (typeof COLOR_OPTIONS)[keyof typeof COLOR_OPTIONS];\n\n// ============================================================================\n// Size Options - Derive types from constants for single source of truth\n// ============================================================================\n\nexport const FONT_SIZE_OPTIONS = {\n \"2xl\": \"2xl\",\n xl: \"xl\",\n lg: \"lg\",\n md: \"md\",\n sm: \"sm\",\n xs: \"xs\",\n} as const;\n\nexport type FontSizeOptions =\n (typeof FONT_SIZE_OPTIONS)[keyof typeof FONT_SIZE_OPTIONS];\n\nexport const BORDER_RADIUS_OPTIONS = {\n none: \"none\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n full: \"full\",\n} as const;\n\nexport type BorderRadiusOptions =\n (typeof BORDER_RADIUS_OPTIONS)[keyof typeof BORDER_RADIUS_OPTIONS];\n\n/** Padding values - numeric, so we use a tuple for derivation */\nexport const PADDING_VALUES = [0, 2, 4, 6, 8, 10] as const;\nexport type PaddingOptions = (typeof PADDING_VALUES)[number];\n\nexport const BUTTON_SIZE_OPTIONS = {\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type ButtonSizeOptions =\n (typeof BUTTON_SIZE_OPTIONS)[keyof typeof BUTTON_SIZE_OPTIONS];\n\nexport const GAP_OPTIONS = {\n none: \"none\",\n xs: \"xs\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type GapOptions = (typeof GAP_OPTIONS)[keyof typeof GAP_OPTIONS];\n\n// ============================================================================\n// Alignment Options - Derive from constants\n// ============================================================================\n\nexport const VERTICAL_ALIGN_OPTIONS = {\n top: \"top\",\n center: \"center\",\n bottom: \"bottom\",\n} as const;\n\nexport type VerticalAlign =\n (typeof VERTICAL_ALIGN_OPTIONS)[keyof typeof VERTICAL_ALIGN_OPTIONS];\n\nexport const HORIZONTAL_ALIGN_OPTIONS = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n} as const;\n\nexport type HorizontalAlign =\n (typeof HORIZONTAL_ALIGN_OPTIONS)[keyof typeof HORIZONTAL_ALIGN_OPTIONS];\n\nexport type AlignOptions = {\n vertical?: VerticalAlign;\n horizontal?: HorizontalAlign;\n};\n\n// ============================================================================\n// Background Options - Derive from constant\n// ============================================================================\n\nexport const BACKGROUND_TYPES = {\n solid: \"solid\",\n image: \"image\",\n} as const;\n\nexport type BackgroundType =\n (typeof BACKGROUND_TYPES)[keyof typeof BACKGROUND_TYPES];\nexport interface BackgroundValue {\n type: BackgroundType;\n color?: ColorOptions;\n resource?: ShareableItem;\n}\n\n// ============================================================================\n// Section Layout - Derive type from config keys (single source of truth)\n// ============================================================================\n\n/**\n * Section layout configuration - single source of truth for layout types.\n * SectionLayoutType is derived from these keys to prevent drift.\n */\nexport const SECTION_LAYOUT_CONFIG: {\n readonly \"single-column\": {\n readonly columns: 1;\n readonly widths: readonly [\"1fr\"];\n readonly gridClasses: \"\";\n };\n readonly \"2c-equal\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-2\";\n };\n readonly \"2c-left-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[2fr_1fr]\";\n };\n readonly \"2c-right-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"2fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr]\";\n };\n readonly \"2c-left-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"3fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_3fr]\";\n };\n readonly \"2c-right-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"3fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[3fr_1fr]\";\n };\n readonly \"3c-equal\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-3\";\n };\n readonly \"3c-middle-wider\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\";\n };\n} = {\n \"single-column\": { columns: 1, widths: [\"1fr\"], gridClasses: \"\" },\n \"2c-equal\": {\n columns: 2,\n widths: [\"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-2\",\n },\n \"2c-left-wider\": {\n columns: 2,\n widths: [\"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[2fr_1fr]\",\n },\n \"2c-right-wider\": {\n columns: 2,\n widths: [\"1fr\", \"2fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr]\",\n },\n \"2c-left-narrow\": {\n columns: 2,\n widths: [\"1fr\", \"3fr\"],\n gridClasses: \"@md:grid-cols-[1fr_3fr]\",\n },\n \"2c-right-narrow\": {\n columns: 2,\n widths: [\"3fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[3fr_1fr]\",\n },\n \"3c-equal\": {\n columns: 3,\n widths: [\"1fr\", \"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-3\",\n },\n \"3c-middle-wider\": {\n columns: 3,\n widths: [\"1fr\", \"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\",\n },\n} as const satisfies Record<\n string,\n {\n readonly columns: number;\n readonly widths: readonly string[];\n readonly gridClasses: string;\n }\n>;\n\n/**\n * Union type of all section layout types, derived from SECTION_LAYOUT_CONFIG keys.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type SectionLayoutType = keyof typeof SECTION_LAYOUT_CONFIG;\n\n/** @deprecated Use SECTION_LAYOUT_CONFIG instead */\nexport const sectionLayoutConfig: typeof SECTION_LAYOUT_CONFIG =\n SECTION_LAYOUT_CONFIG;\n","import type { WidgetSchema, WidgetPath } from \"../types\";\nimport { WIDGET_TYPE_NAMES, isWidgetType } from \"../types\";\n\n/**\n * Generates a unique ID for a widget\n */\nexport function generateWidgetId(type?: string): string {\n const uuid = crypto.randomUUID();\n return type ? `${type}-${uuid}` : uuid;\n}\n\n/**\n * Converts a widget path to a unique string identifier\n */\nexport function pathToId(path: WidgetPath): string {\n return path.join(\"-\");\n}\n\n/**\n * Converts a path string back to a WidgetPath array\n */\nexport function idToPath(id: string): WidgetPath {\n return id.split(\"-\").map((segment) => parseInt(segment, 10));\n}\n\n/**\n * Ensures all widgets in an array have valid IDs\n * Note: Handles null values for sparse arrays (e.g., grid layouts with empty cells)\n */\nexport function ensureWidgetIds(\n widgets: readonly (WidgetSchema | null)[],\n): (WidgetSchema | null)[] {\n return widgets?.map((widget, index) => {\n if (!widget) return null;\n return {\n ...widget,\n id: widget.id || `widget-${index}-${crypto.randomUUID()}`,\n };\n });\n}\n\n/**\n * Deep clones a widget and regenerates all IDs (including nested children)\n */\nexport function deepCloneWidget(widget: WidgetSchema): WidgetSchema {\n // Handle LayoutWidget children recursively\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (\n isWidgetType(widget, WIDGET_TYPE_NAMES.Layout) &&\n Array.isArray(widget.props.children)\n ) {\n // Build cloned widget with recursively cloned children\n return {\n type: widget.type,\n props: {\n ...widget.props,\n children: (\n widget.props.children as readonly (WidgetSchema | null)[]\n ).map((child) => (child ? deepCloneWidget(child) : null)),\n },\n id: generateWidgetId(widget.type),\n };\n }\n\n // Clone non-container widget\n return {\n type: widget.type,\n props: { ...widget.props },\n id: generateWidgetId(widget.type),\n };\n}\n\n/**\n * Deep clones an array of widgets and regenerates all IDs\n */\nexport function deepCloneWidgets(\n widgets: readonly WidgetSchema[],\n): WidgetSchema[] {\n return widgets.map((widget) => deepCloneWidget(widget));\n}\n\n/**\n * Gets a widget at a specific path in the widget tree\n */\nexport function getWidgetByPath(\n screen: readonly (WidgetSchema | null)[],\n path: WidgetPath,\n): WidgetSchema | null {\n if (path.length === 0 || path[0] === undefined) return null;\n\n let current: WidgetSchema | null = screen[path[0]] ?? null;\n\n for (let i = 1; i < path.length; i++) {\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (!isWidgetType(current, WIDGET_TYPE_NAMES.Layout)) return null;\n\n const children = (current.props.children ??\n []) as readonly (WidgetSchema | null)[];\n const index = path[i];\n if (index === undefined) return null;\n current = children[index] ?? null;\n }\n\n return current;\n}\n\n/**\n * Groups children by column index.\n * Accepts nullable children (sparse arrays from grid layouts) and filters them out.\n */\nexport function groupChildrenByColumn(\n children: readonly (WidgetSchema | null)[],\n columnCount: number,\n): WidgetSchema[][] {\n // Use explicit type annotation to avoid never[] inference (narrowing-empty-array-type rule)\n const columns: WidgetSchema[][] = Array.from(\n { length: columnCount },\n (): WidgetSchema[] => [],\n );\n\n // Filter out nulls first, then process (truthiness narrowing rule)\n // This avoids the narrowing-callback-scope issue since we work with non-null array\n const nonNullChildren = children.filter(\n (child): child is WidgetSchema => child !== null,\n );\n\n for (const child of nonNullChildren) {\n // Default to column 0 if no columnIndex specified\n const colIndex = Math.min(child.columnIndex ?? 0, columnCount - 1);\n const column = columns[colIndex];\n if (column) {\n column.push(child);\n }\n }\n\n return columns;\n}\n","import type { ComponentType } from \"react\";\nimport { useCallback } from \"react\";\nimport type React from \"react\";\nimport type {\n WidgetSchema,\n TypedWidgetSchema,\n WidgetPath,\n} from \"@fluid-app/portal-core/types\";\nimport { WIDGET_TYPE_NAMES } from \"@fluid-app/portal-core/types\";\nimport { RegistryProvider, useRegistry } from \"../contexts/RegistryContext\";\n\nexport interface ScreenRendererProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n> {\n /** Array of widget schemas to render */\n screen: TypedWidgetSchema<T>[] | WidgetSchema[];\n /** Widget registry mapping type names to components */\n registry?: T;\n /** Container widgets (like LayoutWidget) receive these additional props */\n containerWidgetTypes?: string[];\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\ninterface ScreenRendererContentProps {\n screen: WidgetSchema[];\n containerWidgetTypes: string[];\n className?: string | undefined;\n}\n\n/**\n * Internal component that uses the registry from context.\n * This allows us to avoid prop drilling the registry.\n */\nfunction ScreenRendererContent({\n screen,\n containerWidgetTypes,\n className,\n}: ScreenRendererContentProps) {\n const registry = useRegistry();\n\n // Check if a widget type is a container\n const isContainerWidget = useCallback(\n (widgetType: string) => containerWidgetTypes.includes(widgetType),\n [containerWidgetTypes],\n );\n\n // Render widgets recursively with path tracking\n const renderWidget = useCallback(\n (widget: WidgetSchema, index: number, currentPath: WidgetPath) => {\n const Component = registry[widget.type];\n if (!Component) {\n console.warn(\n `Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n const widgetPath = [...currentPath, index];\n\n // Build additional props for container widgets\n const additionalProps = isContainerWidget(widget.type)\n ? {\n widgetId: widget.id,\n widgetPath,\n }\n : {};\n\n const props = {\n ...widget.props,\n ...additionalProps,\n };\n\n return (\n <div\n key={widget.id || index}\n className=\"h-full w-full overflow-x-hidden\"\n >\n <Component {...props} />\n </div>\n );\n },\n [registry, isContainerWidget],\n );\n\n return (\n <div className={className || \"h-full w-full\"}>\n {screen?.map((item, index) => {\n // Skip null items (empty grid cells)\n if (!item) return null;\n return renderWidget(item, index, []);\n })}\n </div>\n );\n}\n\n/**\n * ScreenRenderer - View-only component for rendering widget screens.\n *\n * This is a simplified version designed for the SDK that only handles\n * rendering widgets without edit mode or drag-and-drop functionality.\n *\n * Features:\n * - Static rendering of widgets\n * - Registry context: No prop drilling for widget registry\n * - Container widget support with path tracking\n *\n * Usage:\n * ```tsx\n * import { ScreenRenderer } from '@fluid-app/portal-widgets/core';\n * import { WIDGET_REGISTRY } from '@fluid-app/portal-widgets/widgets';\n *\n * <ScreenRenderer\n * screen={widgets}\n * registry={WIDGET_REGISTRY}\n * />\n * ```\n */\nexport function ScreenRenderer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n>(props: ScreenRendererProps<T>): React.JSX.Element {\n const {\n screen,\n registry,\n containerWidgetTypes = [\n WIDGET_TYPE_NAMES.Layout,\n WIDGET_TYPE_NAMES.Container,\n ],\n className,\n } = props;\n\n // Conditionally pass registry prop to satisfy exactOptionalPropertyTypes\n // When registry is undefined, we omit it entirely and let RegistryProvider inherit from parent\n const registryProviderProps = registry !== undefined ? { registry } : {};\n\n return (\n <RegistryProvider {...registryProviderProps}>\n {/* Type assertion required: TypedWidgetSchema<T>[] is a subtype of WidgetSchema[]\n when T's components accept props compatible with WidgetSchema props.\n This enables type-safe widget rendering with custom registries while allowing\n the internal renderer to work with the base WidgetSchema type. */}\n <ScreenRendererContent\n screen={screen as WidgetSchema[]}\n containerWidgetTypes={containerWidgetTypes}\n className={className}\n />\n </RegistryProvider>\n );\n}\n","import { ScreenRenderer as DefaultScreenRenderer } from \"../core/ScreenRenderer\";\nimport { useRegistry } from \"../contexts/RegistryContext\";\nimport { useScreenRenderer } from \"../contexts/ScreenRendererContext\";\nimport type {\n TypedWidgetSchema,\n WidgetSchema,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n SectionLayoutType,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport { sectionLayoutConfig } from \"@fluid-app/portal-core/types\";\nimport type { ComponentType } from \"react\";\nimport type React from \"react\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getGapField,\n getPaddingField,\n gapValues,\n} from \"../core/fields\";\nimport { groupChildrenByColumn } from \"@fluid-app/portal-core/widget-utils\";\n\nconst DEFAULT_BACKGROUND: BackgroundValue = {\n type: \"solid\",\n color: \"background\",\n};\nconst DEFAULT_CHILDREN: (WidgetSchema | null)[] = [];\n\ntype LayoutProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>> = Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ComponentType<any>\n >,\n> = {\n // Section layout type (masonry-style column configuration)\n sectionLayout?: SectionLayoutType;\n\n // Legacy props (deprecated, kept for backward compatibility)\n type?: \"flex\" | \"grid\";\n columns?: number;\n rows?: number;\n direction?: string;\n justify?: string;\n align?: string;\n wrap?: boolean;\n\n // Spacing\n gap?: number; // deprecated, use gapSize instead\n gapSize?: GapOptions;\n\n // Design\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Content - widgets with columnIndex property for masonry layout\n children: (TypedWidgetSchema<T> | null)[] | (WidgetSchema | null)[];\n\n // Widget registry (passed down from parent renderer)\n registry?: T;\n\n // Styling\n className?: string;\n minHeight?: number;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const LayoutWidget = <T extends Record<string, ComponentType<any>>>({\n sectionLayout = \"single-column\",\n gap = 4,\n gapSize,\n background = DEFAULT_BACKGROUND,\n padding = 0,\n borderRadius = \"md\",\n children = DEFAULT_CHILDREN,\n registry,\n className = \"\",\n minHeight,\n}: LayoutProps<T>): React.JSX.Element => {\n // Get registry from context if not provided as prop\n const contextRegistry = useRegistry();\n const effectiveRegistry = registry || contextRegistry;\n\n // Use ScreenRenderer from context (e.g. portal-builder's), fall back to local\n const ContextScreenRenderer = useScreenRenderer();\n const ScreenRenderer = ContextScreenRenderer ?? DefaultScreenRenderer;\n\n // Extract background values\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n // Get layout configuration\n const layoutConfig = sectionLayoutConfig[sectionLayout];\n const columnCount = layoutConfig.columns;\n\n // Calculate gap size\n const gapSizeValue = gapSize ? gapValues[gapSize] : gap;\n\n // Build CSS classes for masonry layout with responsive columns\n const layoutClasses = [\n \"grid\",\n \"grid-cols-1\", // Mobile: single column\n layoutConfig.gridClasses, // Desktop (@md+): configured columns\n `bg-${backgroundColor} p-${padding} rounded-${borderRadius}`,\n `gap-${gapSizeValue}`,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n // Container style\n const containerStyle: React.CSSProperties = {\n ...(minHeight ? { minHeight: `${minHeight}px` } : {}),\n alignItems: \"start\", // Masonry-style: columns don't stretch to match height\n backgroundImage,\n };\n\n // If no registry is available, we can't render the children\n if (!effectiveRegistry) {\n console.warn(\"Layout widget: No registry provided, cannot render children\");\n return <div className={layoutClasses} style={containerStyle} />;\n }\n\n // Group children by column\n const columnGroups = groupChildrenByColumn(\n children as WidgetSchema[],\n columnCount,\n );\n\n // Regular render mode - render each column as a flex column\n return (\n <div className=\"@container\">\n <div className={layoutClasses} style={containerStyle}>\n {columnGroups.map((columnChildren, colIndex) => (\n <ScreenRenderer\n key={colIndex}\n screen={columnChildren}\n registry={effectiveRegistry}\n className={`flex flex-col gap-${gapSizeValue}`}\n />\n ))}\n </div>\n </div>\n );\n};\n\nexport const layoutWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"LayoutWidget\",\n displayName: \"Layout\",\n fields: [\n // Layout Configuration - Visual selector matching Figma design\n {\n key: \"sectionLayout\",\n label: \"Layout Type\",\n type: \"sectionLayoutSelect\",\n description: \"Column layout configuration\",\n defaultValue: \"single-column\",\n group: \"Layout\",\n },\n\n // Design\n getGapField({\n key: \"gapSize\",\n label: \"Gap\",\n description: \"Gap between columns and widgets\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the layout container\",\n defaultValue: \"background\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding for the layout container\",\n defaultValue: 4,\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the layout container\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n ],\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LayoutWidget-fOBC-EML.cjs","names":["ScreenRenderer","DefaultScreenRenderer","gapValues","getGapField","getPaddingField","getBorderRadiusField"],"sources":["../../widgets/src/contexts/RegistryContext.tsx","../../widgets/src/contexts/ScreenRendererContext.tsx","../../core/src/types/widget-schema.ts","../../core/src/types/field-types.ts","../../core/src/widget-utils/widget-utils.ts","../../widgets/src/core/ScreenRenderer.tsx","../../widgets/src/widgets/LayoutWidget.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base props interface for widget components.\n * All widgets receive at least these props from the ScreenRenderer.\n */\nexport interface WidgetBaseProps {\n readonly widget: WidgetSchema;\n readonly path?: readonly number[];\n}\n\n/**\n * Generic widget component type.\n * Widgets receive WidgetBaseProps and may have additional props.\n */\nexport type WidgetComponent<P extends WidgetBaseProps = WidgetBaseProps> =\n ComponentType<P>;\n\n/**\n * Registry mapping widget type names to their components.\n * The keys are widget type strings (e.g., \"TextWidget\", \"ButtonWidget\").\n * Using WidgetComponent for the value type provides better type safety than `any`.\n */\nexport type WidgetRegistry = Readonly<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Widget props vary by type\n Record<string, WidgetComponent<any>>\n>;\n\n/**\n * Context for providing the widget registry to all components in the tree.\n * This eliminates prop drilling and makes the registry accessible anywhere.\n *\n * Default to undefined - registry must be provided by a RegistryProvider.\n */\nexport const RegistryContext: React.Context<WidgetRegistry | undefined> =\n createContext<WidgetRegistry | undefined>(undefined);\n\nexport interface RegistryProviderProps {\n registry?: WidgetRegistry;\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the widget registry available to all descendants.\n * A registry must be provided either via props or by nesting within another RegistryProvider.\n */\nexport function RegistryProvider({\n registry,\n children,\n}: RegistryProviderProps): React.JSX.Element {\n // If no registry provided, inherit from parent provider\n const parentRegistry = useContext(RegistryContext);\n const contextValue = registry ?? parentRegistry;\n\n if (!contextValue) {\n throw new Error(\n \"RegistryProvider requires a registry prop or must be nested within another RegistryProvider.\",\n );\n }\n\n return (\n <RegistryContext.Provider value={contextValue}>\n {children}\n </RegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the widget registry from anywhere in the component tree.\n * Must be used within a RegistryProvider that has a registry prop.\n */\nexport function useRegistry(): WidgetRegistry {\n const registry = useContext(RegistryContext);\n\n if (!registry) {\n throw new Error(\n \"useRegistry must be used within a RegistryProvider with a registry prop.\",\n );\n }\n\n return registry;\n}\n","import React, { createContext, useContext, type ComponentType } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { WidgetRegistry } from \"./RegistryContext\";\n\n/**\n * Props that any ScreenRenderer must accept.\n * This is the minimal interface used by container widgets (e.g. LayoutWidget)\n * to render their children.\n */\nexport interface ScreenRendererComponentProps {\n screen: WidgetSchema[];\n registry?: WidgetRegistry;\n className?: string;\n}\n\n/**\n * Context for providing an alternative ScreenRenderer component.\n *\n * This allows packages like portal-builder to inject their own ScreenRenderer\n * (with edit mode, drag-and-drop, etc.) so that container widgets in portal-widgets\n * use the richer renderer instead of the basic view-only one.\n *\n * Default is undefined — container widgets fall back to the local ScreenRenderer import.\n */\nexport const ScreenRendererContext: React.Context<\n ComponentType<ScreenRendererComponentProps> | undefined\n> = createContext<ComponentType<ScreenRendererComponentProps> | undefined>(\n undefined,\n);\n\n/**\n * Hook to get the ScreenRenderer component from context.\n * Returns undefined if no override has been provided.\n */\nexport function useScreenRenderer():\n | ComponentType<ScreenRendererComponentProps>\n | undefined {\n return useContext(ScreenRendererContext);\n}\n","import type { ComponentType } from \"react\";\nimport type { DataSourceConfig } from \"../data-sources/types\";\n\n/**\n * Widget type names as a const object.\n * This serves as the single source of truth for widget discriminants.\n * Use `as const` for literal type inference (safety-as-const-deep-readonly rule).\n */\nexport const WIDGET_TYPE_NAMES = {\n Alert: \"AlertWidget\",\n Calendar: \"CalendarWidget\",\n Carousel: \"CarouselWidget\",\n CatchUp: \"CatchUpWidget\",\n Chart: \"ChartWidget\",\n Container: \"ContainerWidget\",\n Embed: \"EmbedWidget\",\n Image: \"ImageWidget\",\n Layout: \"LayoutWidget\",\n List: \"ListWidget\",\n MySite: \"MySiteWidget\",\n Nested: \"NestedWidget\",\n QuickShare: \"QuickShareWidget\",\n RecentActivity: \"RecentActivityWidget\",\n Spacer: \"SpacerWidget\",\n Table: \"TableWidget\",\n Text: \"TextWidget\",\n ToDo: \"ToDoWidget\",\n Video: \"VideoWidget\",\n} as const;\n\n/**\n * Union of all known widget type names.\n * Derived from WIDGET_TYPE_NAMES to avoid duplication (deriving-typeof-for-object-keys rule).\n */\nexport type WidgetTypeName =\n (typeof WIDGET_TYPE_NAMES)[keyof typeof WIDGET_TYPE_NAMES];\n\n/**\n * Legacy alias for backwards compatibility.\n * Prefer using WidgetTypeName for new code when you need the union type.\n */\nexport type WidgetType = string;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type WidgetRegistry = Record<WidgetType, ComponentType<any>>;\n\n/**\n * Base widget schema with loose typing for runtime data.\n * Use TypedWidgetSchema<T> when you have a known registry for better type safety.\n */\nexport type WidgetSchema = {\n readonly type: WidgetType;\n readonly props: Readonly<Record<string, unknown>>;\n readonly id?: string; // Optional unique identifier for drag-and-drop\n /** Optional data source configuration for data-bound widgets */\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n};\n\n/**\n * Type-safe widget schema based on registry.\n * Uses discriminated unions - the `type` field serves as discriminant.\n * When narrowed (e.g., `if (widget.type === \"AlertWidget\")`),\n * TypeScript automatically knows the correct props type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TypedWidgetSchema<T extends Record<string, ComponentType<any>>> = {\n [K in keyof T]: {\n readonly type: K;\n readonly props: Readonly<React.ComponentProps<T[K]>>;\n readonly id?: string;\n readonly dataSource?: DataSourceConfig | undefined;\n /** Column index for masonry layouts (0-indexed) */\n readonly columnIndex?: number;\n };\n}[keyof T];\n\n/**\n * Widget path in the tree - array of indices.\n * Readonly tuple to prevent accidental mutation.\n */\nexport type WidgetPath = readonly number[];\n\n// ============================================================================\n// Type Guards and Assertion Functions\n// ============================================================================\n\n/**\n * Type predicate to check if a string is a known widget type name.\n * Use for runtime validation of widget types.\n *\n * @example\n * if (isWidgetTypeName(widget.type)) {\n * // TypeScript knows widget.type is WidgetTypeName\n * }\n */\nexport function isWidgetTypeName(type: string): type is WidgetTypeName {\n // Type assertion required in type guard: Object.values() returns string[], but we\n // need to check against WidgetTypeName values. The assertion is safe because we're\n // checking membership, and the return type predicate ensures correct narrowing.\n return Object.values(WIDGET_TYPE_NAMES).includes(type as WidgetTypeName);\n}\n\n/**\n * Type predicate to check if a widget has a specific type.\n * Enables type-safe widget narrowing without `as` assertions.\n *\n * @example\n * if (isWidgetType(widget, \"LayoutWidget\")) {\n * // TypeScript knows widget.type === \"LayoutWidget\"\n * // and widget.props is LayoutWidget props\n * }\n */\nexport function isWidgetType<T extends WidgetTypeName>(\n widget: WidgetSchema | null | undefined,\n typeName: T,\n): widget is WidgetSchema & { readonly type: T } {\n return widget != null && widget.type === typeName;\n}\n\n/**\n * Helper for exhaustive switch statements on widget types.\n * Use in the default case to ensure all widget types are handled.\n *\n * @example\n * switch (widget.type) {\n * case \"AlertWidget\": return handleAlert();\n * case \"TextWidget\": return handleText();\n * // ... all other widget types\n * default: return assertNever(widget.type, \"widget type\");\n * }\n */\nexport function assertNever(value: never, context?: string): never {\n const message = context\n ? `Unexpected ${context}: ${String(value)}`\n : `Unexpected value: ${String(value)}`;\n throw new Error(message);\n}\n\n/**\n * Assertion function that throws if value is undefined.\n * Narrows the type to exclude undefined.\n *\n * @example\n * const widget = screen[0];\n * assertDefined(widget, \"widget at index 0\");\n * // TypeScript knows widget is defined here\n */\nexport function assertDefined<T>(\n value: T | undefined | null,\n name?: string,\n): asserts value is T {\n if (value == null) {\n throw new Error(name ? `${name} is required` : \"Value is required\");\n }\n}\n","import type { ShareableItem } from \"./shareable-item\";\n\n// ============================================================================\n// Color Options - Derive type from constant for single source of truth\n// ============================================================================\n\n/**\n * Color options constant - single source of truth for color values.\n * Use COLOR_OPTIONS.primary instead of \"primary\" for type-safe comparisons.\n */\nexport const COLOR_OPTIONS = {\n background: \"background\",\n foreground: \"foreground\",\n primary: \"primary\",\n secondary: \"secondary\",\n accent: \"accent\",\n muted: \"muted\",\n destructive: \"destructive\",\n transparent: \"transparent\",\n} as const;\n\n/**\n * Union type of all color options, derived from COLOR_OPTIONS constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type ColorOptions = (typeof COLOR_OPTIONS)[keyof typeof COLOR_OPTIONS];\n\n// ============================================================================\n// Size Options - Derive types from constants for single source of truth\n// ============================================================================\n\nexport const FONT_SIZE_OPTIONS = {\n \"2xl\": \"2xl\",\n xl: \"xl\",\n lg: \"lg\",\n md: \"md\",\n sm: \"sm\",\n xs: \"xs\",\n} as const;\n\nexport type FontSizeOptions =\n (typeof FONT_SIZE_OPTIONS)[keyof typeof FONT_SIZE_OPTIONS];\n\nexport const BORDER_RADIUS_OPTIONS = {\n none: \"none\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n full: \"full\",\n} as const;\n\nexport type BorderRadiusOptions =\n (typeof BORDER_RADIUS_OPTIONS)[keyof typeof BORDER_RADIUS_OPTIONS];\n\n/** Padding values - numeric, so we use a tuple for derivation */\nexport const PADDING_VALUES = [0, 2, 4, 6, 8, 10] as const;\nexport type PaddingOptions = (typeof PADDING_VALUES)[number];\n\nexport const BUTTON_SIZE_OPTIONS = {\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type ButtonSizeOptions =\n (typeof BUTTON_SIZE_OPTIONS)[keyof typeof BUTTON_SIZE_OPTIONS];\n\nexport const GAP_OPTIONS = {\n none: \"none\",\n xs: \"xs\",\n sm: \"sm\",\n md: \"md\",\n lg: \"lg\",\n xl: \"xl\",\n} as const;\n\nexport type GapOptions = (typeof GAP_OPTIONS)[keyof typeof GAP_OPTIONS];\n\n// ============================================================================\n// Alignment Options - Derive from constants\n// ============================================================================\n\nexport const VERTICAL_ALIGN_OPTIONS = {\n top: \"top\",\n center: \"center\",\n bottom: \"bottom\",\n} as const;\n\nexport type VerticalAlign =\n (typeof VERTICAL_ALIGN_OPTIONS)[keyof typeof VERTICAL_ALIGN_OPTIONS];\n\nexport const HORIZONTAL_ALIGN_OPTIONS = {\n left: \"left\",\n center: \"center\",\n right: \"right\",\n} as const;\n\nexport type HorizontalAlign =\n (typeof HORIZONTAL_ALIGN_OPTIONS)[keyof typeof HORIZONTAL_ALIGN_OPTIONS];\n\nexport type AlignOptions = {\n vertical?: VerticalAlign;\n horizontal?: HorizontalAlign;\n};\n\n// ============================================================================\n// Background Options - Derive from constant\n// ============================================================================\n\nexport const BACKGROUND_TYPES = {\n solid: \"solid\",\n image: \"image\",\n} as const;\n\nexport type BackgroundType =\n (typeof BACKGROUND_TYPES)[keyof typeof BACKGROUND_TYPES];\nexport interface BackgroundValue {\n type: BackgroundType;\n color?: ColorOptions;\n resource?: ShareableItem;\n}\n\n// ============================================================================\n// Section Layout - Derive type from config keys (single source of truth)\n// ============================================================================\n\n/**\n * Section layout configuration - single source of truth for layout types.\n * SectionLayoutType is derived from these keys to prevent drift.\n */\nexport const SECTION_LAYOUT_CONFIG: {\n readonly \"single-column\": {\n readonly columns: 1;\n readonly widths: readonly [\"1fr\"];\n readonly gridClasses: \"\";\n };\n readonly \"2c-equal\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-2\";\n };\n readonly \"2c-left-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[2fr_1fr]\";\n };\n readonly \"2c-right-wider\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"2fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr]\";\n };\n readonly \"2c-left-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"1fr\", \"3fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_3fr]\";\n };\n readonly \"2c-right-narrow\": {\n readonly columns: 2;\n readonly widths: readonly [\"3fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[3fr_1fr]\";\n };\n readonly \"3c-equal\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"1fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-3\";\n };\n readonly \"3c-middle-wider\": {\n readonly columns: 3;\n readonly widths: readonly [\"1fr\", \"2fr\", \"1fr\"];\n readonly gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\";\n };\n} = {\n \"single-column\": { columns: 1, widths: [\"1fr\"], gridClasses: \"\" },\n \"2c-equal\": {\n columns: 2,\n widths: [\"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-2\",\n },\n \"2c-left-wider\": {\n columns: 2,\n widths: [\"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[2fr_1fr]\",\n },\n \"2c-right-wider\": {\n columns: 2,\n widths: [\"1fr\", \"2fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr]\",\n },\n \"2c-left-narrow\": {\n columns: 2,\n widths: [\"1fr\", \"3fr\"],\n gridClasses: \"@md:grid-cols-[1fr_3fr]\",\n },\n \"2c-right-narrow\": {\n columns: 2,\n widths: [\"3fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[3fr_1fr]\",\n },\n \"3c-equal\": {\n columns: 3,\n widths: [\"1fr\", \"1fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-3\",\n },\n \"3c-middle-wider\": {\n columns: 3,\n widths: [\"1fr\", \"2fr\", \"1fr\"],\n gridClasses: \"@md:grid-cols-[1fr_2fr_1fr]\",\n },\n} as const satisfies Record<\n string,\n {\n readonly columns: number;\n readonly widths: readonly string[];\n readonly gridClasses: string;\n }\n>;\n\n/**\n * Union type of all section layout types, derived from SECTION_LAYOUT_CONFIG keys.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type SectionLayoutType = keyof typeof SECTION_LAYOUT_CONFIG;\n\n/** @deprecated Use SECTION_LAYOUT_CONFIG instead */\nexport const sectionLayoutConfig: typeof SECTION_LAYOUT_CONFIG =\n SECTION_LAYOUT_CONFIG;\n","import type { WidgetSchema, WidgetPath } from \"../types\";\nimport { WIDGET_TYPE_NAMES, isWidgetType } from \"../types\";\n\n/**\n * Generates a unique ID for a widget\n */\nexport function generateWidgetId(type?: string): string {\n const uuid = crypto.randomUUID();\n return type ? `${type}-${uuid}` : uuid;\n}\n\n/**\n * Converts a widget path to a unique string identifier\n */\nexport function pathToId(path: WidgetPath): string {\n return path.join(\"-\");\n}\n\n/**\n * Converts a path string back to a WidgetPath array\n */\nexport function idToPath(id: string): WidgetPath {\n return id.split(\"-\").map((segment) => parseInt(segment, 10));\n}\n\n/**\n * Ensures all widgets in an array have valid IDs\n * Note: Handles null values for sparse arrays (e.g., grid layouts with empty cells)\n */\nexport function ensureWidgetIds(\n widgets: readonly (WidgetSchema | null)[],\n): (WidgetSchema | null)[] {\n return widgets?.map((widget, index) => {\n if (!widget) return null;\n return {\n ...widget,\n id: widget.id || `widget-${index}-${crypto.randomUUID()}`,\n };\n });\n}\n\n/**\n * Deep clones a widget and regenerates all IDs (including nested children)\n */\nexport function deepCloneWidget(widget: WidgetSchema): WidgetSchema {\n // Handle LayoutWidget children recursively\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (\n isWidgetType(widget, WIDGET_TYPE_NAMES.Layout) &&\n Array.isArray(widget.props.children)\n ) {\n // Build cloned widget with recursively cloned children\n return {\n type: widget.type,\n props: {\n ...widget.props,\n children: (\n widget.props.children as readonly (WidgetSchema | null)[]\n ).map((child) => (child ? deepCloneWidget(child) : null)),\n },\n id: generateWidgetId(widget.type),\n };\n }\n\n // Clone non-container widget\n return {\n type: widget.type,\n props: { ...widget.props },\n id: generateWidgetId(widget.type),\n };\n}\n\n/**\n * Deep clones an array of widgets and regenerates all IDs\n */\nexport function deepCloneWidgets(\n widgets: readonly WidgetSchema[],\n): WidgetSchema[] {\n return widgets.map((widget) => deepCloneWidget(widget));\n}\n\n/**\n * Gets a widget at a specific path in the widget tree\n */\nexport function getWidgetByPath(\n screen: readonly (WidgetSchema | null)[],\n path: WidgetPath,\n): WidgetSchema | null {\n if (path.length === 0 || path[0] === undefined) return null;\n\n let current: WidgetSchema | null = screen[path[0]] ?? null;\n\n for (let i = 1; i < path.length; i++) {\n // Use type guard for discriminated union narrowing (narrowing-discriminated-unions rule)\n if (!isWidgetType(current, WIDGET_TYPE_NAMES.Layout)) return null;\n\n const children = (current.props.children ??\n []) as readonly (WidgetSchema | null)[];\n const index = path[i];\n if (index === undefined) return null;\n current = children[index] ?? null;\n }\n\n return current;\n}\n\n/**\n * Groups children by column index.\n * Accepts nullable children (sparse arrays from grid layouts) and filters them out.\n */\nexport function groupChildrenByColumn(\n children: readonly (WidgetSchema | null)[],\n columnCount: number,\n): WidgetSchema[][] {\n // Use explicit type annotation to avoid never[] inference (narrowing-empty-array-type rule)\n const columns: WidgetSchema[][] = Array.from(\n { length: columnCount },\n (): WidgetSchema[] => [],\n );\n\n // Filter out nulls first, then process (truthiness narrowing rule)\n // This avoids the narrowing-callback-scope issue since we work with non-null array\n const nonNullChildren = children.filter(\n (child): child is WidgetSchema => child !== null,\n );\n\n for (const child of nonNullChildren) {\n // Default to column 0 if no columnIndex specified\n const colIndex = Math.min(child.columnIndex ?? 0, columnCount - 1);\n const column = columns[colIndex];\n if (column) {\n column.push(child);\n }\n }\n\n return columns;\n}\n","import type { ComponentType } from \"react\";\nimport { useCallback } from \"react\";\nimport type React from \"react\";\nimport type {\n WidgetSchema,\n TypedWidgetSchema,\n WidgetPath,\n} from \"@fluid-app/portal-core/types\";\nimport { WIDGET_TYPE_NAMES } from \"@fluid-app/portal-core/types\";\nimport { RegistryProvider, useRegistry } from \"../contexts/RegistryContext\";\n\nexport interface ScreenRendererProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n> {\n /** Array of widget schemas to render */\n screen: TypedWidgetSchema<T>[] | WidgetSchema[];\n /** Widget registry mapping type names to components */\n registry?: T;\n /** Container widgets (like LayoutWidget) receive these additional props */\n containerWidgetTypes?: string[];\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\ninterface ScreenRendererContentProps {\n screen: WidgetSchema[];\n containerWidgetTypes: string[];\n className?: string | undefined;\n}\n\n/**\n * Internal component that uses the registry from context.\n * This allows us to avoid prop drilling the registry.\n */\nfunction ScreenRendererContent({\n screen,\n containerWidgetTypes,\n className,\n}: ScreenRendererContentProps) {\n const registry = useRegistry();\n\n // Check if a widget type is a container\n const isContainerWidget = useCallback(\n (widgetType: string) => containerWidgetTypes.includes(widgetType),\n [containerWidgetTypes],\n );\n\n // Render widgets recursively with path tracking\n const renderWidget = useCallback(\n (widget: WidgetSchema, index: number, currentPath: WidgetPath) => {\n const Component = registry[widget.type];\n if (!Component) {\n console.warn(\n `Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n const widgetPath = [...currentPath, index];\n\n // Build additional props for container widgets\n const additionalProps = isContainerWidget(widget.type)\n ? {\n widgetId: widget.id,\n widgetPath,\n }\n : {};\n\n const props = {\n ...widget.props,\n ...additionalProps,\n };\n\n return (\n <div\n key={widget.id || index}\n className=\"h-full w-full overflow-x-hidden\"\n >\n <Component {...props} />\n </div>\n );\n },\n [registry, isContainerWidget],\n );\n\n return (\n <div className={className || \"h-full w-full\"}>\n {screen?.map((item, index) => {\n // Skip null items (empty grid cells)\n if (!item) return null;\n return renderWidget(item, index, []);\n })}\n </div>\n );\n}\n\n/**\n * ScreenRenderer - View-only component for rendering widget screens.\n *\n * This is a simplified version designed for the SDK that only handles\n * rendering widgets without edit mode or drag-and-drop functionality.\n *\n * Features:\n * - Static rendering of widgets\n * - Registry context: No prop drilling for widget registry\n * - Container widget support with path tracking\n *\n * Usage:\n * ```tsx\n * import { ScreenRenderer } from '@fluid-app/portal-widgets/core';\n * import { WIDGET_REGISTRY } from '@fluid-app/portal-widgets/widgets';\n *\n * <ScreenRenderer\n * screen={widgets}\n * registry={WIDGET_REGISTRY}\n * />\n * ```\n */\nexport function ScreenRenderer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>>,\n>(props: ScreenRendererProps<T>): React.JSX.Element {\n const {\n screen,\n registry,\n containerWidgetTypes = [\n WIDGET_TYPE_NAMES.Layout,\n WIDGET_TYPE_NAMES.Container,\n ],\n className,\n } = props;\n\n // Conditionally pass registry prop to satisfy exactOptionalPropertyTypes\n // When registry is undefined, we omit it entirely and let RegistryProvider inherit from parent\n const registryProviderProps = registry !== undefined ? { registry } : {};\n\n return (\n <RegistryProvider {...registryProviderProps}>\n {/* Type assertion required: TypedWidgetSchema<T>[] is a subtype of WidgetSchema[]\n when T's components accept props compatible with WidgetSchema props.\n This enables type-safe widget rendering with custom registries while allowing\n the internal renderer to work with the base WidgetSchema type. */}\n <ScreenRendererContent\n screen={screen as WidgetSchema[]}\n containerWidgetTypes={containerWidgetTypes}\n className={className}\n />\n </RegistryProvider>\n );\n}\n","import { ScreenRenderer as DefaultScreenRenderer } from \"../core/ScreenRenderer\";\nimport { useRegistry } from \"../contexts/RegistryContext\";\nimport { useScreenRenderer } from \"../contexts/ScreenRendererContext\";\nimport type {\n TypedWidgetSchema,\n WidgetSchema,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n SectionLayoutType,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport { sectionLayoutConfig } from \"@fluid-app/portal-core/types\";\nimport type { ComponentType } from \"react\";\nimport type React from \"react\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getGapField,\n getPaddingField,\n gapValues,\n} from \"../core/fields\";\nimport { groupChildrenByColumn } from \"@fluid-app/portal-core/widget-utils\";\n\nconst DEFAULT_BACKGROUND: BackgroundValue = {\n type: \"solid\",\n color: \"background\",\n};\nconst DEFAULT_CHILDREN: (WidgetSchema | null)[] = [];\n\ntype LayoutProps<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends Record<string, ComponentType<any>> = Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ComponentType<any>\n >,\n> = {\n // Section layout type (masonry-style column configuration)\n sectionLayout?: SectionLayoutType;\n\n // Legacy props (deprecated, kept for backward compatibility)\n type?: \"flex\" | \"grid\";\n columns?: number;\n rows?: number;\n direction?: string;\n justify?: string;\n align?: string;\n wrap?: boolean;\n\n // Spacing\n gap?: number; // deprecated, use gapSize instead\n gapSize?: GapOptions;\n\n // Design\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Content - widgets with columnIndex property for masonry layout\n children: (TypedWidgetSchema<T> | null)[] | (WidgetSchema | null)[];\n\n // Widget registry (passed down from parent renderer)\n registry?: T;\n\n // Styling\n className?: string;\n minHeight?: number;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const LayoutWidget = <T extends Record<string, ComponentType<any>>>({\n sectionLayout = \"single-column\",\n gap = 4,\n gapSize,\n background = DEFAULT_BACKGROUND,\n padding = 0,\n borderRadius = \"md\",\n children = DEFAULT_CHILDREN,\n registry,\n className = \"\",\n minHeight,\n}: LayoutProps<T>): React.JSX.Element => {\n // Get registry from context if not provided as prop\n const contextRegistry = useRegistry();\n const effectiveRegistry = registry || contextRegistry;\n\n // Use ScreenRenderer from context (e.g. portal-builder's), fall back to local\n const ContextScreenRenderer = useScreenRenderer();\n const ScreenRenderer = ContextScreenRenderer ?? DefaultScreenRenderer;\n\n // Extract background values\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n // Get layout configuration\n const layoutConfig = sectionLayoutConfig[sectionLayout];\n const columnCount = layoutConfig.columns;\n\n // Calculate gap size\n const gapSizeValue = gapSize ? gapValues[gapSize] : gap;\n\n // Build CSS classes for masonry layout with responsive columns\n const layoutClasses = [\n \"grid\",\n \"grid-cols-1\", // Mobile: single column\n layoutConfig.gridClasses, // Desktop (@md+): configured columns\n `bg-${backgroundColor} p-${padding} rounded-${borderRadius}`,\n `gap-${gapSizeValue}`,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n // Container style\n const containerStyle: React.CSSProperties = {\n ...(minHeight ? { minHeight: `${minHeight}px` } : {}),\n alignItems: \"start\", // Masonry-style: columns don't stretch to match height\n backgroundImage,\n };\n\n // If no registry is available, we can't render the children\n if (!effectiveRegistry) {\n console.warn(\"Layout widget: No registry provided, cannot render children\");\n return <div className={layoutClasses} style={containerStyle} />;\n }\n\n // Group children by column\n const columnGroups = groupChildrenByColumn(\n children as WidgetSchema[],\n columnCount,\n );\n\n // Regular render mode - render each column as a flex column\n return (\n <div className=\"@container\">\n <div className={layoutClasses} style={containerStyle}>\n {columnGroups.map((columnChildren, colIndex) => (\n <ScreenRenderer\n key={colIndex}\n screen={columnChildren}\n registry={effectiveRegistry}\n className={`flex flex-col gap-${gapSizeValue}`}\n />\n ))}\n </div>\n </div>\n );\n};\n\nexport const layoutWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"LayoutWidget\",\n displayName: \"Layout\",\n fields: [\n // Layout Configuration - Visual selector matching Figma design\n {\n key: \"sectionLayout\",\n label: \"Layout Type\",\n type: \"sectionLayoutSelect\",\n description: \"Column layout configuration\",\n defaultValue: \"single-column\",\n group: \"Layout\",\n },\n\n // Design\n getGapField({\n key: \"gapSize\",\n label: \"Gap\",\n description: \"Gap between columns and widgets\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the layout container\",\n defaultValue: \"background\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding for the layout container\",\n defaultValue: 4,\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the layout container\",\n defaultValue: \"md\",\n group: \"Design\",\n }),\n ],\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import "./FluidProvider-Bp1sNJnr.mjs";
1
+ import "./FluidProvider-x96kqsgN.mjs";
2
2
  import "./error-state-ChXg5_p-.mjs";
3
3
  import "./LayoutWidget-Dc_wvaLg.mjs";
4
4
  import "./WidgetInteractionContext-MZuUseep.mjs";
@@ -9,7 +9,7 @@ import "./AlertWidget-gHoPBcqf.mjs";
9
9
  import "./CalendarWidget-7ZL9lHKT.mjs";
10
10
  import "./purify.es-CS-U7fYi.mjs";
11
11
  import "./src-PSNQSAUF.mjs";
12
- import { n as messagingScreenPropertySchema, t as MessagingScreen } from "./MessagingScreen-7pC6SdRs.mjs";
12
+ import { n as messagingScreenPropertySchema, t as MessagingScreen } from "./MessagingScreen-We1B2pka.mjs";
13
13
  import "./scroll-arrows-DqQW_zzf.mjs";
14
14
  import "./MediaRenderer-DIlOQCjD.mjs";
15
15
  import "./CarouselWidget-jFV94CEm.mjs";
@@ -24,8 +24,8 @@ import "./NestedWidget-DXnktmBL.mjs";
24
24
  import "./QuickShareWidget-BaBc9fbT.mjs";
25
25
  import "./RecentActivityWidget-BdqjzwXL.mjs";
26
26
  import "./SpacerWidget-DXXnYNIX.mjs";
27
- import "./TableWidget-D_6Qi3m8.mjs";
27
+ import "./TableWidget-BbjQhI2A.mjs";
28
28
  import "./ToDoWidget-CmJIUKr0.mjs";
29
29
  import "./VideoWidget-D6aWKmRN.mjs";
30
- import "./es-BL8VBBDa.mjs";
30
+ import "./es-DxWiENwN.mjs";
31
31
  export { MessagingScreen, messagingScreenPropertySchema };
@@ -1,8 +1,8 @@
1
1
  const require_chunk = require("./chunk-9hOWP6kD.cjs");
2
- const require_FluidProvider = require("./FluidProvider-PqLUzyvQ.cjs");
2
+ const require_FluidProvider = require("./FluidProvider-BmkSwcgV.cjs");
3
3
  const require_src = require("./src-CaogiXSc.cjs");
4
- const require_use_fluid_api = require("./use-fluid-api-CTRIRz1H.cjs");
5
- const require_es = require("./es-kNOrmozy.cjs");
4
+ const require_use_fluid_api = require("./use-fluid-api-DvJuEWRf.cjs");
5
+ const require_es = require("./es-BtechuHV.cjs");
6
6
  let react = require("react");
7
7
  react = require_chunk.__toESM(react);
8
8
  let _tanstack_react_query = require("@tanstack/react-query");
@@ -77398,4 +77398,4 @@ Object.defineProperty(exports, "useMessagingConfig", {
77398
77398
  }
77399
77399
  });
77400
77400
 
77401
- //# sourceMappingURL=MessagingScreen-C-kF4Z5h.cjs.map
77401
+ //# sourceMappingURL=MessagingScreen-CRLd91tP.cjs.map