├── .babelrc ├── .gitignore ├── README.md ├── blocks ├── block-layout │ ├── block-layout.build.js │ ├── block-layout.editor.css │ ├── block-layout.view.css │ ├── block-layout.view.js │ └── index.php ├── carousel │ ├── carousel.build.js │ ├── carousel.editor.css │ ├── carousel.view.css │ ├── carousel.view.js │ └── index.php ├── gallery │ ├── dist │ │ ├── css │ │ │ ├── lightbox.css │ │ │ └── lightbox.min.css │ │ ├── images │ │ │ ├── close.png │ │ │ ├── loading.gif │ │ │ ├── next.png │ │ │ └── prev.png │ │ └── js │ │ │ ├── lightbox-plus-jquery.js │ │ │ ├── lightbox-plus-jquery.min.js │ │ │ ├── lightbox-plus-jquery.min.map │ │ │ ├── lightbox.js │ │ │ ├── lightbox.min.js │ │ │ └── lightbox.min.map │ ├── gallery.build.js │ ├── gallery.editor.css │ ├── gallery.view.css │ └── index.php ├── hello-world │ ├── hello-world.build.js │ ├── hello-world.editor.css │ ├── hello-world.view.css │ ├── hello-world.view.js │ └── index.php ├── image-hero │ ├── image-hero.build.js │ ├── image-hero.editor.css │ ├── image-hero.view.css │ ├── image-hero.view.js │ └── index.php ├── media-block │ ├── index.php │ ├── media-block.build.js │ ├── media-block.editor.css │ ├── media-block.view.css │ └── media-block.view.js ├── prism-code │ ├── index.php │ ├── prism-code.build.js │ ├── prism-code.editor.css │ ├── prism-code.view.css │ └── prism-code.view.js ├── quote │ ├── index.php │ ├── quote.build.js │ ├── quote.editor.css │ └── quote.view.css ├── react-view │ ├── index.php │ ├── react-view.build.js │ ├── react-view.editor.css │ ├── react-view.view.css │ └── react-view.view.js ├── recent-posts │ ├── index.php │ ├── recent-posts.build.js │ ├── recent-posts.editor.css │ ├── recent-posts.view.css │ └── recent-posts.view.js └── side-by-side │ ├── index.php │ ├── side-by-side.build.js │ ├── side-by-side.editor.css │ └── side-by-side.view.css ├── index.php ├── js └── i18n.js ├── package-lock.json ├── package.json ├── src ├── block-layout │ ├── block-layout.editor.css │ ├── block-layout.src.js │ └── block-layout.view.css ├── carousel │ ├── carousel.editor.css │ ├── carousel.src.js │ └── carousel.view.css ├── gallery │ ├── Image.js │ ├── gallery.editor.css │ ├── gallery.src.js │ └── gallery.view.css ├── hello-world │ ├── _test.scss │ ├── hello-world.editor.css │ ├── hello-world.src.js │ └── hello-world.view.scss ├── image-hero │ ├── image-hero.editor.css │ ├── image-hero.src.js │ └── image-hero.view.css ├── media-block │ ├── media-block.editor.css │ ├── media-block.src.js │ └── media-block.view.css ├── prism-code │ ├── prism-code.editor.css │ ├── prism-code.src.js │ ├── prism-code.view.css │ └── prismjs │ │ ├── prism.css │ │ └── prism.js ├── quote │ ├── quote.editor.css │ ├── quote.src.js │ └── quote.view.css ├── react-view │ ├── components │ │ └── Editor.jsx │ ├── react-view.editor.css │ ├── react-view.src.js │ ├── react-view.view.js │ └── react-view.view.scss ├── recent-posts │ ├── recent-posts.editor.css │ ├── recent-posts.src.js │ └── recent-posts.view.css └── side-by-side │ ├── side-by-side.editor.scss │ ├── side-by-side.src.js │ └── side-by-side.view.scss └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@wordpress/babel-plugin-makepot", 5 | { 6 | "output": "languages/guty-blocks-js.pot" 7 | } 8 | ], 9 | [ 10 | "transform-react-jsx", 11 | { 12 | "pragma": "wp.element.createElement" 13 | } 14 | ] 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # guty-blocks 2 | 3 | A build environment for [Gutenberg blocks](https://wordpress.org/gutenberg/handbook/block-api/) with a few example blocks. 4 | 5 | ## How to use the blocks 6 | 7 | 1. Install and activate the [Gutenberg plugin](https://WordPress.org/plugins/gutenberg/) inside of WordPress 8 | 2. Clone this project into the plugins folder of WordPress 9 | 3. Activate this plugin inside of WordPress 10 | 4. You now should have access to guty-blocks in the Gutenberg editor 11 | 12 | ## Current example blocks: 13 | 14 | - Hello world - simple static block 15 | - Media - Allows adding an image and text to the side 16 | - Quote - a simple quote block 17 | - Custom Gallery - A custom image gallery that you can add images, title, rearrange images, etc. It uses [lightbox2](http://lokeshdhakar.com/projects/lightbox2/) by Lokesh Dhakar (MIT). 18 | - Carousel - A simple carousel example 19 | - Image hero - Includes a background, hero text, and a gradient overlay 20 | - Recent posts - Uses WordPress API to fetch posts and include them in the block 21 | - Block layout - Uses InnerBlock component to nest blocks into blocks on the page (css) 22 | - Prism code - A code formatter using [prismjs](http://prismjs.com/) 23 | - React View - An example where a live react app is included in the view of the page. In the editor posts are selected. In the view, the react app will fetch those post ids and preview the post. 24 | - Side by Side - A simple image next to text that is responsive. 25 | 26 | ## How to make blocks 27 | 28 | Make sure you have installed and activated the [Gutenberg plugin](https://wordpress.org/plugins/gutenberg/) and this project plugin. You will need to have `npm` installed and configured correctly for your Operating System. 29 | 30 | 1. Run `npm install` within this project plugin's main directory. 31 | 2. Duplicate any of the folders within the src folder and make sure to rename the js and css files to your new block name. You will need to rename file names inside of the `src.js` file as well. 32 | 3. You will need to create an `index.php` file that loads your new block in the `blocks/[yourblockname]` folder after the block is built. You can follow the pattern in the example blocks inside of the blocks folder 33 | 4. Webpack looks for `*.js` files following this pattern: `./source/[yourblockname]/[yourblockname].src.js`. If you do not include the `.src.js` suffix, Webpack will not consider it the main gutenberg block JS file. 34 | 5. Webpack also looks for `*.view.js` files to build files that may be called as a part of the view functionality, but are separate from the editor. 35 | 36 | ## How to build the block files 37 | 38 | 1. `npm run watch` 39 | Sets up a "watcher" that monitors files for changes to trigger a rebuild. Essentially the same as `npm run build` below, but continuous. 40 | 2. `npm run build` 41 | This command runs [Babel](https://babeljs.io/) to transpile modern JavaScript to something more browsers understand. It uses the [@WordPress/babel-plugin-makepot](https://www.npmjs.com/package/@wordpress/babel-plugin-makepot) Babel plugin to automatically extract all translatable strings from the JavaScript, and create a POT file for them (`guty-blocks-js.pot`). This `-js.pot` POT file is an intermediary step to internationlizing all strings for the plugin. 42 | 43 | ## Internationlization (i18n) / Localization 44 | 45 | ### What is [internationalization](https://developer.wordpress.org/plugins/internationalization/) (i18n)? 46 | > Internationalization is the process of developing a plugin, so it can easily be translated into other languages. Internationalization is often abbreviated as i18n (because there are 18 letters between the letters i and n). 47 | 48 | In WordPress, we use the gettext family of functions to make "literal strings" in our code available for translation. This is "internationalization." 49 | 50 | For example, a 404 page might have a hard-coded message that says "Oops! That page can’t be found." That literal string is not content coming from the database, and it can't easily be changed to another language by the site owner, without editing our code. 51 | 52 | However, to internationalize that string, we would wrap it in a gettext function, so that it is made available for translation. 53 | 54 | A simple example of how we might do this in WordPress PHP: 55 | `` 56 | 57 | The Gutenberg team has taken those gettext functions, and made them available to JS as well, via the [@wordpress/i18n](https://www.npmjs.com/package/@wordpress/i18n#api) npm package. The same concepts from WordPress PHP can now be used in your Gutenblocks. 58 | 59 | Now let's assume we have a Gutenblock that has a literal string in it, that we need to make available for translation. 60 | 61 | We would import the __() function into our block like so, `const { __ } = wp.i18n;`. 62 | 63 | Then we would use that function to wrap a literal string that needs to be available for translation, like so, `title: __( 'Hello World!', 'my-plugin-text-domain' )`. 64 | 65 | We would then use a tool (of which there are many) to create a POT (Portable Object Template) file. This file contains the original strings (in English) from the plugin. It is sort of like a dictionary, containing all the known literal strings. 66 | 67 | ### What is [localization](https://developer.wordpress.org/plugins/internationalization/localization/) (l10n)? 68 | > Localization describes the subsequent process of translating an internationalized plugin. Localization is abbreviated as l10n (because there are 10 letters between the l and the n). 69 | 70 | Localization then, is when a translator provides translated versions of those literal string in another language. 71 | 72 | > Every translator will take the POT file and translate the msgstr sections into their own language. The result is a PO (Portable Object) file with the same format as a POT, but with translations and some specific headers. There is one PO file per language. 73 | 74 | > From every translated PO file, an MO (Machine Object) file is built. These are machine-readable, binary files that the gettext functions actually use (they don’t care about .POT or .PO files), and are a “compiled” version of the PO file. The conversion is done using the msgfmt tool. In general, an application may use more than one large logical translatable module and a different MO file accordingly. A text domain is a handle of each module, which has a different MO file. 75 | 76 | ### How do we create the plugin's POT file? 77 | 78 | Gutenberg's [internationalization](https://developer.wordpress.org/plugins/internationalization/) (i18n) and [localization](https://developer.wordpress.org/plugins/internationalization/localization/) (l10n) features, much like Gutenberg itself, are evolving quickly. The workflow for i18n/l10n may change, improve, and simplify as the project evolves. 79 | 80 | For the purposes of `guty-blocks`, we are using a combination of three methods to generate our plugin's final POT file. 81 | 82 | First, the build process, either with `npm run watch` or `npm run build`, will use the [@WordPress/babel-plugin-makepot](https://www.npmjs.com/package/@wordpress/babel-plugin-makepot) Babel plugin, to automatically extract all translatable strings from the JavaScript, and create a POT file specifically for those JS strings (`guty-blocks-js.pot`). 83 | 84 | Second, the build process provides the command `npm run pot-to-php` to generate a PHP file, `guty-blocks-js-translations.php`, containing the JS strings from `guty-blocks-js.pot`, via [@wordpress/i18n's](https://www.npmjs.com/package/@wordpress/i18n) [pot-to-php](https://www.npmjs.com/package/@wordpress/i18n#build) script. 85 | 86 | Third, the build process provides the command `npm run makepot`* to utilize [WP-CLI's](https://wp-cli.org/#installing) [`wp i18n make-pot`](https://github.com/wp-cli/i18n-command) command, to build a final POT file, `guty-blocks.pot`, that contains all the strings from both JS and PHP. `guty-blocks.pot` is the final POT file, the one used for our plugin's text domain, and is the one that any localized PO/MO files should be built from. 87 | 88 | ***NOTE:** `npm run makepot` requires that you have [WP-CLI](https://wp-cli.org/#installing) installed and configured properly for your project. The [i18n command](https://github.com/wp-cli/i18n-command) is slated for future inclusion as a bundled WP-CLI command, but as of WP-CLI v 1.5.0, it is still an add-on command that must be installed separately. Refer to the i18n command's [installation instructions](https://github.com/wp-cli/i18n-command#installing). 89 | -------------------------------------------------------------------------------- /blocks/block-layout/block-layout.build.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 11); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ({ 67 | 68 | /***/ 11: 69 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 70 | 71 | "use strict"; 72 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 73 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css__ = __webpack_require__(12); 74 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css__); 75 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__block_layout_view_css__ = __webpack_require__(13); 76 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__block_layout_view_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__block_layout_view_css__); 77 | 78 | 79 | 80 | const { 81 | registerBlockType 82 | } = wp.blocks; 83 | 84 | const { 85 | InnerBlocks, 86 | InspectorControls 87 | } = wp.editor; 88 | 89 | registerBlockType('guty-blocks/block-layout', { 90 | title: 'Block Layout', 91 | category: 'layout', 92 | 93 | attributes: {// Somewhat like setting initial state in a react app. 94 | // Strategy for mapping rendered attributes back into editable state 95 | 96 | }, 97 | 98 | // The editor "render" function 99 | edit(props) { 100 | return [props.isSelected && wp.element.createElement( 101 | InspectorControls, 102 | null, 103 | 'Select the number of columns for your blocks:' 104 | ), wp.element.createElement( 105 | 'div', 106 | { 'class': props.className }, 107 | wp.element.createElement(InnerBlocks, { 108 | layouts: { 109 | normal: { label: 'Normal Width', icon: 'align-center' }, 110 | wide: { label: 'Width Width', icon: 'align-wide' } 111 | } }) 112 | )]; 113 | }, 114 | 115 | // The save "render" function 116 | save(props) { 117 | return wp.element.createElement( 118 | 'div', 119 | { 'class': props.className }, 120 | wp.element.createElement(InnerBlocks.Content, null) 121 | ); 122 | } 123 | 124 | }); 125 | 126 | /***/ }), 127 | 128 | /***/ 12: 129 | /***/ (function(module, exports) { 130 | 131 | // removed by extract-text-webpack-plugin 132 | 133 | /***/ }), 134 | 135 | /***/ 13: 136 | /***/ (function(module, exports) { 137 | 138 | // removed by extract-text-webpack-plugin 139 | 140 | /***/ }) 141 | 142 | /******/ }); -------------------------------------------------------------------------------- /blocks/block-layout/block-layout.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-block-layout .editor-block-list__layout{ 2 | width: 100% 3 | } 4 | 5 | .wp-block-guty-blocks-block-layout .editor-block-list__layout > .editor-block-list__block { 6 | display: inline-block; 7 | width: 50%; 8 | padding: 8px; 9 | } 10 | -------------------------------------------------------------------------------- /blocks/block-layout/block-layout.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-block-layout { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | align-items: stretch; 6 | } 7 | 8 | .wp-block-guty-blocks-block-layout > * { 9 | width: 50%; 10 | } 11 | -------------------------------------------------------------------------------- /blocks/block-layout/block-layout.view.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 8); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ({ 67 | 68 | /***/ 10: 69 | /***/ (function(module, exports) { 70 | 71 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-block-layout {\n| display: flex;\n| flex-direction: row;"); 72 | 73 | /***/ }), 74 | 75 | /***/ 8: 76 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 77 | 78 | "use strict"; 79 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 80 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css__ = __webpack_require__(9); 81 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__block_layout_editor_css__); 82 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__block_layout_view_css__ = __webpack_require__(10); 83 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__block_layout_view_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__block_layout_view_css__); 84 | 85 | 86 | 87 | const { 88 | registerBlockType, 89 | InnerBlocks, 90 | InspectorControls 91 | } = wp.blocks; 92 | 93 | registerBlockType('guty-blocks/block-layout', { 94 | title: 'Block Layout', 95 | category: 'layout', 96 | 97 | attributes: {// Somewhat like setting initial state in a react app. 98 | // Strategy for mapping rendered attributes back into editable state 99 | 100 | }, 101 | 102 | // The editor "render" function 103 | edit(props) { 104 | return [props.isSelected && wp.element.createElement( 105 | InspectorControls, 106 | null, 107 | 'Select the number of columns for your blocks:' 108 | ), wp.element.createElement( 109 | 'div', 110 | { 'class': props.className }, 111 | wp.element.createElement(InnerBlocks, { 112 | layouts: { 113 | normal: { label: 'Normal Width', icon: 'align-center' }, 114 | wide: { label: 'Width Width', icon: 'align-wide' } 115 | } }) 116 | )]; 117 | }, 118 | 119 | // The save "render" function 120 | save(props) { 121 | return wp.element.createElement( 122 | 'div', 123 | { 'class': props.className }, 124 | wp.element.createElement(InnerBlocks.Content, null) 125 | ); 126 | } 127 | 128 | }); 129 | 130 | /***/ }), 131 | 132 | /***/ 9: 133 | /***/ (function(module, exports) { 134 | 135 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-block-layout .editor-block-list__layout{\n| width: 100%\n| }"); 136 | 137 | /***/ }) 138 | 139 | /******/ }); -------------------------------------------------------------------------------- /blocks/block-layout/index.php: -------------------------------------------------------------------------------- 1 | wp.element.createElement( 137 | 'button', 138 | { 139 | onClick: open, 140 | style: { marginBottom: '16px' } }, 141 | 'Select/Change Images' 142 | ) 143 | }); 144 | 145 | return [wp.element.createElement( 146 | InspectorControls, 147 | null, 148 | wp.element.createElement( 149 | 'div', 150 | null, 151 | MyMediaUpload 152 | ), 153 | wp.element.createElement( 154 | 'div', 155 | { style: { marginBottom: '16px' } }, 156 | 'Set fade length (milliseconds):', 157 | wp.element.createElement('input', { 158 | type: 'number', 159 | value: fadeTime, 160 | onChange: changeFadeTime 161 | }) 162 | ), 163 | wp.element.createElement( 164 | 'div', 165 | { style: { marginBottom: '16px' } }, 166 | 'Set slide interval (seconds):', 167 | wp.element.createElement('input', { 168 | type: 'number', 169 | value: carouselTime, 170 | onChange: changeCarouselTime 171 | }) 172 | ) 173 | ), MyMediaUpload, wp.element.createElement( 174 | 'div', 175 | { 176 | className: className, 177 | 'data-carousel-time': carouselTime, 178 | style: { 179 | paddingBottom: '56.25%' 180 | } }, 181 | wp.element.createElement( 182 | 'div', 183 | { className: 'inner' }, 184 | carouselImages.length ? carouselImages.map((el, index) => { 185 | return wp.element.createElement('img', { 186 | src: el.sizes.full.url, 187 | className: !index ? 'active' : null, 188 | style: { transition: `opacity ${fadeTime}ms` } 189 | }); 190 | }) : 'No Images Yet' 191 | ) 192 | )]; 193 | }, 194 | 195 | // The save "render" function 196 | save(props) { 197 | const { 198 | className, 199 | attributes: { 200 | carouselImages, 201 | carouselTime, 202 | fadeTime 203 | } 204 | } = props; 205 | 206 | return wp.element.createElement( 207 | 'div', 208 | { 209 | className: className, 210 | 'data-carousel-time': carouselTime, 211 | style: { 212 | paddingBottom: '56.25%' 213 | } }, 214 | wp.element.createElement( 215 | 'div', 216 | { className: 'inner' }, 217 | carouselImages.length ? carouselImages.map((el, index) => { 218 | return wp.element.createElement('img', { 219 | src: el.sizes.full.url, 220 | className: !index ? 'active' : null, 221 | style: { transition: `opacity ${fadeTime}ms` } 222 | }); 223 | }) : 'No images yet...' 224 | ) 225 | ); 226 | } 227 | 228 | });``; 229 | 230 | /***/ }), 231 | 232 | /***/ 15: 233 | /***/ (function(module, exports) { 234 | 235 | // removed by extract-text-webpack-plugin 236 | 237 | /***/ }), 238 | 239 | /***/ 16: 240 | /***/ (function(module, exports) { 241 | 242 | // removed by extract-text-webpack-plugin 243 | 244 | /***/ }) 245 | 246 | /******/ }); -------------------------------------------------------------------------------- /blocks/carousel/carousel.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /blocks/carousel/carousel.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-carousel { 2 | width: 100%; 3 | position: relative; 4 | } 5 | 6 | .wp-block-guty-blocks-carousel .inner { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | height: 100%; 12 | overflow: hidden; 13 | } 14 | 15 | .wp-block-guty-blocks-carousel .inner img { 16 | position: absolute; 17 | width: 100%; 18 | top: 50%; 19 | transform: translateY(-50%); 20 | opacity: 0; 21 | } 22 | 23 | .wp-block-guty-blocks-carousel .inner img.active { 24 | opacity: 1; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /blocks/carousel/carousel.view.js: -------------------------------------------------------------------------------- 1 | /* 2 | A simple carousel script that cycles through the carousel images 3 | */ 4 | 5 | function Carousel(container) { 6 | //reference to carousel container 7 | this.container = container; 8 | 9 | // reference to carousel slides 10 | this.slides = null; 11 | 12 | // reference top setInterval 13 | this.slideTimer = null; 14 | 15 | // Pull off data attribute to set carousel timing 16 | // Defaults to 5 if something goes wrong 17 | this.carouselTime = this.container.getAttribute('data-carousel-time') * 1000 || 5000; 18 | 19 | 20 | // engage! 21 | this.init(); 22 | } 23 | 24 | var proto = Carousel.prototype; 25 | 26 | proto.init = function () { 27 | this.createChildren() 28 | .enable() 29 | .run(); 30 | } 31 | 32 | proto.createChildren = function() { 33 | this.slides = this.container.querySelectorAll(':scope img'); 34 | // this.slides[0].classList.add('active'); 35 | return this; 36 | } 37 | 38 | proto.enable = function() { 39 | this.nextSlide = this.nextSlide.bind(this); 40 | return this; 41 | } 42 | 43 | proto.run = function() { 44 | 45 | this.slideTimer = setInterval( 46 | this.nextSlide, 47 | this.carouselTime 48 | ); 49 | } 50 | 51 | proto.nextSlide = function() { 52 | var activeSlide = this.container.querySelector(':scope .active'); 53 | 54 | activeSlide.classList.remove('active'); 55 | 56 | if (activeSlide.nextSibling) { 57 | activeSlide.nextSibling.classList.add('active'); 58 | } else { 59 | this.slides[0].classList.add('active'); 60 | } 61 | } 62 | 63 | 64 | // Instantiate for every carousel on page 65 | document.addEventListener("DOMContentLoaded", function (event) { 66 | var carousels = document.querySelectorAll('.wp-block-guty-blocks-carousel'); 67 | for (var i = 0; i < carousels.length; i++) { 68 | console.log("%c Carousel present on page and running...", "color: cadetblue;"); 69 | new Carousel(carousels[i]); 70 | } 71 | }); -------------------------------------------------------------------------------- /blocks/carousel/index.php: -------------------------------------------------------------------------------- 1 | .nav { 92 | left: 0; 93 | } 94 | 95 | .lb-nav a { 96 | outline: none; 97 | background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='); 98 | } 99 | 100 | .lb-prev, .lb-next { 101 | height: 100%; 102 | cursor: pointer; 103 | display: block; 104 | } 105 | 106 | .lb-nav a.lb-prev { 107 | width: 34%; 108 | left: 0; 109 | float: left; 110 | background: url(../images/prev.png) left 48% no-repeat; 111 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 112 | opacity: 0; 113 | -webkit-transition: opacity 0.6s; 114 | -moz-transition: opacity 0.6s; 115 | -o-transition: opacity 0.6s; 116 | transition: opacity 0.6s; 117 | } 118 | 119 | .lb-nav a.lb-prev:hover { 120 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 121 | opacity: 1; 122 | } 123 | 124 | .lb-nav a.lb-next { 125 | width: 64%; 126 | right: 0; 127 | float: right; 128 | background: url(../images/next.png) right 48% no-repeat; 129 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 130 | opacity: 0; 131 | -webkit-transition: opacity 0.6s; 132 | -moz-transition: opacity 0.6s; 133 | -o-transition: opacity 0.6s; 134 | transition: opacity 0.6s; 135 | } 136 | 137 | .lb-nav a.lb-next:hover { 138 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 139 | opacity: 1; 140 | } 141 | 142 | .lb-dataContainer { 143 | margin: 0 auto; 144 | padding-top: 5px; 145 | *zoom: 1; 146 | width: 100%; 147 | border-bottom-left-radius: 4px; 148 | border-bottom-right-radius: 4px; 149 | } 150 | 151 | .lb-dataContainer:after { 152 | content: ""; 153 | display: table; 154 | clear: both; 155 | } 156 | 157 | .lb-data { 158 | padding: 0 4px; 159 | color: #ccc; 160 | } 161 | 162 | .lb-data .lb-details { 163 | width: 85%; 164 | float: left; 165 | text-align: left; 166 | line-height: 1.1em; 167 | } 168 | 169 | .lb-data .lb-caption { 170 | font-size: 13px; 171 | font-weight: bold; 172 | line-height: 1em; 173 | } 174 | 175 | .lb-data .lb-caption a { 176 | color: #4ae; 177 | } 178 | 179 | .lb-data .lb-number { 180 | display: block; 181 | clear: left; 182 | padding-bottom: 1em; 183 | font-size: 12px; 184 | color: #999999; 185 | } 186 | 187 | .lb-data .lb-close { 188 | display: block; 189 | float: right; 190 | width: 30px; 191 | height: 30px; 192 | background: url(../images/close.png) top right no-repeat; 193 | text-align: right; 194 | outline: none; 195 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); 196 | opacity: 0.7; 197 | -webkit-transition: opacity 0.2s; 198 | -moz-transition: opacity 0.2s; 199 | -o-transition: opacity 0.2s; 200 | transition: opacity 0.2s; 201 | } 202 | 203 | .lb-data .lb-close:hover { 204 | cursor: pointer; 205 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 206 | opacity: 1; 207 | } 208 | -------------------------------------------------------------------------------- /blocks/gallery/dist/css/lightbox.min.css: -------------------------------------------------------------------------------- 1 | .lb-loader,.lightbox{text-align:center;line-height:0}.lb-dataContainer:after,.lb-outerContainer:after{content:"";clear:both}html.lb-disable-scrolling{overflow:hidden;position:fixed;height:100vh;width:100vw}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:alpha(Opacity=80);opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;font-weight:400}.lightbox .lb-image{display:block;height:auto;max-width:inherit;max-height:none;border-radius:3px;border:4px solid #fff}.lightbox a img{border:none}.lb-outerContainer{position:relative;width:250px;height:250px;margin:0 auto;border-radius:4px;background-color:#fff}.lb-loader,.lb-nav{position:absolute;left:0}.lb-outerContainer:after{display:table}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../images/loading.gif) no-repeat}.lb-nav{top:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../images/prev.png) left 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../images/next.png) right 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{display:table}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-caption a{color:#4ae}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../images/close.png) top right no-repeat;text-align:right;outline:0;filter:alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:alpha(Opacity=100);opacity:1} -------------------------------------------------------------------------------- /blocks/gallery/dist/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimSchofield/guty-blocks/099aa6135d75ff0a10517f2919efcb1dbf67775c/blocks/gallery/dist/images/close.png -------------------------------------------------------------------------------- /blocks/gallery/dist/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimSchofield/guty-blocks/099aa6135d75ff0a10517f2919efcb1dbf67775c/blocks/gallery/dist/images/loading.gif -------------------------------------------------------------------------------- /blocks/gallery/dist/images/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimSchofield/guty-blocks/099aa6135d75ff0a10517f2919efcb1dbf67775c/blocks/gallery/dist/images/next.png -------------------------------------------------------------------------------- /blocks/gallery/dist/images/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimSchofield/guty-blocks/099aa6135d75ff0a10517f2919efcb1dbf67775c/blocks/gallery/dist/images/prev.png -------------------------------------------------------------------------------- /blocks/gallery/dist/js/lightbox.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lightbox v2.10.0 3 | * by Lokesh Dhakar 4 | * 5 | * More info: 6 | * http://lokeshdhakar.com/projects/lightbox2/ 7 | * 8 | * Copyright 2007, 2018 Lokesh Dhakar 9 | * Released under the MIT license 10 | * https://github.com/lokesh/lightbox2/blob/master/LICENSE 11 | * 12 | * @preserve 13 | */ 14 | !function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.lightbox=b(a.jQuery)}(this,function(a){function b(b){this.album=[],this.currentImageIndex=void 0,this.init(),this.options=a.extend({},this.constructor.defaults),this.option(b)}return b.defaults={albumLabel:"Image %1 of %2",alwaysShowNavOnTouchDevices:!1,fadeDuration:600,fitImagesInViewport:!0,imageFadeDuration:600,positionFromTop:50,resizeDuration:700,showImageNumberLabel:!0,wrapAround:!1,disableScrolling:!1,sanitizeTitle:!1},b.prototype.option=function(b){a.extend(this.options,b)},b.prototype.imageCountLabel=function(a,b){return this.options.albumLabel.replace(/%1/g,a).replace(/%2/g,b)},b.prototype.init=function(){var b=this;a(document).ready(function(){b.enable(),b.build()})},b.prototype.enable=function(){var b=this;a("body").on("click","a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]",function(c){return b.start(a(c.currentTarget)),!1})},b.prototype.build=function(){if(!(a("#lightbox").length>0)){var b=this;a('
').appendTo(a("body")),this.$lightbox=a("#lightbox"),this.$overlay=a("#lightboxOverlay"),this.$outerContainer=this.$lightbox.find(".lb-outerContainer"),this.$container=this.$lightbox.find(".lb-container"),this.$image=this.$lightbox.find(".lb-image"),this.$nav=this.$lightbox.find(".lb-nav"),this.containerPadding={top:parseInt(this.$container.css("padding-top"),10),right:parseInt(this.$container.css("padding-right"),10),bottom:parseInt(this.$container.css("padding-bottom"),10),left:parseInt(this.$container.css("padding-left"),10)},this.imageBorderWidth={top:parseInt(this.$image.css("border-top-width"),10),right:parseInt(this.$image.css("border-right-width"),10),bottom:parseInt(this.$image.css("border-bottom-width"),10),left:parseInt(this.$image.css("border-left-width"),10)},this.$overlay.hide().on("click",function(){return b.end(),!1}),this.$lightbox.hide().on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$outerContainer.on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$lightbox.find(".lb-prev").on("click",function(){return 0===b.currentImageIndex?b.changeImage(b.album.length-1):b.changeImage(b.currentImageIndex-1),!1}),this.$lightbox.find(".lb-next").on("click",function(){return b.currentImageIndex===b.album.length-1?b.changeImage(0):b.changeImage(b.currentImageIndex+1),!1}),this.$nav.on("mousedown",function(a){3===a.which&&(b.$nav.css("pointer-events","none"),b.$lightbox.one("contextmenu",function(){setTimeout(function(){this.$nav.css("pointer-events","auto")}.bind(b),0)}))}),this.$lightbox.find(".lb-loader, .lb-close").on("click",function(){return b.end(),!1})}},b.prototype.start=function(b){function c(a){d.album.push({alt:a.attr("data-alt"),link:a.attr("href"),title:a.attr("data-title")||a.attr("title")})}var d=this,e=a(window);e.on("resize",a.proxy(this.sizeOverlay,this)),a("select, object, embed").css({visibility:"hidden"}),this.sizeOverlay(),this.album=[];var f,g=0,h=b.attr("data-lightbox");if(h){f=a(b.prop("tagName")+'[data-lightbox="'+h+'"]');for(var i=0;ii||e.height>h)&&(e.width/i>e.height/h?(g=i,f=parseInt(e.height/(e.width/g),10),d.width(g),d.height(f)):(f=h,g=parseInt(e.width/(e.height/f),10),d.width(g),d.height(f)))),c.sizeContainer(d.width(),d.height())},e.src=this.album[b].link,this.currentImageIndex=b},b.prototype.sizeOverlay=function(){this.$overlay.width(a(document).width()).height(a(document).height())},b.prototype.sizeContainer=function(a,b){function c(){d.$lightbox.find(".lb-dataContainer").width(g),d.$lightbox.find(".lb-prevLink").height(h),d.$lightbox.find(".lb-nextLink").height(h),d.showImage()}var d=this,e=this.$outerContainer.outerWidth(),f=this.$outerContainer.outerHeight(),g=a+this.containerPadding.left+this.containerPadding.right+this.imageBorderWidth.left+this.imageBorderWidth.right,h=b+this.containerPadding.top+this.containerPadding.bottom+this.imageBorderWidth.top+this.imageBorderWidth.bottom;e!==g||f!==h?this.$outerContainer.animate({width:g,height:h},this.options.resizeDuration,"swing",function(){c()}):c()},b.prototype.showImage=function(){this.$lightbox.find(".lb-loader").stop(!0).hide(),this.$lightbox.find(".lb-image").fadeIn(this.options.imageFadeDuration),this.updateNav(),this.updateDetails(),this.preloadNeighboringImages(),this.enableKeyboardNav()},b.prototype.updateNav=function(){var a=!1;try{document.createEvent("TouchEvent"),a=!!this.options.alwaysShowNavOnTouchDevices}catch(a){}this.$lightbox.find(".lb-nav").show(),this.album.length>1&&(this.options.wrapAround?(a&&this.$lightbox.find(".lb-prev, .lb-next").css("opacity","1"),this.$lightbox.find(".lb-prev, .lb-next").show()):(this.currentImageIndex>0&&(this.$lightbox.find(".lb-prev").show(),a&&this.$lightbox.find(".lb-prev").css("opacity","1")),this.currentImageIndex1&&this.options.showImageNumberLabel){var d=this.imageCountLabel(this.currentImageIndex+1,this.album.length);this.$lightbox.find(".lb-number").text(d).fadeIn("fast")}else this.$lightbox.find(".lb-number").hide();this.$outerContainer.removeClass("animating"),this.$lightbox.find(".lb-dataContainer").fadeIn(this.options.resizeDuration,function(){return b.sizeOverlay()})},b.prototype.preloadNeighboringImages=function(){if(this.album.length>this.currentImageIndex+1){(new Image).src=this.album[this.currentImageIndex+1].link}if(this.currentImageIndex>0){(new Image).src=this.album[this.currentImageIndex-1].link}},b.prototype.enableKeyboardNav=function(){a(document).on("keyup.keyboard",a.proxy(this.keyboardAction,this))},b.prototype.disableKeyboardNav=function(){a(document).off(".keyboard")},b.prototype.keyboardAction=function(a){var b=a.keyCode,c=String.fromCharCode(b).toLowerCase();27===b||c.match(/x|o|c/)?this.end():"p"===c||37===b?0!==this.currentImageIndex?this.changeImage(this.currentImageIndex-1):this.options.wrapAround&&this.album.length>1&&this.changeImage(this.album.length-1):"n"!==c&&39!==b||(this.currentImageIndex!==this.album.length-1?this.changeImage(this.currentImageIndex+1):this.options.wrapAround&&this.album.length>1&&this.changeImage(0))},b.prototype.end=function(){this.disableKeyboardNav(),a(window).off("resize",this.sizeOverlay),this.$lightbox.fadeOut(this.options.fadeDuration),this.$overlay.fadeOut(this.options.fadeDuration),a("select, object, embed").css({visibility:"visible"}),this.options.disableScrolling&&a("html").removeClass("lb-disable-scrolling")},new b}); 15 | //# sourceMappingURL=lightbox.min.map -------------------------------------------------------------------------------- /blocks/gallery/dist/js/lightbox.min.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../src/js/lightbox.js"],"names":["root","factory","define","amd","exports","module","require","lightbox","jQuery","this","$","Lightbox","options","album","currentImageIndex","init","extend","constructor","defaults","option","albumLabel","alwaysShowNavOnTouchDevices","fadeDuration","fitImagesInViewport","imageFadeDuration","positionFromTop","resizeDuration","showImageNumberLabel","wrapAround","disableScrolling","sanitizeTitle","prototype","imageCountLabel","currentImageNum","totalImages","replace","self","document","ready","enable","build","on","event","start","currentTarget","length","appendTo","$lightbox","$overlay","$outerContainer","find","$container","$image","$nav","containerPadding","top","parseInt","css","right","bottom","left","imageBorderWidth","hide","end","target","attr","changeImage","which","one","setTimeout","bind","$link","addToAlbum","push","alt","link","title","$window","window","proxy","sizeOverlay","visibility","$links","imageNumber","dataLightboxValue","prop","i","j","scrollTop","scrollLeft","fadeIn","addClass","disableKeyboardNav","preloader","Image","onload","imageHeight","imageWidth","maxImageHeight","maxImageWidth","windowHeight","windowWidth","src","width","height","maxWidth","maxHeight","sizeContainer","postResize","newWidth","newHeight","showImage","oldWidth","outerWidth","oldHeight","outerHeight","animate","stop","updateNav","updateDetails","preloadNeighboringImages","enableKeyboardNav","alwaysShowNav","createEvent","e","show","$caption","text","html","undefined","open","location","href","labelText","removeClass","keyboardAction","off","keycode","keyCode","key","String","fromCharCode","toLowerCase","match","fadeOut"],"mappings":";;;;;;;;;;;;;CAeC,SAAUA,EAAMC,GACS,kBAAXC,SAAyBA,OAAOC,IAEvCD,QAAQ,UAAWD,GACO,gBAAZG,SAIdC,OAAOD,QAAUH,EAAQK,QAAQ,WAGjCN,EAAKO,SAAWN,EAAQD,EAAKQ,SAEnCC,KAAM,SAAUC,GAEhB,QAASC,GAASC,GAChBH,KAAKI,SACLJ,KAAKK,sBAAoB,GACzBL,KAAKM,OAGLN,KAAKG,QAAUF,EAAEM,UAAWP,KAAKQ,YAAYC,UAC7CT,KAAKU,OAAOP,GAged,MA3dAD,GAASO,UACPE,WAAY,iBACZC,6BAA6B,EAC7BC,aAAc,IACdC,qBAAqB,EACrBC,kBAAmB,IAGnBC,gBAAiB,GACjBC,eAAgB,IAChBC,sBAAsB,EACtBC,YAAY,EACZC,kBAAkB,EASlBC,eAAe,GAGjBnB,EAASoB,UAAUZ,OAAS,SAASP,GACnCF,EAAEM,OAAOP,KAAKG,QAASA,IAGzBD,EAASoB,UAAUC,gBAAkB,SAASC,EAAiBC,GAC7D,MAAOzB,MAAKG,QAAQQ,WAAWe,QAAQ,MAAOF,GAAiBE,QAAQ,MAAOD,IAGhFvB,EAASoB,UAAUhB,KAAO,WACxB,GAAIqB,GAAO3B,IAEXC,GAAE2B,UAAUC,MAAM,WAChBF,EAAKG,SACLH,EAAKI,WAMT7B,EAASoB,UAAUQ,OAAS,WAC1B,GAAIH,GAAO3B,IACXC,GAAE,QAAQ+B,GAAG,QAAS,+EAAgF,SAASC,GAE7G,MADAN,GAAKO,MAAMjC,EAAEgC,EAAME,iBACZ,KAMXjC,EAASoB,UAAUS,MAAQ,WACzB,KAAI9B,EAAE,aAAamC,OAAS,GAA5B,CAIA,GAAIT,GAAO3B,IACXC,GAAE,qoBAAqoBoC,SAASpC,EAAE,SAGlpBD,KAAKsC,UAAkBrC,EAAE,aACzBD,KAAKuC,SAAkBtC,EAAE,oBACzBD,KAAKwC,gBAAkBxC,KAAKsC,UAAUG,KAAK,sBAC3CzC,KAAK0C,WAAkB1C,KAAKsC,UAAUG,KAAK,iBAC3CzC,KAAK2C,OAAkB3C,KAAKsC,UAAUG,KAAK,aAC3CzC,KAAK4C,KAAkB5C,KAAKsC,UAAUG,KAAK,WAG3CzC,KAAK6C,kBACHC,IAAKC,SAAS/C,KAAK0C,WAAWM,IAAI,eAAgB,IAClDC,MAAOF,SAAS/C,KAAK0C,WAAWM,IAAI,iBAAkB,IACtDE,OAAQH,SAAS/C,KAAK0C,WAAWM,IAAI,kBAAmB,IACxDG,KAAMJ,SAAS/C,KAAK0C,WAAWM,IAAI,gBAAiB,KAGtDhD,KAAKoD,kBACHN,IAAKC,SAAS/C,KAAK2C,OAAOK,IAAI,oBAAqB,IACnDC,MAAOF,SAAS/C,KAAK2C,OAAOK,IAAI,sBAAuB,IACvDE,OAAQH,SAAS/C,KAAK2C,OAAOK,IAAI,uBAAwB,IACzDG,KAAMJ,SAAS/C,KAAK2C,OAAOK,IAAI,qBAAsB,KAIvDhD,KAAKuC,SAASc,OAAOrB,GAAG,QAAS,WAE/B,MADAL,GAAK2B,OACE,IAGTtD,KAAKsC,UAAUe,OAAOrB,GAAG,QAAS,SAASC,GAIzC,MAHmC,aAA/BhC,EAAEgC,EAAMsB,QAAQC,KAAK,OACvB7B,EAAK2B,OAEA,IAGTtD,KAAKwC,gBAAgBR,GAAG,QAAS,SAASC,GAIxC,MAHmC,aAA/BhC,EAAEgC,EAAMsB,QAAQC,KAAK,OACvB7B,EAAK2B,OAEA,IAGTtD,KAAKsC,UAAUG,KAAK,YAAYT,GAAG,QAAS,WAM1C,MAL+B,KAA3BL,EAAKtB,kBACPsB,EAAK8B,YAAY9B,EAAKvB,MAAMgC,OAAS,GAErCT,EAAK8B,YAAY9B,EAAKtB,kBAAoB,IAErC,IAGTL,KAAKsC,UAAUG,KAAK,YAAYT,GAAG,QAAS,WAM1C,MALIL,GAAKtB,oBAAsBsB,EAAKvB,MAAMgC,OAAS,EACjDT,EAAK8B,YAAY,GAEjB9B,EAAK8B,YAAY9B,EAAKtB,kBAAoB,IAErC,IAgBTL,KAAK4C,KAAKZ,GAAG,YAAa,SAASC,GACb,IAAhBA,EAAMyB,QACR/B,EAAKiB,KAAKI,IAAI,iBAAkB,QAEhCrB,EAAKW,UAAUqB,IAAI,cAAe,WAChCC,WAAW,WACP5D,KAAK4C,KAAKI,IAAI,iBAAkB,SAClCa,KAAKlC,GAAO,QAMpB3B,KAAKsC,UAAUG,KAAK,yBAAyBT,GAAG,QAAS,WAEvD,MADAL,GAAK2B,OACE,MAKXpD,EAASoB,UAAUY,MAAQ,SAAS4B,GAelC,QAASC,GAAWD,GAClBnC,EAAKvB,MAAM4D,MACTC,IAAKH,EAAMN,KAAK,YAChBU,KAAMJ,EAAMN,KAAK,QACjBW,MAAOL,EAAMN,KAAK,eAAiBM,EAAMN,KAAK,WAlBlD,GAAI7B,GAAU3B,KACVoE,EAAUnE,EAAEoE,OAEhBD,GAAQpC,GAAG,SAAU/B,EAAEqE,MAAMtE,KAAKuE,YAAavE,OAE/CC,EAAE,yBAAyB+C,KACzBwB,WAAY,WAGdxE,KAAKuE,cAELvE,KAAKI,QACL,IAYIqE,GAZAC,EAAc,EAWdC,EAAoBb,EAAMN,KAAK,gBAGnC,IAAImB,EAAmB,CACrBF,EAASxE,EAAE6D,EAAMc,KAAK,WAAa,mBAAqBD,EAAoB,KAC5E,KAAK,GAAIE,GAAI,EAAGA,EAAIJ,EAAOrC,OAAQyC,IAAMA,EACvCd,EAAW9D,EAAEwE,EAAOI,KAChBJ,EAAOI,KAAOf,EAAM,KACtBY,EAAcG,OAIlB,IAA0B,aAAtBf,EAAMN,KAAK,OAEbO,EAAWD,OACN,CAELW,EAASxE,EAAE6D,EAAMc,KAAK,WAAa,SAAWd,EAAMN,KAAK,OAAS,KAClE,KAAK,GAAIsB,GAAI,EAAGA,EAAIL,EAAOrC,OAAQ0C,IAAMA,EACvCf,EAAW9D,EAAEwE,EAAOK,KAChBL,EAAOK,KAAOhB,EAAM,KACtBY,EAAcI,GAOtB,GAAIhC,GAAOsB,EAAQW,YAAc/E,KAAKG,QAAQa,gBAC1CmC,EAAOiB,EAAQY,YACnBhF,MAAKsC,UAAUU,KACbF,IAAKA,EAAM,KACXK,KAAMA,EAAO,OACZ8B,OAAOjF,KAAKG,QAAQU,cAGnBb,KAAKG,QAAQiB,kBACfnB,EAAE,QAAQiF,SAAS,wBAGrBlF,KAAKyD,YAAYiB,IAInBxE,EAASoB,UAAUmC,YAAc,SAASiB,GACxC,GAAI/C,GAAO3B,IAEXA,MAAKmF,oBACL,IAAIxC,GAAS3C,KAAKsC,UAAUG,KAAK,YAEjCzC,MAAKuC,SAAS0C,OAAOjF,KAAKG,QAAQU,cAElCZ,EAAE,cAAcgF,OAAO,QACvBjF,KAAKsC,UAAUG,KAAK,uFAAuFY,OAE3GrD,KAAKwC,gBAAgB0C,SAAS,YAG9B,IAAIE,GAAY,GAAIC,MACpBD,GAAUE,OAAS,WACjB,GACIC,GACAC,EACAC,EACAC,EACAC,EACAC,CAEJjD,GAAOa,MACLS,IAAOtC,EAAKvB,MAAMsE,GAAaT,IAC/B4B,IAAOlE,EAAKvB,MAAMsE,GAAaR,OAGpBjE,EAAEmF,GAEfzC,EAAOmD,MAAMV,EAAUU,OACvBnD,EAAOoD,OAAOX,EAAUW,QAEpBpE,EAAKxB,QAAQW,sBAIf8E,EAAiB3F,EAAEoE,QAAQyB,QAC3BH,EAAiB1F,EAAEoE,QAAQ0B,SAC3BL,EAAiBE,EAAcjE,EAAKkB,iBAAiBM,KAAOxB,EAAKkB,iBAAiBI,MAAQtB,EAAKyB,iBAAiBD,KAAOxB,EAAKyB,iBAAiBH,MAAQ,GACrJwC,EAAiBE,EAAehE,EAAKkB,iBAAiBC,IAAMnB,EAAKkB,iBAAiBK,OAASvB,EAAKyB,iBAAiBN,IAAMnB,EAAKyB,iBAAiBF,OAAS,IAGlJvB,EAAKxB,QAAQ6F,UAAYrE,EAAKxB,QAAQ6F,SAAWN,IACnDA,EAAgB/D,EAAKxB,QAAQ6F,UAE3BrE,EAAKxB,QAAQ8F,WAAatE,EAAKxB,QAAQ8F,UAAYP,IACrDD,EAAiB9D,EAAKxB,QAAQ8F,YAK3Bb,EAAUU,MAAQJ,GAAmBN,EAAUW,OAASN,KACtDL,EAAUU,MAAQJ,EAAkBN,EAAUW,OAASN,GAC1DD,EAAcE,EACdH,EAAcxC,SAASqC,EAAUW,QAAUX,EAAUU,MAAQN,GAAa,IAC1E7C,EAAOmD,MAAMN,GACb7C,EAAOoD,OAAOR,KAEdA,EAAcE,EACdD,EAAazC,SAASqC,EAAUU,OAASV,EAAUW,OAASR,GAAc,IAC1E5C,EAAOmD,MAAMN,GACb7C,EAAOoD,OAAOR,MAIpB5D,EAAKuE,cAAcvD,EAAOmD,QAASnD,EAAOoD,WAG5CX,EAAUS,IAAe7F,KAAKI,MAAMsE,GAAaR,KACjDlE,KAAKK,kBAAoBqE,GAI3BxE,EAASoB,UAAUiD,YAAc,WAC/BvE,KAAKuC,SACFuD,MAAM7F,EAAE2B,UAAUkE,SAClBC,OAAO9F,EAAE2B,UAAUmE,WAIxB7F,EAASoB,UAAU4E,cAAgB,SAASV,EAAYD,GAQtD,QAASY,KACPxE,EAAKW,UAAUG,KAAK,qBAAqBqD,MAAMM,GAC/CzE,EAAKW,UAAUG,KAAK,gBAAgBsD,OAAOM,GAC3C1E,EAAKW,UAAUG,KAAK,gBAAgBsD,OAAOM,GAC3C1E,EAAK2E,YAXP,GAAI3E,GAAO3B,KAEPuG,EAAYvG,KAAKwC,gBAAgBgE,aACjCC,EAAYzG,KAAKwC,gBAAgBkE,cACjCN,EAAYZ,EAAaxF,KAAK6C,iBAAiBM,KAAOnD,KAAK6C,iBAAiBI,MAAQjD,KAAKoD,iBAAiBD,KAAOnD,KAAKoD,iBAAiBH,MACvIoD,EAAYd,EAAcvF,KAAK6C,iBAAiBC,IAAM9C,KAAK6C,iBAAiBK,OAASlD,KAAKoD,iBAAiBN,IAAM9C,KAAKoD,iBAAiBF,MASvIqD,KAAaH,GAAYK,IAAcJ,EACzCrG,KAAKwC,gBAAgBmE,SACnBb,MAAOM,EACPL,OAAQM,GACPrG,KAAKG,QAAQc,eAAgB,QAAS,WACvCkF,MAGFA,KAKJjG,EAASoB,UAAUgF,UAAY,WAC7BtG,KAAKsC,UAAUG,KAAK,cAAcmE,MAAK,GAAMvD,OAC7CrD,KAAKsC,UAAUG,KAAK,aAAawC,OAAOjF,KAAKG,QAAQY,mBAErDf,KAAK6G,YACL7G,KAAK8G,gBACL9G,KAAK+G,2BACL/G,KAAKgH,qBAIP9G,EAASoB,UAAUuF,UAAY,WAI7B,GAAII,IAAgB,CACpB,KACErF,SAASsF,YAAY,cACrBD,IAAiBjH,KAAKG,QAAmC,4BACzD,MAAOgH,IAETnH,KAAKsC,UAAUG,KAAK,WAAW2E,OAE3BpH,KAAKI,MAAMgC,OAAS,IAClBpC,KAAKG,QAAQgB,YACX8F,GACFjH,KAAKsC,UAAUG,KAAK,sBAAsBO,IAAI,UAAW,KAE3DhD,KAAKsC,UAAUG,KAAK,sBAAsB2E,SAEtCpH,KAAKK,kBAAoB,IAC3BL,KAAKsC,UAAUG,KAAK,YAAY2E,OAC5BH,GACFjH,KAAKsC,UAAUG,KAAK,YAAYO,IAAI,UAAW,MAG/ChD,KAAKK,kBAAoBL,KAAKI,MAAMgC,OAAS,IAC/CpC,KAAKsC,UAAUG,KAAK,YAAY2E,OAC5BH,GACFjH,KAAKsC,UAAUG,KAAK,YAAYO,IAAI,UAAW,SAQzD9C,EAASoB,UAAUwF,cAAgB,WACjC,GAAInF,GAAO3B,IAIX,QAAwD,KAA7CA,KAAKI,MAAMJ,KAAKK,mBAAmB8D,OACC,KAA7CnE,KAAKI,MAAMJ,KAAKK,mBAAmB8D,MAAc,CACjD,GAAIkD,GAAWrH,KAAKsC,UAAUG,KAAK,cAC/BzC,MAAKG,QAAQkB,cACfgG,EAASC,KAAKtH,KAAKI,MAAMJ,KAAKK,mBAAmB8D,OAEjDkD,EAASE,KAAKvH,KAAKI,MAAMJ,KAAKK,mBAAmB8D,OAEnDkD,EAASpC,OAAO,QACbxC,KAAK,KAAKT,GAAG,QAAS,SAASC,OACCuF,KAA3BvH,EAAED,MAAMwD,KAAK,UACfa,OAAOoD,KAAKxH,EAAED,MAAMwD,KAAK,QAASvD,EAAED,MAAMwD,KAAK,WAE/CkE,SAASC,KAAO1H,EAAED,MAAMwD,KAAK,UAKrC,GAAIxD,KAAKI,MAAMgC,OAAS,GAAKpC,KAAKG,QAAQe,qBAAsB,CAC9D,GAAI0G,GAAY5H,KAAKuB,gBAAgBvB,KAAKK,kBAAoB,EAAGL,KAAKI,MAAMgC,OAC5EpC,MAAKsC,UAAUG,KAAK,cAAc6E,KAAKM,GAAW3C,OAAO,YAEzDjF,MAAKsC,UAAUG,KAAK,cAAcY,MAGpCrD,MAAKwC,gBAAgBqF,YAAY,aAEjC7H,KAAKsC,UAAUG,KAAK,qBAAqBwC,OAAOjF,KAAKG,QAAQc,eAAgB,WAC3E,MAAOU,GAAK4C,iBAKhBrE,EAASoB,UAAUyF,yBAA2B,WAC5C,GAAI/G,KAAKI,MAAMgC,OAASpC,KAAKK,kBAAoB,EAAG,EAChC,GAAIgF,QACVQ,IAAM7F,KAAKI,MAAMJ,KAAKK,kBAAoB,GAAG6D,KAE3D,GAAIlE,KAAKK,kBAAoB,EAAG,EACZ,GAAIgF,QACVQ,IAAM7F,KAAKI,MAAMJ,KAAKK,kBAAoB,GAAG6D,OAI7DhE,EAASoB,UAAU0F,kBAAoB,WACrC/G,EAAE2B,UAAUI,GAAG,iBAAkB/B,EAAEqE,MAAMtE,KAAK8H,eAAgB9H,QAGhEE,EAASoB,UAAU6D,mBAAqB,WACtClF,EAAE2B,UAAUmG,IAAI,cAGlB7H,EAASoB,UAAUwG,eAAiB,SAAS7F,GAC3C,GAII+F,GAAU/F,EAAMgG,QAChBC,EAAUC,OAAOC,aAAaJ,GAASK,aALlB,MAMrBL,GAA2BE,EAAII,MAAM,SACvCtI,KAAKsD,MACY,MAAR4E,GAPc,KAOCF,EACO,IAA3BhI,KAAKK,kBACPL,KAAKyD,YAAYzD,KAAKK,kBAAoB,GACjCL,KAAKG,QAAQgB,YAAcnB,KAAKI,MAAMgC,OAAS,GACxDpC,KAAKyD,YAAYzD,KAAKI,MAAMgC,OAAS,GAEtB,MAAR8F,GAZc,KAYCF,IACpBhI,KAAKK,oBAAsBL,KAAKI,MAAMgC,OAAS,EACjDpC,KAAKyD,YAAYzD,KAAKK,kBAAoB,GACjCL,KAAKG,QAAQgB,YAAcnB,KAAKI,MAAMgC,OAAS,GACxDpC,KAAKyD,YAAY,KAMvBvD,EAASoB,UAAUgC,IAAM,WACvBtD,KAAKmF,qBACLlF,EAAEoE,QAAQ0D,IAAI,SAAU/H,KAAKuE,aAC7BvE,KAAKsC,UAAUiG,QAAQvI,KAAKG,QAAQU,cACpCb,KAAKuC,SAASgG,QAAQvI,KAAKG,QAAQU,cACnCZ,EAAE,yBAAyB+C,KACzBwB,WAAY,YAEVxE,KAAKG,QAAQiB,kBACfnB,EAAE,QAAQ4H,YAAY,yBAInB,GAAI3H","file":"lightbox.min.js"} -------------------------------------------------------------------------------- /blocks/gallery/gallery.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | 7 | .galleryInspector > *{ 8 | display: block; 9 | margin: 15px; 10 | } 11 | -------------------------------------------------------------------------------- /blocks/gallery/gallery.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-gallery h1 { 2 | margin-bottom: 16px; 3 | } 4 | 5 | .wp-block-guty-blocks-gallery * { 6 | box-sizing: border-box; 7 | } 8 | 9 | :root { 10 | --PADDING: 32px; 11 | } 12 | 13 | @media (max-width: 450px) { 14 | :root { 15 | --PADDING: 16px; 16 | } 17 | } 18 | 19 | .wp-block-guty-blocks-gallery .galleryContainer { 20 | position: relative; 21 | display: flex; 22 | flex-direction: row; 23 | flex-wrap: wrap; 24 | margin: calc(-1 * var(--PADDING)) calc(-.5 * var(--PADDING)) 0 calc(-.5 * var(--PADDING)); 25 | } 26 | 27 | .wp-block-guty-blocks-gallery .galleryContainer>* { 28 | padding: var(--PADDING) calc(0.5 * var(--PADDING)) 0 calc(0.5 * var(--PADDING)); 29 | } 30 | 31 | .wp-block-guty-blocks-gallery .galleryContainer>* { 32 | width: 100%; 33 | } 34 | 35 | .wp-block-guty-blocks-gallery .galleryContainer_2up>* { 36 | width: 50%; 37 | } 38 | 39 | .wp-block-guty-blocks-gallery .galleryContainer_3up>* { 40 | width: 33.3%; 41 | } 42 | 43 | .wp-block-guty-blocks-gallery .galleryContainer_4up>* { 44 | width: 25%; 45 | } 46 | 47 | @media (max-width: 450px) { 48 | .wp-block-guty-blocks-gallery .galleryContainer>*, 49 | .wp-block-guty-blocks-gallery .galleryContainer_2up>*, 50 | .wp-block-guty-blocks-gallery .galleryContainer_3up>*, 51 | .wp-block-guty-blocks-gallery .galleryContainer_4up>* { 52 | width: 100%; 53 | } 54 | } 55 | 56 | .wp-block-guty-blocks-gallery .galleryContainer_alignLeft { 57 | justify-content: flex-start; 58 | } 59 | 60 | .wp-block-guty-blocks-gallery .galleryContainer_alignCenter { 61 | justify-content: center; 62 | } 63 | 64 | .wp-block-guty-blocks-gallery .galleryContainer_alignRight { 65 | justify-content: flex-end; 66 | } 67 | 68 | .imageWrapper { 69 | position: relative; 70 | } 71 | 72 | .imageWrapper-buttons { 73 | position: absolute; 74 | display: flex; 75 | padding: 0 30px; 76 | justify-content: space-between; 77 | width: 100%; 78 | top: calc(50% + 16px); 79 | left: 0; 80 | transform: translateY(-50%); 81 | } 82 | 83 | .imageWrapper-buttons button { 84 | opacity: 0; 85 | transition: 200ms; 86 | } 87 | 88 | .imageWrapper:hover button { 89 | opacity: 1; 90 | cursor: pointer; 91 | border: none; 92 | background: cadetblue; 93 | color: white; 94 | } 95 | 96 | .imageWrapper:hover button:hover { 97 | color: cadetblue; 98 | background: white; 99 | } 100 | 101 | .galleryContainer .galleryContainer-addPhoto { 102 | position: absolute; 103 | bottom: 0; 104 | right: 0; 105 | text-align: right; 106 | } 107 | 108 | .galleryContainer .galleryContainer-addPhoto button { 109 | cursor: pointer; 110 | border: none; 111 | background: cadetblue; 112 | color: white; 113 | transition: 200ms; 114 | } 115 | 116 | .galleryContainer .galleryContainer-addPhoto button:hover { 117 | background: rgba(95, 158, 160, .8); 118 | color: white; 119 | } -------------------------------------------------------------------------------- /blocks/gallery/index.php: -------------------------------------------------------------------------------- 1 | wp.element.createElement( 132 | 'button', 133 | { onClick: open }, 134 | 'Open Media Library' 135 | ) 136 | }); 137 | } 138 | 139 | // Actual elements being rendered 140 | return [isSelected && wp.element.createElement( 141 | InspectorControls, 142 | { key: 'controls' }, 143 | wp.element.createElement( 144 | 'p', 145 | null, 146 | 'This is where some style options can be presented for your block!' 147 | ) 148 | ), wp.element.createElement( 149 | 'div', 150 | { className: props.className }, 151 | wp.element.createElement( 152 | 'div', 153 | { 'class': 'left' }, 154 | imageSide 155 | ), 156 | wp.element.createElement( 157 | 'div', 158 | { 'class': 'right' }, 159 | wp.element.createElement(RichText, { 160 | tagName: 'p', 161 | onChange: onChangeContent, 162 | value: content 163 | }) 164 | ) 165 | )]; 166 | }, 167 | 168 | // The save "render" function 169 | save(props) { 170 | return wp.element.createElement( 171 | 'div', 172 | { className: props.className }, 173 | wp.element.createElement( 174 | 'div', 175 | { 'class': 'left' }, 176 | wp.element.createElement('img', { src: props.attributes.imageUrl, alt: '' }) 177 | ), 178 | wp.element.createElement( 179 | 'div', 180 | { 'class': 'right' }, 181 | wp.element.createElement( 182 | 'p', 183 | null, 184 | props.attributes.content 185 | ) 186 | ) 187 | ); 188 | } 189 | 190 | }); 191 | 192 | /***/ }), 193 | 194 | /***/ 28: 195 | /***/ (function(module, exports) { 196 | 197 | // removed by extract-text-webpack-plugin 198 | 199 | /***/ }), 200 | 201 | /***/ 29: 202 | /***/ (function(module, exports) { 203 | 204 | // removed by extract-text-webpack-plugin 205 | 206 | /***/ }) 207 | 208 | /******/ }); -------------------------------------------------------------------------------- /blocks/media-block/media-block.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-media-block { 2 | position: relative; 3 | background: cadetblue; 4 | padding:2em; 5 | display: flex; 6 | flex-direction: row; 7 | } 8 | .wp-block-guty-blocks-media-block:before { 9 | content:'Editing mode'; 10 | position: absolute; 11 | top: 4px; 12 | left: 4px; 13 | color: red; 14 | } 15 | 16 | .wp-block-guty-blocks-media-block .left { 17 | flex: 33%; 18 | padding-right: 16px; 19 | } 20 | 21 | .wp-block-guty-blocks-media-block .right { 22 | flex: 66%; 23 | } 24 | 25 | .wp-block-guty-blocks-media-block p { 26 | font-size: 24px; 27 | color: white; 28 | } -------------------------------------------------------------------------------- /blocks/media-block/media-block.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-media-block { 2 | position: relative; 3 | background: cadetblue; 4 | padding:2em; 5 | display: flex; 6 | flex-direction: row; 7 | } 8 | .wp-block-guty-blocks-media-block:before { 9 | content:'View Mode'; 10 | position: absolute; 11 | top: 4px; 12 | left: 4px; 13 | color: blue; 14 | } 15 | 16 | .wp-block-guty-blocks-media-block .left { 17 | flex: 33%; 18 | padding-right: 16px; 19 | } 20 | 21 | .wp-block-guty-blocks-media-block .right { 22 | flex: 66%; 23 | } 24 | 25 | .wp-block-guty-blocks-media-block p { 26 | font-size: 24px; 27 | color: white; 28 | } -------------------------------------------------------------------------------- /blocks/media-block/media-block.view.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 17); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ({ 67 | 68 | /***/ 17: 69 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 70 | 71 | "use strict"; 72 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 73 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__media_block_editor_css__ = __webpack_require__(18); 74 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__media_block_editor_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__media_block_editor_css__); 75 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__media_block_view_css__ = __webpack_require__(19); 76 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__media_block_view_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__media_block_view_css__); 77 | 78 | 79 | 80 | const { 81 | registerBlockType, 82 | RichText, // Editable field 83 | InspectorControls, // allows us to add controls on the sidebar 84 | MediaUpload // allows us to upload images 85 | } = wp.blocks; 86 | 87 | registerBlockType('guty-blocks/media-block', { 88 | title: 'Media Item Block', 89 | icon: 'smiley', 90 | category: 'common', 91 | 92 | attributes: { // Somewhat like setting initial state in a react app 93 | content: { 94 | type: 'array', 95 | source: 'children', 96 | selector: 'p', 97 | default: 'Editable block content...' 98 | }, 99 | imageUrl: { 100 | type: 'string', 101 | default: null 102 | } 103 | }, 104 | 105 | // The editor "render" function 106 | edit(props) { 107 | 108 | let { content, imageUrl, focus, isSelected } = props.attributes; 109 | 110 | function onChangeContent(updatedContent) { 111 | props.setAttributes({ content: updatedContent }); 112 | } 113 | 114 | function setImage(image) { 115 | console.log(image); 116 | props.setAttributes({ imageUrl: image.url }); 117 | } 118 | 119 | // If an image isn't selected show the upload button 120 | // otherwise, show the image 121 | let imageSide = null; 122 | if (imageUrl) { 123 | imageSide = wp.element.createElement('img', { src: imageUrl, alt: '' }); 124 | } else { 125 | imageSide = wp.element.createElement(MediaUpload, { 126 | type: 'image', 127 | onSelect: setImage, 128 | render: ({ open }) => wp.element.createElement( 129 | 'button', 130 | { onClick: open }, 131 | 'Open Media Library' 132 | ) 133 | }); 134 | } 135 | 136 | // Actual elements being rendered 137 | return [isSelected && wp.element.createElement( 138 | InspectorControls, 139 | { key: 'controls' }, 140 | wp.element.createElement( 141 | 'p', 142 | null, 143 | 'This is where some style options can be presented for your block!' 144 | ) 145 | ), wp.element.createElement( 146 | 'div', 147 | { className: props.className }, 148 | wp.element.createElement( 149 | 'div', 150 | { 'class': 'left' }, 151 | imageSide 152 | ), 153 | wp.element.createElement( 154 | 'div', 155 | { 'class': 'right' }, 156 | wp.element.createElement(RichText, { 157 | tagName: 'p', 158 | onChange: onChangeContent, 159 | value: content 160 | }) 161 | ) 162 | )]; 163 | }, 164 | 165 | // The save "render" function 166 | save(props) { 167 | return wp.element.createElement( 168 | 'div', 169 | { className: props.className }, 170 | wp.element.createElement( 171 | 'div', 172 | { 'class': 'left' }, 173 | wp.element.createElement('img', { src: props.attributes.imageUrl, alt: '' }) 174 | ), 175 | wp.element.createElement( 176 | 'div', 177 | { 'class': 'right' }, 178 | wp.element.createElement( 179 | 'p', 180 | null, 181 | props.attributes.content 182 | ) 183 | ) 184 | ); 185 | } 186 | 187 | }); 188 | 189 | /***/ }), 190 | 191 | /***/ 18: 192 | /***/ (function(module, exports) { 193 | 194 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-media-block {\n| position: relative;\n| background: cadetblue;"); 195 | 196 | /***/ }), 197 | 198 | /***/ 19: 199 | /***/ (function(module, exports) { 200 | 201 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-media-block {\n| position: relative;\n| background: cadetblue;"); 202 | 203 | /***/ }) 204 | 205 | /******/ }); -------------------------------------------------------------------------------- /blocks/prism-code/index.php: -------------------------------------------------------------------------------- 1 | 'guty-blocks/prism-code-editor-script', 27 | 'editor_style' => 'guty-blocks/prism-code-editor-style', 28 | 'style' => 'guty-blocks/prism-code-editor-style', 29 | ) ); 30 | } 31 | add_action( 'init', 'gutenberg_prism_code_block' ); -------------------------------------------------------------------------------- /blocks/prism-code/prism-code.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /blocks/prism-code/prism-code.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-prism-code.wp-block-guty-blocks-prism-code textarea { 2 | box-shadow: none; 3 | font-family: inherit; 4 | font-size: inherit; 5 | color: inherit; 6 | line-height: inherit; 7 | border: none; 8 | padding: 0; 9 | margin: 0; 10 | width: 100%; 11 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 12 | background: transparent; 13 | } 14 | 15 | code { 16 | padding: 0; 17 | margin: 0; 18 | font-size: inherit; 19 | } 20 | 21 | 22 | /* Prism styles */ 23 | /* PrismJS 1.12.2 24 | http://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+jsx */ 25 | /** 26 | * prism.js default theme for JavaScript, CSS and HTML 27 | * Based on dabblet (http://dabblet.com) 28 | * @author Lea Verou 29 | */ 30 | 31 | code[class*="language-"], 32 | pre[class*="language-"] { 33 | color: black; 34 | background: none; 35 | text-shadow: 0 1px white; 36 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 37 | text-align: left; 38 | white-space: pre; 39 | word-spacing: normal; 40 | word-break: normal; 41 | word-wrap: normal; 42 | line-height: 1.5; 43 | 44 | -moz-tab-size: 4; 45 | -o-tab-size: 4; 46 | tab-size: 4; 47 | 48 | -webkit-hyphens: none; 49 | -moz-hyphens: none; 50 | -ms-hyphens: none; 51 | hyphens: none; 52 | } 53 | 54 | pre[class*="language-"]::-moz-selection, 55 | pre[class*="language-"] ::-moz-selection, 56 | code[class*="language-"]::-moz-selection, 57 | code[class*="language-"] ::-moz-selection { 58 | text-shadow: none; 59 | background: #b3d4fc; 60 | } 61 | 62 | pre[class*="language-"]::selection, 63 | pre[class*="language-"] ::selection, 64 | code[class*="language-"]::selection, 65 | code[class*="language-"] ::selection { 66 | text-shadow: none; 67 | background: #b3d4fc; 68 | } 69 | 70 | @media print { 71 | 72 | code[class*="language-"], 73 | pre[class*="language-"] { 74 | text-shadow: none; 75 | } 76 | } 77 | 78 | /* Code blocks */ 79 | pre[class*="language-"] { 80 | padding: 1em; 81 | margin: .5em 0; 82 | overflow: auto; 83 | } 84 | 85 | :not(pre)>code[class*="language-"], 86 | pre[class*="language-"] { 87 | background: #f5f2f0; 88 | } 89 | 90 | /* Inline code */ 91 | :not(pre)>code[class*="language-"] { 92 | padding: .1em; 93 | border-radius: .3em; 94 | white-space: normal; 95 | } 96 | 97 | .token.comment, 98 | .token.prolog, 99 | .token.doctype, 100 | .token.cdata { 101 | color: slategray; 102 | } 103 | 104 | .token.punctuation { 105 | color: #999; 106 | } 107 | 108 | .namespace { 109 | opacity: .7; 110 | } 111 | 112 | .token.property, 113 | .token.tag, 114 | .token.boolean, 115 | .token.number, 116 | .token.constant, 117 | .token.symbol, 118 | .token.deleted { 119 | color: #905; 120 | } 121 | 122 | .token.selector, 123 | .token.attr-name, 124 | .token.string, 125 | .token.char, 126 | .token.builtin, 127 | .token.inserted { 128 | color: #690; 129 | } 130 | 131 | .token.operator, 132 | .token.entity, 133 | .token.url, 134 | .language-css .token.string, 135 | .style .token.string { 136 | color: #a67f59; 137 | background: hsla(0, 0%, 100%, .5); 138 | } 139 | 140 | .token.atrule, 141 | .token.attr-value, 142 | .token.keyword { 143 | color: #07a; 144 | } 145 | 146 | .token.function, 147 | .token.class-name { 148 | color: #DD4A68; 149 | } 150 | 151 | .token.regex, 152 | .token.important, 153 | .token.variable { 154 | color: #e90; 155 | } 156 | 157 | .token.important, 158 | .token.bold { 159 | font-weight: bold; 160 | } 161 | 162 | .token.italic { 163 | font-style: italic; 164 | } 165 | 166 | .token.entity { 167 | cursor: help; 168 | } -------------------------------------------------------------------------------- /blocks/quote/index.php: -------------------------------------------------------------------------------- 1 | props.setAttributes({ quoteContent: changes }) 122 | }), 123 | wp.element.createElement( 124 | 'span', 125 | { className: 'first-last-quotes' }, 126 | '\u201D' 127 | ) 128 | ), 129 | wp.element.createElement( 130 | 'div', 131 | { className: 'author' }, 132 | '- ', 133 | wp.element.createElement(RichText, { 134 | tagName: 'span', 135 | value: props.attributes.author, 136 | onChange: changes => props.setAttributes({ author: changes }) 137 | }) 138 | ) 139 | ); 140 | }, 141 | 142 | // The save "render" function 143 | save(props) { 144 | let { className } = props; 145 | return wp.element.createElement( 146 | 'aside', 147 | { className: "quote " + className }, 148 | wp.element.createElement( 149 | 'div', 150 | { className: 'quote-text' }, 151 | wp.element.createElement( 152 | 'span', 153 | { className: 'first-last-quotes' }, 154 | '\u201C' 155 | ), 156 | props.attributes.quoteContent, 157 | wp.element.createElement( 158 | 'span', 159 | { className: 'first-last-quotes' }, 160 | '\u201D' 161 | ) 162 | ), 163 | wp.element.createElement( 164 | 'div', 165 | { className: 'author' }, 166 | '- ', 167 | props.attributes.author 168 | ) 169 | ); 170 | } 171 | 172 | }); 173 | 174 | /***/ }), 175 | 176 | /***/ 45: 177 | /***/ (function(module, exports) { 178 | 179 | // removed by extract-text-webpack-plugin 180 | 181 | /***/ }), 182 | 183 | /***/ 46: 184 | /***/ (function(module, exports) { 185 | 186 | // removed by extract-text-webpack-plugin 187 | 188 | /***/ }) 189 | 190 | /******/ }); -------------------------------------------------------------------------------- /blocks/quote/quote.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-quote .blocks-rich-text { 2 | display: inline; 3 | } 4 | 5 | .wp-block-guty-blocks-quote .author .blocks-rich-text { 6 | font-variant: small-caps; 7 | color: #646464; 8 | } 9 | -------------------------------------------------------------------------------- /blocks/quote/quote.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-quote { 2 | width: 80%; 3 | margin: 50px auto 25px; 4 | padding: 8px; 5 | border: 1px solid #d8d2d0; 6 | text-align: center; 7 | } 8 | 9 | .wp-block-guty-blocks-quote .quote-text { 10 | padding: 8px; 11 | font-size: 24px; 12 | } 13 | 14 | .wp-block-guty-blocks-quote .first-last-quotes { 15 | display: inline-block; 16 | font-size: 36px; 17 | line-height: 24px; 18 | } 19 | 20 | .wp-block-guty-blocks-quote .author { 21 | display: inline-block; 22 | font-variant: small-caps; 23 | color: #646464; 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /blocks/react-view/index.php: -------------------------------------------------------------------------------- 1 | { 174 | return wp.element.createElement(RenderArticleButton, { 175 | title: el.title.rendered, 176 | desc: el.excerpt.rendered, 177 | url: el.link 178 | }); 179 | }) 180 | ) 181 | )]; 182 | }, 183 | 184 | // The save "render" function 185 | save(props) { 186 | 187 | let { posts, numberUp } = props.attributes; 188 | 189 | return wp.element.createElement( 190 | 'div', 191 | { className: props.className }, 192 | wp.element.createElement( 193 | 'div', 194 | { className: `articleContainer ${numberUp}` }, 195 | posts && posts.map(el => { 196 | return wp.element.createElement(RenderArticleButton, { 197 | title: el.title.rendered, 198 | desc: el.excerpt.rendered, 199 | url: el.link 200 | }); 201 | }) 202 | ) 203 | ); 204 | } 205 | 206 | }); 207 | 208 | function RenderArticleButton(props) { 209 | return wp.element.createElement( 210 | 'div', 211 | { 'class': 'articleButton' }, 212 | wp.element.createElement('h3', { dangerouslySetInnerHTML: { __html: props.title } }), 213 | wp.element.createElement('p', { dangerouslySetInnerHTML: { __html: props.desc } }), 214 | wp.element.createElement( 215 | 'a', 216 | { href: props.url }, 217 | 'Go to article...' 218 | ) 219 | ); 220 | } 221 | 222 | /***/ }), 223 | 224 | /***/ 52: 225 | /***/ (function(module, exports) { 226 | 227 | // removed by extract-text-webpack-plugin 228 | 229 | /***/ }), 230 | 231 | /***/ 53: 232 | /***/ (function(module, exports) { 233 | 234 | // removed by extract-text-webpack-plugin 235 | 236 | /***/ }) 237 | 238 | /******/ }); -------------------------------------------------------------------------------- /blocks/recent-posts/recent-posts.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-recent-posts { 2 | } 3 | 4 | .articleContainer { 5 | display: grid; 6 | } 7 | 8 | .one_up { 9 | grid-template-columns: repeat(1, 1fr); 10 | } 11 | .two_up { 12 | grid-template-columns: repeat(2, 1fr); 13 | } 14 | .three_up { 15 | grid-template-columns: repeat(3, 1fr); 16 | } 17 | 18 | .articleContainer > * { 19 | grid-column: auto; 20 | margin: 8px; 21 | padding: 1em; 22 | border: 1px solid grey; 23 | } 24 | -------------------------------------------------------------------------------- /blocks/recent-posts/recent-posts.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-recent-posts { 2 | } 3 | 4 | .articleContainer { 5 | display: grid; 6 | } 7 | 8 | .one_up { 9 | grid-template-columns: repeat(1, 1fr); 10 | } 11 | .two_up { 12 | grid-template-columns: repeat(2, 1fr); 13 | } 14 | .three_up { 15 | grid-template-columns: repeat(3, 1fr); 16 | } 17 | 18 | .articleContainer > * { 19 | grid-column: auto; 20 | margin: 8px; 21 | padding: 1em; 22 | border: 1px solid grey; 23 | } 24 | -------------------------------------------------------------------------------- /blocks/recent-posts/recent-posts.view.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 39); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ({ 67 | 68 | /***/ 39: 69 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 70 | 71 | "use strict"; 72 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 73 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__recent_posts_editor_css__ = __webpack_require__(40); 74 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__recent_posts_editor_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__recent_posts_editor_css__); 75 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__recent_posts_view_css__ = __webpack_require__(41); 76 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__recent_posts_view_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__recent_posts_view_css__); 77 | 78 | 79 | 80 | const { 81 | registerBlockType, 82 | Editable, // Text field - will be replaced by RichText in future updates 83 | InspectorControls // allows us to add controls on the sidebar 84 | } = wp.blocks; 85 | 86 | registerBlockType('guty-blocks/recent-posts', { 87 | title: 'Recent Posts Block', 88 | icon: 'welcome-write-blog', 89 | category: 'common', 90 | 91 | attributes: { // Somewhat like setting initial state in a react app 92 | numberUp: { 93 | type: 'string', 94 | default: '1_up' 95 | }, 96 | posts: { 97 | type: 'array', 98 | default: [] 99 | } 100 | }, 101 | 102 | // The editor "render" function 103 | edit(props) { 104 | 105 | let { content, posts, numberUp } = props.attributes; 106 | 107 | function onChangeContent(updatedContent) { 108 | props.setAttributes({ content: updatedContent }); 109 | } 110 | function onChangeNumberUp(newNumberUp) { 111 | props.setAttributes({ numberUp: newNumberUp.target.value }); 112 | } 113 | 114 | async function fetchArticles() { 115 | let data = await fetch('/wp-json/wp/v2/posts/'); 116 | let posts = await data.json(); 117 | props.setAttributes({ posts }); 118 | } 119 | 120 | // Actual elements being rendered 121 | return [!!focus && wp.element.createElement( 122 | InspectorControls, 123 | { key: 'controls' }, 124 | 'Select Layout:', 125 | wp.element.createElement( 126 | 'select', 127 | { 128 | onChange: onChangeNumberUp }, 129 | wp.element.createElement( 130 | 'option', 131 | { value: 'one_up' }, 132 | '1 up blocks' 133 | ), 134 | wp.element.createElement( 135 | 'option', 136 | { value: 'two_up' }, 137 | '2 up blocks' 138 | ), 139 | wp.element.createElement( 140 | 'option', 141 | { value: 'three_up' }, 142 | '3 up blocks' 143 | ) 144 | ) 145 | ), wp.element.createElement( 146 | 'div', 147 | { className: props.className }, 148 | wp.element.createElement( 149 | 'div', 150 | { className: 'sysMessage' }, 151 | wp.element.createElement( 152 | 'button', 153 | { onClick: fetchArticles }, 154 | 'Fetch me recent articles!' 155 | ), 156 | posts.length === 0 ? wp.element.createElement( 157 | 'h1', 158 | null, 159 | 'Posts not pulled yet' 160 | ) : wp.element.createElement( 161 | 'h1', 162 | null, 163 | posts.length, 164 | ' found!' 165 | ) 166 | ), 167 | wp.element.createElement( 168 | 'div', 169 | { className: `articleContainer ${numberUp}` }, 170 | posts && posts.map(el => { 171 | return wp.element.createElement(RenderArticleButton, { 172 | title: el.title.rendered, 173 | desc: el.excerpt.rendered, 174 | url: el.link 175 | }); 176 | }) 177 | ) 178 | )]; 179 | }, 180 | 181 | // The save "render" function 182 | save(props) { 183 | 184 | let { posts, numberUp } = props.attributes; 185 | 186 | return wp.element.createElement( 187 | 'div', 188 | { className: props.className }, 189 | wp.element.createElement( 190 | 'div', 191 | { className: `articleContainer ${numberUp}` }, 192 | posts && posts.map(el => { 193 | return wp.element.createElement(RenderArticleButton, { 194 | title: el.title.rendered, 195 | desc: el.excerpt.rendered, 196 | url: el.link 197 | }); 198 | }) 199 | ) 200 | ); 201 | } 202 | 203 | }); 204 | 205 | function RenderArticleButton(props) { 206 | return wp.element.createElement( 207 | 'div', 208 | { 'class': 'articleButton' }, 209 | wp.element.createElement('h3', { dangerouslySetInnerHTML: { __html: props.title } }), 210 | wp.element.createElement('p', { dangerouslySetInnerHTML: { __html: props.desc } }), 211 | wp.element.createElement( 212 | 'a', 213 | { href: props.url }, 214 | 'Go to article...' 215 | ) 216 | ); 217 | } 218 | 219 | /***/ }), 220 | 221 | /***/ 40: 222 | /***/ (function(module, exports) { 223 | 224 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-recent-posts {\n| }\n| "); 225 | 226 | /***/ }), 227 | 228 | /***/ 41: 229 | /***/ (function(module, exports) { 230 | 231 | throw new Error("Module parse failed: Unexpected token (1:0)\nYou may need an appropriate loader to handle this file type.\n| .wp-block-guty-blocks-recent-posts {\n| }\n| "); 232 | 233 | /***/ }) 234 | 235 | /******/ }); -------------------------------------------------------------------------------- /blocks/side-by-side/index.php: -------------------------------------------------------------------------------- 1 | { 122 | const newImage = image.sizes.medium || image.sizes.thumbnail; 123 | const url = newImage.url; 124 | setAttributes({ selectedImage: url }); 125 | }, 126 | type: 'image', 127 | value: selectedImage, 128 | render: ({ open }) => wp.element.createElement('div', { 129 | className: 'left-image', 130 | onClick: open, 131 | style: { backgroundImage: `url(${selectedImage})` } }) 132 | }) 133 | ), 134 | wp.element.createElement( 135 | 'div', 136 | { className: 'right' }, 137 | wp.element.createElement(RichText, { 138 | tagName: 'p', 139 | value: content, 140 | onChange: changeText, 141 | placeholder: 'Enter text here...', 142 | isSelected: isSelected 143 | }) 144 | ) 145 | ); 146 | }, 147 | 148 | save(props) { 149 | const { className } = props; 150 | const { selectedImage, content } = props.attributes; 151 | 152 | return wp.element.createElement( 153 | 'div', 154 | { className: className }, 155 | wp.element.createElement( 156 | 'div', 157 | { className: 'left' }, 158 | wp.element.createElement('div', { 159 | className: 'left-image', 160 | onClick: open, 161 | style: { backgroundImage: `url(${selectedImage})` } 162 | }) 163 | ), 164 | wp.element.createElement( 165 | 'div', 166 | { className: 'right' }, 167 | wp.element.createElement( 168 | 'p', 169 | null, 170 | content 171 | ) 172 | ) 173 | ); 174 | } 175 | 176 | }); 177 | 178 | /***/ }), 179 | 180 | /***/ 55: 181 | /***/ (function(module, exports) { 182 | 183 | // removed by extract-text-webpack-plugin 184 | 185 | /***/ }), 186 | 187 | /***/ 56: 188 | /***/ (function(module, exports) { 189 | 190 | // removed by extract-text-webpack-plugin 191 | 192 | /***/ }) 193 | 194 | /******/ }); -------------------------------------------------------------------------------- /blocks/side-by-side/side-by-side.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-side-by-side .left-image { 2 | border: 2px solid transparent; 3 | transition: border 200ms; } 4 | 5 | .wp-block-guty-blocks-side-by-side .left-image:hover { 6 | cursor: pointer; 7 | border: 2px solid green; } 8 | 9 | .wp-block-guty-blocks-side-by-side .left-image:active { 10 | border: 2px solid lightgreen; } 11 | -------------------------------------------------------------------------------- /blocks/side-by-side/side-by-side.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-side-by-side { 2 | margin: auto; 3 | max-width: 30em; 4 | display: flex; 5 | flex-direction: row; } 6 | .wp-block-guty-blocks-side-by-side .left { 7 | width: 30%; 8 | padding: 2em 0; 9 | display: flex; 10 | justify-content: flex-end; 11 | align-items: center; } 12 | .wp-block-guty-blocks-side-by-side .left .left-image { 13 | position: relative; 14 | width: 100%; 15 | padding-top: 100%; 16 | border-radius: 20%; 17 | overflow: hidden; 18 | background-size: cover; 19 | background-position: center; 20 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); } 21 | .wp-block-guty-blocks-side-by-side .right { 22 | width: 70%; 23 | padding: 2em 1em; 24 | display: flex; 25 | justify-content: flex-start; 26 | align-items: center; } 27 | .wp-block-guty-blocks-side-by-side .right p { 28 | margin: 0; } 29 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | .editor-block-list__block { 6 | display: inline-block; 7 | width: 50%; 8 | padding: 8px; 9 | } 10 | -------------------------------------------------------------------------------- /src/block-layout/block-layout.src.js: -------------------------------------------------------------------------------- 1 | import './block-layout.editor.css'; 2 | import './block-layout.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | InnerBlocks, 10 | InspectorControls 11 | } = wp.editor; 12 | 13 | registerBlockType('guty-blocks/block-layout', { 14 | title: 'Block Layout', 15 | category: 'layout', 16 | 17 | attributes: { // Somewhat like setting initial state in a react app. 18 | // Strategy for mapping rendered attributes back into editable state 19 | 20 | }, 21 | 22 | // The editor "render" function 23 | edit(props) { 24 | return ([ 25 | props.isSelected && ( 26 | 27 | Select the number of columns for your blocks: 28 | 29 | 30 | ), 31 | < div class={ props.className }> 32 | 37 | 38 | ]); 39 | }, 40 | 41 | // The save "render" function 42 | save(props) { 43 | return ( 44 |
45 | 46 |
47 | ); 48 | } 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /src/block-layout/block-layout.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-block-layout { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | align-items: stretch; 6 | } 7 | 8 | .wp-block-guty-blocks-block-layout > * { 9 | width: 50%; 10 | } 11 | -------------------------------------------------------------------------------- /src/carousel/carousel.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /src/carousel/carousel.src.js: -------------------------------------------------------------------------------- 1 | import './carousel.editor.css'; 2 | import './carousel.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | MediaUpload, 10 | InspectorControls 11 | } = wp.editor; 12 | 13 | registerBlockType('guty-blocks/carousel', { 14 | title: 'Carousel', 15 | icon: 'welcome-write-blog', 16 | category: 'common', 17 | 18 | attributes: { // Somewhat like setting initial state in a react app 19 | carouselImages: { 20 | type: 'array', 21 | default: [] 22 | }, 23 | carouselTime: { 24 | type: 'number', 25 | default: '5' 26 | }, 27 | fadeTime: { 28 | type: 'number', 29 | default: '500' 30 | } 31 | }, 32 | 33 | // The editor "render" function 34 | edit(props) { 35 | const { 36 | className, 37 | setAttributes, 38 | attributes: { 39 | carouselImages, 40 | carouselTime, 41 | fadeTime 42 | } 43 | } = props; 44 | 45 | function changeImageArray(changes) { 46 | setAttributes({ carouselImages: changes }); 47 | } 48 | function changeCarouselTime(changes) { 49 | setAttributes({ carouselTime: changes.target.value }); 50 | } 51 | 52 | function changeFadeTime(changes) { 53 | setAttributes({ fadeTime: changes.target.value }); 54 | } 55 | 56 | 57 | const MyMediaUpload = ( 62 | 67 | )} 68 | />; 69 | 70 | return ([ 71 | 72 |
73 | {MyMediaUpload} 74 |
75 |
76 | Set fade length (milliseconds): 77 | 82 |
83 |
84 | Set slide interval (seconds): 85 | 90 |
91 |
92 | , 93 | MyMediaUpload 94 | , 95 |
101 |
102 | {carouselImages.length ? 103 | carouselImages.map((el, index) => { 104 | return ; 109 | }) 110 | : 111 | 'No Images Yet' 112 | } 113 |
114 |
115 | ]); 116 | }, 117 | 118 | // The save "render" function 119 | save(props) { 120 | const { 121 | className, 122 | attributes: { 123 | carouselImages, 124 | carouselTime, 125 | fadeTime 126 | } 127 | } = props; 128 | 129 | return ( 130 |
136 |
137 | {carouselImages.length ? 138 | carouselImages.map((el, index) => { 139 | return ; 144 | }) 145 | : 146 | 'No images yet...' 147 | } 148 |
149 |
150 | ); 151 | } 152 | 153 | }); `` -------------------------------------------------------------------------------- /src/carousel/carousel.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-carousel { 2 | width: 100%; 3 | position: relative; 4 | } 5 | 6 | .wp-block-guty-blocks-carousel .inner { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | height: 100%; 12 | overflow: hidden; 13 | } 14 | 15 | .wp-block-guty-blocks-carousel .inner img { 16 | position: absolute; 17 | width: 100%; 18 | top: 50%; 19 | transform: translateY(-50%); 20 | opacity: 0; 21 | } 22 | 23 | .wp-block-guty-blocks-carousel .inner img.active { 24 | opacity: 1; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/gallery/Image.js: -------------------------------------------------------------------------------- 1 | const Image = function(props) { 2 | 3 | const { photo, galleryID } = props; 4 | const photoThumb = photo.sizes.medium || photo.sizes.thumbnail; 5 | 6 | return ( 7 | 8 |
15 | 16 | ); 17 | } 18 | 19 | export { Image }; -------------------------------------------------------------------------------- /src/gallery/gallery.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | 7 | .galleryInspector > *{ 8 | display: block; 9 | margin: 15px; 10 | } 11 | -------------------------------------------------------------------------------- /src/gallery/gallery.src.js: -------------------------------------------------------------------------------- 1 | import './gallery.editor.css'; 2 | import './gallery.view.css'; 3 | 4 | import { Image } from './Image'; 5 | 6 | const { 7 | registerBlockType, 8 | } = wp.blocks; 9 | 10 | const { 11 | RichText, 12 | PlainText, 13 | InspectorControls, 14 | MediaUpload 15 | } = wp.editor; 16 | 17 | registerBlockType('guty-blocks/gallery', { 18 | title: 'Custom Photo Gallery', 19 | icon: 'format-gallery', 20 | category: 'common', 21 | 22 | attributes: { 23 | titleText: { 24 | type: 'string' 25 | }, 26 | imagesArray: { 27 | type: 'array', 28 | default: [] 29 | }, 30 | columns: { 31 | type: 'number', 32 | default: 2 33 | }, 34 | alignment: { 35 | type: 'string', 36 | default: 'alignLeft' 37 | } 38 | }, 39 | 40 | // The editor "render" function 41 | edit(props) { 42 | 43 | const { focus, className, setAttributes } = props; 44 | const { titleText, imagesArray, columns, alignment } = props.attributes; 45 | 46 | const changeTitle = (changes) => { 47 | setAttributes({ titleText: changes }) 48 | }; 49 | 50 | function changeImageArray(changes) { 51 | setAttributes({ imagesArray: changes }) 52 | } 53 | 54 | function changeColumns(changes) { 55 | setAttributes({ columns: changes.target.value }); 56 | } 57 | 58 | function changeAlignment(changes) { 59 | setAttributes({ alignment: changes.target.value }); 60 | } 61 | 62 | function moveElement(elIndex, direction) { 63 | let tempArray = [...imagesArray]; 64 | 65 | //take out element 66 | const el = tempArray.splice(elIndex, 1)[0]; 67 | 68 | // place element in either previous or next 69 | tempArray.splice(elIndex + direction, 0, el); 70 | 71 | //set array back 72 | setAttributes({ imagesArray: tempArray }); 73 | } 74 | 75 | function removeElement(elIndex) { 76 | let tempArray = [...imagesArray]; 77 | tempArray.splice(elIndex, 1); 78 | setAttributes({ imagesArray: tempArray }); 79 | } 80 | 81 | function addPhotos(images) { 82 | setAttributes({ 83 | imagesArray: [...imagesArray, ...images] 84 | }); 85 | } 86 | 87 | const MyMediaUpload = ( 92 | 96 | )} 97 | />; 98 | 99 | const AddPhotosUpload = { 104 | return ( 105 |
106 | 107 |
108 | ) 109 | }} 110 | />; 111 | 112 | 113 | return ([ 114 | focus && 115 |
116 | Settings for gallery:"{titleText}" 117 | {MyMediaUpload} 118 |
119 |
120 | 124 |
125 |
126 |
127 | 131 |
132 |
133 |
, 134 |
135 |
136 |

137 | 143 | </h1> 144 | 145 | <div className={`galleryContainer galleryContainer_${columns}up galleryContainer_${alignment}`}> 146 | {imagesArray.length ? 147 | imagesArray.map((el, index) => { 148 | return ( 149 | <div className="imageWrapper"> 150 | <Image 151 | key={index} 152 | photo={el} 153 | galleryID={titleText} 154 | /> 155 | <div className="imageWrapper-buttons"> 156 | {index !== 0 && <button onClick={() => moveElement(index, -1)}>&lt;</button>} 157 | <button onClick={() => removeElement(index)}>Remove</button> 158 | {(index !== (imagesArray.length - 1)) && <button onClick={() => moveElement(index, 1)}>&gt;</button>} 159 | </div> 160 | </div> 161 | ); 162 | }) 163 | : 164 | <div> 165 | <h3>No images yet</h3> 166 | {MyMediaUpload} 167 | </div> 168 | } 169 | {AddPhotosUpload} 170 | </div> 171 | </section> 172 | 173 | </div> 174 | ]); 175 | }, 176 | 177 | // The save "render" function 178 | save(props) { 179 | 180 | const { className } = props; 181 | const { titleText, imagesArray, columns, alignment } = props.attributes; 182 | 183 | return ( 184 | <div className={className}> 185 | <section className='section'> 186 | <h1>{titleText}</h1> 187 | 188 | <div className={`galleryContainer galleryContainer_${columns}up galleryContainer_${alignment}`}> 189 | { 190 | imagesArray.map((el, index) => { 191 | return <Image 192 | key={index} 193 | photo={el} 194 | galleryID={titleText} 195 | /> 196 | }) 197 | } 198 | </div> 199 | </section> 200 | </div> 201 | ); 202 | } 203 | 204 | }); -------------------------------------------------------------------------------- /src/gallery/gallery.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-gallery h1 { 2 | margin-bottom: 16px; 3 | } 4 | 5 | .wp-block-guty-blocks-gallery * { 6 | box-sizing: border-box; 7 | } 8 | 9 | :root { 10 | --PADDING: 32px; 11 | } 12 | 13 | @media (max-width: 450px) { 14 | :root { 15 | --PADDING: 16px; 16 | } 17 | } 18 | 19 | .wp-block-guty-blocks-gallery .galleryContainer { 20 | position: relative; 21 | display: flex; 22 | flex-direction: row; 23 | flex-wrap: wrap; 24 | margin: calc(-1 * var(--PADDING)) calc(-.5 * var(--PADDING)) 0 calc(-.5 * var(--PADDING)); 25 | } 26 | 27 | .wp-block-guty-blocks-gallery .galleryContainer>* { 28 | padding: var(--PADDING) calc(0.5 * var(--PADDING)) 0 calc(0.5 * var(--PADDING)); 29 | } 30 | 31 | .wp-block-guty-blocks-gallery .galleryContainer>* { 32 | width: 100%; 33 | } 34 | 35 | .wp-block-guty-blocks-gallery .galleryContainer_2up>* { 36 | width: 50%; 37 | } 38 | 39 | .wp-block-guty-blocks-gallery .galleryContainer_3up>* { 40 | width: 33.3%; 41 | } 42 | 43 | .wp-block-guty-blocks-gallery .galleryContainer_4up>* { 44 | width: 25%; 45 | } 46 | 47 | @media (max-width: 450px) { 48 | .wp-block-guty-blocks-gallery .galleryContainer>*, 49 | .wp-block-guty-blocks-gallery .galleryContainer_2up>*, 50 | .wp-block-guty-blocks-gallery .galleryContainer_3up>*, 51 | .wp-block-guty-blocks-gallery .galleryContainer_4up>* { 52 | width: 100%; 53 | } 54 | } 55 | 56 | .wp-block-guty-blocks-gallery .galleryContainer_alignLeft { 57 | justify-content: flex-start; 58 | } 59 | 60 | .wp-block-guty-blocks-gallery .galleryContainer_alignCenter { 61 | justify-content: center; 62 | } 63 | 64 | .wp-block-guty-blocks-gallery .galleryContainer_alignRight { 65 | justify-content: flex-end; 66 | } 67 | 68 | .imageWrapper { 69 | position: relative; 70 | } 71 | 72 | .imageWrapper-buttons { 73 | position: absolute; 74 | display: flex; 75 | padding: 0 30px; 76 | justify-content: space-between; 77 | width: 100%; 78 | top: calc(50% + 16px); 79 | left: 0; 80 | transform: translateY(-50%); 81 | } 82 | 83 | .imageWrapper-buttons button { 84 | opacity: 0; 85 | transition: 200ms; 86 | } 87 | 88 | .imageWrapper:hover button { 89 | opacity: 1; 90 | cursor: pointer; 91 | border: none; 92 | background: cadetblue; 93 | color: white; 94 | } 95 | 96 | .imageWrapper:hover button:hover { 97 | color: cadetblue; 98 | background: white; 99 | } 100 | 101 | .galleryContainer .galleryContainer-addPhoto { 102 | position: absolute; 103 | bottom: 0; 104 | right: 0; 105 | text-align: right; 106 | } 107 | 108 | .galleryContainer .galleryContainer-addPhoto button { 109 | cursor: pointer; 110 | border: none; 111 | background: cadetblue; 112 | color: white; 113 | transition: 200ms; 114 | } 115 | 116 | .galleryContainer .galleryContainer-addPhoto button:hover { 117 | background: rgba(95, 158, 160, .8); 118 | color: white; 119 | } -------------------------------------------------------------------------------- /src/hello-world/_test.scss: -------------------------------------------------------------------------------- 1 | body { 2 | 3 | } -------------------------------------------------------------------------------- /src/hello-world/hello-world.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /src/hello-world/hello-world.src.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { registerBlockType } = wp.blocks; 5 | // const { __ } = wp.i18n; 6 | 7 | import './hello-world.editor.css'; 8 | import './hello-world.view.scss'; 9 | 10 | registerBlockType('guty-blocks/hello-world', { 11 | title: 'Hello World!', 12 | icon: 'welcome-write-blog', 13 | category: 'common', 14 | 15 | attributes: { // Somewhat like setting initial state in a react app 16 | }, 17 | 18 | // The editor "render" function 19 | edit(props) { 20 | const { className } = props; 21 | 22 | return ( 23 | <div className={className}> 24 | {/* <h1>{ __( 'Hello World!', 'guty-blocks' ) }</h1> */} 25 | <h1>Hello World!</h1> 26 | </div> 27 | ); 28 | }, 29 | 30 | // The save "render" function 31 | save(props) { 32 | const { className } = props; 33 | 34 | return ( 35 | <div className={className}> 36 | {/* <h1>{ __( 'Hello World!', 'guty-blocks' ) }</h1> */} 37 | <h1>Hello World!</h1> 38 | </div> 39 | ); 40 | } 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /src/hello-world/hello-world.view.scss: -------------------------------------------------------------------------------- 1 | // @import 'test'; 2 | 3 | 4 | .wp-block-guty-blocks-hello-world { 5 | background: tomato; 6 | color: white; 7 | } -------------------------------------------------------------------------------- /src/image-hero/image-hero.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-image-hero { 2 | position: relative; 3 | background-position: center; 4 | background-size: cover; 5 | text-align: center; 6 | padding: 8em 0; 7 | } 8 | 9 | .overlay { 10 | position: absolute; 11 | top:0; 12 | left: 0; 13 | width: 100%; 14 | height: 100%; 15 | } 16 | 17 | .wp-block-guty-blocks-image-hero h1 { 18 | position: relative; 19 | z-index:1; 20 | } -------------------------------------------------------------------------------- /src/image-hero/image-hero.src.js: -------------------------------------------------------------------------------- 1 | import './image-hero.editor.css'; 2 | import './image-hero.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | AlignmentToolbar, //prebuild alignment button component that we put in block controls for this block 10 | RichText, 11 | InspectorControls, // allows us to add controls on the sidebar 12 | BlockControls, //component that appears right above block when it is selected 13 | MediaUpload, // allows us to upload images 14 | ColorPalette, // prebuilt component that allows color picking in inspector controls 15 | } = wp.editor; 16 | 17 | registerBlockType('guty-blocks/image-hero', { 18 | title: 'Image Hero Block', 19 | icon: 'format-image', 20 | category: 'common', 21 | 22 | // Somewhat like setting initial state in a react app 23 | attributes: { 24 | alignement: { 25 | type: 'string' 26 | }, 27 | content: { 28 | type: 'array', 29 | source: 'children', 30 | selector: 'h1', 31 | default: 'Editable block content...', 32 | }, 33 | imageUrl: { 34 | type: 'string', 35 | default: "http://placehold.it/800x300" 36 | }, 37 | textColor: { 38 | type: 'string', 39 | default: null 40 | }, 41 | gradientColor: { 42 | type: 'string', 43 | default: null 44 | } 45 | }, 46 | 47 | // The editor "render" function 48 | edit(props) { 49 | 50 | let { focus } = props; 51 | let { alignment, content, imageUrl, textColor, gradientColor } = props.attributes; 52 | 53 | function onChangeContent(updatedContent) { 54 | props.setAttributes({ content: updatedContent }); 55 | } 56 | function onChangeImage(imgObject) { 57 | props.setAttributes({ imageUrl: imgObject.url }); 58 | } 59 | function onChangeGradientColor(color) { 60 | props.setAttributes({ gradientColor: color }); 61 | } 62 | function onChangeTextColor(color) { 63 | props.setAttributes({ textColor: color }); 64 | } 65 | 66 | // Actual elements being 67 | return ([ 68 | props.isSelected && (<InspectorControls> 69 | <MediaUpload 70 | type="image" 71 | onSelect={onChangeImage} 72 | render={({ open }) => ( 73 | <button onClick={open}> 74 | Select a background image 75 | </button> 76 | )} 77 | /><br /><br /> 78 | <span>Select text color:</span> 79 | <ColorPalette 80 | value={textColor} 81 | onChange={onChangeTextColor} 82 | /> 83 | <br /><br /> 84 | <span>Select a gradient color:</span> 85 | <ColorPalette 86 | value={gradientColor} 87 | onChange={onChangeGradientColor} 88 | /> 89 | <br /><br /> 90 | </InspectorControls> 91 | ), 92 | props.isSelected && ( 93 | <BlockControls> 94 | <AlignmentToolbar 95 | value={ alignment } 96 | onChange={ (change) => props.setAttributes({alignment: change}) } 97 | /> 98 | </BlockControls> 99 | ), 100 | <div className={props.className} style={{ backgroundImage: `url(${imageUrl})` }}> 101 | <div 102 | className={`overlay`} 103 | style={{ 104 | background: gradientColor, 105 | opacity: '.3' 106 | }} 107 | ></div> 108 | <RichText 109 | tagName="h1" 110 | value={ content } 111 | onChange={onChangeContent} 112 | isSelected={props.isSelected} 113 | style={{ 114 | color: textColor, 115 | textAlign: alignment 116 | }} 117 | /> 118 | </div> 119 | ]); 120 | }, 121 | 122 | // The save "render" function 123 | save(props) { 124 | let { className } = props; 125 | let { alignment, content, imageUrl, gradientColor, textColor } = props.attributes; 126 | 127 | return ( 128 | <div className={className} style={{ backgroundImage: `url(${imageUrl})` }}> 129 | <div 130 | className={`overlay`} //Note to self... `${props.className}` was undefined here. Bug? 131 | style={{ 132 | background: gradientColor, 133 | opacity: '.3' 134 | }} 135 | ></div> 136 | <h1 style={{ 137 | color: textColor, 138 | textAlign: alignment 139 | }}>{content}</h1> 140 | </div> 141 | ); 142 | } 143 | 144 | }); -------------------------------------------------------------------------------- /src/image-hero/image-hero.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-image-hero { 2 | position: relative; 3 | background-position: center; 4 | background-size: cover; 5 | text-align: center; 6 | padding: 8em 0; 7 | left: calc(50% - 50vw); 8 | width: 100vw; 9 | } 10 | 11 | .overlay { 12 | position: absolute; 13 | top:0; 14 | left: 0; 15 | width: 100%; 16 | height: 100%; 17 | } 18 | 19 | .wp-block-guty-blocks-image-hero h1 { 20 | position: relative; 21 | z-index:1; 22 | } -------------------------------------------------------------------------------- /src/media-block/media-block.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-media-block { 2 | position: relative; 3 | background: cadetblue; 4 | padding:2em; 5 | display: flex; 6 | flex-direction: row; 7 | } 8 | .wp-block-guty-blocks-media-block:before { 9 | content:'Editing mode'; 10 | position: absolute; 11 | top: 4px; 12 | left: 4px; 13 | color: red; 14 | } 15 | 16 | .wp-block-guty-blocks-media-block .left { 17 | flex: 33%; 18 | padding-right: 16px; 19 | } 20 | 21 | .wp-block-guty-blocks-media-block .right { 22 | flex: 66%; 23 | } 24 | 25 | .wp-block-guty-blocks-media-block p { 26 | font-size: 24px; 27 | color: white; 28 | } -------------------------------------------------------------------------------- /src/media-block/media-block.src.js: -------------------------------------------------------------------------------- 1 | import './media-block.editor.css'; 2 | import './media-block.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | RichText, // Editable field 10 | InspectorControls, // allows us to add controls on the sidebar 11 | MediaUpload // allows us to upload images 12 | } = wp.editor 13 | 14 | registerBlockType('guty-blocks/media-block', { 15 | title: 'Media Item Block', 16 | icon: 'smiley', 17 | category: 'common', 18 | 19 | attributes: { // Somewhat like setting initial state in a react app 20 | content: { 21 | type: 'array', 22 | source: 'children', 23 | selector: 'p', 24 | default: 'Editable block content...', 25 | }, 26 | imageUrl: { 27 | type: 'string', 28 | default: null 29 | } 30 | }, 31 | 32 | // The editor "render" function 33 | edit(props) { 34 | 35 | let { content, imageUrl, focus, isSelected } = props.attributes; 36 | 37 | function onChangeContent(updatedContent) { 38 | props.setAttributes({ content: updatedContent }); 39 | } 40 | 41 | function setImage(image) { 42 | console.log(image); 43 | props.setAttributes({ imageUrl: image.url }) 44 | } 45 | 46 | 47 | // If an image isn't selected show the upload button 48 | // otherwise, show the image 49 | let imageSide = null; 50 | if (imageUrl) { 51 | imageSide = <img src={imageUrl} alt="" />; 52 | } else { 53 | imageSide = <MediaUpload 54 | type="image" 55 | onSelect={setImage} 56 | render={({ open }) => ( 57 | <button onClick={open}> 58 | Open Media Library 59 | </button> 60 | )} 61 | /> 62 | } 63 | 64 | // Actual elements being rendered 65 | return ([ 66 | isSelected && ( 67 | <InspectorControls key="controls"> 68 | <p>This is where some style options can be presented for your block!</p> 69 | </InspectorControls> 70 | ), 71 | <div className={props.className}> 72 | <div class="left"> 73 | {imageSide} 74 | </div> 75 | <div class="right"> 76 | <RichText 77 | tagName="p" 78 | onChange={onChangeContent} 79 | value={content} 80 | /> 81 | </div> 82 | </div> 83 | ]); 84 | }, 85 | 86 | // The save "render" function 87 | save(props) { 88 | return ( 89 | <div className={props.className}> 90 | <div class="left"> 91 | <img src={props.attributes.imageUrl} alt="" /> 92 | </div> 93 | <div class="right"> 94 | <p>{props.attributes.content}</p> 95 | </div> 96 | </div> 97 | ); 98 | } 99 | 100 | }); -------------------------------------------------------------------------------- /src/media-block/media-block.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-media-block { 2 | position: relative; 3 | background: cadetblue; 4 | padding:2em; 5 | display: flex; 6 | flex-direction: row; 7 | } 8 | .wp-block-guty-blocks-media-block:before { 9 | content:'View Mode'; 10 | position: absolute; 11 | top: 4px; 12 | left: 4px; 13 | color: blue; 14 | } 15 | 16 | .wp-block-guty-blocks-media-block .left { 17 | flex: 33%; 18 | padding-right: 16px; 19 | } 20 | 21 | .wp-block-guty-blocks-media-block .right { 22 | flex: 66%; 23 | } 24 | 25 | .wp-block-guty-blocks-media-block p { 26 | font-size: 24px; 27 | color: white; 28 | } -------------------------------------------------------------------------------- /src/prism-code/prism-code.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /src/prism-code/prism-code.src.js: -------------------------------------------------------------------------------- 1 | import './prism-code.editor.css'; 2 | import './prism-code.view.css'; 3 | 4 | import TextareaAutosize from 'react-autosize-textarea'; 5 | 6 | var Prism = require('./prismjs/prism.js'); 7 | 8 | var nativeElements = {}; 9 | 10 | const { 11 | registerBlockType, 12 | } = wp.blocks; 13 | 14 | const { 15 | PlainText, 16 | BlockControls, 17 | InspectorControls 18 | } = wp.editor; 19 | 20 | console.log(wp.editor); 21 | 22 | registerBlockType('guty-blocks/prism-code', { 23 | title: 'Prism Code Formatter', 24 | icon: 'editor-code', 25 | category: 'common', 26 | 27 | attributes: { // Somewhat like setting initial state in a react app 28 | codeString: { 29 | type: 'string', 30 | default: '', 31 | }, 32 | beautifulCode: { 33 | type: 'string', 34 | default: '' 35 | }, 36 | language: { 37 | type: 'string', 38 | default: 'javascript' 39 | }, 40 | tabLength: { 41 | type: 'number', 42 | default: 4 43 | } 44 | }, 45 | 46 | // The editor "render" function 47 | edit(props) { 48 | const { 49 | className, 50 | setAttributes, 51 | focus, 52 | attributes: { 53 | codeString, 54 | beautifulCode, 55 | language, 56 | tabLength 57 | } 58 | } = props; 59 | 60 | function changeLanguage(event) { 61 | let newLanguage = event.target.value; 62 | let tempCodeString = Prism.highlight(codeString, Prism.languages[newLanguage]); 63 | setAttributes({ 64 | language: newLanguage, 65 | beautifulCode: tempCodeString 66 | }) 67 | } 68 | function changeCode(changes, event) { 69 | let tempCodeString = Prism.highlight(changes, Prism.languages[language]); 70 | setAttributes({ 71 | beautifulCode: tempCodeString, 72 | codeString: changes 73 | }); 74 | } 75 | 76 | function changeTabLength(event) { 77 | console.log(event.target.value, parseInt(event.target.value)) 78 | setAttributes({ tabLength: parseInt(event.target.value) }) 79 | } 80 | 81 | function checkKey(event) { 82 | // checks for a tab, and if present, manually adds spacing 83 | if(event.keyCode == 9) { 84 | // escape browser tabbing 85 | event.preventDefault(); 86 | 87 | // get cursor location 88 | let location = event.nativeEvent.target.selectionEnd; 89 | 90 | // "splice" a tab 91 | let newCodeString = codeString.slice(0,location) + ' '.repeat(tabLength) + codeString.slice(location); 92 | 93 | 94 | let newBeautifulCodeString = Prism.highlight(newCodeString, Prism.languages[language]); 95 | 96 | setAttributes({ 97 | codeString: newCodeString, 98 | beautifulCode: newBeautifulCodeString 99 | }); 100 | 101 | // setTimout hack will have to suffice since setAttribute callback is not available 102 | setTimeout(() => { 103 | nativeElements.inputRef.focus(); 104 | nativeElements.inputRef.selectionEnd = location + tabLength; 105 | }, 0); 106 | } 107 | } 108 | 109 | return ([ 110 | <InspectorControls> 111 | <div style={{ margin: '24px 0'}}> 112 | <strong style={{display: 'block'}}>Change the language:</strong> 113 | <select onChange={changeLanguage}> 114 | <option value="javascript" selected={language === 'javascript'}>Javascript</option> 115 | <option value="jsx" selected={language === 'jsx'}>JSX</option> 116 | <option value="markup" selected={language === 'markup'}>HTML</option> 117 | <option value="css" selected={language === 'css'}>CSS</option> 118 | <option value="php" selected={language === 'php'}>PHP</option> 119 | </select> 120 | </div> 121 | <div style={{ margin: '24px 0'}}> 122 | <strong style={{display: 'block'}}>Tab character length:</strong> 123 | <select onChange={changeTabLength}> 124 | <option value="2" selected={tabLength == 2}>2</option> 125 | <option value="4" selected={tabLength == 4}>4</option> 126 | </select> 127 | </div> 128 | </InspectorControls> 129 | , 130 | <div className={className}> 131 | <pre class={`language-${language}`}> 132 | <TextareaAutosize 133 | value={codeString} 134 | tag="code" 135 | onChange={(e) => changeCode(e.target.value, e)} 136 | onKeyDown={checkKey} 137 | placeholder='Type some code here...' 138 | innerRef={el => (nativeElements.inputRef = el)} //storing reference to try to set cursor position 139 | /> 140 | </pre> 141 | <pre class={`language-${language}`}> 142 | <code dangerouslySetInnerHTML={{ __html: beautifulCode }}> 143 | </code> 144 | </pre> 145 | 146 | </div> 147 | ]); 148 | }, 149 | 150 | // The save "render" function 151 | save(props) { 152 | const { 153 | className, 154 | attributes: { 155 | codeString, 156 | beautifulCode, 157 | language 158 | } 159 | } = props; 160 | 161 | return ( 162 | <div className={className}> 163 | <pre class={`language-${language}`}> 164 | <code dangerouslySetInnerHTML={{ __html: beautifulCode }}> 165 | </code> 166 | </pre> 167 | </div> 168 | ); 169 | } 170 | 171 | }); -------------------------------------------------------------------------------- /src/prism-code/prism-code.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-prism-code.wp-block-guty-blocks-prism-code textarea { 2 | box-shadow: none; 3 | font-family: inherit; 4 | font-size: inherit; 5 | color: inherit; 6 | line-height: inherit; 7 | border: none; 8 | padding: 0; 9 | margin: 0; 10 | width: 100%; 11 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 12 | background: transparent; 13 | } 14 | 15 | code { 16 | padding: 0; 17 | margin: 0; 18 | font-size: inherit; 19 | } 20 | 21 | 22 | /* Prism styles */ 23 | /* PrismJS 1.12.2 24 | http://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+jsx */ 25 | /** 26 | * prism.js default theme for JavaScript, CSS and HTML 27 | * Based on dabblet (http://dabblet.com) 28 | * @author Lea Verou 29 | */ 30 | 31 | code[class*="language-"], 32 | pre[class*="language-"] { 33 | color: black; 34 | background: none; 35 | text-shadow: 0 1px white; 36 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 37 | text-align: left; 38 | white-space: pre; 39 | word-spacing: normal; 40 | word-break: normal; 41 | word-wrap: normal; 42 | line-height: 1.5; 43 | 44 | -moz-tab-size: 4; 45 | -o-tab-size: 4; 46 | tab-size: 4; 47 | 48 | -webkit-hyphens: none; 49 | -moz-hyphens: none; 50 | -ms-hyphens: none; 51 | hyphens: none; 52 | } 53 | 54 | pre[class*="language-"]::-moz-selection, 55 | pre[class*="language-"] ::-moz-selection, 56 | code[class*="language-"]::-moz-selection, 57 | code[class*="language-"] ::-moz-selection { 58 | text-shadow: none; 59 | background: #b3d4fc; 60 | } 61 | 62 | pre[class*="language-"]::selection, 63 | pre[class*="language-"] ::selection, 64 | code[class*="language-"]::selection, 65 | code[class*="language-"] ::selection { 66 | text-shadow: none; 67 | background: #b3d4fc; 68 | } 69 | 70 | @media print { 71 | 72 | code[class*="language-"], 73 | pre[class*="language-"] { 74 | text-shadow: none; 75 | } 76 | } 77 | 78 | /* Code blocks */ 79 | pre[class*="language-"] { 80 | padding: 1em; 81 | margin: .5em 0; 82 | overflow: auto; 83 | } 84 | 85 | :not(pre)>code[class*="language-"], 86 | pre[class*="language-"] { 87 | background: #f5f2f0; 88 | } 89 | 90 | /* Inline code */ 91 | :not(pre)>code[class*="language-"] { 92 | padding: .1em; 93 | border-radius: .3em; 94 | white-space: normal; 95 | } 96 | 97 | .token.comment, 98 | .token.prolog, 99 | .token.doctype, 100 | .token.cdata { 101 | color: slategray; 102 | } 103 | 104 | .token.punctuation { 105 | color: #999; 106 | } 107 | 108 | .namespace { 109 | opacity: .7; 110 | } 111 | 112 | .token.property, 113 | .token.tag, 114 | .token.boolean, 115 | .token.number, 116 | .token.constant, 117 | .token.symbol, 118 | .token.deleted { 119 | color: #905; 120 | } 121 | 122 | .token.selector, 123 | .token.attr-name, 124 | .token.string, 125 | .token.char, 126 | .token.builtin, 127 | .token.inserted { 128 | color: #690; 129 | } 130 | 131 | .token.operator, 132 | .token.entity, 133 | .token.url, 134 | .language-css .token.string, 135 | .style .token.string { 136 | color: #a67f59; 137 | background: hsla(0, 0%, 100%, .5); 138 | } 139 | 140 | .token.atrule, 141 | .token.attr-value, 142 | .token.keyword { 143 | color: #07a; 144 | } 145 | 146 | .token.function, 147 | .token.class-name { 148 | color: #DD4A68; 149 | } 150 | 151 | .token.regex, 152 | .token.important, 153 | .token.variable { 154 | color: #e90; 155 | } 156 | 157 | .token.important, 158 | .token.bold { 159 | font-weight: bold; 160 | } 161 | 162 | .token.italic { 163 | font-style: italic; 164 | } 165 | 166 | .token.entity { 167 | cursor: help; 168 | } -------------------------------------------------------------------------------- /src/prism-code/prismjs/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.12.2 2 | http://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+php+jsx */ 3 | /** 4 | * prism.js default theme for JavaScript, CSS and HTML 5 | * Based on dabblet (http://dabblet.com) 6 | * @author Lea Verou 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: black; 12 | background: none; 13 | text-shadow: 0 1px white; 14 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 15 | text-align: left; 16 | white-space: pre; 17 | word-spacing: normal; 18 | word-break: normal; 19 | word-wrap: normal; 20 | line-height: 1.5; 21 | 22 | -moz-tab-size: 4; 23 | -o-tab-size: 4; 24 | tab-size: 4; 25 | 26 | -webkit-hyphens: none; 27 | -moz-hyphens: none; 28 | -ms-hyphens: none; 29 | hyphens: none; 30 | } 31 | 32 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 33 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 34 | text-shadow: none; 35 | background: #b3d4fc; 36 | } 37 | 38 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 39 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 40 | text-shadow: none; 41 | background: #b3d4fc; 42 | } 43 | 44 | @media print { 45 | code[class*="language-"], 46 | pre[class*="language-"] { 47 | text-shadow: none; 48 | } 49 | } 50 | 51 | /* Code blocks */ 52 | pre[class*="language-"] { 53 | padding: 1em; 54 | margin: .5em 0; 55 | overflow: auto; 56 | } 57 | 58 | :not(pre) > code[class*="language-"], 59 | pre[class*="language-"] { 60 | background: #f5f2f0; 61 | } 62 | 63 | /* Inline code */ 64 | :not(pre) > code[class*="language-"] { 65 | padding: .1em; 66 | border-radius: .3em; 67 | white-space: normal; 68 | } 69 | 70 | .token.comment, 71 | .token.prolog, 72 | .token.doctype, 73 | .token.cdata { 74 | color: slategray; 75 | } 76 | 77 | .token.punctuation { 78 | color: #999; 79 | } 80 | 81 | .namespace { 82 | opacity: .7; 83 | } 84 | 85 | .token.property, 86 | .token.tag, 87 | .token.boolean, 88 | .token.number, 89 | .token.constant, 90 | .token.symbol, 91 | .token.deleted { 92 | color: #905; 93 | } 94 | 95 | .token.selector, 96 | .token.attr-name, 97 | .token.string, 98 | .token.char, 99 | .token.builtin, 100 | .token.inserted { 101 | color: #690; 102 | } 103 | 104 | .token.operator, 105 | .token.entity, 106 | .token.url, 107 | .language-css .token.string, 108 | .style .token.string { 109 | color: #a67f59; 110 | background: hsla(0, 0%, 100%, .5); 111 | } 112 | 113 | .token.atrule, 114 | .token.attr-value, 115 | .token.keyword { 116 | color: #07a; 117 | } 118 | 119 | .token.function, 120 | .token.class-name { 121 | color: #DD4A68; 122 | } 123 | 124 | .token.regex, 125 | .token.important, 126 | .token.variable { 127 | color: #e90; 128 | } 129 | 130 | .token.important, 131 | .token.bold { 132 | font-weight: bold; 133 | } 134 | .token.italic { 135 | font-style: italic; 136 | } 137 | 138 | .token.entity { 139 | cursor: help; 140 | } -------------------------------------------------------------------------------- /src/quote/quote.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-quote .blocks-rich-text { 2 | display: inline; 3 | } 4 | 5 | .wp-block-guty-blocks-quote .author .blocks-rich-text { 6 | font-variant: small-caps; 7 | color: #646464; 8 | } 9 | -------------------------------------------------------------------------------- /src/quote/quote.src.js: -------------------------------------------------------------------------------- 1 | import './quote.editor.css'; 2 | import './quote.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | RichText 10 | } = wp.editor; 11 | 12 | registerBlockType('guty-blocks/quote', { 13 | title: 'Custom Quote Block', 14 | category: 'common', 15 | 16 | attributes: { // Somewhat like setting initial state in a react app. 17 | // Strategy for mapping rendered attributes back into editable state 18 | author: { 19 | type: 'string', 20 | default: 'author' 21 | }, 22 | quoteContent: { 23 | type: 'string', 24 | default: 'Enter quote here' 25 | } 26 | }, 27 | 28 | // The editor "render" function 29 | edit(props) { 30 | let {className} = props; 31 | return ( 32 | <aside className={"quote " + className}> 33 | <div className="quote-text"> 34 | <span className="first-last-quotes">“</span> 35 | <RichText 36 | tagName="span" 37 | value={props.attributes.quoteContent} 38 | onChange={(changes) => props.setAttributes({ quoteContent:changes })} 39 | /> 40 | <span className="first-last-quotes">”</span> 41 | </div> 42 | <div className="author"> 43 | - <RichText 44 | tagName="span" 45 | value={props.attributes.author} 46 | onChange={(changes) => props.setAttributes({ author: changes})} 47 | /> 48 | </div> 49 | </aside> 50 | ); 51 | }, 52 | 53 | // The save "render" function 54 | save(props) { 55 | let {className} = props; 56 | return ( 57 | <aside className={"quote " + className}> 58 | <div className="quote-text"> 59 | <span className="first-last-quotes">“</span> 60 | {props.attributes.quoteContent} 61 | <span className="first-last-quotes">”</span> 62 | </div> 63 | <div className="author"> 64 | - {props.attributes.author} 65 | </div> 66 | </aside> 67 | ); 68 | } 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /src/quote/quote.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-quote { 2 | width: 80%; 3 | margin: 50px auto 25px; 4 | padding: 8px; 5 | border: 1px solid #d8d2d0; 6 | text-align: center; 7 | } 8 | 9 | .wp-block-guty-blocks-quote .quote-text { 10 | padding: 8px; 11 | font-size: 24px; 12 | } 13 | 14 | .wp-block-guty-blocks-quote .first-last-quotes { 15 | display: inline-block; 16 | font-size: 36px; 17 | line-height: 24px; 18 | } 19 | 20 | .wp-block-guty-blocks-quote .author { 21 | display: inline-block; 22 | font-variant: small-caps; 23 | color: #646464; 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/react-view/components/Editor.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const buttonStyles = { 4 | padding: '.5em', 5 | margin: '.5em .5em 0 0', 6 | background: 'white', 7 | border: '1px solid #222', 8 | cursor: 'pointer' 9 | } 10 | 11 | const buttonStyles_selected = Object.assign({}, buttonStyles, {background: '#faf'}) 12 | 13 | export default class Editor extends React.Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | currentPage: 1, 18 | totalPages: null, 19 | results: [], 20 | } 21 | this.fetchSomePosts = this.fetchSomePosts.bind(this); 22 | this.pageUp = this.pageUp.bind(this); 23 | this.pageBack = this.pageBack.bind(this); 24 | } 25 | 26 | componentDidMount() { 27 | this.fetchSomePosts(1); 28 | } 29 | 30 | fetchSomePosts(pageIndex) { 31 | fetch(`/wp-json/wp/v2/posts/?per_page=30&page=${pageIndex}`) 32 | .then((res) => { 33 | 34 | !this.state.totalPages && this.setState({ 35 | totalPages: res.headers.get('X-WP-TotalPages') 36 | }) 37 | 38 | return res.json() 39 | }) 40 | .then((data) => { 41 | console.log(data); 42 | this.setState({ 43 | results: data, 44 | currentPage: pageIndex 45 | }) 46 | }); 47 | } 48 | 49 | pageUp() { 50 | let nextPage = this.state.currentPage + 1; 51 | this.fetchSomePosts(nextPage); 52 | } 53 | 54 | pageBack() { 55 | let previousPage = this.state.currentPage - 1; 56 | this.fetchSomePosts(previousPage); 57 | } 58 | 59 | 60 | render() { 61 | 62 | const { results } = this.state; 63 | const { selectedPostIds, togglePost } = this.props; 64 | 65 | return ( 66 | <div style={{overflow: 'auto'}}> 67 | <strong>Select related posts:</strong> 68 | <p>Showing page {this.state.currentPage} of {this.state.totalPages} pages</p> 69 | <ul style={{padding: '1em 0'}}> 70 | { 71 | this.state.results.map((el, i) => { 72 | 73 | let isSelected = !(selectedPostIds.indexOf(el.id) === -1); 74 | 75 | return ( 76 | <li 77 | key={el.id} 78 | style={{display: 'inline-block'}}> 79 | <button 80 | style={isSelected ? buttonStyles_selected : buttonStyles} 81 | onClick={() => togglePost(el.id)} 82 | >{el.title.rendered}</button> 83 | </li> 84 | ); 85 | }) 86 | } 87 | </ul> 88 | { 89 | this.state.currentPage > 1 && 90 | <button onClick={this.pageBack}>Previous 20</button> 91 | } 92 | { 93 | this.state.currentPage < this.state.totalPages && 94 | <button onClick={this.pageUp} style={{float: 'right'}}>Next 20</button> 95 | } 96 | </div> 97 | ); 98 | } 99 | } -------------------------------------------------------------------------------- /src/react-view/react-view.editor.css: -------------------------------------------------------------------------------- 1 | /* .wp-block-guty-blocks-hello-world { 2 | background: cadetblue; 3 | color: white; 4 | } */ 5 | 6 | -------------------------------------------------------------------------------- /src/react-view/react-view.src.js: -------------------------------------------------------------------------------- 1 | import './react-view.editor.css'; 2 | import './react-view.view.scss'; 3 | 4 | import Editor from './components/Editor.jsx' 5 | 6 | const { 7 | registerBlockType, 8 | } = wp.blocks; 9 | 10 | registerBlockType('guty-blocks/react-view', { 11 | title: 'React View Test', 12 | icon: 'welcome-write-blog', 13 | category: 'common', 14 | 15 | attributes: { // Somewhat like setting initial state in a react app 16 | selectedPostIds: { 17 | type: 'array', 18 | default: [] 19 | }, 20 | }, 21 | 22 | // The editor "render" function 23 | edit(props) { 24 | const { 25 | className, 26 | setAttributes, 27 | attributes: { 28 | selectedPostIds, 29 | posts 30 | } 31 | } = props; 32 | 33 | function togglePost( postId ) { 34 | const index = selectedPostIds.indexOf(postId); 35 | if (index === -1) { 36 | setAttributes({ 37 | selectedPostIds: [ ...selectedPostIds, postId ] 38 | }); 39 | } else { 40 | setAttributes({ 41 | selectedPostIds: selectedPostIds.filter((el, i) => i !== index), 42 | }) 43 | } 44 | } 45 | 46 | return ( 47 | <div 48 | className={className} 49 | id={'live-react'} 50 | data-post-ids={ JSON.stringify( selectedPostIds ) } 51 | > 52 | <Editor 53 | selectedPostIds={selectedPostIds} 54 | togglePost={togglePost} 55 | /> 56 | </div> 57 | ); 58 | }, 59 | 60 | // The save "render" function 61 | save(props) { 62 | const { 63 | className, 64 | attributes: { selectedPostIds } 65 | } = props; 66 | 67 | return ( 68 | <div 69 | className={className} 70 | id={'live-react'} 71 | data-post-ids={ JSON.stringify( selectedPostIds ) } 72 | > 73 | Javascript must be enabled to view this block. 74 | </div> 75 | ); 76 | } 77 | 78 | }); -------------------------------------------------------------------------------- /src/react-view/react-view.view.js: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | // import ReactDOM from 'react-dom'; 3 | 4 | /** 5 | * Import HTML entity encoder/decoder. 6 | * 7 | * Lodash has it's own "unescape" entity decoder, but it is not as "robust" as he. 8 | * 9 | * @link https://lodash.com/docs/4.17.5#unescape 10 | * @link https://github.com/mathiasbynens/he 11 | */ 12 | import he from 'he'; 13 | 14 | class ReactLive extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = { 18 | postsIds: JSON.parse( this.props.posts ), 19 | posts: [] 20 | } 21 | 22 | this.fetchPosts = this.fetchPosts.bind(this); 23 | this.createMarkup = this.createMarkup.bind(this); 24 | } 25 | 26 | componentDidMount() { 27 | this.fetchPosts(); 28 | } 29 | 30 | fetchPosts() { 31 | fetch('/wp-json/wp/v2/posts/' + '?include[]=' + this.state.postsIds.join('&include[]=')) 32 | .then((res) => res.json()) 33 | .then((json) => { 34 | this.setState({ 35 | posts: json 36 | }) 37 | }); 38 | } 39 | 40 | /** 41 | * Do not expect REST API to return plain old strings for everything. 42 | * It is common and expected that WP content from the REST API will contain markup of one kind or another, 43 | * depending on the field. 44 | * 45 | * @link https://github.com/WP-API/WP-API/issues/1227 46 | * 47 | * @param string 48 | * @returns {{__html}} 49 | */ 50 | createMarkup( string ) { 51 | return { __html: he.decode( string ).trim() }; 52 | }; 53 | 54 | render() { 55 | return ( 56 | <div> 57 | <strong> 58 | React is running live in the view. It takes the ids of the posts from the saved div in the editor and fetches the post content from the REST API to render below: 59 | </strong> 60 | <ul> 61 | {!this.state.posts.length ? 62 | <p>Loading...</p> 63 | : 64 | this.state.posts.map((el, i) => { 65 | return ( 66 | <li> 67 | <h3 dangerouslySetInnerHTML={ this.createMarkup( el.title.rendered ) } /> 68 | <div dangerouslySetInnerHTML={ this.createMarkup( el.excerpt.rendered ) } /> 69 | </li> 70 | ) 71 | }) 72 | } 73 | </ul> 74 | </div> 75 | ); 76 | } 77 | } 78 | 79 | window.onload = function () { 80 | let container = document.getElementById('live-react'); 81 | if (container) { 82 | let postData = container.getAttribute('data-post-ids') 83 | ReactDOM.render( 84 | <ReactLive posts={postData}/>, 85 | container 86 | ) 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /src/react-view/react-view.view.scss: -------------------------------------------------------------------------------- 1 | // @import 'test'; 2 | 3 | 4 | .wp-block-guty-blocks-hello-world { 5 | 6 | } -------------------------------------------------------------------------------- /src/recent-posts/recent-posts.editor.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-recent-posts { 2 | } 3 | 4 | .articleContainer { 5 | display: grid; 6 | } 7 | 8 | .one_up { 9 | grid-template-columns: repeat(1, 1fr); 10 | } 11 | .two_up { 12 | grid-template-columns: repeat(2, 1fr); 13 | } 14 | .three_up { 15 | grid-template-columns: repeat(3, 1fr); 16 | } 17 | 18 | .articleContainer > * { 19 | grid-column: auto; 20 | margin: 8px; 21 | padding: 1em; 22 | border: 1px solid grey; 23 | } 24 | -------------------------------------------------------------------------------- /src/recent-posts/recent-posts.src.js: -------------------------------------------------------------------------------- 1 | import './recent-posts.editor.css'; 2 | import './recent-posts.view.css'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | Editable, // Text field - will be replaced by RichText in future updates 10 | InspectorControls, // allows us to add controls on the sidebar 11 | } = wp.editor; 12 | 13 | registerBlockType('guty-blocks/recent-posts', { 14 | title: 'Recent Posts Block', 15 | icon: 'welcome-write-blog', 16 | category: 'common', 17 | 18 | attributes: { // Somewhat like setting initial state in a react app 19 | numberUp: { 20 | type: 'string', 21 | default: '1_up' 22 | }, 23 | posts: { 24 | type: 'array', 25 | default: [] 26 | } 27 | }, 28 | 29 | // The editor "render" function 30 | edit(props) { 31 | 32 | let { content, posts, numberUp } = props.attributes; 33 | 34 | function onChangeContent(updatedContent) { 35 | props.setAttributes({ content: updatedContent }); 36 | } 37 | function onChangeNumberUp(newNumberUp) { 38 | props.setAttributes({ numberUp: newNumberUp.target.value }); 39 | } 40 | 41 | async function fetchArticles() { 42 | let data = await fetch('/wp-json/wp/v2/posts/'); 43 | let posts = await data.json(); 44 | props.setAttributes({ posts }); 45 | } 46 | 47 | // Actual elements being rendered 48 | return ([ 49 | !!focus && ( 50 | <InspectorControls key="controls"> 51 | Select Layout: 52 | <select 53 | onChange={onChangeNumberUp}> 54 | <option value="one_up">1 up blocks</option> 55 | <option value="two_up">2 up blocks</option> 56 | <option value="three_up">3 up blocks</option> 57 | </select> 58 | 59 | </InspectorControls> 60 | ), 61 | <div className={props.className}> 62 | <div className="sysMessage"> 63 | <button onClick={fetchArticles}>Fetch me recent articles!</button> 64 | {posts.length === 0 ? 65 | <h1>Posts not pulled yet</h1> 66 | : 67 | <h1>{posts.length} found!</h1>} 68 | </div> 69 | <div className={`articleContainer ${numberUp}`}> 70 | {posts && posts.map((el) => { 71 | return <RenderArticleButton 72 | title={el.title.rendered} 73 | desc={el.excerpt.rendered} 74 | url={el.link} 75 | /> 76 | })} 77 | </div> 78 | </div> 79 | ]); 80 | }, 81 | 82 | // The save "render" function 83 | save(props) { 84 | 85 | let { posts, numberUp } = props.attributes; 86 | 87 | return ( 88 | <div className={props.className}> 89 | <div className={`articleContainer ${numberUp}`}> 90 | {posts && posts.map((el) => { 91 | return <RenderArticleButton 92 | title={el.title.rendered} 93 | desc={el.excerpt.rendered} 94 | url={el.link} 95 | /> 96 | })} 97 | </div> 98 | </div> 99 | ); 100 | } 101 | 102 | }); 103 | 104 | function RenderArticleButton(props) { 105 | return ( 106 | <div class="articleButton"> 107 | <h3 dangerouslySetInnerHTML={{ __html: props.title }}></h3> 108 | <p dangerouslySetInnerHTML={{ __html: props.desc }}></p> 109 | <a href={props.url}>Go to article...</a> 110 | </div> 111 | ) 112 | } -------------------------------------------------------------------------------- /src/recent-posts/recent-posts.view.css: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-recent-posts { 2 | } 3 | 4 | .articleContainer { 5 | display: grid; 6 | } 7 | 8 | .one_up { 9 | grid-template-columns: repeat(1, 1fr); 10 | } 11 | .two_up { 12 | grid-template-columns: repeat(2, 1fr); 13 | } 14 | .three_up { 15 | grid-template-columns: repeat(3, 1fr); 16 | } 17 | 18 | .articleContainer > * { 19 | grid-column: auto; 20 | margin: 8px; 21 | padding: 1em; 22 | border: 1px solid grey; 23 | } 24 | -------------------------------------------------------------------------------- /src/side-by-side/side-by-side.editor.scss: -------------------------------------------------------------------------------- 1 | .wp-block-guty-blocks-side-by-side { 2 | 3 | .left-image { 4 | border: 2px solid transparent; 5 | transition: border 200ms; 6 | } 7 | .left-image:hover { 8 | cursor: pointer; 9 | border: 2px solid green; 10 | } 11 | 12 | .left-image:active { 13 | border: 2px solid lightgreen; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/side-by-side/side-by-side.src.js: -------------------------------------------------------------------------------- 1 | import './side-by-side.editor.scss'; 2 | import './side-by-side.view.scss'; 3 | 4 | const { 5 | registerBlockType, 6 | } = wp.blocks; 7 | 8 | const { 9 | RichText, 10 | MediaUpload 11 | } = wp.editor; 12 | 13 | registerBlockType('guty-blocks/side-by-side', { 14 | title: 'Side by side block', 15 | icon: '', 16 | category: 'common', 17 | 18 | attributes: { 19 | selectedImage: { 20 | type: 'string', 21 | default: 'http://placehold.it/200x200' 22 | }, 23 | content: { 24 | type: 'array', 25 | source: 'children', 26 | selector: 'p' 27 | } 28 | }, 29 | 30 | edit(props) { 31 | const { className, setAttributes } = props; 32 | const { selectedImage, content, isSelected } = props.attributes; 33 | 34 | function changeText(changes) { 35 | setAttributes({ content: changes }); 36 | } 37 | 38 | return ( 39 | <div className={className}> 40 | <div className='left'> 41 | <MediaUpload 42 | onSelect={ image => { 43 | const newImage = image.sizes.medium || image.sizes.thumbnail; 44 | const url = newImage.url; 45 | setAttributes({ selectedImage: url }) 46 | }} 47 | type="image" 48 | value={selectedImage} 49 | render={ ({ open } ) =>( 50 | <div 51 | className="left-image" 52 | onClick={open} 53 | style={{backgroundImage: `url(${selectedImage})`}}> 54 | </div> 55 | )} 56 | /> 57 | </div> 58 | <div className='right'> 59 | <RichText 60 | tagName="p" 61 | value={content} 62 | onChange={changeText} 63 | placeholder='Enter text here...' 64 | isSelected={isSelected} 65 | /> 66 | </div> 67 | </div> 68 | ); 69 | }, 70 | 71 | save(props) { 72 | const { className } = props; 73 | const { selectedImage, content } = props.attributes; 74 | 75 | return ( 76 | <div className={className}> 77 | <div className='left'> 78 | <div 79 | className="left-image" 80 | onClick={open} 81 | style={{backgroundImage: `url(${selectedImage})`}} 82 | > 83 | </div> 84 | </div> 85 | <div className='right'> 86 | <p> 87 | {content} 88 | </p> 89 | </div> 90 | </div> 91 | ); 92 | } 93 | 94 | }); -------------------------------------------------------------------------------- /src/side-by-side/side-by-side.view.scss: -------------------------------------------------------------------------------- 1 | // @import 'test'; 2 | .wp-block-guty-blocks-side-by-side { 3 | margin: auto; 4 | max-width: 30em; 5 | display: flex; 6 | flex-direction: row; 7 | .left { 8 | width: 30%; 9 | padding: 2em 0; 10 | display: flex; 11 | justify-content: flex-end; 12 | align-items: center; 13 | 14 | .left-image { 15 | position: relative; 16 | width: 100%; 17 | padding-top:100%; 18 | border-radius: 20%; 19 | overflow: hidden; 20 | background-size: cover; 21 | background-position: center; 22 | box-shadow: 0 0 20px rgba(0,0,0,.3); 23 | } 24 | 25 | } 26 | .right { 27 | width: 70%; 28 | padding: 2em 1em; 29 | display: flex; 30 | justify-content: flex-start; 31 | align-items: center; 32 | 33 | p { 34 | margin: 0; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 3 | const glob = require('glob'); 4 | 5 | // different instances for editor or view css files 6 | const editorExtractTextPlugin = new ExtractTextPlugin("[name]/[name].editor.css"); 7 | const viewExtractTextPlugin = new ExtractTextPlugin("[name]/[name].view.css"); 8 | 9 | module.exports = [{ 10 | entry: function () { 11 | /* 12 | function to map globs to appropriate entries for separate build files 13 | object that is returned looks like: 14 | { 15 | block-name: 'src/block-name/block-name.js', 16 | ... 17 | } 18 | */ 19 | let entriesObject = {} 20 | glob.sync("./src/**/*.src.js").map((el) => { 21 | let path = el; 22 | let name = el.split('/').pop().split('.')[0]; 23 | entriesObject[name] = path; 24 | }) 25 | return entriesObject; 26 | }, 27 | // { 28 | // 'media-block': './src/media-block/media-block.js', 29 | // 'image-hero': './src/image-hero/image-hero.js', 30 | // 'recent-posts': './src/recent-posts/recent-posts.js', 31 | // 'hello-world': './src/hello-world/hello-world.js' 32 | // }, 33 | output: { 34 | path: path.resolve(__dirname, 'blocks'), 35 | filename: '[name]/[name].build.js' 36 | }, 37 | module: { 38 | rules: [{ 39 | test: /\.editor.css$/, 40 | use: editorExtractTextPlugin.extract({ 41 | fallback: 'style-loader', 42 | use: 'css-loader' 43 | }) 44 | }, 45 | { 46 | test: /\.view.css$/, 47 | use: viewExtractTextPlugin.extract({ 48 | fallback: 'style-loader', 49 | use: 'css-loader' 50 | }) 51 | }, 52 | { 53 | test: /\.editor.scss$/, 54 | use: editorExtractTextPlugin.extract({ 55 | use: [{ 56 | loader: 'css-loader' 57 | }, { 58 | loader: 'sass-loader' 59 | }], 60 | fallback: 'style-loader' 61 | }) 62 | }, 63 | { 64 | test: /\.view.scss$/, 65 | use: viewExtractTextPlugin.extract({ 66 | use: [{ 67 | loader: 'css-loader' 68 | }, { 69 | loader: 'sass-loader' 70 | }], 71 | fallback: 'style-loader' 72 | }) 73 | }, 74 | { 75 | test: /\.jsx?$/, 76 | loader: 'babel-loader' 77 | } 78 | ] 79 | }, 80 | plugins: [ 81 | editorExtractTextPlugin, 82 | viewExtractTextPlugin 83 | ], 84 | stats: { 85 | colors: true 86 | } 87 | }, 88 | // Config for view javacsript files 89 | { 90 | entry: function () { 91 | /* 92 | function to map globs to appropriate entries for separate build files 93 | object that is returned looks like: 94 | { 95 | block-name: 'src/block-name/block-name.js', 96 | ... 97 | } 98 | */ 99 | let entriesObject = {} 100 | glob.sync("./src/**/*.view.js").map((el) => { 101 | let path = el; 102 | let name = el.split('/').pop().split('.')[0]; 103 | entriesObject[name] = path; 104 | }) 105 | return entriesObject; 106 | }, 107 | output: { 108 | path: path.resolve(__dirname, 'blocks'), 109 | filename: '[name]/[name].view.js' 110 | }, 111 | module: { 112 | rules: [{ 113 | test: /.view.js/, 114 | loader: 'babel-loader' 115 | }] 116 | }, 117 | stats: { 118 | colors: true 119 | } 120 | 121 | } 122 | ]; --------------------------------------------------------------------------------