apostrophe 4.28.0 → 4.29.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 (88) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +142 -0
  3. package/defaults.js +1 -0
  4. package/lib/safe-json-script.js +27 -0
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +1 -1
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +1 -0
  7. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +3 -5
  8. package/modules/@apostrophecms/area/ui/apos/components/AposBreadcrumbOperations.vue +13 -1
  9. package/modules/@apostrophecms/asset/lib/globalIcons.js +3 -0
  10. package/modules/@apostrophecms/attachment/index.js +43 -1
  11. package/modules/@apostrophecms/color-field/index.js +7 -1
  12. package/modules/@apostrophecms/doc/index.js +11 -1
  13. package/modules/@apostrophecms/doc-type/index.js +165 -32
  14. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +1 -1
  15. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +104 -59
  16. package/modules/@apostrophecms/file/index.js +109 -9
  17. package/modules/@apostrophecms/i18n/i18n/de.json +0 -2
  18. package/modules/@apostrophecms/i18n/i18n/en.json +40 -1
  19. package/modules/@apostrophecms/i18n/i18n/es.json +0 -1
  20. package/modules/@apostrophecms/i18n/i18n/fr.json +0 -1
  21. package/modules/@apostrophecms/i18n/i18n/it.json +0 -1
  22. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
  23. package/modules/@apostrophecms/i18n/i18n/sk.json +0 -1
  24. package/modules/@apostrophecms/i18n/ui/apos/apps/AposI18nBatchReporting.js +18 -1
  25. package/modules/@apostrophecms/i18n/ui/apos/apps/AposI18nLocalizeActions.js +50 -0
  26. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +56 -13
  27. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +8 -2
  28. package/modules/@apostrophecms/layout-column-widget/index.js +156 -163
  29. package/modules/@apostrophecms/layout-widget/index.js +7 -2
  30. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposAreaLayoutEditor.vue +6 -11
  31. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridColumn.vue +3 -5
  32. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridLayout.vue +4 -4
  33. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridManager.vue +0 -16
  34. package/modules/@apostrophecms/layout-widget/ui/apos/lib/grid-state.mjs +7 -27
  35. package/modules/@apostrophecms/layout-widget/views/column.html +7 -9
  36. package/modules/@apostrophecms/login/index.js +39 -40
  37. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +17 -2
  38. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +3 -2
  39. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +1 -0
  40. package/modules/@apostrophecms/page/index.js +2 -0
  41. package/modules/@apostrophecms/piece-type/index.js +3 -1
  42. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  43. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +5 -0
  44. package/modules/@apostrophecms/recently-edited/index.js +831 -0
  45. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposCellTitle.vue +54 -0
  46. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedCombo.vue +454 -0
  47. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedFilterTag.vue +75 -0
  48. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedFilters.vue +287 -0
  49. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedIcon.vue +16 -0
  50. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedManager.vue +346 -0
  51. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedBatch.js +193 -0
  52. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedData.js +276 -0
  53. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedFetch.js +199 -0
  54. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedFilters.js +100 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +8 -4
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +1 -1
  57. package/modules/@apostrophecms/styles/index.js +10 -0
  58. package/modules/@apostrophecms/styles/lib/apiRoutes.js +6 -0
  59. package/modules/@apostrophecms/styles/lib/handlers.js +5 -0
  60. package/modules/@apostrophecms/styles/lib/methods.js +9 -3
  61. package/modules/@apostrophecms/styles/lib/presets.js +119 -0
  62. package/modules/@apostrophecms/styles/ui/apos/components/TheAposStyles.vue +3 -8
  63. package/modules/@apostrophecms/styles/ui/apos/composables/AposStyles.js +1 -3
  64. package/modules/@apostrophecms/styles/ui/apos/render-factory.js +29 -0
  65. package/modules/@apostrophecms/styles/ui/apos/universal/backgroundHelpers.mjs +140 -0
  66. package/modules/@apostrophecms/styles/ui/apos/universal/customRules.mjs +105 -0
  67. package/modules/@apostrophecms/styles/ui/apos/universal/render.mjs +195 -15
  68. package/modules/@apostrophecms/template/index.js +22 -6
  69. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +2 -0
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +18 -4
  71. package/modules/@apostrophecms/ui/ui/apos/composables/useInfiniteScroll.js +91 -0
  72. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
  73. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +5 -2
  74. package/modules/@apostrophecms/ui/ui/apos/utils/index.js +9 -0
  75. package/modules/@apostrophecms/url/index.js +38 -4
  76. package/modules/@apostrophecms/widget-type/index.js +22 -6
  77. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +8 -4
  78. package/package.json +19 -19
  79. package/test/files.js +129 -0
  80. package/test/layout-widget-migration.js +719 -0
  81. package/test/login-requirements.js +1 -1
  82. package/test/pieces-public-api.js +80 -0
  83. package/test/pieces.js +25 -0
  84. package/test/recently-edited.js +2311 -0
  85. package/test/schemas.js +39 -3
  86. package/test/static-build.js +642 -0
  87. package/test/styles.js +2569 -0
  88. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposLayoutColControlDialog.vue +0 -171
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <span
3
+ class="apos-recently-edited__cell-title"
4
+ data-apos-test="recently-edited-cell-title"
5
+ >
6
+ <button
7
+ class="apos-table__cell-field"
8
+ :class="`apos-table__cell-field--${header.name}`"
9
+ type="button"
10
+ >
11
+ {{ get(header.name) }}
12
+ </button>
13
+ <a
14
+ v-if="item._url"
15
+ :href="item._url"
16
+ class="apos-recently-edited__cell-link"
17
+ data-apos-test="recently-edited-cell-link"
18
+ @click.stop
19
+ >
20
+ <AposIndicator
21
+ icon="arrow-top-right-icon"
22
+ :icon-size="14"
23
+ decorative
24
+ />
25
+ </a>
26
+ </span>
27
+ </template>
28
+
29
+ <script>
30
+ import AposCellMixin from 'Modules/@apostrophecms/ui/mixins/AposCellMixin';
31
+
32
+ export default {
33
+ name: 'AposCellTitle',
34
+ mixins: [ AposCellMixin ]
35
+ };
36
+ </script>
37
+
38
+ <style scoped>
39
+ .apos-recently-edited__cell-title {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: 5px;
43
+ }
44
+
45
+ .apos-recently-edited__cell-link {
46
+ display: inline-flex;
47
+ flex-shrink: 0;
48
+ color: var(--a-base-5);
49
+ }
50
+
51
+ .apos-recently-edited__cell-link:hover {
52
+ color: var(--a-primary);
53
+ }
54
+ </style>
@@ -0,0 +1,454 @@
1
+ <template>
2
+ <AposInputWrapper
3
+ :field="field"
4
+ :error="null"
5
+ :uid="uid"
6
+ :modifiers="modifiers"
7
+ :display-options="{}"
8
+ >
9
+ <template #body>
10
+ <div
11
+ ref="root"
12
+ class="apos-input-wrapper"
13
+ :class="modifierClasses"
14
+ :data-apos-test="'recently-edited-combo-' + field.name"
15
+ @keyup.esc="onEscKeyup"
16
+ >
17
+ <ul
18
+ ref="selectEl"
19
+ class="apos-input apos-input--select apos-combo-filter__select"
20
+ :class="{ 'apos-combo-filter__select--has-tags': selected.length }"
21
+ role="combobox"
22
+ :aria-expanded="isOpen.toString()"
23
+ aria-haspopup="listbox"
24
+ :aria-owns="listId"
25
+ :aria-activedescendant="activeDescendantId"
26
+ :aria-label="$t(field.label || 'apostrophe:filter')"
27
+ tabindex="0"
28
+ @click="toggle"
29
+ @keydown="onKeydown"
30
+ >
31
+ <li
32
+ v-if="!selected.length"
33
+ class="apos-combo-filter__placeholder"
34
+ aria-hidden="true"
35
+ >
36
+ {{ $t('apostrophe:any') }}
37
+ </li>
38
+ <li
39
+ v-for="(val, tagIndex) in selected"
40
+ :key="val"
41
+ class="apos-combo-filter__tag"
42
+ tabindex="0"
43
+ role="button"
44
+ :aria-label="`${choiceLabel(val)} — ${$t('apostrophe:remove')}`"
45
+ @mousedown.stop.prevent="removeValue(val)"
46
+ @click.stop
47
+ @keydown="onTagKeydown($event, val, tagIndex)"
48
+ >
49
+ {{ choiceLabel(val) }}
50
+ <AposIndicator
51
+ icon="close-icon"
52
+ :icon-size="10"
53
+ aria-hidden="true"
54
+ />
55
+ </li>
56
+ </ul>
57
+ <AposIndicator
58
+ icon="plus-icon"
59
+ class="apos-input-icon"
60
+ :icon-size="14"
61
+ :title="$t(addLabel)"
62
+ aria-hidden="true"
63
+ />
64
+ <!-- Dropdown: only unselected choices -->
65
+ <ul
66
+ v-show="isOpen"
67
+ :id="listId"
68
+ class="apos-combo-filter__list"
69
+ data-apos-test="recently-edited-combo-list"
70
+ role="listbox"
71
+ :aria-label="$t(field.label || 'apostrophe:filter')"
72
+ >
73
+ <li
74
+ :id="optionId(-1)"
75
+ class="apos-combo-filter__item"
76
+ role="option"
77
+ :aria-selected="(!selected.length).toString()"
78
+ :class="{ 'is-focused': focusedIndex === -1 }"
79
+ @mousedown.stop.prevent="clearAndClose"
80
+ @click.stop
81
+ @mouseover="focusedIndex = -1"
82
+ >
83
+ {{ $t('apostrophe:any') }}
84
+ </li>
85
+ <li
86
+ v-for="(choice, i) in availableChoices"
87
+ :id="optionId(i)"
88
+ :key="choice.value"
89
+ class="apos-combo-filter__item"
90
+ role="option"
91
+ aria-selected="false"
92
+ :class="{ 'is-focused': focusedIndex === i }"
93
+ @mousedown.stop.prevent="addChoice(choice)"
94
+ @click.stop
95
+ @mouseover="focusedIndex = i"
96
+ >
97
+ {{ choice.label }}
98
+ </li>
99
+ </ul>
100
+ </div>
101
+ </template>
102
+ </AposInputWrapper>
103
+ </template>
104
+
105
+ <script setup>
106
+ // "Add-to-list" multi-select combo for filter panels.
107
+ // Reuses the standard .apos-input / .apos-input--select / .apos-input-wrapper
108
+ // CSS classes so sizing, padding, and modifiers (small, micro) all match the
109
+ // sibling AposInputSelect dropdowns automatically.
110
+ import {
111
+ computed, nextTick, onBeforeUnmount, ref
112
+ } from 'vue';
113
+ import { createId } from '@paralleldrive/cuid2';
114
+
115
+ const props = defineProps({
116
+ field: {
117
+ type: Object,
118
+ required: true
119
+ },
120
+ modelValue: {
121
+ type: Object,
122
+ required: true
123
+ },
124
+ status: {
125
+ type: Object,
126
+ default: () => ({})
127
+ },
128
+ modifiers: {
129
+ type: Array,
130
+ default: () => []
131
+ },
132
+ addLabel: {
133
+ type: String,
134
+ default: 'apostrophe:addItem'
135
+ },
136
+ // Accepted to avoid Vue "unknown prop" warnings — the parent
137
+ // passes it for AposInputSelect compatibility.
138
+ noBlurEmit: {
139
+ type: Boolean,
140
+ default: false
141
+ }
142
+ });
143
+
144
+ const emit = defineEmits([ 'update:modelValue' ]);
145
+
146
+ const uid = createId();
147
+ const listId = `combo-list-${uid}`;
148
+ const root = ref(null);
149
+ const selectEl = ref(null);
150
+ const isOpen = ref(false);
151
+ const focusedIndex = ref(null);
152
+ const escConsumed = ref(false);
153
+
154
+ const choices = computed(() => props.field?.choices || []);
155
+ const selected = computed(() => props.modelValue?.data || []);
156
+
157
+ const modifierClasses = computed(() =>
158
+ props.modifiers.reduce((acc, mod) => {
159
+ acc[`apos-combo-filter--${mod}`] = true;
160
+ return acc;
161
+ }, {})
162
+ );
163
+
164
+ const availableChoices = computed(() =>
165
+ choices.value.filter(c => !selected.value.includes(c.value))
166
+ );
167
+
168
+ function optionId(index) {
169
+ return `${listId}-opt-${index}`;
170
+ }
171
+
172
+ const activeDescendantId = computed(() => {
173
+ if (!isOpen.value || focusedIndex.value == null) {
174
+ return undefined;
175
+ }
176
+ return optionId(focusedIndex.value);
177
+ });
178
+
179
+ function choiceLabel(val) {
180
+ const choice = choices.value.find(c => c.value === val);
181
+ return choice?.label ?? val;
182
+ }
183
+
184
+ onBeforeUnmount(() => {
185
+ document.removeEventListener('mousedown', onDocumentMousedown, true);
186
+ });
187
+
188
+ // --- Open / Close ---
189
+
190
+ function open() {
191
+ if (isOpen.value) {
192
+ return;
193
+ }
194
+ isOpen.value = true;
195
+ focusedIndex.value = -1;
196
+ document.addEventListener('mousedown', onDocumentMousedown, true);
197
+ }
198
+
199
+ function close() {
200
+ if (!isOpen.value) {
201
+ return;
202
+ }
203
+ isOpen.value = false;
204
+ focusedIndex.value = null;
205
+ document.removeEventListener('mousedown', onDocumentMousedown, true);
206
+ }
207
+
208
+ function toggle() {
209
+ isOpen.value ? close() : open();
210
+ }
211
+
212
+ function onDocumentMousedown(event) {
213
+ if (root.value && !root.value.contains(event.target)) {
214
+ close();
215
+ }
216
+ }
217
+
218
+ // --- Keyboard navigation ---
219
+
220
+ function onKeydown(event) {
221
+ const { key } = event;
222
+
223
+ if (key === 'Escape') {
224
+ if (isOpen.value) {
225
+ event.preventDefault();
226
+ event.stopPropagation();
227
+ escConsumed.value = true;
228
+ close();
229
+ }
230
+ return;
231
+ }
232
+
233
+ if (key === 'Tab') {
234
+ close();
235
+ return;
236
+ }
237
+
238
+ if (!isOpen.value) {
239
+ if (key === ' ' || key === 'Enter' || key === 'ArrowDown' || key === 'ArrowUp') {
240
+ event.preventDefault();
241
+ open();
242
+ }
243
+ return;
244
+ }
245
+
246
+ // Dropdown is open — navigate or select.
247
+ const maxIndex = availableChoices.value.length - 1;
248
+
249
+ if (key === 'ArrowDown') {
250
+ event.preventDefault();
251
+ if (focusedIndex.value == null || focusedIndex.value >= maxIndex) {
252
+ focusedIndex.value = -1;
253
+ } else {
254
+ focusedIndex.value++;
255
+ }
256
+ scrollFocusedIntoView();
257
+ return;
258
+ }
259
+
260
+ if (key === 'ArrowUp') {
261
+ event.preventDefault();
262
+ if (focusedIndex.value == null || focusedIndex.value <= -1) {
263
+ focusedIndex.value = maxIndex;
264
+ } else {
265
+ focusedIndex.value--;
266
+ }
267
+ scrollFocusedIntoView();
268
+ return;
269
+ }
270
+
271
+ if (key === ' ' || key === 'Enter') {
272
+ event.preventDefault();
273
+ selectFocused();
274
+ }
275
+ }
276
+
277
+ function onEscKeyup(event) {
278
+ if (escConsumed.value) {
279
+ event.stopPropagation();
280
+ escConsumed.value = false;
281
+ }
282
+ }
283
+
284
+ function scrollFocusedIntoView() {
285
+ const el = root.value?.querySelector(`#${optionId(focusedIndex.value)}`);
286
+ el?.scrollIntoView({ block: 'nearest' });
287
+ }
288
+
289
+ function selectFocused() {
290
+ if (focusedIndex.value === -1) {
291
+ clearAndClose();
292
+ } else if (
293
+ focusedIndex.value != null &&
294
+ focusedIndex.value >= 0 &&
295
+ focusedIndex.value < availableChoices.value.length
296
+ ) {
297
+ addChoice(availableChoices.value[focusedIndex.value]);
298
+ }
299
+ }
300
+
301
+ // --- Selection ---
302
+
303
+ function emitSelection(data) {
304
+ emit('update:modelValue', { data });
305
+ }
306
+
307
+ function addChoice(choice) {
308
+ emitSelection([ ...selected.value, choice.value ]);
309
+ close();
310
+ }
311
+
312
+ function removeValue(val) {
313
+ emitSelection(selected.value.filter(v => v !== val));
314
+ }
315
+
316
+ function removeValueAndRefocus(val, tagIndex) {
317
+ const newSelected = selected.value.filter(v => v !== val);
318
+ emitSelection(newSelected);
319
+ nextTick(() => {
320
+ const tags = root.value?.querySelectorAll('.apos-combo-filter__tag');
321
+ if (!tags || tags.length === 0) {
322
+ selectEl.value?.focus();
323
+ } else if (tagIndex < tags.length) {
324
+ tags[tagIndex].focus();
325
+ } else {
326
+ tags[tags.length - 1].focus();
327
+ }
328
+ });
329
+ }
330
+
331
+ function onTagKeydown(event, val, tagIndex) {
332
+ const { key } = event;
333
+ if (key === ' ' || key === 'Enter') {
334
+ event.preventDefault();
335
+ event.stopPropagation();
336
+ removeValueAndRefocus(val, tagIndex);
337
+ return;
338
+ }
339
+ // Prevent arrow keys from bubbling to combobox handler
340
+ // and from scrolling the page behind.
341
+ if (key === 'ArrowDown' || key === 'ArrowUp') {
342
+ event.preventDefault();
343
+ event.stopPropagation();
344
+ }
345
+ }
346
+
347
+ function clearAndClose() {
348
+ emitSelection([]);
349
+ close();
350
+ }
351
+ </script>
352
+
353
+ <style lang="scss" scoped>
354
+
355
+ .apos-combo-filter__select {
356
+ display: flex;
357
+ flex-wrap: wrap;
358
+ align-items: center;
359
+ margin: 0;
360
+ padding-right: $input-padding + 20px;
361
+ gap: 4px;
362
+ list-style: none;
363
+ cursor: pointer;
364
+ user-select: none;
365
+
366
+ &--has-tags {
367
+ padding: ($input-padding - $spacing-half) ($input-padding + 20px) ($input-padding - $spacing-half) ($input-padding - $spacing-half);
368
+ }
369
+ }
370
+
371
+ // --- Modifier overrides (small, small+inline) ---
372
+
373
+ .apos-combo-filter--small {
374
+ .apos-combo-filter__select--has-tags {
375
+ padding: $spacing-half ($input-padding + 20px) $spacing-half $spacing-three-quarters;
376
+ }
377
+ }
378
+
379
+ .apos-combo-filter--small.apos-combo-filter--inline {
380
+ .apos-combo-filter__select {
381
+ padding-right: $spacing-half + 20px;
382
+ }
383
+
384
+ .apos-combo-filter__select--has-tags {
385
+ padding: $spacing-one-quarter ($spacing-half + 20px) $spacing-one-quarter $spacing-three-quarters;
386
+ }
387
+ }
388
+
389
+ .apos-combo-filter__placeholder {
390
+ pointer-events: none;
391
+ }
392
+
393
+ // Adapt the icon wrapper so that we have the standard dropdown
394
+ // 20x20 area, while our icon is 14x14 (because it looks weirdly huge at 20x20).
395
+ :deep(.apos-input-icon) {
396
+ width: 20px;
397
+ height: 20px;
398
+ }
399
+
400
+ .apos-combo-filter__tag {
401
+ @include type-base;
402
+
403
+ & {
404
+ display: flex;
405
+ align-items: center;
406
+ gap: 4px;
407
+ padding: 5px 8px;
408
+ border: 1px solid var(--a-base-8);
409
+ border-radius: var(--a-border-radius);
410
+ background-color: var(--a-white);
411
+ cursor: pointer;
412
+ }
413
+
414
+ &:hover {
415
+ border-color: var(--a-base-3);
416
+ background-color: var(--a-base-8);
417
+ }
418
+
419
+ :deep(.apos-indicator) {
420
+ width: 10px;
421
+ height: 10px;
422
+ }
423
+ }
424
+
425
+ .apos-combo-filter__list {
426
+ z-index: $z-index-manager-display;
427
+ position: absolute;
428
+ top: 100%;
429
+ left: 0;
430
+ width: 100%;
431
+ max-height: 300px;
432
+ margin: 0;
433
+ padding-left: 0;
434
+ list-style: none;
435
+ overflow-y: auto;
436
+ user-select: none;
437
+ background-color: var(--a-white);
438
+ box-shadow: 0 0 3px var(--a-base-2);
439
+ border-radius: var(--a-border-radius);
440
+ }
441
+
442
+ .apos-combo-filter__item {
443
+ @include type-base;
444
+
445
+ & {
446
+ padding: 10px;
447
+ cursor: pointer;
448
+ }
449
+
450
+ &.is-focused {
451
+ background-color: var(--a-base-9);
452
+ }
453
+ }
454
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ class="apos-recently-edited-filter-tag"
5
+ data-apos-test="recently-edited-tag"
6
+ :data-apos-test-tag="tag.name"
7
+ @click="$emit('clear', tag.name, tag.value)"
8
+ >
9
+ <close-icon
10
+ class="apos-recently-edited-filter-tag__close-icon"
11
+ :size="10"
12
+ title=""
13
+ />
14
+ <span class="apos-recently-edited-filter-tag__label">
15
+ {{ $t(tag.label) }}
16
+ </span>
17
+ <span class="apos-recently-edited-filter-tag__value">
18
+ {{ tag.valueLabel }}
19
+ </span>
20
+ </button>
21
+ </template>
22
+
23
+ <script setup>
24
+ defineProps({
25
+ tag: {
26
+ type: Object,
27
+ required: true
28
+ }
29
+ });
30
+
31
+ defineEmits([ 'clear' ]);
32
+ </script>
33
+
34
+ <style lang="scss" scoped>
35
+ .apos-recently-edited-filter-tag {
36
+ @include apos-button-reset();
37
+ @include type-base;
38
+
39
+ & {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: 5px;
43
+ padding: 8px 10px;
44
+ border: 1px solid var(--a-base-8);
45
+ border-radius: var(--a-border-radius);
46
+ background-color: var(--a-white);
47
+ white-space: nowrap;
48
+ line-height: 1.1;
49
+ }
50
+
51
+ &:hover {
52
+ border-color: var(--a-base-5);
53
+
54
+ .apos-recently-edited-filter-tag__close-icon {
55
+ color: var(--a-primary);
56
+ }
57
+ }
58
+ }
59
+
60
+ .apos-recently-edited-filter-tag__close-icon {
61
+ position: relative;
62
+ top: 1px;
63
+ color: var(--a-base-4);
64
+ }
65
+
66
+ .apos-recently-edited-filter-tag__label {
67
+ color: var(--a-base-4);
68
+ }
69
+
70
+ .apos-recently-edited-filter-tag__value {
71
+ overflow: hidden;
72
+ max-width: 200px;
73
+ text-overflow: ellipsis;
74
+ }
75
+ </style>