init: чистый старт Laravel + Vuexy

This commit is contained in:
2026-02-20 13:30:03 +03:00
commit af53445c26
474 changed files with 58860 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import { stringifyQuery } from 'ufo'
import type { MaybeRefOrGetter } from 'vue'
interface Options {
query: MaybeRefOrGetter<Record<string, any>>
}
export const createUrl = (url: MaybeRefOrGetter<string>, options?: Options) => computed(() => {
if (!options?.query)
return toValue(url)
const _url = toValue(url)
const _query = toValue(options?.query)
const queryObj = Object.fromEntries(
Object.entries(_query).map(([key, val]) => [key, toValue(val)]),
)
return `${_url}${queryObj ? `?${stringifyQuery(queryObj)}` : ''}`
})

View File

@@ -0,0 +1,43 @@
// Ported from [Nuxt](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/cookie.ts)
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
import { parse, serialize } from 'cookie-es'
import { destr } from 'destr'
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
export interface CookieOptions<T = any> extends _CookieOptions {
decode?(value: string): T
encode?(value: T): string
default?: () => T | Ref<T>
watch?: boolean | 'shallow'
}
export type CookieRef<T> = Ref<T>
const CookieDefaults: CookieOptions<any> = {
path: '/',
watch: true,
decode: val => destr(decodeURIComponent(val)),
encode: val => encodeURIComponent(typeof val === 'string' ? val : JSON.stringify(val)),
}
export const useCookie = <T = string | null | undefined>(name: string, _opts?: CookieOptions<T>): CookieRef<T> => {
const opts = { ...CookieDefaults, ..._opts || {} }
const cookies = parse(document.cookie, opts)
const cookie = ref<T | undefined>(cookies[name] as any ?? opts.default?.())
watch(cookie, () => {
document.cookie = serializeCookie(name, cookie.value, opts)
})
return cookie as CookieRef<T>
}
function serializeCookie(name: string, value: any, opts: CookieSerializeOptions = {}) {
if (value === null || value === undefined)
return serialize(name, value, { ...opts, maxAge: -1 })
return serialize(name, value, { ...opts, maxAge: 60 * 60 * 24 * 30 })
}

View File

@@ -0,0 +1,28 @@
import { useTheme } from 'vuetify'
import { useConfigStore } from '@core/stores/config'
// composable function to return the image variant as per the current theme and skin
export const useGenerateImageVariant = (imgLight: string, imgDark: string, imgLightBordered?: string, imgDarkBordered?: string, bordered = false) => {
const configStore = useConfigStore()
const { global } = useTheme()
return computed(() => {
if (global.name.value === 'light') {
if (configStore.skin === 'bordered' && bordered)
return imgLightBordered
else
return imgLight
}
if (global.name.value === 'dark') {
if (configStore.skin === 'bordered' && bordered)
return imgDarkBordered
else
return imgDark
}
// Add a default return statement
return imgLight
})
}

View File

@@ -0,0 +1,29 @@
import type { Ref } from 'vue'
import { useDisplay } from 'vuetify'
export const useResponsiveLeftSidebar = (mobileBreakpoint: Ref<boolean> | undefined = undefined) => {
const { mdAndDown, name: currentBreakpoint } = useDisplay()
const _mobileBreakpoint = mobileBreakpoint || mdAndDown
const isLeftSidebarOpen = ref(true)
const setInitialValue = () => {
isLeftSidebarOpen.value = !_mobileBreakpoint.value
}
// Set the initial value of sidebar
setInitialValue()
watch(
currentBreakpoint,
() => {
// Reset left sidebar
isLeftSidebarOpen.value = !_mobileBreakpoint.value
},
)
return {
isLeftSidebarOpen,
}
}

View File

@@ -0,0 +1,42 @@
import { VThemeProvider } from 'vuetify/components/VThemeProvider'
import { useConfigStore } from '@core/stores/config'
import { AppContentLayoutNav } from '@layouts/enums'
// TODO: Use `VThemeProvider` from dist instead of lib (Using this component from dist causes navbar to loose sticky positioning)
export const useSkins = () => {
const configStore = useConfigStore()
const layoutAttrs = computed(() => ({
verticalNavAttrs: {
wrapper: h(VThemeProvider, { tag: 'div' }),
wrapperProps: {
withBackground: true,
theme: (configStore.isVerticalNavSemiDark && configStore.appContentLayoutNav === AppContentLayoutNav.Vertical)
? 'dark'
: undefined,
},
},
}))
const injectSkinClasses = () => {
if (typeof document !== 'undefined') {
const bodyClasses = document.body.classList
const genSkinClass = (_skin?: string) => `skin--${_skin}`
watch(
() => configStore.skin,
(val, oldVal) => {
bodyClasses.remove(genSkinClass(oldVal))
bodyClasses.add(genSkinClass(val))
},
{ immediate: true },
)
}
}
return {
injectSkinClasses,
layoutAttrs,
}
}