Added grid layout for monthly view
This commit is contained in:
24
package-lock.json
generated
24
package-lock.json
generated
@@ -35,6 +35,7 @@
|
||||
"npm-run-all2": "^6.1.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.0.3",
|
||||
"sass": "^1.72.0",
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "~5.4.0",
|
||||
@@ -3694,6 +3695,12 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
|
||||
"integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -5294,6 +5301,23 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.72.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz",
|
||||
"integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"npm-run-all2": "^6.1.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.0.3",
|
||||
"sass": "^1.72.0",
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "~5.4.0",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { useDark } from '@vueuse/core'
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
const dark = useDark()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/calendar'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { currentConfig, currentDate, getPeriodOfYear } = useCalendar()
|
||||
|
||||
const navbarTitle = computed(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
return `${currentDate.currentMonthName} ${currentDate.currentYear} ${currentDate.currentPeriodAbbr}`
|
||||
|
||||
case 'year':
|
||||
return `Année ${currentDate.currentYear} ${currentDate.currentPeriodAbbr}`
|
||||
|
||||
case 'decade':
|
||||
return `Années ${Number(currentDate.currentYear)} ${getPeriodOfYear(Number(currentDate.currentYear)).short} - ${Number(currentDate.currentYear) + 10} ${getPeriodOfYear(Number(currentDate.currentYear) + 10).short}`
|
||||
|
||||
case 'century':
|
||||
return `Années ${Number(currentDate.currentYear)} ${getPeriodOfYear(Number(currentDate.currentYear)).short} - ${Number(currentDate.currentYear) + 100} ${getPeriodOfYear(Number(currentDate.currentYear) + 100).short}`
|
||||
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<h1 class="text-2xl font-bold">
|
||||
{{ navbarTitle }}
|
||||
</h1>
|
||||
</header>
|
||||
</template>
|
||||
@@ -5,35 +5,30 @@ import Century from './state/Century.vue'
|
||||
import Decade from './state/Decade.vue'
|
||||
import Monthly from './state/Monthly.vue'
|
||||
import Year from './state/Year.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { currentConfig } = useCalendar()
|
||||
|
||||
let currentViewComponent: any
|
||||
|
||||
const currentViewComponent = computed(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
currentViewComponent = Monthly
|
||||
break
|
||||
return Monthly
|
||||
|
||||
case 'year':
|
||||
currentViewComponent = Year
|
||||
break
|
||||
return Year
|
||||
|
||||
case 'decade':
|
||||
currentViewComponent = Decade
|
||||
break
|
||||
return Decade
|
||||
|
||||
case 'century':
|
||||
currentViewComponent = Century
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
return Century
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full grid grid-rows-[auto,1fr]">
|
||||
<CalendarMenu />
|
||||
<component :is="currentViewComponent" />
|
||||
</div>
|
||||
41
src/components/calendar/CalendarMenu.vue
Normal file
41
src/components/calendar/CalendarMenu.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select'
|
||||
import { useCalendar } from '@/stores/calendar'
|
||||
|
||||
const { currentConfig, currentDate, viewTypeOptions } = useCalendar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="py-4 border-slate-700 border-b-[1px]">
|
||||
<div class="container">
|
||||
<div class="grid md:grid-cols-12">
|
||||
<div class="md:col-span-9">
|
||||
<h1 class="text-2xl font-bold">
|
||||
{{ currentDate.currentDateTitle }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="md:col-span-3">
|
||||
<Select v-model="currentConfig.viewType">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Changer le mode d'affichage" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem v-for="(option, index) in viewTypeOptions" :key="index" :value="option">
|
||||
{{ option }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
29
src/components/calendar/CalendarTile.vue
Normal file
29
src/components/calendar/CalendarTile.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{
|
||||
faded?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="tile relative text-xs text-center p-4 border-slate-700"
|
||||
:class="{
|
||||
'text-slate-500': props.faded,
|
||||
'text-slate-300': !props.faded
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
&:not(:nth-child(10n)) {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
&:nth-child(n + 11) {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
src/components/calendar/state/Monthly.vue
Normal file
21
src/components/calendar/state/Monthly.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useCalendar } from '@/stores/calendar'
|
||||
import CalendarTile from '../CalendarTile.vue'
|
||||
|
||||
const { staticConfig } = useCalendar()
|
||||
|
||||
const daysPerMonth = computed(() => staticConfig.daysPerMonth)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid" :class="`grid-cols-10`">
|
||||
<CalendarTile v-for="day in daysPerMonth" :key="day">
|
||||
<span class="font-bold">{{ day }}</span>
|
||||
</CalendarTile>
|
||||
|
||||
<CalendarTile v-for="nextMonthDay in 8" :key="nextMonthDay" faded>
|
||||
<span>{{ nextMonthDay }}</span>
|
||||
</CalendarTile>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div>Monthly</div>
|
||||
</template>
|
||||
26
src/components/ui/button/Button.vue
Normal file
26
src/components/ui/button/Button.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { Primitive, type PrimitiveProps } from 'radix-vue'
|
||||
import { type ButtonVariants, buttonVariants } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants['variant']
|
||||
size?: ButtonVariants['size']
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: 'button',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
34
src/components/ui/button/index.ts
Normal file
34
src/components/ui/button/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
|
||||
export { default as Button } from './Button.vue'
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||
15
src/components/ui/select/Select.vue
Normal file
15
src/components/ui/select/Select.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectRootEmits, SelectRootProps } from 'radix-vue'
|
||||
import { SelectRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectRootProps>()
|
||||
const emits = defineEmits<SelectRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</SelectRoot>
|
||||
</template>
|
||||
53
src/components/ui/select/SelectContent.vue
Normal file
53
src/components/ui/select/SelectContent.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
SelectContent,
|
||||
type SelectContentEmits,
|
||||
type SelectContentProps,
|
||||
SelectPortal,
|
||||
SelectViewport,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
import { SelectScrollDownButton, SelectScrollUpButton } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<SelectContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
position: 'popper',
|
||||
},
|
||||
)
|
||||
const emits = defineEmits<SelectContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectPortal>
|
||||
<SelectContent
|
||||
v-bind="{ ...forwarded, ...$attrs }" :class="cn(
|
||||
'relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectViewport :class="cn('p-1', position === 'popper' && 'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]')">
|
||||
<slot />
|
||||
</SelectViewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectContent>
|
||||
</SelectPortal>
|
||||
</template>
|
||||
19
src/components/ui/select/SelectGroup.vue
Normal file
19
src/components/ui/select/SelectGroup.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { SelectGroup, type SelectGroupProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectGroupProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectGroup :class="cn('p-1 w-full', props.class)" v-bind="delegatedProps">
|
||||
<slot />
|
||||
</SelectGroup>
|
||||
</template>
|
||||
44
src/components/ui/select/SelectItem.vue
Normal file
44
src/components/ui/select/SelectItem.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
SelectItem,
|
||||
SelectItemIndicator,
|
||||
type SelectItemProps,
|
||||
SelectItemText,
|
||||
useForwardProps,
|
||||
} from 'radix-vue'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectItemIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
</SelectItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectItemText>
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</template>
|
||||
11
src/components/ui/select/SelectItemText.vue
Normal file
11
src/components/ui/select/SelectItemText.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { SelectItemText, type SelectItemTextProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectItemTextProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItemText v-bind="props">
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</template>
|
||||
13
src/components/ui/select/SelectLabel.vue
Normal file
13
src/components/ui/select/SelectLabel.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { SelectLabel, type SelectLabelProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectLabel :class="cn('py-1.5 pl-8 pr-2 text-sm font-semibold', props.class)">
|
||||
<slot />
|
||||
</SelectLabel>
|
||||
</template>
|
||||
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { SelectScrollDownButton, type SelectScrollDownButtonProps, useForwardProps } from 'radix-vue'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollDownButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<slot>
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
</slot>
|
||||
</SelectScrollDownButton>
|
||||
</template>
|
||||
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { SelectScrollUpButton, type SelectScrollUpButtonProps, useForwardProps } from 'radix-vue'
|
||||
import { ChevronUp } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollUpButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<slot>
|
||||
<ChevronUp class="h-4 w-4" />
|
||||
</slot>
|
||||
</SelectScrollUpButton>
|
||||
</template>
|
||||
17
src/components/ui/select/SelectSeparator.vue
Normal file
17
src/components/ui/select/SelectSeparator.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { SelectSeparator, type SelectSeparatorProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
|
||||
</template>
|
||||
31
src/components/ui/select/SelectTrigger.vue
Normal file
31
src/components/ui/select/SelectTrigger.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { SelectIcon, SelectTrigger, type SelectTriggerProps, useForwardProps } from 'radix-vue'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<SelectTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectTrigger
|
||||
v-bind="forwardedProps"
|
||||
:class="cn(
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
<SelectIcon as-child>
|
||||
<ChevronDown class="w-4 h-4 opacity-50" />
|
||||
</SelectIcon>
|
||||
</SelectTrigger>
|
||||
</template>
|
||||
11
src/components/ui/select/SelectValue.vue
Normal file
11
src/components/ui/select/SelectValue.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { SelectValue, type SelectValueProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectValueProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectValue v-bind="props">
|
||||
<slot />
|
||||
</SelectValue>
|
||||
</template>
|
||||
11
src/components/ui/select/index.ts
Normal file
11
src/components/ui/select/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { default as Select } from './Select.vue'
|
||||
export { default as SelectValue } from './SelectValue.vue'
|
||||
export { default as SelectTrigger } from './SelectTrigger.vue'
|
||||
export { default as SelectContent } from './SelectContent.vue'
|
||||
export { default as SelectGroup } from './SelectGroup.vue'
|
||||
export { default as SelectItem } from './SelectItem.vue'
|
||||
export { default as SelectItemText } from './SelectItemText.vue'
|
||||
export { default as SelectLabel } from './SelectLabel.vue'
|
||||
export { default as SelectSeparator } from './SelectSeparator.vue'
|
||||
export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue'
|
||||
export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue'
|
||||
@@ -1,5 +1,4 @@
|
||||
import './assets/main.css'
|
||||
import './assets/theme.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
@@ -24,6 +24,7 @@ type CalendarCurrentDate = {
|
||||
currentYear: ComputedRef<string | string[]>
|
||||
currentPeriod: Ref<LeimPeriod>
|
||||
currentPeriodAbbr: Ref<LeimPeriodShort>
|
||||
currentDateTitle: ComputedRef<string>
|
||||
}
|
||||
|
||||
export const useCalendar = defineStore('calendar', () => {
|
||||
@@ -63,6 +64,12 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
const currentConfig: Ref<CalendarCurrentConfig> = ref({
|
||||
viewType: 'month'
|
||||
})
|
||||
const viewTypeOptions: Set<CalendarViewType> = new Set<CalendarViewType>([
|
||||
'month',
|
||||
'year',
|
||||
'decade',
|
||||
'century'
|
||||
])
|
||||
|
||||
// Get date from URL params
|
||||
const params = useUrlSearchParams('history', {
|
||||
@@ -105,6 +112,25 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
return currentPeriod.value === 'ante' ? 'A.R' : 'N.R'
|
||||
})
|
||||
|
||||
const currentDateTitle = computed(() => {
|
||||
switch (currentConfig.value.viewType) {
|
||||
case 'month':
|
||||
return `${currentMonthName.value} ${currentYear.value} ${currentPeriodAbbr.value}`
|
||||
|
||||
case 'year':
|
||||
return `Année ${currentYear.value} ${currentPeriodAbbr.value}`
|
||||
|
||||
case 'decade':
|
||||
return `Années ${Number(currentYear.value)} ${getPeriodOfYear(Number(currentYear.value)).short} - ${Number(currentYear.value) + 10} ${getPeriodOfYear(Number(currentYear.value) + 10).short}`
|
||||
|
||||
case 'century':
|
||||
return `Années ${Number(currentYear.value)} ${getPeriodOfYear(Number(currentYear.value)).short} - ${Number(currentYear.value) + 100} ${getPeriodOfYear(Number(currentYear.value) + 100).short}`
|
||||
|
||||
default:
|
||||
return 'Date inconnue'
|
||||
}
|
||||
})
|
||||
|
||||
// Create base config
|
||||
const currentDate: CalendarCurrentDate = {
|
||||
currentDay,
|
||||
@@ -112,7 +138,8 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
currentMonthName,
|
||||
currentYear,
|
||||
currentPeriod,
|
||||
currentPeriodAbbr
|
||||
currentPeriodAbbr,
|
||||
currentDateTitle
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,6 +225,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
|
||||
return {
|
||||
staticConfig,
|
||||
viewTypeOptions,
|
||||
currentConfig,
|
||||
currentDate,
|
||||
params,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import Calendar from '@/components/Calendar.vue'
|
||||
import Calendar from '@/components/calendar/Calendar.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="container h-full grid items-center">
|
||||
<main class="h-full">
|
||||
<Calendar />
|
||||
<!-- <pre>
|
||||
{{ currentDate }}
|
||||
|
||||
Reference in New Issue
Block a user