@onerjs/shared-ui-components 8.48.4 → 8.48.5

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 (104) hide show
  1. package/fluent/hoc/buttonLine.js +2 -1
  2. package/fluent/hoc/buttonLine.js.map +1 -1
  3. package/fluent/primitives/contextMenu.js.map +1 -1
  4. package/fluent/primitives/popover.js.map +1 -1
  5. package/modularTool/components/errorBoundary.d.ts +31 -0
  6. package/modularTool/components/errorBoundary.js +91 -0
  7. package/modularTool/components/errorBoundary.js.map +1 -0
  8. package/modularTool/components/extensibleAccordion.d.ts +67 -0
  9. package/modularTool/components/extensibleAccordion.js +148 -0
  10. package/modularTool/components/extensibleAccordion.js.map +1 -0
  11. package/modularTool/components/pane.d.ts +4 -0
  12. package/modularTool/components/pane.js +20 -0
  13. package/modularTool/components/pane.js.map +1 -0
  14. package/modularTool/components/teachingMoment.d.ts +20 -0
  15. package/modularTool/components/teachingMoment.js +17 -0
  16. package/modularTool/components/teachingMoment.js.map +1 -0
  17. package/modularTool/components/theme.d.ts +10 -0
  18. package/modularTool/components/theme.js +24 -0
  19. package/modularTool/components/theme.js.map +1 -0
  20. package/modularTool/components/uxContextProvider.d.ts +2 -0
  21. package/modularTool/components/uxContextProvider.js +19 -0
  22. package/modularTool/components/uxContextProvider.js.map +1 -0
  23. package/modularTool/contexts/extensionManagerContext.d.ts +6 -0
  24. package/modularTool/contexts/extensionManagerContext.js +6 -0
  25. package/modularTool/contexts/extensionManagerContext.js.map +1 -0
  26. package/modularTool/contexts/settingsContext.d.ts +3 -0
  27. package/modularTool/contexts/settingsContext.js +6 -0
  28. package/modularTool/contexts/settingsContext.js.map +1 -0
  29. package/modularTool/extensibility/builtInsExtensionFeed.d.ts +21 -0
  30. package/modularTool/extensibility/builtInsExtensionFeed.js +26 -0
  31. package/modularTool/extensibility/builtInsExtensionFeed.js.map +1 -0
  32. package/modularTool/extensibility/extensionFeed.d.ts +113 -0
  33. package/modularTool/extensibility/extensionFeed.js +2 -0
  34. package/modularTool/extensibility/extensionFeed.js.map +1 -0
  35. package/modularTool/extensibility/extensionManager.d.ts +111 -0
  36. package/modularTool/extensibility/extensionManager.js +277 -0
  37. package/modularTool/extensibility/extensionManager.js.map +1 -0
  38. package/modularTool/hooks/observableHooks.d.ts +35 -0
  39. package/modularTool/hooks/observableHooks.js +84 -0
  40. package/modularTool/hooks/observableHooks.js.map +1 -0
  41. package/modularTool/hooks/resourceHooks.d.ts +20 -0
  42. package/modularTool/hooks/resourceHooks.js +101 -0
  43. package/modularTool/hooks/resourceHooks.js.map +1 -0
  44. package/modularTool/hooks/settingsHooks.d.ts +8 -0
  45. package/modularTool/hooks/settingsHooks.js +40 -0
  46. package/modularTool/hooks/settingsHooks.js.map +1 -0
  47. package/modularTool/hooks/teachingMomentHooks.d.ts +34 -0
  48. package/modularTool/hooks/teachingMomentHooks.js +89 -0
  49. package/modularTool/hooks/teachingMomentHooks.js.map +1 -0
  50. package/modularTool/hooks/themeHooks.d.ts +17 -0
  51. package/modularTool/hooks/themeHooks.js +38 -0
  52. package/modularTool/hooks/themeHooks.js.map +1 -0
  53. package/modularTool/hooks/useResizeHandle.d.ts +35 -0
  54. package/modularTool/hooks/useResizeHandle.js +75 -0
  55. package/modularTool/hooks/useResizeHandle.js.map +1 -0
  56. package/modularTool/misc/assert.d.ts +5 -0
  57. package/modularTool/misc/assert.js +10 -0
  58. package/modularTool/misc/assert.js.map +1 -0
  59. package/modularTool/misc/graphUtils.d.ts +44 -0
  60. package/modularTool/misc/graphUtils.js +90 -0
  61. package/modularTool/misc/graphUtils.js.map +1 -0
  62. package/modularTool/misc/observableCollection.d.ts +23 -0
  63. package/modularTool/misc/observableCollection.js +43 -0
  64. package/modularTool/misc/observableCollection.js.map +1 -0
  65. package/modularTool/modularTool.d.ts +42 -0
  66. package/modularTool/modularTool.js +223 -0
  67. package/modularTool/modularTool.js.map +1 -0
  68. package/modularTool/modularity/serviceContainer.d.ts +64 -0
  69. package/modularTool/modularity/serviceContainer.js +181 -0
  70. package/modularTool/modularity/serviceContainer.js.map +1 -0
  71. package/modularTool/modularity/serviceDefinition.d.ts +64 -0
  72. package/modularTool/modularity/serviceDefinition.js +11 -0
  73. package/modularTool/modularity/serviceDefinition.js.map +1 -0
  74. package/modularTool/services/extensionsListService.d.ts +3 -0
  75. package/modularTool/services/extensionsListService.js +202 -0
  76. package/modularTool/services/extensionsListService.js.map +1 -0
  77. package/modularTool/services/globalSettings.d.ts +3 -0
  78. package/modularTool/services/globalSettings.js +9 -0
  79. package/modularTool/services/globalSettings.js.map +1 -0
  80. package/modularTool/services/reactContextService.d.ts +18 -0
  81. package/modularTool/services/reactContextService.js +5 -0
  82. package/modularTool/services/reactContextService.js.map +1 -0
  83. package/modularTool/services/settingsService.d.ts +24 -0
  84. package/modularTool/services/settingsService.js +41 -0
  85. package/modularTool/services/settingsService.js.map +1 -0
  86. package/modularTool/services/settingsStore.d.ts +55 -0
  87. package/modularTool/services/settingsStore.js +35 -0
  88. package/modularTool/services/settingsStore.js.map +1 -0
  89. package/modularTool/services/shellService.d.ts +256 -0
  90. package/modularTool/services/shellService.js +729 -0
  91. package/modularTool/services/shellService.js.map +1 -0
  92. package/modularTool/services/shellSettingsService.d.ts +3 -0
  93. package/modularTool/services/shellSettingsService.js +35 -0
  94. package/modularTool/services/shellSettingsService.js.map +1 -0
  95. package/modularTool/services/themeSelectorService.d.ts +3 -0
  96. package/modularTool/services/themeSelectorService.js +42 -0
  97. package/modularTool/services/themeSelectorService.js.map +1 -0
  98. package/modularTool/services/themeService.d.ts +60 -0
  99. package/modularTool/services/themeService.js +69 -0
  100. package/modularTool/services/themeService.js.map +1 -0
  101. package/modularTool/themes/babylonTheme.d.ts +3 -0
  102. package/modularTool/themes/babylonTheme.js +36 -0
  103. package/modularTool/themes/babylonTheme.js.map +1 -0
  104. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensionManager.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/extensibility/extensionManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type IDisposable, type Nullable } from \"core/index\";\r\nimport { type ServiceContainer } from \"../modularity/serviceContainer\";\r\nimport { type IExtensionFeed, type ExtensionMetadata, type ExtensionModule } from \"./extensionFeed\";\r\n\r\nimport { Logger } from \"core/Misc/logger\";\r\n\r\n/**\r\n * Represents a loaded extension.\r\n */\r\nexport interface IExtension {\r\n /**\r\n * The metadata for the extension.\r\n */\r\n readonly metadata: ExtensionMetadata;\r\n\r\n /**\r\n * Whether the extension is currently being installed, uninstalled, enabled, or disabled.\r\n */\r\n readonly isStateChanging: boolean;\r\n\r\n /**\r\n * Whether the extension is enabled.\r\n */\r\n readonly isInstalled: boolean;\r\n\r\n /**\r\n * Installs the extension.\r\n */\r\n installAsync(): Promise<void>;\r\n\r\n /**\r\n * Uninstalls the extension.\r\n */\r\n uninstallAsync(): Promise<void>;\r\n\r\n /**\r\n * Adds a handler that is called when the state of the extension changes.\r\n * @param handler The handler to add.\r\n * @returns A disposable that removes the handler when disposed.\r\n */\r\n addStateChangedHandler(handler: () => void): IDisposable;\r\n}\r\n\r\n/**\r\n * Provides information about an extension installation failure.\r\n */\r\nexport type InstallFailedInfo = {\r\n /**\r\n * The metadata of the extension that failed to install.\r\n */\r\n extension: ExtensionMetadata;\r\n\r\n /**\r\n * The error that occurred during the installation.\r\n */\r\n error: unknown;\r\n};\r\n\r\ntype InstalledExtension = {\r\n metadata: ExtensionMetadata;\r\n feed: IExtensionFeed;\r\n isStateChanging: boolean;\r\n extensionModule?: ExtensionModule;\r\n registrationToken?: IDisposable;\r\n};\r\n\r\nconst InstalledExtensionsKey = \"Extensions/InstalledExtensions\";\r\n\r\nconst ExtensionInstalledKeyPrefix = \"Extensions/IsExtensionInstalled\";\r\nfunction GetExtensionInstalledKey(name: string): string {\r\n return `${ExtensionInstalledKeyPrefix}/${name}`;\r\n}\r\n\r\n/**\r\n * Represents a query for loaded extensions.\r\n */\r\nexport interface IExtensionQuery {\r\n /**\r\n * The total number of extensions that satisfy the query.\r\n */\r\n readonly totalCount: number;\r\n\r\n /**\r\n * Fetches a range of extensions from the query.\r\n * @param index The index of the first extension to fetch.\r\n * @param count The number of extensions to fetch.\r\n * @returns A promise that resolves to the extensions.\r\n */\r\n getExtensionsAsync(index: number, count: number): Promise<IExtension[]>;\r\n}\r\n\r\nfunction GetExtensionIdentity(feed: string, name: string) {\r\n return `${feed}/${name}`;\r\n}\r\n\r\n/**\r\n * Manages the installation, uninstallation, enabling, and disabling of extensions.\r\n */\r\nexport class ExtensionManager implements IDisposable {\r\n private readonly _installedExtensions = new Map<string, InstalledExtension>();\r\n\r\n private readonly _stateChangedHandlers = new Map<string, Set<() => void>>();\r\n\r\n private constructor(\r\n private readonly _namespace: string,\r\n private readonly _serviceContainer: ServiceContainer,\r\n private readonly _feeds: readonly IExtensionFeed[],\r\n private readonly _onInstallFailed: (info: InstallFailedInfo) => void\r\n ) {}\r\n\r\n /**\r\n * Creates a new instance of the ExtensionManager.\r\n * This will automatically rehydrate previously installed and enabled extensions.\r\n * @param namespace The namespace to use for storing extension state in local storage.\r\n * @param serviceContainer The service container to use.\r\n * @param feeds The extension feeds to include.\r\n * @param onInstallFailed A callback that is called when an extension installation fails.\r\n * @returns A promise that resolves to the new instance of the ExtensionManager.\r\n */\r\n public static async CreateAsync(\r\n namespace: string,\r\n serviceContainer: ServiceContainer,\r\n feeds: readonly IExtensionFeed[],\r\n onInstallFailed: (info: InstallFailedInfo) => void\r\n ): Promise<ExtensionManager> {\r\n namespace = `Babylon/${namespace}`;\r\n\r\n const extensionManager = new ExtensionManager(namespace, serviceContainer, feeds, onInstallFailed);\r\n\r\n // Rehydrate installed extensions.\r\n const installedExtensionNames = JSON.parse(localStorage.getItem(`${namespace}/${InstalledExtensionsKey}`) ?? \"[]\") as string[];\r\n for (const installedExtensionName of installedExtensionNames) {\r\n const installedExtensionRaw = localStorage.getItem(`${namespace}/${GetExtensionInstalledKey(installedExtensionName)}`);\r\n if (installedExtensionRaw) {\r\n const installedExtensionData = JSON.parse(installedExtensionRaw) as {\r\n feed: string;\r\n metadata: ExtensionMetadata;\r\n };\r\n\r\n const feed = feeds.find((feed) => feed.name === installedExtensionData.feed);\r\n if (feed) {\r\n const installedExtension = extensionManager._createInstalledExtension(installedExtensionData.metadata, feed);\r\n extensionManager._installedExtensions.set(installedExtension.metadata.name, installedExtension);\r\n }\r\n }\r\n }\r\n\r\n // Load installed and enabled extensions.\r\n const enablePromises: Promise<void>[] = [];\r\n for (const extension of extensionManager._installedExtensions.values()) {\r\n enablePromises.push(\r\n (async () => {\r\n try {\r\n await extensionManager._enableAsync(extension.metadata, false, false);\r\n } catch {\r\n // If enabling the extension fails, uninstall it. The extension install fail callback will still be called,\r\n // so the owner of the ExtensionManager instance can decide what to do with the error.\r\n await extensionManager._uninstallAsync(extension.metadata, false);\r\n }\r\n })()\r\n );\r\n }\r\n\r\n await Promise.all(enablePromises);\r\n\r\n return extensionManager;\r\n }\r\n\r\n /**\r\n * Gets the names of the feeds that are included in the extension manager.\r\n * @returns The names of the feeds.\r\n */\r\n public get feedNames() {\r\n return this._feeds.map((feed) => feed.name);\r\n }\r\n\r\n /**\r\n * Queries the extension manager for extensions.\r\n * @param filter The filter to apply to the query.\r\n * @param feeds The feeds to include in the query.\r\n * @param installedOnly Whether to only include installed extensions.\r\n * @returns A promise that resolves to the extension query.\r\n */\r\n public async queryExtensionsAsync(filter = \"\", feeds: string[] = this.feedNames, installedOnly = false): Promise<IExtensionQuery> {\r\n if (installedOnly) {\r\n const installedExtensions = Array.from(this._installedExtensions.values()).filter((installedExtension) => feeds.includes(installedExtension.feed.name));\r\n return {\r\n totalCount: installedExtensions.length,\r\n getExtensionsAsync: async (index, count) => {\r\n return installedExtensions.slice(index, index + count).map((installedExtension) => this._createExtension(installedExtension.metadata, installedExtension.feed));\r\n },\r\n };\r\n }\r\n\r\n const queries = await Promise.all(\r\n this._feeds.filter((feed) => feeds.includes(feed.name)).map(async (feed) => Object.assign(await feed.queryExtensionsAsync(filter), { feed }))\r\n );\r\n const totalCount = queries.reduce((sum, query) => sum + query.totalCount, 0);\r\n\r\n return {\r\n totalCount,\r\n getExtensionsAsync: async (index, count) => {\r\n const extensions = new Array<IExtension>();\r\n let remaining = count;\r\n\r\n for (const query of queries) {\r\n if (remaining <= 0) {\r\n break;\r\n }\r\n\r\n if (index >= query.totalCount) {\r\n index -= query.totalCount;\r\n continue;\r\n }\r\n\r\n // This is intentionally sequential as we are querying for results until the count of results is met.\r\n // eslint-disable-next-line no-await-in-loop\r\n const metadataSlice = await query.getExtensionMetadataAsync(index, remaining);\r\n extensions.push(...metadataSlice.map((metadata) => this._createExtension(metadata, query.feed)));\r\n remaining -= metadataSlice.length;\r\n index = 0;\r\n }\r\n\r\n return extensions;\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Disposes the extension manager.\r\n */\r\n public dispose() {\r\n for (const installedExtension of this._installedExtensions.values()) {\r\n // eslint-disable-next-line github/no-then\r\n this._disableAsync(installedExtension.metadata, false).catch((error) => {\r\n Logger.Warn(`Failed to disable extension ${installedExtension.metadata.name}: ${error}`);\r\n });\r\n }\r\n\r\n this._stateChangedHandlers.clear();\r\n }\r\n\r\n private _getInstalledExtensionStorageKey(metadata: ExtensionMetadata, feed: IExtensionFeed): string {\r\n return `${this._namespace}/${GetExtensionInstalledKey(GetExtensionIdentity(feed.name, metadata.name))}`;\r\n }\r\n\r\n private _updateInstalledExtensionsStorage() {\r\n localStorage.setItem(\r\n `${this._namespace}/${InstalledExtensionsKey}`,\r\n JSON.stringify(Array.from(this._installedExtensions.values()).map((extension) => GetExtensionIdentity(extension.feed.name, extension.metadata.name)))\r\n );\r\n }\r\n\r\n private async _installAsync(metadata: ExtensionMetadata, feed: IExtensionFeed, isNestedStateChange: boolean): Promise<InstalledExtension> {\r\n let installedExtension = this._installedExtensions.get(metadata.name);\r\n\r\n if (!installedExtension) {\r\n installedExtension = this._createInstalledExtension(metadata, feed);\r\n installedExtension.isStateChanging = true;\r\n this._installedExtensions.set(metadata.name, installedExtension);\r\n\r\n try {\r\n // Enable the extension.\r\n await this._enableAsync(metadata, true, true);\r\n } catch (error) {\r\n this._installedExtensions.delete(metadata.name);\r\n throw error;\r\n } finally {\r\n !isNestedStateChange && (installedExtension.isStateChanging = false);\r\n }\r\n\r\n // Mark the extension as being installed.\r\n localStorage.setItem(\r\n this._getInstalledExtensionStorageKey(metadata, feed),\r\n JSON.stringify({\r\n feed: feed.name,\r\n metadata,\r\n })\r\n );\r\n this._updateInstalledExtensionsStorage();\r\n }\r\n\r\n return installedExtension;\r\n }\r\n\r\n private async _uninstallAsync(metadata: ExtensionMetadata, isNestedStateChange: boolean): Promise<void> {\r\n const installedExtension = this._installedExtensions.get(metadata.name);\r\n if (installedExtension && (isNestedStateChange || !installedExtension.isStateChanging)) {\r\n try {\r\n !isNestedStateChange && (installedExtension.isStateChanging = true);\r\n\r\n // Disable the extension.\r\n await this._disableAsync(metadata, true);\r\n\r\n // Remove the extension from in memory.\r\n this._installedExtensions.delete(metadata.name);\r\n\r\n // Mark the extension as being uninstalled.\r\n localStorage.removeItem(this._getInstalledExtensionStorageKey(metadata, installedExtension.feed));\r\n this._updateInstalledExtensionsStorage();\r\n } finally {\r\n !isNestedStateChange && (installedExtension.isStateChanging = false);\r\n }\r\n }\r\n }\r\n\r\n private async _enableAsync(metadata: ExtensionMetadata, isInitialInstall: boolean, isNestedStateChange: boolean): Promise<void> {\r\n const installedExtension = this._installedExtensions.get(metadata.name);\r\n if (installedExtension && (isNestedStateChange || !installedExtension.isStateChanging)) {\r\n try {\r\n !isNestedStateChange && (installedExtension.isStateChanging = true);\r\n\r\n // If we haven't done so already, load the extension module.\r\n if (!installedExtension.extensionModule) {\r\n installedExtension.extensionModule = await installedExtension.feed.getExtensionModuleAsync(metadata.name);\r\n }\r\n\r\n if (!installedExtension.extensionModule) {\r\n throw new Error(`Unable to load extension module for \"${metadata.name}\" from feed \"${installedExtension.feed.name}\".`);\r\n }\r\n\r\n // Register the ServiceDefinitions.\r\n let servicesRegistrationToken: Nullable<IDisposable> = null;\r\n if (installedExtension.extensionModule.default.serviceDefinitions) {\r\n servicesRegistrationToken = await this._serviceContainer.addServicesAsync(...installedExtension.extensionModule.default.serviceDefinitions);\r\n }\r\n\r\n // Create a registration token to for dispose.\r\n installedExtension.registrationToken = {\r\n dispose: () => {\r\n servicesRegistrationToken?.dispose();\r\n },\r\n };\r\n } catch (error: unknown) {\r\n this._onInstallFailed({\r\n extension: metadata,\r\n error,\r\n });\r\n throw error;\r\n } finally {\r\n !isNestedStateChange && (installedExtension.isStateChanging = false);\r\n }\r\n }\r\n }\r\n\r\n private async _disableAsync(metadata: ExtensionMetadata, isNestedStateChange: boolean): Promise<void> {\r\n const installedExtension = this._installedExtensions.get(metadata.name);\r\n if (installedExtension && (isNestedStateChange || !installedExtension.isStateChanging)) {\r\n try {\r\n !isNestedStateChange && (installedExtension.isStateChanging = true);\r\n\r\n // Unregister the service registrations.\r\n installedExtension.registrationToken?.dispose();\r\n } finally {\r\n !isNestedStateChange && (installedExtension.isStateChanging = false);\r\n }\r\n }\r\n }\r\n\r\n private _addStateChangedHandler(metadata: ExtensionMetadata, handler: () => void): IDisposable {\r\n let stateChangedHandlers = this._stateChangedHandlers.get(metadata.name);\r\n if (!stateChangedHandlers) {\r\n this._stateChangedHandlers.set(metadata.name, (stateChangedHandlers = new Set()));\r\n }\r\n\r\n stateChangedHandlers.add(handler);\r\n\r\n return {\r\n dispose: () => {\r\n stateChangedHandlers.delete(handler);\r\n if (stateChangedHandlers.size === 0) {\r\n this._stateChangedHandlers.delete(metadata.name);\r\n }\r\n },\r\n };\r\n }\r\n\r\n private _createExtension(metadata: ExtensionMetadata, feed: IExtensionFeed): IExtension {\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n const extensionManager = this;\r\n return {\r\n metadata,\r\n get isStateChanging() {\r\n return extensionManager._installedExtensions.get(metadata.name)?.isStateChanging ?? false;\r\n },\r\n get isInstalled() {\r\n return extensionManager._installedExtensions.has(metadata.name);\r\n },\r\n installAsync: async () => {\r\n await extensionManager._installAsync(metadata, feed, false);\r\n },\r\n uninstallAsync: async () => await extensionManager._uninstallAsync(metadata, false),\r\n addStateChangedHandler: (handler: () => void) => extensionManager._addStateChangedHandler(metadata, handler),\r\n };\r\n }\r\n\r\n private _createInstalledExtension(metadata: ExtensionMetadata, feed: IExtensionFeed): InstalledExtension {\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n const extensionManager = this;\r\n let isStateChanging = false;\r\n return {\r\n metadata,\r\n feed,\r\n get isStateChanging() {\r\n return isStateChanging;\r\n },\r\n set isStateChanging(value) {\r\n if (value !== isStateChanging) {\r\n isStateChanging = value;\r\n extensionManager._stateChangedHandlers.get(this.metadata.name)?.forEach((handler) => handler());\r\n }\r\n },\r\n };\r\n }\r\n}\r\n"]}
@@ -0,0 +1,35 @@
1
+ import { type IReadonlyObservable } from "@onerjs/core/index.js";
2
+ import { type ObservableCollection } from "../misc/observableCollection.js";
3
+ /**
4
+ * Returns the current value of the accessor and updates it when the specified event is fired on the specified element.
5
+ * @param accessor A function that returns the current value.
6
+ * @param element The element to listen for the event on.
7
+ * @param eventNames The names of the events to listen for.
8
+ * @returns The current value of the accessor.
9
+ * * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),
10
+ * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.
11
+ */
12
+ export declare function useEventfulState<T>(accessor: () => T, element: HTMLElement | null | undefined, ...eventNames: string[]): T;
13
+ /**
14
+ * Returns the current value of the accessor and updates it when any of the specified observables change.
15
+ * @param accessor A function that returns the current value.
16
+ * @param observables The observables to listen for changes on.
17
+ * @returns The current value of the accessor.
18
+ * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),
19
+ * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.
20
+ */
21
+ export declare function useObservableState<T>(accessor: () => T, ...observables: Array<IReadonlyObservable | null | undefined>): T;
22
+ /**
23
+ * Returns a copy of the items in the collection and updates it when the collection changes.
24
+ * @param collection The collection to observe.
25
+ * @returns A copy of the items in the collection.
26
+ */
27
+ export declare function useObservableCollection<T>(collection: ObservableCollection<T>): T[];
28
+ /**
29
+ * Returns a copy of the items in the collection sorted by the order property and updates it when the collection changes.
30
+ * @param collection The collection to observe.
31
+ * @returns A copy of the items in the collection sorted by the order property.
32
+ */
33
+ export declare function useOrderedObservableCollection<T extends Readonly<{
34
+ order?: number;
35
+ }>>(collection: ObservableCollection<T>): T[];
@@ -0,0 +1,84 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ /**
3
+ * Returns the current value of the accessor and updates it when the specified event is fired on the specified element.
4
+ * @param accessor A function that returns the current value.
5
+ * @param element The element to listen for the event on.
6
+ * @param eventNames The names of the events to listen for.
7
+ * @returns The current value of the accessor.
8
+ * * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),
9
+ * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.
10
+ */
11
+ export function useEventfulState(accessor, element, ...eventNames) {
12
+ const [current, setCurrent] = useState(accessor);
13
+ useEffect(() => {
14
+ setCurrent(accessor);
15
+ if (element) {
16
+ const removers = eventNames.map((eventName) => {
17
+ const handler = () => {
18
+ setCurrent(accessor());
19
+ };
20
+ element.addEventListener(eventName, handler);
21
+ return () => {
22
+ element.removeEventListener(eventName, handler);
23
+ };
24
+ });
25
+ return () => {
26
+ removers.forEach((remove) => remove());
27
+ };
28
+ }
29
+ return undefined;
30
+ }, [accessor, element, ...eventNames]);
31
+ return current;
32
+ }
33
+ /**
34
+ * Returns the current value of the accessor and updates it when any of the specified observables change.
35
+ * @param accessor A function that returns the current value.
36
+ * @param observables The observables to listen for changes on.
37
+ * @returns The current value of the accessor.
38
+ * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),
39
+ * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.
40
+ */
41
+ export function useObservableState(accessor, ...observables) {
42
+ const [current, setCurrent] = useState(accessor);
43
+ useEffect(() => {
44
+ setCurrent(accessor);
45
+ const observers = observables.map((observable) => {
46
+ if (observable) {
47
+ const observer = observable.add(() => {
48
+ setCurrent(accessor());
49
+ });
50
+ return observer;
51
+ }
52
+ return null;
53
+ });
54
+ return () => {
55
+ observers.forEach((observer) => observer?.remove());
56
+ };
57
+ }, [accessor, ...observables]);
58
+ return current;
59
+ }
60
+ /**
61
+ * Returns a copy of the items in the collection and updates it when the collection changes.
62
+ * @param collection The collection to observe.
63
+ * @returns A copy of the items in the collection.
64
+ */
65
+ export function useObservableCollection(collection) {
66
+ const itemsRef = useRef([...collection.items]);
67
+ return useObservableState(useCallback(() => {
68
+ if (itemsRef.current.length !== collection.items.length || !itemsRef.current.every((item, index) => item === collection.items[index])) {
69
+ itemsRef.current = [...collection.items];
70
+ }
71
+ return itemsRef.current;
72
+ }, [collection]), collection.observable);
73
+ }
74
+ /**
75
+ * Returns a copy of the items in the collection sorted by the order property and updates it when the collection changes.
76
+ * @param collection The collection to observe.
77
+ * @returns A copy of the items in the collection sorted by the order property.
78
+ */
79
+ export function useOrderedObservableCollection(collection) {
80
+ const items = useObservableCollection(collection);
81
+ const sortedItems = useMemo(() => items.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), [items]);
82
+ return sortedItems;
83
+ }
84
+ //# sourceMappingURL=observableHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observableHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/observableHooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type IReadonlyObservable } from \"core/index\";\r\n\r\nimport { type ObservableCollection } from \"../misc/observableCollection\";\r\n\r\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\r\n\r\n/**\r\n * Returns the current value of the accessor and updates it when the specified event is fired on the specified element.\r\n * @param accessor A function that returns the current value.\r\n * @param element The element to listen for the event on.\r\n * @param eventNames The names of the events to listen for.\r\n * @returns The current value of the accessor.\r\n * * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),\r\n * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.\r\n */\r\nexport function useEventfulState<T>(accessor: () => T, element: HTMLElement | null | undefined, ...eventNames: string[]): T {\r\n const [current, setCurrent] = useState(accessor);\r\n\r\n useEffect(() => {\r\n setCurrent(accessor);\r\n if (element) {\r\n const removers = eventNames.map((eventName) => {\r\n const handler = () => {\r\n setCurrent(accessor());\r\n };\r\n\r\n element.addEventListener(eventName, handler);\r\n\r\n return () => {\r\n element.removeEventListener(eventName, handler);\r\n };\r\n });\r\n\r\n return () => {\r\n removers.forEach((remove) => remove());\r\n };\r\n }\r\n\r\n return undefined;\r\n }, [accessor, element, ...eventNames]);\r\n\r\n return current;\r\n}\r\n\r\n/**\r\n * Returns the current value of the accessor and updates it when any of the specified observables change.\r\n * @param accessor A function that returns the current value.\r\n * @param observables The observables to listen for changes on.\r\n * @returns The current value of the accessor.\r\n * @remarks If the accessor function is not idempotent (e.g. it returns a different array or object instance each time it is called),\r\n * then there is a good chance it should be wrapped in a `useCallback` to prevent unnecessary re-renders or re-render infinite loops.\r\n */\r\nexport function useObservableState<T>(accessor: () => T, ...observables: Array<IReadonlyObservable | null | undefined>): T {\r\n const [current, setCurrent] = useState(accessor);\r\n\r\n useEffect(() => {\r\n setCurrent(accessor);\r\n const observers = observables.map((observable) => {\r\n if (observable) {\r\n const observer = observable.add(() => {\r\n setCurrent(accessor());\r\n });\r\n\r\n return observer;\r\n }\r\n return null;\r\n });\r\n\r\n return () => {\r\n observers.forEach((observer) => observer?.remove());\r\n };\r\n }, [accessor, ...observables]);\r\n\r\n return current;\r\n}\r\n\r\n/**\r\n * Returns a copy of the items in the collection and updates it when the collection changes.\r\n * @param collection The collection to observe.\r\n * @returns A copy of the items in the collection.\r\n */\r\nexport function useObservableCollection<T>(collection: ObservableCollection<T>) {\r\n const itemsRef = useRef([...collection.items]);\r\n return useObservableState(\r\n useCallback(() => {\r\n if (itemsRef.current.length !== collection.items.length || !itemsRef.current.every((item, index) => item === collection.items[index])) {\r\n itemsRef.current = [...collection.items];\r\n }\r\n return itemsRef.current;\r\n }, [collection]),\r\n collection.observable\r\n );\r\n}\r\n\r\n/**\r\n * Returns a copy of the items in the collection sorted by the order property and updates it when the collection changes.\r\n * @param collection The collection to observe.\r\n * @returns A copy of the items in the collection sorted by the order property.\r\n */\r\nexport function useOrderedObservableCollection<T extends Readonly<{ order?: number }>>(collection: ObservableCollection<T>) {\r\n const items = useObservableCollection(collection);\r\n const sortedItems = useMemo(() => items.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), [items]);\r\n return sortedItems;\r\n}\r\n"]}
@@ -0,0 +1,20 @@
1
+ import { type DependencyList } from "react";
2
+ import { type IDisposable } from "@onerjs/core/index.js";
3
+ /**
4
+ * Custom hook to manage a resource with automatic disposal. The resource is created once initially, and recreated
5
+ * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is
6
+ * disposed. The final instance is disposed when the component using this hook unmounts.
7
+ * @param factory A function that creates the resource.
8
+ * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.
9
+ * @returns The created resource.
10
+ */
11
+ export declare function useResource<T extends IDisposable | null | undefined>(factory: () => T, deps?: DependencyList): T;
12
+ /**
13
+ * Custom hook to manage an asynchronous resource with automatic disposal. The resource is created once initially, and recreated
14
+ * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is
15
+ * disposed. The final instance is disposed when the component using this hook unmounts.
16
+ * @param factory A function that creates the resource.
17
+ * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.
18
+ * @returns The created resource.
19
+ */
20
+ export declare function useAsyncResource<T extends IDisposable | null | undefined>(factory: (abortSignal: AbortSignal) => Promise<T>, deps?: DependencyList): T | undefined;
@@ -0,0 +1,101 @@
1
+ import { useRef, useEffect, useState } from "react";
2
+ /**
3
+ * Custom hook to manage a resource with automatic disposal. The resource is created once initially, and recreated
4
+ * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is
5
+ * disposed. The final instance is disposed when the component using this hook unmounts.
6
+ * @param factory A function that creates the resource.
7
+ * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.
8
+ * @returns The created resource.
9
+ */
10
+ export function useResource(factory, deps) {
11
+ const resourceRef = useRef();
12
+ const factoryRef = useRef(factory);
13
+ const depsRef = useRef(deps);
14
+ const initializedRef = useRef(false);
15
+ // Initialize resource synchronously on first call, when factory changes, or when deps change
16
+ if (!initializedRef.current || factoryRef.current !== factory || !DepsEqual(depsRef.current, deps)) {
17
+ // Dispose old resource if it exists
18
+ resourceRef.current?.dispose();
19
+ // Create new resource
20
+ resourceRef.current = factory();
21
+ initializedRef.current = true;
22
+ }
23
+ // Update refs to capture latest values
24
+ factoryRef.current = factory;
25
+ depsRef.current = deps;
26
+ // Cleanup effect for component unmount
27
+ useEffect(() => {
28
+ return () => {
29
+ resourceRef.current?.dispose();
30
+ resourceRef.current = undefined;
31
+ };
32
+ }, []);
33
+ return resourceRef.current;
34
+ }
35
+ /**
36
+ * Custom hook to manage an asynchronous resource with automatic disposal. The resource is created once initially, and recreated
37
+ * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is
38
+ * disposed. The final instance is disposed when the component using this hook unmounts.
39
+ * @param factory A function that creates the resource.
40
+ * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.
41
+ * @returns The created resource.
42
+ */
43
+ export function useAsyncResource(factory, deps) {
44
+ const [resource, setResource] = useState();
45
+ const factoryRef = useRef(factory);
46
+ // Update refs to capture latest values
47
+ factoryRef.current = factory;
48
+ useEffect(() => {
49
+ const abortController = new AbortController();
50
+ // Dispose old resource if it exists
51
+ resource?.dispose();
52
+ setResource(undefined);
53
+ // Create new resource
54
+ void (async () => {
55
+ try {
56
+ const newVal = await factory(abortController.signal);
57
+ if (!abortController.signal.aborted) {
58
+ setResource(newVal); // This will trigger a re-render so the new resource is returned to caller
59
+ }
60
+ else {
61
+ newVal?.dispose();
62
+ }
63
+ }
64
+ catch (error) {
65
+ if (error instanceof Error && error.name === "AbortError") {
66
+ return;
67
+ }
68
+ }
69
+ })();
70
+ return () => {
71
+ abortController.abort();
72
+ resource?.dispose();
73
+ setResource(undefined);
74
+ };
75
+ }, [factory, ...(deps ?? [])]);
76
+ return resource;
77
+ }
78
+ /**
79
+ * Compares two dependency lists for equality.
80
+ * @param a The first dependency list.
81
+ * @param b The second dependency list.
82
+ * @returns True if the dependency lists are equal.
83
+ */
84
+ function DepsEqual(a, b) {
85
+ if (a === b) {
86
+ return true;
87
+ }
88
+ if (a === undefined || b === undefined) {
89
+ return false;
90
+ }
91
+ if (a.length !== b.length) {
92
+ return false;
93
+ }
94
+ for (let i = 0; i < a.length; i++) {
95
+ if (!Object.is(a[i], b[i])) {
96
+ return false;
97
+ }
98
+ }
99
+ return true;
100
+ }
101
+ //# sourceMappingURL=resourceHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resourceHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/resourceHooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type DependencyList, useRef, useEffect, useState } from \"react\";\r\nimport { type IDisposable } from \"core/index\";\r\n\r\n/**\r\n * Custom hook to manage a resource with automatic disposal. The resource is created once initially, and recreated\r\n * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is\r\n * disposed. The final instance is disposed when the component using this hook unmounts.\r\n * @param factory A function that creates the resource.\r\n * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.\r\n * @returns The created resource.\r\n */\r\nexport function useResource<T extends IDisposable | null | undefined>(factory: () => T, deps?: DependencyList): T {\r\n const resourceRef = useRef<T>();\r\n const factoryRef = useRef(factory);\r\n const depsRef = useRef(deps);\r\n const initializedRef = useRef(false);\r\n\r\n // Initialize resource synchronously on first call, when factory changes, or when deps change\r\n if (!initializedRef.current || factoryRef.current !== factory || !DepsEqual(depsRef.current, deps)) {\r\n // Dispose old resource if it exists\r\n resourceRef.current?.dispose();\r\n\r\n // Create new resource\r\n resourceRef.current = factory();\r\n initializedRef.current = true;\r\n }\r\n\r\n // Update refs to capture latest values\r\n factoryRef.current = factory;\r\n depsRef.current = deps;\r\n\r\n // Cleanup effect for component unmount\r\n useEffect(() => {\r\n return () => {\r\n resourceRef.current?.dispose();\r\n resourceRef.current = undefined;\r\n };\r\n }, []);\r\n\r\n return resourceRef.current as T;\r\n}\r\n\r\n/**\r\n * Custom hook to manage an asynchronous resource with automatic disposal. The resource is created once initially, and recreated\r\n * if the factory function or any dependency changes. Whenever the resource is recreated, the previous instance is\r\n * disposed. The final instance is disposed when the component using this hook unmounts.\r\n * @param factory A function that creates the resource.\r\n * @param deps An optional dependency list. When any dependency changes, the resource is disposed and recreated.\r\n * @returns The created resource.\r\n */\r\nexport function useAsyncResource<T extends IDisposable | null | undefined>(factory: (abortSignal: AbortSignal) => Promise<T>, deps?: DependencyList): T | undefined {\r\n const [resource, setResource] = useState<T | undefined>();\r\n const factoryRef = useRef(factory);\r\n\r\n // Update refs to capture latest values\r\n factoryRef.current = factory;\r\n\r\n useEffect(() => {\r\n const abortController = new AbortController();\r\n\r\n // Dispose old resource if it exists\r\n resource?.dispose();\r\n setResource(undefined);\r\n\r\n // Create new resource\r\n void (async () => {\r\n try {\r\n const newVal = await factory(abortController.signal);\r\n if (!abortController.signal.aborted) {\r\n setResource(newVal); // This will trigger a re-render so the new resource is returned to caller\r\n } else {\r\n newVal?.dispose();\r\n }\r\n } catch (error) {\r\n if (error instanceof Error && error.name === \"AbortError\") {\r\n return;\r\n }\r\n }\r\n })();\r\n\r\n return () => {\r\n abortController.abort();\r\n resource?.dispose();\r\n setResource(undefined);\r\n };\r\n }, [factory, ...(deps ?? [])]);\r\n\r\n return resource;\r\n}\r\n\r\n/**\r\n * Compares two dependency lists for equality.\r\n * @param a The first dependency list.\r\n * @param b The second dependency list.\r\n * @returns True if the dependency lists are equal.\r\n */\r\nfunction DepsEqual(a: DependencyList | undefined, b: DependencyList | undefined): boolean {\r\n if (a === b) {\r\n return true;\r\n }\r\n if (a === undefined || b === undefined) {\r\n return false;\r\n }\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < a.length; i++) {\r\n if (!Object.is(a[i], b[i])) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n"]}
@@ -0,0 +1,8 @@
1
+ import { type Dispatch, type SetStateAction } from "react";
2
+ import { type SettingDescriptor } from "../services/settingsStore.js";
3
+ /**
4
+ * Hook that reads and writes a setting from the settings store.
5
+ * @param descriptor The setting descriptor that identifies the setting and its default value.
6
+ * @returns A tuple of [currentValue, setValue, resetValue] similar to React's useState.
7
+ */
8
+ export declare function useSetting<T>(descriptor: SettingDescriptor<T>): [T, Dispatch<SetStateAction<T>>, () => void];
@@ -0,0 +1,40 @@
1
+ import { useCallback, useEffect, useMemo } from "react";
2
+ import { Observable } from "@onerjs/core/Misc/observable.js";
3
+ import { useSettingsStore } from "../contexts/settingsContext.js";
4
+ import { useObservableState } from "./observableHooks.js";
5
+ /**
6
+ * Hook that reads and writes a setting from the settings store.
7
+ * @param descriptor The setting descriptor that identifies the setting and its default value.
8
+ * @returns A tuple of [currentValue, setValue, resetValue] similar to React's useState.
9
+ */
10
+ export function useSetting(descriptor) {
11
+ const settingsStore = useSettingsStore();
12
+ // Only watch for this specific setting to change. Otherwise, any time any setting changes we would
13
+ // call readSetting again, which if it is an object, it will be a new instance, which can cause
14
+ // unnecessary re-renders in consumers of this hook.
15
+ const settingObservable = useMemo(() => new Observable(), []);
16
+ useEffect(() => {
17
+ if (settingsStore) {
18
+ const observer = settingsStore.onChanged.add((key) => {
19
+ if (key === descriptor.key) {
20
+ settingObservable.notifyObservers();
21
+ }
22
+ });
23
+ return () => {
24
+ observer.remove();
25
+ };
26
+ }
27
+ return undefined;
28
+ }, [settingsStore, descriptor.key]);
29
+ const value = useObservableState(useCallback(() => settingsStore?.readSetting(descriptor) ?? descriptor.defaultValue, [settingsStore, descriptor.key, descriptor.defaultValue]), settingObservable);
30
+ const setValue = useCallback((newValue) => {
31
+ const value = typeof newValue === "function" ? newValue(settingsStore?.readSetting(descriptor) ?? descriptor.defaultValue) : newValue;
32
+ settingsStore?.writeSetting(descriptor, value);
33
+ }, [settingsStore, descriptor.key, descriptor.defaultValue]);
34
+ const resetValue = useCallback(() => settingsStore?.writeSetting(descriptor, descriptor.defaultValue), [settingsStore, descriptor.key, descriptor.defaultValue]);
35
+ if (!settingsStore) {
36
+ throw new Error("Settings store is not available in context.");
37
+ }
38
+ return [value, setValue, resetValue];
39
+ }
40
+ //# sourceMappingURL=settingsHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settingsHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/settingsHooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAI5F,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAI,UAAgC;IAC1D,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IAEzC,mGAAmG;IACnG,+FAA+F;IAC/F,oDAAoD;IACpD,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,UAAU,EAAQ,EAAE,EAAE,CAAC,CAAC;IACpE,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,aAAa,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACjD,IAAI,GAAG,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC;oBACzB,iBAAiB,CAAC,eAAe,EAAE,CAAC;gBACxC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,OAAO,GAAG,EAAE;gBACR,QAAQ,CAAC,MAAM,EAAE,CAAC;YACtB,CAAC,CAAC;QACN,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpC,MAAM,KAAK,GAAG,kBAAkB,CAC5B,WAAW,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,WAAW,CAAI,UAAU,CAAC,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,EACjJ,iBAAiB,CACpB,CAAC;IAEF,MAAM,QAAQ,GAAG,WAAW,CACxB,CAAC,QAA2B,EAAQ,EAAE;QAClC,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAE,QAA2B,CAAC,aAAa,EAAE,WAAW,CAAI,UAAU,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7J,aAAa,EAAE,YAAY,CAAI,UAAU,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC,EACD,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,YAAY,CAAC,CAC3D,CAAC;IAEF,MAAM,UAAU,GAAG,WAAW,CAAC,GAAS,EAAE,CAAC,aAAa,EAAE,YAAY,CAAI,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;IAE1K,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAClD,CAAC","sourcesContent":["import { type Dispatch, type SetStateAction, useCallback, useEffect, useMemo } from \"react\";\r\n\r\nimport { type SettingDescriptor } from \"../services/settingsStore\";\r\n\r\nimport { Observable } from \"core/Misc/observable\";\r\nimport { useSettingsStore } from \"../contexts/settingsContext\";\r\nimport { useObservableState } from \"./observableHooks\";\r\n\r\n/**\r\n * Hook that reads and writes a setting from the settings store.\r\n * @param descriptor The setting descriptor that identifies the setting and its default value.\r\n * @returns A tuple of [currentValue, setValue, resetValue] similar to React's useState.\r\n */\r\nexport function useSetting<T>(descriptor: SettingDescriptor<T>): [T, Dispatch<SetStateAction<T>>, () => void] {\r\n const settingsStore = useSettingsStore();\r\n\r\n // Only watch for this specific setting to change. Otherwise, any time any setting changes we would\r\n // call readSetting again, which if it is an object, it will be a new instance, which can cause\r\n // unnecessary re-renders in consumers of this hook.\r\n const settingObservable = useMemo(() => new Observable<void>(), []);\r\n useEffect(() => {\r\n if (settingsStore) {\r\n const observer = settingsStore.onChanged.add((key) => {\r\n if (key === descriptor.key) {\r\n settingObservable.notifyObservers();\r\n }\r\n });\r\n\r\n return () => {\r\n observer.remove();\r\n };\r\n }\r\n\r\n return undefined;\r\n }, [settingsStore, descriptor.key]);\r\n\r\n const value = useObservableState(\r\n useCallback(() => settingsStore?.readSetting<T>(descriptor) ?? descriptor.defaultValue, [settingsStore, descriptor.key, descriptor.defaultValue]),\r\n settingObservable\r\n );\r\n\r\n const setValue = useCallback(\r\n (newValue: SetStateAction<T>): void => {\r\n const value = typeof newValue === \"function\" ? (newValue as (prev: T) => T)(settingsStore?.readSetting<T>(descriptor) ?? descriptor.defaultValue) : newValue;\r\n settingsStore?.writeSetting<T>(descriptor, value);\r\n },\r\n [settingsStore, descriptor.key, descriptor.defaultValue]\r\n );\r\n\r\n const resetValue = useCallback((): void => settingsStore?.writeSetting<T>(descriptor, descriptor.defaultValue), [settingsStore, descriptor.key, descriptor.defaultValue]);\r\n\r\n if (!settingsStore) {\r\n throw new Error(\"Settings store is not available in context.\");\r\n }\r\n\r\n return [value, setValue, resetValue] as const;\r\n}\r\n"]}
@@ -0,0 +1,34 @@
1
+ import { type Nullable } from "@onerjs/core/index.js";
2
+ import { type OnOpenChangeData, type PositioningImperativeRef } from "@fluentui/react-components";
3
+ /**
4
+ * Creates a hook for managing teaching moment state.
5
+ * @param name The unique name of the teaching moment.
6
+ * @returns A hook that returns the teaching moment state.
7
+ */
8
+ export declare function MakeTeachingMoment(name: string): (suppress?: boolean) => {
9
+ readonly shouldDisplay: boolean;
10
+ readonly onDismissed: () => void;
11
+ readonly reset: () => void;
12
+ };
13
+ /**
14
+ * Creates a hook for managing teaching moment state for a dialog.
15
+ * @param name The unique name of the teaching moment.
16
+ * @returns A hook that returns the teaching moment state for a dialog.
17
+ */
18
+ export declare function MakeDialogTeachingMoment(name: string): (suppress?: boolean) => {
19
+ readonly shouldDisplay: boolean;
20
+ readonly onOpenChange: (e: unknown, data: OnOpenChangeData) => void;
21
+ readonly reset: () => void;
22
+ };
23
+ /**
24
+ * Creates a hook for managing teaching moment state for a popover.
25
+ * @param name The unique name of the teaching moment.
26
+ * @returns A hook that returns the teaching moment state for a popover.
27
+ */
28
+ export declare function MakePopoverTeachingMoment(name: string): (suppress?: boolean) => {
29
+ readonly shouldDisplay: boolean;
30
+ readonly positioningRef: import("react").Dispatch<import("react").SetStateAction<Nullable<PositioningImperativeRef>>>;
31
+ readonly targetRef: import("react").Dispatch<import("react").SetStateAction<Nullable<HTMLElement>>>;
32
+ readonly onOpenChange: (e: unknown, data: OnOpenChangeData) => void;
33
+ readonly reset: () => void;
34
+ };
@@ -0,0 +1,89 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useSetting } from "./settingsHooks.js";
3
+ import { AsyncLock } from "@onerjs/core/Misc/asyncLock.js";
4
+ import { Deferred } from "@onerjs/core/Misc/deferred.js";
5
+ const SequencerLock = new AsyncLock();
6
+ /**
7
+ * Creates a hook for managing teaching moment state.
8
+ * @param name The unique name of the teaching moment.
9
+ * @returns A hook that returns the teaching moment state.
10
+ */
11
+ export function MakeTeachingMoment(name) {
12
+ return (suppress) => {
13
+ const [hasDisplayed, setHasDisplayed, resetDisplayed] = useSetting({ key: `TeachingMoments/${name}`, defaultValue: false });
14
+ const [shouldDisplay, setShouldDisplay] = useState(false);
15
+ const deferredRef = useRef();
16
+ useEffect(() => {
17
+ if (!hasDisplayed && !suppress && !deferredRef.current) {
18
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
19
+ SequencerLock.lockAsync(async () => {
20
+ deferredRef.current = new Deferred();
21
+ setShouldDisplay(true);
22
+ // Just hold the lock until the hook cleanup, which is effectively component unmount (e.g. the teaching moment is dismissed).
23
+ await deferredRef.current.promise;
24
+ });
25
+ }
26
+ return () => {
27
+ deferredRef.current?.resolve();
28
+ };
29
+ }, [hasDisplayed, suppress]);
30
+ const onDismissed = useCallback(() => {
31
+ setHasDisplayed(true);
32
+ deferredRef.current?.resolve();
33
+ deferredRef.current = undefined;
34
+ setShouldDisplay(false);
35
+ }, []);
36
+ return {
37
+ shouldDisplay,
38
+ onDismissed,
39
+ reset: resetDisplayed,
40
+ };
41
+ };
42
+ }
43
+ /**
44
+ * Creates a hook for managing teaching moment state for a dialog.
45
+ * @param name The unique name of the teaching moment.
46
+ * @returns A hook that returns the teaching moment state for a dialog.
47
+ */
48
+ export function MakeDialogTeachingMoment(name) {
49
+ const useTeachingMoment = MakeTeachingMoment(name);
50
+ return (suppress) => {
51
+ const { shouldDisplay, onDismissed, reset } = useTeachingMoment(suppress);
52
+ const onOpenChange = useCallback((e, data) => {
53
+ if (!data.open) {
54
+ onDismissed();
55
+ }
56
+ }, []);
57
+ return {
58
+ shouldDisplay,
59
+ onOpenChange,
60
+ reset,
61
+ };
62
+ };
63
+ }
64
+ /**
65
+ * Creates a hook for managing teaching moment state for a popover.
66
+ * @param name The unique name of the teaching moment.
67
+ * @returns A hook that returns the teaching moment state for a popover.
68
+ */
69
+ export function MakePopoverTeachingMoment(name) {
70
+ const useDialogTeachingMoment = MakeDialogTeachingMoment(name);
71
+ return (suppress) => {
72
+ const [target, setTarget] = useState(null);
73
+ const [positioningRef, setPositioningRef] = useState(null);
74
+ const { shouldDisplay, onOpenChange, reset } = useDialogTeachingMoment(suppress || !target || !positioningRef);
75
+ useEffect(() => {
76
+ if (target && positioningRef) {
77
+ positioningRef.setTarget(target);
78
+ }
79
+ }, [target, positioningRef]);
80
+ return {
81
+ shouldDisplay,
82
+ positioningRef: setPositioningRef,
83
+ targetRef: setTarget,
84
+ onOpenChange,
85
+ reset,
86
+ };
87
+ };
88
+ }
89
+ //# sourceMappingURL=teachingMomentHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teachingMomentHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/teachingMomentHooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Nullable } from \"core/index\";\r\n\r\nimport { type OnOpenChangeData, type PositioningImperativeRef } from \"@fluentui/react-components\";\r\n\r\nimport { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useSetting } from \"./settingsHooks\";\r\n\r\nimport { AsyncLock } from \"core/Misc/asyncLock\";\r\nimport { Deferred } from \"core/Misc/deferred\";\r\n\r\nconst SequencerLock = new AsyncLock();\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state.\r\n */\r\nexport function MakeTeachingMoment(name: string) {\r\n return (suppress?: boolean) => {\r\n const [hasDisplayed, setHasDisplayed, resetDisplayed] = useSetting({ key: `TeachingMoments/${name}`, defaultValue: false });\r\n const [shouldDisplay, setShouldDisplay] = useState(false);\r\n\r\n const deferredRef = useRef<Deferred<void>>();\r\n\r\n useEffect(() => {\r\n if (!hasDisplayed && !suppress && !deferredRef.current) {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n SequencerLock.lockAsync(async () => {\r\n deferredRef.current = new Deferred<void>();\r\n setShouldDisplay(true);\r\n // Just hold the lock until the hook cleanup, which is effectively component unmount (e.g. the teaching moment is dismissed).\r\n await deferredRef.current.promise;\r\n });\r\n }\r\n\r\n return () => {\r\n deferredRef.current?.resolve();\r\n };\r\n }, [hasDisplayed, suppress]);\r\n\r\n const onDismissed = useCallback(() => {\r\n setHasDisplayed(true);\r\n deferredRef.current?.resolve();\r\n deferredRef.current = undefined;\r\n setShouldDisplay(false);\r\n }, []);\r\n\r\n return {\r\n shouldDisplay,\r\n onDismissed,\r\n reset: resetDisplayed,\r\n } as const;\r\n };\r\n}\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state for a dialog.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state for a dialog.\r\n */\r\nexport function MakeDialogTeachingMoment(name: string) {\r\n const useTeachingMoment = MakeTeachingMoment(name);\r\n\r\n return (suppress?: boolean) => {\r\n const { shouldDisplay, onDismissed, reset } = useTeachingMoment(suppress);\r\n\r\n const onOpenChange = useCallback((e: unknown, data: OnOpenChangeData) => {\r\n if (!data.open) {\r\n onDismissed();\r\n }\r\n }, []);\r\n\r\n return {\r\n shouldDisplay,\r\n onOpenChange,\r\n reset,\r\n } as const;\r\n };\r\n}\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state for a popover.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state for a popover.\r\n */\r\nexport function MakePopoverTeachingMoment(name: string) {\r\n const useDialogTeachingMoment = MakeDialogTeachingMoment(name);\r\n\r\n return (suppress?: boolean) => {\r\n const [target, setTarget] = useState<Nullable<HTMLElement>>(null);\r\n const [positioningRef, setPositioningRef] = useState<Nullable<PositioningImperativeRef>>(null);\r\n\r\n const { shouldDisplay, onOpenChange, reset } = useDialogTeachingMoment(suppress || !target || !positioningRef);\r\n\r\n useEffect(() => {\r\n if (target && positioningRef) {\r\n positioningRef.setTarget(target);\r\n }\r\n }, [target, positioningRef]);\r\n\r\n return {\r\n shouldDisplay,\r\n positioningRef: setPositioningRef,\r\n targetRef: setTarget,\r\n onOpenChange,\r\n reset,\r\n } as const;\r\n };\r\n}\r\n"]}
@@ -0,0 +1,17 @@
1
+ import { type ThemeMode } from "../services/themeService.js";
2
+ /**
3
+ * Hook that provides the current theme mode state and controls for changing it.
4
+ * @returns An object with the current dark mode state, theme mode, and functions to set or toggle the theme.
5
+ */
6
+ export declare function useThemeMode(): {
7
+ readonly isDarkMode: boolean;
8
+ readonly themeMode: ThemeMode;
9
+ readonly setThemeMode: (mode: ThemeMode) => void;
10
+ readonly toggleThemeMode: () => void | undefined;
11
+ };
12
+ /**
13
+ * Hook that returns the current Fluent UI theme based on the active theme mode.
14
+ * @param invert If true, inverts the theme (returns light theme in dark mode and vice versa). Defaults to false.
15
+ * @returns The current Fluent UI theme object.
16
+ */
17
+ export declare function useTheme(invert?: boolean): import("@fluentui/tokens").Theme;
@@ -0,0 +1,38 @@
1
+ import { ThemeModeSettingDescriptor, ThemeResolver } from "../services/themeService.js";
2
+ import { useCallback } from "react";
3
+ import { useSettingsStore } from "../contexts/settingsContext.js";
4
+ import { DarkTheme, LightTheme } from "../themes/babylonTheme.js";
5
+ import { useObservableState } from "./observableHooks.js";
6
+ import { useResource } from "./resourceHooks.js";
7
+ /**
8
+ * Hook that provides the current theme mode state and controls for changing it.
9
+ * @returns An object with the current dark mode state, theme mode, and functions to set or toggle the theme.
10
+ */
11
+ export function useThemeMode() {
12
+ const settingsStore = useSettingsStore();
13
+ const themeResolver = useResource(useCallback(() => (settingsStore ? new ThemeResolver(settingsStore) : undefined), [settingsStore]));
14
+ const state = useObservableState(useCallback(() => ({
15
+ isDarkMode: themeResolver?.isDark ?? false,
16
+ themeMode: themeResolver?.mode ?? ThemeModeSettingDescriptor.defaultValue,
17
+ setThemeMode: (mode) => {
18
+ if (themeResolver) {
19
+ themeResolver.mode = mode;
20
+ }
21
+ },
22
+ toggleThemeMode: () => themeResolver?.toggle(),
23
+ }), [themeResolver]), themeResolver?.onChanged);
24
+ if (!themeResolver) {
25
+ throw new Error("Settings store is not available in context.");
26
+ }
27
+ return state;
28
+ }
29
+ /**
30
+ * Hook that returns the current Fluent UI theme based on the active theme mode.
31
+ * @param invert If true, inverts the theme (returns light theme in dark mode and vice versa). Defaults to false.
32
+ * @returns The current Fluent UI theme object.
33
+ */
34
+ export function useTheme(invert = false) {
35
+ const { isDarkMode } = useThemeMode();
36
+ return isDarkMode !== invert ? DarkTheme : LightTheme;
37
+ }
38
+ //# sourceMappingURL=themeHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themeHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/themeHooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,0BAA0B,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErG,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,YAAY;IACxB,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAEtI,MAAM,KAAK,GAAG,kBAAkB,CAC5B,WAAW,CACP,GAAG,EAAE,CACD,CAAC;QACG,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,KAAK;QAC1C,SAAS,EAAE,aAAa,EAAE,IAAI,IAAI,0BAA0B,CAAC,YAAY;QACzE,YAAY,EAAE,CAAC,IAAe,EAAE,EAAE;YAC9B,IAAI,aAAa,EAAE,CAAC;gBAChB,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;YAC9B,CAAC;QACL,CAAC;QACD,eAAe,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE;KACjD,CAAU,EACf,CAAC,aAAa,CAAC,CAClB,EACD,aAAa,EAAE,SAAS,CAC3B,CAAC;IAEF,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAM,GAAG,KAAK;IACnC,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,EAAE,CAAC;IACtC,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;AAC1D,CAAC","sourcesContent":["import { type ThemeMode, ThemeModeSettingDescriptor, ThemeResolver } from \"../services/themeService\";\r\n\r\nimport { useCallback } from \"react\";\r\n\r\nimport { useSettingsStore } from \"../contexts/settingsContext\";\r\nimport { DarkTheme, LightTheme } from \"../themes/babylonTheme\";\r\nimport { useObservableState } from \"./observableHooks\";\r\nimport { useResource } from \"./resourceHooks\";\r\n\r\n/**\r\n * Hook that provides the current theme mode state and controls for changing it.\r\n * @returns An object with the current dark mode state, theme mode, and functions to set or toggle the theme.\r\n */\r\nexport function useThemeMode() {\r\n const settingsStore = useSettingsStore();\r\n const themeResolver = useResource(useCallback(() => (settingsStore ? new ThemeResolver(settingsStore) : undefined), [settingsStore]));\r\n\r\n const state = useObservableState(\r\n useCallback(\r\n () =>\r\n ({\r\n isDarkMode: themeResolver?.isDark ?? false,\r\n themeMode: themeResolver?.mode ?? ThemeModeSettingDescriptor.defaultValue,\r\n setThemeMode: (mode: ThemeMode) => {\r\n if (themeResolver) {\r\n themeResolver.mode = mode;\r\n }\r\n },\r\n toggleThemeMode: () => themeResolver?.toggle(),\r\n }) as const,\r\n [themeResolver]\r\n ),\r\n themeResolver?.onChanged\r\n );\r\n\r\n if (!themeResolver) {\r\n throw new Error(\"Settings store is not available in context.\");\r\n }\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * Hook that returns the current Fluent UI theme based on the active theme mode.\r\n * @param invert If true, inverts the theme (returns light theme in dark mode and vice versa). Defaults to false.\r\n * @returns The current Fluent UI theme object.\r\n */\r\nexport function useTheme(invert = false) {\r\n const { isDarkMode } = useThemeMode();\r\n return isDarkMode !== invert ? DarkTheme : LightTheme;\r\n}\r\n"]}