mirror of https://github.com/Lissy93/dashy.git
✨ Adds refresh button to widget, for reloading data
This commit is contained in:
parent
ae8179ecd7
commit
2075cbc222
|
@ -0,0 +1 @@
|
||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="sync" class="svg-inline--fa fa-sync fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M500 8h-27.711c-6.739 0-12.157 5.548-11.997 12.286l2.347 98.575C418.212 52.043 342.256 8 256 8 134.813 8 33.933 94.924 12.296 209.824 10.908 217.193 16.604 224 24.103 224h28.576c5.674 0 10.542-3.982 11.737-9.529C83.441 126.128 161.917 60 256 60c79.545 0 147.942 47.282 178.676 115.302l-126.39-3.009c-6.737-.16-12.286 5.257-12.286 11.997V212c0 6.627 5.373 12 12 12h192c6.627 0 12-5.373 12-12V20c0-6.627-5.373-12-12-12zm-12.103 280h-28.576c-5.674 0-10.542 3.982-11.737 9.529C428.559 385.872 350.083 452 256 452c-79.546 0-147.942-47.282-178.676-115.302l126.39 3.009c6.737.16 12.286-5.257 12.286-11.997V300c0-6.627-5.373-12-12-12H12c-6.627 0-12 5.373-12 12v192c0 6.627 5.373 12 12 12h27.711c6.739 0 12.157-5.548 11.997-12.286l-2.347-98.575C93.788 459.957 169.744 504 256 504c121.187 0 222.067-86.924 243.704-201.824 1.388-7.369-4.308-14.176-11.807-14.176z"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -12,11 +12,11 @@
|
||||||
@openContextMenu="openContextMenu"
|
@openContextMenu="openContextMenu"
|
||||||
>
|
>
|
||||||
<!-- If no items, show message -->
|
<!-- If no items, show message -->
|
||||||
<div v-if="(!items || items.length < 1) && !isEditMode" class="no-items">
|
<div v-if="sectionType === 'empty'" class="no-items">
|
||||||
{{ $t('home.no-items-section') }}
|
{{ $t('home.no-items-section') }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Item Container -->
|
<!-- Item Container -->
|
||||||
<div v-else
|
<div v-else-if="sectionType === 'item'"
|
||||||
:class="`there-are-items ${isGridLayout? 'item-group-grid': ''} inner-size-${itemSize}`"
|
:class="`there-are-items ${isGridLayout? 'item-group-grid': ''} inner-size-${itemSize}`"
|
||||||
:style="gridStyle" :id="`section-${groupId}`"
|
:style="gridStyle" :id="`section-${groupId}`"
|
||||||
> <!-- Show for each item -->
|
> <!-- Show for each item -->
|
||||||
|
@ -57,6 +57,14 @@
|
||||||
:itemSize="itemSize"
|
:itemSize="itemSize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="sectionType === 'widget'">
|
||||||
|
<WidgetBase
|
||||||
|
v-for="(widget, widgetIndx) in widgets"
|
||||||
|
:key="widgetIndx"
|
||||||
|
:widget="widget"
|
||||||
|
:index="index"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<!-- Modal for opening in modal view -->
|
<!-- Modal for opening in modal view -->
|
||||||
<IframeModal
|
<IframeModal
|
||||||
:ref="`iframeModal-${groupId}`"
|
:ref="`iframeModal-${groupId}`"
|
||||||
|
@ -87,6 +95,7 @@
|
||||||
<script>
|
<script>
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import Item from '@/components/LinkItems/Item.vue';
|
import Item from '@/components/LinkItems/Item.vue';
|
||||||
|
import WidgetBase from '@/components/Widgets/WidgetBase';
|
||||||
import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
||||||
import IframeModal from '@/components/LinkItems/IframeModal.vue';
|
import IframeModal from '@/components/LinkItems/IframeModal.vue';
|
||||||
import EditSection from '@/components/InteractiveEditor/EditSection.vue';
|
import EditSection from '@/components/InteractiveEditor/EditSection.vue';
|
||||||
|
@ -107,12 +116,14 @@ export default {
|
||||||
icon: String,
|
icon: String,
|
||||||
displayData: Object,
|
displayData: Object,
|
||||||
items: Array,
|
items: Array,
|
||||||
|
widgets: Array,
|
||||||
index: Number,
|
index: Number,
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Collapsable,
|
Collapsable,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
Item,
|
Item,
|
||||||
|
WidgetBase,
|
||||||
IframeModal,
|
IframeModal,
|
||||||
EditSection,
|
EditSection,
|
||||||
},
|
},
|
||||||
|
@ -139,6 +150,12 @@ export default {
|
||||||
sortOrder() {
|
sortOrder() {
|
||||||
return this.displayData.sortBy || defaultSortOrder;
|
return this.displayData.sortBy || defaultSortOrder;
|
||||||
},
|
},
|
||||||
|
/* A section can contain either items or widgets */
|
||||||
|
sectionType() {
|
||||||
|
if (this.widgets && this.widgets.length > 0) return 'widget';
|
||||||
|
if (this.items && this.items.length > 0) return 'item';
|
||||||
|
return 'empty';
|
||||||
|
},
|
||||||
/* If the sortBy attribute is specified, then return sorted data */
|
/* If the sortBy attribute is specified, then return sorted data */
|
||||||
sortedItems() {
|
sortedItems() {
|
||||||
let { items } = this;
|
let { items } = this;
|
||||||
|
|
|
@ -39,8 +39,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
update() {
|
||||||
|
this.setTime();
|
||||||
|
this.setDate();
|
||||||
|
},
|
||||||
/* Get and format the current time */
|
/* Get and format the current time */
|
||||||
updateTime() {
|
setTime() {
|
||||||
this.time = Intl.DateTimeFormat(this.timeFormat, {
|
this.time = Intl.DateTimeFormat(this.timeFormat, {
|
||||||
timeZone: this.timeZone,
|
timeZone: this.timeZone,
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
|
@ -49,7 +53,7 @@ export default {
|
||||||
}).format();
|
}).format();
|
||||||
},
|
},
|
||||||
/* Get and format the date */
|
/* Get and format the date */
|
||||||
updateDate() {
|
setDate() {
|
||||||
this.date = new Date().toLocaleDateString(this.timeFormat, {
|
this.date = new Date().toLocaleDateString(this.timeFormat, {
|
||||||
weekday: 'long', day: 'numeric', year: 'numeric', month: 'short',
|
weekday: 'long', day: 'numeric', year: 'numeric', month: 'short',
|
||||||
});
|
});
|
||||||
|
@ -57,14 +61,13 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
// Set initial date and time
|
// Set initial date and time
|
||||||
this.updateTime();
|
this.update();
|
||||||
this.updateDate();
|
|
||||||
// Update the date every hour, and the time each second
|
// Update the date every hour, and the time each second
|
||||||
this.timeUpdateInterval = setInterval(() => {
|
this.timeUpdateInterval = setInterval(() => {
|
||||||
this.updateTime();
|
this.setTime();
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (now.getMinutes() === 0 && now.getSeconds() === 0) {
|
if (now.getMinutes() === 0 && now.getSeconds() === 0) {
|
||||||
this.updateDate();
|
this.setDate();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,6 +65,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Create new chart, using the crypto data */
|
/* Create new chart, using the crypto data */
|
||||||
generateChart() {
|
generateChart() {
|
||||||
return new Chart(`#${this.chartId}`, {
|
return new Chart(`#${this.chartId}`, {
|
||||||
|
|
|
@ -75,6 +75,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Make GET request to CoinGecko API endpoint */
|
/* Make GET request to CoinGecko API endpoint */
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios.get(this.endpoint)
|
axios.get(this.endpoint)
|
||||||
|
|
|
@ -52,6 +52,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Make GET request to CoinGecko API endpoint */
|
/* Make GET request to CoinGecko API endpoint */
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios.get(this.endpoint)
|
axios.get(this.endpoint)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="iframe-widget">
|
<div class="iframe-widget">
|
||||||
<iframe v-if="frameUrl" :src="frameUrl" />
|
<iframe v-if="frameUrl" :src="frameUrl" :id="frameId" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import ErrorHandler from '@/utils/ErrorHandler';
|
||||||
export default {
|
export default {
|
||||||
mixins: [WidgetMixin],
|
mixins: [WidgetMixin],
|
||||||
computed: {
|
computed: {
|
||||||
|
/* Gets users specified URL to load into the iframe */
|
||||||
frameUrl() {
|
frameUrl() {
|
||||||
const usersChoice = this.options.url;
|
const usersChoice = this.options.url;
|
||||||
if (!usersChoice || typeof usersChoice !== 'string') {
|
if (!usersChoice || typeof usersChoice !== 'string') {
|
||||||
|
@ -19,6 +20,16 @@ export default {
|
||||||
}
|
}
|
||||||
return usersChoice;
|
return usersChoice;
|
||||||
},
|
},
|
||||||
|
/* Generates an ID for the iframe */
|
||||||
|
frameId() {
|
||||||
|
return `iframe-${btoa(this.frameUrl || 'empty').substring(0, 16)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/* Refreshes iframe contents, called by parent */
|
||||||
|
update() {
|
||||||
|
(document.getElementById(this.frameId) || {}).src = this.frameUrl;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -54,6 +54,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Make GET request to Jokes API endpoint */
|
/* Make GET request to Jokes API endpoint */
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios.get(this.endpoint)
|
axios.get(this.endpoint)
|
||||||
|
|
|
@ -72,6 +72,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Create new chart, using the crypto data */
|
/* Create new chart, using the crypto data */
|
||||||
generateChart() {
|
generateChart() {
|
||||||
return new Chart(`#${this.chartId}`, {
|
return new Chart(`#${this.chartId}`, {
|
||||||
|
|
|
@ -50,6 +50,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Makes GET request to the TFL API */
|
/* Makes GET request to the TFL API */
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios.get(widgetApiEndpoints.tflStatus)
|
axios.get(widgetApiEndpoints.tflStatus)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="weather">
|
<LoadingAnimation v-if="loading" class="loader" />
|
||||||
|
<div v-else class="weather">
|
||||||
<!-- Icon + Temperature -->
|
<!-- Icon + Temperature -->
|
||||||
<div class="intro">
|
<div class="intro">
|
||||||
<p class="temp">{{ temp }}</p>
|
<p class="temp">{{ temp }}</p>
|
||||||
|
@ -65,6 +66,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchWeather();
|
||||||
|
},
|
||||||
/* Adds units symbol to temperature, depending on metric or imperial */
|
/* Adds units symbol to temperature, depending on metric or imperial */
|
||||||
processTemp(temp) {
|
processTemp(temp) {
|
||||||
return `${Math.round(temp)}${this.tempDisplayUnits}`;
|
return `${Math.round(temp)}${this.tempDisplayUnits}`;
|
||||||
|
@ -73,6 +78,7 @@ export default {
|
||||||
fetchWeather() {
|
fetchWeather() {
|
||||||
axios.get(this.endpoint)
|
axios.get(this.endpoint)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
this.loading = false;
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
this.icon = data.weather[0].icon;
|
this.icon = data.weather[0].icon;
|
||||||
this.description = data.weather[0].description;
|
this.description = data.weather[0].description;
|
||||||
|
@ -141,6 +147,10 @@ export default {
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/weather-icons.scss';
|
@import '@/styles/weather-icons.scss';
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
p {
|
p {
|
||||||
color: var(--widget-text-color);
|
color: var(--widget-text-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchWeather();
|
||||||
|
},
|
||||||
/* Adds units symbol to temperature, depending on metric or imperial */
|
/* Adds units symbol to temperature, depending on metric or imperial */
|
||||||
processTemp(temp) {
|
processTemp(temp) {
|
||||||
return `${Math.round(temp)}${this.tempDisplayUnits}`;
|
return `${Math.round(temp)}${this.tempDisplayUnits}`;
|
||||||
|
|
|
@ -1,19 +1,71 @@
|
||||||
<template>
|
<template>
|
||||||
<Clock v-if="widgetType === 'clock'" :options="widgetOptions" />
|
<div class="widget-base">
|
||||||
<Weather v-else-if="widgetType === 'weather'" :options="widgetOptions" />
|
<Button :click="update" class="update-btn">
|
||||||
<WeatherForecast v-else-if="widgetType === 'weather-forecast'" :options="widgetOptions" />
|
<UpdateIcon />
|
||||||
<TflStatus v-else-if="widgetType === 'tfl-status'" :options="widgetOptions" />
|
</Button>
|
||||||
<CryptoPriceChart v-else-if="widgetType === 'crypto-price-chart'" :options="widgetOptions" />
|
<Clock
|
||||||
<CryptoWatchList v-else-if="widgetType === 'crypto-watch-list'" :options="widgetOptions" />
|
v-if="widgetType === 'clock'"
|
||||||
<XkcdComic v-else-if="widgetType === 'xkcd-comic'" :options="widgetOptions" />
|
:options="widgetOptions"
|
||||||
<ExchangeRates v-else-if="widgetType === 'exchange-rates'" :options="widgetOptions" />
|
:ref="widgetRef"
|
||||||
<StockPriceChart v-else-if="widgetType === 'stock-price-chart'" :options="widgetOptions" />
|
/>
|
||||||
<Jokes v-else-if="widgetType === 'joke'" :options="widgetOptions" />
|
<Weather
|
||||||
<IframeWidget v-else-if="widgetType === 'iframe'" :options="widgetOptions" />
|
v-else-if="widgetType === 'weather'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<WeatherForecast
|
||||||
|
v-else-if="widgetType === 'weather-forecast'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<TflStatus
|
||||||
|
v-else-if="widgetType === 'tfl-status'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<CryptoPriceChart
|
||||||
|
v-else-if="widgetType === 'crypto-price-chart'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<CryptoWatchList
|
||||||
|
v-else-if="widgetType === 'crypto-watch-list'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<XkcdComic
|
||||||
|
v-else-if="widgetType === 'xkcd-comic'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<ExchangeRates
|
||||||
|
v-else-if="widgetType === 'exchange-rates'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<StockPriceChart
|
||||||
|
v-else-if="widgetType === 'stock-price-chart'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<Jokes
|
||||||
|
v-else-if="widgetType === 'joke'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
<IframeWidget
|
||||||
|
v-else-if="widgetType === 'iframe'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ErrorHandler from '@/utils/ErrorHandler';
|
import ErrorHandler from '@/utils/ErrorHandler';
|
||||||
|
import Button from '@/components/FormElements/Button';
|
||||||
|
import UpdateIcon from '@/assets/interface-icons/widget-update.svg';
|
||||||
|
|
||||||
import Clock from '@/components/Widgets/Clock.vue';
|
import Clock from '@/components/Widgets/Clock.vue';
|
||||||
import Weather from '@/components/Widgets/Weather.vue';
|
import Weather from '@/components/Widgets/Weather.vue';
|
||||||
import WeatherForecast from '@/components/Widgets/WeatherForecast.vue';
|
import WeatherForecast from '@/components/Widgets/WeatherForecast.vue';
|
||||||
|
@ -29,6 +81,8 @@ import IframeWidget from '@/components/Widgets/IframeWidget.vue';
|
||||||
export default {
|
export default {
|
||||||
name: 'Widget',
|
name: 'Widget',
|
||||||
components: {
|
components: {
|
||||||
|
Button,
|
||||||
|
UpdateIcon,
|
||||||
Clock,
|
Clock,
|
||||||
Weather,
|
Weather,
|
||||||
WeatherForecast,
|
WeatherForecast,
|
||||||
|
@ -46,9 +100,7 @@ export default {
|
||||||
index: Number,
|
index: Number,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
groupId() {
|
/* Returns the widget type, shows error if not specified */
|
||||||
return `widget-${this.index}`;
|
|
||||||
},
|
|
||||||
widgetType() {
|
widgetType() {
|
||||||
if (!this.widget.type) {
|
if (!this.widget.type) {
|
||||||
ErrorHandler('Missing type attribute for widget');
|
ErrorHandler('Missing type attribute for widget');
|
||||||
|
@ -56,14 +108,44 @@ export default {
|
||||||
}
|
}
|
||||||
return this.widget.type.toLowerCase();
|
return this.widget.type.toLowerCase();
|
||||||
},
|
},
|
||||||
|
/* Returns the users specified widget options, or empty object */
|
||||||
widgetOptions() {
|
widgetOptions() {
|
||||||
return this.widget.options || {};
|
return this.widget.options || {};
|
||||||
},
|
},
|
||||||
|
widgetRef() {
|
||||||
|
return `widget-${this.widgetType}-${this.index}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
update() {
|
||||||
|
this.$refs[this.widgetRef].update();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/media-queries.scss';
|
@import '@/styles/media-queries.scss';
|
||||||
|
.widget-base {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
button.update-btn {
|
||||||
|
height: 1.5rem;
|
||||||
|
min-width: auto;
|
||||||
|
width: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.1rem 0;
|
||||||
|
position: absolute;
|
||||||
|
right: -0.25rem;
|
||||||
|
top: -0.25rem;
|
||||||
|
border: none;
|
||||||
|
opacity: var(--dimming-factor);
|
||||||
|
color: var(--widget-text-color);
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--widget-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<div class="widget-error"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WidgetError',
|
||||||
|
props: {
|
||||||
|
errorMessage: String,
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.widget-error {}
|
||||||
|
</style>
|
|
@ -40,11 +40,18 @@ export default {
|
||||||
}
|
}
|
||||||
return 'latest';
|
return 'latest';
|
||||||
},
|
},
|
||||||
|
endpoint() {
|
||||||
|
return `${widgetApiEndpoints.xkcdComic}?comic=${this.comicNumber}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* Extends mixin, and updates data. Called by parent component */
|
||||||
|
update() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
/* Make GET request to CoinGecko API endpoint */
|
/* Make GET request to CoinGecko API endpoint */
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios.get(`${widgetApiEndpoints.xkcdComic}?comic=${this.comicNumber}`)
|
axios.get(this.endpoint)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.processData(response.data);
|
this.processData(response.data);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,10 +1,31 @@
|
||||||
|
import LoadingAnimation from '@/assets/interface-icons/loader.svg';
|
||||||
|
|
||||||
const WidgetMixin = {
|
const WidgetMixin = {
|
||||||
|
components: {
|
||||||
|
LoadingAnimation,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
|
/* The options prop is an object of settings for a given widget */
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data: () => ({
|
||||||
|
loading: true, // Indicates current loading status, to display spinner
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
/* Overridden by widget component. Re-fetches and renders any external data *
|
||||||
|
* Called by parent component, and triggered either by user or time interval */
|
||||||
|
update() {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('No update method configured for this widget');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// If the mounted function isn't overridden,then hide loader
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WidgetMixin;
|
export default WidgetMixin;
|
||||||
|
|
|
@ -331,6 +331,9 @@ html[data-theme='colorful'] {
|
||||||
div.context-menu {
|
div.context-menu {
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
}
|
}
|
||||||
|
.collapsable.is-open {
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme='minimal-light'], html[data-theme='minimal-dark'], html[data-theme='vaporware'] {
|
html[data-theme='minimal-light'], html[data-theme='minimal-dark'], html[data-theme='vaporware'] {
|
||||||
|
|
|
@ -28,23 +28,25 @@
|
||||||
+ (this.colCount ? `col-count-${this.colCount} ` : '')"
|
+ (this.colCount ? `col-count-${this.colCount} ` : '')"
|
||||||
>
|
>
|
||||||
<!-- Display any dynamic widget content -->
|
<!-- Display any dynamic widget content -->
|
||||||
<WidgetGroup v-if="!singleSectionView" :widgets="widgets" />
|
<!-- <WidgetGroup v-if="!singleSectionView" :widgets="widgets" /> -->
|
||||||
<Section
|
<template v-for="(section, index) in filteredTiles">
|
||||||
v-for="(section, index) in filteredTiles"
|
<Section
|
||||||
:key="index"
|
:key="index"
|
||||||
:index="index"
|
:index="index"
|
||||||
:title="section.name"
|
:title="section.name"
|
||||||
:icon="section.icon || undefined"
|
:icon="section.icon || undefined"
|
||||||
:displayData="getDisplayData(section)"
|
:displayData="getDisplayData(section)"
|
||||||
:groupId="`section-${index}`"
|
:groupId="`section-${index}`"
|
||||||
:items="filterTiles(section.items, searchValue)"
|
:items="filterTiles(section.items, searchValue)"
|
||||||
:searchTerm="searchValue"
|
:widgets="section.widgets"
|
||||||
:itemSize="itemSizeBound"
|
:searchTerm="searchValue"
|
||||||
@itemClicked="finishedSearching()"
|
:itemSize="itemSizeBound"
|
||||||
@change-modal-visibility="updateModalVisibility"
|
@itemClicked="finishedSearching()"
|
||||||
:class="
|
@change-modal-visibility="updateModalVisibility"
|
||||||
(searchValue && filterTiles(section.items, searchValue).length === 0) ? 'no-results' : ''"
|
:class="
|
||||||
/>
|
(searchValue && filterTiles(section.items, searchValue).length === 0) ? 'no-results' : ''"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<!-- Show add new section button, in edit mode -->
|
<!-- Show add new section button, in edit mode -->
|
||||||
<AddNewSection v-if="isEditMode" />
|
<AddNewSection v-if="isEditMode" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,7 +65,7 @@
|
||||||
|
|
||||||
import SettingsContainer from '@/components/Settings/SettingsContainer.vue';
|
import SettingsContainer from '@/components/Settings/SettingsContainer.vue';
|
||||||
import Section from '@/components/LinkItems/Section.vue';
|
import Section from '@/components/LinkItems/Section.vue';
|
||||||
import WidgetGroup from '@/components/Widgets/WidgetGroup';
|
// import WidgetGroup from '@/components/Widgets/WidgetGroup';
|
||||||
import EditModeSaveMenu from '@/components/InteractiveEditor/EditModeSaveMenu.vue';
|
import EditModeSaveMenu from '@/components/InteractiveEditor/EditModeSaveMenu.vue';
|
||||||
import ExportConfigMenu from '@/components/InteractiveEditor/ExportConfigMenu.vue';
|
import ExportConfigMenu from '@/components/InteractiveEditor/ExportConfigMenu.vue';
|
||||||
import AddNewSection from '@/components/InteractiveEditor/AddNewSectionLauncher.vue';
|
import AddNewSection from '@/components/InteractiveEditor/AddNewSectionLauncher.vue';
|
||||||
|
@ -77,7 +79,7 @@ export default {
|
||||||
name: 'home',
|
name: 'home',
|
||||||
components: {
|
components: {
|
||||||
SettingsContainer,
|
SettingsContainer,
|
||||||
WidgetGroup,
|
// WidgetGroup,
|
||||||
EditModeSaveMenu,
|
EditModeSaveMenu,
|
||||||
ExportConfigMenu,
|
ExportConfigMenu,
|
||||||
AddNewSection,
|
AddNewSection,
|
||||||
|
@ -160,7 +162,7 @@ export default {
|
||||||
},
|
},
|
||||||
/* Returns only the tiles that match the users search query */
|
/* Returns only the tiles that match the users search query */
|
||||||
filterTiles(allTiles, searchTerm) {
|
filterTiles(allTiles, searchTerm) {
|
||||||
return searchTiles(allTiles, searchTerm);
|
return searchTiles(allTiles, searchTerm) || [];
|
||||||
},
|
},
|
||||||
/* Returns optional section display preferences if available */
|
/* Returns optional section display preferences if available */
|
||||||
getDisplayData(section) {
|
getDisplayData(section) {
|
||||||
|
@ -216,10 +218,12 @@ export default {
|
||||||
let isNeeded = false;
|
let isNeeded = false;
|
||||||
if (!this.sections) return false;
|
if (!this.sections) return false;
|
||||||
this.sections.forEach((section) => {
|
this.sections.forEach((section) => {
|
||||||
if (section.icon && section.icon.includes(prefix)) isNeeded = true;
|
if (section && section.items) {
|
||||||
section.items.forEach((item) => {
|
if (section.icon && section.icon.includes(prefix)) isNeeded = true;
|
||||||
if (item.icon && item.icon.includes(prefix)) isNeeded = true;
|
section.items.forEach((item) => {
|
||||||
});
|
if (item.icon && item.icon.includes(prefix)) isNeeded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return isNeeded;
|
return isNeeded;
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue