Cleanup grid/filters logic a bit

get rid of grids.js file, remove filter templates from complete.mako

move all that instead to filter-components.mako

for now, base template does import + setup for the latter, "just in
case" a given view has any grids.  each grid should (still) be
isolated but no code should be duplicated now.  whereas before the
grid filter templates were in comlete.mako and hence could be declared
more than once if multiple grids are on a page
This commit is contained in:
Lance Edgar 2024-04-24 17:31:53 -05:00
parent d6fa83cd87
commit 9f984241c4
5 changed files with 329 additions and 303 deletions

View file

@ -1,167 +0,0 @@
const GridFilterNumericValue = {
template: '#grid-filter-numeric-value-template',
props: {
value: String,
wantsRange: Boolean,
},
data() {
return {
startValue: null,
endValue: null,
}
},
mounted() {
if (this.wantsRange) {
if (this.value.includes('|')) {
let values = this.value.split('|')
if (values.length == 2) {
this.startValue = values[0]
this.endValue = values[1]
} else {
this.startValue = this.value
}
} else {
this.startValue = this.value
}
} else {
this.startValue = this.value
}
},
watch: {
// when changing from e.g. 'equal' to 'between' filter verbs,
// must proclaim new filter value, to reflect (lack of) range
wantsRange(val) {
if (val) {
this.$emit('input', this.startValue + '|' + this.endValue)
} else {
this.$emit('input', this.startValue)
}
},
},
methods: {
focus() {
this.$refs.startValue.focus()
},
startValueChanged(value) {
if (this.wantsRange) {
value += '|' + this.endValue
}
this.$emit('input', value)
},
endValueChanged(value) {
value = this.startValue + '|' + value
this.$emit('input', value)
},
},
}
Vue.component('grid-filter-numeric-value', GridFilterNumericValue)
const GridFilterDateValue = {
template: '#grid-filter-date-value-template',
props: {
value: String,
dateRange: Boolean,
},
data() {
return {
startDate: null,
endDate: null,
}
},
mounted() {
if (this.dateRange) {
if (this.value.includes('|')) {
let values = this.value.split('|')
if (values.length == 2) {
this.startDate = values[0]
this.endDate = values[1]
} else {
this.startDate = this.value
}
} else {
this.startDate = this.value
}
} else {
this.startDate = this.value
}
},
methods: {
focus() {
this.$refs.startDate.focus()
},
startDateChanged(value) {
if (this.dateRange) {
value += '|' + this.endDate
}
this.$emit('input', value)
},
endDateChanged(value) {
value = this.startDate + '|' + value
this.$emit('input', value)
},
},
}
Vue.component('grid-filter-date-value', GridFilterDateValue)
const GridFilter = {
template: '#grid-filter-template',
props: {
filter: Object
},
methods: {
changeVerb() {
// set focus to value input, "as quickly as we can"
this.$nextTick(function() {
this.focusValue()
})
},
valuedVerb() {
/* this returns true if the filter's current verb should expose value input(s) */
// if filter has no "valueless" verbs, then all verbs should expose value inputs
if (!this.filter.valueless_verbs) {
return true
}
// if filter *does* have valueless verbs, check if "current" verb is valueless
if (this.filter.valueless_verbs.includes(this.filter.verb)) {
return false
}
// current verb is *not* valueless
return true
},
multiValuedVerb() {
/* this returns true if the filter's current verb should expose a multi-value input */
// if filter has no "multi-value" verbs then we safely assume false
if (!this.filter.multiple_value_verbs) {
return false
}
// if filter *does* have multi-value verbs, see if "current" is one
if (this.filter.multiple_value_verbs.includes(this.filter.verb)) {
return true
}
// current verb is not multi-value
return false
},
focusValue: function() {
this.$refs.valueInput.focus()
// this.$refs.valueInput.select()
}
}
}
Vue.component('grid-filter', GridFilter)

View file

@ -3,6 +3,7 @@
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" /> <%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
<%namespace name="base_meta" file="/base_meta.mako" /> <%namespace name="base_meta" file="/base_meta.mako" />
<%namespace file="/formposter.mako" import="declare_formposter_mixin" /> <%namespace file="/formposter.mako" import="declare_formposter_mixin" />
<%namespace file="/grids/filter-components.mako" import="make_grid_filter_components" />
<%namespace name="page_help" file="/page_help.mako" /> <%namespace name="page_help" file="/page_help.mako" />
<%namespace name="multi_file_upload" file="/multi_file_upload.mako" /> <%namespace name="multi_file_upload" file="/multi_file_upload.mako" />
<!DOCTYPE html> <!DOCTYPE html>
@ -90,7 +91,6 @@
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.numericinput.js') + '?ver={}'.format(tailbone.__version__))} ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.numericinput.js') + '?ver={}'.format(tailbone.__version__))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.oncebutton.js') + '?ver={}'.format(tailbone.__version__))} ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.oncebutton.js') + '?ver={}'.format(tailbone.__version__))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.timepicker.js') + '?ver={}'.format(tailbone.__version__))} ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.timepicker.js') + '?ver={}'.format(tailbone.__version__))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.grid.js') + '?ver={}'.format(tailbone.__version__))}
<script type="text/javascript"> <script type="text/javascript">
@ -896,6 +896,9 @@
</%def> </%def>
<%def name="make_whole_page_component()"> <%def name="make_whole_page_component()">
${make_grid_filter_components()}
${self.declare_whole_page_vars()} ${self.declare_whole_page_vars()}
${self.modify_whole_page_vars()} ${self.modify_whole_page_vars()}
${self.finalize_whole_page_vars()} ${self.finalize_whole_page_vars()}

View file

@ -1,131 +1,5 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<script type="text/x-template" id="grid-filter-numeric-value-template">
<div class="level">
<div class="level-left">
<div class="level-item">
<b-input v-model="startValue"
ref="startValue"
@input="startValueChanged">
</b-input>
</div>
<div v-show="wantsRange"
class="level-item">
and
</div>
<div v-show="wantsRange"
class="level-item">
<b-input v-model="endValue"
ref="endValue"
@input="endValueChanged">
</b-input>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="grid-filter-date-value-template">
<div class="level">
<div class="level-left">
<div class="level-item">
<tailbone-datepicker v-model="startDate"
ref="startDate"
@input="startDateChanged">
</tailbone-datepicker>
</div>
<div v-show="dateRange"
class="level-item">
and
</div>
<div v-show="dateRange"
class="level-item">
<tailbone-datepicker v-model="endDate"
ref="endDate"
@input="endDateChanged">
</tailbone-datepicker>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="grid-filter-template">
<div class="level filter" v-show="filter.visible">
<div class="level-left"
style="align-items: start;">
<div class="level-item filter-fieldname">
<b-field>
<b-checkbox-button v-model="filter.active" native-value="IGNORED">
<b-icon pack="fas" icon="check" v-show="filter.active"></b-icon>
<span>{{ filter.label }}</span>
</b-checkbox-button>
</b-field>
</div>
<b-field grouped v-show="filter.active"
class="level-item"
style="align-items: start;">
<b-select v-model="filter.verb"
@input="focusValue()"
class="filter-verb">
<option v-for="verb in filter.verbs"
:key="verb"
:value="verb">
{{ filter.verb_labels[verb] }}
</option>
</b-select>
## only one of the following "value input" elements will be rendered
<grid-filter-date-value v-if="filter.data_type == 'date'"
v-model="filter.value"
v-show="valuedVerb()"
:date-range="filter.verb == 'between'"
ref="valueInput">
</grid-filter-date-value>
<b-select v-if="filter.data_type == 'choice'"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
<option v-for="choice in filter.choices"
:key="choice"
:value="choice">
{{ filter.choice_labels[choice] || choice }}
</option>
</b-select>
<grid-filter-numeric-value v-if="filter.data_type == 'number'"
v-model="filter.value"
v-show="valuedVerb()"
:wants-range="filter.verb == 'between'"
ref="valueInput">
</grid-filter-numeric-value>
<b-input v-if="filter.data_type == 'string' && !multiValuedVerb()"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
</b-input>
<b-input v-if="filter.data_type == 'string' && multiValuedVerb()"
type="textarea"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
</b-input>
</b-field>
</div><!-- level-left -->
</div><!-- level -->
</script>
<script type="text/x-template" id="${grid.component}-template"> <script type="text/x-template" id="${grid.component}-template">
<div> <div>

View file

@ -0,0 +1,313 @@
## -*- coding: utf-8; -*-
<%def name="make_grid_filter_components()">
${self.make_grid_filter_numeric_value_component()}
${self.make_grid_filter_date_value_component()}
${self.make_grid_filter_component()}
</%def>
<%def name="make_grid_filter_numeric_value_component()">
<script type="text/x-template" id="grid-filter-numeric-value-template">
<div class="level">
<div class="level-left">
<div class="level-item">
<b-input v-model="startValue"
ref="startValue"
@input="startValueChanged">
</b-input>
</div>
<div v-show="wantsRange"
class="level-item">
and
</div>
<div v-show="wantsRange"
class="level-item">
<b-input v-model="endValue"
ref="endValue"
@input="endValueChanged">
</b-input>
</div>
</div>
</div>
</script>
<script>
const GridFilterNumericValue = {
template: '#grid-filter-numeric-value-template',
props: {
value: String,
wantsRange: Boolean,
},
data() {
return {
startValue: null,
endValue: null,
}
},
mounted() {
if (this.wantsRange) {
if (this.value.includes('|')) {
let values = this.value.split('|')
if (values.length == 2) {
this.startValue = values[0]
this.endValue = values[1]
} else {
this.startValue = this.value
}
} else {
this.startValue = this.value
}
} else {
this.startValue = this.value
}
},
watch: {
// when changing from e.g. 'equal' to 'between' filter verbs,
// must proclaim new filter value, to reflect (lack of) range
wantsRange(val) {
if (val) {
this.$emit('input', this.startValue + '|' + this.endValue)
} else {
this.$emit('input', this.startValue)
}
},
},
methods: {
focus() {
this.$refs.startValue.focus()
},
startValueChanged(value) {
if (this.wantsRange) {
value += '|' + this.endValue
}
this.$emit('input', value)
},
endValueChanged(value) {
value = this.startValue + '|' + value
this.$emit('input', value)
},
},
}
Vue.component('grid-filter-numeric-value', GridFilterNumericValue)
</script>
</%def>
<%def name="make_grid_filter_date_value_component()">
<script type="text/x-template" id="grid-filter-date-value-template">
<div class="level">
<div class="level-left">
<div class="level-item">
<tailbone-datepicker v-model="startDate"
ref="startDate"
@input="startDateChanged">
</tailbone-datepicker>
</div>
<div v-show="dateRange"
class="level-item">
and
</div>
<div v-show="dateRange"
class="level-item">
<tailbone-datepicker v-model="endDate"
ref="endDate"
@input="endDateChanged">
</tailbone-datepicker>
</div>
</div>
</div>
</script>
<script>
const GridFilterDateValue = {
template: '#grid-filter-date-value-template',
props: {
value: String,
dateRange: Boolean,
},
data() {
return {
startDate: null,
endDate: null,
}
},
mounted() {
if (this.dateRange) {
if (this.value.includes('|')) {
let values = this.value.split('|')
if (values.length == 2) {
this.startDate = values[0]
this.endDate = values[1]
} else {
this.startDate = this.value
}
} else {
this.startDate = this.value
}
} else {
this.startDate = this.value
}
},
methods: {
focus() {
this.$refs.startDate.focus()
},
startDateChanged(value) {
if (this.dateRange) {
value += '|' + this.endDate
}
this.$emit('input', value)
},
endDateChanged(value) {
value = this.startDate + '|' + value
this.$emit('input', value)
},
},
}
Vue.component('grid-filter-date-value', GridFilterDateValue)
</script>
</%def>
<%def name="make_grid_filter_component()">
<script type="text/x-template" id="grid-filter-template">
<div class="level filter" v-show="filter.visible">
<div class="level-left"
style="align-items: start;">
<div class="level-item filter-fieldname">
<b-field>
<b-checkbox-button v-model="filter.active" native-value="IGNORED">
<b-icon pack="fas" icon="check" v-show="filter.active"></b-icon>
<span>{{ filter.label }}</span>
</b-checkbox-button>
</b-field>
</div>
<b-field grouped v-show="filter.active"
class="level-item"
style="align-items: start;">
<b-select v-model="filter.verb"
@input="focusValue()"
class="filter-verb">
<option v-for="verb in filter.verbs"
:key="verb"
:value="verb">
{{ filter.verb_labels[verb] }}
</option>
</b-select>
## only one of the following "value input" elements will be rendered
<grid-filter-date-value v-if="filter.data_type == 'date'"
v-model="filter.value"
v-show="valuedVerb()"
:date-range="filter.verb == 'between'"
ref="valueInput">
</grid-filter-date-value>
<b-select v-if="filter.data_type == 'choice'"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
<option v-for="choice in filter.choices"
:key="choice"
:value="choice">
{{ filter.choice_labels[choice] || choice }}
</option>
</b-select>
<grid-filter-numeric-value v-if="filter.data_type == 'number'"
v-model="filter.value"
v-show="valuedVerb()"
:wants-range="filter.verb == 'between'"
ref="valueInput">
</grid-filter-numeric-value>
<b-input v-if="filter.data_type == 'string' && !multiValuedVerb()"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
</b-input>
<b-input v-if="filter.data_type == 'string' && multiValuedVerb()"
type="textarea"
v-model="filter.value"
v-show="valuedVerb()"
ref="valueInput">
</b-input>
</b-field>
</div><!-- level-left -->
</div><!-- level -->
</script>
<script>
const GridFilter = {
template: '#grid-filter-template',
props: {
filter: Object
},
methods: {
changeVerb() {
// set focus to value input, "as quickly as we can"
this.$nextTick(function() {
this.focusValue()
})
},
valuedVerb() {
/* this returns true if the filter's current verb should expose value input(s) */
// if filter has no "valueless" verbs, then all verbs should expose value inputs
if (!this.filter.valueless_verbs) {
return true
}
// if filter *does* have valueless verbs, check if "current" verb is valueless
if (this.filter.valueless_verbs.includes(this.filter.verb)) {
return false
}
// current verb is *not* valueless
return true
},
multiValuedVerb() {
/* this returns true if the filter's current verb should expose a multi-value input */
// if filter has no "multi-value" verbs then we safely assume false
if (!this.filter.multiple_value_verbs) {
return false
}
// if filter *does* have multi-value verbs, see if "current" is one
if (this.filter.multiple_value_verbs.includes(this.filter.verb)) {
return true
}
// current verb is not multi-value
return false
},
focusValue: function() {
this.$refs.valueInput.focus()
// this.$refs.valueInput.select()
}
}
}
Vue.component('grid-filter', GridFilter)
</script>
</%def>

View file

@ -299,6 +299,11 @@
% endif % endif
</%def> </%def>
<%def name="make_grid_component()">
## TODO: stop using |n filter?
${grid.render_complete(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())|n}
</%def>
<%def name="render_grid_component()"> <%def name="render_grid_component()">
<${grid.component} ref="grid" :csrftoken="csrftoken" <${grid.component} ref="grid" :csrftoken="csrftoken"
% if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple': % if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple':
@ -309,11 +314,16 @@
</%def> </%def>
<%def name="make_this_page_component()"> <%def name="make_this_page_component()">
## define grid
${self.make_grid_component()}
${parent.make_this_page_component()} ${parent.make_this_page_component()}
<script type="text/javascript">
${grid.component_studly}.data = function() { return ${grid.component_studly}Data } ## finalize grid
<script>
${grid.component_studly}.data = () => { return ${grid.component_studly}Data }
Vue.component('${grid.component}', ${grid.component_studly}) Vue.component('${grid.component}', ${grid.component_studly})
</script> </script>
@ -323,13 +333,6 @@
${self.page_content()} ${self.page_content()}
</%def> </%def>
<%def name="render_this_page_template()">
${parent.render_this_page_template()}
## TODO: stop using |n filter
${grid.render_complete(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())|n}
</%def>
<%def name="modify_this_page_vars()"> <%def name="modify_this_page_vars()">
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">