|
| 1 | +import { LINE_TYPES } from '@/constants/lineTypes'; |
| 2 | + |
| 3 | +/** |
| 4 | + * Shared mixin for script display presentation logic. |
| 5 | + * Handles stage directions, act/scene labels, intervals, and viewport tracking. |
| 6 | + * |
| 7 | + * This mixin assumes the component has: |
| 8 | + * - Props: line, lineIndex, previousLine, previousLineIndex, acts, scenes, |
| 9 | + * stageDirectionStyles, stageDirectionStyleOverrides |
| 10 | + * - Refs: lineContainer |
| 11 | + * - Methods: isWholeLineCut, getPreviousLineForIndex (from scriptNavigationMixin) |
| 12 | + */ |
| 13 | +export default { |
| 14 | + data() { |
| 15 | + return { |
| 16 | + observer: null, |
| 17 | + }; |
| 18 | + }, |
| 19 | + computed: { |
| 20 | + needsActSceneLabel() { |
| 21 | + let { previousLine } = this; |
| 22 | + let lineIndex = this.previousLineIndex; |
| 23 | + while (previousLine != null && this.isWholeLineCut(previousLine)) { |
| 24 | + [lineIndex, previousLine] = this.getPreviousLineForIndex(previousLine.page, lineIndex); |
| 25 | + } |
| 26 | + if (previousLine == null) { |
| 27 | + return true; |
| 28 | + } |
| 29 | + return !(previousLine.act_id === this.line.act_id |
| 30 | + && previousLine.scene_id === this.line.scene_id); |
| 31 | + }, |
| 32 | + needsIntervalBanner() { |
| 33 | + let { previousLine, lineIndex } = this; |
| 34 | + while (previousLine != null && this.isWholeLineCut(previousLine)) { |
| 35 | + [lineIndex, previousLine] = this.getPreviousLineForIndex(previousLine.page, lineIndex); |
| 36 | + } |
| 37 | + if (previousLine == null) { |
| 38 | + return false; |
| 39 | + } |
| 40 | + return previousLine.act_id !== this.line.act_id; |
| 41 | + }, |
| 42 | + previousActLabel() { |
| 43 | + return this.acts.find((act) => (act.id === this.previousLine.act_id)).name; |
| 44 | + }, |
| 45 | + actLabel() { |
| 46 | + return this.acts.find((act) => (act.id === this.line.act_id)).name; |
| 47 | + }, |
| 48 | + sceneLabel() { |
| 49 | + return this.scenes.find((scene) => (scene.id === this.line.scene_id)).name; |
| 50 | + }, |
| 51 | + stageDirectionStyle() { |
| 52 | + const sdStyle = this.stageDirectionStyles.find( |
| 53 | + (style) => (style.id === this.line.stage_direction_style_id), |
| 54 | + ); |
| 55 | + const override = this.stageDirectionStyleOverrides |
| 56 | + .find((elem) => elem.settings.id === sdStyle.id); |
| 57 | + if (this.line.line_type === LINE_TYPES.STAGE_DIRECTION) { |
| 58 | + return override ? override.settings : sdStyle; |
| 59 | + } |
| 60 | + return null; |
| 61 | + }, |
| 62 | + stageDirectionStyling() { |
| 63 | + if (this.line.stage_direction_style_id == null || this.stageDirectionStyle == null) { |
| 64 | + return { |
| 65 | + 'background-color': 'darkslateblue', |
| 66 | + 'font-style': 'italic', |
| 67 | + }; |
| 68 | + } |
| 69 | + const style = { |
| 70 | + 'font-weight': this.stageDirectionStyle.bold ? 'bold' : 'normal', |
| 71 | + 'font-style': this.stageDirectionStyle.italic ? 'italic' : 'normal', |
| 72 | + 'text-decoration-line': this.stageDirectionStyle.underline ? 'underline' : 'none', |
| 73 | + color: this.stageDirectionStyle.text_colour, |
| 74 | + }; |
| 75 | + if (this.stageDirectionStyle.enable_background_colour) { |
| 76 | + style['background-color'] = this.stageDirectionStyle.background_colour; |
| 77 | + } |
| 78 | + return style; |
| 79 | + }, |
| 80 | + }, |
| 81 | + mounted() { |
| 82 | + /* eslint-disable no-restricted-syntax */ |
| 83 | + this.observer = new MutationObserver((mutations) => { |
| 84 | + for (const m of mutations) { |
| 85 | + const newValue = m.target.getAttribute(m.attributeName); |
| 86 | + this.$nextTick(() => { |
| 87 | + this.onClassChange(newValue, m.oldValue); |
| 88 | + }); |
| 89 | + } |
| 90 | + }); |
| 91 | + /* eslint-enable no-restricted-syntax */ |
| 92 | + |
| 93 | + this.observer.observe(this.$refs.lineContainer, { |
| 94 | + attributes: true, |
| 95 | + attributeOldValue: true, |
| 96 | + attributeFilter: ['class'], |
| 97 | + }); |
| 98 | + }, |
| 99 | + destroyed() { |
| 100 | + this.observer.disconnect(); |
| 101 | + }, |
| 102 | + methods: { |
| 103 | + onClassChange(classAttrValue, oldClassAttrValue) { |
| 104 | + const classList = classAttrValue.split(' '); |
| 105 | + const oldClassList = oldClassAttrValue.split(' '); |
| 106 | + if (classList.includes('last-script-element') && !oldClassList.includes('last-script-element')) { |
| 107 | + this.$emit('last-line-change', this.line.page, this.lineIndex); |
| 108 | + } |
| 109 | + if (classList.includes('first-script-element') && !oldClassList.includes('first-script-element')) { |
| 110 | + let previousLine = null; |
| 111 | + if (this.previousLine != null) { |
| 112 | + previousLine = `page_${this.previousLine.page}_line_${this.previousLineIndex}`; |
| 113 | + } |
| 114 | + this.$emit('first-line-change', this.line.page, this.lineIndex, previousLine); |
| 115 | + } |
| 116 | + }, |
| 117 | + startInterval() { |
| 118 | + this.$emit('start-interval', this.acts.find((act) => (act.id === this.previousLine.act_id)).id); |
| 119 | + }, |
| 120 | + }, |
| 121 | +}; |
0 commit comments