Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/marius-wieschollek/passwords-webextension.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json32
-rw-r--r--package.json2
-rw-r--r--src/js/App/Background.js3
-rw-r--r--src/js/Controller/Theme/List.js7
-rw-r--r--src/js/Controller/Theme/Save.js16
-rw-r--r--src/js/Controller/Theme/Show.js13
-rw-r--r--src/js/Converter/ThemeConverter.js45
-rw-r--r--src/js/Definition/Theme.json12
-rw-r--r--src/js/Manager/BadgeManager.js20
-rw-r--r--src/js/Manager/ControllerManager.js14
-rw-r--r--src/js/Manager/ConverterManager.js11
-rw-r--r--src/js/Models/Theme/Theme.js183
-rw-r--r--src/js/Queue/Client/MiningClient.js10
-rw-r--r--src/js/Queue/FeedbackQueue.js5
-rw-r--r--src/js/Repositories/ThemeRepository.js141
-rw-r--r--src/js/Services/SettingsService.js5
-rw-r--r--src/js/Services/SystemService.js8
-rw-r--r--src/js/Services/ThemeService.js185
-rw-r--r--src/js/Services/ToastService.js30
-rw-r--r--src/js/Themes/Dark.json9
-rw-r--r--src/js/Themes/Light.json13
-rw-r--r--src/js/preview.js1
-rw-r--r--src/platform/generic/_locales/de/messages.json242
-rw-r--r--src/platform/generic/_locales/en/messages.json326
-rw-r--r--src/scss/_base.scss10
-rw-r--r--src/scss/_fonts.scss13
-rw-r--r--src/scss/_theme.scss7
-rw-r--r--src/scss/includes.scss1
-rw-r--r--src/scss/preview.scss4
-rw-r--r--src/vue/App/Options.vue5
-rw-r--r--src/vue/App/Popup.vue13
-rw-r--r--src/vue/Components/Collected/MinedProperty.vue17
-rw-r--r--src/vue/Components/Form/InputField.vue49
-rw-r--r--src/vue/Components/Form/SelectField.vue29
-rw-r--r--src/vue/Components/Options/Theming.vue79
-rw-r--r--src/vue/Components/Popup/Collected.vue18
-rw-r--r--src/vue/Components/Tabs.vue2
-rw-r--r--src/vue/Components/Theming/BadgeIcon.vue61
-rw-r--r--src/vue/Components/Theming/CustomColorAlpha.vue81
-rw-r--r--src/vue/Components/Theming/CustomColorInherit.vue87
-rw-r--r--src/vue/Components/Theming/CustomColorSet.vue31
-rw-r--r--src/vue/Components/Theming/CustomColorToast.vue77
-rw-r--r--src/vue/Components/Theming/CustomFontFamily.vue52
-rw-r--r--src/vue/Components/Theming/CustomFontSize.vue59
-rw-r--r--src/vue/Components/Theming/CustomTheme.vue117
-rw-r--r--src/vue/Components/Theming/PreviewTheme.vue80
-rw-r--r--src/vue/Components/Toast/Toast.vue123
-rw-r--r--webpack.config.js17
48 files changed, 1860 insertions, 505 deletions
diff --git a/package-lock.json b/package-lock.json
index 9df7c0b..dcee739 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6799,9 +6799,9 @@
}
},
"terser": {
- "version": "4.6.3",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz",
- "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==",
+ "version": "4.6.6",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
+ "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -6959,9 +6959,9 @@
}
},
"tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
"dev": true
},
"tty-browserify": {
@@ -7355,9 +7355,9 @@
"dev": true
},
"webpack": {
- "version": "4.41.6",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz",
- "integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==",
+ "version": "4.42.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz",
+ "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.8.5",
@@ -7397,6 +7397,12 @@
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true
+ },
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
@@ -7407,13 +7413,13 @@
}
},
"loader-utils": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
- "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
- "emojis-list": "^2.0.0",
+ "emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
diff --git a/package.json b/package.json
index 28bde59..a86ab81 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webextension-polyfill": "^0.6.0",
- "webpack": "^4.41.6",
+ "webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
},
"repository": {
diff --git a/src/js/App/Background.js b/src/js/App/Background.js
index 9a4c7ee..ae6c1d9 100644
--- a/src/js/App/Background.js
+++ b/src/js/App/Background.js
@@ -12,6 +12,8 @@ import BadgeManager from '@js/Manager/BadgeManager';
import ContextMenuManager from '@js/Manager/ContextMenuManager';
import MiningManager from '@js/Manager/MiningManager';
import NotificationService from '@js/Services/NotificationService';
+import ThemeService from '@js/Services/ThemeService';
+import ThemeRepository from '@js/Repositories/ThemeRepository';
class Background {
async init() {
@@ -27,6 +29,7 @@ class Background {
TabManager.init();
NotificationService.init();
RecommendationManager.init();
+ ThemeService.init(ThemeRepository);
BadgeManager.init();
ContextMenuManager.init();
MiningManager.init();
diff --git a/src/js/Controller/Theme/List.js b/src/js/Controller/Theme/List.js
index 4273d00..1a42419 100644
--- a/src/js/Controller/Theme/List.js
+++ b/src/js/Controller/Theme/List.js
@@ -4,11 +4,8 @@ import ThemeRepository from '@js/Repositories/ThemeRepository';
export default class List extends AbstractController {
async execute(message, reply) {
- let themes = await ThemeRepository.findAll(),
- list = {};
+ let themes = await ThemeRepository.findAll();
- for(let theme of themes) list[theme.getId()] = theme.getLabel();
-
- reply.setType('theme.info').setPayload(list);
+ reply.setType('theme.items').setPayload(themes);
}
} \ No newline at end of file
diff --git a/src/js/Controller/Theme/Save.js b/src/js/Controller/Theme/Save.js
new file mode 100644
index 0000000..2e809a4
--- /dev/null
+++ b/src/js/Controller/Theme/Save.js
@@ -0,0 +1,16 @@
+import AbstractController from '@js/Controller/AbstractController';
+import ThemeRepository from '@js/Repositories/ThemeRepository';
+import ErrorManager from '@js/Manager/ErrorManager';
+
+export default class Save extends AbstractController {
+
+ async execute(message, reply) {
+ try {
+ await ThemeRepository.update(message.getPayload());
+ reply.setPayload({success: true});
+ } catch(e) {
+ ErrorManager.logError(e);
+ reply.setType('error').setPayload({success: false, message: e.message});
+ }
+ }
+} \ No newline at end of file
diff --git a/src/js/Controller/Theme/Show.js b/src/js/Controller/Theme/Show.js
new file mode 100644
index 0000000..9cb7a62
--- /dev/null
+++ b/src/js/Controller/Theme/Show.js
@@ -0,0 +1,13 @@
+import AbstractController from '@js/Controller/AbstractController';
+import ThemeRepository from '@js/Repositories/ThemeRepository';
+
+export default class Show extends AbstractController {
+ async execute(message, reply) {
+ try {
+ let theme = await ThemeRepository.findById(message.getPayload());
+ reply.setType('theme.item').setPayload(theme);
+ } catch(e) {
+ reply.setType('error');
+ }
+ }
+} \ No newline at end of file
diff --git a/src/js/Converter/ThemeConverter.js b/src/js/Converter/ThemeConverter.js
new file mode 100644
index 0000000..77133a6
--- /dev/null
+++ b/src/js/Converter/ThemeConverter.js
@@ -0,0 +1,45 @@
+import Theme from '@js/Models/Theme/Theme';
+
+export default class ThemeConverter {
+ /**
+ *
+ * @param {Message} message
+ * @returns {Message}
+ */
+ convert(message) {
+ if(message.getType() === 'theme.items') {
+ return this._covertItems(message);
+ } else {
+ return this._covertItem(message);
+ }
+ }
+
+ /**
+ *
+ * @param {Message} message
+ * @returns {Message}
+ */
+ _covertItems(message) {
+ let payload = message.getPayload(),
+ servers = [];
+
+ if(payload !== null) {
+ for(let data of payload) {
+ servers.push(new Theme(data));
+ }
+ }
+
+ return message.setPayload(servers);
+ }
+
+ /**
+ *
+ * @param {Message} message
+ * @returns {Message}
+ */
+ _covertItem(message) {
+ let payload = message.getPayload();
+
+ return message.setPayload(new Theme(payload));
+ }
+} \ No newline at end of file
diff --git a/src/js/Definition/Theme.json b/src/js/Definition/Theme.json
index c160dc8..2b166fc 100644
--- a/src/js/Definition/Theme.json
+++ b/src/js/Definition/Theme.json
@@ -5,7 +5,19 @@
"label": {
"type": "string"
},
+ "font": {
+ "type": "object"
+ },
+ "variables": {
+ "type": "object"
+ },
+ "badge": {
+ "type": "object"
+ },
"colors": {
"type": "object"
+ },
+ "style": {
+ "type": "boolean"
}
} \ No newline at end of file
diff --git a/src/js/Manager/BadgeManager.js b/src/js/Manager/BadgeManager.js
index 793f636..0bf8c6d 100644
--- a/src/js/Manager/BadgeManager.js
+++ b/src/js/Manager/BadgeManager.js
@@ -4,6 +4,7 @@ import TabManager from '@js/Manager/TabManager';
import ServerManager from '@js/Manager/ServerManager';
import ErrorManager from '@js/Manager/ErrorManager';
import LocalisationService from '@js/Services/LocalisationService';
+import ThemeService from '@js/Services/ThemeService';
class BadgeManager {
@@ -64,11 +65,7 @@ class BadgeManager {
await this._api.browserAction.setBadgeText({text: '', tabId});
}
- if(SystemService.getBrowserPlatform() === 'firefox') {
- await this._api.browserAction.setBadgeTextColor({color: '#fff'});
- }
-
- await this._api.browserAction.setBadgeBackgroundColor({color: '#0082c9'});
+ await this._setBadgeTheme();
} catch(e) {
ErrorManager.logError(e);
}
@@ -96,6 +93,19 @@ class BadgeManager {
ErrorManager.logError(e);
}
}
+
+ async _setBadgeTheme() {
+ if(SystemService.getBrowserPlatform() === 'firefox') {
+ let color = await ThemeService.getBadgeTextColor();
+ await this._api.browserAction.setBadgeTextColor({color});
+ }
+
+ let color = await ThemeService.getBadgeBackgroundColor();
+ await this._api.browserAction.setBadgeBackgroundColor({color});
+
+ let icon = await ThemeService.getBadgeIcon();
+ await this._api.browserAction.setIcon({path:icon});
+ }
}
export default new BadgeManager(); \ No newline at end of file
diff --git a/src/js/Manager/ControllerManager.js b/src/js/Manager/ControllerManager.js
index 9bcf97c..20e4ec8 100644
--- a/src/js/Manager/ControllerManager.js
+++ b/src/js/Manager/ControllerManager.js
@@ -165,6 +165,20 @@ class ControllerManager {
await this._executeController(module, message, reply);
}
);
+ MessageService.listen(
+ 'theme.show',
+ async (message, reply) => {
+ let module = await import(/* webpackChunkName: "ThemeShow" */ '@js/Controller/Theme/Show');
+ await this._executeController(module, message, reply);
+ }
+ );
+ MessageService.listen(
+ 'theme.save',
+ async (message, reply) => {
+ let module = await import(/* webpackChunkName: "ThemeSave" */ '@js/Controller/Theme/Save');
+ await this._executeController(module, message, reply);
+ }
+ );
}
/**
diff --git a/src/js/Manager/ConverterManager.js b/src/js/Manager/ConverterManager.js
index 5ccd273..61df209 100644
--- a/src/js/Manager/ConverterManager.js
+++ b/src/js/Manager/ConverterManager.js
@@ -1,8 +1,9 @@
+import ErrorManager from '@js/Manager/ErrorManager';
import MessageService from '@js/Services/MessageService';
+import ThemeConverter from '@js/Converter/ThemeConverter';
+import FolderConverter from '@js/Converter/FolderConverter';
import ServerConverter from '@js/Converter/ServerConverter';
import PasswordConverter from '@js/Converter/PasswordConverter';
-import ErrorManager from '@js/Manager/ErrorManager';
-import FolderConverter from '@js/Converter/FolderConverter';
class ConverterManager {
@@ -25,6 +26,12 @@ class ConverterManager {
await this._executeConverter(FolderConverter, message);
}
);
+ MessageService.convert(
+ ['theme.items', 'theme.item', 'theme.save', 'theme.preview'],
+ async (message) => {
+ await this._executeConverter(ThemeConverter, message);
+ }
+ );
}
diff --git a/src/js/Models/Theme/Theme.js b/src/js/Models/Theme/Theme.js
index b69e10e..6567197 100644
--- a/src/js/Models/Theme/Theme.js
+++ b/src/js/Models/Theme/Theme.js
@@ -1,5 +1,5 @@
import AbstractModel from 'passwords-client/src/Model/AbstractModel';
-import Properties from '@js/Definition/Theme'
+import Properties from '@js/Definition/Theme';
export default class Theme extends AbstractModel {
@@ -11,44 +11,207 @@ export default class Theme extends AbstractModel {
* @return {Object}
*/
getId() {
- return this.getProperty('id')
+ return this.getProperty('id');
}
-
+
/**
* @param {String} value
* @return {this}
*/
setId(value) {
- return this.setProperty('id', value)
+ return this.setProperty('id', value);
}
/**
* @return {Object}
*/
getLabel() {
- return this.getProperty('label')
+ return this.getProperty('label');
}
-
+
/**
* @param {String} value
* @return {this}
*/
setLabel(value) {
- return this.setProperty('label', value)
+ return this.setProperty('label', value);
}
/**
* @return {Object}
*/
getColors() {
- return this.getProperty('colors')
+ return this.getProperty('colors');
}
-
+
/**
* @param {Object} value
* @return {this}
*/
setColors(value) {
- return this.setProperty('colors', value)
+ return this.setProperty('colors', value);
+ }
+
+ /**
+ * @return {Object}
+ */
+ getVariables() {
+ return this.getProperty('variables');
+ }
+
+ /**
+ * @param {Object} value
+ * @return {this}
+ */
+ setVariables(value) {
+ return this.setProperty('variables', value);
+ }
+
+ /**
+ * @return {Object}
+ */
+ getBadge() {
+ return this.getProperty('badge');
+ }
+
+ /**
+ * @param {Object} value
+ * @return {this}
+ */
+ setBadge(value) {
+ return this.setProperty('badge', value);
+ }
+
+ /**
+ * @return {String}
+ */
+ getBadgeIcon() {
+ return this._getSubProperty('badge', 'icon');
+ }
+
+ /**
+ * @param {String} value
+ * @return {this}
+ */
+ setBadgeIcon(value) {
+ return this._setSubProperty('badge', 'icon', value);
+ }
+
+ /**
+ * @return {String}
+ */
+ getBadgeBackgroundColor() {
+ return this._getSubProperty('badge', 'color-bg');
+ }
+
+ /**
+ * @param {String} value
+ * @return {this}
+ */
+ setBadgeBackgroundColor(value) {
+ return this._setSubProperty('badge', 'color-bg', value);
+ }
+
+ /**
+ * @return {String}
+ */
+ getBadgeForegroundColor() {
+ return this._getSubProperty('badge', 'color-fg');
+ }
+
+ /**
+ * @param {String} value
+ * @return {this}
+ */
+ setBadgeForegroundColor(value) {
+ return this._setSubProperty('badge', 'color-fg', value);
+ }
+
+ /**
+ * @return {Object}
+ */
+ getFont() {
+ return this.getProperty('font');
+ }
+
+ /**
+ * @param {Object} value
+ * @return {this}
+ */
+ setFont(value) {
+ return this.setProperty('font', value);
+ }
+
+ /**
+ * @return {String}
+ */
+ getFontFamily() {
+ return this._getSubProperty('font', 'family');
+ }
+
+ /**
+ * @param {String} value
+ * @return {this}
+ */
+ setFontFamily(value) {
+ return this._setSubProperty('font', 'family', value);
+ }
+
+ /**
+ * @return {String}
+ */
+ getFontSize() {
+ return this._getSubProperty('font', 'size');
+ }
+
+ /**
+ * @param {String} value
+ * @return {this}
+ */
+ setFontSize(value) {
+ return this._setSubProperty('font', 'size', value);
+ }
+
+ /**
+ * @return {Boolean}
+ */
+ getStyle() {
+ return this.getProperty('style');
+ }
+
+ /**
+ * @param {Boolean} value
+ * @return {this}
+ */
+ setStyle(value) {
+ return this.setProperty('style', value);
+ }
+
+ /**
+ * @param {String} property
+ * @param {String} key
+ * @return {(null|String)}
+ * @private
+ */
+ _getSubProperty(property, key) {
+ let data = this.getProperty(property);
+ if(data !== null && data !== undefined && data.hasOwnProperty(key)) return data[key];
+
+ return null;
+ }
+
+ /**
+ * @param {String} property
+ * @param {String} key
+ * @param {String} value
+ * @return {this}
+ * @private
+ */
+ _setSubProperty(property, key, value) {
+ let data = this.getProperty(property);
+ if(data === null || data === undefined) data = {};
+ data[key] = value;
+
+ return this.setProperty(property, data);
}
} \ No newline at end of file
diff --git a/src/js/Queue/Client/MiningClient.js b/src/js/Queue/Client/MiningClient.js
index 630bd23..66e6666 100644
--- a/src/js/Queue/Client/MiningClient.js
+++ b/src/js/Queue/Client/MiningClient.js
@@ -1,14 +1,21 @@
import FeedbackClient from '@js/Queue/Client/FeedbackClient';
import MiningItem from '@js/Models/Queue/MiningItem';
+import EventQueue from '@js/Event/EventQueue';
+import ErrorManager from '@js/Manager/ErrorManager';
class MiningClient extends FeedbackClient {
+ get update() {
+ return this._event;
+ }
+
constructor() {
let worker = (i) => { return this._worker(i); };
let feedbackWorker = (i) => { return this._feedbackWorker(i); };
super('mining', worker, feedbackWorker, MiningItem);
this._items = {};
this._solvedItems = {};
+ this._event = new EventQueue();
}
/**
@@ -54,6 +61,9 @@ class MiningClient extends FeedbackClient {
resolve,
reject
};
+
+ this._event.emit(item)
+ .catch(ErrorManager.catch);
});
}
diff --git a/src/js/Queue/FeedbackQueue.js b/src/js/Queue/FeedbackQueue.js
index 2a89852..98250e7 100644
--- a/src/js/Queue/FeedbackQueue.js
+++ b/src/js/Queue/FeedbackQueue.js
@@ -1,14 +1,13 @@
import Queue from '@js/Queue/Queue';
-import MessageService from '@js/Services/MessageService';
import FeedbackItem from '@js/Models/Queue/FeedbackItem';
export default class FeedbackQueue extends Queue {
/**
*
- * @param {String} name
+ * @param {String} name
* @param {(String|null)} [area=null]
- * @param {QueueItem} [type=FeedbackItem]
+ * @param {QueueItem} [type=FeedbackItem]
*/
constructor(name, area, type = FeedbackItem) {
super(name, area, type);
diff --git a/src/js/Repositories/ThemeRepository.js b/src/js/Repositories/ThemeRepository.js
index 2c4cd6f..3446ed5 100644
--- a/src/js/Repositories/ThemeRepository.js
+++ b/src/js/Repositories/ThemeRepository.js
@@ -1,12 +1,58 @@
import Theme from '@js/Models/Theme/Theme';
import DarkTheme from '@js/Themes/Dark';
import LightTheme from '@js/Themes/Light';
-import LocalisationService from '@js/Services/LocalisationService';
+import RGB from '@js/Themes/RGB';
+import Hacker from '@js/Themes/Hacker';
+import ArcDark from '@js/Themes/ArcDark';
+import ArcLight from '@js/Themes/ArcLight';
+import OledDarkTheme from '@js/Themes/OledDark';
+import AdaptaTealTheme from '@js/Themes/AdaptaTeal';
+import AdaptaLightTheme from '@js/Themes/AdaptaLight';
+import SettingsService from '@js/Services/SettingsService';
+import StorageService from '@js/Services/StorageService';
+import uuid from 'uuidv4';
+import BooleanState from 'passwords-client/src/State/BooleanState';
class ThemeRepository {
+ get STORAGE_KEY() {
+ return 'themes';
+ }
+
constructor() {
this._themes = null;
+ this._customThemes = null;
+ this._loading = new BooleanState(false);
+ }
+
+ /**
+ *
+ * @param {Theme} theme
+ */
+ async create(theme) {
+ if(theme.getId() === null || theme.getId() === undefined) {
+ theme.setId(uuid());
+ }
+
+ await this._saveCustomTheme(theme);
+ if(this._themes !== null) {
+ this._themes[theme.getId()] = theme;
+ }
+ }
+
+ /**
+ *
+ * @param {Theme} theme
+ */
+ async update(theme) {
+ if(theme.getId() === null || theme.getId() === undefined) {
+ return await this.create(theme);
+ }
+
+ await this._saveCustomTheme(theme);
+ if(this._themes !== null) {
+ this._themes[theme.getId()] = theme;
+ }
}
/**
@@ -44,29 +90,102 @@ class ThemeRepository {
* @private
*/
async _listThemes() {
+ if(this._loading.get()) await this._loading.awaitFalse();
if(this._themes !== null) return this._themes;
+ this._loading.set(true);
- this._themes = {};
- let systemThemes = [LightTheme, DarkTheme];
+ let themes = {};
+ let systemThemes = [LightTheme, DarkTheme, OledDarkTheme, AdaptaLightTheme, AdaptaTealTheme, ArcLight, ArcDark, Hacker, RGB];
for(let data of systemThemes) {
- let theme = this._makeSystemTheme(data);
+ let theme = new Theme(data);
- this._themes[theme.getId()] = theme;
+ themes[theme.getId()] = theme;
+ }
+
+ let customThemes = await this._loadCustomThemes();
+ if(customThemes.length === 0) {
+ themes.custom = await this._makeCustomTheme();
+ } else {
+ for(let theme of customThemes) {
+ themes[theme.getId()] = theme;
+ }
}
- return this._themes;
+ this._themes = themes;
+ this._loading.set(false);
+ return themes;
+ }
+
+ async _makeCustomTheme() {
+ let theme = await SettingsService.getValue('theme.custom');
+
+ if(theme === null) {
+ theme = LightTheme;
+ theme.label = 'ThemeCustom';
+ }
+
+ theme.id = 'custom';
+
+ return new Theme(theme);
+ }
+
+ /**
+ *
+ * @returns {Promise<Theme[]>}
+ * @private
+ */
+ async _loadCustomThemes() {
+ if(this._customThemes !== null) {
+ return this._customThemes;
+ }
+
+ let themes = [];
+ if(await StorageService.has(this.STORAGE_KEY)) {
+ let data = await StorageService.get(this.STORAGE_KEY);
+
+ for(let element of data) {
+ themes.push(new Theme(element));
+ }
+ }
+
+ this._customThemes = themes;
+ return themes;
+ }
+
+ /**
+ *
+ * @param theme
+ * @returns {Promise<void>}
+ * @private
+ */
+ async _saveCustomTheme(theme) {
+ let themes = await this._loadCustomThemes();
+
+ for(let i = 0; i < themes.length; i++) {
+ if(themes[i].getId() === theme.getId()) {
+ themes[i] = theme;
+ await this._saveCustomThemeList(themes);
+ return;
+ }
+ }
+
+ themes.push(theme);
+ await this._saveCustomThemeList(themes);
}
/**
*
- * @param {Object} data
- * @return {Theme}
+ * @param {Theme[]} themes
+ * @return {Promise<void>}
* @private
*/
- _makeSystemTheme(data) {
- data.label = LocalisationService.translate(data.label);
+ async _saveCustomThemeList(themes) {
+ let objects = [];
- return new Theme(data);
+ for(let theme of themes) {
+ objects.push(theme.getProperties());
+ }
+ await StorageService.set(this.STORAGE_KEY, objects);
}
}
diff --git a/src/js/Services/SettingsService.js b/src/js/Services/SettingsService.js
index 09880cc..717e0f6 100644
--- a/src/js/Services/SettingsService.js
+++ b/src/js/Services/SettingsService.js
@@ -31,9 +31,14 @@ class SettingsService {
'client.ext.theme.current',
'local.theme.current',
'sync.theme.current'
+ ],
+ 'theme.custom' : [
+ 'sync.theme.custom',
+ 'local.theme.custom'
]
};
this._defaults = {
+ 'theme.custom' : null,
'theme.current' : 'light',
'server.default' : null,
'password.autosubmit' : true,
diff --git a/src/js/Services/SystemService.js b/src/js/Services/SystemService.js
index ee8a836..32aac1f 100644
--- a/src/js/Services/SystemService.js
+++ b/src/js/Services/SystemService.js
@@ -94,6 +94,14 @@ class SystemService {
}
/**
+ * @param {String} path
+ * @return {Promise<String>}
+ */
+ async getFileUrl(path) {
+ return await this.getBrowserApi().runtime.getURL(path);
+ }
+
+ /**
* @returns {Boolean}
*/
hasContextMenu() {
diff --git a/src/js/Services/ThemeService.js b/src/js/Services/ThemeService.js
index 37770df..105230f 100644
--- a/src/js/Services/ThemeService.js
+++ b/src/js/Services/ThemeService.js
@@ -1,17 +1,72 @@
import SettingsService from '@js/Services/SettingsService';
-import ThemeRepository from '@js/Repositories/ThemeRepository';
+import MessageService from '@js/Services/MessageService';
+import SystemService from '@js/Services/SystemService';
import ErrorManager from '@js/Manager/ErrorManager';
class ThemeService {
+ get FONT_MAPPING() {
+ return {
+ default : '-apple-system, BlinkMacSystemFont, Ubuntu, Calibri, "Helvetica Neue", sans-serif',
+ mono : 'FreeMono, "Courier New", monospace',
+ sans : 'Ubuntu, Calibri, "Helvetica Neue", sans-serif',
+ serif : '"Times New Roman", Numbus, serif',
+ light : '"Comfortaa Light","Lato Light","Corbel Light","Gill Sans Light", sans-serif',
+ nextcloud: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
+ dyslexic : 'OpenDyslexic, Dyslexie, sans-serif'
+ };
+ }
+
+ constructor() {
+ /** @type {(ThemeRepository|null)} **/
+ this._repository = null;
+ this._style = null;
+ }
+
+ /**
+ *
+ * @param {ThemeRepository} repository
+ */
+ init(repository) {
+ this._repository = repository;
+ }
+
+
+ async getBadgeIcon() {
+ let theme = await this.getCurrentTheme(),
+ icon = theme.getBadgeIcon();
+
+ return await SystemService.getBrowserApi().runtime.getURL(`img/${icon}.svg`);
+ }
+
+ async getBadgeTextColor() {
+ let theme = await this.getCurrentTheme();
+
+ return theme.getBadgeForegroundColor();
+ }
+
+ async getBadgeBackgroundColor() {
+ let theme = await this.getCurrentTheme();
+
+ return theme.getBadgeBackgroundColor();
+ }
+
async apply() {
- let theme = await this.getCurrentTheme(),
- colors = theme.getColors();
+ let theme = await this.getCurrentTheme();
+ this.applyTheme(theme);
+ }
- for(let color in colors) {
- if(!colors.hasOwnProperty(color)) continue;
- document.documentElement.style.setProperty(`--${color}-color`, colors[color]);
- }
+ /**
+ *
+ * @param {Theme} theme
+ */
+ applyTheme(theme) {
+ this._createStyleSheet(
+ theme,
+ this._applyFont(theme.getFont()),
+ this._applyColors(theme.getColors()),
+ this._applyVariables(theme.getVariables())
+ );
}
/**
@@ -21,12 +76,118 @@ class ThemeService {
async getCurrentTheme() {
let current = await SettingsService.getValue('theme.current');
- try {
- return await ThemeRepository.findById(current);
- } catch(e) {
- ErrorManager.logError(e);
- return await ThemeRepository.findById('light');
+ if(this._repository !== null) {
+ try {
+ return await this._repository.findById(current);
+ } catch(e) {
+ ErrorManager.logError(e);
+ return await this._repository.findById('light');
+ }
+ }
+
+ let reply = await MessageService.send({type: 'theme.show', payload: current});
+ if(reply.getType() === 'theme.item') return reply.getPayload();
+
+ reply = await MessageService.send({type: 'theme.show', payload: 'light'});
+ return reply.getPayload();
+ }
+
+ /**
+ *
+ * @param {Object} colors
+ * @return {{}}
+ * @private
+ */
+ _applyColors(colors) {
+ if(!colors) return {};
+
+ let css = {};
+ for(let color in colors) {
+ if(!colors.hasOwnProperty(color)) continue;
+ css[`--${color}-color`] = colors[color];
+ }
+
+ for(let toast of ['info', 'success', 'warning', 'error']) {
+ let color = `${toast}-fg`;
+ if(colors.hasOwnProperty(color)) {
+ css[`--${toast}-hv-color`] = `${colors[color]}40`;
+ }
+ }
+
+ return css;
+ }
+
+ /**
+ *
+ * @param {Object} font
+ * @return {{}}
+ * @private
+ */
+ _applyFont(font) {
+ if(!font) return {};
+
+ let css = {};
+ if(font.hasOwnProperty('family') && font.family) {
+ let mapping = this.FONT_MAPPING;
+
+ if(mapping.hasOwnProperty(font.family)) {
+ css['--font-family'] = mapping[font.family];
+ } else {
+ css['--font-family'] = font.family;
+ }
}
+
+ if(font.hasOwnProperty('size') && font.size) css['--font-size'] = font.size;
+
+ return css;
+ }
+
+ /**
+ *
+ * @param {Object} variables
+ * @return {{}}
+ * @private
+ */
+ _applyVariables(variables) {
+ if(!variables) return {};
+
+ let css = {};
+ for(let variable in variables) {
+ if(!variables.hasOwnProperty(variable)) continue;
+ css[`--${variable}`] = variables[variable];
+ }
+
+ return css;
+ }
+
+ /**
+ *
+ * @param {Theme} theme
+ * @param {Object} variables
+ * @private
+ */
+ _createStyleSheet(theme, ...variables) {
+ let css = '';
+ variables = Object.assign(...variables);
+ for(let variable in variables) {
+ if(!variables.hasOwnProperty(variable)) continue;
+
+ let value = variables[variable].replace(';', ''),
+ key = variable.replace(';', '');
+
+ css += `${key}: ${value};`;
+ }
+
+ css = `:root { ${css} }`;
+ if(theme.getStyle()) css = `@import url("/css/themes/${theme.getId()}.css");\n${css}`;
+
+ if(this._style === null) {
+ this._style = document.createElement('style');
+ this._style.setAttribute('type', 'text/css');
+ document.body.appendChild(this._style);
+ }
+
+ this._style.innerHTML = css;
}
}
diff --git a/src/js/Services/ToastService.js b/src/js/Services/ToastService.js
index bb2bacc..088722b 100644
--- a/src/js/Services/ToastService.js
+++ b/src/js/Services/ToastService.js
@@ -40,15 +40,26 @@ class ToastService {
}
}
+ /**
+ *
+ * @param {(String|String[])} message The text of the toast
+ * @param {(String|String[])} [title=null] The title of the toast
+ * @param {Number} [ttl=3] Time before the toast is closed
+ * @return {Promise<String>}
+ */
+ success(message, title = null, ttl = 3) {
+ return this.create({type: 'success', title, message, closeable: true, ttl});
+ }
/**
*
* @param {(String|String[])} message The text of the toast
* @param {(String|String[])} [title=null] The title of the toast
+ * @param {Number} [ttl=10] Time before the toast is closed
* @return {Promise<String>}
*/
- warning(message, title = null) {
- return this.create({type: 'warning', title, message, closeable: true, ttl: 10});
+ warning(message, title = null, ttl = 10) {
+ return this.create({type: 'warning', title, message, closeable: true, ttl});
}
@@ -56,10 +67,11 @@ class ToastService {
*
* @param {(String|String[])} message The text of the toast
* @param {(String|String[])} [title=null] The title of the toast
+ * @param {Number} [ttl=10] Time before the toast is closed
* @return {Promise<String>}
*/
- error(message, title = null) {
- return this.create({type: 'error', title, message, closeable: true, ttl: 10});
+ error(message, title = null, ttl = 10) {
+ return this.create({type: 'error', title, message, closeable: true, ttl});
}
/**
@@ -80,16 +92,6 @@ class ToastService {
return this.create(config);
}
- /**
- *
- * @param {(String|String[])} message The text of the toast
- * @param {(String|String[])} [title=null] The title of the toast
- * @return {Promise<String>}
- */
- success(message, title = null) {
- return this.create({type: 'success', title, message, closeable: true, ttl: 3});
- }
-
/**
*
diff --git a/src/js/Themes/Dark.json b/src/js/Themes/Dark.json
index 0de7c67..5dc7752 100644
--- a/src/js/Themes/Dark.json
+++ b/src/js/Themes/Dark.json
@@ -1,6 +1,15 @@
{
"id": "dark",
"label": "ThemeDark",
+ "badge" : {
+ "color-bg": "#0c63db",
+ "color-fg": "#d8d8d8",
+ "icon" : "passwords-light"
+ },
+ "font" : {
+ "family": "default",
+ "size" : "11pt"
+ },
"colors": {
"element-bg" : "#181818",
"element-fg" : "#d8d8d8",
diff --git a/src/js/Themes/Light.json b/src/js/Themes/Light.json
index bd3269f..cd4a39a 100644
--- a/src/js/Themes/Light.json
+++ b/src/js/Themes/Light.json
@@ -1,6 +1,15 @@
{
"id" : "light",
"label" : "ThemeLight",
+ "badge" : {
+ "color-bg": "#0652dd",
+ "color-fg": "#ffffff",
+ "icon" : "passwords-dark"
+ },
+ "font" : {
+ "family": "default",
+ "size" : "11pt"
+ },
"colors": {
"element-bg" : "#ffffff",
"element-fg" : "#000000",
@@ -10,8 +19,8 @@
"element-active-fg" : "#0996f8",
"element-active-hover-bg": "#ededed",
"element-active-hover-fg": "#0670cc",
- "button-bg" : "#00000000",
- "button-fg" : "#00000000",
+ "button-bg" : "inherit",
+ "button-fg" : "inherit",
"button-hover-bg" : "#0996f8",
"button-hover-fg" : "#ffffff",
"info-bg" : "#0652dd",
diff --git a/src/js/preview.js b/src/js/preview.js
index 6297ff6..3bead7b 100644
--- a/src/js/preview.js
+++ b/src/js/preview.js
@@ -1,6 +1,5 @@
import Preview from '@js/App/Preview';
import '@js/Prototype/prototype';
-import '@scss/preview.scss'
// noinspection JSUnresolvedVariable
__webpack_public_path__ = `/`;
diff --git a/src/platform/generic/_locales/de/messages.json b/src/platform/generic/_locales/de/messages.json
index a2fcfc4..6d175d5 100644
--- a/src/platform/generic/_locales/de/messages.json
+++ b/src/platform/generic/_locales/de/messages.json
@@ -489,7 +489,27 @@
}
}
},
- "SettingsCurrentTheme" : {
+ "DemoInfoNotification" : {
+ "message" : "Infomeldung",
+ "description": ""
+ },
+ "DemoSuccessNotification" : {
+ "message" : "Erfolgsmeldung",
+ "description": ""
+ },
+ "DemoWarningNotification" : {
+ "message" : "Warnmeldung",
+ "description": ""
+ },
+ "DemoErrorNotification" : {
+ "message" : "Fehlermeldung",
+ "description": ""
+ },
+ "DemoText" : {
+ "message" : "Demo",
+ "description": ""
+ },
+ "SettingsThemeId" : {
"message" : "Aktives Thema",
"description": ""
},
@@ -497,64 +517,236 @@
"message" : "Dunkles Thema",
"description": ""
},
+ "ThemeOledDark" : {
+ "message" : "Dunkles OLED Thema",
+ "description": ""
+ },
"ThemeLight" : {
- "message" : "Helles Thema",
+ "message" : "Standard",
"description": ""
},
- "DemoInfoNotification" : {
- "message" : "Info Meldung",
+ "ThemeAdaptaLight" : {
+ "message" : "Adapta",
"description": ""
},
- "DemoSuccessNotification" : {
- "message" : "Erfolgsmeldung",
+ "ThemeAdaptaTeal" : {
+ "message" : "Adapta teal",
"description": ""
},
- "DemoWarningNotification" : {
- "message" : "Warnmeldung",
+ "ThemeArcDark" : {
+ "message" : "Arc dark",
"description": ""
},
- "DemoErrorNotification" : {
- "message" : "Fehlermeldung",
+ "ThemeArcLight" : {
+ "message" : "Arc",
"description": ""
},
- "DemoDataInsert" : {
- "message" : "Demo-Daten einfügen",
+ "ThemeHacker" : {
+ "message" : "Hacker",
"description": ""
},
- "DemoText" : {
- "message" : "Demo",
+ "ThemeRGB" : {
+ "message" : "Gaming RGB",
"description": ""
},
- "SettingsCustomTheme" : {
+ "ThemeCustom" : {
"message" : "Eigenes Thema",
"description": ""
},
+ "CustomFont" : {
+ "message" : "Schriftart",
+ "description": ""
+ },
"CustomDefaultElement" : {
- "message" : "Normales Element",
+ "message" : "Normale Elemente",
"description": ""
},
"CustomActiveElement" : {
- "message" : "Aktives Element",
+ "message" : "Aktive Elemente",
"description": ""
},
"CustomButtons" : {
"message" : "Schaltflächen",
"description": ""
},
- "CustomColorsBackground" : {
- "message" : "Farbe Hintergrund",
+ "CustomToasts" : {
+ "message" : "Mini-Benachrichtigungen",
+ "description": ""
+ },
+ "CustomBadge" : {
+ "message" : "Browser-Symbol",
+ "description": ""
+ },
+ "BackgroundColorLabel" : {
+ "message" : "Hintergrundfarbe",
+ "description": ""
+ },
+ "ForegroundColorLabel" : {
+ "message" : "Vordergrundfarbe",
+ "description": ""
+ },
+ "BackgroundInheritLabel" : {
+ "message" : "Hintergrund erben",
+ "description": ""
+ },
+ "ForegroundInheritLabel" : {
+ "message" : "Vordergrund erben",
+ "description": ""
+ },
+ "BackgroundColorBaseTitle" : {
+ "message" : "Diese Farbe wird im Normalfall für den Hintergrund verwendet",
+ "description": ""
+ },
+ "BackgroundColorHoverTitle" : {
+ "message" : "Diese Farbe wird für den Hintergrund verwendet wenn das Element mit der Maus aktiviert wird. Diese Farbe kann auch für Umrandungen verwendet werden wenn das Element nicht aktiviert ist.",
+ "description": ""
+ },
+ "ForegroundColorBaseTitle" : {
+ "message" : "Diese Farbe wird im Normalfall für den Vordergrund verwendet",
+ "description": ""
+ },
+ "ForegroundColorHoverTitle" : {
+ "message" : "Diese Farbe wird für den Vordergrund und Umrandungen verwendet wenn das Element mit der Maus überfahren wird",
+ "description": ""
+ },
+ "BackgroundInheritTitle": {
+ "message" : "Falls aktiviert wird die Farbe des übergeordneten Elements übernommen",
+ "description": ""
+ },
+ "ForegroundInheritTitle": {
+ "message" : "Falls aktiviert wird die Farbe des übergeordneten Elements übernommen",
+ "description": ""
+ },
+ "SettingsCustomFont" : {
+ "message" : "Schriftart",
+ "description": ""
+ },
+ "FontDefault" : {
+ "message" : "Standard",
+ "description": ""
+ },
+ "FontMono" : {
+ "message" : "Monospace-Schrift",
+ "description": ""
+ },
+ "FontSerif" : {
+ "message" : "Serifenschrift",
+ "description": ""
+ },
+ "FontSans" : {
+ "message" : "Serifenlose Schrift",
+ "description": ""
+ },
+ "FontLight" : {
+ "message" : "Dünne Schrift",
+ "description": ""
+ },
+ "FontNextcloud" : {
+ "message" : "Nextcloud",
+ "description": ""
+ },
+ "FontOpenDyslexic" : {
+ "message" : "Open Dyslexic",
+ "description": ""
+ },
+ "FontCustom" : {
+ "message" : "Eigene Schrift",
+ "description": ""
+ },
+ "SettingsCustomFontSize" : {
+ "message" : "Schriftgröße",
+ "description": ""
+ },
+ "FontSizeVerySmall" : {
+ "message" : "Sehr klein",
+ "description": ""
+ },
+ "FontSizeSmall" : {
+ "message" : "Klein",
+ "description": ""
+ },
+ "FontSizeDefault" : {
+ "message" : "Standard",
+ "description": ""
+ },
+ "FontSizeMedium" : {
+ "message" : "Medium",
+ "description": ""
+ },
+ "FontSizeLarge" : {
+ "message" : "Groß",
+ "description": ""
+ },
+ "FontSizeVeryLarge" : {
+ "message" : "Sehr groß",
+ "description": ""
+ },
+ "ToastInfoColors" : {
+ "message" : "Infomeldung",
+ "description": ""
+ },
+ "ToastSuccessColors" : {
+ "message" : "Erfolgsmeldung",
+ "description": ""
+ },
+ "ToastWarningColors" : {
+ "message" : "Warnmeldung",
+ "description": ""
+ },
+ "ToastErrorColors" : {
+ "message" : "Fehlermeldung",
+ "description": ""
+ },
+ "ToastBackgroundTitle" : {
+ "message" : "Hintergrundfarbe für die Mini-Benachrichtigung",
+ "description": ""
+ },
+ "ToastForegroundTitle" : {
+ "message" : "Farbe für Texte und Schaltflächen",
+ "description": ""
+ },
+ "SettingsBadgeIcon" : {
+ "message" : "Symbolleisten-Symbol",
+ "description": ""
+ },
+ "BadgeIconAuto" : {
+ "message" : "Automatisch",
+ "description": ""
+ },
+ "BadgeIconLight" : {
+ "message" : "Helles Symbol",
+ "description": ""
+ },
+ "BadgeIconMedium" : {
+ "message" : "Graues Symbol",
+ "description": ""
+ },
+ "BadgeIconDark" : {
+ "message" : "Dunkles Symbol",
+ "description": ""
+ },
+ "BadgeIconNewLight" : {
+ "message" : "Helles modernes Symbol",
+ "description": ""
+ },
+ "BadgeIconNewMedium" : {
+ "message" : "Graues modernes Symbol",
+ "description": ""
+ },
+ "BadgeIconNewDark" : {
+ "message" : "Dunkles modernes Symbol",
"description": ""
},
- "CustomColorsForeground" : {
- "message" : "Farbe Vordergrund",
+ "CustomBadgeColors" : {
+ "message" : "Abzeichenfarbe",
"description": ""
},
- "CustomAlphaBackground" : {
- "message" : "Transparenz Hintergrund",
+ "BadgeBackgroundTitle" : {
+ "message" : "Hintergrundfarbe des Abzeichens",
"description": ""
},
- "CustomAlphaForeground" : {
- "message" : "Transparenz Vordergrund",
+ "BadgeForegroundTitle" : {
+ "message" : "Textfarbe des Abzeichens",
"description": ""
}
} \ No newline at end of file
diff --git a/src/platform/generic/_locales/en/messages.json b/src/platform/generic/_locales/en/messages.json
index 5491456..29ad693 100644
--- a/src/platform/generic/_locales/en/messages.json
+++ b/src/platform/generic/_locales/en/messages.json
@@ -503,7 +503,27 @@
}
}
},
- "SettingsCurrentTheme" : {
+ "DemoInfoNotification" : {
+ "message" : "Info notification",
+ "description": ""
+ },
+ "DemoSuccessNotification" : {
+ "message" : "Success notification",
+ "description": ""
+ },
+ "DemoWarningNotification" : {
+ "message" : "Warning notification",
+ "description": ""
+ },
+ "DemoErrorNotification" : {
+ "message" : "Error notification",
+ "description": ""
+ },
+ "DemoText" : {
+ "message" : "Demo",
+ "description": ""
+ },
+ "SettingsThemeId" : {
"message" : "Active theme",
"description": ""
},
@@ -511,38 +531,46 @@
"message" : "Dark theme",
"description": ""
},
+ "ThemeOledDark" : {
+ "message" : "Dark OLED",
+ "description": ""
+ },
"ThemeLight" : {
- "message" : "Light theme",
+ "message" : "Default",
"description": ""
},
- "DemoInfoNotification" : {
- "message" : "Info notification",
+ "ThemeAdaptaLight" : {
+ "message" : "Adapta",
"description": ""
},
- "DemoSuccessNotification" : {
- "message" : "Success notification",
+ "ThemeAdaptaTeal" : {
+ "message" : "Adapta teal",
"description": ""
},
- "DemoWarningNotification" : {
- "message" : "Warning notification",
+ "ThemeArcDark" : {
+ "message" : "Arc dark",
"description": ""
},
- "DemoErrorNotification" : {
- "message" : "Error notification",
+ "ThemeArcLight" : {
+ "message" : "Arc",
"description": ""
},
- "DemoDataInsert" : {
- "message" : "Insert demo data",
+ "ThemeHacker" : {
+ "message" : "Hacker",
"description": ""
},
- "DemoText" : {
- "message" : "Demo",
+ "ThemeRGB" : {
+ "message" : "Gaming RGB",
"description": ""
},
- "SettingsCustomTheme" : {
+ "ThemeCustom" : {
"message" : "Custom theme",
"description": ""
},
+ "CustomFont" : {
+ "message" : "Font",
+ "description": ""
+ },
"CustomDefaultElement" : {
"message" : "Default element",
"description": ""
@@ -555,156 +583,184 @@
"message" : "Buttons",
"description": ""
},
- "CustomColorsBackground" : {
+ "CustomToasts" : {
+ "message" : "Toast notifications",
+ "description": ""
+ },
+ "CustomBadge" : {
+ "message" : "Browser icon",
+ "description": ""
+ },
+ "BackgroundColorLabel" : {
"message" : "Background color",
"description": ""
},
- "CustomColorsForeground" : {
+ "ForegroundColorLabel" : {
"message" : "Foreground color",
"description": ""
},
- "CustomAlphaBackground" : {
- "message" : "Background transparent",
+ "BackgroundInheritLabel" : {
+ "message" : "Inherit background",
"description": ""
},
- "CustomAlphaForeground" : {
- "message" : "Foreground transparent",
+ "ForegroundInheritLabel" : {
+ "message" : "Inherit foreground",
"description": ""
},
-
-
-
-
-
-
-
-
-
-
- "PasswordRequestFailedTitle" : {
- "message" : "Nextcloud password request failed",
- "description": "Notification title when the password request fails"
+ "BackgroundColorBaseTitle" : {
+ "message" : "This color is usually used for the background",
+ "description": ""
},
- "PasswordRequestFailedText" : {
- "message" : "The password list could not be retrieved.\nError: $ERROR$",
- "description" : "Notification text when the password request fails",
- "placeholders": {
- "error": {
- "content": "$1",
- "example": "Unknown Error"
- }
- }
+ "BackgroundColorHoverTitle" : {
+ "message" : "This color is used for the background if the element is active. It can also be used for the border of an inactive element",
+ "description": ""
},
- "PasswordEncodingFailedTitle" : {
- "message" : "Could not parse password properties",
- "description": "Notification title when the password properties can not be parsed"
+ "ForegroundColorBaseTitle" : {
+ "message" : "This color is usually used for texts and borders",
+ "description": ""
},
- "PasswordEncodingFailedText" : {
- "message" : "The properties of password #$ID$ do not contain valid JSON data and could not be processed",
- "description" : "Notification text when the password properties can not be parsed",
- "placeholders": {
- "id": {
- "content": "$1",
- "example": "0"
- }
- }
+ "ForegroundColorHoverTitle" : {
+ "message" : "This color is used for texts and borders if the element is active",
+ "description": ""
},
- "PasswordCreatedTitle" : {
- "message" : "Password saved",
- "description": "Notification title when a password was saved"
+ "BackgroundInheritTitle": {
+ "message" : "If activated, the value will be inherited from the parent element",
+ "description": ""
},
- "PasswordCreatedText" : {
- "message" : "The password was stored sucessfully",
- "description": "Notification text when a password was saved"
+ "ForegroundInheritTitle": {
+ "message" : "If activated, the value will be inherited from the parent element",
+ "description": ""
},
- "CreatePasswordFailedTitle" : {
- "message" : "Save password failed",
- "description": "Notification title when saving a password failed"
+ "SettingsCustomFont" : {
+ "message" : "Font family",
+ "description": ""
},
- "CreatePasswordFailedText" : {
- "message" : "The password could not be saved",
- "description": "Notification text when saving a password failed"
+ "FontDefault" : {
+ "message" : "Default",
+ "description": ""
},
- "UpdatedLoginDetectedTitle" : {
- "message" : "Updated login found",
- "description": "Notification title when an updated login is detected"
+ "FontMono" : {
+ "message" : "Monospace",
+ "description": ""
},
- "UpdatedLoginDetectedText" : {
- "message" : "Click here to update the password for \"$USER$\"",
- "description" : "Notification text when an updated login is detected",
- "placeholders": {
- "user": {
- "content": "$1",
- "example": "username"
- }
- }
+ "FontSerif" : {
+ "message" : "Serif",
+ "description": ""
},
- "PasswordUpdatedTitle" : {
- "message" : "Password updated",
- "description": "Notification title when a password was updated"
+ "FontSans" : {
+ "message" : "Sans serif",
+ "description": ""
},
- "PasswordUpdatedText" : {
- "message" : "The password was updated sucessfully",
- "description": "Notification text when a password was updated"
+ "FontLight" : {
+ "message" : "Light",
+ "description": ""
},
- "UpdatePasswordFailedTitle" : {
- "message" : "Update password failed",
- "description": "Notification title when updating a password failed"
+ "FontNextcloud" : {
+ "message" : "Nextcloud",
+ "description": ""
},
- "UpdatePasswordFailedText" : {
- "message" : "The password could not be updated",
- "description": "Notification text when updating a password failed"
+ "FontOpenDyslexic" : {
+ "message" : "Open Dyslexic",
+ "description": ""
},
- "ApiLogin200" : {
- "message" : "URL invalid or API disabled",
- "description": "Error text when an api request could not be decoded"
+ "FontCustom" : {
+ "message" : "Custom",
+ "description": ""
},
- "ApiLogin401" : {
- "message" : "Invalid user credentials",
- "description": "Error text when an api request got a 401 Unauthorized response"
+ "SettingsCustomFontSize" : {
+ "message" : "Font size",
+ "description": ""
},
- "ApiLogin403" : {
- "message" : "Access denied",
- "description": "Error text when an api request got a 403 Forbidden response"
+ "FontSizeVerySmall" : {
+ "message" : "Very small",
+ "description": ""
},
- "ApiLogin404" : {
- "message" : "URL is invalid",
- "description": "Error text when an api request got a 404 Not Found response"
+ "FontSizeSmall" : {
+ "message" : "Small",
+ "description": ""
},
- "ApiLogin500" : {
- "message" : "Server crashed during request",
- "description": "Error text when an api request got a 50x Internal Server Error response"
+ "FontSizeDefault" : {
+ "message" : "Default",
+ "description": ""
},
- "ApiLoginGeneric" : {
- "message" : "Http Error $STATUS$: $MESSAGE$",
- "description" : "Generic error message for failed requests",
- "placeholders": {
- "status" : {
- "content": "$1",
- "example": "HTTP status code"
- },
- "message": {
- "content": "$2",
- "example": "HTTP status text"
- }
- }
+ "FontSizeMedium" : {
+ "message" : "Medium",
+ "description": ""
},
- "SettingsInvalidUrl" : {
- "message" : "The url $URL$ seems to be invalid.\n\n If the url of the app is\nhttps://example.com/index.php/apps/passwords\nyou need to enter\nhttps://example.com\n\nIf the url of the app is\nhttps://example.com/nextcloud/index.php/apps/passwords\nyou need to enter\nhttps://example.com/nextcloud",
- "description" : "Generic error message for failed requests",
- "placeholders": {
- "url": {
- "content": "$1",
- "example": "The user input"
- }
- }
+ "FontSizeLarge" : {
+ "message" : "Large",
+ "description": ""
+ },
+ "FontSizeVeryLarge" : {
+ "message" : "Very large",
+ "description": ""
+ },
+ "ToastInfoColors" : {
+ "message" : "Info toast",
+ "description": ""
+ },
+ "ToastSuccessColors" : {
+ "message" : "Success toast",
+ "description": ""
+ },
+ "ToastWarningColors" : {
+ "message" : "Warning toast",
+ "description": ""
+ },
+ "ToastErrorColors" : {
+ "message" : "Error toast",
+ "description": ""
+ },
+ "ToastBackgroundTitle" : {
+ "message" : "Background color for popup messages",
+ "description": ""
+ },
+ "ToastForegroundTitle" : {
+ "message" : "Color for text and button backgrounds",
+ "description": ""
},
- "DarkMode" : {
- "message" : "Dark Mode",
- "description": "Lable of the Dark Mode setting"
+ "SettingsBadgeIcon" : {
+ "message" : "Toolbar icon",
+ "description": ""
+ },
+ "BadgeIconAuto" : {
+ "message" : "Automatic",
+ "description": ""
+ },
+ "BadgeIconLight" : {
+ "message" : "Light icon",
+ "description": ""
+ },
+ "BadgeIconMedium" : {
+ "message" : "Grey icon",
+ "description": ""
},
- "Url" : { "message": "Url" },
- "Password" : { "message": "Password" },
- "User" : { "message": "User" },
- "Save" : { "message": "Save" }
+ "BadgeIconDark" : {
+ "message" : "Dark icon",
+ "description": ""
+ },
+ "BadgeIconNewLight" : {
+ "message" : "Light modern icon",
+ "description": ""
+ },
+ "BadgeIconNewMedium" : {
+ "message" : "Grey modern icon",
+ "description": ""
+ },
+ "BadgeIconNewDark" : {
+ "message" : "Dark modern icon",
+ "description": ""
+ },
+ "CustomBadgeColors" : {
+ "message" : "Badge colors",
+ "description": ""
+ },
+ "BadgeBackgroundTitle" : {
+ "message" : "Background color of the badge",
+ "description": ""
+ },
+ "BadgeForegroundTitle" : {
+ "message" : "Text color of the badge",
+ "description": ""
+ }
} \ No newline at end of file
diff --git a/src/scss/_base.scss b/src/scss/_base.scss
index 1f625a8..cd15ae8 100644
--- a/src/scss/_base.scss
+++ b/src/scss/_base.scss
@@ -1,4 +1,11 @@
+:root {
+ font-family : var(--font-family);
+ font-size : var(--font-size);
+}
+
body {
+ font-family : var(--font-family);
+ font-size : var(--font-size);
background-color : var(--element-bg-color);
color : var(--element-fg-color);
margin : 0;
@@ -10,6 +17,7 @@ body {
input,
select,
button, {
- font-size : 1rem;
+ font-family : var(--font-family);
+ font-size : 1rem;
}
} \ No newline at end of file
diff --git a/src/scss/_fonts.scss b/src/scss/_fonts.scss
new file mode 100644
index 0000000..9a2365c
--- /dev/null
+++ b/src/scss/_fonts.scss
@@ -0,0 +1,13 @@
+@font-face {
+ font-family : "OpenDyslexic";
+ src : url('../../fonts/OpenDyslexic/OpenDyslexic-Regular.woff');
+ font-weight : normal;
+ font-style : normal;
+}
+
+@font-face {
+ font-family : "OpenDyslexic";
+ src : url('../../fonts/OpenDyslexic/OpenDyslexic-Bold.woff');
+ font-weight : bold;
+ font-style : normal;
+} \ No newline at end of file
diff --git a/src/scss/_theme.scss b/src/scss/_theme.scss
index 4d00cdb..b837253 100644
--- a/src/scss/_theme.scss
+++ b/src/scss/_theme.scss
@@ -1,4 +1,7 @@
:root {
+ --font-family : initial;
+ --font-size : 11pt;
+
/**
* Generic content elements
*/
@@ -39,10 +42,14 @@
*/
--info-bg-color : #0652dd;
--info-fg-color : #fff;
+ --info-hv-color : #ffffff40;
--warning-bg-color : #ffc312;
--warning-fg-color : #fff;
+ --warning-hv-color : #ffffff40;
--error-bg-color : #ff3f34;
--error-fg-color : #fff;
+ --error-hv-color : #ffffff40;
--success-bg-color : #05c46b;
--success-fg-color : #fff;
+ --success-hv-color : #ffffff40;
} \ No newline at end of file
diff --git a/src/scss/includes.scss b/src/scss/includes.scss
index 8c7dde7..8ff7dde 100644
--- a/src/scss/includes.scss
+++ b/src/scss/includes.scss
@@ -1,2 +1,3 @@
@import "base";
+@import "fonts";
@import "theme"; \ No newline at end of file
diff --git a/src/scss/preview.scss b/src/scss/preview.scss
deleted file mode 100644
index 9b7c637..0000000
--- a/src/scss/preview.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-body {
- font-family : Ubuntu, Verdana, sans-serif;
- font-size : 11pt;
-} \ No newline at end of file
diff --git a/src/vue/App/Options.vue b/src/vue/App/Options.vue
index f85cb4b..fb69700 100644
--- a/src/vue/App/Options.vue
+++ b/src/vue/App/Options.vue
@@ -11,9 +11,9 @@
<script>
import Tabs from '@vue/Components/Tabs';
+ import Theming from '@vue/Components/Options/Theming';
import Accounts from '@vue/Components/Options/Accounts';
import Settings from '@vue/Components/Options/Settings';
- import Theming from '@vue/Components/Options/Theming';
export default {
el : '#app',
@@ -38,6 +38,9 @@
</script>
<style lang="scss">
+ @import "@scss/includes";
+ @import "@scssP/browser.scss";
+
body {
min-height : 600px;
diff --git a/src/vue/App/Popup.vue b/src/vue/App/Popup.vue
index ab4c8ee..39295be 100644
--- a/src/vue/App/Popup.vue
+++ b/src/vue/App/Popup.vue
@@ -1,10 +1,10 @@
<template>
<div id="manager">
- <tabs :tabs="tabs" :initial-tab="tab" v-if="authorized" v-on:switch="saveTab($event)" ref="tabs">
- <related slot="related" ref="related"/>
- <search slot="search" ref="search" :initial-status="search"/>
- <browse slot="browse" ref="browse" :initial-status="browse"/>
- <collected slot="collected" ref="collected" :initial-status="collected"/>
+ <tabs :tabs="tabs" :initial-tab="tab" v-if="authorized" v-on:switch="saveTab($event)" >
+ <related slot="related"/>
+ <search slot="search" :initial-status="search"/>
+ <browse slot="browse" :initial-status="browse"/>
+ <collected slot="collected" :initial-status="collected"/>
</tabs>
<authorisation v-if="!authorized"></authorisation>
<div id="toasts"></div>
@@ -112,6 +112,9 @@
</script>
<style lang="scss">
+ @import "@scss/includes";
+ @import "@scssP/browser.scss";
+
body {
overflow : hidden;
diff --git a/src/vue/Components/Collected/MinedProperty.vue b/src/vue/Components/Collected/MinedProperty.vue
index eafc29d..09b9eb1 100644
--- a/src/vue/Components/Collected/MinedProperty.vue
+++ b/src/vue/Components/Collected/MinedProperty.vue
@@ -96,6 +96,7 @@
overflow : hidden;
box-shadow : 0 0 0 1px transparent;
transition : box-shadow .15s ease-in-out;
+ color : var(--element-fg-color);
&:hover {
box-shadow : 0 0 0 1px var(--element-hover-bg-color);
@@ -103,13 +104,15 @@
}
input {
- width : 100%;
- padding : .25rem;
- box-sizing : border-box;
- box-shadow : 0 0 0 1px var(--element-active-fg-color);
- border-radius : 3px;
- border : none;
- line-height : 2rem;
+ width : 100%;
+ padding : .25rem;
+ box-sizing : border-box;
+ box-shadow : 0 0 0 1px var(--element-active-fg-color);
+ border-radius : 3px;
+ border : none;
+ line-height : 2rem;
+ background-color : var(--element-bg-color);
+ color : var(--element-fg-color);
}
&.password {
diff --git a/src/vue/Components/Form/InputField.vue b/src/vue/Components/Form/InputField.vue
index 7c07b58..73ecc4d 100644
--- a/src/vue/Components/Form/InputField.vue
+++ b/src/vue/Components/Form/InputField.vue
@@ -1,5 +1,11 @@
<template>
- <input :placeholder="getPlaceholder" :title="getTitle" v-model="model"/>
+ <input :type="type"
+ :value="value"
+ :checked="isChecked"
+ :placeholder="getPlaceholder"
+ :title="getTitle"
+ @input="handleInput"
+ @change="handleChange"/>
</template>
<script>
@@ -8,9 +14,17 @@
export default {
props: {
- value : {
+ type : {
type : String,
- default: ''
+ default: 'text'
+ },
+ value : {
+ type : [String, Number, Boolean],
+ default: null
+ },
+ checked : {
+ type : Boolean,
+ default: null
},
placeholder: {
type : String,
@@ -22,13 +36,6 @@
}
},
- data() {
- return {
- model: this.value,
- emit : this.value
- };
- },
-
computed: {
getPlaceholder() {
if(this.placeholder.length === 0) return;
@@ -39,19 +46,25 @@
if(this.title.length === 0) return;
return LocalisationService.translate(this.title);
+ },
+ isChecked() {
+ if(this.type !== 'checkbox' && this.type !== 'radio') return undefined;
+
+ return this.checked || this.value;
}
},
- watch: {
- model(value) {
- if(this.emit !== value) {
- this.emit = value;
- this.$emit('input', value);
+ methods: {
+ handleInput($event) {
+ if(this.type !== 'checkbox' && this.type !== 'radio') {
+ this.$emit('input', $event.target.value);
}
},
- value(value) {
- this.emit = value;
- this.model = value;
+ handleChange($event) {
+ if(this.type === 'checkbox' || this.type === 'radio') {
+ this.$emit('change', $event.target.checked);
+ this.$emit('input', $event.target.checked);
+ }
}
}
};
diff --git a/src/vue/Components/Form/SelectField.vue b/src/vue/Components/Form/SelectField.vue
index 5ef325a..bfc3766 100644
--- a/src/vue/Components/Form/SelectField.vue
+++ b/src/vue/Components/Form/SelectField.vue
@@ -1,8 +1,10 @@
<template>
- <select v-model="model">
+ <select @change="handleChange">
<option v-for="option in optionList"
:key="option.id"
:value="option.id"
+ :disabled="option.disabled"
+ :selected="option.id === value"
:title="getTranslated(option.description)">{{getTranslated(option.label)}}
</option>
</select>
@@ -27,13 +29,6 @@
}
},
- data() {
- return {
- model: this.value,
- emit : this.value
- };
- },
-
computed: {
optionList() {
let options = [];
@@ -43,11 +38,12 @@
let config = this.options[id];
if(typeof config === 'string') {
- options.push({id, label: config, description: null});
+ options.push({id, label: config, disabled: false, description: null});
} else {
let option = {
id : config.hasOwnProperty('id') ? config.id:id,
label : config.label,
+ disabled : config.hasOwnProperty('disabled') ? config.disabled === true:false,
description: config.hasOwnProperty('description') ? config.description:null
};
@@ -64,19 +60,10 @@
if(!this.translate || !text) return text;
return LocalisationService.translate(text);
- }
- },
-
- watch: {
- model(value) {
- if(this.emit !== value) {
- this.emit = value;
- this.$emit('input', value);
- }
},
- value(value) {
- this.emit = value;
- this.model = value;
+ handleChange($event) {
+ this.$emit('change', $event.target.value);
+ this.$emit('input', $event.target.value);
}
}
};
diff --git a/src/vue/Components/Options/Theming.vue b/src/vue/Components/Options/Theming.vue
index 333610c..4477459 100644
--- a/src/vue/Components/Options/Theming.vue
+++ b/src/vue/Components/Options/Theming.vue
@@ -1,13 +1,13 @@
<template>
- <div class="theming">
+ <div class="theming" v-if="theme">
<div class="theme-settings">
<div class="setting">
- <translate tag="label" for="theme-current" say="SettingsCurrentTheme"/>
- <select-field id="theme-current" :options="themeList" :translate="false" v-model="currentTheme"/>
+ <translate tag="label" for="theme-current" say="SettingsThemeId"/>
+ <select-field id="theme-current" :options="list" v-model="themeId"/>
</div>
- <custom-theme />
+ <custom-theme :theme="customTheme" v-if="customTheme && themeId === 'custom'"/>
</div>
- <preview-theme :theme="currentTheme"/>
+ <preview-theme :theme="theme"/>
</div>
</template>
@@ -18,29 +18,68 @@
import ToastService from '@js/Services/ToastService';
import PreviewTheme from '@vue/Components/Theming/PreviewTheme';
import CustomTheme from '@vue/Components/Theming/CustomTheme';
+ import ErrorManager from '@js/Manager/ErrorManager';
export default {
components: {CustomTheme, PreviewTheme, Translate, SelectField},
data() {
return {
- currentTheme: null,
- themeList : {}
+ themeId: null,
+ theme : null,
+ themes : []
};
},
- mounted() {
- MessageService.send({type: 'theme.list'}).then((r) => { this.themeList = r.getPayload(); });
- MessageService.send({type: 'setting.get', payload: 'theme.current'}).then((r) => {
- this.currentTheme =
- r.getPayload();
- });
+ computed: {
+ list() {
+ let list = {};
+
+ for(let theme of this.themes) list[theme.getId()] = theme.getLabel();
+
+ return list;
+ },
+ customTheme() {
+ for(let theme of this.themes) {
+ if(theme.getId() === 'custom') return theme;
+ }
+ }
+ },
+
+ async mounted() {
+ let reply = await MessageService.send({type: 'theme.list'});
+ this.themes = reply.getPayload();
+
+ reply = await MessageService.send({type: 'setting.get', payload: 'theme.current'});
+ this.themeId = reply.getPayload();
+
+ this.setCurrentTheme(this.themeId);
+ },
+
+ methods: {
+ setCurrentTheme(themeId) {
+ for(let theme of this.themes) {
+ if(theme.getId() === themeId) {
+ this.theme = theme;
+ return;
+ }
+ }
+ }
},
watch: {
- currentTheme(value) {
+ themeId(value) {
MessageService.send({type: 'setting.set', payload: {setting: 'theme.current', value}})
.catch((e) => { ToastService.error(e.message); });
+
+ this.setCurrentTheme(this.themeId);
+ MessageService.send(
+ {
+ type : 'theme.preview',
+ payload : this.theme,
+ receiver: 'popup'
+ }
+ ).catch(ErrorManager.catch);
}
}
};
@@ -53,15 +92,12 @@
grid-column-gap : 1rem;
padding : 1rem;
- .theme-preview {
- background-color : grey;
- }
-
.theme-settings {
.setting {
display : grid;
- grid-template-areas : "label" "input";
- grid-template-columns : 2fr 1fr;
+ grid-template-areas : "label input";
+ grid-template-columns : 3fr 2fr;
+ margin-bottom : .25rem;
label {
grid-area : label;
@@ -70,6 +106,7 @@
select,
input {
grid-area : input;
+ width : 100%;
}
}
}
@@ -91,7 +128,7 @@
cursor : pointer;
&[disabled] {
- opacity: .25;
+ opacity : .25;
}
}
}
diff --git a/src/vue/Components/Popup/Collected.vue b/src/vue/Components/Popup/Collected.vue
index fc66bd0..fa8c769 100644
--- a/src/vue/Components/Popup/Collected.vue
+++ b/src/vue/Components/Popup/Collected.vue
@@ -36,10 +36,19 @@
data() {
return {
items : MiningClient.getItems(),
- current: null
+ current: null,
+ listener: (i) => { this.addItem(i); }
};
},
+ created() {
+ MiningClient.update.on(this.listener);
+ },
+
+ destroyed() {
+ MiningClient.update.off(this.listener);
+ },
+
async mounted() {
if(this.initialStatus.current !== null && this.tabs.hasOwnProperty(this.initialStatus.current)) {
this.$refs.foldout.setActive(this.initialStatus.current);
@@ -87,6 +96,13 @@
this.current = $event.tab;
},
+ /**
+ * @param {MiningItem} item
+ */
+ addItem(item) {
+ this.items.push(item);
+ },
+
sendStatus() {
let status = {
current: this.current
diff --git a/src/vue/Components/Tabs.vue b/src/vue/Components/Tabs.vue
index 797b100..d07ce0f 100644
--- a/src/vue/Components/Tabs.vue
+++ b/src/vue/Components/Tabs.vue
@@ -18,7 +18,7 @@
:class="`tab-content-${name}`"
:style="{display: name===tab ? 'block':'none'}">
<keep-alive>
- <slot :name="name"/>
+ <slot :name="name" v-if="name===tab"/>
</keep-alive>
</div>
</div>
diff --git a/src/vue/Components/Theming/BadgeIcon.vue b/src/vue/Components/Theming/BadgeIcon.vue
new file mode 100644
index 0000000..6d5ef7a
--- /dev/null
+++ b/src/vue/Components/Theming/BadgeIcon.vue
@@ -0,0 +1,61 @@
+<template>
+ <div class="setting">
+ <translate tag="label" for="badge-icon" say="SettingsBadgeIcon"/>
+ <select-field id="badge-icon" :options="options" v-model="model"/>
+ </div>
+</template>
+
+<script>
+ import SelectField from '@vue/Components/Form/SelectField';
+ import Translate from '@vue/Components/Translate';
+
+ export default {
+ components: {Translate, SelectField},
+
+ props: ['value'],
+
+ data() {
+ return {
+ model: this.mapValue(this.value)
+ };
+ },
+
+ computed: {
+ options() {
+ return {
+ auto : 'BadgeIconAuto',
+ light : 'BadgeIconLight',
+ medium : 'BadgeIconMedium',
+ dark : 'BadgeIconDark',
+ 'new-light': 'BadgeIconNewLight',
+ 'new' : 'BadgeIconNewMedium',
+ 'new-dark' : 'BadgeIconNewDark'
+ };
+ }
+ },
+
+ methods: {
+ mapValue(value) {
+ if(!value) return 'auto';
+ if(value === 'passwords') return 'medium';
+
+ return value.substr(10);
+ },
+ mapModel(value) {
+ if(value === 'auto') return null;
+ if(value === 'medium') return 'passwords';
+
+ return `passwords-${value}`;
+ }
+ },
+
+ watch: {
+ value(value) {
+ this.model = this.mapValue(value);
+ },
+ model(value) {
+ this.$emit('input', this.mapModel(value));
+ }
+ }
+ };
+</script> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomColorAlpha.vue b/src/vue/Components/Theming/CustomColorAlpha.vue
deleted file mode 100644
index e09d346..0000000
--- a/src/vue/Components/Theming/CustomColorAlpha.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
- <div class="color-setting">
- <translate :say="label"/>
- <input type="checkbox" v-model="currentBase"/>
- <input type="checkbox" v-model="currentHover"/>
- </div>
-</template>
-
-<script>
- import Translate from '@vue/Components/Translate';
-
- export default {
- components: {Translate},
-
- props: {
- name : String,
- type : String,
- colors: Object
- },
-
- data() {
- let keyBase = `${this.name}-${this.type}`,
- keyHover = `${this.name}-hover-${this.type}`;
-
- return {
- colorBase : this.colors[keyBase],
- colorHover : this.colors[keyHover],
- defaultBase : this.colors[keyBase].length > 7,
- defaultHover: this.colors[keyHover].length > 7,
- currentBase : this.colors[keyBase].length > 7,
- currentHover: this.colors[keyHover].length > 7,
- keyBase,
- keyHover
- };
- },
-
- computed: {
- label() {
- return this.type === 'bg' ? 'CustomAlphaBackground':'CustomAlphaForeground';
- }
- },
-
- methods: {
- update() {
- let colors = {};
-
- colors[this.keyBase] = this.colorBase.substr(0, 7);
- colors[this.keyHover] = this.colorHover.substr(0, 7);
-
- if(this.currentBase) colors[this.keyBase] += '00';
- if(this.currentHover) colors[this.keyHover] += '00';
-
- this.$emit('update', colors);
- }
- },
-
- watch: {
- colors: {
- deep: true,
- handler(value) {
- this.colorBase = value[this.keyBase];
- this.colorHover = value[this.keyHover];
- this.defaultBase = this.colorBase.length > 7;
- this.defaultHover = this.colorHover.length > 7;
- this.currentBase = this.defaultBase;
- this.currentHover = this.defaultHover;
- }
- },
- currentBase(value) {
- if(this.defaultBase !== value) this.update();
- },
- currentHover(value) {
- if(this.defaultHover !== value) this.update();
- }
- }
- };
-</script>
-
-<style lang="scss">
-
-</style> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomColorInherit.vue b/src/vue/Components/Theming/CustomColorInherit.vue
new file mode 100644
index 0000000..092c763
--- /dev/null
+++ b/src/vue/Components/Theming/CustomColorInherit.vue
@@ -0,0 +1,87 @@
+<template>
+ <div class="color-setting">
+ <translate :say="label"/>
+ <input-field type="checkbox" :title="title" v-model="currentBase"/>
+ <input-field type="checkbox" :title="title" v-model="currentHover"/>
+ </div>
+</template>
+
+<script>
+ import Translate from '@vue/Components/Translate';
+ import InputField from '@vue/Components/Form/InputField';
+
+ export default {
+ components: {InputField, Translate},
+
+ props: {
+ name : String,
+ type : String,
+ colors: Object
+ },
+
+ data() {
+ let keyBase = `${this.name}-${this.type}`,
+ keyHover = `${this.name}-hover-${this.type}`,
+ inheritBase = this.colors[keyBase] === 'inherit',
+ inheritHover = this.colors[keyHover] === 'inherit';
+
+ return {
+ colorBase : inheritBase ? '#000':this.colors[keyBase],
+ colorHover : inheritHover ? '#000':this.colors[keyHover],
+ defaultBase : inheritBase,
+ defaultHover: inheritHover,
+ currentBase : inheritBase,
+ currentHover: inheritHover,
+ keyBase,
+ keyHover
+ };
+ },
+
+ computed: {
+ label() {
+ return this.type === 'bg' ? 'BackgroundInheritLabel':'ForegroundInheritLabel';
+ },
+ title() {
+ return this.type === 'bg' ? 'BackgroundInheritTitle':'ForegroundInheritTitle';
+ }
+ },
+
+ methods: {
+ update() {
+ let colors = {};
+
+ colors[this.keyBase] = this.currentBase ? 'inherit':this.colorBase;
+ colors[this.keyHover] = this.currentHover ? 'inherit':this.colorHover;
+
+ this.$emit('update', colors);
+ }
+ },
+
+ watch: {
+ colors: {
+ deep: true,
+ handler(value) {
+ let inheritBase = this.colors[this.keyBase] === 'inherit',
+ inheritHover = this.colors[this.keyHover] === 'inherit';
+
+ if(!inheritBase) this.colorBase = value[this.keyBase];
+ if(!inheritHover) this.colorHover = value[this.keyHover];
+ this.defaultBase = inheritBase;
+ this.defaultHover = inheritHover;
+ this.currentBase = inheritBase;
+ this.currentHover = inheritHover;
+ }
+ },
+ currentBase() {
+ this.update();
+ },
+ currentHover() {
+ this.update();
+ }
+ }
+ };
+</script>
+
+<style lang="scss">
+
+</style> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomColorSet.vue b/src/vue/Components/Theming/CustomColorSet.vue
index 3960acd..8d0dbe8 100644
--- a/src/vue/Components/Theming/CustomColorSet.vue
+++ b/src/vue/Components/Theming/CustomColorSet.vue
@@ -1,16 +1,17 @@
<template>
<div class="color-setting">
<translate :say="label"/>
- <input type="color" v-model="currentBase" :disabled="!baseEnabled"/>
- <input type="color" v-model="currentHover" :disabled="!hoverEnabled"/>
+ <input-field type="color" v-model="currentBase" :title="baseTitle" :disabled="baseDisabled"/>
+ <input-field type="color" v-model="currentHover" :title="hoverTitle" :disabled="hoverDisabled"/>
</div>
</template>
<script>
import Translate from '@vue/Components/Translate';
+ import InputField from '@vue/Components/Form/InputField';
export default {
- components: {Translate},
+ components: {InputField, Translate},
props: {
name : String,
@@ -34,13 +35,19 @@
computed: {
label() {
- return this.type === 'bg' ? 'CustomColorsBackground':'CustomColorsForeground';
+ return this.type === 'bg' ? 'BackgroundColorLabel':'ForegroundColorLabel';
},
- baseEnabled() {
- return this.defaultBase.length === 7;
+ baseTitle() {
+ return this.type === 'bg' ? 'BackgroundColorBaseTitle':'ForegroundColorBaseTitle';
},
- hoverEnabled() {
- return this.defaultHover.length === 7;
+ hoverTitle() {
+ return this.type === 'bg' ? 'BackgroundColorHoverTitle':'ForegroundColorHoverTitle';
+ },
+ baseDisabled() {
+ return this.defaultBase === 'inherit';
+ },
+ hoverDisabled() {
+ return this.defaultHover === 'inherit';
}
},
@@ -65,11 +72,11 @@
this.currentHover = value[this.keyHover];
}
},
- currentBase(value) {
- if(this.defaultBase !== value) this.update();
+ currentBase() {
+ this.update();
},
- currentHover(value) {
- if(this.defaultHover !== value) this.update();
+ currentHover() {
+ this.update();
}
}
};
diff --git a/src/vue/Components/Theming/CustomColorToast.vue b/src/vue/Components/Theming/CustomColorToast.vue
new file mode 100644
index 0000000..dc133af
--- /dev/null
+++ b/src/vue/Components/Theming/CustomColorToast.vue
@@ -0,0 +1,77 @@
+<template>
+ <div class="color-setting">
+ <translate :say="label"/>
+ <input-field type="color" v-model="currentBackground" :title="titleBg"/>
+ <input-field type="color" v-model="currentForeground" :title="titleFg"/>
+ </div>
+</template>
+
+<script>
+ import Translate from '@vue/Components/Translate';
+ import InputField from '@vue/Components/Form/InputField';
+
+ export default {
+ components: {InputField, Translate},
+
+ props: {
+ name : String,
+ label : String,
+ colors : Object,
+ titleBg: {
+ type : String,
+ default: 'ToastBackgroundTitle'
+ },
+ titleFg: {
+ type : String,
+ default: 'ToastForegroundTitle'
+ }
+ },
+
+ data() {
+ let keyBackground = `${this.name}-bg`,
+ keyForeground = `${this.name}-fg`;
+
+ return {
+ defaultBackground: this.colors[keyBackground],
+ defaultForeground: this.colors[keyForeground],
+ currentBackground: this.colors[keyBackground],
+ currentForeground: this.colors[keyForeground],
+ keyBackground,
+ keyForeground
+ };
+ },
+
+ methods: {
+ update() {
+ let colors = {};
+
+ colors[this.keyBackground] = this.currentBackground;
+ colors[this.keyForeground] = this.currentForeground;
+
+ this.$emit('update', colors);
+ }
+ },
+
+ watch: {
+ colors: {
+ deep: true,
+ handler(value) {
+ this.defaultBackground = value[this.keyBackground];
+ this.defaultForeground = value[this.keyForeground];
+ this.currentBackground = value[this.keyBackground];
+ this.currentForeground = value[this.keyForeground];
+ }
+ },
+ currentBackground() {
+ this.update();
+ },
+ currentForeground() {
+ this.update();
+ }
+ }
+ };
+</script>
+
+<style lang="scss">
+
+</style> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomFontFamily.vue b/src/vue/Components/Theming/CustomFontFamily.vue
new file mode 100644
index 0000000..4e521e0
--- /dev/null
+++ b/src/vue/Components/Theming/CustomFontFamily.vue
@@ -0,0 +1,52 @@
+<template>
+ <div class="setting">
+ <translate tag="label" for="custom-font" say="SettingsCustomFont"/>
+ <select-field id="custom-font" :options="options" v-model="model"/>
+ </div>
+</template>
+
+<script>
+ import SelectField from '@vue/Components/Form/SelectField';
+ import Translate from '@vue/Components/Translate';
+
+ export default {
+ components: {Translate, SelectField},
+
+ props: ['value'],
+
+ data() {
+ return {
+ model: this.value
+ };
+ },
+
+ computed: {
+ options() {
+ let options = {
+ default : 'FontDefault',
+ mono : 'FontMono',
+ serif : 'FontSerif',
+ sans : 'FontSans',
+ light : 'FontLight',
+ nextcloud: 'FontNextcloud',
+ dyslexic : 'FontOpenDyslexic'
+ };
+
+ if(!options.hasOwnProperty(this.value)) {
+ options[this.value] = 'FontCustom';
+ }
+
+ return options;
+ }
+ },
+
+ watch: {
+ value(value) {
+ this.model = value;
+ },
+ model(value) {
+ if(this.value !== value) this.$emit('input', value);
+ }
+ }
+ };
+</script> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomFontSize.vue b/src/vue/Components/Theming/CustomFontSize.vue
new file mode 100644
index 0000000..9414057
--- /dev/null
+++ b/src/vue/Components/Theming/CustomFontSize.vue
@@ -0,0 +1,59 @@
+<template>
+ <div class="setting">
+ <translate tag="label" for="custom-font" say="SettingsCustomFontSize"/>
+ <select-field id="custom-font" :options="options" v-model="model"/>
+ </div>
+</template>
+
+<script>
+ import SelectField from '@vue/Components/Form/SelectField';
+ import Translate from '@vue/Components/Translate';
+
+ export default {
+ components: {Translate, SelectField},
+
+ props: {
+ value: {
+ default: '11pt'
+ }
+ },
+
+ data() {
+ return {
+ model: this.value ? this.value:'11pt'
+ };
+ },
+
+ mounted() {
+ this.model = this.value;
+ },
+
+ computed: {
+ options() {
+ let options = {
+ '8pt' : 'FontSizeVerySmall',
+ '10pt': 'FontSizeSmall',
+ '11pt': 'FontSizeDefault',
+ '12pt': 'FontSizeMedium',
+ '14pt': 'FontSizeLarge',
+ '16pt': 'FontSizeVeryLarge'
+ };
+
+ if(!options.hasOwnProperty(this.value)) {
+ options[this.value] = this.value;
+ }
+
+ return options;
+ }
+ },
+
+ watch: {
+ value(value) {
+ this.model = value;
+ },
+ model(value) {
+ this.$emit('input', value);
+ }
+ }
+ };
+</script> \ No newline at end of file
diff --git a/src/vue/Components/Theming/CustomTheme.vue b/src/vue/Components/Theming/CustomTheme.vue
index 8013b0a..f06b9e3 100644
--- a/src/vue/Components/Theming/CustomTheme.vue
+++ b/src/vue/Components/Theming/CustomTheme.vue
@@ -1,38 +1,113 @@
<template>
- <div>
- <translate tag="h4" say="SettingsCustomTheme"/>
- <div class="theme-colors">
- <translate tag="h4" say="CustomDefaultElement" />
- <custom-color-set name="element" type="bg" :colors="themeColors" v-on:update="update" />
- <custom-color-set name="element" type="fg" :colors="themeColors" v-on:update="update" />
- <translate tag="h4" say="CustomActiveElement" />
- <custom-color-set name="element-active" type="bg" :colors="themeColors" v-on:update="update" />
- <custom-color-set name="element-active" type="fg" :colors="themeColors" v-on:update="update" />
- <translate tag="h4" say="CustomButtons" />
- <custom-color-alpha name="button" type="bg" :colors="themeColors" v-on:update="update" />
- <custom-color-set name="button" type="bg" :colors="themeColors" v-on:update="update" />
- <custom-color-alpha name="button" type="fg" :colors="themeColors" v-on:update="update" />
- <custom-color-set name="button" type="fg" :colors="themeColors" v-on:update="update"/>
- </div>
+ <div class="theme-colors">
+ <translate tag="h4" say="CustomFont"/>
+ <custom-font-family v-model="font.family"/>
+ <custom-font-size v-model="font.size"/>
+ <translate tag="h4" say="CustomDefaultElement"/>
+ <custom-color-set name="element" type="bg" :colors="colors" v-on:update="update"/>
+ <custom-color-set name="element" type="fg" :colors="colors" v-on:update="update"/>
+ <translate tag="h4" say="CustomActiveElement"/>
+ <custom-color-set name="element-active" type="bg" :colors="colors" v-on:update="update"/>
+ <custom-color-set name="element-active" type="fg" :colors="colors" v-on:update="update"/>
+ <translate tag="h4" say="CustomButtons"/>
+ <custom-color-inherit name="button" type="bg" :colors="colors" v-on:update="update"/>
+ <custom-color-set name="button" type="bg" :colors="colors" v-on:update="update"/>
+ <custom-color-inherit name="button" type="fg" :colors="colors" v-on:update="update"/>
+ <custom-color-set name="button" type="fg" :colors="colors" v-on:update="update"/>
+ <translate tag="h4" say="CustomToasts"/>
+ <custom-color-toast name="info" label="ToastInfoColors" :colors="colors" v-on:update="update"/>
+ <custom-color-toast name="success" label="ToastSuccessColors" :colors="colors" v-on:update="update"/>
+ <custom-color-toast name="warning" label="ToastWarningColors" :colors="colors" v-on:update="update"/>
+ <custom-color-toast name="error" label="ToastErrorColors" :colors="colors" v-on:update="update"/>
+ <translate tag="h4" say="CustomBadge"/>
+ <badge-icon v-model="badge.icon"/>
+ <custom-color-toast name="color"
+ label="CustomBadgeColors"
+ title-bg="BadgeBackgroundTitle"
+ title-fg="BadgeForegroundTitle"
+ :colors="badge"
+ v-on:update="updateBadge"/>
</div>
</template>
<script>
- import LightTheme from '@js/Themes/Light';
+ import Theme from '@js/Models/Theme/Theme';
import Translate from '@vue/Components/Translate';
+ import MessageService from '@js/Services/MessageService';
+ import BadgeIcon from '@vue/Components/Theming/BadgeIcon';
+ import CustomFontSize from '@vue/Components/Theming/CustomFontSize';
import CustomColorSet from '@vue/Components/Theming/CustomColorSet';
- import CustomColorAlpha from '@vue/Components/Theming/CustomColorAlpha';
+ import CustomFontFamily from '@vue/Components/Theming/CustomFontFamily';
+ import CustomColorToast from '@vue/Components/Theming/CustomColorToast';
+ import CustomColorInherit from '@vue/Components/Theming/CustomColorInherit';
+ import ToastService from '@js/Services/ToastService';
+ import ErrorManager from '@js/Manager/ErrorManager';
export default {
- components: {CustomColorAlpha, CustomColorSet, Translate},
+ components: {
+ CustomFontFamily,
+ CustomFontSize,
+ BadgeIcon,
+ CustomColorToast,
+ CustomColorInherit,
+ CustomColorSet,
+ Translate
+ },
+
+ props: {
+ theme: Theme
+ },
+
data() {
return {
- themeColors: LightTheme.colors
- }
+ font : this.theme.getFont(),
+ badge : this.theme.getBadge(),
+ colors: this.theme.getColors()
+ };
},
methods: {
update(colors) {
- this.themeColors = Object.assign(this.themeColors, colors);
+ this.colors = Object.assign(this.colors, colors);
+ this.theme.setColors(this.colors);
+ this.updatePreview();
+ this.saveTheme();
+ },
+ updateBadge(colors) {
+ this.badge = Object.assign(this.badge, colors);
+ this.theme.setBadge(this.badge);
+ this.saveTheme();
+ },
+ updatePreview() {
+ MessageService.send(
+ {
+ type : 'theme.preview',
+ payload : this.theme,
+ receiver: 'popup'
+ }
+ );
+ },
+ async saveTheme() {
+ let reply = await MessageService.send({type: 'theme.save', payload: this.theme});
+ if(!reply.getPayload().success) {
+ ToastService.error(['ThemeSaveError', reply.getPayload().message])
+ .catch(ErrorManager.catch);
+ }
+ }
+ },
+ watch : {
+ 'font.family'(value) {
+ this.theme.setFontFamily(value);
+ this.updatePreview();
+ this.saveTheme();
+ },
+ 'font.size'(value) {
+ this.theme.setFontSize(value);
+ this.updatePreview();
+ this.saveTheme();
+ },
+ 'badge.icon'(value) {
+ this.theme.setBadgeIcon(value);
+ this.saveTheme();
}
}
};
diff --git a/src/vue/Components/Theming/PreviewTheme.vue b/src/vue/Components/Theming/PreviewTheme.vue
index be3f210..54b5d50 100644
--- a/src/vue/Components/Theming/PreviewTheme.vue
+++ b/src/vue/Components/Theming/PreviewTheme.vue
@@ -1,28 +1,54 @@
<template>
- <iframe :src="url" class="theme-preview"/>
+ <div class="theme-preview">
+ <div class="badge-preview">
+ <img :src="icon" alt=""/>
+ <div class="badge" :style="style">8</div>
+ </div>
+ <iframe :src="url" class="popup-preview"/>
+ </div>
</template>
<script>
+ import Theme from '@js/Models/Theme/Theme';
+ import SystemService from '@js/Services/SystemService';
+ import ErrorManager from '@js/Manager/ErrorManager';
+
export default {
props: {
- theme: {
- type: String
- }
+ theme: Theme
},
data() {
return {
- url: 'about:blank'
+ url : 'about:blank',
+ icon: null
};
},
mounted() {
this.url = 'preview.html';
+ SystemService.getFileUrl(`/img/${this.theme.getBadgeIcon()}.svg`)
+ .then((r) => {this.icon = r;})
+ .catch(ErrorManager.catch);
+ },
+
+ computed: {
+ style() {
+ return {
+ backgroundColor: this.theme.getBadgeBackgroundColor(),
+ color : this.theme.getBadgeForegroundColor()
+ };
+ }
},
watch: {
- theme(value, oldValue) {
- if(oldValue !== null) this.$el.contentWindow.location.reload();
+ theme: {
+ deep: true,
+ handler(theme) {
+ SystemService.getFileUrl(`/img/${theme.getBadgeIcon()}.svg`)
+ .then((r) => {this.icon = r;})
+ .catch(ErrorManager.catch);
+ }
}
}
};
@@ -30,7 +56,43 @@
<style lang="scss">
.theme-preview {
- height : 360px;
- border : 1px solid var(--element-hover-bg-color);
+ .badge-preview {
+ border : 1px solid var(--element-hover-bg-color);
+ border-radius : 5px;
+ width : 40px;
+ height : 40px;
+ margin : .5rem auto;
+ padding : 7px;
+ position : relative;
+
+ img {
+ width : 24px;
+ height : 24px;
+ }
+
+ .badge {
+ position : absolute;
+ width : 1rem;
+ height : 1rem;
+ font-size : .8rem;
+ line-height : 1rem;
+ text-align : center;
+ border-radius : 3px;
+ right : -.4rem;
+ top : -.4rem;
+
+ &.bottom {
+ top : auto;
+ bottom : -.4rem;
+ }
+ }
+ }
+
+ .popup-preview {
+ height : 360px;
+ border : 1px solid var(--element-hover-bg-color);
+ width : 100%;
+ border-radius : 5px;
+ }
}
</style> \ No newline at end of file
diff --git a/src/vue/Components/Toast/Toast.vue b/src/vue/Components/Toast/Toast.vue
index 3f64c21..38d16c5 100644
--- a/src/vue/Components/Toast/Toast.vue
+++ b/src/vue/Components/Toast/Toast.vue
@@ -37,7 +37,7 @@
return className;
},
hasCloseButton() {
- return this.toast.getCloseable() && this.toast.getDefault() !== 'close'
+ return this.toast.getCloseable() && this.toast.getDefault() !== 'close';
}
},
@@ -72,49 +72,94 @@
}
.icon {
- float : right;
- position : relative;
- margin : -.25rem;
- padding : .25rem;
+ float : right;
+ position : relative;
+ margin : -.25rem;
+ padding : .2rem;
+ text-align : center;
+ cursor : pointer;
+ transition : background-color .15s;
+ border-radius : 3px;
+
+ &:hover {
+ background-color : var(--success-hv-color);
+ }
+ }
+
+ .button {
+ padding : .5rem;
+ margin : 0 -.5rem;
text-align : center;
+ border-top : 1px solid var(--info-hv-color);
+ cursor : pointer;
- &:before {
- position : relative;
- z-index : 1;
+ &:hover {
+ border-top-color : transparent;
+ background-color : var(--info-hv-color);
}
- &:after {
- content : '';
- background-color : var(--element-fg-color);
- opacity : 0;
- position : absolute;
- top : 0;
- left : 0;
- right : 0;
- bottom : 0;
- border-radius : 3px;
- transition : opacity .15s;
- cursor : pointer;
+ &:first-of-type {
+ margin-top : .5rem;
}
- &:hover:after {
- opacity : .25;
+ &:last-of-type {
+ margin-bottom : -.5rem;
+ border-radius : 0 0 .25rem .25rem;
+ }
+ }
+
+ &.success .toast-content {
+ background-color : var(--success-bg-color);
+ color : var(--success-fg-color);
+
+ .icon:hover {
+ background-color : var(--success-hv-color);
+ }
+
+ .button {
+ border-top-color : var(--warning-hv-color);
+
+ &:hover {
+ border-top-color : transparent;
+ background-color : var(--warning-hv-color);
+ }
}
}
&.warning .toast-content {
background-color : var(--warning-bg-color);
- color: var(--warning-fg-color);
+ color : var(--warning-fg-color);
+
+ .icon:hover {
+ background-color : var(--warning-hv-color);
+ }
+
+ .button {
+ border-top-color : var(--warning-hv-color);
+
+ &:hover {
+ border-top-color : transparent;
+ background-color : var(--warning-hv-color);
+ }
+ }
}
&.error .toast-content {
background-color : var(--error-bg-color);
- color: var(--error-fg-color);
- }
+ color : var(--error-fg-color);
- &.success .toast-content {
- background-color : var(--success-bg-color);
- color: var(--success-fg-color);
+ .icon:hover {
+ background-color : var(--error-hv-color);
+ }
+
+ .button {
+ border-top-color : var(--error-hv-color);
+
+ &:hover {
+ border-top-color : transparent;
+ background-color : var(--error-hv-color);
+ }
+ }
}
&.has-default {
@@ -130,27 +175,5 @@
.message {
display : block;
}
-
- .button {
- padding : .5rem;
- margin : 0 -.5rem;
- text-align : center;
- border-top : 1px solid rgba(0, 0, 0, .25);
- cursor : pointer;
-
- &:hover {
- background-color : rgba(0, 0, 0, .25);
- border-top : 1px solid rgba(0, 0, 0, 0);
- }
-
- &:first-of-type {
- margin-top : .5rem;
- }
-
- &:last-of-type {
- margin-bottom : -.5rem;
- border-radius : 0 0 .25rem .25rem;
- }
- }
}
</style> \ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 205fee8..af7b166 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -42,7 +42,7 @@ module.exports = env => {
);
}
- let platformDir = platform === 'firefox' ? `${__dirname}/src/js/Platform`:`${__dirname}/src/platform/${platform}/js/Platform`;
+ let jsPlatformDir = platform === 'firefox' ? `${__dirname}/src/js/Platform`:`${__dirname}/src/platform/${platform}/js/Platform`;
return {
mode : production ? 'production':'development',
devtool: 'none',
@@ -64,9 +64,10 @@ module.exports = env => {
alias : {
'vue$' : 'vue/dist/vue.esm.js',
'@vue' : `${__dirname}/src/vue`,
- '@js/Platform': platformDir,
+ '@js/Platform': jsPlatformDir,
'@js' : `${__dirname}/src/js`,
- '@scss' : `${__dirname}/src/scss`
+ '@scss' : `${__dirname}/src/scss`,
+ '@scssP' : `${__dirname}/src/platform/${platform}/scss`
}
},
module : {
@@ -102,16 +103,6 @@ module.exports = env => {
outputStyle: 'compressed'
}
}
- },
- {
- loader : 'sass-resources-loader',
- options: {
- sourceMap: true,
- resources: [
- `${__dirname}/src/scss/includes.scss`,
- `${__dirname}/src/platform/${platform}/scss/browser.scss`
- ]
- }
}
]
}