UNPKG

@mason-api/javascript-sdk

Version:

Mason component rendering library

127 lines (117 loc) 4.84 kB
import _ from 'lodash'; import update from 'immutability-helper'; import { COMPONENT, DATASOURCE, HTTP, OBJECT, TREE } from '@mason-api/utils'; const applyData = (config, configSubpath, datasources) => update(config, { data: { [configSubpath]: { tree: { $set: TREE.interpolate( _.get(config, `data.${configSubpath}.tree`), datasources, collection => collection || [], value => (_.isNil(value) ? '' : value), ), }, }, }, }); const fetchDatasource = datasource => HTTP.makeCall({ data: OBJECT.keyValueArrayToObject(datasource.queries), headers: OBJECT.keyValueArrayToObject(datasource.headers), url: datasource.url, verb: HTTP.GET, }); const store = { datasources: {}, data: {}, }; let processDatasourceEvents = _.noop; let initDatasourceEventListener = (getContext) => { processDatasourceEvents = (e) => { const form = e.target; const { callback, component, components, instance, render, } = getContext(form); const configSubpath = instance.target.getAttribute('data-config-subpath') || 'default'; const { data: { [configSubpath]: { tree } } } = instance.config; const node = TREE.findNodeAtPath(tree, form.getAttribute('data-path')); const datasourceEvents = _.reduce(_.get(node.p, '_events.success'), (result, event) => { if (event.type === 'datasource') { return _.concat(result, { action: event.action, data: e.detail.data, datasourceId: event.id, path: event.path, }); } return result; }, []); if (!_.isEmpty(datasourceEvents)) { const updatedDatasources = {}; const didFetchData = callback('didFetchData', component.id, instance.target); _.forEach(datasourceEvents, (op) => { updatedDatasources[op.datasourceId] = true; const datasource = _.get(store.datasources, `${component.projectId}.${op.datasourceId}`); const data = didFetchData(op.data, datasource, component.id); receiveDatasource(op.datasourceId, component.projectId, data, op.path, op.action); _.forEach(document.querySelector('mason-canvas'), (canvas) => { const id = canvas.getAttribute('data-id'); const c = _.get(components, id); if (c && COMPONENT.usesDatasource(c, op.datasourceId)) { canvas.setAttribute('data-render', !!component.getAttribute('data-render')); // trigger a re-render using the observer } }); }); } }; initDatasourceEventListener = _.noop; }; const receiveDatasource = (datasourceId, projectId, data, path = '', operation = 'set') => { const datasource = _.get(store.datasources, `${projectId}.${datasourceId}`); let nextDatasource; switch (operation) { case 'replace': case 'set': nextDatasource = DATASOURCE.setData(datasource, data, path); break; case 'merge': nextDatasource = DATASOURCE.mergeData(datasource, data, path); break; case 'remove': nextDatasource = DATASOURCE.removeData(datasource, data, path); break; default: nextDatasource = DATASOURCE.setData(datasource, data, path); } store.datasources[projectId][datasourceId] = nextDatasource; }; export default { has: _.stubTrue, init: getContext => next => (config) => { const { components, projects } = getContext(); const component = _.get(components, config.componentId); const project = _.get(projects, component.projectId); if (!_.has(store.datasources, project.id)) { store.datasources[project.id] = { ...project.datasources }; } initDatasourceEventListener(getContext); return next(config); }, render: getContext => next => (config, configSubpath, target, props) => { const { callback, components } = getContext(); const component = _.get(components, config.componentId); target.addEventListener('didReceiveData', processDatasourceEvents); if (!_.isEmpty(config.data.default.datasources)) { const datasourcesToFetch = _.pick(store.datasources[component.projectId], _.map(config.data.default.datasources, 'id')); const before = callback('willFetchData', config.componentId, target); const after = callback('didFetchData', config.componentId, target); Promise.all(_.map(datasourcesToFetch, datasource => fetchDatasource(before(datasource, config.componentId)) .then(d => receiveDatasource(datasource.id, component.projectId, after(d, datasource, config.componentId))))) .finally(() => next(applyData(config, configSubpath, store.datasources[component.projectId]), configSubpath, target, props)); } else { next(applyData(config, configSubpath, store.datasources[component.projectId]), configSubpath, target, props); } }, };