mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
Add Et2TabsMobile that has special rendering for tabs on mobile
This commit is contained in:
parent
c466a4e9ab
commit
e532ca176b
@ -413,31 +413,6 @@ function send_template()
|
|||||||
|
|
||||||
if ($template === 'mobile')
|
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 ^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^ above widgets get transformed independent of legacy="true" set in overlay ^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -1352,12 +1352,21 @@ export function loadWebComponent(_nodeName : string, _template_node : Element|{[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to find the class for the given node
|
// 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);
|
let widget_class = window.customElements.get(_nodeName);
|
||||||
if(!widget_class)
|
if(!widget_class)
|
||||||
{
|
{
|
||||||
// Given node has no registered class. Try some of our special things (remove type, fallback to actual node)
|
// Given node has no registered class. Try some of our special things (remove type, fallback to actual node)
|
||||||
let tries = [_nodeName.split('-')[0]];
|
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++)
|
for(let i = 0; i < tries.length && !window.customElements.get(_nodeName); i++)
|
||||||
{
|
{
|
||||||
_nodeName = tries[i];
|
_nodeName = tries[i];
|
||||||
@ -1365,6 +1374,7 @@ export function loadWebComponent(_nodeName : string, _template_node : Element|{[
|
|||||||
widget_class = window.customElements.get(_nodeName);
|
widget_class = window.customElements.get(_nodeName);
|
||||||
if(!widget_class)
|
if(!widget_class)
|
||||||
{
|
{
|
||||||
|
debugger;
|
||||||
throw Error("Unknown or unregistered WebComponent '" + _nodeName + "', could not find class. Also checked for " + tries.join(','));
|
throw Error("Unknown or unregistered WebComponent '" + _nodeName + "', could not find class. Also checked for " + tries.join(','));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,8 +315,11 @@ export class Et2Tabs extends Et2InputWidget(SlTabGroup) implements et2_IResizeab
|
|||||||
if(changedProperties.has("tabHeight"))
|
if(changedProperties.has("tabHeight"))
|
||||||
{
|
{
|
||||||
const body = this.shadowRoot.querySelector(".tab-group__body");
|
const body = this.shadowRoot.querySelector(".tab-group__body");
|
||||||
body.style.setProperty("height", this.tabHeight == parseInt(this.tabHeight) + "" ? this.tabHeight + "px" : this.tabHeight);
|
if(body)
|
||||||
body.classList.toggle("tab-group__body-fixed-height", this.tabHeight !== '');
|
{
|
||||||
|
body.style.setProperty("height", this.tabHeight == parseInt(this.tabHeight) + "" ? this.tabHeight + "px" : this.tabHeight);
|
||||||
|
body.classList.toggle("tab-group__body-fixed-height", this.tabHeight !== '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts
Normal file
108
api/js/etemplate/Layout/Et2Tabs/Et2TabsMobile.ts
Normal 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);
|
||||||
|
}
|
@ -27,6 +27,7 @@ import './Layout/Et2Details/Et2Details';
|
|||||||
import './Layout/Et2Tabs/Et2Tab';
|
import './Layout/Et2Tabs/Et2Tab';
|
||||||
import './Layout/Et2Tabs/Et2Tabs';
|
import './Layout/Et2Tabs/Et2Tabs';
|
||||||
import './Layout/Et2Tabs/Et2TabPanel';
|
import './Layout/Et2Tabs/Et2TabPanel';
|
||||||
|
import './Layout/Et2Tabs/Et2TabsMobile';
|
||||||
import './Et2Avatar/Et2Avatar';
|
import './Et2Avatar/Et2Avatar';
|
||||||
import './Et2Avatar/Et2AvatarGroup';
|
import './Et2Avatar/Et2AvatarGroup';
|
||||||
import './Et2Button/Et2Button';
|
import './Et2Button/Et2Button';
|
||||||
|
@ -43,12 +43,15 @@
|
|||||||
</et2-hbox>
|
</et2-hbox>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<et2-vbox>
|
<et2-tabbox id="tabs">
|
||||||
<et2-details id="tab1" summary="Settings" title="">
|
<tabs>
|
||||||
<template id="preferences.settings.tab1" content="tab1"/>
|
<tab id="tab1" label="Settings"/>
|
||||||
</et2-details>
|
</tabs>
|
||||||
</et2-vbox>
|
<tabpanels>
|
||||||
</row>
|
<template id="preferences.settings.tab1" content="tab1"/>
|
||||||
|
</tabpanels>
|
||||||
|
</et2-tabbox>
|
||||||
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
</grid>
|
</grid>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user