diff --git a/package-lock.json b/package-lock.json
index 2dfe86e..ba2af5b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@seafile/seafile-editor",
- "version": "1.0.118",
+ "version": "1.0.121",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@seafile/seafile-editor",
- "version": "1.0.118",
+ "version": "1.0.121",
"license": "Apache-2.0",
"dependencies": {
"@seafile/react-image-lightbox": "2.0.5",
diff --git a/package.json b/package.json
index 29f7e6c..1fc96c8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@seafile/seafile-editor",
- "version": "1.0.118",
+ "version": "1.0.121",
"description": "",
"main": "dist/index.js",
"scripts": {
diff --git a/src/constants/event-types.js b/src/constants/event-types.js
index 07542d7..ec74138 100644
--- a/src/constants/event-types.js
+++ b/src/constants/event-types.js
@@ -6,6 +6,7 @@ export const INTERNAL_EVENTS = {
ON_SELECT_ALL_CELL: 'on_select_all_cell',
ON_TOGGLE_IMAGE_POPOVER: 'on_toggle_image_popover',
OUTLINE_STATE_CHANGED: 'outline_state_changed',
+ RESIZE_ARTICLE: 'resize_article',
};
export const EXTERNAL_EVENTS = {
diff --git a/src/containers/article-info/index.js b/src/containers/article-info/index.js
index ba5b0b9..889951c 100644
--- a/src/containers/article-info/index.js
+++ b/src/containers/article-info/index.js
@@ -2,7 +2,7 @@ import React, { useCallback, useState, useMemo, useEffect } from 'react';
import PropTypes from 'prop-types';
import ResizeWidth from './resize-width';
import EventBus from '../../utils/event-bus';
-import { EXTERNAL_EVENTS } from '../../constants/event-types';
+import { EXTERNAL_EVENTS, INTERNAL_EVENTS } from '../../constants/event-types';
import './style.css';
@@ -34,13 +34,16 @@ const ArticleInfo = ({ isVisible }) => {
}, []);
const resizeWidthEnd = useCallback((width) => {
- const panelWidth = JSON.parse(window.localStorage.getItem('sf-editor-panel-width') || '{}');
- window.localStorage.setItem('sf-editor-panel-width', JSON.stringify({ ...panelWidth, width }));
+ const settings = JSON.parse(window.localStorage.getItem('sf-editor') || '{}');
+ window.localStorage.setItem('sf-editor', JSON.stringify({ ...settings, panelWidth: width }));
+ const eventBus = EventBus.getInstance();
+ eventBus.dispatch(INTERNAL_EVENTS.RESIZE_ARTICLE);
}, []);
useEffect(() => {
- const panelWidth = JSON.parse(window.localStorage.getItem('sf-editor-panel-width', '{}')) || {};
- const width = Math.max(MIN_PANEL_WIDTH, Math.min(parseInt(panelWidth.width, 10) || MIN_PANEL_WIDTH, MAX_PANEL_WIDTH));
+ const settings = JSON.parse(window.localStorage.getItem('sf-editor', '{}')) || {};
+ const { panelWidth } = settings;
+ const width = Math.max(MIN_PANEL_WIDTH, Math.min(parseInt(panelWidth, 10) || MIN_PANEL_WIDTH, MAX_PANEL_WIDTH));
setWidth(width);
}, []);
@@ -57,6 +60,11 @@ const ArticleInfo = ({ isVisible }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ useEffect(() => {
+ const eventBus = EventBus.getInstance();
+ eventBus.dispatch(INTERNAL_EVENTS.RESIZE_ARTICLE);
+ }, [isVisible, fileDetails]);
+
const { component: fileDetailsComponent, props: fileDetailsProps } = fileDetails || {};
return (
diff --git a/src/containers/outline/index.js b/src/containers/outline/index.js
index 810d34c..c6aea3b 100644
--- a/src/containers/outline/index.js
+++ b/src/containers/outline/index.js
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
+import classnames from 'classnames';
import OutlineItem from './outline-item';
import { useScrollContext } from '../../hooks/use-scroll-context';
import { TRANSLATE_NAMESPACE } from '../../constants';
@@ -9,6 +10,20 @@ import { INTERNAL_EVENTS } from '../../constants/event-types';
import './style.css';
+export const getOutlineSetting = () => {
+ const currentValue = localStorage.getItem('sf-editor');
+ const config = currentValue ? JSON.parse(currentValue) : {};
+ const { outlineOpen = false } = config;
+ return outlineOpen;
+};
+
+export const setOutlineSetting = (isShown) => {
+ const currentValue = localStorage.getItem('sf-editor');
+ const config = currentValue ? JSON.parse(currentValue) : {};
+ config['outlineOpen'] = isShown;
+ localStorage.setItem('sf-editor', JSON.stringify(config));
+};
+
const getHeaderList = (children) => {
const headerList = [];
children.forEach((node) => {
@@ -23,7 +38,6 @@ const Outline = ({ editor }) => {
const { t } = useTranslation(TRANSLATE_NAMESPACE);
const scrollRef = useScrollContext();
const [headerList, setHeaderList] = useState([]);
- const [activeId, setActiveId] = useState('');
const [isShown, setIsShown] = useState(false);
const [scrollLeft, setScrollLeft] = useState(0);
@@ -32,47 +46,19 @@ const Outline = ({ editor }) => {
setHeaderList(headerList);
}, [editor.children]);
- const handleScroll = useCallback((e) => {
- const scrollTop = scrollRef.current.scrollTop;
- const styles = getComputedStyle(scrollRef?.current);
- const paddingTop = parseInt(styles.paddingTop);
- for (let i = 0; i < headerList.length; i++) {
- const headerItem = headerList[i];
- const dom = document.getElementById(headerItem.id);
- const { offsetTop, offsetHeight } = dom;
- const styles = getComputedStyle(dom);
- const marginTop = parseInt(styles.marginTop);
- if (offsetTop + offsetHeight + marginTop > scrollTop - paddingTop) {
- setActiveId(headerItem.id);
- break;
- }
- }
- }, [headerList, scrollRef]);
-
- useEffect(() => {
- let observerRefValue = null;
- if (scrollRef.current) {
- scrollRef.current.addEventListener('scroll', handleScroll);
- observerRefValue = scrollRef.current;
- }
-
- return () => {
- observerRefValue.removeEventListener('scroll', handleScroll);
- };
- }, [handleScroll, scrollRef]);
+ const updateOutlineState = useCallback((nextState) => {
+ setOutlineSetting(nextState);
+ setIsShown(nextState);
+ const eventBus = EventBus.getInstance();
+ eventBus.dispatch(INTERNAL_EVENTS.OUTLINE_STATE_CHANGED);
+ }, []);
const toggleShow = useCallback(() => {
- setIsShown(prevIsShown => {
- const newIsShown = !prevIsShown;
- setTimeout(() => {
- const eventBus = EventBus.getInstance();
- eventBus.dispatch(INTERNAL_EVENTS.OUTLINE_STATE_CHANGED, newIsShown);
- }, 0);
- return newIsShown;
- });
- }, []);
+ const nextState = !isShown;
+ updateOutlineState(nextState);
+ }, [isShown, updateOutlineState]);
- useEffect(() => {
+ useEffect(() => {
if (!scrollRef.current) return;
const updateScrollLeft = () => {
@@ -86,35 +72,42 @@ const Outline = ({ editor }) => {
};
}, [scrollRef]);
+ useEffect(() => {
+ const outlineState = getOutlineSetting();
+ console.log(outlineState);
+ updateOutlineState(outlineState);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
return (
-
- {isShown && (
- <>
-
-
{t('Outline')}
-
-
- {headerList.length === 0 ? (
-
{t('No_outline')}
- ) : (
-
- {headerList.map((node, index) => (
-
- ))}
+
+
+ {isShown && (
+ <>
+
+
{t('Outline')}
+
- )}
- >
- )}
+ {headerList.length === 0 ? (
+
{t('No_outline')}
+ ) : (
+
+ {headerList.map((node, index) => (
+
+ ))}
+
+ )}
+ >
+ )}
+
{!isShown && (
- <>
-
- >
+
)}
);
diff --git a/src/containers/outline/outline-item.js b/src/containers/outline/outline-item.js
index 09937e8..9b8520d 100644
--- a/src/containers/outline/outline-item.js
+++ b/src/containers/outline/outline-item.js
@@ -1,21 +1,36 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-const OutlineItem = ({ node, activeId }) => {
+const OutlineItem = ({ node }) => {
+ const [isHighlighted, setIsHighlighted] = useState(false);
+
+ const onMouseOver = useCallback(() => {
+ setIsHighlighted(true);
+ }, []);
+
+ const onMouseOut = useCallback(() => {
+ setIsHighlighted(false);
+ }, []);
+
const onItemClick = useCallback(() => {
const { id } = node;
document.getElementById(id).scrollIntoView();
}, [node]);
- const className = classNames({
- 'outline-h2': node.type === 'header2',
- 'outline-h3': node.type === 'header3',
- 'active': node.id === activeId,
+ const className = classNames('sf-editor-outline-item', {
+ 'pl-5': node.type === 'header2',
+ 'pl-7': node.type === 'header3',
+ 'active': isHighlighted,
});
return (
-
+
{node.children.map(child => child.text).join('')}
);
diff --git a/src/containers/outline/style.css b/src/containers/outline/style.css
index 2564407..9b86fd1 100644
--- a/src/containers/outline/style.css
+++ b/src/containers/outline/style.css
@@ -1,16 +1,26 @@
-.sf-editor-outline {
- width: 220px;
+.sf-editor-outline-wrapper {
display: flex;
+ margin: 20px 30px 20px 16px;
min-height: 0;
- flex-direction: column;
- font-size: 14px;
+ pointer-events: none;
position: fixed;
- margin: 20px 30px 20px 16px;
+ bottom: 0;
top: 100px;
+ z-index: 101;
+}
+
+.sf-editor-outline-wrapper.active {
+ pointer-events: all;
}
-.sf-editor-outline.active {
- pointer-events: all;
+.sf-editor-outline {
+ display: flex;
+ flex: 1 1;
+ flex-direction: column;
+ font-size: 14px;
+ min-height: 0;
+ position: relative;
+ width: 220px;
}
.sf-editor-outline-header {
@@ -38,23 +48,27 @@
}
.sf-editor-outline-list-container {
- padding: 0.5rem 0;
- flex: 1 1;
display: flex;
+ flex: 1 1;
flex-direction: column;
+ list-style: none;
overflow-x: hidden;
overflow-y: auto;
- max-height: 400px;
+ padding: .5rem 0;
+ word-break: break-word;
}
.sf-editor-outline-item {
- padding: 4px 0;
- padding-right: 6px;
- max-width: 100%;
+ padding: 4px 6px 4px 0;
+ width: 100%;
overflow-wrap: anywhere;
cursor: pointer;
}
+.sf-editor-outline-item.active {
+ color: #eb8205;
+}
+
.sf-editor-outline-menu {
line-height: 1;
font-size: 14px;
@@ -70,7 +84,7 @@
justify-content: center;
position: absolute;
top: 20px;
- left: -15px;
+ left: -15px;;
pointer-events: all;
}
@@ -104,43 +118,7 @@
color: #333;
}
-.sf-editor-outline .outline-h2,
-.sf-editor-outline .outline-h3 {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.sf-editor-outline .outline-h2 {
- margin-left: 20px;
- line-height: 2.5;
- color:#364149;
- white-space: nowrap;
- cursor:pointer;
-}
-
-.sf-editor-outline .outline-h2:hover {
- color: #eb8205;
-}
-
-.sf-editor-outline .outline-h3 {
- margin-left: 40px;
- line-height: 2.5;
- color:#364149;
- white-space: nowrap;
- cursor:pointer;
-}
-
-.sf-editor-outline .outline-h3:hover {
- color: #eb8205;
-}
-
.sf-editor-outline .empty-container {
margin-top: 10px;
text-align: center;
}
-
-.sf-editor-outline .outline-h2.active,
-.sf-editor-outline .outline-h3.active {
- color: #eb8205;
-}
diff --git a/src/editors/slate-editor/editor-help/index.js b/src/editors/slate-editor/editor-help/index.js
index 0dfd858..f3e7497 100644
--- a/src/editors/slate-editor/editor-help/index.js
+++ b/src/editors/slate-editor/editor-help/index.js
@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import classNames from 'classnames';
import EventBus from '../../../utils/event-bus';
-import { EXTERNAL_EVENTS } from '../../../constants/event-types';
+import { EXTERNAL_EVENTS, INTERNAL_EVENTS } from '../../../constants/event-types';
import HotKeysHelper from '../../../containers/hotkeys-helper';
import ArticleInfo from '../../../containers/article-info';
@@ -20,6 +20,8 @@ export default function EditorHelp({ children }) {
const updateHelpInfoState = useCallback((state) => {
setIsShowHelpInfo(state);
setIsShowArticleInfo(false);
+ const eventBus = EventBus.getInstance();
+ eventBus.dispatch(INTERNAL_EVENTS.RESIZE_ARTICLE);
}, []);
useEffect(() => {
diff --git a/src/editors/slate-editor/index.js b/src/editors/slate-editor/index.js
index dd29112..8421848 100644
--- a/src/editors/slate-editor/index.js
+++ b/src/editors/slate-editor/index.js
@@ -10,7 +10,7 @@ import { focusEditor } from '../../extension/core';
import { ScrollContext } from '../../hooks/use-scroll-context';
import useSeafileUtils from '../../hooks/use-insert-image';
import { isDocumentEmpty, isMac } from '../../utils/common';
-import Outline from "../../containers/outline";
+import Outline, { getOutlineSetting } from '../../containers/outline';
import { INTERNAL_EVENTS } from '../../constants/event-types';
import './style.css';
@@ -30,23 +30,32 @@ export default function SlateEditor({ value, editorApi, onSave, onContentChanged
const decorate = useHighlight(editor);
- //Adjust article container margin-left value according to isShown of the outline and width of window
- const handleWindowResize = (newIsShown) => {
+ // Adjust article container margin-left value according to isShown of the outline and width of window
+ const handleWindowResize = useCallback(() => {
const rect = scrollRef.current.getBoundingClientRect();
const articleElement = document.querySelector('.article');
const articleRect = articleElement ? articleElement.getBoundingClientRect() : null;
- if (newIsShown && articleRect && (rect.width - articleRect.width) / 2 < 280) {
- setContainerStyle({ marginLeft: '280px' });
+ const isOutlineShow = getOutlineSetting();
+ if (isOutlineShow && articleRect && (rect.width - articleRect.width) / 2 < 280) {
+ setContainerStyle({ marginLeft: 280 });
} else {
setContainerStyle({});
}
- }
+ }, []);
useEffect(() => {
+ handleWindowResize();
+ window.addEventListener('resize', handleWindowResize);
const eventBus = EventBus.getInstance();
const unsubscribeOutline = eventBus.subscribe(INTERNAL_EVENTS.OUTLINE_STATE_CHANGED, handleWindowResize);
- return unsubscribeOutline;
- }, [handleWindowResize]);
+ const unsubscribeResizeArticle = eventBus.subscribe(INTERNAL_EVENTS.RESIZE_ARTICLE, handleWindowResize);
+ return () => {
+ window.removeEventListener('resize', handleWindowResize);
+ unsubscribeOutline();
+ unsubscribeResizeArticle();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
const onChange = useCallback((value) => {
setSlateValue(value);