From 6b0d57cb4b5ade898224ea97e92b581aa64f105a Mon Sep 17 00:00:00 2001 From: Anisha Pant Date: Tue, 25 Jun 2024 16:12:17 -0400 Subject: [PATCH 01/24] Shift select and multiple node move at the same time --- src/components/AnchorMenu/AnchorMenu.css | 7 ++ src/components/AnchorMenu/AnchorMenu.tsx | 50 ++++++++++++-- src/store/treeStore/treeActions.ts | 43 ++++++++++++ src/store/treeStore/treeStore.tsx | 84 +++++++++++++++++++++++- 4 files changed, 178 insertions(+), 6 deletions(-) diff --git a/src/components/AnchorMenu/AnchorMenu.css b/src/components/AnchorMenu/AnchorMenu.css index 3b6b07c2..a5117eb2 100644 --- a/src/components/AnchorMenu/AnchorMenu.css +++ b/src/components/AnchorMenu/AnchorMenu.css @@ -43,6 +43,13 @@ visibility: visible; } +.selectedHighlight{ + .rst__rowContents{ + background-color: #e9b8bf !important; + + } +} + /* override styling */ .anchor-menu__item .rst__rowLabel { flex: 1; diff --git a/src/components/AnchorMenu/AnchorMenu.tsx b/src/components/AnchorMenu/AnchorMenu.tsx index 51f97360..aaf47e9c 100644 --- a/src/components/AnchorMenu/AnchorMenu.tsx +++ b/src/components/AnchorMenu/AnchorMenu.tsx @@ -2,14 +2,15 @@ import './AnchorMenu.css'; import { DndProvider, DragSource, DragSourceConnector, ConnectDragSource } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; -import React from 'react'; +import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { ActionType, Items, MarkedItem, OrderItem } from '../../store/treeStore/treeStore'; +import { ActionType, Items, MarkedItem, OrderItem, TreeContext } from '../../store/treeStore/treeStore'; import { IQuestionnaireItemType } from '../../types/IQuestionnareItemType'; import { moveItemAction, newItemAction, reorderItemAction, + selectMultipleNodesAction, updateMarkedLinkIdAction, } from '../../store/treeStore/treeActions'; import { ValidationErrors } from '../../helpers/orphanValidation'; @@ -84,7 +85,15 @@ const YourExternalNodeComponent = DragSource( const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { const { t } = useTranslation(); + const { dispatch } = useContext(TreeContext); const [collapsedNodes, setCollapsedNodes] = React.useState([]); + const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node}[]>([]); + + const [firstSelectedIndex, setFirstSelectedIndex] = React.useState(null); + + const handleOnNodeClick = (event: React.MouseEvent, node: Node, extendedNode : ExtendedNode) =>{ + dispatch(selectMultipleNodesAction(firstSelectedIndex, orderTreeData, selectedNodes, event, node, extendedNode, setSelectedNodes, setFirstSelectedIndex)) + } const mapToTreeData = (item: OrderItem[], hierarchy: string, parentLinkId?: string): Node[] => { return item @@ -121,6 +130,11 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { return props.qCurrentItem?.linkId === linkId; }; + const isNodeHighlighted = (node: any) => { + return selectedNodes.some( + (item) => item.node.title === node.title); + } + const getRelevantIcon = (type?: string) => { switch (type) { case IQuestionnaireItemType.group: @@ -191,9 +205,22 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { const oldPath = treePathToOrderArray(prevPath); // reorder within same parent if (JSON.stringify(newPath) === JSON.stringify(oldPath)) { - props.dispatch(reorderItemAction(node.title, newPath, moveIndex)); + if(selectedNodes.length > 1 && selectedNodes.some(item => item.node.title === node.title)){ + selectedNodes.map((item, i) =>{ + props.dispatch(reorderItemAction(item.node.title, newPath, moveIndex+i)); + }) + + } else { + props.dispatch(reorderItemAction(node.title, newPath, moveIndex)); + } } else { - props.dispatch(moveItemAction(node.title, newPath, oldPath, moveIndex)); + if(selectedNodes.length > 1 && selectedNodes.some(item => item.node.title === node.title)){ + selectedNodes.map((item, i) =>{ + props.dispatch(moveItemAction(item.node.title, newPath, oldPath, moveIndex+i)); + }) + } else { + props.dispatch(moveItemAction(node.title, newPath, oldPath, moveIndex)); + } } } }} @@ -209,9 +236,24 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { return item ? canTypeHaveChildren(item) : false; }} generateNodeProps={(extendedNode: ExtendedNode) => ({ + onClick: (event: React.MouseEvent) => { + event.stopPropagation(); + const target = event.target as HTMLElement; + const clickedItemClassName = target.className; + + if ( + clickedItemClassName !== 'rstcustom__expandButton' && + clickedItemClassName !== 'rst__collapseButton' + ) { + + handleOnNodeClick(event, extendedNode.node, extendedNode) + } + + }, className: `anchor-menu__item ${hasValidationError(extendedNode.node.title) ? 'validation-error' : ''} ${extendedNode.path.length === 1 ? 'anchor-menu__topitem' : ''} + ${isNodeHighlighted(extendedNode.node) ? "selectedHighlight" : ""} ${isSelectedItem(extendedNode.node.title) ? 'anchor-menu__item--selected' : ''} `, title: ( diff --git a/src/store/treeStore/treeActions.ts b/src/store/treeStore/treeActions.ts index 670b31cb..ca5975d9 100644 --- a/src/store/treeStore/treeActions.ts +++ b/src/store/treeStore/treeActions.ts @@ -51,6 +51,7 @@ export const UPDATE_MARKED_LINK_ID = 'updateMarkedLinkId'; export const UPDATE_VALUESET_ACTION = 'UPDATE_VALUESET'; export const IMPORT_VALUESET_ACTION = 'IMPORT_VALUESET'; export const SAVE_ACTION = 'save'; +export const SELECT_MULTIPLE_NODES_ACTION = 'selectMultipleNodes' type ItemValueType = | string @@ -65,6 +66,22 @@ type ItemValueType = | Coding[] | undefined; // TODO: legg på alle lovlige verdier + +export interface Node { + linkId? : number, + title: string; + hierarchy?: string; + nodeType?: IQuestionnaireItemType; + nodeReadableType?: string; + children: Node[]; +} + +export interface ExtendedNode { + node: Node; + path: string[]; + treeIndex? : number; +} + export interface AddItemCodeAction { type: typeof ADD_ITEM_CODE_ACTION; linkId: string; @@ -124,6 +141,18 @@ export interface UpdateMetadataTranslationAction { translation: string; } +export interface selectMultipleNodesAction { + type: typeof SELECT_MULTIPLE_NODES_ACTION; + firstSelectedIndex: number | null; + orderTreeData: Node[]; + selectedNodes: { node: Node }[]; + event: React.MouseEvent, + node: Node, + extendedNode : ExtendedNode + setSelectedNodes: React.Dispatch>; + setFirstSelectedIndex: React.Dispatch>; +} + export interface UpdateSettingTranslationAction { type: typeof UPDATE_SETTING_TRANSLATION_ACTION; languageCode: string; @@ -344,6 +373,20 @@ export const updateSettingTranslationAction = ( }; }; +export const selectMultipleNodesAction = (firstSelectedIndex: number | null, orderTreeData: Node[], selectedNodes: { node: Node }[], event: React.MouseEvent, node: Node, extendedNode : ExtendedNode, setSelectedNodes: React.Dispatch>, setFirstSelectedIndex: React.Dispatch>) : selectMultipleNodesAction =>{ + return { + type : SELECT_MULTIPLE_NODES_ACTION, + firstSelectedIndex, + orderTreeData, + selectedNodes, + event, + node, + extendedNode, + setSelectedNodes, + setFirstSelectedIndex, + } +} + export const updateContainedValueSetTranslationAction = ( languageCode: string, valueSetId: string, diff --git a/src/store/treeStore/treeStore.tsx b/src/store/treeStore/treeStore.tsx index 162df5ce..662987b5 100644 --- a/src/store/treeStore/treeStore.tsx +++ b/src/store/treeStore/treeStore.tsx @@ -55,10 +55,12 @@ import { UpdateValueSetAction, UPDATE_SETTING_TRANSLATION_ACTION, UpdateSettingTranslationAction, + selectMultipleNodesAction, + SELECT_MULTIPLE_NODES_ACTION, } from './treeActions'; import { IQuestionnaireMetadata, IQuestionnaireMetadataType } from '../../types/IQuestionnaireMetadataType'; import createUUID from '../../helpers/CreateUUID'; -import { IItemProperty } from '../../types/IQuestionnareItemType'; +import { IItemProperty, IQuestionnaireItemType } from '../../types/IQuestionnareItemType'; import { INITIAL_LANGUAGE } from '../../helpers/LanguageHelper'; import { isIgnorableItem } from '../../helpers/itemControl'; import { createOptionReferenceExtensions } from '../../helpers/extensionHelper'; @@ -68,6 +70,7 @@ import { isRecipientList } from '../../helpers/QuestionHelper'; import { IExtentionType } from '../../types/IQuestionnareItemType'; import { createVisibilityCoding, VisibilityType } from '../../helpers/globalVisibilityHelper'; import { tjenesteomraadeCode, getTjenesteomraadeCoding } from '../../helpers/MetadataHelper'; +import { TreeItem, getNodeAtPath } from '@nosferatu500/react-sortable-tree'; export type ActionType = | AddItemCodeAction @@ -95,7 +98,8 @@ export type ActionType = | UpdateValueSetAction | RemoveItemAttributeAction | SaveAction - | UpdateMarkedLinkId; + | UpdateMarkedLinkId + | selectMultipleNodesAction; export interface Items { [linkId: string]: QuestionnaireItem; @@ -166,6 +170,25 @@ export interface MarkedItem { parentArray: Array; } +export interface TreeNodeKeyParams { + treeIndex: number; +} + +export interface Node { + linkId? : number, + title: string; + hierarchy?: string; + nodeType?: IQuestionnaireItemType; + nodeReadableType?: string; + children: Node[]; +} + +export interface ExtendedNode { + node: Node; + path: string[]; + treeIndex? : number; +} + export interface TreeState { isDirty: boolean; qItems: Items; @@ -665,6 +688,60 @@ function removeAttributeFromItem(draft: TreeState, action: RemoveItemAttributeAc } } +function selectMultipleNodes(draft: any , action: selectMultipleNodesAction) : void{ + const {event, firstSelectedIndex, selectedNodes, setSelectedNodes, setFirstSelectedIndex, extendedNode, orderTreeData, node} = action; + + const { treeIndex } = extendedNode; + + if (event.shiftKey && firstSelectedIndex !== null && treeIndex != undefined) { + // Select range of nodes between firstSelectedIndex and treeIndex + let startIndex = Math.min(firstSelectedIndex, treeIndex); + let endIndex = Math.max(firstSelectedIndex, treeIndex); + + // handle cases for reverse mode - firstSelectedIndex is the first one and treeIndex is the second one + if (firstSelectedIndex > treeIndex) { + startIndex = startIndex - 1; + endIndex = endIndex - 1; + } + + const newSelectedNodes: { node: Node }[] = []; + for (let i = startIndex + 1; i <= endIndex; i++) { + const result = getNodeAtPath({ + treeData: orderTreeData, + path: [i], + getNodeKey: ({ treeIndex }: TreeNodeKeyParams) => treeIndex, + }); + + if (result && result.node) { + const nodeExists = selectedNodes.some( + (item) => item.node.title === result.node.title + ); + + if (!nodeExists) { + newSelectedNodes.push({ node: result.node as Node }); + } + } + } + setSelectedNodes((prev) => [...prev, ...newSelectedNodes]); + } else { + const updatedPath = treeIndex && treeIndex + setSelectedNodes((prev) => { + const nodeExists = prev.some( + (item) => item.node.title === node.title + ); + if (nodeExists) { + return prev.filter((item) => item.node.title !== node.title); + } else { + return [...prev, { node, path: updatedPath as number }]; + } + }); + } + + if (treeIndex != null) { + setFirstSelectedIndex(treeIndex); + } +} + const reducer = produce((draft: TreeState, action: ActionType) => { // Flag as dirty on all changes except reset, save and "scroll" if ( @@ -753,6 +830,9 @@ const reducer = produce((draft: TreeState, action: ActionType) => { case UPDATE_MARKED_LINK_ID: updateMarkedItemId(draft, action); break; + case SELECT_MULTIPLE_NODES_ACTION: + selectMultipleNodes(draft, action); + break; } }); From 24aa433811d7c9af992ab265e8b3f8d49266cda1 Mon Sep 17 00:00:00 2001 From: Anisha Pant Date: Tue, 25 Jun 2024 20:23:48 -0400 Subject: [PATCH 02/24] Add settings button to open sidebar --- src/components/AnchorMenu/AnchorMenu.tsx | 12 +------- .../AnchorMenu/ItemButtons/ItemButtons.css | 7 +++++ .../AnchorMenu/ItemButtons/ItemButtons.tsx | 23 +++++++++++++- src/images/icons/settings.svg | 30 +++++++++++++++++++ 4 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/images/icons/settings.svg diff --git a/src/components/AnchorMenu/AnchorMenu.tsx b/src/components/AnchorMenu/AnchorMenu.tsx index 51f97360..c7891e7a 100644 --- a/src/components/AnchorMenu/AnchorMenu.tsx +++ b/src/components/AnchorMenu/AnchorMenu.tsx @@ -215,17 +215,7 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { ${isSelectedItem(extendedNode.node.title) ? 'anchor-menu__item--selected' : ''} `, title: ( - { - props.dispatch( - updateMarkedLinkIdAction( - extendedNode.node.title, - treePathToOrderArray(extendedNode.path), - ), - ); - }} - > + {extendedNode.node.hierarchy} diff --git a/src/components/AnchorMenu/ItemButtons/ItemButtons.css b/src/components/AnchorMenu/ItemButtons/ItemButtons.css index 238d2c98..ea3de96d 100644 --- a/src/components/AnchorMenu/ItemButtons/ItemButtons.css +++ b/src/components/AnchorMenu/ItemButtons/ItemButtons.css @@ -36,3 +36,10 @@ height: 25px; width: 25px; } + +.settings-icon { + background: url('../../../images/icons/settings.svg') no-repeat center; + margin: auto; + height: 25px; + width: 25px; +} diff --git a/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx b/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx index 6d1c6914..1e5d4007 100644 --- a/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx +++ b/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx @@ -5,7 +5,7 @@ import { ActionType } from '../../../store/treeStore/treeStore'; import { QuestionnaireItem } from '../../../types/fhir'; import './ItemButtons.css'; -import { deleteItemAction, duplicateItemAction } from '../../../store/treeStore/treeActions'; +import { deleteItemAction, duplicateItemAction, updateMarkedLinkIdAction } from '../../../store/treeStore/treeActions'; export const generateItemButtons = ( t: TFunction<'translation'>, @@ -17,6 +17,7 @@ export const generateItemButtons = ( if (!item) { return []; } + const dispatchDeleteItem = (event: MouseEvent): void => { event.stopPropagation(); dispatch(deleteItemAction(item.linkId, parentArray)); @@ -27,11 +28,31 @@ export const generateItemButtons = ( dispatch(duplicateItemAction(item.linkId, parentArray)); }; + const dispatchSettingsItem = (event: MouseEvent): void => { + event.stopPropagation(); + dispatch( + updateMarkedLinkIdAction( + item.linkId, + parentArray + ), + ); + }; + const getClassNames = () => { return `item-button ${showLabel ? 'item-button--visible' : ''}`; }; return [ + , + + + + + + ) +} + +export default DeleteConfirmation; \ No newline at end of file diff --git a/src/views/FormBuilder.tsx b/src/views/FormBuilder.tsx index 91609f0a..526445e9 100644 --- a/src/views/FormBuilder.tsx +++ b/src/views/FormBuilder.tsx @@ -14,6 +14,8 @@ import FormFillerPreview from '../components/Refero/FormFillerPreview'; import './FormBuilder.css'; import { IQuestionnaireItemType } from '../types/IQuestionnareItemType'; +import DeleteConfirmation from '../components/MultiSelect/DeleteConfirmation'; +import { deleteItemAction } from '../store/treeStore/treeActions'; interface Node { title: string; @@ -33,11 +35,16 @@ const FormBuilder = (): JSX.Element => { const [translateLang, setTranslateLang] = useState(''); const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node }[]>([]); + const [isDeleteConfirmationModalVisible, setIsDeleteConfirmationModalVisible] = useState(false) const toggleFormDetails = useCallback(() => { setShowFormDetails(!showFormDetails); }, [showFormDetails]); + const handleOnMultipleDelete = () => { + console.log(selectedNodes) + } + return ( <> { setSelectedNodes([])} /> {selectedNodes.length} selected -
+
setIsDeleteConfirmationModalVisible(true)}> Delete
} @@ -96,6 +103,7 @@ const FormBuilder = (): JSX.Element => { isOpen={showFormDetails} /> + ); From b3c654f5a26a5f5136022b35942c6034fa3e9e3c Mon Sep 17 00:00:00 2001 From: Anisha Pant Date: Thu, 27 Jun 2024 20:30:30 -0400 Subject: [PATCH 07/24] Delete selected nodes --- src/components/AnchorMenu/AnchorMenu.tsx | 5 +- src/store/treeStore/treeActions.ts | 6 +- src/store/treeStore/treeStore.tsx | 136 +++++++++++------------ src/views/FormBuilder.tsx | 14 ++- 4 files changed, 86 insertions(+), 75 deletions(-) diff --git a/src/components/AnchorMenu/AnchorMenu.tsx b/src/components/AnchorMenu/AnchorMenu.tsx index 7e2de468..24cb0364 100644 --- a/src/components/AnchorMenu/AnchorMenu.tsx +++ b/src/components/AnchorMenu/AnchorMenu.tsx @@ -34,8 +34,8 @@ interface AnchorMenuProps { qCurrentItem: MarkedItem | undefined; validationErrors: ValidationErrors[]; dispatch: React.Dispatch; - selectedNodes: { node: Node }[]; - setSelectedNodes: React.Dispatch>; + selectedNodes: { node: Node, path: Array }[]; + setSelectedNodes: React.Dispatch }[]>>; } interface ExtendedNode { @@ -94,6 +94,7 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { const [firstSelectedIndex, setFirstSelectedIndex] = React.useState([]); const handleOnNodeClick = (event: React.MouseEvent, node: Node, extendedNode: ExtendedNode) => { + console.log(extendedNode.path) dispatch(selectMultipleNodesAction(firstSelectedIndex, orderTreeData, selectedNodes, event, node, extendedNode, setSelectedNodes, setFirstSelectedIndex)) } diff --git a/src/store/treeStore/treeActions.ts b/src/store/treeStore/treeActions.ts index 68af410b..243b8eb5 100644 --- a/src/store/treeStore/treeActions.ts +++ b/src/store/treeStore/treeActions.ts @@ -145,11 +145,11 @@ export interface selectMultipleNodesAction { type: typeof SELECT_MULTIPLE_NODES_ACTION; firstSelectedIndex: number[] | null; orderTreeData: Node[]; - selectedNodes: { node: Node }[]; + selectedNodes: { node: Node, path: Array }[]; event: React.MouseEvent, node: Node, extendedNode : ExtendedNode - setSelectedNodes: React.Dispatch>; + setSelectedNodes: React.Dispatch }[]>>; setFirstSelectedIndex: React.Dispatch>; } @@ -373,7 +373,7 @@ export const updateSettingTranslationAction = ( }; }; -export const selectMultipleNodesAction = (firstSelectedIndex: number[] | null, orderTreeData: Node[], selectedNodes: { node: Node }[], event: React.MouseEvent, node: Node, extendedNode : ExtendedNode, setSelectedNodes: React.Dispatch>, setFirstSelectedIndex: React.Dispatch>) : selectMultipleNodesAction =>{ +export const selectMultipleNodesAction = (firstSelectedIndex: number[] | null, orderTreeData: Node[], selectedNodes: { node: Node, path : Array }[], event: React.MouseEvent, node: Node, extendedNode : ExtendedNode, setSelectedNodes: React.Dispatch }[]>>, setFirstSelectedIndex: React.Dispatch>) : selectMultipleNodesAction =>{ return { type : SELECT_MULTIPLE_NODES_ACTION, firstSelectedIndex, diff --git a/src/store/treeStore/treeStore.tsx b/src/store/treeStore/treeStore.tsx index cf2246f1..42315fc4 100644 --- a/src/store/treeStore/treeStore.tsx +++ b/src/store/treeStore/treeStore.tsx @@ -98,7 +98,7 @@ export type ActionType = | UpdateValueSetAction | RemoveItemAttributeAction | SaveAction - | UpdateMarkedLinkId + | UpdateMarkedLinkId | selectMultipleNodesAction; export interface Items { @@ -175,7 +175,7 @@ export interface TreeNodeKeyParams { } export interface Node { - linkId? : number, + linkId?: number, title: string; hierarchy?: string; nodeType?: IQuestionnaireItemType; @@ -186,7 +186,7 @@ export interface Node { export interface ExtendedNode { node: Node; path: string[]; - treeIndex? : number; + treeIndex?: number; } export interface TreeState { @@ -692,90 +692,90 @@ function removeAttributeFromItem(draft: TreeState, action: RemoveItemAttributeAc const pathToChild = (index: string, list: Node[]): number[] => { let visitedNode: number[] | null = null; let globalIndex = -1; - + const getData = (child: Node[], path: number[]): number[] | null => { - for (let i = 0; i < child.length; i++) { - globalIndex++; - const currentPath = path.concat(globalIndex); - - // Check if the current object's title matches the id we're looking for - if (child[i].title === index) { - return currentPath.slice(-3); // Return last 3 elements - } - - // If the current object has children, recurse into the children - if (child[i].children && child[i].children.length > 0) { - const result = getData(child[i].children, currentPath); - if (result) { - return result; - } + for (let i = 0; i < child.length; i++) { + globalIndex++; + const currentPath = path.concat(globalIndex); + + // Check if the current object's title matches the id we're looking for + if (child[i].title === index) { + return currentPath.slice(-3); // Return last 3 elements + } + + // If the current object has children, recurse into the children + if (child[i].children && child[i].children.length > 0) { + const result = getData(child[i].children, currentPath); + if (result) { + return result; + } + } } - } - return null; + return null; }; - + visitedNode = getData(list, []); return visitedNode ? visitedNode : []; - }; +}; -function selectMultipleNodes(draft: any , action: selectMultipleNodesAction) : void{ - const {event, firstSelectedIndex, selectedNodes, setSelectedNodes, setFirstSelectedIndex, extendedNode, orderTreeData, node} = action; +function selectMultipleNodes(draft: any, action: selectMultipleNodesAction): void { + const { event, firstSelectedIndex, selectedNodes, setSelectedNodes, setFirstSelectedIndex, extendedNode, orderTreeData, node } = action; - const pathToTheClickedNode = pathToChild(extendedNode.node.title, orderTreeData) + const pathToTheClickedNode = pathToChild(extendedNode.node.title, orderTreeData) const { treeIndex } = extendedNode; - if (event.shiftKey && firstSelectedIndex !== null && treeIndex != undefined) { + if (event.shiftKey && firstSelectedIndex !== null && treeIndex != undefined) { - const parent = pathToTheClickedNode.slice(0, -1); - // Select range of nodes between firstSelectedIndex and treeIndex - let startIndex = Math.min(firstSelectedIndex[firstSelectedIndex.length -1], pathToTheClickedNode[pathToTheClickedNode.length - 1]); - let endIndex = Math.max(firstSelectedIndex[firstSelectedIndex.length -1], pathToTheClickedNode[pathToTheClickedNode.length - 1]); + const parent = pathToTheClickedNode.slice(0, -1); + // Select range of nodes between firstSelectedIndex and treeIndex + let startIndex = Math.min(firstSelectedIndex[firstSelectedIndex.length - 1], pathToTheClickedNode[pathToTheClickedNode.length - 1]); + let endIndex = Math.max(firstSelectedIndex[firstSelectedIndex.length - 1], pathToTheClickedNode[pathToTheClickedNode.length - 1]); - // handle cases for reverse mode - firstSelectedIndex is the first one and treeIndex is the second one - if (firstSelectedIndex[firstSelectedIndex.length -1] > pathToTheClickedNode[pathToTheClickedNode.length - 1]) { - startIndex = startIndex - 1; - endIndex = endIndex - 1; - } + // handle cases for reverse mode - firstSelectedIndex is the first one and treeIndex is the second one + if (firstSelectedIndex[firstSelectedIndex.length - 1] > pathToTheClickedNode[pathToTheClickedNode.length - 1]) { + startIndex = startIndex - 1; + endIndex = endIndex - 1; + } - const newSelectedNodes: { node: Node }[] = []; - for (let i = startIndex + 1; i <= endIndex; i++) { - const result = getNodeAtPath({ - treeData: orderTreeData, - path: [...parent, i], - getNodeKey: ({ treeIndex }: TreeNodeKeyParams) => treeIndex, - }); - - if (result && result.node) { - const nodeExists = selectedNodes.some( - (item) => item.node.title === result.node.title - ); - - if (!nodeExists) { - newSelectedNodes.push({ node: result.node as Node }); - } - } - } - setSelectedNodes((prev) => [...prev, ...newSelectedNodes]); - } else { - const updatedPath = treeIndex && treeIndex - setSelectedNodes((prev) => { - const nodeExists = prev.some( - (item) => item.node.title === node.title + const newSelectedNodes: { node: Node, path: Array }[] = []; + for (let i = startIndex + 1; i <= endIndex; i++) { + const result = getNodeAtPath({ + treeData: orderTreeData, + path: [...parent, i], + getNodeKey: ({ treeIndex }: TreeNodeKeyParams) => treeIndex, + }); + + if (result && result.node) { + const nodeExists = selectedNodes.some( + (item) => item.node.title === result.node.title ); - if (nodeExists) { - return prev.filter((item) => item.node.title !== node.title); - } else { - return [...prev, { node, path: updatedPath as number }]; + + if (!nodeExists) { + newSelectedNodes.push({ node: result.node as Node, path: extendedNode.path }); } - }); + } } + setSelectedNodes((prev) => [...prev, ...newSelectedNodes]); + } else { + const updatedPath = treeIndex && treeIndex + setSelectedNodes((prev) => { + const nodeExists = prev.some( + (item) => item.node.title === node.title + ); + if (nodeExists) { + return prev.filter((item) => item.node.title !== node.title); + } else { + return [...prev, { node, path: extendedNode.path }]; + } + }); + } - if (pathToTheClickedNode) { - setFirstSelectedIndex(pathToTheClickedNode); - } + if (pathToTheClickedNode) { + setFirstSelectedIndex(pathToTheClickedNode); + } } const reducer = produce((draft: TreeState, action: ActionType) => { diff --git a/src/views/FormBuilder.tsx b/src/views/FormBuilder.tsx index 526445e9..d33cafa7 100644 --- a/src/views/FormBuilder.tsx +++ b/src/views/FormBuilder.tsx @@ -34,15 +34,25 @@ const FormBuilder = (): JSX.Element => { const [translationErrors, setTranslationErrors] = useState>([]); const [translateLang, setTranslateLang] = useState(''); - const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node }[]>([]); + const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node, path: Array }[]>([]); const [isDeleteConfirmationModalVisible, setIsDeleteConfirmationModalVisible] = useState(false) const toggleFormDetails = useCallback(() => { setShowFormDetails(!showFormDetails); }, [showFormDetails]); + const treePathToOrderArray = (treePath: string[]): string[] => { + const newPath = [...treePath]; + newPath.splice(-1); + return newPath; + }; + const handleOnMultipleDelete = () => { - console.log(selectedNodes) + selectedNodes.map((item) => { + dispatch(deleteItemAction(item.node.title, treePathToOrderArray(item.path))); + }) + setSelectedNodes([]) + setIsDeleteConfirmationModalVisible(false) } return ( From 92f66f39db323bd393673b64373e715696589445 Mon Sep 17 00:00:00 2001 From: Anisha Pant Date: Fri, 28 Jun 2024 11:50:14 -0400 Subject: [PATCH 08/24] Add new component using plus button --- src/components/AnchorMenu/AnchorMenu.css | 55 ++++++++++------- src/components/AnchorMenu/AnchorMenu.tsx | 77 +++++++++++++++--------- src/images/icons/add-icon.svg | 5 ++ 3 files changed, 87 insertions(+), 50 deletions(-) create mode 100644 src/images/icons/add-icon.svg diff --git a/src/components/AnchorMenu/AnchorMenu.css b/src/components/AnchorMenu/AnchorMenu.css index 5e955057..6796922f 100644 --- a/src/components/AnchorMenu/AnchorMenu.css +++ b/src/components/AnchorMenu/AnchorMenu.css @@ -1,6 +1,6 @@ .questionnaire-overview { text-align: center; - margin-top: 130px; + margin-top: 120px; margin-left: auto; margin-right: auto; position: relative; @@ -39,15 +39,16 @@ .anchor-menu__item--selected { box-shadow: 0 0 0 4px var(--primary-2); } + .anchor-menu__item:hover .item-button { visibility: visible; } -.selectedHighlight{ - .rst__rowContents{ +.selectedHighlight { + .rst__rowContents { background-color: #e9b8bf !important; - } + } } /* override styling */ @@ -61,9 +62,11 @@ padding: 0.5rem; cursor: pointer; } + .anchor-menu__inneritem:hover { background-color: #e4e4e4; } + .anchor-menu__topitem .rst__rowContents { /* override styling */ background-color: #e4f7f9; @@ -75,14 +78,17 @@ } .anchor-menu__dragcomponent { - padding: 8px 16px; + padding: 13px 16px; background: #e4f7f9; margin: 5px; + margin-bottom: 13px; text-align: left; color: black; border: 1px solid #cae7ed; cursor: grab; + position: relative; } + .anchor-menu__dragcomponent:active { cursor: grabbing; } @@ -108,12 +114,12 @@ background: url('../../images/icons/help-circle-outline.svg') no-repeat center; } -.multi-select-widget{ +.multi-select-widget { margin-top: 60px; } -.header-wrapper{ +.header-wrapper { width: 66em; margin-left: 14em; margin-bottom: 2em; @@ -123,21 +129,21 @@ align-items: center; position: absolute; - .title{ + .title { margin-left: 10em; display: flex; padding: 0; align-items: center; background-color: transparent; - } - - .items-selected{ + } + + .items-selected { font-weight: 600; margin-left: 0.6em; - } + } - .delete-multiple{ - width:90px; + .delete-multiple { + width: 90px; border: 1px solid rgb(239, 16, 16); border-radius: 8px; padding: 4px; @@ -145,16 +151,23 @@ display: flex; align-items: center; justify-content: center; - } + } - .bi-trash3{ + .bi-trash3 { margin-right: 4px; - path{ + + path { color: rgb(239, 16, 16); } - } + } - .cross-icon{ + .cross-icon { cursor: pointer; - } - } \ No newline at end of file + } +} + +.plus-icon { + position: absolute; + top: -0.6em; + left: 10.5em; +} \ No newline at end of file diff --git a/src/components/AnchorMenu/AnchorMenu.tsx b/src/components/AnchorMenu/AnchorMenu.tsx index 24cb0364..63b0ade5 100644 --- a/src/components/AnchorMenu/AnchorMenu.tsx +++ b/src/components/AnchorMenu/AnchorMenu.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/prop-types */ import './AnchorMenu.css'; import { DndProvider, DragSource, DragSourceConnector, ConnectDragSource } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; @@ -56,35 +57,6 @@ interface NodeVisibilityToggleEvent { expanded: boolean; } -const newNodeLinkId = 'NEW'; -const externalNodeType = 'yourNodeType'; - -const externalNodeSpec = { - // This needs to return an object with a property `node` in it. - // Object rest spread is recommended to avoid side effects of - // referencing the same object in different trees. - beginDrag: (componentProps: { node: Node }) => ({ node: { ...componentProps.node } }), -}; -const externalNodeCollect = (connect: DragSourceConnector) => ({ - connectDragSource: connect.dragSource(), - // Add props via react-dnd APIs to enable more visual - // customization of your component - // isDragging: monitor.isDragging(), - // didDrop: monitor.didDrop(), -}); - -const ExternalNodeBaseComponent = (props: { connectDragSource: ConnectDragSource; node: Node }): JSX.Element | null => { - return props.connectDragSource(
{props.node.nodeReadableType}
, { - dropEffect: 'copy', - }); -}; - -const YourExternalNodeComponent = DragSource( - externalNodeType, - externalNodeSpec, - externalNodeCollect, -)(ExternalNodeBaseComponent); - const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { const { t } = useTranslation(); const { dispatch } = useContext(TreeContext); @@ -93,6 +65,53 @@ const AnchorMenu = (props: AnchorMenuProps): JSX.Element => { const [collapsedNodes, setCollapsedNodes] = React.useState([]); const [firstSelectedIndex, setFirstSelectedIndex] = React.useState([]); + const newNodeLinkId = 'NEW'; + const externalNodeType = 'yourNodeType'; + + const externalNodeSpec = { + // This needs to return an object with a property `node` in it. + // Object rest spread is recommended to avoid side effects of + // referencing the same object in different trees. + beginDrag: (componentProps: { node: Node }) => ({ node: { ...componentProps.node } }), + }; + const externalNodeCollect = (connect: DragSourceConnector) => ({ + connectDragSource: connect.dragSource(), + // Add props via react-dnd APIs to enable more visual + // customization of your component + // isDragging: monitor.isDragging(), + // didDrop: monitor.didDrop(), + }); + + const ExternalNodeBaseComponent = (props: { connectDragSource: ConnectDragSource; node: Node }): JSX.Element | null => { + return props.connectDragSource(
{props.node.nodeReadableType}
handleOnElementAdd(props.node)}> + + + +
, { + dropEffect: 'copy', + }); + }; + + const YourExternalNodeComponent = DragSource( + externalNodeType, + externalNodeSpec, + externalNodeCollect, + )(ExternalNodeBaseComponent); + + const handleOnElementAdd = (node: Node) => { + const moveIndex = orderTreeData.length; + const newPath: string[] = [] + + if (node.nodeType) { + props.dispatch( + newItemAction( + getInitialItemConfig(node.nodeType, t('Recipient component')), + newPath, + moveIndex, + )) + } + } + const handleOnNodeClick = (event: React.MouseEvent, node: Node, extendedNode: ExtendedNode) => { console.log(extendedNode.path) dispatch(selectMultipleNodesAction(firstSelectedIndex, orderTreeData, selectedNodes, event, node, extendedNode, setSelectedNodes, setFirstSelectedIndex)) diff --git a/src/images/icons/add-icon.svg b/src/images/icons/add-icon.svg new file mode 100644 index 00000000..38c61b41 --- /dev/null +++ b/src/images/icons/add-icon.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file From d699bf0d326e08ab026794b6e4e970d2e1250f26 Mon Sep 17 00:00:00 2001 From: Anisha Pant Date: Mon, 1 Jul 2024 12:40:25 -0400 Subject: [PATCH 09/24] Update node background color --- src/components/AnchorMenu/AnchorMenu.css | 4 ++-- src/components/AnchorMenu/ItemButtons/ItemButtons.tsx | 4 ++-- src/components/Navbar/Navbar.css | 10 +++++++--- src/components/Navbar/Navbar.tsx | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/AnchorMenu/AnchorMenu.css b/src/components/AnchorMenu/AnchorMenu.css index 6796922f..ecff7a41 100644 --- a/src/components/AnchorMenu/AnchorMenu.css +++ b/src/components/AnchorMenu/AnchorMenu.css @@ -46,7 +46,7 @@ .selectedHighlight { .rst__rowContents { - background-color: #e9b8bf !important; + background-color: #e4f7f9 !important; } } @@ -69,7 +69,7 @@ .anchor-menu__topitem .rst__rowContents { /* override styling */ - background-color: #e4f7f9; + /* background-color: #e4f7f9; */ } .anchor-menu__title { diff --git a/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx b/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx index 1e5d4007..a37134fc 100644 --- a/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx +++ b/src/components/AnchorMenu/ItemButtons/ItemButtons.tsx @@ -50,8 +50,8 @@ export const generateItemButtons = ( aria-label="Settings element" title={t('Settings')} > - - {showLabel && } + + {showLabel && } , - + + - - ) -} + ); +}; -export default DeleteConfirmation; \ No newline at end of file +export default DeleteConfirmation; diff --git a/src/store/treeStore/treeActions.ts b/src/store/treeStore/treeActions.ts index ac06ddec..22ce9684 100644 --- a/src/store/treeStore/treeActions.ts +++ b/src/store/treeStore/treeActions.ts @@ -51,7 +51,7 @@ export const UPDATE_MARKED_LINK_ID = 'updateMarkedLinkId'; export const UPDATE_VALUESET_ACTION = 'UPDATE_VALUESET'; export const IMPORT_VALUESET_ACTION = 'IMPORT_VALUESET'; export const SAVE_ACTION = 'save'; -export const UPDATE_SELECTED_NODES_ACTION = 'updateSelectedNodesAction' +export const UPDATE_SELECTED_NODES_ACTION = 'updateSelectedNodesAction'; type ItemValueType = | string @@ -66,9 +66,8 @@ type ItemValueType = | Coding[] | undefined; // TODO: legg på alle lovlige verdier - export interface Node { - linkId? : number, + linkId?: number; title: string; hierarchy?: string; nodeType?: IQuestionnaireItemType; @@ -79,8 +78,8 @@ export interface Node { export interface ExtendedNode { node: Node; path: string[]; - parentNode : Node; - treeIndex? : number; + parentNode: Node; + treeIndex?: number; } export interface AddItemCodeAction { @@ -146,13 +145,13 @@ export interface updateSelectedNodesAction { type: typeof UPDATE_SELECTED_NODES_ACTION; firstSelectedIndex: number[] | null; orderTreeData: Node[]; - selectedNodes: { node: Node, path: Array }[]; - event: React.MouseEvent, - node: Node, - extendedNode : ExtendedNode - setSelectedNodes: React.Dispatch }[]>>; + selectedNodes: { node: Node; path: Array }[]; + event: React.MouseEvent; + node: Node; + extendedNode: ExtendedNode; + setSelectedNodes: React.Dispatch }[]>>; setFirstSelectedIndex: React.Dispatch>; - collapsedNodes : string[] + collapsedNodes: string[]; } export interface UpdateSettingTranslationAction { @@ -375,9 +374,19 @@ export const updateSettingTranslationAction = ( }; }; -export const updateSelectedNodesAction = (firstSelectedIndex: number[] | null, orderTreeData: Node[], selectedNodes: { node: Node, path : Array }[], event: React.MouseEvent, node: Node, extendedNode : ExtendedNode, setSelectedNodes: React.Dispatch }[]>>, setFirstSelectedIndex: React.Dispatch>, collapsedNodes : string[]) : updateSelectedNodesAction =>{ +export const updateSelectedNodesAction = ( + firstSelectedIndex: number[] | null, + orderTreeData: Node[], + selectedNodes: { node: Node; path: Array }[], + event: React.MouseEvent, + node: Node, + extendedNode: ExtendedNode, + setSelectedNodes: React.Dispatch }[]>>, + setFirstSelectedIndex: React.Dispatch>, + collapsedNodes: string[], +): updateSelectedNodesAction => { return { - type : UPDATE_SELECTED_NODES_ACTION, + type: UPDATE_SELECTED_NODES_ACTION, firstSelectedIndex, orderTreeData, selectedNodes, @@ -386,9 +395,9 @@ export const updateSelectedNodesAction = (firstSelectedIndex: number[] | null, o extendedNode, setSelectedNodes, setFirstSelectedIndex, - collapsedNodes - } -} + collapsedNodes, + }; +}; export const updateContainedValueSetTranslationAction = ( languageCode: string, diff --git a/src/store/treeStore/treeStore.tsx b/src/store/treeStore/treeStore.tsx index 0844efb9..b9a5f5e7 100644 --- a/src/store/treeStore/treeStore.tsx +++ b/src/store/treeStore/treeStore.tsx @@ -61,7 +61,7 @@ import { } from './treeActions'; import { IQuestionnaireMetadata, IQuestionnaireMetadataType } from '../../types/IQuestionnaireMetadataType'; import createUUID from '../../helpers/CreateUUID'; -import { IItemProperty, IQuestionnaireItemType } from '../../types/IQuestionnareItemType'; +import { IItemProperty } from '../../types/IQuestionnareItemType'; import { INITIAL_LANGUAGE } from '../../helpers/LanguageHelper'; import { isIgnorableItem } from '../../helpers/itemControl'; import { createOptionReferenceExtensions } from '../../helpers/extensionHelper'; @@ -709,9 +709,9 @@ const pathToChild = (index: string, list: Node[], collapsedNodes: string[]): num const areAllChildrenNodesSelected = ( currentNode: Node, parentNode: Node, - selectedNodes: { node: Node, path: Array }[] + selectedNodes: { node: Node; path: Array }[], ): boolean => { - const selectedTitles = new Set(selectedNodes.map(item => item.node.title)); + const selectedTitles = new Set(selectedNodes.map((item) => item.node.title)); const checkAllChildren = (node: Node): boolean => { if (!node.children || node.children.length === 0) { @@ -730,7 +730,6 @@ const areAllChildrenNodesSelected = ( return true; }; - selectedTitles.add(currentNode.title); return checkAllChildren(parentNode); }; @@ -739,9 +738,9 @@ const areAllChildrenNodesSelected = ( const checkAndAddParentNodes = ( extendedNode: ExtendedNode, orderTreeData: Node[], - selectedNodes: { node: Node, path: Array }[], - collapsedNodes: string[] -): { node: Node, path: Array }[] => { + selectedNodes: { node: Node; path: Array }[], + collapsedNodes: string[], +): { node: Node; path: Array }[] => { const path = extendedNode.path.slice(0, -1); // Remove the last element to get the parent path let currentPath = path; const allSelectedNodes = [...selectedNodes]; @@ -756,9 +755,7 @@ const checkAndAddParentNodes = ( }); if (result && result.node && areAllChildrenNodesSelected(extendedNode.node, result.node, allSelectedNodes)) { - const nodeExists = allSelectedNodes.some( - (item) => item.node.title === result.node.title - ); + const nodeExists = allSelectedNodes.some((item) => item.node.title === result.node.title); if (!nodeExists) { allSelectedNodes.push({ node: result.node, path: currentPath }); @@ -776,12 +773,10 @@ const checkAndAddParentNodes = ( }; // Returns the list of passed node and it's children until the maximum depth -function recursiveAddNodes(node: Node, path: string[], selectedNodes: { node: Node; path: string[]; }[]) { - const nodes: { node: Node; path: string[]; }[] = []; +function recursiveAddNodes(node: Node, path: string[], selectedNodes: { node: Node; path: string[] }[]) { + const nodes: { node: Node; path: string[] }[] = []; const addNodeIfNotExist = (node: Node, path: string[]) => { - const nodeExists = selectedNodes.some( - (item) => item.node.title === node.title - ); + const nodeExists = selectedNodes.some((item) => item.node.title === node.title); if (!nodeExists) { nodes.push({ node, path }); if (node.children) { @@ -793,9 +788,9 @@ function recursiveAddNodes(node: Node, path: string[], selectedNodes: { node: No }; addNodeIfNotExist(node, path); return nodes; -}; +} -const recursiveRemoveChildNodes = (node: Node, selectedNodes: { node: Node; path: string[]; }[]) => { +const recursiveRemoveChildNodes = (node: Node, selectedNodes: { node: Node; path: string[] }[]) => { let nodes = [...selectedNodes]; const removeNodeAndChildren = (node: Node) => { @@ -813,9 +808,9 @@ const recursiveRemoveChildNodes = (node: Node, selectedNodes: { node: Node; path const checkAndRemoveParentNodes = ( extendedNode: ExtendedNode, orderTreeData: Node[], - selectedNodes: { node: Node, path: Array }[], - collapsedNodes: string[] -): { node: Node, path: Array }[] => { + selectedNodes: { node: Node; path: Array }[], + collapsedNodes: string[], +): { node: Node; path: Array }[] => { const path = extendedNode.path.slice(0, -1); // Remove the last element to get the parent path let currentPath = path; let updatedSelectedNodes = [...selectedNodes]; // Copy the selectedNodes array @@ -830,9 +825,7 @@ const checkAndRemoveParentNodes = ( }); if (result && result.node) { - updatedSelectedNodes = updatedSelectedNodes.filter( - (item) => item.node.title !== result.node.title - ); + updatedSelectedNodes = updatedSelectedNodes.filter((item) => item.node.title !== result.node.title); // Move to the next parent currentPath = currentPath.slice(0, -1); @@ -875,14 +868,14 @@ const findNodePath = (title: string, list: Node[]): string[] | null => { // Handle the selection of a single node function handleSingleSelect( - setSelectedNodes: React.Dispatch>, + setSelectedNodes: React.Dispatch>, node: Node, extendedNode: ExtendedNode, orderTreeData: Node[], collapsedNodes: string[], - selectedNodes: { node: Node; path: string[]; }[] + selectedNodes: { node: Node; path: string[] }[], ) { - let newNodes: { node: Node; path: string[]; }[] = []; + let newNodes: { node: Node; path: string[] }[] = []; setSelectedNodes((prevSelectedNodes) => { const nodeExists = prevSelectedNodes.some((item) => item.node.title === node.title); @@ -903,21 +896,25 @@ function handleSingleSelect( if (newNodes.length > 0) { // Add parent nodes if all children are selected - const updatedList = checkAndAddParentNodes(extendedNode, orderTreeData, [...newNodes, ...selectedNodes], collapsedNodes); + const updatedList = checkAndAddParentNodes( + extendedNode, + orderTreeData, + [...newNodes, ...selectedNodes], + collapsedNodes, + ); setSelectedNodes(updatedList); } } - // Select multiple nodes when the shift key is pressed function handleShiftSelect( pathToTheClickedNode: number[], firstSelectedIndex: number[] | null, orderTreeData: Node[], - selectedNodes: { node: Node; path: string[]; }[], + selectedNodes: { node: Node; path: string[] }[], extendedNode: ExtendedNode, collapsedNodes: string[], - setSelectedNodes: React.Dispatch> + setSelectedNodes: React.Dispatch>, ) { if (!firstSelectedIndex) return; @@ -935,8 +932,8 @@ function handleShiftSelect( endIndex -= 1; } - let newSelectedNodes: { node: Node; path: string[]; }[] = []; - const selectedTitles = new Set(selectedNodes.map(item => item.node.title)); + let newSelectedNodes: { node: Node; path: string[] }[] = []; + const selectedTitles = new Set(selectedNodes.map((item) => item.node.title)); for (let i = startIndex + 1; i <= endIndex; i++) { // TO-DO: The parent is considered to be the same, but what if we select different children in the end? Need to fix this. @@ -955,18 +952,22 @@ function handleShiftSelect( // Add the node and its children to the selection const nodesToAdd = recursiveAddNodes(result.node, nodePath, selectedNodes); newSelectedNodes.push(...nodesToAdd); - nodesToAdd.forEach(node => selectedTitles.add(node.node.title)); + nodesToAdd.forEach((node) => selectedTitles.add(node.node.title)); } } } // Add parent nodes if all children are selected - newSelectedNodes = checkAndAddParentNodes(extendedNode, orderTreeData, [...newSelectedNodes, ...selectedNodes], collapsedNodes); + newSelectedNodes = checkAndAddParentNodes( + extendedNode, + orderTreeData, + [...newSelectedNodes, ...selectedNodes], + collapsedNodes, + ); setSelectedNodes(newSelectedNodes); } - -function updateSelectedNodes(draft: any, action: updateSelectedNodesAction): void { +function updateSelectedNodes(draft: TreeState, action: updateSelectedNodesAction): void { const { event, firstSelectedIndex, @@ -976,7 +977,7 @@ function updateSelectedNodes(draft: any, action: updateSelectedNodesAction): voi extendedNode, orderTreeData, node, - collapsedNodes + collapsedNodes, } = action; // Get the path to the node that was clicked @@ -985,7 +986,15 @@ function updateSelectedNodes(draft: any, action: updateSelectedNodesAction): voi // Check if shift key is held and firstSelectedIndex is not null if (event.shiftKey && firstSelectedIndex !== null && treeIndex !== undefined) { - handleShiftSelect(pathToTheClickedNode, firstSelectedIndex, orderTreeData, selectedNodes, extendedNode, collapsedNodes, setSelectedNodes); + handleShiftSelect( + pathToTheClickedNode, + firstSelectedIndex, + orderTreeData, + selectedNodes, + extendedNode, + collapsedNodes, + setSelectedNodes, + ); } else { handleSingleSelect(setSelectedNodes, node, extendedNode, orderTreeData, collapsedNodes, selectedNodes); } @@ -1116,7 +1125,3 @@ export const TreeContextProvider = (props: { children: JSX.Element }): JSX.Eleme {props.children} ); }; - - - - diff --git a/src/views/FormBuilder.tsx b/src/views/FormBuilder.tsx index dd70126f..064e84b0 100644 --- a/src/views/FormBuilder.tsx +++ b/src/views/FormBuilder.tsx @@ -26,8 +26,8 @@ const FormBuilder = (): JSX.Element => { const [translationErrors, setTranslationErrors] = useState>([]); const [translateLang, setTranslateLang] = useState(''); - const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node, path: Array }[]>([]); - const [isDeleteConfirmationModalVisible, setIsDeleteConfirmationModalVisible] = useState(false) + const [selectedNodes, setSelectedNodes] = React.useState<{ node: Node; path: Array }[]>([]); + const [isDeleteConfirmationModalVisible, setIsDeleteConfirmationModalVisible] = useState(false); const toggleFormDetails = useCallback(() => { setShowFormDetails(!showFormDetails); @@ -39,12 +39,10 @@ const FormBuilder = (): JSX.Element => { return newPath; }; - const isParentSelected = ( - path: string[], - ): boolean => { + const isParentSelected = (path: string[]): boolean => { for (const pathString of path) { // Check if any of the selectedNodes have a title that matches the pathString - if (selectedNodes.some(selectedNode => selectedNode.node.title === pathString)) { + if (selectedNodes.some((selectedNode) => selectedNode.node.title === pathString)) { return false; } } @@ -53,17 +51,17 @@ const FormBuilder = (): JSX.Element => { const handleOnMultipleDelete = () => { selectedNodes.map((item) => { - const isDeletable = isParentSelected(item.path.slice(-2, -1)) + const isDeletable = isParentSelected(item.path.slice(-2, -1)); if (!isDeletable) { return; } dispatch(deleteItemAction(item.node.title, treePathToOrderArray(item.path))); - }) - setSelectedNodes([]) - setIsDeleteConfirmationModalVisible(false) - } + }); + setSelectedNodes([]); + setIsDeleteConfirmationModalVisible(false); + }; return ( <> @@ -74,15 +72,18 @@ const FormBuilder = (): JSX.Element => { translationErrors={translationErrors} setTranslationErrors={setTranslationErrors} /> - {selectedNodes.length > 0 &&
-
- setSelectedNodes([])} /> - {selectedNodes.length} selected + {selectedNodes.length > 0 && ( +
+
+ setSelectedNodes([])} /> + {selectedNodes.length} selected +
+
setIsDeleteConfirmationModalVisible(true)}> + + Delete +
-
setIsDeleteConfirmationModalVisible(true)}> - - Delete
-
} + )}
{ isOpen={showFormDetails} /> - +
); From a5598e367ab06a569a553941d21863afc432154b Mon Sep 17 00:00:00 2001 From: anishapant21 Date: Mon, 12 Aug 2024 19:02:37 -0400 Subject: [PATCH 24/24] Remove vscode settings --- .vscode/settings.json | 24 +++++++++--------------- package.json | 2 +- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9d522f78..94b166ae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,10 @@ { - "typescript.tsdk": "node_modules\\typescript\\lib", - "eslint.enable": true, - "eslint.alwaysShowStatus": true, - "eslint.lintTask.enable": true, - "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], - "editor.formatOnSave": true, - "prettier.requireConfig": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[xml]": { - "editor.defaultFormatter": "redhat.vscode-xml" - } -} \ No newline at end of file + "typescript.tsdk": "node_modules\\typescript\\lib", + "eslint.enable": true, + "eslint.alwaysShowStatus": true, + "eslint.lintTask.enable": true, + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "editor.formatOnSave": true, + "prettier.requireConfig": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/package.json b/package.json index e48bda8f..9cacea82 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "remove-markdown": "^0.5.0" }, "scripts": { - "start": "export NODE_OPTIONS=--openssl-legacy-provider && react-scripts start", + "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "lint": "eslint ./src/**/*.{js,ts,tsx} --quiet --fix",