From 0586035b7ec09a54cb9e8a3f0ea14f333617d11f Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Fri, 23 Jun 2023 10:03:05 +0800 Subject: [PATCH 1/7] initial commit --- src/block/timeline/block.json | 15 ++ src/block/timeline/edit.js | 252 +++++++++++++++++++++++++++++ src/block/timeline/editor.scss | 96 +++++++++++ src/block/timeline/example.js | 26 +++ src/block/timeline/index.js | 32 ++++ src/block/timeline/save.js | 93 +++++++++++ src/block/timeline/schema.js | 97 +++++++++++ src/block/timeline/style.js | 142 ++++++++++++++++ src/block/timeline/style.scss | 141 ++++++++++++++++ src/dynamic-breakpoints.php | 2 +- src/icons/images/timeline-icon.svg | 1 + src/icons/index.js | 5 + 12 files changed, 901 insertions(+), 1 deletion(-) create mode 100644 src/block/timeline/block.json create mode 100644 src/block/timeline/edit.js create mode 100644 src/block/timeline/editor.scss create mode 100644 src/block/timeline/example.js create mode 100644 src/block/timeline/index.js create mode 100644 src/block/timeline/save.js create mode 100644 src/block/timeline/schema.js create mode 100644 src/block/timeline/style.js create mode 100644 src/block/timeline/style.scss create mode 100644 src/icons/images/timeline-icon.svg diff --git a/src/block/timeline/block.json b/src/block/timeline/block.json new file mode 100644 index 000000000..97b5008da --- /dev/null +++ b/src/block/timeline/block.json @@ -0,0 +1,15 @@ +{ + "apiVersion": 2, + "name": "stackable/timeline", + "title": "Timeline", + "description": "Show events in chronological order", + "category": "stackable", + "usesContext": [ "postId", "postType", "queryId" , "stackable/innerBlockOrientation" ], + "keywords": [ + "history", + "milestone" + ], + "textdomain": "stackable-ultimate-gutenberg-blocks", + "stk-type": "special", + "stk-demo": "https://wpstackable.com/timeline-block/?utm_source=welcome&utm_medium=settings&utm_campaign=view_demo&utm_content=demolink" +} diff --git a/src/block/timeline/edit.js b/src/block/timeline/edit.js new file mode 100644 index 000000000..71e74610b --- /dev/null +++ b/src/block/timeline/edit.js @@ -0,0 +1,252 @@ +/** + * Internal dependencies + */ +import BlockStyles from './style' + +/** + * External dependencies + */ +import { i18n, version as VERSION } from 'stackable' +import classnames from 'classnames' +import { + AdvancedRangeControl, + AdvancedToolbarControl, + ColorPaletteControl, + ControlSeparator, + GroupPlaceholder, + InspectorLayoutControls, + InspectorStyleControls, + InspectorTabs, + PanelAdvancedSettings, +} from '~stackable/components' +import { + BlockDiv, + useGeneratedCss, + MarginBottom, + getRowClasses, + getAlignmentClasses, + Advanced, + CustomCSS, + Responsive, + CustomAttributes, + EffectsAnimations, + ConditionalDisplay, + getSeparatorClasses, + Transform, + ContentAlign, + getContentAlignmentClasses, + Typography, +} from '~stackable/block-components' +import { useBlockContext } from '~stackable/hooks' +import { + withBlockAttributeContext, + withBlockWrapperIsHovered, + withQueryLoopContext, +} from '~stackable/higher-order' + +/** + * WordPress dependencies + */ +import { compose } from '@wordpress/compose' +import { + __, _x, sprintf, +} from '@wordpress/i18n' +import { InnerBlocks } from '@wordpress/block-editor' + +const ALLOWED_INNER_BLOCKS = [ 'stackable/column' ] + +const TEMPLATE = [ + [ 'stackable/column', {}, [ + [ 'stackable/text', { + text: _x( 'Description for this block. Use this space for describing your block. Any text will do.', 'Content placeholder', i18n ), + } ], + ] ], +] + +const Edit = props => { + const { + className, + clientId, + isSelected, + } = props + + useGeneratedCss( props.attributes ) + + const rowClass = getRowClasses( props.attributes ) + const separatorClass = getSeparatorClasses( props.attributes ) + const blockAlignmentClass = getAlignmentClasses( props.attributes ) + const { hasInnerBlocks } = useBlockContext() + + const blockClassNames = classnames( [ + className, + 'stk-block-timeline', + rowClass, + separatorClass, + { + 'stk-block-timeline--left': props.attributes.timelinePosition !== 'right', + 'stk-block-timeline--right': props.attributes.timelinePosition === 'right', + }, + ] ) + + const contentClassNames = classnames( [ + 'stk-inner-blocks', + blockAlignmentClass, + 'stk-block-content', + ], getContentAlignmentClasses( props.attributes ) ) + + return ( + <> + { isSelected && ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } + + + + + + { ! hasInnerBlocks && } +
+ +
+
+ +
+
+ { /* */ } +
+ { props.isHovered && hasInnerBlocks && } + + ) +} + +export default compose( + withBlockWrapperIsHovered, + withQueryLoopContext, + withBlockAttributeContext, +)( Edit ) diff --git a/src/block/timeline/editor.scss b/src/block/timeline/editor.scss new file mode 100644 index 000000000..b156269f3 --- /dev/null +++ b/src/block/timeline/editor.scss @@ -0,0 +1,96 @@ +@import "common"; + +.stk-block-timeline > .stk-inner-blocks { + display: grid !important; + grid-template-columns: 1fr var(--line-dot-size, 16px) 1fr; + grid-template-rows: auto; + gap: var(--gap, 16px); + position: relative; + align-items: center; +} +.stk-block-timeline--left > .stk-inner-blocks > * { + grid-row: 1 / 2; +} +.stk-block-timeline > .stk-inner-blocks::after { + content: ""; + position: absolute; + inset-inline-start: calc(50% - var(--line-bg-width, 3px) / 2); + width: var(--line-bg-width, 3px); + top: 0; + bottom: 0; + background: var(--fixed-bg); + background-attachment: fixed; +} +/** Trim the top-most line and the bottom-most line */ +.stk-block-timeline > .stk-inner-blocks::after { + top: 50%; + bottom: 50%; +} +[data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline > .stk-inner-blocks::after { + top: 0; +} +[data-type="stackable/timeline"]:has(+ [data-type="stackable/timeline"]) > .stk-block-timeline > .stk-inner-blocks::after { + bottom: 0; +} + +// Remove the top 8px gap. +[data-type="stackable/timeline"] + [data-type="stackable/timeline"] { + margin-top: 0 !important; +} + +@include tablet-mobile { + // In tablet and mobile, fixed background shows weirdly, so we remove it. + .stk-block-timeline__middle, + .stk-block-timeline > .stk-inner-blocks::after { + --fixed-bg: var(--line-accent-bg-color); + } +} + +@include mobile { + .editor-styles-wrapper .stk-block.stk-block-timeline { + --content-line: 0 !important; + } + .stk-block-timeline > .stk-inner-blocks { + grid-template-columns: var(--line-dot-size, 16px) 1fr; + grid-template-rows: auto 1fr; + align-items: flex-start; + } + .stk-block-timeline > .stk-inner-blocks::after { + inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2); + } + .stk-block-timeline .stk-block-timeline__middle { + grid-column: 1 / 2; + grid-row: 1 / 3; + } + .stk-block-timeline .stk-block-timeline__content { + grid-column: 2 / 3; + grid-row: 2 / 3; + text-align: start; + } + .stk-block-timeline .stk-block-timeline__date { + grid-column: 2 / 3; + grid-row: 1 / 2; + text-align: start; + } + + /** Trim the top-most line and the bottom-most line */ + .stk-block-timeline > .stk-inner-blocks::after { + top: calc(var(--line-dot-size, 16px) / 2); + bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2)); + } + [data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline > .stk-inner-blocks::after { + top: -16px; + } + [data-type="stackable/timeline"]:has(+ [data-type="stackable/timeline"]) > .stk-block-timeline > .stk-inner-blocks::after { + bottom: 0; + } + + // Collapsed timeline sticks too much to the side. + .stk-block-timeline { + padding-inline-start: 16px; + } + // Better spacing when multiple timelines are stacked. + [data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline { + padding-top: 16px; + } +} diff --git a/src/block/timeline/example.js b/src/block/timeline/example.js new file mode 100644 index 000000000..addd5fe60 --- /dev/null +++ b/src/block/timeline/example.js @@ -0,0 +1,26 @@ +/** + * The example of this block to show in the inserter. + * Add attributes here that will be used to show the preview of the block. + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n' + +export default { + attributes: { + uniqueId: '1234567', + }, + innerBlocks: [ + { + name: 'stackable/column', attributes: { uniqueId: '1234568' }, innerBlocks: [], + }, + { + name: 'stackable/column', attributes: { uniqueId: '1234569' }, innerBlocks: [], + }, + { + name: 'stackable/column', attributes: { uniqueId: '1234570' }, innerBlocks: [], + }, + ], +} diff --git a/src/block/timeline/index.js b/src/block/timeline/index.js new file mode 100644 index 000000000..9da2c73c9 --- /dev/null +++ b/src/block/timeline/index.js @@ -0,0 +1,32 @@ +/** + * BLOCK: Timeline block. + */ +/** + * External dependencies + */ +import { TimelineIcon } from '~stackable/icons' + +/** + * Internal dependencies + */ +import edit from './edit' +import save from './save' +import schema from './schema' +import metadata from './block.json' +import example from './example' + +export const settings = { + ...metadata, + icon: TimelineIcon, + attributes: schema, + supports: { + anchor: true, + align: [ 'center', 'wide', 'full' ], // Only select alignments. + stkAlign: true, + stkDefaultTab: 'layout', + stkColumnResize: false, + }, + example, + edit, + save, +} diff --git a/src/block/timeline/save.js b/src/block/timeline/save.js new file mode 100644 index 000000000..2be67fc40 --- /dev/null +++ b/src/block/timeline/save.js @@ -0,0 +1,93 @@ +/** + * Internal dependencies + */ +import BlockStyles from './style' + +/** + * External dependencies + */ +import { withVersion } from '~stackable/higher-order' +import { version as VERSION } from 'stackable' +import classnames from 'classnames' +import { + BlockDiv, + CustomCSS, + getAlignmentClasses, + getResponsiveClasses, + getRowClasses, + // Separator, + getSeparatorClasses, + getContentAlignmentClasses, + Typography, +} from '~stackable/block-components' + +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor' +import { applyFilters } from '@wordpress/hooks' +import { compose } from '@wordpress/compose' + +export const Save = props => { + const { + attributes, + } = props + + const rowClass = getRowClasses( props.attributes ) + const separatorClass = getSeparatorClasses( props.attributes ) + const blockAlignmentClass = getAlignmentClasses( props.attributes ) + const responsiveClass = getResponsiveClasses( props.attributes ) + + const blockClassName = classnames( [ + props.className, + 'stk-block-timeline', + responsiveClass, + separatorClass, + { + 'stk-block-timeline--left': props.attributes.timelinePosition !== 'right', + 'stk-block-timeline--right': props.attributes.timelinePosition === 'right', + }, + ] ) + + const contentClassNames = classnames( applyFilters( 'stackable.new-block.save.contentClassNames', [ + [ + rowClass, + 'stk-inner-blocks', + blockAlignmentClass, + 'stk-block-content', + ], + getContentAlignmentClasses( props.attributes ), + ], props ) ) + + return ( + + + + { /* */ } +
+ +
+
+ +
+
+ { /*
*/ } +
+ ) +} + +export default compose( + withVersion( VERSION ) +)( Save ) diff --git a/src/block/timeline/schema.js b/src/block/timeline/schema.js new file mode 100644 index 000000000..64e223577 --- /dev/null +++ b/src/block/timeline/schema.js @@ -0,0 +1,97 @@ +import { + Advanced, + BlockDiv, + Style, + ConditionalDisplay, + CustomAttributes, + CustomCSS, + EffectsAnimations, + MarginBottom, + Responsive, + Row, + Transform, + ContentAlign, + Typography, +} from '~stackable/block-components' +import { AttributeObject } from '~stackable/util' +import { version as VERSION } from 'stackable' + +export const attributes = ( version = VERSION ) => { + const attrObject = new AttributeObject() + + BlockDiv.addAttributes( attrObject ) + Style.addAttributes( attrObject ) + MarginBottom.addAttributes( attrObject ) + Row.addAttributes( attrObject ) + Advanced.addAttributes( attrObject ) + Transform.addAttributes( attrObject ) + EffectsAnimations.addAttributes( attrObject ) + CustomAttributes.addAttributes( attrObject ) + CustomCSS.addAttributes( attrObject ) + Responsive.addAttributes( attrObject ) + ConditionalDisplay.addAttributes( attrObject ) + ContentAlign.addAttributes( attrObject ) + Typography.addAttributes( attrObject, '.stk-block-timeline__date' ) + + attrObject.add( { + attributes: { + timelineAnchor: { + type: 'number', + default: '', + stkResponsive: true, + }, + timelinePosition: { + type: 'string', + default: '', + }, + timelineGap: { + type: 'number', + default: '', + stkResponsive: 'all', + }, + timelineDotSize: { + type: 'number', + default: '', + }, + timelineDotBorderRadius: { + type: 'number', + default: '', + }, + timelineThickness: { + type: 'number', + default: '', + }, + timelineOffset: { + type: 'number', + default: '', + }, + timelineAccentColor: { + type: 'string', + default: '', + }, + timelineBackgroundColor: { + type: 'string', + default: '', + }, + }, + versionAdded: '3.0.0', + versionDeprecated: '', + } ) + + attrObject.addDefaultValues( { + attributes: { + // Get the current date in MMM DD, YYYY format + text: new Date().toLocaleDateString( 'en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + } ), + }, + versionAdded: '3.0.0', + versionDeprecated: '', + } ) + + return attrObject.getMerged( version ) +} + +export default attributes( VERSION ) diff --git a/src/block/timeline/style.js b/src/block/timeline/style.js new file mode 100644 index 000000000..34283fec6 --- /dev/null +++ b/src/block/timeline/style.js @@ -0,0 +1,142 @@ +/** + * External dependencies + */ +import { + Advanced, + BlockDiv, + EffectsAnimations, + MarginBottom, + Transform, + Typography, +} from '~stackable/block-components' +import { BlockCss, BlockCssCompiler } from '~stackable/components' + +/** + * WordPress dependencies + */ +import { memo } from '@wordpress/element' + +const typographyOptions = { + selector: '.stk-block-timeline__date', + hoverSelector: '.stk-block-timeline__date:hover', +} + +const Styles = props => { + const propsToPass = { + ...props, + version: props.version, + versionAdded: '3.0.0', + versionDeprecated: '', + } + + return ( + <> + + + + + + + + + + ) +} + +const BlockStyles = memo( props => { + return ( + <> + + + + + + + + + ) +} ) + +BlockStyles.defaultProps = { + version: '', +} + +BlockStyles.Content = props => { + if ( props.attributes.generatedCss ) { + return + } + + return ( + + + + + + + + + + ) +} + +BlockStyles.Content.defaultProps = { + version: '', + attributes: {}, +} + +export default BlockStyles diff --git a/src/block/timeline/style.scss b/src/block/timeline/style.scss new file mode 100644 index 000000000..837b5a4cd --- /dev/null +++ b/src/block/timeline/style.scss @@ -0,0 +1,141 @@ +@import "common"; + +.stk-block-timeline { + --gap: 16px; + --line-dot-size: 12px; + --line-dot-border-radius: 100%; + --line-accent-bg-color: #000; + --line-accent-bg-color2: var(--line-accent-bg-color, #000); + --line-accent-bg-location: 50%; + --line-bg-color: #eee; + --line-bg-width: 3px; + --content-line: 40px; + --fixed-bg: linear-gradient(to bottom, var(--line-accent-bg-color, #000) 0, var(--line-accent-bg-color2, #000) var(--line-accent-bg-location, 50%), var(--line-bg-color, #eee) var(--line-accent-bg-location, 50%)); +} +.stk-block-timeline > .stk-inner-blocks { + display: grid; + grid-template-columns: 1fr var(--line-dot-size, 16px) 1fr; + grid-template-rows: auto; + column-gap: var(--gap, 16px); + position: relative; + align-items: center; +} +.stk-block-timeline--right .stk-block-timeline__date { + text-align: end; +} +.stk-block-timeline--left > .stk-inner-blocks > * { + grid-row: 1 / 2; +} +.stk-block-timeline--left .stk-block-timeline__content { + grid-column: 1 / 2; + text-align: end; +} +.stk-block-timeline--left .stk-block-timeline__middle { + grid-column: 2 / 3; +} +.stk-block-timeline--left .stk-block-timeline__date { + grid-column: 3 / 4; +} +.stk-block-timeline > .stk-inner-blocks::after { + content: ""; + position: absolute; + inset-inline-start: calc(50% - var(--line-bg-width, 3px) / 2); + width: var(--line-bg-width, 3px); + top: 0; + bottom: 0; + background: var(--fixed-bg); + background-attachment: fixed; +} +.stk-block-timeline__middle { + border-radius: var(--line-dot-border-radius, 100%); + width: var(--line-dot-size, 16px); + height: var(--line-dot-size, 16px); + background: var(--fixed-bg); + background-attachment: fixed; + z-index: 2; + position: relative; +} + +/** Trim the top-most line and the bottom-most line */ +.stk-block-timeline > .stk-inner-blocks::after { + top: 50%; + bottom: 50%; +} +.stk-block-timeline + .stk-block-timeline > .stk-inner-blocks::after { + top: 0; +} +.stk-block-timeline:has(+ .stk-block-timeline) > .stk-inner-blocks::after { + bottom: 0; +} + +/** branching line */ +.stk-block-timeline__middle::after { + content: ""; + position: absolute; + top: calc(50% - var(--line-bg-width, 3px) / 2); + inset-inline-start: 50%; + height: var(--line-bg-width, 3px); + width: var(--content-line, 0); + border-radius: var(--line-bg-width, 3px); + background: var(--fixed-bg); + background-attachment: fixed; +} +.stk-block-timeline--left .stk-block-timeline__middle::after { + inset-inline-start: auto; + inset-inline-end: 50%; +} +.stk-block-timeline--right .stk-block-timeline__content { + margin-inline-start: var(--content-line, 0); +} +.stk-block-timeline--left .stk-block-timeline__content { + margin-inline-end: var(--content-line, 0); +} + +@include mobile { + .stk-block.stk-block-timeline { + --content-line: 0 !important; + } + .stk-block-timeline > .stk-inner-blocks { + grid-template-columns: var(--line-dot-size, 16px) 1fr; + grid-template-rows: auto 1fr; + align-items: flex-start; + } + .stk-block-timeline > .stk-inner-blocks::after { + inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2); + } + .stk-block-timeline .stk-block-timeline__middle { + grid-column: 1 / 2; + grid-row: 1 / 3; + } + .stk-block-timeline .stk-block-timeline__content { + grid-column: 2 / 3; + grid-row: 2 / 3; + text-align: start; + } + .stk-block-timeline .stk-block-timeline__date { + grid-column: 2 / 3; + grid-row: 1 / 2; + text-align: start; + } + + /** Trim the top-most line and the bottom-most line */ + .stk-block-timeline > .stk-inner-blocks::after { + top: calc(var(--line-dot-size, 16px) / 2); + bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2)); + } + .stk-block-timeline + .stk-block-timeline > .stk-inner-blocks::after { + top: -16px; + } + .stk-block-timeline:has(+ .stk-block-timeline) > .stk-inner-blocks::after { + bottom: 0; + } + + // Collapsed timeline sticks too much to the side. + .stk-block-timeline { + padding-inline-start: 16px; + } + // Better spacing when multiple timelines are stacked. + .stk-block-timeline + .stk-block-timeline { + padding-top: 16px; + } +} diff --git a/src/dynamic-breakpoints.php b/src/dynamic-breakpoints.php index a41466c9b..e3aa80dce 100644 --- a/src/dynamic-breakpoints.php +++ b/src/dynamic-breakpoints.php @@ -15,7 +15,7 @@ function stackable_get_responsive_css() { // NOTE: THE VALUE BELOW IS AUTOMATICALLY GENERATED BY THE BUILD PROCESS. return <<.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}}#end-resizable-editor-section{display:none} +#start-resizable-editor-section{display:none}@media only screen and (min-width:1024px){.stk-row{flex-wrap:nowrap}.stk--hide-desktop,.stk--hide-desktop.stk-block{display:none!important}}@media only screen and (min-width:768px){:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(2)){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):last-child){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(3)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(2)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):last-child){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(4)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(3)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(2)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):last-child){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(5)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(4)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(3)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(2)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):last-child){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(6)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(5)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(4)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(3)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):nth-last-child(2)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(6):last-child){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}.stk-block .stk-block.aligncenter,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}.stk-block.stk-block-timeline{--content-line:0!important}.stk-block-timeline>.stk-inner-blocks{align-items:flex-start;grid-template-columns:var(--line-dot-size,16px) 1fr;grid-template-rows:auto 1fr}.stk-block-timeline>.stk-inner-blocks:after{inset-inline-start:calc(var(--line-dot-size, 16px)/2 - var(--line-bg-width, 3px)/2)}.stk-block-timeline .stk-block-timeline__middle{grid-column:1/2;grid-row:1/3}.stk-block-timeline .stk-block-timeline__content{grid-column:2/3;grid-row:2/3;text-align:start}.stk-block-timeline .stk-block-timeline__date{grid-column:2/3;grid-row:1/2;text-align:start}.stk-block-timeline>.stk-inner-blocks:after{bottom:calc(100% - var(--line-dot-size, 16px)/2);top:calc(var(--line-dot-size, 16px)/2)}.stk-block-timeline+.stk-block-timeline>.stk-inner-blocks:after{top:-16px}.stk-block-timeline:has(+.stk-block-timeline)>.stk-inner-blocks:after{bottom:0}.stk-block-timeline{padding-inline-start:16px}.stk-block-timeline+.stk-block-timeline{padding-top:16px}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}}#end-resizable-editor-section{display:none} STK_RESPONSIVE_CSS; } } diff --git a/src/icons/images/timeline-icon.svg b/src/icons/images/timeline-icon.svg new file mode 100644 index 000000000..2599725b1 --- /dev/null +++ b/src/icons/images/timeline-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/index.js b/src/icons/index.js index 9bd146030..be05aaa4c 100644 --- a/src/icons/index.js +++ b/src/icons/index.js @@ -45,6 +45,7 @@ import SVGTableOfContentsIcon from './images/table-of-contents.svg' import SVGTeamMemberIcon from './images/team-member-icon.svg' import SVGTestimonialIcon from './images/testimonial-icon.svg' import SVGTextIcon from './images/text-icon.svg' +import SVGTimelineIcon from './images/timeline-icon.svg' import SVGVideoPopupIcon from './images/video-popup-icon.svg' import SVGUngroupContainerIcon from './images/ungroup-container-icon.svg' @@ -278,6 +279,10 @@ export function TextIcon() { return colorizeIcon( ) } +export function TimelineIcon() { + return colorizeIcon( ) +} + export function VideoPopupIcon() { return colorizeIcon( ) } From 9071531e361ab0d80f1685c5420fb06892497699 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Fri, 23 Jun 2023 10:48:04 +0800 Subject: [PATCH 2/7] added gradient accent color, fixed paddings --- src/block/timeline/edit.js | 41 +++++++++++++++++++++++++++++++--- src/block/timeline/editor.scss | 20 +++++------------ src/block/timeline/schema.js | 8 +++++++ src/block/timeline/style.js | 9 ++++++++ src/block/timeline/style.scss | 27 +++++++++------------- src/dynamic-breakpoints.php | 2 +- 6 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/block/timeline/edit.js b/src/block/timeline/edit.js index 71e74610b..aa524c0a4 100644 --- a/src/block/timeline/edit.js +++ b/src/block/timeline/edit.js @@ -56,13 +56,31 @@ import { InnerBlocks } from '@wordpress/block-editor' const ALLOWED_INNER_BLOCKS = [ 'stackable/column' ] const TEMPLATE = [ - [ 'stackable/column', {}, [ + [ 'stackable/column', { + columnSpacing: { + top: 0, + right: 0, + bottom: 0, + left: 0, + }, + }, [ [ 'stackable/text', { text: _x( 'Description for this block. Use this space for describing your block. Any text will do.', 'Content placeholder', i18n ), } ], ] ], ] +const COLOR_TYPE_CONTROLS = [ + { + value: '', + title: __( 'Single', i18n ), + }, + { + value: 'gradient', + title: __( 'Gradient', i18n ), + }, +] + const Edit = props => { const { className, @@ -142,7 +160,7 @@ const Edit = props => { sliderMax={ 100 } sliderMin={ props.attributes.timelineThickness || 3 } min={ 1 } - placeholder="12" + placeholder="11" /> { + + { props.attributes.timelineAccentColorType === 'gradient' && + + } .stk-inner-blocks > * { @@ -42,13 +41,14 @@ // In tablet and mobile, fixed background shows weirdly, so we remove it. .stk-block-timeline__middle, .stk-block-timeline > .stk-inner-blocks::after { - --fixed-bg: var(--line-accent-bg-color); + --fixed-bg: linear-gradient(to bottom, var(--line-accent-bg-color, #000) 0, var(--line-accent-bg-color-2, #000) var(--line-accent-bg-location, 50%)); } } @include mobile { .editor-styles-wrapper .stk-block.stk-block-timeline { --content-line: 0 !important; + padding-top: 0; } .stk-block-timeline > .stk-inner-blocks { grid-template-columns: var(--line-dot-size, 16px) 1fr; @@ -60,7 +60,8 @@ } .stk-block-timeline .stk-block-timeline__middle { grid-column: 1 / 2; - grid-row: 1 / 3; + grid-row: 1 / 2; + align-self: center; } .stk-block-timeline .stk-block-timeline__content { grid-column: 2 / 3; @@ -75,8 +76,8 @@ /** Trim the top-most line and the bottom-most line */ .stk-block-timeline > .stk-inner-blocks::after { - top: calc(var(--line-dot-size, 16px) / 2); - bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2)); + top: calc(var(--line-dot-size, 16px) / 2 + 0.5em); + bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2) - 0.5em); } [data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline > .stk-inner-blocks::after { top: -16px; @@ -84,13 +85,4 @@ [data-type="stackable/timeline"]:has(+ [data-type="stackable/timeline"]) > .stk-block-timeline > .stk-inner-blocks::after { bottom: 0; } - - // Collapsed timeline sticks too much to the side. - .stk-block-timeline { - padding-inline-start: 16px; - } - // Better spacing when multiple timelines are stacked. - [data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline { - padding-top: 16px; - } } diff --git a/src/block/timeline/schema.js b/src/block/timeline/schema.js index 64e223577..52e20b1c9 100644 --- a/src/block/timeline/schema.js +++ b/src/block/timeline/schema.js @@ -65,10 +65,18 @@ export const attributes = ( version = VERSION ) => { type: 'number', default: '', }, + timelineAccentColorType: { + type: 'string', + default: '', + }, timelineAccentColor: { type: 'string', default: '', }, + timelineAccentColor2: { + type: 'string', + default: '', + }, timelineBackgroundColor: { type: 'string', default: '', diff --git a/src/block/timeline/style.js b/src/block/timeline/style.js index 34283fec6..88cbbdace 100644 --- a/src/block/timeline/style.js +++ b/src/block/timeline/style.js @@ -87,6 +87,15 @@ const Styles = props => { attrName="timelineAccentColor" key="timelineAccentColor" /> + getAttribute( 'timelineAccentColorType' ) === 'gradient' } + dependencies={ [ 'timelineAccentColorType' ] } + /> .stk-inner-blocks { display: grid; grid-template-columns: 1fr var(--line-dot-size, 16px) 1fr; grid-template-rows: auto; column-gap: var(--gap, 16px); - position: relative; + position: static; align-items: center; } .stk-block-timeline--right .stk-block-timeline__date { @@ -94,6 +96,7 @@ @include mobile { .stk-block.stk-block-timeline { --content-line: 0 !important; + padding-top: 0; } .stk-block-timeline > .stk-inner-blocks { grid-template-columns: var(--line-dot-size, 16px) 1fr; @@ -105,7 +108,8 @@ } .stk-block-timeline .stk-block-timeline__middle { grid-column: 1 / 2; - grid-row: 1 / 3; + grid-row: 1 / 2; + align-self: center; } .stk-block-timeline .stk-block-timeline__content { grid-column: 2 / 3; @@ -120,8 +124,8 @@ /** Trim the top-most line and the bottom-most line */ .stk-block-timeline > .stk-inner-blocks::after { - top: calc(var(--line-dot-size, 16px) / 2); - bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2)); + top: calc(var(--line-dot-size, 16px) / 2 + 0.5em); + bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2) - 0.5em); } .stk-block-timeline + .stk-block-timeline > .stk-inner-blocks::after { top: -16px; @@ -129,13 +133,4 @@ .stk-block-timeline:has(+ .stk-block-timeline) > .stk-inner-blocks::after { bottom: 0; } - - // Collapsed timeline sticks too much to the side. - .stk-block-timeline { - padding-inline-start: 16px; - } - // Better spacing when multiple timelines are stacked. - .stk-block-timeline + .stk-block-timeline { - padding-top: 16px; - } } diff --git a/src/dynamic-breakpoints.php b/src/dynamic-breakpoints.php index e3aa80dce..b547689a2 100644 --- a/src/dynamic-breakpoints.php +++ b/src/dynamic-breakpoints.php @@ -15,7 +15,7 @@ function stackable_get_responsive_css() { // NOTE: THE VALUE BELOW IS AUTOMATICALLY GENERATED BY THE BUILD PROCESS. return <<.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}.stk-block.stk-block-timeline{--content-line:0!important}.stk-block-timeline>.stk-inner-blocks{align-items:flex-start;grid-template-columns:var(--line-dot-size,16px) 1fr;grid-template-rows:auto 1fr}.stk-block-timeline>.stk-inner-blocks:after{inset-inline-start:calc(var(--line-dot-size, 16px)/2 - var(--line-bg-width, 3px)/2)}.stk-block-timeline .stk-block-timeline__middle{grid-column:1/2;grid-row:1/3}.stk-block-timeline .stk-block-timeline__content{grid-column:2/3;grid-row:2/3;text-align:start}.stk-block-timeline .stk-block-timeline__date{grid-column:2/3;grid-row:1/2;text-align:start}.stk-block-timeline>.stk-inner-blocks:after{bottom:calc(100% - var(--line-dot-size, 16px)/2);top:calc(var(--line-dot-size, 16px)/2)}.stk-block-timeline+.stk-block-timeline>.stk-inner-blocks:after{top:-16px}.stk-block-timeline:has(+.stk-block-timeline)>.stk-inner-blocks:after{bottom:0}.stk-block-timeline{padding-inline-start:16px}.stk-block-timeline+.stk-block-timeline{padding-top:16px}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}}#end-resizable-editor-section{display:none} +#start-resizable-editor-section{display:none}@media only screen and (min-width:1024px){.stk-row{flex-wrap:nowrap}.stk--hide-desktop,.stk--hide-desktop.stk-block{display:none!important}}@media only screen and (min-width:768px){:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(2)){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):last-child){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(3)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(2)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):last-child){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(4)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(3)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(2)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):last-child){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(5)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(4)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(3)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(2)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):last-child){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(6)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(5)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(4)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(3)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):nth-last-child(2)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(6):last-child){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}.stk-block .stk-block.aligncenter,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.stk-block.stk-block-timeline{--content-line:0!important;padding-top:0}.stk-block-timeline>.stk-inner-blocks{align-items:flex-start;grid-template-columns:var(--line-dot-size,16px) 1fr;grid-template-rows:auto 1fr}.stk-block-timeline>.stk-inner-blocks:after{inset-inline-start:calc(var(--line-dot-size, 16px)/2 - var(--line-bg-width, 3px)/2)}.stk-block-timeline .stk-block-timeline__middle{align-self:center;grid-column:1/2;grid-row:1/2}.stk-block-timeline .stk-block-timeline__content{grid-column:2/3;grid-row:2/3;text-align:start}.stk-block-timeline .stk-block-timeline__date{grid-column:2/3;grid-row:1/2;text-align:start}.stk-block-timeline>.stk-inner-blocks:after{bottom:calc(100% - var(--line-dot-size, 16px)/2 - .5em);top:calc(var(--line-dot-size, 16px)/2 + .5em)}.stk-block-timeline+.stk-block-timeline>.stk-inner-blocks:after{top:-16px}.stk-block-timeline:has(+.stk-block-timeline)>.stk-inner-blocks:after{bottom:0}}#end-resizable-editor-section{display:none} STK_RESPONSIVE_CSS; } } From c03681d895d05b2466e2c37e2937d38d8487d54d Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Fri, 23 Jun 2023 11:38:47 +0800 Subject: [PATCH 3/7] fix: position of accent in editor --- src/block/timeline/editor.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/block/timeline/editor.scss b/src/block/timeline/editor.scss index 7578dcd5e..f505ae857 100644 --- a/src/block/timeline/editor.scss +++ b/src/block/timeline/editor.scss @@ -7,6 +7,9 @@ gap: var(--gap, 16px); align-items: center; } +.stk-block-timeline { + position: relative !important; +} .stk-block-timeline--left > .stk-inner-blocks > * { grid-row: 1 / 2; } From 81684b553855e5ff320b88e1bdf124c9ca11c938 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Fri, 23 Jun 2023 13:26:48 +0800 Subject: [PATCH 4/7] added succeeding accent timeline styling --- src/block/timeline/edit.js | 1 + src/block/timeline/style.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/block/timeline/edit.js b/src/block/timeline/edit.js index aa524c0a4..3d38bbe98 100644 --- a/src/block/timeline/edit.js +++ b/src/block/timeline/edit.js @@ -150,6 +150,7 @@ const Edit = props => { min={ 0 } placeholder="50" responsive="all" + help={ __( 'Succeeding timeline blocks will also use this value.', i18n ) } /> diff --git a/src/block/timeline/style.js b/src/block/timeline/style.js index 88cbbdace..5e783970f 100644 --- a/src/block/timeline/style.js +++ b/src/block/timeline/style.js @@ -33,7 +33,7 @@ const Styles = props => { <> Date: Tue, 11 Jul 2023 14:12:37 +0800 Subject: [PATCH 5/7] added timeline in php register block --- src/blocks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks.php b/src/blocks.php index 4a589e08a..63f6d405a 100644 --- a/src/blocks.php +++ b/src/blocks.php @@ -99,6 +99,7 @@ function stackable_get_stk_wrapper_block_folders_metadata() { 'pricing-box', 'team-member', 'testimonial', + 'timeline', 'video-popup', 'horizontal-scroller', ); From d62c9beeeee15005bb5c408caeb68760b84b1a43 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia <98727316+mxkae@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:24:52 +0800 Subject: [PATCH 6/7] fix (timeline block): major issues (#2738) * fixes gap * removed min height and vertical align * removed transforms * fix timeline date text gradient color * set typography initialOpen to false * fix timeline date gradient color in preview * fix alignment when background is enabled mobile * Changed Thickness to Line Thickness * fix background overlap * fix inner column content error when refreshing * fix inner column placeholder column spacing * fix margin in frontend for blocksy and 2021 theme * fix margin for astra theme * fix content margin when dot size increases * Removed bottom margin of timeline block so it's not annoying * new vertical lines/dots in editor * fix vertical line and dots fill * removed :has * removed hasTransparent * fixed setAttribute for last timeline block * added nextBlock and previousBlock in getBlockContext * use new nextBlock, previousBlock update vertical line max height --------- Co-authored-by: Benjamin Intal --- src/block-components/block-div/edit.js | 1 + src/block-components/helpers/size/edit.js | 5 +- src/block/carousel/edit.js | 1 - src/block/column/edit.js | 7 +- src/block/timeline/edit.js | 230 +++++++++++++++++- src/block/timeline/editor.scss | 130 ++++++++-- src/block/timeline/save.js | 10 +- src/block/timeline/schema.js | 4 + src/block/timeline/style.scss | 28 ++- src/compatibility/astra/editor.scss | 11 + .../resizable-bottom-margin/index.js | 4 +- src/hooks/use-block-context.js | 6 +- src/styles/editor-block.scss | 2 +- 13 files changed, 387 insertions(+), 52 deletions(-) create mode 100644 src/compatibility/astra/editor.scss diff --git a/src/block-components/block-div/edit.js b/src/block-components/block-div/edit.js index 23cb0caaf..e85cdce29 100644 --- a/src/block-components/block-div/edit.js +++ b/src/block-components/block-div/edit.js @@ -46,6 +46,7 @@ export const Edit = props => { initialOpen={ initialOpen === 'spacing' } > { return ( <> - { description: __( 'Adjusts the minimum allowable height of the block', i18n ), } } visualGuide={ props.visualGuide } - /> + /> } { props.hasContentVerticalAlign && { Layout.defaultProps = { attrNameTemplate: '%s', + hasMinHeight: true, hasContentVerticalAlign: true, visualGuide: null, labels: {}, diff --git a/src/block/carousel/edit.js b/src/block/carousel/edit.js index ff869b3aa..ab76600fe 100644 --- a/src/block/carousel/edit.js +++ b/src/block/carousel/edit.js @@ -492,7 +492,6 @@ const Edit = props => { { const { - hasInnerBlocks, isOnlyBlock, + hasInnerBlocks, isOnlyBlock, parentBlock, } = useBlockContext() const { @@ -66,6 +66,9 @@ const Edit = props => { const [ columnClass, columnWrapperClass ] = getColumnClasses( props.attributes ) const blockAlignmentClass = getAlignmentClasses( props.attributes ) + const nonZeroColumnSpacingBlocks = [ 'stackable/timeline' ] + const useZeroColumnSpacing = ! nonZeroColumnSpacingBlocks.includes( parentBlock.name ) + const ALLOWED_INNER_BLOCKS = applyFilters( 'stackable.block.column.allowed-inner-blocks', undefined, props ) const blockClassNames = classnames( [ @@ -99,7 +102,7 @@ const Edit = props => { defaultLocked={ true } min={ [ 0, 0 ] } sliderMax={ [ 200, 30 ] } - placeholder={ isOnlyBlock ? '0' : '12' } + placeholder={ isOnlyBlock && useZeroColumnSpacing ? '0' : '12' } helpTooltip={ { video: 'inner-block-padding', description: __( 'Sets the paddings between the column content and the border.', i18n ), diff --git a/src/block/timeline/edit.js b/src/block/timeline/edit.js index 3d38bbe98..9b22b93c9 100644 --- a/src/block/timeline/edit.js +++ b/src/block/timeline/edit.js @@ -24,6 +24,7 @@ import { useGeneratedCss, MarginBottom, getRowClasses, + getTypographyClasses, getAlignmentClasses, Advanced, CustomCSS, @@ -32,12 +33,11 @@ import { EffectsAnimations, ConditionalDisplay, getSeparatorClasses, - Transform, ContentAlign, getContentAlignmentClasses, Typography, } from '~stackable/block-components' -import { useBlockContext } from '~stackable/hooks' +import { useBlockContext, useDeviceType } from '~stackable/hooks' import { withBlockAttributeContext, withBlockWrapperIsHovered, @@ -52,6 +52,11 @@ import { __, _x, sprintf, } from '@wordpress/i18n' import { InnerBlocks } from '@wordpress/block-editor' +import { addFilter } from '@wordpress/hooks' +import { dispatch } from '@wordpress/data' +import { + useEffect, useRef, useState, +} from '@wordpress/element' const ALLOWED_INNER_BLOCKS = [ 'stackable/column' ] @@ -86,6 +91,7 @@ const Edit = props => { className, clientId, isSelected, + setAttributes, } = props useGeneratedCss( props.attributes ) @@ -93,7 +99,24 @@ const Edit = props => { const rowClass = getRowClasses( props.attributes ) const separatorClass = getSeparatorClasses( props.attributes ) const blockAlignmentClass = getAlignmentClasses( props.attributes ) - const { hasInnerBlocks } = useBlockContext() + const typographyClass = getTypographyClasses( props.attributes ) + const { + hasInnerBlocks, nextBlock, previousBlock, + } = useBlockContext() + const deviceType = useDeviceType() + + const middleRef = useRef() + const branchRef = useRef() + const blockRef = useRef() + const [ middleTopPosition, setMiddleTopPosition ] = useState( { dot: 0, branch: 0 } ) + const [ fillHeight, setFillHeight ] = useState( { verticalLine: 0, middle: 0 } ) + const [ verticalLineMaxHeight, setVerticalLineMaxHeight ] = useState( 0 ) + const [ verticalLineTopPosition, setVerticalLineTopPosition ] = useState( 0 ) + + const dateClassNames = classnames( [ + typographyClass, + 'stk-block-timeline__date', + ] ) const blockClassNames = classnames( [ className, @@ -103,6 +126,7 @@ const Edit = props => { { 'stk-block-timeline--left': props.attributes.timelinePosition !== 'right', 'stk-block-timeline--right': props.attributes.timelinePosition === 'right', + 'stk-block-timeline--last': props.attributes.timelineIsLast, }, ] ) @@ -112,6 +136,137 @@ const Edit = props => { 'stk-block-content', ], getContentAlignmentClasses( props.attributes ) ) + // check if timeline block is single, is first block, or is last block + const getTimelinePosition = () => { + return { + isFirst: ! previousBlock || previousBlock.name !== 'stackable/timeline', + isLast: ! nextBlock || nextBlock.name !== 'stackable/timeline', + } + } + + const getSelectAttributes = () => { + return { + timelineAnchor: + props.attributes.timelineAnchor === '' ? 0.5 : ( props.attributes.timelineAnchor / 100 ), + topPadding: + props.attributes.blockPadding && props.attributes.blockPadding.top !== '' ? props.attributes.blockPadding.top : 16, + topPaddingZero: + props.attributes.blockPadding && props.attributes.blockPadding.top !== '' ? props.attributes.blockPadding.top : 0, + bottomPadding: + props.attributes.blockPadding && props.attributes.blockPadding.bottom !== '' ? props.attributes.blockPadding.bottom : 16, + topPaddingTablet: + props.attributes.blockPaddingTablet && props.attributes.blockPaddingTablet.top !== '' ? props.attributes.blockPaddingTablet.top : 16, + bottomPaddingTablet: + props.attributes.blockPaddingTablet && props.attributes.blockPaddingTablet.bottom !== '' ? props.attributes.blockPaddingTablet.bottom : 16, + topPaddingMobileZero: + props.attributes.blockPaddingMobile && props.attributes.blockPaddingMobile.top !== '' ? props.attributes.blockPaddingMobile.top : 0, + bottomPaddingMobileZero: + props.attributes.blockPaddingMobile && props.attributes.blockPaddingMobile.bottom !== '' ? props.attributes.blockPaddingMobile.bottom : 0, + } + } + + const handleScroll = () => { + const { + timelineAnchor, topPadding, topPaddingZero, + } = getSelectAttributes() + + const lineHeight = ( document.body.clientHeight * timelineAnchor ) - ( blockRef.current.getBoundingClientRect().top ) + topPaddingZero + const fillPercent = ( lineHeight / blockRef.current.getBoundingClientRect().height ) * 100 + + // gets equivalent px of 1% + const fillPxPercent = blockRef.current.getBoundingClientRect().height / 100 + // converts percent to px + const fillPx = fillPercent * fillPxPercent + + let fill = { verticalLine: fillPx, middle: fillPx } + + const dot = ( middleRef.current.getBoundingClientRect().top - blockRef.current.getBoundingClientRect().top ) + topPadding + const branch = ( branchRef.current.getBoundingClientRect().top - blockRef.current.getBoundingClientRect().top ) + topPadding + + // corrects the position of the fill for the first timeline block since the line starts at the middle + if ( ! previousBlock || previousBlock.name !== 'stackable/timeline' ) { + fill = { + verticalLine: fillPx - dot, + middle: fillPx, + } + } + + setMiddleTopPosition( { dot, branch } ) + setFillHeight( fill ) + } + + const updateMaxHeight = () => { + const { + isFirst, isLast, + } = getTimelinePosition() + + const { + topPadding, bottomPadding, + topPaddingTablet, bottomPaddingTablet, + topPaddingMobileZero, bottomPaddingMobileZero, + } = getSelectAttributes() + + let lineMaxHeight = '100%' + let top = '' + if ( deviceType === 'Mobile' && isFirst ) { + lineMaxHeight = `calc(100% + ${ bottomPaddingMobileZero }px - ${ topPaddingMobileZero }px - 16px)` + top = `${ topPaddingMobileZero + 16 }px` + } else if ( deviceType === 'Mobile' && isLast ) { + lineMaxHeight = `${ topPaddingMobileZero + 16 }px` + } else if ( deviceType === 'Tablet' && isFirst ) { + lineMaxHeight = `calc(50% + ${ bottomPaddingTablet / 2 }px - ${ topPaddingTablet / 2 }px)` + } else if ( deviceType === 'Tablet' && isLast ) { + lineMaxHeight = `calc(50% + ${ topPaddingTablet / 2 }px - ${ bottomPaddingTablet / 2 }px)` + } else if ( deviceType === 'Mobile' ) { + lineMaxHeight = '100%' + } else if ( isFirst && isLast ) { + lineMaxHeight = '0' + } else if ( isFirst ) { + lineMaxHeight = `calc(50% + ${ bottomPadding / 2 }px - ${ topPadding / 2 }px)` + } else if ( isLast ) { + lineMaxHeight = `calc(50% + ${ topPadding / 2 }px - ${ bottomPadding / 2 }px)` + } + + setVerticalLineMaxHeight( lineMaxHeight ) + setVerticalLineTopPosition( top ) + handleScroll() + } + + // update max height when device type & padding changes + useEffect( () => { + updateMaxHeight() + }, [ deviceType, + props.attributes.blockPadding, + props.attributes.blockPaddingTablet, + props.attributes.blockPaddingMobile ] ) + + // update accent fill when anchor position or padding changes + useEffect( () => { + document.querySelector( '.interface-interface-skeleton__content' )?.addEventListener( 'scroll', handleScroll ) + handleScroll() + return () => { + document.querySelector( '.interface-interface-skeleton__content' )?.removeEventListener( 'scroll', handleScroll ) + } + }, [ props.attributes.timelineAnchor, + props.attributes.blockPadding, + props.attributes.blockPaddingTablet, + props.attributes.blockPaddingMobile, + nextBlock ] ) + + // update blocks if position changes + useEffect( () => { + // set attribute for frontend + if ( nextBlock && nextBlock.name === 'stackable/timeline' && props.attributes.timelineIsLast ) { + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { timelineIsLast: false } ) + } else if ( ! nextBlock || nextBlock.name !== 'stackable/timeline' ) { + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { timelineIsLast: true } ) + } + + updateMaxHeight() + }, [ nextBlock ] ) + return ( <> { isSelected && ( @@ -171,7 +326,7 @@ const Edit = props => { placeholder="" /> { : __( 'Timeline Accent Color', i18n ) } attribute="timelineAccentColor" - hasTransparent={ true } /> { props.attributes.timelineAccentColorType === 'gradient' && } @@ -221,16 +373,15 @@ const Edit = props => { { ...props } hasTextTag={ false } isMultiline={ true } - initialOpen={ true } + initialOpen={ false } hasTextShadow={ true } /> - + - @@ -254,6 +405,7 @@ const Edit = props => { { ! hasInnerBlocks && }
{ > -
+
+
+
+
+
+
+
+
+
+
+
+
@@ -286,3 +482,11 @@ export default compose( withQueryLoopContext, withBlockAttributeContext, )( Edit ) + +// Change the default bottom margin to 0 for the timeline block because this +// block is usually used multiple times in a page and it can be annoying having +// to remove the bottom margin every time. This works in conjunction with the +// margin-bottom set in style.scss +addFilter( 'stackable.resizable-bottom-margin.default', 'stackable/timeline', ( defaultBottomMargin, blockName ) => { + return blockName === 'stackable/timeline' ? 0 : defaultBottomMargin +} ) diff --git a/src/block/timeline/editor.scss b/src/block/timeline/editor.scss index f505ae857..76e12b424 100644 --- a/src/block/timeline/editor.scss +++ b/src/block/timeline/editor.scss @@ -4,7 +4,7 @@ display: grid !important; grid-template-columns: 1fr var(--line-dot-size, 16px) 1fr; grid-template-rows: auto; - gap: var(--gap, 16px); + column-gap: var(--gap, 16px); align-items: center; } .stk-block-timeline { @@ -13,28 +13,99 @@ .stk-block-timeline--left > .stk-inner-blocks > * { grid-row: 1 / 2; } +// prevents content to be overlapped by background +.stk-block-timeline > .stk-inner-blocks > * { + z-index: 2; +} + +.stk-block-timeline > .stk-inner-blocks::after, +.stk-block-timeline__middle, +.stk-block-timeline__middle::after { + background: none !important; +} +.stk-block-timeline__middle::after, .stk-block-timeline > .stk-inner-blocks::after { - content: ""; + content: none !important; +} + +.stk-block-timeline__middle { + z-index: 2 !important; +} +.stk-block-timeline__middle__dot { + border-radius: var(--line-dot-border-radius, 100%); + width: var(--line-dot-size, 16px); + height: var(--line-dot-size, 16px); + position: relative; + z-index: 2; +} +.stk-block-timeline__middle__dot, +.stk-block-timeline__vertical-line { + overflow: hidden; + background: var(--line-bg-color, #eee) !important; +} +.stk-block-timeline__middle__fill { + position: absolute; + width: 100%; + background: var(--line-accent-bg-color, #000) !important; +} +.stk-block-timeline__vertical-line { position: absolute; + top: 0px; inset-inline-start: calc(50% - var(--line-bg-width, 3px) / 2); width: var(--line-bg-width, 3px); - top: 0; - bottom: 0; - background: var(--fixed-bg); - background-attachment: fixed; + height: 100%; + bottom: unset; } -/** Trim the top-most line and the bottom-most line */ -.stk-block-timeline > .stk-inner-blocks::after { - top: 50%; - bottom: 50%; +.stk-block-timeline__vertical-line__fill { + position: absolute; + width: var(--line-bg-width, 3px); + background: var(--line-accent-bg-color, #000) !important; + max-height: 100%; + z-index: 3 !important; } -[data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline > .stk-inner-blocks::after { - top: 0; + +// branching line +.stk-block-timeline__middle__branch { + position: absolute; + top: calc(50% - var(--line-bg-width, 3px) / 2); + inset-inline-start: 50%; + height: var(--line-bg-width, 3px); + width: var(--content-line, 0); + border-radius: var(--line-bg-width, 3px); + background: var(--line-bg-color, #eee) !important; + overflow: hidden; } -[data-type="stackable/timeline"]:has(+ [data-type="stackable/timeline"]) > .stk-block-timeline > .stk-inner-blocks::after { +.stk-block-timeline__middle__branch__fill { + position: absolute; + top: calc(50% - var(--line-bg-width, 3px) / 2); + width: var(--content-line, 0); + background: var(--line-accent-bg-color, #000) !important; +} +.stk-block-timeline--left .stk-block-timeline__middle__branch { + inset-inline-start: auto; + inset-inline-end: 50%; +} + +// for first timeline block +[data-type="stackable/timeline"]:not([data-type="stackable/timeline"] + [data-type="stackable/timeline"]) > .stk-block-timeline .stk-block-timeline__vertical-line { + top: unset; bottom: 0; } +[data-type="stackable/timeline"]:is([data-type="stackable/timeline"] + style + [data-type="stackable/timeline"]) > .stk-block-timeline .stk-block-timeline__vertical-line { + top: 0; +} + +[data-type="stackable/timeline"]:not([data-type="stackable/timeline"] + [data-type="stackable/timeline"]) > .stk-block-timeline .stk-block-timeline__vertical-line__fill { + top: calc(var(--line-dot-size, 16px) / -2); + max-height: calc(100% + var(--line-dot-size, 16px)); +} + +// set max height to 0 if timeline block is single +[data-type="stackable/timeline"]:not([data-type="stackable/timeline"] + [data-type="stackable/timeline"]) > .stk-block-timeline.stk-block-timeline--last .stk-block-timeline__vertical-line { + max-height: 0; +} + // Remove the top 8px gap. [data-type="stackable/timeline"] + [data-type="stackable/timeline"] { margin-top: 0 !important; @@ -46,6 +117,16 @@ .stk-block-timeline > .stk-inner-blocks::after { --fixed-bg: linear-gradient(to bottom, var(--line-accent-bg-color, #000) 0, var(--line-accent-bg-color-2, #000) var(--line-accent-bg-location, 50%)); } + .stk-block-timeline__middle__branch__fill, + .stk-block-timeline__vertical-line__fill, + .stk-block-timeline__middle__fill { + background: transparent !important; + } + .stk-block-timeline__middle__branch, + .stk-block-timeline__vertical-line, + .stk-block-timeline__middle__dot { + background: var(--line-accent-bg-color, #000) !important; + } } @include mobile { @@ -57,9 +138,13 @@ grid-template-columns: var(--line-dot-size, 16px) 1fr; grid-template-rows: auto 1fr; align-items: flex-start; + padding-left: 16px; } .stk-block-timeline > .stk-inner-blocks::after { - inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2); + inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2 + 16px); + } + .stk-block-timeline.stk-block-background .stk-block-timeline__middle { + margin-left: -16px; } .stk-block-timeline .stk-block-timeline__middle { grid-column: 1 / 2; @@ -76,16 +161,15 @@ grid-row: 1 / 2; text-align: start; } - - /** Trim the top-most line and the bottom-most line */ - .stk-block-timeline > .stk-inner-blocks::after { - top: calc(var(--line-dot-size, 16px) / 2 + 0.5em); - bottom: calc(100% - calc(var(--line-dot-size, 16px) / 2) - 0.5em); + .stk-block-timeline__vertical-line { + inset-inline-start: calc(16px + var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2); } - [data-type="stackable/timeline"] + [data-type="stackable/timeline"] > .stk-block-timeline > .stk-inner-blocks::after { - top: -16px; + + [data-type="stackable/timeline"]:not([data-type="stackable/timeline"] + [data-type="stackable/timeline"]) > .stk-block-timeline .stk-block-timeline__vertical-line { + top: 16px; } - [data-type="stackable/timeline"]:has(+ [data-type="stackable/timeline"]) > .stk-block-timeline > .stk-inner-blocks::after { - bottom: 0; + + [data-type="stackable/timeline"]:is([data-type="stackable/timeline"] + style + [data-type="stackable/timeline"]) > .stk-block-timeline .stk-block-timeline__vertical-line { + top: 0; } } diff --git a/src/block/timeline/save.js b/src/block/timeline/save.js index 2be67fc40..3ec1ef077 100644 --- a/src/block/timeline/save.js +++ b/src/block/timeline/save.js @@ -15,6 +15,7 @@ import { getAlignmentClasses, getResponsiveClasses, getRowClasses, + getTypographyClasses, // Separator, getSeparatorClasses, getContentAlignmentClasses, @@ -36,8 +37,14 @@ export const Save = props => { const rowClass = getRowClasses( props.attributes ) const separatorClass = getSeparatorClasses( props.attributes ) const blockAlignmentClass = getAlignmentClasses( props.attributes ) + const typographyClass = getTypographyClasses( props.attributes ) const responsiveClass = getResponsiveClasses( props.attributes ) + const dateClassNames = classnames( [ + typographyClass, + 'stk-block-timeline__date', + ] ) + const blockClassName = classnames( [ props.className, 'stk-block-timeline', @@ -46,6 +53,7 @@ export const Save = props => { { 'stk-block-timeline--left': props.attributes.timelinePosition !== 'right', 'stk-block-timeline--right': props.attributes.timelinePosition === 'right', + 'stk-is-last': props.attributes.timelineIsLast, }, ] ) @@ -71,7 +79,7 @@ export const Save = props => {
{ type: 'string', default: '', }, + timelineIsLast: { + type: 'boolean', + default: false, + }, }, versionAdded: '3.0.0', versionDeprecated: '', diff --git a/src/block/timeline/style.scss b/src/block/timeline/style.scss index 06f043f52..f75d2bd79 100644 --- a/src/block/timeline/style.scss +++ b/src/block/timeline/style.scss @@ -11,8 +11,16 @@ --line-bg-width: 3px; --content-line: 40px; --fixed-bg: linear-gradient(to bottom, var(--line-accent-bg-color, #000) 0, var(--line-accent-bg-color-2, #000) var(--line-accent-bg-location, 50%), var(--line-bg-color, #eee) var(--line-accent-bg-location, 50%)); + --stk-block-margin-bottom: 0; position: relative; padding: 16px 0; + margin-block-start: 0 !important; +} +// Remove the default bottom margin of the timeline block because it's annoying +// to keep on removing it when you want to stack them. This works in conjunction +// with the stackable.resizable-bottom-margin.default filter in edit.js. +.stk-block.stk-block-timeline { + margin-bottom: 0; } .stk-block-timeline > .stk-inner-blocks { display: grid; @@ -38,6 +46,10 @@ .stk-block-timeline--left .stk-block-timeline__date { grid-column: 3 / 4; } +// prevents content to be overlapped by background +.stk-block-timeline > .stk-inner-blocks > * { + z-index: 2; +} .stk-block-timeline > .stk-inner-blocks::after { content: ""; position: absolute; @@ -47,6 +59,7 @@ bottom: 0; background: var(--fixed-bg); background-attachment: fixed; + z-index: 2; } .stk-block-timeline__middle { border-radius: var(--line-dot-border-radius, 100%); @@ -66,7 +79,7 @@ .stk-block-timeline + .stk-block-timeline > .stk-inner-blocks::after { top: 0; } -.stk-block-timeline:has(+ .stk-block-timeline) > .stk-inner-blocks::after { +.stk-block-timeline:not(.stk-is-last) > .stk-inner-blocks::after { bottom: 0; } @@ -87,10 +100,10 @@ inset-inline-end: 50%; } .stk-block-timeline--right .stk-block-timeline__content { - margin-inline-start: var(--content-line, 0); + margin-inline-start: max(calc(var(--content-line, 0) - calc(var(--line-dot-size, 0) * 0.5)), 0px); } .stk-block-timeline--left .stk-block-timeline__content { - margin-inline-end: var(--content-line, 0); + margin-inline-end: max(calc(var(--content-line, 0) - calc(var(--line-dot-size, 0) * 0.5)), 0px); } @include mobile { @@ -102,9 +115,13 @@ grid-template-columns: var(--line-dot-size, 16px) 1fr; grid-template-rows: auto 1fr; align-items: flex-start; + padding-left: 16px; } .stk-block-timeline > .stk-inner-blocks::after { - inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2); + inset-inline-start: calc(var(--line-dot-size, 16px) / 2 - var(--line-bg-width, 3px) / 2 + 16px); + } + .stk-block-timeline.stk-block-background .stk-block-timeline__middle { + margin-left: -16px; } .stk-block-timeline .stk-block-timeline__middle { grid-column: 1 / 2; @@ -130,7 +147,4 @@ .stk-block-timeline + .stk-block-timeline > .stk-inner-blocks::after { top: -16px; } - .stk-block-timeline:has(+ .stk-block-timeline) > .stk-inner-blocks::after { - bottom: 0; - } } diff --git a/src/compatibility/astra/editor.scss b/src/compatibility/astra/editor.scss new file mode 100644 index 000000000..c0f6195eb --- /dev/null +++ b/src/compatibility/astra/editor.scss @@ -0,0 +1,11 @@ +.stk--is-astra-theme { + .is-root-container > [data-type^="stackable/timeline"][data-align="full"] { + margin-left: 0 !important; + margin-right: 0 !important; + } + + [data-type^="stackable/timeline"]:is([data-align="full"], [data-align="wide"]) .stk-block-content.alignwide { + margin-left: auto !important; + margin-right: auto !important; + } +} diff --git a/src/components/resizable-bottom-margin/index.js b/src/components/resizable-bottom-margin/index.js index e0bcc2a9b..78af92b74 100644 --- a/src/components/resizable-bottom-margin/index.js +++ b/src/components/resizable-bottom-margin/index.js @@ -18,6 +18,7 @@ import { range } from 'lodash' * WordPress dependencies */ import { useState, useEffect } from '@wordpress/element' +import { useBlockEditContext } from '@wordpress/block-editor' import { applyFilters } from '@wordpress/hooks' import { ResizableBox } from '@wordpress/components' @@ -48,6 +49,7 @@ const ENABLE = { const ResizableBottomMarginSingle = props => { const { deviceType } = props + const { name } = useBlockEditContext() const [ currentHeight, setCurrentHeight ] = useState( null ) const [ isResizing, setIsResizing ] = useState( false ) @@ -58,7 +60,7 @@ const ResizableBottomMarginSingle = props => { }, [ isShiftKey ] ) // Allow other developers to override the default bottom margin value. - const defaultBottomMargin = applyFilters( 'stackable.resizable-bottom-margin.default', DEFAULT_BOTTOM_MARGINS[ deviceType ] ) + const defaultBottomMargin = applyFilters( 'stackable.resizable-bottom-margin.default', DEFAULT_BOTTOM_MARGINS[ deviceType ], name ) const classNames = classnames( [ 'stk-resizable-bottom-margin', diff --git a/src/hooks/use-block-context.js b/src/hooks/use-block-context.js index e0347aed4..d1d2e3c1f 100644 --- a/src/hooks/use-block-context.js +++ b/src/hooks/use-block-context.js @@ -40,7 +40,7 @@ const STORE_REDUCER = ( state = {}, action ) => { case 'UPDATE_BLOCK_TREE': { const blocks = {} - action.blockTree.forEach( rootBlock => { + action.blockTree.forEach( ( rootBlock, index, siblingBlocks ) => { // Gather information about the root block. const { clientId, innerBlocks, name, @@ -48,6 +48,8 @@ const STORE_REDUCER = ( state = {}, action ) => { blocks[ clientId ] = { numInnerBlocks: innerBlocks.length, hasInnerBlocks: !! innerBlocks.length, + nextBlock: nth( siblingBlocks, index + 1 ), + previousBlock: index === 0 ? undefined : nth( siblingBlocks, index - 1 ), // nth will loop back to the last if index is -1. innerBlocks, rootBlockClientId: clientId, parentTree: [], @@ -96,6 +98,8 @@ const STORE_REDUCER = ( state = {}, action ) => { adjacentBlock: nth( innerBlocks, ! isLastBlock ? index + 1 : index - 1 ), adjacentBlockIndex: ! isLastBlock ? index + 1 : index - 1, adjacentBlocks: innerBlocks || [], + nextBlock: nth( innerBlocks, index + 1 ), + previousBlock: index === 0 ? undefined : nth( innerBlocks, index - 1 ), // nth will loop back to the last if index is -1. numInnerBlocks: block.innerBlocks.length, hasInnerBlocks: !! block.innerBlocks.length, innerBlocks: block.innerBlocks, diff --git a/src/styles/editor-block.scss b/src/styles/editor-block.scss index 44bce4db1..8de798cd3 100644 --- a/src/styles/editor-block.scss +++ b/src/styles/editor-block.scss @@ -36,7 +36,7 @@ // Add small margin between blocks so that placing your mouse between blocks // will make the block appender appear. -.is-root-container > [data-block][data-type^="stackable/"]:not([data-type^="stackable/image"], [data-align="full"]) ~ [data-block][data-type^="stackable/"]:not([data-type^="stackable/image"], [data-align="full"]) { +.is-root-container > [data-block][data-type^="stackable/"]:not([data-type^="stackable/image"], [data-type^="stackable/timeline"], [data-align="full"]) ~ [data-block][data-type^="stackable/"]:not([data-type^="stackable/image"], [data-type^="stackable/timeline"], [data-align="full"]) { margin-top: 8px; } // Add small margin between blocks so that placing your mouse between blocks From a3b4a53f6850e80cee69b278d0cee093ef94daa2 Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 3 Aug 2023 13:32:16 +0800 Subject: [PATCH 7/7] update dependency list --- src/block/timeline/edit.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/block/timeline/edit.js b/src/block/timeline/edit.js index 9b22b93c9..60fb12028 100644 --- a/src/block/timeline/edit.js +++ b/src/block/timeline/edit.js @@ -251,7 +251,8 @@ const Edit = props => { props.attributes.blockPadding, props.attributes.blockPaddingTablet, props.attributes.blockPaddingMobile, - nextBlock ] ) + nextBlock, + previousBlock ] ) // update blocks if position changes useEffect( () => {