summaryrefslogtreecommitdiffstats
path: root/src/features/todos/components/TodosWebview.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/todos/components/TodosWebview.tsx')
-rw-r--r--src/features/todos/components/TodosWebview.tsx219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/features/todos/components/TodosWebview.tsx b/src/features/todos/components/TodosWebview.tsx
new file mode 100644
index 000000000..3385ff74c
--- /dev/null
+++ b/src/features/todos/components/TodosWebview.tsx
@@ -0,0 +1,219 @@
1import { Component, createRef, ReactElement, MouseEvent } from 'react';
2import { observer } from 'mobx-react';
3import withStyles, { WithStylesProps } from 'react-jss';
4import Webview from 'react-electron-web-view';
5import classnames from 'classnames';
6import { TODOS_PARTITION_ID } from '../../../config';
7import { TodoClientMessage } from '../actions';
8
9const styles = theme => ({
10 root: {
11 background: theme.colorBackground,
12 position: 'relative',
13 borderLeft: [1, 'solid', theme.todos.todosLayer.borderLeftColor],
14 zIndex: 300,
15
16 transform: ({ isVisible, width, isTodosServiceActive }) =>
17 `translateX(${isVisible || isTodosServiceActive ? 0 : width}px)`,
18
19 '& webview': {
20 height: '100%',
21 },
22 },
23 resizeHandler: {
24 position: 'absolute',
25 left: 0,
26 marginLeft: -5,
27 width: 10,
28 zIndex: 400,
29 cursor: 'col-resize',
30 },
31 dragIndicator: {
32 position: 'absolute',
33 left: 0,
34 width: 5,
35 zIndex: 400,
36 background: theme.todos.dragIndicator.background,
37 },
38 isTodosServiceActive: {
39 width: 'calc(100% - 368px)',
40 position: 'absolute',
41 right: 0,
42 zIndex: 0,
43 borderLeftWidth: 0,
44 },
45 hidden: {
46 borderLeftWidth: 0,
47 },
48});
49
50interface IProps extends WithStylesProps<typeof styles> {
51 isTodosServiceActive: boolean;
52 isVisible: boolean;
53 handleClientMessage: (channel: string, message: TodoClientMessage) => void;
54 setTodosWebview: (webView: Webview) => void;
55 resize: (newWidth: number) => void;
56 width: number;
57 minWidth: number;
58 userAgent: string;
59 todoUrl: string;
60 isTodoUrlValid: boolean;
61}
62
63interface IState {
64 isDragging: boolean;
65 width: number;
66 initialPos: number;
67 delta: number;
68}
69
70@observer
71class TodosWebview extends Component<IProps, IState> {
72 private node = createRef<HTMLDivElement>();
73
74 private webview: Webview;
75
76 constructor(props: IProps) {
77 super(props);
78
79 this.state = {
80 isDragging: false,
81 width: 300,
82 initialPos: 0,
83 delta: 0,
84 };
85 this.resizePanel = this.resizePanel.bind(this);
86 this.stopResize = this.stopResize.bind(this);
87 }
88
89 componentDidMount() {
90 this.setState({
91 width: this.props.width,
92 });
93
94 if (this.node.current) {
95 this.node.current.addEventListener('mousemove', this.resizePanel);
96 this.node.current.addEventListener('mouseup', this.stopResize);
97 this.node.current.addEventListener('mouseleave', this.stopResize);
98 }
99 }
100
101 startResize(e: MouseEvent<HTMLDivElement>): void {
102 this.setState({
103 isDragging: true,
104 initialPos: e.clientX,
105 delta: 0,
106 });
107 }
108
109 resizePanel(e: MouseEventInit): void {
110 const { minWidth } = this.props;
111 const { isDragging, initialPos } = this.state;
112
113 if (isDragging && Math.abs(e.clientX! - window.innerWidth) > minWidth) {
114 const delta = e.clientX! - initialPos;
115
116 this.setState({
117 delta,
118 });
119 }
120 }
121
122 stopResize(): void {
123 const { resize, minWidth } = this.props;
124 const { isDragging, delta, width } = this.state;
125
126 if (isDragging) {
127 let newWidth = width + (delta < 0 ? Math.abs(delta) : -Math.abs(delta));
128
129 if (newWidth < minWidth) {
130 newWidth = minWidth;
131 }
132
133 this.setState({
134 isDragging: false,
135 delta: 0,
136 width: newWidth,
137 });
138
139 resize(newWidth);
140 }
141 }
142
143 startListeningToIpcMessages() {
144 if (!this.webview) {
145 return;
146 }
147
148 const { handleClientMessage } = this.props;
149 this.webview.addEventListener('ipc-message', e => {
150 handleClientMessage(e.channel, e.args[0]);
151 });
152 }
153
154 render(): ReactElement {
155 const {
156 classes,
157 isTodosServiceActive,
158 isVisible,
159 userAgent,
160 todoUrl,
161 isTodoUrlValid,
162 } = this.props;
163
164 const { width, delta, isDragging } = this.state;
165 let displayedWidth = isVisible ? width : 0;
166 if (isTodosServiceActive) {
167 displayedWidth = 0;
168 }
169
170 return (
171 <div
172 className={classnames({
173 [classes.root]: true,
174 [classes.isTodosServiceActive]: isTodosServiceActive,
175 'todos__todos-panel--expanded': isTodosServiceActive,
176 [classes.hidden]: !isVisible,
177 })}
178 style={{ width: displayedWidth }}
179 onMouseUp={() => this.stopResize()}
180 ref={this.node}
181 id="todos-panel"
182 >
183 <div
184 className={classes.resizeHandler}
185 style={{
186 left: delta,
187 ...(isDragging ? { width: 600, marginLeft: -200 } : {}),
188 }} // This hack is required as resizing with webviews beneath behaves quite bad
189 onMouseDown={this.startResize}
190 />
191 {isDragging && (
192 <div
193 className={classes.dragIndicator}
194 style={{ left: delta }} // This hack is required as resizing with webviews beneath behaves quite bad
195 />
196 )}
197 {isTodoUrlValid && (
198 <Webview
199 // className={classes.webview} // TODO - [TS DEBT] style not found
200 onDidAttach={() => {
201 const { setTodosWebview } = this.props;
202 setTodosWebview(this.webview);
203 this.startListeningToIpcMessages();
204 }}
205 partition={TODOS_PARTITION_ID}
206 preload="./features/todos/preload.js"
207 ref={webview => {
208 this.webview = webview ? webview.view : null;
209 }}
210 useragent={userAgent}
211 src={todoUrl}
212 />
213 )}
214 </div>
215 );
216 }
217}
218
219export default withStyles(styles, { injectTheme: true })(TodosWebview);