@saltcorn/builder 1.6.0-alpha.9 → 1.6.0-beta.2

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.
@@ -6,9 +6,10 @@
6
6
 
7
7
  import { useNode, useEditor } from "@craftjs/core";
8
8
  //import { ROOT_NODE } from "@craftjs/utils";
9
- import React, { useEffect, useRef, useCallback, Fragment } from "react";
9
+ import React, { useEffect, useRef, useCallback, Fragment, useContext } from "react";
10
10
  import ReactDOM from "react-dom";
11
11
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
12
+ import optionsCtx from "./context";
12
13
  import {
13
14
  faCopy,
14
15
  faUndo,
@@ -32,6 +33,7 @@ export /**
32
33
  */
33
34
  const RenderNode = ({ render }) => {
34
35
  const { id } = useNode();
36
+ const options = useContext(optionsCtx);
35
37
  const { actions, query, isActive } = useEditor((state) => ({
36
38
  isActive: state.nodes[id].events.selected,
37
39
  }));
@@ -47,7 +49,7 @@ const RenderNode = ({ render }) => {
47
49
  } = useNode((node) => ({
48
50
  isHover: node.events.hovered,
49
51
  dom: node.dom,
50
- name: node.data.custom.displayName || node.data.displayName,
52
+ name: node.data.custom.displayName || node.data.props?.custom?.displayName || node.data.displayName,
51
53
  moveable: query.node(node.id).isDraggable(),
52
54
  deletable: query.node(node.id).isDeletable(),
53
55
  parent: node.data.parent,
@@ -60,14 +62,16 @@ const RenderNode = ({ render }) => {
60
62
  const { top, left, bottom, height, width, right } = dom
61
63
  ? dom.getBoundingClientRect()
62
64
  : { top: 0, left: 0, bottom: 0, right: 0, height: 0, width: 0 };
65
+ const rightPos = window.innerWidth - right;
63
66
  return {
64
67
  top: `${top > 0 ? top : bottom}px`,
65
68
  left: `${left}px`,
69
+ right: `${rightPos}px`,
66
70
  topn: top,
67
71
  leftn: left,
72
+ rightn: rightPos,
68
73
  height,
69
74
  width,
70
- right,
71
75
  bottom,
72
76
  };
73
77
  }, []);
@@ -75,14 +79,38 @@ const RenderNode = ({ render }) => {
75
79
  const scroll = useCallback(() => {
76
80
  const { current: currentDOM } = currentRef;
77
81
  if (!currentDOM) return;
78
- const { top, left } = getPos(dom);
79
- currentDOM.style.top = top;
80
- currentDOM.style.left = left;
81
- }, [dom, getPos]);
82
+ const pos = getPos(dom);
83
+ currentDOM.style.top = pos.top;
84
+ if (options.isRTL) {
85
+ currentDOM.style.right = pos.right;
86
+ currentDOM.style.left = 'auto';
87
+ } else {
88
+ currentDOM.style.left = pos.left;
89
+ currentDOM.style.right = 'auto';
90
+ }
91
+ }, [dom, getPos, options.isRTL]);
82
92
 
93
+ const hiddenColumnParents = new Set(["Card", "Container", "Table", "DropMenu"]);
83
94
  useEffect(() => {
84
- if (name === "Column" && parent && parent !== "ROOT")
85
- actions.selectNode(parent);
95
+ if (!isActive) return;
96
+ if (name === "Column" && parent && parent !== "ROOT") {
97
+ const parentNode = query.node(parent).get();
98
+ const parentName = parentNode?.data?.displayName;
99
+ const parentLinked = parentNode?.data?.linkedNodes;
100
+ if (
101
+ hiddenColumnParents.has(parentName) &&
102
+ parentLinked &&
103
+ Object.values(parentLinked).includes(id)
104
+ ) {
105
+ const currentlySelected = query.getEvent("selected").all();
106
+ const otherSelected = currentlySelected.filter((nid) => nid !== id);
107
+ if (otherSelected.length > 0) {
108
+ actions.selectNode([...otherSelected, parent]);
109
+ } else {
110
+ actions.selectNode(parent);
111
+ }
112
+ }
113
+ }
86
114
  }, [isActive]);
87
115
 
88
116
  useEffect(() => {
@@ -127,7 +155,7 @@ const RenderNode = ({ render }) => {
127
155
  isActive ? "activeind" : "hoverind"
128
156
  } px-1 text-white`}
129
157
  style={{
130
- left: getPos(dom).left,
158
+ ...(options.isRTL ? { right: getPos(dom).right, left: 'auto' } : { left: getPos(dom).left, right: 'auto' }),
131
159
  top: getPos(dom).top,
132
160
  zIndex: 1029,
133
161
  }}
@@ -29,6 +29,7 @@ import { View } from "./elements/View";
29
29
  import { SearchBar } from "./elements/SearchBar";
30
30
  import { Link } from "./elements/Link";
31
31
  import { Page } from "./elements/Page";
32
+ import { Prompt } from "./elements/Prompt";
32
33
  import optionsCtx from "./context";
33
34
  import {
34
35
  BoundingBox,
@@ -84,6 +85,7 @@ const WrapElem = ({
84
85
  } d-inline-flex wrap-builder-elem align-items-center justify-content-center`}
85
86
  title={title}
86
87
  ref={disable ? undefined : (ref) => connectors.create(ref, children)}
88
+ onContextMenu={(e) => e.preventDefault()}
87
89
  >
88
90
  <div
89
91
  className={`inner ${innerClass || ""}`}
@@ -115,7 +117,7 @@ const TextElem = ({ connectors }) => {
115
117
  fontSize="22px"
116
118
  title={t("Text")}
117
119
  bold
118
- label={t("Text")}
120
+ label="Text"
119
121
  >
120
122
  <Text text="Hello world" block={false} textStyle={""} />
121
123
  </WrapElem>
@@ -137,7 +139,7 @@ const ColumnsElem = ({ connectors }) => {
137
139
  innerClass="mt-m1px"
138
140
  icon="fas fa-columns"
139
141
  title={t("Split into columns")}
140
- label={t("Columns")}
142
+ label="Columns"
141
143
  >
142
144
  <Columns contents={[]} setting_col_n={1} />
143
145
  </WrapElem>
@@ -158,7 +160,7 @@ const TabsElem = ({ connectors }) => {
158
160
  connectors={connectors}
159
161
  icon={<SegmentedNav className="mb-2 h4" />}
160
162
  title={t("Tabbed content")}
161
- label={t("Tabs")}
163
+ label="Tabs"
162
164
  >
163
165
  <Tabs contents={[]} />
164
166
  </WrapElem>
@@ -180,7 +182,7 @@ const LineBreakElem = ({ connectors }) => {
180
182
  text="↵"
181
183
  fontSize="26px"
182
184
  title={t("Line break")}
183
- label={t("Break")}
185
+ label="Break"
184
186
  >
185
187
  <LineBreak />
186
188
  </WrapElem>
@@ -201,7 +203,7 @@ const HTMLElem = ({ connectors }) => {
201
203
  connectors={connectors}
202
204
  icon="fas fa-code"
203
205
  title={t("HTML code")}
204
- label={t("Code")}
206
+ label="Code"
205
207
  >
206
208
  <HTMLCode text={""} />
207
209
  </WrapElem>
@@ -222,7 +224,7 @@ const CardElem = ({ connectors }) => {
222
224
  connectors={connectors}
223
225
  title={t("Card")}
224
226
  icon="far fa-square"
225
- label={t("Card")}
227
+ label="Card"
226
228
  >
227
229
  <Element canvas is={Card} isFormula={{}} url=""></Element>
228
230
  </WrapElem>
@@ -243,7 +245,7 @@ const ImageElem = ({ connectors, images }) => {
243
245
  connectors={connectors}
244
246
  icon="fas fa-image"
245
247
  title={t("Image")}
246
- label={t("Image")}
248
+ label="Image"
247
249
  >
248
250
  <Image fileid={images.length > 0 ? images[0].id : 0} />
249
251
  </WrapElem>
@@ -264,7 +266,7 @@ const LinkElem = ({ connectors }) => {
264
266
  connectors={connectors}
265
267
  icon="fas fa-link"
266
268
  title={t("Link")}
267
- label={t("Link")}
269
+ label="Link"
268
270
  >
269
271
  <Link />
270
272
  </WrapElem>
@@ -285,7 +287,7 @@ const ViewElem = ({ connectors, views, isPageEdit }) => {
285
287
  connectors={connectors}
286
288
  icon="fas fa-eye"
287
289
  title={t("Embed a view")}
288
- label={t("View")}
290
+ label="View"
289
291
  disable={!views || views.length < (!isPageEdit ? 2 : 1)}
290
292
  >
291
293
  <View
@@ -311,7 +313,7 @@ const SearchElem = ({ connectors }) => {
311
313
  connectors={connectors}
312
314
  icon="fas fa-search"
313
315
  title={t("Search bar")}
314
- label={t("Search")}
316
+ label="Search"
315
317
  >
316
318
  <Element canvas is={SearchBar}></Element>
317
319
  </WrapElem>
@@ -332,7 +334,7 @@ const ContainerElem = ({ connectors }) => {
332
334
  connectors={connectors}
333
335
  icon={<BoundingBox className="mb-2 h5" />}
334
336
  title={t("Container")}
335
- label={t("Contain")}
337
+ label="Contain"
336
338
  >
337
339
  <Element canvas is={Container}></Element>
338
340
  </WrapElem>
@@ -353,7 +355,7 @@ const FieldElem = ({ connectors, fields, field_view_options }) => {
353
355
  connectors={connectors}
354
356
  icon="fas fa-asterisk"
355
357
  title={t("Field")}
356
- label={t("Field")}
358
+ label="Field"
357
359
  >
358
360
  <Field
359
361
  name={fields[0].name}
@@ -380,7 +382,7 @@ const DropDownFilterElem = ({ connectors, fields }) => {
380
382
  connectors={connectors}
381
383
  icon="far fa-caret-square-down"
382
384
  title={t("Dropdown filter")}
383
- label={t("Select")}
385
+ label="Select"
384
386
  >
385
387
  <DropDownFilter
386
388
  name={fields[0].name}
@@ -399,7 +401,7 @@ const DropMenuElem = ({ connectors }) => {
399
401
  connectors={connectors}
400
402
  icon="far fa-caret-square-down"
401
403
  title={t("Dropdown menu")}
402
- label={t("DropMenu")}
404
+ label="DropMenu"
403
405
  >
404
406
  <Element canvas is={DropMenu}></Element>
405
407
  </WrapElem>
@@ -413,7 +415,7 @@ const PageElem = ({ connectors, pages }) => {
413
415
  connectors={connectors}
414
416
  icon="fa-fw far fa-file"
415
417
  title={t("Embed a page")}
416
- label={t("Page")}
418
+ label="Page"
417
419
  disable={pages.length <= 1}
418
420
  >
419
421
  <Page page={pages.length > 0 ? pages[0].name : "page"} />
@@ -436,7 +438,7 @@ const ToggleFilterElem = ({ connectors, fields }) => {
436
438
  connectors={connectors}
437
439
  icon="fas fa-toggle-on"
438
440
  title={t("Toggle filter")}
439
- label={t("Toggle")}
441
+ label="Toggle"
440
442
  >
441
443
  <ToggleFilter name={fields[0].name} value={""} label={""} block={false} />
442
444
  </WrapElem>
@@ -457,7 +459,7 @@ const JoinFieldElem = ({ connectors, options }) => {
457
459
  connectors={connectors}
458
460
  icon={<Diagram3Fill className="mb-2 h5" />}
459
461
  title={t("Join field")}
460
- label={t("Join")}
462
+ label="Join"
461
463
  disable={options.parent_field_list.length === 0}
462
464
  >
463
465
  <JoinField
@@ -484,7 +486,7 @@ const ViewLinkElem = ({ connectors, options }) => {
484
486
  connectors={connectors}
485
487
  icons={["fas fa-eye", "fas fa-link"]}
486
488
  title={t("Link to a view")}
487
- label={t("ViewLink")}
489
+ label="ViewLink"
488
490
  disable={!options.views || options.views.length < 2}
489
491
  >
490
492
  <ViewLink
@@ -510,7 +512,7 @@ const ActionElem = ({ connectors, options }) => {
510
512
  return (
511
513
  <WrapElem
512
514
  connectors={connectors}
513
- label={t("Action")}
515
+ label="Action"
514
516
  icon="fas fa-running"
515
517
  title={t("Action button")}
516
518
  >
@@ -549,7 +551,7 @@ const AggregationElem = ({ connectors, child_field_list, agg_field_opts }) => {
549
551
  connectors={connectors}
550
552
  text="∑"
551
553
  title={t("Aggregation")}
552
- label={t("Aggreg8")}
554
+ label="Aggreg8"
553
555
  bold
554
556
  fontSize="16px"
555
557
  disable={child_field_list.length === 0}
@@ -574,15 +576,71 @@ const TableElem = ({ connectors }) => {
574
576
  innerClass="mt-m1px"
575
577
  icon="fas fa-table"
576
578
  title={t("Table")}
577
- label={t("Table")}
579
+ label="Table"
578
580
  >
579
581
  <Table contents={[[], []]} rows={2} columns={2} />
580
582
  </WrapElem>
581
583
  );
582
584
  };
583
585
 
586
+ const PromptContainerElem = ({ connectors }) => {
587
+ const { t } = useTranslation();
588
+ return (
589
+ <WrapElem
590
+ connectors={connectors}
591
+ icon="fas fa-robot"
592
+ title={t("Generate with AI")}
593
+ label="Generate"
594
+ >
595
+ <Prompt promptType="container" promptText="" />
596
+ </WrapElem>
597
+ );
598
+ };
599
+
600
+ const PromptViewElem = ({ connectors }) => {
601
+ const { t } = useTranslation();
602
+ return (
603
+ <WrapElem
604
+ connectors={connectors}
605
+ icon="fas fa-eye"
606
+ title={t("Prompt View")}
607
+ label="Prompt View"
608
+ >
609
+ <Prompt promptType="view" promptText="" />
610
+ </WrapElem>
611
+ );
612
+ };
613
+
614
+ const PromptFieldElem = ({ connectors }) => {
615
+ const { t } = useTranslation();
616
+ return (
617
+ <WrapElem
618
+ connectors={connectors}
619
+ icon="fas fa-i-cursor"
620
+ title={t("Prompt Field")}
621
+ label="Prompt Field"
622
+ >
623
+ <Prompt promptType="field" promptText="" />
624
+ </WrapElem>
625
+ );
626
+ };
627
+
628
+ const PromptActionElem = ({ connectors }) => {
629
+ const { t } = useTranslation();
630
+ return (
631
+ <WrapElem
632
+ connectors={connectors}
633
+ icon="fas fa-bolt"
634
+ title={t("Prompt Action")}
635
+ label="Prompt Action"
636
+ >
637
+ <Prompt promptType="action" promptText="" />
638
+ </WrapElem>
639
+ );
640
+ };
641
+
584
642
  const chunkToolBox = (elems, expanded) => {
585
- const chunks = chunk(elems, expanded ? 3 : 2);
643
+ const chunks = chunk(elems.filter(Boolean), expanded ? 3 : 2);
586
644
  return chunks.map((es, ix) => (
587
645
  <div className="toolbar-row" key={ix}>
588
646
  {es.map((e, j) => (
@@ -638,6 +696,10 @@ const ToolboxShow = ({ expanded }) => {
638
696
  <DropMenuElem connectors={connectors} />,
639
697
  <TableElem connectors={connectors} />,
640
698
  <PageElem connectors={connectors} pages={pages} />,
699
+ options.has_copilot_generate && <PromptContainerElem connectors={connectors} />,
700
+ // <PromptViewElem connectors={connectors} />,
701
+ // <PromptFieldElem connectors={connectors} />,
702
+ // <PromptActionElem connectors={connectors} />,
641
703
  ],
642
704
  expanded
643
705
  );
@@ -696,6 +758,10 @@ const ToolboxList = ({ expanded }) => {
696
758
  <DropMenuElem connectors={connectors} />
697
759
  ),
698
760
  // <TableElem connectors={connectors} />,
761
+ options.has_copilot_generate && <PromptContainerElem connectors={connectors} />,
762
+ // <PromptViewElem connectors={connectors} />,
763
+ // <PromptFieldElem connectors={connectors} />,
764
+ // <PromptActionElem connectors={connectors} />,
699
765
  options.allowMultipleElementsPerColumn &&
700
766
  !disable_toolbox?.line_break && (
701
767
  <LineBreakElem connectors={connectors} />
@@ -750,6 +816,10 @@ const ToolboxFilter = ({ expanded }) => {
750
816
  <TableElem connectors={connectors} />,
751
817
  <DropMenuElem connectors={connectors} />,
752
818
  <PageElem connectors={connectors} pages={pages} />,
819
+ options.has_copilot_generate && <PromptContainerElem connectors={connectors} />,
820
+ // <PromptViewElem connectors={connectors} />,
821
+ // <PromptFieldElem connectors={connectors} />,
822
+ // <PromptActionElem connectors={connectors} />,
753
823
  ],
754
824
  expanded
755
825
  );
@@ -787,6 +857,10 @@ const ToolboxEdit = ({ expanded }) => {
787
857
  <DropMenuElem connectors={connectors} />,
788
858
  <TableElem connectors={connectors} />,
789
859
  <ViewLinkElem connectors={connectors} options={options} />,
860
+ options.has_copilot_generate && <PromptContainerElem connectors={connectors} />,
861
+ // <PromptViewElem connectors={connectors} />,
862
+ // <PromptFieldElem connectors={connectors} />,
863
+ // <PromptActionElem connectors={connectors} />,
790
864
  ],
791
865
  expanded
792
866
  );
@@ -819,6 +893,10 @@ const ToolboxPage = ({ expanded }) => {
819
893
  <DropMenuElem connectors={connectors} />,
820
894
  <PageElem connectors={connectors} pages={pages} />,
821
895
  <TableElem connectors={connectors} />,
896
+ options.has_copilot_generate && <PromptContainerElem connectors={connectors} />,
897
+ // <PromptViewElem connectors={connectors} />,
898
+ // <PromptFieldElem connectors={connectors} />,
899
+ // <PromptActionElem connectors={connectors} />,
822
900
  ],
823
901
  expanded
824
902
  );
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /*global notifyAlert, apply_showif*/
7
7
 
8
- import React, { Fragment, useContext, useEffect, useState } from "react";
8
+ import React, { Fragment, useContext, useEffect } from "react";
9
9
  import { useNode } from "@craftjs/core";
10
10
  import useTranslation from "../../hooks/useTranslation";
11
11
  import optionsCtx from "../context";
@@ -25,7 +25,7 @@ import {
25
25
  } from "./utils";
26
26
  import { ntimes } from "./Columns";
27
27
  import { ArrayManager } from "./ArrayManager";
28
- import { MultiLineCodeEditor } from "./MonacoEditor";
28
+
29
29
  import Select from "react-select";
30
30
 
31
31
  export /**
@@ -55,6 +55,7 @@ const Action = ({
55
55
  action_bgcol,
56
56
  action_bordercol,
57
57
  action_textcol,
58
+ action_class
58
59
  }) => {
59
60
  const {
60
61
  selected,
@@ -65,7 +66,7 @@ const Action = ({
65
66
  */
66
67
  return (
67
68
  <button
68
- className={`btn ${action_style || "btn-primary"} ${action_size || ""} ${
69
+ className={`btn ${action_style || "btn-primary"} ${action_size || ""} ${action_class || ""} ${
69
70
  selected ? "selected-node" : ""
70
71
  } ${block ? "d-block" : ""}`}
71
72
  ref={(dom) => connect(drag(dom))}
@@ -146,13 +147,6 @@ const ActionSettings = () => {
146
147
  const options = useContext(optionsCtx);
147
148
  const getCfgFields = (fv) => (options.actionConfigForms || {})[fv];
148
149
  const cfgFields = getCfgFields(name);
149
- const cfgFieldsForForm =
150
- name === "run_js_code"
151
- ? (cfgFields || []).filter((f) => f.name !== "code")
152
- : cfgFields;
153
-
154
- const runJsCodeModalOnly = false;
155
- const [codeModalOpen, setCodeModalOpen] = useState(false);
156
150
  const setAProp = setAPropGen(setProp);
157
151
  const use_setting_action_n =
158
152
  setting_action_n || setting_action_n === 0 ? setting_action_n : 0;
@@ -457,116 +451,12 @@ const ActionSettings = () => {
457
451
  ) : null}
458
452
  </Fragment>
459
453
  ) : cfgFields ? (
460
- <Fragment>
461
- {name === "run_js_code" && runJsCodeModalOnly ? (
462
- <div className="builder-config-field" data-field-name="code">
463
- <label>{t("Code")}</label>
464
- <button
465
- type="button"
466
- className="btn btn-secondary btn-sm"
467
- onClick={() => setCodeModalOpen(true)}
468
- >
469
- {t("Open Code Popup")}
470
- </button>
471
- </div>
472
- ) : null}
473
- {name === "run_js_code" && !runJsCodeModalOnly ? (
474
- <Fragment>
475
- <ConfigForm
476
- fields={(cfgFields || []).filter((f) => f.name === "code")}
477
- configuration={configuration}
478
- setProp={setProp}
479
- node={node}
480
- openPopup={() => setCodeModalOpen(true)}
481
- />
482
- {/* <div className="builder-config-field mt-2" data-field-name="code-modal-trigger">
483
- <button
484
- type="button"
485
- className="btn btn-secondary btn-sm"
486
- onClick={() => setCodeModalOpen(true)}
487
- >
488
- {t("Open Code Popup")}
489
- </button>
490
- </div> */}
491
- <ConfigForm
492
- fields={cfgFieldsForForm}
493
- configuration={configuration}
494
- setProp={setProp}
495
- node={node}
496
- />
497
- </Fragment>
498
- ) : (
499
- <ConfigForm
500
- fields={runJsCodeModalOnly ? cfgFieldsForForm : cfgFields}
501
- configuration={configuration}
502
- setProp={setProp}
503
- node={node}
504
- />
505
- )}
506
- {name === "run_js_code" && codeModalOpen ? (
507
- <div
508
- className={`modal fade ${codeModalOpen ? "show" : ""}`}
509
- style={{
510
- display: codeModalOpen ? "block" : "none",
511
- zIndex: 1055,
512
- }}
513
- tabIndex={-1}
514
- role="dialog"
515
- aria-labelledby="codeModalLabel"
516
- aria-hidden={!codeModalOpen}
517
- >
518
- <div
519
- className="modal-backdrop fade show"
520
- style={{ zIndex: 1050 }}
521
- onClick={() => setCodeModalOpen(false)}
522
- aria-hidden="true"
523
- />
524
- <div
525
- className="modal-dialog modal-dialog-centered modal-lg"
526
- role="document"
527
- style={{ zIndex: 1060 }}
528
- onClick={(e) => e.stopPropagation()}
529
- >
530
- <div className="modal-content code-modal">
531
- <div className="modal-header">
532
- <h5 className="modal-title" id="codeModalLabel">
533
- {t("Code")}
534
- </h5>
535
- <button
536
- type="button"
537
- className="btn-close"
538
- aria-label="Close"
539
- onClick={() => setCodeModalOpen(false)}
540
- />
541
- </div>
542
- <div className="modal-body">
543
- <MultiLineCodeEditor
544
- setProp={setProp}
545
- value={configuration?.code ?? ""}
546
- onChange={(code) =>
547
- setProp((prop) => {
548
- if (!prop.configuration)
549
- prop.configuration = {};
550
- prop.configuration.code = code;
551
- })
552
- }
553
- isModalEditor
554
- />
555
- </div>
556
- <div className="modal-footer">
557
- <button
558
- type="button"
559
- className="btn btn-secondary"
560
- onClick={() => setCodeModalOpen(false)}
561
- >
562
- {t("Close")}
563
- </button>
564
- </div>
565
- </div>
566
- </div>
567
- </div>
568
- ) : null}
569
- </Fragment>
454
+ <ConfigForm
455
+ fields={cfgFields}
456
+ configuration={configuration}
457
+ setProp={setProp}
458
+ node={node}
459
+ />
570
460
  ) : null}
571
461
  {cfg_link ? (
572
462
  <a className="d-block mt-2" target="_blank" href={cfg_link}>
@@ -5,7 +5,13 @@
5
5
  */
6
6
  /* globals validate_expression_elem */
7
7
 
8
- import React, { Fragment, useState, useContext, useEffect, useRef } from "react";
8
+ import React, {
9
+ Fragment,
10
+ useState,
11
+ useContext,
12
+ useEffect,
13
+ useRef,
14
+ } from "react";
9
15
  import useTranslation from "../../hooks/useTranslation";
10
16
  import { useNode } from "@craftjs/core";
11
17
  import optionsCtx from "../context";
@@ -157,7 +163,9 @@ const AggregationSettings = () => {
157
163
  <tr>
158
164
  <td>
159
165
  <label>
160
- {options.mode === "filter" ? t("Field") : t("Child table field")}
166
+ {options.mode === "filter"
167
+ ? t("Field")
168
+ : t("Child table field")}
161
169
  </label>
162
170
  </td>
163
171
  <td>
@@ -187,13 +195,13 @@ const AggregationSettings = () => {
187
195
  >
188
196
  {buildOptions(
189
197
  [
190
- t("Count"),
191
- t("CountUnique"),
192
- t("Avg"),
193
- t("Sum"),
194
- t("Max"),
195
- t("Min"),
196
- t("Array_Agg"),
198
+ "Count",
199
+ "CountUnique",
200
+ "Avg",
201
+ "Sum",
202
+ "Max",
203
+ "Min",
204
+ "Array_Agg",
197
205
  ],
198
206
  { valAttr: true }
199
207
  )}
@@ -56,6 +56,8 @@ export const ArrayManager = ({
56
56
  managedArrays,
57
57
  manageContents,
58
58
  initialAddProps,
59
+ contentsKey = "contents",
60
+ onLayoutChange,
59
61
  }) => {
60
62
  const { t } = useTranslation();
61
63
  const { actions, query, connectors } = useEditor((state, query) => {
@@ -77,13 +79,14 @@ export const ArrayManager = ({
77
79
  node.id,
78
80
  options
79
81
  );
80
- layout.contents.splice(rmIx, 1);
82
+ layout[contentsKey].splice(rmIx, 1);
81
83
 
82
84
  managedArrays.forEach((arrNm) => {
83
- layout[arrNm].splice(rmIx, 1);
85
+ if (layout[arrNm]) layout[arrNm].splice(rmIx, 1);
84
86
  });
85
87
  layout[countProp] = node[countProp] - 1;
86
- layout[currentProp] = node[currentProp] - 1;
88
+ layout[currentProp] = Math.max(0, node[currentProp] - 1);
89
+ if (onLayoutChange) onLayoutChange(layout, "delete");
87
90
  actions.delete(node.id);
88
91
  layoutToNodes(layout, query, actions, parentId, options, sibIx);
89
92
  } else {
@@ -119,7 +122,7 @@ export const ArrayManager = ({
119
122
  options
120
123
  );
121
124
 
122
- swapElements(layout.contents, curIx, curIx + delta);
125
+ swapElements(layout[contentsKey], curIx, curIx + delta);
123
126
 
124
127
  managedArrays.forEach((arrNm) => {
125
128
  if (arrNm.includes(".")) {
@@ -130,6 +133,7 @@ export const ArrayManager = ({
130
133
  swapElements(layout[arrNm], curIx, curIx + delta);
131
134
  });
132
135
  layout[currentProp] = node[currentProp] + delta;
136
+ if (onLayoutChange) onLayoutChange(layout, "move");
133
137
  actions.delete(node.id);
134
138
  layoutToNodes(layout, query, actions, parentId, options, sibIx);
135
139
  } else
@@ -156,7 +160,7 @@ export const ArrayManager = ({
156
160
  options
157
161
  );
158
162
 
159
- layout.contents.push(null);
163
+ layout[contentsKey].push(null);
160
164
  managedArrays.forEach((arrNm) => {
161
165
  if (initialAddProps?.[arrNm])
162
166
  layout[arrNm][node[countProp]] = initialAddProps?.[arrNm];
@@ -164,6 +168,7 @@ export const ArrayManager = ({
164
168
  layout[currentProp] = +node[countProp];
165
169
  layout[countProp] = +node[countProp] + 1;
166
170
 
171
+ if (onLayoutChange) onLayoutChange(layout, "add");
167
172
  actions.delete(node.id);
168
173
  layoutToNodes(layout, query, actions, parentId, options, sibIx);
169
174
  } else