Compare commits

...

1 Commits

Author SHA1 Message Date
Thierry
de14528dd4 feat(xo): POC XO Core, XO 6, XO Lite 2023-12-22 15:57:52 +01:00
52 changed files with 2546 additions and 57 deletions

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
@xen-orchestra/web
@xen-orchestra/web-core
@xen-orchestra/web-lite

View File

@@ -0,0 +1,14 @@
import antfu from '@antfu/eslint-config'
export default antfu({
rules: {
'import/order': ['error', { alphabetize: { order: 'asc', orderImportKind: 'asc' } }],
},
overrides: {
vue: {
'vue/component-api-style': 'error',
'vue/no-empty-component-block': 'error',
'vue/block-order': ['error', { order: ['template', 'script', 'style'] }],
},
},
})

View File

@@ -0,0 +1,121 @@
:root {
--color-grey-000: #000000;
--color-grey-100: #1a1b38;
--color-grey-200: #595a6f;
--color-grey-300: #9899a5;
--color-grey-400: #bfbfc6;
--color-grey-500: #e5e5e7;
--color-grey-600: #ffffff;
--color-background-primary: #ffffff;
--color-background-secondary: #f6f6f7;
--color-purple-base: #8f84ff;
--color-purple-d20: color(#8f84ff blend(black 20%));
--color-purple-d40: color(#8f84ff blend(black 40%));
--color-purple-d60: color(#8f84ff blend(black 60%));
--color-purple-l20: color(#8f84ff blend(white 20%));
--color-purple-l40: color(#8f84ff blend(white 40%));
--color-purple-l60: color(#8f84ff blend(white 60%));
--color-background-purple-10: color(white blend(#8f84ff 10%));
--color-background-purple-20: color(white blend(#8f84ff 20%));
--color-background-purple-30: color(white blend(#8f84ff 30%));
--color-background-purple-60: color(white blend(#8f84ff 60%));
--color-green-base: #2ca878;
--color-green-d20: color(#2ca878 blend(black 20%));
--color-green-d40: color(#2ca878 blend(black 40%));
--color-green-d60: color(#2ca878 blend(black 60%));
--color-green-l20: color(#2ca878 blend(white 20%));
--color-green-l40: color(#2ca878 blend(white 40%));
--color-green-l60: color(#2ca878 blend(white 60%));
--color-background-green-10: color(white blend(#2ca878 10%));
--color-background-green-20: color(white blend(#2ca878 20%));
--color-background-green-30: color(white blend(#2ca878 30%));
--color-background-green-60: color(white blend(#2ca878 60%));
--color-orange-base: #ef7f18;
--color-orange-d20: color(#ef7f18 blend(black 20%));
--color-orange-d40: color(#ef7f18 blend(black 40%));
--color-orange-d60: color(#ef7f18 blend(black 60%));
--color-orange-l20: color(#ef7f18 blend(white 20%));
--color-orange-l40: color(#ef7f18 blend(white 40%));
--color-orange-l60: color(#ef7f18 blend(white 60%));
--color-background-orange-10: color(white blend(#ef7f18 10%));
--color-background-orange-20: color(white blend(#ef7f18 20%));
--color-background-orange-30: color(white blend(#ef7f18 30%));
--color-background-orange-60: color(white blend(#ef7f18 60%));
--color-red-base: #be1621;
--color-red-d20: color(#be1621 blend(black 20%));
--color-red-d40: color(#be1621 blend(black 40%));
--color-red-d60: color(#be1621 blend(black 60%));
--color-red-l20: color(#be1621 blend(white 20%));
--color-red-l40: color(#be1621 blend(white 40%));
--color-red-l60: color(#be1621 blend(white 60%));
--color-background-red-10: color(white blend(#be1621 10%));
--color-background-red-20: color(white blend(#be1621 20%));
--color-background-red-30: color(white blend(#be1621 30%));
--color-background-red-60: color(white blend(#be1621 60%));
}
:root.dark {
--color-grey-000: #ffffff;
--color-grey-100: #e5e5e7;
--color-grey-400: #595a6f;
--color-grey-200: #bfbfc6;
--color-grey-300: #9899a5;
--color-grey-500: #1a1b38;
--color-grey-600: #000000;
--color-background-primary: #14141e;
--color-background-secondary: #17182b;
--color-purple-base: #8f84ff;
--color-purple-d20: color(#8f84ff blend(white 20%));
--color-purple-d40: color(#8f84ff blend(white 40%));
--color-purple-d60: color(#8f84ff blend(white 60%));
--color-purple-l20: color(#8f84ff blend(black 20%));
--color-purple-l40: color(#8f84ff blend(black 40%));
--color-purple-l60: color(#8f84ff blend(black 60%));
--color-background-purple-10: color(#17182b blend(#8f84ff 25%));
--color-background-purple-20: color(#17182b blend(#8f84ff 35%));
--color-background-purple-30: color(#17182b blend(#8f84ff 45%));
--color-background-purple-60: color(#17182b blend(#8f84ff 85%));
--color-green-base: #2ca878;
--color-green-d20: color(#2ca878 blend(white 20%));
--color-green-d40: color(#2ca878 blend(white 40%));
--color-green-d60: color(#2ca878 blend(white 60%));
--color-green-l20: color(#2ca878 blend(black 20%));
--color-green-l40: color(#2ca878 blend(black 40%));
--color-green-l60: color(#2ca878 blend(black 60%));
--color-background-green-10: color(#17182b blend(#2ca878 25%));
--color-background-green-20: color(#17182b blend(#2ca878 35%));
--color-background-green-30: color(#17182b blend(#2ca878 45%));
--color-background-green-60: color(#17182b blend(#2ca878 85%));
--color-orange-base: #ef7f18;
--color-orange-d20: color(#ef7f18 blend(white 20%));
--color-orange-d40: color(#ef7f18 blend(white 40%));
--color-orange-d60: color(#ef7f18 blend(white 60%));
--color-orange-l20: color(#ef7f18 blend(black 20%));
--color-orange-l40: color(#ef7f18 blend(black 40%));
--color-orange-l60: color(#ef7f18 blend(black 60%));
--color-background-orange-10: color(#17182b blend(#ef7f18 25%));
--color-background-orange-20: color(#17182b blend(#ef7f18 35%));
--color-background-orange-30: color(#17182b blend(#ef7f18 45%));
--color-background-orange-60: color(#17182b blend(#ef7f18 85%));
--color-red-base: #be1621;
--color-red-d20: color(#be1621 blend(white 20%));
--color-red-d40: color(#be1621 blend(white 40%));
--color-red-d60: color(#be1621 blend(white 60%));
--color-red-l20: color(#be1621 blend(black 20%));
--color-red-l40: color(#be1621 blend(black 40%));
--color-red-l60: color(#be1621 blend(black 60%));
--color-background-red-10: color(#17182b blend(#be1621 25%));
--color-background-red-20: color(#17182b blend(#be1621 35%));
--color-background-red-30: color(#17182b blend(#be1621 45%));
--color-background-red-60: color(#17182b blend(#be1621 85%));
}

View File

@@ -0,0 +1,47 @@
.context-color-success {
color: var(--color-green-base);
}
.context-color-error {
color: var(--color-red-base);
}
.context-color-warning {
color: var(--color-orange-base);
}
.context-color-info {
color: var(--color-purple-base);
}
.context-background-color-success {
background-color: var(--color-background-green-10);
}
.context-background-color-error {
background-color: var(--color-background-red-10);
}
.context-background-color-warning {
background-color: var(--color-background-orange-10);
}
.context-background-color-info {
background-color: var(--color-background-purple-10);
}
.context-border-color-success {
border-color: var(--color-green-base);
}
.context-border-color-error {
border-color: var(--color-red-base);
}
.context-border-color-warning {
border-color: var(--color-orange-base);
}
.context-border-color-info {
border-color: var(--color-purple-base);
}

View File

@@ -0,0 +1,6 @@
@import '@fontsource/poppins/400.css';
@import '@fontsource/poppins/500.css';
@import '@fontsource/poppins/600.css';
@import '@fontsource/poppins/700.css';
@import '@fontsource/poppins/900.css';
@import '@fontsource/poppins/400-italic.css';

View File

@@ -0,0 +1,42 @@
html {
box-sizing: border-box;
font-size: 10px;
}
body {
font-size: 1.6rem;
}
*,
*::before,
*::after {
box-sizing: inherit;
margin: 0;
position: relative;
font-family: Poppins, sans-serif;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
margin: 0;
padding: 0;
font-weight: normal;
}
ol,
ul {
list-style: none;
}
img {
max-width: 100%;
height: auto;
}

View File

@@ -0,0 +1,25 @@
:root {
--shadow-100: 0 0.1rem 0.1rem 0 rgba(26, 27, 56, 0.06);
--shadow-200: 0 0.1rem 0.1rem 0 rgba(26, 27, 56, 0.08), 0 0.2rem 0.1rem 0 rgba(26, 27, 56, 0.06),
0 0.1rem 0.3rem 0 rgba(26, 27, 56, 0.1);
--shadow-300: 0 0.6rem 1rem 0 rgba(26, 27, 56, 0.08), 0 0.1rem 1.8rem 0 rgba(26, 27, 56, 0.06),
0 0.3rem 0.5rem 0 rgba(26, 27, 56, 0.1);
--shadow-400: 0 2.4rem 3.8rem 0 rgba(26, 27, 56, 0.04), 0 0.9rem 4.6rem 0 rgba(26, 27, 56, 0.06),
0 1.1rem 1.5rem 0 rgba(26, 27, 56, 0.1);
}
:root.dark {
--shadow-100: 0 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.12);
--shadow-200: 0 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.16), 0 0.2rem 0.1rem 0 rgba(0, 0, 0, 0.12),
0 0.1rem 0.3rem 0 rgba(0, 0, 0, 0.2);
--shadow-300: 0 0.6rem 1rem 0 rgba(0, 0, 0, 0.16), 0 0.1rem 1.8rem 0 rgba(0, 0, 0, 0.12),
0 0.3rem 0.5rem 0 rgba(0, 0, 0, 0.2);
--shadow-400: 0 2.4rem 3.8rem 0 rgba(0, 0, 0, 0.08), 0 0.9rem 4.6rem 0 rgba(0, 0, 0, 0.12),
0 1.1rem 1.5rem 0 rgba(0, 0, 0, 0.2);
}

View File

@@ -0,0 +1,112 @@
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
.h7,
.p1,
.p2,
.p3,
.p4,
.c1,
.c2,
.c3,
.c4,
.c5 {
font-weight: 400;
&.black {
font-weight: 900;
}
&.semi-bold {
font-weight: 600;
}
&.medium {
font-weight: 500;
}
&.underline {
text-decoration: underline;
}
&.italic {
font-style: italic;
}
}
.h4,
.h5,
.h6,
.h7,
.p1,
.p2,
.p3,
.p4,
.c1,
.c2,
.c3,
.c4,
.c5 {
line-height: 1.5em;
letter-spacing: 0.04em;
}
.h1 {
font-size: 4.8rem;
line-height: 6rem;
}
.h2 {
font-size: 3.6rem;
line-height: 6rem;
}
.h3 {
font-size: 2.4rem;
line-height: 3.2rem;
}
.h4 {
font-size: 2rem;
}
.h5 {
font-size: 1.8rem;
}
.h6,
.p1,
.c1 {
font-size: 1.6rem;
}
.h7,
.p2,
.c2 {
font-size: 1.4rem;
}
.p3,
.c3 {
font-size: 1.2rem;
}
.p4,
.c4 {
font-size: 1rem;
}
.c5 {
font-size: 0.8rem;
}
.c1,
.c2,
.c3,
.c4,
.c5 {
text-transform: uppercase;
}

View File

@@ -0,0 +1,15 @@
@import '_colors.pcss';
@import '_reset.pcss';
@import '_fonts.pcss';
@import '_context.pcss';
@import '_shadows.pcss';
@import '_typography.pcss';
:root {
color: var(--color-grey-100);
background-color: var(--color-background-primary);
&.dark {
color-scheme: dark;
}
}

View File

@@ -0,0 +1,38 @@
<template>
<div :class="classProp" class="ui-card">
<slot />
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useContext } from '../../composables/context.composable'
import type { Color } from '../../types/color'
import { ColorContext } from '../../utils/context'
const props = defineProps<{
color?: Color
}>()
const { name: contextColor, backgroundClass } = useContext(ColorContext, () => props.color)
// We don't want to inherit "info" color
const classProp = computed(() => {
if (props.color === undefined && contextColor.value === 'info')
return 'bg-primary'
return backgroundClass.value
})
</script>
<style lang="postcss" scoped>
.ui-card {
padding: 2.1rem;
border-radius: 0.8rem;
box-shadow: var(--shadow-200);
}
.bg-primary {
background-color: var(--background-color-primary);
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<UiSpinner v-if="busy" class="ui-icon" />
<FontAwesomeIcon v-else-if="icon !== undefined" :icon="icon" class="ui-icon" :fixed-width="fixedWidth" />
</template>
<script lang="ts" setup>
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import UiSpinner from './UiSpinner.vue'
withDefaults(
defineProps<{
busy?: boolean
icon?: IconDefinition
fixedWidth?: boolean
}>(),
{ fixedWidth: true },
)
</script>

View File

@@ -0,0 +1,47 @@
<!-- Adapted from https://www.benmvp.com/blog/how-to-create-circle-svg-gradient-loading-spinner/ -->
<template>
<svg class="ui-spinner" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" fill="none">
<defs>
<linearGradient :id="secondHalfId">
<stop offset="0%" stop-opacity="0" stop-color="currentColor" />
<stop offset="100%" stop-opacity="0.5" stop-color="currentColor" />
</linearGradient>
<linearGradient :id="firstHalfId">
<stop offset="0%" stop-opacity="1" stop-color="currentColor" />
<stop offset="100%" stop-opacity="0.5" stop-color="currentColor" />
</linearGradient>
</defs>
<g stroke-width="40">
<path d="M 30 200 A 170 170 180 0 1 370 200" :stroke="`url(#${secondHalfId})`" />
<path d="M 370 200 A 170 170 0 0 1 30 200" :stroke="`url(#${firstHalfId})`" />
<path stroke="currentColor" stroke-linecap="round" d="M 30 200 A 170 170 180 0 1 30 200" />
</g>
</svg>
</template>
<script lang="ts" setup>
import { uniqueId } from 'lodash-es'
const firstHalfId = uniqueId('spinner-first-half-')
const secondHalfId = uniqueId('spinner-second-half-')
</script>
<style lang="postcss" scoped>
.ui-spinner {
width: 1.2em;
height: 1.2em;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1,36 @@
import type { ComputedRef, InjectionKey, MaybeRefOrGetter } from 'vue'
import { computed, inject, provide, toValue } from 'vue'
type Context<T = any, Output = any> = ReturnType<typeof createContext<T, Output>>
type ContextOutput<Ctx extends Context> = Ctx extends Context<any, infer Output> ? Output : never
type ContextValue<Ctx extends Context> = Ctx extends Context<infer T> ? T : never
export function createContext<T, Output = ComputedRef<T>>(
initialValue: MaybeRefOrGetter<T>,
customBuilder?: (value: ComputedRef<T>) => Output,
) {
return {
id: Symbol('context') as InjectionKey<MaybeRefOrGetter<T>>,
initialValue,
builder: customBuilder ?? (value => value as Output),
}
}
export function useContext<Ctx extends Context, T extends ContextValue<Ctx>>(
context: Ctx,
newValue?: MaybeRefOrGetter<T | undefined>,
): ContextOutput<Ctx> {
const currentValue = inject(context.id, context.initialValue)
const build = (value: MaybeRefOrGetter<T>) => context.builder(computed(() => toValue(value)))
if (newValue !== undefined) {
const updatedValue = () => toValue(newValue) ?? toValue(currentValue)
provide(context.id, updatedValue)
return build(updatedValue)
}
return build(currentValue)
}

View File

@@ -0,0 +1 @@
export type Color = 'info' | 'error' | 'warning' | 'success'

View File

@@ -0,0 +1,12 @@
import { computed } from 'vue'
import { createContext } from '../composables/context.composable'
import type { Color } from '../types/color'
export const DisabledContext = createContext(false)
export const ColorContext = createContext('info' as Color, color => ({
name: color,
textClass: computed(() => `context-color-${color.value}`),
backgroundClass: computed(() => `context-background-color-${color.value}`),
borderClass: computed(() => `context-border-color-${color.value}`),
}))

View File

@@ -0,0 +1,51 @@
{
"name": "@xen-orchestra/web-core",
"type": "module",
"version": "0.0.1",
"private": true,
"exports": {
"./*": {
"types": "./lib/*",
"import": "./lib/*"
},
"./eslint": {
"import": "./eslint.config.js"
}
},
"files": [
"lib"
],
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"devDependencies": {
"@antfu/eslint-config": "^2.4.6",
"@fontsource/poppins": "^5.0.8",
"@fortawesome/fontawesome-common-types": "^6.5.1",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.5",
"@tsconfig/node18": "^18.2.2",
"@types/lodash-es": "^4.17.12",
"@types/node": "^18.19.3",
"@vitejs/plugin-vue": "^5.0.0-beta.1",
"@vue/tsconfig": "^0.5.1",
"@vueuse/core": "^10.7.0",
"eslint": "^8.56.0",
"glob": "^10.3.10",
"lodash-es": "^4.17.21",
"npm-run-all2": "^6.1.1",
"pinia": "^2.1.7",
"typescript": "~5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.6.4",
"vite-plugin-lib-inject-css": "^1.3.0",
"vue": "^3.4.0-beta.4",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.25"
},
"lint-staged": {
"*": "eslint --fix"
}
}

View File

@@ -0,0 +1,10 @@
{
"extends": ["@vue/tsconfig/tsconfig.dom.json"],
"compilerOptions": {
"baseUrl": ".",
"module": "ESNext",
"noEmit": true
},
"include": ["env.d.ts", "eslint.config.js", "lib/**/*", "lib/**/*.vue"],
"exclude": ["lib/**/__tests__/*"]
}

31
@xen-orchestra/web-lite/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
typed-router.d.ts

View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

View File

@@ -0,0 +1,40 @@
# web-lite
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```

1
@xen-orchestra/web-lite/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1 @@
export { default } from '@xen-orchestra/web-core/eslint'

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,40 @@
{
"name": "@xen-orchestra/web-lite",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@antfu/eslint-config": "^2.4.6",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@tsconfig/node18": "^18.2.2",
"@types/node": "^18.19.3",
"@vitejs/plugin-vue": "^5.0.0-beta.1",
"@vue/tsconfig": "^0.5.1",
"@vueuse/core": "^10.7.0",
"@xen-orchestra/web-core": "^0.0.1",
"eslint": "^8.56.0",
"npm-run-all2": "^6.1.1",
"pinia": "^2.1.7",
"postcss-apply": "^0.12.0",
"postcss-color-function": "^4.1.0",
"postcss-nested": "^6.0.1",
"typescript": "~5.3.3",
"unplugin-vue-router": "^0.7.0",
"vite": "^5.0.10",
"vue": "^3.4.0-beta.4",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.25"
},
"lint-staged": {
"*": "eslint --fix"
}
}

View File

@@ -0,0 +1,9 @@
'use strict'
module.exports = {
plugins: {
'postcss-apply': {},
'postcss-nested': {},
'postcss-color-function': {},
},
}

View File

@@ -0,0 +1,3 @@
<template>
<RouterView />
</template>

View File

@@ -0,0 +1,19 @@
<template>
<div class="main-layout">
<div>XO Lite Main Layout</div>
<div class="content">
<slot />
</div>
</div>
</template>
<style lang="postcss" scoped>
.main-layout {
font-size: 2rem;
padding: 4rem;
}
.content {
padding: 1rem 2rem;
}
</style>

View File

@@ -0,0 +1,18 @@
import '@xen-orchestra/web-core/assets/css/base.pcss'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router/auto'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
history: createWebHashHistory(),
})
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,30 @@
<template>
<MainLayout>
<UiCard color="info">
<div>Welcome on XO Lite main page</div>
<div>
Demo icon from web-core:
<UiIcon :icon="faShip" />
</div>
<div>
<button
type="button"
@click="toggleDark()"
>
Toggle Dark Mode {{ isDark ? 'OFF' : 'ON' }}
</button>
</div>
</UiCard>
</MainLayout>
</template>
<script lang="ts" setup>
import { faShip } from '@fortawesome/free-solid-svg-icons/faShip'
import { useDark, useToggle } from '@vueuse/core'
import UiCard from '@xen-orchestra/web-core/components/ui/UiCard.vue'
import UiIcon from '@xen-orchestra/web-core/components/ui/UiIcon.vue'
import MainLayout from '@/layouts/MainLayout.vue'
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>

View File

@@ -0,0 +1,22 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"rootDir": "..",
"paths": {
"@/*": ["./src/*"],
"@xen-orchestra/web-core/*": ["../web-core/lib/*"]
},
"noEmit": true
},
"include": [
"env.d.ts",
"typed-router.d.ts",
"src/**/*",
"src/**/*.vue",
"../web-core/lib/**/*",
"../web-core/lib/**/*.vue"
],
"exclude": ["src/**/__tests__/*"]
}

View File

@@ -0,0 +1,11 @@
{
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
],
"files": []
}

View File

@@ -0,0 +1,11 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"],
"noEmit": true
},
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*"]
}

View File

@@ -0,0 +1,15 @@
import { URL, fileURLToPath } from 'node:url'
import vue from '@vitejs/plugin-vue'
import vueRouter from 'unplugin-vue-router/vite'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vueRouter(), vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})

31
@xen-orchestra/web/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
typed-router.d.ts

View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

View File

@@ -0,0 +1,40 @@
# web
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```

1
@xen-orchestra/web/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1 @@
export { default } from '@xen-orchestra/web-core/eslint'

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
{
"name": "@xen-orchestra/web",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@antfu/eslint-config": "^2.4.6",
"@tsconfig/node18": "^18.2.2",
"@types/node": "^18.19.3",
"@vitejs/plugin-vue": "^5.0.0-beta.1",
"@vue/tsconfig": "^0.5.1",
"@xen-orchestra/web-core": "^0.0.1",
"eslint": "^8.56.0",
"npm-run-all2": "^6.1.1",
"pinia": "^2.1.7",
"postcss-apply": "^0.12.0",
"postcss-color-function": "^4.1.0",
"postcss-nested": "^6.0.1",
"typescript": "~5.3.3",
"unplugin-vue-router": "^0.7.0",
"vite": "^5.0.10",
"vue": "^3.4.0-beta.4",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.25"
},
"lint-staged": {
"*": "eslint --fix"
}
}

View File

@@ -0,0 +1,9 @@
'use strict'
module.exports = {
plugins: {
'postcss-apply': {},
'postcss-nested': {},
'postcss-color-function': {},
},
}

View File

View File

@@ -0,0 +1,3 @@
<template>
<RouterView />
</template>

View File

@@ -0,0 +1,20 @@
<template>
<div class="main-layout">
<div>XO 6 Main Layout</div>
<div class="content">
<slot />
</div>
</div>
</template>
<style lang="postcss" scoped>
.main-layout {
padding: 8rem;
font-size: 3rem;
}
.content {
padding: 2rem 4rem;
border-bottom: 2px solid black;
}
</style>

View File

@@ -0,0 +1,18 @@
import '@xen-orchestra/web-core/assets/css/base.pcss'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router/auto'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
history: createWebHashHistory(),
})
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,27 @@
<template>
<MainLayout>
<UiCard color="info">
<div>Welcome on XO 6 main page</div>
<div>
Demo icon from web-core:
<UiIcon :icon="faRocket" />
</div>
<div>
<button type="button" @click="toggleDark()">
Toggle Dark Mode {{ isDark ? 'OFF' : 'ON' }}
</button>
</div>
</UiCard>
</MainLayout>
</template>
<script lang="ts" setup>
import { faRocket } from '@fortawesome/free-solid-svg-icons/faRocket'
import { useDark, useToggle } from '@vueuse/core'
import UiCard from '@xen-orchestra/web-core/components/ui/UiCard.vue'
import UiIcon from '@xen-orchestra/web-core/components/ui/UiIcon.vue'
import MainLayout from '@/layouts/MainLayout.vue'
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>

View File

@@ -0,0 +1,22 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"rootDir": "..",
"paths": {
"@/*": ["./src/*"],
"@xen-orchestra/web-core/*": ["../web-core/lib/*"]
},
"noEmit": true
},
"include": [
"env.d.ts",
"typed-router.d.ts",
"src/**/*",
"src/**/*.vue",
"../web-core/lib/**/*",
"../web-core/lib/**/*.vue"
],
"exclude": ["src/**/__tests__/*"]
}

View File

@@ -0,0 +1,11 @@
{
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
],
"files": []
}

View File

@@ -0,0 +1,11 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"],
"noEmit": true
},
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*"]
}

View File

@@ -0,0 +1,15 @@
import { URL, fileURLToPath } from 'node:url'
import vue from '@vitejs/plugin-vue'
import vueRouter from 'unplugin-vue-router/vite'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vueRouter(), vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})

1474
yarn.lock

File diff suppressed because it is too large Load Diff