comand-component-library 3.3.74 → 3.3.76

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="['cmd-headline', {'has-pre-headline-text': preHeadlineText, 'has-icon': headlineIcon?.iconClass}, getTextAlign]">
2
+ <div v-if="!editModeContext?.editing" :class="['cmd-headline', {'has-pre-headline-text': preHeadlineText, 'has-icon': headlineIcon?.iconClass}, getTextAlign]">
3
3
  <!-- begin CmdIcon -->
4
4
  <CmdIcon v-if="headlineIcon" :iconClass="headlineIcon?.iconClass" :type="headlineIcon?.iconType" />
5
5
  <!-- end CmdIcon -->
@@ -18,11 +18,24 @@
18
18
  <!-- end slot -->
19
19
  </component>
20
20
  </div>
21
+ <!-- begin edit-mode -->
22
+ <input v-else type="text" :class="['edit-mode', 'headline', 'h'+ headlineLevel]" v-model="editableHeadlineText" />
23
+ <!-- end edit-mode -->
21
24
  </template>
22
25
 
23
26
  <script>
24
27
  export default {
25
28
  name: "CmdHeadline",
29
+ inject: {
30
+ editModeContext: {
31
+ default: null
32
+ }
33
+ },
34
+ data() {
35
+ return {
36
+ editableHeadlineText: this.headlineText
37
+ }
38
+ },
26
39
  props: {
27
40
  /**
28
41
  * text for headline
@@ -60,8 +73,14 @@ export default {
60
73
  textAlign: {
61
74
  type: String,
62
75
  default: null
76
+ },
77
+ editModeContextData: {
78
+ type: Object
63
79
  }
64
80
  },
81
+ mounted() {
82
+ this.editModeContext?.addSaveHandler(this.onSave)
83
+ },
65
84
  computed: {
66
85
  getHeadlineTag() {
67
86
  if(this.headlineLevel) {
@@ -75,59 +94,73 @@ export default {
75
94
  }
76
95
  return ""
77
96
  }
97
+ },
98
+ methods: {
99
+ onSave() {
100
+ const headlineText = this.editableHeadlineText
101
+ return {
102
+ editModeContextData: {
103
+ ...(this.editModeContextData || {})
104
+ },
105
+ headlineText,
106
+ update(props) {
107
+ props.headlineText = headlineText
108
+ }
109
+ }
110
+ }
78
111
  }
79
112
  }
80
113
  </script>
81
114
 
82
115
  <style lang="scss">
83
116
  /* begin cmd-headline ------------------------------------------------------------------------------------------ */
84
- @import '../assets/styles/variables';
117
+ //@import '../assets/styles/variables';
85
118
 
86
119
  .cmd-headline {
87
- margin-bottom: var(--default-margin);
88
- gap: calc(var(--default-gap) / 2);
120
+ margin-bottom: var(--default-margin);
121
+ gap: calc(var(--default-gap) / 2);
89
122
 
90
- &.text-center > * {
91
- text-align: center;
92
- }
123
+ &.text-center > * {
124
+ text-align: center;
125
+ }
93
126
 
94
- &.text-right > * {
95
- text-align: right;
96
- }
127
+ &.text-right > * {
128
+ text-align: right;
129
+ }
97
130
 
98
- &.has-icon {
99
- display: flex;
100
- align-items: center;
101
- }
131
+ &.has-icon {
132
+ display: flex;
133
+ align-items: center;
134
+ }
102
135
 
103
- &.has-pre-headline-text {
104
- text-align: inherit;
136
+ &.has-pre-headline-text {
137
+ text-align: inherit;
105
138
 
106
- [class*="icon-"] {
107
- font-size: 5rem;
108
- }
139
+ [class*="icon-"] {
140
+ font-size: 5rem;
109
141
  }
142
+ }
110
143
 
111
- p {
112
- margin-bottom: 0;
113
- }
144
+ p {
145
+ margin-bottom: 0;
146
+ }
114
147
 
115
- h1, h2, h3, h4, h5, h6 {
116
- margin: 0;
148
+ h1, h2, h3, h4, h5, h6 {
149
+ margin: 0;
117
150
 
118
- &:only-child {
119
- flex: none;
120
- width: 100%;
121
- }
151
+ &:only-child {
152
+ flex: none;
153
+ width: 100%;
122
154
  }
155
+ }
123
156
 
124
- @media only screen and (max-width: $small-max-width) {
125
- flex-direction: column;
126
-
127
- h1 {
128
- margin-bottom: calc(var(--default-margin) * 2);
129
- }
130
- }
157
+ //@media only screen and (max-width: $small-max-width) {
158
+ // flex-direction: column;
159
+ //
160
+ // h1 {
161
+ // margin-bottom: calc(var(--default-margin) * 2);
162
+ // }
163
+ //}
131
164
  }
132
165
  /* end cmd-headline ------------------------------------------------------------------------------------------ */
133
166
  </style>
@@ -1,24 +1,142 @@
1
1
  <template>
2
- <figure :class="['cmd-image', getTextAlign]">
3
- <figcaption v-if="figcaption?.show && figcaption?.position === 'top'">{{figcaption?.text}}</figcaption>
4
- <img :src="imageSource" :alt="image.alt" :title="image.tooltip" />
5
- <figcaption v-if="figcaption?.show && figcaption?.position !== 'top'">{{figcaption?.text}}</figcaption>
2
+ <!-- begin edit-mode -->
3
+ <figure v-if="editModeContext?.editing" :class="['cmd-image flex-container vertical', getTextAlign]">
4
+ <CmdFormElement
5
+ element="input"
6
+ type="checkbox"
7
+ :toggleSwitch="true"
8
+ labelText="Show figcaption"
9
+ v-model="editableShowFigcaption"
10
+ />
11
+ <CmdFormElement
12
+ element="select"
13
+ labelText="Figcaption Position"
14
+ :selectOptions="positionOptions"
15
+ :disabled="!showFigcaption"
16
+ v-model="editableFigcaptionPosition"
17
+ />
18
+ <CmdFormElement
19
+ element="select"
20
+ labelText="Figcaption text alignment"
21
+ :selectOptions="textAlignOptions"
22
+ :disabled="!showFigcaption"
23
+ v-model="editableFigcaptionTextAlign"
24
+ />
25
+ <CmdFormElement element="input" type="text" :required="true" labelText="Alternative Text"
26
+ v-model="editableAlternativeText"/>
27
+ <CmdFormElement element="input" type="text" :required="false" labelText="Tooltip" v-model="editableTooltip"/>
28
+
29
+ <template v-if="figcaption?.position === 'top'">
30
+ <CmdFormElement element="input" type="text" :required="true" labelText="Text figcaption"
31
+ v-model="editableFigcaptionText"/>
32
+ </template>
33
+ <div :class="['box drop-area flex-container vertical', { 'allow-drop': allowDrop }]" v-on="dragAndDropHandler" title="Drag new image to this area to replace old one!">
34
+ <span class="icon-image"></span>
35
+ <img ref="contentImage" :src="image.src" :alt="image.alt" :title="image.tooltip"/>
36
+ </div>
37
+ <button
38
+ type="button"
39
+ :class="['button upload primary', { disabled: uploadInitiated }]"
40
+ :disabled="uploadInitiated"
41
+ @click="selectFiles()"
42
+ >
43
+ <!-- begin CmdIcon -->
44
+ <CmdIcon iconClass="icon-loop"/>
45
+ <!-- end CmdIcon -->
46
+ <span>Select image</span>
47
+ </button>
48
+ <template v-if="figcaption?.position !== 'top'">
49
+ <CmdFormElement
50
+ element="input"
51
+ type="text"
52
+ :class="getTextAlign"
53
+ :required="true"
54
+ labelText="Text figcaption"
55
+ v-model="editableFigcaptionText"
56
+ />
57
+ </template>
58
+ </figure>
59
+
60
+ <!-- begin CmdFormElement -->
61
+ <CmdFormElement
62
+ v-if="editModeContext?.editing"
63
+ class="hidden"
64
+ element="input"
65
+ type="file"
66
+ labelText="Select file"
67
+ :disabled="uploadInitiated"
68
+ @change="fileSelected"
69
+ ref="formElement"
70
+ />
71
+ <!-- end CmdFormElement -->
72
+ <!-- end edit-mode -->
73
+
74
+ <figure v-else :class="['cmd-image', getTextAlign]">
75
+ <figcaption v-if="figcaption?.show && figcaption?.position === 'top'">{{ figcaption?.text }}</figcaption>
76
+ <img :src="imageSource" :alt="image.alt" :title="image.tooltip"/>
77
+ <figcaption v-if="figcaption?.show && figcaption?.position !== 'top'">{{ figcaption?.text }}</figcaption>
6
78
  </figure>
7
79
  </template>
8
80
 
9
81
  <script>
82
+ import {getFileExtension} from "../utils/getFileExtension"
83
+
10
84
  export default {
85
+ name: "CmdImage",
86
+ inject: {
87
+ editModeContext: {
88
+ default: null
89
+ }
90
+ },
11
91
  data() {
12
92
  return {
13
93
  mediumMaxWidth: 1023,
14
94
  smallMaxWidth: 600,
15
- currentWindowWidth: window.innerWidth
95
+ currentWindowWidth: window.innerWidth,
96
+ allowedFileExtensions: ["jpg", "jpeg", "png"],
97
+ uploadInitiated: false,
98
+ allowDrop: false,
99
+ showFigcaption: true,
100
+ figcaptionPosition: null,
101
+ figcaptionTextAlign: null,
102
+ tooltip: null,
103
+ alternativeText: null,
104
+ positionOptions: [
105
+ {
106
+ text: "Above image",
107
+ value: "top"
108
+ },
109
+ {
110
+ text: "Below image",
111
+ value: "bottom"
112
+ }
113
+ ],
114
+ textAlignOptions: [
115
+ {
116
+ text: "Left",
117
+ value: "left"
118
+ },
119
+ {
120
+ text: "Center",
121
+ value: "center"
122
+ },
123
+ {
124
+ text: "Right",
125
+ value: "right"
126
+ }
127
+ ],
128
+ figcaptionText: null
16
129
  }
17
130
  },
18
- name: "CmdImage",
131
+ mounted() {
132
+ this.editModeContext?.addSaveHandler(this.onSave)
133
+ },
19
134
  props: {
135
+ editModeContextData: {
136
+ type: Object
137
+ },
20
138
  /**
21
- * image-object including source(s), alternative text, tooltip (not required)
139
+ * image-object including source, alternative text, tooltip (not required)
22
140
  */
23
141
  image: {
24
142
  type: Object,
@@ -30,6 +148,17 @@ export default {
30
148
  figcaption: {
31
149
  type: Object,
32
150
  required: false
151
+ },
152
+ /**
153
+ * max file size (in bytes) for file to upload
154
+ */
155
+ maxFileUploadSize: {
156
+ type: Number,
157
+ default: 500000
158
+ },
159
+ minImageWidth: {
160
+ type: Number,
161
+ default: 1025
33
162
  }
34
163
  },
35
164
  created() {
@@ -45,12 +174,6 @@ export default {
45
174
  window.removeEventListener("resize", this.updateWindowWidth)
46
175
  },
47
176
  computed: {
48
- getTextAlign() {
49
- if(this.figcaption?.textAlign) {
50
- return "text-" + this.figcaption.textAlign
51
- }
52
- return ''
53
- },
54
177
  imageSource() {
55
178
  // if only one src exists
56
179
  const imgSrc = this.image.src
@@ -70,11 +193,193 @@ export default {
70
193
  }
71
194
  // else return large (will be used if images for small-and -medium-devices do not exist or if screen resolution is larger than mediumMaxWidth)
72
195
  return imgSrc.large
196
+ },
197
+ dragAndDropHandler() {
198
+ // register handlers only if drag-and-drop is enabled
199
+ return {
200
+ dragenter: this.dragEnter,
201
+ dragover: this.dragOver,
202
+ dragleave: this.dragLeave,
203
+ drop: this.drop
204
+ }
205
+ },
206
+ getTextAlign() {
207
+ if (this.figcaption?.textAlign) {
208
+ return "text-" + this.figcaption.textAlign
209
+ }
210
+ return ''
211
+ },
212
+ editableAlternativeText: {
213
+ get() {
214
+ return this.alternativeText == null ? this.image.alt : this.alternativeText
215
+ },
216
+ set(value) {
217
+ this.alternativeText = value
218
+ }
219
+ },
220
+ editableTooltip: {
221
+ get() {
222
+ return this.tooltip == null ? this.image.tooltip : this.tooltip
223
+ },
224
+ set(value) {
225
+ this.tooltip = value
226
+ }
227
+ },
228
+ editableShowFigcaption: {
229
+ get() {
230
+ return this.showFigcaption == null ? this.figcaption.show : this.showFigcaption
231
+ },
232
+ set(value) {
233
+ this.showFigcaption = value
234
+ }
235
+ },
236
+ editableFigcaptionPosition: {
237
+ get() {
238
+ return this.figcaptionPosition == null ? this.figcaption.position : this.figcaptionPosition
239
+ },
240
+ set(value) {
241
+ this.figcaptionPosition = value
242
+ }
243
+ },
244
+ editableFigcaptionTextAlign: {
245
+ get() {
246
+ return this.figcaptionTextAlign || this.figcaption.textAlign
247
+ },
248
+ set(value) {
249
+ this.figcaptionTextAlign = value
250
+ }
251
+ },
252
+ editableFigcaptionText: {
253
+ get() {
254
+ return this.figcaptionText || this.figcaption.text
255
+ },
256
+ set(value) {
257
+ this.figcaptionText = value
258
+ }
73
259
  }
74
260
  },
75
261
  methods: {
76
262
  updateWindowWidth() {
77
263
  this.currentWindowWidth = window.innerWidth
264
+ },
265
+ getImage() {
266
+ return {
267
+ image: {...this.image},
268
+ figcaption: {
269
+ show: this.editableShowFigcaption,
270
+ position: this.editableFigcaptionPosition,
271
+ textAlign: this.editableFigcaptionTextAlign,
272
+ text: this.editableFigcaptionText
273
+ }
274
+ }
275
+ },
276
+ fileSelected(event) {
277
+ if (event.target.files.length > 0) {
278
+ this.checkAndUploadFile(event.target.files[0])
279
+ }
280
+ },
281
+ selectFiles() {
282
+ let inputFile = this.$refs.formElement.getDomElement().querySelector("input[type='file']")
283
+ inputFile.click()
284
+ },
285
+ dragEnter(event) {
286
+ this.dragOver(event)
287
+ },
288
+ dragOver(event) {
289
+ if (event.dataTransfer && event.dataTransfer.items && event.dataTransfer.items.length > 0) {
290
+ event.dataTransfer.dropEffect = "none"
291
+
292
+ let item = event.dataTransfer.items[0]
293
+
294
+ if (item.kind === "file") {
295
+ event.preventDefault()
296
+ event.dataTransfer.dropEffect = "copy"
297
+ this.allowDrop = true
298
+ // this._handle_dragover(event)
299
+ }
300
+ }
301
+ },
302
+ dragLeave() {
303
+ this.allowDrop = false
304
+ },
305
+ drop(event) {
306
+ this.allowDrop = false
307
+ if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
308
+ event.preventDefault()
309
+ this.checkAndUploadFile(event.dataTransfer.files[0])
310
+ }
311
+ },
312
+ onSave() {
313
+ const data = {
314
+ image: {
315
+ alt: this.editableAlternativeText,
316
+ tooltip: this.editableTooltip
317
+ },
318
+ figcaption: {
319
+ position: this.editableFigcaptionPosition,
320
+ textAlign: this.editableFigcaptionTextAlign,
321
+ text: this.editableFigcaptionText,
322
+ show: this.editableShowFigcaption
323
+ }
324
+ }
325
+ return {
326
+ editModeContextData: this.editModeContextData,
327
+ ...data,
328
+ update(props) {
329
+ console.log("CmdImage.update", props)
330
+ if (!props.image) {
331
+ props.image = {}
332
+ }
333
+ props.image.alt = data.image.alt
334
+ props.image.tooltip = data.image.tooltip
335
+ if (!props.figcaption) {
336
+ props.figcaption = {}
337
+ }
338
+ props.figcaption.position = data.figcaption.position
339
+ props.figcaption.textAlign = data.figcaption.textAlign
340
+ props.figcaption.text = data.figcaption.text
341
+ props.figcaption.show = data.figcaption.show
342
+ }
343
+ }
344
+ },
345
+ checkAndUploadFile(file) {
346
+ const errorMessages = []
347
+
348
+ // check size for current file
349
+ if (file.size > this.maxFileUploadSize) {
350
+ errorMessages.push("file too large")
351
+ }
352
+
353
+ // check if current file has allowed file-type
354
+ if (!this.allowedFileExtensions.includes(getFileExtension(file.name))) {
355
+ errorMessages.push("disallowed file extension")
356
+ }
357
+
358
+ if (errorMessages.length) {
359
+ alert(errorMessages)
360
+ return
361
+ }
362
+
363
+ // check for min dimensions
364
+ const image = new Image()
365
+
366
+ image.onload = () => {
367
+ if (image.width < this.minImageWidth) {
368
+ // errorMessages.push("width (" + image.width + " px) too small - at least " + this.minImageWidth + " px required!")
369
+ const confirmUpload = confirm("width (" + image.width + " px) too small - at least " + this.minImageWidth + " px required! Use trotzdem!")
370
+ if (!confirmUpload) {
371
+ alert("Abbruch")
372
+ return
373
+ }
374
+ }
375
+ // revoke URL to clean memory
376
+ URL.revokeObjectURL(image.src)
377
+
378
+ // show preview-image by assigning image.src (containing image date (not its path) to do existing contentImage source
379
+ this.$refs.contentImage.src = image.src
380
+ }
381
+ // create data-url (contains content of a file (not its path))
382
+ image.src = URL.createObjectURL(file)
78
383
  }
79
384
  }
80
385
  }
@@ -83,17 +388,46 @@ export default {
83
388
  <style lang="scss">
84
389
  /* begin cmd-image ------------------------------------------------------------------------------------------ */
85
390
  .cmd-image {
86
- &.text-center {
87
- figcaption {
88
- text-align: center;
89
- }
391
+ &.text-center {
392
+ figcaption {
393
+ text-align: center;
90
394
  }
395
+ }
91
396
 
92
- &.text-right {
93
- figcaption {
94
- text-align: right;
95
- }
397
+ &.text-right {
398
+ figcaption {
399
+ text-align: right;
400
+ }
401
+ }
402
+
403
+ .drop-area {
404
+ border: 0;
405
+ align-items: center;
406
+ justify-content: center;
407
+ padding: 0;
408
+
409
+ > [class*="icon"] {
410
+ position: absolute;
411
+ top: 50%;
412
+ left: 50%;
413
+ transform: translateX(-50%) translateY(-50%);
414
+ font-size: 10rem;
415
+ color: var(--pure-white);
416
+ text-shadow: var(--text-shadow);
417
+ z-index: 10;
96
418
  }
419
+
420
+ img {
421
+ opacity: .7;
422
+ transition: var(--default-transition);
423
+
424
+ &:hover, :active, :focus {
425
+ opacity: 1;
426
+ transition: var(--default-transition);
427
+ }
428
+ }
429
+ }
97
430
  }
431
+
98
432
  /* end cmd-image ------------------------------------------------------------------------------------------ */
99
433
  </style>