@windward/games 0.0.1

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 (68) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.js +11 -0
  3. package/.prettierrc +4 -0
  4. package/README.md +43 -0
  5. package/babel.config.js +1 -0
  6. package/components/content/CrudTable.vue +295 -0
  7. package/components/content/DataTableRowHandler.vue +113 -0
  8. package/components/content/DatableEditor.vue +223 -0
  9. package/components/content/blocks/BaseContentBlock.vue +8 -0
  10. package/components/content/blocks/dragDrop/BucketGame.vue +520 -0
  11. package/components/content/blocks/dragDrop/SortingGame.vue +303 -0
  12. package/components/content/blocks/flashcards/CardFace.vue +112 -0
  13. package/components/content/blocks/flashcards/Flashcard.vue +150 -0
  14. package/components/content/blocks/flashcards/FlashcardSlides.vue +121 -0
  15. package/components/content/blocks/matchingGame/MatchingGame.vue +545 -0
  16. package/components/content/blocks/quizshowGame/AnswerPanel.vue +338 -0
  17. package/components/content/blocks/quizshowGame/Gridview.vue +260 -0
  18. package/components/content/blocks/quizshowGame/QuizShow.vue +516 -0
  19. package/components/content/blocks/quizshowGame/feedbackIcons.vue +41 -0
  20. package/components/content/blocks/slideshow/SlideShow.vue +175 -0
  21. package/components/settings/BucketGameSettingsManager.vue +683 -0
  22. package/components/settings/FlashCardSlidesManager.vue +489 -0
  23. package/components/settings/MatchingGameManager.vue +775 -0
  24. package/components/settings/QuizShowSettingsManager.vue +408 -0
  25. package/components/settings/SlideShowManager.vue +248 -0
  26. package/components/settings/SortingGameSettingsManager.vue +286 -0
  27. package/i18n/en-US/components/content/blocks/bucket_game.ts +39 -0
  28. package/i18n/en-US/components/content/blocks/flashcard.ts +5 -0
  29. package/i18n/en-US/components/content/blocks/index.ts +15 -0
  30. package/i18n/en-US/components/content/blocks/matching_game.ts +26 -0
  31. package/i18n/en-US/components/content/blocks/quizshow_game.ts +35 -0
  32. package/i18n/en-US/components/content/blocks/slideshow.ts +13 -0
  33. package/i18n/en-US/components/content/blocks/sorting_game.ts +5 -0
  34. package/i18n/en-US/components/content/crud_table.ts +6 -0
  35. package/i18n/en-US/components/content/index.ts +7 -0
  36. package/i18n/en-US/components/index.ts +9 -0
  37. package/i18n/en-US/components/navigation/index.ts +5 -0
  38. package/i18n/en-US/components/settings/bucket_game.ts +35 -0
  39. package/i18n/en-US/components/settings/flashcard.ts +26 -0
  40. package/i18n/en-US/components/settings/index.ts +13 -0
  41. package/i18n/en-US/components/settings/matching_game.ts +15 -0
  42. package/i18n/en-US/components/settings/quizshow_game.ts +14 -0
  43. package/i18n/en-US/components/settings/slideshow.ts +11 -0
  44. package/i18n/en-US/index.ts +15 -0
  45. package/i18n/en-US/modules/index.ts +5 -0
  46. package/i18n/en-US/pages/index.ts +5 -0
  47. package/i18n/en-US/shared/content_blocks.ts +14 -0
  48. package/i18n/en-US/shared/index.ts +7 -0
  49. package/i18n/en-US/shared/settings.ts +10 -0
  50. package/i18n/en-US.ts +5 -0
  51. package/jest.config.js +18 -0
  52. package/package.json +51 -0
  53. package/plugin.js +150 -0
  54. package/test/blocks/dragDrop/BucketGame.spec.js +26 -0
  55. package/test/blocks/dragDrop/SortingGame.spec.js +47 -0
  56. package/test/blocks/flashcards/CardFace.spec.js +21 -0
  57. package/test/blocks/flashcards/FlashCardSlides.spec.js +24 -0
  58. package/test/blocks/flashcards/Flashcard.spec.js +24 -0
  59. package/test/blocks/matchingGame/MatchingGame.spec.js +26 -0
  60. package/test/blocks/quizShow/quizShow.spec.js +31 -0
  61. package/test/blocks/slideshow/slideshow.spec.js +24 -0
  62. package/test/mocks.js +2 -0
  63. package/test/settings/BucketGameManager.spec.js +125 -0
  64. package/test/settings/FlashCardSlidesManager.spec.js +24 -0
  65. package/test/settings/MatchingGameManager.spec.js +30 -0
  66. package/test/settings/SlideShowManager.spec.js +30 -0
  67. package/test/settings/SortingGameManager.spec.js +71 -0
  68. package/tsconfig.json +20 -0
@@ -0,0 +1,303 @@
1
+ <template>
2
+ <div>
3
+ <h2>{{ block.metadata.config.title }}</h2>
4
+
5
+ <p>
6
+ {{ block.metadata.config.instructions }}
7
+ </p>
8
+ <v-container :key="'feedback'" :class="feedbackClass()">
9
+ <br />
10
+ <v-row
11
+ class="d-flex justify-space-around"
12
+ align="center"
13
+ justify="center"
14
+ >{{ feedback }}
15
+ </v-row>
16
+ <br />
17
+ </v-container>
18
+ <br />
19
+ <div v-if="render">
20
+ <draggable
21
+ v-model="input"
22
+ :disabled="disabled"
23
+ class="d-flex flex-column mb-6"
24
+ group="cards"
25
+ v-bind="dragOptions"
26
+ @change="onDragChange"
27
+ >
28
+ <v-card
29
+ v-for="index in input.length"
30
+ :key="'bucket_' + index"
31
+ :class="'pa-2 flex-fill bucket ' + bucketStateClass(index)"
32
+ outlined
33
+ tile
34
+ >
35
+ <v-icon>mdi-gesture-tap-hold</v-icon>
36
+ <v-icon v-if="success[index - 1]" color="green"
37
+ >mdi-check-bold
38
+ </v-icon>
39
+ <v-icon v-if="fail[index - 1]" color="red"
40
+ >mdi-close</v-icon
41
+ >
42
+ {{ input[index - 1].value }}
43
+ </v-card>
44
+ </draggable>
45
+ <v-container>
46
+ <v-row
47
+ v-if="!isFail() && !isSuccess()"
48
+ class="d-flex justify-end"
49
+ >
50
+ <v-spacer class="col-lg-1" />
51
+ <v-btn color="secondary" @click="checkAnswers">
52
+ {{
53
+ $t(
54
+ 'plugin.games.components.content.blocks.sorting_game.check_answers'
55
+ )
56
+ }}
57
+ </v-btn>
58
+ </v-row>
59
+ <v-row
60
+ v-if="isFail() || isSuccess()"
61
+ class="d-flex justify-end"
62
+ >
63
+ <v-btn
64
+ v-if="isFail()"
65
+ color="primary"
66
+ class=" "
67
+ @click="continueGame"
68
+ >{{ $t('shared.forms.continue') }} </v-btn
69
+ >&nbsp;&nbsp;
70
+ <v-btn color="secondary" class="" @click="reset"
71
+ >{{ $t('shared.forms.reset') }}
72
+ </v-btn>
73
+ </v-row>
74
+ </v-container>
75
+ </div>
76
+ <div v-if="!render">
77
+ <draggable
78
+ v-model="block.metadata.config.answer"
79
+ :disabled="disabled"
80
+ class="d-flex flex-column mb-6"
81
+ group="cards"
82
+ v-bind="dragOptions"
83
+ @change="onDragChange"
84
+ >
85
+ <v-card
86
+ v-for="index in block.metadata.config.answer.length"
87
+ :key="'bucket_' + index"
88
+ :class="'pa-2 flex-fill bucket ' + bucketStateClass(index)"
89
+ elevation="0"
90
+ outlined
91
+ tile
92
+ >
93
+ <v-icon>mdi-gesture-tap-hold</v-icon>
94
+ <v-icon v-if="success[index - 1]" color="green"
95
+ >mdi-check-bold
96
+ </v-icon>
97
+ <v-icon v-if="fail[index - 1]" color="red"
98
+ >mdi-close</v-icon
99
+ >
100
+ {{ block.metadata.config.answer[index - 1].value }}
101
+ </v-card>
102
+ </draggable>
103
+ </div>
104
+ </div>
105
+ </template>
106
+
107
+ <script>
108
+ import _ from 'lodash'
109
+ import draggable from 'vuedraggable'
110
+ import CrudTable from '../../CrudTable'
111
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
112
+ export default {
113
+ name: 'SortingGame',
114
+ components: { CrudTable, draggable },
115
+ extends: BaseContentBlock,
116
+ beforeMount() {
117
+ if (_.isEmpty(this.block)) {
118
+ this.block = {}
119
+ }
120
+ if (_.isEmpty(this.block.metadata)) {
121
+ this.block.metadata = {}
122
+ }
123
+ if (_.isEmpty(this.block.metadata.config)) {
124
+ this.block.metadata.config = {}
125
+ }
126
+ if (_.isEmpty(this.block.metadata.config.answer)) {
127
+ this.block.metadata.config.answer = []
128
+ }
129
+ this.input = this.shuffle(
130
+ _.cloneDeep(this.block.metadata.config.answer)
131
+ )
132
+ },
133
+ data() {
134
+ return {
135
+ ticker: '123',
136
+ disabled: false,
137
+ reload: false,
138
+ input: [],
139
+ feedback: 'feedback here',
140
+ success: [],
141
+ fail: [],
142
+ seed: 123,
143
+ answer: [],
144
+ }
145
+ },
146
+ computed: {
147
+ dragOptions() {
148
+ return {
149
+ animation: 200,
150
+ }
151
+ },
152
+ },
153
+ methods: {
154
+ shuffle(array) {
155
+ let currentIndex = array.length,
156
+ randomIndex
157
+
158
+ // While there remain elements to shuffle.
159
+ while (currentIndex !== 0) {
160
+ // Pick a remaining element.
161
+ randomIndex = Math.floor(Math.random() * currentIndex)
162
+ currentIndex--
163
+
164
+ // And swap it with the current element.
165
+ ;[array[currentIndex], array[randomIndex]] = [
166
+ array[randomIndex],
167
+ array[currentIndex],
168
+ ]
169
+ }
170
+
171
+ return array
172
+ },
173
+
174
+ onDragChange(evt) {
175
+ if (this.success[evt.moved.oldIndex]) {
176
+ this.success[evt.moved.oldIndex] = false
177
+ }
178
+ },
179
+ async onActionPanelEdit() {
180
+ this.render = !this.render
181
+ if (this.render) {
182
+ this.input = this.shuffle(
183
+ _.cloneDeep(this.block.metadata.config.answer)
184
+ )
185
+ }
186
+ return await new Promise((resolve) => {
187
+ resolve(true)
188
+ })
189
+ },
190
+ reset() {
191
+ this.disabled = false
192
+ this.success = []
193
+ this.fail = []
194
+ this.input = this.shuffle(
195
+ _.cloneDeep(this.block.metadata.config.answer)
196
+ )
197
+ this.feedback = this.block.metadata.config.feedback_default
198
+ this.seed = Math.floor(Math.random() * 100)
199
+ },
200
+ continueGame() {
201
+ this.disabled = false
202
+ this.success = []
203
+ this.fail = []
204
+ this.feedback = this.block.metadata.config.feedback_default
205
+ this.seed = Math.floor(Math.random() * 100)
206
+ },
207
+ isSuccess() {
208
+ let result = false
209
+ let successCounter = 0
210
+ this.success.forEach(function (item) {
211
+ if (item === true) {
212
+ successCounter++
213
+ }
214
+ })
215
+ if (successCounter === this.success.length && successCounter > 0) {
216
+ result = true
217
+ }
218
+ return result
219
+ },
220
+ isFail() {
221
+ let result = false
222
+ let counter = 0
223
+ this.fail.forEach(function (item) {
224
+ if (item === true) {
225
+ result = true
226
+ }
227
+ })
228
+ return result
229
+ },
230
+ feedbackClass() {
231
+ let result = ''
232
+ if (this.isSuccess()) {
233
+ result = 'success '
234
+ }
235
+ if (this.isFail()) {
236
+ result = 'error '
237
+ }
238
+ return result
239
+ },
240
+ checkAnswers() {
241
+ const self = this
242
+ this.disabled = true
243
+
244
+ this.input.forEach(function (item, index) {
245
+ if (self.block.metadata.config.answer[index].id === item.id) {
246
+ self.success[index] = true
247
+ self.fail[index] = false
248
+ } else {
249
+ self.success[index] = false
250
+ self.fail[index] = true
251
+ }
252
+ })
253
+
254
+ if (this.isSuccess()) {
255
+ this.feedback = this.block.metadata.config.feedback_correct
256
+ this.emitCompleted()
257
+ }
258
+ if (this.isFail()) {
259
+ this.feedback = this.block.metadata.config.feedback_incorrect
260
+ }
261
+
262
+ this.seed = Math.floor(Math.random() * 100)
263
+ },
264
+ bucketStateClass(index) {
265
+ if (this.success[index - 1]) {
266
+ return 'bucket--success'
267
+ }
268
+ if (this.fail[index - 1]) {
269
+ return 'bucket--error'
270
+ }
271
+ return ''
272
+ },
273
+ },
274
+ }
275
+ </script>
276
+
277
+ <style scoped>
278
+ .ghost {
279
+ opacity: 0.5;
280
+ background: #c8ebfb;
281
+ }
282
+ .bucket {
283
+ border: solid 2px black;
284
+ color: black;
285
+ cursor: pointer;
286
+ padding: 0.3em;
287
+ margin: 0.3em;
288
+ vertical-align: middle;
289
+ display: inline-block;
290
+ }
291
+ .bucket--default {
292
+ border: dashed 2px #5cb2f1;
293
+ background-color: #ffffff;
294
+ }
295
+ .bucket--error {
296
+ border: dashed 2px #dc3d1a;
297
+ background-color: #fff1f1;
298
+ }
299
+ .bucket--success {
300
+ border: dashed 2px #76b778;
301
+ background-color: #f1fff3;
302
+ }
303
+ </style>
@@ -0,0 +1,112 @@
1
+ <template>
2
+ <div
3
+ v-bind:style="{
4
+ color: textColor,
5
+ }"
6
+ >
7
+ <v-row class="card-header" align="center" justify="center">
8
+ <content-viewer v-model="settings.header" />
9
+ </v-row>
10
+ <br />
11
+ <div class="card-content">
12
+ <v-row
13
+ :class="settings.img ? '' : ' text-justify '"
14
+ align="center"
15
+ justify="center"
16
+ >
17
+ <content-viewer :class="textClass" v-model="settings.text" />
18
+ </v-row>
19
+ <v-row align="center" justify="center" v-if="settings.img !== ''">
20
+ <v-img
21
+ contain
22
+ :aspect-ratio="16 / 9"
23
+ :src="settings.img"
24
+ :alt="settings.altText"
25
+ max-height="250"
26
+ max-width="600"
27
+ />
28
+ </v-row>
29
+ </div>
30
+
31
+ <v-row class="card-footer" align="center" justify="center">{{
32
+ settings.footer
33
+ }}</v-row>
34
+ </div>
35
+ </template>
36
+
37
+ <script>
38
+ import { MathHelper, ContentViewer } from '@windward/core/utils'
39
+
40
+ export default {
41
+ name: 'CardFace',
42
+ components: { ContentViewer },
43
+ props: {
44
+ settings: { type: Object },
45
+ textColor: { type: String },
46
+ },
47
+ computed: {
48
+ textClass() {
49
+ if (
50
+ !this.settings.img &&
51
+ this.settings.text &&
52
+ this.settings.text.length < 300
53
+ ) {
54
+ return 'centered'
55
+ }
56
+ if (
57
+ !this.settings.img &&
58
+ this.settings.text &&
59
+ (MathHelper.containsMathML(this.settings.text.length) ||
60
+ MathHelper.containsLatex(this.settings.text.length))
61
+ ) {
62
+ return 'centered'
63
+ }
64
+
65
+ if (
66
+ !this.settings.img &&
67
+ this.settings.text &&
68
+ this.settings.text.length > 100 &&
69
+ !(
70
+ MathHelper.containsMathML(this.settings.text.length) ||
71
+ MathHelper.containsLatex(this.settings.text.length)
72
+ )
73
+ ) {
74
+ return 'paragraph'
75
+ }
76
+
77
+ return ''
78
+ },
79
+ },
80
+ methods: {},
81
+ }
82
+ </script>
83
+
84
+ <style scoped>
85
+ .card-header {
86
+ font-weight: bold;
87
+ }
88
+ .card-content {
89
+ font-weight: bold;
90
+ margin: 5px;
91
+ }
92
+ .card-footer {
93
+ font-weight: bold;
94
+ }
95
+ .centered {
96
+ margin: 0;
97
+ position: absolute;
98
+ top: 50%;
99
+ left: 50%;
100
+ transform: translate(-50%, -50%);
101
+ }
102
+ .card-footer {
103
+ margin: 0;
104
+ position: absolute;
105
+ top: 95%;
106
+ left: 50%;
107
+ transform: translate(-50%, -50%);
108
+ }
109
+ .paragraph {
110
+ margin: 2%;
111
+ }
112
+ </style>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <v-container>
3
+ <v-card
4
+ @click="toggleCard"
5
+ v-show="value"
6
+ class="animated flipInY flashcard"
7
+ max-height="900"
8
+ min-height="460"
9
+ >
10
+ <card-face :settings="frontFace"></card-face>
11
+ </v-card>
12
+ <v-card
13
+ @click="toggleCard"
14
+ v-show="!value"
15
+ class="animated flipInY flashcard"
16
+ max-height="900"
17
+ min-height="460"
18
+ >
19
+ <card-face :settings="backFace"></card-face>
20
+ </v-card>
21
+ </v-container>
22
+ </template>
23
+ <script>
24
+ import CardFace from './CardFace'
25
+ export default {
26
+ components: { CardFace },
27
+ data() {
28
+ return {
29
+ settings: {},
30
+ }
31
+ },
32
+ props: {
33
+ options: { type: Object, default: {} },
34
+ slide: { type: Number, required: true, default: 0 },
35
+ value: { type: Boolean, required: false, default: false },
36
+ },
37
+
38
+ computed: {
39
+ frontFace() {
40
+ return {
41
+ img:
42
+ this.options.front.img && this.options.front.img.asset
43
+ ? this.options.front.img.asset.public_url
44
+ : '',
45
+ altText: this.options.front.imgAltText
46
+ ? this.options.front.imgAltText
47
+ : '',
48
+ text: this.options.front.text,
49
+ header: this.options.front.header,
50
+ footer: this.$t(
51
+ 'plugin.games.components.content.blocks.flashcard.click_to_show_back'
52
+ ),
53
+ }
54
+ },
55
+ backFace() {
56
+ return {
57
+ img:
58
+ this.options.back.img && this.options.back.img.asset
59
+ ? this.options.back.img.asset.public_url
60
+ : '',
61
+ altText: this.options.back.imgAltText
62
+ ? this.options.back.imgAltText
63
+ : '',
64
+ text: this.options.back.text,
65
+ header: this.options.back.header,
66
+ footer: this.$t(
67
+ 'plugin.games.components.content.blocks.flashcard.click_to_show_front'
68
+ ),
69
+ }
70
+ },
71
+ },
72
+ methods: {
73
+ toggleCard() {
74
+ this.$emit('input', !this.value)
75
+ },
76
+ },
77
+ }
78
+ </script>
79
+ <style scoped>
80
+ .center {
81
+ text-align: center;
82
+ }
83
+
84
+ .flashcard {
85
+ cursor: pointer;
86
+ border-radius: 10px;
87
+ max-height: 100vh;
88
+ margin: 0.5em;
89
+ padding: 25px;
90
+ box-shadow: 0 0px 10px rgba(0, 0, 0, 0.4);
91
+ text-align: center;
92
+ }
93
+
94
+ .flashcard:hover {
95
+ box-shadow: 0 0px 25px rgba(0, 0, 0, 0.8);
96
+ }
97
+
98
+ .animated {
99
+ animation-duration: 0.5s;
100
+ animation-fill-mode: both;
101
+ }
102
+
103
+ @keyframes flipInX {
104
+ from {
105
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
106
+ animation-timing-function: ease-in;
107
+ opacity: 0;
108
+ }
109
+ 40% {
110
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
111
+ animation-timing-function: ease-in;
112
+ }
113
+ 60% {
114
+ transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
115
+ opacity: 1;
116
+ }
117
+ 80% {
118
+ transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
119
+ }
120
+ to {
121
+ transform: perspective(400px);
122
+ }
123
+ }
124
+
125
+ @keyframes flipInY {
126
+ from {
127
+ transform: perspective(400px) rotateY(180deg);
128
+ animation-timing-function: ease-in;
129
+ opacity: 0;
130
+ }
131
+ 50% {
132
+ transform: perspective(400px) rotateY(290deg);
133
+ opacity: 0.25;
134
+ }
135
+
136
+ to {
137
+ transform: perspective(400px) rotateY(360deg);
138
+ opacity: 1;
139
+ }
140
+ }
141
+
142
+ .flipInX {
143
+ backface-visibility: visible !important;
144
+ animation-name: flipInX;
145
+ }
146
+ .flipInY {
147
+ backface-visibility: visible !important;
148
+ animation-name: flipInY;
149
+ }
150
+ </style>
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <div>
3
+ <h2 v-if="block.metadata.config.title">
4
+ {{ block.metadata.config.title }}
5
+ </h2>
6
+
7
+ <p v-if="block.metadata.config.instructions">
8
+ {{ block.metadata.config.instructions }}
9
+ </p>
10
+ <br />
11
+ <v-carousel
12
+ v-model="block.metadata.config.currentSlide"
13
+ show-arrows-on-hover
14
+ hide-delimiters
15
+ >
16
+ <template #prev="{ on, attrs }">
17
+ <v-btn
18
+ variant="elevated"
19
+ color="primary"
20
+ fab
21
+ class="nav-buttons"
22
+ v-bind="attrs"
23
+ v-on="on"
24
+ ><v-icon>mdi-chevron-left</v-icon></v-btn
25
+ >
26
+ </template>
27
+ <template #next="{ on, attrs }">
28
+ <v-btn
29
+ variant="elevated"
30
+ color="primary"
31
+ fab
32
+ class="nav-buttons"
33
+ v-bind="attrs"
34
+ v-on="on"
35
+ ><v-icon>mdi-chevron-right</v-icon></v-btn
36
+ >
37
+ </template>
38
+ <v-carousel-item
39
+ v-for="(card, index) in block.metadata.config.cards"
40
+ :key="index"
41
+ >
42
+ <v-sheet height="100%" tile>
43
+ <flashcard
44
+ :options="card"
45
+ :slide="block.metadata.config.currentSlide"
46
+ v-model="block.metadata.config.cards[index].side"
47
+ @click=""
48
+ :key="seed + '-' + block.metadata.config.currentSlide"
49
+ />
50
+ </v-sheet>
51
+ </v-carousel-item>
52
+ </v-carousel>
53
+
54
+ <v-row justify="center" align="center">
55
+ <div class="pa-3 text-center">
56
+ {{ slide }}/{{ block.metadata.config.cards.length }}
57
+ </div></v-row
58
+ >
59
+ </div>
60
+ </template>
61
+
62
+ <script>
63
+ import _ from 'lodash'
64
+ import Flashcard from './Flashcard'
65
+ import draggable from 'vuedraggable'
66
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
67
+ import Crypto from '~/helpers/Crypto'
68
+ export default {
69
+ name: 'Flashcards',
70
+ components: { Flashcard, draggable },
71
+ extends: BaseContentBlock,
72
+ data() {
73
+ return {
74
+ slideNumber: 0,
75
+ toggle: true,
76
+ seed: Crypto.id(),
77
+ cards: [],
78
+ }
79
+ },
80
+ beforeMount() {
81
+ if (_.isEmpty(this.block)) {
82
+ this.block = {}
83
+ }
84
+ if (_.isEmpty(this.block.metadata)) {
85
+ this.block.metadata = {}
86
+ }
87
+ if (_.isEmpty(this.block.metadata.config)) {
88
+ this.block.metadata.config = {}
89
+ this.block.metadata.config.currentSlide = 0
90
+ this.block.metadata.config.title = ''
91
+ this.block.metadata.config.instructions = ''
92
+ }
93
+
94
+ if (_.isEmpty(this.block.metadata.config.cards)) {
95
+ this.block.metadata.config.cards = []
96
+ }
97
+ },
98
+ computed: {
99
+ slide() {
100
+ if (this.block.metadata.config.cards.length > 0) {
101
+ return this.block.metadata.config.currentSlide + 1
102
+ }
103
+ return 0
104
+ },
105
+ },
106
+ methods: {
107
+ onActionPanelEdit() {
108
+ this.render = !this.render
109
+ },
110
+ },
111
+ }
112
+ </script>
113
+
114
+ <style scoped>
115
+ .bordered {
116
+ outline: #4caf50 solid 5px;
117
+ }
118
+ .control {
119
+ margin: 5px;
120
+ }
121
+ </style>