mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
Improve taxon UI
This commit is contained in:
parent
20a6da6526
commit
c96f032e59
10 changed files with 277 additions and 61 deletions
|
|
@ -26,6 +26,8 @@ import './sylius-product-images-preview';
|
|||
import './sylius-product-slug';
|
||||
import './sylius-taxon-slug';
|
||||
|
||||
import SyliusTaxonomyTree from './sylius-taxon-tree';
|
||||
|
||||
$(document).ready(() => {
|
||||
$('#sylius_product_variant_pricingCalculator').handlePrototypes({
|
||||
prototypePrefix: 'sylius_product_variant_pricingCalculator',
|
||||
|
|
@ -97,6 +99,8 @@ $(document).ready(() => {
|
|||
$(event.target).find('.accordion').accordion();
|
||||
}
|
||||
});
|
||||
|
||||
const taxonomyTree = new SyliusTaxonomyTree();
|
||||
});
|
||||
|
||||
window.$ = $;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
class SyliusTaxonomyTree {
|
||||
constructor() {
|
||||
this.attr = {
|
||||
tree: 'data-sylius-js-tree',
|
||||
parent: 'data-sylius-js-tree-parent',
|
||||
trigger: 'data-sylius-js-tree-trigger',
|
||||
storageName: 'sylius:taxonomy:hidden',
|
||||
};
|
||||
|
||||
this.tree = document.querySelector(`[${this.attr.tree}]`);
|
||||
this.hiddenItems = this.getMapFromStorage();
|
||||
this.renderMap();
|
||||
this.tree.classList.remove('hidden');
|
||||
|
||||
this.tree.querySelectorAll(`[${this.attr.trigger}]`).forEach((trigger) => {
|
||||
trigger.addEventListener('click', this.handleTrigger.bind(this, trigger));
|
||||
});
|
||||
}
|
||||
|
||||
handleTrigger(trigger, e) {
|
||||
e.preventDefault();
|
||||
|
||||
const id = trigger.getAttribute(this.attr.trigger) || null;
|
||||
const parent = this.tree.querySelector(`[${this.attr.parent}="${id}"]`) || this.tree;
|
||||
const toRemove = this.hiddenItems.indexOf(id) === -1;
|
||||
|
||||
this.hiddenItems = !id && this.hiddenItems.length ? [] : this.toggle(toRemove, this.getIDs(parent, toRemove));
|
||||
this.saveMapToStorage(this.hiddenItems);
|
||||
this.renderMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding or removing the given array of items ID from the hiddenItems array
|
||||
* @param {boolean} action - true: add, false: remove from array
|
||||
* @param {Array} ids - array of items ID
|
||||
* @return {Array}
|
||||
*/
|
||||
toggle(action, ids) {
|
||||
const newMap = [...this.hiddenItems];
|
||||
|
||||
ids.forEach((item) => {
|
||||
const index = newMap.indexOf(item);
|
||||
if (action && index === -1) newMap.push(item);
|
||||
if (!action && index !== -1) newMap.splice(index, 1);
|
||||
});
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ID of given Node element (if has one) and optional IDs of children
|
||||
* @param {Node} parent
|
||||
* @param {boolean} withChildren
|
||||
* @return {Array}
|
||||
*/
|
||||
getIDs(parent, withChildren = true) {
|
||||
const arr = parent.hasAttribute(this.attr.parent) ? [parent] : [];
|
||||
const children = withChildren ? [].slice.call(parent.querySelectorAll(`[${this.attr.parent}]`)) : [];
|
||||
return [...arr, ...children].map((child, i) => child.getAttribute(this.attr.parent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides elements if their ID is in the hiddenItems array
|
||||
*/
|
||||
renderMap() {
|
||||
this.tree.querySelectorAll(`[${this.attr.parent}]`).forEach(parent => {
|
||||
const id = parent.getAttribute(this.attr.parent);
|
||||
const state = this.hiddenItems.indexOf(id) !== -1;
|
||||
parent.classList.toggle('hide', state);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items from local storage
|
||||
* @return {Array}
|
||||
*/
|
||||
getMapFromStorage() {
|
||||
const items = localStorage.getItem(this.attr.storageName);
|
||||
return items ? JSON.parse(items) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save items to local storage
|
||||
* @param {Array} items
|
||||
*/
|
||||
saveMapToStorage(items) {
|
||||
window.localStorage.setItem(this.attr.storageName, JSON.stringify(items));
|
||||
}
|
||||
}
|
||||
|
||||
export default SyliusTaxonomyTree;
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
.sylius-tree {
|
||||
&.hidden { display: none; }
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
ul {
|
||||
position: relative;
|
||||
padding: 0 0 0 15px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
bottom: 5px;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: #eee;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.angle.left {
|
||||
transform: rotate(-45deg) translate(-3px);
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.sylius-tree__item {
|
||||
display: flex;
|
||||
margin: 3px 0;
|
||||
border-radius: 3px;
|
||||
padding: 0;
|
||||
transition: all .1s;
|
||||
|
||||
.sylius-tree__icon {
|
||||
flex-shrink: 0;
|
||||
padding-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sylius-tree__title {
|
||||
flex-grow: 1;
|
||||
padding: 4px 10px 4px 0;
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.sylius-tree__action {
|
||||
white-space: nowrap;
|
||||
|
||||
.sylius-tree__action__trigger {
|
||||
box-shadow: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sylius-tree__icon > i::before {
|
||||
content: "\f0da" !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sylius-tree__toggle-all {
|
||||
display: inline-block;
|
||||
color: #999;
|
||||
padding: 3px 0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.ui.hidden.divider.small { margin: 0.5rem 0; }
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
@import "taxonomy";
|
||||
|
|
@ -18,14 +18,14 @@
|
|||
{{ sonata_block_render_event('sylius.admin.product.index.after_header', {'resources': resources}) }}
|
||||
|
||||
<div class="ui two column stackable grid">
|
||||
<div class="three wide column">
|
||||
<div class="sixteen wide mobile sixteen wide tablet three wide computer column">
|
||||
{{ sonata_block_render_event('sylius.admin.product.index.before_taxon_tree', {'resources': resources}) }}
|
||||
|
||||
{{ render(path('sylius_admin_partial_taxon_tree', {'template': '@SyliusAdmin/Taxon/_treeWithoutButtons.html.twig'})) }}
|
||||
|
||||
{{ sonata_block_render_event('sylius.admin.product.index.after_taxon_tree', {'resources': resources}) }}
|
||||
</div>
|
||||
<div class="thirteen wide column sylius-product-index">
|
||||
<div class="sixteen wide mobile sixteen wide tablet thirteen wide computer column sylius-product-index">
|
||||
{{ sonata_block_render_event('sylius.admin.product.index.before_filters', {'resources': resources}) }}
|
||||
|
||||
{% include '@SyliusAdmin/Crud/Index/_content.html.twig' %}
|
||||
|
|
|
|||
|
|
@ -5,42 +5,65 @@
|
|||
{% import '@SyliusUi/Macro/buttons.html.twig' as buttons %}
|
||||
{% import _self as tree %}
|
||||
|
||||
<ul>
|
||||
{% for taxon in taxons if taxon.id != null %}
|
||||
<div class="item" data-id="{{ taxon.id }}">
|
||||
<i class="folder icon"></i>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<li data-id="{{ taxon.id }}" {% if taxon.children is not empty %}data-sylius-js-tree-parent="{{ taxon.id }}"{% endif %}>
|
||||
<div class="sylius-tree__item">
|
||||
<div class="sylius-tree__icon" {% if taxon.children is not empty %}data-sylius-js-tree-trigger="{{ taxon.id }}"{% endif %}>
|
||||
<i class="{{ taxon.children is not empty ? 'caret down' : 'angle left' }} icon"></i>
|
||||
</div>
|
||||
<div class="sylius-tree__title">
|
||||
<a href="{{ path('sylius_admin_product_per_taxon_index', {'taxonId': taxon.id}) }}">
|
||||
{{ taxon.name }}
|
||||
</a>
|
||||
<br>
|
||||
<div class="ui mini buttons" style="margin-top: 5px;">
|
||||
{{ buttons.create(path('sylius_admin_taxon_create_for_parent', { 'id': taxon.id }), 'sylius.ui.add') }}
|
||||
{{ buttons.edit(path('sylius_admin_taxon_update', { 'id': taxon.id }), null, null, false) }}
|
||||
{{ buttons.delete(path('sylius_admin_taxon_delete', { 'id': taxon.id }), null, false, taxon.id) }}
|
||||
<a class="ui icon button sylius-taxon-move-up" data-url="{{ path('sylius_admin_ajax_taxon_move', { id: taxon.id }) }}" data-id="{{ taxon.id }}" data-position="{{ taxon.position }}">
|
||||
<i class="icon arrow up"></i>
|
||||
</div>
|
||||
<div class="sylius-tree__action">
|
||||
<form action="{{ path('sylius_admin_taxon_delete', { 'id': taxon.id }) }}" method="post">
|
||||
<div class="ui tiny basic icon top right pointing dropdown button sylius-tree__action__trigger">
|
||||
<i class="ellipsis horizontal icon"></i>
|
||||
<div class="menu">
|
||||
<a class="item" href="{{ path('sylius_admin_taxon_create_for_parent', { 'id': taxon.id }) }}">
|
||||
<i class="plus icon blue"></i>{{ 'sylius.ui.create'|trans }}
|
||||
</a>
|
||||
<a class="ui icon button sylius-taxon-move-down" data-url="{{ path('sylius_admin_ajax_taxon_move', { id: taxon.id }) }}" data-id="{{ taxon.id }}" data-position="{{ taxon.position }}">
|
||||
<i class="icon arrow down"></i>
|
||||
<a class="item" href="{{ path('sylius_admin_taxon_update', { 'id': taxon.id }) }}">
|
||||
<i class="pencil icon grey"></i>{{ 'sylius.ui.edit'|trans }}
|
||||
</a>
|
||||
<button class="item" type="submit" style="width: 100%;" data-requires-confirmation>
|
||||
<i class="icon trash red"></i>{{ 'sylius.ui.delete'|trans }}
|
||||
</button>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="item sylius-taxon-move-up" data-url="{{ path('sylius_admin_ajax_taxon_move', { id: taxon.id }) }}" data-id="{{ taxon.id }}" data-position="{{ taxon.position }}">
|
||||
<i class="arrow up icon grey"></i>{{ 'sylius.ui.move_up'|trans }}
|
||||
</div>
|
||||
<div class="item sylius-taxon-move-down" data-url="{{ path('sylius_admin_ajax_taxon_move', { id: taxon.id }) }}" data-id="{{ taxon.id }}" data-position="{{ taxon.position }}">
|
||||
<i class="arrow down icon grey"></i>{{ 'sylius.ui.move_down'|trans }}
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token(taxon.id) }}" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
{{ tree.render(taxon.children) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
|
||||
<a href="{{ path('sylius_admin_taxon_create') }}" class="ui large fluid labeled icon primary button">
|
||||
<div class="ui segment sylius-tree hidden" data-sylius-js-tree>
|
||||
<a href="{{ path('sylius_admin_taxon_create') }}" class="ui fluid labeled icon primary button">
|
||||
<i class="plus icon"></i>
|
||||
{{ 'sylius.ui.create'|trans }}
|
||||
</a>
|
||||
</a>
|
||||
|
||||
<div class="ui segment">
|
||||
<div class="ui list sylius-sortable-list">
|
||||
<div class="ui hidden divider small"></div>
|
||||
|
||||
<a href="#" class="sylius-tree__toggle-all" data-sylius-js-tree-trigger>
|
||||
<i class="icon">•</i>{{ 'sylius.ui.toggle_all'|trans }}
|
||||
</a>
|
||||
{{ tree.render(taxons) }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,36 +3,40 @@
|
|||
{% macro render(taxons) %}
|
||||
{% import _self as tree %}
|
||||
|
||||
<ul>
|
||||
{% for taxon in taxons %}
|
||||
<div class="item">
|
||||
<i class="folder icon"></i>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<li data-id="{{ taxon.id }}" {% if taxon.children is not empty %}data-sylius-js-tree-parent="{{ taxon.id }}"{% endif %}>
|
||||
<div class="sylius-tree__item">
|
||||
<div class="sylius-tree__icon" {% if taxon.children is not empty %}data-sylius-js-tree-trigger="{{ taxon.id }}"{% endif %}>
|
||||
<i class="{{ taxon.children is not empty ? 'caret down' : 'angle left' }} icon"></i>
|
||||
</div>
|
||||
<div class="sylius-tree__title">
|
||||
<a href="{{ path('sylius_admin_product_per_taxon_index', {'taxonId': taxon.id}) }}">
|
||||
{{ taxon.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="list">
|
||||
</div>
|
||||
{{ tree.render(taxon.children) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="ui large fluid vertical buttons">
|
||||
<a href="{{ path('sylius_admin_product_index') }}" class="ui primary labeled icon button">
|
||||
<div class="ui vertical fluid labeled icon buttons">
|
||||
<a href="{{ path('sylius_admin_product_index') }}" class="ui primary button">
|
||||
<i class="search icon"></i>
|
||||
{{ 'sylius.ui.browse_all_products'|trans }}
|
||||
</a>
|
||||
<a href="{{ path('sylius_admin_taxon_create') }}" class="ui labeled icon button">
|
||||
|
||||
<a href="{{ path('sylius_admin_taxon_create') }}" class="ui button">
|
||||
<i class="sitemap icon"></i>
|
||||
{{ 'sylius.ui.manage_taxons'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui segment">
|
||||
<div class="ui list">
|
||||
<div class="ui segment sylius-tree hidden" data-sylius-js-tree>
|
||||
<a href="#" class="sylius-tree__toggle-all" data-sylius-js-tree-trigger>
|
||||
<i class="icon">•</i>{{ 'sylius.ui.toggle_all'|trans }}
|
||||
</a>
|
||||
{{ tree.render(taxons) }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
{% block content %}
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.create.before_content', {'resource': resource}) }}
|
||||
|
||||
<div class="ui two column stackable grid">
|
||||
<div class="four wide column">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide mobile sixteen wide tablet four wide computer column">
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.create.before_taxon_tree', {'resource': resource}) }}
|
||||
|
||||
{{ render(path('sylius_admin_partial_taxon_tree', {'template': '@SyliusAdmin/Taxon/_treeWithButtons.html.twig'})) }}
|
||||
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.create.after_taxon_tree', {'resource': resource}) }}
|
||||
</div>
|
||||
<div class="twelve wide column">
|
||||
<div class="sixteen wide mobile sixteen wide tablet twelve wide computer column">
|
||||
{{ parent() }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
{% block content %}
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.update.before_content', {'resource': resource}) }}
|
||||
|
||||
<div class="ui two column stackable grid">
|
||||
<div class="four wide column">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide mobile sixteen wide tablet four wide computer column">
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.update.before_taxon_tree', {'resource': resource}) }}
|
||||
|
||||
{{ render(path('sylius_admin_partial_taxon_tree', {'template': '@SyliusAdmin/Taxon/_treeWithButtons.html.twig'})) }}
|
||||
|
||||
{{ sonata_block_render_event('sylius.admin.taxon.update.after_taxon_tree', {'resource': resource}) }}
|
||||
</div>
|
||||
<div class="twelve wide column">
|
||||
<div class="sixteen wide mobile sixteen wide tablet twelve wide computer column">
|
||||
{{ parent() }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -832,6 +832,7 @@ sylius:
|
|||
this_product_has_no_attributes_defined: 'This product has no attributes defined'
|
||||
title: 'Title'
|
||||
to: 'To'
|
||||
toggle_all: 'Toggle all'
|
||||
toggle_sidebar: 'Toggle sidebar'
|
||||
topic: 'Topic'
|
||||
total: 'Total'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue