apostrophe 4.0.0 → 4.1.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/defaults.js +2 -1
  3. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +10 -10
  4. package/modules/@apostrophecms/attachment/index.js +2 -1
  5. package/modules/@apostrophecms/attachment/public/img/missing-icon.svg +14 -0
  6. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +1 -1
  7. package/modules/@apostrophecms/doc-type/index.js +34 -13
  8. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +3 -3
  9. package/modules/@apostrophecms/i18n/i18n/en.json +13 -0
  10. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +173 -6
  11. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +3 -2
  12. package/modules/@apostrophecms/login/index.js +18 -1
  13. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +2 -2
  14. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +1 -16
  15. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +9 -9
  16. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +4 -4
  17. package/modules/@apostrophecms/modal/ui/apos/components/AposModalLip.vue +2 -2
  18. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +4 -3
  19. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +4 -2
  20. package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +8 -6
  21. package/modules/@apostrophecms/page/index.js +1 -0
  22. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +9 -5
  23. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapButton.vue +1 -1
  24. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +1 -1
  25. package/modules/@apostrophecms/schema/index.js +69 -8
  26. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +1 -1
  27. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +6 -4
  28. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +9 -6
  29. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +1 -1
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +15 -12
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +28 -19
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
  33. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +1 -1
  34. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +2 -2
  35. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +2 -2
  36. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +4 -4
  37. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +4 -1
  38. package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +1 -1
  39. package/modules/@apostrophecms/task/index.js +2 -0
  40. package/modules/@apostrophecms/translation/index.js +233 -0
  41. package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +84 -0
  42. package/modules/@apostrophecms/ui/ui/apos/components/AposAvatar.vue +2 -1
  43. package/modules/@apostrophecms/ui/ui/apos/components/AposCellButton.vue +2 -1
  44. package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +49 -5
  45. package/modules/@apostrophecms/ui/ui/apos/components/AposCloudUploadIcon.vue +10 -5
  46. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +3 -5
  47. package/modules/@apostrophecms/ui/ui/apos/components/AposEmptyState.vue +3 -3
  48. package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +1 -1
  49. package/modules/@apostrophecms/ui/ui/apos/components/AposLabel.vue +1 -1
  50. package/modules/@apostrophecms/ui/ui/apos/components/AposPagerDots.vue +2 -1
  51. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +11 -10
  52. package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +2 -2
  53. package/modules/@apostrophecms/ui/ui/apos/components/AposTag.vue +3 -2
  54. package/modules/@apostrophecms/ui/ui/apos/components/AposTagListItem.vue +2 -1
  55. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +1 -1
  56. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
  57. package/package.json +3 -3
  58. package/test/attachments.js +5 -0
  59. package/test/schemas.js +138 -0
  60. package/test/translation.js +538 -0
  61. package/test-lib/util.js +21 -0
@@ -12,22 +12,7 @@ export default {
12
12
  return {
13
13
  phase: 'beforeSubmit',
14
14
  busy: false,
15
- schema: [
16
- {
17
- name: 'username',
18
- label: 'Username',
19
- placeholder: 'Enter username',
20
- type: 'string',
21
- required: true
22
- },
23
- {
24
- name: 'password',
25
- label: 'Password',
26
- placeholder: 'Enter password',
27
- type: 'password',
28
- required: true
29
- }
30
- ],
15
+ schema: apos.login.schema,
31
16
  requirements: getRequirements(),
32
17
  requirementProps: {},
33
18
  fetchingRequirementProps: false
@@ -1,26 +1,26 @@
1
1
  <template>
2
2
  <transition
3
3
  :name="transitionType"
4
+ :duration="250"
4
5
  @enter="onEnter"
5
6
  @leave="onLeave"
6
- :duration="250"
7
7
  >
8
8
  <section
9
9
  v-if="modal.active"
10
+ ref="modalEl"
10
11
  :class="classes"
11
12
  role="dialog"
12
13
  aria-modal="true"
13
14
  :aria-labelledby="id"
14
- ref="modalEl"
15
+ data-apos-modal
15
16
  @keydown="cycleElementsToFocus"
16
17
  @focus.capture="storeFocusedElement"
17
- data-apos-modal
18
18
  >
19
19
  <transition :name="transitionType">
20
20
  <div
21
- @click="close"
22
21
  v-if="modal.showModal"
23
22
  class="apos-modal__overlay"
23
+ @click="close"
24
24
  />
25
25
  </transition>
26
26
  <transition :name="transitionType" @after-leave="$emit('inactive')">
@@ -39,7 +39,7 @@
39
39
  </div>
40
40
  </template>
41
41
  <template v-else>
42
- <header class="apos-modal__header" v-if="!modal.disableHeader">
42
+ <header v-if="!modal.disableHeader" class="apos-modal__header">
43
43
  <div class="apos-modal__header__main">
44
44
  <div v-if="hasSecondaryControls" class="apos-modal__controls--secondary">
45
45
  <slot name="secondaryControls" />
@@ -50,20 +50,20 @@
50
50
  </span>
51
51
  {{ $t(modalTitle) }}
52
52
  </h2>
53
- <div class="apos-modal__controls--header" v-if="hasBeenLocalized || hasPrimaryControls">
54
- <div class="apos-modal__locale" v-if="hasBeenLocalized">
53
+ <div v-if="hasBeenLocalized || hasPrimaryControls" class="apos-modal__controls--header">
54
+ <div v-if="hasBeenLocalized" class="apos-modal__locale">
55
55
  <span class="apos-modal__locale-label">
56
56
  {{ $t('apostrophe:locale') }}:
57
57
  </span> <span class="apos-modal__locale-name">
58
58
  {{ currentLocale }}
59
59
  </span>
60
60
  </div>
61
- <div class="apos-modal__controls--primary" v-if="hasPrimaryControls">
61
+ <div v-if="hasPrimaryControls" class="apos-modal__controls--primary">
62
62
  <slot name="primaryControls" />
63
63
  </div>
64
64
  </div>
65
65
  </div>
66
- <div class="apos-modal__breadcrumbs" v-if="hasBreadcrumbs">
66
+ <div v-if="hasBreadcrumbs" class="apos-modal__breadcrumbs">
67
67
  <slot class="apos-modal__breadcrumbs" name="breadcrumbs" />
68
68
  </div>
69
69
  </header>
@@ -28,8 +28,8 @@
28
28
  {{ localize(content.description) }}
29
29
  </p>
30
30
  <Component
31
- v-if="content.body"
32
31
  :is="content.body.component"
32
+ v-if="content.body"
33
33
  v-bind="content.body.props"
34
34
  />
35
35
  <div v-if="content.form" class="apos-confirm__schema">
@@ -48,15 +48,15 @@
48
48
  @click="cancel"
49
49
  />
50
50
  <AposButton
51
+ ref="confirm"
51
52
  class="apos-confirm__btn"
52
53
  :label="affirmativeLabel"
53
- @click="confirm"
54
54
  :type="content.theme || 'primary'"
55
55
  :disabled="isDisabled"
56
- ref="confirm"
56
+ @click="confirm"
57
57
  />
58
58
  </div>
59
- <p class="apos-confirm__note" v-if="content.note">
59
+ <p v-if="content.note" class="apos-confirm__note">
60
60
  {{ localize(content.note) }}
61
61
  </p>
62
62
  </template>
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <!-- Disabling since the SVG is mostly not active vue template code. -->
3
3
  <!-- eslint-disable vue/max-attributes-per-line -->
4
- <div class="apos-modal-lip" ref="lip">
4
+ <div ref="lip" class="apos-modal-lip">
5
5
  <div class="apos-modal-lip__shadow">
6
6
  <svg width="406px" height="56px" viewBox="0 0 406 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
7
7
  <title>Shadow</title>
8
8
  <defs>
9
9
  <rect id="shadow-path-1" x="0" y="0" width="406" height="56" />
10
10
  <rect id="shadow-path-3" x="-13" y="20" width="432" height="83" />
11
- <filter x="-6.2%" y="-28.9%" width="112.5%" height="165.1%" filterUnits="objectBoundingBox" id="shadow-filter-4">
11
+ <filter id="shadow-filter-4" x="-6.2%" y="-28.9%" width="112.5%" height="165.1%" filterUnits="objectBoundingBox">
12
12
  <feMorphology radius="3" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
13
13
  <feOffset dx="0" dy="0" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
14
14
  <feGaussianBlur stdDeviation="6.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
@@ -2,13 +2,14 @@
2
2
  <div class="apos-modal-tabs">
3
3
  <ul class="apos-modal-tabs__tabs">
4
4
  <li
5
- class="apos-modal-tabs__tab"
6
5
  v-for="tab in tabs"
7
- :key="tab.name"
8
6
  v-show="tab.isVisible !== false"
7
+ :key="tab.name"
8
+ class="apos-modal-tabs__tab"
9
9
  >
10
10
  <button
11
- :id="tab.name" class="apos-modal-tabs__btn"
11
+ :id="tab.name"
12
+ class="apos-modal-tabs__btn"
12
13
  :aria-selected="tab.name === current ? true : false"
13
14
  @click="selectTab"
14
15
  >
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <transition-group
3
- name="list" tag="div"
4
- class="apos-notifications" :class="themeClass"
3
+ name="list"
4
+ tag="div"
5
+ class="apos-notifications"
6
+ :class="themeClass"
5
7
  >
6
8
  <AposNotification
7
9
  v-for="notification in notifications"
@@ -1,35 +1,37 @@
1
1
  <template>
2
2
  <AposInputWrapper
3
- :modifiers="modifiers" :field="field"
4
- :error="effectiveError" :uid="uid"
3
+ :modifiers="modifiers"
4
+ :field="field"
5
+ :error="effectiveError"
6
+ :uid="uid"
5
7
  :display-options="displayOptions"
6
8
  >
7
9
  <template #body>
8
10
  <div class="apos-input-wrapper">
9
11
  <input
10
- :class="classes"
12
+ :id="uid"
11
13
  v-model="next.url"
14
+ :class="classes"
12
15
  type="url"
13
16
  :placeholder="$t(field.placeholder)"
14
17
  :disabled="field.readOnly"
15
18
  :readonly="tempReadOnly"
16
19
  :required="field.required"
17
- :id="uid"
18
20
  :tabindex="tabindex"
19
21
  >
20
22
  <component
23
+ :is="icon"
21
24
  v-if="icon"
22
25
  :size="iconSize"
23
26
  class="apos-input-icon"
24
- :is="icon"
25
27
  />
26
28
  <!-- eslint-disable vue/no-v-html -->
27
29
  <div
28
30
  v-if="!error && oembedResult.html"
29
- v-html="oembedResult.html"
30
31
  class="apos-input__embed"
31
32
  :class="{ 'apos-is-dynamic': !!dynamicRatio }"
32
33
  :style="{ paddingTop: dynamicRatio && `${(dynamicRatio * 100)}%` }"
34
+ v-html="oembedResult.html"
33
35
  />
34
36
  </div>
35
37
  <!-- eslint-enable vue/no-v-html -->
@@ -702,6 +702,7 @@ database.`);
702
702
  editRole: 'admin',
703
703
  publishRole: 'admin'
704
704
  },
705
+ permissions: {},
705
706
  find(req) {
706
707
  return [];
707
708
  },
@@ -12,15 +12,19 @@
12
12
  {{ selectBoxMessage }}
13
13
  <AposButton
14
14
  v-if="!allPiecesSelection.isSelected"
15
- type="subtle" :modifiers="['inline', 'small', 'no-motion']"
16
- :label="selectBoxMessageButton" class="apos-select-box__select-all"
17
- @click="$emit('select-all')"
15
+ type="subtle"
16
+ :modifiers="['inline', 'small', 'no-motion']"
17
+ :label="selectBoxMessageButton"
18
+ class="apos-select-box__select-all"
18
19
  text-color="var(--a-primary)"
20
+ @click="$emit('select-all')"
19
21
  />
20
22
  <AposButton
21
23
  v-else
22
- type="subtle" :modifiers="['inline', 'small', 'no-motion']"
23
- label="apostrophe:clearSelection" class="apos-select-box__select-all"
24
+ type="subtle"
25
+ :modifiers="['inline', 'small', 'no-motion']"
26
+ label="apostrophe:clearSelection"
27
+ class="apos-select-box__select-all"
24
28
  text-color="var(--a-primary)"
25
29
  @click="clearSelection"
26
30
  />
@@ -1,7 +1,6 @@
1
1
  <template>
2
2
  <AposButton
3
3
  type="rich-text"
4
- @click="click"
5
4
  class="apos-rich-text-editor__control"
6
5
  :class="{ 'apos-is-active': active }"
7
6
  :label="tool.label"
@@ -14,6 +13,7 @@
14
13
  placement: 'top',
15
14
  delay: 650
16
15
  }"
16
+ @click="click"
17
17
  />
18
18
  </template>
19
19
 
@@ -2,7 +2,6 @@
2
2
  <div class="apos-image-control">
3
3
  <AposButton
4
4
  type="rich-text"
5
- @click="click"
6
5
  :class="{ 'apos-is-active': buttonActive }"
7
6
  :label="tool.label"
8
7
  :icon-only="!!tool.icon"
@@ -14,6 +13,7 @@
14
13
  placement: 'top',
15
14
  delay: 650
16
15
  }"
16
+ @click="click"
17
17
  />
18
18
  <AposImageControlDialog
19
19
  :active="active"
@@ -29,6 +29,7 @@ module.exports = {
29
29
  self.arrayManagers = {};
30
30
  self.objectManagers = {};
31
31
  self.fieldMetadataComponents = [];
32
+ self.uiManagerIndicators = [];
32
33
 
33
34
  self.enableBrowserData();
34
35
 
@@ -441,6 +442,13 @@ module.exports = {
441
442
  });
442
443
  },
443
444
 
445
+ // Wrapper around isEqual method to get modified fields between two documents
446
+ // instead of just getting a boolean, it will return an array of the modified fields
447
+
448
+ getChanges(req, schema, one, two) {
449
+ return self.isEqual(req, schema, one, two, { getChanges: true });
450
+ },
451
+
444
452
  // Compare two objects and return true only if their schema fields are equal.
445
453
  //
446
454
  // Note that for relationship fields this comparison is based on the idsStorage
@@ -451,22 +459,40 @@ module.exports = {
451
459
  // This method is invoked by the doc module to compare draft and published
452
460
  // documents and set the modified property of the draft, just before updating the
453
461
  // published version.
462
+ //
463
+ // When passing the option `getChange: true` it'll return an array of changed fields
464
+ // in this case the method won't short circuit by directly returning false
465
+ // when finding a changed field
454
466
 
455
- isEqual(req, schema, one, two) {
467
+ isEqual(req, schema, one, two, options = {}) {
468
+ const changedFields = [];
456
469
  for (const field of schema) {
457
470
  const fieldType = self.fieldTypes[field.type];
458
- if (!fieldType.isEqual) {
459
- if ((!_.isEqual(one[field.name], two[field.name])) &&
460
- !((one[field.name] == null) && (two[field.name] == null))) {
461
- return false;
462
- }
463
- } else {
471
+
472
+ if (fieldType.isEqual) {
464
473
  if (!fieldType.isEqual(req, field, one, two)) {
474
+ if (options.getChanges) {
475
+ changedFields.push(field.name);
476
+ } else {
477
+ return false;
478
+ }
479
+ }
480
+ continue;
481
+ }
482
+
483
+ if (
484
+ !_.isEqual(one[field.name], two[field.name]) &&
485
+ !((one[field.name] == null) && (two[field.name] == null))
486
+ ) {
487
+ if (options.getChanges) {
488
+ changedFields.push(field.name);
489
+ } else {
465
490
  return false;
466
491
  }
467
492
  }
468
493
  }
469
- return true;
494
+
495
+ return options.getChanges ? changedFields : true;
470
496
  },
471
497
 
472
498
  // Index the object's fields for participation in Apostrophe search unless
@@ -1707,6 +1733,40 @@ module.exports = {
1707
1733
  options.allow = '/';
1708
1734
  }
1709
1735
  return options;
1736
+ },
1737
+
1738
+ // Register a Vue component as custom indicator in the UI manager.
1739
+ // The component should be already exist in the admin UI
1740
+ // (created in `ui/apos/components`).
1741
+ // Properties:
1742
+ // - `component`: the name of the Vue component
1743
+ // - `props`: (optional, object) additional props to pass to the component.
1744
+ // - `if`: (optional, object) a standard Apostrophe condition to show/hide
1745
+ // the indicator. Keep in mind the component can also decide internally
1746
+ // to show/hide itself. The condition is evaluated against the draft doc.
1747
+ // The keys represent field names and support dot notation.
1748
+ //
1749
+ // Example:
1750
+ // ```javascript
1751
+ // self.apos.schema.addManagerIndicator({
1752
+ // component: 'MyCustomIndicator',
1753
+ // props: {
1754
+ // label: 'My indicator'
1755
+ // },
1756
+ // if: {
1757
+ // type: 'my-type',
1758
+ // 'myField': 'my-value',
1759
+ // 'myObject.field': 'my-nested-value'
1760
+ // }
1761
+ // });
1762
+ addManagerIndicator({
1763
+ component, props, if: condition
1764
+ }) {
1765
+ self.uiManagerIndicators.push({
1766
+ component,
1767
+ props,
1768
+ if: condition
1769
+ });
1710
1770
  }
1711
1771
  };
1712
1772
  },
@@ -1776,6 +1836,7 @@ module.exports = {
1776
1836
  browserOptions.action = self.action;
1777
1837
  browserOptions.components = { fields: fields };
1778
1838
  browserOptions.fieldMetadataComponents = self.fieldMetadataComponents;
1839
+ browserOptions.customCellIndicators = self.uiManagerIndicators;
1779
1840
  return browserOptions;
1780
1841
  }
1781
1842
  };
@@ -859,7 +859,7 @@ module.exports = (self) => {
859
859
  if (one[field.name].length !== two[field.name].length) {
860
860
  return false;
861
861
  }
862
- for (let i = 0; (i < one.length); i++) {
862
+ for (let i = 0; (i < one[field.name].length); i++) {
863
863
  if (!self.isEqual(req, field.schema, one[field.name][i], two[field.name][i])) {
864
864
  return false;
865
865
  }
@@ -1,19 +1,21 @@
1
1
  <template>
2
2
  <AposInputWrapper
3
- :modifiers="modifiers" :field="field"
4
- :error="effectiveError" :uid="uid"
3
+ :modifiers="modifiers"
4
+ :field="field"
5
+ :error="effectiveError"
6
+ :uid="uid"
5
7
  :display-options="displayOptions"
6
8
  >
7
9
  <template #body>
8
10
  <div class="apos-input-wrapper">
9
11
  <input
12
+ :id="uid"
13
+ v-model="next"
10
14
  type="password"
11
15
  class="apos-input apos-input--password"
12
- v-model="next"
13
16
  :placeholder="$t(field.placeholder)"
14
17
  :disabled="field.readOnly"
15
18
  :required="field.required"
16
- :id="uid"
17
19
  :tabindex="tabindex"
18
20
  :autocomplete="field.autocomplete"
19
21
  @keydown.enter="emitReturn"
@@ -1,20 +1,22 @@
1
1
  <template>
2
2
  <AposInputWrapper
3
- :modifiers="modifiers" :field="field"
4
- :error="effectiveError" :uid="uid"
3
+ :modifiers="modifiers"
4
+ :field="field"
5
+ :error="effectiveError"
6
+ :uid="uid"
5
7
  :display-options="displayOptions"
6
8
  >
7
9
  <template #body>
8
10
  <div class="apos-input-wrapper">
9
- <div class="apos-range" v-apos-tooltip="tooltip">
11
+ <div v-apos-tooltip="tooltip" class="apos-range">
10
12
  <input
13
+ :id="uid"
14
+ v-model="next"
11
15
  type="range"
12
16
  :min="field.min"
13
17
  :max="field.max"
14
18
  :step="field.step"
15
19
  class="apos-range__input"
16
- v-model="next"
17
- :id="uid"
18
20
  :disabled="field.readOnly"
19
21
  >
20
22
  <div class="apos-range__scale">
@@ -39,7 +41,8 @@
39
41
  >
40
42
  {{ valueLabel }}
41
43
  <AposButton
42
- type="quiet" label="apostrophe:clear"
44
+ type="quiet"
45
+ label="apostrophe:clear"
43
46
  class="apos-range__clear"
44
47
  :modifiers="['no-motion']"
45
48
  @click="unset"
@@ -11,9 +11,9 @@
11
11
  <div class="apos-input-wrapper">
12
12
  <textarea
13
13
  v-if="field.textarea && field.type === 'string'"
14
+ :id="uid"
14
15
  v-model="next"
15
16
  rows="5"
16
- :id="uid"
17
17
  :class="classes"
18
18
  :placeholder="$t(field.placeholder)"
19
19
  :disabled="field.readOnly"
@@ -9,10 +9,10 @@
9
9
  <component :is="wrapEl" :class="classList">
10
10
  <div class="apos-field__info">
11
11
  <component
12
+ :is="labelEl"
12
13
  v-if="field.label"
13
14
  class="apos-field__label"
14
15
  :class="{'apos-sr-only': field.hideLabel }"
15
- :is="labelEl"
16
16
  :for="uid"
17
17
  :data-apos-test-name="field.name"
18
18
  :data-apos-test-label="field.label"
@@ -24,8 +24,8 @@
24
24
  *
25
25
  </span>
26
26
  <AposLabel
27
- class="apos-field__tag"
28
27
  v-if="field.tag"
28
+ class="apos-field__tag"
29
29
  :label="field.tag.value || field.tag"
30
30
  :modifiers="[ `apos-is-${field.tag.type || 'success'}`, 'apos-is-filled' ]"
31
31
  data-apos-test="field-tag"
@@ -44,11 +44,13 @@
44
44
  />
45
45
  </span>
46
46
  <span
47
- v-if="displayOptions.changed" class="apos-field__changed"
47
+ v-if="displayOptions.changed"
48
+ class="apos-field__changed"
48
49
  data-apos-test="field-changed"
49
50
  >
50
51
  <AposLabel
51
- label="apostrophe:changed" class="apos-field__changed__label"
52
+ label="apostrophe:changed"
53
+ class="apos-field__changed__label"
52
54
  :modifiers="[ 'apos-is-warning', 'apos-is-filled' ]"
53
55
  tooltip="apostrophe:fieldHasUnpublishedChanges"
54
56
  />
@@ -56,16 +58,16 @@
56
58
  </span>
57
59
  <span data-apos-test="field-meta-wrapper" class="apos-field__label-meta">
58
60
  <component
59
- v-for="component in metaComponents"
60
- :key="component.name"
61
- :is="component.name"
61
+ v-for="{name, namespace, data} in metaComponents"
62
+ :key="name"
63
+ :is="name"
62
64
  :field="field"
63
65
  :items="items"
64
- :namespace="component.namespace"
65
- :meta="component.data"
66
+ :namespace="namespace"
67
+ :meta="data"
66
68
  :meta-raw="meta"
67
- :data-apos-test-component="component.name"
68
- :data-apos-test-namespace="component.namespace"
69
+ :data-apos-test-component="name"
70
+ :data-apos-test-namespace="namespace"
69
71
  data-apos-test="field-meta"
70
72
  />
71
73
  </span>
@@ -81,7 +83,8 @@
81
83
  </div>
82
84
  <slot name="body" />
83
85
  <div
84
- v-if="errorMessage" class="apos-field__error"
86
+ v-if="errorMessage"
87
+ class="apos-field__error"
85
88
  data-apos-test="field-error"
86
89
  >
87
90
  {{ $t(errorMessage) }}
@@ -41,6 +41,7 @@
41
41
  :is="fieldComponentMap[field.type]"
42
42
  :ref="field.name"
43
43
  v-model="fieldState[field.name]"
44
+ :class="{ 'apos-field__wrapper--highlight': highlight(field.name) }"
44
45
  :following-values="followingValues[field.name]"
45
46
  :condition-met="conditionalFields?.if[field.name]"
46
47
  :field="fields[field.name].field"
@@ -60,6 +61,7 @@
60
61
  v-show="displayComponent(field)"
61
62
  :ref="field.name"
62
63
  v-model="compareMetaState[field.name]"
64
+ :class="{ 'apos-field__wrapper--highlight': highlight(field.name) }"
63
65
  :following-values="followingValues[field.name]"
64
66
  :condition-met="conditionalFields?.if[field.name]"
65
67
  :field="fields[field.name].field"
@@ -119,25 +121,32 @@ export default {
119
121
  margin-bottom: 0;
120
122
  }
121
123
 
122
- .apos-schema.apos-schema--compare {
123
- & > ::v-deep [data-apos-field] {
124
- display: flex;
125
-
126
- & > .apos-field__wrapper {
127
- flex-grow: 1;
128
- flex-basis: 50%;
129
- border-right: 1px solid var(--a-base-9);
130
- padding-right: 20px;
131
- }
132
- & > .apos-field__wrapper + .apos-field__wrapper {
133
- border-right: none;
134
- padding-right: 0;
135
- padding-left: 20px;
136
- }
137
-
138
- & .apos-field__label {
139
- word-break: break-all;
140
- }
124
+ .apos-schema.apos-schema--compare > :deep([data-apos-field]) {
125
+ display: flex;
126
+
127
+ &.apos-field--hidden {
128
+ display: none;
129
+ }
130
+
131
+ & > .apos-field__wrapper {
132
+ flex-grow: 1;
133
+ flex-basis: 50%;
134
+ border-right: 1px solid var(--a-base-9);
135
+ padding-right: 20px;
136
+ }
137
+ & > .apos-field__wrapper + .apos-field__wrapper {
138
+ border-right: none;
139
+ padding-right: 0;
140
+ padding-left: 20px;
141
141
  }
142
+
143
+ & .apos-field__label {
144
+ word-break: break-all;
145
+ }
146
+ }
147
+
148
+ :deep(.apos-field__wrapper--highlight > .apos-field) {
149
+ padding: 10px;
150
+ background: var(--a-highlight);
142
151
  }
143
152
  </style>
@@ -3,8 +3,8 @@
3
3
  <li
4
4
  v-for="item in list"
5
5
  :key="item._id"
6
- :class="getClasses(item)"
7
6
  v-apos-tooltip="getTooltip(item)"
7
+ :class="getClasses(item)"
8
8
  @click="select(item, $event)"
9
9
  >
10
10
  <img
@@ -99,7 +99,7 @@ export default {
99
99
  return widgetOptions.minSize || [];
100
100
  },
101
101
  duplicate () {
102
- return this.modelValue.duplicate ? 'apos-input--error' : null;
102
+ return this.modelValue?.duplicate ? 'apos-input--error' : null;
103
103
  }
104
104
  },
105
105
  watch: {
@@ -17,8 +17,8 @@ export default {
17
17
  };
18
18
  },
19
19
  computed: {
20
- classes () {
21
- return [ this.modelValue.duplicate && 'apos-input--error' ];
20
+ classes() {
21
+ return [ this.modelValue?.duplicate && 'apos-input--error' ];
22
22
  }
23
23
  },
24
24
  methods: {