Registering Content Element
Possibly main of Public API custom components Setka Editor do support is Custom Element. This type of components displays directly in editor's post and resulting markup.
Let's build our first Content Element component, which will be fetching random cat image from free public API and which can be added to the post via custom Tool component.
Creating custom Content Element markup
Unlike Tool or Option components Content Element is a little bit harder to implement, cause it needs two components for different Editor's modes: edit view and preview view.
We can start building our component like this:
// Replaces default jsx parser to our own createElement function
/** @jsx SetkaEditor.lib.React.createElement */
const RandomKittenElement = {};
// Extending from Setka Editor React component
RandomKittenElement.edit = class RandomKittenElementEdit extends SetkaEditor.lib.React.Component {
render() {
return (
<div>
RANDOM KITTEN EDIT MODE
</div>
);
}
}
RandomKittenElement.view = () => (
<div>
RANDOM KITTEN VIEW MODE
</div>
);
For now, we have basic markup for our components. Let's try to register them in Setka Editor!
Registering custom Content Element component
Registering Custom Element components is a little bit more complicated too, cause it contains not only simple markup of the component, but also configurations of it' behavior in the post:
SetkaEditor.registerElement({
// Unique type of Custom Component
type: 'RandomKitten',
// Translations for Header
typeName: {
en: (n = 1) => n > 1 ? 'Random Kitten' : 'Random Kittens',
ru: (n = 1) => SetkaEditor.utils.pluralize(n, 'Случайный кот', 'Случайные коты', 'Случайных котов'),
},
// Our components
component: {
edit: RandomKittenElement.edit,
view: RandomKittenElement.view,
},
// Options to be shown, when our component is selected
options: {
basic: [{
title: {
en: 'Settings',
ru: 'Настройки',
},
modules: ['BackgroundColor', 'BottomIndent'],
}],
advanced: [],
},
// Describing state for our components
mapStateToProps: (state, props) => ({
// ⚠️Always get element property for Custom Element
// It contains all properties, attributes & styles for our components
element: SetkaEditor.selectors.getElementById(state, props.id),
// Check if our component is selected
isSelected: SetkaEditor.selectors.isElementSelected(state, props.id),
}),
});
Cool! We've registered our component, but how can we add it to the post?
Creating Tool for adding Custom Element
Of course – we need to develop and register our custom Tool component!
/** @jsx SetkaEditor.lib.React.createElement */
// Describe your component's markup
const RandomKittenToolElement = ({selectedElement, isDisabled, onClick}) => (
<SetkaEditor.UI.Button
kind="icon"
disabled={isDisabled}
onClick={() => onClick(selectedElement)}
>
🐈
</SetkaEditor.UI.Button>
);
const mapStateToProps = state => ({
// Registering current selected element
selectedElement: SetkaEditor.selectors.getSelectedElements(state)[0],
// Checking if we can insertAfter current selected element
isDisabled: !SetkaEditor.selectors.selectedElementsCan(state, 'insertAfter'),
});
const mapDispatchToProps = dispatch => ({
onClick: (selectedElement) => {
dispatch(
SetkaEditor.actions.insertElement({
// When registering Custom Element – you're also registering
// a corresponding Class, describing it's behaviour. This Class
// can be found at SetkaEditor.Classes and be used to insert
// and manipulate components from Tools or Options.
element: new SetkaEditor.Classes.RandomKitten(),
after: selectedElement.id,
})
);
}
});
const RandomKittenTool =
SetkaEditor.lib.ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(RandomKittenToolElement);
SetkaEditor.registerTool({
component: RandomKittenTool,
});
At this point, we've registered our Custom Element and Custom Tool too. If everything is done right – you can see the following situation:
As you can see: our component renders differently in edit and preview modes. That's nice! But it is not any use, really – it does nothing, it is not selectable or customizable. Let's change that.
Adding logic to Content Element
The idea of our custom Content Element was to fetch random kitten photo and save its URL directly into component's attributes. Let's update our edit mode component to do that:
RandomKittenElement.edit = class RandomKittenElementEdit extends SetkaEditor.lib.React.Component {
componentDidMount() {
if (!this.props.element.attributes['data-source']) {
// Simply fetching data from public API...
fetch('https://aws.random.cat/meow')
.then(response => response.json())
.then(data => {
// ...and updating current element's attribute
// with API's response data
this.props.dispatch(SetkaEditor.actions.setElementAttribute({
id: this.props.id,
attrs: {
'data-source': data.file
},
}));
});
}
}
render() {
const {element} = this.props;
// Always add {...element.attributes} to root DOM element
// of your components. It contains all attributes, styles and properties.
return (
<div {...element.attributes}>
{/* Show placeholder if image is not loaded yet */}
{element.attributes['data-source'] ? (
<img src={element.attributes['data-source']} style={{maxWidth: '100%'}}/>
) : `Loading...`}
</div>
);
}
}
Let's also update our view component not to be shown, if there's no image URL saved in our class:
RandomKittenElement.view = ({element}) => element.attributes['data-source'] ? (
<div {...element.attributes}>
<img src={element.attributes['data-source']} style={{maxWidth: '100%'}}/>
</div>
) : null;
Cool! Let's check out how it works:
Awesome! We've registered our fully functional Content Element component. Almost there! However, we cannot select or update it's styling.
Making Content Element selectable
As you remember we've described our state selectors and options in registering component section. Let's make use of that with HOC component, Setka Editor does provide:
RandomKittenElement.edit = class RandomKittenElementEdit extends SetkaEditor.lib.React.Component {
componentDidMount() {
// Everything remains the same
if (!this.props.element.attributes['data-source']) {
fetch('https://aws.random.cat/meow')
.then(response => response.json())
.then(data => {
this.props.dispatch(SetkaEditor.actions.setElementAttribute({
id: this.props.id,
attrs: {
'data-source': data.file
},
}));
});
}
}
render() {
let {element, isSelected} = this.props;
// ❗️ Here we're describing behaviour of our component
// by updating element property with our HOCs.
element = SetkaEditor.lib.ramda.compose(
// Adds style attribute to element
SetkaEditor.HOC.withStyleAttribute,
// Selects element on click
SetkaEditor.HOC.withClickToSelect(this.props.dispatch),
)(element);
return (
<div {...element.attributes}>
{element.attributes['data-source'] ? (
<img src={element.attributes['data-source']} style={{maxWidth: '100%'}}/>
) : `Loading...`}
</div>
);
}
}
It seems like that's all we wanted to do! Now we can update our component's styles with Options we described in registering section. Wow! 💪
Full code of RandomKittenElement & RandomKittenTool
/** @jsx SetkaEditor.lib.React.createElement */
const RandomKittenElement = {};
// Extending from Setka Editor React component
RandomKittenElement.edit = class RandomKittenElementEdit extends SetkaEditor.lib.React.Component {
componentDidMount() {
// Everything remains the same
if (!this.props.element.attributes['data-source']) {
fetch('https://aws.random.cat/meow')
.then(response => response.json())
.then(data => {
this.props.dispatch(SetkaEditor.actions.setElementAttribute({
id: this.props.id,
attrs: {
'data-source': data.file
},
}));
});
}
}
render() {
let {element, isSelected} = this.props;
// ❗️ Here we're describing behaviour of our component
// by updating element property with our HOCs.
element = SetkaEditor.lib.ramda.compose(
// Adds style attribute to element
SetkaEditor.HOC.withStyleAttribute,
// Selects element on click
SetkaEditor.HOC.withClickToSelect(this.props.dispatch),
)(element);
return (
<div {...element.attributes}>
{element.attributes['data-source'] ? (
<img src={element.attributes['data-source']} style={{maxWidth: '100%'}}/>
) : `Loading...`}
</div>
);
}
}
RandomKittenElement.view = ({element}) => element.attributes['data-source'] ? (
<div {...element.attributes}>
<img src={element.attributes['data-source']} style={{maxWidth: '100%'}}/>
</div>
) : null;
SetkaEditor.registerElement({
// Unique type of Custom Component
type: 'RandomKitten',
// Translations for Header
typeName: {
en: (n = 1) => n > 1 ? 'Random Kitten' : 'Random Kittens',
ru: (n = 1) => SetkaEditor.utils.pluralize(n, 'Случайный кот', 'Случайные коты', 'Случайных котов'),
},
// Our components
component: {
edit: RandomKittenElement.edit,
view: RandomKittenElement.view,
},
// Options to be shown, when our component is selected
options: {
basic: [{
title: {
en: 'Settings',
ru: 'Настройки',
},
modules: ['BackgroundColor', 'BottomIndent'],
}],
advanced: [],
},
// Describing state for our components
mapStateToProps: (state, props) => ({
// ⚠️Always get element property for Custom Element
// It contains all properties, attributes & styles for our components
element: SetkaEditor.selectors.getElementById(state, props.id),
// Check if our component is selected
isSelected: SetkaEditor.selectors.isElementSelected(state, props.id),
}),
});
const RandomKittenToolElement = ({selectedElement, isDisabled, onClick}) => (
<SetkaEditor.UI.Button
kind="icon"
disabled={isDisabled}
onClick={() => onClick(selectedElement)}
>
🐈
</SetkaEditor.UI.Button>
);
const mapStateToProps = state => ({
// Registering current selected element
selectedElement: SetkaEditor.selectors.getSelectedElements(state)[0],
// Checking if we can insertAfter current selected element
isDisabled: !SetkaEditor.selectors.selectedElementsCan(state, 'insertAfter'),
});
const mapDispatchToProps = dispatch => ({
onClick: (selectedElement) => {
dispatch(
SetkaEditor.actions.insertElement({
// When registering Custom Element – you're also registering
// a corresponding Class, describing it's behaviour. This Class
// can be found at SetkaEditor.Classes and be used to insert
// and manipulate components from Tools or Options.
element: new SetkaEditor.Classes.RandomKitten(),
after: selectedElement.id,
})
);
}
});
const RandomKittenTool =
SetkaEditor.lib.ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(RandomKittenToolElement);
SetkaEditor.registerTool({
component: RandomKittenTool,
});