mirror of https://github.com/Lissy93/dashy.git
Merge pull request #8 from Lissy93/bug-fixes-and-improvments
Implements custom CSS editor, customizable item grids, other new features and bug fixes
This commit is contained in:
commit
484a62566b
15
README.md
15
README.md
|
@ -36,6 +36,8 @@
|
|||
<img width="800" src="https://i.ibb.co/L8YbNNc/dashy-demo2.gif" alt="Demo">
|
||||
</p>
|
||||
|
||||
![More themes and screens](https://i.ibb.co/M6nyvqW/dashy-options-screen.png)
|
||||
|
||||
---
|
||||
|
||||
## Running the App 🏃♂️
|
||||
|
@ -96,6 +98,7 @@ All fields are optional, unless otherwise stated.
|
|||
- `theme`- String: The default theme for first load (you can change this later from the UI)
|
||||
- `cssThemes` - String[]: An array of custom theme names which can be used in the theme switcher dropdown - _See **theming** below_
|
||||
- `externalStyleSheet` - String or String[] - Either a URL to an external stylesheet or an array or URLs, which can be applied as themes within the UI
|
||||
- `customCss` - String: Raw CSS that will be applied to the page. Please minify it first.
|
||||
|
||||
**`sections`** - Section[]: (required) An array of sections - _See **`section`** below_
|
||||
|
||||
|
@ -104,11 +107,16 @@ All fields are optional, unless otherwise stated.
|
|||
- `items` - Item[]: (required) An array of items - _See **`item`** below_
|
||||
- `displayData`: An object with the following fields (all optional)
|
||||
- `collapsed` - Boolean: If true, the section will be collapsed initially (defaults to `false`)
|
||||
- `rows` - Int: Number of rows the section should span vertically, e.g. 2 (defaults to `1`)
|
||||
- `cols` - Int: Number of columns the section should span horizontally, e.g. 2 (defaults to `1`)
|
||||
- `color` - String: A custom accent color for the section, as a hex code or HTML color (e.g. `#fff`)
|
||||
- `customStyles` - String: Custom CSS properties that should be applied to that section, e.g. `border: 2px dashed #ff0000;`
|
||||
- `itemSize` - String: Specify the size for items within this group, either `small`, `medium` or `large`
|
||||
- `rows` - Int: Number of rows the section should span vertically, e.g. 2 (defaults to `1`)
|
||||
- `cols` - Int: Number of columns the section should span horizontally, e.g. 2 (defaults to `1`)
|
||||
- `layout` - Enum: `auto` or `grid`. If `grid` is selected, then the number of items per row can be set
|
||||
- `itemCountX` - Int: Number of items horizontally (for `layout: grid`)
|
||||
- `itemCountY` - Int: Number of items vertically (for `layout: grid`)
|
||||
|
||||
Note about `rows` and `cols`: These are defined as a proportion of the screen (rather than by number of child items), and is built using [`grid-layout`](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout). For more info, see [this example](https://i.ibb.co/HXRWVRK/how-rows-and-cols-work-in-dashy.png). In order to set the number of items that will display horizontally or vertically within a section, first set `display: grid`, and then specify values for `itemCountX`, and optionally `itemCountY`.
|
||||
|
||||
**`item`**
|
||||
- `title` - String: The text to display on the item
|
||||
|
@ -174,8 +182,9 @@ This wouldn't have been quite so possible without the following components, kudo
|
|||
- [`vue-material-tabs`](https://github.com/jairoblatt/vue-material-tabs) - Tab view component by @jairoblatt `MIT`
|
||||
- [`VJsoneditor`](https://github.com/yansenlei/VJsoneditor) - Interactive JSON editor component by @yansenlei `MIT`
|
||||
- Forked from [`JsonEditor`](https://github.com/josdejong/jsoneditor) by @josdejong `Apache-2.0 License`
|
||||
- And using [`ajv`](https://github.com/ajv-validator/ajv) `MIT` JSON schema Validator [`ace`](https://github.com/ajaxorg/ace) `BSD` code editor
|
||||
- [`vue-toasted`](https://github.com/shakee93/vue-toasted) - Toast notification component by @shakee93 `MIT`
|
||||
- [`vue-prism-editor`](https://github.com/koca/vue-prism-editor) - Lightweight code editor by @koca `MIT`
|
||||
- Forked from [`prism.js`](https://github.com/PrismJS/prism) `MIT`
|
||||
|
||||
Utils:
|
||||
- [`crypto-js`](https://github.com/brix/crypto-js) - Encryption implementations by @evanvosberg and community `MIT`
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"axios": "^0.21.1",
|
||||
"connect": "^3.7.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"highlight.js": "^11.0.0",
|
||||
"prismjs": "^1.23.0",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"remedial": "^1.0.8",
|
||||
"serve-static": "^1.14.1",
|
||||
|
@ -20,6 +22,7 @@
|
|||
"vue-cli-plugin-yaml": "^1.0.2",
|
||||
"vue-js-modal": "^2.0.0-rc.6",
|
||||
"vue-material-tabs": "^0.0.7",
|
||||
"vue-prism-editor": "^1.2.2",
|
||||
"vue-router": "^3.0.3",
|
||||
"vue-select": "^3.11.2",
|
||||
"vue-toasted": "^1.1.28"
|
||||
|
|
|
@ -509,6 +509,6 @@
|
|||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"background_color": "#000000",
|
||||
"background_color": "#0b1021",
|
||||
"theme_color": "#4DBA87"
|
||||
}
|
25
src/App.vue
25
src/App.vue
|
@ -20,11 +20,21 @@ export default {
|
|||
},
|
||||
data: () => ({
|
||||
// pageInfo: this.getPageInfo(conf.pageInfo),
|
||||
appConfig: conf.appConfig || Defaults.appConfig,
|
||||
showFooter: Defaults.visibleComponents.footer,
|
||||
}),
|
||||
computed: {
|
||||
pageInfo: function pi() { return this.getPageInfo(conf.pageInfo); },
|
||||
pageInfo() {
|
||||
return this.getPageInfo(conf.pageInfo);
|
||||
},
|
||||
appConfig() {
|
||||
if (localStorage[localStorageKeys.APP_CONFIG]) {
|
||||
return JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
|
||||
} else if (conf.appConfig) {
|
||||
return conf.appConfig;
|
||||
} else {
|
||||
return Defaults.appConfig;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Returns either page info from the config, or default values */
|
||||
|
@ -53,6 +63,17 @@ export default {
|
|||
}
|
||||
return '';
|
||||
},
|
||||
injectCustomStyles(usersCss) {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = usersCss;
|
||||
document.head.append(style);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.appConfig.customCss) {
|
||||
const cleanedCss = this.appConfig.customCss.replace(/<\/?[^>]+(>|$)/g, '');
|
||||
this.injectCustomStyles(cleanedCss);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="css3-alt" class="svg-inline--fa fa-css3-alt fa-w-12" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M0 32l34.9 395.8L192 480l157.1-52.2L384 32H0zm313.1 80l-4.8 47.3L193 208.6l-.3.1h111.5l-12.8 146.6-98.2 28.7-98.8-29.2-6.4-73.9h48.9l3.2 38.3 52.6 13.3 54.7-15.4 3.7-61.6-166.3-.5v-.1l-.2.1-3.6-46.3L193.1 162l6.5-2.7H76.7L70.9 112h242.2z"></path></svg>
|
After Width: | Height: | Size: 473 B |
|
@ -3,7 +3,7 @@
|
|||
<div class="section intro">
|
||||
<h2>Cloud Backup & Restore</h2>
|
||||
<p class="intro">
|
||||
Cloud backup and restore is an optional feature, that enabled you to upload your
|
||||
Cloud backup and restore is an optional feature, that enables you to upload your
|
||||
config to the internet, and then restore it on any other device or instance of Dashy.
|
||||
<br><br>
|
||||
All data is fully end-to-end encrypted with AES, using your password as the key.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<Tabs :navAuto="true" name="Add Item" ref="tabView">
|
||||
<TabItem name="Config">
|
||||
<TabItem name="Config" class="main-tab">
|
||||
<div class="main-options-container">
|
||||
<h2>Configuration Options</h2>
|
||||
<a href="/conf.yml" download class="hyperlink-wrapper">
|
||||
|
@ -17,10 +17,25 @@
|
|||
<MetaDataIcon class="button-icon"/>
|
||||
Edit Meta Data
|
||||
</button>
|
||||
<button class="config-button center" @click="goToCustomCss()">
|
||||
<CustomCssIcon class="button-icon"/>
|
||||
Edit Custom CSS
|
||||
</button>
|
||||
<button class="config-button center" @click="openCloudSync()">
|
||||
<CloudIcon class="button-icon"/>
|
||||
{{backupId ? 'Edit Cloud Sync' : 'Enable Cloud Sync'}}
|
||||
</button>
|
||||
<button class="config-button center" @click="resetLocalSettings()">
|
||||
<DeleteIcon class="button-icon"/>
|
||||
Reset Local Settings
|
||||
</button>
|
||||
<div class="config-note">
|
||||
<p class="sub-title">Note:</p>
|
||||
<span>
|
||||
All changes made here are stored locally. To apply globally, either export your config
|
||||
into your conf.yml file, or use the cloud backup/ restore feature.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</TabItem>
|
||||
<TabItem name="Backup Config" class="code-container">
|
||||
|
@ -38,24 +53,36 @@
|
|||
<TabItem name="Edit Site Meta">
|
||||
<EditSiteMeta :config="config" />
|
||||
</TabItem>
|
||||
<TabItem name="Custom Styles">
|
||||
<CustomCssEditor :config="config" initialCss="hello" />
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import yaml from 'highlight.js/lib/languages/yaml';
|
||||
import 'highlight.js/styles/mono-blue.css';
|
||||
|
||||
import JsonToYaml from '@/utils/JsonToYaml';
|
||||
import { localStorageKeys, modalNames } from '@/utils/defaults';
|
||||
import EditSiteMeta from '@/components/Configuration/EditSiteMeta';
|
||||
import JsonEditor from '@/components/Configuration/JsonEditor';
|
||||
import CustomCssEditor from '@/components/Configuration/CustomCss';
|
||||
import DownloadIcon from '@/assets/interface-icons/config-download-file.svg';
|
||||
import DeleteIcon from '@/assets/interface-icons/config-delete-local.svg';
|
||||
import EditIcon from '@/assets/interface-icons/config-edit-json.svg';
|
||||
import MetaDataIcon from '@/assets/interface-icons/config-meta-data.svg';
|
||||
import CustomCssIcon from '@/assets/interface-icons/config-custom-css.svg';
|
||||
import CloudIcon from '@/assets/interface-icons/cloud-backup-restore.svg';
|
||||
|
||||
export default {
|
||||
name: 'ConfigContainer',
|
||||
data() {
|
||||
return {
|
||||
jsonParser: JsonToYaml,
|
||||
backupId: localStorage[localStorageKeys.BACKUP_ID] || '',
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -69,10 +96,13 @@ export default {
|
|||
components: {
|
||||
EditSiteMeta,
|
||||
JsonEditor,
|
||||
CustomCssEditor,
|
||||
DownloadIcon,
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
CloudIcon,
|
||||
MetaDataIcon,
|
||||
CustomCssIcon,
|
||||
},
|
||||
methods: {
|
||||
/* Seletcs the edit tab of the tab view */
|
||||
|
@ -84,6 +114,13 @@ export default {
|
|||
const itemToSelect = this.$refs.tabView.navItems[3];
|
||||
this.$refs.tabView.activeTabItem({ tabItem: itemToSelect, byUser: true });
|
||||
},
|
||||
goToCustomCss() {
|
||||
const itemToSelect = this.$refs.tabView.navItems[4];
|
||||
this.$refs.tabView.activeTabItem({ tabItem: itemToSelect, byUser: true });
|
||||
},
|
||||
openCloudSync() {
|
||||
this.$modal.show(modalNames.CLOUD_BACKUP);
|
||||
},
|
||||
copyConfigToClipboard() {
|
||||
navigator.clipboard.writeText(this.jsonParser(this.config));
|
||||
// event.target.textContent = 'Copied to clipboard';
|
||||
|
@ -104,13 +141,20 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
hljs.registerLanguage('yaml', yaml);
|
||||
const highlighted = hljs.highlight(this.jsonParser(this.config), { language: 'yaml' }).value;
|
||||
document.getElementById('conf-yaml').innerHTML = highlighted;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/style-helpers.scss';
|
||||
|
||||
pre {
|
||||
color: var(--config-code-color);
|
||||
font-weight: bold !important;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
|
@ -148,6 +192,12 @@ a.config-button, button.config-button {
|
|||
|
||||
div.code-container {
|
||||
background: var(--config-code-background);
|
||||
#conf-yaml {
|
||||
font-family: 'Inconsolata', sans-serif;
|
||||
.hljs-attr {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
.yaml-action-buttons {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
|
@ -186,7 +236,11 @@ div.code-container {
|
|||
|
||||
.tab-item {
|
||||
overflow-y: auto;
|
||||
@extend .scroll-bar;
|
||||
background: var(--config-settings-background);
|
||||
&.main-tab {
|
||||
min-height: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
a.hyperlink-wrapper {
|
||||
|
@ -205,6 +259,25 @@ a.hyperlink-wrapper {
|
|||
color: var(--config-settings-color);
|
||||
}
|
||||
}
|
||||
|
||||
.config-note {
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
left: 10%;
|
||||
margin: 0.5rem auto;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px dashed var(--config-settings-color);
|
||||
border-radius: var(--curve-factor);
|
||||
text-align: left;
|
||||
opacity: var(--dimming-factor);
|
||||
background: var(--config-settings-background);
|
||||
p.sub-title {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -235,4 +308,8 @@ a.hyperlink-wrapper {
|
|||
background: var(--config-settings-color);
|
||||
}
|
||||
}
|
||||
|
||||
#conf-yaml .hljs-attr {
|
||||
color: #9c03f5;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div class="json-editor-outer">
|
||||
<prism-editor class="my-editor" v-model="customCss" :highlight="highlighter" line-numbers />
|
||||
<button class="save-button" @click="save()">Save Changes</button>
|
||||
<p>Note, you will need to refresh the page for your changes to take effect</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PrismEditor } from 'vue-prism-editor';
|
||||
import { highlight, languages } from 'prismjs/components/prism-core';
|
||||
import 'prismjs/components/prism-css';
|
||||
import 'prismjs/themes/prism-funky.css';
|
||||
import 'vue-prism-editor/dist/prismeditor.min.css';
|
||||
|
||||
import { localStorageKeys } from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'JsonEditor',
|
||||
props: {
|
||||
config: Object,
|
||||
},
|
||||
components: {
|
||||
PrismEditor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
customCss: this.config.appConfig.customCss || '\n\n\n\n\n',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
validate(css) {
|
||||
return css.match(/((?:^\s*)([\w#.@*,:\-.:>,*\s]+)\s*{(?:[\s]*)((?:[A-Za-z\- \s]+[:]\s*['"0-9\w .,/()\-!%]+;?)*)*\s*}(?:\s*))/gmi);
|
||||
},
|
||||
save() {
|
||||
let msg = '';
|
||||
if (this.validate(this.customCss)) {
|
||||
const appConfig = { ...this.config.appConfig };
|
||||
appConfig.customCss = this.customCss;
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(appConfig));
|
||||
msg = 'Changes saved succesfully';
|
||||
this.inject(this.customCss);
|
||||
} else {
|
||||
msg = 'Error - Invalid CSS';
|
||||
}
|
||||
this.$toasted.show(msg);
|
||||
},
|
||||
inject(userStyles) {
|
||||
const cleanedCss = userStyles.replace(/<\/?[^>]+(>|$)/g, '');
|
||||
const style = document.createElement('style');
|
||||
style.textContent = cleanedCss;
|
||||
document.head.append(style);
|
||||
},
|
||||
highlighter(code) {
|
||||
return highlight(code, languages.css);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
button.save-button {
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0.25rem auto;
|
||||
font-size: 1.2rem;
|
||||
background: var(--config-settings-color);
|
||||
color: var(--config-settings-background);
|
||||
border: 1px solid var(--config-settings-background);
|
||||
border-radius: var(--curve-factor);
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: var(--config-settings-background);
|
||||
color: var(--config-settings-color);
|
||||
border-color: var(--config-settings-color);
|
||||
}
|
||||
}
|
||||
|
||||
.prism-editor-wrapper {
|
||||
min-height: 200px;
|
||||
border: 1px solid var(--transparent-70);
|
||||
border-radius: var(--curve-factor);
|
||||
width: 90%;
|
||||
margin: 0.5rem auto;
|
||||
background: var(--transparent-50);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -77,7 +77,7 @@ export default {
|
|||
title: this.config.pageInfo.title,
|
||||
description: this.config.pageInfo.description,
|
||||
footerText: this.config.pageInfo.footerText,
|
||||
navLinks: this.config.pageInfo.navLinks,
|
||||
navLinks: this.config.pageInfo.navLinks || [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -38,7 +38,7 @@ export default {
|
|||
methods: {
|
||||
save() {
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(this.jsonData));
|
||||
this.$toasted.show('Changes seved succesfully');
|
||||
this.$toasted.show('Changes saved succesfully');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<modal :name="name" :resizable="true" width="80%" height="80%" @closed="$emit('closed')">
|
||||
<modal :name="name" :resizable="true" width="80%" height="80%" @closed="modalClosed()">
|
||||
<div slot="top-right" @click="hide()">Close</div>
|
||||
<a @click="hide()" class="close-button" title="Close">x</a>
|
||||
<iframe v-if="url" :src="url" @keydown.esc="close" class="frame"/>
|
||||
|
@ -20,10 +20,14 @@ export default {
|
|||
show: function show(url) {
|
||||
this.url = url;
|
||||
this.$modal.show(this.name);
|
||||
this.$emit('modalChanged', true);
|
||||
},
|
||||
hide: function hide() {
|
||||
this.$modal.hide(this.name);
|
||||
},
|
||||
modalClosed() {
|
||||
this.$emit('modalChanged', false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
<div v-if="!items || items.length < 1" class="no-items">
|
||||
No Items to Show Yet
|
||||
</div>
|
||||
<div v-else class="there-are-items">
|
||||
<div v-else
|
||||
:class="`there-are-items ${isGridLayout? 'item-group-grid': ''}`"
|
||||
:style="gridStyle"
|
||||
>
|
||||
<Item
|
||||
v-for="(item, index) in items"
|
||||
:id="`${index}_${makeId(item.title)}`"
|
||||
|
@ -33,6 +36,7 @@
|
|||
:ref="`iframeModal-${groupId}`"
|
||||
:name="`iframeModal-${groupId}`"
|
||||
@closed="$emit('itemClicked')"
|
||||
@modalChanged="modalChanged"
|
||||
/>
|
||||
</Collapsable>
|
||||
</template>
|
||||
|
@ -50,6 +54,7 @@ export default {
|
|||
displayData: Object,
|
||||
items: Array,
|
||||
itemSize: String,
|
||||
modalOpen: Boolean,
|
||||
},
|
||||
components: {
|
||||
Collapsable,
|
||||
|
@ -57,9 +62,21 @@ export default {
|
|||
IframeModal,
|
||||
},
|
||||
computed: {
|
||||
newItemSize: function size() {
|
||||
newItemSize() {
|
||||
return this.displayData.itemSize || this.itemSize;
|
||||
},
|
||||
isGridLayout() {
|
||||
return this.displayData.layout === 'grid'
|
||||
|| !!(this.displayData.itemCountX || this.displayData.itemCountY);
|
||||
},
|
||||
gridStyle() {
|
||||
let styles = '';
|
||||
styles += this.displayData.itemCountX
|
||||
? `grid-template-columns: repeat(${this.displayData.itemCountX}, 1fr);` : '';
|
||||
styles += this.displayData.itemCountY
|
||||
? `grid-template-rows: repeat(${this.displayData.itemCountY}, 1fr);` : '';
|
||||
return styles;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Returns a unique lowercase string, based on name, for section ID */
|
||||
|
@ -70,11 +87,16 @@ export default {
|
|||
triggerModal(url) {
|
||||
this.$refs[`iframeModal-${this.groupId}`].show(url);
|
||||
},
|
||||
modalChanged(changedTo) {
|
||||
this.$emit('change-modal-visibility', changedTo);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/media-queries.scss';
|
||||
@import '@/styles/style-helpers.scss';
|
||||
|
||||
.no-items {
|
||||
width: 100px;
|
||||
|
@ -92,6 +114,17 @@ export default {
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
&.item-group-grid {
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
@extend .scroll-bar;
|
||||
@include phone { grid-template-columns: repeat(1, 1fr); }
|
||||
@include tablet { grid-template-columns: repeat(2, 1fr); }
|
||||
@include laptop { grid-template-columns: repeat(2, 1fr); }
|
||||
@include monitor { grid-template-columns: repeat(3, 1fr); }
|
||||
@include big-screen { grid-template-columns: repeat(4, 1fr); }
|
||||
@include big-screen-up { grid-template-columns: repeat(5, 1fr); }
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -27,16 +27,13 @@ import IconSpanner from '@/assets/interface-icons/config-editor.svg';
|
|||
import IconCloud from '@/assets/interface-icons/cloud-backup-restore.svg';
|
||||
import ConfigContainer from '@/components/Configuration/ConfigContainer';
|
||||
import CloudBackupRestore from '@/components/Configuration/CloudBackupRestore';
|
||||
import { topLevelConfKeys, localStorageKeys } from '@/utils/defaults';
|
||||
import { topLevelConfKeys, localStorageKeys, modalNames } from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'ConfigLauncher',
|
||||
data() {
|
||||
return {
|
||||
modalNames: {
|
||||
CONF_EDITOR: 'CONF_EDITOR',
|
||||
CLOUD_BACKUP: 'CLOUD_BACKUP',
|
||||
},
|
||||
modalNames,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
@ -52,11 +49,11 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
showEditor: function show() {
|
||||
this.$modal.show(this.modalNames.CONF_EDITOR);
|
||||
this.$modal.show(modalNames.CONF_EDITOR);
|
||||
this.$emit('modalChanged', true);
|
||||
},
|
||||
showCloudModal: function show() {
|
||||
this.$modal.show(this.modalNames.CLOUD_BACKUP);
|
||||
this.$modal.show(modalNames.CLOUD_BACKUP);
|
||||
this.$emit('modalChanged', true);
|
||||
},
|
||||
combineConfig() {
|
||||
|
|
|
@ -5,7 +5,6 @@ import VModal from 'vue-js-modal'; // Modal component
|
|||
import VSelect from 'vue-select'; // Select dropdown component
|
||||
import VTabs from 'vue-material-tabs'; // Tab view component, used on the config page
|
||||
import Toasted from 'vue-toasted'; // Toast component, used to show confirmation notifications
|
||||
|
||||
import { toastedOptions } from './utils/defaults';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
|
|
@ -14,6 +14,13 @@ try {
|
|||
localPageInfo = undefined;
|
||||
}
|
||||
|
||||
let localAppConfig;
|
||||
try {
|
||||
localAppConfig = JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
|
||||
} catch (e) {
|
||||
localAppConfig = undefined;
|
||||
}
|
||||
|
||||
const router = new Router({
|
||||
routes: [
|
||||
{
|
||||
|
@ -23,7 +30,7 @@ const router = new Router({
|
|||
props: {
|
||||
sections: sections || [],
|
||||
pageInfo: localPageInfo || pageInfo || defaultPageInfo,
|
||||
appConfig: appConfig || {},
|
||||
appConfig: localAppConfig || appConfig || {},
|
||||
},
|
||||
meta: {
|
||||
title: 'Home Page',
|
||||
|
|
|
@ -35,9 +35,10 @@
|
|||
--outline-color: none;
|
||||
--curve-factor: 5px; // Border radius for most components
|
||||
--curve-factor-navbar: 16px; // Border radius for header
|
||||
--dimming-factor: 0.8; // Opacity for semi-transparent components
|
||||
--dimming-factor: 0.7; // Opacity for semi-transparent components
|
||||
|
||||
/* Settings for specific components */
|
||||
--scroll-bar-width: 8px;
|
||||
--item-group-padding: 5px; // Determines width of item-group outline
|
||||
--item-shadow: 1px 1px 2px #130f23;
|
||||
--item-hover-shadow: 1px 2px 4px #373737;
|
||||
|
@ -74,4 +75,6 @@
|
|||
--config-settings-background: var(--background-darker);
|
||||
--toast-background: var(--primary);
|
||||
--toast-color: var(--background);
|
||||
--scroll-bar-color: var(--primary);
|
||||
--scroll-bar-background: var(--background-darker);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
@import '@/styles/style-helpers.scss';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inconsolata';
|
||||
src: url('./assets/fonts/Inconsolata-Light.ttf');
|
||||
|
@ -8,6 +10,8 @@ html {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
transition: all 1s;
|
||||
margin-top: -3px;
|
||||
@extend .scroll-bar;
|
||||
}
|
||||
|
||||
/* Default app font face */
|
||||
|
@ -19,33 +23,3 @@ body, div, p, a, span, label, input, button {
|
|||
h1, h2, h3, h4, h5 {
|
||||
font-family: 'Inconsolata', sans-serif;
|
||||
}
|
||||
|
||||
.bold { font-weight: bold; }
|
||||
.light { font-weight: lighter; }
|
||||
.text-left { text-align: left;}
|
||||
.text-right { text-align: right;}
|
||||
.text-center { text-align: center;}
|
||||
.horizontal-center { margin: 0 auto; }
|
||||
.border-box { box-sizing: border-box; }
|
||||
|
||||
/* Overiding styles for the global toast component */
|
||||
.toast-message {
|
||||
background: var(--toast-background) !important;
|
||||
color: var(--toast-color) !important;
|
||||
border: 1px solid var(--toast-color) !important;
|
||||
border-radius: var(--curve-factor) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background: var(--danger) !important;
|
||||
color: var(--white) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
.toast-success {
|
||||
background: var(--success) !important;
|
||||
color: var(--white) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
/* Fancy scrollbar */
|
||||
.scroll-bar {
|
||||
&::-webkit-scrollbar {
|
||||
width: var(--scroll-bar-width);
|
||||
height: var(--scroll-bar-width);
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
border-radius: var(--curve-factor);
|
||||
background-color: var(--scroll-bar-background);
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--scroll-bar-color);
|
||||
border-radius: var(--curve-factor);
|
||||
}
|
||||
}
|
||||
|
||||
/* Single-style helpers */
|
||||
.bold { font-weight: bold; }
|
||||
.light { font-weight: lighter; }
|
||||
.text-left { text-align: left;}
|
||||
.text-right { text-align: right;}
|
||||
.text-center { text-align: center;}
|
||||
.horizontal-center { margin: 0 auto; }
|
||||
.border-box { box-sizing: border-box; }
|
||||
|
||||
/* Overiding styles for the global toast component */
|
||||
.toast-message {
|
||||
background: var(--toast-background) !important;
|
||||
color: var(--toast-color) !important;
|
||||
border: 1px solid var(--toast-color) !important;
|
||||
border-radius: var(--curve-factor) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
.toast-error {
|
||||
background: var(--danger) !important;
|
||||
color: var(--white) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
.toast-success {
|
||||
background: var(--success) !important;
|
||||
color: var(--white) !important;
|
||||
font-size: 1.25rem !important;
|
||||
}
|
|
@ -37,7 +37,6 @@ const encodeGetParams = p => Object.entries(p).map(kv => kv.map(encodeURICompone
|
|||
/* Restores the backup */
|
||||
export const restore = (backupId, password) => {
|
||||
const params = encodeGetParams({ backupId, subHash: makeSubHash(password) });
|
||||
console.log(makeSubHash(password));
|
||||
const url = `${ENDPOINT}/?${params}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axios.get(url).then((response) => {
|
||||
|
|
|
@ -50,6 +50,10 @@ module.exports = {
|
|||
BACKUP_ID: 'backupId',
|
||||
BACKUP_HASH: 'backupHash',
|
||||
},
|
||||
modalNames: {
|
||||
CONF_EDITOR: 'CONF_EDITOR',
|
||||
CLOUD_BACKUP: 'CLOUD_BACKUP',
|
||||
},
|
||||
topLevelConfKeys: {
|
||||
PAGE_INFO: 'pageInfo',
|
||||
APP_CONFIG: 'appConfig',
|
||||
|
|
|
@ -25,8 +25,9 @@
|
|||
:displayData="getDisplayData(section)"
|
||||
:groupId="`section-${index}`"
|
||||
:items="filterTiles(section.items)"
|
||||
:itemSize="itemSizeBound"
|
||||
@itemClicked="finishedSearching()"
|
||||
:itemSize="itemSizeBound"
|
||||
@change-modal-visibility="updateModalVisibility"
|
||||
:class="(filterTiles(section.items).length === 0 && searchValue) ? 'no-results' : ''"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@ module.exports = {
|
|||
chainWebpack: config => {
|
||||
config.module.rules.delete('svg');
|
||||
},
|
||||
|
||||
configureWebpack: {
|
||||
module: {
|
||||
rules: [
|
||||
|
@ -9,4 +10,11 @@ module.exports = {
|
|||
],
|
||||
},
|
||||
},
|
||||
|
||||
pwa: {
|
||||
name: 'Dashy',
|
||||
themeColor: '#00CCB4',
|
||||
msTileColor: '#0b1021',
|
||||
manifestCrossorigin: 'use-credentials',
|
||||
},
|
||||
};
|
||||
|
|
48
yarn.lock
48
yarn.lock
|
@ -2563,6 +2563,15 @@ cli-width@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
clipboard@^2.0.0:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
|
||||
integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==
|
||||
dependencies:
|
||||
good-listener "^1.2.2"
|
||||
select "^1.1.2"
|
||||
tiny-emitter "^2.0.0"
|
||||
|
||||
clipboardy@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
|
||||
|
@ -3255,6 +3264,11 @@ delayed-stream@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
delegate@^3.1.2:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
|
||||
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
|
@ -4434,6 +4448,13 @@ globby@^9.2.0:
|
|||
pify "^4.0.1"
|
||||
slash "^2.0.0"
|
||||
|
||||
good-listener@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
||||
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
|
||||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.2:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
|
@ -4565,6 +4586,11 @@ highlight.js@^10.7.1:
|
|||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
|
||||
integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
|
||||
|
||||
highlight.js@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.0.0.tgz#e22ac9ca45edc4f87a2187685d591a108ceb8449"
|
||||
integrity sha512-ByaTMfsSuoqerTwemOgpIhfULEIaK52JJYhky/sK7/Yqc0+t7Uh5DHay9vIC94YXSupnQ1Vqfc9VXrYP4eXW3Q==
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
|
@ -7069,6 +7095,13 @@ pretty-error@^2.0.2:
|
|||
lodash "^4.17.20"
|
||||
renderkid "^2.0.4"
|
||||
|
||||
prismjs@^1.23.0:
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33"
|
||||
integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==
|
||||
optionalDependencies:
|
||||
clipboard "^2.0.0"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
|
@ -7643,6 +7676,11 @@ select-hose@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
|
||||
|
||||
select@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
|
||||
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
|
||||
|
||||
selfsigned@^1.10.8:
|
||||
version "1.10.8"
|
||||
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30"
|
||||
|
@ -8383,6 +8421,11 @@ timsort@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-emitter@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
@ -8852,6 +8895,11 @@ vue-material-tabs@^0.0.7:
|
|||
resolved "https://registry.yarnpkg.com/vue-material-tabs/-/vue-material-tabs-0.0.7.tgz#5f3fa04ad35384af68582f7c89ad4cecac89207b"
|
||||
integrity sha512-02X5paTksYKrGvSRpMdkctRO9qhvJFD5VEGxd0xjOX4sYz6mZSAez0Z/+aYf7Z5ziY+eJ9dMQmxaLn9DVKQRJw==
|
||||
|
||||
vue-prism-editor@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-1.2.2.tgz#023cfd4329848f191aac851f2f5e6c7a8c2e059f"
|
||||
integrity sha512-Lq2VgVygTx3Whn/tC8gD4m1ajA4lzSyCTqPLZA1Dq/ErbBaZA93FWRblwCoDR7AD2nXhGWuiTzb5ih3guzB7DA==
|
||||
|
||||
vue-resize@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-1.0.1.tgz#c120bed4e09938771d622614f57dbcf58a5147ee"
|
||||
|
|
Loading…
Reference in New Issue