alchemy-form 0.2.2 → 0.2.4

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,19 @@
1
+ ## 0.2.4 (2023-02-26)
2
+
3
+ * Don't look for a field when the path is not specified
4
+ * Make `al-field-schema` elements use `Field#getSubSchema()` method
5
+ * Add an icon to the translatable-field entry buttons to see if it has a translation or not
6
+
7
+ ## 0.2.3 (2023-02-11)
8
+
9
+ * Use `Scene#pushToHistory()` instead of directly calling `pushState`
10
+ * Select `display_field_select` fields when requesting related data
11
+ * Make local-filtering in `al-select` elements case insensitive
12
+ * Fix `al-select` never getting more than the first page in `getRemoteFetchConfig` method
13
+ * Add `hasOneParent` field support
14
+ * Make `FormApi#related()` action make sure fields exist before querying them
15
+ * Add a workaround so `al-select` actually refreshes elements when removing the search value
16
+
1
17
  ## 0.2.2 (2023-01-23)
2
18
 
3
19
  * Fix `al-button`'s `activate` event not being cancelable
@@ -3,6 +3,46 @@ al-field-translatable {
3
3
  position: relative;
4
4
 
5
5
  .prefix-buttons {
6
-
6
+ button {
7
+ display: flex;
8
+ flex-flow: row;
9
+ }
10
+
11
+ .has-empty-content {
12
+ font-size: 0.7rem;
13
+ }
14
+
15
+ .has-content {
16
+ font-size: 0.9rem;
17
+ }
18
+
19
+ .has-empty-content,
20
+ .has-content {
21
+ margin-left: 0.5rem;
22
+ }
23
+
24
+ [data-has-content="true"] {
25
+ .has-empty-content {
26
+ display: none;
27
+ }
28
+ }
29
+
30
+ [data-has-content="false"] {
31
+ .has-content {
32
+ display: none;
33
+ }
34
+ }
35
+
36
+ [icon-name] {
37
+ color: #333;
38
+ }
39
+
40
+ [icon-name="ban"] {
41
+ color: tomato;
42
+ }
43
+
44
+ .has-content {
45
+ color: green;
46
+ }
7
47
  }
8
48
  }
@@ -17,7 +17,7 @@ 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.8
20
+ * @version 0.2.3
21
21
  *
22
22
  * @param {Conduit} conduit
23
23
  */
@@ -34,16 +34,34 @@ FormApi.setAction(async function related(conduit) {
34
34
  if (config.value) {
35
35
  crit.where(model.primary_key).equals(config.value);
36
36
  } else if (config.search) {
37
- let display_fields = Array.cast(model.displayField);
37
+ let display_fields = Array.cast(model.display_field),
38
+ search_fields = [];
38
39
 
39
40
  let or = crit.or();
40
41
  let rx = RegExp.interpretWildcard('*' + body.config.search + '*', 'i');
41
42
 
42
43
  for (let field of display_fields) {
43
- if (!field) {
44
+ if (!field || !model.getField(field)) {
44
45
  continue;
45
46
  }
47
+ search_fields.push(field);
48
+ }
49
+
50
+ if (!search_fields.length) {
51
+ if (model.getField('title')) {
52
+ search_fields.push('title');
53
+ }
54
+
55
+ if (model.getField('name')) {
56
+ search_fields.push('name');
57
+ }
46
58
 
59
+ if (model.getField('slug')) {
60
+ search_fields.push('slug');
61
+ }
62
+ }
63
+
64
+ for (let field of search_fields) {
47
65
  or.where(field).equals(rx);
48
66
  }
49
67
  }
@@ -52,6 +70,10 @@ FormApi.setAction(async function related(conduit) {
52
70
  crit.page(config.page);
53
71
  }
54
72
 
73
+ if (model.display_field_select) {
74
+ crit.select(model.display_field_select);
75
+ }
76
+
55
77
  let records = await model.find('all', crit);
56
78
 
57
79
  let result = {
@@ -21,47 +21,39 @@ FieldSchema.setTemplateFile('form/elements/alchemy_field_schema');
21
21
  *
22
22
  * @author Jelle De Loecker <jelle@elevenways.be>
23
23
  * @since 0.1.0
24
- * @version 0.1.12
24
+ * @version 0.2.4
25
25
  */
26
26
  FieldSchema.setProperty(function schema() {
27
27
 
28
- if (this.alchemy_field && this.alchemy_field.config.options) {
29
- let schema = this.alchemy_field.config.options.schema;
28
+ const field_element = this.alchemy_field;
29
+ const field = field_element?.config;
30
+
31
+ if (field?.options) {
32
+
33
+ let schema = field.options.schema;
30
34
 
31
35
  if (typeof schema == 'string') {
32
- let other_field = this.getSchemaSupplierField();
33
- schema = null;
34
-
35
- if (other_field && other_field.value && other_field.config && other_field.config.options) {
36
- let values = other_field.config.options.values;
37
-
38
- if (values) {
39
- let value;
40
-
41
- if (values instanceof Classes.Alchemy.Map.Backed) {
42
- value = values.get(other_field.value);
43
- } else {
44
- value = values[other_field.value];
45
- }
46
-
47
- if (value) {
48
- if (value.schema) {
49
- schema = value.schema;
50
- } else if (value.value) {
51
- // Enumified values can be wrapped on the server-side
52
- value = value.value;
53
-
54
- if (value.schema) {
55
- schema = value.schema;
56
- }
57
- }
58
- }
59
-
60
- if (!schema) {
61
- schema = value;
62
- }
36
+
37
+ let parent_schema_value;
38
+
39
+ let parent_schema = field_element.queryParents('al-field[field-type="schema"]');
40
+
41
+ if (parent_schema) {
42
+ parent_schema_value = parent_schema.value;
43
+ } else {
44
+
45
+ const form = field_element.alchemy_form;
46
+
47
+ let record_value = form.value;
48
+
49
+ if (form.model) {
50
+ record_value = record_value[form.model];
63
51
  }
52
+
53
+ parent_schema_value = record_value;
64
54
  }
55
+
56
+ return field.getSubschema(parent_schema_value, schema)
65
57
  }
66
58
 
67
59
  return schema;
@@ -75,7 +75,44 @@ FieldTranslatable.setMethod(function showPrefix(prefix) {
75
75
  element.hidden = true;
76
76
  }
77
77
  }
78
+ });
79
+
80
+ /**
81
+ * Check the translation contents
82
+ *
83
+ * @author Jelle De Loecker <jelle@elevenways.be>
84
+ * @since 0.2.4
85
+ * @version 0.2.4
86
+ */
87
+ FieldTranslatable.setMethod(function checkTranslationContents() {
88
+
89
+ let field = this.field_context?.config;
90
+
91
+ let has_content,
92
+ buttons = this.querySelectorAll('button[data-has-content][data-prefix]'),
93
+ button,
94
+ prefix,
95
+ values = this.value,
96
+ value,
97
+ i;
98
+
99
+ for (i = 0; i < buttons.length; i++) {
100
+ button = buttons[i];
101
+ prefix = button.dataset.prefix;
102
+ value = values?.[prefix];
103
+
104
+ if (field) {
105
+ has_content = field.valueHasContent(value);
106
+ } else {
107
+ has_content = value != null && value !== '';
108
+ }
78
109
 
110
+ if (has_content) {
111
+ button.dataset.hasContent = 'true';
112
+ } else {
113
+ button.dataset.hasContent = 'false';
114
+ }
115
+ }
79
116
  });
80
117
 
81
118
  /**
@@ -83,15 +120,37 @@ FieldTranslatable.setMethod(function showPrefix(prefix) {
83
120
  *
84
121
  * @author Jelle De Loecker <jelle@elevenways.be>
85
122
  * @since 0.1.0
86
- * @version 0.1.0
123
+ * @version 0.2.4
87
124
  */
88
125
  FieldTranslatable.setMethod(function introduced() {
89
126
 
90
- const that = this;
127
+ const that = this,
128
+ field = this.field_context;
129
+
130
+ let doTranslationCheck = Function.throttle(() => {
131
+
132
+ if (field?.purpose == 'view') {
133
+ return;
134
+ }
135
+
136
+ that.checkTranslationContents();
137
+ }, 500, true, true);
91
138
 
92
139
  this.onEventSelector('click', '.prefix-buttons button[data-prefix]', function onClick(e) {
140
+
93
141
  e.preventDefault();
94
- that.showPrefix(this.dataset.prefix);
142
+
143
+ let button = this.closest('[data-prefix]');
144
+
145
+ if (!button) {
146
+ return;
147
+ }
148
+
149
+ that.showPrefix(button.dataset.prefix);
150
+ doTranslationCheck();
95
151
  });
96
152
 
153
+ this.addEventListener('change', doTranslationCheck);
154
+ this.addEventListener('keyup', doTranslationCheck);
155
+ this.addEventListener('click', doTranslationCheck);
97
156
  });
@@ -25,6 +25,22 @@ FieldTranslatableEntry.setTemplateFile('form/elements/alchemy_field_translatable
25
25
  */
26
26
  FieldTranslatableEntry.setAttribute('prefix');
27
27
 
28
+ /**
29
+ * Does this translation entry have a valid value?
30
+ *
31
+ * @author Jelle De Loecker <jelle@elevenways.be>
32
+ * @since 0.2.4
33
+ * @version 0.2.4
34
+ */
35
+ FieldTranslatableEntry.setProperty(function has_content() {
36
+
37
+ let field_translatable_el = this.field_context,
38
+ field_el = field_translatable_el.field_context,
39
+ field = field_el.config;
40
+
41
+ field.valueHasContent(this.value);
42
+ });
43
+
28
44
  /**
29
45
  * Get the original value
30
46
  *
@@ -34,7 +50,9 @@ FieldTranslatableEntry.setAttribute('prefix');
34
50
  */
35
51
  FieldTranslatableEntry.setProperty(function original_value() {
36
52
 
37
- let context_value = this.field_context.original_value;
53
+ let field_translatable_el = this.field_context;
54
+
55
+ let context_value = field_translatable_el.original_value;
38
56
 
39
57
  if (context_value) {
40
58
  return context_value[this.prefix];
@@ -289,7 +289,7 @@ Form.setMethod(async function showViolations(err) {
289
289
  *
290
290
  * @author Jelle De Loecker <jelle@elevenways.be>
291
291
  * @since 0.1.0
292
- * @version 0.2.0
292
+ * @version 0.2.4
293
293
  *
294
294
  * @param {String} path
295
295
  *
@@ -297,6 +297,10 @@ Form.setMethod(async function showViolations(err) {
297
297
  */
298
298
  Form.setMethod(function findFieldByPath(path) {
299
299
 
300
+ if (!path) {
301
+ return;
302
+ }
303
+
300
304
  let current = this,
301
305
  result,
302
306
  pieces = path.split('.'),
@@ -924,12 +924,12 @@ AlchemySelect.setMethod(function _processResponseList(list, page) {
924
924
  *
925
925
  * @author Jelle De Loecker <jelle@elevenways.be>
926
926
  * @since 0.2.0
927
- * @version 0.2.0
927
+ * @version 0.2.3
928
928
  */
929
929
  AlchemySelect.setMethod(function getRemoteFetchConfig() {
930
930
 
931
931
  // Don't perform request when everything has already been loaded
932
- if (this.total_item_count >= this.loaded_item_count) {
932
+ if (this.loaded_item_count >= this.total_item_count) {
933
933
  return;
934
934
  }
935
935
 
@@ -970,7 +970,7 @@ AlchemySelect.setMethod(function applyFetchedData(err, result, config) {
970
970
  *
971
971
  * @author Jelle De Loecker <jelle@develry.be>
972
972
  * @since 0.1.0
973
- * @version 0.1.4
973
+ * @version 0.2.3
974
974
  */
975
975
  AlchemySelect.setMethod(function recreateDropdownElements() {
976
976
 
@@ -979,10 +979,12 @@ AlchemySelect.setMethod(function recreateDropdownElements() {
979
979
 
980
980
  Hawkejs.removeChildren(this.dropdown_content);
981
981
 
982
- for (let key of items.keys()) {
983
- item = items.get(key);
984
- item = this._makeOption(item.id, item);
985
- this.addToDropdown(item);
982
+ if (items && typeof items.keys == 'function') {
983
+ for (let key of items.keys()) {
984
+ item = items.get(key);
985
+ item = this._makeOption(item.id, item);
986
+ this.addToDropdown(item);
987
+ }
986
988
  }
987
989
 
988
990
  this.refreshResultAmount();
@@ -1667,7 +1669,7 @@ AlchemySelect.setMethod(function onTypeAreaKeydown(e, is_input) {
1667
1669
  *
1668
1670
  * @author Jelle De Loecker <jelle@develry.be>
1669
1671
  * @since 0.1.0
1670
- * @version 0.1.0
1672
+ * @version 0.2.3
1671
1673
  */
1672
1674
  AlchemySelect.setMethod(function onTypeAreaKeyup(e, is_input) {
1673
1675
 
@@ -1683,15 +1685,20 @@ AlchemySelect.setMethod(function onTypeAreaKeyup(e, is_input) {
1683
1685
  return;
1684
1686
  }
1685
1687
 
1686
- if (this.search_value) {
1688
+ let search_value = this.search_value,
1689
+ previous = this._previous_typed_search_value || '';
1687
1690
 
1691
+ if (previous != search_value) {
1688
1692
  // Only perform a remote search
1689
1693
  // when at least 2 characters have been entered
1690
- if (this.search_value.length > 2) {
1694
+ if (search_value.length === 0 || search_value.length > 2) {
1691
1695
  this.refreshRemote();
1692
1696
  }
1697
+
1693
1698
  }
1694
1699
 
1700
+ this._previous_typed_search_value = search_value;
1701
+
1695
1702
  this.applyLocalFilter();
1696
1703
  });
1697
1704
 
@@ -1700,7 +1707,7 @@ AlchemySelect.setMethod(function onTypeAreaKeyup(e, is_input) {
1700
1707
  *
1701
1708
  * @author Jelle De Loecker <jelle@develry.be>
1702
1709
  * @since 0.1.0
1703
- * @version 0.1.0
1710
+ * @version 0.2.3
1704
1711
  */
1705
1712
  AlchemySelect.setMethod(function applyLocalFilter(query) {
1706
1713
 
@@ -1713,7 +1720,7 @@ AlchemySelect.setMethod(function applyLocalFilter(query) {
1713
1720
  i;
1714
1721
 
1715
1722
  if (query) {
1716
- query = query.trim();
1723
+ query = query.trim().toLowerCase();
1717
1724
  }
1718
1725
 
1719
1726
  for (i = 0; i < this.dropdown_content.children.length; i++) {
@@ -1722,10 +1729,22 @@ AlchemySelect.setMethod(function applyLocalFilter(query) {
1722
1729
 
1723
1730
  if (!query) {
1724
1731
  allow = true;
1725
- } else if (String(element.value).indexOf(query) > -1) {
1726
- allow = true;
1727
- } else if (element.textContent.indexOf(query) > -1) {
1728
- allow = true;
1732
+ }
1733
+
1734
+ if (!allow) {
1735
+ let value = String(element.value).toLowerCase();
1736
+
1737
+ if (value.includes(query)) {
1738
+ allow = true;
1739
+ }
1740
+ }
1741
+
1742
+ if (!allow) {
1743
+ let text = element.textContent.toLowerCase();
1744
+
1745
+ if (text.includes(query)) {
1746
+ allow = true;
1747
+ }
1729
1748
  }
1730
1749
 
1731
1750
  if (allow) {
@@ -1121,7 +1121,7 @@ Table.setMethod(function getRemoteFetchConfig() {
1121
1121
  *
1122
1122
  * @author Jelle De Loecker <jelle@elevenways.be>
1123
1123
  * @since 0.2.0
1124
- * @version 0.2.0
1124
+ * @version 0.2.3
1125
1125
  */
1126
1126
  Table.setMethod(function applyFetchedData(err, result, config) {
1127
1127
 
@@ -1142,8 +1142,7 @@ Table.setMethod(function applyFetchedData(err, result, config) {
1142
1142
  this.updateAnchors();
1143
1143
 
1144
1144
  if (Blast.isBrowser) {
1145
- let current_url = this.getCurrentStateUrl();
1146
- history.pushState(null, document.title, current_url+'');
1145
+ hawkejs.scene.pushToHistory(null, this.getCurrentStateUrl());
1147
1146
  }
1148
1147
  });
1149
1148
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemy-form",
3
3
  "description": "Form plugin for Alchemy",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "repository": {
6
6
  "type" : "git",
7
7
  "url" : "https://github.com/11ways/alchemy-form.git"
@@ -8,9 +8,24 @@
8
8
  <div class="prefix-buttons">
9
9
  <% index = 0 %>
10
10
  <% for (prefix in prefixes) { %>
11
- <button data-prefix=<% prefix %>>
11
+ <% value = null %>
12
+ <% if (values) value = values[prefix] %>
13
+
14
+ <button
15
+ data-prefix={% prefix %}
16
+ data-has-content=<% alchemy_field.config.valueHasContent(value) ? 'true' : 'false' %>
17
+ >
12
18
  <% if (index == 0) $0.classList.add('active') %>
13
- {%= prefix %}
19
+
20
+ <span class="prefix-name">{{ prefix }}</span>
21
+
22
+ <al-icon-stack class="has-empty-content">
23
+ <al-icon icon-name="language"></al-icon>
24
+ <al-icon icon-name="ban" size=2></al-icon>
25
+ </al-icon-stack>
26
+
27
+ <al-icon class="has-content" icon-name="language"></al-icon>
28
+
14
29
  </button>
15
30
 
16
31
  <% index++ %>
@@ -0,0 +1,7 @@
1
+ <al-select
2
+ class="alchemy-field-value"
3
+ ><%
4
+ $0.dataprovider = alchemy_field;
5
+ $0.name = path;
6
+ $0.value = value;
7
+ %></al-select>