🔀 Merge pull request #1871 from alayham/feature/custom-list-widget

Added the custom list widget
This commit is contained in:
Alicia Sykes 2025-07-19 07:37:12 +01:00 committed by GitHub
commit b7d40c5b61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 256 additions and 0 deletions

View File

@ -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
Allows web search using multiple user-defined search engines and other websites.

View 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>

View File

@ -53,6 +53,7 @@ const COMPAT = {
'crypto-price-chart': 'CryptoPriceChart',
'crypto-watch-list': 'CryptoWatchList',
'custom-search': 'CustomSearch',
'custom-list': 'CustomList',
'cve-vulnerabilities': 'CveVulnerabilities',
'domain-monitor': 'DomainMonitor',
'code-stats': 'CodeStats',