Description:
Canvaz is a visual, modular, component-based content editor for React. Unlike any WYSIWYG-editors, Canvaz works with component tree instead of text. It provides extensibility and great visual control of output.
Installation:
# NPM $ npm install canvaz --save
Usage:
Import the component and other required resources.
import * as React from 'react';
import { render } from 'react-dom';
import styled from 'styled-components';
import YouTube from 'react-youtube';
import styles from './styles';
import withCanvaz, {
RehydrationProvider,
CanvazContainer,
TextEditable,
} from '../src';Define editor components. They are used both for final rendering and while editing content.
const Heading = withCanvaz({
label: 'Heading',
})(({ id, children }) =>
<TextEditable id={id}>
<h2>
{children}
</h2>
</TextEditable>
);
const Text = withCanvaz({
label: 'Text',
})(({ id, children }: any) =>
<TextEditable id={id}>
<p>
{children}
</p>
</TextEditable>
);
const Video = withCanvaz({
label: 'YouTube Video',
void: true,
})(({ videoId }) =>
<div className="video">
<YouTube videoId={videoId} />
</div>
);
const ListItem = withCanvaz({
label: 'List Item',
})(({ children, id }) =>
<TextEditable id={id}>
<li>
{children}
</li>
</TextEditable>
);
const List = withCanvaz({
label: 'List',
accept: { ListItem },
})(
({ children, ordered = true }) =>
ordered
? <ol>
{children}
</ol>
: <ul>
{children}
</ul>
);
const Column = withCanvaz({
label: 'Column',
accept: { Heading, Text, Video, List },
})(({ children }) =>
<div className="column">
{children}
</div>
);
const Layout = withCanvaz({
label: 'Layout',
accept: { Column },
})(({ children }) =>
<div className="layout">
{children}
</div>
);
const Article = withCanvaz({
label: 'Article',
accept: { Heading, Text, Video, Layout, List },
})(({ id, title, description, children }) =>
<article>
<header>
<TextEditable id={id} prop="title">
<h1>
{title}
</h1>
</TextEditable>
<TextEditable id={id} prop="description">
<p>
{description}
</p>
</TextEditable>
</header>
<hr />
<main>
{children}
</main>
</article>
);Create root editor component with some initial data tree (e.g. JSON from server) and provide set of components which required to deserialize editor tree.
class App extends React.Component<any> {
render() {
return (
<div className={this.props.className}>
<RehydrationProvider
components={{
Article,
Heading,
Text,
Video,
Layout,
Column,
List,
ListItem,
}}
>
<CanvazContainer
onChange={data => console.log(data)}
edit={true}
data={{
type: 'Article',
props: {
title: 'Beautiful content starts with beautiful editor',
description: [
'Everyone used to visual text editors.',
'They exists for a long time, but we need to move forward.',
'It should be easy and accessible for everyone to craft',
'content without special knowledge, with just right tool.',
].join(' '),
},
children: [
{ type: 'Heading', children: 'Switching to modular' },
{
type: 'Text',
children: [
'Even though text-manipulation is intuitive,',
'it is limited. User interfaces are built with',
'component-based architecture nowadays, so why not adopt',
'it for content we make?',
].join(' '),
},
{
type: 'Text',
children: [
'Go ahead and play around with this content.',
'You can edit any text with double click,',
'remove block by Delete or Backspace and drag-and-drop',
'components. Rollback your changes by Ctrl + Z or Cmd + Z.',
].join(' '),
},
{
type: 'Heading',
children: 'Below is stuff for testing',
},
{
type: 'List',
props: {
ordered: true,
},
children: [
{ type: 'ListItem', children: 'First item' },
{ type: 'ListItem', children: 'Second item' },
{ type: 'ListItem', children: 'Third item' },
],
},
{
type: 'Layout',
children: [
{
type: 'Column',
children: [{ type: 'Text', children: 'Left Side' }],
},
{
type: 'Column',
children: [{ type: 'Text', children: 'Right Side' }],
},
],
},
{ type: 'Video', props: { videoId: 'YE7VzlLtp-4' } },
],
}}
/>
</RehydrationProvider>
</div>
);
}
}
const StyledApp = styled(App)`${styles}`;
Render it.
render(<StyledApp />, document.querySelector('[data-approot]'));





