alchemy-form 0.1.7 → 0.1.10

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/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## 0.1.10 (2022-07-14)
2
+
3
+ * Hide `code-input` contents until it's fully loaded
4
+ * Don't crash when failing to get a field definition in a table
5
+
6
+ ## 0.1.9 (2022-07-06)
7
+
8
+ * Allow supplying a custom `data-src` to `alchemy-field` elements
9
+ * Add `view` templates for File & Datetime fields
10
+ * Use the new `alchemy.getClientModel()` method in `alchemy-field` element
11
+ * Add `alchemy-password-input` element
12
+
13
+ ## 0.1.8 (2022-06-29)
14
+
15
+ * Move the `alchemy-select` pagination checker initializer to a static function
16
+ * Prevent `alchemy-select` from keeping on loading remote data when last few results were empty
17
+ * Fix pagination breaking re-opening of `alchemy-select` dropdown
18
+ * Fix initial value of a `BelongsTo` `alchemy-select` element not loading
19
+ * Fix `alchemy-select` downward-arrow-icon being rendered over value content
20
+ * Use `alchemy-code-input` for HTML fields
21
+ * Allow manually setting a value on `alchemy-field` elements
22
+ * Try to let `Field` instances decide the representation of values in `alchemy-table` elements
23
+ * Don't open the `alchemy-table` contextmenu when clicking on anchors
24
+
1
25
  ## 0.1.7 (2022-06-12)
2
26
 
3
27
  * Fix `<alchemy-table>` code typo
@@ -39,7 +39,7 @@ alchemy-select {
39
39
  align-items: center;
40
40
 
41
41
  input.type-area {
42
- height: 1.5rem;
42
+ height: 1.5rem !important;
43
43
  }
44
44
  }
45
45
 
@@ -102,7 +102,6 @@ alchemy-select {
102
102
  font-family: inherit;
103
103
  font-size: 13px;
104
104
  line-height: 18px;
105
- -webkit-font-smoothing: inherit;
106
105
  display: inline-block !important;
107
106
  padding: 0 !important;
108
107
  min-height: 0 !important;
@@ -113,11 +112,10 @@ alchemy-select {
113
112
  border: 0 none !important;
114
113
  background: none !important;
115
114
  line-height: inherit !important;
116
- -webkit-user-select: auto !important;
117
- -webkit-box-shadow: none !important;
118
115
  box-shadow: none !important;
119
116
  outline: none !important;
120
117
  flex: 0 1 auto;
118
+ margin-right: 20px !important;
121
119
  }
122
120
 
123
121
  .dropdown {
@@ -17,20 +17,23 @@ const FormApi = Function.inherits('Alchemy.Controller', 'FormApi');
17
17
  *
18
18
  * @author Jelle De Loecker <jelle@elevenways.be>
19
19
  * @since 0.1.0
20
- * @version 0.1.6
20
+ * @version 0.1.8
21
21
  *
22
22
  * @param {Conduit} conduit
23
23
  */
24
24
  FormApi.setAction(async function related(conduit) {
25
25
 
26
- const body = conduit.body;
26
+ const body = conduit.body || {},
27
+ config = body.config || {};
27
28
 
28
29
  const model = this.getModel(body.assoc_model);
29
30
  let crit = model.find();
30
31
  crit.limit(50);
31
32
  crit.setOption('scenario', 'related_data');
32
33
 
33
- if (body.config && body.config.search) {
34
+ if (config.value) {
35
+ crit.where(model.primary_key).equals(config.value);
36
+ } else if (config.search) {
34
37
  let display_fields = Array.cast(model.displayField);
35
38
 
36
39
  let or = crit.or();
@@ -45,6 +48,10 @@ FormApi.setAction(async function related(conduit) {
45
48
  }
46
49
  }
47
50
 
51
+ if (config.page) {
52
+ crit.page(config.page);
53
+ }
54
+
48
55
  let records = await model.find('all', crit);
49
56
 
50
57
  let result = {
@@ -34,6 +34,15 @@ Field.setStylesheetFile('form/alchemy_field');
34
34
  */
35
35
  Field.setStatic('use_new_renderer_scope', true);
36
36
 
37
+ /**
38
+ * The data source url/route to use
39
+ *
40
+ * @author Jelle De Loecker <jelle@elevenways.be>
41
+ * @since 0.1.9
42
+ * @version 0.1.9
43
+ */
44
+ Field.setAttribute('data-src');
45
+
37
46
  /**
38
47
  * The name of the field
39
48
  *
@@ -133,7 +142,7 @@ Field.enforceProperty(function alchemy_field_schema(new_value, old_value) {
133
142
  *
134
143
  * @author Jelle De Loecker <jelle@elevenways.be>
135
144
  * @since 0.1.0
136
- * @version 0.1.4
145
+ * @version 0.1.8
137
146
  */
138
147
  Field.enforceProperty(function config(new_value, old_value) {
139
148
 
@@ -148,7 +157,7 @@ Field.enforceProperty(function config(new_value, old_value) {
148
157
 
149
158
  if (new_value && new_value.constructor && new_value.constructor.type_name) {
150
159
  this.field_type = new_value.constructor.type_name;
151
- } else {
160
+ } else if (new_value) {
152
161
  this.field_type = null;
153
162
  }
154
163
 
@@ -160,7 +169,7 @@ Field.enforceProperty(function config(new_value, old_value) {
160
169
  *
161
170
  * @author Jelle De Loecker <jelle@elevenways.be>
162
171
  * @since 0.1.0
163
- * @version 0.1.0
172
+ * @version 0.1.9
164
173
  */
165
174
  Field.enforceProperty(function schema(new_value, old_value) {
166
175
 
@@ -181,15 +190,10 @@ Field.enforceProperty(function schema(new_value, old_value) {
181
190
  let model_name = this.model;
182
191
 
183
192
  if (model_name) {
184
- // @TODO: Always get the Client-side model here
185
- let model = alchemy.getModel(model_name);
193
+ let model = alchemy.getClientModel(model_name);
186
194
 
187
195
  if (model) {
188
196
  new_value = model.schema;
189
-
190
- if (Blast.isNode) {
191
- new_value = JSON.clone(new_value, 'toHawkejs');
192
- }
193
197
  }
194
198
  }
195
199
  }
@@ -493,10 +497,14 @@ Field.setProperty(function wrapper_files() {
493
497
  *
494
498
  * @author Jelle De Loecker <jelle@elevenways.be>
495
499
  * @since 0.1.0
496
- * @version 0.1.4
500
+ * @version 0.1.8
497
501
  */
498
502
  Field.setProperty(function original_value() {
499
503
 
504
+ if (this.assigned_data.original_value != null) {
505
+ return this.assigned_data.original_value;
506
+ }
507
+
500
508
  let alchemy_field_schema = this.alchemy_field_schema,
501
509
  path = this.field_path_in_current_schema;
502
510
 
@@ -517,6 +525,8 @@ Field.setProperty(function original_value() {
517
525
  if (form && form.document) {
518
526
  return Object.path(form.document, path);
519
527
  }
528
+ }, function setOriginalValue(value) {
529
+ return this.assigned_data.original_value = value;
520
530
  });
521
531
 
522
532
  /**
@@ -551,7 +561,7 @@ Field.setProperty(function value_element() {
551
561
  *
552
562
  * @author Jelle De Loecker <jelle@elevenways.be>
553
563
  * @since 0.1.0
554
- * @version 0.1.3
564
+ * @version 0.1.8
555
565
  */
556
566
  Field.setProperty(function value() {
557
567
 
@@ -569,8 +579,9 @@ Field.setProperty(function value() {
569
579
 
570
580
  if (element) {
571
581
  element.value = value;
582
+ } else if (this.original_value == null) {
583
+ this.original_value = value;
572
584
  }
573
-
574
585
  });
575
586
 
576
587
  /**
@@ -708,7 +719,7 @@ Field.setMethod(function retained() {
708
719
  *
709
720
  * @author Jelle De Loecker <jelle@elevenways.be>
710
721
  * @since 0.1.0
711
- * @version 0.1.6
722
+ * @version 0.1.9
712
723
  *
713
724
  * @param {Object} config
714
725
  * @param {HTMLElement} element
@@ -735,7 +746,7 @@ Field.setMethod(async function loadData(config, element) {
735
746
  }
736
747
  }
737
748
 
738
- return this.hawkejs_helpers.Alchemy.getResource({
749
+ let resource_options = {
739
750
  name : 'FormApi#related',
740
751
  post : true,
741
752
  body : {
@@ -744,7 +755,13 @@ Field.setMethod(async function loadData(config, element) {
744
755
  assoc_model : field.options.modelName,
745
756
  config : config,
746
757
  }
747
- });
758
+ };
759
+
760
+ if (this.data_src) {
761
+ resource_options.name = this.data_src;
762
+ }
763
+
764
+ return this.hawkejs_helpers.Alchemy.getResource(resource_options);
748
765
  }
749
766
 
750
767
  });
@@ -86,7 +86,7 @@ FieldArray.setMethod(function introduced() {
86
86
  let view_files = alchemy_field.view_files;
87
87
 
88
88
  if (!view_files || !view_files.length) {
89
- throw new Error('Unable to add new entry for ' + alchemy_field.field_title);
89
+ throw new Error('Unable to add new entry for field "' + alchemy_field.field_title + '", no view files found');
90
90
  }
91
91
 
92
92
  let new_entry = that.createElement('alchemy-field-array-entry');
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The number-input custom element
3
+ *
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
+ * @since 0.1.9
6
+ * @version 0.1.9
7
+ */
8
+ const PasswordInput = Function.inherits('Alchemy.Element.Form.StringInput', 'PasswordInput');
9
+
10
+ /**
11
+ * The template to use for the content of this element
12
+ *
13
+ * @author Jelle De Loecker <jelle@elevenways.be>
14
+ * @since 0.1.9
15
+ * @version 0.1.9
16
+ */
17
+ PasswordInput.setTemplateFile('form/elements/password_input');
18
+
19
+ /**
20
+ * Reference to the repeat input element
21
+ *
22
+ * @author Jelle De Loecker <jelle@elevenways.be>
23
+ * @since 0.1.9
24
+ * @version 0.1.9
25
+ */
26
+ PasswordInput.addElementGetter('repeat_el', '.repeat-input');
27
+
28
+ /**
29
+ * Reference to the repeat label element
30
+ *
31
+ * @author Jelle De Loecker <jelle@elevenways.be>
32
+ * @since 0.1.9
33
+ * @version 0.1.9
34
+ */
35
+ PasswordInput.addElementGetter('repeat_label', '.repeat-input-label');
36
+
37
+ /**
38
+ * The value property
39
+ *
40
+ * @author Jelle De Loecker <jelle@elevenways.be>
41
+ * @since 0.1.9
42
+ * @version 0.1.9
43
+ */
44
+ PasswordInput.setProperty(function value() {
45
+
46
+ let val;
47
+
48
+ if (this.input_el) {
49
+ val = this.input_el.value;
50
+ }
51
+
52
+ if (!val) {
53
+ return undefined;
54
+ }
55
+
56
+ if (!this.valuesAreEqual()) {
57
+ return undefined;
58
+ }
59
+
60
+ return val;
61
+ });
62
+
63
+ /**
64
+ * Are both inputs the same?
65
+ *
66
+ * @author Jelle De Loecker <jelle@elevenways.be>
67
+ * @since 0.1.9
68
+ * @version 0.1.9
69
+ */
70
+ PasswordInput.setMethod(function valuesAreEqual() {
71
+
72
+ let first_value = this.input_el?.value,
73
+ repeat_value = this.repeat_el?.value;
74
+
75
+ return first_value == repeat_value;
76
+ });
77
+
78
+ /**
79
+ * Revalidate the value
80
+ *
81
+ * @author Jelle De Loecker <jelle@elevenways.be>
82
+ * @since 0.1.9
83
+ * @version 0.1.9
84
+ */
85
+ PasswordInput.setMethod(async function revalidate() {
86
+
87
+ let first_value = this.input_el?.value,
88
+ repeat_value = this.repeat_el.value;
89
+
90
+ if (first_value) {
91
+ this.repeat_label.hidden = false;
92
+ } else {
93
+ this.repeat_el.value = '';
94
+ this.repeat_label.hidden = true;
95
+ }
96
+
97
+ this.removeErrors();
98
+
99
+ if (!first_value && !repeat_value) {
100
+ return;
101
+ }
102
+
103
+ if (first_value != repeat_value) {
104
+ return this.addError(this.__('does-not-match'), true);
105
+ }
106
+
107
+ return revalidate.super.call(this);
108
+ });
109
+
110
+ /**
111
+ * Actions to perform when this element
112
+ * has been added to the DOM for the first time
113
+ *
114
+ * @author Jelle De Loecker <jelle@elevenways.be>
115
+ * @since 0.1.9
116
+ * @version 0.1.9
117
+ */
118
+ PasswordInput.setMethod(function introduced() {
119
+
120
+ const that = this;
121
+
122
+ introduced.super.call(this);
123
+
124
+ this.repeat_el.addEventListener('focus', function onFocus(e) {
125
+ that.inputbox_el.classList.add('focus');
126
+ });
127
+
128
+ this.repeat_el.addEventListener('blur', function onBlur(e) {
129
+ that.inputbox_el.classList.remove('focus');
130
+ });
131
+
132
+ this.repeat_el.addEventListener('keyup', Fn.throttle(function onKeyup(e) {
133
+ that.revalidate();
134
+ that.emit('changing');
135
+ }, {
136
+ minimum_wait : 350,
137
+ immediate : false,
138
+ reset_on_call : true
139
+ }));
140
+
141
+ });
@@ -5,9 +5,7 @@
5
5
  * @since 0.1.0
6
6
  * @version 0.1.0
7
7
  */
8
- var AlchemySelect = Function.inherits('Alchemy.Element.Form.Base', function Select() {
9
- Select.super.call(this);
10
- });
8
+ const AlchemySelect = Function.inherits('Alchemy.Element.Form.Base', 'Select');
11
9
 
12
10
  /**
13
11
  * The remote url attribute
@@ -389,14 +387,56 @@ AlchemySelect.setProperty(function loading_dropdown() {
389
387
  *
390
388
  * @author Jelle De Loecker <jelle@develry.be>
391
389
  * @since 0.1.0
392
- * @version 0.1.0
390
+ * @version 0.1.8
393
391
  */
394
392
  AlchemySelect.setProperty(function search_value() {
395
- var type_area = this.type_area;
393
+
394
+ let type_area = this.type_area,
395
+ result = '';
396
396
 
397
397
  if (type_area) {
398
- return type_area.value;
398
+ result = type_area.value;
399
399
  }
400
+
401
+ if (this.previous_search_value == null) {
402
+ this.previous_search_value = '';
403
+ }
404
+
405
+ // If the search value changed, nullify the last page result
406
+ if (this.previous_search_value != result) {
407
+ this.setLastSuccessfulLoadedPage(null);
408
+ }
409
+
410
+ this.previous_search_value = result;
411
+
412
+ return result;
413
+ });
414
+
415
+ /**
416
+ * Make sure the pagination watcher exists
417
+ *
418
+ * @author Jelle De Loecker <jelle@elevenways.be>
419
+ * @since 0.1.0
420
+ * @version 0.1.8
421
+ */
422
+ AlchemySelect.setStatic(function ensurePaginationChecker() {
423
+
424
+ if (AlchemySelect.has_paginator_viewer) {
425
+ return;
426
+ }
427
+
428
+ // Only ever add 1 viewer per page
429
+ AlchemySelect.has_paginator_viewer = true;
430
+
431
+ hawkejs.scene.appears('js-he-ais-pager', {live: true, padding: 10, throttle: 200}, function onInfinity(el) {
432
+
433
+ if (!el.parentElement || !el.parentElement.parentElement) {
434
+ return;
435
+ }
436
+
437
+ const alchemy_select_instance = el.parentElement.parentElement;
438
+ alchemy_select_instance.loadOptions(++alchemy_select_instance.loaded_page);
439
+ });
400
440
  });
401
441
 
402
442
  /**
@@ -420,7 +460,7 @@ AlchemySelect.setMethod(function rendered() {
420
460
  *
421
461
  * @author Jelle De Loecker <jelle@develry.be>
422
462
  * @since 0.1.0
423
- * @version 0.1.0
463
+ * @version 0.1.8
424
464
  */
425
465
  AlchemySelect.setMethod(function introduced() {
426
466
 
@@ -433,14 +473,7 @@ AlchemySelect.setMethod(function introduced() {
433
473
  this.makeSortable();
434
474
  }
435
475
 
436
- if (!AlchemySelect.has_paginator_viewer) {
437
- AlchemySelect.has_paginator_viewer = true;
438
-
439
- hawkejs.scene.appears('js-he-ais-pager', {live: true, padding: 10, throttle: 200}, function onInfinity(el) {
440
- var ais = el.parentElement.parentElement;
441
- ais.loadOptions(++ais.loaded_page);
442
- });
443
- }
476
+ AlchemySelect.ensurePaginationChecker();
444
477
 
445
478
  this.addEventListener('focus-without', function onFocusOut(e) {
446
479
  that.type_area.value = '';
@@ -462,8 +495,6 @@ AlchemySelect.setMethod(function introduced() {
462
495
 
463
496
  this.addEventListener('keydown', function onKeydown(e) {
464
497
 
465
- console.log('keydown:', e);
466
-
467
498
  // Only listen for keydowns on the alchemy-select itself
468
499
  if (e.target != that) {
469
500
  return;
@@ -829,11 +860,12 @@ AlchemySelect.setMethod(function _processPreloadedValues() {
829
860
  *
830
861
  * @author Jelle De Loecker <jelle@develry.be>
831
862
  * @since 0.1.0
832
- * @version 0.1.6
863
+ * @version 0.1.8
833
864
  *
834
865
  * @param {Object} response
866
+ * @param {Number} page The page, if it's via pagination
835
867
  */
836
- AlchemySelect.setMethod(function _processResponseData(response) {
868
+ AlchemySelect.setMethod(function _processResponseData(response, page) {
837
869
 
838
870
  if (!response) {
839
871
  response = {};
@@ -851,14 +883,48 @@ AlchemySelect.setMethod(function _processResponseData(response) {
851
883
  records = [];
852
884
  }
853
885
 
854
- for (record of records) {
886
+ this._processResponseList(records, page);
887
+
888
+ this.refreshResultAmount();
889
+ this._markSelectedItems();
890
+ });
891
+
892
+ /**
893
+ * Process the response list of a certain paige
894
+ *
895
+ * @author Jelle De Loecker <jelle@elevenways.be>
896
+ * @since 0.1.8
897
+ * @version 0.1.8
898
+ *
899
+ * @param {Array} list
900
+ * @param {Number} page
901
+ */
902
+ AlchemySelect.setMethod(function _processResponseList(list, page) {
903
+
904
+ let record,
905
+ item;
906
+
907
+ if (page == 1 && this.current_option && this.current_option.data) {
908
+ record = this.current_option.data;
909
+ item = this._makeOption(record._id || record.id, record);
910
+ this.addToDropdown(item);
911
+ }
912
+
913
+ for (record of list) {
855
914
  item = this._makeOption(record._id || record.id, record);
856
915
  this.addToDropdown(item);
857
916
  }
858
917
 
859
918
  this.loading_dropdown = false;
860
- this.refreshResultAmount();
861
- this._markSelectedItems();
919
+
920
+ // If there were values & they come from a page, record this as a succesful page
921
+ if (page != null && list.length) {
922
+ if (!this.last_page_result) {
923
+ this.setLastSuccessfulLoadedPage(page);
924
+ } else if (page > this.last_page_result) {
925
+ this.setLastSuccessfulLoadedPage(page);
926
+ }
927
+ }
862
928
  });
863
929
 
864
930
  /**
@@ -866,7 +932,7 @@ AlchemySelect.setMethod(function _processResponseData(response) {
866
932
  *
867
933
  * @author Jelle De Loecker <jelle@develry.be>
868
934
  * @since 0.1.0
869
- * @version 0.1.0
935
+ * @version 0.1.8
870
936
  *
871
937
  * @param {Number} page
872
938
  *
@@ -887,6 +953,10 @@ AlchemySelect.setMethod(function loadRemote(page) {
887
953
  return pledge;
888
954
  }
889
955
 
956
+ if (page == null) {
957
+ page = 1;
958
+ }
959
+
890
960
  const that = this;
891
961
 
892
962
  this.loading_dropdown = true;
@@ -906,11 +976,11 @@ AlchemySelect.setMethod(function loadRemote(page) {
906
976
  throw err;
907
977
  }
908
978
 
909
- if (page == null) {
979
+ if (page == null || page == 1) {
910
980
  Hawkejs.removeChildren(that.dropdown_content);
911
981
  }
912
982
 
913
- that._processResponseData(data);
983
+ that._processResponseData(data, page);
914
984
 
915
985
  pledge.resolve(true);
916
986
  };
@@ -1001,7 +1071,19 @@ AlchemySelect.setMethod(function _markSelectedItems() {
1001
1071
  }
1002
1072
  }
1003
1073
  }
1074
+ });
1004
1075
 
1076
+ /**
1077
+ * Set the last successful loaded page
1078
+ *
1079
+ * @author Jelle De Loecker <jelle@develry.be>
1080
+ * @since 0.1.8
1081
+ * @version 0.1.8
1082
+ *
1083
+ * @param {Number} page
1084
+ */
1085
+ AlchemySelect.setMethod(function setLastSuccessfulLoadedPage(page) {
1086
+ this.last_page_result = page;
1005
1087
  });
1006
1088
 
1007
1089
  /**
@@ -1009,15 +1091,13 @@ AlchemySelect.setMethod(function _markSelectedItems() {
1009
1091
  *
1010
1092
  * @author Jelle De Loecker <jelle@develry.be>
1011
1093
  * @since 0.1.0
1012
- * @version 0.1.0
1094
+ * @version 0.1.8
1013
1095
  *
1014
1096
  * @param {Number} page
1015
1097
  */
1016
1098
  AlchemySelect.setMethod(function loadOptions(page) {
1017
1099
 
1018
- var that = this,
1019
- model,
1020
- field;
1100
+ const that = this;
1021
1101
 
1022
1102
  if (this.total_item_count >= this.loaded_item_count) {
1023
1103
  if (this.dropdown_content.children.length == 0) {
@@ -1029,12 +1109,34 @@ AlchemySelect.setMethod(function loadOptions(page) {
1029
1109
  return;
1030
1110
  }
1031
1111
 
1112
+ // Prevent it from keeping on loading empty pages if the previous page was empty
1113
+ if (page && this.last_page_result) {
1114
+ let next_allowed_page = this.last_page_result + 1;
1115
+
1116
+ if (page > next_allowed_page) {
1117
+ return;
1118
+ }
1119
+ }
1120
+
1121
+ // Previous page will only be set to the last page that had items.
1122
+ // So if there are 0 results overall, it won't ever be set.
1123
+ // In that case: ignore any page load over 1.
1124
+ if (page > 1 && !this.last_page_result) {
1125
+ return;
1126
+ }
1127
+
1128
+ // If stuff has already been loaded and this is just re-opening the dropdown,
1129
+ // don't do anything yet
1130
+ if (page == null && this.last_page_result > 0) {
1131
+ return;
1132
+ }
1133
+
1032
1134
  // If an URL is provided, use that
1033
1135
  if (this.src || this.dataprovider) {
1034
1136
  return this.loadRemote(page);
1035
1137
  }
1036
1138
 
1037
- field = this.model_field;
1139
+ let field = this.model_field;
1038
1140
 
1039
1141
  if (!field) {
1040
1142
  return;
@@ -1042,7 +1144,7 @@ AlchemySelect.setMethod(function loadOptions(page) {
1042
1144
 
1043
1145
  this.loading_dropdown = true;
1044
1146
 
1045
- model = this.form.getModel(field.modelName);
1147
+ let model = this.form.getModel(field.modelName);
1046
1148
 
1047
1149
  model.find('all', {
1048
1150
  limit : 20,
@@ -1053,12 +1155,7 @@ AlchemySelect.setMethod(function loadOptions(page) {
1053
1155
  throw err;
1054
1156
  }
1055
1157
 
1056
- records.forEach(function eachRecord(record) {
1057
- var item = that._makeOption(record._id || record.id, record);
1058
- that.addToDropdown(item);
1059
- });
1060
-
1061
- that.loading_dropdown = false;
1158
+ that._processResponseList(records, page);
1062
1159
  });
1063
1160
  });
1064
1161
 
@@ -1699,9 +1796,10 @@ AlchemySelect.setMethod(function applyLocalFilter(query) {
1699
1796
  *
1700
1797
  * @author Jelle De Loecker <jelle@develry.be>
1701
1798
  * @since 0.1.0
1702
- * @version 0.1.0
1799
+ * @version 0.1.8
1703
1800
  */
1704
1801
  AlchemySelect.setMethod('refreshRemote', Fn.throttle(function refreshRemote() {
1802
+ this.loaded_page = 0;
1705
1803
  this.loadRemote();
1706
1804
  }, {
1707
1805
  minimum_wait : 350,
@@ -589,12 +589,55 @@ Table.setMethod(function showPagination() {
589
589
  pager.showPage(records.page, Math.ceil(records.available / records.page_size));
590
590
  });
591
591
 
592
+ /**
593
+ * Get a fieldset's preferred content view
594
+ *
595
+ * @author Jelle De Loecker <jelle@elevenways.be>
596
+ * @since 0.1.8
597
+ * @version 0.1.10
598
+ *
599
+ * @param {FieldConfig} field_config The config on how to display the field
600
+ * @param {Object} container The container where the field should be in
601
+ *
602
+ * @return
603
+ */
604
+ Table.setMethod(function getFieldConfigView(field_config, container) {
605
+
606
+ let value = field_config.getValueIn(container),
607
+ field;
608
+
609
+ try {
610
+ field = field_config.getFieldDefinition();
611
+ } catch (err) {
612
+ console.error('Failed to get field definition:', err);
613
+ }
614
+
615
+ if (value == null && !field) {
616
+ return null;
617
+ }
618
+
619
+ // Fallback to simple text view
620
+ if (!field) {
621
+ let text = field_config.getDisplayValueIn(container);
622
+
623
+ return Hawkejs.createText(text);
624
+ }
625
+
626
+ let alchemy_field = this.createElement('alchemy-field');
627
+ alchemy_field.view_type = 'view_inline';
628
+ alchemy_field.field_name = field.name;
629
+ alchemy_field.config = field;
630
+ alchemy_field.original_value = value;
631
+
632
+ return alchemy_field;
633
+ });
634
+
592
635
  /**
593
636
  * Create a datarow
594
637
  *
595
638
  * @author Jelle De Loecker <jelle@elevenways.be>
596
639
  * @since 0.1.0
597
- * @version 0.1.6
640
+ * @version 0.1.8
598
641
  *
599
642
  * @param {Object} entry
600
643
  *
@@ -602,7 +645,7 @@ Table.setMethod(function showPagination() {
602
645
  */
603
646
  Table.setMethod(function createDataRow(entry) {
604
647
 
605
- let field,
648
+ let field_set_config,
606
649
  value,
607
650
  tr = this.createElement('tr'),
608
651
  td,
@@ -610,13 +653,13 @@ Table.setMethod(function createDataRow(entry) {
610
653
 
611
654
  tr.dataset.pk = id;
612
655
 
613
- for (field of this.fieldset) {
656
+ for (field_set_config of this.fieldset) {
614
657
  td = this.createElement('td');
615
658
 
616
- value = field.getDisplayValueIn(entry);
659
+ let element = this.getFieldConfigView(field_set_config, entry);
617
660
 
618
- if (value != null) {
619
- td.textContent = value;
661
+ if (element) {
662
+ td.append(element);
620
663
  }
621
664
 
622
665
  tr.append(td);
@@ -709,7 +752,7 @@ Table.setMethod(function getEntryActions(record, filter) {
709
752
  *
710
753
  * @author Jelle De Loecker <jelle@elevenways.be>
711
754
  * @since 0.1.6
712
- * @version 0.1.7
755
+ * @version 0.1.8
713
756
  *
714
757
  * @param {Object} entry
715
758
  */
@@ -745,6 +788,13 @@ Table.setMethod(function attachContextMenus() {
745
788
 
746
789
  tr.addEventListener('contextmenu', e => {
747
790
 
791
+ // Ignore right clicks on certain elements
792
+ if (e && e.target) {
793
+ if (e.target.closest('a')) {
794
+ return;
795
+ }
796
+ }
797
+
748
798
  this.selectRow(tr);
749
799
 
750
800
  let menu = this.createElement('he-context-menu');
@@ -67,7 +67,7 @@ CodeInput.setProperty(function value(value) {
67
67
  *
68
68
  * @author Jelle De Loecker <jelle@elevenways.be>
69
69
  * @since 0.1.0
70
- * @version 0.1.0
70
+ * @version 0.1.10
71
71
  */
72
72
  CodeInput.setMethod(async function introduced() {
73
73
 
@@ -75,6 +75,8 @@ CodeInput.setMethod(async function introduced() {
75
75
 
76
76
  let editor_el = this.querySelector('.code-editor');
77
77
 
78
+ editor_el.hidden = false;
79
+
78
80
  let editor = ace.edit(editor_el);
79
81
 
80
82
  editor.session.setUseWrapMode(true);
@@ -73,7 +73,7 @@ AlchemyField.enforceProperty(function alchemy_form(new_value) {
73
73
  *
74
74
  * @author Jelle De Loecker <jelle@elevenways.be>
75
75
  * @since 0.1.0
76
- * @version 0.1.4
76
+ * @version 0.1.9
77
77
  */
78
78
  AlchemyField.setMethod(function populateWidget() {
79
79
 
@@ -101,6 +101,10 @@ AlchemyField.setMethod(function populateWidget() {
101
101
  field_el.widget_settings = config.widget_settings;
102
102
  }
103
103
 
104
+ if (config.data_src) {
105
+ field_el.data_src = config.data_src;
106
+ }
107
+
104
108
  this.element.append(field_el);
105
109
  });
106
110
 
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "alchemy-form",
3
3
  "description": "Form plugin for Alchemy",
4
- "version": "0.1.7",
4
+ "version": "0.1.10",
5
5
  "repository": {
6
6
  "type" : "git",
7
7
  "url" : "https://github.com/11ways/alchemy-form.git"
8
8
  },
9
9
  "peerDependencies": {
10
- "alchemymvc" : "~1.2.0"
10
+ "alchemymvc" : ">=1.2.0"
11
11
  },
12
12
  "license": "MIT",
13
13
  "engines": {
14
- "node" : ">=12.0.0"
14
+ "node" : ">=14.0.0"
15
15
  }
16
16
  }
@@ -1 +1 @@
1
- <div class="code-editor"><% Blast.Classes.Hawkejs.replaceChildren($0, child_nodes) %></div>
1
+ <div class="code-editor" hidden><% Blast.Classes.Hawkejs.replaceChildren($0, child_nodes) %></div>
@@ -0,0 +1,36 @@
1
+ <label>
2
+ <div class="inputlabel">
3
+ <span class="label" data-he-slot="label"></span>
4
+ <span class="description" data-he-slot="description"></span>
5
+ <hr class="spacer">
6
+ </div>
7
+ <div class="inputbox">
8
+ <input
9
+ name=<% self.getAttribute('input-name') || 'name' %>
10
+ placeholder=<% self.getAttribute('placeholder') %>
11
+ minlength=<% self.getAttribute('min-length') %>
12
+ maxlength=<% self.getAttribute('max-length') %>
13
+ class="input"
14
+ value="<% self.getAttribute('value') || '' %>"
15
+ type="password"
16
+ >
17
+ <i class="icon cross"></i>
18
+ <i class="icon checkmark"></i>
19
+ </div>
20
+ </label>
21
+ <label class="repeat-input-label" hidden>
22
+ <div class="inputbox">
23
+ <input
24
+ placeholder=<% self.getAttribute('repeat-placeholder') %>
25
+ class="input repeat-input"
26
+ value="<% self.getAttribute('value') || '' %>"
27
+ minlength=<% self.getAttribute('min-length') %>
28
+ maxlength=<% self.getAttribute('max-length') %>
29
+ type="password"
30
+ >
31
+ <i class="icon cross"></i>
32
+ <i class="icon checkmark"></i>
33
+ </div>
34
+ <div class="errors"></div>
35
+ <div class="success"></div>
36
+ </label>
@@ -2,6 +2,6 @@
2
2
  class="alchemy-field-value"
3
3
  ><%
4
4
  $0.dataprovider = alchemy_field;
5
- $0.value = value;
6
5
  $0.name = path;
6
+ $0.value = value;
7
7
  %></alchemy-select>
@@ -0,0 +1,5 @@
1
+ <alchemy-code-input
2
+ class="alchemy-field-value"
3
+ form=<% form_id %>
4
+ name=<% path %>
5
+ >{{ value }}</alchemy-code-input>
@@ -0,0 +1 @@
1
+ <% include('form/inputs/view_inline/datetime') %>
@@ -0,0 +1 @@
1
+ <span class="alchemy-field-value">{{ value }}</span>
@@ -0,0 +1,4 @@
1
+ <time
2
+ class="alchemy-field-value"
3
+ datetime={% value.toISOString() %}
4
+ >{{ value.format('Y‑m‑d H:i') }}</time>
@@ -0,0 +1,6 @@
1
+ {% if value %}
2
+ <img
3
+ !Media={% value %}
4
+ +media-route="Media::thumb"
5
+ >
6
+ {% /if %}
@@ -0,0 +1 @@
1
+ <span class="alchemy-field-value">{{ value }}</span>
@@ -0,0 +1 @@
1
+ <div data-he-name="field"></div>