react-docx
Version:
React Reconciler for DOCX - build Docx with JSX
436 lines (366 loc) • 14.5 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var ReactReconciler = require('react-reconciler');
var Docx = require('docx');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) { return e; } else {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
}
n['default'] = e;
return Object.freeze(n);
}
}
var ReactReconciler__default = /*#__PURE__*/_interopDefaultLegacy(ReactReconciler);
var Docx__namespace = /*#__PURE__*/_interopNamespace(Docx);
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
/// IS helper from
/// https://github.com/react-spring/react-three-fiber
var is = {
obj: function obj(a) {
return a === Object(a) && !is.arr(a);
},
fun: function fun(a) {
return typeof a === "function";
},
str: function str(a) {
return typeof a === "string";
},
num: function num(a) {
return typeof a === "number";
},
und: function und(a) {
return a === void 0;
},
arr: function arr(a) {
return Array.isArray(a);
},
equ: function equ(a, b) {
// Wrong type or one of the two undefined, doesn't match
if (_typeof(a) !== _typeof(b) || !!a !== !!b) return false; // Atomic, just compare a against b
if (is.str(a) || is.num(a) || is.obj(a)) return a === b; // Array, shallow compare first to see if it's a match
// eslint-disable-next-line eqeqeq
if (is.arr(a) && a == b) return true; // Last resort, go through keys
var i;
for (i in a) {
if (!(i in b)) return false;
}
for (i in b) {
if (a[i] !== b[i]) return false;
}
return is.und(i) ? a === b : true;
}
};
var DocxTypes = {};
Object.keys(Docx__namespace).forEach(function (key) {
return is.fun(Docx__namespace[key]) ? DocxTypes[key.toLowerCase()] = Docx__namespace[key] : null;
});
DocxTypes["document"] = /*#__PURE__*/function () {
function Document(props) {
_classCallCheck(this, Document);
this.type = "document";
this.props = props;
this.children = [];
}
_createClass(Document, [{
key: "addChildElement",
value: function addChildElement(child) {
this.children.push(child);
}
}]);
return Document;
}(); // missing Docx Primitive placholder
DocxTypes["section"] = /*#__PURE__*/function () {
function Section(props) {
_classCallCheck(this, Section);
this.type = "section";
this.props = props;
this.children = [];
}
_createClass(Section, [{
key: "addChildElement",
value: function addChildElement(child) {
this.children.push(child);
}
}]);
return Section;
}(); /// image wrapper
DocxTypes["image"] = function (_ref) {
var src = _ref.src,
width = _ref.width,
height = _ref.height,
__document = _ref.__document,
props = _objectWithoutProperties(_ref, ["src", "width", "height", "__document"]);
if (!src) {
throw new Error("No image src provided");
}
return Docx.Media.addImage(__document, src, width, height, props);
};
DocxTypes["href"] = function (_ref2) {
var src = _ref2.src,
anchor = _ref2.anchor,
__document = _ref2.__document,
label = _ref2.label;
var hyperlink = new Docx.Hyperlink(label || src, "link" + (__document.docRelationships.RelationshipCount + 1), anchor);
__document.docRelationships.createRelationship(hyperlink.linkId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", src, "External");
return hyperlink;
}; // shortcuts
DocxTypes["t"] = DocxTypes["textrun"];
DocxTypes["p"] = DocxTypes["paragraph"];
DocxTypes["img"] = DocxTypes["image"];
var hostConfig = {
// _props contain raw react children too
createInstance: function createInstance(type, _props, rootContainerInstance, hostContext // internalInstanceHandle - fiber root, we don't need any react magic for now
) {
//locate instance constructor in DocxTypes
var classConstructor = DocxTypes[type]; // sanitise children away from props
// eslint-disable-next-line no-unused-vars
var _props$children = _props.children,
children = _props$children === void 0 ? [] : _props$children,
props = _objectWithoutProperties(_props, ["children"]);
if (classConstructor) {
var params = _objectSpread2(_objectSpread2({
text: classConstructor.name === "TextRun" && (is.str(children) || is.num(children)) ? children : undefined
}, props), {}, {
rows: [],
children: [],
// some docx elements require children param
__document: hostContext.document // pass document reference for fictive elements
});
var docxInstance; // either call class constructor or just a function
try {
docxInstance = classConstructor.name ? new classConstructor(params) : classConstructor(params);
} catch (error) {
console.error(error);
}
Object.keys(props).filter(function (p) {
return is.fun(docxInstance[p]);
}).forEach(function (prop) {
var propVal = props[prop];
var propFun = docxInstance[prop];
docxInstance = is.arr(propVal) ? propFun.apply(docxInstance, propVal) // array was passed as prop val so we treat as arguments
: propFun.call(docxInstance, propVal); // whaterver else was passed is single argument
});
return docxInstance;
}
throw new Error("".concat(type, " is not Docx Element"));
},
// both parentInstance and child are types of host elements (DOCX objects)
appendInitialChild: function appendInitialChild(parentInstance, child) {
if ("addChildElement" in parentInstance) {
parentInstance.addChildElement(child);
} else {
var _ref, _parentInstance$type, _parentInstance$proto;
throw new Error("".concat((_ref = (_parentInstance$type = parentInstance === null || parentInstance === void 0 ? void 0 : parentInstance.type) !== null && _parentInstance$type !== void 0 ? _parentInstance$type : parentInstance === null || parentInstance === void 0 ? void 0 : (_parentInstance$proto = parentInstance.prototype) === null || _parentInstance$proto === void 0 ? void 0 : _parentInstance$proto.constructor.name) !== null && _ref !== void 0 ? _ref : parentInstance, " does not have any methods to append a child"));
}
},
// asks us if CreateTextInstance must be called for text content of that specific element
// for explicit TextRun we return true, so no CreateTextInstance is called
shouldSetTextContent: function shouldSetTextContent(type, props) {
return type === "textrun" && (typeof props.children === "string" || typeof props.children === "number");
},
// create implicit TextRun
createTextInstance: function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
return new Docx.TextRun({
text: text
});
},
/// we use that to add sections to document we have in our context
/// in this step all instances are created, and we add fictive section instance to document
finalizeInitialChildren: function finalizeInitialChildren(domElement, type, _props, rootContainerInstance, hostContext) {
var children = _props.children,
props = _objectWithoutProperties(_props, ["children"]);
if (domElement.type === "section" && hostContext.isRootContext) {
hostContext.document.addSection({
children: domElement.children,
properties: props
});
} else if (domElement.type === "section" || hostContext.isRootContext) {
throw new Error("Section is not a root element or part of root Fragment");
}
},
/// provide document instance to all children
getRootHostContext: function getRootHostContext(rootContainerInstance) {
return {
document: rootContainerInstance.document,
isRootContext: true
};
},
// this is how we let createInstance know that its a child element
getChildHostContext: function getChildHostContext(parentHostContext, type, rootContainerInstance) {
return _objectSpread2(_objectSpread2({}, parentHostContext), {}, {
isRootContext: false,
type: type
});
},
// or even that
getPublicInstance: function getPublicInstance(instance) {
return instance;
},
// pre/post commit callbacks
prepareForCommit: function prepareForCommit() {
return null;
},
preparePortalMount: function preparePortalMount() {
return null;
},
clearContainer: function clearContainer() {
return false;
},
resetAfterCommit: function resetAfterCommit(containerInfo) {},
prepareUpdate: function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
console.log("prepareUpdate", domElement, type, oldProps, newProps, rootContainerInstance, hostContext);
return [null];
},
shouldDeprioritizeSubtree: function shouldDeprioritizeSubtree(type, props) {
//console.log("shouldDeprioritizeSubtree", type, props);
return false;
},
now: Date.now(),
isPrimaryRenderer: true,
scheduleDeferredCallback: "",
cancelDeferredCallback: "",
// -------------------
// Mutation
// -------------------
supportsMutation: true,
commitMount: function commitMount(domElement, type, newProps, internalInstanceHandle) {},
commitUpdate: function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) {},
resetTextContent: function resetTextContent(domElement) {},
commitTextUpdate: function commitTextUpdate(textInstance, oldText, newText) {},
appendChild: function appendChild(parentInstance, child) {},
appendChildToContainer: function appendChildToContainer(container, child) {},
insertBefore: function insertBefore(parentInstance, child, beforeChild) {},
insertInContainerBefore: function insertInContainerBefore(container, child, beforeChild) {},
removeChild: function removeChild(parentInstance, child) {},
removeChildFromContainer: function removeChildFromContainer(container, child) {}
};
var DocxRenderer = ReactReconciler__default['default'](hostConfig);
var render = function render(elements, containerNode, callback) {
if (!containerNode || !is.obj(containerNode)) throw new Error("containerNode must be an empty object"); // We must do this only once
if (!containerNode.__internalContainerStructure) {
containerNode.__internalContainerStructure = DocxRenderer.createContainer(containerNode, false, false);
}
DocxRenderer.updateContainer(elements, containerNode.__internalContainerStructure, null, callback);
};
var renderAsyncDocument = function renderAsyncDocument(elements, options, fileProperties) {
var containerNode = {};
containerNode.document = new Docx.Document(options, fileProperties);
return new Promise(function (resolve) {
render(elements, containerNode, function () {
return resolve(containerNode.document);
});
});
};
exports.renderAsyncDocument = renderAsyncDocument;