@babylonjs/shared-ui-components 9.2.0 → 9.2.2

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 (116) hide show
  1. package/fluent/hoc/buttonLine.js +2 -1
  2. package/fluent/hoc/buttonLine.js.map +1 -1
  3. package/fluent/primitives/accordion.contexts.js +2 -2
  4. package/fluent/primitives/accordion.contexts.js.map +1 -1
  5. package/fluent/primitives/contextMenu.js.map +1 -1
  6. package/fluent/primitives/popover.js.map +1 -1
  7. package/fluent/primitives/tooltip.d.ts +1 -1
  8. package/fluent/primitives/tooltip.js.map +1 -1
  9. package/modularTool/components/errorBoundary.d.ts +31 -0
  10. package/modularTool/components/errorBoundary.js +91 -0
  11. package/modularTool/components/errorBoundary.js.map +1 -0
  12. package/modularTool/components/extensibleAccordion.d.ts +67 -0
  13. package/modularTool/components/extensibleAccordion.js +148 -0
  14. package/modularTool/components/extensibleAccordion.js.map +1 -0
  15. package/modularTool/components/pane.d.ts +4 -0
  16. package/modularTool/components/pane.js +20 -0
  17. package/modularTool/components/pane.js.map +1 -0
  18. package/modularTool/components/teachingMoment.d.ts +20 -0
  19. package/modularTool/components/teachingMoment.js +17 -0
  20. package/modularTool/components/teachingMoment.js.map +1 -0
  21. package/modularTool/components/theme.d.ts +10 -0
  22. package/modularTool/components/theme.js +24 -0
  23. package/modularTool/components/theme.js.map +1 -0
  24. package/modularTool/components/uxContextProvider.d.ts +2 -0
  25. package/modularTool/components/uxContextProvider.js +19 -0
  26. package/modularTool/components/uxContextProvider.js.map +1 -0
  27. package/modularTool/contexts/extensionManagerContext.d.ts +6 -0
  28. package/modularTool/contexts/extensionManagerContext.js +6 -0
  29. package/modularTool/contexts/extensionManagerContext.js.map +1 -0
  30. package/modularTool/contexts/settingsContext.d.ts +3 -0
  31. package/modularTool/contexts/settingsContext.js +6 -0
  32. package/modularTool/contexts/settingsContext.js.map +1 -0
  33. package/modularTool/extensibility/builtInsExtensionFeed.d.ts +21 -0
  34. package/modularTool/extensibility/builtInsExtensionFeed.js +26 -0
  35. package/modularTool/extensibility/builtInsExtensionFeed.js.map +1 -0
  36. package/modularTool/extensibility/extensionFeed.d.ts +113 -0
  37. package/modularTool/extensibility/extensionFeed.js +2 -0
  38. package/modularTool/extensibility/extensionFeed.js.map +1 -0
  39. package/modularTool/extensibility/extensionManager.d.ts +111 -0
  40. package/modularTool/extensibility/extensionManager.js +277 -0
  41. package/modularTool/extensibility/extensionManager.js.map +1 -0
  42. package/modularTool/hooks/observableHooks.d.ts +35 -0
  43. package/modularTool/hooks/observableHooks.js +84 -0
  44. package/modularTool/hooks/observableHooks.js.map +1 -0
  45. package/modularTool/hooks/resourceHooks.d.ts +20 -0
  46. package/modularTool/hooks/resourceHooks.js +101 -0
  47. package/modularTool/hooks/resourceHooks.js.map +1 -0
  48. package/modularTool/hooks/settingsHooks.d.ts +8 -0
  49. package/modularTool/hooks/settingsHooks.js +40 -0
  50. package/modularTool/hooks/settingsHooks.js.map +1 -0
  51. package/modularTool/hooks/teachingMomentHooks.d.ts +34 -0
  52. package/modularTool/hooks/teachingMomentHooks.js +89 -0
  53. package/modularTool/hooks/teachingMomentHooks.js.map +1 -0
  54. package/modularTool/hooks/themeHooks.d.ts +17 -0
  55. package/modularTool/hooks/themeHooks.js +38 -0
  56. package/modularTool/hooks/themeHooks.js.map +1 -0
  57. package/modularTool/hooks/useResizeHandle.d.ts +35 -0
  58. package/modularTool/hooks/useResizeHandle.js +75 -0
  59. package/modularTool/hooks/useResizeHandle.js.map +1 -0
  60. package/modularTool/misc/assert.d.ts +5 -0
  61. package/modularTool/misc/assert.js +10 -0
  62. package/modularTool/misc/assert.js.map +1 -0
  63. package/modularTool/misc/graphUtils.d.ts +44 -0
  64. package/modularTool/misc/graphUtils.js +90 -0
  65. package/modularTool/misc/graphUtils.js.map +1 -0
  66. package/modularTool/misc/observableCollection.d.ts +23 -0
  67. package/modularTool/misc/observableCollection.js +43 -0
  68. package/modularTool/misc/observableCollection.js.map +1 -0
  69. package/modularTool/modularTool.d.ts +42 -0
  70. package/modularTool/modularTool.js +244 -0
  71. package/modularTool/modularTool.js.map +1 -0
  72. package/modularTool/modularity/serviceContainer.d.ts +64 -0
  73. package/modularTool/modularity/serviceContainer.js +181 -0
  74. package/modularTool/modularity/serviceContainer.js.map +1 -0
  75. package/modularTool/modularity/serviceDefinition.d.ts +64 -0
  76. package/modularTool/modularity/serviceDefinition.js +11 -0
  77. package/modularTool/modularity/serviceDefinition.js.map +1 -0
  78. package/modularTool/services/extensionsListService.d.ts +3 -0
  79. package/modularTool/services/extensionsListService.js +202 -0
  80. package/modularTool/services/extensionsListService.js.map +1 -0
  81. package/modularTool/services/globalSettings.d.ts +3 -0
  82. package/modularTool/services/globalSettings.js +9 -0
  83. package/modularTool/services/globalSettings.js.map +1 -0
  84. package/modularTool/services/reactContextService.d.ts +18 -0
  85. package/modularTool/services/reactContextService.js +5 -0
  86. package/modularTool/services/reactContextService.js.map +1 -0
  87. package/modularTool/services/settingsService.d.ts +24 -0
  88. package/modularTool/services/settingsService.js +41 -0
  89. package/modularTool/services/settingsService.js.map +1 -0
  90. package/modularTool/services/settingsStore.d.ts +55 -0
  91. package/modularTool/services/settingsStore.js +35 -0
  92. package/modularTool/services/settingsStore.js.map +1 -0
  93. package/modularTool/services/shellService.d.ts +256 -0
  94. package/modularTool/services/shellService.js +729 -0
  95. package/modularTool/services/shellService.js.map +1 -0
  96. package/modularTool/services/shellSettingsService.d.ts +3 -0
  97. package/modularTool/services/shellSettingsService.js +35 -0
  98. package/modularTool/services/shellSettingsService.js.map +1 -0
  99. package/modularTool/services/themeSelectorService.d.ts +3 -0
  100. package/modularTool/services/themeSelectorService.js +42 -0
  101. package/modularTool/services/themeSelectorService.js.map +1 -0
  102. package/modularTool/services/themeService.d.ts +60 -0
  103. package/modularTool/services/themeService.js +69 -0
  104. package/modularTool/services/themeService.js.map +1 -0
  105. package/modularTool/services/toastService.d.ts +17 -0
  106. package/modularTool/services/toastService.js +5 -0
  107. package/modularTool/services/toastService.js.map +1 -0
  108. package/modularTool/themes/babylonTheme.d.ts +3 -0
  109. package/modularTool/themes/babylonTheme.js +36 -0
  110. package/modularTool/themes/babylonTheme.js.map +1 -0
  111. package/nodeGraphSystem/graphCanvas.js +2 -1
  112. package/nodeGraphSystem/graphCanvas.js.map +1 -1
  113. package/nodeGraphSystem/graphNode.d.ts +2 -0
  114. package/nodeGraphSystem/graphNode.js +5 -1
  115. package/nodeGraphSystem/graphNode.js.map +1 -1
  116. package/package.json +1 -1
@@ -8,6 +8,7 @@ import { Button } from "../primitives/button.js";
8
8
  */
9
9
  export const ButtonLine = (props) => {
10
10
  ButtonLine.displayName = "ButtonLine";
11
- return (_jsx(LineContainer, { uniqueId: props.uniqueId ?? props.label, children: _jsx(Button, { ...props }) }));
11
+ const { uniqueId, ...buttonProps } = props;
12
+ return (_jsx(LineContainer, { uniqueId: uniqueId ?? props.label, children: _jsx(Button, { ...buttonProps }) }));
12
13
  };
13
14
  //# sourceMappingURL=buttonLine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buttonLine.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/buttonLine.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAoB,MAAM,sBAAsB,CAAC;AAOhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAuC,CAAC,KAAK,EAAE,EAAE;IACpE,UAAU,CAAC,WAAW,GAAG,YAAY,CAAC;IAEtC,OAAO,CACH,KAAC,aAAa,IAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,YAClD,KAAC,MAAM,OAAK,KAAK,GAAI,GACT,CACnB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { LineContainer } from \"./propertyLines/propertyLine\";\r\nimport { type FunctionComponent } from \"react\";\r\nimport { Button, type ButtonProps } from \"../primitives/button\";\r\n\r\ntype ButtonLineProps = Omit<ButtonProps, \"label\"> & {\r\n label: string; // Require a label when button is the entire line (by default, label is optional on a button)\r\n uniqueId?: string; // The ID of the property line to be used when the label cannot be used as a persistent ID.\r\n};\r\n\r\n/**\r\n * Wraps a button with a label in a line container\r\n * @param props Button props plus a label\r\n * @returns A button inside a line\r\n */\r\nexport const ButtonLine: FunctionComponent<ButtonLineProps> = (props) => {\r\n ButtonLine.displayName = \"ButtonLine\";\r\n\r\n return (\r\n <LineContainer uniqueId={props.uniqueId ?? props.label}>\r\n <Button {...props} />\r\n </LineContainer>\r\n );\r\n};\r\n"]}
1
+ {"version":3,"file":"buttonLine.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/buttonLine.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAoB,MAAM,sBAAsB,CAAC;AAOhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAuC,CAAC,KAAK,EAAE,EAAE;IACpE,UAAU,CAAC,WAAW,GAAG,YAAY,CAAC;IAEtC,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAE,GAAG,KAAK,CAAC;IAE3C,OAAO,CACH,KAAC,aAAa,IAAC,QAAQ,EAAE,QAAQ,IAAI,KAAK,CAAC,KAAK,YAC5C,KAAC,MAAM,OAAK,WAAW,GAAI,GACf,CACnB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { LineContainer } from \"./propertyLines/propertyLine\";\r\nimport { type FunctionComponent } from \"react\";\r\nimport { Button, type ButtonProps } from \"../primitives/button\";\r\n\r\ntype ButtonLineProps = Omit<ButtonProps, \"label\"> & {\r\n label: string; // Require a label when button is the entire line (by default, label is optional on a button)\r\n uniqueId?: string; // The ID of the property line to be used when the label cannot be used as a persistent ID.\r\n};\r\n\r\n/**\r\n * Wraps a button with a label in a line container\r\n * @param props Button props plus a label\r\n * @returns A button inside a line\r\n */\r\nexport const ButtonLine: FunctionComponent<ButtonLineProps> = (props) => {\r\n ButtonLine.displayName = \"ButtonLine\";\r\n\r\n const { uniqueId, ...buttonProps } = props;\r\n\r\n return (\r\n <LineContainer uniqueId={uniqueId ?? props.label}>\r\n <Button {...buttonProps} />\r\n </LineContainer>\r\n );\r\n};\r\n"]}
@@ -172,12 +172,12 @@ export function useAccordionSectionItemState(props) {
172
172
  // Debug: warn if itemId changes (should be stable)
173
173
  const prevItemIdRef = useRef(itemId);
174
174
  useEffect(() => {
175
- if (prevItemIdRef.current !== itemId) {
175
+ if (accordionCtx && prevItemIdRef.current !== itemId) {
176
176
  Logger.Warn(`Accordion: The uniqueId "${itemId}" in section "${sectionCtx?.sectionId}" has changed from "${prevItemIdRef.current}". ` +
177
177
  `Each item must have a unique, stable ID for pin/hide persistence to work correctly.`);
178
178
  }
179
179
  prevItemIdRef.current = itemId;
180
- }, [itemId, sectionCtx?.sectionId]);
180
+ }, [accordionCtx, itemId, sectionCtx?.sectionId]);
181
181
  // Register item and detect duplicates (skip nested items, as children of other AccordionSectionItem should not participate in pin/hide/search).
182
182
  useEffect(() => {
183
183
  if (!accordionCtx || !itemUniqueId || isNested) {
@@ -1 +1 @@
1
- {"version":3,"file":"accordion.contexts.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/accordion.contexts.tsx"],"names":[],"mappings":"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","sourcesContent":["import { type RefObject, createContext, useContext, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport { type AccordionProps, type AccordionSectionBlockProps, type AccordionSectionItemProps } from \"./accordion\";\nimport { DataStorage } from \"core/Misc/dataStorage\";\nimport { Logger } from \"core/Misc/logger\";\n\n// ============================================================================\n// Storage Helpers\n// ============================================================================\n\nconst STORAGE_KEY_ROOT = \"Babylon/Accordion\";\n\nconst ReadFromStorage = <T,>(path: string, initial: T): T => {\n try {\n const stored = DataStorage.ReadString(`${STORAGE_KEY_ROOT}/${path}`, \"\");\n return stored ? JSON.parse(stored) : initial;\n } catch {\n return initial;\n }\n};\n\nconst WriteToStorage = (path: string, data: unknown): void => {\n DataStorage.WriteString(`${STORAGE_KEY_ROOT}/${path}`, JSON.stringify(data));\n};\n\n// ============================================================================\n// State Types\n// ============================================================================\n\n/**\n * Immutable state for the Accordion.\n */\nexport type AccordionState = {\n /** IDs of pinned items (persisted to localStorage). */\n pinnedIds: string[];\n /** IDs of hidden items (persisted to localStorage). */\n hiddenIds: string[];\n /** Current search/filter term. */\n searchTerm: string;\n /** Whether edit mode is active (shows pin/hide controls). */\n editMode: boolean;\n};\n\n/**\n * Actions that can be dispatched to update accordion state.\n */\nexport type AccordionAction =\n | { type: \"SET_SEARCH_TERM\"; term: string }\n | { type: \"SET_EDIT_MODE\"; enabled: boolean }\n | { type: \"TOGGLE_PINNED\"; itemId: string }\n | { type: \"TOGGLE_HIDDEN\"; itemId: string }\n | { type: \"MOVE_PINNED_UP\"; itemId: string }\n | { type: \"REMOVE_STALE_IDS\"; activeIds: Set<string> }\n | { type: \"SHOW_ALL\" }\n | { type: \"HIDE_ALL_VISIBLE\"; visibleItemIds: string[] };\n\n/**\n * Feature flags for the Accordion (immutable after initialization).\n */\nexport type AccordionFeatures = {\n /** Whether pinning is enabled. */\n pinning: boolean;\n /** Whether hiding is enabled. */\n hiding: boolean;\n /** Whether search is enabled. */\n search: boolean;\n};\n\n// ============================================================================\n// Reducer\n// ============================================================================\n\nconst AccordionReducer = (state: AccordionState, action: AccordionAction): AccordionState => {\n switch (action.type) {\n case \"SET_SEARCH_TERM\":\n return { ...state, searchTerm: action.term };\n\n case \"SET_EDIT_MODE\":\n return { ...state, editMode: action.enabled };\n\n case \"TOGGLE_PINNED\": {\n const isPinned = state.pinnedIds.includes(action.itemId);\n return {\n ...state,\n pinnedIds: isPinned ? state.pinnedIds.filter((id) => id !== action.itemId) : [...state.pinnedIds, action.itemId],\n };\n }\n\n case \"TOGGLE_HIDDEN\": {\n const isHidden = state.hiddenIds.includes(action.itemId);\n return {\n ...state,\n hiddenIds: isHidden ? state.hiddenIds.filter((id) => id !== action.itemId) : [...state.hiddenIds, action.itemId],\n };\n }\n\n case \"MOVE_PINNED_UP\": {\n const index = state.pinnedIds.indexOf(action.itemId);\n if (index <= 0) {\n return state;\n }\n const newPinnedIds = [...state.pinnedIds];\n [newPinnedIds[index - 1], newPinnedIds[index]] = [newPinnedIds[index], newPinnedIds[index - 1]];\n return { ...state, pinnedIds: newPinnedIds };\n }\n\n case \"REMOVE_STALE_IDS\": {\n const pinnedIds = state.pinnedIds.filter((id) => action.activeIds.has(id));\n const hiddenIds = state.hiddenIds.filter((id) => action.activeIds.has(id));\n if (pinnedIds.length === state.pinnedIds.length && hiddenIds.length === state.hiddenIds.length) {\n return state;\n }\n return { ...state, pinnedIds, hiddenIds };\n }\n\n case \"SHOW_ALL\":\n return { ...state, hiddenIds: [] };\n\n case \"HIDE_ALL_VISIBLE\":\n return {\n ...state,\n hiddenIds: [...new Set([...state.hiddenIds, ...action.visibleItemIds])],\n };\n\n default:\n return state;\n }\n};\n\n// ============================================================================\n// Accordion Context\n// ============================================================================\n\n/**\n * Context value for the Accordion component.\n */\nexport type AccordionContextValue = {\n /** The unique ID of the Accordion instance. */\n accordionId: string;\n /** State for the Accordion, managed via dispatch function. */\n state: AccordionState;\n /** Dispatch function to update state. */\n dispatch: React.Dispatch<AccordionAction>;\n /** Feature flags. */\n features: AccordionFeatures;\n /** Ref for the pinned items portal container. */\n pinnedContainerRef: RefObject<HTMLDivElement>;\n /** Map of registered item IDs to labels (for duplicate detection and section empty checks). */\n registeredItemIds: Map<string, string>;\n};\n\nexport const AccordionContext = createContext<AccordionContextValue | undefined>(undefined);\n\n/**\n * Hook to create and manage the AccordionContext value.\n *\n * @param props - AccordionProps\n * @returns AccordionContextValue, or undefined if no features are enabled or no uniqueId is provided.\n */\nexport function useAccordionContext(props: AccordionProps): AccordionContextValue | undefined {\n const { uniqueId: accordionId, enablePinnedItems, enableHiddenItems, enableSearchItems } = props;\n\n const features: AccordionFeatures = useMemo(\n () => ({\n pinning: enablePinnedItems ?? false,\n hiding: enableHiddenItems ?? false,\n search: enableSearchItems ?? false,\n }),\n [enablePinnedItems, enableHiddenItems, enableSearchItems]\n );\n\n const hasFeatures = features.pinning || features.hiding || features.search;\n\n // Initialize state from localStorage\n const initialState = useMemo((): AccordionState => {\n if (!accordionId || !hasFeatures) {\n return { pinnedIds: [], hiddenIds: [], searchTerm: \"\", editMode: false };\n }\n return {\n pinnedIds: features.pinning ? ReadFromStorage<string[]>(`Pinned/${accordionId}`, []) : [],\n hiddenIds: features.hiding ? ReadFromStorage<string[]>(`Hidden/${accordionId}`, []) : [],\n searchTerm: \"\",\n editMode: false,\n };\n }, [accordionId, hasFeatures, features.pinning, features.hiding]);\n\n const [state, dispatch] = useReducer(AccordionReducer, initialState);\n\n const pinnedContainerRef = useRef<HTMLDivElement>(null);\n const registeredItemIds = useRef<Map<string, string>>(new Map());\n\n // Persist pinnedIds to localStorage when they change\n useEffect(() => {\n if (accordionId && features.pinning) {\n WriteToStorage(`Pinned/${accordionId}`, state.pinnedIds);\n }\n }, [accordionId, features.pinning, state.pinnedIds]);\n\n // Persist hiddenIds to localStorage when they change\n useEffect(() => {\n if (accordionId && features.hiding) {\n WriteToStorage(`Hidden/${accordionId}`, state.hiddenIds);\n }\n }, [accordionId, features.hiding, state.hiddenIds]);\n\n // Clean stale IDs from localStorage on mount. Parent effects run after\n // children's, so all items will have registered by the time this fires.\n useEffect(() => {\n if (accordionId && hasFeatures) {\n dispatch({ type: \"REMOVE_STALE_IDS\", activeIds: new Set(registeredItemIds.current.keys()) });\n }\n }, [accordionId, hasFeatures]);\n\n // Return undefined if no accordionId or no features enabled\n if (!accordionId || !hasFeatures) {\n return undefined;\n }\n\n return {\n accordionId,\n state,\n dispatch,\n features,\n pinnedContainerRef,\n registeredItemIds: registeredItemIds.current,\n };\n}\n\n// ============================================================================\n// Section Context\n// ============================================================================\n\n/**\n * Context value for an AccordionSectionBlock.\n */\nexport type AccordionSectionBlockContextValue = {\n /** The section ID. */\n sectionId: string;\n};\n\nexport const AccordionSectionBlockContext = createContext<AccordionSectionBlockContextValue | undefined>(undefined);\n\n/**\n * Hook to create the AccordionSectionBlockContext value.\n *\n * @param props - AccordionSectionBlockProps\n * @returns AccordionSectionBlockContextValue and isEmpty state\n */\nexport function useAccordionSectionBlockContext(props: AccordionSectionBlockProps): {\n context: AccordionSectionBlockContextValue;\n isEmpty: boolean;\n} {\n const { sectionId } = props;\n const context = useMemo(() => ({ sectionId }), [sectionId]);\n const isEmpty = useIsSectionEmpty(sectionId);\n\n return { context, isEmpty };\n}\n\n// ============================================================================\n// Item Depth Context (to detect nested AccordionSectionItems)\n// ============================================================================\n\n/**\n * Context to track whether we're inside an AccordionSectionItem.\n * Used to prevent nested items from being individually manageable.\n */\nexport const AccordionItemDepthContext = createContext<boolean>(false);\n\n// ============================================================================\n// Item Context Hook\n// ============================================================================\n\n/**\n * Derived item state, computed from the accordion state during render.\n */\nexport type AccordionItemState = {\n /** The globally unique item ID. */\n itemUniqueId: string;\n /** Whether this item is nested inside another AccordionSectionItem. */\n isNested: boolean;\n /** Whether this item is pinned. */\n isPinned: boolean;\n /** Whether this item is hidden. */\n isHidden: boolean;\n /** Whether this item matches the current search term. */\n isMatch: boolean;\n /** The index of this item in the pinned list (for ordering). */\n pinnedIndex: number;\n /** Whether this pinned item can be moved up (is not first in the pinned list). */\n canMoveUp: boolean;\n /** Whether edit mode is active. */\n inEditMode: boolean;\n /** Callbacks to modify state. */\n actions: {\n togglePinned: () => void;\n toggleHidden: () => void;\n movePinnedUp: () => void;\n };\n};\n\n/**\n * Hook to compute item state from accordion context.\n *\n * @param props - AccordionSectionItemProps\n * @returns AccordionItemState, or undefined if no accordion context or nested item.\n */\nexport function useAccordionSectionItemState(props: AccordionSectionItemProps): AccordionItemState | undefined {\n const { uniqueId: itemId, label: itemLabel, staticItem } = props;\n\n const accordionCtx = useContext(AccordionContext);\n const sectionCtx = useContext(AccordionSectionBlockContext);\n const isNested = useContext(AccordionItemDepthContext);\n\n // Build the globally unique item ID\n const itemUniqueId = useMemo(() => {\n if (!accordionCtx || !sectionCtx) {\n return \"\";\n }\n return `${accordionCtx.accordionId}/${sectionCtx.sectionId}/${itemId}`;\n }, [accordionCtx?.accordionId, sectionCtx?.sectionId, itemId]);\n\n // Debug: warn if itemId changes (should be stable)\n const prevItemIdRef = useRef(itemId);\n useEffect(() => {\n if (prevItemIdRef.current !== itemId) {\n Logger.Warn(\n `Accordion: The uniqueId \"${itemId}\" in section \"${sectionCtx?.sectionId}\" has changed from \"${prevItemIdRef.current}\". ` +\n `Each item must have a unique, stable ID for pin/hide persistence to work correctly.`\n );\n }\n prevItemIdRef.current = itemId;\n }, [itemId, sectionCtx?.sectionId]);\n\n // Register item and detect duplicates (skip nested items, as children of other AccordionSectionItem should not participate in pin/hide/search).\n useEffect(() => {\n if (!accordionCtx || !itemUniqueId || isNested) {\n return;\n }\n const { registeredItemIds } = accordionCtx;\n if (registeredItemIds.has(itemUniqueId)) {\n Logger.Warn(\n `Accordion: Duplicate uniqueId \"${itemId}\" detected in section \"${sectionCtx?.sectionId}\". ` +\n `Each item must have a unique ID within its section for pin/hide persistence to work correctly.`\n );\n }\n registeredItemIds.set(itemUniqueId, itemLabel ?? itemId);\n return () => {\n registeredItemIds.delete(itemUniqueId);\n };\n }, [accordionCtx, itemUniqueId, itemId, itemLabel, sectionCtx?.sectionId, isNested]);\n\n // If no context, static item, or nested, return undefined\n if (!accordionCtx || staticItem) {\n return undefined;\n }\n\n const { state, dispatch, features } = accordionCtx;\n const { pinnedIds, hiddenIds, searchTerm, editMode } = state;\n\n // Compute derived state\n const isPinned = features.pinning && pinnedIds.includes(itemUniqueId);\n const isHidden = features.hiding && hiddenIds.includes(itemUniqueId);\n const pinnedIndex = isPinned ? pinnedIds.indexOf(itemUniqueId) : -1;\n\n const canMoveUp = isPinned && pinnedIndex > 0;\n\n // Search matching\n const searchText = (itemLabel ?? itemId).toLowerCase();\n const isMatch = !features.search || !searchTerm || searchText.includes(searchTerm.toLowerCase());\n\n return {\n itemUniqueId,\n isNested,\n isPinned,\n isHidden,\n isMatch,\n pinnedIndex,\n canMoveUp,\n inEditMode: editMode,\n actions: {\n togglePinned: () => dispatch({ type: \"TOGGLE_PINNED\", itemId: itemUniqueId }),\n toggleHidden: () => dispatch({ type: \"TOGGLE_HIDDEN\", itemId: itemUniqueId }),\n movePinnedUp: () => dispatch({ type: \"MOVE_PINNED_UP\", itemId: itemUniqueId }),\n },\n };\n}\n/**\n * Hook to determine if a section is empty (has no visible items).\n * Derived from accordion state + globally registered items filtered by section prefix.\n *\n * @param sectionId - The section ID to check.\n * @returns Whether the section has no visible items.\n */\nfunction useIsSectionEmpty(sectionId: string): boolean {\n const accordionCtx = useContext(AccordionContext);\n\n if (!accordionCtx) {\n return false;\n }\n\n const { state, features, accordionId, registeredItemIds } = accordionCtx;\n const { pinnedIds, hiddenIds, searchTerm, editMode } = state;\n\n if (editMode) {\n return false;\n }\n\n const sectionPrefix = `${accordionId}/${sectionId}/`;\n let hasItems = false;\n\n for (const [itemId, itemLabel] of registeredItemIds) {\n if (!itemId.startsWith(sectionPrefix)) {\n continue;\n }\n hasItems = true;\n const isPinned = features.pinning && pinnedIds.includes(itemId);\n const isHidden = features.hiding && hiddenIds.includes(itemId);\n const searchText = (itemLabel || itemId).toLowerCase();\n const isMatch = !features.search || !searchTerm || searchText.includes(searchTerm.toLowerCase());\n if (!isPinned && !isHidden && isMatch) {\n return false;\n }\n }\n\n return hasItems;\n}\n"]}
1
+ {"version":3,"file":"accordion.contexts.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/accordion.contexts.tsx"],"names":[],"mappings":"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","sourcesContent":["import { type RefObject, createContext, useContext, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport { type AccordionProps, type AccordionSectionBlockProps, type AccordionSectionItemProps } from \"./accordion\";\nimport { DataStorage } from \"core/Misc/dataStorage\";\nimport { Logger } from \"core/Misc/logger\";\n\n// ============================================================================\n// Storage Helpers\n// ============================================================================\n\nconst STORAGE_KEY_ROOT = \"Babylon/Accordion\";\n\nconst ReadFromStorage = <T,>(path: string, initial: T): T => {\n try {\n const stored = DataStorage.ReadString(`${STORAGE_KEY_ROOT}/${path}`, \"\");\n return stored ? JSON.parse(stored) : initial;\n } catch {\n return initial;\n }\n};\n\nconst WriteToStorage = (path: string, data: unknown): void => {\n DataStorage.WriteString(`${STORAGE_KEY_ROOT}/${path}`, JSON.stringify(data));\n};\n\n// ============================================================================\n// State Types\n// ============================================================================\n\n/**\n * Immutable state for the Accordion.\n */\nexport type AccordionState = {\n /** IDs of pinned items (persisted to localStorage). */\n pinnedIds: string[];\n /** IDs of hidden items (persisted to localStorage). */\n hiddenIds: string[];\n /** Current search/filter term. */\n searchTerm: string;\n /** Whether edit mode is active (shows pin/hide controls). */\n editMode: boolean;\n};\n\n/**\n * Actions that can be dispatched to update accordion state.\n */\nexport type AccordionAction =\n | { type: \"SET_SEARCH_TERM\"; term: string }\n | { type: \"SET_EDIT_MODE\"; enabled: boolean }\n | { type: \"TOGGLE_PINNED\"; itemId: string }\n | { type: \"TOGGLE_HIDDEN\"; itemId: string }\n | { type: \"MOVE_PINNED_UP\"; itemId: string }\n | { type: \"REMOVE_STALE_IDS\"; activeIds: Set<string> }\n | { type: \"SHOW_ALL\" }\n | { type: \"HIDE_ALL_VISIBLE\"; visibleItemIds: string[] };\n\n/**\n * Feature flags for the Accordion (immutable after initialization).\n */\nexport type AccordionFeatures = {\n /** Whether pinning is enabled. */\n pinning: boolean;\n /** Whether hiding is enabled. */\n hiding: boolean;\n /** Whether search is enabled. */\n search: boolean;\n};\n\n// ============================================================================\n// Reducer\n// ============================================================================\n\nconst AccordionReducer = (state: AccordionState, action: AccordionAction): AccordionState => {\n switch (action.type) {\n case \"SET_SEARCH_TERM\":\n return { ...state, searchTerm: action.term };\n\n case \"SET_EDIT_MODE\":\n return { ...state, editMode: action.enabled };\n\n case \"TOGGLE_PINNED\": {\n const isPinned = state.pinnedIds.includes(action.itemId);\n return {\n ...state,\n pinnedIds: isPinned ? state.pinnedIds.filter((id) => id !== action.itemId) : [...state.pinnedIds, action.itemId],\n };\n }\n\n case \"TOGGLE_HIDDEN\": {\n const isHidden = state.hiddenIds.includes(action.itemId);\n return {\n ...state,\n hiddenIds: isHidden ? state.hiddenIds.filter((id) => id !== action.itemId) : [...state.hiddenIds, action.itemId],\n };\n }\n\n case \"MOVE_PINNED_UP\": {\n const index = state.pinnedIds.indexOf(action.itemId);\n if (index <= 0) {\n return state;\n }\n const newPinnedIds = [...state.pinnedIds];\n [newPinnedIds[index - 1], newPinnedIds[index]] = [newPinnedIds[index], newPinnedIds[index - 1]];\n return { ...state, pinnedIds: newPinnedIds };\n }\n\n case \"REMOVE_STALE_IDS\": {\n const pinnedIds = state.pinnedIds.filter((id) => action.activeIds.has(id));\n const hiddenIds = state.hiddenIds.filter((id) => action.activeIds.has(id));\n if (pinnedIds.length === state.pinnedIds.length && hiddenIds.length === state.hiddenIds.length) {\n return state;\n }\n return { ...state, pinnedIds, hiddenIds };\n }\n\n case \"SHOW_ALL\":\n return { ...state, hiddenIds: [] };\n\n case \"HIDE_ALL_VISIBLE\":\n return {\n ...state,\n hiddenIds: [...new Set([...state.hiddenIds, ...action.visibleItemIds])],\n };\n\n default:\n return state;\n }\n};\n\n// ============================================================================\n// Accordion Context\n// ============================================================================\n\n/**\n * Context value for the Accordion component.\n */\nexport type AccordionContextValue = {\n /** The unique ID of the Accordion instance. */\n accordionId: string;\n /** State for the Accordion, managed via dispatch function. */\n state: AccordionState;\n /** Dispatch function to update state. */\n dispatch: React.Dispatch<AccordionAction>;\n /** Feature flags. */\n features: AccordionFeatures;\n /** Ref for the pinned items portal container. */\n pinnedContainerRef: RefObject<HTMLDivElement>;\n /** Map of registered item IDs to labels (for duplicate detection and section empty checks). */\n registeredItemIds: Map<string, string>;\n};\n\nexport const AccordionContext = createContext<AccordionContextValue | undefined>(undefined);\n\n/**\n * Hook to create and manage the AccordionContext value.\n *\n * @param props - AccordionProps\n * @returns AccordionContextValue, or undefined if no features are enabled or no uniqueId is provided.\n */\nexport function useAccordionContext(props: AccordionProps): AccordionContextValue | undefined {\n const { uniqueId: accordionId, enablePinnedItems, enableHiddenItems, enableSearchItems } = props;\n\n const features: AccordionFeatures = useMemo(\n () => ({\n pinning: enablePinnedItems ?? false,\n hiding: enableHiddenItems ?? false,\n search: enableSearchItems ?? false,\n }),\n [enablePinnedItems, enableHiddenItems, enableSearchItems]\n );\n\n const hasFeatures = features.pinning || features.hiding || features.search;\n\n // Initialize state from localStorage\n const initialState = useMemo((): AccordionState => {\n if (!accordionId || !hasFeatures) {\n return { pinnedIds: [], hiddenIds: [], searchTerm: \"\", editMode: false };\n }\n return {\n pinnedIds: features.pinning ? ReadFromStorage<string[]>(`Pinned/${accordionId}`, []) : [],\n hiddenIds: features.hiding ? ReadFromStorage<string[]>(`Hidden/${accordionId}`, []) : [],\n searchTerm: \"\",\n editMode: false,\n };\n }, [accordionId, hasFeatures, features.pinning, features.hiding]);\n\n const [state, dispatch] = useReducer(AccordionReducer, initialState);\n\n const pinnedContainerRef = useRef<HTMLDivElement>(null);\n const registeredItemIds = useRef<Map<string, string>>(new Map());\n\n // Persist pinnedIds to localStorage when they change\n useEffect(() => {\n if (accordionId && features.pinning) {\n WriteToStorage(`Pinned/${accordionId}`, state.pinnedIds);\n }\n }, [accordionId, features.pinning, state.pinnedIds]);\n\n // Persist hiddenIds to localStorage when they change\n useEffect(() => {\n if (accordionId && features.hiding) {\n WriteToStorage(`Hidden/${accordionId}`, state.hiddenIds);\n }\n }, [accordionId, features.hiding, state.hiddenIds]);\n\n // Clean stale IDs from localStorage on mount. Parent effects run after\n // children's, so all items will have registered by the time this fires.\n useEffect(() => {\n if (accordionId && hasFeatures) {\n dispatch({ type: \"REMOVE_STALE_IDS\", activeIds: new Set(registeredItemIds.current.keys()) });\n }\n }, [accordionId, hasFeatures]);\n\n // Return undefined if no accordionId or no features enabled\n if (!accordionId || !hasFeatures) {\n return undefined;\n }\n\n return {\n accordionId,\n state,\n dispatch,\n features,\n pinnedContainerRef,\n registeredItemIds: registeredItemIds.current,\n };\n}\n\n// ============================================================================\n// Section Context\n// ============================================================================\n\n/**\n * Context value for an AccordionSectionBlock.\n */\nexport type AccordionSectionBlockContextValue = {\n /** The section ID. */\n sectionId: string;\n};\n\nexport const AccordionSectionBlockContext = createContext<AccordionSectionBlockContextValue | undefined>(undefined);\n\n/**\n * Hook to create the AccordionSectionBlockContext value.\n *\n * @param props - AccordionSectionBlockProps\n * @returns AccordionSectionBlockContextValue and isEmpty state\n */\nexport function useAccordionSectionBlockContext(props: AccordionSectionBlockProps): {\n context: AccordionSectionBlockContextValue;\n isEmpty: boolean;\n} {\n const { sectionId } = props;\n const context = useMemo(() => ({ sectionId }), [sectionId]);\n const isEmpty = useIsSectionEmpty(sectionId);\n\n return { context, isEmpty };\n}\n\n// ============================================================================\n// Item Depth Context (to detect nested AccordionSectionItems)\n// ============================================================================\n\n/**\n * Context to track whether we're inside an AccordionSectionItem.\n * Used to prevent nested items from being individually manageable.\n */\nexport const AccordionItemDepthContext = createContext<boolean>(false);\n\n// ============================================================================\n// Item Context Hook\n// ============================================================================\n\n/**\n * Derived item state, computed from the accordion state during render.\n */\nexport type AccordionItemState = {\n /** The globally unique item ID. */\n itemUniqueId: string;\n /** Whether this item is nested inside another AccordionSectionItem. */\n isNested: boolean;\n /** Whether this item is pinned. */\n isPinned: boolean;\n /** Whether this item is hidden. */\n isHidden: boolean;\n /** Whether this item matches the current search term. */\n isMatch: boolean;\n /** The index of this item in the pinned list (for ordering). */\n pinnedIndex: number;\n /** Whether this pinned item can be moved up (is not first in the pinned list). */\n canMoveUp: boolean;\n /** Whether edit mode is active. */\n inEditMode: boolean;\n /** Callbacks to modify state. */\n actions: {\n togglePinned: () => void;\n toggleHidden: () => void;\n movePinnedUp: () => void;\n };\n};\n\n/**\n * Hook to compute item state from accordion context.\n *\n * @param props - AccordionSectionItemProps\n * @returns AccordionItemState, or undefined if no accordion context or nested item.\n */\nexport function useAccordionSectionItemState(props: AccordionSectionItemProps): AccordionItemState | undefined {\n const { uniqueId: itemId, label: itemLabel, staticItem } = props;\n\n const accordionCtx = useContext(AccordionContext);\n const sectionCtx = useContext(AccordionSectionBlockContext);\n const isNested = useContext(AccordionItemDepthContext);\n\n // Build the globally unique item ID\n const itemUniqueId = useMemo(() => {\n if (!accordionCtx || !sectionCtx) {\n return \"\";\n }\n return `${accordionCtx.accordionId}/${sectionCtx.sectionId}/${itemId}`;\n }, [accordionCtx?.accordionId, sectionCtx?.sectionId, itemId]);\n\n // Debug: warn if itemId changes (should be stable)\n const prevItemIdRef = useRef(itemId);\n useEffect(() => {\n if (accordionCtx && prevItemIdRef.current !== itemId) {\n Logger.Warn(\n `Accordion: The uniqueId \"${itemId}\" in section \"${sectionCtx?.sectionId}\" has changed from \"${prevItemIdRef.current}\". ` +\n `Each item must have a unique, stable ID for pin/hide persistence to work correctly.`\n );\n }\n prevItemIdRef.current = itemId;\n }, [accordionCtx, itemId, sectionCtx?.sectionId]);\n\n // Register item and detect duplicates (skip nested items, as children of other AccordionSectionItem should not participate in pin/hide/search).\n useEffect(() => {\n if (!accordionCtx || !itemUniqueId || isNested) {\n return;\n }\n const { registeredItemIds } = accordionCtx;\n if (registeredItemIds.has(itemUniqueId)) {\n Logger.Warn(\n `Accordion: Duplicate uniqueId \"${itemId}\" detected in section \"${sectionCtx?.sectionId}\". ` +\n `Each item must have a unique ID within its section for pin/hide persistence to work correctly.`\n );\n }\n registeredItemIds.set(itemUniqueId, itemLabel ?? itemId);\n return () => {\n registeredItemIds.delete(itemUniqueId);\n };\n }, [accordionCtx, itemUniqueId, itemId, itemLabel, sectionCtx?.sectionId, isNested]);\n\n // If no context, static item, or nested, return undefined\n if (!accordionCtx || staticItem) {\n return undefined;\n }\n\n const { state, dispatch, features } = accordionCtx;\n const { pinnedIds, hiddenIds, searchTerm, editMode } = state;\n\n // Compute derived state\n const isPinned = features.pinning && pinnedIds.includes(itemUniqueId);\n const isHidden = features.hiding && hiddenIds.includes(itemUniqueId);\n const pinnedIndex = isPinned ? pinnedIds.indexOf(itemUniqueId) : -1;\n\n const canMoveUp = isPinned && pinnedIndex > 0;\n\n // Search matching\n const searchText = (itemLabel ?? itemId).toLowerCase();\n const isMatch = !features.search || !searchTerm || searchText.includes(searchTerm.toLowerCase());\n\n return {\n itemUniqueId,\n isNested,\n isPinned,\n isHidden,\n isMatch,\n pinnedIndex,\n canMoveUp,\n inEditMode: editMode,\n actions: {\n togglePinned: () => dispatch({ type: \"TOGGLE_PINNED\", itemId: itemUniqueId }),\n toggleHidden: () => dispatch({ type: \"TOGGLE_HIDDEN\", itemId: itemUniqueId }),\n movePinnedUp: () => dispatch({ type: \"MOVE_PINNED_UP\", itemId: itemUniqueId }),\n },\n };\n}\n/**\n * Hook to determine if a section is empty (has no visible items).\n * Derived from accordion state + globally registered items filtered by section prefix.\n *\n * @param sectionId - The section ID to check.\n * @returns Whether the section has no visible items.\n */\nfunction useIsSectionEmpty(sectionId: string): boolean {\n const accordionCtx = useContext(AccordionContext);\n\n if (!accordionCtx) {\n return false;\n }\n\n const { state, features, accordionId, registeredItemIds } = accordionCtx;\n const { pinnedIds, hiddenIds, searchTerm, editMode } = state;\n\n if (editMode) {\n return false;\n }\n\n const sectionPrefix = `${accordionId}/${sectionId}/`;\n let hasItems = false;\n\n for (const [itemId, itemLabel] of registeredItemIds) {\n if (!itemId.startsWith(sectionPrefix)) {\n continue;\n }\n hasItems = true;\n const isPinned = features.pinning && pinnedIds.includes(itemId);\n const isHidden = features.hiding && hiddenIds.includes(itemId);\n const searchText = (itemLabel || itemId).toLowerCase();\n const isMatch = !features.search || !searchTerm || searchText.includes(searchTerm.toLowerCase());\n if (!isPinned && !isHidden && isMatch) {\n return false;\n }\n }\n\n return hasItems;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"contextMenu.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/contextMenu.tsx"],"names":[],"mappings":"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","sourcesContent":["import { type ReactElement, type ReactNode, forwardRef, useState } from \"react\";\r\nimport {\r\n Menu,\r\n MenuTrigger,\r\n MenuPopover,\r\n MenuList,\r\n MenuItem,\r\n MenuDivider,\r\n MenuGroup,\r\n MenuGroupHeader,\r\n makeStyles,\r\n tokens,\r\n type MenuProps as FluentMenuProps,\r\n} from \"@fluentui/react-components\";\r\nimport { type FluentIcon } from \"@fluentui/react-icons\";\r\nimport { Button } from \"shared-ui-components/fluent/primitives/button\";\r\nimport { type BasePrimitiveProps } from \"./primitive\";\r\n\r\nconst useStyles = makeStyles({\r\n menuPopover: {\r\n minWidth: \"160px\",\r\n },\r\n menuItem: {\r\n cursor: \"pointer\",\r\n },\r\n menuItemIcon: {\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n});\r\n\r\n/**\r\n * Represents a single menu item in the context menu.\r\n */\r\nexport type ContextMenuItemProps = {\r\n /**\r\n * Unique key for the menu item.\r\n */\r\n key: string;\r\n /**\r\n * The text label displayed for the menu item.\r\n */\r\n label: string;\r\n /**\r\n * Optional icon to display alongside the menu item.\r\n */\r\n icon?: FluentIcon;\r\n /**\r\n * Called when the menu item is clicked.\r\n */\r\n onClick?: () => void;\r\n /**\r\n * Whether the menu item is disabled.\r\n */\r\n disabled?: boolean;\r\n /**\r\n * Optional secondary text displayed alongside the label.\r\n */\r\n secondaryContent?: string;\r\n};\r\n\r\n/**\r\n * Represents a divider in the context menu.\r\n */\r\nexport type ContextMenuDividerProps = {\r\n /**\r\n * Unique key for the divider.\r\n */\r\n key: string;\r\n /**\r\n * Indicates this is a divider item.\r\n */\r\n type: \"divider\";\r\n};\r\n\r\n/**\r\n * Represents a group of menu items with an optional header.\r\n */\r\nexport type ContextMenuGroupProps = {\r\n /**\r\n * Unique key for the group.\r\n */\r\n key: string;\r\n /**\r\n * Indicates this is a group item.\r\n */\r\n type: \"group\";\r\n /**\r\n * Optional header text for the group.\r\n */\r\n header?: string;\r\n /**\r\n * The menu items within the group.\r\n */\r\n items: ContextMenuItem[];\r\n};\r\n\r\n/**\r\n * Union type representing all possible menu items.\r\n */\r\nexport type ContextMenuItem = ContextMenuItemProps | ContextMenuDividerProps | ContextMenuGroupProps;\r\n\r\ntype ContextMenuWithIconProps = {\r\n /**\r\n * Icon to use as the trigger button.\r\n */\r\n icon: FluentIcon;\r\n trigger?: never;\r\n};\r\n\r\ntype ContextMenuWithTriggerProps = {\r\n icon?: never;\r\n /**\r\n * Custom trigger element for opening the menu.\r\n */\r\n trigger: ReactElement;\r\n};\r\n\r\nexport type ContextMenuProps = BasePrimitiveProps &\r\n (ContextMenuWithIconProps | ContextMenuWithTriggerProps) & {\r\n /**\r\n * Array of menu items to display.\r\n */\r\n items: ContextMenuItem[];\r\n /**\r\n * Positioning of the menu relative to the trigger.\r\n */\r\n positioning?: FluentMenuProps[\"positioning\"];\r\n /**\r\n * Called when the menu open state changes.\r\n */\r\n onOpenChange?: (open: boolean) => void;\r\n };\r\n\r\nconst IsDivider = (item: ContextMenuItem): item is ContextMenuDividerProps => {\r\n return \"type\" in item && item.type === \"divider\";\r\n};\r\n\r\nconst IsGroup = (item: ContextMenuItem): item is ContextMenuGroupProps => {\r\n return \"type\" in item && item.type === \"group\";\r\n};\r\n\r\nconst RenderMenuItem = (item: ContextMenuItemProps, classes: ReturnType<typeof useStyles>) => {\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const Icon = item.icon;\r\n return (\r\n <MenuItem\r\n key={item.key}\r\n className={classes.menuItem}\r\n icon={Icon ? <Icon className={classes.menuItemIcon} /> : undefined}\r\n onClick={item.onClick}\r\n disabled={item.disabled}\r\n secondaryContent={item.secondaryContent}\r\n >\r\n {item.label}\r\n </MenuItem>\r\n );\r\n};\r\n\r\nconst RenderMenuItems = (items: ContextMenuItem[], classes: ReturnType<typeof useStyles>): ReactNode[] => {\r\n return items.map((item) => {\r\n if (IsDivider(item)) {\r\n return <MenuDivider key={item.key} />;\r\n }\r\n\r\n if (IsGroup(item)) {\r\n return (\r\n <MenuGroup key={item.key}>\r\n {item.header && <MenuGroupHeader>{item.header}</MenuGroupHeader>}\r\n {RenderMenuItems(item.items, classes)}\r\n </MenuGroup>\r\n );\r\n }\r\n\r\n return RenderMenuItem(item, classes);\r\n });\r\n};\r\n\r\n/**\r\n * A wrapper around Fluent UI's Menu component providing a simplified API for context menus.\r\n * Supports menu items with icons, dividers, and grouped items.\r\n */\r\nexport const ContextMenu = forwardRef<HTMLButtonElement, ContextMenuProps>((props, ref) => {\r\n const { items, onOpenChange, disabled } = props;\r\n const [open, setOpen] = useState(false);\r\n const classes = useStyles();\r\n\r\n const handleOpenChange = (_: unknown, data: { open: boolean }) => {\r\n setOpen(data.open);\r\n onOpenChange?.(data.open);\r\n };\r\n\r\n return (\r\n <Menu openOnContext open={open} onOpenChange={handleOpenChange}>\r\n <MenuTrigger disableButtonEnhancement>\r\n {props.trigger ?? (\r\n <Button\r\n ref={ref}\r\n icon={props.icon}\r\n onClick={(e) => {\r\n e?.stopPropagation();\r\n setOpen(true);\r\n }}\r\n disabled={disabled}\r\n />\r\n )}\r\n </MenuTrigger>\r\n <MenuPopover className={classes.menuPopover}>\r\n <MenuList>{RenderMenuItems(items, classes)}</MenuList>\r\n </MenuPopover>\r\n </Menu>\r\n );\r\n});\r\n\r\nContextMenu.displayName = \"ContextMenu\";\r\n"]}
1
+ {"version":3,"file":"contextMenu.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/contextMenu.tsx"],"names":[],"mappings":"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","sourcesContent":["import { type ReactElement, type ReactNode, forwardRef, useState } from \"react\";\r\nimport {\r\n Menu,\r\n MenuTrigger,\r\n MenuPopover,\r\n MenuList,\r\n MenuItem,\r\n MenuDivider,\r\n MenuGroup,\r\n MenuGroupHeader,\r\n makeStyles,\r\n tokens,\r\n type MenuProps as FluentMenuProps,\r\n} from \"@fluentui/react-components\";\r\nimport { type FluentIcon } from \"@fluentui/react-icons\";\r\nimport { Button } from \"./button\";\r\nimport { type BasePrimitiveProps } from \"./primitive\";\r\n\r\nconst useStyles = makeStyles({\r\n menuPopover: {\r\n minWidth: \"160px\",\r\n },\r\n menuItem: {\r\n cursor: \"pointer\",\r\n },\r\n menuItemIcon: {\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n});\r\n\r\n/**\r\n * Represents a single menu item in the context menu.\r\n */\r\nexport type ContextMenuItemProps = {\r\n /**\r\n * Unique key for the menu item.\r\n */\r\n key: string;\r\n /**\r\n * The text label displayed for the menu item.\r\n */\r\n label: string;\r\n /**\r\n * Optional icon to display alongside the menu item.\r\n */\r\n icon?: FluentIcon;\r\n /**\r\n * Called when the menu item is clicked.\r\n */\r\n onClick?: () => void;\r\n /**\r\n * Whether the menu item is disabled.\r\n */\r\n disabled?: boolean;\r\n /**\r\n * Optional secondary text displayed alongside the label.\r\n */\r\n secondaryContent?: string;\r\n};\r\n\r\n/**\r\n * Represents a divider in the context menu.\r\n */\r\nexport type ContextMenuDividerProps = {\r\n /**\r\n * Unique key for the divider.\r\n */\r\n key: string;\r\n /**\r\n * Indicates this is a divider item.\r\n */\r\n type: \"divider\";\r\n};\r\n\r\n/**\r\n * Represents a group of menu items with an optional header.\r\n */\r\nexport type ContextMenuGroupProps = {\r\n /**\r\n * Unique key for the group.\r\n */\r\n key: string;\r\n /**\r\n * Indicates this is a group item.\r\n */\r\n type: \"group\";\r\n /**\r\n * Optional header text for the group.\r\n */\r\n header?: string;\r\n /**\r\n * The menu items within the group.\r\n */\r\n items: ContextMenuItem[];\r\n};\r\n\r\n/**\r\n * Union type representing all possible menu items.\r\n */\r\nexport type ContextMenuItem = ContextMenuItemProps | ContextMenuDividerProps | ContextMenuGroupProps;\r\n\r\ntype ContextMenuWithIconProps = {\r\n /**\r\n * Icon to use as the trigger button.\r\n */\r\n icon: FluentIcon;\r\n trigger?: never;\r\n};\r\n\r\ntype ContextMenuWithTriggerProps = {\r\n icon?: never;\r\n /**\r\n * Custom trigger element for opening the menu.\r\n */\r\n trigger: ReactElement;\r\n};\r\n\r\nexport type ContextMenuProps = BasePrimitiveProps &\r\n (ContextMenuWithIconProps | ContextMenuWithTriggerProps) & {\r\n /**\r\n * Array of menu items to display.\r\n */\r\n items: ContextMenuItem[];\r\n /**\r\n * Positioning of the menu relative to the trigger.\r\n */\r\n positioning?: FluentMenuProps[\"positioning\"];\r\n /**\r\n * Called when the menu open state changes.\r\n */\r\n onOpenChange?: (open: boolean) => void;\r\n };\r\n\r\nconst IsDivider = (item: ContextMenuItem): item is ContextMenuDividerProps => {\r\n return \"type\" in item && item.type === \"divider\";\r\n};\r\n\r\nconst IsGroup = (item: ContextMenuItem): item is ContextMenuGroupProps => {\r\n return \"type\" in item && item.type === \"group\";\r\n};\r\n\r\nconst RenderMenuItem = (item: ContextMenuItemProps, classes: ReturnType<typeof useStyles>) => {\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const Icon = item.icon;\r\n return (\r\n <MenuItem\r\n key={item.key}\r\n className={classes.menuItem}\r\n icon={Icon ? <Icon className={classes.menuItemIcon} /> : undefined}\r\n onClick={item.onClick}\r\n disabled={item.disabled}\r\n secondaryContent={item.secondaryContent}\r\n >\r\n {item.label}\r\n </MenuItem>\r\n );\r\n};\r\n\r\nconst RenderMenuItems = (items: ContextMenuItem[], classes: ReturnType<typeof useStyles>): ReactNode[] => {\r\n return items.map((item) => {\r\n if (IsDivider(item)) {\r\n return <MenuDivider key={item.key} />;\r\n }\r\n\r\n if (IsGroup(item)) {\r\n return (\r\n <MenuGroup key={item.key}>\r\n {item.header && <MenuGroupHeader>{item.header}</MenuGroupHeader>}\r\n {RenderMenuItems(item.items, classes)}\r\n </MenuGroup>\r\n );\r\n }\r\n\r\n return RenderMenuItem(item, classes);\r\n });\r\n};\r\n\r\n/**\r\n * A wrapper around Fluent UI's Menu component providing a simplified API for context menus.\r\n * Supports menu items with icons, dividers, and grouped items.\r\n */\r\nexport const ContextMenu = forwardRef<HTMLButtonElement, ContextMenuProps>((props, ref) => {\r\n const { items, onOpenChange, disabled } = props;\r\n const [open, setOpen] = useState(false);\r\n const classes = useStyles();\r\n\r\n const handleOpenChange = (_: unknown, data: { open: boolean }) => {\r\n setOpen(data.open);\r\n onOpenChange?.(data.open);\r\n };\r\n\r\n return (\r\n <Menu openOnContext open={open} onOpenChange={handleOpenChange}>\r\n <MenuTrigger disableButtonEnhancement>\r\n {props.trigger ?? (\r\n <Button\r\n ref={ref}\r\n icon={props.icon}\r\n onClick={(e) => {\r\n e?.stopPropagation();\r\n setOpen(true);\r\n }}\r\n disabled={disabled}\r\n />\r\n )}\r\n </MenuTrigger>\r\n <MenuPopover className={classes.menuPopover}>\r\n <MenuList>{RenderMenuItems(items, classes)}</MenuList>\r\n </MenuPopover>\r\n </Menu>\r\n );\r\n});\r\n\r\nContextMenu.displayName = \"ContextMenu\";\r\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/popover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAA6C,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAA6B,MAAM,4BAA4B,CAAC;AAErJ,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AAEvE,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,OAAO,EAAE;QACL,QAAQ,EAAE,OAAO;KACpB;IACD,OAAO,EAAE;QACL,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,GAAG,EAAE,MAAM,CAAC,gBAAgB;QAC5B,OAAO,EAAE,MAAM,CAAC,kBAAkB;QAClC,QAAQ,EAAE,OAAO;KACpB;CACJ,CAAC,CAAC;AAyBH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAqD,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjG,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAC9F,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,MAAM,YAAY,GAAG,cAAc,KAAK,SAAS,CAAC;IAClD,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC;IAEjE,MAAM,gBAAgB,GAAG,CAAC,CAAU,EAAE,IAAuB,EAAE,EAAE;QAC7D,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QACD,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,OAAO,CACH,MAAC,aAAa,IACV,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EACP,WAAW,IAAI;YACX,KAAK,EAAE,OAAO;YACd,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,QAAQ,EAAE,IAAI;SACjB,aAGL,KAAC,cAAc,IAAC,wBAAwB,kBACnC,KAAK,CAAC,OAAO,IAAI,KAAC,MAAM,IAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,GAAI,GAClG,EACjB,KAAC,cAAc,IAAC,SAAS,EAAE,gBAAgB,IAAI,OAAO,CAAC,OAAO,YAC1D,cAAK,SAAS,EAAE,OAAO,CAAC,OAAO,YAAG,QAAQ,GAAO,GACpC,IACL,CACnB,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC","sourcesContent":["import { type PropsWithChildren, type ReactElement, forwardRef, useState } from \"react\";\r\nimport { Popover as FluentPopover, PopoverTrigger, PopoverSurface, makeStyles, tokens, type PositioningShorthand } from \"@fluentui/react-components\";\r\nimport { type FluentIcon } from \"@fluentui/react-icons\";\r\nimport { Button } from \"shared-ui-components/fluent/primitives/button\";\r\n\r\nconst useStyles = makeStyles({\r\n surface: {\r\n maxWidth: \"400px\",\r\n },\r\n content: {\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: tokens.spacingVerticalM,\r\n padding: tokens.spacingHorizontalL,\r\n minWidth: \"300px\",\r\n },\r\n});\r\n\r\ntype PopoverWithIconProps = {\r\n icon: FluentIcon;\r\n trigger?: never;\r\n};\r\n\r\ntype PopoverWithTriggerProps = {\r\n icon?: never;\r\n trigger: ReactElement;\r\n};\r\n\r\ntype PopoverBaseProps = {\r\n /** Controlled open state */\r\n open?: boolean;\r\n /** Callback when open state changes */\r\n onOpenChange?: (open: boolean) => void;\r\n /** Positioning of the popover */\r\n positioning?: PositioningShorthand;\r\n /** Custom class for the surface */\r\n surfaceClassName?: string;\r\n};\r\n\r\ntype PopoverProps = PopoverBaseProps & (PopoverWithIconProps | PopoverWithTriggerProps);\r\n\r\nexport const Popover = forwardRef<HTMLButtonElement, PropsWithChildren<PopoverProps>>((props, ref) => {\r\n const { children, open: controlledOpen, onOpenChange, positioning, surfaceClassName } = props;\r\n const [internalOpen, setInternalOpen] = useState(false);\r\n const classes = useStyles();\r\n\r\n const isControlled = controlledOpen !== undefined;\r\n const popoverOpen = isControlled ? controlledOpen : internalOpen;\r\n\r\n const handleOpenChange = (_: unknown, data: { open: boolean }) => {\r\n if (!isControlled) {\r\n setInternalOpen(data.open);\r\n }\r\n onOpenChange?.(data.open);\r\n };\r\n\r\n return (\r\n <FluentPopover\r\n open={popoverOpen}\r\n onOpenChange={handleOpenChange}\r\n positioning={\r\n positioning ?? {\r\n align: \"start\",\r\n overflowBoundary: document.body,\r\n autoSize: true,\r\n }\r\n }\r\n >\r\n <PopoverTrigger disableButtonEnhancement>\r\n {props.trigger ?? <Button ref={ref} icon={props.icon} onClick={() => handleOpenChange(null, { open: true })} />}\r\n </PopoverTrigger>\r\n <PopoverSurface className={surfaceClassName ?? classes.surface}>\r\n <div className={classes.content}>{children}</div>\r\n </PopoverSurface>\r\n </FluentPopover>\r\n );\r\n});\r\n\r\nPopover.displayName = \"Popover\";\r\n"]}
1
+ {"version":3,"file":"popover.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/popover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAA6C,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAA6B,MAAM,4BAA4B,CAAC;AAErJ,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,OAAO,EAAE;QACL,QAAQ,EAAE,OAAO;KACpB;IACD,OAAO,EAAE;QACL,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,GAAG,EAAE,MAAM,CAAC,gBAAgB;QAC5B,OAAO,EAAE,MAAM,CAAC,kBAAkB;QAClC,QAAQ,EAAE,OAAO;KACpB;CACJ,CAAC,CAAC;AAyBH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAqD,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjG,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAC9F,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,MAAM,YAAY,GAAG,cAAc,KAAK,SAAS,CAAC;IAClD,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC;IAEjE,MAAM,gBAAgB,GAAG,CAAC,CAAU,EAAE,IAAuB,EAAE,EAAE;QAC7D,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QACD,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,OAAO,CACH,MAAC,aAAa,IACV,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EACP,WAAW,IAAI;YACX,KAAK,EAAE,OAAO;YACd,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,QAAQ,EAAE,IAAI;SACjB,aAGL,KAAC,cAAc,IAAC,wBAAwB,kBACnC,KAAK,CAAC,OAAO,IAAI,KAAC,MAAM,IAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,GAAI,GAClG,EACjB,KAAC,cAAc,IAAC,SAAS,EAAE,gBAAgB,IAAI,OAAO,CAAC,OAAO,YAC1D,cAAK,SAAS,EAAE,OAAO,CAAC,OAAO,YAAG,QAAQ,GAAO,GACpC,IACL,CACnB,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC","sourcesContent":["import { type PropsWithChildren, type ReactElement, forwardRef, useState } from \"react\";\r\nimport { Popover as FluentPopover, PopoverTrigger, PopoverSurface, makeStyles, tokens, type PositioningShorthand } from \"@fluentui/react-components\";\r\nimport { type FluentIcon } from \"@fluentui/react-icons\";\r\nimport { Button } from \"./button\";\r\n\r\nconst useStyles = makeStyles({\r\n surface: {\r\n maxWidth: \"400px\",\r\n },\r\n content: {\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: tokens.spacingVerticalM,\r\n padding: tokens.spacingHorizontalL,\r\n minWidth: \"300px\",\r\n },\r\n});\r\n\r\ntype PopoverWithIconProps = {\r\n icon: FluentIcon;\r\n trigger?: never;\r\n};\r\n\r\ntype PopoverWithTriggerProps = {\r\n icon?: never;\r\n trigger: ReactElement;\r\n};\r\n\r\ntype PopoverBaseProps = {\r\n /** Controlled open state */\r\n open?: boolean;\r\n /** Callback when open state changes */\r\n onOpenChange?: (open: boolean) => void;\r\n /** Positioning of the popover */\r\n positioning?: PositioningShorthand;\r\n /** Custom class for the surface */\r\n surfaceClassName?: string;\r\n};\r\n\r\ntype PopoverProps = PopoverBaseProps & (PopoverWithIconProps | PopoverWithTriggerProps);\r\n\r\nexport const Popover = forwardRef<HTMLButtonElement, PropsWithChildren<PopoverProps>>((props, ref) => {\r\n const { children, open: controlledOpen, onOpenChange, positioning, surfaceClassName } = props;\r\n const [internalOpen, setInternalOpen] = useState(false);\r\n const classes = useStyles();\r\n\r\n const isControlled = controlledOpen !== undefined;\r\n const popoverOpen = isControlled ? controlledOpen : internalOpen;\r\n\r\n const handleOpenChange = (_: unknown, data: { open: boolean }) => {\r\n if (!isControlled) {\r\n setInternalOpen(data.open);\r\n }\r\n onOpenChange?.(data.open);\r\n };\r\n\r\n return (\r\n <FluentPopover\r\n open={popoverOpen}\r\n onOpenChange={handleOpenChange}\r\n positioning={\r\n positioning ?? {\r\n align: \"start\",\r\n overflowBoundary: document.body,\r\n autoSize: true,\r\n }\r\n }\r\n >\r\n <PopoverTrigger disableButtonEnhancement>\r\n {props.trigger ?? <Button ref={ref} icon={props.icon} onClick={() => handleOpenChange(null, { open: true })} />}\r\n </PopoverTrigger>\r\n <PopoverSurface className={surfaceClassName ?? classes.surface}>\r\n <div className={classes.content}>{children}</div>\r\n </PopoverSurface>\r\n </FluentPopover>\r\n );\r\n});\r\n\r\nPopover.displayName = \"Popover\";\r\n"]}
@@ -1,7 +1,7 @@
1
1
  import { type Nullable } from "@babylonjs/core/index.js";
2
2
  import { type ReactElement } from "react";
3
3
  export type TooltipProps = {
4
- content?: Nullable<string>;
4
+ content?: Nullable<string | ReactElement>;
5
5
  children: ReactElement;
6
6
  };
7
7
  export declare const Tooltip: import("react").ForwardRefExoticComponent<TooltipProps & import("react").RefAttributes<HTMLElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/tooltip.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAqB,UAAU,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAItE,iFAAiF;AACjF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACzE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,OAAO,CACH,KAAC,aAAa,IAAC,YAAY,EAAC,aAAa,EAAC,OAAO,EAAE,OAAO,YACrD,QAAQ,GACG,CACnB,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC","sourcesContent":["import { type Nullable } from \"core/index\";\r\n\r\nimport { type ReactElement, forwardRef } from \"react\";\r\n\r\nimport { Tooltip as FluentTooltip } from \"@fluentui/react-components\";\r\n\r\nexport type TooltipProps = { content?: Nullable<string>; children: ReactElement };\r\n\r\n// forwardRef wrapper to avoid \"function components cannot be given refs\" warning\r\n// FluentTooltip handles ref forwarding to children internally via applyTriggerPropsToChildren\r\nexport const Tooltip = forwardRef<HTMLElement, TooltipProps>((props, _ref) => {\r\n const { content, children } = props;\r\n\r\n if (!content) {\r\n return children;\r\n }\r\n\r\n return (\r\n <FluentTooltip relationship=\"description\" content={content}>\r\n {children}\r\n </FluentTooltip>\r\n );\r\n});\r\n\r\nTooltip.displayName = \"Tooltip\";\r\n"]}
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/tooltip.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAqB,UAAU,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAItE,iFAAiF;AACjF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACzE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,OAAO,CACH,KAAC,aAAa,IAAC,YAAY,EAAC,aAAa,EAAC,OAAO,EAAE,OAAO,YACrD,QAAQ,GACG,CACnB,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC","sourcesContent":["import { type Nullable } from \"core/index\";\r\n\r\nimport { type ReactElement, forwardRef } from \"react\";\r\n\r\nimport { Tooltip as FluentTooltip } from \"@fluentui/react-components\";\r\n\r\nexport type TooltipProps = { content?: Nullable<string | ReactElement>; children: ReactElement };\r\n\r\n// forwardRef wrapper to avoid \"function components cannot be given refs\" warning\r\n// FluentTooltip handles ref forwarding to children internally via applyTriggerPropsToChildren\r\nexport const Tooltip = forwardRef<HTMLElement, TooltipProps>((props, _ref) => {\r\n const { content, children } = props;\r\n\r\n if (!content) {\r\n return children;\r\n }\r\n\r\n return (\r\n <FluentTooltip relationship=\"description\" content={content}>\r\n {children}\r\n </FluentTooltip>\r\n );\r\n});\r\n\r\nTooltip.displayName = \"Tooltip\";\r\n"]}
@@ -0,0 +1,31 @@
1
+ import { type ErrorInfo, type ReactNode, Component } from "react";
2
+ /**
3
+ * Props for the {@link ErrorBoundary} component.
4
+ */
5
+ type ErrorBoundaryProps = {
6
+ /** Child components to render */
7
+ children: ReactNode;
8
+ /** Optional fallback UI to show on error */
9
+ fallback?: ReactNode;
10
+ /** Optional callback when an error occurs */
11
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
12
+ /** Optional name for identifying this boundary in logs */
13
+ name?: string;
14
+ };
15
+ type ErrorBoundaryState = {
16
+ hasError: boolean;
17
+ error: Error | null;
18
+ errorInfo: ErrorInfo | null;
19
+ };
20
+ /**
21
+ * Error boundary component that catches JavaScript errors in child components
22
+ * and displays a fallback UI instead of crashing the entire application.
23
+ */
24
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
25
+ constructor(props: ErrorBoundaryProps);
26
+ static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
27
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
28
+ private _handleRetry;
29
+ render(): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
30
+ }
31
+ export {};
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Component } from "react";
3
+ import { makeStyles, tokens } from "@fluentui/react-components";
4
+ import { ErrorCircleRegular } from "@fluentui/react-icons";
5
+ import { Button } from "../../fluent/primitives/button.js";
6
+ import { TokenMap } from "../../fluent/primitives/utils.js";
7
+ const useStyles = makeStyles({
8
+ root: {
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ alignItems: "center",
12
+ justifyContent: "center",
13
+ padding: TokenMap.px20,
14
+ backgroundColor: tokens.colorNeutralBackground1,
15
+ color: tokens.colorNeutralForeground1,
16
+ height: "100%",
17
+ minHeight: "100px",
18
+ },
19
+ icon: {
20
+ fontSize: "48px",
21
+ color: tokens.colorPaletteRedForeground1,
22
+ marginBottom: TokenMap.px12,
23
+ },
24
+ title: {
25
+ fontSize: TokenMap.px16,
26
+ fontWeight: "600",
27
+ marginBottom: TokenMap.px8,
28
+ },
29
+ message: {
30
+ fontSize: TokenMap.px12,
31
+ color: tokens.colorNeutralForeground2,
32
+ marginBottom: TokenMap.px16,
33
+ textAlign: "center",
34
+ maxWidth: "300px",
35
+ },
36
+ details: {
37
+ fontSize: TokenMap.px10,
38
+ color: tokens.colorNeutralForeground3,
39
+ fontFamily: "monospace",
40
+ whiteSpace: "pre-wrap",
41
+ maxHeight: "100px",
42
+ overflow: "auto",
43
+ padding: TokenMap.px8,
44
+ backgroundColor: tokens.colorNeutralBackground3,
45
+ borderRadius: TokenMap.px4,
46
+ marginTop: TokenMap.px8,
47
+ maxWidth: "100%",
48
+ },
49
+ });
50
+ /**
51
+ * Error boundary component that catches JavaScript errors in child components
52
+ * and displays a fallback UI instead of crashing the entire application.
53
+ */
54
+ export class ErrorBoundary extends Component {
55
+ constructor(props) {
56
+ super(props);
57
+ this._handleRetry = () => {
58
+ this.setState({ hasError: false, error: null, errorInfo: null });
59
+ };
60
+ this.state = { hasError: false, error: null, errorInfo: null };
61
+ }
62
+ // eslint-disable-next-line @typescript-eslint/naming-convention
63
+ static getDerivedStateFromError(error) {
64
+ return { hasError: true, error };
65
+ }
66
+ componentDidCatch(error, errorInfo) {
67
+ this.setState({ errorInfo });
68
+ // Log the error
69
+ const boundaryName = this.props.name || "ErrorBoundary";
70
+ // eslint-disable-next-line no-console
71
+ console.error(`[${boundaryName}] Error caught:`, error, errorInfo);
72
+ // Call optional error callback
73
+ this.props.onError?.(error, errorInfo);
74
+ }
75
+ render() {
76
+ if (this.state.hasError) {
77
+ // If a custom fallback is provided, use it
78
+ if (this.props.fallback) {
79
+ return this.props.fallback;
80
+ }
81
+ // Default fallback UI
82
+ return _jsx(ErrorFallback, { error: this.state.error, onRetry: this._handleRetry });
83
+ }
84
+ return this.props.children;
85
+ }
86
+ }
87
+ function ErrorFallback({ error, onRetry }) {
88
+ const styles = useStyles();
89
+ return (_jsxs("div", { className: styles.root, children: [_jsx(ErrorCircleRegular, { className: styles.icon }), _jsx("div", { className: styles.title, children: "Something went wrong" }), _jsx("div", { className: styles.message, children: "An error occurred in this component. You can try again or continue using other parts of the tool." }), _jsx(Button, { label: "Try Again", appearance: "primary", onClick: onRetry }), error && _jsx("div", { className: styles.details, children: error.message })] }));
90
+ }
91
+ //# sourceMappingURL=errorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorBoundary.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/components/errorBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAkC,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,IAAI,EAAE;QACF,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,UAAU,EAAE,QAAQ;QACpB,cAAc,EAAE,QAAQ;QACxB,OAAO,EAAE,QAAQ,CAAC,IAAI;QACtB,eAAe,EAAE,MAAM,CAAC,uBAAuB;QAC/C,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,OAAO;KACrB;IACD,IAAI,EAAE;QACF,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,MAAM,CAAC,0BAA0B;QACxC,YAAY,EAAE,QAAQ,CAAC,IAAI;KAC9B;IACD,KAAK,EAAE;QACH,QAAQ,EAAE,QAAQ,CAAC,IAAI;QACvB,UAAU,EAAE,KAAK;QACjB,YAAY,EAAE,QAAQ,CAAC,GAAG;KAC7B;IACD,OAAO,EAAE;QACL,QAAQ,EAAE,QAAQ,CAAC,IAAI;QACvB,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,YAAY,EAAE,QAAQ,CAAC,IAAI;QAC3B,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,OAAO;KACpB;IACD,OAAO,EAAE;QACL,QAAQ,EAAE,QAAQ,CAAC,IAAI;QACvB,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,UAAU,EAAE,WAAW;QACvB,UAAU,EAAE,UAAU;QACtB,SAAS,EAAE,OAAO;QAClB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,QAAQ,CAAC,GAAG;QACrB,eAAe,EAAE,MAAM,CAAC,uBAAuB;QAC/C,YAAY,EAAE,QAAQ,CAAC,GAAG;QAC1B,SAAS,EAAE,QAAQ,CAAC,GAAG;QACvB,QAAQ,EAAE,MAAM;KACnB;CACJ,CAAC,CAAC;AAsBH;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAChF,YAAY,KAAyB;QACjC,KAAK,CAAC,KAAK,CAAC,CAAC;QAqBT,iBAAY,GAAG,GAAG,EAAE;YACxB,IAAI,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC;QAtBE,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC;IAED,gEAAgE;IAChE,MAAM,CAAC,wBAAwB,CAAC,KAAY;QACxC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACrC,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,SAAoB;QACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;QAE7B,gBAAgB;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,eAAe,CAAC;QACxD,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,IAAI,YAAY,iBAAiB,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAEnE,+BAA+B;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC3C,CAAC;IAMQ,MAAM;QACX,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACtB,2CAA2C;YAC3C,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC/B,CAAC;YAED,sBAAsB;YACtB,OAAO,KAAC,aAAa,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,GAAI,CAAC;QAClF,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC/B,CAAC;CACJ;AAOD,SAAS,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAsB;IACzD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,OAAO,CACH,eAAK,SAAS,EAAE,MAAM,CAAC,IAAI,aACvB,KAAC,kBAAkB,IAAC,SAAS,EAAE,MAAM,CAAC,IAAI,GAAI,EAC9C,cAAK,SAAS,EAAE,MAAM,CAAC,KAAK,qCAA4B,EACxD,cAAK,SAAS,EAAE,MAAM,CAAC,OAAO,kHAAyG,EACvI,KAAC,MAAM,IAAC,KAAK,EAAC,WAAW,EAAC,UAAU,EAAC,SAAS,EAAC,OAAO,EAAE,OAAO,GAAI,EAClE,KAAK,IAAI,cAAK,SAAS,EAAE,MAAM,CAAC,OAAO,YAAG,KAAK,CAAC,OAAO,GAAO,IAC7D,CACT,CAAC;AACN,CAAC","sourcesContent":["import { type ErrorInfo, type ReactNode, Component } from \"react\";\r\n\r\nimport { makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport { ErrorCircleRegular } from \"@fluentui/react-icons\";\r\nimport { Button } from \"shared-ui-components/fluent/primitives/button\";\r\nimport { TokenMap } from \"shared-ui-components/fluent/primitives/utils\";\r\n\r\nconst useStyles = makeStyles({\r\n root: {\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n padding: TokenMap.px20,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n color: tokens.colorNeutralForeground1,\r\n height: \"100%\",\r\n minHeight: \"100px\",\r\n },\r\n icon: {\r\n fontSize: \"48px\",\r\n color: tokens.colorPaletteRedForeground1,\r\n marginBottom: TokenMap.px12,\r\n },\r\n title: {\r\n fontSize: TokenMap.px16,\r\n fontWeight: \"600\",\r\n marginBottom: TokenMap.px8,\r\n },\r\n message: {\r\n fontSize: TokenMap.px12,\r\n color: tokens.colorNeutralForeground2,\r\n marginBottom: TokenMap.px16,\r\n textAlign: \"center\",\r\n maxWidth: \"300px\",\r\n },\r\n details: {\r\n fontSize: TokenMap.px10,\r\n color: tokens.colorNeutralForeground3,\r\n fontFamily: \"monospace\",\r\n whiteSpace: \"pre-wrap\",\r\n maxHeight: \"100px\",\r\n overflow: \"auto\",\r\n padding: TokenMap.px8,\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderRadius: TokenMap.px4,\r\n marginTop: TokenMap.px8,\r\n maxWidth: \"100%\",\r\n },\r\n});\r\n\r\n/**\r\n * Props for the {@link ErrorBoundary} component.\r\n */\r\ntype ErrorBoundaryProps = {\r\n /** Child components to render */\r\n children: ReactNode;\r\n /** Optional fallback UI to show on error */\r\n fallback?: ReactNode;\r\n /** Optional callback when an error occurs */\r\n onError?: (error: Error, errorInfo: ErrorInfo) => void;\r\n /** Optional name for identifying this boundary in logs */\r\n name?: string;\r\n};\r\n\r\ntype ErrorBoundaryState = {\r\n hasError: boolean;\r\n error: Error | null;\r\n errorInfo: ErrorInfo | null;\r\n};\r\n\r\n/**\r\n * Error boundary component that catches JavaScript errors in child components\r\n * and displays a fallback UI instead of crashing the entire application.\r\n */\r\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\r\n constructor(props: ErrorBoundaryProps) {\r\n super(props);\r\n this.state = { hasError: false, error: null, errorInfo: null };\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\r\n return { hasError: true, error };\r\n }\r\n\r\n override componentDidCatch(error: Error, errorInfo: ErrorInfo) {\r\n this.setState({ errorInfo });\r\n\r\n // Log the error\r\n const boundaryName = this.props.name || \"ErrorBoundary\";\r\n // eslint-disable-next-line no-console\r\n console.error(`[${boundaryName}] Error caught:`, error, errorInfo);\r\n\r\n // Call optional error callback\r\n this.props.onError?.(error, errorInfo);\r\n }\r\n\r\n private _handleRetry = () => {\r\n this.setState({ hasError: false, error: null, errorInfo: null });\r\n };\r\n\r\n override render() {\r\n if (this.state.hasError) {\r\n // If a custom fallback is provided, use it\r\n if (this.props.fallback) {\r\n return this.props.fallback;\r\n }\r\n\r\n // Default fallback UI\r\n return <ErrorFallback error={this.state.error} onRetry={this._handleRetry} />;\r\n }\r\n\r\n return this.props.children;\r\n }\r\n}\r\n\r\ntype ErrorFallbackProps = {\r\n error: Error | null;\r\n onRetry: () => void;\r\n};\r\n\r\nfunction ErrorFallback({ error, onRetry }: ErrorFallbackProps) {\r\n const styles = useStyles();\r\n\r\n return (\r\n <div className={styles.root}>\r\n <ErrorCircleRegular className={styles.icon} />\r\n <div className={styles.title}>Something went wrong</div>\r\n <div className={styles.message}>An error occurred in this component. You can try again or continue using other parts of the tool.</div>\r\n <Button label=\"Try Again\" appearance=\"primary\" onClick={onRetry} />\r\n {error && <div className={styles.details}>{error.message}</div>}\r\n </div>\r\n );\r\n}\r\n"]}
@@ -0,0 +1,67 @@
1
+ import { type ComponentType, type PropsWithChildren, type Ref } from "react";
2
+ import { type AccordionProps } from "../../fluent/primitives/accordion.js";
3
+ /**
4
+ * Describes a section within a dynamic accordion that can be added at runtime.
5
+ */
6
+ export type DynamicAccordionSection = Readonly<{
7
+ /**
8
+ * A unique identity for the section, which can be referenced by section content.
9
+ */
10
+ identity: string;
11
+ /**
12
+ * An optional order for the section, relative to other sections.
13
+ * Defaults to 0.
14
+ */
15
+ order?: number;
16
+ /**
17
+ * An optional flag indicating whether the section should be collapsed by default.
18
+ * Defaults to false.
19
+ */
20
+ collapseByDefault?: boolean;
21
+ }>;
22
+ /**
23
+ * Describes content that belongs to a section within a dynamic accordion.
24
+ */
25
+ export type DynamicAccordionSectionContent<ContextT> = Readonly<{
26
+ /**
27
+ * A unique key for the the content.
28
+ */
29
+ key: string;
30
+ /**
31
+ * The section this content belongs to.
32
+ */
33
+ section: string;
34
+ /**
35
+ * An optional order for the content within the section.
36
+ * Defaults to 0.
37
+ */
38
+ order?: number;
39
+ /**
40
+ * The React component that will be rendered for this content.
41
+ */
42
+ component: ComponentType<{
43
+ context: ContextT;
44
+ }>;
45
+ }>;
46
+ /**
47
+ * Imperative handle for controlling section highlights on the extensible accordion.
48
+ */
49
+ export type SectionsImperativeRef = {
50
+ /**
51
+ * Highlights the specified sections, collapsing all others until the context changes.
52
+ * @param sections The identity strings of the sections to highlight.
53
+ */
54
+ highlightSections: (sections: readonly string[]) => void;
55
+ };
56
+ /**
57
+ * An accordion component that supports dynamically adding sections and section content at runtime.
58
+ * Combines statically defined children sections with dynamically registered sections and content.
59
+ * @param props The accordion props including sections, section content, context, and an optional imperative ref.
60
+ * @returns The extensible accordion component.
61
+ */
62
+ export declare function ExtensibleAccordion<ContextT = unknown>(props: PropsWithChildren<{
63
+ sections: readonly DynamicAccordionSection[];
64
+ sectionContent: readonly DynamicAccordionSectionContent<ContextT>[];
65
+ context: ContextT;
66
+ sectionsRef?: Ref<SectionsImperativeRef>;
67
+ } & AccordionProps>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,148 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import { Accordion, AccordionSection } from "../../fluent/primitives/accordion.js";
4
+ import { makeStyles } from "@fluentui/react-components";
5
+ import { UXContextProvider } from "./uxContextProvider.js";
6
+ function AsReadonlyArray(array) {
7
+ return array;
8
+ }
9
+ const useStyles = makeStyles({
10
+ rootDiv: {
11
+ flex: 1,
12
+ overflow: "hidden",
13
+ display: "flex",
14
+ flexDirection: "column",
15
+ },
16
+ });
17
+ /**
18
+ * An accordion component that supports dynamically adding sections and section content at runtime.
19
+ * Combines statically defined children sections with dynamically registered sections and content.
20
+ * @param props The accordion props including sections, section content, context, and an optional imperative ref.
21
+ * @returns The extensible accordion component.
22
+ */
23
+ export function ExtensibleAccordion(props) {
24
+ const classes = useStyles();
25
+ const { children, sections, sectionContent, context, sectionsRef, ...rest } = props;
26
+ const defaultSections = useMemo(() => {
27
+ const defaultSections = [];
28
+ if (children) {
29
+ Children.forEach(children, (child) => {
30
+ if (isValidElement(child)) {
31
+ const childProps = child.props;
32
+ defaultSections.push({
33
+ identity: childProps.title,
34
+ collapseByDefault: childProps.collapseByDefault,
35
+ });
36
+ }
37
+ });
38
+ }
39
+ return AsReadonlyArray(defaultSections);
40
+ }, [children]);
41
+ // Cache stable component wrappers for static (children-based) sections so that
42
+ // React does not unmount/remount them on every re-render. The components read
43
+ // the latest child element from `elements` so they always render fresh content.
44
+ const staticComponentCacheRef = useRef({ elements: new Map(), components: new Map() });
45
+ const defaultSectionContent = useMemo(() => {
46
+ const cache = staticComponentCacheRef.current;
47
+ cache.elements.clear();
48
+ const defaultSectionContent = [];
49
+ if (children) {
50
+ Children.forEach(children, (child, index) => {
51
+ if (isValidElement(child)) {
52
+ const childProps = child.props;
53
+ const key = child.key ?? childProps.title;
54
+ // Update the element so the stable component renders the latest content.
55
+ cache.elements.set(key, child);
56
+ // Create a stable component wrapper only once per key.
57
+ if (!cache.components.has(key)) {
58
+ const capturedKey = key;
59
+ cache.components.set(capturedKey, () => _jsx(_Fragment, { children: cache.elements.get(capturedKey) }));
60
+ }
61
+ defaultSectionContent.push({
62
+ key,
63
+ section: defaultSections[index].identity,
64
+ component: cache.components.get(key),
65
+ });
66
+ }
67
+ });
68
+ }
69
+ return AsReadonlyArray(defaultSectionContent);
70
+ }, [children, defaultSections]);
71
+ const mergedSectionContent = useMemo(() => {
72
+ return AsReadonlyArray([...defaultSectionContent, ...sectionContent].map((content, index) => {
73
+ return {
74
+ ...content,
75
+ key: `${content.key}-${index}`,
76
+ order: content.order ?? index,
77
+ };
78
+ }));
79
+ }, [defaultSectionContent, sectionContent]);
80
+ const mergedSections = useMemo(() => {
81
+ const mergedSections = [...defaultSections.map((s) => ({ ...s, isDefault: true })), ...sections.map((s) => ({ ...s, isDefault: false }))];
82
+ // Check for implicit sections (e.g. sections that were not explicitly defined, but referenced by content).
83
+ const implicitSections = [];
84
+ for (const sectionContent of mergedSectionContent) {
85
+ if (!mergedSections.some((s) => s.identity === sectionContent.section) && !implicitSections.some((s) => s.identity === sectionContent.section)) {
86
+ implicitSections.push({ identity: sectionContent.section });
87
+ }
88
+ }
89
+ return AsReadonlyArray([...implicitSections.map((s) => ({ ...s, isDefault: false })), ...mergedSections].map((section, index) => {
90
+ return {
91
+ ...section,
92
+ order: section.order ?? index,
93
+ collapseByDefault: section.collapseByDefault ?? false,
94
+ };
95
+ }));
96
+ }, [defaultSections, sections, mergedSectionContent]);
97
+ const visibleSections = useMemo(() => {
98
+ if (!context) {
99
+ return [];
100
+ }
101
+ const sortedSections = [...mergedSections].sort((a, b) => {
102
+ // Default sections always come before non-default sections.
103
+ if (a.isDefault !== b.isDefault) {
104
+ return a.isDefault ? -1 : 1;
105
+ }
106
+ return a.order - b.order;
107
+ });
108
+ return sortedSections
109
+ .map((section) => {
110
+ // Get a flat list of the section content, preserving the key so it can be used when each component for each section is rendered.
111
+ const contentForSection = mergedSectionContent.filter((content) => content.section === section.identity);
112
+ // If there is no content for this section, we skip it.
113
+ if (contentForSection.length === 0) {
114
+ return null; // No content for this section
115
+ }
116
+ // Sort the content for this section by order, defaulting to 0 if not specified.
117
+ contentForSection.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
118
+ // Return the section with its identity, collapseByDefault flag, and the content components to render.
119
+ return {
120
+ identity: section.identity,
121
+ collapseByDefault: section.collapseByDefault ?? false,
122
+ components: contentForSection.map((content) => _jsx(content.component, { context: context }, content.key)),
123
+ };
124
+ })
125
+ .filter((section) => section !== null);
126
+ }, [mergedSections, mergedSectionContent, context]);
127
+ const [highlightSections, setHighlightSections] = useState();
128
+ // When the context changes, clear any existing highlights.
129
+ useLayoutEffect(() => {
130
+ setHighlightSections(undefined);
131
+ }, [context]);
132
+ // This just assigns the returned object to any type of React ref, whether it is
133
+ // a mutable ref with a 'current' property or whether it is a callback, useImperativeHandle
134
+ // will deal with it.
135
+ useImperativeHandle(sectionsRef, () => {
136
+ return {
137
+ highlightSections: (sectionsToHighlight) => {
138
+ if (sectionsToHighlight.length > 0) {
139
+ setHighlightSections(sectionsToHighlight);
140
+ }
141
+ },
142
+ };
143
+ }, []);
144
+ return (_jsx("div", { className: classes.rootDiv, children: visibleSections.length > -1 && (_jsx(UXContextProvider, { children: _jsxs(Accordion, { highlightSections: highlightSections, ...rest, children: [...visibleSections.map((section) => {
145
+ return (_jsx(AccordionSection, { title: section.identity, collapseByDefault: section.collapseByDefault, children: section.components }, section.identity));
146
+ })] }) })) }));
147
+ }
148
+ //# sourceMappingURL=extensibleAccordion.js.map