@onereach/webform
Version:
Content Builder includes several views for: - Content builder view itself; - Web Form view; - Slack block-kit builder;
418 lines (359 loc) • 10.9 kB
JavaScript
import Vue from 'vue';
import itemsData from '@/data/contentBuilderFields.json';
import { v4 as uuid } from 'uuid';
import EventBus from '@/helpers/eventBus';
// helpers for testing transform functions between generic and specific channel
// uncomment if you need to test transformation
/* import {
parseSlackMrkdwnToMarkdown,
transformGenericToSlack
} from '@onereach/channel-transformer';
console.log("transformGenericToSlack -> ", transformGenericToSlack); */
import {
OUTPUT_SET_GENERIC,
OUTPUT_SET_CB_SCHEMA,
OUTPUT_SET_LOADING,
OUTPUT_RESET_GENERIC_MODEL,
OUTPUT_SET_GENERIC_FIELD,
OUTPUT_SET_GENERIC_FIELD_VALUE,
OUTPUT_DELETE_GENERIC_FIELD,
OUTPUT_SET_ERROR,
OUTPUT_CLEAR_ERROR,
OUTPUT_SET_GENERIC_CHILDREN,
OUTPUT_SET_WRAPPER_PROP,
OUTPUT_SET_CHANNEL,
OUTPUT_SET_GENERIC_HEIGHT_OF_WEB_PREVIEW,
OUTPUT_SET_STEP_SCHEMA
} from '../mutation-types';
const defaultGenericModel = {
type: 'wrapper',
children: [],
props: {
slack: {
type: 'modal',
title: {
type: 'text',
props: {
value: 'My App',
slack: {
type: 'plain_text',
emoji: true,
verbatim: false
}
}
},
submit: {
type: 'text',
props: {
value: 'Submit',
slack: {
type: 'plain_text',
emoji: true,
verbatim: false
}
}
},
close: {
type: 'text',
props: {
value: 'Cancel',
slack: {
type: 'plain_text',
emoji: true,
verbatim: false
}
}
}
}
}
};
/* const state = {
fieldsList: itemsData,
error: '',
invalidFields: {},
currentChannel: 'web',
genericModel: defaultGenericModel,
slackType: null,
hasInteractive: false,
isGenericMode: false,
loading: true,
stepId: '',
readonly: false,
steps: [],
mergeFields: [],
isNew: true,
authToken: null,
filesApiUrl: null,
schema: null
}; */
const getters = {
getFieldsList: (state) => state.fieldsList[state.currentChannel],
genericModelChildren: (state) => {
if (state.genericModel && state.genericModel.children) {
if (Array.isArray(state.genericModel.children)) {
const sectionIds = []; // track section ids to prevent two sections have the same id, f.e. in case of copying
state.genericModel.children.forEach((child) => {
if (child && typeof child === 'object') {
child.id =
!child.id || sectionIds.includes(child.id) ? uuid() : child.id;
sectionIds.push(child.id);
child.parentId = child.id || uuid();
// child.active = child.active || false;
child.editMode = child.editMode || false;
if (Array.isArray(child.children)) {
return child.children.forEach((children) => {
if (children && typeof children === 'object') {
children.parentId = child.id || uuid();
children.id = children.id || uuid();
// children.active = children.active || false;
children.editMode = children.editMode || false;
}
});
}
}
});
return state.genericModel.children || [];
} else {
return state.genericModel.children;
}
}
},
getGenericDataValue: (state) => ({ id, path }) => {
const field = _.cloneDeep(
state.genericModel.children.filter((field) => field.id === id)[0]
);
return path ? _.get(field, path) : field;
},
getGenericWrapperPropsValue: (state) => ({ path }) => {
return _.get(state.genericModel.props, path);
},
getSchemaValue: (state) => (path) => _.get(state.schema, path)
};
const actions = {
async initialize ({ dispatch }) {
dispatch('initCrossWindowCommunication');
},
sendMessage ({ state }) {
// console.log('sendMesssage state -> ', state);
const invalidFieldsParsed =
window.localStorage.getItem('invalidFields') &&
JSON.parse(window.localStorage.getItem('invalidFields'));
// console.log('invalidFieldsParsed -> ', invalidFieldsParsed);
const postedData = {
eventType: 'set-form-data',
invalid: false,
dataOut: JSON.stringify(state.genericModel)
};
if (invalidFieldsParsed && Object.keys(invalidFieldsParsed).length) {
postedData.invalid = true;
}
window.parent.postMessage(postedData, '*');
},
initCrossWindowCommunication ({ commit, state }) {
if (!window) {
return commit(OUTPUT_SET_LOADING, false);
}
if (!window && process.env.NODE_ENV === 'development') {
commit(OUTPUT_SET_LOADING, false);
}
const handleMessage = (e) => {
let { data } = e;
if (
!data ||
typeof data !== 'string' ||
_.includes(data, 'webpackHotUpdate') ||
data.type === 'webpackOk'
) {
return;
}
data = JSON.parse(data);
if (data.eventType === 'set-initial-data') {
const dataOut = _.get(data, 'dataOut', {});
commit(
OUTPUT_SET_GENERIC,
_.get(dataOut, 'schema.formData.value'),
state.genericModel
);
commit(OUTPUT_SET_CB_SCHEMA, dataOut);
EventBus.$emit('store:set-initial-data', dataOut);
}
// Data for preview instance
if (data.eventType === 'set-preview-data') {
commit(OUTPUT_SET_GENERIC, data.generic || {});
commit(OUTPUT_SET_STEP_SCHEMA, data.schema || {});
}
if (data.eventType === 'update-preview-data') {
// updates schema only
// commit(OUTPUT_SET_GENERIC, data.generic || {});
commit(OUTPUT_SET_STEP_SCHEMA, data.schema || {});
}
};
const tHandler = _.throttle(handleMessage, 300);
window.addEventListener('message', tHandler);
window.parent.postMessage({ eventType: 'get-initial-data' }, '*');
commit(OUTPUT_SET_LOADING, false);
},
setOutputValue ({ dispatch, commit }, payload) {
if (payload) {
commit(OUTPUT_SET_GENERIC_FIELD_VALUE, payload);
}
dispatch('sendMessage');
},
deleteField ({ dispatch, commit }, id) {
commit(OUTPUT_DELETE_GENERIC_FIELD, id);
commit(OUTPUT_CLEAR_ERROR, id);
dispatch('sendMessage');
}
};
const mutations = {
[OUTPUT_SET_CHANNEL] (state, payload) {
state.currentChannel = payload;
},
[OUTPUT_SET_CB_SCHEMA] (state, dataOut) {
const {
slackType,
hasInteractive,
formBuilderType,
stepId,
readonly,
steps,
mergeFields,
isNew,
authToken,
filesApiUrl,
hideInputs,
schema
} = dataOut;
if (_.get(schema, 'formData.value')) {
state.genericModel = _.get(schema, 'formData.value');
}
state.currentChannel = formBuilderType || 'web';
state.slackType = slackType;
state.hideInputs = hideInputs || false;
state.hasInteractive = hasInteractive;
state.stepId = stepId || '';
state.readonly = readonly || false;
state.steps = steps || [];
state.mergeFields = mergeFields || [];
state.isNew = isNew || false;
state.authToken = authToken;
state.filesApiUrl = filesApiUrl;
state.schema = schema;
},
[OUTPUT_SET_LOADING] (state, payload) {
state.loading = payload;
},
[OUTPUT_SET_GENERIC] (state, payload) {
if (_.isArray(payload) || typeof payload === 'string' || !payload) return;
state.genericModel = payload;
// helpers for testing transform functions between generic and specific channel
// uncomment if you need to test transformation
/* console.log(
'transform pure -> ',
transformGenericToSlack(state.genericModel)
);
console.log(
'transform stringified -> ',
JSON.stringify(
transformGenericToSlack(state.genericModel)
)
); */
},
[OUTPUT_SET_GENERIC_CHILDREN] (state, payload) {
if (!Array.isArray(payload)) return;
if (!payload.length) {
const stringified = JSON.stringify({});
window.localStorage.setItem('invalidFields', stringified);
}
state.genericModel.children = payload;
},
[OUTPUT_RESET_GENERIC_MODEL] (state) {
state.genericModel = defaultGenericModel;
},
[OUTPUT_SET_GENERIC_FIELD] (state, payload) {
if (!state.genericModel.children) {
state.genericModel = defaultGenericModel;
}
state.genericModel.children.push(payload);
},
[OUTPUT_DELETE_GENERIC_FIELD] (state, id) {
if (isNaN(id)) {
state.genericModel.children = state.genericModel.children.filter(
(item) => item.id !== id
);
} else {
const index = parseInt(id);
state.genericModel.children.splice(index, 1);
}
},
[OUTPUT_SET_GENERIC_HEIGHT_OF_WEB_PREVIEW] (state, height) {
if (!state.genericModel.props) {
state.genericModel.props = {};
}
state.genericModel.props.webFormHeight = height;
},
[OUTPUT_SET_GENERIC_FIELD_VALUE] (state, payload) {
const { id, path, value } = payload;
const field = _.cloneDeep(
state.genericModel.children.filter((field) => field.id === id)
)[0];
const index = state.genericModel.children.findIndex(
(field) => field.id === id
);
const res = path ? _.set(field, path, value) : value;
Vue.set(state.genericModel.children, index, res);
},
[OUTPUT_SET_WRAPPER_PROP] (state, payload) {
const { path, value } = payload;
const props = _.cloneDeep(state.genericModel.props);
const res = _.set(props, path, value);
Vue.set(state.genericModel, 'props', res);
},
// TODO: for future errors hendling
[OUTPUT_SET_ERROR] (state, schema) {
/*
schema -> Object {id: true|false}
*/
state.invalidFields[schema.id] = schema.invalid;
const stringified = JSON.stringify(state.invalidFields);
window.localStorage.setItem('invalidFields', stringified);
},
[OUTPUT_CLEAR_ERROR] (state, schema) {
if (state.invalidFields[schema.id]) {
delete state.invalidFields[schema.id];
}
const stringified = JSON.stringify(state.invalidFields);
window.localStorage.setItem('invalidFields', stringified);
},
[OUTPUT_SET_STEP_SCHEMA] (state, payload) {
state.schema = payload;
}
};
export default {
namespaced: true,
state () {
return {
fieldsList: itemsData,
error: '',
invalidFields: {},
validation: { $invalid: false },
currentChannel: 'web',
genericModel: defaultGenericModel,
slackType: null,
hasInteractive: false,
isGenericMode: false,
loading: true,
stepId: '',
readonly: false,
steps: [],
mergeFields: [],
isNew: true,
authToken: null,
filesApiUrl: null,
schema: null
};
},
getters,
actions,
mutations
};