WooCommerce Gutenberg Products Block - Version 1.2.0

Version Description

  • 2018-12-04 =
  • Feature - Stand-alone product category block with improved category selection interface.
  • Fix - All users who can edit posts can now use these blocks thanks to a new set of API endpoints allowing view access to products, product categories, and product attributes.
  • Fix - Compatibility with WP 5.0, fixed error Cannot read property Toolbar of undefined.
  • Fix - Only published products are shown in previews.
  • Enhancement - Translations should now load into the block (for WP 5.0+).
  • Enhancement - Modernized build process and developer tools, and added tests for faster future development.

=

Download this release

Release Info

Developer ryelle
Plugin Icon 128x128 WooCommerce Gutenberg Products Block
Version 1.2.0
Comparing to
See all releases

Code changes from version 1.1.2 to 1.2.0

Files changed (40) hide show
  1. Gruntfile.js +0 -145
  2. assets/css/abstracts/_breakpoints.scss +62 -0
  3. assets/css/abstracts/_colors.scss +45 -0
  4. assets/css/abstracts/_mixins.scss +59 -0
  5. assets/css/abstracts/_variables.scss +7 -0
  6. assets/css/gutenberg-products-block-rtl.css +0 -1
  7. assets/css/gutenberg-products-block.css +0 -1
  8. assets/css/product-category-block.scss +69 -0
  9. assets/css/{gutenberg-products-block.scss → products-block.scss} +75 -52
  10. assets/js/components/product-category-control/index.js +158 -0
  11. assets/js/components/product-category-control/style.scss +75 -0
  12. assets/js/components/product-preview/index.js +48 -0
  13. assets/js/components/product-preview/style.scss +90 -0
  14. assets/js/components/search-list-control/hierarchy.js +48 -0
  15. assets/js/components/search-list-control/icons.js +48 -0
  16. assets/js/components/search-list-control/index.js +332 -0
  17. assets/js/components/search-list-control/style.scss +111 -0
  18. assets/js/{products-block.jsx → legacy/products-block.jsx} +114 -99
  19. assets/js/{views → legacy/views}/attribute-select.jsx +46 -44
  20. assets/js/{views → legacy/views}/category-select.jsx +36 -38
  21. assets/js/{views → legacy/views}/specific-select.jsx +44 -46
  22. assets/js/product-category-block.js +352 -0
  23. assets/js/products-block.js +0 -3415
  24. assets/js/utils/get-query.js +30 -0
  25. assets/js/utils/get-shortcode.js +30 -0
  26. assets/js/utils/shared-attributes.js +32 -0
  27. build/product-category-block.css +2472 -0
  28. build/product-category-block.js +69 -0
  29. build/products-block.css +565 -0
  30. build/products-block.js +1 -0
  31. includes/class-wgpb-product-attribute-terms-controller.php +150 -0
  32. includes/class-wgpb-product-attributes-controller.php +178 -0
  33. includes/class-wgpb-product-categories-controller.php +149 -0
  34. includes/class-wgpb-products-controller.php +209 -676
  35. languages/woo-gutenberg-products-block.php +241 -0
  36. package-lock.json +0 -8643
  37. package.json +0 -52
  38. readme.txt +27 -8
  39. webpack.config.js +0 -24
  40. woocommerce-gutenberg-products-block.php +112 -24
Gruntfile.js DELETED
@@ -1,145 +0,0 @@
1
- /**
2
- * This file has a bunch on extra stuff that isn't needed and might not work.
3
- * That's OK. This is just for compiling the CSS during Products block prototype development. :)
4
- */
5
-
6
- /* jshint node:true */
7
- module.exports = function( grunt ) {
8
- 'use strict';
9
-
10
- grunt.initConfig({
11
-
12
- // Setting folder templates.
13
- dirs: {
14
- css: 'assets/css',
15
- fonts: 'assets/fonts',
16
- images: 'assets/images',
17
- js: 'assets/js'
18
- },
19
-
20
- // Sass linting with Stylelint.
21
- stylelint: {
22
- options: {
23
- configFile: '.stylelintrc'
24
- },
25
- all: [
26
- '<%= dirs.css %>/*.scss',
27
- ]
28
- },
29
-
30
- // Compile all .scss files.
31
- sass: {
32
- compile: {
33
- options: {
34
- sourceMap: 'none'
35
- },
36
- files: [{
37
- expand: true,
38
- cwd: '<%= dirs.css %>/',
39
- src: ['*.scss'],
40
- dest: '<%= dirs.css %>/',
41
- ext: '.css'
42
- }]
43
- }
44
- },
45
-
46
- // Generate RTL .css files
47
- rtlcss: {
48
- woocommerce: {
49
- expand: true,
50
- cwd: '<%= dirs.css %>',
51
- src: [
52
- '*.css',
53
- '!select2.css',
54
- '!*-rtl.css'
55
- ],
56
- dest: '<%= dirs.css %>/',
57
- ext: '-rtl.css'
58
- }
59
- },
60
-
61
- // Minify all .css files.
62
- cssmin: {
63
- minify: {
64
- expand: true,
65
- cwd: '<%= dirs.css %>/',
66
- src: ['*.css'],
67
- dest: '<%= dirs.css %>/',
68
- ext: '.css'
69
- }
70
- },
71
-
72
- // Watch changes for assets.
73
- watch: {
74
- css: {
75
- files: ['<%= dirs.css %>/*.scss'],
76
- tasks: ['sass', 'rtlcss', 'cssmin']
77
- },
78
- js: {
79
- files: [
80
- '<%= dirs.js %>/admin/*js',
81
- '<%= dirs.js %>/frontend/*js',
82
- '!<%= dirs.js %>/admin/*.min.js',
83
- '!<%= dirs.js %>/frontend/*.min.js'
84
- ],
85
- tasks: ['jshint', 'uglify']
86
- }
87
- },
88
-
89
- // Autoprefixer.
90
- postcss: {
91
- options: {
92
- processors: [
93
- require( 'autoprefixer' )({
94
- browsers: [
95
- '> 0.1%',
96
- 'ie 8',
97
- 'ie 9'
98
- ]
99
- })
100
- ]
101
- },
102
- dist: {
103
- src: [
104
- '<%= dirs.css %>/*.css'
105
- ]
106
- }
107
- }
108
- });
109
-
110
- // Load NPM tasks to be used here
111
- grunt.loadNpmTasks( 'grunt-sass' );
112
- grunt.loadNpmTasks( 'grunt-rtlcss' );
113
- grunt.loadNpmTasks( 'grunt-postcss' );
114
- grunt.loadNpmTasks( 'grunt-stylelint' );
115
- grunt.loadNpmTasks( 'grunt-wp-i18n' );
116
- grunt.loadNpmTasks( 'grunt-checktextdomain' );
117
- grunt.loadNpmTasks( 'grunt-contrib-jshint' );
118
- grunt.loadNpmTasks( 'grunt-contrib-uglify' );
119
- grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
120
- grunt.loadNpmTasks( 'grunt-contrib-concat' );
121
- grunt.loadNpmTasks( 'grunt-contrib-watch' );
122
- grunt.loadNpmTasks( 'grunt-contrib-clean' );
123
-
124
- // Register tasks
125
- grunt.registerTask( 'default', [
126
- 'css',
127
- ]);
128
-
129
- grunt.registerTask( 'css', [
130
- 'sass',
131
- 'rtlcss',
132
- 'postcss',
133
- 'cssmin',
134
- ]);
135
-
136
- grunt.registerTask( 'docs', [
137
- 'clean:apidocs',
138
- 'shell:apidocs'
139
- ]);
140
-
141
- // Only an alias to 'default' task.
142
- grunt.registerTask( 'dev', [
143
- 'default'
144
- ]);
145
- };
assets/css/abstracts/_breakpoints.scss ADDED
@@ -0,0 +1,62 @@
1
+ /* stylelint-disable block-closing-brace-newline-after */
2
+
3
+ // Breakpoints
4
+ // Forked from https://github.com/Automattic/wp-calypso/blob/46ae24d8800fb85da6acf057a640e60dac988a38/assets/stylesheets/shared/mixins/_breakpoints.scss
5
+
6
+ // Think very carefully before adding a new breakpoint.
7
+ // The list below is based on wp-admin's main breakpoints
8
+ $breakpoints: 320px, 400px, 600px, 782px, 960px, 1280px, 1440px;
9
+
10
+ @mixin breakpoint( $sizes... ) {
11
+ @each $size in $sizes {
12
+ @if type-of( $size ) == string {
13
+ $approved-value: 0;
14
+ @each $breakpoint in $breakpoints {
15
+ $and-larger: ">" + $breakpoint;
16
+ $and-smaller: "<" + $breakpoint;
17
+
18
+ @if $size == $and-smaller {
19
+ $approved-value: 1;
20
+ @media (max-width: $breakpoint) {
21
+ @content;
22
+ }
23
+ }
24
+ @else {
25
+ @if $size == $and-larger {
26
+ $approved-value: 2;
27
+ @media (min-width: $breakpoint + 1) {
28
+ @content;
29
+ }
30
+ }
31
+ @else {
32
+ @each $breakpoint-end in $breakpoints {
33
+ $range: $breakpoint + "-" + $breakpoint-end;
34
+ @if $size == $range {
35
+ $approved-value: 3;
36
+ @media (min-width: $breakpoint + 1) and (max-width: $breakpoint-end) {
37
+ @content;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ @if $approved-value == 0 {
45
+ $sizes: "";
46
+ @each $breakpoint in $breakpoints {
47
+ $sizes: $sizes + " " + $breakpoint;
48
+ }
49
+ @warn "ERROR in breakpoint( #{ $size } ) : You can only use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]";
50
+ }
51
+ }
52
+ @else {
53
+ $sizes: "";
54
+ @each $breakpoint in $breakpoints {
55
+ $sizes: $sizes + " " + $breakpoint;
56
+ }
57
+ @error "ERROR in breakpoint( #{ $size } ) : Please wrap the breakpoint $size in parenthesis. You can use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]";
58
+ }
59
+ }
60
+ }
61
+
62
+ /* stylelint-enable */
assets/css/abstracts/_colors.scss ADDED
@@ -0,0 +1,45 @@
1
+ // Greys
2
+ $core-grey-light-100: #f8f9f9;
3
+ $core-grey-light-200: #f3f4f5;
4
+ $core-grey-light-300: #edeff0;
5
+ $core-grey-light-400: #e8eaeb;
6
+ $core-grey-light-500: #e2e4e7;
7
+ $core-grey-light-600: #d7dade;
8
+ $core-grey-light-700: #ccd0d4;
9
+ $core-grey-light-800: #b5bcc2;
10
+ $core-grey-light-900: #a2aab2;
11
+ $core-grey-dark-100: #86909b;
12
+ $core-grey-dark-200: #78848f;
13
+ $core-grey-dark-300: #6c7781; // This & below have 4.5+ contrast against white
14
+ $core-grey-dark-400: #606a73;
15
+ $core-grey-dark-500: #555d66;
16
+ $core-grey-dark-600: #40464d;
17
+ $core-grey-dark-700: #32373c;
18
+ $core-grey-dark-800: #23282d;
19
+ $core-grey-dark-900: #191e23;
20
+
21
+ $gray-text: $core-grey-dark-500;
22
+
23
+ // WooCommerce Purples
24
+ $woocommerce-100: #ffd7ff;
25
+ $woocommerce-200: #e2a5d7;
26
+ $woocommerce-300: #c88bbd;
27
+ $woocommerce-400: #af72a4;
28
+ $woocommerce-500: #95588a;
29
+ $woocommerce-600: #7c3f71;
30
+ $woocommerce-700: #622557;
31
+ $woocommerce-800: #490c3e;
32
+ $woocommerce-900: #2f0024;
33
+ $woocommerce: $woocommerce-500;
34
+
35
+ $wp-admin-background: #f1f1f1;
36
+ $black: #24292d; // same as wp-admin sidebar
37
+
38
+ $white: #fff;
39
+
40
+ // Bright colors
41
+ $valid-green: #4ab866;
42
+ $notice-yellow: #ffb900;
43
+ $error-red: #d94f4f;
44
+ $box-shadow-blue: #5b9dd9;
45
+ $core-orange: #ca4a1f;
assets/css/abstracts/_mixins.scss ADDED
@@ -0,0 +1,59 @@
1
+ // Rem output with px fallback
2
+ @mixin font-size($sizeValue: 16, $lineHeight: false ) {
3
+ font-size: $sizeValue + px;
4
+ font-size: ($sizeValue / 16) + rem;
5
+ @if ($lineHeight) {
6
+ line-height: $lineHeight;
7
+ }
8
+ }
9
+
10
+ @mixin hover-state {
11
+ &:hover,
12
+ &:active,
13
+ &:focus {
14
+ @content;
15
+ }
16
+ }
17
+
18
+ // Adds animation to placeholder section
19
+ @mixin placeholder( $lighten-percentage: 30% ) {
20
+ animation: loading-fade 1.6s ease-in-out infinite;
21
+ background-color: $core-grey-light-500;
22
+ color: transparent;
23
+
24
+ &::after {
25
+ content: "\00a0";
26
+ }
27
+ }
28
+
29
+ // Adds animation to transforms
30
+ @mixin animate-transform( $duration: 0.2s ) {
31
+ transition: transform ease $duration;
32
+
33
+ @media screen and (prefers-reduced-motion: reduce) {
34
+ transition: none;
35
+ }
36
+ }
37
+
38
+ // Hide an element from sighted users, but availble to screen reader users.
39
+ @mixin visually-hidden() {
40
+ clip: rect(1px, 1px, 1px, 1px);
41
+ clip-path: inset(50%);
42
+ height: 1px;
43
+ width: 1px;
44
+ margin: -1px;
45
+ overflow: hidden;
46
+ /* Many screen reader and browser combinations announce broken words as they would appear visually. */
47
+ overflow-wrap: normal !important;
48
+ word-wrap: normal !important;
49
+ }
50
+
51
+ // Unhide a visually hidden element
52
+ @mixin visually-shown() {
53
+ clip: auto;
54
+ clip-path: none;
55
+ height: auto;
56
+ width: auto;
57
+ margin: unset;
58
+ overflow: hidden;
59
+ }
assets/css/abstracts/_variables.scss ADDED
@@ -0,0 +1,7 @@
1
+ $gap-largest: 40px;
2
+ $gap-larger: 36px;
3
+ $gap-large: 24px;
4
+ $gap: 16px;
5
+ $gap-small: 12px;
6
+ $gap-smaller: 8px;
7
+ $gap-smallest: 4px;
assets/css/gutenberg-products-block-rtl.css DELETED
@@ -1 +0,0 @@
1
- .wc-products-block-preview{overflow:hidden}.wc-products-block-preview .product-preview{float:right;text-align:center;margin-left:3.8%}.wc-products-block-preview.cols-1 .product-preview{float:none;margin-left:0}.wc-products-block-preview.cols-2 .product-preview{width:48%}.wc-products-block-preview.cols-2 .product-preview:nth-of-type(2n){margin-left:0}.wc-products-block-preview.cols-2 .product-preview:nth-of-type(2n+1){clear:both}.wc-products-block-preview.cols-3 .product-preview{width:30.75%}.wc-products-block-preview.cols-3 .product-preview:nth-of-type(3n){margin-left:0}.wc-products-block-preview.cols-3 .product-preview:nth-of-type(3n+1){clear:both}.wc-products-block-preview.cols-4 .product-preview{width:22.05%}.wc-products-block-preview.cols-4 .product-preview:nth-of-type(4n){margin-left:0}.wc-products-block-preview.cols-4 .product-preview:nth-of-type(4n+1){clear:both}.wc-products-block-preview.cols-5 .product-preview{width:16.9%}.wc-products-block-preview.cols-5 .product-preview:nth-of-type(5n){margin-left:0}.wc-products-block-preview.cols-5 .product-preview:nth-of-type(5n+1){clear:both}.wc-products-block-preview.cols-5 .product-preview .product-add-to-cart{font-size:.75em}.wc-products-block-preview.cols-6 .product-preview{width:13.5%}.wc-products-block-preview.cols-6 .product-preview:nth-of-type(6n){margin-left:0}.wc-products-block-preview.cols-6 .product-preview:nth-of-type(6n+1){clear:both}.wc-products-block-preview.cols-6 .product-preview .product-add-to-cart{font-size:.75em}.wc-products-block-preview .product-add-to-cart{display:inline-block;background:#ababab;border-radius:1.5em;color:#fff;cursor:pointer;padding:.75em 1.25em;line-height:1.2em;margin-top:.5em;margin-bottom:1em}.wc-products-settings{background-color:#f8f9f9;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;position:relative}.wc-products-settings__title{margin:0;padding:1.5rem 0;text-align:center;border-bottom:1px solid #e6eaee}.wc-products-settings__title .dashicon{vertical-align:top;margin-left:.25em}.wc-products-settings__footer{margin:2em 0 0;padding:1.5em 0;border-top:1px solid #e6eaee;text-align:center}.wc-products-settings__footer .button{padding-right:1.5em;padding-left:1.5em}.wc-products-display-options--popover+.wc-products-settings__footer,.wc-products-settings-heading+.wc-products-settings__footer{margin-top:-2em;border-top:none}p.wc-products-block-description{margin:2em 0 1.5em 0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:1em;text-align:center}.wc-products-display-options{margin:0 0 2.5em}.wc-products-display-options__option{display:-webkit-box;display:flex;-webkit-box-align:start;align-items:flex-start;flex-wrap:wrap;margin:0 auto;padding:1.25em 1.5em;max-width:80%;background:#fff;border-width:1px 1px 0;border-style:solid;border-color:#e6eaee;cursor:pointer}.wc-products-display-options__option:last-of-type{border-bottom-width:1px}.wc-products-display-options__option--attribute,.wc-products-display-options__option--best_selling,.wc-products-display-options__option--featured,.wc-products-display-options__option--on_sale,.wc-products-display-options__option--top_rated{display:none;background-color:#fdfdfd;padding-top:1em;padding-bottom:1em}.wc-products-display-options__option--attribute .wc-products-display-options__option-title,.wc-products-display-options__option--best_selling .wc-products-display-options__option-title,.wc-products-display-options__option--featured .wc-products-display-options__option-title,.wc-products-display-options__option--on_sale .wc-products-display-options__option-title,.wc-products-display-options__option--top_rated .wc-products-display-options__option-title{font-size:1.15em}.wc-products-display-options__option--attribute .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--best_selling .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--featured .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--on_sale .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--top_rated .wc-products-display-options__icon .dashicon{height:20px;width:20px}.wc-products-display-options__option--current{cursor:default}.wc-products-display-options__option--current .wc-products-display-options__option-title{color:#86909b}.wc-products-display-options__option-content{width:85%;align-self:center}.wc-products-display-options__option-title{display:block;font-size:1.25em}p.wc-products-display-options__option-description{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:1em;color:#86909b}.wc-products-display-options__icon{align-self:center;margin-right:auto;color:#b9bcc2}.wc-products-display-options__icon .dashicon{height:25px;width:25px}.wc-products-display-options--popover{position:absolute;left:-2em;max-width:60%;margin:0;z-index:999;box-shadow:0 2px 10px 0 rgba(0,0,0,.1);margin-top:-2.15em}.wc-products-display-options--popover .wc-products-display-options__option{margin:0;max-width:none}.wc-products-display-options--popover__arrow{width:2em;height:1.25em;position:absolute;top:-1.15em;left:30%;overflow:hidden}.wc-products-display-options--popover__arrow:after{content:'';position:absolute;width:1.25em;height:1.25em;background:#fff;-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);top:.625em;right:.3125em;box-shadow:0 2px 10px 0 rgba(0,0,0,.1);border:1px solid #e6eaee}.wc-products-display-options--extended .wc-products-display-options__option--attribute,.wc-products-display-options--extended .wc-products-display-options__option--best_selling,.wc-products-display-options--extended .wc-products-display-options__option--featured,.wc-products-display-options--extended .wc-products-display-options__option--on_sale,.wc-products-display-options--extended .wc-products-display-options__option--top_rated{display:-webkit-box;display:flex}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--category{border-bottom-width:1px}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--filter{margin-top:.5em}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--attribute{margin-bottom:.5em;border-bottom-width:1px}.wc-products-settings-heading{margin:0 0 2em 0;padding:1em 2em;text-align:center;border-bottom:1px solid #e6eaee}.wc-products-list-card{position:relative;margin-right:auto;margin-left:auto;padding:0 1em;overflow:hidden;box-sizing:border-box}.wc-products-list-card .wc-products-list-card__input-wrapper{position:relative;background:#fff;margin:0 0 1em}.wc-products-list-card .wc-products-list-card__input-wrapper .dashicon{position:absolute;top:calc(1em - 1px);right:1em;z-index:1}.wc-products-list-card input[type=search]{position:relative;width:100%;margin:0;padding:1em 3em 1em 1.25em;border-radius:0;background:0 0;border-color:#e6eaee;box-shadow:none;z-index:2}.wc-products-list-card .wc-products-list-card__results{max-height:200px;overflow-y:auto;overflow-x:hidden;box-sizing:border-box}.wc-products-list-card .wc-products-list-card__results ul{list-style:none}.wc-products-list-card .wc-products-list-card__results ul li{margin:0;border:1px solid #e6eaee;border-bottom-width:0}.wc-products-list-card .wc-products-list-card__results ul li:last-child{border-bottom-width:1px}.wc-products-list-card .wc-products-list-card__content{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;padding:.75em 1.25em;background:#fff;border-bottom:1px solid #e6eaee}.wc-products-list-card:after{content:'';position:absolute;right:0;bottom:0;width:100%;height:1.5em;background:-webkit-linear-gradient(rgba(255,255,255,.1) 0,#f8f9f9 100%);background:linear-gradient(rgba(255,255,255,.1) 0,#f8f9f9 100%)}.wc-products-list-card--taxonomy .wc-products-list-card__taxonomy-count{text-align:center;width:30px;font-size:.8em;border:1px solid #e9e9e9;border-radius:1em;color:#aaa}.wc-products-list-card--taxonomy input[type=checkbox]{position:relative;margin-top:0;margin-left:.75em;border-radius:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results{padding-bottom:1.3em}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li{margin-top:-1px}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li:first-child{margin-top:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li:last-child{border-bottom-width:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul{display:none;padding:1em 3.25em 1em 1.25em;background:#fdfdfd;border-bottom:1px solid #e6eaee}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul li{margin-bottom:.25em;border:none}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul li:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open{margin:.5em 0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open:first-child{margin-top:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul{display:block}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul li label{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul li .wc-products-list-card__taxonomy-count{margin-right:auto}.wc-products-list-card--taxonomy-category .wc-products-list-card__accordion-button{cursor:pointer;color:#666;margin:0 auto 0 1em;padding:0 .75em 0 0;border:none;border-radius:0;background:0 0;outline:0;text-decoration:none}.wc-products-list-card--taxonomy-category .wc-products-list-card__accordion-button .dashicon{align-self:center;display:-webkit-box;display:flex}.wc-products-list-card--taxonomy-category input[type=checkbox]:indeterminate:before{position:absolute;top:0;bottom:0;left:0;right:0;content:'';margin:42% 20%;width:60%;background:#0073aa}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results{padding-bottom:1.3em}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul{padding:1em 3.25em 1em 1.25em;background:#fdfdfd;border-bottom:1px solid #e6eaee}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul li{margin-bottom:.25em;border:none}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul li .wc-products-list-card__content{padding:0;background:0 0;border:none}.wc-products-list-card--taxonomy-atributes__atribute{margin:-1px 0 0;border-width:1px 1px 0;border-style:solid;border-color:#e6eaee}.wc-products-list-card--taxonomy-atributes__atribute:first-child{margin-top:0}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open{margin-top:.5em;margin-bottom:.5em}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open:first-child{margin-top:0}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__taxonomy-count{margin-right:auto}.wc-products-list-card--taxonomy-atributes input[type=radio]{position:relative;margin-top:0;margin-left:.75em;border-radius:100%}.wc-products-list-card--specific{overflow:visible}.wc-products-list-card--specific:after{content:none}.wc-products-list-card--specific .wc-products-list-card__item{position:relative;border:none}.wc-products-list-card--specific .wc-products-list-card__item img{margin:0;outline:4px solid #00a0d2;outline-offset:-4px}.wc-products-list-card--specific .wc-products-list-card__item button{position:absolute;top:0;left:0;background:#00a0d2;padding:0;margin:0;border:none;margin-right:auto;line-height:10px;cursor:pointer}.wc-products-list-card--specific .wc-products-list-card__item .dashicon{color:#fff}.wc-products-list-card--specific .wc-products-list-card__input-wrapper{margin:0}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-1 .wc-products-list-card__item{width:100%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-2 .wc-products-list-card__item{width:50%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-3 .wc-products-list-card__item{width:33.3333333333%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-4 .wc-products-list-card__item{width:25%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-5 .wc-products-list-card__item{width:20%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-6 .wc-products-list-card__item{width:16.6666666667%}.wc-products-list-card--specific .wc-products-list-card__results{max-height:none;overflow:visible}.wc-products-list-card--specific .wc-products-list-card__results h3{margin:0 0 1em;font-size:1em}.wc-products-list-card--specific .wc-products-list-card__results ul{display:-webkit-box;display:flex;flex-wrap:wrap;margin:0 -.5em -1em}.wc-products-list-card--specific .wc-products-list-card__results ul li{border:none;padding:0 .5em;margin:0 0 1em}.wc-products-list-card--specific .wc-products-list-card__results .wc-products-list-card__content{position:relative;display:block;padding:0;background:0 0;border:none}.wc-products-list-card__search-wrapper{position:relative;margin:0 0 1.5em}.wc-products-list-card__search-results{width:100%;list-style:none;background:#fff;margin:-1px 0 0;border:1px solid #e6eaee;box-shadow:0 1px 3px #e6eaee}.wc-products-list-card__search-results>div{max-height:175px;overflow-y:auto}.wc-products-list-card__search-results .wc-products-list-card__content{position:relative;border-width:1px 0 0;border-style:solid;border-color:#e6eaee;-webkit-transition:opacity .7s;transition:opacity .7s;cursor:pointer;color:#00a0d2}.wc-products-list-card__search-results .wc-products-list-card__content--added{background-color:#f7fcff}.wc-products-list-card__search-results .wc-products-list-card__content:hover{background-color:#f7fcff}.wc-products-list-card__search-results .wc-products-list-card__content--transition-exit-active{opacity:0}.wc-products-list-card__search-results .wc-products-list-card__content:first-child{border-top-width:0}.wc-products-list-card__search-results .wc-products-list-card__content img{-o-object-fit:cover;object-fit:cover;-o-object-position:center;object-position:center;width:2.5em;height:2.5em;margin:0 0 0 1em}.wc-products-list-card__search-results .wc-products-list-card__content .dashicon{color:#0073aa;margin-right:auto}.wc-products-list-card__search-wrapper--with-results+.wc-products-list-card__results-wrapper .wc-products-list-card__item img{outline:0}.wc-products-list-card__search-wrapper--with-results+.wc-products-list-card__results-wrapper .wc-products-list-card__item button{display:none}.wc-products-list-card__search-no-results{display:block;margin:1em 0 0}.wc-products-list-card__search-no-selected{display:block;margin:-.75em 0 0}.wc-products-list-card__results-wrapper{position:relative;overflow:hidden}@media only screen and (min-width:700px){.wc-products-settings-heading{display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between}.wc-products-list-card{max-width:480px}}.edit-post-sidebar .wc-products-scope-descriptions{margin-bottom:1.5em;position:relative;padding-right:46px;padding-top:1em;padding-bottom:1.5em;border-bottom:1px solid #e6eaee;display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between}.edit-post-sidebar h3{font-weight:500;margin-bottom:5px;color:#555d66}.edit-post-sidebar .scope-description{font-size:12px}.edit-post-sidebar .wc-products-scope-description--edit-quicklink{margin-right:1em;min-width:24px}.edit-post-sidebar .wc-products-scope-description--edit-quicklink a{cursor:pointer}
assets/css/gutenberg-products-block.css DELETED
@@ -1 +0,0 @@
1
- .wc-products-block-preview{overflow:hidden}.wc-products-block-preview .product-preview{float:left;text-align:center;margin-right:3.8%}.wc-products-block-preview.cols-1 .product-preview{float:none;margin-right:0}.wc-products-block-preview.cols-2 .product-preview{width:48%}.wc-products-block-preview.cols-2 .product-preview:nth-of-type(2n){margin-right:0}.wc-products-block-preview.cols-2 .product-preview:nth-of-type(2n+1){clear:both}.wc-products-block-preview.cols-3 .product-preview{width:30.75%}.wc-products-block-preview.cols-3 .product-preview:nth-of-type(3n){margin-right:0}.wc-products-block-preview.cols-3 .product-preview:nth-of-type(3n+1){clear:both}.wc-products-block-preview.cols-4 .product-preview{width:22.05%}.wc-products-block-preview.cols-4 .product-preview:nth-of-type(4n){margin-right:0}.wc-products-block-preview.cols-4 .product-preview:nth-of-type(4n+1){clear:both}.wc-products-block-preview.cols-5 .product-preview{width:16.9%}.wc-products-block-preview.cols-5 .product-preview:nth-of-type(5n){margin-right:0}.wc-products-block-preview.cols-5 .product-preview:nth-of-type(5n+1){clear:both}.wc-products-block-preview.cols-5 .product-preview .product-add-to-cart{font-size:.75em}.wc-products-block-preview.cols-6 .product-preview{width:13.5%}.wc-products-block-preview.cols-6 .product-preview:nth-of-type(6n){margin-right:0}.wc-products-block-preview.cols-6 .product-preview:nth-of-type(6n+1){clear:both}.wc-products-block-preview.cols-6 .product-preview .product-add-to-cart{font-size:.75em}.wc-products-block-preview .product-add-to-cart{display:inline-block;background:#ababab;border-radius:1.5em;color:#fff;cursor:pointer;padding:.75em 1.25em;line-height:1.2em;margin-top:.5em;margin-bottom:1em}.wc-products-settings{background-color:#f8f9f9;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;position:relative}.wc-products-settings__title{margin:0;padding:1.5rem 0;text-align:center;border-bottom:1px solid #e6eaee}.wc-products-settings__title .dashicon{vertical-align:top;margin-right:.25em}.wc-products-settings__footer{margin:2em 0 0;padding:1.5em 0;border-top:1px solid #e6eaee;text-align:center}.wc-products-settings__footer .button{padding-left:1.5em;padding-right:1.5em}.wc-products-display-options--popover+.wc-products-settings__footer,.wc-products-settings-heading+.wc-products-settings__footer{margin-top:-2em;border-top:none}p.wc-products-block-description{margin:2em 0 1.5em 0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:1em;text-align:center}.wc-products-display-options{margin:0 0 2.5em}.wc-products-display-options__option{display:-webkit-box;display:flex;-webkit-box-align:start;align-items:flex-start;flex-wrap:wrap;margin:0 auto;padding:1.25em 1.5em;max-width:80%;background:#fff;border-width:1px 1px 0;border-style:solid;border-color:#e6eaee;cursor:pointer}.wc-products-display-options__option:last-of-type{border-bottom-width:1px}.wc-products-display-options__option--attribute,.wc-products-display-options__option--best_selling,.wc-products-display-options__option--featured,.wc-products-display-options__option--on_sale,.wc-products-display-options__option--top_rated{display:none;background-color:#fdfdfd;padding-top:1em;padding-bottom:1em}.wc-products-display-options__option--attribute .wc-products-display-options__option-title,.wc-products-display-options__option--best_selling .wc-products-display-options__option-title,.wc-products-display-options__option--featured .wc-products-display-options__option-title,.wc-products-display-options__option--on_sale .wc-products-display-options__option-title,.wc-products-display-options__option--top_rated .wc-products-display-options__option-title{font-size:1.15em}.wc-products-display-options__option--attribute .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--best_selling .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--featured .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--on_sale .wc-products-display-options__icon .dashicon,.wc-products-display-options__option--top_rated .wc-products-display-options__icon .dashicon{height:20px;width:20px}.wc-products-display-options__option--current{cursor:default}.wc-products-display-options__option--current .wc-products-display-options__option-title{color:#86909b}.wc-products-display-options__option-content{width:85%;align-self:center}.wc-products-display-options__option-title{display:block;font-size:1.25em}p.wc-products-display-options__option-description{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:1em;color:#86909b}.wc-products-display-options__icon{align-self:center;margin-left:auto;color:#b9bcc2}.wc-products-display-options__icon .dashicon{height:25px;width:25px}.wc-products-display-options--popover{position:absolute;right:-2em;max-width:60%;margin:0;z-index:999;box-shadow:0 2px 10px 0 rgba(0,0,0,.1);margin-top:-2.15em}.wc-products-display-options--popover .wc-products-display-options__option{margin:0;max-width:none}.wc-products-display-options--popover__arrow{width:2em;height:1.25em;position:absolute;top:-1.15em;right:30%;overflow:hidden}.wc-products-display-options--popover__arrow:after{content:'';position:absolute;width:1.25em;height:1.25em;background:#fff;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);top:.625em;left:.3125em;box-shadow:0 2px 10px 0 rgba(0,0,0,.1);border:1px solid #e6eaee}.wc-products-display-options--extended .wc-products-display-options__option--attribute,.wc-products-display-options--extended .wc-products-display-options__option--best_selling,.wc-products-display-options--extended .wc-products-display-options__option--featured,.wc-products-display-options--extended .wc-products-display-options__option--on_sale,.wc-products-display-options--extended .wc-products-display-options__option--top_rated{display:-webkit-box;display:flex}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--category{border-bottom-width:1px}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--filter{margin-top:.5em}.wc-products-display-options--extended:not(.wc-products-display-options--popover) .wc-products-display-options__option--attribute{margin-bottom:.5em;border-bottom-width:1px}.wc-products-settings-heading{margin:0 0 2em 0;padding:1em 2em;text-align:center;border-bottom:1px solid #e6eaee}.wc-products-list-card{position:relative;margin-left:auto;margin-right:auto;padding:0 1em;overflow:hidden;box-sizing:border-box}.wc-products-list-card .wc-products-list-card__input-wrapper{position:relative;background:#fff;margin:0 0 1em}.wc-products-list-card .wc-products-list-card__input-wrapper .dashicon{position:absolute;top:calc(1em - 1px);left:1em;z-index:1}.wc-products-list-card input[type=search]{position:relative;width:100%;margin:0;padding:1em 1.25em 1em 3em;border-radius:0;background:0 0;border-color:#e6eaee;box-shadow:none;z-index:2}.wc-products-list-card .wc-products-list-card__results{max-height:200px;overflow-y:auto;overflow-x:hidden;box-sizing:border-box}.wc-products-list-card .wc-products-list-card__results ul{list-style:none}.wc-products-list-card .wc-products-list-card__results ul li{margin:0;border:1px solid #e6eaee;border-bottom-width:0}.wc-products-list-card .wc-products-list-card__results ul li:last-child{border-bottom-width:1px}.wc-products-list-card .wc-products-list-card__content{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;padding:.75em 1.25em;background:#fff;border-bottom:1px solid #e6eaee}.wc-products-list-card:after{content:'';position:absolute;left:0;bottom:0;width:100%;height:1.5em;background:-webkit-linear-gradient(rgba(255,255,255,.1) 0,#f8f9f9 100%);background:linear-gradient(rgba(255,255,255,.1) 0,#f8f9f9 100%)}.wc-products-list-card--taxonomy .wc-products-list-card__taxonomy-count{text-align:center;width:30px;font-size:.8em;border:1px solid #e9e9e9;border-radius:1em;color:#aaa}.wc-products-list-card--taxonomy input[type=checkbox]{position:relative;margin-top:0;margin-right:.75em;border-radius:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results{padding-bottom:1.3em}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li{margin-top:-1px}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li:first-child{margin-top:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li:last-child{border-bottom-width:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul{display:none;padding:1em 1.25em 1em 3.25em;background:#fdfdfd;border-bottom:1px solid #e6eaee}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul li{margin-bottom:.25em;border:none}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li ul li:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open{margin:.5em 0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open:first-child{margin-top:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul{display:block}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul li label{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row}.wc-products-list-card--taxonomy-category .wc-products-list-card__results ul li.wc-products-list-card__accordion-open ul li .wc-products-list-card__taxonomy-count{margin-left:auto}.wc-products-list-card--taxonomy-category .wc-products-list-card__accordion-button{cursor:pointer;color:#666;margin:0 1em 0 auto;padding:0 0 0 .75em;border:none;border-radius:0;background:0 0;outline:0;text-decoration:none}.wc-products-list-card--taxonomy-category .wc-products-list-card__accordion-button .dashicon{align-self:center;display:-webkit-box;display:flex}.wc-products-list-card--taxonomy-category input[type=checkbox]:indeterminate:before{position:absolute;top:0;bottom:0;right:0;left:0;content:'';margin:42% 20%;width:60%;background:#0073aa}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results{padding-bottom:1.3em}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul{padding:1em 1.25em 1em 3.25em;background:#fdfdfd;border-bottom:1px solid #e6eaee}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul li{margin-bottom:.25em;border:none}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__results ul li .wc-products-list-card__content{padding:0;background:0 0;border:none}.wc-products-list-card--taxonomy-atributes__atribute{margin:-1px 0 0;border-width:1px 1px 0;border-style:solid;border-color:#e6eaee}.wc-products-list-card--taxonomy-atributes__atribute:first-child{margin-top:0}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open{margin-top:.5em;margin-bottom:.5em}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open:first-child{margin-top:0}.wc-products-list-card--taxonomy-atributes__atribute.wc-products-list-card__accordion-open:last-child{margin-bottom:0}.wc-products-list-card--taxonomy-atributes .wc-products-list-card__taxonomy-count{margin-left:auto}.wc-products-list-card--taxonomy-atributes input[type=radio]{position:relative;margin-top:0;margin-right:.75em;border-radius:100%}.wc-products-list-card--specific{overflow:visible}.wc-products-list-card--specific:after{content:none}.wc-products-list-card--specific .wc-products-list-card__item{position:relative;border:none}.wc-products-list-card--specific .wc-products-list-card__item img{margin:0;outline:4px solid #00a0d2;outline-offset:-4px}.wc-products-list-card--specific .wc-products-list-card__item button{position:absolute;top:0;right:0;background:#00a0d2;padding:0;margin:0;border:none;margin-left:auto;line-height:10px;cursor:pointer}.wc-products-list-card--specific .wc-products-list-card__item .dashicon{color:#fff}.wc-products-list-card--specific .wc-products-list-card__input-wrapper{margin:0}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-1 .wc-products-list-card__item{width:100%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-2 .wc-products-list-card__item{width:50%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-3 .wc-products-list-card__item{width:33.3333333333%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-4 .wc-products-list-card__item{width:25%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-5 .wc-products-list-card__item{width:20%}.wc-products-list-card--specific .wc-products-list-card__results-wrapper--cols-6 .wc-products-list-card__item{width:16.6666666667%}.wc-products-list-card--specific .wc-products-list-card__results{max-height:none;overflow:visible}.wc-products-list-card--specific .wc-products-list-card__results h3{margin:0 0 1em;font-size:1em}.wc-products-list-card--specific .wc-products-list-card__results ul{display:-webkit-box;display:flex;flex-wrap:wrap;margin:0 -.5em -1em}.wc-products-list-card--specific .wc-products-list-card__results ul li{border:none;padding:0 .5em;margin:0 0 1em}.wc-products-list-card--specific .wc-products-list-card__results .wc-products-list-card__content{position:relative;display:block;padding:0;background:0 0;border:none}.wc-products-list-card__search-wrapper{position:relative;margin:0 0 1.5em}.wc-products-list-card__search-results{width:100%;list-style:none;background:#fff;margin:-1px 0 0;border:1px solid #e6eaee;box-shadow:0 1px 3px #e6eaee}.wc-products-list-card__search-results>div{max-height:175px;overflow-y:auto}.wc-products-list-card__search-results .wc-products-list-card__content{position:relative;border-width:1px 0 0;border-style:solid;border-color:#e6eaee;-webkit-transition:opacity .7s;transition:opacity .7s;cursor:pointer;color:#00a0d2}.wc-products-list-card__search-results .wc-products-list-card__content--added{background-color:#f7fcff}.wc-products-list-card__search-results .wc-products-list-card__content:hover{background-color:#f7fcff}.wc-products-list-card__search-results .wc-products-list-card__content--transition-exit-active{opacity:0}.wc-products-list-card__search-results .wc-products-list-card__content:first-child{border-top-width:0}.wc-products-list-card__search-results .wc-products-list-card__content img{-o-object-fit:cover;object-fit:cover;-o-object-position:center;object-position:center;width:2.5em;height:2.5em;margin:0 1em 0 0}.wc-products-list-card__search-results .wc-products-list-card__content .dashicon{color:#0073aa;margin-left:auto}.wc-products-list-card__search-wrapper--with-results+.wc-products-list-card__results-wrapper .wc-products-list-card__item img{outline:0}.wc-products-list-card__search-wrapper--with-results+.wc-products-list-card__results-wrapper .wc-products-list-card__item button{display:none}.wc-products-list-card__search-no-results{display:block;margin:1em 0 0}.wc-products-list-card__search-no-selected{display:block;margin:-.75em 0 0}.wc-products-list-card__results-wrapper{position:relative;overflow:hidden}@media only screen and (min-width:700px){.wc-products-settings-heading{display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between}.wc-products-list-card{max-width:480px}}.edit-post-sidebar .wc-products-scope-descriptions{margin-bottom:1.5em;position:relative;padding-left:46px;padding-top:1em;padding-bottom:1.5em;border-bottom:1px solid #e6eaee;display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between}.edit-post-sidebar h3{font-weight:500;margin-bottom:5px;color:#555d66}.edit-post-sidebar .scope-description{font-size:12px}.edit-post-sidebar .wc-products-scope-description--edit-quicklink{margin-left:1em;min-width:24px}.edit-post-sidebar .wc-products-scope-description--edit-quicklink a{cursor:pointer}
assets/css/product-category-block.scss ADDED
@@ -0,0 +1,69 @@
1
+ // Import the woocommerce components stylesheet
2
+ // @todo Move this to a separate file so we can build a cacheable single stylesheet for all blocks.
3
+ @import "../../node_modules/@woocommerce/components/build-style/style.css";
4
+
5
+ // Hack to hide preview overflow.
6
+ .editor-block-preview__content {
7
+ overflow: hidden;
8
+ }
9
+
10
+ .wc-block-products-category {
11
+ overflow: hidden;
12
+
13
+ &.components-placeholder {
14
+ padding: 2em 1em;
15
+ }
16
+
17
+ .editor-block-preview & {
18
+ min-width: 5em;
19
+
20
+ .wc-product-preview__title,
21
+ .wc-product-preview__price,
22
+ .wc-product-preview__add-to-cart {
23
+ font-size: 0.6em;
24
+ }
25
+
26
+ &.cols-2 {
27
+ min-width: 2 * 5em;
28
+ }
29
+ &.cols-3 {
30
+ min-width: 3 * 5em;
31
+ }
32
+ &.cols-4 {
33
+ min-width: 4 * 5em;
34
+ }
35
+ &.cols-5 {
36
+ min-width: 5 * 5em;
37
+ }
38
+ &.cols-6 {
39
+ min-width: 6 * 5em;
40
+ }
41
+
42
+ &.is-loading,
43
+ &.is-not-found {
44
+ min-width: auto;
45
+ }
46
+ }
47
+ }
48
+
49
+ .wc-block-products-category__selection {
50
+ width: 100%;
51
+ }
52
+
53
+ .components-panel {
54
+ .woocommerce-search-list {
55
+ padding: 0;
56
+ }
57
+ .woocommerce-search-list__selected {
58
+ margin: 0 0 $gap;
59
+ padding: 0;
60
+ border-top: none;
61
+ // 54px is the height of 1 row of tags in the sidebar.
62
+ min-height: 54px;
63
+ }
64
+ .woocommerce-search-list__search {
65
+ margin: 0 0 $gap;
66
+ padding: 0;
67
+ border-top: none;
68
+ }
69
+ }
assets/css/{gutenberg-products-block.scss → products-block.scss} RENAMED
@@ -31,7 +31,7 @@ $color__alt-background: #fdfdfd;
31
margin-right: 0;
32
}
33
34
- &:nth-of-type(2n+1) {
35
clear: both;
36
}
37
}
@@ -43,7 +43,7 @@ $color__alt-background: #fdfdfd;
43
margin-right: 0;
44
}
45
46
- &:nth-of-type(3n+1) {
47
clear: both;
48
}
49
}
@@ -55,7 +55,7 @@ $color__alt-background: #fdfdfd;
55
margin-right: 0;
56
}
57
58
- &:nth-of-type(4n+1) {
59
clear: both;
60
}
61
}
@@ -67,12 +67,12 @@ $color__alt-background: #fdfdfd;
67
margin-right: 0;
68
}
69
70
- &:nth-of-type(5n+1) {
71
clear: both;
72
}
73
74
.product-add-to-cart {
75
- font-size: .75em;
76
}
77
}
78
@@ -83,12 +83,12 @@ $color__alt-background: #fdfdfd;
83
margin-right: 0;
84
}
85
86
- &:nth-of-type(6n+1) {
87
clear: both;
88
}
89
90
.product-add-to-cart {
91
- font-size: .75em;
92
}
93
}
94
@@ -96,13 +96,39 @@ $color__alt-background: #fdfdfd;
96
display: inline-block;
97
background: #ababab;
98
border-radius: 1.5em;
99
- color: #ffffff;
100
cursor: pointer;
101
- padding: .75em 1.25em;
102
line-height: 1.2em;
103
- margin-top: .5em;
104
margin-bottom: 1em;
105
}
106
}
107
108
/**
@@ -125,7 +151,7 @@ $color__alt-background: #fdfdfd;
125
126
.dashicon {
127
vertical-align: top;
128
- margin-right: .25em;
129
}
130
}
131
@@ -142,10 +168,8 @@ $color__alt-background: #fdfdfd;
142
}
143
}
144
145
- .wc-products-settings-heading +
146
- .wc-products-settings__footer,
147
- .wc-products-display-options--popover +
148
- .wc-products-settings__footer {
149
margin-top: -2em;
150
border-top: none;
151
}
@@ -260,22 +284,22 @@ p.wc-products-display-options__option-description {
260
}
261
262
.wc-products-display-options--popover__arrow {
263
- width: 2em;
264
- height: 1.25em;
265
position: absolute;
266
top: -1.15em;
267
right: 30%;
268
overflow: hidden;
269
270
- &:after {
271
- content: '';
272
position: absolute;
273
width: 1.25em;
274
height: 1.25em;
275
background: #fff;
276
transform: rotate(45deg);
277
- top: .625em;
278
- left: .3125em;
279
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
280
border: 1px solid $color__border;
281
}
@@ -300,11 +324,11 @@ p.wc-products-display-options__option-description {
300
}
301
302
&--filter {
303
- margin-top: .5em;
304
}
305
306
&--attribute {
307
- margin-bottom: .5em;
308
border-bottom-width: 1px;
309
}
310
}
@@ -336,12 +360,12 @@ p.wc-products-display-options__option-description {
336
337
.wc-products-list-card__input-wrapper {
338
position: relative;
339
- background: #ffffff;
340
margin: 0 0 1em;
341
342
.dashicon {
343
position: absolute;
344
- top: calc( 1em - 1px );
345
left: 1em;
346
z-index: 1;
347
}
@@ -384,19 +408,19 @@ p.wc-products-display-options__option-description {
384
display: flex;
385
align-items: center;
386
flex-direction: row;
387
- padding: .75em 1.25em;
388
background: #fff;
389
border-bottom: 1px solid $color__border;
390
}
391
392
- &:after {
393
- content: '';
394
position: absolute;
395
left: 0;
396
bottom: 0;
397
width: 100%;
398
height: 1.5em;
399
- background: linear-gradient( rgba( 255, 255, 255, .1 ) 0, #f8f9f9 100% );
400
}
401
}
402
@@ -408,7 +432,7 @@ p.wc-products-display-options__option-description {
408
.wc-products-list-card__taxonomy-count {
409
text-align: center;
410
width: 30px;
411
- font-size: .8em;
412
border: 1px solid #e9e9e9;
413
border-radius: 1em;
414
color: #aaa;
@@ -417,7 +441,7 @@ p.wc-products-display-options__option-description {
417
input[type="checkbox"] {
418
position: relative;
419
margin-top: 0;
420
- margin-right: .75em;
421
border-radius: 0;
422
}
423
}
@@ -449,7 +473,7 @@ p.wc-products-display-options__option-description {
449
border-bottom: 1px solid $color__border;
450
451
li {
452
- margin-bottom: .25em;
453
border: none;
454
455
&:last-child {
@@ -459,7 +483,7 @@ p.wc-products-display-options__option-description {
459
}
460
461
&.wc-products-list-card__accordion-open {
462
- margin: .5em 0;
463
464
&:first-child {
465
margin-top: 0;
@@ -493,7 +517,7 @@ p.wc-products-display-options__option-description {
493
cursor: pointer;
494
color: #666;
495
margin: 0 1em 0 auto;
496
- padding: 0 0 0 .75em;
497
border: none;
498
border-radius: 0;
499
background: none;
@@ -508,13 +532,13 @@ p.wc-products-display-options__option-description {
508
509
input[type="checkbox"] {
510
&:indeterminate {
511
- &:before {
512
position: absolute;
513
top: 0;
514
bottom: 0;
515
right: 0;
516
left: 0;
517
- content: '';
518
margin: 42% 20%;
519
width: 60%;
520
background: $color__link;
@@ -537,7 +561,7 @@ p.wc-products-display-options__option-description {
537
border-bottom: 1px solid $color__border;
538
539
li {
540
- margin-bottom: .25em;
541
border: none;
542
543
.wc-products-list-card__content {
@@ -560,8 +584,8 @@ p.wc-products-display-options__option-description {
560
}
561
562
&.wc-products-list-card__accordion-open {
563
- margin-top: .5em;
564
- margin-bottom: .5em;
565
566
&:first-child {
567
margin-top: 0;
@@ -580,7 +604,7 @@ p.wc-products-display-options__option-description {
580
input[type="radio"] {
581
position: relative;
582
margin-top: 0;
583
- margin-right: .75em;
584
border-radius: 100%;
585
}
586
}
@@ -592,7 +616,7 @@ p.wc-products-display-options__option-description {
592
.wc-products-list-card--specific {
593
overflow: visible;
594
595
- &:after {
596
content: none;
597
}
598
@@ -620,7 +644,7 @@ p.wc-products-display-options__option-description {
620
}
621
622
.dashicon {
623
- color: #ffffff;
624
}
625
}
626
@@ -630,7 +654,7 @@ p.wc-products-display-options__option-description {
630
631
.wc-products-list-card__results-wrapper {
632
@for $i from 1 through 6 {
633
- $width: percentage( 1 / $i );
634
635
&--cols-#{$i} {
636
.wc-products-list-card__item {
@@ -652,11 +676,11 @@ p.wc-products-display-options__option-description {
652
ul {
653
display: flex;
654
flex-wrap: wrap;
655
- margin: 0 -.5em -1em;
656
657
li {
658
border: none;
659
- padding: 0 .5em;
660
margin: 0 0 1em;
661
}
662
}
@@ -679,7 +703,7 @@ p.wc-products-display-options__option-description {
679
.wc-products-list-card__search-results {
680
width: 100%;
681
list-style: none;
682
- background: #ffffff;
683
margin: -1px 0 0;
684
border: 1px solid $color__border;
685
box-shadow: 0 1px 3px $color__border;
@@ -694,16 +718,16 @@ p.wc-products-display-options__option-description {
694
border-width: 1px 0 0;
695
border-style: solid;
696
border-color: $color__border;
697
- transition: opacity .7s;
698
cursor: pointer;
699
color: $color__link--hover;
700
701
&--added {
702
- background-color: lighten( $color__link, 65% );
703
}
704
705
&:hover {
706
- background-color: lighten( $color__link, 65% );
707
}
708
709
&--transition-exit-active {
@@ -729,8 +753,7 @@ p.wc-products-display-options__option-description {
729
}
730
}
731
732
- .wc-products-list-card__search-wrapper--with-results +
733
- .wc-products-list-card__results-wrapper {
734
.wc-products-list-card__item {
735
img {
736
outline: none;
@@ -749,7 +772,7 @@ p.wc-products-display-options__option-description {
749
750
.wc-products-list-card__search-no-selected {
751
display: block;
752
- margin: -.75em 0 0;
753
}
754
755
.wc-products-list-card__results-wrapper {
31
margin-right: 0;
32
}
33
34
+ &:nth-of-type(2n + 1) {
35
clear: both;
36
}
37
}
43
margin-right: 0;
44
}
45
46
+ &:nth-of-type(3n + 1) {
47
clear: both;
48
}
49
}
55
margin-right: 0;
56
}
57
58
+ &:nth-of-type(4n + 1) {
59
clear: both;
60
}
61
}
67
margin-right: 0;
68
}
69
70
+ &:nth-of-type(5n + 1) {
71
clear: both;
72
}
73
74
.product-add-to-cart {
75
+ font-size: 0.75em;
76
}
77
}
78
83
margin-right: 0;
84
}
85
86
+ &:nth-of-type(6n + 1) {
87
clear: both;
88
}
89
90
.product-add-to-cart {
91
+ font-size: 0.75em;
92
}
93
}
94
96
display: inline-block;
97
background: #ababab;
98
border-radius: 1.5em;
99
+ color: #fff;
100
cursor: pointer;
101
+ padding: 0.75em 1.25em;
102
line-height: 1.2em;
103
+ margin-top: 0.5em;
104
margin-bottom: 1em;
105
}
106
+
107
+ .editor-block-preview & {
108
+ min-width: 5em;
109
+
110
+ .product-title,
111
+ .product-price,
112
+ .product-add-to-cart {
113
+ font-size: 0.6em !important;
114
+ }
115
+
116
+ &.cols-2 {
117
+ min-width: 2 * 5em;
118
+ }
119
+ &.cols-3 {
120
+ min-width: 3 * 5em;
121
+ }
122
+ &.cols-4 {
123
+ min-width: 4 * 5em;
124
+ }
125
+ &.cols-5 {
126
+ min-width: 5 * 5em;
127
+ }
128
+ &.cols-6 {
129
+ min-width: 6 * 5em;
130
+ }
131
+ }
132
}
133
134
/**
151
152
.dashicon {
153
vertical-align: top;
154
+ margin-right: 0.25em;
155
}
156
}
157
168
}
169
}
170
171
+ .wc-products-settings-heading + .wc-products-settings__footer,
172
+ .wc-products-display-options--popover + .wc-products-settings__footer {
173
margin-top: -2em;
174
border-top: none;
175
}
284
}
285
286
.wc-products-display-options--popover__arrow {
287
+ width: 2em;
288
+ height: 1.25em;
289
position: absolute;
290
top: -1.15em;
291
right: 30%;
292
overflow: hidden;
293
294
+ &::after {
295
+ content: "";
296
position: absolute;
297
width: 1.25em;
298
height: 1.25em;
299
background: #fff;
300
transform: rotate(45deg);
301
+ top: 0.625em;
302
+ left: 0.3125em;
303
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
304
border: 1px solid $color__border;
305
}
324
}
325
326
&--filter {
327
+ margin-top: 0.5em;
328
}
329
330
&--attribute {
331
+ margin-bottom: 0.5em;
332
border-bottom-width: 1px;
333
}
334
}
360
361
.wc-products-list-card__input-wrapper {
362
position: relative;
363
+ background: #fff;
364
margin: 0 0 1em;
365
366
.dashicon {
367
position: absolute;
368
+ top: calc(1em - 1px);
369
left: 1em;
370
z-index: 1;
371
}
408
display: flex;
409
align-items: center;
410
flex-direction: row;
411
+ padding: 0.75em 1.25em;
412
background: #fff;
413
border-bottom: 1px solid $color__border;
414
}
415
416
+ &::after {
417
+ content: "";
418
position: absolute;
419
left: 0;
420
bottom: 0;
421
width: 100%;
422
height: 1.5em;
423
+ background: linear-gradient(rgba(255, 255, 255, 0.1) 0, #f8f9f9 100%);
424
}
425
}
426
432
.wc-products-list-card__taxonomy-count {
433
text-align: center;
434
width: 30px;
435
+ font-size: 0.8em;
436
border: 1px solid #e9e9e9;
437
border-radius: 1em;
438
color: #aaa;
441
input[type="checkbox"] {
442
position: relative;
443
margin-top: 0;
444
+ margin-right: 0.75em;
445
border-radius: 0;
446
}
447
}
473
border-bottom: 1px solid $color__border;
474
475
li {
476
+ margin-bottom: 0.25em;
477
border: none;
478
479
&:last-child {
483
}
484
485
&.wc-products-list-card__accordion-open {
486
+ margin: 0.5em 0;
487
488
&:first-child {
489
margin-top: 0;
517
cursor: pointer;
518
color: #666;
519
margin: 0 1em 0 auto;
520
+ padding: 0 0 0 0.75em;
521
border: none;
522
border-radius: 0;
523
background: none;
532
533
input[type="checkbox"] {
534
&:indeterminate {
535
+ &::before {
536
position: absolute;
537
top: 0;
538
bottom: 0;
539
right: 0;
540
left: 0;
541
+ content: "";
542
margin: 42% 20%;
543
width: 60%;
544
background: $color__link;
561
border-bottom: 1px solid $color__border;
562
563
li {
564
+ margin-bottom: 0.25em;
565
border: none;
566
567
.wc-products-list-card__content {
584
}
585
586
&.wc-products-list-card__accordion-open {
587
+ margin-top: 0.5em;
588
+ margin-bottom: 0.5em;
589
590
&:first-child {
591
margin-top: 0;
604
input[type="radio"] {
605
position: relative;
606
margin-top: 0;
607
+ margin-right: 0.75em;
608
border-radius: 100%;
609
}
610
}
616
.wc-products-list-card--specific {
617
overflow: visible;
618
619
+ &::after {
620
content: none;
621
}
622
644
}
645
646
.dashicon {
647
+ color: #fff;
648
}
649
}
650
654
655
.wc-products-list-card__results-wrapper {
656
@for $i from 1 through 6 {
657
+ $width: percentage(1 / $i);
658
659
&--cols-#{$i} {
660
.wc-products-list-card__item {
676
ul {
677
display: flex;
678
flex-wrap: wrap;
679
+ margin: 0 -0.5em -1em;
680
681
li {
682
border: none;
683
+ padding: 0 0.5em;
684
margin: 0 0 1em;
685
}
686
}
703
.wc-products-list-card__search-results {
704
width: 100%;
705
list-style: none;
706
+ background: #fff;
707
margin: -1px 0 0;
708
border: 1px solid $color__border;
709
box-shadow: 0 1px 3px $color__border;
718
border-width: 1px 0 0;
719
border-style: solid;
720
border-color: $color__border;
721
+ transition: opacity 0.7s;
722
cursor: pointer;
723
color: $color__link--hover;
724
725
&--added {
726
+ background-color: lighten($color__link, 65%);
727
}
728
729
&:hover {
730
+ background-color: lighten($color__link, 65%);
731
}
732
733
&--transition-exit-active {
753
}
754
}
755
756
+ .wc-products-list-card__search-wrapper--with-results + .wc-products-list-card__results-wrapper {
757
.wc-products-list-card__item {
758
img {
759
outline: none;
772
773
.wc-products-list-card__search-no-selected {
774
display: block;
775
+ margin: -0.75em 0 0;
776
}
777
778
.wc-products-list-card__results-wrapper {
assets/js/components/product-category-control/index.js ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { __, _n, sprintf } from '@wordpress/i18n';
5
+ import { addQueryArgs } from '@wordpress/url';
6
+ import apiFetch from '@wordpress/api-fetch';
7
+ import { Component } from '@wordpress/element';
8
+ import { find, first, last } from 'lodash';
9
+ import { MenuItem } from '@wordpress/components';
10
+ import PropTypes from 'prop-types';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import './style.scss';
16
+ import { CheckedIcon, UncheckedIcon } from '../search-list-control/icons';
17
+ import SearchListControl from '../search-list-control';
18
+
19
+ class ProductCategoryControl extends Component {
20
+ constructor() {
21
+ super( ...arguments );
22
+ this.state = {
23
+ list: [],
24
+ loading: true,
25
+ };
26
+ this.renderItem = this.renderItem.bind( this );
27
+ }
28
+
29
+ componentDidMount() {
30
+ apiFetch( {
31
+ path: addQueryArgs( '/wc-pb/v3/products/categories', { per_page: -1 } ),
32
+ } )
33
+ .then( ( list ) => {
34
+ this.setState( { list, loading: false } );
35
+ } )
36
+ .catch( () => {
37
+ this.setState( { list: [], loading: false } );
38
+ } );
39
+ }
40
+
41
+ getBreadcrumbsForDisplay( breadcrumbs ) {
42
+ if ( breadcrumbs.length === 1 ) {
43
+ return first( breadcrumbs );
44
+ }
45
+ if ( breadcrumbs.length === 2 ) {
46
+ return first( breadcrumbs ) + ' › ' + last( breadcrumbs );
47
+ }
48
+
49
+ return first( breadcrumbs ) + ' … ' + last( breadcrumbs );
50
+ }
51
+
52
+ renderItem( { getHighlightedName, isSelected, item, onSelect, search, depth = 0 } ) {
53
+ const classes = [
54
+ 'woocommerce-search-list__item',
55
+ 'woocommerce-product-categories__item',
56
+ `depth-${ depth }`,
57
+ ];
58
+ if ( search.length ) {
59
+ classes.push( 'is-searching' );
60
+ }
61
+ if ( depth === 0 && item.parent !== 0 ) {
62
+ classes.push( 'is-skip-level' );
63
+ }
64
+
65
+ const accessibleName = ! item.breadcrumbs.length ?
66
+ item.name :
67
+ `${ item.breadcrumbs.join( ', ' ) }, ${ item.name }`;
68
+
69
+ return (
70
+ <MenuItem
71
+ key={ item.id }
72
+ role="menuitemcheckbox"
73
+ className={ classes.join( ' ' ) }
74
+ onClick={ onSelect( item ) }
75
+ isSelected={ isSelected }
76
+ aria-label={ sprintf(
77
+ _n(
78
+ '%s, has %d product',
79
+ '%s, has %d products',
80
+ item.count,
81
+ 'woo-gutenberg-products-block'
82
+ ),
83
+ accessibleName,
84
+ item.count
85
+ ) }
86
+ >
87
+ <span className="woocommerce-search-list__item-state">
88
+ { isSelected ? <CheckedIcon /> : <UncheckedIcon /> }
89
+ </span>
90
+ <span className="woocommerce-product-categories__item-label">
91
+ { !! item.breadcrumbs.length && (
92
+ <span className="woocommerce-product-categories__item-prefix">
93
+ { this.getBreadcrumbsForDisplay( item.breadcrumbs ) }
94
+ </span>
95
+ ) }
96
+ <span
97
+ className="woocommerce-product-categories__item-name"
98
+ dangerouslySetInnerHTML={ {
99
+ __html: getHighlightedName( item.name, search ),
100
+ } }
101
+ />
102
+ </span>
103
+ <span className="woocommerce-product-categories__item-count">
104
+ { item.count }
105
+ </span>
106
+ </MenuItem>
107
+ );
108
+ }
109
+
110
+ render() {
111
+ const { list, loading } = this.state;
112
+ const { selected, onChange } = this.props;
113
+
114
+ const messages = {
115
+ clear: __( 'Clear all product categories', 'woo-gutenberg-products-block' ),
116
+ list: __( 'Product Categories', 'woo-gutenberg-products-block' ),
117
+ noItems: __( 'Your store doesn\'t have any product categories.', 'woo-gutenberg-products-block' ),
118
+ search: __( 'Search for product categories', 'woo-gutenberg-products-block' ),
119
+ selected: ( n ) =>
120
+ sprintf(
121
+ _n(
122
+ '%d category selected',
123
+ '%d categories selected',
124
+ n,
125
+ 'woo-gutenberg-products-block'
126
+ ),
127
+ n
128
+ ),
129
+ updated: __( 'Category search results updated.', 'woo-gutenberg-products-block' ),
130
+ };
131
+
132
+ return (
133
+ <SearchListControl
134
+ className="woocommerce-product-categories"
135
+ list={ list }
136
+ isLoading={ loading }
137
+ selected={ selected.map( ( id ) => find( list, { id } ) ).filter( Boolean ) }
138
+ onChange={ onChange }
139
+ renderItem={ this.renderItem }
140
+ messages={ messages }
141
+ isHierarchical
142
+ />
143
+ );
144
+ }
145
+ }
146
+
147
+ ProductCategoryControl.propTypes = {
148
+ /**
149
+ * Callback to update the selected product categories.
150
+ */
151
+ onChange: PropTypes.func.isRequired,
152
+ /**
153
+ * The list of currently selected category IDs.
154
+ */
155
+ selected: PropTypes.array.isRequired,
156
+ };
157
+
158
+ export default ProductCategoryControl;
assets/js/components/product-category-control/style.scss ADDED
@@ -0,0 +1,75 @@
1
+ .woocommerce-product-categories {
2
+ .woocommerce-product-categories__item {
3
+ display: flex;
4
+ align-items: center;
5
+ }
6
+ }
7
+
8
+ .woocommerce-product-categories__item-label {
9
+ display: flex;
10
+ flex: 1;
11
+
12
+ // Anything deeper than 5 levels will use this fallback depth
13
+ [class*="depth-"] & {
14
+ padding-left: $gap-small * 5;
15
+ }
16
+
17
+ .depth-0 & {
18
+ padding-left: 0;
19
+ }
20
+
21
+ .depth-1 & {
22
+ padding-left: $gap-small;
23
+ }
24
+
25
+ .depth-2 & {
26
+ padding-left: $gap-small * 2;
27
+ }
28
+
29
+ .depth-3 & {
30
+ padding-left: $gap-small * 3;
31
+ }
32
+
33
+ .depth-4 & {
34
+ padding-left: $gap-small * 4;
35
+ }
36
+ }
37
+
38
+ .woocommerce-product-categories__item {
39
+ .woocommerce-product-categories__item-name {
40
+ display: inline-block;
41
+ }
42
+
43
+ .woocommerce-product-categories__item-prefix {
44
+ display: none;
45
+ color: $core-grey-dark-300;
46
+ }
47
+
48
+ &.is-searching, &.is-skip-level {
49
+ .woocommerce-product-categories__item-prefix {
50
+ display: inline-block;
51
+ margin-right: $gap-smallest;
52
+
53
+ &:after {
54
+ content: ' ›';
55
+ }
56
+ }
57
+ }
58
+
59
+ &.is-searching {
60
+ .woocommerce-product-categories__item-name {
61
+ color: $core-grey-dark-900;
62
+ }
63
+ }
64
+
65
+ .woocommerce-product-categories__item-count {
66
+ flex: 0;
67
+ padding: $gap-smallest/2 $gap-smaller;
68
+ border: 1px solid $core-grey-light-500;
69
+ border-radius: 12px;
70
+ font-size: 0.8em;
71
+ line-height: 1.4;
72
+ color: $core-grey-dark-300;
73
+ background: $white;
74
+ }
75
+ }
assets/js/components/product-preview/index.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import PropTypes from 'prop-types';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import './style.scss';
11
+
12
+ /**
13
+ * Display a preview for a given product.
14
+ */
15
+ const ProductPreview = ( { product } ) => {
16
+ let image = null;
17
+ if ( product.images.length ) {
18
+ image = <img src={ product.images[ 0 ].src } alt="" />;
19
+ }
20
+
21
+ return (
22
+ <div className="wc-product-preview">
23
+ { image }
24
+ <div className="wc-product-preview__title">{ product.name }</div>
25
+ <div
26
+ className="wc-product-preview__price"
27
+ dangerouslySetInnerHTML={ { __html: product.price_html } }
28
+ />
29
+ <span className="wc-product-preview__add-to-cart">
30
+ { __( 'Add to cart', 'woo-gutenberg-products-block' ) }
31
+ </span>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ ProductPreview.propTypes = {
37
+ /**
38
+ * The product object as returned from the API.
39
+ */
40
+ product: PropTypes.shape( {
41
+ id: PropTypes.number,
42
+ images: PropTypes.array,
43
+ name: PropTypes.string,
44
+ price_html: PropTypes.string,
45
+ } ).isRequired,
46
+ };
47
+
48
+ export default ProductPreview;
assets/js/components/product-preview/style.scss ADDED
@@ -0,0 +1,90 @@
1
+ .wc-product-preview {
2
+ float: left;
3
+ text-align: center;
4
+ margin-right: 3.8%;
5
+
6
+ .cols-1 & {
7
+ float: none;
8
+ margin-right: 0;
9
+ }
10
+
11
+ .cols-2 & {
12
+ width: 48%;
13
+
14
+ &:nth-of-type(2n) {
15
+ margin-right: 0;
16
+ }
17
+
18
+ &:nth-of-type(2n+1) {
19
+ clear: both;
20
+ }
21
+ }
22
+
23
+ .cols-3 & {
24
+ width: 30.75%;
25
+
26
+ &:nth-of-type(3n) {
27
+ margin-right: 0;
28
+ }
29
+
30
+ &:nth-of-type(3n+1) {
31
+ clear: both;
32
+ }
33
+ }
34
+
35
+ .cols-4 & {
36
+ width: 22.05%;
37
+
38
+ &:nth-of-type(4n) {
39
+ margin-right: 0;
40
+ }
41
+
42
+ &:nth-of-type(4n+1) {
43
+ clear: both;
44
+ }
45
+ }
46
+
47
+ .cols-5 & {
48
+ width: 16.9%;
49
+
50
+ &:nth-of-type(5n) {
51
+ margin-right: 0;
52
+ }
53
+
54
+ &:nth-of-type(5n+1) {
55
+ clear: both;
56
+ }
57
+
58
+ .wc-product-preview__add-to-cart {
59
+ font-size: 0.75em;
60
+ }
61
+ }
62
+
63
+ .cols-6 & {
64
+ width: 13.5%;
65
+
66
+ &:nth-of-type(6n) {
67
+ margin-right: 0;
68
+ }
69
+
70
+ &:nth-of-type(6n+1) {
71
+ clear: both;
72
+ }
73
+
74
+ .wc-product-preview__add-to-cart {
75
+ font-size: 0.75em;
76
+ }
77
+ }
78
+ }
79
+
80
+ .wc-product-preview__add-to-cart {
81
+ display: inline-block;
82
+ background: #ababab;
83
+ border-radius: 1.5em;
84
+ color: #fff;
85
+ cursor: pointer;
86
+ padding: 0.75em 1.25em;
87
+ line-height: 1.2em;
88
+ margin-top: 0.5em;
89
+ margin-bottom: 1em;
90
+ }
assets/js/components/search-list-control/hierarchy.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { forEach, groupBy, keyBy } from 'lodash';
5
+
6
+ /**
7
+ * Returns terms in a tree form.
8
+ *
9
+ * @param {Array} filteredList Array of terms, possibly a subset of all terms, in flat format.
10
+ * @param {Array} list Array of the full list of terms, defaults to the filteredList.
11
+ *
12
+ * @return {Array} Array of terms in tree format.
13
+ */
14
+ export function buildTermsTree( filteredList, list = filteredList ) {
15
+ const termsByParent = groupBy( filteredList, 'parent' );
16
+ const listById = keyBy( list, 'id' );
17
+
18
+ const getParentsName = ( term = {} ) => {
19
+ if ( ! term.parent ) {
20
+ return term.name ? [ term.name ] : [];
21
+ }
22
+
23
+ const parentName = getParentsName( listById[ term.parent ] );
24
+ return [ ...parentName, term.name ];
25
+ };
26
+
27
+ const fillWithChildren = ( terms ) => {
28
+ return terms.map( ( term ) => {
29
+ const children = termsByParent[ term.id ];
30
+ delete termsByParent[ term.id ];
31
+ return {
32
+ ...term,
33
+ breadcrumbs: getParentsName( listById[ term.parent ] ),
34
+ children: children && children.length ? fillWithChildren( children ) : [],
35
+ };
36
+ } );
37
+ };
38
+
39
+ const tree = fillWithChildren( termsByParent[ '0' ] || [] );
40
+ delete termsByParent[ '0' ];
41
+
42
+ // anything left in termsByParent has no visible parent
43
+ forEach( termsByParent, ( terms ) => {
44
+ tree.push( ...fillWithChildren( terms || [] ) );
45
+ } );
46
+
47
+ return tree;
48
+ }
assets/js/components/search-list-control/icons.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { SVG } from '@wordpress/components';
5
+
6
+ export const CheckedIcon = () => (
7
+ <SVG
8
+ viewBox="0 0 16 16"
9
+ width="16"
10
+ height="16"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <defs>
14
+ <path
15
+ id="checked"
16
+ d="M15.2222 1H2.7778C1.791 1 1 1.8 1 2.7778v12.4444C1 16.2 1.7911 17 2.7778 17h12.4444C16.209 17 17 16.2 17 15.2222V2.7778C17 1.8 16.2089 1 15.2222 1zm-8 12.4444L2.7778 9 4.031 7.7467l3.1911 3.1822 6.7467-6.7467 1.2533 1.2622-8 8z"
17
+ />
18
+ </defs>
19
+ <g fill="none" fillRule="evenodd" transform="translate(-1 -1)">
20
+ <mask id="checked-mask" fill="#fff">
21
+ <use xlinkHref="#checked" />
22
+ </mask>
23
+ <path fill="#1E8CBE" d="M0 0h18v18H0z" mask="url(#checked-mask)" />
24
+ </g>
25
+ </SVG>
26
+ );
27
+
28
+ export const UncheckedIcon = () => (
29
+ <SVG
30
+ viewBox="0 0 16 16"
31
+ width="16"
32
+ height="16"
33
+ xmlns="http://www.w3.org/2000/svg"
34
+ >
35
+ <defs>
36
+ <path
37
+ id="unchecked"
38
+ d="M15.2222 2.7778v12.4444H2.7778V2.7778h12.4444zm0-1.7778H2.7778C1.8 1 1 1.8 1 2.7778v12.4444C1 16.2 1.8 17 2.7778 17h12.4444C16.2 17 17 16.2 17 15.2222V2.7778C17 1.8 16.2 1 15.2222 1z"
39
+ />
40
+ </defs>
41
+ <g fill="none" fillRule="evenodd" transform="translate(-1 -1)">
42
+ <mask id="unchecked-mask" fill="#fff">
43
+ <use xlinkHref="#unchecked" />
44
+ </mask>
45
+ <path fill="#6C7781" d="M0 0h18v18H0z" mask="url(#unchecked-mask)" />
46
+ </g>
47
+ </SVG>
48
+ );
assets/js/components/search-list-control/index.js ADDED
@@ -0,0 +1,332 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { __, _n, sprintf } from '@wordpress/i18n';
5
+ import {
6
+ Button,
7
+ MenuItem,
8
+ MenuGroup,
9
+ Spinner,
10
+ TextControl,
11
+ withSpokenMessages,
12
+ } from '@wordpress/components';
13
+ import { Component, Fragment } from '@wordpress/element';
14
+ import { compose, withInstanceId, withState } from '@wordpress/compose';
15
+ import { escapeRegExp, findIndex } from 'lodash';
16
+ import Gridicon from 'gridicons';
17
+ import PropTypes from 'prop-types';
18
+ import { Tag } from '@woocommerce/components';
19
+
20
+ /**
21
+ * Internal dependencies
22
+ */
23
+ import './style.scss';
24
+ import { buildTermsTree } from './hierarchy';
25
+ import { CheckedIcon, UncheckedIcon } from './icons';
26
+
27
+ const defaultMessages = {
28
+ clear: __( 'Clear all selected items', 'woo-gutenberg-products-block' ),
29
+ list: __( 'Results', 'woo-gutenberg-products-block' ),
30
+ noItems: __( 'No items found.', 'woo-gutenberg-products-block' ),
31
+ noResults: __( 'No results for %s', 'woo-gutenberg-products-block' ),
32
+ search: __( 'Search for items', 'woo-gutenberg-products-block' ),
33
+ selected: ( n ) =>
34
+ sprintf( _n( '%d item selected', '%d items selected', n, 'woo-gutenberg-products-block' ), n ),
35
+ updated: __( 'Search results updated.', 'woo-gutenberg-products-block' ),
36
+ };
37
+
38
+ /**
39
+ * Component to display a searchable, selectable list of items.
40
+ */
41
+ export class SearchListControl extends Component {
42
+ constructor() {
43
+ super( ...arguments );
44
+
45
+ this.onSelect = this.onSelect.bind( this );
46
+ this.onRemove = this.onRemove.bind( this );
47
+ this.onClear = this.onClear.bind( this );
48
+ this.isSelected = this.isSelected.bind( this );
49
+ this.defaultRenderItem = this.defaultRenderItem.bind( this );
50
+ this.renderList = this.renderList.bind( this );
51
+ }
52
+
53
+ onRemove( id ) {
54
+ const { selected, onChange } = this.props;
55
+ return () => {
56
+ const i = findIndex( selected, { id } );
57
+ onChange( [ ...selected.slice( 0, i ), ...selected.slice( i + 1 ) ] );
58
+ };
59
+ }
60
+
61
+ onSelect( item ) {
62
+ const { selected, onChange } = this.props;
63
+ return () => {
64
+ if ( this.isSelected( item ) ) {
65
+ this.onRemove( item.id )();
66
+ return;
67
+ }
68
+ onChange( [ ...selected, item ] );
69
+ };
70
+ }
71
+
72
+ onClear() {
73
+ this.props.onChange( [] );
74
+ }
75
+
76
+ isSelected( item ) {
77
+ return -1 !== findIndex( this.props.selected, { id: item.id } );
78
+ }
79
+
80
+ getFilteredList( list, search ) {
81
+ const { isHierarchical } = this.props;
82
+ if ( ! search ) {
83
+ return isHierarchical ? buildTermsTree( list ) : list;
84
+ }
85
+ const messages = { ...defaultMessages, ...this.props.messages };
86
+ const re = new RegExp( escapeRegExp( search ), 'i' );
87
+ this.props.debouncedSpeak( messages.updated );
88
+ const filteredList = list
89
+ .map( ( item ) => ( re.test( item.name ) ? item : false ) )
90
+ .filter( Boolean );
91
+ return isHierarchical ? buildTermsTree( filteredList, list ) : filteredList;
92
+ }
93
+
94
+ getHighlightedName( name, search ) {
95
+ if ( ! search ) {
96
+ return name;
97
+ }
98
+ const re = new RegExp( escapeRegExp( search ), 'ig' );
99
+ return name.replace( re, '<strong>amp;</strong>' );
100
+ }
101
+
102
+ defaultRenderItem( {
103
+ depth = 0,
104
+ getHighlightedName,
105
+ item,
106
+ isSelected,
107
+ onSelect,
108
+ search = '',
109
+ } ) {
110
+ const classes = [ 'woocommerce-search-list__item' ];
111
+ if ( this.props.isHierarchical ) {
112
+ classes.push( `depth-${ depth }` );
113
+ }
114
+
115
+ return (
116
+ <MenuItem
117
+ key={ item.id }
118
+ role="menuitemcheckbox"
119
+ className={ classes.join( ' ' ) }
120
+ onClick={ onSelect( item ) }
121
+ isSelected={ isSelected }
122
+ >
123
+ <span className="woocommerce-search-list__item-state">
124
+ { isSelected ? <CheckedIcon /> : <UncheckedIcon /> }
125
+ </span>
126
+ <span
127
+ className="woocommerce-search-list__item-name"
128
+ dangerouslySetInnerHTML={ {
129
+ __html: getHighlightedName( item.name, search ),
130
+ } }
131
+ />
132
+ </MenuItem>
133
+ );
134
+ }
135
+
136
+ renderList( list, depth = 0 ) {
137
+ const { search } = this.props;
138
+ const renderItem = this.props.renderItem || this.defaultRenderItem;
139
+ if ( ! list ) {
140
+ return null;
141
+ }
142
+ return list.map( ( item ) => (
143
+ <Fragment key={ item.id }>
144
+ { renderItem( {
145
+ getHighlightedName: this.getHighlightedName,
146
+ item,
147
+ isSelected: this.isSelected( item ),
148
+ onSelect: this.onSelect,
149
+ search,
150
+ depth,
151
+ } ) }
152
+ { this.renderList( item.children, depth + 1 ) }
153
+ </Fragment>
154
+ ) );
155
+ }
156
+
157
+ renderListSection() {
158
+ const { isLoading, search } = this.props;
159
+ const list = this.getFilteredList( this.props.list, search );
160
+ const messages = { ...defaultMessages, ...this.props.messages };
161
+
162
+ if ( isLoading ) {
163
+ return (
164
+ <div className="woocommerce-search-list__list is-loading">
165
+ <Spinner />
166
+ </div>
167
+ );
168
+ }
169
+
170
+ if ( ! list.length ) {
171
+ return (
172
+ <div className="woocommerce-search-list__list is-not-found">
173
+ <span className="woocommerce-search-list__not-found-icon">
174
+ <Gridicon
175
+ icon="notice-outline"
176
+ role="img"
177
+ aria-hidden="true"
178
+ focusable="false"
179
+ />
180
+ </span>
181
+ <span className="woocommerce-search-list__not-found-text">
182
+ { search ? sprintf( messages.noResults, search ) : messages.noItems }
183
+ </span>
184
+ </div>
185
+ );
186
+ }
187
+
188
+ return (
189
+ <MenuGroup
190
+ label={ messages.list }
191
+ className="woocommerce-search-list__list"
192
+ >
193
+ { this.renderList( list ) }
194
+ </MenuGroup>
195
+ );
196
+ }
197
+
198
+ render() {
199
+ const { className = '', search, selected, setState } = this.props;
200
+ const messages = { ...defaultMessages, ...this.props.messages };
201
+ const selectedCount = selected.length;
202
+
203
+ return (
204
+ <div className={ `woocommerce-search-list ${ className }` }>
205
+ <div className="woocommerce-search-list__selected">
206
+ <div className="woocommerce-search-list__selected-header">
207
+ <strong>{ messages.selected( selectedCount ) }</strong>
208
+ { selectedCount > 0 ? (
209
+ <Button
210
+ isLink
211
+ isDestructive
212
+ onClick={ this.onClear }
213
+ aria-label={ messages.clear }
214
+ >
215
+ { __( 'Clear all', 'woo-gutenberg-products-block' ) }
216
+ </Button>
217
+ ) : null }
218
+ </div>
219
+ { selected.map( ( item, i ) => (
220
+ <Tag
221
+ key={ i }
222
+ label={ item.name }
223
+ id={ item.id }
224
+ remove={ this.onRemove }
225
+ />
226
+ ) ) }
227
+ </div>
228
+
229
+ <div className="woocommerce-search-list__search">
230
+ <TextControl
231
+ label={ messages.search }
232
+ type="search"
233
+ value={ search }
234
+ onChange={ ( value ) => setState( { search: value } ) }
235
+ />
236
+ </div>
237
+
238
+ { this.renderListSection() }
239
+ </div>
240
+ );
241
+ }
242
+ }
243
+
244
+ SearchListControl.propTypes = {
245
+ /**
246
+ * Additional CSS classes.
247
+ */
248
+ className: PropTypes.string,
249
+ /**
250
+ * Whether the list of items is hierarchical or not. If true, each list item is expected to
251
+ * have a parent property.
252
+ */
253
+ isHierarchical: PropTypes.bool,
254
+ /**
255
+ * Whether the list of items is still loading.
256
+ */
257
+ isLoading: PropTypes.bool,
258
+ /**
259
+ * A complete list of item objects, each with id, name properties. This is displayed as a
260
+ * clickable/keyboard-able list, and possibly filtered by the search term (searches name).
261
+ */
262
+ list: PropTypes.arrayOf(
263
+ PropTypes.shape( {
264
+ id: PropTypes.number,
265
+ name: PropTypes.string,
266
+ } )
267
+ ),
268
+ /**
269
+ * Messages displayed or read to the user. Configure these to reflect your object type.
270
+ * See `defaultMessages` above for examples.
271
+ */
272
+ messages: PropTypes.shape( {
273
+ /**
274
+ * A more detailed label for the "Clear all" button, read to screen reader users.
275
+ */
276
+ clear: PropTypes.string,
277
+ /**
278
+ * Label for the list of selectable items, only read to screen reader users.
279
+ */
280
+ list: PropTypes.string,
281
+ /**
282
+ * Message to display when the list is empty (implies nothing loaded from the server
283
+ * or parent component).
284
+ */
285
+ noItems: PropTypes.string,
286
+ /**
287
+ * Message to display when no matching results are found. %s is the search term.
288
+ */
289
+ noResults: PropTypes.string,
290
+ /**
291
+ * Label for the search input
292
+ */
293
+ search: PropTypes.string,
294
+ /**
295
+ * Label for the selected items. This is actually a function, so that we can pass
296
+ * through the count of currently selected items.
297
+ */
298
+ selected: PropTypes.func,
299
+ /**
300
+ * Label indicating that search results have changed, read to screen reader users.
301
+ */
302
+ updated: PropTypes.string,
303
+ } ),
304
+ /**
305
+ * Callback fired when selected items change, whether added, cleared, or removed.
306
+ * Passed an array of item objects (as passed in via props.list).
307
+ */
308
+ onChange: PropTypes.func.isRequired,
309
+ /**
310
+ * Callback to render each item in the selection list, allows any custom object-type rendering.
311
+ */
312
+ renderItem: PropTypes.func,
313
+ /**
314
+ * The list of currently selected items.
315
+ */
316
+ selected: PropTypes.array.isRequired,
317
+ // from withState
318
+ search: PropTypes.string,
319
+ setState: PropTypes.func,
320
+ // from withSpokenMessages
321
+ debouncedSpeak: PropTypes.func,
322
+ // from withInstanceId
323
+ instanceId: PropTypes.number,
324
+ };
325
+
326
+ export default compose( [
327
+ withState( {
328
+ search: '',
329
+ } ),
330
+ withSpokenMessages,
331
+ withInstanceId,
332
+ ] )( SearchListControl );
assets/js/components/search-list-control/style.scss ADDED
@@ -0,0 +1,111 @@
1
+ .woocommerce-search-list {
2
+ width: 100%;
3
+ padding: 0 0 $gap;
4
+ text-align: left;
5
+ }
6
+
7
+ .woocommerce-search-list__selected {
8
+ margin: $gap 0;
9
+ padding: $gap 0 0;
10
+ // 76px is the height of 1 row of tags.
11
+ min-height: 76px;
12
+ border-top: 1px solid $core-grey-light-500;
13
+
14
+ .woocommerce-search-list__selected-header {
15
+ margin-bottom: $gap-smaller;
16
+
17
+ button {
18
+ margin-left: $gap-small;
19
+ }
20
+ }
21
+
22
+ .woocommerce-tag__text {
23
+ max-width: 13em;
24
+ }
25
+ }
26
+
27
+ .woocommerce-search-list__search {
28
+ margin: $gap 0;
29
+ padding: $gap 0 0;
30
+ border-top: 1px solid $core-grey-light-500;
31
+
32
+ .components-base-control__field {
33
+ margin-bottom: $gap;
34
+ }
35
+ }
36
+
37
+ .woocommerce-search-list__list {
38
+ padding: 0;
39
+ max-height: 18.5em;
40
+ overflow-x: hidden;
41
+ overflow-y: auto;
42
+ border-top: 1px solid $core-grey-light-500;
43
+ border-bottom: 1px solid $core-grey-light-500;
44
+
45
+ &.is-loading {
46
+ padding: $gap-small 0;
47
+ text-align: center;
48
+ border: none;
49
+ }
50
+
51
+ &.is-not-found {
52
+ padding: $gap-small 0;
53
+ text-align: center;
54
+ border: none;
55
+
56
+ .woocommerce-search-list__not-found-icon,
57
+ .woocommerce-search-list__not-found-text {
58
+ display: inline-block;
59
+ }
60
+
61
+ .woocommerce-search-list__not-found-icon {
62
+ margin-right: $gap;
63
+
64
+ .gridicon {
65
+ vertical-align: top;
66
+ margin-top: -1px;
67
+ }
68
+ }
69
+ }
70
+
71
+ .components-spinner {
72
+ float: none;
73
+ }
74
+
75
+ .components-menu-group__label {
76
+ @include visually-hidden;
77
+ }
78
+
79
+ & > [role="menu"] {
80
+ border: 1px solid $core-grey-light-500;
81
+ border-bottom: none;
82
+ }
83
+
84
+ .woocommerce-search-list__item {
85
+ display: flex;
86
+ align-items: center;
87
+ margin-bottom: 0;
88
+ padding: $gap;
89
+ background: $white;
90
+ // !important to keep the border around on hover
91
+ border-bottom: 1px solid $core-grey-light-500 !important;
92
+ color: $core-grey-dark-500;
93
+
94
+ .woocommerce-search-list__item-state {
95
+ flex: 0 0 16px;
96
+ margin-right: $gap-smaller;
97
+ }
98
+
99
+ .woocommerce-search-list__item-name {
100
+ flex: 1;
101
+ }
102
+
103
+ @include hover-state {
104
+ background: $core-grey-light-100;
105
+ }
106
+
107
+ &:last-of-type {
108
+ border-bottom: none !important;
109
+ }
110
+ }
111
+ }
assets/js/{products-block.jsx → legacy/products-block.jsx} RENAMED
@@ -1,10 +1,12 @@
1
const { __ } = wp.i18n;
2
- const { RawHTML } = wp.element;
3
const { registerBlockType } = wp.blocks;
4
const { InspectorControls, BlockControls } = wp.editor;
5
- const { Toolbar, Dropdown, Dashicon, RangeControl, Tooltip, SelectControl } = wp.components;
6
const { apiFetch } = wp;
7
8
import { ProductsSpecificSelect } from './views/specific-select.jsx';
9
import { ProductsCategorySelect } from './views/category-select.jsx';
10
import { ProductsAttributeSelect, getAttributeSlug, getAttributeID } from './views/attribute-select.jsx';
@@ -18,54 +20,54 @@ import { ProductsAttributeSelect, getAttributeSlug, getAttributeID } from './vie
18
* no_orderby - (optional) If set the setting does not allow orderby settings.
19
*/
20
const PRODUCTS_BLOCK_DISPLAY_SETTINGS = {
21
- 'specific' : {
22
title: __( 'Individual products' ),
23
description: __( 'Hand-pick which products to display' ),
24
value: 'specific',
25
},
26
- 'category' : {
27
title: __( 'Product category' ),
28
description: __( 'Display products from a specific category or multiple categories' ),
29
value: 'category',
30
},
31
- 'filter' : {
32
title: __( 'Filter products' ),
33
description: __( 'E.g. featured products, or products with a specific attribute like size or color' ),
34
value: 'filter',
35
- group_container: 'filter'
36
},
37
- 'featured' : {
38
title: __( 'Featured products' ),
39
description: '',
40
value: 'featured',
41
},
42
- 'on_sale' : {
43
title: __( 'On sale' ),
44
description: '',
45
value: 'on_sale',
46
},
47
- 'best_selling' : {
48
title: __( 'Best sellers' ),
49
description: '',
50
value: 'best_selling',
51
no_orderby: true,
52
},
53
- 'top_rated' : {
54
title: __( 'Top rated' ),
55
description: '',
56
value: 'top_rated',
57
no_orderby: true,
58
},
59
- 'attribute' : {
60
title: __( 'Attribute' ),
61
description: '',
62
value: 'attribute',
63
},
64
- 'all' : {
65
title: __( 'All products' ),
66
description: __( 'Display all products ordered chronologically, alphabetically, by price, by rating or by sales' ),
67
value: 'all',
68
- }
69
};
70
71
/**
@@ -75,15 +77,15 @@ const PRODUCTS_BLOCK_DISPLAY_SETTINGS = {
75
* @return bool
76
*/
77
function supportsOrderby( display ) {
78
- return ! ( PRODUCTS_BLOCK_DISPLAY_SETTINGS.hasOwnProperty( display )
79
- && PRODUCTS_BLOCK_DISPLAY_SETTINGS[ display ].hasOwnProperty( 'no_orderby' )
80
- && PRODUCTS_BLOCK_DISPLAY_SETTINGS[ display ].no_orderby );
81
}
82
83
/**
84
* One option from the list of all available ways to display products.
85
*/
86
- class ProductsBlockSettingsEditorDisplayOption extends React.Component {
87
render() {
88
let icon = 'arrow-right-alt2';
89
@@ -94,12 +96,18 @@ class ProductsBlockSettingsEditorDisplayOption extends React.Component {
94
let classes = 'wc-products-display-options__option wc-products-display-options__option--' + this.props.value;
95
96
if ( this.props.current === this.props.value ) {
97
- icon = 'yes';
98
classes += ' wc-products-display-options__option--current';
99
}
100
101
return (
102
- <div className={ classes } onClick={ () => { this.props.current !== this.props.value && this.props.update_display_callback( this.props.value ) } } >
103
<div className="wc-products-display-options__option-content">
104
<span className="wc-products-display-options__option-title">{ this.props.title }</span>
105
<p className="wc-products-display-options__option-description">{ this.props.description }</p>
@@ -109,14 +117,14 @@ class ProductsBlockSettingsEditorDisplayOption extends React.Component {
109
</div>
110
</div>
111
);
112
}
113
}
114
115
/**
116
* A list of all available ways to display products.
117
*/
118
- class ProductsBlockSettingsEditorDisplayOptions extends React.Component {
119
-
120
/**
121
* Constructor.
122
*/
@@ -157,10 +165,10 @@ class ProductsBlockSettingsEditorDisplayOptions extends React.Component {
157
/**
158
* Close the menu when user clicks outside the search area.
159
*/
160
- handleClickOutside( evt ) {
161
- if ( this.wrapperRef && ! this.wrapperRef.contains( event.target ) && 'wc-products-settings-heading__change-button button-link' !== event.target.getAttribute( 'class' ) ) {
162
- this.props.closeMenu();
163
- }
164
}
165
166
/**
@@ -177,13 +185,13 @@ class ProductsBlockSettingsEditorDisplayOptions extends React.Component {
177
classes += ' wc-products-display-options--popover';
178
}
179
180
- let display_settings = [];
181
- for ( var setting_key in PRODUCTS_BLOCK_DISPLAY_SETTINGS ) {
182
- display_settings.push( <ProductsBlockSettingsEditorDisplayOption { ...PRODUCTS_BLOCK_DISPLAY_SETTINGS[ setting_key ] } update_display_callback={ this.props.update_display_callback } extended={ this.props.extended } current={ this.props.current } /> );
183
}
184
185
- let arrow = <span className="wc-products-display-options--popover__arrow"></span>;
186
- let description = <p className="wc-products-block-description">{ __( 'Choose which products you\'d like to display:' ) }</p>;
187
188
return (
189
<div className={ classes } ref={ this.setWrapperRef }>
@@ -198,8 +206,7 @@ class ProductsBlockSettingsEditorDisplayOptions extends React.Component {
198
/**
199
* The products block when in Edit mode.
200
*/
201
- class ProductsBlockSettingsEditor extends React.Component {
202
-
203
/**
204
* Constructor.
205
*/
@@ -209,7 +216,7 @@ class ProductsBlockSettingsEditor extends React.Component {
209
display: props.selected_display,
210
menu_visible: props.selected_display ? false : true,
211
expanded_group: '',
212
- }
213
214
this.updateDisplay = this.updateDisplay.bind( this );
215
this.closeMenu = this.closeMenu.bind( this );
@@ -221,7 +228,6 @@ class ProductsBlockSettingsEditor extends React.Component {
221
* @param value String
222
*/
223
updateDisplay( value ) {
224
-
225
// If not a group update display.
226
let new_state = {
227
display: value,
@@ -236,7 +242,7 @@ class ProductsBlockSettingsEditor extends React.Component {
236
new_state = {
237
menu_visible: true,
238
expanded_group: value,
239
- }
240
241
// If the group has already been expanded, collapse it.
242
if ( this.state.expanded_group === PRODUCTS_BLOCK_DISPLAY_SETTINGS[ value ].group_container ) {
@@ -268,16 +274,29 @@ class ProductsBlockSettingsEditor extends React.Component {
268
} else if ( 'category' === this.state.display ) {
269
extra_settings = <ProductsCategorySelect { ...this.props } />;
270
} else if ( 'attribute' === this.state.display ) {
271
- extra_settings = <ProductsAttributeSelect { ...this.props } />
272
}
273
274
const menu = this.state.menu_visible ? <ProductsBlockSettingsEditorDisplayOptions extended={ this.state.expanded_group ? true : false } existing={ this.state.display ? true : false } current={ this.state.display } closeMenu={ this.closeMenu } update_display_callback={ this.updateDisplay } /> : null;
275
276
let heading = null;
277
if ( this.state.display ) {
278
- const group_options = [ 'featured', 'on_sale', 'attribute', 'best_selling', 'top_rated' ];
279
- let should_group_expand = group_options.includes( this.state.display ) ? this.state.display : '';
280
- let menu_link = <button type="button" className="wc-products-settings-heading__change-button button-link" onClick={ () => { this.setState( { menu_visible: ! this.state.menu_visible, expanded_group: should_group_expand } ) } }>{ __( 'Display different products' ) }</button>;
281
282
heading = (
283
<div className="wc-products-settings-heading">
@@ -293,13 +312,13 @@ class ProductsBlockSettingsEditor extends React.Component {
293
}
294
295
let done_button = <button type="button" className="button wc-products-settings__footer-button" onClick={ this.props.done_callback }>{ __( 'Done' ) }</button>;
296
- if ( ['', 'specific', 'category', 'attribute'].includes( this.state.display ) && ! this.props.selected_display_setting.length ) {
297
const done_tooltips = {
298
'': __( 'Please select which products you\'d like to display' ),
299
specific: __( 'Please search for and select products to display' ),
300
category: __( 'Please select at least one category to display' ),
301
attribute: __( 'Please select an attribute' ),
302
- }
303
304
done_button = (
305
<Tooltip text={ done_tooltips[ this.state.display ] } >
@@ -308,7 +327,6 @@ class ProductsBlockSettingsEditor extends React.Component {
308
);
309
}
310
311
-
312
return (
313
<div className={ 'wc-products-settings ' + ( this.state.expanded_group ? 'expanded-group-' + this.state.expanded_group : '' ) }>
314
<h4 className="wc-products-settings__title"><Dashicon icon={ 'screenoptions' } /> { __( 'Products' ) }</h4>
@@ -330,14 +348,13 @@ class ProductsBlockSettingsEditor extends React.Component {
330
/**
331
* One product in the product block preview.
332
*/
333
- class ProductPreview extends React.Component {
334
-
335
render() {
336
- const { attributes, product } = this.props;
337
338
let image = null;
339
if ( product.images.length ) {
340
- image = <img src={ product.images[0].src } />
341
}
342
343
return (
@@ -354,8 +371,7 @@ class ProductPreview extends React.Component {
354
/**
355
* Renders a preview of what the block will look like with current settings.
356
*/
357
- class ProductsBlockPreview extends React.Component {
358
-
359
/**
360
* Constructor
361
*/
@@ -395,7 +411,8 @@ class ProductsBlockPreview extends React.Component {
395
getQuery() {
396
const { columns, rows, display, display_setting, orderby } = this.props.attributes;
397
398
- let query = {
399
per_page: rows * columns,
400
};
401
@@ -405,7 +422,7 @@ class ProductsBlockPreview extends React.Component {
405
} else if ( 'category' === display ) {
406
query.category = display_setting.join( ',' );
407
} else if ( 'attribute' === display && display_setting.length ) {
408
- query.attribute = getAttributeSlug( display_setting[0] );
409
410
if ( display_setting.length > 1 ) {
411
query.attribute_term = display_setting.slice( 1 ).join( ',' );
@@ -436,7 +453,7 @@ class ProductsBlockPreview extends React.Component {
436
query_string += key + '=' + query[ key ] + '&';
437
}
438
439
- const endpoint = '/wgbp/v3/products' + query_string;
440
return endpoint;
441
}
442
@@ -449,13 +466,13 @@ class ProductsBlockPreview extends React.Component {
449
450
self.setState( {
451
loaded: false,
452
- query: query
453
} );