diff --git a/plugin/assets/css/src/block-editor.css b/plugin/assets/css/src/block-editor.css
index 3635dad7d..ae0018309 100644
--- a/plugin/assets/css/src/block-editor.css
+++ b/plugin/assets/css/src/block-editor.css
@@ -21,6 +21,7 @@
@import "./base/variables.css";
@import "./conf/index.css";
@import "./components/masonry-grid.css";
+@import "./components/card.css";
@import "./overrides.css";
@import "./material-components.css";
diff --git a/plugin/assets/css/src/block/style/masonry.css b/plugin/assets/css/src/block/style/masonry.css
new file mode 100644
index 000000000..408834d4b
--- /dev/null
+++ b/plugin/assets/css/src/block/style/masonry.css
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ * Masonry Grid for query loop with card.
+ */
+.is-style-material-masonry.wp-block-post-template.is-flex-container {
+ grid-gap: var(--mdc-layout-grid-margin-desktop, 24px);
+ grid-template-columns: repeat(auto-fill, minmax(40%, 1fr));
+ grid-auto-rows: var(--mdc-layout-grid-margin-desktop, 24px);
+ margin: var(--mdc-layout-grid-margin-desktop, 24px) 0;
+ padding: var(--mdc-layout-grid-margin-desktop, 24px);
+
+ @media (--medium-viewport) {
+ display: grid;
+ }
+
+ @nest .material-archive__wide & {
+ grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
+ }
+
+ &.columns-3 {
+ grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
+ }
+
+ &.columns-4 {
+ grid-template-columns: repeat(auto-fill, minmax(20%, 1fr));
+ }
+
+ &.columns-5 {
+ grid-template-columns: repeat(auto-fill, minmax(16%, 1fr));
+ }
+
+ &.columns-6 {
+ grid-template-columns: repeat(auto-fill, minmax(14%, 1fr));
+ }
+}
+
+.is-style-material-masonry.wp-block-post-template.is-flex-container > li,
+.is-style-material-masonry.wp-block-query-loop.is-flex-container > li {
+ width: 100% !important;
+}
+
+.entry-content h2.post-card__title {
+ margin: revert;
+}
diff --git a/plugin/assets/css/src/components/card.css b/plugin/assets/css/src/components/card.css
new file mode 100644
index 000000000..22c2d6809
--- /dev/null
+++ b/plugin/assets/css/src/components/card.css
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.post-card {
+
+ &.sticky .post-card__title {
+ align-items: center;
+ display: flex;
+ }
+
+ &.sticky .post-card__title .material-icons {
+ margin-right: 5px;
+ }
+
+ & .post-card__title a {
+ text-decoration: none;
+ }
+
+ & a:focus {
+ outline: 1px dotted;
+ }
+
+ .mdc-card__action--button {
+ text-transform: none;
+ }
+
+ .mdc-card__action-buttons .mdc-button__icon {
+ color: var(--mdc-theme-primary, #6200ee);
+ }
+
+ .post-card__primary,
+ .post-card__secondary {
+ padding-left: 1rem;
+ padding-right: 1rem;
+ }
+
+ .post-card__subtitle {
+ color: rgba(var(--mdc-theme-on-surface-rgb, 0, 0, 0), .54);
+ }
+
+ .mdc-card__media {
+
+ & img {
+ bottom: 0;
+ height: 100%;
+ object-fit: cover;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ }
+ }
+
+ .avatar {
+ border-radius: 50%;
+ margin-right: 1rem;
+ }
+}
diff --git a/plugin/assets/css/src/front-end.css b/plugin/assets/css/src/front-end.css
index 4614a1c09..1b822fbc2 100644
--- a/plugin/assets/css/src/front-end.css
+++ b/plugin/assets/css/src/front-end.css
@@ -26,3 +26,4 @@
@import "../../src/block-editor/blocks/common-posts-list/style.css";
@import "../../src/block-editor/blocks/contact-form/inner-blocks/common/style.css";
@import "../../src/block-editor/blocks/card/style.css";
+@import "./block/style/masonry.css";
diff --git a/plugin/assets/src/block-editor/blocks/card-image/block.json b/plugin/assets/src/block-editor/blocks/card-image/block.json
new file mode 100644
index 000000000..13ee98fd1
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-image/block.json
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "name": "material/image-card-query",
+ "category": "material",
+ "title": "Material Image Card for Query loop",
+ "supports": {},
+ "apiVersion": 2,
+ "description": "Material image card for query loop block",
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js",
+ "usesContext": [ "postId", "postType", "queryId" ],
+ "textdomain": "material-design-google"
+}
diff --git a/plugin/assets/src/block-editor/blocks/card-image/edit.js b/plugin/assets/src/block-editor/blocks/card-image/edit.js
new file mode 100644
index 000000000..5876fde38
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-image/edit.js
@@ -0,0 +1,68 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed uder the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * External dependencies
+ */
+/**
+ * WordPress dependencies
+ */
+import { useBlockProps } from '@wordpress/block-editor';
+import ServerSideRender from '@wordpress/server-side-render';
+
+/**
+ * Internal dependencies
+ */
+import { name } from './block.json';
+
+/**
+ * Edit.
+ *
+ * @param {Object} props
+ * @param {{postType:string,postId:number,queryId:number}} props.context
+ * @param {Object} props.attributes
+ * @return {JSX.Element} Block edit.
+ */
+const Edit = ( { context, attributes } ) => {
+ const urlQueryArgs = {
+ materialParamContext: [],
+ };
+ // Server side rendering doesn't support passing context yet. This hack adds context as url param to later manually parse in php.
+ for ( const key in context ) {
+ urlQueryArgs.materialParamContext[ key ] = context[ key ];
+ }
+
+ const preventAnchorLink = e => {
+ e.preventDefault();
+ return false;
+ };
+
+ return (
+ <>
+ { /* Prevent anchor click coming from SSR. */ }
+ { /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */ }
+
+
+
+ >
+ );
+};
+
+export default Edit;
diff --git a/plugin/assets/src/block-editor/blocks/card-image/index.js b/plugin/assets/src/block-editor/blocks/card-image/index.js
new file mode 100644
index 000000000..4027a7776
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-image/index.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import { icon } from '../card-query/icon';
+import edit from './edit';
+
+const { name, title } = metadata;
+
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ title,
+ description: metadata.description,
+ icon,
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/card-image/style.css b/plugin/assets/src/block-editor/blocks/card-image/style.css
new file mode 100644
index 000000000..5c57463d0
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-image/style.css
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.wp-block-material-image-card-query a {
+ display: block;
+}
+
+.wp-block-material-image-card-query {
+ position: relative;
+
+ .mdc-image-list__supporting {
+ position: absolute;
+ background: rgba(0, 0, 0, .6);
+ bottom: 7.5px;
+ color: #fff;
+ height: auto;
+ width: 100%;
+ align-items: start;
+ border-bottom-left-radius: var(--mdc-image-list-radius, 4px);
+ border-bottom-right-radius: var(--mdc-image-list-radius, 4px);
+ max-height: 100%;
+ min-height: 48px;
+ overflow: hidden;
+ padding: 8px 16px;
+ }
+}
diff --git a/plugin/assets/src/block-editor/blocks/card-query/block.json b/plugin/assets/src/block-editor/blocks/card-query/block.json
new file mode 100644
index 000000000..ecf98fe77
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/block.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "name": "material/card-query",
+ "category": "material",
+ "attributes": {
+ "showExcerpt": {
+ "type": "boolean",
+ "default": true
+ },
+ "showDate": {
+ "type": "boolean",
+ "default": true
+ },
+ "showAuthor": {
+ "type": "boolean",
+ "default": true
+ },
+ "showComments": {
+ "type": "boolean",
+ "default": true
+ },
+ "showFeaturedImage": {
+ "type": "boolean",
+ "default": true
+ },
+ "showTitle": {
+ "type": "boolean",
+ "default": true
+ },
+ "postContentLength": {
+ "type": "number",
+ "default": 20
+ },
+ "cardStyle": {
+ "enum": [ "global", "elevated", "outlined"],
+ "default": "global",
+ "type": "string"
+ }
+ },
+ "title": "Material Card for Query loop",
+ "supports": {},
+ "apiVersion": 2,
+ "description": "Material card for query loop block",
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js",
+ "usesContext": [ "postId", "postType", "queryId" ],
+ "textdomain": "material-design-google"
+}
diff --git a/plugin/assets/src/block-editor/blocks/card-query/edit.js b/plugin/assets/src/block-editor/blocks/card-query/edit.js
new file mode 100644
index 000000000..410da6068
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/edit.js
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed uder the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * External dependencies
+ */
+/**
+ * WordPress dependencies
+ */
+import { useBlockProps } from '@wordpress/block-editor';
+import ServerSideRender from '@wordpress/server-side-render';
+
+/**
+ * Internal dependencies
+ */
+import { name } from './block.json';
+import InspectControls from './inspectControl';
+
+/**
+ * Edit.
+ *
+ * @param {Object} props
+ * @param {{postType:string,postId:number,queryId:number}} props.context
+ * @param {Function} props.setAttributes
+ * @param {Object} props.attributes
+ * @return {JSX.Element} Block edit.
+ */
+const Edit = ( { context, setAttributes, attributes } ) => {
+ const urlQueryArgs = {
+ materialParamContext: [],
+ };
+ // Server side rendering doesn't support passing context yet. This hack adds context as url param to later manually parse in php.
+ for ( const key in context ) {
+ urlQueryArgs.materialParamContext[ key ] = context[ key ];
+ }
+
+ const preventAnchorLink = e => {
+ e.preventDefault();
+ return false;
+ };
+
+ return (
+ <>
+
+ { /* Prevent anchor click coming from SSR. */ }
+ { /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */ }
+
+
+
+ >
+ );
+};
+
+export default Edit;
diff --git a/plugin/assets/src/block-editor/blocks/card-query/elevationControl.js b/plugin/assets/src/block-editor/blocks/card-query/elevationControl.js
new file mode 100644
index 000000000..e745d83b6
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/elevationControl.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WordPress dependencies
+ */
+import { RadioControl } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+const ElevationStyleControl = ( { onChange, selected } ) => {
+ const options = [
+ {
+ label: __( 'Elevated', 'material-design' ),
+ value: 'elevated',
+ },
+ {
+ label: __( 'Outlined', 'material-design' ),
+ value: 'outlined',
+ },
+ {
+ label: __( 'Inherit from Global Settings', 'material-design' ),
+ value: 'global',
+ },
+ ];
+ return (
+
+ );
+};
+
+export default ElevationStyleControl;
diff --git a/plugin/assets/src/block-editor/blocks/card-query/icon.js b/plugin/assets/src/block-editor/blocks/card-query/icon.js
new file mode 100644
index 000000000..10143e32f
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/icon.js
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Block Icon component.
+ *
+ * @return {JSX.Element} Function returning the HTML markup for the component.
+ */
+export const icon = () => (
+
+
+
+);
diff --git a/plugin/assets/src/block-editor/blocks/card-query/index.js b/plugin/assets/src/block-editor/blocks/card-query/index.js
new file mode 100644
index 000000000..391fdc349
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/index.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import { icon } from './icon';
+import edit from './edit';
+
+const { name, title } = metadata;
+
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ title,
+ description: metadata.description,
+ icon,
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/card-query/inspectControl.js b/plugin/assets/src/block-editor/blocks/card-query/inspectControl.js
new file mode 100644
index 000000000..3e2b17aa3
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/card-query/inspectControl.js
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * External dependencies
+ */
+
+/**
+ * WordPress dependencies
+ */
+import { InspectorControls } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import genericAttributesSetter from '../../utils/generic-attributes-setter';
+import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import ElevationStyleControl from './elevationControl';
+
+const MIN_POST_CONTENT_LENGTH = 10;
+const MAX_POST_CONTENT_LENGTH = 30;
+
+/**
+ * @param {Object} props
+ * @param {Function} props.setAttributes
+ * @param {Object} props.attributes
+ * @param {boolean} props.attributes.showFeaturedImage
+ * @param {boolean} props.attributes.showTitle
+ * @param {boolean} props.attributes.showExcerpt
+ * @param {boolean} props.attributes.showDate
+ * @param {boolean} props.attributes.showAuthor
+ * @param {boolean} props.attributes.showComments
+ * @param {number} props.attributes.postContentLength
+ * @param {string} props.attributes.cardStyle
+ */
+const InspectControls = ( {
+ setAttributes,
+ attributes: {
+ showFeaturedImage,
+ showTitle,
+ showExcerpt,
+ showDate,
+ showAuthor,
+ showComments,
+ postContentLength,
+ cardStyle,
+ },
+} ) => {
+ const setter = genericAttributesSetter( setAttributes );
+ return (
+
+
+
+
+
+
+ { showExcerpt && (
+
+ ) }
+
+
+
+
+
+
+
+ );
+};
+
+export default InspectControls;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-first/block.json b/plugin/assets/src/block-editor/blocks/query-pagination-first/block.json
new file mode 100644
index 000000000..e53d778b1
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-first/block.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "material/query-pagination-first",
+ "title": "First Page",
+ "category": "material",
+ "parent": [ "material/query-pagination" ],
+ "description": "Displays the first posts page link.",
+ "textdomain": "material-design-google",
+ "attributes": {
+ "label": {
+ "type": "string"
+ }
+ },
+ "usesContext": [ "queryId", "query" ],
+ "supports": {
+ "reusable": false,
+ "html": false,
+ "color": {
+ "gradients": true,
+ "text": false
+ },
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalTextTransform": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ }
+ },
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js"
+}
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-first/edit.js b/plugin/assets/src/block-editor/blocks/query-pagination-first/edit.js
new file mode 100644
index 000000000..327f2c9ea
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-first/edit.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+const QueryPaginationFirstEdit = () => {
+ return (
+ event.preventDefault() }
+ { ...useBlockProps() }
+ >
+
+ first_page
+
+
+ { __( 'First page', 'material-design' ) }
+
+
+ );
+};
+
+export default QueryPaginationFirstEdit;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-first/index.js b/plugin/assets/src/block-editor/blocks/query-pagination-first/index.js
new file mode 100644
index 000000000..f7c574f3e
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-first/index.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ icon: () => (
+
+
+
+ ),
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-last/block.json b/plugin/assets/src/block-editor/blocks/query-pagination-last/block.json
new file mode 100644
index 000000000..f4bd16b29
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-last/block.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "material/query-pagination-last",
+ "title": "Last Page",
+ "category": "material",
+ "parent": [ "material/query-pagination" ],
+ "description": "Displays the last posts page link.",
+ "textdomain": "material-design-google",
+ "attributes": {
+ "label": {
+ "type": "string"
+ }
+ },
+ "usesContext": [ "queryId", "query" ],
+ "supports": {
+ "reusable": false,
+ "html": false,
+ "color": {
+ "gradients": true,
+ "text": false
+ },
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalTextTransform": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ }
+ },
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js"
+}
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-last/edit.js b/plugin/assets/src/block-editor/blocks/query-pagination-last/edit.js
new file mode 100644
index 000000000..6546fb368
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-last/edit.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+const QueryPaginationLastEdit = () => {
+ return (
+ event.preventDefault() }
+ { ...useBlockProps() }
+ >
+
+ last_page
+
+
+ { __( 'Last page', 'material-design' ) }
+
+
+ );
+};
+
+export default QueryPaginationLastEdit;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-last/index.js b/plugin/assets/src/block-editor/blocks/query-pagination-last/index.js
new file mode 100644
index 000000000..e72637589
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-last/index.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ icon: () => (
+
+
+
+ ),
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-next/block.json b/plugin/assets/src/block-editor/blocks/query-pagination-next/block.json
new file mode 100644
index 000000000..5ed96a8b5
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-next/block.json
@@ -0,0 +1,45 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "material/query-pagination-next",
+ "title": "Next Page",
+ "category": "material",
+ "parent": [ "material/query-pagination" ],
+ "description": "Displays the next posts page link.",
+ "textdomain": "material-design-google",
+ "attributes": {
+ "label": {
+ "type": "string"
+ },
+ "className": {
+ "type": "string",
+ "default": "mdc-ripple-surface"
+ },
+ "class": {
+ "type": "string",
+ "default": "mdc-ripple-surface"
+ }
+ },
+ "usesContext": [ "queryId", "query" ],
+ "supports": {
+ "reusable": false,
+ "html": false,
+ "color": {
+ "gradients": true,
+ "text": false
+ },
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalTextTransform": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ }
+ },
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js"
+}
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-next/edit.js b/plugin/assets/src/block-editor/blocks/query-pagination-next/edit.js
new file mode 100644
index 000000000..45da8e677
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-next/edit.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+const QueryPaginationNextEdit = () => {
+ return (
+ event.preventDefault() }
+ { ...useBlockProps() }
+ >
+
+ chevron_right
+
+
+ { __( 'Next page', 'material-design' ) }
+
+
+ );
+};
+
+export default QueryPaginationNextEdit;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-next/index.js b/plugin/assets/src/block-editor/blocks/query-pagination-next/index.js
new file mode 100644
index 000000000..cb5b0f050
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-next/index.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ icon: () => (
+
+
+
+ ),
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-previous/block.json b/plugin/assets/src/block-editor/blocks/query-pagination-previous/block.json
new file mode 100644
index 000000000..13be4d0b7
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-previous/block.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "material/query-pagination-previous",
+ "title": "Previous Page",
+ "category": "material",
+ "parent": [ "material/query-pagination" ],
+ "description": "Displays the previous posts page link.",
+ "textdomain": "material-design-google",
+ "attributes": {
+ "label": {
+ "type": "string"
+ }
+ },
+ "usesContext": [ "queryId", "query" ],
+ "supports": {
+ "reusable": false,
+ "html": false,
+ "color": {
+ "gradients": true,
+ "text": false
+ },
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalTextTransform": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ }
+ },
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js"
+}
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-previous/edit.js b/plugin/assets/src/block-editor/blocks/query-pagination-previous/edit.js
new file mode 100644
index 000000000..69b3aec08
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-previous/edit.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+const QueryPaginationPreviousEdit = () => {
+ return (
+ event.preventDefault() }
+ { ...useBlockProps() }
+ >
+
+ chevron_left
+
+
+ { __( 'Previous page', 'material-design' ) }
+
+
+ );
+};
+
+export default QueryPaginationPreviousEdit;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination-previous/index.js b/plugin/assets/src/block-editor/blocks/query-pagination-previous/index.js
new file mode 100644
index 000000000..1e92679ad
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination-previous/index.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ icon: () => (
+
+
+
+ ),
+ edit,
+};
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination/block.json b/plugin/assets/src/block-editor/blocks/query-pagination/block.json
new file mode 100644
index 000000000..dc9f1e4cb
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination/block.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "material/query-pagination",
+ "title": "Material Pagination",
+ "category": "material",
+ "parent": [ "core/query" ],
+ "description": "Displays a paginated navigation to next/previous set of posts, when applicable.",
+ "textdomain": "material-design",
+ "attributes": {
+ "paginationArrow": {
+ "type": "string",
+ "default": "none"
+ }
+ },
+ "usesContext": [ "queryId", "query" ],
+ "supports": {
+ "align": true,
+ "reusable": false,
+ "html": false,
+ "color": {
+ "gradients": true,
+ "link": true
+ },
+ "__experimentalLayout": {
+ "allowSwitching": false,
+ "allowInheriting": false,
+ "default": {
+ "type": "flex"
+ }
+ }
+ },
+ "editorStyle": "material-block-editor-css",
+ "editorScript": "material-block-editor-js"
+}
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination/edit.js b/plugin/assets/src/block-editor/blocks/query-pagination/edit.js
new file mode 100644
index 000000000..12e1725f6
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination/edit.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { getBlockSupport } from '@wordpress/blocks';
+import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
+
+// Override core template.
+const TEMPLATE = [
+ [ 'material/query-pagination-first' ],
+ [ 'material/query-pagination-previous' ],
+ [ 'material/query-pagination-next' ],
+ [ 'material/query-pagination-last' ],
+];
+
+const getDefaultBlockLayout = blockTypeOrName => {
+ const layoutBlockSupportConfig = getBlockSupport(
+ blockTypeOrName,
+ '__experimentalLayout'
+ );
+
+ return layoutBlockSupportConfig?.default;
+};
+
+const QueryPaginationEdit = ( { attributes: { layout }, name } ) => {
+ const usedLayout = layout || getDefaultBlockLayout( name );
+ const blockProps = useBlockProps();
+ const innerBlockProps = useInnerBlocksProps( blockProps, {
+ template: TEMPLATE,
+ allowedBlocks: TEMPLATE,
+ __experimentalLayout: usedLayout,
+ } );
+
+ return (
+ <>
+
+ >
+ );
+};
+
+export default QueryPaginationEdit;
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination/hooks.js b/plugin/assets/src/block-editor/blocks/query-pagination/hooks.js
new file mode 100644
index 000000000..4b741bcda
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination/hooks.js
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * WordPress dependencies
+ */
+import { addFilter } from '@wordpress/hooks';
+import { __ } from '@wordpress/i18n';
+
+const addMaterialStyle = ( settings, name ) => {
+ if ( 'core/query-pagination' === name ) {
+ settings.styles = [
+ {
+ name: 'material',
+ label: __( 'Material', 'material-design' ),
+ },
+ {
+ name: 'regular',
+ label: __( 'Regular', 'material-design' ),
+ isDefault: true,
+ },
+ ];
+ }
+
+ return settings;
+};
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'material/query-navigation-style',
+ addMaterialStyle
+);
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination/index.js b/plugin/assets/src/block-editor/blocks/query-pagination/index.js
new file mode 100644
index 000000000..c99934256
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination/index.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Internal dependencies
+ */
+
+import metadata from './block.json';
+import './hooks';
+import edit from './edit';
+import save from './save';
+
+const { name } = metadata;
+const isQueryLoopRequired = true;
+export { metadata, name, isQueryLoopRequired };
+
+export const settings = {
+ icon: () => (
+
+
+
+ ),
+ edit,
+ save,
+};
diff --git a/plugin/assets/src/block-editor/blocks/query-pagination/save.js b/plugin/assets/src/block-editor/blocks/query-pagination/save.js
new file mode 100644
index 000000000..caf0ef46c
--- /dev/null
+++ b/plugin/assets/src/block-editor/blocks/query-pagination/save.js
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WordPress dependencies
+ */
+import { InnerBlocks } from '@wordpress/block-editor';
+
+const Save = () => {
+ return ;
+};
+
+export default Save;
diff --git a/plugin/assets/src/block-editor/helpers/index.js b/plugin/assets/src/block-editor/helpers/index.js
index 567289379..0ec220fd0 100644
--- a/plugin/assets/src/block-editor/helpers/index.js
+++ b/plugin/assets/src/block-editor/helpers/index.js
@@ -26,8 +26,16 @@ import { registerBlockType } from '@wordpress/blocks';
*/
export const registerBlocks = blocks => {
blocks.keys().forEach( modulePath => {
- const { name, settings, metadata } = blocks( modulePath );
- registerBlockType( name, { ...settings, ...( metadata || {} ) } );
+ const { name, settings, metadata, isQueryLoopRequired } = blocks(
+ modulePath
+ );
+
+ if (
+ ! isQueryLoopRequired ||
+ window?.materialDesign?.canUseQueryLoop
+ ) {
+ registerBlockType( name, { ...settings, ...( metadata || {} ) } );
+ }
} );
};
diff --git a/plugin/assets/src/block-editor/index.js b/plugin/assets/src/block-editor/index.js
index 7904dd3b8..0068335fd 100755
--- a/plugin/assets/src/block-editor/index.js
+++ b/plugin/assets/src/block-editor/index.js
@@ -27,6 +27,7 @@ import { updateCategory } from '@wordpress/blocks';
import { registerBlocks, MaterialLogo } from './helpers';
import './blocks/data-table/hooks';
import './formats';
+import './style/core-template';
/**
* Register the blocks.
diff --git a/plugin/assets/src/block-editor/style/core-template/index.js b/plugin/assets/src/block-editor/style/core-template/index.js
new file mode 100644
index 000000000..94edc1559
--- /dev/null
+++ b/plugin/assets/src/block-editor/style/core-template/index.js
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { registerBlockStyle } from '@wordpress/blocks';
+
+registerBlockStyle( 'core/post-template', {
+ name: 'material-masonry',
+ label: 'Material Masonry Grid',
+} );
diff --git a/plugin/assets/src/front-end/index.js b/plugin/assets/src/front-end/index.js
index d0d05963f..7bcd91e19 100644
--- a/plugin/assets/src/front-end/index.js
+++ b/plugin/assets/src/front-end/index.js
@@ -27,6 +27,7 @@ import {
initTextFields,
} from '../common/mdc-components-init';
import { initContactForm } from './contact-form';
+import { masonryInit } from './masonry';
addEventListener( 'DOMContentLoaded', () => {
initButtons();
@@ -34,6 +35,7 @@ addEventListener( 'DOMContentLoaded', () => {
initTabBar();
initContactForm();
initToolTips();
+ masonryInit();
// If material theme is not active then init text fields.
if (
diff --git a/plugin/assets/src/front-end/masonry.js b/plugin/assets/src/front-end/masonry.js
new file mode 100644
index 000000000..00840de1e
--- /dev/null
+++ b/plugin/assets/src/front-end/masonry.js
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+let gridElement = null;
+let rowHeight = 24;
+let rowGap = 24;
+export const masonryInit = () => {
+ gridElement = document.querySelector( '.is-flex-container' );
+
+ if ( ! gridElement ) {
+ return;
+ }
+
+ const mediaQuery = window.matchMedia( '(min-width: 840px)' );
+
+ mediaQuery.addEventListener( 'change', handleResize );
+ handleResize( mediaQuery );
+};
+
+const handleResize = mediaQuery => {
+ if ( mediaQuery.matches ) {
+ resizeAllGridItems();
+ }
+};
+
+const resizeAllGridItems = () => {
+ const cells = gridElement.querySelectorAll(
+ '.is-style-material-masonry .wp-block-post'
+ );
+
+ if ( cells.length <= 0 ) {
+ return;
+ }
+
+ rowHeight = parseInt(
+ window
+ .getComputedStyle( gridElement )
+ .getPropertyValue( 'grid-auto-rows' ),
+ 10
+ );
+
+ rowGap = parseInt(
+ window
+ .getComputedStyle( gridElement )
+ .getPropertyValue( 'grid-row-gap' ),
+ 10
+ );
+
+ const hasPostCard = cells[ 0 ].querySelectorAll( '.post-card' ).length > 0;
+
+ if ( ! hasPostCard ) {
+ gridElement.style.gridAutoRows = 'minmax(min-content,max-content)';
+
+ // Let it re-render to compute height.
+ setTimeout( () => {
+ cells.forEach( resizeGridItem );
+ gridElement.style.removeProperty( 'grid-auto-rows' );
+ }, 0 );
+ return;
+ }
+
+ cells.forEach( resizeGridItem );
+};
+
+const resizeGridItem = cell => {
+ if ( ! cell ) {
+ return;
+ }
+
+ let cellCard = cell.querySelector( '.post-card' );
+
+ if ( ! cellCard ) {
+ const imageCardBlock = cell.querySelector(
+ '.wp-block-material-image-card-query'
+ );
+ if ( imageCardBlock ) {
+ // For material image card.
+ cellCard = imageCardBlock;
+ } else {
+ // If we have a cell without card wrapper inside let's use that, Used for default WP template.
+ cellCard = cell;
+ }
+ }
+
+ const contentHeight = cellCard.getBoundingClientRect().height;
+
+ const rowSpan = Math.ceil(
+ ( contentHeight + rowGap ) / ( rowHeight + rowGap )
+ );
+
+ cell.style.gridRowEnd = 'span ' + rowSpan;
+};
diff --git a/plugin/php/block-patterns/material-card-image.php b/plugin/php/block-patterns/material-card-image.php
new file mode 100644
index 000000000..d3f2a3a88
--- /dev/null
+++ b/plugin/php/block-patterns/material-card-image.php
@@ -0,0 +1,46 @@
+ __( 'Query with material image card.', 'material-design' ),
+ 'content' => '
+
+
+
+
+
+
+
+
+
+
+
+
+
+',
+ 'description' => __( 'Query with material image card and material pagination.', 'material-design' ),
+ 'blockTypes' => [ 'core/query' ],
+ 'categories' => [ 'material', 'query' ],
+];
diff --git a/plugin/php/block-patterns/material-card.php b/plugin/php/block-patterns/material-card.php
new file mode 100644
index 000000000..843e5c4f7
--- /dev/null
+++ b/plugin/php/block-patterns/material-card.php
@@ -0,0 +1,46 @@
+ __( 'Query with material Card', 'material-design' ),
+ 'content' => '
+
+
+
+
+
+
+
+
+
+
+
+
+
+',
+ 'description' => __( 'Query with material card and pagination.', 'material-design' ),
+ 'blockTypes' => [ 'core/query' ],
+ 'categories' => [ 'material', 'query' ],
+];
diff --git a/plugin/php/blocks/class-blocks.php b/plugin/php/blocks/class-blocks.php
new file mode 100644
index 000000000..cf14c7600
--- /dev/null
+++ b/plugin/php/blocks/class-blocks.php
@@ -0,0 +1,172 @@
+ 'template-parts/blocks/card-query.php',
+ 'material/image-card-query' => 'template-parts/blocks/image-card-query.php',
+ 'material/query-pagination' => 'template-parts/blocks/query-pagination.php',
+ 'material/query-pagination-next' => 'template-parts/blocks/query-pagination-next.php',
+ 'material/query-pagination-previous' => 'template-parts/blocks/query-pagination-previous.php',
+ 'material/query-pagination-first' => 'template-parts/blocks/query-pagination-first.php',
+ 'material/query-pagination-last' => 'template-parts/blocks/query-pagination-last.php',
+ ];
+
+ /**
+ * Register any needed hooks/filters.
+ *
+ * @action init
+ */
+ public function init() {
+ // Only add block for 5.8 or later.
+ if ( version_compare( '5.8', get_bloginfo( 'version' ), '<=' ) ) {
+ add_action( 'init', [ $this, 'action_register_blocks' ] );
+ }
+ }
+
+ /**
+ * Register all blocks living in the "/blocks/" folder in the theme.
+ */
+ public function action_register_blocks() {
+ $folders = static::get_blocks_folders();
+
+ // Register blocks.
+ foreach ( $folders as $folder ) {
+ $object = static::get_block_object( $folder );
+
+ if ( ! $object ) {
+ continue;
+ }
+
+ if ( ! array_key_exists( $object['name'], static::DYNAMIC_BLOCKS ) ) {
+ // Todo once class-block-type.php is migrated this condition can be removed.
+ continue;
+ }
+
+ // If this is a dynamic block, register render_callback.
+ if ( array_key_exists( $object['name'], static::DYNAMIC_BLOCKS ) ) {
+ $object['render_callback'] = [ static::class, 'render' ];
+ }
+
+ /**
+ * Filters the arguments for registering a block type.
+ *
+ * @param array $metadata Array of arguments for registering a block type.
+ * @param string $name Block name.
+ */
+ $args = apply_filters( 'material_design_block_type_args', $object, $object['name'] );
+
+ if ( function_exists( 'register_block_type' ) ) {
+ register_block_type( $folder, $args );
+ }
+ }
+ }
+
+ /**
+ * Get all blocks from file-system (folders).
+ *
+ * @return array
+ */
+ public static function get_blocks_folders() {
+ $root_folder = __DIR__ . '/../../assets/src/block-editor/blocks';
+
+ return glob( $root_folder . '/*', GLOB_ONLYDIR | GLOB_MARK );
+ }
+
+ /**
+ * Get block object.
+ *
+ * @param string $block_folder Block folder.
+ *
+ * @return object
+ */
+ public static function get_block_object( $block_folder ) {
+ $block_file = $block_folder . 'block.json';
+
+ if ( ! file_exists( $block_file ) ) {
+ return null;
+ }
+
+ // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
+ return json_decode( file_get_contents( $block_file ), true );
+ }
+
+ /**
+ * Render Dynamic blocks.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block content.
+ * @param object $block Block object.
+ *
+ * @return false|string
+ */
+ public static function render( $attributes, $content, $block ) {
+ $template = __DIR__ . '/../' . static::DYNAMIC_BLOCKS[ $block->name ];
+
+ if ( ! file_exists( $template ) ) {
+ return false;
+ }
+
+ // Remove this once server side context is available via gutenberg.
+ if ( ! empty( $block->block_type->uses_context ) && ! empty( $_GET['materialParamContext'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ foreach ( $block->block_type->uses_context as $context ) {
+ if ( empty( $_GET['materialParamContext'][ $context ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ continue;
+ }
+ switch ( $context ) {
+ case 'postId':
+ case 'queryId':
+ $sanitize_callback = 'intval';
+ break;
+ case 'postType':
+ $sanitize_callback = 'sanitize_key';
+ break;
+ }
+ $block->context[ $context ] = call_user_func( $sanitize_callback, $_GET['materialParamContext'][ $context ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ }
+ }
+
+ ob_start();
+
+ load_template(
+ $template,
+ false,
+ [
+ 'attributes' => $attributes,
+ 'content' => $content,
+ 'block' => $block,
+ ]
+ );
+
+ return ob_get_clean();
+ }
+}
diff --git a/plugin/php/class-block-patterns.php b/plugin/php/class-block-patterns.php
index 7c3e00db2..810a770fb 100644
--- a/plugin/php/class-block-patterns.php
+++ b/plugin/php/class-block-patterns.php
@@ -50,7 +50,7 @@ public function __construct( Plugin $plugin ) {
* Initiate the class.
*/
public function init() {
- add_action( 'init', [ $this, 'register' ] );
+ add_action( 'init', [ $this, 'register' ], 9 );
}
/**
@@ -86,6 +86,8 @@ public function register() {
'pricing',
'hero-section-image',
'latest-posts',
+ 'material-card',
+ 'material-card-image',
];
foreach ( $patterns as $pattern ) {
diff --git a/plugin/php/class-block-types.php b/plugin/php/class-block-types.php
index c5e17523b..8bdbaa95e 100644
--- a/plugin/php/class-block-types.php
+++ b/plugin/php/class-block-types.php
@@ -25,6 +25,7 @@
namespace MaterialDesign\Plugin;
+use MaterialDesign\Plugin\Blocks\Blocks;
use MaterialDesign\Plugin\Blocks\Card_Block;
use MaterialDesign\Plugin\Blocks\Posts_List_Block;
use MaterialDesign\Plugin\Blocks\Contact_Form_Block;
@@ -91,6 +92,9 @@ public function init() {
$static_block = new Card_Block( $this->plugin, $static_card_block );
$static_block->init();
}
+
+ $blocks = new Blocks( $this->plugin );
+ $blocks->init();
}
/**
@@ -240,6 +244,7 @@ public function enqueue_block_editor_assets() {
],
'postTypes' => $post_types,
'doesRequireBackCompatList' => version_compare( get_bloginfo( 'version' ), '5.8', '<' ),
+ 'canUseQueryLoop' => version_compare( '5.8', get_bloginfo( 'version' ), '<=' ),
];
if ( Helpers::is_current_user_admin_or_editor_with_manage_options() ) {
diff --git a/plugin/php/template-parts/blocks/card-query.php b/plugin/php/template-parts/blocks/card-query.php
new file mode 100644
index 000000000..c17a081b4
--- /dev/null
+++ b/plugin/php/template-parts/blocks/card-query.php
@@ -0,0 +1,149 @@
+block_types->get_global_styles( 'card_style' );
+$classes = $card_style === 'outlined'
+ ||
+ ( $card_style === 'global' && $global_card_style === 'outlined' ) ? 'mdc-card--outlined' : '';
+
+if ( empty( $block ) || ! isset( $block->context['postId'] ) ) {
+ return '';
+}
+$post_id_card = $block->context['postId'];
+$post_card = get_post( $post_id_card );
+$post_link = get_the_permalink( $post_card );
+$post_content = wp_trim_words( get_the_excerpt( $post_card ), $content_length, ' […]' );
+?>
+