Skip to content

Commit

Permalink
feat(admin/twig): json menu nested copy/paste functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Davidmattei committed Oct 7, 2024
1 parent 831f14a commit f09c928
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 58 deletions.
34 changes: 30 additions & 4 deletions EMS/core-bundle/assets/js/component/jsonMenuNestedComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import ajaxModal from "../helper/ajaxModal";
export default class JsonMenuNestedComponent {
id;
#tree;
#top;
#header;
#footer;
element;
#pathPrefix;
#loadParentIds = [];
Expand All @@ -14,6 +17,9 @@ export default class JsonMenuNestedComponent {
this.id = element.id;
this.element = element;
this.#tree = element.querySelector('.jmn-tree');
this.#top = element.querySelector('.jmn-top');
this.#header = element.querySelector('.jmn-header');
this.#footer = element.querySelector('.jmn-footer');
this.#pathPrefix = `/component/json-menu-nested/${element.dataset.hash}`;
this._addClickListeners();
this._addClickLongPressListeners();
Expand All @@ -22,16 +28,26 @@ export default class JsonMenuNestedComponent {
});
}

load({ activeItemId = null, loadChildrenId: loadChildrenId = null} = {}) {
load({
activeItemId = null,
copyItemId = null,
loadChildrenId = null
} = {}) {
this._post('/render', {
active_item_id: activeItemId,
copy_item_id: copyItemId,
load_parent_ids: this.#loadParentIds,
load_children_id: loadChildrenId
}).then((json) => {
if (!json.hasOwnProperty('tree') || !json.hasOwnProperty('load_parent_ids')) return;
const { tree, loadParentIds, top, header, footer } = json;

this.#loadParentIds = json.load_parent_ids;
this.#tree.innerHTML = json.tree;
if (top) this.#top.innerHTML = top;
if (header) this.#header.innerHTML = header;
if (footer) this.#footer.innerHTML = footer;

if (!tree || !loadParentIds) return;
this.#loadParentIds = loadParentIds;
this.#tree.innerHTML = tree;

let eventCanceled = this._dispatchEvent('jmn-load', { data: json, elements: this._sortables() });
if (!eventCanceled) this.loading(false);
Expand Down Expand Up @@ -64,6 +80,7 @@ export default class JsonMenuNestedComponent {
if (element.classList.contains('jmn-btn-edit')) this._onClickButtonEdit(element, itemId);
if (element.classList.contains('jmn-btn-view')) this._onClickButtonView(element, itemId);
if (element.classList.contains('jmn-btn-delete')) this._onClickButtonDelete(itemId);
if (element.classList.contains('jmn-btn-copy')) this._onClickButtonCopy(itemId);

if (element.dataset.hasOwnProperty('jmnModalCustom')) this._onClickModalCustom(element, itemId);
}, true);
Expand All @@ -80,6 +97,15 @@ export default class JsonMenuNestedComponent {
_onClickButtonDelete(itemId) {
this.itemDelete(itemId);
}
_onClickButtonCopy(itemId) {
document.dispatchEvent(new CustomEvent('jmn.copy', {
cancelable: true,
detail: { itemId: itemId }
}))
}
onCopy(itemId) {
this.load({ copyItemId: itemId });
}
_onClickModalCustom(element, itemId) {
const modalCustomName = element.dataset.jmnModalCustom;
this._ajaxModal(element, `/item/${itemId}/modal-custom/${modalCustomName}`, 'jmn-modal-custom');
Expand Down
14 changes: 10 additions & 4 deletions EMS/core-bundle/assets/js/initEms.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,18 @@ import JsonMenuNestedComponent from "./component/jsonMenuNestedComponent";
function initJsonMenuNestedComponent() {
const elements = document.getElementsByClassName('json-menu-nested-component');

window.jsonMenuNestedComponents = [];
let jsonMenuNestedComponents = [];
[].forEach.call(elements, function (element) {
const component = new JsonMenuNestedComponent(element);
if (component.id in window.jsonMenuNestedComponents) throw new Error(`duplicate id : ${component.id}`);
window.jsonMenuNestedComponents[component.id] = component;
const component = new JsonMenuNestedComponent(element)
if (component.id in jsonMenuNestedComponents) throw new Error(`duplicate id : ${component.id}`)
jsonMenuNestedComponents[component.id] = component
});

document.addEventListener('jmn.copy', (e) => {
Object.values(jsonMenuNestedComponents).forEach((component) => component.onCopy(e.detail.itemId))
})

window.jsonMenuNestedComponents = jsonMenuNestedComponents
}

function initMediaLibrary() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,26 @@ public function __construct(
}

/**
* @return array{ load_parent_ids: string[], tree: string }
* @return array{ loadParentIds: string[], tree: string, top: string, footer: string }
*/
public function render(JsonMenuNestedConfig $config, ?string $activeItemId, ?string $loadChildrenId, string ...$loadParentIds): array
public function render(JsonMenuNestedConfig $config, ?string $activeItemId, ?string $copyItemId, ?string $loadChildrenId, string ...$loadParentIds): array
{
$menu = $config->jsonMenuNested;
$renderContext = new JsonMenuNestedRenderContext($menu, $activeItemId, $loadChildrenId, ...$loadParentIds);
$renderContext = new JsonMenuNestedRenderContext(
menu: $menu,
activeItemId: $activeItemId,
copyItemId: $copyItemId,
loadChildrenId: $loadChildrenId
);
$renderContext->loadParents(...$loadParentIds);

$template = $this->jsonMenuNestedTemplateFactory->create($config, ['render' => $renderContext]);

return [
'load_parent_ids' => $renderContext->getParentIds(),
'loadParentIds' => $renderContext->getParentIds(),
'tree' => $template->block('jmn_render'),
'top' => $template->block('jmn_layout_top'),
'footer' => $template->block('jmn_layout_footer'),
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class JsonMenuNestedRenderContext
{
public ?JsonMenuNested $activeItem;
public ?JsonMenuNested $copyItem;

/** @var array<string, JsonMenuNested> */
public array $loadParents = [];
/** @var array<string, JsonMenuNested> */
Expand All @@ -17,29 +19,52 @@ class JsonMenuNestedRenderContext
public function __construct(
private readonly JsonMenuNested $menu,
?string $activeItemId,
?string $loadChildrenId,
string ...$loadParentIds
?string $copyItemId,
?string $loadChildrenId
) {
$this->addActiveItem($menu);
$this->activeItem = $activeItemId ? $menu->getItemById($activeItemId) : $menu;

$this->activeItem = $activeItemId ? $menu->getItemById($activeItemId) : null;
if ($this->activeItem) {
foreach ($this->activeItem->getPath() as $activeParent) {
$this->addParent($activeParent);
}
$this->loadPath($this->activeItem);
}

$this->copyItem = $copyItemId ? $menu->getItemById($copyItemId) : null;
if ($this->copyItem) {
$this->loadPath($this->copyItem);
}

$loadChildren = $loadChildrenId ? $this->menu->getItemById($loadChildrenId) : null;
if ($loadChildren) {
foreach ($loadChildren as $loadChild) {
if ($loadChild->hasChildren()) {
$this->addParent($loadChild);
}
$this->loadAllChildren($loadChildren);
}
}

public function isActive(JsonMenuNested $item): bool
{
return $this->activeItem === $item || $this->copyItem === $item;
}

public function loadPath(JsonMenuNested $item): void
{
foreach ($item->getPath() as $itemParent) {
$this->addParent($itemParent);
}
}

public function loadAllChildren(JsonMenuNested $item): void
{
foreach ($item as $child) {
if ($child->hasChildren()) {
$this->addParent($child);
}
}
}

public function loadParents(string ...$loadParentIds): void
{
foreach ($loadParentIds as $loadParentId) {
$this->addParent($menu->getItemById($loadParentId));
$this->addParent($this->menu->getItemById($loadParentId));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

<div class="json-menu-nested-component" id="{{ id }}" data-hash="{{ hash }}" {% if config.activeItemId %}data-active-item-id="{{ config.activeItemId }}"{% endif %}>
<div class="jmn-node-loading"><i class="fa fa-cog fa-spin fa-3x fa-fw"></i></div>
{{ template.block('jmn_layout_top', _context)|raw }}
<div class="jmn-top">{{ template.block('jmn_layout_top', _context)|raw }}</div>
<div class="jmn-wrapper">
{{ template.block('_jmn_columns', _context)|raw }}
<div class="jmn-header">{{ template.block('jmn_columns', _context)|raw }}</div>
<div id="{{- "tree-#{id}" -}}" class="jmn-tree jmn-node jmn-sortable" data-id="{{ config.jsonMenuNested.id }}" data-types="{{ config.nodes.types('_root')|json_encode|e('html_attr') }}"></div>
</div>
{{ template.block('jmn_layout_footer', _context)|raw }}
<div class="jmn-footer">{{ template.block('jmn_layout_footer', _context)|raw }}</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% trans_default_domain 'EMSCoreBundle' %}
{%- trans_default_domain 'EMSCoreBundle' -%}
{# @var config \EMS\CoreBundle\Core\Component\JsonMenuNested\Config\JsonMenuNestedConfig #}
{# @var column \EMS\CoreBundle\Core\Component\JsonMenuNested\Config\JsonMenuNestedColumn #}
{# @var template \EMS\CoreBundle\Core\Component\JsonMenuNested\Template\JsonMenuNestedTemplate #}
Expand All @@ -8,10 +8,8 @@
{# @var node \EMS\CoreBundle\Core\Component\JsonMenuNested\Config\JsonMenuNestedNode #}

{%- block jmn_layout_top -%}
<div class="jmn-top">
{{ template.block('jmn_title', _context)|raw }}
{{ template.block('jmn_actions', _context)|raw }}
</div>
{{ template.block('jmn_title', _context)|raw }}
{{ template.block('jmn_actions', _context)|raw }}
{%- endblock jmn_layout_top -%}

{%- block jmn_layout_footer -%}{% endblock jmn_layout_footer %}
Expand All @@ -23,17 +21,15 @@
{{ template.block('jmn_button_add', _context)|raw }}
{%- endblock jmn_actions -%}

{%- block _jmn_columns -%}
<div class="jmn-header">
{% for column in config.columns %}
<div class="jmn-column {{ "jmn-column-#{column.name}" }}" style="{{ column.width ? "width:#{column.width}px;" }}" >
{%- if template.hasBlock("jmn_column_#{column.name}") -%}
{{ template.block("jmn_column_#{column.name}", _context)|raw }}
{%- endif -%}
</div>
{% endfor %}
</div>
{%- endblock _jmn_columns -%}
{%- block jmn_columns -%}
{% for column in config.columns %}
<div class="jmn-column {{ "jmn-column-#{column.name}" }}" style="{{ column.width ? "width:#{column.width}px;" }}" >
{%- if template.hasBlock("jmn_column_#{column.name}") -%}
{{ template.block("jmn_column_#{column.name}", _context)|raw }}
{%- endif -%}
</div>
{% endfor %}
{%- endblock jmn_columns -%}

{% block jmn_render %}
{% with { 'menu': menu } %}{{ template.block('jmn_items', _context)|raw }}{% endwith %}
Expand All @@ -48,7 +44,7 @@

{%- block jmn_item -%}
{%- apply spaceless -%}
<div class="{{ html_classes('jmn-node', { 'jmn-collapsible': item.hasChildren, 'jmn-node-active': item == render.activeItem }) }}" data-id="{{ item.id }}" data-type="{{ item.type }}">
<div class="{{ html_classes('jmn-node', { 'jmn-collapsible': item.hasChildren, 'jmn-node-active': render.isActive(item) }) }}" data-id="{{ item.id }}" data-type="{{ item.type }}">
{{ template.block('jmn_item_row', _context)|raw }}
{%- if node.leaf == false -%}
<div id="{{ "children-#{item.id}" }}" class="jmn-children jmn-sortable" data-types="{{ config.nodes.types(item.type)|json_encode|e('html_attr') }}">
Expand Down Expand Up @@ -112,6 +108,10 @@
<button class="jmn-btn-view" data-modal-size="{{- buttonModalSize|default('md') -}}">{{- buttonLabel|default('View') -}}</button>
{%- endblock jmn_button_item_view -%}

{%- block jmn_button_item_copy -%}
<button class="jmn-btn-copy">{{- buttonLabel|default('Copy') -}}</button>
{%- endblock jmn_button_item_copy -%}

{%- block jmn_button_item_add -%}
<button class="jmn-btn-add" data-add="{{ addNode.id }}" data-modal-size="{{- buttonModalSize|default('md') -}}">
{% if addNode.icon %}<i class="{{ addNode.icon }}"></i>{% endif %}
Expand All @@ -132,9 +132,13 @@
{%- endblock jmn_button_item_more -%}

{%- block jmn_button_item_more_menu -%}
<li>{{ template.block('jmn_button_item_view', _context)|raw }}</li>
<li>{{ template.block('jmn_button_item_edit', _context)|raw }}</li>
<li>{{ template.block('jmn_button_item_delete', _context)|raw }}</li>
{%- set menuMoreItems = menuMoreItems|default(['jmn_button_item_view', 'jmn_button_item_edit', 'jmn_button_item_copy', 'jmn_button_item_delete']) -%}
{%- for menuMoreItem in menuMoreItems -%}
{%- set blockMenuMoreItem = template.block(menuMoreItem, _context)|raw -%}
{%- if blockMenuMoreItem|length > 0 -%}
<li>{{ blockMenuMoreItem|raw }}</li>
{%- endif -%}
{%- endfor -%}
{%- endblock jmn_button_item_more_menu -%}

{%- block jmn_button_add -%}
Expand Down
35 changes: 23 additions & 12 deletions demo/skeleton/template_ems/dashboard/json_menu_nested.twig
Original file line number Diff line number Diff line change
Expand Up @@ -58,32 +58,43 @@
{%- endblock jmn_button_add_menu -%}

{%- block jmn_button_item_more_menu -%}
<li>{{ template.block('jmn_button_item_view', _context)|raw }}</li>
<li>{{ template.block('jmn_button_item_edit', _context)|raw }}</li>
{%- if item.type == 'page' and pageInfo -%}
<li>
<button class="btn-detach-page" data-item-id="{{ item.id }}" data-page-id="{{ pageInfo.id.ouuid }}">Detach page</button>
</li>
{% elseif item.children|length == 0 %}
<li>{{- template.block('jmn_button_item_delete', _context)|raw -}}</li>
{% endif %}
{%- set menuMoreItems = menuMoreItems|default([
'jmn_button_item_view',
'jmn_button_item_edit',
'button_detach',
'jmn_button_item_copy',
'jmn_button_item_delete'
]) -%}
{{ parent() }}
{%- endblock jmn_button_item_more_menu -%}

{%- block jmn_button_item_add -%}
{% set buttonLabel = "Create #{addNode.type}" %}
{%- set buttonLabel = "Create #{addNode.type}" -%}
{{ parent() }}
{%- endblock -%}

{%- block jmn_button_item_edit -%}
{% if node.type|default('') == 'page' %}{% set buttonLabel = 'Edit item' %}{% endif %}
{%- if node.type|default('') == 'page' -%}{%- set buttonLabel = 'Edit item' -%}{% endif -%}
{{ parent() }}
{%- endblock -%}

{%- block jmn_button_item_view -%}
{% if node.type|default('') == 'page' %}{% set buttonLabel = 'View item' %}{% endif %}
{%- if node.type|default('') == 'page' -%}{%- set buttonLabel = 'View item' -%}{%- endif -%}
{{ parent() }}
{%- endblock -%}

{%- block button_detach -%}
{%- if item.type == 'page' and pageInfo -%}
<button class="btn-detach-page" data-item-id="{{ item.id }}" data-page-id="{{ pageInfo.id.ouuid }}">Detach page</button>
{%- endif -%}
{%- endblock button_detach -%}

{%- block jmn_button_item_delete -%}
{%- if item.children|length == 0 and (item.type != 'page' or pageInfo == false) -%}
{{ parent() }}
{%- endif -%}
{%- endblock -%}

{%- block jmn_item_title -%}
{% if item.type == 'page' %}
{% if page and pageInfo %}
Expand Down

0 comments on commit f09c928

Please sign in to comment.