Good working day

This commit is contained in:
sheychen 2019-04-15 18:01:30 +02:00
parent 3c92244f5e
commit 2f52372e35
21 changed files with 10316 additions and 174 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*node_modules
*vscode

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10078
compiler/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,8 @@
"@babel/preset-env": "^7.2.0", "@babel/preset-env": "^7.2.0",
"autoprefixer": "^8.3.0", "autoprefixer": "^8.3.0",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"chart.js": "^2.8.0",
"core-js": "^2.6.5",
"css-loader": "^0.28.11", "css-loader": "^0.28.11",
"file-loader": "^1.1.11", "file-loader": "^1.1.11",
"friendly-errors-webpack-plugin": "^1.7.0", "friendly-errors-webpack-plugin": "^1.7.0",
@ -38,16 +40,17 @@
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"node-sass": "^4.11.0", "node-sass": "^4.11.0",
"optimize-css-assets-webpack-plugin": "^4.0.0", "optimize-css-assets-webpack-plugin": "^4.0.0",
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0",
"sass-loader": "^7.0.1", "sass-loader": "^7.0.1",
"string-replace-webpack-plugin": "^0.1.3", "string-replace-webpack-plugin": "^0.1.3",
"url-loader": "^1.0.1", "url-loader": "^1.0.1",
"vue-chartjs": "^3.4.2",
"vue-loader": "^15.4.2", "vue-loader": "^15.4.2",
"vue-style-loader": "^4.1.2", "vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.22", "vue-template-compiler": "^2.5.22",
"webpack": "^4.6.0", "webpack": "^4.6.0",
"webpack-cli": "^3.2.1", "webpack-cli": "^3.2.1"
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"vue": "^2.5.22" "vue": "^2.5.22"

View File

@ -6,10 +6,15 @@ export const emitErrorMixin = {
} }
} }
export const saveOptionsMixin = { export const handleOptionsMixin = {
methods: { methods: {
saveOptions(options) { saveOptions(options) {
this.$emit("save", options) this.$emit("save", options)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
} }
} }
} }

View File

@ -1,25 +0,0 @@
<template lang="pug">
div(@click="changeName()").
Hello, compiler.
</template>
<script>
export default {
name : 'example',
data() {
return {
name : 'world'
};
},
methods : {
changeName() {
this.name = 'foobar';
}
}
};
</script>
<style lang="sass" scoped>
div
font-weight: bold
</style>

View File

@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
a.account(target="_blank" :href="account.url") a.account(target="_blank" :href="account.url")
.avatar(:style="avatarStyle(account.avatar_static)") .avatar(v-if="showMedia" :style="avatarStyle(account.avatar_static)")
.name(v-html="parseEmojis(account.display_name, account.emojis)") .name(v-html="parseEmojis(account.display_name, account.emojis)")
.acct @{{ account.acct }} .acct @{{ account.acct }}
</template> </template>
@ -9,7 +9,13 @@ a.account(target="_blank" :href="account.url")
import { parseEmojisMixin } from './tools' import { parseEmojisMixin } from './tools'
export default { export default {
props: ["account"], props: {
account: Object,
showMedia: {
type: Boolean,
default: true
}
},
mixins: [ parseEmojisMixin ], mixins: [ parseEmojisMixin ],
methods: { methods: {
avatarStyle(avatar) { avatarStyle(avatar) {

View File

@ -2,21 +2,22 @@
.client .client
.statues .statues
.header(v-if="notifications.length > 0") Accueil .header(v-if="notifications.length > 0") Accueil
.list(v-if="statues.length > 0") .list(v-if="statues.length > 0" @scroll="onScroll")
template(v-for="status in statues") template(v-for="status in statues")
status(v-if="showStatus(status)" :key="status.id" :status="status" :now="now" @mark="onStatusMark") status(v-if="showStatus(status)" :key="status.id" :status="status" :now="now" :showMedia="showMedia" @mark="onStatusMark")
.status(v-show="loadingOlder") Loading...
template(v-else) Loading... template(v-else) Loading...
.notifications(v-if="notifications.length > 0") .notifications(v-if="notifications.length > 0")
.header .header
| Notifications | Notifications
span.date(@click.stop.prevent="onNotificationsClear") span.date(@click.stop.prevent="onNotificationsClear")
.list .list
notification(v-for="notification in notifications" :key="notification.id" :notification="notification" :now="now" @dismiss="onNotificationDismiss") notification(v-for="notification in notifications" :key="notification.id" :notification="notification" :now="now" :showMedia="showMedia" @dismiss="onNotificationDismiss")
</template> </template>
<script> <script>
import { timerMinin } from '../core/fromNow.vue' import { timerMinin } from '../core/fromNow.vue'
import { emitErrorMixin, saveOptionsMixin } from '../core/tools' import { emitErrorMixin } from '../core/tools'
import statusVue from './status.vue' import statusVue from './status.vue'
import notificationVue from './notification.vue' import notificationVue from './notification.vue'
@ -33,7 +34,8 @@ export default {
reconnect: Boolean, reconnect: Boolean,
buffer: Number, buffer: Number,
reblog: Boolean, reblog: Boolean,
reply: Boolean reply: Boolean,
showMedia: Boolean
}, },
data() { data() {
return { return {
@ -46,17 +48,25 @@ export default {
}), }),
statues: [], statues: [],
notifications: [], notifications: [],
now: Date.now() now: Date.now(),
loadingOlder: false
}; };
}, },
methods: { methods: {
addStatus(status) { onScroll(event) {
this.statues.unshift(status) if(!this.loadingOlder && event.target.scrollHeight - event.target.clientHeight - event.target.scrollTop - 100 < 0) {
this.statues.splice(this.buffer) this.loadingOlder = true
}, this.rest
addNotification(notif) { .get("/timelines/home", { params: { limit: this.buffer,
this.notifications.push(notif) max_id: this.statues[this.statues.length - 1].id } })
this.notifications.splice(this.buffer) .then(res => {
this.statues.push.apply(this.statues, res.data)
this.loadingOlder = false
})
.catch(this.emitError)
} else if(event.target.scrollTop < 20) {
this.statues.splice(this.buffer)
}
}, },
removeStatus(id) { removeStatus(id) {
for (var i = this.statues.length - 1; i >= 0; i--) { for (var i = this.statues.length - 1; i >= 0; i--) {
@ -101,11 +111,11 @@ export default {
const payload = JSON.parse(event.payload) const payload = JSON.parse(event.payload)
switch (event.event) { switch (event.event) {
case "update": case "update":
this.addStatus(payload) this.statues.unshift(payload)
break break
case "notification": case "notification":
this.addNotification(payload) this.notifications.unshift(payload)
break break
case "delete": case "delete":

View File

@ -3,7 +3,7 @@
.header(@click="showSettings = !showSettings") .header(@click="showSettings = !showSettings")
| Mastodon: | Mastodon:
span(v-html="parseEmojis(account.display_name, account.emojis)") span(v-html="parseEmojis(account.display_name, account.emojis)")
| @{{ server }} | {{ server ? '@' + server : '' }}
.settings(v-show="showSettings") .settings(v-show="showSettings")
p p
label(for="reconnect") Reconnect: label(for="reconnect") Reconnect:
@ -17,6 +17,9 @@
p p
label(for="buffer") Buffer: label(for="buffer") Buffer:
input#buffer(type="number" :value="buffer" @keyup.enter="setOption('buffer', parseInt($event.target.value))") input#buffer(type="number" :value="buffer" @keyup.enter="setOption('buffer', parseInt($event.target.value))")
p
label(for="showMedia") Show media:
input#showMedia(type="checkbox" :checked="showMedia" @change="setOption('showMedia', $event.target.checked)")
client(v-if="server && token" v-bind="$props") client(v-if="server && token" v-bind="$props")
.auth(v-else) .auth(v-else)
form(@submit.prevent="setServer") form(@submit.prevent="setServer")
@ -31,13 +34,13 @@
</template> </template>
<script> <script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools' import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import { parseEmojisMixin } from './tools' import { parseEmojisMixin } from './tools'
import clientVue from './client.vue' import clientVue from './client.vue'
export default { //TODO: Use oauth export default { //TODO: Use oauth
name: 'mastodon', name: 'mastodon',
mixins: [ emitErrorMixin, saveOptionsMixin, parseEmojisMixin ], mixins: [ emitErrorMixin, handleOptionsMixin, parseEmojisMixin ],
components: { components: {
client: clientVue client: clientVue
}, },
@ -63,6 +66,10 @@ export default { //TODO: Use oauth
reply: { reply: {
default: false, default: false,
type: Boolean type: Boolean
},
showMedia: {
default: true,
type: Boolean
} }
}, },
data() { data() {
@ -81,11 +88,6 @@ export default { //TODO: Use oauth
}).then(() => this.saveOptions({...this.$props, }).then(() => this.saveOptions({...this.$props,
server: this.newServer, token: this.newToken})) server: this.newServer, token: this.newToken}))
.catch(this.emitError) .catch(this.emitError)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
} }
}, },
created() { created() {

View File

@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
.notification .notification
account(:account="notification.account") account(:account="notification.account" :showMedia="showMedia")
span.colored.text-icon.letter(v-if="notification.type == 'mention'") span.colored.text-icon.letter(v-if="notification.type == 'mention'")
span.colored.text-icon.letter(v-if="notification.type == 'reblog'") span.colored.text-icon.letter(v-if="notification.type == 'reblog'")
@ -11,7 +11,7 @@
.content .content
template(v-if="notification.type == 'follow'") Vous suit template(v-if="notification.type == 'follow'") Vous suit
status.reblog(v-else-if="notification.status" :status="notification.status" :now="now" status.reblog(v-else-if="notification.status" :status="notification.status" :now="now"
:withAccount="notification.type != 'mention'" @mark.stop.prevent="") :showMedia="showMedia" :withAccount="notification.type != 'mention'" @mark.stop.prevent="")
a.date(@click.stop.prevent="makeDismiss" style="margin-top: -1em") a.date(@click.stop.prevent="makeDismiss" style="margin-top: -1em")
</template> </template>
@ -22,7 +22,13 @@ import accountVue from './account.vue'
import statusVue from './status.vue' import statusVue from './status.vue'
export default { export default {
props: ["notification"], props: {
notification: Object,
showMedia: {
type: Boolean,
default: true
}
},
mixins: [ timedMixin ], mixins: [ timedMixin ],
components: { components: {
fromNow: fromNowVue, fromNow: fromNowVue,

View File

@ -1,7 +1,7 @@
<template lang="pug"> <template lang="pug">
div div
.status .status
account(v-if="withAccount" :account="status.account") account(v-if="withAccount" :account="status.account" :showMedia="showMedia")
span.text-icon.letter(v-if="status.reblog") span.text-icon.letter(v-if="status.reblog")
@ -15,9 +15,11 @@ div
div(v-if="!status.spoiler_text || !status.sensitive") div(v-if="!status.spoiler_text || !status.sensitive")
.text(v-html="parseEmojis(status.content, status.emojis)") .text(v-html="parseEmojis(status.content, status.emojis)")
a.media(v-for="media in status.media_attachments" :href="media.url" target="_blank") a.media(v-for="media in status.media_attachments" :href="media.url" target="_blank")
img(v-if="media.type == 'image' || media.type == 'gifv'" :src="media.preview_url" :alt="media.description" :title="media.description") template(v-if="showMedia")
.gif(v-if="media.type == 'gifv'") GIF img(v-if="media.type == 'image' || media.type == 'gifv'" :src="media.preview_url" :alt="media.description" :title="media.description")
status.reblog(v-else :status="status.reblog" :now="now") .gif(v-if="media.type == 'gifv'") GIF
template(v-else) Hidden media
status.reblog(v-else :status="status.reblog" :now="now" :showMedia="showMedia")
.meta(v-if="!status.reblog") .meta(v-if="!status.reblog")
a.replies(@click.stop.prevent="makeReply(status)") a.replies(@click.stop.prevent="makeReply(status)")
@ -47,6 +49,10 @@ export default {
}, },
props: { props: {
status: Object, status: Object,
showMedia: {
type: Boolean,
default: true
},
withAccount: { withAccount: {
type: Boolean, type: Boolean,
default: true default: true

View File

@ -31,12 +31,12 @@
</template> </template>
<script> <script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools' import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import fromNowVue, { timerMinin } from '../core/fromNow.vue'; import fromNowVue, { timerMinin } from '../core/fromNow.vue';
export default { export default {
name: 'nextcloud-news', name: 'nextcloud-news',
mixins: [ emitErrorMixin, timerMinin, saveOptionsMixin ], mixins: [ emitErrorMixin, timerMinin, handleOptionsMixin ],
components: { components: {
fromNow: fromNowVue fromNow: fromNowVue
}, },
@ -103,11 +103,6 @@ export default {
}).then(() => this.saveOptions({...this.$props, }).then(() => this.saveOptions({...this.$props,
server: this.newServer, token: this.newToken, username: this.newUsername })) server: this.newServer, token: this.newToken, username: this.newUsername }))
.catch(this.emitError) .catch(this.emitError)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
} }
}, },
created() { created() {

View File

@ -0,0 +1,72 @@
<script>
import { Bar, mixins } from 'vue-chartjs'
import moment from 'moment'
const { reactiveProp } = mixins
export default {
extends: Bar,
mixins: [ reactiveProp ],
mounted () {
this.renderChart(this.chartData, {
responsive: true,
legend: {
labels: {
fontColor: "white"
}
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
ticks: {
fontColor: "white",
source: 'data',
autoSkip: true,
maxRotation: 0,
autoSkipPadding: 5
},
time: {
displayFormats: {
hour: 'HH[h]'
}
}
}],
yAxes: [{
id: 'y-axis-temp',
display: true,
position: 'left',
ticks: {
fontColor: "white"
}
},{
id: 'y-axis-rain',
display: true,
position: 'right',
ticks: {
fontColor: "white",
beginAtZero: true,
suggestedMax: 1
}
}]
},
tooltips: {
intersect: false,
callbacks: {
title: function(tooltipItem, myData) {
var item = myData.datasets[tooltipItem[0].datasetIndex].data[tooltipItem[0].index]
return moment(item.x || item.t).format('HH[h]')
},
label: function(tooltipItem, myData) {
var label = myData.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += tooltipItem.value;
return label;
}
}
}
})
}
}
</script>

View File

@ -8,6 +8,9 @@
p p
label(for="update") Update interval: label(for="update") Update interval:
input#update(type="number" :value="update" @keyup.enter="setOption('update', parseInt($event.target.value))") input#update(type="number" :value="update" @keyup.enter="setOption('update', parseInt($event.target.value))")
p
label(for="forecastLimit") Forecast limit:
input#forecastLimit(type="number" :value="forecastLimit" @keyup.enter="setOption('forecastLimit', parseInt($event.target.value))")
p p
button(@click="showAdd = true") Add city button(@click="showAdd = true") Add city
template(v-if="weathers.length > 0 || cities.length == 0") template(v-if="weathers.length > 0 || cities.length == 0")
@ -24,27 +27,21 @@
| {{ city.main.temp }}°C {{ city.main.humidity }}% | {{ city.main.temp }}°C {{ city.main.humidity }}%
input.weather(v-show="showAdd" placeholder="city id" @keyup.enter="addCity(parseInt($event.target.value))") input.weather(v-show="showAdd" placeholder="city id" @keyup.enter="addCity(parseInt($event.target.value))")
.forecast .forecast
template(v-if="forecast") chart(v-if="forecast" :chartData="forecastChart")
.list
.line(v-for="line in forecast")
| {{ formatDate(line.dt) }}
.data
| {{ line.main.temp }}°C {{ line.main.humidity }}%
.main(v-for="main in line.weather")
.ic
img(:src="`https://openweathermap.org/img/w/${main.icon}.png`" :alt="main.main")
p {{ main.description }}
template(v-else) Loading... template(v-else) Loading...
template(v-else) Loading... template(v-else) Loading...
</template> </template>
<script> <script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools' import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import chartVue from './chart.vue'
export default { export default {
name: 'openweathermap', name: 'openweathermap',
components: {}, components: {
mixins: [ emitErrorMixin, saveOptionsMixin ], chart: chartVue
},
mixins: [ emitErrorMixin, handleOptionsMixin ],
props: { props: {
token: String, token: String,
cities: { cities: {
@ -64,6 +61,10 @@ export default {
lang: { lang: {
default: 'fr', default: 'fr',
type: String type: String
},
forecastLimit: {
default: 9,
type: Number
} }
}, },
data() { data() {
@ -82,6 +83,31 @@ export default {
showAdd: this.cities.length == 0 showAdd: this.cities.length == 0
}; };
}, },
computed: {
forecastChart() { return {
datasets: [{
type: 'line',
label: 'Temperature',
yAxisID: 'y-axis-temp',
borderColor: 'white',
borderWidth: 1,
fill: false,
data: this.forecast.map(function (line) { return {
x: line.dt * 1000, y: line.main.temp
} })
},{
type: 'bar',
label: 'Percipitation',
yAxisID: 'y-axis-rain',
borderColor: '#DDDDDD',
backgroundColor: '#DDDDDD33',
borderWidth: 1,
data: this.forecast.filter(f => 'rain' in f && '3h' in f.rain).map(function (line) { return {
x: line.dt * 1000, y: line.rain['3h']
} })
}]
} }
},
methods: { methods: {
makeSelect(id) { makeSelect(id) {
this.selected = id this.selected = id
@ -103,7 +129,8 @@ export default {
loadForecast() { loadForecast() {
if(this.weathers[this.selected]) { if(this.weathers[this.selected]) {
this.rest.get('forecast', { params: { this.rest.get('forecast', { params: {
id: this.weathers[this.selected].id id: this.weathers[this.selected].id,
cnt: this.forecastLimit
}}) }})
.then(res => this.forecast = res.data.list) .then(res => this.forecast = res.data.list)
.catch(this.emitError) .catch(this.emitError)
@ -117,11 +144,6 @@ export default {
const options = {...this.$props} const options = {...this.$props}
options.cities.push({id: id}) options.cities.push({id: id})
this.saveOptions(options) this.saveOptions(options)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
} }
}, },
created() { created() {

View File

@ -25,6 +25,10 @@ module.exports = (env) => {
entry : { entry : {
[filename] : './entry.js' [filename] : './entry.js'
}, },
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
output : { output : {
filename : '[name].js', filename : '[name].js',
path : path.resolve(__dirname, 'dist', filepath) path : path.resolve(__dirname, 'dist', filepath)

View File

@ -1,6 +1,7 @@
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: Verdana, Geneva, sans-serif;
} }
body { body {
@ -62,14 +63,14 @@ a {
flex: 1; flex: 1;
} }
.mastodon .header, .mastodon .settings, .mastodon .client .list > div { #services > div > .header, #services > div > .settings {
margin: 0.3em; margin: 0.3em;
background-color: #222; background-color: #222;
border-radius: 0.3em; border-radius: 0.3em;
padding: 0.3em; padding: 0.3em;
} }
.mastodon .header { #services > div > .header {
font-size: large; font-size: large;
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
@ -91,6 +92,13 @@ a {
min-height: min-content; min-height: min-content;
} }
.mastodon .client .list > div {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.mastodon .client .statues { .mastodon .client .statues {
-webkit-box-flex: 1; -webkit-box-flex: 1;
-ms-flex: 1; -ms-flex: 1;
@ -182,19 +190,6 @@ a {
max-width: 30%; max-width: 30%;
} }
.openweathermap > .header, .openweathermap .settings {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.openweathermap > .header {
font-size: large;
text-align: center;
font-weight: bold;
}
.openweathermap .ic { .openweathermap .ic {
overflow: hidden; overflow: hidden;
height: 30px; height: 30px;
@ -227,35 +222,11 @@ a {
-webkit-box-flex: 1; -webkit-box-flex: 1;
-ms-flex: 1; -ms-flex: 1;
flex: 1; flex: 1;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
max-height: 100%; max-height: 100%;
overflow-y: scroll; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.openweathermap .forecast .line {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
min-width: 15em;
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.openweathermap .forecast .line .data {
float: right;
}
.openweathermap .forecast .line .main p {
display: inline;
}
.openweathermap .weather { .openweathermap .weather {
min-width: 17em; min-width: 17em;
border: 1px solid #222; border: 1px solid #222;
@ -283,19 +254,6 @@ a {
vertical-align: top; vertical-align: top;
} }
.nextcloud-news > .header, .nextcloud-news .settings {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.nextcloud-news > .header {
font-size: large;
text-align: center;
font-weight: bold;
}
.nextcloud-news .news { .nextcloud-news .news {
margin: 0.3em; margin: 0.3em;
background-color: #222; background-color: #222;

9
main.css.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -40,11 +40,11 @@ var app = new Vue({
this.saveServices() this.saveServices()
}, },
setService(id, options) { setService(id, options) {
this.services.push({ this.$set(this.services, id, {
type: this.services[id].type, type: this.services[id].type,
options: options options: options
}) })
this.removeService(id) this.saveServices()
}, },
removeService(id) { removeService(id) {
this.services.splice(id, 1) this.services.splice(id, 1)
@ -52,6 +52,7 @@ var app = new Vue({
}, },
saveServices() { saveServices() {
localStorage.setItem(servicesStorage, JSON.stringify(this.services)) localStorage.setItem(servicesStorage, JSON.stringify(this.services))
this.$forceUpdate()
} }
} }
}) })

View File

@ -16,6 +16,7 @@ $foreColor: #eee
* *
margin: 0 margin: 0
padding: 0 padding: 0
font-family: Verdana, Geneva, sans-serif
body body
background-color: $backColor background-color: $backColor
@ -55,14 +56,14 @@ a
display: flex display: flex
& > div & > div
flex: 1 flex: 1
& > .header, & > .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.mastodon .mastodon
.header, .settings, .client .list > div
@include tile
.header
font-size: large
text-align: center
font-weight: bold
.client .client
display: flex display: flex
height: 100vh height: 100vh
@ -71,6 +72,8 @@ a
height: 100% height: 100%
overflow-y: auto overflow-y: auto
min-height: min-content min-height: min-content
& > div
@include tile
.statues .statues
flex: 1 flex: 1
.notifications .notifications
@ -125,12 +128,6 @@ a
float: right float: right
.openweathermap .openweathermap
& > .header, .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.ic .ic
overflow: hidden overflow: hidden
height: 30px height: 30px
@ -149,19 +146,9 @@ a
@include tile @include tile
.forecast .forecast
flex: 1 flex: 1
display: flex
flex-wrap: wrap
max-height: 100% max-height: 100%
overflow-y: scroll overflow-y: auto
overflow-x: hidden overflow-x: hidden
.line
flex: 1
min-width: 15em
@include tile
.data
float: right
.main p
display: inline
.weather .weather
min-width: 17em min-width: 17em
border: 1px solid $tileColor border: 1px solid $tileColor
@ -179,12 +166,6 @@ a
vertical-align: top vertical-align: top
.nextcloud-news .nextcloud-news
& > .header, .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.news .news
@include tile @include tile
.date .date