330 lines
13 KiB
PHP
330 lines
13 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace zin;
|
|
|
|
require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
|
|
require_once dirname(__DIR__) . DS . 'sidebar' . DS . 'v1.php';
|
|
require_once dirname(__DIR__) . DS . 'dropdown' . DS . 'v1.php';
|
|
|
|
class moduleMenu extends wg
|
|
{
|
|
private static array $filterMap = array();
|
|
|
|
protected static array $defineProps = array(
|
|
'modules: array',
|
|
'activeKey?: int|string',
|
|
'settingLink?: string',
|
|
'settingApp?: string=""',
|
|
'closeLink: string',
|
|
'showDisplay?: bool=true',
|
|
'allText?: string',
|
|
'title?: string',
|
|
'titleShow?: bool=true',
|
|
'app?: string=""',
|
|
'checkbox?: bool',
|
|
'preserve?: string|bool',
|
|
'tree?: array',
|
|
'checkOnClick?: bool|string',
|
|
'appendSettingItems?: array',
|
|
'onCheck?: function',
|
|
'toggleSidebar?: bool=true',
|
|
'isInModal?: bool=false'
|
|
);
|
|
|
|
protected static array $defineBlocks = array
|
|
(
|
|
'header' => array(),
|
|
'footer' => array()
|
|
);
|
|
|
|
public static function getPageCSS(): ?string
|
|
{
|
|
return <<<'CSS'
|
|
.module-menu {max-height: calc(100vh - 79px); display: flex; flex-direction: column; min-height: 32px; --menu-selected-bg: none;}
|
|
.module-menu header a:hover > .icon {color: var(--color-primary-600) !important;}
|
|
.module-menu .tree-item * {white-space: nowrap;}
|
|
.module-menu .tree-item .item-content {color: var(--color-gray-700)}
|
|
.module-menu .tree-item > .selected .item-content {color: var(--color-fore)}
|
|
.module-menu > .tree.has-nested-items {padding-left: calc(2 * var(--space))}
|
|
.has-module-menu-header #mainMenu {padding-left: 180px;}
|
|
.module-menu-header.is-fixed {position: absolute; left: 0; top: -44px; width: 160px; height: 32px; border: 1px solid var(--color-border); justify-content: center; padding: 0 24px; border-right: 0;}
|
|
.module-menu-header.is-fixed::before,
|
|
.module-menu-header.is-fixed::after {content: ''; position: absolute; top: 0; right: -12px; width: 0; height: 0; border-style: solid; border-color: transparent transparent transparent var(--color-border); border-width: 15px 0 15px 12px;}
|
|
.module-menu-header.is-fixed::after {right: -11px; border-color: transparent transparent transparent var(--color-canvas);}
|
|
.has-module-menu-header.is-sidebar-left-collapsed .module-menu-header.is-fixed {left: var(--gutter-width)}
|
|
.module-menu-header.is-fixed .module-title {font-size: var(--font-size-base);}
|
|
.module-menu-header.is-fixed > .btn-close {position: absolute; right: 0; font-weight: normal;}
|
|
.module-menu-header.is-fixed > .btn-close:not(:hover) {opacity: .5;}
|
|
.sidebar > .module-menu-header.is-fixed {display: flex!important;}
|
|
.sidebar-left > .module-menu {margin-right: -8px}
|
|
.sidebar-left.is-expanded > .module-menu ~ .sidebar-gutter {margin-left: 4px}
|
|
.sidebar-right.is-expanded > .module-menu ~ .sidebar-gutter {margin-right: 4px}
|
|
.is-expanded > .module-menu ~ .sidebar-gutter > .gutter-toggle {opacity: 0}
|
|
.has-module-menu-header .sidebar-left {transition-property: width;}
|
|
.has-module-menu-header .module-menu {max-height: calc(100vh - 105px); }
|
|
.has-module-menu-header .module-menu > .tree {padding-top: 8px; padding-bottom: 8px;}
|
|
CSS;
|
|
}
|
|
|
|
private array $modules = array();
|
|
|
|
private function buildMenuTree(int|string $parentID = 0): array
|
|
{
|
|
$children = zget($this->modules, $parentID, []);
|
|
if(count($children) === 0) return [];
|
|
|
|
global $app;
|
|
$activeKey = $this->prop('activeKey');
|
|
$treeItems = array();
|
|
$tab = $this->prop('app') ? $this->prop('app') : $app->tab;
|
|
$titleAttrs = array('data-app' => $tab);
|
|
if(isInModal()) $titleAttrs['data-load'] = 'modal';
|
|
|
|
foreach($children as $child)
|
|
{
|
|
$item = array(
|
|
'key' => $child->id,
|
|
'text' => $child->name,
|
|
'hint' => is_array($child->name) ? (isset($child->name['text']) ? $child->name['text'] : current($child->name)) : $child->name,
|
|
'url' => zget($child, 'url', ''),
|
|
'titleAttrs' => $titleAttrs,
|
|
'contentClass' => 'overflow-x-hidden'
|
|
);
|
|
$items = $this->buildMenuTree($child->id);
|
|
if($items) $item['items'] = $items;
|
|
if($child->id == $activeKey || $child->id == 'product-' . $activeKey)
|
|
{
|
|
$itemKey = $this->prop('checkbox') ? 'checked' : 'selected';
|
|
$item[$itemKey] = true;
|
|
}
|
|
$treeItems[] = $item;
|
|
}
|
|
|
|
return $treeItems;
|
|
}
|
|
|
|
private function setMenuTreeProps(): void
|
|
{
|
|
$modules = $this->prop('modules');
|
|
if($modules)
|
|
{
|
|
foreach($modules as $module) $this->modules[$module->parent][] = $module;
|
|
}
|
|
|
|
$this->setProp('items', $this->buildMenuTree());
|
|
}
|
|
|
|
private function getTitle(): string
|
|
{
|
|
if($this->prop('title')) return $this->prop('title');
|
|
|
|
global $lang;
|
|
$activeKey = $this->prop('activeKey');
|
|
|
|
if(empty($activeKey))
|
|
{
|
|
$allText = $this->prop('allText');
|
|
if(empty($allText)) return $lang->all;
|
|
return $allText;
|
|
}
|
|
|
|
$modules = $this->prop('modules');
|
|
$moduleName = '';
|
|
if($modules) array_map(function($module) use(&$moduleName, $activeKey) { if($module->id == $activeKey || $module->id == 'product-' . $activeKey) $moduleName = $module->name; }, $modules);
|
|
|
|
return $moduleName;
|
|
}
|
|
|
|
private function buildActions(): node|array|null
|
|
{
|
|
$settingLink = $this->prop('settingLink');
|
|
$showDisplay = $this->prop('showDisplay');
|
|
$appendSettingItems = $this->prop('appendSettingItems');
|
|
$isInModal = $this->prop('isInModal');
|
|
if(!$settingLink && !$showDisplay && !$appendSettingItems) return null;
|
|
|
|
global $app;
|
|
$lang = $app->loadLang('datatable')->datatable;
|
|
|
|
$items = array();
|
|
if($settingLink && common::hasPriv('tree', 'browse'))
|
|
{
|
|
$tab = $this->prop('settingApp', $app->tab);
|
|
$settingText = $this->prop('settingText', $lang->moduleSetting);
|
|
|
|
if(empty($this->prop('items')))
|
|
{
|
|
$items[] = btn
|
|
(
|
|
setClass('m-4 mt-0'),
|
|
set::text($settingText),
|
|
set::url($settingLink),
|
|
set::type('primary-pale'),
|
|
setData('app', $tab),
|
|
$isInModal ? setData(array('toggle' => 'modal', 'size' => 'md')) : null
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$items[] = array
|
|
(
|
|
'text' => $settingText,
|
|
'url' => $settingLink,
|
|
'data-app' => $tab,
|
|
'data-toggle' => $isInModal ? 'modal' : '',
|
|
'data-size' => $isInModal ? 'md' : ''
|
|
);
|
|
}
|
|
}
|
|
if($showDisplay)
|
|
{
|
|
$datatableId = $app->moduleName . ucfirst($app->methodName);
|
|
$currentModule = $app->rawModule;
|
|
$currentMethod = $app->rawMethod;
|
|
|
|
if(empty($this->prop('items')))
|
|
{
|
|
$items[] = btn
|
|
(
|
|
setClass('m-4 mt-0'),
|
|
set::text($lang->displaySetting),
|
|
set::url(createLink('datatable', 'ajaxDisplay', "datatableId=$datatableId&moduleName=$app->moduleName&methodName=$app->methodName¤tModule=$currentModule¤tMethod=$currentMethod")),
|
|
set::type('primary-pale'),
|
|
setData(array('toggle' => 'modal', 'size' => 'md'))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$items[] = array
|
|
(
|
|
'text' => $lang->displaySetting,
|
|
'url' => createLink('datatable', 'ajaxDisplay', "datatableId=$datatableId&moduleName=$app->moduleName&methodName=$app->methodName¤tModule=$currentModule¤tMethod=$currentMethod"),
|
|
'data-toggle' => 'modal',
|
|
'data-size' => 'md'
|
|
);
|
|
}
|
|
}
|
|
if($appendSettingItems)
|
|
{
|
|
if(empty($this->prop('items')))
|
|
{
|
|
foreach($appendSettingItems as $item)
|
|
{
|
|
$items[] = btn(setClass('m-4 mt-0'), set::type('primary-pale'), set($item));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$items = array_merge($items, $appendSettingItems);
|
|
}
|
|
}
|
|
|
|
if(empty($items)) return null;
|
|
if(empty($this->prop('items'))) return $items;
|
|
|
|
return new dropdown
|
|
(
|
|
new btn
|
|
(
|
|
set::type('ghost'),
|
|
set::icon('cog-outline'),
|
|
set::size('sm'),
|
|
set::caret(false)
|
|
),
|
|
set::items($items),
|
|
set::placement('top-end')
|
|
);
|
|
}
|
|
|
|
private function buildCloseBtn(): node|null
|
|
{
|
|
$closeLink = $this->prop('closeLink');
|
|
$tab = $this->prop('app');
|
|
$titleAttrs = array();
|
|
if($tab) $titleAttrs['app'] = $tab;
|
|
if(isInModal()) $titleAttrs['load'] = 'modal';
|
|
if(!$closeLink) return null;
|
|
|
|
$activeKey = $this->prop('activeKey');
|
|
if(empty($activeKey)) return null;
|
|
|
|
return btn
|
|
(
|
|
setClass('btn-close rounded-full'),
|
|
set::icon('close'),
|
|
set::url($closeLink),
|
|
set::size('sm'),
|
|
set::type('ghost'),
|
|
$titleAttrs ? setData($titleAttrs) : null
|
|
);
|
|
}
|
|
|
|
protected function build(): array
|
|
{
|
|
global $app;
|
|
$this->setMenuTreeProps();
|
|
|
|
$title = $this->getTitle();
|
|
$userTreeProps = $this->prop('tree');
|
|
$treeProps = $this->props->pick(array('items', 'activeClass', 'activeIcon', 'activeKey', 'onClickItem', 'defaultNestedShow', 'changeActiveKey', 'isDropdownMenu', 'checkbox', 'checkOnClick', 'onCheck'));
|
|
$preserve = $this->prop('preserve', $app->rawModule . '-' . $app->rawMethod);
|
|
$isInSidebar = $this->parent instanceof sidebar;
|
|
$titleShow = $this->prop('titleShow');
|
|
if(!is_null($this->prop('filterMap'))) static::$filterMap = $this->prop('filterMap');
|
|
|
|
$header = $titleShow ? h::header
|
|
(
|
|
setClass('module-menu-header h-10 flex items-center pl-4 flex-none gap-3', $isInSidebar ? 'is-fixed rounded rounded-r-none canvas' : ''),
|
|
span
|
|
(
|
|
setClass('module-title text-lg font-semibold clip'),
|
|
$title
|
|
),
|
|
$this->buildCloseBtn()
|
|
) : null;
|
|
|
|
$actions = $this->buildActions();
|
|
$hasActionDropdown = $actions && $actions instanceof dropdown;
|
|
$hasToggleBtn = $this->prop('toggleSidebar');
|
|
|
|
return array
|
|
(
|
|
$isInSidebar ? $header : null,
|
|
div
|
|
(
|
|
setID('moduleMenu'),
|
|
setClass('module-menu shadow ring rounded bg-canvas col relative'),
|
|
$this->block('header'),
|
|
$isInSidebar ? null : $header,
|
|
zui::tree
|
|
(
|
|
set::_tag('menu'),
|
|
set::_class('tree tree-lines col flex-auto scrollbar-hover overflow-y-auto overflow-x-hidden pr-2 pl-4'),
|
|
set::defaultNestedShow(true),
|
|
set::hover(true),
|
|
set::lines(true),
|
|
set::preserve($preserve),
|
|
set($treeProps),
|
|
set($userTreeProps)
|
|
),
|
|
$hasActionDropdown ? null : $actions,
|
|
$this->block('footer'),
|
|
($hasActionDropdown || $hasToggleBtn) ? row
|
|
(
|
|
setClass('justify-end p-1 flex-none'),
|
|
$hasActionDropdown ? $actions : div(),
|
|
$hasToggleBtn ? btn
|
|
(
|
|
set::type('ghost'),
|
|
set::size('sm'),
|
|
set::icon('menu-arrow-left text-gray'),
|
|
set::hint($app->lang->collapse),
|
|
on::click()->do('$this.closest(".sidebar").sidebar("toggle");')
|
|
) : null
|
|
) : null,
|
|
$isInSidebar && !empty($header) ? on::init()->do('$("#mainContainer").addClass("has-module-menu-header")') : null
|
|
),
|
|
);
|
|
}
|
|
}
|