Skip to content

Commit

Permalink
feat(metadata-views): support add folder
Browse files Browse the repository at this point in the history
  • Loading branch information
renjie-run committed Dec 11, 2024
1 parent 4c231ea commit 48dd123
Show file tree
Hide file tree
Showing 37 changed files with 2,313 additions and 1,196 deletions.
491 changes: 34 additions & 457 deletions frontend/src/components/dir-view-mode/dir-column-nav/index.js

Large diffs are not rendered by default.

463 changes: 463 additions & 0 deletions frontend/src/components/dir-view-mode/dir-files.js

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions frontend/src/components/dir-view-mode/dir-views/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.metadata-views-dropdown-menu .dropdown-header {
padding: 0 1rem;
font-weight: normal;
color: #666;
}

.metadata-views-dropdown-menu .dropdown-header,
.metadata-views-dropdown-menu .dropdown-item {
width: 100%;
height: 2rem;
display: flex;
align-items: center;
gap: 0.5rem;
}

.metadata-views-dropdown-menu .metadata-view-icon {
display: flex;
align-items: center;
font-size: 1rem;
color: #666;
fill: #666;
}

.metadata-views-dropdown-menu .dropdown-item:hover .metadata-view-icon,
.metadata-views-dropdown-menu .dropdown-item:focus .metadata-view-icon,
.metadata-views-dropdown-menu .dropdown-item:focus .sf3-font {
color: #fff;
fill: #fff;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import TreeSection from '../tree-section';
import { MetadataTreeView, useMetadata } from '../../metadata';
import ExtensionPrompts from './extension-prompts';
import LibSettingsDialog, { TAB } from '../dialog/lib-settings';
import { useMetadataStatus } from '../../hooks';
import TreeSection from '../../tree-section';
import ExtensionPrompts from '../extension-prompts';
import LibSettingsDialog, { TAB } from '../../dialog/lib-settings';
import ViewsMoreOperations from './views-more-operations';
import { MetadataTreeView, useMetadata } from '../../../metadata';
import { useMetadataStatus } from '../../../hooks';
import { gettext } from '../../../utils/constants';

import './index.css';


const DirViews = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
const enableMetadataManagement = useMemo(() => {
Expand All @@ -30,9 +34,27 @@ const DirViews = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
return null;
}

const renderTreeSectionHeaderOperations = (menuProps) => {
const canAdd = userPerm === 'rw' || userPerm === 'admin';

let operations = [];
if (enableMetadata && canAdd) {
operations.push(
<ViewsMoreOperations
key={'tree-section-more-operation'}
menuProps={menuProps}
/>
);
}
return operations;
};

return (
<>
<TreeSection title={gettext('Views')}>
<TreeSection
title={gettext('Views')}
renderHeaderOperations={renderTreeSectionHeaderOperations}
>
{!enableMetadata ? (
<ExtensionPrompts onExtendedProperties={onExtendedProperties} />
) : Array.isArray(navigation) && navigation.length > 0 ? (
Expand Down
66 changes: 66 additions & 0 deletions frontend/src/components/dir-view-mode/dir-views/new-view-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import Icon from '../../icon';
import TextTranslation from '../../../utils/text-translation';
import { gettext } from '../../../utils/constants';
import { VIEW_TYPE, VIEW_TYPE_ICON } from '../../../metadata/constants';

export const KEY_ADD_VIEW_MAP = {
ADD_FOLDER: 'ADD_FOLDER',
ADD_TABLE: 'ADD_TABLE',
ADD_GALLERY: 'ADD_GALLERY',
ADD_KANBAN: 'ADD_KANBAN',
ADD_MAP: 'ADD_MAP',
};

const ADD_VIEW_OPTIONS = [
{
key: KEY_ADD_VIEW_MAP.ADD_TABLE,
type: VIEW_TYPE.TABLE,
},
{
key: KEY_ADD_VIEW_MAP.ADD_GALLERY,
type: VIEW_TYPE.GALLERY,
},
{
key: KEY_ADD_VIEW_MAP.ADD_KANBAN,
type: VIEW_TYPE.KANBAN,
},
{
key: KEY_ADD_VIEW_MAP.ADD_MAP,
type: VIEW_TYPE.MAP,
},
];

const translateLabel = (type) => {
switch (type) {
case VIEW_TYPE.TABLE:
return gettext('Table');
case VIEW_TYPE.GALLERY:
return gettext('Gallery');
case VIEW_TYPE.KANBAN:
return gettext('Kanban');
case VIEW_TYPE.MAP:
return gettext('Map');
default:
return type;
}
};

const getNewViewSubMenus = () => {
return ADD_VIEW_OPTIONS.map((option) => {
const { key, type } = option;
return {
key,
value: translateLabel(type),
icon_dom: <Icon symbol={VIEW_TYPE_ICON[type] || VIEW_TYPE.TABLE} className='metadata-view-icon' />
};
});
};

export const getNewViewMenuItem = () => {
return {
...TextTranslation.ADD_VIEW,
subOpListHeader: gettext('New view'),
subOpList: getNewViewSubMenus(),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useCallback } from 'react';
import ItemDropdownMenu from '../../dropdown-menu/item-dropdown-menu';
import TextTranslation from '../../../utils/text-translation';
import { isMobile } from '../../../utils/utils';
import EventBus from '../../common/event-bus';
import { EVENT_BUS_TYPE, VIEW_TYPE } from '../../../metadata/constants';
import { getNewViewMenuItem, KEY_ADD_VIEW_MAP } from './new-view-menu';

const ViewsMoreOperations = ({ menuProps }) => {
const eventBus = EventBus.getInstance();

const addView = (viewType) => {
eventBus.dispatch(EVENT_BUS_TYPE.ADD_VIEW, { viewType });
};

const clickMenu = (option) => {
switch (option) {
case TextTranslation.ADD_FOLDER.key: {
eventBus.dispatch(EVENT_BUS_TYPE.ADD_FOLDER);
return;
}
case KEY_ADD_VIEW_MAP.ADD_TABLE: {
addView(VIEW_TYPE.TABLE);
return;
}
case KEY_ADD_VIEW_MAP.ADD_GALLERY: {
addView(VIEW_TYPE.GALLERY);
return;
}
case KEY_ADD_VIEW_MAP.ADD_KANBAN: {
addView(VIEW_TYPE.KANBAN);
return;
}
case KEY_ADD_VIEW_MAP.ADD_MAP: {
addView(VIEW_TYPE.MAP);
return;
}
default: {
return;
}
}
};

const getMoreOperationsMenus = useCallback(() => {
return [
TextTranslation.ADD_FOLDER,
getNewViewMenuItem(),
];
}, []);

return (
<div className="tree-section-header-operation tree-section-more-operation">
<ItemDropdownMenu
{...menuProps}
menuClassname={'metadata-views-dropdown-menu'}
item={{ name: 'views' }}
toggleClass="sf3-font sf3-font-more"
menuStyle={isMobile ? { zIndex: 1050 } : {}}
getMenuList={getMoreOperationsMenus}
onMenuItemClick={clickMenu}
/>
</div>
);
};

export default ViewsMoreOperations;
20 changes: 17 additions & 3 deletions frontend/src/components/dropdown-menu/item-dropdown-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ class ItemDropdownMenu extends React.Component {
onMenuItemClick = (event) => {
let operation = Utils.getEventData(event, 'toggle') ?? event.currentTarget.getAttribute('data-toggle');
let item = this.props.item;
this.props.unfreezeItem();
this.props.onMenuItemClick(operation, event, item);
this.setState({ isItemMenuShow: false });
};

onDropDownMouseMove = () => {
Expand Down Expand Up @@ -176,7 +178,7 @@ class ItemDropdownMenu extends React.Component {
}

return (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.onDropdownToggleClick} className="vam">
<Dropdown direction='down' isOpen={this.state.isItemMenuShow} toggle={this.onDropdownToggleClick} className="vam">
<DropdownToggle
tag={tagName || 'i'}
role="button"
Expand All @@ -192,6 +194,10 @@ class ItemDropdownMenu extends React.Component {
<ModalPortal>
<DropdownMenu
style={menuStyle}
className={this.props.menuClassname}
positionFixed
flip={false}
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
>
{menuList.map((menuItem, index) => {
if (menuItem === 'Divider') {
Expand All @@ -214,13 +220,21 @@ class ItemDropdownMenu extends React.Component {
<span className="mr-auto">{menuItem.value}</span>
<i className="sf3-font-down sf3-font rotate-270"></i>
</DropdownToggle>
<DropdownMenu>
<DropdownMenu
positionFixed
flip={false}
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
>
{menuItem.subOpListHeader && <DropdownItem header>{menuItem.subOpListHeader}</DropdownItem>}
{menuItem.subOpList.map((item, index) => {
if (item == 'Divider') {
return <DropdownItem key={index} divider />;
} else {
return (
<DropdownItem key={index} data-toggle={item.key} onClick={this.onMenuItemClick} onKeyDown={this.onMenuItemKeyDown}>{item.value}</DropdownItem>
<DropdownItem key={index} data-toggle={item.key} onClick={this.onMenuItemClick} onKeyDown={this.onMenuItemKeyDown}>
{item.icon_dom || null}
<span>{item.value}</span>
</DropdownItem>
);
}
})}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/tree-section/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@
margin-left: 0;
line-height: 1.5;
font-size: 16px;
color: #999 !important;
color: #666 !important;
}

.tree-section .tree-section-more-operation .dropdown .sf-dropdown-toggle.sf3-font-new {
font-size: 12px;
}

.tree-section .tree-section-header .tree-section-more-operation {
Expand Down
43 changes: 14 additions & 29 deletions frontend/src/components/tree-section/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
import { isMobile } from '../../utils/utils';

import './index.css';

const TreeSection = ({ title, children, moreKey, moreOperations, moreOperationClick, className, isDisplayFiles }) => {
const TreeSection = ({ title, children, renderHeaderOperations, className }) => {
const [showChildren, setShowChildren] = useState(true);
const [highlight, setHighlight] = useState(false);
const [freeze, setFreeze] = useState(false);

const validMoreOperations = useMemo(() => {
if (!Array.isArray(moreOperations) || moreOperations.length === 0) return [];
return moreOperations.filter(operation => operation.key && operation.value);
}, [moreOperations]);

const toggleShowChildren = useCallback((event) => {
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
Expand All @@ -39,13 +32,24 @@ const TreeSection = ({ title, children, moreKey, moreOperations, moreOperationCl

const freezeItem = useCallback(() => {
setFreeze(true);
setHighlight(true);
}, []);

const unfreezeItem = useCallback(() => {
setFreeze(false);
setHighlight(false);
}, []);

const renderOperations = useCallback(() => {
if (!renderHeaderOperations) {
return null;
}
return renderHeaderOperations({
freezeItem,
unfreezeItem,
});
}, [renderHeaderOperations, freezeItem, unfreezeItem]);

return (
<div className={classnames('tree-section', className)}>
<div
Expand All @@ -56,22 +60,7 @@ const TreeSection = ({ title, children, moreKey, moreOperations, moreOperationCl
>
<div className="tree-section-header-title">{title}</div>
<div className="tree-section-header-operations">
{validMoreOperations.length > 0 && (
<>
<div className="tree-section-header-operation tree-section-more-operation">
<ItemDropdownMenu
item={moreKey}
toggleClass="sf3-font sf3-font-more"
freezeItem={freezeItem}
unfreezeItem={unfreezeItem}
getMenuList={() => validMoreOperations}
onMenuItemClick={moreOperationClick}
menuStyle={isMobile ? { zIndex: 1050 } : {}}
isDisplayFiles={isDisplayFiles}
/>
</div>
</>
)}
{renderOperations()}
<div className="tree-section-header-operation" onClick={toggleShowChildren}>
<i className={`sf3-font sf3-font-down ${showChildren ? '' : 'rotate-90'}`}></i>
</div>
Expand All @@ -88,12 +77,8 @@ const TreeSection = ({ title, children, moreKey, moreOperations, moreOperationCl

TreeSection.propTypes = {
title: PropTypes.any.isRequired,
moreOperations: PropTypes.array,
children: PropTypes.any,
moreKey: PropTypes.object,
moreOperationClick: PropTypes.func,
className: PropTypes.string,
isDisplayFiles: PropTypes.bool,
};

export default TreeSection;
Loading

0 comments on commit 48dd123

Please sign in to comment.