From a3ed7f298dd3af50b02e76c864dc33119fb678cc Mon Sep 17 00:00:00 2001
From: liuhongbo <916196375@qq.com>
Date: Wed, 19 Jun 2024 16:52:27 +0800
Subject: [PATCH] feat: set icon
---
frontend/package-lock.json | 39 ++
frontend/package.json | 4 +
.../wiki2/editor-component/page-header.jsx | 165 ++++++++
frontend/src/pages/wiki2/index.js | 2 +-
frontend/src/pages/wiki2/main-panel.js | 35 +-
frontend/src/pages/wiki2/top-nav/index.jsx | 15 +-
.../wiki2/view-structure/views/view-item.js | 352 ++++++++++++++++++
frontend/src/pages/wiki2/wiki.css | 133 ++++++-
8 files changed, 707 insertions(+), 38 deletions(-)
create mode 100644 frontend/src/pages/wiki2/editor-component/page-header.jsx
create mode 100644 frontend/src/pages/wiki2/view-structure/views/view-item.js
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 0beb5c17b4..4190e778dd 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -11,6 +11,8 @@
"@codemirror/lang-markdown": "6.2.3",
"@codemirror/language-data": "6.3.1",
"@codemirror/view": "6.22.1",
+ "@emoji-mart/data": "^1.2.1",
+ "@emoji-mart/react": "^1.1.1",
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "2.0.2",
"@seafile/resumablejs": "1.1.16",
@@ -26,6 +28,8 @@
"copy-to-clipboard": "^3.0.8",
"crypto-js": "4.2.0",
"deep-copy": "1.4.2",
+ "emoji-mart": "^5.6.0",
+ "glamor": "^2.20.40",
"i18next": "^17.0.13",
"i18next-browser-languagedetector": "^3.0.3",
"i18next-xhr-backend": "^3.1.2",
@@ -2820,6 +2824,20 @@
"postcss-selector-parser": "^6.0.10"
}
},
+ "node_modules/@emoji-mart/data": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
+ "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw=="
+ },
+ "node_modules/@emoji-mart/react": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz",
+ "integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==",
+ "peerDependencies": {
+ "emoji-mart": "^5.2",
+ "react": "^16.8 || ^17 || ^18"
+ }
+ },
"node_modules/@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
@@ -10374,6 +10392,11 @@
"url": "https://github.com/sindresorhus/emittery?sponsor=1"
}
},
+ "node_modules/emoji-mart": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz",
+ "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow=="
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -30527,6 +30550,17 @@
"dev": true,
"requires": {}
},
+ "@emoji-mart/data": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
+ "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw=="
+ },
+ "@emoji-mart/react": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz",
+ "integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==",
+ "requires": {}
+ },
"@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
@@ -36409,6 +36443,11 @@
"integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==",
"dev": true
},
+ "emoji-mart": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz",
+ "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow=="
+ },
"emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index e60c4aa086..a30a224e6e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,6 +6,8 @@
"@codemirror/lang-markdown": "6.2.3",
"@codemirror/language-data": "6.3.1",
"@codemirror/view": "6.22.1",
+ "@emoji-mart/data": "^1.2.1",
+ "@emoji-mart/react": "^1.1.1",
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "2.0.2",
"@seafile/resumablejs": "1.1.16",
@@ -21,6 +23,8 @@
"copy-to-clipboard": "^3.0.8",
"crypto-js": "4.2.0",
"deep-copy": "1.4.2",
+ "emoji-mart": "^5.6.0",
+ "glamor": "^2.20.40",
"i18next": "^17.0.13",
"i18next-browser-languagedetector": "^3.0.3",
"i18next-xhr-backend": "^3.1.2",
diff --git a/frontend/src/pages/wiki2/editor-component/page-header.jsx b/frontend/src/pages/wiki2/editor-component/page-header.jsx
new file mode 100644
index 0000000000..962c720d48
--- /dev/null
+++ b/frontend/src/pages/wiki2/editor-component/page-header.jsx
@@ -0,0 +1,165 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import PropTypes from 'prop-types';
+import { Input, Popover, PopoverBody } from 'reactstrap';
+import classnames from 'classnames';
+import data from '@emoji-mart/data';
+import Picker from '@emoji-mart/react';
+import { init } from 'emoji-mart';
+import { gettext } from '../../../utils/constants';
+
+init({ data }); // init data for emoji-mart, used in Picker and `getRandomEmoji` method
+
+const propTypes = {
+ currentPageConfig: PropTypes.object.isRequired,
+ onUpdatePage: PropTypes.func.isRequired,
+};
+
+const PageHeader = ({ currentPageConfig, onUpdatePage }) => {
+ const [isShowController, setIsShowController] = useState(false);
+ const [isShowIconPanel, setIsShowIconPanel] = useState(false);
+ const iconPanelPopoverRef = useRef(null);
+
+ console.log('currentPageConfig', currentPageConfig);
+
+ const handleRenameDocument = useCallback((e) => {
+ const { nativeEvent: { isComposing } } = e;
+ if (isComposing) return;
+
+ const newName = e.target.value.trim();
+ const { id, name, icon } = currentPageConfig;
+ if (newName === name) return;
+ const pageConfig = { name: newName, icon };
+ onUpdatePage && onUpdatePage(id, pageConfig);
+ }, [currentPageConfig, onUpdatePage]);
+
+ const changeControllerDisplayed = useCallback(() => {
+ setIsShowController(!isShowController);
+ }, [isShowController]);
+
+ const handleSetIcon = useCallback((emoji, cb) => {
+ onUpdatePage(currentPageConfig.id, { name: currentPageConfig.name, icon: emoji });
+ cb && cb();
+ }, [currentPageConfig.id, currentPageConfig.name, onUpdatePage]);
+
+ const setRandomEmoji = useCallback(() => {
+ const nativeEmojis = Reflect.ownKeys(data.natives);
+ const emojiCount = nativeEmojis.length;
+ const emoji = nativeEmojis[Math.floor(Math.random() * emojiCount)];
+ handleSetIcon(emoji);
+ }, [handleSetIcon]);
+
+ const handleIconPanelDisplayedChange = useCallback(() => {
+ setIsShowIconPanel(!isShowIconPanel);
+ }, [isShowIconPanel]);
+
+ const handleClickAddIcon = useCallback(() => {
+ setRandomEmoji();
+ handleIconPanelDisplayedChange();
+ }, [handleIconPanelDisplayedChange, setRandomEmoji]);
+
+ const handleIconPanelListener = useCallback((e) => {
+ const isClickInPopover = iconPanelPopoverRef.current.contains(e.target);
+ if (!isClickInPopover) handleIconPanelDisplayedChange();
+ }, [handleIconPanelDisplayedChange]);
+
+ // Update current page favicon
+ useEffect(() => {
+ let faviconUrl = '';
+ if (currentPageConfig.icon) {
+ faviconUrl = `data:image/svg+xml,`;
+ } else {
+ const { serviceUrl, mediaUrl, faviconPath } = window.seafile;
+ faviconUrl = `${serviceUrl}${mediaUrl}${faviconPath}`;
+ }
+ document.getElementById('favicon').href = faviconUrl;
+ }, [currentPageConfig.icon]);
+
+ useEffect(() => {
+ if (isShowIconPanel) {
+ setTimeout(() => {
+ // Avoid open behavior closing popover
+ addEventListener('click', handleIconPanelListener);
+ }, 0);
+ }
+ if (!isShowIconPanel) removeEventListener('click', handleIconPanelListener);
+
+ return () => {
+ removeEventListener('click', handleIconPanelListener);
+ };
+ }, [handleIconPanelListener, isShowIconPanel]);
+
+ const handleIconRemove = () => {
+ handleSetIcon('');
+ handleIconPanelDisplayedChange();
+
+ };
+
+ const handleAddCover = useCallback(() => { }, []);
+
+
+
+ return (
+
+
+
+
+
+
+
+ {currentPageConfig.icon}
+
+
+
void 0}
+ placement="bottom"
+ isOpen={currentPageConfig.icon && isShowIconPanel}
+ innerClassName='wiki-icon-panel'
+ hideArrow={true}
+ >
+
+
+ {gettext('Emojis')}
+ {gettext('Remove')}
+
+
+ handleSetIcon(emoji.native, handleIconPanelDisplayedChange)}
+ previewPosition="none"
+ skinTonePosition="none"
+ locale={window.seafile.lang.slice(0, 2)}
+ maxFrequentRows={2}
+ />
+
+
+
+
+ {!currentPageConfig.icon && (
+
+
+ {gettext('Add icon')}
+
+ )}
+
+
+ {gettext('Add cover')}
+
+
+
+
+
+
+ );
+};
+
+PageHeader.propTypes = propTypes;
+
+export default PageHeader;
\ No newline at end of file
diff --git a/frontend/src/pages/wiki2/index.js b/frontend/src/pages/wiki2/index.js
index b61d42697e..f1dacc7833 100644
--- a/frontend/src/pages/wiki2/index.js
+++ b/frontend/src/pages/wiki2/index.js
@@ -197,7 +197,7 @@ class Wiki extends Component {
}, () => {
callback && callback();
});
- this.cacheHistoryFiles(docUuid,name,id);
+ this.cacheHistoryFiles(docUuid, name, id);
};
onUpdatePage = (pageId, newPage) => {
diff --git a/frontend/src/pages/wiki2/main-panel.js b/frontend/src/pages/wiki2/main-panel.js
index 9503cebc62..8e728a8ae2 100644
--- a/frontend/src/pages/wiki2/main-panel.js
+++ b/frontend/src/pages/wiki2/main-panel.js
@@ -1,13 +1,13 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { SdocWikiViewer } from '@seafile/sdoc-editor';
-import { Input } from 'reactstrap';
import { gettext, username } from '../../utils/constants';
import Loading from '../../components/loading';
import { Utils } from '../../utils/utils';
import Account from '../../components/common/account';
import WikiTopNav from './top-nav';
import { getCurrentPageConfig } from './utils';
+import PageHeader from './editor-component/page-header';
const propTypes = {
path: PropTypes.string.isRequired,
@@ -32,6 +32,7 @@ class MainPanel extends Component {
docUuid: '',
currentPageConfig: {},
};
+ this.scrollRef = React.createRef();
}
static getDerivedStateFromProps(props, state) {
@@ -53,20 +54,10 @@ class MainPanel extends Component {
return { ...props, docUuid: window.seafile.docUuid, currentPageConfig };
}
- handleRenameDocument = (e) => {
- const { nativeEvent: { isComposing } } = e;
- if (isComposing) return;
- const newName = e.target.value.trim();
- const { currentPageConfig } = this.state;
- const { id, name, icon } = currentPageConfig;
- if (newName === name) return;
- const pageConfig = { name: newName, icon };
- this.props.onUpdatePage(id, pageConfig);
- };
render() {
- const { permission, pathExist, isDataLoading, isViewFile, config } = this.props;
+ const { permission, pathExist, isDataLoading, isViewFile, config, onUpdatePage } = this.props;
const { currentPageConfig = {}, } = this.state;
const isViewingFile = pathExist && !isDataLoading && isViewFile;
const isReadOnly = !(permission === 'rw');
@@ -77,6 +68,7 @@ class MainPanel extends Component {
{username &&
@@ -89,14 +81,17 @@ class MainPanel extends Component {
}
{this.props.pathExist && this.props.isDataLoading && }
{isViewingFile && Utils.isSdocFile(this.props.path) && (
- <>
- }
- />
- >
+
)}
diff --git a/frontend/src/pages/wiki2/top-nav/index.jsx b/frontend/src/pages/wiki2/top-nav/index.jsx
index 94ac31a68e..683f0a81e7 100644
--- a/frontend/src/pages/wiki2/top-nav/index.jsx
+++ b/frontend/src/pages/wiki2/top-nav/index.jsx
@@ -37,16 +37,26 @@ function getPaths(navigation, currentPageId, pages) {
return pathStr.split('-').map(id => idPageMap[id]);
}
-function WikiTopNav({ config, currentPageId }) {
+function WikiTopNav({ config, currentPageId, currentPageConfig }) {
const { navigation, pages } = config;
const paths = getPaths(navigation, currentPageId, pages);
+ const customIcon = currentPageConfig.icon;
return (
{paths.map((item, index) => {
return (
-
+ {
+ item.type === 'folder' && ()
+ }
+ {
+ item.type !== 'folder' && (
+ customIcon
+ ? {customIcon}
+ :
+ )
+ }
{item.name}
{index !== paths.length - 1 && /
}
@@ -60,6 +70,7 @@ function WikiTopNav({ config, currentPageId }) {
WikiTopNav.propTypes = {
config: PropTypes.object,
currentPageId: PropTypes.string,
+ currentPageConfig: PropTypes.object,
};
export default WikiTopNav;
diff --git a/frontend/src/pages/wiki2/view-structure/views/view-item.js b/frontend/src/pages/wiki2/view-structure/views/view-item.js
new file mode 100644
index 0000000000..7957125928
--- /dev/null
+++ b/frontend/src/pages/wiki2/view-structure/views/view-item.js
@@ -0,0 +1,352 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import ViewEditPopover from './view-edit-popover';
+import PageDropdownMenu from './page-dropdownmenu';
+import DeleteDialog from './delete-dialog';
+import { gettext } from '../../../../utils/constants';
+import AddNewPageDialog from '../add-new-page-dialog';
+import Icon from '../../../../components/icon';
+import NavItemIcon from '../nav-item-icon';
+import DraggedViewItem from '../views/dragged-view-item';
+
+class ViewItem extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ isShowViewEditor: false,
+ isShowViewOperationDropdown: false,
+ isShowDeleteDialog: false,
+ isShowInsertPage: false,
+ viewName: props.view.name || '',
+ viewIcon: props.view.icon,
+ isSelected: props.currentPageId === props.view.id,
+ isMouseEnter: false,
+ };
+ this.viewItemRef = React.createRef();
+ }
+
+ onMouseEnter = () => {
+ this.setState({ isMouseEnter: true });
+ if (this.state.isSelected) return;
+ };
+
+ onMouseMove = () => {
+ if (!this.state.isMouseEnter) this.setState({ isMouseEnter: true });
+ };
+
+ onMouseLeave = () => {
+ this.setState({ isMouseEnter: false });
+ if (this.state.isSelected) return;
+ };
+
+ onCurrentPageChanged = (currentPageId) => {
+ const { isSelected } = this.state;
+ if (currentPageId === this.props.view.id && isSelected === false) {
+ this.setState({ isSelected: true });
+ } else if (currentPageId !== this.props.view.id && isSelected === true) {
+ this.setState({ isSelected: false });
+ }
+ };
+
+ toggleViewEditor = (e) => {
+ if (e) e.stopPropagation();
+ this.setState({ isShowViewEditor: !this.state.isShowViewEditor }, () => {
+ if (!this.state.isShowViewEditor) {
+ this.saveViewProperties();
+ }
+ });
+ };
+
+ toggleInsertPage = () => {
+ this.setState({ isShowInsertPage: !this.state.isShowInsertPage });
+ };
+
+ saveViewProperties = () => {
+ const { name, icon, id } = this.props.view;
+ const { viewIcon } = this.state;
+ let viewName = this.state.viewName.trim();
+ if (viewIcon !== icon || viewName !== name) {
+ let newView = {};
+ if (viewName !== name) {
+ newView.name = viewName;
+ }
+ if (viewIcon !== icon) {
+ newView.icon = viewIcon;
+ }
+ this.props.onUpdatePage(id, newView);
+ }
+ };
+
+ onChangeName = (newViewName) => {
+ this.setState({ viewName: newViewName });
+ };
+
+ onChangeIcon = (newViewIcon) => {
+ this.setState({ viewIcon: newViewIcon });
+ };
+
+ openDeleteDialog = () => {
+ this.setState({ isShowDeleteDialog: true });
+ };
+
+ closeDeleteDialog = () => {
+ this.setState({ isShowDeleteDialog: false });
+ };
+
+ onViewOperationDropdownToggle = () => {
+ const isShowViewOperationDropdown = !this.state.isShowViewOperationDropdown;
+ this.setState({ isShowViewOperationDropdown });
+ this.changeItemFreeze(isShowViewOperationDropdown);
+ };
+
+ changeItemFreeze = (isFreeze) => {
+ if (isFreeze) {
+ this.viewItemRef.classList.add('view-freezed');
+ } else {
+ this.viewItemRef.classList.remove('view-freezed');
+ }
+ };
+
+ renderIcon = (icon) => {
+ if (!icon) {
+ return null;
+ }
+ if (icon.includes('dtable-icon')) {
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ setDocUuid = (docUuid) => {
+ window.seafile['docUuid'] = docUuid;
+ };
+
+ getFolderChildrenHeight = () => {
+ const folded = this.props.getFoldState(this.props.view.id);
+ if (folded) return 0;
+ return 'auto';
+ };
+
+ onClickFolderChildren = (e) => {
+ e.stopPropagation();
+ e.nativeEvent.stopImmediatePropagation();
+ };
+
+ renderView = (view, index, pagesLength, isOnlyOneView) => {
+ const { isEditMode, views, folderId, pathStr } = this.props;
+ const id = view.id;
+ if (!views.find(item => item.id === id)) return;
+ return (
+ item.id === id), view)}
+ viewIndex={index}
+ folderId={folderId}
+ isEditMode={isEditMode}
+ renderFolderMenuItems={this.props.renderFolderMenuItems}
+ duplicatePage={this.props.duplicatePage}
+ onSetFolderId={this.props.onSetFolderId}
+ onSelectView={this.props.onSelectView}
+ onUpdatePage={this.props.onUpdatePage}
+ onDeleteView={this.props.onDeleteView}
+ onMoveViewToFolder={(targetFolderId) => {
+ this.props.onMoveViewToFolder(folderId, view.id, targetFolderId);
+ }}
+ onMoveView={this.props.onMoveView}
+ onMoveFolder={this.props.onMoveFolder}
+ views={views}
+ pathStr={pathStr + '-' + view.id}
+ currentPageId={this.props.currentPageId}
+ addPageInside={this.props.addPageInside}
+ getFoldState={this.props.getFoldState}
+ toggleExpand={this.props.toggleExpand}
+ />
+ );
+ };
+
+ toggleExpand = (e) => {
+ e.nativeEvent.stopImmediatePropagation();
+ this.props.toggleExpand(this.props.view.id);
+ this.forceUpdate();
+ };
+
+ onAddNewPage = (newPage) => {
+ const { view } = this.props;
+ this.props.addPageInside(Object.assign({ parentPageId: view.id }, newPage));
+ };
+
+ render() {
+ const {
+ connectDragSource, connectDragPreview, connectDropTarget, isOver, canDrop, isDragging,
+ infolder, view, pagesLength, isEditMode, folderId, isOnlyOneView, pathStr,
+ } = this.props;
+ const { isShowViewEditor, viewName, viewIcon, isSelected } = this.state;
+ const isOverView = isOver && canDrop;
+ if (isSelected) this.setDocUuid(view.docUuid);
+
+ let viewCanDropTop;
+ let viewCanDrop;
+ if (infolder) {
+ viewCanDropTop = false;
+ viewCanDrop = isOverView;
+ } else {
+ viewCanDropTop = isOverView && isDragging;
+ viewCanDrop = isOverView && !isDragging;
+ }
+ let viewEditorId = `view-editor-${view.id}`;
+ let fn = isEditMode ? connectDragSource : (argu) => { argu; };
+ let childNumber = Array.isArray(view.children) ? view.children.length : 0;
+
+ const folded = this.props.getFoldState(view.id);
+ return (
+
+ {
+ fn(connectDropTarget(
+ connectDragPreview(
+
this.viewItemRef = ref}
+ onMouseEnter={this.onMouseEnter}
+ onMouseMove={this.onMouseMove}
+ onMouseLeave={this.onMouseLeave}
+ id={viewEditorId}
+ >
+
{ } : (e) => this.props.onSelectView(view.id)}>
+
+ {childNumber === 0 && (
+ view.icon
+ ? {view.icon}
+ : )
+ }
+ {(!this.state.isMouseEnter && childNumber > 0) &&
+
+ }
+ {(this.state.isMouseEnter && childNumber > 0) &&
+
+ }
+ {/* {this.renderIcon(view.icon)} */}
+ {view.name}
+ {isShowViewEditor && (
+
+ )}
+
+
+
+ {isEditMode &&
+ <>
+
+
+ {this.state.isShowViewOperationDropdown &&
+
+ }
+
+
+
+
+ >
+ }
+
+ {this.state.isShowDeleteDialog &&
+
+ }
+ {this.state.isShowInsertPage &&
+
+ }
+
+ )
+ ))
+ }
+
+ {view.children &&
+ view.children.map((item, index) => {
+ return this.renderView(item, index, pagesLength, isOnlyOneView);
+ })
+ }
+
+
+ );
+ }
+}
+
+ViewItem.propTypes = {
+ isOver: PropTypes.bool,
+ canDrop: PropTypes.bool,
+ isDragging: PropTypes.bool,
+ draggedPage: PropTypes.object,
+ isEditMode: PropTypes.bool,
+ infolder: PropTypes.bool,
+ view: PropTypes.object,
+ folder: PropTypes.object,
+ views: PropTypes.array,
+ viewIndex: PropTypes.number,
+ folderId: PropTypes.string,
+ pagesLength: PropTypes.number,
+ connectDragSource: PropTypes.func,
+ connectDragPreview: PropTypes.func,
+ connectDropTarget: PropTypes.func,
+ renderFolderMenuItems: PropTypes.func,
+ duplicatePage: PropTypes.func,
+ onSetFolderId: PropTypes.func,
+ onSelectView: PropTypes.func,
+ onUpdatePage: PropTypes.func,
+ onDeleteView: PropTypes.func,
+ onMoveViewToFolder: PropTypes.func,
+ onMoveView: PropTypes.func,
+ isOnlyOneView: PropTypes.bool,
+ onMoveFolder: PropTypes.func,
+ pathStr: PropTypes.string,
+ currentPageId: PropTypes.string,
+ addPageInside: PropTypes.func,
+ getFoldState: PropTypes.func,
+ toggleExpand: PropTypes.func,
+};
+
+export default ViewItem;
diff --git a/frontend/src/pages/wiki2/wiki.css b/frontend/src/pages/wiki2/wiki.css
index 0beb6f2fd6..3527c180f9 100644
--- a/frontend/src/pages/wiki2/wiki.css
+++ b/frontend/src/pages/wiki2/wiki.css
@@ -70,6 +70,10 @@ img[src=""] {
color: #212529;
}
+.main-panel-center .cur-view-content .sf-wiki-title:focus {
+ box-shadow: none;
+}
+
/* reset article h1 */
.wiki2-main-panel .article h1 {
margin-top: 0;
@@ -141,21 +145,6 @@ img[src=""] {
flex: 1;
display: flex;
flex-direction: column;
- overflow: hidden;
-}
-
-.sdoc-editor-container .wiki-editor-header {
- display: flex;
- justify-content: space-between;
- padding: 10px 20px;
- height: 56px;
- border-bottom: 1px solid #e5e5e5;
- background-color: #fff;
- z-index: 1000;
-}
-
-.sdoc-editor-container .wiki-editor-header .doc-ops {
- display: flex;
}
.sdoc-editor-container .wiki-viewer-container {
@@ -243,3 +232,117 @@ img[src=""] {
.sdoc-editor-container .sdoc-editor-content .article #sdoc-editor {
width: 100%;
}
+
+/* editor layout */
+.main-panel-center .cur-view-content .wiki-page-gap-container {
+ padding: 0 50px;
+ padding-left: 142px;
+}
+
+.main-panel-center .cur-view-content .sf-wiki-title {
+ padding-left: 0;
+ border: none;
+ font-weight: bold;
+ font-size: 26pt;
+}
+
+.cur-view-content .wiki-cover {
+ height: 30vh;
+ width: 100%;
+ background-color: aqua;
+}
+
+.cur-view-content .wiki-editor-container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+
+.cur-view-content .sdoc-scroll-container {
+ overflow-y: auto;
+}
+
+.cur-view-content .sdoc-editor-container .sdoc-editor-content {
+ position: static;
+}
+
+.wiki-page-controller {
+ display: flex;
+ visibility: hidden;
+}
+
+.wiki-page-controller.show {
+ visibility: visible;
+}
+
+.wiki-page-controller-item {
+ display: flex;
+ align-items: center;
+ padding: 6px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.wiki-icon-container {
+ margin-bottom: 8px;
+ line-height: 78px;
+}
+
+.wiki-icon-wrapper {
+ display: none;
+ margin-top: -48px;
+ width: 78px;
+ height: 78px;
+ font-size: 78px;
+}
+
+.wiki-icon-wrapper:hover {
+ cursor: pointer;
+ background-color: #d3c5c370;
+}
+
+.wiki-icon-wrapper.show {
+ display: block;
+}
+
+.wiki-page-controller-item .text {
+ margin-left: 6px;
+}
+
+.wiki-page-controller-item:hover {
+ background-color: #efefef;
+ border-radius: 4px;
+}
+
+.wiki-icon-panel {
+ width: 352px;
+ height: 435px;
+}
+
+.wiki-icon-panel-header {
+ display: flex;
+ justify-content: space-between;
+ padding: 6px 14px;
+ background-color: #fff;
+}
+
+.wiki-icon-panel-header>span {
+ padding: 2px 8px;
+ border-radius: 4px;
+ user-select: none;
+}
+
+.wiki-icon-panel-header>span:hover {
+ background-color: #efefef;
+ cursor: pointer;
+}
+
+.wiki-icon-panel-body {
+ padding: 0;
+ width: 352px;
+ background-color: #fff;
+}
+
+.wiki-icon-panel-body div {
+ width: 100%;
+}
\ No newline at end of file