@ynput/ayon-frontend-shared 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index.cjs4.js +4 -4
- package/dist/_virtual/index.cjs5.js +4 -4
- package/dist/_virtual/index.cjs6.js +2 -2
- package/dist/_virtual/index.cjs7.js +2 -2
- package/dist/_virtual/index.es4.js +4 -4
- package/dist/_virtual/index.es5.js +4 -4
- package/dist/_virtual/index.es6.js +2 -2
- package/dist/_virtual/index.es7.js +2 -2
- package/dist/hooks.cjs.js +2 -0
- package/dist/hooks.cjs.js.map +1 -1
- package/dist/hooks.es.js +2 -0
- package/dist/hooks.es.js.map +1 -1
- package/dist/node_modules/@reduxjs/toolkit/dist/query/react/rtk-query-react.modern.cjs.js +19 -10
- package/dist/node_modules/@reduxjs/toolkit/dist/query/react/rtk-query-react.modern.cjs.js.map +1 -1
- package/dist/node_modules/@reduxjs/toolkit/dist/query/react/rtk-query-react.modern.es.js +21 -12
- package/dist/node_modules/@reduxjs/toolkit/dist/query/react/rtk-query-react.modern.es.js.map +1 -1
- package/dist/node_modules/@reduxjs/toolkit/dist/query/rtk-query.modern.cjs.js +174 -68
- package/dist/node_modules/@reduxjs/toolkit/dist/query/rtk-query.modern.cjs.js.map +1 -1
- package/dist/node_modules/@reduxjs/toolkit/dist/query/rtk-query.modern.es.js +174 -68
- package/dist/node_modules/@reduxjs/toolkit/dist/query/rtk-query.modern.es.js.map +1 -1
- package/dist/node_modules/@standard-schema/utils/dist/index.cjs.js +23 -0
- package/dist/node_modules/@standard-schema/utils/dist/index.cjs.js.map +1 -0
- package/dist/node_modules/@standard-schema/utils/dist/index.es.js +23 -0
- package/dist/node_modules/@standard-schema/utils/dist/index.es.js.map +1 -0
- package/dist/node_modules/match-sorter/dist/match-sorter.esm.cjs.js +1 -1
- package/dist/node_modules/match-sorter/dist/match-sorter.esm.es.js +1 -1
- package/dist/node_modules/parse-numeric-range/index.cjs.js +1 -1
- package/dist/node_modules/parse-numeric-range/index.es.js +1 -1
- package/dist/node_modules/rehype-prism-plus/dist/index.es.cjs.js +1 -1
- package/dist/node_modules/rehype-prism-plus/dist/index.es.es.js +1 -1
- package/dist/node_modules/remove-accents/index.cjs.js +1 -1
- package/dist/node_modules/remove-accents/index.es.js +1 -1
- package/dist/shared/src/api/generated/anatomy.cjs.js +13 -0
- package/dist/shared/src/api/generated/anatomy.cjs.js.map +1 -1
- package/dist/shared/src/api/generated/anatomy.es.js +13 -0
- package/dist/shared/src/api/generated/anatomy.es.js.map +1 -1
- package/dist/shared/src/api/queries/entities/transformDetailsPanelData.cjs.js +4 -0
- package/dist/shared/src/api/queries/entities/transformDetailsPanelData.cjs.js.map +1 -1
- package/dist/shared/src/api/queries/entities/transformDetailsPanelData.es.js +4 -0
- package/dist/shared/src/api/queries/entities/transformDetailsPanelData.es.js.map +1 -1
- package/dist/shared/src/components/EntityPath/EntityPath.cjs.js +11 -1
- package/dist/shared/src/components/EntityPath/EntityPath.cjs.js.map +1 -1
- package/dist/shared/src/components/EntityPath/EntityPath.es.js +12 -2
- package/dist/shared/src/components/EntityPath/EntityPath.es.js.map +1 -1
- package/dist/shared/src/components/EntityPath/EntityPath.styled.cjs.js +10 -1
- package/dist/shared/src/components/EntityPath/EntityPath.styled.cjs.js.map +1 -1
- package/dist/shared/src/components/EntityPath/EntityPath.styled.es.js +10 -1
- package/dist/shared/src/components/EntityPath/EntityPath.styled.es.js.map +1 -1
- package/dist/shared/src/containers/Actions/Actions.cjs.js +10 -6
- package/dist/shared/src/containers/Actions/Actions.cjs.js.map +1 -1
- package/dist/shared/src/containers/Actions/Actions.es.js +10 -6
- package/dist/shared/src/containers/Actions/Actions.es.js.map +1 -1
- package/dist/shared/src/containers/Feed/components/CommentInput/CommentInput.cjs.js +5 -3
- package/dist/shared/src/containers/Feed/components/CommentInput/CommentInput.cjs.js.map +1 -1
- package/dist/shared/src/containers/Feed/components/CommentInput/CommentInput.es.js +5 -3
- package/dist/shared/src/containers/Feed/components/CommentInput/CommentInput.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTableColumns.cjs.js +13 -1
- package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTableColumns.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTableColumns.es.js +13 -1
- package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTableColumns.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/ClipboardContext.cjs.js +0 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/ClipboardContext.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/ClipboardContext.es.js +0 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/ClipboardContext.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.cjs.js +3 -0
- package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.es.js +3 -0
- package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/BooleanWidget.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/BooleanWidget.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/CellWidget.cjs.js +24 -6
- package/dist/shared/src/containers/ProjectTreeTable/widgets/CellWidget.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/CellWidget.es.js +24 -6
- package/dist/shared/src/containers/ProjectTreeTable/widgets/CellWidget.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/DateWidget.cjs.js +7 -5
- package/dist/shared/src/containers/ProjectTreeTable/widgets/DateWidget.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/DateWidget.es.js +7 -5
- package/dist/shared/src/containers/ProjectTreeTable/widgets/DateWidget.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/EnumWidget.cjs.js +8 -0
- package/dist/shared/src/containers/ProjectTreeTable/widgets/EnumWidget.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/EnumWidget.es.js +8 -0
- package/dist/shared/src/containers/ProjectTreeTable/widgets/EnumWidget.es.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/TextWidget.cjs.js.map +1 -1
- package/dist/shared/src/containers/ProjectTreeTable/widgets/TextWidget.es.js.map +1 -1
- package/dist/shared/src/containers/RepresentationsList/RepresentationsList.cjs.js +1 -2
- package/dist/shared/src/containers/RepresentationsList/RepresentationsList.cjs.js.map +1 -1
- package/dist/shared/src/containers/RepresentationsList/RepresentationsList.es.js +1 -2
- package/dist/shared/src/containers/RepresentationsList/RepresentationsList.es.js.map +1 -1
- package/dist/shared/src/context/RemoteModulesContext.cjs.js +1 -1
- package/dist/shared/src/context/RemoteModulesContext.cjs.js.map +1 -1
- package/dist/shared/src/context/RemoteModulesContext.es.js +1 -1
- package/dist/shared/src/context/RemoteModulesContext.es.js.map +1 -1
- package/dist/shared/src/hooks/useLoadModules.cjs.js +136 -0
- package/dist/shared/src/hooks/useLoadModules.cjs.js.map +1 -0
- package/dist/shared/src/hooks/useLoadModules.es.js +136 -0
- package/dist/shared/src/hooks/useLoadModules.es.js.map +1 -0
- package/dist/shared/src/hooks/useScopedStatuses.cjs.js +4 -1
- package/dist/shared/src/hooks/useScopedStatuses.cjs.js.map +1 -1
- package/dist/shared/src/hooks/useScopedStatuses.es.js +4 -1
- package/dist/shared/src/hooks/useScopedStatuses.es.js.map +1 -1
- package/dist/types/api/generated/access.d.ts +6 -6
- package/dist/types/api/generated/actions.d.ts +6 -6
- package/dist/types/api/generated/activityFeed.d.ts +8 -8
- package/dist/types/api/generated/addons.d.ts +28 -28
- package/dist/types/api/generated/anatomy.d.ts +36 -6
- package/dist/types/api/generated/attributes.d.ts +5 -5
- package/dist/types/api/generated/authentication.d.ts +5 -5
- package/dist/types/api/generated/bundles.d.ts +7 -7
- package/dist/types/api/generated/configuration.d.ts +7 -7
- package/dist/types/api/generated/desktop.d.ts +12 -12
- package/dist/types/api/generated/entityLists.d.ts +12 -12
- package/dist/types/api/generated/events.d.ts +8 -8
- package/dist/types/api/generated/files.d.ts +7 -7
- package/dist/types/api/generated/folders.d.ts +8 -8
- package/dist/types/api/generated/graphql.d.ts +25 -25
- package/dist/types/api/generated/inbox.d.ts +1 -1
- package/dist/types/api/generated/links.d.ts +5 -5
- package/dist/types/api/generated/market.d.ts +6 -6
- package/dist/types/api/generated/onboarding.d.ts +3 -3
- package/dist/types/api/generated/operations.d.ts +3 -3
- package/dist/types/api/generated/products.d.ts +5 -5
- package/dist/types/api/generated/projectDashboard.d.ts +4 -4
- package/dist/types/api/generated/projects.d.ts +27 -27
- package/dist/types/api/generated/representations.d.ts +5 -5
- package/dist/types/api/generated/reviewables.d.ts +7 -7
- package/dist/types/api/generated/services.d.ts +6 -6
- package/dist/types/api/generated/system.d.ts +12 -12
- package/dist/types/api/generated/tasks.d.ts +8 -8
- package/dist/types/api/generated/teams.d.ts +6 -6
- package/dist/types/api/generated/thumbnails.d.ts +11 -11
- package/dist/types/api/generated/uRIs.d.ts +2 -2
- package/dist/types/api/generated/users.d.ts +26 -26
- package/dist/types/api/generated/versions.d.ts +6 -6
- package/dist/types/api/generated/workfiles.d.ts +6 -6
- package/dist/types/api/generated/ynputCloud.d.ts +5 -5
- package/dist/types/api/queries/actions/getActions.d.ts +29 -29
- package/dist/types/api/queries/activities/getActivities.d.ts +72 -71
- package/dist/types/api/queries/activities/getMentions.d.ts +18 -18
- package/dist/types/api/queries/activities/updateActivities.d.ts +62 -62
- package/dist/types/api/queries/activities/updateReaction.d.ts +10 -10
- package/dist/types/api/queries/addons/getAddons.d.ts +67 -67
- package/dist/types/api/queries/addons/updateAddons.d.ts +35 -35
- package/dist/types/api/queries/attributes/getAttributes.d.ts +23 -23
- package/dist/types/api/queries/attributes/updateAttributes.d.ts +12 -12
- package/dist/types/api/queries/authentication/getAuthentication.d.ts +15 -15
- package/dist/types/api/queries/entities/getEntity.d.ts +73 -73
- package/dist/types/api/queries/entities/getEntityPanel.d.ts +44 -44
- package/dist/types/api/queries/entities/transformDetailsPanelData.d.ts +1 -0
- package/dist/types/api/queries/entities/updateEntity.d.ts +32 -32
- package/dist/types/api/queries/folders/getFolders.d.ts +26 -26
- package/dist/types/api/queries/overview/getOverview.d.ts +72 -72
- package/dist/types/api/queries/overview/updateOverview.d.ts +5 -5
- package/dist/types/api/queries/project/getProject.d.ts +103 -103
- package/dist/types/api/queries/review/getReview.d.ts +36 -36
- package/dist/types/api/queries/review/updateReview.d.ts +13 -13
- package/dist/types/api/queries/system/getSystem.d.ts +39 -39
- package/dist/types/api/queries/userDashboard/getUserDashboard.d.ts +53 -53
- package/dist/types/api/queries/users/getUsers.d.ts +110 -110
- package/dist/types/api/queries/watchers/getWatchers.d.ts +20 -20
- package/dist/types/components/EntityPath/EntityPath.styled.d.ts +1 -0
- package/dist/types/containers/Actions/Actions.d.ts +5 -1
- package/dist/types/containers/ProjectTreeTable/widgets/BooleanWidget.d.ts +1 -2
- package/dist/types/containers/ProjectTreeTable/widgets/CellWidget.d.ts +10 -0
- package/dist/types/containers/ProjectTreeTable/widgets/DateWidget.d.ts +2 -3
- package/dist/types/containers/ProjectTreeTable/widgets/EnumWidget.d.ts +1 -1
- package/dist/types/containers/ProjectTreeTable/widgets/TextWidget.d.ts +1 -1
- package/dist/types/containers/RepresentationsList/RepresentationsList.d.ts +1 -1
- package/dist/types/hooks/index.d.ts +1 -0
- package/dist/types/hooks/useLoadModules.d.ts +28 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipboardContext.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/context/ClipboardContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useCallback, useMemo, useEffect } from 'react'\n\n// Contexts\nimport { ROW_SELECTION_COLUMN_ID, useSelectionCellsContext } from './SelectionCellsContext'\nimport { useCellEditing } from './CellEditingContext'\n\n// Utils\nimport { getCellValue, parseCellId } from '../utils/cellUtils'\n\n// Types\nimport { EntityUpdate } from '../hooks/useUpdateOverview'\n\n// Import from the new modular files\nimport {\n getEntityPath,\n parseClipboardText,\n clipboardError,\n processFieldValue,\n} from './clipboard/clipboardUtils'\nimport { validateClipboardData } from './clipboard/clipboardValidation'\nimport { ClipboardContextType, ClipboardProviderProps } from './clipboard/clipboardTypes'\nimport { useProjectTableContext } from './ProjectTableContext'\nimport { AttributeEnumItem } from '@shared/api'\n\nconst ClipboardContext = createContext<ClipboardContextType | undefined>(undefined)\n\nexport const ClipboardProvider: React.FC<ClipboardProviderProps> = ({ children, options }) => {\n const { foldersMap, tasksMap, attribFields } = useProjectTableContext()\n // Get selection information from SelectionCellsContext\n const { selectedCells, gridMap, focusedCellId } = useSelectionCellsContext()\n const { updateEntities } = useCellEditing()\n\n // convert attribs to object\n const attribByField = useMemo(() => {\n return attribFields.reduce((acc: Record<string, AttributeEnumItem[]>, attrib) => {\n if (attrib.data?.enum?.length) {\n acc[attrib.name] = attrib.data?.enum\n }\n return acc\n }, {})\n }, [attribFields])\n\n const columnEnums = useMemo(() => ({ ...options, ...attribByField }), [attribByField])\n\n const columnReadOnly = attribFields\n .filter((attrib) => attrib.readOnly)\n .map((attrib) => attrib.name)\n\n const getSelectionData = useCallback(\n async (selected: string[], config?: { headers?: boolean; fullRow?: boolean }) => {\n const { headers, fullRow } = config || {}\n try {\n // First, organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n selected.forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n // do not include row selection column\n if (colId === ROW_SELECTION_COLUMN_ID) return\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n\n cellsByRow.get(rowId)?.add(colId)\n })\n\n if (fullRow) {\n const selectedRows = selected\n .filter(\n (cellId) =>\n parseCellId(cellId)?.rowId &&\n parseCellId(cellId)?.colId === ROW_SELECTION_COLUMN_ID,\n )\n .map((cellId) => parseCellId(cellId)?.rowId) as string[]\n\n // select the whole row\n // For rows with selection cells, add all available columns\n selectedRows.forEach((rowId) => {\n // add the rowId if it doesn't exist\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n const allColumns = Array.from(gridMap.colIdToIndex.keys())\n allColumns.forEach((colId) => {\n cellsByRow.get(rowId)?.add(colId)\n })\n })\n }\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Build clipboard text\n let clipboardText = ''\n\n // Get the first row to determine columns\n const firstRowId = sortedRows[0]\n if (!firstRowId) return ''\n\n // Get all column IDs for the first row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(firstRowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Include headers if requested\n if (headers && colIds.length > 0) {\n const headerValues: string[] = []\n\n for (const colId of colIds) {\n // Use colId as the column name since we don't have direct access to column names\n const columnName = colId\n headerValues.push(`${columnName.replace(/\"/g, '\"\"')}`)\n }\n\n clipboardText += headerValues.join('\\t') + '\\n'\n }\n\n for (const rowId of sortedRows) {\n // Determine if this is a folder or task by checking which map contains the ID\n const isFolder = foldersMap.has(rowId)\n const entity = isFolder ? foldersMap.get(rowId) : tasksMap.get(rowId)\n\n if (!entity) continue\n\n // Get all column IDs for this row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(rowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Filter out the row selection column from the copied data\n const filteredColIds = colIds.filter((colId) => colId !== ROW_SELECTION_COLUMN_ID)\n\n const rowValues: string[] = []\n\n // For each column in this row\n for (const colId of filteredColIds) {\n // Determine the value based on the column ID\n let cellValue = ''\n // @ts-ignore\n const foundValue = getCellValue(entity, colId)\n cellValue = foundValue !== undefined && foundValue !== null ? String(foundValue) : ''\n\n // Special handling for name field - include full path\n if (colId === 'name') {\n cellValue = getEntityPath(rowId, isFolder, foldersMap, tasksMap)\n }\n\n if (colId === 'subType') {\n // get folderType or taskType\n // @ts-ignore\n cellValue = entity[isFolder ? 'folderType' : 'taskType'] || ''\n }\n\n // Escape double quotes in the cell value and wrap in quotes\n rowValues.push(`${cellValue.replace(/\"/g, '\"\"')}`)\n }\n\n // Add row to clipboard text\n clipboardText += rowValues.join('\\t') + '\\n'\n }\n\n return clipboardText\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, focusedCellId, gridMap, foldersMap, tasksMap],\n )\n\n const copyToClipboard: ClipboardContextType['copyToClipboard'] = useCallback(\n async (selected, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n const clipboardText = await getSelectionData(selected, { fullRow })\n if (!clipboardText) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n try {\n await navigator.clipboard.writeText(clipboardText)\n console.log('Copied to clipboard successfully', clipboardText)\n } catch (error: any) {\n clipboardError(`Failed to copy to clipboard: ${error.message}`)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap],\n )\n\n const exportCSV: ClipboardContextType['exportCSV'] = useCallback(\n async (selected, projectName, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n\n try {\n // Get clipboard text with headers included for CSV export\n const clipboardText = await getSelectionData(selected, { headers: true, fullRow })\n if (!clipboardText) return\n\n // create a csv file and download it\n const blob = new Blob([clipboardText], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n const selectedCount = selected.length\n a.download = `${projectName}-export-${selectedCount}_cells-${new Date()\n .toISOString()\n .slice(0, 10)}.csv`\n a.click()\n URL.revokeObjectURL(url)\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap, getSelectionData],\n )\n\n const pasteFromClipboard: ClipboardContextType['pasteFromClipboard'] = useCallback(\n async (selected) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n let clipboardText: string\n try {\n clipboardText = await navigator.clipboard.readText()\n } catch (error: any) {\n clipboardError(`Failed to read from clipboard: ${error.message}`)\n return\n }\n if (!clipboardText.trim()) return\n\n // Parse the clipboard text\n const parsedData = parseClipboardText(clipboardText)\n if (!parsedData.length) return\n\n // Determine if we have a single value in the clipboard (one row, one column)\n const isSingleCellValue = parsedData.length === 1 && parsedData[0].values.length === 1\n\n // Organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n Array.from(selected).forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n cellsByRow.get(rowId)?.add(colId)\n })\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // For each row, get the sorted column IDs\n const firstRow = sortedRows[0]\n const selectedColIds = Array.from(cellsByRow.get(firstRow) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // First pass: validate all values for status and subType\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n\n // Get the appropriate value from the clipboard data\n // If it's a single cell value, use it for all cells\n // Otherwise use the modulo approach to repeat values\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n // Validate clipboard data for this cell\n const isValid = validateClipboardData({\n colId,\n isFolder,\n pasteValue,\n parsedData,\n columnEnums,\n columnReadOnly,\n rowIndex,\n colIndex,\n isSingleCellValue,\n })\n\n if (!isValid) return\n }\n }\n\n // Create a map to consolidate updates for the same entity\n const entitiesMap = new Map<\n string,\n {\n id: string\n type: string\n fields: Record<string, any>\n attrib: Record<string, any>\n }\n >()\n\n // For each column, prepare updates\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n // Skip special handling for 'name' which we don't want to paste\n if (colId === 'name') continue\n\n // Check if this is an attribute field by examining the first entity\n let isAttrib = false\n // Check if the field potentially contains array values\n let fieldValueType: 'string' | 'number' | 'boolean' | 'array' = 'string'\n\n if (sortedRows.length > 0) {\n const firstRowId = sortedRows[0]\n const isFolder = foldersMap.has(firstRowId)\n const entity = isFolder ? foldersMap.get(firstRowId) : tasksMap.get(firstRowId)\n\n if (entity) {\n isAttrib = colId.startsWith('attrib_')\n\n // Determine if field is an array and its value type\n // @ts-ignore - Check entity property or attribute\n const fieldValue = getCellValue(entity, colId)\n if (Array.isArray(fieldValue)) {\n fieldValueType = 'array'\n } else if (typeof fieldValue === 'number') {\n fieldValueType = 'number'\n } else if (typeof fieldValue === 'boolean') {\n fieldValueType = 'boolean'\n }\n\n // Special case for subType\n if (colId === 'subType') {\n isAttrib = false\n }\n }\n }\n\n // Process each row individually\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n const entityType = isFolder ? 'folder' : 'task'\n\n // Get the appropriate value from the clipboard data\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n let fieldToUpdate = colId.split('_').pop() || colId\n\n // Special handling for subType (convert to folderType or taskType)\n if (colId === 'subType') {\n fieldToUpdate = isFolder ? 'folderType' : 'taskType'\n isAttrib = false\n\n // Skip empty values for enum fields\n if (!pasteValue) continue\n }\n\n // Process the value based on its type\n const processedValue = processFieldValue(pasteValue, fieldValueType)\n\n // Get or create entity entry in the map\n const entityKey = `${rowId}-${entityType}`\n if (!entitiesMap.has(entityKey)) {\n entitiesMap.set(entityKey, {\n id: rowId,\n type: entityType,\n fields: {},\n attrib: {},\n })\n }\n\n const entityData = entitiesMap.get(entityKey)!\n\n // Add the field to the appropriate place\n if (isAttrib) {\n entityData.attrib[fieldToUpdate] = processedValue\n } else {\n entityData.fields[fieldToUpdate] = processedValue\n }\n }\n }\n\n // Convert the consolidated map to EntityUpdate array\n const allEntityUpdates: EntityUpdate[] = []\n\n entitiesMap.forEach((entity) => {\n // For regular fields, create one update per field\n Object.entries(entity.fields).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n })\n })\n\n // For attributes, create one update per attribute\n Object.entries(entity.attrib).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n isAttrib: true,\n })\n })\n })\n\n // Make a single call to update all entities\n if (allEntityUpdates.length > 0) {\n try {\n await updateEntities(allEntityUpdates)\n } catch (error) {\n console.error('Error updating entities:', error)\n clipboardError(\n `Failed to update: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n }\n },\n [selectedCells, gridMap, foldersMap, tasksMap, updateEntities, columnEnums],\n )\n\n // Set up keyboard event listeners\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Copy functionality (Ctrl+C or Command+C)\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n copyToClipboard()\n }\n\n // Paste functionality (Ctrl+V or Command+V)\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n pasteFromClipboard()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [copyToClipboard, pasteFromClipboard])\n\n const value = useMemo(\n () => ({\n copyToClipboard,\n pasteFromClipboard,\n exportCSV,\n }),\n [copyToClipboard, pasteFromClipboard],\n )\n\n return <ClipboardContext.Provider value={value}>{children}</ClipboardContext.Provider>\n}\n\nexport const useClipboard = (): ClipboardContextType => {\n const context = useContext(ClipboardContext)\n if (context === undefined) {\n throw new Error('useClipboard must be used within a ClipboardProvider')\n }\n return context\n}\n"],"names":["createContext","useProjectTableContext","useSelectionCellsContext","useCellEditing","useMemo","useCallback","parseCellId","ROW_SELECTION_COLUMN_ID","colIds","getCellValue","getEntityPath","clipboardError","parseClipboardText","validateClipboardData","processFieldValue","value","useEffect","jsx","useContext"],"mappings":";;;;;;;;;;AAwBA,MAAM,mBAAmBA,oBAAgD,MAAS;AAE3E,MAAM,oBAAsD,CAAC,EAAE,UAAU,cAAc;AAC5F,QAAM,EAAE,YAAY,UAAU,aAAA,IAAiBC,oBAAAA,uBAAuB;AAEtE,QAAM,EAAE,eAAe,SAAS,cAAA,IAAkBC,sBAAAA,yBAAyB;AACrE,QAAA,EAAE,eAAe,IAAIC,kCAAe;AAGpC,QAAA,gBAAgBC,MAAAA,QAAQ,MAAM;AAClC,WAAO,aAAa,OAAO,CAAC,KAA0C,WAAW;;AAC3E,WAAA,kBAAO,SAAP,mBAAa,SAAb,mBAAmB,QAAQ;AAC7B,YAAI,OAAO,IAAI,KAAI,YAAO,SAAP,mBAAa;AAAA,MAAA;AAE3B,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,EAAA,GACJ,CAAC,YAAY,CAAC;AAEX,QAAA,cAAcA,cAAQ,OAAO,EAAE,GAAG,SAAS,GAAG,cAAc,IAAI,CAAC,aAAa,CAAC;AAErF,QAAM,iBAAiB,aACpB,OAAO,CAAC,WAAW,OAAO,QAAQ,EAClC,IAAI,CAAC,WAAW,OAAO,IAAI;AAE9B,QAAM,mBAAmBC,MAAA;AAAA,IACvB,OAAO,UAAoB,WAAsD;AAC/E,YAAM,EAAE,SAAS,QAAQ,IAAI,UAAU,CAAC;AACpC,UAAA;AAEI,cAAA,iCAAiB,IAAyB;AAGvC,iBAAA,QAAQ,CAAC,WAAW;;AACrB,gBAAA,WAAWC,sBAAY,MAAM;AACnC,cAAI,CAAC,SAAU;AAET,gBAAA,EAAE,OAAO,MAAA,IAAU;AAGzB,cAAI,UAAUC,sBAAAA,wBAAyB;AAEvC,cAAI,CAAC,WAAW,IAAI,KAAK,GAAG;AAC1B,uBAAW,IAAI,OAAW,oBAAA,IAAA,CAAK;AAAA,UAAA;AAGjC,2BAAW,IAAI,KAAK,MAApB,mBAAuB,IAAI;AAAA,QAAK,CACjC;AAED,YAAI,SAAS;AACX,gBAAM,eAAe,SAClB;AAAA,YACC,CAAC;;AACCD,sCAAY,YAAA,MAAM,MAAlBA,mBAAqB,YACrBA,2BAAY,MAAM,MAAlBA,mBAAqB,WAAUC,sBAAAA;AAAAA;AAAAA,UAAA,EAElC,IAAI,CAAC;;AAAWD,mCAAY,YAAA,MAAM,MAAlBA,mBAAqB;AAAA,WAAK;AAIhC,uBAAA,QAAQ,CAAC,UAAU;AAE9B,gBAAI,CAAC,WAAW,IAAI,KAAK,GAAG;AAC1B,yBAAW,IAAI,OAAW,oBAAA,IAAA,CAAK;AAAA,YAAA;AAEjC,kBAAM,aAAa,MAAM,KAAK,QAAQ,aAAa,MAAM;AAC9C,uBAAA,QAAQ,CAAC,UAAU;;AAC5B,+BAAW,IAAI,KAAK,MAApB,mBAAuB,IAAI;AAAA,YAAK,CACjC;AAAA,UAAA,CACF;AAAA,QAAA;AAIG,cAAA,aAAa,MAAM,KAAK,WAAW,KAAM,CAAA,EAAE,KAAK,CAAC,GAAG,MAAM;AAC9D,gBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,gBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,iBAAO,SAAS;AAAA,QAAA,CACjB;AAGD,YAAI,gBAAgB;AAGd,cAAA,aAAa,WAAW,CAAC;AAC3B,YAAA,CAAC,WAAmB,QAAA;AAGxB,cAAM,SAAS,MAAM,KAAK,WAAW,IAAI,UAAU,KAAK,CAAE,CAAA,EAAE,KAAK,CAAC,GAAG,MAAM;AACzE,gBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,gBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,iBAAO,SAAS;AAAA,QAAA,CACjB;AAGG,YAAA,WAAW,OAAO,SAAS,GAAG;AAChC,gBAAM,eAAyB,CAAC;AAEhC,qBAAW,SAAS,QAAQ;AAE1B,kBAAM,aAAa;AACnB,yBAAa,KAAK,GAAG,WAAW,QAAQ,MAAM,IAAI,CAAC,EAAE;AAAA,UAAA;AAGtC,2BAAA,aAAa,KAAK,GAAI,IAAI;AAAA,QAAA;AAG7C,mBAAW,SAAS,YAAY;AAExB,gBAAA,WAAW,WAAW,IAAI,KAAK;AAC/B,gBAAA,SAAS,WAAW,WAAW,IAAI,KAAK,IAAI,SAAS,IAAI,KAAK;AAEpE,cAAI,CAAC,OAAQ;AAGb,gBAAME,UAAS,MAAM,KAAK,WAAW,IAAI,KAAK,KAAK,CAAE,CAAA,EAAE,KAAK,CAAC,GAAG,MAAM;AACpE,kBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,kBAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,mBAAO,SAAS;AAAA,UAAA,CACjB;AAGD,gBAAM,iBAAiBA,QAAO,OAAO,CAAC,UAAU,UAAUD,6CAAuB;AAEjF,gBAAM,YAAsB,CAAC;AAG7B,qBAAW,SAAS,gBAAgB;AAElC,gBAAI,YAAY;AAEV,kBAAA,aAAaE,UAAAA,aAAa,QAAQ,KAAK;AAC7C,wBAAY,eAAe,UAAa,eAAe,OAAO,OAAO,UAAU,IAAI;AAGnF,gBAAI,UAAU,QAAQ;AACpB,0BAAYC,eAAAA,cAAc,OAAO,UAAU,YAAY,QAAQ;AAAA,YAAA;AAGjE,gBAAI,UAAU,WAAW;AAGvB,0BAAY,OAAO,WAAW,eAAe,UAAU,KAAK;AAAA,YAAA;AAI9D,sBAAU,KAAK,GAAG,UAAU,QAAQ,MAAM,IAAI,CAAC,EAAE;AAAA,UAAA;AAIlC,2BAAA,UAAU,KAAK,GAAI,IAAI;AAAA,QAAA;AAGnC,eAAA;AAAA,eACA,OAAO;AACN,gBAAA,MAAM,gCAAgC,KAAK;AAAA,MAAA;AAAA,IAEvD;AAAA,IACA,CAAC,eAAe,eAAe,SAAS,YAAY,QAAQ;AAAA,EAC9D;AAEA,QAAM,kBAA2DL,MAAA;AAAA,IAC/D,OAAO,UAAU,YAAY;AAChB,iBAAA,YAAY,MAAM,KAAK,aAAa;AAC3C,UAAA,CAAC,SAAS,OAAQ;AACtB,YAAM,gBAAgB,MAAM,iBAAiB,UAAU,EAAE,SAAS;AAClE,UAAI,CAAC,cAAe;AAChB,UAAA,CAAC,UAAU,WAAW;AACxBM,uBAAAA,eAAe,8CAA8C;AAC7D;AAAA,MAAA;AAEE,UAAA,CAAC,OAAO,iBAAiB;AAC3BA,uBAAAA,eAAe,sDAAsD;AACrE;AAAA,MAAA;AAEE,UAAA;AACI,cAAA,UAAU,UAAU,UAAU,aAAa;AACzC,gBAAA,IAAI,oCAAoC,aAAa;AAAA,eACtD,OAAY;AACJA,uBAAAA,eAAA,gCAAgC,MAAM,OAAO,EAAE;AAAA,MAAA;AAAA,IAElE;AAAA,IACA,CAAC,eAAe,YAAY,UAAU,OAAO;AAAA,EAC/C;AAEA,QAAM,YAA+CN,MAAA;AAAA,IACnD,OAAO,UAAU,aAAa,YAAY;AAC7B,iBAAA,YAAY,MAAM,KAAK,aAAa;AAC3C,UAAA,CAAC,SAAS,OAAQ;AAElB,UAAA;AAEI,cAAA,gBAAgB,MAAM,iBAAiB,UAAU,EAAE,SAAS,MAAM,SAAS;AACjF,YAAI,CAAC,cAAe;AAGd,cAAA,OAAO,IAAI,KAAK,CAAC,aAAa,GAAG,EAAE,MAAM,YAAY;AACrD,cAAA,MAAM,IAAI,gBAAgB,IAAI;AAC9B,cAAA,IAAI,SAAS,cAAc,GAAG;AACpC,UAAE,OAAO;AACT,cAAM,gBAAgB,SAAS;AAC/B,UAAE,WAAW,GAAG,WAAW,WAAW,aAAa,WAAU,oBAAI,KAAK,GACnE,YAAY,EACZ,MAAM,GAAG,EAAE,CAAC;AACf,UAAE,MAAM;AACR,YAAI,gBAAgB,GAAG;AAAA,eAChB,OAAO;AACN,gBAAA,MAAM,gCAAgC,KAAK;AAAA,MAAA;AAAA,IAEvD;AAAA,IACA,CAAC,eAAe,YAAY,UAAU,SAAS,gBAAgB;AAAA,EACjE;AAEA,QAAM,qBAAiEA,MAAA;AAAA,IACrE,OAAO,aAAa;AACP,iBAAA,YAAY,MAAM,KAAK,aAAa;AAC3C,UAAA,CAAC,SAAS,OAAQ;AAClB,UAAA,CAAC,UAAU,WAAW;AACxBM,uBAAAA,eAAe,8CAA8C;AAC7D;AAAA,MAAA;AAEE,UAAA,CAAC,OAAO,iBAAiB;AAC3BA,uBAAAA,eAAe,sDAAsD;AACrE;AAAA,MAAA;AAEE,UAAA;AACA,UAAA;AACc,wBAAA,MAAM,UAAU,UAAU,SAAS;AAAA,eAC5C,OAAY;AACJA,uBAAAA,eAAA,kCAAkC,MAAM,OAAO,EAAE;AAChE;AAAA,MAAA;AAEE,UAAA,CAAC,cAAc,OAAQ;AAGrB,YAAA,aAAaC,kCAAmB,aAAa;AAC/C,UAAA,CAAC,WAAW,OAAQ;AAGlB,YAAA,oBAAoB,WAAW,WAAW,KAAK,WAAW,CAAC,EAAE,OAAO,WAAW;AAG/E,YAAA,iCAAiB,IAAyB;AAGhD,YAAM,KAAK,QAAQ,EAAE,QAAQ,CAAC,WAAW;;AACjC,cAAA,WAAWN,sBAAY,MAAM;AACnC,YAAI,CAAC,SAAU;AAET,cAAA,EAAE,OAAO,MAAA,IAAU;AAEzB,YAAI,CAAC,WAAW,IAAI,KAAK,GAAG;AAC1B,qBAAW,IAAI,OAAW,oBAAA,IAAA,CAAK;AAAA,QAAA;AAEjC,yBAAW,IAAI,KAAK,MAApB,mBAAuB,IAAI;AAAA,MAAK,CACjC;AAGK,YAAA,aAAa,MAAM,KAAK,WAAW,KAAM,CAAA,EAAE,KAAK,CAAC,GAAG,MAAM;AAC9D,cAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,cAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,eAAO,SAAS;AAAA,MAAA,CACjB;AAGK,YAAA,WAAW,WAAW,CAAC;AAC7B,YAAM,iBAAiB,MAAM,KAAK,WAAW,IAAI,QAAQ,KAAK,CAAE,CAAA,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/E,cAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,cAAM,SAAS,QAAQ,aAAa,IAAI,CAAC,KAAK;AAC9C,eAAO,SAAS;AAAA,MAAA,CACjB;AAGD,eAAS,WAAW,GAAG,WAAW,eAAe,QAAQ,YAAY;AAC7D,cAAA,QAAQ,eAAe,QAAQ;AAErC,iBAAS,WAAW,GAAG,WAAW,WAAW,QAAQ,YAAY;AACzD,gBAAA,QAAQ,WAAW,QAAQ;AAC3B,gBAAA,WAAW,WAAW,IAAI,KAAK;AAKjC,cAAA;AACJ,cAAI,mBAAmB;AACrB,yBAAa,WAAW,CAAC,EAAE,OAAO,CAAC;AAAA,UAAA,OAC9B;AACC,kBAAA,gBAAgB,WAAW,WAAW;AAC5C,kBAAM,gBAAgB,WAAW,WAAW,aAAa,EAAE,OAAO;AAClE,yBAAa,WAAW,aAAa,EAAE,OAAO,aAAa;AAAA,UAAA;AAI7D,gBAAM,UAAUO,oBAAAA,sBAAsB;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA,CACD;AAED,cAAI,CAAC,QAAS;AAAA,QAAA;AAAA,MAChB;AAII,YAAA,kCAAkB,IAQtB;AAGF,eAAS,WAAW,GAAG,WAAW,eAAe,QAAQ,YAAY;AAC7D,cAAA,QAAQ,eAAe,QAAQ;AAGrC,YAAI,UAAU,OAAQ;AAGtB,YAAI,WAAW;AAEf,YAAI,iBAA4D;AAE5D,YAAA,WAAW,SAAS,GAAG;AACnB,gBAAA,aAAa,WAAW,CAAC;AACzB,gBAAA,WAAW,WAAW,IAAI,UAAU;AACpC,gBAAA,SAAS,WAAW,WAAW,IAAI,UAAU,IAAI,SAAS,IAAI,UAAU;AAE9E,cAAI,QAAQ;AACC,uBAAA,MAAM,WAAW,SAAS;AAI/B,kBAAA,aAAaJ,UAAAA,aAAa,QAAQ,KAAK;AACzC,gBAAA,MAAM,QAAQ,UAAU,GAAG;AACZ,+BAAA;AAAA,YAAA,WACR,OAAO,eAAe,UAAU;AACxB,+BAAA;AAAA,YAAA,WACR,OAAO,eAAe,WAAW;AACzB,+BAAA;AAAA,YAAA;AAInB,gBAAI,UAAU,WAAW;AACZ,yBAAA;AAAA,YAAA;AAAA,UACb;AAAA,QACF;AAIF,iBAAS,WAAW,GAAG,WAAW,WAAW,QAAQ,YAAY;AACzD,gBAAA,QAAQ,WAAW,QAAQ;AAC3B,gBAAA,WAAW,WAAW,IAAI,KAAK;AAC/B,gBAAA,aAAa,WAAW,WAAW;AAGrC,cAAA;AACJ,cAAI,mBAAmB;AACrB,yBAAa,WAAW,CAAC,EAAE,OAAO,CAAC;AAAA,UAAA,OAC9B;AACC,kBAAA,gBAAgB,WAAW,WAAW;AAC5C,kBAAM,gBAAgB,WAAW,WAAW,aAAa,EAAE,OAAO;AAClE,yBAAa,WAAW,aAAa,EAAE,OAAO,aAAa;AAAA,UAAA;AAG7D,cAAI,gBAAgB,MAAM,MAAM,GAAG,EAAE,SAAS;AAG9C,cAAI,UAAU,WAAW;AACvB,4BAAgB,WAAW,eAAe;AAC/B,uBAAA;AAGX,gBAAI,CAAC,WAAY;AAAA,UAAA;AAIb,gBAAA,iBAAiBK,eAAAA,kBAAkB,YAAY,cAAc;AAGnE,gBAAM,YAAY,GAAG,KAAK,IAAI,UAAU;AACxC,cAAI,CAAC,YAAY,IAAI,SAAS,GAAG;AAC/B,wBAAY,IAAI,WAAW;AAAA,cACzB,IAAI;AAAA,cACJ,MAAM;AAAA,cACN,QAAQ,CAAC;AAAA,cACT,QAAQ,CAAA;AAAA,YAAC,CACV;AAAA,UAAA;AAGG,gBAAA,aAAa,YAAY,IAAI,SAAS;AAG5C,cAAI,UAAU;AACD,uBAAA,OAAO,aAAa,IAAI;AAAA,UAAA,OAC9B;AACM,uBAAA,OAAO,aAAa,IAAI;AAAA,UAAA;AAAA,QACrC;AAAA,MACF;AAIF,YAAM,mBAAmC,CAAC;AAE9B,kBAAA,QAAQ,CAAC,WAAW;AAEvB,eAAA,QAAQ,OAAO,MAAM,EAAE,QAAQ,CAAC,CAAC,OAAOC,MAAK,MAAM;AACxD,2BAAiB,KAAK;AAAA,YACpB,IAAI,OAAO;AAAA,YACX,MAAM,OAAO;AAAA,YACb;AAAA,YACA,OAAAA;AAAAA,UAAA,CACD;AAAA,QAAA,CACF;AAGM,eAAA,QAAQ,OAAO,MAAM,EAAE,QAAQ,CAAC,CAAC,OAAOA,MAAK,MAAM;AACxD,2BAAiB,KAAK;AAAA,YACpB,IAAI,OAAO;AAAA,YACX,MAAM,OAAO;AAAA,YACb;AAAA,YACA,OAAAA;AAAAA,YACA,UAAU;AAAA,UAAA,CACX;AAAA,QAAA,CACF;AAAA,MAAA,CACF;AAGG,UAAA,iBAAiB,SAAS,GAAG;AAC3B,YAAA;AACF,gBAAM,eAAe,gBAAgB;AAAA,iBAC9B,OAAO;AACN,kBAAA,MAAM,4BAA4B,KAAK;AAC/CJ,yBAAA;AAAA,YACE,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAC/E;AAAA,QAAA;AAAA,MACF;AAAA,IAEJ;AAAA,IACA,CAAC,eAAe,SAAS,YAAY,UAAU,gBAAgB,WAAW;AAAA,EAC5E;AAGAK,QAAAA,UAAU,MAAM;AACR,UAAA,gBAAgB,CAAC,MAAqB;AAE1C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK;AAC7B,wBAAA;AAAA,MAAA;AAIlB,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK;AAC1B,2BAAA;AAAA,MAAA;AAAA,IAEvB;AAEO,WAAA,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM;AACJ,aAAA,oBAAoB,WAAW,aAAa;AAAA,IACrD;AAAA,EAAA,GACC,CAAC,iBAAiB,kBAAkB,CAAC;AAExC,QAAM,QAAQZ,MAAA;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,CAAC,iBAAiB,kBAAkB;AAAA,EACtC;AAEA,SAAQa,2BAAAA,kBAAAA,IAAA,iBAAiB,UAAjB,EAA0B,OAAe,SAAS,CAAA;AAC5D;AAEO,MAAM,eAAe,MAA4B;AAChD,QAAA,UAAUC,iBAAW,gBAAgB;AAC3C,MAAI,YAAY,QAAW;AACnB,UAAA,IAAI,MAAM,sDAAsD;AAAA,EAAA;AAEjE,SAAA;AACT;;;"}
|
|
1
|
+
{"version":3,"file":"ClipboardContext.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/context/ClipboardContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useCallback, useMemo, useEffect } from 'react'\n\n// Contexts\nimport { ROW_SELECTION_COLUMN_ID, useSelectionCellsContext } from './SelectionCellsContext'\nimport { useCellEditing } from './CellEditingContext'\n\n// Utils\nimport { getCellValue, parseCellId } from '../utils/cellUtils'\n\n// Types\nimport { EntityUpdate } from '../hooks/useUpdateOverview'\n\n// Import from the new modular files\nimport {\n getEntityPath,\n parseClipboardText,\n clipboardError,\n processFieldValue,\n} from './clipboard/clipboardUtils'\nimport { validateClipboardData } from './clipboard/clipboardValidation'\nimport { ClipboardContextType, ClipboardProviderProps } from './clipboard/clipboardTypes'\nimport { useProjectTableContext } from './ProjectTableContext'\nimport { AttributeEnumItem } from '@shared/api'\n\nconst ClipboardContext = createContext<ClipboardContextType | undefined>(undefined)\n\nexport const ClipboardProvider: React.FC<ClipboardProviderProps> = ({ children, options }) => {\n const { foldersMap, tasksMap, attribFields } = useProjectTableContext()\n // Get selection information from SelectionCellsContext\n const { selectedCells, gridMap, focusedCellId } = useSelectionCellsContext()\n const { updateEntities } = useCellEditing()\n\n // convert attribs to object\n const attribByField = useMemo(() => {\n return attribFields.reduce((acc: Record<string, AttributeEnumItem[]>, attrib) => {\n if (attrib.data?.enum?.length) {\n acc[attrib.name] = attrib.data?.enum\n }\n return acc\n }, {})\n }, [attribFields])\n\n const columnEnums = useMemo(() => ({ ...options, ...attribByField }), [attribByField])\n\n const columnReadOnly = attribFields\n .filter((attrib) => attrib.readOnly)\n .map((attrib) => attrib.name)\n\n const getSelectionData = useCallback(\n async (selected: string[], config?: { headers?: boolean; fullRow?: boolean }) => {\n const { headers, fullRow } = config || {}\n try {\n // First, organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n selected.forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n // do not include row selection column\n if (colId === ROW_SELECTION_COLUMN_ID) return\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n\n cellsByRow.get(rowId)?.add(colId)\n })\n\n if (fullRow) {\n const selectedRows = selected\n .filter(\n (cellId) =>\n parseCellId(cellId)?.rowId &&\n parseCellId(cellId)?.colId === ROW_SELECTION_COLUMN_ID,\n )\n .map((cellId) => parseCellId(cellId)?.rowId) as string[]\n\n // select the whole row\n // For rows with selection cells, add all available columns\n selectedRows.forEach((rowId) => {\n // add the rowId if it doesn't exist\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n const allColumns = Array.from(gridMap.colIdToIndex.keys())\n allColumns.forEach((colId) => {\n cellsByRow.get(rowId)?.add(colId)\n })\n })\n }\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Build clipboard text\n let clipboardText = ''\n\n // Get the first row to determine columns\n const firstRowId = sortedRows[0]\n if (!firstRowId) return ''\n\n // Get all column IDs for the first row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(firstRowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Include headers if requested\n if (headers && colIds.length > 0) {\n const headerValues: string[] = []\n\n for (const colId of colIds) {\n // Use colId as the column name since we don't have direct access to column names\n const columnName = colId\n headerValues.push(`${columnName.replace(/\"/g, '\"\"')}`)\n }\n\n clipboardText += headerValues.join('\\t') + '\\n'\n }\n\n for (const rowId of sortedRows) {\n // Determine if this is a folder or task by checking which map contains the ID\n const isFolder = foldersMap.has(rowId)\n const entity = isFolder ? foldersMap.get(rowId) : tasksMap.get(rowId)\n\n if (!entity) continue\n\n // Get all column IDs for this row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(rowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Filter out the row selection column from the copied data\n const filteredColIds = colIds.filter((colId) => colId !== ROW_SELECTION_COLUMN_ID)\n\n const rowValues: string[] = []\n\n // For each column in this row\n for (const colId of filteredColIds) {\n // Determine the value based on the column ID\n let cellValue = ''\n // @ts-ignore\n const foundValue = getCellValue(entity, colId)\n cellValue = foundValue !== undefined && foundValue !== null ? String(foundValue) : ''\n\n // Special handling for name field - include full path\n if (colId === 'name') {\n cellValue = getEntityPath(rowId, isFolder, foldersMap, tasksMap)\n }\n\n if (colId === 'subType') {\n // get folderType or taskType\n // @ts-ignore\n cellValue = entity[isFolder ? 'folderType' : 'taskType'] || ''\n }\n\n // Escape double quotes in the cell value and wrap in quotes\n rowValues.push(`${cellValue.replace(/\"/g, '\"\"')}`)\n }\n\n // Add row to clipboard text\n clipboardText += rowValues.join('\\t') + '\\n'\n }\n\n return clipboardText\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, focusedCellId, gridMap, foldersMap, tasksMap],\n )\n\n const copyToClipboard: ClipboardContextType['copyToClipboard'] = useCallback(\n async (selected, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n const clipboardText = await getSelectionData(selected, { fullRow })\n if (!clipboardText) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n try {\n await navigator.clipboard.writeText(clipboardText)\n console.log('Copied to clipboard successfully', clipboardText)\n } catch (error: any) {\n clipboardError(`Failed to copy to clipboard: ${error.message}`)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap],\n )\n\n const exportCSV: ClipboardContextType['exportCSV'] = useCallback(\n async (selected, projectName, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n\n try {\n // Get clipboard text with headers included for CSV export\n const clipboardText = await getSelectionData(selected, { headers: true, fullRow })\n if (!clipboardText) return\n\n // create a csv file and download it\n const blob = new Blob([clipboardText], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n const selectedCount = selected.length\n a.download = `${projectName}-export-${selectedCount}_cells-${new Date()\n .toISOString()\n .slice(0, 10)}.csv`\n a.click()\n URL.revokeObjectURL(url)\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap, getSelectionData],\n )\n\n const pasteFromClipboard: ClipboardContextType['pasteFromClipboard'] = useCallback(\n async (selected) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n let clipboardText: string\n try {\n clipboardText = await navigator.clipboard.readText()\n } catch (error: any) {\n clipboardError(`Failed to read from clipboard: ${error.message}`)\n return\n }\n\n // we can have empty text in the clipboard\n //if (!clipboardText.trim()) return\n\n // Parse the clipboard text\n const parsedData = parseClipboardText(clipboardText)\n if (!parsedData.length) return\n\n // Determine if we have a single value in the clipboard (one row, one column)\n const isSingleCellValue = parsedData.length === 1 && parsedData[0].values.length === 1\n\n // Organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n Array.from(selected).forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n cellsByRow.get(rowId)?.add(colId)\n })\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // For each row, get the sorted column IDs\n const firstRow = sortedRows[0]\n const selectedColIds = Array.from(cellsByRow.get(firstRow) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // First pass: validate all values for status and subType\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n\n // Get the appropriate value from the clipboard data\n // If it's a single cell value, use it for all cells\n // Otherwise use the modulo approach to repeat values\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n // Validate clipboard data for this cell\n const isValid = validateClipboardData({\n colId,\n isFolder,\n pasteValue,\n parsedData,\n columnEnums,\n columnReadOnly,\n rowIndex,\n colIndex,\n isSingleCellValue,\n })\n\n if (!isValid) return\n }\n }\n\n // Create a map to consolidate updates for the same entity\n const entitiesMap = new Map<\n string,\n {\n id: string\n type: string\n fields: Record<string, any>\n attrib: Record<string, any>\n }\n >()\n\n // For each column, prepare updates\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n // Skip special handling for 'name' which we don't want to paste\n if (colId === 'name') continue\n\n // Check if this is an attribute field by examining the first entity\n let isAttrib = false\n // Check if the field potentially contains array values\n let fieldValueType: 'string' | 'number' | 'boolean' | 'array' = 'string'\n\n if (sortedRows.length > 0) {\n const firstRowId = sortedRows[0]\n const isFolder = foldersMap.has(firstRowId)\n const entity = isFolder ? foldersMap.get(firstRowId) : tasksMap.get(firstRowId)\n\n if (entity) {\n isAttrib = colId.startsWith('attrib_')\n\n // Determine if field is an array and its value type\n // @ts-ignore - Check entity property or attribute\n const fieldValue = getCellValue(entity, colId)\n if (Array.isArray(fieldValue)) {\n fieldValueType = 'array'\n } else if (typeof fieldValue === 'number') {\n fieldValueType = 'number'\n } else if (typeof fieldValue === 'boolean') {\n fieldValueType = 'boolean'\n }\n\n // Special case for subType\n if (colId === 'subType') {\n isAttrib = false\n }\n }\n }\n\n // Process each row individually\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n const entityType = isFolder ? 'folder' : 'task'\n\n // Get the appropriate value from the clipboard data\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n let fieldToUpdate = colId.split('_').pop() || colId\n\n // Special handling for subType (convert to folderType or taskType)\n if (colId === 'subType') {\n fieldToUpdate = isFolder ? 'folderType' : 'taskType'\n isAttrib = false\n\n // Skip empty values for enum fields\n if (!pasteValue) continue\n }\n\n // Process the value based on its type\n const processedValue = processFieldValue(pasteValue, fieldValueType)\n\n // Get or create entity entry in the map\n const entityKey = `${rowId}-${entityType}`\n if (!entitiesMap.has(entityKey)) {\n entitiesMap.set(entityKey, {\n id: rowId,\n type: entityType,\n fields: {},\n attrib: {},\n })\n }\n\n const entityData = entitiesMap.get(entityKey)!\n\n // Add the field to the appropriate place\n if (isAttrib) {\n entityData.attrib[fieldToUpdate] = processedValue\n } else {\n entityData.fields[fieldToUpdate] = processedValue\n }\n }\n }\n\n // Convert the consolidated map to EntityUpdate array\n const allEntityUpdates: EntityUpdate[] = []\n\n entitiesMap.forEach((entity) => {\n // For regular fields, create one update per field\n Object.entries(entity.fields).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n })\n })\n\n // For attributes, create one update per attribute\n Object.entries(entity.attrib).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n isAttrib: true,\n })\n })\n })\n\n // Make a single call to update all entities\n if (allEntityUpdates.length > 0) {\n try {\n await updateEntities(allEntityUpdates)\n } catch (error) {\n console.error('Error updating entities:', error)\n clipboardError(\n `Failed to update: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n }\n },\n [selectedCells, gridMap, foldersMap, tasksMap, updateEntities, columnEnums],\n )\n\n // Set up keyboard event listeners\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Copy functionality (Ctrl+C or Command+C)\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n copyToClipboard()\n }\n\n // Paste functionality (Ctrl+V or Command+V)\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n pasteFromClipboard()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [copyToClipboard, pasteFromClipboard])\n\n const value = useMemo(\n () => ({\n copyToClipboard,\n pasteFromClipboard,\n exportCSV,\n }),\n [copyToClipboard, pasteFromClipboard],\n )\n\n return <ClipboardContext.Provider value={value}>{children}</ClipboardContext.Provider>\n}\n\nexport const useClipboard = (): ClipboardContextType => {\n const context = useContext(ClipboardContext)\n if (context === undefined) {\n throw new Error('useClipboard must be used within a ClipboardProvider')\n }\n return context\n}\n"],"names":["createContext","useProjectTableContext","useSelectionCellsContext","useCellEditing","useMemo","useCallback","parseCellId","ROW_SELECTION_COLUMN_ID","colIds","getCellValue","getEntityPath","clipboardError","parseClipboardText","validateClipboardData","processFieldValue","value","useEffect","jsx","useContext"],"mappings":"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"}
|
|
@@ -175,7 +175,6 @@ const ClipboardProvider = ({ children, options }) => {
|
|
|
175
175
|
clipboardError(`Failed to read from clipboard: ${error.message}`);
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
if (!clipboardText.trim()) return;
|
|
179
178
|
const parsedData = parseClipboardText(clipboardText);
|
|
180
179
|
if (!parsedData.length) return;
|
|
181
180
|
const isSingleCellValue = parsedData.length === 1 && parsedData[0].values.length === 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipboardContext.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/context/ClipboardContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useCallback, useMemo, useEffect } from 'react'\n\n// Contexts\nimport { ROW_SELECTION_COLUMN_ID, useSelectionCellsContext } from './SelectionCellsContext'\nimport { useCellEditing } from './CellEditingContext'\n\n// Utils\nimport { getCellValue, parseCellId } from '../utils/cellUtils'\n\n// Types\nimport { EntityUpdate } from '../hooks/useUpdateOverview'\n\n// Import from the new modular files\nimport {\n getEntityPath,\n parseClipboardText,\n clipboardError,\n processFieldValue,\n} from './clipboard/clipboardUtils'\nimport { validateClipboardData } from './clipboard/clipboardValidation'\nimport { ClipboardContextType, ClipboardProviderProps } from './clipboard/clipboardTypes'\nimport { useProjectTableContext } from './ProjectTableContext'\nimport { AttributeEnumItem } from '@shared/api'\n\nconst ClipboardContext = createContext<ClipboardContextType | undefined>(undefined)\n\nexport const ClipboardProvider: React.FC<ClipboardProviderProps> = ({ children, options }) => {\n const { foldersMap, tasksMap, attribFields } = useProjectTableContext()\n // Get selection information from SelectionCellsContext\n const { selectedCells, gridMap, focusedCellId } = useSelectionCellsContext()\n const { updateEntities } = useCellEditing()\n\n // convert attribs to object\n const attribByField = useMemo(() => {\n return attribFields.reduce((acc: Record<string, AttributeEnumItem[]>, attrib) => {\n if (attrib.data?.enum?.length) {\n acc[attrib.name] = attrib.data?.enum\n }\n return acc\n }, {})\n }, [attribFields])\n\n const columnEnums = useMemo(() => ({ ...options, ...attribByField }), [attribByField])\n\n const columnReadOnly = attribFields\n .filter((attrib) => attrib.readOnly)\n .map((attrib) => attrib.name)\n\n const getSelectionData = useCallback(\n async (selected: string[], config?: { headers?: boolean; fullRow?: boolean }) => {\n const { headers, fullRow } = config || {}\n try {\n // First, organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n selected.forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n // do not include row selection column\n if (colId === ROW_SELECTION_COLUMN_ID) return\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n\n cellsByRow.get(rowId)?.add(colId)\n })\n\n if (fullRow) {\n const selectedRows = selected\n .filter(\n (cellId) =>\n parseCellId(cellId)?.rowId &&\n parseCellId(cellId)?.colId === ROW_SELECTION_COLUMN_ID,\n )\n .map((cellId) => parseCellId(cellId)?.rowId) as string[]\n\n // select the whole row\n // For rows with selection cells, add all available columns\n selectedRows.forEach((rowId) => {\n // add the rowId if it doesn't exist\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n const allColumns = Array.from(gridMap.colIdToIndex.keys())\n allColumns.forEach((colId) => {\n cellsByRow.get(rowId)?.add(colId)\n })\n })\n }\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Build clipboard text\n let clipboardText = ''\n\n // Get the first row to determine columns\n const firstRowId = sortedRows[0]\n if (!firstRowId) return ''\n\n // Get all column IDs for the first row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(firstRowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Include headers if requested\n if (headers && colIds.length > 0) {\n const headerValues: string[] = []\n\n for (const colId of colIds) {\n // Use colId as the column name since we don't have direct access to column names\n const columnName = colId\n headerValues.push(`${columnName.replace(/\"/g, '\"\"')}`)\n }\n\n clipboardText += headerValues.join('\\t') + '\\n'\n }\n\n for (const rowId of sortedRows) {\n // Determine if this is a folder or task by checking which map contains the ID\n const isFolder = foldersMap.has(rowId)\n const entity = isFolder ? foldersMap.get(rowId) : tasksMap.get(rowId)\n\n if (!entity) continue\n\n // Get all column IDs for this row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(rowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Filter out the row selection column from the copied data\n const filteredColIds = colIds.filter((colId) => colId !== ROW_SELECTION_COLUMN_ID)\n\n const rowValues: string[] = []\n\n // For each column in this row\n for (const colId of filteredColIds) {\n // Determine the value based on the column ID\n let cellValue = ''\n // @ts-ignore\n const foundValue = getCellValue(entity, colId)\n cellValue = foundValue !== undefined && foundValue !== null ? String(foundValue) : ''\n\n // Special handling for name field - include full path\n if (colId === 'name') {\n cellValue = getEntityPath(rowId, isFolder, foldersMap, tasksMap)\n }\n\n if (colId === 'subType') {\n // get folderType or taskType\n // @ts-ignore\n cellValue = entity[isFolder ? 'folderType' : 'taskType'] || ''\n }\n\n // Escape double quotes in the cell value and wrap in quotes\n rowValues.push(`${cellValue.replace(/\"/g, '\"\"')}`)\n }\n\n // Add row to clipboard text\n clipboardText += rowValues.join('\\t') + '\\n'\n }\n\n return clipboardText\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, focusedCellId, gridMap, foldersMap, tasksMap],\n )\n\n const copyToClipboard: ClipboardContextType['copyToClipboard'] = useCallback(\n async (selected, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n const clipboardText = await getSelectionData(selected, { fullRow })\n if (!clipboardText) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n try {\n await navigator.clipboard.writeText(clipboardText)\n console.log('Copied to clipboard successfully', clipboardText)\n } catch (error: any) {\n clipboardError(`Failed to copy to clipboard: ${error.message}`)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap],\n )\n\n const exportCSV: ClipboardContextType['exportCSV'] = useCallback(\n async (selected, projectName, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n\n try {\n // Get clipboard text with headers included for CSV export\n const clipboardText = await getSelectionData(selected, { headers: true, fullRow })\n if (!clipboardText) return\n\n // create a csv file and download it\n const blob = new Blob([clipboardText], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n const selectedCount = selected.length\n a.download = `${projectName}-export-${selectedCount}_cells-${new Date()\n .toISOString()\n .slice(0, 10)}.csv`\n a.click()\n URL.revokeObjectURL(url)\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap, getSelectionData],\n )\n\n const pasteFromClipboard: ClipboardContextType['pasteFromClipboard'] = useCallback(\n async (selected) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n let clipboardText: string\n try {\n clipboardText = await navigator.clipboard.readText()\n } catch (error: any) {\n clipboardError(`Failed to read from clipboard: ${error.message}`)\n return\n }\n if (!clipboardText.trim()) return\n\n // Parse the clipboard text\n const parsedData = parseClipboardText(clipboardText)\n if (!parsedData.length) return\n\n // Determine if we have a single value in the clipboard (one row, one column)\n const isSingleCellValue = parsedData.length === 1 && parsedData[0].values.length === 1\n\n // Organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n Array.from(selected).forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n cellsByRow.get(rowId)?.add(colId)\n })\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // For each row, get the sorted column IDs\n const firstRow = sortedRows[0]\n const selectedColIds = Array.from(cellsByRow.get(firstRow) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // First pass: validate all values for status and subType\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n\n // Get the appropriate value from the clipboard data\n // If it's a single cell value, use it for all cells\n // Otherwise use the modulo approach to repeat values\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n // Validate clipboard data for this cell\n const isValid = validateClipboardData({\n colId,\n isFolder,\n pasteValue,\n parsedData,\n columnEnums,\n columnReadOnly,\n rowIndex,\n colIndex,\n isSingleCellValue,\n })\n\n if (!isValid) return\n }\n }\n\n // Create a map to consolidate updates for the same entity\n const entitiesMap = new Map<\n string,\n {\n id: string\n type: string\n fields: Record<string, any>\n attrib: Record<string, any>\n }\n >()\n\n // For each column, prepare updates\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n // Skip special handling for 'name' which we don't want to paste\n if (colId === 'name') continue\n\n // Check if this is an attribute field by examining the first entity\n let isAttrib = false\n // Check if the field potentially contains array values\n let fieldValueType: 'string' | 'number' | 'boolean' | 'array' = 'string'\n\n if (sortedRows.length > 0) {\n const firstRowId = sortedRows[0]\n const isFolder = foldersMap.has(firstRowId)\n const entity = isFolder ? foldersMap.get(firstRowId) : tasksMap.get(firstRowId)\n\n if (entity) {\n isAttrib = colId.startsWith('attrib_')\n\n // Determine if field is an array and its value type\n // @ts-ignore - Check entity property or attribute\n const fieldValue = getCellValue(entity, colId)\n if (Array.isArray(fieldValue)) {\n fieldValueType = 'array'\n } else if (typeof fieldValue === 'number') {\n fieldValueType = 'number'\n } else if (typeof fieldValue === 'boolean') {\n fieldValueType = 'boolean'\n }\n\n // Special case for subType\n if (colId === 'subType') {\n isAttrib = false\n }\n }\n }\n\n // Process each row individually\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n const entityType = isFolder ? 'folder' : 'task'\n\n // Get the appropriate value from the clipboard data\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n let fieldToUpdate = colId.split('_').pop() || colId\n\n // Special handling for subType (convert to folderType or taskType)\n if (colId === 'subType') {\n fieldToUpdate = isFolder ? 'folderType' : 'taskType'\n isAttrib = false\n\n // Skip empty values for enum fields\n if (!pasteValue) continue\n }\n\n // Process the value based on its type\n const processedValue = processFieldValue(pasteValue, fieldValueType)\n\n // Get or create entity entry in the map\n const entityKey = `${rowId}-${entityType}`\n if (!entitiesMap.has(entityKey)) {\n entitiesMap.set(entityKey, {\n id: rowId,\n type: entityType,\n fields: {},\n attrib: {},\n })\n }\n\n const entityData = entitiesMap.get(entityKey)!\n\n // Add the field to the appropriate place\n if (isAttrib) {\n entityData.attrib[fieldToUpdate] = processedValue\n } else {\n entityData.fields[fieldToUpdate] = processedValue\n }\n }\n }\n\n // Convert the consolidated map to EntityUpdate array\n const allEntityUpdates: EntityUpdate[] = []\n\n entitiesMap.forEach((entity) => {\n // For regular fields, create one update per field\n Object.entries(entity.fields).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n })\n })\n\n // For attributes, create one update per attribute\n Object.entries(entity.attrib).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n isAttrib: true,\n })\n })\n })\n\n // Make a single call to update all entities\n if (allEntityUpdates.length > 0) {\n try {\n await updateEntities(allEntityUpdates)\n } catch (error) {\n console.error('Error updating entities:', error)\n clipboardError(\n `Failed to update: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n }\n },\n [selectedCells, gridMap, foldersMap, tasksMap, updateEntities, columnEnums],\n )\n\n // Set up keyboard event listeners\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Copy functionality (Ctrl+C or Command+C)\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n copyToClipboard()\n }\n\n // Paste functionality (Ctrl+V or Command+V)\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n pasteFromClipboard()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [copyToClipboard, pasteFromClipboard])\n\n const value = useMemo(\n () => ({\n copyToClipboard,\n pasteFromClipboard,\n exportCSV,\n }),\n [copyToClipboard, pasteFromClipboard],\n )\n\n return <ClipboardContext.Provider value={value}>{children}</ClipboardContext.Provider>\n}\n\nexport const useClipboard = (): ClipboardContextType => {\n const context = useContext(ClipboardContext)\n if (context === undefined) {\n throw new Error('useClipboard must be used within a ClipboardProvider')\n }\n return context\n}\n"],"names":["colIds","value","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ClipboardContext.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/context/ClipboardContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useCallback, useMemo, useEffect } from 'react'\n\n// Contexts\nimport { ROW_SELECTION_COLUMN_ID, useSelectionCellsContext } from './SelectionCellsContext'\nimport { useCellEditing } from './CellEditingContext'\n\n// Utils\nimport { getCellValue, parseCellId } from '../utils/cellUtils'\n\n// Types\nimport { EntityUpdate } from '../hooks/useUpdateOverview'\n\n// Import from the new modular files\nimport {\n getEntityPath,\n parseClipboardText,\n clipboardError,\n processFieldValue,\n} from './clipboard/clipboardUtils'\nimport { validateClipboardData } from './clipboard/clipboardValidation'\nimport { ClipboardContextType, ClipboardProviderProps } from './clipboard/clipboardTypes'\nimport { useProjectTableContext } from './ProjectTableContext'\nimport { AttributeEnumItem } from '@shared/api'\n\nconst ClipboardContext = createContext<ClipboardContextType | undefined>(undefined)\n\nexport const ClipboardProvider: React.FC<ClipboardProviderProps> = ({ children, options }) => {\n const { foldersMap, tasksMap, attribFields } = useProjectTableContext()\n // Get selection information from SelectionCellsContext\n const { selectedCells, gridMap, focusedCellId } = useSelectionCellsContext()\n const { updateEntities } = useCellEditing()\n\n // convert attribs to object\n const attribByField = useMemo(() => {\n return attribFields.reduce((acc: Record<string, AttributeEnumItem[]>, attrib) => {\n if (attrib.data?.enum?.length) {\n acc[attrib.name] = attrib.data?.enum\n }\n return acc\n }, {})\n }, [attribFields])\n\n const columnEnums = useMemo(() => ({ ...options, ...attribByField }), [attribByField])\n\n const columnReadOnly = attribFields\n .filter((attrib) => attrib.readOnly)\n .map((attrib) => attrib.name)\n\n const getSelectionData = useCallback(\n async (selected: string[], config?: { headers?: boolean; fullRow?: boolean }) => {\n const { headers, fullRow } = config || {}\n try {\n // First, organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n selected.forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n // do not include row selection column\n if (colId === ROW_SELECTION_COLUMN_ID) return\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n\n cellsByRow.get(rowId)?.add(colId)\n })\n\n if (fullRow) {\n const selectedRows = selected\n .filter(\n (cellId) =>\n parseCellId(cellId)?.rowId &&\n parseCellId(cellId)?.colId === ROW_SELECTION_COLUMN_ID,\n )\n .map((cellId) => parseCellId(cellId)?.rowId) as string[]\n\n // select the whole row\n // For rows with selection cells, add all available columns\n selectedRows.forEach((rowId) => {\n // add the rowId if it doesn't exist\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n const allColumns = Array.from(gridMap.colIdToIndex.keys())\n allColumns.forEach((colId) => {\n cellsByRow.get(rowId)?.add(colId)\n })\n })\n }\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Build clipboard text\n let clipboardText = ''\n\n // Get the first row to determine columns\n const firstRowId = sortedRows[0]\n if (!firstRowId) return ''\n\n // Get all column IDs for the first row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(firstRowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Include headers if requested\n if (headers && colIds.length > 0) {\n const headerValues: string[] = []\n\n for (const colId of colIds) {\n // Use colId as the column name since we don't have direct access to column names\n const columnName = colId\n headerValues.push(`${columnName.replace(/\"/g, '\"\"')}`)\n }\n\n clipboardText += headerValues.join('\\t') + '\\n'\n }\n\n for (const rowId of sortedRows) {\n // Determine if this is a folder or task by checking which map contains the ID\n const isFolder = foldersMap.has(rowId)\n const entity = isFolder ? foldersMap.get(rowId) : tasksMap.get(rowId)\n\n if (!entity) continue\n\n // Get all column IDs for this row, sorted by their index in the grid\n const colIds = Array.from(cellsByRow.get(rowId) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // Filter out the row selection column from the copied data\n const filteredColIds = colIds.filter((colId) => colId !== ROW_SELECTION_COLUMN_ID)\n\n const rowValues: string[] = []\n\n // For each column in this row\n for (const colId of filteredColIds) {\n // Determine the value based on the column ID\n let cellValue = ''\n // @ts-ignore\n const foundValue = getCellValue(entity, colId)\n cellValue = foundValue !== undefined && foundValue !== null ? String(foundValue) : ''\n\n // Special handling for name field - include full path\n if (colId === 'name') {\n cellValue = getEntityPath(rowId, isFolder, foldersMap, tasksMap)\n }\n\n if (colId === 'subType') {\n // get folderType or taskType\n // @ts-ignore\n cellValue = entity[isFolder ? 'folderType' : 'taskType'] || ''\n }\n\n // Escape double quotes in the cell value and wrap in quotes\n rowValues.push(`${cellValue.replace(/\"/g, '\"\"')}`)\n }\n\n // Add row to clipboard text\n clipboardText += rowValues.join('\\t') + '\\n'\n }\n\n return clipboardText\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, focusedCellId, gridMap, foldersMap, tasksMap],\n )\n\n const copyToClipboard: ClipboardContextType['copyToClipboard'] = useCallback(\n async (selected, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n const clipboardText = await getSelectionData(selected, { fullRow })\n if (!clipboardText) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n try {\n await navigator.clipboard.writeText(clipboardText)\n console.log('Copied to clipboard successfully', clipboardText)\n } catch (error: any) {\n clipboardError(`Failed to copy to clipboard: ${error.message}`)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap],\n )\n\n const exportCSV: ClipboardContextType['exportCSV'] = useCallback(\n async (selected, projectName, fullRow) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n\n try {\n // Get clipboard text with headers included for CSV export\n const clipboardText = await getSelectionData(selected, { headers: true, fullRow })\n if (!clipboardText) return\n\n // create a csv file and download it\n const blob = new Blob([clipboardText], { type: 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n const selectedCount = selected.length\n a.download = `${projectName}-export-${selectedCount}_cells-${new Date()\n .toISOString()\n .slice(0, 10)}.csv`\n a.click()\n URL.revokeObjectURL(url)\n } catch (error) {\n console.error('Failed to copy to clipboard:', error)\n }\n },\n [selectedCells, foldersMap, tasksMap, gridMap, getSelectionData],\n )\n\n const pasteFromClipboard: ClipboardContextType['pasteFromClipboard'] = useCallback(\n async (selected) => {\n selected = selected || Array.from(selectedCells)\n if (!selected.length) return\n if (!navigator.clipboard) {\n clipboardError('Clipboard API not supported in this browser.')\n return\n }\n if (!window.isSecureContext) {\n clipboardError('Clipboard operations require a secure HTTPS context.')\n return\n }\n let clipboardText: string\n try {\n clipboardText = await navigator.clipboard.readText()\n } catch (error: any) {\n clipboardError(`Failed to read from clipboard: ${error.message}`)\n return\n }\n\n // we can have empty text in the clipboard\n //if (!clipboardText.trim()) return\n\n // Parse the clipboard text\n const parsedData = parseClipboardText(clipboardText)\n if (!parsedData.length) return\n\n // Determine if we have a single value in the clipboard (one row, one column)\n const isSingleCellValue = parsedData.length === 1 && parsedData[0].values.length === 1\n\n // Organize selected cells by row\n const cellsByRow = new Map<string, Set<string>>()\n\n // Parse all selected cells and organize by rowId and colId\n Array.from(selected).forEach((cellId) => {\n const position = parseCellId(cellId)\n if (!position) return\n\n const { rowId, colId } = position\n\n if (!cellsByRow.has(rowId)) {\n cellsByRow.set(rowId, new Set())\n }\n cellsByRow.get(rowId)?.add(colId)\n })\n\n // Get sorted row IDs based on their index in the grid\n const sortedRows = Array.from(cellsByRow.keys()).sort((a, b) => {\n const indexA = gridMap.rowIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.rowIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // For each row, get the sorted column IDs\n const firstRow = sortedRows[0]\n const selectedColIds = Array.from(cellsByRow.get(firstRow) || []).sort((a, b) => {\n const indexA = gridMap.colIdToIndex.get(a) ?? Infinity\n const indexB = gridMap.colIdToIndex.get(b) ?? Infinity\n return indexA - indexB\n })\n\n // First pass: validate all values for status and subType\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n\n // Get the appropriate value from the clipboard data\n // If it's a single cell value, use it for all cells\n // Otherwise use the modulo approach to repeat values\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n // Validate clipboard data for this cell\n const isValid = validateClipboardData({\n colId,\n isFolder,\n pasteValue,\n parsedData,\n columnEnums,\n columnReadOnly,\n rowIndex,\n colIndex,\n isSingleCellValue,\n })\n\n if (!isValid) return\n }\n }\n\n // Create a map to consolidate updates for the same entity\n const entitiesMap = new Map<\n string,\n {\n id: string\n type: string\n fields: Record<string, any>\n attrib: Record<string, any>\n }\n >()\n\n // For each column, prepare updates\n for (let colIndex = 0; colIndex < selectedColIds.length; colIndex++) {\n const colId = selectedColIds[colIndex]\n\n // Skip special handling for 'name' which we don't want to paste\n if (colId === 'name') continue\n\n // Check if this is an attribute field by examining the first entity\n let isAttrib = false\n // Check if the field potentially contains array values\n let fieldValueType: 'string' | 'number' | 'boolean' | 'array' = 'string'\n\n if (sortedRows.length > 0) {\n const firstRowId = sortedRows[0]\n const isFolder = foldersMap.has(firstRowId)\n const entity = isFolder ? foldersMap.get(firstRowId) : tasksMap.get(firstRowId)\n\n if (entity) {\n isAttrib = colId.startsWith('attrib_')\n\n // Determine if field is an array and its value type\n // @ts-ignore - Check entity property or attribute\n const fieldValue = getCellValue(entity, colId)\n if (Array.isArray(fieldValue)) {\n fieldValueType = 'array'\n } else if (typeof fieldValue === 'number') {\n fieldValueType = 'number'\n } else if (typeof fieldValue === 'boolean') {\n fieldValueType = 'boolean'\n }\n\n // Special case for subType\n if (colId === 'subType') {\n isAttrib = false\n }\n }\n }\n\n // Process each row individually\n for (let rowIndex = 0; rowIndex < sortedRows.length; rowIndex++) {\n const rowId = sortedRows[rowIndex]\n const isFolder = foldersMap.has(rowId)\n const entityType = isFolder ? 'folder' : 'task'\n\n // Get the appropriate value from the clipboard data\n let pasteValue\n if (isSingleCellValue) {\n pasteValue = parsedData[0].values[0]\n } else {\n const pasteRowIndex = rowIndex % parsedData.length\n const pasteColIndex = colIndex % parsedData[pasteRowIndex].values.length\n pasteValue = parsedData[pasteRowIndex].values[pasteColIndex]\n }\n\n let fieldToUpdate = colId.split('_').pop() || colId\n\n // Special handling for subType (convert to folderType or taskType)\n if (colId === 'subType') {\n fieldToUpdate = isFolder ? 'folderType' : 'taskType'\n isAttrib = false\n\n // Skip empty values for enum fields\n if (!pasteValue) continue\n }\n\n // Process the value based on its type\n const processedValue = processFieldValue(pasteValue, fieldValueType)\n\n // Get or create entity entry in the map\n const entityKey = `${rowId}-${entityType}`\n if (!entitiesMap.has(entityKey)) {\n entitiesMap.set(entityKey, {\n id: rowId,\n type: entityType,\n fields: {},\n attrib: {},\n })\n }\n\n const entityData = entitiesMap.get(entityKey)!\n\n // Add the field to the appropriate place\n if (isAttrib) {\n entityData.attrib[fieldToUpdate] = processedValue\n } else {\n entityData.fields[fieldToUpdate] = processedValue\n }\n }\n }\n\n // Convert the consolidated map to EntityUpdate array\n const allEntityUpdates: EntityUpdate[] = []\n\n entitiesMap.forEach((entity) => {\n // For regular fields, create one update per field\n Object.entries(entity.fields).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n })\n })\n\n // For attributes, create one update per attribute\n Object.entries(entity.attrib).forEach(([field, value]) => {\n allEntityUpdates.push({\n id: entity.id,\n type: entity.type,\n field,\n value,\n isAttrib: true,\n })\n })\n })\n\n // Make a single call to update all entities\n if (allEntityUpdates.length > 0) {\n try {\n await updateEntities(allEntityUpdates)\n } catch (error) {\n console.error('Error updating entities:', error)\n clipboardError(\n `Failed to update: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n }\n },\n [selectedCells, gridMap, foldersMap, tasksMap, updateEntities, columnEnums],\n )\n\n // Set up keyboard event listeners\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Copy functionality (Ctrl+C or Command+C)\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n copyToClipboard()\n }\n\n // Paste functionality (Ctrl+V or Command+V)\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n pasteFromClipboard()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [copyToClipboard, pasteFromClipboard])\n\n const value = useMemo(\n () => ({\n copyToClipboard,\n pasteFromClipboard,\n exportCSV,\n }),\n [copyToClipboard, pasteFromClipboard],\n )\n\n return <ClipboardContext.Provider value={value}>{children}</ClipboardContext.Provider>\n}\n\nexport const useClipboard = (): ClipboardContextType => {\n const context = useContext(ClipboardContext)\n if (context === undefined) {\n throw new Error('useClipboard must be used within a ClipboardProvider')\n }\n return context\n}\n"],"names":["colIds","value","jsx"],"mappings":"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"}
|
|
@@ -24,6 +24,9 @@ const getEntityPath = (entityId, isFolder, foldersMap, tasksMap) => {
|
|
|
24
24
|
const processFieldValue = (value, fieldValueType) => {
|
|
25
25
|
if (fieldValueType === "array") {
|
|
26
26
|
try {
|
|
27
|
+
if (value === "") {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
27
30
|
try {
|
|
28
31
|
const parsed = JSON.parse(value);
|
|
29
32
|
return Array.isArray(parsed) ? parsed : [parsed];
|
package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboardUtils.cjs.js","sources":["../../../../../../../src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.ts"],"sourcesContent":["import { toast } from 'react-toastify'\nimport { FolderNodeMap, TaskNodeMap } from '../../types/table'\nimport { ParsedClipboardData } from './clipboardTypes'\n\nexport const clipboardError = (error: string) => toast.error(error)\n\n// Parse clipboard text into rows and columns\nexport const parseClipboardText = (clipboardText: string): ParsedClipboardData[] => {\n const rows = clipboardText.trim().split('\\n')\n const parsedData: ParsedClipboardData[] = []\n\n // Store original column order for validation\n const origColumnOrder: string[] = []\n\n // Parse each row into values\n rows.forEach((row) => {\n const rowValues = row.split('\\t')\n parsedData.push({ values: rowValues, colIds: origColumnOrder })\n })\n\n return parsedData\n}\n\n// Get the full path for an entity\nexport const getEntityPath = (\n entityId: string,\n isFolder: boolean,\n foldersMap: FolderNodeMap,\n tasksMap: TaskNodeMap,\n): string => {\n const entity = isFolder ? foldersMap.get(entityId) : tasksMap.get(entityId)\n if (!entity) return ''\n\n const name = entity.name || ''\n // @ts-ignore\n const parentId = entity.folderId || entity.parentId\n\n // If no parent, return just the name\n if (!parentId) return name\n\n // If has parent, get parent path (parents are always folders)\n const parentPath = getEntityPath(parentId, true, foldersMap, tasksMap)\n\n // Combine paths with \" / \" separator\n return parentPath ? `${parentPath} / ${name}` : name\n}\n\n// Process a field value based on its type\nexport const processFieldValue = (\n value: string,\n fieldValueType: 'string' | 'number' | 'boolean' | 'array',\n): any => {\n if (fieldValueType === 'array') {\n try {\n // Try to parse as JSON first (for copied arrays)\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed) ? parsed : [parsed]\n } catch {\n // If not valid JSON, treat as comma-separated values\n return value.includes(',') ? value.split(',').map((v) => v.trim()) : [value]\n }\n } catch {\n // Fallback to single-item array if parsing fails\n return [value]\n }\n } else if (fieldValueType === 'number') {\n return Number(value) || 0\n } else if (fieldValueType === 'boolean') {\n return value.toLowerCase() === 'true' || value === '1' || value.toLowerCase() === 'yes'\n }\n\n // Default string handling\n return value\n}\n"],"names":["toast"],"mappings":";;;AAIO,MAAM,iBAAiB,CAAC,UAAkBA,cAAA,MAAM,MAAM,KAAK;AAGrD,MAAA,qBAAqB,CAAC,kBAAiD;AAClF,QAAM,OAAO,cAAc,KAAK,EAAE,MAAM,IAAI;AAC5C,QAAM,aAAoC,CAAC;AAG3C,QAAM,kBAA4B,CAAC;AAG9B,OAAA,QAAQ,CAAC,QAAQ;AACd,UAAA,YAAY,IAAI,MAAM,GAAI;AAChC,eAAW,KAAK,EAAE,QAAQ,WAAW,QAAQ,iBAAiB;AAAA,EAAA,CAC/D;AAEM,SAAA;AACT;AAGO,MAAM,gBAAgB,CAC3B,UACA,UACA,YACA,aACW;AACL,QAAA,SAAS,WAAW,WAAW,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACtE,MAAA,CAAC,OAAe,QAAA;AAEd,QAAA,OAAO,OAAO,QAAQ;AAEtB,QAAA,WAAW,OAAO,YAAY,OAAO;AAGvC,MAAA,CAAC,SAAiB,QAAA;AAGtB,QAAM,aAAa,cAAc,UAAU,MAAM,YAAY,QAAQ;AAGrE,SAAO,aAAa,GAAG,UAAU,MAAM,IAAI,KAAK;AAClD;AAGa,MAAA,oBAAoB,CAC/B,OACA,mBACQ;AACR,MAAI,mBAAmB,SAAS;AAC1B,QAAA;
|
|
1
|
+
{"version":3,"file":"clipboardUtils.cjs.js","sources":["../../../../../../../src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.ts"],"sourcesContent":["import { toast } from 'react-toastify'\nimport { FolderNodeMap, TaskNodeMap } from '../../types/table'\nimport { ParsedClipboardData } from './clipboardTypes'\n\nexport const clipboardError = (error: string) => toast.error(error)\n\n// Parse clipboard text into rows and columns\nexport const parseClipboardText = (clipboardText: string): ParsedClipboardData[] => {\n const rows = clipboardText.trim().split('\\n')\n const parsedData: ParsedClipboardData[] = []\n\n // Store original column order for validation\n const origColumnOrder: string[] = []\n\n // Parse each row into values\n rows.forEach((row) => {\n const rowValues = row.split('\\t')\n parsedData.push({ values: rowValues, colIds: origColumnOrder })\n })\n\n return parsedData\n}\n\n// Get the full path for an entity\nexport const getEntityPath = (\n entityId: string,\n isFolder: boolean,\n foldersMap: FolderNodeMap,\n tasksMap: TaskNodeMap,\n): string => {\n const entity = isFolder ? foldersMap.get(entityId) : tasksMap.get(entityId)\n if (!entity) return ''\n\n const name = entity.name || ''\n // @ts-ignore\n const parentId = entity.folderId || entity.parentId\n\n // If no parent, return just the name\n if (!parentId) return name\n\n // If has parent, get parent path (parents are always folders)\n const parentPath = getEntityPath(parentId, true, foldersMap, tasksMap)\n\n // Combine paths with \" / \" separator\n return parentPath ? `${parentPath} / ${name}` : name\n}\n\n// Process a field value based on its type\nexport const processFieldValue = (\n value: string,\n fieldValueType: 'string' | 'number' | 'boolean' | 'array',\n): any => {\n if (fieldValueType === 'array') {\n try {\n if (value === '') {\n return []\n }\n // Try to parse as JSON first (for copied arrays)\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed) ? parsed : [parsed]\n } catch {\n // If not valid JSON, treat as comma-separated values\n return value.includes(',') ? value.split(',').map((v) => v.trim()) : [value]\n }\n } catch {\n // Fallback to single-item array if parsing fails\n return [value]\n }\n } else if (fieldValueType === 'number') {\n return Number(value) || 0\n } else if (fieldValueType === 'boolean') {\n return value.toLowerCase() === 'true' || value === '1' || value.toLowerCase() === 'yes'\n }\n\n // Default string handling\n return value\n}\n"],"names":["toast"],"mappings":";;;AAIO,MAAM,iBAAiB,CAAC,UAAkBA,cAAA,MAAM,MAAM,KAAK;AAGrD,MAAA,qBAAqB,CAAC,kBAAiD;AAClF,QAAM,OAAO,cAAc,KAAK,EAAE,MAAM,IAAI;AAC5C,QAAM,aAAoC,CAAC;AAG3C,QAAM,kBAA4B,CAAC;AAG9B,OAAA,QAAQ,CAAC,QAAQ;AACd,UAAA,YAAY,IAAI,MAAM,GAAI;AAChC,eAAW,KAAK,EAAE,QAAQ,WAAW,QAAQ,iBAAiB;AAAA,EAAA,CAC/D;AAEM,SAAA;AACT;AAGO,MAAM,gBAAgB,CAC3B,UACA,UACA,YACA,aACW;AACL,QAAA,SAAS,WAAW,WAAW,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACtE,MAAA,CAAC,OAAe,QAAA;AAEd,QAAA,OAAO,OAAO,QAAQ;AAEtB,QAAA,WAAW,OAAO,YAAY,OAAO;AAGvC,MAAA,CAAC,SAAiB,QAAA;AAGtB,QAAM,aAAa,cAAc,UAAU,MAAM,YAAY,QAAQ;AAGrE,SAAO,aAAa,GAAG,UAAU,MAAM,IAAI,KAAK;AAClD;AAGa,MAAA,oBAAoB,CAC/B,OACA,mBACQ;AACR,MAAI,mBAAmB,SAAS;AAC1B,QAAA;AACF,UAAI,UAAU,IAAI;AAChB,eAAO,CAAC;AAAA,MAAA;AAGN,UAAA;AACI,cAAA,SAAS,KAAK,MAAM,KAAK;AAC/B,eAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAAA,MAAA,QACzC;AAEN,eAAO,MAAM,SAAS,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,IAAI,CAAC,KAAK;AAAA,MAAA;AAAA,IAC7E,QACM;AAEN,aAAO,CAAC,KAAK;AAAA,IAAA;AAAA,EACf,WACS,mBAAmB,UAAU;AAC/B,WAAA,OAAO,KAAK,KAAK;AAAA,EAAA,WACf,mBAAmB,WAAW;AAChC,WAAA,MAAM,kBAAkB,UAAU,UAAU,OAAO,MAAM,kBAAkB;AAAA,EAAA;AAI7E,SAAA;AACT;;;;;"}
|
|
@@ -22,6 +22,9 @@ const getEntityPath = (entityId, isFolder, foldersMap, tasksMap) => {
|
|
|
22
22
|
const processFieldValue = (value, fieldValueType) => {
|
|
23
23
|
if (fieldValueType === "array") {
|
|
24
24
|
try {
|
|
25
|
+
if (value === "") {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
25
28
|
try {
|
|
26
29
|
const parsed = JSON.parse(value);
|
|
27
30
|
return Array.isArray(parsed) ? parsed : [parsed];
|
package/dist/shared/src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboardUtils.es.js","sources":["../../../../../../../src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.ts"],"sourcesContent":["import { toast } from 'react-toastify'\nimport { FolderNodeMap, TaskNodeMap } from '../../types/table'\nimport { ParsedClipboardData } from './clipboardTypes'\n\nexport const clipboardError = (error: string) => toast.error(error)\n\n// Parse clipboard text into rows and columns\nexport const parseClipboardText = (clipboardText: string): ParsedClipboardData[] => {\n const rows = clipboardText.trim().split('\\n')\n const parsedData: ParsedClipboardData[] = []\n\n // Store original column order for validation\n const origColumnOrder: string[] = []\n\n // Parse each row into values\n rows.forEach((row) => {\n const rowValues = row.split('\\t')\n parsedData.push({ values: rowValues, colIds: origColumnOrder })\n })\n\n return parsedData\n}\n\n// Get the full path for an entity\nexport const getEntityPath = (\n entityId: string,\n isFolder: boolean,\n foldersMap: FolderNodeMap,\n tasksMap: TaskNodeMap,\n): string => {\n const entity = isFolder ? foldersMap.get(entityId) : tasksMap.get(entityId)\n if (!entity) return ''\n\n const name = entity.name || ''\n // @ts-ignore\n const parentId = entity.folderId || entity.parentId\n\n // If no parent, return just the name\n if (!parentId) return name\n\n // If has parent, get parent path (parents are always folders)\n const parentPath = getEntityPath(parentId, true, foldersMap, tasksMap)\n\n // Combine paths with \" / \" separator\n return parentPath ? `${parentPath} / ${name}` : name\n}\n\n// Process a field value based on its type\nexport const processFieldValue = (\n value: string,\n fieldValueType: 'string' | 'number' | 'boolean' | 'array',\n): any => {\n if (fieldValueType === 'array') {\n try {\n // Try to parse as JSON first (for copied arrays)\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed) ? parsed : [parsed]\n } catch {\n // If not valid JSON, treat as comma-separated values\n return value.includes(',') ? value.split(',').map((v) => v.trim()) : [value]\n }\n } catch {\n // Fallback to single-item array if parsing fails\n return [value]\n }\n } else if (fieldValueType === 'number') {\n return Number(value) || 0\n } else if (fieldValueType === 'boolean') {\n return value.toLowerCase() === 'true' || value === '1' || value.toLowerCase() === 'yes'\n }\n\n // Default string handling\n return value\n}\n"],"names":[],"mappings":";AAIO,MAAM,iBAAiB,CAAC,UAAkB,MAAM,MAAM,KAAK;AAGrD,MAAA,qBAAqB,CAAC,kBAAiD;AAClF,QAAM,OAAO,cAAc,KAAK,EAAE,MAAM,IAAI;AAC5C,QAAM,aAAoC,CAAC;AAG3C,QAAM,kBAA4B,CAAC;AAG9B,OAAA,QAAQ,CAAC,QAAQ;AACd,UAAA,YAAY,IAAI,MAAM,GAAI;AAChC,eAAW,KAAK,EAAE,QAAQ,WAAW,QAAQ,iBAAiB;AAAA,EAAA,CAC/D;AAEM,SAAA;AACT;AAGO,MAAM,gBAAgB,CAC3B,UACA,UACA,YACA,aACW;AACL,QAAA,SAAS,WAAW,WAAW,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACtE,MAAA,CAAC,OAAe,QAAA;AAEd,QAAA,OAAO,OAAO,QAAQ;AAEtB,QAAA,WAAW,OAAO,YAAY,OAAO;AAGvC,MAAA,CAAC,SAAiB,QAAA;AAGtB,QAAM,aAAa,cAAc,UAAU,MAAM,YAAY,QAAQ;AAGrE,SAAO,aAAa,GAAG,UAAU,MAAM,IAAI,KAAK;AAClD;AAGa,MAAA,oBAAoB,CAC/B,OACA,mBACQ;AACR,MAAI,mBAAmB,SAAS;AAC1B,QAAA;
|
|
1
|
+
{"version":3,"file":"clipboardUtils.es.js","sources":["../../../../../../../src/containers/ProjectTreeTable/context/clipboard/clipboardUtils.ts"],"sourcesContent":["import { toast } from 'react-toastify'\nimport { FolderNodeMap, TaskNodeMap } from '../../types/table'\nimport { ParsedClipboardData } from './clipboardTypes'\n\nexport const clipboardError = (error: string) => toast.error(error)\n\n// Parse clipboard text into rows and columns\nexport const parseClipboardText = (clipboardText: string): ParsedClipboardData[] => {\n const rows = clipboardText.trim().split('\\n')\n const parsedData: ParsedClipboardData[] = []\n\n // Store original column order for validation\n const origColumnOrder: string[] = []\n\n // Parse each row into values\n rows.forEach((row) => {\n const rowValues = row.split('\\t')\n parsedData.push({ values: rowValues, colIds: origColumnOrder })\n })\n\n return parsedData\n}\n\n// Get the full path for an entity\nexport const getEntityPath = (\n entityId: string,\n isFolder: boolean,\n foldersMap: FolderNodeMap,\n tasksMap: TaskNodeMap,\n): string => {\n const entity = isFolder ? foldersMap.get(entityId) : tasksMap.get(entityId)\n if (!entity) return ''\n\n const name = entity.name || ''\n // @ts-ignore\n const parentId = entity.folderId || entity.parentId\n\n // If no parent, return just the name\n if (!parentId) return name\n\n // If has parent, get parent path (parents are always folders)\n const parentPath = getEntityPath(parentId, true, foldersMap, tasksMap)\n\n // Combine paths with \" / \" separator\n return parentPath ? `${parentPath} / ${name}` : name\n}\n\n// Process a field value based on its type\nexport const processFieldValue = (\n value: string,\n fieldValueType: 'string' | 'number' | 'boolean' | 'array',\n): any => {\n if (fieldValueType === 'array') {\n try {\n if (value === '') {\n return []\n }\n // Try to parse as JSON first (for copied arrays)\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed) ? parsed : [parsed]\n } catch {\n // If not valid JSON, treat as comma-separated values\n return value.includes(',') ? value.split(',').map((v) => v.trim()) : [value]\n }\n } catch {\n // Fallback to single-item array if parsing fails\n return [value]\n }\n } else if (fieldValueType === 'number') {\n return Number(value) || 0\n } else if (fieldValueType === 'boolean') {\n return value.toLowerCase() === 'true' || value === '1' || value.toLowerCase() === 'yes'\n }\n\n // Default string handling\n return value\n}\n"],"names":[],"mappings":";AAIO,MAAM,iBAAiB,CAAC,UAAkB,MAAM,MAAM,KAAK;AAGrD,MAAA,qBAAqB,CAAC,kBAAiD;AAClF,QAAM,OAAO,cAAc,KAAK,EAAE,MAAM,IAAI;AAC5C,QAAM,aAAoC,CAAC;AAG3C,QAAM,kBAA4B,CAAC;AAG9B,OAAA,QAAQ,CAAC,QAAQ;AACd,UAAA,YAAY,IAAI,MAAM,GAAI;AAChC,eAAW,KAAK,EAAE,QAAQ,WAAW,QAAQ,iBAAiB;AAAA,EAAA,CAC/D;AAEM,SAAA;AACT;AAGO,MAAM,gBAAgB,CAC3B,UACA,UACA,YACA,aACW;AACL,QAAA,SAAS,WAAW,WAAW,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACtE,MAAA,CAAC,OAAe,QAAA;AAEd,QAAA,OAAO,OAAO,QAAQ;AAEtB,QAAA,WAAW,OAAO,YAAY,OAAO;AAGvC,MAAA,CAAC,SAAiB,QAAA;AAGtB,QAAM,aAAa,cAAc,UAAU,MAAM,YAAY,QAAQ;AAGrE,SAAO,aAAa,GAAG,UAAU,MAAM,IAAI,KAAK;AAClD;AAGa,MAAA,oBAAoB,CAC/B,OACA,mBACQ;AACR,MAAI,mBAAmB,SAAS;AAC1B,QAAA;AACF,UAAI,UAAU,IAAI;AAChB,eAAO,CAAC;AAAA,MAAA;AAGN,UAAA;AACI,cAAA,SAAS,KAAK,MAAM,KAAK;AAC/B,eAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAAA,MAAA,QACzC;AAEN,eAAO,MAAM,SAAS,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,IAAI,CAAC,KAAK;AAAA,MAAA;AAAA,IAC7E,QACM;AAEN,aAAO,CAAC,KAAK;AAAA,IAAA;AAAA,EACf,WACS,mBAAmB,UAAU;AAC/B,WAAA,OAAO,KAAK,KAAK;AAAA,EAAA,WACf,mBAAmB,WAAW;AAChC,WAAA,MAAM,kBAAkB,UAAU,UAAU,OAAO,MAAM,kBAAkB;AAAA,EAAA;AAI7E,SAAA;AACT;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BooleanWidget.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/BooleanWidget.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport styled from 'styled-components'\nimport { WidgetBaseProps } from './CellWidget'\n\nconst StyledCheckbox = styled.input`\n margin: auto;\n z-index: 1;\n cursor: pointer;\n\n width: 16px;\n height: 16px;\n background-color: transparent;\n border: 1px solid var(--md-sys-color-outline);\n appearance: none;\n border-radius: 2px;\n\n &:hover {\n border-color: hsl(212.31deg 16.83% 74.65%);\n }\n\n &:checked {\n background-color: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n appearance: auto;\n }\n`\n\
|
|
1
|
+
{"version":3,"file":"BooleanWidget.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/BooleanWidget.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport styled from 'styled-components'\nimport { WidgetBaseProps } from './CellWidget'\n\nconst StyledCheckbox = styled.input`\n margin: auto;\n z-index: 1;\n cursor: pointer;\n\n width: 16px;\n height: 16px;\n background-color: transparent;\n border: 1px solid var(--md-sys-color-outline);\n appearance: none;\n border-radius: 2px;\n\n &:hover {\n border-color: hsl(212.31deg 16.83% 74.65%);\n }\n\n &:checked {\n background-color: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n appearance: auto;\n }\n`\n\nexport interface BooleanWidgetProps\n extends Omit<React.HTMLAttributes<HTMLInputElement>, 'onChange'>,\n WidgetBaseProps {\n value: boolean\n isReadOnly?: boolean\n}\n\nexport const BooleanWidget = forwardRef<HTMLInputElement, BooleanWidgetProps>(\n ({ value, onChange, isReadOnly, isEditing, onCancelEdit, ...props }, ref) => {\n return (\n <StyledCheckbox\n {...props}\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n ref={ref}\n type=\"checkbox\"\n disabled={isReadOnly}\n readOnly={isReadOnly}\n />\n )\n },\n)\n"],"names":["forwardRef","jsx"],"mappings":";;;;;AAIA,MAAM,iBAAiB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BvB,MAAM,gBAAgBA,MAAA;AAAA,EAC3B,CAAC,EAAE,OAAO,UAAU,YAAY,WAAW,cAAc,GAAG,MAAM,GAAG,QAAQ;AAEzE,WAAAC,2BAAA,kBAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACE,GAAG;AAAA,QACJ,SAAS;AAAA,QACT,UAAU,CAAC,MAAM,SAAU,EAAE,OAA4B,OAAO;AAAA,QAChE;AAAA,QACA,MAAK;AAAA,QACL,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,EAAA;AAGN;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BooleanWidget.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/BooleanWidget.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport styled from 'styled-components'\nimport { WidgetBaseProps } from './CellWidget'\n\nconst StyledCheckbox = styled.input`\n margin: auto;\n z-index: 1;\n cursor: pointer;\n\n width: 16px;\n height: 16px;\n background-color: transparent;\n border: 1px solid var(--md-sys-color-outline);\n appearance: none;\n border-radius: 2px;\n\n &:hover {\n border-color: hsl(212.31deg 16.83% 74.65%);\n }\n\n &:checked {\n background-color: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n appearance: auto;\n }\n`\n\
|
|
1
|
+
{"version":3,"file":"BooleanWidget.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/BooleanWidget.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport styled from 'styled-components'\nimport { WidgetBaseProps } from './CellWidget'\n\nconst StyledCheckbox = styled.input`\n margin: auto;\n z-index: 1;\n cursor: pointer;\n\n width: 16px;\n height: 16px;\n background-color: transparent;\n border: 1px solid var(--md-sys-color-outline);\n appearance: none;\n border-radius: 2px;\n\n &:hover {\n border-color: hsl(212.31deg 16.83% 74.65%);\n }\n\n &:checked {\n background-color: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n appearance: auto;\n }\n`\n\nexport interface BooleanWidgetProps\n extends Omit<React.HTMLAttributes<HTMLInputElement>, 'onChange'>,\n WidgetBaseProps {\n value: boolean\n isReadOnly?: boolean\n}\n\nexport const BooleanWidget = forwardRef<HTMLInputElement, BooleanWidgetProps>(\n ({ value, onChange, isReadOnly, isEditing, onCancelEdit, ...props }, ref) => {\n return (\n <StyledCheckbox\n {...props}\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n ref={ref}\n type=\"checkbox\"\n disabled={isReadOnly}\n readOnly={isReadOnly}\n />\n )\n },\n)\n"],"names":["jsx"],"mappings":";;;AAIA,MAAM,iBAAiB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BvB,MAAM,gBAAgB;AAAA,EAC3B,CAAC,EAAE,OAAO,UAAU,YAAY,WAAW,cAAc,GAAG,MAAM,GAAG,QAAQ;AAEzE,WAAAA,kCAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACE,GAAG;AAAA,QACJ,SAAS;AAAA,QACT,UAAU,CAAC,MAAM,SAAU,EAAE,OAA4B,OAAO;AAAA,QAChE;AAAA,QACA,MAAK;AAAA,QACL,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,EAAA;AAGN;"}
|
|
@@ -46,6 +46,7 @@ const EditorCellComponent = ({
|
|
|
46
46
|
isReadOnly,
|
|
47
47
|
enableCustomValues,
|
|
48
48
|
onChange,
|
|
49
|
+
pt,
|
|
49
50
|
...props
|
|
50
51
|
}) => {
|
|
51
52
|
const ref = React.useRef(null);
|
|
@@ -112,20 +113,37 @@ const EditorCellComponent = ({
|
|
|
112
113
|
type,
|
|
113
114
|
onOpen: () => !isReadOnly && setEditingCellId(cellId),
|
|
114
115
|
enableCustomValues,
|
|
115
|
-
...sharedProps
|
|
116
|
+
...sharedProps,
|
|
117
|
+
...pt == null ? void 0 : pt.enum
|
|
116
118
|
}
|
|
117
119
|
);
|
|
118
120
|
}
|
|
119
121
|
case textTypes.includes(type):
|
|
120
|
-
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(
|
|
123
|
+
TextWidget.TextWidget,
|
|
124
|
+
{
|
|
125
|
+
value,
|
|
126
|
+
isInherited,
|
|
127
|
+
...sharedProps,
|
|
128
|
+
...pt == null ? void 0 : pt.text
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
case type === "datetime":
|
|
132
|
+
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(
|
|
133
|
+
DateWidget.DateWidget,
|
|
134
|
+
{
|
|
135
|
+
value: value ? value : void 0,
|
|
136
|
+
isInherited,
|
|
137
|
+
...sharedProps,
|
|
138
|
+
...pt == null ? void 0 : pt.date
|
|
139
|
+
}
|
|
140
|
+
);
|
|
123
141
|
case type === "boolean":
|
|
124
|
-
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(BooleanWidget.BooleanWidget, { value, ...sharedProps });
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(BooleanWidget.BooleanWidget, { value, ...sharedProps, ...pt == null ? void 0 : pt.boolean });
|
|
125
143
|
case isPlaceholder:
|
|
126
144
|
return null;
|
|
127
145
|
default:
|
|
128
|
-
return
|
|
146
|
+
return null;
|
|
129
147
|
}
|
|
130
148
|
}, [cellId, value, type, isCurrentCellEditing, options, isCollapsed]);
|
|
131
149
|
return /* @__PURE__ */ jsxRuntime.jsxRuntimeExports.jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CellWidget.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/CellWidget.tsx"],"sourcesContent":["import { useMemo, memo, useCallback, useRef, FC } from 'react'\nimport styled from 'styled-components'\n\n// Widgets\nimport { BooleanWidget } from './BooleanWidget'\nimport { CollapsedWidget } from './CollapsedWidget'\nimport { DateWidget } from './DateWidget'\nimport { EnumWidget } from './EnumWidget'\nimport { TextWidget, TextWidgetType } from './TextWidget'\n\n// Contexts\nimport { useCellEditing } from '../context/CellEditingContext'\n\n// Utils\nimport { getCellId } from '../utils/cellUtils'\nimport clsx from 'clsx'\nimport { useSelectionCellsContext } from '../context/SelectionCellsContext'\nimport { AttributeData, AttributeEnumItem } from '../types'\n\nconst Cell = styled.div`\n position: absolute;\n inset: 0;\n padding: 4px 8px;\n display: flex;\n align-items: center;\n\n &:focus-visible {\n outline: none;\n }\n\n &.inherited {\n opacity: 0.6;\n font-style: italic;\n }\n\n &.loading {\n inset: 4px;\n border-radius: 4px;\n opacity: 1;\n }\n`\n\ntype WidgetAttributeData = Pick<AttributeData, 'type'>\n\nexport type CellValue = string | number | boolean\n\ninterface EditorCellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n rowId: string\n columnId: string\n value: CellValue | CellValue[]\n attributeData?: WidgetAttributeData\n options?: AttributeEnumItem[]\n isCollapsed?: boolean\n isInherited?: boolean\n isPlaceholder?: boolean\n isFocused?: boolean\n isReadOnly?: boolean\n enableCustomValues?: boolean\n onChange?: (value: CellValue | CellValue[], key?: 'Enter' | 'Click' | 'Escape') => void\n}\n\nexport interface WidgetBaseProps {\n isEditing?: boolean\n onChange: Required<EditorCellProps>['onChange']\n onCancelEdit?: () => void\n}\n\nconst EditorCellComponent: FC<EditorCellProps> = ({\n rowId,\n columnId,\n value,\n attributeData,\n options = [],\n isCollapsed,\n isInherited,\n isPlaceholder,\n isReadOnly,\n enableCustomValues,\n onChange,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const type = attributeData?.type\n\n const { isEditing, setEditingCellId } = useCellEditing()\n const { isCellFocused, gridMap, selectCell, focusCell } = useSelectionCellsContext()\n const cellId = getCellId(rowId, columnId)\n\n const isCurrentCellEditing = isEditing(cellId)\n const isCurrentCellFocused = isCellFocused(cellId)\n\n const handleDoubleClick = useCallback(() => {\n if (isPlaceholder || isReadOnly) return\n setEditingCellId(cellId)\n }, [cellId, setEditingCellId, isPlaceholder])\n\n const handleSingleClick = () => {\n // clicking a cell that is not editing will close the editor on this cell\n if (!isCurrentCellEditing) {\n setEditingCellId(null)\n }\n }\n\n const moveToNextRow = () => {\n const rowIndex = gridMap.rowIdToIndex.get(rowId)\n if (rowIndex === undefined) return\n const newRowId = gridMap.indexToRowId.get(rowIndex + 1)\n if (newRowId) {\n const newCellId = getCellId(newRowId, columnId)\n selectCell(newCellId, false, false)\n focusCell(newCellId)\n setEditingCellId(newCellId)\n }\n }\n\n const handleOnChange: WidgetBaseProps['onChange'] = (newValue, key) => {\n setEditingCellId(null)\n if (isReadOnly) return\n // move to the next cell row\n key === 'Enter' && moveToNextRow()\n // make change if the value is different or if the key is 'Enter'\n if (newValue !== value || key === 'Enter') {\n onChange?.(newValue, key)\n }\n }\n\n const handleCancel = () => {\n setEditingCellId(null)\n }\n\n const widget = useMemo(() => {\n // Common props shared across all widgets\n const sharedProps: WidgetBaseProps = {\n onChange: handleOnChange,\n onCancelEdit: handleCancel,\n isEditing: isCurrentCellEditing,\n }\n\n const textTypes: TextWidgetType[] = ['string', 'integer', 'float']\n\n // Determine widget type based on attribute type\n switch (true) {\n // this is showing the collapsed widget (dot)\n case isCollapsed: {\n // if enum, find the first selected option and get its color\n const firstSelectedOption = type?.includes('list')\n ? options.find((option) =>\n Array.isArray(value) ? value.includes(option.value) : value === option.value,\n )\n : undefined\n const color = firstSelectedOption?.color\n return <CollapsedWidget color={color} />\n }\n\n case !!options.length: {\n const enumValue = Array.isArray(value) ? value : [value]\n return (\n <EnumWidget\n value={enumValue}\n options={options}\n type={type}\n onOpen={() => !isReadOnly && setEditingCellId(cellId)}\n enableCustomValues={enableCustomValues}\n {...sharedProps}\n />\n )\n }\n\n case textTypes.includes(type as TextWidgetType):\n return <TextWidget value={value as string} isInherited={isInherited} {...sharedProps} />\n\n case type === 'datetime' && value !== null && value !== undefined:\n return <DateWidget value={value as string} isInherited={isInherited} {...sharedProps} />\n\n case type === 'boolean':\n return <BooleanWidget value={value as boolean} {...sharedProps} />\n\n case isPlaceholder:\n return null\n\n default:\n // if the type is not recognized, fall back to the TextWidget\n return <TextWidget value={value as string} {...sharedProps} />\n }\n }, [cellId, value, type, isCurrentCellEditing, options, isCollapsed])\n\n return (\n <Cell\n {...props}\n className={clsx(props.className, { inherited: isInherited && !isCurrentCellEditing })}\n ref={ref}\n onDoubleClick={handleDoubleClick}\n onClick={handleSingleClick}\n id={cellId}\n data-tooltip={\n isInherited && !isCurrentCellEditing && isCurrentCellFocused ? 'Inherited' : undefined\n }\n data-tooltip-delay={200}\n >\n {widget}\n </Cell>\n )\n}\n\n// Custom comparison function for memo\nfunction arePropsEqual(prevProps: EditorCellProps, nextProps: EditorCellProps) {\n // Only re-render if these props change\n return (\n prevProps.rowId === nextProps.rowId &&\n prevProps.columnId === nextProps.columnId &&\n prevProps.isCollapsed === nextProps.isCollapsed &&\n JSON.stringify(prevProps.value) === JSON.stringify(nextProps.value) &&\n prevProps?.attributeData?.type === nextProps?.attributeData?.type &&\n // Only check options length for list types to avoid deep comparison\n ((!prevProps?.attributeData?.type.includes('list') &&\n !nextProps?.attributeData?.type.includes('list')) ||\n prevProps.options?.length === nextProps.options?.length) &&\n prevProps.isInherited === nextProps.isInherited &&\n prevProps.enableCustomValues === nextProps.enableCustomValues &&\n prevProps.isReadOnly === nextProps.isReadOnly &&\n prevProps.isPlaceholder === nextProps.isPlaceholder &&\n prevProps.isFocused === nextProps.isFocused &&\n prevProps.isCollapsed === nextProps.isCollapsed\n )\n}\n\nexport const CellWidget = memo(EditorCellComponent, arePropsEqual)\n"],"names":["useRef","useCellEditing","useSelectionCellsContext","getCellId","useCallback","useMemo","jsx","CollapsedWidget","EnumWidget","TextWidget","DateWidget","BooleanWidget","memo"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CellWidget.cjs.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/CellWidget.tsx"],"sourcesContent":["import { useMemo, memo, useCallback, useRef, FC } from 'react'\nimport styled from 'styled-components'\n\n// Widgets\nimport { BooleanWidget, BooleanWidgetProps } from './BooleanWidget'\nimport { CollapsedWidget } from './CollapsedWidget'\nimport { DateWidget, DateWidgetProps } from './DateWidget'\nimport { EnumWidget, EnumWidgetProps } from './EnumWidget'\nimport { TextWidget, TextWidgetProps, TextWidgetType } from './TextWidget'\n\n// Contexts\nimport { useCellEditing } from '../context/CellEditingContext'\n\n// Utils\nimport { getCellId } from '../utils/cellUtils'\nimport clsx from 'clsx'\nimport { useSelectionCellsContext } from '../context/SelectionCellsContext'\nimport { AttributeData, AttributeEnumItem } from '../types'\n\nconst Cell = styled.div`\n position: absolute;\n inset: 0;\n padding: 4px 8px;\n display: flex;\n align-items: center;\n\n &:focus-visible {\n outline: none;\n }\n\n &.inherited {\n opacity: 0.6;\n font-style: italic;\n }\n\n &.loading {\n inset: 4px;\n border-radius: 4px;\n opacity: 1;\n }\n`\n\ntype WidgetAttributeData = Pick<AttributeData, 'type'>\n\nexport type CellValue = string | number | boolean\n\ninterface EditorCellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n rowId: string\n columnId: string\n value: CellValue | CellValue[]\n attributeData?: WidgetAttributeData\n options?: AttributeEnumItem[]\n isCollapsed?: boolean\n isInherited?: boolean\n isPlaceholder?: boolean\n isFocused?: boolean\n isReadOnly?: boolean\n enableCustomValues?: boolean\n onChange?: (value: CellValue | CellValue[], key?: 'Enter' | 'Click' | 'Escape') => void\n // options passthrough props\n pt?: {\n enum?: Partial<EnumWidgetProps>\n text?: Partial<TextWidgetProps>\n date?: Partial<DateWidgetProps>\n boolean?: Partial<BooleanWidgetProps>\n }\n}\n\nexport interface WidgetBaseProps {\n isEditing?: boolean\n onChange: Required<EditorCellProps>['onChange']\n onCancelEdit?: () => void\n}\n\nconst EditorCellComponent: FC<EditorCellProps> = ({\n rowId,\n columnId,\n value,\n attributeData,\n options = [],\n isCollapsed,\n isInherited,\n isPlaceholder,\n isReadOnly,\n enableCustomValues,\n onChange,\n pt,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const type = attributeData?.type\n\n const { isEditing, setEditingCellId } = useCellEditing()\n const { isCellFocused, gridMap, selectCell, focusCell } = useSelectionCellsContext()\n const cellId = getCellId(rowId, columnId)\n\n const isCurrentCellEditing = isEditing(cellId)\n const isCurrentCellFocused = isCellFocused(cellId)\n\n const handleDoubleClick = useCallback(() => {\n if (isPlaceholder || isReadOnly) return\n setEditingCellId(cellId)\n }, [cellId, setEditingCellId, isPlaceholder])\n\n const handleSingleClick = () => {\n // clicking a cell that is not editing will close the editor on this cell\n if (!isCurrentCellEditing) {\n setEditingCellId(null)\n }\n }\n\n const moveToNextRow = () => {\n const rowIndex = gridMap.rowIdToIndex.get(rowId)\n if (rowIndex === undefined) return\n const newRowId = gridMap.indexToRowId.get(rowIndex + 1)\n if (newRowId) {\n const newCellId = getCellId(newRowId, columnId)\n selectCell(newCellId, false, false)\n focusCell(newCellId)\n setEditingCellId(newCellId)\n }\n }\n\n const handleOnChange: WidgetBaseProps['onChange'] = (newValue, key) => {\n setEditingCellId(null)\n if (isReadOnly) return\n // move to the next cell row\n key === 'Enter' && moveToNextRow()\n // make change if the value is different or if the key is 'Enter'\n if (newValue !== value || key === 'Enter') {\n onChange?.(newValue, key)\n }\n }\n\n const handleCancel = () => {\n setEditingCellId(null)\n }\n\n const widget = useMemo(() => {\n // Common props shared across all widgets\n const sharedProps: WidgetBaseProps = {\n onChange: handleOnChange,\n onCancelEdit: handleCancel,\n isEditing: isCurrentCellEditing,\n }\n\n const textTypes: TextWidgetType[] = ['string', 'integer', 'float']\n\n // Determine widget type based on attribute type\n switch (true) {\n // this is showing the collapsed widget (dot)\n case isCollapsed: {\n // if enum, find the first selected option and get its color\n const firstSelectedOption = type?.includes('list')\n ? options.find((option) =>\n Array.isArray(value) ? value.includes(option.value) : value === option.value,\n )\n : undefined\n const color = firstSelectedOption?.color\n return <CollapsedWidget color={color} />\n }\n\n case !!options.length: {\n const enumValue = Array.isArray(value) ? value : [value]\n return (\n <EnumWidget\n value={enumValue}\n options={options}\n type={type}\n onOpen={() => !isReadOnly && setEditingCellId(cellId)}\n enableCustomValues={enableCustomValues}\n {...sharedProps}\n {...pt?.enum}\n />\n )\n }\n\n case textTypes.includes(type as TextWidgetType):\n return (\n <TextWidget\n value={value as string}\n isInherited={isInherited}\n {...sharedProps}\n {...pt?.text}\n />\n )\n\n case type === 'datetime':\n return (\n <DateWidget\n value={value ? (value as string) : undefined}\n isInherited={isInherited}\n {...sharedProps}\n {...pt?.date}\n />\n )\n\n case type === 'boolean':\n return <BooleanWidget value={value as boolean} {...sharedProps} {...pt?.boolean} />\n\n case isPlaceholder:\n return null\n\n default:\n // TODO: We should not allow editing unrecognized types\n // At this point, only list_of_strings without proper options is unrecognized\n // (tags if not tags are specified in anatomy) and in that case, validation\n // on the server fails with a string value. Unless we have a widget that\n // accepts a string value AND options at the same time we shouldn't show\n // any edit widget\n\n //console.log(`Unrecognized type \"${type}\" for cell ${cellId}.`)\n return null\n }\n }, [cellId, value, type, isCurrentCellEditing, options, isCollapsed])\n\n return (\n <Cell\n {...props}\n className={clsx(props.className, { inherited: isInherited && !isCurrentCellEditing })}\n ref={ref}\n onDoubleClick={handleDoubleClick}\n onClick={handleSingleClick}\n id={cellId}\n data-tooltip={\n isInherited && !isCurrentCellEditing && isCurrentCellFocused ? 'Inherited' : undefined\n }\n data-tooltip-delay={200}\n >\n {widget}\n </Cell>\n )\n}\n\n// Custom comparison function for memo\nfunction arePropsEqual(prevProps: EditorCellProps, nextProps: EditorCellProps) {\n // Only re-render if these props change\n return (\n prevProps.rowId === nextProps.rowId &&\n prevProps.columnId === nextProps.columnId &&\n prevProps.isCollapsed === nextProps.isCollapsed &&\n JSON.stringify(prevProps.value) === JSON.stringify(nextProps.value) &&\n prevProps?.attributeData?.type === nextProps?.attributeData?.type &&\n // Only check options length for list types to avoid deep comparison\n ((!prevProps?.attributeData?.type.includes('list') &&\n !nextProps?.attributeData?.type.includes('list')) ||\n prevProps.options?.length === nextProps.options?.length) &&\n prevProps.isInherited === nextProps.isInherited &&\n prevProps.enableCustomValues === nextProps.enableCustomValues &&\n prevProps.isReadOnly === nextProps.isReadOnly &&\n prevProps.isPlaceholder === nextProps.isPlaceholder &&\n prevProps.isFocused === nextProps.isFocused &&\n prevProps.isCollapsed === nextProps.isCollapsed\n )\n}\n\nexport const CellWidget = memo(EditorCellComponent, arePropsEqual)\n"],"names":["useRef","useCellEditing","useSelectionCellsContext","getCellId","useCallback","useMemo","jsx","CollapsedWidget","EnumWidget","TextWidget","DateWidget","BooleanWidget","memo"],"mappings":"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"}
|
|
@@ -44,6 +44,7 @@ const EditorCellComponent = ({
|
|
|
44
44
|
isReadOnly,
|
|
45
45
|
enableCustomValues,
|
|
46
46
|
onChange,
|
|
47
|
+
pt,
|
|
47
48
|
...props
|
|
48
49
|
}) => {
|
|
49
50
|
const ref = useRef(null);
|
|
@@ -110,20 +111,37 @@ const EditorCellComponent = ({
|
|
|
110
111
|
type,
|
|
111
112
|
onOpen: () => !isReadOnly && setEditingCellId(cellId),
|
|
112
113
|
enableCustomValues,
|
|
113
|
-
...sharedProps
|
|
114
|
+
...sharedProps,
|
|
115
|
+
...pt == null ? void 0 : pt.enum
|
|
114
116
|
}
|
|
115
117
|
);
|
|
116
118
|
}
|
|
117
119
|
case textTypes.includes(type):
|
|
118
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
121
|
+
TextWidget,
|
|
122
|
+
{
|
|
123
|
+
value,
|
|
124
|
+
isInherited,
|
|
125
|
+
...sharedProps,
|
|
126
|
+
...pt == null ? void 0 : pt.text
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
case type === "datetime":
|
|
130
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
131
|
+
DateWidget,
|
|
132
|
+
{
|
|
133
|
+
value: value ? value : void 0,
|
|
134
|
+
isInherited,
|
|
135
|
+
...sharedProps,
|
|
136
|
+
...pt == null ? void 0 : pt.date
|
|
137
|
+
}
|
|
138
|
+
);
|
|
121
139
|
case type === "boolean":
|
|
122
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(BooleanWidget, { value, ...sharedProps });
|
|
140
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(BooleanWidget, { value, ...sharedProps, ...pt == null ? void 0 : pt.boolean });
|
|
123
141
|
case isPlaceholder:
|
|
124
142
|
return null;
|
|
125
143
|
default:
|
|
126
|
-
return
|
|
144
|
+
return null;
|
|
127
145
|
}
|
|
128
146
|
}, [cellId, value, type, isCurrentCellEditing, options, isCollapsed]);
|
|
129
147
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CellWidget.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/CellWidget.tsx"],"sourcesContent":["import { useMemo, memo, useCallback, useRef, FC } from 'react'\nimport styled from 'styled-components'\n\n// Widgets\nimport { BooleanWidget } from './BooleanWidget'\nimport { CollapsedWidget } from './CollapsedWidget'\nimport { DateWidget } from './DateWidget'\nimport { EnumWidget } from './EnumWidget'\nimport { TextWidget, TextWidgetType } from './TextWidget'\n\n// Contexts\nimport { useCellEditing } from '../context/CellEditingContext'\n\n// Utils\nimport { getCellId } from '../utils/cellUtils'\nimport clsx from 'clsx'\nimport { useSelectionCellsContext } from '../context/SelectionCellsContext'\nimport { AttributeData, AttributeEnumItem } from '../types'\n\nconst Cell = styled.div`\n position: absolute;\n inset: 0;\n padding: 4px 8px;\n display: flex;\n align-items: center;\n\n &:focus-visible {\n outline: none;\n }\n\n &.inherited {\n opacity: 0.6;\n font-style: italic;\n }\n\n &.loading {\n inset: 4px;\n border-radius: 4px;\n opacity: 1;\n }\n`\n\ntype WidgetAttributeData = Pick<AttributeData, 'type'>\n\nexport type CellValue = string | number | boolean\n\ninterface EditorCellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n rowId: string\n columnId: string\n value: CellValue | CellValue[]\n attributeData?: WidgetAttributeData\n options?: AttributeEnumItem[]\n isCollapsed?: boolean\n isInherited?: boolean\n isPlaceholder?: boolean\n isFocused?: boolean\n isReadOnly?: boolean\n enableCustomValues?: boolean\n onChange?: (value: CellValue | CellValue[], key?: 'Enter' | 'Click' | 'Escape') => void\n}\n\nexport interface WidgetBaseProps {\n isEditing?: boolean\n onChange: Required<EditorCellProps>['onChange']\n onCancelEdit?: () => void\n}\n\nconst EditorCellComponent: FC<EditorCellProps> = ({\n rowId,\n columnId,\n value,\n attributeData,\n options = [],\n isCollapsed,\n isInherited,\n isPlaceholder,\n isReadOnly,\n enableCustomValues,\n onChange,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const type = attributeData?.type\n\n const { isEditing, setEditingCellId } = useCellEditing()\n const { isCellFocused, gridMap, selectCell, focusCell } = useSelectionCellsContext()\n const cellId = getCellId(rowId, columnId)\n\n const isCurrentCellEditing = isEditing(cellId)\n const isCurrentCellFocused = isCellFocused(cellId)\n\n const handleDoubleClick = useCallback(() => {\n if (isPlaceholder || isReadOnly) return\n setEditingCellId(cellId)\n }, [cellId, setEditingCellId, isPlaceholder])\n\n const handleSingleClick = () => {\n // clicking a cell that is not editing will close the editor on this cell\n if (!isCurrentCellEditing) {\n setEditingCellId(null)\n }\n }\n\n const moveToNextRow = () => {\n const rowIndex = gridMap.rowIdToIndex.get(rowId)\n if (rowIndex === undefined) return\n const newRowId = gridMap.indexToRowId.get(rowIndex + 1)\n if (newRowId) {\n const newCellId = getCellId(newRowId, columnId)\n selectCell(newCellId, false, false)\n focusCell(newCellId)\n setEditingCellId(newCellId)\n }\n }\n\n const handleOnChange: WidgetBaseProps['onChange'] = (newValue, key) => {\n setEditingCellId(null)\n if (isReadOnly) return\n // move to the next cell row\n key === 'Enter' && moveToNextRow()\n // make change if the value is different or if the key is 'Enter'\n if (newValue !== value || key === 'Enter') {\n onChange?.(newValue, key)\n }\n }\n\n const handleCancel = () => {\n setEditingCellId(null)\n }\n\n const widget = useMemo(() => {\n // Common props shared across all widgets\n const sharedProps: WidgetBaseProps = {\n onChange: handleOnChange,\n onCancelEdit: handleCancel,\n isEditing: isCurrentCellEditing,\n }\n\n const textTypes: TextWidgetType[] = ['string', 'integer', 'float']\n\n // Determine widget type based on attribute type\n switch (true) {\n // this is showing the collapsed widget (dot)\n case isCollapsed: {\n // if enum, find the first selected option and get its color\n const firstSelectedOption = type?.includes('list')\n ? options.find((option) =>\n Array.isArray(value) ? value.includes(option.value) : value === option.value,\n )\n : undefined\n const color = firstSelectedOption?.color\n return <CollapsedWidget color={color} />\n }\n\n case !!options.length: {\n const enumValue = Array.isArray(value) ? value : [value]\n return (\n <EnumWidget\n value={enumValue}\n options={options}\n type={type}\n onOpen={() => !isReadOnly && setEditingCellId(cellId)}\n enableCustomValues={enableCustomValues}\n {...sharedProps}\n />\n )\n }\n\n case textTypes.includes(type as TextWidgetType):\n return <TextWidget value={value as string} isInherited={isInherited} {...sharedProps} />\n\n case type === 'datetime' && value !== null && value !== undefined:\n return <DateWidget value={value as string} isInherited={isInherited} {...sharedProps} />\n\n case type === 'boolean':\n return <BooleanWidget value={value as boolean} {...sharedProps} />\n\n case isPlaceholder:\n return null\n\n default:\n // if the type is not recognized, fall back to the TextWidget\n return <TextWidget value={value as string} {...sharedProps} />\n }\n }, [cellId, value, type, isCurrentCellEditing, options, isCollapsed])\n\n return (\n <Cell\n {...props}\n className={clsx(props.className, { inherited: isInherited && !isCurrentCellEditing })}\n ref={ref}\n onDoubleClick={handleDoubleClick}\n onClick={handleSingleClick}\n id={cellId}\n data-tooltip={\n isInherited && !isCurrentCellEditing && isCurrentCellFocused ? 'Inherited' : undefined\n }\n data-tooltip-delay={200}\n >\n {widget}\n </Cell>\n )\n}\n\n// Custom comparison function for memo\nfunction arePropsEqual(prevProps: EditorCellProps, nextProps: EditorCellProps) {\n // Only re-render if these props change\n return (\n prevProps.rowId === nextProps.rowId &&\n prevProps.columnId === nextProps.columnId &&\n prevProps.isCollapsed === nextProps.isCollapsed &&\n JSON.stringify(prevProps.value) === JSON.stringify(nextProps.value) &&\n prevProps?.attributeData?.type === nextProps?.attributeData?.type &&\n // Only check options length for list types to avoid deep comparison\n ((!prevProps?.attributeData?.type.includes('list') &&\n !nextProps?.attributeData?.type.includes('list')) ||\n prevProps.options?.length === nextProps.options?.length) &&\n prevProps.isInherited === nextProps.isInherited &&\n prevProps.enableCustomValues === nextProps.enableCustomValues &&\n prevProps.isReadOnly === nextProps.isReadOnly &&\n prevProps.isPlaceholder === nextProps.isPlaceholder &&\n prevProps.isFocused === nextProps.isFocused &&\n prevProps.isCollapsed === nextProps.isCollapsed\n )\n}\n\nexport const CellWidget = memo(EditorCellComponent, arePropsEqual)\n"],"names":["jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CellWidget.es.js","sources":["../../../../../../src/containers/ProjectTreeTable/widgets/CellWidget.tsx"],"sourcesContent":["import { useMemo, memo, useCallback, useRef, FC } from 'react'\nimport styled from 'styled-components'\n\n// Widgets\nimport { BooleanWidget, BooleanWidgetProps } from './BooleanWidget'\nimport { CollapsedWidget } from './CollapsedWidget'\nimport { DateWidget, DateWidgetProps } from './DateWidget'\nimport { EnumWidget, EnumWidgetProps } from './EnumWidget'\nimport { TextWidget, TextWidgetProps, TextWidgetType } from './TextWidget'\n\n// Contexts\nimport { useCellEditing } from '../context/CellEditingContext'\n\n// Utils\nimport { getCellId } from '../utils/cellUtils'\nimport clsx from 'clsx'\nimport { useSelectionCellsContext } from '../context/SelectionCellsContext'\nimport { AttributeData, AttributeEnumItem } from '../types'\n\nconst Cell = styled.div`\n position: absolute;\n inset: 0;\n padding: 4px 8px;\n display: flex;\n align-items: center;\n\n &:focus-visible {\n outline: none;\n }\n\n &.inherited {\n opacity: 0.6;\n font-style: italic;\n }\n\n &.loading {\n inset: 4px;\n border-radius: 4px;\n opacity: 1;\n }\n`\n\ntype WidgetAttributeData = Pick<AttributeData, 'type'>\n\nexport type CellValue = string | number | boolean\n\ninterface EditorCellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n rowId: string\n columnId: string\n value: CellValue | CellValue[]\n attributeData?: WidgetAttributeData\n options?: AttributeEnumItem[]\n isCollapsed?: boolean\n isInherited?: boolean\n isPlaceholder?: boolean\n isFocused?: boolean\n isReadOnly?: boolean\n enableCustomValues?: boolean\n onChange?: (value: CellValue | CellValue[], key?: 'Enter' | 'Click' | 'Escape') => void\n // options passthrough props\n pt?: {\n enum?: Partial<EnumWidgetProps>\n text?: Partial<TextWidgetProps>\n date?: Partial<DateWidgetProps>\n boolean?: Partial<BooleanWidgetProps>\n }\n}\n\nexport interface WidgetBaseProps {\n isEditing?: boolean\n onChange: Required<EditorCellProps>['onChange']\n onCancelEdit?: () => void\n}\n\nconst EditorCellComponent: FC<EditorCellProps> = ({\n rowId,\n columnId,\n value,\n attributeData,\n options = [],\n isCollapsed,\n isInherited,\n isPlaceholder,\n isReadOnly,\n enableCustomValues,\n onChange,\n pt,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const type = attributeData?.type\n\n const { isEditing, setEditingCellId } = useCellEditing()\n const { isCellFocused, gridMap, selectCell, focusCell } = useSelectionCellsContext()\n const cellId = getCellId(rowId, columnId)\n\n const isCurrentCellEditing = isEditing(cellId)\n const isCurrentCellFocused = isCellFocused(cellId)\n\n const handleDoubleClick = useCallback(() => {\n if (isPlaceholder || isReadOnly) return\n setEditingCellId(cellId)\n }, [cellId, setEditingCellId, isPlaceholder])\n\n const handleSingleClick = () => {\n // clicking a cell that is not editing will close the editor on this cell\n if (!isCurrentCellEditing) {\n setEditingCellId(null)\n }\n }\n\n const moveToNextRow = () => {\n const rowIndex = gridMap.rowIdToIndex.get(rowId)\n if (rowIndex === undefined) return\n const newRowId = gridMap.indexToRowId.get(rowIndex + 1)\n if (newRowId) {\n const newCellId = getCellId(newRowId, columnId)\n selectCell(newCellId, false, false)\n focusCell(newCellId)\n setEditingCellId(newCellId)\n }\n }\n\n const handleOnChange: WidgetBaseProps['onChange'] = (newValue, key) => {\n setEditingCellId(null)\n if (isReadOnly) return\n // move to the next cell row\n key === 'Enter' && moveToNextRow()\n // make change if the value is different or if the key is 'Enter'\n if (newValue !== value || key === 'Enter') {\n onChange?.(newValue, key)\n }\n }\n\n const handleCancel = () => {\n setEditingCellId(null)\n }\n\n const widget = useMemo(() => {\n // Common props shared across all widgets\n const sharedProps: WidgetBaseProps = {\n onChange: handleOnChange,\n onCancelEdit: handleCancel,\n isEditing: isCurrentCellEditing,\n }\n\n const textTypes: TextWidgetType[] = ['string', 'integer', 'float']\n\n // Determine widget type based on attribute type\n switch (true) {\n // this is showing the collapsed widget (dot)\n case isCollapsed: {\n // if enum, find the first selected option and get its color\n const firstSelectedOption = type?.includes('list')\n ? options.find((option) =>\n Array.isArray(value) ? value.includes(option.value) : value === option.value,\n )\n : undefined\n const color = firstSelectedOption?.color\n return <CollapsedWidget color={color} />\n }\n\n case !!options.length: {\n const enumValue = Array.isArray(value) ? value : [value]\n return (\n <EnumWidget\n value={enumValue}\n options={options}\n type={type}\n onOpen={() => !isReadOnly && setEditingCellId(cellId)}\n enableCustomValues={enableCustomValues}\n {...sharedProps}\n {...pt?.enum}\n />\n )\n }\n\n case textTypes.includes(type as TextWidgetType):\n return (\n <TextWidget\n value={value as string}\n isInherited={isInherited}\n {...sharedProps}\n {...pt?.text}\n />\n )\n\n case type === 'datetime':\n return (\n <DateWidget\n value={value ? (value as string) : undefined}\n isInherited={isInherited}\n {...sharedProps}\n {...pt?.date}\n />\n )\n\n case type === 'boolean':\n return <BooleanWidget value={value as boolean} {...sharedProps} {...pt?.boolean} />\n\n case isPlaceholder:\n return null\n\n default:\n // TODO: We should not allow editing unrecognized types\n // At this point, only list_of_strings without proper options is unrecognized\n // (tags if not tags are specified in anatomy) and in that case, validation\n // on the server fails with a string value. Unless we have a widget that\n // accepts a string value AND options at the same time we shouldn't show\n // any edit widget\n\n //console.log(`Unrecognized type \"${type}\" for cell ${cellId}.`)\n return null\n }\n }, [cellId, value, type, isCurrentCellEditing, options, isCollapsed])\n\n return (\n <Cell\n {...props}\n className={clsx(props.className, { inherited: isInherited && !isCurrentCellEditing })}\n ref={ref}\n onDoubleClick={handleDoubleClick}\n onClick={handleSingleClick}\n id={cellId}\n data-tooltip={\n isInherited && !isCurrentCellEditing && isCurrentCellFocused ? 'Inherited' : undefined\n }\n data-tooltip-delay={200}\n >\n {widget}\n </Cell>\n )\n}\n\n// Custom comparison function for memo\nfunction arePropsEqual(prevProps: EditorCellProps, nextProps: EditorCellProps) {\n // Only re-render if these props change\n return (\n prevProps.rowId === nextProps.rowId &&\n prevProps.columnId === nextProps.columnId &&\n prevProps.isCollapsed === nextProps.isCollapsed &&\n JSON.stringify(prevProps.value) === JSON.stringify(nextProps.value) &&\n prevProps?.attributeData?.type === nextProps?.attributeData?.type &&\n // Only check options length for list types to avoid deep comparison\n ((!prevProps?.attributeData?.type.includes('list') &&\n !nextProps?.attributeData?.type.includes('list')) ||\n prevProps.options?.length === nextProps.options?.length) &&\n prevProps.isInherited === nextProps.isInherited &&\n prevProps.enableCustomValues === nextProps.enableCustomValues &&\n prevProps.isReadOnly === nextProps.isReadOnly &&\n prevProps.isPlaceholder === nextProps.isPlaceholder &&\n prevProps.isFocused === nextProps.isFocused &&\n prevProps.isCollapsed === nextProps.isCollapsed\n )\n}\n\nexport const CellWidget = memo(EditorCellComponent, arePropsEqual)\n"],"names":["jsx"],"mappings":"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"}
|