Improve taxon UI

This commit is contained in:
kulczy 2019-03-14 10:19:17 +01:00 committed by Mateusz Zalewski
parent 20a6da6526
commit c96f032e59
No known key found for this signature in database
GPG key ID: 0545A7503DD474B8
10 changed files with 277 additions and 61 deletions

View file

@ -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.$ = $;

View file

@ -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;

View file

@ -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; }
}

View file

@ -0,0 +1 @@
@import "taxonomy";

View file

@ -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' %}

View file

@ -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">&bull;</i>{{ 'sylius.ui.toggle_all'|trans }}
</a>
{{ tree.render(taxons) }}
</div>
</div>

View file

@ -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">&bull;</i>{{ 'sylius.ui.toggle_all'|trans }}
</a>
{{ tree.render(taxons) }}
</div>
</div>

View file

@ -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>

View file

@ -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>

View file

@ -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'