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()