@equinor/fusion-framework-cli
Version:
--- title: Fusion Framework CLI ---
216 lines • 8.31 kB
JavaScript
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