graft-react
Version:
react admin and helper components for graft-db
340 lines (339 loc) • 16.4 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
return { next: verb(0), "throw": verb(1), "return": verb(2) };
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var React = require("react");
;
var debounce = require("lodash.debounce");
var utils_1 = require("../utils");
var graft_db_1 = require("graft-db");
var WithClient_1 = require("./WithClient");
var modal_1 = require("../modal");
var draft_js_1 = require("draft-js");
var Immutable = require("immutable");
var Buttons_1 = require("react-md/lib/Buttons");
var MenuButton_1 = require("react-md/lib/Menus/MenuButton");
var FontIcons_1 = require("react-md/lib/FontIcons");
var Toolbars_1 = require("react-md/lib/Toolbars");
var SelectFields_1 = require("react-md/lib/SelectFields");
var ListItem_1 = require("react-md/lib/Lists/ListItem");
var Link = function (_a) {
var entityKey = _a.entityKey, children = _a.children;
var url = draft_js_1.Entity.get(entityKey).getData().url;
return React.createElement("a", { href: url }, children);
};
var NodeLink = function (_a) {
var entityKey = _a.entityKey, children = _a.children;
var id = draft_js_1.Entity.get(entityKey).getData().id;
return React.createElement("a", { href: "#node:" + id }, children);
};
function isType() {
var types = [];
for (var _i = 0; _i < arguments.length; _i++) {
types[_i] = arguments[_i];
}
return function (contentBlock, callback) {
contentBlock.findEntityRanges(function (character) {
var key = character.getEntity();
if (key === null) {
return false;
}
var entity = draft_js_1.Entity.get(key);
if (!entity) {
return false;
}
return !!utils_1.find(types, function (t) { return t == entity.getType(); });
}, callback);
};
}
var decorator = new draft_js_1.CompositeDecorator([
{
strategy: isType('NODE_LINK'),
component: NodeLink,
},
{
strategy: isType('LINK'),
component: Link,
},
]);
var BLOCK_TYPES = [
{ name: 'Normal', type: 'unstyled', className: 'editor-normal' },
{ name: 'Heading', type: 'header-one', className: 'editor-heading-one' },
{ name: 'Subheading', type: 'header-two', className: 'editor-heading-two' },
{ name: 'Small', type: 'small', className: 'editor-small' },
{ name: 'Quote', type: 'blockquote', className: 'editor-quote' },
{ name: 'List', type: 'unordered-list-item', className: 'editor-list-item' },
];
var MediaBlock = function (props) {
var entity = draft_js_1.Entity.get(props.block.getEntityAt(0));
var _a = entity.getData(), src = _a.src, id = _a.id;
var type = entity.getType();
var media;
if (type === 'IMAGE') {
media = React.createElement("img", { id: id, src: src, width: '100', height: '100' });
}
else if (type === 'VIDEO') {
media = React.createElement("span", null, "[VIDEO]");
}
else {
media = React.createElement("span", null,
"[",
type,
":ATOM]");
}
return media;
};
var blockRenderMap = draft_js_1.DefaultDraftBlockRenderMap.merge(Immutable.Map({
'small': {
element: 'div'
}
}));
function blockRendererFn(block) {
if (block.getType() === 'atomic') {
return {
component: MediaBlock,
editable: false,
};
}
return null;
}
function getClassNameForBlock(contentBlock) {
var type = contentBlock.getType();
var blockType = utils_1.find(BLOCK_TYPES, function (b) { return b.type == type; });
if (blockType) {
return blockType.className;
}
return '';
}
var RichTextProperty = (function (_super) {
__extends(RichTextProperty, _super);
function RichTextProperty() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var _this = _super.apply(this, args) || this;
_this.save = debounce(function () { return __awaiter(_this, void 0, void 0, function () {
var client, _a, object, property, value, editorState, v, args;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
client = this.context.client;
_a = this.props, object = _a.object, property = _a.property, value = _a.value;
editorState = this.state.editorState;
if (!editorState) {
return [2 /*return*/];
}
v = JSON.stringify(draft_js_1.convertToRaw(editorState.getCurrentContent()));
return [4 /*yield*/, client.storeValue({
id: value ? value.id : null,
owner: object.id,
property: property.id,
}, v)];
case 1:
args = _b.sent();
return [4 /*yield*/, client.setValue(args)];
case 2:
_b.sent();
return [2 /*return*/];
}
});
}); }, 750);
_this.setEditorState = function (editorState) {
var oldValue = JSON.stringify(draft_js_1.convertToRaw(_this.getEditorState().getCurrentContent()));
var newValue = JSON.stringify(draft_js_1.convertToRaw(editorState.getCurrentContent()));
_this.setState({ editorState: editorState }, function () {
if (newValue == oldValue) {
return;
}
_this.save();
_this.focus();
});
};
_this.resetEditorState = function () {
var editorState = draft_js_1.EditorState.createEmpty(decorator);
_this.setState({ editorState: editorState });
};
_this.onClickBold = function (e) {
e.preventDefault();
var editorState = _this.getEditorState();
_this.setEditorState(draft_js_1.RichUtils.toggleInlineStyle(editorState, 'BOLD'));
};
_this.onClickItalic = function (e) {
e.preventDefault();
var editorState = _this.getEditorState();
_this.setEditorState(draft_js_1.RichUtils.toggleInlineStyle(editorState, 'ITALIC'));
};
_this.changeBlockStyle = function (type) {
console.log(type);
var editorState = _this.getEditorState();
_this.setEditorState(draft_js_1.RichUtils.toggleBlockType(editorState, type));
};
_this.focus = function () {
if (!_this.ref) {
return;
}
_this.ref.focus(0);
};
_this.setLink = function () { return __awaiter(_this, void 0, void 0, function () {
var url, key, editorState;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, modal_1.prompt('Enter URL')];
case 1:
url = _a.sent();
if (!url) {
return [2 /*return*/];
}
key = draft_js_1.Entity.create('LINK', 'MUTABLE', { url: url });
editorState = this.getEditorState();
this.setEditorState(draft_js_1.RichUtils.toggleLink(editorState, editorState.getSelection(), key));
return [2 /*return*/];
}
});
}); };
_this.setImg = function () { return __awaiter(_this, void 0, void 0, function () {
var src, editorState, entityKey;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, modal_1.prompt('Enter image url:')];
case 1:
src = _a.sent();
editorState = this.getEditorState();
entityKey = draft_js_1.Entity.create('IMAGE', 'IMMUTABLE', { src: src });
this.setEditorState(draft_js_1.AtomicBlockUtils.insertAtomicBlock(editorState, entityKey, ' '));
return [2 /*return*/];
}
});
}); };
_this.handleKeyCommand = function (command) {
var editorState = _this.getEditorState();
var newState = draft_js_1.RichUtils.handleKeyCommand(editorState, command);
if (newState) {
_this.setEditorState(newState);
return true;
}
return false;
};
_this.keyBindings = function (e) {
return draft_js_1.getDefaultKeyBinding(e);
};
_this.onTab = function (e) {
e.preventDefault();
var editorState = _this.getEditorState();
_this.setEditorState(draft_js_1.RichUtils.onTab(e, editorState, 2));
};
_this.setRef = function (ref) {
_this.ref = ref;
};
_this.raise = function (raised) {
_this.setState({ raised: raised });
};
_this.state = {
editorState: null,
raised: false,
};
return _this;
}
RichTextProperty.prototype.getEditorState = function () {
var editorState = this.state.editorState;
if (editorState) {
return editorState;
}
var value = this.props.value;
if (value && value.jsonValue) {
try {
return draft_js_1.EditorState.createWithContent(draft_js_1.convertFromRaw(JSON.parse(value.jsonValue)), decorator);
}
catch (err) {
console.warn('bad rich text value', err);
}
}
return draft_js_1.EditorState.createEmpty(decorator);
};
RichTextProperty.prototype.renderEditor = function () {
var editorState = this.getEditorState();
var selection = editorState.getSelection();
var blockType = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType();
var blockItem = utils_1.find(BLOCK_TYPES, function (t) { return t.type == blockType; });
var property = this.props.property;
var raised = this.state.raised;
var nav = React.createElement(Buttons_1.default, { icon: true, disabled: true }, utils_1.propertyIconName(graft_db_1.PropertyType.RichText));
var actions = [
React.createElement(SelectFields_1.default, { id: 'block-types', itemLabel: 'name', itemValue: 'type', menuItems: BLOCK_TYPES.map(function (_a) {
var name = _a.name, type = _a.type;
return ({ name: name, type: type });
}), position: SelectFields_1.default.Positions.BELOW, onChange: this.changeBlockStyle, value: blockItem ? blockItem.type : 'unstyled' }),
React.createElement(MenuButton_1.default, { id: 'more-btn', buttonChildren: 'more_vert', icon: true, onClick: this.focus, onMenuToggle: this.raise },
React.createElement(ListItem_1.default, { onClick: this.onClickBold, primaryText: 'Bold', leftIcon: React.createElement(FontIcons_1.default, null, "format_bold") }),
React.createElement(ListItem_1.default, { onClick: this.onClickItalic, primaryText: 'Italic', leftIcon: React.createElement(FontIcons_1.default, null, "format_italic") }),
React.createElement(ListItem_1.default, { onClick: this.setImg, primaryText: 'Insert Image', leftIcon: React.createElement(FontIcons_1.default, null, "insert_photo") }),
React.createElement(ListItem_1.default, { onClick: this.setLink, primaryText: 'Insert Link', leftIcon: React.createElement(FontIcons_1.default, null, "link") }),
React.createElement(ListItem_1.default, { primaryText: "Clear Formatting", leftIcon: React.createElement(FontIcons_1.default, null, "format_clear") })),
React.createElement(Buttons_1.default, { icon: true, tooltipLabel: property.hint, tooltipPosition: 'left' }, "help"),
];
return (React.createElement("div", { className: 'md-cell md-cell--12' },
React.createElement("div", null,
React.createElement(Toolbars_1.default, { singleColor: true, themed: true, nav: nav, title: property.name, actions: actions, className: 'property-toolbar', style: { zIndex: raised ? 20 : null } }),
React.createElement("div", { className: 'property-field' },
React.createElement(draft_js_1.Editor, { ref: this.setRef, stripPastedStyles: true, spellCheck: true, editorState: editorState, onChange: this.setEditorState, handleKeyCommand: this.handleKeyCommand, blockStyleFn: getClassNameForBlock, blockRenderMap: blockRenderMap, blockRendererFn: blockRendererFn, keyBindingFn: this.keyBindings, onTab: this.onTab })))));
};
RichTextProperty.prototype.renderResetMessage = function () {
return React.createElement("div", null,
React.createElement("div", null, "The rich text content is corrupt."),
React.createElement("div", null, "An error was raised while attempting to draw the content:"),
React.createElement(Buttons_1.default, { raised: true, label: 'reset data', onClick: this.resetEditorState }),
React.createElement("div", { className: 'editor-small' }, "Resetting data will lose any content you have"));
};
RichTextProperty.prototype.render = function () {
try {
return this.renderEditor();
}
catch (e) {
return this.renderResetMessage();
}
};
return RichTextProperty;
}(React.Component));
RichTextProperty.contextTypes = WithClient_1.clientContextTypes;
exports.RichTextProperty = RichTextProperty;