diff --git a/src/components/chart-editor.vue b/src/components/chart-editor.vue index 7cfed828..b748ae75 100644 --- a/src/components/chart-editor.vue +++ b/src/components/chart-editor.vue @@ -296,7 +296,7 @@ export default class ChartEditorV extends Vue { onChartsEdited(): void { this.edited = true; - this.$emit('slide-edit'); + this.$emit('slide-edit', this.chartConfigs.length !== 0); } } diff --git a/src/components/dynamic-editor.vue b/src/components/dynamic-editor.vue index 9d2e6fd2..2c31b8d3 100644 --- a/src/components/dynamic-editor.vue +++ b/src/components/dynamic-editor.vue @@ -95,6 +95,7 @@ import { Options, Prop, Vue } from 'vue-property-decorator'; import { BasePanel, + BaseStartingConfig, ChartPanel, ConfigFileStructure, DefaultConfigs, @@ -146,45 +147,7 @@ export default class DynamicEditorV extends Vue { video: 'video-editor' }; - startingConfig: DefaultConfigs = { - text: { - type: PanelType.Text, - title: '', - content: '' - }, - dynamic: { - type: PanelType.Dynamic, - title: '', - titleTag: '', - content: '', - children: [] - }, - slideshow: { - type: PanelType.Slideshow, - items: [], - userCreated: true - }, - image: { - type: PanelType.Image, - src: '' - }, - chart: { - type: PanelType.Chart, - src: '' - }, - map: { - type: PanelType.Map, - config: '', - title: '', - scrollguard: false - }, - video: { - type: PanelType.Video, - title: '', - videoType: '', - src: '' - } - }; + startingConfig: DefaultConfigs = JSON.parse(JSON.stringify(BaseStartingConfig)); editingStatus = 'text'; editingSlide = -1; diff --git a/src/components/image-editor.vue b/src/components/image-editor.vue index 6d30756a..9e1e27d3 100644 --- a/src/components/image-editor.vue +++ b/src/components/image-editor.vue @@ -293,7 +293,7 @@ export default class ImageEditorV extends Vue { onImagesEdited(): void { this.edited = true; - this.$emit('slide-edit'); + this.$emit('slide-edit', this.imagePreviews.length !== 0); } } diff --git a/src/components/slide-editor.vue b/src/components/slide-editor.vue index 2f49f366..a0a19ad8 100644 --- a/src/components/slide-editor.vue +++ b/src/components/slide-editor.vue @@ -34,7 +34,13 @@ class="editor-input rounded-none cursor-pointer w-4 h-4" v-model="rightOnly" :disabled="rightOnly && determineEditorType(currentSlide.panel[panelIndex]) === 'dynamic'" - @change.stop="$vfm.open(`right-only-${slideIndex}`)" + @change.stop=" + if (currentSlide.panel.length > 1 && panelModified(currentSlide.panel[0])) { + $vfm.open(`right-only-${slideIndex}`); + } else { + toggleRightOnly(); + } + " /> {{ $t('editor.slides.centerSlide') }} @@ -269,7 +285,15 @@ :sourceCounts="sourceCounts" :centerSlide="centerSlide" :dynamicSelected="dynamicSelected" - @slide-edit="$emit('slide-edit')" + @slide-edit="(changedFromDefault: boolean = true) => { + $emit('slide-edit'); + + // changedFromDefault should hold a boolean indicating whether the panel is actually modified + // (different from initial state). Only needed for some multimedia editors; text editors + // write directly to currentSlide constantly, which is handled by panelModified(). + currentSlide.panel[panelIndex].modified = changedFromDefault || undefined; + } + " v-else > @@ -308,6 +332,7 @@ import { Options, Prop, Vue, Watch } from 'vue-property-decorator'; import { BasePanel, + BaseStartingConfig, ChartPanel, ConfigFileStructure, DefaultConfigs, @@ -334,6 +359,7 @@ import SlideshowEditorV from './slideshow-editor.vue'; import LoadingPageV from './helpers/loading-page.vue'; import DynamicEditorV from './dynamic-editor.vue'; import ConfirmationModalV from './helpers/confirmation-modal.vue'; +import { toRaw } from 'vue'; @Options({ components: { @@ -383,13 +409,21 @@ export default class SlideEditorV extends Vue { this.currentSlide ? (this.rightOnly = this.currentSlide.panel.length === 1) : false; } - changePanelType(prevType: string, newType: string): void { - const startingConfig: DefaultConfigs = { - text: { - type: PanelType.Text, - title: '', - content: '' - }, + /** + * Determines whether a given panel has been modified from the default configuration of its type. + * Note that some editors (e.g. text) write directly to currentSlide after each change, + * while other editors (e.g. image) do not. The first type is handled completely in + * panelModified; the second type requires you to set `panel.modified` for the given panel beforehand, + * indicating whether changes have been made from the specific editor sub-component (see + * ``'s `@slide-edit` event handler). + * @param {BasePanel} panel The panel to analyze. + * @returns {boolean} Whether panel has been modified. + */ + panelModified(panel: BasePanel): boolean { + const prevType = this.currentSlide.panel[this.panelIndex].type; + + let startingConfig = { + ...JSON.parse(JSON.stringify(BaseStartingConfig)), dynamic: { type: PanelType.Dynamic, title: @@ -403,30 +437,42 @@ export default class SlideEditorV extends Vue { : '', children: [] }, - slideshow: { - type: PanelType.Slideshow, - items: [], - userCreated: true - }, - image: { - type: PanelType.Image, - src: '' - }, - chart: { - type: PanelType.Chart, - src: '' - }, map: { type: PanelType.Map, - config: '', + config: `${this.configFileStructure.uuid}/ramp-config/${ + this.configFileStructure.uuid + }-map-${this.getNumberOfMaps()}.json`, title: '', scrollguard: false - }, - video: { - type: PanelType.Video, - title: '', - videoType: '', - src: '' + } + }; + + const oldStartingConfig = startingConfig[panel.type as keyof DefaultConfigs]; + + let newConfig = Object.assign({}, toRaw(panel)); + newConfig.customStyles = newConfig.customStyles || undefined; + + return ( + JSON.stringify(oldStartingConfig) !== JSON.stringify(newConfig) || + this.currentSlide.panel[this.panelIndex].modified === true + ); + } + + changePanelType(prevType: string, newType: string): void { + let startingConfig = { + ...JSON.parse(JSON.stringify(BaseStartingConfig)), + dynamic: { + type: PanelType.Dynamic, + title: + this.currentSlide.panel[0] && prevType === 'text' + ? (this.currentSlide.panel[0] as TextPanel).title + : '', + titleTag: '', + content: + this.currentSlide.panel[0] && prevType === 'text' + ? (this.currentSlide.panel[0] as TextPanel).content + : '', + children: [] } }; @@ -628,6 +674,14 @@ export default class SlideEditorV extends Vue { } } } + + getNumberOfMaps(): number { + let n = 0; + this.configFileStructure.rampConfig.forEach((f) => { + n += 1; + }); + return n; + } } diff --git a/src/components/slideshow-editor.vue b/src/components/slideshow-editor.vue index 394a0a63..7216eedc 100644 --- a/src/components/slideshow-editor.vue +++ b/src/components/slideshow-editor.vue @@ -124,6 +124,7 @@ import { Options, Prop, Vue } from 'vue-property-decorator'; import { BasePanel, + BaseStartingConfig, ChartPanel, ConfigFileStructure, DefaultConfigs, @@ -163,43 +164,17 @@ export default class SlideshowEditorV extends Vue { video: 'video-editor' }; - // TODO: we use this and a few other functions (updating source counts, etc.) in multiple places. We should probably look in to putting this somewhere else. startingConfig: DefaultConfigs = { - text: { - type: PanelType.Text, - title: '', - content: '' - }, - dynamic: { - type: PanelType.Dynamic, - title: '', - titleTag: '', - content: '', - children: [] - }, + ...JSON.parse(JSON.stringify(BaseStartingConfig)), slideshow: { type: PanelType.Slideshow, items: [] }, - chart: { - type: PanelType.Chart, - src: '' - }, - image: { - type: PanelType.Image, - src: '' - }, map: { type: PanelType.Map, config: '', title: '', scrollguard: true // default to ON for slideshows. Allows users to use the cursor to switch slides. - }, - video: { - type: PanelType.Video, - title: '', - videoType: '', - src: '' } }; @@ -289,7 +264,7 @@ export default class SlideshowEditorV extends Vue { (this.$refs.slideEditor as ImageEditorV | ChartEditorV).saveChanges(); if (itemConfig.type === PanelType.Map) { - this.$emit('slide-edit'); + this.$emit('slide-edit', false); } } } diff --git a/src/components/video-editor.vue b/src/components/video-editor.vue index 7baf03b8..0e048f0f 100644 --- a/src/components/video-editor.vue +++ b/src/components/video-editor.vue @@ -273,7 +273,10 @@ export default class VideoEditorV extends Vue { onVideoEdited(): void { this.edited = true; - this.$emit('slide-edit'); + this.$emit( + 'slide-edit', + (this.videoPreview?.videoType || this.videoPreview?.title?.length) ? true : false + ); } } diff --git a/src/definitions.ts b/src/definitions.ts index 3adde3d2..d045063e 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -156,6 +156,7 @@ export interface BasePanel { type: string; width?: number; customStyles?: string; + modified?: boolean; } export interface TextPanel extends BasePanel { @@ -282,3 +283,43 @@ export interface DefaultConfigs { video: VideoPanel; image: ImagePanel; } + +export const BaseStartingConfig: DefaultConfigs = { + text: { + type: PanelType.Text, + title: '', + content: '' + }, + dynamic: { + type: PanelType.Dynamic, + title: '', + titleTag: '', + content: '', + children: [] + }, + slideshow: { + type: PanelType.Slideshow, + items: [], + userCreated: true + }, + image: { + type: PanelType.Image, + src: '' + }, + chart: { + type: PanelType.Chart, + src: '' + }, + map: { + type: PanelType.Map, + config: '', + title: '', + scrollguard: false + }, + video: { + type: PanelType.Video, + title: '', + videoType: '', + src: '' + } +};