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,${currentPageConfig.icon}`; + } 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