mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-26 23:24:38 +02:00
🔀 Merge pull request #1871 from alayham/feature/custom-list-widget
Added the custom list widget
This commit is contained in:
commit
b7d40c5b61
149
docs/widgets.md
149
docs/widgets.md
@ -1298,6 +1298,155 @@ In other words: Private, noncomercial, moderate use of the API is tolerated. The
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Custom List
|
||||||
|
|
||||||
|
Renders custom schema-compliant JOSN in a list.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
**Field** | **Type** | **Required** | **Description**
|
||||||
|
--- | --- | --- | ---
|
||||||
|
**`url`** | `text` | Required | A string containing the url of a json file.
|
||||||
|
**`title`** | `text` | optional | A title for the widget. Can be helpful if stacking multiple lists in the same section.
|
||||||
|
**`daysForNew`** | `number` | Optional | Used to highlight new items.
|
||||||
|
|
||||||
|
#### Json Schema
|
||||||
|
The input file should comply with the following schema:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"link": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text",
|
||||||
|
"url",
|
||||||
|
"title"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text",
|
||||||
|
"title"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"link",
|
||||||
|
"value",
|
||||||
|
"date"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: This json data was generated by a data worflow that gets the new releases of a few projects from GitHub. The system used to build the data workflow is n8n.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"link": {
|
||||||
|
"text": "jellyfin/jellyfin",
|
||||||
|
"url": "https://github.com/jellyfin/jellyfin/releases/tag/v10.10.7",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"text": "v10.10.7",
|
||||||
|
"title": "2025-04-05"
|
||||||
|
},
|
||||||
|
"date": "2025-04-05T19:14:59Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": {
|
||||||
|
"text": "jellyfin/jellyfin-web",
|
||||||
|
"url": "https://github.com/jellyfin/jellyfin-web/releases/tag/v10.10.7",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"text": "v10.10.7",
|
||||||
|
"title": "2025-04-05"
|
||||||
|
},
|
||||||
|
"date": "2025-04-05T19:15:00Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": {
|
||||||
|
"text": "lissy93/dashy",
|
||||||
|
"url": "https://github.com/Lissy93/dashy/releases/tag/3.1.1",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"text": "3.1.1",
|
||||||
|
"title": "2024-05-30"
|
||||||
|
},
|
||||||
|
"date": "2024-05-30T17:20:53Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": {
|
||||||
|
"text": "VSCodium/vscodium",
|
||||||
|
"url": "https://github.com/VSCodium/vscodium/releases/tag/1.102.14746",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"text": "1.102.14746",
|
||||||
|
"title": "2025-07-16"
|
||||||
|
},
|
||||||
|
"date": "2025-07-16T18:27:49Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
#### Notes
|
||||||
|
- This widget is designed to render data generated by another system that complies with the schema. The example JSON data above was generated using a n8n workflow, and other ETL or workflow systems can generate similar results.
|
||||||
|
- To avoid requests to a different system in each refresh, you can save the input files locally in the user-data folder inside your Dashy installation.
|
||||||
|
- To use json files from a different domain, remember to add `useProxy: true` to the widget configuration. I have not tested this use case because I save all my input data locally on the Dashy server. Please open a ticket if you have an issue in this use case.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
This widget renders a json file that from a `json-data` directory inside the `user-data` directory on the Dashy server.
|
||||||
|
```yaml
|
||||||
|
- type: custom-list
|
||||||
|
options:
|
||||||
|
url: /json-data/github-releases.json
|
||||||
|
title: 'Github Releases'
|
||||||
|
daysForNew: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Info
|
||||||
|
|
||||||
|
- **CORS**: 🟢 Not needed for files hosted inside the `user-data` directory. Use `useProxy: true` to bypass CORS restrictions when using data from a different server.
|
||||||
|
- **Auth**: 🟢 Not Required
|
||||||
|
- **Price**: 🟢 Free
|
||||||
|
- **Host**: user defined
|
||||||
|
- **Privacy**: depends on the user defined host.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Custom search
|
### Custom search
|
||||||
|
|
||||||
Allows web search using multiple user-defined search engines and other websites.
|
Allows web search using multiple user-defined search engines and other websites.
|
||||||
|
106
src/components/Widgets/CustomList.vue
Normal file
106
src/components/Widgets/CustomList.vue
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<div class="custom-list">
|
||||||
|
<div class="custom-list-title" v-if="title">
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
<div v-for="(item, key) in data" :key="key" class="custom-list-row">
|
||||||
|
<div v-if="item.link" class="custom-list-cell">
|
||||||
|
<a :href="item.link.url" :title="item.link.title" target="_blank">
|
||||||
|
{{ item.link.text }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.value" class="custom-list-cell" :title="item.value.title">
|
||||||
|
{{ item.value.text }}
|
||||||
|
<span v-if="item.isNew" class="custom-list-new-value"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [WidgetMixin],
|
||||||
|
components: {},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
url() {
|
||||||
|
return this.options.url || '';
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
return this.options.title || '';
|
||||||
|
},
|
||||||
|
daysForNew() {
|
||||||
|
return parseInt(Number(this.options.daysForNew)) || false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
if (this.url) {
|
||||||
|
this.startLoading();
|
||||||
|
this.makeRequest(this.options.url).then(this.processData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
processData(data) {
|
||||||
|
let today = new Date();
|
||||||
|
this.data = data.sort((a, b) => new Date(a.date) < new Date(b.date));
|
||||||
|
if (this.daysForNew) {
|
||||||
|
let threshold = this.daysForNew * 1000 * 60 * 60 * 24;
|
||||||
|
this.data = this.data.map((item) => {
|
||||||
|
item.isNew = (today - new Date(item.date) < threshold);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.finishLoading();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.custom-list {
|
||||||
|
.custom-list-title {
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
border: 1px solid var(--outline-color);
|
||||||
|
border-radius: var(--curve-factor);
|
||||||
|
box-shadow: var(--item-shadow);
|
||||||
|
color: var(--item-text-color);
|
||||||
|
margin: .5rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
background: var(--item-background);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
.custom-list-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: var(--widget-text-color);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
.custom-list-cell {
|
||||||
|
display: inline-block;
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--item-text-color);
|
||||||
|
}
|
||||||
|
.custom-list-new-value{
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--success);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px dashed var(--widget-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -53,6 +53,7 @@ const COMPAT = {
|
|||||||
'crypto-price-chart': 'CryptoPriceChart',
|
'crypto-price-chart': 'CryptoPriceChart',
|
||||||
'crypto-watch-list': 'CryptoWatchList',
|
'crypto-watch-list': 'CryptoWatchList',
|
||||||
'custom-search': 'CustomSearch',
|
'custom-search': 'CustomSearch',
|
||||||
|
'custom-list': 'CustomList',
|
||||||
'cve-vulnerabilities': 'CveVulnerabilities',
|
'cve-vulnerabilities': 'CveVulnerabilities',
|
||||||
'domain-monitor': 'DomainMonitor',
|
'domain-monitor': 'DomainMonitor',
|
||||||
'code-stats': 'CodeStats',
|
'code-stats': 'CodeStats',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user