More development and UI fixes

This commit is contained in:
Jonatan Nilsson 2022-08-08 07:22:41 +00:00
parent 71f157d393
commit 727af236ec
16 changed files with 586 additions and 179 deletions

View file

@ -5,7 +5,7 @@ import PageRoutes from '../base/page/routes.mjs'
export default class Server extends Parent { export default class Server extends Parent {
init() { init() {
this.flaskaOptions.appendHeaders['Content-Security-Policy'] = `default-src 'self'; script-src 'self' talk.hyvor.com; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; iframe-src talk.hyvor.com` //; frame-ancestors 'none'` this.flaskaOptions.appendHeaders['Content-Security-Policy'] = `default-src 'self'; script-src 'self' talk.hyvor.com; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-src talk.hyvor.com` //; frame-ancestors 'none'`
} }
addCustomRoutes() { addCustomRoutes() {

View file

@ -1,4 +1,4 @@
const EditPage = require('./editpage') const EditPage = require('./site_editpage')
const AllPages = require('./site_pages') const AllPages = require('./site_pages')
const AllArticles = require('./site_articles') const AllArticles = require('./site_articles')
const EditArticle = require('./editarticle') const EditArticle = require('./editarticle')

View file

@ -30,33 +30,45 @@ const FileUpload = {
}, },
view: function(vnode) { view: function(vnode) {
let media = vnode.attrs.media let imageLink = this.preview && this.preview.preview || vnode.attrs.media
return m('fileupload', { return m('fileupload', {
class: vnode.attrs.class || null, class: (vnode.attrs.class || '') + ' '
+ (!imageLink ? 'empty' : '') + ' '
+ (vnode.attrs.useimg ? 'useimg' : ''),
}, [ }, [
this.preview || media imageLink && vnode.attrs.useimg
? vnode.attrs.useimg ? m('img', { src: imageLink })
? [ m('img', { src: this.preview && this.preview.preview || media }), m('div.showicon')] : null,
: m('a.display.inside', { imageLink && !vnode.attrs.useimg
href: this.preview && this.preview.preview || media, ? m('a', {
style: { href: imageLink,
'background-image': 'url("' + (this.preview && this.preview.preview || media) + '")', style: {
}, 'background-image': 'url("' + (imageLink) + '")',
}, m('div.showicon')) 'height': vnode.attrs.height + 'px'
: m('div.inside.showbordericon') },
})
: null
, ,
!imageLink
? m('div.noimage', {
style: {
'padding-top': vnode.attrs.useimg ? '56.25%' : null,
'height': !vnode.attrs.useimg ? vnode.attrs.height + 'px' : null,
},
}, vnode.children)
: null,
m('input', { m('input', {
accept: 'image/*', accept: 'image/*',
type: 'file', type: 'file',
onchange: this.fileChanged.bind(this, vnode), onchange: this.fileChanged.bind(this, vnode),
}), }),
media && vnode.attrs.ondelete /*imageLink && vnode.attrs.ondelete
? m('button.remove', { onclick: vnode.attrs.ondelete }) ? m('button.remove', { onclick: vnode.attrs.ondelete })
: null, : null,
this.loading this.loading
? m('div.loading-spinner') ? m('div.loading-spinner')
: null, : null,*/
]) ])
}, },
} }

View file

@ -111,8 +111,9 @@ const AdminArticles = {
view: function(vnode) { view: function(vnode) {
return [ return [
m('div.wrapper.admin', [ m('div.admin', [
m('div.inside', [ m('div.inside.vertical', [
m('div.spacer'),
m('h2', 'All articles'), m('h2', 'All articles'),
m('div.actions', [ m('div.actions', [
m('span', 'Actions:'), m('span', 'Actions:'),

View file

@ -0,0 +1,351 @@
const FileUpload = require('./fileupload')
const PageTree = require('../page_tree')
const api = require('../api')
const Editor = require('./editor')
const AdminEditPage = {
oninit: function(vnode) {
this.loading = false
this.showLoading = null
this.data = {
page: null,
}
this.pages = [{id: null, name: 'Frontpage'}]
this.pages = this.pages.concat(PageTree.getFlatTree())
this.newBanner = null
this.newMedia = null
this.editor = null
this.fetchPage(vnode)
},
onbeforeupdate: function(vnode) {
if (this.lastid !== m.route.param('id')) {
this.fetchPage(vnode)
}
},
fetchPage: function(vnode) {
this.lastid = m.route.param('id')
return this.requestPage(
api.sendRequest({
method: 'GET',
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
})
)
},
requestPage: function(data) {
this.error = ''
if (this.showLoading) {
clearTimeout(this.showLoading)
}
if (this.data.page) {
this.showLoading = setTimeout(() => {
this.showLoading = null
this.loading = true
m.redraw()
}, 150)
} else {
this.loading = true
}
data
.then((result) => {
this.data = result
if (this.data.page.id) {
document.title = 'Editing: ' + this.data.page.name + ' - Admin NFP Moe'
this.editedPath = true
} else {
document.title = 'Create Page - Admin NFP Moe'
}
}, (err) => {
this.error = err.message
})
.then(() => {
if (!this.data.page) {
this.data.page = {}
}
clearTimeout(this.showLoading)
this.showLoading = null
this.loading = false
m.redraw()
})
},
updateValue: function(name, e) {
this.data.page[name] = e.currentTarget.value
if (name === 'path') {
this.editedPath = true
} else if (name === 'name' && !this.editedPath) {
this.data.page.path = this.data.page.name.toLowerCase().replace(/ /g, '-')
}
},
updateParent: function(e) {
this.data.page.parent_id = Number(e.currentTarget.value) || null
},
mediaUploaded: function(type, file) {
if (type === 'banner') {
this.newBanner = file
} else {
this.newMedia = file
}
},
mediaRemoved: function(type) {
this.data.page['remove_' + type] = true
this.data.page[type + '_prefix'] = null
},
save: function(vnode, e) {
e.preventDefault()
if (!this.data.page.name) {
this.error = 'Name is missing'
} else if (!this.data.page.path) {
this.error = 'Path is missing'
} else {
this.error = ''
}
if (this.error) return
let formData = new FormData()
if (this.newBanner) {
formData.append('banner', this.newBanner.file)
}
if (this.newMedia) {
formData.append('media', this.newMedia.file)
}
if (this.data.page.id) {
formData.append('id', this.data.page.id)
}
formData.append('name', this.data.page.name)
formData.append('parent_id', this.data.page.parent_id || null)
formData.append('path', this.data.page.path)
formData.append('remove_banner', this.data.page.remove_banner ? true : false)
formData.append('remove_media', this.data.page.remove_media ? true : false)
this.loading = true
this.requestPage(
this.editor.save()
.then(body => {
formData.append('content', JSON.stringify(body))
return api.sendRequest({
method: 'PUT',
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
body: formData,
})
})
.then(data => {
if (!data.page.id) {
throw new Error('Something went wrong with saving, try again later')
} else if (this.lastid === 'add') {
this.lastid = data.page.id.toString()
m.route.set('/admin/pages/' + data.page.id)
}
return PageTree.refreshTree().then(() => {
this.pages = [{id: null, name: 'Frontpage'}]
this.pages = this.pages.concat(PageTree.getFlatTree())
return data
})
})
)
},
view: function(vnode) {
let page = this.data.page
let bannerImage = page && page.banner_alt_prefix
? page.banner_alt_prefix + '_large.avif'
: null
let mediaImage = page && page.media_alt_prefix
? page.media_alt_prefix + '_large.avif'
: null
return [
m('div.admin', [
!this.loading
? m(FileUpload, {
class: 'banner',
height: 150,
onfile: this.mediaUploaded.bind(this, 'banner'),
ondelete: this.mediaRemoved.bind(this, 'banner'),
media: bannerImage,
}, 'Click to upload banner image')
: null,
m('div.inside.vertical', {
onsubmit: this.save.bind(this, vnode),
}, [
m('div.page-goback', [
'« ',
m(m.route.Link, { href: '/admin/pages' }, 'Pages')
]),
m('h2', this.lastid === 'add' ? 'Create page' : 'Edit ' + (page && page.name || '(untitled)')),
page
? m('div.actions', [
m('span', 'Actions:'),
m(m.route.Link, { href: '/page/' + page.path }, 'View page'),
])
: null,
m('div.error', {
hidden: !this.error,
onclick: function() { vnode.state.error = '' },
}, this.error),
this.loading
? m('div.loading-spinner')
: [
m(FileUpload, {
class: 'cover',
useimg: true,
onfile: this.mediaUploaded.bind(this, 'media'),
ondelete: this.mediaRemoved.bind(this, 'media'),
media: mediaImage,
}, 'Click to upload page image'),
m('form', [
m('label', 'Parent'),
m('select', {
onchange: this.updateParent.bind(this),
}, this.pages.filter(item => !page || item.id !== page.id).map((item) => {
return m('option', {
value: item.id || 0,
selected: item.id === page.parent_id
}, item.name)
})),
m('div.input-row', [
m('div', [
m('label', 'Name'),
m('input', {
type: 'text',
value: page.name,
oninput: this.updateValue.bind(this, 'name'),
}),
]),
m('div', [
m('label', 'Path'),
m('input', {
type: 'text',
value: page.path,
oninput: this.updateValue.bind(this, 'path'),
}),
]),
]),
m('label', 'Description'),
m(Editor, {
oncreate: (subnode) => {
this.editor = subnode.state.editor
},
contentdata: page.content,
}),
m('input', {
hidden: !page.name || !page.path,
type: 'submit',
value: 'Save',
}),
])
],
]),
]),
/*
this.loading && !this.data.page
? m('div.admin-spinner.loading-spinner')
: null,
this.data.page
? m('div.admin-wrapper', [
this.loading
? m('div.loading-spinner')
: null,
m('div.admin-actions', this.data.page.id
? [
m('span', 'Actions:'),
m(m.route.Link, { href: '/page/' + this.data.page.path }, 'View page'),
]
: null),
m('article.editarticle', [
m('header', m('h1',
(this.data.page.id ? 'Edit ' : 'Create Page ') + (this.data.page.name || '(untitled)')
)
),
m('div.error', {
hidden: !this.error,
onclick: () => { vnode.state.error = '' },
}, this.error),
m(FileUpload, {
height: 300,
onfile: this.mediaUploaded.bind(this, 'banner'),
ondelete: this.mediaRemoved.bind(this, 'banner'),
media: bannerImage,
}),
m(FileUpload, {
class: 'cover',
useimg: true,
onfile: this.mediaUploaded.bind(this, 'media'),
ondelete: this.mediaRemoved.bind(this, 'media'),
media: mediaImage,
}),
m('form.editarticle.content', {
onsubmit: this.save.bind(this, vnode),
}, [
m('label', 'Parent'),
m('select', {
onchange: this.updateParent.bind(this),
}, this.pages.filter(item => !this.data.page || item.id !== this.data.page.id).map((item) => {
return m('option', {
value: item.id || 0,
selected: item.id === this.data.page.parent_id
}, item.name)
})),
m('div.input-row', [
m('div.input-group', [
m('label', 'Name'),
m('input', {
type: 'text',
value: this.data.page.name,
oninput: this.updateValue.bind(this, 'name'),
}),
]),
m('div.input-group', [
m('label', 'Path'),
m('input', {
type: 'text',
value: this.data.page.path,
oninput: this.updateValue.bind(this, 'path'),
}),
]),
]),
m('label', 'Description'),
m(Editor, {
oncreate: (subnode) => {
this.editor = subnode.state.editor
},
contentdata: this.data.page.content,
}),
m('div', {
hidden: !this.data.page.name || !this.data.page.path
}, [
m('input', {
type: 'submit',
value: 'Save',
}),
]),
]),
]),
])
: m('div.error', {
hidden: !this.error,
onclick: () => { this.fetchPage(vnode) },
}, this.error),,*/
]
},
}
module.exports = AdminEditPage

View file

@ -68,8 +68,9 @@ const AdminPages = {
view: function(vnode) { view: function(vnode) {
return [ return [
m('div.wrapper.admin', [ m('div.admin', [
m('div.inside', [ m('div.inside.vertical', [
m('div.spacer'),
m('h2', 'All pages'), m('h2', 'All pages'),
m('div.actions', [ m('div.actions', [
m('span', 'Actions:'), m('span', 'Actions:'),
@ -80,7 +81,7 @@ const AdminPages = {
onclick: function() { vnode.state.error = '' }, onclick: function() { vnode.state.error = '' },
}, this.error), }, this.error),
this.loading this.loading
? m('div.loading-spinner.full') ? m('div.loading-spinner')
: m('table', [ : m('table', [
m('thead', m('thead',
m('tr', [ m('tr', [

View file

@ -68,7 +68,9 @@ if (Authentication.currentUser) {
} }
const Loader = { const Loader = {
view: function() { return m('div.loading-spinner') }, view: function() {
return m('div.wrapper', m('div.loading-spinner'))
},
} }
const AdminResolver = { const AdminResolver = {
onmatch: function(args, requestedPath) { onmatch: function(args, requestedPath) {

View file

@ -1,5 +1,6 @@
const Fileinfo = require('./fileinfo') const Fileinfo = require('./fileinfo')
const EditorBlock = require('./editorblock') const EditorBlock = require('./editorblock')
const media = require('./media')
const Article = { const Article = {
oninit: function(vnode) { oninit: function(vnode) {
@ -13,29 +14,17 @@ const Article = {
if (this.lastId !== article.id) { if (this.lastId !== article.id) {
this.lastId = article.id this.lastId = article.id
if (article.media_alt_prefix) { let pictureCover = '(max-width: 639px) calc(100vw - 40px), '
this.pictureFallback = article.media_alt_prefix + '_small.jpg' + '(max-width: 1000px) 300px, '
this.pictureJpeg = article.media_alt_prefix + '_small.jpg' + ' 720w, ' + '400px'
+ article.media_alt_prefix + '_medium.jpg' + ' 1300w, ' if (vnode.attrs.full) {
+ article.media_alt_prefix + '_large.jpg 1920w' pictureCover = '(max-width: 1280) calc(100vw - 2rem), '
this.pictureAvif = article.media_alt_prefix + '_small.avif' + ' 720w, ' + '1248px'
+ article.media_alt_prefix + '_medium.avif' + ' 1300w, '
+ article.media_alt_prefix + '_large.avif 1920w'
if (vnode.attrs.full) {
this.pictureCover = '(max-width: 1280) calc(100vw - 2rem), '
+ '1248px'
} else {
this.pictureCover = '(max-width: 639px) calc(100vw - 40px), '
+ '(max-width: 1000px) 300px, '
+ '400px'
}
} else {
this.pictureFallback = null
this.pictureJpeg = null
this.pictureAvif = null
this.pictureCover = null
} }
this.pictureData = media.generatePictureSource(
article,
pictureCover)
} }
}, },
@ -51,27 +40,12 @@ const Article = {
m('h2', article.name) m('h2', article.name)
), ),
m('div.row', [ m('div.row', [
this.pictureFallback media.getArticlePicture(
? m(vnode.attrs.full ? 'a' : m.route.Link, { this.pictureData,
class: 'cover', !vnode.attrs.full,
target: vnode.attrs.full ? '_blank' : '', vnode.attrs.full ? article.media_path : '/article/' + article.path,
href: vnode.attrs.full ? article.media_path : '/article/' + article.path, 'Image for news item ' + article.name,
}, ),
m('picture', [
m('source', {
srcset: this.pictureAvif,
sizes: this.pictureCover,
type: 'image/avif',
}),
m('img', {
srcset: this.pictureJpeg,
sizes: this.pictureCover,
alt: 'Image for news item ' + article.name,
src: this.pictureFallback,
}),
])
)
: null,
m('div', [ m('div', [
m('div.description', m('div.description',
article.content.blocks.map(block => { article.content.blocks.map(block => {

View file

@ -1,4 +1,5 @@
const Fileinfo = require('./fileinfo') const Fileinfo = require('./fileinfo')
const media = require('./media')
const Articleslim = { const Articleslim = {
oninit: function(vnode) { oninit: function(vnode) {
@ -35,23 +36,10 @@ const Articleslim = {
} }
} }
if (article.media_alt_prefix) { this.pictureData = media.generatePictureSource(
this.pictureFallback = article.media_alt_prefix + '_small.jpg' article,
this.pictureJpeg = article.media_alt_prefix + '_small.jpg' + ' 720w, ' '(max-width: 440px) calc(100vw - 40px), '
+ article.media_alt_prefix + '_medium.jpg' + ' 1300w, ' + '124px')
+ article.media_alt_prefix + '_large.jpg 1920w'
this.pictureAvif = article.media_alt_prefix + '_small.avif' + ' 720w, '
+ article.media_alt_prefix + '_medium.avif' + ' 1300w, '
+ article.media_alt_prefix + '_large.avif 1920w'
this.pictureCover = '(max-width: 440px) calc(100vw - 40px), '
+ '124px'
} else {
this.pictureFallback = null
this.pictureJpeg = null
this.pictureAvif = null
this.pictureCover = null
}
} }
}, },
@ -59,26 +47,13 @@ const Articleslim = {
let article = vnode.attrs.article let article = vnode.attrs.article
return m('articleslim', [ return m('articleslim', [
this.pictureFallback media.getArticlePicture(
? m(m.route.Link, { this.pictureData,
class: 'cover', true,
href: '/article/' + article.path, '/article/' + article.path,
}, 'Image for news item ' + article.name,
m('picture', [ m('a.cover.nobg')
m('source', { ),
srcset: this.pictureAvif,
sizes: this.pictureCover,
type: 'image/avif',
}),
m('img', {
srcset: this.pictureJpeg,
sizes: this.pictureCover,
alt: 'Image for news item ' + article.name,
src: this.pictureFallback,
}),
])
)
: m('a.cover.nobg'),
m('div', [ m('div', [
m(m.route.Link, m(m.route.Link,
{ class: 'title', href: '/article/' + article.path }, { class: 'title', href: '/article/' + article.path },

View file

@ -7,7 +7,6 @@ const Authentication = {
authListeners: [], authListeners: [],
updateToken: function(token) { updateToken: function(token) {
console.log('updateToken', token)
if (!token) return Authentication.clearToken() if (!token) return Authentication.clearToken()
localStorage.setItem(storageName, token) localStorage.setItem(storageName, token)
Authentication.currentUser = JSON.parse(atob(token.split('.')[1])) Authentication.currentUser = JSON.parse(atob(token.split('.')[1]))
@ -18,8 +17,6 @@ const Authentication = {
}, },
clearToken: function() { clearToken: function() {
var err = new Error()
console.log('clearing', err.stack)
Authentication.currentUser = null Authentication.currentUser = null
localStorage.removeItem(storageName) localStorage.removeItem(storageName)
Authentication.isAdmin = false Authentication.isAdmin = false

View file

@ -1,21 +1,15 @@
export function generateSource(item, cover) { export function generatePictureSource(item, cover) {
if (!item) return if (!item || !item.media_alt_prefix) return null
if (item.media_alt_prefix) { return {
item.pictureFallback = item.media_alt_prefix + '_small.jpg' fallback: item.media_alt_prefix + '_small.jpg',
item.pictureJpeg = item.media_alt_prefix + '_small.jpg' + ' 720w, ' jpeg: item.media_alt_prefix + '_small.jpg' + ' 720w, '
+ item.media_alt_prefix + '_medium.jpg' + ' 1300w, ' + item.media_alt_prefix + '_medium.jpg' + ' 1300w, '
+ item.media_alt_prefix + '_large.jpg 1920w' + item.media_alt_prefix + '_large.jpg 1920w',
item.pictureAvif = item.media_alt_prefix + '_small.avif' + ' 720w, ' avif: item.media_alt_prefix + '_small.avif' + ' 720w, '
+ item.media_alt_prefix + '_medium.avif' + ' 1300w, ' + item.media_alt_prefix + '_medium.avif' + ' 1300w, '
+ item.media_alt_prefix + '_large.avif 1920w' + item.media_alt_prefix + '_large.avif 1920w',
cover: cover,
item.pictureCover = cover
} else {
item.pictureFallback = null
item.pictureJpeg = null
item.pictureAvif = null
item.pictureCover = null
} }
} }
@ -48,3 +42,28 @@ export function getBannerImage(item, prefix) {
return out return out
} }
export function getArticlePicture(pictureData, useRouteLink, path, altText, fallback) {
if (!pictureData) return fallback || null
return m(useRouteLink ? m.route.Link : 'a', {
class: 'cover',
rel: useRouteLink ? null : 'noopener',
target: useRouteLink ? null : '_blank',
href: path,
},
m('picture', [
m('source', {
srcset: pictureData.avif,
sizes: pictureData.cover,
type: 'image/avif',
}),
m('img', {
srcset: pictureData.jpeg,
sizes: pictureData.cover,
alt: altText,
src: pictureData.fallback,
}),
])
)
}

View file

@ -59,24 +59,6 @@ const SiteArticle = {
.then((result) => { .then((result) => {
this.data = result this.data = result
if (this.data.article.media_alt_prefix) {
this.data.article.pictureFallback = this.data.article.media_alt_prefix + '_small.jpg'
this.data.article.pictureJpeg = this.data.article.media_alt_prefix + '_small.jpg' + ' 720w, '
+ this.data.article.media_alt_prefix + '_medium.jpg' + ' 1300w, '
+ this.data.article.media_alt_prefix + '_large.jpg 1920w'
this.data.article.pictureAvif = this.data.article.media_alt_prefix + '_small.avif' + ' 720w, '
+ this.data.article.media_alt_prefix + '_medium.avif' + ' 1300w, '
+ this.data.article.media_alt_prefix + '_large.avif 1920w'
this.data.article.pictureCover = '(max-width: 840px) calc(100vw - 82px), '
+ '758px'
} else {
this.data.article.pictureFallback = null
this.data.article.pictureJpeg = null
this.data.article.pictureAvif = null
this.data.article.pictureCover = null
}
if (!this.data.article) { if (!this.data.article) {
this.error = 'Article not found' this.error = 'Article not found'
} }
@ -93,7 +75,6 @@ const SiteArticle = {
view: function(vnode) { view: function(vnode) {
let article = this.data.article let article = this.data.article
let banner = media.getBannerImage(article, '/article/')
return [ return [
this.loading this.loading
@ -107,14 +88,6 @@ const SiteArticle = {
}, },
}, 'Page error: ' + this.error + '. Click here to try again')) }, 'Page error: ' + this.error + '. Click here to try again'))
: null, : null,
/*(banner
? m('a.page-banner', {
href: banner.original,
target: '_blank',
style: { 'background-image': 'url("' + banner.banner + '")' },
},
)
: null),*/
(article (article
? m('.inside.vertical', [ ? m('.inside.vertical', [
m('div.page-goback', ['« ', m(m.route.Link, { m('div.page-goback', ['« ', m(m.route.Link, {
@ -143,7 +116,7 @@ const SiteArticle = {
hyvor_talk.reload() hyvor_talk.reload()
} }
}}, m('div.loading-spinner')) }}, m('div.loading-spinner'))
: m('button', { : m('button.comments', {
onclick: function() { window.LoadComments = true }, onclick: function() { window.LoadComments = true },
}, 'Open comment discussion'), }, 'Open comment discussion'),
]) ])

View file

@ -20,6 +20,7 @@ const SitePage = {
total_articles: 0, total_articles: 0,
featured: null, featured: null,
} }
this.picture = null
this.children = [] this.children = []
this.currentPage = Number(m.route.param('page')) || 1 this.currentPage = Number(m.route.param('page')) || 1
@ -87,7 +88,7 @@ const SitePage = {
title = 'NFP Moe - Anime/Manga translation group' title = 'NFP Moe - Anime/Manga translation group'
} }
media.generateSource(this.data.page, this.picture = media.generatePictureSource(this.data.page,
'(max-width: 840px) calc(100vw - 82px), ' '(max-width: 840px) calc(100vw - 82px), '
+ '758px') + '758px')
@ -111,8 +112,6 @@ const SitePage = {
let page = this.data.page let page = this.data.page
let featuredBanner = media.getBannerImage(this.data.featured, '/article/') let featuredBanner = media.getBannerImage(this.data.featured, '/article/')
let pageBanner = media.getBannerImage(this.data.page, '/page/') let pageBanner = media.getBannerImage(this.data.page, '/page/')
let paginatorMaxPath = Math.floor(this.data.total_articles / ArticlesPerPage) + 1
let paginatorPrefix = page ? '/page/' + page.path : '/'
return ([ return ([
this.loading this.loading
@ -171,25 +170,8 @@ const SitePage = {
]) ])
: null, : null,
m('div.container', [ m('div.container', [
(page && page.pictureFallback (page
? m('a.cover', { ? media.getArticlePicture(this.picture, false, page.media_path, 'Image for page ' + page.name)
rel: 'noopener',
href: page.media_path,
}, [
m('picture', [
m('source', {
srcset: page.pictureAvif,
sizes: page.pictureCover,
type: 'image/avif',
}),
m('img', {
srcset: page.pictureJpeg,
sizes: page.pictureCover,
alt: 'Image for news item ' + page.name,
src: page.pictureFallback,
}),
]),
])
: null), : null),
(page && page.content (page && page.content
? page.content.blocks.map(block => { ? page.content.blocks.map(block => {

View file

@ -2,6 +2,8 @@
===================== Variables ===================== ===================== Variables =====================
*/ */
:root { :root {
--admin-bg: hsl(213.9, 100%, 95%);
--admin-color: #000;
--admin-table-border: #01579b; --admin-table-border: #01579b;
--admin-table-header-bg: #3D77C7; --admin-table-header-bg: #3D77C7;
--admin-table-header-fg: #fff; --admin-table-header-fg: #fff;
@ -11,12 +13,32 @@
===================== main ===================== ===================== main =====================
*/ */
[hidden] { display: none !important; }
.admin {
background: var(--admin-bg);
color: var(--admin-color);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
min-height: calc(100vh - 200px);
}
.admin .inside { .admin .inside {
padding-bottom: 1rem; padding-bottom: 1rem;
} }
.admin table { .admin .spacer {
margin: 1rem; height: 40px;
}
.admin .loading-spinner {
position: relative;
left: unset;
top: unset;
min-height: 300px;
height: calc(100vh - 300px);
} }
.admin table { .admin table {
@ -24,6 +46,7 @@
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
font-size: 0.75em; font-size: 0.75em;
margin-bottom: 1rem;
} }
.admin table thead th, .admin table thead th,
@ -47,6 +70,7 @@
background: transparent; background: transparent;
border-color: transparent; border-color: transparent;
padding: 0; padding: 0;
margin: 0;
} }
.admin table td.right, .admin table td.right,
@ -55,7 +79,7 @@
} }
.admin .actions { .admin .actions {
margin: 0.5rem 1rem 0; margin: 0.5rem 0;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
font-size: 0.875rem; font-size: 0.875rem;
@ -64,3 +88,89 @@
.admin .actions a { .admin .actions a {
margin-left: 0.5rem; margin-left: 0.5rem;
} }
.admin form {
margin: 1rem 0 0;
}
.admin form input[type=text],
.admin form select {
width: 100%;
}
.admin .input-row {
display: flex;
margin-right: -1rem;
}
.admin .input-row > * {
margin-right: 1rem;
flex: 2 1 50px;
}
/* ************** fileupload ************** */
fileupload {
position: relative;
display: block;
width: 100%;
}
fileupload.banner {
margin: 0 0 -1px;
}
fileupload.empty {
border: 3px solid var(--seperator);
border-style: dashed;
}
fileupload.empty.useimg {
max-width: 700px;
margin: 0 auto;
}
fileupload a {
display: block;
width: 100%;
background-position: center;
background-size: cover;
}
fileupload .noimage {
color: var(--seperator);
}
fileupload input {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.01;
width: 100%;
cursor: pointer;
text-indent: -9999px;
z-index: 2;
}
/*
===================== 3rd party =====================
*/
.ce-block__content,
.ce-toolbar__content { max-width:calc(100% - 120px) !important; }
.cdx-block { max-width: 100% !important; }
.codex-editor {
border: 1px solid var(--color);
background: var(--bg);
color: var(--color);
}
.codex-editor:hover,
.codex-editor:active {
border-color: var(--link);
}

File diff suppressed because one or more lines are too long