mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-19 07:27:56 +00:00
Merge pull request #7434 from haiwen/optimize/file_name_in_table_view
Optimize/file name in table view
This commit is contained in:
commit
a87e662f55
8
frontend/package-lock.json
generated
8
frontend/package-lock.json
generated
@ -19,7 +19,7 @@
|
|||||||
"@seafile/sdoc-editor": "1.0.212",
|
"@seafile/sdoc-editor": "1.0.212",
|
||||||
"@seafile/seafile-calendar": "0.0.28",
|
"@seafile/seafile-calendar": "0.0.28",
|
||||||
"@seafile/seafile-editor": "1.0.133",
|
"@seafile/seafile-editor": "1.0.133",
|
||||||
"@seafile/sf-metadata-ui-component": "^0.0.64",
|
"@seafile/sf-metadata-ui-component": "^0.0.68",
|
||||||
"@seafile/stldraw-editor": "0.1.5",
|
"@seafile/stldraw-editor": "0.1.5",
|
||||||
"@uiw/codemirror-extensions-langs": "^4.19.4",
|
"@uiw/codemirror-extensions-langs": "^4.19.4",
|
||||||
"@uiw/codemirror-themes": "^4.23.5",
|
"@uiw/codemirror-themes": "^4.23.5",
|
||||||
@ -5410,9 +5410,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@seafile/sf-metadata-ui-component": {
|
"node_modules/@seafile/sf-metadata-ui-component": {
|
||||||
"version": "0.0.64",
|
"version": "0.0.68",
|
||||||
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.64.tgz",
|
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.68.tgz",
|
||||||
"integrity": "sha512-C4kLqsZCg+KutvOzsv65j58x7PQ6t8wPRJwXIBmEBSqB0/suZZnEV6uNhU5W83zN2CzHmGeRvg4+v+/Pj4pEiA==",
|
"integrity": "sha512-DTBEyPxj1TNSBNkv+VAidt9x2/KQwHVKF0wbxHGEFxXR/OxX3S7bbqMpf9/FtUVIeEPjk8xPTHs4jYEDN6MY3Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@seafile/seafile-calendar": "0.0.28",
|
"@seafile/seafile-calendar": "0.0.28",
|
||||||
"@seafile/seafile-editor": "^1.0.133",
|
"@seafile/seafile-editor": "^1.0.133",
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"@seafile/sdoc-editor": "1.0.212",
|
"@seafile/sdoc-editor": "1.0.212",
|
||||||
"@seafile/seafile-calendar": "0.0.28",
|
"@seafile/seafile-calendar": "0.0.28",
|
||||||
"@seafile/seafile-editor": "1.0.133",
|
"@seafile/seafile-editor": "1.0.133",
|
||||||
"@seafile/sf-metadata-ui-component": "^0.0.64",
|
"@seafile/sf-metadata-ui-component": "^0.0.68",
|
||||||
"@seafile/stldraw-editor": "0.1.5",
|
"@seafile/stldraw-editor": "0.1.5",
|
||||||
"@uiw/codemirror-extensions-langs": "^4.19.4",
|
"@uiw/codemirror-extensions-langs": "^4.19.4",
|
||||||
"@uiw/codemirror-themes": "^4.23.5",
|
"@uiw/codemirror-themes": "^4.23.5",
|
||||||
|
@ -7,7 +7,7 @@ import { siteRoot, thumbnailDefaultSize } from '../../../utils/constants';
|
|||||||
import { getParentDirFromRecord } from '../../utils/cell';
|
import { getParentDirFromRecord } from '../../utils/cell';
|
||||||
import { checkIsDir } from '../../utils/row';
|
import { checkIsDir } from '../../utils/row';
|
||||||
|
|
||||||
const FileName = ({ record, className: propsClassName, value, ...params }) => {
|
const FileName = ({ record, className: propsClassName, value, onFileNameClick, ...params }) => {
|
||||||
const parentDir = useMemo(() => getParentDirFromRecord(record), [record]);
|
const parentDir = useMemo(() => getParentDirFromRecord(record), [record]);
|
||||||
const isDir = useMemo(() => checkIsDir(record), [record]);
|
const isDir = useMemo(() => checkIsDir(record), [record]);
|
||||||
const className = useMemo(() => {
|
const className = useMemo(() => {
|
||||||
@ -30,7 +30,7 @@ const FileName = ({ record, className: propsClassName, value, ...params }) => {
|
|||||||
return { iconUrl: defaultIconUrl, defaultIconUrl };
|
return { iconUrl: defaultIconUrl, defaultIconUrl };
|
||||||
}, [isDir, value, parentDir]);
|
}, [isDir, value, parentDir]);
|
||||||
|
|
||||||
return (<FileNameFormatter { ...params } className={className} value={value} { ...iconUrl } />);
|
return (<FileNameFormatter { ...params } className={className} value={value} onClickName={onFileNameClick} { ...iconUrl } />);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ FileName.propTypes = {
|
|||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
record: PropTypes.object.isRequired,
|
record: PropTypes.object.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
onFileNameClick: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FileName;
|
export default FileName;
|
||||||
|
@ -96,7 +96,7 @@ const ContextMenu = ({
|
|||||||
>
|
>
|
||||||
{options.map((option, index) => {
|
{options.map((option, index) => {
|
||||||
if (option === 'Divider') {
|
if (option === 'Divider') {
|
||||||
return <DropdownItem key="divider-item" divider />;
|
return <DropdownItem key={index} divider />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@ -114,10 +114,13 @@ const ContextMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
ContextMenu.propTypes = {
|
ContextMenu.propTypes = {
|
||||||
options: PropTypes.arrayOf(PropTypes.shape({
|
options: PropTypes.arrayOf(PropTypes.oneOfType([
|
||||||
|
PropTypes.shape({
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
})).isRequired,
|
}),
|
||||||
|
PropTypes.string
|
||||||
|
])).isRequired,
|
||||||
boundaryCoordinates: PropTypes.object,
|
boundaryCoordinates: PropTypes.object,
|
||||||
ignoredTriggerElements: PropTypes.array,
|
ignoredTriggerElements: PropTypes.array,
|
||||||
onOptionClick: PropTypes.func.isRequired,
|
onOptionClick: PropTypes.func.isRequired,
|
||||||
|
@ -36,15 +36,8 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name {
|
|
||||||
text-decoration: none;
|
|
||||||
flex: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name:hover {
|
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-color: #212529;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-body .sf-metadata-kanban-card-record:first-child {
|
.sf-metadata-kanban-card .sf-metadata-kanban-card-body .sf-metadata-kanban-card-record:first-child {
|
||||||
|
@ -4,7 +4,6 @@ import classnames from 'classnames';
|
|||||||
import Formatter from '../formatter';
|
import Formatter from '../formatter';
|
||||||
import { getCellValueByColumn, isValidCellValue } from '../../../../../utils/cell';
|
import { getCellValueByColumn, isValidCellValue } from '../../../../../utils/cell';
|
||||||
import { CellType } from '../../../../../constants';
|
import { CellType } from '../../../../../constants';
|
||||||
import { getEventClassName } from '../../../../../utils/common';
|
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@ -28,10 +27,8 @@ const Card = ({
|
|||||||
onSelectCard(record);
|
onSelectCard(record);
|
||||||
}, [record, onSelectCard]);
|
}, [record, onSelectCard]);
|
||||||
|
|
||||||
const handleClickFilename = useCallback((event) => {
|
const handleFilenameClick = useCallback((event) => {
|
||||||
if (titleColumn?.type !== CellType.FILE_NAME) return;
|
if (titleColumn?.type !== CellType.FILE_NAME) return;
|
||||||
const eventName = getEventClassName(event);
|
|
||||||
if (eventName !== 'sf-metadata-file-name') return;
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.nativeEvent.stopImmediatePropagation();
|
event.nativeEvent.stopImmediatePropagation();
|
||||||
onOpenFile(record);
|
onOpenFile(record);
|
||||||
@ -45,8 +42,8 @@ const Card = ({
|
|||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
{titleColumn && (
|
{titleColumn && (
|
||||||
<div className="sf-metadata-kanban-card-header" onClick={handleClickFilename}>
|
<div className="sf-metadata-kanban-card-header">
|
||||||
<Formatter value={titleValue} column={titleColumn} record={record}/>
|
<Formatter value={titleValue} column={titleColumn} record={record} onFileNameClick={handleFilenameClick} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="sf-metadata-kanban-card-body">
|
<div className="sf-metadata-kanban-card-body">
|
||||||
|
@ -14,7 +14,7 @@ const SPECIAL_FILE_ICON = [
|
|||||||
'word.png',
|
'word.png',
|
||||||
];
|
];
|
||||||
|
|
||||||
const Formatter = ({ value, column, record }) => {
|
const Formatter = ({ value, column, record, ...params }) => {
|
||||||
let className = '';
|
let className = '';
|
||||||
|
|
||||||
if (column.type === CellType.FILE_NAME && value) {
|
if (column.type === CellType.FILE_NAME && value) {
|
||||||
@ -24,7 +24,7 @@ const Formatter = ({ value, column, record }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<CellFormatter readonly={true} className={className} value={value} field={column} record={record} />);
|
return (<CellFormatter { ...params } readonly={true} className={className} value={value} field={column} record={record} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
Formatter.propTypes = {
|
Formatter.propTypes = {
|
||||||
|
@ -6,7 +6,7 @@ import RateEditor from '../../../../../../components/cell-editors/rate-editor';
|
|||||||
import { canEditCell } from '../../../../../../utils/column';
|
import { canEditCell } from '../../../../../../utils/column';
|
||||||
import { CellType } from '../../../../../../constants';
|
import { CellType } from '../../../../../../constants';
|
||||||
|
|
||||||
const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
|
const Formatter = ({ isCellSelected, field, value, onChange, record, ...params }) => {
|
||||||
const { type } = field;
|
const { type } = field;
|
||||||
const cellEditAble = canEditCell(field, record, true);
|
const cellEditAble = canEditCell(field, record, true);
|
||||||
if (type === CellType.CHECKBOX && cellEditAble) {
|
if (type === CellType.CHECKBOX && cellEditAble) {
|
||||||
@ -16,7 +16,7 @@ const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
|
|||||||
return (<RateEditor isCellSelected={isCellSelected} value={value} field={field} onChange={onChange} />);
|
return (<RateEditor isCellSelected={isCellSelected} value={value} field={field} onChange={onChange} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<CellFormatter readonly={true} isCellSelected={isCellSelected} value={value} field={field} record={record} />);
|
return (<CellFormatter { ...params } readonly={true} value={value} field={field} record={record} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
Formatter.propTypes = {
|
Formatter.propTypes = {
|
||||||
|
@ -308,6 +308,7 @@
|
|||||||
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell {
|
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell .sf-metadata-ui.cell-formatter-container {
|
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell .sf-metadata-ui.cell-formatter-container {
|
||||||
|
@ -6,8 +6,9 @@ import CellOperationBtn from './operation-btn';
|
|||||||
import { Utils } from '../../../../../../../utils/utils';
|
import { Utils } from '../../../../../../../utils/utils';
|
||||||
import ObjectUtils from '../../../../../../utils/object-utils';
|
import ObjectUtils from '../../../../../../utils/object-utils';
|
||||||
import { isCellValueChanged, getCellValueByColumn } from '../../../../../../utils/cell';
|
import { isCellValueChanged, getCellValueByColumn } from '../../../../../../utils/cell';
|
||||||
import { CellType, PRIVATE_COLUMN_KEYS, TABLE_SUPPORT_EDIT_TYPE_MAP } from '../../../../../../constants';
|
import { CellType, PRIVATE_COLUMN_KEYS, TABLE_SUPPORT_EDIT_TYPE_MAP, EDITOR_TYPE, EVENT_BUS_TYPE } from '../../../../../../constants';
|
||||||
import { checkIsDir } from '../../../../../../utils/row';
|
import { checkIsDir } from '../../../../../../utils/row';
|
||||||
|
import { openFile } from '../../../../../../utils/file';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@ -148,6 +149,16 @@ const Cell = React.memo(({
|
|||||||
cellMetaData.modifyRecord({ rowId, cellKey: columnKey, updates, originalUpdates: updated, oldRowData, originalOldRowData });
|
cellMetaData.modifyRecord({ rowId, cellKey: columnKey, updates, originalUpdates: updated, oldRowData, originalOldRowData });
|
||||||
}, [cellMetaData, record, column, getOldRowData]);
|
}, [cellMetaData, record, column, getOldRowData]);
|
||||||
|
|
||||||
|
const onFileNameClick = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.nativeEvent.stopImmediatePropagation();
|
||||||
|
if (!isCellSelected) return;
|
||||||
|
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||||
|
openFile(repoID, record, () => {
|
||||||
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
|
||||||
|
});
|
||||||
|
}, [isCellSelected, record]);
|
||||||
|
|
||||||
const cellValue = getCellValueByColumn(record, column);
|
const cellValue = getCellValueByColumn(record, column);
|
||||||
const cellEvents = needBindEvents && getEvents();
|
const cellEvents = needBindEvents && getEvents();
|
||||||
const containerProps = {
|
const containerProps = {
|
||||||
@ -158,7 +169,7 @@ const Cell = React.memo(({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={`${record._id}-${column.key}`} {...containerProps}>
|
<div key={`${record._id}-${column.key}`} {...containerProps}>
|
||||||
<Formatter isCellSelected={isCellSelected} value={cellValue} field={column} onChange={modifyRecord} record={record} />
|
<Formatter isCellSelected={isCellSelected} value={cellValue} field={column} onChange={modifyRecord} record={record} onFileNameClick={onFileNameClick} />
|
||||||
{isCellSelected && (<CellOperationBtn record={record} column={column}/>)}
|
{isCellSelected && (<CellOperationBtn record={record} column={column}/>)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
.sf-metadata-cell-operation-btn {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 2px 4px;
|
|
||||||
position: absolute;
|
|
||||||
right: 8px;
|
|
||||||
top: 6px;
|
|
||||||
transition: background 0.020s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-metadata-cell-operation-btn:hover {
|
|
||||||
background: #efefef;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-metadata-cell-operation-btn .sf-metadata-icon {
|
|
||||||
fill: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import React, { useMemo } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
|
||||||
import { IconBtn } from '@seafile/sf-metadata-ui-component';
|
|
||||||
import { gettext } from '../../../../../../../../../utils/constants';
|
|
||||||
import { EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, EDITOR_TYPE } from '../../../../../../../../constants';
|
|
||||||
import { checkIsDir } from '../../../../../../../../utils/row';
|
|
||||||
import { openFile } from '../../../../../../../../utils/file';
|
|
||||||
|
|
||||||
import './index.css';
|
|
||||||
|
|
||||||
const FileNameOperationBtn = ({ column, record, ...props }) => {
|
|
||||||
|
|
||||||
const fileName = useMemo(() => {
|
|
||||||
const { key } = column;
|
|
||||||
return record[key];
|
|
||||||
}, [column, record]);
|
|
||||||
|
|
||||||
const isDir = useMemo(() => checkIsDir(record), [record]);
|
|
||||||
|
|
||||||
const handelClick = (event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.nativeEvent.stopImmediatePropagation();
|
|
||||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
|
||||||
openFile(repoID, record, () => {
|
|
||||||
window.sfMetadataContext.eventBus.dispatch(METADATA_EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!fileName) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IconBtn id="sf-metadata-cell-open-file-btn" className="sf-metadata-cell-operation-btn" size={20} iconName="open-file" onClick={handelClick} />
|
|
||||||
<UncontrolledTooltip
|
|
||||||
hideArrow
|
|
||||||
target="sf-metadata-cell-open-file-btn"
|
|
||||||
placement="bottom"
|
|
||||||
fade={false}
|
|
||||||
delay={{ show: 0, hide: 0 }}
|
|
||||||
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
|
||||||
className="sf-metadata-tooltip"
|
|
||||||
>
|
|
||||||
{isDir ? gettext('Open folder') : gettext('Open file')}
|
|
||||||
</UncontrolledTooltip>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
FileNameOperationBtn.propTypes = {
|
|
||||||
column: PropTypes.object,
|
|
||||||
record: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FileNameOperationBtn;
|
|
@ -1,14 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CellType } from '../../../../../../../constants';
|
import { CellType } from '../../../../../../../constants';
|
||||||
import FileNameOperationBtn from './file-name-operation-btn';
|
|
||||||
import LinkOperationBtn from './link-operation-btn';
|
import LinkOperationBtn from './link-operation-btn';
|
||||||
|
|
||||||
const CellOperationBtn = ({ column, record }) => {
|
const CellOperationBtn = ({ column, record }) => {
|
||||||
switch (column.type) {
|
switch (column.type) {
|
||||||
case CellType.FILE_NAME: {
|
|
||||||
return (<FileNameOperationBtn column={column} record={record} />);
|
|
||||||
}
|
|
||||||
case CellType.LINK: {
|
case CellType.LINK: {
|
||||||
return (<LinkOperationBtn column={column} record={record} />);
|
return (<LinkOperationBtn column={column} record={record} />);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user