diff --git a/.prettierrc.js b/.prettierrc.js
index 51b8aeb4..c7229cb5 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,3 +1,6 @@
// Import the default config file and expose it in the project root.
// Useful for editor integrations.
-module.exports = require( '@wordpress/prettier-config' );
+module.exports = {
+ ...require( '@wordpress/prettier-config' ),
+ printWidth: 120,
+};
diff --git a/blocks/layout-grid/editor.scss b/blocks/layout-grid/editor.scss
index f55c9b97..5e6df18c 100644
--- a/blocks/layout-grid/editor.scss
+++ b/blocks/layout-grid/editor.scss
@@ -228,13 +228,13 @@
// Note we add the alignment on the parent block as the editor nesting means it's not possible to affect the grid
@for $x from 1 through 4 {
- &.column#{ $x }-grid__valign-top [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__valign-top [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
align-self: flex-start;
}
- &.column#{ $x }-grid__valign-center [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__valign-center [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
align-self: center;
}
- &.column#{ $x }-grid__valign-bottom [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__valign-bottom [data-type="jetpack/layout-grid-column"].wp-block:nth-child(#{ $x }) {
align-self: flex-end;
}
}
diff --git a/blocks/layout-grid/front.css b/blocks/layout-grid/front.css
index afd6d1bc..59aed811 100644
--- a/blocks/layout-grid/front.css
+++ b/blocks/layout-grid/front.css
@@ -12,1034 +12,270 @@
grid-gap: 24px;
-ms-grid-columns: (1fr)[4];
grid-template-columns: repeat(4, 1fr); }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ @media (min-width: 600px) {
+ .wp-block-jetpack-layout-grid {
+ -ms-grid-columns: (1fr)[8];
+ grid-template-columns: repeat(8, 1fr); } }
+ @media (min-width: 1080px) {
+ .wp-block-jetpack-layout-grid {
+ -ms-grid-columns: (1fr)[12];
+ grid-template-columns: repeat(12, 1fr); } }
+ .wp-block-jetpack-layout-grid .wp-block-jetpack-layout-grid-column * {
+ word-break: break-word;
+ word-wrap: break-word; }
+ .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__none {
+ grid-gap: 0px; }
+ .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__small {
+ grid-gap: 8px; }
+ .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__medium {
+ grid-gap: 16px; }
+ .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__huge {
+ grid-gap: 48px; }
+
+@-moz-document url-prefix() {
+ .wp-block-jetpack-layout-grid .wp-block-cover {
+ max-height: 0; } }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-1 {
+ -ms-grid-column: 1;
+ grid-column-start: 1; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-1 {
+ -ms-grid-column-span: 1;
+ grid-column-end: span 1; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-2 {
+ -ms-grid-column: 2;
+ grid-column-start: 2; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-2 {
+ -ms-grid-column-span: 2;
+ grid-column-end: span 2; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-3 {
+ -ms-grid-column: 3;
+ grid-column-start: 3; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-3 {
+ -ms-grid-column-span: 3;
+ grid-column-end: span 3; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-4 {
+ -ms-grid-column: 4;
+ grid-column-start: 4; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-4 {
+ -ms-grid-column-span: 4;
+ grid-column-end: span 4; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-5 {
+ -ms-grid-column: 5;
+ grid-column-start: 5; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-5 {
+ -ms-grid-column-span: 5;
+ grid-column-end: span 5; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-6 {
+ -ms-grid-column: 6;
+ grid-column-start: 6; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-6 {
+ -ms-grid-column-span: 6;
+ grid-column-end: span 6; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-7 {
+ -ms-grid-column: 7;
+ grid-column-start: 7; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-7 {
+ -ms-grid-column-span: 7;
+ grid-column-end: span 7; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-8 {
+ -ms-grid-column: 8;
+ grid-column-start: 8; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-8 {
+ -ms-grid-column-span: 8;
+ grid-column-end: span 8; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-9 {
+ -ms-grid-column: 9;
+ grid-column-start: 9; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-9 {
+ -ms-grid-column-span: 9;
+ grid-column-end: span 9; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-10 {
+ -ms-grid-column: 10;
+ grid-column-start: 10; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-10 {
+ -ms-grid-column-span: 10;
+ grid-column-end: span 10; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-11 {
+ -ms-grid-column: 11;
+ grid-column-start: 11; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-11 {
+ -ms-grid-column-span: 11;
+ grid-column-end: span 11; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__start-12 {
+ -ms-grid-column: 12;
+ grid-column-start: 12; }
+
+.wp-block-jetpack-layout-grid-column.grid-column-mobile__span-12 {
+ -ms-grid-column-span: 12;
+ grid-column-end: span 12; }
+
+@media (min-width: 600px) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-1 {
-ms-grid-column: 1;
grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-1 {
+ -ms-grid-column-span: 1;
+ grid-column-end: span 1; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-2 {
-ms-grid-column: 2;
grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-2 {
+ -ms-grid-column-span: 2;
+ grid-column-end: span 2; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-3 {
-ms-grid-column: 3;
grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-3 {
+ -ms-grid-column-span: 3;
+ grid-column-end: span 3; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-4 {
-ms-grid-column: 4;
grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-4 {
+ -ms-grid-column-span: 4;
+ grid-column-end: span 4; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-5 {
-ms-grid-column: 5;
grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-5 {
+ -ms-grid-column-span: 5;
+ grid-column-end: span 5; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-6 {
-ms-grid-column: 6;
grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-6 {
+ -ms-grid-column-span: 6;
+ grid-column-end: span 6; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-7 {
-ms-grid-column: 7;
grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-7 {
+ -ms-grid-column-span: 7;
+ grid-column-end: span 7; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-8 {
-ms-grid-column: 8;
grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-8 {
+ -ms-grid-column-span: 8;
+ grid-column-end: span 8; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-9 {
-ms-grid-column: 9;
grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-9 {
+ -ms-grid-column-span: 9;
+ grid-column-end: span 9; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-10 {
-ms-grid-column: 10;
grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-10 {
+ -ms-grid-column-span: 10;
+ grid-column-end: span 10; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-11 {
-ms-grid-column: 11;
grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-11 {
+ -ms-grid-column-span: 11;
+ grid-column-end: span 11; }
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__start-12 {
-ms-grid-column: 12;
grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-tablet__span-12 {
+ -ms-grid-column-span: 12;
+ grid-column-end: span 12; } }
+
+@media (min-width: 1080px) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-1 {
+ -ms-grid-column: 1;
+ grid-column-start: 1; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-1 {
-ms-grid-column-span: 1;
grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-2 {
+ -ms-grid-column: 2;
+ grid-column-start: 2; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-2 {
-ms-grid-column-span: 2;
grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-3 {
+ -ms-grid-column: 3;
+ grid-column-start: 3; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-3 {
-ms-grid-column-span: 3;
grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-4 {
+ -ms-grid-column: 4;
+ grid-column-start: 4; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-4 {
-ms-grid-column-span: 4;
grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-5 {
+ -ms-grid-column: 5;
+ grid-column-start: 5; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-5 {
-ms-grid-column-span: 5;
grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-6 {
+ -ms-grid-column: 6;
+ grid-column-start: 6; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-6 {
-ms-grid-column-span: 6;
grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-7 {
+ -ms-grid-column: 7;
+ grid-column-start: 7; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-7 {
-ms-grid-column-span: 7;
grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-8 {
+ -ms-grid-column: 8;
+ grid-column-start: 8; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-8 {
-ms-grid-column-span: 8;
grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-9 {
+ -ms-grid-column: 9;
+ grid-column-start: 9; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-9 {
-ms-grid-column-span: 9;
grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-10 {
+ -ms-grid-column: 10;
+ grid-column-start: 10; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-10 {
-ms-grid-column-span: 10;
grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-11 {
+ -ms-grid-column: 11;
+ grid-column-start: 11; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-11 {
-ms-grid-column-span: 11;
grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__start-12 {
+ -ms-grid-column: 12;
+ grid-column-start: 12; }
+ .wp-block-jetpack-layout-grid-column.grid-column-desktop__span-12 {
-ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column1-mobile-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column2-mobile-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column3-mobile-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column4-mobile-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- @media (min-width: 600px) {
- .wp-block-jetpack-layout-grid {
- -ms-grid-columns: (1fr)[8];
- grid-template-columns: repeat(8, 1fr); }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column1-tablet-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column2-tablet-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column3-tablet-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column4-tablet-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 4;
- grid-row-start: 4; } }
- @media (min-width: 1080px) {
- .wp-block-jetpack-layout-grid {
- -ms-grid-columns: (1fr)[12];
- grid-template-columns: repeat(12, 1fr); }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 1;
- grid-column-start: 1; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 2;
- grid-column-start: 2; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 3;
- grid-column-start: 3; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 4;
- grid-column-start: 4; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 5;
- grid-column-start: 5; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 6;
- grid-column-start: 6; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 7;
- grid-column-start: 7; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 8;
- grid-column-start: 8; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 9;
- grid-column-start: 9; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 10;
- grid-column-start: 10; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 11;
- grid-column-start: 11; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__start-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column: 12;
- grid-column-start: 12; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 1;
- grid-column-end: span 1; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 2;
- grid-column-end: span 2; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 3;
- grid-column-end: span 3; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 4;
- grid-column-end: span 4; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-5 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 5;
- grid-column-end: span 5; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-6 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 6;
- grid-column-end: span 6; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-7 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 7;
- grid-column-end: span 7; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-8 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 8;
- grid-column-end: span 8; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-9 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 9;
- grid-column-end: span 9; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-10 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 10;
- grid-column-end: span 10; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-11 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 11;
- grid-column-end: span 11; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__span-12 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-column-span: 12;
- grid-column-end: span 12; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__row-1 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 1;
- grid-row-start: 1; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__row-2 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 2;
- grid-row-start: 2; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__row-3 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 3;
- grid-row-start: 3; }
- .wp-block-jetpack-layout-grid.column1-desktop-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(1) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column2-desktop-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(2) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column3-desktop-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(3) {
- -ms-grid-row: 4;
- grid-row-start: 4; }
- .wp-block-jetpack-layout-grid.column4-desktop-grid__row-4 > .wp-block-jetpack-layout-grid-column:nth-child(4) {
- -ms-grid-row: 4;
- grid-row-start: 4; } }
- .wp-block-jetpack-layout-grid .wp-block-jetpack-layout-grid-column * {
- word-break: break-word;
- word-wrap: break-word; }
- .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__none {
- grid-gap: 0px; }
- .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__small {
- grid-gap: 8px; }
- .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__medium {
- grid-gap: 16px; }
- .wp-block-jetpack-layout-grid.wp-block-jetpack-layout-gutter__huge {
- grid-gap: 48px; }
-
-@-moz-document url-prefix() {
- .wp-block-jetpack-layout-grid .wp-block-cover {
- max-height: 0; } }
+ grid-column-end: span 12; } }
diff --git a/blocks/layout-grid/front.scss b/blocks/layout-grid/front.scss
index 86676721..34b6471b 100644
--- a/blocks/layout-grid/front.scss
+++ b/blocks/layout-grid/front.scss
@@ -5,7 +5,6 @@
@import './constants.scss';
-
/**
* Responsive Grid Options
*/
@@ -14,92 +13,18 @@
.wp-block-jetpack-layout-grid {
display: grid;
grid-gap: $grid-gutter;
- grid-template-columns: $grid-mobile;
-
- // Grid rules.
- // Mobile first.
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-mobile-grid__start-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-start: $i;
- }
- }
- }
-
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-mobile-grid__span-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-end: span $i; // Set it to span $i columns, regardless of starting position.
- }
- }
- }
- @for $i from 1 through 4 {
- @for $x from 1 through 4 {
- &.column#{ $x }-mobile-grid__row-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-row-start: $i;
- }
- }
- }
+ // Default to mobile
+ grid-template-columns: $grid-mobile;
// Tablet grid rules.
@media (min-width: #{ ($break-small) }) {
-
grid-template-columns: $grid-tablet;
-
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-tablet-grid__start-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-start: $i;
- }
- }
- }
-
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-tablet-grid__span-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-end: span $i; // Set it to span $i columns, regardless of starting position.
- }
- }
- }
-
- @for $i from 1 through 4 {
- @for $x from 1 through 4 {
- &.column#{ $x }-tablet-grid__row-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-row-start: $i;
- }
- }
- }
}
-
// Desktop grid rules.
@media (min-width: #{ ($break-xlarge) }) {
grid-template-columns: $grid-desktop;
-
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-desktop-grid__start-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-start: $i;
- }
- }
- }
-
- @for $i from 1 through 12 {
- @for $x from 1 through 4 {
- &.column#{ $x }-desktop-grid__span-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-column-end: span $i; // Set it to span $i columns, regardless of starting position.
- }
- }
- }
-
- @for $i from 1 through 4 {
- @for $x from 1 through 4 {
- &.column#{ $x }-desktop-grid__row-#{ $i } > .wp-block-jetpack-layout-grid-column:nth-child(#{ $x }) {
- grid-row-start: $i;
- }
- }
- }
}
// Ensure long lines wrap in themes that don't already enforce this
@@ -131,3 +56,41 @@
}
}
}
+
+.wp-block-jetpack-layout-grid-column {
+ @for $position from 1 through 12 {
+ &.grid-column-mobile__start-#{ $position } {
+ grid-column-start: $position;
+ }
+
+ &.grid-column-mobile__span-#{ $position } {
+ grid-column-end: span $position; // Set it to span $i columns, regardless of starting position.
+ }
+ }
+
+ // Tablet grid rules.
+ @media (min-width: #{ ($break-small) }) {
+ @for $position from 1 through 12 {
+ &.grid-column-tablet__start-#{ $position } {
+ grid-column-start: $position;
+ }
+
+ &.grid-column-tablet__span-#{ $position } {
+ grid-column-end: span $position; // Set it to span $i columns, regardless of starting position.
+ }
+ }
+ }
+
+ // Desktop grid rules.
+ @media (min-width: #{ ($break-xlarge) }) {
+ @for $position from 1 through 12 {
+ &.grid-column-desktop__start-#{ $position } {
+ grid-column-start: $position;
+ }
+
+ &.grid-column-desktop__span-#{ $position } {
+ grid-column-end: span $position; // Set it to span $i columns, regardless of starting position.
+ }
+ }
+ }
+}
diff --git a/blocks/layout-grid/src/constants.js b/blocks/layout-grid/src/constants.js
index aa9aee1b..55a483fc 100644
--- a/blocks/layout-grid/src/constants.js
+++ b/blocks/layout-grid/src/constants.js
@@ -5,6 +5,70 @@
import { __ } from '@wordpress/i18n';
import { mobile, tablet, desktop } from '@wordpress/icons';
+/**
+ * @callback GetColumn
+ * @param {Device} device - Device to change
+ * @param {number} column - Column to change
+ * @returns {Column}
+ */
+
+/**
+ * @callback ChangeColumnCallback
+ * @param {Device} device - Device to change
+ * @param {number} column - Column to change
+ * @param {Column} value - New column value
+ */
+
+/**
+ * @typedef Column
+ * @type
+ * @property {number} start - Column start, relative to the row
+ * @property {number} span - Column span
+ * @property {number} row - Column row
+ */
+
+/**
+ * @typedef ColumnAdjustment
+ * @type
+ * @property {number} [start] - Column start
+ * @property {number} [span] - Column span
+ * @property {number} [row] - Column row
+ * @property {number} column - Column being adjusted
+ */
+
+/**
+ * @typedef ColumnAttributes
+ * @type
+ * @property {number} startDesktop
+ * @property {number} spanDesktop
+ * @property {number} rowDesktop
+ * @property {number} startMobile
+ * @property {number} spanMobile
+ * @property {number} rowMobile
+ * @property {number} startTablet
+ * @property {number} spanTablet
+ * @property {number} rowTablet
+ */
+
+/**
+ * @typedef ColumnDescription
+ * @type
+ * @property {string} label - Column label
+ * @property {number} value - Number of columns
+ */
+
+/**
+ * @typedef {"Desktop" | "Mobile" | "Tablet"} Device
+ */
+
+/**
+ * @typedef DeviceBreakpoint
+ * @type
+ * @property {string} label - Device label
+ * @property {Device} value - ID for this device
+ * @property {ReactElement} icon - Icon for this device
+ */
+
function getSpacingValues() {
return [
{ value: 'small', label: __( 'Small', 'layout-grid' ) },
@@ -14,15 +78,21 @@ function getSpacingValues() {
];
}
-export const getPaddingValues = () => ( [
- { value: 'none', label: __( 'No padding', 'layout-grid' ) },
-].concat( getSpacingValues() ) );
+export const getPaddingValues = () =>
+ [ { value: 'none', label: __( 'No padding', 'layout-grid' ) } ].concat(
+ getSpacingValues()
+ );
-export const getGutterValues = () => ( [
- { value: 'none', label: __( 'No gutter', 'layout-grid' ) },
-].concat( getSpacingValues() ) );
+export const getGutterValues = () =>
+ [ { value: 'none', label: __( 'No gutter', 'layout-grid' ) } ].concat(
+ getSpacingValues()
+ );
-export const getColumns = () => ( [
+/**
+ * Get column details
+ * @returns {ColumnDescription[]}
+ */
+export const getColumns = () => [
{
label: __( '1 column', 'layout-grid' ),
value: 1,
@@ -39,30 +109,46 @@ export const getColumns = () => ( [
label: __( '4 columns', 'layout-grid' ),
value: 4,
},
-] );
+];
+/** @type {Device} */
export const DEVICE_DESKTOP = 'Desktop';
+
+/** @type {Device} */
export const DEVICE_TABLET = 'Tablet';
+
+/** @type {Device} */
export const DEVICE_MOBILE = 'Mobile';
-export const getLayouts = () => ( [
- { value: DEVICE_DESKTOP, label: __( 'Desktop', 'layout-grid' ), icon: desktop },
- { value: DEVICE_TABLET, label: __( 'Tablet', 'layout-grid' ), icon: tablet },
- { value: DEVICE_MOBILE, label: __( 'Mobile', 'layout-grid' ), icon: mobile },
-] );
+/**
+ * Get all the device breakpoints
+ *
+ * @returns {DeviceBreakpoint[]}
+ */
+export const getLayouts = () => [
+ {
+ value: DEVICE_DESKTOP,
+ label: __( 'Desktop', 'layout-grid' ),
+ icon: desktop,
+ },
+ {
+ value: DEVICE_TABLET,
+ label: __( 'Tablet', 'layout-grid' ),
+ icon: tablet,
+ },
+ {
+ value: DEVICE_MOBILE,
+ label: __( 'Mobile', 'layout-grid' ),
+ icon: mobile,
+ },
+];
+/** @type {number} */
export const MAX_COLUMNS = 4;
+/** @type {Device[]} */
export const DEVICE_BREAKPOINTS = [
DEVICE_DESKTOP,
DEVICE_TABLET,
DEVICE_MOBILE,
];
-
-export function getSpanForDevice( column, device ) {
- return `column${ column + 1 }${ device }Span`;
-}
-
-export function getOffsetForDevice( column, device ) {
- return `column${ column + 1 }${ device }Offset`;
-}
diff --git a/blocks/layout-grid/src/grid-column/edit.js b/blocks/layout-grid/src/grid-column/edit.js
index 1614b477..87a45888 100644
--- a/blocks/layout-grid/src/grid-column/edit.js
+++ b/blocks/layout-grid/src/grid-column/edit.js
@@ -1,9 +1,3 @@
-/**
- * External dependencies
- */
-
-import classnames from 'classnames';
-
/**
* WordPress dependencies
*/
@@ -15,9 +9,10 @@ import {
withColors,
BlockControls,
BlockVerticalAlignmentToolbar,
+ getColorClassName,
} from '@wordpress/block-editor';
-import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
+import { useState } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { PanelBody, SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
@@ -26,119 +21,100 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { getPaddingValues } from '../constants';
-
-class Edit extends Component {
- constructor( props ) {
- super( props );
-
- this.state = { direction: null };
- }
-
- onLeftIn = () => {
- this.setState( { direction: 'left' } );
- document.addEventListener( 'mouseup', this.onLeftOut );
+import { getGridClasses, getBackgroundClasses, getColumnVerticalAlignClasses, getPaddingClasses } from '../grid-css';
+
+function Edit( props ) {
+ const {
+ className,
+ hasChildBlocks,
+ backgroundColor,
+ setBackgroundColor,
+ attributes,
+ setAttributes,
+ updateAlignment,
+ } = props;
+ const { padding, verticalAlignment } = attributes;
+ const [ direction, setDirection ] = useState( null );
+ const backgroundClass = getColorClassName( 'background-color', backgroundColor );
+ const classes = getGridClasses( className, {
+ ...getBackgroundClasses( backgroundColor, backgroundClass ),
+ ...getColumnVerticalAlignClasses( verticalAlignment ),
+ ...getPaddingClasses( padding ),
+
+ 'components-resizable-box__container': true,
+ 'wp-blocks-jetpack-layout-grid__showleft': direction === 'right',
+ 'wp-blocks-jetpack-layout-grid__showright': direction === 'left',
+ } );
+
+
+ const style = {
+ backgroundColor: backgroundColor.color,
+ };
+
+ function changeDirectionEnd() {
+ setDirection( null );
+
+ document.removeEventListener( 'mouseup', changeDirectionEnd );
}
- onLeftOut = () => {
- this.setState( { direction: null } );
- document.removeEventListener( 'mouseup', this.onLeftOut );
- }
-
- onRightIn = () => {
- this.setState( { direction: 'right' } );
- document.addEventListener( 'mouseup', this.onRightOut );
- }
+ function changeDirectionStart( direction ) {
+ setDirection( direction );
- onRightOut = () => {
- this.setState( { direction: null } );
- document.removeEventListener( 'mouseup', this.onRightOut );
+ document.addEventListener( 'mouseup', changeDirectionEnd );
}
- render() {
- const {
- className,
- hasChildBlocks,
- backgroundColor,
- setBackgroundColor,
- attributes,
- setAttributes,
- updateAlignment,
- } = this.props;
- const { padding, verticalAlignment } = attributes;
- const { direction } = this.state;
- const classes = classnames( className, backgroundColor.class, {
- [ 'wp-block-jetpack-layout-grid__padding-' + padding ]: true,
- 'has-background': backgroundColor.color,
- 'components-resizable-box__container': true,
- [ backgroundColor.class ]: backgroundColor.class,
- 'wp-blocks-jetpack-layout-grid__showleft': direction === 'right',
- 'wp-blocks-jetpack-layout-grid__showright': direction === 'left',
- [ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment,
- } );
- const style = {
- backgroundColor: backgroundColor.color,
- };
-
- // Note we wrap the InnerBlock with 'fake' resize handles. These are here so they always match the current column dimensions. Functionally
- // they do nothing other than disappear when the mouse button is pressed. The real resizing happens in the ResizeGrid component.
- // We identify the left and right handles by data-resize-left and data-resize-right
- return (
-
-
-
-
-
-
-
-
-
- }
+ // Note we wrap the InnerBlock with 'fake' resize handles. These are here so they always match the current column dimensions. Functionally
+ // they do nothing other than disappear when the mouse button is pressed. The real resizing happens in the ResizeGrid component.
+ // We identify the left and right handles by data-resize-left and data-resize-right
+ // TODO: remove this data stuff and the DOM crawling
+ return (
+
+
+ changeDirectionStart( 'right' ) }
+ data-resize-right
+ >
+ changeDirectionStart( 'left' ) }
+ data-resize-left
+ >
+
+
+
}
+ />
+
+
+
-
-
-
-
- { __( 'Choose padding for this column:', 'layout-grid' ) }
- setAttributes( { padding: newValue } ) }
- options={ getPaddingValues() }
- />
-
-
-
-
-
+ { __( 'Choose padding for this column:', 'layout-grid' ) }
+ setAttributes( { padding: newValue } ) }
+ options={ getPaddingValues() }
/>
-
-
- );
- }
+
+
+
+
+
+
+
+ );
}
export default compose(
@@ -155,12 +131,8 @@ export default compose(
return {
updateAlignment( verticalAlignment ) {
const { clientId, setAttributes } = ownProps;
- const { updateBlockAttributes } = dispatch(
- 'core/block-editor'
- );
- const { getBlockRootClientId } = registry.select(
- 'core/block-editor'
- );
+ const { updateBlockAttributes } = dispatch( 'core/block-editor' );
+ const { getBlockRootClientId } = registry.select( 'core/block-editor' );
// Update own alignment.
setAttributes( { verticalAlignment } );
diff --git a/blocks/layout-grid/src/grid-column/save.js b/blocks/layout-grid/src/grid-column/save.js
index ec518c26..7addf95f 100644
--- a/blocks/layout-grid/src/grid-column/save.js
+++ b/blocks/layout-grid/src/grid-column/save.js
@@ -1,29 +1,24 @@
-/**
- * External dependencies
- */
-
-import classnames from 'classnames';
-
/**
* Internal dependencies
*/
import { InnerBlocks, getColorClassName } from '@wordpress/block-editor';
+import {
+ getPaddingClasses,
+ getColumnVerticalAlignClasses,
+ getBackgroundClasses,
+ getGridClasses,
+ getColumnClasses,
+} from '../grid-css';
const save = ( { attributes = {} } ) => {
- const {
- className,
- backgroundColor,
- customBackgroundColor,
- padding,
- verticalAlignment,
- } = attributes;
+ const { className, backgroundColor, customBackgroundColor, padding, verticalAlignment } = attributes;
const backgroundClass = getColorClassName( 'background-color', backgroundColor );
- const classes = classnames( className, {
- [ 'wp-block-jetpack-layout-grid__padding-' + padding ]: true,
- 'has-background': backgroundColor,
- [ backgroundClass ]: backgroundClass,
- [ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment,
+ const classes = getGridClasses( className, {
+ ...getPaddingClasses( padding ),
+ ...getColumnVerticalAlignClasses( verticalAlignment ),
+ ...getBackgroundClasses( backgroundColor, backgroundClass ),
+ ...getColumnClasses( attributes, 'grid-column-$device__$grid-$position' ),
} );
const style = {
backgroundColor: backgroundClass ? undefined : customBackgroundColor,
diff --git a/blocks/layout-grid/src/grid-css.js b/blocks/layout-grid/src/grid-css.js
new file mode 100644
index 00000000..817651ed
--- /dev/null
+++ b/blocks/layout-grid/src/grid-css.js
@@ -0,0 +1,206 @@
+/**
+ * External dependencies
+ */
+
+import classnames from 'classnames';
+
+/**
+ * Internal dependencies
+ */
+
+import { DEVICE_BREAKPOINTS } from './constants';
+import { convertAttributesToColumn } from './grid-values';
+
+/** @typedef {import('./constants.js').ColumnAttributes} ColumnAttributes */
+/** @typedef {import('./constants.js').Device} Device */
+
+/**
+ * This removes our own CSS so we don't duplicate anything
+ *
+ * @param {string} className
+ * @returns {string}
+ */
+function removeGridClasses( className ) {
+ if ( ! className ) {
+ return className;
+ }
+
+ return className
+ .replace( /column\d-\w*-grid__\w*-\d*/g, '' )
+ .replace( /column\d-grid__\w*-\d*/g, '' )
+ .replace( /\s{2,}/, '' )
+ .replace( /wp-block-jetpack-layout-gutter__\w*/, '' )
+ .replace( /is-vertically-aligned-\w*/, '' )
+ .replace( /are-vertically-aligned-\w*/, '' );
+}
+
+/**
+ * Get classes for all columns on the device
+ *
+ * @param {Device} device - Selected device
+ * @param {ColumnAttributes[]} columns - Attributes for all column
+ * @returns {Object.}
+ */
+export function getColumnClassesForEditor( device, columns ) {
+ if ( ! columns || columns.length === 0 ) {
+ return {};
+ }
+
+ return Object.assign.apply(
+ {},
+ columns.map( ( column, index ) =>
+ getColumnClasses( column, `grid-column-${ index + 1 }__$grid-$position`, [ device ] )
+ )
+ );
+}
+
+function transformCssPattern( pattern, gridItem, gridPosition, device ) {
+ return pattern
+ .replace( '$grid', gridItem )
+ .replace( '$position', gridPosition )
+ .replace( '$device', device.toLowerCase() );
+}
+
+/**
+ * Get classes for a column across all devices
+ *
+ * @param {ColumnAttributes} columnAttributes - Attributes for the column
+ * @param {string} cssPattern - Pattern for the CSS name. Use `$grid` for the grid item (start, span, row), `$position` for the grid position, and `$device` for the device
+ * @returns {Object.}
+ */
+export function getColumnClasses( columnAttributes, cssPattern, devices = DEVICE_BREAKPOINTS ) {
+ return Object.assign.apply(
+ {},
+ devices.map( ( device ) => {
+ const { span, start, row } = convertAttributesToColumn( device, columnAttributes );
+
+ return {
+ // span is always present
+ [ transformCssPattern( cssPattern, 'span', span, device ) ]: true,
+
+ // start is always present, but is a value offset from the row
+ [ transformCssPattern( cssPattern, 'start', start + 1, device ) ]: true,
+
+ // row is only needed if not on the first row
+ [ transformCssPattern( cssPattern, 'row', row + 1, device ) ]: row > 0,
+ };
+ } )
+ );
+}
+
+/**
+ * Get Layout Grid editor classes
+ *
+ * @param {string} device - Current editor device
+ * @param {boolean} canResize - Can we resize the selected device at the current screen width?
+ * @returns {Object.}
+ */
+export function getGridEditorClasses( device, canResize ) {
+ return {
+ // Breakpoint
+ 'wp-block-jetpack-layout-tablet': device === 'Tablet',
+ 'wp-block-jetpack-layout-desktop': device === 'Desktop',
+ 'wp-block-jetpack-layout-mobile': device === 'Mobile',
+
+ // Is this grid resizable on this screen size?
+ 'wp-block-jetpack-layout-resizable': canResize,
+ };
+}
+
+/**
+ * Get classes for the grid vertical alignment
+ *
+ * @param {string|null} verticalAlignment - Vertical alignment
+ * @returns {Object.}
+ */
+export function getGridVerticalAlignClasses( verticalAlignment ) {
+ return {
+ [ `are-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment && verticalAlignment !== 'top',
+ };
+}
+
+/**
+ * Get classes for the column vertical alignment
+ *
+ * @param {string|null} verticalAlignment - Vertical alignment
+ * @returns {Object.}
+ */
+export function getColumnVerticalAlignClasses( verticalAlignment ) {
+ return {
+ [ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment && verticalAlignment !== 'top',
+ };
+}
+
+/**
+ * Get vertical alignment for all the columns for the editor
+ *
+ * @param {ColumnAttributes[]} columns - Attributes for all column
+ * @returns {Object.}
+ */
+export function getColumnVerticalAlignEditorClasses( columns ) {
+ if ( ! columns || columns.length === 0 ) {
+ return {};
+ }
+
+ return Object.assign.apply(
+ {},
+ columns.map( ( column, index ) => ( {
+ [ `grid-column-${ index + 1 }__valign-top` ]: column.verticalAlignment === 'top',
+ [ `grid-column-${ index + 1 }__valign-center` ]: column.verticalAlignment === 'center',
+ [ `grid-column-${ index + 1 }__valign-bottom` ]: column.verticalAlignment === 'bottom',
+ } ) )
+ );
+}
+
+/**
+ * Get classes for the padding
+ *
+ * @param {string} padding - Padding size
+ * @returns {Object.}
+ */
+export function getPaddingClasses( padding ) {
+ return {
+ [ 'wp-block-jetpack-layout-grid__padding-' + padding ]: padding.length > 0,
+ };
+}
+
+/**
+ * Get classes for the gutter
+ *
+ * @param {boolean} addGutterEnds - Add gutter to the ends of the gried
+ * @param {('none'|'small'|'medium'|'huge')} gutterSize - Gutter size
+ * @returns {Object.}
+ */
+export function getGutterClasses( addGutterEnds, gutterSize ) {
+ return {
+ 'wp-block-jetpack-layout-gutter__nowrap': ! addGutterEnds,
+ 'wp-block-jetpack-layout-gutter__none': gutterSize === 'none',
+ 'wp-block-jetpack-layout-gutter__small': gutterSize === 'small',
+ 'wp-block-jetpack-layout-gutter__medium': gutterSize === 'medium',
+ 'wp-block-jetpack-layout-gutter__huge': gutterSize === 'huge',
+ };
+}
+
+/**
+ * Get classes for the background colour
+ *
+ * @param {string} backgroundColor - Background colour
+ * @param {string} backgroundClass - Background class
+ * @returns {Object.}
+ */
+export function getBackgroundClasses( backgroundColor, backgroundClass ) {
+ return {
+ 'has-background': backgroundColor,
+ [ backgroundClass ]: backgroundClass,
+ };
+}
+
+/**
+ * Merge all column classes together along with the editor class name. Note this will remove existing Layout Grid classes
+ *
+ * @param {string} className - Existing editor className
+ * @param {Object.} extra - Any additional classes
+ */
+export function getGridClasses( className, extra ) {
+ return classnames( removeGridClasses( className ), extra );
+}
diff --git a/blocks/layout-grid/src/grid-values.js b/blocks/layout-grid/src/grid-values.js
new file mode 100644
index 00000000..6f5fc062
--- /dev/null
+++ b/blocks/layout-grid/src/grid-values.js
@@ -0,0 +1,143 @@
+/**
+ * Internal dependencies
+ */
+
+import { DEVICE_MOBILE, DEVICE_TABLET, DEVICE_BREAKPOINTS } from './constants';
+
+/** @typedef {import('./constants.js').Device} Device */
+/** @typedef {import('./constants.js').ColumnAttributes} ColumnAttributes */
+/** @typedef {import('./constants.js').Column} Column */
+
+/**
+ * Get grid width for a device
+ *
+ * @param {Device} device - Device
+ * @returns {number}
+ */
+export function getGridWidth( device ) {
+ if ( device === DEVICE_TABLET ) {
+ return 8;
+ } else if ( device === DEVICE_MOBILE ) {
+ return 4;
+ }
+
+ return 12;
+}
+
+/**
+ * Get grid rows for a device
+ *
+ * @param {Device} device - Device
+ * @returns {number}
+ */
+export const getGridRows = ( device ) => {
+ if ( device === DEVICE_TABLET ) {
+ return 2;
+ } else if ( device === DEVICE_MOBILE ) {
+ return 4;
+ }
+
+ return 1;
+};
+
+/**
+ * Get default span for a column, given a device and number of columns
+ *
+ * - 1 column: desktop=1x12x1 tablet=1x8x1 mobile=1x4x1
+ * - 2 column: desktop=2x6x1 tablet=2x4x1 mobile=1x4x2
+ * - 3 column: desktop=3x4x1 tablet=2x4x1 + 1x8x1 mobile=1x4x3
+ * - 4 column: desktop=4x3x1 tablet=2x4x2 mobile=1x4x4
+ *
+ * @param {Device} device - Device
+ * @param {number} columns - Number of columns
+ * @param {number} column - Current column
+ * @returns {number}
+ */
+export function getDefaultSpan( device, columns, column ) {
+ if ( device === DEVICE_TABLET ) {
+ if ( columns === 3 && column === 2 ) {
+ return getGridWidth( device );
+ } else if ( columns > 1 ) {
+ return getGridWidth( device ) / 2;
+ }
+
+ return getGridWidth( device );
+ }
+
+ if ( device === DEVICE_MOBILE ) {
+ return getGridWidth( device );
+ }
+
+ return getGridWidth( device ) / columns;
+}
+
+/**
+ * @param {Device} device
+ * @param {ColumnAttributes} attributes
+ * @returns {Column}
+ */
+export function convertAttributesToColumn( device, attributes ) {
+ return {
+ span: attributes[ `span${ device }` ] || 0,
+ start: attributes[ `start${ device }` ] || 0,
+ row: attributes[ `row${ device }` ] || 0,
+ };
+}
+
+/**
+ * @param {Device} device
+ * @param {Column} column
+ * @returns {ColumnAttributes} attributes
+ */
+export function convertColumnToAttributes( device, column ) {
+ const { span, start, row } = column;
+
+ return {
+ [ `span${ device }` ]: span,
+ [ `row${ device }` ]: row,
+ [ `start${ device }` ]: start,
+ };
+}
+
+/**
+ * Get the row, treating `start` as a single row split into `maxWidth` individual rows
+ * @param {number} start - Absolute start
+ * @param {number} maxWidth - Maximum width of the grid for the device
+ * @returns {number}
+ */
+function getGridRow( start, maxWidth ) {
+ return Math.floor( start / maxWidth );
+}
+
+/**
+ * Get the relative start position, treating `start` as a single row split into `maxWidth` individual rows
+ *
+ * @param {number} start - Absolute start
+ * @param {number} maxWidth - Maximum width of the grid for the device
+ * @returns {number}
+ */
+function getGridRowStart( start, maxWidth ) {
+ return start % maxWidth;
+}
+
+/**
+ * Get default layout for a number of columns on a particular device
+ *
+ * @param {number} totalColumns - Total number of columns
+ * @param {number} columnNumber - Column number
+ * @returns {ColumnAttributes}
+ */
+export function getDefaultColumnAttributes( totalColumns, columnNumber ) {
+ return Object.assign.apply(
+ {},
+ DEVICE_BREAKPOINTS.map( ( device ) => {
+ const span = getDefaultSpan( device, totalColumns, columnNumber );
+
+ return convertColumnToAttributes( device, {
+ span,
+ row: getGridRow( span * columnNumber, getGridWidth( device ) ),
+ start: getGridRowStart( span * columnNumber, getGridWidth( device ) ),
+ } );
+ } )
+ );
+}
diff --git a/blocks/layout-grid/src/grid.scss b/blocks/layout-grid/src/grid.scss
index 3f86f160..d3b12d82 100644
--- a/blocks/layout-grid/src/grid.scss
+++ b/blocks/layout-grid/src/grid.scss
@@ -58,8 +58,8 @@
@for $i from 1 through 12 {
@for $x from 1 through 4 {
- &.column#{ $x }-grid__start-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
- &.column#{ $x }-grid__start-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__start-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
+ &.grid-column-#{ $x }__start-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
grid-column-start: $i;
}
}
@@ -67,21 +67,21 @@
@for $i from 1 through 12 {
@for $x from 1 through 4 {
- &.column#{ $x }-grid__span-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
- &.column#{ $x }-grid__span-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__span-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
+ &.grid-column-#{ $x }__span-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
grid-column-end: span $i; // Set it to span $i columns, regardless of starting position.
}
}
- &.column4-grid__span-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(5) {
+ &.grid-column-4__span-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(5) {
grid-column-end: span $i; // The re-resizable module adds extra elements that confuse the calculation in 4 column mode
}
}
@for $i from 1 through 4 {
@for $x from 1 through 4 {
- &.column#{ $x }-grid__row-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
- &.column#{ $x }-grid__row-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
+ &.grid-column-#{ $x }__row-#{ $i } > .wpcom-resize-grid > .wp-block:nth-child(#{ $x }),
+ &.grid-column-#{ $x }__row-#{ $i } > .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block:nth-child(#{ $x }) {
grid-row-start: $i;
}
}
diff --git a/blocks/layout-grid/src/grid/block-controls/index.js b/blocks/layout-grid/src/grid/block-controls/index.js
new file mode 100644
index 00000000..769395b4
--- /dev/null
+++ b/blocks/layout-grid/src/grid/block-controls/index.js
@@ -0,0 +1,84 @@
+/**
+ * WordPress dependencies
+ */
+
+import {
+ BlockControls,
+ BlockVerticalAlignmentToolbar,
+} from '@wordpress/block-editor';
+import {
+ Button,
+ ToolbarGroup,
+ MenuGroup,
+ MenuItem,
+ Dropdown,
+} from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import { getLayouts } from '../../constants';
+
+/** @typedef {import('../edit.js').SetDeviceCallback} SetDeviceCallback */
+/** @typedef {import('../edit.js').VerticalAlignment} VerticalAlignment */
+/** @typedef {import('../edit.js').SetAlignmentCallback} SetAlignmentCallback */
+/** @typedef {import('../../constants.js').Device} Device */
+
+/**
+ * Block controls for the Layout Grid
+ *
+ * @param {object} props - Component props
+ * @param {Device} props.device - Current device
+ * @param {SetDeviceCallback} props.setDevice - Callback to change the device
+ * @param {VerticalAlignment} props.verticalAlignment - Vertical verticalAlignment
+ * @param {SetAlignmentCallback} props.setVerticalAlignment - Callback to change the vertical alignment
+ */
+function LayoutGridBlockControls( props ) {
+ const {
+ device,
+ setDevice,
+ verticalAlignment,
+ setVerticalAlignment,
+ } = props;
+
+ return (
+
+
+ (
+
+ layout.value === device
+ ).icon
+ }
+ />
+
+ ) }
+ renderContent={ ( { onClose } ) => (
+
+ { getLayouts().map( ( layout ) => (
+ setDevice( layout.value ) }
+ icon={ layout.icon }
+ >
+ { layout.label }
+
+ ) ) }
+
+ ) }
+ />
+
+ );
+}
+
+export default LayoutGridBlockControls;
diff --git a/blocks/layout-grid/src/grid/change-number-columns.js b/blocks/layout-grid/src/grid/change-number-columns.js
new file mode 100644
index 00000000..10230680
--- /dev/null
+++ b/blocks/layout-grid/src/grid/change-number-columns.js
@@ -0,0 +1,73 @@
+/**
+ * External dependencies
+ */
+
+import { times } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+
+import { createBlock } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+
+import { getDefaultColumnAttributes } from '../grid-values';
+
+/**
+ * Returns a grid of columns with the default layout applied to all columns (i.e. the columns equally fill the width for a device)
+ *
+ * @param {object[]} currentBlocks - Current blocks
+ * @returns {object[]}
+ */
+export function setDefaultGridLayout( currentBlocks ) {
+ const defaultColumns = times( currentBlocks.length, ( columnNumber ) =>
+ getDefaultColumnAttributes( currentBlocks.length, columnNumber )
+ );
+
+ // Apply the device attributes for each column to the block
+ return currentBlocks.map( ( block, columnNumber ) => ( {
+ ...block,
+ attributes: {
+ ...block.attributes,
+ ...defaultColumns[ columnNumber ],
+ },
+ } ) );
+}
+
+/**
+ * Adjust the number of columns, by adding new columns, or intelligently removing columns (priority given to empty ones)
+ *
+ * @param {object[]} innerBlocks - Current blocks
+ * @param {number} columns - New number of columnrs
+ * @returns {object[]}
+ */
+export function changeNumberOfColumns( innerBlocks, columns ) {
+ if ( columns > innerBlocks.length ) {
+ // Add to end
+ return [
+ ...innerBlocks,
+ ...times( columns - innerBlocks.length, ( column ) => createBlock( 'jetpack/layout-grid-column' ) ),
+ ];
+ }
+
+ // Reverse the blocks so we start at the end. This happens in-place
+ innerBlocks.reverse();
+
+ // Remove empty blocks, then remove from the end
+ let totalRemoved = 0;
+
+ return innerBlocks
+ .filter( ( block ) => {
+ if ( totalRemoved < innerBlocks.length - columns && block.innerBlocks.length === 0 ) {
+ totalRemoved++;
+ return false;
+ }
+
+ return true;
+ } )
+ .slice( Math.max( 0, innerBlocks.length - columns - totalRemoved ) )
+ .reverse();
+}
diff --git a/blocks/layout-grid/src/grid/css-classname.js b/blocks/layout-grid/src/grid/css-classname.js
deleted file mode 100644
index 5948eb19..00000000
--- a/blocks/layout-grid/src/grid/css-classname.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/**
- * Internal dependencies
- */
-
-import { DEVICE_BREAKPOINTS, getSpanForDevice, getOffsetForDevice } from '../constants';
-import { getDefaultSpan, getGridWidth } from './grid-defaults';
-
-const getDevicelessSpanClassName = ( column, value ) => `column${ column + 1 }-grid__span-${ value }`;
-const getDevicelessOffsetClassName = ( column, value ) => `column${ column + 1 }-grid__start-${ value }`;
-const getDevicelessRowClassName = ( column, value ) => `column${ column + 1 }-grid__row-${ value }`;
-const getDevicelessAlignmentClassName = ( column, value ) => `column${ column + 1 }-grid__valign-${ value }`;
-
-const getDeviceSpanClassName = ( column, value, device ) => `column${ column + 1 }-${ device.toLowerCase() }-grid__span-${ value }`;
-const getDeviceOffsetClassName = ( column, value, device ) => `column${ column + 1 }-${ device.toLowerCase() }-grid__start-${ value }`;
-const getDeviceRowClassName = ( column, value, device ) => `column${ column + 1 }-${ device.toLowerCase() }-grid__row-${ value }`;
-
-function convertOffsetsToPositions( totalColumns, device, attributes ) {
- const offsets = [];
- let position = 0;
-
- for ( let column = 0; column < totalColumns; column++ ) {
- const customSpanName = getSpanForDevice( column, device );
- const customOffsetName = getOffsetForDevice( column, device );
- const span = attributes[ customSpanName ] || getDefaultSpan( device, totalColumns, column );
- const offset = attributes[ customOffsetName ] || 0;
-
- // Position is the current position plus the offset
- offsets.push( {
- position: position + offset,
- span,
- } );
-
- // Move the position up by the offset and the width of the column
- position += offset;
- position += span;
- }
-
- return offsets;
-}
-
-/*
- * Converts a position into a row
- */
-function getGridRow( position, maxWidth ) {
- return Math.floor( position / maxWidth );
-}
-
-/*
- * Converts a position into an offset from the start of a row
- */
-function getGridRowStart( position, maxWidth ) {
- return position % maxWidth;
-}
-
-function getDeviceClass( name, column, value, device, enabled = true ) {
- return {
- name,
- column,
- value,
- device,
- enabled,
- };
-}
-
-function getDeviceColumnClass( device, columns, attributes ) {
- const classes = [];
- const width = getGridWidth( device );
-
- // Convert all the offsets into absolute positions. Treat it as one giant row
- const positions = convertOffsetsToPositions( columns, device, attributes );
-
- // Now go through and convert this to classes, converting the single row absolute positions into rows and row offsets
- for ( let index = 0; index < positions.length; index++ ) {
- const { span, position } = positions[ index ];
- const row = getGridRow( position, width );
- const offset = getGridRowStart( position, width );
-
- classes.push( getDeviceClass( 'span', index, span, device ) );
- classes.push( getDeviceClass( 'offset', index, offset + 1, device, offset > 0 ) );
- classes.push( getDeviceClass( 'row', index, row + 1, device ) );
- }
-
- return classes;
-}
-
-function convertClassesToObject( classes, map ) {
- const cssValues = {};
-
- classes
- .filter( ( item ) => item.enabled && map[ item.name ] )
- .map( ( item ) => cssValues[ map[ item.name ]( item.column, item.value, item.device ) ] = true );
-
- return cssValues;
-}
-
-/**
- * These are used in the editor which doesn't rely on CSS media queries, and so the classes need to be device agnostic
- *
- * @param {string} device - Device string
- * @param {number} columns - Number of columns
- * @param {object} attributes - Grid block attributes
- * @param {object[]} columnAttributes - Grid column block attributes
- */
-export function getAsEditorCSS( device, columns, attributes = {}, columnAttributes = [] ) {
- const values = getDeviceColumnClass(
- device,
- columns,
- attributes
- );
- const map = {
- span: getDevicelessSpanClassName,
- offset: getDevicelessOffsetClassName,
- row: getDevicelessRowClassName,
- };
-
- // Apply column-specific alignment at the global level. This is because of the nested DOM inside the editor
- const columnAlignments = {};
- for ( let index = 0; index < columns; index++ ) {
- // If the column has a vertical alignment and it's not the same as the global one then add a CSS class
- if (
- columnAttributes[ index ].verticalAlignment &&
- columnAttributes[ index ].verticalAlignment !== attributes.verticalAlignment
- ) {
- columnAlignments[
- getDevicelessAlignmentClassName(
- index,
- columnAttributes[ index ].verticalAlignment
- )
- ] = true;
- }
- }
-
- return {
- ...convertClassesToObject( values, map ),
- ...columnAlignments,
- };
-}
-
-/*
- * These are used in the front end and need device-specific CSS
- */
-export function getAsCSS( columns, attributes = {} ) {
- let classes = {};
- const map = {
- span: getDeviceSpanClassName,
- offset: getDeviceOffsetClassName,
- row: getDeviceRowClassName,
- };
-
- for ( let deviceIndex = 0; deviceIndex < DEVICE_BREAKPOINTS.length; deviceIndex++ ) {
- classes = {
- ...classes,
- ...convertClassesToObject( getDeviceColumnClass( DEVICE_BREAKPOINTS[ deviceIndex ], columns, attributes ), map ),
- };
- }
-
- if ( ! attributes.addGutterEnds ) {
- classes[ 'wp-block-jetpack-layout-gutter__nowrap' ] = true;
- }
-
- if ( attributes.verticalAlignment && attributes.verticalAlignment !== 'top' ) {
- classes[
- `are-vertically-aligned-${ attributes.verticalAlignment }`
- ] = true;
- }
-
- return classes;
-}
-
-export function removeGridClasses( classes ) {
- if ( ! classes ) {
- return classes;
- }
-
- return classes
- .replace( /column\d-\w*-grid__\w*-\d*/g, '' )
- .replace( /column\d-grid__\w*-\d*/g, '' )
- .replace( /\s{2,}/, '' )
- .replace( /wp-block-jetpack-layout-gutter__\w*/, '' )
- .replace( /is-vertically-aligned-\w*/, '' )
- .replace( /are-vertically-aligned-\w*/ );
-}
-
-export function getGutterClasses( { gutterSize, addGutterEnds } ) {
- // Note that 'large' is the default and doesn't output any CSS class
- return {
- 'wp-block-jetpack-layout-gutter__nowrap': ! addGutterEnds,
- 'wp-block-jetpack-layout-gutter__none': gutterSize === 'none',
- 'wp-block-jetpack-layout-gutter__small': gutterSize === 'small',
- 'wp-block-jetpack-layout-gutter__medium': gutterSize === 'medium',
- 'wp-block-jetpack-layout-gutter__huge': gutterSize === 'huge',
- };
-}
diff --git a/blocks/layout-grid/src/grid/edit.js b/blocks/layout-grid/src/grid/edit.js
index 661a39e8..0bf733a6 100644
--- a/blocks/layout-grid/src/grid/edit.js
+++ b/blocks/layout-grid/src/grid/edit.js
@@ -2,460 +2,243 @@
* External dependencies
*/
-import { times } from 'lodash';
-import classnames from 'classnames';
+import { times, pickBy, isNumber } from 'lodash';
/**
* WordPress dependencies
*/
-import {
- InnerBlocks,
- InspectorControls,
-} from '@wordpress/block-editor';
-import { Component, createRef } from '@wordpress/element';
-import {
- BlockControls,
- BlockVerticalAlignmentToolbar,
-} from '@wordpress/block-editor';
-import {
- PanelBody,
- TextControl,
- ButtonGroup,
- Button,
- IconButton,
- Placeholder,
- IsolatedEventContainer,
- ToggleControl,
- SelectControl,
- Disabled,
- ToolbarGroup,
- MenuGroup,
- MenuItem,
- Dropdown,
-} from '@wordpress/components';
+import { InnerBlocks } from '@wordpress/block-editor';
+import { useRef } from '@wordpress/element';
+import { IsolatedEventContainer } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { ENTER, SPACE } from '@wordpress/keycodes';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
-import { createBlock } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import {
- getAsEditorCSS,
- removeGridClasses,
+ getGridClasses,
+ getColumnClassesForEditor,
+ getGridEditorClasses,
+ getGridVerticalAlignClasses,
+ getColumnVerticalAlignEditorClasses,
getGutterClasses,
-} from './css-classname';
-import ColumnIcon from '../icons';
-import { getLayouts, getColumns, DEVICE_BREAKPOINTS, getSpanForDevice, getOffsetForDevice, getGutterValues } from '../constants';
-import { getGridWidth, getDefaultSpan } from './grid-defaults';
-import ResizeGrid from './resize-grid';
-import LayoutGrid from './layout-grid';
+} from '../grid-css';
+import { getGridWidth, convertAttributesToColumn, convertColumnToAttributes } from '../grid-values';
+import { changeNumberOfColumns, setDefaultGridLayout } from './change-number-columns';
+import ResizeGrid from './grid-resizer';
+import { getGridAdjustments, validateGridAdjustments } from './grid-adjust';
+import LayoutGridPlaceholder from './placeholder';
+import LayoutGridBlockControls from './block-controls';
+import LayoutGridInspector from './inspector';
const ALLOWED_BLOCKS = [ 'jetpack/layout-grid-column' ];
const MINIMUM_RESIZE_SIZE = 50; // Empirically determined to be a good size
-class Edit extends Component {
- constructor( props ) {
- super( props );
+/** @typedef {import('../constants.js').Device} Device */
+/** @typedef {"top" | "center" | "bottom"} VerticalAlignment */
- this.overlayRef = createRef();
- }
-
- /*
- * Change the layout (number of columns), resetting everything to the default
- */
- onChangeLayout = ( columns ) => {
- const columnValues = {};
+/**
+ * @callback SetDeviceCallback
+ * @param {string} device - New device
+ */
- for ( let pos = 0; pos < columns; pos++ ) {
- for ( let device = 0; device < DEVICE_BREAKPOINTS.length; device++ ) {
- const defaultSpan = getDefaultSpan( DEVICE_BREAKPOINTS[ device ], columns, pos );
+/**
+ * @callback SetAlignmentCallback
+ * @param {VerticalAlignment} alignment - New alignment
+ */
- columnValues[ getSpanForDevice( pos, DEVICE_BREAKPOINTS[ device ] ) ] = defaultSpan;
- columnValues[ getOffsetForDevice( pos, DEVICE_BREAKPOINTS[ device ] ) ] = 0;
- }
- }
+/**
+ * Can we resize at the current device breakpoint?
+ *
+ * @param {Device} device - Current device
+ * @param {} overlayRef - Reference to the grid overlay
+ */
+function canResizeBreakpoint( device, overlayRef ) {
+ if ( overlayRef && overlayRef.current ) {
+ const { width } = overlayRef.current.getBoundingClientRect();
- this.props.updateColumns( this.props.columns, columns, columnValues );
+ return width / getGridWidth( device ) > MINIMUM_RESIZE_SIZE;
}
- onResize = ( column, adjustment ) => {
- const { attributes, columns } = this.props;
- const grid = new LayoutGrid( attributes, this.props.deviceType, columns );
- const adjustedGrid = grid.getAdjustedGrid( column, adjustment );
-
- if ( adjustedGrid ) {
- this.adjustGrid( adjustedGrid );
- }
- }
+ return false;
+}
- onChangeSpan = ( column, device, value ) => {
- const { attributes, columns } = this.props;
- const grid = new LayoutGrid( attributes, device, columns );
- const adjustedGrid = grid.getAdjustedGrid( column, { span: parseInt( value, 10 ) } );
+function Edit( props ) {
+ const {
+ className,
+ attributes = {},
+ isSelected,
+ numberOfColumns,
+ setAttributes,
+ updateAlignment,
+ columnAttributes,
+ updateColumn,
+ updateNumberColumns,
+ setDeviceType,
+ deviceType,
+ } = props;
+ const { gutterSize, addGutterEnds, verticalAlignment } = attributes;
+ const overlayRef = useRef();
+ const classes = getGridClasses( className, {
+ ...getGridEditorClasses( deviceType, canResizeBreakpoint( deviceType, overlayRef ) ),
+
+ // The editor has all the column settings on the grid as the DOM is arranged differently
+ ...getColumnClassesForEditor( deviceType, columnAttributes ),
+
+ // For the editor we need the grid vertical align as well as all the column individual aligns
+ ...getGridVerticalAlignClasses( verticalAlignment ),
+ ...getColumnVerticalAlignEditorClasses( columnAttributes ),
+
+ ...getGutterClasses( addGutterEnds, gutterSize ),
+ } );
- if ( adjustedGrid ) {
- this.adjustGrid( adjustedGrid );
- }
+ if ( numberOfColumns === 0 ) {
+ return ;
}
- onChangeOffset = ( column, device, value ) => {
- const { attributes, columns } = this.props;
- const grid = new LayoutGrid( attributes, device, columns );
- const adjustedGrid = grid.getAdjustedGrid( column, { start: grid.convertOffsetToStart( column, parseInt( value, 10 ) ) } );
-
- if ( adjustedGrid ) {
- this.adjustGrid( adjustedGrid );
+ // Convert our attributes into column details
+ function getColumn( device, column ) {
+ if ( columnAttributes[ column ] ) {
+ return convertAttributesToColumn( device, columnAttributes[ column ] );
}
+ debugger;
+ return null;
}
- adjustGrid( grid ) {
- const { setAttributes, attributes } = this.props;
-
- setAttributes( {
- ...grid,
- className: removeGridClasses( attributes.className ),
- } );
- }
-
- renderDeviceSettings( columns, device, attributes ) {
- const grid = new LayoutGrid( attributes, device, this.props.columns );
- const settings = [];
-
- for ( let column = 0; column < columns; column++ ) {
- const span = grid.getSpan( column ) || getDefaultSpan( device, columns, column );
- const offset = grid.getOffset( column ) || 0;
-
- settings.push( (
-
-
{ __( 'Column', 'layout-grid' ) } { column + 1 }
-
- this.onChangeOffset( column, device, value ) }
- />
- this.onChangeSpan( column, device, value ) }
- />
-
-
- ) );
- }
-
- return settings;
- }
+ function setColumn( device, columnNumber, attributesToAdjust ) {
+ // Convert all the columns to grid format
+ const columns = columnAttributes.map( ( column, pos ) => getColumn( device, pos ) );
- canResizeBreakpoint( device ) {
- if ( this.overlayRef && this.overlayRef.current ) {
- const { width } = this.overlayRef.current.getBoundingClientRect();
+ // Get a set of adjustments (this adjustment could cause other columns to adjust)
+ const adjustments = getGridAdjustments( columns, attributesToAdjust, columnNumber );
- return width / getGridWidth( device ) > MINIMUM_RESIZE_SIZE;
+ if (
+ adjustments.length > 0 &&
+ validateGridAdjustments( columns, adjustments, getGridWidth( device ) )
+ ) {
+ // Apply adjustments to column
+ adjustments.forEach( ( adjustment ) => {
+ updateColumn( adjustment.column, convertColumnToAttributes( device, adjustment ) );
+ } );
}
-
- return false;
}
- render() {
- const {
- className,
- attributes = {},
- isSelected,
- columns,
- setAttributes,
- updateAlignment,
- columnAttributes,
- setDeviceType,
- deviceType,
- } = this.props;
- const extra = getAsEditorCSS(
- deviceType,
- columns,
- attributes,
- columnAttributes
- );
- const { gutterSize, addGutterEnds, verticalAlignment } = attributes;
- const layoutGrid = new LayoutGrid( attributes, deviceType, columns );
- const classes = classnames(
- removeGridClasses( className ),
- extra,
- {
- 'wp-block-jetpack-layout-tablet': deviceType === 'Tablet',
- 'wp-block-jetpack-layout-desktop': deviceType === 'Desktop',
- 'wp-block-jetpack-layout-mobile': deviceType === 'Mobile',
- 'wp-block-jetpack-layout-resizable': this.canResizeBreakpoint(
- deviceType
- ),
- [ `are-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment,
- },
- getGutterClasses( attributes )
- );
-
- if ( columns === 0 ) {
- return (
-
+
+ setColumn( deviceType, columnNumber, adjustment ) }
+ totalColumns={ getGridWidth( deviceType ) }
+ isSelected={ isSelected }
+ columns={ times( numberOfColumns, ( count ) => getColumn( deviceType, count ) ) }
>
-
+
+
+
- );
-
- return (
- <>
-
-
-
- { times( getGridWidth( deviceType ) ).map( ( item ) =>
) }
-
-
-
-
-
-
-
- { getColumns().map( ( column ) => (
-
this.onChangeLayout( column.value ) }
- onKeyDown={ ( event ) => {
- if ( ENTER === event.keyCode || SPACE === event.keyCode ) {
- event.preventDefault();
- this.onChangeLayout( column.value );
- }
- } }
- role="button"
- tabIndex="0"
- aria-label={ column.label }
- >
-
-
-
-
- { column.label }
-
-
- ) ) }
-
-
- { __( 'Changing the number of columns will reset your layout and could remove content.', 'layout-grid' ) }
-
-
-
- { __( "Note that previewing your post will show your browser's breakpoint, not the currently selected one.", 'layout-grid' ) }
-
- { getLayouts().map( ( layout ) => (
- setDeviceType( layout.value ) }
- >
- { layout.label }
-
- ) ) }
-
-
- { this.renderDeviceSettings( columns, deviceType, attributes ) }
-
-
-
- { __( 'Gutter size', 'layout-grid' ) }
-
- setAttributes( { gutterSize: newValue, addGutterEnds: newValue === 'none' ? false : addGutterEnds } ) }
- options={ getGutterValues() }
- />
-
- { gutterSize === 'none' ? (
-
- { toggleControl }
-
- ) : toggleControl }
-
-
-
-
-
-
-
-
- (
-
- layout.value === deviceType ).icon }
- />
-
- ) }
- renderContent={ ( { onClose } ) => (
-
- { getLayouts().map( ( layout ) => (
- setDeviceType( layout.value ) }
- icon={ layout.icon }
- >
- { layout.label }
-
- ) ) }
-
- ) }
- />
-
- >
- );
- }
+
+
+ >
+ );
}
-function getColumnBlocks( currentBlocks, previous, columns ) {
- if ( columns > previous ) {
- // Add new blocks to the end
- return [
- ...currentBlocks,
- ...times( columns - previous, () => createBlock( 'jetpack/layout-grid-column' ) ),
- ];
- }
+export default compose( [
+ withDispatch(
+ /**
+ * @param {{clientId: string, attributes: object, name: string, columns: number}} ownProps - Our own props
+ */
+ ( dispatch, ownProps, registry ) => ( {
+ /**
+ * Update all child Column blocks with a new vertical alignment setting
+ * based on whatever alignment is passed in. This allows change to parent
+ * to overide anything set on a individual column basis.
+ *
+ * @param {string} verticalAlignment the vertical alignment setting
+ */
+ updateAlignment( verticalAlignment ) {
+ const { clientId, setAttributes } = ownProps;
+ const { updateBlockAttributes } = dispatch( 'core/block-editor' );
+ const { getBlockOrder } = registry.select( 'core/block-editor' );
+
+ // Update own alignment.
+ setAttributes( { verticalAlignment } );
+
+ // Update all child Column Blocks to match
+ const innerBlockClientIds = getBlockOrder( clientId );
+ innerBlockClientIds.forEach( ( innerBlockClientId ) => {
+ updateBlockAttributes( innerBlockClientId, {
+ verticalAlignment,
+ } );
+ } );
+ },
- // A little ugly but... ideally we remove empty blocks first, and then anything with content from the end
- let cleanedBlocks = [ ...currentBlocks ];
- let totalRemoved = 0;
+ updateColumn( columnNumber, attributes ) {
+ const { updateBlockAttributes } = dispatch( 'core/block-editor' );
+ const { getBlockOrder } = registry.select( 'core/block-editor' );
- // Reverse the blocks so we start at the end. This happens in-place
- cleanedBlocks.reverse();
+ // Update all child Column Blocks to match
+ const innerBlockClientIds = getBlockOrder( ownProps.clientId );
- // Remove empty blocks
- cleanedBlocks = cleanedBlocks.filter( ( block ) => {
- if ( totalRemoved < previous - columns && block.innerBlocks.length === 0 ) {
- totalRemoved++;
- return false;
- }
+ updateBlockAttributes( innerBlockClientIds[ columnNumber ], pickBy( attributes, isNumber ) );
+ },
- return true;
- } );
+ /**
+ * Change the number of columns
+ *
+ * @param {number} columnCount - New column count
+ */
+ updateNumberColumns( columnCount ) {
+ const { clientId } = ownProps;
+ const { replaceInnerBlocks } = dispatch( 'core/block-editor' );
+ const { getBlocks } = registry.select( 'core/block-editor' );
+
+ // Replace the whole block with a new one so that our changes to both the attributes and innerBlocks are atomic
+ // This ensures that the undo history has a single entry, preventing traversing to a 'half way' point where innerBlocks are changed
+ // but the column attributes arent
+ const blockCopy = setDefaultGridLayout( changeNumberOfColumns( getBlocks( clientId ), columnCount ) );
+
+ replaceInnerBlocks( clientId, blockCopy );
+ },
- // If we still need to remove blocks then do them from the beginning before flipping it back round
- return cleanedBlocks
- .slice( Math.max( 0, previous - columns - totalRemoved ) )
- .reverse();
-}
+ setDeviceType( type ) {
+ const { __experimentalSetPreviewDeviceType } = dispatch( 'core/edit-post' );
-export default compose( [
- withDispatch( ( dispatch, ownProps, registry ) => ( {
- /**
- * Update all child Column blocks with a new vertical alignment setting
- * based on whatever alignment is passed in. This allows change to parent
- * to overide anything set on a individual column basis.
- *
- * @param {string} verticalAlignment the vertical alignment setting
- */
- updateAlignment( verticalAlignment ) {
- const { clientId, setAttributes } = ownProps;
- const { updateBlockAttributes } = dispatch( 'core/block-editor' );
- const { getBlockOrder } = registry.select( 'core/block-editor' );
-
- // Update own alignment.
- setAttributes( { verticalAlignment } );
-
- // Update all child Column Blocks to match
- const innerBlockClientIds = getBlockOrder( clientId );
- innerBlockClientIds.forEach( ( innerBlockClientId ) => {
- updateBlockAttributes( innerBlockClientId, {
- verticalAlignment,
- } );
- } );
- },
- updateColumns( previous, columns, columnValues ) {
- const { clientId } = ownProps;
- const { replaceBlock } = dispatch( 'core/block-editor' );
- const { getBlocks } = registry.select( 'core/block-editor' );
- const innerBlocks = getColumnBlocks( getBlocks( clientId ), previous, columns );
-
- // Replace the whole block with a new one so that our changes to both the attributes and innerBlocks are atomic
- // This ensures that the undo history has a single entry, preventing traversing to a 'half way' point where innerBlocks are changed
- // but the column attributes arent
- const blockCopy = createBlock( ownProps.name, {
- ...ownProps.attributes,
- ...columnValues,
- className: removeGridClasses( ownProps.attributes.className ),
- }, innerBlocks );
-
- replaceBlock( clientId, blockCopy );
- },
- setDeviceType( type ) {
- const {
- __experimentalSetPreviewDeviceType,
- } = dispatch( 'core/edit-post' );
-
- __experimentalSetPreviewDeviceType( type );
- }
- } ) ),
+ __experimentalSetPreviewDeviceType( type );
+ },
+ } )
+ ),
withSelect( ( select, { clientId } ) => {
- const { getBlockOrder, getBlockCount, getBlocksByClientId } = select(
- 'core/block-editor'
- );
+ const { getBlockOrder, getBlockCount, getBlocksByClientId } = select( 'core/block-editor' );
return {
- columns: getBlockCount( clientId ),
+ numberOfColumns: getBlockCount( clientId ),
columnAttributes: getBlockOrder( clientId ).map(
( innerBlockClientId ) => getBlocksByClientId( innerBlockClientId )[ 0 ].attributes
),
diff --git a/blocks/layout-grid/src/grid/grid-adjust/adjust.js b/blocks/layout-grid/src/grid/grid-adjust/adjust.js
new file mode 100644
index 00000000..41f5f719
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-adjust/adjust.js
@@ -0,0 +1,113 @@
+/**
+ * Internal dependencies
+ */
+
+/** @typedef {import('../../constants.js').Column} Column */
+/** @typedef {import('../../constants.js').ColumnAdjustment} ColumnAdjustment */
+
+/**
+ * Adjustment of span, start, or offset for a column. Not all values need be present. If both `start` and `offset` are present then `start` is used.
+ *
+ * @typedef GridAdjustment
+ * @type
+ * @property {number} [span]
+ * @property {number} [offset]
+ * @property {number} [start]
+ */
+
+/**
+ * Shrink the span of a column
+ *
+ * @param {Column} column
+ * @param {number} columnNumber
+ * @param {number} diff
+ * @returns {ColumnAdjustment}
+ */
+function shrinkColumn( column, columnNumber, diff ) {
+ return {
+ column: columnNumber,
+ start: column.start,
+ span: column.span - diff,
+ };
+}
+
+/**
+ * Shrink a column while moving it by the same amount
+ *
+ * @param {Column} column
+ * @param {number} columnNumber
+ * @param {number} diff
+ * @returns {ColumnAdjustment}
+ */
+function shrinkAndPushColumn( column, columnNumber, diff ) {
+ return {
+ column: columnNumber,
+ start: column.start + diff,
+ span: column.span - diff,
+ };
+}
+
+/**
+ * Get the adjusted start value, either by using an offset (from the block inspector) or a start (from resizing). Return the current start if neither adjustment is present
+ *
+ * @param {GridAdjustment} adjustment - The change in span and start/offset
+ * @param {Column[]} grid - Grid columns
+ * @param {number} columnNumber - The column being adjusted
+ * @param {number} currentStart - Current start
+ */
+function getAdjustedStart( adjustment, grid, columnNumber, currentStart ) {
+ if ( typeof adjustment.offset !== 'undefined' ) {
+ const lastColumn = columnNumber > 0 ? grid[ columnNumber - 1 ] : null;
+
+ return lastColumn ? lastColumn.start + lastColumn.span + adjustment.offset : adjustment.offset;
+ }
+
+ if ( typeof adjustment.start !== 'undefined' ) {
+ return adjustment.start;
+ }
+
+ return currentStart;
+}
+
+/**
+ * Returns a set of grid adjustments that result from a single column being adjusted. Multiple columns may be affected. Includes original adjustment
+ *
+ * @param {Column[]} grid - Grid columns
+ * @param {GridAdjustment} adjustment - The change in span and start/offset
+ * @param {number} columnNumber - The column being adjusted
+ * @returns {ColumnAdjustment[]}
+ */
+export default function getGridAdjustments( grid, adjustment, columnNumber ) {
+ const { span: currentSpan, start: currentStart, row } = grid[ columnNumber ];
+ const adjustedSpan = adjustment.span || currentSpan;
+ const adjustedStart = getAdjustedStart( adjustment, grid, columnNumber, currentStart );
+ const changes = [ { column: columnNumber, start: adjustedStart, span: adjustedSpan } ];
+
+ // Has anything changed? Return no adjustments if not
+ if ( adjustedSpan === currentSpan && adjustedStart === currentStart ) {
+ return [];
+ }
+
+ // Start position has moved left - we may need to adjust the span of the previous column (if it's on the same row)
+ if ( adjustedStart < currentStart && columnNumber > 0 ) {
+ const previousEnd = grid[ columnNumber - 1 ].start + grid[ columnNumber - 1 ].span;
+
+ // Overlapping the previous column on the same row? Squash it down
+ if ( adjustedStart < previousEnd && grid[ columnNumber - 1 ].row === row ) {
+ changes.push( shrinkColumn( grid[ columnNumber - 1 ], columnNumber - 1, currentStart - adjustedStart ) );
+ }
+ }
+
+ // Span has increased - we need to adjust the start and span of the next column (if it's on the same row)
+ if ( adjustedSpan > currentSpan && columnNumber < grid.length - 1 ) {
+ const newEnd = adjustedStart + adjustedSpan;
+ const diff = grid[ columnNumber + 1 ].start - newEnd;
+
+ // Is this overlapping the next column? Shrink and move it up
+ if ( diff < 0 && grid[ columnNumber + 1 ].row === row ) {
+ changes.push( shrinkAndPushColumn( grid[ columnNumber + 1 ], columnNumber + 1, Math.abs( diff ) ) );
+ }
+ }
+
+ return changes;
+}
diff --git a/blocks/layout-grid/src/grid/grid-adjust/index.js b/blocks/layout-grid/src/grid/grid-adjust/index.js
new file mode 100644
index 00000000..aec5466e
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-adjust/index.js
@@ -0,0 +1,2 @@
+export { default as getGridAdjustments } from './adjust';
+export { default as validateGridAdjustments } from './validate';
diff --git a/blocks/layout-grid/src/grid/grid-adjust/validate.js b/blocks/layout-grid/src/grid/grid-adjust/validate.js
new file mode 100644
index 00000000..764bb4bd
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-adjust/validate.js
@@ -0,0 +1,66 @@
+/** @typedef {import('../../constants.js').Column} Column */
+/** @typedef {import('../../constants.js').ColumnAdjustment} ColumnAdjustment */
+
+/**
+ * Get the column values, including any adjustments
+ *
+ * @param {Column} existing - Existing column data
+ * @param {ColumnAdjustment|null} adjustmentsWithColumn - Adjustment for column, if any
+ */
+function getAdjustedColumn( existing, adjustmentsWithColumn ) {
+ if ( adjustmentsWithColumn ) {
+ return {
+ start: typeof adjustmentsWithColumn.start !== 'undefined' ? adjustmentsWithColumn.start : existing.start,
+ span: typeof adjustmentsWithColumn.span !== 'undefined' ? adjustmentsWithColumn.span : existing.span,
+ row: existing.row,
+ };
+ }
+
+ return existing;
+}
+
+const findColumn = ( adjustments, colNumber ) => adjustments.find( ( adjustment ) => adjustment.column === colNumber );
+
+/**
+ * Determine if an adjusted grid is valid:
+ * - No columns overlaps
+ * - Each column has a minimum span of 1
+ * - Each column has a start and end that fits within the grid
+ *
+ * @param {Column[]} grid - Grid columns
+ * @param {ColumnAdjustment[]} adjustments - Adjustments
+ * @param {number} gridWidth - Width of the grid for the current device
+ * @returns {boolean}
+ **/
+export default function validateGridAdjustments( grid, adjustments, gridWidth ) {
+ for ( let pos = 0; pos < grid.length; pos++ ) {
+ // Get column detail with any adjustments applied
+ const { start, span, row } = getAdjustedColumn( grid[ pos ], findColumn( adjustments, pos ) );
+
+ // Minimum size
+ if ( span < 1 ) {
+ return false;
+ }
+
+ // Start fits into the grid
+ if ( start < 0 || start > gridWidth ) {
+ return false;
+ }
+
+ // End fits into the grid
+ if ( start + span > gridWidth ) {
+ return false;
+ }
+
+ // Does it overlap with the end of last column and we're on the same row?
+ if ( pos > 0 ) {
+ const lastColumn = getAdjustedColumn( grid[ pos - 1 ], findColumn( adjustments, pos - 1 ) );
+
+ if ( lastColumn.row === row && start < lastColumn.start + lastColumn.span ) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/blocks/layout-grid/src/grid/grid-defaults.js b/blocks/layout-grid/src/grid/grid-defaults.js
deleted file mode 100644
index 59aa2101..00000000
--- a/blocks/layout-grid/src/grid/grid-defaults.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Internal dependencies
- */
-
-import { DEVICE_MOBILE, DEVICE_TABLET } from '../constants';
-
-export const getGridWidth = device => {
- if ( device === DEVICE_TABLET ) {
- return 8;
- } else if ( device === DEVICE_MOBILE ) {
- return 4;
- }
-
- return 12;
-};
-
-export const getGridRows = device => {
- if ( device === DEVICE_TABLET ) {
- return 2;
- } else if ( device === DEVICE_MOBILE ) {
- return 4;
- }
-
- return 1;
-};
-
-export const getGridMax = ( device, columns ) => {
- if ( device === DEVICE_TABLET && columns > 2 ) {
- // 2x2 grid
- return getGridWidth( device ) * 2;
- }
-
- if ( device === DEVICE_MOBILE ) {
- return getGridWidth( device ) * columns;
- }
-
- return getGridWidth( device );
-};
-
-// Default spans to fill the device
-// 1 column: desktop=1x12x1 tablet=1x8x1 mobile=1x4x1
-// 2 column: desktop=2x6x1 tablet=2x4x1 mobile=1x4x2
-// 3 column: desktop=3x4x1 tablet=2x4x1 + 1x8x1 mobile=1x4x3
-// 4 column: desktop=4x3x1 tablet=2x4x2 mobile=1x4x4
-export function getDefaultSpan( device, columns, column ) {
- if ( device === DEVICE_TABLET ) {
- if ( columns === 3 && column === 2 ) {
- return getGridWidth( device );
- } else if ( columns > 1 ) {
- return getGridWidth( device ) / 2;
- }
-
- return getGridWidth( device );
- }
-
- if ( device === DEVICE_MOBILE ) {
- return getGridWidth( device );
- }
-
- return getGridWidth( device ) / columns;
-}
diff --git a/blocks/layout-grid/src/grid/grid-resizer/index.js b/blocks/layout-grid/src/grid/grid-resizer/index.js
new file mode 100644
index 00000000..c5adcdad
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-resizer/index.js
@@ -0,0 +1,151 @@
+/**
+ * External dependencies
+ */
+
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+
+import { useState, useRef } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+
+import ResizeHandle from './resize-handle';
+import Resizer from './resizer';
+import { getAdjustedOffset, getMouseX, getAdjustedTop } from './resize-position';
+
+/** @typedef {import('../../constants.js').Column} Column */
+
+/**
+ * Resize data
+ *
+ * @typedef ResizeData
+ * @type
+ * @property {number} resizingColumn - The column being resized
+ * @property {('left'|'right')} direction - `left` if the left handle is resizing, `right` if the right
+ * @property {number} xPos - X position of the drag handle
+ * @property {number} height - Height of the drag handle
+ * @property {number} width - Wdith of the drag handle
+ * @property {number} top - Top offset of the drag handle
+ * @property {number} max - Maximum xPos
+ */
+
+/**
+ * @callback OnResize - Called when the grid is resized.
+ * @param {number} columnNumber - The column that has changed
+ * @param {Column} adjustments - Adjustments
+ */
+
+/**
+ * Get's the column number by looking at the position of the element within the DOM
+ *
+ * @param {Node} element - Element to look at
+ * @returns {number}
+ */
+function getColumnNumber( element ) {
+ let pos = 0;
+
+ while ( element.previousSibling !== null ) {
+ element = element.previousSibling;
+ pos++;
+ }
+
+ return pos;
+}
+
+/**
+ * Get all the details from the initial mouse/touch event:
+ * - column being changed
+ * - which direction
+ * - start position
+ * - height and width of the column
+ *
+ * @param {object} ev - The event that triggered the resize
+ * @param {object} containerRef - Reference to the resize container
+ * @returns {ResizeData}
+ */
+function getStartOfResize( ev, containerRef ) {
+ const { target } = ev;
+ const block = target.closest( '.wp-block' );
+ const { height, right, left, top } = block.getBoundingClientRect();
+ const { width } = target.getBoundingClientRect();
+ const isLeft = target.dataset.resizeLeft;
+
+ return {
+ resizingColumn: getColumnNumber( block ),
+ direction: isLeft ? 'left' : 'right',
+ xPos: getAdjustedOffset( containerRef, getMouseX( ev ), width ),
+ height,
+ width,
+ top: getAdjustedTop( top, containerRef ),
+ max: isLeft ? getAdjustedOffset( containerRef, right, width ) : getAdjustedOffset( containerRef, left, width ),
+ };
+}
+
+/**
+ * The ResizeGrid is responsible for providing resizable grid column handles. It maps absolute mouse positions to grid columns.
+ *
+ * Due to the way the Gutenberg DOM is laid out, the ResizeGrid doesn't provide the resize handles that surround a column. Instead it
+ * listens for mousedown events and when one happens it then displays a 'fake' resize handle that can be dragged. As the fake handle is
+ * moved, the underlying grid is updated, giving the appearance it is being directly updated.
+ *
+ * @param {object} props - Component props
+ * @param {string} props.className - Class name
+ * @param {boolean} props.isSelected - Is the block selected?
+ * @param {number} props.totalColumns - Number of columns for the selected device
+ * @param {Column[]} props.columns - All columns for the selected device
+ * @param {object} props.children - Child components
+ * @param {OnResize} props.onResize - Resize callback when a change to columns is made
+ */
+function ResizeGrid( props ) {
+ const { className, children, isSelected, onResize, totalColumns, columns } = props;
+ const [ resizing, setResizing ] = useState( null );
+ const containerRef = useRef();
+ const classes = classnames( className, { 'wp-block-jetpack-layout-grid__resizing': resizing !== null } );
+
+ function startResize( ev ) {
+ const { target } = ev;
+
+ if ( ( ev.button === 0 || ev.touches ) && ( target.dataset.resizeRight || target.dataset.resizeLeft ) ) {
+ setResizing( getStartOfResize( ev, containerRef ) );
+
+ if ( ev.button === 0 ) {
+ ev.preventDefault();
+ }
+
+ ev.stopPropagation();
+ }
+ }
+
+ return (
+
+ { resizing && (
+ setResizing( null ) }
+ totalColumns={ totalColumns }
+ columns={ columns }
+ container={ containerRef }
+ renderHandle={ ( resize ) => (
+
+ ) }
+ />
+ ) }
+
+ { children }
+
+ );
+}
+
+export default ResizeGrid;
diff --git a/blocks/layout-grid/src/grid/resize-grid/nearest.js b/blocks/layout-grid/src/grid/grid-resizer/nearest.js
similarity index 75%
rename from blocks/layout-grid/src/grid/resize-grid/nearest.js
rename to blocks/layout-grid/src/grid/grid-resizer/nearest.js
index fafc54bd..6a4db1e7 100644
--- a/blocks/layout-grid/src/grid/resize-grid/nearest.js
+++ b/blocks/layout-grid/src/grid/grid-resizer/nearest.js
@@ -1,5 +1,11 @@
-/*
+/**
* Returns the 0-based column that the mouse is closest to
+ *
+ * @param {Element} parent - Parent node
+ * @param {number} xPos - X position
+ * @param {('left'|'right')} direction - `left` if the left handle is resizing, `right` if the right
+ * @param {number} totalColumns - Number of columns for the selected device
+ * @returns {number}
*/
export default function findNearest( parent, xPos, direction, totalColumns ) {
// Each column is 1/12th the width of the parent
diff --git a/blocks/layout-grid/src/grid/resize-grid/resize-handle.js b/blocks/layout-grid/src/grid/grid-resizer/resize-handle.js
similarity index 100%
rename from blocks/layout-grid/src/grid/resize-grid/resize-handle.js
rename to blocks/layout-grid/src/grid/grid-resizer/resize-handle.js
diff --git a/blocks/layout-grid/src/grid/grid-resizer/resize-position.js b/blocks/layout-grid/src/grid/grid-resizer/resize-position.js
new file mode 100644
index 00000000..e4bfde72
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-resizer/resize-position.js
@@ -0,0 +1,34 @@
+/**
+ * Adjust an offset by the grid top
+ *
+ * @param {number} offset
+ * @param {object} containerRef
+ * @returns {number}
+ */
+export function getAdjustedTop( offset, containerRef ) {
+ return offset - containerRef.current.getBoundingClientRect().top;
+}
+
+/**
+ * Get an X client position from the mouse or touch
+ *
+ * @param {object} event
+ * @returns {number}
+ */
+export function getMouseX( event ) {
+ const { clientX, targetTouches } = event;
+
+ return clientX || ( targetTouches && targetTouches[ 0 ].clientX );
+}
+
+/**
+ * Adjust an X position by the container and handle
+ *
+ * @param {object} containerRef
+ * @param {number} offset
+ * @param {number} currentWidth
+ * @returns {number}
+ */
+export function getAdjustedOffset( containerRef, offset, currentWidth ) {
+ return offset - containerRef.current.getBoundingClientRect().left - currentWidth / 2;
+}
diff --git a/blocks/layout-grid/src/grid/grid-resizer/resizer.js b/blocks/layout-grid/src/grid/grid-resizer/resizer.js
new file mode 100644
index 00000000..f7848292
--- /dev/null
+++ b/blocks/layout-grid/src/grid/grid-resizer/resizer.js
@@ -0,0 +1,149 @@
+/**
+ * WordPress dependencies
+ */
+
+import { useState, useEffect } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { getStart } from '../grid-adjust';
+import { getAdjustedOffset, getMouseX } from './resize-position';
+import findNearest from './nearest';
+
+/** @typedef {import('./index.js').OnResize} OnResize */
+/** @typedef {import('./index.js').ResizeData} ResizeData */
+/** @typedef {import('../../constants.js').Column} Column */
+
+/**
+ * Render a resize handle
+ * @callback RenderHandler
+ * @param {ResizeData} resize
+ */
+
+/**
+ * @callback StopResizing
+ */
+
+/**
+ * Get the adjustment applied to the current column to transform to the current mouse/touch position
+ *
+ * @param {ResizeData} resize - Resize data
+ * @param {object} containerRef - Grid container
+ * @param {('left'|'right')} direction - Direction the adjustment is going
+ * @param {number} totalColumns - Number of columns
+ * @param {Column[]} columns - All columns for the selected device
+ * @param {MouseEventInit} mouse - Mouse event
+ */
+function getAdjustmentForMove( resize, containerRef, direction, totalColumns, columns, mouse ) {
+ const { start, span } = columns[ resize.resizingColumn ];
+ const nearest = Math.min(
+ totalColumns,
+ Math.max( 0, findNearest( containerRef.current, getMouseX( mouse ), direction, totalColumns ) )
+ );
+
+ if ( direction === 'left' ) {
+ // Moving the left edge of a column, so we affect both span and start
+ if ( nearest === start ) {
+ // No change
+ return null;
+ }
+
+ // We're changing the start position - adjust the span
+ const diff = Math.abs( nearest - start );
+ const adjustment = {
+ start: nearest,
+ span: nearest > start ? span - diff : span + diff,
+ };
+
+ // Check we don't go beyond the end
+ if ( adjustment.start >= start + span ) {
+ return null;
+ }
+
+ // Minimum span of 1
+ adjustment.span = Math.max( 1, adjustment.span );
+ return adjustment;
+ }
+
+ // Moving the right edge of a column so we only affect the span
+ return {
+ span: Math.max( 1, nearest - start ),
+ };
+}
+
+/**
+ * Adjust an x position based on the limits of the grid
+ *
+ * @param {ResizeData} resize - Resize data
+ * @param {number} offset - X position
+ * @returns {number} - X position that fits within the limits
+ */
+function getRestrictedOffset( resize, offset ) {
+ const { direction, max, width } = resize;
+
+ // Ensure we dont go beyond or before the end of the other side of our block
+ if ( direction === 'left' ) {
+ return Math.min( max - width, offset );
+ }
+
+ return Math.max( max + width, offset );
+}
+
+/**
+ * A resizer component that listens to mouse/touch drag events in a grid container and issues `onResize` adjustments.
+ * The component should only be created when a drag event has started
+ *
+ * @param {object} props - Component props
+ * @param {number} props.totalColumns - Total number of columns for a row in the device
+ * @param {OnResize} props.onResize - Resize callback when an adjustment is made
+ * @param {RenderHandler} props.renderHandle - Render a drag handle
+ * @param {StopResizing} props.onStopResizing - Callback when the resize has finished
+ * @param {ResizeData} props.resize - Initial resize data
+ * @param {object} props.container - Node reference to resize container
+ * @param {Column[]} props.columns - All columns for the selected device
+ */
+export default function Resizer( props ) {
+ const { onStopResizing, renderHandle, totalColumns, columns, onResize, container } = props;
+ const [ resize, setResize ] = useState( props.resize );
+
+ function onMouseMove( ev ) {
+ const { height, width, direction, resizingColumn } = resize;
+
+ ev.stopPropagation(); // don't pass this anywhere else
+ if ( ev.touches === undefined ) {
+ ev.preventDefault(); // stop browser drag
+ }
+
+ // Update the x position
+ setResize( {
+ ...resize,
+ xPos: getRestrictedOffset( resize, getAdjustedOffset( container, getMouseX( ev ), width ) ),
+ height,
+ } );
+
+ // Finally pass this up if a grid adjustment has been triggered
+ const adjustment = getAdjustmentForMove( resize, container, direction, totalColumns, columns, ev );
+ if ( adjustment ) {
+ onResize( resizingColumn, adjustment );
+ }
+ }
+
+ useEffect( () => {
+ document.addEventListener( 'touchmove', onMouseMove );
+ document.addEventListener( 'mousemove', onMouseMove );
+
+ document.addEventListener( 'touchend', onStopResizing );
+ document.addEventListener( 'mouseup', onStopResizing );
+
+ return () => {
+ document.removeEventListener( 'touchmove', onMouseMove );
+ document.removeEventListener( 'mousemove', onMouseMove );
+
+ document.removeEventListener( 'touchend', onStopResizing );
+ document.removeEventListener( 'mouseup', onStopResizing );
+ };
+ }, [] );
+
+ return renderHandle( resize );
+}
diff --git a/blocks/layout-grid/src/grid/inspector/column-settings.js b/blocks/layout-grid/src/grid/inspector/column-settings.js
new file mode 100644
index 00000000..035610e1
--- /dev/null
+++ b/blocks/layout-grid/src/grid/inspector/column-settings.js
@@ -0,0 +1,97 @@
+/**
+ * External dependencies
+ */
+
+import { times } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+
+import { TextControl } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { getGridWidth } from '../../grid-values';
+
+/** @typedef {import('../../constants.js').Device} Device */
+/** @typedef {import('../../constants.js').Column} Column */
+/** @typedef {import('../../constants.js').GetColumn} GetColumn */
+/** @typedef {import('../../constants.js').ChangeColumnCallback} ChangeColumnCallback */
+
+function convertStartToOffset( device, columnCount, getColumn ) {
+ const offsets = [];
+ let position = 0;
+ let lastRow = 0;
+
+ for ( let index = 0; index < columnCount; index++ ) {
+ const { start, span, row } = getColumn( device, index );
+
+ if ( lastRow !== row ) {
+ position = 0;
+ }
+
+ offsets.push( start - position );
+ position = start + span;
+ }
+
+ return offsets;
+}
+
+/**
+ * Inspector settings for a column in the Layout Grid
+ *
+ * @param {object} props - Component props
+ * @param {number} props.columnCount - Current number of columns
+ * @param {Device} props.device - Current device
+ * @param {GetColumn} props.getColumn - Get details for a column
+ * @param {ChangeColumnCallback} props.onChangeColumn - Callback to change the offset of a column
+ */
+function ColumnInspectorSettings( props ) {
+ const { columnCount, device, getColumn, onChangeColumn } = props;
+ const offsets = convertStartToOffset( device, columnCount, getColumn );
+
+ return (
+ <>
+ { times( columnCount, ( column ) => (
+
+
+ { __( 'Column', 'layout-grid' ) } { column + 1 }
+
+
+
+
+ onChangeColumn( device, column, {
+ offset: parseInt( value, 10 ),
+ } )
+ }
+ />
+
+
+ onChangeColumn( device, column, {
+ span: parseInt( value, 10 ),
+ } )
+ }
+ />
+
+
+ ) ) }
+ >
+ );
+}
+
+export default ColumnInspectorSettings;
diff --git a/blocks/layout-grid/src/grid/inspector/column.js b/blocks/layout-grid/src/grid/inspector/column.js
new file mode 100644
index 00000000..097ca923
--- /dev/null
+++ b/blocks/layout-grid/src/grid/inspector/column.js
@@ -0,0 +1,57 @@
+/**
+ * External dependencies
+ */
+
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+
+import { ENTER, SPACE } from '@wordpress/keycodes';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import ColumnIcon from '../../icons';
+
+/** @typedef {import('../../constants.js').ColumnDescription} ColumnDescription */
+/** @typedef {import('./index.js').ChangeNumColumnsCallback} ChangeNumColumnsCallback */
+
+/**
+ * A column in the Layout Grid inspector
+ *
+ * @param {object} props - Component props
+ * @param {number} props.columnCount - Total number of columns
+ * @param {ColumnDescription} props.column - Current column
+ * @param {ChangeNumColumnsCallback} props.changeNumberOfColumns - Change the number of columns
+ */
+function InsectorColumn( { column, columnCount, changeNumberOfColumns } ) {
+ return (
+ changeNumberOfColumns( column.value ) }
+ onKeyDown={ ( event ) => {
+ if ( ENTER === event.keyCode || SPACE === event.keyCode ) {
+ event.preventDefault();
+ changeNumberOfColumns( column.value );
+ }
+ } }
+ role="button"
+ tabIndex={ 0 }
+ aria-label={ column.label }
+ >
+
+
+
+
+ { column.label }
+
+
+ );
+}
+
+export default InsectorColumn;
diff --git a/blocks/layout-grid/src/grid/inspector/index.js b/blocks/layout-grid/src/grid/inspector/index.js
new file mode 100644
index 00000000..cab5dcaf
--- /dev/null
+++ b/blocks/layout-grid/src/grid/inspector/index.js
@@ -0,0 +1,163 @@
+/**
+ * WordPress dependencies
+ */
+
+import { InspectorControls } from '@wordpress/block-editor';
+import {
+ Button,
+ PanelBody,
+ ButtonGroup,
+ SelectControl,
+ Disabled,
+ ToggleControl,
+} from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { getLayouts, getColumns, getGutterValues } from '../../constants';
+import InsectorColumn from './column';
+import ColumnInspectorSettings from './column-settings';
+
+/** @typedef {import('../../constants.js').GetColumn} GetColumn */
+/** @typedef {import('../../constants.js').Device} Device */
+/** @typedef {import('../../constants.js').ChangeColumnCallback} ChangeColumnCallback */
+/** @typedef {import('../edit.js').SetDeviceCallback} SetDeviceCallback */
+
+/**
+ * @callback SetAttributesCallback
+ * @param {object} attributes - Attributes to change
+ */
+
+/**
+ * @callback ChangeNumColumnsCallback
+ * @param {number} numberOfColumns - New number of columns
+ */
+
+/**
+ * Inspector control for the Layout Grid
+ *
+ * @param {object} props - Component props
+ * @param {number} props.columnCount - Current number of columns
+ * @param {Device} props.device - Current device
+ * @param {string} props.gutterSize - Current gutter size
+ * @param {boolean} props.addGutterEnds - Add gutter ends
+ * @param {SetAttributesCallback} props.setAttributes - Set block attributes
+ * @param {ChangeNumColumnsCallback} props.changeNumberOfColumns - Change number of columns
+ * @param {GetColumn} props.getColumn - Get details for a column
+ * @param {ChangeColumnCallback} props.changeColumn - Set details for a column
+ * @param {SetDeviceCallback} props.setDevice - Callback to change the device
+ */
+function LayoutGridInspector( props ) {
+ const {
+ columnCount,
+ device,
+ gutterSize,
+ addGutterEnds,
+ setAttributes,
+ changeNumberOfColumns,
+ setDevice,
+ getColumn,
+ changeColumn,
+ } = props;
+ const toggleControl = (
+
+ setAttributes( { addGutterEnds: newValue } )
+ }
+ />
+ );
+
+ return (
+
+
+
+ { getColumns().map( ( column ) => (
+
+ ) ) }
+
+
+
+
+ { __(
+ 'Changing the number of columns will reset your layout and could remove content.',
+ 'layout-grid'
+ ) }
+
+
+
+
+
+
+
+ { __(
+ "Note that previewing your post will show your browser's breakpoint, not the currently selected one.",
+ 'layout-grid'
+ ) }
+
+
+
+ { getLayouts().map( ( layout ) => (
+ setDevice( layout.value ) }
+ >
+ { layout.label }
+
+ ) ) }
+
+
+
+
+
+
+ { __( 'Gutter size', 'layout-grid' ) }
+
+
+ setAttributes( {
+ gutterSize: newValue,
+ addGutterEnds:
+ newValue === 'none' ? false : addGutterEnds,
+ } )
+ }
+ options={ getGutterValues() }
+ />
+
+ { gutterSize === 'none' ? (
+ { toggleControl }
+ ) : (
+ toggleControl
+ ) }
+
+
+ );
+}
+
+export default LayoutGridInspector;
diff --git a/blocks/layout-grid/src/grid/layout-grid/index.js b/blocks/layout-grid/src/grid/layout-grid/index.js
deleted file mode 100644
index 6c479391..00000000
--- a/blocks/layout-grid/src/grid/layout-grid/index.js
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * Internal dependencies
- */
-
-import {
- getSpanForDevice,
- getOffsetForDevice,
-} from '../../constants';
-import {
- getGridWidth,
- getGridMax,
- getDefaultSpan,
-} from '../grid-defaults';
-
-/**
- * This contains all the grid column layout logic. That is, it knows how to move and resize columns on a grid (including a grid with multiple rows).
- * It does not handle mapping resize handles to grid positions
- */
-class LayoutGrid {
- constructor( attributes, device, columns ) {
- this.attributes = attributes;
- this.device = device;
- this.columnCount = columns;
- }
-
- // Gets a copy of the grid so we can modify it
- getGridValues() {
- const grid = {};
-
- for ( let pos = 0; pos < this.columnCount; pos++ ) {
- const defaultSpan = getDefaultSpan( this.device, this.columnCount, pos );
-
- grid[ getSpanForDevice( pos, this.device ) ] = this.getSpan( pos ) || defaultSpan;
- grid[ getOffsetForDevice( pos, this.device ) ] = this.getOffset( pos );
- }
-
- return grid;
- }
-
- /*
- * Apply a set of adjustments and return a new copy of the grid
- */
- applyAdjustments( adjustments ) {
- let grid = this.getGridValues();
-
- // Run through the adjustments and apply to the grid
- for ( let index = 0; index < adjustments.length; index++ ) {
- grid = { ...grid, ...adjustments[ index ] };
- }
-
- return grid;
- }
-
- getSpanAdjustment( column, value ) {
- return { [ getSpanForDevice( column, this.device ) ]: value }
- }
-
- getAdjustOffset( column, value ) {
- return { [ getOffsetForDevice( column, this.device ) ]: value }
- }
-
- getShrinkOffset( column, remaining ) {
- const spareOffset = this.getOffset( column );
- const offsetUsed = remaining >= spareOffset ? spareOffset : remaining;
-
- return {
- adjustment: this.getAdjustOffset( column, spareOffset - offsetUsed ),
- offsetUsed,
- };
- }
-
- hasOverlaps( positions ) {
- for ( let index = 0; index < positions.length; index++ ) {
- const first = positions[ index ];
-
- for ( let inner = index + 1; inner < positions.length; inner++ ) {
- const second = positions[ inner ];
-
- if ( first.start > second.start && first.start < second.end ) {
- return true;
- }
-
- if ( first.end > second.start && first.end < second.end ) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /*
- * Determine if an adjusted grid is valid. That is, the total of all the spans and offsets does not exceed
- * the maximum allowed by the grid layout, and none of the columns overlap each other
- */
- validateGrid( grid ) {
- const positions = [];
- const rowWidth = getGridWidth( this.device );
- let total = 0, rowTotal = 0;
-
- for ( let pos = 0; pos < this.columnCount; pos++ ) {
- const span = grid[ getSpanForDevice( pos, this.device ) ];
- const offset = grid[ getOffsetForDevice( pos, this.device ) ];
-
- rowTotal += offset;
- if ( rowTotal >= rowWidth ) {
- rowTotal = rowTotal - rowWidth;
- }
-
- rowTotal += span;
-
- // Does this row exceed the limit?
- if ( rowTotal > rowWidth ) {
- return false;
- }
-
- positions.push( { start: total + offset, end: total + offset + span } );
-
- total += span + offset;
- }
-
- // Does it fit within the grid?
- if ( total > getGridMax( this.device, this.columnCount ) ) {
- return false;
- }
-
- // Do we have any overlaps?
- if ( this.hasOverlaps( positions ) ) {
- return false;
- }
-
- return true;
- }
-
- /*
- * The end of a column was adjusted. Absorb/increase any offsets in subsequent columns so they remain in place
- * Returns an array of column adjustments
- */
- getEndAdjustments( column, diff ) {
- const changes = [];
-
- if ( diff < 0 ) {
- // Column end has moved left - add extra offset to the next column to keep it in place
- const adjustment = this.getAdjustOffset( column, this.getOffset( column ) + Math.abs( diff ) );
-
- return [ adjustment ];
- }
-
- if ( diff > 0 ) {
- // Column end has moved right - eat any offset space after the column
- for ( let index = column; index < this.columnCount && diff > 0; index++ ) {
- const adjust = this.getShrinkOffset( index, Math.abs( diff ) );
-
- changes.push( adjust.adjustment );
- diff -= adjust.offsetUsed;
- }
- }
-
- return changes;
- }
-
- getStartMovedLeft( column, diff ) {
- const changes = [];
-
- // Column start has moved left - eat any offset space before the column, including the column's own offset
- for ( let index = column; index >= 0 && diff > 0; index-- ) {
- const adjust = this.getShrinkOffset( index, diff );
-
- changes.push( adjust.adjustment );
- diff -= adjust.offsetUsed;
- }
-
- return changes;
- }
-
- /*
- * The start of a column was adjusted. Make adjustments to other columns so everything remains in place
- * Returns an array of column adjustments
- */
- getStartAdjustments( column, newStart ) {
- const currentOffset = this.getOffset( column );
- const newOffset = this.getOffsetFromStart( column, newStart );
- const diff = newOffset - currentOffset;
-
- if ( diff < 0 ) {
- return this.getStartMovedLeft( column, Math.abs( diff ) );
- }
-
- return [ this.getAdjustOffset( column, newOffset ) ];
- }
-
- /*
- * Get span for a column
- */
- getSpan( column ) {
- return this.attributes[ getSpanForDevice( column, this.device ) ]
- }
-
- /*
- * Get offset from previous column
- */
- getOffset( column ) {
- return this.attributes[ getOffsetForDevice( column, this.device ) ]
- }
-
- /*
- * Get absolute start value for a column from the start of the row
- */
- getStart( column ) {
- let start = 0;
-
- // Add all the offset and spans of previous columns
- for ( let pos = 0; pos < column; pos++ ) {
- start += this.getSpan( pos ) + this.getOffset( pos );
- }
-
- const rows = Math.max( 1, ( Math.floor( start / getGridWidth( this.device ) ) ) );
-
- // Finally add the offset of our column to take us to the start of it, and then make it relative to the start of the row
- return ( start + this.getOffset( column ) ) % ( rows * getGridWidth( this.device ) );
- }
-
- /*
- * Opposite of getStart() - returns an offset from the previous column when given a new start position
- */
- getOffsetFromStart( column, start ) {
- if ( column === 0 ) {
- // Simple - the first column offset is the start position
- return start;
- }
-
- const currentStart = this.getStart( column ); // This is the current start
- const diff = start - currentStart;
-
- return this.getOffset( column ) + diff;
- }
-
- convertOffsetToStart( column, offset ) {
- const start = this.getStart( column );
- const diff = offset - this.getOffset( column );
-
- return start + diff;
- }
-
- /*
- * Returns the layout grid, with an adjustment made. If no adjustment is made then returns null
- */
- getAdjustedGrid( column, adjustment ) {
- const {
- start = this.getStart( column ),
- span = this.getSpan( column ),
- } = adjustment;
-
- // Get an array of adjustments so we can then check the grid is still valid before committing
- let adjustments = [];
-
- if ( start !== this.getStart( column ) && span !== this.getSpan( column ) ) {
- // Both start and span have changed
- adjustments = adjustments.concat( this.getStartAdjustments( column, start ) );
- adjustments = adjustments.concat( this.getSpanAdjustment( column, span ) );
- } else if ( span !== this.getSpan( column ) ) {
- // Only span has changed. Adjust that, and any columns that come after this
- adjustments = adjustments.concat( this.getSpanAdjustment( column, span ) );
- adjustments = adjustments.concat( this.getEndAdjustments( column + 1, span - this.getSpan( column ) ) );
- } else if ( start !== this.getStart( column ) ) {
- // Only the start has changed. Adjust that, and ensure subsequent columns dont move
- adjustments = adjustments.concat( this.getStartAdjustments( column, start ) );
- adjustments = adjustments.concat( this.getEndAdjustments( column + 1, start - this.getStart( column ) ) );
- }
-
- // Convert the array of adjustments to a new set of attributes
- const adjustedGrid = this.applyAdjustments( adjustments );
-
- // Now check everything still fits. If it doesnt then we ignore the entire change
- if ( adjustments.length > 0 && this.validateGrid( adjustedGrid ) ) {
- // Update all the values at once
- return adjustedGrid;
- }
-
- return null;
- }
-}
-
-export default LayoutGrid;
diff --git a/blocks/layout-grid/src/grid/placeholder/index.js b/blocks/layout-grid/src/grid/placeholder/index.js
new file mode 100644
index 00000000..296a2d24
--- /dev/null
+++ b/blocks/layout-grid/src/grid/placeholder/index.js
@@ -0,0 +1,54 @@
+/**
+ * WordPress dependencies
+ */
+
+import { Button, Placeholder } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+
+import { getColumns } from '../../constants';
+import ColumnIcon from '../../icons';
+
+/** @typedef {import('../inspector/index.js').ChangeNumColumnsCallback} ChangeNumColumnsCallback */
+
+/**
+ * Layout grid placeholder
+ *
+ * @param {object} props - Component props
+ * @param {string} props.className - Class for the placeholder
+ * @param {ChangeNumColumnsCallback} props.changeNumberOfColumns - Change the number of columns
+ */
+function LayoutGridPlaceholder( props ) {
+ const { className, changeNumberOfColumns } = props;
+
+ return (
+
+
+ { getColumns().map( ( column ) => (
+
+ }
+ onClick={ () => changeNumberOfColumns( column.value ) }
+ className="block-editor-inner-blocks__template-picker-option"
+ label={ column.label }
+ isSecondary
+ />
+
+ ) ) }
+
+
+ );
+}
+
+export default LayoutGridPlaceholder;
diff --git a/blocks/layout-grid/src/grid/resize-grid/index.js b/blocks/layout-grid/src/grid/resize-grid/index.js
deleted file mode 100644
index 301567c4..00000000
--- a/blocks/layout-grid/src/grid/resize-grid/index.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * External dependencies
- */
-
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-
-import { Component, createRef } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-
-import findNearest from './nearest';
-import ResizeHandle from './resize-handle';
-
-/*
- * The ResizeGrid is responsible for providing resizable grid column handles. It maps absolute mouse positions to grid columns, and then
- * validates that with the LayoutGrid.
- *
- * Due to the way the Gutenberg DOM is laid out, the ResizeGrid doesn't provide the resize handles that surround a column. Instead it
- * listens for mousedown events and when one happens it then displays a 'fake' resize handle that can be dragged. As the fake handle is
- * moved, the underlying grid is updated, giving the appearance it is being directly updated.
- */
-class ResizeGrid extends Component {
- constructor( props ) {
- super( props );
-
- this.containerRef = createRef();
- this.state = {
- resizingColumn: -1,
- xPos: 0,
- height: 0,
- };
- }
-
- getNearestColumn( direction, mouse ) {
- const { totalColumns, layoutGrid } = this.props;
- const start = layoutGrid.getStart( this.state.resizingColumn );
- const span = layoutGrid.getSpan( this.state.resizingColumn );
- const nearest = Math.min( totalColumns, Math.max( 0, findNearest( this.containerRef.current, this.getMouseX( mouse ), direction, totalColumns ) ) );
-
- if ( direction === 'left' ) {
- if ( nearest === start ) {
- // No change
- return null;
- }
-
- // We're changing the start position - adjust the span
- const diff = Math.abs( nearest - start );
- const adjustment = {
- start: nearest,
- span: nearest > start ? span - diff : span + diff,
- direction,
- };
-
- // Check we don't go beyond the end
- if ( adjustment.start >= start + span ) {
- return null;
- }
-
- // Minimum span of 1
- adjustment.span = Math.max( 1, adjustment.span );
- return adjustment;
- }
-
- // We're changing the span
- // New span is from the new position minus the current start
- return {
- span: Math.max( 1, nearest - start ),
- direction,
- };
- }
-
- getMouseX( event ) {
- const { clientX, targetTouches } = event;
-
- return clientX || ( targetTouches && targetTouches[ 0 ].clientX );
- }
-
- getAdjustedOffset( offset, optionalWidth = 0 ) {
- const { width } = this.state;
- const handleWidth = optionalWidth > 0 ? optionalWidth : width;
-
- return offset - this.containerRef.current.getBoundingClientRect().left - ( ( handleWidth ) / 2 );
- }
-
- getAdjustedTop( offset ) {
- return offset - this.containerRef.current.getBoundingClientRect().top;
- }
-
- getRestrictedOffset( offset ) {
- const { direction, max, width } = this.state;
-
- // Ensure we dont go beyond or before the end of the other side of our block
- if ( direction === 'left' ) {
- return Math.min( max - width, offset );
- }
-
- return Math.max( max + width, offset );
- }
-
- getChildPosition( element ) {
- let pos = 0;
-
- while ( element.previousSibling !== null ) {
- element = element.previousSibling;
- pos++;
- }
-
- return pos;
- }
-
- onMouseDown = ev => {
- const { target } = ev;
-
- // This is a bit of hardcoded DOM searching - we check if the current click is on a resize handle and then find the column associated with that
- // There may be a better way.
- if ( ( ev.button === 0 || ev.touches ) && ( target.dataset.resizeRight || target.dataset.resizeLeft ) ) {
- this.block = target.closest( '.wp-block' );
-
- const { height, right, left, top } = this.block.getBoundingClientRect();
- const { width } = target.getBoundingClientRect();
- const pos = this.getChildPosition( this.block );
- const isLeft = target.dataset.resizeLeft;
-
- this.setState( {
- resizingColumn: pos,
- xPos: this.getAdjustedOffset( this.getMouseX( ev ), width ),
- height,
- width,
- top: this.getAdjustedTop( top ),
- direction: isLeft ? 'left' : 'right',
- max: isLeft ? this.getAdjustedOffset( right, width ) : this.getAdjustedOffset( left, width ),
- } );
-
- if ( ev.button === 0 ) {
- document.addEventListener( 'mousemove', this.onMouseMove );
- document.addEventListener( 'mouseup', this.onMouseUp );
-
- ev.preventDefault();
- } else {
- document.addEventListener( 'touchmove', this.onMouseMove );
- document.addEventListener( 'touchend', this.onMouseUp );
- }
-
- ev.stopPropagation();
- }
- }
-
- onMouseMove = ev => {
- ev.stopPropagation();
-
- if ( ev.touches === undefined ) {
- ev.preventDefault();
- }
-
- const { height } = this.block.getBoundingClientRect();
-
- this.setState( {
- xPos: this.getRestrictedOffset( this.getAdjustedOffset( this.getMouseX( ev ) ) ),
- height,
- } );
-
- // Finally pass this up if a grid adjustment has been triggered
- const adjustment = this.getNearestColumn( this.state.direction, ev );
- if ( adjustment ) {
- this.props.onResize( this.state.resizingColumn, adjustment );
- }
- }
-
- onMouseUp = ev => {
- this.setState( { resizingColumn: -1 } );
-
- document.removeEventListener( 'mousemove', this.onMouseMove );
- document.removeEventListener( 'mouseup', this.onMouseUp );
- document.removeEventListener( 'touchmove', this.onMouseMove );
- document.removeEventListener( 'touchend', this.onMouseUp );
- }
-
- render() {
- const { className, children, isSelected } = this.props;
- const { resizingColumn, xPos, height } = this.state;
- const classes = classnames(
- className,
- resizingColumn !== -1 ? 'wp-block-jetpack-layout-grid__resizing' : null,
- );
-
- return (
-
- { resizingColumn !== -1 && }
- { children }
-
- );
- }
-}
-
-export default ResizeGrid;
diff --git a/blocks/layout-grid/src/grid/save.js b/blocks/layout-grid/src/grid/save.js
index 800ce8bb..3e5466b2 100644
--- a/blocks/layout-grid/src/grid/save.js
+++ b/blocks/layout-grid/src/grid/save.js
@@ -1,9 +1,3 @@
-/**
- * External dependencies
- */
-
-import classnames from 'classnames';
-
/**
* WordPress dependencies
*/
@@ -14,18 +8,18 @@ import { InnerBlocks } from '@wordpress/block-editor';
* Internal dependencies
*/
-import { getAsCSS, removeGridClasses, getGutterClasses } from './css-classname';
+import {
+ getGridVerticalAlignClasses,
+ getGutterClasses,
+ getGridClasses,
+} from '../grid-css';
-const save = ( { attributes, innerBlocks } ) => {
- const {
- className,
- } = attributes;
- const extra = getAsCSS( innerBlocks.length, attributes );
- const classes = classnames(
- removeGridClasses( className ),
- extra,
- getGutterClasses( attributes ),
- );
+export default function save( { attributes } ) {
+ const { className, addGutterEnds, gutterSize, verticalAlignment } = attributes;
+ const classes = getGridClasses( className, {
+ ...getGutterClasses( addGutterEnds, gutterSize ),
+ ...getGridVerticalAlignClasses( verticalAlignment ),
+ } );
return (
@@ -33,5 +27,3 @@ const save = ( { attributes, innerBlocks } ) => {
);
};
-
-export default save;
diff --git a/blocks/layout-grid/src/index.js b/blocks/layout-grid/src/index.js
index 419c2f86..45cdfa91 100644
--- a/blocks/layout-grid/src/index.js
+++ b/blocks/layout-grid/src/index.js
@@ -14,20 +14,7 @@ import saveGrid from './grid/save';
import editColumn from './grid-column/edit';
import saveColumn from './grid-column/save';
import { GridIcon, GridColumnIcon } from './icons';
-import { getSpanForDevice, getOffsetForDevice, DEVICE_BREAKPOINTS, MAX_COLUMNS } from './constants';
-
-function getColumnAttributes( total, breakpoints ) {
- const attributes = {};
-
- for ( let index = 0; index < total; index++ ) {
- breakpoints.map( ( breakpoint ) => {
- attributes[ getSpanForDevice( index, breakpoint ) ] = { type: 'number' };
- attributes[ getOffsetForDevice( index, breakpoint ) ] = { type: 'number', default: 0 };
- } );
- }
-
- return attributes;
-}
+import { DEVICE_BREAKPOINTS } from './constants';
export function registerBlock() {
registerBlockType( 'jetpack/layout-grid', {
@@ -86,7 +73,6 @@ export function registerBlock() {
verticalAlignment: {
type: 'string',
},
- ...getColumnAttributes( MAX_COLUMNS, DEVICE_BREAKPOINTS ),
},
edit: editGrid,
save: saveGrid,
@@ -117,6 +103,21 @@ export function registerBlock() {
verticalAlignment: {
type: 'string',
},
+ ...Object.assign.apply(
+ {},
+ DEVICE_BREAKPOINTS.map( ( breakpoint ) => ( {
+ [ `span${ breakpoint }` ]: {
+ type: 'number',
+ },
+ [ `start${ breakpoint }` ]: {
+ type: 'number',
+ },
+ [ `row${ breakpoint }` ]: {
+ type: 'number',
+ default: 0,
+ },
+ } ) )
+ ),
},
edit: editColumn,
save: saveColumn,
diff --git a/blocks/layout-grid/style.scss b/blocks/layout-grid/style.scss
index 38184c4b..c8cf909c 100644
--- a/blocks/layout-grid/style.scss
+++ b/blocks/layout-grid/style.scss
@@ -170,6 +170,7 @@
* Individual column alignment
*/
.wp-block-jetpack-layout-grid-column {
+ // These only affect the front end. The editor has specific CSS
&.is-vertically-aligned-top {
align-self: flex-start;
}
diff --git a/bundler/resources/jetpack-layout-grid/readme.txt b/bundler/resources/jetpack-layout-grid/readme.txt
index e7bbfd67..aefabd7c 100644
--- a/bundler/resources/jetpack-layout-grid/readme.txt
+++ b/bundler/resources/jetpack-layout-grid/readme.txt
@@ -27,6 +27,9 @@ You can follow development, file an issue, suggest features, and view the source
* Add vertical alignment to grid and grid columns
* Mirror grid device breakpoint with editor preview breakpoint
+= 1.2.2 - 23rd June 2020 =
+* Fix the CSS loading fix from 1.2.1 so it uses wp_register_style
+
= 1.2.1 - 10th June 2020 =
* Fix block inserter to show inside a grid column
* Fix vertical margin in editor so it better matches the display