apostrophe 3.27.0 → 3.28.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.28.0 (2022-08-31)
4
+
5
+ ### Fixes
6
+
7
+ * Fix UI bug when creating a document via a relationship.
8
+
9
+ ### Adds
10
+
11
+ * Support for uploading `webp` files for display as images. This is supported by all current browsers now that Microsoft has removed IE11. For best results, you should run `npm update` on your project to make sure you are receiving the latest release of `uploadfs` which uses `sharp` for image processing. Thanks to [Isaac Preston](https://github.com/ixc7) for this addition.
12
+ * Clicking outside a modal now closes it, the same way the `Escape` key does when pressed.
13
+ * `checkboxes` fields now support `min` and `max` properties. Thanks to [Gabe Flores](https://github.com/gabeflores-appstem).
14
+
3
15
  ## 3.27.0 (2022-08-18)
4
16
 
5
17
  ### Adds
@@ -54,7 +54,8 @@ module.exports = {
54
54
  'gif',
55
55
  'jpg',
56
56
  'png',
57
- 'svg'
57
+ 'svg',
58
+ 'webp'
58
59
  ],
59
60
  extensionMaps: { jpeg: 'jpg' },
60
61
  // uploadfs should treat this as an image and create scaled versions
@@ -90,14 +91,16 @@ module.exports = {
90
91
  self.croppable = {
91
92
  gif: true,
92
93
  jpg: true,
93
- png: true
94
+ png: true,
95
+ webp: true
94
96
  };
95
97
 
96
98
  // Do NOT add keys here unless they have the value `true`
97
99
  self.sized = {
98
100
  gif: true,
99
101
  jpg: true,
100
- png: true
102
+ png: true,
103
+ webp: true
101
104
  };
102
105
 
103
106
  self.sizeAvailableInArchive = self.options.sizeAvailableInArchive || 'one-sixth';
@@ -1018,7 +1021,7 @@ module.exports = {
1018
1021
  return;
1019
1022
  }
1020
1023
  let sizes;
1021
- if (![ 'gif', 'jpg', 'png' ].includes(self.resolveExtension(attachment.extension))) {
1024
+ if (![ 'gif', 'jpg', 'png', 'webp' ].includes(self.resolveExtension(attachment.extension))) {
1022
1025
  sizes = [ { name: 'original' } ];
1023
1026
  } else {
1024
1027
  sizes = self.imageSizes.concat([ { name: 'original' } ]);
@@ -12,7 +12,7 @@ module.exports = function(self) {
12
12
  const total = await self.db.count();
13
13
  let n = 0;
14
14
  await self.each({}, argv.parallel || 1, async function(file) {
15
- if (!_.includes([ 'jpg', 'png', 'gif' ], file.extension)) {
15
+ if (!_.includes([ 'jpg', 'png', 'gif', 'webp' ], file.extension)) {
16
16
  n++;
17
17
  console.log('Skipping a non-image attachment: ' + file.name + '.' + file.extension);
18
18
  return;
@@ -727,6 +727,9 @@ export default {
727
727
  window.localStorage.setItem(this.savePreferenceName, pref);
728
728
  },
729
729
  onContentChanged(e) {
730
+ if (this.original?._id !== e.doc._id) {
731
+ return;
732
+ }
730
733
  if (e.doc.type !== this.docType) {
731
734
  this.docType = e.doc.type;
732
735
  }
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <transition
3
3
  :name="transitionType"
4
- @enter="finishEnter"
5
- @leave="finishExit"
4
+ @enter="onEnter"
5
+ @leave="onLeave"
6
6
  :duration="250"
7
7
  >
8
8
  <section
@@ -14,12 +14,18 @@
14
14
  ref="modalEl"
15
15
  >
16
16
  <transition :name="transitionType">
17
- <div class="apos-modal__overlay" v-if="modal.showModal" />
17
+ <div
18
+ @click="close"
19
+ v-if="modal.showModal"
20
+ class="apos-modal__overlay"
21
+ />
18
22
  </transition>
19
23
  <transition :name="transitionType" @after-leave="$emit('inactive')">
20
24
  <div
21
- v-if="modal.showModal" :class="innerClasses"
22
- class="apos-modal__inner" data-apos-modal-inner
25
+ v-if="modal.showModal"
26
+ :class="innerClasses"
27
+ class="apos-modal__inner"
28
+ data-apos-modal-inner
23
29
  >
24
30
  <template v-if="modal.busy">
25
31
  <div class="apos-modal__busy">
@@ -180,16 +186,13 @@ export default {
180
186
  }
181
187
  },
182
188
  methods: {
183
- esc (e) {
184
- if (apos.modal.stack[apos.modal.stack.length - 1] !== this) {
185
- return;
186
- }
187
- if (e.keyCode === 27) {
188
- e.stopPropagation();
189
- this.$emit('esc');
189
+ onKeydown (e) {
190
+ const hasPressedEsc = e.keyCode === 27;
191
+ if (hasPressedEsc) {
192
+ this.close(e);
190
193
  }
191
194
  },
192
- finishEnter () {
195
+ onEnter () {
193
196
  this.$emit('show-modal');
194
197
  this.bindEventListeners();
195
198
  apos.modal.stack = apos.modal.stack || [];
@@ -198,7 +201,7 @@ export default {
198
201
  this.$emit('ready');
199
202
  });
200
203
  },
201
- finishExit () {
204
+ onLeave () {
202
205
  this.removeEventListeners();
203
206
  this.$emit('no-modal');
204
207
  // pop doesn't quite suffice because of race conditions when
@@ -206,10 +209,17 @@ export default {
206
209
  apos.modal.stack = apos.modal.stack.filter(modal => modal !== this);
207
210
  },
208
211
  bindEventListeners () {
209
- window.addEventListener('keydown', this.esc);
212
+ window.addEventListener('keydown', this.onKeydown);
210
213
  },
211
214
  removeEventListeners () {
212
- window.removeEventListener('keydown', this.esc);
215
+ window.removeEventListener('keydown', this.onKeydown);
216
+ },
217
+ close (e) {
218
+ if (apos.modal.stack[apos.modal.stack.length - 1] !== this) {
219
+ return;
220
+ }
221
+ e.stopPropagation();
222
+ this.$emit('esc');
213
223
  },
214
224
  trapFocus () {
215
225
  // Adapted from https://uxdesign.cc/how-to-trap-focus-inside-modal-to-make-it-ada-compliant-6a50f9a70700
@@ -254,6 +254,13 @@ module.exports = (self) => {
254
254
  });
255
255
  }
256
256
  }
257
+
258
+ if ((field.min !== undefined) && (destination[field.name].length < field.min)) {
259
+ throw self.apos.error('min');
260
+ }
261
+ if ((field.max !== undefined) && (destination[field.name].length > field.max)) {
262
+ throw self.apos.error('max');
263
+ }
257
264
  },
258
265
  index: function (value, field, texts) {
259
266
  const silent = field.silent === undefined ? true : field.silent;
@@ -300,6 +307,14 @@ module.exports = (self) => {
300
307
  return choices;
301
308
  }
302
309
  });
310
+ },
311
+ validate: function (field, options, warn, fail) {
312
+ if (field.max && typeof field.max !== 'number') {
313
+ fail('Property "max" must be a number');
314
+ }
315
+ if (field.min && typeof field.min !== 'number') {
316
+ fail('Property "min" must be a number');
317
+ }
303
318
  }
304
319
  });
305
320
 
@@ -47,6 +47,17 @@ export default {
47
47
  return 'required';
48
48
  }
49
49
 
50
+ if (this.field.min) {
51
+ if ((values != null) && (values.length < this.field.min)) {
52
+ return 'min';
53
+ }
54
+ }
55
+ if (this.field.max) {
56
+ if ((values != null) && (values.length > this.field.max)) {
57
+ return 'max';
58
+ }
59
+ }
60
+
50
61
  if (Array.isArray(values)) {
51
62
  values.forEach(chosen => {
52
63
  if (!this.choices.map(choice => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apostrophe",
3
- "version": "3.27.0",
3
+ "version": "3.28.0",
4
4
  "description": "The Apostrophe Content Management System.",
5
5
  "main": "index.js",
6
6
  "scripts": {