├── .prettierrc ├── .prettierignore ├── babel.config.js ├── src ├── blocks │ ├── i18n.js │ ├── block-toggles │ │ ├── attributes.js │ │ ├── components │ │ │ └── inspector.js │ │ └── save.js │ ├── block-sharing │ │ ├── styles │ │ │ └── editor.scss │ │ ├── components │ │ │ └── sharing.js │ │ └── index.js │ ├── block-posts-grid │ │ ├── deprecated │ │ │ ├── index.js │ │ │ └── save.deprecated.js │ │ ├── save.js │ │ ├── edit.js │ │ └── index.js │ ├── block-grid-container │ │ ├── deprecated │ │ │ ├── index.js │ │ │ └── save.deprecated.js │ │ ├── save.js │ │ └── attributes.js │ ├── block-image-carousel │ │ ├── deprecated │ │ │ └── index.js │ │ ├── attributes.js │ │ ├── components │ │ │ └── swap-slide-toolbar.js │ │ └── index.js │ ├── block-horizontal-tabs │ │ ├── attributes.js │ │ ├── styles │ │ │ └── style.scss │ │ ├── components │ │ │ └── horizontal-tab │ │ │ │ └── index.php │ │ ├── save.js │ │ └── index.js │ ├── block-vertical-tabs │ │ ├── attributes.js │ │ ├── components │ │ │ ├── vertical-align-toolbar.js │ │ │ ├── vertical-tab │ │ │ │ └── index.php │ │ │ └── inspector.js │ │ ├── styles │ │ │ ├── style.scss │ │ │ └── editor.scss │ │ ├── save.js │ │ └── index.js │ ├── block-carousel │ │ ├── deprecated │ │ │ ├── index.js │ │ │ ├── 2.0 │ │ │ │ └── save.deprecated.js │ │ │ ├── 3.0 │ │ │ │ └── save.deprecated.js │ │ │ └── 1.0 │ │ │ │ └── save.deprecated.js │ │ ├── attributes.js │ │ ├── save.js │ │ └── components │ │ │ └── swap-slide-toolbar.js │ ├── block-column-container │ │ ├── save.js │ │ └── attributes.js │ ├── block-heading │ │ ├── attributes.js │ │ ├── components │ │ │ ├── heading-toolbar.js │ │ │ ├── custom-heading.js │ │ │ └── subheading-toolbar.js │ │ ├── index.js │ │ └── save.js │ ├── block-cta │ │ ├── attributes.js │ │ ├── save.js │ │ └── index.js │ └── block-post-grid │ │ └── index.js ├── update-category │ ├── register-stores.js │ ├── register-filters.js │ ├── index.js │ ├── deregister-blocks.js │ └── register-styles.js ├── vendor.js ├── register.js ├── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-regular-400.eot │ ├── fa-regular-400.ttf │ ├── fa-solid-900.eot │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ ├── fa-solid-900.woff2 │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.woff │ └── fa-regular-400.woff2 ├── block-colors.scss ├── extend │ ├── index.js │ ├── font-colors │ │ └── editor.scss │ ├── space-settings-inspector │ │ ├── editor.scss │ │ ├── index.test.js │ │ └── utils.js │ ├── check-core-block.js │ ├── show-hide-inspector │ │ └── editor.scss │ └── width-settings │ │ ├── index.test.js │ │ └── utils.js ├── components │ ├── block-selector │ │ ├── editor.scss │ │ └── index.js │ ├── large-modal │ │ ├── index.js │ │ └── editor.scss │ ├── settings-spacer │ │ └── index.js │ ├── templates-modal │ │ ├── page-templates │ │ │ ├── page-types.js │ │ │ └── page-templates.js │ │ ├── reusable-button.js │ │ ├── section-button.js │ │ ├── page-layout-button.js │ │ └── section-templates │ │ │ └── templates │ │ │ ├── inform-map-embed-focus.js │ │ │ ├── introduce-page-title.js │ │ │ └── watch-embedded-video.js │ ├── color-appender │ │ └── editor.scss │ ├── custom-palette │ │ └── editor.scss │ ├── pause-toolbar │ │ └── index.js │ ├── show-hide-toolbar │ │ └── index.js │ ├── width-toolbar │ │ └── index.js │ ├── preview-panel │ │ └── index.js │ ├── vertical-alignment-toolbar │ │ └── index.js │ ├── youtube-api-toggle │ │ └── index.js │ ├── remove-button │ │ └── index.js │ └── resizable-box │ │ └── index.js ├── stores │ └── twitter │ │ ├── controls.js │ │ ├── prepare-query.js │ │ ├── selectors.js │ │ ├── index.js │ │ ├── actions.js │ │ ├── reducer.js │ │ └── resolvers.js ├── frontend.js ├── editor.js └── blocks.js ├── composer.json ├── languages └── en.mo ├── .husky ├── pre-push └── pre-commit ├── assets ├── c9-logo.png ├── wood-tile.jpg ├── screenshot-1.jpg ├── screenshot-2.jpg ├── screenshot-3.jpg ├── screenshot-4.jpg ├── screenshot-5.jpg ├── screenshot-6.jpg ├── device-previews.png ├── grid-screenshot.jpg ├── hero-screenshot.jpg ├── using-templates.jpg ├── drag-drop-section.jpg ├── reusable-layouts.jpg ├── change-number-rows.jpg ├── change-overall-width.jpg ├── pick-column-layout.png ├── building-from-scratch.jpg ├── c9-grid-block-preview.jpg ├── change-number-columns.jpg ├── building-from-templates.jpg ├── c9-admin-dashboard-preview.jpg ├── c9-admin-plugin-screenshot.jpg ├── c9-blocks-netflix-tutorial.jpg ├── responsive-landing-2020-theme.jpg ├── awareness-consideration-conversion.jpg ├── page-template-icons │ └── hero-screenshot.jpg ├── section-template-icons │ ├── 1882E2F807C6F8D2.png │ ├── 1882E2F807C6F8D6.jpg │ ├── 8BB68308AC383C1E.jpg │ ├── about-picture-contact-card.svg │ ├── single-profile.svg │ └── classic-header-statement-logo.svg ├── icon-c9-heading.svg ├── c9-trash.svg ├── fa-icons │ ├── facebook-square-brands.svg │ ├── chevron-circle-right-solid.svg │ ├── linkedin-in-brands.svg │ ├── twitter-square-brands.svg │ ├── instagram-brands.svg │ └── tiktok.svg ├── icon-carousel-next.svg ├── icon-carousel-prev.svg ├── icon-c9-subheading-enable-toolbar-icon.js ├── icon-c9-grid.svg ├── toggle-icon.svg ├── icon-c9-tabs-horizontal.svg ├── icon-c9-cta-bar.svg ├── icon-c9-tabs-vertical-tabs.svg ├── toggle-open.js ├── c9-feather-logo-gray.svg ├── icon-c9-toggles.svg ├── icon-c9-anything-carousel.svg ├── c9-feather-logo-icon.js ├── icon-c9-image-carousel.svg ├── icon-c9-post-grid.svg ├── tab-icons.js ├── icon-c9-social-share.svg └── c9-feather-logo-gradient.svg ├── jest.setup.js ├── .distignore ├── .eslintignore ├── CHANGELOG.md ├── phpunit.xml.dist ├── jest.config.js ├── .editorconfig ├── phpcs.xml.dist ├── config ├── babel-preset.js ├── externals.js └── paths.js ├── tests ├── test-class-check-dist-files-exist.php ├── bootstrap.php └── test-class-layout-endpoints.php ├── .eslintrc.json ├── .travis.yml ├── plugin.php └── dist └── blocks.frontend.build.js /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/**/*.js 2 | dist/*.js 3 | dist/*.css 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ['@babel/preset-env'] }; 2 | -------------------------------------------------------------------------------- /src/blocks/i18n.js: -------------------------------------------------------------------------------- 1 | wp.i18n.setLocaleData({ '': {} }, 'c9-blocks'); 2 | -------------------------------------------------------------------------------- /src/update-category/register-stores.js: -------------------------------------------------------------------------------- 1 | // import "../stores/twitter"; 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "wp-cli/i18n-command": "^2.4" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /languages/en.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/languages/en.mo -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /assets/c9-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/c9-logo.png -------------------------------------------------------------------------------- /assets/wood-tile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/wood-tile.jpg -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | global.wp = { 2 | i18n: { 3 | __: jest.fn((text) => text), 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /.distignore: -------------------------------------------------------------------------------- 1 | /.wordpress-org 2 | /.git 3 | /.github 4 | /node_modules 5 | 6 | .distignore 7 | .gitignore -------------------------------------------------------------------------------- /assets/screenshot-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-1.jpg -------------------------------------------------------------------------------- /assets/screenshot-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-2.jpg -------------------------------------------------------------------------------- /assets/screenshot-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-3.jpg -------------------------------------------------------------------------------- /assets/screenshot-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-4.jpg -------------------------------------------------------------------------------- /assets/screenshot-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-5.jpg -------------------------------------------------------------------------------- /assets/screenshot-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/screenshot-6.jpg -------------------------------------------------------------------------------- /src/vendor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import bootstrap styles. 3 | */ 4 | import './vendor/c9-bootstrap.css'; 5 | -------------------------------------------------------------------------------- /assets/device-previews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/device-previews.png -------------------------------------------------------------------------------- /assets/grid-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/grid-screenshot.jpg -------------------------------------------------------------------------------- /assets/hero-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/hero-screenshot.jpg -------------------------------------------------------------------------------- /assets/using-templates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/using-templates.jpg -------------------------------------------------------------------------------- /src/register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Register block category + side menu. 3 | */ 4 | import './update-category'; 5 | -------------------------------------------------------------------------------- /assets/drag-drop-section.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/drag-drop-section.jpg -------------------------------------------------------------------------------- /assets/reusable-layouts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/reusable-layouts.jpg -------------------------------------------------------------------------------- /assets/change-number-rows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/change-number-rows.jpg -------------------------------------------------------------------------------- /assets/change-overall-width.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/change-overall-width.jpg -------------------------------------------------------------------------------- /assets/pick-column-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/pick-column-layout.png -------------------------------------------------------------------------------- /src/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /src/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /src/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /src/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /src/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /src/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /src/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /src/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /src/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /assets/building-from-scratch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/building-from-scratch.jpg -------------------------------------------------------------------------------- /assets/c9-grid-block-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/c9-grid-block-preview.jpg -------------------------------------------------------------------------------- /assets/change-number-columns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/change-number-columns.jpg -------------------------------------------------------------------------------- /src/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /src/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /src/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/src/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.min.js 2 | **/node_modules/** 3 | **/vendor/** 4 | build 5 | coverage 6 | cypress 7 | node_modules 8 | vendor -------------------------------------------------------------------------------- /assets/building-from-templates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/building-from-templates.jpg -------------------------------------------------------------------------------- /assets/c9-admin-dashboard-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/c9-admin-dashboard-preview.jpg -------------------------------------------------------------------------------- /assets/c9-admin-plugin-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/c9-admin-plugin-screenshot.jpg -------------------------------------------------------------------------------- /assets/c9-blocks-netflix-tutorial.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/c9-blocks-netflix-tutorial.jpg -------------------------------------------------------------------------------- /assets/responsive-landing-2020-theme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/responsive-landing-2020-theme.jpg -------------------------------------------------------------------------------- /assets/awareness-consideration-conversion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/awareness-consideration-conversion.jpg -------------------------------------------------------------------------------- /assets/page-template-icons/hero-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/page-template-icons/hero-screenshot.jpg -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | Based on https://keepachangelog.com/en/0.3.0/ 3 | 4 | ## [Unreleased] 5 | 6 | Just added changelog. No version numbers yet. -------------------------------------------------------------------------------- /assets/section-template-icons/1882E2F807C6F8D2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/section-template-icons/1882E2F807C6F8D2.png -------------------------------------------------------------------------------- /assets/section-template-icons/1882E2F807C6F8D6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/section-template-icons/1882E2F807C6F8D6.jpg -------------------------------------------------------------------------------- /assets/section-template-icons/8BB68308AC383C1E.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covertnine/c9-blocks/HEAD/assets/section-template-icons/8BB68308AC383C1E.jpg -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx pretty-quick --staged && npm run lint:fix 5 | npm run build 6 | git add . -------------------------------------------------------------------------------- /src/block-colors.scss: -------------------------------------------------------------------------------- 1 | // Colors. 2 | 3 | $black: rgba(41, 41, 41, 0.15); 4 | $white: #f4f4f4; 5 | $gray: #dedede; 6 | $green: #bada55; 7 | $red: orangered; 8 | -------------------------------------------------------------------------------- /src/extend/index.js: -------------------------------------------------------------------------------- 1 | //import "./font-colors"; 2 | import './show-hide-inspector'; 3 | import './space-settings-inspector'; 4 | //import './width-settings'; 5 | -------------------------------------------------------------------------------- /src/components/block-selector/editor.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Editor Styles 3 | */ 4 | .is-hovered .block-selector, 5 | .is-selected .block-selector { 6 | visibility: visible; 7 | opacity: 1; 8 | } 9 | -------------------------------------------------------------------------------- /src/blocks/block-toggles/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | toggleCount: { 6 | type: 'number', 7 | default: 2, 8 | }, 9 | instanceId: { 10 | type: 'number', 11 | }, 12 | }; 13 | 14 | export default attributes; 15 | -------------------------------------------------------------------------------- /src/stores/twitter/controls.js: -------------------------------------------------------------------------------- 1 | const { apiFetch } = wp; 2 | 3 | export function API_FETCH({ request }) { 4 | return apiFetch(request).then((fetchedData) => { 5 | if (fetchedData && fetchedData.success && fetchedData.response) { 6 | return fetchedData.response; 7 | } 8 | return false; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/large-modal/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Modal } = wp.components; 5 | 6 | const LargeModal = (props) => ( 7 | 8 | {props.children} 9 | 10 | ); 11 | 12 | export default LargeModal; 13 | -------------------------------------------------------------------------------- /src/stores/twitter/prepare-query.js: -------------------------------------------------------------------------------- 1 | import qs from 'qs'; 2 | 3 | export default function prepareQuery(type, data) { 4 | const additionalData = qs.stringify(data, { encode: false }); 5 | const query = `/c9-blocks/get_twitter_${type}/${ 6 | additionalData ? `?${additionalData}` : '' 7 | }`; 8 | 9 | return query; 10 | } 11 | -------------------------------------------------------------------------------- /src/extend/font-colors/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-ext-badge { 2 | position: relative; 3 | left: 8px; 4 | padding: 1px 3px; 5 | font-size: 8px; 6 | font-weight: 700; 7 | color: #a9a9a9; 8 | text-transform: uppercase; 9 | background-color: #f3f3f3; 10 | border-radius: 2px; 11 | transition: 0.15s background-color, 0.15s color; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/settings-spacer/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styles 3 | */ 4 | import styled from 'styled-components'; 5 | 6 | const StyledSpacer = styled.div` 7 | height: 15px; 8 | border-top: 1px solid #e2e4e7; 9 | margin: 0 -16px; 10 | `; 11 | 12 | const SettingsSpacer = () => ; 13 | 14 | export default SettingsSpacer; 15 | -------------------------------------------------------------------------------- /src/extend/space-settings-inspector/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-ext-badge { 2 | position: relative; 3 | left: 8px; 4 | padding: 1px 3px; 5 | font-size: 8px; 6 | font-weight: 700; 7 | color: #a9a9a9; 8 | text-transform: uppercase; 9 | background-color: #f3f3f3; 10 | border-radius: 2px; 11 | transition: 0.15s background-color, 0.15s color; 12 | } 13 | -------------------------------------------------------------------------------- /src/blocks/block-sharing/styles/editor.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Editor styles for the admin 3 | */ 4 | /* compatibility with wp 6.4.3 */ 5 | .wp-block[data-type='c9-blocks/social-share'] .fa-facebook-f:before { 6 | content: '\f09a'; 7 | } 8 | 9 | .c9-block-sharing { 10 | margin-bottom: 0; 11 | } 12 | 13 | .c9-block-sharing .c9-share-list a:hover { 14 | color: #fff; 15 | } 16 | -------------------------------------------------------------------------------- /src/stores/twitter/selectors.js: -------------------------------------------------------------------------------- 1 | import prepareQuery from './prepare-query'; 2 | 3 | export function getTwitterFeed(state, data) { 4 | const query = prepareQuery('feed', data); 5 | 6 | return state.feeds[query]; 7 | } 8 | 9 | export function getTwitterProfile(state, data) { 10 | const query = prepareQuery('profile', data); 11 | 12 | return state.profiles[query]; 13 | } 14 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/blocks/block-posts-grid/deprecated/index.js: -------------------------------------------------------------------------------- 1 | import SaveDeprecated from './save.deprecated'; 2 | import attributes from '../attributes'; 3 | 4 | export const BlockPostsGridDeprecated = (props) => ( 5 | 6 | ); 7 | 8 | const Deprecated = [ 9 | { 10 | attributes: attributes, 11 | save: BlockPostsGridDeprecated, 12 | }, 13 | ]; 14 | 15 | export default Deprecated; 16 | -------------------------------------------------------------------------------- /assets/icon-c9-heading.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 231 -------------------------------------------------------------------------------- /src/stores/twitter/index.js: -------------------------------------------------------------------------------- 1 | import reducer from './reducer'; 2 | import * as selectors from './selectors'; 3 | import * as actions from './actions'; 4 | import * as controls from './controls'; 5 | import * as resolvers from './resolvers'; 6 | 7 | const { registerStore } = wp.data; 8 | 9 | registerStore('c9-blocks/twitter', { 10 | reducer, 11 | selectors, 12 | actions, 13 | controls, 14 | resolvers, 15 | }); 16 | -------------------------------------------------------------------------------- /src/stores/twitter/actions.js: -------------------------------------------------------------------------------- 1 | export function apiFetch(request) { 2 | return { 3 | type: 'API_FETCH', 4 | request, 5 | }; 6 | } 7 | 8 | export function setTwitterFeed(query, feed) { 9 | return { 10 | type: 'SET_TWITTER_FEED', 11 | query, 12 | feed, 13 | }; 14 | } 15 | 16 | export function setTwitterProfile(query, profile) { 17 | return { 18 | type: 'SET_TWITTER_PROFILE', 19 | query, 20 | profile, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/frontend.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gutenberg Blocks Frontend JS 3 | * 4 | * All blocks frontend related JavaScript files should be imported here. 5 | * You can create a new block folder in this dir and include code 6 | * for that block here as well. 7 | * 8 | * All blocks should be included here since this is the file that 9 | * Webpack is compiling as the input file. 10 | */ 11 | 12 | import './blocks/block-grid-container/frontend.js'; 13 | -------------------------------------------------------------------------------- /src/extend/check-core-block.js: -------------------------------------------------------------------------------- 1 | const supportedBlocks = [ 2 | 'core/heading', 3 | 'core/quote', 4 | 'core/list', 5 | 'core/table', 6 | ]; 7 | 8 | /** 9 | * Check if core block may be extended. 10 | * 11 | * @param {String} name - block name. 12 | * 13 | * @return {Boolean} block supported. 14 | */ 15 | export default function checkCoreBlock(name) { 16 | return name && /^core/.test(name) && -1 !== supportedBlocks.indexOf(name); 17 | } 18 | -------------------------------------------------------------------------------- /src/update-category/register-filters.js: -------------------------------------------------------------------------------- 1 | const { addFilter } = wp.hooks; 2 | 3 | function formatClassName(className) { 4 | const result = className.replace('wp-block-c9-blocks', 'c9'); 5 | return result; 6 | } 7 | 8 | addFilter( 9 | 'c9-blocks.editor.className', 10 | 'c9-blocks/format-classname', 11 | formatClassName 12 | ); 13 | addFilter( 14 | 'c9-blocks.blocks.className', 15 | 'c9-blocks/format-classname', 16 | formatClassName 17 | ); 18 | -------------------------------------------------------------------------------- /src/blocks/block-toggles/components/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { InspectorControls } = wp.blockEditor; 6 | 7 | /** 8 | * Create an Inspector Controls wrapper Component 9 | */ 10 | export default class Inspector extends Component { 11 | constructor() { 12 | super(...arguments); 13 | } 14 | 15 | render() { 16 | return ; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/blocks/block-grid-container/deprecated/index.js: -------------------------------------------------------------------------------- 1 | import SaveDeprecated from './save.deprecated'; 2 | import attributes from '../attributes'; 3 | 4 | export const BlockGridContainerDeprecated = (props) => ( 5 | 6 | ); 7 | 8 | const Deprecated = [ 9 | { 10 | attributes: attributes, 11 | save: BlockGridContainerDeprecated, 12 | supports: { 13 | anchor: true, 14 | }, 15 | }, 16 | ]; 17 | 18 | export default Deprecated; 19 | -------------------------------------------------------------------------------- /src/extend/show-hide-inspector/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-ext-badge { 2 | position: relative; 3 | left: 8px; 4 | padding: 1px 3px; 5 | font-size: 8px; 6 | font-weight: 700; 7 | color: #a9a9a9; 8 | text-transform: uppercase; 9 | background-color: #f3f3f3; 10 | border-radius: 2px; 11 | transition: 0.15s background-color, 0.15s color; 12 | } 13 | 14 | .components-toolbar>div>.components-toolbar__control.components-button.has-icon .dashicon { 15 | margin-right: 2px; 16 | } 17 | -------------------------------------------------------------------------------- /src/update-category/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { updateCategory } = wp.blocks; 5 | 6 | /** 7 | * Icon 8 | */ 9 | import Icon from '../../assets/c9-feather-logo-icon'; 10 | 11 | if (updateCategory) { 12 | updateCategory('c9-blocks', { icon: Icon }); 13 | } 14 | 15 | import './deregister-blocks'; 16 | import './register-filters'; 17 | import './register-styles'; 18 | import './register-stores'; 19 | import '../components/sidebar'; 20 | -------------------------------------------------------------------------------- /src/extend/width-settings/index.test.js: -------------------------------------------------------------------------------- 1 | import { c9AlignConfig } from './utils'; 2 | 3 | describe('width settings extension should work correctly', () => { 4 | it('returns no alignment when align is unset', () => { 5 | expect(c9AlignConfig('container', '')).toStrictEqual(''); 6 | }); 7 | 8 | it('returns alignfull if all align is full and container is fluid', () => { 9 | expect(c9AlignConfig('container-fluid', 'full')).toStrictEqual('alignfull'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // Indicates whether the coverage information should be collected while executing the test 8 | collectCoverage: true, 9 | coverageDirectory: 'coverage', 10 | preset: '@wordpress/jest-preset-default', 11 | transform: { 12 | '\\.[jt]sx?$': 'babel-jest', 13 | }, 14 | setupFiles: ['./jest.setup.js'], 15 | }; 16 | -------------------------------------------------------------------------------- /src/extend/width-settings/utils.js: -------------------------------------------------------------------------------- 1 | export const c9AlignConfig = (containerWidth, align) => { 2 | let containerAlign = ''; 3 | if (align !== undefined && 0 != align.length) { 4 | if ('container' == containerWidth) { 5 | containerAlign = 'alignwide'; 6 | } else if ('container-fluid' == containerWidth) { 7 | containerAlign = 'alignfull'; 8 | } else if ('container-narrow' == containerWidth) { 9 | containerAlign = 'alignnarrow'; 10 | } 11 | } 12 | 13 | return containerAlign; 14 | }; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /assets/c9-trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/fa-icons/facebook-square-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/fa-icons/chevron-circle-right-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/templates-modal/page-templates/page-types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | awareness: 3 | 'Page templates for potential fans learning about your product, service, event for the first time. Give them the most information they need right upfront.', 4 | consideration: 5 | 'Page templates for likely fans that are somewhat familiar with you, but need some information or convincing.', 6 | conversion: 7 | 'Page templates for fans familiar with your product doing research before purchase or informed users ready to convert to customers.', 8 | }; 9 | -------------------------------------------------------------------------------- /src/blocks/block-posts-grid/deprecated/save.deprecated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Container from './container.deprecated'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { InnerBlocks } = wp.blockEditor; 11 | 12 | export default class Save extends Component { 13 | constructor() { 14 | super(...arguments); 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/stores/twitter/reducer.js: -------------------------------------------------------------------------------- 1 | function reducer(state = { feeds: {}, profiles: {} }, action) { 2 | switch (action.type) { 3 | case 'SET_TWITTER_FEED': 4 | if (!state.feeds[action.query] && action.feed) { 5 | state.feeds[action.query] = action.feed; 6 | } 7 | break; 8 | case 'SET_TWITTER_PROFILE': 9 | if (!state.profiles[action.query] && action.profile) { 10 | state.profiles[action.query] = action.profile; 11 | } 12 | break; 13 | // no default 14 | } 15 | 16 | return state; 17 | } 18 | 19 | export default reducer; 20 | -------------------------------------------------------------------------------- /assets/icon-carousel-next.svg: -------------------------------------------------------------------------------- 1 | icon-carousel-next -------------------------------------------------------------------------------- /assets/icon-carousel-prev.svg: -------------------------------------------------------------------------------- 1 | icon-carousel-prev -------------------------------------------------------------------------------- /src/blocks/block-grid-container/deprecated/save.deprecated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Container from './container.deprecated'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { InnerBlocks } = wp.blockEditor; 11 | 12 | export default class Save extends Component { 13 | constructor() { 14 | super(...arguments); 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /assets/fa-icons/linkedin-in-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/stores/twitter/resolvers.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions'; 2 | import prepareQuery from './prepare-query'; 3 | 4 | export function* getTwitterFeed(data) { 5 | const query = prepareQuery('feed', data); 6 | const feed = yield actions.apiFetch({ path: query }); 7 | 8 | return actions.setTwitterFeed(query, feed); 9 | } 10 | 11 | export function* getTwitterProfile(data) { 12 | const query = prepareQuery('profile', data); 13 | const profile = yield actions.apiFetch({ path: query }); 14 | 15 | return actions.setTwitterProfile(query, profile); 16 | } 17 | -------------------------------------------------------------------------------- /src/blocks/block-grid-container/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import ResizableContainer from './components/resizable-container'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { InnerBlocks } = wp.blockEditor; 11 | 12 | export default class Save extends Component { 13 | constructor() { 14 | super(...arguments); 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/block-selector/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styles 3 | */ 4 | import styled from 'styled-components'; 5 | 6 | const StyledSelector = styled.div` 7 | visibility: hidden; 8 | border: 2px dashed #aaa; 9 | font-size: 12px; 10 | text-align: center; 11 | cursor: pointer; 12 | text-transform: uppercase; 13 | font-weight: bold; 14 | color: #999; 15 | opacity: 0; 16 | transition: opacity 1s; 17 | `; 18 | 19 | const BlockSelector = ({ text }) => ( 20 | {text} 21 | ); 22 | 23 | export default BlockSelector; 24 | -------------------------------------------------------------------------------- /assets/icon-c9-subheading-enable-toolbar-icon.js: -------------------------------------------------------------------------------- 1 | export default ( 2 | 8 | 9 | 10 | 11 | Artboard 1 copy 10 12 | 16 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | Generally-applicable sniffs for WordPress plugins 4 | 5 | 6 | 7 | 8 | 9 | 10 | . 11 | 12 | 13 | 14 | 15 | */node_modules/* 16 | */vendor/* 17 | 18 | -------------------------------------------------------------------------------- /src/update-category/deregister-blocks.js: -------------------------------------------------------------------------------- 1 | const { unregisterBlockType } = wp.blocks; 2 | 3 | let loadBlocksEditor = null; 4 | 5 | if ('undefined' !== typeof window._wpLoadBlockEditor) { 6 | // Using Gutenberg plugin 7 | loadBlocksEditor = window._wpLoadBlockEditor; 8 | } else if ('undefined' !== typeof window._wpLoadGutenbergEditor) { 9 | // Using WP core Gutenberg 10 | loadBlocksEditor = window._wpLoadGutenbergEditor; 11 | } 12 | 13 | if (loadBlocksEditor) { 14 | loadBlocksEditor.then(() => { 15 | unregisterBlockType('core/verse'); 16 | // unregisterBlockType("core/columns"); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/update-category/register-styles.js: -------------------------------------------------------------------------------- 1 | // const { registerBlockStyle } = wp.blocks; 2 | 3 | let loadBlocksEditor = null; 4 | 5 | if ('undefined' !== typeof window._wpLoadBlockEditor) { 6 | // Using Gutenberg plugin 7 | loadBlocksEditor = window._wpLoadBlockEditor; 8 | } else if ('undefined' !== typeof window._wpLoadGutenbergEditor) { 9 | // Using WP core Gutenberg 10 | loadBlocksEditor = window._wpLoadGutenbergEditor; 11 | } 12 | 13 | if (loadBlocksEditor) { 14 | loadBlocksEditor.then(() => { 15 | // registerBlockStyle("core/button", { 16 | // name: "custom-button-style", 17 | // label: "My Button Style" 18 | // }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /assets/icon-c9-grid.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 19 -------------------------------------------------------------------------------- /assets/toggle-icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/blocks/block-image-carousel/deprecated/index.js: -------------------------------------------------------------------------------- 1 | import SaveDeprecatedV1 from './1.0/save.deprecated'; 2 | import SaveDeprecatedV2 from './2.0/save.deprecated'; 3 | import attributes from '../attributes'; 4 | 5 | const Deprecated = [ 6 | { 7 | attributes: { 8 | ...attributes, 9 | slideMaxHeight: { 10 | type: 'number', 11 | default: -1, 12 | }, 13 | slideEqualHeight: { 14 | type: 'boolean', 15 | default: false, 16 | }, 17 | }, 18 | save: (props) => { 19 | return ; 20 | }, 21 | }, 22 | { 23 | attributes, 24 | save: (props) => { 25 | return ; 26 | }, 27 | }, 28 | ]; 29 | 30 | export default Deprecated; 31 | -------------------------------------------------------------------------------- /assets/icon-c9-tabs-horizontal.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 20 -------------------------------------------------------------------------------- /src/blocks/block-horizontal-tabs/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | tabActive: { 6 | type: 'string', 7 | default: 'tab-1', 8 | }, 9 | buttonsAlign: { 10 | type: 'string', 11 | }, 12 | tabsData: { 13 | type: 'array', 14 | default: [ 15 | { 16 | slug: 'tab-1', 17 | title: 'Tab 1', 18 | }, 19 | { 20 | slug: 'tab-2', 21 | title: 'Tab 2', 22 | }, 23 | ], 24 | }, 25 | instanceId: { 26 | type: 'number', 27 | }, 28 | tabBackgroundColor: { 29 | type: 'string', 30 | }, 31 | tabTextColor: { 32 | type: 'string', 33 | }, 34 | tabContentBackgroundColor: { 35 | type: 'string', 36 | }, 37 | blockBackgroundColor: { 38 | type: 'string', 39 | }, 40 | }; 41 | 42 | export default attributes; 43 | -------------------------------------------------------------------------------- /assets/icon-c9-cta-bar.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 13 -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | tabActive: { 6 | type: 'string', 7 | default: 'tab-1', 8 | }, 9 | tabsData: { 10 | type: 'array', 11 | default: [ 12 | { 13 | slug: 'tab-1', 14 | title: 'Tab 1', 15 | }, 16 | { 17 | slug: 'tab-2', 18 | title: 'Tab 2', 19 | }, 20 | ], 21 | }, 22 | instanceId: { 23 | type: 'number', 24 | }, 25 | tabBackgroundColor: { 26 | type: 'string', 27 | }, 28 | tabTextColor: { 29 | type: 'string', 30 | }, 31 | tabContentBackgroundColor: { 32 | type: 'string', 33 | }, 34 | blockBackgroundColor: { 35 | type: 'string', 36 | }, 37 | verticalAlign: { 38 | type: 'string', 39 | }, 40 | textAlign: { 41 | type: 'string', 42 | }, 43 | }; 44 | 45 | export default attributes; 46 | -------------------------------------------------------------------------------- /src/blocks/block-posts-grid/save.js: -------------------------------------------------------------------------------- 1 | import Container from './components/container'; 2 | 3 | const { Component } = wp.element; 4 | const { InnerBlocks } = wp.blockEditor; 5 | 6 | export default class Save extends Component { 7 | constructor() { 8 | super(...arguments); 9 | } 10 | 11 | render() { 12 | const { attributes } = this.props; 13 | const sanitizedAttributes = { 14 | ...attributes, 15 | containerVideoID: attributes.containerVideoID 16 | ? String(attributes.containerVideoID).replace(/[^a-zA-Z0-9-_]/g, '') 17 | : '', 18 | }; 19 | 20 | return ( 21 | 22 | 23 | 24 | ); 25 | } 26 | } -------------------------------------------------------------------------------- /assets/fa-icons/twitter-square-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon-c9-tabs-vertical-tabs.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 21 -------------------------------------------------------------------------------- /config/babel-preset.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | // 'env', 5 | '@babel/preset-env', 6 | { 7 | // Do not transform modules to CJS. 8 | modules: false, 9 | targets: { 10 | browsers: [ 11 | 'last 2 Chrome versions', 12 | 'last 2 Edge versions', 13 | 'last 2 Firefox versions', 14 | 'last 2 Safari versions', 15 | 'last 2 iOS versions', 16 | 'last 1 Android version', 17 | 'last 1 ChromeAndroid version', 18 | ], 19 | }, 20 | }, 21 | ], 22 | ], 23 | plugins: [ 24 | ['@babel/plugin-proposal-class-properties'], 25 | [ 26 | '@babel/plugin-transform-react-jsx', 27 | { 28 | pragma: 'wp.element.createElement', 29 | }, 30 | ], 31 | // Polyfills the runtime needed for async/await and generators. 32 | ['@babel/plugin-transform-runtime'], 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/deprecated/index.js: -------------------------------------------------------------------------------- 1 | import SaveDeprecatedV1 from './1.0/save.deprecated'; 2 | import SaveDeprecatedV2 from './2.0/save.deprecated'; 3 | import SaveDeprecatedV3 from './3.0/save.deprecated'; 4 | import attributes from '../attributes'; 5 | 6 | const Deprecated = [ 7 | { 8 | attributes: { 9 | ...attributes, 10 | slideMaxHeight: { 11 | type: 'number', 12 | default: -1, 13 | }, 14 | slideEqualHeight: { 15 | type: 'boolean', 16 | default: false, 17 | }, 18 | }, 19 | save: (props) => { 20 | return ; 21 | }, 22 | }, 23 | { 24 | attributes, 25 | save: (props) => { 26 | return ; 27 | }, 28 | }, 29 | { 30 | attributes, 31 | save: (props) => { 32 | return ; 33 | }, 34 | }, 35 | ]; 36 | 37 | export default Deprecated; 38 | -------------------------------------------------------------------------------- /tests/test-class-check-dist-files-exist.php: -------------------------------------------------------------------------------- 1 | assertTrue( 32 | $check 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/color-appender/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-block-default-palette { 2 | .components-color-palette__item { 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | } 7 | 8 | .components-color-palette__item-wrapper { 9 | transform: scale(1.1); 10 | } 11 | 12 | .components-color-palette__item-wrapper:hover { 13 | transform: scale(1.1); 14 | } 15 | 16 | .components-color-palette__item svg { 17 | color: rgba(100, 100, 100, 1); 18 | } 19 | } 20 | 21 | .c9-colors-remove-last { 22 | display: inline-block; 23 | height: 28px; 24 | width: 28px; 25 | margin-right: 14px; 26 | margin-bottom: 14px; 27 | 28 | .components-button { 29 | border-radius: 50%; 30 | height: 100%; 31 | width: 100%; 32 | padding: 0; 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | } 37 | } 38 | 39 | .c9-colors-add-new { 40 | margin-bottom: 16px; 41 | } 42 | -------------------------------------------------------------------------------- /src/blocks/block-toggles/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { applyFilters } = wp.hooks; 6 | const { InnerBlocks } = wp.blockEditor; 7 | 8 | /** 9 | * External Dependencies. 10 | */ 11 | import classnames from 'classnames'; 12 | 13 | export default class Save extends Component { 14 | constructor() { 15 | super(...arguments); 16 | } 17 | 18 | render() { 19 | const { 20 | attributes: { reverseToggle }, 21 | className = '', 22 | ...otherProps 23 | } = this.props; 24 | 25 | return ( 26 |
35 | 36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | { 13 | return ( 14 | 31 | ); 32 | }; 33 | 34 | export default withDispatch((dispatch) => { 35 | const { insertBlocks } = dispatch('core/block-editor'); 36 | 37 | return { 38 | insertBlocks, 39 | }; 40 | })(ReusableButton); 41 | -------------------------------------------------------------------------------- /src/components/custom-palette/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-custom-color-settings-container .c9-color-icon-indicate { 2 | position: relative; 3 | transform: scale(1); 4 | transition: transform 0.1s ease; 5 | border-radius: 50%; 6 | padding: 0; 7 | } 8 | 9 | .c9-block-default-palette .components-color-palette__item-wrapper { 10 | padding: 2px; 11 | } 12 | 13 | .c9-custom-color-settings-container .c9-color-icon-indicate:hover { 14 | transform: scale(1); 15 | height: auto; 16 | } 17 | 18 | .c9-custom-color-settings-container 19 | .c9-color-icon-indicate 20 | .component-color-indicator.c9-advanced-color-indicate { 21 | width: 28px; 22 | height: 28px; 23 | border-radius: 50%; 24 | margin: 0; 25 | } 26 | 27 | .components-popover.popover-color 28 | .components-popover__content 29 | > div 30 | > .components-base-control { 31 | padding: 16px 16px 12px; 32 | } 33 | 34 | .components-popover.popover-color 35 | > .components-popover__content 36 | > .components-base-control { 37 | padding: 0 10px; 38 | } 39 | -------------------------------------------------------------------------------- /src/extend/space-settings-inspector/index.test.js: -------------------------------------------------------------------------------- 1 | import { c9SpacingConfig } from './utils'; 2 | 3 | describe('space settings extension should work correctly', () => { 4 | const defaultConfig = { 5 | linked: true, 6 | icon: 'admin-links', 7 | top: '-1', 8 | bottom: '-1', 9 | left: '-1', 10 | right: '-1', 11 | }; 12 | const sameValueConfig = { 13 | linked: true, 14 | icon: 'admin-links', 15 | top: '3', 16 | bottom: '3', 17 | left: '3', 18 | right: '3', 19 | }; 20 | 21 | it('returns no padding when in unset state', () => { 22 | expect(c9SpacingConfig(defaultConfig, defaultConfig)).toStrictEqual([]); 23 | }); 24 | 25 | it('returns one padding name if all values are the same', () => { 26 | expect(c9SpacingConfig(sameValueConfig, defaultConfig)).toStrictEqual([ 27 | 'p-3', 28 | ]); 29 | }); 30 | 31 | it('returns one margin name if all values are the same', () => { 32 | expect(c9SpacingConfig(defaultConfig, sameValueConfig)).toStrictEqual([ 33 | 'my-3', 34 | ]); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | instanceId: { 6 | type: 'number', 7 | }, 8 | slides: { 9 | type: 'number', 10 | default: 3, 11 | }, 12 | showControls: { 13 | type: 'boolean', 14 | default: true, 15 | }, 16 | showIndicators: { 17 | type: 'boolean', 18 | default: true, 19 | }, 20 | autoSlide: { 21 | type: 'boolean', 22 | default: true, 23 | }, 24 | wrapAround: { 25 | type: 'boolean', 26 | default: true, 27 | }, 28 | slideTime: { 29 | type: 'number', 30 | default: 5000, 31 | }, 32 | slideMaxHeight: { 33 | type: 'number', 34 | default: 250, 35 | }, 36 | slideSizes: { 37 | type: 'array', 38 | default: [], 39 | }, 40 | verticalAlign: { 41 | type: 'string', 42 | }, 43 | align: { 44 | type: 'string', 45 | default: '', 46 | }, 47 | containerWidth: { 48 | type: 'string', 49 | default: 'container', 50 | }, 51 | transitionType: { 52 | type: 'string', 53 | default: 'slide', 54 | }, 55 | }; 56 | 57 | export default attributes; 58 | -------------------------------------------------------------------------------- /src/blocks/block-sharing/components/sharing.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { applyFilters } = wp.hooks; 6 | 7 | /** 8 | * External Dependencies. 9 | */ 10 | import classnames from 'classnames'; 11 | 12 | /** 13 | * Create a ShareLinks wrapper Component 14 | */ 15 | export default class ShareLinks extends Component { 16 | constructor() { 17 | super(...arguments); 18 | } 19 | 20 | render() { 21 | const { 22 | attributes: { 23 | shareButtonStyle, 24 | shareButtonShape, 25 | shareButtonSize, 26 | shareButtonColor, 27 | shareAlignment, 28 | }, 29 | className = '', 30 | } = this.props; 31 | 32 | return ( 33 |
44 | {this.props.children} 45 |
46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /assets/toggle-open.js: -------------------------------------------------------------------------------- 1 | export default ( 2 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | -------------------------------------------------------------------------------- /src/components/pause-toolbar/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | 7 | /** 8 | * Control Settings 9 | */ 10 | const DEFAULT_PAUSE_CONTROLS = [ 11 | { 12 | icon: 'controls-pause', 13 | title: __('Pause', 'c9-blocks'), 14 | pause: true, 15 | }, 16 | ]; 17 | 18 | /** 19 | * Create a Width Toolbar Component 20 | */ 21 | export function PauseToolbar({ 22 | value, 23 | onChange, 24 | PauseControls = DEFAULT_PAUSE_CONTROLS, 25 | }) { 26 | function applyOrUnset(pause) { 27 | return () => onChange(value === pause ? false : pause); 28 | } 29 | 30 | return ( 31 | 32 | {PauseControls.map((control) => { 33 | const { pause } = control; 34 | return ( 35 | 41 | ); 42 | })} 43 | 44 | ); 45 | } 46 | 47 | export default PauseToolbar; 48 | -------------------------------------------------------------------------------- /src/blocks/block-sharing/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | 6 | /** 7 | * Styles 8 | */ 9 | import './styles/style.scss'; 10 | 11 | import Icon from '../../../assets/icon-c9-social-share.svg'; 12 | 13 | /** 14 | * WordPress dependencies 15 | */ 16 | const { __ } = wp.i18n; 17 | const { registerBlockType } = wp.blocks; 18 | 19 | registerBlockType('c9-blocks/social-share', { 20 | title: __('C9 Social Share', 'c9-blocks'), 21 | icon: Icon, 22 | category: 'c9-blocks', 23 | description: __( 24 | 'Add buttons for social media share links to any page or post with custom color and shape settings.', 25 | 'c9-blocks' 26 | ), 27 | example: { 28 | viewportWidth: '280', 29 | attributes: { 30 | linkedin: true, 31 | email: true, 32 | shareAlignment: 'center', 33 | shareButtonColor: 'c9-share-color-social', 34 | }, 35 | }, 36 | keywords: [__('share', 'c9-blocks'), __('social', 'c9-blocks')], 37 | 38 | // Render the block components 39 | edit: Edit, 40 | 41 | // Render via PHP 42 | save() { 43 | return null; 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /assets/c9-feather-logo-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | c9-feather-logo-gray 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /config/externals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts dash-separated strings to camelCase. 3 | * 4 | * @param {string} string Input dash-delimited string. 5 | * @return {string} Camel-cased string. 6 | */ 7 | const camelCaseDash = (string) => 8 | string.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase()); 9 | 10 | /** 11 | * Define externals to load components through the wp global. 12 | */ 13 | const externals = [ 14 | 'components', 15 | 'edit-post', 16 | 'element', 17 | 'plugins', 18 | 'editor', 19 | 'blocks', 20 | 'hooks', 21 | 'utils', 22 | 'date', 23 | 'data', 24 | 'i18n', 25 | 'block-editor', 26 | 'compose', 27 | 'keycodes', 28 | 'rich-text', 29 | 'shortcode', 30 | 'viewport', 31 | 'server-side-render', 32 | ].reduce( 33 | (externals, name) => ({ 34 | ...externals, 35 | [`@wordpress/${name}`]: `wp.${camelCaseDash(name)}`, 36 | }), 37 | { 38 | wp: 'wp', 39 | react: 'React', 40 | 'react-dom': 'ReactDOM', 41 | lodash: 'lodash', 42 | jquery: 'jQuery', 43 | ga: 'ga', 44 | gtag: 'gtag', 45 | // Add other global libraries or localized data as needed. 46 | } 47 | ); 48 | 49 | module.exports = externals; -------------------------------------------------------------------------------- /assets/fa-icons/instagram-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icon-c9-toggles.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 18 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:react/recommended", 11 | "plugin:prettier/recommended", 12 | "plugin:jsx-a11y/recommended" 13 | ], 14 | "globals": { 15 | "Atomics": "readonly", 16 | "SharedArrayBuffer": "readonly", 17 | "wp": "readonly", 18 | "c9_blocks_params": "readonly", 19 | "c9_blocks_assets": "readonly" 20 | }, 21 | "parser": "@babel/eslint-parser", 22 | "parserOptions": { 23 | "ecmaFeatures": { 24 | "jsx": true 25 | }, 26 | "ecmaVersion": 2018, 27 | "sourceType": "module", 28 | "requireConfigFile": false, 29 | "babelOptions": { 30 | "presets": ["@babel/preset-react"] 31 | } 32 | }, 33 | "settings": { 34 | "react": { 35 | "version": "detect" 36 | } 37 | }, 38 | "plugins": ["react", "jsx-a11y"], 39 | "rules": { 40 | "react/react-in-jsx-scope": "off", 41 | "react/display-name": "off", 42 | "react/prop-types": "off", 43 | "prettier/prettier": ["error", { "singleQuote": true }] 44 | }, 45 | "ignorePatterns": ["node_modules/**/*.js", "dist/*.js", "**/templates/*.js"] 46 | } 47 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Paths 3 | * 4 | * Project-related paths for Webpack and other tooling. 5 | */ 6 | 7 | const path = require('path'); 8 | const fs = require('fs'); 9 | 10 | // Resolve the real project directory (resolving symlinks if any). 11 | const pluginDir = fs.realpathSync(process.cwd()); 12 | const resolvePlugin = (relativePath) => path.resolve(pluginDir, relativePath); 13 | 14 | // Export paths. 15 | module.exports = { 16 | // Environment variables. 17 | dotenv: resolvePlugin('.env'), 18 | 19 | // Plugin source folder. 20 | pluginSrc: resolvePlugin('src'), 21 | 22 | // Entry points for Webpack. 23 | pluginBlocksJs: resolvePlugin('src/blocks.js'), 24 | pluginBlocksRegisterJs: resolvePlugin('src/register.js'), 25 | pluginBlocksFrontendJs: resolvePlugin('src/frontend.js'), 26 | pluginBlocksBootstrapJs: resolvePlugin('src/vendor.js'), 27 | pluginBlocksEditorJs: resolvePlugin('src/editor.js'), 28 | 29 | // Output folder for the built assets. 30 | pluginDist: resolvePlugin('dist'), 31 | 32 | // Lockfile for dependency management. 33 | yarnLockFile: resolvePlugin('yarn.lock'), 34 | 35 | // Root project path. 36 | appPath: resolvePlugin('.'), 37 | }; -------------------------------------------------------------------------------- /src/blocks/block-column-container/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import ResizableContainer from './components/resizable-container'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { InnerBlocks } = wp.blockEditor; 11 | 12 | /** 13 | * External dependencies 14 | */ 15 | import classnames from 'classnames'; 16 | 17 | export default class Save extends Component { 18 | constructor() { 19 | super(...arguments); 20 | } 21 | 22 | render() { 23 | const { 24 | attributes: { 25 | columnsGap, 26 | responsiveToggle, 27 | flipColumnsMobile, 28 | columnMaxWidth, 29 | }, 30 | } = this.props; 31 | 32 | return ( 33 | 34 |
45 | 46 |
47 |
48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/blocks/block-heading/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | isCollapsed: { 6 | type: 'boolean', 7 | default: false, 8 | }, 9 | heading: { 10 | type: 'string', 11 | default: '', 12 | }, 13 | subheading: { 14 | type: 'string', 15 | default: '', 16 | }, 17 | addSubheading: { 18 | type: 'boolean', 19 | default: false, 20 | }, 21 | wrapper: { 22 | type: 'array', 23 | source: 'query', 24 | selector: '.section-heading', 25 | query: { 26 | class: { 27 | type: 'string', 28 | source: 'attribute', 29 | attribute: 'class', 30 | }, 31 | }, 32 | }, 33 | tagLevel: { 34 | type: 'number', 35 | default: 1, 36 | }, 37 | displayLevel: { 38 | type: 'string', 39 | default: '', 40 | }, 41 | type: { 42 | type: 'string', 43 | default: 'c9-h h', 44 | }, 45 | backgroundColor: { 46 | type: 'string', 47 | }, 48 | textColor: { 49 | type: 'string', 50 | }, 51 | subTextColor: { 52 | type: 'string', 53 | }, 54 | textAlign: { 55 | type: 'string', 56 | default: 'left', 57 | }, 58 | weight: { 59 | type: 'string', 60 | }, 61 | overrideStyle: { 62 | type: 'boolean', 63 | default: false, 64 | }, 65 | }; 66 | 67 | export default attributes; 68 | -------------------------------------------------------------------------------- /src/editor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import global styles. 3 | */ 4 | import './block-globals.editor.scss'; 5 | 6 | /** 7 | * Import blocks editor styles. 8 | */ 9 | import './blocks/block-carousel/styles/editor.scss'; 10 | import './blocks/block-column-container/styles/editor.scss'; 11 | import './blocks/block-cta/styles/editor.scss'; 12 | import './blocks/block-grid-container/styles/editor.scss'; 13 | import './blocks/block-heading/styles/editor.scss'; 14 | import './blocks/block-horizontal-tabs/styles/editor.scss'; 15 | import './blocks/block-image-carousel/styles/editor.scss'; 16 | import './blocks/block-post-grid/styles/editor.scss'; 17 | import './blocks/block-posts-grid/styles/editor.scss'; 18 | import './blocks/block-sharing/styles/editor.scss'; 19 | import './blocks/block-toggles/styles/editor.scss'; 20 | import './blocks/block-vertical-tabs/styles/editor.scss'; 21 | 22 | /** 23 | * Import side menu / inspector styles. 24 | */ 25 | import './components/block-selector/editor.scss'; 26 | import './components/color-appender/editor.scss'; 27 | import './components/custom-palette/editor.scss'; 28 | import './components/large-modal/editor.scss'; 29 | import './components/sidebar/editor.scss'; 30 | import './components/templates-modal/editor.scss'; 31 | -------------------------------------------------------------------------------- /src/components/show-hide-toolbar/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | 7 | /** 8 | * Control Settings 9 | */ 10 | const DEFAULT_DISPLAY_CONTROLS = [ 11 | { 12 | icon: 'smartphone', 13 | title: __('Mobile', 'c9-blocks'), 14 | }, 15 | { 16 | icon: 'tablet', 17 | title: __('Tablet', 'c9-blocks'), 18 | }, 19 | { 20 | icon: 'desktop', 21 | title: __('Desktop', 'c9-blocks'), 22 | }, 23 | ]; 24 | 25 | /** 26 | * Create a Show/Hide Toolbar Component 27 | */ 28 | export function ShowHideToolbar({ 29 | value, 30 | onChange, 31 | displayControls = DEFAULT_DISPLAY_CONTROLS, 32 | }) { 33 | function applyOrUnset(display, i) { 34 | return () => onChange(value[i] === display ? [false, i] : [display, i]); 35 | } 36 | 37 | return ( 38 | 39 | {displayControls.map((control, i) => { 40 | return ( 41 | 47 | ); 48 | })} 49 | 50 | ); 51 | } 52 | 53 | export default ShowHideToolbar; 54 | -------------------------------------------------------------------------------- /src/components/templates-modal/section-button.js: -------------------------------------------------------------------------------- 1 | const { Icon } = wp.components; 2 | const { withDispatch } = wp.data; 3 | const { decodeEntities } = wp.htmlEntities; 4 | 5 | const SectionButton = ({ 6 | label, 7 | icon, 8 | description, 9 | preview, 10 | section, 11 | insertBlocks, 12 | open, 13 | close, 14 | onHover, 15 | }) => { 16 | return ( 17 | 41 | ); 42 | }; 43 | 44 | export default withDispatch((dispatch) => { 45 | const { insertBlocks } = dispatch('core/block-editor'); 46 | 47 | return { 48 | insertBlocks, 49 | }; 50 | })(SectionButton); 51 | -------------------------------------------------------------------------------- /src/blocks/block-heading/components/heading-toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __, sprintf } = wp.i18n; 5 | const { Component } = wp.element; 6 | const { ToolbarGroup, ToolbarButton } = wp.components; 7 | 8 | /** 9 | * External dependencies 10 | */ 11 | import range from 'lodash/range'; 12 | 13 | /** 14 | * Create a HeadingToolbar wrapper Component 15 | */ 16 | class HeadingToolbar extends Component { 17 | createLevelControl(targetLevel, selectedLevel, onChange) { 18 | return ( 19 | onChange(targetLevel)} 26 | subscript={String(targetLevel)} 27 | /> 28 | ); 29 | } 30 | 31 | render() { 32 | const { minLevel, maxLevel, selectedLevel, onChange } = this.props; 33 | 34 | return ( 35 | 36 | {range(minLevel, maxLevel).map((index) => 37 | this.createLevelControl(index, selectedLevel, onChange) 38 | )} 39 | 40 | ); 41 | } 42 | } 43 | 44 | export default HeadingToolbar; 45 | -------------------------------------------------------------------------------- /src/components/width-toolbar/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | 7 | /** 8 | * Control Settings 9 | */ 10 | const DEFAULT_WIDTH_CONTROLS = [ 11 | { 12 | icon: 'align-center', 13 | title: __('Narrow Width', 'c9-blocks'), 14 | width: 'narrow', 15 | }, 16 | { 17 | icon: 'align-wide', 18 | title: __('Wide Width', 'c9-blocks'), 19 | width: 'wide', 20 | }, 21 | { 22 | icon: 'align-full-width', 23 | title: __('Full Width', 'c9-blocks'), 24 | width: 'full', 25 | }, 26 | ]; 27 | 28 | /** 29 | * Create a Width Toolbar Component 30 | */ 31 | export function WidthToolbar({ 32 | value, 33 | onChange, 34 | widthControls = DEFAULT_WIDTH_CONTROLS, 35 | }) { 36 | function applyOrUnset(width) { 37 | return () => onChange(value === width ? undefined : width); 38 | } 39 | 40 | return ( 41 | 42 | {widthControls.map((control) => { 43 | const { width } = control; 44 | return ( 45 | 51 | ); 52 | })} 53 | 54 | ); 55 | } 56 | 57 | export default WidthToolbar; 58 | -------------------------------------------------------------------------------- /src/blocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gutenberg Blocks 3 | * 4 | * All blocks related JavaScript files should be imported here. 5 | * You can create a new block folder in this dir and include code 6 | * for that block here as well. 7 | * 8 | * All blocks should be included here since this is the file that 9 | * Webpack is compiling as the input file. 10 | */ 11 | 12 | /** 13 | * Block extensions. 14 | */ 15 | import './extend'; 16 | 17 | // set locale 18 | import './blocks/i18n.js'; 19 | 20 | // main blocks 21 | import './blocks/block-sharing'; 22 | import './blocks/block-cta'; 23 | import './blocks/block-heading'; 24 | import './blocks/block-image-carousel'; 25 | import './blocks/block-grid-container'; 26 | import './blocks/block-horizontal-tabs'; 27 | import './blocks/block-vertical-tabs'; 28 | import './blocks/block-toggles'; 29 | import './blocks/block-posts-grid'; 30 | import './blocks/block-carousel'; 31 | 32 | // child blocks 33 | import './blocks/block-column-container'; 34 | import './blocks/block-column-container/components/column.js'; 35 | import './blocks/block-horizontal-tabs/components/horizontal-tab'; 36 | import './blocks/block-vertical-tabs/components/vertical-tab'; 37 | import './blocks/block-toggles/components/toggle.js'; 38 | import './blocks/block-carousel/components/slide.js'; 39 | import './blocks/block-post-grid'; 40 | -------------------------------------------------------------------------------- /src/blocks/block-heading/components/custom-heading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { applyFilters } = wp.hooks; 6 | const { useBlockProps } = wp.blockEditor; 7 | const { getBlockType } = wp.blocks; 8 | 9 | /** 10 | * External Dependencies. 11 | */ 12 | import classnames from 'classnames'; 13 | /** 14 | * Create a C9CustomHeading wrapper Component 15 | */ 16 | export default class C9CustomHeading extends Component { 17 | constructor() { 18 | super(...arguments); 19 | } 20 | 21 | render() { 22 | const { 23 | attributes: { textAlign, anchor }, 24 | className = '', 25 | } = this.props; 26 | 27 | const extraProps = useBlockProps 28 | ? useBlockProps.save() 29 | : applyFilters( 30 | 'blocks.getSaveContent.extraProps', 31 | this.props, 32 | getBlockType('c9-blocks/heading'), 33 | this.props.attributes 34 | ); 35 | 36 | return ( 37 |
47 | {this.props.children} 48 |
49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /assets/fa-icons/tiktok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tiktok 5 | 6 | -------------------------------------------------------------------------------- /src/blocks/block-heading/components/subheading-toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | 7 | import Icon from '../../../../assets/icon-c9-subheading-enable-toolbar-icon'; 8 | 9 | /** 10 | * Control Settings 11 | */ 12 | const DEFAULT_ENABLE_CONTROLS = [ 13 | { 14 | icon: Icon, 15 | title: __('Enable Subheading', 'c9-blocks'), 16 | enabled: true, 17 | }, 18 | ]; 19 | 20 | /** 21 | * Create an Subheading Toolbar wrapper Component 22 | */ 23 | export function SubheadingToolbar({ 24 | value, 25 | onChange, 26 | enableControls = DEFAULT_ENABLE_CONTROLS, 27 | }) { 28 | function applyOrUnset(enabled) { 29 | return () => onChange(value === enabled ? false : enabled); 30 | } 31 | 32 | return ( 33 | 34 | {enableControls.map((control, index) => { 35 | //destructure to remove 'enabled' from the rest of the control props 36 | const { enabled, ...buttonProps } = control; 37 | return ( 38 | 44 | ); 45 | })} 46 | 47 | ); 48 | } 49 | 50 | export default SubheadingToolbar; 51 | -------------------------------------------------------------------------------- /assets/icon-c9-anything-carousel.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 14 -------------------------------------------------------------------------------- /src/components/preview-panel/index.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const StyledDiv = styled.div` 4 | top: 0; 5 | left: calc(100% + 16px); 6 | width: 450px; 7 | border: 1px solid #ddd; 8 | border-radius: 0px; 9 | position: absolute; 10 | padding: 0; 11 | background-color: #fff; 12 | `; 13 | 14 | /** 15 | * WordPress dependencies 16 | */ 17 | const { BlockPreview } = wp.blockEditor; 18 | 19 | function PreviewPanel({ item }) { 20 | return ( 21 | 22 |
23 |
24 | .c9-grid .block-editor-block-list__block:not(.wp-block-separator):not(.rich-text) {margin-top: 0px; margin-bottom: 0px;}', 32 | }, 33 | ]} 34 | blocks={item} 35 | /> 36 |
37 |
38 |
39 | ); 40 | } 41 | 42 | export default PreviewPanel; 43 | -------------------------------------------------------------------------------- /src/blocks/block-horizontal-tabs/styles/style.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * #.# Styles 3 | * 4 | * CSS for both Frontend+Backend. 5 | */ 6 | 7 | .entry-content .c9-horizontal-tabs>.nav.nav-tabs, 8 | .editor-styles-wrapper .c9-horizontal-tabs>.nav.nav-tabs { 9 | font-size: inherit; 10 | list-style: none; 11 | padding: 0px; 12 | margin: 30px 0 0 0; 13 | } 14 | 15 | .c9-horizontal-tabs .nav-tabs .nav-link { 16 | display: block; 17 | border: none; 18 | border-radius: 0px; 19 | color: #979797; 20 | text-transform: uppercase; 21 | font-size: 16px; 22 | padding: 13px 35px; 23 | font-weight: 700; 24 | letter-spacing: .09em; 25 | text-decoration: none; 26 | } 27 | 28 | .c9-horizontal-tabs .nav-tabs .nav-link.active { 29 | color: #2b2b2b; 30 | background-color: #fff; 31 | text-decoration: none; 32 | } 33 | 34 | .c9-horizontal-tabs .nav-tabs .nav-link:focus, 35 | .c9-horizontal-tabs .nav-tabs .nav-link:focus-within, 36 | .c9-horizontal-tabs .nav-tabs .nav-link:active { 37 | outline-style: solid; 38 | outline-color: #fff; 39 | } 40 | 41 | .c9-horizontal-tabs .tab-content { 42 | padding: 25px; 43 | } 44 | 45 | .c9-horizontal-tabs p:last-child:last-of-type { 46 | margin-bottom: 0px; 47 | } 48 | 49 | @media only screen and (max-width: 812px) { 50 | .c9-horizontal-tabs>.nav.nav-tabs { 51 | flex-wrap: wrap; 52 | } 53 | 54 | .c9-horizontal-tabs>.nav.nav-tabs .nav-link { 55 | font-size: 12px; 56 | padding: 10px 15px; 57 | } 58 | } -------------------------------------------------------------------------------- /src/components/vertical-alignment-toolbar/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import icons from '../../../assets/c9-vertical-alignment-icons'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { __ } = wp.i18n; 10 | const { ToolbarGroup, ToolbarButton } = wp.components; 11 | 12 | /** 13 | * Control Settings 14 | */ 15 | const DEFAULT_ALIGN_CONTROLS = [ 16 | { 17 | icon: icons.top, 18 | title: __('Vertical Align Top', 'c9-blocks'), 19 | align: 'top', 20 | }, 21 | { 22 | icon: icons.center, 23 | title: __('Vertical Align Middle', 'c9-blocks'), 24 | align: 'center', 25 | }, 26 | { 27 | icon: icons.bottom, 28 | title: __('Vertical Align Bottom', 'c9-blocks'), 29 | align: 'bottom', 30 | }, 31 | ]; 32 | 33 | /** 34 | * Create a Vertical Alignment Toolbar Component 35 | */ 36 | export function VerticalAlignmentToolbar({ 37 | value, 38 | onChange, 39 | alignControls = DEFAULT_ALIGN_CONTROLS, 40 | }) { 41 | function applyOrUnset(align) { 42 | return () => onChange(value === align ? undefined : align); 43 | } 44 | 45 | return ( 46 | 47 | {alignControls.map((control) => { 48 | const { align } = control; 49 | return ( 50 | 56 | ); 57 | })} 58 | 59 | ); 60 | } 61 | 62 | export default VerticalAlignmentToolbar; 63 | -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/components/vertical-align-toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import icons from '../../../../assets/c9-vertical-alignment-icons'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { __ } = wp.i18n; 10 | const { ToolbarGroup, ToolbarButton } = wp.components; 11 | 12 | /** 13 | * Control Settings 14 | */ 15 | const DEFAULT_ALIGN_CONTROLS = [ 16 | { 17 | icon: icons.top, 18 | title: __('Vertical Align Top', 'c9-blocks'), 19 | align: 'start', 20 | }, 21 | { 22 | icon: icons.center, 23 | title: __('Vertical Align Middle', 'c9-blocks'), 24 | align: 'center', 25 | }, 26 | { 27 | icon: icons.bottom, 28 | title: __('Vertical Align Bottom', 'c9-blocks'), 29 | align: 'end', 30 | }, 31 | ]; 32 | 33 | /** 34 | * Create a Vertical Alignment Toolbar Component 35 | */ 36 | export function VerticalAlignmentToolbar({ 37 | value, 38 | onChange, 39 | alignControls = DEFAULT_ALIGN_CONTROLS, 40 | }) { 41 | function applyOrUnset(align) { 42 | return () => onChange(value === align ? undefined : align); 43 | } 44 | 45 | return ( 46 | 47 | {alignControls.map((control) => { 48 | const { align } = control; 49 | return ( 50 | 56 | ); 57 | })} 58 | 59 | ); 60 | } 61 | 62 | export default VerticalAlignmentToolbar; 63 | -------------------------------------------------------------------------------- /src/components/templates-modal/page-templates/page-templates.js: -------------------------------------------------------------------------------- 1 | import lpAboutCorporate from './templates/about-corporate'; 2 | import lpAboutCreative from './templates/about-creative'; 3 | import lpContactShort from './templates/contact-short'; 4 | import lpContact from './templates/contact'; 5 | import lpEvent from './templates/event'; 6 | import lpLaunchRelease from './templates/launch-release'; 7 | import lpMedia from './templates/media'; 8 | import lpPortfolio from './templates/portfolio'; 9 | import lpPriceMenu from './templates/price-menu'; 10 | import lpResources from './templates/resources'; 11 | import lpService from './templates/service'; 12 | import lpPricing from './templates/pricing'; 13 | import lpStyle from './templates/style-guide'; 14 | import leadGen1 from './templates/lead-generation-1'; 15 | import leadGen2 from './templates/lead-generation-2'; 16 | import leadGen3 from './templates/lead-generation-3'; 17 | import lpVideos from './templates/videos'; 18 | import lpStory1200 from './templates/story-article-1200'; 19 | import lpStory1600 from './templates/story-article-1600'; 20 | import lpStory2000 from './templates/story-article-2000'; 21 | 22 | const pageTemplates = { 23 | lpAboutCorporate, 24 | lpAboutCreative, 25 | lpContactShort, 26 | lpContact, 27 | lpEvent, 28 | lpLaunchRelease, 29 | lpMedia, 30 | lpVideos, 31 | lpPortfolio, 32 | lpPriceMenu, 33 | lpResources, 34 | lpService, 35 | lpPricing, 36 | lpStyle, 37 | leadGen1, 38 | leadGen2, 39 | leadGen3, 40 | lpStory1200, 41 | lpStory1600, 42 | lpStory2000, 43 | }; 44 | 45 | export default pageTemplates; 46 | -------------------------------------------------------------------------------- /src/components/youtube-api-toggle/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | 3 | /** 4 | * WordPress dependencies 5 | */ 6 | const { Component } = wp.element; 7 | const { ToggleControl } = wp.components; 8 | const { withDispatch } = wp.data; 9 | 10 | class YoutubeAPIToggle extends Component { 11 | constructor() { 12 | super(...arguments); 13 | this.saveConfig = this.saveConfig.bind(this); 14 | this.state = { 15 | isSaving: false, 16 | youtubeAPIDisabled: c9_blocks_params.disable_youtube_api === 'true', 17 | }; 18 | } 19 | 20 | /** 21 | * Stores current color configuration to plugin settings. 22 | */ 23 | saveConfig(value) { 24 | if (false === this.state.isSaving) { 25 | this.setState({ isSaving: true }); 26 | const settingModel = new wp.api.models.Settings({ 27 | c9_blocks_disable_youtube_api: JSON.stringify(value), 28 | }); 29 | // eslint-disable-next-line no-unused-vars 30 | settingModel.save().then((response) => { 31 | this.setState({ isSaving: false, youtubeAPIDisabled: value }); 32 | c9_blocks_params.c9_blocks_disable_youtube_api = JSON.stringify(value); 33 | }); 34 | } 35 | } 36 | 37 | render() { 38 | const { youtubeAPIDisabled } = this.state; 39 | 40 | return ( 41 | { 44 | this.saveConfig(value); 45 | }} 46 | /> 47 | ); 48 | } 49 | } 50 | 51 | export default withDispatch((dispatch) => { 52 | const { updateSettings } = dispatch('core/block-editor'); 53 | return { 54 | updateSettings, 55 | }; 56 | })(YoutubeAPIToggle); 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | 4 | language: php 5 | 6 | notifications: 7 | email: 8 | on_success: never 9 | on_failure: change 10 | 11 | branches: 12 | only: 13 | - master 14 | - develop 15 | 16 | cache: 17 | directories: 18 | - vendor 19 | - $HOME/.composer/cache 20 | 21 | matrix: 22 | include: 23 | - php: 7.1 24 | env: WP_VERSION=latest 25 | - php: 7.0 26 | env: WP_VERSION=latest 27 | - php: 5.6 28 | env: WP_VERSION=5.0 29 | - php: 5.6 30 | env: WP_VERSION=latest 31 | - php: 5.6 32 | env: WP_VERSION=trunk 33 | - php: 5.6 34 | env: WP_TRAVISCI=phpcs 35 | 36 | before_script: 37 | - export PATH="$HOME/.composer/vendor/bin:$PATH" 38 | - | 39 | if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then 40 | phpenv config-rm xdebug.ini 41 | else 42 | echo "xdebug.ini does not exist" 43 | fi 44 | - | 45 | if [[ ! -z "$WP_VERSION" ]] ; then 46 | bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION 47 | composer global require "phpunit/phpunit=4.8.*|5.7.*" 48 | fi 49 | - | 50 | if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then 51 | composer global require wp-coding-standards/wpcs 52 | phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs 53 | fi 54 | script: 55 | - | 56 | if [[ ! -z "$WP_VERSION" ]] ; then 57 | phpunit 58 | WP_MULTISITE=1 phpunit 59 | fi 60 | - | 61 | if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then 62 | phpcs 63 | fi 64 | -------------------------------------------------------------------------------- /src/components/large-modal/editor.scss: -------------------------------------------------------------------------------- 1 | .c9-templates-modal { 2 | .components-modal__header { 3 | border-bottom: none; 4 | } 5 | 6 | .c9-component-modal-tab-panel .components-tab-panel__tabs { 7 | display: flex; 8 | align-items: center; 9 | margin-top: -24px; 10 | margin-bottom: 9px; 11 | } 12 | 13 | @media (min-width: 600px) { 14 | min-width: 50vw; 15 | max-width: calc(100vw - 496px); // fix for Ace editor tooltips 16 | transform: translateX(-50%); 17 | left: calc(50% - 150px); 18 | 19 | // position 20 | top: 65px; 21 | } 22 | 23 | @media (min-width: 768px) { 24 | left: calc(50% - 180px); 25 | } 26 | 27 | @media (min-width: 992px) { 28 | left: 40px; 29 | position: absolute; 30 | } 31 | 32 | @media (max-width: 1199px) { 33 | min-width: 50vw; 34 | } 35 | 36 | @media (min-width: 1200px) { 37 | min-width: calc(100vw - 520px); 38 | max-width: calc(100vw - 520px); 39 | } 40 | 41 | // header 42 | .components-modal__header { 43 | .components-modal__icon-container svg { 44 | display: block; 45 | margin-right: 10px; 46 | } 47 | 48 | .components-modal__header-heading { 49 | font-weight: 600; 50 | text-transform: uppercase; 51 | font-size: 16px; 52 | color: #576d7b; 53 | letter-spacing: 0.5px; 54 | } 55 | } 56 | } 57 | 58 | /* block preview updates */ 59 | .c9-modal-preview 60 | .block-editor-block-preview__container 61 | .block-editor-block-preview__content { 62 | width: 100vw; 63 | min-width: 100vw; 64 | } 65 | .block-editor-block-patterns-list 66 | .block-editor-block-preview__container 67 | > .block-editor-block-preview__content 68 | iframe { 69 | max-width: none; 70 | } 71 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | div { 7 | box-sizing: border-box; 8 | } 9 | 10 | .c9-vertical-tabs { 11 | > .nav.nav-tabs { 12 | list-style: none; 13 | } 14 | 15 | .c9-tabs-content-wrapper { 16 | display: grid; 17 | } 18 | } 19 | 20 | .c9-vertical-tabs .nav-pills .nav-link { 21 | display: block; 22 | border: none; 23 | border-radius: 0px; 24 | color: #979797; 25 | text-transform: uppercase; 26 | font-size: 16px; 27 | padding: 13px 20px; 28 | font-weight: 700; 29 | letter-spacing: 0.005em; 30 | text-decoration: none; 31 | } 32 | 33 | .c9-vertical-tabs .tab-content { 34 | padding: 0px 25px; 35 | } 36 | 37 | .c9 .c9-vertical-tabs p:only-child { 38 | margin-bottom: 0px; 39 | } 40 | 41 | @media only screen and (max-width: 1024px) { 42 | .c9-layout-columns-6 43 | .c9-is-responsive-column 44 | .c9-vertical-tabs 45 | .nav-pills 46 | .nav-link, 47 | .c9-layout-columns-5 48 | .c9-is-responsive-column 49 | .c9-vertical-tabs 50 | .nav-pills 51 | .nav-link, 52 | .c9-layout-columns-4 53 | .c9-is-responsive-column 54 | .c9-vertical-tabs 55 | .nav-pills 56 | .nav-link, 57 | .c9-layout-columns-3 58 | .c9-is-responsive-column 59 | .c9-vertical-tabs 60 | .nav-pills 61 | .nav-link { 62 | font-size: 1em; 63 | } 64 | } 65 | 66 | .c9-vertical-tabs .nav-pills .nav-link.active, 67 | .c9-vertical-tabs .nav-pills .show > .nav-link, 68 | .c9-vertical-tabs .nav-pills .nav-link.active { 69 | color: #2b2b2b; 70 | background-color: #ccc; 71 | text-decoration: none; 72 | } 73 | 74 | @media only screen and (max-width: 568px) { 75 | .c9-vertical-tabs .nav { 76 | margin-bottom: 20px; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/blocks/block-image-carousel/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | instanceId: { 6 | type: 'number', 7 | }, 8 | slides: { 9 | type: 'number', 10 | default: 3, 11 | }, 12 | showControls: { 13 | type: 'boolean', 14 | default: true, 15 | }, 16 | showIndicators: { 17 | type: 'boolean', 18 | default: true, 19 | }, 20 | autoSlide: { 21 | type: 'boolean', 22 | default: true, 23 | }, 24 | wrapAround: { 25 | type: 'boolean', 26 | default: true, 27 | }, 28 | url: { 29 | type: 'array', 30 | default: [null, null, null], 31 | }, 32 | link: { 33 | type: 'array', 34 | default: [null, null, null], 35 | }, 36 | id: { 37 | type: 'array', 38 | default: [null, null, null], 39 | }, 40 | captionTitle: { 41 | type: 'array', 42 | default: [null, null, null], 43 | }, 44 | captionContent: { 45 | type: 'array', 46 | default: [null, null, null], 47 | }, 48 | slideTime: { 49 | type: 'number', 50 | default: 5000, 51 | }, 52 | isResponsive: { 53 | type: 'boolean', 54 | default: false, 55 | }, 56 | slideMaxHeight: { 57 | type: 'number', 58 | default: 250, 59 | }, 60 | slideCustomHeight: { 61 | type: 'boolean', 62 | default: false, 63 | }, 64 | verticalAlign: { 65 | type: 'string', 66 | }, 67 | align: { 68 | type: 'string', 69 | default: '', 70 | }, 71 | containerWidth: { 72 | type: 'string', 73 | default: 'container', 74 | }, 75 | transitionType: { 76 | type: 'string', 77 | default: 'slide', 78 | }, 79 | linkTarget: { 80 | type: 'string', 81 | source: 'attribute', 82 | selector: 'a', 83 | attribute: 'target', 84 | }, 85 | rel: { 86 | type: 'string', 87 | source: 'attribute', 88 | selector: 'a', 89 | attribute: 'rel', 90 | }, 91 | }; 92 | 93 | export default attributes; 94 | -------------------------------------------------------------------------------- /assets/c9-feather-logo-icon.js: -------------------------------------------------------------------------------- 1 | const { G, Path, SVG } = wp.components; 2 | 3 | const Icon = ( 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | c9-feather-logo-icon 26 | 27 | 28 | 32 | 33 | 34 | 35 | ); 36 | 37 | export default Icon; 38 | -------------------------------------------------------------------------------- /assets/icon-c9-image-carousel.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 11 -------------------------------------------------------------------------------- /tests/test-class-layout-endpoints.php: -------------------------------------------------------------------------------- 1 | server = $wp_rest_server; 43 | // do_action( 'rest_api_init' ); 44 | // $this->routes = $this->server->get_routes(); 45 | // } 46 | // /** 47 | // * Tests that the gutenberg namespace exists in the REST API. 48 | // */ 49 | // public function test_namespace_exists() { 50 | // $this->assertArrayHasKey( $this->namespace, $this->routes ); 51 | // } 52 | // /** 53 | // * Tests the retrieval of favorite layouts. 54 | // */ 55 | // public function test_saved_block_exists() { 56 | // $request = new \WP_REST_Request( 'GET', '/wp/v2/types/wp_block' ); 57 | // $response = $this->server->dispatch( $request ); 58 | // $this->assertNotNull( $response->get_data() ); 59 | // } 60 | // /** 61 | // * Tests the retrieval of posts. 62 | // */ 63 | // public function test_saved_post_categories_exists() { 64 | // $request = new \WP_REST_Request( 'GET', '/wp/v2/categories' ); 65 | // $response = $this->server->dispatch( $request ); 66 | // $this->assertNotNull( $response->get_data() ); 67 | // }. 68 | } 69 | -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/components/vertical-tab/index.php: -------------------------------------------------------------------------------- 1 | ', 28 | $classes, 29 | esc_attr($id), 30 | esc_attr($aria_label) 31 | ); 32 | 33 | $tab_markup .= $content . ''; 34 | 35 | return $tab_markup; 36 | } 37 | 38 | /** 39 | * Registers the vertical tab component block on server 40 | */ 41 | function covertnine_blocks_register_vertical_tabs() 42 | { 43 | /* Check if the register function exists */ 44 | if (!function_exists( 'register_block_type' )) { 45 | return; 46 | } 47 | 48 | /* Block attributes */ 49 | register_block_type( 'c9-blocks/vertical-tabs-tab', array( 50 | 'title' => __('C9 Vertical Tab', 'c9-blocks'), 51 | 'category' => 'common', 52 | 'parent' => 'c9-blocks/vertical-tabs', 53 | 'attributes' => array( 54 | 'slug' => array( 55 | 'type' => 'string', 56 | ), 57 | 'tabActive' => array( 58 | 'type' => 'string', 59 | ), 60 | 'id' => array( 61 | 'type' => 'number', 62 | ), 63 | ), 64 | 'render_callback' => 'covertnine_blocks_render_vertical_tabs' 65 | ) ); 66 | } 67 | add_action( 'init', 'covertnine_blocks_register_vertical_tabs' ); 68 | -------------------------------------------------------------------------------- /src/blocks/block-horizontal-tabs/components/horizontal-tab/index.php: -------------------------------------------------------------------------------- 1 | ', 28 | $classes, 29 | esc_attr($id), 30 | esc_attr($aria_label) 31 | ); 32 | 33 | $tab_markup .= $content . ''; 34 | 35 | return $tab_markup; 36 | } 37 | 38 | /** 39 | * Registers the horizontal tab component block on server 40 | */ 41 | function covertnine_blocks_register_horizontal_tabs() 42 | { 43 | /* Check if the register function exists */ 44 | if (!function_exists( 'register_block_type' )) { 45 | return; 46 | } 47 | 48 | /* Block attributes */ 49 | register_block_type( 'c9-blocks/horizontal-tabs-tab', array( 50 | 'title' => __('C9 Horizontal Tab', 'c9-blocks'), 51 | 'category' => 'common', 52 | 'parent' => 'c9-blocks/horizontal-tabs', 53 | 'attributes' => array( 54 | 'slug' => array( 55 | 'type' => 'string', 56 | ), 57 | 'tabActive' => array( 58 | 'type' => 'string', 59 | ), 60 | 'id' => array( 61 | 'type' => 'number', 62 | ), 63 | ), 64 | 'render_callback' => 'covertnine_blocks_render_horizontal_tabs' 65 | ) ); 66 | } 67 | add_action( 'init', 'covertnine_blocks_register_horizontal_tabs' ); 68 | -------------------------------------------------------------------------------- /src/components/templates-modal/page-layout-button.js: -------------------------------------------------------------------------------- 1 | const { Icon } = wp.components; 2 | const { withDispatch } = wp.data; 3 | const { useState } = wp.element; 4 | import HoverIntent from 'react-hoverintent'; 5 | 6 | const LayoutButton = ({ 7 | label, 8 | icon, 9 | description, 10 | preview, 11 | recommended, 12 | layout, 13 | resetBlocks, 14 | insertBlocks, 15 | open, 16 | close, 17 | onHover, 18 | }) => { 19 | const [hovered, setHovered] = useState(false); 20 | const toggleHover = () => setHovered(!hovered); 21 | return ( 22 | 68 | ); 69 | }; 70 | 71 | export default withDispatch((dispatch) => { 72 | const { resetBlocks, insertBlocks } = dispatch('core/block-editor'); 73 | 74 | return { 75 | resetBlocks, 76 | insertBlocks, 77 | }; 78 | })(LayoutButton); 79 | -------------------------------------------------------------------------------- /src/blocks/block-heading/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | 8 | /** 9 | * Styles 10 | */ 11 | import './styles/style.scss'; 12 | 13 | import Icon from '../../../assets/icon-c9-heading.svg'; 14 | 15 | /** 16 | * WordPress dependencies 17 | */ 18 | const { __ } = wp.i18n; 19 | const { registerBlockType } = wp.blocks; 20 | const { compose } = wp.compose; 21 | const { withSelect } = wp.data; 22 | const { withViewportMatch } = wp.viewport; 23 | 24 | registerBlockType('c9-blocks/heading', { 25 | title: __('C9 Heading', 'c9-blocks'), 26 | icon: Icon, 27 | category: 'c9-blocks', 28 | supports: { 29 | anchor: true, 30 | }, 31 | keywords: [ 32 | __('heading', 'c9-blocks'), 33 | __('c9', 'c9-blocks'), 34 | __('covertnine', 'c9-blocks'), 35 | ], 36 | 37 | description: __( 38 | 'An advanced heading block with inline subheading font, custom colors, and style settings.', 39 | 'c9-blocks' 40 | ), 41 | 42 | example: { 43 | viewportWidth: '280', 44 | attributes: { 45 | heading: 'Headlines H1-H6', 46 | subheading: 'Subheading H1-H6', 47 | addSubheading: true, 48 | tagLevel: 3, 49 | }, 50 | }, 51 | 52 | attributes: attributes, 53 | 54 | // Render the block components 55 | edit: compose( 56 | withViewportMatch({ isLargeViewport: 'medium' }), 57 | withSelect((select, { clientId, isLargeViewport, isCollapsed }) => { 58 | const { 59 | getBlockRootClientId, 60 | getSettings, 61 | isBlockSelected, 62 | hasSelectedInnerBlock, 63 | } = select('core/block-editor'); 64 | const settings = getSettings(); 65 | return { 66 | isCollapsed: 67 | isCollapsed || 68 | !isLargeViewport || 69 | (!settings.hasFixedToolbar && !!getBlockRootClientId(clientId)), 70 | isSelectedBlockInRoot: 71 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 72 | }; 73 | }) 74 | )(Edit), 75 | 76 | // Save the attributes and markup 77 | save: Save, 78 | }); 79 | -------------------------------------------------------------------------------- /assets/icon-c9-post-grid.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 2 -------------------------------------------------------------------------------- /src/blocks/block-horizontal-tabs/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { RichText, InnerBlocks } = wp.blockEditor; 6 | const { applyFilters } = wp.hooks; 7 | 8 | /** 9 | * External Dependencies. 10 | */ 11 | import classnames from 'classnames'; 12 | 13 | export default class Save extends Component { 14 | constructor() { 15 | super(...arguments); 16 | } 17 | 18 | render() { 19 | const { 20 | tabActive, 21 | buttonsAlign, 22 | tabsData = [], 23 | tabBackgroundColor, 24 | tabTextColor, 25 | tabContentBackgroundColor, 26 | blockBackgroundColor, 27 | instanceId, 28 | } = this.props.attributes; 29 | 30 | const { className = '', ...otherProps } = this.props; 31 | 32 | return ( 33 |
41 |
    48 | {tabsData.map((tabData) => { 49 | const { slug, title } = tabData; 50 | const selected = tabActive === slug; 51 | return ( 52 |
  • 53 | 66 |
  • 67 | ); 68 | })} 69 |
70 |
76 | 77 |
78 |
79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /dist/blocks.frontend.build.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see blocks.frontend.build.js.LICENSE.txt */ 2 | !function(){var e={1198:function(){function e(e,t,o){var a=o("#".concat(e)),r=a.attr("style"),n=o(''));n.on("load",(function(){this.parentNode.parentNode.style.opacity=1})),a.replaceWith(n)}function t(e,r){if(YT&&YT.Player)new YT.Player(e,{playerVars:{autoplay:1,controls:0,disablekb:0,autohide:1,wmode:"opaque",hd:1,enablejsapi:1,loop:1,showinfo:0,iv_load_policy:3,rel:0,modestbranding:1,playlist:r},videoId:r,events:{onReady:o,onError:a}});else setTimeout((function(){t(e,r)}),100)}function o(e){e.target.mute(),e.target.playVideo(),e.target.getIframe().parentNode.parentNode.style.opacity=1}function a(e){console.log("The YouTube IFrame Player API fired an onError event with error code: "+e.data);var t=e.target.getIframe();t.parentNode.parentNode.style.opacity=0,t.parentNode.parentNode.style.transition="300ms",t.parentNode.parentNode.dataset.c9VideoContainerError=e.data}document.addEventListener("DOMContentLoaded",(function(){for(var o="true"===c9_blocks_params.disable_youtube_api,a=window.jQuery,r=document.getElementsByClassName("c9-video"),n=0;n { 26 | let classes = []; 27 | // abstract side class assignment 28 | function assignSideClasses(sideClass, level) { 29 | if (-1 != level) { 30 | classes.push(`${sideClass}-${level}`); 31 | } 32 | } 33 | 34 | // padding 35 | if ( 36 | padding.top === padding.left && 37 | padding.top === padding.bottom && 38 | padding.top === padding.right && 39 | -1 != padding.top 40 | ) { 41 | classes.push(`p-${padding.top}`); 42 | } else if (padding.top === padding.bottom && 0 <= padding.top) { 43 | classes.push(`py-${padding.top}`); 44 | assignSideClasses('pl', padding.left); 45 | assignSideClasses('pr', padding.right); 46 | } else if (padding.left === padding.right && 0 <= padding.left) { 47 | classes.push(`px-${padding.left}`); 48 | assignSideClasses('pt', padding.top); 49 | assignSideClasses('pb', padding.bottom); 50 | } else { 51 | ['top', 'bottom', 'left', 'right'].map((s) => 52 | assignSideClasses(`p${s[0]}`, padding[s]) 53 | ); 54 | } 55 | 56 | // margin 57 | if (margin.top === margin.bottom && -1 != margin.top) { 58 | classes.push(`my-${margin.top}`); 59 | } else { 60 | ['top', 'bottom'].map((s) => assignSideClasses(`m${s[0]}`, margin[s])); 61 | } 62 | 63 | return classes; 64 | }; 65 | -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { RichText, InnerBlocks } = wp.blockEditor; 6 | const { applyFilters } = wp.hooks; 7 | 8 | /** 9 | * WordPress dependencies 10 | */ 11 | import classnames from 'classnames'; 12 | 13 | export default class Save extends Component { 14 | constructor() { 15 | super(...arguments); 16 | } 17 | 18 | render() { 19 | const { 20 | tabActive, 21 | textAlign, 22 | tabsData = [], 23 | tabBackgroundColor, 24 | tabTextColor, 25 | tabContentBackgroundColor, 26 | blockBackgroundColor, 27 | verticalAlign, 28 | instanceId, 29 | } = this.props.attributes; 30 | 31 | const { className = '', ...otherProps } = this.props; 32 | 33 | return ( 34 |
45 |
46 |
53 | {tabsData.map((tabData) => { 54 | const { slug, title } = tabData; 55 | const selected = tabActive === slug; 56 | return ( 57 | 71 | ); 72 | })} 73 |
74 |
75 |
76 |
85 | 86 |
87 |
88 |
89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /assets/tab-icons.js: -------------------------------------------------------------------------------- 1 | const tabIcons = { 2 | horizontal: ( 3 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ), 37 | vertical: ( 38 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ), 79 | }; 80 | 81 | export default tabIcons; 82 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/save.js: -------------------------------------------------------------------------------- 1 | import ResizableCarouselContainer from './components/resizable-carousel-container'; 2 | 3 | /** 4 | * External Dependencies. 5 | */ 6 | import classnames from 'classnames'; 7 | 8 | /** 9 | * WordPress dependencies 10 | */ 11 | const { Component, Fragment } = wp.element; 12 | const { InnerBlocks } = wp.blockEditor; 13 | 14 | export default class Save extends Component { 15 | constructor() { 16 | super(...arguments); 17 | } 18 | 19 | /** 20 | * Returns the indicators layout configuration for a given amount of tabs. 21 | * 22 | * @param {number} slides Amount of indicators to create. 23 | * @param {number} id Instance Id of this carousel block. 24 | * 25 | * @return {Object[]} Indicators layout configuration. 26 | */ 27 | createIndicators(slides, id) { 28 | let indicators = []; 29 | for (let i = 0; i < slides; i++) { 30 | indicators.push( 31 |
  • 37 | ); 38 | } 39 | 40 | return indicators; 41 | } 42 | 43 | render() { 44 | const { showIndicators, slides, showControls, instanceId, verticalAlign } = 45 | this.props.attributes; 46 | 47 | return ( 48 | 49 | {showIndicators && ( 50 |
      51 | {this.createIndicators(slides, instanceId)} 52 |
    53 | )} 54 |
    60 | 61 |
    62 | {showControls && ( 63 | 64 | 70 | 73 | 79 | 82 | 83 | )} 84 |
    85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/deprecated/2.0/save.deprecated.js: -------------------------------------------------------------------------------- 1 | import ResizableCarouselContainer from './resizable-carousel-container.deprecated'; 2 | 3 | /** 4 | * External Dependencies. 5 | */ 6 | import classnames from 'classnames'; 7 | 8 | /** 9 | * WordPress dependencies 10 | */ 11 | const { Component, Fragment } = wp.element; 12 | const { InnerBlocks } = wp.blockEditor; 13 | 14 | export default class Save extends Component { 15 | constructor() { 16 | super(...arguments); 17 | } 18 | 19 | /** 20 | * Returns the indicators layout configuration for a given amount of tabs. 21 | * 22 | * @param {number} slides Amount of indicators to create. 23 | * @param {number} id Instance Id of this carousel block. 24 | * 25 | * @return {Object[]} Indicators layout configuration. 26 | */ 27 | createIndicators(slides, id) { 28 | let indicators = []; 29 | for (let i = 0; i < slides; i++) { 30 | indicators.push( 31 |
  • 37 | ); 38 | } 39 | 40 | return indicators; 41 | } 42 | 43 | render() { 44 | const { showIndicators, slides, showControls, instanceId, verticalAlign } = 45 | this.props.attributes; 46 | 47 | return ( 48 | 49 | {showIndicators && ( 50 |
      51 | {this.createIndicators(slides, instanceId)} 52 |
    53 | )} 54 |
    60 | 61 |
    62 | {showControls && ( 63 | 64 | 70 | 73 | 79 | 82 | 83 | )} 84 |
    85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/deprecated/3.0/save.deprecated.js: -------------------------------------------------------------------------------- 1 | import ResizableCarouselContainer from './resizable-carousel-container.deprecated'; 2 | 3 | /** 4 | * External Dependencies. 5 | */ 6 | import classnames from 'classnames'; 7 | 8 | /** 9 | * WordPress dependencies 10 | */ 11 | const { Component, Fragment } = wp.element; 12 | const { InnerBlocks } = wp.blockEditor; 13 | 14 | export default class Save extends Component { 15 | constructor() { 16 | super(...arguments); 17 | } 18 | 19 | /** 20 | * Returns the indicators layout configuration for a given amount of tabs. 21 | * 22 | * @param {number} slides Amount of indicators to create. 23 | * @param {number} id Instance Id of this carousel block. 24 | * 25 | * @return {Object[]} Indicators layout configuration. 26 | */ 27 | createIndicators(slides, id) { 28 | let indicators = []; 29 | for (let i = 0; i < slides; i++) { 30 | indicators.push( 31 |
  • 37 | ); 38 | } 39 | 40 | return indicators; 41 | } 42 | 43 | render() { 44 | const { showIndicators, slides, showControls, instanceId, verticalAlign } = 45 | this.props.attributes; 46 | 47 | return ( 48 | 49 | {showIndicators && ( 50 |
      51 | {this.createIndicators(slides, instanceId)} 52 |
    53 | )} 54 |
    60 | 61 |
    62 | {showControls && ( 63 | 64 | 70 | 73 | 79 | 82 | 83 | )} 84 |
    85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/blocks/block-heading/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import CustomHeading from './components/custom-heading'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { RichText } = wp.blockEditor; 11 | 12 | /** 13 | * External Dependencies. 14 | */ 15 | import classnames from 'classnames'; 16 | 17 | export default class Save extends Component { 18 | constructor() { 19 | super(...arguments); 20 | } 21 | 22 | /** 23 | * Returns appropriate css class for given type, displayLevel, tagLevel, override values. 24 | * 25 | * @param {string} type Type of text - Heading, Subheading, Text-XL 26 | * @param {number} displayLevel Custom tag level. 27 | * @param {string} tagLevel Base tag level by default. 28 | * @param {boolean} override Toggle between using just the base class or overriding the style. 29 | * 30 | * @return {string} Appropriate css class based on configuration. 31 | */ 32 | c9TextStyleConfig(type, display, tag, override) { 33 | if (!override && 'c9-txl display-' != type) { 34 | return type.split(' ')[0]; 35 | } else if (0 == display) { 36 | return `${type}${tag}`; 37 | } else { 38 | return `${type}${display}`; 39 | } 40 | } 41 | 42 | render() { 43 | // Setup the attributes 44 | const { 45 | attributes: { 46 | heading, 47 | subheading, 48 | textColor, 49 | subTextColor, 50 | tagLevel, 51 | type, 52 | displayLevel, 53 | weight, 54 | overrideStyle, 55 | addSubheading, 56 | }, 57 | } = this.props; 58 | 59 | // Save the block markup for the front end 60 | return ( 61 | 62 | 73 | 74 | {addSubheading && ( 75 |
    { 77 | if (0 == display) { 78 | return `${type}${tag}`; 79 | } else { 80 | return `${type}${display}`; 81 | } 82 | })(type, displayLevel, tagLevel)} 83 | > 84 | 85 | {subheading.startsWith(' ') 86 | ? subheading 87 | : ' ' + subheading} 88 | 89 |
    90 | )} 91 |
    92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /assets/section-template-icons/about-picture-contact-card.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon-c9-social-share.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 copy 18 -------------------------------------------------------------------------------- /src/blocks/block-posts-grid/edit.js: -------------------------------------------------------------------------------- 1 | import Inspector from './components/inspector'; 2 | import Container from './components/container'; 3 | import cryptoRandomString from 'crypto-random-string'; 4 | 5 | const { Component, Fragment } = wp.element; 6 | const { InnerBlocks, BlockControls } = wp.blockEditor; 7 | 8 | const ALLOWED_BLOCKS = ['c9-blocks/post-grid']; 9 | 10 | class Edit extends Component { 11 | constructor() { 12 | super(...arguments); 13 | } 14 | 15 | componentDidMount() { 16 | this.initializeInstanceId(); 17 | } 18 | 19 | componentDidUpdate(prevProps) { 20 | this.checkBlockIdAndUpdate(); 21 | this.initializeInstanceId(prevProps); 22 | } 23 | 24 | initializeInstanceId(prevProps = {}) { 25 | const { instanceId } = this.props.attributes; 26 | if ( 27 | instanceId === undefined && 28 | (!prevProps.attributes || instanceId !== prevProps.attributes.instanceId) 29 | ) { 30 | const newInstanceId = 31 | this.props.instanceId || 32 | parseInt(cryptoRandomString({ length: 4, type: 'numeric' })); 33 | this.props.setAttributes({ instanceId: newInstanceId }); 34 | } 35 | } 36 | 37 | checkBlockIdAndUpdate = () => { 38 | const { attributes, setAttributes } = this.props; 39 | const { instanceId, containerVideoID } = attributes; 40 | 41 | const sanitizedInstanceId = instanceId && String(instanceId).replace(/[^a-zA-Z0-9-_]/g, ''); 42 | const sanitizedContainerVideoID = 43 | containerVideoID && String(containerVideoID).replace(/[^a-zA-Z0-9-_]/g, ''); 44 | 45 | if ( 46 | sanitizedInstanceId !== undefined && 47 | document.querySelectorAll(`#player-${sanitizedContainerVideoID}-${sanitizedInstanceId}`) 48 | .length > 1 49 | ) { 50 | const newInstanceId = parseInt( 51 | cryptoRandomString({ length: 4, type: 'numeric' }) 52 | ); 53 | setAttributes({ instanceId: newInstanceId }); 54 | } 55 | }; 56 | 57 | render() { 58 | return ( 59 | 60 | 61 | 62 | 63 | 64 | 69 | 70 | 71 | ); 72 | } 73 | } 74 | 75 | export default Edit; -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/styles/editor.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * #.# Editor Styles 3 | * 4 | * CSS for just Backend enqueued after style.scss 5 | * which makes it higher in priority. 6 | */ 7 | 8 | div[data-type="c9-blocks/vertical-tabs"] .c9-vertical-tabs { 9 | .tab-content [data-tab] { 10 | display: none; 11 | } 12 | 13 | .nav-item { 14 | transition: 0.15s border-color, 0.15s background-color, 0.15s opacity; 15 | } 16 | 17 | .c9-tab-admin-wrapper { 18 | position: relative; 19 | 20 | // show remove button on hover. 21 | &:not(:hover)>.c9-component-remove-button { 22 | opacity: 0; 23 | } 24 | } 25 | } 26 | 27 | .c9-component-remove-button { 28 | position: absolute; 29 | align-items: center; 30 | justify-content: center; 31 | top: 0; 32 | right: 0; 33 | width: 20px; 34 | height: 20px; 35 | padding: 0 !important; 36 | margin-top: -10px; 37 | margin-right: -10px; 38 | color: #fff; 39 | background-color: #4f5969; 40 | border-radius: 50%; 41 | opacity: 0.7; 42 | transition: 0.2s opacity, 0.2s background-color; 43 | 44 | &:hover { 45 | background-color: #c9586c; 46 | opacity: 1; 47 | } 48 | 49 | svg { 50 | width: auto; 51 | height: 0.8em; 52 | } 53 | } 54 | 55 | .c9-component-remove-button-confirm { 56 | 57 | .c9-component-remove-button-confirm-yep, 58 | .c9-component-remove-button-confirm-nope { 59 | padding: 0; 60 | margin-left: 5px; 61 | 62 | &:hover, 63 | &:focus { 64 | text-decoration: underline; 65 | } 66 | 67 | &, 68 | &:focus { 69 | background: none; 70 | box-shadow: none; 71 | } 72 | } 73 | 74 | .c9-component-remove-button-confirm-yep, 75 | .c9-component-remove-button-confirm-yep:focus { 76 | color: #c9586c; 77 | } 78 | 79 | .c9-component-remove-button-confirm-nope, 80 | .c9-component-remove-button-confirm-nope:focus { 81 | color: #999; 82 | } 83 | 84 | &.components-popover { 85 | &::before { 86 | border-color: transparent; 87 | } 88 | 89 | &.is-top::after { 90 | border-top-color: #191e23; 91 | } 92 | 93 | &.is-bottom::after { 94 | border-bottom-color: #191e23; 95 | } 96 | } 97 | 98 | .components-popover__content { 99 | padding: 4px 12px; 100 | color: $white; 101 | white-space: nowrap; 102 | background: #191e23; 103 | border-width: 0; 104 | } 105 | 106 | &:not(.is-mobile) .components-popover__content { 107 | min-width: 0; 108 | } 109 | 110 | .components-tooltip__shortcut { 111 | display: block; 112 | color: #7e8993; 113 | text-align: center; 114 | } 115 | } 116 | 117 | .editor-styles-wrapper div[data-type="c9-blocks/vertical-tabs"] { 118 | margin-top: 32px; 119 | margin-bottom: 32px; 120 | } 121 | -------------------------------------------------------------------------------- /src/components/remove-button/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { Component } = wp.element; 5 | const { Path, SVG } = wp.components; 6 | const { __ } = wp.i18n; 7 | const { Button, Popover } = wp.components; 8 | 9 | /** 10 | * Create an RemoveButton wrapper Component 11 | */ 12 | export default class RemoveButton extends Component { 13 | constructor() { 14 | super(...arguments); 15 | 16 | this.state = { 17 | confirmed: -1, 18 | }; 19 | } 20 | 21 | render() { 22 | const { 23 | onRemove, 24 | show, 25 | style, 26 | tooltipText = __('Remove block?', 'c9-blocks'), 27 | tooltipRemoveText = __('Remove', 'c9-blocks'), 28 | tooltipCancelText = __('Cancel', 'c9-blocks'), 29 | } = this.props; 30 | 31 | const { confirmed } = this.state; 32 | 33 | if (!show) { 34 | return ''; 35 | } 36 | 37 | return ( 38 | 70 | 80 | 81 | ) : ( 82 | '' 83 | )} 84 | 103 | 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/components/resizable-box/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * External dependencies 3 | */ 4 | import classnames from 'classnames'; 5 | import { Resizable } from 're-resizable'; 6 | 7 | function ResizableBox({ className, refHandle, showHandle = false, ...props }) { 8 | // Removes the inline styles in the drag handles. 9 | const handleStylesOverrides = { 10 | width: null, 11 | height: null, 12 | top: null, 13 | right: null, 14 | bottom: null, 15 | left: null, 16 | }; 17 | 18 | const handleClassName = 'components-resizable-box__handle'; 19 | const sideHandleClassName = 'components-resizable-box__side-handle'; 20 | const cornerHandleClassName = 'components-resizable-box__corner-handle'; 21 | 22 | return ( 23 | 88 | ); 89 | } 90 | 91 | export default ResizableBox; 92 | -------------------------------------------------------------------------------- /src/blocks/block-posts-grid/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | import Deprecated from './deprecated'; 8 | 9 | import Icon from '../../../assets/icon-c9-post-grid.svg'; 10 | 11 | /** 12 | * Styles 13 | */ 14 | import './styles/style.scss'; 15 | 16 | /** 17 | * WordPress dependencies 18 | */ 19 | const { __ } = wp.i18n; 20 | const { compose } = wp.compose; 21 | const { withSelect } = wp.data; 22 | const { registerBlockType } = wp.blocks; 23 | 24 | import cryptoRandomString from 'crypto-random-string'; 25 | 26 | // JavaScript sanitization for text 27 | const sanitizeText = (text) => { 28 | // Safely encode the text and remove any special characters 29 | return encodeURIComponent(text); 30 | }; 31 | 32 | registerBlockType('c9-blocks/posts-grid', { 33 | title: __('C9 Posts Grid', 'c9-blocks'), 34 | icon: Icon, 35 | category: 'c9-blocks', 36 | supports: { 37 | // fill in features 38 | }, 39 | keywords: [__('responsive', 'c9-blocks')], 40 | description: __( 41 | 'Display responsive grids with post content of any kind with filtering, sorting, and flexible layout settings. (Includes customizable outer container)', 42 | 'c9-blocks' 43 | ), 44 | example: { 45 | viewportWidth: '280', 46 | attributes: {}, 47 | innerBlocks: [ 48 | { 49 | name: 'c9-blocks/post-grid', 50 | attributes: { 51 | displayPostDate: true, 52 | displayPostExcerpt: true, 53 | displayPostAuthor: true, 54 | displayPostLink: true, 55 | displaySectionTitle: true, 56 | columns: 3, 57 | excerptLength: 20, 58 | sectionTitle: 'The Latest News', // Example sanitized content 59 | imageSize: 'c9-feature-medium-crop', 60 | }, 61 | }, 62 | ], 63 | }, 64 | attributes, 65 | 66 | // Render the block components 67 | edit: compose([ 68 | withSelect((select, ownProps) => { 69 | const { isBlockSelected, hasSelectedInnerBlock } = 70 | select('core/block-editor'); 71 | 72 | const { clientId } = ownProps; 73 | 74 | return { 75 | isSelectedBlockInRoot: 76 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 77 | instanceId: parseInt( 78 | cryptoRandomString({ length: 4, type: 'numeric' }) 79 | ), 80 | }; 81 | }), 82 | ])(Edit), 83 | 84 | // Save the attributes and markup 85 | save: (props) => { 86 | const { attributes } = props; 87 | 88 | // Example of sanitizing before save 89 | const sanitizedSectionTitle = sanitizeText(attributes.sectionTitle); 90 | 91 | return ; 92 | }, 93 | 94 | deprecated: Deprecated, 95 | }); -------------------------------------------------------------------------------- /src/blocks/block-grid-container/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | instanceId: { 6 | type: 'number', 7 | }, 8 | lockMovement: { 9 | type: 'boolean', 10 | default: true, 11 | }, 12 | rows: { 13 | type: 'number', 14 | default: 1, 15 | }, 16 | align: { 17 | type: 'string', 18 | default: 'full', 19 | }, 20 | containerImgURL: { 21 | type: 'string', 22 | }, 23 | containerHue: { 24 | type: 'string', 25 | default: undefined, 26 | }, 27 | containerOpacity: { 28 | type: 'number', 29 | default: 10, 30 | }, 31 | bgImgSize: { 32 | type: 'string', 33 | default: 'cover', 34 | }, 35 | bgCustomX: { 36 | type: 'object', 37 | default: { 38 | size: 'auto', 39 | unit: 'px', 40 | }, 41 | }, 42 | bgCustomY: { 43 | type: 'object', 44 | default: { 45 | size: 'auto', 46 | unit: 'px', 47 | }, 48 | }, 49 | bgImgRepeat: { 50 | type: 'string', 51 | default: 'no-repeat', 52 | }, 53 | // true evaluates to backgroundAttachment fixed, false to scroll 54 | bgImgAttach: { 55 | type: 'boolean', 56 | default: false, 57 | }, 58 | overlayHue: { 59 | type: 'string', 60 | default: undefined, 61 | }, 62 | overlayOpacity: { 63 | type: 'number', 64 | default: 10, 65 | }, 66 | blendMode: { 67 | type: 'string', 68 | default: 'normal', 69 | }, 70 | linkedValToggle: { 71 | type: 'boolean', 72 | default: true, 73 | }, 74 | minScreenHeight: { 75 | type: 'number', 76 | default: 20, 77 | }, 78 | containerMargin: { 79 | type: 'object', 80 | default: { 81 | linked: true, 82 | icon: 'admin-links', 83 | unit: 'px', 84 | top: '-1', 85 | bottom: '-1', 86 | }, 87 | }, 88 | containerPadding: { 89 | type: 'object', 90 | default: { 91 | linked: true, 92 | icon: 'admin-links', 93 | top: '3', 94 | bottom: '3', 95 | left: '3', 96 | right: '3', 97 | }, 98 | }, 99 | focalPoint: { 100 | type: 'object', 101 | default: { 102 | x: 0.5, 103 | y: 0.5, 104 | }, 105 | }, 106 | videoType: { 107 | type: 'string', 108 | default: 'upload', 109 | }, 110 | containerVideoURL: { 111 | type: 'string', 112 | default: '', 113 | }, 114 | containerVideoID: { 115 | type: 'string', 116 | default: '', 117 | }, 118 | cannotEmbed: { 119 | type: 'boolean', 120 | default: false, 121 | }, 122 | bgImgSizeMobile: { 123 | type: 'string', 124 | default: 'cover', 125 | }, 126 | focalPointMobile: { 127 | type: 'object', 128 | default: { 129 | x: 0.5, 130 | y: 0.5, 131 | }, 132 | }, 133 | overrideMobile: { 134 | type: 'boolean', 135 | default: false, 136 | }, 137 | bgCustomXMobile: { 138 | type: 'object', 139 | default: { 140 | size: 'auto', 141 | unit: 'px', 142 | }, 143 | }, 144 | bgCustomYMobile: { 145 | type: 'object', 146 | default: { 147 | size: 'auto', 148 | unit: 'px', 149 | }, 150 | }, 151 | }; 152 | 153 | export default attributes; 154 | -------------------------------------------------------------------------------- /src/components/templates-modal/section-templates/templates/inform-map-embed-focus.js: -------------------------------------------------------------------------------- 1 | export default { 2 | icon: ( 3 | 7 | ), 8 | title: 'Inform Map Embed Focus', 9 | description: 'Address • Map Embed Code • Button Link', 10 | markup: ` 11 |
    12 |
    13 |
    14 | 15 |
    16 | 17 | 18 | 19 |
    20 |
    21 |

    Address
    2999 N. Chicago Ave.
    Ste. 207
    Chicago, Illinois 60666

    22 | 23 | 24 | 25 |

    Phone
    555-666-4000

    26 | 27 | 28 | 29 |
    30 | 31 |
    32 |
    33 |
    34 |
    35 |
    36 | `, 37 | }; 38 | -------------------------------------------------------------------------------- /src/blocks/block-column-container/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | const attributes = { 5 | align: { 6 | type: 'string', 7 | default: '', 8 | }, 9 | columns: { 10 | type: 'number', 11 | }, 12 | columnMaxWidth: { 13 | type: 'number', 14 | }, 15 | centerColumns: { 16 | type: 'boolean', 17 | default: true, 18 | }, 19 | columnsGap: { 20 | type: 'number', 21 | default: 2, 22 | }, 23 | responsiveToggle: { 24 | type: 'boolean', 25 | default: true, 26 | }, 27 | layout: { 28 | type: 'string', 29 | }, 30 | containerWidth: { 31 | type: 'string', 32 | default: 'container', 33 | }, 34 | verticalAlign: { 35 | type: 'string', 36 | }, 37 | containerImgURL: { 38 | type: 'string', 39 | }, 40 | bgImgSize: { 41 | type: 'string', 42 | default: 'cover', 43 | }, 44 | bgCustomX: { 45 | type: 'object', 46 | default: { 47 | size: 'auto', 48 | unit: 'px', 49 | }, 50 | }, 51 | bgCustomY: { 52 | type: 'object', 53 | default: { 54 | size: 'auto', 55 | unit: 'px', 56 | }, 57 | }, 58 | bgImgRepeat: { 59 | type: 'string', 60 | default: 'no-repeat', 61 | }, 62 | // true evaluates to backgroundAttachment scroll, false to fixed 63 | bgImgAttach: { 64 | type: 'boolean', 65 | default: false, 66 | }, 67 | overlayHue: { 68 | type: 'string', 69 | default: undefined, 70 | }, 71 | overlayOpacity: { 72 | type: 'number', 73 | default: 10, 74 | }, 75 | blendMode: { 76 | type: 'string', 77 | default: 'normal', 78 | }, 79 | linkedValToggle: { 80 | type: 'boolean', 81 | default: true, 82 | }, 83 | minScreenHeight: { 84 | type: 'number', 85 | default: 10, 86 | }, 87 | containerMargin: { 88 | type: 'object', 89 | default: { 90 | linked: true, 91 | icon: 'admin-links', 92 | top: '-1', 93 | bottom: '-1', 94 | }, 95 | }, 96 | containerPadding: { 97 | type: 'object', 98 | default: { 99 | linked: true, 100 | icon: 'admin-links', 101 | top: '-1', 102 | bottom: '-1', 103 | left: '-1', 104 | right: '-1', 105 | }, 106 | }, 107 | containerHue: { 108 | type: 'string', 109 | default: undefined, 110 | }, 111 | containerOpacity: { 112 | type: 'number', 113 | default: 10, 114 | }, 115 | focalPoint: { 116 | type: 'object', 117 | default: { 118 | x: 0.5, 119 | y: 0.5, 120 | }, 121 | }, 122 | bgImgSizeMobile: { 123 | type: 'string', 124 | default: 'cover', 125 | }, 126 | focalPointMobile: { 127 | type: 'object', 128 | default: { 129 | x: 0.5, 130 | y: 0.5, 131 | }, 132 | }, 133 | overrideMobile: { 134 | type: 'boolean', 135 | default: false, 136 | }, 137 | bgCustomXMobile: { 138 | type: 'object', 139 | default: { 140 | size: 'auto', 141 | unit: 'px', 142 | }, 143 | }, 144 | bgCustomYMobile: { 145 | type: 'object', 146 | default: { 147 | size: 'auto', 148 | unit: 'px', 149 | }, 150 | }, 151 | flipColumnsMobile: { 152 | type: 'boolean', 153 | default: false, 154 | }, 155 | }; 156 | 157 | export default attributes; 158 | -------------------------------------------------------------------------------- /src/blocks/block-cta/attributes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block Attributes 3 | */ 4 | 5 | const attributes = { 6 | type: { 7 | type: 'string', 8 | value: 'c9-p', 9 | }, 10 | buttonText: { 11 | type: 'string', 12 | }, 13 | buttonUrl: { 14 | type: 'string', 15 | source: 'attribute', 16 | selector: 'a', 17 | attribute: 'href', 18 | }, 19 | buttonAlignment: { 20 | type: 'string', 21 | default: 'left', 22 | }, 23 | buttonBackgroundColor: { 24 | type: 'string', 25 | }, 26 | buttonTextColor: { 27 | type: 'string', 28 | default: '#ffffff', 29 | }, 30 | buttonSize: { 31 | type: 'string', 32 | default: 'c9-button-size-medium', 33 | }, 34 | buttonShape: { 35 | type: 'string', 36 | default: 'square', 37 | }, 38 | buttonTarget: { 39 | type: 'boolean', 40 | default: false, 41 | }, 42 | blendMode: { 43 | type: 'string', 44 | default: 'normal', 45 | }, 46 | ctaTitle: { 47 | type: 'array', 48 | selector: '.c9-cta-title', 49 | source: 'children', 50 | }, 51 | ctaLayout: { 52 | type: 'string', 53 | default: 'two-thirds', 54 | }, 55 | ctaMargin: { 56 | type: 'object', 57 | default: { 58 | linked: true, 59 | icon: 'admin-links', 60 | unit: 'px', 61 | top: '-1', 62 | bottom: '-1', 63 | }, 64 | }, 65 | ctaPadding: { 66 | type: 'object', 67 | default: { 68 | linked: true, 69 | icon: 'admin-links', 70 | top: '5', 71 | bottom: '5', 72 | left: '5', 73 | right: '5', 74 | }, 75 | }, 76 | ctaTextFontSize: { 77 | type: 'number', 78 | }, 79 | ctaText: { 80 | type: 'array', 81 | selector: '.c9-cta-text', 82 | source: 'children', 83 | }, 84 | ctaWidth: { 85 | type: 'string', 86 | default: 'container', 87 | }, 88 | ctaBackgroundColor: { 89 | type: 'string', 90 | }, 91 | ctaBackgroundOpacity: { 92 | type: 'number', 93 | default: 10, 94 | }, 95 | ctaTextColor: { 96 | type: 'string', 97 | }, 98 | imgURL: { 99 | type: 'string', 100 | source: 'attribute', 101 | attribute: 'src', 102 | selector: 'img', 103 | }, 104 | imgID: { 105 | type: 'number', 106 | }, 107 | imgAlt: { 108 | type: 'string', 109 | source: 'attribute', 110 | attribute: 'alt', 111 | selector: 'img', 112 | }, 113 | imgSize: { 114 | type: 'string', 115 | default: 'cover', 116 | }, 117 | focalPoint: { 118 | type: 'object', 119 | default: { 120 | x: 0.5, 121 | y: 0.5, 122 | }, 123 | }, 124 | // true evaluates to backgroundAttachment fixed, false to scroll 125 | imgAttach: { 126 | type: 'boolean', 127 | default: false, 128 | }, 129 | // Deprecated 130 | ctaTitleFontSize: { 131 | type: 'string', 132 | default: '32', 133 | }, 134 | align: { 135 | type: 'string', 136 | default: '', 137 | }, 138 | disableToolbar: { 139 | type: 'boolean', 140 | default: false, 141 | }, 142 | fontSize: { 143 | type: 'string', 144 | }, 145 | customFontSize: { 146 | type: 'string', 147 | }, 148 | dropCap: { 149 | type: 'boolean', 150 | default: false, 151 | }, 152 | }; 153 | 154 | export default attributes; 155 | -------------------------------------------------------------------------------- /src/blocks/block-post-grid/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | 6 | /** 7 | * Styles 8 | */ 9 | import './styles/style.scss'; 10 | 11 | import Icon from '../../../assets/icon-c9-post-grid.svg'; 12 | 13 | /** 14 | * WordPress dependencies 15 | */ 16 | const { registerBlockType } = wp.blocks; 17 | const { compose } = wp.compose; 18 | const { withSelect } = wp.data; 19 | 20 | /** 21 | * External Dependencies. 22 | */ 23 | import classnames from 'classnames'; 24 | import isUndefined from 'lodash/isUndefined'; 25 | import pickBy from 'lodash/pickBy'; 26 | 27 | registerBlockType('c9-blocks/post-grid', { 28 | icon: Icon, 29 | parent: ['c9-blocks/post-container'], 30 | /* Add alignment to block wrapper. */ 31 | supports: { 32 | inserter: false, 33 | reusable: false, 34 | }, 35 | getEditWrapperProps({ align }) { 36 | if ( 37 | 'full' === align || 38 | 'wide' === align || 39 | 'narrow' === align || 40 | '' === align 41 | ) { 42 | return { 'data-align': align }; 43 | } 44 | }, 45 | 46 | edit: compose([ 47 | withSelect((select, props) => { 48 | const { order, categories, tags, filterByCategory, filterByTag } = 49 | props.attributes; 50 | 51 | const { getEntityRecords, getMedia } = select('core', 'c9-blocks'); 52 | 53 | const setCategories = filterByCategory ? categories : undefined; 54 | const setTags = filterByTag ? tags : undefined; 55 | 56 | const latestPostsQuery = pickBy( 57 | { 58 | categories: setCategories, 59 | tags: setTags, 60 | order, 61 | orderby: props.attributes.orderBy, 62 | // eslint-disable-next-line camelcase 63 | per_page: props.attributes.postsToShow, 64 | offset: props.attributes.offset, 65 | }, 66 | (value) => !isUndefined(value) 67 | ); 68 | 69 | return { 70 | latestPosts: getEntityRecords( 71 | 'postType', 72 | props.attributes.postType, 73 | latestPostsQuery 74 | ), 75 | getMedia, 76 | }; 77 | }), 78 | ])(Edit), 79 | 80 | // Render via PHP 81 | save() { 82 | return null; 83 | }, 84 | }); 85 | 86 | /* Add the vertical column alignment class to the column container block. */ 87 | const withClientIdClassName = wp.compose.createHigherOrderComponent( 88 | (BlockListBlock) => { 89 | return (props) => { 90 | const blockName = props.block.name; 91 | 92 | if ('c9-blocks/post-grid' === blockName) { 93 | return ( 94 | 103 | ); 104 | } else { 105 | return ; 106 | } 107 | }; 108 | }, 109 | 'withClientIdClassName' 110 | ); 111 | 112 | wp.hooks.addFilter( 113 | 'editor.BlockListBlock', 114 | 'c9-blocks/add-vertical-align-class', 115 | withClientIdClassName 116 | ); 117 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/components/swap-slide-toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | const { dispatch } = wp.data; 7 | const { createSuccessNotice } = dispatch('core/notices'); 8 | 9 | /** 10 | * Control Settings 11 | */ 12 | const DEFAULT_WIDTH_CONTROLS = [ 13 | { 14 | icon: 'arrow-left-alt', 15 | title: __('Swap Slide toward Left', 'c9-blocks'), 16 | isLeft: true, 17 | }, 18 | { 19 | icon: 'arrow-right-alt', 20 | title: __('Swap Slide toward Right', 'c9-blocks'), 21 | isLeft: false, 22 | }, 23 | ]; 24 | 25 | const TIMEOUT_DELAY = 250; 26 | 27 | /** 28 | * Create a Width Toolbar Component 29 | */ 30 | export function SwapSlideToolbar({ 31 | swapSlide, 32 | activeSlide, 33 | slides, 34 | carouselRef, 35 | widthControls = DEFAULT_WIDTH_CONTROLS, 36 | }) { 37 | return ( 38 | 39 | {widthControls.map((control) => { 40 | const { isLeft, ...buttonProps } = control; 41 | return ( 42 | { 46 | const $ = window.jQuery; 47 | let config = { opacity: 0, marginLeft: '200px' }; 48 | if (isLeft) { 49 | config.marginLeft = '-200px'; 50 | } 51 | $('.carousel-inner', carouselRef.current).animate( 52 | config, 53 | 400, 54 | 'swing', 55 | () => { 56 | if (isLeft) { 57 | const targetSlide = 58 | -1 < activeSlide - 1 ? activeSlide - 1 : slides - 1; 59 | swapSlide(activeSlide, targetSlide); 60 | setTimeout(() => { 61 | $(carouselRef.current).carousel('prev'); 62 | $('.carousel-inner', carouselRef.current).animate({ 63 | opacity: 100, 64 | marginLeft: 0, 65 | }); 66 | createSuccessNotice( 67 | `c9-blocks/carousel: #${ 68 | activeSlide + 1 69 | } is swapped with Slide #${targetSlide + 1}`, 70 | { id: 'swapBlockSlideNotice' } 71 | ); 72 | }, TIMEOUT_DELAY); 73 | } else { 74 | const targetSlide = 75 | slides > activeSlide + 1 ? activeSlide + 1 : 0; 76 | swapSlide(activeSlide, targetSlide); 77 | setTimeout(() => { 78 | $(carouselRef.current).carousel('next'); 79 | $('.carousel-inner', carouselRef.current).animate({ 80 | opacity: 100, 81 | marginLeft: 0, 82 | }); 83 | createSuccessNotice( 84 | `c9-blocks/carousel: #${ 85 | activeSlide + 1 86 | } is swapped with Slide #${targetSlide + 1}`, 87 | { id: 'swapBlockSlideNotice' } 88 | ); 89 | }, TIMEOUT_DELAY); 90 | } 91 | } 92 | ); 93 | }} 94 | /> 95 | ); 96 | })} 97 | 98 | ); 99 | } 100 | 101 | export default SwapSlideToolbar; 102 | -------------------------------------------------------------------------------- /src/components/templates-modal/section-templates/templates/introduce-page-title.js: -------------------------------------------------------------------------------- 1 | export default { 2 | icon: ( 3 | 7 | ), 8 | title: 'Introduce Page Title', 9 | description: '50 Words • Page Title • Logo', 10 | markup: ` 11 |
    12 |
    13 |
    14 |

    Company Name

    15 | 16 | 17 | 18 |

    Mutually Making Weird Words Meaningless

    19 | 20 | 21 | 22 |

    Click the outer edges of the container to change the background color, image, or video! Once you highlight the C9 Grid block, click on it, and you can make changes under Background Settings in the sidebar from the WordPress edit screen.

    23 |
    24 | 25 | 26 | 27 |
    28 |
    Logo
    29 |
    30 |
    31 |
    32 | `, 33 | }; 34 | -------------------------------------------------------------------------------- /src/blocks/block-carousel/deprecated/1.0/save.deprecated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * External Dependencies. 3 | */ 4 | import classnames from 'classnames'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component, Fragment } = wp.element; 10 | const { InnerBlocks } = wp.blockEditor; 11 | const { applyFilters } = wp.hooks; 12 | 13 | export default class Save extends Component { 14 | constructor() { 15 | super(...arguments); 16 | } 17 | 18 | /** 19 | * Returns the indicators layout configuration for a given amount of tabs. 20 | * 21 | * @param {number} slides Amount of indicators to create. 22 | * @param {number} id Instance Id of this carousel block. 23 | * 24 | * @return {Object[]} Indicators layout configuration. 25 | */ 26 | createIndicators(slides, id) { 27 | let indicators = []; 28 | for (let i = 0; i < slides; i++) { 29 | indicators.push( 30 |
  • 36 | ); 37 | } 38 | 39 | return indicators; 40 | } 41 | 42 | render() { 43 | const { 44 | showIndicators, 45 | autoSlide, 46 | slides, 47 | showControls, 48 | instanceId, 49 | wrapAround, 50 | slideTime, 51 | slideMaxHeight, 52 | slideEqualHeight, 53 | verticalAlign, 54 | } = this.props.attributes; 55 | 56 | const { className = '' } = this.props; 57 | 58 | return ( 59 | 110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /assets/c9-feather-logo-gradient.svg: -------------------------------------------------------------------------------- 1 | 4 | feather gradient logo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | 8 | /** 9 | * Styles 10 | */ 11 | import './styles/style.scss'; 12 | 13 | import Icon from '../../../assets/icon-c9-tabs-vertical-tabs.svg'; 14 | 15 | /** 16 | * WordPress dependencies 17 | */ 18 | const { __ } = wp.i18n; 19 | const { compose } = wp.compose; 20 | const { withSelect, withDispatch } = wp.data; 21 | const { registerBlockType } = wp.blocks; 22 | 23 | registerBlockType('c9-blocks/vertical-tabs', { 24 | title: __('C9 Vertical Tabs', 'c9-blocks'), 25 | icon: Icon, 26 | category: 'c9-blocks', 27 | supports: { 28 | // fill in features 29 | }, 30 | keywords: [ 31 | __('tabs', 'c9-blocks'), 32 | __('container', 'c9-blocks'), 33 | __('responsive', 'c9-blocks'), 34 | ], 35 | description: __( 36 | 'Display tabbed content with a vertical button interface for switching between multiple types of content.', 37 | 'c9-blocks' 38 | ), 39 | example: { 40 | viewportWidth: '300', 41 | attributes: { 42 | tabsData: [ 43 | { slug: 'tab-1', title: 'Tab 1' }, 44 | { slug: 'tab-2', title: 'Tab 2' }, 45 | { slug: 'tab-3', title: 'Tab 3' }, 46 | ], 47 | }, 48 | innerBlocks: [ 49 | { 50 | name: 'c9-blocks/vertical-tabs-tab', 51 | attributes: { 52 | slug: 'tab-1', 53 | tabActive: 'tab-1', 54 | }, 55 | innerBlocks: [ 56 | { 57 | name: 'c9-blocks/heading', 58 | attributes: { 59 | heading: 'Tab Headline', 60 | tagLevel: 3, 61 | }, 62 | }, 63 | { 64 | name: 'core/paragraph', 65 | attributes: { 66 | content: 67 | 'Vertical tabs can be aligned horizontally and vertically inside of the tab container. Almost any block can go inside of tabs including buttons, text, videos, and image galleries.', 68 | }, 69 | }, 70 | ], 71 | }, 72 | { 73 | name: 'c9-blocks/vertical-tabs-tab', 74 | attributes: { 75 | slug: 'tab-2', 76 | tabActive: 'tab-1', 77 | }, 78 | }, 79 | { 80 | name: 'c9-blocks/vertical-tabs-tab', 81 | attributes: { 82 | slug: 'tab-3', 83 | tabActive: 'tab-1', 84 | }, 85 | }, 86 | ], 87 | }, 88 | attributes, 89 | // Render the block components 90 | edit: compose([ 91 | withSelect((select, ownProps) => { 92 | const { getBlock, isBlockSelected, hasSelectedInnerBlock } = 93 | select('core/block-editor'); 94 | 95 | const { clientId } = ownProps; 96 | 97 | return { 98 | block: getBlock(clientId), 99 | isSelectedBlockInRoot: 100 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 101 | }; 102 | }), 103 | withDispatch((dispatch) => { 104 | const { updateBlockAttributes, removeBlock } = 105 | dispatch('core/block-editor'); 106 | 107 | return { 108 | updateBlockAttributes, 109 | removeBlock, 110 | }; 111 | }), 112 | ])(Edit), 113 | 114 | // Save the attributes and markup 115 | save: (props) => { 116 | return ; 117 | }, 118 | }); 119 | -------------------------------------------------------------------------------- /src/blocks/block-horizontal-tabs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | 8 | /** 9 | * Styles 10 | */ 11 | import './styles/style.scss'; 12 | 13 | import Icon from '../../../assets/icon-c9-tabs-horizontal.svg'; 14 | 15 | /** 16 | * WordPress dependencies 17 | */ 18 | const { __ } = wp.i18n; 19 | const { compose } = wp.compose; 20 | const { withSelect, withDispatch } = wp.data; 21 | const { registerBlockType } = wp.blocks; 22 | 23 | registerBlockType('c9-blocks/horizontal-tabs', { 24 | title: __('C9 Horizontal Tabs', 'c9-blocks'), 25 | icon: Icon, 26 | category: 'c9-blocks', 27 | supports: { 28 | // fill in features 29 | }, 30 | keywords: [ 31 | __('tabs', 'c9-blocks'), 32 | __('container', 'c9-blocks'), 33 | __('responsive', 'c9-blocks'), 34 | ], 35 | description: __( 36 | 'Display tabbed content with a horizontal button interface for switching between multiple types of content.', 37 | 'c9-blocks' 38 | ), 39 | example: { 40 | viewportWidth: '280', 41 | attributes: { 42 | buttonsAlign: 'center', 43 | tabsData: [ 44 | { slug: 'tab-1', title: 'Tab 1' }, 45 | { slug: 'tab-2', title: 'Tab 2' }, 46 | { slug: 'tab-3', title: 'Tab 3' }, 47 | ], 48 | }, 49 | innerBlocks: [ 50 | { 51 | name: 'c9-blocks/horizontal-tabs-tab', 52 | attributes: { slug: 'tab-1', tabActive: 'tab-1' }, 53 | innerBlocks: [ 54 | { 55 | name: 'c9-blocks/heading', 56 | attributes: { 57 | heading: 'Tab Number One', 58 | tagLevel: 3, 59 | }, 60 | }, 61 | { 62 | name: 'core/paragraph', 63 | attributes: { 64 | content: 65 | 'Horizontal tabs can be aligned vertically inside of the tab container, and colors for tabs can be customized with labels, colors, and alignments. Almost any block can go inside of tabs themselves including buttons, text, videos, and image galleries.', 66 | }, 67 | }, 68 | ], 69 | }, 70 | { 71 | name: 'c9-blocks/horizontal-tabs-tab', 72 | attributes: { slug: 'tab-2', tabActive: 'tab-1' }, 73 | }, 74 | { 75 | name: 'c9-blocks/horizontal-tabs-tab', 76 | attributes: { slug: 'tab-3', tabActive: 'tab-1' }, 77 | }, 78 | ], 79 | }, 80 | attributes, 81 | // Render the block components 82 | edit: compose([ 83 | withSelect((select, ownProps) => { 84 | const { getBlock, isBlockSelected, hasSelectedInnerBlock } = 85 | select('core/block-editor'); 86 | 87 | const { clientId } = ownProps; 88 | 89 | return { 90 | block: getBlock(clientId), 91 | isSelectedBlockInRoot: 92 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 93 | }; 94 | }), 95 | withDispatch((dispatch) => { 96 | const { updateBlockAttributes, removeBlock } = 97 | dispatch('core/block-editor'); 98 | 99 | return { 100 | updateBlockAttributes, 101 | removeBlock, 102 | }; 103 | }), 104 | ])(Edit), 105 | 106 | // Save the attributes and markup 107 | save: (props) => { 108 | return ; 109 | }, 110 | }); 111 | -------------------------------------------------------------------------------- /src/blocks/block-vertical-tabs/components/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { Component } = wp.element; 6 | const { InspectorControls, AlignmentToolbar, PanelColorSettings } = 7 | wp.blockEditor; 8 | const { BaseControl } = wp.components; 9 | const { ContrastChecker } = wp.blockEditor; 10 | 11 | /** 12 | * Create an Inspector Controls wrapper Component 13 | */ 14 | export default class Inspector extends Component { 15 | constructor() { 16 | super(...arguments); 17 | } 18 | 19 | render() { 20 | const { attributes, setAttributes } = this.props; 21 | 22 | const { 23 | textAlign, 24 | tabBackgroundColor, 25 | tabTextColor, 26 | tabContentBackgroundColor, 27 | blockBackgroundColor, 28 | } = attributes; 29 | 30 | return ( 31 | 32 | 33 | setAttributes({ textAlign: value })} 36 | controls={['left', 'center', 'right']} 37 | /> 38 | 39 | 46 | setAttributes({ blockBackgroundColor: value }), 47 | label: __('Background Color', 'c9-blocks'), 48 | }, 49 | ]} 50 | > 51 | 57 | 58 | setAttributes({ tabBackgroundColor: value }), 65 | label: __('Background Color', 'c9-blocks'), 66 | }, 67 | { 68 | value: tabTextColor, 69 | onChange: (value) => setAttributes({ tabTextColor: value }), 70 | label: __('Text Color', 'c9-blocks'), 71 | }, 72 | ]} 73 | > 74 | 82 | 83 | 90 | setAttributes({ tabContentBackgroundColor: value }), 91 | label: __('Background Color', 'c9-blocks'), 92 | }, 93 | ]} 94 | > 95 | 101 | 102 | 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/blocks/block-image-carousel/components/swap-slide-toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies 3 | */ 4 | const { __ } = wp.i18n; 5 | const { ToolbarGroup, ToolbarButton } = wp.components; 6 | const { dispatch } = wp.data; 7 | const { createSuccessNotice } = dispatch('core/notices'); 8 | 9 | /** 10 | * Control Settings 11 | */ 12 | const DEFAULT_WIDTH_CONTROLS = [ 13 | { 14 | icon: 'arrow-left-alt', 15 | title: __('Swap Slide toward Left', 'c9-blocks'), 16 | isLeft: true, 17 | }, 18 | { 19 | icon: 'arrow-right-alt', 20 | title: __('Swap Slide toward Right', 'c9-blocks'), 21 | isLeft: false, 22 | }, 23 | ]; 24 | 25 | const TIMEOUT_DELAY = 250; 26 | 27 | /** 28 | * Create a Width Toolbar Component 29 | */ 30 | export function SwapSlideToolbar({ 31 | swapSlide, 32 | slides, 33 | carouselRef, 34 | widthControls = DEFAULT_WIDTH_CONTROLS, 35 | }) { 36 | return ( 37 | 38 | {widthControls.map((control, index) => { 39 | const { isLeft, ...buttonProps } = control; 40 | const key = control.title || index; 41 | return ( 42 | { 46 | const $ = window.jQuery; 47 | const activeSlide = $('div.active', carouselRef.current).index(); 48 | let config = { opacity: 0, marginLeft: '200px' }; 49 | if (isLeft) { 50 | config.marginLeft = '-200px'; 51 | } 52 | $('.carousel-inner', carouselRef.current).animate( 53 | config, 54 | 400, 55 | 'swing', 56 | () => { 57 | if (isLeft) { 58 | const targetSlide = 59 | -1 < activeSlide - 1 ? activeSlide - 1 : slides - 1; 60 | swapSlide(activeSlide, targetSlide); 61 | setTimeout(() => { 62 | $(carouselRef.current).carousel('prev'); 63 | $('.carousel-inner', carouselRef.current).animate({ 64 | opacity: 100, 65 | marginLeft: 0, 66 | }); 67 | createSuccessNotice( 68 | `c9-blocks/image-carousel: #${ 69 | activeSlide + 1 70 | } is swapped with Slide #${targetSlide + 1}`, 71 | { id: 'swapBlockSlideNotice' } 72 | ); 73 | }, TIMEOUT_DELAY); 74 | } else { 75 | const targetSlide = 76 | slides > activeSlide + 1 ? activeSlide + 1 : 0; 77 | swapSlide(activeSlide, targetSlide); 78 | setTimeout(() => { 79 | $(carouselRef.current).carousel('next'); 80 | $('.carousel-inner', carouselRef.current).animate({ 81 | opacity: 100, 82 | marginLeft: 0, 83 | }); 84 | createSuccessNotice( 85 | `c9-blocks/image-carousel: #${ 86 | activeSlide + 1 87 | } is swapped with Slide #${targetSlide + 1}`, 88 | { id: 'swapBlockSlideNotice' } 89 | ); 90 | }, TIMEOUT_DELAY); 91 | } 92 | } 93 | ); 94 | }} 95 | /> 96 | ); 97 | })} 98 | 99 | ); 100 | } 101 | 102 | export default SwapSlideToolbar; 103 | -------------------------------------------------------------------------------- /assets/section-template-icons/single-profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Single Bio 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/blocks/block-cta/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import CallToAction from './components/cta'; 5 | 6 | /** 7 | * WordPress dependencies 8 | */ 9 | const { Component } = wp.element; 10 | const { RichText } = wp.blockEditor; 11 | 12 | /** 13 | * External Dependencies. 14 | */ 15 | import classnames from 'classnames'; 16 | 17 | export default class Save extends Component { 18 | constructor() { 19 | super(...arguments); 20 | } 21 | 22 | /** 23 | * Returns appropriate css class for given width and text/button option. 24 | * 25 | * @param {string} ctaWidth Wideness of given field. 26 | * @param {string} textOrButton Choice of text or button. 27 | * 28 | * @return {string} Appropriate css class based on configuration. 29 | */ 30 | layoutClass(ctaWidth, textOrButton) { 31 | if ('two-thirds' == ctaWidth) { 32 | if ('text' == textOrButton) { 33 | return 'col-md-8'; 34 | } 35 | return 'col-md-4'; 36 | } else if ('three-quarters' == ctaWidth) { 37 | if ('text' == textOrButton) { 38 | return 'col-md-9'; 39 | } 40 | return 'col-md-3'; 41 | } 42 | } 43 | 44 | render() { 45 | // Setup the attributes 46 | const { 47 | attributes: { 48 | buttonText, 49 | buttonUrl, 50 | buttonBackgroundColor, 51 | buttonTextColor, 52 | buttonSize, 53 | buttonShape, 54 | buttonTarget, 55 | ctaText, 56 | ctaTextColor, 57 | imgURL, 58 | imgAlt, 59 | ctaLayout, 60 | type, 61 | customFontSize, 62 | }, 63 | } = this.props; 64 | 65 | return ( 66 | 67 | {imgURL && !!imgURL.length && ( 68 |
    69 | {imgAlt} 74 |
    75 | )} 76 | 77 |
    82 | {ctaText && ( 83 | 92 | )} 93 |
    94 | {buttonText && ( 95 | 120 | )} 121 |
    122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/components/templates-modal/section-templates/templates/watch-embedded-video.js: -------------------------------------------------------------------------------- 1 | export default { 2 | icon: ( 3 | 7 | ), 8 | title: 'Watch Embedded Video', 9 | description: '50 Words • Button Link • Video Embed', 10 | markup: ` 11 |
    12 |
    13 |
    14 |

    Watch the Video

    15 | 16 | 17 | 18 |

    Cause that's how we get paid

    19 | 20 | 21 | 22 |
    23 | 24 | 25 | 26 |

    Upload your own MP4 files, or link to a YouTube video. YouTube video links in buttons will open up in a lightbox so users stay on your site.

    27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
    36 | 37 | 38 | 39 |
    40 |
    41 |
    42 |
    43 |
    44 | `, 45 | }; 46 | -------------------------------------------------------------------------------- /src/blocks/block-image-carousel/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | import Deprecated from './deprecated'; 8 | 9 | /** 10 | * Styles 11 | */ 12 | import './styles/style.scss'; 13 | 14 | import Icon from '../../../assets/icon-c9-image-carousel.svg'; 15 | 16 | import cryptoRandomString from 'crypto-random-string'; 17 | 18 | /** 19 | * WordPress dependencies 20 | */ 21 | const { __ } = wp.i18n; 22 | const { compose } = wp.compose; 23 | const { withSelect, withDispatch } = wp.data; 24 | const { registerBlockType } = wp.blocks; 25 | 26 | registerBlockType('c9-blocks/image-carousel', { 27 | title: __('C9 Image Carousel', 'c9-blocks'), 28 | icon: Icon, 29 | category: 'c9-blocks', 30 | supports: { 31 | // fill in features 32 | }, 33 | keywords: [__('carousel', 'c9-blocks'), __('responsive', 'c9-blocks')], 34 | description: __( 35 | 'Display an animated carousel of images with captions with custom settings for navigation.', 36 | 'c9-blocks' 37 | ), 38 | example: { 39 | viewportWidth: '280', 40 | attributes: { 41 | autoSlide: false, 42 | url: [ 43 | 'https://work.covertnine.com/wp-content/uploads/2020/07/clark-young-fQxMGkYXqFU-unsplash-55.jpg', 44 | 'https://work.covertnine.com/wp-content/uploads/2020/07/jezael-melgoza-HYQvV8wWX18-unsplash-55.jpg', 45 | 'https://work.covertnine.com/wp-content/uploads/2020/07/set-of-tool-wrench-162553-55.jpg', 46 | ], 47 | id: [null, null, null], 48 | captionTitle: [ 49 | 'Slide Caption Top', 50 | 'Slide Caption Top', 51 | 'Slide Caption Top', 52 | ], 53 | captionContent: [ 54 | 'Slide Caption Bottom', 55 | 'Slide Caption Bottom', 56 | 'Slide Caption Bottom', 57 | ], 58 | isResponsive: true, 59 | slideMaxHeight: 640, 60 | slideEqualHeight: true, 61 | }, 62 | }, 63 | attributes, 64 | 65 | /* Add alignment to block wrapper. */ 66 | getEditWrapperProps({ align }) { 67 | if ( 68 | 'full' === align || 69 | 'wide' === align || 70 | 'narrow' === align || 71 | '' === align 72 | ) { 73 | return { 'data-align': align }; 74 | } 75 | }, 76 | 77 | // Render the block components 78 | edit: compose([ 79 | withSelect((select, ownProps) => { 80 | const { getBlock, isBlockSelected, hasSelectedInnerBlock } = 81 | select('core/block-editor'); 82 | 83 | const { clientId } = ownProps; 84 | 85 | return { 86 | block: getBlock(clientId), 87 | isSelectedBlockInRoot: 88 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 89 | instanceId: parseInt( 90 | cryptoRandomString({ length: 4, type: 'numeric' }) 91 | ), 92 | }; 93 | }), 94 | withDispatch((dispatch) => { 95 | const { updateBlockAttributes, removeBlock, toggleSelection } = 96 | dispatch('core/block-editor'); 97 | 98 | return { 99 | updateBlockAttributes, 100 | removeBlock, 101 | onResizeStart: () => toggleSelection(false), 102 | onResizeStop: () => toggleSelection(true), 103 | }; 104 | }), 105 | ])(Edit), 106 | 107 | // Save the attributes and markup 108 | save: (props) => { 109 | return ; 110 | }, 111 | 112 | deprecated: Deprecated, 113 | }); 114 | -------------------------------------------------------------------------------- /assets/section-template-icons/classic-header-statement-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/blocks/block-cta/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal dependencies 3 | */ 4 | import Edit from './edit'; 5 | import Save from './save'; 6 | import attributes from './attributes'; 7 | 8 | /** 9 | * Styles 10 | */ 11 | import './styles/style.scss'; 12 | 13 | import Icon from '../../../assets/icon-c9-cta-bar.svg'; 14 | 15 | /** 16 | * WordPress dependencies 17 | */ 18 | const { __ } = wp.i18n; 19 | const { compose } = wp.compose; 20 | const { withSelect } = wp.data; 21 | const { registerBlockType } = wp.blocks; 22 | 23 | registerBlockType('c9-blocks/cta', { 24 | title: __('C9 Call To Action', 'c9-blocks'), 25 | icon: Icon, 26 | category: 'c9-blocks', 27 | keywords: [__('call to action', 'c9-blocks'), __('cta', 'c9-blocks')], 28 | 29 | description: __( 30 | 'Get users to action with stylized text, colors, and buttons in one responsive block.', 31 | 'c9-blocks' 32 | ), 33 | 34 | example: { 35 | viewportWidth: '280', 36 | attributes: { 37 | type: 'c9-h', 38 | buttonText: 'Sign Up Now', 39 | ctaBackgroundColor: '#ededed', 40 | fontSize: 'larger', 41 | customFontSize: 20, 42 | align: 'full', 43 | ctaWidth: 'container-fluid', 44 | ctaText: [ 45 | { 46 | type: 'p', 47 | props: { 48 | children: [ 49 | 'Use the call to action to entice a user to sign up, navigate to another page, or call attention to what you want them to do next.', 50 | ], 51 | }, 52 | }, 53 | ], 54 | buttonUrl: '#', 55 | }, 56 | }, 57 | 58 | attributes: attributes, 59 | 60 | /* Add alignment to block wrapper. */ 61 | getEditWrapperProps({ align }) { 62 | if ( 63 | 'full' === align || 64 | 'wide' === align || 65 | 'narrow' === align || 66 | '' === align 67 | ) { 68 | return { 'data-align': align }; 69 | } 70 | }, 71 | 72 | edit: compose([ 73 | withSelect((select, ownProps) => { 74 | const { isBlockSelected, hasSelectedInnerBlock } = 75 | select('core/block-editor'); 76 | 77 | const { clientId } = ownProps; 78 | 79 | return { 80 | isSelectedBlockInRoot: 81 | isBlockSelected(clientId) || hasSelectedInnerBlock(clientId, true), 82 | }; 83 | }), 84 | ])(Edit), 85 | 86 | // Save the attributes and markup 87 | save: Save, 88 | 89 | deprecated: [ 90 | { 91 | attributes: { 92 | ...attributes, 93 | ctaPadding: { 94 | type: 'object', 95 | default: { 96 | linked: true, 97 | icon: 'admin-links', 98 | top: '3', 99 | bottom: '3', 100 | left: '3', 101 | right: '3', 102 | }, 103 | }, 104 | }, 105 | save: (props) => { 106 | return ; 107 | }, 108 | }, 109 | ], 110 | }); 111 | 112 | /* Add the container class to the cta block. */ 113 | const withClientIdClassName = wp.compose.createHigherOrderComponent( 114 | (BlockListBlock) => { 115 | return (props) => { 116 | const blockName = props.block.name; 117 | 118 | if ('c9-blocks/cta' === blockName) { 119 | return ( 120 | 121 | ); 122 | } else { 123 | return ; 124 | } 125 | }; 126 | }, 127 | 'withClientIdClassName' 128 | ); 129 | 130 | wp.hooks.addFilter( 131 | 'editor.BlockListBlock', 132 | 'c9-blocks/add-container-class', 133 | withClientIdClassName 134 | ); 135 | --------------------------------------------------------------------------------