feat(treetable): optionale Kopfzeile und abschaltbare Expand-Buttons
Neues Attribut header rendert eine sticky Spaltenüberschriftenzeile. showExpandButtons=false blendet die Ein-/Ausklappen-Buttons aus; die Topbar entfällt dann ganz, wenn auch keine Children oder Filter vorhanden sind — der scrollbare Bereich nutzt in dem Fall die volle Höhe. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
3a6732e36b
commit
981b28df03
1 file changed
+68
-17
@@ -39,6 +39,20 @@ export interface iTreetable {
|
|||||||
* <tr>-Zeile; die columnmapper-Inhalte brauchen dann kein eigenes
|
* <tr>-Zeile; die columnmapper-Inhalte brauchen dann kein eigenes
|
||||||
* Selektions-Styling mehr. */
|
* Selektions-Styling mehr. */
|
||||||
isSelected?: (node: iTreenode) => boolean
|
isSelected?: (node: iTreenode) => boolean
|
||||||
|
/** Optional: Spaltenüberschriften (ein Titel je columnmapper-Spalte,
|
||||||
|
* 1:1 von links nach rechts). Wenn gesetzt, rendert das Widget eine
|
||||||
|
* Kopfzeile über den Datenzeilen. Hinweis zur ersten Spalte: Die
|
||||||
|
* Einrückung/der Expand-Pfeil der Baumdarstellung liegt im selben
|
||||||
|
* td wie die ERSTE columnmapper-Zelle — der erste Titel steht
|
||||||
|
* daher direkt über der ersten Datenspalte. Die Kopfzeile bleibt
|
||||||
|
* beim Scrollen sticky sichtbar. */
|
||||||
|
header?: string[]
|
||||||
|
/** Optional: Ein-/Ausklappen-Buttons (unfold_more/unfold_less) in der
|
||||||
|
* Topbar anzeigen. Default true. Für flache Listen ohne Hierarchie
|
||||||
|
* auf false setzen — die Topbar entfällt dann ganz, sofern auch
|
||||||
|
* keine Children (z.B. TopbarButtons) übergeben und kein filter
|
||||||
|
* gesetzt ist. */
|
||||||
|
showExpandButtons?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Treetable: m.ClosureComponent<iTreetable> = (vnode) => {
|
export const Treetable: m.ClosureComponent<iTreetable> = (vnode) => {
|
||||||
@@ -58,26 +72,63 @@ export const Treetable: m.ClosureComponent<iTreetable> = (vnode) => {
|
|||||||
const topHasSiblingsWithChildren = vnode.attrs.treenodes.some(
|
const topHasSiblingsWithChildren = vnode.attrs.treenodes.some(
|
||||||
(tn: iTreenode) => tn.hasChildren
|
(tn: iTreenode) => tn.hasChildren
|
||||||
)
|
)
|
||||||
|
// Topbar (Expand-Buttons, Children, Suchfeld) nur rendern, wenn
|
||||||
|
// tatsächlich Inhalt vorhanden ist — bei flachen Listen ohne
|
||||||
|
// Expand-Buttons, Children und filter bleibt sie komplett weg.
|
||||||
|
const showExpand = vnode.attrs.showExpandButtons !== false
|
||||||
|
// vnode.children kann ein Array oder ein Primitiv sein
|
||||||
|
// (Mithril-Typ ChildArrayOrPrimitive).
|
||||||
|
const hatChildren = Array.isArray(vnode.children)
|
||||||
|
? vnode.children.length > 0
|
||||||
|
: vnode.children != null
|
||||||
|
const hatTopbar = showExpand || hatChildren || vnode.attrs.filter != undefined
|
||||||
return m("div.w3-border-theme", {style: {height: '99%', overflow: 'hidden', 'border-style': 'solid', 'border-width': '1px'}},
|
return m("div.w3-border-theme", {style: {height: '99%', overflow: 'hidden', 'border-style': 'solid', 'border-width': '1px'}},
|
||||||
m("div.w3-bar.w3-theme",
|
hatTopbar
|
||||||
m(TopbarButton, {
|
? m("div.w3-bar.w3-theme",
|
||||||
onclick: () => {
|
showExpand
|
||||||
expandtoggle = true; changeTreenodes(vnode.attrs.treenodes, expandtoggle)
|
? [
|
||||||
}},
|
m(TopbarButton, {
|
||||||
m("i.material-icons", "unfold_more")
|
onclick: () => {
|
||||||
),
|
expandtoggle = true; changeTreenodes(vnode.attrs.treenodes, expandtoggle)
|
||||||
m(TopbarButton, {
|
}},
|
||||||
onclick: () => {
|
m("i.material-icons", "unfold_more")
|
||||||
expandtoggle = false; changeTreenodes(vnode.attrs.treenodes, expandtoggle)
|
),
|
||||||
}},
|
m(TopbarButton, {
|
||||||
m("i.material-icons", "unfold_less")
|
onclick: () => {
|
||||||
),
|
expandtoggle = false; changeTreenodes(vnode.attrs.treenodes, expandtoggle)
|
||||||
vnode.children,
|
}},
|
||||||
(vnode.attrs.filter != undefined) ? m(SearchField, {setSearchvalue: setSearchvalue} as iSearchfield) : null
|
m("i.material-icons", "unfold_less")
|
||||||
),
|
)
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
vnode.children,
|
||||||
|
(vnode.attrs.filter != undefined) ? m(SearchField, {setSearchvalue: setSearchvalue} as iSearchfield) : null
|
||||||
|
)
|
||||||
|
: null,
|
||||||
m("div", {style: {height: '100%'}},
|
m("div", {style: {height: '100%'}},
|
||||||
m("div", {style: {height: 'calc(100% - 32px', overflow: 'auto', width: '100%'}},
|
// Ohne Topbar nutzt der scrollbare Bereich die volle Höhe.
|
||||||
|
m("div", {style: {height: hatTopbar ? 'calc(100% - 32px' : '100%', overflow: 'auto', width: '100%'}},
|
||||||
m("table.w3-table", {style: {'overflow-y': 'auto'}},
|
m("table.w3-table", {style: {'overflow-y': 'auto'}},
|
||||||
|
// Optionale Spaltenüberschriften: Ein Titel je
|
||||||
|
// columnmapper-Spalte, 1:1 von links nach rechts
|
||||||
|
// (die Einrückung teilt sich das td mit der
|
||||||
|
// ersten Datenspalte, keine Führungs-Spalte).
|
||||||
|
// Die Kopfzeile bleibt beim Scrollen sticky.
|
||||||
|
vnode.attrs.header
|
||||||
|
? m("thead",
|
||||||
|
m("tr.w3-theme",
|
||||||
|
vnode.attrs.header.map((titel: string) =>
|
||||||
|
m("th", {
|
||||||
|
style: {
|
||||||
|
position: 'sticky',
|
||||||
|
top: '0',
|
||||||
|
'z-index': '1',
|
||||||
|
'text-align': 'left',
|
||||||
|
'white-space': 'nowrap'
|
||||||
|
},
|
||||||
|
title: titel
|
||||||
|
}, titel))))
|
||||||
|
: null,
|
||||||
vnode.attrs.treenodes.map((tn:iTreenode, z) => {
|
vnode.attrs.treenodes.map((tn:iTreenode, z) => {
|
||||||
if (vnode.attrs.filter == undefined || (vnode.attrs.filter != undefined && vnode.attrs.filter(tn, searchvalue) == true)) {
|
if (vnode.attrs.filter == undefined || (vnode.attrs.filter != undefined && vnode.attrs.filter(tn, searchvalue) == true)) {
|
||||||
return m(Treerow, {
|
return m(Treerow, {
|
||||||
|
|||||||
Reference in new issue
Block a user