Initial commit
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Parcel cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Node packages and builds
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
yarn.lock
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.user
|
||||||
|
|
||||||
|
# Editor files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
.vscode
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Temporary and system files
|
||||||
|
*~
|
||||||
|
.fuse_hidden*
|
||||||
|
.directory
|
||||||
|
.nfs*
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Icon
|
||||||
|
._*
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
*.stackdump
|
||||||
|
[Dd]esktop.ini
|
||||||
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Example Vue Application
|
||||||
|
|
||||||
|
This is an example single page application built in Vue.js.
|
||||||
|
|
||||||
|
It uses nested components, routing, composables and the fancy language
|
||||||
|
extensions pug, typescript and sass, that compile to html, javascript and css.
|
||||||
|
|
||||||
|
To make developing and building the project a breeze, the zero configuration
|
||||||
|
build tool parcel is used.
|
||||||
14
package.json
Normal file
14
package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "vue-example-app",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"source": "src/index.pug",
|
||||||
|
"devDependencies": {
|
||||||
|
"@parcel/transformer-pug": "2.8.3",
|
||||||
|
"@parcel/transformer-sass": "2.8.3",
|
||||||
|
"@parcel/transformer-vue": "2.8.3",
|
||||||
|
"parcel": "^2.8.3",
|
||||||
|
"vue": "^3.2.47",
|
||||||
|
"vue-router": "^4.1.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/components/App.vue
Normal file
29
src/components/App.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
Header Vue Example App
|
||||||
|
Navigation
|
||||||
|
Content.content
|
||||||
|
Footer © Dietrich
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Header from './Header.vue'
|
||||||
|
import Navigation from './Navigation.vue'
|
||||||
|
import Content from './Content.vue'
|
||||||
|
import Footer from './Footer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Header, Navigation, Content, Footer },
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass">
|
||||||
|
body > #app
|
||||||
|
height: 100vh
|
||||||
|
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
align-items: stretch
|
||||||
|
|
||||||
|
> .content
|
||||||
|
flex-grow: 1
|
||||||
|
</style>
|
||||||
33
src/components/Content.vue
Normal file
33
src/components/Content.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
main
|
||||||
|
RouterView(v-slot="{ Component }")
|
||||||
|
Transition
|
||||||
|
KeepAlive
|
||||||
|
Component.page(:is="Component")
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
main
|
||||||
|
position: relative
|
||||||
|
overflow: hidden
|
||||||
|
font-size: 1.5em
|
||||||
|
background: #16a085
|
||||||
|
|
||||||
|
> .page
|
||||||
|
position: absolute
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
overflow-y: auto
|
||||||
|
padding: 1em 4em
|
||||||
|
|
||||||
|
background: inherit
|
||||||
|
|
||||||
|
> .page.v-leave-active, > .page.v-enter-active
|
||||||
|
transition: transform 0.3s ease
|
||||||
|
|
||||||
|
> .page.v-enter-from
|
||||||
|
transform: translateY(100%)
|
||||||
|
</style>
|
||||||
15
src/components/Footer.vue
Normal file
15
src/components/Footer.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
footer
|
||||||
|
span
|
||||||
|
slot
|
||||||
|
RouterLink(to="/imprint") Imprint
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
footer
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: space-around
|
||||||
|
height: 3em
|
||||||
|
background: #2c3e50
|
||||||
|
</style>
|
||||||
26
src/components/Header.vue
Normal file
26
src/components/Header.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
header
|
||||||
|
RouterLink(to="/")
|
||||||
|
img(src="../img/logo.svg")
|
||||||
|
h1
|
||||||
|
slot
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
header
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
background: #2c3e50
|
||||||
|
|
||||||
|
a
|
||||||
|
position: absolute
|
||||||
|
display: block
|
||||||
|
left: 1em
|
||||||
|
width: 3em
|
||||||
|
height: 3em
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
</style>
|
||||||
32
src/components/Navigation.vue
Normal file
32
src/components/Navigation.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
nav
|
||||||
|
template(v-for="route in $router.options.routes")
|
||||||
|
RouterLink(:to="route.path" v-if="route.meta.nav") {{ route.meta.nav }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: ['items'],
|
||||||
|
emits: ['navigate'],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
nav
|
||||||
|
display: flex
|
||||||
|
align-items: stretch
|
||||||
|
justify-content: center
|
||||||
|
height: 3em
|
||||||
|
|
||||||
|
a
|
||||||
|
cursor: pointer
|
||||||
|
flex-basis: 0
|
||||||
|
flex-grow: 1
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
background: #34495e
|
||||||
|
|
||||||
|
&.router-link-active, &:hover
|
||||||
|
background: #8e44ad
|
||||||
|
</style>
|
||||||
14
src/components/routes/About.vue
Normal file
14
src/components/routes/About.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
h2 About
|
||||||
|
|
||||||
|
p
|
||||||
|
| This is an example single page application built in Vue.js.
|
||||||
|
p
|
||||||
|
| It uses nested components, routing, composables and the fancy language
|
||||||
|
| extensions pug, typescript and sass, that compile to html, javascript and
|
||||||
|
| css.
|
||||||
|
p
|
||||||
|
| To make developing and building the project a breeze, the zero
|
||||||
|
| configuration build tool parcel is used.
|
||||||
|
</template>
|
||||||
40
src/components/routes/Counter.vue
Normal file
40
src/components/routes/Counter.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
#text-container You clicked #[span.counter {{ counterText }}].
|
||||||
|
#button-container
|
||||||
|
button(@click="counter = (counter > 0) ? counter - 1 : 0") -
|
||||||
|
button(@click="counter++") +
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
data: () => ({
|
||||||
|
counter: 0
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
counterText() {
|
||||||
|
switch (this.counter) {
|
||||||
|
case 0 : return "not once"
|
||||||
|
case 1 : return "once"
|
||||||
|
default : return `${this.counter} times`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
section
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
gap: 1em
|
||||||
|
font-size: 1.5em
|
||||||
|
|
||||||
|
span.counter
|
||||||
|
color: #bdc3c7
|
||||||
|
|
||||||
|
button
|
||||||
|
width: 2em
|
||||||
|
height: 2em
|
||||||
|
font-size: inherit
|
||||||
|
</style>
|
||||||
65
src/components/routes/Imprint.vue
Normal file
65
src/components/routes/Imprint.vue
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
h2 Imprint
|
||||||
|
|
||||||
|
p
|
||||||
|
| Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||||
|
| tempor incididunt ut labore et dolore magna aliqua. Tempor commodo
|
||||||
|
| ullamcorper a lacus. Id neque aliquam vestibulum morbi blandit cursus
|
||||||
|
| risus at ultrices. Turpis tincidunt id aliquet risus feugiat in ante.
|
||||||
|
| Morbi blandit cursus risus at ultrices mi. Fringilla phasellus faucibus
|
||||||
|
| scelerisque eleifend donec pretium vulputate. Ac feugiat sed lectus
|
||||||
|
| vestibulum. Placerat duis ultricies lacus sed turpis tincidunt id aliquet
|
||||||
|
| risus. Nunc faucibus a pellentesque sit. Enim lobortis scelerisque
|
||||||
|
| fermentum dui faucibus in ornare quam viverra. Sagittis vitae et leo duis
|
||||||
|
| ut diam. Sed faucibus turpis in eu mi bibendum neque egestas congue.
|
||||||
|
p
|
||||||
|
| Pharetra sit amet aliquam id diam maecenas ultricies mi. Neque convallis
|
||||||
|
| a cras semper auctor neque. Est sit amet facilisis magna etiam tempor
|
||||||
|
| orci eu. Fermentum iaculis eu non diam phasellus vestibulum lorem sed
|
||||||
|
| risus. Pellentesque pulvinar pellentesque habitant morbi tristique
|
||||||
|
| senectus et. Elementum pulvinar etiam non quam lacus. Ac turpis egestas
|
||||||
|
| integer eget aliquet. Volutpat ac tincidunt vitae semper quis. Nisl nunc
|
||||||
|
| mi ipsum faucibus vitae aliquet nec ullamcorper sit. Ultricies integer
|
||||||
|
| quis auctor elit sed vulputate. In est ante in nibh mauris cursus mattis
|
||||||
|
| molestie. Suspendisse in est ante in nibh mauris cursus mattis molestie.
|
||||||
|
| Vel orci porta non pulvinar neque laoreet suspendisse. Massa tincidunt
|
||||||
|
| nunc pulvinar sapien et ligula ullamcorper malesuada proin. Id semper
|
||||||
|
| risus in hendrerit gravida.
|
||||||
|
p
|
||||||
|
| Risus at ultrices mi tempus imperdiet. Nulla pharetra diam sit amet nisl
|
||||||
|
| suscipit. Pretium lectus quam id leo in vitae turpis massa sed. Neque
|
||||||
|
| vitae tempus quam pellentesque nec nam aliquam sem et. Dui id ornare arcu
|
||||||
|
| odio ut sem nulla pharetra. Ligula ullamcorper malesuada proin libero
|
||||||
|
| nunc consequat. At varius vel pharetra vel turpis nunc eget lorem.
|
||||||
|
| Sodales neque sodales ut etiam sit amet nisl purus in. Pharetra diam sit
|
||||||
|
| amet nisl suscipit. Vitae nunc sed velit dignissim sodales ut eu sem.
|
||||||
|
| Blandit massa enim nec dui nunc mattis enim ut tellus. Habitant morbi
|
||||||
|
| tristique senectus et netus et malesuada fames. In eu mi bibendum neque
|
||||||
|
| egestas.
|
||||||
|
p
|
||||||
|
| Morbi blandit cursus risus at. Viverra ipsum nunc aliquet bibendum enim
|
||||||
|
| facilisis gravida. Diam in arcu cursus euismod. Imperdiet proin fermentum
|
||||||
|
| leo vel orci porta non pulvinar. Volutpat blandit aliquam etiam erat
|
||||||
|
| velit scelerisque in dictum non. Scelerisque felis imperdiet proin
|
||||||
|
| fermentum leo vel. Ullamcorper malesuada proin libero nunc consequat
|
||||||
|
| interdum varius sit. Nunc sed augue lacus viverra vitae congue eu
|
||||||
|
| consequat ac. Etiam dignissim diam quis enim lobortis scelerisque
|
||||||
|
| fermentum dui faucibus. Cursus euismod quis viverra nibh cras pulvinar
|
||||||
|
| mattis nunc. Velit scelerisque in dictum non consectetur a. Nunc aliquet
|
||||||
|
| bibendum enim facilisis gravida neque convallis a cras. Aliquet enim
|
||||||
|
| tortor at auctor urna nunc id cursus. Sed euismod nisi porta lorem mollis
|
||||||
|
| aliquam ut porttitor leo. Suspendisse in est ante in nibh. Cursus metus
|
||||||
|
| aliquam eleifend mi in nulla. Sit amet consectetur adipiscing elit duis
|
||||||
|
| tristique sollicitudin nibh sit. Enim eu turpis egestas pretium aenean
|
||||||
|
| pharetra magna ac placerat. Amet nulla facilisi morbi tempus. Nibh sit
|
||||||
|
| amet commodo nulla facilisi nullam vehicula.
|
||||||
|
p
|
||||||
|
| Id aliquet lectus proin nibh nisl. Dui ut ornare lectus sit amet est
|
||||||
|
| placerat. Ultrices dui sapien eget mi proin sed. Enim nulla aliquet
|
||||||
|
| porttitor lacus luctus accumsan tortor posuere ac. Odio ut enim blandit
|
||||||
|
| volutpat maecenas volutpat blandit. Sed nisi lacus sed viverra tellus.
|
||||||
|
| Faucibus a pellentesque sit amet porttitor eget dolor. In arcu cursus
|
||||||
|
| euismod quis. Ultrices mi tempus imperdiet nulla. Curabitur vitae nunc
|
||||||
|
| sed velit dignissim sodales ut eu sem.
|
||||||
|
</template>
|
||||||
11
src/components/routes/Index.vue
Normal file
11
src/components/routes/Index.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
h2 Welcome
|
||||||
|
p Go look around and click some buttons!
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
section
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
</style>
|
||||||
43
src/components/routes/Joke.vue
Normal file
43
src/components/routes/Joke.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
ErrorBox(v-if="joke.error !== null" @click="loadJoke").error
|
||||||
|
| {{ joke.error }}
|
||||||
|
Spinner(v-else-if="joke.setup === null" color="white")
|
||||||
|
.content(v-else @click="loadJoke")
|
||||||
|
| {{ joke.setup }}
|
||||||
|
.punchline {{ joke.punchline || "" }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useJokeAPI } from '/src/composables/jokeAPI.ts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
let { joke, loadJoke } = useJokeAPI()
|
||||||
|
return { joke, loadJoke }
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.loadJoke()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
section
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
|
.error
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
.content
|
||||||
|
cursor: pointer
|
||||||
|
font-family: "Times New Roman"
|
||||||
|
font-size: 1.5em
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.punchline
|
||||||
|
margin-top: 1em
|
||||||
|
font-size: 0.8em
|
||||||
|
font-style: italic
|
||||||
|
</style>
|
||||||
11
src/components/routes/NotFound.vue
Normal file
11
src/components/routes/NotFound.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
section
|
||||||
|
h2 Not Found
|
||||||
|
p Whoopsy daisy! This page doesn't seem to exist :(
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
section
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
</style>
|
||||||
29
src/components/routes/routes.ts
Normal file
29
src/components/routes/routes.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import Index from './Index.vue'
|
||||||
|
import Counter from './Counter.vue'
|
||||||
|
import Joke from './Joke.vue'
|
||||||
|
import About from './About.vue'
|
||||||
|
import Imprint from './Imprint.vue'
|
||||||
|
|
||||||
|
import NotFound from './NotFound.vue'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: "/", component: Index,
|
||||||
|
meta: { nav: false, title: "Welcome to my Vue example" },
|
||||||
|
}, {
|
||||||
|
path: "/counter", component: Counter,
|
||||||
|
meta: { nav: "Counter", title: "Vue counter" },
|
||||||
|
}, {
|
||||||
|
path: "/joke", component: Joke,
|
||||||
|
meta: { nav: "Joke", title: "Vue joke API consumer" },
|
||||||
|
}, {
|
||||||
|
path: "/about", component: About,
|
||||||
|
meta: { nav: "About", title: "About this lorem ipsum" },
|
||||||
|
}, {
|
||||||
|
path: "/imprint", component: Imprint,
|
||||||
|
meta: { nav: false, title: "Vue example imprint" },
|
||||||
|
}, {
|
||||||
|
path: "/:path(.*)", component: NotFound,
|
||||||
|
meta: { nav: false, title: "Vue example page not found" },
|
||||||
|
},
|
||||||
|
]
|
||||||
11
src/components/widgets/ErrorBox.vue
Normal file
11
src/components/widgets/ErrorBox.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
div
|
||||||
|
slot
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
div
|
||||||
|
color: #e74c3c
|
||||||
|
background: rgba(255, 255, 255, 0.75)
|
||||||
|
padding: 1em
|
||||||
|
</style>
|
||||||
38
src/components/widgets/Spinner.vue
Normal file
38
src/components/widgets/Spinner.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.spinner(:style="style")
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
size: { default: "3em" },
|
||||||
|
color: { default: "black" },
|
||||||
|
width: { default: "0.3em" },
|
||||||
|
period: { default: "2s" },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
style() {
|
||||||
|
return {
|
||||||
|
width: this.size,
|
||||||
|
height: this.size,
|
||||||
|
borderColor: `${this.color} transparent`,
|
||||||
|
borderWidth: this.width,
|
||||||
|
animationDuration: this.period,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass" scoped>
|
||||||
|
.spinner
|
||||||
|
border-style: solid
|
||||||
|
border-radius: 50%
|
||||||
|
animation: spin 2s linear infinite
|
||||||
|
|
||||||
|
@keyframes spin
|
||||||
|
0%
|
||||||
|
transform: rotate(0)
|
||||||
|
100%
|
||||||
|
transform: rotate(360deg)
|
||||||
|
</style>
|
||||||
63
src/composables/jokeAPI.ts
Normal file
63
src/composables/jokeAPI.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
const DEFAULT_BASE = 'https://official-joke-api.appspot.com'
|
||||||
|
const DEFAULT_URL = `${DEFAULT_BASE}/jokes/programming/random`
|
||||||
|
const DEFAULT_CHAR_DELAY = 20
|
||||||
|
const DEFAULT_PUNCHLINE_DELAY = 2000
|
||||||
|
|
||||||
|
export class Joke {
|
||||||
|
error : string | null = null
|
||||||
|
setup : string | null = null
|
||||||
|
punchline : string = ""
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.error = null
|
||||||
|
this.setup = null
|
||||||
|
this.punchline = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useJokeAPI(
|
||||||
|
url: string = DEFAULT_URL,
|
||||||
|
charDelay: int = DEFAULT_CHAR_DELAY,
|
||||||
|
punchlineDelay: int = DEFAULT_PUNCHLINE_DELAY
|
||||||
|
) {
|
||||||
|
let joke = reactive(new Joke())
|
||||||
|
|
||||||
|
let working = false
|
||||||
|
async function loadJoke(): bool {
|
||||||
|
if (working) return false
|
||||||
|
working = true
|
||||||
|
|
||||||
|
// Load joke
|
||||||
|
joke.reset()
|
||||||
|
try {
|
||||||
|
let res = await fetch(url)
|
||||||
|
var [{ setup, punchline }] = await res.json()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animate joke appearance
|
||||||
|
if (setup && punchline) {
|
||||||
|
for (let i = 0; i <= setup.length; i++) {
|
||||||
|
joke.setup = setup.substr(0, i)
|
||||||
|
await sleep(charDelay)
|
||||||
|
}
|
||||||
|
await sleep(punchlineDelay)
|
||||||
|
for (let i = 0; i <= punchline.length; i++) {
|
||||||
|
joke.punchline = punchline.substr(0, i)
|
||||||
|
await sleep(charDelay)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
joke.error = "Loading joke failed"
|
||||||
|
}
|
||||||
|
working = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return { joke, loadJoke }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sleep(ms: number) {
|
||||||
|
await new Promise((res, rej) => setTimeout(res, ms))
|
||||||
|
}
|
||||||
2
src/img/logo.svg
Normal file
2
src/img/logo.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="128" height="128" enable-background="new" version="1.1" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><path d="m63.5-1.35e-7c-0.612 0.00401-1.22 0.0247-1.82 0.0593-9.95 0.586-19.2 4.75-26.3 11.8-8.09 8.09-12.3 19.1-11.8 30.5 0.115 2.4 0.264 3.64 0.69 5.85 0.678 3.48 1.69 6.49 3.27 9.76 1.48 3.08 2.87 5.27 4.98 7.89 2.87 3.56 4.16 6.24 6.16 12.8 0.23 0.758 0.621 1.8 0.862 2.32 1.63 3.5 4.56 6.08 8.04 7.08 1.96 0.563 2.41 0.598 7.99 0.598h5.29l0.919-0.322c1.15-0.402 1.91-0.838 2.98-1.69 2.92-2.31 8.13-9.92 8.62-16.8 0.492-6.86-1.18-18.2-1.21-24 0-6.24 0.411-13.9 0.985-18.4h10.3v-7.3h-28.7c-4.43 0-5.99 0.574-8.37 2.87-2.3 2.3-3.61 4.68-5.01 9.6h1.64c2.71-4.27 4.1-5.17 8.04-5.17h3.77l-2.13 18.2c-0.328 2.3-1.39 4.1-4.18 6.65-2.54 2.3-3.28 3.61-3.28 5.42 0 2.46 1.97 4.51 4.51 4.51 2.54 0 4.51-1.89 5.58-5.42 0.739-2.13 1.89-10.5 2.63-18.8l0.985-10.6h8.94c-0.672 8.24-2.08 16.9-1.28 25.5 0.807 8.64 1.85 18.4-1.71 22.6-3.07 3.67-4.41 5.03-5.37 5.49l-0.747 0.356-3.91-0.0101c-4.34 0-4.87-0.0686-6.19-0.758-1.56-0.827-2.57-2.33-3.3-4.92-1.18-4.26-3.03-8.47-5.09-11.6-0.402-0.598-1.26-1.77-1.93-2.6-4.07-5.07-6.33-10.2-7.29-16.5-0.253-1.63-0.334-5.68-0.173-7.48 0.609-6.48 3.09-12.7 7.14-17.7 1.18-1.49 3.58-3.9 5.12-5.12 4.67-3.73 10-6.06 16.1-7.02 1.88-0.299 7.3-0.299 9.19 0 5.44 0.85 10.4 2.85 14.6 5.8 6.53 4.69 11.1 11.4 13 19.1 1.16 4.7 1.3 8.97 0.472 13.9-1.01 6-3.54 11.8-7 16.1-1.62 2.01-1.98 2.48-2.76 3.69-3.06 4.69-5.07 10.5-5.87 17.1-0.0689 0.563-0.195 2.28-0.275 3.79-0.172 3.19-0.207 3.3-1.38 4.52-1.87 1.95-5.52 3.52-10.7 4.55-4.65 0.942-9.1 1.38-15.9 1.54l-3.45 0.0916-0.724 0.391c-1.23 0.655-1.77 1.53-1.84 3.01-0.0459 0.873-0.0104 1.06 0.23 1.61 0.379 0.816 0.862 1.36 1.61 1.76 0.575 0.322 0.701 0.345 2.05 0.368 12.4 0.253 14.2 0.321 17.8 0.666 5.01 0.483 8.33 1.42 9.69 2.75 0.701 0.678 0.782 1.01 0.356 1.46-0.724 0.747-2.62 1.32-5.68 1.71-3.07 0.391-5.32 0.471-14.4 0.54-8.28 0.0578-8.88 0.08-9.43 0.276-0.816 0.31-1.46 0.908-1.87 1.74-0.31 0.609-0.356 0.827-0.356 1.64 0 0.827 0.0462 1.03 0.356 1.66 0.241 0.494 0.54 0.862 0.931 1.16 0.931 0.747 1.3 0.804 4.96 0.804 3.73 0 9.23 0.172 11.4 0.345 5.32 0.437 9 1.45 11.7 3.21 0.931 0.609 2.44 2.1 3.26 3.23 0.885 1.2 0.988 1.29 1.85 1.68 1.31 0.598 2.87 0.356 4-0.632 0.873-0.77 1.3-2.31 1-3.57-0.333-1.4-2.65-4.21-5.01-6.04l-0.609-0.471 0.425-0.195c0.747-0.368 2-1.22 2.63-1.83 1.28-1.21 2.11-2.7 2.47-4.41 0.218-1.07 0.218-1.18 0.0459-2.36-0.471-3.3-2.59-5.91-6.07-7.49l-0.839-0.38 0.931-0.471c2.1-1.06 4.34-2.86 5.58-4.5 0.689-0.908 1.61-2.77 1.87-3.79 0.115-0.437 0.23-1.62 0.299-2.87 0.299-5.99 0.965-9.57 2.46-13.4 1.13-2.88 2.54-5.31 4.42-7.57 3.93-4.72 6.64-10.1 8.26-16.5 2.95-11.6 1.07-23.3-5.29-33-5.04-7.66-12.8-13.5-21.4-16.3-4.01-1.29-8.6-1.96-12.9-1.92z" fill="#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
10
src/index.pug
Normal file
10
src/index.pug
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
doctype html
|
||||||
|
|
||||||
|
html(lang="en")
|
||||||
|
head
|
||||||
|
meta(charset="utf-8")
|
||||||
|
title Hello Seb
|
||||||
|
link(rel="stylesheet" href="index.sass")
|
||||||
|
body
|
||||||
|
#app
|
||||||
|
script(src="index.ts" type="module")
|
||||||
16
src/index.sass
Normal file
16
src/index.sass
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
html, body
|
||||||
|
margin: 0
|
||||||
|
min-height: 100%
|
||||||
|
font-family: sans-serif
|
||||||
|
color: white
|
||||||
|
|
||||||
|
*
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5
|
||||||
|
font-weight: normal
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
a
|
||||||
|
text-decoration: none
|
||||||
|
color: inherit
|
||||||
18
src/index.ts
Normal file
18
src/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
import App from './components/App.vue'
|
||||||
|
|
||||||
|
import routes from './components/routes/routes.ts'
|
||||||
|
const history = createWebHistory()
|
||||||
|
const router = createRouter({ history, routes })
|
||||||
|
router.afterEach((to, from) => { document.title = to.meta.title })
|
||||||
|
|
||||||
|
import ErrorBox from './components/widgets/ErrorBox.vue'
|
||||||
|
import Spinner from './components/widgets/Spinner.vue'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
app.component('ErrorBox', ErrorBox)
|
||||||
|
app.component('Spinner', Spinner)
|
||||||
|
app.mount("#app")
|
||||||
Reference in New Issue
Block a user