Display profile panel (#1178)

* Expose Display.displayGenerator

* Update search when assigning options context

* Don't clear selection unless the popup changes

* Merge search styles

* Create panel for changing the profile
This commit is contained in:
toasted-nutbread 2020-12-29 23:38:44 -05:00 committed by GitHub
parent ae36cccc36
commit ad90bad057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 228 additions and 14 deletions

View File

@ -54,6 +54,14 @@
</div>
</div>
<div class="content-footer-container1"><div class="content-footer-container2"><div class="content-footer" id="content-footer"></div><div class="scrollbar-spacer scrollbar"></div></div></div>
<div class="overlay-panel-container">
<div class="overlay-panel scrollbar" id="profile-panel" hidden>
<div class="overlay-panel-inner">
<h3>Default Profile</h3>
<div class="profile-list" id="profile-list"></div>
</div>
</div>
</div>
</div>
<div class="content-sidebar scrollbar" id="content-sidebar" hidden>
<div class="content-sidebar-inner">
@ -65,7 +73,7 @@
<button class="sidebar-button" disabled id="navigate-next-button" title="Next definition (Alt + F)"><span class="sidebar-button-icon" data-icon="right-chevron"></span></button>
</div>
<div class="content-sidebar-bottom">
<!--<button class="sidebar-button" id="profile-button"><span class="sidebar-button-icon" data-icon="profile"></span></button>-->
<button class="sidebar-button" id="profile-button"><span class="sidebar-button-icon" data-icon="profile"></span></button>
</div>
</div>
</div>
@ -96,10 +104,12 @@
<script src="/mixed/js/display-generator.js"></script>
<script src="/mixed/js/display-history.js"></script>
<script src="/mixed/js/display-notification.js"></script>
<script src="/mixed/js/display-profile-selection.js"></script>
<script src="/mixed/js/document-focus-controller.js"></script>
<script src="/mixed/js/dynamic-loader.js"></script>
<script src="/mixed/js/frame-endpoint.js"></script>
<script src="/mixed/js/media-loader.js"></script>
<script src="/mixed/js/panel-element.js"></script>
<script src="/mixed/js/scroll.js"></script>
<script src="/mixed/js/text-scanner.js"></script>
<script src="/mixed/js/html-template-collection.js"></script>

View File

@ -17,6 +17,7 @@
/* global
* Display
* DisplayProfileSelection
* DocumentFocusController
* JapaneseUtil
* api
@ -33,6 +34,8 @@
const japaneseUtil = new JapaneseUtil(null);
const display = new Display('popup', japaneseUtil, documentFocusController);
await display.prepare();
const displayProfileSelection = new DisplayProfileSelection(display);
displayProfileSelection.prepare();
display.initializeState();
yomichan.ready();

View File

@ -349,6 +349,8 @@ class Frontend {
const {usePopupWindow, showIframePopupsInRootFrame} = this._options.general;
const isIframe = !this._useProxyPopup && (window !== window.parent);
const currentPopup = this._popup;
let popupPromise;
if (usePopupWindow) {
popupPromise = this._popupCache.get('window');
@ -393,7 +395,10 @@ class Frontend {
}
if (this._updatePopupToken !== token) { return; }
this._clearSelection(true);
if (popup !== currentPopup) {
this._clearSelection(true);
}
this._popupEventListeners.removeAllEventListeners();
this._popup = popup;
if (popup !== null) {

View File

@ -38,6 +38,7 @@
--action-button-padding: 0.3em;
--list-margin: 0.72em;
--main-content-size: 100%;
--main-content-vertical-padding: 0em;
--main-content-horizontal-padding: 0em;
--entry-horizontal-padding: 0.72em;
@ -77,6 +78,7 @@
--entry-current-indicator-triangle-size: calc(1em * (var(--entry-current-indicator-triangle-size-no-units) / var(--font-size-no-units)));
--animation-duration: 0.125s;
--animation-duration2: calc(2 * var(--animation-duration));
/* Colors */
--background-color: #ffffff;
@ -239,6 +241,12 @@ h2 {
margin: 0.25em 0 0;
border-bottom: calc(1em / (var(--font-size-no-units) * var(--h2-font-size-no-units))) solid var(--light-border-color);
}
h3 {
font-size: 1em;
font-weight: bold;
margin: 0.25em 0 0.375em;
padding: 0;
}
a {
color: var(--link-color);
text-decoration: underline;
@ -323,7 +331,10 @@ a {
position: relative;
}
.content-body-inner {
width: 100%;
width: var(--main-content-size);
max-width: 100%;
box-sizing: border-box;
margin: 0 auto;
padding: var(--main-content-vertical-padding) var(--main-content-horizontal-padding);
}
.content-footer-container1 {
@ -344,8 +355,8 @@ a {
flex: 1 1 auto;
}
.content-footer {
max-width: var(--main-content-size);
width: 100%;
width: var(--main-content-size);
max-width: 100%;
}
@ -365,7 +376,7 @@ a {
overflow-x: hidden;
overflow-y: auto;
background-color: var(--sidebar-background-color);
z-index: 1;
z-index: 10;
position: relative;
display: block;
}
@ -502,6 +513,9 @@ button.sidebar-button.danger:hover .sidebar-button-icon,
button.sidebar-button.danger:focus .sidebar-button-icon {
background-color: var(--sidebar-button-danger-icon-color);
}
button.sidebar-button.sidebar-button-highlight>.sidebar-button-icon {
background-color: var(--accent-color);
}
.sidebar-button-icon[data-icon=cross] {
mask-image: url(/mixed/img/cross.svg);
-webkit-mask-image: url(/mixed/img/cross.svg);
@ -1459,6 +1473,78 @@ button.action-button[data-icon=source-term]::before {
}
/* Overlays */
.overlay-panel-container {
pointer-events: none;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 6;
}
.overlay-panel {
pointer-events: auto;
background-color: var(--background-color);
display: block;
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: var(--main-content-size);
max-width: 100%;
box-sizing: border-box;
margin: 0 auto;
padding: var(--main-content-vertical-padding) var(--main-content-horizontal-padding);
overflow-y: scroll;
transform: none;
opacity: 1;
visibility: visible;
transition:
opacity var(--animation-duration2) ease-out,
visibility 0s linear,
transform var(--animation-duration2) ease-out;
}
.overlay-panel[hidden] {
transform: translate(4em, 0);
opacity: 0;
visibility: hidden;
transition:
opacity var(--animation-duration2) ease-in,
visibility 0s linear var(--animation-duration2),
transform var(--animation-duration2) ease-in;
}
.overlay-panel[hidden]:not(.hidden-animating) {
display: none;
}
.overlay-panel-inner {
padding: var(--entry-vertical-padding) var(--entry-horizontal-padding);
}
/* Profile panel */
.profile-list {
display: flex;
flex-flow: column nowrap;
align-items: stretch;
}
.profile-list-item {
display: flex;
flex-flow: row nowrap;
align-items: center;
cursor: pointer;
}
.profile-list-item-selection {
flex: 0 0 auto;
text-align: center;
padding: 0.25em 0.5em 0.25em 0;
}
.profile-list-item-name {
flex: 1 1 auto;
padding: 0.25em 0;
}
/* Conditional styles */
:root:not([data-enable-search-tags=true]) .tag[data-category=search] {
display: none;

View File

@ -57,14 +57,6 @@ h1 {
border-bottom: calc(1em / (var(--font-size-no-units) * 2)) solid var(--separator-color1);
}
/* Content layout */
.content-body-inner {
width: var(--main-content-size);
max-width: 100%;
box-sizing: border-box;
margin: 0 auto;
}
/* Search bar */
.search-textbox-container {
display: flex;

View File

@ -129,5 +129,9 @@
<div class="footer-notification-body"></div>
<button class="footer-notification-close-button"><span class="footer-notification-close-button-icon"></span></button>
</div></template>
<template id="profile-list-item-template"><label class="profile-list-item">
<div class="profile-list-item-selection"><label class="radio"><input type="radio" class="profile-entry-is-default-radio" name="profile-entry-default-radio"><span class="radio-body"><span class="radio-border"></span><span class="radio-dot"></span></span></label></div>
<div class="profile-list-item-name"></div>
</label></template>
</body></html>

View File

@ -121,6 +121,10 @@ class DisplayGenerator {
return this._templates.instantiate('footer-notification');
}
createProfileListItem() {
return this._templates.instantiate('profile-list-item');
}
// Private
_createTermExpression(details) {

View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2020 Yomichan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* global
* PanelElement
* api
*/
class DisplayProfileSelection {
constructor(display) {
this._display = display;
this._profielList = document.querySelector('#profile-list');
this._profileButton = document.querySelector('#profile-button');
this._profilePanel = new PanelElement({
node: document.querySelector('#profile-panel'),
closingAnimationDuration: 375 // Milliseconds; includes buffer
});
this._profileListNeedsUpdate = false;
this._eventListeners = new EventListenerCollection();
this._source = generateId(16);
}
async prepare() {
yomichan.on('optionsUpdated', this._onOptionsUpdated.bind(this));
this._profileButton.addEventListener('click', this._onProfileButtonClick.bind(this), false);
this._profileListNeedsUpdate = true;
}
// Private
_onOptionsUpdated({source}) {
if (source === this._source) { return; }
this._profileListNeedsUpdate = true;
if (this._profilePanel.isVisible()) {
this._updateProfileList();
}
}
_onProfileButtonClick(e) {
e.preventDefault();
e.stopPropagation();
this._setProfilePanelVisible(!this._profilePanel.isVisible());
}
_setProfilePanelVisible(visible) {
this._profilePanel.setVisible(visible);
this._profileButton.classList.toggle('sidebar-button-highlight', visible);
if (visible && this._profileListNeedsUpdate) {
this._updateProfileList();
}
}
async _updateProfileList() {
this._profileListNeedsUpdate = false;
const options = await api.optionsGetFull();
this._eventListeners.removeAllEventListeners();
const displayGenerator = this._display.displayGenerator;
const {profileCurrent, profiles} = options;
const fragment = document.createDocumentFragment();
for (let i = 0, ii = profiles.length; i < ii; ++i) {
const {name} = profiles[i];
const entry = displayGenerator.createProfileListItem();
const radio = entry.querySelector('.profile-entry-is-default-radio');
radio.checked = (i === profileCurrent);
const nameNode = entry.querySelector('.profile-list-item-name');
nameNode.textContent = name;
fragment.appendChild(entry);
this._eventListeners.addEventListener(radio, 'change', this._onProfileRadioChange.bind(this, i), false);
}
this._profielList.textContent = '';
this._profielList.appendChild(fragment);
}
_onProfileRadioChange(index, e) {
if (e.currentTarget.checked) {
this._setProfileCurrent(index);
}
}
async _setProfileCurrent(index) {
await api.modifySettings([{
action: 'set',
path: 'profileCurrent',
value: index,
scope: 'global'
}], this._source);
this._setProfilePanelVisible(false);
}
}

View File

@ -170,6 +170,10 @@ class Display extends EventDispatcher {
]);
}
get displayGenerator() {
return this._displayGenerator;
}
get autoPlayAudioDelay() {
return this._autoPlayAudioDelay;
}
@ -523,6 +527,7 @@ class Display extends EventDispatcher {
_onMessageSetOptionsContext({optionsContext}) {
this.setOptionsContext(optionsContext);
this.searchLast();
}
_onMessageSetContent({details}) {