loading and saving to localstorage and filesystem

This commit is contained in:
Norman Köhring 2023-03-11 17:43:35 +01:00
parent 033688dde4
commit 408b3d156d
8 changed files with 186 additions and 18 deletions

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.png" /> <link rel="icon" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Starsy ̋̄— Star System Generator</title> <title>Starsy — Star System Generator</title>
</head> </head>
<body class="theme-inverse"> <body class="theme-inverse">
<div id="app"></div> <div id="app"></div>

View file

@ -8,13 +8,53 @@
<section id="settings"> <section id="settings">
<ObjectSettings v-if="selectedObject" /> <ObjectSettings v-if="selectedObject" />
<Tips> <AppMenu default-slot="tips">
<li>Edit planets by clicking directly inside the graphic or in the table below.</li> <template #tips>
<li>Drag planets around to change their distance.</li> <ul>
<li>The last removed planet can be restored from the table.</li> <li>Edit planets by clicking directly inside the graphic or in the table below.</li>
<li><strong>ONLY THE LAST</strong> removed planet can be restored.</li> <li>Drag planets around to change their distance.</li>
<li>Click on a planet to get more tips.</li> <li>The last removed planet can be restored from the table.</li>
</Tips> <li><strong>ONLY THE LAST</strong> removed planet can be restored.</li>
<li>Click on a planet to get more tips.</li>
</ul>
</template>
<template #load>
<p><i>(Careful! Loading will overwrite the current state!)</i></p>
<p>
<b>Local Storage</b>
<br />
<ul>
<li :key="name" v-for="{name, star, objects } in storageInfo">
{{ name }} ("{{ star }}", {{ objects }} objects)
<button @click="replaceCurrent(loadPreset(name))">load</button>
<button @click="deletePreset(name)" v-if="name !== 'example'">delete</button>
</li>
</ul>
</p>
<p>
<b>File System</b>
<br />
<input type="file" @change="loadJSONFile($event)" />
</p>
</template>
<template #save>
<p>
<label>
Save current system as:
<input placeholder="fancy star system" v-model="currentName" />
</label>
</p>
<p>
<b>Local Storage </b>
<button @click="savePreset(star, objects)">save as "{{ currentName }}"</button>
</p>
<p>
<b>File System </b>
<a :href="fileBlob" :download="`${currentName}.json`">download as {{ currentName }}.json</a>
</p>
</template>
<template #x></template>
</AppMenu>
<SystemSettings /> <SystemSettings />
<ObjectList /> <ObjectList />
</section> </section>
@ -22,20 +62,52 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue' import { computed } from 'vue'
import exampleData from './example-data'
import Headline from './components/Headline.vue' import Headline from './components/Headline.vue'
import SystemDiagram from './components/SystemDiagram.vue' import SystemDiagram from './components/SystemDiagram.vue'
import AppMenu from './components/AppMenu.vue'
import Tips from './components/Tips.vue' import Tips from './components/Tips.vue'
import SystemSettings from './components/SystemSettings.vue' import SystemSettings from './components/SystemSettings.vue'
import ObjectList from './components/ObjectList.vue' import ObjectList from './components/ObjectList.vue'
import ObjectSettings from './components/ObjectSettings.vue' import ObjectSettings from './components/ObjectSettings.vue'
import useObjects from './useObjects'
const { selectedObject } = useObjects() import useObjects from './useObjects'
import useStorage from './useStorage'
const { star, objects, selectedObject, replaceCurrent } = useObjects()
const labelFonts = ['xolonium', 'douar', 'lack'] const labelFonts = ['xolonium', 'douar', 'lack']
const themes = ['default', 'retro', 'inverse', 'paper'] const themes = ['default', 'retro', 'inverse', 'paper']
const {
storageInfo,
loadPreset,
savePreset,
deletePreset,
currentName,
} = useStorage(star, objects)
const fileBlob = computed(() => {
const jsonFileData = JSON.stringify({ star, objects })
return `data:text/json;charset=utf-8,${encodeURIComponent(jsonFileData)}`
})
function loadJSONFile (event) {
const file = event.target.files[0]
if (!file) return
const reader = new FileReader()
reader.onload = evt => {
try {
const preset = JSON.parse(evt.target.result)
replaceCurrent(preset)
} catch {
alert('Failed to read file. Are you sure, it is a valid Starsy JSON file?')
}
}
reader.readAsText(file)
}
function setTheme (theme) { function setTheme (theme) {
const classes = document.body.className.split(' ') const classes = document.body.className.split(' ')
const currentTheme = classes.find(c => c.startsWith('theme-')) const currentTheme = classes.find(c => c.startsWith('theme-'))

View file

@ -88,15 +88,15 @@ button.small {
background-size: 24px; background-size: 24px;
} }
.tip { .info {
width: calc(100% - 4em); width: calc(100% - 4em);
margin-left: -1em; margin-left: -1em;
padding: 1em 2em; padding: 1em 2em;
border: 2px solid #8888; border: 2px solid #8888;
border-left-width: 1em; border-left-width: 1em;
} }
.tip > header { font-weight: bold; } .info > header { font-weight: bold; }
.tip > li { margin-top: .75em; } .info > li { margin-top: .75em; }
body.theme-retro { body.theme-retro {
--bg-app: #4B4839; --bg-app: #4B4839;
@ -150,6 +150,10 @@ text.tilted { transform: rotate(-45deg) translate(0, 100%); transform-origin: le
h1 { h1 {
font-family: xolonium; font-family: xolonium;
} }
a { color: #AAF; }
.theme-paper a { color: #33A; }
#app > header { #app > header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -354,3 +358,10 @@ h1 {
color: var(--fg-app); color: var(--fg-app);
font-weight: bold; font-weight: bold;
} }
.app-menu-button {
margin: 0 1em 0 0;
padding: .2em 1em;
}
.highlighted {
font-weight: bold;
}

View file

@ -0,0 +1,28 @@
<template>
<div class="info">
<header>
<button v-for="(_, slot) in slots"
:key="slot"
@click="shownSlot = slot"
class="app-menu-button"
:class="{ highlighted: shownSlot === slot }"
>
{{ slot }}
</button>
</header>
<slot :name="shownSlot"></slot>
</div>
</template>
<script setup>
import { ref, useSlots } from 'vue'
const props = defineProps({
defaultSlot: {
type: String,
},
})
const slots = useSlots()
const shownSlot = ref(props.defaultSlot || Object.keys(slots)[0] || 'default')
</script>

View file

@ -1,5 +1,5 @@
<template> <template>
<ul class="tip"> <ul class="info">
<header> <header>
Tips: Tips:
<button @click="tipsShown = !tipsShown" v-if="collapsible"> <button @click="tipsShown = !tipsShown" v-if="collapsible">

View file

@ -10,8 +10,7 @@ import {
MAX_AMOUNT_RINGS, MAX_AMOUNT_RINGS,
} from './constants' } from './constants'
import { steepCurve } from './utils' import { steepCurve } from './utils'
import exampleData from './example-data.js' import exampleData from './example-data/sol'
const star = reactive(exampleData.star) const star = reactive(exampleData.star)
const objects = reactive(exampleData.objects) const objects = reactive(exampleData.objects)
@ -99,6 +98,18 @@ export default function useObjects () {
} }
function randomizeObject (object) { function randomizeObject (object) {
console.log('randomize', object)
}
function replaceCurrent (preset) {
const { star: newStar, objects: newObjects } = preset
Object.keys(star).forEach(key => {
star[key] = newStar[key]
})
objects.length = 0
newObjects.forEach(object => objects.push(object))
} }
return { return {
@ -113,5 +124,6 @@ export default function useObjects () {
restoreDeleted, restoreDeleted,
randomizeObject, randomizeObject,
autoName, autoName,
replaceCurrent,
} }
} }

45
src/useStorage.js Normal file
View file

@ -0,0 +1,45 @@
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'
export default function useStarsyStorage(star, objects) {
const store = useStorage('starsy', {
example: { star, objects }
})
const storageInfo = computed(() => {
return Object.keys(store.value).map(name => {
const { star, objects } = store.value[name]
return {
name,
star: star.designation,
objects: objects.length,
}
})
})
const currentName = ref('example')
function loadPreset(name) {
return store.value[name]
}
function savePreset(star, objects) {
const name = currentName.value
store.value[name] = { star, objects }
console.log('saved preset', name, store.value[name])
}
function deletePreset(name) {
delete store.value[name]
}
return {
store,
storageInfo,
loadPreset,
savePreset,
deletePreset,
currentName,
}
}