colonel-kurtz
Version:
1,386 lines (1,101 loc) • 37.2 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = _interopDefault(require('react'));
var reactTransitionGroup = require('react-transition-group');
var Ink = _interopDefault(require('react-ink'));
var FocusTrap = _interopDefault(require('react-focus-trap'));
var DOM = _interopDefault(require('react-dom'));
var groupBy = _interopDefault(require('group-by'));
var classNames = _interopDefault(require('classnames'));
var Microcosm = _interopDefault(require('microcosm'));
var DefaultBlockType = /*@__PURE__*/(function (superclass) {
function DefaultBlockType () {
superclass.apply(this, arguments);
}
if ( superclass ) DefaultBlockType.__proto__ = superclass;
DefaultBlockType.prototype = Object.create( superclass && superclass.prototype );
DefaultBlockType.prototype.constructor = DefaultBlockType;
DefaultBlockType.prototype.render = function render () {
return React.createElement( 'div', null, this.props.children )
};
return DefaultBlockType;
}(React.Component));
var KEY_DELIMETER = '.';
function isBlank(value) {
return value === '' || value === null || value === undefined
}
function isObject(target) {
return !(!target || typeof target !== 'object')
}
function copyOver(next, obj) {
for (var key in obj) {
next[key] = obj[key];
}
return next
}
function castPath(value) {
if (Array.isArray(value)) {
return value
} else if (isBlank(value)) {
return []
}
return typeof value === 'string' ? value.trim().split(KEY_DELIMETER) : [value]
}
function assign(subject) {
var values = [], len = arguments.length - 1;
while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
return values.reduce(copyOver, subject)
}
/**
* Shallow copy an object
*/
function clone(target) {
if (Array.isArray(target)) {
return target.slice(0)
} else if (isObject(target) === false) {
return {}
}
var copy = {};
for (var key in target) {
copy[key] = target[key];
}
return copy
}
/**
* Retrieve a value from an object. If no key is provided, just return
* the object.
*/
function get(object, keyPath, fallback) {
var path = castPath(keyPath);
var value = object;
for (var i = 0, len = path.length; i < len; i++) {
if (value == null) {
break
}
value = value[path[i]];
}
if (value === undefined || value === null) {
return arguments.length <= 2 ? value : fallback
}
return value
}
/**
* Non-destructively assign a value to a provided object at a given key. If the
* value is the same, don't do anything. Otherwise return a new object.
*/
function set(object, key, value) {
// Ensure we're working with a key path, like: ['a', 'b', 'c']
var path = castPath(key);
var len = path.length;
if (len <= 0) {
return value
}
if (get(object, path) === value) {
return object
}
var root = clone(object);
var node = root;
// For each key in the path...
for (var i = 0; i < len; i++) {
var key$1 = path[i];
var next = value;
// Are we at the end?
if (i < len - 1) {
// No: Check to see if the key is already assigned,
if (key$1 in node) {
// If yes, clone that value
next = clone(node[key$1]);
} else {
// Otherwise assign an object so that we can keep drilling down
next = {};
}
}
// Assign the value, then continue on to the next iteration of the loop
// using the next step down
node[key$1] = next;
node = node[key$1];
}
return root
}
var defaults = {
component: DefaultBlockType,
group: null,
maxChildren: Infinity,
root: true,
types: []
};
var BlockType = function BlockType(config) {
assign(this, defaults, config);
};
BlockType.prototype.valueOf = function valueOf () {
return this.id
};
/**
* Block Type Store
*
* A Block Type describes the editing experience for a Block. Whenever
* an action associated with block type the system, this module tells
* Colonel Kurtz how that action manipulates block type data.
*/
var BlockTypes = {
getInitialState: function getInitialState() {
return []
},
deserialize: function deserialize(blockTypes) {
if ( blockTypes === void 0 ) blockTypes = [];
return blockTypes.map(function (options) { return new BlockType(options); })
},
serialize: function serialize() {
return undefined
}
}
var Actions = {
create: function create(type, position, parent) {
return { type: type, position: position, parent: parent }
},
destroy: function destroy(id) {
return id
},
update: function update(id, content) {
// valueOf() allows blocks to be passed, it will return
// the id
return { id: id.valueOf(), content: content }
},
set: function set(id, path, value) {
return { id: id.valueOf(), path: path, value: value }
},
move: function move(block, distance) {
return { block: block, distance: distance }
}
}
var uidCounter = 0;
function uid() {
uidCounter += 1;
return ("c" + uidCounter)
}
var Block = function Block(params) {
this.id = uid();
this.content = params.content || {};
this.parent = params.parent;
this.type = params.type;
this.clientOnly = params.clientOnly || false;
};
Block.prototype.valueOf = function valueOf () {
return this.id
};
/**
* InsertAfter
* Given a list and an item, non-destructively return a new list
* including the item after a given position
*/
var inRange = function (value, min, max) { return Math.max(min, Math.min(max, value)); };
function insertAt(list, item, position) {
if ( position === void 0 ) position = list.length;
var corrected = inRange(position, 0, list.length);
var head = list.slice(0, corrected);
var tail = list.slice(corrected);
return head.concat(item, tail)
}
/**
* siblingsOf
* Get the siblings of a provided block
*/
function siblingsOf(list, block) {
if (block.parent) {
return list.filter(function (i) { return i.parent === block.parent; })
} else {
return list.filter(function (i) { return !i.parent; })
}
}
/**
* Given a list of blocks, determine if a provided block
* is the first child of its parent
*/
function siblingAt(list, block, delta) {
var siblings = siblingsOf(list, block);
var index = siblings.indexOf(block);
return index !== -1 ? siblings[index + delta] : null
}
function blocksToJson(items) {
// If items are null or undefined, assume an empty list
items = items || [];
var root = items.filter(function (i) { return !i.parent; });
function jsonify(block) {
var children = items.filter(function (i) { return i.parent === block; });
return {
content: block.content,
type: block.type,
blocks: children.map(jsonify)
}
}
return root.map(jsonify)
}
function jsonToBlocks(blocks, parent) {
// If blocks are null or undefined, assume an empty list
blocks = blocks || [];
return blocks.reduce(function(memo, params) {
var block = new Block(assign({}, params, { parent: parent }));
var children = jsonToBlocks(params.blocks, block);
return memo.concat(block, children)
}, [])
}
/**
* Block Store
*
* The Block Store is responsible for defining how blocks are stored
* within Colonel Kurtz. Whenever an action associated with block
* records is pushed into the system, this module tells Colonel Kurtz
* how that action manipulates block data.
*/
var Blocks = {
register: function register() {
var obj;
return ( obj = {}, obj[Actions.create] = this.create, obj[Actions.destroy] = this.destroy, obj[Actions.update] = this.update, obj[Actions.set] = this.set, obj[Actions.move] = this.move, obj)
},
getInitialState: function getInitialState() {
return []
},
find: function find(state, id) {
return state.filter(function (block) { return block.valueOf() === id; })[0]
},
getChildren: function getChildren(state, parent) {
return state.filter(function (i) { return i.parent === parent; })
},
getDepth: function getDepth(state, block, maxDepth) {
var currentBlock = block;
var depth = 0;
while (currentBlock && depth <= maxDepth + 1) {
depth += 1;
currentBlock = state.filter(function (i) { return i === currentBlock.parent; })[0];
}
return depth
},
filterChildren: function filterChildren(state) {
return state.filter(function (i) { return !i.parent; })
},
/**
* `blocksToJson` takes a list of blocks and transforms them into
* the nested structure shown in the front end
*/
serialize: function serialize(state) {
return blocksToJson(state)
},
/**
* jsonToBlocks takes this nested structure and flattens
* into a list for this store.
*/
deserialize: function deserialize(state) {
return jsonToBlocks(state)
},
create: function create(state, ref) {
var type = ref.type;
var parent = ref.parent;
var position = ref.position;
var record = new Block({ clientOnly: true, parent: parent, type: type });
// If the provided position is a Block, place the new block right
// after it.
if (position instanceof Block) {
position = state.indexOf(position) + 1;
}
return insertAt(state, record, position || 0)
},
update: function update(state, ref) {
var id = ref.id;
var content = ref.content;
var block = Blocks.find(state, id);
block.content = assign({}, block.content, content);
return state
},
set: function set$1(state, ref) {
var id = ref.id;
var path = ref.path;
var value = ref.value;
var block = Blocks.find(state, id);
block.content = set(block.content, path, value);
return state
},
destroy: function destroy(state, id) {
return state.filter(function(block) {
for (var b = block; b; b = b.parent) {
if (b.id == id) { return false }
}
return true
})
},
move: function move(state, ref) {
var block = ref.block;
var distance = ref.distance;
var without = state.filter(function (i) { return i !== block; });
var before = siblingAt(state, block, distance);
return insertAt(without, block, state.indexOf(before))
}
};
/**
* Bootstrap
* This plugin is responsible for injecting data into the system
*/
var parseElement = function(element) {
var data = [];
try {
data = JSON.parse(element.value);
} catch (x) {
// Do nothing
}
return data
};
var bootstrap = {
filter: function filter(blockTypes, acceptable) {
if (!acceptable) { return blockTypes }
return blockTypes.filter(function (type) { return acceptable.indexOf(type.id) > -1; })
},
register: function register(
app,
ref,
next
) {
var allow = ref.allow;
var maxChildren = ref.maxChildren; if ( maxChildren === void 0 ) maxChildren = Infinity;
var blocks = ref.blocks;
var blockTypes = ref.blockTypes;
var maxDepth = ref.maxDepth; if ( maxDepth === void 0 ) maxDepth = Infinity;
if (blocks instanceof HTMLElement) {
blocks = parseElement(blocks);
}
app.replace(
{
maxChildren: maxChildren,
maxDepth: maxDepth,
blocks: blocks,
blockTypes: this.filter(blockTypes, allow)
},
next
);
}
}
/**
* Animator
* In order to make block animations easy to edit in one place, this
* component maintains all of this logic.
*/
var defaultProps = {
component: 'div',
transitionName: 'col-editor-block',
transitionEnterTimeout: 280,
transitionLeaveTimeout: 280
};
var Animator = /*@__PURE__*/(function (superclass) {
function Animator () {
superclass.apply(this, arguments);
}
if ( superclass ) Animator.__proto__ = superclass;
Animator.prototype = Object.create( superclass && superclass.prototype );
Animator.prototype.constructor = Animator;
Animator.prototype.render = function render () {
return React.createElement(reactTransitionGroup.CSSTransitionGroup, this.props)
};
return Animator;
}(React.Component));
Animator.defaultProps = defaultProps;
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var defaultProps$1 = {
className: 'col-btn',
tagName: 'button',
type: 'button'
};
var Button = /*@__PURE__*/(function (superclass) {
function Button () {
superclass.apply(this, arguments);
}
if ( superclass ) Button.__proto__ = superclass;
Button.prototype = Object.create( superclass && superclass.prototype );
Button.prototype.constructor = Button;
Button.prototype.render = function render () {
var ref = this.props;
var tagName = ref.tagName;
var children = ref.children;
var rest = objectWithoutProperties( ref, ["tagName", "children"] );
var attrs = rest;
return React.createElement(tagName, attrs, [
React.createElement( Ink, { key: "__ink__" }) ].concat( children
))
};
return Button;
}(React.Component));
Button.defaultProps = defaultProps$1;
function objectWithoutProperties$1 (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var defaultProps$2 = {
className: 'col-menu-handle',
label: 'Open the menu for this block',
type: 'button'
};
var MenuHandle = /*@__PURE__*/(function (superclass) {
function MenuHandle () {
superclass.apply(this, arguments);
}
if ( superclass ) MenuHandle.__proto__ = superclass;
MenuHandle.prototype = Object.create( superclass && superclass.prototype );
MenuHandle.prototype.constructor = MenuHandle;
MenuHandle.prototype.render = function render () {
var ref = this.props;
var label = ref.label;
var rest = objectWithoutProperties$1( ref, ["label"] );
var safe = rest;
return (
React.createElement( Button, safe,
React.createElement( 'span', { className: "col-hidden" }, label),
React.createElement( 'svg', { width: "24", height: "24", viewBox: "0 0 24 24", 'aria-hidden': "true" },
React.createElement( 'path', { d: "M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" })
)
)
)
};
return MenuHandle;
}(React.Component));
MenuHandle.defaultProps = defaultProps$2;
function objectWithoutProperties$2 (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var defaultProps$3 = {
className: 'col-menu-item',
type: 'button',
onClick: function onClick() {},
isDisabled: function isDisabled() {}
};
var MenuItem = /*@__PURE__*/(function (superclass) {
function MenuItem () {
superclass.apply(this, arguments);
}
if ( superclass ) MenuItem.__proto__ = superclass;
MenuItem.prototype = Object.create( superclass && superclass.prototype );
MenuItem.prototype.constructor = MenuItem;
MenuItem.prototype.isDisabled = function isDisabled () {
var ref = this.props;
var app = ref.app;
var block = ref.block;
var isDisabled = ref.isDisabled;
return isDisabled(app, block)
};
MenuItem.prototype.render = function render () {
var ref = this.props;
var label = ref.label;
var app = ref.app;
var block = ref.block;
var onOpen = ref.onOpen;
var onExit = ref.onExit;
var active = ref.active;
var isDisabled = ref.isDisabled;
var items = ref.items;
var rest = objectWithoutProperties$2( ref, ["label", "app", "block", "onOpen", "onExit", "active", "isDisabled", "items"] );
var safe = rest;
return (
React.createElement( Button, Object.assign({},
safe, { onClick: this._onClick.bind(this), disabled: this.isDisabled() }),
this._formatLabel(label)
)
)
};
MenuItem.prototype._formatLabel = function _formatLabel (label) {
var ref = this.props;
var app = ref.app;
var block = ref.block;
if (typeof label === 'function') {
return label(app, block, this)
} else {
return label
}
};
MenuItem.prototype._onClick = function _onClick () {
var ref = this.props;
var app = ref.app;
var block = ref.block;
var onClick = ref.onClick;
onClick(app, block, this);
};
return MenuItem;
}(React.Component));
MenuItem.defaultProps = defaultProps$3;
var destroy = Actions.destroy;
var move = Actions.move;
var menuItems = [
{
id: 'moveBefore',
label: 'Move Up',
onClick: function onClick(app, block) {
app.push(move, [block, -1]);
},
isDisabled: function isDisabled(app, block) {
return siblingsOf(app.state.blocks, block)[0] === block
}
},
{
id: 'moveAfter',
label: 'Move Down',
onClick: function onClick(app, block) {
app.push(move, [block, 1]);
},
isDisabled: function isDisabled(app, block) {
return siblingsOf(app.state.blocks, block).pop() === block
}
},
{
id: 'destroy',
label: 'Remove',
onClick: function onClick(app, block) {
app.push(destroy, block.id);
}
}
]
var defaultProps$4 = {
items: []
};
var BlockMenu = /*@__PURE__*/(function (superclass) {
function BlockMenu () {
superclass.apply(this, arguments);
}
if ( superclass ) BlockMenu.__proto__ = superclass;
BlockMenu.prototype = Object.create( superclass && superclass.prototype );
BlockMenu.prototype.constructor = BlockMenu;
BlockMenu.prototype.getMenuItem = function getMenuItem (item) {
var this$1 = this;
var id = item.id;
return (
React.createElement( MenuItem, Object.assign({}, { key: id, ref: function (el) { return (this$1[id] = el); } }, item, this.props))
)
};
BlockMenu.prototype.getMenuItems = function getMenuItems () {
var ref = this.props;
var items = ref.items;
return items.concat(menuItems).map(this.getMenuItem, this)
};
BlockMenu.prototype.getMenu = function getMenu () {
if (!this.props.active) { return null }
return React.createElement(
FocusTrap,
{
active: true,
key: 'menu',
className: 'col-menu',
element: 'nav',
onExit: this.props.onExit,
role: 'navigation'
},
this.getMenuItems()
)
};
BlockMenu.prototype.render = function render () {
var this$1 = this;
return (
React.createElement( Animator, {
className: "col-menu-wrapper", transitionName: "col-menu", transitionEnterTimeout: 300, transitionLeaveTimeout: 200 },
React.createElement( MenuHandle, {
key: "handle", ref: function (el) { return (this$1.handle = el); }, onClick: this.props.onOpen }),
this.getMenu()
)
)
};
return BlockMenu;
}(React.Component));
BlockMenu.Item = MenuItem;
BlockMenu.defaultProps = defaultProps$4;
/**
* A fallback block type
*/
var FallbackBlockType = /*@__PURE__*/(function (BlockType$$1) {
function FallbackBlockType(ref) {
var block = ref.block;
BlockType$$1.call(this, {
type: 'unsupported',
component: /*@__PURE__*/(function (superclass) {
function FallbackBlock () {
superclass.apply(this, arguments);
}
if ( superclass ) FallbackBlock.__proto__ = superclass;
FallbackBlock.prototype = Object.create( superclass && superclass.prototype );
FallbackBlock.prototype.constructor = FallbackBlock;
FallbackBlock.prototype.render = function render () {
return (
React.createElement( 'section', { className: "col-unsupported" },
React.createElement( 'header', { className: "col-unsupported-header" },
React.createElement( 'p', { className: "col-unsupported-subtitle" }, "Error"),
React.createElement( 'p', { className: "col-unsupported-title" }, "Unrecognized block “", block.type, "”")
),
React.createElement( 'div', { className: "col-unsupported-content" },
React.createElement( 'p', null, "This typically happens when a block type is removed, or the identifier changes." ),
React.createElement( 'p', null,
React.createElement( 'b', { className: "col-strong" }, "Your content has not been lost!"), ' ', "Feel free to ignore this message, or build a new block with the information below:" )
),
React.createElement( 'pre', { className: "col-unsupported-data" },
JSON.stringify(block.content, null, 4)
)
)
)
};
return FallbackBlock;
}(React.Component))
});
}
if ( BlockType$$1 ) FallbackBlockType.__proto__ = BlockType$$1;
FallbackBlockType.prototype = Object.create( BlockType$$1 && BlockType$$1.prototype );
FallbackBlockType.prototype.constructor = FallbackBlockType;
return FallbackBlockType;
}(BlockType));
var defaultProps$5 = {
label: 'Add block',
className: 'col-btn-fab',
symbol: '+'
};
var ActionButton = /*@__PURE__*/(function (superclass) {
function ActionButton () {
superclass.apply(this, arguments);
}
if ( superclass ) ActionButton.__proto__ = superclass;
ActionButton.prototype = Object.create( superclass && superclass.prototype );
ActionButton.prototype.constructor = ActionButton;
ActionButton.prototype.focus = function focus () {
DOM.findDOMNode(this).focus();
};
ActionButton.prototype.render = function render () {
var ref = this.props;
var className = ref.className;
var disabled = ref.disabled;
var label = ref.label;
var onClick = ref.onClick;
var symbol = ref.symbol;
return (
React.createElement( Button, { className: className, onClick: onClick, disabled: disabled },
React.createElement( 'span', { className: "col-hidden" }, label),
React.createElement( 'span', { 'aria-hidden': "true" }, symbol)
)
)
};
return ActionButton;
}(React.Component));
ActionButton.defaultProps = defaultProps$5;
var defaultProps$6 = {
items: []
};
var BlockTypeGroup = /*@__PURE__*/(function (superclass) {
function BlockTypeGroup() {
superclass.apply(this, arguments);
this.state = {
open: false
};
}
if ( superclass ) BlockTypeGroup.__proto__ = superclass;
BlockTypeGroup.prototype = Object.create( superclass && superclass.prototype );
BlockTypeGroup.prototype.constructor = BlockTypeGroup;
BlockTypeGroup.prototype.open = function open () {
this.setState({ open: true });
};
BlockTypeGroup.prototype.close = function close () {
this.setState({ open: false });
};
BlockTypeGroup.prototype.getButton = function getButton (type) {
var id = type.id;
var label = type.label;
var ref = this.props;
var onAdd = ref.onAdd;
return (
React.createElement( Button, { key: id, className: "col-menu-item", onClick: function () { return onAdd(type); } },
label
)
)
};
BlockTypeGroup.prototype.getMenu = function getMenu () {
return this.state.open ? (
React.createElement( FocusTrap, {
key: "menu", className: "col-menu", element: "nav", active: true, onExit: this.close.bind(this), role: "navigation" },
this.props.items.map(this.getButton, this)
)
) : null
};
BlockTypeGroup.prototype.render = function render () {
return (
React.createElement( Animator, {
role: "button", transitionName: "col-menu", className: "col-switch-dropdown", transitionEnterTimeout: 300, transitionLeaveTimeout: 200, onKeyUp: this._onKeyUp.bind(this) },
React.createElement( Button, {
key: "label", className: "col-switch-btn col-menu-label", onClick: this.open.bind(this) },
this.props.label
),
this.getMenu()
)
)
};
BlockTypeGroup.prototype._onKeyUp = function _onKeyUp (event) {
// Do not allow escape presses to bubble up to parent switch
if (event.key === 'Escape' && this.state.open) {
event.stopPropagation();
}
};
return BlockTypeGroup;
}(React.Component));
BlockTypeGroup.defaultProps = defaultProps$6;
var SwitchNav = /*@__PURE__*/(function (superclass) {
function SwitchNav () {
superclass.apply(this, arguments);
}
if ( superclass ) SwitchNav.__proto__ = superclass;
SwitchNav.prototype = Object.create( superclass && superclass.prototype );
SwitchNav.prototype.constructor = SwitchNav;
SwitchNav.prototype.componentDidMount = function componentDidMount () {
DOM.findDOMNode(this).focus();
};
SwitchNav.prototype.getButton = function getButton (type) {
var id = type.id;
var label = type.label;
var ref = this.props;
var onAdd = ref.onAdd;
return {
name: label,
type: type,
component: (
React.createElement( Button, { key: id, className: "col-switch-btn", onClick: function () { return onAdd(type); } },
label
)
)
}
};
SwitchNav.prototype.getGroups = function getGroups (blocks) {
var groups = groupBy(blocks.filter(function (b) { return b.group; }), function (type) { return type.group; });
var items = [];
for (var name in groups) {
items.push({
name: name,
type: groups[name][0],
component: (
React.createElement( BlockTypeGroup, {
key: name, items: groups[name], label: name, onAdd: this.props.onAdd })
)
});
}
return items
};
SwitchNav.prototype.render = function render () {
var ref = this.props;
var blockTypes = ref.blockTypes;
var ungrouped = blockTypes.filter(function (b) { return !b.group; }).map(this.getButton, this);
var grouped = this.getGroups(blockTypes);
var sorted = grouped.concat(ungrouped).sort(function(a, b) {
return blockTypes.indexOf(a.type) - blockTypes.indexOf(b.type)
});
return (
React.createElement( 'nav', { className: "col-switch-nav", role: "navigation" },
sorted.map(function (s) { return s.component; })
)
)
};
return SwitchNav;
}(React.Component));
/**
* typesForBlock
* Extracted logic to get the types of children a block may have
*/
function typesForBlock(blockTypes, block) {
if (block) {
var types = blockTypes.filter(function (i) { return i.id === block.type; })[0].types;
return blockTypes.filter(function (i) { return types.indexOf(i.id) > -1; })
} else {
return blockTypes.filter(function (type) { return type.root; })
}
}
var Switch = /*@__PURE__*/(function (superclass) {
function Switch() {
superclass.apply(this, arguments);
this.state = {
open: false
};
}
if ( superclass ) Switch.__proto__ = superclass;
Switch.prototype = Object.create( superclass && superclass.prototype );
Switch.prototype.constructor = Switch;
Switch.prototype.open = function open () {
this.setState({ open: true });
};
Switch.prototype.close = function close () {
var this$1 = this;
this.setState({ open: false }, function () {
this$1.toggle.focus();
});
};
Switch.prototype.getToggle = function getToggle () {
var this$1 = this;
if (this.state.open) { return null }
return (
React.createElement( ActionButton, {
ref: function (el) { return (this$1.toggle = el); }, disabled: this.hasMaxChildren(), label: "Open the block menu and create a block", onClick: this._onToggle.bind(this) })
)
};
Switch.prototype.getNav = function getNav (blockTypes) {
var this$1 = this;
if (!this.state.open) {
return null
}
return (
React.createElement( SwitchNav, {
ref: function (el) { return (this$1.nav = el); }, blockTypes: blockTypes, onAdd: this._onAdd.bind(this), onExit: this.close.bind(this) })
)
};
Switch.prototype.hasMaxChildren = function hasMaxChildren () {
var ref = this.props;
var app = ref.app;
var parent = ref.parent;
if (!parent) {
return (
Blocks.filterChildren(app.state.blocks).length >= app.state.maxChildren
)
}
var children = Blocks.getChildren(app.state.blocks, parent);
var type = app.state.blockTypes.filter(function (t) { return t.id === parent.type; })[0];
return children.length >= type.maxChildren
};
Switch.prototype.depth = function depth () {
var ref = this.props;
var app = ref.app;
var parent = ref.parent;
return Blocks.getDepth(app.state.blocks, parent, app.state.maxDepth) + 1
};
Switch.prototype.hasHitMaxDepth = function hasHitMaxDepth () {
var ref = this.props;
var app = ref.app;
if (!app.state.maxDepth) { return false }
return this.depth() >= app.state.maxDepth
};
Switch.prototype.render = function render () {
var ref = this.props;
var app = ref.app;
var parent = ref.parent;
var types = typesForBlock(app.state.blockTypes, parent);
var className = classNames('col-switch', {
'col-switch-disabled': this.hasMaxChildren() || this.hasHitMaxDepth()
});
return types.length ? (
React.createElement( 'div', { className: className, onKeyUp: this._onKeyUp.bind(this) },
this.getToggle(),
this.getNav(types)
)
) : null
};
Switch.prototype._onAdd = function _onAdd (type) {
var ref = this.props;
var app = ref.app;
var position = ref.position;
var parent = ref.parent;
app.push(Actions.create, [type.id, position, parent]);
this.setState({ open: false });
};
Switch.prototype._onToggle = function _onToggle () {
var ref = this.props;
var app = ref.app;
var position = ref.position;
var parent = ref.parent;
var types = typesForBlock(app.state.blockTypes, parent);
// If only one type exists, instead of opening the nav, just
// create that element
if (types.length === 1) {
app.push(Actions.create, [types[0].id, position, parent]);
} else {
this.open();
}
};
Switch.prototype._onKeyUp = function _onKeyUp (e) {
if (e.key === 'Escape') {
this.close();
}
};
return Switch;
}(React.Component));
/**
* respondsTo
* Can an object respond to a method name?
*/
function responesTo(obj, key) {
return !!(obj && typeof obj[key] === 'function')
}
var Block$1 = /*@__PURE__*/(function (superclass) {
function Block() {
superclass.apply(this, arguments);
this.state = {
extraMenuItems: [],
menuOpen: false
};
}
if ( superclass ) Block.__proto__ = superclass;
Block.prototype = Object.create( superclass && superclass.prototype );
Block.prototype.constructor = Block;
Block.prototype.getBlockType = function getBlockType () {
var ref = this.props;
var app = ref.app;
var block = ref.block;
var blockType = app.state.blockTypes.filter(function (i) { return i.id === block.type; })[0];
return blockType ? blockType : new FallbackBlockType({ block: block })
};
Block.prototype.getMenuItems = function getMenuItems () {
return this.state.extraMenuItems
};
Block.prototype.setMenuItems = function setMenuItems (component) {
if (responesTo(component, 'getMenuItems')) {
this.setState({ extraMenuItems: component.getMenuItems() });
}
};
Block.prototype.openMenu = function openMenu () {
this.setState({ menuOpen: true });
};
Block.prototype.closeMenu = function closeMenu () {
this.setState({ menuOpen: false });
};
Block.prototype.componentDidMount = function componentDidMount () {
this.setMenuItems(this.block);
// Trigger an initial change to ensure default content
// is assigned immediately
this._onChange(this.getContent(this.props.block));
};
Block.prototype.getContent = function getContent (block) {
var ref = this.getBlockType();
var component = ref.component;
var defaults = component.defaultProps || {};
return assign({}, defaults.content, block.content)
};
Block.prototype.render = function render () {
var this$1 = this;
var ref = this.props;
var app = ref.app;
var block = ref.block;
var children = ref.children;
var ref$1 = this.getBlockType();
var Component = ref$1.component;
var ref$2 = this.state;
var menuOpen = ref$2.menuOpen;
var extraMenuItems = ref$2.extraMenuItems;
// Determine content by taking the default content and extend it with
// the current block content
var content = this.getContent(block);
return (
React.createElement( 'div', { className: "col-editor-block" },
React.createElement( 'div', { className: ("col-block col-block-" + (block.type)) },
React.createElement( Component, Object.assign({},
{ ref: function (el) { return (this$1.block = el); } }, block, { content: content, onChange: this._onChange.bind(this) }),
React.createElement( Switch, { app: app, parent: block }),
React.createElement( Animator, { className: "col-block-children" }, children)
),
React.createElement( BlockMenu, {
ref: function (el) { return (this$1.menu = el); }, app: app, block: block, items: extraMenuItems, active: menuOpen, onOpen: this.openMenu.bind(this), onExit: this.closeMenu.bind(this) })
),
React.createElement( Switch, { app: app, position: block, parent: block.parent })
)
)
};
Block.prototype._onChange = function _onChange (keypath, value) {
var ref = this.props;
var app = ref.app;
var block = ref.block;
if (typeof keypath === 'object') {
// onChange({ field: 'value' })
app.push(Actions.update, [block, keypath]);
} else {
// onChange('field', 'value')
// onChange('deep.field', 'value')
app.push(Actions.set, [block, keypath, value]);
}
};
return Block;
}(React.PureComponent));
var EditorBlock = /*@__PURE__*/(function (superclass) {
function EditorBlock () {
superclass.apply(this, arguments);
}
if ( superclass ) EditorBlock.__proto__ = superclass;
EditorBlock.prototype = Object.create( superclass && superclass.prototype );
EditorBlock.prototype.constructor = EditorBlock;
EditorBlock.prototype.getBlock = function getBlock (block) {
return React.createElement( EditorBlock, { key: block, app: this.props.app, block: block })
};
EditorBlock.prototype.render = function render () {
var ref = this.props;
var app = ref.app;
var block = ref.block;
return (
React.createElement( Block$1, { app: app, block: block },
Blocks.getChildren(app.state.blocks, block).map(this.getBlock, this)
)
)
};
return EditorBlock;
}(React.Component));
/**
* This is the root component that contains sections for
* toggling between viewing modes and viewing managed content
*/
var App = /*@__PURE__*/(function (superclass) {
function App () {
superclass.apply(this, arguments);
}
if ( superclass ) App.__proto__ = superclass;
App.prototype = Object.create( superclass && superclass.prototype );
App.prototype.constructor = App;
App.prototype.getBlock = function getBlock (block, i) {
return React.createElement( EditorBlock, { key: block, app: this.props.app, block: block })
};
App.prototype.render = function render () {
var ref = this.props;
var app = ref.app;
var parents = Blocks.filterChildren(app.state.blocks);
return (
React.createElement( 'div', { className: "colonel" },
React.createElement( Switch, { app: app }),
React.createElement( Animator, { className: "col-block-children" },
parents.map(this.getBlock, this)
)
)
)
};
return App;
}(React.Component));
/**
* Render
* Handles updating the browser UI
*/
var render = {
render: function render(app, el) {
DOM.render(React.createElement( App, { app: app }), el);
},
register: function register(app, ref) {
var this$1 = this;
var el = ref.el;
this.render(app, el);
app.listen(function (i) { return this$1.render(app, el); });
}
}
/**
* Colonel Kurts
* A custom block editor
*/
var ColonelKurtz = /*@__PURE__*/(function (Microcosm$$1) {
function ColonelKurtz(options) {
Microcosm$$1.call(this);
/**
* A block is an individual chunk of content. It can have children
*/
this.addStore('blocks', Blocks);
/**
* A block type defines the editing experience for a specific type
* content
*/
this.addStore('blockTypes', BlockTypes);
/**
* The bootstrap plugin takes seed data and prepares the
* application's state beyond initializing
*/
this.addPlugin(bootstrap, options);
/**
* The render plugin handles updating the browser ui
*/
this.addPlugin(render, options);
}
if ( Microcosm$$1 ) ColonelKurtz.__proto__ = Microcosm$$1;
ColonelKurtz.prototype = Object.create( Microcosm$$1 && Microcosm$$1.prototype );
ColonelKurtz.prototype.constructor = ColonelKurtz;
ColonelKurtz.prototype.toJSON = function toJSON () {
return this.serialize().blocks
};
return ColonelKurtz;
}(Microcosm));
module.exports = ColonelKurtz;
//# sourceMappingURL=colonel.js.map