From dfa09a8149d462dfbdb3cf6caa5b0d55a1126581 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 10:38:33 +0200 Subject: [PATCH 01/74] feat(TaskGrid): add inline creation feature and update interfaces --- src/components/TaskGrid/TaskGridDatasetControl.ts | 4 ++++ .../components/grid/grid-customizer/GridCustomizer.ts | 11 +++++++++++ src/components/TaskGrid/interfaces.ts | 2 ++ 3 files changed, 17 insertions(+) diff --git a/src/components/TaskGrid/TaskGridDatasetControl.ts b/src/components/TaskGrid/TaskGridDatasetControl.ts index cd37dfd4..fdd2cb59 100644 --- a/src/components/TaskGrid/TaskGridDatasetControl.ts +++ b/src/components/TaskGrid/TaskGridDatasetControl.ts @@ -93,6 +93,10 @@ export class TaskGridDatasetControl extends EventEmitter return this._gridParameters.enableCustomColumnDeletion ?? true; } + public isInlineCreateEnabled(): boolean { + return this._gridParameters.enableInlineCreate ?? true; + } + public isShowHierarchyToggleVisible(): boolean { return this._gridParameters.enableShowHierarchyToggle ?? true; } diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 10cdcba7..6a8760b8 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -367,6 +367,17 @@ export class GridCustomizer implements IGridCustomizer { if (!parentId) { this._gridApi.ensureIndexVisible(0); } + if (this._datasetControl.isInlineCreateEnabled() && records.length === 1) { + const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; + const recordId = records[0][primaryIdAttribute] as string; + const node = this._gridApi.getRowNode(recordId); + if (node?.rowIndex != null) { + this._gridApi.startEditingCell({ + rowIndex: node.rowIndex, + colKey: this._nativeColumns.subject + }); + } + } } private _onAfterTaskDataUpdated = (newData: IRawRecord[]) => { diff --git a/src/components/TaskGrid/interfaces.ts b/src/components/TaskGrid/interfaces.ts index 941298ef..4fecdabb 100644 --- a/src/components/TaskGrid/interfaces.ts +++ b/src/components/TaskGrid/interfaces.ts @@ -63,6 +63,7 @@ export interface ITaskGridParameters { enableCustomColumnCreation?: boolean; enableCustomColumnEditing?: boolean; enableCustomColumnDeletion?: boolean; + enableInlineCreate?: boolean; } /** Available data providers injected into `ITaskDataProviderStrategy` at construction time. */ @@ -163,4 +164,5 @@ export interface ITaskGridDatasetControl extends IDatasetControl { isCustomColumnDeletionEnabled: () => boolean; /** Whether user queries are enabled (from `ITaskGridParameters.enableUserQueries`). */ isUserQueriesFeatureEnabled: () => boolean; + isInlineCreateEnabled: () => boolean; } \ No newline at end of file From 2c035a116df24148da5ce51057442c24a08017b4 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 11:02:45 +0200 Subject: [PATCH 02/74] fix(TaskGrid): update client-libraries version and refactor inline editing logic --- package-lock.json | 9 +++---- package.json | 2 +- .../grid/grid-customizer/GridCustomizer.ts | 24 ++++++++++++------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a12b36e..29f9271f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fluentui/react-migration-v8-v9": "^9.6.20", - "@talxis/client-libraries": "1.2604.1", + "@talxis/client-libraries": "1.2605.1", "@talxis/client-metadata": "^0.0.3", "color": "^4.2.3", "dayjs": "^1.11.10", @@ -2757,13 +2757,14 @@ } }, "node_modules/@talxis/client-libraries": { - "version": "1.2604.1", - "resolved": "https://registry.npmjs.org/@talxis/client-libraries/-/client-libraries-1.2604.1.tgz", - "integrity": "sha512-fXUf3Vrguzgy/d7okMC32/ICI6xr6zQjWLVUvl69G5S18D1CifNpeeTouGI7GpRVOfj+S6EbeL9LZYrAD2qUiw==", + "version": "1.2605.1", + "resolved": "https://registry.npmjs.org/@talxis/client-libraries/-/client-libraries-1.2605.1.tgz", + "integrity": "sha512-51+6HbStfwJt0EpoWIXdDefriSIJBa28beem+aQwUUzcBVfJ+xl56quISfob67gaUrsDerIM7OX01sOh5GNIkQ==", "license": "MIT", "dependencies": { "@azure/msal-browser": "^2.27.0", "@microsoft/microsoft-graph-client": "^2.2.1", + "@talxis/client-metadata": "^0.0.3", "async-retry": "^1.3.3", "dayjs": "^1.11.13", "debounce": "^2.2.0", diff --git a/package.json b/package.json index 2ce93bf6..419bb8d2 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fluentui/react-migration-v8-v9": "^9.6.20", - "@talxis/client-libraries": "1.2604.1", + "@talxis/client-libraries": "1.2605.1", "@talxis/client-metadata": "^0.0.3", "color": "^4.2.3", "dayjs": "^1.11.10", diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 6a8760b8..0b9f7b1f 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -368,15 +368,21 @@ export class GridCustomizer implements IGridCustomizer { this._gridApi.ensureIndexVisible(0); } if (this._datasetControl.isInlineCreateEnabled() && records.length === 1) { - const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; - const recordId = records[0][primaryIdAttribute] as string; - const node = this._gridApi.getRowNode(recordId); - if (node?.rowIndex != null) { - this._gridApi.startEditingCell({ - rowIndex: node.rowIndex, - colKey: this._nativeColumns.subject - }); - } + setTimeout(() => { + const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; + const recordId = records[0][primaryIdAttribute] as string; + this._startEditingCell(recordId, this._nativeColumns.subject); + }, 10); + } + } + + private _startEditingCell(recordId: string, columnKey: string) { + const node = this._gridApi.getRowNode(recordId); + if (node?.rowIndex != null) { + this._gridApi.startEditingCell({ + rowIndex: node.rowIndex, + colKey: columnKey + }); } } From f554647137bfe214bfdfe760bd7d03110a65e545 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 11:51:44 +0200 Subject: [PATCH 03/74] feat(TaskGrid): enhance AddTaskButton and Header for task creation with templating support --- .../add-task-button/AddTaskButton.tsx | 16 ++++++---------- .../grid/grid-customizer/GridCustomizer.ts | 4 ++-- .../TaskGrid/components/header/Header.tsx | 3 ++- .../SavedQueryDataProvider.ts | 1 + 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx b/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx index 4be0d494..f1acef8b 100644 --- a/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx +++ b/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx @@ -14,6 +14,7 @@ export const AddTaskButton = (props: ICellProps) => { const localizationService = useLocalizationService(); const [isButtonMounted, setIsButtonMounted] = React.useState(true); const isTaskAddingEnabled = taskDataProvider.isTaskAddingEnabled(); + const isTemplatingEnabled = datasetControl.isTemplatingEnabled(); const addTaskFromTemplate = async (templateId: string) => { //this needs to be done so the button menu does not overlay the dialog @@ -37,7 +38,7 @@ export const AddTaskButton = (props: ICellProps) => { iconProps: { iconName: 'Add' }, - onClick: () => {taskDataProvider.createTask(record.getRecordId())} + onClick: () => { taskDataProvider.createTask(record.getRecordId()) } }, { key: 'divider', @@ -79,15 +80,10 @@ export const AddTaskButton = (props: ICellProps) => { else if (isButtonVisible()) { return + iconProps={{ iconName: 'Add' }} + onClick={!isTemplatingEnabled ? () => taskDataProvider.createTask(record.getRecordId()) : undefined} + menuProps={isTemplatingEnabled ? { items: getMenuItems() } : undefined} + styles={{ menuIcon: styles.addTaskMenuIcon }} /> } else { return <> diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 0b9f7b1f..081c09ab 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -372,7 +372,7 @@ export class GridCustomizer implements IGridCustomizer { const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; const recordId = records[0][primaryIdAttribute] as string; this._startEditingCell(recordId, this._nativeColumns.subject); - }, 10); + }, 100); } } @@ -381,7 +381,7 @@ export class GridCustomizer implements IGridCustomizer { if (node?.rowIndex != null) { this._gridApi.startEditingCell({ rowIndex: node.rowIndex, - colKey: columnKey + colKey: columnKey, }); } } diff --git a/src/components/TaskGrid/components/header/Header.tsx b/src/components/TaskGrid/components/header/Header.tsx index 439c2a0d..6553281b 100644 --- a/src/components/TaskGrid/components/header/Header.tsx +++ b/src/components/TaskGrid/components/header/Header.tsx @@ -44,7 +44,8 @@ export const Header = (props: ITaskGridHeaderProps) => { text: localizationService.getLocalizedString('new'), disabled: isLoading, iconProps: { iconName: 'Add' }, - subMenuProps: { + onClick: (isTaskAddingEnabled && !isTemplatingEnabled) ? () => { provider.createTask(); } : undefined, + subMenuProps: (isTaskAddingEnabled && !isTemplatingEnabled) ? undefined : { items: [ ...(isTaskAddingEnabled ? [{ key: 'addTopLevelTask', diff --git a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts index baa4d121..0234cf50 100644 --- a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts +++ b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts @@ -41,6 +41,7 @@ export interface ISavedQueryMetadata { linking?: ComponentFramework.PropertyHelper.DataSetApi.LinkEntityExposedExpression[]; isFlatListEnabled?: boolean; searchQuery?: string | undefined; + quickFindColumns?: string[]; } const REQUIRED_COLUMNS = ['subject', 'parentId', 'stackRank', 'path', 'stateCode']; From e1f0310290bb7dd4b2a629b102e1e24b10ddb4b7 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 12:17:28 +0200 Subject: [PATCH 04/74] feat(TaskGrid): update task management parameters and methods for consistency --- .../TaskGrid/TaskGridDatasetControl.ts | 17 +++++++++++--- .../add-task-button/AddTaskButton.tsx | 2 +- .../TaskGrid/components/header/Header.tsx | 6 ++--- .../task-data-provider/TaskDataProvider.ts | 5 ----- src/components/TaskGrid/interfaces.ts | 22 ++++++++++++++++--- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/components/TaskGrid/TaskGridDatasetControl.ts b/src/components/TaskGrid/TaskGridDatasetControl.ts index fdd2cb59..28a189c0 100644 --- a/src/components/TaskGrid/TaskGridDatasetControl.ts +++ b/src/components/TaskGrid/TaskGridDatasetControl.ts @@ -94,7 +94,7 @@ export class TaskGridDatasetControl extends EventEmitter } public isInlineCreateEnabled(): boolean { - return this._gridParameters.enableInlineCreate ?? true; + return this._gridParameters.enableInlineCreation ?? true; } public isShowHierarchyToggleVisible(): boolean { @@ -123,7 +123,7 @@ export class TaskGridDatasetControl extends EventEmitter } public isSaveQueryChangesEnabled(): boolean { - return this._gridParameters.enableSaveChangesToQuery ?? true; + return this._gridParameters.enableSaveQueryChanges ?? true; } public isUserQueriesFeatureEnabled(): boolean { @@ -223,11 +223,22 @@ export class TaskGridDatasetControl extends EventEmitter public getPcfContext(): ComponentFramework.Context { return this._getPcfContext(); } + + public isTaskEditingEnabled(): boolean { + return this._gridParameters.enableTaskEditing ?? true; + } + public isTaskCreatingEnabled(): boolean { + return this._gridParameters.enableTaskCreation ?? true; + } + public isTaskDeletingEnabled(): boolean { + return this._gridParameters.enableTaskDeletion ?? true; + } + public getParameters(): IDatasetControlParameters { return { Grid: this.getDataset(), EnableEditing: { - raw: this._dataProvider.isTaskEditingEnabled() + raw: this.isTaskEditingEnabled() }, EnableAutoSave: { raw: true diff --git a/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx b/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx index f1acef8b..29137069 100644 --- a/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx +++ b/src/components/TaskGrid/components/grid/cell-renderers/add-task-button/AddTaskButton.tsx @@ -13,7 +13,7 @@ export const AddTaskButton = (props: ICellProps) => { const datasetControl = useDatasetControl(); const localizationService = useLocalizationService(); const [isButtonMounted, setIsButtonMounted] = React.useState(true); - const isTaskAddingEnabled = taskDataProvider.isTaskAddingEnabled(); + const isTaskAddingEnabled = datasetControl.isTaskCreatingEnabled(); const isTemplatingEnabled = datasetControl.isTemplatingEnabled(); const addTaskFromTemplate = async (templateId: string) => { diff --git a/src/components/TaskGrid/components/header/Header.tsx b/src/components/TaskGrid/components/header/Header.tsx index 6553281b..87e207f1 100644 --- a/src/components/TaskGrid/components/header/Header.tsx +++ b/src/components/TaskGrid/components/header/Header.tsx @@ -30,9 +30,9 @@ export const Header = (props: ITaskGridHeaderProps) => { const getCommandBarItems = (items: ICommandBarItemProps[]): ICommandBarItemProps[] => { const isTemplatingEnabled = datasetControl.isTemplatingEnabled(); const isEditColumnsEnabled = datasetControl.getParameters().EnableEditColumns?.raw; - const isTaskAddingEnabled = provider.isTaskAddingEnabled(); - const isTaskEditingEnabled = provider.isTaskEditingEnabled(); - const isTaskDeletingEnabled = provider.isTaskDeletingEnabled(); + const isTaskAddingEnabled = datasetControl.isTaskCreatingEnabled(); + const isTaskEditingEnabled = datasetControl.isTaskEditingEnabled(); + const isTaskDeletingEnabled = datasetControl.isTaskDeletingEnabled(); const isShowHierarchyToggleVisible = datasetControl.isShowHierarchyToggleVisible(); const isHideInactiveTasksToggleVisible = datasetControl.isHideInactiveTasksToggleVisible(); const selectedIds = provider.getSelectedRecordIds(); diff --git a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts index 4f56047e..228ba3d3 100644 --- a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts +++ b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts @@ -129,11 +129,6 @@ export interface ITaskDataProvider extends IDataProvider { /** Returns `true` when the grid is displaying a flat list instead of a tree hierarchy. */ isFlatListEnabled(): boolean; /** Returns `true` when task creation is allowed (from `ITaskDataProviderStrategy.onIsTaskAddingEnabled`). */ - isTaskAddingEnabled(): boolean; - /** Returns `true` when inline cell editing is allowed (from `ITaskDataProviderStrategy.onIsTaskEditingEnabled`). */ - isTaskEditingEnabled(): boolean; - /** Returns `true` when task deletion is allowed (from `ITaskDataProviderStrategy.onIsTaskDeletingEnabled`). */ - isTaskDeletingEnabled(): boolean; /** Returns the root task id when the tree is scoped to a subtree, or `null` for a full tree. */ getRootTaskId: () => string | null; /** Moves a task to a position relative to another task. Returns the updated raw records, or `null` on cancellation. */ diff --git a/src/components/TaskGrid/interfaces.ts b/src/components/TaskGrid/interfaces.ts index 4fecdabb..da6da837 100644 --- a/src/components/TaskGrid/interfaces.ts +++ b/src/components/TaskGrid/interfaces.ts @@ -42,6 +42,12 @@ export interface ITaskGridParameters { enableRowDragging?: boolean; /** Show the *Edit Columns* button in the ribbon. Defaults to `true`. */ enableEditColumns?: boolean; + /** Enable editing of tasks directly in the grid. Defaults to `true`. */ + enableTaskEditing?: boolean; + /** Enable creation of new tasks. Defaults to `true`. */ + enableTaskCreation?: boolean; + /** Enable deletion of tasks. Defaults to `true`. */ + enableTaskDeletion?: boolean; /** Show the search / quick-find input. Defaults to `true`. */ enableQuickFind?: boolean; /** Show the view-switcher dropdown. Defaults to `true`. */ @@ -59,11 +65,15 @@ export interface ITaskGridParameters { /** Show the "Save as new" button in the query manager. Defaults to `true`. */ enableSaveAsNewQuery?: boolean; /** Show the "Save changes" button in the query manager. Defaults to `true`. */ - enableSaveChangesToQuery?: boolean; + enableSaveQueryChanges?: boolean; + /** Enable creation of custom columns. Defaults to `true`. */ enableCustomColumnCreation?: boolean; + /** Enable editing of custom columns. Defaults to `true`. */ enableCustomColumnEditing?: boolean; + /** Enable deletion of custom columns. Defaults to `true`. */ enableCustomColumnDeletion?: boolean; - enableInlineCreate?: boolean; + /** Enable inline creation of tasks. Defaults to `true`. */ + enableInlineCreation?: boolean; } /** Available data providers injected into `ITaskDataProviderStrategy` at construction time. */ @@ -148,13 +158,19 @@ export interface ITaskGridDatasetControl extends IDatasetControl { isEditColumnsScopeSelectorEnabled: () => boolean; /** Returns `true` when a template data provider was supplied through the descriptor. */ isTemplatingEnabled: () => boolean; + /** Returns `true` when inline creation of tasks is enabled. */ + isTaskCreatingEnabled: () => boolean; + /** Returns `true` when inline editing of tasks is enabled. */ + isTaskEditingEnabled: () => boolean; + /** Returns `true` when task deletion is enabled. */ + isTaskDeletingEnabled: () => boolean; /** Returns `true` when a custom columns strategy was supplied through the descriptor. */ isCustomColumnsEnabled: () => boolean; /** Whether the view manager is enabled (from `ITaskGridParameters.enableQueryManager`). */ isViewManagerEnabled: () => boolean; /** Whether the "Save as new" button is enabled (from `ITaskGridParameters.enableSaveAsNewQuery`). */ isSaveQueryAsNewEnabled: () => boolean; - /** Whether the "Save changes" button is enabled (from `ITaskGridParameters.enableSaveChangesToQuery`). */ + /** Whether the "Save changes" button is enabled (from `ITaskGridParameters.enableSaveQueryChanges`). */ isSaveQueryChangesEnabled: () => boolean; /** Whether custom column creation is enabled (from `ITaskGridParameters.enableCustomColumnCreation`). */ isCustomColumnCreationEnabled: () => boolean; From b162d9aa0a4c5f3e4987edc602995586e7b5790b Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 14:05:56 +0200 Subject: [PATCH 05/74] feat(TaskGrid): refactor localization handling and improve task management features --- src/components/TaskGrid/TaskGrid.tsx | 7 ++-- .../TaskGrid/TaskGridDatasetControl.ts | 5 ++- .../TaskGrid/TaskGridDatasetControlFactory.ts | 4 ++- .../grid/grid-customizer/GridCustomizer.ts | 30 +++++----------- .../view-switcher/view-manager/ViewManager.ts | 3 +- src/components/TaskGrid/context.ts | 5 +-- .../SavedQueryDataProvider.ts | 1 + .../task-data-provider/TaskDataProvider.ts | 34 ++++++------------- src/components/TaskGrid/interfaces.ts | 3 +- src/components/TaskGrid/labels.ts | 24 ------------- src/utils/index.ts | 3 +- src/utils/localization/LocalizationService.ts | 22 ++++++++++++ src/utils/localization/index.ts | 1 + 13 files changed, 63 insertions(+), 79 deletions(-) create mode 100644 src/utils/localization/LocalizationService.ts create mode 100644 src/utils/localization/index.ts diff --git a/src/components/TaskGrid/TaskGrid.tsx b/src/components/TaskGrid/TaskGrid.tsx index dc2c4e12..b5f09ff8 100644 --- a/src/components/TaskGrid/TaskGrid.tsx +++ b/src/components/TaskGrid/TaskGrid.tsx @@ -8,12 +8,13 @@ import { useTheme } from "@fluentui/react"; import { getDatasetControlStyles } from "./styles"; import { Grid } from "./components/grid"; import { ITaskDataProvider } from "./data-providers/task-data-provider"; -import { ITaskGridLabels, LocalizationService } from "./labels"; +import { ITaskGridLabels } from "./labels"; import { TASK_GRID_LABELS } from "./labels"; import { ITaskGridState, TaskGridDatasetControlFactory } from "./TaskGridDatasetControlFactory"; import { Header } from "./components/header/Header"; import { ITaskGridComponents, TaskGridComponents } from "./components/components"; import { ITaskGridDescriptor, ITaskGridDatasetControl } from "./interfaces"; +import { LocalizationService } from "../../utils"; interface ITaskGridProps { //should be replaced by Context API in future @@ -35,9 +36,7 @@ export const TaskGrid = (props: ITaskGridProps) => { const components = { ...TaskGridComponents, ...props.components }; const pcfContextRef = useRef(props.pcfContext); pcfContextRef.current = props.pcfContext; - const labelsRef = useRef(); - labelsRef.current = { ...TASK_GRID_LABELS, ...props.labels }; - const localizationService = React.useMemo(() => new LocalizationService(() => labelsRef.current!), []); + const localizationService = React.useMemo(() => new LocalizationService({ ...TASK_GRID_LABELS, ...props.labels }), []); const [instanceState, setInstanceState] = React.useState<{ instance: ITaskGridDatasetControl; diff --git a/src/components/TaskGrid/TaskGridDatasetControl.ts b/src/components/TaskGrid/TaskGridDatasetControl.ts index 28a189c0..a5ad7438 100644 --- a/src/components/TaskGrid/TaskGridDatasetControl.ts +++ b/src/components/TaskGrid/TaskGridDatasetControl.ts @@ -3,7 +3,8 @@ import { IDatasetControlEvents } from "../../utils/dataset-control"; import { EditColumns, IEditColumns } from "../../utils/dataset-control/EditColumns"; import { IDataset, ICommand, EventEmitter, IDataProvider, Operators, Filtering } from "@talxis/client-libraries"; import { IDeleteTasksResult, ITaskDataProvider } from "./data-providers/task-data-provider"; -import { ILocalizationService, ITaskGridLabels } from "./labels"; +import { ILocalizationService } from "../../utils"; +import { ITaskGridLabels } from "./labels"; import { ISavedQueryDataProvider } from "./data-providers/saved-query-data-provider"; import { ITaskGridState } from "./TaskGridDatasetControlFactory"; import { Type } from "@talxis/client-libraries/dist/utils/fetch-xml/filter/Type"; @@ -228,9 +229,11 @@ export class TaskGridDatasetControl extends EventEmitter return this._gridParameters.enableTaskEditing ?? true; } public isTaskCreatingEnabled(): boolean { + if(!this.isTaskEditingEnabled()) return false; return this._gridParameters.enableTaskCreation ?? true; } public isTaskDeletingEnabled(): boolean { + if(!this.isTaskEditingEnabled()) return false; return this._gridParameters.enableTaskDeletion ?? true; } diff --git a/src/components/TaskGrid/TaskGridDatasetControlFactory.ts b/src/components/TaskGrid/TaskGridDatasetControlFactory.ts index b32b75b4..9de938ca 100644 --- a/src/components/TaskGrid/TaskGridDatasetControlFactory.ts +++ b/src/components/TaskGrid/TaskGridDatasetControlFactory.ts @@ -1,6 +1,7 @@ import { Dataset } from "@talxis/client-libraries"; import { ITaskDataProvider, TaskDataProvider } from "./data-providers/task-data-provider"; -import { ILocalizationService, ITaskGridLabels } from "./labels"; +import { ILocalizationService } from "../../utils"; +import { ITaskGridLabels } from "./labels"; import { ISavedQuery, ISavedQueryDataProvider, SavedQueryDataProvider } from "./data-providers/saved-query-data-provider"; import { RecordTree } from "./data-providers/task-data-provider/record-tree"; import { CustomColumnsDataProvider } from "./data-providers/custom-columns-data-provider/CustomColumnsDataProvider"; @@ -49,6 +50,7 @@ export class TaskGridDatasetControlFactory { localizationService: parameters.localizationService, nativeColumns: parameters.taskGridDescriptor.onGetNativeColumns(), strategy: taskStrategy, + savedQueryDataProvider: savedQueryDataProvider, onIsFlatListEnabled: () => TaskGridDatasetControlFactory._getIsFlatlistEnabled(parameters, savedQueryDataProvider) }); diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 081c09ab..48ef165f 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -5,7 +5,8 @@ import { GridDragHandler, IDragOperation } from "../grid-drag-handler"; import { GroupCell } from "../group-cell"; import { TreeExpandCollapseHeader } from "../cell-headers/tree-expand-collapse-header"; import { AddTaskButton } from "../cell-renderers/add-task-button"; -import { ILocalizationService, ITaskGridLabels } from "../../../labels"; +import { ILocalizationService } from "../../../../../utils"; +import { ITaskGridLabels } from "../../../labels"; import { PercentComplete } from "../cell-renderers/percent-complete"; import { INativeColumns, ITaskGridDatasetControl } from "../../../interfaces"; @@ -364,26 +365,13 @@ export class GridCustomizer implements IGridCustomizer { parentNode.setExpanded(true); } } - if (!parentId) { - this._gridApi.ensureIndexVisible(0); - } - if (this._datasetControl.isInlineCreateEnabled() && records.length === 1) { - setTimeout(() => { - const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; - const recordId = records[0][primaryIdAttribute] as string; - this._startEditingCell(recordId, this._nativeColumns.subject); - }, 100); - } - } - - private _startEditingCell(recordId: string, columnKey: string) { - const node = this._gridApi.getRowNode(recordId); - if (node?.rowIndex != null) { - this._gridApi.startEditingCell({ - rowIndex: node.rowIndex, - colKey: columnKey, - }); - } + setTimeout(() => { + const primaryIdAttribute = this._taskDataProvider.getMetadata().PrimaryIdAttribute; + const recordId = records[0][primaryIdAttribute] as string; + const node = this._gridApi.getRowNode(recordId); + this._gridApi.setFocusedCell(node!.rowIndex!, this._nativeColumns.subject); + this._gridApi.ensureNodeVisible(node!); + }, 100); } private _onAfterTaskDataUpdated = (newData: IRawRecord[]) => { diff --git a/src/components/TaskGrid/components/header/view-switcher/view-manager/ViewManager.ts b/src/components/TaskGrid/components/header/view-switcher/view-manager/ViewManager.ts index 12d4af2d..ebb0743c 100644 --- a/src/components/TaskGrid/components/header/view-switcher/view-manager/ViewManager.ts +++ b/src/components/TaskGrid/components/header/view-switcher/view-manager/ViewManager.ts @@ -1,7 +1,8 @@ import { Dataset, ICommand, IDataProvider, IDataset, IRetrieveRecordCommandOptions } from "@talxis/client-libraries"; import { DatasetControl, IDatasetControl } from "../../../../../../utils/dataset-control"; import { ITaskGridDatasetControl } from "../../../../interfaces"; -import { ILocalizationService, ITaskGridLabels } from "../../../../labels"; +import { ILocalizationService } from "../../../../../../utils"; +import { ITaskGridLabels } from "../../../../labels"; import { IDeletedUserQueriesResult, ISavedQueryDataProvider } from "../../../../data-providers/saved-query-data-provider"; import { ErrorHelper } from "../../../../../../utils"; diff --git a/src/components/TaskGrid/context.ts b/src/components/TaskGrid/context.ts index 72b95ef8..504164fb 100644 --- a/src/components/TaskGrid/context.ts +++ b/src/components/TaskGrid/context.ts @@ -1,8 +1,9 @@ import * as React from "react"; import { ITaskDataProvider } from "./data-providers/task-data-provider"; import { ITaskGridDatasetControl, ITaskGridDescriptor } from "./interfaces"; -import { ITaskGridLabels, LocalizationService } from "./labels"; +import { ITaskGridLabels } from "./labels"; import { ITaskGridComponents, TaskGridComponents } from "./components/components"; +import { ILocalizationService } from "../../utils"; export const PcfContext = React.createContext | null>(null); PcfContext.displayName = 'PcfContext'; @@ -22,7 +23,7 @@ TaskGridDescriptorContext.displayName = 'TaskGridDescriptor'; export const RootElementIdContext = React.createContext(''); RootElementIdContext.displayName = 'RootElementId'; -export const LocalizationServiceContext = React.createContext | null>(null); +export const LocalizationServiceContext = React.createContext | null>(null); LocalizationServiceContext.displayName = 'LocalizationService'; export const AgGridLicenseKeyContext = React.createContext(null); diff --git a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts index 0234cf50..08a3732c 100644 --- a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts +++ b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts @@ -219,6 +219,7 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { linking: provider.getLinking(), searchQuery: provider.getSearchQuery(), isFlatListEnabled: provider.isFlatListEnabled(), + quickFindColumns: provider.getQuickFindColumns().map(col => col.name), columns: [ ...provider.getColumns().map((col: any) => { const newCol = { diff --git a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts index 228ba3d3..b80eaaef 100644 --- a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts +++ b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts @@ -1,8 +1,10 @@ import { EventEmitter, GetDataEvent, IAvailableColumnOptions, IAvailableRelatedColumn, IColumn, ICommand, IDataProvider, IDataProviderEventListeners, IEventBubbleOptions, IEventEmitter, IRawRecord, IRecord, IRecordSaveOperationResult, IRetrievedData, IRetrieveRecordCommandOptions, MemoryDataProvider, Operators, Type } from "@talxis/client-libraries"; import { IRecordTree, RecordTree } from "./record-tree/RecordTree"; import { ErrorHelper } from "../../../../utils/error-handling"; -import { ILocalizationService, ITaskGridLabels } from "../../labels"; +import { ILocalizationService } from "../../../../utils"; +import { ITaskGridLabels } from "../../labels"; import { INativeColumns } from "../../interfaces"; +import { ISavedQueryDataProvider } from "../saved-query-data-provider"; export interface IFailedRecord { id: string; @@ -22,6 +24,7 @@ export interface ITaskDataProviderParameters { nativeColumns: INativeColumns; localizationService: ILocalizationService; strategy: ITaskDataProviderStrategy; + savedQueryDataProvider: ISavedQueryDataProvider; onIsFlatListEnabled: () => boolean; } @@ -66,12 +69,6 @@ export interface ITaskDataProviderStrategy { onIsRecordActive(recordId: string): boolean; /** Opens the record detail view. Called when a user clicks a non-subject cell. */ onOpenDatasetItem(entityReference: ComponentFramework.EntityReference, context?: { columnName?: string }): void; - /** Return `false` to disable task creation (hide the *New* button). Defaults to `true`. */ - onIsTaskAddingEnabled?(): boolean; - /** Return `false` to disable inline cell editing. Defaults to `true`. */ - onIsTaskEditingEnabled?(): boolean; - /** Return `false` to disable task deletion (hide the *Delete* button). Defaults to `true`. */ - onIsTaskDeletingEnabled?(): boolean; /** When provided, the task tree is scoped to the subtree of the returned task id. */ onGetRootTaskId?: () => string | undefined } @@ -141,6 +138,7 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro private _hasDataBeenLoaded: boolean = false; private _taskTree: IRecordTree; private _strategy: ITaskDataProviderStrategy; + private _savedQueryDataProvider: ISavedQueryDataProvider; private _onFlatListEnabled: () => boolean; public readonly taskEvents: EventEmitter = new EventEmitter(); @@ -148,7 +146,8 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro super({ dataSource: [], metadata: { PrimaryIdAttribute: 'id' } - }) + }); + this._savedQueryDataProvider = parameters.savedQueryDataProvider; this._nativeColumns = parameters.nativeColumns; this._taskTree = new RecordTree({ taskDataProvider: this @@ -166,18 +165,6 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro return this._strategy.onGetRootTaskId?.() ?? null; } - public isTaskAddingEnabled(): boolean { - return this._strategy.onIsTaskAddingEnabled?.() ?? true; - } - - public isTaskEditingEnabled(): boolean { - return this._strategy.onIsTaskEditingEnabled?.() ?? true; - } - - public isTaskDeletingEnabled(): boolean { - return this._strategy.onIsTaskDeletingEnabled?.() ?? true; - } - public getRecordTree(): IRecordTree { return this._taskTree; } @@ -378,9 +365,9 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro } public getQuickFindColumns(): IColumn[] { - return this._strategy.onGetQuickFindColumns().map(columnName => { - return this.getColumnsMap()[columnName]; - }).filter(col => col) as IColumn[]; + const quickFindColumnNames = this._savedQueryDataProvider.getCurrentQuery().quickFindColumns ?? []; + const existingQuickFindColumns = quickFindColumnNames.map(columnName => this.getColumnsMap()[columnName]).filter(col => col) as IColumn[]; + return existingQuickFindColumns; } public createGroupedRecordDataProvider(group: IRecord): IDataProvider { @@ -417,6 +404,7 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro localizationService: this._localizationService, nativeColumns: this._nativeColumns, strategy: this._strategy, + savedQueryDataProvider: this._savedQueryDataProvider, onIsFlatListEnabled: () => this._onFlatListEnabled(), }); } diff --git a/src/components/TaskGrid/interfaces.ts b/src/components/TaskGrid/interfaces.ts index da6da837..adca9d36 100644 --- a/src/components/TaskGrid/interfaces.ts +++ b/src/components/TaskGrid/interfaces.ts @@ -4,8 +4,9 @@ import { IGridCustomizerStrategy } from "./components/grid/grid-customizer"; import { ICustomColumnsDataProvider, ICustomColumnsStrategy } from "./data-providers/custom-columns-data-provider/CustomColumnsDataProvider"; import { ISavedQueryDataProvider, ISavedQueryStrategy } from "./data-providers/saved-query-data-provider"; import { ITaskDataProviderStrategy, ITaskDataProvider } from "./data-providers/task-data-provider"; -import { ILocalizationService, ITaskGridLabels } from "./labels"; +import { ITaskGridLabels } from "./labels"; import { ITaskGridState } from "./TaskGridDatasetControlFactory"; +import { ILocalizationService } from "../../utils"; export interface ITaskGridDatasetControlParameters { dataset: IDataset; diff --git a/src/components/TaskGrid/labels.ts b/src/components/TaskGrid/labels.ts index b6dcf059..5aa3412d 100644 --- a/src/components/TaskGrid/labels.ts +++ b/src/components/TaskGrid/labels.ts @@ -1,5 +1,3 @@ -import { Liquid } from "liquidjs"; - export interface ITaskGridLabels { name: string; no: string; @@ -77,10 +75,6 @@ export interface ITaskGridLabels { deletingUserQueriesError: string; } -export interface ILocalizationService { - getLocalizedString: (key: keyof T, variables?: {[key: string]: string}) => string; -} - export const TASK_GRID_LABELS: ITaskGridLabels = { name: 'Name', no: 'No', @@ -156,22 +150,4 @@ export const TASK_GRID_LABELS: ITaskGridLabels = { deletingUserQueriesError: 'Some views could not be deleted due to following reasons:', noRecordsFound: 'No records found.', unexpectedErrorOccurred: 'Unexpected error occurred.', -} - -export class LocalizationService implements ILocalizationService { - private _getLabels: () => T; - private _liquidEngine: Liquid; - - constructor(getLabels: () => T) { - this._getLabels = getLabels; - this._liquidEngine = new Liquid(); - } - - public getLocalizedString(key: keyof T, variables?: {[key: string]: string}): string { - const labels = this._getLabels(); - if(variables) { - return this._liquidEngine.parseAndRenderSync(labels[key] as unknown as string, variables); - } - return labels[key]; - } } \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 613c6a58..ff811fc3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './theme'; export * from './BaseControls'; export * from './dataset'; -export * from './error-handling'; \ No newline at end of file +export * from './error-handling'; +export * from './localization'; \ No newline at end of file diff --git a/src/utils/localization/LocalizationService.ts b/src/utils/localization/LocalizationService.ts new file mode 100644 index 00000000..bef2788f --- /dev/null +++ b/src/utils/localization/LocalizationService.ts @@ -0,0 +1,22 @@ +import { Liquid } from "liquidjs"; + +export interface ILocalizationService { + getLocalizedString: (key: keyof T, variables?: {[key: string]: string}) => string; +} + +export class LocalizationService implements ILocalizationService { + private _labels: T; + private _liquidEngine: Liquid; + + constructor(labels: T) { + this._labels = labels; + this._liquidEngine = new Liquid(); + } + + public getLocalizedString(key: keyof T, variables?: {[key: string]: string}): string { + if(variables) { + return this._liquidEngine.parseAndRenderSync(this._labels[key] as unknown as string, variables); + } + return this._labels[key]; + } +} \ No newline at end of file diff --git a/src/utils/localization/index.ts b/src/utils/localization/index.ts new file mode 100644 index 00000000..7d612832 --- /dev/null +++ b/src/utils/localization/index.ts @@ -0,0 +1 @@ +export * from './LocalizationService'; \ No newline at end of file From c86c3eac7e2021a05f45381eb646e5fa12b124e3 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Thu, 14 May 2026 16:32:09 +0200 Subject: [PATCH 06/74] feat(TaskGrid): integrate path column handling and enhance virtual column management --- .../TaskGrid/TaskGridDatasetControl.ts | 7 +- .../TaskGrid/TaskGridDatasetControlFactory.ts | 8 +- .../grid/grid-customizer/GridCustomizer.ts | 2 +- .../SavedQueryDataProvider.ts | 97 ++++++++++++------- .../task-data-provider/TaskDataProvider.ts | 36 ++++++- src/components/TaskGrid/interfaces.ts | 2 +- src/components/TaskGrid/labels.ts | 2 + 7 files changed, 105 insertions(+), 49 deletions(-) diff --git a/src/components/TaskGrid/TaskGridDatasetControl.ts b/src/components/TaskGrid/TaskGridDatasetControl.ts index a5ad7438..0e304d80 100644 --- a/src/components/TaskGrid/TaskGridDatasetControl.ts +++ b/src/components/TaskGrid/TaskGridDatasetControl.ts @@ -5,7 +5,7 @@ import { IDataset, ICommand, EventEmitter, IDataProvider, Operators, Filtering } import { IDeleteTasksResult, ITaskDataProvider } from "./data-providers/task-data-provider"; import { ILocalizationService } from "../../utils"; import { ITaskGridLabels } from "./labels"; -import { ISavedQueryDataProvider } from "./data-providers/saved-query-data-provider"; +import { ISavedQueryDataProvider, PATH_COLUMN_NAME } from "./data-providers/saved-query-data-provider"; import { ITaskGridState } from "./TaskGridDatasetControlFactory"; import { Type } from "@talxis/client-libraries/dist/utils/fetch-xml/filter/Type"; import { ICustomColumnsDataProvider } from "./data-providers/custom-columns-data-provider/CustomColumnsDataProvider"; @@ -52,7 +52,10 @@ export class TaskGridDatasetControl extends EventEmitter } public getNativeColumns() { - return this._descriptor.onGetNativeColumns(); + return { + ...this._descriptor.onGetNativeColumns(), + path: PATH_COLUMN_NAME + } } public getLocalizationService() { diff --git a/src/components/TaskGrid/TaskGridDatasetControlFactory.ts b/src/components/TaskGrid/TaskGridDatasetControlFactory.ts index 9de938ca..66346499 100644 --- a/src/components/TaskGrid/TaskGridDatasetControlFactory.ts +++ b/src/components/TaskGrid/TaskGridDatasetControlFactory.ts @@ -2,8 +2,7 @@ import { Dataset } from "@talxis/client-libraries"; import { ITaskDataProvider, TaskDataProvider } from "./data-providers/task-data-provider"; import { ILocalizationService } from "../../utils"; import { ITaskGridLabels } from "./labels"; -import { ISavedQuery, ISavedQueryDataProvider, SavedQueryDataProvider } from "./data-providers/saved-query-data-provider"; -import { RecordTree } from "./data-providers/task-data-provider/record-tree"; +import { ISavedQuery, ISavedQueryDataProvider, PATH_COLUMN_NAME, SavedQueryDataProvider } from "./data-providers/saved-query-data-provider"; import { CustomColumnsDataProvider } from "./data-providers/custom-columns-data-provider/CustomColumnsDataProvider"; import { ITaskGridDatasetControl, ITaskGridDescriptor } from "./interfaces"; import { TaskGridDatasetControl } from "./TaskGridDatasetControl"; @@ -34,7 +33,8 @@ export class TaskGridDatasetControlFactory { const savedQueryStrategy = parameters.taskGridDescriptor.onCreateSavedQueryStrategy(); const savedQueryDataProvider = new SavedQueryDataProvider(savedQueryStrategy, { - nativeColumns: parameters.taskGridDescriptor.onGetNativeColumns(), + localizationService: parameters.localizationService, + nativeColumns: { ...parameters.taskGridDescriptor.onGetNativeColumns(), path: PATH_COLUMN_NAME }, customColumnsDataProvider: customColumnsDataProvider, preferredQuery: parameters.state.savedQuery, }) @@ -48,7 +48,7 @@ export class TaskGridDatasetControlFactory { taskDataProvider = new TaskDataProvider({ localizationService: parameters.localizationService, - nativeColumns: parameters.taskGridDescriptor.onGetNativeColumns(), + nativeColumns: { ...parameters.taskGridDescriptor.onGetNativeColumns(), path: PATH_COLUMN_NAME }, strategy: taskStrategy, savedQueryDataProvider: savedQueryDataProvider, onIsFlatListEnabled: () => TaskGridDatasetControlFactory._getIsFlatlistEnabled(parameters, savedQueryDataProvider) diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 48ef165f..38ce9a8c 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -131,7 +131,7 @@ export class GridCustomizer implements IGridCustomizer { } private _injectAddTaskColumn(columnDefs: ColDef[]) { - if (!columnDefs.find(colDef => colDef.colId === ADD_TASK_COLUMN_NAME)) { + if (!this._taskDataProvider.isFlatListEnabled() &&!columnDefs.find(colDef => colDef.colId === ADD_TASK_COLUMN_NAME)) { columnDefs.push({ colId: ADD_TASK_COLUMN_NAME, headerName: '', diff --git a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts index 08a3732c..ec331769 100644 --- a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts +++ b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts @@ -1,8 +1,9 @@ -import { EventEmitter, IColumn, IEventEmitter, IFetchXmlDataProviderColumn } from "@talxis/client-libraries"; +import { DataTypes, EventEmitter, IColumn, IEventEmitter, IFetchXmlDataProviderColumn } from "@talxis/client-libraries"; import { ITaskDataProvider } from "../task-data-provider"; import { ICustomColumnsDataProvider } from "../custom-columns-data-provider/CustomColumnsDataProvider"; import { INativeColumns } from "../../interfaces"; -import { ErrorHelper } from "../../../../utils"; +import { ErrorHelper, ILocalizationService } from "../../../../utils"; +import { ITaskGridLabels } from "../../labels"; export interface ICreateUserQueryParams { @@ -26,7 +27,7 @@ export interface ISavedQueryDataProviderEvents { onError: (error: any, message: string) => void; } -export type IDeletedUserQueriesResult = {success: true; deletedQueryIds: string[]} | {success: false; deletedQueryIds: string[]; errors: {queryId: string; error: any}[]}; +export type IDeletedUserQueriesResult = { success: true; deletedQueryIds: string[] } | { success: false; deletedQueryIds: string[]; errors: { queryId: string; error: any }[] }; export interface ISavedQuery extends ISavedQueryMetadata { @@ -44,7 +45,8 @@ export interface ISavedQueryMetadata { quickFindColumns?: string[]; } -const REQUIRED_COLUMNS = ['subject', 'parentId', 'stackRank', 'path', 'stateCode']; +export const PATH_COLUMN_NAME = 'path__virtual'; +const REQUIRED_COLUMNS = ['subject', 'parentId', 'stackRank', 'stateCode']; /** Strategy interface for loading and persisting system and user-defined saved views (queries). */ export interface ISavedQueryStrategy { @@ -91,6 +93,7 @@ export interface ISavedQueryDataProvider { interface ISavedQueryDataProviderParameters { nativeColumns: INativeColumns; + localizationService: ILocalizationService; customColumnsDataProvider?: ICustomColumnsDataProvider; preferredQuery?: Partial & { id: string }; } @@ -102,6 +105,7 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { private _userQueries: ISavedQuery[] = []; private _customColumnsDataProvider?: ICustomColumnsDataProvider; private _nativeColumns: INativeColumns; + private _localizationService: ILocalizationService; private _preferredQuery?: Partial & { id: string }; public queryEvents = new EventEmitter(); @@ -110,6 +114,7 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { this._preferredQuery = parameters.preferredQuery; this._nativeColumns = parameters.nativeColumns; this._customColumnsDataProvider = parameters.customColumnsDataProvider; + this._localizationService = parameters.localizationService; } public getSystemQueries(): ISavedQuery[] { @@ -212,6 +217,55 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { this.harmonizeColumns(this._currentQuery.columns); } + public includeRequiredColumns(columns: IColumn[]) { + const allQueries = [...this.getSystemQueries(), ...this.getUserQueries()]; + const allQueryColumns = [...new Map(allQueries.flatMap(query => query.columns.map(col => [col.name, col]))).values()]; + for (const requiredColumnName of REQUIRED_COLUMNS) { + const mappedRequiredColumnName = this._nativeColumns[requiredColumnName as keyof INativeColumns]; + if (!columns.find(col => col.name === mappedRequiredColumnName)) { + const columnFromQueries = allQueryColumns.find(col => col.name === mappedRequiredColumnName); + if (!columnFromQueries) { + throw new Error(`Required column ${mappedRequiredColumnName} is missing from both current query and all available queries`); + } + columns.push(columnFromQueries); + } + } + this._includePathColumn(columns); + } + + public harmonizeColumns(columns: IColumn[]) { + for (const column of columns) { + switch (column.name) { + case this._nativeColumns.subject: { + column.isHidden = false; + break; + } + case this._nativeColumns.path: { + column.metadata = { + ...column.metadata, + IsValidForUpdate: false + } + break; + } + } + } + } + + + //TODO: this will not work with user queries (localization) + private _includePathColumn(columns: IColumn[]) { + if (!columns.find(col => col.name === PATH_COLUMN_NAME)) { + columns.push({ + name: PATH_COLUMN_NAME, + dataType: DataTypes.Multiple, + displayName: this._localizationService.getLocalizedString('path'), + isVirtual: true, + visualSizeFactor: 300, + isHidden: this._currentQuery?.isFlatListEnabled ? false : true + }) + } + } + private _getMetadataForSavedQuery(provider: ITaskDataProvider): ISavedQueryMetadata { return { sorting: provider.getSorting(), @@ -249,6 +303,8 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { !!customColumnsMap.get(name); } + + //TODO: localization for virtual columns private _parseSavedQueryMetadata(metadata: ISavedQueryMetadata): ISavedQueryMetadata { const parsed = metadata; if (!this._customColumnsDataProvider) { @@ -270,37 +326,4 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { } : undefined }; } - - public includeRequiredColumns(columns: IColumn[]) { - const allQueries = [...this.getSystemQueries(), ...this.getUserQueries()]; - const allQueryColumns = [...new Map(allQueries.flatMap(query => query.columns.map(col => [col.name, col]))).values()]; - for (const requiredColumnName of REQUIRED_COLUMNS) { - const mappedRequiredColumnName = this._nativeColumns[requiredColumnName as keyof INativeColumns]; - if (!columns.find(col => col.name === mappedRequiredColumnName)) { - const columnFromQueries = allQueryColumns.find(col => col.name === mappedRequiredColumnName); - if (!columnFromQueries) { - throw new Error(`Required column ${mappedRequiredColumnName} is missing from both current query and all available queries`); - } - columns.push(columnFromQueries); - } - } - } - - public harmonizeColumns(columns: IColumn[]) { - for (const column of columns) { - switch (column.name) { - case this._nativeColumns.subject: { - column.isHidden = false; - break; - } - case this._nativeColumns.path: { - column.metadata = { - ...column.metadata, - IsValidForUpdate: false - } - break; - } - } - } - } } \ No newline at end of file diff --git a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts index b80eaaef..54993478 100644 --- a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts +++ b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts @@ -4,7 +4,7 @@ import { ErrorHelper } from "../../../../utils/error-handling"; import { ILocalizationService } from "../../../../utils"; import { ITaskGridLabels } from "../../labels"; import { INativeColumns } from "../../interfaces"; -import { ISavedQueryDataProvider } from "../saved-query-data-provider"; +import { ISavedQueryDataProvider, PATH_COLUMN_NAME } from "../saved-query-data-provider"; export interface IFailedRecord { id: string; @@ -181,8 +181,24 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro return this._strategy.onGetRawRecords(ids); } - public onGetAvailableColumns(): Promise { - return this._strategy.onGetAvailableColumns(); + public async onGetAvailableColumns(options?: { entityName?: string }): Promise { + return this._getColumnsWithUnusedVirtualColumns(await this._strategy.onGetAvailableColumns(options)); + } + + private _getColumnsWithUnusedVirtualColumns(columns: IColumn[]): IColumn[] { + columns = [...columns]; + const columnsMap = new Map(columns.map(col => [col.name, col])); + const virtualColumns = this._savedQueryDataProvider.getSystemQueries().flatMap(query => query.columns).filter(column => column.isVirtual); + for (const virtualColumn of virtualColumns) { + //path column is always part of available columns (hidden) + if (!columnsMap.has(virtualColumn.name) || virtualColumn.name === PATH_COLUMN_NAME) { + columns.push({ + ...virtualColumn, + isHidden: false + }); + } + } + return columns; } public onGetAvailableRelatedColumns(): Promise { @@ -418,7 +434,7 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro } public dispatchEvent(event: K, ...args: Parameters): boolean { - if(event === 'onNewDataLoaded') { + if (event === 'onNewDataLoaded') { this._taskTree.build(); } return super.dispatchEvent(event, ...args); @@ -435,7 +451,9 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro private async _loadDataFromStrategy() { return ErrorHelper.executeWithErrorHandling({ operation: async () => { + const virtualColumns = structuredClone(this.getColumns().filter(col => col.isVirtual)); const { columns, rawData, metadata } = await this._strategy.onInitialize(this); + this._harmonizeVirtualColumns(virtualColumns, columns); this.setDataSource(rawData); this.setMetadata(metadata); this.setColumns(columns); @@ -446,6 +464,16 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro }) } + //fetch xml provider will override virtual column metadata by + private _harmonizeVirtualColumns(virtualColumns: IColumn[], columns: IColumn[]) { + columns.map((col, i) => { + const virtualCol = virtualColumns.find(virtualCol => virtualCol.name === col.name); + if (virtualCol) { + columns[i] = virtualCol; + } + }); + } + private _createTasks(rawRecords: IRawRecord[], parentId?: string) { const records: IRecord[] = []; for (const rawRecord of rawRecords) { diff --git a/src/components/TaskGrid/interfaces.ts b/src/components/TaskGrid/interfaces.ts index adca9d36..627a915a 100644 --- a/src/components/TaskGrid/interfaces.ts +++ b/src/components/TaskGrid/interfaces.ts @@ -91,7 +91,7 @@ export interface ITaskStrategyDeps { */ export interface ITaskGridDescriptor { /** Returns the mapping of logical column roles to physical schema attribute names. */ - onGetNativeColumns: () => INativeColumns; + onGetNativeColumns: () => Omit; /** Returns the strategy responsible for loading system/user views and persisting view changes. */ onCreateSavedQueryStrategy: () => ISavedQueryStrategy; /** Returns the strategy that handles all task CRUD, move, template and record-save operations. */ diff --git a/src/components/TaskGrid/labels.ts b/src/components/TaskGrid/labels.ts index 5aa3412d..bfa8219a 100644 --- a/src/components/TaskGrid/labels.ts +++ b/src/components/TaskGrid/labels.ts @@ -32,6 +32,7 @@ export interface ITaskGridLabels { templateCreatedSuccessfully: string; description: string; noTaskTemplates: string; + path: string; addChild: string; addCustomColumn: string; confirmColumnDelete: string; @@ -80,6 +81,7 @@ export const TASK_GRID_LABELS: ITaskGridLabels = { no: 'No', yes: 'Yes', ok: 'Ok', + path: 'Path', confirm: 'Confirm', confirmation: 'Confirmation', myViews: 'My views', From a1db2337454cf9cc3c4de15f90be192e736a77f7 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Fri, 15 May 2026 15:53:10 +0200 Subject: [PATCH 07/74] feat(TaskGrid): enhance custom columns handling with raw record fetching capabilities --- .../CustomColumnsDataProvider.ts | 6 +++- ...TalxisCustomColumnsDataProviderStrategy.ts | 13 +++++++-- .../SavedQueryDataProvider.ts | 29 ++++++++++++++----- .../task-data-provider/TaskDataProvider.ts | 19 +++--------- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/components/TaskGrid/data-providers/custom-columns-data-provider/CustomColumnsDataProvider.ts b/src/components/TaskGrid/data-providers/custom-columns-data-provider/CustomColumnsDataProvider.ts index 84ed5d32..9507b61e 100644 --- a/src/components/TaskGrid/data-providers/custom-columns-data-provider/CustomColumnsDataProvider.ts +++ b/src/components/TaskGrid/data-providers/custom-columns-data-provider/CustomColumnsDataProvider.ts @@ -1,4 +1,4 @@ -import { DatasetConstants, IColumn, IEventEmitter, EventEmitter } from "@talxis/client-libraries"; +import { DatasetConstants, IColumn, IEventEmitter, EventEmitter, IRawRecord } from "@talxis/client-libraries"; import { ErrorHelper } from "../../../../utils"; @@ -14,6 +14,10 @@ export interface ICustomColumnsStrategy { onRefresh: () => Promise; /** Returns the currently loaded custom columns synchronously without a network fetch. */ onGetColumns: () => IColumn[]; + /** Fetches the raw records for custom columns. */ + onGetRawRecords: () => Promise; + /** Fetches a single raw record by its ID. */ + onGetRawRecord: (recordId: string) => Promise; } /** Manages the lifecycle of dynamic (user-defined) columns and wraps the strategy with error handling. */ diff --git a/src/components/TaskGrid/data-providers/custom-columns-data-provider/TalxisCustomColumnsDataProviderStrategy.ts b/src/components/TaskGrid/data-providers/custom-columns-data-provider/TalxisCustomColumnsDataProviderStrategy.ts index 9a405238..f2f47327 100644 --- a/src/components/TaskGrid/data-providers/custom-columns-data-provider/TalxisCustomColumnsDataProviderStrategy.ts +++ b/src/components/TaskGrid/data-providers/custom-columns-data-provider/TalxisCustomColumnsDataProviderStrategy.ts @@ -8,9 +8,9 @@ export const ATTRIBUTE_VALUE_ENTITY_NAME = 'talxis_attributevalue'; export const CUSTOM_COLUMNS_REFERENED_ENTITY_NAVIGATION_NAME = 'talxis_task_talxis_attributevalue_regardingobjectid'; interface ITalxisCustomColumnsStrategyParameters { - //entity name to fetch attributes for + //entity name to fetch custom columns for entityName: string; - //record id to fetch attributes for + //record id to fetch custom columns for recordId?: string; } @@ -36,6 +36,15 @@ export class TalxisCustomColumnsStrategy implements ITalxisCustomColumnsStrategy return this.onGetColumns(); } + //get all attribute values based given the + public async onGetRawRecords(): Promise { + return []; + } + + public async onGetRawRecord(recordId: string): Promise { + throw new Error('Method not implemented.'); + } + public onGetColumns(): IColumn[] { return this._attributes.map(attr => { const dataType = Attribute.GetDataTypeFromMetadata({ ...attr as any, attributeDescriptor: attr }); diff --git a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts index ec331769..7d88acd6 100644 --- a/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts +++ b/src/components/TaskGrid/data-providers/saved-query-data-provider/SavedQueryDataProvider.ts @@ -106,6 +106,7 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { private _customColumnsDataProvider?: ICustomColumnsDataProvider; private _nativeColumns: INativeColumns; private _localizationService: ILocalizationService; + private _systemQueriesColumnsMap: Map = new Map(); private _preferredQuery?: Partial & { id: string }; public queryEvents = new EventEmitter(); @@ -203,18 +204,20 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { const userQueries = await this._strategy.onGetUserQueries(); const allQueries = [...systemQueries, ...userQueries]; this._systemQueries = systemQueries; + const systemQueriesColumns = this._includePathColumn(systemQueries.flatMap(query => query.columns)); + this._systemQueriesColumnsMap = new Map(systemQueriesColumns.map(col => [col.name, col])); this._userQueries = userQueries; if (this._systemQueries.length === 0) { throw new Error('At least one system query is required'); } this._currentQuery = allQueries.find(q => q.id === this._preferredQuery?.id) ?? userQueries[0] ?? systemQueries[0]; + this.includeRequiredColumns(this._currentQuery.columns); + this.harmonizeColumns(this._currentQuery.columns); this._currentQuery = { ...this._currentQuery, //makes sure custom columns from the current query are properly merged with the columns from the custom columns data provider strategy ...this._parseSavedQueryMetadata(this._currentQuery) } - this.includeRequiredColumns(this._currentQuery.columns); - this.harmonizeColumns(this._currentQuery.columns); } public includeRequiredColumns(columns: IColumn[]) { @@ -251,8 +254,6 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { } } - - //TODO: this will not work with user queries (localization) private _includePathColumn(columns: IColumn[]) { if (!columns.find(col => col.name === PATH_COLUMN_NAME)) { columns.push({ @@ -264,6 +265,7 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { isHidden: this._currentQuery?.isFlatListEnabled ? false : true }) } + return columns; } private _getMetadataForSavedQuery(provider: ITaskDataProvider): ISavedQueryMetadata { @@ -304,18 +306,29 @@ export class SavedQueryDataProvider implements ISavedQueryDataProvider { } - //TODO: localization for virtual columns private _parseSavedQueryMetadata(metadata: ISavedQueryMetadata): ISavedQueryMetadata { const parsed = metadata; + + // Enrich partial column definitions with full definitions from system queries + let columns = parsed.columns.map(col => { + const systemCol = this._systemQueriesColumnsMap.get(col.name); + if(!systemCol && col.isVirtual) { + throw new Error(`Virtual column ${col.name} is missing from system queries. Make sure all virtual columns are included in the system queries returned by the strategy's onGetSystemQueries method.`); + } + return systemCol ? { ...systemCol, ...col, metadata: { ...systemCol.metadata, ...col.metadata } } : col; + }); + if (!this._customColumnsDataProvider) { - return parsed; + return { ...parsed, columns }; } + const customColumnsMap = new Map(this._customColumnsDataProvider.getColumns().map(col => [col.name, col])); - let columns = parsed.columns.filter(col => this._isCustomColumnResolvable(col.name, customColumnsMap)); + columns = columns.filter(col => this._isCustomColumnResolvable(col.name, customColumnsMap)); columns = columns.map(col => { const customCol = customColumnsMap.get(col.name); return customCol ? { ...customCol, ...col, metadata: { ...customCol.metadata, ...col.metadata } } : col; - }) + }); + return { ...parsed, sorting: parsed.sorting?.filter(sort => this._isCustomColumnResolvable(sort.name, customColumnsMap)), diff --git a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts index 54993478..1393e014 100644 --- a/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts +++ b/src/components/TaskGrid/data-providers/task-data-provider/TaskDataProvider.ts @@ -190,8 +190,7 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro const columnsMap = new Map(columns.map(col => [col.name, col])); const virtualColumns = this._savedQueryDataProvider.getSystemQueries().flatMap(query => query.columns).filter(column => column.isVirtual); for (const virtualColumn of virtualColumns) { - //path column is always part of available columns (hidden) - if (!columnsMap.has(virtualColumn.name) || virtualColumn.name === PATH_COLUMN_NAME) { + if (!columnsMap.has(virtualColumn.name)) { columns.push({ ...virtualColumn, isHidden: false @@ -303,12 +302,12 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro }); } - public openDatasetItem(entityReference: ComponentFramework.EntityReference, context?: { columnName?: string }): void { + public onOpenDatasetItem(entityReference: ComponentFramework.EntityReference, context?: { columnName?: string }): void { if (!context || context?.columnName === this.getNativeColumns().subject) { this.editTasks([entityReference.id.guid]); } else { - this._strategy.onOpenDatasetItem(entityReference); + this._strategy.onOpenDatasetItem(entityReference, context); } } @@ -365,16 +364,6 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro return this._strategy.onIsRecordActive(recordId); } - public onOpenDatasetItem(entityReference: ComponentFramework.EntityReference): void { - if (entityReference.etn === this.getEntityName()) { - this.editTasks([entityReference.id.guid]); - } - else { - //@ts-ignore - typings - this._sourceDataProvider.onOpenDatasetItem(entityReference); - } - } - public async destroy(): Promise { super.destroy(); this.taskEvents.clearEventListeners(); @@ -464,7 +453,7 @@ export class TaskDataProvider extends MemoryDataProvider implements ITaskDataPro }) } - //fetch xml provider will override virtual column metadata by + //fetch xml provider will override virtual column metadata by default private _harmonizeVirtualColumns(virtualColumns: IColumn[], columns: IColumn[]) { columns.map((col, i) => { const virtualCol = virtualColumns.find(virtualCol => virtualCol.name === col.name); From 32a67a4f29b21a92b829a67136650528fd56f126 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Tue, 19 May 2026 10:43:20 +0200 Subject: [PATCH 08/74] feat: Refactor MultiRecordSelector and add LookupMany component - Updated package.json to use local client libraries. - Enhanced GridCustomizer to include column mapping. - Improved MultiRecordSelector with new state management for default options and rendering. - Introduced LookupMany component for handling multiple record selections. - Created reusable components for multi-value handling (MultiValueContainer, MultiValueLabel, MultiValueRemove). - Added styles for new components to ensure consistent UI. - Implemented option rendering with custom styles in LookupMany and MultiRecordSelector. - Added support for navigation in multi-value labels. --- package-lock.json | 1109 ++--------------- package.json | 2 +- .../grid/grid-customizer/GridCustomizer.ts | 3 +- .../grid/lookup-many/LookupMany.tsx | 124 ++ .../grid/lookup-many/components/index.tsx | 13 + .../MultiValueContainer.tsx | 14 + .../multi-value-container/styles.ts | 21 + .../multi-value-label/MultiValueLabel.tsx | 25 + .../components/multi-value-label/index.ts | 1 + .../components/multi-value-label/styles.ts | 16 + .../multi-value-remove/MultiValueRemove.tsx | 17 + .../components/multi-value-remove/styles.ts | 25 + .../lookup-many/components/option/Option.tsx | 15 + .../lookup-many/components/option/styles.ts | 23 + .../components/grid/lookup-many/index.ts | 3 + .../people-lookup-many/PeopleLookupMany.tsx | 14 + .../people-lookup-many/components/index.tsx | 7 + .../multi-value-label/MultiValueLabel.tsx | 11 + .../components/multi-value-label/index.ts | 1 + .../components/multi-value-label/styles.ts | 16 + .../lookup-many/people-lookup-many/index.ts | 1 + .../components/grid/lookup-many/styles.ts | 14 + .../MultiRecordSelector.tsx | 44 +- .../MultiValueContainer.tsx | 14 + .../multi-value-container/styles.ts | 21 + .../multi-value-label/MultiValueLabel.tsx | 25 + .../components/multi-value-label/styles.ts | 16 + .../multi-value-remove/MultiValueRemove.tsx | 17 + .../components/multi-value-remove/styles.ts | 25 + .../components/option/Option.tsx | 15 + .../components/option/styles.ts | 23 + .../grid/multi-record-selector/styles.ts | 14 + 32 files changed, 635 insertions(+), 1054 deletions(-) create mode 100644 src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/index.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/MultiValueLabel.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/index.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/styles.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/index.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/index.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/index.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/styles.ts create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/MultiValueContainer.tsx create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/styles.ts create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/MultiValueLabel.tsx create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/styles.ts create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/MultiValueRemove.tsx create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/styles.ts create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/option/Option.tsx create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/components/option/styles.ts create mode 100644 src/components/TaskGrid/components/grid/multi-record-selector/styles.ts diff --git a/package-lock.json b/package-lock.json index 29f9271f..e816b4c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fluentui/react-migration-v8-v9": "^9.6.20", - "@talxis/client-libraries": "1.2605.1", + "@talxis/client-libraries": "/Users/dominikbrych/Repos/NETWORG/INT0014/src/SDK/ClientLibraries", "@talxis/client-metadata": "^0.0.3", "color": "^4.2.3", "dayjs": "^1.11.10", @@ -136,6 +136,57 @@ "typescript": "^4.6.2" } }, + "../INT0014/src/SDK/ClientLibraries": { + "name": "@talxis/client-libraries", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@azure/msal-browser": "^2.27.0", + "@microsoft/microsoft-graph-client": "^2.2.1", + "@talxis/client-metadata": "^0.0.3", + "async-retry": "^1.3.3", + "dayjs": "^1.11.13", + "debounce": "^2.2.0", + "deep-eql": "^5.0.2", + "exceljs": "^4.3.0", + "fast-sort": "^3.4.1", + "humanize-duration": "^3.32.1", + "liquidjs": "^9.25.1", + "numeral": "^2.0.6", + "validator": "^13.12.0" + }, + "devDependencies": { + "@microsoft/eslint-plugin-power-apps": "^0.2.6", + "@microsoft/microsoft-graph-types": "^1.13.0", + "@rollup/plugin-commonjs": "^21.0.2", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^13.1.3", + "@rollup/plugin-typescript": "^8.3.1", + "@types/async-retry": "^1.4.8", + "@types/deep-eql": "^4.0.2", + "@types/humanize-duration": "^3.27.4", + "@types/node": "^20.11.17", + "@types/numeral": "^2.0.5", + "@types/powerapps-component-framework": "^1.3.15", + "@types/validator": "^13.12.2", + "@types/xrm": "^9.0.81", + "@typescript-eslint/eslint-plugin": "^5.36.1", + "@typescript-eslint/parser": "^5.36.1", + "eslint": "^8.23.0", + "glob": "^11.0.0", + "rollup": "^2.70.0", + "rollup-plugin-delete": "^2.0.0", + "rollup-plugin-dts": "^4.2.0", + "rollup-plugin-multi-input": "^1.3.1", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-visualizer": "^5.12.0", + "typedoc": "^0.26.9", + "typedoc-plugin-markdown": "^4.2.9", + "typescript": "^4.6.2" + } + }, "../INT0015/common/temp/node_modules/.pnpm/react-dom@17.0.2_react@17.0.2/node_modules/react-dom": { "version": "17.0.2", "license": "MIT", @@ -259,27 +310,6 @@ "node": ">=6.0.0" } }, - "node_modules/@azure/msal-browser": { - "version": "2.39.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.39.0.tgz", - "integrity": "sha512-kks/n2AJzKUk+DBqZhiD+7zeQGBl+WpSOQYzWy6hff3bU0ZrYFqr4keFLlzB5VKuKZog0X59/FGHb1RPBDZLVg==", - "license": "MIT", - "dependencies": { - "@azure/msal-common": "13.3.3" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@azure/msal-common": { - "version": "13.3.3", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.3.tgz", - "integrity": "sha512-n278DdCXKeiWhLwhEL7/u9HRMyzhUXLefeajiknf6AmEedoiOiv2r5aRJ7LXdT3NGPyubkdIbthaJlVtmuEqvA==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.18.6", "license": "MIT", @@ -784,47 +814,6 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, - "node_modules/@fast-csv/format": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", - "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", - "license": "MIT", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isboolean": "^3.0.3", - "lodash.isequal": "^4.5.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0" - } - }, - "node_modules/@fast-csv/format/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "license": "MIT" - }, - "node_modules/@fast-csv/parse": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", - "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", - "license": "MIT", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.groupby": "^4.6.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0", - "lodash.isundefined": "^3.0.1", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/@fast-csv/parse/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "license": "MIT" - }, "node_modules/@floating-ui/core": { "version": "1.6.8", "license": "MIT", @@ -2590,23 +2579,6 @@ "version": "1.10.295", "license": "MIT" }, - "node_modules/@microsoft/microsoft-graph-client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-2.2.1.tgz", - "integrity": "sha512-fbDN3UJ+jtSP9llAejqmslMcv498YuIrS3OS/Luivb8OSjdUESZKdP0gcUunnuNIayePVT0/bGYSJTzAIptJQQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.4.4", - "msal": "^1.4.4", - "tslib": "^1.9.3" - } - }, - "node_modules/@microsoft/microsoft-graph-client/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -2757,25 +2729,8 @@ } }, "node_modules/@talxis/client-libraries": { - "version": "1.2605.1", - "resolved": "https://registry.npmjs.org/@talxis/client-libraries/-/client-libraries-1.2605.1.tgz", - "integrity": "sha512-51+6HbStfwJt0EpoWIXdDefriSIJBa28beem+aQwUUzcBVfJ+xl56quISfob67gaUrsDerIM7OX01sOh5GNIkQ==", - "license": "MIT", - "dependencies": { - "@azure/msal-browser": "^2.27.0", - "@microsoft/microsoft-graph-client": "^2.2.1", - "@talxis/client-metadata": "^0.0.3", - "async-retry": "^1.3.3", - "dayjs": "^1.11.13", - "debounce": "^2.2.0", - "deep-eql": "^5.0.2", - "exceljs": "^4.3.0", - "fast-sort": "^3.4.1", - "humanize-duration": "^3.32.1", - "liquidjs": "^9.25.1", - "numeral": "^2.0.6", - "validator": "^13.12.0" - } + "resolved": "../INT0014/src/SDK/ClientLibraries", + "link": true }, "node_modules/@talxis/client-metadata": { "version": "0.0.3", @@ -3386,96 +3341,6 @@ "node": ">=4" } }, - "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "license": "MIT", - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/aria-query": { "version": "5.1.3", "dev": true, @@ -3492,21 +3357,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "license": "MIT", - "dependencies": { - "retry": "0.13.1" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.5", "dev": true, @@ -3659,37 +3509,9 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, "node_modules/big.js": { "version": "5.2.2", "dev": true, @@ -3698,36 +3520,6 @@ "node": "*" } }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "license": "MIT", - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "license": "MIT" - }, "node_modules/boolbase": { "version": "1.0.0", "dev": true, @@ -3735,6 +3527,7 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -3779,61 +3572,11 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "dev": true, "license": "MIT" }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "engines": { - "node": ">=0.2.0" - } - }, "node_modules/builtin-modules": { "version": "3.3.0", "dev": true, @@ -3892,18 +3635,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "license": "MIT/X11", - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "2.4.2", "license": "MIT", @@ -3986,23 +3717,9 @@ "dev": true, "license": "MIT" }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/concat-map": { "version": "0.0.1", + "dev": true, "license": "MIT" }, "node_modules/concat-with-sourcemaps": { @@ -4027,12 +3744,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -4049,31 +3760,6 @@ "node": ">=10" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -4279,18 +3965,9 @@ } } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-equal": { - "version": "2.2.0", - "dev": true, + "node_modules/deep-equal": { + "version": "2.2.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -4493,45 +4170,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, @@ -4573,15 +4211,6 @@ "node": ">= 4" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.12.0", "dev": true, @@ -4735,26 +4364,6 @@ "node": ">=0.8.x" } }, - "node_modules/exceljs": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", - "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", - "license": "MIT", - "dependencies": { - "archiver": "^5.0.0", - "dayjs": "^1.8.34", - "fast-csv": "^4.3.1", - "jszip": "^3.10.1", - "readable-stream": "^3.6.0", - "saxes": "^5.0.1", - "tmp": "^0.2.0", - "unzipper": "^0.10.11", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=8.3.0" - } - }, "node_modules/external-svg-loader": { "version": "1.7.1", "license": "MIT", @@ -4762,19 +4371,6 @@ "idb-keyval": "^6.2.0" } }, - "node_modules/fast-csv": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", - "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", - "license": "MIT", - "dependencies": { - "@fast-csv/format": "4.3.5", - "@fast-csv/parse": "4.3.6" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "license": "MIT" @@ -4799,12 +4395,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-sort": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.4.1.tgz", - "integrity": "sha512-76uvGPsF6So53sZAqenP9UVT3p5l7cyTHkLWVCMinh41Y8NDrK1IYXJgaBMfc1gk7nJiSRZp676kddFG2Aa5+A==", - "license": "MIT" - }, "node_modules/fastq": { "version": "1.15.0", "dev": true, @@ -4864,14 +4454,9 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, "node_modules/fs.realpath": { "version": "1.0.0", + "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -4886,56 +4471,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/function-bind": { "version": "1.1.1", "license": "MIT" @@ -5074,6 +4609,7 @@ }, "node_modules/graceful-fs": { "version": "4.2.10", + "dev": true, "license": "ISC" }, "node_modules/has": { @@ -5185,26 +4721,6 @@ "version": "6.2.1", "license": "Apache-2.0" }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.2.4", "dev": true, @@ -5213,12 +4729,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/import-cwd": { "version": "3.0.0", "dev": true, @@ -5276,6 +4786,7 @@ }, "node_modules/inflight": { "version": "1.0.6", + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -5284,6 +4795,7 @@ }, "node_modules/inherits": { "version": "2.0.4", + "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -5900,103 +5412,10 @@ "dev": true, "license": "MIT" }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jszip/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/keyborg": { "version": "2.6.0", "license": "MIT" }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lilconfig": { "version": "2.0.6", "dev": true, @@ -6026,12 +5445,6 @@ "url": "https://opencollective.com/liquidjs" } }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "license": "ISC" - }, "node_modules/loader-runner": { "version": "4.3.0", "dev": true, @@ -6070,86 +5483,14 @@ "license": "MIT", "peer": true }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "license": "MIT" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "license": "MIT" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "license": "MIT" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "license": "MIT" - }, - "node_modules/lodash.groupby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", - "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "license": "MIT" - }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "license": "MIT" - }, - "node_modules/lodash.isnil": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isundefined": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", - "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", - "license": "MIT" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "dev": true, "license": "MIT" }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "license": "MIT" - }, "node_modules/lodash.uniq": { "version": "4.5.0", + "dev": true, "license": "MIT" }, "node_modules/loose-envify": { @@ -6259,6 +5600,7 @@ }, "node_modules/minimatch": { "version": "3.1.2", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -6267,15 +5609,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.2", "dev": true, @@ -6284,42 +5617,11 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/ms": { "version": "2.1.2", "dev": true, "license": "MIT" }, - "node_modules/msal": { - "version": "1.4.18", - "resolved": "https://registry.npmjs.org/msal/-/msal-1.4.18.tgz", - "integrity": "sha512-QyWMWrZqpwtK6LEqhwtbikxIWqA1EOcdMvDeIDjIXdGU29wM4orwq538sPe1+JfKDIgPmJj1Fgi5B7luaw/IyA==", - "deprecated": "This package is no longer supported. Please use @azure/msal-browser instead.", - "license": "MIT", - "dependencies": { - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/msal/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, "node_modules/nanoid": { "version": "3.3.7", "dev": true, @@ -6347,15 +5649,6 @@ "dev": true, "license": "MIT" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-url": { "version": "6.1.0", "dev": true, @@ -6442,6 +5735,7 @@ }, "node_modules/once": { "version": "1.4.0", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -6521,12 +5815,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6567,6 +5855,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7222,12 +6511,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, "node_modules/promise.series": { "version": "0.2.0", "dev": true, @@ -7354,50 +6637,6 @@ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/redent": { "version": "3.0.0", "dev": true, @@ -7461,15 +6700,6 @@ "node": ">=8" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "dev": true, @@ -7848,6 +7078,7 @@ }, "node_modules/safe-buffer": { "version": "5.1.2", + "dev": true, "license": "MIT" }, "node_modules/safe-identifier": { @@ -7855,18 +7086,6 @@ "dev": true, "license": "ISC" }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/scheduler": { "version": "0.20.2", "license": "MIT", @@ -7909,12 +7128,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, @@ -8012,35 +7225,6 @@ "node": ">= 0.4" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/string-hash": { "version": "1.1.3", "dev": true, @@ -8169,22 +7353,6 @@ "tslib": "^2.3.1" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/terser": { "version": "5.16.2", "dev": true, @@ -8286,15 +7454,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "license": "MIT", @@ -8313,15 +7472,6 @@ "node": ">=8.0" } }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "license": "MIT/X11", - "engines": { - "node": "*" - } - }, "node_modules/tslib": { "version": "2.5.0", "license": "0BSD" @@ -8338,54 +7488,6 @@ "node": ">=4.2.0" } }, - "node_modules/unzipper": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", - "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/unzipper/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/update-browserslist-db": { "version": "1.0.10", "dev": true, @@ -8462,17 +7564,9 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", + "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/validator": { "version": "13.12.0", "license": "MIT", @@ -8760,14 +7854,9 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "dev": true, "license": "ISC" }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "license": "MIT" - }, "node_modules/y18n": { "version": "5.0.8", "dev": true, @@ -8787,62 +7876,6 @@ "engines": { "node": ">= 6" } - }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "license": "MIT", - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } } } } diff --git a/package.json b/package.json index 419bb8d2..771eb71d 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fluentui/react-migration-v8-v9": "^9.6.20", - "@talxis/client-libraries": "1.2605.1", + "@talxis/client-libraries": "/Users/dominikbrych/Repos/NETWORG/INT0014/src/SDK/ClientLibraries", "@talxis/client-metadata": "^0.0.3", "color": "^4.2.3", "dayjs": "^1.11.10", diff --git a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts index 38ce9a8c..7ad0b03e 100644 --- a/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts +++ b/src/components/TaskGrid/components/grid/grid-customizer/GridCustomizer.ts @@ -153,6 +153,7 @@ export class GridCustomizer implements IGridCustomizer { for (const colDef of columnDefs) { colDef.onCellDoubleClicked = () => { } const columnName = colDef.colId as string; + const column = this._taskDataProvider.getColumnsMap()[columnName]; switch (columnName) { case this._nativeColumns.subject: { colDef.cellRenderer = GroupCell; @@ -165,7 +166,7 @@ export class GridCustomizer implements IGridCustomizer { } case this._nativeColumns.percentComplete: { colDef.cellRenderer = PercentComplete; - break + break; } } } diff --git a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx new file mode 100644 index 00000000..d8b516d1 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx @@ -0,0 +1,124 @@ +import { IDataProvider } from '@talxis/client-libraries'; +import * as React from 'react'; +import { SelectInstance } from 'react-select'; +import { useLocalizationService } from '../../../context'; +import { MultiValueContainer } from './components/multi-value-container/MultiValueContainer'; +import { MultiValueRemove } from './components/multi-value-remove/MultiValueRemove'; +import { getLookupManyStyles } from './styles'; +import { DEFAULT_COMPONENTS, ILookupManyComponents } from './components'; + +export interface ILookupManyProps { + dataProvider: IDataProvider; + container: HTMLElement; + isDisabled?: boolean; + selectedRecords?: ComponentFramework.EntityReference[]; + components?: Partial; + onRecordSelect: (selectedRecords: ComponentFramework.EntityReference[]) => void; + onRecordOpen?: (record: ComponentFramework.EntityReference) => void; +} + +export interface ILookupManyRef { + openMenu: () => void; +} + +export const LookupMany = (props: ILookupManyProps) => { + const { dataProvider, selectedRecords = [], container, isDisabled = false, onRecordSelect, onRecordOpen } = props; + const components = { ...DEFAULT_COMPONENTS, ...props.components }; + const localizationService = useLocalizationService(); + const ref = React.useRef(null); + const [renderKey, setRenderKey] = React.useState(0); + const isFirstRenderRef = React.useRef(true); + const [defaultOptions, setDefaultOptions] = React.useState(false); + const MultiValueLabel = React.useRef(components.onRenderMultiValueLabel); + + const onLoadOptions = async (inputValue: string): Promise => { + dataProvider.setSearchQuery(inputValue); + const records = await dataProvider.refresh(); + return records.map(record => { + return { + ...record.getNamedReference(), + rawData: record.getRawData() + } + }) + } + + const openMenu = React.useCallback(() => { + if (!isDisabled) { + ref.current?.focusInput(); + ref.current?.openMenu('first') + } + }, [isDisabled]); + + const onKeyDown = (event: React.KeyboardEvent) => { + switch (event.key) { + case 'Enter': { + ref.current?.openMenu('first'); + } + } + } + + const onMenuOpen = (isOpen: boolean) => { + const controlElement = ref.current?.controlRef; + if (isOpen && !defaultOptions) { + setDefaultOptions(true); + setRenderKey(prev => prev + 1); + } + if (isOpen && controlElement) { + setTimeout(() => { + controlElement.scrollTop = controlElement.scrollHeight; + }, 0); + } + } + + React.useEffect(() => { + container.addEventListener('dblclick', openMenu); + return () => { + container.removeEventListener('dblclick', openMenu); + } + }, []); + + React.useEffect(() => { + if (!isFirstRenderRef.current) { + ref.current?.openMenu('first'); + } + isFirstRenderRef.current = false; + }, [renderKey]); + + + + return + {components.onRenderSelect({ + //@ts-ignore + ref: ref, + isMulti: true, + menuPortalTarget: document.body, + openMenuOnClick: false, + isDisabled: isDisabled, + placeholder: '', + value: selectedRecords, + menuPlacement: 'auto', + isClearable: false, + menuShouldScrollIntoView: false, + defaultOptions: defaultOptions, + styles: getLookupManyStyles(), + components: { + IndicatorSeparator: () => <>, + DropdownIndicator: () => <>, + LoadingIndicator: () => <>, + MultiValueContainer: MultiValueContainer, + MultiValueRemove: MultiValueRemove, + MultiValueLabel: MultiValueLabel.current + }, + onKeyDown: onKeyDown, + noOptionsMessage: () => localizationService.getLocalizedString('noRecordsFound'), + loadingMessage: () => localizationService.getLocalizedString('loading'), + getOptionValue: (record) => record.id.guid, + getOptionLabel: (record) => record.name, + onChange: (selectedRecords) => onRecordSelect(selectedRecords as ComponentFramework.EntityReference[]), + onMenuOpen: () => onMenuOpen(true), + onBlur: () => onMenuOpen(false), + loadOptions: onLoadOptions, + onNavigate: onRecordOpen + })} + +} diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx new file mode 100644 index 00000000..a7e71b59 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx @@ -0,0 +1,13 @@ +import { MultiValueGenericProps, Props } from "react-select"; +import AsyncSelect from "react-select/async"; +import { MultiValueLabel } from "./multi-value-label"; + +export interface ILookupManyComponents { + onRenderMultiValueLabel: (props: MultiValueGenericProps) => JSX.Element; + onRenderSelect: (selectProps: Props) => JSX.Element; +} + +export const DEFAULT_COMPONENTS: ILookupManyComponents = { + onRenderSelect: (selectProps) => , + onRenderMultiValueLabel: (props) => , +} \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx new file mode 100644 index 00000000..36b243da --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { useTheme } from '@fluentui/react'; +import { MultiValueGenericProps } from 'react-select'; +import { getMultiValueContainerStyles } from './styles'; + +export const MultiValueContainer = ({ children, innerProps, selectProps }: MultiValueGenericProps) => { + const theme = useTheme(); + const styles = React.useMemo(() => getMultiValueContainerStyles(theme, selectProps.isDisabled), [theme, selectProps.isDisabled]); + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts new file mode 100644 index 00000000..a4d865f6 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts @@ -0,0 +1,21 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueContainerStyles = (theme: ITheme, isDisabled?: boolean) => { + return mergeStyleSets({ + root: { + display: 'inline-flex', + alignItems: 'center', + backgroundColor: theme.palette.neutralLighter, + border: `1px solid ${theme.palette.neutralLight}`, + borderRadius: 2, + margin: '2px 4px 2px 0', + padding: '1px 4px', + paddingRight: isDisabled ? 4 : 0, + fontSize: 12, + color: theme.palette.neutralPrimary, + maxWidth: 200, + overflow: 'hidden', + height: 20 + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/MultiValueLabel.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/MultiValueLabel.tsx new file mode 100644 index 00000000..a2cafb32 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/MultiValueLabel.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { Link } from '@fluentui/react'; +import { MultiValueGenericProps } from 'react-select'; +import { getMultiValueLabelStyles } from './styles'; + +export const MultiValueLabel = ({ children, selectProps }: MultiValueGenericProps) => { + const styles = React.useMemo(() => getMultiValueLabelStyles(), []); + const { onNavigate } = selectProps as any; + + if (onNavigate) { + return ( + { + e.preventDefault(); + onNavigate(); + }} + > + {children} + + ); + } + + return {children}; +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/index.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/index.ts new file mode 100644 index 00000000..62c39052 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/index.ts @@ -0,0 +1 @@ +export * from './MultiValueLabel'; \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/styles.ts new file mode 100644 index 00000000..d1933dc0 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-label/styles.ts @@ -0,0 +1,16 @@ +import { mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueLabelStyles = () => { + return mergeStyleSets({ + root: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + fontWeight: 600 + }, + link: { + fontWeight: 600, + pointerEvents: 'all', + } + }); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx new file mode 100644 index 00000000..db3a7937 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { Icon, useTheme } from '@fluentui/react'; +import { MultiValueRemoveProps } from 'react-select'; +import { getMultiValueRemoveStyles } from './styles'; + +export const MultiValueRemove = ({ innerProps, selectProps }: MultiValueRemoveProps) => { + const theme = useTheme(); + const styles = React.useMemo(() => getMultiValueRemoveStyles(theme), [theme]); + if(selectProps.isDisabled) { + return <> + } + return ( +
+ +
+ ); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts new file mode 100644 index 00000000..b553f9fc --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts @@ -0,0 +1,25 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueRemoveStyles = (theme: ITheme) => { + return mergeStyleSets({ + root: { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + marginLeft: 2, + padding: 1, + cursor: 'pointer', + borderRadius: 2, + color: theme.palette.neutralSecondary, + height: 20, + width: 20, + ':hover': { + color: theme.palette.neutralPrimary, + backgroundColor: theme.palette.neutralLight, + }, + }, + icon: { + fontSize: 10, + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx new file mode 100644 index 00000000..cecdb6c7 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { useTheme } from '@fluentui/react'; +import { OptionProps } from 'react-select'; +import { getOptionStyles } from './styles'; + +export const Option = (props: OptionProps) => { + const { children, innerProps, isFocused, isSelected } = props; + const theme = useTheme(); + const styles = React.useMemo(() => getOptionStyles(theme, isFocused, isSelected), [theme, isFocused, isSelected]); + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts new file mode 100644 index 00000000..31bc5308 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts @@ -0,0 +1,23 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getOptionStyles = (theme: ITheme, isFocused: boolean, isSelected: boolean) => { + return mergeStyleSets({ + root: { + padding: '6px 8px', + cursor: 'pointer', + fontSize: 14, + color: theme.semanticColors.bodyText, + backgroundColor: isSelected + ? theme.palette.themeLighter + : isFocused + ? theme.semanticColors.menuItemBackgroundHovered + : theme.semanticColors.menuBackground, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + ':active': { + backgroundColor: theme.semanticColors.menuItemBackgroundPressed, + }, + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/index.ts b/src/components/TaskGrid/components/grid/lookup-many/index.ts new file mode 100644 index 00000000..609a7387 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/index.ts @@ -0,0 +1,3 @@ +export * from './LookupMany'; +export * from './people-lookup-many'; +export * from 'react-select'; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx new file mode 100644 index 00000000..19ccb45b --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx @@ -0,0 +1,14 @@ +import { ILookupManyProps, LookupMany } from "../LookupMany"; +import { DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS } from "./components"; + +export interface IPeopleLookupManyProps extends ILookupManyProps { + +} + +export const PeopleLookupMany = (props: ILookupManyProps) => { + const components = {...DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS, ...props.components}; + return +} \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx new file mode 100644 index 00000000..3a8f3c8f --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx @@ -0,0 +1,7 @@ +import { DEFAULT_COMPONENTS, ILookupManyComponents } from "../../components"; +import { MultiValueLabel } from "./multi-value-label"; + +export const DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS: ILookupManyComponents = { + ...DEFAULT_COMPONENTS, + onRenderMultiValueLabel: (props) => , +} \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx new file mode 100644 index 00000000..36e4b5df --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; +import { Link, Persona } from '@fluentui/react'; +import { MultiValueGenericProps } from 'react-select'; +import { getMultiValueLabelStyles } from './styles'; + +export const MultiValueLabel = ({ children, selectProps }: MultiValueGenericProps) => { + const styles = React.useMemo(() => getMultiValueLabelStyles(), []); + const { onNavigate } = selectProps as any; + + return +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/index.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/index.ts new file mode 100644 index 00000000..62c39052 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/index.ts @@ -0,0 +1 @@ +export * from './MultiValueLabel'; \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts new file mode 100644 index 00000000..d1933dc0 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts @@ -0,0 +1,16 @@ +import { mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueLabelStyles = () => { + return mergeStyleSets({ + root: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + fontWeight: 600 + }, + link: { + fontWeight: 600, + pointerEvents: 'all', + } + }); +}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/index.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/index.ts new file mode 100644 index 00000000..9b1b4ea1 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/index.ts @@ -0,0 +1 @@ +export * from './PeopleLookupMany'; \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/styles.ts new file mode 100644 index 00000000..ad7d7191 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/styles.ts @@ -0,0 +1,14 @@ +import { StylesConfig } from 'react-select'; + +export const getLookupManyStyles = (): StylesConfig => { + return { + control: (base) => ({ + ...base, + maxHeight: 200, + overflow: 'auto', + border: 'none', + background: 'none', + boxShadow: 'none', + }), + }; +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/MultiRecordSelector.tsx b/src/components/TaskGrid/components/grid/multi-record-selector/MultiRecordSelector.tsx index 70691510..8893c33b 100644 --- a/src/components/TaskGrid/components/grid/multi-record-selector/MultiRecordSelector.tsx +++ b/src/components/TaskGrid/components/grid/multi-record-selector/MultiRecordSelector.tsx @@ -4,12 +4,17 @@ import { SelectInstance } from 'react-select'; import AsyncSelect from 'react-select/async'; import { AsyncProps } from 'react-select/dist/declarations/src/useAsync'; import { useLocalizationService } from '../../../context'; +import { MultiValueContainer } from './components/multi-value-container/MultiValueContainer'; +import { MultiValueRemove } from './components/multi-value-remove/MultiValueRemove'; +import { MultiValueLabel } from './components/multi-value-label/MultiValueLabel'; +import { getMultiRecordSelectorStyles } from './styles'; interface IMultiRecordSelectorProps { onSelectionChange: (selectedRecords: ComponentFramework.EntityReference[]) => void; dataProvider: IDataProvider; container: HTMLElement; selectedRecords?: ComponentFramework.EntityReference[]; + onNavigate?: (record: ComponentFramework.EntityReference) => void; onGetRef?: (ref: IMultRecordSelectorRef) => void; onMenuToggle?: (isOpen: boolean) => void; onOverrideComponentProps?: (props: AsyncProps) => AsyncProps; @@ -24,6 +29,9 @@ export const MultiRecordSelector = (props: IMultiRecordSelectorProps) => { const localizationService = useLocalizationService(); const onOverrideComponentProps = props.onOverrideComponentProps ?? ((p) => p); const ref = React.useRef(null); + const [renderKey, setRenderKey] = React.useState(0); + const isFirstRenderRef = React.useRef(true); + const [defaultOptions, setDefaultOptions] = React.useState(false); const onLoadOptions = async (inputValue: string): Promise => { dataProvider.setSearchQuery(inputValue); @@ -54,6 +62,10 @@ export const MultiRecordSelector = (props: IMultiRecordSelectorProps) => { const onMenuOpen = (isOpen: boolean) => { props.onMenuToggle?.(isOpen); const controlElement = ref.current?.controlRef; + if(isOpen && !defaultOptions) { + setDefaultOptions(true); + setRenderKey(prev => prev + 1); + } if (isOpen && controlElement) { setTimeout(() => { controlElement.scrollTop = controlElement.scrollHeight; @@ -68,6 +80,14 @@ export const MultiRecordSelector = (props: IMultiRecordSelectorProps) => { } }, []); + React.useEffect(() => { + if (!isFirstRenderRef.current) { + ref.current?.openMenu('first'); + } + isFirstRenderRef.current = false; + }, [renderKey]); + + props.onGetRef?.({ openMenu }); const componentProps = onOverrideComponentProps({ isMulti: true, @@ -81,6 +101,7 @@ export const MultiRecordSelector = (props: IMultiRecordSelectorProps) => { menuPlacement: 'auto', isClearable: false, menuShouldScrollIntoView: false, + defaultOptions: defaultOptions, noOptionsMessage: () => localizationService.getLocalizedString('noRecordsFound'), loadingMessage: () => localizationService.getLocalizedString('loading'), getOptionValue: (record) => record.id.guid, @@ -89,24 +110,19 @@ export const MultiRecordSelector = (props: IMultiRecordSelectorProps) => { onMenuOpen: () => onMenuOpen(true), onBlur: () => onMenuOpen(false), loadOptions: onLoadOptions, - styles: { - control: (base) => { - return { - ...base, - maxHeight: 200, - overflow: 'auto', - border: 'none', - background: 'none', - boxShadow: 'none', - } - }, - }, + onNavigate: props.onNavigate, + styles: getMultiRecordSelectorStyles(), components: { IndicatorSeparator: () => <>, DropdownIndicator: () => <>, LoadingIndicator: () => <>, + MultiValueContainer: MultiValueContainer, + MultiValueRemove: MultiValueRemove, + MultiValueLabel: MultiValueLabel, + //Option: Option, }, }) - - return + return + + } \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/MultiValueContainer.tsx b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/MultiValueContainer.tsx new file mode 100644 index 00000000..765206e3 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/MultiValueContainer.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { useTheme } from '@fluentui/react'; +import { MultiValueGenericProps } from 'react-select'; +import { getMultiValueContainerStyles } from './styles'; + +export const MultiValueContainer = ({ children, innerProps, selectProps }: MultiValueGenericProps) => { + const theme = useTheme(); + const styles = React.useMemo(() => getMultiValueContainerStyles(theme, selectProps.isDisabled), [theme, selectProps.isDisabled]); + return ( +
+ {children} +
+ ); +}; \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/styles.ts b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/styles.ts new file mode 100644 index 00000000..a4d865f6 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-container/styles.ts @@ -0,0 +1,21 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueContainerStyles = (theme: ITheme, isDisabled?: boolean) => { + return mergeStyleSets({ + root: { + display: 'inline-flex', + alignItems: 'center', + backgroundColor: theme.palette.neutralLighter, + border: `1px solid ${theme.palette.neutralLight}`, + borderRadius: 2, + margin: '2px 4px 2px 0', + padding: '1px 4px', + paddingRight: isDisabled ? 4 : 0, + fontSize: 12, + color: theme.palette.neutralPrimary, + maxWidth: 200, + overflow: 'hidden', + height: 20 + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/MultiValueLabel.tsx b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/MultiValueLabel.tsx new file mode 100644 index 00000000..a2cafb32 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/MultiValueLabel.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { Link } from '@fluentui/react'; +import { MultiValueGenericProps } from 'react-select'; +import { getMultiValueLabelStyles } from './styles'; + +export const MultiValueLabel = ({ children, selectProps }: MultiValueGenericProps) => { + const styles = React.useMemo(() => getMultiValueLabelStyles(), []); + const { onNavigate } = selectProps as any; + + if (onNavigate) { + return ( + { + e.preventDefault(); + onNavigate(); + }} + > + {children} + + ); + } + + return {children}; +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/styles.ts b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/styles.ts new file mode 100644 index 00000000..d1933dc0 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-label/styles.ts @@ -0,0 +1,16 @@ +import { mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueLabelStyles = () => { + return mergeStyleSets({ + root: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + fontWeight: 600 + }, + link: { + fontWeight: 600, + pointerEvents: 'all', + } + }); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/MultiValueRemove.tsx b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/MultiValueRemove.tsx new file mode 100644 index 00000000..db3a7937 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/MultiValueRemove.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { Icon, useTheme } from '@fluentui/react'; +import { MultiValueRemoveProps } from 'react-select'; +import { getMultiValueRemoveStyles } from './styles'; + +export const MultiValueRemove = ({ innerProps, selectProps }: MultiValueRemoveProps) => { + const theme = useTheme(); + const styles = React.useMemo(() => getMultiValueRemoveStyles(theme), [theme]); + if(selectProps.isDisabled) { + return <> + } + return ( +
+ +
+ ); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/styles.ts b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/styles.ts new file mode 100644 index 00000000..b553f9fc --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/multi-value-remove/styles.ts @@ -0,0 +1,25 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getMultiValueRemoveStyles = (theme: ITheme) => { + return mergeStyleSets({ + root: { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + marginLeft: 2, + padding: 1, + cursor: 'pointer', + borderRadius: 2, + color: theme.palette.neutralSecondary, + height: 20, + width: 20, + ':hover': { + color: theme.palette.neutralPrimary, + backgroundColor: theme.palette.neutralLight, + }, + }, + icon: { + fontSize: 10, + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/option/Option.tsx b/src/components/TaskGrid/components/grid/multi-record-selector/components/option/Option.tsx new file mode 100644 index 00000000..cecdb6c7 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/option/Option.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { useTheme } from '@fluentui/react'; +import { OptionProps } from 'react-select'; +import { getOptionStyles } from './styles'; + +export const Option = (props: OptionProps) => { + const { children, innerProps, isFocused, isSelected } = props; + const theme = useTheme(); + const styles = React.useMemo(() => getOptionStyles(theme, isFocused, isSelected), [theme, isFocused, isSelected]); + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/components/option/styles.ts b/src/components/TaskGrid/components/grid/multi-record-selector/components/option/styles.ts new file mode 100644 index 00000000..31bc5308 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/components/option/styles.ts @@ -0,0 +1,23 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getOptionStyles = (theme: ITheme, isFocused: boolean, isSelected: boolean) => { + return mergeStyleSets({ + root: { + padding: '6px 8px', + cursor: 'pointer', + fontSize: 14, + color: theme.semanticColors.bodyText, + backgroundColor: isSelected + ? theme.palette.themeLighter + : isFocused + ? theme.semanticColors.menuItemBackgroundHovered + : theme.semanticColors.menuBackground, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + ':active': { + backgroundColor: theme.semanticColors.menuItemBackgroundPressed, + }, + }, + }); +}; diff --git a/src/components/TaskGrid/components/grid/multi-record-selector/styles.ts b/src/components/TaskGrid/components/grid/multi-record-selector/styles.ts new file mode 100644 index 00000000..8fb6e406 --- /dev/null +++ b/src/components/TaskGrid/components/grid/multi-record-selector/styles.ts @@ -0,0 +1,14 @@ +import { StylesConfig } from 'react-select'; + +export const getMultiRecordSelectorStyles = (): StylesConfig => { + return { + control: (base) => ({ + ...base, + maxHeight: 200, + overflow: 'auto', + border: 'none', + background: 'none', + boxShadow: 'none', + }), + }; +}; From 949487353c06d2f442538c4b8d963ef1074ec522 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Tue, 19 May 2026 11:24:00 +0200 Subject: [PATCH 09/74] feat(TaskGrid): enhance LookupMany component with context support and customizable styles --- .../grid/lookup-many/LookupMany.tsx | 76 ++++++++++--------- .../MultiValueContainer.tsx | 4 +- .../multi-value-container/styles.ts | 4 +- .../multi-value-remove/MultiValueRemove.tsx | 4 +- .../components/multi-value-remove/styles.ts | 4 +- .../components/grid/lookup-many/context.ts | 8 ++ .../people-lookup-many/PeopleLookupMany.tsx | 18 +++-- .../people-lookup-many/components/index.tsx | 5 ++ .../multi-value-label/MultiValueLabel.tsx | 35 ++++++++- .../components/multi-value-label/styles.ts | 19 +---- .../lookup-many/people-lookup-many/context.ts | 8 ++ 11 files changed, 116 insertions(+), 69 deletions(-) create mode 100644 src/components/TaskGrid/components/grid/lookup-many/context.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/context.ts diff --git a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx index d8b516d1..dcaefab8 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx @@ -6,10 +6,12 @@ import { MultiValueContainer } from './components/multi-value-container/MultiVal import { MultiValueRemove } from './components/multi-value-remove/MultiValueRemove'; import { getLookupManyStyles } from './styles'; import { DEFAULT_COMPONENTS, ILookupManyComponents } from './components'; +import { LookupManyPropsContext } from './context'; export interface ILookupManyProps { dataProvider: IDataProvider; container: HTMLElement; + selectedRecordHeight?: number; isDisabled?: boolean; selectedRecords?: ComponentFramework.EntityReference[]; components?: Partial; @@ -84,41 +86,41 @@ export const LookupMany = (props: ILookupManyProps) => { isFirstRenderRef.current = false; }, [renderKey]); - - - return - {components.onRenderSelect({ - //@ts-ignore - ref: ref, - isMulti: true, - menuPortalTarget: document.body, - openMenuOnClick: false, - isDisabled: isDisabled, - placeholder: '', - value: selectedRecords, - menuPlacement: 'auto', - isClearable: false, - menuShouldScrollIntoView: false, - defaultOptions: defaultOptions, - styles: getLookupManyStyles(), - components: { - IndicatorSeparator: () => <>, - DropdownIndicator: () => <>, - LoadingIndicator: () => <>, - MultiValueContainer: MultiValueContainer, - MultiValueRemove: MultiValueRemove, - MultiValueLabel: MultiValueLabel.current - }, - onKeyDown: onKeyDown, - noOptionsMessage: () => localizationService.getLocalizedString('noRecordsFound'), - loadingMessage: () => localizationService.getLocalizedString('loading'), - getOptionValue: (record) => record.id.guid, - getOptionLabel: (record) => record.name, - onChange: (selectedRecords) => onRecordSelect(selectedRecords as ComponentFramework.EntityReference[]), - onMenuOpen: () => onMenuOpen(true), - onBlur: () => onMenuOpen(false), - loadOptions: onLoadOptions, - onNavigate: onRecordOpen - })} - + return + + {components.onRenderSelect({ + //@ts-ignore + ref: ref, + isMulti: true, + menuPortalTarget: document.body, + openMenuOnClick: false, + isDisabled: isDisabled, + placeholder: '', + value: selectedRecords, + menuPlacement: 'auto', + isClearable: false, + menuShouldScrollIntoView: false, + defaultOptions: defaultOptions, + styles: getLookupManyStyles(), + components: { + IndicatorSeparator: () => <>, + DropdownIndicator: () => <>, + LoadingIndicator: () => <>, + MultiValueContainer: MultiValueContainer, + MultiValueRemove: MultiValueRemove, + MultiValueLabel: MultiValueLabel.current + }, + onKeyDown: onKeyDown, + noOptionsMessage: () => localizationService.getLocalizedString('noRecordsFound'), + loadingMessage: () => localizationService.getLocalizedString('loading'), + getOptionValue: (record) => record.id.guid, + getOptionLabel: (record) => record.name, + onChange: (selectedRecords) => onRecordSelect(selectedRecords as ComponentFramework.EntityReference[]), + onMenuOpen: () => onMenuOpen(true), + onBlur: () => onMenuOpen(false), + loadOptions: onLoadOptions, + onNavigate: onRecordOpen + })} + + } diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx index 36b243da..3c62aaa5 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/MultiValueContainer.tsx @@ -2,10 +2,12 @@ import * as React from 'react'; import { useTheme } from '@fluentui/react'; import { MultiValueGenericProps } from 'react-select'; import { getMultiValueContainerStyles } from './styles'; +import { useLookupManyProps } from '../../context'; export const MultiValueContainer = ({ children, innerProps, selectProps }: MultiValueGenericProps) => { const theme = useTheme(); - const styles = React.useMemo(() => getMultiValueContainerStyles(theme, selectProps.isDisabled), [theme, selectProps.isDisabled]); + const props = useLookupManyProps(); + const styles = React.useMemo(() => getMultiValueContainerStyles(theme, selectProps.isDisabled, props.selectedRecordHeight), [theme, selectProps.isDisabled, props.selectedRecordHeight]); return (
{children} diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts index a4d865f6..73ad683e 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-container/styles.ts @@ -1,6 +1,6 @@ import { ITheme, mergeStyleSets } from '@fluentui/react'; -export const getMultiValueContainerStyles = (theme: ITheme, isDisabled?: boolean) => { +export const getMultiValueContainerStyles = (theme: ITheme, isDisabled?: boolean, height?: number) => { return mergeStyleSets({ root: { display: 'inline-flex', @@ -15,7 +15,7 @@ export const getMultiValueContainerStyles = (theme: ITheme, isDisabled?: boolean color: theme.palette.neutralPrimary, maxWidth: 200, overflow: 'hidden', - height: 20 + height: height ?? 20 }, }); }; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx index db3a7937..b3bdc5e2 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/MultiValueRemove.tsx @@ -2,10 +2,12 @@ import * as React from 'react'; import { Icon, useTheme } from '@fluentui/react'; import { MultiValueRemoveProps } from 'react-select'; import { getMultiValueRemoveStyles } from './styles'; +import { useLookupManyProps } from '../../context'; export const MultiValueRemove = ({ innerProps, selectProps }: MultiValueRemoveProps) => { const theme = useTheme(); - const styles = React.useMemo(() => getMultiValueRemoveStyles(theme), [theme]); + const props = useLookupManyProps(); + const styles = React.useMemo(() => getMultiValueRemoveStyles(theme, props.selectedRecordHeight), [theme, props.selectedRecordHeight]); if(selectProps.isDisabled) { return <> } diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts index b553f9fc..6880af4a 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts +++ b/src/components/TaskGrid/components/grid/lookup-many/components/multi-value-remove/styles.ts @@ -1,6 +1,6 @@ import { ITheme, mergeStyleSets } from '@fluentui/react'; -export const getMultiValueRemoveStyles = (theme: ITheme) => { +export const getMultiValueRemoveStyles = (theme: ITheme, height?: number) => { return mergeStyleSets({ root: { display: 'inline-flex', @@ -11,7 +11,7 @@ export const getMultiValueRemoveStyles = (theme: ITheme) => { cursor: 'pointer', borderRadius: 2, color: theme.palette.neutralSecondary, - height: 20, + height: height ?? 20, width: 20, ':hover': { color: theme.palette.neutralPrimary, diff --git a/src/components/TaskGrid/components/grid/lookup-many/context.ts b/src/components/TaskGrid/components/grid/lookup-many/context.ts new file mode 100644 index 00000000..1d0ff2c9 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/context.ts @@ -0,0 +1,8 @@ +import React from "react"; +import { ILookupManyProps } from "./LookupMany"; + +export const LookupManyPropsContext = React.createContext(null as any); + +export const useLookupManyProps = () => { + return React.useContext(LookupManyPropsContext); +} \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx index 19ccb45b..b068aa6b 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/PeopleLookupMany.tsx @@ -1,14 +1,18 @@ import { ILookupManyProps, LookupMany } from "../LookupMany"; import { DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS } from "./components"; +import { PeopleLookupManyPropsContext } from "./context"; export interface IPeopleLookupManyProps extends ILookupManyProps { - + imageUrlPropertyName?: string } -export const PeopleLookupMany = (props: ILookupManyProps) => { - const components = {...DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS, ...props.components}; - return +export const PeopleLookupMany = (props: IPeopleLookupManyProps) => { + const components = { ...DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS, ...props.components }; + return + + } \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx index 3a8f3c8f..1fad172c 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx @@ -1,7 +1,12 @@ import { DEFAULT_COMPONENTS, ILookupManyComponents } from "../../components"; import { MultiValueLabel } from "./multi-value-label"; +export interface IPeopleLookupManyComponents extends ILookupManyComponents { + onRenderMultiValueLabel: (props: any) => JSX.Element; +} + export const DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS: ILookupManyComponents = { ...DEFAULT_COMPONENTS, onRenderMultiValueLabel: (props) => , + onRenderSelect: (props) => } \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx index 36e4b5df..085807c9 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/MultiValueLabel.tsx @@ -1,11 +1,40 @@ import * as React from 'react'; -import { Link, Persona } from '@fluentui/react'; +import { Link, Persona, PersonaSize, TooltipHost } from '@fluentui/react'; import { MultiValueGenericProps } from 'react-select'; import { getMultiValueLabelStyles } from './styles'; +import { usePeopleLookupManyProps } from '../../context'; -export const MultiValueLabel = ({ children, selectProps }: MultiValueGenericProps) => { +export const MultiValueLabel = ({ data, selectProps }: MultiValueGenericProps) => { const styles = React.useMemo(() => getMultiValueLabelStyles(), []); + const {imageUrlPropertyName = 'imageurl'} = usePeopleLookupManyProps(); const { onNavigate } = selectProps as any; + const imageUrl = (data as any).rawData?.[imageUrlPropertyName] ?? undefined; - return + + const persona = ( + + + + ); + + if (onNavigate) { + return ( + { + e.preventDefault(); + onNavigate(data); + }} + > + {persona} + + ); + } + + return <>{persona}; }; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts index d1933dc0..19041323 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/multi-value-label/styles.ts @@ -1,16 +1,3 @@ -import { mergeStyleSets } from '@fluentui/react'; - -export const getMultiValueLabelStyles = () => { - return mergeStyleSets({ - root: { - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - fontWeight: 600 - }, - link: { - fontWeight: 600, - pointerEvents: 'all', - } - }); -}; +export const getMultiValueLabelStyles = () => ({ + persona: { margin: 0 } +}); diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/context.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/context.ts new file mode 100644 index 00000000..8f2c6f52 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/context.ts @@ -0,0 +1,8 @@ +import React from "react"; +import { IPeopleLookupManyProps } from "./PeopleLookupMany"; + +export const PeopleLookupManyPropsContext = React.createContext(null as any); + +export const usePeopleLookupManyProps = () => { + return React.useContext(PeopleLookupManyPropsContext); +} \ No newline at end of file From 80294fbd623afa8f3c487d1fd70a1df91c922824 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Tue, 19 May 2026 11:39:35 +0200 Subject: [PATCH 10/74] feat(TaskGrid): add customizable option rendering and styles for LookupMany component --- .../grid/lookup-many/LookupMany.tsx | 15 ++++++------ .../grid/lookup-many/components/index.tsx | 4 +++- .../people-lookup-many/components/index.tsx | 5 ---- .../components/option/Option.tsx | 8 +++++++ .../components/option/index.ts | 1 + .../components/option/styles.ts | 23 +++++++++++++++++++ 6 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/Option.tsx create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/index.ts create mode 100644 src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/styles.ts diff --git a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx index dcaefab8..f1e5dbe3 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx @@ -19,10 +19,6 @@ export interface ILookupManyProps { onRecordOpen?: (record: ComponentFramework.EntityReference) => void; } -export interface ILookupManyRef { - openMenu: () => void; -} - export const LookupMany = (props: ILookupManyProps) => { const { dataProvider, selectedRecords = [], container, isDisabled = false, onRecordSelect, onRecordOpen } = props; const components = { ...DEFAULT_COMPONENTS, ...props.components }; @@ -32,6 +28,7 @@ export const LookupMany = (props: ILookupManyProps) => { const isFirstRenderRef = React.useRef(true); const [defaultOptions, setDefaultOptions] = React.useState(false); const MultiValueLabel = React.useRef(components.onRenderMultiValueLabel); + const Option = React.useRef(components.onRenderOption); const onLoadOptions = async (inputValue: string): Promise => { dataProvider.setSearchQuery(inputValue); @@ -73,9 +70,9 @@ export const LookupMany = (props: ILookupManyProps) => { } React.useEffect(() => { - container.addEventListener('dblclick', openMenu); + //container.addEventListener('dblclick', openMenu); return () => { - container.removeEventListener('dblclick', openMenu); + //container.removeEventListener('dblclick', openMenu); } }, []); @@ -93,7 +90,8 @@ export const LookupMany = (props: ILookupManyProps) => { ref: ref, isMulti: true, menuPortalTarget: document.body, - openMenuOnClick: false, + closeMenuOnSelect: false, + //openMenuOnClick: false, isDisabled: isDisabled, placeholder: '', value: selectedRecords, @@ -108,7 +106,8 @@ export const LookupMany = (props: ILookupManyProps) => { LoadingIndicator: () => <>, MultiValueContainer: MultiValueContainer, MultiValueRemove: MultiValueRemove, - MultiValueLabel: MultiValueLabel.current + MultiValueLabel: MultiValueLabel.current, + Option: Option.current }, onKeyDown: onKeyDown, noOptionsMessage: () => localizationService.getLocalizedString('noRecordsFound'), diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx index a7e71b59..23c3744d 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/components/index.tsx @@ -1,13 +1,15 @@ -import { MultiValueGenericProps, Props } from "react-select"; +import { MultiValueGenericProps, OptionProps, Props, components } from "react-select"; import AsyncSelect from "react-select/async"; import { MultiValueLabel } from "./multi-value-label"; export interface ILookupManyComponents { onRenderMultiValueLabel: (props: MultiValueGenericProps) => JSX.Element; onRenderSelect: (selectProps: Props) => JSX.Element; + onRenderOption: (props: OptionProps) => JSX.Element; } export const DEFAULT_COMPONENTS: ILookupManyComponents = { onRenderSelect: (selectProps) => , onRenderMultiValueLabel: (props) => , + onRenderOption: (props) => } \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx index 1fad172c..3a8f3c8f 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx @@ -1,12 +1,7 @@ import { DEFAULT_COMPONENTS, ILookupManyComponents } from "../../components"; import { MultiValueLabel } from "./multi-value-label"; -export interface IPeopleLookupManyComponents extends ILookupManyComponents { - onRenderMultiValueLabel: (props: any) => JSX.Element; -} - export const DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS: ILookupManyComponents = { ...DEFAULT_COMPONENTS, onRenderMultiValueLabel: (props) => , - onRenderSelect: (props) => } \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/Option.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/Option.tsx new file mode 100644 index 00000000..d59aa84e --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/Option.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import { useTheme } from '@fluentui/react'; +import { OptionProps } from 'react-select'; +import { getOptionStyles } from './styles'; + +export const Option = (props: OptionProps) => { + return
Option
+}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/index.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/index.ts new file mode 100644 index 00000000..a2e93736 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/index.ts @@ -0,0 +1 @@ +export * from './Option'; \ No newline at end of file diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/styles.ts new file mode 100644 index 00000000..31bc5308 --- /dev/null +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/option/styles.ts @@ -0,0 +1,23 @@ +import { ITheme, mergeStyleSets } from '@fluentui/react'; + +export const getOptionStyles = (theme: ITheme, isFocused: boolean, isSelected: boolean) => { + return mergeStyleSets({ + root: { + padding: '6px 8px', + cursor: 'pointer', + fontSize: 14, + color: theme.semanticColors.bodyText, + backgroundColor: isSelected + ? theme.palette.themeLighter + : isFocused + ? theme.semanticColors.menuItemBackgroundHovered + : theme.semanticColors.menuBackground, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + ':active': { + backgroundColor: theme.semanticColors.menuItemBackgroundPressed, + }, + }, + }); +}; From 4e2f6d78ddcb1ab632a3e945aa58f20563da8f01 Mon Sep 17 00:00:00 2001 From: Dominik Brych Date: Tue, 19 May 2026 12:34:44 +0200 Subject: [PATCH 11/74] feat(TaskGrid): update LookupMany component with enhanced option rendering and styles --- .../grid/lookup-many/LookupMany.tsx | 5 ++- .../lookup-many/components/option/Option.tsx | 15 --------- .../lookup-many/components/option/styles.ts | 23 -------------- .../people-lookup-many/components/index.tsx | 2 ++ .../components/option/Option.tsx | 24 ++++++++++++-- .../components/option/styles.ts | 31 +++++-------------- 6 files changed, 35 insertions(+), 65 deletions(-) delete mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx delete mode 100644 src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts diff --git a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx index f1e5dbe3..8757964f 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/LookupMany.tsx @@ -36,7 +36,10 @@ export const LookupMany = (props: ILookupManyProps) => { return records.map(record => { return { ...record.getNamedReference(), - rawData: record.getRawData() + rawData: { + ...record.getRawData(), + imageurl: 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-react-assets/persona-male.png' + } } }) } diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx b/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx deleted file mode 100644 index cecdb6c7..00000000 --- a/src/components/TaskGrid/components/grid/lookup-many/components/option/Option.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import { useTheme } from '@fluentui/react'; -import { OptionProps } from 'react-select'; -import { getOptionStyles } from './styles'; - -export const Option = (props: OptionProps) => { - const { children, innerProps, isFocused, isSelected } = props; - const theme = useTheme(); - const styles = React.useMemo(() => getOptionStyles(theme, isFocused, isSelected), [theme, isFocused, isSelected]); - return ( -
- {children} -
- ); -}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts b/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts deleted file mode 100644 index 31bc5308..00000000 --- a/src/components/TaskGrid/components/grid/lookup-many/components/option/styles.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ITheme, mergeStyleSets } from '@fluentui/react'; - -export const getOptionStyles = (theme: ITheme, isFocused: boolean, isSelected: boolean) => { - return mergeStyleSets({ - root: { - padding: '6px 8px', - cursor: 'pointer', - fontSize: 14, - color: theme.semanticColors.bodyText, - backgroundColor: isSelected - ? theme.palette.themeLighter - : isFocused - ? theme.semanticColors.menuItemBackgroundHovered - : theme.semanticColors.menuBackground, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - ':active': { - backgroundColor: theme.semanticColors.menuItemBackgroundPressed, - }, - }, - }); -}; diff --git a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx index 3a8f3c8f..1a24aaa6 100644 --- a/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx +++ b/src/components/TaskGrid/components/grid/lookup-many/people-lookup-many/components/index.tsx @@ -1,7 +1,9 @@ import { DEFAULT_COMPONENTS, ILookupManyComponents } from "../../components"; import { MultiValueLabel } from "./multi-value-label"; +import { Option } from "./option"; export const DEFAULT_PEOPLE_LOOKUP_MANY_COMPONENTS: ILookupManyComponents = { ...DEFAULT_COMPONENTS, onRenderMultiValueLabel: (props) => , + onRenderOption: (props) =>