numerus/web/template/form.gohtml

183 lines
7.6 KiB
Plaintext
Raw Normal View History

2023-02-11 21:16:48 +00:00
{{ define "hidden-field" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InputField*/ -}}
<input type="hidden" name="{{ .Name }}"
2023-02-11 21:16:48 +00:00
{{- range $attribute := .Attributes }} {{$attribute}} {{ end }}
value="{{ .Val }}">
2023-02-11 21:16:48 +00:00
{{- end }}
{{ define "input-field" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InputField*/ -}}
<div class="input {{ if .Errors }}has-errors{{ end }}">
{{ if eq .Type "textarea" }}
<textarea name="{{ .Name }}" id="{{ .Name }}-field"
{{- range $attribute := .Attributes }} {{$attribute}} {{ end }}
{{ if .Required }}required="required"{{ end }} placeholder="{{ .Label }}"
>{{ .Val }}</textarea>
{{ else }}
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Name }}-field"
{{- range $attribute := .Attributes }} {{$attribute}} {{ end }}
{{ if .Required }}required="required"{{ end }} value="{{ .Val }}" placeholder="{{ .Label }}">
{{ end }}
<label for="{{ .Name }}-field">{{ .Label }}</label>
{{- if .Errors }}
<ul>
{{- range $error := .Errors }}
<li>{{ . }}</li>
{{- end }}
</ul>
{{- end }}
</div>
{{- end }}
{{ define "hidden-select-field" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.SelectField*/ -}}
{{- range $selected := .Selected }}
<input type="hidden" name="{{ $.Name }}"
{{- range $attribute := $.Attributes }} {{$attribute}} {{ end }}
value="{{ . }}">
{{- end }}
{{- end }}
{{ define "select-field" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.SelectField*/ -}}
<div class="input{{ if .Errors }} has-errors{{ end }}"
{{- if .Multiple }}
@focusin.window="! $el.contains($event.target) && close() "
@click.away="close()"
x-data="{
options: [],
name: '',
filter: '',
open: false,
init() {
const select = $el.querySelector('select');
this.name = select.name;
for (const option of select.options) {
this.options.push({
value: option.value,
label: option.innerText,
search: this.normalize(option.innerText),
selected: option.getAttribute('selected') !== null,
});
}
select.remove();
const template = $el.querySelector('template');
const input = template.content.querySelector('input');
const label = $el.querySelector('label');
input.after(label);
},
selected() {
return this.options.filter(option => option.selected);
},
normalize(s) {
return s.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase();
},
unselected() {
const filter = this.normalize(this.filter);
return this.options.filter(option => !option.selected && (filter === '' || option.search.includes(filter)));
},
empty() {
return this.selected().length === 0;
},
selectOption(option) {
option.selected = true;
this.filter = '';
},
selectFirst() {
if (!this.open) {
return;
}
const all = this.unselected();
if (all.length === 0) {
return;
}
this.selectOption(all[0]);
},
unselectLast() {
if (this.filter !== '') {
return;
}
const all = this.selected();
if (all.length === 0) {
return;
}
all[all.length - 1].selected = false;
},
toggle() {
if (this.open) {
return this.close();
}
this.open = true;
},
close() {
this.open = false;
},
}"
{{- end -}}
>
<select id="{{ .Name }}-field" name="{{ .Name }}"
{{- range $attribute := .Attributes }} {{$attribute}} {{ end -}}
{{ if .Multiple }} multiple="multiple"{{ end -}}
{{ if .Required }} required="required"{{ end -}}
>
{{- with .EmptyLabel }}
<option value="">{{ . }}</option>
{{- end}}
{{- $withinGroup := "" -}}
{{- range $option := .Options }}
{{- if ne .Group $withinGroup }}
{{- if ne $withinGroup "" }}
</optgroup>
{{ end }}
<optgroup label="{{ .Group }}">
{{- $withinGroup = .Group -}}
{{ end }}
<option value="{{ .Value }}"
{{- if $.IsSelected .Value }} selected="selected"{{ end }}>{{ .Label }}</option>
{{- end }}
{{- if ne $withinGroup "" }}
</optgroup>
{{- end }}
</select>
{{- if .Multiple }}
<template x-if="true">
<div class="multiselect">
<div class="tags" :class="{'empty': empty()}"
@click="toggle()"
>
<template x-for="option in selected()">
<div class="tag">
<input type="hidden" :name="name" :value="option.value">
<span x-text="option.label"></span>
<button type="button" @click.stop="option.selected = false">×</button>
</div>
</template>
<input id="{{ .Name }}-field" :placeholder="empty() && '{{ .Label }}'" x-model="filter"
@input="open = true"
@keydown.escape="close()"
@keydown.prevent.enter="selectFirst()"
@keydown.backspace="unselectLast()"
>
</div>
<ul class="options" x-show.transition="open">
<template x-for="option in unselected()">
<li
x-text="option.label"
@click.stop="selectOption(option)"
></li>
</template>
</ul>
</div>
</template>
{{ end -}}
<label for="{{ .Name }}-field">{{ .Label }}</label>
{{- if .Errors }}
<ul>
{{- range $error := .Errors }}
<li>{{ . }}</li>
{{- end }}
</ul>
{{- end }}
</div>
{{- end }}