diff --git a/package-lock.json b/package-lock.json index 4bfa3ae..fbfc55f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.8.1", "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "bps": "^2.0.1", "center-align": "^1.0.1", "crc-32": "^1.2.2", @@ -1554,6 +1555,22 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, "node_modules/bps": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bps/-/bps-2.0.1.tgz", diff --git a/package.json b/package.json index 93aa2d5..992c118 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.8.1", "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "bps": "^2.0.1", "center-align": "^1.0.1", "crc-32": "^1.2.2", diff --git a/src/components/PresetPicker.vue b/src/components/PresetPicker.vue index 7400258..0e849d6 100644 --- a/src/components/PresetPicker.vue +++ b/src/components/PresetPicker.vue @@ -10,14 +10,19 @@ export default defineComponent({ data() { return { selected: "custom", + localPresets: [], }; }, emits: [ - 'selected' + "selected", + "save", ], props: { generator: null, }, + async mounted() { + this.localPresets = await localforage.getItem("local_presets") ?? []; + }, computed: { settings() { const settings = {}; @@ -54,26 +59,60 @@ export default defineComponent({ methods: { change() { if (this.selected && this.selected != "custom") { - this.$emit("selected", this.presets[this.selected]); + if (this.selected.startsWith("local_")) { + this.$emit("selected", this.localPresets[this.selected.substring(6)]); + } else { + this.$emit("selected", this.presets[this.selected]); + } } }, + settingsMatch(newSettings, preset) { + for (const settingName of Object.keys(newSettings)) { + if (preset[settingName] != newSettings[settingName]) { + return false; + } + } + return true + }, settingChanged(newSettings) { for (const presetName of Object.keys(this.presets)) { const preset = this.presets[presetName]; - var matches = true; - for (const settingName of Object.keys(newSettings)) { - if (preset[settingName] != newSettings[settingName]) { - matches = false; - break; - } - } - if (matches) { + if (this.settingsMatch(newSettings, preset)) { this.selected = presetName; return; } } + for (const [idx, preset] of this.localPresets.entries()) { + if (this.settingsMatch(newSettings, preset)) { + this.selected = `local_${idx}`; + return; + } + } this.selected = "custom"; }, + saveClicked() { + this.$emit("save", this.localPresets.map(s => s.display)); + }, + async deleteClicked() { + const idx = this.selected.substring(6); + this.localPresets.splice(idx, 1); + this.selected = "custom"; + await this.updateLocalPresets(); + }, + async savePreset(idx, settings) { + if (idx == null) { + idx = this.localPresets.length; + this.localPresets.push(settings); + } else { + this.localPresets[idx] = settings; + } + this.selected = `local_${idx}`; + await this.updateLocalPresets(); + }, + async updateLocalPresets() { + const copy = JSON.parse(JSON.stringify(this.localPresets)); + await localforage.setItem("local_presets", copy); + }, }, }); @@ -84,12 +123,29 @@ export default defineComponent({ Preset: - Custom - - - {{ presets[name].display }} - - + Custom + + + + {{ presets[name].display }} + + + + + + + {{ preset.display }} + + + + + Save Preset + + + + diff --git a/src/components/Seed.vue b/src/components/Seed.vue index cf4f28a..f9b0b41 100644 --- a/src/components/Seed.vue +++ b/src/components/Seed.vue @@ -89,7 +89,7 @@ export default defineComponent({ } }) .catch(error => { - if (error.response.status == 409) { + if (error.response?.status == 409) { // still generating, try again setTimeout(this.fetchSeed.bind(this), 2000); } else { diff --git a/src/main.js b/src/main.js index 1e5f047..a57624e 100644 --- a/src/main.js +++ b/src/main.js @@ -3,6 +3,8 @@ import App from "./App.vue"; import router from "./router"; import "bootstrap/dist/css/bootstrap.min.css"; +import "bootstrap/dist/js/bootstrap.min.js"; +import "bootstrap-icons/font/bootstrap-icons.css"; import "bootstrap"; import "./assets/main.css"; diff --git a/src/views/GenerateView.vue b/src/views/GenerateView.vue index bfe25c6..77fc21c 100644 --- a/src/views/GenerateView.vue +++ b/src/views/GenerateView.vue @@ -2,6 +2,7 @@ import { defineComponent } from "vue"; import axios from "axios"; +import { Modal } from "bootstrap"; import PresetPicker from "@/components/PresetPicker.vue"; import SettingPicker from "@/components/SettingPicker.vue"; @@ -14,10 +15,15 @@ export default defineComponent({ data() { return { set: {}, + existingLocalPresets: [], + newPresetName: "", + replacePreset: null, + modal: null, }; }, mounted() { - document.title = "ALttPRandomizer"; + document.title = "ALttPRandomizer" + this.modal = new Modal(document.getElementById("savePresetModal"), {}); }, watch: { set: { @@ -52,11 +58,65 @@ export default defineComponent({ } } }, + savePreset(names) { + this.newPresetName = ""; + this.existingLocalPresets = names; + this.modal.show(); + }, + async modalSavePreset() { + const preset = JSON.parse(JSON.stringify(this.set)); + preset.display = this.newPresetName; + if (this.replacePreset != null) { + preset.display = this.existingLocalPresets[this.replacePreset]; + } + await this.$refs.preset.savePreset(this.replacePreset, preset); + this.modal.hide(); + }, }, }); + + + + + Save Preset + + + + + + New + + + + {{ preset }} + + + + + + + + Name + + + + + + + + Generate Seed @@ -64,7 +124,8 @@ export default defineComponent({ - +