feat: add pinia and pinia-shared-state for sync shared state between tabs

This commit is contained in:
Sadegh Barati 2023-11-02 13:37:16 +03:30
parent 67e6f1a979
commit f5bfa881ce
16 changed files with 184 additions and 47 deletions

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { defineAsyncComponent } from 'vue'
import Spinner from './Spinner.vue'
import { useConfigStore } from '@/stores/config'
@ -6,7 +7,7 @@ import { useConfigStore } from '@/stores/config'
const props = defineProps<{
name: string
}>()
const { style } = useConfigStore()
const { style } = storeToRefs(useConfigStore())
const Component = defineAsyncComponent({
loadingComponent: Spinner,

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import StyleSwitcher from './StyleSwitcher.vue'
import ComponentLoader from './ComponentLoader.vue'
import Stackblitz from './Stackblitz.vue'
@ -18,7 +19,7 @@ withDefaults(defineProps<{
sfcTsHtml?: string
}>(), { align: 'center' })
const { style } = useConfigStore()
const { style } = storeToRefs(useConfigStore())
</script>
<template>

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { useConfigStore } from '@/stores/config'
@ -7,7 +8,7 @@ import { Button } from '@/lib/registry/new-york/ui/button'
import CheckIcon from '~icons/radix-icons/check'
import CopyIcon from '~icons/radix-icons/copy'
const { theme, config } = useConfigStore()
const { theme, radius } = storeToRefs(useConfigStore())
const activeTheme = computed(() => themes.find(i => i.name === theme.value))
@ -34,7 +35,7 @@ async function copyCode() {
<span class="line text-white">&nbsp;&nbsp;--border:{{ activeTheme?.cssVars.light.border }};</span>
<span class="line text-white">&nbsp;&nbsp;--input:{{ activeTheme?.cssVars.light.input }};</span>
<span class="line text-white">&nbsp;&nbsp;--ring:{{ activeTheme?.cssVars.light.ring }};</span>
<span class="line text-white">&nbsp;&nbsp;--radius: {{ config.radius }}rem;</span>
<span class="line text-white">&nbsp;&nbsp;--radius: {{ radius }}rem;</span>
<span class="line text-white">&#125;</span>
<span class="line text-white">&nbsp;</span>
<span class="line text-white">.dark &#123;</span>

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { type SelectTriggerProps } from 'radix-vue'
import { useConfigStore } from '@/stores/config'
@ -13,11 +14,11 @@ import {
import { styles } from '@/lib/registry/styles'
const props = defineProps<SelectTriggerProps & { class?: string }>()
const { config } = useConfigStore()
const { style } = storeToRefs(useConfigStore())
</script>
<template>
<Select v-model="config.style">
<Select v-model="style">
<SelectTrigger :class="cn('h-7 w-[145px] text-xs [&_svg]:h-4 [&_svg]:w-4', props.class)">
<span class="text-muted-foreground">Style: </span>
<SelectValue placeholder="Select style" />

View File

@ -1,5 +1,8 @@
/* eslint-disable vue/component-definition-name-casing */
// https://vitepress.dev/guide/custom-theme
import { createPinia } from 'pinia'
import { PiniaSharedState } from 'pinia-shared-state'
import Layout from './layout/MainLayout.vue'
import DocsLayout from './layout/DocsLayout.vue'
import ExamplesLayout from './layout/ExamplesLayout.vue'
@ -9,9 +12,16 @@ import './styles/vp-doc.css'
import './styles/shiki.css'
import './styles/themes.css'
const pinia = createPinia()
pinia.use(PiniaSharedState({
enable: false,
type: 'native',
}))
export default {
Layout,
enhanceApp({ app }) {
app.use(pinia)
// ...
app.component('docs', DocsLayout)
app.component('examples', ExamplesLayout)

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useMagicKeys, useToggle } from '@vueuse/core'
import { onMounted, ref, watch } from 'vue'
import { onMounted, ref, watch, watchEffect } from 'vue'
import { Content, useData, useRoute, useRouter } from 'vitepress'
import { SearchIcon } from 'lucide-vue-next'
import { type NavItem, docsConfig } from '../config/docs'
@ -21,15 +22,24 @@ import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import File from '~icons/radix-icons/file'
import Circle from '~icons/radix-icons/circle'
const { radius, theme } = useConfigStore()
const { frontmatter, isDark } = useData()
const { radius, theme } = storeToRefs(useConfigStore())
// Whenever the component is mounted, update the document class list
onMounted(() => {
// onMounted(() => {
// document.documentElement.style.setProperty('--radius', `${radius.value}rem`)
// document.documentElement.classList.add(`theme-${theme.value}`)
// })
watchEffect(() => {
document.documentElement.className = ''
if (isDark.value)
document.documentElement.classList.add('dark')
document.documentElement.style.setProperty('--radius', `${radius.value}rem`)
document.documentElement.classList.add(`theme-${theme.value}`)
})
const { frontmatter, isDark } = useData()
const $route = useRoute()
const $router = useRouter()
const toggleDark = useToggle(isDark)

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { onMounted, watch } from 'vue'
import { Paintbrush } from 'lucide-vue-next'
import { useData } from 'vitepress'
@ -13,10 +14,14 @@ import { Label } from '@/lib/registry/new-york/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/lib/registry/new-york/ui/tooltip'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/lib/registry/new-york/ui/dialog'
import RadixIconsCheck from '~icons/radix-icons/check'
import RadixIconsSun from '~icons/radix-icons/sun'
import RadixIconsMoon from '~icons/radix-icons/moon'
// import { Input } from '@/lib/registry/new-york/ui/input'
// import ArrowTopRight from '~icons/radix-icons/arrow-top-right'
type Color =
| 'zinc'
| 'slate'
@ -47,7 +52,7 @@ const allColors: Color[] = [
'violet',
]
const { theme, radius, setRadius, setTheme } = useConfigStore()
const { theme, radius, alias } = storeToRefs(useConfigStore())
const { isDark } = useData()
// Whenever the component is mounted, update the document class list
@ -101,7 +106,7 @@ watch(radius, (radius) => {
? 'border-foreground'
: 'border-transparent'
"
@click="setTheme(color)"
@click="theme = color"
>
<span
class="flex h-6 w-6 items-center justify-center rounded-full"
@ -154,7 +159,7 @@ watch(radius, (radius) => {
? 'border-foreground border-2'
: ''
"
@click="setTheme(color)"
@click="theme = color"
>
<span
class="h-5 w-5 rounded-full flex items-center justify-center"
@ -184,7 +189,7 @@ watch(radius, (radius) => {
? 'border-foreground border-2'
: ''
"
@click="setRadius(r)"
@click="radius = r"
>
<span class="text-xs">
{{ r }}
@ -216,6 +221,20 @@ watch(radius, (radius) => {
</Button>
</div>
</div>
<!-- <div class="space-y-1.5 pt-6">
<Label for="alias" class="text-xs text-primary"> <a class="flex gap-1 items-center text-primary" href="/docs/components-json.html#aliases-components">Alias <ArrowTopRight width="8" /></a> </Label>
<div class="flex space-x-2 py-1.5">
<input id="alias" v-model.lazy="alias" class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50">
</div>
</div> -->
<!-- <div class="space-y-1.5 pt-6">
<Button @click="resetToDefault">
Reset configs
</Button>
</div> -->
</div>
</PopoverContent>
</Popover>

View File

@ -26,6 +26,8 @@
"codesandbox": "^2.2.3",
"date-fns": "^2.30.0",
"lucide-vue-next": "^0.276.0",
"pinia": "^2.1.7",
"pinia-shared-state": "^0.4.5",
"radix-vue": "^1.0.0",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2",

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { VisLine, VisScatter, VisStackedBar, VisXYContainer } from '@unovis/vue'
import { computed } from 'vue'
import { useData } from 'vitepress'
@ -18,10 +19,10 @@ const data = [
{ revenue: 26475, subscription: 189 },
]
const cfg = useConfigStore()
const { theme: themeStore } = storeToRefs(useConfigStore())
const { isDark } = useData()
const theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))
const theme = computed(() => themes.find(theme => theme.name === themeStore.value))
const lineX = (d: Data, i: number) => i
const lineY = (d: Data) => d.revenue

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
import { ChevronDown, Minus, Plus, Send } from 'lucide-vue-next'
import { VisStackedBar, VisXYContainer } from '@unovis/vue'
@ -17,8 +18,8 @@ import { themes } from '@/lib/registry/themes'
import { useConfigStore } from '@/stores/config'
const { isDark } = useData()
const cfg = useConfigStore()
const theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))
const { theme: themeStore } = storeToRefs(useConfigStore())
const theme = computed(() => themes.find(theme => theme.name === themeStore.value))
const goal = ref(350)

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { VisCrosshair, VisLine, VisScatter, VisTooltip, VisXYContainer } from '@unovis/vue'
import { Line } from '@unovis/ts'
import {
@ -10,7 +11,7 @@ import {
} from '@/lib/registry/default/ui/card'
import { useConfigStore } from '@/stores/config'
const { themePrimary } = useConfigStore()
const { themePrimary } = storeToRefs(useConfigStore())
type Data = typeof data[number]
const data = [

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { VisLine, VisScatter, VisStackedBar, VisXYContainer } from '@unovis/vue'
import { computed } from 'vue'
import { useData } from 'vitepress'
@ -18,10 +19,10 @@ const data = [
{ revenue: 26475, subscription: 189 },
]
const cfg = useConfigStore()
const { theme: themeStore } = storeToRefs(useConfigStore())
const { isDark } = useData()
const theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))
const theme = computed(() => themes.find(theme => theme.name === themeStore.value))
const lineX = (d: Data, i: number) => i
const lineY = (d: Data) => d.revenue

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { ref } from 'vue'
import { VisStackedBar, VisXYContainer } from '@unovis/vue'
import { MinusIcon, PlusIcon } from '@radix-icons/vue'
@ -14,7 +15,7 @@ import {
} from '@/lib/registry/new-york/ui/card'
import { useConfigStore } from '@/stores/config'
const { themePrimary } = useConfigStore()
const { themePrimary } = storeToRefs(useConfigStore())
const goal = ref(350)

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { VisCrosshair, VisLine, VisScatter, VisTooltip, VisXYContainer } from '@unovis/vue'
import { Line } from '@unovis/ts'
import {
@ -10,7 +11,7 @@ import {
} from '@/lib/registry/new-york/ui/card'
import { useConfigStore } from '@/stores/config'
const { themePrimary } = useConfigStore()
const { themePrimary } = storeToRefs(useConfigStore())
type Data = typeof data[number]
const data = [
@ -57,7 +58,7 @@ function computeLineOpacity(val: any, index: number) {
<template>
<Card>
<CardHeader>
<CardTitle>Exercise Minutes</CardTitle>
<CardTitle>Exercise Minutes {{ themePrimary }}</CardTitle>
<CardDescription>
Your excercise minutes are ahead of where you normally are.
</CardDescription>

View File

@ -1,5 +1,7 @@
import { computed } from 'vue'
import { useSessionStorage } from '@vueuse/core'
import { defineStore } from 'pinia'
import 'pinia-shared-state'
import { computed, ref } from 'vue'
import { useData } from 'vitepress'
import { type Theme, themes } from './../lib/registry/themes'
import { type Style, styles } from '@/lib/registry/styles'
@ -12,27 +14,22 @@ interface Config {
export const RADII = [0, 0.25, 0.5, 0.75, 1]
export function useConfigStore() {
// const DEFAULT_CONFIGS = {
// theme: 'zinc',
// radius: 0.5,
// style: styles[0].name,
// alias: '@/components/',
// } as const
export const useConfigStore = defineStore('config', () => {
const { isDark } = useData()
const config = useSessionStorage<Config>('config', {
theme: 'zinc',
radius: 0.5,
style: styles[0].name,
})
const themeClass = computed(() => `theme-${config.value.theme}`)
const theme = ref<Config['theme']>('zinc')
const radius = ref(0.5)
const style = ref<Config['style']>('default')
const alias = ref('@/components/')
const theme = computed(() => config.value.theme)
const radius = computed(() => config.value.radius)
const style = computed(() => config.value.style)
function setTheme(themeName: Theme['name']) {
config.value.theme = themeName
}
function setRadius(newRadius: number) {
config.value.radius = newRadius
}
const themeClass = computed(() => `theme-${theme.value}`)
const themePrimary = computed(() => {
const t = themes.find(t => t.name === theme.value)
@ -41,5 +38,16 @@ export function useConfigStore() {
})`
})
return { config, theme, setTheme, radius, setRadius, themeClass, style, themePrimary }
}
return {
themeClass,
themePrimary,
theme,
radius,
style,
alias,
}
}, {
share: {
enable: true,
},
})

View File

@ -92,6 +92,12 @@ importers:
lucide-vue-next:
specifier: ^0.276.0
version: 0.276.0(vue@3.3.6)
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.2.2)(vue@3.3.6)
pinia-shared-state:
specifier: ^0.4.5
version: 0.4.5(pinia@2.1.7)(vue@3.3.6)
radix-vue:
specifier: ^1.0.0
version: 1.0.0(vue@3.3.6)
@ -3328,6 +3334,15 @@ packages:
dependencies:
fill-range: 7.0.1
/broadcast-channel@5.5.1:
resolution: {integrity: sha512-C7LtMmJCIIU07xtJngYE2OxaGTGBsG+wOa0mBSPRpbTF36kqtsXQhpxtCVDTkpe8gpZMn9C6PhH+mZ/js4IabA==}
dependencies:
'@babel/runtime': 7.23.2
oblivious-set: 1.1.1
p-queue: 6.6.2
unload: 2.4.1
dev: false
/browserslist@4.22.1:
resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@ -4439,6 +4454,10 @@ packages:
dev: false
patched: true
/devalue@4.3.2:
resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==}
dev: false
/didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@ -5151,6 +5170,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: false
/eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
dev: true
@ -7489,6 +7512,10 @@ packages:
object-keys: 1.1.1
dev: false
/oblivious-set@1.1.1:
resolution: {integrity: sha512-Oh+8fK09mgGmAshFdH6hSVco6KZmd1tTwNFWj35OvzdmJTMZtAkbn05zar2iG3v6sDs1JLEtOiBGNb6BHwkb2w==}
dev: false
/ohash@1.1.3:
resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==}
dev: false
@ -7625,6 +7652,21 @@ packages:
aggregate-error: 3.1.0
dev: true
/p-queue@6.6.2:
resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
engines: {node: '>=8'}
dependencies:
eventemitter3: 4.0.7
p-timeout: 3.2.0
dev: false
/p-timeout@3.2.0:
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
engines: {node: '>=8'}
dependencies:
p-finally: 1.0.0
dev: false
/p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
@ -7841,6 +7883,38 @@ packages:
engines: {node: '>=4'}
dev: false
/pinia-shared-state@0.4.5(pinia@2.1.7)(vue@3.3.6):
resolution: {integrity: sha512-rBOepDuMtTo0GMbCNv+CZIPXbN13MxhXwtXnQ0mnN5hzId4Tv4O71c4GUKzJx8NORvYRGn/Fks+vVH23Z3qA1Q==}
peerDependencies:
pinia: ^2
dependencies:
broadcast-channel: 5.5.1
devalue: 4.3.2
pinia: 2.1.7(typescript@5.2.2)(vue@3.3.6)
vue-demi: 0.14.6(vue@3.3.6)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/pinia@2.1.7(typescript@5.2.2)(vue@3.3.6):
resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==}
peerDependencies:
'@vue/composition-api': ^1.4.0
typescript: '>=4.4.4'
vue: ^2.6.14 || ^3.3.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
typescript:
optional: true
dependencies:
'@vue/devtools-api': 6.5.1
typescript: 5.2.2
vue: 3.3.6(typescript@5.2.2)
vue-demi: 0.14.6(vue@3.3.6)
dev: false
/pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
@ -9495,6 +9569,10 @@ packages:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'}
/unload@2.4.1:
resolution: {integrity: sha512-IViSAm8Z3sRBYA+9wc0fLQmU9Nrxb16rcDmIiR6Y9LJSZzI7QY5QsDhqPpKOjAn0O9/kfK1TfNEMMAGPTIraPw==}
dev: false
/unplugin-icons@0.17.1:
resolution: {integrity: sha512-KsWejBPCHokYCNDQUzGu6R3E3XDYH/YpewgQwrVBXgpl1iR0RdW1NEGNdjlbuapwVnZXVgA5eiDTfNaQCawSdg==}
peerDependencies: