From dfd9a4846fdd14087c01596a47f99e097a657f17 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 04:38:52 +0100 Subject: [PATCH 1/9] :recycle: refactoring components --- .../modules/authentication/authentication.module.ts | 2 -- src/app/modules/dashboard/dashboard.module.ts | 13 +++---------- src/app/modules/design/design.module.ts | 12 ++++++++++++ src/app/modules/user/interfaces/user.interface.ts | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 src/app/modules/design/design.module.ts diff --git a/src/app/modules/authentication/authentication.module.ts b/src/app/modules/authentication/authentication.module.ts index de22ff4..40acc9f 100644 --- a/src/app/modules/authentication/authentication.module.ts +++ b/src/app/modules/authentication/authentication.module.ts @@ -27,7 +27,5 @@ import { AuthEffects } from './store/auth.effects'; SharedModule, StoreModule.forFeature(authFeatureKey, authReducer), ], - exports: [], - providers: [], }) export class AuthenticationModule {} diff --git a/src/app/modules/dashboard/dashboard.module.ts b/src/app/modules/dashboard/dashboard.module.ts index b00f7f6..214e4cb 100644 --- a/src/app/modules/dashboard/dashboard.module.ts +++ b/src/app/modules/dashboard/dashboard.module.ts @@ -7,14 +7,7 @@ import { DashboardComponent } from './pages/dashboard/dashboard.component'; import { dashboardRoutes } from './routes/dashboard.routes'; @NgModule({ - declarations: [ - DashboardComponent - ], - imports: [ - SharedModule, - RouterModule.forChild(dashboardRoutes), - ], - exports: [], - providers: [], + declarations: [DashboardComponent], + imports: [SharedModule, RouterModule.forChild(dashboardRoutes)], }) -export class DashboardModule { } +export class DashboardModule {} diff --git a/src/app/modules/design/design.module.ts b/src/app/modules/design/design.module.ts new file mode 100644 index 0000000..fbfa64f --- /dev/null +++ b/src/app/modules/design/design.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule + ] +}) +export class DesignModule { } diff --git a/src/app/modules/user/interfaces/user.interface.ts b/src/app/modules/user/interfaces/user.interface.ts index 485103c..b68decd 100644 --- a/src/app/modules/user/interfaces/user.interface.ts +++ b/src/app/modules/user/interfaces/user.interface.ts @@ -20,7 +20,7 @@ export interface AuthResponse { export interface FromDate { date: Date; - timezone_type: number; + timezoneType: number; timezone: string; } From a244979c1fcef0841e4e1b3dcb8305c2bca2beab Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 05:04:14 +0100 Subject: [PATCH 2/9] update seo service --- src/app/shared/services/seo.service.ts | 178 +++++++++++++------------ 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/src/app/shared/services/seo.service.ts b/src/app/shared/services/seo.service.ts index d72c27a..472d7e0 100644 --- a/src/app/shared/services/seo.service.ts +++ b/src/app/shared/services/seo.service.ts @@ -28,97 +28,101 @@ export class SeoService implements OnDestroy { } private updateTitle(url: string): void { - this.title.setTitle(meta[url].title); + if (meta[url]) { + this.title.setTitle(meta[url].title); + } } private updateMeta(url: string): void { - const oldTagOgTitle = this.meta.getTag('property="og:title"'); - const newTagOgTitle = { - property: 'og:title', - content: meta[url].title, - }; - const oldTagTwitterTitle = this.meta.getTag('name="twitter:title"'); - const newTagTwitterTitle = { - name: 'twitter:title', - content: meta[url].title, - }; - const oldTagDescription = this.meta.getTag('name="description"'); - const newTagDescription = { - name: 'description', - content: meta[url].description, - }; - const oldTagOgDescription = this.meta.getTag('property="og:description"'); - const newTagOgDescription = { - property: 'og:description', - content: meta[url].description, - }; - const oldTagTwitterDescription = this.meta.getTag( - 'property="og:description"' - ); - const newTagTwitterDescription = { - property: 'og:description', - content: meta[url].description, - }; - const oldTagOgImage = this.meta.getTag('property="og:image"'); - const imageTag = - meta[url].metaTags?.['image'] ?? - this.meta.getTag('property="og:image"')!.content; - const newTagOgImage = { - property: 'og:image', - content: imageTag, - }; - const oldTagTwitterImage = this.meta.getTag('name="twitter:image"'); - const newTagTwitterImage = { - name: 'twitter:image', - content: imageTag, - }; - const oldTagOgUrl = this.meta.getTag('property="og:url"'); - const newTagOgUrl = { - property: 'og:url', - content: meta[url].metaTags?.['og:url'], - }; - const oldTagKeywords = this.meta.getTag('name="keywords"'); - const newTagKeywords = { - name: 'keywords', - content: meta[url].keywords, - }; + if (meta[url]) { + const oldTagOgTitle = this.meta.getTag('property="og:title"'); + const newTagOgTitle = { + property: 'og:title', + content: meta[url].title, + }; + const oldTagTwitterTitle = this.meta.getTag('name="twitter:title"'); + const newTagTwitterTitle = { + name: 'twitter:title', + content: meta[url].title, + }; + const oldTagDescription = this.meta.getTag('name="description"'); + const newTagDescription = { + name: 'description', + content: meta[url].description, + }; + const oldTagOgDescription = this.meta.getTag('property="og:description"'); + const newTagOgDescription = { + property: 'og:description', + content: meta[url].description, + }; + const oldTagTwitterDescription = this.meta.getTag( + 'property="og:description"' + ); + const newTagTwitterDescription = { + property: 'og:description', + content: meta[url].description, + }; + const oldTagOgImage = this.meta.getTag('property="og:image"'); + const imageTag = + meta[url].metaTags?.['image'] ?? + this.meta.getTag('property="og:image"')!.content; + const newTagOgImage = { + property: 'og:image', + content: imageTag, + }; + const oldTagTwitterImage = this.meta.getTag('name="twitter:image"'); + const newTagTwitterImage = { + name: 'twitter:image', + content: imageTag, + }; + const oldTagOgUrl = this.meta.getTag('property="og:url"'); + const newTagOgUrl = { + property: 'og:url', + content: meta[url].metaTags?.['og:url'], + }; + const oldTagKeywords = this.meta.getTag('name="keywords"'); + const newTagKeywords = { + name: 'keywords', + content: meta[url].keywords, + }; - // Update description - oldTagDescription - ? this.meta.updateTag(newTagDescription as MetaDefinition) - : this.meta.addTag(newTagDescription as MetaDefinition); - // Update og:description - oldTagOgDescription - ? this.meta.updateTag(newTagOgDescription as MetaDefinition) - : this.meta.addTag(newTagOgDescription as MetaDefinition); - // Update twitter:description - oldTagTwitterDescription - ? this.meta.updateTag(newTagTwitterDescription as MetaDefinition) - : this.meta.addTag(newTagTwitterDescription as MetaDefinition); - // Update og:title - oldTagOgTitle - ? this.meta.updateTag(newTagOgTitle as MetaDefinition) - : this.meta.addTag(newTagOgTitle as MetaDefinition); - // Update twitter:title - oldTagTwitterTitle - ? this.meta.updateTag(newTagTwitterTitle as MetaDefinition) - : this.meta.addTag(newTagTwitterTitle as MetaDefinition); - // Update og:image - oldTagOgImage - ? this.meta.updateTag(newTagOgImage as MetaDefinition) - : this.meta.addTag(newTagOgImage as MetaDefinition); - // Update twitter:image - oldTagTwitterImage - ? this.meta.updateTag(newTagTwitterImage as MetaDefinition) - : this.meta.addTag(newTagTwitterImage as MetaDefinition); - // Update og:url - oldTagOgUrl - ? this.meta.updateTag(newTagOgUrl as MetaDefinition) - : this.meta.addTag(newTagOgUrl as MetaDefinition); - // Update keywords - oldTagKeywords - ? this.meta.updateTag(newTagKeywords as MetaDefinition) - : this.meta.addTag(newTagKeywords as MetaDefinition); + // Update description + oldTagDescription + ? this.meta.updateTag(newTagDescription as MetaDefinition) + : this.meta.addTag(newTagDescription as MetaDefinition); + // Update og:description + oldTagOgDescription + ? this.meta.updateTag(newTagOgDescription as MetaDefinition) + : this.meta.addTag(newTagOgDescription as MetaDefinition); + // Update twitter:description + oldTagTwitterDescription + ? this.meta.updateTag(newTagTwitterDescription as MetaDefinition) + : this.meta.addTag(newTagTwitterDescription as MetaDefinition); + // Update og:title + oldTagOgTitle + ? this.meta.updateTag(newTagOgTitle as MetaDefinition) + : this.meta.addTag(newTagOgTitle as MetaDefinition); + // Update twitter:title + oldTagTwitterTitle + ? this.meta.updateTag(newTagTwitterTitle as MetaDefinition) + : this.meta.addTag(newTagTwitterTitle as MetaDefinition); + // Update og:image + oldTagOgImage + ? this.meta.updateTag(newTagOgImage as MetaDefinition) + : this.meta.addTag(newTagOgImage as MetaDefinition); + // Update twitter:image + oldTagTwitterImage + ? this.meta.updateTag(newTagTwitterImage as MetaDefinition) + : this.meta.addTag(newTagTwitterImage as MetaDefinition); + // Update og:url + oldTagOgUrl + ? this.meta.updateTag(newTagOgUrl as MetaDefinition) + : this.meta.addTag(newTagOgUrl as MetaDefinition); + // Update keywords + oldTagKeywords + ? this.meta.updateTag(newTagKeywords as MetaDefinition) + : this.meta.addTag(newTagKeywords as MetaDefinition); + } } ngOnDestroy() { From f87f1938d9f7f814f2bf42fd47e6f6530dbb9ad6 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 05:05:05 +0100 Subject: [PATCH 3/9] setup design module --- src/app/core/routes/app.routing.ts | 5 ++++ src/app/modules/design/design.module.ts | 14 ++++----- .../pages/calendar/calendar.component.html | 1 + .../pages/calendar/calendar.component.scss | 0 .../pages/calendar/calendar.component.ts | 9 ++++++ .../modules/design/routes/design.routes.ts | 30 +++++++++++++++++++ 6 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 src/app/modules/design/pages/calendar/calendar.component.html create mode 100644 src/app/modules/design/pages/calendar/calendar.component.scss create mode 100644 src/app/modules/design/pages/calendar/calendar.component.ts create mode 100644 src/app/modules/design/routes/design.routes.ts diff --git a/src/app/core/routes/app.routing.ts b/src/app/core/routes/app.routing.ts index 8d72471..dca0241 100644 --- a/src/app/core/routes/app.routing.ts +++ b/src/app/core/routes/app.routing.ts @@ -18,5 +18,10 @@ export const ROUTES: Routes = [ m => m.DashboardModule ), }, + { + path: 'components', + loadChildren: () => + import('@modules/design/design.module').then(m => m.DesignModule), + }, { path: '**', pathMatch: 'full', component: NotFoundComponent }, ]; diff --git a/src/app/modules/design/design.module.ts b/src/app/modules/design/design.module.ts index fbfa64f..287b712 100644 --- a/src/app/modules/design/design.module.ts +++ b/src/app/modules/design/design.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - +import { RouterModule } from '@angular/router'; +import { SharedModule } from '@app/shared/shared.module'; +import { designRoutes } from './routes/design.routes'; +import { CalendarComponent } from './pages/calendar/calendar.component'; @NgModule({ - declarations: [], - imports: [ - CommonModule - ] + declarations: [CalendarComponent], + imports: [RouterModule.forChild(designRoutes), SharedModule], }) -export class DesignModule { } +export class DesignModule {} diff --git a/src/app/modules/design/pages/calendar/calendar.component.html b/src/app/modules/design/pages/calendar/calendar.component.html new file mode 100644 index 0000000..6d8082c --- /dev/null +++ b/src/app/modules/design/pages/calendar/calendar.component.html @@ -0,0 +1 @@ +<p>calendar works!</p> diff --git a/src/app/modules/design/pages/calendar/calendar.component.scss b/src/app/modules/design/pages/calendar/calendar.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modules/design/pages/calendar/calendar.component.ts b/src/app/modules/design/pages/calendar/calendar.component.ts new file mode 100644 index 0000000..98a05c8 --- /dev/null +++ b/src/app/modules/design/pages/calendar/calendar.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './calendar.component.html', + styleUrls: ['./calendar.component.scss'], +}) +export class CalendarComponent { + constructor() {} +} diff --git a/src/app/modules/design/routes/design.routes.ts b/src/app/modules/design/routes/design.routes.ts new file mode 100644 index 0000000..189fb32 --- /dev/null +++ b/src/app/modules/design/routes/design.routes.ts @@ -0,0 +1,30 @@ +import { Routes } from '@angular/router'; +import { NgxPermissionsGuard } from 'ngx-permissions'; + +import { AuthGuard } from '@app/core/guards/auth.guard'; +import { RoleGuard } from '@app/core/guards/role.guard'; +import { ADMIN_ROLE } from '@app/core/guards/user.roles'; + +import { CpanelComponent } from '@app/shared/themes/layouts/cpanel/cpanel.component'; +import { CalendarComponent } from '../pages/calendar/calendar.component'; + +export const designRoutes: Routes = [ + { + path: '', + canActivate: [AuthGuard, RoleGuard], + component: CpanelComponent, + children: [ + { path: '', redirectTo: 'calendar', pathMatch: 'full' }, + { + path: 'calendar', + component: CalendarComponent, + canActivate: [NgxPermissionsGuard], + data: { + permissions: { + only: [ADMIN_ROLE], + }, + }, + }, + ], + }, +]; From 5accddaa04592a8d47659676dee6e03baad73886 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 05:06:00 +0100 Subject: [PATCH 4/9] refcatoring --- src/app/modules/dashboard/dashboard.module.ts | 2 +- .../shared/components/calendar/calendar.module.ts | 12 ++++++++++++ src/app/shared/shared.module.ts | 6 ++++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 src/app/shared/components/calendar/calendar.module.ts diff --git a/src/app/modules/dashboard/dashboard.module.ts b/src/app/modules/dashboard/dashboard.module.ts index 214e4cb..1551ee5 100644 --- a/src/app/modules/dashboard/dashboard.module.ts +++ b/src/app/modules/dashboard/dashboard.module.ts @@ -8,6 +8,6 @@ import { dashboardRoutes } from './routes/dashboard.routes'; @NgModule({ declarations: [DashboardComponent], - imports: [SharedModule, RouterModule.forChild(dashboardRoutes)], + imports: [RouterModule.forChild(dashboardRoutes), SharedModule], }) export class DashboardModule {} diff --git a/src/app/shared/components/calendar/calendar.module.ts b/src/app/shared/components/calendar/calendar.module.ts new file mode 100644 index 0000000..ee453f2 --- /dev/null +++ b/src/app/shared/components/calendar/calendar.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule + ] +}) +export class CalendarModule { } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 3f90227..9576144 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -9,6 +9,7 @@ import { HeadingModule } from './components/headings/heading.module'; import { SkeletonModule } from './components/skeletons/skeleton.module'; import { SnippetModule } from './components/snippets/snippet.module'; import { TextareaModule } from './components/textarea/textarea.module'; +import { CalendarModule } from './components/calendar/calendar.module'; import { ClickOutsideDirective } from './directives/click-outside.directive'; import { StatusColorPipe } from './pipes/status-color.pipe'; @@ -17,13 +18,14 @@ import { StatusValuePipe } from './pipes/status-value.pipe'; const DECLARATIONS = [ClickOutsideDirective, StatusColorPipe, StatusValuePipe]; const MODULES = [ AlertModule, - ThemeModule, ButtonModule, - InputsModule, + CalendarModule, HeadingModule, + InputsModule, SkeletonModule, SnippetModule, TextareaModule, + ThemeModule, ]; @NgModule({ From e6fdb11b23b4910b4ef75ba35a9272a15d5fa5f4 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 05:06:14 +0100 Subject: [PATCH 5/9] :lipstick: Add components menu to sidebar --- src/app/modules/dashboard/navigation/admin.menu.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/app/modules/dashboard/navigation/admin.menu.ts b/src/app/modules/dashboard/navigation/admin.menu.ts index 9918ea2..9fe689d 100644 --- a/src/app/modules/dashboard/navigation/admin.menu.ts +++ b/src/app/modules/dashboard/navigation/admin.menu.ts @@ -45,6 +45,19 @@ export const adminMenu: Menu[] = [ }, ], }, + { + group: $localize`Composants`, + items: [ + { + title: $localize`Calendrier`, + svgPath: [ + 'M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z', + ], + link: '/components/calendar', + roles: [], + }, + ], + }, { group: $localize`Opérations`, items: [ From ede8a089e01c7a2ebec37790c0c4f51b7865dcaf Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 06:57:58 +0100 Subject: [PATCH 6/9] wip --- .../pages/calendar/calendar.component.html | 6 +- .../components/calendar/calendar.module.ts | 13 +- .../components/calendar/calendar.service.ts | 116 ++++++++++++++ .../calendar/calendar/calendar.component.html | 22 +++ .../calendar/calendar/calendar.component.scss | 0 .../calendar/calendar/calendar.component.ts | 57 +++++++ .../shared/components/calendar/interfaces.ts | 149 ++++++++++++++++++ 7 files changed, 355 insertions(+), 8 deletions(-) create mode 100644 src/app/shared/components/calendar/calendar.service.ts create mode 100644 src/app/shared/components/calendar/calendar/calendar.component.html create mode 100644 src/app/shared/components/calendar/calendar/calendar.component.scss create mode 100644 src/app/shared/components/calendar/calendar/calendar.component.ts create mode 100644 src/app/shared/components/calendar/interfaces.ts diff --git a/src/app/modules/design/pages/calendar/calendar.component.html b/src/app/modules/design/pages/calendar/calendar.component.html index 6d8082c..77de52a 100644 --- a/src/app/modules/design/pages/calendar/calendar.component.html +++ b/src/app/modules/design/pages/calendar/calendar.component.html @@ -1 +1,5 @@ -<p>calendar works!</p> +<h2>Calendrier</h2> + +<div class="mt-10"> + <tw-calendar></tw-calendar> +</div> diff --git a/src/app/shared/components/calendar/calendar.module.ts b/src/app/shared/components/calendar/calendar.module.ts index ee453f2..217ea2d 100644 --- a/src/app/shared/components/calendar/calendar.module.ts +++ b/src/app/shared/components/calendar/calendar.module.ts @@ -1,12 +1,11 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; - +import { CalendarComponent } from './calendar/calendar.component'; @NgModule({ - declarations: [], - imports: [ - CommonModule - ] + declarations: [CalendarComponent], + imports: [CommonModule], + exports: [CalendarComponent], }) -export class CalendarModule { } +export class CalendarModule {} diff --git a/src/app/shared/components/calendar/calendar.service.ts b/src/app/shared/components/calendar/calendar.service.ts new file mode 100644 index 0000000..0a3deb5 --- /dev/null +++ b/src/app/shared/components/calendar/calendar.service.ts @@ -0,0 +1,116 @@ +import { Injectable } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +import { + ICalendarComponent, + IView, + CalendarMode, + QueryMode, +} from './interfaces'; + +@Injectable() +export class CalendarService { + queryMode!: QueryMode; + currentDateChangedFromParent$!: Observable<Date>; + currentDateChangedFromChildren$!: Observable<Date>; + eventSourceChanged$!: Observable<void>; + + private _currentDate!: Date; + private currentDateChangedFromParent = new Subject<Date>(); + private currentDateChangedFromChildren = new Subject<Date>(); + private eventSourceChanged = new Subject<void>(); + + constructor() { + this.currentDateChangedFromParent$ = + this.currentDateChangedFromParent.asObservable(); + this.currentDateChangedFromChildren$ = + this.currentDateChangedFromChildren.asObservable(); + this.eventSourceChanged$ = this.eventSourceChanged.asObservable(); + } + + setCurrentDate(val: Date, fromParent: boolean = false) { + this._currentDate = val; + if (fromParent) { + this.currentDateChangedFromParent.next(val); + } else { + this.currentDateChangedFromChildren.next(val); + } + } + + get currentDate(): Date { + return this._currentDate; + } + + rangeChanged(component: ICalendarComponent) { + if (this.queryMode === 'local') { + if (component.eventSource && component.onDataLoaded) { + component.onDataLoaded(); + } + } else if (this.queryMode === 'remote') { + component.onRangeChanged.emit(component.range); + } + } + + private getStep(mode: CalendarMode): { + years: number; + months: number; + days: number; + } { + switch (mode) { + case 'month': + return { + years: 0, + months: 1, + days: 0, + }; + case 'week': + return { + years: 0, + months: 0, + days: 7, + }; + case 'day': + return { + years: 0, + months: 0, + days: 1, + }; + } + } + + getAdjacentCalendarDate(mode: CalendarMode, direction: number): Date { + let calculateCalendarDate = new Date(this.currentDate.getTime()); + const step = this.getStep(mode), + year = calculateCalendarDate.getFullYear() + direction * step.years, + month = calculateCalendarDate.getMonth() + direction * step.months, + date = calculateCalendarDate.getDate() + direction * step.days; + + calculateCalendarDate.setFullYear(year, month, date); + + if (mode === 'month') { + const firstDayInNextMonth = new Date(year, month + 1, 1); + if (firstDayInNextMonth.getTime() <= calculateCalendarDate.getTime()) { + calculateCalendarDate = new Date( + firstDayInNextMonth.getTime() - 24 * 60 * 60 * 1000 + ); + } + } + return calculateCalendarDate; + } + + getAdjacentViewStartTime( + component: ICalendarComponent, + direction: number + ): Date { + const adjacentCalendarDate = this.getAdjacentCalendarDate( + component.mode, + direction + ); + + return component.getRange(adjacentCalendarDate).startTime; + } + + loadEvents() { + this.eventSourceChanged.next(); + } +} diff --git a/src/app/shared/components/calendar/calendar/calendar.component.html b/src/app/shared/components/calendar/calendar/calendar.component.html new file mode 100644 index 0000000..174325f --- /dev/null +++ b/src/app/shared/components/calendar/calendar/calendar.component.html @@ -0,0 +1,22 @@ +<ng-template #monthviewDefaultDisplayEventTemplate let-view="view" let-row="row" let-col="col"> + {{ view.dates[row * 7 + col].label }} +</ng-template> +<ng-template + #monthviewDefaultEventDetailTemplate + let-showEventDetail="showEventDetail" + let-selectedDate="selectedDate" let-noEventsLabel="noEventsLabel"> + <div class="event-detail-container" has-bouncing="false" *ngIf="showEventDetail" overflow-scroll="false"> + <div class="event-detail-item" *ngFor="let event of selectedDate?.events" (click)="eventSelected(event)"> + <span *ngIf="!event.allDay" class="monthview-eventdetail-timecolumn"> + {{event.startTime | date: 'HH:mm'}} + - + {{event.endTime | date: 'HH:mm'}} + </span> + <span *ngIf="event.allDay" class="monthview-eventdetail-timecolumn">{{ allDayLabel }}</span> + <span class="event-detail"> | {{ event.title }}</span> + </div> + <div class="event-detail-item" *ngIf="selectedDate?.events.length === 0"> + <div class="no-events-label">{{ noEventsLabel }}</div> + </div> + </div> +</ng-template> \ No newline at end of file diff --git a/src/app/shared/components/calendar/calendar/calendar.component.scss b/src/app/shared/components/calendar/calendar/calendar.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/calendar/calendar/calendar.component.ts b/src/app/shared/components/calendar/calendar/calendar.component.ts new file mode 100644 index 0000000..1d994c6 --- /dev/null +++ b/src/app/shared/components/calendar/calendar/calendar.component.ts @@ -0,0 +1,57 @@ +import { + Component, + EventEmitter, + Input, + OnInit, + Output, + TemplateRef, + Inject, + LOCALE_ID, +} from '@angular/core'; +import { Subscription } from 'rxjs'; + +import { IEvent, IRange, ITimeSelected } from '../interfaces'; +import { CalendarService } from '../calendar.service'; + +@Component({ + selector: 'tw-calendar', + templateUrl: './calendar.component.html', + styleUrls: ['./calendar.component.scss'], + providers: [CalendarService], +}) +export class CalendarComponent { + @Input() + get currentDate(): Date { + return this._currentDate; + } + + set currentDate(date: Date) { + if (!date) { + date = new Date(); + } + + this._currentDate = date; + this.calendarService.setCurrentDate(date, true); + this.onCurrentDateChanged.emit(this._currentDate); + } + + @Input() locale = ''; + + @Output() onCurrentDateChanged = new EventEmitter<Date>(); + @Output() onRangeChanged = new EventEmitter<IRange>(); + @Output() onEventSelected = new EventEmitter<IEvent>(); + @Output() onTimeSelected = new EventEmitter<ITimeSelected>(); + @Output() onTitleChanged = new EventEmitter<string>(); + + private _currentDate!: Date; + private hourParts = 1; + private hourSegments = 1; + private currentDateChangedFromChildrenSubscription!: Subscription; + + constructor( + private calendarService: CalendarService, + @Inject(LOCALE_ID) private appLocale: string + ) { + this.locale = appLocale; + } +} diff --git a/src/app/shared/components/calendar/interfaces.ts b/src/app/shared/components/calendar/interfaces.ts new file mode 100644 index 0000000..6181329 --- /dev/null +++ b/src/app/shared/components/calendar/interfaces.ts @@ -0,0 +1,149 @@ +import { EventEmitter, TemplateRef } from '@angular/core'; + +export interface IEvent { + allDay: boolean; + endTime: Date; + startTime: Date; + title: string; +} + +export interface IRange { + startTime: Date; + endTime: Date; +} + +export interface IView {} + +export interface IDayView extends IView { + allDayEvents: IDisplayAllDayEvent[]; + rows: IDayViewRow[]; +} + +export interface IDayViewRow { + events: IDisplayEvent[]; + time: Date; +} + +export interface IMonthView extends IView { + dates: IMonthViewRow[]; + dayHeaders: string[]; +} + +export interface IMonthViewRow { + current?: boolean; + date: Date; + events: IEvent[]; + hasEvent?: boolean; + label: string; + secondary: boolean; + selected?: boolean; + disabled: boolean; +} + +export interface IWeekView extends IView { + dates: IWeekViewDateRow[]; + rows: IWeekViewRow[][]; +} + +export interface IWeekViewDateRow { + current?: boolean; + date: Date; + events: IDisplayEvent[]; + hasEvent?: boolean; + selected?: boolean; + dayHeader: string; +} + +export interface IWeekViewRow { + events: IDisplayEvent[]; + time: Date; +} + +export interface IDisplayEvent { + endIndex: number; + endOffset?: number; + event: IEvent; + startIndex: number; + startOffset?: number; + overlapNumber?: number; + position?: number; +} + +export interface IDisplayWeekViewHeader { + viewDate: IWeekViewDateRow; +} + +export interface IDisplayAllDayEvent { + event: IEvent; +} + +export interface ICalendarComponent { + currentViewIndex: number; + direction: number; + eventSource: IEvent[]; + getRange: (date: Date) => IRange; + getViewData: (date: Date) => IView; + mode: CalendarMode; + range: IRange; + view: IView; + onDataLoaded: () => void; + onRangeChanged: EventEmitter<IRange>; +} + +export interface ITimeSelected { + events: IEvent[]; + selectedTime: Date; + disabled: boolean; +} + +export interface IMonthViewDisplayEventTemplateContext { + view: IView; + row: number; + col: number; +} + +export interface IMonthViewEventDetailTemplateContext { + selectedDate: ITimeSelected; + noEventsLabel: string; +} + +export interface IWeekViewAllDayEventSectionTemplateContext { + day: IWeekViewDateRow; + eventTemplate: TemplateRef<IDisplayAllDayEvent>; +} + +export interface IWeekViewNormalEventSectionTemplateContext { + tm: IWeekViewRow; + eventTemplate: TemplateRef<IDisplayEvent>; +} + +export interface IDayViewAllDayEventSectionTemplateContext { + alldayEvents: IDisplayAllDayEvent[]; + eventTemplate: TemplateRef<IDisplayAllDayEvent>; +} + +export interface IDayViewNormalEventSectionTemplateContext { + tm: IDayViewRow; + eventTemplate: TemplateRef<IDisplayEvent>; +} + +export interface IDateFormatter { + formatMonthViewDay?: (date: Date) => string; + formatMonthViewDayHeader?: (date: Date) => string; + formatMonthViewTitle?: (date: Date) => string; + formatWeekViewDayHeader?: (date: Date) => string; + formatWeekViewTitle?: (date: Date) => string; + formatWeekViewHourColumn?: (date: Date) => string; + formatDayViewTitle?: (date: Date) => string; + formatDayViewHourColumn?: (date: Date) => string; +} + +export type CalendarMode = 'day' | 'month' | 'week'; + +export type QueryMode = 'local' | 'remote'; + +export enum Step { + QuarterHour = 15, + HalfHour = 30, + Hour = 60, +} From b1b190d7f0d7dc5ff14f19a755d27b4ecafc2e70 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 08:13:22 +0100 Subject: [PATCH 7/9] :construction: wip --- .../components/calendar/calendar.module.ts | 3 +- .../calendar/calendar/calendar.component.html | 47 +- .../calendar/calendar/calendar.component.ts | 132 ++++- .../calendar/month/month-view.component.html | 0 .../calendar/month/month-view.component.ts | 548 ++++++++++++++++++ 5 files changed, 721 insertions(+), 9 deletions(-) create mode 100644 src/app/shared/components/calendar/month/month-view.component.html create mode 100644 src/app/shared/components/calendar/month/month-view.component.ts diff --git a/src/app/shared/components/calendar/calendar.module.ts b/src/app/shared/components/calendar/calendar.module.ts index 217ea2d..ba3ab33 100644 --- a/src/app/shared/components/calendar/calendar.module.ts +++ b/src/app/shared/components/calendar/calendar.module.ts @@ -2,9 +2,10 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { CalendarComponent } from './calendar/calendar.component'; +import { MonthViewComponent } from './month/month-view.component'; @NgModule({ - declarations: [CalendarComponent], + declarations: [CalendarComponent, MonthViewComponent], imports: [CommonModule], exports: [CalendarComponent], }) diff --git a/src/app/shared/components/calendar/calendar/calendar.component.html b/src/app/shared/components/calendar/calendar/calendar.component.html index 174325f..17e8448 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.html +++ b/src/app/shared/components/calendar/calendar/calendar.component.html @@ -1,16 +1,16 @@ -<ng-template #monthviewDefaultDisplayEventTemplate let-view="view" let-row="row" let-col="col"> +<ng-template #monthViewDefaultDisplayEventTemplate let-view="view" let-row="row" let-col="col"> {{ view.dates[row * 7 + col].label }} </ng-template> <ng-template - #monthviewDefaultEventDetailTemplate + #monthViewDefaultEventDetailTemplate let-showEventDetail="showEventDetail" let-selectedDate="selectedDate" let-noEventsLabel="noEventsLabel"> <div class="event-detail-container" has-bouncing="false" *ngIf="showEventDetail" overflow-scroll="false"> <div class="event-detail-item" *ngFor="let event of selectedDate?.events" (click)="eventSelected(event)"> <span *ngIf="!event.allDay" class="monthview-eventdetail-timecolumn"> - {{event.startTime | date: 'HH:mm'}} + {{ event.startTime | date: 'HH:mm' }} - - {{event.endTime | date: 'HH:mm'}} + {{ event.endTime | date: 'HH:mm' }} </span> <span *ngIf="event.allDay" class="monthview-eventdetail-timecolumn">{{ allDayLabel }}</span> <span class="event-detail"> | {{ event.title }}</span> @@ -19,4 +19,41 @@ <div class="no-events-label">{{ noEventsLabel }}</div> </div> </div> -</ng-template> \ No newline at end of file +</ng-template> +<ng-template #defaultWeekViewHeaderTemplate let-viewDate="viewDate"> + {{ viewDate.dayHeader }} +</ng-template> +<ng-template #defaultAllDayEventTemplate let-displayEvent="displayEvent"> + <div class="calendar-event-inner">{{ displayEvent.event.title }}</div> +</ng-template> +<ng-template #defaultNormalEventTemplate let-displayEvent="displayEvent"> + <div class="calendar-event-inner">{{ displayEvent.event.title }}</div> +</ng-template> +<ng-template #defaultWeekViewAllDayEventSectionTemplate let-day="day" let-eventTemplate="eventTemplate"> + <div [ngClass]="{'calendar-event-wrap': day.events}" *ngIf="day.events" + [ngStyle]="{height: 25*day.events.length+'px'}"> + <div *ngFor="let displayEvent of day.events" class="calendar-event" tappable + (click)="eventSelected(displayEvent.event)" + [ngStyle]="{top: 25*displayEvent.position+'px', width: 100*(displayEvent.endIndex-displayEvent.startIndex)+'%', height: '25px'}"> + <ng-template [ngTemplateOutlet]="eventTemplate" + [ngTemplateOutletContext]="{displayEvent:displayEvent}"> + </ng-template> + </div> + </div> +</ng-template> +<ng-template #defaultNormalEventSectionTemplate let-tm="tm" let-hourParts="hourParts" + let-eventTemplate="eventTemplate"> + <div [ngClass]="{'calendar-event-wrap': tm.events}" *ngIf="tm.events"> + <div *ngFor="let displayEvent of tm.events" class="calendar-event" tappable + (click)="eventSelected(displayEvent.event)" + [ngStyle]="{top: (37*displayEvent.startOffset/hourParts)+'px',left: 100/displayEvent.overlapNumber*displayEvent.position+'%', width: 100/displayEvent.overlapNumber+'%', height: 37*(displayEvent.endIndex -displayEvent.startIndex - (displayEvent.endOffset + displayEvent.startOffset)/hourParts)+'px'}"> + <ng-template [ngTemplateOutlet]="eventTemplate" + [ngTemplateOutletContext]="{displayEvent:displayEvent}"> + </ng-template> + </div> + </div> +</ng-template> + +<div [ngSwitch]="calendarMode" class="{{ calendarMode }}view-container"> + Le calendrier ici +</div> \ No newline at end of file diff --git a/src/app/shared/components/calendar/calendar/calendar.component.ts b/src/app/shared/components/calendar/calendar/calendar.component.ts index 1d994c6..2929970 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.ts +++ b/src/app/shared/components/calendar/calendar/calendar.component.ts @@ -7,10 +7,28 @@ import { TemplateRef, Inject, LOCALE_ID, + OnDestroy, } from '@angular/core'; import { Subscription } from 'rxjs'; -import { IEvent, IRange, ITimeSelected } from '../interfaces'; +import { + CalendarMode, + IDateFormatter, + IDayViewAllDayEventSectionTemplateContext, + IDayViewNormalEventSectionTemplateContext, + IDisplayAllDayEvent, + IDisplayEvent, + IDisplayWeekViewHeader, + IEvent, + IMonthViewDisplayEventTemplateContext, + IMonthViewEventDetailTemplateContext, + IRange, + ITimeSelected, + IWeekViewAllDayEventSectionTemplateContext, + IWeekViewNormalEventSectionTemplateContext, + QueryMode, + Step, +} from '../interfaces'; import { CalendarService } from '../calendar.service'; @Component({ @@ -19,7 +37,7 @@ import { CalendarService } from '../calendar.service'; styleUrls: ['./calendar.component.scss'], providers: [CalendarService], }) -export class CalendarComponent { +export class CalendarComponent implements OnInit, OnDestroy { @Input() get currentDate(): Date { return this._currentDate; @@ -35,7 +53,46 @@ export class CalendarComponent { this.onCurrentDateChanged.emit(this._currentDate); } + @Input() eventSource: IEvent[] = []; + @Input() calendarMode: CalendarMode = 'month'; + @Input() formatDay = 'd'; + @Input() formatDayHeader = 'EEE'; + @Input() formatDayTitle = 'MMMM dd, yyyy'; + @Input() formatWeekTitle = "MMMM yyyy, 'Week' w"; + @Input() formatMonthTitle = 'MMMM yyyy'; + @Input() formatWeekViewDayHeader = 'EEE d'; + @Input() formatHourColumn = 'ha'; + @Input() showEventDetail = true; + @Input() startingDayMonth = 0; + @Input() startingDayWeek = 0; + @Input() allDayLabel = 'all day'; + @Input() noEventsLabel = 'No Events'; + @Input() queryMode: QueryMode = 'local'; + @Input() step: Step = Step.Hour; + @Input() timeInterval = 60; + @Input() autoSelect = true; + @Input() markDisabled!: (date: Date) => boolean; + @Input() + monthViewDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + @Input() + monthViewEventDetailTemplate!: TemplateRef<IMonthViewEventDetailTemplateContext>; + @Input() weekViewHeaderTemplate!: TemplateRef<IDisplayWeekViewHeader>; + @Input() weekViewAllDayEventTemplate!: TemplateRef<IDisplayAllDayEvent>; + @Input() weekViewNormalEventTemplate!: TemplateRef<IDisplayEvent>; + @Input() dayViewAllDayEventTemplate!: TemplateRef<IDisplayAllDayEvent>; + @Input() dayViewNormalEventTemplate!: TemplateRef<IDisplayEvent>; + @Input() + weekViewAllDayEventSectionTemplate!: TemplateRef<IWeekViewAllDayEventSectionTemplateContext>; + @Input() + weekViewNormalEventSectionTemplate!: TemplateRef<IWeekViewNormalEventSectionTemplateContext>; + @Input() + dayViewAllDayEventSectionTemplate!: TemplateRef<IDayViewAllDayEventSectionTemplateContext>; + @Input() + dayViewNormalEventSectionTemplate!: TemplateRef<IDayViewNormalEventSectionTemplateContext>; + @Input() dateFormatter!: IDateFormatter; @Input() locale = ''; + @Input() startHour = 0; + @Input() endHour = 24; @Output() onCurrentDateChanged = new EventEmitter<Date>(); @Output() onRangeChanged = new EventEmitter<IRange>(); @@ -46,7 +103,7 @@ export class CalendarComponent { private _currentDate!: Date; private hourParts = 1; private hourSegments = 1; - private currentDateChangedFromChildrenSubscription!: Subscription; + private currentDateChangedFromChildrenSubscription!: Subscription | null; constructor( private calendarService: CalendarService, @@ -54,4 +111,73 @@ export class CalendarComponent { ) { this.locale = appLocale; } + + ngOnInit() { + if (this.autoSelect) { + if (this.autoSelect.toString() === 'false') { + this.autoSelect = false; + } else { + this.autoSelect = true; + } + } + this.hourSegments = 60 / this.timeInterval; + this.hourParts = 60 / this.step; + if (this.hourParts <= this.hourSegments) { + this.hourParts = 1; + } else { + this.hourParts = this.hourParts / this.hourSegments; + } + this.startHour = parseInt(this.startHour.toString(), 10); + this.endHour = parseInt(this.endHour.toString(), 10); + this.calendarService.queryMode = this.queryMode; + + this.currentDateChangedFromChildrenSubscription = + this.calendarService.currentDateChangedFromChildren$.subscribe( + currentDate => { + this._currentDate = currentDate; + this.onCurrentDateChanged.emit(currentDate); + } + ); + } + + ngOnDestroy() { + if (this.currentDateChangedFromChildrenSubscription) { + this.currentDateChangedFromChildrenSubscription.unsubscribe(); + this.currentDateChangedFromChildrenSubscription = null; + } + } + + rangeChanged(range: IRange) { + this.onRangeChanged.emit(range); + } + + eventSelected(event: IEvent) { + this.onEventSelected.emit(event); + } + + timeSelected(timeSelected: ITimeSelected) { + this.onTimeSelected.emit(timeSelected); + } + + titleChanged(title: string) { + this.onTitleChanged.emit(title); + } + + loadEvents() { + this.calendarService.loadEvents(); + } + + next() { + this.currentDate = this.calendarService.getAdjacentCalendarDate( + this.calendarMode, + 1 + ); + } + + previous() { + this.currentDate = this.calendarService.getAdjacentCalendarDate( + this.calendarMode, + -1 + ); + } } diff --git a/src/app/shared/components/calendar/month/month-view.component.html b/src/app/shared/components/calendar/month/month-view.component.html new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/calendar/month/month-view.component.ts b/src/app/shared/components/calendar/month/month-view.component.ts new file mode 100644 index 0000000..3ab1b05 --- /dev/null +++ b/src/app/shared/components/calendar/month/month-view.component.ts @@ -0,0 +1,548 @@ +import { + Component, + OnInit, + OnChanges, + Input, + Output, + EventEmitter, + SimpleChanges, + TemplateRef, +} from '@angular/core'; +import { Subscription } from 'rxjs'; +import { DatePipe } from '@angular/common'; + +import { + ICalendarComponent, + IEvent, + IMonthView, + IMonthViewRow, + ITimeSelected, + IRange, + CalendarMode, + IDateFormatter, + IMonthViewDisplayEventTemplateContext, +} from '../interfaces'; +import { CalendarService } from '../calendar.service'; + +@Component({ + selector: 'month-view', + templateUrl: './month-view.component.html', +}) +export class MonthViewComponent + implements OnInit, OnChanges, ICalendarComponent +{ + @Input() + monthViewDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + @Input() + monthViewInactiveDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + @Input() + monthViewEventDetailTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + + @Input() formatDay!: string; + @Input() formatDayHeader!: string; + @Input() formatMonthTitle!: string; + @Input() eventSource!: IEvent[]; + @Input() startingDayMonth!: number; + @Input() showEventDetail!: boolean; + @Input() noEventsLabel!: string; + @Input() autoSelect = true; + @Input() markDisabled!: (date: Date) => boolean; + @Input() locale!: string; + @Input() dateFormatter!: IDateFormatter; + @Input() spaceBetween!: number; + + @Output() onRangeChanged = new EventEmitter<IRange>(); + @Output() onEventSelected = new EventEmitter<IEvent>(); + @Output() onTimeSelected = new EventEmitter<ITimeSelected>(true); + @Output() onTitleChanged = new EventEmitter<string>(true); + + public view!: IMonthView; + public currentViewIndex = 0; + public selectedDate!: IMonthViewRow; + public range!: IRange; + public mode: CalendarMode = 'month'; + public direction = 0; + + private moveOnSelected = false; + private inited = false; + private currentDateChangedFromParentSubscription!: Subscription | null; + private eventSourceChangedSubscription!: Subscription | null; + private formatDayLabel!: (date: Date) => string; + private formatDayHeaderLabel!: (date: Date) => string; + private formatTitle!: (date: Date) => string; + + constructor(private calendarService: CalendarService) {} + + static getDates(startDate: Date, n: number): Date[] { + const dates = new Array(n), + current = new Date(startDate.getTime()); + let i = 0; + current.setHours(12); // Prevent repeated dates because of timezone bug + while (i < n) { + dates[i++] = new Date(current.getTime()); + current.setDate(current.getDate() + 1); + } + return dates; + } + + ngOnInit() { + if (this.dateFormatter && this.dateFormatter.formatMonthViewDay) { + this.formatDayLabel = this.dateFormatter.formatMonthViewDay; + } else { + const dayLabelDatePipe = new DatePipe('en-US'); + this.formatDayLabel = (date: Date): string => { + return dayLabelDatePipe.transform(date, this.formatDay)!; + }; + } + + if (this.dateFormatter && this.dateFormatter.formatMonthViewDayHeader) { + this.formatDayHeaderLabel = this.dateFormatter.formatMonthViewDayHeader; + } else { + const datePipe = new DatePipe(this.locale); + this.formatDayHeaderLabel = (date: Date): string => { + return datePipe.transform(date, this.formatDayHeader)!; + }; + } + + if (this.dateFormatter && this.dateFormatter.formatMonthViewTitle) { + this.formatTitle = this.dateFormatter.formatMonthViewTitle; + } else { + const datePipe = new DatePipe(this.locale); + this.formatTitle = (date: Date) => { + return datePipe.transform(date, this.formatMonthTitle)!; + }; + } + + this.refreshView(); + this.inited = true; + + this.currentDateChangedFromParentSubscription = + this.calendarService.currentDateChangedFromParent$.subscribe( + currentDate => { + this.refreshView(); + } + ); + + this.eventSourceChangedSubscription = + this.calendarService.eventSourceChanged$.subscribe(() => { + this.onDataLoaded(); + }); + } + + ngOnDestroy() { + if (this.currentDateChangedFromParentSubscription) { + this.currentDateChangedFromParentSubscription.unsubscribe(); + this.currentDateChangedFromParentSubscription = null; + } + + if (this.eventSourceChangedSubscription) { + this.eventSourceChangedSubscription.unsubscribe(); + this.eventSourceChangedSubscription = null; + } + } + + ngOnChanges(changes: SimpleChanges) { + if (!this.inited) { + return; + } + + const eventSourceChange = changes['eventSource']; + if (eventSourceChange && eventSourceChange.currentValue) { + this.onDataLoaded(); + } + } + + ngAfterViewInit() { + const title = this.getTitle(); + this.onTitleChanged.emit(title); + } + + move(direction: number) { + if (direction === 0) { + return; + } + + this.direction = direction; + if (!this.moveOnSelected) { + const adjacentDate = this.calendarService.getAdjacentCalendarDate( + this.mode, + direction + ); + this.calendarService.setCurrentDate(adjacentDate); + } + this.refreshView(); + this.direction = 0; + this.moveOnSelected = false; + } + + createDateObject(date: Date): IMonthViewRow { + let disabled = false; + if (this.markDisabled) { + disabled = this.markDisabled(date); + } + + return { + date: date, + events: [], + label: this.formatDayLabel(date), + secondary: false, + disabled: disabled, + }; + } + + getViewData(startTime: Date): IMonthView { + const startDate = startTime, + date = startDate.getDate(), + month = (startDate.getMonth() + (date !== 1 ? 1 : 0)) % 12, + dates = MonthViewComponent.getDates(startDate, 42), + days: IMonthViewRow[] = []; + + for (let i = 0; i < 42; i++) { + const dateObject = this.createDateObject(dates[i]); + dateObject.secondary = dates[i].getMonth() !== month; + days[i] = dateObject; + } + + const dayHeaders: string[] = []; + for (let i = 0; i < 7; i++) { + dayHeaders.push(this.formatDayHeaderLabel(days[i].date)); + } + return { + dates: days, + dayHeaders: dayHeaders, + }; + } + + getHighlightClass(date: IMonthViewRow): string { + let className = ''; + + if (date.hasEvent) { + if (date.secondary) { + className = 'monthview-secondary-with-event'; + } else { + className = 'monthview-primary-with-event'; + } + } + + if (date.selected) { + if (className) { + className += ' '; + } + className += 'monthview-selected'; + } + + if (date.current) { + if (className) { + className += ' '; + } + className += 'monthview-current'; + } + + if (date.secondary) { + if (className) { + className += ' '; + } + className += 'text-muted'; + } + + if (date.disabled) { + if (className) { + className += ' '; + } + className += 'monthview-disabled'; + } + return className; + } + + getRange(currentDate: Date): IRange { + const year = currentDate.getFullYear(), + month = currentDate.getMonth(), + firstDayOfMonth = new Date(year, month, 1), + difference = this.startingDayMonth - firstDayOfMonth.getDay(), + numDisplayedFromPreviousMonth = + difference > 0 ? 7 - difference : -difference, + startDate = new Date(firstDayOfMonth.getTime()); + + if (numDisplayedFromPreviousMonth > 0) { + startDate.setDate(-numDisplayedFromPreviousMonth + 1); + } + + const endDate = new Date(startDate.getTime()); + endDate.setDate(endDate.getDate() + 42); + + return { + startTime: startDate, + endTime: endDate, + }; + } + + onDataLoaded() { + const range = this.range, + eventSource = this.eventSource, + len = eventSource ? eventSource.length : 0, + startTime = range.startTime, + endTime = range.endTime, + utcStartTime = new Date( + Date.UTC( + startTime.getFullYear(), + startTime.getMonth(), + startTime.getDate() + ) + ), + utcEndTime = new Date( + Date.UTC(endTime.getFullYear(), endTime.getMonth(), endTime.getDate()) + ), + dates = this.view.dates, + oneDay = 86400000, + eps = 0.0006; + + for (let r = 0; r < 42; r += 1) { + if (dates[r].hasEvent) { + dates[r].hasEvent = false; + dates[r].events = []; + } + } + + for (let i = 0; i < len; i += 1) { + const event = eventSource[i], + eventStartTime = new Date(event.startTime.getTime()), + eventEndTime = new Date(event.endTime.getTime()); + let st: Date, et: Date; + + if (event.allDay) { + if (eventEndTime <= utcStartTime || eventStartTime >= utcEndTime) { + continue; + } else { + st = utcStartTime; + et = utcEndTime; + } + } else { + if (eventEndTime <= startTime || eventStartTime >= endTime) { + continue; + } else { + st = startTime; + et = endTime; + } + } + + let timeDiff: number; + let timeDifferenceStart: number; + if (eventStartTime <= st) { + timeDifferenceStart = 0; + } else { + timeDiff = eventStartTime.getTime() - st.getTime(); + if (!event.allDay) { + timeDiff = + timeDiff - + (eventStartTime.getTimezoneOffset() - st.getTimezoneOffset()) * + 60000; + } + timeDifferenceStart = timeDiff / oneDay; + } + + let timeDifferenceEnd: number; + if (eventEndTime >= et) { + timeDiff = et.getTime() - st.getTime(); + if (!event.allDay) { + timeDiff = + timeDiff - + (et.getTimezoneOffset() - st.getTimezoneOffset()) * 60000; + } + timeDifferenceEnd = timeDiff / oneDay; + } else { + timeDiff = eventEndTime.getTime() - st.getTime(); + if (!event.allDay) { + timeDiff = + timeDiff - + (eventEndTime.getTimezoneOffset() - st.getTimezoneOffset()) * 60000; + } + timeDifferenceEnd = timeDiff / oneDay; + } + + let index = Math.floor(timeDifferenceStart); + while (index < timeDifferenceEnd - eps) { + dates[index].hasEvent = true; + let eventSet = dates[index].events; + if (eventSet) { + eventSet.push(event); + } else { + eventSet = []; + eventSet.push(event); + dates[index].events = eventSet; + } + index += 1; + } + } + + for (let r = 0; r < 42; r += 1) { + if (dates[r].hasEvent) { + dates[r].events.sort(this.compareEvent); + } + } + + if (this.autoSelect) { + let findSelected = false; + for (let r = 0; r < 42; r += 1) { + if (dates[r].selected) { + this.selectedDate = dates[r]; + findSelected = true; + break; + } + } + + if (findSelected) { + this.onTimeSelected.emit({ + selectedTime: this.selectedDate.date, + events: this.selectedDate.events, + disabled: this.selectedDate.disabled, + }); + } + } + } + + refreshView() { + this.range = this.getRange(this.calendarService.currentDate); + + if (this.inited) { + const title = this.getTitle(); + this.onTitleChanged.emit(title); + } + this.view = this.getViewData(this.range.startTime); + + this.updateCurrentView(this.range.startTime); + this.calendarService.rangeChanged(this); + } + + getTitle(): string { + const currentViewStartDate = this.range.startTime, + date = currentViewStartDate.getDate(), + month = (currentViewStartDate.getMonth() + (date !== 1 ? 1 : 0)) % 12, + year = + currentViewStartDate.getFullYear() + + (date !== 1 && month === 0 ? 1 : 0), + headerDate = new Date(year, month, 1, 12, 0, 0, 0); + return this.formatTitle(headerDate); + } + + private compareEvent(event1: IEvent, event2: IEvent): number { + if (event1.allDay) { + return 1; + } else if (event2.allDay) { + return -1; + } else { + return event1.startTime.getTime() - event2.startTime.getTime(); + } + } + + select(viewDate: IMonthViewRow) { + if (!this.view) { + return; + } + + const selectedDate = viewDate.date, + events = viewDate.events; + + if (!viewDate.disabled) { + const dates = this.view.dates, + currentCalendarDate = this.calendarService.currentDate, + currentMonth = currentCalendarDate.getMonth(), + currentYear = currentCalendarDate.getFullYear(), + selectedMonth = selectedDate.getMonth(), + selectedYear = selectedDate.getFullYear(); + let direction = 0; + + if (currentYear === selectedYear) { + if (currentMonth !== selectedMonth) { + direction = currentMonth < selectedMonth ? 1 : -1; + } + } else { + direction = currentYear < selectedYear ? 1 : -1; + } + + this.calendarService.setCurrentDate(selectedDate); + if (direction === 0) { + const currentViewStartDate = this.range.startTime, + oneDay = 86400000, + selectedDayDifference = Math.floor( + (selectedDate.getTime() - + currentViewStartDate.getTime() - + (selectedDate.getTimezoneOffset() - + currentViewStartDate.getTimezoneOffset()) * + 60000) / + oneDay + ); + + for (let r = 0; r < 42; r += 1) { + dates[r].selected = false; + } + + if (selectedDayDifference >= 0 && selectedDayDifference < 42) { + dates[selectedDayDifference].selected = true; + this.selectedDate = dates[selectedDayDifference]; + } + } else { + this.moveOnSelected = true; + this.move(direction); + } + } + + this.onTimeSelected.emit({ + selectedTime: selectedDate, + events: events, + disabled: viewDate.disabled, + }); + } + + updateCurrentView(currentViewStartDate: Date) { + const currentCalendarDate = this.calendarService.currentDate, + today = new Date(), + oneDay = 86400000, + selectedDayDifference = Math.floor( + (currentCalendarDate.getTime() - + currentViewStartDate.getTime() - + (currentCalendarDate.getTimezoneOffset() - + currentViewStartDate.getTimezoneOffset()) * + 60000) / + oneDay + ), + currentDayDifference = Math.floor( + (today.getTime() - + currentViewStartDate.getTime() - + (today.getTimezoneOffset() - + currentViewStartDate.getTimezoneOffset()) * + 60000) / + oneDay + ), + view = this.view; + + for (let r = 0; r < 42; r += 1) { + view.dates[r].selected = false; + } + + if ( + selectedDayDifference >= 0 && + selectedDayDifference < 42 && + !view.dates[selectedDayDifference].disabled && + (this.autoSelect || this.moveOnSelected) + ) { + view.dates[selectedDayDifference].selected = true; + this.selectedDate = view.dates[selectedDayDifference]; + } else { + this.selectedDate = { + date: new Date(), + events: [], + label: 'null', + secondary: false, + disabled: false, + }; + } + + if (currentDayDifference >= 0 && currentDayDifference < 42) { + view.dates[currentDayDifference].current = true; + } + } + + eventSelected(event: IEvent) { + this.onEventSelected.emit(event); + } +} From fcb98bb6707e13d685c28fcdad8c1aee1e57ef67 Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 18 Oct 2022 14:55:33 +0100 Subject: [PATCH 8/9] :construction: wip --- .../pages/calendar/calendar.component.html | 5 +- .../pages/calendar/calendar.component.ts | 13 +- .../components/calendar/calendar.service.ts | 1 - .../calendar/calendar/calendar.component.html | 60 +++-- .../calendar/calendar/calendar.component.ts | 11 + .../shared/components/calendar/interfaces.ts | 2 - .../calendar/month/month-view.component.html | 227 ++++++++++++++++++ .../calendar/month/month-view.component.ts | 110 +++++---- src/index.html | 2 +- 9 files changed, 358 insertions(+), 73 deletions(-) diff --git a/src/app/modules/design/pages/calendar/calendar.component.html b/src/app/modules/design/pages/calendar/calendar.component.html index 77de52a..0970bb5 100644 --- a/src/app/modules/design/pages/calendar/calendar.component.html +++ b/src/app/modules/design/pages/calendar/calendar.component.html @@ -1,5 +1,8 @@ <h2>Calendrier</h2> <div class="mt-10"> - <tw-calendar></tw-calendar> + <tw-calendar + [currentDate]="calendar.currentDate" + monthStyle="overflow-hidden rounded-lg mb-20" + ></tw-calendar> </div> diff --git a/src/app/modules/design/pages/calendar/calendar.component.ts b/src/app/modules/design/pages/calendar/calendar.component.ts index 98a05c8..d2d3391 100644 --- a/src/app/modules/design/pages/calendar/calendar.component.ts +++ b/src/app/modules/design/pages/calendar/calendar.component.ts @@ -1,9 +1,18 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; @Component({ templateUrl: './calendar.component.html', styleUrls: ['./calendar.component.scss'], + encapsulation: ViewEncapsulation.None, }) -export class CalendarComponent { +export class CalendarComponent implements OnInit { + public calendar!: any; constructor() {} + + ngOnInit(): void { + this.calendar = { + mode: 'month', + currentDate: new Date(), + }; + } } diff --git a/src/app/shared/components/calendar/calendar.service.ts b/src/app/shared/components/calendar/calendar.service.ts index 0a3deb5..149cf54 100644 --- a/src/app/shared/components/calendar/calendar.service.ts +++ b/src/app/shared/components/calendar/calendar.service.ts @@ -3,7 +3,6 @@ import { Observable, Subject } from 'rxjs'; import { ICalendarComponent, - IView, CalendarMode, QueryMode, } from './interfaces'; diff --git a/src/app/shared/components/calendar/calendar/calendar.component.html b/src/app/shared/components/calendar/calendar/calendar.component.html index 17e8448..19af189 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.html +++ b/src/app/shared/components/calendar/calendar/calendar.component.html @@ -1,8 +1,18 @@ -<ng-template #monthViewDefaultDisplayEventTemplate let-view="view" let-row="row" let-col="col"> - {{ view.dates[row * 7 + col].label }} +<ng-template #monthViewDefaultDisplayEventTemplate let-view="view"> + <time [attr.datetime]="view.date" + [ngClass]="{ 'flex h-6 w-6 items-center justify-center rounded-full bg-primary-600 font-semibold text-white' : view.current }" + >{{ view.label }}</time> + <ol class="mt-2" *ngIf="view.events.length > 0"> + <li *ngFor="let event of view.events"> + <a href="#" class="flex group"> + <p class="flex-auto font-medium truncate text-slate-900 dark:text-white/75 group-hover:text-primary-600">{{ event.title }}</p> + <time [attr.datetime]="event.startTime" class="flex-none hidden ml-3 text-slate-500 group-hover:text-primary-600 xl:block">{{ event.startTime }}</time> + </a> + </li> + </ol> </ng-template> -<ng-template - #monthViewDefaultEventDetailTemplate +<ng-template + #monthViewDefaultEventDetailTemplate let-showEventDetail="showEventDetail" let-selectedDate="selectedDate" let-noEventsLabel="noEventsLabel"> <div class="event-detail-container" has-bouncing="false" *ngIf="showEventDetail" overflow-scroll="false"> @@ -41,19 +51,37 @@ </div> </div> </ng-template> -<ng-template #defaultNormalEventSectionTemplate let-tm="tm" let-hourParts="hourParts" - let-eventTemplate="eventTemplate"> - <div [ngClass]="{'calendar-event-wrap': tm.events}" *ngIf="tm.events"> - <div *ngFor="let displayEvent of tm.events" class="calendar-event" tappable - (click)="eventSelected(displayEvent.event)" - [ngStyle]="{top: (37*displayEvent.startOffset/hourParts)+'px',left: 100/displayEvent.overlapNumber*displayEvent.position+'%', width: 100/displayEvent.overlapNumber+'%', height: 37*(displayEvent.endIndex -displayEvent.startIndex - (displayEvent.endOffset + displayEvent.startOffset)/hourParts)+'px'}"> - <ng-template [ngTemplateOutlet]="eventTemplate" - [ngTemplateOutletContext]="{displayEvent:displayEvent}"> - </ng-template> - </div> +<ng-template #defaultNormalEventSectionTemplate let-tm="tm" let-hourParts="hourParts" let-eventTemplate="eventTemplate"> + <div [ngClass]="{'calendar-event-wrap': tm.events}" *ngIf="tm.events"> + <div *ngFor="let displayEvent of tm.events" class="calendar-event" tappable + (click)="eventSelected(displayEvent.event)" + [ngStyle]="{top: (37*displayEvent.startOffset/hourParts)+'px',left: 100/displayEvent.overlapNumber*displayEvent.position+'%', width: 100/displayEvent.overlapNumber+'%', height: 37*(displayEvent.endIndex -displayEvent.startIndex - (displayEvent.endOffset + displayEvent.startOffset)/hourParts)+'px'}"> + <ng-template [ngTemplateOutlet]="eventTemplate" + [ngTemplateOutletContext]="{displayEvent:displayEvent}"> + </ng-template> </div> + </div> </ng-template> -<div [ngSwitch]="calendarMode" class="{{ calendarMode }}view-container"> - Le calendrier ici +<div [ngSwitch]="calendarMode" class="{{ calendarMode }}-view"> + <month-view *ngSwitchCase="'month'" + [formatDay]="formatDay" + [formatDayHeader]="formatDayHeader" + [formatMonthTitle]="formatMonthTitle" + [startingDayMonth]="startingDayMonth" + [showEventDetail]="showEventDetail" + [noEventsLabel]="noEventsLabel" + [autoSelect]="autoSelect" + [eventSource]="eventSource" + [markDisabled]="markDisabled" + [monthViewDisplayEventTemplate]="monthViewDisplayEventTemplate || monthViewDefaultDisplayEventTemplate" + [monthViewEventDetailTemplate]="monthViewEventDetailTemplate|| monthViewDefaultEventDetailTemplate" + [locale]="locale" + [dateFormatter]="dateFormatter" + [monthClass]="monthClass" + (onRangeChanged)="rangeChanged($event)" + (onEventSelected)="eventSelected($event)" + (onTimeSelected)="timeSelected($event)" + (onTitleChanged)="titleChanged($event)"> + </month-view> </div> \ No newline at end of file diff --git a/src/app/shared/components/calendar/calendar/calendar.component.ts b/src/app/shared/components/calendar/calendar/calendar.component.ts index 2929970..8f3840c 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.ts +++ b/src/app/shared/components/calendar/calendar/calendar.component.ts @@ -93,6 +93,10 @@ export class CalendarComponent implements OnInit, OnDestroy { @Input() locale = ''; @Input() startHour = 0; @Input() endHour = 24; + @Input() styleClass!: string; + @Input('monthStyle') monthClass!: string; + @Input('weekStyle') weekClass!: string; + @Input('dayStyle') dayClass!: string; @Output() onCurrentDateChanged = new EventEmitter<Date>(); @Output() onRangeChanged = new EventEmitter<IRange>(); @@ -105,11 +109,18 @@ export class CalendarComponent implements OnInit, OnDestroy { private hourSegments = 1; private currentDateChangedFromChildrenSubscription!: Subscription | null; + monthStyle!: string; + weekStyle!: string; + dayStyle!: string; + constructor( private calendarService: CalendarService, @Inject(LOCALE_ID) private appLocale: string ) { this.locale = appLocale; + this.monthStyle = this.monthClass; + this.weekStyle = this.weekClass; + this.dayStyle = this.dayClass; } ngOnInit() { diff --git a/src/app/shared/components/calendar/interfaces.ts b/src/app/shared/components/calendar/interfaces.ts index 6181329..2393f50 100644 --- a/src/app/shared/components/calendar/interfaces.ts +++ b/src/app/shared/components/calendar/interfaces.ts @@ -98,8 +98,6 @@ export interface ITimeSelected { export interface IMonthViewDisplayEventTemplateContext { view: IView; - row: number; - col: number; } export interface IMonthViewEventDetailTemplateContext { diff --git a/src/app/shared/components/calendar/month/month-view.component.html b/src/app/shared/components/calendar/month/month-view.component.html index e69de29..dd7d69c 100644 --- a/src/app/shared/components/calendar/month/month-view.component.html +++ b/src/app/shared/components/calendar/month/month-view.component.html @@ -0,0 +1,227 @@ +<div> + <!-- <ng-template [ngTemplateOutlet]="monthViewEventDetailTemplate" + [ngTemplateOutletContext]="{showEventDetail:showEventDetail, selectedDate: selectedDate, noEventsLabel: noEventsLabel}"> + </ng-template> --> + <div class="shadow ring-1 ring-black ring-opacity-5 lg:flex lg:flex-auto lg:flex-col" [ngClass]="monthClass"> + <!-- Weekly Days --> + <div class="grid grid-cols-7 gap-px text-xs font-semibold leading-6 text-center bg-gray-200 border-b border-slate-300 text-slate-700 lg:flex-none dark:text-slate-300 dark:bg-gray-700 dark:border-gray-600"> + <div *ngFor="let dayHeader of view.dayHeaders" class="flex justify-center py-2 bg-white dark:bg-gray-800"> + <span class="uppercase">{{ dayHeader.slice(0, 1) }}</span> + <span class="sr-only sm:not-sr-only">{{ dayHeader.slice(1, 3) }}</span> + </div> + </div> + <div class="flex text-xs leading-6 bg-gray-200 text-slate-700 lg:flex-auto dark:bg-gray-600 dark:text-slate-300"> + <!-- Desktop View --> + <div class="hidden w-full lg:grid lg:grid-cols-7 lg:grid-rows-6 lg:gap-px"> + <div *ngFor="let view of view.dates" class="relative px-3 py-2" tappable [ngClass]="getHighlightClass(view)" (click)="select(view)"> + <ng-template [ngTemplateOutlet]="monthViewDisplayEventTemplate" + [ngTemplateOutletContext]="{ view }"></ng-template> + </div> + </div> + <!-- Mobile View --> + <div class="grid w-full grid-cols-7 grid-rows-6 gap-px isolate lg:hidden"> + <!-- + Always include: "flex h-14 flex-col py-2 px-3 hover:bg-gray-100 focus:z-10" + Is current month, include: "bg-white" + Is not current month, include: "bg-gray-50" + Is selected or is today, include: "font-semibold" + Is selected, include: "text-white" + Is not selected and is today, include: "text-primary-600" + Is not selected and is current month, and is not today, include: "text-gray-900" + Is not selected, is not current month, and is not today: "text-gray-500" + --> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <!-- + Always include: "ml-auto" + Is selected, include: "flex h-6 w-6 items-center justify-center rounded-full" + Is selected and is today, include: "bg-primary-600" + Is selected and is not today, include: "bg-gray-900" + --> + <time datetime="2021-12-27" class="ml-auto">27</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2021-12-28" class="ml-auto">28</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2021-12-29" class="ml-auto">29</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2021-12-30" class="ml-auto">30</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2021-12-31" class="ml-auto">31</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-01" class="ml-auto">1</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-02" class="ml-auto">2</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-03" class="ml-auto">3</time> + <span class="sr-only">2 events</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + </span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-04" class="ml-auto">4</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-05" class="ml-auto">5</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-06" class="ml-auto">6</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-07" class="ml-auto">7</time> + <span class="sr-only">1 event</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + </span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-08" class="ml-auto">8</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-09" class="ml-auto">9</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-10" class="ml-auto">10</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-11" class="ml-auto">11</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 font-semibold bg-white text-primary-600 h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-12" class="ml-auto">12</time> + <span class="sr-only">1 event</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + </span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-13" class="ml-auto">13</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-14" class="ml-auto">14</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-15" class="ml-auto">15</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-16" class="ml-auto">16</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-17" class="ml-auto">17</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-18" class="ml-auto">18</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-19" class="ml-auto">19</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-20" class="ml-auto">20</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-21" class="ml-auto">21</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 font-semibold text-white bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-22" class="flex items-center justify-center w-6 h-6 ml-auto bg-gray-900 rounded-full">22</time> + <span class="sr-only">2 events</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + </span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-23" class="ml-auto">23</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-24" class="ml-auto">24</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-25" class="ml-auto">25</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-26" class="ml-auto">26</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-27" class="ml-auto">27</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-28" class="ml-auto">28</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-29" class="ml-auto">29</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-30" class="ml-auto">30</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-01-31" class="ml-auto">31</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-01" class="ml-auto">1</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-02" class="ml-auto">2</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-03" class="ml-auto">3</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-04" class="ml-auto">4</time> + <span class="sr-only">1 event</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> + </span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-05" class="ml-auto">5</time> + <span class="sr-only">0 events</span> + </button> + <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> + <time datetime="2022-02-06" class="ml-auto">6</time> + <span class="sr-only">0 events</span> + </button> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/src/app/shared/components/calendar/month/month-view.component.ts b/src/app/shared/components/calendar/month/month-view.component.ts index 3ab1b05..1c4aa7e 100644 --- a/src/app/shared/components/calendar/month/month-view.component.ts +++ b/src/app/shared/components/calendar/month/month-view.component.ts @@ -36,7 +36,9 @@ export class MonthViewComponent @Input() monthViewInactiveDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; @Input() - monthViewEventDetailTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + monthViewEventDetailTemplate!: + | TemplateRef<IMonthViewDisplayEventTemplateContext> + | any; @Input() formatDay!: string; @Input() formatDayHeader!: string; @@ -50,6 +52,7 @@ export class MonthViewComponent @Input() locale!: string; @Input() dateFormatter!: IDateFormatter; @Input() spaceBetween!: number; + @Input() monthClass!: string; @Output() onRangeChanged = new EventEmitter<IRange>(); @Output() onEventSelected = new EventEmitter<IEvent>(); @@ -82,6 +85,7 @@ export class MonthViewComponent dates[i++] = new Date(current.getTime()); current.setDate(current.getDate() + 1); } + return dates; } @@ -117,12 +121,9 @@ export class MonthViewComponent this.inited = true; this.currentDateChangedFromParentSubscription = - this.calendarService.currentDateChangedFromParent$.subscribe( - currentDate => { - this.refreshView(); - } - ); - + this.calendarService.currentDateChangedFromParent$.subscribe(() => { + this.refreshView(); + }); this.eventSourceChangedSubscription = this.calendarService.eventSourceChanged$.subscribe(() => { this.onDataLoaded(); @@ -191,11 +192,11 @@ export class MonthViewComponent } getViewData(startTime: Date): IMonthView { - const startDate = startTime, - date = startDate.getDate(), - month = (startDate.getMonth() + (date !== 1 ? 1 : 0)) % 12, - dates = MonthViewComponent.getDates(startDate, 42), - days: IMonthViewRow[] = []; + const startDate = startTime; + const date = startDate.getDate(); + const month = (startDate.getMonth() + (date !== 1 ? 1 : 0)) % 12; + const dates = MonthViewComponent.getDates(startDate, 42); + const days: IMonthViewRow[] = []; for (let i = 0; i < 42; i++) { const dateObject = this.createDateObject(dates[i]); @@ -207,6 +208,7 @@ export class MonthViewComponent for (let i = 0; i < 7; i++) { dayHeaders.push(this.formatDayHeaderLabel(days[i].date)); } + return { dates: days, dayHeaders: dayHeaders, @@ -218,9 +220,9 @@ export class MonthViewComponent if (date.hasEvent) { if (date.secondary) { - className = 'monthview-secondary-with-event'; + className = 'secondary-with-event'; } else { - className = 'monthview-primary-with-event'; + className = 'primary-with-event'; } } @@ -228,40 +230,48 @@ export class MonthViewComponent if (className) { className += ' '; } - className += 'monthview-selected'; + className += 'selected'; } if (date.current) { if (className) { className += ' '; } - className += 'monthview-current'; + className += 'current'; } if (date.secondary) { if (className) { className += ' '; } - className += 'text-muted'; + className += + 'bg-gray-50 text-slate-500 dark:text-slate-400 dark:bg-gray-700'; + } else { + if (className) { + className += ' '; + } + className += + 'bg-white text-slate-700 dark:bg-gray-800 dark:text-slate-300'; } if (date.disabled) { if (className) { className += ' '; } - className += 'monthview-disabled'; + className += 'disabled cursor-disabled bg-gray-100 dark:bg-gray-600'; } + return className; } getRange(currentDate: Date): IRange { - const year = currentDate.getFullYear(), - month = currentDate.getMonth(), - firstDayOfMonth = new Date(year, month, 1), - difference = this.startingDayMonth - firstDayOfMonth.getDay(), - numDisplayedFromPreviousMonth = - difference > 0 ? 7 - difference : -difference, - startDate = new Date(firstDayOfMonth.getTime()); + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + const firstDayOfMonth = new Date(year, month, 1); + const difference = this.startingDayMonth - firstDayOfMonth.getDay(); + const numDisplayedFromPreviousMonth = + difference > 0 ? 7 - difference : -difference; + const startDate = new Date(firstDayOfMonth.getTime()); if (numDisplayedFromPreviousMonth > 0) { startDate.setDate(-numDisplayedFromPreviousMonth + 1); @@ -414,13 +424,13 @@ export class MonthViewComponent } getTitle(): string { - const currentViewStartDate = this.range.startTime, - date = currentViewStartDate.getDate(), - month = (currentViewStartDate.getMonth() + (date !== 1 ? 1 : 0)) % 12, - year = - currentViewStartDate.getFullYear() + - (date !== 1 && month === 0 ? 1 : 0), - headerDate = new Date(year, month, 1, 12, 0, 0, 0); + const currentViewStartDate = this.range.startTime; + const date = currentViewStartDate.getDate(); + const month = (currentViewStartDate.getMonth() + (date !== 1 ? 1 : 0)) % 12; + const year = + currentViewStartDate.getFullYear() + (date !== 1 && month === 0 ? 1 : 0); + const headerDate = new Date(year, month, 1, 12, 0, 0, 0); + return this.formatTitle(headerDate); } @@ -439,16 +449,16 @@ export class MonthViewComponent return; } - const selectedDate = viewDate.date, - events = viewDate.events; + const selectedDate = viewDate.date; + const events = viewDate.events; if (!viewDate.disabled) { - const dates = this.view.dates, - currentCalendarDate = this.calendarService.currentDate, - currentMonth = currentCalendarDate.getMonth(), - currentYear = currentCalendarDate.getFullYear(), - selectedMonth = selectedDate.getMonth(), - selectedYear = selectedDate.getFullYear(); + const dates = this.view.dates; + const currentCalendarDate = this.calendarService.currentDate; + const currentMonth = currentCalendarDate.getMonth(); + const currentYear = currentCalendarDate.getFullYear(); + const selectedMonth = selectedDate.getMonth(); + const selectedYear = selectedDate.getFullYear(); let direction = 0; if (currentYear === selectedYear) { @@ -461,16 +471,16 @@ export class MonthViewComponent this.calendarService.setCurrentDate(selectedDate); if (direction === 0) { - const currentViewStartDate = this.range.startTime, - oneDay = 86400000, - selectedDayDifference = Math.floor( - (selectedDate.getTime() - - currentViewStartDate.getTime() - - (selectedDate.getTimezoneOffset() - - currentViewStartDate.getTimezoneOffset()) * - 60000) / - oneDay - ); + const currentViewStartDate = this.range.startTime; + const oneDay = 86400000; + const selectedDayDifference = Math.floor( + (selectedDate.getTime() - + currentViewStartDate.getTime() - + (selectedDate.getTimezoneOffset() - + currentViewStartDate.getTimezoneOffset()) * + 60000) / + oneDay + ); for (let r = 0; r < 42; r += 1) { dates[r].selected = false; diff --git a/src/index.html b/src/index.html index 1be3ff3..e045faf 100644 --- a/src/index.html +++ b/src/index.html @@ -29,7 +29,7 @@ <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@100;300;400;500;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://rsms.me/inter/inter.css"> </head> -<body class="font-sans antialiased text-slate-500"> +<body class="font-sans antialiased text-slate-500 dark:text-slate-400"> <admin-root></admin-root> </body> </html> From 69afdff2c3e4f19d5cbe24db0f32290469232e9d Mon Sep 17 00:00:00 2001 From: Arthur Monney <monneylobe@gmail.com> Date: Tue, 25 Oct 2022 13:04:55 +0100 Subject: [PATCH 9/9] :sparkles: Finish calendar month view --- .../calendar/calendar/calendar.component.html | 16 ++ .../calendar/calendar/calendar.component.ts | 2 + .../calendar/month/month-view.component.html | 205 +----------------- .../calendar/month/month-view.component.ts | 2 + 4 files changed, 24 insertions(+), 201 deletions(-) diff --git a/src/app/shared/components/calendar/calendar/calendar.component.html b/src/app/shared/components/calendar/calendar/calendar.component.html index 19af189..5152a85 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.html +++ b/src/app/shared/components/calendar/calendar/calendar.component.html @@ -11,6 +11,21 @@ </li> </ol> </ng-template> +<ng-template #monthViewDefaultDisplayResponsiveEventTemplate let-view="view"> + <time [attr.datetime]="view.date" + [ngClass]="{ 'flex h-6 w-6 items-center justify-center rounded-full bg-primary-600 font-semibold text-white' : view.current }" + class="ml-auto" + >{{ view.label }}</time> + <span *ngIf="view.events.length === 0" class="sr-only">0 évènement</span> + <ng-template [ngIf]="view.events.length > 0"> + <span class="sr-only">{{ view.events.length }} évènements</span> + <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> + <span *ngFor="let event of view.events" + class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400 dark:bg-gray-300" + ></span> + </span> + </ng-template> +</ng-template> <ng-template #monthViewDefaultEventDetailTemplate let-showEventDetail="showEventDetail" @@ -75,6 +90,7 @@ [eventSource]="eventSource" [markDisabled]="markDisabled" [monthViewDisplayEventTemplate]="monthViewDisplayEventTemplate || monthViewDefaultDisplayEventTemplate" + [monthViewDisplayResponsiveEventTemplate]="monthViewDisplayResponsiveEventTemplate || monthViewDefaultDisplayResponsiveEventTemplate" [monthViewEventDetailTemplate]="monthViewEventDetailTemplate|| monthViewDefaultEventDetailTemplate" [locale]="locale" [dateFormatter]="dateFormatter" diff --git a/src/app/shared/components/calendar/calendar/calendar.component.ts b/src/app/shared/components/calendar/calendar/calendar.component.ts index 8f3840c..1d9615a 100644 --- a/src/app/shared/components/calendar/calendar/calendar.component.ts +++ b/src/app/shared/components/calendar/calendar/calendar.component.ts @@ -75,6 +75,8 @@ export class CalendarComponent implements OnInit, OnDestroy { @Input() monthViewDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; @Input() + monthViewDisplayResponsiveEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + @Input() monthViewEventDetailTemplate!: TemplateRef<IMonthViewEventDetailTemplateContext>; @Input() weekViewHeaderTemplate!: TemplateRef<IDisplayWeekViewHeader>; @Input() weekViewAllDayEventTemplate!: TemplateRef<IDisplayAllDayEvent>; diff --git a/src/app/shared/components/calendar/month/month-view.component.html b/src/app/shared/components/calendar/month/month-view.component.html index dd7d69c..fd4e7b9 100644 --- a/src/app/shared/components/calendar/month/month-view.component.html +++ b/src/app/shared/components/calendar/month/month-view.component.html @@ -13,213 +13,16 @@ <div class="flex text-xs leading-6 bg-gray-200 text-slate-700 lg:flex-auto dark:bg-gray-600 dark:text-slate-300"> <!-- Desktop View --> <div class="hidden w-full lg:grid lg:grid-cols-7 lg:grid-rows-6 lg:gap-px"> - <div *ngFor="let view of view.dates" class="relative px-3 py-2" tappable [ngClass]="getHighlightClass(view)" (click)="select(view)"> + <div *ngFor="let view of view.dates" class="relative px-3 py-2 hover:cursor-pointer" tappable [ngClass]="getHighlightClass(view)" (click)="select(view)"> <ng-template [ngTemplateOutlet]="monthViewDisplayEventTemplate" [ngTemplateOutletContext]="{ view }"></ng-template> </div> </div> <!-- Mobile View --> <div class="grid w-full grid-cols-7 grid-rows-6 gap-px isolate lg:hidden"> - <!-- - Always include: "flex h-14 flex-col py-2 px-3 hover:bg-gray-100 focus:z-10" - Is current month, include: "bg-white" - Is not current month, include: "bg-gray-50" - Is selected or is today, include: "font-semibold" - Is selected, include: "text-white" - Is not selected and is today, include: "text-primary-600" - Is not selected and is current month, and is not today, include: "text-gray-900" - Is not selected, is not current month, and is not today: "text-gray-500" - --> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <!-- - Always include: "ml-auto" - Is selected, include: "flex h-6 w-6 items-center justify-center rounded-full" - Is selected and is today, include: "bg-primary-600" - Is selected and is not today, include: "bg-gray-900" - --> - <time datetime="2021-12-27" class="ml-auto">27</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2021-12-28" class="ml-auto">28</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2021-12-29" class="ml-auto">29</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2021-12-30" class="ml-auto">30</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2021-12-31" class="ml-auto">31</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-01" class="ml-auto">1</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-02" class="ml-auto">2</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-03" class="ml-auto">3</time> - <span class="sr-only">2 events</span> - <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - </span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-04" class="ml-auto">4</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-05" class="ml-auto">5</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-06" class="ml-auto">6</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-07" class="ml-auto">7</time> - <span class="sr-only">1 event</span> - <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - </span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-08" class="ml-auto">8</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-09" class="ml-auto">9</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-10" class="ml-auto">10</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-11" class="ml-auto">11</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 font-semibold bg-white text-primary-600 h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-12" class="ml-auto">12</time> - <span class="sr-only">1 event</span> - <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - </span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-13" class="ml-auto">13</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-14" class="ml-auto">14</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-15" class="ml-auto">15</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-16" class="ml-auto">16</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-17" class="ml-auto">17</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-18" class="ml-auto">18</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-19" class="ml-auto">19</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-20" class="ml-auto">20</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-21" class="ml-auto">21</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 font-semibold text-white bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-22" class="flex items-center justify-center w-6 h-6 ml-auto bg-gray-900 rounded-full">22</time> - <span class="sr-only">2 events</span> - <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - </span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-23" class="ml-auto">23</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-24" class="ml-auto">24</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-25" class="ml-auto">25</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-26" class="ml-auto">26</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-27" class="ml-auto">27</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-28" class="ml-auto">28</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-29" class="ml-auto">29</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-30" class="ml-auto">30</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-900 bg-white h-14 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-01-31" class="ml-auto">31</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-01" class="ml-auto">1</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-02" class="ml-auto">2</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-03" class="ml-auto">3</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-04" class="ml-auto">4</time> - <span class="sr-only">1 event</span> - <span class="-mx-0.5 mt-auto flex flex-wrap-reverse"> - <span class="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400"></span> - </span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-05" class="ml-auto">5</time> - <span class="sr-only">0 events</span> - </button> - <button type="button" class="flex flex-col px-3 py-2 text-gray-500 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10"> - <time datetime="2022-02-06" class="ml-auto">6</time> - <span class="sr-only">0 events</span> + <button *ngFor="let view of view.dates" type="button" class="flex flex-col px-3 py-2 text-slate-500 dark:text-slate-400 h-14 bg-gray-50 hover:bg-gray-100 focus:z-10" tappable [ngClass]="getHighlightClass(view)" (click)="select(view)"> + <ng-template [ngTemplateOutlet]="monthViewDisplayResponsiveEventTemplate" + [ngTemplateOutletContext]="{ view }"></ng-template> </button> </div> </div> diff --git a/src/app/shared/components/calendar/month/month-view.component.ts b/src/app/shared/components/calendar/month/month-view.component.ts index 1c4aa7e..401bfcc 100644 --- a/src/app/shared/components/calendar/month/month-view.component.ts +++ b/src/app/shared/components/calendar/month/month-view.component.ts @@ -34,6 +34,8 @@ export class MonthViewComponent @Input() monthViewDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; @Input() + monthViewDisplayResponsiveEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; + @Input() monthViewInactiveDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>; @Input() monthViewEventDetailTemplate!: