xen-orchestra/@xen-orchestra/lite
2024-01-31 15:54:43 +01:00
..
.vscode feat(lite): initial Vue.js implementation 2022-11-03 10:30:39 +01:00
docs feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
public feat(lite): introducing @xen-orchestra/web-core (#7302) 2024-01-18 10:04:25 +01:00
scripts feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
src feat(lite): introducing @xen-orchestra/web-core (#7302) 2024-01-18 10:04:25 +01:00
.env.dist feat(lite): initial Vue.js implementation 2022-11-03 10:30:39 +01:00
.npmignore chore(lite): normalize package 2022-11-03 10:30:39 +01:00
CHANGELOG.md feat(lite): introduce PWA manifest (#7291) 2024-01-16 11:19:10 +01:00
env.d.ts feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
index.html chore: format with Prettier 2024-01-19 16:42:10 +01:00
package.json feat(xo6): introducing @xen-orchestra/web (#7309) 2024-01-23 11:25:18 +01:00
postcss.config.mjs feat(lite): introducing @xen-orchestra/web-core (#7302) 2024-01-18 10:04:25 +01:00
README.md feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
tsconfig.app.json feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
tsconfig.json fix: format with Prettier 2024-01-31 15:54:43 +01:00
tsconfig.node.json feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
tsconfig.type-check.json feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00
vite.config.ts feat(lite): upgrade deps + root eslint config (#7292) 2024-01-15 11:12:53 +01:00

POC XO Lite

  • Clone
  • Copy .env.dist to .env and set vars
  • yarn
  • yarn dev

Conventions

File names

Type Format Exemple
Component components/<PascalCase>.vue components/FooBar.vue
View views/<PascalCase>View.vue views/FooBarView.vue
Composable composables/<kebab-case>.composable.ts composables/foo-bar.composable.ts
Store stores/<kebab-case>.store.ts stores/foo-bar.store.ts
Other libs/<kebab-case>.ts libs/foo-bar.ts

For components and views, prepend the subdirectories names to the resulting filename.

Example: components/foo/bar/FooBarBaz.vue

Vue Components

Use Vue Single File Components (*.vue).

Insert blocks in the following order: template, script then style.

Template

Use HTML.

If your component only has one root element, add the component name as a class.

<!-- MyComponent.vue -->
<template>
  <div class="my-component">...</div>
</template>

Script

Use composition API + TypeScript + setup attribute (<script lang="ts" setup>).

Note: When reading Vue official doc, don't forget to set "API Preference" toggle (in the upper left) on "Composition".

<script lang="ts" setup>
import { computed, ref } from 'vue'

const props = defineProps<{
  greetings: string
}>()

const firstName = ref('')
const lastName = ref('')

const fullName = computed(() => `${props.greetings} ${firstName.value} ${lastName.value}`)
</script>

CSS

Always use scoped attribute (<style scoped>).

Nested rules are allowed.

Vue variables can be interpolated with v-bind.

<script lang="ts" setup>
import { ref } from 'vue'

const fontSize = ref('2rem')
</script>

<style scoped>
.my-item {
  .nested {
    font-size: v-bind(fontSize);
  }
}
</style>

Icons

This project is using Font Awesome 6 Free.

Icons can be displayed with the UiIcon component.

Passing undefined as icon prop will disable the component (no need to use an additional v-if condition).

Use the busy prop to display a loader icon.

<template>
  <div>
    <UiIcon :icon="faDisplay" />
  </div>
</template>

<script lang="ts" setup>
import UiIcon from '@/components/ui/icon/UiIcon.vue'
import { faDisplay } from '@fortawesome/free-solid-svg-icons'
</script>

Font weight <=> Style name

Here is the equivalent between font weight and style name.

Style name Font weight
Solid 900
Regular 400

CSS

Always use rem unit (1rem = 10px)

Store

Use Pinia store with setup function.

State are ref

Getters are computed

Actions/Mutations are simple functions

Naming convention

For a foobar store, create a store/foobar.store.ts then use defineStore('foobar', setupFunc)

Example

import { computed, ref } from 'vue'

export const useFoobarStore = defineStore('foobar', () => {
  const aStateVar = ref(0)
  const otherStateVar = ref(0)
  const aGetter = computed(() => aStateVar.value * 2)
  const anAction = () => (otherStateVar.value += 10)

  return {
    aStateVar,
    otherStateVar,
    aGetter,
    anAction,
  }
})

I18n

Internationalization of the app is done with Vue-i18n.

Locale files are located in src/locales directory.

Source of truth is en-US.json file.

To quickly check if there are missing translations in other locale files, open main.ts and check the messages property of createI18n() for TypeScript error.

Example

{
  "hello": "Hello",
  "hello_name": "Hello {name}",
  "hello_linked": "@:hello_name how are you?",
  "hello_plural": "No hello | Hello to you | Hello to {count} persons"
}
<!-- String -->

<p>{{ $t("hello") }}</p>
<!-- Hello -->
<p>{{ $t("hello_name", { name: "World" }) }}</p>
<!-- Hello World -->
<p>{{ $t("hello_linked", { name: "World" }) }}</p>
<!-- Hello World how are you? -->
<p>{{ $tc("hello_plural", 0) }}</p>
<!-- No hello -->
<p>{{ $tc("hello_plural", 1) }}</p>
<!-- Hello to you -->
<p>{{ $tc("hello_plural", 4) }}</p>
<!-- Hello to 4 persons -->

<!-- Date and time -->

<p>{{ $d(date, "date_short") }}</p>
<!-- 9/10/2022 -->
<p>{{ $d(date, "date_medium") }}</p>
<!-- Sep 10, 2022 -->
<p>{{ $d(date, "date_long") }}</p>
<!-- September 10, 2022 -->
<p>{{ $d(date, "datetime_short") }}</p>
<!-- 9/10/2022, 06:30 PM -->
<p>{{ $d(date, "datetime_medium") }}</p>
<!-- Sep 10, 2022, 06:30 PM -->
<p>{{ $d(date, "datetime_long") }}</p>
<!-- September 10, 2022 at 06:30 PM -->
<p>{{ $d(date, "time") }}</p>
<!-- 06:30 PM -->

<!-- Number -->

<p>{{ $n(1234567.898765) }}</p>
<!-- 1,234,567.899 -->