Form-Widget um stickyButtons-Attribut erweitert (default true): nur der Formularinhalt scrollt, die Buttons bleiben fixiert am unteren Rand. Neue CSS-Klasse .form-view-container (flex-Column, volle Höhe) für die umschließenden View-Container. Form-Views (kunde, projekt, call_log, tags) auf w3-auto.form-view-container umgestellt; Tag-Form von w3-twothird auf w3-auto korrigiert. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
154 lines
6.3 KiB
TypeScript
154 lines
6.3 KiB
TypeScript
import m from "mithril"
|
|
|
|
export interface iForm {
|
|
|
|
onchanged?: (e:Event) => void
|
|
onsubmit: (form:HTMLFormElement) => boolean
|
|
oncancel: (e:Event) => void
|
|
showbuttons?: boolean
|
|
/** Wenn true (Default), bleiben die Buttons am unteren Rand fixiert,
|
|
* während der Formularinhalt scrollt. Bei false scrollen die Buttons
|
|
* mit dem Formular. */
|
|
stickyButtons?: boolean
|
|
/** Die Funktion gibt das HTMLFormElement zurück, welches in der Form-Componente angelegt wird */
|
|
onformcreate?: (form:HTMLFormElement) => void
|
|
}
|
|
|
|
export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
|
let form: HTMLFormElement
|
|
let isDirty: boolean = false
|
|
let showButtons: boolean = vnode.attrs.showbuttons == undefined ? true : vnode.attrs.showbuttons
|
|
// Default: Buttons bleiben fixiert am unteren Rand (stickyButtons = true).
|
|
let stickyButtons: boolean = vnode.attrs.stickyButtons == undefined ? true : vnode.attrs.stickyButtons
|
|
function canCloseForm(typ: string = "") {
|
|
if (!isDirty) {
|
|
return true
|
|
}
|
|
let text = (typ == "reset") ? "zurücksetzen" : "beenden"
|
|
return confirm (`Das Formular enthält noch ungespeicherte Eingaben. Wollen Sie das Formular trotzdem ${text}?`)
|
|
}
|
|
function onchanged (e:Event) {
|
|
isDirty = true
|
|
if (vnode.attrs.onchanged != undefined) {
|
|
vnode.attrs.onchanged(e)
|
|
}
|
|
}
|
|
function submit (e:Event) {
|
|
e.preventDefault()
|
|
if (vnode.attrs.onsubmit(form) == true) {
|
|
isDirty = false
|
|
}
|
|
}
|
|
function registerFormElement (f:HTMLFormElement) {
|
|
if (vnode.attrs.onformcreate != undefined) {
|
|
vnode.attrs.onformcreate(f)
|
|
}
|
|
}
|
|
function cancel(e:Event) {
|
|
e.preventDefault()
|
|
if (canCloseForm() == true) {
|
|
isDirty = false
|
|
if (vnode.attrs.oncancel != undefined) {
|
|
vnode.attrs.oncancel(e)
|
|
}
|
|
else {
|
|
history.back()
|
|
}
|
|
}
|
|
else {
|
|
e['redraw'] = false
|
|
}
|
|
}
|
|
function reset(e:Event) {
|
|
if (canCloseForm("reset")) {
|
|
isDirty = false
|
|
// Hier passiert nichts.
|
|
// Da mithril nach jedem User-Event (wie z.B. einem 'Click') ein redraw() ausführt, wird hier die Form
|
|
// neu gezeichnet und zwar mit den ursprünglichen Daten aus den zu grundeliegenden Datenmodel, das ja
|
|
// ohne submit noch nicht geändert wurde.
|
|
return
|
|
}
|
|
e['redraw'] = false
|
|
return
|
|
}
|
|
return {
|
|
onremove: (vnode) => {
|
|
let message = "remove"
|
|
if (isDirty) {
|
|
message += " isDirty"
|
|
}
|
|
console.log(message)
|
|
},
|
|
view: (vnode) => {
|
|
const buttons = showButtons == true
|
|
? m("div.w3-bar.w3-padding-16",
|
|
{
|
|
style: stickyButtons
|
|
? {flex: '0 0 auto', position: 'sticky', bottom: '0', 'background-color': 'white'}
|
|
: {}
|
|
},
|
|
[
|
|
m("button.w3-button.w3-theme.w3-right.w3-mobile", {style: {margin: '0px 0px 2px 4px'}, type: "submit"}, "Speichern"),
|
|
m("button.w3-button.w3-theme.w3-right.w3-mobile", {style: {margin: '0px 0px 2px 4px'}, onclick: cancel, type: "button"}, "Abbrechen"),
|
|
m("button.w3-button.w3-theme.w3-right.w3-mobile", {onclick: reset, type: "button"}, "Zurücksetzen")
|
|
])
|
|
: null
|
|
|
|
// Bei stickyButtons liegt der Formularinhalt in einem scrollbaren
|
|
// Container, die Buttons bleiben am unteren Rand fixiert. Das
|
|
// umschließende form-Element wird zum flex-Container, damit das
|
|
// Layout auch bei Fenstergrößenänderung stabil bleibt.
|
|
if (stickyButtons) {
|
|
return m("form", {
|
|
style: {display: 'flex', 'flex-direction': 'column', height: '100%'},
|
|
onreset: () => {console.log("reset")},
|
|
onsubmit: submit,
|
|
oncreate: (vnode) => {
|
|
form = vnode.dom as HTMLFormElement
|
|
let elements = form.querySelectorAll("input,textarea")
|
|
for (let index = 0; index < elements.length; index++) {
|
|
const element = elements[index];
|
|
element.addEventListener("change", onchanged)
|
|
}
|
|
registerFormElement(form)
|
|
}
|
|
},
|
|
// Scrollbarer Formular-Inhalt.
|
|
m("div", {style: {flex: '1', 'overflow-y': 'auto', 'padding-right': '8px'}},
|
|
vnode.children
|
|
),
|
|
// Fixierter Button-Bereich.
|
|
buttons
|
|
)
|
|
}
|
|
// Ohne stickyButtons: bisheriges Verhalten (Buttons scrollen mit).
|
|
return m("form", {
|
|
onreset: () => {console.log("reset")},
|
|
onsubmit: submit,
|
|
oncreate: (vnode) => {
|
|
form = vnode.dom as HTMLFormElement
|
|
let elements = form.querySelectorAll("input,textarea")
|
|
for (let index = 0; index < elements.length; index++) {
|
|
const element = elements[index];
|
|
element.addEventListener("change", onchanged)
|
|
}
|
|
registerFormElement(form)
|
|
}
|
|
},
|
|
vnode.children,
|
|
buttons
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface iFormBorder {
|
|
maxwidth: string
|
|
}
|
|
export const FormBorder: m.Component<iFormBorder> = {
|
|
view: (vnode) => {
|
|
let mw:string = vnode.attrs.maxwidth === undefined ? "600px" : vnode.attrs.maxwidth
|
|
return m("div.w3-border.w3-border-theme.w3-container.w3-content.w3-margin-top",
|
|
{style: {'max-width': mw, 'box-shadow': '3px 3px 3px darkgrey'}}, vnode.children)
|
|
}
|
|
} |