diff --git a/view/src/components/Table/Table.tsx b/view/src/components/Table/Table.tsx index a17bd17..060a82c 100644 --- a/view/src/components/Table/Table.tsx +++ b/view/src/components/Table/Table.tsx @@ -552,7 +552,6 @@ function Table({ { - +export const mapValueData = ( + layout: TableLayout, + data: { [key: string]: ITableData[] } +) => { const processEntry = (entry: ITableData, rowMapping: IRowMapping) => { let processedEntry: { [key: string]: any } = {}; - processedEntry["_"] = rowMapping.labelFormat.replace(/{([^}]+)}/g, (match, placeholder) => { - const entry_placeholder = entry[placeholder]; - // Remove double quotes from string literal - const format_entry_placeholder = - typeof entry_placeholder === "string" - ? entry[placeholder].replace(/['"]+/g, "") - : entry[placeholder]; - return format_entry_placeholder || ""; - }); + processedEntry["_"] = rowMapping.labelFormat.replace( + /{([^}]+)}/g, + (match, placeholder) => { + const entry_placeholder = entry[placeholder]; + // Remove double quotes from string literal + const format_entry_placeholder = + typeof entry_placeholder === "string" + ? entry[placeholder].replace(/['"]+/g, "") + : entry[placeholder]; + return format_entry_placeholder || ""; + } + ); // EVERY row should have an IRI, but just in case // default it to an empty string @@ -47,18 +52,18 @@ export const mapValueData = (layout: TableLayout, data: {[key: string]: ITableDa ): ITableData[] => { if (!data[rowMapping.id]) return []; return data[rowMapping.id] - .filter(row => { + .filter((row) => { // If there's no parentIri specified, we're looking for root nodes // If there is a parentIri, we're looking for children of a specific parent if (parentIri) { return row[`${parentId}Iri`] === parentIri; } else { - return !row['parentIri']; + return !row["parentIri"]; } }) .map((row: ITableData) => { let processedRow = processEntry(row, rowMapping); - + // Save the rowType for row-specific actions in the Table // such as right click context menus, etc. processedRow["rowType"] = rowMapping.id; @@ -68,24 +73,27 @@ export const mapValueData = (layout: TableLayout, data: {[key: string]: ITableDa // If the row is recursive, get its children and add to allChildren // Otherwise, check for subRowMappings if (rowMapping.isRecursive) { - children = recursiveMapper(rowMapping, 'parent', row.iri); + children = recursiveMapper(rowMapping, "parent", row.iri); } else if (rowMapping.subRowMappings) { - children = rowMapping.subRowMappings.flatMap((subMapping: IRowMapping) => recursiveMapper(subMapping, rowMapping.id, row.iri)); + children = rowMapping.subRowMappings.flatMap( + (subMapping: IRowMapping) => + recursiveMapper(subMapping, rowMapping.id, row.iri) + ); } if (children.length > 0) { processedRow["children"] = children; } - + return processedRow; }); - } + }; return recursiveMapper(layout.rowMapping); -} +}; /** - * Styles the font color as a tailwind class. Each returned string is a concatenation of Tailwind CSS classes + * Styles the font color as a tailwind class. Each returned string is a concatenation of Tailwind CSS classes * * @deprecated * This function is not generic for all OML models. Remove after v1.0.0. @@ -94,21 +102,21 @@ export const mapValueData = (layout: TableLayout, data: {[key: string]: ITableDa export const getLifecycleStateStyles = (state: CMState) => { switch (state) { case CMState.Proposed: - return 'text-[color:var(--vscode-debugIcon-stepOverForeground)]'; // blue for proposed + return "text-[color:var(--vscode-debugIcon-stepOverForeground)]"; // blue for proposed case CMState.Preliminary: - return 'text-[color:var(--vscode-gitDecoration-untrackedResourceForeground)]'; // green for preliminary + return "text-[color:var(--vscode-gitDecoration-untrackedResourceForeground)]"; // green for preliminary case CMState.Threat: - return 'text-[color:var(--vscode-charts-purple)]'; // purple for threat + return "text-[color:var(--vscode-charts-purple)]"; // purple for threat case CMState.Baseline: - return ''; // default for baseline + return ""; // default for baseline case CMState.Deprecated: - return 'line-through'; // default + strike through for deprecated + return "line-through"; // default + strike through for deprecated case CMState.Retracted: - return 'text-[color:var(--vscode-charts-red)] line-through'; // red + strike through for retracted + return "text-[color:var(--vscode-charts-red)] line-through"; // red + strike through for retracted default: - return ''; + return ""; } -} +}; /** * Sets the font style based on a conditional for a given row. @@ -127,12 +135,16 @@ export const setFontStyle = ( row: Row ) => { // Remove redundant double quotes from string - const formattedConditional = row.original[conditional].slice(1, -1) + const formattedConditional = row.original[conditional].slice(1, -1); return styles[formattedConditional]; }; // Helper to get row range on Shift + Click to select multiple rows -export function getRowRange(rows: Array>, idA: string, idB: string) { +export function getRowRange( + rows: Array>, + idA: string, + idB: string +) { const range: Array> = []; // Don't do anything if we Shift + Clicked the same as previous row if (idA === idB) return range; @@ -160,4 +172,4 @@ export function getRowRange(rows: Array>, idA: strin } return range; -} \ No newline at end of file +} diff --git a/view/src/components/Tree/Tree.tsx b/view/src/components/Tree/Tree.tsx index f4e0361..4640b25 100644 --- a/view/src/components/Tree/Tree.tsx +++ b/view/src/components/Tree/Tree.tsx @@ -1,22 +1,28 @@ -import React, { useState } from 'react' +import React, { useState } from "react"; import { IconChevronRight, IconChevronDown, - IconChevronUp -} from '@nasa-jpl/react-stellar' -import { cloneDeep } from 'lodash'; -import { VSCodeTextField } from '@vscode/webview-ui-toolkit/react'; + IconChevronUp, +} from "@nasa-jpl/react-stellar"; +import { cloneDeep } from "lodash"; +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; import useResizeObserver from "use-resize-observer"; -import escaperegexp from 'lodash.escaperegexp' -import { NodeApi, NodeRendererProps, Tree as ArboristTree, TreeApi } from "react-arborist"; -import ITableData from '../../interfaces/ITableData' -import './Tree.css' -import { FillFlexParent } from './FillFlexParent'; -import { TreeLayout } from '../../interfaces/DataLayoutsType'; -import { useWizards } from '../../providers/WizardController'; -import { useVisionTree } from './use-vision-tree'; -import useContextMenu from '../ContextMenu/useContextMenu'; -import ContextMenu from '../ContextMenu/ContextMenu'; +import escaperegexp from "lodash.escaperegexp"; +import { + NodeApi, + NodeRendererProps, + Tree as ArboristTree, + TreeApi, +} from "react-arborist"; +import ITableData from "../../interfaces/ITableData"; +import "./Tree.css"; +import { FillFlexParent } from "./FillFlexParent"; +import { TreeLayout } from "../../interfaces/DataLayoutsType"; +import { useWizards } from "../../providers/WizardController"; +import { useVisionTree } from "./use-vision-tree"; +import useContextMenu from "../ContextMenu/useContextMenu"; +import ContextMenu from "../ContextMenu/ContextMenu"; +import { setFontStyle } from "./treeUtils"; const INDENT_STEP = 30; @@ -42,20 +48,22 @@ function Tree({ webviewPath, modelCommands, layout, - onClickRow = () => { } + onClickRow = () => {}, }: { - className?: string - rowData: ITableData[] - webviewPath: string + className?: string; + rowData: ITableData[]; + webviewPath: string; modelCommands: Record>; - layout: TreeLayout - onClickRow?: Function + layout: TreeLayout; + onClickRow?: Function; }) { const { openWizard } = useWizards(); const { rightClick, setRightClick, coordinates, setCoordinates } = useContextMenu(); const { data, onToggle, onMove } = useVisionTree(rowData); - const [currentTree, setCurrentTree] = useState | null | undefined>(null); + const [currentTree, setCurrentTree] = useState< + TreeApi | null | undefined + >(null); const [currentRowData, setCurrentRowData] = useState(rowData); const [flatData, setFlatData] = useState([]); const [active, setActive] = useState(null); @@ -69,7 +77,9 @@ function Tree({ const [currentResultIndex, setCurrentResultIndex] = useState(0); const searchInputRef = React.useRef(null); - const { ref: headerRef, height: headerHeight = 0 } = useResizeObserver({ box: "border-box" }); + const { ref: headerRef, height: headerHeight = 0 } = useResizeObserver({ + box: "border-box", + }); React.useEffect(() => { // flat data used for search functionality @@ -82,54 +92,83 @@ function Tree({ }, [currentTree, searchTerm]); const toggleExpandOrCollapseAll = () => { - setIsAllRowsExpanded(previousExpansionState => { - + setIsAllRowsExpanded((previousExpansionState) => { if (previousExpansionState === false) currentTree?.openAll(); else currentTree?.closeAll(); - return !previousExpansionState + return !previousExpansionState; }); - } + }; const navigateResults = (forward: boolean) => { - setCurrentResultIndex(prev => { + setCurrentResultIndex((prev) => { let id; if (forward) { - id = (prev + 1) % searchResults.length + id = (prev + 1) % searchResults.length; } else { - id = prev > 0 ? prev - 1 : searchResults.length - 1 + id = prev > 0 ? prev - 1 : searchResults.length - 1; } if (searchResults.length === 0 || id > searchResults.length) return -1; - currentTree?.scrollTo(searchResults[id].id, "auto") + currentTree?.scrollTo(searchResults[id].id, "auto"); return id; }); }; // Used for handling Context Menu data-vscode-context (useMemo makes operation more efficient) - const { iriArray, labelArray, maturityArray, shouldAllowDelete, rowTypesObject } = React.useMemo(() => { - return selectedRows.reduce<{ iriArray: string[], labelArray: string[], maturityArray: string[], shouldAllowDelete: boolean, rowTypesObject: Record }>((acc, node: NodeApi) => { - acc.iriArray.push(node.data.iri); - acc.maturityArray.push(node.data.maturity || ""); - acc.shouldAllowDelete = acc.shouldAllowDelete - && !["Baseline", "Retracted", "Deprecated"].includes(node.data.maturity || "") - && node.data.shouldAllowDelete === true; - acc.labelArray.push(node.data.name); - - // Check if the rowType already exists in rowTypesObject, if not add a new array - if (!acc.rowTypesObject[node.data.rowType]) { - acc.rowTypesObject[node.data.rowType] = []; - } + const { + iriArray, + labelArray, + maturityArray, + shouldAllowDelete, + rowTypesObject, + } = React.useMemo(() => { + return selectedRows.reduce<{ + iriArray: string[]; + labelArray: string[]; + maturityArray: string[]; + shouldAllowDelete: boolean; + rowTypesObject: Record; + }>( + (acc, node: NodeApi) => { + acc.iriArray.push(node.data.iri); + acc.maturityArray.push(node.data.maturity || ""); + acc.shouldAllowDelete = + acc.shouldAllowDelete && + !["Baseline", "Retracted", "Deprecated"].includes( + node.data.maturity || "" + ) && + node.data.shouldAllowDelete === true; + acc.labelArray.push(node.data.name); - // Add the iri to the corresponding rowType array in rowTypesObject - acc.rowTypesObject[node.data.rowType].push(node.data.iri); + // Check if the rowType already exists in rowTypesObject, if not add a new array + if (!acc.rowTypesObject[node.data.rowType]) { + acc.rowTypesObject[node.data.rowType] = []; + } - return acc; - }, { iriArray: [], labelArray: [], maturityArray: [], shouldAllowDelete: true, rowTypesObject: {} }); + // Add the iri to the corresponding rowType array in rowTypesObject + acc.rowTypesObject[node.data.rowType].push(node.data.iri); + + return acc; + }, + { + iriArray: [], + labelArray: [], + maturityArray: [], + shouldAllowDelete: true, + rowTypesObject: {}, + } + ); }, [selectedRows]); const oneRowTypeSelected = Object.keys(rowTypesObject).length === 1; - const selectedRowType = oneRowTypeSelected ? Object.keys(rowTypesObject)[0] : ''; - const hasDiagram = !!layout.diagrams?.["all-rows"] || Object.keys(layout.diagrams || {}).includes(selectedRowType); - const diagram = layout.diagrams?.["all-rows"] || (hasDiagram ? layout.diagrams?.[selectedRowType] : ""); + const selectedRowType = oneRowTypeSelected + ? Object.keys(rowTypesObject)[0] + : ""; + const hasDiagram = + !!layout.diagrams?.["all-rows"] || + Object.keys(layout.diagrams || {}).includes(selectedRowType); + const diagram = + layout.diagrams?.["all-rows"] || + (hasDiagram ? layout.diagrams?.[selectedRowType] : ""); const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "f" && (event.metaKey || event.ctrlKey)) { @@ -139,10 +178,19 @@ function Tree({ } }; - const handleDeleteRows = async (evt: React.KeyboardEvent, iriArray: string[], labelArray: string[]) => { - if (evt.key !== "Backspace" || iriArray.length === 0 || shouldAllowDelete === false) return; - openWizard("DeleteElementsWizard", { iriArray, labelArray }) - } + const handleDeleteRows = async ( + evt: React.KeyboardEvent, + iriArray: string[], + labelArray: string[] + ) => { + if ( + evt.key !== "Backspace" || + iriArray.length === 0 || + shouldAllowDelete === false + ) + return; + openWizard("DeleteElementsWizard", { iriArray, labelArray }); + }; React.useEffect(() => { window.addEventListener("keydown", handleKeyDown); @@ -175,23 +223,24 @@ function Tree({ e.stopPropagation(); toggleExpandOrCollapseAll(); }, - className: "flex" + className: "flex", }} > - {isAllRowsExpanded ? + {isAllRowsExpanded ? ( : + /> + ) : ( - } + )}
@@ -200,31 +249,33 @@ function Tree({ ref={searchInputRef} className="vscode-input-rounded w-1/2 mt-[1.25px]" type="text" - value={(searchTerm ?? '') as string} - onKeyDown={e => { - if (e.key === 'Enter') { + value={(searchTerm ?? "") as string} + onKeyDown={(e) => { + if (e.key === "Enter") { navigateResults(true); } }} - onInput={e => { + onInput={(e) => { // @ts-ignore const newSearchTerm = e.currentTarget.value; setSearchTerm(newSearchTerm); const newResults = newSearchTerm - ? flatData.filter(node => node.name.toLowerCase().includes(newSearchTerm.toLowerCase())) + ? flatData.filter((node) => + node.name + .toLowerCase() + .includes(newSearchTerm.toLowerCase()) + ) : []; setSearchResults(newResults); setCurrentResultIndex(newResults.length > 0 ? 0 : -1); - if (newResults.length > 0) currentTree?.scrollTo(newResults[0].id, "auto") + if (newResults.length > 0) + currentTree?.scrollTo(newResults[0].id, "auto"); }} placeholder={`Search...`} /> {searchResults.length > 0 && (
- -
- handleDeleteRows(e, iriArray, labelArray)}> + handleDeleteRows(e, iriArray, labelArray)} + > {(dimens) => ( setCurrentTree(t)} openByDefault={false} selection={active?.id} - className={'vision-tree'} - rowClassName={'row'} + className={"vision-tree"} + rowClassName={"row"} rowHeight={38} indent={INDENT_STEP} overscanCount={8} disableEdit={true} onMove={onMove} onSelect={(selected) => setSelectedRows(selected)} - onActivate={(node) => { setActive(node.data); onClickRow(node.data) }} + onActivate={(node) => { + setActive(node.data); + onClickRow(node.data); + }} onFocus={(node) => setFocused(node.data)} onToggle={() => { if (isAllRowsExpanded) setIsAllRowsExpanded(false); @@ -286,15 +340,17 @@ function Tree({ }); }} > - {(props) => + {(props) => ( } + /> + )} )} @@ -308,7 +364,7 @@ function Tree({ /> )} - ) + ); } function Node({ @@ -316,59 +372,91 @@ function Node({ tree, style, dragHandle, + layout, oneRowTypeSelected, selectedRowType, searchTerm, searchResults, - currentResultIndex + currentResultIndex, }: NodeRendererProps & { - oneRowTypeSelected: boolean, - selectedRowType: string, - searchTerm: string, - searchResults: ITableData[], - currentResultIndex: number + layout: TreeLayout; + oneRowTypeSelected: boolean; + selectedRowType: string; + searchTerm: string; + searchResults: ITableData[]; + currentResultIndex: number; }) { const indentSize = Number.parseFloat(`${style.paddingLeft || 0}`); - const isCurrentResult = searchResults[currentResultIndex]?.id === node.data.id; + const isCurrentResult = + searchResults[currentResultIndex]?.id === node.data.id; const createHighlight = (text: string, highlight: string) => { - return text.split(new RegExp(`(${escaperegexp(highlight)})`, 'gi')).map((chunk, i) => ( - chunk.toLowerCase() === highlight.toLowerCase() ? - {chunk} : - chunk - )); + return text + .split(new RegExp(`(${escaperegexp(highlight)})`, "gi")) + .map((chunk, i) => + chunk.toLowerCase() === highlight.toLowerCase() ? ( + + {chunk} + + ) : ( + chunk + ) + ); }; return (
{ // on right-click IF row isn't selected if (e.button === 2 && !node.isSelected) { // highlight row just like left click without shift/cmd click - tree.deselectAll() - node.select() - node.activate() + tree.deselectAll(); + node.select(); + node.activate(); } }} data-vscode-context={`{ - "oneRowTypeSelected": ${oneRowTypeSelected && node.data.rowType == selectedRowType}, + "oneRowTypeSelected": ${ + oneRowTypeSelected && node.data.rowType == selectedRowType + }, "preventDefaultContextMenuItems": true} `} > -
-
+
+
{new Array(indentSize / INDENT_STEP).fill(0).map((_, index) => { return
; })}
- node.isInternal && node.toggle()} node={node} /> - - {searchTerm ? createHighlight(node.data.name, searchTerm) : node.data.name} + node.isInternal && node.toggle()} + node={node} + /> + + {searchTerm + ? createHighlight(node.data.name, searchTerm) + : node.data.name}
@@ -396,36 +484,40 @@ function Input({ node }: { node: NodeApi }) { ); } -function FolderArrow({ node, onClick }: { node: NodeApi; onClick: () => void }) { - return ( - node.isInternal ? ( - - ) : null - ); +function FolderArrow({ + node, + onClick, +}: { + node: NodeApi; + onClick: () => void; +}) { + return node.isInternal ? ( + + ) : null; } -export default Tree \ No newline at end of file +export default Tree; diff --git a/view/src/components/Tree/treeUtils.ts b/view/src/components/Tree/treeUtils.ts index ebfd219..5bc3d64 100644 --- a/view/src/components/Tree/treeUtils.ts +++ b/view/src/components/Tree/treeUtils.ts @@ -4,32 +4,41 @@ import { IRowMapping, TreeLayout } from "../../interfaces/DataLayoutsType"; // Process the TREE json data into content // that React-Table can render. -export const mapTreeValueData = (layout: TreeLayout, data: {[key: string]: ITableData[]}) => { - - const processEntry = (entry: ITableData, rowMapping: IRowMapping, identifier: string) => { +export const mapTreeValueData = ( + layout: TreeLayout, + data: { [key: string]: ITableData[] } +) => { + const processEntry = ( + entry: ITableData, + rowMapping: IRowMapping, + identifier: string + ) => { let processedEntry: { [key: string]: any } = {}; // React Arborist requires name and ID parameters - processedEntry["name"] = rowMapping.labelFormat.replace(/{([^}]+)}/g, (match, placeholder) => { - const entry_placeholder = entry[placeholder]; - // Remove double quotes from string literal - const format_entry_placeholder = - typeof entry_placeholder === "string" - ? entry[placeholder].replace(/['"]+/g, "") - : entry[placeholder]; - return format_entry_placeholder || ""; - }); - + processedEntry["name"] = rowMapping.labelFormat.replace( + /{([^}]+)}/g, + (match, placeholder) => { + const entry_placeholder = entry[placeholder]; + // Remove double quotes from string literal + const format_entry_placeholder = + typeof entry_placeholder === "string" + ? entry[placeholder].replace(/['"]+/g, "") + : entry[placeholder]; + return format_entry_placeholder || ""; + } + ); + // Add ID for row sorting processedEntry["id"] = identifier; processedEntry["shouldAllowDelete"] = rowMapping.canDeleteElements ?? true; - + // EVERY row should have an IRI, but just in case // default it to an empty string processedEntry["iri"] = entry["iri"] || ""; - + // Include maturity for color coding processedEntry["maturity"] = entry["maturity"] || ""; - + return processedEntry; }; @@ -41,102 +50,132 @@ export const mapTreeValueData = (layout: TreeLayout, data: {[key: string]: ITabl ): ITableData[] => { if (!data[rowMapping.id]) return []; return data[rowMapping.id] - .filter(row => { - // If there's no parentIri specified, we're looking for root nodes - // If there is a parentIri, we're looking for children of a specific parent - if (parentIri) { - return row[`${parentId}Iri`] === parentIri; - } else { - return !row['parentIri']; - } - }) - .map((row: ITableData, index: number) => { - let identifier = recursiveIdentifier ? `${recursiveIdentifier}-${index}` : `${index}`; - let processedRow = processEntry(row, rowMapping, identifier); - - // Save the rowType for row-specific actions in the Table - // such as right click context menus, etc. - processedRow["rowType"] = rowMapping.id; - - let children: ITableData[] = []; - - // If the row is recursive, get its children and add to allChildren - // Otherwise, check for subRowMappings - if (rowMapping.isRecursive) { - children = recursiveMapper(rowMapping, 'parent', row.iri, identifier); - } else if (rowMapping.subRowMappings) { - children = rowMapping.subRowMappings.flatMap((subMapping: IRowMapping) => recursiveMapper(subMapping, rowMapping.id, row.iri, identifier)); - } + .filter((row) => { + // If there's no parentIri specified, we're looking for root nodes + // If there is a parentIri, we're looking for children of a specific parent + if (parentIri) { + return row[`${parentId}Iri`] === parentIri; + } else { + return !row["parentIri"]; + } + }) + .map((row: ITableData, index: number) => { + let identifier = recursiveIdentifier + ? `${recursiveIdentifier}-${index}` + : `${index}`; + let processedRow = processEntry(row, rowMapping, identifier); - if (children.length > 0) { - processedRow["children"] = children; - } - - return processedRow; - }); - } + // Save the rowType for row-specific actions in the Table + // such as right click context menus, etc. + processedRow["rowType"] = rowMapping.id; + + let children: ITableData[] = []; + + // If the row is recursive, get its children and add to allChildren + // Otherwise, check for subRowMappings + if (rowMapping.isRecursive) { + children = recursiveMapper(rowMapping, "parent", row.iri, identifier); + } else if (rowMapping.subRowMappings) { + children = rowMapping.subRowMappings.flatMap( + (subMapping: IRowMapping) => + recursiveMapper(subMapping, rowMapping.id, row.iri, identifier) + ); + } + + if (children.length > 0) { + processedRow["children"] = children; + } + + return processedRow; + }); + }; return recursiveMapper(layout.rowMapping); -} +}; -// Each returned string is a concatenation of Tailwind CSS classes +/** + * Styles the font color as a tailwind class. Each returned string is a concatenation of Tailwind CSS classes + * + * @deprecated + * This function is not generic for all OML models. Remove after v1.0.0. + * + */ export const getLifecycleStateStyles = (state: CMState) => { switch (state) { case CMState.Proposed: - return 'text-[color:var(--vscode-debugIcon-stepOverForeground)]'; // blue for proposed + return "text-[color:var(--vscode-debugIcon-stepOverForeground)]"; // blue for proposed case CMState.Preliminary: - return 'text-[color:var(--vscode-gitDecoration-untrackedResourceForeground)]'; // green for preliminary + return "text-[color:var(--vscode-gitDecoration-untrackedResourceForeground)]"; // green for preliminary case CMState.Threat: - return 'text-[color:var(--vscode-charts-purple)]'; // purple for threat + return "text-[color:var(--vscode-charts-purple)]"; // purple for threat case CMState.Baseline: - return ''; // default for baseline + return ""; // default for baseline case CMState.Deprecated: - return 'line-through'; // default + strike through for deprecated + return "line-through"; // default + strike through for deprecated case CMState.Retracted: - return 'text-[color:var(--vscode-charts-red)] line-through'; // red + strike through for retracted + return "text-[color:var(--vscode-charts-red)] line-through"; // red + strike through for retracted default: - return ''; + return ""; } -} +}; + +/** + * Sets the font style based on a conditional for a given node. + * + * @remarks + * This method uses styles from {@link https://tailwindcss.com/ | TailwindCSS}. + * + * @param styles - The styles that come from the OML model. + * @param conditional - The conditional that determines which style to apply. + * @param row - The row on which to set the font style. + * + */ +export const setFontStyle = ( + styles: Record>, + conditional: string, + node: ITableData +) => { + // Remove redundant double quotes from string + const formattedConditional = node.data[conditional].slice(1, -1); + return styles[formattedConditional]; +}; export const areArraysOfObjectsEqual = ( ...arrays: [{ [x: string]: ITableData[] }] | any[] ) => { - // Check if lengths are equal - const lengthCheck = arrays.every((arr) => arr.length === arrays[0].length); - if (!lengthCheck) { + // Check if lengths are equal + const lengthCheck = arrays.every((arr) => arr.length === arrays[0].length); + if (!lengthCheck) { + return false; + } + + // Helper function to compare two objects + const areObjectsEqual = ( + obj1: { [x: string]: { toString: () => any } }, + obj2: { [x: string]: { toString: () => any } } + ) => { + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + // Check if number of keys is the same + if (keys1.length !== keys2.length) { return false; } - // Helper function to compare two objects - const areObjectsEqual = ( - obj1: { [x: string]: { toString: () => any } }, - obj2: { [x: string]: { toString: () => any } } - ) => { - const keys1 = Object.keys(obj1); - const keys2 = Object.keys(obj2); - - // Check if number of keys is the same - if (keys1.length !== keys2.length) { - return false; - } + // Check if values for each key are the same + return keys1.every((key) => obj1[key].toString() === obj2[key].toString()); + }; - // Check if values for each key are the same - return keys1.every( - (key) => obj1[key].toString() === obj2[key].toString() - ); - }; - - // Check if every object in the first array is present in the other arrays - const areArraysEqual = Object.values(arrays[0]).every((obj: any) => - arrays - .slice(1) - .every((arr) => - arr.some((arrObj: { [x: string]: { toString: () => any } }) => - areObjectsEqual(obj, arrObj) - ) + // Check if every object in the first array is present in the other arrays + const areArraysEqual = Object.values(arrays[0]).every((obj: any) => + arrays + .slice(1) + .every((arr) => + arr.some((arrObj: { [x: string]: { toString: () => any } }) => + areObjectsEqual(obj, arrObj) ) - ); + ) + ); - return areArraysEqual; -}; \ No newline at end of file + return areArraysEqual; +};