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', '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', '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() } + > + + + { __( '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() } + > + + + { __( '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, ' […]' ); +?> +
+ +
diff --git a/plugin/php/template-parts/blocks/image-card-query.php b/plugin/php/template-parts/blocks/image-card-query.php new file mode 100644 index 000000000..e7b97ff02 --- /dev/null +++ b/plugin/php/template-parts/blocks/image-card-query.php @@ -0,0 +1,42 @@ +context['postId']; +$post_link = get_the_permalink( $post_id_image_card ); +if ( has_post_thumbnail( $post_id_image_card ) ) { + $thumbnail = get_the_post_thumbnail_url( $post_id_image_card ); +} else { + $thumbnail = get_template_directory_uri() . '/assets/images/placeholder.png'; +} +$wrapper_attributes = get_block_wrapper_attributes(); +?> +
> + + +
+ +
+
+
diff --git a/plugin/php/template-parts/blocks/query-pagination-first.php b/plugin/php/template-parts/blocks/query-pagination-first.php new file mode 100644 index 000000000..f0537dee3 --- /dev/null +++ b/plugin/php/template-parts/blocks/query-pagination-first.php @@ -0,0 +1,70 @@ +context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; +/** + * Copied from core without nonce verification. + */ +$page_number = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended +$wrapper_attributes = get_block_wrapper_attributes(); +$default_label = __( 'First', 'material-design' ); +$label = isset( $attributes['label'] ) && ! empty( $attributes['label'] ) ? $attributes['label'] : $default_label; +$content = ''; +$url = ''; + +// Check if the pagination is for Query that inherits the global context +// and handle appropriately. +if ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ) { + global $wp_query; + $url = get_pagenum_link( 1 ); + $query_page_number = (int) get_query_var( 'paged' ); + $page_number = $query_page_number > 0 ? $query_page_number : $page_number; +} else { + $url = add_query_arg( $page_key, 1 ); +} +$is_disabled = 1 === $page_number; + +if ( ! empty( $url ) ) : + $screen_reader = sprintf( + /* translators: available page description. */ + esc_html__( '%s page', 'material-design' ), + esc_html( $label ) + ); + $inner_content = sprintf( + ' + %s', + $screen_reader + ); + $inner_content_with_anchor = $is_disabled ? $inner_content : sprintf( + '%s', + esc_url( $url ), + $inner_content + ); + $content = sprintf( + '
  • %s
  • ', + $wrapper_attributes, + $inner_content_with_anchor + ); + ?> + context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; +/** + * Copied from core without nonce verification. + */ +$page_number = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended +$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0; +$wrapper_attributes = get_block_wrapper_attributes(); +$default_label = __( 'Last', 'material-design' ); +$label = isset( $attributes['label'] ) && ! empty( $attributes['label'] ) ? $attributes['label'] : $default_label; +$content = ''; +$url = ''; +$is_disabled = false; +// Check if the pagination is for Query that inherits the global context. +if ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ) { + + // Take into account if we have set a bigger `max page` + // than what the query has. + global $wp_query; + $max_page = $wp_query->max_num_pages; + $url = get_pagenum_link( $max_page ); + $current_page_num = get_query_var( 'paged' ); + $is_disabled = (int) $current_page_num === (int) $max_page; + +} elseif ( ! $max_page || $max_page > $page_number ) { + $custom_query = new WP_Query( build_query_vars_from_query_block( $block, $page_number ) ); + $custom_query_max_pages = (int) $custom_query->max_num_pages; + $is_disabled = $custom_query_max_pages === $page_number; + $url = add_query_arg( $page_key, $custom_query_max_pages ); + + wp_reset_postdata(); // Restore original Post Data. +} + +if ( ! empty( $url ) ) : + $screen_reader = sprintf( + /* translators: available page description. */ + esc_html__( '%s page', 'material-design' ), + esc_html( $label ) + ); + $inner_content = sprintf( + ' + %s', + $screen_reader + ); + $inner_content_with_anchor = $is_disabled ? $inner_content : sprintf( + '%s', + esc_url( $url ), + $inner_content + ); + $content = sprintf( + '
  • %s
  • ', + $wrapper_attributes, + $inner_content_with_anchor + ); + ?> + context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; +/** + * Copied from core without nonce verification. + */ +$page_number = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended +$max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0; +$default_label = __( 'Next', 'material-design' ); +$label = isset( $attributes['label'] ) && ! empty( $attributes['label'] ) ? $attributes['label'] : $default_label; +$url = ''; +$content = ''; +$wrapper_attributes = get_block_wrapper_attributes(); +$is_disabled = false; + +// Check if the pagination is for Query that inherits the global context. +if ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ) { + // Take into account if we have set a bigger `max page` + // than what the query has. + global $wp_query; + + if ( $max_page > $wp_query->max_num_pages ) { + $max_page = $wp_query->max_num_pages; + } + $max_page = $wp_query->max_num_pages; + $url = get_pagenum_link( $max_page ); + $current_page_num = get_query_var( 'paged' ); + $is_disabled = (int) $current_page_num === (int) $max_page; + $url = next_posts( $max_page, false ); + // Force in loop to show disabled state, as next_posts returns empty when on last page. + $url = $url ? $url : '#'; +} elseif ( ! $max_page || $max_page > $page_number ) { + $custom_query = new WP_Query( build_query_vars_from_query_block( $block, $page_number ) ); + $custom_query_max_pages = (int) $custom_query->max_num_pages; + $is_disabled = $custom_query_max_pages === $page_number; + // In case of disabled - url won't be used. + $url = add_query_arg( $page_key, $page_number + 1 ); + wp_reset_postdata(); // Restore original Post Data. +} + +if ( ! empty( $url ) ) : + $screen_reader = sprintf( + /* translators: available page description. */ + esc_html__( '%s page', 'material-design' ), + esc_html( $label ) + ); + $inner_content = sprintf( + ' + %s', + $screen_reader + ); + $inner_content_with_anchor = $is_disabled ? $inner_content : sprintf( + '%s', + esc_url( $url ), + $inner_content + ); + $content = sprintf( + '
  • %s
  • ', + $wrapper_attributes, + $inner_content_with_anchor + ); + ?> + context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; +/** + * Copied from core without nonce verification. + */ +$page_number = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended +$default_label = __( 'Previous', 'material-design' ); +$label = isset( $attributes['label'] ) && ! empty( $attributes['label'] ) ? $attributes['label'] : $default_label; +$content = ''; +$url = ''; +$wrapper_attributes = get_block_wrapper_attributes(); + +// Check if the pagination is for Query that inherits the global context +// and handle appropriately. +if ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ) { + global $wp_query; + $url = previous_posts( false ); + // Force in loop to show disabled state, as previous_posts returns empty when on first page. + $url = $url ? $url : '#'; + $query_page_number = (int) get_query_var( 'paged' ); + $page_number = $query_page_number > 0 ? $query_page_number : $page_number; +} else { + $url = add_query_arg( $page_key, $page_number - 1 ); +} + +$is_disabled = 1 === $page_number; + +if ( ! empty( $url ) ) : + $screen_reader = sprintf( + /* translators: available page description. */ + esc_html__( '%s page', 'material-design' ), + esc_html( $label ) + ); + $inner_content = sprintf( + ' + %s', + $screen_reader + ); + $inner_content_with_anchor = $is_disabled ? $inner_content : sprintf( + '%s', + esc_url( $url ), + $inner_content + ); + $content = sprintf( + '
  • %s
  • ', + $wrapper_attributes, + $inner_content_with_anchor + ); + ?> + + + +
      > + +
    diff --git a/plugin/tests/phpunit/php/class-test-block-patterns.php b/plugin/tests/phpunit/php/class-test-block-patterns.php index 6ba6fe0ac..177507819 100644 --- a/plugin/tests/phpunit/php/class-test-block-patterns.php +++ b/plugin/tests/phpunit/php/class-test-block-patterns.php @@ -61,7 +61,7 @@ class Test_Block_Patterns extends \WP_UnitTestCase { public function test_init() { $block_patterns = get_plugin_instance()->block_patterns; - $this->assertEquals( 10, has_action( 'init', [ $block_patterns, 'register' ] ) ); + $this->assertEquals( 9, has_action( 'init', [ $block_patterns, 'register' ] ) ); } /**