Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Show hide components #78

Merged
merged 7 commits into from
Jul 5, 2021
15 changes: 14 additions & 1 deletion docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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**
Expand Down
24 changes: 18 additions & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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: {
Expand All @@ -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()) {
Expand Down
8 changes: 4 additions & 4 deletions src/components/PageStrcture/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
};
},
};
Expand Down
10 changes: 7 additions & 3 deletions src/components/Settings/SettingsContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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',
Expand All @@ -65,6 +68,7 @@ export default {
IconOpen,
IconClose,
},
inject: ['visibleComponents'],
methods: {
userIsTypingSomething(something) {
this.$emit('user-is-searchin', something);
Expand Down Expand Up @@ -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,
};
},
};
Expand Down
22 changes: 9 additions & 13 deletions src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
};

Expand All @@ -22,23 +22,19 @@ 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,
},
},
{
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,
},
},
Expand All @@ -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: '/' });
Expand All @@ -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,
},
},
Expand Down
121 changes: 63 additions & 58 deletions src/utils/ConfigAccumalator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
/* 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;
}
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) {
/* Page Info */
pageInfo() {
const defaults = defaultPageInfo;
let localPageInfo;
try {
const json = JSON.parse(localSections);
if (json.length >= 1) return json;
localPageInfo = JSON.parse(localStorage[localStorageKeys.PAGE_INFO]);
} catch (e) {
// The data in local storage has been malformed, will return conf.sections instead
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;
}
// 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;
})();
/* 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(),
};
}
}
42 changes: 42 additions & 0 deletions src/utils/ConfigHelpers.js
Original file line number Diff line number Diff line change
@@ -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,
};
};
Loading