UNPKG

react-kityminder

Version:

Mind map for react, based on kityminder.

456 lines (394 loc) 11.9 kB
import 'kity'; import 'kityminder-core'; import { useEffect, useState, createElement, useRef, useMemo, useCallback, forwardRef } from 'react'; function useValue(minder, value) { useEffect(() => { if (minder) { // const animationOptionName = 'layoutAnimationDuration' // const animationDuration = minder.getOption(animationOptionName) // if (animationDuration) { // minder.setOption(animationOptionName, 0) // } minder.importJson(value); // if (animationDuration) { // minder.setOption(animationOptionName, animationDuration) // } } }, [minder, value]); } const eventHandlerPrefixRE = /^on(\w+)$/; const getEventName = propName => { const name = eventHandlerPrefixRE.exec(propName); return name && name[1].toLowerCase(); }; const forEachHandler = (minder, props, cb) => { if (minder) { Object.keys(props).forEach(propName => { const eventName = getEventName(propName); if (eventName && handlerPropKeys.indexOf(eventName) >= 0) { cb(eventName, propName); } }); } }; const eventNames = ('click,dblclick,mousedown,mousemove,mouseup,mousewheel,keydown,keyup,keypress,touchstart,touchend,touchmove,' + 'execcommand,' + 'selectionchange,contentchange,interactchange,' + 'editnoterequest,shownoterequest,hidenoterequest,' + 'nodechange,editnode').split(','); let handlerPropKeys = []; eventNames.map(eventName => { handlerPropKeys = handlerPropKeys.concat([eventName, `before${eventName}`, `pre${eventName}`, `after${eventName}`]); }); // 数据 function useEvents(minder, props) { // 事件监听 useEffect(() => { const handlers = {}; forEachHandler(minder, props, (eventName, propName) => { handlers[eventName] = props[propName]; minder.on(eventName, props[propName]); }); return () => { forEachHandler(minder, props, eventName => { minder.off(eventName, handlers[eventName]); }); }; }, [minder, ...handlerPropKeys.map(handlerProp => props[handlerProp])]); } function useChangeHandler(minder, onChange) { useEffect(() => { const changeHandler = e => onChange(e.minder.exportJson()); if (minder && onChange) { minder.on('contentchange', changeHandler); } return () => { if (minder && onChange) { minder.off('contentchange', changeHandler); } }; }, [minder, onChange]); } function getKeyCode(evt) { return evt.keyCode || evt.witch; } function Editor(props) { const [value, setValue] = useState(props.initialValue); const onKeydown = evt => { evt.stopPropagation(); const { KeyMap } = window.kityminder; const keyCode = getKeyCode(evt); if (keyCode === KeyMap.enter) { props.onSubmit(); } if (keyCode === KeyMap.esc) { props.onCancel(); } }; const onChange = ({ target: { value } }) => { setValue(value); props.onChange(value); }; return /*#__PURE__*/createElement("input", { value: value, onChange: onChange, onKeyDown: onKeydown, onBlur: props.onSubmit }); } function isInputValue(e) { const keyCode = getKeyCode(e); // a-zA-Z if (keyCode >= 65 && keyCode <= 90) return true; // 0-9 以及其上面的符号 if (keyCode >= 48 && keyCode <= 57) return true; // 小键盘区域 (除回车外) if (keyCode !== 108 && keyCode >= 96 && keyCode <= 111) return true; return false; } function isIntendToInput(e) { const keyCode = getKeyCode(e); if (e.ctrlKey || e.metaKey || e.altKey) return false; if (isInputValue(e)) return true; // 输入法 if (keyCode === 229 || keyCode === 0) return true; return false; } function EditorWrapper(minder, props) { const valueRef = useRef(); const editingNodeRef = useRef(); const editorRef = useRef(); const [initialValue, setInitialValue] = useState(); const [editingNode, setEditingNode] = useState(); const { Editor, onEdit, onEditEnd } = props; const setEditorValue = v => { valueRef.current = v; }; const setEditorEditingNode = v => { editingNodeRef.current = v; setEditingNode(v); }; const exitEdit = () => { setEditorEditingNode(); minder.focus(); }; const onSubmit = (...args) => { const { node } = editingNodeRef.current || {}; if (node && (!onEditEnd || onEditEnd && onEditEnd(...args) !== false)) { node.setText(valueRef.current); minder.select(node, true); minder.fire('nodechange', { node }); minder.fire('contentchange'); minder.getRoot().renderTree(); minder.layout(300); } exitEdit(); }; useEffect(() => { const edit = e => { if (onEdit && onEdit(e) !== false || !onEdit) { const node = minder.getSelectedNode(); if (node) { const box = node.getRenderer('OutlineRenderer').getRenderShape().getRenderBox('view'); const { text = '' } = node.data; const editingNode = { node, box }; if (box.x > 0 || box.y > 0) { let value = text; if (props.appendKey) { value += isInputValue(e.originEvent) ? e.originEvent.key : ''; } setEditorEditingNode(editingNode); setInitialValue(value); setEditorValue(value); } } } }; const editNodeName = 'editnode'; const editNodeHandler = edit; const dblclickName = 'dblclick'; const dblclickHandler = edit; const keydownName = 'keydown'; const keydownHandler = e => { if (isIntendToInput(e.originEvent) && minder.getSelectedNode()) { edit(e); } }; if (minder) { minder.on(editNodeName, editNodeHandler); minder.on(dblclickName, dblclickHandler); minder.on(keydownName, keydownHandler); } return () => { if (minder) { minder.off(editNodeName, editNodeHandler); minder.off(dblclickName, dblclickHandler); minder.off(keydownName, keydownHandler); } }; }, [minder, onEdit]); useEffect(() => { const escHandler = evt => { const keyCode = getKeyCode(evt); if (keyCode === window.kityminder.KeyMap.esc) { exitEdit(); } }; document.addEventListener('keydown', escHandler); return () => { document.removeEventListener('keydown', escHandler); }; }, [minder]); useEffect(() => { if (minder && editingNode && editorRef.current) { let inputEl; for (const type of ['input', 'textarea', 'select']) { inputEl = editorRef.current.querySelector(type); if (inputEl) { inputEl.focus(); break; } } } }, [minder, Editor, initialValue, editingNode]); return useMemo(() => props => editingNode ? /*#__PURE__*/createElement("div", { style: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }, onClick: onSubmit }, /*#__PURE__*/createElement("div", { ref: editorRef, style: { position: 'absolute', top: `${editingNode.box.y + editingNode.box.height / 2}px`, left: `${editingNode.box.x}px`, transform: 'translateY(-50%)' }, onClick: e => e.stopPropagation() }, /*#__PURE__*/createElement(Editor, Object.assign({}, props, { minder: minder, initialValue: initialValue, onSubmit: onSubmit, onChange: setEditorValue, onCancel: exitEdit })))) : null, [minder, Editor, initialValue, editingNode]); } function Note(props) { return /*#__PURE__*/createElement("div", { style: { background: '#fff', height: '100%' } }, props.node.data.note); } function NoteWrapper(minder, props) { const [showingNode, setShowingNode] = useState(); const { Note } = props; const showNote = useCallback(({ node }) => { if (node) { const box = node.getRenderer('OutlineRenderer').getRenderShape().getRenderBox('view'); const editingNode = { node, box }; if (box.x > 0 || box.y > 0) { setShowingNode(editingNode); } } }, []); const hideNote = useCallback(() => { setShowingNode(); }, []); useEffect(() => { const showNodeName = 'shownoterequest'; const mousewheel = 'mousewheel'; if (minder) { minder.on(showNodeName, showNote); minder.on(mousewheel, hideNote); document.addEventListener(mousewheel, hideNote); } return () => { if (minder) { minder.off(showNodeName, showNote); minder.off(mousewheel, hideNote); document.removeEventListener(mousewheel, hideNote); } }; }, [minder]); return useMemo(() => props => showingNode ? /*#__PURE__*/createElement("div", { style: { position: 'absolute', top: `${showingNode.box.y + showingNode.box.height / 2}px`, left: `${showingNode.box.x}px`, width: `${showingNode.box.width}px`, height: `${showingNode.box.height}px`, transform: 'translateY(-50%)' }, onMouseOut: hideNote }, /*#__PURE__*/createElement(Note, Object.assign({}, props, { node: showingNode.node }))) : null, [minder, Note, showingNode]); } const domPropNames = ['id', 'className', 'style', 'title', 'tabIndex']; var Kityminder = /*#__PURE__*/forwardRef(function Kityminder(props, ref) { const minderRef = useRef(); let minder = minderRef.current; const domProps = {}; Object.keys(props).forEach(propKey => { if (domPropNames.indexOf(propKey) >= 0) { domProps[propKey] = props[propKey]; } }); if (!minder) { minder = minderRef.current = new window.kityminder.Minder(props); if (props.onMinderChange) { setTimeout(() => { // fix warning: Cannot update a component while rendering a different component. props.onMinderChange(minder); }, 0); } if (ref) { ref.current = minder; } } useValue(minder, props.value); useEvents(minder, props); useChangeHandler(minder, props.onChange); const EditorComponent = EditorWrapper(minder, Object.assign({ Editor }, props)); const NoteComponent = NoteWrapper(minder, Object.assign({ Note }, props)); return /*#__PURE__*/createElement("div", Object.assign({}, domProps, { style: { position: 'relative', width: '100%', height: '400px', ...domProps.style } }), useMemo(() => /*#__PURE__*/createElement("div", { ref: div => { if (div) { minder.renderTo(div); const receiver = div.querySelector('.km-receiver'); if (receiver) { Object.assign(receiver.style, { position: 'absolute', left: '-99999px', top: '-99999px' }); } } }, style: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 } }), [minder]), /*#__PURE__*/createElement(EditorComponent, props), /*#__PURE__*/createElement(NoteComponent, props)); }); function extendsKityminder(kityminder) { // fix: detached node getMinder() returns undefined kityminder.Minder.prototype.attachNode = function (node) { const _this = this; const rc = _this.getRenderContainer(); node.traverse(function (current) { current.attached = true; current.minder = _this; rc.addShape(current.getRenderContainer()); }); rc.addShape(node.getRenderContainer()); _this.fire('nodeattach', { node }); }; kityminder.Node.prototype.getMinder = function () { return this.getRoot().minder || this.minder; }; } const kity = window.kity; const kityminder = window.kityminder; extendsKityminder(kityminder); export default Kityminder; export { Kityminder, kity, kityminder }; //# sourceMappingURL=react-kityminder.modern.js.map