UNPKG

react-simple-wysiwyg

Version:
455 lines (433 loc) â€Ē 21.3 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('../node_modules/react/index.js')) : typeof define === 'function' && define.amd ? define(['exports', '../node_modules/react/index.js'], factory) : (global = global || self, factory(global.ReactSimpleWysiwyg = {}, global.React)); }(this, function (exports, React) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; } function compare(a, b, keys) { return keys.every(function (key) { return a[key] === b[key]; }); } function deepMerge(target) { var _a, _b; var sources = []; for (var _i = 1; _i < arguments.length; _i++) { sources[_i - 1] = arguments[_i]; } if (!sources.length) { return target; } var source = sources.shift(); if (isObject(target) && isObject(source)) { for (var key in source) { if (!source.hasOwnProperty(key)) { continue; } if (isObject(source[key])) { if (!target[key]) { Object.assign(target, (_a = {}, _a[key] = {}, _a)); } deepMerge(target[key], source[key]); } else { Object.assign(target, (_b = {}, _b[key] = source[key], _b)); } } } return deepMerge.apply(void 0, [target].concat(sources)); } function findLastTextNode(node) { if (node.nodeType === Node.TEXT_NODE) { return node; } var children = node.childNodes; for (var i = children.length - 1; i >= 0; i--) { var textNode = findLastTextNode(children[i]); if (textNode !== null) { return textNode; } } return null; } function getSelectedNode() { if (document.selection) { return document.selection.createRange().parentElement(); } var selection = window.getSelection(); if (selection.rangeCount > 0) { return selection.getRangeAt(0).startContainer.parentNode; } } function isObject(item) { return (item && typeof item === 'object' && !Array.isArray(item)); } function normalizeHtml(str) { return str && str.replace(/&nbsp;|\u202F|\u00A0/g, ' '); } function replaceCaret(el) { // Place the caret at the end of the element var target = findLastTextNode(el); // do not move caret if element was not focused var isTargetFocused = document.activeElement === el; if (target !== null && target.nodeValue !== null && isTargetFocused) { var range = document.createRange(); var sel = window.getSelection(); range.setStart(target, target.nodeValue.length); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); if (el instanceof HTMLElement) { el.focus(); } } } /** * Based on https://github.com/lovasoa/react-contenteditable * A simple component for an html element with editable contents. */ var ContentEditable = /** @class */ (function (_super) { __extends(ContentEditable, _super); function ContentEditable(props) { var _this = _super.call(this, props) || this; _this.previousValue = props.value; _this.onChange = _this.onChange.bind(_this); _this.setElementRef = _this.setElementRef.bind(_this); return _this; } ContentEditable.prototype.shouldComponentUpdate = function (nextProps) { var props = this.props; if (!this.el) { return true; } if (normalizeHtml(nextProps.value) !== normalizeHtml(this.el.innerHTML)) { return true; } return !compare(props, nextProps, ['disabled', 'tagName', 'className']); }; ContentEditable.prototype.componentDidUpdate = function () { if (!this.el) { return; } if (this.props.value !== this.el.innerHTML) { this.previousValue = this.props.value; this.el.innerHTML = this.props.value; } replaceCaret(this.el); }; ContentEditable.prototype.setElementRef = function (el) { var contentEditableRef = this.props.contentEditableRef; this.el = el; contentEditableRef && contentEditableRef(el); }; ContentEditable.prototype.onChange = function (event) { if (!this.el) { return; } var value = this.el.innerHTML; var previous = this.previousValue; this.previousValue = value; if (this.props.onChange && value !== previous) { this.props.onChange(__assign({}, event, { target: { value: value } })); } }; ContentEditable.prototype.render = function () { var _a = this.props, contentEditableRef = _a.contentEditableRef, tagName = _a.tagName, value = _a.value, props = __rest(_a, ["contentEditableRef", "tagName", "value"]); return React.createElement(tagName || 'div', __assign({}, props, { contentEditable: !this.props.disabled, dangerouslySetInnerHTML: { __html: value }, onBlur: this.props.onBlur || this.onChange, onInput: this.onChange, onKeyDown: this.props.onKeyDown || this.onChange, onKeyUp: this.props.onKeyUp || this.onChange, ref: this.setElementRef })); }; return ContentEditable; }(React.Component)); var styles = { button: { normal: { backgroundColor: 'unset', border: 'none', color: '#222', height: 24, outline: 'none', padding: 0, verticalAlign: 'top', width: 24, }, hovered: { backgroundColor: '#eaeaea', }, active: { backgroundColor: '#e0e0e0', }, }, contentEditable: { flex: 1, outline: 'none', padding: 5, }, dropdown: { boxSizing: 'border-box', height: 20, marginTop: 2, outline: 'none', verticalAlign: 'top', }, editor: { border: '1px solid #ddd', borderRadius: 3, display: 'flex', flexDirection: 'column', minHeight: 100, }, separator: { backgroundColor: '#ddd', display: 'inline-block', height: 20, margin: 2, verticalAlign: 'top', width: 1, }, }; var EditorContext = React.createContext({ styles: styles, }); var Editor = /** @class */ (function (_super) { __extends(Editor, _super); function Editor(props) { var _this = _super.call(this, props) || this; _this.state = {}; _this.onClickOutside = _this.onClickOutside.bind(_this); _this.onTextSelect = _this.onTextSelect.bind(_this); _this.setContentEditableRef = _this.setContentEditableRef.bind(_this); return _this; } Editor.prototype.componentDidMount = function () { document.addEventListener('click', this.onClickOutside); }; Editor.prototype.componentWillUnmount = function () { document.removeEventListener('click', this.onClickOutside); }; Editor.prototype.setContentEditableRef = function (el) { this.setState({ contentEditable: el }); this.props.contentEditableRef && this.props.contentEditableRef(el); }; Editor.prototype.onClickOutside = function (event) { var contentEditable = this.state.contentEditable; if (event.target === contentEditable) { return; } if (contentEditable && contentEditable.contains(event.target)) { return; } this.setState({ selection: null }); }; Editor.prototype.onTextSelect = function (e) { this.props.onSelect && this.props.onSelect(e); this.setState({ selection: getSelectedNode() }); }; Editor.prototype.render = function () { var _a = this.props, children = _a.children, styles$1 = _a.styles, props = __rest(_a, ["children", "styles"]); var _b = this.state, contentEditable = _b.contentEditable, selection = _b.selection; var allStyles = deepMerge({}, styles, styles$1); var context = { el: contentEditable, selection: selection, styles: allStyles, }; return (React.createElement("div", { style: context.styles.editor }, React.createElement(EditorContext.Provider, { value: context }, children, React.createElement(ContentEditable, __assign({}, props, { contentEditableRef: this.setContentEditableRef, onSelect: this.onTextSelect, style: allStyles.contentEditable }))))); }; return Editor; }(React.PureComponent)); function withEditorContext(Component) { WithEditorContext.displayName = "withEditorContext(" + (Component.displayName || Component.name) + ")"; return WithEditorContext; function WithEditorContext(props) { return (React.createElement(EditorContext.Consumer, null, function (context) { return (React.createElement(Component, __assign({}, props, { el: context.el, selection: context.selection, styles: context.styles }))); })); } } function OrderedListIcon() { return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", style: { verticalAlign: 'text-top' } }, React.createElement("path", { fill: "currentColor", d: "M 6.99938,12.998L 6.99938,10.998L 20.9994,10.998L 20.9994,12.998L 6.99938,12.998 Z M 6.99938,18.9981L 6.99938,16.9981L 20.9994,16.9981L 20.9994,18.9981L 6.99938,18.9981 Z M 6.99938,6.99809L 6.99938,4.99809L 20.9994,4.99809L 20.9994,6.99809L 6.99938,6.99809 Z M 2.99938,7.99809L 2.99938,4.99809L 1.99938,4.99809L 1.99938,3.99809L 3.99938,3.99809L 3.99938,7.99809L 2.99938,7.99809 Z M 1.99938,16.9981L 1.99938,15.9981L 4.99938,15.9981L 4.99938,19.9981L 1.99938,19.9981L 1.99938,18.9981L 3.99938,18.9981L 3.99938,18.4981L 2.99938,18.4981L 2.99938,17.4981L 3.99938,17.4981L 3.99938,16.9981L 1.99938,16.9981 Z M 4.25,10C 4.66421,10 5,10.3358 5,10.75C 5,10.9524 4.91983,11.1361 4.7895,11.271L 3.11983,13L 5,13L 5,14L 2,14L 2,13.0782L 4,11L 2,11L 2,10L 4.25,10 Z " }))); } function UnorderedListIcon() { return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", style: { verticalAlign: 'text-top' } }, React.createElement("path", { fill: "currentColor", d: "M 7,5L 21,5L 21,7L 7,7L 7,5 Z M 7,13L 7,11L 21,11L 21,13L 7,13 Z M 4,4.50001C 4.83,4.50001 5.5,5.16993 5.5,6.00001C 5.5,6.83008 4.83,7.50001 4,7.50001C 3.17,7.50001 2.5,6.83008 2.5,6.00001C 2.5,5.16993 3.17,4.50001 4,4.50001 Z M 4,10.5C 4.83,10.5 5.5,11.17 5.5,12C 5.5,12.83 4.83,13.5 4,13.5C 3.17,13.5 2.5,12.83 2.5,12C 2.5,11.17 3.17,10.5 4,10.5 Z M 7,19L 7,17L 21,17L 21,19L 7,19 Z M 4,16.5C 4.83,16.5 5.5,17.17 5.5,18C 5.5,18.83 4.83,19.5 4,19.5C 3.17,19.5 2.5,18.83 2.5,18C 2.5,17.17 3.17,16.5 4,16.5 Z " }))); } // tslint:disable:max-line-length var BtnBold = createButton('Bold', '𝐁', 'bold'); var BtnClearFormatting = createButton('Clear formatting', 'TĖēₓ', 'removeFormat'); var BtnItalic = createButton('Italic', '𝑰', 'italic'); var BtnLink = createButton('Link', '🔗', function (selected) { if (selected && selected.nodeName === 'A') { document.execCommand('unlink'); } else { document.execCommand('createLink', false, prompt('URL')); } }); var BtnNumberedList = createButton('Numbered list', React.createElement(OrderedListIcon, null), 'insertOrderedList'); var BtnRedo = createButton('Redo', '↷', 'redo'); var BtnUnderline = createButton('Underline', React.createElement("span", { style: { textDecoration: 'underline' } }, "\uD835\uDC14"), 'underline'); var BtnUndo = createButton('Undo', 'â†ķ', 'undo'); var BtnBulletList = createButton('Bullet list', React.createElement(UnorderedListIcon, null), 'insertUnorderedList'); function Button(props) { var _a = React.useState(false), hovered = _a[0], setHovered = _a[1]; var active = props.active, styles = props.styles, el = props.el, selection = props.selection, inputProps = __rest(props, ["active", "styles", "el", "selection"]); var style = __assign({}, styles.button.normal, props.style, (hovered ? styles.button.hovered : {}), (hovered ? props.hoverStyle : {}), (active ? styles.button.active : {})); var onHover = function (e) { setHovered(true); props.onMouseEnter && props.onMouseEnter(e); }; var onUnHover = function (e) { setHovered(false); props.onMouseLeave && props.onMouseLeave(e); }; return (React.createElement("button", __assign({}, inputProps, { style: style, onMouseEnter: onHover, onMouseLeave: onUnHover }))); } function createButton(title, content, command) { ButtonFactory.displayName = title.replace(/\s/g, ''); return withEditorContext(ButtonFactory); function ButtonFactory(props) { var selection = props.selection, buttonProps = __rest(props, ["selection"]); var active = false; if (typeof command === 'string') { active = !!selection && document.queryCommandState(command); } return (React.createElement(Button, __assign({ title: title }, buttonProps, { onMouseDown: action, active: active }), content)); function action() { if (typeof command === 'function') { command(selection); } else { document.execCommand(command); } } } } var BtnStyles = createDropdown('Styles', [ ['Normal', 'formatBlock', 'DIV'], ['𝗛ð—ēð—Ūð—ąð—ēð—ŋ 𝟭', 'formatBlock', 'H1'], ['Header 2', 'formatBlock', 'H2'], ['ð™ē𝚘𝚍𝚎', 'formatBlock', 'PRE'], ]); function createDropdown(title, items) { DropdownFactory.displayName = title; return withEditorContext(DropdownFactory); function DropdownFactory(props) { var selection = props.selection, ddProps = __rest(props, ["selection"]); return (React.createElement(Dropdown, __assign({}, ddProps, { onChange: onChange, title: title, items: items }))); function onChange(e) { var selected = parseInt(e.target.value, 10); var _a = items[selected], command = _a[1], commandArgument = _a[2]; e.preventDefault(); e.target.selectedIndex = 0; if (typeof command === 'function') { command(selection); } else { document.execCommand(command, false, commandArgument); } } } } function Dropdown(props) { var el = props.el, items = props.items, selected = props.selected, selection = props.selection, styles = props.styles, inputProps = __rest(props, ["el", "items", "selected", "selection", "styles"]); var style = __assign({}, styles.dropdown, props.style); return (React.createElement("select", __assign({}, inputProps, { value: selected, style: style }), React.createElement("option", { hidden: true }, props.title), items.map(function (item, index) { return (React.createElement("option", { key: index, value: index }, item[0])); }))); } function Separator(context) { return (React.createElement("span", { style: context.styles.separator })); } var WrappedSeparator = withEditorContext(Separator); function Toolbar(props) { var rootStyle = __assign({}, styles$1.root, props.style); return (React.createElement("div", __assign({}, props, { style: rootStyle }))); } var styles$1 = { root: { backgroundColor: '#f5f5f5', borderBottom: '1px solid #ddd', }, }; function DefaultEditor(props) { return (React.createElement(Editor, __assign({}, props), React.createElement(Toolbar, null, React.createElement(BtnUndo, null), React.createElement(BtnRedo, null), React.createElement(WrappedSeparator, null), React.createElement(BtnBold, null), React.createElement(BtnItalic, null), React.createElement(BtnUnderline, null), React.createElement(WrappedSeparator, null), React.createElement(BtnNumberedList, null), React.createElement(BtnBulletList, null), React.createElement(WrappedSeparator, null), React.createElement(BtnLink, null), React.createElement(BtnClearFormatting, null), React.createElement(WrappedSeparator, null), React.createElement(BtnStyles, null)))); } exports.BtnBold = BtnBold; exports.BtnBulletList = BtnBulletList; exports.BtnClearFormatting = BtnClearFormatting; exports.BtnItalic = BtnItalic; exports.BtnLink = BtnLink; exports.BtnNumberedList = BtnNumberedList; exports.BtnRedo = BtnRedo; exports.BtnStyles = BtnStyles; exports.BtnUnderline = BtnUnderline; exports.BtnUndo = BtnUndo; exports.Button = Button; exports.ContentEditable = ContentEditable; exports.DefaultEditor = DefaultEditor; exports.Dropdown = Dropdown; exports.Editor = Editor; exports.Separator = WrappedSeparator; exports.Toolbar = Toolbar; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=index.umd.js.map