Begins implementation of Terms Carousel.

This commit is contained in:
Mateus Machado Luna 2019-09-30 16:51:24 -03:00
parent b3ccca80f6
commit 4cd201863e
10 changed files with 2065 additions and 1 deletions

View File

@ -31,5 +31,7 @@ sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/gutenberg-blocks/tainaca
sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/gutenberg-blocks/tainacan-facets/facets-list/facets-list.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css
echo "Compilação do Sass Concluído!"
exit 0

View File

@ -0,0 +1,424 @@
[data-align='full'] .block-control {
width: calc(100% + 14px); }
.block-control {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 5px;
position: relative;
top: -14px;
left: -14px;
width: calc(100% + 28px);
background: #f2f2f2; }
.block-control p {
font-size: 0.875rem !important;
margin: 12px 1.5rem 16px 0;
fill: #555758; }
.block-control p svg {
top: 4px;
margin-right: 0.75rem;
position: relative;
fill: #555758; }
.components-placeholder__fieldset p {
font-size: 0.875rem !important;
margin-top: -2px;
fill: #555758; }
.components-placeholder__fieldset p svg {
margin-right: 1rem;
top: 4px;
position: relative;
fill: #555758; }
.wp-block-tainacan-modal {
width: 50%; }
@media only screen and (max-width: 1024px) {
.wp-block-tainacan-modal {
width: 75%; } }
@media only screen and (max-width: 768px) {
.wp-block-tainacan-modal {
width: 100%; } }
.wp-block-tainacan-modal.dynamic-modal {
width: 90%;
height: 90%;
width: 80vw;
height: 80vh; }
.wp-block-tainacan-modal.dynamic-modal iframe {
width: calc(100% + 32px);
height: calc(100% - 128px);
margin-left: -16px; }
.wp-block-tainacan-modal.dynamic-modal .modal-footer-area {
height: 68px;
width: calc(100% + 32px);
margin-left: -16px !important;
padding: 1.2rem 1.2rem !important;
border-top: 1px solid #cbcbcb; }
.wp-block-tainacan-modal .spinner-container {
min-height: 56px;
padding: 1rem;
display: flex;
justify-content: center;
align-items: center;
color: #555758; }
.wp-block-tainacan-modal .spinner-container .components-spineer {
margin: 8px; }
.wp-block-tainacan-modal .components-modal__header .components-icon-button {
padding-right: 0; }
.wp-block-tainacan-modal hr {
margin: 5px 0.75rem; }
.wp-block-tainacan-modal .modal-search-area {
display: flex;
justify-content: center;
margin-bottom: 14px; }
.wp-block-tainacan-modal .modal-search-area .components-base-control {
width: 60%; }
@media only screen and (max-width: 1024px) {
.wp-block-tainacan-modal .modal-search-area .components-base-control {
width: 95%; } }
.wp-block-tainacan-modal .modal-search-area .components-base-control:nth-child(2) {
flex-shrink: 2; }
.wp-block-tainacan-modal .modal-search-area .components-base-control .components-base-control__field {
flex-wrap: wrap; }
.wp-block-tainacan-modal .modal-search-area .components-base-control label.components-base-control__label {
font-weight: 500;
color: #454647;
font-size: 0.875rem;
text-align: center; }
.wp-block-tainacan-modal .modal-search-area .components-base-control input.components-text-control__input {
padding: 5px 8px;
height: 28px; }
.wp-block-tainacan-modal .modal-radio-area-label {
font-weight: 500;
margin-left: 1.75rem; }
.wp-block-tainacan-modal .modal-checkbox-list,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field {
padding: 0;
display: -ms-grid;
display: grid;
grid-template-columns: repeat(auto-fill, 250px);
grid-gap: 0px;
justify-content: space-evenly;
list-style-type: none;
margin: 12px;
max-height: 50vh;
overflow-y: auto;
overflow-x: hidden; }
.wp-block-tainacan-modal .modal-checkbox-list .components-base-control,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .components-base-control {
overflow: hidden; }
.wp-block-tainacan-modal .modal-checkbox-list .modal-checkbox-list-item,
.wp-block-tainacan-modal .modal-checkbox-list .components-radio-control__option,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .modal-checkbox-list-item,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .components-radio-control__option {
display: flex;
justify-content: flex-start; }
.wp-block-tainacan-modal .modal-checkbox-list .modal-checkbox-list-item img,
.wp-block-tainacan-modal .modal-checkbox-list .components-radio-control__option img,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .modal-checkbox-list-item img,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .components-radio-control__option img {
width: 24px;
height: 24px;
margin-right: 10px; }
.wp-block-tainacan-modal .modal-checkbox-list .modal-checkbox-list-item label,
.wp-block-tainacan-modal .modal-checkbox-list .components-radio-control__option label,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .modal-checkbox-list-item label,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .components-radio-control__option label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inline-block;
max-width: 85%; }
@media only screen and (max-width: 768px) {
.wp-block-tainacan-modal .modal-checkbox-list .modal-checkbox-list-item label,
.wp-block-tainacan-modal .modal-checkbox-list .components-radio-control__option label,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .modal-checkbox-list-item label,
.wp-block-tainacan-modal .modal-radio-list .components-base-control__field .components-radio-control__option label {
max-width: 80%; } }
.wp-block-tainacan-modal .modal-loadmore-section {
display: flex;
align-items: baseline;
justify-content: center; }
.wp-block-tainacan-modal .modal-loadmore-section p {
color: #555758;
margin-right: 12px;
margin-top: 2px; }
.wp-block-tainacan-modal .modal-footer-area {
border-top: 1px solid #e2e4e7;
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 16px 16px 0 16px;
margin: 0 -16px; }
.wp-block-tainacan-modal .modal-footer-area p {
padding: 0 12px;
color: #454647; }
.components-panel__body .term-carousel-view-modes {
display: flex;
margin: 12px 0 24px 0; }
.components-panel__body .term-carousel-view-modes button {
background: transparent;
border: 1px solid #cbcbcb;
border-radius: 5px;
padding: 2px;
margin: 0;
width: calc(50% - 6px);
flex-shrink: 0;
cursor: pointer;
transition: border ease 0.5s, opacity ease 0.5; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-grid {
margin-right: 6px; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-grid div {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%;
grid-column-gap: 2px;
grid-row-gap: 2px;
width: 60px;
height: 40px;
margin: 4px auto 8px auto; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-grid div > div:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2;
width: 41px;
height: 42px; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-grid div > div {
width: 20px;
height: 20px;
background: #555758;
transition: background-color ease 0.5s; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-thumbnail {
margin-left: 6px; }
.components-panel__body .term-carousel-view-modes button.term-carousel-view-mode-thumbnail div {
display: block;
height: 40px;
width: 40px;
background: #555758;
margin: 4px auto 8px auto;
transition: background-color ease 0.5s; }
.components-panel__body .term-carousel-view-modes button label {
font-size: 0.75rem;
color: #555758; }
.components-panel__body .term-carousel-view-modes button.is-active {
border: 2px solid #298596; }
.components-panel__body .term-carousel-view-modes button.is-active.term-carousel-view-mode-thumbnail div, .components-panel__body .term-carousel-view-modes button.is-active.term-carousel-view-mode-grid div > div {
background-color: #298596; }
.components-panel__body .term-carousel-view-modes button:hover {
opacity: 0.9;
border-width: 2px; }
.components-panel__body .term-carousel-view-modes button:hover button {
background: #f2f2f2; }
.wp-block-tainacan-carousel-terms-list {
margin: 2rem 0px; }
.wp-block-tainacan-carousel-terms-list .spinner-container {
min-height: 56px;
padding: 1rem;
display: flex;
justify-content: center;
align-items: center;
color: #555758; }
@-webkit-keyframes skeleton-animation {
0% {
opacity: 1.0; }
50% {
opacity: 0.2; }
100% {
opacity: 1.0; } }
@-moz-keyframes skeleton-animation {
0% {
opacity: 1.0; }
50% {
opacity: 0.2; }
100% {
opacity: 1.0; } }
@-o-keyframes skeleton-animation {
0% {
opacity: 1.0; }
50% {
opacity: 0.2; }
100% {
opacity: 1.0; } }
@keyframes skeleton-animation {
0% {
opacity: 1.0; }
50% {
opacity: 0.2; }
100% {
opacity: 1.0; } }
.wp-block-tainacan-carousel-terms-list .skeleton {
border-radius: 2px;
background: #f2f2f2;
-webkit-animation: skeleton-animation 1.8s ease infinite;
-moz-animation: skeleton-animation 1.8s ease infinite;
-o-animation: skeleton-animation 1.8s ease infinite;
animation: skeleton-animation 1.8s ease infinite; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel {
position: relative;
width: calc(100% + 40px);
left: -20px; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container {
margin: 0 50px; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container a > span,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container a:hover > span {
color: black;
font-weight: bold;
text-decoration: none;
padding: 8px 16px;
display: block;
line-height: 1.2rem; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container a > img {
width: 100%;
height: auto; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container a:hover {
text-decoration: none; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container .swiper-slide.term-list-item-grid a {
width: 100%;
display: block; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container .swiper-slide.term-list-item-grid .term-items-grid {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%;
width: 100%; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container .swiper-slide.term-list-item-grid .term-items-grid img:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper-container .swiper-slide.term-list-item-grid .term-items-grid img {
width: 100%;
height: auto;
padding: 2px;
margin-bottom: 0px; }
.wp-block-tainacan-carousel-terms-list .preview-warning {
width: 100%;
font-size: 0.875rem;
font-style: italic;
color: #898d8f;
text-align: center;
margin: 4px auto; }
.wp-block-tainacan-carousel-terms-list .swiper-button-prev, .wp-block-tainacan-carousel-terms-list .swiper-button-next {
top: calc(50% - 42px);
bottom: initial;
background: none;
border: none;
width: 42px;
height: 42px;
padding: 0;
margin: 0 -4px; }
.wp-block-tainacan-carousel-terms-list .swiper-button-prev svg, .wp-block-tainacan-carousel-terms-list .swiper-button-next svg {
fill: #298596; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel {
position: relative; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container .skeleton,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .skeleton {
min-height: 150px;
max-height: 150px; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container.has-arrows-none .swiper-button-prev, .wp-block-tainacan-carousel-terms-list .terms-list-edit-container.has-arrows-none .swiper-button-next,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel.has-arrows-none .swiper-button-prev,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel.has-arrows-none .swiper-button-next {
display: none; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container.has-arrows-left .swiper-button-next,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel.has-arrows-left .swiper-button-next {
left: 10px;
right: auto;
top: calc(50% + 12px) !important; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container.has-arrows-right .swiper-button-prev,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel.has-arrows-right .swiper-button-prev {
right: 10px;
left: auto; }
.wp-block-tainacan-carousel-terms-list .terms-list-edit-container.has-arrows-right .swiper-button-next,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel.has-arrows-right .swiper-button-next {
top: calc(50% + 12px) !important; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit {
display: flex;
align-items: flex-start;
overflow-x: scroll;
list-style: none;
margin: 0 36px; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item {
position: relative;
display: block;
margin: 16px 32px 16px 0px;
width: calc(16.666% - 32px);
min-width: calc(16.666% - 32px); }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item.term-list-item-grid {
margin: 16px;
width: calc(25% - 16px);
min-width: calc(25% - 16px); }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item a {
color: #454647;
font-weight: bold;
line-height: normal; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item img {
height: auto;
padding: 0px;
margin-bottom: 0.5rem; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item:hover a {
color: #454647;
text-decoration: none; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item button {
position: absolute !important;
background-color: rgba(255, 255, 255, 0.75);
color: #454647;
padding: 2px;
margin-left: 5px;
min-width: 14px;
visibility: hidden;
position: relative;
opacity: 0;
right: -14px;
top: 0px;
justify-content: center;
z-index: 999; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item:hover button {
visibility: visible;
background-color: white !important;
opacity: 1;
right: -8px;
top: -8px;
border: 1px solid #cbcbcb;
border-radius: 12px;
transition: opacity linear 0.15s, right linear 0.15s; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item:hover button:hover {
background-color: white !important;
border: 1px solid #cbcbcb !important; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item .term-items-grid {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item .term-items-grid img:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2; }
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item .term-items-grid img {
padding: 2px;
margin-bottom: 0px; }
@media only screen and (max-width: 1686px) {
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item {
width: calc(20% - 32px);
min-width: calc(20% - 32px); } }
@media only screen and (max-width: 1452px) {
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item {
width: calc(25% - 32px);
min-width: calc(25% - 32px); } }
@media only screen and (max-width: 1118px) {
.wp-block-tainacan-carousel-terms-list ul.terms-edit li.term-list-item {
width: calc(33.333% - 32px);
min-width: calc(33.333% - 32px); } }
@media only screen and (max-width: 854px) {
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item {
width: calc(50% - 32px);
min-width: calc(50% - 32px); } }
@media only screen and (max-width: 584px) {
.wp-block-tainacan-carousel-terms-list ul.terms-list-edit li.term-list-item {
width: calc(100% - 32px);
min-width: calc(100% - 32px); } }
/*# sourceMappingURL=tainacan-gutenberg-block-carousel-terms-list.css.map */

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@ function tainacan_blocks_add_gutenberg_blocks_actions() {
add_action('init', 'tainacan_blocks_register_tainacan_items_list');
add_action('init', 'tainacan_blocks_register_tainacan_dynamic_items_list');
add_action('init', 'tainacan_blocks_register_tainacan_carousel_items_list');
add_action('init', 'tainacan_blocks_register_tainacan_carousel_terms_list');
add_action('init', 'tainacan_blocks_register_tainacan_search_bar');
add_action('init', 'tainacan_blocks_register_tainacan_collections_list');
add_action('init', 'tainacan_blocks_register_tainacan_carousel_collections_list');
@ -176,6 +177,36 @@ function tainacan_blocks_register_tainacan_carousel_items_list(){
}
}
function tainacan_blocks_register_tainacan_carousel_terms_list(){
global $TAINACAN_BASE_URL;
wp_enqueue_script(
'carousel-terms-list-theme',
$TAINACAN_BASE_URL . '/assets/gutenberg_carousel_terms_list_theme-components.js',
array('wp-components')
);
wp_register_script(
'carousel-terms-list',
$TAINACAN_BASE_URL . '/assets/gutenberg_carousel_terms_list-components.js',
array('wp-blocks', 'wp-element', 'wp-components', 'wp-editor')
);
wp_register_style(
'carousel-terms-list',
$TAINACAN_BASE_URL . '/assets/css/tainacan-gutenberg-block-carousel-terms-list.css',
array('wp-edit-blocks')
);
if (function_exists('register_block_type')) {
register_block_type( 'tainacan/carousel-terms-list', array(
'editor_script' => 'carousel-terms-list',
'style' => 'carousel-terms-list',
'script' => 'carousel-terms-list-theme'
) );
}
}
function tainacan_blocks_register_tainacan_search_bar(){
global $TAINACAN_BASE_URL;
@ -281,6 +312,7 @@ function tainacan_blocks_add_plugin_settings() {
wp_localize_script( 'items-list', 'tainacan_plugin', $settings );
wp_localize_script( 'dynamic-items-list', 'tainacan_plugin', $settings );
wp_localize_script( 'carousel-items-list', 'tainacan_plugin', $settings );
wp_localize_script( 'carousel-terms-list', 'tainacan_plugin', $settings );
wp_localize_script( 'search-bar', 'tainacan_plugin', $settings );
wp_localize_script( 'collections-list', 'tainacan_plugin', $settings );
wp_localize_script( 'carousel-collections-list', 'tainacan_plugin', $settings );

View File

@ -0,0 +1,72 @@
import Vue from 'vue';
import CarouselTermsListTheme from './carousel-terms-list-theme.vue';
// This is rendered on the theme side.
document.addEventListener("DOMContentLoaded", () => {
// Gets all divs with content created by our block;
let blocks = document.getElementsByClassName('wp-block-tainacan-carousel-terms-list');
if (blocks) {
let blockIds = Object.values(blocks).map((block) => block.id);
// Creates a new Vue Instance to manage each block isolatelly
for (let blockId of blockIds) {
// Configure Vue logic before passing it to constructor:
let vueOptions = {
data: {
selectedItem: [],
maxItemsNumber: 12,
arrowsPosition: 'around',
autoPlay: false,
autoPlaySpeed: 3,
loopSlides: false,
hideName: true,
showCollectionThumbnail: false,
tainacanApiRoot: '',
tainacanBaseUrl: '',
className: ''
},
render(h){
return h(CarouselTermsListTheme, {
props: {
blockId: blockId,
selectedTerms: this.selectedTerms,
maxItemsNumber: this.maxItemsNumber,
arrowsPosition: this.arrowsPosition,
autoPlay: this.autoPlay,
autoPlaySpeed: this.autoPlaySpeed,
loopSlides: this.loopSlides,
hideName: this.hideName,
showCollectionThumbnail: this.showCollectionThumbnail,
tainacanApiRoot: this.tainacanApiRoot,
tainacanBaseUrl: this.tainacanBaseUrl,
className: this.className,
}
});
},
beforeMount () {
this.className = this.$el.attributes.class != undefined ? this.$el.attributes.class.value : undefined;
this.selectedTerms = this.$el.attributes['selected-terms'] != undefined ? JSON.parse(this.$el.attributes['selected-terms'].value) : undefined;
this.maxItemsNumber = this.$el.attributes['max-terms-number'] != undefined ? this.$el.attributes['max-terms-number'].value : undefined;
this.arrowsPosition = this.$el.attributes['arrows-position'] != undefined ? this.$el.attributes['arrows-position'].value : undefined;
this.autoPlay = this.$el.attributes['auto-play'] != undefined ? this.$el.attributes['auto-play'].value == 'true' : false;
this.autoPlaySpeed = this.$el.attributes['auto-play-speed'] != undefined ? this.$el.attributes['auto-play-speed'].value : 3;
this.loopSlides = this.$el.attributes['loop-slides'] != undefined ? this.$el.attributes['loop-slides'].value == 'true' : false;
this.hideName = this.$el.attributes['hide-name'] != undefined ? this.$el.attributes['hide-name'].value == 'true' : false;
this.showCollectionThumbnail = this.$el.attributes['show-collection-thumbnail'] != undefined ? this.$el.attributes['show-collection-thumbnail'].value == 'true' : false;
this.tainacanApiRoot = this.$el.attributes['tainacan-api-root'] != undefined ? this.$el.attributes['tainacan-api-root'].value : undefined;
this.tainacanBaseUrl = this.$el.attributes['tainacan-base-url'] != undefined ? this.$el.attributes['tainacan-base-url'].value : undefined;
},
methods: {
__(text, domain) {
return wp.i18n.__(text, domain);
}
}
};
new Vue( Object.assign({ el: '#' + blockId }, vueOptions) );
}
}
});

View File

@ -0,0 +1,303 @@
<template>
<div :class="className">
<div v-if="!isLoading">
<div
:class="'tainacan-carousel has-arrows-' + arrowsPosition"
v-if="terms.length > 0">
<swiper
role="list"
ref="myTermSwiper"
:options="swiperOptions">
<swiper-slide
role="listitem"
:key="index"
v-for="(term, index) of terms"
:class="'term-list-item ' + (!showTermThumbnail ? 'term-list-item-grid' : '')">
<a
v-if="showTermThumbnail"
:id="isNaN(term.id) ? term.id : 'term-id-' + term.id"
:href="term.url"
target="_blank">
<img
:src="
term.thumbnail && term.thumbnail['tainacan-medium'][0] && term.thumbnail['tainacan-medium'][0]
?
term.thumbnail['tainacan-medium'][0]
:
(term.thumbnail && term.thumbnail['thumbnail'][0] && term.thumbnail['thumbnail'][0]
?
term.thumbnail['thumbnail'][0]
:
`${tainacanBaseUrl}/admin/images/placeholder_square.png`)
"
:alt="term.name ? term.name : $root.__('Thumbnail', 'tainacan')">
<span v-if="!hideName">{{ term.name ? term.name : '' }}</span>
</a>
<a
v-else
:id="isNaN(term.id) ? term.id : 'term-id-' + term.id"
:href="term.url"
target="_blank">
<div class="term-items-grid">
<img
:src="
termItems[term.id][0] && termItems[term.id][0].thumbnail && termItems[term.id][0].thumbnail['tainacan-medium'][0] && termItems[term.id][0].thumbnail['tainacan-medium'][0]
?
termItems[term.id][0].thumbnail['tainacan-medium'][0]
:
(termItems[term.id][0] && termItems[term.id][0].thumbnail && termItems[term.id][0].thumbnail['thumbnail'][0] && termItems[term.id][0].thumbnail['thumbnail'][0]
?
termItems[term.id][0].thumbnail['thumbnail'][0]
:
`${tainacanBaseUrl}/admin/images/placeholder_square.png`)
"
:alt="termItems[term.id][0] && termItems[term.id][0].name ? termItems[term.id][0].name : $root.__( 'Thumbnail', 'tainacan' ) ">
<img
:src="
termItems[term.id][1] && termItems[term.id][1].thumbnail && termItems[term.id][1].thumbnail['tainacan-medium'][0] && termItems[term.id][1].thumbnail['tainacan-medium'][0]
?
termItems[term.id][1].thumbnail['tainacan-medium'][0]
:
(termItems[term.id][1] && termItems[term.id][1].thumbnail && termItems[term.id][1].thumbnail['thumbnail'][0] && termItems[term.id][1].thumbnail['thumbnail'][0]
?
termItems[term.id][1].thumbnail['thumbnail'][0]
:
`${tainacanBaseUrl}/admin/images/placeholder_square.png`)
"
:alt="termItems[term.id][1] && termItems[term.id][1].name ? termItems[term.id][1].name : $root.__( 'Thumbnail', 'tainacan' ) ">
<img
:src="
termItems[term.id][2] && termItems[term.id][2].thumbnail && termItems[term.id][2].thumbnail['tainacan-medium'][0] && termItems[term.id][2].thumbnail['tainacan-medium'][0]
?
termItems[term.id][2].thumbnail['tainacan-medium'][0]
:
(termItems[term.id][2] && termItems[term.id][2].thumbnail && termItems[term.id][2].thumbnail['thumbnail'][0] && termItems[term.id][2].thumbnail['thumbnail'][0]
?
termItems[term.id][2].thumbnail['thumbnail'][0]
:
`${tainacanBaseUrl}/admin/images/placeholder_square.png`)
"
:alt="termItems[term.id][2] && termItems[term.id][2].name ? termItems[term.id][2].name : $root.__( 'Thumbnail', 'tainacan' ) ">
</div>
<span v-if="!hideName">{{ term.name ? term.name : '' }}</span>
</a>
</swiper-slide>
</swiper>
<button
class="swiper-button-prev"
:id="blockId + '-prev'"
slot="button-prev"
:style="hideName ? 'top: calc(50% - 21px)' : 'top: calc(50% - 42px)'">
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
<button
class="swiper-button-next"
:id="blockId + '-next'"
slot="button-next"
:style="hideName ? 'top: calc(50% - 21px)' : 'top: calc(50% - 42px)'">
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
</div>
<div
v-else
class="spinner-container">
{{ $root.__('No terms found.', 'tainacan') }}
</div>
<!-- Swiper buttons are hidden as they actually swipe from slide to slide -->
</div>
<div v-else-if="isLoading && !autoPlay">
<div :class="'tainacan-carousel has-arrows-' + arrowsPosition">
<swiper
role="list"
ref="myTermSwiper"
:options="swiperOptions">
<swiper-slide
role="listitem"
:key="index"
v-for="(term, index) of 18"
class="term-list-item skeleton">
<a>
<img>
<span v-if="!hideName" />
</a>
</swiper-slide>
</swiper>
<button
class="swiper-button-prev"
:id="blockId + '-prev'"
slot="button-prev"
:style="hideName ? 'top: calc(50% - 21px)' : 'top: calc(50% - 42px)'">
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
<button
class="swiper-button-next"
:id="blockId + '-next'"
slot="button-next"
:style="hideName ? 'top: calc(50% - 21px)' : 'top: calc(50% - 42px)'">
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import qs from 'qs';
import 'swiper/dist/css/swiper.css';
import { swiper, swiperSlide } from 'vue-awesome-swiper';
export default {
name: "CarouselTermsListTheme",
data() {
return {
terms: [],
termItems: {},
term: undefined,
termsRequestSource: undefined,
isLoading: false,
isLoadingTerm: false,
localMaxTermsNumber: undefined,
localOrder: undefined,
tainacanAxios: undefined,
paged: undefined,
totalTerms: 0,
swiperOptions: {
mousewheel: true,
observer: true,
preventInteractionOnTransition: true,
allowClick: true,
allowTouchMove: true,
slidesPerView: this.showTermThumbnail ? 6 : 5,
slidesPerGroup: 1,
spaceBetween: this.showTermThumbnail ? 32 : 16,
slideToClickedSlide: true,
navigation: {
nextEl: '#' + this.blockId + '-next',
prevEl: '#' + this.blockId + '-prev',
},
breakpoints: {
498: { slidesPerView: this.showTermThumbnail ? 1 : 1 },
768: { slidesPerView: this.showTermThumbnail ? 2 : 1 },
1024: { slidesPerView: this.showTermThumbnail ? 3 : 2 },
1366: { slidesPerView: this.showTermThumbnail ? 4 : 3 },
1600: { slidesPerView: this.showTermThumbnail ? 5 : 4 },
},
autoplay: this.autoPlay ? { delay: this.autoPlaySpeed*1000 } : false,
loop: this.loopSlides
},
errorMessage: 'No terms found.'
}
},
components: {
swiper,
swiperSlide
},
props: {
blockId: String,
selectedTerms: Array,
maxTermsNumber: Number,
arrowsPosition: String,
autoPlay: false,
autoPlaySpeed: Number,
loopSlides: Boolean,
hideName: Boolean,
showTermThumbnail: Boolean,
tainacanApiRoot: String,
tainacanBaseUrl: String,
className: String
},
methods: {
fetchTerms() {
this.isLoading = true;
this.errorMessage = 'No terms found.';
if (this.termsRequestSource != undefined && typeof this.termsRequestSource == 'function')
this.termsRequestSource.cancel('Previous terms search canceled.');
this.termsRequestSource = axios.CancelToken.source();
let endpoint = '/terms?'+ qs.stringify({ postin: this.selectedTerms }) + '&fetch_only=name,url,thumbnail';
this.tainacanAxios.get(endpoint, { cancelToken: this.termsRequestSource.token })
.then(response => {
if (this.showTermThumbnail) {
for (let term of response.data)
this.terms.push(term);
this.isLoading = false;
} else {
let promises = [];
for (let term of response.data) {
promises.push(
this.tainacanAxios.get('/term/' + term.id + '/items?perpage=3&fetch_only=name,url,thumbnail')
.then(response => { return({ termId: term.id, termItems: response.data.items }) })
);
this.terms.push(term);
}
axios.all(promises).then((results) => {
for (let result of results) {
this.termItems[result.termId] = result.termItems;
}
this.isLoading = false;
})
}
this.totalTerms = response.headers['x-wp-total'];
}).catch((error) => {
this.isLoading = false;
if (error.response && error.response.status && error.response.status == 401)
this.errorMessage = 'Not allowed to see these terms.'
});
},
},
created() {
this.tainacanAxios = axios.create({ baseURL: this.tainacanApiRoot });
this.fetchTerms();
},
}
</script>
<style lang="scss">
@import './carousel-terms-list.scss';
</style>

View File

@ -0,0 +1,360 @@
@import '../../gutenberg-blocks-style.scss';
.components-panel__body .term-carousel-view-modes {
display: flex;
margin: 12px 0 24px 0;
button {
background: transparent;
border: 1px solid #cbcbcb;
border-radius: 5px;
padding: 2px;
margin: 0;
width: calc(50% - 6px);
flex-shrink: 0;
cursor: pointer;
transition: border ease 0.5s, opacity ease 0.5;
&.term-carousel-view-mode-grid {
margin-right: 6px;
div {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%;
grid-column-gap: 2px;
grid-row-gap: 2px;
width: 60px;
height: 40px;
margin: 4px auto 8px auto;
&>div:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2;
width: 41px;
height: 42px;
}
&>div {
width: 20px;
height: 20px;
background: #555758;
transition: background-color ease 0.5s;
}
}
}
&.term-carousel-view-mode-thumbnail {
margin-left: 6px;
div {
display: block;
height: 40px;
width: 40px;
background: #555758;
margin: 4px auto 8px auto;
transition: background-color ease 0.5s;
}
}
label {
font-size: 0.75rem;
color: #555758;
}
&.is-active {
border: 2px solid #298596;
&.term-carousel-view-mode-thumbnail div,
&.term-carousel-view-mode-grid div>div {
background-color: #298596;
}
}
&:hover {
opacity: 0.9;
border-width: 2px;
button {
background: #f2f2f2;
}
}
}
}
.wp-block-tainacan-carousel-terms-list {
margin: 2rem 0px;
// Spinner
.spinner-container {
min-height: 56px;
padding: 1rem;
display: flex;
justify-content: center;
align-items: center;
color: #555758;
}
// Skeleton loading
@-webkit-keyframes skeleton-animation {
0%{opacity: 1.0}
50%{opacity: 0.2}
100%{opacity: 1.0}
}
@-moz-keyframes skeleton-animation {
0%{opacity: 1.0}
50%{opacity: 0.2}
100%{opacity: 1.0}
}
@-o-keyframes skeleton-animation {
0%{opacity: 1.0}
50%{opacity: 0.2}
100%{opacity: 1.0}
}
@keyframes skeleton-animation {
0%{opacity: 1.0}
50%{opacity: 0.2}
100%{opacity: 1.0}
}
.skeleton {
border-radius: 2px;
background: #f2f2f2;
-webkit-animation: skeleton-animation 1.8s ease infinite;
-moz-animation: skeleton-animation 1.8s ease infinite;
-o-animation: skeleton-animation 1.8s ease infinite;
animation: skeleton-animation 1.8s ease infinite;
}
// Tainacan Carousel
.tainacan-carousel {
position: relative;
width: calc(100% + 40px);
left: -20px;
.swiper-container {
margin: 0 50px;
a>span,
a:hover>span {
color: black;
font-weight: bold;
text-decoration: none;
padding: 8px 16px;
display: block;
line-height: 1.2rem;
}
a>img {
width: 100%;
height: auto;
}
a:hover {
text-decoration: none;
}
.swiper-slide.term-list-item-grid {
a {
width: 100%;
display: block;
}
.term-items-grid {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%;
width: 100%;
img:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2;
}
img {
width: 100%;
height: auto;
padding: 2px;
margin-bottom: 0px;
}
}
}
}
}
.preview-warning {
width: 100%;
font-size: 0.875rem;
font-style: italic;
color: #898d8f;
text-align: center;
margin: 4px auto;
}
// Next and previous buttons
.swiper-button-prev, .swiper-button-next {
top: calc(50% - 42px);
bottom: initial;
background: none;
border: none;
width: 42px;
height: 42px;
padding: 0;
margin: 0 -4px;
svg {
fill: #298596;
}
}
// Carousel placeholder on editor side ----------------------------------------------------
.terms-list-edit-container,
.tainacan-carousel {
position: relative;
& .skeleton {
min-height: 150px;
max-height: 150px;
}
&.has-arrows-none .swiper-button-prev,
&.has-arrows-none .swiper-button-next {
display: none;
}
&.has-arrows-left .swiper-button-next {
left: 10px;
right: auto;
top: calc(50% + 12px) !important;
}
&.has-arrows-right .swiper-button-prev {
right: 10px;
left: auto;
}
&.has-arrows-right .swiper-button-next {
top: calc(50% + 12px) !important;
}
}
ul.terms-list-edit {
display: flex;
align-items: flex-start;
overflow-x: scroll;
list-style: none;
margin: 0 36px;
li.term-list-item {
position: relative;
display: block;
margin: 16px 32px 16px 0px;
width: calc(16.666% - 32px);
min-width: calc(16.666% - 32px);
&.term-list-item-grid {
margin: 16px;
width: calc(25% - 16px);
min-width: calc(25% - 16px);
}
a {
color: #454647;
font-weight: bold;
line-height: normal;
}
img {
height: auto;
padding: 0px;
margin-bottom: 0.5rem;
}
&:hover a {
color: #454647;
text-decoration: none;
}
button {
position: absolute !important;
background-color: rgba(255, 255, 255, 0.75);
color: #454647;
padding: 2px;
margin-left: 5px;
min-width: 14px;
visibility: hidden;
position: relative;
opacity: 0;
right: -14px;
top: 0px;
justify-content: center;
z-index: 999;
}
&:hover button {
visibility: visible;
background-color: rgba(255, 255, 255, 1) !important;
opacity: 1;
right: -8px;
top: -8px;
border: 1px solid #cbcbcb;
border-radius: 12px;
transition: opacity linear 0.15s, right linear 0.15s;
}
&:hover button:hover {
background-color: rgba(255, 255, 255, 1) !important;
border: 1px solid #cbcbcb !important;
}
.term-items-grid {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 50% 50%;
img:first-of-type {
grid-column-start: 1;
grid-column-end: span 2;
grid-row-start: 1;
grid-row-end: span 2;
}
img {
padding: 2px;
margin-bottom: 0px;
}
}
}
}
@media only screen and (max-width: 1686px) {
ul.terms-list-edit li.term-list-item {
width: calc(20% - 32px);
min-width: calc(20% - 32px);
}
}
@media only screen and (max-width: 1452px) {
ul.terms-list-edit li.term-list-item {
width: calc(25% - 32px);
min-width: calc(25% - 32px);
}
}
@media only screen and (max-width: 1118px) {
ul.terms-edit li.term-list-item {
width: calc(33.333% - 32px);
min-width: calc(33.333% - 32px);
}
}
@media only screen and (max-width: 854px) {
ul.terms-list-edit li.term-list-item {
width: calc(50% - 32px);
min-width: calc(50% - 32px);
}
}
@media only screen and (max-width: 584px) {
ul.terms-list-edit li.term-list-item {
width: calc(100% - 32px);
min-width: calc(100% - 32px);
}
}
}

View File

@ -0,0 +1,309 @@
import tainacan from '../../api-client/axios.js';
import axios from 'axios';
const { __ } = wp.i18n;
const { TextControl, Button, Modal, CheckboxControl, Spinner } = wp.components;
export default class TermsModal extends React.Component {
constructor(props) {
super(props);
// Initialize state
this.state = {
searchTermName: '',
termsRequestSource: undefined,
terms: [],
temporarySelectedTerms: [],
isLoadingTerms: false,
modalTerms: [],
totalModalTerms: 0,
termsPerPage: 24,
termsPage: 1,
};
// Bind events
this.selectTemporaryTerm = this.selectTemporaryTerm.bind(this);
this.removeTemporaryTermOfId = this.removeTemporaryTermOfId.bind(this);
this.applySelectedTerms = this.applySelectedTerms.bind(this);
this.isTemporaryTermSelected = this.isTemporaryTermSelected.bind(this);
this.toggleSelectTemporaryTerm = this.toggleSelectTemporaryTerm.bind(this);
this.cancelSelection = this.cancelSelection.bind(this);
this.selectTerm = this.selectTerm.bind(this);
this.fetchModalTerms = this.fetchModalTerms.bind(this);
this.fetchTerms = this.fetchTerms.bind(this);
}
componentWillMount() {
this.fetchModalTerms();
this.setState( {
terms: [],
termsPage: 1,
temporarySelectedTerms: JSON.parse(JSON.stringify(this.props.selectedTermsObject))
} );
}
selectTemporaryTerm(term) {
let existingTermIndex = this.state.temporarySelectedTerms.findIndex((existingTerm) => existingTerm.id == term.id);
if (existingTermIndex < 0) {
let aTemporarySelectedTerms = this.state.temporarySelectedTerms;
aTemporarySelectedTerms.push({
id: term.id,
name: term.name,
url: term.url,
thumbnail: term.thumbnail
});
this.setState({ temporarySelectedTerms: aTemporarySelectedTerms });
}
}
removeTemporaryTermOfId(termId) {
let existingTermIndex = this.state.temporarySelectedTerms.findIndex((existingTerm) => existingTerm.id == termId);
if (existingTermIndex >= 0) {
let aTemporarySelectedTerms = this.state.temporarySelectedTerms;
aTemporarySelectedTerms.splice(existingTermIndex, 1);
this.setState({ temporarySelectedTerms: aTemporarySelectedTerms });
}
}
applySelectedTerms() {
let aSelectedTermsObject = JSON.parse(JSON.stringify(this.state.temporarySelectedTerms));
this.props.onApplySelection(aSelectedTermsObject);
}
isTemporaryTermSelected(termId) {
return this.state.temporarySelectedTerms.findIndex(term => term.id == termId) >= 0;
}
toggleSelectTemporaryTerm(term, isChecked) {
if (isChecked)
this.selectTemporaryTerm(term);
else
this.removeTemporaryTermOfId(term.id);
}
cancelSelection() {
this.setState({
termsPage: 1,
modalTerms: []
});
this.props.onCancelSelection();
}
selectTerm(selectedTermId) {
this.setState({
termId: selectedTermId
});
this.fetchTerm();
this.fetchModalTerms();
}
fetchModalTerms() {
let currentModalTerms = this.state.modalTerms;
if (this.state.termsPage <= 1)
currentModalTerms = [];
let endpoint = '/terms/?orderby=title&order=asc&perpage=' + this.state.termsPerPage + '&paged=' + this.state.termsPage;
this.setState({
isLoadingTerms: true,
modalTerms: currentModalTerms,
});
tainacan.get(endpoint)
.then(response => {
for (let term of response.data) {
currentModalTerms.push({
name: term.name,
id: term.id,
url: term.url,
thumbnail: [{
src: term.thumbnail['tainacan-medium'] != undefined ? term.thumbnail['tainacan-medium'][0] : term.thumbnail['medium'][0],
alt: term.name
}]
});
}
this.setState({
termsPage: this.state.termsPage + 1,
isLoadingTerms: false,
modalTerms: currentModalTerms,
totalModalTerms: response.headers['x-wp-total']
});
return currentModalTerms;
})
.catch(error => {
console.log('Error trying to fetch terms: ' + error);
});
}
fetchTerms(name) {
if (this.state.termsRequestSource != undefined)
this.state.termsRequestSource.cancel('Previous terms search canceled.');
let aTermRequestSource = axios.CancelToken.source();
this.setState({
termsRequestSource: aTermRequestSource,
isLoadingTerms: true
});
let endpoint = '/terms/?orderby=title&order=asc&perpage=' + this.state.termsPerPage;
if (name != undefined && name != '')
endpoint += '&search=' + name;
tainacan.get(endpoint, { cancelToken: aTermRequestSource.token })
.then(response => {
let someTerms = this.state.terms;
someTerms = response.data.map((term) => ({
name: term.name,
id: term.id,
url: term.url,
thumbnail: [{
src: term.thumbnail['tainacan-medium'] != undefined ? term.thumbnail['tainacan-medium'][0] : term.thumbnail['medium'][0],
alt: term.name
}]
}));
this.setState({
isLoadingTerms: false,
terms: someTerms
});
return someTerms;
})
.catch(error => {
console.log('Error trying to fetch terms: ' + error);
});
}
render() {
return (
<Modal
className="wp-block-tainacan-modal"
title={__('Select the desired terms from your Taxonomy', 'tainacan')}
onRequestClose={ () => this.cancelSelection() }
contentLabel={__('Select terms', 'tainacan')}>
<div>
<div className="modal-search-area">
<TextControl
label={__('Search for a term', 'tainacan')}
value={ this.state.searchTermName }
onInput={(value) => {
this.setState({
searchTermName: value.target.value
});
}}
onChange={(value) => this.fetchTerms(value)}/>
</div>
{(
this.state.searchTermName != '' ? (
this.state.terms.length > 0 ?
(
<div>
<ul className="modal-checkbox-list">
{
this.state.terms.map((term) =>
<li
key={ term.id }
className="modal-checkbox-list-item">
{ term.thumbnail ?
<img
aria-hidden
src={ term.thumbnail && term.thumbnail[0] && term.thumbnail[0].src ? term.thumbnail[0].src : `${tainacan_plugin.base_url}/admin/images/placeholder_square.png`}
alt={ term.thumbnail && term.thumbnail[0] ? term.thumbnail[0].alt : term.name }/>
: null
}
<CheckboxControl
label={ term.name }
checked={ this.isTemporaryTermSelected(term.id) }
onChange={ ( isChecked ) => { this.toggleSelectTemporaryTerm(term, isChecked) } }
/>
</li>
)
}
</ul>
{ this.state.isLoadingTerms ? <div class="spinner-container"><Spinner /></div> : null }
</div>
)
: this.state.isLoadingTerms ? <div class="spinner-container"><Spinner /></div> :
<div className="modal-loadmore-section">
<p>{ __('Sorry, no terms found.', 'tainacan') }</p>
</div>
) :
this.state.modalTerms.length > 0 ?
(
<div>
<ul className="modal-checkbox-list">
{
this.state.modalTerms.map((term) =>
<li
key={ term.id }
className="modal-checkbox-list-item">
{ term.thumbnail ?
<img
aria-hidden
src={ term.thumbnail && term.thumbnail[0] && term.thumbnail[0].src ? term.thumbnail[0].src : `${tainacan_plugin.base_url}/admin/images/placeholder_square.png`}
alt={ term.thumbnail && term.thumbnail[0] ? term.thumbnail[0].alt : term.name }/>
: null
}
<CheckboxControl
label={ term.name }
checked={ this.isTemporaryTermSelected(term.id) }
onChange={ ( isChecked ) => { this.toggleSelectTemporaryTerm(term, isChecked) } } />
</li>
)
}
{ this.state.isLoadingTerms ? <div class="spinner-container"><Spinner /></div> : null }
</ul>
<div className="modal-loadmore-section">
<p>{ __('Showing', 'tainacan') + " " + this.state.modalTerms.length + " " + __('of', 'tainacan') + " " + this.state.totalModalTerms + " " + __('terms', 'tainacan') + "."}</p>
{
this.state.modalTerms.length < this.state.totalModalTerms ? (
<Button
isDefault
isSmall
onClick={ () => this.fetchModalTerms() }>
{__('Load more', 'tainacan')}
</Button>
) : null
}
</div>
</div>
) : this.state.isLoadingTerms ? <Spinner /> :
<div className="modal-loadmore-section">
<p>{ __('Sorry, no terms found.', 'tainacan') }</p>
</div>
)}
<div className="modal-footer-area">
<Button
isDefault
onClick={ () => this.cancelSelection() }>
{__('Cancel', 'tainacan')}
</Button>
<Button
isPrimary
type="submit"
onClick={ () => this.applySelectedTerms() }>
{__('Finish', 'tainacan')}
</Button>
</div>
</div>
</Modal>
);
}
}

View File

@ -0,0 +1,551 @@
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { RangeControl, Spinner, Button, BaseControl, ToggleControl, SelectControl, Placeholder, IconButton, PanelBody } = wp.components;
const { InspectorControls } = wp.editor;
import CarouselTermsModal from './carousel-terms-modal.js';
import tainacan from '../../api-client/axios.js';
import axios from 'axios';
import qs from 'qs';
registerBlockType('tainacan/carousel-terms-list', {
title: __('Tainacan Terms Carousel', 'tainacan'),
icon:
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height="24px"
width="24px">
<path
fill="#298596"
d="M18,17v2H12a5.65,5.65,0,0,0-.36-2ZM2,7v7.57a5.74,5.74,0,0,1,2-1.2V7ZM20,6H15L13,4H8A2,2,0,0,0,6,6v7a6,6,0,0,1,5.19,3H20a2,2,0,0,0,2-2V8A2,2,0,0,0,20,6ZM7,16.05v6.06l3.06-3.06ZM5,22.11V16.05L1.94,19.11Z"/>
</svg>,
category: 'tainacan-blocks',
keywords: [ __( 'terms', 'tainacan' ), __( 'carousel', 'tainacan' ), __( 'slider', 'tainacan' ), __( 'taxonomy', 'tainacan' ) ],
attributes: {
content: {
type: 'array',
source: 'children',
selector: 'div'
},
terms: {
type: Array,
default: []
},
isModalOpen: {
type: Boolean,
default: false
},
selectedTerms: {
type: Array,
default: []
},
itemsRequestSource: {
type: String,
default: undefined
},
maxTermsNumber: {
type: Number,
value: undefined
},
isLoading: {
type: Boolean,
value: false
},
isLoadingTerm: {
type: Boolean,
value: false
},
arrowsPosition: {
type: String,
value: 'search'
},
autoPlay: {
type: Boolean,
value: false
},
autoPlaySpeed: {
type: Number,
value: 3
},
loopSlides: {
type: Boolean,
value: false
},
hideName: {
type: Boolean,
value: true
},
showTermThumbnail: {
type: Boolean,
value: false
},
term: {
type: Object,
value: undefined
},
blockId: {
type: String,
default: undefined
},
termBackgroundColor: {
type: String,
default: "#454647"
},
termTextColor: {
type: String,
default: "#ffffff"
}
},
supports: {
align: ['full', 'wide'],
html: false,
multiple: true
},
edit({ attributes, setAttributes, className, isSelected, clientId }){
let {
terms,
content,
isModalOpen,
itemsRequestSource,
selectedTerms,
isLoading,
arrowsPosition,
autoPlay,
autoPlaySpeed,
loopSlides,
hideName,
showTermThumbnail
} = attributes;
// Obtains block's client id to render it on save function
setAttributes({ blockId: clientId });
function prepareItem(term, termItems) {
return (
<li
key={ term.id }
className={ 'term-list-item ' + (!showTermThumbnail ? 'term-list-item-grid' : '')}>
<IconButton
onClick={ () => removeItemOfId(term.id) }
icon="no-alt"
label={__('Remove', 'tainacan')}/>
<a
id={ isNaN(term.id) ? term.id : 'term-id-' + term.id }
href={ term.url }
target="_blank">
{ !showTermThumbnail ?
<div class="term-items-grid">
<img
src={
termItems[0] && termItems[0].thumbnail && termItems[0].thumbnail['tainacan-medium'][0] && termItems[0].thumbnail['tainacan-medium'][0]
?
termItems[0].thumbnail['tainacan-medium'][0]
:
(termItems[0] && termItems[0].thumbnail && termItems[0].thumbnail['thumbnail'][0] && termItems[0].thumbnail['thumbnail'][0]
?
termItems[0].thumbnail['thumbnail'][0]
:
`${tainacan_plugin.base_url}/admin/images/placeholder_square.png`)
}
alt={ termItems[0] && termItems[0].name ? termItems[0].name : __( 'Thumbnail', 'tainacan' ) }/>
<img
src={
termItems[1] && termItems[1].thumbnail && termItems[1].thumbnail['tainacan-medium'][0] && termItems[1].thumbnail['tainacan-medium'][0]
?
termItems[1].thumbnail['tainacan-medium'][0]
:
(termItems[1] && termItems[1].thumbnail && termItems[1].thumbnail['thumbnail'][0] && termItems[1].thumbnail['thumbnail'][0]
?
termItems[1].thumbnail['thumbnail'][0]
:
`${tainacan_plugin.base_url}/admin/images/placeholder_square.png`)
}
alt={ termItems[1] && termItems[1].name ? termItems[1].name : __( 'Thumbnail', 'tainacan' ) }/>
<img
src={
termItems[2] && termItems[2].thumbnail && termItems[2].thumbnail['tainacan-medium'][0] && termItems[2].thumbnail['tainacan-medium'][0]
?
termItems[2].thumbnail['tainacan-medium'][0]
:
(termItems[2] && termItems[2].thumbnail && termItems[2].thumbnail['thumbnail'][0] && termItems[2].thumbnail['thumbnail'][0]
?
termItems[2].thumbnail['thumbnail'][0]
:
`${tainacan_plugin.base_url}/admin/images/placeholder_square.png`)
}
alt={ termItems[2] && termItems[2].name ? termItems[2].name : __( 'Thumbnail', 'tainacan' ) }/>
</div>
:
<img
src={
term.thumbnail && term.thumbnail['tainacan-medium'][0] && term.thumbnail['tainacan-medium'][0]
?
term.thumbnail['tainacan-medium'][0]
:
(term.thumbnail && term.thumbnail['thumbnail'][0] && term.thumbnail['thumbnail'][0]
?
term.thumbnail['thumbnail'][0]
:
`${tainacan_plugin.base_url}/admin/images/placeholder_square.png`)
}
alt={ term.name ? term.name : __( 'Thumbnail', 'tainacan' ) }/>
}
{ !hideName ? <span>{ term.name ? term.name : '' }</span> : null }
</a>
</li>
);
}
function setContent(){
isLoading = true;
setAttributes({
isLoading: isLoading
});
if (itemsRequestSource != undefined && typeof itemsRequestSource == 'function')
itemsRequestSource.cancel('Previous terms search canceled.');
itemsRequestSource = axios.CancelToken.source();
terms = [];
let endpoint = '/terms?'+ qs.stringify({ postin: selectedTerms }) + '&fetch_only=name,url,thumbnail';
tainacan.get(endpoint, { cancelToken: itemsRequestSource.token })
.then(response => {
if (showTermThumbnail) {
for (let term of response.data) {
terms.push(prepareItem(term));
}
setAttributes({
content: <div></div>,
terms: terms,
isLoading: false,
itemsRequestSource: itemsRequestSource
});
} else {
let promises = [];
for (let term of response.data) {
promises.push(
tainacan.get('/term/' + term.id + '/items?perpage=3&fetch_only=name,url,thumbnail')
.then(response => { return({ term: term, termItems: response.data.items }) })
.catch((error) => console.log(error))
);
}
axios.all(promises).then((results) => {
for (let result of results) {
terms.push(prepareItem(result.term, result.termItems));
}
setAttributes({
content: <div></div>,
terms: terms,
isLoading: false,
itemsRequestSource: itemsRequestSource
});
})
}
});
}
function openCarouselModal() {
isModalOpen = true;
setAttributes( {
isModalOpen: isModalOpen
} );
}
function removeItemOfId(itemId) {
let existingItemIndex = terms.findIndex((existingItem) => existingItem.key == itemId);
if (existingItemIndex >= 0)
terms.splice(existingItemIndex, 1);
let existingSelectedItemIndex = selectedTerms.findIndex((existingSelectedItem) => existingSelectedItem == itemId);
if (existingSelectedItemIndex >= 0)
selectedTerms.splice(existingSelectedItemIndex, 1);
setAttributes({
selectedTerms: selectedTerms,
terms: terms,
content: <div></div>
});
}
// Executed only on the first load of page
if(content && content.length && content[0].type)
setContent();
return (
<div className={className}>
<div>
<InspectorControls>
<PanelBody
title={__('Carousel', 'tainacan')}
initialOpen={ true }
>
<div>
{/* <ToggleControl
label={__('Show term\'s thumbnail', 'tainacan')}
help={ !showTermThumbnail ? __('Toggle to show items grid instead of term\'s thumbnail', 'tainacan') : __('Do not show term\'s thumbnail instead of items grid', 'tainacan')}
checked={ showTermThumbnail ? showTermThumbnail : false }
onChange={ ( isChecked ) => {
showTermThumbnail = isChecked;
setAttributes({ showTermThumbnail: showTermThumbnail });
setContent();
}
}
/> */}
<BaseControl
id="term-carousel-view-modes"
label={ __('Term layout', 'tainacan')}>
<div className="term-carousel-view-modes">
<button
onClick={ () => {
showTermThumbnail = false;
setAttributes({ showTermThumbnail: showTermThumbnail });
setContent();
}
}
className={'term-carousel-view-mode-grid' + (showTermThumbnail ? '' : ' is-active')}>
<div>
<div />
<div />
<div />
</div>
<label>{ __('Items\'s grid', 'tainacan') }</label>
</button>
<button
onClick={ () => {
showTermThumbnail = true;
setAttributes({ showTermThumbnail: showTermThumbnail });
setContent();
}
}
className={'term-carousel-view-mode-thumbnail' + (showTermThumbnail ? ' is-active' : '')}>
<div />
<label>{ __('Thumbnail', 'tainacan') }</label>
</button>
</div>
</BaseControl>
<ToggleControl
label={__('Hide name', 'tainacan')}
help={ !hideName ? __('Toggle to hide term\'s name', 'tainacan') : __('Do not hide term\'s name', 'tainacan')}
checked={ hideName }
onChange={ ( isChecked ) => {
hideName = isChecked;
setAttributes({ hideName: hideName });
setContent();
}
}
/>
<ToggleControl
label={__('Loop slides', 'tainacan')}
help={ !loopSlides ? __('Toggle to make slides loop from first to last', 'tainacan') : __('Do not loop slides from first to last', 'tainacan')}
checked={ loopSlides }
onChange={ ( isChecked ) => {
loopSlides = isChecked;
setAttributes({ loopSlides: loopSlides });
}
}
/>
<ToggleControl
label={__('Auto play', 'tainacan')}
help={ !autoPlay ? __('Toggle to automatically slide to next term', 'tainacan') : __('Do not automatically slide to next term', 'tainacan')}
checked={ autoPlay }
onChange={ ( isChecked ) => {
autoPlay = isChecked;
setAttributes({ autoPlay: autoPlay });
}
}
/>
{
autoPlay ?
<RangeControl
label={__('Seconds before translating to next', 'tainacan')}
value={ autoPlaySpeed ? autoPlaySpeed : 3 }
onChange={ ( aAutoPlaySpeed ) => {
autoPlaySpeed = aAutoPlaySpeed;
setAttributes( { autoPlaySpeed: aAutoPlaySpeed } )
}}
min={ 1 }
max={ 5 }
/>
: null
}
<SelectControl
label={__('Arrows', 'tainacan')}
value={ arrowsPosition }
options={ [
{ label: __('Around', 'tainacan'), value: 'around' },
{ label: __('Left', 'tainacan'), value: 'left' },
{ label: __('Right', 'tainacan'), value: 'right' }
] }
onChange={ ( aPosition ) => {
arrowsPosition = aPosition;
setAttributes({ arrowsPosition: arrowsPosition });
}}/>
</div>
</PanelBody>
</InspectorControls>
</div>
{ isSelected ?
(
<div>
{ isModalOpen ?
<CarouselTermsModal
selectedTermsObject={ selectedTerms }
onApplySelection={ (aSelectionOfTerms) => {
selectedTerms = selectedTerms.concat(aSelectionOfTerms.map((term) => { return term.id; }));
setAttributes({
selectedTerms: selectedTerms,
isModalOpen: false
});
setContent();
}}
onCancelSelection={ () => setAttributes({ isModalOpen: false }) }/>
: null
}
{ terms.length ? (
<div className="block-control">
<p>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height="24px"
width="24px">
<path d="M18,17v2H12a5.65,5.65,0,0,0-.36-2ZM2,7v7.57a5.74,5.74,0,0,1,2-1.2V7ZM20,6H15L13,4H8A2,2,0,0,0,6,6v7a6,6,0,0,1,5.19,3H20a2,2,0,0,0,2-2V8A2,2,0,0,0,20,6ZM7,16.05v6.06l3.06-3.06ZM5,22.11V16.05L1.94,19.11Z"/>
</svg>
{__('List terms on a Carousel', 'tainacan')}
</p>
<Button
isPrimary
type="submit"
onClick={ () => openCarouselModal() }>
{__('Add more terms', 'tainacan')}
</Button>
</div>
): null
}
</div>
) : null
}
{ !terms.length && !isLoading ? (
<Placeholder
icon={(
<img
width={148}
src={ `${tainacan_plugin.base_url}/admin/images/tainacan_logo_header.svg` }
alt="Tainacan Logo"/>
)}>
<p>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height="24px"
width="24px">
<path d="M18,17v2H12a5.65,5.65,0,0,0-.36-2ZM2,7v7.57a5.74,5.74,0,0,1,2-1.2V7ZM20,6H15L13,4H8A2,2,0,0,0,6,6v7a6,6,0,0,1,5.19,3H20a2,2,0,0,0,2-2V8A2,2,0,0,0,20,6ZM7,16.05v6.06l3.06-3.06ZM5,22.11V16.05L1.94,19.11Z"/>
</svg>
{__('List terms on a Carousel, using search or term selection.', 'tainacan')}
</p>
<Button
isPrimary
type="submit"
onClick={ () => openCarouselModal() }>
{__('Select Terms', 'tainacan')}
</Button>
</Placeholder>
) : null
}
{ isLoading ?
<div class="spinner-container">
<Spinner />
</div> :
<div>
{ isSelected && terms.length ?
<div class="preview-warning">{__('Warning: this is just a demonstration. To see the carousel in action, either preview or publish your post.', 'tainacan')}</div>
: null
}
{ terms.length ? (
<div
className={'terms-list-edit-container ' + (arrowsPosition ? 'has-arrows-' + arrowsPosition : '')}>
<button
class="swiper-button-prev"
slot="button-prev"
style={{ cursor: 'not-allowed' }}>
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
<ul className={'terms-list-edit'}>
{ terms }
</ul>
<button
class="swiper-button-next"
slot="button-next"
style={{ cursor: 'not-allowed' }}>
<svg
width="42"
height="42"
viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
<path
d="M0 0h24v24H0z"
fill="none"/>
</svg>
</button>
</div>
):null
}
</div>
}
</div>
);
},
save({ attributes, className }){
const {
content,
blockId,
selectedTerms,
arrowsPosition,
maxTermsNumber,
autoPlay,
autoPlaySpeed,
loopSlides,
hideName,
showTermThumbnail
} = attributes;
return <div
className={ className }
selected-terms={ JSON.stringify(selectedTerms) }
arrows-position={ arrowsPosition }
auto-play={ '' + autoPlay }
auto-play-speed={ autoPlaySpeed }
loop-slides={ '' + loopSlides }
hide-name={ '' + hideName }
max-terms-number={ maxTermsNumber }
tainacan-api-root={ tainacan_plugin.root }
tainacan-base-url={ tainacan_plugin.base_url }
show-term-thumbnail={ '' + showTermThumbnail }
id={ 'wp-block-tainacan-carousel-terms-list_' + blockId }>
{ content }
</div>
}
});

View File

@ -27,7 +27,11 @@ module.exports = {
gutenberg_carousel_collections_list_theme: './src/gutenberg-blocks/tainacan-collections/carousel-collections-list/carousel-collections-list-theme.js',
gutenberg_facets_list: './src/gutenberg-blocks/tainacan-facets/facets-list/index.js',
gutenberg_facets_list_theme: './src/gutenberg-blocks/tainacan-facets/facets-list/facets-list-theme.js'
gutenberg_facets_list_theme: './src/gutenberg-blocks/tainacan-facets/facets-list/facets-list-theme.js',
gutenberg_carousel_terms_list: './src/gutenberg-blocks/tainacan-terms/carousel-terms-list/index.js',
gutenberg_carousel_terms_list_theme: './src/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list-theme.js'
},
output: {
path: path.resolve(__dirname, './src/assets/'),