diff --git a/webui/src/Components/CollectionsNestingTable/CollectionsNestingTable.tsx b/webui/src/Components/CollectionsNestingTable/CollectionsNestingTable.tsx index 5a142b1f34..babeec8919 100644 --- a/webui/src/Components/CollectionsNestingTable/CollectionsNestingTable.tsx +++ b/webui/src/Components/CollectionsNestingTable/CollectionsNestingTable.tsx @@ -1,6 +1,9 @@ import React from 'react' import { capitalize } from 'lodash-es' -import { CollectionsNestingTableCollectionsList } from './CollectionsNestingTableGroupsList.js' +import { + CollectionItemsCollapseButtons, + CollectionsNestingTableCollectionsList, +} from './CollectionsNestingTableGroupsList.js' import type { CollectionsNestingTableCollection, CollectionsNestingTableItem } from './Types.js' import { useCollectionsListItemDrop } from './useItemDrop.js' import { @@ -9,6 +12,9 @@ import { } from './CollectionsNestingTableContext.js' import { CollectionsNestingTableCollectionContents } from './CollectionsNestingTableGroupContents.js' import { observer } from 'mobx-react-lite' +import { usePanelCollapseHelperContextForPanel } from '~/Helpers/CollapseHelper.js' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons' interface CollectionsNestingTableProps< TCollection extends CollectionsNestingTableCollection, @@ -64,17 +70,11 @@ export const CollectionsNestingTable = observer(function CollectionsNestingTable nestingLevel={0} /> - {(isDragging || ungroupedItems.length > 0) && collections.length > 0 && ( -
- Ungrouped {capitalize(itemName)}s -
- )} - - 0} + itemName={itemName} /> {items.length === 0 && ( @@ -87,6 +87,62 @@ export const CollectionsNestingTable = observer(function CollectionsNestingTable ) }) +// eslint-disable-next-line react-refresh/only-export-components +export const UNGROUPED_PANEL_ID = '__ungrouped__' + +const UngroupedSection = observer(function UngroupedSection({ + isDragging, + ungroupedItems, + hasCollections, + itemName, +}: { + isDragging: boolean + ungroupedItems: TItem[] + hasCollections: boolean + itemName: string +}) { + const collapseHelper = usePanelCollapseHelperContextForPanel(null, UNGROUPED_PANEL_ID) + const isCollapsed = collapseHelper.isCollapsed + + if (!(isDragging || ungroupedItems.length > 0) || !hasCollections) { + return ( + + ) + } + + return ( + <> +
+
+
+ + Ungrouped {capitalize(itemName)}s +
+ {!isCollapsed && ungroupedItems.length > 1 && ( +
e.stopPropagation()}> + item.id)} /> +
+ )} +
+
+ + {!isCollapsed && ( + + )} + + ) +}) + function getGroupedItems( allItems: TItem[], validCollections: CollectionsNestingTableCollection[] diff --git a/webui/src/Components/CollectionsNestingTable/CollectionsNestingTableGroupsList.tsx b/webui/src/Components/CollectionsNestingTable/CollectionsNestingTableGroupsList.tsx index 299f0f216d..270fa70842 100644 --- a/webui/src/Components/CollectionsNestingTable/CollectionsNestingTableGroupsList.tsx +++ b/webui/src/Components/CollectionsNestingTable/CollectionsNestingTableGroupsList.tsx @@ -1,12 +1,15 @@ -import React from 'react' +import React, { useCallback } from 'react' import { observer } from 'mobx-react-lite' -import { usePanelCollapseHelperContextForPanel } from '~/Helpers/CollapseHelper.js' +import { usePanelCollapseHelperContext, usePanelCollapseHelperContextForPanel } from '~/Helpers/CollapseHelper.js' import { CollectionsNestingTableDropZone } from './CollectionsNestingTableDropZone.js' import type { CollectionsNestingTableCollection, CollectionsNestingTableItem } from './Types.js' import { useCollectionListCollectionDrop } from './useCollectionDrop.js' import { CollectionsNestingTableCollectionRow } from './CollectionsNestingTableGroupRow.js' import { useCollectionsNestingTableContext } from './CollectionsNestingTableContext.js' import { CollectionsNestingTableCollectionContents } from './CollectionsNestingTableGroupContents.js' +import { CButton } from '@coreui/react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCompressArrowsAlt, faExpandArrowsAlt } from '@fortawesome/free-solid-svg-icons' interface CollectionsNestingTableCollectionsListProps< TCollection extends CollectionsNestingTableCollection, @@ -91,6 +94,9 @@ const CollectionsNestingTableCollectionSingle = observer(function CollectionsNes isCollapsed={isCollapsed} nestingLevel={nestingLevel} > + {!isCollapsed && itemsInCollection.length > 1 && ( + item.id)} /> + )} {!!GroupHeaderContent && } @@ -119,3 +125,37 @@ const CollectionsNestingTableCollectionSingle = observer(function CollectionsNes ) }) + +export const CollectionItemsCollapseButtons = observer(function CollectionItemsCollapseButtons({ + itemIds, +}: { + itemIds: string[] +}) { + const panelCollapseHelper = usePanelCollapseHelperContext() + + const hasCollapsed = itemIds.some((id) => panelCollapseHelper.isPanelCollapsed(null, id)) + const hasExpanded = itemIds.some((id) => !panelCollapseHelper.isPanelCollapsed(null, id)) + + const collapseAll = useCallback(() => { + panelCollapseHelper.setMultipleCollapsed(itemIds, true) + }, [panelCollapseHelper, itemIds]) + + const expandAll = useCallback(() => { + panelCollapseHelper.setMultipleCollapsed(itemIds, false) + }, [panelCollapseHelper, itemIds]) + + return ( + <> + {hasCollapsed && ( + + + + )} + {hasExpanded && ( + + + + )} + + ) +}) diff --git a/webui/src/Helpers/CollapseHelper.tsx b/webui/src/Helpers/CollapseHelper.tsx index 69608ead33..516b288122 100644 --- a/webui/src/Helpers/CollapseHelper.tsx +++ b/webui/src/Helpers/CollapseHelper.tsx @@ -18,6 +18,7 @@ export interface PanelCollapseHelper { canExpandAll(parentId: string | null, panelIds: string[]): boolean canCollapseAll(parentId: string | null, panelIds: string[]): boolean setPanelCollapsed: (panelId: string, collapsed: boolean) => void + setMultipleCollapsed: (panelIds: string[], collapsed: boolean) => void togglePanelCollapsed: (parentId: string | null, panelId: string) => void isPanelCollapsed: (parentId: string | null, panelId: string) => boolean } @@ -119,6 +120,16 @@ class PanelCollapseHelperStore implements PanelCollapseHelper { }) } + setMultipleCollapsed = (panelIds: string[], collapsed: boolean): void => { + runInAction(() => { + for (const panelId of panelIds) { + this.#ids.set(panelId, collapsed) + } + + this.#writeState() + }) + } + togglePanelCollapsed = (parentId: string | null, panelId: string): void => { runInAction(() => { const currentState = this.isPanelCollapsed(parentId, panelId) diff --git a/webui/src/Variables/CustomVariablesList.tsx b/webui/src/Variables/CustomVariablesList.tsx index a76f57ff53..42288d5a91 100644 --- a/webui/src/Variables/CustomVariablesList.tsx +++ b/webui/src/Variables/CustomVariablesList.tsx @@ -18,7 +18,10 @@ import { RootAppStoreContext } from '~/Stores/RootAppStore.js' import { observer } from 'mobx-react-lite' import { NonIdealState } from '~/Components/NonIdealState.js' import { Link } from '@tanstack/react-router' -import { CollectionsNestingTable } from '~/Components/CollectionsNestingTable/CollectionsNestingTable' +import { + CollectionsNestingTable, + UNGROUPED_PANEL_ID, +} from '~/Components/CollectionsNestingTable/CollectionsNestingTable' import type { CollectionsNestingTableCollection, CollectionsNestingTableItem, @@ -39,7 +42,11 @@ export const CustomVariablesListPage = observer(function CustomVariablesList() { const customVariableValues = useVariablesValuesForLabel('custom') const allVariableNames = useComputed( - () => [...Array.from(customVariables.customVariables.keys()), ...customVariables.allCustomVariableCollectionIds], + () => [ + ...Array.from(customVariables.customVariables.keys()), + ...customVariables.allCustomVariableCollectionIds, + UNGROUPED_PANEL_ID, + ], [customVariables] ) @@ -159,7 +166,11 @@ const ExpandCollapseButtons = observer(function ExpandCollapseButtons() { const { variablesStore: customVariables } = useContext(RootAppStoreContext) const rootCustomVariables = Array.from(customVariables.customVariables.keys()) // TODO - filter - const rootPanels = [...customVariables.rootCustomVariableCollections().map((c) => c.id), ...rootCustomVariables] + const rootPanels = [ + ...customVariables.rootCustomVariableCollections().map((c) => c.id), + ...rootCustomVariables, + UNGROUPED_PANEL_ID, + ] const panelCollapseHelper = usePanelCollapseHelperContext()