Add CTRL+F shortcut to work as browser search

* Add new finder for each webview contents
This commit is contained in:
sudheer 2018-07-06 11:44:19 +05:30
parent e0943cc7fd
commit e95c8cc3c5
6 changed files with 191 additions and 4 deletions

View file

@ -8,5 +8,5 @@
}],
"react"
],
"plugins": ["transform-object-rest-spread"]
"plugins": ["transform-object-rest-spread", "transform-class-properties"]
}

View file

@ -11,6 +11,7 @@ import {Grid, Row} from 'react-bootstrap';
import {ipcRenderer, remote} from 'electron';
import Finder from '../../main/finder';
import Utils from '../../utils/util.js';
import LoginModal from './LoginModal.jsx';
@ -45,6 +46,7 @@ const MainPage = createReactClass({
}
}
}
return {
key,
unreadCounts: new Array(this.props.teams.length),
@ -141,10 +143,19 @@ const MainPage = createReactClass({
}
}
});
const webview = document.getElementById('mattermostView' + this.state.key);
this.finder = new Finder(webview);
ipcRenderer.on('toggle-find', () => {
this.finder.toggle();
});
},
componentDidUpdate(prevProps, prevState) {
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
this.finder.destroy();
const webview = document.getElementById('mattermostView' + this.state.key);
this.finder = new Finder(webview);
}
},
handleSelect(key) {
@ -152,13 +163,13 @@ const MainPage = createReactClass({
this.setState({
key: newKey,
});
this.handleOnTeamFocused(newKey);
var webview = document.getElementById('mattermostView' + newKey);
ipcRenderer.send('update-title', {
title: webview.getTitle(),
});
this.handleOnTeamFocused(newKey);
},
handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned) {
var unreadCounts = this.state.unreadCounts;
var mentionCounts = this.state.mentionCounts;

View file

@ -11,3 +11,64 @@
div[id*="-permissionDialog"] {
max-width: 350px;
}
.finder {
position: fixed;
top: 0;
right: 20px;
padding: 5px;
background: #eee;
border: 1px solid #d7d7d7;
border-top: none;
border-right: none;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
font-size: 0px;
}
.finder-input-wrapper {
display: inline-block;
position: relative;
}
.finder button {
border: 1px solid #d2d2d2;
border-radius: 3px;
background: white;
outline: none;
padding: 5px;
cursor: pointer;
font-size: 14px;
}
.finder button:hover {
background: #f0f0f0;
}
.finder-input {
border: 1px solid #d2d2d2;
border-radius: 3px;
padding: 5px;
outline: none;
font-size: 14px;
}
.finder-input:focus {
border-color: #35b5f4;
box-shadow: 0 0 1px #35b5f4;
}
.finder__hidden {
display: none;
}
.finder-progress__disabled {
display: none;
}
.finder-progress {
position: absolute;
font-size: 12px;
right: 10px;
top: 10px;
}

109
src/main/finder.js Normal file
View file

@ -0,0 +1,109 @@
// Copyright (c) 2015-2016 Yuya Ochiai
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export default class Finder {
constructor(target) {
this.target = target;
}
toggle() {
return this.opened ? this.close() : this.open();
}
open() {
if (!this.initialized) {
this.initialize();
}
this.opened = true;
this.$finder.classList.remove('finder__hidden');
this.$input.focus();
}
close = () => {
this.opened = false;
this.target.stopFindInPage('clearSelection');
this.$finder.classList.add('finder__hidden');
}
findNext = () => {
if (this.$input.value) {
this.target.findInPage(this.$input.value);
}
}
findPrev = () => {
if (this.$input.value) {
this.target.findInPage(this.$input.value, {forward: false});
}
}
find = (keyword) => {
this.target.stopFindInPage('clearSelection');
if (keyword) {
this.target.findInPage(keyword);
} else {
this.$progress.textContent = '0/0';
}
}
handleKeyEvent = (event) => {
if (event.code === 'Escape') {
this.close();
} else if (event.code === 'Enter') {
this.findNext();
} else {
this.find(event.target.value);
}
}
foundInPage = (event) => {
const {matches, activeMatchOrdinal} = event.result;
this.$progress.classList.remove('finder-progress__disabled');
this.$progress.textContent = `${activeMatchOrdinal}/${matches}`;
}
destroy() {
this.initialized = false;
if (this.$input) {
this.$input.removeEventListener('keyup', this.handleKeyEvent);
this.$prev.removeEventListener('click', this.findPrev);
this.$next.removeEventListener('click', this.findNext);
this.$close.removeEventListener('click', this.close);
this.target.removeEventListener('found-in-page', this.foundInPage);
const searchDiv = document.getElementById('searchDiv');
searchDiv.parentNode.removeChild(searchDiv);
}
}
initialize() {
this.initialized = true;
const wrapper = document.createElement('div');
wrapper.setAttribute('id', 'searchDiv');
wrapper.innerHTML = `
<div class="finder finder__hidden">
<div class="finder-input-wrapper">
<input class="finder-input" placeholder="" />
<span class="finder-progress finder-progress__disabled"></span>
</div>
<button class="finder-prev"></button>
<button class="finder-next"></button>
<button class="finder-close"></button>
</div>`;
document.body.appendChild(wrapper);
this.$finder = wrapper.querySelector('.finder');
this.$progress = this.$finder.querySelector('.finder-progress');
this.$input = this.$finder.querySelector('.finder-input');
this.$prev = this.$finder.querySelector('.finder-prev');
this.$next = this.$finder.querySelector('.finder-next');
this.$close = this.$finder.querySelector('.finder-close');
this.$input.addEventListener('keyup', this.handleKeyEvent);
this.$prev.addEventListener('click', this.findPrev);
this.$next.addEventListener('click', this.findNext);
this.$close.addEventListener('click', this.close);
this.target.addEventListener('found-in-page', this.foundInPage);
}
}

View file

@ -95,6 +95,12 @@ function createTemplate(mainWindow, config, isDev) {
template.push({
label: '&View',
submenu: [{
label: 'Find..',
accelerator: 'CmdOrCtrl+F',
click(item, focusedWindow) {
focusedWindow.webContents.send('toggle-find');
},
}, {
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click(item, focusedWindow) {

View file

@ -24,7 +24,7 @@ module.exports = merge(base, {
},
module: {
rules: [{
test: /\.jsx$/,
test: /\.(js|jsx)?$/,
use: {
loader: 'babel-loader',
},