@asd20/ui-next 2.33.2 → 2.34.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 +14 -0
- package/package.json +1 -1
- package/src/components/atoms/Asd20RichBodyContent/index.vue +3 -2
- package/src/components/molecules/Asd20PublicFormField/index.vue +34 -1
- package/src/components/organisms/Asd20MediaSection/index.vue +26 -5
- package/src/components/organisms/Asd20PublicFormModal/index.vue +1 -1
- package/src/components/templates/Asd20ArticleDigestCompactTemplate/index.vue +1 -0
- package/src/components/templates/Asd20ArticleDigestTemplate/index.vue +1 -0
- package/src/components/templates/Asd20ArticleListTemplate/index.vue +1 -0
- package/src/components/templates/Asd20ArticleTemplate/index.vue +3 -2
- package/src/components/templates/Asd20CampusDetailTemplate/index.vue +1 -0
- package/src/components/templates/Asd20ClubsTemplate/index.vue +1 -0
- package/src/components/templates/Asd20DetailAlternateTemplate/index.vue +1 -0
- package/src/components/templates/Asd20DetailImageFullTemplate/index.vue +1 -0
- package/src/components/templates/Asd20DetailImageTemplate/index.vue +1 -0
- package/src/components/templates/Asd20DetailTemplate/index.vue +1 -0
- package/src/components/templates/Asd20FileListPageTemplate/index.vue +1 -0
- package/src/components/templates/Asd20GroupFeatureTemplate/index.vue +1 -0
- package/src/components/templates/Asd20ProfileTemplate/index.vue +1 -0
- package/src/components/templates/Asd20WayfindingAlternateTemplate/index.vue +1 -0
- package/src/components/templates/Asd20WayfindingImageTemplate/index.vue +1 -0
- package/src/components/templates/Asd20WayfindingTemplate/index.vue +1 -0
- package/src/helpers/excludeBodyMedia.js +96 -0
- package/src/helpers/publicFormDefinition.js +31 -13
- package/src/mixins/pageTemplateMixin.js +0 -44
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# [2.34.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.33.3...ui-next-v2.34.0) (2026-10-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* support checkbox groups for school transfer records ([9596d21](https://github.com/academydistrict20/asd20-ui-next/commit/9596d21fea018afec4c03de64471549cb77e9487))
|
|
9
|
+
|
|
10
|
+
## [2.33.3](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.33.2...ui-next-v2.33.3) (2026-10-27)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* limit size of videos in body content; exclude videos and images already in body content from media section ([aba90ce](https://github.com/academydistrict20/asd20-ui-next/commit/aba90ceec4c244a8317cc544972fa753615da982))
|
|
16
|
+
|
|
3
17
|
## [2.33.2](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.33.1...ui-next-v2.33.2) (2026-10-26)
|
|
4
18
|
|
|
5
19
|
## [2.33.1](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.33.0...ui-next-v2.33.1) (2026-10-26)
|
package/package.json
CHANGED
|
@@ -149,6 +149,32 @@
|
|
|
149
149
|
</label>
|
|
150
150
|
</fieldset>
|
|
151
151
|
|
|
152
|
+
<fieldset
|
|
153
|
+
v-else-if="field.type === 'checkbox-group'"
|
|
154
|
+
:id="inputId"
|
|
155
|
+
class="asd20-public-form-field__choices"
|
|
156
|
+
tabindex="-1"
|
|
157
|
+
:aria-describedby="describedBy"
|
|
158
|
+
:aria-invalid="hasErrors ? 'true' : undefined"
|
|
159
|
+
>
|
|
160
|
+
<legend>{{ displayLabel }}</legend>
|
|
161
|
+
<label
|
|
162
|
+
v-for="option in field.options"
|
|
163
|
+
:key="option.value"
|
|
164
|
+
:for="`${inputId}-${option.value}`"
|
|
165
|
+
>
|
|
166
|
+
<input
|
|
167
|
+
:id="`${inputId}-${option.value}`"
|
|
168
|
+
:name="inputId"
|
|
169
|
+
type="checkbox"
|
|
170
|
+
:value="option.value"
|
|
171
|
+
:checked="Array.isArray(modelValue) && modelValue.includes(option.value)"
|
|
172
|
+
@change="toggleChoice(option.value, $event.target.checked)"
|
|
173
|
+
/>
|
|
174
|
+
<span>{{ option.label }}</span>
|
|
175
|
+
</label>
|
|
176
|
+
</fieldset>
|
|
177
|
+
|
|
152
178
|
<asd20-checkbox
|
|
153
179
|
v-else-if="field.type === 'checkbox'"
|
|
154
180
|
:id="inputId"
|
|
@@ -276,6 +302,12 @@ export default {
|
|
|
276
302
|
},
|
|
277
303
|
},
|
|
278
304
|
methods: {
|
|
305
|
+
toggleChoice(value, checked) {
|
|
306
|
+
const selected = Array.isArray(this.modelValue) ? this.modelValue : []
|
|
307
|
+
this.$emit('update:modelValue', checked
|
|
308
|
+
? [...selected.filter(choice => choice !== value), value]
|
|
309
|
+
: selected.filter(choice => choice !== value))
|
|
310
|
+
},
|
|
279
311
|
addFiles(event) {
|
|
280
312
|
const addedFiles = Array.from(event.target.files || [])
|
|
281
313
|
if (addedFiles.length) {
|
|
@@ -407,7 +439,8 @@ export default {
|
|
|
407
439
|
align-items: flex-start;
|
|
408
440
|
}
|
|
409
441
|
|
|
410
|
-
&__choices input[type='radio']
|
|
442
|
+
&__choices input[type='radio'],
|
|
443
|
+
&__choices input[type='checkbox'] {
|
|
411
444
|
flex: 0 0 auto;
|
|
412
445
|
margin: 0;
|
|
413
446
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section
|
|
3
|
-
v-
|
|
3
|
+
v-if="showImageGallery || remainingMedia.videos.length"
|
|
4
4
|
class="asd20-media-section"
|
|
5
5
|
role="complementary"
|
|
6
6
|
>
|
|
7
7
|
<slot name="before" />
|
|
8
8
|
|
|
9
9
|
<asd20-image-gallery
|
|
10
|
-
v-if="
|
|
10
|
+
v-if="showImageGallery"
|
|
11
11
|
title="Images"
|
|
12
|
-
:images="
|
|
12
|
+
:images="galleryImages"
|
|
13
13
|
>
|
|
14
14
|
</asd20-image-gallery>
|
|
15
15
|
|
|
16
16
|
<asd20-videos-section
|
|
17
|
-
v-if="videos.length > 0"
|
|
17
|
+
v-if="remainingMedia.videos.length > 0"
|
|
18
18
|
title="Videos"
|
|
19
|
-
:videos="videos"
|
|
19
|
+
:videos="remainingMedia.videos"
|
|
20
20
|
>
|
|
21
21
|
</asd20-videos-section>
|
|
22
22
|
<slot />
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<script>
|
|
27
27
|
import { defineAsyncComponent } from 'vue'
|
|
28
|
+
import excludeBodyMedia from '../../../helpers/excludeBodyMedia'
|
|
28
29
|
|
|
29
30
|
export default {
|
|
30
31
|
name: 'Asd20MediaSection',
|
|
@@ -40,6 +41,26 @@ export default {
|
|
|
40
41
|
allowSingleImageGallery: { type: Boolean, default: false },
|
|
41
42
|
images: { type: Array, default: () => [] },
|
|
42
43
|
videos: { type: Array, default: () => [] },
|
|
44
|
+
// Include only messages whose bodies are displayed on this page.
|
|
45
|
+
bodyMessages: { type: Array, default: () => [] },
|
|
46
|
+
},
|
|
47
|
+
computed: {
|
|
48
|
+
remainingMedia() {
|
|
49
|
+
return excludeBodyMedia({
|
|
50
|
+
images: this.images,
|
|
51
|
+
videos: this.videos,
|
|
52
|
+
bodyMessages: this.bodyMessages,
|
|
53
|
+
})
|
|
54
|
+
},
|
|
55
|
+
galleryImages() {
|
|
56
|
+
return this.remainingMedia.images.filter(image => !image.isCover)
|
|
57
|
+
},
|
|
58
|
+
showImageGallery() {
|
|
59
|
+
// Preserve galleries that have one image left after body duplicates
|
|
60
|
+
// are removed, while retaining the existing single-image opt-in.
|
|
61
|
+
return this.galleryImages.length > 0 &&
|
|
62
|
+
(this.allowSingleImageGallery || this.images.length > 1)
|
|
63
|
+
},
|
|
43
64
|
},
|
|
44
65
|
}
|
|
45
66
|
</script>
|
|
@@ -410,7 +410,7 @@ export default {
|
|
|
410
410
|
for (const field of this.formDefinition.fields) {
|
|
411
411
|
if (!isPublicFormFieldVisible(field, this.values)) {
|
|
412
412
|
this.values[field.name] =
|
|
413
|
-
field.type === 'checkbox' ? false : field.type
|
|
413
|
+
field.type === 'checkbox' ? false : ['file', 'checkbox-group'].includes(field.type) ? [] : ''
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
416
|
if (this.validationAttempted) this.validate(false)
|
|
@@ -210,6 +210,7 @@
|
|
|
210
210
|
|
|
211
211
|
<asd20-media-section
|
|
212
212
|
v-if="combinedMessageImages.length > 1 || combinedMessageVideos.length"
|
|
213
|
+
:body-messages="[...primaryMessages, ...secondaryMessages]"
|
|
213
214
|
:images="combinedMessageImages || []"
|
|
214
215
|
:videos="combinedMessageVideos || []"
|
|
215
216
|
>
|
|
@@ -211,6 +211,7 @@
|
|
|
211
211
|
|
|
212
212
|
<asd20-media-section
|
|
213
213
|
v-if="combinedMessageImages.length > 1 || combinedMessageVideos.length"
|
|
214
|
+
:body-messages="[...primaryMessages, ...secondaryMessages]"
|
|
214
215
|
:images="combinedMessageImages || []"
|
|
215
216
|
:videos="combinedMessageVideos || []"
|
|
216
217
|
>
|
|
@@ -230,6 +230,7 @@
|
|
|
230
230
|
|
|
231
231
|
<asd20-media-section
|
|
232
232
|
v-if="combinedMessageImages.length > 1 || combinedMessageVideos.length"
|
|
233
|
+
:body-messages="[...primaryMessages, ...secondaryMessages]"
|
|
233
234
|
:images="combinedMessageImages || []"
|
|
234
235
|
:videos="combinedMessageVideos || []"
|
|
235
236
|
>
|
|
@@ -95,9 +95,10 @@
|
|
|
95
95
|
></asd20-feeds-section> -->
|
|
96
96
|
|
|
97
97
|
<asd20-media-section
|
|
98
|
-
v-if="
|
|
98
|
+
v-if="combinedMessageImages.length || combinedMessageVideos.length"
|
|
99
|
+
:body-messages="[...primaryDetailMessages, ...secondaryMessages]"
|
|
99
100
|
allow-single-image-gallery
|
|
100
|
-
:images="
|
|
101
|
+
:images="combinedMessageImages || []"
|
|
101
102
|
:videos="combinedMessageVideos || []"
|
|
102
103
|
>
|
|
103
104
|
</asd20-media-section>
|
|
@@ -222,6 +222,7 @@
|
|
|
222
222
|
|
|
223
223
|
<asd20-media-section
|
|
224
224
|
v-if="combinedMessageImages.length > 1 || combinedMessageVideos.length"
|
|
225
|
+
:body-messages="[...primaryMessages, ...secondaryMessages]"
|
|
225
226
|
:images="combinedMessageImages || []"
|
|
226
227
|
:videos="combinedMessageVideos || []"
|
|
227
228
|
>
|
|
@@ -276,6 +276,7 @@
|
|
|
276
276
|
|
|
277
277
|
<asd20-media-section
|
|
278
278
|
v-if="combinedMessageImages.length > 1 || combinedMessageVideos.length"
|
|
279
|
+
:body-messages="[...primaryMessages, ...secondaryMessages]"
|
|
279
280
|
:images="combinedMessageImages || []"
|
|
280
281
|
:videos="combinedMessageVideos || []"
|
|
281
282
|
>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { parseFragment } from 'parse5'
|
|
2
|
+
import { buildRichBodyRenderModel } from '../components/atoms/Asd20RichBodyContent/messageDocumentRenderer'
|
|
3
|
+
|
|
4
|
+
const STORAGE_HOST = 'asd20websitestorage.blob.core.windows.net'
|
|
5
|
+
const RELATIVE_ORIGIN = 'https://ui-next.invalid'
|
|
6
|
+
|
|
7
|
+
function resourceKey(value) {
|
|
8
|
+
if (typeof value !== 'string' || !value.trim()) return ''
|
|
9
|
+
try {
|
|
10
|
+
const url = new URL(value.trim(), RELATIVE_ORIGIN)
|
|
11
|
+
if (!['http:', 'https:'].includes(url.protocol)) return ''
|
|
12
|
+
url.hash = ''
|
|
13
|
+
// The website media proxy and blob origin serve the same stored files.
|
|
14
|
+
if (url.pathname.startsWith('/_media/')) {
|
|
15
|
+
return `storage:${url.pathname.slice('/_media'.length)}${url.search}`
|
|
16
|
+
}
|
|
17
|
+
if (url.hostname === STORAGE_HOST) {
|
|
18
|
+
return `storage:${url.pathname}${url.search}`
|
|
19
|
+
}
|
|
20
|
+
return url.href
|
|
21
|
+
} catch {
|
|
22
|
+
return ''
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function videoKey(value) {
|
|
27
|
+
const key = resourceKey(value)
|
|
28
|
+
if (!key || key.startsWith('storage:')) return key
|
|
29
|
+
const url = new URL(key)
|
|
30
|
+
const host = url.hostname.replace(/^www\./, '')
|
|
31
|
+
if (['youtube.com', 'youtube-nocookie.com', 'youtu.be'].includes(host)) {
|
|
32
|
+
const id = host === 'youtu.be'
|
|
33
|
+
? url.pathname.split('/')[1]
|
|
34
|
+
: url.searchParams.get('v') || url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/)?.[1]
|
|
35
|
+
if (id) return `youtube:${id}`
|
|
36
|
+
}
|
|
37
|
+
if (['vimeo.com', 'player.vimeo.com'].includes(host)) {
|
|
38
|
+
const id = url.pathname.match(/^\/(?:video\/)?(\d+)(?:\/|$)/)?.[1]
|
|
39
|
+
if (id) return `vimeo:${id}`
|
|
40
|
+
}
|
|
41
|
+
if (host === 'live.myvrspot.com' && url.searchParams.get('v')) {
|
|
42
|
+
return `myvrspot:${url.searchParams.get('v')}`
|
|
43
|
+
}
|
|
44
|
+
return key
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function addSource(sources, value, normalize) {
|
|
48
|
+
const key = normalize(value)
|
|
49
|
+
if (key) sources.add(key)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function collectHtmlMedia(html, sources) {
|
|
53
|
+
const visit = node => {
|
|
54
|
+
const attrs = Object.fromEntries((node.attrs || []).map(attr => [attr.name, attr.value]))
|
|
55
|
+
if (node.tagName === 'img') {
|
|
56
|
+
addSource(sources.images, attrs.src, resourceKey)
|
|
57
|
+
}
|
|
58
|
+
if (['iframe', 'video'].includes(node.tagName) ||
|
|
59
|
+
(node.tagName === 'source' && node.parentNode?.tagName === 'video')) {
|
|
60
|
+
addSource(sources.videos, attrs.src, videoKey)
|
|
61
|
+
}
|
|
62
|
+
for (const child of node.childNodes || []) visit(child)
|
|
63
|
+
}
|
|
64
|
+
visit(parseFragment(html || ''))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default function excludeBodyMedia({ images = [], videos = [], bodyMessages = [] } = {}) {
|
|
68
|
+
const sources = { images: new Set(), videos: new Set() }
|
|
69
|
+
for (const message of bodyMessages) {
|
|
70
|
+
if (!message) continue
|
|
71
|
+
// Use the renderer's selected content, including its HTML fallback for
|
|
72
|
+
// invalid/unsupported JSON, rather than inspecting stale saved sidecars.
|
|
73
|
+
const model = buildRichBodyRenderModel({
|
|
74
|
+
bodyContent: message.bodyContent || message.bodyHtml || '',
|
|
75
|
+
bodyJson: message.bodyJson,
|
|
76
|
+
})
|
|
77
|
+
for (const node of model.nodes) {
|
|
78
|
+
if (node.html) collectHtmlMedia(node.html, sources)
|
|
79
|
+
if (node.type === 'accordion') {
|
|
80
|
+
for (const item of node.block.items || []) {
|
|
81
|
+
if (item.contentType !== 'tableau') collectHtmlMedia(item.bodyHtml, sources)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
images: images.filter(image => image && ![
|
|
89
|
+
image.url,
|
|
90
|
+
...(image.files || []).map(file => file?.url),
|
|
91
|
+
].some(url => sources.images.has(resourceKey(url)))),
|
|
92
|
+
videos: videos.filter(video => video && ![
|
|
93
|
+
video.embedUrl, video.watchUrl, video.url,
|
|
94
|
+
].some(url => sources.videos.has(videoKey(url)))),
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -7,6 +7,7 @@ export const SUPPORTED_PUBLIC_FORM_FIELD_TYPES = Object.freeze([
|
|
|
7
7
|
'select',
|
|
8
8
|
'radio',
|
|
9
9
|
'checkbox',
|
|
10
|
+
'checkbox-group',
|
|
10
11
|
'file',
|
|
11
12
|
])
|
|
12
13
|
|
|
@@ -48,7 +49,7 @@ function optionalBoolean(value, label) {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
function normalizeOptions(field) {
|
|
51
|
-
if (!['select', 'radio'].includes(field.type)) return undefined
|
|
52
|
+
if (!['select', 'radio', 'checkbox-group'].includes(field.type)) return undefined
|
|
52
53
|
if (
|
|
53
54
|
!Array.isArray(field.options) ||
|
|
54
55
|
!field.options.length ||
|
|
@@ -184,17 +185,19 @@ function normalizeConditions(conditions) {
|
|
|
184
185
|
return conditions.map(normalizeCondition)
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
function matchesCondition(condition, values) {
|
|
189
|
+
const value = values[condition.fieldName]
|
|
190
|
+
return Array.isArray(value)
|
|
191
|
+
? value.some(choice => condition.values.includes(choice))
|
|
192
|
+
: condition.values.includes(String(value || '').trim())
|
|
193
|
+
}
|
|
194
|
+
|
|
187
195
|
export function isPublicFormFieldVisible(field, values) {
|
|
188
|
-
return (
|
|
189
|
-
!field.visibleWhen ||
|
|
190
|
-
field.visibleWhen.values.includes(String(values[field.visibleWhen.fieldName] || '').trim())
|
|
191
|
-
)
|
|
196
|
+
return !field.visibleWhen || matchesCondition(field.visibleWhen, values)
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
export function isPublicFormFieldRequired(field, values) {
|
|
195
|
-
const required = field.required || field.requiredWhen?.some(condition =>
|
|
196
|
-
condition.values.includes(String(values[condition.fieldName] || '').trim())
|
|
197
|
-
)
|
|
200
|
+
const required = field.required || field.requiredWhen?.some(condition => matchesCondition(condition, values))
|
|
198
201
|
const uploaded = field.requiredUnlessUploaded &&
|
|
199
202
|
Array.isArray(values[field.requiredUnlessUploaded]) &&
|
|
200
203
|
values[field.requiredUnlessUploaded].length > 0
|
|
@@ -268,8 +271,8 @@ export function normalizePublicFormDefinition(definition) {
|
|
|
268
271
|
)
|
|
269
272
|
if (
|
|
270
273
|
!dependency ||
|
|
271
|
-
dependency
|
|
272
|
-
!['radio', 'select'].includes(dependency.type) ||
|
|
274
|
+
fields.indexOf(dependency) >= fields.indexOf(field) ||
|
|
275
|
+
!['radio', 'select', 'checkbox-group'].includes(dependency.type) ||
|
|
273
276
|
condition.values.some(
|
|
274
277
|
value => !dependency.options.some(option => option.value === value)
|
|
275
278
|
)
|
|
@@ -369,7 +372,7 @@ export function createPublicFormValues(definition) {
|
|
|
369
372
|
return Object.fromEntries(
|
|
370
373
|
definition.fields.map(field => [
|
|
371
374
|
field.name,
|
|
372
|
-
field.type
|
|
375
|
+
['file', 'checkbox-group'].includes(field.type) ? [] : field.type === 'checkbox' ? false : '',
|
|
373
376
|
])
|
|
374
377
|
)
|
|
375
378
|
}
|
|
@@ -403,9 +406,9 @@ export function validatePublicFormValues(definition, sourceValues = {}) {
|
|
|
403
406
|
const values = {}
|
|
404
407
|
|
|
405
408
|
for (const field of definition.fields) {
|
|
406
|
-
if (!isPublicFormFieldVisible(field,
|
|
409
|
+
if (!isPublicFormFieldVisible(field, values)) {
|
|
407
410
|
values[field.name] =
|
|
408
|
-
field.type
|
|
411
|
+
['file', 'checkbox-group'].includes(field.type) ? [] : field.type === 'checkbox' ? false : ''
|
|
409
412
|
continue
|
|
410
413
|
}
|
|
411
414
|
const required = isPublicFormFieldRequired(field, sourceValues)
|
|
@@ -433,6 +436,21 @@ export function validatePublicFormValues(definition, sourceValues = {}) {
|
|
|
433
436
|
'Upload up to 5 PDF documents, 5 MB each and 10 MB total.'
|
|
434
437
|
)
|
|
435
438
|
}
|
|
439
|
+
} else if (field.type === 'checkbox-group') {
|
|
440
|
+
const selected = rawValue == null ? [] : rawValue
|
|
441
|
+
const allowed = new Set((field.options || []).map(option => option.value))
|
|
442
|
+
if (
|
|
443
|
+
!Array.isArray(selected) ||
|
|
444
|
+
selected.length > allowed.size ||
|
|
445
|
+
selected.some(choice => typeof choice !== 'string' || !allowed.has(choice)) ||
|
|
446
|
+
new Set(selected).size !== selected.length
|
|
447
|
+
) {
|
|
448
|
+
values[field.name] = []
|
|
449
|
+
fieldErrors.push(`Select valid choices for ${field.label.toLowerCase()}.`)
|
|
450
|
+
} else {
|
|
451
|
+
values[field.name] = [...selected]
|
|
452
|
+
if (required && !selected.length) fieldErrors.push(`${field.label} is required.`)
|
|
453
|
+
}
|
|
436
454
|
} else if (field.type === 'checkbox') {
|
|
437
455
|
const value = rawValue === true
|
|
438
456
|
values[field.name] = value
|
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
import responsiveBreakpointMixin from './responsiveBreakpointMixin'
|
|
2
2
|
import { buildBreadcrumbLinks } from '../helpers/breadcrumbLinks'
|
|
3
3
|
|
|
4
|
-
function getMessageBodyHtml(message) {
|
|
5
|
-
return (message && (message.bodyContent || message.bodyHtml)) || ''
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function getImageUrls(image) {
|
|
9
|
-
const urls = []
|
|
10
|
-
const files = (image && image.files) || []
|
|
11
|
-
if (image && image.url) urls.push(image.url)
|
|
12
|
-
files.forEach(file => {
|
|
13
|
-
if (file && file.url) urls.push(file.url)
|
|
14
|
-
})
|
|
15
|
-
return urls
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function normalizeHtmlValue(value) {
|
|
19
|
-
return String(value || '').replace(/&/g, '&')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function imageAppearsInBody(image, bodyHtml) {
|
|
23
|
-
if (!bodyHtml) return false
|
|
24
|
-
const normalizedBodyHtml = normalizeHtmlValue(bodyHtml)
|
|
25
|
-
return getImageUrls(image).some(url =>
|
|
26
|
-
normalizedBodyHtml.includes(normalizeHtmlValue(url))
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
4
|
export default {
|
|
31
5
|
mixins: [responsiveBreakpointMixin],
|
|
32
6
|
|
|
@@ -270,24 +244,6 @@ export default {
|
|
|
270
244
|
}, [])
|
|
271
245
|
},
|
|
272
246
|
|
|
273
|
-
articleGalleryImages() {
|
|
274
|
-
const messageBodies = (this.primaryDetailMessages || [])
|
|
275
|
-
.map(getMessageBodyHtml)
|
|
276
|
-
.filter(Boolean)
|
|
277
|
-
|
|
278
|
-
return this.combinedMessageImages.filter(image => {
|
|
279
|
-
if (!image || image.isCover) return false
|
|
280
|
-
return !messageBodies.some(bodyHtml => imageAppearsInBody(image, bodyHtml))
|
|
281
|
-
})
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
shouldShowArticleMediaSection() {
|
|
285
|
-
return (
|
|
286
|
-
this.articleGalleryImages.length > 0 ||
|
|
287
|
-
this.combinedMessageVideos.length > 0
|
|
288
|
-
)
|
|
289
|
-
},
|
|
290
|
-
|
|
291
247
|
combinedMessageVideos() {
|
|
292
248
|
return (this.messages || []).reduce((a, m) => {
|
|
293
249
|
const videos = m.videos || []
|