mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 19:01:42 +00:00
kanban supports collapse (#7437)
* kanban supports collapse * optimize * optimize ui --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
}
|
||||
|
||||
.file-details-collapse .file-details-collapse-header .file-details-collapse-header-operation:hover {
|
||||
background-color: #DBDBDB;
|
||||
background-color: #EFEFEF;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
@@ -27,3 +27,25 @@
|
||||
color: #666666;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.sf-metadata-view-kanban-board-header .board-header-operation-btn {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.board-header-operation-btn .kanban-header-op-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 0.5rem;
|
||||
color: #666666;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.board-header-operation-btn .kanban-header-op-btn.kanban-header-collapse-btn:hover,
|
||||
.board-header-operation-btn .kanban-header-op-btn.kanban-more-operations-toggle:hover {
|
||||
background-color: #EFEFEF;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import CellFormatter from '../../../../../components/cell-formatter';
|
||||
import { gettext } from '../../../../../../utils/constants';
|
||||
import OpMenu from './op-menu';
|
||||
@@ -7,9 +8,11 @@ import { CellType } from '../../../../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Header = ({ readonly, haveFreezed, value, groupByColumn, cardsQuantity, onDelete, onFreezed, onUnFreezed }) => {
|
||||
const Header = ({ readonly, haveFreezed, value, groupByColumn, cardsQuantity, onDelete, onFreezed, onUnFreezed, isCollapsed, onCollapse }) => {
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
const headerRef = useRef();
|
||||
|
||||
const onMouseEnter = useCallback(() => {
|
||||
if (haveFreezed) return;
|
||||
setActive(true);
|
||||
@@ -20,10 +23,14 @@ const Header = ({ readonly, haveFreezed, value, groupByColumn, cardsQuantity, on
|
||||
setActive(false);
|
||||
}, [haveFreezed]);
|
||||
|
||||
const handelUnFreezed = useCallback((keepActive) => {
|
||||
const keepActive = useCallback((event) => {
|
||||
return event.target.className?.includes('kanban-header-op-btn') || event.target === headerRef.current;
|
||||
}, []);
|
||||
|
||||
const handelUnFreezed = useCallback((event) => {
|
||||
onUnFreezed();
|
||||
!keepActive && setActive(false);
|
||||
}, [onUnFreezed]);
|
||||
!keepActive(event) && setActive(false);
|
||||
}, [onUnFreezed, keepActive]);
|
||||
|
||||
const titleValue = useMemo(() => {
|
||||
if (!value || !groupByColumn) return null;
|
||||
@@ -31,9 +38,14 @@ const Header = ({ readonly, haveFreezed, value, groupByColumn, cardsQuantity, on
|
||||
return value;
|
||||
}, [value, groupByColumn]);
|
||||
|
||||
const handleCollapse = useCallback((event) => {
|
||||
onCollapse();
|
||||
!keepActive(event) && setActive(false);
|
||||
}, [onCollapse, keepActive]);
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-view-kanban-board-header" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<div className="sf-metadata-view-kanban-board-header-title">
|
||||
<div className="sf-metadata-view-kanban-board-header-title" ref={headerRef}>
|
||||
{value ? (
|
||||
<CellFormatter value={titleValue} field={groupByColumn} readonly={true} />
|
||||
) : (
|
||||
@@ -41,8 +53,15 @@ const Header = ({ readonly, haveFreezed, value, groupByColumn, cardsQuantity, on
|
||||
)}
|
||||
<span className="cards-quantity">{cardsQuantity}</span>
|
||||
</div>
|
||||
{value && !readonly && active && (
|
||||
<OpMenu onDelete={onDelete} onFreezed={onFreezed} onUnFreezed={handelUnFreezed} />
|
||||
{active && (
|
||||
<div className="board-header-operation-btn">
|
||||
{value && !readonly && <OpMenu onDelete={onDelete} onFreezed={onFreezed} onUnFreezed={handelUnFreezed} />}
|
||||
<i
|
||||
className={classNames('sf3-font sf3-font-down kanban-header-op-btn kanban-header-collapse-btn', { 'rotate-90': isCollapsed })}
|
||||
title={isCollapsed ? gettext('Expand') : gettext('Collapse')}
|
||||
onClick={handleCollapse}>
|
||||
</i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@@ -9,8 +9,7 @@ const OpMenu = ({ onDelete, onFreezed, onUnFreezed }) => {
|
||||
const toggle = useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
if (isShow) {
|
||||
const isClickToggleBtn = event.target.className?.includes('kanban-more-operations-toggle');
|
||||
onUnFreezed(isClickToggleBtn);
|
||||
onUnFreezed(event);
|
||||
} else {
|
||||
onFreezed();
|
||||
}
|
||||
@@ -35,7 +34,7 @@ const OpMenu = ({ onDelete, onFreezed, onUnFreezed }) => {
|
||||
tag="i"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className="sf-dropdown-toggle sf3-font-more sf3-font kanban-more-operations-toggle"
|
||||
className="sf-dropdown-toggle sf3-font-more sf3-font kanban-header-op-btn kanban-more-operations-toggle"
|
||||
title={gettext('More operations')}
|
||||
aria-label={gettext('More operations')}
|
||||
data-toggle="dropdown"
|
||||
|
@@ -31,6 +31,7 @@ const Board = ({
|
||||
onContextMenu,
|
||||
}) => {
|
||||
const [isDraggingOver, setDraggingOver] = useState(false);
|
||||
const [isCollapsed, setCollapsed] = useState(false);
|
||||
const boardName = useMemo(() => `sf_metadata_kanban_board_${board.key}`, [board]);
|
||||
const cardsQuantity = useMemo(() => board.children.length, [board.children]);
|
||||
|
||||
@@ -54,6 +55,10 @@ const Board = ({
|
||||
setTimeout(() => updateDragging(false), 0);
|
||||
}, [isDraggingOver, onMove, updateDragging]);
|
||||
|
||||
const onCollapse = useCallback(() => {
|
||||
setCollapsed(!isCollapsed);
|
||||
}, [isCollapsed]);
|
||||
|
||||
return (
|
||||
<section draggable={false} className="sf-metadata-view-kanban-board">
|
||||
<Header
|
||||
@@ -65,55 +70,59 @@ const Board = ({
|
||||
onDelete={() => deleteOption(board.key)}
|
||||
onFreezed={onFreezed}
|
||||
onUnFreezed={onUnFreezed}
|
||||
isCollapsed={isCollapsed}
|
||||
onCollapse={onCollapse}
|
||||
/>
|
||||
<Container
|
||||
orientation="vertical"
|
||||
groupName={boardName}
|
||||
dragClass="kanban-drag-card"
|
||||
dropClass="kanban-drop-card"
|
||||
onDragStart={onDragStart}
|
||||
onDrop={e => onDragEnd(boardIndex, e)}
|
||||
onDragEnter={() => setDraggingOver(true)}
|
||||
onDragLeave={() => setDraggingOver(false)}
|
||||
shouldAcceptDrop={(sourceContainer) => sourceContainer.groupName !== boardName}
|
||||
getChildPayload={(cardIndex) => ({ boardIndex, cardIndex })}
|
||||
dropPlaceholder={{
|
||||
animationDuration: 300,
|
||||
showOnTop: true,
|
||||
className: 'card-drop-preview',
|
||||
}}
|
||||
getGhostParent={() => {
|
||||
{!isCollapsed && (
|
||||
<Container
|
||||
orientation="vertical"
|
||||
groupName={boardName}
|
||||
dragClass="kanban-drag-card"
|
||||
dropClass="kanban-drop-card"
|
||||
onDragStart={onDragStart}
|
||||
onDrop={e => onDragEnd(boardIndex, e)}
|
||||
onDragEnter={() => setDraggingOver(true)}
|
||||
onDragLeave={() => setDraggingOver(false)}
|
||||
shouldAcceptDrop={(sourceContainer) => sourceContainer.groupName !== boardName}
|
||||
getChildPayload={(cardIndex) => ({ boardIndex, cardIndex })}
|
||||
dropPlaceholder={{
|
||||
animationDuration: 300,
|
||||
showOnTop: true,
|
||||
className: 'card-drop-preview',
|
||||
}}
|
||||
getGhostParent={() => {
|
||||
// return anchestor of container who doesn't have a transform property
|
||||
return document.querySelector('.sf-metadata-main');
|
||||
}}
|
||||
>
|
||||
{board.children.map((cardKey) => {
|
||||
const record = getRowById(metadata, cardKey);
|
||||
if (!record) return null;
|
||||
const recordId = getRecordIdFromRecord(record);
|
||||
const isSelected = selectedCard === recordId;
|
||||
const CardElement = (
|
||||
<Card
|
||||
key={cardKey}
|
||||
isSelected={isSelected}
|
||||
displayEmptyValue={displayEmptyValue}
|
||||
displayColumnName={displayColumnName}
|
||||
record={record}
|
||||
titleColumn={titleColumn}
|
||||
displayColumns={displayColumns}
|
||||
onOpenFile={onOpenFile}
|
||||
onSelectCard={onSelectCard}
|
||||
onContextMenu={(e) => onContextMenu(e, recordId)}
|
||||
/>
|
||||
);
|
||||
if (readonly) return CardElement;
|
||||
return (
|
||||
<Draggable key={`sf-metadata-kanban-card-${cardKey}`}>
|
||||
{CardElement}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
</Container>
|
||||
return document.querySelector('.sf-metadata-main');
|
||||
}}
|
||||
>
|
||||
{board.children.map((cardKey) => {
|
||||
const record = getRowById(metadata, cardKey);
|
||||
if (!record) return null;
|
||||
const recordId = getRecordIdFromRecord(record);
|
||||
const isSelected = selectedCard === recordId;
|
||||
const CardElement = (
|
||||
<Card
|
||||
key={cardKey}
|
||||
isSelected={isSelected}
|
||||
displayEmptyValue={displayEmptyValue}
|
||||
displayColumnName={displayColumnName}
|
||||
record={record}
|
||||
titleColumn={titleColumn}
|
||||
displayColumns={displayColumns}
|
||||
onOpenFile={onOpenFile}
|
||||
onSelectCard={onSelectCard}
|
||||
onContextMenu={(e) => onContextMenu(e, recordId)}
|
||||
/>
|
||||
);
|
||||
if (readonly) return CardElement;
|
||||
return (
|
||||
<Draggable key={`sf-metadata-kanban-card-${cardKey}`}>
|
||||
{CardElement}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
</Container>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user