diff --git a/Changelog.md b/Changelog.md
index 79fe813aa7..9a393ad05c 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -29,7 +29,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- FIO-7146: formiojs-circleci-to-ghactions
- FIO-6859: update-s3-to-accept-headers-from-signer-presign
- FIO 7239: support for AWS S3 Multipart Upload
- - FIO-7239: add polyfill and include token in abort and complete requests for multipart upload
+ - FIO-7239: add polyfill and include token in abort and complete requests for multipart upload2
+ - FIO-7429: removed columns component settings(pull, offset, push)
+ - FIO-7466: Tooltips fix
+ - FIO-7355: fixed issue with HTML5 select flickering on initial click
+ - FIO-7530: added ability to pass onSetItems component setting as a string (needed for builder mode)
+ - FIO-7528: Revert FIO-4405: fixed an issue where walidation error displays with empty value even if it is not required (#4746)
+ - FIO-7547: Container hidden with conditional logic still appears in submission #5401
+ - FIO-7550: Fixing choices css issue
+ - FIO-7074/FIO-7379: Fixes some issues caused by Wizzard was not always setting _data to submission data
+ - FIO-7208: Moved Tree component to the contrib library
+ - FIO-7406 Fixed plain Textarea interpolating data in readonly mode [#5396](https://github.com/formio/formio.js/pull/5383)
+ - FIO-7112: fixed issues with calendar widget display for value components in new simple conditionals ui
+
+### Changed
+ - Add capability for adding sanitize profiles through sanitizeConfig in options
## 5.0.0-rc.26
### Changed
diff --git a/gulpfile.js b/gulpfile.js
index fa4b8277e3..d0bd609d64 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -75,7 +75,7 @@ gulp.task('styles-builder', function builderStyles() {
});
gulp.task('styles-full', gulp.series('builder-fonts', function fullStyles() {
return compileStyles([
- './node_modules/@formio/choices.js/public/assets/styles/choices.min.css',
+ './node_modules/@formio/choices.js/public/assets/styles/choices.css',
'./node_modules/tippy.js/dist/tippy.css',
'./node_modules/dialog-polyfill/dialog-polyfill.css',
'./node_modules/dragula/dist/dragula.css',
diff --git a/src/Wizard.js b/src/Wizard.js
index b514d19121..49f54eec5a 100644
--- a/src/Wizard.js
+++ b/src/Wizard.js
@@ -897,25 +897,23 @@ export default class Wizard extends Webform {
}
setValue(submission, flags = {}, ignoreEstablishment) {
- this._submission = submission;
- if (
- (flags && flags.fromSubmission && (this.options.readOnly || this.editMode) && !this.isHtmlRenderMode()) ||
- (flags && flags.fromSubmission && (this.prefixComps.length || this.suffixComps.length) && submission._id) ||
- (this.options.server && (this.prefixComps.length || this.suffixComps.length))
- ) {
- this._data = submission.data;
- }
-
- if (!ignoreEstablishment) {
- this.establishPages(submission.data);
- }
const changed = this.getPages({ all: true }).reduce((changed, page) => {
return this.setNestedValue(page, submission.data, flags, changed) || changed;
}, false);
+ this.mergeData(this.data, submission.data);
+
if (changed) {
this.pageFieldLogic(this.page);
}
+
+ submission.data = this.data;
+ this._submission = submission;
+
+ if (!ignoreEstablishment) {
+ this.establishPages(submission.data);
+ }
+
this.setEditMode(submission);
return changed;
diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js
index c64abaa4eb..e03c56d624 100644
--- a/src/components/_classes/component/Component.js
+++ b/src/components/_classes/component/Component.js
@@ -211,7 +211,12 @@ export default class Component extends Element {
return {
operators: ['isEqual', 'isNotEqual', 'isEmpty', 'isNotEmpty'],
valueComponent() {
- return { type: 'textfield' };
+ return {
+ type: 'textfield',
+ widget: {
+ type: 'input'
+ }
+ };
}
};
}
@@ -1223,7 +1228,7 @@ export default class Component extends Element {
placement: 'right',
zIndex: 10000,
interactive: true,
- content: this.t(tooltipText, { _userInput: true }),
+ content: this.t(this.sanitize(tooltipText), { _userInput: true }),
});
}
});
@@ -3321,8 +3326,6 @@ export default class Component extends Element {
shouldSkipValidation(data, dirty, row) {
const rules = [
- // Do not check custom validation for empty data if it is not required
- () => this.component.validate.custom && !this.dataValue && !this.component.validate.required,
// Force valid if component is read-only
() => this.options.readOnly,
// Do not check validations if component is not an input component.
diff --git a/src/components/_classes/component/Component.unit.js b/src/components/_classes/component/Component.unit.js
index 7726f1b97c..46606065f2 100644
--- a/src/components/_classes/component/Component.unit.js
+++ b/src/components/_classes/component/Component.unit.js
@@ -9,6 +9,7 @@ import { comp1 } from './fixtures';
import _merge from 'lodash/merge';
import comp3 from './fixtures/comp3';
import comp4 from './fixtures/comp4';
+import comp5 from './fixtures/comp5';
describe('Component', () => {
it('Should create a Component', (done) => {
@@ -356,4 +357,17 @@ describe('Component', () => {
.catch(done);
});
});
+
+ it('Should not execute code inside Tooltips/Description', (done) => {
+ const formElement = document.createElement('div');
+ const form = new Webform(formElement);
+
+ form.setForm(comp5).then(() => {
+ setTimeout(() => {
+ assert.equal(window._ee, undefined, 'Should not execute code inside Tooltips/Description');
+ done();
+ }, 200);
+ })
+ .catch(done);
+ });
});
diff --git a/src/components/_classes/component/fixtures/comp5.js b/src/components/_classes/component/fixtures/comp5.js
new file mode 100644
index 0000000000..68806c95d7
--- /dev/null
+++ b/src/components/_classes/component/fixtures/comp5.js
@@ -0,0 +1,24 @@
+export default {
+ type: 'form',
+ display: 'form',
+ components: [
+ {
+ label: 'Text Field',
+ description: "",
+ tooltip: "
{
}, 100);
}).catch(done);
});
+
+ it('Should not set the default value when clearOnHide during the server-side validation', (done) => {
+ const form = _.cloneDeep(comp4);
+ const element = document.createElement('div');
+
+ Formio.createForm(element, form, { server: true, noDefaults: true }).then(form => {
+ form.setValue({ data: { checkbox: false } }, {
+ sanitize: true,
+ }, true);
+
+ form.checkConditions();
+ form.clearOnHide();
+
+ setTimeout(() => {
+ assert.deepEqual(form._data, { checkbox: false }, 'Should not add Container\'s key');
+ done();
+ }, 200);
+ }).catch(done);
+ });
});
diff --git a/src/components/container/fixtures/comp4.js b/src/components/container/fixtures/comp4.js
new file mode 100644
index 0000000000..c48b94e359
--- /dev/null
+++ b/src/components/container/fixtures/comp4.js
@@ -0,0 +1,43 @@
+export default {
+ type: 'form',
+ display: 'form',
+ components: [
+ {
+ label: 'Checkbox',
+ tableView: false,
+ key: 'checkbox',
+ type: 'checkbox',
+ input: true,
+ },
+ {
+ label: 'Container',
+ tableView: false,
+ key: 'container',
+ conditional: {
+ show: true,
+ when: 'checkbox',
+ eq: 'true',
+ },
+ type: 'container',
+ input: true,
+ components: [
+ {
+ label: 'Text Field',
+ applyMaskOn: 'change',
+ tableView: true,
+ key: 'textField',
+ type: 'textfield',
+ input: true,
+ },
+ ],
+ },
+ {
+ type: 'button',
+ label: 'Submit',
+ key: 'submit',
+ disableOnInvalid: true,
+ input: true,
+ tableView: false,
+ },
+ ],
+};
diff --git a/src/components/container/fixtures/index.js b/src/components/container/fixtures/index.js
index 63f18da5cf..f1a58d4b03 100644
--- a/src/components/container/fixtures/index.js
+++ b/src/components/container/fixtures/index.js
@@ -1,4 +1,5 @@
import comp1 from './comp1';
import comp2 from './comp2';
import comp3 from './comp3';
-export { comp1, comp2, comp3 };
+import comp4 from './comp4';
+export { comp1, comp2, comp3, comp4 };
diff --git a/src/components/index.js b/src/components/index.js
index 5ec679e647..9df2ad255a 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -42,7 +42,6 @@ import TagsComponent from './tags/Tags';
import TextAreaComponent from './textarea/TextArea';
import TextFieldComponent from './textfield/TextField';
import TimeComponent from './time/Time';
-import TreeComponent from './tree/Tree';
import UnknownComponent from './unknown/Unknown';
import UrlComponent from './url/Url';
import WellComponent from './well/Well';
@@ -93,7 +92,6 @@ export default {
textarea: TextAreaComponent,
textfield: TextFieldComponent,
time: TimeComponent,
- tree: TreeComponent,
unknown: UnknownComponent,
url: UrlComponent,
well: WellComponent,
diff --git a/src/components/select/Select.js b/src/components/select/Select.js
index 9e651ee580..d41a252c6d 100644
--- a/src/components/select/Select.js
+++ b/src/components/select/Select.js
@@ -400,8 +400,10 @@ export default class SelectComponent extends ListComponent {
}
// Allow js processing (needed for form builder)
- if (this.component.onSetItems && typeof this.component.onSetItems === 'function') {
- const newItems = this.component.onSetItems(this, items);
+ if (this.component.onSetItems) {
+ const newItems = typeof this.component.onSetItems === 'function'
+ ? this.component.onSetItems(this, items)
+ : this.evaluate(this.component.onSetItems, { items: items }, 'items');
if (newItems) {
items = newItems;
}
@@ -943,7 +945,11 @@ export default class SelectComponent extends ListComponent {
}
this.focusableElement = input;
- this.addEventListener(input, 'focus', () => this.update());
+
+ if (this.component.dataSrc === 'custom') {
+ this.addEventListener(input, 'focus', () => this.updateCustomItems());
+ }
+
this.addEventListener(input, 'keydown', (event) => {
const { key } = event;
diff --git a/src/components/select/Select.unit.js b/src/components/select/Select.unit.js
index e775fdde29..51bb3f06fa 100644
--- a/src/components/select/Select.unit.js
+++ b/src/components/select/Select.unit.js
@@ -42,7 +42,7 @@ describe('Select Component', () => {
assert.equal(component.dataValue.value, 'a');
assert.equal(typeof component.dataValue , 'object');
done();
- }, 100);
+ }, 300);
});
});
diff --git a/src/components/textarea/TextArea.js b/src/components/textarea/TextArea.js
index bfe2ad4b12..3639aedafe 100644
--- a/src/components/textarea/TextArea.js
+++ b/src/components/textarea/TextArea.js
@@ -342,10 +342,10 @@ export default class TextAreaComponent extends TextFieldComponent {
if (this.options.readOnly || this.disabled) {
if (this.refs.input && this.refs.input[index]) {
if (this.component.inputFormat === 'plain') {
- this.refs.input[index].innerText = this.interpolate(value, {}, { noeval: true });
+ this.refs.input[index].innerText = this.isPlain ? value : this.interpolate(value, {}, { noeval: true });
}
else {
- this.setContent(this.refs.input[index], this.interpolate(value, {}, { noeval: true }), this.shouldSanitizeValue);
+ this.setContent(this.refs.input[index], this.isPlain ? value : this.interpolate(value, {}, { noeval: true }), this.shouldSanitizeValue);
}
}
}
diff --git a/src/components/textfield/TextField.js b/src/components/textfield/TextField.js
index 22f82c1f12..08871db25b 100644
--- a/src/components/textfield/TextField.js
+++ b/src/components/textfield/TextField.js
@@ -44,6 +44,12 @@ export default class TextFieldComponent extends Input {
return {
...super.conditionOperatorsSettings,
operators: [...super.conditionOperatorsSettings.operators, 'includes', 'notIncludes', 'endsWith', 'startsWith'],
+ valueComponent(classComp) {
+ return {
+ ...classComp,
+ type: 'textfield',
+ };
+ }
};
}
diff --git a/src/components/tree/Node.js b/src/components/tree/Node.js
deleted file mode 100644
index 58db7167b8..0000000000
--- a/src/components/tree/Node.js
+++ /dev/null
@@ -1,227 +0,0 @@
-import _ from 'lodash';
-
-export default class Node {
- constructor(
- parent,
- {
- data = {},
- children = [],
- } = {},
- {
- checkNode,
- createComponents,
- isNew = true,
- removeComponents,
- parentPath = ''
- } = {},
- ) {
- this.parent = parent;
- this.previousData = {};
- this.persistentData = _.cloneDeep(data);
- this.new = isNew;
- this.createComponents = createComponents;
- this.checkNode = checkNode;
- this.removeComponents = removeComponents;
- this.revertAvailable = false;
- this.editing = false;
- this.collapsed = false;
- this.components = [];
- this.children = [];
- this.parentPath = parentPath;
-
- this.resetData();
- this.children = children.map((child, index) => new Node(this, child, {
- checkNode,
- createComponents,
- isNew: false,
- removeComponents,
- parentPath: this.getChildrenPath(index),
- }));
-}
-
- get value() {
- return this.new
- ? null // Check the special case for empty root node.
- : {
- data: _.cloneDeep(this.persistentData),
- children: this.children.filter((child) => !child.new).map((child) => child.value),
- };
- }
-
- get isRoot() {
- return this.parent === null;
- }
-
- get changing() {
- return this.new || this.editing;
- }
-
- get hasChangingChildren() {
- return this.changin || this.children.some((child) => child.hasChangingChildren);
- }
-
- get hasData() {
- return !_.isEmpty(this.persistentData);
- }
-
- get hasChildren() {
- return Array.isArray(this.children) && this.children.length > 0;
- }
-
- getChildrenPath(index) {
- return this.parentPath ? `${this.parentPath}.children[${index}]` : '';
- }
-
- eachChild(iteratee) {
- iteratee(this);
- this.children.forEach((child) => child.eachChild(iteratee));
- return this;
- }
-
- getComponents() {
- return this.children.reduce(
- (components, child) => components.concat(child.getComponents()),
- this.components,
- );
- }
-
- validateNode() {
- let valid = true;
- this.getComponents().forEach(comp => {
- comp.setPristine(false);
- valid &= comp.checkValidity(null, false, this.persistentData);
- });
- return valid;
- }
-
- addChild() {
- if (this.new) {
- return null;
- }
-
- const child = new Node(this, {}, {
- checkNode: this.checkNode,
- createComponents: this.createComponents,
- isNew: true,
- removeComponents: this.removeComponents,
- parentPath: this.getChildrenPath(this.children.length),
- });
- this.children = this.children.concat(child);
- return child;
- }
-
- removeChild(childToRemove) {
- if (!this.new) {
- this.children = this.children.filter((child) => child !== childToRemove);
- }
-
- return this;
- }
-
- edit() {
- if (this.new) {
- return this;
- }
-
- this.editing = true;
- return this.resetData();
- }
-
- save() {
- const isValid = this.validateNode();
- if (this.changing && isValid) {
- if (this.new) {
- this.new = false;
- }
- else {
- this.editing = false;
- this.revertAvailable = true;
- }
- this.commitData();
- }
-
- return isValid;
- }
-
- cancel() {
- if (this.new) {
- this.remove();
- }
- else if (this.editing) {
- this.editing = false;
- this.resetData();
- }
-
- return this;
- }
-
- remove() {
- this.parent.removeChild(this);
- this.parent = null;
- this.clearComponents();
- return this;
- }
-
- revert() {
- if (!this.revertAvailable) {
- return this;
- }
-
- this.data = this.previousData;
- return this.commitData();
- }
-
- commitData() {
- this.previousData = this.persistentData;
- this.persistentData = _.cloneDeep(this.data);
- this.clearComponents();
- return this;
- }
-
- resetData() {
- this.data = _.cloneDeep(this.persistentData);
- this.updateComponentsContext();
- return this;
- }
-
- updateComponentsContext() {
- if (this.changing) {
- this.instantiateComponents();
- }
- else {
- this.clearComponents();
- }
-
- return this;
- }
-
- instantiateComponents() {
- this.components = this.createComponents(this.data, this);
- this.components.forEach((component) => {
- if (this.parentPath) {
- const path = this.calculateComponentPath(component);
- component.path = path;
- }
- });
- this.checkNode(this);
- }
-
- clearComponents() {
- this.removeComponents(this.components);
- this.components = [];
- }
-
- /**
- * Return a path of component's value.
- *
- * @param {Object} component - The component instance.
- * @return {string} - The component's value path.
- */
- calculateComponentPath(component) {
- let path = '';
- if (component.component.key) {
- path = `${this.parentPath}.data.${component.component.key}`;
- }
- return path;
- }
-}
diff --git a/src/components/tree/Tree.form.js b/src/components/tree/Tree.form.js
deleted file mode 100644
index 2fe5c9a19b..0000000000
--- a/src/components/tree/Tree.form.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import componentEditForm from '../_classes/component/Component.form';
-import TreeEditData from './editForm/Tree.edit.data';
-import TreeDisplayData from './editForm/Tree.edit.display';
-export default function(...extend) {
- return componentEditForm([
- {
- key: 'display',
- components: TreeDisplayData,
- },
- {
- key: 'data',
- components: TreeEditData,
- },
- ], ...extend);
-}
diff --git a/src/components/tree/Tree.js b/src/components/tree/Tree.js
deleted file mode 100644
index 67a5e7c683..0000000000
--- a/src/components/tree/Tree.js
+++ /dev/null
@@ -1,520 +0,0 @@
-import _ from 'lodash';
-import Component from '../_classes/component/Component';
-import Components from '../Components';
-import NestedDataComponent from '../_classes/nesteddata/NestedDataComponent';
-import Node from './Node';
-
-export default class TreeComponent extends NestedDataComponent {
- static schema(...extend) {
- return NestedDataComponent.schema({
- label: 'Tree',
- key: 'tree',
- type: 'tree',
- clearOnHide: true,
- input: true,
- tree: true,
- components: [],
- multiple: false,
- }, ...extend);
- }
-
- static get builderInfo() {
- return {
- title: 'Tree',
- icon: 'indent',
- weight: 40,
- documentation: '/userguide/form-building/data-components#tree',
- showPreview: false,
- schema: TreeComponent.schema(),
- };
- }
-
- constructor(...args) {
- super(...args);
- this.type = 'tree';
- }
-
- get emptyValue() {
- return {};
- }
-
- get viewComponents() {
- if (!this.viewComponentsInstantiated) {
- this.viewComponentsInstantiated = true;
- this._viewComponents = this.createComponents({});
- }
-
- return this._viewComponents;
- }
-
- init() {
- if (this.builderMode) {
- return super.init();
- }
-
- this.components = [];
- this.componentOptions = {
- ...this.options,
- parent: this,
- root: this.root || this,
- };
- this.disabled = this.shouldDisabled;
- this.setRoot();
- this.viewComponentsInstantiated = false;
- this._viewComponents = [];
- }
-
- get disabled() {
- return super.disabled;
- }
-
- set disabled(disabled) {
- super.disabled = disabled;
- this.viewComponents.forEach((component) => component.parentDisabled = disabled);
- }
-
- get isDefaultValueComponent() {
- return !!this.options.editComponent && !!this.options.editForm && this.component.key === 'defaultValue';
- }
-
- destroy(all = false) {
- if (!this.builderMode) {
- this.removeComponents(this._viewComponents, all);
- }
- super.destroy(all);
- }
-
- createComponents(data, node) {
- const components = this.componentComponents.map(
- (component) => {
- const componentInstance = Components.create(component, this.componentOptions, data);
- componentInstance.init();
- componentInstance.parentDisabled = this.disabled;
- return componentInstance;
- },
- );
-
- if (node) {
- this.checkNode(this.data, node);
- }
-
- return components;
- }
-
- removeComponents(components, all = false) {
- return components.map((component) => component.destroy(all));
- }
-
- render() {
- if (this.builderMode) {
- return super.render();
- }
-
- return super.render(this.renderTree(this.treeRoot));
- }
-
- renderTree(node = {}, odd = true) {
- const childNodes = (node.hasChildren && !node.collapsed)
- ? this.renderChildNodes(node.children, !odd)
- : [];
- const content = node.changing
- ? this.renderEdit(node)
- : this.renderView(node);
-
- return this.renderTemplate('tree', {
- odd,
- childNodes,
- content,
- node,
- });
- }
-
- renderChildNodes(nodes = [], odd) {
- return nodes.map((node) => this.renderTree(node, odd));
- }
-
- renderEdit(node = {}) {
- return this.renderTemplate('treeEdit', {
- children: this.renderComponents(node.components),
- node,
- });
- }
-
- renderView(node = {}) {
- return this.renderTemplate('treeView', {
- values: this.viewComponents.map((component) => {
- component.data = node.data;
- component.checkComponentConditions(node.data);
- return component.getView(component.dataValue);
- }),
- nodeData: node.data,
- node,
- });
- }
-
- attach(element) {
- if (this.builderMode) {
- return super.attach(element);
- }
-
- this.loadRefs(element, {
- root: 'single',
- });
-
- return Promise.all([
- super.attach(element),
- this.attachNode(this.refs.root, this.treeRoot),
- ]);
- }
-
- attachNode(element, node) {
- if (!element) {
- return Promise.resolve();
- }
-
- let componentsPromise = Promise.resolve();
- let childrenPromise = Promise.resolve();
-
- node.refs = _.reduce(
- element.children,
- (refs, child) => (
- child.hasAttribute('ref')
- ? {
- ...refs,
- [child.getAttribute('ref')]: child,
- }
- : refs
- ),
- {},
- );
-
- if (node.refs.content) {
- this.attachActions(node);
- componentsPromise = this.attachComponents(node);
- }
-
- if (node.refs.childNodes) {
- childrenPromise = this.attachChildren(node);
- }
-
- return Promise.all([
- componentsPromise,
- childrenPromise,
- ]);
- }
-
- attachActions(node) {
- if (!node.editing) {
- this.loadRefs.call(node, node.refs.content, {
- addChild: 'single',
- editNode: 'single',
- removeNode: 'single',
- revertNode: 'single',
- toggleNode: 'single',
- });
- }
-
- //load refs correctly (if there is nested tree)
- this.loadRefs.call(node, node.refs.content.children[0]?.children[1] || node.refs.content, {
- cancelNode: 'single',
- saveNode: 'single',
- });
-
- if (node.refs.addChild) {
- this.addEventListener(node.refs.addChild, 'click', () => {
- this.addChild(node);
- });
- }
-
- if (node.refs.cancelNode) {
- this.addEventListener(node.refs.cancelNode, 'click', () => {
- this.cancelNode(node);
- });
- }
-
- if (node.refs.editNode) {
- this.addEventListener(node.refs.editNode, 'click', () => {
- this.editNode(node);
- });
- }
-
- if (node.refs.removeNode) {
- this.addEventListener(node.refs.removeNode, 'click', () => {
- this.removeNode(node);
- });
- }
-
- if (node.refs.revertNode) {
- this.addEventListener(node.refs.revertNode, 'click', () => {
- this.revertNode(node);
- });
- }
-
- if (node.refs.saveNode) {
- this.addEventListener(node.refs.saveNode, 'click', () => {
- this.saveNode(node);
- });
- }
-
- if (node.refs.toggleNode) {
- this.addEventListener(node.refs.toggleNode, 'click', () => {
- this.toggleNode(node);
- });
- }
- }
-
- attachComponents(node, ...args) {
- if (this.builderMode) {
- return super.attachComponents.call(this, node, ...args);
- }
-
- this.loadRefs.call(node, node.refs.content, {
- nodeEdit: 'single',
- });
-
- return node.refs.nodeEdit
- ? super.attachComponents(node.refs.nodeEdit, node.components)
- : Promise.resolve();
- }
-
- attachChildren(node) {
- const childElements = node.refs.childNodes.children;
-
- return Promise.all(
- _.map(
- childElements,
- (childElement, index) => this.attachNode(childElement, node.children[index]),
- ),
- );
- }
-
- setValue(value, flags = {}) {
- const changed = this.updateValue(value, flags);
- this.setRoot();
- return changed;
- }
-
- addChild(parent) {
- if (this.options.readOnly || parent.new) {
- return;
- }
-
- this.hook('tree.addChild', {
- parent,
- component: this,
- }, () => {
- const child = parent.addChild();
- this.redraw();
-
- return child;
- });
- }
-
- cancelNode(node) {
- if (this.options.readOnly) {
- return;
- }
-
- this.hook('tree.cancelNode', {
- node,
- component: this,
- }, () => {
- if (node.isRoot) {
- if (node.persistentData && !_.isEmpty(node.persistentData)) {
- node.cancel();
- this.redraw();
- }
- else {
- this.removeRoot();
- }
- }
- else {
- node.cancel();
- this.redraw();
- }
-
- return node;
- });
- }
-
- editNode(node) {
- if (this.options.readOnly || node.new) {
- return;
- }
-
- this.hook('tree.editNode', {
- node,
- component: this,
- }, () => {
- node.edit();
- this.redraw();
-
- return node;
- });
- }
-
- removeNode(node) {
- if (this.options.readOnly || node.new) {
- return;
- }
-
- this.hook('tree.removeNode', {
- node,
- component: this,
- }, () => {
- if (node.isRoot) {
- this.removeRoot();
- }
- else {
- node.remove();
- this.updateTree();
- }
-
- return node;
- });
- }
-
- revertNode(node) {
- if (this.options.readOnly || !node.revertAvailable) {
- return;
- }
-
- this.hook('tree.revertNode', {
- node,
- component: this,
- }, () => {
- node.revert();
- this.updateTree();
-
- return node;
- });
- }
-
- saveNode(node) {
- if (this.options.readOnly) {
- return;
- }
-
- this.hook('tree.saveNode', {
- node,
- component: this,
- }, () => {
- const isSaved = node.save();
- if (isSaved) {
- this.updateTree();
- }
-
- return node;
- });
- }
-
- toggleNode(node) {
- this.hook('tree.toggleNode', {
- node,
- component: this,
- }, () => {
- node.collapsed = !node.collapsed;
- this.redraw();
-
- return node;
- });
- }
-
- removeRoot() {
- if (this.options.readOnly) {
- return;
- }
-
- this.dataValue = this.defaultValue;
- this.setRoot();
- this.redraw();
- }
-
- setRoot() {
- const value = this.getValue();
- this.treeRoot = new Node(null, value, {
- isNew: this.builderMode ? true : !value.data,
- createComponents: this.createComponents.bind(this),
- checkNode: this.checkNode.bind(this, this.data),
- removeComponents: this.removeComponents,
- parentPath: this.isDefaultValueComponent ? (this.path || this.component.key) : null,
- });
- this.hook('tree.setRoot', {
- root: this.treeRoot,
- component: this,
- });
- this.redraw();
- }
-
- getValue() {
- return this.dataValue || {};
- }
-
- updateTree() {
- this.updateValue(this.treeRoot.value);
- this.redraw();
- }
-
- checkData(data, flags, row) {
- return this.checkNode(data, this.treeRoot, flags, row);
- }
-
- checkNode(data, node, flags, row) {
- return node.children.reduce(
- (result, child) => this.checkNode(data, child, flags, row) && result,
- super.checkData(data, flags, node.data, node.components) && !node.editing && !node.new,
- );
- }
-
- getComponents() {
- return this.treeRoot && (this.isDefaultValueComponent || (!this.isDefaultValueComponent && !this.builderMode))
- ? this.treeRoot.getComponents()
- : super.getComponents();
- }
-
- getValueAsString(value, options) {
- const getChildAsString = (value) => {
- let result = (`
-
${k} | -
- ${value.data[k]}
- - `)); - - if (value.children?.length !== 0) { - value.children?.forEach((v) => { - result += getChildAsString(v); - }); - } - - result += ` - |
-
---|