chore: init

This commit is contained in:
Anthony Fu 2020-08-10 02:43:04 +08:00
commit 76fb0845ec
24 changed files with 4433 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.DS_Store
dist
*.local

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
// as this is a project template, free feel to it change to your name.
Copyright (c) 2020 Anthony Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
<h1 align='center'><samp>Vitesse</samp></h1>
<p align='center'>
<samp>Opinionated <a href="https://github.com/vitejs/vite">Vite</a> Starter Template</samp>
</p>
<p align='center'>
<sup><em>Mocking up web app with <b>vitesse</b></em></sup>
</p>
<br>
<p align='center'>
<a href="https://vitesse.netilfy.app">Live Example</a>
</p>
<br>
## Pre-packed
### UI Frameworks
- [Tailwind CSS](https://tailwindcss.com/)
- [tailwindcss-dark-mode](https://github.com/ChanceArthur/tailwindcss-dark-mode)
- [Iconify](https://iconify.design) - use icons from your favorite sets. [🔍Icônes](https://icones.netlify.app/)
<!-- - [variantwind](https://github.com/sibbngheid/variantwind) -->
### Router
- [Vue Router](https://github.com/vuejs/vue-router)
- [Voie](https://github.com/vamplate/vite-plugin-voie) - file based routing
### Utils
- [VueUse](https://github.com/antfu/vueuse)
### Misc
- Use Composition API with [`<script setup>` SFC](https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-script-setup.md)
### Dev tools
- [Typescript](https://www.typescriptlang.org/)
- [pnpm](https://pnpm.js.org/)
- [Netlify](https://www.netlify.com/)
- [ESLint](https://eslint.org/) with [@antfu/eslint-config-vue](https://github.com/antfu/eslint-config)
## Use It!
```bash
git clone https://github.com/antfu/vite-starter && cd vite-starter && rm -rf .git && git init && pnpm i
```
## Why
I have created several Vite apps recently. Setting the configs up is kinda the bottleneck for me to make the idea simply comes true in very a short time. So I made this starter template for myself to create apps more easily, along with some good practices that I have learned during making those apps. It's strongly opinionated, but feel free to tweak it or even maintains your own forks.

14
index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vitesse</title>
<script src="https://cdn.jsdelivr.net/npm/@iconify/iconify@1.0.7/dist/iconify.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

12
netlify.toml Executable file
View File

@ -0,0 +1,12 @@
[build.environment]
# to disable npm auto install. see https://community.netlify.com/t/using-pnpm-and-pnpm-workspaces/2759
NPM_FLAGS="--prefix=/"
[build]
publish = "dist"
command = "npx pnpm i && npm run build"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200

34
package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "@vitesse/template",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"@iconify/core": "^1.0.0-beta.3",
"@vueuse/core": "^4.0.0-beta.4",
"variantwind": "^0.3.4",
"vue": "^3.0.0-rc.1",
"vue-router": "^4.0.0-beta.6"
},
"devDependencies": {
"@antfu/eslint-config-vue": "^0.2.14",
"@typescript-eslint/eslint-plugin": "^3.8.0",
"@vue/compiler-sfc": "^3.0.0-rc.5",
"eslint": "^7.6.0",
"pnpm": "^5.4.12",
"tailwindcss": "^1.6.2",
"tailwindcss-dark-mode": "^1.1.6",
"typescript": "^3.9.7",
"vite": "^1.0.0-rc.1",
"vite-plugin-voie": "^0.2.0"
},
"eslintConfig": {
"extends": "@antfu/eslint-config-vue",
"rules": {
"import/no-absolute-path": "off"
}
}
}

3913
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
}

6
public/favicon.svg Normal file
View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"
preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32">
<path
d="M27.562 26L17.17 8.928l2.366-3.888L17.828 4L16 7.005L14.17 4l-1.708 1.04l2.366 3.888L4.438 26H2v2h28v-2zM16 10.85L25.22 26H17v-8h-2v8H6.78z"
fill="#fff" />
</svg>

After

Width:  |  Height:  |  Size: 346 B

6
src/App.vue Normal file
View File

@ -0,0 +1,6 @@
<template>
<div class="px-4 py-10 text-center text-gray-700 dark:text-gray-200">
<router-view />
<Footer />
</div>
</template>

31
src/components/Footer.vue Normal file
View File

@ -0,0 +1,31 @@
<template>
<div
class="text-xl mt-4"
>
<div
class="inline-block mx-2 cursor-pointer select-none"
@click="isDark = !isDark"
>
<Icon
class="inline-block"
:icon="isDark ? 'carbon:moon' : 'carbon:sun'"
/>
</div>
<a
class="mx-2"
href="https://github.com/antfu/vitesse"
target="_blank"
>
<Icon
class="inline-block"
icon="carbon:code"
/>
</a>
</div>
</template>
<script setup lang='ts'>
export { isDark } from '../utils/dark'
</script>

49
src/components/Icon.vue Normal file
View File

@ -0,0 +1,49 @@
<template>
<div ref="el" :class="$attrs.class" style="vertical-align: text-bottom" />
</template>
<script setup="props" lang="ts">
import { watch, ref, onMounted, nextTick } from 'vue'
declare const props: {
icon: string
}
export const el = ref<HTMLElement | null>(null)
const update = async() => {
if (el.value) {
await nextTick()
// @ts-ignore
const data = window.Iconify.getSVGObject(props.icon)
if (data) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
for (const key of Object.keys(data.attributes))
svg.setAttribute(key, data.attributes[key])
svg.innerHTML = data ? data.body : null
el.value.textContent = ''
el.value.appendChild(svg)
}
else {
const span = document.createElement('span')
span.className = 'iconify'
span.dataset.icon = props.icon
el.value.textContent = ''
el.value.appendChild(span)
}
}
}
watch(() => props.icon, update, { flush: 'post' })
onMounted(update)
</script>
<style>
span.iconify {
background: #5551;
border-radius: 100%;
min-width: 10px;
min-height: 1em;
}
</style>

56
src/components/Modal.vue Normal file
View File

@ -0,0 +1,56 @@
<template>
<div
class="fixed top-0 bottom-0 left-0 right-0 z-40"
:class="value ? '': 'pointer-events-none'"
>
<div
class="bg-white bg-opacity-85 bottom-0 left-0 right-0 top-0 absolute transition-opacity duration-500 ease-out"
:class="value ? '': 'opacity-0'"
@click="$emit('update:value', false)"
/>
<div
class="bg-white absolute transition-all duration-200 ease-out border-gray-200"
:class="positionClass"
:style="value ? {}: {transform}"
>
<slot />
</div>
</div>
</template>
<script setup="props" lang="ts">
import { computed } from 'vue'
declare const props: {
value?: boolean
direction?: 'bottom' | 'top' | 'left' | 'right'
}
const defaultDirection = 'bottom'
export const positionClass = computed(() => {
switch (props.direction || defaultDirection) {
case 'bottom':
return 'bottom-0 left-0 right-0 border-t'
case 'top':
return 'top-0 left-0 right-0 border-b'
case 'left':
return 'bottom-0 left-0 top-0 border-r'
case 'right':
return 'bottom-0 top-0 right-0 border-l'
}
})
export const transform = computed(() => {
switch (props.direction || defaultDirection) {
case 'bottom':
return 'translateY(100%)'
case 'top':
return 'translateY(-100%)'
case 'left':
return 'translateX(-100%)'
case 'right':
return 'translateX(100%)'
}
})
</script>

12
src/components/index.ts Normal file
View File

@ -0,0 +1,12 @@
import { Plugin } from 'vue'
import Modal from './Modal.vue'
import Icon from './Icon.vue'
import Footer from './Footer.vue'
export const registerComponents: Plugin = {
install(app) {
app.component('Modal', Modal)
app.component('Icon', Icon)
app.component('Footer', Footer)
},
}

26
src/main.postcss Executable file
View File

@ -0,0 +1,26 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
html, body, #app {
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
}
.schema-dark {
background: #222;
}
.btn {
@apply px-4 py-1 rounded bg-teal-600 inline-block text-white cursor-pointer;
}
.btn:hover {
@apply bg-teal-700;
}
.btn[disabled] {
@apply cursor-default bg-gray-600 opacity-50;
}

18
src/main.ts Normal file
View File

@ -0,0 +1,18 @@
import './main.postcss'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
// @ts-ignore
import routes from '/@voie/pages'
import { registerComponents } from './components'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
history: createWebHistory(),
routes,
})
app.use(router)
app.use(registerComponents)
app.mount('#app')

31
src/pages/hi/[name].vue Normal file
View File

@ -0,0 +1,31 @@
<template>
<div>
<p class="text-4xl">
<Icon class="iconify inline-block" icon="carbon:pedestrian" />
</p>
<p>
Hi, {{ name }}!
</p>
<div>
<button
class="btn m-3 text-sm mt-8"
@click="back"
>
Back
</button>
</div>
</div>
</template>
<script setup='props' lang='ts'>
import { useRouter } from 'vue-router'
declare const props: {
name: string
}
const router = useRouter()
export const back = () => router.push('/')
</script>

43
src/pages/index.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<div>
<p class="text-4xl">
<Icon class="iconify inline-block" icon="carbon:campsite" />
</p>
<p>
Vitesse
</p>
<div class="py-4" />
<input
v-model="name"
placeholder="What's your name?"
class="px-4 py-2 border border-gray-200 rounded text-center outline-none active:outline-none bg-transparent dark:border-gray-700"
@keydown.enter="go"
>
<div>
<button
class="btn m-3 text-sm"
:disabled="!name"
@click="go"
>
GO
</button>
</div>
</div>
</template>
<script setup='props' lang='ts'>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
export const name = ref('')
const router = useRouter()
export const go = () => {
if (name.value)
router.push(`/hi/${name.value}`)
}
</script>

5
src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module '*.vue' {
import { defineComponent } from 'vue'
const Component: ReturnType<typeof defineComponent>
export default Component
}

26
src/utils/dark.ts Normal file
View File

@ -0,0 +1,26 @@
import { watch, computed, Ref } from 'vue'
import { useStorage, usePreferredDark } from '@vueuse/core'
const preferredDark = usePreferredDark()
const schema = useStorage('vitesse-schema', 'auto') as Ref<'auto' | 'dark' | 'light'>
export const isDark = computed({
get() {
return schema.value === 'auto' ? preferredDark.value : schema.value === 'dark'
},
set(v: boolean) {
if (v === preferredDark.value)
schema.value = 'auto'
else
schema.value = v ? 'dark' : 'light'
},
})
watch(
isDark,
(v) => {
const html = document.getElementsByTagName('html')[0]
html.classList.toggle('schema-dark', v)
},
{ immediate: true },
)

3
src/window.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
declare interface Window {
// extend the window
}

32
tailwind.config.js Normal file
View File

@ -0,0 +1,32 @@
/* eslint-disable @typescript-eslint/no-var-requires */
module.exports = {
purge: {
content: [
'./index.html',
'./src/**/*.vue',
'./src/**/*.js',
'./src/**/*.ts',
],
whitelist: [
'schema-dark',
],
},
theme: {
darkSelector: '.schema-dark',
extend: {
opacity: {
10: '0.1',
85: '0.85',
},
},
},
variants: {
backgroundColor: ['dark', 'dark-hover', 'dark-group-hover', 'hover'],
borderColor: ['dark', 'dark-disabled', 'dark-focus', 'dark-active'],
textColor: ['dark', 'dark-hover', 'dark-active', 'hover', 'active'],
},
plugins: [
require('tailwindcss-dark-mode')(),
],
}

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"module": "ESNext",
"target": "es2016",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"incremental": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true
},
"exclude": [
"dist",
"node_modules"
]
}

10
vite.config.js Normal file
View File

@ -0,0 +1,10 @@
import voie from 'vite-plugin-voie'
export default {
plugins: [
voie({
pagesDir: 'src/pages',
extensions: ['vue', 'ts'],
}),
],
}