@oarepo/data-renderer
Version:
A library for providing simple (but configurable) UI for rendering of JSON data
774 lines (687 loc) • 20.7 kB
JavaScript
/*!
* 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 };