Skip to content

Commit

Permalink
fix: outline style (#215)
Browse files Browse the repository at this point in the history
* fix: outline style

* feat: update version

* feat: update version

* feat: update version

---------

Co-authored-by: 杨国璇 <[email protected]>
  • Loading branch information
YangGuoXuan-0503 and 杨国璇 authored Oct 12, 2024
1 parent e0ea51d commit 61fdde4
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 139 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@seafile/seafile-editor",
"version": "1.0.118",
"version": "1.0.121",
"description": "",
"main": "dist/index.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/constants/event-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
18 changes: 13 additions & 5 deletions src/containers/article-info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
}, []);

Expand All @@ -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 (
<div className="sf-article-info-container-wrapper" style={containerWrapperStyle}>
Expand Down
123 changes: 58 additions & 65 deletions src/containers/outline/index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) => {
Expand All @@ -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);

Expand All @@ -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 = () => {
Expand All @@ -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 (
<div className="sf-editor-outline" style={{ left: -scrollLeft }}>
{isShown && (
<>
<div className="sf-editor-outline-header">
<h2 className="sf-editor-outline-header_title">{t('Outline')}</h2>
<span className="sf-editor-outline-header_close iconfont icon-x" onClick={toggleShow}></span>
</div>
{headerList.length === 0 ? (
<div className="empty-container">{t('No_outline')}</div>
) : (
<div className="sf-editor-outline-list-container">
{headerList.map((node, index) => (
<OutlineItem key={index} node={node} activeId={activeId} />
))}
<div className={classnames('sf-editor-outline-wrapper', { 'active': isShown })} style={{ left: -scrollLeft }}>
<div className="sf-editor-outline" >
{isShown && (
<>
<div className="sf-editor-outline-header">
<h2 className="sf-editor-outline-header_title">{t('Outline')}</h2>
<span className="sf-editor-outline-header_close iconfont icon-x" onClick={toggleShow}></span>
</div>
)}
</>
)}
{headerList.length === 0 ? (
<div className="empty-container">{t('No_outline')}</div>
) : (
<div className="sf-editor-outline-list-container">
{headerList.map((node, index) => (
<OutlineItem key={index} node={node} />
))}
</div>
)}
</>
)}
</div>
{!isShown && (
<>
<span
id="sf-editor-outline-menu"
className="sf-editor-outline-menu sf-edito-tooltip iconfont icon-outline"
onClick={toggleShow}
>
<span className="custom-tooltip">{t('Outline')}</span>
</span>
</>
<span
id="sf-editor-outline-menu"
className="sf-editor-outline-menu sf-editor-tooltip iconfont icon-outline"
onClick={toggleShow}
>
<span className="custom-tooltip">{t('Outline')}</span>
</span>
)}
</div>
);
Expand Down
29 changes: 22 additions & 7 deletions src/containers/outline/outline-item.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className={className} onClick={onItemClick}>
<div
className={className}
onClick={onItemClick}
onMouseOver={onMouseOver}
onMouseOut={onMouseOut}
>
{node.children.map(child => child.text).join('')}
</div>
);
Expand Down
Loading

0 comments on commit 61fdde4

Please sign in to comment.