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:
parent
ae36cccc36
commit
ad90bad057
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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; }
|
||||
|
||||
if (popup !== currentPopup) {
|
||||
this._clearSelection(true);
|
||||
}
|
||||
|
||||
this._popupEventListeners.removeAllEventListeners();
|
||||
this._popup = popup;
|
||||
if (popup !== null) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -121,6 +121,10 @@ class DisplayGenerator {
|
||||
return this._templates.instantiate('footer-notification');
|
||||
}
|
||||
|
||||
createProfileListItem() {
|
||||
return this._templates.instantiate('profile-list-item');
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_createTermExpression(details) {
|
||||
|
105
ext/mixed/js/display-profile-selection.js
Normal file
105
ext/mixed/js/display-profile-selection.js
Normal 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);
|
||||
}
|
||||
}
|
@ -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}) {
|
||||
|
Loading…
Reference in New Issue
Block a user