@o3r/components
Version:
This module contains component-related features (Component replacement, CMS compatibility, helpers, pipes, debugging developer tools...) It comes with an integrated ng builder to help you generate components compatible with Otter features (CMS integration
1 lines • 106 kB
Source Map (JSON)
{"version":3,"file":"o3r-components.mjs","sources":["../../src/core/rendering/helpers.ts","../../src/devkit/components-devkit.interface.ts","../../src/stores/placeholder-request/placeholder-request.actions.ts","../../src/stores/placeholder-request/placeholder-request.reducer.ts","../../src/stores/placeholder-request/placeholder-request.state.ts","../../src/stores/placeholder-request/placeholder-request.module.ts","../../src/stores/placeholder-request/placeholder-request.selectors.ts","../../src/stores/placeholder-request/placeholder-request.sync.ts","../../src/stores/placeholder-template/placeholder-template.actions.ts","../../src/stores/placeholder-template/placeholder-template.reducer.ts","../../src/stores/placeholder-template/placeholder-template.state.ts","../../src/stores/placeholder-template/placeholder-template.module.ts","../../src/stores/placeholder-template/placeholder-template.selectors.ts","../../src/stores/placeholder-template/placeholder-template.sync.ts","../../src/devkit/components-devtools.token.ts","../../src/devkit/inspector/otter-inspector.helpers.ts","../../src/devkit/inspector/otter-inspector.service.ts","../../src/devkit/components-devtools.message.service.ts","../../src/devkit/components-devtools.module.ts","../../src/tools/component-replacement/c11n.directive.ts","../../src/tools/component-replacement/c11n.helpers.ts","../../src/tools/component-replacement/c11n.token.ts","../../src/tools/component-replacement/c11n.service.ts","../../src/tools/component-replacement/c11n.mock.module.ts","../../src/tools/component-replacement/c11n.module.ts","../../src/tools/pipes/capitalize/capitalize.pipe.ts","../../src/tools/pipes/duration/duration.model.ts","../../src/tools/pipes/duration/duration.pipe.ts","../../src/tools/pipes/keep-white-space/keep-white-space.pipe.ts","../../src/tools/pipes/replace-with-bold/replace-with-bold.pipe.ts","../../src/tools/placeholder/placeholder.component.ts","../../src/tools/placeholder/placeholder.template.html","../../src/tools/placeholder/placeholder.module.ts","../../src/o3r-components.ts"],"sourcesContent":["import {\n animationFrameScheduler,\n from,\n Observable,\n observeOn,\n of,\n} from 'rxjs';\nimport {\n bufferCount,\n concatMap,\n delay,\n mergeMap,\n scan,\n tap,\n} from 'rxjs/operators';\n\n/**\n * Buffers and emits data for lazy/progressive rendering of big lists\n * That could solve issues with long-running tasks when trying to render an array\n * of similar components.\n * @param delayMs Delay between data emits\n * @param concurrency Amount of elements that should be emitted at once\n */\nexport function lazyArray<T>(delayMs = 0, concurrency = 2) {\n let isFirstEmission = true;\n return (source$: Observable<T[]>) => {\n return source$.pipe(\n mergeMap((items) => {\n if (!isFirstEmission) {\n return of(items);\n }\n\n const items$ = from(items);\n\n return items$.pipe(\n bufferCount(concurrency),\n concatMap((value, index) => {\n return of(value).pipe(\n observeOn(animationFrameScheduler),\n delay(index * delayMs)\n );\n }),\n scan((acc: T[], steps: T[]) => {\n return [...acc, ...steps];\n }, []),\n tap((scannedItems: T[]) => {\n const scanDidComplete = scannedItems.length === items.length;\n\n if (scanDidComplete) {\n isFirstEmission = false;\n }\n })\n );\n })\n );\n };\n}\n","import type {\n ConnectContentMessage,\n DevtoolsCommonOptions,\n MessageDataTypes,\n OtterMessageContent,\n RequestMessagesContentMessage,\n} from '@o3r/core';\nimport type {\n PlaceholderMode,\n} from '../stores';\nimport {\n OtterLikeComponentInfo,\n} from './inspector';\n\n/**\n * Component Devtools options\n */\nexport interface ComponentsDevtoolsServiceOptions extends DevtoolsCommonOptions {\n}\n\n/**\n * Message to give the selected component information\n */\nexport interface SelectedComponentInfoMessage extends OtterLikeComponentInfo, OtterMessageContent<'selectedComponentInfo'> {\n}\n\n/**\n * Message to toggle the inspector\n */\nexport interface ToggleInspectorMessage extends OtterMessageContent<'toggleInspector'> {\n /** Is the inspector running */\n isRunning: boolean;\n}\n\n/**\n * Message to toggle the placeholder mode\n */\nexport interface PlaceholderModeMessage extends OtterMessageContent<'placeholderMode'> {\n /** Placeholder mode */\n mode: PlaceholderMode;\n}\n\n/**\n * Message to know the component selection availability\n */\nexport interface IsComponentSelectionAvailableMessage extends OtterMessageContent<'isComponentSelectionAvailable'> {\n available: boolean;\n}\n\ntype ComponentsMessageContents =\n | IsComponentSelectionAvailableMessage\n | SelectedComponentInfoMessage\n | ToggleInspectorMessage\n | PlaceholderModeMessage;\n\n/** List of possible DataTypes for Components messages */\nexport type ComponentsMessageDataTypes = MessageDataTypes<ComponentsMessageContents>;\n\n/** List of all messages for Components purpose */\nexport type AvailableComponentsMessageContents =\n | ComponentsMessageContents\n | ConnectContentMessage\n | RequestMessagesContentMessage<ComponentsMessageDataTypes>;\n\n/**\n * Determine if the given message is a Components message\n * @param message message to check\n */\nexport const isComponentsMessage = (message: any): message is AvailableComponentsMessageContents => {\n return message && (\n message.dataType === 'requestMessages'\n || message.dataType === 'connect'\n || message.dataType === 'selectedComponentInfo'\n || message.dataType === 'isComponentSelectionAvailable'\n || message.dataType === 'placeholderMode'\n || message.dataType === 'toggleInspector'\n );\n};\n","import {\n createAction,\n props,\n} from '@ngrx/store';\nimport {\n asyncProps,\n AsyncRequest,\n FailAsyncStoreItemEntitiesActionPayload,\n FromApiActionPayload,\n UpdateAsyncStoreItemEntityActionPayloadWithId,\n UpdateEntityActionPayloadWithId,\n} from '@o3r/core';\nimport {\n PlaceholderRequestModel,\n PlaceholderRequestReply,\n} from './placeholder-request.state';\n\nconst ACTION_FAIL_ENTITIES = '[PlaceholderRequest] fail entities';\nconst ACTION_SET_ENTITY_FROM_URL = '[PlaceholderRequest] set entity from url';\nconst ACTION_CANCEL_REQUEST = '[PlaceholderRequest] cancel request';\nconst ACTION_UPDATE_ENTITY = '[PlaceholderRequest] update entity';\nconst ACTION_UPDATE_ENTITY_SYNC = '[PlaceholderRequest] update entity sync';\n\n/** Action to cancel a Request ID registered in the store. Can happen from effect based on a switchMap for instance */\nexport const cancelPlaceholderRequest = createAction(ACTION_CANCEL_REQUEST, props<AsyncRequest & { id: string }>());\n\n/** Action to update failureStatus for PlaceholderRequestModels */\nexport const failPlaceholderRequestEntity = createAction(ACTION_FAIL_ENTITIES, props<FailAsyncStoreItemEntitiesActionPayload<any>>());\n\n/** Action to update an entity */\nexport const updatePlaceholderRequestEntity = createAction(ACTION_UPDATE_ENTITY, props<UpdateAsyncStoreItemEntityActionPayloadWithId<PlaceholderRequestModel>>());\n\n/** Action to update an entity without impact on request id */\nexport const updatePlaceholderRequestEntitySync = createAction(ACTION_UPDATE_ENTITY_SYNC, props<UpdateEntityActionPayloadWithId<PlaceholderRequestModel>>());\n\n/** Action to update PlaceholderRequest with known IDs, will create the entity with only the url, the call will be created in the effect */\nexport const setPlaceholderRequestEntityFromUrl = createAction(ACTION_SET_ENTITY_FROM_URL, asyncProps<FromApiActionPayload<PlaceholderRequestReply> & { resolvedUrl: string; id: string }>());\n","import {\n createEntityAdapter,\n} from '@ngrx/entity';\nimport {\n ActionCreator,\n createReducer,\n on,\n ReducerTypes,\n} from '@ngrx/store';\nimport {\n asyncStoreItemAdapter,\n createEntityAsyncRequestAdapter,\n} from '@o3r/core';\nimport * as actions from './placeholder-request.actions';\nimport {\n PlaceholderRequestModel,\n PlaceholderRequestState,\n PlaceholderRequestStateDetails,\n} from './placeholder-request.state';\n\n/**\n * PlaceholderRequest Store adapter\n */\nexport const placeholderRequestAdapter = createEntityAsyncRequestAdapter(createEntityAdapter<PlaceholderRequestModel>({\n selectId: (model) => model.id\n}));\n\n/**\n * PlaceholderRequest Store initial value\n */\nexport const placeholderRequestInitialState: PlaceholderRequestState = placeholderRequestAdapter.getInitialState<PlaceholderRequestStateDetails>({\n requestIds: []\n});\n\n/**\n * Reducers of Placeholder request store that handles the call to the placeholder template URL\n */\nexport const placeholderRequestReducerFeatures: ReducerTypes<PlaceholderRequestState, ActionCreator[]>[] = [\n on(actions.cancelPlaceholderRequest, (state, action) => {\n const id = action.id;\n if (!id || !state.entities[id]) {\n return state;\n }\n return placeholderRequestAdapter.updateOne({\n id: action.id,\n changes: asyncStoreItemAdapter.resolveRequest(state.entities[id], action.requestId)\n }, asyncStoreItemAdapter.resolveRequest(state, action.requestId));\n }),\n on(actions.updatePlaceholderRequestEntity, (state, action) => {\n const currentEntity = state.entities[action.entity.id]!;\n const newEntity = asyncStoreItemAdapter.resolveRequest({ ...action.entity, ...asyncStoreItemAdapter.extractAsyncStoreItem(currentEntity) }, action.requestId);\n return placeholderRequestAdapter.updateOne({\n id: newEntity.id,\n changes: newEntity\n }, asyncStoreItemAdapter.resolveRequest(state, action.requestId));\n }),\n on(actions.updatePlaceholderRequestEntitySync, (state, action) => {\n return placeholderRequestAdapter.updateOne({\n id: action.entity.id,\n changes: {\n ...action.entity\n }\n }, state);\n }),\n on(actions.setPlaceholderRequestEntityFromUrl, (state, payload) => {\n const currentEntity = state.entities[payload.id];\n // Nothing to update if resolved URLs already match\n if (currentEntity && currentEntity.resolvedUrl === payload.resolvedUrl) {\n return state;\n }\n let newEntity = {\n id: payload.id,\n resolvedUrl: payload.resolvedUrl,\n used: true\n };\n if (currentEntity) {\n newEntity = { ...asyncStoreItemAdapter.extractAsyncStoreItem(currentEntity), ...newEntity };\n }\n return placeholderRequestAdapter.addOne(\n asyncStoreItemAdapter.addRequest(\n asyncStoreItemAdapter.initialize(newEntity),\n payload.requestId),\n asyncStoreItemAdapter.addRequest(state, payload.requestId)\n );\n }),\n on(actions.failPlaceholderRequestEntity, (state, payload) => {\n return placeholderRequestAdapter.failRequestMany(asyncStoreItemAdapter.resolveRequest(state, payload.requestId), payload && payload.ids, payload.requestId);\n })\n];\n\n/**\n * PlaceholderRequest Store reducer\n */\nexport const placeholderRequestReducer = createReducer(\n placeholderRequestInitialState,\n ...placeholderRequestReducerFeatures\n);\n","import {\n EntityState,\n} from '@ngrx/entity';\nimport {\n AsyncStoreItem,\n} from '@o3r/core';\n\n/**\n * Variable model from the placeholder reply\n */\nexport interface PlaceholderVariable {\n type: 'fact' | 'fullUrl' | 'relativeUrl' | 'localisation';\n value: string;\n parameters?: Record<string, string>;\n path?: string;\n}\n\n/**\n * Raw JSON template coming back from the CMS or any other source\n */\nexport interface PlaceholderRequestReply {\n template?: string;\n vars?: Record<string, PlaceholderVariable>;\n}\n\n/**\n * PlaceholderRequest model\n */\nexport interface PlaceholderRequestModel extends AsyncStoreItem, PlaceholderRequestReply {\n /** Raw URL that is not localized, ex: my_url/[LANGUAGE]/my_placeholder.json */\n id: string;\n /** Resolved URL that is localized, ex: my_url/en-GB/my_placeholder.json */\n resolvedUrl: string;\n /** Rendered template associated to the resolved URL, can be dynamic */\n renderedTemplate?: string;\n /** Unknown type found in the reply */\n unknownTypeFound?: boolean;\n\n /** A mechanism to cache previous request results for a given language. This boolean disables the dynamic rendering when it is set to false */\n used?: boolean;\n}\n\n/**\n * PlaceholderRequest state details\n */\nexport interface PlaceholderRequestStateDetails extends AsyncStoreItem {}\n\n/**\n * PlaceholderRequest store state\n */\nexport interface PlaceholderRequestState extends EntityState<PlaceholderRequestModel>, PlaceholderRequestStateDetails {\n}\n\n/**\n * Name of the PlaceholderRequest Store\n */\nexport const PLACEHOLDER_REQUEST_STORE_NAME = 'placeholderRequest';\n\n/**\n * PlaceholderRequest Store Interface\n */\nexport interface PlaceholderRequestStore {\n /** PlaceholderRequest state */\n [PLACEHOLDER_REQUEST_STORE_NAME]: PlaceholderRequestState;\n}\n","import {\n InjectionToken,\n ModuleWithProviders,\n NgModule,\n} from '@angular/core';\nimport {\n Action,\n ActionReducer,\n StoreModule,\n} from '@ngrx/store';\nimport {\n placeholderRequestReducer,\n} from './placeholder-request.reducer';\nimport {\n PLACEHOLDER_REQUEST_STORE_NAME,\n PlaceholderRequestState,\n} from './placeholder-request.state';\n\n/** Token of the PlaceholderRequest reducer */\nexport const PLACEHOLDER_REQUEST_REDUCER_TOKEN = new InjectionToken<ActionReducer<PlaceholderRequestState, Action>>('Feature PlaceholderRequest Reducer');\n\n/** Provide default reducer for PlaceholderRequest store */\nexport function getDefaultplaceholderRequestReducer() {\n return placeholderRequestReducer;\n}\n\n@NgModule({\n imports: [\n StoreModule.forFeature(PLACEHOLDER_REQUEST_STORE_NAME, PLACEHOLDER_REQUEST_REDUCER_TOKEN)\n ],\n providers: [\n { provide: PLACEHOLDER_REQUEST_REDUCER_TOKEN, useFactory: getDefaultplaceholderRequestReducer }\n ]\n})\nexport class PlaceholderRequestStoreModule {\n public static forRoot<T extends PlaceholderRequestState>(reducerFactory: () => ActionReducer<T, Action>): ModuleWithProviders<PlaceholderRequestStoreModule> {\n return {\n ngModule: PlaceholderRequestStoreModule,\n providers: [\n { provide: PLACEHOLDER_REQUEST_REDUCER_TOKEN, useFactory: reducerFactory }\n ]\n };\n }\n}\n","import {\n createFeatureSelector,\n createSelector,\n} from '@ngrx/store';\nimport {\n placeholderRequestAdapter,\n} from './placeholder-request.reducer';\nimport {\n PLACEHOLDER_REQUEST_STORE_NAME,\n PlaceholderRequestState,\n} from './placeholder-request.state';\n\nexport const selectPlaceholderRequestState = createFeatureSelector<PlaceholderRequestState>(PLACEHOLDER_REQUEST_STORE_NAME);\n\nconst { selectEntities } = placeholderRequestAdapter.getSelectors();\n\n/** Select the dictionary of PlaceholderRequest entities */\nexport const selectPlaceholderRequestEntities = createSelector(selectPlaceholderRequestState, (state) => state && selectEntities(state));\n\n/**\n * Select a specific PlaceholderRequest entity using a raw url as id\n * @param rawUrl\n */\nexport const selectPlaceholderRequestEntityUsage = (rawUrl: string) => createSelector(\n selectPlaceholderRequestState,\n (state) => {\n return state?.entities[rawUrl] ? state.entities[rawUrl].used : undefined;\n });\n","import {\n asyncEntitySerializer,\n Serializer,\n} from '@o3r/core';\nimport {\n placeholderRequestAdapter,\n placeholderRequestInitialState,\n} from './placeholder-request.reducer';\nimport {\n PlaceholderRequestModel,\n PlaceholderRequestState,\n} from './placeholder-request.state';\n\nexport const placeholderRequestStorageSerializer = asyncEntitySerializer;\n\nexport const placeholderRequestStorageDeserializer = (rawObject: any) => {\n if (!rawObject || !rawObject.ids) {\n return placeholderRequestInitialState;\n }\n const storeObject = placeholderRequestAdapter.getInitialState(rawObject);\n for (const id of rawObject.ids) {\n storeObject.entities[id] = rawObject.entities[id] as PlaceholderRequestModel;\n }\n return storeObject;\n};\n\nexport const placeholderRequestStorageSync: Readonly<Serializer<PlaceholderRequestState>> = {\n serialize: placeholderRequestStorageSerializer,\n deserialize: placeholderRequestStorageDeserializer\n} as const;\n","import {\n createAction,\n props,\n} from '@ngrx/store';\nimport {\n SetEntityActionPayload,\n} from '@o3r/core';\nimport {\n type PlaceholderMode,\n PlaceholderTemplateModel,\n} from './placeholder-template.state';\n\nconst ACTION_DELETE_ENTITY = '[PlaceholderTemplate] delete entity';\nconst ACTION_SET_ENTITY = '[PlaceholderTemplate] set entity';\nconst ACTION_TOGGLE_MODE = '[PlaceholderTemplate] toggle mode';\n\n/** Action to delete a specific entity */\nexport const deletePlaceholderTemplateEntity = createAction(ACTION_DELETE_ENTITY, props<{ id: string }>());\n\n/** Action to clear all placeholderTemplate and fill the store with the payload */\nexport const setPlaceholderTemplateEntity = createAction(ACTION_SET_ENTITY, props<SetEntityActionPayload<PlaceholderTemplateModel>>());\n\nexport const togglePlaceholderModeTemplate = createAction(ACTION_TOGGLE_MODE, props<{ mode: PlaceholderMode }>());\n","import {\n createEntityAdapter,\n} from '@ngrx/entity';\nimport {\n ActionCreator,\n createReducer,\n on,\n ReducerTypes,\n} from '@ngrx/store';\nimport * as actions from './placeholder-template.actions';\nimport {\n PlaceholderTemplateModel,\n PlaceholderTemplateState,\n} from './placeholder-template.state';\n\n/**\n * PlaceholderTemplate Store adapter\n */\nexport const placeholderTemplateAdapter = createEntityAdapter<PlaceholderTemplateModel>({\n selectId: (model) => model.id\n});\n\n/**\n * PlaceholderTemplate Store initial value\n */\nexport const placeholderTemplateInitialState: PlaceholderTemplateState = placeholderTemplateAdapter.getInitialState({\n mode: 'normal'\n});\n\n/**\n * List of basic actions for PlaceholderTemplate Store\n */\nexport const placeholderTemplateReducerFeatures: ReducerTypes<PlaceholderTemplateState, ActionCreator[]>[] = [\n on(actions.setPlaceholderTemplateEntity, (state, payload) =>\n placeholderTemplateAdapter.addOne(payload.entity, placeholderTemplateAdapter.removeOne(payload.entity.id, state))),\n on(actions.deletePlaceholderTemplateEntity, (state, payload) => {\n const id = payload.id;\n if (!id || !state.entities[id]) {\n return state;\n }\n return placeholderTemplateAdapter.removeOne(id, state);\n }),\n on(actions.togglePlaceholderModeTemplate, (state, payload) => {\n return {\n ...state,\n mode: payload.mode\n };\n })\n];\n\n/**\n * PlaceholderTemplate Store reducer\n */\nexport const placeholderTemplateReducer = createReducer(\n placeholderTemplateInitialState,\n ...placeholderTemplateReducerFeatures\n);\n","import {\n EntityState,\n} from '@ngrx/entity';\n\n/**\n * PlaceholderTemplate model\n */\nexport interface PlaceholderTemplateModel {\n /** Placeholder id that is unique*/\n id: string;\n /** Urls to the templates to be fetched, and priority for rendering order */\n urlsWithPriority: { rawUrl: string; priority: number }[];\n}\n\n/** Possible placeholder mode */\nexport type PlaceholderMode = 'normal' | 'debug' | 'pending';\n\n/**\n * PlaceholderTemplate store state\n */\nexport interface PlaceholderTemplateState extends EntityState<PlaceholderTemplateModel> {\n mode: PlaceholderMode;\n}\n\n/**\n * Name of the PlaceholderTemplate Store\n */\nexport const PLACEHOLDER_TEMPLATE_STORE_NAME = 'placeholderTemplate';\n\n/**\n * PlaceholderTemplate Store Interface\n */\nexport interface PlaceholderTemplateStore {\n /** PlaceholderTemplate state */\n [PLACEHOLDER_TEMPLATE_STORE_NAME]: PlaceholderTemplateState;\n}\n","import {\n InjectionToken,\n ModuleWithProviders,\n NgModule,\n} from '@angular/core';\nimport {\n Action,\n ActionReducer,\n StoreModule,\n} from '@ngrx/store';\nimport {\n placeholderTemplateReducer,\n} from './placeholder-template.reducer';\nimport {\n PLACEHOLDER_TEMPLATE_STORE_NAME,\n PlaceholderTemplateState,\n} from './placeholder-template.state';\n\n/** Token of the PlaceholderTemplate reducer */\nexport const PLACEHOLDER_TEMPLATE_REDUCER_TOKEN = new InjectionToken<ActionReducer<PlaceholderTemplateState, Action>>('Feature PlaceholderTemplate Reducer');\n\n/** Provide default reducer for PlaceholderTemplate store */\nexport function getDefaultPlaceholderTemplateReducer() {\n return placeholderTemplateReducer;\n}\n\n@NgModule({\n imports: [\n StoreModule.forFeature(PLACEHOLDER_TEMPLATE_STORE_NAME, PLACEHOLDER_TEMPLATE_REDUCER_TOKEN)\n ],\n providers: [\n { provide: PLACEHOLDER_TEMPLATE_REDUCER_TOKEN, useFactory: getDefaultPlaceholderTemplateReducer }\n ]\n})\nexport class PlaceholderTemplateStoreModule {\n public static forRoot<T extends PlaceholderTemplateState>(reducerFactory: () => ActionReducer<T, Action>): ModuleWithProviders<PlaceholderTemplateStoreModule> {\n return {\n ngModule: PlaceholderTemplateStoreModule,\n providers: [\n { provide: PLACEHOLDER_TEMPLATE_REDUCER_TOKEN, useFactory: reducerFactory }\n ]\n };\n }\n}\n","import {\n createFeatureSelector,\n createSelector,\n} from '@ngrx/store';\nimport {\n selectPlaceholderRequestState,\n} from '../placeholder-request';\nimport {\n placeholderTemplateAdapter,\n} from './placeholder-template.reducer';\nimport {\n PLACEHOLDER_TEMPLATE_STORE_NAME,\n PlaceholderTemplateState,\n} from './placeholder-template.state';\n\nconst { selectEntities } = placeholderTemplateAdapter.getSelectors();\n\nexport const selectPlaceholderTemplateState = createFeatureSelector<PlaceholderTemplateState>(PLACEHOLDER_TEMPLATE_STORE_NAME);\n\n/** Select the dictionary of PlaceholderTemplate entities */\nexport const selectPlaceholderTemplateEntities = createSelector(selectPlaceholderTemplateState, (state) => state && selectEntities(state));\n\n/**\n * Select a specific PlaceholderTemplate\n * @param placeholderId\n */\nexport const selectPlaceholderTemplateEntity = (placeholderId: string) =>\n createSelector(selectPlaceholderTemplateState, (state) => state?.entities[placeholderId]);\n\n/**\n * Select the ordered rendered placeholder template full data (url, priority etc.) for a given placeholderId\n * Return undefined if the placeholder is not found\n * Returns {orderedRenderedTemplates: undefined, isPending: true} if any of the request is still pending\n * @param placeholderId\n */\nexport const selectSortedTemplates = (placeholderId: string) => createSelector(\n selectPlaceholderTemplateEntity(placeholderId),\n selectPlaceholderRequestState,\n (placeholderTemplate, placeholderRequestState) => {\n if (!placeholderTemplate || !placeholderRequestState) {\n return;\n }\n // The isPending will be considered true if any of the Url is still pending\n let isPending: boolean | undefined = false;\n const templates: { rawUrl: string; priority: number; renderedTemplate?: string; resolvedUrl: string }[] = [];\n placeholderTemplate.urlsWithPriority.forEach((urlWithPriority) => {\n const placeholderRequest = placeholderRequestState.entities[urlWithPriority.rawUrl];\n if (placeholderRequest) {\n // If one of the items is pending, we will wait to display all contents at the same time\n isPending = isPending || placeholderRequest.isPending;\n // Templates in failure will be ignored from the list\n if (!placeholderRequest.isFailure) {\n templates.push({\n rawUrl: urlWithPriority.rawUrl,\n resolvedUrl: placeholderRequest.resolvedUrl,\n priority: urlWithPriority.priority,\n renderedTemplate: placeholderRequest.renderedTemplate\n });\n }\n }\n });\n // No need to perform sorting if still pending\n if (isPending) {\n return { orderedTemplates: undefined, isPending };\n }\n // Sort templates by priority\n const orderedTemplates = templates.sort((template1, template2) => {\n return (template2.priority - template1.priority) || 1;\n }).filter((templateData) => !!templateData.renderedTemplate);\n\n return { orderedTemplates, isPending };\n });\n\n/**\n * Select the ordered rendered templates for a given placeholderId\n * Return undefined if the placeholder is not found\n * Returns {orderedRenderedTemplates: undefined, isPending: true} if any of the request is still pending\n * @param placeholderId\n * @deprecated Please use {@link selectSortedTemplates} instead\n */\nexport const selectPlaceholderRenderedTemplates = (placeholderId: string) => createSelector(\n selectSortedTemplates(placeholderId),\n (placeholderData) => {\n if (!placeholderData) {\n return;\n }\n return {\n orderedRenderedTemplates: placeholderData.orderedTemplates?.map((placeholder) => placeholder.renderedTemplate),\n isPending: placeholderData.isPending\n };\n });\n\nexport const selectPlaceholderTemplateMode = createSelector(selectPlaceholderTemplateState, (state) => state.mode);\n","import {\n Serializer,\n} from '@o3r/core';\nimport {\n placeholderTemplateAdapter,\n placeholderTemplateInitialState,\n} from './placeholder-template.reducer';\nimport {\n PlaceholderTemplateModel,\n PlaceholderTemplateState,\n} from './placeholder-template.state';\n\nexport const placeholderTemplateStorageDeserializer = (rawObject: any) => {\n if (!rawObject || !rawObject.ids) {\n return placeholderTemplateInitialState;\n }\n const storeObject = placeholderTemplateAdapter.getInitialState(rawObject);\n for (const id of rawObject.ids) {\n storeObject.entities[id] = rawObject.entities[id] as PlaceholderTemplateModel;\n }\n return storeObject;\n};\n\nexport const placeholderTemplateStorageSync: Readonly<Serializer<PlaceholderTemplateState>> = {\n deserialize: placeholderTemplateStorageDeserializer\n} as const;\n","import {\n InjectionToken,\n} from '@angular/core';\nimport {\n ComponentsDevtoolsServiceOptions,\n} from './components-devkit.interface';\n\nexport const OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS: Readonly<ComponentsDevtoolsServiceOptions> = {\n isActivatedOnBootstrap: false\n} as const;\n\nexport const OTTER_COMPONENTS_DEVTOOLS_OPTIONS = new InjectionToken<ComponentsDevtoolsServiceOptions>('Otter Components Devtools options');\n","import {\n otterComponentInfoPropertyName,\n} from '@o3r/core';\n\nexport interface OtterLikeComponentInfo {\n /** Container information */\n container?: OtterLikeComponentInfo;\n /** Configuration ID */\n configId?: string;\n /** Component name */\n componentName: string;\n /** Component translation */\n translations?: Record<string, string[]>;\n /** Component analytics */\n analytics?: Record<string, string[]>;\n}\n\n/**\n * Otter inspector css class\n */\nexport const INSPECTOR_CLASS = 'otter-devtools-inspector';\n\n/**\n * Determine if a node is an Otter container\n * @param node Element to check\n * @returns true if the node is an Otter container\n */\nexport const isContainer = (node: Element | undefined | null): node is Element => {\n return !!node?.tagName.toLowerCase().endsWith('cont');\n};\n\n/**\n * Determine the config id of a component instance\n * @param instance component instance\n * @returns the config id of the component instance\n */\nexport const getConfigId = (instance: any) => {\n return instance[otterComponentInfoPropertyName]?.configId;\n};\n\n/**\n * Recursive method to determin the translations of a node\n * @param node HTMLElement to check\n * @param rec recursive method\n * @returns the trasnslations associated to their component name\n */\nexport function getTranslationsRec(node: Element | null, rec: typeof getTranslationsRec): Record<string, string[]> | undefined {\n const angularDevTools = (window as any).ng;\n const o3rInfoProperty: typeof otterComponentInfoPropertyName = '__otter-info__';\n if (!node || !angularDevTools) {\n return;\n }\n const component = angularDevTools.getComponent(node);\n const subTranslations: (Record<string, string[]> | undefined)[] = Array.from(node.children).map((child) => rec(child, rec));\n const translations: Record<string, string[]> = {};\n subTranslations.forEach((s) => {\n Object.entries(s || {}).forEach(([k, v]) => {\n if (v.length > 0) {\n translations[k] = v;\n }\n });\n });\n if (component) {\n const componentName: string = component.constructor.name;\n const componentTranslations = Object.values<string>(component[o3rInfoProperty]?.translations || {}).filter((t) => typeof t === 'string');\n if (componentTranslations.length > 0) {\n translations[componentName] = componentTranslations;\n }\n }\n return Object.keys(translations).length > 0 ? translations : undefined;\n}\n\n/**\n * Determine the translations of a node\n * @param node HTMLElement to check\n * @returns the translations associated to their component name\n */\nexport const getTranslations = (node: Element | null): Record<string, string[]> | undefined => getTranslationsRec(node, getTranslations);\n\n/**\n * Recursive method to determine the analytics of a node\n * @param node Element to check\n * @param rec recursive method\n * @returns the analytics associated to their component name\n */\nexport function getAnalyticEventsRec(node: Element | null, rec: typeof getAnalyticEventsRec): Record<string, string[]> | undefined {\n const angularDevTools = (window as any).ng;\n const o3rInfoProperty: typeof otterComponentInfoPropertyName = '__otter-info__';\n if (!node || !angularDevTools) {\n return;\n }\n const component = angularDevTools.getComponent(node);\n const subEvents = Array.from(node.children).map((child) => rec(child, rec));\n const events: Record<string, string[]> = {};\n subEvents.forEach((s) => {\n Object.entries(s || {}).forEach(([k, v]) => {\n if (v.length > 0) {\n events[k] = v;\n }\n });\n });\n if (component && component[o3rInfoProperty]) {\n const componentName: string = component.constructor.name;\n const componentEvents: string[] = Object.values<any>(component.analyticsEvents || {}).map((eventConstructor) => eventConstructor.name);\n if (componentEvents.length > 0) {\n events[componentName] = componentEvents;\n }\n }\n return Object.keys(events).length > 0 ? events : undefined;\n}\n\n/**\n * Determine the analytics of a node\n * @param node Element to check\n * @returns the analytics associated to their component name\n */\nexport const getAnalyticEvents = (node: Element | null): Record<string, string[]> | undefined => getAnalyticEventsRec(node, getAnalyticEvents);\n\n/**\n * Determine all info from an Otter component\n * @param componentClassInstance component instance\n * @param host HTML element hosting the component\n * @returns all info from an Otter component\n */\nexport const getOtterLikeComponentInfo = (componentClassInstance: any, host: Element): OtterLikeComponentInfo => {\n const configId = getConfigId(componentClassInstance);\n const translations = getTranslations(host);\n const analytics = getAnalyticEvents(host);\n return {\n // Cannot use anymore `constructor.name` else all components are named `_a`\n componentName: componentClassInstance.constructor.ɵcmp?.debugInfo?.className || componentClassInstance.constructor.name,\n configId,\n translations,\n analytics\n };\n};\n","import {\n BehaviorSubject,\n Observable,\n} from 'rxjs';\nimport {\n Ng,\n} from './ng';\nimport {\n getOtterLikeComponentInfo,\n INSPECTOR_CLASS,\n isContainer,\n OtterLikeComponentInfo,\n} from './otter-inspector.helpers';\n\ninterface ComponentInfo extends OtterLikeComponentInfo {\n component: any;\n host: Element;\n}\n\n/**\n * Service to handle the custom inspector for the Otter Devtools Chrome extension.\n */\nexport class OtterInspectorService {\n private readonly angularDevTools: Ng | undefined;\n private readonly elementMouseOverCallback = this.elementMouseOver.bind(this);\n private readonly elementClickCallback = this.elementClick.bind(this);\n private readonly cancelEventCallback = this.cancelEvent.bind(this);\n private selectedComponent: ComponentInfo | undefined;\n private inspectorDiv: HTMLDivElement | null = null;\n private readonly otterLikeComponentInfoToBeSent = new BehaviorSubject<OtterLikeComponentInfo | undefined>(undefined);\n\n /**\n * Stream of component info to be sent to the extension app.\n */\n public otterLikeComponentInfoToBeSent$: Observable<OtterLikeComponentInfo | undefined> = this.otterLikeComponentInfoToBeSent.asObservable();\n\n constructor() {\n this.angularDevTools = (window as any).ng as Ng | undefined;\n }\n\n private startInspecting() {\n window.addEventListener('mouseover', this.elementMouseOverCallback, true);\n window.addEventListener('click', this.elementClickCallback, true);\n window.addEventListener('mouseout', this.cancelEventCallback, true);\n }\n\n private elementClick(e: MouseEvent) {\n e.stopImmediatePropagation();\n e.stopPropagation();\n e.preventDefault();\n if (!this.selectedComponent || !this.angularDevTools) {\n return;\n }\n const parentElement = this.selectedComponent.host.parentElement;\n const parent = parentElement && (this.angularDevTools.getComponent(parentElement) || this.angularDevTools.getOwningComponent(parentElement));\n const parentHost = parent && this.angularDevTools.getHostElement(parent);\n const container = isContainer(parentHost)\n ? getOtterLikeComponentInfo(parent, parentHost)\n : undefined;\n\n const { component, host, ...infoToBeSent } = this.selectedComponent;\n\n this.otterLikeComponentInfoToBeSent.next({\n ...infoToBeSent,\n container\n });\n }\n\n private isOtterLikeComponent(info: OtterLikeComponentInfo) {\n const hasConfigId = !!info.configId;\n const hasTranslations = !!info.translations?.length;\n const hasAnalytics = Object.keys(info.analytics || {}).length > 0;\n return hasConfigId || hasTranslations || hasAnalytics;\n }\n\n private findComponentInfo(node: Element): ComponentInfo | undefined {\n if (!this.angularDevTools) {\n return;\n }\n let componentClassInstance = this.angularDevTools.getComponent(node) || this.angularDevTools.getOwningComponent(node);\n let o3rLikeComponentInfo: OtterLikeComponentInfo;\n let isO3rLike: boolean;\n if (!componentClassInstance) {\n return;\n }\n do {\n o3rLikeComponentInfo = getOtterLikeComponentInfo(componentClassInstance, this.angularDevTools.getHostElement(componentClassInstance));\n isO3rLike = this.isOtterLikeComponent(o3rLikeComponentInfo);\n if (!isO3rLike) {\n componentClassInstance = this.angularDevTools.getOwningComponent(componentClassInstance);\n }\n } while (!isO3rLike && componentClassInstance);\n if (isO3rLike) {\n return {\n component: componentClassInstance,\n host: this.angularDevTools.getHostElement(componentClassInstance),\n ...o3rLikeComponentInfo\n };\n }\n }\n\n private elementMouseOver(e: MouseEvent) {\n this.cancelEvent(e);\n\n const el = e.target as Element | undefined;\n\n if (el) {\n const selectedComponent = this.findComponentInfo(el);\n if (selectedComponent?.host !== this.selectedComponent?.host) {\n this.unHighlight();\n this.highlight(selectedComponent);\n }\n }\n }\n\n private highlight(selectedComponent: ComponentInfo | undefined) {\n this.selectedComponent = selectedComponent;\n if (this.selectedComponent?.host && this.inspectorDiv) {\n const rect = this.selectedComponent.host.getBoundingClientRect();\n this.inspectorDiv.style.width = `${rect.width}px`;\n this.inspectorDiv.style.height = `${rect.height}px`;\n this.inspectorDiv.style.top = `${rect.top}px`;\n this.inspectorDiv.style.left = `${rect.left}px`;\n this.inspectorDiv.firstChild!.textContent = `<${this.selectedComponent.componentName}>`;\n }\n }\n\n private unHighlight() {\n if (this.selectedComponent?.host && this.inspectorDiv) {\n this.inspectorDiv.style.width = '0';\n this.inspectorDiv.style.height = '0';\n this.inspectorDiv.style.top = '0';\n this.inspectorDiv.style.left = '0';\n }\n this.selectedComponent = undefined;\n }\n\n private cancelEvent(e: MouseEvent): void {\n e.stopPropagation();\n e.stopImmediatePropagation();\n e.preventDefault();\n }\n\n /**\n * Prepare the inspector div and add it to the DOM.\n */\n public prepareInspector() {\n if (this.inspectorDiv) {\n return;\n }\n const inspectorCss = document.createElement('style');\n inspectorCss.textContent = `\n .${INSPECTOR_CLASS} {\n z-index: 9999999;\n width: 0;\n height: 0;\n background: rgba(104, 182, 255, 0.35);\n position: fixed;\n left: 0;\n top: 0;\n pointer-events: none;\n }\n\n .${INSPECTOR_CLASS} > span {\n bottom: -25px;\n position: absolute;\n right: 10px;\n background: rgba(104, 182, 255, 0.9);;\n padding: 5px;\n border-radius: 5px;\n color: white;\n }`;\n\n const inspectorDiv = document.createElement('div');\n const inspectorSpan = document.createElement('span');\n inspectorDiv.append(inspectorSpan);\n inspectorDiv.classList.add(INSPECTOR_CLASS);\n\n document.head.append(inspectorCss);\n document.body.append(inspectorDiv);\n this.inspectorDiv = inspectorDiv;\n }\n\n /**\n * Toggle the inspector.\n * @param isRunning true if the inspector is running\n */\n public toggleInspector(isRunning: boolean) {\n if (isRunning) {\n this.startInspecting();\n } else {\n this.stopInspecting();\n }\n }\n\n public stopInspecting() {\n this.unHighlight();\n window.removeEventListener('mouseover', this.elementMouseOverCallback, true);\n window.removeEventListener('click', this.elementClickCallback, true);\n window.removeEventListener('mouseout', this.cancelEventCallback, true);\n }\n}\n","import {\n DestroyRef,\n inject,\n Inject,\n Injectable,\n Optional,\n} from '@angular/core';\nimport {\n takeUntilDestroyed,\n} from '@angular/core/rxjs-interop';\nimport {\n Store,\n} from '@ngrx/store';\nimport {\n DevtoolsServiceInterface,\n filterMessageContent,\n sendOtterMessage,\n} from '@o3r/core';\nimport {\n LoggerService,\n} from '@o3r/logger';\nimport {\n firstValueFrom,\n fromEvent,\n} from 'rxjs';\nimport {\n filter,\n} from 'rxjs/operators';\nimport {\n type PlaceholderTemplateState,\n togglePlaceholderModeTemplate,\n} from '../stores';\nimport {\n AvailableComponentsMessageContents,\n ComponentsDevtoolsServiceOptions,\n ComponentsMessageDataTypes,\n isComponentsMessage,\n} from './components-devkit.interface';\nimport {\n OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS,\n OTTER_COMPONENTS_DEVTOOLS_OPTIONS,\n} from './components-devtools.token';\nimport {\n OtterInspectorService,\n OtterLikeComponentInfo,\n} from './inspector';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ComponentsDevtoolsMessageService implements DevtoolsServiceInterface {\n private readonly options: ComponentsDevtoolsServiceOptions;\n private readonly inspectorService: OtterInspectorService;\n private readonly sendMessage = sendOtterMessage<AvailableComponentsMessageContents>;\n private readonly destroyRef = inject(DestroyRef);\n\n constructor(\n private readonly logger: LoggerService,\n private readonly store: Store<PlaceholderTemplateState>,\n @Optional() @Inject(OTTER_COMPONENTS_DEVTOOLS_OPTIONS) options?: ComponentsDevtoolsServiceOptions\n ) {\n this.options = {\n ...OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS,\n ...options\n };\n\n this.inspectorService = new OtterInspectorService();\n if (this.options.isActivatedOnBootstrap) {\n this.activate();\n }\n }\n\n /**\n * Function to connect the plugin to the Otter DevTools extension\n */\n private async connectPlugin() {\n this.logger.debug('Otter DevTools is plugged to components service of the application');\n const selectComponentInfo = await firstValueFrom(this.inspectorService.otterLikeComponentInfoToBeSent$);\n if (selectComponentInfo) {\n await this.sendCurrentSelectedComponent();\n }\n }\n\n private async sendCurrentSelectedComponent() {\n const selectComponentInfo = await firstValueFrom(this.inspectorService.otterLikeComponentInfoToBeSent$);\n if (selectComponentInfo) {\n this.sendMessage('selectedComponentInfo', selectComponentInfo);\n }\n }\n\n private sendIsComponentSelectionAvailable() {\n this.sendMessage('isComponentSelectionAvailable', { available: !!(window as any).ng });\n }\n\n /**\n * Function to trigger a re-send a requested messages to the Otter Chrome DevTools extension\n * @param only restricted list of messages to re-send\n */\n private handleReEmitRequest(only?: ComponentsMessageDataTypes[]) {\n if (!only) {\n void this.sendCurrentSelectedComponent();\n this.sendIsComponentSelectionAvailable();\n return;\n }\n if (only.includes('selectedComponentInfo')) {\n void this.sendCurrentSelectedComponent();\n }\n if (only.includes('isComponentSelectionAvailable')) {\n this.sendIsComponentSelectionAvailable();\n }\n }\n\n /**\n * Function to handle the incoming messages from Otter Chrome DevTools extension\n * @param message message coming from the Otter Chrome DevTools extension\n */\n private async handleEvents(message: AvailableComponentsMessageContents) {\n this.logger.debug('Message handling by the components service', message);\n\n switch (message.dataType) {\n case 'connect': {\n await this.connectPlugin();\n break;\n }\n case 'requestMessages': {\n this.handleReEmitRequest(message.only);\n break;\n }\n case 'toggleInspector': {\n this.inspectorService.toggleInspector(message.isRunning);\n break;\n }\n case 'placeholderMode': {\n this.store.dispatch(togglePlaceholderModeTemplate({ mode: message.mode }));\n break;\n }\n default: {\n this.logger.warn('Message ignored by the components service', message);\n }\n }\n }\n\n /** @inheritDoc */\n public activate() {\n fromEvent(window, 'message').pipe(takeUntilDestroyed(this.destroyRef), filterMessageContent(isComponentsMessage)).subscribe((e) => this.handleEvents(e));\n\n this.inspectorService.prepareInspector();\n this.inspectorService.otterLikeComponentInfoToBeSent$\n .pipe(\n takeUntilDestroyed(this.destroyRef),\n filter((info): info is OtterLikeComponentInfo => !!info)\n ).subscribe(\n (info) => this.sendMessage('selectedComponentInfo', info)\n );\n }\n}\n","import {\n ModuleWithProviders,\n NgModule,\n} from '@angular/core';\nimport {\n StoreModule,\n} from '@ngrx/store';\nimport {\n PlaceholderTemplateStoreModule,\n} from '../stores/placeholder-template/placeholder-template.module';\nimport type {\n ComponentsDevtoolsServiceOptions,\n} from './components-devkit.interface';\nimport {\n ComponentsDevtoolsMessageService,\n} from './components-devtools.message.service';\nimport {\n OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS,\n OTTER_COMPONENTS_DEVTOOLS_OPTIONS,\n} from './components-devtools.token';\n\n@NgModule({\n imports: [\n StoreModule,\n PlaceholderTemplateStoreModule\n ],\n providers: [\n { provide: OTTER_COMPONENTS_DEVTOOLS_OPTIONS, useValue: OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS },\n ComponentsDevtoolsMessageService\n ]\n})\nexport class ComponentsDevtoolsModule {\n /**\n * Initialize Otter Devtools\n * @param options\n */\n public static instrument(options: Partial<ComponentsDevtoolsServiceOptions>): ModuleWithProviders<ComponentsDevtoolsModule> {\n return {\n ngModule: ComponentsDevtoolsModule,\n providers: [\n { provide: OTTER_COMPONENTS_DEVTOOLS_OPTIONS, useValue: { ...OTTER_COMPONENTS_DEVTOOLS_DEFAULT_OPTIONS, ...options }, multi: false },\n ComponentsDevtoolsMessageService\n ]\n };\n }\n}\n","import {\n ComponentRef,\n Directive,\n forwardRef,\n Injector,\n Input,\n KeyValueChangeRecord,\n KeyValueDiffer,\n KeyValueDiffers,\n OnChanges,\n OnDestroy,\n SimpleChange,\n SimpleChanges,\n Type,\n ViewContainerRef,\n} from '@angular/core';\nimport {\n AbstractControl,\n ControlValueAccessor,\n FormControl,\n NG_VALIDATORS,\n NG_VALUE_ACCESSOR,\n NgControl,\n} from '@angular/forms';\nimport type {\n BaseContextOutput,\n Configuration,\n Context,\n ContextInput,\n Functionify,\n} from '@o3r/core';\nimport {\n Subscription,\n} from 'rxjs';\n\n@Directive({\n selector: '[c11n]',\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => C11nDirective),\n multi: true\n },\n {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => C11nDirective),\n multi: true\n }\n ]\n})\nexport class C11nDirective<\n D extends Configuration = Configuration,\n I extends ContextInput = ContextInput,\n O extends BaseContextOutput = BaseContextOutput,\n T extends Context<I, O> = Context<I, O>> implements OnChanges, OnDestroy {\n /** The component information passed to the directive */\n @Input() public component!: Type<T>;\n\n /** The information related to configuration */\n @Input() public config?: D;\n\n /** Formcontrol */\n @Input() public formControl?: FormControl;\n\n /** The input setter */\n @Input() public set inputs(value: { [K in keyof I]: I[K] }) {\n this._inputs = value;\n if (!this.differInputs && value) {\n // eslint-disable-next-line unicorn/no-array-callback-reference -- KeyValueDiffers.find is not an array function\n this.differInputs = this.differsService.find(value).create();\n }\n }\n\n /** The input getter */\n public get inputs(): { [K in keyof I]: I[K] } {\n return this._inputs;\n }\n\n /** The information related to output */\n @Input() public outputs?: Functionify<O>;\n\n /** The component reference */\n public componentRef!: ComponentRef<T>;\n\n private componentSubscriptions: Subscription[] = [];\n\n private _inputs!: { [K in keyof I]: I[K] };\n\n private differInputs!: KeyValueDiffer<string, any>;\n\n /** Set of inputs when the component was created. */\n private readonly uninitializedInputs = new Set<string>();\n\n constructor(public viewContainerRef: ViewContainerRef,\n private readonly differsService: KeyValueDiffers,\n private readonly injector: Injector) {}\n\n /**\n * Type guard for component implementing CVA\n * @param _cmp Component instance\n */\n private componentImplementsCva(_cmp: T): _cmp is T & ControlValueAccessor {\n return !!this.formControl;\n }\n\n private updateInputs(record: KeyValueChangeRecord<string, any>, inputChanges: SimpleChanges) {\n const recordKey = record.key;\n const isFirstChange = this.uninitializedInputs.has(recordKey);\n this.uninitializedInputs.delete(recordKey);\n inputChanges[recordKey] = new SimpleChange(record.previousValue, record.currentValue, isFirstChange);\n }\n\n /**\n * called when data-bound property change\n * @param changes The changes that occur\n */\n public ngOnChanges(changes: SimpleChanges) {\n const inputChanges: SimpleChanges = {};\n\n if (changes.component && changes.component.currentValue) {\n if (this.componentRef) {\n this.componentSubscriptions.forEach((s) => s.unsubscribe());\n this.componentSubscriptions = [];\n this.componentRef.destroy();\n }\n\n const ngControl = !!this.formControl && this.injector.get(NgControl);\n\n this.viewContainerRef.clear();\n this.componentRef = this.viewContainerRef.createComponent<T>(changes.component.currentValue);\n Object.keys(this.componentRef.instance)\n .filter((prop) => !(this.outputs && Object.keys(this.outputs).includes(prop)))\n .forEach((prop) => {\n this.uninitializedInputs.add(prop);\n });\n\n if (ngControl && this.componentImplementsCva(this.componentRef.instance)) {\n ngControl.valueAccessor = this.componentRef.instance;\n }\n\n // Initialize outputs\n if (this.outputs) {\n const subscriptions = Object.keys(this.outputs).map((outputName) => this.componentRef.instance[outputName].subscribe((val: any) => this.outputs));\n this.componentSubscriptions.push(...subscriptions);\n }\n\n // In case of async component change keep the inputs\n if (!changes.inputs && this.inputs) {\n Object.keys(this.inputs).forEach((inputName) => {\n const currentInputValue = this.inputs[inputName];\n inputChanges[inputName] = new SimpleChange(undefined, currentInputValue, true);\n this.uninitializedInputs.delete(inputName);\n });\n }\n // In case of lazy loaded component keep the config\n if (!changes.config && this.config) {\n inputChanges.config = new SimpleChange(this.componentRef.instance.config, this.config, true);\n this.uninitializedInputs.delete('config');\n }\n }\n\n if (this.componentRef && this.differInputs) {\n const changesInInputs = this.differInputs.diff(this.inputs);\n if (changesInInputs) {\n changesInInputs.forEachAddedItem((record) => this.updateInputs(record, inputChanges));\n changesInInputs.forEachChangedItem((record) => this.updateInputs(record, inputChanges));\n changesInInputs.forEachRemovedItem((record) => this.updateInputs(record, inputChanges));\n }\n }\n\n if (this.componentRef && changes.conf