UNPKG

@equinor/fusion-framework-cli

Version:

--- title: Fusion Framework CLI ---

216 lines 8.31 kB
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useFramework } from '@equinor/fusion-framework-react'; import { useCurrentApp } from '@equinor/fusion-framework-react/app'; import { useObservableState, useObservableSubscription } from '@equinor/fusion-observable/react'; import '@equinor/fusion-framework-app'; import { ChipElement } from '@equinor/fusion-wc-chip'; ChipElement; import { EMPTY, catchError, lastValueFrom, map, of } from 'rxjs'; function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); } function convertGraphic(graphic) { if (graphic === undefined) { return {}; } if (typeof graphic === 'string') { return { graphicType: graphic.startsWith('<') ? 'inline-html' : 'eds', graphic: graphic, }; } if (graphic.type === 'svg') { return { graphicType: 'inline-svg', graphic: graphic.content, }; } return { graphicType: 'inline-html', graphic: graphic.content, }; } function convertMeta(meta) { if (meta === undefined) { return {}; } if (typeof meta === 'string') { return { metaType: meta.startsWith('<') ? 'inline-html' : 'eds', meta: meta, }; } if (meta.type === 'svg') { return { metaType: 'inline-svg', meta: meta.content, }; } return { metaType: 'inline-html', meta: meta.content, }; } /** * Map context query result to ContextSelectorResult. * Add any icons to selected types by using the 'graphic' property * @param src context query result * @returns src mapped to ContextResult type */ const mapper = (src) => { return src.map((i) => { const baseResult = { id: i.id, title: i.title, subTitle: i.subTitle ?? i.type.id, ...convertGraphic(i.graphic), ...convertMeta(i.meta), }; // Displays the status of the EquinorTask if it is not 'active' const isEquinorTaskInactive = !!(i.value.taskState && i.value.taskState.toLowerCase() !== 'active'); if (i.type.id === 'EquinorTask' && isEquinorTaskInactive) { baseResult.meta = `<fwc-chip disabled variant="outlined" value="${i.value.taskState}" />`; baseResult.metaType = 'inline-html'; } if (i.type.id === 'OrgChart') { // Org charts should always have 'list' icon baseResult.graphic = 'list'; baseResult.graphicType = 'eds'; // Displays the org chart status if it is not 'active' const isOrgChartInactive = !!(i.value.state && i.value.state.toLowerCase() !== 'active'); if (isOrgChartInactive) { baseResult.meta = `<fwc-chip disabled variant="outlined" value="${capitalizeFirstLetter(i.value.state ?? '')}" />`; baseResult.metaType = 'inline-html'; } } return baseResult; }); }; /** * Create a single ContextResultItem * @param props pops for the item to merge with defaults * @returns ContextResultItem */ const singleItem = (props) => { return Object.assign({ id: 'no-such-item', title: 'Change me' }, props); }; /** * Hook for querying context and setting resolver for ContextSelector component * See React Components storybook for info about ContextSelector component and its resolver * @link https://equinor.github.io/fusion-react-components/?path=/docs/data-contextselector--component * @return Array<ContextResolver, SetContextCallback> */ export const useContextResolver = () => { /* Framework modules */ const framework = useFramework(); const { currentApp } = useCurrentApp(); /** App module collection instance */ const instance$ = useMemo(() => currentApp?.instance$ || EMPTY, [currentApp]); /* context provider state */ const [provider, setProvider] = useState(null); /* Current context observable */ const { value: currentContext } = useObservableState(useMemo(() => provider?.currentContext$ || EMPTY, [provider])); const preselected = useMemo(() => { return currentContext ? mapper([currentContext]) : []; }, [currentContext]); /** callback function when current app instance changes */ const onContextProviderChange = useCallback((modules) => { /** try to get the context module from the app module instance */ const contextProvider = modules.context; if (contextProvider) { setProvider(contextProvider); } else { setProvider(null); } }, []); /** clear the app provider */ const clearContextProvider = useCallback(() => { setProvider(null); }, []); /** observe changes to app modules and clear / set the context provider on change */ useObservableSubscription(instance$, onContextProviderChange, clearContextProvider); useEffect(() => framework.modules.event.addEventListener('onReactAppLoaded', (e) => { // Only change provider if event is for current app if (e.detail.env.manifest.appKey === framework.modules.app.current?.appKey) { console.debug('useContextResolver::onReactAppLoaded', 'using legacy register hack method'); return onContextProviderChange(e.detail.modules); } }), [framework, onContextProviderChange]); const processError = useCallback((err) => { if (err.name === 'QueryClientError') { return processError(err.cause); } if (err.name === 'FusionContextSearchError') { const error = err; return [ singleItem({ id: error.name, title: error.title, subTitle: error.description, graphic: 'error_outlined', isDisabled: true, }), ]; } return [ singleItem({ title: 'Unexpected error occurred', subTitle: 'Please try again or report the issue in Services@Equinor', graphic: 'error_outlined', isDisabled: true, }), ]; }, []); /** * set resolver for ContextSelector * @return ContextResolver */ const minLength = 2; const resolver = useMemo(() => provider && { searchQuery: async (search) => { if (search.length < minLength) { return [ singleItem({ // TODO - make as enum if used for checks, or type id: 'min-length', title: `Type ${minLength - search.length} more chars to search`, isDisabled: true, }), ]; } try { return lastValueFrom(provider.queryContext(search).pipe(map(mapper), map((x) => x.length ? x : [ singleItem({ // TODO - make as enum if used for checks, or type id: 'no-results', title: 'No results found', graphic: 'info_circle', isDisabled: true, }), ]), /** handle failures */ catchError((err) => { console.error('PORTAL::ContextResolver', `failed to resolve context for query ${search}`, err, err.cause); return of(processError(err)); }))); /** this should NEVER happen! */ } catch (e) { const err = e; console.error('PORTAL::ContextResolver', `unhandled error for [${search}]`, e); return processError(err); } }, initialResult: preselected, }, [provider, preselected, processError]); return { resolver, provider, currentContext: preselected }; }; export default useContextResolver; //# sourceMappingURL=useContextResolver.js.map