@windward/core 0.7.0 → 0.9.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 (64) hide show
  1. package/.eslintrc.js +5 -1
  2. package/.prettierrc +3 -2
  3. package/components/Content/Blocks/Accordion.vue +10 -2
  4. package/components/Content/Blocks/BlockQuote.vue +6 -2
  5. package/components/Content/Blocks/ClickableIcons.vue +12 -10
  6. package/components/Content/Blocks/Email.vue +7 -7
  7. package/components/Content/Blocks/GenerateAIQuestionButton.vue +85 -18
  8. package/components/Content/Blocks/HorizontalRule.vue +2 -0
  9. package/components/Content/Blocks/Image.vue +7 -182
  10. package/components/Content/Blocks/OpenResponse.vue +25 -0
  11. package/components/Content/Blocks/OpenResponseCollate.vue +13 -12
  12. package/components/Content/Blocks/ScenarioChoice.vue +10 -2
  13. package/components/Content/Blocks/Tab.vue +27 -3
  14. package/components/Content/Blocks/UserUpload.vue +5 -3
  15. package/components/Content/Blocks/Video.vue +33 -7
  16. package/components/Navigation/Items/GlossaryNav.vue +25 -10
  17. package/components/Settings/AccordionSettings.vue +20 -34
  18. package/components/Settings/BlockQuoteSettings.vue +17 -41
  19. package/components/Settings/ClickableIconsSettings.vue +18 -40
  20. package/components/Settings/EmailSettings.vue +12 -38
  21. package/components/Settings/FileDownloadSettings.vue +10 -35
  22. package/components/Settings/ImageSettings.vue +13 -247
  23. package/components/Settings/OpenResponseCollateSettings.vue +47 -55
  24. package/components/Settings/OpenResponseSettings.vue +62 -36
  25. package/components/Settings/ScenarioChoiceSettings.vue +13 -35
  26. package/components/Settings/TabSettings.vue +25 -34
  27. package/components/Settings/UserUploadSettings.vue +6 -30
  28. package/components/Settings/VideoSettings/SourcePicker.vue +1 -0
  29. package/components/Settings/VideoSettings.vue +6 -42
  30. package/components/utils/ContentViewer.vue +0 -3
  31. package/components/utils/FillInBlank/FillInBlankInput.vue +29 -47
  32. package/components/utils/TinyMCEWrapper.vue +42 -81
  33. package/components/utils/glossary/GlossaryToolTip.vue +6 -0
  34. package/helpers/GlossaryHelper.ts +12 -2
  35. package/helpers/tinymce/WindwardPlugins.ts +166 -118
  36. package/i18n/en-US/components/content/blocks/generate_questions.ts +3 -2
  37. package/i18n/en-US/components/settings/open_response_collate.ts +1 -1
  38. package/i18n/en-US/components/settings/scenario_choice.ts +3 -2
  39. package/i18n/en-US/components/settings/video.ts +1 -1
  40. package/i18n/en-US/components/utils/FillInBlank/FillInBlankInput.ts +2 -0
  41. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
  42. package/i18n/en-US/shared/settings.ts +1 -1
  43. package/i18n/es-ES/components/content/blocks/generate_questions.ts +2 -1
  44. package/i18n/es-ES/components/settings/open_response_collate.ts +1 -1
  45. package/i18n/es-ES/components/settings/scenario_choice.ts +3 -2
  46. package/i18n/es-ES/components/settings/video.ts +1 -1
  47. package/i18n/es-ES/components/utils/FillInBlank/FillInBlankInput.ts +2 -0
  48. package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +3 -2
  49. package/i18n/es-ES/shared/settings.ts +1 -1
  50. package/i18n/sv-SE/components/content/blocks/generate_questions.ts +2 -1
  51. package/i18n/sv-SE/components/settings/open_response_collate.ts +1 -1
  52. package/i18n/sv-SE/components/settings/scenario_choice.ts +3 -2
  53. package/i18n/sv-SE/components/settings/video.ts +1 -1
  54. package/i18n/sv-SE/components/utils/FillInBlank/FillInBlankInput.ts +2 -0
  55. package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +2 -0
  56. package/package.json +3 -2
  57. package/pages/glossary.vue +1 -1
  58. package/stylelint.config.js +14 -0
  59. package/test/Components/Content/Blocks/OpenResponseCollate.spec.js +3 -3
  60. package/test/Components/Settings/TabSettings.spec.js +2 -2
  61. package/test/__mocks__/contentBlockMock.js +20 -0
  62. package/test/__mocks__/modelMock.js +1 -1
  63. package/test/helpers/GlossaryHelper.spec.js +22 -3
  64. package/test/mocks.js +8 -0
package/.eslintrc.js CHANGED
@@ -4,7 +4,11 @@ module.exports = {
4
4
  browser: true,
5
5
  node: true,
6
6
  },
7
- extends: ['plugin:nuxt/recommended', 'plugin:prettier/recommended'],
7
+ extends: [
8
+ '@nuxtjs/eslint-config-typescript',
9
+ 'plugin:prettier/recommended',
10
+ 'plugin:nuxt/recommended',
11
+ ],
8
12
  plugins: [],
9
13
  // add your custom rules here
10
14
  rules: {},
package/.prettierrc CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
- "semi": false,
3
- "singleQuote": true
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5"
4
5
  }
@@ -7,9 +7,17 @@
7
7
  block.metadata.config.instructions
8
8
  "
9
9
  >
10
- <h2>{{ block.metadata.config.title }}</h2>
10
+ <h2 v-if="block.metadata.config.title" tabindex="0">
11
+ {{ block.metadata.config.title }}
12
+ </h2>
11
13
 
12
- <p>{{ block.metadata.config.instructions }}</p>
14
+ <p
15
+ v-if="block.metadata.config.instructions"
16
+ tabindex="0"
17
+ class="pt-3"
18
+ >
19
+ {{ block.metadata.config.instructions }}
20
+ </p>
13
21
  </v-container>
14
22
  <v-expansion-panels
15
23
  :value="selectedPanels"
@@ -7,10 +7,14 @@
7
7
  "
8
8
  >
9
9
  <v-col cols="12" class="pb-0">
10
- <h2 v-if="block.metadata.config.title">
10
+ <h2 v-if="block.metadata.config.title" tabindex="0">
11
11
  {{ block.metadata.config.title }}
12
12
  </h2>
13
- <p v-if="block.metadata.config.instructions" class="pb-0 mb-0">
13
+ <p
14
+ v-if="block.metadata.config.instructions"
15
+ tabindex="0"
16
+ class="pt-3"
17
+ >
14
18
  {{ block.metadata.config.instructions }}
15
19
  </p>
16
20
  </v-col>
@@ -1,16 +1,15 @@
1
1
  <template>
2
2
  <div>
3
3
  <v-container class="pa-0">
4
- <h2>{{ block.metadata.config.title }}</h2>
5
-
6
- <p>{{ block.metadata.config.description }}</p>
7
-
8
- <p>
9
- {{
10
- $t(
11
- 'windward.core.components.settings.clickable_icon.information'
12
- )
13
- }}
4
+ <h2 v-if="block.metadata.config.title" tabindex="0">
5
+ {{ block.metadata.config.title }}
6
+ </h2>
7
+ <p
8
+ v-if="block.metadata.config.description"
9
+ tabindex="0"
10
+ class="pt-3"
11
+ >
12
+ {{ block.metadata.config.description }}
14
13
  </p>
15
14
  </v-container>
16
15
  <v-container class="pa-0">
@@ -92,6 +91,9 @@ export default {
92
91
  )
93
92
  if (_.isEmpty(this.block.metadata.config.items)) {
94
93
  this.block.metadata.config.items = []
94
+ this.block.metadata.config.description = this.$t(
95
+ 'windward.core.components.settings.clickable_icon.information'
96
+ )
95
97
  }
96
98
  if (_.isEmpty(this.block.metadata.config.title)) {
97
99
  this.block.metadata.config.title = ''
@@ -7,10 +7,14 @@
7
7
  "
8
8
  >
9
9
  <v-col cols="12" class="pa-0">
10
- <h2 v-if="block.metadata.config.title">
10
+ <h2 v-if="block.metadata.config.title" tabindex="0">
11
11
  {{ block.metadata.config.title }}
12
12
  </h2>
13
- <p v-if="block.metadata.config.instructions" class="pb-0 mb-0">
13
+ <p
14
+ v-if="block.metadata.config.instructions"
15
+ tabindex="0"
16
+ class="pt-3"
17
+ >
14
18
  {{ block.metadata.config.instructions }}
15
19
  </p>
16
20
  </v-col>
@@ -171,11 +175,7 @@
171
175
  >
172
176
  <v-row class="pt-4">
173
177
  <v-col cols="10" class="pa-0">
174
- <v-btn
175
- elevation="0"
176
- color="primary"
177
- text
178
- >
178
+ <v-btn elevation="0" color="primary" text>
179
179
  <v-icon>mdi-reply</v-icon>
180
180
  {{
181
181
  $t(
@@ -1,21 +1,50 @@
1
1
  <template>
2
- <v-btn
3
- elevation="0"
4
- color="secondary"
5
- @click="generateAIQuestion"
6
- :loading="isLoading"
7
- :disabled="isLoading"
8
- >
9
- <v-icon class="pr-1" v-if="!isLoading">mdi-magic-staff</v-icon>
10
- {{
11
- this.$t(
12
- 'windward.core.components.content.blocks.generate_questions.button_label'
13
- )
14
- }}
15
- <template v-slot:loader>
16
- <v-progress-circular indeterminate size="23"></v-progress-circular>
17
- </template>
18
- </v-btn>
2
+ <div>
3
+ <v-row>
4
+ <v-col cols="12" class="d-flex justify-end pb-1">
5
+ <v-btn
6
+ elevation="0"
7
+ color="secondary"
8
+ class="mb-1 btn-selector"
9
+ :loading="isLoading"
10
+ :disabled="isLoading"
11
+ @click="generateAIQuestion"
12
+ >
13
+ <v-icon class="pr-1" v-if="!isLoading"
14
+ >mdi-magic-staff</v-icon
15
+ >
16
+ {{
17
+ this.$t(
18
+ 'windward.core.components.content.blocks.generate_questions.button_label'
19
+ )
20
+ }}
21
+ <template v-slot:loader>
22
+ <v-progress-circular
23
+ indeterminate
24
+ size="23"
25
+ ></v-progress-circular>
26
+ </template>
27
+ </v-btn>
28
+ </v-col>
29
+ <v-col cols="12" class="d-flex justify-end pt-0">
30
+ <v-select
31
+ v-model="selectedContent"
32
+ :items="flattenedContent"
33
+ class="btn-selector"
34
+ outlined
35
+ hide-details
36
+ dense
37
+ :label="
38
+ $t(
39
+ 'windward.core.components.content.blocks.generate_questions.selected_pages'
40
+ )
41
+ "
42
+ item-text="content.name"
43
+ return-object
44
+ ></v-select>
45
+ </v-col>
46
+ </v-row>
47
+ </div>
19
48
  </template>
20
49
 
21
50
  <script>
@@ -23,6 +52,9 @@ import AssessmentQuestion from '~/models/AssessmentQuestion'
23
52
  import Course from '~/models/Course'
24
53
  import Assessment from '~/models/Assessment'
25
54
  import Content from '~/models/Content'
55
+ import { mapGetters } from 'vuex'
56
+ import _ from 'lodash'
57
+ import Crypto from '~/helpers/Crypto'
26
58
 
27
59
  export default {
28
60
  name: 'GenerateAIQuestionButton',
@@ -35,15 +67,45 @@ export default {
35
67
  data() {
36
68
  return {
37
69
  isLoading: false,
70
+ selectedContent: '',
38
71
  }
39
72
  },
73
+ computed: {
74
+ ...mapGetters({
75
+ contentTree: 'content/getTree',
76
+ }),
77
+ flattenedContent() {
78
+ let cloneContentTree = _.cloneDeep(this.contentTree)
79
+ const homepage = this.$ContentService.getHomepage()
80
+ if (!_.isEmpty(homepage)) {
81
+ cloneContentTree.unshift(homepage)
82
+ }
83
+ let fullTree = []
84
+ // flatten content tree to get nested children pages
85
+ cloneContentTree.forEach((content) => {
86
+ fullTree.push(content)
87
+ if (content.children.length > 0) {
88
+ fullTree = fullTree.concat(_.flatten(content.children))
89
+ }
90
+ })
91
+ //
92
+ if (_.isEmpty(this.selectedContent)) {
93
+ // returns array so hold here to pluck out below
94
+ const currentPage = fullTree.filter(
95
+ (contentPage) => contentPage.id === this.content.id
96
+ )
97
+ this.selectedContent = currentPage[0] ? currentPage[0] : ''
98
+ }
99
+ return fullTree
100
+ },
101
+ },
40
102
  methods: {
41
103
  async generateAIQuestion() {
42
104
  this.isLoading = true
43
105
  try {
44
106
  const response = await AssessmentQuestion.custom(
45
107
  new Course(this.course),
46
- new Content(this.content),
108
+ new Content(this.selectedContent),
47
109
  new Assessment({ id: this.block.id }),
48
110
  new AssessmentQuestion(),
49
111
  `suggest/${this.questionType}`
@@ -67,3 +129,8 @@ export default {
67
129
  },
68
130
  }
69
131
  </script>
132
+ <style scoped>
133
+ .btn-selector {
134
+ width: 100%;
135
+ }
136
+ </style>
@@ -25,6 +25,8 @@ export default {
25
25
  },
26
26
  data() {
27
27
  return {
28
+ saveState: false,
29
+ trackable: false,
28
30
  isDashed: 'solid',
29
31
  }
30
32
  },
@@ -1,99 +1,25 @@
1
1
  <template>
2
2
  <div>
3
- <v-container class="pa-0 ma-0">
4
- <div v-if="!imageUrl" class="img-holder">
5
- <v-skeleton-loader
6
- height="300px"
7
- :elevation="2"
8
- type="image"
9
- ></v-skeleton-loader>
10
-
11
- <div class="no-source-overlay">
12
- <v-icon x-large>mdi-file-question</v-icon><br />
13
- {{
14
- $t(
15
- 'windward.core.components.content.blocks.image.no_image_url'
16
- )
17
- }}
18
- </div>
19
- </div>
20
- <v-responsive :aspect-ratio="aspectRatio">
21
- <v-img
22
- v-if="imageUrl"
23
- :alt="altText"
24
- :aria-describedby="describedByText"
25
- :class="imageClass"
26
- :src="imageUrl"
27
- contain
28
- @click="onHandleModal"
29
- >
30
- <template #placeholder>
31
- <v-skeleton-loader
32
- type="image, image, list-item-avatar"
33
- height="100%"
34
- width="100%"
35
- ></v-skeleton-loader>
36
- </template>
37
- </v-img>
38
- </v-responsive>
39
- <TextViewer
40
- v-if="describedById"
41
- v-model="describedByText"
42
- :id="describedById"
43
- class="sr-only"
44
- ></TextViewer>
45
- </v-container>
46
- <DialogBox v-model="dialog" persistent :trigger="false">
47
- <template #title></template>
48
- <template #form="{ on, attrs }">
49
- <v-responsive
50
- :aspect-ratio="aspectRatio"
51
- v-bind="attrs"
52
- v-on="on"
53
- >
54
- <v-img
55
- v-if="imageUrl"
56
- :alt="altText"
57
- :aria-describedby="describedByText"
58
- :class="imageClass"
59
- :src="imageUrl"
60
- contain
61
- @click="onHandleModal"
62
- >
63
- <template #placeholder>
64
- <v-skeleton-loader
65
- type="image, image, list-item-avatar"
66
- height="100%"
67
- width="100%"
68
- ></v-skeleton-loader>
69
- </template>
70
- </v-img>
71
- </v-responsive>
72
- </template>
73
- </DialogBox>
3
+ <ImageAssetViewer
4
+ v-model="block.metadata.config"
5
+ :assets="block.assets"
6
+ ></ImageAssetViewer>
74
7
  </div>
75
8
  </template>
76
9
 
77
10
  <script>
78
11
  import _ from 'lodash'
79
- import DialogBox from '~/components/Core/DialogBox.vue'
80
- import TextViewer from '~/components/Text/TextViewer'
81
- import Crypto from '~/helpers/Crypto'
12
+ import ImageAssetViewer from '~/components/Content/ImageAssetViewer.vue'
82
13
  import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
83
14
 
84
15
  export default {
85
16
  name: 'ContentBlockImage',
86
17
  extends: BaseContentBlock,
87
18
  components: {
88
- DialogBox,
89
- TextViewer,
19
+ ImageAssetViewer,
90
20
  },
91
21
  data() {
92
- return {
93
- id: 'image_' + Crypto.id(),
94
- aspectRatio: undefined,
95
- dialog: false,
96
- }
22
+ return {}
97
23
  },
98
24
  beforeMount() {
99
25
  if (_.isEmpty(this.block.metadata.config)) {
@@ -112,106 +38,5 @@ export default {
112
38
  this.block.metadata.config.ariaDescribedBy = ''
113
39
  }
114
40
  },
115
- computed: {
116
- describedById() {
117
- // If there's a described by
118
- if (this.describedByText) {
119
- return this.id
120
- } else {
121
- return null
122
- }
123
- },
124
- altText() {
125
- // Get the asset info first and fallback to the block metadata if inherit is set to false
126
- if (_.get(this.block.metadata, 'config.inherit', true)) {
127
- return _.get(this.fileAsset, 'asset.metadata.props.alt', null)
128
- } else {
129
- return _.get(this.block.metadata, 'config.alt', null)
130
- }
131
- },
132
- describedByText() {
133
- // Get the asset info first and fallback to the block metadata if inherit is set to false
134
- if (_.get(this.block.metadata, 'config.inherit', true)) {
135
- return _.get(
136
- this.fileAsset,
137
- 'asset.metadata.props.aria_describedby',
138
- null
139
- )
140
- } else {
141
- return _.get(
142
- this.block.metadata,
143
- 'config.ariaDescribedBy',
144
- null
145
- )
146
- }
147
- },
148
- fileAsset() {
149
- return this.resolveAsset(
150
- _.get(this.block, 'metadata.config.asset', null)
151
- )
152
- },
153
- imageUrl() {
154
- // Get the image public url and fallback to the block body
155
- return _.get(this.fileAsset, 'asset.public_url', this.block.body)
156
- },
157
- imageClass() {
158
- let imageClass = ''
159
- // change cursor to pointer for images that open in larger modal
160
- if (this.block.metadata.config.modal) {
161
- imageClass += ' container-pointer'
162
- }
163
- // If NOT hide background, inclide the extra class
164
- if (!_.get(this.block.metadata, 'config.hideBackground', false)) {
165
- imageClass += ' img-white'
166
- }
167
- return 'img-display' + imageClass
168
- },
169
- },
170
- watch: {
171
- value(newValue) {
172
- if (
173
- !_.isEmpty(newValue.metadata.config.height) &&
174
- !_.isEmpty(newValue.metadata.config.width)
175
- ) {
176
- this.aspectRatio =
177
- newValue.metadata.config.width +
178
- '/' +
179
- newValue.metadata.config.height
180
- } else {
181
- this.aspectRatio = undefined
182
- }
183
- },
184
- },
185
- methods: {
186
- onHandleModal() {
187
- if (this.block.metadata.config.modal) {
188
- this.dialog = true
189
- }
190
- },
191
- },
192
41
  }
193
42
  </script>
194
-
195
- <style lang="scss" scoped>
196
- .img-display {
197
- border-radius: 3px;
198
- }
199
- .img-holder {
200
- height: 300px;
201
- }
202
- .img-white {
203
- background: #fff;
204
- }
205
- .no-source-overlay {
206
- text-align: center;
207
- margin-top: -175px;
208
- }
209
- .container-pointer {
210
- cursor: pointer;
211
- }
212
- ::v-deep .v-skeleton-loader.v-skeleton-loader--is-loading {
213
- .v-skeleton-loader__image {
214
- height: 100%;
215
- }
216
- }
217
- </style>
@@ -1,5 +1,24 @@
1
1
  <template>
2
2
  <div>
3
+ <v-container
4
+ class="pa-0"
5
+ v-if="
6
+ block.metadata.config.title ||
7
+ block.metadata.config.instructions
8
+ "
9
+ >
10
+ <h2 v-if="block.metadata.config.title" tabindex="0">
11
+ {{ block.metadata.config.title }}
12
+ </h2>
13
+
14
+ <p
15
+ v-if="block.metadata.config.instructions"
16
+ tabindex="0"
17
+ class="pt-3"
18
+ >
19
+ {{ block.metadata.config.instructions }}
20
+ </p>
21
+ </v-container>
3
22
  <div v-if="stateLoaded">
4
23
  <div v-if="block.body && !submitted">
5
24
  <TextViewer v-model="block.body" :height="200"></TextViewer>
@@ -118,6 +137,12 @@ export default {
118
137
  if (_.isEmpty(this.block.metadata.config)) {
119
138
  this.block.metadata.config = {}
120
139
  }
140
+ if (_.isEmpty(this.block.metadata.config.title)) {
141
+ this.block.metadata.config.title = ''
142
+ }
143
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
144
+ this.block.metadata.config.instructions = ''
145
+ }
121
146
  if (_.isEmpty(this.block.metadata.config.sample_response)) {
122
147
  this.block.metadata.config.sample_response = ''
123
148
  }
@@ -1,7 +1,13 @@
1
1
  <template>
2
2
  <div>
3
3
  <div v-if="block.metadata.config.linked.length">
4
- <v-btn elevation="0" color="primary" outlined block @click="onCollate">
4
+ <v-btn
5
+ elevation="0"
6
+ color="primary"
7
+ outlined
8
+ block
9
+ @click="onCollate"
10
+ >
5
11
  <v-icon class="mr-3">mdi-file-word</v-icon>
6
12
  {{
7
13
  $t(
@@ -74,19 +80,14 @@ export default {
74
80
  'metadata->block->block_id',
75
81
  this.block.metadata.config.linked
76
82
  )
83
+ .with('contentBlock')
77
84
  .get()
78
- let collated = ''
79
- const sortedStates = []
80
85
 
81
- // Sorted the states based on the linked order
82
- this.block.metadata.config.linked.forEach((linkedId) => {
83
- const found = userState.find((state) => {
84
- return state.metadata.block.block_id === linkedId
85
- })
86
- if (found) {
87
- sortedStates.push(found)
88
- }
89
- })
86
+ let collated = ''
87
+ // sort by order on the page
88
+ const sortedStates = userState.sort((a, b) =>
89
+ a.content_block.order < b.content_block.order ? -1 : 1
90
+ )
90
91
 
91
92
  sortedStates.forEach((state) => {
92
93
  // Prepend the prompt from the state if include prompts is enabled
@@ -1,8 +1,16 @@
1
1
  <template>
2
2
  <div>
3
3
  <v-container class="pa-0">
4
- <h2>{{ block.metadata.config.title }}</h2>
5
- <p>{{ block.metadata.config.description }}</p>
4
+ <h2 v-if="block.metadata.config.title" tabindex="0">
5
+ {{ block.metadata.config.title }}
6
+ </h2>
7
+ <p
8
+ v-if="block.metadata.config.description"
9
+ tabindex="0"
10
+ class="pt-3"
11
+ >
12
+ {{ block.metadata.config.description }}
13
+ </p>
6
14
  <div
7
15
  v-if="choiceIndex !== null && block.metadata.config.show_reset"
8
16
  class="text-right"
@@ -1,9 +1,23 @@
1
1
  <template>
2
2
  <div>
3
- <v-container class="pa-0">
4
- <h2>{{ block.metadata.config.title }}</h2>
3
+ <v-container
4
+ v-if="
5
+ block.metadata.config.title ||
6
+ block.metadata.config.instructions
7
+ "
8
+ class="pa-0"
9
+ >
10
+ <h2 v-if="block.metadata.config.title" tabindex="0">
11
+ {{ block.metadata.config.title }}
12
+ </h2>
5
13
 
6
- <p>{{ block.metadata.config.instructions }}</p>
14
+ <p
15
+ v-if="block.metadata.config.instructions"
16
+ tabindex="0"
17
+ class="pt-3"
18
+ >
19
+ {{ block.metadata.config.instructions }}
20
+ </p>
7
21
  </v-container>
8
22
  <v-container class="pa-0">
9
23
  <v-tabs dark v-model="block.metadata.config.currentTab" show-arrows>
@@ -33,6 +47,14 @@
33
47
  v-if="!render && tabContent.expand"
34
48
  v-model="tabContent.content"
35
49
  ></TextEditor>
50
+ <ImageAssetViewer
51
+ v-if="
52
+ tabContent.imageAsset &&
53
+ tabContent.imageAsset.asset
54
+ "
55
+ v-model="tabContent.imageAsset"
56
+ :assets="block.assets"
57
+ ></ImageAssetViewer>
36
58
  </v-container>
37
59
  </v-tab-item>
38
60
  </v-tabs>
@@ -44,6 +66,7 @@
44
66
  import _ from 'lodash'
45
67
  import TextEditor from '~/components/Text/TextEditor'
46
68
  import TextViewer from '~/components/Text/TextViewer'
69
+ import ImageAssetViewer from '~/components/Content/ImageAssetViewer.vue'
47
70
  import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
48
71
 
49
72
  export default {
@@ -51,6 +74,7 @@ export default {
51
74
  components: {
52
75
  TextEditor,
53
76
  TextViewer,
77
+ ImageAssetViewer,
54
78
  },
55
79
  extends: BaseContentBlock,
56
80
  beforeMount() {
@@ -1,12 +1,14 @@
1
1
  <template>
2
2
  <v-container class="pa-0">
3
3
  <div>
4
- <h2 v-if="block.metadata.config.title">
4
+ <h2 v-if="block.metadata.config.title" tabindex="0">
5
5
  {{ block.metadata.config.title }}
6
6
  </h2>
7
7
  <v-row>
8
- <v-col cols="12">
9
- <p>{{ block.metadata.config.instructions }}</p>
8
+ <v-col v-if="block.metadata.config.instructions" cols="12">
9
+ <p tabindex="0" class="pt-3">
10
+ {{ block.metadata.config.instructions }}
11
+ </p>
10
12
  </v-col>
11
13
  <v-col v-if="!blockExists" cols="12">
12
14
  <v-alert color="warning">