formeo 3.0.8 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/formeo.es.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /**
3
3
  formeo - https://formeo.io
4
- Version: 3.0.7
4
+ Version: 3.1.0
5
5
  Author: Draggable https://draggable.io
6
6
  */
7
7
 
@@ -434,7 +434,7 @@ if (window !== void 0) {
434
434
  window.SmartTooltip = SmartTooltip;
435
435
  }
436
436
  const name$1 = "formeo";
437
- const version$2 = "3.0.7";
437
+ const version$2 = "3.1.0";
438
438
  const type = "module";
439
439
  const main = "dist/formeo.cjs.js";
440
440
  const module = "dist/formeo.es.js";
@@ -526,6 +526,7 @@ const devDependencies = {
526
526
  "@semantic-release/changelog": "^6.0.3",
527
527
  "@semantic-release/git": "^10.0.1",
528
528
  "@semantic-release/npm": "^12.0.1",
529
+ "ace-builds": "^1.36.5",
529
530
  jsdom: "^25.0.1",
530
531
  lefthook: "^1.7.18",
531
532
  "npm-run-all": "^2.1.0",
@@ -3049,7 +3050,7 @@ function lastChild(el, selector) {
3049
3050
  }
3050
3051
  return last || null;
3051
3052
  }
3052
- function index(el, selector) {
3053
+ function index$6(el, selector) {
3053
3054
  var index2 = 0;
3054
3055
  if (!el || !el.parentNode) {
3055
3056
  return -1;
@@ -3660,8 +3661,8 @@ Sortable.prototype = /** @lends Sortable.prototype */
3660
3661
  if (lastDownEl === target) {
3661
3662
  return;
3662
3663
  }
3663
- oldIndex = index(target);
3664
- oldDraggableIndex = index(target, options.draggable);
3664
+ oldIndex = index$6(target);
3665
+ oldDraggableIndex = index$6(target, options.draggable);
3665
3666
  if (typeof filter === "function") {
3666
3667
  if (filter.call(this, evt, target, this)) {
3667
3668
  _dispatchEvent({
@@ -4081,8 +4082,8 @@ Sortable.prototype = /** @lends Sortable.prototype */
4081
4082
  return completedFired = true;
4082
4083
  }
4083
4084
  function changed() {
4084
- newIndex = index(dragEl);
4085
- newDraggableIndex = index(dragEl, options.draggable);
4085
+ newIndex = index$6(dragEl);
4086
+ newDraggableIndex = index$6(dragEl, options.draggable);
4086
4087
  _dispatchEvent({
4087
4088
  sortable: _this,
4088
4089
  name: "change",
@@ -4168,7 +4169,7 @@ Sortable.prototype = /** @lends Sortable.prototype */
4168
4169
  direction = _getSwapDirection(evt, target, targetRect, vertical, differentRowCol ? 1 : options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold, isCircumstantialInvert, lastTarget === target);
4169
4170
  var sibling;
4170
4171
  if (direction !== 0) {
4171
- var dragIndex = index(dragEl);
4172
+ var dragIndex = index$6(dragEl);
4172
4173
  do {
4173
4174
  dragIndex -= direction;
4174
4175
  sibling = parentEl.children[dragIndex];
@@ -4230,14 +4231,14 @@ Sortable.prototype = /** @lends Sortable.prototype */
4230
4231
  },
4231
4232
  _onDrop: function _onDrop(evt) {
4232
4233
  var el = this.el, options = this.options;
4233
- newIndex = index(dragEl);
4234
- newDraggableIndex = index(dragEl, options.draggable);
4234
+ newIndex = index$6(dragEl);
4235
+ newDraggableIndex = index$6(dragEl, options.draggable);
4235
4236
  pluginEvent2("drop", this, {
4236
4237
  evt
4237
4238
  });
4238
4239
  parentEl = dragEl && dragEl.parentNode;
4239
- newIndex = index(dragEl);
4240
- newDraggableIndex = index(dragEl, options.draggable);
4240
+ newIndex = index$6(dragEl);
4241
+ newDraggableIndex = index$6(dragEl, options.draggable);
4241
4242
  if (Sortable.eventCanceled) {
4242
4243
  this._nulling();
4243
4244
  return;
@@ -4584,7 +4585,7 @@ function _getSwapDirection(evt, target, targetRect, vertical, swapThreshold, inv
4584
4585
  return 0;
4585
4586
  }
4586
4587
  function _getInsertDirection(target) {
4587
- if (index(dragEl) < index(target)) {
4588
+ if (index$6(dragEl) < index$6(target)) {
4588
4589
  return 1;
4589
4590
  } else {
4590
4591
  return -1;
@@ -4632,7 +4633,7 @@ Sortable.utils = {
4632
4633
  closest,
4633
4634
  toggleClass,
4634
4635
  clone,
4635
- index,
4636
+ index: index$6,
4636
4637
  nextTick: _nextTick,
4637
4638
  cancelNextTick: _cancelNextTick,
4638
4639
  detectDirection: _detectDirection,
@@ -6776,6 +6777,9 @@ class Field extends Component {
6776
6777
  */
6777
6778
  constructor(fieldData = /* @__PURE__ */ Object.create(null)) {
6778
6779
  super("field", { ...DEFAULT_DATA$3(), ...fieldData });
6780
+ __publicField(this, "setData", (path, value) => {
6781
+ return super.set(path, value);
6782
+ });
6779
6783
  /**
6780
6784
  * Updates the conditions panel when linked field data changes
6781
6785
  */
@@ -6961,9 +6965,8 @@ class Field extends Component {
6961
6965
  /**
6962
6966
  * wrapper for Data.set
6963
6967
  */
6964
- set(...args) {
6965
- const [path, value] = args;
6966
- const data = super.set(path, value);
6968
+ set(path, value) {
6969
+ const data = this.setData(path, value);
6967
6970
  this.updatePreview();
6968
6971
  return data;
6969
6972
  }
@@ -7055,7 +7058,10 @@ class Field extends Component {
7055
7058
  const prevData = clone$1(this.data);
7056
7059
  const { action = {} } = Controls$2.get(prevData.config.controlId);
7057
7060
  prevData.id = `prev-${this.id}`;
7058
- prevData.action = action;
7061
+ prevData.action = Object.entries(action).reduce((acc, [key, value]) => {
7062
+ acc[key] = value.bind(this);
7063
+ return acc;
7064
+ }, {});
7059
7065
  if ((_a = this.data) == null ? void 0 : _a.config.editableContent) {
7060
7066
  prevData.attrs = { ...prevData.attrs, contenteditable: true };
7061
7067
  }
@@ -7267,1008 +7273,578 @@ class Control {
7267
7273
  return (((_b = localeTranslations[lookup]) == null ? void 0 : _b.call(localeTranslations)) ?? localeTranslations[lookup]) || mi18n.get(lookup, args);
7268
7274
  }
7269
7275
  }
7270
- const rowControl = {
7271
- config: {
7272
- label: "row"
7273
- },
7274
- meta: {
7275
- group: "layout",
7276
- icon: "rows",
7277
- id: "layout-row"
7278
- }
7279
- };
7280
- const columnControl = {
7281
- config: {
7282
- label: "column"
7276
+ const defaultOptions = Object.freeze({
7277
+ sortable: true,
7278
+ elementOrder: {},
7279
+ groupOrder: [],
7280
+ groups: [
7281
+ {
7282
+ id: "layout",
7283
+ label: "controls.groups.layout",
7284
+ elementOrder: ["row", "column"]
7285
+ },
7286
+ {
7287
+ id: "common",
7288
+ label: "controls.groups.form",
7289
+ elementOrder: ["button", "checkbox"]
7290
+ },
7291
+ {
7292
+ id: "html",
7293
+ label: "controls.groups.html",
7294
+ elementOrder: ["header", "block-text"]
7295
+ }
7296
+ ],
7297
+ disable: {
7298
+ groups: [],
7299
+ elements: [],
7300
+ formActions: []
7283
7301
  },
7284
- meta: {
7285
- group: "layout",
7286
- icon: "columns",
7287
- id: "layout-column"
7288
- }
7289
- };
7290
- const layoutControls = [rowControl, columnControl];
7291
- class HiddenControl extends Control {
7302
+ elements: [],
7303
+ container: null,
7304
+ panels: { displayType: "slider" }
7305
+ });
7306
+ let Controls$1 = class Controls {
7292
7307
  constructor() {
7293
- const hiddenInput = {
7294
- tag: "input",
7295
- attrs: {
7296
- type: "hidden",
7297
- value: ""
7298
- },
7299
- config: {
7300
- label: mi18n.get("hidden"),
7301
- hideLabel: true
7302
- },
7303
- meta: {
7304
- group: "common",
7305
- icon: "hidden",
7306
- id: "hidden"
7308
+ __publicField(this, "groupLabel", (key) => mi18n.get(key) || key || "");
7309
+ __publicField(this, "layoutTypes", {
7310
+ row: () => Stages2.active.addChild(),
7311
+ column: () => this.layoutTypes.row().addChild(),
7312
+ field: (controlData) => this.layoutTypes.column().addChild(controlData)
7313
+ });
7314
+ /**
7315
+ * Append an element to the stage
7316
+ * @param {String} id of elements
7317
+ */
7318
+ __publicField(this, "addElement", (id) => {
7319
+ const {
7320
+ meta: { group, id: metaId },
7321
+ ...elementData
7322
+ } = get(this.get(id), "controlData");
7323
+ set(elementData, "config.controlId", metaId);
7324
+ if (group === "layout") {
7325
+ return this.layoutTypes[metaId.replace("layout-", "")]();
7307
7326
  }
7308
- };
7309
- super(hiddenInput);
7310
- }
7311
- }
7312
- class NumberControl extends Control {
7313
- constructor() {
7314
- const numberInput = {
7315
- tag: "input",
7316
- attrs: {
7317
- type: "number",
7318
- required: false,
7319
- className: ""
7320
- },
7321
- config: {
7322
- label: mi18n.get("number")
7327
+ return this.layoutTypes.field(elementData);
7328
+ });
7329
+ __publicField(this, "applyOptions", async (controlOptions = {}) => {
7330
+ const { container, elements, groupOrder, ...options } = merge(defaultOptions, controlOptions);
7331
+ this.container = container;
7332
+ this.groupOrder = unique(groupOrder.concat(["common", "html", "layout"]));
7333
+ this.options = options;
7334
+ const [layoutControls, formControls, htmlControls] = await Promise.all([
7335
+ Promise.resolve().then(() => index$5),
7336
+ Promise.resolve().then(() => index$3),
7337
+ Promise.resolve().then(() => index$1)
7338
+ ]);
7339
+ const allControls = [layoutControls.default, formControls.default, htmlControls.default].flat();
7340
+ return Promise.all(this.registerControls([...allControls, ...elements]));
7341
+ });
7342
+ this.data = /* @__PURE__ */ new Map();
7343
+ this.buttonActions = {
7344
+ // this is used for keyboard navigation. when tabbing through controls it
7345
+ // will auto navigated between the groups
7346
+ focus: ({ target }) => {
7347
+ const group = target.closest(`.${CONTROL_GROUP_CLASSNAME}`);
7348
+ return group && this.panels.nav.refresh(indexOfNode(group));
7323
7349
  },
7324
- meta: {
7325
- group: "common",
7326
- icon: "hash",
7327
- id: "number"
7350
+ click: ({ target }) => {
7351
+ this.addElement(target.parentElement.id);
7328
7352
  }
7329
7353
  };
7330
- super(numberInput);
7331
7354
  }
7332
- }
7333
- class TextAreaControl extends Control {
7334
- constructor() {
7335
- const textAreaConfig = {
7336
- tag: "textarea",
7337
- config: {
7338
- label: mi18n.get("controls.form.textarea")
7339
- },
7340
- // This is the beginning of actions being supported for render
7341
- // editor field actions should be in config.action
7342
- // action: {
7343
- // mousedown: function(evt) {
7344
- // let {target} = evt;
7345
- // let startHeight = target.style.height;
7346
- // const onMouseup = evt => {
7347
- // let {target} = evt;
7348
- // let endHeight = target.style.height;
7349
- // if (startHeight !== endHeight) {
7350
- // //eslint-disable-next-line
7351
- // let fieldId = closest(target, '.stage-field').id;
7352
- // const field = d.fields.get(fieldId).instance;
7353
- // field.addAttribute('style', `height: ${endHeight}`);
7354
- // }
7355
- // target.removeEventListener('mouseup', onMouseup);
7356
- // };
7357
- // target.addEventListener('mouseup', onMouseup);
7358
- // }
7359
- // },
7360
- meta: {
7361
- group: "common",
7362
- icon: "textarea",
7363
- id: "textarea"
7364
- },
7365
- attrs: {
7366
- required: false
7367
- }
7368
- };
7369
- super(textAreaConfig);
7355
+ /**
7356
+ * Methods to be called on initialization
7357
+ * @param {Object} controlOptions
7358
+ */
7359
+ async init(controlOptions, sticky = false) {
7360
+ await this.applyOptions(controlOptions);
7361
+ this.buildDOM(sticky);
7362
+ return this;
7370
7363
  }
7371
- }
7372
- class TextControl extends Control {
7373
- constructor() {
7374
- const textInput = {
7375
- tag: "input",
7376
- attrs: {
7377
- required: false,
7378
- type: "text",
7379
- className: ""
7380
- },
7381
- config: {
7382
- label: mi18n.get("controls.form.input.text")
7383
- },
7384
- meta: {
7385
- group: "common",
7386
- icon: "text-input",
7387
- id: "text-input"
7364
+ /**
7365
+ * Generate control config for UI and bind actions
7366
+ * @return {Array} elementControls
7367
+ */
7368
+ registerControls(elements) {
7369
+ this.controls = [];
7370
+ return elements.map(async (Element) => {
7371
+ const isControl = typeof Element === "function";
7372
+ let control;
7373
+ if (isControl) {
7374
+ control = new Element();
7375
+ } else {
7376
+ control = new Control(Element);
7388
7377
  }
7389
- };
7390
- super(textInput);
7378
+ this.add(control);
7379
+ this.controls.push(control.dom);
7380
+ return control.promise();
7381
+ });
7391
7382
  }
7392
- }
7393
- class FileControl extends Control {
7394
- constructor() {
7395
- const fileInput = {
7396
- tag: "input",
7397
- attrs: {
7398
- type: "file",
7399
- required: false
7400
- },
7401
- config: {
7402
- label: mi18n.get("fileUpload")
7403
- },
7404
- meta: {
7405
- group: "common",
7406
- icon: "upload",
7407
- id: "upload"
7383
+ /**
7384
+ * Group elements into their respective control group
7385
+ * @return {Array} allGroups
7386
+ */
7387
+ groupElements() {
7388
+ let groups = this.options.groups.slice();
7389
+ let elements = this.controls.slice();
7390
+ let allGroups = [];
7391
+ const usedElementIds = [];
7392
+ groups = orderObjectsBy(groups, this.groupOrder, "id");
7393
+ groups = groups.filter((group) => match(group.id, this.options.disable.groups));
7394
+ allGroups = groups.map((group) => {
7395
+ const groupConfig = {
7396
+ tag: "ul",
7397
+ attrs: {
7398
+ className: CONTROL_GROUP_CLASSNAME,
7399
+ id: `${group.id}-${CONTROL_GROUP_CLASSNAME}`
7400
+ },
7401
+ config: {
7402
+ label: this.groupLabel(group.label)
7403
+ }
7404
+ };
7405
+ if (this.options.elementOrder[group.id]) {
7406
+ const userOrder = this.options.elementOrder[group.id];
7407
+ const newOrder = unique(userOrder.concat(group.elementOrder));
7408
+ group.elementOrder = newOrder;
7408
7409
  }
7409
- };
7410
- super(fileInput);
7410
+ elements = orderObjectsBy(elements, group.elementOrder, "meta.id");
7411
+ groupConfig.content = elements.filter((control) => {
7412
+ const { controlData: field } = this.get(control.id);
7413
+ const controlId = field.meta.id || "";
7414
+ const filters = [
7415
+ match(controlId, this.options.disable.elements),
7416
+ field.meta.group === group.id,
7417
+ !usedElementIds.includes(controlId)
7418
+ ];
7419
+ let shouldFilter = true;
7420
+ shouldFilter = filters.every((val) => val === true);
7421
+ if (shouldFilter) {
7422
+ usedElementIds.push(controlId);
7423
+ }
7424
+ return shouldFilter;
7425
+ });
7426
+ return groupConfig;
7427
+ });
7428
+ return allGroups;
7411
7429
  }
7412
- }
7413
- const generateOptionConfig = (type2, count = 3) => Array.from({ length: count }, (v, k) => k + 1).map((i) => {
7414
- const selectedKey = type2 === "checkbox" ? "checked" : "selected";
7415
- return {
7416
- label: mi18n.get("labelCount", {
7417
- label: toTitleCase(type2),
7418
- count: i
7419
- }),
7420
- value: `${type2}-${i}`,
7421
- [selectedKey]: !i
7422
- };
7423
- });
7424
- class SelectControl extends Control {
7425
- constructor() {
7426
- const selectConfig = {
7427
- tag: "select",
7428
- config: {
7429
- label: mi18n.get("controls.form.select")
7430
- },
7431
- attrs: {
7432
- required: false,
7433
- className: ""
7434
- },
7435
- meta: {
7436
- group: "common",
7437
- icon: "select",
7438
- id: "select"
7439
- },
7440
- options: generateOptionConfig("option")
7441
- };
7442
- super(selectConfig);
7443
- }
7444
- }
7445
- class CheckboxGroupControl extends Control {
7446
- constructor() {
7447
- const checkboxGroup = {
7448
- tag: "input",
7449
- attrs: {
7450
- type: "checkbox",
7451
- required: false
7452
- },
7453
- config: {
7454
- label: mi18n.get("controls.form.checkbox-group"),
7455
- disabledAttrs: ["type"]
7456
- },
7457
- meta: {
7458
- group: "common",
7459
- icon: "checkbox",
7460
- id: "checkbox"
7461
- },
7462
- options: generateOptionConfig("checkbox", 1)
7463
- };
7464
- super(checkboxGroup);
7430
+ add(control = /* @__PURE__ */ Object.create(null)) {
7431
+ const controlConfig = clone$1(control);
7432
+ this.data.set(controlConfig.id, controlConfig);
7433
+ if (controlConfig.controlData.meta.id) {
7434
+ this.data.set(controlConfig.controlData.meta.id, controlConfig.controlData);
7435
+ }
7436
+ return controlConfig;
7465
7437
  }
7466
- }
7467
- class RadioGroupControl extends Control {
7468
- constructor() {
7469
- const radioGroup = {
7470
- tag: "input",
7471
- attrs: {
7472
- type: "radio",
7473
- required: false
7474
- },
7475
- config: {
7476
- label: mi18n.get("controls.form.radio-group"),
7477
- disabledAttrs: ["type"]
7478
- },
7479
- meta: {
7480
- group: "common",
7481
- icon: "radio-group",
7482
- id: "radio"
7483
- },
7484
- options: generateOptionConfig("radio")
7485
- };
7486
- super(radioGroup);
7438
+ get(controlId) {
7439
+ return clone$1(this.data.get(controlId));
7487
7440
  }
7488
- }
7489
- class ButtonControl extends Control {
7490
- constructor() {
7491
- const buttonConfig = {
7492
- tag: "button",
7493
- attrs: {
7494
- className: [{ label: "grouped", value: "f-btn-group" }, { label: "ungrouped", value: "f-field-group" }]
7495
- },
7496
- config: {
7497
- label: mi18n.get("controls.form.button"),
7498
- hideLabel: true
7499
- },
7500
- meta: {
7501
- group: "common",
7502
- icon: "button",
7503
- id: "button"
7504
- },
7505
- options: [
7506
- {
7507
- label: mi18n.get("button"),
7508
- type: ["button", "submit", "reset"].map((buttonType) => ({
7509
- label: buttonType,
7510
- type: buttonType
7511
- })),
7512
- className: [
7513
- {
7514
- label: "default",
7515
- value: "",
7516
- selected: true
7517
- },
7518
- {
7519
- label: "primary",
7520
- value: "primary"
7521
- },
7522
- {
7523
- label: "danger",
7524
- value: "error"
7525
- },
7526
- {
7527
- label: "success",
7528
- value: "success"
7529
- },
7530
- {
7531
- label: "warning",
7532
- value: "warning"
7533
- }
7534
- ]
7441
+ /**
7442
+ * Generate the DOM config for form actions like settings, save and clear
7443
+ * @return {Object} form action buttons config
7444
+ */
7445
+ formActions() {
7446
+ if (this.options.disable.formActions === true) {
7447
+ return null;
7448
+ }
7449
+ const clearBtn = {
7450
+ ...dom.btnTemplate({ content: [dom.icon("bin"), mi18n.get("clear")], title: mi18n.get("clearAll") }),
7451
+ className: ["clear-form"],
7452
+ action: {
7453
+ click: (evt) => {
7454
+ if (Rows2.size) {
7455
+ events.confirmClearAll = new window.CustomEvent("confirmClearAll", {
7456
+ detail: {
7457
+ confirmationMessage: mi18n.get("confirmClearAll"),
7458
+ clearAllAction: () => {
7459
+ Stages2.clearAll().then(() => {
7460
+ const evtData = {
7461
+ src: evt.target
7462
+ };
7463
+ events.formeoCleared(evtData);
7464
+ });
7465
+ },
7466
+ btnCoords: dom.coords(evt.target)
7467
+ }
7468
+ });
7469
+ document.dispatchEvent(events.confirmClearAll);
7470
+ } else {
7471
+ window.alert(mi18n.get("cannotClearFields"));
7472
+ }
7535
7473
  }
7536
- ]
7537
- };
7538
- super(buttonConfig);
7539
- }
7540
- }
7541
- class DateControl extends Control {
7542
- constructor() {
7543
- const dateInput = {
7544
- tag: "input",
7545
- attrs: {
7546
- type: "date",
7547
- required: false,
7548
- className: ""
7549
- },
7550
- config: {
7551
- label: mi18n.get("controls.form.input.date")
7552
- },
7553
- meta: {
7554
- group: "common",
7555
- icon: "calendar",
7556
- id: "date-input"
7557
7474
  }
7558
7475
  };
7559
- super(dateInput);
7560
- }
7561
- }
7562
- const formControls = [
7563
- ButtonControl,
7564
- DateControl,
7565
- HiddenControl,
7566
- NumberControl,
7567
- TextAreaControl,
7568
- TextControl,
7569
- FileControl,
7570
- SelectControl,
7571
- CheckboxGroupControl,
7572
- RadioGroupControl
7573
- ];
7574
- const headerTags = Array.from(Array(5).keys()).slice(1).map((key) => `h${key}`);
7575
- const headerKey = "controls.html.header";
7576
- class HeaderControl extends Control {
7577
- constructor() {
7578
- const header = {
7579
- tag: headerTags[0],
7580
- attrs: {
7581
- tag: headerTags.map((tag, index2) => ({
7582
- label: tag.toUpperCase(),
7583
- value: tag,
7584
- selected: !index2
7585
- })),
7586
- className: ""
7587
- },
7588
- config: {
7589
- label: mi18n.get(headerKey),
7590
- hideLabel: true,
7591
- editableContent: true
7592
- },
7593
- meta: {
7594
- group: "html",
7595
- icon: "header",
7596
- id: "html.header"
7597
- },
7598
- content: mi18n.get(headerKey),
7476
+ const saveBtn = {
7477
+ ...dom.btnTemplate({ content: [dom.icon("floppy-disk"), mi18n.get("save")], title: mi18n.get("save") }),
7478
+ className: ["save-form"],
7599
7479
  action: {
7600
- // onRender: evt => {},
7601
- // click: evt => {},
7480
+ click: ({ target }) => {
7481
+ const { formData } = components;
7482
+ const saveEvt = {
7483
+ action: () => {
7484
+ },
7485
+ coords: dom.coords(target),
7486
+ message: "",
7487
+ button: target
7488
+ };
7489
+ actions.click.btn(saveEvt);
7490
+ return actions.save.form(formData);
7491
+ }
7602
7492
  }
7603
7493
  };
7604
- super(header);
7605
- }
7606
- /**
7607
- * class configuration
7608
- */
7609
- static get definition() {
7610
- return {
7611
- // i18n custom mappings (defaults to camelCase type)
7612
- i18n: {
7613
- "en-US": {
7614
- header: "Custom English Header"
7494
+ const formActions = {
7495
+ className: "form-actions f-btn-group",
7496
+ content: Object.entries({ clearBtn, saveBtn }).reduce((acc, [key, value]) => {
7497
+ if (!this.options.disable.formActions.includes(key)) {
7498
+ acc.push(value);
7615
7499
  }
7616
- }
7500
+ return acc;
7501
+ }, [])
7617
7502
  };
7503
+ return formActions;
7618
7504
  }
7619
- get content() {
7620
- return super.i18n(headerKey);
7621
- }
7622
- }
7623
- class ParagraphControl extends Control {
7624
- constructor() {
7625
- const paragraphConfig = {
7626
- tag: "p",
7627
- attrs: {
7628
- className: ""
7629
- },
7630
- config: {
7631
- label: mi18n.get("controls.html.paragraph"),
7632
- hideLabel: true,
7633
- editableContent: true
7634
- },
7635
- meta: {
7636
- group: "html",
7637
- icon: "paragraph",
7638
- id: "paragraph"
7505
+ /**
7506
+ * Returns the markup for the form controls/fields
7507
+ * @return {DOM}
7508
+ */
7509
+ buildDOM(sticky) {
7510
+ const groupedFields = this.groupElements();
7511
+ const formActions = this.formActions();
7512
+ const { displayType } = this.options.panels;
7513
+ this.panels = new Panels({ panels: groupedFields, type: "controls", displayType });
7514
+ const groupsWrapClasses = ["control-groups", "formeo-panels-wrap", `panel-count-${groupedFields.length}`];
7515
+ const groupsWrap = dom.create({
7516
+ className: groupsWrapClasses,
7517
+ content: [this.panels.panelNav, this.panels.panelsWrap]
7518
+ });
7519
+ const controlClasses = ["formeo-controls"];
7520
+ if (sticky) {
7521
+ controlClasses.push("formeo-sticky");
7522
+ }
7523
+ const element = dom.create({
7524
+ className: controlClasses,
7525
+ content: [groupsWrap, formActions]
7526
+ });
7527
+ const groups = element.getElementsByClassName("control-group");
7528
+ this.dom = element;
7529
+ this.groups = groups;
7530
+ const [firstGroup] = groups;
7531
+ this.currentGroup = firstGroup;
7532
+ this.actions = {
7533
+ filter: (term) => {
7534
+ const filtering = term !== "";
7535
+ const fields2 = this.controls;
7536
+ let filteredTerm = groupsWrap.querySelector(".filtered-term");
7537
+ dom.toggleElementsByStr(fields2, term);
7538
+ if (filtering) {
7539
+ const filteredStr = mi18n.get("controls.filteringTerm", term);
7540
+ element.classList.add("filtered");
7541
+ if (filteredTerm) {
7542
+ filteredTerm.textContent = filteredStr;
7543
+ } else {
7544
+ filteredTerm = dom.create({
7545
+ tag: "h5",
7546
+ className: "filtered-term",
7547
+ content: filteredStr
7548
+ });
7549
+ groupsWrap.insertBefore(filteredTerm, groupsWrap.firstChild);
7550
+ }
7551
+ } else if (filteredTerm) {
7552
+ element.classList.remove("filtered");
7553
+ filteredTerm.remove();
7554
+ }
7639
7555
  },
7640
- // eslint-disable-next-line
7641
- content: "Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment."
7556
+ addElement: this.addElement,
7557
+ // @todo finish the addGroup method
7558
+ addGroup: (group) => console.log(group)
7642
7559
  };
7643
- super(paragraphConfig);
7644
- }
7645
- }
7646
- class HRControl extends Control {
7647
- constructor() {
7648
- const hrConfig = {
7649
- tag: "hr",
7650
- config: {
7651
- label: mi18n.get("controls.html.divider"),
7652
- hideLabel: true
7653
- },
7654
- meta: {
7655
- group: "html",
7656
- icon: "divider",
7657
- id: "divider"
7560
+ for (let i = groups.length - 1; i >= 0; i--) {
7561
+ const storeID = `formeo-controls-${groups[i]}`;
7562
+ if (!this.options.sortable) {
7563
+ window.localStorage.removeItem(storeID);
7658
7564
  }
7659
- };
7660
- super(hrConfig);
7661
- }
7662
- }
7663
- class TinyMCEControl extends Control {
7664
- constructor(options) {
7665
- const textAreaConfig = {
7666
- tag: "textarea",
7667
- config: {
7668
- label: "WYSIWYG",
7669
- editableContent: true
7670
- },
7671
- meta: {
7672
- group: "html",
7673
- icon: "rich-text",
7674
- id: "tinymce"
7675
- },
7676
- attrs: {
7677
- required: false
7678
- },
7679
- dependencies: { js: "https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js" },
7680
- // this action is passed to the rendered control/element
7681
- // useful for actions and events on the control preview
7682
- action: {
7683
- onRender: (elem) => {
7684
- const selector = `#${elem.id}`;
7685
- window.tinymce.remove(selector);
7686
- window.tinymce.init({
7687
- selector
7688
- });
7689
- }
7690
- },
7691
- controlAction: {
7692
- // callback when control is clicked
7693
- click: () => {
7565
+ Sortable.create(groups[i], {
7566
+ animation: 150,
7567
+ forceFallback: true,
7568
+ fallbackClass: "control-moving",
7569
+ fallbackOnBody: true,
7570
+ group: {
7571
+ name: "controls",
7572
+ pull: "clone",
7573
+ put: false
7694
7574
  },
7695
- // callback for when control is rendered
7696
- onRender: () => {
7575
+ onStart: ({ item }) => {
7576
+ const { controlData } = this.get(item.id);
7577
+ if (this.options.ghostPreview) {
7578
+ item.innerHTML = "";
7579
+ item.appendChild(new Field(controlData).preview);
7580
+ }
7581
+ },
7582
+ onEnd: ({ from, item, clone: clone2 }) => {
7583
+ if (from.contains(clone2)) {
7584
+ from.replaceChild(item, clone2);
7585
+ }
7586
+ },
7587
+ sort: this.options.sortable,
7588
+ store: {
7589
+ /**
7590
+ * Get the order of elements.
7591
+ * @param {Sortable} sortable
7592
+ * @return {Array}
7593
+ */
7594
+ get: () => {
7595
+ const order = window.localStorage.getItem(storeID);
7596
+ return order ? order.split("|") : [];
7597
+ },
7598
+ /**
7599
+ * Save the order of elements.
7600
+ * @param {Sortable} sortable
7601
+ */
7602
+ set: (sortable) => {
7603
+ const order = sortable.toArray();
7604
+ window.localStorage.setItem(storeID, order.join("|"));
7605
+ }
7697
7606
  }
7698
- }
7699
- };
7700
- const mergedOptions = merge(textAreaConfig, options);
7701
- super(mergedOptions);
7702
- }
7703
- }
7704
- const htmlControls = [HeaderControl, ParagraphControl, HRControl, TinyMCEControl];
7705
- const defaultOptions = Object.freeze({
7706
- sortable: true,
7707
- elementOrder: {},
7708
- groupOrder: [],
7709
- groups: [
7710
- {
7711
- id: "layout",
7712
- label: "controls.groups.layout",
7713
- elementOrder: ["row", "column"]
7714
- },
7715
- {
7716
- id: "common",
7717
- label: "controls.groups.form",
7718
- elementOrder: ["button", "checkbox"]
7719
- },
7720
- {
7721
- id: "html",
7722
- label: "controls.groups.html",
7723
- elementOrder: ["header", "block-text"]
7607
+ });
7724
7608
  }
7725
- ],
7726
- disable: {
7727
- groups: [],
7728
- elements: [],
7729
- formActions: []
7730
- },
7731
- elements: [],
7732
- container: null,
7733
- panels: { displayType: "slider" }
7734
- });
7735
- const defaultElements = [...formControls, ...htmlControls, ...layoutControls];
7736
- let Controls$1 = class Controls {
7609
+ return element;
7610
+ }
7611
+ };
7612
+ const Controls$2 = new Controls$1();
7613
+ class ComponentData extends Data {
7737
7614
  constructor() {
7738
- __publicField(this, "groupLabel", (key) => mi18n.get(key) || key || "");
7739
- __publicField(this, "layoutTypes", {
7740
- row: () => Stages2.active.addChild(),
7741
- column: () => this.layoutTypes.row().addChild(),
7742
- field: (controlData) => this.layoutTypes.column().addChild(controlData)
7615
+ super(...arguments);
7616
+ __publicField(this, "load", (dataArg) => {
7617
+ const data = parseData(dataArg);
7618
+ this.empty();
7619
+ for (const [key, val] of Object.entries(data)) {
7620
+ this.add(key, val);
7621
+ }
7622
+ return this.data;
7743
7623
  });
7744
7624
  /**
7745
- * Append an element to the stage
7746
- * @param {String} id of elements
7625
+ * Retrieves data from the specified path or adds new data if no path is provided.
7626
+ *
7627
+ * @param {string} [path] - The path to retrieve data from. If not provided, new data will be added.
7628
+ * @returns {*} The data retrieved from the specified path or the result of adding new data.
7747
7629
  */
7748
- __publicField(this, "addElement", (id) => {
7749
- const {
7750
- meta: { group, id: metaId },
7751
- ...elementData
7752
- } = get(this.get(id), "controlData");
7753
- set(elementData, "config.controlId", metaId);
7754
- if (group === "layout") {
7755
- return this.layoutTypes[metaId.replace("layout-", "")]();
7756
- }
7757
- return this.layoutTypes.field(elementData);
7630
+ __publicField(this, "get", (path) => path ? get(this.data, path) : this.add());
7631
+ /**
7632
+ * Adds a new component with the given id and data.
7633
+ *
7634
+ * @param {string} id - The unique identifier for the component. If not provided, a new UUID will be generated.
7635
+ * @param {Object} [data=Object.create(null)] - The data to initialize the component with.
7636
+ * @returns {Object} The newly created component.
7637
+ */
7638
+ __publicField(this, "add", (id, data = /* @__PURE__ */ Object.create(null)) => {
7639
+ const elemId = id || uuid();
7640
+ const component = this.Component({ ...data, id: elemId });
7641
+ this.set(elemId, component);
7642
+ this.active = component;
7643
+ return component;
7758
7644
  });
7759
- __publicField(this, "applyOptions", async (controlOptions = {}) => {
7760
- const { container, elements, groupOrder, ...options } = merge(defaultOptions, controlOptions);
7761
- this.container = container;
7762
- this.groupOrder = unique(groupOrder.concat(["common", "html", "layout"]));
7763
- this.options = options;
7764
- return Promise.all(this.registerControls([...defaultElements, ...elements]));
7765
- });
7766
- this.data = /* @__PURE__ */ new Map();
7767
- this.buttonActions = {
7768
- // this is used for keyboard navigation. when tabbing through controls it
7769
- // will auto navigated between the groups
7770
- focus: ({ target }) => {
7771
- const group = target.closest(`.${CONTROL_GROUP_CLASSNAME}`);
7772
- return group && this.panels.nav.refresh(indexOfNode(group));
7773
- },
7774
- click: ({ target }) => {
7775
- this.addElement(target.parentElement.id);
7645
+ /**
7646
+ * removes a component form the index
7647
+ * @param {String|Array} componentId
7648
+ */
7649
+ __publicField(this, "remove", (componentId) => {
7650
+ if (Array.isArray(componentId)) {
7651
+ for (const id of componentId) {
7652
+ this.get(id).remove();
7653
+ }
7654
+ } else {
7655
+ this.get(componentId).remove();
7776
7656
  }
7777
- };
7657
+ return this.data;
7658
+ });
7659
+ /**
7660
+ * Deletes a component from the data object.
7661
+ *
7662
+ * @param {string} componentId - The ID of the component to delete.
7663
+ * @returns {string} The ID of the deleted component.
7664
+ */
7665
+ __publicField(this, "delete", (componentId) => {
7666
+ delete this.data[componentId];
7667
+ return componentId;
7668
+ });
7669
+ /**
7670
+ * Clears all instances from the store
7671
+ * @param {Object} evt
7672
+ */
7673
+ __publicField(this, "clearAll", (isAnimated = true) => {
7674
+ const promises = Object.values(this.data).map((component) => component.empty(isAnimated));
7675
+ return Promise.all(promises);
7676
+ });
7677
+ __publicField(this, "conditionMap", /* @__PURE__ */ new Map());
7778
7678
  }
7779
7679
  /**
7780
- * Methods to be called on initialization
7781
- * @param {Object} controlOptions
7680
+ * Extends the configVal for a component type,
7681
+ * eventually read by Component
7682
+ * @return {Object} configVal
7782
7683
  */
7783
- async init(controlOptions, sticky = false) {
7784
- await this.applyOptions(controlOptions);
7785
- this.buildDOM(sticky);
7786
- return this;
7684
+ set config(config2) {
7685
+ this.configVal = merge(this.configVal, clone$1(config2));
7787
7686
  }
7788
7687
  /**
7789
- * Generate control config for UI and bind actions
7790
- * @return {Array} elementControls
7688
+ * Reads configVal for a component type
7689
+ * @return {Object} configVal
7791
7690
  */
7792
- registerControls(elements) {
7793
- this.controls = [];
7794
- return elements.map(async (Element) => {
7795
- const isControl = typeof Element === "function";
7796
- let control;
7797
- if (isControl) {
7798
- control = new Element();
7799
- } else {
7800
- control = new Control(Element);
7801
- }
7802
- this.add(control);
7803
- this.controls.push(control.dom);
7804
- return control.promise();
7805
- });
7691
+ get config() {
7692
+ return this.configVal;
7806
7693
  }
7694
+ }
7695
+ const DEFAULT_DATA$2 = () => Object.freeze({ children: [] });
7696
+ class Stage extends Component {
7807
7697
  /**
7808
- * Group elements into their respective control group
7809
- * @return {Array} allGroups
7698
+ * Process options and load existing fields from data to the stage
7699
+ * @param {Object} formeoOptions
7700
+ * @param {String} stageData uuid
7701
+ * @return {Object} DOM element
7810
7702
  */
7811
- groupElements() {
7812
- let groups = this.options.groups.slice();
7813
- let elements = this.controls.slice();
7814
- let allGroups = [];
7815
- const usedElementIds = [];
7816
- groups = orderObjectsBy(groups, this.groupOrder, "id");
7817
- groups = groups.filter((group) => match(group.id, this.options.disable.groups));
7818
- allGroups = groups.map((group) => {
7819
- const groupConfig = {
7820
- tag: "ul",
7821
- attrs: {
7822
- className: CONTROL_GROUP_CLASSNAME,
7823
- id: `${group.id}-${CONTROL_GROUP_CLASSNAME}`
7824
- },
7825
- config: {
7826
- label: this.groupLabel(group.label)
7827
- }
7828
- };
7829
- if (this.options.elementOrder[group.id]) {
7830
- const userOrder = this.options.elementOrder[group.id];
7831
- const newOrder = unique(userOrder.concat(group.elementOrder));
7832
- group.elementOrder = newOrder;
7703
+ constructor(stageData, render) {
7704
+ super("stage", { ...DEFAULT_DATA$2(), ...stageData }, render);
7705
+ const children = this.createChildWrap();
7706
+ this.dom = dom.create({
7707
+ attrs: {
7708
+ className: [STAGE_CLASSNAME, "empty"],
7709
+ id: this.id
7710
+ },
7711
+ children
7712
+ });
7713
+ Sortable.create(children, {
7714
+ animation: 150,
7715
+ fallbackClass: "row-moving",
7716
+ forceFallback: true,
7717
+ group: {
7718
+ name: "stage",
7719
+ pull: true,
7720
+ put: ["row", "column", "controls"]
7721
+ },
7722
+ sort: true,
7723
+ disabled: false,
7724
+ onAdd: this.onAdd.bind(this),
7725
+ onRemove: this.onRemove.bind(this),
7726
+ onStart: () => {
7727
+ stages.active = this;
7728
+ },
7729
+ onSort: this.onSort.bind(this),
7730
+ draggable: `.${ROW_CLASSNAME}`,
7731
+ handle: ".item-move"
7732
+ });
7733
+ }
7734
+ empty(isAnimated = true) {
7735
+ return new Promise((resolve) => {
7736
+ if (isAnimated) {
7737
+ this.dom.classList.add("removing-all-fields");
7738
+ animate.slideUp(this.dom, ANIMATION_SPEED_BASE, () => {
7739
+ resolve(super.empty(isAnimated));
7740
+ this.dom.classList.remove("removing-all-fields");
7741
+ animate.slideDown(this.dom, ANIMATION_SPEED_BASE);
7742
+ });
7743
+ } else {
7744
+ resolve(super.empty());
7833
7745
  }
7834
- elements = orderObjectsBy(elements, group.elementOrder, "meta.id");
7835
- groupConfig.content = elements.filter((control) => {
7836
- const { controlData: field } = this.get(control.id);
7837
- const controlId = field.meta.id || "";
7838
- const filters = [
7839
- match(controlId, this.options.disable.elements),
7840
- field.meta.group === group.id,
7841
- !usedElementIds.includes(controlId)
7842
- ];
7843
- let shouldFilter = true;
7844
- shouldFilter = filters.every((val) => val === true);
7845
- if (shouldFilter) {
7846
- usedElementIds.push(controlId);
7847
- }
7848
- return shouldFilter;
7849
- });
7850
- return groupConfig;
7851
7746
  });
7852
- return allGroups;
7853
7747
  }
7854
- add(control = /* @__PURE__ */ Object.create(null)) {
7855
- const controlConfig = clone$1(control);
7856
- this.data.set(controlConfig.id, controlConfig);
7857
- if (controlConfig.controlData.meta.id) {
7858
- this.data.set(controlConfig.controlData.meta.id, controlConfig.controlData);
7748
+ onAdd(...args) {
7749
+ const component = super.onAdd(...args);
7750
+ if (component && component.name === "column") {
7751
+ component.parent.autoColumnWidths();
7859
7752
  }
7860
- return controlConfig;
7861
7753
  }
7862
- get(controlId) {
7863
- return clone$1(this.data.get(controlId));
7754
+ }
7755
+ let Stages$1 = class Stages extends ComponentData {
7756
+ constructor(stageData) {
7757
+ super("stages", stageData);
7758
+ }
7759
+ Component(data) {
7760
+ return new Stage(data);
7864
7761
  }
7762
+ };
7763
+ const stages = new Stages$1();
7764
+ const DEFAULT_DATA$1 = () => Object.freeze({
7765
+ config: {
7766
+ fieldset: false,
7767
+ // wrap contents of row in fieldset
7768
+ legend: "",
7769
+ // Legend for fieldset
7770
+ inputGroup: false
7771
+ // is repeatable input-group?
7772
+ },
7773
+ children: [],
7774
+ className: [ROW_CLASSNAME]
7775
+ });
7776
+ class Row extends Component {
7865
7777
  /**
7866
- * Generate the DOM config for form actions like settings, save and clear
7867
- * @return {Object} form action buttons config
7778
+ * Set default and generate dom for row in editor
7779
+ * @param {String} dataID
7780
+ * @return {Object}
7868
7781
  */
7869
- formActions() {
7870
- if (this.options.disable.formActions === true) {
7871
- return null;
7872
- }
7873
- const clearBtn = {
7874
- ...dom.btnTemplate({ content: [dom.icon("bin"), mi18n.get("clear")], title: mi18n.get("clearAll") }),
7875
- className: ["clear-form"],
7876
- action: {
7877
- click: (evt) => {
7878
- if (Rows2.size) {
7879
- events.confirmClearAll = new window.CustomEvent("confirmClearAll", {
7880
- detail: {
7881
- confirmationMessage: mi18n.get("confirmClearAll"),
7882
- clearAllAction: () => {
7883
- Stages2.clearAll().then(() => {
7884
- const evtData = {
7885
- src: evt.target
7886
- };
7887
- events.formeoCleared(evtData);
7888
- });
7889
- },
7890
- btnCoords: dom.coords(evt.target)
7891
- }
7892
- });
7893
- document.dispatchEvent(events.confirmClearAll);
7894
- } else {
7895
- window.alert(mi18n.get("cannotClearFields"));
7896
- }
7897
- }
7782
+ constructor(rowData) {
7783
+ super("row", { ...DEFAULT_DATA$1(), ...rowData });
7784
+ /**
7785
+ * Read columns and generate bootstrap cols
7786
+ * @param {Object} row DOM element
7787
+ */
7788
+ __publicField(this, "autoColumnWidths", () => {
7789
+ const columns2 = this.children;
7790
+ if (!columns2.length) {
7791
+ return;
7898
7792
  }
7899
- };
7900
- const saveBtn = {
7901
- ...dom.btnTemplate({ content: [dom.icon("floppy-disk"), mi18n.get("save")], title: mi18n.get("save") }),
7902
- className: ["save-form"],
7903
- action: {
7904
- click: ({ target }) => {
7905
- const { formData } = components;
7906
- const saveEvt = {
7907
- action: () => {
7908
- },
7909
- coords: dom.coords(target),
7910
- message: "",
7911
- button: target
7912
- };
7913
- actions.click.btn(saveEvt);
7914
- return actions.save.form(formData);
7915
- }
7916
- }
7917
- };
7918
- const formActions = {
7919
- className: "form-actions f-btn-group",
7920
- content: Object.entries({ clearBtn, saveBtn }).reduce((acc, [key, value]) => {
7921
- if (!this.options.disable.formActions.includes(key)) {
7922
- acc.push(value);
7923
- }
7924
- return acc;
7925
- }, [])
7926
- };
7927
- return formActions;
7928
- }
7929
- /**
7930
- * Returns the markup for the form controls/fields
7931
- * @return {DOM}
7932
- */
7933
- buildDOM(sticky) {
7934
- const groupedFields = this.groupElements();
7935
- const formActions = this.formActions();
7936
- const { displayType } = this.options.panels;
7937
- this.panels = new Panels({ panels: groupedFields, type: "controls", displayType });
7938
- const groupsWrapClasses = ["control-groups", "formeo-panels-wrap", `panel-count-${groupedFields.length}`];
7939
- const groupsWrap = dom.create({
7940
- className: groupsWrapClasses,
7941
- content: [this.panels.panelNav, this.panels.panelsWrap]
7942
- });
7943
- const controlClasses = ["formeo-controls"];
7944
- if (sticky) {
7945
- controlClasses.push("formeo-sticky");
7946
- }
7947
- const element = dom.create({
7948
- className: controlClasses,
7949
- content: [groupsWrap, formActions]
7950
- });
7951
- const groups = element.getElementsByClassName("control-group");
7952
- this.dom = element;
7953
- this.groups = groups;
7954
- const [firstGroup] = groups;
7955
- this.currentGroup = firstGroup;
7956
- this.actions = {
7957
- filter: (term) => {
7958
- const filtering = term !== "";
7959
- const fields2 = this.controls;
7960
- let filteredTerm = groupsWrap.querySelector(".filtered-term");
7961
- dom.toggleElementsByStr(fields2, term);
7962
- if (filtering) {
7963
- const filteredStr = mi18n.get("controls.filteringTerm", term);
7964
- element.classList.add("filtered");
7965
- if (filteredTerm) {
7966
- filteredTerm.textContent = filteredStr;
7967
- } else {
7968
- filteredTerm = dom.create({
7969
- tag: "h5",
7970
- className: "filtered-term",
7971
- content: filteredStr
7972
- });
7973
- groupsWrap.insertBefore(filteredTerm, groupsWrap.firstChild);
7974
- }
7975
- } else if (filteredTerm) {
7976
- element.classList.remove("filtered");
7977
- filteredTerm.remove();
7978
- }
7979
- },
7980
- addElement: this.addElement,
7981
- // @todo finish the addGroup method
7982
- addGroup: (group) => console.log(group)
7983
- };
7984
- for (let i = groups.length - 1; i >= 0; i--) {
7985
- const storeID = `formeo-controls-${groups[i]}`;
7986
- if (!this.options.sortable) {
7987
- window.localStorage.removeItem(storeID);
7988
- }
7989
- Sortable.create(groups[i], {
7990
- animation: 150,
7991
- forceFallback: true,
7992
- fallbackClass: "control-moving",
7993
- fallbackOnBody: true,
7994
- group: {
7995
- name: "controls",
7996
- pull: "clone",
7997
- put: false
7998
- },
7999
- onStart: ({ item }) => {
8000
- const { controlData } = this.get(item.id);
8001
- if (this.options.ghostPreview) {
8002
- item.innerHTML = "";
8003
- item.appendChild(new Field(controlData).preview);
8004
- }
8005
- },
8006
- onEnd: ({ from, item, clone: clone2 }) => {
8007
- if (from.contains(clone2)) {
8008
- from.replaceChild(item, clone2);
8009
- }
8010
- },
8011
- sort: this.options.sortable,
8012
- store: {
8013
- /**
8014
- * Get the order of elements.
8015
- * @param {Sortable} sortable
8016
- * @return {Array}
8017
- */
8018
- get: () => {
8019
- const order = window.localStorage.getItem(storeID);
8020
- return order ? order.split("|") : [];
8021
- },
8022
- /**
8023
- * Save the order of elements.
8024
- * @param {Sortable} sortable
8025
- */
8026
- set: (sortable) => {
8027
- const order = sortable.toArray();
8028
- window.localStorage.setItem(storeID, order.join("|"));
8029
- }
8030
- }
8031
- });
8032
- }
8033
- return element;
8034
- }
8035
- };
8036
- const Controls$2 = new Controls$1();
8037
- class ComponentData extends Data {
8038
- constructor() {
8039
- super(...arguments);
8040
- __publicField(this, "load", (dataArg) => {
8041
- const data = parseData(dataArg);
8042
- this.empty();
8043
- for (const [key, val] of Object.entries(data)) {
8044
- this.add(key, val);
7793
+ const width = Number.parseFloat((100 / columns2.length).toFixed(1)) / 1;
7794
+ for (const column of columns2) {
7795
+ column.removeClasses(bsColRegExp);
7796
+ const colDom = column.dom;
7797
+ const newColWidth = numToPercent(width);
7798
+ column.set("config.width", newColWidth);
7799
+ colDom.style.width = newColWidth;
7800
+ colDom.dataset.colWidth = newColWidth;
7801
+ const refreshTimeout = setTimeout(() => {
7802
+ clearTimeout(refreshTimeout);
7803
+ column.refreshFieldPanels();
7804
+ }, ANIMATION_SPEED_FAST);
7805
+ document.dispatchEvent(events.columnResized);
8045
7806
  }
8046
- return this.data;
7807
+ this.updateColumnPreset();
8047
7808
  });
8048
7809
  /**
8049
- * Retrieves data from the specified path or adds new data if no path is provided.
8050
- *
8051
- * @param {string} [path] - The path to retrieve data from. If not provided, new data will be added.
8052
- * @returns {*} The data retrieved from the specified path or the result of adding new data.
8053
- */
8054
- __publicField(this, "get", (path) => path ? get(this.data, path) : this.add());
8055
- /**
8056
- * Adds a new component with the given id and data.
8057
- *
8058
- * @param {string} id - The unique identifier for the component. If not provided, a new UUID will be generated.
8059
- * @param {Object} [data=Object.create(null)] - The data to initialize the component with.
8060
- * @returns {Object} The newly created component.
7810
+ * Updates the column preset <select>
7811
+ * @return {Object} columnPresetConfig
8061
7812
  */
8062
- __publicField(this, "add", (id, data = /* @__PURE__ */ Object.create(null)) => {
8063
- const elemId = id || uuid();
8064
- const component = this.Component({ ...data, id: elemId });
8065
- this.set(elemId, component);
8066
- this.active = component;
8067
- return component;
7813
+ __publicField(this, "updateColumnPreset", () => {
7814
+ this.columnPresetControl.innerHTML = "";
7815
+ const presetOptions = this.getColumnPresetOptions.map(
7816
+ ({ label, ...attrs }) => dom.create({
7817
+ tag: "option",
7818
+ content: label,
7819
+ attrs
7820
+ })
7821
+ );
7822
+ this.columnPresetControl.append(...presetOptions);
8068
7823
  });
8069
7824
  /**
8070
- * removes a component form the index
8071
- * @param {String|Array} componentId
7825
+ * Set the widths of columns in a row
7826
+ * @param {Object} row DOM element
7827
+ * @param {String} widths
8072
7828
  */
8073
- __publicField(this, "remove", (componentId) => {
8074
- if (Array.isArray(componentId)) {
8075
- for (const id of componentId) {
8076
- this.get(id).remove();
8077
- }
8078
- } else {
8079
- this.get(componentId).remove();
7829
+ __publicField(this, "setColumnWidths", (widths) => {
7830
+ if (typeof widths === "string") {
7831
+ widths = widths.split(",");
8080
7832
  }
8081
- return this.data;
8082
- });
8083
- /**
8084
- * Deletes a component from the data object.
8085
- *
8086
- * @param {string} componentId - The ID of the component to delete.
8087
- * @returns {string} The ID of the deleted component.
8088
- */
8089
- __publicField(this, "delete", (componentId) => {
8090
- delete this.data[componentId];
8091
- return componentId;
8092
- });
8093
- /**
8094
- * Clears all instances from the store
8095
- * @param {Object} evt
8096
- */
8097
- __publicField(this, "clearAll", (isAnimated = true) => {
8098
- const promises = Object.values(this.data).map((component) => component.empty(isAnimated));
8099
- return Promise.all(promises);
7833
+ this.children.forEach((column, i) => {
7834
+ column.setWidth(`${widths[i]}%`);
7835
+ column.refreshFieldPanels();
7836
+ });
8100
7837
  });
8101
- __publicField(this, "conditionMap", /* @__PURE__ */ new Map());
8102
- }
8103
- /**
8104
- * Extends the configVal for a component type,
8105
- * eventually read by Component
8106
- * @return {Object} configVal
8107
- */
8108
- set config(config2) {
8109
- this.configVal = merge(this.configVal, clone$1(config2));
8110
- }
8111
- /**
8112
- * Reads configVal for a component type
8113
- * @return {Object} configVal
8114
- */
8115
- get config() {
8116
- return this.configVal;
8117
- }
8118
- }
8119
- const DEFAULT_DATA$2 = () => Object.freeze({ children: [] });
8120
- class Stage extends Component {
8121
- /**
8122
- * Process options and load existing fields from data to the stage
8123
- * @param {Object} formeoOptions
8124
- * @param {String} stageData uuid
8125
- * @return {Object} DOM element
8126
- */
8127
- constructor(stageData, render) {
8128
- super("stage", { ...DEFAULT_DATA$2(), ...stageData }, render);
8129
7838
  const children = this.createChildWrap();
8130
7839
  this.dom = dom.create({
8131
- attrs: {
8132
- className: [STAGE_CLASSNAME, "empty"],
8133
- id: this.id
7840
+ tag: "li",
7841
+ className: [ROW_CLASSNAME, "empty"],
7842
+ dataset: {
7843
+ hoverTag: mi18n.get("row"),
7844
+ editingHoverTag: mi18n.get("editing.row")
8134
7845
  },
8135
- children
8136
- });
8137
- Sortable.create(children, {
8138
- animation: 150,
8139
- fallbackClass: "row-moving",
8140
- forceFallback: true,
8141
- group: {
8142
- name: "stage",
8143
- pull: true,
8144
- put: ["row", "column", "controls"]
8145
- },
8146
- sort: true,
8147
- disabled: false,
8148
- onAdd: this.onAdd.bind(this),
8149
- onRemove: this.onRemove.bind(this),
8150
- onStart: () => {
8151
- stages.active = this;
8152
- },
8153
- onSort: this.onSort.bind(this),
8154
- draggable: `.${ROW_CLASSNAME}`,
8155
- handle: ".item-move"
8156
- });
8157
- }
8158
- empty(isAnimated = true) {
8159
- return new Promise((resolve) => {
8160
- if (isAnimated) {
8161
- this.dom.classList.add("removing-all-fields");
8162
- animate.slideUp(this.dom, ANIMATION_SPEED_BASE, () => {
8163
- resolve(super.empty(isAnimated));
8164
- this.dom.classList.remove("removing-all-fields");
8165
- animate.slideDown(this.dom, ANIMATION_SPEED_BASE);
8166
- });
8167
- } else {
8168
- resolve(super.empty());
8169
- }
8170
- });
8171
- }
8172
- onAdd(...args) {
8173
- const component = super.onAdd(...args);
8174
- if (component && component.name === "column") {
8175
- component.parent.autoColumnWidths();
8176
- }
8177
- }
8178
- }
8179
- let Stages$1 = class Stages extends ComponentData {
8180
- constructor(stageData) {
8181
- super("stages", stageData);
8182
- }
8183
- Component(data) {
8184
- return new Stage(data);
8185
- }
8186
- };
8187
- const stages = new Stages$1();
8188
- const DEFAULT_DATA$1 = () => Object.freeze({
8189
- config: {
8190
- fieldset: false,
8191
- // wrap contents of row in fieldset
8192
- legend: "",
8193
- // Legend for fieldset
8194
- inputGroup: false
8195
- // is repeatable input-group?
8196
- },
8197
- children: [],
8198
- className: [ROW_CLASSNAME]
8199
- });
8200
- class Row extends Component {
8201
- /**
8202
- * Set default and generate dom for row in editor
8203
- * @param {String} dataID
8204
- * @return {Object}
8205
- */
8206
- constructor(rowData) {
8207
- super("row", { ...DEFAULT_DATA$1(), ...rowData });
8208
- /**
8209
- * Read columns and generate bootstrap cols
8210
- * @param {Object} row DOM element
8211
- */
8212
- __publicField(this, "autoColumnWidths", () => {
8213
- const columns2 = this.children;
8214
- if (!columns2.length) {
8215
- return;
8216
- }
8217
- const width = Number.parseFloat((100 / columns2.length).toFixed(1)) / 1;
8218
- for (const column of columns2) {
8219
- column.removeClasses(bsColRegExp);
8220
- const colDom = column.dom;
8221
- const newColWidth = numToPercent(width);
8222
- column.set("config.width", newColWidth);
8223
- colDom.style.width = newColWidth;
8224
- colDom.dataset.colWidth = newColWidth;
8225
- const refreshTimeout = setTimeout(() => {
8226
- clearTimeout(refreshTimeout);
8227
- column.refreshFieldPanels();
8228
- }, ANIMATION_SPEED_FAST);
8229
- document.dispatchEvent(events.columnResized);
8230
- }
8231
- this.updateColumnPreset();
8232
- });
8233
- /**
8234
- * Updates the column preset <select>
8235
- * @return {Object} columnPresetConfig
8236
- */
8237
- __publicField(this, "updateColumnPreset", () => {
8238
- this.columnPresetControl.innerHTML = "";
8239
- const presetOptions = this.getColumnPresetOptions.map(
8240
- ({ label, ...attrs }) => dom.create({
8241
- tag: "option",
8242
- content: label,
8243
- attrs
8244
- })
8245
- );
8246
- this.columnPresetControl.append(...presetOptions);
8247
- });
8248
- /**
8249
- * Set the widths of columns in a row
8250
- * @param {Object} row DOM element
8251
- * @param {String} widths
8252
- */
8253
- __publicField(this, "setColumnWidths", (widths) => {
8254
- if (typeof widths === "string") {
8255
- widths = widths.split(",");
8256
- }
8257
- this.children.forEach((column, i) => {
8258
- column.setWidth(`${widths[i]}%`);
8259
- column.refreshFieldPanels();
8260
- });
8261
- });
8262
- const children = this.createChildWrap();
8263
- this.dom = dom.create({
8264
- tag: "li",
8265
- className: [ROW_CLASSNAME, "empty"],
8266
- dataset: {
8267
- hoverTag: mi18n.get("row"),
8268
- editingHoverTag: mi18n.get("editing.row")
8269
- },
8270
- id: this.id,
8271
- content: [this.getComponentTag(), this.getActionButtons(), this.editWindow, children]
7846
+ id: this.id,
7847
+ content: [this.getComponentTag(), this.getActionButtons(), this.editWindow, children]
8272
7848
  });
8273
7849
  Sortable.create(children, {
8274
7850
  animation: 150,
@@ -9713,7 +9289,7 @@ let FormeoEditor$1 = class FormeoEditor {
9713
9289
  this.opts = opts;
9714
9290
  dom.setOptions = opts;
9715
9291
  components.config = config2;
9716
- this.userFormData = cleanFormData(userFormData || formData);
9292
+ this.userFormData = userFormData || formData;
9717
9293
  this.Components = components;
9718
9294
  this.dom = dom;
9719
9295
  events.init({ debug, ...events$1 });
@@ -9729,7 +9305,8 @@ let FormeoEditor$1 = class FormeoEditor {
9729
9305
  return this.Components.formData;
9730
9306
  }
9731
9307
  set formData(data = {}) {
9732
- this.load({ ...this.userFormData, ...data }, this.opts);
9308
+ this.userFormData = cleanFormData(data);
9309
+ this.load(this.userFormData, this.opts);
9733
9310
  }
9734
9311
  get json() {
9735
9312
  return this.Components.json;
@@ -9775,12 +9352,12 @@ let FormeoEditor$1 = class FormeoEditor {
9775
9352
  }, console.error);
9776
9353
  }
9777
9354
  };
9778
- this.render();
9779
9355
  (_b = (_a = this.opts).onLoad) == null ? void 0 : _b.call(_a, this);
9780
9356
  });
9781
9357
  }
9782
9358
  load(formData = this.userFormData, opts = this.opts) {
9783
- return this.Components.load(formData, opts);
9359
+ this.Components.load(formData, opts);
9360
+ this.render();
9784
9361
  }
9785
9362
  /**
9786
9363
  * Render the formeo sections
@@ -9943,164 +9520,598 @@ let FormeoRenderer$1 = class FormeoRenderer {
9943
9520
  className: "add-input-group btn pull-right",
9944
9521
  type: "button"
9945
9522
  },
9946
- children: "Add +",
9523
+ children: "Add +",
9524
+ action: {
9525
+ click: (e) => {
9526
+ const fInputGroup = e.target.parentElement;
9527
+ const elem = dom.render(this.cloneComponentData(id));
9528
+ fInputGroup.insertBefore(elem, fInputGroup.lastChild);
9529
+ elem.appendChild(createRemoveButton());
9530
+ }
9531
+ }
9532
+ }));
9533
+ __publicField(this, "processColumns", (rowId) => {
9534
+ return this.orderChildren("columns", this.form.rows[rowId].children).map(
9535
+ (column) => this.cacheComponent(this.processColumn(column))
9536
+ );
9537
+ });
9538
+ __publicField(this, "processFields", (fieldIds) => this.orderChildren("fields", fieldIds).map(({ id, ...field }) => {
9539
+ var _a, _b;
9540
+ const controlId = ((_a = field.config) == null ? void 0 : _a.controlId) || ((_b = field.meta) == null ? void 0 : _b.id);
9541
+ const { action = {}, dependencies: dependencies2 = {} } = this.elements[controlId] || {};
9542
+ if (dependencies2) {
9543
+ fetchDependencies(dependencies2);
9544
+ }
9545
+ const mergedFieldData = merge({ action }, field);
9546
+ return this.cacheComponent({ ...mergedFieldData, id: this.prefixId(id) });
9547
+ }));
9548
+ /**
9549
+ * Evaulate and execute conditions for fields by creating listeners for input and changes
9550
+ * @return {Array} flattened array of conditions
9551
+ */
9552
+ __publicField(this, "handleComponentCondition", (component, ifRest, thenConditions) => {
9553
+ const listenerEvent = LISTEN_TYPE_MAP(component);
9554
+ if (listenerEvent) {
9555
+ component.addEventListener(
9556
+ listenerEvent,
9557
+ (evt) => {
9558
+ if (this.evaluateCondition(ifRest, evt)) {
9559
+ for (const thenCondition of thenConditions) {
9560
+ this.execResult(thenCondition, evt);
9561
+ }
9562
+ }
9563
+ },
9564
+ false
9565
+ );
9566
+ }
9567
+ const fakeEvt = { target: component };
9568
+ if (this.evaluateCondition(ifRest, fakeEvt)) {
9569
+ for (const thenCondition of thenConditions) {
9570
+ this.execResult(thenCondition, fakeEvt);
9571
+ }
9572
+ }
9573
+ });
9574
+ __publicField(this, "applyConditions", () => {
9575
+ for (const { conditions } of Object.values(this.components)) {
9576
+ if (conditions) {
9577
+ for (const condition of conditions) {
9578
+ const { if: ifConditions, then: thenConditions } = condition;
9579
+ for (const ifCondition of ifConditions) {
9580
+ const { source, ...ifRest } = ifCondition;
9581
+ if (isAddress(source)) {
9582
+ const components2 = this.getComponents(source);
9583
+ for (const component of components2) {
9584
+ this.handleComponentCondition(component, ifRest, thenConditions);
9585
+ }
9586
+ }
9587
+ }
9588
+ }
9589
+ }
9590
+ }
9591
+ });
9592
+ /**
9593
+ * Evaulate conditions
9594
+ */
9595
+ __publicField(this, "evaluateCondition", ({ sourceProperty, targetProperty, comparison, target }, evt) => {
9596
+ var _a;
9597
+ const comparisonMap = {
9598
+ equals: isEqual$1,
9599
+ notEquals: (source, target2) => !isEqual$1(source, target2),
9600
+ contains: (source, target2) => source.includes(target2),
9601
+ notContains: (source, target2) => !source.includes(target2)
9602
+ };
9603
+ const sourceValue = String(evt.target[sourceProperty]);
9604
+ const targetValue = String(isAddress(target) ? this.getComponent(target)[targetProperty] : target);
9605
+ return (_a = comparisonMap[comparison]) == null ? void 0 : _a.call(comparisonMap, sourceValue, targetValue);
9606
+ });
9607
+ __publicField(this, "execResult", ({ assignment, target, targetProperty, value }) => {
9608
+ var _a;
9609
+ const assignMap = {
9610
+ equals: (elem) => {
9611
+ var _a2;
9612
+ const propMap = {
9613
+ value: () => {
9614
+ elem[targetProperty] = value;
9615
+ },
9616
+ isNotVisible: () => {
9617
+ elem.parentElement.setAttribute("hidden", true);
9618
+ elem.required = false;
9619
+ },
9620
+ isVisible: () => {
9621
+ elem.parentElement.removeAttribute("hidden");
9622
+ elem.required = elem._required;
9623
+ }
9624
+ };
9625
+ (_a2 = propMap[targetProperty]) == null ? void 0 : _a2.call(propMap);
9626
+ }
9627
+ };
9628
+ if (isAddress(target)) {
9629
+ const elem = this.getComponent(target);
9630
+ if (elem && elem._required === void 0) {
9631
+ elem._required = elem.required;
9632
+ }
9633
+ (_a = assignMap[assignment]) == null ? void 0 : _a.call(assignMap, elem);
9634
+ }
9635
+ });
9636
+ __publicField(this, "getComponent", (address) => {
9637
+ const componentId = address.slice(address.indexOf(".") + 1);
9638
+ const component = isExternalAddress(address) ? this.external[componentId] : this.renderedForm.querySelector(`#f-${componentId}`);
9639
+ return component;
9640
+ });
9641
+ __publicField(this, "getComponents", (address) => {
9642
+ const components2 = [];
9643
+ const componentId = address.slice(address.indexOf(".") + 1);
9644
+ if (isExternalAddress(address)) {
9645
+ components2.push(this.external[componentId]);
9646
+ } else {
9647
+ components2.push(...this.renderedForm.querySelectorAll(`[name=f-${componentId}]`));
9648
+ }
9649
+ return components2;
9650
+ });
9651
+ const { renderContainer, external, elements, formData } = processOptions(opts);
9652
+ this.container = renderContainer;
9653
+ this.form = cleanFormData(formDataArg || formData);
9654
+ this.external = external;
9655
+ this.dom = dom;
9656
+ this.components = /* @__PURE__ */ Object.create(null);
9657
+ this.elements = elements;
9658
+ }
9659
+ get processedData() {
9660
+ return Object.values(this.form.stages).map((stage) => {
9661
+ stage.children = this.processRows(stage.id);
9662
+ stage.className = STAGE_CLASSNAME;
9663
+ return dom.render(stage);
9664
+ });
9665
+ }
9666
+ };
9667
+ const LISTEN_TYPE_MAP = (component) => {
9668
+ const typesMap = [
9669
+ ["input", (c) => ["textarea", "text"].includes(c.type)],
9670
+ ["change", (c) => ["select"].includes(c.tagName.toLowerCase()) || ["checkbox", "radio"].includes(c.type)]
9671
+ ];
9672
+ const [listenerEvent] = typesMap.find((typeMap) => typeMap[1](component)) || [false];
9673
+ return listenerEvent;
9674
+ };
9675
+ if (window !== void 0) {
9676
+ window.FormeoEditor = FormeoEditor$1;
9677
+ window.FormeoRenderer = FormeoRenderer$1;
9678
+ }
9679
+ const FormeoEditor2 = FormeoEditor$1;
9680
+ const FormeoRenderer2 = FormeoRenderer$1;
9681
+ const rowControl = {
9682
+ config: {
9683
+ label: "row"
9684
+ },
9685
+ meta: {
9686
+ group: "layout",
9687
+ icon: "rows",
9688
+ id: "layout-row"
9689
+ }
9690
+ };
9691
+ const columnControl = {
9692
+ config: {
9693
+ label: "column"
9694
+ },
9695
+ meta: {
9696
+ group: "layout",
9697
+ icon: "columns",
9698
+ id: "layout-column"
9699
+ }
9700
+ };
9701
+ const index$4 = [rowControl, columnControl];
9702
+ const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
9703
+ __proto__: null,
9704
+ default: index$4
9705
+ }, Symbol.toStringTag, { value: "Module" }));
9706
+ class HiddenControl extends Control {
9707
+ constructor() {
9708
+ const hiddenInput = {
9709
+ tag: "input",
9710
+ attrs: {
9711
+ type: "hidden",
9712
+ value: ""
9713
+ },
9714
+ config: {
9715
+ label: mi18n.get("hidden"),
9716
+ hideLabel: true
9717
+ },
9718
+ meta: {
9719
+ group: "common",
9720
+ icon: "hidden",
9721
+ id: "hidden"
9722
+ }
9723
+ };
9724
+ super(hiddenInput);
9725
+ }
9726
+ }
9727
+ class NumberControl extends Control {
9728
+ constructor() {
9729
+ const numberInput = {
9730
+ tag: "input",
9731
+ attrs: {
9732
+ type: "number",
9733
+ required: false,
9734
+ className: ""
9735
+ },
9736
+ config: {
9737
+ label: mi18n.get("number")
9738
+ },
9739
+ meta: {
9740
+ group: "common",
9741
+ icon: "hash",
9742
+ id: "number"
9743
+ }
9744
+ };
9745
+ super(numberInput);
9746
+ }
9747
+ }
9748
+ class TextAreaControl extends Control {
9749
+ constructor() {
9750
+ const textAreaConfig = {
9751
+ tag: "textarea",
9752
+ config: {
9753
+ label: mi18n.get("controls.form.textarea")
9754
+ },
9755
+ // actions here will be applied to the preview in the editor
9756
+ action: {
9757
+ input: function({ target: { value } }) {
9758
+ var _a;
9759
+ (_a = this.setData) == null ? void 0 : _a.call(this, "value", value);
9760
+ }
9761
+ },
9762
+ meta: {
9763
+ group: "common",
9764
+ icon: "textarea",
9765
+ id: "textarea"
9766
+ },
9767
+ attrs: {
9768
+ required: false
9769
+ }
9770
+ };
9771
+ super(textAreaConfig);
9772
+ }
9773
+ }
9774
+ class TextControl extends Control {
9775
+ constructor() {
9776
+ const textInput = {
9777
+ tag: "input",
9778
+ attrs: {
9779
+ required: false,
9780
+ type: "text",
9781
+ className: ""
9782
+ },
9783
+ config: {
9784
+ label: mi18n.get("controls.form.input.text")
9785
+ },
9786
+ meta: {
9787
+ group: "common",
9788
+ icon: "text-input",
9789
+ id: "text-input"
9790
+ }
9791
+ };
9792
+ super(textInput);
9793
+ }
9794
+ }
9795
+ class FileControl extends Control {
9796
+ constructor() {
9797
+ const fileInput = {
9798
+ tag: "input",
9799
+ attrs: {
9800
+ type: "file",
9801
+ required: false
9802
+ },
9803
+ config: {
9804
+ label: mi18n.get("fileUpload")
9805
+ },
9806
+ meta: {
9807
+ group: "common",
9808
+ icon: "upload",
9809
+ id: "upload"
9810
+ }
9811
+ };
9812
+ super(fileInput);
9813
+ }
9814
+ }
9815
+ const generateOptionConfig = (type2, count = 3) => Array.from({ length: count }, (v, k) => k + 1).map((i) => {
9816
+ const selectedKey = type2 === "checkbox" ? "checked" : "selected";
9817
+ return {
9818
+ label: mi18n.get("labelCount", {
9819
+ label: toTitleCase(type2),
9820
+ count: i
9821
+ }),
9822
+ value: `${type2}-${i}`,
9823
+ [selectedKey]: !i
9824
+ };
9825
+ });
9826
+ class SelectControl extends Control {
9827
+ constructor() {
9828
+ const selectConfig = {
9829
+ tag: "select",
9830
+ config: {
9831
+ label: mi18n.get("controls.form.select")
9832
+ },
9833
+ attrs: {
9834
+ required: false,
9835
+ className: ""
9836
+ },
9837
+ meta: {
9838
+ group: "common",
9839
+ icon: "select",
9840
+ id: "select"
9841
+ },
9842
+ options: generateOptionConfig("option")
9843
+ };
9844
+ super(selectConfig);
9845
+ }
9846
+ }
9847
+ class CheckboxGroupControl extends Control {
9848
+ constructor() {
9849
+ const checkboxGroup = {
9850
+ tag: "input",
9851
+ attrs: {
9852
+ type: "checkbox",
9853
+ required: false
9854
+ },
9855
+ config: {
9856
+ label: mi18n.get("controls.form.checkbox-group"),
9857
+ disabledAttrs: ["type"]
9858
+ },
9859
+ meta: {
9860
+ group: "common",
9861
+ icon: "checkbox",
9862
+ id: "checkbox"
9863
+ },
9864
+ options: generateOptionConfig("checkbox", 1)
9865
+ };
9866
+ super(checkboxGroup);
9867
+ }
9868
+ }
9869
+ class RadioGroupControl extends Control {
9870
+ constructor() {
9871
+ const radioGroup = {
9872
+ tag: "input",
9873
+ attrs: {
9874
+ type: "radio",
9875
+ required: false
9876
+ },
9877
+ config: {
9878
+ label: mi18n.get("controls.form.radio-group"),
9879
+ disabledAttrs: ["type"]
9880
+ },
9881
+ meta: {
9882
+ group: "common",
9883
+ icon: "radio-group",
9884
+ id: "radio"
9885
+ },
9886
+ options: generateOptionConfig("radio")
9887
+ };
9888
+ super(radioGroup);
9889
+ }
9890
+ }
9891
+ class ButtonControl extends Control {
9892
+ constructor() {
9893
+ const buttonConfig = {
9894
+ tag: "button",
9895
+ attrs: {
9896
+ className: [{ label: "grouped", value: "f-btn-group" }, { label: "ungrouped", value: "f-field-group" }]
9897
+ },
9898
+ config: {
9899
+ label: mi18n.get("controls.form.button"),
9900
+ hideLabel: true
9901
+ },
9902
+ meta: {
9903
+ group: "common",
9904
+ icon: "button",
9905
+ id: "button"
9906
+ },
9907
+ options: [
9908
+ {
9909
+ label: mi18n.get("button"),
9910
+ type: ["button", "submit", "reset"].map((buttonType) => ({
9911
+ label: buttonType,
9912
+ type: buttonType
9913
+ })),
9914
+ className: [
9915
+ {
9916
+ label: "default",
9917
+ value: "",
9918
+ selected: true
9919
+ },
9920
+ {
9921
+ label: "primary",
9922
+ value: "primary"
9923
+ },
9924
+ {
9925
+ label: "danger",
9926
+ value: "error"
9927
+ },
9928
+ {
9929
+ label: "success",
9930
+ value: "success"
9931
+ },
9932
+ {
9933
+ label: "warning",
9934
+ value: "warning"
9935
+ }
9936
+ ]
9937
+ }
9938
+ ]
9939
+ };
9940
+ super(buttonConfig);
9941
+ }
9942
+ }
9943
+ class DateControl extends Control {
9944
+ constructor() {
9945
+ const dateInput = {
9946
+ tag: "input",
9947
+ attrs: {
9948
+ type: "date",
9949
+ required: false,
9950
+ className: ""
9951
+ },
9952
+ config: {
9953
+ label: mi18n.get("controls.form.input.date")
9954
+ },
9955
+ meta: {
9956
+ group: "common",
9957
+ icon: "calendar",
9958
+ id: "date-input"
9959
+ }
9960
+ };
9961
+ super(dateInput);
9962
+ }
9963
+ }
9964
+ const index$2 = [
9965
+ ButtonControl,
9966
+ DateControl,
9967
+ HiddenControl,
9968
+ NumberControl,
9969
+ TextAreaControl,
9970
+ TextControl,
9971
+ FileControl,
9972
+ SelectControl,
9973
+ CheckboxGroupControl,
9974
+ RadioGroupControl
9975
+ ];
9976
+ const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
9977
+ __proto__: null,
9978
+ default: index$2
9979
+ }, Symbol.toStringTag, { value: "Module" }));
9980
+ const headerTags = Array.from(Array(5).keys()).slice(1).map((key) => `h${key}`);
9981
+ const headerKey = "controls.html.header";
9982
+ class HeaderControl extends Control {
9983
+ constructor() {
9984
+ const header = {
9985
+ tag: headerTags[0],
9986
+ attrs: {
9987
+ tag: headerTags.map((tag, index2) => ({
9988
+ label: tag.toUpperCase(),
9989
+ value: tag,
9990
+ selected: !index2
9991
+ })),
9992
+ className: ""
9993
+ },
9994
+ config: {
9995
+ label: mi18n.get(headerKey),
9996
+ hideLabel: true,
9997
+ editableContent: true
9998
+ },
9999
+ meta: {
10000
+ group: "html",
10001
+ icon: "header",
10002
+ id: "html.header"
10003
+ },
10004
+ content: mi18n.get(headerKey),
9947
10005
  action: {
9948
- click: (e) => {
9949
- const fInputGroup = e.target.parentElement;
9950
- const elem = dom.render(this.cloneComponentData(id));
9951
- fInputGroup.insertBefore(elem, fInputGroup.lastChild);
9952
- elem.appendChild(createRemoveButton());
9953
- }
9954
- }
9955
- }));
9956
- __publicField(this, "processColumns", (rowId) => {
9957
- return this.orderChildren("columns", this.form.rows[rowId].children).map(
9958
- (column) => this.cacheComponent(this.processColumn(column))
9959
- );
9960
- });
9961
- __publicField(this, "processFields", (fieldIds) => this.orderChildren("fields", fieldIds).map(({ id, ...field }) => {
9962
- var _a, _b;
9963
- const controlId = ((_a = field.config) == null ? void 0 : _a.controlId) || ((_b = field.meta) == null ? void 0 : _b.id);
9964
- const { action = {}, dependencies: dependencies2 = {} } = this.elements[controlId] || {};
9965
- if (dependencies2) {
9966
- fetchDependencies(dependencies2);
9967
- }
9968
- const mergedFieldData = merge({ action }, field);
9969
- return this.cacheComponent({ ...mergedFieldData, id: this.prefixId(id) });
9970
- }));
9971
- /**
9972
- * Evaulate and execute conditions for fields by creating listeners for input and changes
9973
- * @return {Array} flattened array of conditions
9974
- */
9975
- __publicField(this, "handleComponentCondition", (component, ifRest, thenConditions) => {
9976
- const listenerEvent = LISTEN_TYPE_MAP(component);
9977
- if (listenerEvent) {
9978
- component.addEventListener(
9979
- listenerEvent,
9980
- (evt) => {
9981
- if (this.evaluateCondition(ifRest, evt)) {
9982
- for (const thenCondition of thenConditions) {
9983
- this.execResult(thenCondition, evt);
9984
- }
9985
- }
9986
- },
9987
- false
9988
- );
10006
+ // onRender: evt => {},
10007
+ // click: evt => {},
9989
10008
  }
9990
- const fakeEvt = { target: component };
9991
- if (this.evaluateCondition(ifRest, fakeEvt)) {
9992
- for (const thenCondition of thenConditions) {
9993
- this.execResult(thenCondition, fakeEvt);
10009
+ };
10010
+ super(header);
10011
+ }
10012
+ /**
10013
+ * class configuration
10014
+ */
10015
+ static get definition() {
10016
+ return {
10017
+ // i18n custom mappings (defaults to camelCase type)
10018
+ i18n: {
10019
+ "en-US": {
10020
+ header: "Custom English Header"
9994
10021
  }
9995
10022
  }
9996
- });
9997
- __publicField(this, "applyConditions", () => {
9998
- for (const { conditions } of Object.values(this.components)) {
9999
- if (conditions) {
10000
- for (const condition of conditions) {
10001
- const { if: ifConditions, then: thenConditions } = condition;
10002
- for (const ifCondition of ifConditions) {
10003
- const { source, ...ifRest } = ifCondition;
10004
- if (isAddress(source)) {
10005
- const components2 = this.getComponents(source);
10006
- for (const component of components2) {
10007
- this.handleComponentCondition(component, ifRest, thenConditions);
10008
- }
10009
- }
10010
- }
10011
- }
10012
- }
10023
+ };
10024
+ }
10025
+ get content() {
10026
+ return super.i18n(headerKey);
10027
+ }
10028
+ }
10029
+ class ParagraphControl extends Control {
10030
+ constructor() {
10031
+ const paragraphConfig = {
10032
+ tag: "p",
10033
+ attrs: {
10034
+ className: ""
10035
+ },
10036
+ config: {
10037
+ label: mi18n.get("controls.html.paragraph"),
10038
+ hideLabel: true,
10039
+ editableContent: true
10040
+ },
10041
+ meta: {
10042
+ group: "html",
10043
+ icon: "paragraph",
10044
+ id: "paragraph"
10045
+ },
10046
+ // eslint-disable-next-line
10047
+ content: "Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment."
10048
+ };
10049
+ super(paragraphConfig);
10050
+ }
10051
+ }
10052
+ class HRControl extends Control {
10053
+ constructor() {
10054
+ const hrConfig = {
10055
+ tag: "hr",
10056
+ config: {
10057
+ label: mi18n.get("controls.html.divider"),
10058
+ hideLabel: true
10059
+ },
10060
+ meta: {
10061
+ group: "html",
10062
+ icon: "divider",
10063
+ id: "divider"
10013
10064
  }
10014
- });
10015
- /**
10016
- * Evaulate conditions
10017
- */
10018
- __publicField(this, "evaluateCondition", ({ sourceProperty, targetProperty, comparison, target }, evt) => {
10019
- var _a;
10020
- const comparisonMap = {
10021
- equals: isEqual$1,
10022
- notEquals: (source, target2) => !isEqual$1(source, target2),
10023
- contains: (source, target2) => source.includes(target2),
10024
- notContains: (source, target2) => !source.includes(target2)
10025
- };
10026
- const sourceValue = String(evt.target[sourceProperty]);
10027
- const targetValue = String(isAddress(target) ? this.getComponent(target)[targetProperty] : target);
10028
- return (_a = comparisonMap[comparison]) == null ? void 0 : _a.call(comparisonMap, sourceValue, targetValue);
10029
- });
10030
- __publicField(this, "execResult", ({ assignment, target, targetProperty, value }) => {
10031
- var _a;
10032
- const assignMap = {
10033
- equals: (elem) => {
10034
- var _a2;
10035
- const propMap = {
10036
- value: () => {
10037
- elem[targetProperty] = value;
10038
- },
10039
- isNotVisible: () => {
10040
- elem.parentElement.setAttribute("hidden", true);
10041
- elem.required = false;
10042
- },
10043
- isVisible: () => {
10044
- elem.parentElement.removeAttribute("hidden");
10045
- elem.required = elem._required;
10046
- }
10047
- };
10048
- (_a2 = propMap[targetProperty]) == null ? void 0 : _a2.call(propMap);
10065
+ };
10066
+ super(hrConfig);
10067
+ }
10068
+ }
10069
+ class TinyMCEControl extends Control {
10070
+ constructor(options) {
10071
+ const textAreaConfig = {
10072
+ tag: "textarea",
10073
+ config: {
10074
+ label: "WYSIWYG",
10075
+ editableContent: true
10076
+ },
10077
+ meta: {
10078
+ group: "html",
10079
+ icon: "rich-text",
10080
+ id: "tinymce"
10081
+ },
10082
+ attrs: {
10083
+ required: false
10084
+ },
10085
+ dependencies: { js: "https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js" },
10086
+ // this action is passed to the rendered control/element
10087
+ // useful for actions and events on the control preview
10088
+ action: {
10089
+ onRender: (elem) => {
10090
+ const selector = `#${elem.id}`;
10091
+ window.tinymce.remove(selector);
10092
+ window.tinymce.init({
10093
+ selector
10094
+ });
10049
10095
  }
10050
- };
10051
- if (isAddress(target)) {
10052
- const elem = this.getComponent(target);
10053
- if (elem && elem._required === void 0) {
10054
- elem._required = elem.required;
10096
+ },
10097
+ controlAction: {
10098
+ // callback when control is clicked
10099
+ click: () => {
10100
+ },
10101
+ // callback for when control is rendered
10102
+ onRender: () => {
10055
10103
  }
10056
- (_a = assignMap[assignment]) == null ? void 0 : _a.call(assignMap, elem);
10057
- }
10058
- });
10059
- __publicField(this, "getComponent", (address) => {
10060
- const componentId = address.slice(address.indexOf(".") + 1);
10061
- const component = isExternalAddress(address) ? this.external[componentId] : this.renderedForm.querySelector(`#f-${componentId}`);
10062
- return component;
10063
- });
10064
- __publicField(this, "getComponents", (address) => {
10065
- const components2 = [];
10066
- const componentId = address.slice(address.indexOf(".") + 1);
10067
- if (isExternalAddress(address)) {
10068
- components2.push(this.external[componentId]);
10069
- } else {
10070
- components2.push(...this.renderedForm.querySelectorAll(`[name=f-${componentId}]`));
10071
10104
  }
10072
- return components2;
10073
- });
10074
- const { renderContainer, external, elements, formData } = processOptions(opts);
10075
- this.container = renderContainer;
10076
- this.form = cleanFormData(formDataArg || formData);
10077
- this.external = external;
10078
- this.dom = dom;
10079
- this.components = /* @__PURE__ */ Object.create(null);
10080
- this.elements = elements;
10081
- }
10082
- get processedData() {
10083
- return Object.values(this.form.stages).map((stage) => {
10084
- stage.children = this.processRows(stage.id);
10085
- stage.className = STAGE_CLASSNAME;
10086
- return dom.render(stage);
10087
- });
10105
+ };
10106
+ const mergedOptions = merge(textAreaConfig, options);
10107
+ super(mergedOptions);
10088
10108
  }
10089
- };
10090
- const LISTEN_TYPE_MAP = (component) => {
10091
- const typesMap = [
10092
- ["input", (c) => ["textarea", "text"].includes(c.type)],
10093
- ["change", (c) => ["select"].includes(c.tagName.toLowerCase()) || ["checkbox", "radio"].includes(c.type)]
10094
- ];
10095
- const [listenerEvent] = typesMap.find((typeMap) => typeMap[1](component)) || [false];
10096
- return listenerEvent;
10097
- };
10098
- if (window !== void 0) {
10099
- window.FormeoEditor = FormeoEditor$1;
10100
- window.FormeoRenderer = FormeoRenderer$1;
10101
10109
  }
10102
- const FormeoEditor2 = FormeoEditor$1;
10103
- const FormeoRenderer2 = FormeoRenderer$1;
10110
+ const index = [HeaderControl, ParagraphControl, HRControl, TinyMCEControl];
10111
+ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
10112
+ __proto__: null,
10113
+ default: index
10114
+ }, Symbol.toStringTag, { value: "Module" }));
10104
10115
  export {
10105
10116
  FormeoEditor2 as FormeoEditor,
10106
10117
  FormeoRenderer2 as FormeoRenderer