diff --git a/README.md b/README.md index ba1c96eb..c5111b4d 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,9 @@ [![Awesome Self-Hosted](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/awesome-selfhosted/awesome-selfhosted#personal-dashboards) ![Docker Pulls](https://img.shields.io/docker/pulls/lissy93/dashy?logo=docker&style=flat-square) ![Stars](https://flat.badgen.net/github/stars/lissy93/dashy?icon=github) -![Current Version](https://img.shields.io/github/package-json/v/lissy93/dashy?style=flat-square&logo=azurepipelines&color=00af87) ![GitHub Status](https://flat.badgen.net/github/status/lissy93/dashy?icon=github) -![App Size](https://img.shields.io/github/languages/code-size/lissy93/dashy?style=flat-square) -![Code Quality](https://app.codacy.com/project/badge/Grade/3be23a4a3a8a4689bd47745b201ecb74) +![License MIT](https://img.shields.io/badge/License-MIT-09be48?style=flat-square&logo=opensourceinitiative) +![Current Version](https://img.shields.io/github/package-json/v/lissy93/dashy?style=flat-square&logo=azurepipelines&color=00af87) ## Features 🌈 @@ -89,8 +88,6 @@ Dashy supports 1-Click deployments on several popular cloud platforms (with more - [Deploy with Vercel](https://vercel.com/new/project?template=https://github.com/lissy93/dashy) - [Deploy with PWD](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/Lissy93/dashy/master/docker-compose.yml) -**[⬆️ Back to Top](#dashy)** - #### Basic Commands The following commands can be run on Dashy. If you are using Docker, than precede each command with `docker exec -it [container-id]`, where container id can be found by running `docker ps`, e.g. `docker exec -it 92490c12baff yarn build`. @@ -106,6 +103,8 @@ If you prefer [`NPM`](https://docs.npmjs.com), then just replace `yarn` with `np - `yarn test` - Runs tests, and outputs results - `yarn install` - Install all dependencies +**[⬆️ Back to Top](#dashy)** + --- ## Configuring 🔧 @@ -200,10 +199,39 @@ At present, access control is handled on the frontend, and therefore in security > For full monitoring documentation, see: [**Status Indicators**](./docs/status-indicators.md) -Dashy has an optional feature that can display a small icon ([like this](./docs/assets/status-check-demo.gif)) next to each of your running services, indicating it's current status. This is useful if you are using Dashy as your homelab's start page, as it gives you an overview of the health of each of your running services. Hovering over the indicator will show additional information, including average response time and an error message for services which are down. +Dashy has an optional feature that can display a small icon next to each of your running services, indicating it's current status. This is useful if you are using Dashy as your homelab's start page, as it gives you an overview of the health of each of your running services. Hovering over the indicator will show additional information, including average response time and an error message for services which are down. By default, this feature is off, but you can enable it globally by setting `appConfig.statusCheck: true`, or enable/ disable it for an individual item, with `item[n].statusCheck`. You can also specify an time interval in seconds under `appConfig.statusCheckInterval`, which will determine how often to recheck services, if this value is `0`, then status is only checked on initial page load, this is default behavior. +

+ Status Checks demo +

+ +**[⬆️ Back to Top](#dashy)** + +--- + +## Opening Methods 🖱️ + +One of the primary purposes of Dashy is to make launching commonly used apps and services as quick as possible. To aid in this, there are several different options on how items can be opened. You can configure your preference by setting the `target` property of any item, to one of the following values: +- `sametab` - The app will be launched in the current tab +- `newtab` - The app will be launched in a new tab +- `modal` - Launch app in a resizable/ movable popup modal on the current page +- `workspace` - Changes to Workspace view, and launches app + +Even if the target is not set (or is set to `sametab`), you can still launch any given app in an alternative method: Alt + Click will open the modal, and Ctrl + Click will open in a new tab. You can also right-click on any item to see all options (as seen in the screenshot below). This custom context menu can be disabled by setting `appConfig.disableContextMenu: true`. + +

+ +

+ +The modal and workspace views work by rendering the target application in an iframe. For this to work, the HTTP response header [`X-Frame-Options`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) for a given application needs to be set to `ALLOW`. If you are getting a `Refused to Connect` error then this header is set to `DENY` (or `SAMEORIGIN` and it's on a different host). + +Here's a quick demo of the workspace view: +

+ Workspace view demo +

+ **[⬆️ Back to Top](#dashy)** --- @@ -240,18 +268,12 @@ Before you submit your pull request, please ensure the following: - Your pull request will need to be up-to-date with master, and the PR template must be filled in ### Repo Status -![Open Issues](https://flat.badgen.net/github/open-issues/lissy93/dashy?icon=github) -![Closed Issues](https://flat.badgen.net/github/closed-issues/lissy93/dashy?icon=github) + ![Open PRs](https://flat.badgen.net/github/open-prs/lissy93/dashy?icon=github) ![Total PRs](https://flat.badgen.net/github/prs/lissy93/dashy?icon=github) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/lissy93/dashy?style=flat-square) ![Last Commit](https://flat.badgen.net/github/last-commit/lissy93/dashy?icon=github) ![Contributors](https://flat.badgen.net/github/contributors/lissy93/dashy?icon=github) -![GitHub Status](https://flat.badgen.net/github/status/lissy93/dashy?icon=github) -![Stars](https://flat.badgen.net/github/stars/lissy93/dashy?icon=github) -![Docker Pulls](https://img.shields.io/docker/pulls/lissy93/dashy?logo=docker&style=flat-square) -![Total Lines](https://img.shields.io/tokei/lines/github/lissy93/dashy?style=flat-square) -![Maintenance](https://img.shields.io/maintenance/yes/2021?style=flat-square) **[⬆️ Back to Top](#dashy)** @@ -268,6 +290,9 @@ If you've found a bug, or something that isn't working as you'd expect, please r - [Ask a Question 🤷‍♀️](https://github.com/Lissy93/dashy/issues/new?assignees=Lissy93&labels=%F0%9F%A4%B7%E2%80%8D%E2%99%82%EF%B8%8F+Question&template=question------.md&title=%5BQUESTION%5D) - [Share Feedback 🌈](https://github.com/Lissy93/dashy/issues/new?assignees=&labels=%F0%9F%8C%88+Feedback&template=share-feedback---.md&title=%5BFEEDBACK%5D) +[**Issue Status**](https://isitmaintained.com/project/lissy93/dashy) ![Resolution Time](http://isitmaintained.com/badge/resolution/lissy93/dashy.svg) ![Open Issues](http://isitmaintained.com/badge/open/lissy93/dashy.svg) ![Closed Issues](https://badgen.net/github/closed-issues/lissy93/dashy) + + For more general questions about any of the technologies used, [StackOverflow](https://stackoverflow.com/questions/) may be more helpful first port of info If you need to get in touch securely with the author (me, Alicia Sykes), drop me a message at: diff --git a/docs/assets/workspace-demo.gif b/docs/assets/workspace-demo.gif new file mode 100644 index 00000000..c6e186ae Binary files /dev/null and b/docs/assets/workspace-demo.gif differ diff --git a/docs/configuring.md b/docs/configuring.md index 6018730f..29fec3e1 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -61,6 +61,7 @@ All fields are optional, unless otherwise stated. **`enableFontAwesome`** | `boolean` | _Optional_ | Where `true` is enabled, if left blank font-awesome will be enabled only if required by 1 or more icons **`fontAwesomeKey`** | `string` | _Optional_ | If you have a font-awesome key, then you can use it here and make use of premium icons. It is a 10-digit alpha-numeric string from you're FA kit URL (e.g. `13014ae648`) **`faviconApi`** | `string` | _Optional_ | Which service to use to resolve favicons. Set to `local` to do this locally, without using an API. Available options are: `local`, `faviconkit`, `google`, `clearbit`, `webmasterapi` and `allesedv`. Defaults to `faviconkit`. See [Icons](/docs/icons.md#favicons) for more info +**`auth`** | `array` | _Optional_ | An array of objects containing usernames and hashed passwords. If this is not provided, then authentication will be off by default, and you will not need any credentials to access the app. Note authentication is done on the client side, and so if your instance of Dashy is exposed to the internet, it is recommend to configure your web server to handle this. See [`auth`](#appconfigauth-optional) **`layout`** | `string` | _Optional_ | App layout, either `horizontal`, `vertical`, `auto` or `sidebar`. Defaults to `auto`. This specifies the layout and direction of how sections are positioned on the home screen. This can also be modified from the UI. **`iconSize`** | `string` | _Optional_ | The size of link items / icons. Can be either `small`, `medium,` or `large`. Defaults to `medium`. This can also be set directly from the UI. **`theme`** | `string` | _Optional_ | The default theme for first load (you can change this later from the UI) @@ -68,9 +69,9 @@ All fields are optional, unless otherwise stated. **`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 -**`auth`** | `array` | _Optional_ | An array of objects containing usernames and hashed passwords. If this is not provided, then authentication will be off by default, and you will not need any credentials to access the app. Note authentication is done on the client side, and so if your instance of Dashy is exposed to the internet, it is recommend to configure your web server to handle this. See [`auth`](#appconfigauth-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`. **[⬆️ Back to Top](#configuring)** @@ -103,7 +104,7 @@ All fields are optional, unless otherwise stated. **`description`** | `string` | _Optional_ | Additional info about an item, which is shown in the tooltip on hover, or visible on large tiles **`url`** | `string` | Required | The URL / location of web address for when the item is clicked **`icon`** | `string` | _Optional_ | The icon for a given item. Can be a font-awesome icon, favicon, remote URL or local URL. See [`item.icon`](#sectionicon-and-sectionitemicon) -**`target`** | `string` | _Optional_ | The opening method for when the item is clicked, either `newtab`, `sametab` or `iframe`. Where `newtab` will open the link in a new tab, `sametab` will open it in the current tab, and `iframe` will open a pop-up modal with the content displayed within that iframe. Note that for the iframe to load, you must have set the CORS headers to either allow `*` ot allow the domain that you are hosting Dashy on, for some websites and self-hosted services, this is already set. +**`target`** | `string` | _Optional_ | The opening method for when the item is clicked, either `newtab`, `sametab`, `modal` or `workspace`. Where `newtab` will open the link in a new tab, `sametab` will open it in the current tab, and `modal` will open a pop-up modal with the content displayed within that iframe. Note that for the iframe to load, you must have set the CORS headers to either allow `*` ot allow the domain that you are hosting Dashy on, for some websites and self-hosted services, this is already set. **`statusCheck`** | `boolean` | _Optional_ | When set to `true`, Dashy will ping the URL associated with the current service, and display its status as a dot next to the item. The value here will override `appConfig.statusCheck` so you can turn off or on checks for a given service. Defaults to `appConfig.statusCheck`, falls back to `false` **`color`** | `string` | _Optional_ | An optional color for the text and font-awesome icon to be displayed in. Note that this will override the current theme and so may not display well **`backgroundColor`** | `string` | _Optional_ | An optional background fill color for the that given item. Again, this will override the current theme and so might not display well against the background diff --git a/docs/theming.md b/docs/theming.md index dcd8721b..b70ef628 100644 --- a/docs/theming.md +++ b/docs/theming.md @@ -120,8 +120,9 @@ You can target specific elements on the UI with these variables. All are optiona - `--status-check-tooltip-color` - Text color for the status check tooltips. Defaults to `--primary` - `--code-editor-color` - Text color used within raw code editors. Defaults to `--black` - `--code-editor-background` - Background color for raw code editors. Defaults to `--white` - - +- `--context-menu-color` - Text color for right-click context menu over items. Defaults to `--primary` +- `--context-menu-background` - Background color of right-click context menu. Defaults to `--background` +- `--context-menu-secondary-color` - Border and outline color for context menu. Defaults to `--background-darker` #### Non-Color Variables - `--outline-color` - Used to outline focused or selected elements diff --git a/package.json b/package.json index a324a147..1a75858a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Dashy", - "version": "1.2.5", + "version": "1.3.5", "license": "MIT", "main": "server", "scripts": { @@ -86,4 +86,4 @@ "> 1%", "last 2 versions" ] -} +} \ No newline at end of file diff --git a/src/assets/interface-icons/open-workspace.svg b/src/assets/interface-icons/open-workspace.svg new file mode 100644 index 00000000..b0a186ca --- /dev/null +++ b/src/assets/interface-icons/open-workspace.svg @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/src/components/LinkItems/Collapsable.vue b/src/components/LinkItems/Collapsable.vue index 0e6dd567..d33db09b 100644 --- a/src/components/LinkItems/Collapsable.vue +++ b/src/components/LinkItems/Collapsable.vue @@ -42,11 +42,6 @@ export default { components: { Icon, }, - data() { - return { - isOpen: !this.collapsed, - }; - }, methods: { /* Check that row & column span is valid, and not over the max */ checkSpanNum(span, classPrefix) { diff --git a/src/components/LinkItems/ContextMenu.vue b/src/components/LinkItems/ContextMenu.vue new file mode 100644 index 00000000..3e8de908 --- /dev/null +++ b/src/components/LinkItems/ContextMenu.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/src/components/LinkItems/Item.vue b/src/components/LinkItems/Item.vue index f69bcad6..6bc9ba40 100644 --- a/src/components/LinkItems/Item.vue +++ b/src/components/LinkItems/Item.vue @@ -1,6 +1,9 @@ diff --git a/src/main.js b/src/main.js index c4e7361c..ca08f56d 100644 --- a/src/main.js +++ b/src/main.js @@ -7,16 +7,18 @@ 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 Dashy from './App.vue'; -import router from './router'; -import registerServiceWorker from './registerServiceWorker'; +import { toastedOptions } from '@/utils/defaults'; +import Dashy from '@/App.vue'; +import router from '@/router'; +import registerServiceWorker from '@/registerServiceWorker'; +import clickOutside from '@/utils/ClickOutside'; Vue.use(VTooltip); Vue.use(VModal); Vue.use(VTabs); Vue.use(Toasted, toastedOptions); Vue.component('v-select', VSelect); +Vue.directive('clickOutside', clickOutside); Vue.config.productionTip = false; diff --git a/src/styles/color-palette.scss b/src/styles/color-palette.scss index 2acf6a25..bb90b2d2 100644 --- a/src/styles/color-palette.scss +++ b/src/styles/color-palette.scss @@ -78,4 +78,8 @@ --status-check-tooltip-color: var(--primary); --code-editor-color: var(--black); --code-editor-background: var(--white); + + --context-menu-background: var(--background); + --context-menu-color: var(--primary); + --context-menu-secondary-color: var(--background-darker); } diff --git a/src/styles/color-themes.scss b/src/styles/color-themes.scss index d01128b0..350dca44 100644 --- a/src/styles/color-themes.scss +++ b/src/styles/color-themes.scss @@ -88,9 +88,13 @@ html[data-theme='matrix'] { --font-body: 'Cutive Mono', monospace; --font-headings: 'VT323', monospace; --about-page-background: var(--background); + --context-menu-secondary-color: var(--primary); .prism-editor-wrapper.my-editor { border: 1px solid var(--primary); } + div.context-menu ul li:hover { + color: var(--background); + } } html[data-theme='hacker-girl'] { @@ -183,6 +187,12 @@ html[data-theme='material-original'] { --about-page-accent: #000; --about-page-color: var(--background-darker); --about-page-background: var(--background); + --context-menu-background: var(--white); + --context-menu-secondary-color: var(--white); + div.context-menu ul li:hover { + background: var(--primary); + color: var(--white); + } } html[data-theme='material-dark-original'] { @@ -221,6 +231,13 @@ html[data-theme='material-dark-original'] { &::-webkit-scrollbar-thumb { border-left: 1px solid #131a1f; } + div.context-menu { + border: none; + background: #131a1f; + ul li:hover { + background: #333c43; + } + } } html[data-theme='colorful'] { @@ -233,14 +250,14 @@ html[data-theme='colorful'] { --item-group-outer-background: #05070e; --item-group-heading-text-color: #e8eae1; --item-group-heading-text-color-hover: #fff; - .item:nth-child(1n) { color: #eb5cad; border: 1px solid #eb5cad; } - .item:nth-child(2n) { color: #985ceb; border: 1px solid #985ceb; } - .item:nth-child(3n) { color: #5c90eb; border: 1px solid #5c90eb; } - .item:nth-child(4n) { color: #5cdfeb; border: 1px solid #5cdfeb; } - .item:nth-child(5n) { color: #5ceb8d; border: 1px solid #5ceb8d; } - .item:nth-child(6n) { color: #afeb5c; border: 1px solid #afeb5c; } - .item:nth-child(7n) { color: #ebb75c; border: 1px solid #ebb75c; } - .item:nth-child(8n) { color: #eb615c; border: 1px solid #eb615c; } + .item-wrapper:nth-child(1n) { .item { color: #eb5cad; border: 1px solid #eb5cad; } } + .item-wrapper:nth-child(2n) { .item { color: #985ceb; border: 1px solid #985ceb; } } + .item-wrapper:nth-child(3n) { .item { color: #5c90eb; border: 1px solid #5c90eb; } } + .item-wrapper:nth-child(4n) { .item { color: #5cdfeb; border: 1px solid #5cdfeb; } } + .item-wrapper:nth-child(5n) { .item { color: #5ceb8d; border: 1px solid #5ceb8d; } } + .item-wrapper:nth-child(6n) { .item { color: #afeb5c; border: 1px solid #afeb5c; } } + .item-wrapper:nth-child(7n) { .item { color: #ebb75c; border: 1px solid #ebb75c; } } + .item-wrapper:nth-child(8n) { .item { color: #eb615c; border: 1px solid #eb615c; } } .item:hover, .item:focus { opacity: 0.85; outline: none; @@ -252,12 +269,20 @@ html[data-theme='colorful'] { h1, h2, h3, h4 { font-weight: normal; } + div.context-menu { + border-color: var(--primary); + } } html[data-theme='minimal-light'], html[data-theme='minimal-dark'], html[data-theme='vaporware'] { --font-body: 'PTMono-Regular', 'Courier New', monospace; --font-headings: 'PTMono-Regular', 'Courier New', monospace; + --footer-height: 94px; + .item.size-medium .tile-title { + max-width: 100px; + } + label.lbl-toggle h3 { font-size: 1.8rem; } @@ -448,6 +473,7 @@ html[data-theme='material'] { --welcome-popup-text-color: #f5f5f5; --footer-text-color: #f5f5f5cc; // --login-form-background-secondary: #f5f5f5cc; + --context-menu-secondary-color: #f5f5f5; header { background: #4285f4; @@ -466,6 +492,14 @@ html[data-theme='material'] { .prism-editor-wrapper { background: #f5f5f5; } + .item:focus { + outline-color: #4285f4cc; + } + div.context-menu { + border: none; + background: var(--white); + ul li:hover { svg path { fill: var(--background-darker); }} + } } html[data-theme='material-dark'] { @@ -520,6 +554,13 @@ html[data-theme='material-dark'] { background: #131a1f !important; } } + div.context-menu { + border: none; + background: var(--background); + ul li:hover { + background: #131a1f; + } + } } html[data-theme='minimal-light'] { @@ -546,7 +587,8 @@ html[data-theme='minimal-light'] { --login-form-color: #101931; --about-page-background: var(--background); --about-page-color: var(--background-darker); - + --context-menu-color: var(--background-darker); + --context-menu-secondary-color: var(--primary); section.filter-container { background: #fff; border-bottom: 1px dashed #00000038; @@ -591,6 +633,10 @@ html[data-theme='minimal-dark'] { border: 1px solid #fff; } } + + div.context-menu { + border-color: var(--primary); + } } html[data-theme='vaporware'] { diff --git a/src/styles/dimensions.scss b/src/styles/dimensions.scss index 13eed42a..442cfb7d 100644 --- a/src/styles/dimensions.scss +++ b/src/styles/dimensions.scss @@ -19,6 +19,7 @@ --item-icon-transform: drop-shadow(2px 4px 6px var(--transparent-50)) saturate(0.65); --item-icon-transform-hover: drop-shadow(4px 8px 3px var(--transparent-50)) saturate(2); --item-group-shadow: var(--item-shadow); + --context-menu-shadow: var(--item-shadow); /* Settings and config menu */ --settings-container-shadow: none; diff --git a/src/utils/ClickOutside.js b/src/utils/ClickOutside.js new file mode 100644 index 00000000..83bb4bbc --- /dev/null +++ b/src/utils/ClickOutside.js @@ -0,0 +1,37 @@ +/** + * A simple Vue directive to trigger an event when the user + * clicks anywhere other than the specified element. + * Used to close context menu's popup menus and tips. + */ + +const instances = []; + +function onDocumentClick(e, el, fn) { + const { target } = e; + if (el !== target && !el.contains(target)) { + fn(e); + } +} + +export default { + bind(element, binding) { + const el = element; + el.dataset.outsideClickIndex = instances.length; + + const fn = binding.value; + const click = (e) => { + onDocumentClick(e, el, fn); + }; + + document.addEventListener('click', click); + document.addEventListener('touchstart', click); + instances.push(click); + }, + unbind(el) { + if (!el.dataset) return; + const index = el.dataset.outsideClickIndex; + const handler = instances[index]; + document.removeEventListener('click', handler); + instances.splice(index, 1); + }, +}; diff --git a/src/utils/ConfigSchema.json b/src/utils/ConfigSchema.json index 1416d2c9..8535ab16 100644 --- a/src/utils/ConfigSchema.json +++ b/src/utils/ConfigSchema.json @@ -176,7 +176,12 @@ "disableServiceWorker": { "type": "boolean", "default": false, - "description": "If set to true, then service worker will not be used" + "description": "If set to true, then service workers will not be used to cache page contents" + }, + "disableContextMenu": { + "type": "boolean", + "default": false, + "description": "If set to true, custom right-click context menu will be disabled" } }, "additionalProperties": false @@ -295,7 +300,8 @@ "enum": [ "newtab", "sametab", - "iframe" + "modal", + "workspace" ], "default": "newtab", "description": "Opening method, when item is clicked"