@saltcorn/builder 0.5.6-beta.3 → 0.6.0-beta.0

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,6 +6,8 @@ import {
6
6
  faChevronRight,
7
7
  } from "@fortawesome/free-solid-svg-icons";
8
8
  import { useNode } from "@craftjs/core";
9
+ import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
10
+ import faIcons from "./faicons";
9
11
 
10
12
  export const blockProps = (is_block) =>
11
13
  is_block ? { style: { display: "block" } } : {};
@@ -17,7 +19,12 @@ export const BlockSetting = ({ block, setProp }) => (
17
19
  name="block"
18
20
  type="checkbox"
19
21
  checked={block}
20
- onChange={(e) => setProp((prop) => (prop.block = e.target.checked))}
22
+ onChange={(e) => {
23
+ if (e.target) {
24
+ const target_value = e.target.checked;
25
+ setProp((prop) => (prop.block = target_value));
26
+ }
27
+ }}
21
28
  />
22
29
  <label className="form-check-label">Block display</label>
23
30
  </div>
@@ -36,9 +43,12 @@ export const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
36
43
  type="text"
37
44
  className="form-control text-to-display"
38
45
  value={node[nodekey]}
39
- onChange={(e) =>
40
- setProp((prop) => (prop[nodekey] = e.target.value))
41
- }
46
+ onChange={(e) => {
47
+ if (e.target) {
48
+ const target_value = e.target.value;
49
+ setProp((prop) => (prop[nodekey] = target_value));
50
+ }
51
+ }}
42
52
  />
43
53
  ) : (
44
54
  children
@@ -50,7 +60,7 @@ export const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
50
60
  }`}
51
61
  title="Calculated formula"
52
62
  type="button"
53
- onClick={(e) =>
63
+ onClick={() =>
54
64
  setProp((prop) => (prop.isFormula[nodekey] = !isFormula[nodekey]))
55
65
  }
56
66
  >
@@ -73,7 +83,12 @@ export const MinRoleSetting = ({ minRole, setProp }) => {
73
83
  <label>Minimum Role</label>
74
84
  <select
75
85
  value={minRole}
76
- onChange={(e) => setProp((prop) => (prop.minRole = e.target.value))}
86
+ onChange={(e) => (e) => {
87
+ if (e.target) {
88
+ const target_value = e.target.value;
89
+ setProp((prop) => (prop.minRole = target_value));
90
+ }
91
+ }}
77
92
  >
78
93
  {options.roles.map((r) => (
79
94
  <option key={r.id} value={r.id}>
@@ -95,7 +110,12 @@ export const MinRoleSettingRow = ({ minRole, setProp }) => {
95
110
  <select
96
111
  value={minRole}
97
112
  className="form-control"
98
- onChange={(e) => setProp((prop) => (prop.minRole = e.target.value))}
113
+ onChange={(e) => {
114
+ if (e.target) {
115
+ const target_value = e.target.value;
116
+ setProp((prop) => (prop.minRole = target_value));
117
+ }
118
+ }}
99
119
  >
100
120
  {options.roles.map((r) => (
101
121
  <option key={r.id} value={r.id}>
@@ -112,7 +132,12 @@ const TextStyleSelect = ({ textStyle, setProp }) => {
112
132
  <select
113
133
  value={textStyle}
114
134
  className="form-control"
115
- onChange={(e) => setProp((prop) => (prop.textStyle = e.target.value))}
135
+ onChange={(e) => {
136
+ if (e.target) {
137
+ const target_value = e.target.value;
138
+ setProp((prop) => (prop.textStyle = target_value));
139
+ }
140
+ }}
116
141
  >
117
142
  <option value="">Normal</option>
118
143
  <option value="h1">Heading 1</option>
@@ -254,13 +279,14 @@ export const fetchViewPreview = (args = {}) => (changes = {}) => {
254
279
  });
255
280
  };
256
281
 
257
- export const SelectUnits = ({ vert, ...props }) => (
282
+ export const SelectUnits = ({ vert, autoable, ...props }) => (
258
283
  <select {...props}>
259
284
  <option>px</option>
260
285
  <option>%</option>
261
286
  <option>{vert ? "vh" : "vw"}</option>
262
287
  <option>em</option>
263
288
  <option>rem</option>
289
+ {autoable && <option>auto</option>}
264
290
  </select>
265
291
  );
266
292
 
@@ -284,6 +310,19 @@ export const parseStyles = (styles) =>
284
310
  }),
285
311
  {}
286
312
  );
313
+
314
+ export const reactifyStyles = (styles) => {
315
+ const toCamel = (s) => {
316
+ return s.replace(/([-][a-z])/gi, ($1) => {
317
+ return $1.toUpperCase().replace("-", "");
318
+ });
319
+ };
320
+ const reactified = {};
321
+ Object.keys(styles).forEach((k) => {
322
+ reactified[toCamel(k)] = styles[k];
323
+ });
324
+ return reactified;
325
+ };
287
326
  const isCheckbox = (f) =>
288
327
  f && f.type && (f.type === "Bool" || f.type.name === "Bool");
289
328
  export const setInitialConfig = (setProp, fieldview, fields) => {
@@ -295,6 +334,24 @@ export const setInitialConfig = (setProp, fieldview, fields) => {
295
334
  });
296
335
  });
297
336
  };
337
+
338
+ const ColorInput = ({ value, onChange }) =>
339
+ value ? (
340
+ <input
341
+ type="color"
342
+ value={value}
343
+ className="form-control"
344
+ onChange={(e) => e.target && onChange(e.target.value)}
345
+ />
346
+ ) : (
347
+ <button
348
+ className="btn btn-sm btn-outline-secondary"
349
+ onClick={() => onChange("#000000")}
350
+ >
351
+ <small>Set color</small>
352
+ </button>
353
+ );
354
+
298
355
  export const ConfigForm = ({
299
356
  fields,
300
357
  configuration,
@@ -322,7 +379,9 @@ export const ConfigForm = ({
322
379
  setProp={setProp}
323
380
  onChange={onChange}
324
381
  />
325
- {f.sublabel ? <i>{f.sublabel}</i> : null}
382
+ {f.sublabel ? (
383
+ <i dangerouslySetInnerHTML={{ __html: f.sublabel }}></i>
384
+ ) : null}
326
385
  </div>
327
386
  );
328
387
  })}
@@ -338,25 +397,41 @@ export const ConfigField = ({
338
397
  setProp,
339
398
  onChange,
340
399
  props,
400
+ isStyle,
341
401
  }) => {
342
402
  const myOnChange = (v) => {
343
403
  setProp((prop) => {
344
- if (configuration) prop.configuration[field.name] = v;
345
- else prop[field.name] = v;
404
+ if (configuration) {
405
+ if (!prop.configuration) prop.configuration = {};
406
+ prop.configuration[field.name] = v;
407
+ } else if (isStyle) {
408
+ if (!prop.style) prop.style = {};
409
+ prop.style[field.name] = v;
410
+ } else prop[field.name] = v;
346
411
  });
347
412
  onChange && onChange(field.name, v);
348
413
  };
349
414
  const value = or_if_undef(
350
- configuration ? configuration[field.name] : props[field.name],
415
+ configuration
416
+ ? configuration[field.name]
417
+ : isStyle
418
+ ? props.style[field.name]
419
+ : props[field.name],
351
420
  field.default
352
421
  );
353
- return {
422
+ if (field.input_type === "fromtype") field.input_type = null;
423
+ if (field.type && field.type.name === "String" && field.attributes.options) {
424
+ field.input_type = "select";
425
+ field.options = field.attributes.options;
426
+ if (!field.required) field.options.unshift("");
427
+ }
428
+ const dispatch = {
354
429
  String: () => (
355
430
  <input
356
431
  type="text"
357
432
  className="form-control"
358
- value={value}
359
- onChange={(e) => myOnChange(e.target.value)}
433
+ value={value || ""}
434
+ onChange={(e) => e.target && myOnChange(e.target.value)}
360
435
  />
361
436
  ),
362
437
  Integer: () => (
@@ -364,34 +439,27 @@ export const ConfigField = ({
364
439
  type="number"
365
440
  className="form-control"
366
441
  step={1}
367
- value={value}
368
- onChange={(e) => myOnChange(e.target.value)}
442
+ value={value || ""}
443
+ onChange={(e) => e.target && myOnChange(e.target.value)}
369
444
  />
370
445
  ),
371
446
  Float: () => (
372
447
  <input
373
448
  type="number"
374
449
  className="form-control"
375
- value={value}
450
+ value={value || ""}
376
451
  step={0.01}
377
- onChange={(e) => myOnChange(e.target.value)}
378
- />
379
- ),
380
- Color: () => (
381
- <input
382
- type="color"
383
- value={value}
384
- className="form-control"
385
- onChange={(e) => myOnChange(e.target.value)}
452
+ onChange={(e) => e.target && myOnChange(e.target.value)}
386
453
  />
387
454
  ),
455
+ Color: () => <ColorInput value={value} onChange={(c) => myOnChange(c)} />,
388
456
  Bool: () => (
389
457
  <div className="form-check">
390
458
  <input
391
459
  type="checkbox"
392
460
  className="form-check-input"
393
461
  checked={value}
394
- onChange={(e) => myOnChange(e.target.checked)}
462
+ onChange={(e) => e.target && myOnChange(e.target.checked)}
395
463
  />
396
464
  <label className="form-check-label">{field.label}</label>
397
465
  </div>
@@ -402,14 +470,23 @@ export const ConfigField = ({
402
470
  type="text"
403
471
  className="form-control"
404
472
  value={value}
405
- onChange={(e) => myOnChange(e.target.value)}
473
+ onChange={(e) => e.target && myOnChange(e.target.value)}
474
+ />
475
+ ),
476
+ code: () => (
477
+ <textarea
478
+ rows="6"
479
+ type="text"
480
+ className="form-control"
481
+ value={value}
482
+ onChange={(e) => e.target && myOnChange(e.target.value)}
406
483
  />
407
484
  ),
408
485
  select: () => (
409
486
  <select
410
487
  className="form-control"
411
- value={value}
412
- onChange={(e) => myOnChange(e.target.value)}
488
+ value={value || ""}
489
+ onChange={(e) => e.target && myOnChange(e.target.value)}
413
490
  >
414
491
  {field.options.map((o, ix) => (
415
492
  <option key={ix}>{o}</option>
@@ -417,13 +494,14 @@ export const ConfigField = ({
417
494
  </select>
418
495
  ),
419
496
  btn_select: () => (
420
- <div class="btn-group w-100" role="group">
497
+ <div className="btn-group w-100" role="group">
421
498
  {field.options.map((o, ix) => (
422
499
  <button
500
+ key={ix}
423
501
  title={o.title || o.value}
424
502
  type="button"
425
503
  style={{ width: `${Math.floor(100 / field.options.length)}%` }}
426
- class={`btn btn-sm btn-${
504
+ className={`btn btn-sm btn-${
427
505
  value !== o.value ? "outline-" : ""
428
506
  }secondary ${field.btnClass || ""}`}
429
507
  onClick={() => myOnChange(o.value)}
@@ -433,37 +511,77 @@ export const ConfigField = ({
433
511
  ))}
434
512
  </div>
435
513
  ),
436
- DimUnits: () => (
437
- <Fragment>
438
- <input
439
- type="number"
440
- value={value}
441
- step="1"
442
- min="0"
443
- max="9999"
444
- className="w-50 form-control-sm d-inline dimunit"
445
- onChange={(e) => myOnChange(e.target.value)}
446
- />
447
- <SelectUnits
448
- value={or_if_undef(
449
- configuration
450
- ? configuration[field.name + "Unit"]
451
- : props[field.name + "Unit"],
452
- "px"
514
+ DimUnits: () => {
515
+ let styleVal, styleDim;
516
+ if (isStyle && value === "auto") {
517
+ styleVal = "";
518
+ styleDim = "auto";
519
+ } else if (isStyle && value && typeof value === "string") {
520
+ const matches = value.match(/^([0-9]+\.?[0-9]*)(.*)/);
521
+ if (matches) {
522
+ styleVal = matches[1];
523
+ styleDim = matches[2];
524
+ }
525
+ }
526
+ return (
527
+ <Fragment>
528
+ {styleDim !== "auto" && (
529
+ <input
530
+ type="number"
531
+ value={(isStyle ? styleVal : value) || ""}
532
+ step="1"
533
+ min="0"
534
+ max="9999"
535
+ className="w-50 form-control-sm d-inline dimunit"
536
+ disabled={field.autoable && styleDim === "auto"}
537
+ onChange={(e) =>
538
+ myOnChange(
539
+ isStyle
540
+ ? `${e.target.value}${styleDim || "px"}`
541
+ : e.target.value
542
+ )
543
+ }
544
+ />
453
545
  )}
454
- className="w-50 form-control-sm d-inline dimunit"
455
- vert={true}
456
- onChange={(e) =>
457
- setProp((prop) => {
458
- if (configuration)
459
- prop.configuration[field.name + "Unit"] = e.target.value;
460
- else prop[field.name + "Unit"] = e.target.value;
461
- })
462
- }
463
- />
464
- </Fragment>
465
- ),
466
- }[field.input_type || field.type.name || field.type]();
546
+ <SelectUnits
547
+ value={or_if_undef(
548
+ configuration
549
+ ? configuration[field.name + "Unit"]
550
+ : isStyle
551
+ ? styleDim
552
+ : props[field.name + "Unit"],
553
+ "px"
554
+ )}
555
+ autoable={field.autoable}
556
+ className={`w-${
557
+ styleDim === "auto" ? 100 : 50
558
+ } form-control-sm d-inline dimunit`}
559
+ vert={true}
560
+ onChange={(e) => {
561
+ if (!e.target) return;
562
+ const target_value = e.target.value;
563
+ setProp((prop) => {
564
+ const myStyleVal =
565
+ target_value === "auto" && field.autoable && isStyle
566
+ ? ""
567
+ : styleVal;
568
+ if (configuration)
569
+ prop.configuration[field.name + "Unit"] = target_value;
570
+ else if (isStyle) {
571
+ prop.style[field.name] = `${or_if_undef(
572
+ myStyleVal,
573
+ 0
574
+ )}${target_value}`;
575
+ } else prop[field.name + "Unit"] = target_value;
576
+ });
577
+ }}
578
+ />
579
+ </Fragment>
580
+ );
581
+ },
582
+ };
583
+ const f = dispatch[field.input_type || field.type.name || field.type];
584
+ return f ? f() : null;
467
585
  };
468
586
 
469
587
  export const SettingsFromFields = (fields) => () => {
@@ -497,7 +615,7 @@ export const SettingsSectionHeaderRow = ({ title }) => (
497
615
  </tr>
498
616
  );
499
617
 
500
- export const SettingsRow = ({ field, node, setProp, onChange }) => {
618
+ export const SettingsRow = ({ field, node, setProp, onChange, isStyle }) => {
501
619
  const fullWidth = ["String", "Bool", "textarea"].includes(field.type);
502
620
  const needLabel = field.type !== "Bool";
503
621
  const inner = field.canBeFormula ? (
@@ -519,6 +637,7 @@ export const SettingsRow = ({ field, node, setProp, onChange }) => {
519
637
  props={node}
520
638
  setProp={setProp}
521
639
  onChange={onChange}
640
+ isStyle={isStyle}
522
641
  />
523
642
  );
524
643
  return (
@@ -562,3 +681,143 @@ export class ErrorBoundary extends React.Component {
562
681
  return this.props.children;
563
682
  }
564
683
  }
684
+
685
+ export const ButtonOrLinkSettingsRows = ({
686
+ setProp,
687
+ btnClass = null,
688
+ keyPrefix = "",
689
+ values,
690
+ linkFirst = false,
691
+ }) => {
692
+ const setAProp = (key) => (e) => {
693
+ if (e.target) {
694
+ const target_value = e.target.value;
695
+ setProp((prop) => (prop[key] = target_value));
696
+ }
697
+ };
698
+ const addBtnClass = (s) => (btnClass ? `${btnClass} ${s}` : s);
699
+ return [
700
+ <tr key="btnstyle">
701
+ <td>
702
+ <label>Style</label>
703
+ </td>
704
+ <td>
705
+ <select
706
+ className="form-control"
707
+ value={values[keyPrefix + "style"]}
708
+ onChange={setAProp(keyPrefix + "style")}
709
+ >
710
+ {linkFirst ? (
711
+ <option value={addBtnClass("btn-link")}>Link</option>
712
+ ) : null}
713
+ <option value={addBtnClass("btn-primary")}>Primary button</option>
714
+ <option value={addBtnClass("btn-secondary")}>Secondary button</option>
715
+ <option value={addBtnClass("btn-success")}>Success button</option>
716
+ <option value={addBtnClass("btn-danger")}>Danger button</option>
717
+ <option value={addBtnClass("btn-outline-primary")}>
718
+ Primary outline button
719
+ </option>
720
+ <option value={addBtnClass("btn-outline-secondary")}>
721
+ Secondary outline button
722
+ </option>
723
+ <option value={addBtnClass("btn-custom-color")}>
724
+ Button custom color
725
+ </option>
726
+ {!linkFirst ? (
727
+ <option value={addBtnClass("btn-link")}>Link</option>
728
+ ) : null}
729
+ </select>
730
+ </td>
731
+ </tr>,
732
+ <tr key="btnsz">
733
+ <td>
734
+ <label>Action size</label>
735
+ </td>
736
+ <td>
737
+ <select
738
+ className="form-control"
739
+ value={values[keyPrefix + "size"]}
740
+ onChange={setAProp(keyPrefix + "size")}
741
+ >
742
+ <option value="">Standard</option>
743
+ <option value="btn-lg">Large</option>
744
+ <option value="btn-sm">Small</option>
745
+ <option value="btn-block">Block</option>
746
+ <option value="btn-block btn-lg">Large block</option>
747
+ </select>
748
+ </td>
749
+ </tr>,
750
+ <tr key="btnicon">
751
+ <td>
752
+ <label>Icon</label>
753
+ </td>
754
+ <td>
755
+ <FontIconPicker
756
+ value={values[keyPrefix + "icon"]}
757
+ onChange={(value) =>
758
+ setProp((prop) => (prop[keyPrefix + "icon"] = value))
759
+ }
760
+ isMulti={false}
761
+ icons={faIcons}
762
+ />
763
+ </td>
764
+ </tr>,
765
+ ...(values[keyPrefix + "style"] === addBtnClass("btn-custom-color")
766
+ ? [
767
+ <tr key="btnbgcol">
768
+ <td>
769
+ <label>Background</label>
770
+ </td>
771
+ <td>
772
+ <input
773
+ type="color"
774
+ value={values[keyPrefix + "bgcol"]}
775
+ className="form-control-sm w-50"
776
+ onChange={setAProp(keyPrefix + "bgcol")}
777
+ />
778
+ </td>
779
+ </tr>,
780
+ <tr key="btnbdcol">
781
+ <td>
782
+ <label>Border</label>
783
+ </td>
784
+ <td>
785
+ <input
786
+ type="color"
787
+ value={values[keyPrefix + "bordercol"]}
788
+ className="form-control-sm w-50"
789
+ onChange={setAProp(keyPrefix + "bordercol")}
790
+ />
791
+ </td>
792
+ </tr>,
793
+ <tr key="btntxtcol">
794
+ <td>
795
+ <label>Text</label>
796
+ </td>
797
+ <td>
798
+ <input
799
+ type="color"
800
+ value={values[keyPrefix + "textcol"]}
801
+ className="form-control-sm w-50"
802
+ onChange={setAProp(keyPrefix + "textcol")}
803
+ />
804
+ </td>
805
+ </tr>,
806
+ ]
807
+ : []),
808
+ ];
809
+ };
810
+ export const bstyleopt = (style) => ({
811
+ value: style,
812
+ title: style,
813
+ label: (
814
+ <div
815
+ style={{
816
+ borderLeftStyle: style,
817
+ borderTopStyle: style,
818
+ height: "15px",
819
+ width: "6px",
820
+ }}
821
+ ></div>
822
+ ),
823
+ });