@windward/core 0.3.0 → 0.4.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 (63) hide show
  1. package/components/Content/Blocks/Accordion.vue +37 -0
  2. package/components/Content/Blocks/BlockQuote.vue +2 -2
  3. package/components/Content/Blocks/ClickableIcons.vue +108 -21
  4. package/components/Content/Blocks/Email.vue +19 -6
  5. package/components/Content/Blocks/Feedback.vue +12 -5
  6. package/components/Content/Blocks/Image.vue +47 -19
  7. package/components/Content/Blocks/OpenResponse.vue +7 -3
  8. package/components/Content/Blocks/OpenResponseCollate.vue +1 -1
  9. package/components/Content/Blocks/ScenarioChoice.vue +2 -2
  10. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +2 -0
  11. package/components/Content/Blocks/UserUpload.vue +1 -0
  12. package/components/Content/Blocks/Video.vue +82 -11
  13. package/components/Navigation/Items/AskTheExpert.vue +1 -0
  14. package/components/Settings/AccordionSettings.vue +55 -0
  15. package/components/Settings/ClickableIconsSettings.vue +89 -8
  16. package/components/Settings/EmailSettings.vue +6 -10
  17. package/components/Settings/FeedbackSettings.vue +1 -0
  18. package/components/Settings/ImageSettings.vue +142 -39
  19. package/components/Settings/MathSettings.vue +1 -1
  20. package/components/Settings/OpenResponseSettings.vue +1 -2
  21. package/components/Settings/ScenarioChoiceSettings.vue +1 -0
  22. package/components/Settings/TabSettings.vue +7 -1
  23. package/components/Settings/TextEditorSettings.vue +3 -2
  24. package/components/Settings/UserUploadSettings.vue +1 -1
  25. package/components/Settings/VideoSettings.vue +102 -63
  26. package/components/utils/ContentViewer.vue +6 -1
  27. package/components/utils/FillInBlank/FillInBlankInput.vue +3 -0
  28. package/components/utils/MathExpressionEditor.vue +37 -11
  29. package/components/utils/MathLiveWrapper.vue +47 -25
  30. package/components/utils/TinyMCEWrapper.vue +214 -34
  31. package/components/utils/assets/tinymce/content/dark/content.scss +4 -0
  32. package/components/utils/assets/tinymce/{css/content.scss → content/global.scss} +38 -37
  33. package/components/utils/assets/tinymce/content/light/content.scss +4 -0
  34. package/components/utils/assets/tinymce/ui/dark/content.scss +803 -0
  35. package/components/utils/assets/tinymce/ui/dark/skin.scss +4727 -0
  36. package/components/utils/assets/tinymce/ui/global.scss +19 -0
  37. package/components/utils/assets/tinymce/ui/light/content.scss +822 -0
  38. package/components/utils/assets/tinymce/ui/light/skin.scss +4731 -0
  39. package/components/utils/glossary/CourseGlossary.vue +3 -1
  40. package/config/tinymce.config.ts +32 -20
  41. package/helpers/FillInBlankHelper.ts +34 -28
  42. package/helpers/GlossaryHelper.ts +90 -73
  43. package/helpers/MathHelper.ts +108 -28
  44. package/helpers/tinymce/WindwardPlugins.ts +335 -0
  45. package/i18n/en-US/components/settings/clickable_icon.ts +2 -0
  46. package/i18n/en-US/components/settings/image.ts +6 -1
  47. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
  48. package/i18n/en-US/shared/settings.ts +3 -0
  49. package/i18n/es-ES/components/settings/clickable_icon.ts +2 -0
  50. package/i18n/es-ES/components/settings/image.ts +8 -1
  51. package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +1 -0
  52. package/i18n/es-ES/shared/settings.ts +3 -0
  53. package/i18n/sv-SE/components/settings/clickable_icon.ts +2 -0
  54. package/i18n/sv-SE/components/settings/image.ts +6 -1
  55. package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +1 -0
  56. package/i18n/sv-SE/shared/settings.ts +3 -0
  57. package/package.json +6 -4
  58. package/test/Components/Settings/AccordionSettings.spec.js +16 -2
  59. package/test/__mocks__/contentBlockMock.js +6 -0
  60. package/test/__mocks__/contentSettingsMock.js +6 -0
  61. package/test/helpers/MathHelper.spec.js +36 -4
  62. package/tsconfig.json +4 -0
  63. package/helpers/tinymce/plugin.ts +0 -208
@@ -45,11 +45,12 @@
45
45
  <v-tab-item class="mb-1">
46
46
  <ContentBlockAsset
47
47
  v-model="media.source"
48
- mimes="video/mp4,audio/mpeg,video/webm"
48
+ :assets.sync="block.assets"
49
+ mimes="video/mp4,audio/mpeg,video/webm,video/youtube"
49
50
  allow-url
50
51
  class="mb-4"
51
52
  :disabled="render"
52
- @click:file="onSourceSelect"
53
+ @change="onSourceSelect"
53
54
  >
54
55
  <template #title>
55
56
  {{
@@ -66,14 +67,14 @@
66
67
  }}
67
68
  </template>
68
69
  </ContentBlockAsset>
69
-
70
70
  <ContentBlockAsset
71
71
  v-model="media.track"
72
+ :assets.sync="block.assets"
72
73
  mimes="text/vtt,text/xml"
73
74
  allow-url
74
75
  class="mb-4"
75
76
  :disabled="render"
76
- @click:file="onTrackSelect"
77
+ @change="onTrackSelect"
77
78
  >
78
79
  <template #title>
79
80
  {{
@@ -95,10 +96,11 @@
95
96
  <v-tab-item class="mb-1">
96
97
  <ContentBlockAsset
97
98
  v-model="media.poster"
99
+ :assets.sync="block.assets"
98
100
  mimes="image/png,image/jpeg"
99
101
  allow-url
100
102
  :disabled="render"
101
- @click:file="onPosterSelect"
103
+ @change="onPosterSelect"
102
104
  >
103
105
  <template #title>
104
106
  {{
@@ -120,11 +122,12 @@
120
122
  <v-tab-item class="mb-1">
121
123
  <ContentBlockAsset
122
124
  v-model="media.ads.preroll.source"
125
+ :assets.sync="block.assets"
123
126
  mimes="video/mp4,video/webm"
124
127
  allow-url
125
128
  class="mb-4"
126
129
  :disabled="render"
127
- @click:file="onAdSourceSelect($event, 0)"
130
+ @change="onAdSourceSelect($event, 0)"
128
131
  >
129
132
  <template #title>
130
133
  {{
@@ -144,10 +147,11 @@
144
147
 
145
148
  <ContentBlockAsset
146
149
  v-model="media.ads.preroll.track"
150
+ :assets.sync="block.assets"
147
151
  mimes="text/vtt,text/xml"
148
152
  allow-url
149
153
  :disabled="render"
150
- @click:file="onAdTrackSelect($event, 0)"
154
+ @change="onAdTrackSelect($event, 0)"
151
155
  >
152
156
  <template #title>
153
157
  {{
@@ -169,11 +173,12 @@
169
173
  <v-tab-item class="mb-1">
170
174
  <ContentBlockAsset
171
175
  v-model="media.ads.postroll.source"
176
+ :assets.sync="block.assets"
172
177
  mimes="video/mp4,video/webm"
173
178
  allow-url
174
179
  class="mb-4"
175
180
  :disabled="render"
176
- @click:file="onAdSourceSelect($event, 100)"
181
+ @change="onAdSourceSelect($event, 100)"
177
182
  >
178
183
  <template #title>
179
184
  {{
@@ -193,10 +198,11 @@
193
198
 
194
199
  <ContentBlockAsset
195
200
  v-model="media.ads.postroll.track"
201
+ :assets.sync="block.assets"
196
202
  mimes="text/vtt,text/xml"
197
203
  allow-url
198
204
  :disabled="render"
199
- @click:file="onAdTrackSelect($event, 100)"
205
+ @change="onAdTrackSelect($event, 100)"
200
206
  >
201
207
  <template #title>
202
208
  {{
@@ -267,6 +273,7 @@
267
273
  v-bind="attrs"
268
274
  v-on="on"
269
275
  text
276
+ elevation="0"
270
277
  color="error"
271
278
  :disabled="
272
279
  render ||
@@ -296,6 +303,7 @@
296
303
  <v-btn
297
304
  v-bind="attrs"
298
305
  v-on="on"
306
+ elevation="0"
299
307
  color="primary"
300
308
  text
301
309
  :disabled="render"
@@ -330,6 +338,7 @@
330
338
  <v-row>
331
339
  <v-col cols="12">
332
340
  <v-select
341
+ id="video-playback-rates"
333
342
  v-model="
334
343
  block.metadata.config.attributes
335
344
  .playbackrates
@@ -454,8 +463,9 @@
454
463
  </div>
455
464
  </template>
456
465
  <script>
466
+ import _ from 'lodash'
457
467
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
458
-
468
+ import Uuid from '~/helpers/Uuid'
459
469
  export default {
460
470
  name: 'VideoSettings',
461
471
  extends: BaseContentSettings,
@@ -495,20 +505,83 @@ export default {
495
505
  this.playlistPaginator = 1
496
506
  }
497
507
  },
498
- mounted() {},
508
+ mounted() {
509
+ this.reloadMedia()
510
+ },
499
511
  methods: {
500
512
  reloadMedia() {
501
- this.media.source = null
502
- this.media.track = null
503
- this.media.poster = null
513
+ // Prefill the media sources with the related file asset
514
+ // If the source is empty then it will just return null
515
+ this.media.source = this.resolveAsset(
516
+ _.get(
517
+ this.block,
518
+ 'metadata.config.playlist[' +
519
+ this.playlistIndex +
520
+ '].sources[0]',
521
+ null
522
+ )
523
+ )
524
+ this.media.track = this.resolveAsset(
525
+ _.get(
526
+ this.block,
527
+ 'metadata.config.playlist[' +
528
+ this.playlistIndex +
529
+ '].tracks[0]',
530
+ null
531
+ )
532
+ )
533
+ this.media.poster = this.resolveAsset(
534
+ _.get(this.block, 'metadata.config.attributes.poster', null)
535
+ )
536
+
504
537
  this.media.ads = {
505
538
  preroll: {
506
- source: null,
507
- track: null,
539
+ source: this.resolveAsset(
540
+ _.get(
541
+ this.block,
542
+ 'metadata.config.playlist[' +
543
+ this.playlistIndex +
544
+ '].ads[' +
545
+ this.getAdSlot(0) +
546
+ '].sources[0]',
547
+ null
548
+ )
549
+ ),
550
+ track: this.resolveAsset(
551
+ _.get(
552
+ this.block,
553
+ 'metadata.config.playlist[' +
554
+ this.playlistIndex +
555
+ '].ads[' +
556
+ this.getAdSlot(0) +
557
+ '].tracks[0]',
558
+ null
559
+ )
560
+ ),
508
561
  },
509
562
  postroll: {
510
- source: null,
511
- track: null,
563
+ source: this.resolveAsset(
564
+ _.get(
565
+ this.block,
566
+ 'metadata.config.playlist[' +
567
+ this.playlistIndex +
568
+ '].ads[' +
569
+ this.getAdSlot(100) +
570
+ '].sources[0]',
571
+ null
572
+ )
573
+ ),
574
+ track: this.resolveAsset(
575
+ _.get(
576
+ this.block,
577
+ 'metadata.config.playlist[' +
578
+ this.playlistIndex +
579
+ '].ads[' +
580
+ this.getAdSlot(100) +
581
+ '].tracks[0]',
582
+ null
583
+ )
584
+ ),
512
585
  },
513
586
  }
514
587
  },
@@ -593,19 +666,11 @@ export default {
593
666
  return this.block.metadata.config.playlist.length - 1
594
667
  },
595
668
  onPosterSelect(file) {
596
- if (file === null) {
597
- delete this.block.metadata.config.attributes
598
- .poster_file_asset_id
599
- this.block.metadata.config.attributes.poster = ''
600
- } else {
601
- this.block.metadata.config.attributes.poster_file_asset_id =
602
- file.file_asset_id
603
- this.block.metadata.config.attributes.poster =
604
- file.asset.public_url
605
- }
669
+ this.block.metadata.config.attributes.poster = file
670
+
671
+ this.reloadVideo()
606
672
  },
607
673
  onSourceSelect(file) {
608
- this.reloadVideo()
609
674
  // No file, clear the selection
610
675
  if (_.isEmpty(file)) {
611
676
  delete this.block.metadata.config.playlist[this.playlistIndex]
@@ -626,37 +691,23 @@ export default {
626
691
  // Apply the playlist source video(s)
627
692
  this.block.metadata.config.playlist[
628
693
  this.playlistIndex
629
- ].sources = [
630
- {
631
- file_asset_id: file.file_asset_id,
632
- src: file.asset.public_url,
633
- type: file.asset.metadata.mime,
634
- },
635
- ]
694
+ ].sources = [file]
636
695
 
637
696
  this.showVideo = true
638
697
  }
698
+ this.reloadVideo()
639
699
  },
640
700
  onTrackSelect(file) {
641
- this.reloadVideo()
642
701
  if (_.isEmpty(file)) {
643
702
  this.block.metadata.config.playlist[this.playlistIndex].tracks =
644
703
  []
645
704
  this.showVideo = false
646
705
  } else {
647
706
  this.block.metadata.config.playlist[this.playlistIndex].tracks =
648
- [
649
- {
650
- file_asset_id: file.file_asset_id,
651
- src: file.asset.public_url,
652
- kind: 'captions',
653
- srclang: 'en-US',
654
- default: true,
655
- },
656
- ]
707
+ [file]
657
708
  }
709
+ this.reloadVideo()
658
710
  },
659
-
660
711
  onAdSourceSelect(file, playAtPercent) {
661
712
  const adIndex = this.getAdSlot(playAtPercent)
662
713
 
@@ -674,14 +725,9 @@ export default {
674
725
 
675
726
  this.block.metadata.config.playlist[this.playlistIndex].ads[
676
727
  adIndex
677
- ].sources = [
678
- {
679
- file_asset_id: file.file_asset_id,
680
- src: file.asset.public_url,
681
- type: file.asset.metadata.mime,
682
- },
683
- ]
728
+ ].sources = [file]
684
729
  }
730
+ this.reloadVideo()
685
731
  },
686
732
  onAdTrackSelect(file, playAtPercent) {
687
733
  const adIndex = this.getAdSlot(playAtPercent)
@@ -697,16 +743,9 @@ export default {
697
743
 
698
744
  this.block.metadata.config.playlist[this.playlistIndex].ads[
699
745
  adIndex
700
- ].tracks = [
701
- {
702
- file_asset_id: file.file_asset_id,
703
- src: file.asset.public_url,
704
- kind: 'captions',
705
- srclang: 'en-US',
706
- default: true,
707
- },
708
- ]
746
+ ].tracks = [file]
709
747
  }
748
+ this.reloadVideo()
710
749
  },
711
750
  onPlaybackChange(e) {
712
751
  // Sort the list so we don't get instances of 2, 1, 0.25, 3
@@ -79,6 +79,11 @@ img {
79
79
  width: 100% !important;
80
80
  }
81
81
  .text-viewer {
82
- @import './assets/tinymce/css/content';
82
+ @import './assets/tinymce/content/global';
83
+ }
84
+ .ML__text {
85
+ font-family: 'Roboto', sans-serif !important;
86
+ font-size: 1rem;
87
+ line-height: 1.75em;
83
88
  }
84
89
  </style>
@@ -34,6 +34,7 @@
34
34
  ><v-btn
35
35
  ref="action_button_1"
36
36
  color="primary"
37
+ elevation="0"
37
38
  outlined
38
39
  @click="show = false"
39
40
  >
@@ -50,6 +51,7 @@
50
51
  <v-btn
51
52
  color="primary"
52
53
  outlined
54
+ elevation="0"
53
55
  @click="displayAnswer"
54
56
  v-if="!answerIsCorrect"
55
57
  >
@@ -92,6 +94,7 @@
92
94
  <v-btn
93
95
  v-if="userInput && userInput.length > 0"
94
96
  @click="toggleToolTip"
97
+ elevation="0"
95
98
  color="primary"
96
99
  outlined
97
100
  :aria-label="
@@ -41,14 +41,17 @@
41
41
  :key="buttonIndex"
42
42
  class="btn"
43
43
  @click="insert(button)"
44
- v-html="'<strong>'+convertLatexToMarkup(button.text)+'</strong>'"
45
- >
46
- </button>
44
+ v-html="
45
+ '<strong>' +
46
+ convertLatexToMarkup(button.text) +
47
+ '</strong>'
48
+ "
49
+ ></button>
47
50
  </div>
48
51
  </v-container>
49
52
  </v-tab-item>
50
53
  </v-tabs-items>
51
-
54
+ <br />
52
55
  <math-live-wrapper
53
56
  ref="mathfield"
54
57
  v-model="formula"
@@ -89,6 +92,7 @@
89
92
  <v-btn
90
93
  primary
91
94
  color="success"
95
+ elevation="0"
92
96
  @click="setSRText"
93
97
  :class="fontSize"
94
98
  >{{
@@ -98,7 +102,7 @@
98
102
  }}
99
103
  </v-btn>
100
104
  &nbsp;
101
- <v-btn color="primary" :class="fontSize" @click="readSRText"
105
+ <v-btn color="primary" elevation="0" :class="fontSize" @click="readSRText"
102
106
  >{{
103
107
  $t(
104
108
  'windward.core.components.utils.math_expression_editor.read_text_aloud'
@@ -131,7 +135,7 @@ export default {
131
135
  spokenText: '',
132
136
  spokenTextChanged: false,
133
137
  options: {
134
- smartFence: true,
138
+ smartFence: false,
135
139
  smartMode: false,
136
140
  virtualKeyboardMode: 'none',
137
141
  keypressSound: 'none',
@@ -242,16 +246,38 @@ export default {
242
246
  this.$emit('input', latex)
243
247
  }
244
248
  },
245
- getBtnCode(button) {
246
- if (button.cmd) {
249
+ getBtnCode(button, isAlpha) {
250
+ if (button.cmd && isAlpha) {
247
251
  return button.latex
248
252
  }
249
253
  return button.text
250
254
  },
255
+
256
+ isLastCharValid(str, pos = 0) {
257
+ const lastChar = str.slice(pos, pos + 1)
258
+ const alphanumericRegex = /^[a-z0-9]$/i
259
+ const validSymbols = [')', '}', '|']
260
+ return (
261
+ alphanumericRegex.test(lastChar) ||
262
+ validSymbols.includes(lastChar)
263
+ )
264
+ },
251
265
  insert(latex) {
252
- this.$refs.mathfield.insert(this.getBtnCode(latex), {
253
- selectionMode: 'placeholder',
254
- })
266
+ const existingText = this.$refs.mathfield.getValue()
267
+ this.$refs.mathfield.insert(
268
+ this.getBtnCode(
269
+ latex,
270
+ this.isLastCharValid(
271
+ existingText,
272
+ this.$refs.mathfield.$el.position - 1
273
+ )
274
+ ),
275
+ {
276
+ selectionMode: 'placeholder',
277
+ insertionMode: 'replaceSelection',
278
+ focus: true,
279
+ }
280
+ )
255
281
  },
256
282
 
257
283
  setSRText() {
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <math-field :id="id" style="font-size: 1.5rem; display: block"
2
+ <math-field
3
+ :id="id"
4
+ style="font-size: 1.5rem; display: block"
5
+ @change="onInput"
6
+ @input="onInput"
3
7
  ><slot></slot
4
8
  ></math-field>
5
9
  </template>
@@ -21,6 +25,13 @@ export default {
21
25
  }
22
26
  },
23
27
  watch: {
28
+ value(newVal) {
29
+ let current = this.$el.getValue()
30
+ newVal !== current &&
31
+ this.$el.setValue(newVal, {
32
+ silenceNotifications: !0,
33
+ })
34
+ },
24
35
  options: {
25
36
  deep: true,
26
37
  handler(newValue) {
@@ -29,30 +40,27 @@ export default {
29
40
  },
30
41
  },
31
42
  mounted() {
32
- this.$nextTick(() => {
33
- this.$el.setOptions({
34
- ...this.options,
35
- macros: {
36
- ...this.$el.getOptions('macros'),
37
- smallfrac: '{}^{#1}\\!\\!/\\!{}_{#2}',
38
- },
39
- onContentDidChange: () => {
40
- this.$emit('input', this.$el.getValue())
41
- this.$emit('change')
42
- },
43
- onFocus: () => {
44
- this.$emit('focus')
45
- },
46
- onBlur: () => {
47
- this.$emit('blur')
48
- },
49
- onUndoStateDidChange: (t, e) => {
50
- this.$emit('undo-state-did-change', e)
51
- },
52
- onReadAloudStatus: (t, e) => {
53
- this.$emit('read-aloud-status-change', e)
54
- },
55
- })
43
+ //this.$nextTick(() => this.$el.setOptions(this.options))
44
+ this.$el.mathVirtualKeyboardPolicy = 'manual'
45
+
46
+ this.$el.menuItems = this.$el.menuItems.filter((item) => {
47
+ const itemIdPrefix = item.id ? item.id.split('-')[0] : null
48
+
49
+ if (itemIdPrefix === 'color' || itemIdPrefix === 'background') {
50
+ return false
51
+ }
52
+
53
+ if (
54
+ !_.isEmpty(item.submenu) &&
55
+ typeof item.submenu !== 'undefined'
56
+ ) {
57
+ item.submenu = item.submenu.filter((subItem) => {
58
+ return subItem.id
59
+ ? !subItem.id.startsWith('mode-latex')
60
+ : true
61
+ })
62
+ }
63
+ return true
56
64
  })
57
65
  if (this.value !== '') {
58
66
  this.$el.setValue(this.value)
@@ -83,6 +91,20 @@ export default {
83
91
  setValue(value) {
84
92
  this.$el.setValue(value)
85
93
  },
94
+ onInput() {
95
+ this.$emit('input', this.$el.getValue())
96
+ this.$emit('change')
97
+ },
86
98
  },
87
99
  }
88
100
  </script>
101
+ <style>
102
+ @media not (pointer: coarse) {
103
+ math-field::part(virtual-keyboard-toggle) {
104
+ display: none;
105
+ }
106
+ math-field::part(menu-toggle) {
107
+ display: none;
108
+ }
109
+ }
110
+ </style>