diff --git a/frontend/package.json b/frontend/package.json index e88734239d5..60caf543cb7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -158,8 +158,8 @@ "@patternfly/react-code-editor": "~6.4.0", "@patternfly/react-component-groups": "~6.4.0", "@patternfly/react-core": "~6.4.0", - "@patternfly/react-data-view": "6.4.0-prerelease.12", - "@patternfly/react-drag-drop": "~6.4.0", + "@patternfly/react-data-view": "~6.4.0-prerelease.12", + "@patternfly/react-drag-drop": "~6.5.0-prerelease.38", "@patternfly/react-icons": "~6.4.0", "@patternfly/react-log-viewer": "~6.3.0", "@patternfly/react-styles": "~6.4.0", diff --git a/frontend/packages/console-app/locales/en/console-app.json b/frontend/packages/console-app/locales/en/console-app.json index d134b8f810e..82d0b2f8ddf 100644 --- a/frontend/packages/console-app/locales/en/console-app.json +++ b/frontend/packages/console-app/locales/en/console-app.json @@ -346,7 +346,6 @@ "Navigation": "Navigation", "Pinned resources": "Pinned resources", "Main navigation": "Main navigation", - "Drag to reorder": "Drag to reorder", "Unpin": "Unpin", "Remove from navigation?": "Remove from navigation?", "Remove": "Remove", diff --git a/frontend/packages/console-app/src/components/nav/NavItemResource.tsx b/frontend/packages/console-app/src/components/nav/NavItemResource.tsx index 4a23db19a9c..5ca0a057968 100644 --- a/frontend/packages/console-app/src/components/nav/NavItemResource.tsx +++ b/frontend/packages/console-app/src/components/nav/NavItemResource.tsx @@ -1,6 +1,8 @@ import type { FC } from 'react'; import { useMemo, useCallback } from 'react'; import { NavItem } from '@patternfly/react-core'; +import { css } from '@patternfly/react-styles'; +import navStyles from '@patternfly/react-styles/css/components/Nav/nav'; import type { ResourceNSNavItem } from '@console/dynamic-plugin-sdk'; import { ALL_NAMESPACES_KEY } from '@console/dynamic-plugin-sdk/src/constants'; import { referenceForExtensionModel } from '@console/internal/module/k8s'; @@ -19,6 +21,7 @@ export const NavItemResource: FC = ({ namespaced, className, dataAttributes, + listItem = true, ...navLinkProps }) => { const [activeNamespace] = useActiveNamespace(); @@ -41,6 +44,20 @@ export const NavItemResource: FC = ({ : `/k8s/cluster/${resourceReference}`, [namespaced, resourceReference, lastNamespace, activeNamespace], ); + + if (!listItem) { + return ( +
+ +
+ ); + } return ( @@ -51,4 +68,6 @@ export const NavItemResource: FC = ({ export type NavItemResourceProps = Omit & Pick & { namespaced: boolean; + /** When false, renders a div instead of li for use in non-list contexts like drag-and-drop. */ + listItem?: boolean; }; diff --git a/frontend/packages/console-app/src/components/nav/PerspectiveNav.scss b/frontend/packages/console-app/src/components/nav/PerspectiveNav.scss index 55ffc4675fd..a39dba00b40 100644 --- a/frontend/packages/console-app/src/components/nav/PerspectiveNav.scss +++ b/frontend/packages/console-app/src/components/nav/PerspectiveNav.scss @@ -23,10 +23,14 @@ border: none; } } - &--dragging { - .oc-pinned-resource__unpin-button, - .oc-pinned-resource__drag-button { - opacity: 0; - } +} + +.co-pinned-resource-item { + display: flex; + margin-bottom: var(--pf-t--global--spacer--sm); + + .pf-v6-c-nav__link { + padding-block: 0; + padding-inline-end: 0; } } diff --git a/frontend/packages/console-app/src/components/nav/PerspectiveNav.tsx b/frontend/packages/console-app/src/components/nav/PerspectiveNav.tsx index f2d66ffac3b..32719495ef0 100644 --- a/frontend/packages/console-app/src/components/nav/PerspectiveNav.tsx +++ b/frontend/packages/console-app/src/components/nav/PerspectiveNav.tsx @@ -1,14 +1,14 @@ -import type { FC, ReactElement } from 'react'; -import { useState, useEffect, useMemo } from 'react'; -import { NavGroup, NavList } from '@patternfly/react-core'; -import { css } from '@patternfly/react-styles'; +import type { FC } from 'react'; +import { useCallback, useState, useEffect, useMemo } from 'react'; +import { NavList } from '@patternfly/react-core'; +import type { DragDropSortProps, DraggableObject } from '@patternfly/react-drag-drop'; +import { DragDropSort } from '@patternfly/react-drag-drop'; import { useTranslation } from 'react-i18next'; import { useActivePerspective, isNavSection, isNavItem, } from '@console/dynamic-plugin-sdk/src/lib-core'; -import withDragDropContext from '@console/internal/components/utils/drag-drop-context'; import { modelFor } from '@console/internal/module/k8s'; import { usePinnedResources } from '@console/shared/src/hooks/usePinnedResources'; import PinnedResource from './PinnedResource'; @@ -23,7 +23,6 @@ const PerspectiveNav: FC<{}> = () => { const allNavExtensions = useNavExtensionsForPerspective(activePerspective); const [pinnedResources, setPinnedResources, pinnedResourcesLoaded] = usePinnedResources(); const [validPinnedResources, setValidPinnedResources] = useState([]); - const [isDragged, setIsDragged] = useState(false); const { t } = useTranslation(); useEffect(() => { @@ -36,29 +35,29 @@ const PerspectiveNav: FC<{}> = () => { return getSortedNavExtensions(topLevelNavExtensions); }, [allNavExtensions]); - const getPinnedItems = (): ReactElement[] => - validPinnedResources.map((resource, idx) => ( - 1} - /> - )); + const draggableItems = useMemo(() => { + return validPinnedResources.map((res, idx) => ({ + id: res, + props: { className: 'co-pinned-resource-item' }, + content: ( + + ), + })); + }, [validPinnedResources, setPinnedResources]); - const NavGroupWithDnd = withDragDropContext(() => ( - - {getPinnedItems()} - - )); + const onDrop = useCallback( + (_, newItems) => { + const newOrder = newItems.map((item) => item.id as string); + setPinnedResources(newOrder); + }, + [setPinnedResources], + ); // We have to use NavList if there is at least one extension that will render an
  • , but we // can't use NavList if there are no extensions that render an
  • @@ -71,7 +70,18 @@ const PerspectiveNav: FC<{}> = () => { {orderedNavExtensions.map((extension) => ( ))} - {pinnedResourcesLoaded && validPinnedResources?.length > 0 ? : null} + {pinnedResourcesLoaded && validPinnedResources?.length > 0 ? ( +
    + {draggableItems.length === 1 ? ( + draggableItems[0].content + ) : ( + + )} +
    + ) : null} ); diff --git a/frontend/packages/console-app/src/components/nav/PinnedResource.scss b/frontend/packages/console-app/src/components/nav/PinnedResource.scss deleted file mode 100644 index 7d88d378c97..00000000000 --- a/frontend/packages/console-app/src/components/nav/PinnedResource.scss +++ /dev/null @@ -1,60 +0,0 @@ -.oc-pinned-resource.pf-v6-c-nav__item { - .pf-v6-c-nav__link { - display: block; - flex-grow: 1; - overflow: hidden; - position: relative; - text-overflow: ellipsis; - white-space: nowrap; - - &:hover { - --pf-v6-c-nav__section-title--PaddingRight: 30px; - .oc-pinned-resource__unpin-button, - .oc-pinned-resource__drag-button { - .oc-pinned-resource__delete-icon, - .oc-pinned-resource__drag-icon { - opacity: 1; - } - } - } - } - - .oc-pinned-resource__unpin-button { - background-color: transparent !important; - padding: 0; - position: absolute; - right: 0; - - .oc-pinned-resource__delete-icon { - opacity: 0; - } - } - - .oc-pinned-resource__drag-button { - background-color: transparent !important; - cursor: move; - left: -10px; - padding: 0; - position: absolute; - - .oc-pinned-resource__drag-icon { - opacity: 0; - } - } - - .oc-pinned-resource__unpin-button:focus-visible, - .oc-pinned-resource__drag-button:focus-visible { - .oc-pinned-resource__delete-icon, - .oc-pinned-resource__drag-icon { - opacity: 1; - } - } -} - -.oc-pinned-resource--dragging { - outline: 1px dashed var(--pf-t--global--border--color--on-secondary); - overflow: hidden; - div { - opacity: 0.5; - } -} diff --git a/frontend/packages/console-app/src/components/nav/PinnedResource.tsx b/frontend/packages/console-app/src/components/nav/PinnedResource.tsx index ef56fdc2db8..a3217f1e50a 100644 --- a/frontend/packages/console-app/src/components/nav/PinnedResource.tsx +++ b/frontend/packages/console-app/src/components/nav/PinnedResource.tsx @@ -1,37 +1,25 @@ -import type { FC, MouseEvent, ReactElement } from 'react'; -import { Button } from '@patternfly/react-core'; -import { GripVerticalIcon } from '@patternfly/react-icons/dist/esm/icons/grip-vertical-icon'; +import type { FC, MouseEvent } from 'react'; +import { Button, Flex, FlexItem, Truncate } from '@patternfly/react-core'; import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; -import { css } from '@patternfly/react-styles'; -import { debounce } from 'lodash'; -import { useDrag, useDrop } from 'react-dnd'; import { useTranslation } from 'react-i18next'; import type { K8sModel } from '@console/internal/module/k8s'; import { modelFor } from '@console/internal/module/k8s'; import { useK8sModel } from '@console/shared/src/hooks/useK8sModel'; -import './PinnedResource.scss'; import { NavItemResource } from './NavItemResource'; import useConfirmNavUnpinModal from './useConfirmNavUnpinModal'; -type PinnedResourceProps = { - resourceRef?: string; - navResources?: string[]; - onChange?: (pinnedResources: string[]) => void; - idx?: number; - draggable?: boolean; - onReorder?: (pinnedResources: string[]) => void; - onDrag?: (dragging: boolean) => void; -}; - -type DraggableButtonProps = { - dragRef?: (node) => void | null; -}; +interface PinnedResourceProps { + resourceRef: string; + navResources: string[]; + onChange: (pinnedResources: string[]) => void; + idx: number; +} -type RemoveButtonProps = { - resourceRef?: string; - navResources?: string[]; - onChange?: (pinnedResources: string[]) => void; -}; +interface RemoveButtonProps { + resourceRef: string; + navResources: string[]; + onChange: (pinnedResources: string[]) => void; +} export type DragItem = { idx: number; @@ -39,20 +27,6 @@ export type DragItem = { type: string; }; -const DraggableButton: FC = ({ dragRef }) => { - const { t } = useTranslation(); - return ( -