Add Et2TabsMobile that has special rendering for tabs on mobile

This commit is contained in:
nathan 2023-02-21 10:31:38 -07:00
parent c466a4e9ab
commit e532ca176b
6 changed files with 134 additions and 34 deletions

View File

@ -413,31 +413,6 @@ function send_template()
if ($template === 'mobile')
{
// replace tabs in mobile template with details widgets
$str = preg_replace_callback('#^(\s+)<et2-tabbox\s*([^>]*)>\n\s*<tabs>\n(.*)\n\s*</tabs>\n\s*<tabpanels>\n(.*)\n\s*</tabpanels>\n\s*</et2-tabbox>#ms',
static function($matches)
{
$indent = $matches[1];
$tabbox_attrs = parseAttrs($matches[2]);
unset($tabbox_attrs['align_tabs']);
if (preg_match_all('#<tab\s(.*)/>#', $matches[3], $tabs) !==
preg_match_all('#<template\s(.*)/>#', $matches[4], $panels))
{
throw Exception("Error parsing tabbox for mobile template into details");
}
$details = [];
foreach($tabs[1] as $n => $tab)
{
$tab_attrs = parseAttrs($tab);
$tab_attrs['id'] = $tab_attrs['id'] ?? $tabbox_attrs['id'].$n;
$tab_attrs['summary'] = $tab_attrs['label'];
$tab_attrs['title'] = $tab_attrs['statustext'];
unset($tab_attrs['label'], $tab_attrs['statustext']);
$details[] = $indent."\t".'<et2-details'.stringAttrs($tab_attrs).'>'."\n$indent\t\t".$panels[0][$n]."\n$indent\t</et2-details>";
}
unset($tabbox_attrs['id']);
return $indent.'<vbox'.stringAttrs($tabbox_attrs).">\n".implode("\n", $details)."\n$indent</vbox>";
}, $str);
}
// ^^^^^^^^^^^^^^^^ above widgets get transformed independent of legacy="true" set in overlay ^^^^^^^^^^^^^^^^^^

View File

@ -1352,12 +1352,21 @@ export function loadWebComponent(_nodeName : string, _template_node : Element|{[
}
// Try to find the class for the given node
let mobile = (typeof egwIsMobile != "undefined" && egwIsMobile());
if(mobile && typeof window.customElements.get(_nodeName + "_mobile") != "undefined")
{
_nodeName += "_mobile";
}
let widget_class = window.customElements.get(_nodeName);
if(!widget_class)
{
// Given node has no registered class. Try some of our special things (remove type, fallback to actual node)
let tries = [_nodeName.split('-')[0]];
if (_template_node.nodeName) tries = tries.concat(_template_node.nodeName.toLowerCase());
if(_template_node.nodeName)
{
tries = tries.concat(_template_node.nodeName.toLowerCase());
}
for(let i = 0; i < tries.length && !window.customElements.get(_nodeName); i++)
{
_nodeName = tries[i];
@ -1365,6 +1374,7 @@ export function loadWebComponent(_nodeName : string, _template_node : Element|{[
widget_class = window.customElements.get(_nodeName);
if(!widget_class)
{
debugger;
throw Error("Unknown or unregistered WebComponent '" + _nodeName + "', could not find class. Also checked for " + tries.join(','));
}
}

View File

@ -315,10 +315,13 @@ export class Et2Tabs extends Et2InputWidget(SlTabGroup) implements et2_IResizeab
if(changedProperties.has("tabHeight"))
{
const body = this.shadowRoot.querySelector(".tab-group__body");
if(body)
{
body.style.setProperty("height", this.tabHeight == parseInt(this.tabHeight) + "" ? this.tabHeight + "px" : this.tabHeight);
body.classList.toggle("tab-group__body-fixed-height", this.tabHeight !== '');
}
}
}
/**
* Create the nodes for tabs

View File

@ -0,0 +1,108 @@
import {Et2Tabs} from "./Et2Tabs";
import {classMap, html, repeat, TemplateResult} from "@lion/core";
import {et2_createWidget} from "../../et2_core_widget";
import {et2_template} from "../../et2_widget_template";
import {Et2Details} from "../Et2Details/Et2Details";
import {SlTab, SlTabPanel} from "@shoelace-style/shoelace";
/**
* Widget to render tabs in a mobile-friendly way
*
* We render tabs as a series of details instead of normal tabs.
* loadWebComponent() will load this component instead of Et2Tabs on mobile browsers
*/
export class Et2TabsMobile extends Et2Tabs
{
connectedCallback()
{
super.connectedCallback();
this.nav = this.shadowRoot.querySelector("et2-vbox");
}
protected createTabs(tabData)
{
// "Tabs" are created in render()
this.tabData = tabData;
// Create tab panels here though
tabData.forEach((tab, index) =>
{
let panel = this.createPanel(tab, true);
panel.slot = tab.id;
});
}
getAllTabs(includeDisabled = false)
{
const slot = <Et2Details[]><unknown>this.shadowRoot.querySelectorAll('et2-details');
const tabNames = ["et2-details"];
// It's really not a list of SlTab...
return <SlTab[]><unknown>[...slot].filter((el) =>
{
return includeDisabled ? tabNames.indexOf(el.tagName.toLowerCase()) != -1 : tabNames.indexOf(el.tagName.toLowerCase()) !== -1 && !el.disabled;
});
}
getAllPanels()
{
const slot = this.querySelector('slot')!;
return <[SlTabPanel]><unknown>[...this.querySelectorAll('et2-tab-panel')]
}
syncIndicator()
{
// Don't have an indicator to sync
}
protected tabTemplate(tab, index : number) : TemplateResult
{
if(tab.XMLNode)
{
// Just read the XMLNode
let tabContent = this.createElementFromNode(tab.XMLNode);
tabContent.getDOMNode().slot = tab.id;
}
else
{
let template = <et2_template>et2_createWidget('template', tab.widget_options, this);
template.getDOMNode().slot = tab.id;
}
return html`
<et2-details
id="${tab.id}"
summary="${tab.label}"
?open=${index == this._selectedIndex}
?disabled=${tab.disabled}
?hidden=${tab.hidden}
>
<slot name="${tab.id}"/>
</et2-details>`
}
render()
{
return html`
<et2-vbox
part="base"
class=${classMap({
'tab-group': true,
'tab-group-mobile': true,
// Get styling as if it were top
'tab-group--top': true
})}
@click=${this.handleClick}
@keydown=${this.handleKeyDown}
>
${repeat(this.tabData, this.tabTemplate.bind(this))}
<slot>
</et2-vbox>
`;
}
}
if(typeof customElements.get("et2-tabbox_mobile") == "undefined")
{
customElements.define("et2-tabbox_mobile", Et2TabsMobile);
}

View File

@ -27,6 +27,7 @@ import './Layout/Et2Details/Et2Details';
import './Layout/Et2Tabs/Et2Tab';
import './Layout/Et2Tabs/Et2Tabs';
import './Layout/Et2Tabs/Et2TabPanel';
import './Layout/Et2Tabs/Et2TabsMobile';
import './Et2Avatar/Et2Avatar';
import './Et2Avatar/Et2AvatarGroup';
import './Et2Button/Et2Button';

View File

@ -43,11 +43,14 @@
</et2-hbox>
</row>
<row>
<et2-vbox>
<et2-details id="tab1" summary="Settings" title="">
<et2-tabbox id="tabs">
<tabs>
<tab id="tab1" label="Settings"/>
</tabs>
<tabpanels>
<template id="preferences.settings.tab1" content="tab1"/>
</et2-details>
</et2-vbox>
</tabpanels>
</et2-tabbox>
</row>
</rows>
</grid>