mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-03 16:10:26 +00:00
Feature/gallery view (#6578)
* add gallery view, display images in grid layout * remove redundant code * clean up redundant code, improve responsive gallery * improve gaps in gallery
This commit is contained in:
@@ -113,7 +113,7 @@ class DirTool extends React.Component {
|
|||||||
|
|
||||||
if (isFileExtended) {
|
if (isFileExtended) {
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<div className="dir-tool">
|
||||||
<MetadataViewToolBar viewId={viewId} />
|
<MetadataViewToolBar viewId={viewId} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -13,3 +13,16 @@ const TAG_COLORS = ['#FBD44A', '#EAA775', '#F4667C', '#DC82D2', '#9860E5', '#9F8
|
|||||||
export const SIDE_PANEL_FOLDED_WIDTH = 71;
|
export const SIDE_PANEL_FOLDED_WIDTH = 71;
|
||||||
|
|
||||||
export { KeyCodes, zIndexes, TAG_COLORS };
|
export { KeyCodes, zIndexes, TAG_COLORS };
|
||||||
|
|
||||||
|
export const VIEW_OPTIONS = [
|
||||||
|
{
|
||||||
|
key: 'table',
|
||||||
|
label: 'Table',
|
||||||
|
type: 'table',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'gallery',
|
||||||
|
label: 'Gallery',
|
||||||
|
type: 'image',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
@@ -339,3 +339,10 @@ img[src=""],img:not([src]) { /* for first loading img*/
|
|||||||
.dir-tool>div {
|
.dir-tool>div {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dir-tool {
|
||||||
|
height: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@@ -112,9 +112,9 @@ class MetadataManagerAPI {
|
|||||||
return this.req.get(url);
|
return this.req.get(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
addView = (repoID, name) => {
|
addView = (repoID, name, type = 'table') => {
|
||||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/views/';
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/views/';
|
||||||
const params = { name };
|
const params = { name, type };
|
||||||
return this._sendPostRequest(url, params, { headers: { 'Content-type': 'application/json' } });
|
return this._sendPostRequest(url, params, { headers: { 'Content-type': 'application/json' } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -107,8 +107,8 @@ export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView,
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [repoID, selectMetadataView]);
|
}, [repoID, selectMetadataView]);
|
||||||
|
|
||||||
const addView = useCallback((name, successCallback, failCallback) => {
|
const addView = useCallback((name, type, successCallback, failCallback) => {
|
||||||
metadataAPI.addView(repoID, name).then(res => {
|
metadataAPI.addView(repoID, name, type).then(res => {
|
||||||
const view = res.data.view;
|
const view = res.data.view;
|
||||||
let newNavigation = navigation.slice(0);
|
let newNavigation = navigation.slice(0);
|
||||||
newNavigation.push({ _id: view._id, type: 'view' });
|
newNavigation.push({ _id: view._id, type: 'view' });
|
||||||
|
@@ -65,3 +65,7 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.metadata-tree-view .metadata-views-icon {
|
||||||
|
fill: #666;
|
||||||
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||||
|
import { Form, Input } from 'reactstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
import { CustomizeAddTool } from '@seafile/sf-metadata-ui-component';
|
||||||
|
import Icon from '../../components/icon';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
import { PRIVATE_FILE_TYPE, VIEW_OPTIONS } from '../../constants';
|
||||||
import ViewItem from './view-item';
|
import ViewItem from './view-item';
|
||||||
import { useMetadata } from '../hooks';
|
import { useMetadata } from '../hooks';
|
||||||
import { Form, Input } from 'reactstrap';
|
|
||||||
import Icon from '../../components/icon';
|
|
||||||
import { AddView } from '../metadata-view/components/popover/view-popover';
|
import { AddView } from '../metadata-view/components/popover/view-popover';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
@@ -27,6 +27,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
updateView,
|
updateView,
|
||||||
moveView
|
moveView
|
||||||
} = useMetadata();
|
} = useMetadata();
|
||||||
|
const [newView, setNewView] = useState(null);
|
||||||
|
|
||||||
const [showAddViewPopover, setShowAddViewPopover] = useState(false);
|
const [showAddViewPopover, setShowAddViewPopover] = useState(false);
|
||||||
const [showInput, setShowInput] = useState(false);
|
const [showInput, setShowInput] = useState(false);
|
||||||
@@ -71,7 +72,8 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePopoverOptionClick = () => {
|
const handlePopoverOptionClick = (option) => {
|
||||||
|
setNewView(option);
|
||||||
setShowInput(true);
|
setShowInput(true);
|
||||||
setShowAddViewPopover(false);
|
setShowAddViewPopover(false);
|
||||||
};
|
};
|
||||||
@@ -79,10 +81,10 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
const handleInputSubmit = useCallback((event) => {
|
const handleInputSubmit = useCallback((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
addView(inputValue);
|
addView(inputValue, newView.type);
|
||||||
setShowInput(false);
|
setShowInput(false);
|
||||||
setInputValue('Untitled');
|
setInputValue('Untitled');
|
||||||
}, [inputValue, addView]);
|
}, [inputValue, addView, newView]);
|
||||||
|
|
||||||
const handleClickOutsideInput = useCallback((event) => {
|
const handleClickOutsideInput = useCallback((event) => {
|
||||||
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
||||||
@@ -127,9 +129,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
{showInput && (
|
{showInput && (
|
||||||
<Form onSubmit={handleInputSubmit} className='tree-view-inner sf-metadata-view-form'>
|
<Form onSubmit={handleInputSubmit} className='tree-view-inner sf-metadata-view-form'>
|
||||||
<div className="left-icon">
|
<div className="left-icon">
|
||||||
<div className="tree-node-icon">
|
<Icon symbol={newView.type} className="metadata-views-icon" />
|
||||||
<Icon symbol="table" className="metadata-views-icon" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
className='sf-metadata-view-input'
|
className='sf-metadata-view-input'
|
||||||
@@ -157,7 +157,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showAddViewPopover && (
|
{showAddViewPopover && (
|
||||||
<AddView target='sf-metadata-view-popover' toggle={togglePopover} onOptionClick={handlePopoverOptionClick} />
|
<AddView target='sf-metadata-view-popover' options={VIEW_OPTIONS} toggle={togglePopover} onOptionClick={handlePopoverOptionClick} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -151,7 +151,7 @@ const ViewItem = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="left-icon">
|
<div className="left-icon">
|
||||||
<div className="tree-node-icon">
|
<div className="tree-node-icon">
|
||||||
<Icon symbol="table" className="metadata-views-icon" />
|
<Icon symbol={view.type ? view.type : 'table'} className="metadata-views-icon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="right-icon" id={`metadata-view-dropdown-item-${view._id}`} >
|
<div className="right-icon" id={`metadata-view-dropdown-item-${view._id}`} >
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import SliderSetter from './slider-setter';
|
||||||
import FilterSetter from './filter-setter';
|
import FilterSetter from './filter-setter';
|
||||||
import SortSetter from './sort-setter';
|
import SortSetter from './sort-setter';
|
||||||
import GroupbySetter from './groupby-setter';
|
import GroupbySetter from './groupby-setter';
|
||||||
@@ -5,6 +6,7 @@ import PreHideColumnSetter from './pre-hide-column-setter';
|
|||||||
import HideColumnSetter from './hide-column-setter';
|
import HideColumnSetter from './hide-column-setter';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
SliderSetter,
|
||||||
FilterSetter,
|
FilterSetter,
|
||||||
SortSetter,
|
SortSetter,
|
||||||
GroupbySetter,
|
GroupbySetter,
|
||||||
|
@@ -0,0 +1,43 @@
|
|||||||
|
import React, { useState, useCallback } from 'react';
|
||||||
|
import { Button, Input } from 'reactstrap';
|
||||||
|
import { EVENT_BUS_TYPE } from '../../constants';
|
||||||
|
|
||||||
|
const SliderSetter = () => {
|
||||||
|
const [sliderValue, setSliderValue] = useState(0);
|
||||||
|
|
||||||
|
const handleGalleryColumnsChange = useCallback((e) => {
|
||||||
|
const adjust = parseInt(e.target.value, 10);
|
||||||
|
setSliderValue(adjust);
|
||||||
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleImageExpand = useCallback(() => {
|
||||||
|
const adjust = Math.min(sliderValue + 1, 2);
|
||||||
|
setSliderValue(adjust);
|
||||||
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
|
||||||
|
}, [sliderValue]);
|
||||||
|
|
||||||
|
const handleImageShrink = useCallback(() => {
|
||||||
|
const adjust = Math.max(sliderValue - 1, -2);
|
||||||
|
setSliderValue(adjust);
|
||||||
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
|
||||||
|
}, [sliderValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button type="button" size='sm' onClick={handleImageShrink} >-</Button>
|
||||||
|
<Input
|
||||||
|
type="range"
|
||||||
|
min="-2"
|
||||||
|
max="2"
|
||||||
|
step="1"
|
||||||
|
value={sliderValue}
|
||||||
|
onChange={handleGalleryColumnsChange}
|
||||||
|
className="custom-slider ml-2 mr-2"
|
||||||
|
/>
|
||||||
|
<Button type="button" size='sm' onClick={handleImageExpand} className='mr-2' >+</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SliderSetter;
|
@@ -6,7 +6,7 @@ import Icon from '../../../../../../components/icon';
|
|||||||
|
|
||||||
import '../index.css';
|
import '../index.css';
|
||||||
|
|
||||||
const AddView = ({ target, toggle, onOptionClick }) => {
|
const AddView = ({ target, options, toggle, onOptionClick }) => {
|
||||||
const popoverRef = useRef(null);
|
const popoverRef = useRef(null);
|
||||||
|
|
||||||
const handleClickOutside = useCallback((event) => {
|
const handleClickOutside = useCallback((event) => {
|
||||||
@@ -39,14 +39,16 @@ const AddView = ({ target, toggle, onOptionClick }) => {
|
|||||||
<div ref={popoverRef}>
|
<div ref={popoverRef}>
|
||||||
<div className='sf-metadata-addview-popover-header'>{gettext('New view')}</div>
|
<div className='sf-metadata-addview-popover-header'>{gettext('New view')}</div>
|
||||||
<div className='sf-metadata-addview-popover-body'>
|
<div className='sf-metadata-addview-popover-body'>
|
||||||
<button className='dropdown-item sf-metadata-addview-popover-item' onClick={onOptionClick}>
|
{options.map((item, index) => {
|
||||||
<div className="left-icon">
|
return (
|
||||||
<div className="metadata-view-icon">
|
<button key={index} className='dropdown-item sf-metadata-addview-popover-item' onClick={() => onOptionClick(item)}>
|
||||||
<Icon symbol="table" />
|
<div className="left-icon">
|
||||||
</div>
|
<Icon symbol={item.type} className='metadata-view-icon' />
|
||||||
</div>
|
</div>
|
||||||
<div>{gettext('Table')}</div>
|
<div>{gettext(item.label)}</div>
|
||||||
</button>
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UncontrolledPopover>
|
</UncontrolledPopover>
|
||||||
|
@@ -55,6 +55,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
fill: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-rename-view-popover-body {
|
.sf-metadata-rename-view-popover-body {
|
||||||
@@ -63,4 +64,5 @@
|
|||||||
|
|
||||||
.dropdown-item:hover .metadata-view-icon {
|
.dropdown-item:hover .metadata-view-icon {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { CommonlyUsedHotkey, getValidGroupbys } from '../../_basic';
|
|||||||
import { gettext } from '../../utils';
|
import { gettext } from '../../utils';
|
||||||
import { useMetadata } from '../../hooks';
|
import { useMetadata } from '../../hooks';
|
||||||
import TableMain from './table-main';
|
import TableMain from './table-main';
|
||||||
|
import Gallery from './gallery';
|
||||||
import { Utils } from '../../../../utils/utils';
|
import { Utils } from '../../../../utils/utils';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
@@ -175,22 +176,25 @@ const Container = () => {
|
|||||||
{errorMsg && (<div className="d-center-middle error">{gettext(errorMsg)}</div>)}
|
{errorMsg && (<div className="d-center-middle error">{gettext(errorMsg)}</div>)}
|
||||||
{!errorMsg && (
|
{!errorMsg && (
|
||||||
<div className="sf-metadata-container" ref={containerRef}>
|
<div className="sf-metadata-container" ref={containerRef}>
|
||||||
<TableMain
|
{metadata.view.type === 'table' && (
|
||||||
isGroupView={isGroupView}
|
<TableMain
|
||||||
isLoadingMore={isLoadingMore}
|
isGroupView={isGroupView}
|
||||||
loadMore={loadMore}
|
isLoadingMore={isLoadingMore}
|
||||||
metadata={metadata}
|
loadMore={loadMore}
|
||||||
modifyRecord={modifyRecord}
|
metadata={metadata}
|
||||||
modifyRecords={modifyRecords}
|
modifyRecord={modifyRecord}
|
||||||
recordGetterById={recordGetterById}
|
modifyRecords={modifyRecords}
|
||||||
recordGetterByIndex={recordGetterByIndex}
|
recordGetterById={recordGetterById}
|
||||||
getTableContentRect={getTableContentRect}
|
recordGetterByIndex={recordGetterByIndex}
|
||||||
getAdjacentRowsIds={getAdjacentRowsIds}
|
getTableContentRect={getTableContentRect}
|
||||||
loadAll={loadAll}
|
getAdjacentRowsIds={getAdjacentRowsIds}
|
||||||
renameColumn={renameColumn}
|
loadAll={loadAll}
|
||||||
deleteColumn={deleteColumn}
|
renameColumn={renameColumn}
|
||||||
modifyColumnData={modifyColumnData}
|
deleteColumn={deleteColumn}
|
||||||
/>
|
modifyColumnData={modifyColumnData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{metadata.view.type === 'image' && (<Gallery />)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
.metadata-gallery-container {
|
||||||
|
height: calc(100vh - 100px);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-gallery-image-list {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-gallery-image-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-gallery-grid-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
@@ -0,0 +1,92 @@
|
|||||||
|
import React, { useMemo, useState, useEffect, useRef } from 'react';
|
||||||
|
import { CenteredLoading } from '@seafile/sf-metadata-ui-component';
|
||||||
|
import { useMetadata } from '../../../hooks';
|
||||||
|
import { Utils } from '../../../../../utils/utils';
|
||||||
|
import { PRIVATE_COLUMN_KEY } from '../../../_basic';
|
||||||
|
import { siteRoot } from '../../../../../utils/constants';
|
||||||
|
import { EVENT_BUS_TYPE } from '../../../constants';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const Gallery = () => {
|
||||||
|
const [imageWidth, setImageWidth] = useState(100);
|
||||||
|
const [columns, setColumns] = useState(8);
|
||||||
|
const [containerWidth, setContainerWidth] = useState(960);
|
||||||
|
const [adjustValue, setAdjustValue] = useState(0);
|
||||||
|
const { isLoading, metadata } = useMetadata();
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
setContainerWidth(containerRef.current.offsetWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(handleResize);
|
||||||
|
const currentContainer = containerRef.current;
|
||||||
|
|
||||||
|
if (currentContainer) {
|
||||||
|
resizeObserver.observe(currentContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (currentContainer) {
|
||||||
|
resizeObserver.unobserve(currentContainer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, (adjust) => {
|
||||||
|
setAdjustValue(adjust);
|
||||||
|
});
|
||||||
|
}, [columns]);
|
||||||
|
|
||||||
|
const imageItems = useMemo(() => {
|
||||||
|
return metadata.rows
|
||||||
|
.filter(row => Utils.imageCheck(row[PRIVATE_COLUMN_KEY.FILE_NAME]))
|
||||||
|
.map(item => {
|
||||||
|
const fileName = item[PRIVATE_COLUMN_KEY.FILE_NAME];
|
||||||
|
const parentDir = item[PRIVATE_COLUMN_KEY.PARENT_DIR];
|
||||||
|
const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
|
||||||
|
const date = item[PRIVATE_COLUMN_KEY.FILE_CTIME].split('T')[0];
|
||||||
|
|
||||||
|
const src = `${siteRoot}repo/${repoID}/raw${path}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: fileName,
|
||||||
|
url: `${siteRoot}lib/${repoID}/file${path}`,
|
||||||
|
src: src,
|
||||||
|
date: date,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [metadata, repoID]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const columns = (Utils.isDesktop() ? 8 : 4) - adjustValue;
|
||||||
|
const adjustedImageWidth = (containerWidth - columns * 2 - 2) / columns;
|
||||||
|
setColumns(columns);
|
||||||
|
setImageWidth(adjustedImageWidth);
|
||||||
|
}, [containerWidth, adjustValue]);
|
||||||
|
|
||||||
|
if (isLoading) return (<CenteredLoading />);
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="metadata-gallery-container">
|
||||||
|
<ul className="metadata-gallery-image-list" style={{ gridTemplateColumns: `repeat(${columns}, 1fr)` }}>
|
||||||
|
{imageItems.map((img, index) => (
|
||||||
|
<li key={index} className='metadata-gallery-image-item' style={{ width: imageWidth, height: imageWidth }}>
|
||||||
|
<img
|
||||||
|
className="metadata-gallery-grid-image"
|
||||||
|
src={img.src}
|
||||||
|
alt={img.name}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Gallery;
|
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../data-process-setter';
|
import { SliderSetter, FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../data-process-setter';
|
||||||
import { EVENT_BUS_TYPE } from '../../constants';
|
import { EVENT_BUS_TYPE } from '../../constants';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
@@ -71,6 +71,7 @@ const ViewToolBar = ({ viewId }) => {
|
|||||||
onClick={onHeaderClick}
|
onClick={onHeaderClick}
|
||||||
>
|
>
|
||||||
<div className="sf-metadata-tool-left-operations">
|
<div className="sf-metadata-tool-left-operations">
|
||||||
|
{view.type === 'image' && <SliderSetter />}
|
||||||
<FilterSetter
|
<FilterSetter
|
||||||
isNeedSubmit={true}
|
isNeedSubmit={true}
|
||||||
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
|
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
|
||||||
|
@@ -47,4 +47,6 @@ export const EVENT_BUS_TYPE = {
|
|||||||
SAVED: 'saved',
|
SAVED: 'saved',
|
||||||
ERROR: 'error',
|
ERROR: 'error',
|
||||||
|
|
||||||
|
// gallery
|
||||||
|
MODIFY_GALLERY_COLUMNS: 'modify_gallery_columns',
|
||||||
};
|
};
|
||||||
|
@@ -4,6 +4,7 @@ class View {
|
|||||||
constructor(object, columns) {
|
constructor(object, columns) {
|
||||||
|
|
||||||
this._id = object._id || '';
|
this._id = object._id || '';
|
||||||
|
this.type = object.type || 'table';
|
||||||
|
|
||||||
// filter
|
// filter
|
||||||
this.filters = object.filters || [];
|
this.filters = object.filters || [];
|
||||||
|
@@ -271,6 +271,7 @@ class LibContentContainer extends React.Component {
|
|||||||
sortOrder={this.props.sortOrder}
|
sortOrder={this.props.sortOrder}
|
||||||
sortItems={this.props.sortItems}
|
sortItems={this.props.sortItems}
|
||||||
viewId={this.props.viewId}
|
viewId={this.props.viewId}
|
||||||
|
viewType={this.props.viewType}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@@ -575,6 +575,7 @@ class MetadataViews(APIView):
|
|||||||
def post(self, request, repo_id):
|
def post(self, request, repo_id):
|
||||||
# Add a metadata view
|
# Add a metadata view
|
||||||
view_name = request.data.get('name')
|
view_name = request.data.get('name')
|
||||||
|
view_type = request.data.get('type', 'table')
|
||||||
if not view_name:
|
if not view_name:
|
||||||
error_msg = 'view name is invalid.'
|
error_msg = 'view name is invalid.'
|
||||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
@@ -595,7 +596,7 @@ class MetadataViews(APIView):
|
|||||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_view = RepoMetadataViews.objects.add_view(repo_id, view_name)
|
new_view = RepoMetadataViews.objects.add_view(repo_id, view_name, view_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
error_msg = 'Internal Server Error'
|
error_msg = 'Internal Server Error'
|
||||||
|
@@ -61,8 +61,9 @@ class RepoMetadata(models.Model):
|
|||||||
|
|
||||||
class RepoView(object):
|
class RepoView(object):
|
||||||
|
|
||||||
def __init__(self, name, view_ids=None):
|
def __init__(self, name, type= 'table', view_ids=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.type = type
|
||||||
self.view_json = {}
|
self.view_json = {}
|
||||||
|
|
||||||
self.init_view(view_ids)
|
self.init_view(view_ids)
|
||||||
@@ -77,12 +78,13 @@ class RepoView(object):
|
|||||||
"groupbys": [],
|
"groupbys": [],
|
||||||
"filter_conjunction": "And",
|
"filter_conjunction": "And",
|
||||||
"hidden_columns": [],
|
"hidden_columns": [],
|
||||||
|
"type": self.type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RepoMetadataViewsManager(models.Manager):
|
class RepoMetadataViewsManager(models.Manager):
|
||||||
|
|
||||||
def add_view(self, repo_id, view_name):
|
def add_view(self, repo_id, view_name, view_type):
|
||||||
metadata_views = self.filter(repo_id=repo_id).first()
|
metadata_views = self.filter(repo_id=repo_id).first()
|
||||||
if not metadata_views:
|
if not metadata_views:
|
||||||
new_view = RepoView(view_name)
|
new_view = RepoView(view_name)
|
||||||
@@ -100,7 +102,7 @@ class RepoMetadataViewsManager(models.Manager):
|
|||||||
view_details = json.loads(metadata_views.details)
|
view_details = json.loads(metadata_views.details)
|
||||||
view_name = get_no_duplicate_obj_name(view_name, metadata_views.view_names)
|
view_name = get_no_duplicate_obj_name(view_name, metadata_views.view_names)
|
||||||
exist_view_ids = metadata_views.view_ids
|
exist_view_ids = metadata_views.view_ids
|
||||||
new_view = RepoView(view_name, exist_view_ids)
|
new_view = RepoView(view_name, view_type, exist_view_ids)
|
||||||
view_json = new_view.view_json
|
view_json = new_view.view_json
|
||||||
view_id = view_json.get('_id')
|
view_id = view_json.get('_id')
|
||||||
view_details['views'].append(view_json)
|
view_details['views'].append(view_json)
|
||||||
|
Reference in New Issue
Block a user