2
0
Fork 0

feat: split up base templates into more sections (def blocks)

partly to allow easier customization, partly just for clarity
This commit is contained in:
Lance Edgar 2024-08-20 13:45:48 -05:00
parent fce0de5d30
commit 869963403d
3 changed files with 151 additions and 91 deletions

View file

@ -15,6 +15,10 @@
<whole-page /> <whole-page />
</div> </div>
## nb. sometimes a template needs to define something
## before the body content proper is rendered
${self.before_content()}
## content body from derived/child template ## content body from derived/child template
${self.body()} ${self.body()}
@ -26,8 +30,12 @@
</body> </body>
</html> </html>
## nb. this becomes part of the page <title> tag within <head>
## it also is used as default value for content_title() below
<%def name="title()"></%def> <%def name="title()"></%def>
## nb. this is the "content title" as shown on screen, within the
## "hero bar" just below the "index title"
<%def name="content_title()">${self.title()}</%def> <%def name="content_title()">${self.title()}</%def>
<%def name="header_core()"> <%def name="header_core()">
@ -41,7 +49,10 @@
${self.vuejs()} ${self.vuejs()}
${self.buefy()} ${self.buefy()}
${self.fontawesome()} ${self.fontawesome()}
${self.hamburger_menu_js()}
</%def>
<%def name="hamburger_menu_js()">
<script> <script>
## NOTE: this code was copied from ## NOTE: this code was copied from
@ -88,14 +99,21 @@
<%def name="core_styles()"> <%def name="core_styles()">
${self.buefy_styles()} ${self.buefy_styles()}
${self.base_styles()}
</%def>
<%def name="buefy_styles()">
${h.stylesheet_link(h.get_liburl(request, 'buefy.css'))}
</%def>
<%def name="base_styles()">
<style> <style>
/* ****************************** */ ##############################
/* page */ ## page
/* ****************************** */ ##############################
/* nb. helps force footer to bottom of screen */ ## nb. helps force footer to bottom of screen
html, body { html, body {
height: 100%; height: 100%;
} }
@ -106,12 +124,14 @@
} }
% endif % endif
/* nb. this refers to the "menu-sized" app title in far left of main menu */ ## nb. this refers to the "menu-sized" app title in far left of main menu
#global-header-title { #navbar-brand-title {
font-weight: bold; font-weight: bold;
} }
#current-context { #header-index-title {
display: flex;
gap: 1.5rem;
padding-left: 0.5rem; padding-left: 0.5rem;
} }
@ -132,10 +152,6 @@
</style> </style>
</%def> </%def>
<%def name="buefy_styles()">
${h.stylesheet_link(h.get_liburl(request, 'buefy.css'))}
</%def>
<%def name="extra_styles()"> <%def name="extra_styles()">
${base_meta.extra_styles()} ${base_meta.extra_styles()}
</%def> </%def>
@ -144,19 +160,148 @@
<%def name="render_vue_template_whole_page()"> <%def name="render_vue_template_whole_page()">
<script type="text/x-template" id="whole-page-template"> <script type="text/x-template" id="whole-page-template">
## nb. the whole-page contains 3 elements:
## 1) header-wrapper
## 2) content-wrapper
## 3) footer
<div id="whole-page" <div id="whole-page"
style="height: 100%; display: flex; flex-direction: column; justify-content: space-between;"> style="height: 100%; display: flex; flex-direction: column; justify-content: space-between;">
## nb. the header-wrapper contains 2 elements:
## 1) header proper (menu + index title area)
## 2) page/content title area
<div class="header-wrapper"> <div class="header-wrapper">
## nb. the header proper contains 2 elements:
## 1) menu bar
## 2) index title area
<header> <header>
<nav class="navbar" role="navigation" aria-label="main navigation">
## nb. this is the main menu bar
<nav class="navbar" role="navigation" aria-label="main navigation">
${self.render_navbar_brand()}
${self.render_navbar_menu()}
</nav>
## nb. this is the "index title" area
<nav class="level" style="margin: 0.5rem 0.5rem 0.5rem auto;">
## nb. this is the index title proper
<div class="level-left">
<div id="header-index-title" class="level-item">
% if index_title:
% if index_url:
<h1 class="title">${h.link_to(index_title, index_url)}</h1>
% else:
<h1 class="title">${index_title}</h1>
% endif
% if master and master.creatable and not master.creating and master.has_perm('create'):
<wutta-button once type="is-primary"
tag="a" href="${url(f'{route_prefix}.create')}"
icon-left="plus"
label="Create New" />
% endif
% endif
</div>
</div>
## nb. this is a utility area for the master context
<div class="level-right">
## Configure button
% if master and master.configurable and not master.configuring and master.has_perm('configure'):
<div class="level-item">
<wutta-button once type="is-primary"
tag="a" href="${url(f'{route_prefix}.configure')}"
icon-left="cog"
label="Configure" />
</div>
% endif
${self.render_theme_picker()}
${self.render_feedback_button()}
</div>
</nav>
</header>
## nb. the page / content title area (aka. hero bar)
% if capture(self.content_title):
<section id="content-title"
class="has-background-primary">
<div style="display: flex; align-items: center; padding: 0.5rem;">
<h1 class="title has-text-white"
v-html="contentTitleHTML">
</h1>
<div style="flex-grow: 1; display: flex; gap: 0.5rem;">
${self.render_instance_header_title_extras()}
</div>
<div style="display: flex; gap: 0.5rem;">
${self.render_instance_header_buttons()}
</div>
</div>
</section>
% endif
</div> <!-- header-wrapper -->
## nb. the page content area
<div class="content-wrapper"
style="flex-grow: 1; padding: 0.5rem;">
<section id="page-body" style="height: 100%;">
% if request.session.peek_flash('error'):
% for error in request.session.pop_flash('error'):
<b-notification type="is-warning">
${error}
</b-notification>
% endfor
% endif
% if request.session.peek_flash('warning'):
% for msg in request.session.pop_flash('warning'):
<b-notification type="is-warning">
${msg}
</b-notification>
% endfor
% endif
% if request.session.peek_flash():
% for msg in request.session.pop_flash():
<b-notification type="is-info">
${msg}
</b-notification>
% endfor
% endif
<div style="height: 100%;">
${self.render_this_page_component()}
</div>
</section>
</div><!-- content-wrapper -->
## nb. the page footer
<footer class="footer">
<div class="content">
${base_meta.footer()}
</div>
</footer>
</div>
</script>
</%def>
<%def name="render_navbar_brand()">
<div class="navbar-brand"> <div class="navbar-brand">
<a class="navbar-item" href="${url('home')}"> <a class="navbar-item" href="${url('home')}">
<div style="display: flex; gap: 0.3rem; align-items: center;"> <div style="display: flex; gap: 0.3rem; align-items: center;">
${base_meta.header_logo()} ${base_meta.header_logo()}
<div id="global-header-title"> <div id="navbar-brand-title">
${base_meta.global_title()} ${base_meta.global_title()}
</div> </div>
</div> </div>
@ -167,8 +312,16 @@
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
</a> </a>
</div> </div>
</%def>
<%def name="render_navbar_menu()">
<div class="navbar-menu" id="navbar-menu"> <div class="navbar-menu" id="navbar-menu">
${self.render_navbar_start()}
${self.render_navbar_end()}
</div>
</%def>
<%def name="render_navbar_start()">
<div class="navbar-start"> <div class="navbar-start">
% for topitem in menus: % for topitem in menus:
@ -207,125 +360,23 @@
% endif % endif
% endfor % endfor
</div><!-- navbar-start -->
${self.render_navbar_end()}
</div> </div>
</nav>
<nav class="level" style="margin: 0.5rem 0.5rem 0.5rem auto;">
<div class="level-left">
## Current Context
<div id="current-context" class="level-item"
style="display: flex; gap: 1.5rem;">
% if index_title:
% if index_url:
<h1 class="title">${h.link_to(index_title, index_url)}</h1>
% else:
<h1 class="title">${index_title}</h1>
% endif
% if master and master.creatable and not master.creating and master.has_perm('create'):
<wutta-button once type="is-primary"
tag="a" href="${url(f'{route_prefix}.create')}"
icon-left="plus"
label="Create New" />
% endif
% endif
</div>
</div><!-- level-left -->
<div class="level-right">
% if master and master.configurable and not master.configuring and master.has_perm('configure'):
<div class="level-item">
<wutta-button once type="is-primary"
tag="a" href="${url(f'{route_prefix}.configure')}"
icon-left="cog"
label="Configure" />
</div>
% endif
</div> <!-- level-right -->
</nav><!-- level -->
</header>
## Page Title
% if capture(self.content_title):
<section id="content-title"
class="has-background-primary">
<div style="display: flex; align-items: center; padding: 0.5rem;">
<h1 class="title has-text-white"
v-html="contentTitleHTML">
</h1>
<div style="flex-grow: 1; display: flex; gap: 0.5rem;">
${self.render_instance_header_title_extras()}
</div>
<div style="display: flex; gap: 0.5rem;">
${self.render_instance_header_buttons()}
</div>
</div>
</section>
% endif
</div> <!-- header-wrapper -->
<div class="content-wrapper"
style="flex-grow: 1; padding: 0.5rem;">
## Page Body
<section id="page-body" style="height: 100%;">
% if request.session.peek_flash('error'):
% for error in request.session.pop_flash('error'):
<b-notification type="is-warning">
${error}
</b-notification>
% endfor
% endif
% if request.session.peek_flash('warning'):
% for msg in request.session.pop_flash('warning'):
<b-notification type="is-warning">
${msg}
</b-notification>
% endfor
% endif
% if request.session.peek_flash():
% for msg in request.session.pop_flash():
<b-notification type="is-info">
${msg}
</b-notification>
% endfor
% endif
<div style="height: 100%;">
${self.render_this_page_component()}
</div>
</section>
</div><!-- content-wrapper -->
## Footer
<footer class="footer">
<div class="content">
${base_meta.footer()}
</div>
</footer>
</div>
</script>
</%def> </%def>
<%def name="render_navbar_end()">
<div class="navbar-end">
${self.render_user_menu()}
</div>
</%def>
<%def name="render_theme_picker()"></%def>
<%def name="render_feedback_button()"></%def>
<%def name="render_vue_script_whole_page()"> <%def name="render_vue_script_whole_page()">
<script> <script>
let WholePage = { const WholePage = {
template: '#whole-page-template', template: '#whole-page-template',
computed: {}, computed: {},
@ -360,8 +411,9 @@
}, },
} }
let WholePageData = { const WholePageData = {
contentTitleHTML: ${json.dumps(capture(self.content_title))|n}, contentTitleHTML: ${json.dumps(capture(self.content_title))|n},
referrer: location.href,
mountedHooks: [], mountedHooks: [],
} }
@ -379,16 +431,12 @@
</script> </script>
</%def> </%def>
<%def name="before_content()"></%def>
<%def name="render_this_page_component()"> <%def name="render_this_page_component()">
<this-page @change-content-title="changeContentTitle" /> <this-page @change-content-title="changeContentTitle" />
</%def> </%def>
<%def name="render_navbar_end()">
<div class="navbar-end">
${self.render_user_menu()}
</div>
</%def>
<%def name="render_user_menu()"> <%def name="render_user_menu()">
% if request.user: % if request.user:
<div class="navbar-item has-dropdown is-hoverable"> <div class="navbar-item has-dropdown is-hoverable">
@ -468,6 +516,10 @@
<%def name="render_prevnext_header_buttons()"></%def> <%def name="render_prevnext_header_buttons()"></%def>
##############################
## vue components + app
##############################
<%def name="render_vue_templates()"> <%def name="render_vue_templates()">
${self.render_vue_template_whole_page()} ${self.render_vue_template_whole_page()}
${self.render_vue_script_whole_page()} ${self.render_vue_script_whole_page()}

View file

@ -14,6 +14,10 @@
<%def name="render_vue_templates()"> <%def name="render_vue_templates()">
${parent.render_vue_templates()} ${parent.render_vue_templates()}
${self.render_vue_template_grid()}
</%def>
<%def name="render_vue_template_grid()">
% if grid is not Undefined: % if grid is not Undefined:
${grid.render_vue_template()} ${grid.render_vue_template()}
% endif % endif

View file

@ -6,6 +6,7 @@
<%def name="render_vue_templates()"> <%def name="render_vue_templates()">
${parent.render_vue_templates()} ${parent.render_vue_templates()}
${self.render_vue_template_this_page()} ${self.render_vue_template_this_page()}
${self.render_vue_script_this_page()}
</%def> </%def>
<%def name="render_vue_template_this_page()"> <%def name="render_vue_template_this_page()">
@ -14,6 +15,9 @@
${self.page_content()} ${self.page_content()}
</div> </div>
</script> </script>
</%def>
<%def name="render_vue_script_this_page()">
<script> <script>
const ThisPage = { const ThisPage = {