├── .gitattributes
├── src
├── styles
│ ├── style
│ │ ├── _background-parallax.scss
│ │ ├── _horizontal-gutter.scss
│ │ ├── _shadow.scss
│ │ ├── _background-radius.scss
│ │ ├── _radius.scss
│ │ ├── _filters.scss
│ │ ├── _padding.scss
│ │ ├── _margin-bottom.scss
│ │ ├── _margin-top.scss
│ │ ├── _background-overlay.scss
│ │ ├── _margin-left.scss
│ │ ├── _margin-right.scss
│ │ ├── _margin-left-negative.scss
│ │ ├── _margin-right-negative.scss
│ │ ├── _captions.scss
│ │ ├── gutter.scss
│ │ ├── _background-classes.scss
│ │ ├── _carousel.scss
│ │ ├── _bricks.scss
│ │ ├── _grid.scss
│ │ ├── _flickity.scss
│ │ └── _core-themes.scss
│ ├── admin.scss
│ ├── editor
│ │ ├── _gallery-upload.scss
│ │ ├── _icons.scss
│ │ ├── _remove-gallery-item-button.scss
│ │ ├── _is-background-transient.scss
│ │ ├── _gallery-item.scss
│ │ ├── _flickity-arrows.scss
│ │ ├── _radius.scss
│ │ ├── core
│ │ │ ├── _breakpoints.scss
│ │ │ ├── _variables.scss
│ │ │ ├── _colors.scss
│ │ │ └── _mixins.scss
│ │ ├── _inspector.scss
│ │ ├── _inspector-tabs.scss
│ │ └── _core-themes.scss
│ ├── editor.scss
│ └── style.scss
├── blocks
│ ├── carousel
│ │ ├── styles
│ │ │ ├── style.scss
│ │ │ └── editor.scss
│ │ └── components
│ │ │ ├── inspector.js
│ │ │ └── edit.js
│ ├── stacked
│ │ ├── styles
│ │ │ ├── editor.scss
│ │ │ └── style.scss
│ │ ├── components
│ │ │ ├── inspector.js
│ │ │ └── edit.js
│ │ └── index.js
│ └── masonry
│ │ ├── styles
│ │ ├── style.scss
│ │ └── editor.scss
│ │ ├── components
│ │ ├── inspector.js
│ │ └── edit.js
│ │ └── index.js
├── components
│ ├── lightbox
│ │ ├── options.js
│ │ ├── classes.js
│ │ ├── attributes.js
│ │ ├── transforms.js
│ │ ├── index.js
│ │ └── control.js
│ ├── global
│ │ ├── index.js
│ │ ├── styles.js
│ │ ├── classes.js
│ │ ├── transforms.js
│ │ ├── attributes.js
│ │ └── toolbar.js
│ ├── background
│ │ ├── index.js
│ │ ├── styles.js
│ │ ├── transforms.js
│ │ ├── attributes.js
│ │ ├── classes.js
│ │ ├── toolbar.js
│ │ └── panel.js
│ ├── gallery-placeholder.js
│ ├── gallery-dropzone.js
│ ├── responsive-tabs-control.js
│ ├── gallery-upload.js
│ ├── slider-panel.js
│ ├── size-control.js
│ └── gallery-image.js
├── utils
│ ├── caption-options.js
│ ├── link-options.js
│ ├── block-category.js
│ ├── filter-options.js
│ ├── helper.js
│ └── autoplay-options.js
├── js
│ └── block-gallery-masonry.js
├── common.scss
└── blocks.js
├── .eslintignore
├── .babelrc
├── .editorconfig
├── .gitignore
├── includes
├── admin
│ ├── class-block-gallery-footer-text.php
│ ├── class-block-gallery-url-generator.php
│ └── class-block-gallery-action-links.php
├── class-block-gallery-body-classes.php
└── class-block-gallery-block-assets.php
├── README.md
├── CONTRIBUTING.md
├── package.json
├── .eslintrc.json
├── class-block-gallery.php
└── readme.txt
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/src/styles/style/_background-parallax.scss:
--------------------------------------------------------------------------------
1 | .has-parallax {
2 | background-attachment: fixed;
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/style/_horizontal-gutter.scss:
--------------------------------------------------------------------------------
1 | .has-horizontal-gutter {
2 | overflow-x: hidden;
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/admin.scss:
--------------------------------------------------------------------------------
1 | .block-gallery-plugins-gopro {
2 | color: #39b54a;
3 | font-weight: 700;
4 | }
5 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.min.js
2 | **/*.build.js
3 | **/node_modules/**
4 | **/vendor/**
5 | build
6 | coverage
7 | cypress
8 | node_modules
9 | vendor
10 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@wordpress/babel-preset-default"],
3 | "plugins": [
4 | [
5 | "@wordpress/babel-plugin-makepot",
6 | {
7 | "output": "languages/block-gallery-js.pot"
8 | }
9 | ]
10 | ]
11 | }
--------------------------------------------------------------------------------
/src/blocks/carousel/styles/style.scss:
--------------------------------------------------------------------------------
1 | .wp-block-blockgallery-carousel {
2 |
3 | &,
4 | .blockgallery {
5 | height: 100%;
6 | position: relative;
7 | }
8 |
9 | .blockgallery--figure {
10 | height: 100%;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/style/_shadow.scss:
--------------------------------------------------------------------------------
1 | .has-shadow-sml {
2 | box-shadow: 0 0.5vw 2vw -0.25vw rgba(0, 0, 0, 0.2);
3 | }
4 |
5 | .has-shadow-med {
6 | box-shadow: 0 1vw 3vw -0.5vw rgba(0, 0, 0, 0.2);
7 | }
8 |
9 | .has-shadow-lrg {
10 | box-shadow: 0 1.8vw 3vw -0.7vw rgba(0, 0, 0, 0.2);
11 | }
12 |
--------------------------------------------------------------------------------
/src/styles/style/_background-radius.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding a border-radius to the figure elements.
2 | @for $i from 2 through 20 {
3 |
4 | .has-background-border-radius-#{ $i } {
5 | border-radius: #{ $i }px;
6 |
7 | &::before {
8 | border-radius: #{ $i }px;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/lightbox/options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 |
6 | /**
7 | * Style options.
8 | */
9 | const lightboxStyleOptions = [
10 | { value: 'light', label: __( 'Light' ) },
11 | { value: 'dark', label: __( 'Dark' ) },
12 | ];
13 |
14 | export default lightboxStyleOptions;
--------------------------------------------------------------------------------
/src/components/lightbox/classes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS classes
3 | */
4 | function LightboxClasses( attributes ) {
5 | return [
6 | { 'has-lightbox': attributes.lightbox },
7 | { [ `has-lightbox-style-${ attributes.lightboxStyle }` ] : attributes.lightbox && attributes.lightboxStyle },
8 | ];
9 | }
10 |
11 | export default LightboxClasses;
--------------------------------------------------------------------------------
/src/components/lightbox/attributes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set the attributes for this component
3 | * @type {Object}
4 | */
5 | const LightboxAttributes = {
6 | lightbox: {
7 | type: 'boolean',
8 | default: false,
9 | },
10 | lightboxStyle: {
11 | type: 'string',
12 | default: 'light',
13 | },
14 | };
15 |
16 | export default LightboxAttributes;
--------------------------------------------------------------------------------
/src/utils/caption-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 |
6 | /**
7 | * Link options.
8 | */
9 | const captionOptions = [
10 | { value: 'dark', label: __( 'Dark' ) },
11 | { value: 'light', label: __( 'Light' ) },
12 | { value: 'none', label: __( 'None' ) },
13 | ];
14 |
15 | export default captionOptions;
--------------------------------------------------------------------------------
/src/styles/style/_radius.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding a border-radius to the figure elements.
2 | @for $i from 2 through 20 {
3 |
4 | &.has-border-radius-#{ $i } {
5 |
6 | .blockgallery--item img {
7 | border-radius: #{ $i }px;
8 | }
9 |
10 | .blockgallery--item figcaption {
11 | border-radius: 0 0 #{ $i }px #{ $i }px;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/lightbox/transforms.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set the attributes for the component transformations
3 | * @type {Object}
4 | */
5 | function LightboxTransforms( props ) {
6 |
7 | const transforms = {
8 | lightbox: props.lightbox,
9 | lightboxStyle: props.lightboxStyle,
10 | };
11 |
12 | return transforms;
13 | }
14 |
15 | export default LightboxTransforms;
--------------------------------------------------------------------------------
/src/utils/link-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 |
6 | /**
7 | * Link options.
8 | */
9 | const linkOptions = [
10 | { value: 'attachment', label: __( 'Attachment Page' ) },
11 | { value: 'media', label: __( 'Media File' ) },
12 | { value: 'none', label: __( 'None' ) },
13 | ];
14 |
15 | export default linkOptions;
--------------------------------------------------------------------------------
/src/styles/style/_filters.scss:
--------------------------------------------------------------------------------
1 | .has-filter-grayscale img {
2 | filter: grayscale(1);
3 | }
4 |
5 | .has-filter-saturation img {
6 | filter: saturate(1.75);
7 | }
8 |
9 | .has-filter-sepia img {
10 | filter: sepia(0.5);
11 | }
12 |
13 | .has-filter-dim img {
14 | filter: brightness(0.5);
15 | }
16 |
17 | .has-filter-vintage img {
18 | filter: contrast(1.3) saturate(1.5) sepia(0.6);
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/lightbox/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import LightboxAttributes from './attributes';
5 | import LightboxClasses from './classes';
6 | import LightboxTransforms from './transforms';
7 | import LightboxControl from './control';
8 |
9 | /**
10 | * Export
11 | */
12 | export {
13 | LightboxAttributes,
14 | LightboxClasses,
15 | LightboxTransforms,
16 | LightboxControl,
17 | };
18 |
--------------------------------------------------------------------------------
/src/js/block-gallery-masonry.js:
--------------------------------------------------------------------------------
1 | ( function( $ ) {
2 | "use strict";
3 |
4 | var container = $( '.wp-block-blockgallery-masonry ul' );
5 |
6 | $( document ).ready( function () {
7 |
8 | container.imagesLoaded(function(){
9 | container.masonry( {
10 | itemSelector: '.blockgallery--item',
11 | transitionDuration: '0.2s',
12 | percentPosition: true,
13 | } );
14 | });
15 | });
16 |
17 | } )( jQuery );
18 |
--------------------------------------------------------------------------------
/src/components/global/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import GlobalAttributes from './attributes';
5 | import GlobalClasses from './classes';
6 | import GlobalToolbar from './toolbar';
7 | import GlobalTransforms from './transforms';
8 | import GlobalStyles from './styles';
9 |
10 | /**
11 | * Export
12 | */
13 | export {
14 | GlobalAttributes,
15 | GlobalClasses,
16 | GlobalToolbar,
17 | GlobalTransforms,
18 | GlobalStyles,
19 | };
20 |
--------------------------------------------------------------------------------
/src/styles/editor/_gallery-upload.scss:
--------------------------------------------------------------------------------
1 | .components-blockgallery-gallery-item__upload.components-button {
2 | border-radius: 0;
3 | border: none;
4 | box-shadow: none !important;
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | min-height: 100px;
9 | padding: 0 !important;
10 |
11 | .dashicon {
12 | margin-top: 10px;
13 | }
14 |
15 | &:hover,
16 | &:focus {
17 | border: $border-width solid $dark-gray-500;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/block-category.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { getCategories, setCategories } = wp.blocks;
5 |
6 | /**
7 | * Internal dependencies
8 | */
9 | import icons from './icons';
10 |
11 | setCategories( [
12 | // Add a Block Gallery block category
13 | {
14 | slug: 'block-gallery',
15 | title: 'Block Gallery',
16 | icon: icons.logo,
17 | },
18 | ...getCategories().filter( ( { slug } ) => slug !== 'block-gallery' ),
19 | ] );
20 |
--------------------------------------------------------------------------------
/src/components/global/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { getColorClassName } = wp.editor;
5 |
6 | /**
7 | * Background Classes
8 | */
9 | function GlobalStyles( attributes ) {
10 |
11 | const captionColorClass = getColorClassName( 'color', attributes.captionColor );
12 |
13 | const styles = {
14 | color: captionColorClass ? undefined : attributes.customCaptionColor,
15 | };
16 |
17 | return styles;
18 | }
19 |
20 | export default GlobalStyles;
--------------------------------------------------------------------------------
/src/styles/style/_padding.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-bottom to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 20 {
5 |
6 | &.has-padding-#{ $i * 5 } {
7 | padding: #{ $i * 5 }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 20 {
15 |
16 | &.has-padding-mobile-#{ $i * 5 } {
17 | padding: #{ $i * 5 }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # https://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 | indent_style = tab
15 |
16 | [*.yml]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/src/utils/filter-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 |
6 | /**
7 | * Link options.
8 | */
9 | const filterOptions = [
10 | { value: 'grayscale', label: __( 'Grayscale' ) },
11 | { value: 'sepia', label: __( 'Sepia' ) },
12 | { value: 'saturation', label: __( 'Saturation' ) },
13 | { value: 'dim', label: __( 'Dark' ) },
14 | { value: 'vintage', label: __( 'Vintage' ) },
15 | { value: 'none', label: __( 'Original' ) },
16 | ];
17 |
18 | export default filterOptions;
--------------------------------------------------------------------------------
/src/styles/style/_margin-bottom.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-bottom to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-margin-bottom-#{ $i * 5 } {
7 | margin-bottom: #{ $i * 5 }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-margin-bottom-mobile-#{ $i * 5 } {
17 | margin-bottom: #{ $i * 5 }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/style/_margin-top.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-top to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-margin-top-#{ $i * 5 } {
7 | margin-top: #{ round($i * 5 / 2) }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-margin-top-mobile-#{ $i * 5 } {
17 | margin-top: #{ round($i * 5 / 2) }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/style/_background-overlay.scss:
--------------------------------------------------------------------------------
1 | .has-background-overlay {
2 |
3 | position: relative;
4 |
5 | &::before {
6 | background-color: inherit;
7 | bottom: 0;
8 | content: "";
9 | left: 0;
10 | opacity: 0.5;
11 | position: absolute;
12 | right: 0;
13 | top: 0;
14 | }
15 | }
16 |
17 | @for $i from 1 through 9 {
18 |
19 | .has-background-overlay-#{ $i * 10 }::before {
20 | opacity: $i * 0.1;
21 | }
22 | }
23 |
24 | .has-background-overlay:not(.has-background) {
25 | background-color: #000;
26 | }
27 |
--------------------------------------------------------------------------------
/src/styles/style/_margin-left.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-left to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-margin-left-#{ $i * 5 } {
7 | margin-left: #{ round($i * 5 / 2) }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-margin-left-mobile-#{ $i * 5 } {
17 | margin-left: #{ round($i * 5 / 2) }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/style/_margin-right.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-right to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-margin-right-#{ $i * 5 } {
7 | margin-right: #{ round($i * 5 / 2) }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-margin-right-mobile-#{ $i * 5 } {
17 | margin-right: #{ round($i * 5 / 2) }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/style/_margin-left-negative.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-left to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-negative-margin-left-#{ $i * 5 } {
7 | margin-left: -#{ round($i * 5 / 2) }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-negative-margin-left-mobile-#{ $i * 5 } {
17 | margin-left: -#{ round($i * 5 / 2) }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/style/_margin-right-negative.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding margin-right to the figure elements.
2 | @media (min-width: 700px) {
3 | // For desktop.
4 | @for $i from 1 through 10 {
5 |
6 | &.has-negative-margin-right-#{ $i * 5 } {
7 | margin-right: -#{ round($i * 5 / 2) }px !important;
8 | }
9 | }
10 | }
11 |
12 | @media (max-width: 699px) {
13 | // For desktop.
14 | @for $i from 1 through 10 {
15 |
16 | &.has-negative-margin-right-mobile-#{ $i * 5 } {
17 | margin-right: -#{ round($i * 5 / 2) }px !important;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/background/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import BackgroundAttributes from './attributes';
5 | import BackgroundClasses from './classes';
6 | import BackgroundPanel from './panel';
7 | import BackgroundStyles from './styles';
8 | import BackgroundToolbar from './toolbar';
9 | import BackgroundTransforms from './transforms';
10 |
11 | /**
12 | * Export
13 | */
14 | export {
15 | BackgroundAttributes,
16 | BackgroundClasses,
17 | BackgroundPanel,
18 | BackgroundStyles,
19 | BackgroundToolbar,
20 | BackgroundTransforms,
21 | };
22 |
--------------------------------------------------------------------------------
/src/common.scss:
--------------------------------------------------------------------------------
1 | // Block Gallery branding color.
2 | $blockgallery: #1e35b9;
3 |
4 | // Colors.
5 | $blue: #0085ba;
6 | $white: #fff;
7 | $black: rgb(41, 41, 41);
8 | $black-000: #000;
9 |
10 | // Output all blocks
11 | @mixin blockGalleryEditorBlocks {
12 |
13 | .wp-block[data-type="blockgallery/offset"],
14 | .wp-block[data-type="blockgallery/masonry"],
15 | .wp-block[data-type="blockgallery/stacked"],
16 | .wp-block[data-type="blockgallery/carousel"],
17 | .wp-block[data-type="blockgallery/thumbnails"],
18 | .wp-block[data-type="blockgallery/auto-height"] {
19 | @content;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/background/styles.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { getColorClassName } = wp.editor;
5 |
6 | /**
7 | * Background Classes
8 | */
9 | function BackgroundStyles( attributes ) {
10 |
11 | const backgroundClass = getColorClassName( 'background-color', attributes.backgroundColor );
12 |
13 | const styles = {
14 | backgroundImage: attributes.backgroundImg ? `url(${ attributes.backgroundImg })` : undefined,
15 | backgroundColor: backgroundClass ? undefined : attributes.customBackgroundColor,
16 | };
17 |
18 | return styles;
19 | }
20 |
21 | export default BackgroundStyles;
--------------------------------------------------------------------------------
/src/blocks/carousel/styles/editor.scss:
--------------------------------------------------------------------------------
1 | .editor-block-list__block[data-type="blockgallery/carousel"] {
2 |
3 | // Fix fullwidth alignment bug where gutters would not display.
4 | &.editor-block-list__block[data-align="full"] > .editor-block-list__block-edit figure {
5 | width: auto;
6 | }
7 |
8 | // Fullwidth upload image button.
9 | .blockgallery--item-uploader {
10 |
11 | .components-form-file-upload,
12 | .components-blockgallery-gallery-item__upload {
13 | height: 100%;
14 | width: 100%;
15 | }
16 | }
17 |
18 | .flickity-enabled {
19 | position: inherit;
20 | height: 100% !important;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/styles/editor/_icons.scss:
--------------------------------------------------------------------------------
1 | // The icon that displays on the CoBlocks block inserter category.
2 | .components-blockgallery-logo-icon {
3 | position: relative;
4 |
5 | &.components-panel__icon {
6 | margin-left: 8px;
7 | top: 1px;
8 | }
9 |
10 | &--pink {
11 | fill: $blockgallery;
12 | }
13 | }
14 |
15 | // Style our custom colored icons like the default icons.
16 | .components-blockgallery-svg {
17 | .components-panel &,
18 | .components-toolbar &,
19 | .editor-block-navigation__item & {
20 | color: $dark-gray-500;
21 | }
22 | }
23 |
24 | .editor-block-icon.has-colors::after {
25 | color: $dark-gray-500;
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.min.js
2 | **/*.build.js
3 | **/node_modules/**
4 | **/vendor/**
5 | **/dist/blocks.build.js
6 | **/dist/blocks.editor.build.css
7 | **/dist/blocks.style.build.css
8 | **/build/**
9 | **/languages/block-gallery.pot
10 | build
11 | coverage
12 | cypress
13 | node_modules
14 | vendor
15 | *.sublime-project
16 | *.sublime-workspace
17 | .DS_Store
18 | .ftppass
19 | *.cache
20 | sftp.json.github/
21 | src/.DS_Store
22 | dist/blocks.editor.build.css
23 | dist/styles/block-gallery-admin.css
24 | dist/styles/block-gallery-admin.min.css
25 | src/.DS_Store
26 | dist/css/block-gallery-admin.css
27 | dist/css/block-gallery-admin.min.css
28 |
--------------------------------------------------------------------------------
/src/styles/editor/_remove-gallery-item-button.scss:
--------------------------------------------------------------------------------
1 | .components-blockgallery-gallery-item__remove {
2 |
3 | &-wrapper {
4 | background-color: $blue;
5 | display: inline-flex;
6 | padding: 2px;
7 | position: absolute;
8 | right: 2px;
9 | top: 2px;
10 | z-index: 20;
11 | transition: padding 0.1s linear;
12 |
13 | svg {
14 | position: relative;
15 | }
16 | }
17 |
18 | &-button {
19 | box-shadow: none !important;
20 | padding: 0;
21 | color: $white;
22 |
23 | &:hover {
24 | background: $blue !important;
25 | border-color: $blue !important;
26 | color: #fff !important;
27 | opacity: 0.75;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/blocks/stacked/styles/editor.scss:
--------------------------------------------------------------------------------
1 | .editor-block-list__block[data-type="blockgallery/stacked"] {
2 |
3 | // Set a min-height for the "Upload an Image" trigger for this gallery.
4 | .components-blockgallery-gallery-item__upload {
5 | min-height: 100px;
6 | width: 100% !important;
7 | }
8 |
9 | // Fix issue where selected images are not wider than the viewport.
10 | .blockgallery--item:not(.blockgallery--item-uploader) .blockgallery--figure {
11 | display: inline-block;
12 | width: auto !important;
13 | }
14 |
15 | .has-fullwidth-images .blockgallery--item:not(.blockgallery--item-uploader) .blockgallery--figure {
16 | width: 100% !important;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/helper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import pick from 'lodash/pick';
5 | import get from 'lodash/get';
6 |
7 | export function overlayToClass( ratio ) {
8 | return ( ratio === 0 || ratio === 50 ) ?
9 | null :
10 | 'has-background-overlay-' + ( 10 * Math.round( ratio / 10 ) );
11 | }
12 |
13 | export const pickRelevantMediaFiles = ( image ) => {
14 | const imageProps = pick( image, [ 'alt', 'id', 'link', 'caption' ] );
15 | imageProps.url = get( image, [ 'sizes', 'large', 'url' ] ) || get( image, [ 'media_details', 'sizes', 'large', 'source_url' ] ) || image.url;
16 | return imageProps;
17 | };
18 |
19 | export const ALLOWED_MEDIA_TYPES = [ 'image' ];
20 |
--------------------------------------------------------------------------------
/src/styles/editor/_is-background-transient.scss:
--------------------------------------------------------------------------------
1 | // Animation effect when a background image is uploaded
2 | @keyframes background_loading_fade {
3 |
4 | 0% {
5 | opacity: 0.5;
6 | }
7 |
8 | 50% {
9 | opacity: 0.75;
10 | }
11 |
12 | 100% {
13 | opacity: 0.5;
14 | }
15 | }
16 |
17 | @mixin background_loading_fade {
18 | animation: background_loading_fade 1.6s ease-in-out infinite;
19 | }
20 |
21 | .has-background-image-transient > div {
22 | z-index: 2;
23 | }
24 |
25 | .has-background-image-transient::after {
26 | position: absolute;
27 | top: 0;
28 | bottom: 0;
29 | right: 0;
30 | left: 0;
31 | background: #fff;
32 | content: "";
33 | z-index: 1;
34 | @include background_loading_fade;
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/autoplay-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 |
6 | /**
7 | * Link options.
8 | */
9 | const autoPlayOptions = [
10 | { value: 1000, label: __( 'One Second' ) },
11 | { value: 2000, label: __( 'Two Seconds' ) },
12 | { value: 3000, label: __( 'Three Seconds' ) },
13 | { value: 4000, label: __( 'Four Seconds' ) },
14 | { value: 5000, label: __( 'Five Seconds' ) },
15 | { value: 6000, label: __( 'Six Second' ) },
16 | { value: 7000, label: __( 'Seven Seconds' ) },
17 | { value: 8000, label: __( 'Eight Seconds' ) },
18 | { value: 9000, label: __( 'Nine Seconds' ) },
19 | { value: 10000, label: __( 'Ten Seconds' ) },
20 | ];
21 |
22 | export default autoPlayOptions;
--------------------------------------------------------------------------------
/src/styles/style/_captions.scss:
--------------------------------------------------------------------------------
1 | .has-caption-style-light {
2 |
3 | .blockgallery--item .blockgallery--figure figcaption {
4 | background: linear-gradient(0deg, rgba($color: $white, $alpha: 0.93) 6.3%, rgba($color: $white, $alpha: 0.5) 61%, rgba(255, 255, 255, 0)) !important;
5 | opacity: 1 !important;
6 | }
7 | }
8 |
9 | .blockgallery:not(.has-caption-color).has-caption-style-dark .blockgallery--figure figcaption {
10 | color: $white;
11 | }
12 |
13 | .blockgallery:not(.has-caption-color).has-caption-style-light .blockgallery--figure figcaption {
14 | color: $black;
15 | }
16 |
17 | .has-caption-style-none {
18 |
19 | .blockgallery--item .blockgallery--figure figcaption {
20 | background: none !important;
21 | opacity: 1 !important;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/blocks/masonry/styles/style.scss:
--------------------------------------------------------------------------------
1 | .wp-block-blockgallery-masonry {
2 | position: relative;
3 |
4 | ul {
5 | padding: 0 !important;
6 | list-style: none !important;
7 | }
8 |
9 | li {
10 | margin: 0 !important;
11 | }
12 |
13 | figure {
14 | overflow: hidden;
15 | }
16 |
17 | img {
18 | vertical-align: bottom;
19 | }
20 |
21 | figcaption {
22 | position: absolute !important;
23 | bottom: 0;
24 | width: 100%;
25 | max-height: 100% !important;
26 | overflow: auto;
27 | padding: 30px 10px 10px !important;
28 | opacity: 0.9;
29 | text-align: center;
30 | font-size: 13px;
31 | background: linear-gradient(0deg, rgba($color: $black, $alpha: 0.7) 0, rgba($color: $black, $alpha: 0.3) 50%, transparent);
32 |
33 | img {
34 | display: inline;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/blocks/stacked/styles/style.scss:
--------------------------------------------------------------------------------
1 | .wp-block-blockgallery-stacked {
2 | position: relative;
3 | text-align: center;
4 |
5 | &:not(.has-caption-color) {
6 | color: #333 !important;
7 | }
8 |
9 | .blockgallery--item {
10 | margin-left: auto;
11 | margin-right: auto;
12 |
13 | &:last-child {
14 | margin-bottom: 0;
15 |
16 | figure {
17 | margin-bottom: 0 !important;
18 | }
19 |
20 | figcaption {
21 | padding-bottom: 0;
22 | }
23 | }
24 | }
25 |
26 | .blockgallery--caption {
27 | padding-bottom: 1em;
28 | padding-top: 1em;
29 | text-align: center;
30 |
31 | &:not([class*="font-size"]) {
32 | font-size: 13px;
33 | }
34 |
35 | .is-selected &,
36 | .is-typing & {
37 | padding-left: 1em;
38 | padding-right: 1em;
39 | }
40 | }
41 |
42 | .has-fullwidth-images img {
43 | width: 100%;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/styles/editor/_gallery-item.scss:
--------------------------------------------------------------------------------
1 | .blockgallery--item {
2 | margin: 0;
3 |
4 | figure:focus {
5 | outline: none;
6 | }
7 |
8 | img:focus {
9 | outline: none;
10 | box-shadow: inset 0 0 0 4px #0085ba;
11 | }
12 |
13 | .is-selected {
14 |
15 | &::after {
16 | position: absolute;
17 | top: 0;
18 | right: 0;
19 | bottom: 0;
20 | left: 0;
21 | box-shadow: inset 0 0 0 4px #0085ba;
22 | z-index: 1;
23 | content: "";
24 | }
25 | }
26 |
27 | .is-transient img {
28 | opacity: 0.3;
29 | }
30 |
31 | .editor-rich-text {
32 | z-index: 2;
33 | }
34 |
35 | .components-spinner {
36 | position: absolute;
37 | top: 50%;
38 | left: 50%;
39 | margin-top: -9px;
40 | margin-left: -9px;
41 | }
42 | }
43 |
44 | .has-caption-color .blockgallery--caption {
45 |
46 | a {
47 | color: inherit !important;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/background/transforms.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set the attributes for the Background transformations
3 | * @type {Object}
4 | */
5 | function BackgroundTransforms( props ) {
6 |
7 | const transforms = {
8 | backgroundColor: props.backgroundColor,
9 | customBackgroundColor: props.customBackgroundColor,
10 | backgroundImg: props.backgroundImg,
11 | backgroundOverlay: props.backgroundOverlay,
12 | backgroundPosition: props.backgroundPosition,
13 | backgroundRepeat: props.backgroundRepeat,
14 | backgroundSize: props.backgroundSize,
15 | backgroundPadding: props.backgroundPadding,
16 | backgroundPaddingMobile: props.backgroundPaddingMobile,
17 | backgroundRadius: props.backgroundRadius,
18 | hasParallax: props.hasParallax,
19 | captionStyle: props.captionStyle,
20 | };
21 |
22 | return transforms;
23 | }
24 |
25 | export default BackgroundTransforms;
--------------------------------------------------------------------------------
/src/styles/editor/_flickity-arrows.scss:
--------------------------------------------------------------------------------
1 | .has-no-arrows {
2 |
3 | .flickity-prev-next-button {
4 | background-color: $blue;
5 | border: none;
6 | width: 38px;
7 | height: 38px;
8 | border-radius: 4px;
9 | opacity: 1;
10 | display: none;
11 | transition: none;
12 |
13 | &:hover {
14 | background-color: #007eb1;
15 | }
16 |
17 | &:active {
18 | background-color: #006a95;
19 | }
20 |
21 | .flickity-button-icon {
22 | fill: $white;
23 | top: 22.5%;
24 | width: 55%;
25 | height: 55%;
26 | }
27 |
28 | &.previous {
29 | left: 15px;
30 |
31 | .flickity-button-icon {
32 | left: 27%;
33 | }
34 | }
35 |
36 | &.next {
37 | right: 15px;
38 |
39 | .flickity-button-icon {
40 | left: 20%;
41 | }
42 | }
43 | }
44 |
45 | &.blockgallery.is-selected .flickity-prev-next-button {
46 | display: block;
47 | }
48 | }
49 |
50 | .has-no-dots .flickity-page-dots {
51 | display: none !important;
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/background/attributes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set the attributes for the Background Panel
3 | * @type {Object}
4 | */
5 | const BackgroundAttributes = {
6 | backgroundImg: {
7 | type: 'string',
8 | },
9 | backgroundPosition: {
10 | type: 'string',
11 | default: 'center center',
12 | },
13 | backgroundRepeat: {
14 | type: 'string',
15 | default: 'no-repeat',
16 | },
17 | backgroundSize: {
18 | type: 'string',
19 | },
20 | backgroundRadius: {
21 | type: 'number',
22 | default: 0,
23 | },
24 | backgroundPadding: {
25 | type: 'number',
26 | default: 0,
27 | },
28 | backgroundPaddingMobile: {
29 | type: 'number',
30 | default: 0,
31 | },
32 | hasParallax: {
33 | type: 'boolean',
34 | default: false,
35 | },
36 | backgroundOverlay: {
37 | type: 'number',
38 | default: 0,
39 | },
40 | backgroundColor: {
41 | type: 'string',
42 | },
43 | customBackgroundColor: {
44 | type: 'string',
45 | },
46 | };
47 |
48 | export default BackgroundAttributes;
--------------------------------------------------------------------------------
/src/styles/editor/_radius.scss:
--------------------------------------------------------------------------------
1 | // Utility class for adding a border-radius to the figure elements.
2 | @for $i from 2 through 20 {
3 |
4 | .has-border-radius-#{ $i } {
5 |
6 | .blockgallery--figure::after {
7 | border-radius: #{ $i }px;
8 | }
9 |
10 | .components-blockgallery-gallery-item__remove-button,
11 | .components-blockgallery-gallery-item__remove-wrapper {
12 | border-top-right-radius: #{ $i }px !important;
13 | border-bottom-left-radius: #{ $i }px !important;
14 | }
15 |
16 | .components-blockgallery-gallery-item__upload.components-button {
17 | border-radius: #{ $i }px;
18 | }
19 |
20 | figcaption {
21 | border-bottom-right-radius: #{ $i - 4}px !important;
22 | border-bottom-left-radius: #{ $i - 4 }px !important;
23 | }
24 | }
25 | }
26 |
27 | @for $i from 13 through 20 {
28 |
29 | .has-border-radius-#{ $i } {
30 |
31 | .components-blockgallery-gallery-item__remove-wrapper {
32 | padding: 6px;
33 |
34 | svg {
35 | right: -1px;
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/global/classes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import { LightboxClasses } from '../../components/lightbox';
5 | import { BackgroundClasses } from '../../components/background';
6 |
7 | /**
8 | * WordPress dependencies
9 | */
10 | const { getColorClassName } = wp.editor;
11 |
12 | /**
13 | * CSS classes
14 | */
15 | function GlobalClasses( attributes ) {
16 |
17 | const captionColorClass = getColorClassName( 'color', attributes.captionColor );
18 |
19 | const {
20 | align,
21 | images,
22 | radius,
23 | filter,
24 | captionStyle,
25 | customCaptionColor,
26 | } = attributes;
27 |
28 | return [
29 | 'blockgallery',
30 | { 'has-no-alignment' : ! align },
31 | { [ `has-border-radius-${ radius }` ] : radius > 0 },
32 | { [ `has-filter-${ filter }` ] : filter != 'none' },
33 | { [ `has-caption-style-${ captionStyle }` ] : captionStyle != undefined },
34 | { 'has-caption-color': captionColorClass, },
35 | ...LightboxClasses( attributes ),
36 | ...BackgroundClasses( attributes ),
37 | captionColorClass,
38 | ];
39 | }
40 |
41 | export default GlobalClasses;
42 |
--------------------------------------------------------------------------------
/src/blocks.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { registerBlockType } = wp.blocks;
5 |
6 | // Category slug and title
7 | const category = {
8 | slug: 'block-gallery',
9 | title: 'Block Gallery',
10 | };
11 |
12 | // Custom foreground icon color based on the Block Gallery branding
13 | const iconColor = '#1e35b9';
14 |
15 | // Register block icons
16 | import icons from './utils/block-category';
17 |
18 | // Editor and Frontend Styles
19 | import './styles/editor.scss';
20 | import './styles/style.scss';
21 |
22 | // Register Blocks
23 | import * as carousel from './blocks/carousel';
24 | import * as masonry from './blocks/masonry';
25 | import * as stacked from './blocks/stacked';
26 |
27 | export function registerBlocks () {
28 | [
29 | carousel,
30 | masonry,
31 | stacked,
32 | ].forEach( ( block ) => {
33 |
34 | if ( ! block ) {
35 | return;
36 | }
37 |
38 | const { name, icon, settings } = block;
39 |
40 | registerBlockType( `blockgallery/${ name }`, { category: category.slug, icon: { src: icon, foreground: iconColor, }, ...settings } );
41 | } );
42 | };
43 | registerBlocks();
44 |
--------------------------------------------------------------------------------
/src/styles/editor.scss:
--------------------------------------------------------------------------------
1 | // Core Gutenberg modules
2 | @import "editor/core/colors";
3 | @import "editor/core/variables";
4 | @import "editor/core/breakpoints";
5 | @import "editor/core/mixins";
6 |
7 | // Our modules
8 | @import "editor/icons";
9 | @import "editor/gallery-item";
10 | @import "editor/remove-gallery-item-button";
11 | @import "editor/gallery-upload";
12 | @import "editor/inspector";
13 | @import "editor/inspector-tabs";
14 | @import "editor/radius";
15 | @import "editor/is-background-transient";
16 | @import "editor/flickity-arrows";
17 | @import "editor/core-themes";
18 |
19 | .components-toolbar {
20 |
21 | .components-dropdown-menu {
22 | padding: 0;
23 | }
24 | }
25 |
26 | .blockgallery {
27 |
28 | .components-resizable-box__handle {
29 | z-index: 9999;
30 | }
31 | }
32 |
33 | .components-blockgallery-inspector__lightbox {
34 | display: none;
35 | }
36 |
37 | .editor-media-placeholder[class*="blockgallery"] .components-placeholder__label .dashicon {
38 | margin-right: 0.7ch;
39 | position: relative;
40 | top: -2px;
41 | }
42 |
43 | .components-blockgallery-inspector__size-control--shadow {
44 | margin-bottom: 2.85em;
45 | }
46 |
--------------------------------------------------------------------------------
/src/styles/editor/core/_breakpoints.scss:
--------------------------------------------------------------------------------
1 | // Breakpoints & Media Queries
2 |
3 | // Most used breakpoints
4 | $break-huge: 1440px;
5 | $break-wide: 1280px;
6 | $break-large: 960px; // admin sidebar auto folds
7 | $break-medium: 782px; // adminbar goes big
8 | $break-small: 600px;
9 | $break-mobile: 480px;
10 |
11 | // All media queries currently in WordPress:
12 | //
13 | // min-width: 2000px
14 | // min-width: 1680px
15 | // min-width: 1250px
16 | // max-width: 1120px *
17 | // max-width: 1000px
18 | // min-width: 769px and max-width: 1000px
19 | // max-width: 960px *
20 | // max-width: 900px
21 | // max-width: 850px
22 | // min-width: 800px and max-width: 1499px
23 | // max-width: 800px
24 | // max-width: 799px
25 | // max-width: 782px *
26 | // max-width: 768px
27 | // max-width: 640px *
28 | // max-width: 600px *
29 | // max-width: 520px
30 | // max-width: 500px
31 | // max-width: 480px *
32 | // max-width: 400px *
33 | // max-width: 380px
34 | // max-width: 320px *
35 | //
36 | // Those marked * seem to be more commonly used than the others.
37 | // Let's try and use as few of these as possible, and be mindful about adding new ones, so we don't make the situation worse
38 |
--------------------------------------------------------------------------------
/src/styles/style/gutter.scss:
--------------------------------------------------------------------------------
1 | .blockgallery {
2 |
3 | &.has-gutter {
4 | overflow: hidden;
5 | }
6 |
7 | &:not(.has-gutter) {
8 | margin-left: auto !important;
9 | margin-right: auto !important;
10 | }
11 | }
12 |
13 | // For desktop.
14 | @media (min-width: 700px) {
15 | // Add negative margin to the parent ul to offset the gutter.
16 | @for $i from 1 through 10 {
17 |
18 | .has-gutter-#{ $i * 5 } {
19 | margin: -#{ round($i * 5 / 2) }px !important;
20 | max-width: calc(100% + #{ round($i * 5) }px) !important;
21 | }
22 | }
23 |
24 | // Add gutter margin to the figure elements.
25 | @for $i from 1 through 10 {
26 |
27 | .has-gutter-#{ $i * 5 } {
28 |
29 | .blockgallery--figure {
30 | margin: #{ round($i * 5 / 2) }px;
31 | }
32 | }
33 | }
34 | }
35 |
36 | // For desktop.
37 | @media (max-width: 699px) {
38 | // Add gutter margin to the figure elements.
39 | @for $i from 1 through 10 {
40 |
41 | .has-gutter-mobile-#{ $i * 5 } {
42 | margin: -#{ round($i * 5 / 2) }px !important;
43 | max-width: calc(100% + #{ round($i * 5) }px) !important;
44 |
45 | .blockgallery--figure {
46 | margin: #{ round($i * 5 / 2) }px;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/styles/style/_background-classes.scss:
--------------------------------------------------------------------------------
1 | .has-background {
2 |
3 | // Background repeat
4 | &-repeat {
5 | background-repeat: repeat;
6 | }
7 |
8 | &-no-repeat {
9 | background-repeat: no-repeat;
10 | }
11 |
12 | &-repeat-x {
13 | background-repeat: repeat-x;
14 | }
15 |
16 | &-repeat-y {
17 | background-repeat: repeat-y;
18 | }
19 |
20 | // Background size
21 | &-cover {
22 | background-size: cover;
23 | }
24 |
25 | &-auto {
26 | background-size: auto;
27 | }
28 |
29 | &-contain {
30 | background-size: contain;
31 | }
32 |
33 | // Background position
34 | &-top-left {
35 | background-position: top left;
36 | }
37 |
38 | &-top-center {
39 | background-position: top center;
40 | }
41 |
42 | &-top-right {
43 | background-position: top right;
44 | }
45 |
46 | &-center-left {
47 | background-position: center left;
48 | }
49 |
50 | &-center-center {
51 | background-position: center center;
52 | }
53 |
54 | &-center-right {
55 | background-position: center right;
56 | }
57 |
58 | &-bottom-left {
59 | background-position: bottom left;
60 | }
61 |
62 | &-bottom-center {
63 | background-position: bottom center;
64 | }
65 |
66 | &-bottom-right {
67 | background-position: bottom right;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/global/transforms.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import * as helper from './../../utils/helper';
5 | import { LightboxTransforms } from '../../components/lightbox';
6 | import { BackgroundTransforms } from '../../components/background';
7 |
8 | /**
9 | * Set global transforms that every block uses.
10 | * @type {Object}
11 | */
12 | function GlobalTransforms( props ) {
13 |
14 | const transforms = {
15 | align: props.align,
16 | gutter: props.gutter,
17 | gutterMobile: props.gutterMobile,
18 | gridSize: props.gridSize,
19 | images: props.images.map( ( image ) => helper.pickRelevantMediaFiles( image ) ),
20 | linkTo: props.linkTo,
21 | radius: props.radius,
22 | shadow: props.shadow,
23 | filter: props.filter,
24 | height: props.height,
25 | primaryCaption: props.primaryCaption,
26 | captions: props.captions,
27 | captionColor: props.captionColor,
28 | customCaptionColor: props.customCaptionColor,
29 | pageDots: props.pageDots,
30 | prevNextButtons: props.prevNextButtons,
31 | autoPlay: props.autoPlay,
32 | autoPlaySpeed: props.autoPlaySpeed,
33 | draggable: props.draggable,
34 | fontSize: props.fontSize,
35 | customFontSize: props.customFontSize,
36 | ...LightboxTransforms( props ),
37 | ...BackgroundTransforms( props ),
38 | };
39 |
40 | return transforms;
41 | }
42 |
43 | export default GlobalTransforms;
--------------------------------------------------------------------------------
/src/styles/style/_carousel.scss:
--------------------------------------------------------------------------------
1 | .blockgallery--item {
2 |
3 | // We really don't want margin on the flickity items.
4 | // Set 100% so the ResizableBox functions properly.
5 | .wp-block-blockgallery-carousel &,
6 | .wp-block-blockgallery-thumbnails & {
7 | margin: 0 !important;
8 | height: 100%;
9 | }
10 |
11 | .has-carousel-sml & {
12 | width: 65%;
13 |
14 | @media (min-width: 700px) {
15 | width: 33.333%;
16 | }
17 |
18 | @media (min-width: 1100px) {
19 | width: 25%;
20 | }
21 |
22 | @media (min-width: 1600px) {
23 | width: 20%;
24 | }
25 | }
26 |
27 | .has-carousel-med & {
28 | width: 70%;
29 |
30 | @media (min-width: 700px) {
31 | width: 33.333%;
32 | }
33 |
34 | @media (min-width: 1800px) {
35 | width: 20%;
36 | }
37 | }
38 |
39 | .has-carousel-lrg & {
40 | width: 80%;
41 |
42 | @media (min-width: 600px) {
43 | width: 70%;
44 | }
45 |
46 | @media (min-width: 1300px) {
47 | width: 60%;
48 | }
49 | }
50 |
51 | .has-carousel-xlrg & {
52 | width: 100%;
53 |
54 | @media (min-width: 1200px) {
55 | width: 80%;
56 | }
57 |
58 | @media (min-width: 1800px) {
59 | width: 66.666%;
60 | }
61 | }
62 |
63 | .has-no-alignment .has-carousel-lrg & {
64 | @media (min-width: 1300px) {
65 | width: 70%;
66 | }
67 | }
68 |
69 | .has-no-alignment .has-carousel-xlrg & {
70 | width: 100%;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/includes/admin/class-block-gallery-footer-text.php:
--------------------------------------------------------------------------------
1 | ★★★★★'
47 | );
48 |
49 | return $footer_text;
50 | }
51 | }
52 |
53 | new Block_Gallery_Footer_Text();
54 |
--------------------------------------------------------------------------------
/includes/admin/class-block-gallery-url-generator.php:
--------------------------------------------------------------------------------
1 | apply_filters( 'blockgallery_affiliate_id', null ) );
29 |
30 | return $id;
31 | }
32 |
33 | /**
34 | * Returns a URL that points to the Block Architect store.
35 | *
36 | * @since 1.0.0
37 | * @param string|string $path A URL path to append to the store URL.
38 | * @param array|array $params An array of key/value params to add to the query string.
39 | * @return string
40 | */
41 | public function get_store_url( $path = '', $params = array() ) {
42 |
43 | $id = $this->get_affiliate_id();
44 |
45 | $params = array_merge( $params, $id );
46 |
47 | $url = trailingslashit( BLOCKGALLERY_SHOP_URL . $path ) . '?' . http_build_query( $params, '', '&' );
48 |
49 | return $url;
50 | }
51 | }
52 |
53 | return new Block_Gallery_URL_Generator();
54 |
--------------------------------------------------------------------------------
/src/components/gallery-placeholder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import * as helper from './../utils/helper';
5 |
6 | /**
7 | * WordPress dependencies
8 | */
9 | const { __ } = wp.i18n;
10 | const { Component, Fragment } = wp.element;
11 | const { MediaPlaceholder } = wp.editor;
12 |
13 | /**
14 | * Gallery Image Component
15 | */
16 | class GalleryPlaceholder extends Component {
17 |
18 | constructor() {
19 | super( ...arguments );
20 | this.onSelectImages = this.onSelectImages.bind( this );
21 | }
22 |
23 | onSelectImages( images ) {
24 | this.props.setAttributes( {
25 | images: images.map( ( image ) => helper.pickRelevantMediaFiles( image ) ),
26 | } );
27 | }
28 |
29 | render() {
30 |
31 | const {
32 | attributes,
33 | className,
34 | noticeOperations,
35 | noticeUI,
36 | } = this.props;
37 |
38 | return (
39 |
40 |
54 |
55 | );
56 | }
57 | }
58 |
59 | export default GalleryPlaceholder;
60 |
61 |
--------------------------------------------------------------------------------
/src/components/gallery-dropzone.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WordPress dependencies
3 | */
4 | const { __ } = wp.i18n;
5 | const { Component, Fragment } = wp.element;
6 | const { mediaUpload } = wp.editor;
7 | const { DropZone } = wp.components;
8 |
9 | /**
10 | * Internal dependencies
11 | */
12 | import * as helper from './../utils/helper';
13 |
14 | /**
15 | * Gallery Drop Zone Component
16 | */
17 | class GalleryDropZone extends Component {
18 |
19 | constructor() {
20 | super( ...arguments );
21 |
22 | this.addFiles = this.addFiles.bind( this );
23 | }
24 |
25 | addFiles( files ) {
26 | const currentImages = this.props.attributes.images || [];
27 | const { noticeOperations, setAttributes } = this.props;
28 | mediaUpload( {
29 | allowedTypes: helper.ALLOWED_MEDIA_TYPES,
30 | filesList: files,
31 | onFileChange: ( images ) => {
32 | const imagesNormalized = images.map( ( image ) => helper.pickRelevantMediaFiles( image ) );
33 | setAttributes( {
34 | images: currentImages.concat( imagesNormalized ),
35 | } );
36 | },
37 | onError: noticeOperations.createErrorNotice,
38 | } );
39 | }
40 |
41 | render() {
42 |
43 | const {
44 | attributes,
45 | className,
46 | noticeOperations,
47 | noticeUI,
48 | label,
49 | } = this.props;
50 |
51 | return (
52 |
53 |
57 |
58 | );
59 | }
60 | }
61 |
62 | export default GalleryDropZone;
--------------------------------------------------------------------------------
/src/components/lightbox/control.js:
--------------------------------------------------------------------------------
1 | import lightboxStyleOptions from './options';
2 |
3 | /**
4 | * WordPress dependencies
5 | */
6 | const { __ } = wp.i18n;
7 | const { Component, Fragment } = wp.element;
8 | const { PanelBody, SelectControl, ToggleControl } = wp.components;
9 |
10 | /**
11 | * Gutter Controls Component
12 | */
13 | class LightboxControl extends Component {
14 |
15 | constructor() {
16 | super( ...arguments );
17 | this.setLightboxTo = this.setLightboxTo.bind( this );
18 | this.setLightboxStyleTo = this.setLightboxStyleTo.bind( this );
19 | }
20 |
21 | setLightboxTo() {
22 | this.props.setAttributes( { lightbox: ! this.props.attributes.lightbox } );
23 | }
24 |
25 | setLightboxStyleTo( value ) {
26 | this.props.setAttributes( { lightboxStyle: value } );
27 | }
28 |
29 | render() {
30 |
31 | const {
32 | attributes,
33 | setAttributes,
34 | } = this.props;
35 |
36 | const {
37 | lightbox,
38 | lightboxStyle,
39 | } = attributes;
40 |
41 | return (
42 |
43 |
44 |
49 | { lightbox && }
56 |
57 |
58 | );
59 | }
60 | }
61 |
62 | export default LightboxControl;
--------------------------------------------------------------------------------
/src/styles/style/_bricks.scss:
--------------------------------------------------------------------------------
1 | .has-bricks-grid-sml {
2 |
3 | .blockgallery--item img {
4 |
5 | @media screen and (min-width: 800px) {
6 | max-height: 250px !important;
7 | }
8 |
9 | @media screen and (min-width: 1200px) {
10 | max-height: 300px !important;
11 | }
12 | }
13 | }
14 |
15 | .has-bricks-grid-med {
16 |
17 | .blockgallery--item img {
18 |
19 | @media screen and (min-width: 700px) {
20 | max-height: 250px !important;
21 | }
22 |
23 | @media screen and (min-width: 1000px) {
24 | max-height: 300px !important;
25 | }
26 |
27 | @media screen and (min-width: 1400px) {
28 | max-height: 400px !important;
29 | }
30 | }
31 | }
32 |
33 | .has-bricks-grid-lrg {
34 |
35 | .blockgallery--item img {
36 |
37 | @media screen and (min-width: 300px) {
38 | max-height: 180px !important;
39 | }
40 |
41 | @media screen and (min-width: 600px) {
42 | max-height: 300px !important;
43 | }
44 |
45 | @media screen and (min-width: 1000px) {
46 | max-height: 350px !important;
47 | }
48 |
49 | @media screen and (min-width: 1400px) {
50 | max-height: 450px !important;
51 | }
52 |
53 | @media screen and (min-width: 1900px) {
54 | max-height: 550px !important;
55 | }
56 | }
57 | }
58 |
59 | .has-bricks-grid-xlrg {
60 |
61 | .blockgallery--item img {
62 |
63 | @media screen and (min-width: 300px) {
64 | max-height: 200px !important;
65 | }
66 |
67 | @media screen and (min-width: 600px) {
68 | max-height: 350px !important;
69 | }
70 |
71 | @media screen and (min-width: 1000px) {
72 | max-height: 400px !important;
73 | }
74 |
75 | @media screen and (min-width: 1400px) {
76 | max-height: 550px !important;
77 | }
78 |
79 | @media screen and (min-width: 1900px) {
80 | max-height: 650px !important;
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/blocks/masonry/styles/editor.scss:
--------------------------------------------------------------------------------
1 | .editor-block-list__block[data-type="blockgallery/masonry"] {
2 |
3 | // Fixes an issue where figures are set to display 100% if alignfull is set.
4 | .blockgallery--figure {
5 | width: auto !important;
6 |
7 | img {
8 | vertical-align: bottom;
9 | }
10 | }
11 |
12 | .components-blockgallery-gallery-item__upload {
13 | min-height: 200px;
14 | width: 100% !important;
15 |
16 | @media (max-width: 700px) {
17 |
18 | span {
19 | display: none;
20 | }
21 |
22 | .dashicon {
23 | margin-top: 0;
24 | }
25 | }
26 | }
27 |
28 | // Captions.
29 | .editor-rich-text {
30 | bottom: 0;
31 | left: 0;
32 | max-height: 100%;
33 | overflow-y: auto;
34 | position: absolute;
35 | right: 0;
36 | width: 100%;
37 |
38 | .editor-rich-text__tinymce {
39 | color: inherit;
40 |
41 | a {
42 | color: inherit;
43 | }
44 |
45 | &:not(.mce-content-body) {
46 | opacity: 0.6;
47 | }
48 |
49 | &:focus a[data-mce-selected] {
50 | opacity: 0.2;
51 | }
52 | }
53 | }
54 |
55 | .editor-rich-text figcaption:not([data-is-placeholder-visible="true"]) {
56 | position: relative !important;
57 | overflow: hidden;
58 | }
59 |
60 | .is-selected .editor-rich-text {
61 | // IE calculates this incorrectly, so leave it to modern browsers.
62 | @supports (position: sticky) {
63 | bottom: 4px;
64 | left: 4px;
65 | right: 4px;
66 | width: calc(100% - 8px);
67 | margin-top: -4px;
68 | }
69 |
70 | figcaption {
71 | padding-bottom: 6px !important;
72 | }
73 |
74 | // Override negative margins so this toolbar isn't hidden by overflow.
75 | // Overflow is needed for long captions.
76 | .editor-rich-text__inline-toolbar {
77 | top: 0;
78 | }
79 |
80 | // Make extra space for the inline toolbar.
81 | .editor-rich-text__tinymce {
82 | padding-top: 48px;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/styles/editor/_inspector.scss:
--------------------------------------------------------------------------------
1 | .components-blockgallery-inspector__size-control {
2 | align-items: center;
3 | display: flex;
4 | justify-content: space-between;
5 | margin-bottom: 1.7em;
6 |
7 | &--label {
8 | display: block;
9 | margin-bottom: 4px;
10 | }
11 |
12 | .components-range-control + &--label {
13 | margin-top: 0 !important;
14 | }
15 |
16 | + .components-blockgallery-inspector__tabs {
17 | margin-top: 2em;
18 | }
19 |
20 | .components-button {
21 | background: none;
22 | border: 1px solid #8d96a0;
23 | box-shadow: 0 0 0 transparent;
24 | position: relative;
25 | text-shadow: none;
26 | transition: box-shadow 0.1s linear;
27 |
28 | &.is-primary {
29 |
30 | &,
31 | &:focus,
32 | &:active {
33 | border-color: #6c7781;
34 | background: #6c7781;
35 | color: #fff;
36 | cursor: default;
37 | }
38 | }
39 |
40 | &.is-button:first-child {
41 | border-radius: 4px 0 0 4px;
42 | }
43 |
44 | &.is-button:last-child:not(.is-small) {
45 | border-radius: 0 4px 4px 0;
46 | }
47 | }
48 | }
49 |
50 | .components-blockgallery-inspector__lightbox-style {
51 | margin-top: -0.75em !important;
52 | }
53 |
54 | .components-blockgallery-inspector__background-overlay-style-control {
55 | margin-bottom: 1.8em !important;
56 |
57 | .components-select-control__input {
58 | margin-top: 0.5em;
59 | }
60 |
61 | label {
62 | display: none;
63 | }
64 | }
65 |
66 | .components-blockgallery-inspector__autoplayspeed-select {
67 | margin-bottom: 1.75em !important;
68 | }
69 |
70 | .components-blockgallery-inspector__background-size {
71 | margin-bottom: 2em !important;
72 | }
73 |
74 | .components-blockgallery-inspector__background-remove-button {
75 | justify-content: center;
76 | line-height: 26px;
77 | width: 100%;
78 | display: none;
79 | }
80 |
81 | .components-blockgallery-inspector__background-image-overlay {
82 | margin-bottom: 0.7em !important;
83 | margin-top: -0.7em !important;
84 | }
85 |
--------------------------------------------------------------------------------
/src/styles/style/_grid.scss:
--------------------------------------------------------------------------------
1 | .has-grid-sml .blockgallery--item {
2 |
3 | @media (min-width: 250px) {
4 | width: 1 / 2 * 100%;
5 | }
6 |
7 | @media (min-width: 500px) {
8 | width: 1 / 3 * 100%;
9 | }
10 |
11 | @media (min-width: 800px) {
12 | width: 1 / 4 * 100%;
13 | }
14 |
15 | @media (min-width: 1300px) {
16 | width: 1 / 5 * 100%;
17 | }
18 |
19 | @media (min-width: 1700px) {
20 | width: 1 / 6 * 100%;
21 | }
22 |
23 | @media (min-width: 1900px) {
24 | width: 1 / 7 * 100%;
25 | }
26 | }
27 |
28 | .has-grid-med .blockgallery--item {
29 |
30 | @media (min-width: 350px) {
31 | width: 1 / 2 * 100%;
32 | }
33 |
34 | @media (min-width: 650px) {
35 | width: 1 / 3 * 100%;
36 | }
37 |
38 | @media (min-width: 1100px) {
39 | width: 1 / 4 * 100%;
40 | }
41 |
42 | .alignfull & {
43 | @media (min-width: 1600px) {
44 | width: 1 / 5 * 100%;
45 | }
46 |
47 | @media (min-width: 1900px) {
48 | width: 1 / 6 * 100%;
49 | }
50 | }
51 | }
52 |
53 | .has-grid-lrg .blockgallery--item {
54 |
55 | @media (min-width: 400px) {
56 | width: 1 / 2 * 100%;
57 | }
58 |
59 | @media (min-width: 800px) {
60 | width: 1 / 3 * 100%;
61 | }
62 |
63 | .alignfull & {
64 | @media (min-width: 1600px) {
65 | width: 1 / 4 * 100%;
66 | }
67 |
68 | @media (min-width: 1900px) {
69 | width: 1 / 5 * 100%;
70 | }
71 | }
72 | }
73 |
74 | .has-grid-xlrg .blockgallery--item {
75 |
76 | @media (min-width: 400px) {
77 | width: 1 / 2 * 100%;
78 | }
79 |
80 | .alignfull & {
81 | @media (min-width: 1600px) {
82 | width: 1 / 3 * 100%;
83 | }
84 |
85 | @media (min-width: 1900px) {
86 | width: 1 / 4 * 100%;
87 | }
88 | }
89 | }
90 |
91 | .has-no-alignment .has-grid-lrg .blockgallery--item {
92 |
93 | @media (min-width: 400px) {
94 | width: 1 / 2 * 100%;
95 | }
96 |
97 | @media (min-width: 900px) {
98 | width: 1 / 3 * 100%;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/background/classes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import * as ratio from './../../utils/helper';
5 |
6 | /**
7 | * WordPress dependencies
8 | */
9 | const { getColorClassName } = wp.editor;
10 |
11 | /**
12 | * Background Classes
13 | */
14 | function BackgroundClasses( attributes ) {
15 |
16 | const backgroundClass = getColorClassName( 'background-color', attributes.backgroundColor );
17 | const backgroundSizeDefault = ( typeof options !== 'undefined' && typeof options.backgroundSize !== 'undefined' ) ? options.backgroundSize : 'cover';
18 | const backgroundSize = attributes.backgroundSize ? attributes.backgroundSize : backgroundSizeDefault;
19 |
20 | return [
21 | { 'has-background': attributes.backgroundColor || attributes.customBackgroundColor },
22 | { [ backgroundClass ]: backgroundClass },
23 | { 'has-padding': attributes.backgroundPadding > 0 },
24 | { [ `has-background-border-radius-${ attributes.backgroundRadius }` ] : attributes.backgroundRadius > 0 },
25 | { [ `has-padding-${ attributes.backgroundPadding }` ] : attributes.backgroundPadding > 0 },
26 | { [ `has-padding-mobile-${ attributes.backgroundPaddingMobile }` ]: attributes.backgroundPaddingMobile > 0 },
27 | { 'has-parallax': attributes.backgroundImg && attributes.hasParallax },
28 | { 'has-background-image': attributes.backgroundImg },
29 | { 'has-background-image-transient': attributes.backgroundImg && 0 === attributes.backgroundImg.indexOf( 'blob:' ) },
30 | { [ `has-background-${ attributes.backgroundRepeat }` ] : attributes.backgroundImg && attributes.backgroundRepeat },
31 | { [ `has-background-${ attributes.backgroundPosition.split(' ').join('-') }` ] : attributes.backgroundImg && attributes.backgroundPosition },
32 | { [ `has-background-${ backgroundSize }` ] : attributes.backgroundImg },
33 | { [ `has-background-overlay` ] : attributes.backgroundImg && attributes.backgroundOverlay !== 0 },
34 | { [ ratio.overlayToClass( attributes.backgroundOverlay ) ] : attributes.backgroundImg && attributes.backgroundOverlay !== 0 && attributes.backgroundOverlay !== 50 },
35 | ];
36 | }
37 |
38 | export default BackgroundClasses;
--------------------------------------------------------------------------------
/src/components/global/attributes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import Lightbox, { LightboxAttributes } from '../../components/lightbox';
5 | import BackgroundPanel, { BackgroundAttributes } from '../../components/background';
6 |
7 | /**
8 | * Set global attributes that every block uses.
9 | * @type {Object}
10 | */
11 | const GlobalAttributes = {
12 | images: {
13 | type: 'array',
14 | default: [],
15 | source: 'query',
16 | selector: '.blockgallery--item',
17 | query: {
18 | url: {
19 | source: 'attribute',
20 | selector: 'img',
21 | attribute: 'src',
22 | },
23 | link: {
24 | source: 'attribute',
25 | selector: 'img',
26 | attribute: 'data-link',
27 | },
28 | alt: {
29 | source: 'attribute',
30 | selector: 'img',
31 | attribute: 'alt',
32 | default: '',
33 | },
34 | id: {
35 | source: 'attribute',
36 | selector: 'img',
37 | attribute: 'data-id',
38 | },
39 | caption: {
40 | type: 'array',
41 | source: 'children',
42 | selector: 'figcaption',
43 | },
44 | },
45 | },
46 | linkTo: {
47 | type: 'string',
48 | default: 'none',
49 | },
50 | align: {
51 | type: 'string',
52 | },
53 | gutter: {
54 | type: 'number',
55 | default: 15,
56 | },
57 | gutterMobile: {
58 | type: 'number',
59 | default: 15,
60 | },
61 | radius: {
62 | type: 'number',
63 | default: 0,
64 | },
65 | shadow: {
66 | type: 'string',
67 | default: 'none',
68 | },
69 | filter: {
70 | type: 'string',
71 | default: 'none',
72 | },
73 | captions: {
74 | type: 'boolean',
75 | default: true,
76 | },
77 | captionStyle: {
78 | type: 'string',
79 | default: 'dark',
80 | },
81 | captionColor: {
82 | type: 'string',
83 | },
84 | customCaptionColor: {
85 | type: 'string',
86 | },
87 | fontSize: {
88 | type: 'string',
89 | },
90 | customFontSize: {
91 | type: 'number',
92 | },
93 | primaryCaption: {
94 | type: 'array',
95 | source: 'children',
96 | selector: '.blockgallery--primary-caption',
97 | },
98 | ...LightboxAttributes,
99 | ...BackgroundAttributes,
100 | };
101 |
102 | export default GlobalAttributes;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Block Gallery — WordPress Gallery Gutenberg blocks
2 |
3 | #### Notice: Block Gallery in being deprecated in favor of the wonderful gallery blocks within CoBlocks . Each Block Gallery block may be transformed into it's corresponding CoBlocks gallery block.
4 |
5 | ---
6 |
7 | The first of its kind, [Block Gallery](https://wpblockgallery.com/) offers an unrivaled drag and drop gallery building experience in WordPress 5.0. Drop your images in your choice of photo gallery block, customize display settings, hit publish.
8 |
9 | An innovative transform system lets you instantly change your photos galleries into another form. Go from a fullscreen masonry gallery to a casual carousel, with just a single click. You won’t find another Gutenberg gallery plugin with this kind of capability. Guaranteed.
10 |
11 | [](https://richtabor.com/block-gallery-automattic-design-awards/?utm_medium=block-gallery-github&utm_source=readme&utm_campaign=readme&utm_content=design-awards-banner)
12 |
13 | Block Gallery was selected as this year’s winner in the Best Solution category of the [Automattic Design Awards](https://automatticdesignaward.blog/2018/12/08/the-winners/) at WordCamp US 2018. [Continue reading...](https://richtabor.com/block-gallery-automattic-design-awards/)
14 |
15 | ## Installation ##
16 | 1. Install the offical [Gutenberg](https://wordpress.org/plugins/gutenberg/) plugin. Note that Gutenberg is not suggested for use on production sites.
17 | 2. [Download the latest release](https://github.com/godaddy/block-gallery/releases) from the GitHub repository, or..
18 | 3. Download the plugin from the [WordPress plugin directory](https://wordpress.org/plugins/block-gallery/).
19 |
20 | ## Development ##
21 | 1. Clone the GitHub repository: `https://github.com/godaddy/block-gallery.git`
22 | 2. Browse to the folder in the command line.
23 | 3. Run the `npm install` command to install the plugin's dependencies within a /node_modules/ folder.
24 | 4. Run the `npm start` command for development.
25 | 5. Run the `build` gulp task to process build files and generate a zip.
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contribute To Block Gallery
2 |
3 | Community made patches, localisations, bug reports and contributions are always welcome!
4 |
5 | When contributing please ensure you follow the guidelines below so that we can keep on top of things.
6 |
7 | __Please Note:__ GitHub is for bug reports and contributions only - if you have a support question head over to the [support forum on WordPress.org](https://wordpress.org/support/plugin/block-gallery).
8 |
9 | ## Getting Started
10 |
11 | * __Do not report potential security vulnerabilities here. Email them privately to me at [plugins@godaddy.com](mailto:plugins@godaddy.com)__
12 | * Before submitting a ticket, please be sure to replicate the behavior with no other plugins active and on a base theme like Twenty Seventeen.
13 | * Submit a ticket for your issue, assuming one does not already exist.
14 | * Raise it on our [Issue Tracker](https://github.com/godaddy/block-gallery/issues)
15 | * Clearly describe the issue including steps to reproduce the bug.
16 | * Make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using.
17 |
18 | ## Making Changes
19 |
20 | * Fork the repository on GitHub
21 | * Make the changes to your forked repository
22 | * Ensure you stick to the [WordPress Coding Standards](https://codex.wordpress.org/WordPress_Coding_Standards)
23 | * When committing, reference your issue (if present) and include a note about the fix
24 | * If possible, and if applicable, please also add/update unit tests for your changes
25 | * Push the changes to your fork and submit a pull request to the 'master' branch of the Block Gallery repository
26 |
27 | ## Code Documentation
28 |
29 | * We ensure that every Block Gallery function is documented well and follows the standards set by phpDoc
30 | * Finally, please use tabs and not spaces. The tab indent size should be 8.
31 |
32 | At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary.
33 |
34 | # Additional Resources
35 | * [General GitHub Documentation](https://help.github.com/)
36 | * [GitHub Pull Request documentation](https://help.github.com/send-pull-requests/)
37 | * [PHPUnit Tests Guide](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)
--------------------------------------------------------------------------------
/src/styles/editor/_inspector-tabs.scss:
--------------------------------------------------------------------------------
1 | .components-blockgallery-inspector__tabs {
2 | position: relative;
3 | display: flex;
4 | flex-direction: row-reverse;
5 | align-items: center;
6 |
7 | .components-base-control,
8 | .components-base-control__field {
9 | margin-bottom: 0 !important;
10 | }
11 |
12 | .components-tab-panel__tab-content {
13 | width: 100%;
14 | }
15 |
16 | &-item {
17 | appearance: none;
18 | background: $white;
19 | border-radius: 4px;
20 | border: 1px solid #8d96a0;
21 | color: #6c7781;
22 | height: 28px;
23 | line-height: 1;
24 | margin: 0;
25 | padding-left: 7px;
26 | padding-right: 7px;
27 |
28 | @media (max-width: 782px) {
29 | height: 40px;
30 | padding-left: 17px;
31 | padding-right: 17px;
32 | }
33 |
34 | @media (max-width: 514px) {
35 | padding-left: 10px;
36 | padding-right: 10px;
37 | }
38 |
39 | &:focus {
40 | border-color: #00a0d2 !important;
41 | color: #00a0d2;
42 | outline-offset: -2px;
43 | outline: 2px solid transparent;
44 | }
45 |
46 | &:not(.is-active):active:enabled {
47 | background: #eee;
48 | border-color: #999;
49 | box-shadow: inset 0 1px 0 #999;
50 | }
51 |
52 | &:not(.is-active):hover {
53 | background: #fafafa;
54 | border-color: #999;
55 | box-shadow: inset 0 -1px 0 #999;
56 | color: #555d66;
57 | cursor: pointer;
58 | }
59 |
60 | &.is-active {
61 | background: #6c7781;
62 | border-color: #6c7781;
63 | color: $white;
64 | z-index: 1;
65 |
66 | &:focus {
67 | background: #00a0d2;
68 | }
69 | }
70 |
71 | &:first-of-type {
72 | border-top-right-radius: 0;
73 | border-bottom-right-radius: 0;
74 |
75 | &:not(.is-active) {
76 | border-right: none;
77 | }
78 | }
79 |
80 | &:last-of-type {
81 | border-top-left-radius: 0;
82 | border-bottom-left-radius: 0;
83 |
84 | &:not(.is-active) {
85 | border-left: none;
86 | }
87 | }
88 |
89 | svg {
90 | height: 15px;
91 | position: relative;
92 | width: 15px;
93 | top: 0.08em;
94 | }
95 | }
96 |
97 | .components-tab-panel__tabs {
98 | align-items: center;
99 | display: flex;
100 | margin-left: 7px;
101 | margin-right: 1px;
102 | position: relative;
103 | top: 11px;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "block-gallery",
3 | "title": "Block Gallery",
4 | "description": "The most advanced suite of gallery blocks for the Gutenberg block editor. Create stunning masonry , carousel and stacked galleries in seconds, with the brilliantly intuitive interface. Block Gallery is absolutely the best collection of native editor gallery blocks in the world.",
5 | "version": "1.1.6",
6 | "tested_up_to": "5.2",
7 | "author": "GoDaddy",
8 | "author_uri": "https://www.godaddy.com",
9 | "requires": "5.0",
10 | "license": "GPL-2.0",
11 | "scripts": {
12 | "start": "gulp | cgb-scripts start",
13 | "build": "cgb-scripts build | babel src",
14 | "eject": "cgb-scripts eject",
15 | "makepot": "wp i18n make-pot . --merge=languages/block-gallery-js.pot --skip-js",
16 | "makepot:php": "npx pot-to-php languages/block-gallery.pot languages/block-gallery-translations.php block-gallery"
17 | },
18 | "dependencies": {
19 | "cgb-scripts": "1.11.0",
20 | "classnames": "2.2.5",
21 | "compose": "^0.1.2",
22 | "delete-empty": "^2.0.0",
23 | "gutenberg-sortable": "^1.0.5",
24 | "lodash": "4.17.13",
25 | "memize": "^1.0.5",
26 | "react": "^16.2.0",
27 | "react-flickity-component": "^3.1.0",
28 | "react-masonry-component": "^6.2.1",
29 | "stylelint": "^9.4.0",
30 | "stylelint-config-wordpress": "^13.0.0"
31 | },
32 | "devDependencies": {
33 | "@wordpress/babel-plugin-makepot": "^2.1.2",
34 | "@wordpress/babel-preset-default": "^1.1.2",
35 | "@wordpress/i18n": "^1.2.3",
36 | "babel-cli": "^6.26.0",
37 | "babel-core": "^6.25.0",
38 | "babel-loader": "^7.1.4",
39 | "del": "^3.0.0",
40 | "gulp": "^4.0.0",
41 | "gulp-autoprefixer": "^5.0.0",
42 | "gulp-cache": "^1.0.2",
43 | "gulp-copy": "^1.0.1",
44 | "gulp-if": "^2.0.2",
45 | "gulp-line-ending-corrector": "^1.0.1",
46 | "gulp-notify": "^3.2.0",
47 | "gulp-open": "^2.0.0",
48 | "gulp-rename": "^1.2.0",
49 | "gulp-replace-task": "^0.11.0",
50 | "gulp-run-command": "0.0.9",
51 | "gulp-sass": "^3.2.1",
52 | "gulp-sftp": "^0.1.5",
53 | "gulp-uglify": "^1.5.3",
54 | "gulp-uglifycss": "^1.0.8",
55 | "gulp-wp-pot": "^2.3.2",
56 | "gulp-zip": "^4.0.0",
57 | "stylelint-config-wordpress": "^13.0.0"
58 | },
59 | "stylelint": {
60 | "extends": "stylelint-config-wordpress/scss",
61 | "rules": {
62 | "at-rule-empty-line-before": null,
63 | "no-descending-specificity": null
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/styles/editor/core/_variables.scss:
--------------------------------------------------------------------------------
1 | // Often re-used variables
2 |
3 | // Fonts & basics
4 | $default-font: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif;
5 | $default-font-size: 13px;
6 | $default-line-height: 1.4;
7 | $editor-font: "Noto Serif", serif;
8 | $editor-html-font: Menlo, Consolas, monaco, monospace;
9 | $editor-font-size: 16px;
10 | $text-editor-font-size: 14px;
11 | $editor-line-height: 1.8;
12 | $big-font-size: 18px;
13 | $mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in"
14 |
15 | // Grid size
16 | $item-spacing: 10px; // Should deprecate this in favor of $grid-size.
17 | $grid-size-small: 4px;
18 | $grid-size: 8px;
19 | $grid-size-large: 16px;
20 |
21 | // Widths, heights & dimensions
22 | $panel-padding: 16px;
23 | $header-height: 56px;
24 | $panel-header-height: 50px;
25 | $admin-bar-height: 32px;
26 | $admin-bar-height-big: 46px;
27 | $admin-sidebar-width: 160px;
28 | $admin-sidebar-width-big: 190px;
29 | $admin-sidebar-width-collapsed: 36px;
30 | $empty-paragraph-height: $text-editor-font-size * 4;
31 |
32 | // Visuals
33 | $shadow-popover: 0 3px 30px rgba($dark-gray-900, 0.1);
34 | $shadow-toolbar: 0 2px 10px rgba($dark-gray-900, 0.1), 0 0 2px rgba($dark-gray-900, 0.1);
35 | $shadow-below-only: 0 5px 10px rgba($dark-gray-900, 0.05), 0 2px 2px rgba($dark-gray-900, 0.05);
36 | $shadow-modal: 0 3px 30px rgba($dark-gray-900, 0.2);
37 |
38 | // Editor Widths
39 | $sidebar-width: 280px;
40 | $content-width: 610px; // For the visual width, subtract 30px (2 * $block-padding + 2px borders). This comes to 580px, which is optimized for 70 characters.
41 |
42 | // Block UI
43 | $border-width: 1px;
44 | $block-controls-height: 36px;
45 | $icon-button-size: 36px;
46 | $icon-button-size-small: 24px;
47 | $inserter-tabs-height: 36px;
48 | $block-toolbar-height: $block-controls-height + $border-width;
49 |
50 | // Blocks
51 | $block-padding: 14px; // padding of nested blocks
52 | $block-spacing: 4px; // vertical space between blocks
53 |
54 | $block-side-ui-width: 28px; // width of the side UI, matches half matches half of a single line of text, so two buttons stacke matches 1
55 | $block-side-ui-clearance: 2px; // space between side UI and block
56 | $block-side-ui-padding: $block-side-ui-width + $block-side-ui-clearance; // total space used to accommodate side UI
57 |
58 | $block-container-side-padding: $block-side-ui-width + $block-padding + 2 * $block-side-ui-clearance;
59 |
60 | // Buttons & UI Widgets
61 | $radius-round-rectangle: 4px;
62 | $radius-round: 50%;
63 |
--------------------------------------------------------------------------------
/src/styles/style.scss:
--------------------------------------------------------------------------------
1 | .blockgallery {
2 | list-style: none !important;
3 | margin-left: auto !important;
4 | margin-right: auto !important;
5 | padding: 0;
6 |
7 | &--item {
8 | list-style: none !important;
9 | margin: 0;
10 | padding: 0 !important;
11 |
12 | figure {
13 | margin: 0;
14 | position: relative;
15 | }
16 |
17 | img {
18 | vertical-align: middle;
19 | opacity: 1 !important;
20 | }
21 | }
22 |
23 | & &--item figcaption {
24 | margin: 0 !important;
25 | }
26 |
27 | &:not(.has-padding) {
28 | padding: 0 !important;
29 | }
30 |
31 | &:not(.has-margin) &--item {
32 | margin: auto !important;
33 | }
34 |
35 | &.is-cropped {
36 |
37 | .blockgallery--item,
38 | .blockgallery--item-thumbnail {
39 |
40 | a,
41 | img {
42 | // IE11 doesn't support object-fit, so just make sure images aren't skewed.
43 | // The following rules are for all browsers.
44 | width: 100%;
45 |
46 | // IE11 doesn't read rules inside this query.
47 | // They are applied only to modern browsers.
48 | @supports (position: sticky) {
49 | flex: 1;
50 | height: 100%;
51 | object-fit: cover;
52 | }
53 | }
54 | }
55 | }
56 | }
57 |
58 | .alignfull,
59 | .alignwide {
60 |
61 | ul.blockgallery {
62 | max-width: 100%;
63 | }
64 | }
65 |
66 | .blockgallery--item-thumbnail {
67 | list-style: none !important;
68 | margin: 0;
69 | padding: 0;
70 |
71 | figure {
72 | margin: 0;
73 | position: relative;
74 | }
75 |
76 | img {
77 | vertical-align: middle;
78 | }
79 | }
80 |
81 | figcaption.blockgallery--primary-caption {
82 | font-size: 13px;
83 | margin-bottom: 1em;
84 | margin-top: 1.2em;
85 | text-align: center;
86 |
87 | &:not(.has-caption-color) {
88 | color: #555d66;
89 | }
90 | }
91 |
92 | // Front-end modules
93 | @import "style/grid";
94 | @import "style/bricks";
95 | @import "style/gutter";
96 | @import "style/horizontal-gutter";
97 | @import "style/carousel";
98 | @import "style/margin-bottom";
99 | @import "style/margin-top";
100 | @import "style/margin-right";
101 | @import "style/margin-left";
102 | @import "style/margin-right-negative";
103 | @import "style/margin-left-negative";
104 | @import "style/padding";
105 | @import "style/shadow";
106 | @import "style/radius";
107 | @import "style/filters";
108 | @import "style/captions";
109 | @import "style/background-classes";
110 | @import "style/background-overlay";
111 | @import "style/background-parallax";
112 | @import "style/background-radius";
113 | @import "style/flickity";
114 | @import "style/core-themes";
115 |
--------------------------------------------------------------------------------
/src/components/background/toolbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import icons from './../../utils/icons';
5 |
6 | /**
7 | * Internal dependencies
8 | */
9 | import * as helper from './../../utils/helper';
10 |
11 | /**
12 | * WordPress dependencies
13 | */
14 | const { __ } = wp.i18n;
15 | const { Component, Fragment } = wp.element;
16 | const { MediaUpload, MediaUploadCheck } = wp.editor;
17 | const { Toolbar, IconButton } = wp.components;
18 |
19 | /**
20 | * Background Image Toolbar
21 | */
22 | class BackgroundToolbar extends Component {
23 |
24 | constructor( props ) {
25 | super( ...arguments );
26 | }
27 |
28 | render() {
29 |
30 | const {
31 | attributes,
32 | setAttributes,
33 | } = this.props;
34 |
35 | const {
36 | backgroundColor,
37 | backgroundImg,
38 | backgroundPadding,
39 | backgroundPaddingMobile,
40 | } = attributes;
41 |
42 | return (
43 |
44 |
45 |
46 | { ! backgroundImg ?
47 | {
49 | setAttributes( {
50 | backgroundImg: media.url,
51 | } );
52 |
53 | if ( ! backgroundPadding && ! backgroundPaddingMobile ) {
54 | setAttributes( {
55 | backgroundPadding: 30,
56 | backgroundPaddingMobile: 30,
57 | } );
58 | }
59 | } }
60 | allowedTypes={ helper.ALLOWED_MEDIA_TYPES }
61 | value={ backgroundImg }
62 | render={ ( { open } ) => (
63 |
69 | ) }
70 | />
71 | :
72 | {
77 | setAttributes( {
78 | backgroundImg: '',
79 | backgroundOverlay: 0,
80 | backgroundRepeat: 'no-repeat',
81 | backgroundPosition: '',
82 | backgroundSize: 'cover',
83 | hasParallax: false,
84 | } );
85 |
86 | if ( ! backgroundColor ) {
87 | setAttributes( {
88 | backgroundPadding: 0,
89 | backgroundPaddingMobile: 0,
90 | } );
91 | }
92 | } }
93 | />
94 | }
95 |
96 |
97 |
98 | );
99 | }
100 | }
101 |
102 | export default BackgroundToolbar;
--------------------------------------------------------------------------------
/src/components/responsive-tabs-control.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import icons from './../utils/icons';
5 |
6 | /**
7 | * WordPress dependencies
8 | */
9 | const { __, sprintf } = wp.i18n;
10 | const { Component, Fragment } = wp.element;
11 | const { RangeControl, TabPanel } = wp.components;
12 |
13 | /**
14 | * Gutter Controls Component
15 | */
16 | class ResponsiveTabsControl extends Component {
17 |
18 | constructor() {
19 | super( ...arguments );
20 | this.setGutterTo = this.setGutterTo.bind( this );
21 | this.setGutterMobileTo = this.setGutterMobileTo.bind( this );
22 | }
23 |
24 | setGutterTo( value ) {
25 | this.props.setAttributes( { gutter: value } );
26 | }
27 |
28 | setGutterMobileTo( value ) {
29 | this.props.setAttributes( { gutterMobile: value } );
30 | }
31 |
32 | render() {
33 |
34 | const {
35 | attributes,
36 | label = __( 'Gutter' ),
37 | max = 50,
38 | min = 0,
39 | onChange = this.setGutterTo,
40 | onChangeMobile = this.setGutterMobileTo,
41 | setAttributes,
42 | step = 5,
43 | value = this.props.attributes.gutter,
44 | valueMobile = this.props.attributes.gutterMobile,
45 | } = this.props;
46 |
47 | return (
48 |
49 |
65 | {
66 | ( tab ) => {
67 | if ( 'mobile' === tab.name ) {
68 | return (
69 | onChangeMobile( valueMobile ) }
74 | min={ min }
75 | max={ max }
76 | step={ step }
77 | />
78 | )
79 | } else {
80 | return (
81 | onChange( value ) }
85 | min={ min }
86 | max={ max }
87 | step={ step }
88 | />
89 | )
90 | }
91 | }
92 | }
93 |
94 |
95 | );
96 | }
97 | }
98 |
99 | export default ResponsiveTabsControl;
100 |
--------------------------------------------------------------------------------
/includes/class-block-gallery-body-classes.php:
--------------------------------------------------------------------------------
1 | get( 'TextDomain' ) );
68 | }
69 |
70 | /**
71 | * Add .is-{theme} class to the frontend for select themes.
72 | *
73 | * @param array $classes Classes for the body element.
74 | * @return array (Maybe) filtered body classes.
75 | *
76 | * @access public
77 | */
78 | public function body_class( $classes ) {
79 |
80 | if ( $this->is_active_theme( $this->themes() ) ) {
81 | $classes[] = 'is-' . $this->theme_slug();
82 | }
83 |
84 | return $classes;
85 | }
86 |
87 | /**
88 | * Add .is-{theme} class to the admin editor for select themes.
89 | *
90 | * @param array $classes Classes for the admin editor body element.
91 | * @return array (Maybe) filtered body classes.
92 | *
93 | * @access public
94 | */
95 | public function admin_body_class( $classes ) {
96 | global $pagenow;
97 |
98 | // Return if not on viewing the editor.
99 | if ( ! in_array( $pagenow, array( 'post.php' ), true ) ) {
100 | return $classes;
101 | }
102 |
103 | if ( $this->is_active_theme( $this->themes() ) ) {
104 | $classes .= 'is-' . $this->theme_slug();
105 | }
106 |
107 | return $classes;
108 | }
109 | }
110 |
111 | new Block_Gallery_Body_Classes();
112 |
--------------------------------------------------------------------------------
/src/components/gallery-upload.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External Dependencies
3 | */
4 | import classnames from 'classnames';
5 |
6 | /**
7 | * Internal dependencies
8 | */
9 | import * as helper from './../utils/helper';
10 |
11 | /**
12 | * WordPress dependencies
13 | */
14 | const { __ } = wp.i18n;
15 | const { Component, Fragment } = wp.element;
16 | const { compose } = wp.compose;
17 | const { withSelect } = wp.data;
18 | const {
19 | IconButton,
20 | DropZone,
21 | FormFileUpload,
22 | PanelBody,
23 | Toolbar,
24 | withNotices,
25 | } = wp.components;
26 | const {
27 | MediaPlaceholder,
28 | mediaUpload,
29 | } = wp.editor;
30 |
31 | /**
32 | * Gallery Image Component
33 | */
34 | class GalleryUpload extends Component {
35 |
36 | constructor() {
37 | super( ...arguments );
38 | this.addFiles = this.addFiles.bind( this );
39 | this.uploadFromFiles = this.uploadFromFiles.bind( this );
40 | }
41 |
42 | uploadFromFiles( event ) {
43 | this.addFiles( event.target.files );
44 | }
45 |
46 | addFiles( files ) {
47 | const currentImages = this.props.attributes.images || [];
48 | const { noticeOperations, setAttributes } = this.props;
49 | mediaUpload( {
50 | allowedTypes: helper.ALLOWED_MEDIA_TYPES,
51 | filesList: files,
52 | onFileChange: ( images ) => {
53 | setAttributes( {
54 | images: currentImages.concat( images ),
55 | } );
56 | },
57 | onError: noticeOperations.createErrorNotice,
58 | } );
59 | }
60 |
61 | render() {
62 |
63 | const {
64 | gutter,
65 | gutterMobile,
66 | marginLeft,
67 | marginRight,
68 | marginTop,
69 | marginBottom,
70 | } = this.props;
71 |
72 | const className = classnames(
73 | 'blockgallery--figure', {
74 | [ `has-margin-top-${ gutter }` ] : marginTop && gutter > 0,
75 | [ `has-margin-top-mobile-${ gutterMobile }` ] : marginTop && gutterMobile > 0,
76 | [ `has-margin-right-${ gutter }` ] : marginRight && gutter > 0,
77 | [ `has-margin-right-mobile-${ gutterMobile }` ] : marginRight && gutterMobile > 0,
78 | [ `has-margin-bottom-${ gutter }` ] : marginBottom && gutter > 0,
79 | [ `has-margin-bottom-mobile-${ gutterMobile }` ] : marginBottom && gutterMobile > 0,
80 | [ `has-margin-left-${ gutter }` ] : marginLeft && gutter > 0,
81 | [ `has-margin-left-mobile-${ gutterMobile }` ] : marginLeft && gutterMobile > 0,
82 | } );
83 |
84 | return (
85 |
86 |
87 |
88 |
96 | { __( 'Upload an image' ) }
97 |
98 |
99 |
100 |
101 | )
102 | }
103 | }
104 |
105 | export default GalleryUpload;
106 |
107 |
--------------------------------------------------------------------------------
/includes/admin/class-block-gallery-action-links.php:
--------------------------------------------------------------------------------
1 | has_pro() || Block_Gallery()->is_pro() ) {
36 | return $links;
37 | }
38 |
39 | // Generator the upgrade link.
40 | $url_generator = new Block_Gallery_URL_Generator();
41 |
42 | $url = $url_generator->get_store_url(
43 | 'pricing',
44 | array(
45 | 'utm_medium' => 'block-gallery-lite',
46 | 'utm_source' => 'plugins-page',
47 | 'utm_campaign' => 'plugins-row',
48 | 'utm_content' => 'go-pro',
49 | )
50 | );
51 |
52 | $links['go_pro'] = sprintf( '%2$s ', esc_url( $url ), esc_html__( 'Go Pro', '@@textdomain' ) );
53 |
54 | return $links;
55 | }
56 |
57 | /**
58 | * Plugin row meta links
59 | *
60 | * @param array $plugin_meta An array of the plugin's metadata.
61 | * @param string $plugin_file Path to the plugin file.
62 | * @return array $input
63 | */
64 | public function plugin_row_meta( $plugin_meta, $plugin_file ) {
65 |
66 | // Check if this is defined.
67 | if ( ! defined( 'BLOCKGALLERYPRO_PLUGIN_BASE' ) ) {
68 | define( 'BLOCKGALLERYPRO_PLUGIN_BASE', null );
69 | }
70 |
71 | $url_generator = new Block_Gallery_URL_Generator();
72 |
73 | $support_url = $url_generator->get_store_url(
74 | '/',
75 | array(
76 | 'utm_medium' => 'block-gallery-lite',
77 | 'utm_source' => 'plugins-page',
78 | 'utm_campaign' => 'plugins-row',
79 | 'utm_content' => 'help-and-faqs',
80 | )
81 | );
82 |
83 | if ( BLOCKGALLERY_PLUGIN_BASE === $plugin_file ) {
84 | $row_meta = array(
85 | 'docs' => '' . __( 'Help & FAQs', '@@textdomain' ) . ' ',
86 | 'review' => '' . __( 'Leave a Review', '@@textdomain' ) . ' ',
87 | );
88 |
89 | $plugin_meta = array_merge( $plugin_meta, $row_meta );
90 | }
91 |
92 | return $plugin_meta;
93 | }
94 | }
95 |
96 | new Block_Gallery_Action_Links();
97 |
--------------------------------------------------------------------------------
/src/styles/editor/_core-themes.scss:
--------------------------------------------------------------------------------
1 | .is-twentysixteen,
2 | .is-twentyseventeen,
3 | .is-twentythirteen,
4 | .is-twentytwelve {
5 |
6 | .blockgallery--primary-caption.editor-rich-text__tinymce {
7 | text-align: left;
8 | font-style: italic;
9 | margin-bottom: 0;
10 | }
11 | }
12 |
13 | .is-twentynineteen {
14 |
15 | @include blockGalleryEditorBlocks {
16 | margin-bottom: 48px;
17 | margin-top: 50px;
18 | }
19 |
20 | .blockgallery--primary-caption {
21 | margin-bottom: -10px;
22 | }
23 | }
24 |
25 | .is-twentyfifteen {
26 |
27 | @include blockGalleryEditorBlocks {
28 | margin-bottom: 42px;
29 | margin-top: 42px;
30 | }
31 |
32 | .blockgallery--primary-caption.editor-rich-text__tinymce {
33 | color: #707070;
34 | font-family: "Noto Sans", sans-serif;
35 | font-size: 12px;
36 | line-height: 1.5;
37 | margin-bottom: -5px;
38 |
39 | @media screen and (min-width: 46.25em) {
40 | font-size: 14px;
41 | }
42 |
43 | @media screen and (min-width: 55em) {
44 | font-size: 16px;
45 | }
46 |
47 | @media screen and (min-width: 59.6875em) {
48 | font-size: 12px;
49 | }
50 |
51 | @media screen and (min-width: 68.75em) {
52 | font-size: 14px;
53 | }
54 |
55 | @media screen and (min-width: 77.5em) {
56 | font-size: 16px;
57 | }
58 | }
59 | }
60 |
61 | .is-twentyfourteen {
62 |
63 | .blockgallery--primary-caption.editor-rich-text__tinymce {
64 | text-align: left;
65 | font-style: italic;
66 | margin-bottom: -7px;
67 | padding: 0;
68 | margin-top: 8px;
69 | }
70 | }
71 |
72 | .is-twentythirteen {
73 |
74 | @include blockGalleryEditorBlocks {
75 | margin-bottom: 40px;
76 | margin-top: 40px;
77 | }
78 |
79 | .blockgallery--primary-caption.editor-rich-text__tinymce {
80 | padding: 0;
81 | margin-top: 8px;
82 | }
83 | }
84 |
85 |
86 | .is-twentytwelve {
87 |
88 | // Override the theme's border radius behavior for our blocks.
89 | .blockgallery:not([class*="border-radius"]) img {
90 | border-radius: inherit;
91 | }
92 |
93 | .blockgallery--primary-caption.editor-rich-text__tinymce {
94 | padding: 0;
95 | margin-top: 1.1em;
96 | }
97 |
98 | .wp-block[data-type="blockgallery/stacked"] figcaption {
99 | text-align: center !important;
100 | }
101 | }
102 |
103 | .is-twentyeleven {
104 |
105 | // Override the theme's padding/border effect on our blocks.
106 | .blockgallery img {
107 | border: 0;
108 | padding: 0;
109 | max-width: 100%;
110 | }
111 |
112 | .blockgallery--primary-caption.editor-rich-text__tinymce {
113 | padding: 2px 0 0 40px;
114 | margin-bottom: -5px;
115 | text-align: left;
116 | font-family: Georgia, serif;
117 | font-size: 12px;
118 |
119 | &::before {
120 | color: #666;
121 | content: "\2014";
122 | font-size: 14px;
123 | font-style: normal;
124 | font-weight: 600;
125 | margin-right: 5px;
126 | position: absolute;
127 | left: 10px;
128 | top: 0;
129 | }
130 | }
131 |
132 | .wp-block[data-type="blockgallery/stacked"] figcaption {
133 | text-align: center;
134 | padding-left: 1em;
135 | padding-right: 1em;
136 |
137 | &::before {
138 | display: none;
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/blocks/carousel/components/inspector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import { title } from '../'
5 | import ResponsiveTabsControl from '../../../components/responsive-tabs-control';
6 | import SizeControl from '../../../components/size-control';
7 | import { BackgroundPanel } from '../../../components/background';
8 | import SliderPanel from '../../../components/slider-panel';
9 |
10 | /**
11 | * WordPress dependencies
12 | */
13 | const { __, sprintf } = wp.i18n;
14 | const { Component, Fragment } = wp.element;
15 | const { InspectorControls } = wp.editor;
16 | const { PanelBody, RangeControl } = wp.components;
17 |
18 | /**
19 | * Inspector controls
20 | */
21 | class Inspector extends Component {
22 |
23 | constructor( props ) {
24 | super( ...arguments );
25 | this.setSizeControl = this.setSizeControl.bind( this );
26 | this.setRadiusTo = this.setRadiusTo.bind( this );
27 | this.setHeightTo = this.setHeightTo.bind( this );
28 | }
29 |
30 | setRadiusTo( value ) {
31 | this.props.setAttributes( { radius: value } );
32 | }
33 |
34 | setSizeControl( value ) {
35 | this.props.setAttributes( { gridSize: value } );
36 | }
37 |
38 | setHeightTo( value ) {
39 | this.props.setAttributes( { height: value } );
40 | }
41 |
42 | render() {
43 |
44 | const {
45 | attributes,
46 | setAttributes,
47 | isSelected,
48 | } = this.props;
49 |
50 | const {
51 | align,
52 | gridSize,
53 | gutter,
54 | height,
55 | images,
56 | radius,
57 | } = attributes;
58 |
59 | return (
60 | isSelected && (
61 |
62 |
63 |
69 |
76 | { gridSize != null && ( align == 'wide' || align == 'full' ) &&
77 |
81 | }
82 | { gridSize != 'xlrg' && ! align &&
83 |
87 | }
88 |
96 | { gutter > 0 && }
104 |
105 |
106 |
109 |
110 |
111 | )
112 | )
113 | }
114 | };
115 |
116 | export default Inspector;
117 |
--------------------------------------------------------------------------------
/src/styles/editor/core/_colors.scss:
--------------------------------------------------------------------------------
1 | // Colors
2 |
3 | // Hugo's new WordPress shades of gray,
4 | // from http://codepen.io/hugobaeta/pen/grJjVp
5 | $black: #000;
6 | $dark-gray-900: #191e23;
7 | $dark-gray-800: #23282d;
8 | $dark-gray-700: #32373c;
9 | $dark-gray-600: #40464d;
10 | $dark-gray-500: #555d66; // Use this most of the time for dark items.
11 | $dark-gray-400: #606a73;
12 | $dark-gray-300: #6c7781; // Lightest gray that can be used for AA text contrast.
13 | $dark-gray-200: #7e8993;
14 | $dark-gray-150: #8d96a0; // Lightest gray that can be used for AA non-text contrast.
15 | $dark-gray-100: #8f98a1;
16 | $light-gray-900: #a2aab2;
17 | $light-gray-800: #b5bcc2;
18 | $light-gray-700: #ccd0d4;
19 | $light-gray-600: #d7dade;
20 | $light-gray-500: #e2e4e7; // Good for "grayed" items and borders
21 | $light-gray-400: #e8eaeb;
22 | $light-gray-300: #edeff0;
23 | $light-gray-200: #f3f4f5;
24 | $light-gray-100: #f8f9f9;
25 | $white: #fff;
26 |
27 | // Dark opacities, for use with light themes.
28 | $dark-opacity-900: rgba(#000510, 0.9);
29 | $dark-opacity-800: rgba(#00000a, 0.85);
30 | $dark-opacity-700: rgba(#06060b, 0.8);
31 | $dark-opacity-600: rgba(#000913, 0.75);
32 | $dark-opacity-500: rgba(#0a1829, 0.7);
33 | $dark-opacity-400: rgba(#0a1829, 0.65);
34 | $dark-opacity-300: rgba(#0e1c2e, 0.62);
35 | $dark-opacity-200: rgba(#162435, 0.55);
36 | $dark-opacity-100: rgba(#223443, 0.5);
37 | $dark-opacity-light-900: rgba(#304455, 0.45);
38 | $dark-opacity-light-800: rgba(#425863, 0.4);
39 | $dark-opacity-light-700: rgba(#667886, 0.35);
40 | $dark-opacity-light-600: rgba(#7b86a2, 0.3);
41 | $dark-opacity-light-500: rgba(#9197a2, 0.25);
42 | $dark-opacity-light-400: rgba(#95959c, 0.2);
43 | $dark-opacity-light-300: rgba(#829493, 0.15);
44 | $dark-opacity-light-200: rgba(#8b8b96, 0.1);
45 | $dark-opacity-light-100: rgba(#747474, 0.05);
46 |
47 | // Light opacities, for use with dark themes.
48 | $light-opacity-900: rgba($white, 1);
49 | $light-opacity-800: rgba($white, 0.9);
50 | $light-opacity-700: rgba($white, 0.85);
51 | $light-opacity-600: rgba($white, 0.8);
52 | $light-opacity-500: rgba($white, 0.75);
53 | $light-opacity-400: rgba($white, 0.7);
54 | $light-opacity-300: rgba($white, 0.65);
55 | $light-opacity-200: rgba($white, 0.6);
56 | $light-opacity-100: rgba($white, 0.55);
57 | $light-opacity-light-900: rgba($white, 0.5);
58 | $light-opacity-light-800: rgba($white, 0.45);
59 | $light-opacity-light-700: rgba($white, 0.4);
60 | $light-opacity-light-600: rgba($white, 0.35);
61 | $light-opacity-light-500: rgba($white, 0.3);
62 | $light-opacity-light-400: rgba($white, 0.25);
63 | $light-opacity-light-300: rgba($white, 0.2);
64 | $light-opacity-light-200: rgba($white, 0.15);
65 | $light-opacity-light-100: rgba($white, 0.1);
66 |
67 | // Additional colors
68 | // some from https://make.wordpress.org/design/handbook/foundations/colors/
69 | $blue-wordpress-700: #00669b;
70 | $blue-dark-900: #0071a1;
71 |
72 | $blue-medium-900: #006589;
73 | $blue-medium-800: #00739c;
74 | $blue-medium-700: #007fac;
75 | $blue-medium-600: #008dbe;
76 | $blue-medium-500: #00a0d2;
77 | $blue-medium-400: #33b3db;
78 | $blue-medium-300: #66c6e4;
79 | $blue-medium-200: #bfe7f3;
80 | $blue-medium-100: #e5f5fa;
81 | $blue-medium-highlight: #b3e7fe;
82 | $blue-medium-focus: #007cba;
83 |
84 | // Alert colors
85 | $alert-yellow: #f0b849;
86 | $alert-red: #d94f4f;
87 | $alert-green: #4ab866;
88 |
--------------------------------------------------------------------------------
/src/styles/style/_flickity.scss:
--------------------------------------------------------------------------------
1 | // Flickity v2.1.2
2 | .flickity-enabled {
3 | position: relative;
4 |
5 | &:focus {
6 | outline: none;
7 | }
8 |
9 | &.is-draggable {
10 | user-select: none;
11 | }
12 | }
13 |
14 | .flickity-viewport {
15 | overflow: hidden;
16 | position: relative;
17 | height: 100%;
18 |
19 | .is-cropped & {
20 | height: 100% !important;
21 | }
22 | }
23 |
24 | .flickity-slider {
25 | position: absolute;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | .flickity-enabled.is-draggable .flickity-viewport {
31 | cursor: move;
32 | cursor: -webkit-grab;
33 | cursor: grab;
34 | }
35 |
36 | .flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
37 | cursor: -webkit-grabbing;
38 | cursor: grabbing;
39 | }
40 |
41 | .flickity-button {
42 | background: hsla(0, 0%, 100%, 0.75);
43 | border: none;
44 | color: #000;
45 | position: absolute;
46 | padding: 0;
47 | transition: background 100ms, opacity 100ms;
48 |
49 | &:hover {
50 | background: $white;
51 | cursor: pointer;
52 | }
53 |
54 | &:focus {
55 | outline: none;
56 | border: none;
57 | background: $white;
58 | box-shadow: 0 0 0 2px #000;
59 | }
60 |
61 | &:active {
62 | border: none;
63 | opacity: 0.6;
64 | }
65 |
66 | &:disabled {
67 | opacity: 0.25;
68 | cursor: auto;
69 | pointer-events: none;
70 | }
71 | }
72 |
73 | .flickity-button-icon {
74 | fill: #000;
75 | transform: translate3d(0, 0, 0);
76 | }
77 |
78 | .flickity-prev-next-button {
79 | top: 50%;
80 | width: 57px;
81 | height: 72px;
82 | border-radius: 9px;
83 | transform: translateY(-50%);
84 |
85 | .has-top-left-carousel-arrows & {
86 | top: 20px;
87 | transform: none;
88 | width: 42px;
89 | height: 42px;
90 | border-radius: 4px;
91 |
92 | &.previous {
93 | left: 20px;
94 | }
95 |
96 | &.next {
97 | left: calc(25px + 42px);
98 | }
99 | }
100 | }
101 |
102 | .flickity-prev-next-button.previous {
103 | left: 10px;
104 |
105 | @media (min-width: 600px) {
106 | left: 20px;
107 | }
108 | }
109 |
110 | .flickity-prev-next-button.next {
111 | right: 10px;
112 |
113 | @media (min-width: 600px) {
114 | right: 20px;
115 | }
116 | }
117 |
118 | .flickity-rtl .flickity-prev-next-button.previous {
119 | left: auto;
120 | right: 10px;
121 |
122 | @media (min-width: 600px) {
123 | right: 20px;
124 | }
125 | }
126 |
127 | .flickity-rtl .flickity-prev-next-button.next {
128 | right: auto;
129 | left: 10px;
130 |
131 | @media (min-width: 600px) {
132 | left: 20px;
133 | }
134 | }
135 |
136 | .flickity-prev-next-button .flickity-button-icon {
137 | position: absolute;
138 | left: 23%;
139 | top: 25%;
140 | width: 50%;
141 | height: 50%;
142 | }
143 |
144 | .previous.flickity-prev-next-button .flickity-button-icon {
145 | left: 26%;
146 | }
147 |
148 | .flickity-page-dots {
149 | position: absolute;
150 | width: 100%;
151 | bottom: 18px;
152 | padding: 0 !important;
153 | margin: 0 !important;
154 | list-style: none;
155 | text-align: center;
156 | line-height: 1;
157 | }
158 |
159 | .flickity-rtl .flickity-page-dots {
160 | direction: rtl;
161 | }
162 |
163 | .flickity-page-dots .dot {
164 | display: inline-block;
165 | width: 9px;
166 | height: 9px;
167 | margin: 0 6px;
168 | background: hsla(0, 0%, 0, 0.3);
169 | border-radius: 50%;
170 | cursor: pointer;
171 | }
172 |
173 | .flickity-page-dots .dot.is-selected {
174 | background: hsla(0, 0, 100%, 0.75);
175 | }
176 |
--------------------------------------------------------------------------------
/src/components/slider-panel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import map from 'lodash/map';
5 |
6 | /**
7 | * Internal dependencies
8 | */
9 | import autoPlayOptions from '../utils/autoplay-options';
10 |
11 | /**
12 | * WordPress dependencies
13 | */
14 | const { __, sprintf } = wp.i18n;
15 | const { Component, Fragment } = wp.element;
16 | const { PanelBody, ToggleControl, SelectControl } = wp.components;
17 |
18 | class SliderPanel extends Component {
19 |
20 | constructor( props ) {
21 | super( ...arguments );
22 | this.getAutoPlayHelp = this.getAutoPlayHelp.bind( this );
23 | }
24 |
25 | getAutoPlayHelp( checked ) {
26 | // Retrieve the height value and divide it to display full seconds.
27 | const speed = this.props.attributes.autoPlaySpeed / 1000;
28 | const time = ( speed > 1 ) ? __( 'seconds' ) : __( 'second' );
29 |
30 | // translators: 1. Speed of the slider, 2: Time until the slide advances
31 | return checked ? sprintf( __( 'Automatically advancing to the next gallery item after %1$d %2$s.' ), speed, time ) : __( 'Automatically advance to the next gallery item after a set duration.' );
32 | }
33 |
34 | getDraggableHelp( checked ) {
35 | return checked ? __( 'Dragging and flicking enabled on desktop and mobile devices.' ) : __( 'Toggle to enable drag functionality on desktop and mobile devices.' );
36 | }
37 |
38 | getArrowNavigationHelp( checked ) {
39 | return checked ? __( 'Showing slide navigation arrows.' ) : __( 'Toggle to show slide navigation arrows.' );
40 | }
41 |
42 | getDotNavigationHelp( checked ) {
43 | return checked ? __( 'Showing dot navigation arrows.' ) : __( 'Toggle to show dot navigation.' );
44 | }
45 |
46 | render() {
47 |
48 | const {
49 | attributes,
50 | setAttributes,
51 | } = this.props;
52 |
53 | const {
54 | autoPlay,
55 | autoPlaySpeed,
56 | draggable,
57 | pageDots,
58 | prevNextButtons,
59 | } = attributes;
60 |
61 | return (
62 |
63 |
64 | setAttributes( { autoPlay: ! autoPlay } ) }
68 | help={ this.getAutoPlayHelp }
69 | />
70 | { autoPlay && setAttributes( { autoPlaySpeed: value } ) }
75 | options={ autoPlayOptions }
76 | className='components-blockgallery-inspector__autoplayspeed-select'
77 | /> }
78 | setAttributes( { draggable: ! draggable } ) }
82 | help={ this.getDraggableHelp }
83 | />
84 | setAttributes( { prevNextButtons: ! prevNextButtons } ) }
88 | help={ this.getArrowNavigationHelp }
89 | />
90 | setAttributes( { pageDots: ! pageDots } ) }
94 | help={ this.getDotNavigationHelp }
95 | />
96 |
97 |
98 | );
99 | }
100 | }
101 |
102 | export default SliderPanel;
--------------------------------------------------------------------------------
/src/components/global/toolbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import * as helper from './../../utils/helper';
5 | import icons from './../../utils/icons';
6 | import { BackgroundToolbar } from '../../components/background';
7 |
8 | /**
9 | * WordPress dependencies
10 | */
11 | const { __ } = wp.i18n;
12 | const { Component, Fragment } = wp.element;
13 | const {
14 | IconButton,
15 | Toolbar,
16 | DropdownMenu,
17 | } = wp.components;
18 | const {
19 | BlockControls,
20 | MediaUpload,
21 | MediaUploadCheck,
22 | AlignmentToolbar,
23 | } = wp.editor;
24 |
25 | /**
26 | * Global Toolbar Component
27 | */
28 | class GlobalToolbar extends Component {
29 |
30 | constructor( props ) {
31 | super( ...arguments );
32 | this.onSelectImages = this.onSelectImages.bind( this );
33 | }
34 |
35 | onSelectImages( images ) {
36 | this.props.setAttributes( {
37 | images: images.map( ( image ) => helper.pickRelevantMediaFiles( image ) ),
38 | } );
39 | }
40 |
41 | render() {
42 |
43 | const {
44 | attributes,
45 | isSelected,
46 | setAttributes,
47 | hasAlignmentToolbar,
48 | } = this.props;
49 |
50 | const {
51 | images,
52 | contentAlign,
53 | } = attributes;
54 |
55 | return (
56 | isSelected && (
57 |
58 |
59 | { hasAlignmentToolbar && (
60 | {
63 | setAttributes( { contentAlign: nextAlign } );
64 | } }
65 | />
66 | ) }
67 |
68 | { !! images.length && (
69 |
70 |
71 | img.id ) }
77 | render={ ( { open } ) => (
78 |
84 | ) }
85 | />
86 |
87 | { setAttributes( { filter: 'grayscale' } ) },
95 | },
96 | {
97 | icon: icons.imageFilterSepia,
98 | title: __( 'Sepia' ),
99 | onClick: () => { setAttributes( { filter: 'sepia' } ) },
100 | },
101 | {
102 | icon: icons.imageFilterSaturation,
103 | title: __( 'Saturation' ),
104 | onClick: () => { setAttributes( { filter: 'saturation' } ) },
105 | },
106 | {
107 | icon: icons.imageFilterDark,
108 | title: __( 'Dark' ),
109 | onClick: () => { setAttributes( { filter: 'dim' } ) },
110 | },
111 | {
112 | icon: icons.imageFilterVintage,
113 | title: __( 'Vintage' ),
114 | onClick: () => { setAttributes( { filter: 'vintage' } ) },
115 | },
116 | {
117 | icon: icons.image,
118 | title: __( 'Original' ),
119 | onClick: () => { setAttributes( { filter: 'none' } ) },
120 | },
121 | ] }
122 | />
123 |
124 | ) }
125 |
126 |
127 | )
128 | )
129 | }
130 | }
131 |
132 | export default GlobalToolbar;
133 |
--------------------------------------------------------------------------------
/src/styles/style/_core-themes.scss:
--------------------------------------------------------------------------------
1 | .is-twentynineteen {
2 |
3 | .entry-content div[class*="wp-block-blockgallery"] {
4 | margin-bottom: 46px;
5 | margin-top: 46px;
6 |
7 | .blockgallery--caption {
8 | font-size: 0.71111em;
9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
10 | line-height: 1.6;
11 | margin: 0 0 -20px;
12 | padding: 0.5rem;
13 | text-align: center;
14 | }
15 | }
16 |
17 | .wp-block-blockgallery-stacked figcaption:not([class*="font-size"]) {
18 | font-size: 0.71111em;
19 | }
20 | }
21 |
22 | .is-twentyseventeen {
23 |
24 | .entry-content div[class*="wp-block-blockgallery"] {
25 | margin-top: 1.65em;
26 | margin-bottom: 1.75em;
27 |
28 | .blockgallery--primary-caption {
29 | font-style: italic;
30 | margin-bottom: -10px;
31 | margin-top: 1em;
32 | }
33 |
34 | &:not([class*="masonry"]) {
35 |
36 | .blockgallery:not(.has-caption-color) figcaption {
37 | color: #555d66;
38 | }
39 | }
40 | }
41 |
42 | .wp-block-blockgallery-masonry figcaption {
43 | padding-bottom: 6px;
44 | }
45 | }
46 |
47 | .is-twentysixteen {
48 |
49 | .entry-content div[class*="wp-block-blockgallery"] {
50 | margin-top: 2.3em;
51 | margin-bottom: 2.3em;
52 |
53 | .blockgallery--primary-caption {
54 | margin-bottom: -10px;
55 | margin-top: 0.6em;
56 | }
57 | }
58 |
59 | .wp-block-blockgallery-masonry figcaption {
60 | padding-bottom: 6px;
61 | }
62 | }
63 |
64 | .is-twentyfifteen {
65 |
66 | .entry-content div[class*="wp-block-blockgallery"] {
67 | margin-top: 2.2em;
68 | margin-bottom: 2.2em;
69 |
70 | .blockgallery--primary-caption {
71 | margin-top: 0.5em;
72 | padding-bottom: 0;
73 | margin-bottom: -15px;
74 | }
75 | }
76 |
77 | .wp-block-blockgallery-masonry figcaption {
78 | font-size: 13px !important;
79 | }
80 | }
81 |
82 | .is-twentyfourteen {
83 |
84 | .entry-content div[class*="wp-block-blockgallery"] {
85 | margin-top: 30px;
86 | margin-bottom: 30px;
87 |
88 | .blockgallery--primary-caption {
89 | text-align: left;
90 | margin-bottom: -7px;
91 | }
92 | }
93 | }
94 |
95 | .is-twentythirteen {
96 |
97 | .entry-content div[class*="wp-block-blockgallery"] {
98 | margin-top: 34px;
99 | margin-bottom: 30px;
100 |
101 | .blockgallery--primary-caption {
102 | margin-bottom: -10px;
103 | }
104 | }
105 |
106 | .wp-block-blockgallery-masonry figcaption {
107 | font-size: 13px !important;
108 | }
109 | }
110 |
111 | .is-twentytwelve {
112 |
113 | .entry-content div[class*="wp-block-blockgallery"] {
114 | margin-top: 32px;
115 | margin-bottom: 32px;
116 |
117 | // Override the theme's border radius behavior for our blocks.
118 | .blockgallery:not([class*="border-radius"]) img {
119 | border-radius: inherit;
120 | }
121 |
122 | .blockgallery--primary-caption {
123 | margin-bottom: -10px;
124 | }
125 | }
126 |
127 | .wp-block-blockgallery-masonry figcaption {
128 | padding-bottom: 6px;
129 | }
130 | }
131 |
132 | .is-twentyeleven {
133 |
134 | .entry-content div[class*="wp-block-blockgallery"] {
135 | margin-top: 33px;
136 | margin-bottom: 32px;
137 |
138 | // Override the theme's padding/border effect on our blocks.
139 | .blockgallery img {
140 | border: 0;
141 | padding: 0;
142 | max-width: 100%;
143 | }
144 |
145 | .blockgallery--primary-caption {
146 | margin-bottom: -15px;
147 |
148 | &::before {
149 | color: #666;
150 | content: '\2014';
151 | font-size: 14px;
152 | font-style: normal;
153 | font-weight: 600;
154 | margin-right: 5px;
155 | position: absolute;
156 | left: 10px;
157 | top: 0;
158 | }
159 | }
160 | }
161 |
162 | .wp-block-blockgallery-stacked figcaption {
163 | text-align: center !important;
164 | padding-left: 1em !important;
165 | padding-right: 1em !important;
166 |
167 | &::before {
168 | display: none;
169 | padding-left: 0;
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/blocks/masonry/components/inspector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import { title } from '../'
5 | import ResponsiveTabsControl from '../../../components/responsive-tabs-control';
6 | import linkOptions from '../../../utils/link-options';
7 | import captionOptions from '../../../utils/caption-options';
8 | import SizeControl from '../../../components/size-control';
9 | import { LightboxControl } from '../../../components/lightbox';
10 | import { BackgroundPanel } from '../../../components/background';
11 |
12 | /**
13 | * WordPress dependencies
14 | */
15 | const { __, sprintf } = wp.i18n;
16 | const { Component, Fragment } = wp.element;
17 | const { InspectorControls } = wp.editor;
18 | const { PanelBody, RangeControl, ToggleControl, SelectControl } = wp.components;
19 |
20 | /**
21 | * Inspector controls
22 | */
23 | class Inspector extends Component {
24 |
25 | constructor( props ) {
26 | super( ...arguments );
27 | this.setSizeControl = this.setSizeControl.bind( this );
28 | this.setLinkTo = this.setLinkTo.bind( this );
29 | this.setRadiusTo = this.setRadiusTo.bind( this );
30 | this.setCaptionStyleTo = this.setCaptionStyleTo.bind( this );
31 | }
32 |
33 | componentDidUpdate( prevProps ) {
34 | if ( this.props.attributes.gutter <= 0 ) {
35 | this.props.setAttributes( {
36 | radius: 0,
37 | } );
38 | }
39 | }
40 |
41 | setLinkTo( value ) {
42 | this.props.setAttributes( { linkTo: value } );
43 | }
44 |
45 | setRadiusTo( value ) {
46 | this.props.setAttributes( { radius: value } );
47 | }
48 |
49 | setSizeControl( value ) {
50 | this.props.setAttributes( { gridSize: value } );
51 | }
52 |
53 | setCaptionStyleTo( value ) {
54 | this.props.setAttributes( { captionStyle: value } );
55 | }
56 |
57 | getCaptionsHelp( checked ) {
58 | return checked ? __( 'Showing captions for each media item.' ) : __( 'Toggle to show media captions.' );
59 | }
60 |
61 | render() {
62 |
63 | const {
64 | attributes,
65 | setAttributes,
66 | isSelected,
67 | } = this.props;
68 |
69 | const {
70 | align,
71 | captionStyle,
72 | gridSize,
73 | gutter,
74 | images,
75 | lightbox,
76 | linkTo,
77 | radius,
78 | captions,
79 | } = attributes;
80 |
81 | return (
82 | isSelected && (
83 |
84 |
85 |
86 |
93 |
94 | { gutter > 0 && }
103 |
104 | setAttributes( { captions: ! captions } ) }
108 | help={ this.getCaptionsHelp }
109 | />
110 | { captions &&
111 |
117 | }
118 |
119 | { ! lightbox &&
123 |
129 | }
130 |
133 |
134 |
135 | )
136 | )
137 | }
138 | };
139 |
140 | export default Inspector;
141 |
--------------------------------------------------------------------------------
/src/blocks/stacked/components/inspector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | import { title } from '../'
5 | import ResponsiveTabsControl from '../../../components/responsive-tabs-control';
6 | import linkOptions from '../../../utils/link-options';
7 | import SizeControl from '../../../components/size-control';
8 | import { LightboxControl } from '../../../components/lightbox';
9 | import { BackgroundPanel } from '../../../components/background';
10 |
11 | /**
12 | * WordPress dependencies
13 | */
14 | const { __, sprintf } = wp.i18n;
15 | const { Component, Fragment } = wp.element;
16 | const { compose } = wp.compose;
17 | const { withSelect } = wp.data;
18 | const { InspectorControls, FontSizePicker, withFontSizes } = wp.editor;
19 | const { PanelBody, RangeControl, ToggleControl, SelectControl } = wp.components;
20 |
21 | /**
22 | * Inspector controls
23 | */
24 | class Inspector extends Component {
25 |
26 | constructor( props ) {
27 | super( ...arguments );
28 | this.setLinkTo = this.setLinkTo.bind( this );
29 | this.setRadiusTo = this.setRadiusTo.bind( this );
30 | this.setFullwidthTo = this.setFullwidthTo.bind( this );
31 | this.setShadowTo = this.setShadowTo.bind( this );
32 | }
33 |
34 | setLinkTo( value ) {
35 | this.props.setAttributes( { linkTo: value } );
36 | }
37 |
38 | setRadiusTo( value ) {
39 | this.props.setAttributes( { radius: value } );
40 | }
41 |
42 | setShadowTo( value ) {
43 | this.props.setAttributes( { shadow: value } );
44 | }
45 |
46 | setFullwidthTo() {
47 | this.props.setAttributes( { fullwidth: ! this.props.attributes.fullwidth, shadow: 'none' } );
48 | }
49 |
50 | getFullwidthImagesHelp( checked ) {
51 | return checked ? __( 'Fullwidth images are enabled.' ) : __( 'Toggle to fill the available gallery area with completely fullwidth images.' );
52 | }
53 |
54 | getCaptionsHelp( checked ) {
55 | return checked ? __( 'Showing captions for each media item.' ) : __( 'Toggle to show media captions.' );
56 | }
57 |
58 | render() {
59 |
60 | const {
61 | attributes,
62 | setAttributes,
63 | isSelected,
64 | setFontSize,
65 | fontSize,
66 | wideControlsEnabled = false,
67 | } = this.props;
68 |
69 | const {
70 | align,
71 | images,
72 | linkTo,
73 | gutter,
74 | lightbox,
75 | fullwidth,
76 | radius,
77 | shadow,
78 | captions,
79 | backgroundPadding,
80 | } = attributes;
81 |
82 | return (
83 | isSelected && (
84 |
85 |
86 |
87 | { wideControlsEnabled &&
88 | 1 ? __( 'Fullwidth Images' ) : __( 'Fullwidth Image' ) }
90 | checked={ !! fullwidth }
91 | help={ this.getFullwidthImagesHelp }
92 | onChange={ this.setFullwidthTo }
93 | />
94 | }
95 | { images.length > 1 &&
96 |
99 | }
100 | { gutter > 0 && }
108 | { ! fullwidth && }
116 |
117 | setAttributes( { captions: ! captions } ) }
121 | help={ this.getCaptionsHelp }
122 | />
123 | { captions &&
124 |
128 | }
129 |
130 | { ! lightbox &&
134 |
140 | }
141 |
144 |
145 |
146 | )
147 | )
148 | }
149 | };
150 |
151 | export default compose( [
152 | withSelect( ( select ) => ( {
153 | wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide,
154 | } ) ),
155 | withFontSizes( 'fontSize' ),
156 | ] )( Inspector );
157 |
--------------------------------------------------------------------------------
/src/components/size-control.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import map from 'lodash/map';
6 |
7 | /**
8 | * WordPress dependencies
9 | */
10 | const { __ } = wp.i18n;
11 | const { compose } = wp.compose;
12 | const { Component, Fragment } = wp.element;
13 | const { ButtonGroup, Button } = wp.components;
14 | const { withSelect } = wp.data;
15 |
16 | class SizeControl extends Component {
17 |
18 | constructor( props ) {
19 | super( ...arguments );
20 | this.getSizes = this.getSizes.bind( this );
21 | }
22 |
23 | componentDidUpdate( prevProps ) {
24 |
25 | const { align, columns, gridSize } = this.props.attributes;
26 |
27 | // Prevent small and medium column grid sizes without wide or full alignments.
28 | if ( align == undefined ) {
29 | if ( columns == 'med' || columns == 'sml' ) {
30 | this.props.setAttributes( {
31 | gridSize: 'xlrg',
32 | } );
33 | }
34 | }
35 | }
36 |
37 | getSizes() {
38 | const { type, wideControlsEnabled } = this.props;
39 | const { align } = this.props.attributes;
40 |
41 | const defaultSizes = [
42 | {
43 | shortName: 'None',
44 | size: 'none',
45 | },
46 | {
47 | shortName: 'S',
48 | size: 'sml',
49 | },
50 | {
51 | shortName: 'M',
52 | size: 'med',
53 | },
54 | {
55 | shortName: 'L',
56 | size: 'lrg',
57 | },
58 | ];
59 |
60 | const standardSizes = [
61 | {
62 | shortName: wideControlsEnabled == true && 'wide' == align || 'full' == align ? 'L' : __( 'Large' ),
63 | size: 'lrg',
64 | },
65 | {
66 | shortName: wideControlsEnabled == true && 'wide' == align || 'full' == align ? 'XL' : __( 'Extra Large' ),
67 | size: 'xlrg',
68 | },
69 | ];
70 |
71 | const wideSizes = [
72 | {
73 | shortName: 'M',
74 | size: 'med',
75 | },
76 | ];
77 |
78 | const fullSizes = [
79 | {
80 | shortName: 'S',
81 | size: 'sml',
82 | },
83 | ];
84 |
85 | // If this is a standard size settings, not a complex grid sizer.
86 | if ( 'smlx' == type ) {
87 |
88 | const standardSizes = [
89 | {
90 | shortName: 'S',
91 | size: 'sml',
92 | },
93 | {
94 | shortName: 'M',
95 | size: 'med',
96 | },
97 | {
98 | shortName: 'L',
99 | size: 'lrg',
100 | },
101 | {
102 | shortName: 'XL',
103 | size: 'xlrg',
104 | },
105 | ];
106 |
107 | return standardSizes;
108 | }
109 |
110 | // If this is a standard size settings, not a complex grid sizer.
111 | if ( 'reverse-grid' == type ) {
112 |
113 | const standardSizes = [
114 | {
115 | shortName: wideControlsEnabled == true && 'wide' == align || 'full' == align ? 'S' : __( 'Small' ),
116 | size: 'sml',
117 | },
118 | {
119 | shortName: wideControlsEnabled == true && 'wide' == align || 'full' == align ? 'M' : __( 'Medium' ),
120 | size: 'med',
121 | },
122 | ];
123 |
124 | const wideSizes = [
125 | {
126 | shortName: 'L',
127 | size: 'lrg',
128 | },
129 | ];
130 |
131 | const fullSizes = [
132 | {
133 | shortName: 'XL',
134 | size: 'xlrg',
135 | },
136 | ];
137 |
138 | if ( 'wide' == align ) {
139 | return standardSizes.concat( wideSizes );
140 | } else if ( 'full' == align ) {
141 | return standardSizes.concat( wideSizes ).concat( fullSizes );
142 | } else {
143 | return standardSizes;
144 | }
145 | }
146 |
147 | // If this is a standard size settings, not a complex grid sizer.
148 | if ( 'grid' != type ) {
149 | return defaultSizes;
150 | }
151 |
152 | if ( wideControlsEnabled == true && 'wide' == align ) {
153 | return wideSizes.concat( standardSizes );
154 | } else if ( wideControlsEnabled == true && 'full' == align ) {
155 | return fullSizes.concat( wideSizes ).concat( standardSizes );
156 | } else {
157 | return standardSizes;
158 | }
159 | }
160 |
161 | render() {
162 |
163 | const {
164 | onChange,
165 | value,
166 | resetValue = undefined,
167 | label,
168 | className,
169 | reset = true,
170 | } = this.props;
171 |
172 | const classes = classnames(
173 | 'components-blockgallery-inspector__size-control', {
174 | [ className ] : className,
175 | }
176 | );
177 |
178 | return (
179 |
180 | { label && { label }
}
181 |
182 |
183 | { map( this.getSizes(), ( { size, shortName } ) => (
184 | onChange( size ) }
190 | >
191 | { shortName }
192 |
193 | ) ) }
194 |
195 | { reset &&
196 | onChange( resetValue ) }
199 | >
200 | { __( 'Reset' ) }
201 |
202 | }
203 |
204 |
205 | );
206 | }
207 | }
208 |
209 | export default compose( [
210 | withSelect( ( select ) => ( {
211 | wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide,
212 | } ) ),
213 | ] )( SizeControl );
--------------------------------------------------------------------------------
/includes/class-block-gallery-block-assets.php:
--------------------------------------------------------------------------------
1 | _version = BLOCKGALLERY_VERSION;
70 | $this->_slug = 'block-gallery';
71 | $this->_dir = untrailingslashit( plugin_dir_path( '/', __FILE__ ) );
72 | $this->_url = untrailingslashit( plugins_url( '/', dirname( __FILE__ ) ) );
73 |
74 | add_action( 'init', array( $this, 'register_blocks' ), 99 );
75 | add_action( 'init', array( $this, 'block_assets' ) );
76 | add_action( 'init', array( $this, 'editor_assets' ) );
77 | add_action( 'enqueue_block_editor_assets', array( $this, 'localization' ) );
78 | add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
79 | add_action( 'the_post', array( $this, 'frontend_scripts' ) );
80 | }
81 |
82 | /**
83 | * Add actions to enqueue assets.
84 | *
85 | * @access public
86 | */
87 | public function register_blocks() {
88 |
89 | // Return early if this function does not exist.
90 | if ( ! function_exists( 'register_block_type' ) ) {
91 | return;
92 | }
93 |
94 | // Shortcut for the slug.
95 | $slug = $this->_slug;
96 |
97 | register_block_type(
98 | 'blockgallery/carousel',
99 | array(
100 | 'editor_script' => $slug . '-editor',
101 | 'editor_style' => $slug . '-editor',
102 | 'style' => $slug . '-frontend',
103 | )
104 | );
105 | register_block_type(
106 | 'blockgallery/masonry',
107 | array(
108 | 'editor_script' => $slug . '-editor',
109 | 'editor_style' => $slug . '-editor',
110 | 'style' => $slug . '-frontend',
111 | )
112 | );
113 |
114 | register_block_type(
115 | 'blockgallery/stacked',
116 | array(
117 | 'editor_script' => $slug . '-editor',
118 | 'editor_style' => $slug . '-editor',
119 | 'style' => $slug . '-frontend',
120 | )
121 | );
122 | }
123 |
124 | /**
125 | * Enqueue block assets for use within Gutenberg.
126 | *
127 | * @access public
128 | */
129 | public function block_assets() {
130 |
131 | // Styles.
132 | wp_register_style(
133 | $this->_slug . '-frontend',
134 | $this->_url . '/dist/blocks.style.build.css',
135 | array(),
136 | $this->_version
137 | );
138 | }
139 |
140 | /**
141 | * Enqueue block assets for use within Gutenberg.
142 | *
143 | * @access public
144 | */
145 | public function editor_assets() {
146 |
147 | // Styles.
148 | wp_register_style(
149 | $this->_slug . '-editor',
150 | $this->_url . '/dist/blocks.editor.build.css',
151 | array(),
152 | $this->_version
153 | );
154 |
155 | // Scripts.
156 | wp_register_script(
157 | $this->_slug . '-editor',
158 | $this->_url . '/dist/blocks.build.js',
159 | array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-plugins', 'wp-components', 'wp-edit-post', 'wp-api' ),
160 | $this->_version,
161 | true
162 | );
163 | }
164 |
165 | /**
166 | * Enqueue front-end assets for blocks.
167 | *
168 | * @access public
169 | */
170 | public function frontend_scripts() {
171 |
172 | // Define where the asset is loaded from.
173 | $dir = Block_Gallery()->asset_source( 'js' );
174 |
175 | // Define where the vendor asset is loaded from.
176 | $vendors_dir = Block_Gallery()->asset_source( 'js', 'vendors' );
177 |
178 | // Masonry block.
179 | if ( has_block( 'blockgallery/masonry' ) ) {
180 | wp_enqueue_script(
181 | $this->_slug . '-masonry',
182 | $dir . $this->_slug . '-masonry' . BLOCKGALLERY_ASSET_SUFFIX . '.js',
183 | array( 'jquery', 'masonry', 'imagesloaded' ),
184 | $this->_version,
185 | true
186 | );
187 | }
188 |
189 | // Carousel block.
190 | if ( has_block( 'blockgallery/carousel' ) ) {
191 | wp_enqueue_script(
192 | $this->_slug . '-flickity',
193 | $vendors_dir . 'flickity' . BLOCKGALLERY_ASSET_SUFFIX . '.js',
194 | array( 'jquery' ),
195 | $this->_version,
196 | true
197 | );
198 | }
199 | }
200 |
201 | /**
202 | * Enqueue Jed-formatted localization data.
203 | *
204 | * @access public
205 | */
206 | public function localization() {
207 | if ( function_exists( 'wp_set_script_translations' ) ) {
208 | wp_set_script_translations( $this->_slug . '-editor', '@@textdomain' );
209 | }
210 | }
211 | }
212 |
213 | Block_Gallery_Block_Assets::register();
214 |
--------------------------------------------------------------------------------
/src/blocks/masonry/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import filter from 'lodash/filter';
6 |
7 | /**
8 | * Internal dependencies
9 | */
10 | import './styles/style.scss';
11 | import './styles/editor.scss';
12 | import icons from './../../utils/icons';
13 | import Edit from './components/edit';
14 | import { BackgroundStyles } from '../../components/background/';
15 | import { GlobalAttributes, GlobalTransforms, GlobalClasses, GlobalStyles } from '../../components/global/';
16 |
17 | /**
18 | * WordPress dependencies
19 | */
20 | const { __ } = wp.i18n;
21 | const { createBlock } = wp.blocks;
22 | const { RichText } = wp.editor;
23 |
24 | /**
25 | * Block constants
26 | */
27 | const name = 'masonry';
28 |
29 | const title = __( 'Masonry' );
30 |
31 | const icon = icons.masonry;
32 |
33 | const keywords = [
34 | __( 'gallery' ),
35 | __( 'images' ),
36 | __( 'photos' ),
37 | ];
38 |
39 | const blockAttributes = {
40 | ...GlobalAttributes,
41 |
42 | // Block specific attributes.
43 | gridSize: {
44 | type: 'string',
45 | default: 'xlrg',
46 | },
47 | };
48 |
49 | const settings = {
50 |
51 | title: title,
52 |
53 | description: __( 'Display multiple images in an organized masonry gallery.' ),
54 |
55 | keywords: keywords,
56 |
57 | attributes: blockAttributes,
58 |
59 | supports: {
60 | align: [ 'wide', 'full' ],
61 | },
62 |
63 | transforms: {
64 | from: [
65 | {
66 | type: 'block',
67 | blocks: [ 'blockgallery/stacked' ],
68 | transform: ( attributes ) => (
69 | createBlock( `blockgallery/${ name }`, {
70 | ...GlobalTransforms( attributes ),
71 | } )
72 | ),
73 | },
74 | {
75 | type: 'block',
76 | blocks: [ 'blockgallery/carousel' ],
77 | transform: ( attributes ) => (
78 | createBlock( `blockgallery/${ name }`, {
79 | ...GlobalTransforms( attributes ),
80 | } )
81 | ),
82 | },
83 | {
84 | type: 'block',
85 | blocks: [ 'blockgallery/thumbnails' ],
86 | transform: ( attributes ) => (
87 | createBlock( `blockgallery/${ name }`, {
88 | ...GlobalTransforms( attributes ),
89 | } )
90 | ),
91 | },
92 | {
93 | type: 'block',
94 | blocks: [ 'blockgallery/offset' ],
95 | transform: ( attributes ) => (
96 | createBlock( `blockgallery/${ name }`, {
97 | ...GlobalTransforms( attributes ),
98 | } )
99 | ),
100 | },
101 | {
102 | type: 'block',
103 | blocks: [ 'blockgallery/auto-height' ],
104 | transform: ( attributes ) => (
105 | createBlock( `blockgallery/${ name }`, {
106 | ...GlobalTransforms( attributes ),
107 | } )
108 | ),
109 | },
110 | {
111 | type: 'block',
112 | blocks: [ 'core/gallery' ],
113 | transform: ( attributes ) => (
114 | createBlock( `blockgallery/${ name }`, {
115 | ...GlobalTransforms( attributes ),
116 | } )
117 | ),
118 | },
119 | {
120 | type: 'block',
121 | isMultiBlock: true,
122 | blocks: [ 'core/image' ],
123 | transform: ( attributes ) => {
124 | const validImages = filter( attributes, ( { id, url } ) => id && url );
125 | if ( validImages.length > 0 ) {
126 | return createBlock( `blockgallery/${ name }`, {
127 | images: validImages.map( ( { id, url, alt, caption } ) => ( { id, url, alt, caption } ) ),
128 | ids: validImages.map( ( { id } ) => id ),
129 | } );
130 | }
131 | return createBlock( `blockgallery/${ name }` );
132 | },
133 | },
134 | {
135 | type: 'prefix',
136 | prefix: ':masonry',
137 | transform: function( content ) {
138 | return createBlock( `blockgallery/${ name }`, {
139 | content,
140 | } );
141 | },
142 | },
143 | ],
144 | to: [
145 | {
146 | type: 'block',
147 | blocks: [ 'core/gallery' ],
148 | transform: ( attributes ) => (
149 | createBlock( 'core/gallery', {
150 | ...GlobalTransforms( attributes ),
151 | } )
152 | ),
153 | },
154 | ],
155 | },
156 |
157 | edit: Edit,
158 |
159 | save( { attributes, className } ) {
160 |
161 | const {
162 | gridSize,
163 | gutter,
164 | gutterMobile,
165 | images,
166 | linkTo,
167 | captions,
168 | } = attributes;
169 |
170 | const wrapperClasses = classnames(
171 | ...GlobalClasses( attributes ), {
172 | [ `has-gutter` ] : gutter > 0,
173 | }
174 | );
175 |
176 | const wrapperStyles = {
177 | ...BackgroundStyles( attributes ),
178 | };
179 |
180 | const masonryClasses = classnames(
181 | `has-grid-${ gridSize }`, {
182 | [ `has-gutter-${ gutter }` ] : gutter > 0,
183 | [ `has-gutter-mobile-${ gutterMobile }` ] : gutterMobile > 0,
184 | }
185 | );
186 |
187 | const masonryStyles = {
188 | ...GlobalStyles( attributes ),
189 | };
190 |
191 | return (
192 |
193 |
197 |
201 | { images.map( ( image ) => {
202 | let href;
203 |
204 | switch ( linkTo ) {
205 | case 'media':
206 | href = image.url;
207 | break;
208 | case 'attachment':
209 | href = image.link;
210 | break;
211 | }
212 |
213 | const img = ;
214 |
215 | return (
216 |
217 |
218 | { href ? { img } : img }
219 | { captions && image.caption && image.caption.length > 0 && (
220 |
221 | ) }
222 |
223 |
224 | );
225 | } ) }
226 |
227 |
228 |
229 | );
230 | },
231 | };
232 |
233 | export { name, title, icon, settings };
234 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "babel-eslint",
4 | "extends": [
5 | "wordpress",
6 | "plugin:react/recommended",
7 | "plugin:jsx-a11y/recommended",
8 | "plugin:jest/recommended"
9 | ],
10 | "env": {
11 | "browser": false,
12 | "es6": true,
13 | "node": true,
14 | "mocha": true,
15 | "jest/globals": true
16 | },
17 | "parserOptions": {
18 | "sourceType": "module",
19 | "ecmaFeatures": {
20 | "jsx": true
21 | }
22 | },
23 | "globals": {
24 | "wp": true,
25 | "wpApiSettings": true,
26 | "window": true,
27 | "document": true
28 | },
29 | "plugins": ["react", "jsx-a11y", "jest"],
30 | "settings": {
31 | "react": {
32 | "pragma": "wp"
33 | }
34 | },
35 | "rules": {
36 | "array-bracket-spacing": ["error", "always"],
37 | "brace-style": ["error", "1tbs"],
38 | "camelcase": ["error", { "properties": "never" }],
39 | "comma-dangle": ["error", "always-multiline"],
40 | "comma-spacing": "error",
41 | "comma-style": "error",
42 | "computed-property-spacing": ["error", "always"],
43 | "constructor-super": "error",
44 | "dot-notation": "error",
45 | "eol-last": "error",
46 | "eqeqeq": "error",
47 | "func-call-spacing": "error",
48 | "indent": ["error", "tab", { "SwitchCase": 1 }],
49 | "jsx-a11y/label-has-for": [
50 | "error",
51 | {
52 | "required": "id"
53 | }
54 | ],
55 | "jsx-a11y/media-has-caption": "off",
56 | "jsx-a11y/no-noninteractive-tabindex": "off",
57 | "jsx-a11y/role-has-required-aria-props": "off",
58 | "jsx-quotes": "error",
59 | "key-spacing": "error",
60 | "keyword-spacing": "error",
61 | "lines-around-comment": "off",
62 | "no-alert": "error",
63 | "no-bitwise": "error",
64 | "no-caller": "error",
65 | "no-console": "error",
66 | "no-const-assign": "error",
67 | "no-debugger": "error",
68 | "no-dupe-args": "error",
69 | "no-dupe-class-members": "error",
70 | "no-dupe-keys": "error",
71 | "no-duplicate-case": "error",
72 | "no-duplicate-imports": "error",
73 | "no-else-return": "error",
74 | "no-eval": "error",
75 | "no-extra-semi": "error",
76 | "no-fallthrough": "error",
77 | "no-lonely-if": "error",
78 | "no-mixed-operators": "error",
79 | "no-mixed-spaces-and-tabs": "error",
80 | "no-multiple-empty-lines": ["error", { "max": 1 }],
81 | "no-multi-spaces": "error",
82 | "no-multi-str": "off",
83 | "no-negated-in-lhs": "error",
84 | "no-nested-ternary": "error",
85 | "no-redeclare": "error",
86 | "no-restricted-syntax": [
87 | "error",
88 | {
89 | "selector":
90 | "ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]",
91 | "message": "Path access on WordPress dependencies is not allowed."
92 | },
93 | {
94 | "selector": "ImportDeclaration[source.value=/^blocks$/]",
95 | "message": "Use @wordpress/blocks as import path instead."
96 | },
97 | {
98 | "selector": "ImportDeclaration[source.value=/^components$/]",
99 | "message": "Use @wordpress/components as import path instead."
100 | },
101 | {
102 | "selector": "ImportDeclaration[source.value=/^date$/]",
103 | "message": "Use @wordpress/date as import path instead."
104 | },
105 | {
106 | "selector": "ImportDeclaration[source.value=/^editor$/]",
107 | "message": "Use @wordpress/editor as import path instead."
108 | },
109 | {
110 | "selector": "ImportDeclaration[source.value=/^element$/]",
111 | "message": "Use @wordpress/element as import path instead."
112 | },
113 | {
114 | "selector": "ImportDeclaration[source.value=/^i18n$/]",
115 | "message": "Use @wordpress/i18n as import path instead."
116 | },
117 | {
118 | "selector": "ImportDeclaration[source.value=/^data$/]",
119 | "message": "Use @wordpress/data as import path instead."
120 | },
121 | {
122 | "selector": "ImportDeclaration[source.value=/^utils$/]",
123 | "message": "Use @wordpress/utils as import path instead."
124 | },
125 | {
126 | "selector":
127 | "CallExpression[callee.name=/^__|_n|_x$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])",
128 | "message": "Translate function arguments must be string literals."
129 | },
130 | {
131 | "selector":
132 | "CallExpression[callee.name=/^_n|_x$/]:not([arguments.1.type=/^Literal|BinaryExpression$/])",
133 | "message": "Translate function arguments must be string literals."
134 | },
135 | {
136 | "selector":
137 | "CallExpression[callee.name=_nx]:not([arguments.2.type=/^Literal|BinaryExpression$/])",
138 | "message": "Translate function arguments must be string literals."
139 | }
140 | ],
141 | "no-shadow": "error",
142 | "no-undef": "error",
143 | "no-undef-init": "error",
144 | "no-unreachable": "error",
145 | "no-unsafe-negation": "error",
146 | "no-unused-expressions": "error",
147 | "no-unused-vars": "error",
148 | "no-useless-computed-key": "error",
149 | "no-useless-constructor": "error",
150 | "no-useless-return": "error",
151 | "no-var": "error",
152 | "no-whitespace-before-property": "error",
153 | "object-curly-spacing": ["error", "always"],
154 | "padded-blocks": ["error", "never"],
155 | "prefer-const": "error",
156 | "quote-props": ["error", "as-needed"],
157 | "react/display-name": "off",
158 | "react/jsx-curly-spacing": [
159 | "error",
160 | {
161 | "when": "always",
162 | "children": true
163 | }
164 | ],
165 | "react/jsx-equals-spacing": "error",
166 | "react/jsx-indent": ["error", "tab"],
167 | "react/jsx-indent-props": ["error", "tab"],
168 | "react/jsx-key": "error",
169 | "react/jsx-tag-spacing": "error",
170 | "react/no-children-prop": "off",
171 | "react/no-find-dom-node": "warn",
172 | "react/prop-types": "off",
173 | "semi": "error",
174 | "semi-spacing": "error",
175 | "space-before-blocks": ["error", "always"],
176 | "space-before-function-paren": ["error", "never"],
177 | "space-in-parens": ["error", "always"],
178 | "space-infix-ops": ["error", { "int32Hint": false }],
179 | "space-unary-ops": [
180 | "error",
181 | {
182 | "overrides": {
183 | "!": true
184 | }
185 | }
186 | ],
187 | "template-curly-spacing": ["error", "always"],
188 | "valid-jsdoc": ["error", { "requireReturn": false }],
189 | "valid-typeof": "error",
190 | "yoda": "off"
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/blocks/stacked/components/edit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import filter from 'lodash/filter';
6 |
7 | /**
8 | * Internal dependencies
9 | */
10 | import GalleryImage from '../../../components/gallery-image';
11 | import GalleryPlaceholder from '../../../components/gallery-placeholder';
12 | import GalleryDropZone from '../../../components/gallery-dropzone';
13 | import GalleryUpload from '../../../components/gallery-upload';
14 | import Inspector from './inspector';
15 | import { BackgroundStyles } from '../../../components/background/';
16 | import { title, icon } from '../'
17 | import { GlobalClasses, GlobalToolbar } from '../../../components/global/';
18 |
19 | /**
20 | * WordPress dependencies
21 | */
22 | const { __, sprintf } = wp.i18n;
23 | const { Component, Fragment } = wp.element;
24 | const { compose } = wp.compose;
25 | const { withSelect } = wp.data;
26 | const { withNotices } = wp.components;
27 | const { withColors, withFontSizes } = wp.editor;
28 |
29 | /**
30 | * Block edit function
31 | */
32 | class Edit extends Component {
33 | constructor() {
34 | super( ...arguments );
35 |
36 | this.onSelectImage = this.onSelectImage.bind( this );
37 | this.onRemoveImage = this.onRemoveImage.bind( this );
38 | this.setImageAttributes = this.setImageAttributes.bind( this );
39 |
40 | this.state = {
41 | selectedImage: null,
42 | };
43 | }
44 |
45 | componentDidMount() {
46 | // This block does not support caption style.
47 | this.props.setAttributes( {
48 | captionStyle: undefined,
49 | } );
50 | }
51 |
52 | componentDidUpdate( prevProps ) {
53 | // Deselect images when deselecting the block
54 | if ( ! this.props.isSelected && prevProps.isSelected ) {
55 | this.setState( {
56 | selectedImage: null,
57 | captionSelected: false,
58 | } );
59 | }
60 | }
61 |
62 | onSelectImage( index ) {
63 | return () => {
64 | if ( this.state.selectedImage !== index ) {
65 | this.setState( {
66 | selectedImage: index,
67 | } );
68 | }
69 | };
70 | }
71 |
72 | onRemoveImage( index ) {
73 | return () => {
74 | const images = filter( this.props.attributes.images, ( img, i ) => index !== i );
75 | this.setState( { selectedImage: null } );
76 | this.props.setAttributes( {
77 | images,
78 | } );
79 | };
80 | }
81 |
82 | setImageAttributes( index, attributes ) {
83 | const { attributes: { images }, setAttributes } = this.props;
84 | if ( ! images[ index ] ) {
85 | return;
86 | }
87 | setAttributes( {
88 | images: [
89 | ...images.slice( 0, index ),
90 | {
91 | ...images[ index ],
92 | ...attributes,
93 | },
94 | ...images.slice( index + 1 ),
95 | ],
96 | } );
97 | }
98 |
99 | render() {
100 | const {
101 | attributes,
102 | backgroundColor,
103 | captionColor,
104 | className,
105 | isSelected,
106 | noticeOperations,
107 | noticeUI,
108 | setAttributes,
109 | fontSize,
110 | } = this.props;
111 |
112 | const {
113 | align,
114 | fullwidth,
115 | gutter,
116 | gutterMobile,
117 | images,
118 | linkTo,
119 | shadow,
120 | captions,
121 | } = attributes;
122 |
123 | const dropZone = (
124 |
128 | );
129 |
130 | const wrapperClasses = classnames(
131 | ...GlobalClasses( attributes ), {
132 | 'has-fullwidth-images': fullwidth,
133 | [ `align${ align }` ] : align,
134 | [ `has-margin` ] : gutter > 0,
135 | [ `has-margin-bottom-${ gutter }` ] : gutter > 0,
136 | [ `has-margin-bottom-mobile-${ gutterMobile }` ] : gutterMobile > 0,
137 | }
138 | );
139 |
140 | const wrapperStyles = {
141 | ...BackgroundStyles( attributes ),
142 | backgroundColor: backgroundColor.color,
143 | color: captionColor.color,
144 | };
145 |
146 | if ( images.length === 0 ) {
147 | return (
148 |
153 | );
154 | }
155 |
156 | return (
157 |
158 |
161 |
164 | { noticeUI }
165 |
166 |
167 | { dropZone }
168 | { images.map( ( img, index ) => {
169 | // translators: %1$d is the order number of the image, %2$d is the total number of images.
170 | const ariaLabel = __( sprintf( 'image %1$d of %2$d in gallery', ( index + 1 ), images.length ) );
171 |
172 | return (
173 |
174 | this.setImageAttributes( index, attrs ) }
186 | caption={ img.caption }
187 | aria-label={ ariaLabel }
188 | captions={ captions }
189 | supportsCaption={ true }
190 | fontSize={ fontSize.size }
191 | />
192 |
193 | );
194 | } ) }
195 | { isSelected && (
196 |
201 | ) }
202 |
203 |
204 |
205 | );
206 | }
207 | }
208 |
209 | export default compose( [
210 | withSelect( ( select ) => ( {
211 | editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(),
212 | pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(),
213 | publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(),
214 | wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide,
215 | } ) ),
216 | withColors( { backgroundColor : 'background-color', captionColor : 'color' } ),
217 | withFontSizes( 'fontSize' ),
218 | withNotices,
219 | ] )( Edit );
220 |
--------------------------------------------------------------------------------
/src/components/gallery-image.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External Dependencies
3 | */
4 | import classnames from 'classnames';
5 |
6 | /**
7 | * WordPress dependencies
8 | */
9 | const { __ } = wp.i18n;
10 | const { Component, Fragment } = wp.element;
11 | const { compose } = wp.compose;
12 | const { IconButton, Spinner } = wp.components;
13 | const { RichText } = wp.editor;
14 | const { withSelect } = wp.data;
15 | const { BACKSPACE, DELETE } = wp.keycodes;
16 | const { isBlobURL } = wp.blob;
17 |
18 | /**
19 | * Gallery Image Component
20 | */
21 | class GalleryImage extends Component {
22 | constructor() {
23 | super( ...arguments );
24 |
25 | this.onImageClick = this.onImageClick.bind( this );
26 | this.onSelectCaption = this.onSelectCaption.bind( this );
27 | this.onKeyDown = this.onKeyDown.bind( this );
28 | this.bindContainer = this.bindContainer.bind( this );
29 |
30 | this.state = {
31 | captionSelected: false,
32 | captionFocused: false,
33 | };
34 | }
35 |
36 | bindContainer( ref ) {
37 | this.container = ref;
38 | }
39 |
40 | onSelectCaption() {
41 | if ( ! this.state.captionSelected ) {
42 | this.setState( {
43 | captionSelected: true,
44 | } );
45 | }
46 |
47 | if ( ! this.props.isSelected ) {
48 | this.props.onSelect();
49 | }
50 | }
51 |
52 | onImageClick() {
53 | if ( ! this.props.isSelected ) {
54 | this.props.onSelect();
55 | }
56 |
57 | if ( this.state.captionSelected ) {
58 | this.setState( {
59 | captionSelected: false,
60 | captionFocused: false,
61 | } );
62 | }
63 | }
64 |
65 | onKeyDown( event ) {
66 | if (
67 | this.container === document.activeElement &&
68 | this.props.isSelected && [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1
69 | ) {
70 | event.stopPropagation();
71 | event.preventDefault();
72 | this.props.onRemove();
73 | }
74 | }
75 |
76 | componentDidUpdate( prevProps ) {
77 | const { isSelected, image, url } = this.props;
78 | if ( image && ! url ) {
79 | this.props.setAttributes( {
80 | url: image.source_url,
81 | alt: image.alt_text,
82 | } );
83 | }
84 |
85 | // unselect the caption so when the user selects other image and comeback
86 | // the caption is not immediately selected
87 | if ( this.state.captionSelected && ! isSelected && prevProps.isSelected ) {
88 | this.setState( {
89 | captionSelected: false,
90 | captionFocused: false,
91 | } );
92 | }
93 | }
94 |
95 | render() {
96 |
97 | const {
98 | alt,
99 | caption,
100 | fontSize,
101 | gutter,
102 | gutterMobile,
103 | id,
104 | isSelected,
105 | link,
106 | linkTo,
107 | marginBottom,
108 | marginLeft,
109 | marginRight,
110 | marginTop,
111 | onRemove,
112 | setAttributes,
113 | shadow,
114 | supportsCaption,
115 | url,
116 | captions,
117 | 'aria-label': ariaLabel,
118 | } = this.props;
119 |
120 | let href;
121 |
122 | switch ( linkTo ) {
123 | case 'media':
124 | href = url;
125 | break;
126 | case 'attachment':
127 | href = link;
128 | break;
129 | }
130 |
131 | const imgClasses = classnames( {
132 | [ `has-shadow-${ shadow }` ] : shadow != 'none' || shadow != undefined,
133 | } );
134 |
135 | // Disable reason: Image itself is not meant to be
136 | // interactive, but should direct image selection and unfocus caption fields
137 | // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events
138 | const img = (
139 | // Disable reason: Image itself is not meant to be interactive, but should
140 | // direct image selection and unfocus caption fields.
141 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
142 |
143 |
153 | { isBlobURL( url ) && }
154 |
155 | /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
156 | );
157 |
158 | const className = classnames( {
159 | 'is-selected': isSelected,
160 | 'is-transient': url && 0 === url.indexOf( 'blob:' ),
161 | [ `has-margin-top-${ gutter }` ] : marginTop && gutter > 0,
162 | [ `has-margin-top-mobile-${ gutterMobile }` ] : marginTop && gutterMobile > 0,
163 | [ `has-margin-right-${ gutter }` ] : marginRight && gutter > 0,
164 | [ `has-margin-right-mobile-${ gutterMobile }` ] : marginRight && gutterMobile > 0,
165 | [ `has-margin-bottom-${ gutter }` ] : marginBottom && gutter > 0,
166 | [ `has-margin-bottom-mobile-${ gutterMobile }` ] : marginBottom && gutterMobile > 0,
167 | [ `has-margin-left-${ gutter }` ] : marginLeft && gutter > 0,
168 | [ `has-margin-left-mobile-${ gutterMobile }` ] : marginLeft && gutterMobile > 0,
169 | } );
170 |
171 | const captionStyles = {
172 | fontSize: fontSize ? fontSize + 'px' : undefined,
173 | };
174 |
175 | // Disable reason: Each block can be selected by clicking on it and we should keep the same saved markup
176 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
177 | return (
178 |
179 | { isSelected &&
180 |
181 |
187 |
188 | }
189 | { href ? { img } : img }
190 | { ( supportsCaption === true ) && ( ! RichText.isEmpty( caption ) || isSelected ) && captions ? (
191 | setAttributes( { caption: newCaption } ) }
199 | unstableOnFocus={ this.onSelectCaption }
200 | inlineToolbar
201 | />
202 | ) : null }
203 |
204 | );
205 | /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
206 | }
207 | }
208 |
209 | export default compose( [
210 | withSelect( ( select, ownProps ) => {
211 | const { getMedia } = select( 'core' );
212 | const { id } = ownProps;
213 |
214 | return {
215 | image: id ? getMedia( id ) : null,
216 | };
217 | } ),
218 | ] )( GalleryImage );
219 |
--------------------------------------------------------------------------------
/src/blocks/stacked/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import filter from 'lodash/filter';
6 |
7 | /**
8 | * Internal dependencies
9 | */
10 | import './styles/style.scss';
11 | import './styles/editor.scss';
12 | import Edit from './components/edit';
13 | import icons from './../../utils/icons';
14 | import { BackgroundStyles } from '../../components/background/';
15 | import { GlobalAttributes, GlobalTransforms, GlobalClasses, GlobalStyles } from '../../components/global/';
16 |
17 | /**
18 | * WordPress dependencies
19 | */
20 | const { __ } = wp.i18n;
21 | const { createBlock } = wp.blocks;
22 | const { RichText, getFontSizeClass } = wp.editor;
23 |
24 | /**
25 | * Block constants
26 | */
27 | const name = 'stacked';
28 |
29 | const title = __( 'Stacked' );
30 |
31 | const icon = icons.stacked;
32 |
33 | const keywords = [
34 | __( 'gallery' ),
35 | __( 'images' ),
36 | __( 'photos' ),
37 | ];
38 |
39 | const blockAttributes = {
40 | ...GlobalAttributes,
41 |
42 | // Block specific attributes and overrides.
43 | align: {
44 | type: 'string',
45 | default: 'full',
46 | },
47 | captionStyle: {
48 | type: 'string',
49 | },
50 | fullwidth: {
51 | type: 'boolean',
52 | default: true,
53 | },
54 | gutter: {
55 | type: 'number',
56 | default: 0,
57 | },
58 | gutterMobile: {
59 | type: 'number',
60 | default: 0,
61 | },
62 | };
63 |
64 | const settings = {
65 |
66 | title: title,
67 |
68 | description: __( 'Display multiple images in an single column stacked gallery.' ),
69 |
70 | keywords: keywords,
71 |
72 | attributes: blockAttributes,
73 |
74 | supports: {
75 | align: [ 'wide', 'full' ],
76 | },
77 |
78 | transforms: {
79 | from: [
80 | {
81 | type: 'block',
82 | blocks: [ 'blockgallery/masonry' ],
83 | transform: ( attributes ) => (
84 | createBlock( `blockgallery/${ name }`, {
85 | ...GlobalTransforms( attributes ),
86 | } )
87 | ),
88 | },
89 | {
90 | type: 'block',
91 | blocks: [ 'blockgallery/carousel' ],
92 | transform: ( attributes ) => (
93 | createBlock( `blockgallery/${ name }`, {
94 | ...GlobalTransforms( attributes ),
95 | } )
96 | ),
97 | },
98 | {
99 | type: 'block',
100 | blocks: [ 'blockgallery/thumbnails' ],
101 | transform: ( attributes ) => (
102 | createBlock( `blockgallery/${ name }`, {
103 | ...GlobalTransforms( attributes ),
104 | } )
105 | ),
106 | },
107 | {
108 | type: 'block',
109 | blocks: [ 'blockgallery/offset' ],
110 | transform: ( attributes ) => (
111 | createBlock( `blockgallery/${ name }`, {
112 | ...GlobalTransforms( attributes ),
113 | } )
114 | ),
115 | },
116 | {
117 | type: 'block',
118 | blocks: [ 'blockgallery/auto-height' ],
119 | transform: ( attributes ) => (
120 | createBlock( `blockgallery/${ name }`, {
121 | ...GlobalTransforms( attributes ),
122 | } )
123 | ),
124 | },
125 | {
126 | type: 'block',
127 | blocks: [ 'core/gallery' ],
128 | transform: ( attributes ) => (
129 | createBlock( `blockgallery/${ name }`, {
130 | ...GlobalTransforms( attributes ),
131 | } )
132 | ),
133 | },
134 | {
135 | type: 'block',
136 | isMultiBlock: true,
137 | blocks: [ 'core/image' ],
138 | transform: ( attributes ) => {
139 | const validImages = filter( attributes, ( { id, url } ) => id && url );
140 | if ( validImages.length > 0 ) {
141 | return createBlock( `blockgallery/${ name }`, {
142 | images: validImages.map( ( { id, url, alt, caption } ) => ( { id, url, alt, caption } ) ),
143 | ids: validImages.map( ( { id } ) => id ),
144 | } );
145 | }
146 | return createBlock( `blockgallery/${ name }` );
147 | },
148 | },
149 | {
150 | type: 'prefix',
151 | prefix: ':stacked',
152 | transform: function( content ) {
153 | return createBlock( `blockgallery/${ name }`, {
154 | content,
155 | } );
156 | },
157 | },
158 | ],
159 | to: [
160 | {
161 | type: 'block',
162 | blocks: [ 'core/gallery' ],
163 | transform: ( attributes ) => (
164 | createBlock( 'core/gallery', {
165 | ...GlobalTransforms( attributes ),
166 | } )
167 | ),
168 | },
169 | ],
170 | },
171 |
172 | edit: Edit,
173 |
174 | save( { attributes, className } ) {
175 |
176 | const {
177 | captionColor,
178 | captions,
179 | customCaptionColor,
180 | customFontSize,
181 | fontSize,
182 | fullwidth,
183 | gutter,
184 | gutterMobile,
185 | images,
186 | linkTo,
187 | shadow,
188 | } = attributes;
189 |
190 | const wrapperClasses = classnames(
191 | ...GlobalClasses( attributes ), {
192 | 'has-fullwidth-images': fullwidth,
193 | [ `has-margin` ] : gutter > 0,
194 | }
195 | );
196 |
197 | const wrapperStyles = {
198 | ...GlobalStyles( attributes ),
199 | ...BackgroundStyles( attributes ),
200 | };
201 |
202 | const fontSizeClass = getFontSizeClass( fontSize );
203 |
204 | const figureClasses = classnames(
205 | 'blockgallery--figure', {
206 | [ `has-margin-bottom-${ gutter }` ] : gutter > 0,
207 | [ `has-margin-bottom-mobile-${ gutterMobile }` ] : gutterMobile > 0,
208 | [ fontSizeClass ]: fontSizeClass,
209 | } );
210 |
211 | const captionClasses = classnames(
212 | 'blockgallery--caption', {
213 | [ fontSizeClass ]: fontSizeClass,
214 | } );
215 |
216 | const captionStyles = {
217 | fontSize: fontSizeClass ? undefined : customFontSize,
218 | };
219 |
220 | return (
221 |
222 |
223 | { images.map( ( image ) => {
224 | let href;
225 |
226 | switch ( linkTo ) {
227 | case 'media':
228 | href = image.url;
229 | break;
230 | case 'attachment':
231 | href = image.link;
232 | break;
233 | }
234 |
235 | const imgClasses = classnames(
236 | image.id ? [ `wp-image-${ image.id }` ] : null, {
237 | [ `has-shadow-${ shadow }` ] : shadow != 'none' || shadow != undefined ,
238 | } );
239 |
240 | const img = ;
241 |
242 | return (
243 |
244 |
245 | { href ? { img } : img }
246 | { captions && image.caption && image.caption.length > 0 && (
247 |
248 | ) }
249 |
250 |
251 | );
252 | } ) }
253 |
254 |
255 | );
256 | },
257 | };
258 |
259 | export { name, title, icon, settings };
260 |
--------------------------------------------------------------------------------
/src/blocks/masonry/components/edit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import filter from 'lodash/filter';
6 | import Masonry from 'react-masonry-component';
7 |
8 | /**
9 | * Internal dependencies
10 | */
11 | import GalleryImage from '../../../components/gallery-image';
12 | import GalleryPlaceholder from '../../../components/gallery-placeholder';
13 | import GalleryDropZone from '../../../components/gallery-dropzone';
14 | import GalleryUpload from '../../../components/gallery-upload';
15 | import Inspector from './inspector';
16 | import { BackgroundStyles } from '../../../components/background/';
17 | import { title, icon } from '../'
18 | import { GlobalClasses, GlobalToolbar, GlobalStyles } from '../../../components/global/';
19 |
20 | /**
21 | * WordPress dependencies
22 | */
23 | const { __, sprintf } = wp.i18n;
24 | const { Component, Fragment } = wp.element;
25 | const { compose } = wp.compose;
26 | const { withSelect } = wp.data;
27 | const { withNotices } = wp.components;
28 | const { withColors } = wp.editor;
29 |
30 | /**
31 | * Block consts
32 | */
33 | const masonryOptions = {
34 | transitionDuration: 0,
35 | percentPosition: true,
36 | };
37 |
38 | /**
39 | * Block edit function
40 | */
41 | class Edit extends Component {
42 | constructor() {
43 | super( ...arguments );
44 |
45 | this.onSelectImage = this.onSelectImage.bind( this );
46 | this.onRemoveImage = this.onRemoveImage.bind( this );
47 | this.setImageAttributes = this.setImageAttributes.bind( this );
48 |
49 | this.state = {
50 | selectedImage: null,
51 | };
52 | }
53 |
54 | componentDidMount() {
55 |
56 | if ( this.props.wideControlsEnabled == true && ! this.props.attributes.align && this.props.attributes.gridSize == 'xlrg' ) {
57 | this.props.setAttributes( {
58 | align: 'wide',
59 | gridSize: 'lrg'
60 | } );
61 | }
62 | }
63 |
64 | componentDidUpdate( prevProps ) {
65 | // Deselect images when deselecting the block.
66 | if ( ! this.props.isSelected && prevProps.isSelected ) {
67 | this.setState( {
68 | selectedImage: null,
69 | captionSelected: false,
70 | } );
71 | }
72 | }
73 |
74 | onSelectImage( index ) {
75 | return () => {
76 | if ( this.state.selectedImage !== index ) {
77 | this.setState( {
78 | selectedImage: index,
79 | } );
80 | }
81 | };
82 | }
83 |
84 | onRemoveImage( index ) {
85 | return () => {
86 | const images = filter( this.props.attributes.images, ( img, i ) => index !== i );
87 | const { gridSize } = this.props.attributes;
88 | this.setState( { selectedImage: null } );
89 | this.props.setAttributes( {
90 | images,
91 | } );
92 | };
93 | }
94 |
95 | setImageAttributes( index, attributes ) {
96 | const { attributes: { images }, setAttributes } = this.props;
97 | if ( ! images[ index ] ) {
98 | return;
99 | }
100 | setAttributes( {
101 | images: [
102 | ...images.slice( 0, index ),
103 | {
104 | ...images[ index ],
105 | ...attributes,
106 | },
107 | ...images.slice( index + 1 ),
108 | ],
109 | } );
110 | }
111 |
112 | render() {
113 | const {
114 | attributes,
115 | backgroundColor,
116 | className,
117 | editorSidebarOpened,
118 | isSelected,
119 | noticeOperations,
120 | noticeUI,
121 | pluginSidebarOpened,
122 | publishSidebarOpened,
123 | setAttributes,
124 | captionColor,
125 | } = this.props;
126 |
127 | const {
128 | align,
129 | customBackgroundColor,
130 | gridSize,
131 | gutter,
132 | gutterMobile,
133 | images,
134 | linkTo,
135 | captions,
136 | } = attributes;
137 |
138 | const dropZone = (
139 |
144 | );
145 |
146 | const sidebarIsOpened = editorSidebarOpened || pluginSidebarOpened || publishSidebarOpened;
147 |
148 | const wrapperClasses = classnames(
149 | ...GlobalClasses( attributes ),
150 | sidebarIsOpened, {
151 | [ `align${ align }` ] : align,
152 | [ `has-gutter` ] : gutter > 0,
153 | }
154 | );
155 |
156 | const wrapperStyles = {
157 | ...BackgroundStyles( attributes ),
158 | backgroundColor: backgroundColor.color,
159 | };
160 |
161 | const masonryClasses = classnames(
162 | `has-grid-${ gridSize }`, {
163 | [ `has-gutter-${ gutter }` ] : gutter > 0,
164 | [ `has-gutter-mobile-${ gutterMobile }` ] : gutterMobile > 0,
165 | }
166 | );
167 |
168 | const masonryStyles = {
169 | color: captionColor.color,
170 | };
171 |
172 | if ( images.length === 0 ) {
173 | return (
174 |
180 | );
181 | }
182 |
183 | return (
184 |
185 |
188 |
191 | { noticeUI }
192 |
193 |
197 | { dropZone }
198 |
206 | { images.map( ( img, index ) => {
207 | // translators: %1$d is the order number of the image, %2$d is the total number of images
208 | const ariaLabel = __( sprintf( 'image %1$d of %2$d in gallery', ( index + 1 ), images.length ) );
209 |
210 | return (
211 |
212 | this.setImageAttributes( index, attrs ) }
220 | caption={ img.caption }
221 | aria-label={ ariaLabel }
222 | captions={ captions }
223 | supportsCaption={ true }
224 | />
225 |
226 | );
227 | } ) }
228 | { isSelected && (
229 |
232 | ) }
233 |
234 |
235 |
236 |
237 | );
238 | }
239 | }
240 |
241 | export default compose( [
242 | withSelect( ( select ) => ( {
243 | editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(),
244 | pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(),
245 | publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(),
246 | wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide,
247 | } ) ),
248 | withColors( { backgroundColor : 'background-color', captionColor : 'color' } ),
249 | withNotices,
250 | ] )( Edit );
--------------------------------------------------------------------------------
/src/components/background/panel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import isEmpty from 'lodash/isEmpty';
5 |
6 | /**
7 | * Internal dependencies
8 | */
9 | import ResponsiveTabsControl from '../../components/responsive-tabs-control';
10 |
11 | /**
12 | * WordPress dependencies
13 | */
14 | const { __ } = wp.i18n;
15 | const { Component, Fragment } = wp.element;
16 | const { compose } = wp.compose;
17 | const { withColors, ColorPalette, PanelColorSettings } = wp.editor;
18 | const { SelectControl, RangeControl, ToggleControl, PanelBody, Button } = wp.components;
19 |
20 | /**
21 | * Gutter Controls Component
22 | */
23 | class BackgroundPanel extends Component {
24 |
25 | constructor() {
26 | super( ...arguments );
27 | this.setBackgroundPaddingTo = this.setBackgroundPaddingTo.bind( this );
28 | this.setBackgroundPaddingMobileTo = this.setBackgroundPaddingMobileTo.bind( this );
29 | this.getColors = this.getColors.bind( this );
30 | }
31 |
32 | setBackgroundPaddingTo( value ) {
33 | this.props.setAttributes( { backgroundPadding: value } );
34 |
35 | if ( this.props.attributes.backgroundPadding <= 0 ) {
36 | this.props.setAttributes( {
37 | backgroundRadius: 0,
38 | } );
39 | }
40 |
41 | }
42 |
43 | setBackgroundPaddingMobileTo( value ) {
44 | this.props.setAttributes( { backgroundPaddingMobile: value } );
45 | }
46 |
47 | getColors() {
48 |
49 | const {
50 | backgroundColor,
51 | setBackgroundColor,
52 | captionColor,
53 | setCaptionColor,
54 | hasCaption,
55 | attributes,
56 | } = this.props;
57 |
58 | const {
59 | backgroundImg,
60 | backgroundPadding,
61 | backgroundPaddingMobile,
62 | } = attributes;
63 |
64 | const background = [
65 | {
66 | value: backgroundColor.color,
67 | onChange: ( nextBackgroundColor ) => {
68 |
69 | setBackgroundColor( nextBackgroundColor );
70 |
71 | // Add default padding, if they are not yet present.
72 | if ( ! backgroundPadding && ! backgroundPaddingMobile ) {
73 | this.props.setAttributes( {
74 | backgroundPadding: 30,
75 | backgroundPaddingMobile: 30,
76 | } );
77 | }
78 |
79 | // Reset when cleared.
80 | if ( ! nextBackgroundColor && ! backgroundImg ) {
81 | this.props.setAttributes( {
82 | backgroundPadding: 0,
83 | backgroundPaddingMobile: 0,
84 | } );
85 | }
86 | },
87 | label: __( 'Background Color' ),
88 | },
89 | ];
90 |
91 | const caption = [
92 | {
93 | value: captionColor.color,
94 | onChange: setCaptionColor,
95 | label: __( 'Caption Text Color' ),
96 | },
97 | ];
98 |
99 | if ( hasCaption ) {
100 | return background.concat( caption );
101 | } else {
102 | return background;
103 | }
104 | }
105 |
106 | render() {
107 |
108 | const {
109 | attributes,
110 | setAttributes,
111 | backgroundColor,
112 | setBackgroundColor,
113 | captionColor,
114 | setCaptionColor,
115 | } = this.props;
116 |
117 | const {
118 | align,
119 | backgroundPosition,
120 | backgroundRepeat,
121 | backgroundSize,
122 | backgroundOverlay,
123 | backgroundPadding,
124 | backgroundPaddingMobile,
125 | hasParallax,
126 | backgroundImg,
127 | backgroundRadius,
128 | } = attributes;
129 |
130 | const backgroundPositionOptions = [
131 | { value: 'top left', label: __( 'Top Left' ) },
132 | { value: 'top center', label: __( 'Top Center' ) },
133 | { value: 'top right', label: __( 'Top Right' ) },
134 | { value: 'center left', label: __( 'Center Left' ) },
135 | { value: 'center center', label: __( 'Center Center' ) },
136 | { value: 'center right', label: __( 'Center Right' ) },
137 | { value: 'bottom left', label: __( 'Bottom Left' ) },
138 | { value: 'bottom center', label: __( 'Bottom Center' ) },
139 | { value: 'bottom right', label: __( 'Bottom Right' ) },
140 | ];
141 |
142 | const backgroundRepeatOptions = [
143 | { value: 'no-repeat', label: __( 'No Repeat' ) },
144 | { value: 'repeat', label: __( 'Repeat' ) },
145 | { value: 'repeat-x', label: __( 'Repeat Horizontally' ) },
146 | { value: 'repeat-y', label: __( 'Repeat Vertically' ) },
147 | ];
148 |
149 | const backgroundSizeOptions = [
150 | { value: 'auto', label: __( 'Auto' ) },
151 | { value: 'cover', label: __( 'Cover' ) },
152 | { value: 'contain', label: __( 'Contain' ) },
153 | ];
154 |
155 | const backgroundSizeDefault = ( typeof options !== 'undefined' && typeof options.backgroundSize !== 'undefined' ) ? options.backgroundSize : 'cover';
156 |
157 | return (
158 |
159 | { ( ! isEmpty( backgroundImg ) || ! isEmpty( backgroundColor.color ) ) && (
160 |
164 |
173 | { ( ( ! isEmpty( backgroundImg ) || ! isEmpty( backgroundColor.color ) ) && backgroundPadding > 0 ) && align != 'full' &&
174 | setAttributes( { backgroundRadius: nextBackgroundRadius } ) }
178 | min={ 0 }
179 | max={ 20 }
180 | step={ 1 }
181 | />
182 | }
183 | { backgroundImg && (
184 |
185 | setAttributes( { backgroundOverlay: nextBackgroundOverlay } ) }
190 | min={ 0 }
191 | max={ 90 }
192 | step={ 10 }
193 | />
194 | setAttributes( { backgroundPosition: nextbackgroundPosition } ) }
200 | />
201 | setAttributes( { backgroundRepeat: nextbackgroundRepeat } ) }
207 | />
208 | setAttributes( { backgroundSize: nextbackgroundSize } ) }
214 | />
215 | setAttributes( { hasParallax: ! hasParallax } ) }
220 | />
221 | setAttributes( { backgroundImg: '', backgroundOverlay: 0, } ) }
225 | >
226 | { __( 'Remove Background Image' ) }
227 |
228 |
229 | ) }
230 |
231 | ) }
232 |
237 |
238 |
239 | );
240 | }
241 | }
242 |
243 | export default compose( [
244 | withColors( { backgroundColor : 'background-color', captionColor : 'color' } ),
245 | ] )( BackgroundPanel );
246 |
--------------------------------------------------------------------------------
/src/styles/editor/core/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Breakpoint mixins
2 |
3 | @mixin break-huge() {
4 | @media (min-width: #{ ($break-huge) }) {
5 | @content;
6 | }
7 | }
8 |
9 | @mixin break-wide() {
10 | @media (min-width: #{ ($break-wide) }) {
11 | @content;
12 | }
13 | }
14 |
15 | @mixin break-large() {
16 | @media (min-width: #{ ($break-large) }) {
17 | @content;
18 | }
19 | }
20 |
21 | @mixin break-medium() {
22 | @media (min-width: #{ ($break-medium) }) {
23 | @content;
24 | }
25 | }
26 |
27 | @mixin break-small() {
28 | @media (min-width: #{ ($break-small) }) {
29 | @content;
30 | }
31 | }
32 |
33 | @mixin break-mobile() {
34 | @media (min-width: #{ ($break-mobile) }) {
35 | @content;
36 | }
37 | }
38 |
39 |
40 | /**
41 | * Long content fade mixin
42 | *
43 | * Creates a fading overlay to signify that the content is longer
44 | * than the space allows.
45 | */
46 |
47 | @mixin long-content-fade($direction: right, $size: 20%, $color: #fff, $edge: 0, $z-index: false) {
48 | content: "";
49 | display: block;
50 | position: absolute;
51 | -webkit-touch-callout: none;
52 | -webkit-user-select: none;
53 | -khtml-user-select: none;
54 | -moz-user-select: none;
55 | -ms-user-select: none;
56 | user-select: none;
57 | pointer-events: none;
58 |
59 | @if $z-index {
60 | z-index: $z-index;
61 | }
62 |
63 | @if $direction == "bottom" {
64 | background: linear-gradient(to top, rgba($color, 0), $color 90%);
65 | left: $edge;
66 | right: $edge;
67 | top: $edge;
68 | bottom: calc(100% - $size);
69 | width: auto;
70 | }
71 |
72 | @if $direction == "top" {
73 | background: linear-gradient(to bottom, rgba($color, 0), $color 90%);
74 | top: calc(100% - $size);
75 | left: $edge;
76 | right: $edge;
77 | bottom: $edge;
78 | width: auto;
79 | }
80 |
81 | @if $direction == "left" {
82 | background: linear-gradient(to left, rgba($color, 0), $color 90%);
83 | top: $edge;
84 | left: $edge;
85 | bottom: $edge;
86 | right: auto;
87 | width: $size;
88 | height: auto;
89 | }
90 |
91 | @if $direction == "right" {
92 | background: linear-gradient(to right, rgba($color, 0), $color 90%);
93 | top: $edge;
94 | bottom: $edge;
95 | right: $edge;
96 | left: auto;
97 | width: $size;
98 | height: auto;
99 | }
100 | }
101 |
102 | /**
103 | * Button states and focus styles
104 | */
105 |
106 | // Buttons with rounded corners.
107 | @mixin button-style__disabled {
108 | opacity: 0.6;
109 | cursor: default;
110 | }
111 |
112 | @mixin button-style__hover {
113 | background-color: $white;
114 | color: $dark-gray-900;
115 | box-shadow: inset 0 0 0 1px $light-gray-500, inset 0 0 0 2px $white, 0 1px 1px rgba($dark-gray-900, 0.2);
116 | }
117 |
118 | @mixin button-style__active() {
119 | outline: none;
120 | background-color: $white;
121 | color: $dark-gray-900;
122 | box-shadow: inset 0 0 0 1px $light-gray-700, inset 0 0 0 2px $white;
123 | }
124 |
125 | @mixin button-style__focus-active() {
126 | background-color: $white;
127 | color: $dark-gray-900;
128 | box-shadow: inset 0 0 0 1px $dark-gray-300, inset 0 0 0 2px $white;
129 |
130 | // Windows High Contrast mode will show this outline, but not the box-shadow.
131 | outline: 2px solid transparent;
132 | outline-offset: -2px;
133 | }
134 |
135 | // Switch.
136 | @mixin switch-style__focus-active() {
137 | box-shadow: 0 0 0 2px $white, 0 0 0 3px $dark-gray-300;
138 |
139 | // Windows High Contrast mode will show this outline, but not the box-shadow.
140 | outline: 2px solid transparent;
141 | outline-offset: 2px;
142 | }
143 |
144 | // Formatting Buttons.
145 | @mixin formatting-button-style__hover {
146 | color: $dark-gray-500;
147 | box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px $white;
148 | }
149 |
150 | @mixin formatting-button-style__active() {
151 | outline: none;
152 | color: $white;
153 | box-shadow: none;
154 | background: $dark-gray-500;
155 | }
156 |
157 | @mixin formatting-button-style__focus() {
158 | box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px $white;
159 |
160 | // Windows High Contrast mode will show this outline, but not the box-shadow.
161 | outline: 2px solid transparent;
162 | outline-offset: -2px;
163 | }
164 |
165 | // Tabs, Inputs, Square buttons.
166 | @mixin input-style__neutral() {
167 | box-shadow: 0 0 0 transparent;
168 | transition: box-shadow 0.1s linear;
169 | border-radius: $radius-round-rectangle;
170 | border: $border-width solid $dark-gray-150;
171 | }
172 |
173 | @mixin input-style__focus() {
174 | color: $dark-gray-900;
175 | border-color: $blue-medium-500;
176 | box-shadow: 0 0 0 1px $blue-medium-500;
177 |
178 | // Windows High Contrast mode will show this outline, but not the box-shadow.
179 | outline: 2px solid transparent;
180 | outline-offset: -2px;
181 | }
182 |
183 | // Square buttons.
184 | @mixin square-style__neutral() {
185 | outline-offset: -1px;
186 | }
187 |
188 | @mixin square-style__focus() {
189 | color: $dark-gray-900;
190 | outline: 1px solid $dark-gray-300;
191 | box-shadow: none;
192 | }
193 |
194 | // Menu items.
195 | @mixin menu-style__neutral() {
196 | border: none;
197 | box-shadow: none;
198 | }
199 |
200 | @mixin menu-style__hover() {
201 | color: $dark-gray-900;
202 | border: none;
203 | box-shadow: none;
204 | }
205 |
206 | @mixin menu-style__focus() {
207 | color: $dark-gray-900;
208 | border: none;
209 | box-shadow: none;
210 | outline-offset: -2px;
211 | outline: 1px dotted $dark-gray-500;
212 | }
213 |
214 | // Blocks in the Library.
215 | @mixin block-style__disabled {
216 | opacity: 0.6;
217 | cursor: default;
218 | }
219 |
220 | @mixin block-style__hover {
221 | background: $light-gray-100;
222 | color: $dark-gray-900;
223 | }
224 |
225 | @mixin block-style__focus-active() {
226 | color: $dark-gray-900;
227 | box-shadow: 0 0 0 2px $blue-medium-500;
228 |
229 | // Windows High Contrast mode will show this outline, but not the box-shadow.
230 | outline: 2px solid transparent;
231 | outline-offset: -2px;
232 | }
233 |
234 | /**
235 | * Applies editor left position to the selector passed as argument
236 | */
237 |
238 | @mixin editor-left($selector) {
239 | #{$selector} { /* Set left position when auto-fold is not on the body element. */
240 | left: 0;
241 |
242 | @include break-medium() {
243 | left: $admin-sidebar-width;
244 | }
245 | }
246 |
247 | .auto-fold #{$selector} { /* Auto fold is when on smaller breakpoints, nav menu auto colllapses. */
248 | @include break-medium() {
249 | left: $admin-sidebar-width-collapsed;
250 | }
251 |
252 | @include break-large() {
253 | left: $admin-sidebar-width;
254 | }
255 | }
256 |
257 | /* Sidebar manually collapsed. */
258 | .folded #{$selector} {
259 | left: 0;
260 |
261 | @include break-medium() {
262 | left: $admin-sidebar-width-collapsed;
263 | }
264 | }
265 |
266 | /* Mobile menu opened. */
267 | @media (max-width: #{ ($break-medium) }) {
268 | .auto-fold .wp-responsive-open #{$selector} {
269 | left: $admin-sidebar-width-big;
270 | }
271 | }
272 |
273 | /* In small screens with resposive menu expanded there is small white space. */
274 | @media (max-width: #{ ($break-small) }) {
275 | .auto-fold .wp-responsive-open #{$selector} {
276 | margin-left: -18px;
277 | }
278 | }
279 |
280 | body.is-fullscreen-mode #{$selector} {
281 | left: 0 !important;
282 | }
283 | }
284 |
285 | /**
286 | * Applies editor right position to the selector passed as argument
287 | */
288 |
289 | @mixin editor-right($selector) {
290 | #{ $selector } {
291 | right: 0;
292 | }
293 |
294 | .edit-post-layout.is-sidebar-opened #{ $selector } {
295 | right: $sidebar-width;
296 | }
297 | }
298 |
299 |
300 | /**
301 | * Styles that are reused verbatim in a few places
302 | */
303 |
304 | @mixin caption-style() {
305 | margin-top: 0.5em;
306 | margin-bottom: 1em;
307 | color: $dark-gray-500;
308 | text-align: center;
309 | font-size: $default-font-size;
310 | }
311 |
312 | @mixin dropdown-arrow() {
313 | content: "";
314 | pointer-events: none;
315 | display: block;
316 | width: 0;
317 | height: 0;
318 | border-left: 3px solid transparent;
319 | border-right: 3px solid transparent;
320 | border-top: 5px solid currentColor;
321 | margin-left: $grid-size-small;
322 |
323 | // This gives the icon space on the right side consistent with the material
324 | // icon standards.
325 | margin-right: 2px;
326 | }
327 |
--------------------------------------------------------------------------------
/class-block-gallery.php:
--------------------------------------------------------------------------------
1 | Notice: Block Gallery in being deprecated in favor of the wonderful – and fully featured – gallery blocks within the CoBlocks plugin. Each Block Gallery block may be transformed into it's corresponding CoBlocks gallery block via traditional block transforms.
6 | * Author: GoDaddy
7 | * Author URI: https://www.godaddy.com
8 | * Version: 1.1.6
9 | * Text Domain: @@textdomain
10 | * Domain Path: languages
11 | * Tested up to: 5.2
12 | *
13 | * Block Gallery is free software: you can redistribute it and/or modify
14 | * it under the terms of the GNU General Public License as published by
15 | * the Free Software Foundation, either version 2 of the License, or
16 | * any later version.
17 | *
18 | * Block Gallery is distributed in the hope that it will be useful,
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | * GNU General Public License for more details.
22 | *
23 | * You should have received a copy of the GNU General Public License
24 | * along with @@pkg.title. If not, see .
25 | *
26 | * @package Block Gallery
27 | */
28 |
29 | // Exit if accessed directly.
30 | if ( ! defined( 'ABSPATH' ) ) {
31 | exit;
32 | }
33 |
34 | if ( ! class_exists( 'Block_Gallery' ) ) :
35 | /**
36 | * Main Block_Gallery Class.
37 | *
38 | * @since 1.0.0
39 | */
40 | final class Block_Gallery {
41 | /**
42 | * This plugin's instance.
43 | *
44 | * @var Block_Gallery
45 | * @since 1.0.0
46 | */
47 | private static $instance;
48 |
49 | /**
50 | * Main Block_Gallery Instance.
51 | *
52 | * Insures that only one instance of Block_Gallery exists in memory at any one
53 | * time. Also prevents needing to define globals all over the place.
54 | *
55 | * @since 1.0.0
56 | * @static
57 | * @uses Block_Gallery::constants() Setup the constants needed.
58 | * @uses Block_Gallery::includes() Include the required files.
59 | * @see WIDGETOPTS()
60 | * @return object|Block_Gallery The one true Block_Gallery
61 | */
62 | public static function instance() {
63 | if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Block_Gallery ) ) {
64 | self::$instance = new Block_Gallery();
65 | self::$instance->init();
66 | self::$instance->constants();
67 | self::$instance->asset_suffix();
68 | self::$instance->includes();
69 |
70 | }
71 | return self::$instance;
72 | }
73 |
74 | /**
75 | * Throw error on object clone.
76 | *
77 | * The whole idea of the singleton design pattern is that there is a single
78 | * object therefore, we don't want the object to be cloned.
79 | *
80 | * @since 1.0.0
81 | * @access protected
82 | * @return void
83 | */
84 | public function __clone() {
85 | // Cloning instances of the class is forbidden.
86 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheating huh?', '@@textdomain' ), '1.0' );
87 | }
88 |
89 | /**
90 | * Disable unserializing of the class.
91 | *
92 | * @since 1.0.0
93 | * @access protected
94 | * @return void
95 | */
96 | public function __wakeup() {
97 | // Unserializing instances of the class is forbidden.
98 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheating huh?', '@@textdomain' ), '1.0' );
99 | }
100 |
101 | /**
102 | * Setup plugin constants.
103 | *
104 | * @access private
105 | * @since 1.0.0
106 | * @return void
107 | */
108 | private function constants() {
109 | $this->define( 'BLOCKGALLERY_DEBUG', true );
110 | $this->define( 'BLOCKGALLERY_VERSION', '@@pkg.version' );
111 | $this->define( 'BLOCKGALLERY_HAS_PRO', false );
112 | $this->define( 'BLOCKGALLERY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
113 | $this->define( 'BLOCKGALLERY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
114 | $this->define( 'BLOCKGALLERY_PLUGIN_FILE', __FILE__ );
115 | $this->define( 'BLOCKGALLERY_PLUGIN_BASE', plugin_basename( __FILE__ ) );
116 | $this->define( 'BLOCKGALLERY_SHOP_URL', 'https://wpblockgallery.com/' );
117 | $this->define( 'BLOCKGALLERY_REVIEW_URL', 'https://wordpress.org/support/plugin/block-gallery/reviews/' );
118 | }
119 |
120 | /**
121 | * Define constant if not already set.
122 | *
123 | * @param string|string $name Name of the definition.
124 | * @param string|bool $value Default value.
125 | */
126 | private function define( $name, $value ) {
127 | if ( ! defined( $name ) ) {
128 | define( $name, $value );
129 | }
130 | }
131 |
132 | /**
133 | * Include required files.
134 | *
135 | * @access private
136 | * @since 1.0.0
137 | * @return void
138 | */
139 | private function includes() {
140 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/class-block-gallery-block-assets.php';
141 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/class-block-gallery-body-classes.php';
142 |
143 | if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
144 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/admin/class-block-gallery-url-generator.php';
145 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/admin/class-block-gallery-action-links.php';
146 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/admin/class-block-gallery-footer-text.php';
147 | require_once BLOCKGALLERY_PLUGIN_DIR . 'includes/admin/class-block-gallery-feedback.php';
148 | }
149 | }
150 |
151 | /**
152 | * Load actions
153 | *
154 | * @return void
155 | */
156 | private function init() {
157 | add_action( 'init', array( $this, 'load_textdomain' ) );
158 | }
159 |
160 | /**
161 | * Change the plugin's minified or src file name, based on debug mode.
162 | *
163 | * @since 1.0.0
164 | */
165 | public function asset_suffix() {
166 | if ( true === BLOCKGALLERY_DEBUG ) {
167 | define( 'BLOCKGALLERY_ASSET_SUFFIX', null );
168 | } else {
169 | define( 'BLOCKGALLERY_ASSET_SUFFIX', '.min' );
170 | }
171 | }
172 |
173 | /**
174 | * If debug is on, serve unminified source assets.
175 | *
176 | * @since 1.0.0
177 | * @param string|string $type The type of resource.
178 | * @param string|string $directory Any extra directories needed.
179 | */
180 | public function asset_source( $type = 'js', $directory = null ) {
181 |
182 | if ( 'js' === $type ) {
183 | if ( true === BLOCKGALLERY_DEBUG ) {
184 | return BLOCKGALLERY_PLUGIN_URL . 'src/' . $type . '/' . $directory . '/';
185 | } else {
186 | return BLOCKGALLERY_PLUGIN_URL . 'dist/' . $type . '/' . $directory . '/';
187 | }
188 | } else {
189 | return BLOCKGALLERY_PLUGIN_URL . 'dist/css/' . $directory;
190 | }
191 | }
192 |
193 | /**
194 | * Check if pro exists.
195 | *
196 | * @access public
197 | */
198 | public function has_pro() {
199 | if ( true === BLOCKGALLERY_HAS_PRO ) {
200 | return true;
201 | } else {
202 | return false;
203 | }
204 | }
205 |
206 | /**
207 | * Check if pro is activated.
208 | *
209 | * @access public
210 | */
211 | public function is_pro() {
212 |
213 | if ( class_exists( 'Block_Gallery_Pro' ) ) {
214 | return true;
215 | } else {
216 | return false;
217 | }
218 | }
219 |
220 | /**
221 | * Loads the plugin language files.
222 | *
223 | * @access public
224 | * @since 1.0.0
225 | * @return void
226 | */
227 | public function load_textdomain() {
228 | load_plugin_textdomain( '@@textdomain', false, dirname( plugin_basename( BLOCKGALLERY_PLUGIN_DIR ) ) . '/languages/' );
229 | }
230 | }
231 | endif;
232 |
233 | /**
234 | * The main function for that returns Block_Gallery
235 | *
236 | * The main function responsible for returning the one true Block_Gallery
237 | * Instance to functions everywhere.
238 | *
239 | * Use this function like you would a global variable, except without needing
240 | * to declare the global.
241 | *
242 | * Example:
243 | *
244 | * @since 1.0.0
245 | * @return object|Block_Gallery The one true Block_Gallery Instance.
246 | */
247 | function block_gallery() {
248 | return Block_Gallery::instance();
249 | }
250 |
251 | // Get the plugin running. Load on plugins_loaded action to avoid issue on multisite.
252 | if ( function_exists( 'is_multisite' ) && is_multisite() ) {
253 | add_action( 'plugins_loaded', 'block_gallery', 90 );
254 | } else {
255 | block_gallery();
256 | }
257 |
--------------------------------------------------------------------------------
/src/blocks/carousel/components/edit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import classnames from 'classnames';
5 | import filter from 'lodash/filter';
6 | import Flickity from 'react-flickity-component';
7 |
8 | /**
9 | * Internal dependencies
10 | */
11 | import GalleryImage from '../../../components/gallery-image';
12 | import GalleryPlaceholder from '../../../components/gallery-placeholder';
13 | import GalleryDropZone from '../../../components/gallery-dropzone';
14 | import GalleryUpload from '../../../components/gallery-upload';
15 | import Inspector from './inspector';
16 | import { BackgroundStyles } from '../../../components/background/';
17 | import { title, icon } from '../'
18 | import { GlobalClasses, GlobalToolbar } from '../../../components/global/';
19 |
20 | /**
21 | * WordPress dependencies
22 | */
23 | const { __, sprintf } = wp.i18n;
24 | const { Component, Fragment } = wp.element;
25 | const { compose } = wp.compose;
26 | const { withSelect } = wp.data;
27 | const { withNotices, ResizableBox } = wp.components;
28 | const { withColors, RichText } = wp.editor;
29 |
30 | /**
31 | * Block consts.
32 | */
33 | const flickityOptions = {
34 | draggable: false,
35 | pageDots: true,
36 | prevNextButtons: true,
37 | wrapAround: true,
38 | autoPlay: false,
39 | arrowShape: {
40 | x0: 10,
41 | x1: 60, y1: 50,
42 | x2: 65, y2: 45,
43 | x3: 20
44 | },
45 | }
46 |
47 | /**
48 | * Block edit function
49 | */
50 | class Edit extends Component {
51 | constructor() {
52 | super( ...arguments );
53 |
54 | this.onSelectImage = this.onSelectImage.bind( this );
55 | this.onRemoveImage = this.onRemoveImage.bind( this );
56 | this.setImageAttributes = this.setImageAttributes.bind( this );
57 | this.onFocusCaption = this.onFocusCaption.bind( this );
58 | this.onItemClick = this.onItemClick.bind( this );
59 |
60 | this.state = {
61 | selectedImage: null,
62 | captionFocused: false,
63 | };
64 | }
65 |
66 | componentDidMount() {
67 |
68 | // This block does not support the following attributes.
69 | this.props.setAttributes( {
70 | lightbox: undefined,
71 | lightboxStyle: undefined,
72 | shadow: undefined,
73 | } );
74 | }
75 |
76 | componentDidUpdate( prevProps ) {
77 |
78 | // Deselect images when deselecting the block.
79 | if ( ! this.props.isSelected && prevProps.isSelected ) {
80 | this.setState( {
81 | selectedImage: null,
82 | captionSelected: false,
83 | captionFocused: false,
84 | } );
85 | }
86 |
87 | if ( ! this.props.isSelected && prevProps.isSelected && this.state.captionFocused ) {
88 | this.setState( {
89 | captionFocused: false,
90 | } );
91 | }
92 |
93 | if ( this.props.attributes.gutter <= 0 ) {
94 | this.props.setAttributes( {
95 | radius: 0,
96 | } );
97 | }
98 |
99 | if ( this.props.attributes.gridSize == 'xlrg' && prevProps.attributes.align == undefined ) {
100 | this.props.setAttributes( {
101 | gutter: 0,
102 | gutterMobile: 0,
103 | } );
104 | }
105 | }
106 |
107 | onSelectImage( index ) {
108 | return () => {
109 | if ( this.state.selectedImage !== index ) {
110 | this.setState( {
111 | selectedImage: index,
112 | captionFocused: false,
113 | } );
114 | }
115 | };
116 | }
117 |
118 | onRemoveImage( index ) {
119 | return () => {
120 | const images = filter( this.props.attributes.images, ( img, i ) => index !== i );
121 | const { gridSize } = this.props.attributes;
122 | this.setState( { selectedImage: null } );
123 | this.props.setAttributes( {
124 | images,
125 | } );
126 | };
127 | }
128 |
129 | setImageAttributes( index, attributes ) {
130 | const { attributes: { images }, setAttributes } = this.props;
131 | if ( ! images[ index ] ) {
132 | return;
133 | }
134 | setAttributes( {
135 | images: [
136 | ...images.slice( 0, index ),
137 | {
138 | ...images[ index ],
139 | ...attributes,
140 | },
141 | ...images.slice( index + 1 ),
142 | ],
143 | } );
144 | }
145 |
146 | onFocusCaption() {
147 | if ( ! this.state.captionFocused ) {
148 | this.setState( {
149 | captionFocused: true,
150 | } );
151 | }
152 | }
153 |
154 | onItemClick() {
155 | if ( ! this.props.isSelected ) {
156 | this.props.onSelect();
157 | }
158 |
159 | if ( this.state.captionFocused ) {
160 | this.setState( {
161 | captionFocused: false,
162 | } );
163 | }
164 | }
165 |
166 | render() {
167 | const {
168 | attributes,
169 | backgroundColor,
170 | className,
171 | isSelected,
172 | noticeOperations,
173 | noticeUI,
174 | setAttributes,
175 | toggleSelection,
176 | captionColor,
177 | } = this.props;
178 |
179 | const {
180 | align,
181 | autoPlay,
182 | gridSize,
183 | gutter,
184 | gutterMobile,
185 | height,
186 | images,
187 | pageDots,
188 | prevNextButtons,
189 | primaryCaption,
190 | } = attributes;
191 |
192 | const dropZone = (
193 |
198 | );
199 |
200 | const wrapperClasses = classnames(
201 | 'is-cropped',
202 | ...GlobalClasses( attributes ), {
203 | [ `align${ align }` ] : align,
204 | [ `has-horizontal-gutter` ] : gutter > 0,
205 | [ `has-no-dots` ] : ! pageDots,
206 | [ `has-no-arrows` ] : ! prevNextButtons,
207 | 'is-selected': isSelected,
208 |
209 | }
210 | );
211 |
212 | const wrapperStyles = {
213 | ...BackgroundStyles( attributes ),
214 | backgroundColor: backgroundColor.color,
215 | 'is-selected': isSelected,
216 | };
217 |
218 | const captionStyles = {
219 | color: captionColor.color,
220 | };
221 |
222 | const flickityClasses = classnames(
223 | 'has-carousel',
224 | `has-carousel-${ gridSize }`, {}
225 | );
226 |
227 | if ( images.length === 0 ) {
228 | return (
229 |
235 | );
236 | }
237 |
238 | return (
239 |
240 |
243 |
246 | { noticeUI }
247 | {
267 | setAttributes( {
268 | height: parseInt( height + delta.height, 10 ),
269 | } );
270 | toggleSelection( true );
271 | } }
272 | onResizeStart={ () => {
273 | toggleSelection( false );
274 | } }
275 | >
276 | { dropZone }
277 |
278 |
282 |
this.flkty = c }
286 | options={ flickityOptions }
287 | reloadOnUpdate={ true }
288 | updateOnEachImageLoad={ true }
289 | >
290 | { images.map( ( img, index ) => {
291 | // translators: %1$d is the order number of the image, %2$d is the total number of images
292 | const ariaLabel = __( sprintf( 'image %1$d of %2$d in gallery', ( index + 1 ), images.length ) );
293 |
294 | return (
295 |
296 | this.setImageAttributes( index, attrs ) }
308 | caption={ img.caption }
309 | aria-label={ ariaLabel }
310 | supportsCaption={ false }
311 | />
312 |
313 | );
314 | } ) }
315 | { isSelected && (
316 |
322 | ) }
323 |
324 |
325 |
326 |
327 | { ( ! RichText.isEmpty( primaryCaption ) || isSelected ) && (
328 | setAttributes( { primaryCaption: value } ) }
336 | isSelected={ this.state.captionFocused }
337 | keepPlaceholderOnFocus
338 | inlineToolbar
339 | />
340 | ) }
341 |
342 | );
343 | }
344 | }
345 |
346 | export default compose( [
347 | withColors( { backgroundColor : 'background-color', captionColor : 'color' } ),
348 | withNotices,
349 | ] )( Edit );
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === Block Gallery - Photo Gallery Gutenberg Blocks ===
2 | Author URI: https://www.godaddy.com
3 | Plugin URI: https://wpblockgallery.com
4 | Contributors: richtabor
5 | Tags: blocks, gutenberg, gallery, page builder, gutenberg blocks, editor, photo gallery, masonry, block, slider, carousel
6 | Requires at least: 5.0
7 | Tested up to: 5.2
8 | Requires PHP: 5.2.4
9 | Stable tag: @@pkg.version
10 | License: GPL-2.0
11 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
12 |
13 | == Description ==
14 |
15 | ## Important notice
16 | **Block Gallery in being deprecated** in favor of the wonderful gallery blocks within [CoBlocks](https://wordpress.org/plugins/coblocks). Each Block Gallery block may be transformed into it's corresponding CoBlocks gallery block via traditional block transforms.
17 |
18 | [Block Gallery](https://wpblockgallery.com?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery) is a suite of beautiful gallery Gutenberg blocks for photographers, artists, writers and content marketers. This is the smartest, most powerful photo gallery plugin for WordPress. Block Gallery is absolutely brilliant any way you look at it.
19 |
20 | >Good news! Block Gallery was selected as this year’s winner in the Best Solution category of the [Automattic Design Awards](https://automatticdesignaward.blog/2018/12/08/the-winners/) at WordCamp US 2018. 🔥🔥
21 |
22 | ## A short demo of Block Gallery
23 | [vimeo https://vimeo.com/296746112]
24 |
25 | ## Unrivaled, in every way
26 | The first of its kind, [Block Gallery](https://wpblockgallery.com?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery) offers an unrivaled drag and drop gallery building experience in Gutenberg. Drop your images in your choice of photo gallery block, customize display settings, hit publish.
27 |
28 | ## Unparalleled capabilities
29 | An innovative transform system lets you instantly change your photos galleries into another form. Go from a fullscreen masonry gallery to a casual carousel, with just a single click. You won't find another Gutenberg gallery plugin with this kind of capability. Guaranteed.
30 |
31 | ## Highly responsive
32 | Our Gutenberg gallery blocks are second-to-none, featuring fullscale responsive support. And with fine controls for mobile and desktop styles, you can set custom styling for each gallery.
33 |
34 | ## A Super-fast experience
35 | We've built a highly interactive and intuitive experience with a focus on speed and ease of use. Drag. Drop. Transform. Style.
36 |
37 | ## Included Gallery Gutenberg Blocks
38 |
39 | * Masonry Gallery - ([demo](https://richtabor.com/block-gallery-blocks/?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery-masonry-demo#masonry))
40 | * Fullscreen Stacked Gallery - ([demo](https://richtabor.com/block-gallery-blocks/?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery-stacked-demo#stacked))
41 | * Carousel Slider - ([demo](https://richtabor.com/block-gallery-blocks/?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery-carousel-demo#carousel))
42 |
43 | ## Works with CoBlocks
44 | If you enjoy Block Gallery, check out [CoBlocks](https://wordpress.org/plugins/coblocks), a suite of page builder WordPress blocks and tools for the Gutenberg editor — also built by Rich. It's fantastic!
45 |
46 | == Screenshots ==
47 |
48 | 1. Masonry Grid block
49 | 2. Carousel Slideshow block
50 | 3. Stacked Fullwidth block
51 | 4. Offset Grid block (Pro Only - coming soon)
52 | 5. Auto Height Slider block (Pro Only - coming soon)
53 |
54 | == Installation ==
55 |
56 | 1. Upload the `block-gallery` folder to your `/wp-content/plugins/` directory or alternatively upload the block-gallery.zip file via the plugin page of WordPress by clicking 'Add New' and selecting the zip from your computer.
57 | 2. Install and activate the Gutenberg WordPress plugin (if pre WordPress 5.0).
58 | 3. Activate the Block Gallery WordPress plugin through the 'Plugins' menu in WordPress.
59 |
60 | == Frequently Asked Questions ==
61 |
62 | = How do I start using Gutenberg? =
63 | To get the full experience of the next-generation WordPress block editor, you'll need a Gutenberg-ready WordPress theme, like [Tabor](https://themebeans.com/themes/tabor?utm_medium=block-gallery-lite&utm_source=readme&utm_campaign=readme&utm_content=tabor) or [Stash](https://themebeans.com/themes/stash?utm_medium=block-gallery-lite&utm_source=readme&utm_campaign=readme&utm_content=stash). Then install the [Gutenberg](https://wordpress.org/plugins/gutenberg/) WordPress plugin. That's it! 💥
64 |
65 | = What themes work with Block Gallery =
66 | Most WordPress themes that have baked in Gutenberg support will work with Block Gallery. If you’re looking for exceptional themes, check out my theme catalogue at [ThemeBeans](https://themebeans.com?utm_medium=wp.org&utm_source=wordpressorg&utm_campaign=readme&utm_content=block-gallery).
67 |
68 | = Is Block Gallery free? =
69 | Yes! Block Gallery's core features are absolutely free.
70 |
71 | = Where can I ask for help? =
72 | Please reach out via the official [support forum on WordPress.org](https://wordpress.org/support/plugin/block-gallery/).
73 |
74 | == Changelog ==
75 |
76 | = 1.1.6 =
77 | * New: Block Gallery now supports WordPress 5.1 and Gutenberg 5.0
78 | * Tweak: Add Block Gallery color to icons within the block inserter
79 |
80 | = 1.1.5 =
81 | * Tweak: Use the MediaUploadCheck component to make sure the current user has upload permissions
82 | * Fix: Resolve lodash/isEmpty issue with the npm start command [thanks @mtekk]
83 | * Fix: Resolve issue where image radius styles were not applied to child captions [thanks @wido]
84 |
85 | = 1.1.4 =
86 | * New: Add toggles for turning image captions on/off for each block
87 | * New: Add new "none" Caption Style option
88 | * New: Add new options for slider autoplay times up to 10 seconds [thanks @batracy]
89 | * Fix: Resolve translatable placeholder issue in the Slider Settings panel [thanks @morganestes]
90 | * Fix: Resolve translatable invalid HTML issue in the Carousel block [thanks @morganestes]
91 | * Fix: Resolve issue where the Carousel autoplay speed was not functioning properly [thanks @morganestes]
92 | * Fix: Resolve PHP 5.4.16 compatibility issue [thanks @ndcadmin]
93 | * Tweak: Adjust Stacked Inspector interface
94 |
95 | = 1.1.3 =
96 | * Fix: Resolve issue where block assets were not loading on the blogroll
97 |
98 | = 1.1.2 =
99 | * New: Add minor style touch-ups for the default Twenty Nineteen WordPress theme
100 |
101 | = 1.1.1 =
102 | * Tweak: Remove Gutenberg check
103 |
104 | = 1.1.0, December 04, 2018 =
105 | * New: Add ability to transform Image blocks to Block Gallery blocks
106 | * New: Add ":" prefix transforms using each blocks' name - i.e. ":masonry",
107 | * New: Load frontend assets only on pages that need them
108 | * New: Add translation strings in /languages/block-gallery.pot
109 | * New: Add support for the WP 5.0 wp_set_script_translations() function
110 | * New: Add styling for the core Twenty Seventeen theme
111 | * New: Add styling for the core Twenty Sixteen theme
112 | * New: Add styling for the core Twenty Fifteen theme
113 | * New: Add styling for the core Twenty Fourteen theme
114 | * New: Add styling for the core Twenty Twelve theme
115 | * New: Add styling for the core Twenty Eleven theme
116 | * New: Add block-gallery-translations.php for referencing PHP translatable strings
117 | * Tweak: Improve grid size responsiveness for the Masonry block
118 | * Tweak: Hide the GalleryUpload component if not selected
119 | * Tweak: Improve Flickity focus styles for better theme compatibility
120 |
121 | = 1.0.9 =
122 | Tweak: Use better specificity for figcaption margins
123 | Tweak: Add inherit color for caption link hovers
124 |
125 | = 1.0.8 =
126 | * Tweak: Remove unnecessary style dependancies
127 |
128 | = 1.0.7 =
129 | * Tweak: Indicate uploading using a spinner
130 | * Tweak: Adjust figcaption margin for better theme compatibility
131 |
132 | = 1.0.6 =
133 | * Tweak: Adjust mobile styles for the block inspector controls UI
134 | * Tweak: Adjust UI of SizeControl controls
135 | * Tweak: Adjust pickRelevantMediaFiles
136 | * Tweak: Hide shadow controls if Stacked block is fullwidth
137 | * Tweak: Improve default caption style for Carousel
138 | * Tweak: Tweak mobile styles for Carousel block arrows
139 | * Tweak: Adjust height of Stacked image uploader
140 |
141 | = 1.0.5 =
142 | * Tweak: Ensure the last figcaption in the Stacked Block is styled appropriately
143 | * Tweak: Update styling of feedback notice
144 | * Tweak: Improve language of the gallery instructions for placeholders
145 | * Tweak: Tweak icon placement in the placeholder label
146 | * Tweak: Remove caption color that was overriding theme styles
147 | * Tweak: Use register_block_type to check if the block editor is live
148 | * Tweak: Improve UI of the ResponsiveTabsControl component
149 | * Tweak: Add tab navigation support for gallery images
150 | * Tweak: Tweak editor styles for captions
151 |
152 | = 1.0.4 =
153 | * Fix: Resolve issue with the Stacked block shadow attribute
154 |
155 | = 1.0.3 =
156 | * New: Add support for adding a primary caption to the Carousel block
157 | * New: Add font size option for the Stacked gallery block
158 | * Tweak: Improve UI of the slider arrows within the editor
159 | * Tweak: Improve help language of the Slider Settings panel for better context and understanding
160 | * Tweak: Improve UI of the ResponsiveTabsControl component
161 | * Tweak: Add slider controls to global transforms
162 | * Fix: Improve display of fullwidth images in the Stacked gallery block
163 | * Fix Improved reliablity of the Stacked block when triggering fullwidth imagery
164 | * Fix: Improve display of carousel arrows
165 |
166 | = 1.0.2 =
167 | * Tweak: Improve figcaption display
168 | * Tweak: Improve block category registration
169 | * Tweak: Add icon to the block category for Gutenberg 4.2+
170 | * Fix: Color palette colors properly render in the editor
171 |
172 | = 1.0.1 =
173 | * New: Improve block registration
174 |
175 | = 1.0.0 =
176 | * Initial release on WordPress.org. Enjoy!
177 |
--------------------------------------------------------------------------------