mirror of https://github.com/Lissy93/dashy.git
🔀 Merge pull request #78 from Lissy93/FEATURE_show-hide-components
[FEATURE] Show hide components
This commit is contained in:
commit
6643fcf976
|
@ -68,7 +68,7 @@ All fields are optional, unless otherwise stated.
|
|||
**`cssThemes`** | `string[]` | _Optional_ | An array of custom theme names which can be used in the theme switcher dropdown
|
||||
**`externalStyleSheet`** | `string` or `string[]` | _Optional_ | Either a URL to an external stylesheet or an array or URLs, which can be applied as themes within the UI
|
||||
**`customCss`** | `string` | _Optional_ | Raw CSS that will be applied to the page. This can also be set from the UI. Please minify it first.
|
||||
**`showSplashScreen`** | `boolean` | _Optional_ | Should display a splash screen while the app is loading. Defaults to false, except on first load
|
||||
**`hideComponents`** | `object` | _Optional_ | A list of key page components (header, footer, search, settings, etc) that are present by default, but can be removed using this option. See [`appConfig.hideComponents`](#appconfighideComponents-optional)
|
||||
**`allowConfigEdit`** | `boolean` | _Optional_ | Should prevent / allow the user to write configuration changes to the conf.yml from the UI. When set to `false`, the user can only apply changes locally using the config editor within the app, whereas if set to `true` then changes can be written to disk directly through the UI. Defaults to `true`. Note that if authentication is enabled, the user must be of type `admin` in order to apply changes globally.
|
||||
**`disableServiceWorker`** | `boolean` | _Optional_ | Service workers cache web applications to improve load times and offer basic offline functionality, and are enabled by default in Dashy. The service worker can sometimes cause older content to be cached, requiring the app to be hard-refreshed. If you do not want SW functionality, or are having issues with caching, set this property to `true` to disable all service workers.
|
||||
**`disableContextMenu`** | `boolean` | _Optional_ | If set to `true`, the custom right-click context menu will be disabled. Defaults to `false`.
|
||||
|
@ -85,6 +85,19 @@ All fields are optional, unless otherwise stated.
|
|||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
### `appConfig.hideComponents` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`hideHeading`** | `boolean` | _Optional_ | If set to `true`, the page title & sub-title will not be visible. Defaults to `false`
|
||||
**`hideNav`** | `boolean` | _Optional_ | If set to `true`, the navigation menu will not be visible. Defaults to `false`
|
||||
**`hideSearch`** | `boolean` | _Optional_ | If set to `true`, the search bar will not be visible. Defaults to `false`
|
||||
**`hideSettings`** | `boolean` | _Optional_ | If set to `true`, the settings menu will not be visible. Defaults to `false`
|
||||
**`hideFooter`** | `boolean` | _Optional_ | If set to `true`, the footer will not be visible. Defaults to `false`
|
||||
**`hideSplashScreen`** | `boolean` | _Optional_ | If set to `true`, splash screen will not be visible while the app loads. Defaults to `true` (except on first load, when the loading screen is always shown)
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
### `section`
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
|
|
24
src/App.vue
24
src/App.vue
|
@ -11,8 +11,17 @@
|
|||
import Header from '@/components/PageStrcture/Header.vue';
|
||||
import Footer from '@/components/PageStrcture/Footer.vue';
|
||||
import LoadingScreen from '@/components/PageStrcture/LoadingScreen.vue';
|
||||
import Defaults, { localStorageKeys, splashScreenTime } from '@/utils/defaults';
|
||||
import { config, appConfig, pageInfo } from '@/utils/ConfigAccumalator';
|
||||
import { componentVisibility } from '@/utils/ConfigHelpers';
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import {
|
||||
localStorageKeys,
|
||||
splashScreenTime,
|
||||
visibleComponents as defaultVisibleComponents,
|
||||
} from '@/utils/defaults';
|
||||
|
||||
const Accumulator = new ConfigAccumulator();
|
||||
const config = Accumulator.config();
|
||||
const visibleComponents = componentVisibility(config.appConfig) || defaultVisibleComponents;
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
@ -23,13 +32,15 @@ export default {
|
|||
},
|
||||
provide: {
|
||||
config,
|
||||
visibleComponents,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showFooter: Defaults.visibleComponents.footer,
|
||||
showFooter: visibleComponents.footer,
|
||||
isLoading: true,
|
||||
appConfig,
|
||||
pageInfo,
|
||||
appConfig: Accumulator.appConfig(),
|
||||
pageInfo: Accumulator.pageInfo(),
|
||||
visibleComponents,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -45,7 +56,8 @@ export default {
|
|||
document.head.append(style);
|
||||
},
|
||||
shouldShowSplash() {
|
||||
return this.appConfig.showSplashScreen || !localStorage[localStorageKeys.HIDE_WELCOME_BANNER];
|
||||
return (this.visibleComponents || defaultVisibleComponents).splashScreen
|
||||
|| !localStorage[localStorageKeys.HIDE_WELCOME_BANNER];
|
||||
},
|
||||
hideSplash() {
|
||||
if (this.shouldShowSplash() && !this.shouldHidePageComponents()) {
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
<script>
|
||||
import PageTitle from '@/components/PageStrcture/PageTitle.vue';
|
||||
import Nav from '@/components/PageStrcture/Nav.vue';
|
||||
import { visibleComponents } from '@/utils/defaults';
|
||||
import { visibleComponents as defaultVisibleComponents } from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'Header',
|
||||
inject: ['visibleComponents'],
|
||||
components: {
|
||||
PageTitle,
|
||||
Nav,
|
||||
|
@ -21,9 +22,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
hiddenComponents: this.pageInfo.hiddenComponents || {},
|
||||
titleVisible: visibleComponents.pageTitle,
|
||||
navVisible: visibleComponents.navigation,
|
||||
titleVisible: (this.visibleComponents || defaultVisibleComponents).pageTitle,
|
||||
navVisible: (this.visibleComponents || defaultVisibleComponents).navigation,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import SearchBar from '@/components/Settings/SearchBar';
|
||||
import ConfigLauncher from '@/components/Settings/ConfigLauncher';
|
||||
import ThemeSelector from '@/components/Settings/ThemeSelector';
|
||||
|
@ -41,6 +40,10 @@ import AppInfoModal from '@/components/Configuration/AppInfoModal';
|
|||
import { logout as registerLogout } from '@/utils/Auth';
|
||||
import IconOpen from '@/assets/interface-icons/config-open-settings.svg';
|
||||
import IconClose from '@/assets/interface-icons/config-close.svg';
|
||||
import {
|
||||
localStorageKeys,
|
||||
visibleComponents as defaultVisibleComponents,
|
||||
} from '@/utils/defaults';
|
||||
|
||||
export default {
|
||||
name: 'SettingsContainer',
|
||||
|
@ -65,6 +68,7 @@ export default {
|
|||
IconOpen,
|
||||
IconClose,
|
||||
},
|
||||
inject: ['visibleComponents'],
|
||||
methods: {
|
||||
userIsTypingSomething(something) {
|
||||
this.$emit('user-is-searchin', something);
|
||||
|
@ -106,13 +110,13 @@ export default {
|
|||
},
|
||||
getSettingsVisibility() {
|
||||
return JSON.parse(localStorage[localStorageKeys.HIDE_SETTINGS]
|
||||
|| Defaults.visibleComponents.settings);
|
||||
|| (this.visibleComponents || defaultVisibleComponents).settings);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchVisible: Defaults.visibleComponents.searchBar,
|
||||
settingsVisible: this.getSettingsVisibility(),
|
||||
searchVisible: (this.visibleComponents || defaultVisibleComponents).searchBar,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,13 +6,13 @@ import Login from '@/views/Login.vue';
|
|||
import Workspace from '@/views/Workspace.vue';
|
||||
import DownloadConfig from '@/views/DownloadConfig.vue';
|
||||
import { isLoggedIn } from '@/utils/Auth';
|
||||
import { appConfig, pageInfo, sections } from '@/utils/ConfigAccumalator';
|
||||
import { config } from '@/utils/ConfigHelpers';
|
||||
import { metaTagData } from '@/utils/defaults';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
const isAuthenticated = () => {
|
||||
const users = appConfig.auth;
|
||||
const users = config.appConfig.auth;
|
||||
return (!users || isLoggedIn(users));
|
||||
};
|
||||
|
||||
|
@ -22,13 +22,9 @@ const router = new Router({
|
|||
path: '/',
|
||||
name: 'home',
|
||||
component: Home,
|
||||
props: {
|
||||
appConfig,
|
||||
pageInfo,
|
||||
sections,
|
||||
},
|
||||
props: config,
|
||||
meta: {
|
||||
title: pageInfo.title || 'Home Page',
|
||||
title: config.pageInfo.title || 'Home Page',
|
||||
metaTags: metaTagData,
|
||||
},
|
||||
},
|
||||
|
@ -36,9 +32,9 @@ const router = new Router({
|
|||
path: '/workspace',
|
||||
name: 'workspace',
|
||||
component: Workspace,
|
||||
props: { appConfig, pageInfo, sections },
|
||||
props: config,
|
||||
meta: {
|
||||
title: pageInfo.title || 'Dashy Workspace',
|
||||
title: config.pageInfo.title || 'Dashy Workspace',
|
||||
metaTags: metaTagData,
|
||||
},
|
||||
},
|
||||
|
@ -47,7 +43,7 @@ const router = new Router({
|
|||
name: 'login',
|
||||
component: Login,
|
||||
props: {
|
||||
appConfig,
|
||||
appConfig: config.appConfig,
|
||||
},
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (isAuthenticated()) router.push({ path: '/' });
|
||||
|
@ -63,9 +59,9 @@ const router = new Router({
|
|||
path: '/download',
|
||||
name: 'download',
|
||||
component: DownloadConfig,
|
||||
props: { appConfig, pageInfo, sections },
|
||||
props: config,
|
||||
meta: {
|
||||
title: pageInfo.title || 'Download Dashy Config',
|
||||
title: config.pageInfo.title || 'Download Dashy Config',
|
||||
metaTags: metaTagData,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -3,73 +3,78 @@
|
|||
* Also ensures that any missing attributes are populated with defaults, and the
|
||||
* object is structurally sound, to avoid any error if the user is missing something
|
||||
* The main config object is made up of three parts: appConfig, pageInfo and sections
|
||||
* For anything other than these three sections, please see @utils/ConfigHelpers.js
|
||||
*/
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import {
|
||||
localStorageKeys,
|
||||
appConfig as defaultAppConfig,
|
||||
pageInfo as defaultPageInfo,
|
||||
iconSize as defaultIconSize,
|
||||
layout as defaultLayout,
|
||||
} from '@/utils/defaults';
|
||||
|
||||
import conf from '../../public/conf.yml';
|
||||
|
||||
/**
|
||||
* Returns the appConfig section, as JSON
|
||||
*/
|
||||
export const appConfig = (() => {
|
||||
const appConfigFile = conf.appConfig || {};
|
||||
let usersAppConfig = Defaults.appConfig;
|
||||
if (localStorage[localStorageKeys.APP_CONFIG]) {
|
||||
usersAppConfig = JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
|
||||
} else if (appConfigFile !== {}) {
|
||||
usersAppConfig = appConfigFile;
|
||||
export default class ConfigAccumulator {
|
||||
constructor() {
|
||||
this.conf = conf;
|
||||
}
|
||||
usersAppConfig.layout = localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|
||||
|| appConfigFile.layout || Defaults.layout;
|
||||
usersAppConfig.iconSize = localStorage[localStorageKeys.ICON_SIZE]
|
||||
|| appConfigFile.iconSize || Defaults.iconSize;
|
||||
return usersAppConfig;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Returns the pageInfo section, as JSON
|
||||
*/
|
||||
export const pageInfo = (() => {
|
||||
const defaults = Defaults.pageInfo;
|
||||
let localPageInfo;
|
||||
try {
|
||||
localPageInfo = JSON.parse(localStorage[localStorageKeys.PAGE_INFO]);
|
||||
} catch (e) {
|
||||
localPageInfo = {};
|
||||
}
|
||||
const pi = conf.pageInfo || defaults; // The page info object to return
|
||||
pi.title = localPageInfo.title || conf.pageInfo.title || defaults.title;
|
||||
pi.description = localPageInfo.description || conf.pageInfo.description || defaults.description;
|
||||
pi.navLinks = localPageInfo.navLinks || conf.pageInfo.navLinks || defaults.navLinks;
|
||||
pi.footerText = localPageInfo.footerText || conf.pageInfo.footerText || defaults.footerText;
|
||||
return pi;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Returns the sections section, as an array of JSON objects
|
||||
*/
|
||||
export const sections = (() => {
|
||||
// If the user has stored sections in local storage, return those
|
||||
const localSections = localStorage[localStorageKeys.CONF_SECTIONS];
|
||||
if (localSections) {
|
||||
try {
|
||||
const json = JSON.parse(localSections);
|
||||
if (json.length >= 1) return json;
|
||||
} catch (e) {
|
||||
// The data in local storage has been malformed, will return conf.sections instead
|
||||
/* App Config */
|
||||
appConfig() {
|
||||
const appConfigFile = this.conf.appConfig || {};
|
||||
let usersAppConfig = defaultAppConfig;
|
||||
if (localStorage[localStorageKeys.APP_CONFIG]) {
|
||||
usersAppConfig = JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
|
||||
} else if (appConfigFile !== {}) {
|
||||
usersAppConfig = appConfigFile;
|
||||
}
|
||||
usersAppConfig.layout = localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|
||||
|| appConfigFile.layout || defaultLayout;
|
||||
usersAppConfig.iconSize = localStorage[localStorageKeys.ICON_SIZE]
|
||||
|| appConfigFile.iconSize || defaultIconSize;
|
||||
return usersAppConfig;
|
||||
}
|
||||
// If the function hasn't yet returned, then return the config file sections
|
||||
return conf.sections;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Returns the complete configuration, as JSON
|
||||
*/
|
||||
export const config = (() => {
|
||||
const result = {
|
||||
appConfig,
|
||||
pageInfo,
|
||||
sections,
|
||||
};
|
||||
return result;
|
||||
})();
|
||||
/* Page Info */
|
||||
pageInfo() {
|
||||
const defaults = defaultPageInfo;
|
||||
let localPageInfo;
|
||||
try {
|
||||
localPageInfo = JSON.parse(localStorage[localStorageKeys.PAGE_INFO]);
|
||||
} catch (e) {
|
||||
localPageInfo = {};
|
||||
}
|
||||
const pi = this.conf.pageInfo || defaults; // The page info object to return
|
||||
pi.title = localPageInfo.title || conf.pageInfo.title || defaults.title;
|
||||
pi.description = localPageInfo.description || conf.pageInfo.description || defaults.description;
|
||||
pi.navLinks = localPageInfo.navLinks || conf.pageInfo.navLinks || defaults.navLinks;
|
||||
pi.footerText = localPageInfo.footerText || conf.pageInfo.footerText || defaults.footerText;
|
||||
return pi;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
sections() {
|
||||
// If the user has stored sections in local storage, return those
|
||||
const localSections = localStorage[localStorageKeys.CONF_SECTIONS];
|
||||
if (localSections) {
|
||||
try {
|
||||
const json = JSON.parse(localSections);
|
||||
if (json.length >= 1) return json;
|
||||
} catch (e) {
|
||||
// The data in local storage has been malformed, will return conf.sections instead
|
||||
}
|
||||
}
|
||||
// If the function hasn't yet returned, then return the config file sections
|
||||
return this.conf.sections;
|
||||
}
|
||||
|
||||
/* Complete config */
|
||||
config() {
|
||||
return {
|
||||
appConfig: this.appConfig(),
|
||||
pageInfo: this.pageInfo(),
|
||||
sections: this.sections(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
|
||||
import { visibleComponents } from '@/utils/defaults';
|
||||
|
||||
/**
|
||||
* Initiates the Accumulator class and generates a complete config object
|
||||
* Self-executing function, returns the full user config as a JSON object
|
||||
*/
|
||||
export const config = (() => {
|
||||
const Accumulator = new ConfigAccumulator();
|
||||
return Accumulator.config();
|
||||
})();
|
||||
|
||||
/**
|
||||
* Generates an object containing booleans indicating which
|
||||
* components should be hidden. This enables the user to hide
|
||||
* parts of the page and disable functionality that they don't need/ want
|
||||
* All options fallback on the values defined in the defaults
|
||||
* @param {object} appConfig The full app config
|
||||
* @returns {object} result
|
||||
*/
|
||||
export const componentVisibility = (appConfig) => {
|
||||
// Get users choice from app config
|
||||
const usersChoice = appConfig.hideComponents || {};
|
||||
// Checks if value is defined, and is a boolean
|
||||
const isThere = (userValue) => typeof userValue === 'boolean';
|
||||
// For each option, return users choice (if specified), else use the default
|
||||
return {
|
||||
pageTitle: isThere(usersChoice.hideHeading)
|
||||
? !usersChoice.hideHeading : visibleComponents.pageTitle,
|
||||
navigation: isThere(usersChoice.hideNav)
|
||||
? !usersChoice.hideNav : visibleComponents.navigation,
|
||||
searchBar: isThere(usersChoice.hideSearch)
|
||||
? !usersChoice.hideSearch : visibleComponents.searchBar,
|
||||
settings: isThere(usersChoice.hideSettings)
|
||||
? !usersChoice.hideSettings : visibleComponents.settings,
|
||||
footer: isThere(usersChoice.hideFooter)
|
||||
? !usersChoice.hideFooter : visibleComponents.footer,
|
||||
splashScreen: isThere(usersChoice.hideSplashScreen)
|
||||
? !usersChoice.hideSplashScreen : visibleComponents.splashScreen,
|
||||
};
|
||||
};
|
|
@ -123,6 +123,42 @@
|
|||
"default": "medium",
|
||||
"description": "The size of each link item / icon"
|
||||
},
|
||||
"hideComponents": {
|
||||
"type": "object",
|
||||
"description": "Hide individual parts of the page. If not set, all components are visible by default",
|
||||
"properties": {
|
||||
"hideHeading": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If set to true, the page heading & subtitle will be hidden"
|
||||
},
|
||||
"hideNav": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If set to true, the navigation menu will be hidden"
|
||||
},
|
||||
"hideSearch": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If set to true, the search bar will be hidden"
|
||||
},
|
||||
"hideSettings": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If set to true, the settings buttons will be hidden"
|
||||
},
|
||||
"hideFooter": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "If set to true, the page footer will be hidden"
|
||||
},
|
||||
"hideSplashScreen": {
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "If set to true, the loading / splash screen will not be shown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cssThemes": {
|
||||
"type": "array",
|
||||
"description": "Theme names to be added to the dropdown",
|
||||
|
@ -144,11 +180,6 @@
|
|||
"type": "string",
|
||||
"description": "Any custom CSS overides, must be minified"
|
||||
},
|
||||
"showSplashScreen": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Display a loading screen when the app is launched"
|
||||
},
|
||||
"statusCheck": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
|
|
Loading…
Reference in New Issue