Form-Widget stickyButtons und einheitliches Form-View-Layout

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>
This commit is contained in:
gg committed 2026-08-31 20:10:54 +02:00
1 parent 3accb8f690
commit 28f858f6da
6 files changed
+89 -27

No files matched your search

+49 -7
View File
@@ -6,6 +6,10 @@ export interface iForm {
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
}
@@ -14,6 +18,8 @@ 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
@@ -74,6 +80,48 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
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,
@@ -88,13 +136,7 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
}
},
vnode.children,
showButtons == true ?
m("div.w3-bar.w3-padding-16",[
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
buttons
)
}
}