Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

複数タブに対応 #45

Merged
merged 25 commits into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6f492f2
react-tabs利用
Sota-Watanabe Jul 26, 2020
fae1b8b
タブ数固定の実装
Sota-Watanabe Jul 26, 2020
0a3ab8a
新規タブのみ追加(名前非参照)
Sota-Watanabe Jul 26, 2020
1b3eafe
新規タブのみ追加(名前非参照)
Sota-Watanabe Jul 26, 2020
105fc02
ストアの構成を変更
Sota-Watanabe Jul 27, 2020
8266116
ファイル新規作成機能追加
Sota-Watanabe Jul 27, 2020
3546956
動的タブ追加機能 (editorId挿入版)
Sota-Watanabe Jul 28, 2020
b49b1ba
配列への書き込み方法変更
Sota-Watanabe Jul 28, 2020
3ab0445
コメント追加
Sota-Watanabe Jul 28, 2020
36cf80c
コメント追加, editorInstanceの位置変更
Sota-Watanabe Jul 28, 2020
7dbb5a6
FileStatus判別機能の追加
Sota-Watanabe Jul 28, 2020
f2b0c75
FileStatusによりTabの色変更
Sota-Watanabe Jul 28, 2020
0482ded
console.log削除, コメント追加
Sota-Watanabe Jul 28, 2020
f843058
editorId追加
Sota-Watanabe Jul 28, 2020
63d32db
今のところ初期状態がTabなし
Sota-Watanabe Jul 28, 2020
0f33060
タブ削除機能追加, 常にエディタ画面を表示(Document >=1)
Sota-Watanabe Jul 28, 2020
48634d5
undefinedの対処
Sota-Watanabe Jul 29, 2020
2d7e84a
新規タブ起動の領域作成
Sota-Watanabe Jul 29, 2020
8588017
不要なパッケージの削除
Sota-Watanabe Jul 29, 2020
92ed864
テスト用コマンドの削除
Sota-Watanabe Jul 29, 2020
3e11e05
Merge用に細かい修正
Sota-Watanabe Jul 29, 2020
4e61383
改行追加
Sota-Watanabe Jul 29, 2020
ffcc0d1
eslint-disable, コメント削除
Sota-Watanabe Jul 29, 2020
6bcf0ca
コメント追加
Sota-Watanabe Jul 29, 2020
c18c8d1
#45 の修正
Sota-Watanabe Jul 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"dependencies": {
"ace-builds": "^1.4.11",
"electron": "^8.2.5",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-ace": "^8.1.0",
"react-dom": "^16.13.1",
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { remote } from 'electron';
import Footer from './components/Footer';
import Main from './components/Main';
import Body from './components/Body';
import TitleBar from './components/TitleBar';

export default class App extends Component {
Expand Down Expand Up @@ -38,7 +38,7 @@ export default class App extends Component {
return (
<div className="flex flex-col h-screen">
{!isFullScreen && <TitleBar />}
<Main />
<Body />
<Footer />
</div>
);
Expand Down
19 changes: 19 additions & 0 deletions src/renderer/components/Body.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import ResizePanel from 'react-resize-panel';
import SideBar from './SideBar';
import Main from './Main';

export default function Body() {
return (
<div className="flex flex-auto">
<ResizePanel
direction="e"
handleClass="hidden"
style={{ width: '200px' }}
>
<SideBar />
</ResizePanel>
<Main />
</div>
);
}
110 changes: 65 additions & 45 deletions src/renderer/components/EditArea.jsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,72 @@
import 'ace-builds/src-noconflict/ace';
import 'ace-builds/src-noconflict/mode-c_cpp';
import './theme-xenon';
import React, { useCallback } from 'react';
import React from 'react';
import AceEditor from 'react-ace';
import { useDispatch, useSelector } from 'react-redux';
import { setSelectedText, setText } from '../reducks/edit/actions';
import { getActiveText } from '../reducks/edit/selectors';
import { getNewText } from '../reducks/file/selectors';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import {
setSelectedText,
setText,
setEditorId,
setActiveEditorId,
} from '../reducks/editor/actions';

let // エディタインスタンス
editorInstance = null;
const EditArea = React.memo(
(props) => {
let editorInstance = null;
/* XenonTextで開いている際, 他のエディタなどでファイルが変更されると
initialTextが変更され反映される.
しかしeditorIdも新しくなるためゾンビがのこるかもしれない.
確認が必要 */
const { initialText } = props;

export default function EditArea() {
const activeText = getActiveText(useSelector((state) => state));
const initialText = getNewText(useSelector((state) => state));
const dispatch = useDispatch();
const onChange = useCallback(() => {
dispatch(setText(editorInstance));
});
const onLoad = useCallback((newEditorInstance) => {
editorInstance = newEditorInstance;
dispatch(setText(editorInstance));
});
const onSelectionChange = useCallback(() => {
dispatch(setSelectedText(editorInstance));
});
const dispatch = useDispatch();
const onChange = () => {
dispatch(setText(editorInstance));
};
/* editorInstance作成後 */
const onLoad = (newEditorInstance) => {
editorInstance = newEditorInstance;
/* ストアにeditorIdを登録(初期状態は'') */
dispatch(setEditorId(editorInstance.id));
/* ストアにActiveEditorIdを登録 */
dispatch(setActiveEditorId(editorInstance.id));
/* ストアにTextを登録 */
dispatch(setText(editorInstance));
};
const onSelectionChange = () => {
dispatch(setSelectedText(editorInstance));
};

return (
<div className="bg-gray-900 flex-auto">
<AceEditor
defaultValue={initialText}
editorProps={{ $blockScrolling: 'true' }}
focus={false}
fontSize="16px"
height="100%"
highlightActiveLine={false}
mode="c_cpp"
name="UNIQUE_ID_OF_DIV"
onChange={onChange}
onLoad={onLoad}
onSelectionChange={onSelectionChange}
showPrintMargin={false}
tabSize={4}
theme="xenon"
value={activeText}
width="100%"
wrapEnabed={false}
/>
</div>
);
}
return (
<div className="bg-gray-900 flex-auto">
<AceEditor
defaultValue={initialText}
editorProps={{ $blockScrolling: 'true' }}
fontSize="16px"
height="100%"
highlightActiveLine={false}
mode="c_cpp"
name="UNIQUE_ID_OF_DIV"
onChange={onChange}
onLoad={onLoad}
onSelectionChange={onSelectionChange}
showPrintMargin={false}
tabSize={4}
theme="xenon"
width="100%"
wrapEnabed={false}
/>
</div>
);
},
// Props.initialTextが変更されない限り, 再レンダリングしない
(prevProps, nextProps) => prevProps.initialText === nextProps.initialText,
);

EditArea.propTypes = {
initialText: PropTypes.string.isRequired,
};

export default EditArea;
4 changes: 2 additions & 2 deletions src/renderer/components/FileBlock.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import FileStatus from './FileStatus';

export default function FileBlock() {
const item = 'untitled';

return (
<div className="flex h-6 w-full bg-gray-800 items-center">
<FileStatus />
{/* 現在未実装 */}
{/* <FileStatus /> */}
<h3
className="
text-xs text-gray-300
Expand Down
30 changes: 25 additions & 5 deletions src/renderer/components/FileStatus.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React from 'react';
import { useSelector } from 'react-redux';
import { getFileStatus } from '../reducks/file/selectors';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import { getFileStatus } from '../reducks/editor/selectors';
import { deleteDocument } from '../reducks/editor/actions';

const savedImagePathCommand =
'M4.293 4.293a1 1 0 011.414 0L10 ' +
Expand All @@ -13,17 +17,33 @@ const unsavedImagePathCommand =
'1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 ' +
'10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z';

export default function FileStatus() {
export default function FileStatus(props) {
const { editorId } = props;
let fileStatusPathCommand = savedImagePathCommand;
const isSaved = getFileStatus(useSelector((state) => state));
const dispatch = useDispatch();
/* FileStatusが押された時
-> タブとドキュメントを削除 */
const onClick = (e) => {
e.stopPropagation();
dispatch(deleteDocument(editorId));
};

const isSaved = getFileStatus(
useSelector((state) => state.editor),
editorId,
);
if (!isSaved) {
fileStatusPathCommand = unsavedImagePathCommand;
}
return (
<div className="w-3 h-3 mx-2 flex-shrink-0">
<div className="w-3 h-3 mx-2 flex-shrink-0" onClick={onClick}>
<svg className="text-gray-300" fill="currentColor" viewBox="0 0 20 20">
<path clipRule="evenodd" d={fileStatusPathCommand} fillRule="evenodd" />
</svg>
</div>
);
}

FileStatus.propTypes = {
editorId: PropTypes.string.isRequired,
};
2 changes: 1 addition & 1 deletion src/renderer/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { getCharCount } from '../reducks/edit/selectors';
import { getCharCount } from '../reducks/editor/selectors';

export default function Footer() {
const charCount = getCharCount(useSelector((state) => state));
Expand Down
58 changes: 43 additions & 15 deletions src/renderer/components/Main.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
import React from 'react';
import ResizePanel from 'react-resize-panel';
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import EditArea from './EditArea';
import SideBar from './SideBar';
import Tabs from './Tabs';

import { getActiveEditorId, getDocuments } from '../reducks/editor/selectors';
import { setNewDocument } from '../reducks/editor/actions';

export default function Main() {
// state.editor変更時, なぜ2回呼び出されるのかは不明
const dispatch = useDispatch();

const editorSelector = useSelector((state) => state.editor);
const activeEditorId = getActiveEditorId(editorSelector);
const documents = getDocuments(editorSelector);

/* これでできたけど根本的な理由は不明, あとで調べる */
useEffect(() => {
if (documents.length === 0) {
dispatch(setNewDocument());
}
});

const shouldShow = (id) => {
if (id !== activeEditorId) {
return false;
}
return true;
};

return (
<div className="flex flex-auto">
<ResizePanel
direction="e"
handleClass="hidden"
style={{ width: '200px' }}
>
<SideBar />
</ResizePanel>
<div className="flex flex-col w-full">
<Tabs />
<EditArea />
</div>
<div className="flex flex-auto flex-col">
<Tabs documents={documents} />
{/* documentsに含まれているdocumentを全てレンダリング */}
{documents.map((document, index) => {
return (
<div
className={`flex flex-auto ${
/* アクティブなdocumentのみを表示 */
shouldShow(document.editorId) ? '' : 'hidden'
}`}
// eslint-disable-next-line react/no-array-index-key
key={index}
>
<EditArea initialText={document.fileText} />
</div>
);
})}
</div>
);
}
41 changes: 36 additions & 5 deletions src/renderer/components/Tab.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import FileStatus from './FileStatus';
import { setActiveEditorId } from '../reducks/editor/actions';

export default function Tabs() {
const item = 'Untitled';
const activeTabColor = 'bg-gray-900';
const inactiveTabColor = 'bg-gray-800';

export default function Tab(props) {
const { title } = props;
const { editorId } = props;
const { isActive } = props;
const dispatch = useDispatch();

// タブ(FileStatus以外の場所)が押された時
const onClick = () => {
/* activeEditorIdをストアにセット */
dispatch(setActiveEditorId(editorId));
};

let tabColor = inactiveTabColor;
if (isActive) {
tabColor = activeTabColor;
}

return (
<div className="bg-gray-900 h-8 w-40 flex flex-row items-center">
<div
className={`"h-8 w-40 flex flex-row items-center " ${tabColor}`}
onClick={onClick}
>
<h2
className="
text-xs text-gray-300 select-none
my-1 ml-2 leading-6
w-full h-6
"
>
{item}
{title}
</h2>
<FileStatus />
<FileStatus editorId={editorId} />
</div>
);
}

Tab.propTypes = {
editorId: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
isActive: PropTypes.bool.isRequired,
};
Loading