Added grid layout for monthly view

This commit is contained in:
Alexis
2024-04-01 15:05:08 +02:00
parent abc4d908f1
commit e7d3fd3fd3
30 changed files with 498 additions and 146 deletions

24
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -1,5 +1,8 @@
<script setup lang="ts">
import { useDark } from '@vueuse/core'
import { RouterView } from 'vue-router'
const dark = useDark()
</script>
<template>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>

View 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>

View File

@@ -1,3 +0,0 @@
<template>
<div>Monthly</div>
</template>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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'

View File

@@ -1,5 +1,4 @@
import './assets/main.css'
import './assets/theme.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'

View File

@@ -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,

View File

@@ -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 }}