@universityofmaryland/web-feeds-library 1.3.0-beta.1 → 1.3.0-beta.3
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.
- package/dist/factory/core/createBaseFeed.d.ts.map +1 -1
- package/dist/factory/core/createBaseFeed.js +2 -0
- package/dist/factory/core/createBaseFeed.js.map +1 -1
- package/dist/factory/core/types.d.ts +1 -0
- package/dist/factory/core/types.d.ts.map +1 -1
- package/dist/feeds/experts/_types.d.ts +4 -3
- package/dist/feeds/experts/_types.d.ts.map +1 -1
- package/dist/feeds/experts/bio.d.ts.map +1 -1
- package/dist/feeds/experts/bio.js +14 -4
- package/dist/feeds/experts/bio.js.map +1 -1
- package/dist/strategies/display/experts.d.ts +2 -1
- package/dist/strategies/display/experts.d.ts.map +1 -1
- package/dist/strategies/display/experts.js +43 -11
- package/dist/strategies/display/experts.js.map +1 -1
- package/dist/strategies/fetch/experts.d.ts +1 -1
- package/dist/strategies/fetch/experts.d.ts.map +1 -1
- package/dist/strategies/fetch/experts.js +6 -6
- package/dist/strategies/fetch/experts.js.map +1 -1
- package/dist/strategies/fetch/graphql.js +3 -3
- package/dist/strategies/fetch/graphql.js.map +1 -1
- package/dist/types/api.d.ts +1 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/data/experts.d.ts +8 -0
- package/dist/types/data/experts.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createBaseFeed.d.ts","sourceRoot":"","sources":["../../../source/factory/core/createBaseFeed.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAsB,MAAM,SAAS,CAAC;AAyChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,EACpD,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,GACxC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"createBaseFeed.d.ts","sourceRoot":"","sources":["../../../source/factory/core/createBaseFeed.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAsB,MAAM,SAAS,CAAC;AAyChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,EACpD,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,GACxC,iBAAiB,CAsInB"}
|
|
@@ -20,6 +20,7 @@ function createBaseFeed(config) {
|
|
|
20
20
|
isLazyLoad = false,
|
|
21
21
|
enableCategoryFallback = false,
|
|
22
22
|
categories,
|
|
23
|
+
ids,
|
|
23
24
|
entriesToRemove,
|
|
24
25
|
fetchStrategy,
|
|
25
26
|
displayStrategy,
|
|
@@ -58,6 +59,7 @@ function createBaseFeed(config) {
|
|
|
58
59
|
const baseProps = {
|
|
59
60
|
token,
|
|
60
61
|
categories,
|
|
62
|
+
ids,
|
|
61
63
|
entriesToRemove,
|
|
62
64
|
numberOfColumnsToShow,
|
|
63
65
|
numberOfRowsToStart,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createBaseFeed.js","sources":["../../../source/factory/core/createBaseFeed.ts"],"sourcesContent":["/**\n * Base Feed Factory\n *\n * Creates a complete feed with initialization, data fetching, and display logic.\n * Uses strategy pattern to allow customization of fetch, display, and layout.\n *\n * @module factory/core/createBaseFeed\n */\n\nimport { LoadingState } from '../../states';\nimport {\n createFeedHelpers,\n createDisplayHandlers,\n createFetchHandlers,\n} from '../helpers';\nimport { BaseFeedConfig, FeedFactoryResult, CardMappingOptions } from './types';\n\n/**\n * Create a base feed using the factory pattern\n *\n * This factory eliminates ~70% of boilerplate code by handling:\n * - Feed initialization (container, loading, state)\n * - Strategy composition (fetch, display, layout)\n * - Lifecycle management (start, load more, no results)\n * - Shadow DOM support\n * - Event system\n *\n * @template TData - The type of entry data from the API\n * @template TVariables - The type of API variables\n *\n * @param config - Configuration for the feed\n * @returns ElementModel with feed element and styles\n *\n * @example\n * ```typescript\n * // Simple grid feed\n * const eventsFeed = createBaseFeed({\n * token: 'my-token',\n * isThemeDark: false,\n * numberOfColumnsToShow: 3,\n * numberOfRowsToStart: 2,\n * isLazyLoad: true,\n * fetchStrategy: eventsFetchStrategy,\n * displayStrategy: eventsDisplayStrategy,\n * layoutStrategy: gridGapLayout,\n * imageConfig: (entry) => ({\n * imageUrl: entry.image[0].url,\n * altText: entry.image[0].altText,\n * linkUrl: entry.url,\n * }),\n * });\n *\n * // Use the feed\n * document.body.appendChild(eventsFeed.element);\n * ```\n */\nexport function createBaseFeed<TData, TVariables = any>(\n config: BaseFeedConfig<TData, TVariables>,\n): FeedFactoryResult {\n const {\n token,\n isThemeDark = false,\n isTransparent = false,\n isOverlay = false,\n isAligned = false,\n isMediaTrained = null,\n cardType,\n numberOfColumnsToShow = 1,\n numberOfRowsToStart,\n isLazyLoad = false,\n enableCategoryFallback = false,\n categories,\n entriesToRemove,\n fetchStrategy,\n displayStrategy,\n layoutStrategy,\n noResultsConfig,\n imageConfig,\n } = config;\n\n const container = document.createElement('div');\n const loading = new LoadingState({ isThemeDark });\n let styles = loading.styles;\n\n // Create helpers for state management\n const helpers = createFeedHelpers({\n container,\n initialStyles: styles,\n });\n\n // Override setStyles to also update the local styles variable\n const originalSetStyles = helpers.setStyles;\n helpers.setStyles = (additionalStyles: string) => {\n styles += additionalStyles;\n originalSetStyles(additionalStyles);\n };\n\n // Shadow root support\n let shadowRoot: ShadowRoot | null = null;\n const shadowRootCallback = (shadow: ShadowRoot) => {\n shadowRoot = shadow;\n\n helpers.setShadowRoot(shadow);\n };\n\n const cardMappingOptions: CardMappingOptions = {\n isThemeDark,\n isTransparent,\n isOverlay,\n isAligned,\n imageConfig,\n ...(cardType && { cardType }),\n };\n\n const layoutElement = layoutStrategy.create({\n columns: numberOfColumnsToShow as 2 | 3 | 4,\n isThemeDark,\n });\n helpers.setStyles(layoutElement.styles);\n\n const baseProps = {\n token,\n categories,\n entriesToRemove,\n numberOfColumnsToShow,\n numberOfRowsToStart,\n isLazyLoad,\n isMediaTrained,\n getOffset: helpers.getOffset,\n };\n\n // Create a mutable reference for lazy load callback\n // This allows us to set it after fetchHandlers are created\n const lazyLoadCallbackRef = {\n current: undefined as (() => Promise<void>) | undefined,\n };\n\n // Create display handlers with callback reference\n const displayHandlers = createDisplayHandlers({\n displayStrategy,\n layoutStrategy,\n helpers,\n cardMappingOptions,\n isLazyLoad,\n numberOfColumnsToShow,\n numberOfRowsToStart,\n noResultsConfig: {\n ...noResultsConfig,\n isThemeDark, // Ensure theme is passed to EmptyState\n },\n // Pass a function that calls the current callback\n lazyLoadCallback: isLazyLoad\n ? async () => {\n if (lazyLoadCallbackRef.current) {\n await lazyLoadCallbackRef.current();\n }\n }\n : undefined,\n });\n\n // Create fetch handlers\n const fetchHandlers = createFetchHandlers({\n fetchStrategy,\n helpers,\n baseProps,\n displayHandlers,\n layoutElement,\n isThemeDark,\n enableCategoryFallback,\n });\n\n // Set the lazy load callback reference now that fetch handlers exist\n lazyLoadCallbackRef.current = fetchHandlers.loadMore;\n\n // Append loading indicator to container\n container.appendChild(loading.element);\n\n // Start fetching data\n fetchHandlers.start();\n\n // Return feed with getter for styles to ensure dynamic access\n return {\n element: container,\n get styles() {\n return styles;\n },\n events: {\n callback: shadowRootCallback,\n },\n };\n}\n"],"names":[],"mappings":";;;;;;;;AAwDO,SAAS,eACd,QACmB;AACnB,QAAM;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB;AAAA,IACA,wBAAwB;AAAA,IACxB;AAAA,IACA,aAAa;AAAA,IACb,yBAAyB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,QAAM,UAAU,IAAI,aAAa,EAAE,aAAa;AAChD,MAAI,SAAS,QAAQ;AAGrB,QAAM,UAAU,kBAAkB;AAAA,IAChC;AAAA,IACA,eAAe;AAAA,EAAA,CAChB;AAGD,QAAM,oBAAoB,QAAQ;AAClC,UAAQ,YAAY,CAAC,qBAA6B;AAChD,cAAU;AACV,sBAAkB,gBAAgB;AAAA,EACpC;AAIA,QAAM,qBAAqB,CAAC,WAAuB;AAGjD,YAAQ,cAAc,MAAM;AAAA,EAC9B;AAEA,QAAM,qBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,YAAY,EAAE,SAAA;AAAA,EAAS;AAG7B,QAAM,gBAAgB,eAAe,OAAO;AAAA,IAC1C,SAAS;AAAA,IACT;AAAA,EAAA,CACD;AACD,UAAQ,UAAU,cAAc,MAAM;AAEtC,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,EAAA;AAKrB,QAAM,sBAAsB;AAAA,IAC1B,SAAS;AAAA,EAAA;AAIX,QAAM,kBAAkB,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,MACf,GAAG;AAAA,MACH;AAAA;AAAA,IAAA;AAAA;AAAA,IAGF,kBAAkB,aACd,YAAY;AACV,UAAI,oBAAoB,SAAS;AAC/B,cAAM,oBAAoB,QAAA;AAAA,MAC5B;AAAA,IACF,IACA;AAAA,EAAA,CACL;AAGD,QAAM,gBAAgB,oBAAoB;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAGD,sBAAoB,UAAU,cAAc;AAG5C,YAAU,YAAY,QAAQ,OAAO;AAGrC,gBAAc,MAAA;AAGd,SAAO;AAAA,IACL,SAAS;AAAA,IACT,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"createBaseFeed.js","sources":["../../../source/factory/core/createBaseFeed.ts"],"sourcesContent":["/**\n * Base Feed Factory\n *\n * Creates a complete feed with initialization, data fetching, and display logic.\n * Uses strategy pattern to allow customization of fetch, display, and layout.\n *\n * @module factory/core/createBaseFeed\n */\n\nimport { LoadingState } from '../../states';\nimport {\n createFeedHelpers,\n createDisplayHandlers,\n createFetchHandlers,\n} from '../helpers';\nimport { BaseFeedConfig, FeedFactoryResult, CardMappingOptions } from './types';\n\n/**\n * Create a base feed using the factory pattern\n *\n * This factory eliminates ~70% of boilerplate code by handling:\n * - Feed initialization (container, loading, state)\n * - Strategy composition (fetch, display, layout)\n * - Lifecycle management (start, load more, no results)\n * - Shadow DOM support\n * - Event system\n *\n * @template TData - The type of entry data from the API\n * @template TVariables - The type of API variables\n *\n * @param config - Configuration for the feed\n * @returns ElementModel with feed element and styles\n *\n * @example\n * ```typescript\n * // Simple grid feed\n * const eventsFeed = createBaseFeed({\n * token: 'my-token',\n * isThemeDark: false,\n * numberOfColumnsToShow: 3,\n * numberOfRowsToStart: 2,\n * isLazyLoad: true,\n * fetchStrategy: eventsFetchStrategy,\n * displayStrategy: eventsDisplayStrategy,\n * layoutStrategy: gridGapLayout,\n * imageConfig: (entry) => ({\n * imageUrl: entry.image[0].url,\n * altText: entry.image[0].altText,\n * linkUrl: entry.url,\n * }),\n * });\n *\n * // Use the feed\n * document.body.appendChild(eventsFeed.element);\n * ```\n */\nexport function createBaseFeed<TData, TVariables = any>(\n config: BaseFeedConfig<TData, TVariables>,\n): FeedFactoryResult {\n const {\n token,\n isThemeDark = false,\n isTransparent = false,\n isOverlay = false,\n isAligned = false,\n isMediaTrained = null,\n cardType,\n numberOfColumnsToShow = 1,\n numberOfRowsToStart,\n isLazyLoad = false,\n enableCategoryFallback = false,\n categories,\n ids,\n entriesToRemove,\n fetchStrategy,\n displayStrategy,\n layoutStrategy,\n noResultsConfig,\n imageConfig,\n } = config;\n\n const container = document.createElement('div');\n const loading = new LoadingState({ isThemeDark });\n let styles = loading.styles;\n\n // Create helpers for state management\n const helpers = createFeedHelpers({\n container,\n initialStyles: styles,\n });\n\n // Override setStyles to also update the local styles variable\n const originalSetStyles = helpers.setStyles;\n helpers.setStyles = (additionalStyles: string) => {\n styles += additionalStyles;\n originalSetStyles(additionalStyles);\n };\n\n // Shadow root support\n let shadowRoot: ShadowRoot | null = null;\n const shadowRootCallback = (shadow: ShadowRoot) => {\n shadowRoot = shadow;\n\n helpers.setShadowRoot(shadow);\n };\n\n const cardMappingOptions: CardMappingOptions = {\n isThemeDark,\n isTransparent,\n isOverlay,\n isAligned,\n imageConfig,\n ...(cardType && { cardType }),\n };\n\n const layoutElement = layoutStrategy.create({\n columns: numberOfColumnsToShow as 2 | 3 | 4,\n isThemeDark,\n });\n helpers.setStyles(layoutElement.styles);\n\n const baseProps = {\n token,\n categories,\n ids,\n entriesToRemove,\n numberOfColumnsToShow,\n numberOfRowsToStart,\n isLazyLoad,\n isMediaTrained,\n getOffset: helpers.getOffset,\n };\n\n // Create a mutable reference for lazy load callback\n // This allows us to set it after fetchHandlers are created\n const lazyLoadCallbackRef = {\n current: undefined as (() => Promise<void>) | undefined,\n };\n\n // Create display handlers with callback reference\n const displayHandlers = createDisplayHandlers({\n displayStrategy,\n layoutStrategy,\n helpers,\n cardMappingOptions,\n isLazyLoad,\n numberOfColumnsToShow,\n numberOfRowsToStart,\n noResultsConfig: {\n ...noResultsConfig,\n isThemeDark, // Ensure theme is passed to EmptyState\n },\n // Pass a function that calls the current callback\n lazyLoadCallback: isLazyLoad\n ? async () => {\n if (lazyLoadCallbackRef.current) {\n await lazyLoadCallbackRef.current();\n }\n }\n : undefined,\n });\n\n // Create fetch handlers\n const fetchHandlers = createFetchHandlers({\n fetchStrategy,\n helpers,\n baseProps,\n displayHandlers,\n layoutElement,\n isThemeDark,\n enableCategoryFallback,\n });\n\n // Set the lazy load callback reference now that fetch handlers exist\n lazyLoadCallbackRef.current = fetchHandlers.loadMore;\n\n // Append loading indicator to container\n container.appendChild(loading.element);\n\n // Start fetching data\n fetchHandlers.start();\n\n // Return feed with getter for styles to ensure dynamic access\n return {\n element: container,\n get styles() {\n return styles;\n },\n events: {\n callback: shadowRootCallback,\n },\n };\n}\n"],"names":[],"mappings":";;;;;;;;AAwDO,SAAS,eACd,QACmB;AACnB,QAAM;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB;AAAA,IACA,wBAAwB;AAAA,IACxB;AAAA,IACA,aAAa;AAAA,IACb,yBAAyB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,QAAM,UAAU,IAAI,aAAa,EAAE,aAAa;AAChD,MAAI,SAAS,QAAQ;AAGrB,QAAM,UAAU,kBAAkB;AAAA,IAChC;AAAA,IACA,eAAe;AAAA,EAAA,CAChB;AAGD,QAAM,oBAAoB,QAAQ;AAClC,UAAQ,YAAY,CAAC,qBAA6B;AAChD,cAAU;AACV,sBAAkB,gBAAgB;AAAA,EACpC;AAIA,QAAM,qBAAqB,CAAC,WAAuB;AAGjD,YAAQ,cAAc,MAAM;AAAA,EAC9B;AAEA,QAAM,qBAAyC;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,YAAY,EAAE,SAAA;AAAA,EAAS;AAG7B,QAAM,gBAAgB,eAAe,OAAO;AAAA,IAC1C,SAAS;AAAA,IACT;AAAA,EAAA,CACD;AACD,UAAQ,UAAU,cAAc,MAAM;AAEtC,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,EAAA;AAKrB,QAAM,sBAAsB;AAAA,IAC1B,SAAS;AAAA,EAAA;AAIX,QAAM,kBAAkB,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,MACf,GAAG;AAAA,MACH;AAAA;AAAA,IAAA;AAAA;AAAA,IAGF,kBAAkB,aACd,YAAY;AACV,UAAI,oBAAoB,SAAS;AAC/B,cAAM,oBAAoB,QAAA;AAAA,MAC5B;AAAA,IACF,IACA;AAAA,EAAA,CACL;AAGD,QAAM,gBAAgB,oBAAoB;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAGD,sBAAoB,UAAU,cAAc;AAG5C,YAAU,YAAY,QAAQ,OAAO;AAGrC,gBAAc,MAAA;AAGd,SAAO;AAAA,IACL,SAAS;AAAA,IACT,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;"}
|
|
@@ -66,6 +66,7 @@ export interface BaseFeedConfig<TData, TVariables = any> {
|
|
|
66
66
|
enableCategoryFallback?: boolean;
|
|
67
67
|
isMediaTrained?: boolean | null;
|
|
68
68
|
categories?: string[];
|
|
69
|
+
ids?: string[];
|
|
69
70
|
entriesToRemove?: Array<string | number>;
|
|
70
71
|
fetchStrategy: FetchStrategy<TData, TVariables>;
|
|
71
72
|
displayStrategy: DisplayStrategy<TData>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../source/factory/core/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../source/factory/core/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,9 +3,10 @@ export interface BaseProps {
|
|
|
3
3
|
numberOfRowsToStart: number;
|
|
4
4
|
categories?: string[];
|
|
5
5
|
isThemeDark?: boolean;
|
|
6
|
-
isMediaTrained?: boolean;
|
|
7
6
|
isLazyLoad: boolean;
|
|
8
7
|
entriesToRemove?: string[];
|
|
8
|
+
ids?: string[];
|
|
9
|
+
isMediaTrained?: boolean;
|
|
9
10
|
styleCallback?: (cssString: string) => void;
|
|
10
11
|
}
|
|
11
12
|
export interface GridProps extends BaseProps {
|
|
@@ -17,7 +18,7 @@ export interface GridProps extends BaseProps {
|
|
|
17
18
|
export interface ListProps extends BaseProps {
|
|
18
19
|
cardType?: 'list' | 'tabular';
|
|
19
20
|
}
|
|
20
|
-
export interface BioProps extends Omit<BaseProps, 'numberOfRowsToStart' | 'isLazyLoad' | 'categories' | 'entriesToRemove' | 'isMediaTrained'> {
|
|
21
|
-
expertId
|
|
21
|
+
export interface BioProps extends Omit<BaseProps, 'numberOfRowsToStart' | 'isLazyLoad' | 'categories' | 'entriesToRemove' | 'ids' | 'isMediaTrained'> {
|
|
22
|
+
expertId?: string;
|
|
22
23
|
}
|
|
23
24
|
//# sourceMappingURL=_types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../../../source/feeds/experts/_types.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IAExB,KAAK,EAAE,MAAM,CAAC;IAEd,mBAAmB,EAAE,MAAM,CAAC;IAE5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,
|
|
1
|
+
{"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../../../source/feeds/experts/_types.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IAExB,KAAK,EAAE,MAAM,CAAC;IAEd,mBAAmB,EAAE,MAAM,CAAC;IAE5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAEf,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAOD,MAAM,WAAW,SAAU,SAAQ,SAAS;IAE1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC;AAOD,MAAM,WAAW,SAAU,SAAQ,SAAS;IAE1C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAOD,MAAM,WAAW,QACf,SAAQ,IAAI,CACV,SAAS,EACP,qBAAqB,GACrB,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,KAAK,GACL,gBAAgB,CACnB;IAED,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bio.d.ts","sourceRoot":"","sources":["../../../source/feeds/experts/bio.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AA8MjD,eAAO,MAAM,UAAU,GAAI,OAAO,QAAQ,KAAG,
|
|
1
|
+
{"version":3,"file":"bio.d.ts","sourceRoot":"","sources":["../../../source/feeds/experts/bio.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AA8MjD,eAAO,MAAM,UAAU,GAAI,OAAO,QAAQ,KAAG,YAqE5C,CAAC"}
|
|
@@ -12,7 +12,7 @@ const createFetchProps = (token, expertId) => ({
|
|
|
12
12
|
token,
|
|
13
13
|
limit: 1,
|
|
14
14
|
offset: 0,
|
|
15
|
-
|
|
15
|
+
ids: [expertId]
|
|
16
16
|
});
|
|
17
17
|
const createSuccessAnnouncer = (expert) => {
|
|
18
18
|
const fullName = buildFullName(expert);
|
|
@@ -110,23 +110,33 @@ const expertsBio = (props) => {
|
|
|
110
110
|
"expert-bio-feed"
|
|
111
111
|
);
|
|
112
112
|
const container = containerBuilder.getElement();
|
|
113
|
+
if (!expertId) {
|
|
114
|
+
logError("invalid_request", "unknown");
|
|
115
|
+
const model2 = containerBuilder.build();
|
|
116
|
+
return {
|
|
117
|
+
element: model2.element,
|
|
118
|
+
styles: "",
|
|
119
|
+
events: {}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const validExpertId = expertId;
|
|
113
123
|
const loading = new LoadingState({ isThemeDark });
|
|
114
124
|
const state = new BioFeedState(loading.styles);
|
|
115
125
|
const initialize = async () => {
|
|
116
126
|
loading.show(container);
|
|
117
127
|
try {
|
|
118
|
-
const fetchProps = createFetchProps(token,
|
|
128
|
+
const fetchProps = createFetchProps(token, validExpertId);
|
|
119
129
|
const variables = expertsFetchStrategy.composeApiVariables(fetchProps);
|
|
120
130
|
const entries = await expertsFetchStrategy.fetchEntries(variables);
|
|
121
131
|
loading.hide();
|
|
122
132
|
if (!entries || entries.length === 0) {
|
|
123
|
-
logError("not_found",
|
|
133
|
+
logError("not_found", validExpertId);
|
|
124
134
|
return;
|
|
125
135
|
}
|
|
126
136
|
await renderSuccess(container, entries[0], state, isThemeDark);
|
|
127
137
|
} catch (error) {
|
|
128
138
|
loading.hide();
|
|
129
|
-
logError("graphql_error",
|
|
139
|
+
logError("graphql_error", validExpertId, error);
|
|
130
140
|
}
|
|
131
141
|
};
|
|
132
142
|
initialize();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bio.js","sources":["../../../source/feeds/experts/bio.ts"],"sourcesContent":["/**\n * Expert Bio Feed (Refactored with Element Builder)\n *\n * Displays a single expert's profile with summary.\n * Uses element builder pattern for clean, declarative construction.\n *\n * @module feeds/experts/bio\n */\n\nimport { ElementBuilder } from '@universityofmaryland/web-builder-library';\nimport { person } from '@universityofmaryland/web-elements-library/composite';\nimport { LoadingState, Announcer } from '../../states';\nimport { expertsFetchStrategy } from '../../strategies/fetch/experts';\nimport {\n mapExpertToBioProps,\n buildFullName,\n} from '../../strategies/display/experts';\nimport { styles as styleUtilities } from '../../helpers';\nimport { type ExpertEntry } from 'types/data';\nimport { type BioProps } from './_types';\nimport { type ElementModel } from '../../_types';\n\n// ============================================================================\n// PURE HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Create base props for fetch strategy\n *\n * @param token - API authentication token\n * @param expertId - Expert ID to fetch\n * @returns Base props object for strategy's composeApiVariables\n */\nconst createFetchProps = (token: string, expertId: string) => ({\n token,\n limit: 1,\n offset: 0,\n id: expertId,\n});\n\n/**\n * Create accessibility announcer for loaded expert\n *\n * @param expert - Expert entry\n * @returns Announcer element model\n */\nconst createSuccessAnnouncer = (expert: ExpertEntry): Announcer => {\n const fullName = buildFullName(expert);\n const message = `Loaded profile for ${fullName}`;\n return new Announcer({ message });\n};\n\n// ============================================================================\n// STATE MANAGER CLASS\n// ============================================================================\n\n/**\n * Manages bio feed state and shadow DOM synchronization\n *\n * Encapsulates style accumulation and shadow DOM updates.\n * Provides immutable-style API for adding styles.\n */\nclass BioFeedState {\n private stylesArray: string[] = [];\n private shadowRoot: ShadowRoot | null = null;\n\n /**\n * Initialize state with initial styles\n *\n * @param initialStyles - Initial CSS styles\n */\n constructor(initialStyles: string) {\n this.stylesArray.push(initialStyles);\n }\n\n /**\n * Add styles to the accumulated styles\n *\n * @param styles - CSS styles to add\n */\n addStyles(styles: string): void {\n this.stylesArray.push(styles);\n }\n\n /**\n * Set shadow root reference for style updates\n *\n * @param shadow - Shadow root element\n */\n setShadowRoot(shadow: ShadowRoot): void {\n this.shadowRoot = shadow;\n }\n\n /**\n * Update shadow DOM styles\n *\n * @returns Promise that resolves when styles are updated\n */\n async updateShadowStyles(): Promise<void> {\n if (!this.shadowRoot) return;\n await styleUtilities.setShadowStyles({\n shadowRoot: this.shadowRoot,\n styles: this.getStyles(),\n });\n }\n\n /**\n * Get accumulated styles as single string\n *\n * @returns Combined CSS styles\n */\n getStyles(): string {\n return this.stylesArray.join('\\n');\n }\n\n /**\n * Get shadow root callback for events\n *\n * @returns Callback function for shadow root\n */\n getShadowCallback(): (shadow: ShadowRoot) => void {\n return (shadow) => this.setShadowRoot(shadow);\n }\n}\n\n// ============================================================================\n// RENDERING FUNCTIONS\n// ============================================================================\n\n/**\n * Render successful bio with expert data\n *\n * @param container - Container element to render into\n * @param expert - Expert entry data\n * @param state - State manager instance\n * @param isThemeDark - Dark theme flag\n * @returns Promise that resolves when rendering is complete\n */\nconst renderSuccess = async (\n container: HTMLElement,\n expert: ExpertEntry,\n state: BioFeedState,\n isThemeDark: boolean,\n): Promise<void> => {\n const bioProps = mapExpertToBioProps(expert, 'small', isThemeDark);\n const bioElement = person.bio.small(bioProps);\n const announcer = createSuccessAnnouncer(expert);\n const children = [bioElement.element, announcer.getElement()];\n\n children.forEach((child) => container.appendChild(child));\n\n state.addStyles(bioElement.styles);\n await state.updateShadowStyles();\n};\n\n/**\n * Error types for expert bio feed\n */\ntype BioErrorType = 'not_found' | 'graphql_error' | 'invalid_request';\n\n/**\n * Log error to console with specific warning message\n *\n * @param errorType - Type of error that occurred\n * @param expertId - Expert ID that was requested\n * @param error - Optional error object for GraphQL errors\n */\nconst logError = (\n errorType: BioErrorType,\n expertId: string,\n error?: any,\n): void => {\n switch (errorType) {\n case 'not_found':\n console.warn(\n `[Expert Bio Feed] No expert found with ID \"${expertId}\". ` +\n `Please verify the expert ID is correct and the expert exists in the system.`,\n );\n break;\n case 'graphql_error':\n console.warn(\n `[Expert Bio Feed] GraphQL error occurred while fetching expert \"${expertId}\". ` +\n `Check network connection and API availability.`,\n error,\n );\n break;\n case 'invalid_request':\n console.warn(\n `[Expert Bio Feed] Invalid request for expert \"${expertId}\". ` +\n `Ensure both data-token and data-id attributes are provided.`,\n );\n break;\n }\n};\n\n// ============================================================================\n// MAIN EXPORT\n// ============================================================================\n\n/**\n * Create an expert bio feed\n *\n * Fetches and displays a single expert's bio with summary.\n * Uses element builder pattern for clean construction.\n *\n * @param props - Feed configuration\n * @returns ElementModel with bio element, styles, and events\n *\n * @example\n * ```typescript\n * const bio = expertBio({\n * token: 'my-token',\n * expertId: 'john-doe',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With dark theme\n * const bio = expertBio({\n * token: 'my-token',\n * expertId: 'jane-smith',\n * isThemeDark: true,\n * });\n * ```\n */\nexport const expertsBio = (props: BioProps): ElementModel => {\n const { token, expertId, isThemeDark = false } = props;\n\n // Create container using ElementBuilder\n const containerBuilder = new ElementBuilder('div').withClassName(\n 'expert-bio-feed',\n );\n\n // Get element for manipulation (non-destructive)\n const container = containerBuilder.getElement();\n\n // Initialize state management\n const loading = new LoadingState({ isThemeDark });\n const state = new BioFeedState(loading.styles);\n\n /**\n * Fetch expert data and render\n */\n const initialize = async (): Promise<void> => {\n loading.show(container);\n\n try {\n const fetchProps = createFetchProps(token, expertId);\n const variables = expertsFetchStrategy.composeApiVariables(fetchProps);\n const entries = await expertsFetchStrategy.fetchEntries(variables);\n\n loading.hide();\n\n if (!entries || entries.length === 0) {\n logError('not_found', expertId);\n return;\n }\n\n await renderSuccess(container, entries[0], state, isThemeDark);\n } catch (error) {\n loading.hide();\n logError('graphql_error', expertId, error);\n }\n };\n\n // Start initialization\n initialize();\n\n // Build and return element model\n const model = containerBuilder.build();\n\n return {\n element: model.element,\n get styles() {\n return state.getStyles();\n },\n events: {\n callback: state.getShadowCallback(),\n },\n };\n};\n"],"names":["styleUtilities.setShadowStyles"],"mappings":";;;;;;;;;;AAiCA,MAAM,mBAAmB,CAAC,OAAe,cAAsB;AAAA,EAC7D;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,IAAI;AACN;AAQA,MAAM,yBAAyB,CAAC,WAAmC;AACjE,QAAM,WAAW,cAAc,MAAM;AACrC,QAAM,UAAU,sBAAsB,QAAQ;AAC9C,SAAO,IAAI,UAAU,EAAE,SAAS;AAClC;AAYA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjB,YAAY,eAAuB;AARnC,SAAQ,cAAwB,CAAA;AAChC,SAAQ,aAAgC;AAQtC,SAAK,YAAY,KAAK,aAAa;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAsB;AAC9B,SAAK,YAAY,KAAK,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,QAA0B;AACtC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,qBAAoC;AACxC,QAAI,CAAC,KAAK,WAAY;AACtB,UAAMA,gBAA+B;AAAA,MACnC,YAAY,KAAK;AAAA,MACjB,QAAQ,KAAK,UAAA;AAAA,IAAU,CACxB;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAoB;AAClB,WAAO,KAAK,YAAY,KAAK,IAAI;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAkD;AAChD,WAAO,CAAC,WAAW,KAAK,cAAc,MAAM;AAAA,EAC9C;AACF;AAeA,MAAM,gBAAgB,OACpB,WACA,QACA,OACA,gBACkB;AAClB,QAAM,WAAW,oBAAoB,QAAQ,SAAS,WAAW;AACjE,QAAM,aAAa,OAAO,IAAI,MAAM,QAAQ;AAC5C,QAAM,YAAY,uBAAuB,MAAM;AAC/C,QAAM,WAAW,CAAC,WAAW,SAAS,UAAU,YAAY;AAE5D,WAAS,QAAQ,CAAC,UAAU,UAAU,YAAY,KAAK,CAAC;AAExD,QAAM,UAAU,WAAW,MAAM;AACjC,QAAM,MAAM,mBAAA;AACd;AAcA,MAAM,WAAW,CACf,WACA,UACA,UACS;AACT,UAAQ,WAAA;AAAA,IACN,KAAK;AACH,cAAQ;AAAA,QACN,8CAA8C,QAAQ;AAAA,MAAA;AAGxD;AAAA,IACF,KAAK;AACH,cAAQ;AAAA,QACN,mEAAmE,QAAQ;AAAA,QAE3E;AAAA,MAAA;AAEF;AAAA,IACF,KAAK;AACH,cAAQ;AAAA,QACN,iDAAiD,QAAQ;AAAA,MAAA;AAG3D;AAAA,EAAA;AAEN;AAiCO,MAAM,aAAa,CAAC,UAAkC;AAC3D,QAAM,EAAE,OAAO,UAAU,cAAc,UAAU;AAGjD,QAAM,mBAAmB,IAAI,eAAe,KAAK,EAAE;AAAA,IACjD;AAAA,EAAA;AAIF,QAAM,YAAY,iBAAiB,WAAA;AAGnC,QAAM,UAAU,IAAI,aAAa,EAAE,aAAa;AAChD,QAAM,QAAQ,IAAI,aAAa,QAAQ,MAAM;AAK7C,QAAM,aAAa,YAA2B;AAC5C,YAAQ,KAAK,SAAS;AAEtB,QAAI;AACF,YAAM,aAAa,iBAAiB,OAAO,QAAQ;AACnD,YAAM,YAAY,qBAAqB,oBAAoB,UAAU;AACrE,YAAM,UAAU,MAAM,qBAAqB,aAAa,SAAS;AAEjE,cAAQ,KAAA;AAER,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,iBAAS,aAAa,QAAQ;AAC9B;AAAA,MACF;AAEA,YAAM,cAAc,WAAW,QAAQ,CAAC,GAAG,OAAO,WAAW;AAAA,IAC/D,SAAS,OAAO;AACd,cAAQ,KAAA;AACR,eAAS,iBAAiB,UAAU,KAAK;AAAA,IAC3C;AAAA,EACF;AAGA,aAAA;AAGA,QAAM,QAAQ,iBAAiB,MAAA;AAE/B,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,IAAI,SAAS;AACX,aAAO,MAAM,UAAA;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,MACN,UAAU,MAAM,kBAAA;AAAA,IAAkB;AAAA,EACpC;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"bio.js","sources":["../../../source/feeds/experts/bio.ts"],"sourcesContent":["/**\n * Expert Bio Feed (Refactored with Element Builder)\n *\n * Displays a single expert's profile with summary.\n * Uses element builder pattern for clean, declarative construction.\n *\n * @module feeds/experts/bio\n */\n\nimport { ElementBuilder } from '@universityofmaryland/web-builder-library';\nimport { person } from '@universityofmaryland/web-elements-library/composite';\nimport { LoadingState, Announcer } from '../../states';\nimport { expertsFetchStrategy } from '../../strategies/fetch/experts';\nimport {\n mapExpertToBioProps,\n buildFullName,\n} from '../../strategies/display/experts';\nimport { styles as styleUtilities } from '../../helpers';\nimport { type ExpertEntry } from 'types/data';\nimport { type BioProps } from './_types';\nimport { type ElementModel } from '../../_types';\n\n// ============================================================================\n// PURE HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Create base props for fetch strategy\n *\n * @param token - API authentication token\n * @param expertId - Expert ID to fetch\n * @returns Base props object for strategy's composeApiVariables\n */\nconst createFetchProps = (token: string, expertId: string) => ({\n token,\n limit: 1,\n offset: 0,\n ids: [expertId],\n});\n\n/**\n * Create accessibility announcer for loaded expert\n *\n * @param expert - Expert entry\n * @returns Announcer element model\n */\nconst createSuccessAnnouncer = (expert: ExpertEntry): Announcer => {\n const fullName = buildFullName(expert);\n const message = `Loaded profile for ${fullName}`;\n return new Announcer({ message });\n};\n\n// ============================================================================\n// STATE MANAGER CLASS\n// ============================================================================\n\n/**\n * Manages bio feed state and shadow DOM synchronization\n *\n * Encapsulates style accumulation and shadow DOM updates.\n * Provides immutable-style API for adding styles.\n */\nclass BioFeedState {\n private stylesArray: string[] = [];\n private shadowRoot: ShadowRoot | null = null;\n\n /**\n * Initialize state with initial styles\n *\n * @param initialStyles - Initial CSS styles\n */\n constructor(initialStyles: string) {\n this.stylesArray.push(initialStyles);\n }\n\n /**\n * Add styles to the accumulated styles\n *\n * @param styles - CSS styles to add\n */\n addStyles(styles: string): void {\n this.stylesArray.push(styles);\n }\n\n /**\n * Set shadow root reference for style updates\n *\n * @param shadow - Shadow root element\n */\n setShadowRoot(shadow: ShadowRoot): void {\n this.shadowRoot = shadow;\n }\n\n /**\n * Update shadow DOM styles\n *\n * @returns Promise that resolves when styles are updated\n */\n async updateShadowStyles(): Promise<void> {\n if (!this.shadowRoot) return;\n await styleUtilities.setShadowStyles({\n shadowRoot: this.shadowRoot,\n styles: this.getStyles(),\n });\n }\n\n /**\n * Get accumulated styles as single string\n *\n * @returns Combined CSS styles\n */\n getStyles(): string {\n return this.stylesArray.join('\\n');\n }\n\n /**\n * Get shadow root callback for events\n *\n * @returns Callback function for shadow root\n */\n getShadowCallback(): (shadow: ShadowRoot) => void {\n return (shadow) => this.setShadowRoot(shadow);\n }\n}\n\n// ============================================================================\n// RENDERING FUNCTIONS\n// ============================================================================\n\n/**\n * Render successful bio with expert data\n *\n * @param container - Container element to render into\n * @param expert - Expert entry data\n * @param state - State manager instance\n * @param isThemeDark - Dark theme flag\n * @returns Promise that resolves when rendering is complete\n */\nconst renderSuccess = async (\n container: HTMLElement,\n expert: ExpertEntry,\n state: BioFeedState,\n isThemeDark: boolean,\n): Promise<void> => {\n const bioProps = mapExpertToBioProps(expert, 'small', isThemeDark);\n const bioElement = person.bio.small(bioProps);\n const announcer = createSuccessAnnouncer(expert);\n const children = [bioElement.element, announcer.getElement()];\n\n children.forEach((child) => container.appendChild(child));\n\n state.addStyles(bioElement.styles);\n await state.updateShadowStyles();\n};\n\n/**\n * Error types for expert bio feed\n */\ntype BioErrorType = 'not_found' | 'graphql_error' | 'invalid_request';\n\n/**\n * Log error to console with specific warning message\n *\n * @param errorType - Type of error that occurred\n * @param expertId - Expert ID that was requested\n * @param error - Optional error object for GraphQL errors\n */\nconst logError = (\n errorType: BioErrorType,\n expertId: string,\n error?: any,\n): void => {\n switch (errorType) {\n case 'not_found':\n console.warn(\n `[Expert Bio Feed] No expert found with ID \"${expertId}\". ` +\n `Please verify the expert ID is correct and the expert exists in the system.`,\n );\n break;\n case 'graphql_error':\n console.warn(\n `[Expert Bio Feed] GraphQL error occurred while fetching expert \"${expertId}\". ` +\n `Check network connection and API availability.`,\n error,\n );\n break;\n case 'invalid_request':\n console.warn(\n `[Expert Bio Feed] Invalid request for expert \"${expertId}\". ` +\n `Ensure both data-token and data-id attributes are provided.`,\n );\n break;\n }\n};\n\n// ============================================================================\n// MAIN EXPORT\n// ============================================================================\n\n/**\n * Create an expert bio feed\n *\n * Fetches and displays a single expert's bio with summary.\n * Uses element builder pattern for clean construction.\n *\n * @param props - Feed configuration\n * @returns ElementModel with bio element, styles, and events\n *\n * @example\n * ```typescript\n * const bio = expertBio({\n * token: 'my-token',\n * expertId: 'john-doe',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With dark theme\n * const bio = expertBio({\n * token: 'my-token',\n * expertId: 'jane-smith',\n * isThemeDark: true,\n * });\n * ```\n */\nexport const expertsBio = (props: BioProps): ElementModel => {\n const { token, expertId, isThemeDark = false } = props;\n\n // Create container using ElementBuilder\n const containerBuilder = new ElementBuilder('div').withClassName(\n 'expert-bio-feed',\n );\n\n // Get element for manipulation (non-destructive)\n const container = containerBuilder.getElement();\n\n // Validate required expertId\n if (!expertId) {\n logError('invalid_request', 'unknown');\n const model = containerBuilder.build();\n return {\n element: model.element,\n styles: '',\n events: {},\n };\n }\n\n // Store validated expertId for use in async function\n const validExpertId = expertId;\n\n // Initialize state management\n const loading = new LoadingState({ isThemeDark });\n const state = new BioFeedState(loading.styles);\n\n /**\n * Fetch expert data and render\n */\n const initialize = async (): Promise<void> => {\n loading.show(container);\n\n try {\n const fetchProps = createFetchProps(token, validExpertId);\n const variables = expertsFetchStrategy.composeApiVariables(fetchProps);\n const entries = await expertsFetchStrategy.fetchEntries(variables);\n\n loading.hide();\n\n if (!entries || entries.length === 0) {\n logError('not_found', validExpertId);\n return;\n }\n\n await renderSuccess(container, entries[0], state, isThemeDark);\n } catch (error) {\n loading.hide();\n logError('graphql_error', validExpertId, error);\n }\n };\n\n // Start initialization\n initialize();\n\n // Build and return element model\n const model = containerBuilder.build();\n\n return {\n element: model.element,\n get styles() {\n return state.getStyles();\n },\n events: {\n callback: state.getShadowCallback(),\n },\n };\n};\n"],"names":["styleUtilities.setShadowStyles","model"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { DisplayStrategy } from '../../factory/core/types';
|
|
2
2
|
import { ExpertEntry } from '../../types/data';
|
|
3
3
|
export declare const buildFullName: (entry: ExpertEntry) => string;
|
|
4
|
+
export declare const extractAreasOfExpertise: (entry: ExpertEntry) => string | null;
|
|
4
5
|
export declare const mapExpertToBioProps: (entry: ExpertEntry, displayType: "small" | "full", isThemeDark?: boolean) => {
|
|
5
6
|
name: HTMLElement | null;
|
|
6
7
|
pronouns: HTMLElement | null;
|
|
7
|
-
job: HTMLElement | null;
|
|
8
8
|
association: HTMLElement | null;
|
|
9
|
+
subText: HTMLElement | null;
|
|
9
10
|
email: HTMLElement | null;
|
|
10
11
|
linkedin: HTMLElement | null;
|
|
11
12
|
phone: null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/strategies/display/experts.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,eAAe,EAAsB,MAAM,0BAA0B,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAqFzC,eAAO,MAAM,aAAa,GAAI,OAAO,WAAW,KAAG,MAUlD,CAAC;
|
|
1
|
+
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/strategies/display/experts.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,eAAe,EAAsB,MAAM,0BAA0B,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAqFzC,eAAO,MAAM,aAAa,GAAI,OAAO,WAAW,KAAG,MAUlD,CAAC;AA4HF,eAAO,MAAM,uBAAuB,GAAI,OAAO,WAAW,KAAG,MAAM,GAAG,IAQrE,CAAC;AAgVF,eAAO,MAAM,mBAAmB,GAC9B,OAAO,WAAW,EAClB,aAAa,OAAO,GAAG,MAAM,EAC7B,cAAa,OAAe;;;;;;;;;;;;;CAqC7B,CAAC;AAqBF,eAAO,MAAM,sBAAsB,EAAE,eAAe,CAAC,WAAW,CAwB/D,CAAC"}
|
|
@@ -68,6 +68,13 @@ const extractDescription = (entry, displayType) => {
|
|
|
68
68
|
const extractPronouns = (entry) => {
|
|
69
69
|
return entry.pronouns || null;
|
|
70
70
|
};
|
|
71
|
+
const extractAreasOfExpertise = (entry) => {
|
|
72
|
+
const areas = entry.areasOfExpertise;
|
|
73
|
+
if (!areas || areas.length === 0) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return areas.map((area) => area.title).join(", ");
|
|
77
|
+
};
|
|
71
78
|
const createNameElement = (fullName, url, containerTag) => {
|
|
72
79
|
return createTextWithLink({
|
|
73
80
|
text: fullName,
|
|
@@ -106,6 +113,10 @@ const createPronounsElement = (pronouns) => {
|
|
|
106
113
|
if (!pronouns) return null;
|
|
107
114
|
return createTextContainer({ text: pronouns });
|
|
108
115
|
};
|
|
116
|
+
const createExpertiseElement = (expertise) => {
|
|
117
|
+
if (!expertise) return null;
|
|
118
|
+
return createTextContainer({ text: expertise });
|
|
119
|
+
};
|
|
109
120
|
const createContactElements = (contactData) => {
|
|
110
121
|
return CONTACT_CONFIGS.reduce(
|
|
111
122
|
(elements, config) => {
|
|
@@ -129,16 +140,22 @@ const createBlockCardProps = (entry, options) => {
|
|
|
129
140
|
const association = extractPrimaryAssociation(entry);
|
|
130
141
|
const imageData = extractImageData(entry, fullName);
|
|
131
142
|
const pronouns = extractPronouns(entry);
|
|
143
|
+
const expertise = extractAreasOfExpertise(entry);
|
|
132
144
|
const name = createNameElement(fullName, profileUrl);
|
|
133
|
-
|
|
145
|
+
createJobElement(jobTitle);
|
|
134
146
|
const associationElement = createAssociationElement(association);
|
|
135
|
-
const image = createImageElement(
|
|
147
|
+
const image = createImageElement(
|
|
148
|
+
imageData,
|
|
149
|
+
profileUrl,
|
|
150
|
+
`View profile for ${fullName}`
|
|
151
|
+
);
|
|
136
152
|
const pronounsElement = createPronounsElement(pronouns);
|
|
153
|
+
const expertiseElement = createExpertiseElement(expertise);
|
|
137
154
|
return person.block({
|
|
138
155
|
name,
|
|
139
156
|
pronouns: pronounsElement,
|
|
140
|
-
job,
|
|
141
157
|
association: associationElement,
|
|
158
|
+
subText: expertiseElement,
|
|
142
159
|
image,
|
|
143
160
|
isThemeDark
|
|
144
161
|
});
|
|
@@ -151,16 +168,22 @@ const createListCardProps = (entry, options) => {
|
|
|
151
168
|
const association = extractPrimaryAssociation(entry);
|
|
152
169
|
const imageData = extractImageData(entry, fullName);
|
|
153
170
|
const pronouns = extractPronouns(entry);
|
|
171
|
+
const expertise = extractAreasOfExpertise(entry);
|
|
154
172
|
const name = createNameElement(fullName, profileUrl);
|
|
155
|
-
|
|
173
|
+
createJobElement(jobTitle);
|
|
156
174
|
const associationElement = createAssociationElement(association);
|
|
157
|
-
const image = createImageElement(
|
|
175
|
+
const image = createImageElement(
|
|
176
|
+
imageData,
|
|
177
|
+
profileUrl,
|
|
178
|
+
`View profile for ${fullName}`
|
|
179
|
+
);
|
|
158
180
|
const pronounsElement = createPronounsElement(pronouns);
|
|
181
|
+
const expertiseElement = createExpertiseElement(expertise);
|
|
159
182
|
return person.list({
|
|
160
183
|
name,
|
|
161
184
|
pronouns: pronounsElement,
|
|
162
|
-
job,
|
|
163
185
|
association: associationElement,
|
|
186
|
+
subText: expertiseElement,
|
|
164
187
|
image,
|
|
165
188
|
isThemeDark
|
|
166
189
|
});
|
|
@@ -174,17 +197,23 @@ const createTabularCardProps = (entry, options) => {
|
|
|
174
197
|
const imageData = extractImageData(entry, fullName);
|
|
175
198
|
const contactData = extractContactData(entry);
|
|
176
199
|
const pronouns = extractPronouns(entry);
|
|
200
|
+
const expertise = extractAreasOfExpertise(entry);
|
|
177
201
|
const name = createNameElement(fullName, profileUrl);
|
|
178
|
-
|
|
202
|
+
createJobElement(jobTitle);
|
|
179
203
|
const associationElement = createAssociationElement(association);
|
|
180
|
-
const image = createImageElement(
|
|
204
|
+
const image = createImageElement(
|
|
205
|
+
imageData,
|
|
206
|
+
profileUrl,
|
|
207
|
+
`View profile for ${fullName}`
|
|
208
|
+
);
|
|
181
209
|
const contactElements = createContactElements(contactData);
|
|
182
210
|
const pronounsElement = createPronounsElement(pronouns);
|
|
211
|
+
const expertiseElement = createExpertiseElement(expertise);
|
|
183
212
|
return person.tabular({
|
|
184
213
|
name,
|
|
185
214
|
pronouns: pronounsElement,
|
|
186
|
-
job,
|
|
187
215
|
association: associationElement,
|
|
216
|
+
subText: expertiseElement,
|
|
188
217
|
image,
|
|
189
218
|
...contactElements,
|
|
190
219
|
isThemeDark
|
|
@@ -219,18 +248,20 @@ const mapExpertToBioProps = (entry, displayType, isThemeDark = false) => {
|
|
|
219
248
|
const contactData = extractContactData(entry);
|
|
220
249
|
const description = extractDescription(entry);
|
|
221
250
|
const pronouns = extractPronouns(entry);
|
|
251
|
+
const expertise = extractAreasOfExpertise(entry);
|
|
222
252
|
const name = createNameElement(fullName, profileUrl, "h1");
|
|
223
|
-
|
|
253
|
+
createJobElement(jobTitle);
|
|
224
254
|
const associationElement = createAssociationElement(association);
|
|
225
255
|
const image = createImageElement(imageData);
|
|
226
256
|
const descriptionElement = createDescriptionElement(description);
|
|
227
257
|
const contactElements = createContactElements(contactData);
|
|
228
258
|
const pronounsElement = createPronounsElement(pronouns);
|
|
259
|
+
const expertiseElement = createExpertiseElement(expertise);
|
|
229
260
|
return {
|
|
230
261
|
name,
|
|
231
262
|
pronouns: pronounsElement,
|
|
232
|
-
job,
|
|
233
263
|
association: associationElement,
|
|
264
|
+
subText: expertiseElement,
|
|
234
265
|
email: contactElements.email || null,
|
|
235
266
|
linkedin: contactElements.linkedin || null,
|
|
236
267
|
phone: null,
|
|
@@ -261,6 +292,7 @@ const expertsDisplayStrategy = {
|
|
|
261
292
|
export {
|
|
262
293
|
buildFullName,
|
|
263
294
|
expertsDisplayStrategy,
|
|
295
|
+
extractAreasOfExpertise,
|
|
264
296
|
mapExpertToBioProps
|
|
265
297
|
};
|
|
266
298
|
//# sourceMappingURL=experts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experts.js","sources":["../../../source/strategies/display/experts.ts"],"sourcesContent":["/**\n * Experts Display Strategy\n *\n * Strategy for displaying expert entries using person or card elements.\n * Uses a functional composition approach with pure data extraction,\n * element creation, and card composition functions.\n *\n * @module strategies/display/experts\n */\n\nimport {\n person,\n card,\n} from '@universityofmaryland/web-elements-library/composite';\nimport {\n createTextWithLink,\n createTextContainer,\n createImageOrLinkedImage,\n} from '@universityofmaryland/web-utilities-library/elements';\nimport { DisplayStrategy, CardMappingOptions } from '../../factory/core/types';\nimport { ElementModel } from '../../_types';\nimport { ExpertEntry } from 'types/data';\n\n// ============================================================================\n// TYPE DEFINITIONS\n// ============================================================================\n\n/**\n * Contact field configuration\n */\ninterface ContactConfig {\n key: keyof Pick<ExpertEntry, 'email' | 'website' | 'linkedin' | 'twitter'>;\n label: (value: string) => string;\n url: (value: string) => string;\n}\n\n/**\n * Extracted association data\n */\ninterface AssociationData {\n title: string;\n url?: string | null;\n}\n\n/**\n * Extracted image data\n */\ninterface ImageData {\n url: string;\n altText: string;\n}\n\n/**\n * Extracted contact data\n */\ninterface ContactData {\n email?: string | null;\n website?: string | null;\n linkedin?: string | null;\n twitter?: string | null;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\n/**\n * Contact field configuration\n *\n * Defines how each contact type should be rendered as a link.\n */\nconst CONTACT_CONFIGS: ContactConfig[] = [\n {\n key: 'email',\n label: () => 'Email',\n url: (value) => `mailto:${value}`,\n },\n {\n key: 'website',\n label: (value) => value,\n url: (value) => value,\n },\n {\n key: 'linkedin',\n label: (value) => value,\n url: (value) => value,\n },\n {\n key: 'twitter',\n label: (value) => value,\n url: (value) => value,\n },\n];\n\n// ============================================================================\n// PURE DATA EXTRACTION FUNCTIONS\n// ============================================================================\n\n/**\n * Build full name from expert entry\n *\n * Combines prefix (optional), first, middle (optional), last name, and suffix (optional).\n *\n * @param entry - Expert entry\n * @returns Full name string\n */\nexport const buildFullName = (entry: ExpertEntry): string => {\n const parts = [\n entry.prefix,\n entry.firstName,\n entry.middleName,\n entry.lastName,\n entry.suffix,\n ].filter(Boolean);\n\n return parts.join(' ');\n};\n\n/**\n * Build expert profile URL\n *\n * @param entry - Expert entry\n * @returns Profile URL string\n */\nconst buildProfileUrl = (entry: ExpertEntry): string => {\n return `https://umdrightnow.umd.edu/expert/${entry.slug}`;\n};\n\n/**\n * Extract primary job title\n *\n * @param entry - Expert entry\n * @returns Job title string or null\n */\nconst extractPrimaryJobTitle = (entry: ExpertEntry): string | null => {\n return entry.organizations?.[0]?.jobs?.[0]?.title || null;\n};\n\n/**\n * Extract primary association (campus unit)\n *\n * @param entry - Expert entry\n * @returns Association data or null\n */\nconst extractPrimaryAssociation = (\n entry: ExpertEntry,\n): AssociationData | null => {\n const campusUnit = entry.organizations?.[0]?.jobs?.[0]?.campusUnits?.[0];\n if (!campusUnit) return null;\n\n return {\n title: campusUnit.title,\n url: campusUnit.link?.url,\n };\n};\n\n/**\n * Extract image data from headshot\n *\n * @param entry - Expert entry\n * @param fullName - Full name for alt text\n * @returns Image data or null\n */\nconst extractImageData = (\n entry: ExpertEntry,\n fullName: string,\n): ImageData | null => {\n const headshotUrl = entry.headshot?.[0]?.url;\n if (!headshotUrl) return null;\n\n return {\n url: headshotUrl,\n altText: fullName,\n };\n};\n\n/**\n * Extract contact data\n *\n * @param entry - Expert entry\n * @returns Contact data object\n */\nconst extractContactData = (entry: ExpertEntry): ContactData => {\n return {\n email: entry.email || null,\n website: entry.website || null,\n linkedin: entry.linkedin || null,\n twitter: entry.twitter || null,\n };\n};\n\n/**\n * Extract description based on display type\n *\n * @param entry - Expert entry\n * @param displayType - 'small' for summary, 'full' for biography\n * @returns HTML description string or null\n */\nconst extractDescription = (\n entry: ExpertEntry,\n displayType: 'small' | 'full',\n): string | null => {\n if (displayType === 'full') {\n return entry.bio?.html || null;\n }\n return entry.summary?.html || null;\n};\n\n/**\n * Extract pronouns\n *\n * @param entry - Expert entry\n * @returns Pronouns string or null\n */\nconst extractPronouns = (entry: ExpertEntry): string | null => {\n return entry.pronouns || null;\n};\n\n// ============================================================================\n// ELEMENT CREATION FUNCTIONS\n// ============================================================================\n\n/**\n * Create name element with link\n *\n * @param fullName - Full name text\n * @param url - Profile URL\n * @param containerTag - HTML tag for container (default: undefined)\n * @returns Name element or null\n */\nconst createNameElement = (\n fullName: string,\n url: string,\n containerTag?: 'h1' | 'h2' | 'h3',\n): HTMLElement | null => {\n return createTextWithLink({\n text: fullName,\n url,\n containerTag,\n });\n};\n\n/**\n * Create job title element\n *\n * @param jobTitle - Job title text\n * @returns Job element or null\n */\nconst createJobElement = (jobTitle: string | null): HTMLElement | null => {\n if (!jobTitle) return null;\n return createTextContainer({ text: jobTitle });\n};\n\n/**\n * Create association element (with optional link)\n *\n * @param association - Association data\n * @returns Association element or null\n */\nconst createAssociationElement = (\n association: AssociationData | null,\n): HTMLElement | null => {\n if (!association) return null;\n\n if (association.url) {\n return createTextWithLink({\n text: association.title,\n url: association.url,\n });\n }\n\n return createTextContainer({ text: association.title });\n};\n\n/**\n * Create image element (with optional link)\n *\n * @param imageData - Image data\n * @param linkUrl - Optional link URL\n * @param linkLabel - Optional link label\n * @returns Image element or null\n */\nconst createImageElement = (\n imageData: ImageData | null,\n linkUrl?: string,\n linkLabel?: string,\n): HTMLImageElement | HTMLAnchorElement | null => {\n if (!imageData) return null;\n\n return createImageOrLinkedImage({\n imageUrl: imageData.url,\n altText: imageData.altText,\n linkUrl,\n linkLabel,\n });\n};\n\n/**\n * Create description element\n *\n * @param description - HTML description text\n * @returns Description element or null\n */\nconst createDescriptionElement = (\n description: string | null,\n): HTMLElement | null => {\n if (!description) return null;\n return createTextContainer({ text: description, allowHTML: true });\n};\n\n/**\n * Create pronouns element\n *\n * @param pronouns - Pronouns text\n * @returns Pronouns element or null\n */\nconst createPronounsElement = (pronouns: string | null): HTMLElement | null => {\n if (!pronouns) return null;\n return createTextContainer({ text: pronouns });\n};\n\n/**\n * Create contact elements from contact data\n *\n * @param contactData - Contact data object\n * @returns Object with contact elements keyed by contact type\n */\nconst createContactElements = (\n contactData: ContactData,\n): { [key: string]: HTMLElement | null } => {\n return CONTACT_CONFIGS.reduce<{ [key: string]: HTMLElement | null }>(\n (elements, config) => {\n const value = contactData[config.key];\n if (!value) return elements;\n\n const element = createTextWithLink({\n text: config.label(value),\n url: config.url(value),\n });\n\n elements[config.key] = element;\n return elements;\n },\n {},\n );\n};\n\n// ============================================================================\n// CARD COMPOSITION FUNCTIONS\n// ============================================================================\n\n/**\n * Create props for person block card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person block card element\n */\nconst createBlockCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const pronouns = extractPronouns(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(imageData, profileUrl, `View profile for ${fullName}`);\n const pronounsElement = createPronounsElement(pronouns);\n\n return person.block({\n name,\n pronouns: pronounsElement,\n job,\n association: associationElement,\n image,\n isThemeDark,\n });\n};\n\n/**\n * Create props for person list card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person list card element\n */\nconst createListCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const pronouns = extractPronouns(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(imageData, profileUrl, `View profile for ${fullName}`);\n const pronounsElement = createPronounsElement(pronouns);\n\n return person.list({\n name,\n pronouns: pronounsElement,\n job,\n association: associationElement,\n image,\n isThemeDark,\n });\n};\n\n/**\n * Create props for person tabular card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person tabular card element\n */\nconst createTabularCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const contactData = extractContactData(entry);\n const pronouns = extractPronouns(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(imageData, profileUrl, `View profile for ${fullName}`);\n const contactElements = createContactElements(contactData);\n const pronounsElement = createPronounsElement(pronouns);\n\n return person.tabular({\n name,\n pronouns: pronounsElement,\n job,\n association: associationElement,\n image,\n ...contactElements,\n isThemeDark,\n });\n};\n\n/**\n * Create props for overlay card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Overlay card element\n */\nconst createOverlayCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const imageData = extractImageData(entry, fullName);\n\n // Create elements\n const headline = createNameElement(fullName, profileUrl);\n const text = createJobElement(jobTitle);\n const backgroundImage = createImageElement(\n imageData,\n profileUrl,\n `View profile for ${fullName}`,\n );\n\n return card.overlay.image({\n headline,\n text,\n backgroundImage,\n isThemeDark,\n });\n};\n\n/**\n * Map expert entry to PersonBio props\n *\n * Shared helper for creating PersonBio props from expert data.\n * Used by both the display strategy and bio feed for consistency.\n *\n * @param entry - Expert entry from API\n * @param displayType - 'small' for summary, 'full' for biography\n * @param isThemeDark - Dark theme flag\n * @returns Props for person.bio.full() or person.bio.small()\n *\n * @example\n * ```typescript\n * const bioProps = mapExpertToBioProps(expert, 'full', false);\n * const bioElement = person.bio.full(bioProps);\n * ```\n */\nexport const mapExpertToBioProps = (\n entry: ExpertEntry,\n displayType: 'small' | 'full',\n isThemeDark: boolean = false,\n) => {\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const contactData = extractContactData(entry);\n const description = extractDescription(entry, displayType);\n const pronouns = extractPronouns(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl, 'h1');\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(imageData); // No link for bio display\n const descriptionElement = createDescriptionElement(description);\n const contactElements = createContactElements(contactData);\n const pronounsElement = createPronounsElement(pronouns);\n\n return {\n name,\n pronouns: pronounsElement,\n job,\n association: associationElement,\n email: contactElements.email || null,\n linkedin: contactElements.linkedin || null,\n phone: null,\n address: null,\n additionalContact: null,\n image,\n description: descriptionElement,\n isThemeDark,\n };\n};\n\n// ============================================================================\n// DISPLAY STRATEGY\n// ============================================================================\n\n/**\n * Experts display strategy\n *\n * Maps expert entries to person elements for profile display.\n * Optimized for displaying faculty and expert profiles with contact\n * information and skills.\n *\n * @example\n * ```typescript\n * const feed = createBaseFeed({\n * displayStrategy: expertsDisplayStrategy,\n * // ...\n * });\n * ```\n */\nexport const expertsDisplayStrategy: DisplayStrategy<ExpertEntry> = {\n layoutType: 'list',\n\n mapEntryToCard: (\n entry: ExpertEntry,\n options: CardMappingOptions,\n ): ElementModel => {\n const { isOverlay = false, cardType = 'block' } = options;\n\n // Handle overlay card type (requires headshot)\n if (isOverlay && entry.headshot?.[0]?.url) {\n return createOverlayCardProps(entry, options);\n }\n\n // Route to appropriate card type\n switch (cardType) {\n case 'list':\n return createListCardProps(entry, options);\n case 'tabular':\n return createTabularCardProps(entry, options);\n default:\n return createBlockCardProps(entry, options);\n }\n },\n};\n"],"names":[],"mappings":";;AAuEA,MAAM,kBAAmC;AAAA,EACvC;AAAA,IACE,KAAK;AAAA,IACL,OAAO,MAAM;AAAA,IACb,KAAK,CAAC,UAAU,UAAU,KAAK;AAAA,EAAA;AAAA,EAEjC;AAAA,IACE,KAAK;AAAA,IACL,OAAO,CAAC,UAAU;AAAA,IAClB,KAAK,CAAC,UAAU;AAAA,EAAA;AAAA,EAElB;AAAA,IACE,KAAK;AAAA,IACL,OAAO,CAAC,UAAU;AAAA,IAClB,KAAK,CAAC,UAAU;AAAA,EAAA;AAAA,EAElB;AAAA,IACE,KAAK;AAAA,IACL,OAAO,CAAC,UAAU;AAAA,IAClB,KAAK,CAAC,UAAU;AAAA,EAAA;AAEpB;AAcO,MAAM,gBAAgB,CAAC,UAA+B;AAC3D,QAAM,QAAQ;AAAA,IACZ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EAAA,EACN,OAAO,OAAO;AAEhB,SAAO,MAAM,KAAK,GAAG;AACvB;AAQA,MAAM,kBAAkB,CAAC,UAA+B;AACtD,SAAO,sCAAsC,MAAM,IAAI;AACzD;AAQA,MAAM,yBAAyB,CAAC,UAAsC;AACpE,SAAO,MAAM,gBAAgB,CAAC,GAAG,OAAO,CAAC,GAAG,SAAS;AACvD;AAQA,MAAM,4BAA4B,CAChC,UAC2B;AAC3B,QAAM,aAAa,MAAM,gBAAgB,CAAC,GAAG,OAAO,CAAC,GAAG,cAAc,CAAC;AACvE,MAAI,CAAC,WAAY,QAAO;AAExB,SAAO;AAAA,IACL,OAAO,WAAW;AAAA,IAClB,KAAK,WAAW,MAAM;AAAA,EAAA;AAE1B;AASA,MAAM,mBAAmB,CACvB,OACA,aACqB;AACrB,QAAM,cAAc,MAAM,WAAW,CAAC,GAAG;AACzC,MAAI,CAAC,YAAa,QAAO;AAEzB,SAAO;AAAA,IACL,KAAK;AAAA,IACL,SAAS;AAAA,EAAA;AAEb;AAQA,MAAM,qBAAqB,CAAC,UAAoC;AAC9D,SAAO;AAAA,IACL,OAAO,MAAM,SAAS;AAAA,IACtB,SAAS,MAAM,WAAW;AAAA,IAC1B,UAAU,MAAM,YAAY;AAAA,IAC5B,SAAS,MAAM,WAAW;AAAA,EAAA;AAE9B;AASA,MAAM,qBAAqB,CACzB,OACA,gBACkB;AAIlB,SAAO,MAAM,SAAS,QAAQ;AAChC;AAQA,MAAM,kBAAkB,CAAC,UAAsC;AAC7D,SAAO,MAAM,YAAY;AAC3B;AAcA,MAAM,oBAAoB,CACxB,UACA,KACA,iBACuB;AACvB,SAAO,mBAAmB;AAAA,IACxB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAQA,MAAM,mBAAmB,CAAC,aAAgD;AACxE,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,oBAAoB,EAAE,MAAM,UAAU;AAC/C;AAQA,MAAM,2BAA2B,CAC/B,gBACuB;AACvB,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,YAAY,KAAK;AACnB,WAAO,mBAAmB;AAAA,MACxB,MAAM,YAAY;AAAA,MAClB,KAAK,YAAY;AAAA,IAAA,CAClB;AAAA,EACH;AAEA,SAAO,oBAAoB,EAAE,MAAM,YAAY,OAAO;AACxD;AAUA,MAAM,qBAAqB,CACzB,WACA,SACA,cACgD;AAChD,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,yBAAyB;AAAA,IAC9B,UAAU,UAAU;AAAA,IACpB,SAAS,UAAU;AAAA,IACnB;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAQA,MAAM,2BAA2B,CAC/B,gBACuB;AACvB,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,oBAAoB,EAAE,MAAM,aAAa,WAAW,MAAM;AACnE;AAQA,MAAM,wBAAwB,CAAC,aAAgD;AAC7E,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,oBAAoB,EAAE,MAAM,UAAU;AAC/C;AAQA,MAAM,wBAAwB,CAC5B,gBAC0C;AAC1C,SAAO,gBAAgB;AAAA,IACrB,CAAC,UAAU,WAAW;AACpB,YAAM,QAAQ,YAAY,OAAO,GAAG;AACpC,UAAI,CAAC,MAAO,QAAO;AAEnB,YAAM,UAAU,mBAAmB;AAAA,QACjC,MAAM,OAAO,MAAM,KAAK;AAAA,QACxB,KAAK,OAAO,IAAI,KAAK;AAAA,MAAA,CACtB;AAED,eAAS,OAAO,GAAG,IAAI;AACvB,aAAO;AAAA,IACT;AAAA,IACA,CAAA;AAAA,EAAC;AAEL;AAaA,MAAM,uBAAuB,CAC3B,OACA,YACiB;AACjB,QAAM,EAAE,cAAc,MAAA,IAAU;AAGhC,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,uBAAuB,KAAK;AAC7C,QAAM,cAAc,0BAA0B,KAAK;AACnD,QAAM,YAAY,iBAAiB,OAAO,QAAQ;AAClD,QAAM,WAAW,gBAAgB,KAAK;AAGtC,QAAM,OAAO,kBAAkB,UAAU,UAAU;AACnD,QAAM,MAAM,iBAAiB,QAAQ;AACrC,QAAM,qBAAqB,yBAAyB,WAAW;AAC/D,QAAM,QAAQ,mBAAmB,WAAW,YAAY,oBAAoB,QAAQ,EAAE;AACtF,QAAM,kBAAkB,sBAAsB,QAAQ;AAEtD,SAAO,OAAO,MAAM;AAAA,IAClB;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EAAA,CACD;AACH;AASA,MAAM,sBAAsB,CAC1B,OACA,YACiB;AACjB,QAAM,EAAE,cAAc,MAAA,IAAU;AAGhC,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,uBAAuB,KAAK;AAC7C,QAAM,cAAc,0BAA0B,KAAK;AACnD,QAAM,YAAY,iBAAiB,OAAO,QAAQ;AAClD,QAAM,WAAW,gBAAgB,KAAK;AAGtC,QAAM,OAAO,kBAAkB,UAAU,UAAU;AACnD,QAAM,MAAM,iBAAiB,QAAQ;AACrC,QAAM,qBAAqB,yBAAyB,WAAW;AAC/D,QAAM,QAAQ,mBAAmB,WAAW,YAAY,oBAAoB,QAAQ,EAAE;AACtF,QAAM,kBAAkB,sBAAsB,QAAQ;AAEtD,SAAO,OAAO,KAAK;AAAA,IACjB;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EAAA,CACD;AACH;AASA,MAAM,yBAAyB,CAC7B,OACA,YACiB;AACjB,QAAM,EAAE,cAAc,MAAA,IAAU;AAGhC,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,uBAAuB,KAAK;AAC7C,QAAM,cAAc,0BAA0B,KAAK;AACnD,QAAM,YAAY,iBAAiB,OAAO,QAAQ;AAClD,QAAM,cAAc,mBAAmB,KAAK;AAC5C,QAAM,WAAW,gBAAgB,KAAK;AAGtC,QAAM,OAAO,kBAAkB,UAAU,UAAU;AACnD,QAAM,MAAM,iBAAiB,QAAQ;AACrC,QAAM,qBAAqB,yBAAyB,WAAW;AAC/D,QAAM,QAAQ,mBAAmB,WAAW,YAAY,oBAAoB,QAAQ,EAAE;AACtF,QAAM,kBAAkB,sBAAsB,WAAW;AACzD,QAAM,kBAAkB,sBAAsB,QAAQ;AAEtD,SAAO,OAAO,QAAQ;AAAA,IACpB;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AACH;AASA,MAAM,yBAAyB,CAC7B,OACA,YACiB;AACjB,QAAM,EAAE,cAAc,MAAA,IAAU;AAGhC,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,uBAAuB,KAAK;AAC7C,QAAM,YAAY,iBAAiB,OAAO,QAAQ;AAGlD,QAAM,WAAW,kBAAkB,UAAU,UAAU;AACvD,QAAM,OAAO,iBAAiB,QAAQ;AACtC,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,oBAAoB,QAAQ;AAAA,EAAA;AAG9B,SAAO,KAAK,QAAQ,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAmBO,MAAM,sBAAsB,CACjC,OACA,aACA,cAAuB,UACpB;AAEH,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,uBAAuB,KAAK;AAC7C,QAAM,cAAc,0BAA0B,KAAK;AACnD,QAAM,YAAY,iBAAiB,OAAO,QAAQ;AAClD,QAAM,cAAc,mBAAmB,KAAK;AAC5C,QAAM,cAAc,mBAAmB,KAAkB;AACzD,QAAM,WAAW,gBAAgB,KAAK;AAGtC,QAAM,OAAO,kBAAkB,UAAU,YAAY,IAAI;AACzD,QAAM,MAAM,iBAAiB,QAAQ;AACrC,QAAM,qBAAqB,yBAAyB,WAAW;AAC/D,QAAM,QAAQ,mBAAmB,SAAS;AAC1C,QAAM,qBAAqB,yBAAyB,WAAW;AAC/D,QAAM,kBAAkB,sBAAsB,WAAW;AACzD,QAAM,kBAAkB,sBAAsB,QAAQ;AAEtD,SAAO;AAAA,IACL;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb,OAAO,gBAAgB,SAAS;AAAA,IAChC,UAAU,gBAAgB,YAAY;AAAA,IACtC,OAAO;AAAA,IACP,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB;AAAA,IACA,aAAa;AAAA,IACb;AAAA,EAAA;AAEJ;AAqBO,MAAM,yBAAuD;AAAA,EAClE,YAAY;AAAA,EAEZ,gBAAgB,CACd,OACA,YACiB;AACjB,UAAM,EAAE,YAAY,OAAO,WAAW,YAAY;AAGlD,QAAI,aAAa,MAAM,WAAW,CAAC,GAAG,KAAK;AACzC,aAAO,uBAAuB,OAAO,OAAO;AAAA,IAC9C;AAGA,YAAQ,UAAA;AAAA,MACN,KAAK;AACH,eAAO,oBAAoB,OAAO,OAAO;AAAA,MAC3C,KAAK;AACH,eAAO,uBAAuB,OAAO,OAAO;AAAA,MAC9C;AACE,eAAO,qBAAqB,OAAO,OAAO;AAAA,IAAA;AAAA,EAEhD;AACF;"}
|
|
1
|
+
{"version":3,"file":"experts.js","sources":["../../../source/strategies/display/experts.ts"],"sourcesContent":["/**\n * Experts Display Strategy\n *\n * Strategy for displaying expert entries using person or card elements.\n * Uses a functional composition approach with pure data extraction,\n * element creation, and card composition functions.\n *\n * @module strategies/display/experts\n */\n\nimport {\n person,\n card,\n} from '@universityofmaryland/web-elements-library/composite';\nimport {\n createTextWithLink,\n createTextContainer,\n createImageOrLinkedImage,\n} from '@universityofmaryland/web-utilities-library/elements';\nimport { DisplayStrategy, CardMappingOptions } from '../../factory/core/types';\nimport { ElementModel } from '../../_types';\nimport { ExpertEntry } from 'types/data';\n\n// ============================================================================\n// TYPE DEFINITIONS\n// ============================================================================\n\n/**\n * Contact field configuration\n */\ninterface ContactConfig {\n key: keyof Pick<ExpertEntry, 'email' | 'website' | 'linkedin' | 'twitter'>;\n label: (value: string) => string;\n url: (value: string) => string;\n}\n\n/**\n * Extracted association data\n */\ninterface AssociationData {\n title: string;\n url?: string | null;\n}\n\n/**\n * Extracted image data\n */\ninterface ImageData {\n url: string;\n altText: string;\n}\n\n/**\n * Extracted contact data\n */\ninterface ContactData {\n email?: string | null;\n website?: string | null;\n linkedin?: string | null;\n twitter?: string | null;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\n/**\n * Contact field configuration\n *\n * Defines how each contact type should be rendered as a link.\n */\nconst CONTACT_CONFIGS: ContactConfig[] = [\n {\n key: 'email',\n label: () => 'Email',\n url: (value) => `mailto:${value}`,\n },\n {\n key: 'website',\n label: (value) => value,\n url: (value) => value,\n },\n {\n key: 'linkedin',\n label: (value) => value,\n url: (value) => value,\n },\n {\n key: 'twitter',\n label: (value) => value,\n url: (value) => value,\n },\n];\n\n// ============================================================================\n// PURE DATA EXTRACTION FUNCTIONS\n// ============================================================================\n\n/**\n * Build full name from expert entry\n *\n * Combines prefix (optional), first, middle (optional), last name, and suffix (optional).\n *\n * @param entry - Expert entry\n * @returns Full name string\n */\nexport const buildFullName = (entry: ExpertEntry): string => {\n const parts = [\n entry.prefix,\n entry.firstName,\n entry.middleName,\n entry.lastName,\n entry.suffix,\n ].filter(Boolean);\n\n return parts.join(' ');\n};\n\n/**\n * Build expert profile URL\n *\n * @param entry - Expert entry\n * @returns Profile URL string\n */\nconst buildProfileUrl = (entry: ExpertEntry): string => {\n return `https://umdrightnow.umd.edu/expert/${entry.slug}`;\n};\n\n/**\n * Extract primary job title\n *\n * @param entry - Expert entry\n * @returns Job title string or null\n */\nconst extractPrimaryJobTitle = (entry: ExpertEntry): string | null => {\n return entry.organizations?.[0]?.jobs?.[0]?.title || null;\n};\n\n/**\n * Extract primary association (campus unit)\n *\n * @param entry - Expert entry\n * @returns Association data or null\n */\nconst extractPrimaryAssociation = (\n entry: ExpertEntry,\n): AssociationData | null => {\n const campusUnit = entry.organizations?.[0]?.jobs?.[0]?.campusUnits?.[0];\n if (!campusUnit) return null;\n\n return {\n title: campusUnit.title,\n url: campusUnit.link?.url,\n };\n};\n\n/**\n * Extract image data from headshot\n *\n * @param entry - Expert entry\n * @param fullName - Full name for alt text\n * @returns Image data or null\n */\nconst extractImageData = (\n entry: ExpertEntry,\n fullName: string,\n): ImageData | null => {\n const headshotUrl = entry.headshot?.[0]?.url;\n if (!headshotUrl) return null;\n\n return {\n url: headshotUrl,\n altText: fullName,\n };\n};\n\n/**\n * Extract contact data\n *\n * @param entry - Expert entry\n * @returns Contact data object\n */\nconst extractContactData = (entry: ExpertEntry): ContactData => {\n return {\n email: entry.email || null,\n website: entry.website || null,\n linkedin: entry.linkedin || null,\n twitter: entry.twitter || null,\n };\n};\n\n/**\n * Extract description based on display type\n *\n * @param entry - Expert entry\n * @param displayType - 'small' for summary, 'full' for biography\n * @returns HTML description string or null\n */\nconst extractDescription = (\n entry: ExpertEntry,\n displayType: 'small' | 'full',\n): string | null => {\n if (displayType === 'full') {\n return entry.bio?.html || null;\n }\n return entry.summary?.html || null;\n};\n\n/**\n * Extract pronouns\n *\n * @param entry - Expert entry\n * @returns Pronouns string or null\n */\nconst extractPronouns = (entry: ExpertEntry): string | null => {\n return entry.pronouns || null;\n};\n\n/**\n * Extract areas of expertise as comma-separated string\n *\n * Extracts the titles from the areasOfExpertise categories\n * and joins them with commas for display as subText.\n *\n * @param entry - Expert entry\n * @returns Comma-separated string of expertise areas or null\n *\n * @example\n * ```typescript\n * const entry = {\n * areasOfExpertise: [\n * { id: '1', title: 'Climate Change' },\n * { id: '2', title: 'Environmental Policy' },\n * { id: '3', title: 'Sustainability' }\n * ]\n * };\n * extractAreasOfExpertise(entry);\n * // Returns: \"Climate Change, Environmental Policy, Sustainability\"\n * ```\n */\nexport const extractAreasOfExpertise = (entry: ExpertEntry): string | null => {\n const areas = entry.areasOfExpertise;\n\n if (!areas || areas.length === 0) {\n return null;\n }\n\n return areas.map((area) => area.title).join(', ');\n};\n\n// ============================================================================\n// ELEMENT CREATION FUNCTIONS\n// ============================================================================\n\n/**\n * Create name element with link\n *\n * @param fullName - Full name text\n * @param url - Profile URL\n * @param containerTag - HTML tag for container (default: undefined)\n * @returns Name element or null\n */\nconst createNameElement = (\n fullName: string,\n url: string,\n containerTag?: 'h1' | 'h2' | 'h3',\n): HTMLElement | null => {\n return createTextWithLink({\n text: fullName,\n url,\n containerTag,\n });\n};\n\n/**\n * Create job title element\n *\n * @param jobTitle - Job title text\n * @returns Job element or null\n */\nconst createJobElement = (jobTitle: string | null): HTMLElement | null => {\n if (!jobTitle) return null;\n return createTextContainer({ text: jobTitle });\n};\n\n/**\n * Create association element (with optional link)\n *\n * @param association - Association data\n * @returns Association element or null\n */\nconst createAssociationElement = (\n association: AssociationData | null,\n): HTMLElement | null => {\n if (!association) return null;\n\n if (association.url) {\n return createTextWithLink({\n text: association.title,\n url: association.url,\n });\n }\n\n return createTextContainer({ text: association.title });\n};\n\n/**\n * Create image element (with optional link)\n *\n * @param imageData - Image data\n * @param linkUrl - Optional link URL\n * @param linkLabel - Optional link label\n * @returns Image element or null\n */\nconst createImageElement = (\n imageData: ImageData | null,\n linkUrl?: string,\n linkLabel?: string,\n): HTMLImageElement | HTMLAnchorElement | null => {\n if (!imageData) return null;\n\n return createImageOrLinkedImage({\n imageUrl: imageData.url,\n altText: imageData.altText,\n linkUrl,\n linkLabel,\n });\n};\n\n/**\n * Create description element\n *\n * @param description - HTML description text\n * @returns Description element or null\n */\nconst createDescriptionElement = (\n description: string | null,\n): HTMLElement | null => {\n if (!description) return null;\n return createTextContainer({ text: description, allowHTML: true });\n};\n\n/**\n * Create pronouns element\n *\n * @param pronouns - Pronouns text\n * @returns Pronouns element or null\n */\nconst createPronounsElement = (pronouns: string | null): HTMLElement | null => {\n if (!pronouns) return null;\n return createTextContainer({ text: pronouns });\n};\n\n/**\n * Create pronouns element\n *\n * @param pronouns - Pronouns text\n * @returns Pronouns element or null\n */\nconst createExpertiseElement = (\n expertise: string | null,\n): HTMLElement | null => {\n if (!expertise) return null;\n return createTextContainer({ text: expertise });\n};\n\n/**\n * Create contact elements from contact data\n *\n * @param contactData - Contact data object\n * @returns Object with contact elements keyed by contact type\n */\nconst createContactElements = (\n contactData: ContactData,\n): { [key: string]: HTMLElement | null } => {\n return CONTACT_CONFIGS.reduce<{ [key: string]: HTMLElement | null }>(\n (elements, config) => {\n const value = contactData[config.key];\n if (!value) return elements;\n\n const element = createTextWithLink({\n text: config.label(value),\n url: config.url(value),\n });\n\n elements[config.key] = element;\n return elements;\n },\n {},\n );\n};\n\n// ============================================================================\n// CARD COMPOSITION FUNCTIONS\n// ============================================================================\n\n/**\n * Create props for person block card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person block card element\n */\nconst createBlockCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const pronouns = extractPronouns(entry);\n const expertise = extractAreasOfExpertise(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(\n imageData,\n profileUrl,\n `View profile for ${fullName}`,\n );\n const pronounsElement = createPronounsElement(pronouns);\n const expertiseElement = createExpertiseElement(expertise);\n\n return person.block({\n name,\n pronouns: pronounsElement,\n association: associationElement,\n subText: expertiseElement,\n image,\n isThemeDark,\n });\n};\n\n/**\n * Create props for person list card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person list card element\n */\nconst createListCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const pronouns = extractPronouns(entry);\n const expertise = extractAreasOfExpertise(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(\n imageData,\n profileUrl,\n `View profile for ${fullName}`,\n );\n const pronounsElement = createPronounsElement(pronouns);\n const expertiseElement = createExpertiseElement(expertise);\n\n return person.list({\n name,\n pronouns: pronounsElement,\n association: associationElement,\n subText: expertiseElement,\n image,\n isThemeDark,\n });\n};\n\n/**\n * Create props for person tabular card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Person tabular card element\n */\nconst createTabularCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const contactData = extractContactData(entry);\n const pronouns = extractPronouns(entry);\n const expertise = extractAreasOfExpertise(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl);\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(\n imageData,\n profileUrl,\n `View profile for ${fullName}`,\n );\n const contactElements = createContactElements(contactData);\n const pronounsElement = createPronounsElement(pronouns);\n const expertiseElement = createExpertiseElement(expertise);\n\n return person.tabular({\n name,\n pronouns: pronounsElement,\n association: associationElement,\n subText: expertiseElement,\n image,\n ...contactElements,\n isThemeDark,\n });\n};\n\n/**\n * Create props for overlay card\n *\n * @param entry - Expert entry\n * @param options - Card mapping options\n * @returns Overlay card element\n */\nconst createOverlayCardProps = (\n entry: ExpertEntry,\n options: CardMappingOptions,\n): ElementModel => {\n const { isThemeDark = false } = options;\n\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const imageData = extractImageData(entry, fullName);\n\n // Create elements\n const headline = createNameElement(fullName, profileUrl);\n const text = createJobElement(jobTitle);\n const backgroundImage = createImageElement(\n imageData,\n profileUrl,\n `View profile for ${fullName}`,\n );\n\n return card.overlay.image({\n headline,\n text,\n backgroundImage,\n isThemeDark,\n });\n};\n\n/**\n * Map expert entry to PersonBio props\n *\n * Shared helper for creating PersonBio props from expert data.\n * Used by both the display strategy and bio feed for consistency.\n *\n * @param entry - Expert entry from API\n * @param displayType - 'small' for summary, 'full' for biography\n * @param isThemeDark - Dark theme flag\n * @returns Props for person.bio.full() or person.bio.small()\n *\n * @example\n * ```typescript\n * const bioProps = mapExpertToBioProps(expert, 'full', false);\n * const bioElement = person.bio.full(bioProps);\n * ```\n */\nexport const mapExpertToBioProps = (\n entry: ExpertEntry,\n displayType: 'small' | 'full',\n isThemeDark: boolean = false,\n) => {\n // Extract data\n const fullName = buildFullName(entry);\n const profileUrl = buildProfileUrl(entry);\n const jobTitle = extractPrimaryJobTitle(entry);\n const association = extractPrimaryAssociation(entry);\n const imageData = extractImageData(entry, fullName);\n const contactData = extractContactData(entry);\n const description = extractDescription(entry, displayType);\n const pronouns = extractPronouns(entry);\n const expertise = extractAreasOfExpertise(entry);\n\n // Create elements\n const name = createNameElement(fullName, profileUrl, 'h1');\n const job = createJobElement(jobTitle);\n const associationElement = createAssociationElement(association);\n const image = createImageElement(imageData);\n const descriptionElement = createDescriptionElement(description);\n const contactElements = createContactElements(contactData);\n const pronounsElement = createPronounsElement(pronouns);\n const expertiseElement = createExpertiseElement(expertise);\n\n return {\n name,\n pronouns: pronounsElement,\n association: associationElement,\n subText: expertiseElement,\n email: contactElements.email || null,\n linkedin: contactElements.linkedin || null,\n phone: null,\n address: null,\n additionalContact: null,\n image,\n description: descriptionElement,\n isThemeDark,\n };\n};\n\n// ============================================================================\n// DISPLAY STRATEGY\n// ============================================================================\n\n/**\n * Experts display strategy\n *\n * Maps expert entries to person elements for profile display.\n * Optimized for displaying faculty and expert profiles with contact\n * information and skills.\n *\n * @example\n * ```typescript\n * const feed = createBaseFeed({\n * displayStrategy: expertsDisplayStrategy,\n * // ...\n * });\n * ```\n */\nexport const expertsDisplayStrategy: DisplayStrategy<ExpertEntry> = {\n layoutType: 'list',\n\n mapEntryToCard: (\n entry: ExpertEntry,\n options: CardMappingOptions,\n ): ElementModel => {\n const { isOverlay = false, cardType = 'block' } = options;\n\n // Handle overlay card type (requires headshot)\n if (isOverlay && entry.headshot?.[0]?.url) {\n return createOverlayCardProps(entry, options);\n }\n\n // Route to appropriate card type\n switch (cardType) {\n case 'list':\n return createListCardProps(entry, options);\n case 'tabular':\n return createTabularCardProps(entry, options);\n default:\n return createBlockCardProps(entry, options);\n }\n },\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ExpertEntry } from '../../types/data';
|
|
2
|
-
export declare const EXPERTS_QUERY = "\n query getExpertsContent($limit: Int, $offset: Int, $
|
|
2
|
+
export declare const EXPERTS_QUERY = "\n query getExpertsContent($limit: Int, $offset: Int, $ids: [QueryArgument], $relatedTo: [QueryArgument], $isMediaTrained:Boolean) {\n entryCount(\n section: \"experts\"\n limit: $limit\n offset: $offset\n type: \"expertsContent\"\n id: $ids\n relatedTo: $relatedTo\n expertsMediaTrained: $isMediaTrained\n )\n entries(\n section: \"experts\"\n limit: $limit\n offset: $offset\n type: \"expertsContent\"\n id: $ids\n relatedTo: $relatedTo\n expertsMediaTrained: $isMediaTrained\n orderBy: \"expertsNameLast\"\n ) {\n ...EntriesNativeFields\n ... on expertsContent_Entry {\n ...Content\n ...Categories\n }\n }\n }\n \n fragment EntriesNativeFields on EntryInterface {\n id\n uid\n status\n enabled\n slug\n title\n type: typeHandle\n postDate\n expiryDate\n dateUpdated\n dateCreated\n }\n\n \n fragment CategoryFields on CategoryInterface {\n title\n url\n id\n }\n\n \n fragment Content on expertsContent_Entry {\n ...Name\n ...Imagery\n ...Biography\n organizations: expertsOrganization {\n ...Organizations\n }\n ...Contact\n ...Skills\n }\n\n \n fragment Categories on expertsContent_Entry {\n areasOfExpertise: expertsCategoryAreaOfExpertise {\n ...CategoryFields\n }\n campusUnits: categoryCampusUnit {\n ...CategoryFields\n }\n tags: expertsCategoryTags {\n ...CategoryFields\n }\n }\n\n \n fragment Name on expertsContent_Entry {\n prefix: expertsNamePrefix\n firstName: expertsNameFirst\n middleName: expertsNameMiddle\n lastName: expertsNameLast\n suffix: expertsNameSuffix\n pronouns: expertsNamePronouns\n }\n\n \n fragment Imagery on expertsContent_Entry {\n headshot: expertsImageHeadShot {\n ... on experts_Asset {\n url\n }\n }\n }\n\n \n fragment Biography on expertsContent_Entry {\n summary: expertsSummary {\n html\n }\n bio: expertsBiography {\n html\n }\n }\n\n \n fragment Organizations on expertsOrganization_Entry {\n id\n title\n url\n jobs: expertsOrganizationJob {\n ...Jobs\n }\n }\n\n \n fragment Jobs on expertsOrganizationJob_Entry {\n id\n url\n title: expertsOrganizationJobTitle\n roomNumber: expertsOrganizationJobRoomNumber\n campusUnits: categoryCampusUnit {\n title\n ... on campusUnits_Category {\n link: externalLink {\n url\n }\n }\n }\n }\n\n \n fragment Contact on expertsContent_Entry {\n email: expertsContactEmail\n linkedin: expertsContactLinkedin\n website: expertsContactWebsite\n twitter: expertsContactTwitter\n }\n\n \n fragment Skills on expertsContent_Entry {\n languages: expertsLanguages\n mediaTrained: expertsMediaTrained\n }\n\n";
|
|
3
3
|
export declare const expertsFetchStrategy: import('../../factory').FetchStrategy<ExpertEntry, any>;
|
|
4
4
|
//# sourceMappingURL=experts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/strategies/fetch/experts.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwIzC,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/strategies/fetch/experts.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwIzC,eAAO,MAAM,aAAa,y1FAuCzB,CAAC;AAmBF,eAAO,MAAM,oBAAoB,yDAyC/B,CAAC"}
|
|
@@ -116,13 +116,13 @@ const FRAGMENT_EXPERT_CATEGORIES = `
|
|
|
116
116
|
}
|
|
117
117
|
`;
|
|
118
118
|
const EXPERTS_QUERY = `
|
|
119
|
-
query getExpertsContent($limit: Int, $offset: Int, $
|
|
119
|
+
query getExpertsContent($limit: Int, $offset: Int, $ids: [QueryArgument], $relatedTo: [QueryArgument], $isMediaTrained:Boolean) {
|
|
120
120
|
entryCount(
|
|
121
121
|
section: "experts"
|
|
122
122
|
limit: $limit
|
|
123
123
|
offset: $offset
|
|
124
124
|
type: "expertsContent"
|
|
125
|
-
id: $
|
|
125
|
+
id: $ids
|
|
126
126
|
relatedTo: $relatedTo
|
|
127
127
|
expertsMediaTrained: $isMediaTrained
|
|
128
128
|
)
|
|
@@ -131,7 +131,7 @@ const EXPERTS_QUERY = `
|
|
|
131
131
|
limit: $limit
|
|
132
132
|
offset: $offset
|
|
133
133
|
type: "expertsContent"
|
|
134
|
-
id: $
|
|
134
|
+
id: $ids
|
|
135
135
|
relatedTo: $relatedTo
|
|
136
136
|
expertsMediaTrained: $isMediaTrained
|
|
137
137
|
orderBy: "expertsNameLast"
|
|
@@ -173,10 +173,10 @@ const expertsFetchStrategy = createGraphQLFetchStrategy({
|
|
|
173
173
|
return data.data.entryCount || 0;
|
|
174
174
|
},
|
|
175
175
|
composeVariables: (baseVariables) => {
|
|
176
|
-
const { categories, entriesToRemove,
|
|
176
|
+
const { categories, entriesToRemove, ids, isMediaTrained, ...rest } = baseVariables;
|
|
177
177
|
const variables = { ...rest };
|
|
178
|
-
if (
|
|
179
|
-
variables.
|
|
178
|
+
if (ids) {
|
|
179
|
+
variables.ids = Array.isArray(ids) ? ids : [ids];
|
|
180
180
|
}
|
|
181
181
|
if (categories) {
|
|
182
182
|
variables.relatedTo = categories;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experts.js","sources":["../../../source/strategies/fetch/experts.ts"],"sourcesContent":["/**\n * Experts Fetch Strategy\n *\n * Strategy for fetching expert profile data from the UMD Experts GraphQL API.\n *\n * @module strategies/fetch/experts\n */\n\nimport { createGraphQLFetchStrategy } from './graphql';\nimport { ExpertEntry } from 'types/data';\n\n/**\n * GraphQL fragments for expert data\n */\nconst FRAGMENT_CATEGORIES_NATIVE_FIELDS = `\n fragment CategoryFields on CategoryInterface {\n title\n url\n id\n }\n`;\n\nconst FRAGMENT_ENTRIES_NATIVE_FIELDS = `\n fragment EntriesNativeFields on EntryInterface {\n id\n uid\n status\n enabled\n slug\n title\n type: typeHandle\n postDate\n expiryDate\n dateUpdated\n dateCreated\n }\n`;\n\nconst FRAGMENT_EXPERT_JOBS = `\n fragment Jobs on expertsOrganizationJob_Entry {\n id\n url\n title: expertsOrganizationJobTitle\n roomNumber: expertsOrganizationJobRoomNumber\n campusUnits: categoryCampusUnit {\n title\n ... on campusUnits_Category {\n link: externalLink {\n url\n }\n }\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_NAME = `\n fragment Name on expertsContent_Entry {\n prefix: expertsNamePrefix\n firstName: expertsNameFirst\n middleName: expertsNameMiddle\n lastName: expertsNameLast\n suffix: expertsNameSuffix\n pronouns: expertsNamePronouns\n }\n`;\n\nconst FRAGMENT_EXPERT_IMAGERY = `\n fragment Imagery on expertsContent_Entry {\n headshot: expertsImageHeadShot {\n ... on experts_Asset {\n url\n }\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_BIOGRAPHY = `\n fragment Biography on expertsContent_Entry {\n summary: expertsSummary {\n html\n }\n bio: expertsBiography {\n html\n }\n }\n`;\n\nconst FRAGMENT_ENTRIES_ORGANIZATIONS = `\n fragment Organizations on expertsOrganization_Entry {\n id\n title\n url\n jobs: expertsOrganizationJob {\n ...Jobs\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_CONTACT = `\n fragment Contact on expertsContent_Entry {\n email: expertsContactEmail\n linkedin: expertsContactLinkedin\n website: expertsContactWebsite\n twitter: expertsContactTwitter\n }\n`;\n\nconst FRAGMENT_EXPERT_SKILLS = `\n fragment Skills on expertsContent_Entry {\n languages: expertsLanguages\n mediaTrained: expertsMediaTrained\n }\n`;\n\nconst FRAGMENT_EXPERT_CONTENT = `\n fragment Content on expertsContent_Entry {\n ...Name\n ...Imagery\n ...Biography\n organizations: expertsOrganization {\n ...Organizations\n }\n ...Contact\n ...Skills\n }\n`;\n\nconst FRAGMENT_EXPERT_CATEGORIES = `\n fragment Categories on expertsContent_Entry {\n areasOfExpertise: expertsCategoryAreaOfExpertise {\n ...CategoryFields\n }\n campusUnits: categoryCampusUnit {\n ...CategoryFields\n }\n tags: expertsCategoryTags {\n ...CategoryFields\n }\n }\n`;\n\n/**\n * GraphQL query for experts\n * Supports filtering by ID, relatedTo, or fetching all experts\n */\nexport const EXPERTS_QUERY = `\n query getExpertsContent($limit: Int, $offset: Int, $
|
|
1
|
+
{"version":3,"file":"experts.js","sources":["../../../source/strategies/fetch/experts.ts"],"sourcesContent":["/**\n * Experts Fetch Strategy\n *\n * Strategy for fetching expert profile data from the UMD Experts GraphQL API.\n *\n * @module strategies/fetch/experts\n */\n\nimport { createGraphQLFetchStrategy } from './graphql';\nimport { ExpertEntry } from 'types/data';\n\n/**\n * GraphQL fragments for expert data\n */\nconst FRAGMENT_CATEGORIES_NATIVE_FIELDS = `\n fragment CategoryFields on CategoryInterface {\n title\n url\n id\n }\n`;\n\nconst FRAGMENT_ENTRIES_NATIVE_FIELDS = `\n fragment EntriesNativeFields on EntryInterface {\n id\n uid\n status\n enabled\n slug\n title\n type: typeHandle\n postDate\n expiryDate\n dateUpdated\n dateCreated\n }\n`;\n\nconst FRAGMENT_EXPERT_JOBS = `\n fragment Jobs on expertsOrganizationJob_Entry {\n id\n url\n title: expertsOrganizationJobTitle\n roomNumber: expertsOrganizationJobRoomNumber\n campusUnits: categoryCampusUnit {\n title\n ... on campusUnits_Category {\n link: externalLink {\n url\n }\n }\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_NAME = `\n fragment Name on expertsContent_Entry {\n prefix: expertsNamePrefix\n firstName: expertsNameFirst\n middleName: expertsNameMiddle\n lastName: expertsNameLast\n suffix: expertsNameSuffix\n pronouns: expertsNamePronouns\n }\n`;\n\nconst FRAGMENT_EXPERT_IMAGERY = `\n fragment Imagery on expertsContent_Entry {\n headshot: expertsImageHeadShot {\n ... on experts_Asset {\n url\n }\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_BIOGRAPHY = `\n fragment Biography on expertsContent_Entry {\n summary: expertsSummary {\n html\n }\n bio: expertsBiography {\n html\n }\n }\n`;\n\nconst FRAGMENT_ENTRIES_ORGANIZATIONS = `\n fragment Organizations on expertsOrganization_Entry {\n id\n title\n url\n jobs: expertsOrganizationJob {\n ...Jobs\n }\n }\n`;\n\nconst FRAGMENT_EXPERT_CONTACT = `\n fragment Contact on expertsContent_Entry {\n email: expertsContactEmail\n linkedin: expertsContactLinkedin\n website: expertsContactWebsite\n twitter: expertsContactTwitter\n }\n`;\n\nconst FRAGMENT_EXPERT_SKILLS = `\n fragment Skills on expertsContent_Entry {\n languages: expertsLanguages\n mediaTrained: expertsMediaTrained\n }\n`;\n\nconst FRAGMENT_EXPERT_CONTENT = `\n fragment Content on expertsContent_Entry {\n ...Name\n ...Imagery\n ...Biography\n organizations: expertsOrganization {\n ...Organizations\n }\n ...Contact\n ...Skills\n }\n`;\n\nconst FRAGMENT_EXPERT_CATEGORIES = `\n fragment Categories on expertsContent_Entry {\n areasOfExpertise: expertsCategoryAreaOfExpertise {\n ...CategoryFields\n }\n campusUnits: categoryCampusUnit {\n ...CategoryFields\n }\n tags: expertsCategoryTags {\n ...CategoryFields\n }\n }\n`;\n\n/**\n * GraphQL query for experts\n * Supports filtering by ID, relatedTo, or fetching all experts\n */\nexport const EXPERTS_QUERY = `\n query getExpertsContent($limit: Int, $offset: Int, $ids: [QueryArgument], $relatedTo: [QueryArgument], $isMediaTrained:Boolean) {\n entryCount(\n section: \"experts\"\n limit: $limit\n offset: $offset\n type: \"expertsContent\"\n id: $ids\n relatedTo: $relatedTo\n expertsMediaTrained: $isMediaTrained\n )\n entries(\n section: \"experts\"\n limit: $limit\n offset: $offset\n type: \"expertsContent\"\n id: $ids\n relatedTo: $relatedTo\n expertsMediaTrained: $isMediaTrained\n orderBy: \"expertsNameLast\"\n ) {\n ...EntriesNativeFields\n ... on expertsContent_Entry {\n ...Content\n ...Categories\n }\n }\n }\n ${FRAGMENT_ENTRIES_NATIVE_FIELDS}\n ${FRAGMENT_CATEGORIES_NATIVE_FIELDS}\n ${FRAGMENT_EXPERT_CONTENT}\n ${FRAGMENT_EXPERT_CATEGORIES}\n ${FRAGMENT_EXPERT_NAME}\n ${FRAGMENT_EXPERT_IMAGERY}\n ${FRAGMENT_EXPERT_BIOGRAPHY}\n ${FRAGMENT_ENTRIES_ORGANIZATIONS}\n ${FRAGMENT_EXPERT_JOBS}\n ${FRAGMENT_EXPERT_CONTACT}\n ${FRAGMENT_EXPERT_SKILLS}\n`;\n\n/**\n * Experts fetch strategy\n *\n * Fetches expert profile data from the UMD Experts GraphQL API.\n * Handles category filtering via relatedTo parameter, pagination,\n * and entry exclusion.\n *\n * @example\n * ```typescript\n * const feed = createBaseFeed({\n * token: 'my-token',\n * fetchStrategy: expertsFetchStrategy,\n * categories: ['computer-science', 'engineering'],\n * // ...\n * });\n * ```\n */\nexport const expertsFetchStrategy = createGraphQLFetchStrategy<ExpertEntry>({\n endpoint: 'https://umd-api.production.servd.dev/graphql',\n\n queries: {\n entries: EXPERTS_QUERY,\n },\n\n transformResponse: (data) => {\n if (!data || !data.data || !data.data.entries) {\n return null;\n }\n return data.data.entries || null;\n },\n\n transformCount: (data) => {\n if (!data || !data.data) {\n return 0;\n }\n return data.data.entryCount || 0;\n },\n\n composeVariables: (baseVariables) => {\n const { categories, entriesToRemove, ids, isMediaTrained, ...rest } =\n baseVariables;\n\n const variables: any = { ...rest };\n\n if (ids) {\n variables.ids = Array.isArray(ids) ? ids : [ids];\n }\n\n if (categories) {\n variables.relatedTo = categories;\n }\n\n if (isMediaTrained) {\n variables.isMediaTrained = isMediaTrained;\n }\n\n return variables;\n },\n});\n"],"names":[],"mappings":";AAcA,MAAM,oCAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1C,MAAM,iCAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBvC,MAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiB7B,MAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAW7B,MAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUhC,MAAM,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWlC,MAAM,iCAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWvC,MAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAShC,MAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAO/B,MAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahC,MAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkB5B,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA4BzB,8BAA8B;AAAA,IAC9B,iCAAiC;AAAA,IACjC,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,yBAAyB;AAAA,IACzB,8BAA8B;AAAA,IAC9B,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,sBAAsB;AAAA;AAoBnB,MAAM,uBAAuB,2BAAwC;AAAA,EAC1E,UAAU;AAAA,EAEV,SAAS;AAAA,IACP,SAAS;AAAA,EAAA;AAAA,EAGX,mBAAmB,CAAC,SAAS;AAC3B,QAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,KAAK,KAAK,SAAS;AAC7C,aAAO;AAAA,IACT;AACA,WAAO,KAAK,KAAK,WAAW;AAAA,EAC9B;AAAA,EAEA,gBAAgB,CAAC,SAAS;AACxB,QAAI,CAAC,QAAQ,CAAC,KAAK,MAAM;AACvB,aAAO;AAAA,IACT;AACA,WAAO,KAAK,KAAK,cAAc;AAAA,EACjC;AAAA,EAEA,kBAAkB,CAAC,kBAAkB;AACnC,UAAM,EAAE,YAAY,iBAAiB,KAAK,gBAAgB,GAAG,SAC3D;AAEF,UAAM,YAAiB,EAAE,GAAG,KAAA;AAE5B,QAAI,KAAK;AACP,gBAAU,MAAM,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;AAAA,IACjD;AAEA,QAAI,YAAY;AACd,gBAAU,YAAY;AAAA,IACxB;AAEA,QAAI,gBAAgB;AAClB,gBAAU,iBAAiB;AAAA,IAC7B;AAEA,WAAO;AAAA,EACT;AACF,CAAC;"}
|
|
@@ -66,7 +66,7 @@ function createGraphQLFetchStrategy(config) {
|
|
|
66
66
|
numberOfRowsToStart,
|
|
67
67
|
getOffset,
|
|
68
68
|
entriesToRemove,
|
|
69
|
-
|
|
69
|
+
ids,
|
|
70
70
|
isMediaTrained
|
|
71
71
|
} = props;
|
|
72
72
|
const baseVariables = {
|
|
@@ -80,8 +80,8 @@ function createGraphQLFetchStrategy(config) {
|
|
|
80
80
|
if (entriesToRemove) {
|
|
81
81
|
baseVariables.entriesToRemove = entriesToRemove;
|
|
82
82
|
}
|
|
83
|
-
if (
|
|
84
|
-
baseVariables.
|
|
83
|
+
if (ids) {
|
|
84
|
+
baseVariables.ids = ids;
|
|
85
85
|
}
|
|
86
86
|
if (isMediaTrained !== void 0) {
|
|
87
87
|
baseVariables.isMediaTrained = isMediaTrained;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.js","sources":["../../../source/strategies/fetch/graphql.ts"],"sourcesContent":["/**\n * GraphQL Fetch Strategy Factory\n *\n * Generic factory for creating GraphQL-based fetch strategies.\n * Handles common GraphQL patterns: queries, variables, transformations.\n *\n * @module strategies/fetch/graphql\n */\n\nimport {\n fetchGraphQL,\n GraphQLVariables,\n} from '@universityofmaryland/web-utilities-library/network';\nimport { FetchStrategy } from '../../factory/core/types';\n\n/**\n * Configuration for creating a GraphQL fetch strategy\n */\nexport interface GraphQLFetchConfig<TData, TResponse = any> {\n /** GraphQL endpoint URL */\n endpoint: string;\n\n /** GraphQL queries */\n queries: {\n /** Query for fetching entries */\n entries: string;\n /** Optional separate query for count (if not in entries query) */\n count?: string;\n };\n\n /** Transform GraphQL response to entries array */\n transformResponse: (data: TResponse) => TData[] | null;\n\n /** Transform GraphQL response to count */\n transformCount: (data: TResponse) => number;\n\n /** Optional: Compose additional variables */\n composeVariables?: (baseVariables: any) => any;\n}\n\n/**\n * Create a GraphQL fetch strategy\n *\n * This factory handles the common patterns for GraphQL APIs:\n * - Fetching with variables\n * - Response transformation\n * - Count vs entries queries\n * - Error handling\n *\n * @template TData - The type of entry data\n * @template TResponse - The type of raw GraphQL response\n * @template TVariables - The type of GraphQL variables\n *\n * @param config - Configuration for the strategy\n * @returns Fetch strategy for use with createBaseFeed\n *\n * @example\n * ```typescript\n * const eventsFetchStrategy = createGraphQLFetchStrategy({\n * endpoint: 'https://calendar.umd.edu/graphql',\n * queries: {\n * entries: EVENTS_QUERY,\n * count: EVENTS_COUNT_QUERY,\n * },\n * transformResponse: (data) => data?.data?.entries?.events || null,\n * transformCount: (data) => data?.data?.count?.events?.length || 0,\n * });\n * ```\n */\nexport function createGraphQLFetchStrategy<\n TData,\n TResponse = any,\n TVariables = any,\n>(\n config: GraphQLFetchConfig<TData, TResponse>,\n): FetchStrategy<TData, TVariables> {\n const {\n endpoint,\n queries,\n transformResponse,\n transformCount,\n composeVariables,\n } = config;\n\n return {\n /**\n * Fetch the total count of entries\n */\n fetchCount: async (variables: TVariables): Promise<number | null> => {\n try {\n // Use count query if provided, otherwise use entries query\n const query = queries.count || queries.entries;\n const response = await fetchGraphQL({\n url: endpoint,\n query,\n token: (variables as any).token,\n variables: variables as GraphQLVariables,\n });\n\n // Check for errors\n if (!response || !response.data || response.message) {\n if (response?.message) {\n console.error(`GraphQL Error: ${response.message}`);\n }\n return null;\n }\n\n return transformCount(response);\n } catch (error) {\n console.error('Fetch count error:', error);\n return null;\n }\n },\n\n /**\n * Fetch the actual entries\n */\n fetchEntries: async (variables: TVariables): Promise<TData[] | null> => {\n try {\n const response = await fetchGraphQL({\n url: endpoint,\n query: queries.entries,\n token: (variables as any).token,\n variables: variables as GraphQLVariables,\n });\n\n // Check for errors\n if (!response || !response.data || response.message) {\n if (response?.message) {\n console.error(`GraphQL Error: ${response.message}`);\n }\n return null;\n }\n\n return transformResponse(response);\n } catch (error) {\n console.error('Fetch entries error:', error);\n return null;\n }\n },\n\n /**\n * Compose API variables from feed props\n */\n composeApiVariables: (props: any): TVariables => {\n const {\n token,\n categories,\n numberOfColumnsToShow = 1,\n numberOfRowsToStart,\n getOffset,\n entriesToRemove,\n
|
|
1
|
+
{"version":3,"file":"graphql.js","sources":["../../../source/strategies/fetch/graphql.ts"],"sourcesContent":["/**\n * GraphQL Fetch Strategy Factory\n *\n * Generic factory for creating GraphQL-based fetch strategies.\n * Handles common GraphQL patterns: queries, variables, transformations.\n *\n * @module strategies/fetch/graphql\n */\n\nimport {\n fetchGraphQL,\n GraphQLVariables,\n} from '@universityofmaryland/web-utilities-library/network';\nimport { FetchStrategy } from '../../factory/core/types';\n\n/**\n * Configuration for creating a GraphQL fetch strategy\n */\nexport interface GraphQLFetchConfig<TData, TResponse = any> {\n /** GraphQL endpoint URL */\n endpoint: string;\n\n /** GraphQL queries */\n queries: {\n /** Query for fetching entries */\n entries: string;\n /** Optional separate query for count (if not in entries query) */\n count?: string;\n };\n\n /** Transform GraphQL response to entries array */\n transformResponse: (data: TResponse) => TData[] | null;\n\n /** Transform GraphQL response to count */\n transformCount: (data: TResponse) => number;\n\n /** Optional: Compose additional variables */\n composeVariables?: (baseVariables: any) => any;\n}\n\n/**\n * Create a GraphQL fetch strategy\n *\n * This factory handles the common patterns for GraphQL APIs:\n * - Fetching with variables\n * - Response transformation\n * - Count vs entries queries\n * - Error handling\n *\n * @template TData - The type of entry data\n * @template TResponse - The type of raw GraphQL response\n * @template TVariables - The type of GraphQL variables\n *\n * @param config - Configuration for the strategy\n * @returns Fetch strategy for use with createBaseFeed\n *\n * @example\n * ```typescript\n * const eventsFetchStrategy = createGraphQLFetchStrategy({\n * endpoint: 'https://calendar.umd.edu/graphql',\n * queries: {\n * entries: EVENTS_QUERY,\n * count: EVENTS_COUNT_QUERY,\n * },\n * transformResponse: (data) => data?.data?.entries?.events || null,\n * transformCount: (data) => data?.data?.count?.events?.length || 0,\n * });\n * ```\n */\nexport function createGraphQLFetchStrategy<\n TData,\n TResponse = any,\n TVariables = any,\n>(\n config: GraphQLFetchConfig<TData, TResponse>,\n): FetchStrategy<TData, TVariables> {\n const {\n endpoint,\n queries,\n transformResponse,\n transformCount,\n composeVariables,\n } = config;\n\n return {\n /**\n * Fetch the total count of entries\n */\n fetchCount: async (variables: TVariables): Promise<number | null> => {\n try {\n // Use count query if provided, otherwise use entries query\n const query = queries.count || queries.entries;\n const response = await fetchGraphQL({\n url: endpoint,\n query,\n token: (variables as any).token,\n variables: variables as GraphQLVariables,\n });\n\n // Check for errors\n if (!response || !response.data || response.message) {\n if (response?.message) {\n console.error(`GraphQL Error: ${response.message}`);\n }\n return null;\n }\n\n return transformCount(response);\n } catch (error) {\n console.error('Fetch count error:', error);\n return null;\n }\n },\n\n /**\n * Fetch the actual entries\n */\n fetchEntries: async (variables: TVariables): Promise<TData[] | null> => {\n try {\n const response = await fetchGraphQL({\n url: endpoint,\n query: queries.entries,\n token: (variables as any).token,\n variables: variables as GraphQLVariables,\n });\n\n // Check for errors\n if (!response || !response.data || response.message) {\n if (response?.message) {\n console.error(`GraphQL Error: ${response.message}`);\n }\n return null;\n }\n\n return transformResponse(response);\n } catch (error) {\n console.error('Fetch entries error:', error);\n return null;\n }\n },\n\n /**\n * Compose API variables from feed props\n */\n composeApiVariables: (props: any): TVariables => {\n const {\n token,\n categories,\n numberOfColumnsToShow = 1,\n numberOfRowsToStart,\n getOffset,\n entriesToRemove,\n ids,\n isMediaTrained,\n } = props;\n\n // Base variables - pass all props through\n const baseVariables: any = {\n token,\n limit: numberOfColumnsToShow * numberOfRowsToStart,\n offset: getOffset ? getOffset() : 0,\n };\n\n // Pass categories through (let composeVariables decide how to map it)\n if (categories) {\n baseVariables.categories = categories;\n }\n\n // Pass entriesToRemove through\n if (entriesToRemove) {\n baseVariables.entriesToRemove = entriesToRemove;\n }\n\n // Pass id through\n if (ids) {\n baseVariables.ids = ids;\n }\n\n // Pass isMediaTrained through (can be true, false, or null)\n if (isMediaTrained !== undefined) {\n baseVariables.isMediaTrained = isMediaTrained;\n }\n\n // Allow custom variable composition\n if (composeVariables) {\n return composeVariables(baseVariables);\n }\n\n // Default mapping (for strategies without custom composeVariables)\n const defaultVariables: any = { ...baseVariables };\n\n // Default: map categories to 'related' if not customized\n if (categories) {\n defaultVariables.related = categories;\n delete defaultVariables.categories;\n }\n\n return defaultVariables as TVariables;\n },\n };\n}\n"],"names":[],"mappings":";AAqEO,SAAS,2BAKd,QACkC;AAClC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,YAAY,OAAO,cAAkD;AACnE,UAAI;AAEF,cAAM,QAAQ,QAAQ,SAAS,QAAQ;AACvC,cAAM,WAAW,MAAM,aAAa;AAAA,UAClC,KAAK;AAAA,UACL;AAAA,UACA,OAAQ,UAAkB;AAAA,UAC1B;AAAA,QAAA,CACD;AAGD,YAAI,CAAC,YAAY,CAAC,SAAS,QAAQ,SAAS,SAAS;AACnD,cAAI,UAAU,SAAS;AACrB,oBAAQ,MAAM,kBAAkB,SAAS,OAAO,EAAE;AAAA,UACpD;AACA,iBAAO;AAAA,QACT;AAEA,eAAO,eAAe,QAAQ;AAAA,MAChC,SAAS,OAAO;AACd,gBAAQ,MAAM,sBAAsB,KAAK;AACzC,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAKA,cAAc,OAAO,cAAmD;AACtE,UAAI;AACF,cAAM,WAAW,MAAM,aAAa;AAAA,UAClC,KAAK;AAAA,UACL,OAAO,QAAQ;AAAA,UACf,OAAQ,UAAkB;AAAA,UAC1B;AAAA,QAAA,CACD;AAGD,YAAI,CAAC,YAAY,CAAC,SAAS,QAAQ,SAAS,SAAS;AACnD,cAAI,UAAU,SAAS;AACrB,oBAAQ,MAAM,kBAAkB,SAAS,OAAO,EAAE;AAAA,UACpD;AACA,iBAAO;AAAA,QACT;AAEA,eAAO,kBAAkB,QAAQ;AAAA,MACnC,SAAS,OAAO;AACd,gBAAQ,MAAM,wBAAwB,KAAK;AAC3C,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAKA,qBAAqB,CAAC,UAA2B;AAC/C,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,wBAAwB;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA,IACE;AAGJ,YAAM,gBAAqB;AAAA,QACzB;AAAA,QACA,OAAO,wBAAwB;AAAA,QAC/B,QAAQ,YAAY,cAAc;AAAA,MAAA;AAIpC,UAAI,YAAY;AACd,sBAAc,aAAa;AAAA,MAC7B;AAGA,UAAI,iBAAiB;AACnB,sBAAc,kBAAkB;AAAA,MAClC;AAGA,UAAI,KAAK;AACP,sBAAc,MAAM;AAAA,MACtB;AAGA,UAAI,mBAAmB,QAAW;AAChC,sBAAc,iBAAiB;AAAA,MACjC;AAGA,UAAI,kBAAkB;AACpB,eAAO,iBAAiB,aAAa;AAAA,MACvC;AAGA,YAAM,mBAAwB,EAAE,GAAG,cAAA;AAGnC,UAAI,YAAY;AACd,yBAAiB,UAAU;AAC3B,eAAO,iBAAiB;AAAA,MAC1B;AAEA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;"}
|
package/dist/types/api.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface CategoryApiVariables extends BaseApiVariables {
|
|
|
21
21
|
related?: string[];
|
|
22
22
|
relatedToAll?: string[];
|
|
23
23
|
relatedTo?: string[];
|
|
24
|
+
ids?: string[];
|
|
24
25
|
}
|
|
25
26
|
export interface ExclusionApiVariables extends BaseApiVariables {
|
|
26
27
|
not?: (string | number)[];
|
package/dist/types/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../source/types/api.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG;IAEtC,IAAI,CAAC,EAAE;QAEL,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAEd,UAAU,CAAC,EAAE,MAAM,CAAC;QAEpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IAEF,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAExB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,MAAM,WAAW,YAAY;IAE3B,OAAO,EAAE,MAAM,CAAC;IAEhB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAOD,MAAM,WAAW,gBAAgB;IAE/B,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAE5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../source/types/api.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG;IAEtC,IAAI,CAAC,EAAE;QAEL,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAEd,UAAU,CAAC,EAAE,MAAM,CAAC;QAEpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IAEF,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IAExB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,MAAM,WAAW,YAAY;IAE3B,OAAO,EAAE,MAAM,CAAC;IAEhB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAOD,MAAM,WAAW,gBAAgB;IAE/B,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAE5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAOD,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAE7D,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAE1B,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;CACf;AAQD,MAAM,WAAW,gBACf,SAAQ,oBAAoB,EAC1B,qBAAqB;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB"}
|
|
@@ -28,6 +28,11 @@ export interface ExpertSkills {
|
|
|
28
28
|
languages?: string[] | null;
|
|
29
29
|
mediaTrained?: boolean | null;
|
|
30
30
|
}
|
|
31
|
+
export interface ExpertCategory {
|
|
32
|
+
id: string;
|
|
33
|
+
title: string;
|
|
34
|
+
url?: string | null;
|
|
35
|
+
}
|
|
31
36
|
export interface ExpertEntry extends BaseEntry {
|
|
32
37
|
prefix?: string | null;
|
|
33
38
|
firstName: string;
|
|
@@ -43,6 +48,9 @@ export interface ExpertEntry extends BaseEntry {
|
|
|
43
48
|
html: string;
|
|
44
49
|
} | null;
|
|
45
50
|
organizations?: ExpertOrganization[];
|
|
51
|
+
areasOfExpertise?: ExpertCategory[] | null;
|
|
52
|
+
campusUnits?: ExpertCategory[] | null;
|
|
53
|
+
tags?: ExpertCategory[] | null;
|
|
46
54
|
email?: string | null;
|
|
47
55
|
linkedin?: string | null;
|
|
48
56
|
website?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/types/data/experts.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAO3C,MAAM,WAAW,UAAU;IAEzB,KAAK,EAAE,MAAM,CAAC;IAEd,IAAI,CAAC,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,IAAI,CAAC;CACV;AAOD,MAAM,WAAW,SAAS;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;CACnC;AAOD,MAAM,WAAW,kBAAkB;IAEjC,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAOD,MAAM,WAAW,aAAa;IAE5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAOD,MAAM,WAAW,YAAY;IAE3B,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAE5B,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B;AAQD,MAAM,WAAW,WAAY,SAAQ,SAAS;IAE5C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B,QAAQ,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAE1B,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IAET,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IAET,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAErC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B"}
|
|
1
|
+
{"version":3,"file":"experts.d.ts","sourceRoot":"","sources":["../../../source/types/data/experts.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAO3C,MAAM,WAAW,UAAU;IAEzB,KAAK,EAAE,MAAM,CAAC;IAEd,IAAI,CAAC,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,IAAI,CAAC;CACV;AAOD,MAAM,WAAW,SAAS;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;CACnC;AAOD,MAAM,WAAW,kBAAkB;IAEjC,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAOD,MAAM,WAAW,aAAa;IAE5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAOD,MAAM,WAAW,YAAY;IAE3B,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAE5B,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B;AAOD,MAAM,WAAW,cAAc;IAE7B,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAQD,MAAM,WAAW,WAAY,SAAQ,SAAS;IAE5C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B,QAAQ,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAE1B,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IAET,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IAET,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAErC,gBAAgB,CAAC,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAE3C,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAEtC,IAAI,CAAC,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAE/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@universityofmaryland/web-feeds-library",
|
|
3
|
-
"version": "1.3.0-beta.
|
|
3
|
+
"version": "1.3.0-beta.3",
|
|
4
4
|
"description": "UMD Feed Elements",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -57,10 +57,12 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@types/postcss-js": "^4.0.4",
|
|
60
|
+
"@universityofmaryland/web-token-library": "^1.0.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
60
63
|
"@universityofmaryland/web-builder-library": "^1.0.0",
|
|
61
64
|
"@universityofmaryland/web-elements-library": "^1.0.0",
|
|
62
65
|
"@universityofmaryland/web-styles-library": "^1.0.0",
|
|
63
|
-
"@universityofmaryland/web-token-library": "^1.0.0",
|
|
64
66
|
"@universityofmaryland/web-utilities-library": "^1.0.0"
|
|
65
67
|
},
|
|
66
68
|
"devDependencies": {
|