From 23b5cc80ba02936659564dd03b173d3214ce5978 Mon Sep 17 00:00:00 2001
From: Terry Truong <terry06890@gmail.com>
Date: Tue, 13 Sep 2022 19:59:06 +1000
Subject: Use Vue Composition API and ESLint

---
 src/components/SettingsModal.vue | 231 +++++++++++++++++++--------------------
 1 file changed, 113 insertions(+), 118 deletions(-)

(limited to 'src/components/SettingsModal.vue')

diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue
index 4f5f05e..df8444f 100644
--- a/src/components/SettingsModal.vue
+++ b/src/components/SettingsModal.vue
@@ -1,8 +1,8 @@
 <template>
-<div class="fixed left-0 top-0 w-full h-full bg-black/40" @click="onClose">
+<div class="fixed left-0 top-0 w-full h-full bg-black/40" @click="onClose" ref="rootRef">
 	<div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2
 		min-w-[8cm] max-w-[80%] max-h-[80%] overflow-auto" :style="styles">
-		<close-icon @click.stop="onClose" ref="closeIcon"
+		<close-icon @click.stop="onClose" ref="closeRef"
 			class="absolute top-1 right-1 md:top-2 md:right-2 w-8 h-8 hover:cursor-pointer" />
 		<h1 class="text-xl md:text-2xl font-bold text-center py-2" :class="borderBClasses">Settings</h1>
 		<div class="pb-2" :class="borderBClasses">
@@ -56,7 +56,7 @@
 				<input type="range"
 					min="15" :max="uiOpts.breakpoint == 'sm' ? 150 : 200" v-model.number="lytOpts.minTileSz"
 					@input="onSettingChgThrottled('LYT', 'minTileSz')" @change="onSettingChg('LYT', 'minTileSz')"
-					name="minTileSizeInput" ref="minTileSzInput"/>
+					name="minTileSizeInput" ref="minTileSzRef"/>
 				<div class="my-auto text-right">{{lytOpts.minTileSz}} px</div>
 				<!-- Row 2 -->
 				<label for="maxTileSizeInput" @click="onReset('LYT', 'maxTileSz')" :class="rLabelClasses">
@@ -64,7 +64,7 @@
 				</label>
 				<input type="range" min="15" max="400" v-model.number="lytOpts.maxTileSz"
 					@input="onSettingChgThrottled('LYT', 'maxTileSz')" @change="onSettingChg('LYT', 'maxTileSz')"
-					name="maxTileSizeInput" ref="maxTileSzInput"/>
+					name="maxTileSizeInput" ref="maxTileSzRef"/>
 				<div class="my-auto text-right">{{lytOpts.maxTileSz}} px</div>
 				<!-- Row 3 -->
 				<label for="tileSpacingInput" @click="onReset('LYT', 'tileSpacing')" :class="rLabelClasses">
@@ -107,129 +107,124 @@
 			Reset
 		</s-button>
 		<transition name="fade">
-			<div v-if="saved" class="absolute right-1 bottom-1" ref="saveIndicator"> Saved </div>
+			<div v-if="saved" class="absolute right-1 bottom-1" ref="saveIndRef"> Saved </div>
 		</transition>
 	</div>
 </div>
 </template>
 
-<script lang="ts">
-import {defineComponent, PropType} from 'vue';
+<script setup lang="ts">
+import {ref, computed, watch, PropType} from 'vue';
 import SButton from './SButton.vue';
 import CloseIcon from './icon/CloseIcon.vue';
 import {UiOptions, OptionType, getDefaultLytOpts, getDefaultUiOpts} from '../lib';
 import {LayoutOptions} from '../layout';
 
-export default defineComponent({
-	props: {
-		lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
-		uiOpts: {type: Object as PropType<UiOptions>, required: true},
-	},
-	data(){
-		return {
-			sweepLeaves: this.lytOpts.layoutType == 'sweep',
-				// For making only two of 'layoutType's values available for user selection
-			saved: false, // Set to true after a setting is saved
-			settingChgTimeout: 0, // Use to throttle some setting-change handling
-		};
-	},
-	computed: {
-		styles(): Record<string,string> {
-			return {
-				backgroundColor: this.uiOpts.bgColorAlt,
-				borderRadius: this.uiOpts.borderRadius + 'px',
-				boxShadow: this.uiOpts.shadowNormal,
-			};
-		},
-		borderBClasses(): string {
-			return 'border-b border-stone-400';
-		},
-		rLabelClasses(): string { // For reset-upon-click labels
-			return "w-fit hover:cursor-pointer hover:text-lime-600";
-		},
-	},
-	methods: {
-		onClose(evt: Event){
-			if (evt.target == this.$el || (this.$refs.closeIcon as typeof CloseIcon).$el.contains(evt.target)){
-				this.$emit('close');
-			}
-		},
-		onSettingChg(optionType: OptionType, option: string){
-			// Maintain min/max-tile-size consistency
-			if (optionType == 'LYT' && (option == 'minTileSz' || option == 'maxTileSz')){
-				let minInput = this.$refs.minTileSzInput as HTMLInputElement;
-				let maxInput = this.$refs.maxTileSzInput as HTMLInputElement;
-				if (option == 'minTileSz' && Number(minInput.value) > Number(maxInput.value)){
-					this.lytOpts.maxTileSz = this.lytOpts.minTileSz;
-					this.$emit('setting-chg', 'LYT', 'maxTileSz');
-				} else if (option == 'maxTileSz' && Number(maxInput.value) < Number(minInput.value)){
-					this.lytOpts.minTileSz = this.lytOpts.maxTileSz;
-					this.$emit('setting-chg', 'LYT', 'minTileSz');
-				}
-			}
-			// Notify parent component
-			this.$emit('setting-chg', optionType, option,
-				{relayout: optionType == 'LYT', reinit: optionType == 'UI' && option == 'tree'});
-			// Possibly make saved-indicator appear/animate
-			if (!this.saved){
-				this.saved = true;
-			} else {
-				let el = this.$refs.saveIndicator as HTMLDivElement;
-				el.classList.remove('animate-flash-green');
-				el.offsetWidth; // Triggers reflow
-				el.classList.add('animate-flash-green');
-			}
-		},
-		onSettingChgThrottled(optionType: OptionType, option: string){
-			if (this.settingChgTimeout == 0){
-				this.settingChgTimeout = setTimeout(() => {
-					this.settingChgTimeout = 0;
-					this.onSettingChg(optionType, option);
-				}, this.uiOpts.animationDelay);
-			}
-		},
-		onReset(optionType: OptionType, option: string){
-			// Restore the setting's default
-			let defaultLytOpts = getDefaultLytOpts();
-			let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
-			if (optionType == 'LYT'){
-				let lytOpt = option as keyof LayoutOptions;
-				if (this.lytOpts[lytOpt] == defaultLytOpts[lytOpt]){
-					return;
-				}
-				(this.lytOpts[lytOpt] as any) = defaultLytOpts[lytOpt];
-				if (option == 'layoutType'){
-					this.sweepLeaves = this.lytOpts.layoutType == 'sweep';
-				}
-			} else {
-				let uiOpt = option as keyof UiOptions;
-				if (this.uiOpts[uiOpt] == defaultUiOpts[uiOpt]){
-					return;
-				}
-				(this.uiOpts[uiOpt] as any) = defaultUiOpts[uiOpt];
-			}
-			// Notify parent component
-			this.onSettingChg(optionType, option);
-		},
-		onResetAll(){
-			// Restore default options
-			let defaultLytOpts = getDefaultLytOpts();
-			let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
-			let needReinit = this.uiOpts.tree != defaultUiOpts.tree;
-			Object.assign(this.lytOpts, defaultLytOpts);
-			Object.assign(this.uiOpts, defaultUiOpts);
-			// Notify parent component
-			this.$emit('reset', needReinit);
-			// Clear saved-indicator
-			this.saved = false;
-		},
-	},
-	watch: {
-		sweepLeaves(newVal: boolean, oldVal: boolean){
-			this.lytOpts.layoutType = newVal ? 'sweep' : 'rect';
-		},
-	},
-	components: {SButton, CloseIcon, },
-	emits: ['close', 'setting-chg', 'reset', ],
+// Refs
+const rootRef = ref(null as HTMLDivElement | null);
+const closeRef = ref(null as typeof CloseIcon | null);
+const minTileSzRef = ref(null as HTMLInputElement | null);
+const maxTileSzRef = ref(null as HTMLInputElement | null);
+const saveIndRef = ref(null as HTMLDivElement | null);
+
+// Props + events
+const props = defineProps({
+	lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
+	uiOpts: {type: Object as PropType<UiOptions>, required: true},
 });
+const emit = defineEmits(['close', 'setting-chg', 'reset', ]);
+
+// For settings
+const sweepLeaves = ref(props.lytOpts.layoutType == 'sweep');
+	// For making only two of 'layoutType's values available for user selection)
+watch(sweepLeaves, (newVal) => {props.lytOpts.layoutType = newVal ? 'sweep' : 'rect'})
+
+// Settings change handling
+const saved = ref(false); // Set to true after a setting is saved
+const settingChgTimeout = ref(0); // Used to throttle some setting-change handling
+function onSettingChg(optionType: OptionType, option: string){
+	// Maintain min/max-tile-size consistency
+	if (optionType == 'LYT' && (option == 'minTileSz' || option == 'maxTileSz')){
+		let minInput = minTileSzRef.value!;
+		let maxInput = maxTileSzRef.value!;
+		if (option == 'minTileSz' && Number(minInput.value) > Number(maxInput.value)){
+			props.lytOpts.maxTileSz = props.lytOpts.minTileSz;
+			emit('setting-chg', 'LYT', 'maxTileSz');
+		} else if (option == 'maxTileSz' && Number(maxInput.value) < Number(minInput.value)){
+			props.lytOpts.minTileSz = props.lytOpts.maxTileSz;
+			emit('setting-chg', 'LYT', 'minTileSz');
+		}
+	}
+	// Notify parent component
+	emit('setting-chg', optionType, option,
+		{relayout: optionType == 'LYT', reinit: optionType == 'UI' && option == 'tree'});
+	// Possibly make saved-indicator appear/animate
+	if (!saved.value){
+		saved.value = true;
+	} else {
+		let el = saveIndRef.value!;
+		el.classList.remove('animate-flash-green');
+		el.offsetWidth; // Triggers reflow
+		el.classList.add('animate-flash-green');
+	}
+}
+function onSettingChgThrottled(optionType: OptionType, option: string){
+	if (settingChgTimeout.value == 0){
+		settingChgTimeout.value = setTimeout(() => {
+			settingChgTimeout.value = 0;
+			onSettingChg(optionType, option);
+		}, props.uiOpts.animationDelay);
+	}
+}
+function onReset(optionType: OptionType, option: string){
+	// Restore the setting's default
+	let defaultLytOpts = getDefaultLytOpts();
+	let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
+	if (optionType == 'LYT'){
+		let lytOpt = option as keyof LayoutOptions;
+		if (props.lytOpts[lytOpt] == defaultLytOpts[lytOpt]){
+			return;
+		}
+		(props.lytOpts[lytOpt] as any) = defaultLytOpts[lytOpt];
+		if (option == 'layoutType'){
+			sweepLeaves.value = props.lytOpts.layoutType == 'sweep';
+		}
+	} else {
+		let uiOpt = option as keyof UiOptions;
+		if (props.uiOpts[uiOpt] == defaultUiOpts[uiOpt]){
+			return;
+		}
+		(props.uiOpts[uiOpt] as any) = defaultUiOpts[uiOpt];
+	}
+	// Notify parent component
+	onSettingChg(optionType, option);
+}
+function onResetAll(){
+	// Restore default options
+	let defaultLytOpts = getDefaultLytOpts();
+	let defaultUiOpts = getDefaultUiOpts(defaultLytOpts);
+	let needReinit = props.uiOpts.tree != defaultUiOpts.tree;
+	Object.assign(props.lytOpts, defaultLytOpts);
+	Object.assign(props.uiOpts, defaultUiOpts);
+	// Notify parent component
+	emit('reset', needReinit);
+	// Clear saved-indicator
+	saved.value = false;
+}
+
+// Close handling
+function onClose(evt: Event){
+	if (evt.target == rootRef.value || closeRef.value!.$el.contains(evt.target)){
+		emit('close');
+	}
+}
+
+// Styles and classes
+const styles = computed(() => ({
+	backgroundColor: props.uiOpts.bgColorAlt,
+	borderRadius: props.uiOpts.borderRadius + 'px',
+	boxShadow: props.uiOpts.shadowNormal,
+}));
+const borderBClasses = 'border-b border-stone-400';
+const rLabelClasses = "w-fit hover:cursor-pointer hover:text-lime-600"; // For reset-upon-click labels
 </script>
-- 
cgit v1.2.3

