Skip to content

Commit

Permalink
Add source for Gutenberg extensions
Browse files Browse the repository at this point in the history
The source for block editor (Gutenberg) extensions was stored in the
Calypso repository:

https://github.com/Automattic/wp-calypso

Move the source into this repository.
  • Loading branch information
roccotripaldi authored and sirreal committed Mar 25, 2019
1 parent 9c47bef commit a59fb1f
Show file tree
Hide file tree
Showing 223 changed files with 15,209 additions and 15 deletions.
1 change: 1 addition & 0 deletions class.jetpack-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ public static function enqueue_block_editor_assets() {
'wp-edit-post',
'wp-editor',
'wp-element',
'wp-escape-html',
'wp-hooks',
'wp-i18n',
'wp-keycodes',
Expand Down
28 changes: 13 additions & 15 deletions extensions/README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
# Jetpack Block Editor Extensions

This directory lists extensions for the Block Editor, also known as Gutenberg, [that was introduced in WordPress 5.0](https://wordpress.org/news/2018/12/bebo/).
This directory lists extensions for the Block Editor, also known as Gutenberg,
[that was introduced in WordPress 5.0](https://wordpress.org/news/2018/12/bebo/).

## Extension Type

We define different types of block editor extensions:

- Blocks are available in the editor itself, and live in the `blocks` directory.
- Plugins are available in the Jetpack sidebar that appears on the right side of the block editor. Those live in the `plugins` directory.

When adding a new extension, add a new directory for your extension the matching directory.
- Blocks are available in the editor itself.
- Plugins are available in the Jetpack sidebar that appears on the right side of the block editor.

## Extension Structure

Your extension should follow this structure:
Extensions loosely follow this structure:

```
.
└── blockname/
└── blockname.php ← PHP file where the block and its assets are registered.
└── block-or-plugin-name/
├── block-or-plugin-name.php ← PHP file where the block and its assets are registered.
├── editor.js ← script loaded only in the editor
├── editor.scss ← styles loaded only in the editor
├── view.js ← script loaded in the editor and theme
└── view.scss ← styles loaded in the editor and theme
```

If your block depends on another block, place them all in extensions folder:

```
.
├── blockname/
├── block-name/
└── sub-blockname/
```

**Note that this directory is still being populated. For now, you can find the blocks [here](https://github.com/Automattic/wp-calypso/tree/master/client/gutenberg/extensions).

## Develop new blocks

You can follow [the instructions here](../docs/guides/gutenberg-blocks.md) to add your own block to Jetpack.

## Block naming conventions
Coming when [#11640](https://github.com/Automattic/jetpack/pull/11640) lands.

Blocks should use the `jetpack/` prefix, e.g. `jetpack/markdown`.
200 changes: 200 additions & 0 deletions extensions/blocks/business-hours/components/day-edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* External dependencies
*/
import { isEmpty } from 'lodash';
import { Component, Fragment } from '@wordpress/element';
import { IconButton, TextControl, ToggleControl } from '@wordpress/components';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import { __ } from '../../../utils/i18n';

const defaultOpen = '09:00';
const defaultClose = '17:00';

class DayEdit extends Component {
renderInterval = ( interval, intervalIndex ) => {
const { day } = this.props;
const { opening, closing } = interval;
return (
<Fragment key={ intervalIndex }>
<div className="business-hours__row">
<div className={ classNames( day.name, 'business-hours__day' ) }>
{ intervalIndex === 0 && this.renderDayToggle() }
</div>
<div className={ classNames( day.name, 'business-hours__hours' ) }>
<TextControl
type="time"
label={ __( 'Opening' ) }
value={ opening }
className="business-hours__open"
placeholder={ defaultOpen }
onChange={ value => {
this.setHour( value, 'opening', intervalIndex );
} }
/>
<TextControl
type="time"
label={ __( 'Closing' ) }
value={ closing }
className="business-hours__close"
placeholder={ defaultClose }
onChange={ value => {
this.setHour( value, 'closing', intervalIndex );
} }
/>
</div>
<div className="business-hours__remove">
{ day.hours.length > 1 && (
<IconButton
isSmall
isLink
icon="trash"
onClick={ () => {
this.removeInterval( intervalIndex );
} }
/>
) }
</div>
</div>
{ intervalIndex === day.hours.length - 1 && (
<div className="business-hours__row business-hours-row__add">
<div className={ classNames( day.name, 'business-hours__day' ) }>&nbsp;</div>
<div className={ classNames( day.name, 'business-hours__hours' ) }>
<IconButton isLink label={ __( 'Add Hours' ) } onClick={ this.addInterval }>
{ __( 'Add Hours' ) }
</IconButton>
</div>
<div className="business-hours__remove">&nbsp;</div>
</div>
) }
</Fragment>
);
};

setHour = ( hourValue, hourType, hourIndex ) => {
const { day, attributes, setAttributes } = this.props;
const { days } = attributes;
setAttributes( {
days: days.map( value => {
if ( value.name === day.name ) {
return {
...value,
hours: value.hours.map( ( hour, index ) => {
if ( index === hourIndex ) {
return {
...hour,
[ hourType ]: hourValue,
};
}
return hour;
} ),
};
}
return value;
} ),
} );
};

toggleClosed = nextValue => {
const { day, attributes, setAttributes } = this.props;
const { days } = attributes;

setAttributes( {
days: days.map( value => {
if ( value.name === day.name ) {
const hours = nextValue
? [
{
opening: defaultOpen,
closing: defaultClose,
},
]
: [];
return {
...value,
hours,
};
}
return value;
} ),
} );
};

addInterval = () => {
const { day, attributes, setAttributes } = this.props;
const { days } = attributes;
day.hours.push( { opening: '', closing: '' } );
setAttributes( {
days: days.map( value => {
if ( value.name === day.name ) {
return {
...value,
hours: day.hours,
};
}
return value;
} ),
} );
};

removeInterval = hourIndex => {
const { day, attributes, setAttributes } = this.props;
const { days } = attributes;

setAttributes( {
days: days.map( value => {
if ( day.name === value.name ) {
return {
...value,
hours: value.hours.filter( ( hour, index ) => {
return hourIndex !== index;
} ),
};
}
return value;
} ),
} );
};

isClosed() {
const { day } = this.props;
return isEmpty( day.hours );
}

renderDayToggle() {
const { day, localization } = this.props;
return (
<Fragment>
<span className="business-hours__day-name">{ localization.days[ day.name ] }</span>
<ToggleControl
label={ this.isClosed() ? __( 'Closed' ) : __( 'Open' ) }
checked={ ! this.isClosed() }
onChange={ this.toggleClosed }
/>
</Fragment>
);
}

renderClosed() {
const { day } = this.props;
return (
<div className="business-hours__row business-hours-row__closed">
<div className={ classNames( day.name, 'business-hours__day' ) }>
{ this.renderDayToggle() }
</div>
<div className={ classNames( day.name, 'closed', 'business-hours__hours' ) }>&nbsp;</div>
<div className="business-hours__remove">&nbsp;</div>
</div>
);
}

render() {
const { day } = this.props;
return this.isClosed() ? this.renderClosed() : day.hours.map( this.renderInterval );
}
}

export default DayEdit;
58 changes: 58 additions & 0 deletions extensions/blocks/business-hours/components/day-preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* External dependencies
*/
import { Component, Fragment } from '@wordpress/element';
import { date } from '@wordpress/date';
import { isEmpty } from 'lodash';
import { sprintf } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { _x } from '../../../utils/i18n';

class DayPreview extends Component {
formatTime( time ) {
const { timeFormat } = this.props;
const [ hours, minutes ] = time.split( ':' );
const _date = new Date();
if ( ! hours || ! minutes ) {
return false;
}
_date.setHours( hours );
_date.setMinutes( minutes );
return date( timeFormat, _date );
}

renderInterval = ( interval, key ) => {
return (
<dd key={ key }>
{ sprintf(
_x( 'From %s to %s', 'from business opening hour to closing hour' ),
this.formatTime( interval.opening ),
this.formatTime( interval.closing )
) }
</dd>
);
};

render() {
const { day, localization } = this.props;
const hours = day.hours.filter(
// remove any malformed or empty intervals
interval => this.formatTime( interval.opening ) && this.formatTime( interval.closing )
);
return (
<Fragment>
<dt className={ day.name }>{ localization.days[ day.name ] }</dt>
{ isEmpty( hours ) ? (
<dd>{ _x( 'Closed', 'business is closed on a full day' ) }</dd>
) : (
hours.map( this.renderInterval )
) }
</Fragment>
);
}
}

export default DayPreview;
Loading

0 comments on commit a59fb1f

Please sign in to comment.