UNPKG

@oarepo/data-renderer

Version:

A library for providing simple (but configurable) UI for rendering of JSON data

774 lines (687 loc) 20.7 kB
/*! * undefined vundefined * (c) */ import deepmerge from 'deepmerge'; import startCase from 'lodash.startcase'; function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function isString(obj) { return Object.prototype.toString.call(obj) === '[object String]'; } function isObject(obj) { return Object(obj) === obj; } function f(decorated) { if (decorated === undefined) { // used as a decorator return function (inner) { return f(inner); }; } var ret = function ret() { return decorated.apply(void 0, arguments); }; ret._dataRendererApply = true; return ret; } /** * description * @param funcOrValue * @param extra {context, layout, data, paths, value, values, pathValues} * @param recursive * @returns {{}|*} */ function applyFunctions(funcOrValue, extra) { var recursive = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; if (funcOrValue === null || funcOrValue === undefined) { return funcOrValue; } if (isString(funcOrValue)) { return funcOrValue; } if (funcOrValue instanceof Function) { if (funcOrValue._dataRendererApply) { // the result of a function is supposed to be resolved, so do not resolve again return funcOrValue(extra); } else { return funcOrValue; } } if (recursive) { if (Array.isArray(funcOrValue)) { return funcOrValue.map(function (x) { return applyFunctions(x, extra, recursive); }); } if (isObject(funcOrValue)) { return Object.getOwnPropertyNames(funcOrValue).filter(function (x) { return !x.startsWith('__'); }).reduce(function (prev, code) { prev[code] = applyFunctions(funcOrValue[code], extra, // do not recurse children, they should be evaluated when their kvpair is rendererd recursive && code !== 'children' && code !== 'item'); return prev; }, {}); } } return funcOrValue; } var RendererMixin = { props: { schema: { type: String, default: 'inline' }, layout: Object, pathLayouts: Object, rendererComponents: Object, extraProps: Object, prop: [String, Number], level: { type: Number, default: 0 } }, methods: { renderBefore: function renderBefore(h, before) { return h(before); }, renderAfter: function renderAfter(h, after) { return h(after); }, getLayout: function getLayout(code, extra) { var schema = this.schema; var localLayout = this.currentLayout[code] || {}; var globalLayout = this.$oarepo.dataRenderer.layouts[schema][code]; var pathLayout = this.getPathLayout(this.paths, code); var merged = (this.$oarepo.dataRenderer.merge || deepmerge.all)([globalLayout, localLayout, pathLayout], this.$oarepo.dataRenderer.layoutMergeOptions); return applyFunctions(merged, extra); }, getPathLayout: function getPathLayout(paths, code) { if (this.pathLayouts === undefined) { return {}; } for (var i = 0; i < paths.length; i++) { var p = this.pathLayouts[paths[i]]; if (p && p[code] !== undefined) { return p[code]; } } return {}; }, renderElement: function renderElement(h, elDef, options, paths, renderChildren, classCode, extra) { var component = elDef.component; if (component === null) { return []; } if (component !== undefined) { var ret = [h(component, Object.assign({}, extra, { class: [].concat(_toConsumableArray(elDef.class || []), ["iqdr-".concat(classCode)], _toConsumableArray(paths.map(function (path) { return "iqdr-path-".concat(path.replace('/', '-')); })), ["iqdr-level-".concat(this.level), this.level === 0 ? "iqdr-layout-".concat(this.schema) : '']), style: elDef.style, attrs: elDef.attrs, props: options }))]; return ret; } var element = elDef.element; if (element === null) { return []; } if (element !== undefined) { var _ret = [h(element, Object.assign({}, extra, { class: [].concat(_toConsumableArray(elDef.class || []), ["iqdr-".concat(classCode)], _toConsumableArray(paths.map(function (path) { return "iqdr-path-".concat(path.replace('/', '-')); })), ["iqdr-level-".concat(this.level), this.level === 0 ? "iqdr-layout-".concat(this.schema) : '']), style: elDef.style, attrs: elDef.attrs, props: options }), renderChildren ? renderChildren() : [])]; return _ret; } return renderChildren ? renderChildren() : []; } }, computed: { currentLayout: function currentLayout() { var layout = this.layout || {}; var pathLayout = this.pathLayouts !== undefined ? this.pathLayouts[this.prop] || {} : {}; return Object.assign({}, layout, pathLayout); } } }; var KVPairComponent = { name: 'DataRendererKVPair', mixins: [RendererMixin], props: { context: [Array, Object], prop: [String, Number], paths: Array }, methods: { createLabel: function createLabel(h, label, value, extra) { var labelTranslator = this.layout.labelTranslator || this.$oarepo.dataRenderer.layouts[this.schema].labelTranslator; return this.renderElement(h, label, Object.assign({}, this.$props, { value: value }), this.paths, function () { return [labelTranslator(label.label, extra)]; }, 'label', {}); }, getValueWrapper: function getValueWrapper(value) { var valueType = Object.prototype.toString.call(value); var type; if (valueType === '[object String]') { type = 'string'; } else if (valueType === '[object Number]') { type = 'number'; } else if (valueType === '[object Boolean]') { type = 'boolean'; } else if (valueType === '[object Array]') { type = 'array'; } else if (valueType === '[object Object]') { type = 'object'; } else if (this.layout.children !== undefined) { type = 'object'; } else if (this.layout.item !== undefined) { type = 'array'; } else if (this.layout.type !== undefined) { type = this.layout.type; } else { type = 'undefined'; } var valueWrapper = this.rendererComponents[type] || this.$oarepo.dataRenderer.rendererComponents[type]; return Object.assign({}, valueWrapper, this.layout.valueWrapper || {}, { valueType: type }); }, renderChildren: function renderChildren(h, value, extra) { var ret = []; var label = this.getLayout('label', extra); if (label.label) { ret.push(this.createLabel(h, label, value, extra)); } var valueWrapper = this.getValueWrapper(value); ret.push(h(valueWrapper.component, { props: { value: value, schema: this.schema, layout: Object.assign({}, this.currentLayout, { valueWrapper: valueWrapper }), paths: this.paths, pathLayouts: this.pathLayouts, rendererComponents: this.rendererComponents, extraProps: this.extraProps, context: this.context, prop: this.prop, level: this.level }, scopedSlots: this.$scopedSlots, slots: this.slots })); return ret; } }, render: function render(h) { var _this = this; var value = this.context[this.prop]; var extra = Object.assign({}, this.$props, { value: value }); return this.renderElement(h, this.getLayout('wrapper', extra), Object.assign({}, this.$props, { value: value }), this.paths, function () { return _this.renderChildren(h, value, extra); }, 'wrapper', {}); } }; function range(n) { return Array.from(Array(n).keys()); } var ArrayComponent = { name: 'data-renderer-array-component', mixins: [RendererMixin], props: { value: Array, paths: Array }, render: function render(h) { var _this = this; var layout = this.currentLayout; var value = this.value; var itemLayout = layout.item; if (itemLayout === undefined) { itemLayout = this.$oarepo.dataRenderer.createDynamicArrayLayout({ value: value, paths: this.paths, schema: this.schema, layout: layout, pathLayouts: this.pathLayouts, rendererComponents: this.rendererComponents, vue: this }); } itemLayout.showEmpty = layout.showEmpty || this.$oarepo.dataRenderer.layouts[this.schema].showEmpty; return this.renderElement(h, this.getLayout('array-wrapper', this.$props), this.$props, this.paths, function () { if (!value) { return []; } return range(value.length).map(function (index) { return h(KVPairComponent, { props: { context: value, prop: index, schema: _this.schema, layout: itemLayout, paths: [].concat(_toConsumableArray(_this.paths.map(function (path) { return "".concat(path, "/").concat(index); })), ["".concat(index)]), pathLayouts: _this.pathLayouts, rendererComponents: _this.rendererComponents, extraProps: _this.extraProps, level: _this.level + 1 }, scopedSlots: _this.$scopedSlots, slots: _this.slots }); }); }, 'array-wrapper', {}); }, computed: {} }; var ObjectComponent = { name: 'data-renderer-object-component', mixins: [RendererMixin], props: { value: Object, paths: Array }, render: function render(h) { var _this = this; var layout = this.currentLayout; var value = this.value || {}; var childrenLayouts = layout.children; if (childrenLayouts === undefined || childrenLayouts.length === 0) { childrenLayouts = this.$oarepo.dataRenderer.createDynamicObjectLayout({ value: value, paths: this.paths, schema: this.schema, layout: layout.childrenLayout, vue: this }); } var showEmpty = layout.showEmpty || this.$oarepo.dataRenderer.layouts[this.schema].showEmpty; return this.renderElement(h, this.getLayout('children-wrapper', this.$props), this.$props, this.paths, function () { var ret = []; if (layout.before) { ret.push(_this.renderBefore(h, layout.before)); } var renderedItems = childrenLayouts.map(function (childLayout) { var prop = childLayout.prop; if ((value[prop] === null || value[prop] === undefined) && !showEmpty) { return; } if (!childLayout.label) { childLayout.label = { label: prop }; } if (layout.childrenLayout !== undefined) { childLayout = Object.assign({}, childLayout, layout.childrenLayout); } ret.push(h(KVPairComponent, { props: { context: value, prop: prop, schema: _this.schema, layout: childLayout, paths: [].concat(_toConsumableArray(_this.paths.map(function (path) { return "".concat(path, "/").concat(prop); })), [prop]), pathLayouts: _this.pathLayouts, rendererComponents: _this.rendererComponents, extraProps: _this.extraProps, level: _this.level + 1 }, scopedSlots: _this.$scopedSlots, slots: _this.slots })); }); ret.push(renderedItems); if (layout.after) { ret.push(_this.renderAfter(h, layout.after)); } return ret; }, 'children-wrapper', {}); }, computed: {} }; var DataRendererComponent = { name: 'data-renderer', props: { schema: { type: String, default: 'inline' }, layout: Object, data: Object, pathLayouts: Object, rendererComponents: Object, extraProps: Object }, render: function render(h) { var context = this.data; if (Array.isArray(context)) { return h(ArrayComponent, { props: { value: context, paths: [], schema: this.schema, layout: this.layout || {}, pathLayouts: this.pathLayouts || {}, rendererComponents: this.rendererComponents || {}, extraProps: this.extraProps || {} } }); } else { return h(ObjectComponent, { props: { value: context, paths: [], schema: this.schema, layout: this.layout || {}, pathLayouts: this.pathLayouts || {}, rendererComponents: this.rendererComponents || {}, extraProps: this.extraProps || {} } }); } } }; function createDynamicArrayLayout(_ref) { var schema = _ref.schema, vue = _ref.vue; return vue.$oarepo.dataRenderer.layouts[schema]; } function createDynamicObjectLayout(_ref2) { var layout = _ref2.layout, value = _ref2.value, schema = _ref2.schema, vue = _ref2.vue; var props = Object.keys(value); var itemDef = vue.$oarepo.dataRenderer.layouts[schema]; return props.map(function (prop) { var item = Object.assign({}, itemDef, { prop: prop }); if (!item.label.label) { item.label = Object.assign({}, item.label, { label: prop }); } if (layout) { item = Object.assign({}, item, layout); } item.showEmpty = true; return item; }); } var StringComponent = { name: 'data-renderer-string-component', mixins: [RendererMixin], props: { value: String, paths: Array }, render: function render(h) { var valueDef = this.getLayout('value', this.$props); var children = []; if (this.$slots.before) { children.push(this.$slots.before); } children.push(this.value); if (this.$slots.after) { children.push(this.$slots.after); } return this.renderElement(h, valueDef, this.$props, this.paths, function () { return children; }, 'value', {}); }, computed: {} }; var NumberComponent = { name: 'data-renderer-number-component', mixins: [RendererMixin], props: { value: Number, paths: Array }, render: function render(h) { var value = this.getLayout('value', this.$props); var children = []; if (this.$slots.before) { children.push(this.$slots.before); } children.push(this.value.toString()); if (this.$slots.after) { children.push(this.$slots.after); } return this.renderElement(h, value, this.$props, this.paths, function () { return children; }, 'value', {}); }, computed: {} }; var BooleanComponent = { name: 'data-renderer-boolean-component', mixins: [RendererMixin], props: { value: Boolean, paths: Array }, render: function render(h) { var value = this.getLayout('value', this.$props); var children = []; if (this.$slots.before) { children.push(this.$slots.before); } if (this.value === undefined) { children.push('false'); } else { children.push(this.value.toString()); } if (this.$slots.after) { children.push(this.$slots.after); } return this.renderElement(h, value, this.$props, this.paths, function () { return children; }, 'value', {}); }, computed: {} }; var UndefinedComponent = { name: 'data-renderer-undefined-component', mixins: [RendererMixin], props: { value: [undefined, null], paths: Array }, render: function render(h) { var value = this.getLayout('value', this.$props); var children = []; if (this.$slots.before) { children.push(this.$slots.before); } children.push('---'); if (this.$slots.after) { children.push(this.$slots.after); } return this.renderElement(h, value, this.$props, this.paths, function () { return children; }, 'value', {}); }, computed: {} }; var index = { install: function install(Vue, options) { options = Object.assign({}, options, { showEmpty: false }); Vue.component(options.dataRendererName || DataRendererComponent.name, DataRendererComponent); if (Vue.prototype.$oarepo === undefined) { Vue.prototype.$oarepo = {}; } Vue.prototype.$oarepo.dataRenderer = Object.assign({ layouts: { inline: { 'children-wrapper': { element: 'div' }, 'array-wrapper': { element: 'div' }, wrapper: { element: 'div' }, label: { element: 'label' }, value: { element: 'div' }, labelTranslator: function labelTranslator(label) { return ( /*extra*/ "".concat(startCase(label), ": ") ); }, showEmpty: false }, block: { 'children-wrapper': { element: 'div' }, 'array-wrapper': { element: 'div' }, wrapper: { element: 'div' }, label: { element: 'label' }, value: { element: 'div' }, labelTranslator: function labelTranslator(label) { return ( /*extra*/ startCase(label) ); }, showEmpty: false }, table: { 'children-wrapper': { element: 'table' }, 'array-wrapper': { element: 'table' }, wrapper: { element: 'tr' }, label: { element: 'td' }, value: { element: 'td' }, labelTranslator: function labelTranslator(label) { return ( /*extra*/ startCase(label) ); }, showEmpty: false }, flex: { 'children-wrapper': { element: 'div' }, 'array-wrapper': { element: 'div' }, wrapper: { element: 'div', class: ['row'] }, label: { element: 'label', class: ['col-auto'] }, value: { element: 'div', class: ['col-auto'] }, labelTranslator: function labelTranslator(label) { return ( /*extra*/ startCase(label) ); }, showEmpty: false } }, layoutMergeOptions: {}, rendererComponents: { string: { component: StringComponent }, number: { component: NumberComponent }, boolean: { component: BooleanComponent }, undefined: { component: UndefinedComponent }, array: { component: ArrayComponent }, object: { component: ObjectComponent } }, createDynamicObjectLayout: createDynamicObjectLayout, createDynamicArrayLayout: createDynamicArrayLayout }, options); } }; export default index; export { ArrayComponent, BooleanComponent, DataRendererComponent, NumberComponent, ObjectComponent, StringComponent, UndefinedComponent, f };