mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-24 06:05:31 +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
|
||||
|
||||
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-watch-list': 'CryptoWatchList',
|
||||
'custom-search': 'CustomSearch',
|
||||
'custom-list': 'CustomList',
|
||||
'cve-vulnerabilities': 'CveVulnerabilities',
|
||||
'domain-monitor': 'DomainMonitor',
|
||||
'code-stats': 'CodeStats',
|
||||
|
Loading…
x
Reference in New Issue
Block a user