Content Slots
DayFlow๋ ์ฌ๋กฏ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋๋ถ์ ์ํ๋ ํ๋ ์์ํฌ(React, Vue ๋ฑ)์ UI ์ปดํฌ๋ํธ๋ฅผ ์บ๋ฆฐ๋ ์ฝ์ด ์์ง์ ์ง์ ์ฃผ์ ํ ์ ์์ต๋๋ค.
์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ ๊ฒ์ด ContentSlot ๋ฉ์ปค๋์ฆ์
๋๋ค. ์ฝ์ด ์์ง์ ๋๋ถ๋ถ์ UI ์์์ ๋ํด Preact๋ก ๋ง๋ ๊ธฐ๋ณธ ๊ตฌํ์ ์ ๊ณตํ์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์
๋์์ธ์ ๋ง์ถ๊ฑฐ๋ ํน์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๊ธฐ ์ํด ์ด๋ฅผ ๊ต์ฒดํ ์ ์์ต๋๋ค.
๋ชฉ์ฐจ
- ๋์ ๋ฐฉ์
- ์ฌ์ฉ ๊ฐ๋ฅํ ์ฌ๋กฏ
- ์ด๋ฒคํธ ์ฝํ ์ธ
- ์ด๋ฒคํธ ์์ธ ์ฝํ ์ธ
- ์ด๋ฒคํธ ์์ธ ๋ํ์์
- ๋ชจ๋ฐ์ผ ์ด๋ฒคํธ ์์ธ
- ์ฌ์ฉ์ ์ ์ ์์ ์ ํ๊ธฐ ์ฃผ์ ํ๊ธฐ
- ์ปจํ ์คํธ ๋ฉ๋ด
- ์ฐ๊ฐ ๋ทฐ ๊ทธ๋ฆฌ๋ ํ์
๋์ ๋ฐฉ์
- ์ฝ์ด๊ฐ ์ฌ๋กฏ์ ์ ์ํฉ๋๋ค:
@dayflow/core์์์ ํน์ UI ์์ญ์ดContentSlot์ผ๋ก ๊ฐ์ธ์ฌ ์์ต๋๋ค. ๊ฐ ์ฌ๋กฏ์๋generatorName๊ณผgeneratorArgs๊ฐ ์์ต๋๋ค. - ๊ตฌํ์ ์ฌ๋ฌ๋ถ์ด ์ ๊ณตํฉ๋๋ค:
DayFlowCalendar์ปดํฌ๋ํธ์ ์ปดํฌ๋ํธ๋ ๋ ๋ ํจ์๋ฅผ ์ ๋ฌํ๋ฉด, ์ด๋ํฐ๊ฐ ์ฝ์ด๊ฐ ์ง์ ํ ์ ํํ ์์น๋ก ํฌํธ์ ํตํด ๋ฐฐ์นํฉ๋๋ค.
์ฌ์ฉ ๊ฐ๋ฅํ ์ฌ๋กฏ
| ์ ๋๋ ์ดํฐ ์ด๋ฆ | ์ค๋ช | ์ธ์ |
|---|---|---|
colorPicker | ์ด๋ฒคํธ ์์์ ๊ณ ๋ฅด๋ ์์ ์์ ์ ํ๊ธฐ์ ๋๋ค. | { color, onChange, onChangeComplete } |
createCalendarDialogColorPicker | ๋ํ์์์์ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ด ํ๋ถํ ์์ ์ ํ๊ธฐ์ ๋๋ค. | { color, onChange, onAccept, onCancel, styles } |
eventContent* | ์ด๋ฒคํธ ์นด๋์ ์ฌ์ฉ์ ์ ์ ๋ ๋๋ง์
๋๋ค(์: eventContentDay). | { event, viewType, isAllDay, isMobile, isSelected, isDragging, layout } |
eventContextMenu | ์ด๋ฒคํธ์ฉ ์ฌ์ฉ์ ์ ์ ์ฐํด๋ฆญ ์ปจํ ์คํธ ๋ฉ๋ด์ ๋๋ค. | { event, onClose } |
eventDetailContent | ์ด๋ฒคํธ ์์ธ๋ฅผ ์ด์์ ๋ ํ์ค๋ฒยทํจ๋์ ํ์๋๋ ๋ด์ฉ์ ๋๋ค. | { event, isAllDay, onEventDelete, onEventUpdate, onClose, app } |
eventDetailDialog | ์ด๋ฒคํธ ์์ธ๋ฅผ ์ด ๋ ํ์ํ ์ฌ์ฉ์ ์ ์ ๋ํ์์์
๋๋ค. useCalendarApp์ useEventDetailDialog: true๊ฐ ํ์ํฉ๋๋ค. | { event, isOpen, isAllDay, onEventDelete, onEventUpdate, onClose, app } |
gridContextMenu | ์บ๋ฆฐ๋ ์ ยท๊ทธ๋ฆฌ๋์ฉ ์ฌ์ฉ์ ์ ์ ์ฐํด๋ฆญ ์ปจํ ์คํธ ๋ฉ๋ด์ ๋๋ค. | { date, viewType, onClose } |
gridPopupContent | ์ฐ๊ฐ ๋ทฐ grid ๋ชจ๋์ ์ฌ์ฉ์ ์ ์ ํ์
๋ด์ฉ์
๋๋ค(gridDateClick: 'popup'์ผ ๋). | { date, events } |
mobileEventDetail | ๋ชจ๋ฐ์ผ์ฉ ์ฌ์ฉ์ ์ ์ ์ด๋ฒคํธ ์์ธ ๋๋ก์ดยท๋ํ์์์ ๋๋ค. | { isOpen, onClose, onSave, onEventDelete, draftEvent, app, timeFormat } |
sidebarCalendarColorPicker | ์ฌ์ด๋๋ฐ์์ ์บ๋ฆฐ๋ ์์์ ๊ณ ๋ฅด๋ ์์ ์ ํ๊ธฐ์ ๋๋ค. | { color, onChange, onChangeComplete } |
titleBarSlot | ์ฌ์ด๋๋ฐ ์ ๋ชฉ ํ์์ค์ ์ถ๊ฐํ ๋ด์ฉ์ ๋๋ค. | { isCollapsed, toggleCollapsed } |
์ด๋ฒคํธ ์ฝํ ์ธ
eventContent ์ฌ๋กฏ์ ์ฌ์ฉํ๋ฉด ์บ๋ฆฐ๋์์ ์ด๋ฒคํธ๊ฐ ๋ ๋๋ง๋๋ ๋ฐฉ์์ ์์ ํ ๋ฐ๊ฟ ์ ์์ต๋๋ค. ๋ค๋ฅธ ์ฌ๋กฏ๊ณผ ๋ฌ๋ฆฌ ์ด๋ฒคํธ ์ฝํ
์ธ ๋ ๋ทฐ๋ง๋ค ๋ฐ๋์ ๋ฐ๋ก ์ง์ ํด์ผ ํฉ๋๋ค. ๊ฐ ๋ทฐ์ ์ด๋ฒคํธ ๊ตฌ์กฐ์ ๋ง๋ ๋ ์ด์์์ ์ ์งํ๊ธฐ ์ํด์์
๋๋ค.
๋ทฐ๋ณ ์ฌ๋กฏ
| ์ฌ๋กฏ ์ด๋ฆ | ์ค๋ช |
|---|---|
eventContentDay | ์ผ ๋ทฐ์ ์๊ฐ ์ง์ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentWeek | ์ฃผ ๋ทฐ์ ์๊ฐ ์ง์ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentMonth | ์ ๋ทฐ์ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentYear | ์ฐ๊ฐ ๋ทฐ์ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentAllDayDay | ์ผ ๋ทฐ์ ์ข ์ผ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentAllDayWeek | ์ฃผ ๋ทฐ์ ์ข ์ผ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentAllDayMonth | ์ ๋ทฐ์ ์ข ์ผ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
eventContentAllDayYear | ์ฐ๊ฐ ๋ทฐ์ ์ข ์ผ ์ด๋ฒคํธ๋ฅผ ๋์ฒดํฉ๋๋ค. |
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
export const EventContentShowcase = () => {
return (
<DayFlowCalendar
calendar={calendar}
// Customize event rendering for Day View
eventContentDay={({ event, isSelected }) => (
<div className='custom-event-card'>
<span>{event.title}</span>
{/* ... add your custom icons or layout */}
</div>
)}
// Customize event rendering for Month View
eventContentMonth={({ event }) => (
<div className='flex items-center gap-1'>
<span>๐๏ธ</span>
<span className='truncate'>{event.title}</span>
</div>
)}
// Omit other view overrides for brevity...
/>
);
};<template>
<DayFlowCalendar :calendar="calendar">
<!-- Customize event rendering for Day View -->
<template #eventContentDay="{ event, isSelected }">
<div class="custom-event-card">
<span>{{ event.title }}</span>
</div>
</template>
<!-- Customize event rendering for Month View -->
<template #eventContentMonth="{ event }">
<div class="flex items-center gap-1">
<span>๐๏ธ</span>
<span class="truncate">{{ event.title }}</span>
</div>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
</script><dayflow-calendar
[calendar]="calendar"
[eventContentDay]="dayContent"
[eventContentMonth]="monthContent"
>
</dayflow-calendar>
<ng-template #dayContent let-args>
<div class="custom-event-card">
<span>{{ args.event.title }}</span>
</div>
</ng-template>
<ng-template #monthContent let-args>
<div class="flex items-center gap-1">
<span>๐๏ธ</span>
<span class="truncate">{{ args.event.title }}</span>
</div>
</ng-template><DayFlowCalendar
{calendar}
eventContentDay={CustomDayContent}
eventContentMonth={CustomMonthContent}
/>
<!-- CustomDayContent.svelte -->
<script lang="ts">
import type { EventContentSlotArgs } from '@dayflow/core';
let { event } = $props<EventContentSlotArgs>();
</script>
<div class="custom-event-card">
<span>{event.title}</span>
</div>
<!-- CustomMonthContent.svelte -->
<script lang="ts">
import type { EventContentSlotArgs } from '@dayflow/core';
let { event } = $props<EventContentSlotArgs>();
</script>
<div class="flex items-center gap-1">
<span>๐๏ธ</span>
<span class="truncate">{event.title}</span>
</div>์ด๋ฒคํธ ์์ธ ์ฝํ ์ธ
eventDetailContent ์ฌ๋กฏ์ผ๋ก ์ด๋ฒคํธ ์์ธ๋ฅผ ์ด์์ ๋ ํ์ค๋ฒ๋ ํจ๋์ ํ์๋๋ ๋ด์ฉ์ ์ํ๋ ๋๋ก ๋ฐ๊ฟ ์ ์์ต๋๋ค.
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
import { useCallback } from 'react';
export const CustomDetailPanelShowcase = () => {
const detailPanel = useCallback(
({ event, onEventDelete, onEventUpdate, onClose }) => {
return (
<div className='p-4 space-y-3'>
<h5 className='font-bold'>{event.title}</h5>
<p>{event.description}</p>
<div className='flex gap-2'>
<button
onClick={() => onEventUpdate({ ...event, title: 'Updated' })}
>
Update
</button>
<button onClick={() => onEventDelete(event.id)}>Delete</button>
</div>
</div>
);
},
[]
);
return (
<DayFlowCalendar calendar={calendar} eventDetailContent={detailPanel} />
);
};<template>
<DayFlowCalendar :calendar="calendar">
<template #eventDetailContent="{ event, onEventDelete, onEventUpdate, onClose }">
<div class="p-4 space-y-3">
<h5 class="font-bold">{{ event.title }}</h5>
<p>{{ event.description }}</p>
<div class="flex gap-2">
<button @click="onEventUpdate({ ...event, title: 'Updated' })">
Update
</button>
<button @click="onEventDelete(event.id)">Delete</button>
</div>
</div>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
</script><dayflow-calendar
[calendar]="calendar"
[eventDetailContent]="detailPanel"
>
</dayflow-calendar>
<ng-template #detailPanel let-args>
<div class="p-4 space-y-3">
<h5 class="font-bold">{{ args.event.title }}</h5>
<p>{{ args.event.description }}</p>
<div class="flex gap-2">
<button (click)="args.onEventUpdate({ ...args.event, title: 'Updated' })">
Update
</button>
<button (click)="args.onEventDelete(args.event.id)">Delete</button>
</div>
</div>
</ng-template><DayFlowCalendar {calendar} eventDetailContent={CustomDetailPanel} />
<!-- CustomDetailPanel.svelte -->
<script lang="ts">
import type { EventDetailContentSlotArgs } from '@dayflow/core';
let { event, onEventDelete, onEventUpdate } = $props<EventDetailContentSlotArgs>();
</script>
<div class="p-4 space-y-3">
<h5 class="font-bold">{event.title}</h5>
<p>{event.description}</p>
<div class="flex gap-2">
<button onclick={() => onEventUpdate({ ...event, title: 'Updated' })}>
Update
</button>
<button onclick={() => onEventDelete(event.id)}>Delete</button>
</div>
</div>์ด๋ฒคํธ ์์ธ ๋ํ์์
์ด๋ฒคํธ ์์ธ๋ฅผ ๋ชจ๋ฌ ํํ๋ก ๋ณด๊ณ ํธ์งํ๊ณ ์ถ๋ค๋ฉด eventDetailDialog ์ฌ๋กฏ์ ์ฌ์ฉํ์ธ์. ๋ชจ๋ฐ์ผ ์ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์ด๋, ๋ณต์กํ ์ด๋ฒคํธ ๋ฐ์ดํฐ๋ฅผ ์ํด ๋ ๋์ ๊ณต๊ฐ์ด ํ์ํ ๋ ํนํ ์ ์ฉํฉ๋๋ค.
์ฌ์ ์กฐ๊ฑด: ๋ํ์์ ๋ชจ๋๋ฅผ ์ผ๋ ค๋ฉด
useCalendarApp์useEventDetailDialog: true๋ฅผ ์ง์ ํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ด ์ฌ๋กฏ์ ์ ํ ํธ์ถ๋์ง ์์ต๋๋ค.
๋์ ๋๊ธฐ: ์ง์ ๋ง๋ ๋ํ์์๋ก ๊ต์ฒดํ์ง ์๊ณ ๋ด์ฅ ํ๋กํ ํจ๋๋ง ์์ ๊ณ ์ถ๋ค๋ฉด, ์ฌ๋กฏ ๋์
useCalendarApp์ค์ ์useEventDetailPanel: false๋ฅผ ์ง์ ํ์ธ์.
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
import { useCallback } from 'react';
export const CustomDetailDialogShowcase = () => {
const customDialog = useCallback(({ event, isOpen, onClose }) => {
if (!isOpen) return null;
return (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/50'>
<div className='bg-white p-6 rounded-lg shadow-xl'>
<h2>{event.title}</h2>
{/* ... build your custom dialog UI here */}
<button onClick={onClose}>Close</button>
</div>
</div>
);
}, []);
return (
<DayFlowCalendar calendar={calendar} eventDetailDialog={customDialog} />
);
};<template>
<DayFlowCalendar :calendar="calendar">
<template #eventDetailDialog="{ event, isOpen, onClose }">
<div v-if="isOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white p-6 rounded-lg shadow-xl">
<h2>{{ event.title }}</h2>
<button @click="onClose()">Close</button>
</div>
</div>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
</script><dayflow-calendar
[calendar]="calendar"
[eventDetailDialog]="customDialog"
>
</dayflow-calendar>
<ng-template #customDialog let-args>
<div *ngIf="args.isOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white p-6 rounded-lg shadow-xl">
<h2>{{ args.event.title }}</h2>
<button (click)="args.onClose()">Close</button>
</div>
</div>
</ng-template><DayFlowCalendar {calendar} eventDetailDialog={CustomDialog} />
<!-- CustomDialog.svelte -->
<script lang="ts">
import type { EventDetailDialogSlotArgs } from '@dayflow/core';
let { event, isOpen, onClose } = $props<EventDetailDialogSlotArgs>();
</script>
{#if isOpen}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white p-6 rounded-lg shadow-xl">
<h2>{event.title}</h2>
<button onclick={onClose}>Close</button>
</div>
</div>
{/if}๋ชจ๋ฐ์ผ ์ด๋ฒคํธ ์์ธ
๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ(๋๋ ์์ ํ๋ฉด)์์ DayFlow๋ ์ด๋ฒคํธ ์์ฑ๊ณผ ํธ์ง์ ์ํด ์ ์ฉ mobileEventDetail ์ฌ๋กฏ์ ์ฌ์ฉํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ ์ ์ฒด ํ๋ฉด ๋๋ก์ด์
๋๋ค.
Tap an event to open the mobile drawer. This showcase runs in an iframe so DayFlow reads a real mobile viewport instead of the desktop window.
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
import { useCallback } from 'react';
export const MobileEventDetailShowcase = () => {
const customMobileDrawer = useCallback(
({ isOpen, onClose, onSave, onEventDelete, draftEvent }) => {
if (!isOpen || !draftEvent) return null;
return (
<div className='fixed inset-0 z-50 flex flex-col bg-white'>
<header className='flex items-center justify-between p-4 border-b'>
<button onClick={onClose}>Back</button>
<h2>{draftEvent.id ? 'Edit' : 'New'} Event</h2>
<button onClick={() => onSave(draftEvent)}>Save</button>
</header>
<div className='p-4'>
<input
defaultValue={draftEvent.title}
onChange={e => (draftEvent.title = e.target.value)}
/>
{/* ... build your mobile-optimized UI */}
</div>
</div>
);
},
[]
);
return (
<DayFlowCalendar
calendar={calendar}
mobileEventDetail={customMobileDrawer}
/>
);
};<template>
<DayFlowCalendar :calendar="calendar">
<template #mobileEventDetail="{ isOpen, onClose, onSave, onEventDelete, draftEvent }">
<div v-if="isOpen && draftEvent" class="fixed inset-0 z-50 flex flex-col bg-white">
<header class="flex items-center justify-between p-4 border-b">
<button @click="onClose()">Back</button>
<h2>{{ draftEvent.id ? 'Edit' : 'New' }} Event</h2>
<button @click="onSave(draftEvent)">Save</button>
</header>
<div class="p-4">
<input
:value="draftEvent.title"
@input="draftEvent.title = $event.target.value"
/>
</div>
</div>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
</script><dayflow-calendar
[calendar]="calendar"
[mobileEventDetail]="customMobileDrawer"
>
</dayflow-calendar>
<ng-template #customMobileDrawer let-args>
<div *ngIf="args.isOpen && args.draftEvent" class="fixed inset-0 z-50 flex flex-col bg-white">
<header class="flex items-center justify-between p-4 border-b">
<button (click)="args.onClose()">Back</button>
<h2>{{ args.draftEvent.id ? 'Edit' : 'New' }} Event</h2>
<button (click)="args.onSave(args.draftEvent)">Save</button>
</header>
<div class="p-4">
<input
[value]="args.draftEvent.title"
(input)="args.draftEvent.title = $any($event.target).value"
/>
</div>
</div>
</ng-template><DayFlowCalendar {calendar} mobileEventDetail={CustomMobileDrawer} />
<!-- CustomMobileDrawer.svelte -->
<script lang="ts">
import type { MobileEventDetailSlotArgs } from '@dayflow/core';
let { isOpen, onClose, onSave, draftEvent } = $props<MobileEventDetailSlotArgs>();
</script>
{#if isOpen && draftEvent}
<div class="fixed inset-0 z-50 flex flex-col bg-white">
<header class="flex items-center justify-between p-4 border-b">
<button onclick={onClose}>Back</button>
<h2>{draftEvent.id ? 'Edit' : 'New'} Event</h2>
<button onclick={() => onSave(draftEvent)}>Save</button>
</header>
<div class="p-4">
<input
value={draftEvent.title}
oninput={(e) => { if(draftEvent) draftEvent.title = e.currentTarget.value; }}
/>
</div>
</div>
{#/if}์์: ์ฌ์ฉ์ ์ ์ ์์ ์ ํ๊ธฐ ์ฃผ์ ํ๊ธฐ
DayFlow๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ด์ฅ BlossomColorPicker๋ฅผ ์ฌ์ฉํฉ๋๋ค. react-color๋ vue-color ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๊ณ ์ถ๋ค๋ฉด colorPicker ์ฌ๋กฏ์ผ๋ก ์ฃผ์
ํ ์ ์์ต๋๋ค.
React ์์
react-color๋ฅผ ์ค์นํฉ๋๋ค:
DayFlowCalendar์ ์ฃผ์
ํฉ๋๋ค:
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
import { SketchPicker, PhotoshopPicker } from 'react-color';
function MyCalendar() {
return (
<DayFlowCalendar
calendar={calendar}
colorPicker={args => (
<SketchPicker
color={args.color}
onChange={color => args.onChange({ hex: color.hex })}
/>
)}
createCalendarDialogColorPicker={args => (
<PhotoshopPicker
color={args.color}
onChange={color => args.onChange({ hex: color.hex })}
onAccept={args.onAccept}
onCancel={args.onCancel}
/>
)}
/>
);
}<template>
<DayFlowCalendar :calendar="calendar">
<template #colorPicker="args">
<SketchPicker
:color="args.color"
@change="args.onChange({ hex: $event.hex })"
/>
</template>
<template #createCalendarDialogColorPicker="args">
<PhotoshopPicker
:color="args.color"
@change="args.onChange({ hex: $event.hex })"
@accept="args.onAccept"
@cancel="args.onCancel"
/>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
import { SketchPicker, PhotoshopPicker } from 'vue-color';
</script><dayflow-calendar
[calendar]="calendar"
[colorPicker]="picker"
[createCalendarDialogColorPicker]="dialogPicker"
>
</dayflow-calendar>
<ng-template #picker let-args>
<sketch-picker
[color]="args.color"
(change)="args.onChange({ hex: $event.hex })"
>
</sketch-picker>
</ng-template>
<ng-template #dialogPicker let-args>
<photoshop-picker
[color]="args.color"
(change)="args.onChange({ hex: $event.hex })"
(accept)="args.onAccept()"
(cancel)="args.onCancel()"
>
</photoshop-picker>
</ng-template><DayFlowCalendar
{calendar}
colorPicker={CustomColorPicker}
createCalendarDialogColorPicker={CustomDialogColorPicker}
/>
<!-- CustomColorPicker.svelte -->
<script lang="ts">
import type { ColorPickerSlotArgs } from '@dayflow/core';
let { color, onChange } = $props<ColorPickerSlotArgs>();
</script>
<!-- Render custom picker element/component here -->
<!-- CustomDialogColorPicker.svelte -->
<script lang="ts">
import type { CreateCalendarDialogColorPickerSlotArgs } from '@dayflow/core';
let { color, onChange, onAccept, onCancel } = $props<CreateCalendarDialogColorPickerSlotArgs>();
</script>
<!-- Render custom dialog picker element/component here -->์ปจํ ์คํธ ๋ฉ๋ด ์ฌ๋กฏ
eventContextMenu์ gridContextMenu ์ฌ๋กฏ์ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ ์ฐํด๋ฆญ ๋ฉ๋ด๋ฅผ ์ง์ ๋ง๋ React ์ปดํฌ๋ํธ๋ก ์์ ํ ๊ต์ฒดํ ์ ์์ต๋๋ค. ๋ ์ฌ๋กฏ ๋ชจ๋ ๋ฉ๋ด๋ฅผ ๋ซ๋ onClose ์ฝ๋ฐฑ์ ์ ๋ฌ๋ฐ์ต๋๋ค.
eventContextMenuโ ์ด๋ฒคํธ๋ฅผ ๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ๋ฒํผ์ผ๋ก ํด๋ฆญํ ๋ ํธ์ถ๋ฉ๋๋ค.{ event, onClose }๋ฅผ ์ ๋ฌ๋ฐ์ต๋๋ค.gridContextMenuโ ์บ๋ฆฐ๋ ๊ทธ๋ฆฌ๋์ ๋น ์์ญ์ ๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ๋ฒํผ์ผ๋ก ํด๋ฆญํ ๋ ํธ์ถ๋ฉ๋๋ค.{ date, viewType, onClose }๋ฅผ ์ ๋ฌ๋ฐ์ต๋๋ค.
์๋ฌด ์ด๋ฒคํธ๋ ๋น ์ ์ ๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ๋ฒํผ์ผ๋ก ํด๋ฆญํด ์ฌ์ฉ์ ์ ์ ๋ฉ๋ด๋ฅผ ํ์ธํด ๋ณด์ธ์:
์์ค ์ฝ๋ ๋ณด๊ธฐ
import { DayFlowCalendar } from '@dayflow/react';
import type {
EventContextMenuSlotArgs,
GridContextMenuSlotArgs,
} from '@dayflow/core';
import { useCallback } from 'react';
function MyCalendar() {
const eventContextMenu = useCallback(
({ event, onClose }: EventContextMenuSlotArgs) => (
<div className='custom-menu'>
<button
onClick={() => {
/* duplicate */ onClose();
}}
>
Duplicate
</button>
<button
onClick={() => {
/* share */ onClose();
}}
>
Share
</button>
<button
onClick={() => {
calendar.deleteEvent(event.id);
onClose();
}}
>
Delete
</button>
</div>
),
[]
);
const gridContextMenu = useCallback(
({ date, onClose }: GridContextMenuSlotArgs) => (
<div className='custom-menu'>
<button
onClick={() => {
/* create event at date */ onClose();
}}
>
New Event
</button>
<button
onClick={() => {
/* set reminder */ onClose();
}}
>
Remind me
</button>
</div>
),
[]
);
return (
<DayFlowCalendar
calendar={calendar}
eventContextMenu={eventContextMenu}
gridContextMenu={gridContextMenu}
/>
);
}<template>
<DayFlowCalendar :calendar="calendar">
<template #eventContextMenu="{ event, onClose }">
<div class="custom-menu">
<button @click="onClose()">Duplicate</button>
<button @click="onClose()">Share</button>
<button @click="calendar.deleteEvent(event.id); onClose()">Delete</button>
</div>
</template>
<template #gridContextMenu="{ date, onClose }">
<div class="custom-menu">
<button @click="onClose()">New Event</button>
<button @click="onClose()">Remind me</button>
</div>
</template>
</DayFlowCalendar>
</template>
<script setup>
import { DayFlowCalendar } from '@dayflow/vue';
</script><dayflow-calendar
[calendar]="calendar"
[eventContextMenu]="eventMenu"
[gridContextMenu]="gridMenu"
>
</dayflow-calendar>
<ng-template #eventMenu let-args>
<div class="custom-menu">
<button (click)="args.onClose()">Duplicate</button>
<button (click)="args.onClose()">Share</button>
<button (click)="calendar.deleteEvent(args.event.id); args.onClose()">Delete</button>
</div>
</ng-template>
<ng-template #gridMenu let-args>
<div class="custom-menu">
<button (click)="args.onClose()">New Event</button>
<button (click)="args.onClose()">Remind me</button>
</div>
</ng-template><DayFlowCalendar
{calendar}
eventContextMenu={CustomEventMenu}
gridContextMenu={CustomGridMenu}
/>
<!-- CustomEventMenu.svelte -->
<script lang="ts">
import type { EventContextMenuSlotArgs } from '@dayflow/core';
let { event, onClose, calendar } = $props<EventContextMenuSlotArgs>();
</script>
<div class="custom-menu">
<button onclick={onClose}>Duplicate</button>
<button onclick={onClose}>Share</button>
<button onclick={() => { calendar.deleteEvent(event.id); onClose(); }}>Delete</button>
</div>
<!-- CustomGridMenu.svelte -->
<script lang="ts">
import type { GridContextMenuSlotArgs } from '@dayflow/core';
let { onClose } = $props<GridContextMenuSlotArgs>();
</script>
<div class="custom-menu">
<button onclick={onClose}>New Event</button>
<button onclick={onClose}>Remind me</button>
</div>์ฐ๊ฐ ๋ทฐ ๊ทธ๋ฆฌ๋ ํ์
์ฐ๊ฐ ๋ทฐ๊ฐ grid ๋ชจ๋์ด๊ณ gridDateClick: 'popup'(๊ธฐ๋ณธ๊ฐ)์ผ ๋, ๋ ์ง ์
์ ํด๋ฆญํ๋ฉด ๊ทธ๋ ์ ์ด๋ฒคํธ๋ฅผ ๋์ดํ๋ ํ์
์ด ์ด๋ฆฝ๋๋ค. gridPopupContent ์ฌ๋กฏ์ผ๋ก ์ด ํ์
์ ๋ณธ๋ฌธ์ ์ง์ ๋ง๋ UI๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค.
์ด ์ฌ๋กฏ์ createYearView๊ฐ ์๋๋ผ <DayFlowCalendar>์ prop์ผ๋ก ์ ๋ฌํด์ผ ํฉ๋๋ค. ํ๋ ์์ํฌ๊ฐ ๋ ๋๋งํ ๋
ธ๋(React, Vue ๋ฑ)๋ ๋ฐ๋์ ์ฌ๋กฏ ์์คํ
์ ๊ฑฐ์ณ์ผ ํ๋ฉฐ, ์ฝ์ด ๋ทฐ ์ค์ ์ผ๋ก ์ ๋ฌํ๋ฉด Preact๊ฐ ํฌ๋์ํฉ๋๋ค.
import {
useCalendarApp,
DayFlowCalendar,
createYearView,
ViewType,
type GridPopupContentSlotArgs,
} from '@dayflow/react';
function renderYearGridPopup({ date, events }: GridPopupContentSlotArgs) {
return (
<div className='rounded-xl border bg-white p-3 shadow-xl'>
<div className='text-sm font-semibold'>{date.toDateString()}</div>
{events.length === 0 ? (
<div className='text-gray-400'>No events</div>
) : (
events.map(e => <div key={e.id}>{e.title}</div>)
)}
</div>
);
}
function MyCalendar() {
const calendar = useCalendarApp({
views: [
createYearView({
mode: 'grid',
gridDateClick: 'popup',
}),
],
defaultView: ViewType.YEAR,
});
return (
<DayFlowCalendar
calendar={calendar}
gridPopupContent={renderYearGridPopup}
/>
);
}