feat: work on date menu

This commit is contained in:
bkfox 2023-11-02 21:54:15 +01:00
parent ab231e9a89
commit 87a2ee5a45
30 changed files with 5302 additions and 4116 deletions

File diff suppressed because it is too large Load Diff

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

View File

@ -89,11 +89,7 @@ Usefull context:
{% block main %}
<header class="container header {% if cover %}has-cover{% endif %}">
{% block header %}
{% if object %}
{% include header_template_name|default:"./widgets/header.html" with title=object.title cover=object.cover.url %}
{% else %}
{% include header_template_name|default:"./widgets/header.html" %}
{% endif %}
{% include header_template_name|default:"./widgets/header.html" %}
{% endblock %}
</header>

View File

@ -24,7 +24,16 @@
{% block content-container %}
{% block secondary-nav-container %}
<section class="container">
<nav class="secondary-nav">
{% block secondary-nav %}{% endblock %}
</nav>
</section>
{% endblock %}
{{ block.super }}
<div class="container">
{% block before_list %}{% endblock %}

View File

@ -19,11 +19,15 @@
{% endblock %}
{% block header %}
{% with "./widgets/diffusion_list_header.html" as header_template_name %}
{% with "./widgets/dated_list_header.html" as header_template_name %}
{{ block.super }}
{% endwith %}
{% endblock %}
{% block secondary-nav %}
{% include "./widgets/dates_menu.html" with url_name="diffusion-list" %}
{% endblock %}
{% block pages_list %}
{% with hide_schedule=True %}
<section role="list" class="list">

View File

@ -16,11 +16,17 @@
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
{% block header %}
{% with "./widgets/log_list_header.html" as header_template_name %}
{% with "./widgets/dated_list_header.html" as header_template_name %}
{{ block.super }}
{% endwith %}
{% endblock %}
{% block secondary-nav %}
{% include "./widgets/dates_menu.html" with url_name="log-list" %}
{% endblock %}
{% block pages_list_ %}
<section>
{# <h4 class="subtitle size-4">{{ date }}</h4> #}

View File

@ -11,76 +11,3 @@
{{ block.super }}
{% endif %}
{% endblock %}
{% block before_list_ %}
{% if view.has_filters and object_list %}
<form method="GET" action="" class="list-filters">
<a-dropdown :item-class="dropdown-trigger"
button-icon-open="fa-solid fa-bars" button-icon-close="fa-solid fa-bars"
:content-class="dropdown-menu">
<template #default>
<div class="dropdown-menu">
{% block filters-content %}
<div class="dropdown-item">
<div class="field is-grouped is-grouped-right">
<div class="control">
<button class="button is-primary"/>{% translate "Apply" %}</button>
</div>
<div class="control">
<a href="?" class="button is-secondary">{% translate "Reset" %}</a>
</div>
</div>
</div>
{% endblock %}
</div>
</template>
</a-dropdown>
</form>
{% comment %}
<div class="media-content">
{% block filters %}
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% translate "Search" %}</label>
</div>
<div class="field-body">
<div class="field">
<div class="control has-icons-left">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
<input class="input" type="text" name="q"
value="{{ filterset_data.q }}"
placeholder="{% translate "Search content" %}">
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% translate "Categories" %}</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
{% for label, value in categories %}
<label class="checkbox">
<input type="checkbox" class="checkbox" name="category__id__in"
value="{{ value }}"
{% if value in filterset_data.category__id__in %}checked{% endif %} />
{{ label }}
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
</div>
<div class="media-right">
</div>
{% endcomment %}
</form>
{% endif %}
{% endblock %}

View File

@ -16,13 +16,15 @@
{% endfor %}
</section>
<div class="has-text-right">
<a href="{% url "episode-list" parent_slug=program.slug %}"
class="button action"
aria-label="{% translate "Show all program's articles" %}">
{% translate "All episodes" %}
</a>
</div>
<nav class="has-text-right">
<li class="nav-item">
<a href="{% url "episode-list" parent_slug=program.slug %}"
class="button action"
aria-label="{% translate "Show all program's articles" %}">
{% translate "All episodes" %}
</a>
</li>
</nav>
</section>
{% endif %}
@ -37,13 +39,15 @@
{% endfor %}
</section>
<div class="has-text-right">
<a href="{% url "article-list" parent_slug=program.slug %}"
class="button action"
aria-label="{% translate "Show all program's articles" %}">
{% translate "All articles" %}
</a>
</div>
<nav class="has-text-right">
<li class="nav-item">
<a href="{% url "article-list" parent_slug=program.slug %}"
class="button action"
aria-label="{% translate "Show all program's articles" %}">
{% translate "All articles" %}
</a>
</li>
</nav>
</section>
{% endif %}

View File

@ -11,9 +11,25 @@ An empty date results to a title or a separator
{% endcomment %}
{% load i18n %}
<a-dropdown class="nav-item dropdown" button-class="dropdown-trigger button"
content-class="dropdown-menu"
buttonIconOpen="fa-solid fa-bars" buttonIconClose="fa-solid fa-bars">
<template #default>
<div class="dropdown-content">
<div class="dropdown-item">
<h4>{% translate "Pick a date" %}</h4>
<v-calendar mode="date" borderless
:initial-page="{month: {{date.month}}, year: {{date.year}}}"
@dayclick="(event) => window.aircox.pickDate({% url url_name %}, event)"
color="yellow"
/>
</div>
</div>
</template>
</a-dropdown>
{% for day in dates %}
<li class="nav-item">
<a class="heading {% if day == date %}highlight{% endif %}" href="{% url url_name date=day %}">
<li class="nav-item {% if day == date %}active{% endif %}">
<a href="{% url url_name date=day %}" class="button">
{{ day|date:"l d" }}
</a>
</li>

View File

@ -9,18 +9,11 @@
<div class="column">
{{ block.super }}
{% spaceless %}
<section class="heading content">
{% block content %}{{ block.content }}{% endblock %}
</section>
{% endspaceless %}
</div>
</div>
{% endblock %}
{% block outer %}
{{ block.super }}
<nav class="preview-nav has-text-right">
{% block header-nav %}{% endblock %}
</nav>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% load i18n aircox %}
{% block outer %}
<article class="preview list-item {% if is_primary %}is-primary{% endif %}{% block card_class %}{% endblock %}">
<article class="preview list-item {% if not cover %}no-cover{% endif %}{% if is_primary %}is-primary{% endif %}{% block card_class %}{% endblock %}">
{% block inner %}
<a href="{{ url|escape }}" class="headings is-fullwidth columns">

View File

@ -1,14 +0,0 @@
{% extends "./header.html" %}
{% block outer %}
{% with date|date:"l d F Y" as subtitle %}
{{ block.super }}
{% endwith %}
{% endblock %}
{% block header-nav %}
<div>
{% include "./dates_menu.html" with url_name="log-list" %}
</div>
{% endblock %}

View File

@ -57,9 +57,6 @@ class TestBaseView:
"station": station,
"page": None, # get_page() returns None
"has_sidebar": base_view.has_sidebar,
"has_filters": False,
"sidebar_object_list": published_pages[: base_view.list_count],
"sidebar_list_url": base_view.get_sidebar_url(),
"audio_streams": station.streams,
"model": base_view.model,
}

View File

@ -5,7 +5,6 @@ __all__ = ["ArticleDetailView", "ArticleListView"]
class ArticleDetailView(PageDetailView):
has_sidebar = True
model = Article
def get_sidebar_queryset(self):

View File

@ -2,19 +2,12 @@ from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views.generic.base import ContextMixin, TemplateResponseMixin
from ..models import Page
__all__ = ("BaseView", "BaseAPIView")
class BaseView(TemplateResponseMixin, ContextMixin):
header_template_name = "aircox/widgets/header.html"
has_sidebar = True
"""Show side navigation."""
has_filters = False
"""Show filters nav."""
list_count = 5
"""Item count for small lists displayed on page."""
@property
def station(self):
@ -23,33 +16,14 @@ class BaseView(TemplateResponseMixin, ContextMixin):
# def get_queryset(self):
# return super().get_queryset().station(self.station)
def get_sidebar_queryset(self):
"""Return a queryset of items to render on the side nav."""
return (
Page.objects.select_subclasses().published().order_by("-pub_date")
)
def get_sidebar_url(self):
return reverse("page-list")
def get_page(self):
return None
def get_context_data(self, **kwargs):
kwargs.setdefault("station", self.station)
kwargs.setdefault("page", self.get_page())
kwargs.setdefault("has_filters", self.has_filters)
kwargs.setdefault("header_template_name", self.header_template_name)
has_sidebar = kwargs.setdefault("has_sidebar", self.has_sidebar)
if has_sidebar and "sidebar_object_list" not in kwargs:
sidebar_object_list = self.get_sidebar_queryset()
if sidebar_object_list is not None:
kwargs["sidebar_object_list"] = sidebar_object_list[
: self.list_count
]
kwargs["sidebar_list_url"] = self.get_sidebar_url()
if "audio_streams" not in kwargs:
kwargs["audio_streams"] = self.station.streams
@ -61,6 +35,11 @@ class BaseView(TemplateResponseMixin, ContextMixin):
)
kwargs["model"] = model
page = kwargs.get("page")
if page:
kwargs["title"] = page.display_title
kwargs["cover"] = page.cover and page.cover.url
return super().get_context_data(**kwargs)
def dispatch(self, *args, **kwargs):

View File

@ -13,7 +13,6 @@ class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
"""View for timetables."""
model = Diffusion
has_filters = True
redirect_date_url = "diffusion-list"
attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS

View File

@ -14,7 +14,6 @@ class HomeView(BaseView, ListView):
queryset = Diffusion.objects.on_air().select_related("episode")
logs_count = 5
publications_count = 5
has_filters = False
def get_queryset(self):
return super().get_queryset().date(date.today())

View File

@ -72,7 +72,6 @@ class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
`request.GET`, defaults to today)."""
redirect_date_url = "log-list"
has_filters = True
attach_to_value = StaticPage.ATTACH_TO_LOGS
def get_date(self):

View File

@ -71,7 +71,7 @@ class ParentMixin:
def get_context_data(self, **kwargs):
self.parent = kwargs.setdefault("parent", self.parent)
if self.parent is not None:
kwargs.setdefault("cover", self.parent.cover)
kwargs.setdefault("cover", self.parent.cover.url)
return super().get_context_data(**kwargs)

View File

@ -22,8 +22,6 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
"""Base view class for BasePage list."""
template_name = "aircox/basepage_list.html"
item_template_name = "aircox/widgets/page_item.html"
has_sidebar = True
paginate_by = 30
has_headline = True
@ -41,20 +39,22 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
)
def get_context_data(self, **kwargs):
kwargs.setdefault("item_template_name", self.item_template_name)
kwargs.setdefault("has_headline", self.has_headline)
context = super().get_context_data(**kwargs)
if not context.get("page") and not context.get("title"):
model = self.model._meta.verbose_name_plural
parent = context.get("parent")
if parent:
parent = parent.get_display_title()
title = _("{model} of {parent}")
else:
title = _("{model}")
context["title"] = title.format(model=model, parent=parent)
parent = context.get("parent")
if not context.get("page"):
if not context.get("title"):
model = self.model._meta.verbose_name_plural
if parent:
parent = parent.display_title
title = _("{model} of {parent}")
else:
title = _("{model}")
context["title"] = title.format(model=model, parent=parent)
if not context.get("cover") and parent and parent.cover:
context["cover"] = parent.cover.url
return context
@ -63,7 +63,13 @@ class BasePageDetailView(BaseView, DetailView):
template_name = "aircox/basepage_detail.html"
context_object_name = "page"
has_filters = False
def get_context_data(self, **kwargs):
if self.object.cover:
kwargs.setdefault("cover", self.object.cover.url)
if self.object.title:
kwargs.setdefault("title", self.object.display_title)
return super().get_context_data(**kwargs)
def get_queryset(self):
return super().get_queryset().select_related("cover")
@ -96,7 +102,6 @@ class PageListView(FiltersMixin, BasePageListView):
filterset_class = PageFilters
template_name = None
has_filters = True
categories = None
filters = None
@ -132,7 +137,6 @@ class PageDetailView(BasePageDetailView):
template_name = None
context_object_name = "page"
has_filters = False
def get_template_names(self):
return super().get_template_names() + ["aircox/page_detail.html"]

View File

@ -10,8 +10,10 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.0.0",
"@popperjs/core": "^2.11.8",
"core-js": "^3.8.3",
"lodash": "^4.17.21",
"v-calendar": "^3.1.2",
"vue": "^3.2.13",
"vue3-carousel": "^0.3.1"
},

View File

@ -1,4 +1,4 @@
import 'vue3-carousel/dist/carousel.css'
import {Calendar, DatePicker} from 'v-calendar';
import components from './components'
import { Carousel, Pagination, Navigation, Slide } from 'vue3-carousel'
@ -12,6 +12,10 @@ const App = {
Carousel,
Pagination,
Navigation,
...{
VCalendar: Calendar,
VDatepicker: DatePicker
},
},
computed: {

View File

@ -15,14 +15,17 @@ export default class Builder {
/**
* Fetch app from remote and mount application.
*/
fetch(url, {el='#app', ...options}={}) {
return fetch(url, options).then(response => response.text())
fetch(url, {el='#app', historySave=true, ...options}={}) {
const fut = fetch(url, options).then(response => response.text())
.then(content => {
let doc = new DOMParser().parseFromString(content, 'text/html')
let app = doc.querySelector(el)
content = app ? app.innerHTML : content
return this.mount({content, title: doc.title, reset:true, url })
})
if(historySave)
fut.then(() => this.historySave(url))
return fut
}
/**
@ -61,7 +64,9 @@ export default class Builder {
container.innerHTML = content
if(title)
document.title = title
return createApp(config, props)
const app = createApp(config, props)
app.config.globalProperties.window = window
return app
}
unmount() {
@ -106,7 +111,7 @@ export default class Builder {
else
options = {...options, method: target.method, body: formData}
}
this.fetch(url, options).then(() => this.historySave(url))
this.fetch(url, options)
event.preventDefault();
event.stopPropagation();
}

View File

@ -1,6 +1,5 @@
@charset "utf-8";
$font-special: "bagnard";
$font-special-url: url("assets/Bagnard.otf");
@ -45,14 +44,32 @@ $screen-wide: 1380px;
@import 'v-calendar/style.css';
@import "~bulma/sass/utilities/_all.sass";
@import "~bulma/sass/components/dropdown.sass";
$body-background-color: $light;
$menu-item-hover-background-color: #dfdfdf;
$menu-item-active-background-color: #d2d2d2;
@import "~bulma";
@import "~bulma/sass/base/_all";
@import "~bulma/sass/components/_all";
@import "~bulma/sass/form/_all";
@import "~bulma/sass/grid/_all";
@import "~bulma/sass/helpers/_all";
@import "~bulma/sass/layout/_all";
@import "~bulma/sass/elements/box";
@import "~bulma/sass/elements/button";
@import "~bulma/sass/elements/container";
@import "~bulma/sass/elements/content";
@import "~bulma/sass/elements/icon";
// @import "~bulma/sass/elements/image";
// @import "~bulma/sass/elements/notification";
@import "~bulma/sass/elements/progress";
@import "~bulma/sass/elements/table";
@import "~bulma/sass/elements/tag";
@import "~bulma/sass/elements/title";
//-- helpers/modifiers
.is-fullwidth { width: 100%; }
@ -157,40 +174,6 @@ a.navbar-item.is-active {
}
}
//-- cards
.card {
.title {
a {
color: $dark;
}
padding: 0.2em;
font-size: $size-5;
font-weight: $weight-medium;
}
&.is-primary {
box-shadow: 0em 0em 0.5em $black
}
}
.card-super-title {
position: absolute;
z-index: 1000;
font-size: $size-6;
font-weight: $weight-bold;
padding: 0.2em;
top: 1em;
background-color: #ffffffc7;
max-width: 90%;
.fas {
padding: 0.1em;
font-size: 0.8em;
}
}
//-- page
.page {
& > .cover {
@ -221,6 +204,7 @@ a.navbar-item.is-active {
}
// FIXME: remove -> only used in comment
.media.item .headline {
line-height: 1.2em;
max-height: calc(1.2em * 3);
@ -503,6 +487,54 @@ h1, h2, h3, h4, h5, h6, .heading, .title, .subtitle {
}
}
.dropdown-item {
font-size: unset !important
}
.vc-weekday-1, .vc-weekday-7 {
color: var(--highlight-color-2) !important;
}
// ---- secondary navigation
nav {
margin: $mp-3 0;
.nav-item {
font-family: unset;
list-style: none;
display: inline-block;
margin-bottom: $mp-3;
vertical-align: unset !important;
a, .button {
padding: $mp-3;
border: 1px solid hsl(0deg, 0%, 86%);
background-color: var(--highlight-color);
}
button, .button {
vertical-align: inherit;
padding-top: $mp-3;
padding-bottom: $mp-3;
height: unset !important;
}
&.active a, &.active button {
background-color: var(--highlight-color-2);
color: var(--highlight-color);
}
&:not(:last-child) {
margin-right: $mp-2;
}
}
}
// ---- ---- previews & page items
.preview {
position: relative;
@ -548,8 +580,6 @@ h1, h2, h3, h4, h5, h6, .heading, .title, .subtitle {
}
.list-item {
width: 100%;
@ -568,10 +598,13 @@ h1, h2, h3, h4, h5, h6, .heading, .title, .subtitle {
}
.media-content {
height: var(--preview-cover-small-size);
display: flex;
flex-direction: column;
.list-item:not(.no-cover) & {
height: var(--preview-cover-small-size);
}
.content { flex-grow: 1; }
.actions {
@ -639,26 +672,6 @@ h1, h2, h3, h4, h5, h6, .heading, .title, .subtitle {
}
.preview-nav {
margin-top: $mp-3;
.nav-item {
font-size: $text-size-bigger;
font-family: unset;
list-style: none;
display: inline-block;
&:not(:last-child) {
margin-right: $mp-2;
}
a {
padding: $mp-3;
}
}
}
.preview-header {
width: 100%;

View File

@ -1,23 +0,0 @@
<template>
<carousel :items-to-show="1.5">
<slot></slot>
<template #addons>
<navigation />
<pagination />
</template>
</carousel>
</template>
<script>
// If you are using PurgeCSS, make sure to whitelist the carousel CSS classes
import 'vue3-carousel/dist/carousel.css'
import { Carousel, Pagination, Navigation } from 'vue3-carousel'
export default {
components: {
Carousel,
Pagination,
Navigation,
},
}
</script>

View File

@ -1,24 +1,18 @@
<template>
<div>
<div :class="['a-dropdown', itemClass, active ? activeClass : '']"
style="display: flex; flex-direction: row"
@click="noButton && toggle()">
<div style="flex:auto">
<slot name="item"></slot>
</div>
<slot name="button">
<span :class="[buttonClass]" style="flex-grow:0" @click="toggle()">
<span class="icon">
<i v-if="!active" :class="buttonIconOpen"></i>
<i v-if="active" :class="buttonIconClose"></i>
</span>
<component :is="tag" :class="[itemClass, active ? activeClass : '']">
<slot name="before-button"></slot>
<slot name="button" :toggle="toggle" :active="active">
<button :class="[buttonClass]" @click="toggle()">
<span class="icon">
<i v-if="!active" :class="buttonIconOpen"></i>
<i v-if="active" :class="buttonIconClose"></i>
</span>
</slot>
</div>
<div :class="contentClass" v-if="active">
</button>
</slot>
<div :class="contentClass" v-show="active">
<slot></slot>
</div>
</div>
</component>
</template>
<script>
export default {
@ -29,9 +23,9 @@ export default {
},
props: {
itemClass: String,
tag: {type: String, default: "div"},
activeClass: {type: String, default: "is-active"},
buttonClass: String,
buttonClass: {type: String, default: "button"},
buttonIconOpen: { type: String, default:"fa fa-angle-down"},
buttonIconClose: { type: String, default:"fa fa-angle-up"},
contentClass: String,

View File

@ -12,6 +12,7 @@ import Builder from './appBuilder'
import Sound from './sound'
import {Set} from './model'
import "./assets/vendor"
import './assets/styles.scss'
@ -68,5 +69,10 @@ window.aircox = {
else
for(let item of container.querySelectorAll('a.navbar-item'))
item.style.display = null;
},
pickDate(url, date) {
url = `${url}?date=${date.id}`
this.builder.fetch(url)
}
}