pimelon-ui 0.1.122 → 0.1.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/melon/Help/HelpModal.vue +1 -1
- package/melon/HelpCenter/HelpCenter.vue +1 -1
- package/package.json +1 -1
- package/src/components/Calendar/Calendar.vue +40 -28
- package/src/components/Calendar/CalendarDaily.vue +22 -10
- package/src/components/Calendar/CalendarEvent.vue +27 -15
- package/src/components/Calendar/CalendarMonthly.vue +33 -14
- package/src/components/Calendar/CalendarTimeMarker.vue +1 -1
- package/src/components/Calendar/CalendarWeekly.vue +33 -13
- package/src/components/Calendar/EventModalContent.vue +1 -1
- package/src/components/Calendar/ShowMoreCalendarEvent.vue +1 -1
- package/src/components/Calendar/calendarUtils.js +49 -0
- package/src/components/Calendar.story.md +5 -5
- package/src/components/Calendar.story.vue +28 -18
- package/src/components/TextEditor/TextEditor.story.vue +1 -1
- package/src/components/TextEditor/emoji-extension.js +18 -0
package/melon/Help/HelpModal.vue
CHANGED
package/package.json
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
<div class="mb-2 flex justify-between">
|
|
15
15
|
<!-- left side -->
|
|
16
16
|
<!-- Year, Month -->
|
|
17
|
-
<span class="text-
|
|
17
|
+
<span class="text-lg font-medium text-ink-gray-8">
|
|
18
|
+
{{ currentMonthYear }}
|
|
19
|
+
</span>
|
|
18
20
|
<!-- right side -->
|
|
19
21
|
<!-- actions buttons for calendar -->
|
|
20
22
|
<div class="flex gap-x-1">
|
|
@@ -64,7 +66,14 @@
|
|
|
64
66
|
:events="events"
|
|
65
67
|
:current-date="selectedDay"
|
|
66
68
|
:config="overrideConfig"
|
|
67
|
-
|
|
69
|
+
>
|
|
70
|
+
<template #header="{ parseDateWithDay, currentDate, fullDay }">
|
|
71
|
+
<slot
|
|
72
|
+
name="daily-header"
|
|
73
|
+
v-bind="{ parseDateWithDay, currentDate, fullDay }"
|
|
74
|
+
/>
|
|
75
|
+
</template>
|
|
76
|
+
</CalendarDaily>
|
|
68
77
|
|
|
69
78
|
<NewEventModal
|
|
70
79
|
v-if="showEventModal"
|
|
@@ -74,7 +83,7 @@
|
|
|
74
83
|
</div>
|
|
75
84
|
</template>
|
|
76
85
|
<script setup>
|
|
77
|
-
import { computed, onMounted, onUnmounted, provide, ref
|
|
86
|
+
import { computed, onMounted, onUnmounted, provide, ref } from 'vue'
|
|
78
87
|
import { Button } from '../Button'
|
|
79
88
|
import TabButtons from '../TabButtons.vue'
|
|
80
89
|
import {
|
|
@@ -98,18 +107,6 @@ const props = defineProps({
|
|
|
98
107
|
config: {
|
|
99
108
|
type: Object,
|
|
100
109
|
},
|
|
101
|
-
create: {
|
|
102
|
-
type: Function,
|
|
103
|
-
required: false,
|
|
104
|
-
},
|
|
105
|
-
update: {
|
|
106
|
-
type: Function,
|
|
107
|
-
required: false,
|
|
108
|
-
},
|
|
109
|
-
delete: {
|
|
110
|
-
type: Function,
|
|
111
|
-
required: false,
|
|
112
|
-
},
|
|
113
110
|
onClick: {
|
|
114
111
|
type: Function,
|
|
115
112
|
required: false,
|
|
@@ -124,6 +121,8 @@ const props = defineProps({
|
|
|
124
121
|
},
|
|
125
122
|
})
|
|
126
123
|
|
|
124
|
+
const emit = defineEmits(['create', 'update', 'delete'])
|
|
125
|
+
|
|
127
126
|
const defaultConfig = {
|
|
128
127
|
scrollToHour: 15,
|
|
129
128
|
disableModes: [],
|
|
@@ -134,6 +133,7 @@ const defaultConfig = {
|
|
|
134
133
|
hourHeight: 50,
|
|
135
134
|
enableShortcuts: true,
|
|
136
135
|
showIcon: true,
|
|
136
|
+
timeFormat: '12h',
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const overrideConfig = { ...defaultConfig, ...props.config }
|
|
@@ -174,19 +174,25 @@ provide('activeView', activeView)
|
|
|
174
174
|
provide('config', overrideConfig)
|
|
175
175
|
|
|
176
176
|
const parseEvents = computed(() => {
|
|
177
|
-
return
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
return (
|
|
178
|
+
props.events?.map((event) => {
|
|
179
|
+
const { fromDate, toDate, ...rest } = event
|
|
180
|
+
const date = parseDate(fromDate)
|
|
181
|
+
const from_time = new Date(fromDate).toLocaleTimeString()
|
|
182
|
+
const to_time = new Date(toDate).toLocaleTimeString()
|
|
183
|
+
if (event.isFullDay) {
|
|
184
|
+
return { ...rest, date }
|
|
185
|
+
}
|
|
186
|
+
return { ...rest, date, from_time, to_time }
|
|
187
|
+
}) || []
|
|
188
|
+
)
|
|
187
189
|
})
|
|
188
190
|
const events = ref(parseEvents.value)
|
|
189
191
|
|
|
192
|
+
function reloadEvents() {
|
|
193
|
+
events.value = parseEvents.value
|
|
194
|
+
}
|
|
195
|
+
|
|
190
196
|
events.value.forEach((event) => {
|
|
191
197
|
if (!event.from_time || !event.to_time) {
|
|
192
198
|
return
|
|
@@ -208,21 +214,25 @@ provide('calendarActions', {
|
|
|
208
214
|
// CRUD actions on an event
|
|
209
215
|
function createNewEvent(event) {
|
|
210
216
|
events.value.push(event)
|
|
211
|
-
|
|
217
|
+
event.fromDate = event.date + ' ' + event.from_time
|
|
218
|
+
event.toDate = event.date + ' ' + event.to_time
|
|
219
|
+
emit('create', event)
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
function updateEventState(event) {
|
|
215
223
|
const eventID = event.id
|
|
216
224
|
let eventIndex = events.value.findIndex((e) => e.id === eventID)
|
|
225
|
+
event.fromDate = event.date + ' ' + event.from_time
|
|
226
|
+
event.toDate = event.date + ' ' + event.to_time
|
|
217
227
|
events.value[eventIndex] = event
|
|
218
|
-
|
|
228
|
+
emit('update', event)
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
function deleteEvent(eventID) {
|
|
222
232
|
// Delete event
|
|
223
233
|
const eventIndex = events.value.findIndex((event) => event.id === eventID)
|
|
224
234
|
events.value.splice(eventIndex, 1)
|
|
225
|
-
|
|
235
|
+
emit('delete', eventID)
|
|
226
236
|
}
|
|
227
237
|
|
|
228
238
|
function openModal(data) {
|
|
@@ -430,4 +440,6 @@ function isCurrentMonthDate(date) {
|
|
|
430
440
|
date = new Date(date)
|
|
431
441
|
return date.getMonth() === currentMonth.value
|
|
432
442
|
}
|
|
443
|
+
|
|
444
|
+
defineExpose({ reloadEvents })
|
|
433
445
|
</script>
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-[90%] min-h-[500px] min-w-[600px]">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<slot name="header" v-bind="{ parseDateWithDay, currentDate, fullDay }">
|
|
4
|
+
<p class="pb-2 text-base font-semibold text-ink-gray-8">
|
|
5
|
+
{{ parseDateWithDay(currentDate, (fullDay = true)) }}
|
|
6
|
+
</p>
|
|
7
|
+
</slot>
|
|
6
8
|
<div class="h-full overflow-hidden">
|
|
7
9
|
<div
|
|
8
|
-
class="flex h-full w-full overflow-scroll border-
|
|
10
|
+
class="flex h-full w-full overflow-scroll border-outline-gray-1"
|
|
11
|
+
:class="[
|
|
12
|
+
config.noBorder ? 'border-t-[1px]' : 'border-[1px] border-r-0',
|
|
13
|
+
]"
|
|
9
14
|
ref="gridRef"
|
|
10
15
|
>
|
|
11
16
|
<!-- Left column -->
|
|
12
17
|
<div class="grid h-full w-16 grid-cols-1">
|
|
13
18
|
<span
|
|
14
19
|
v-for="time in 24"
|
|
15
|
-
class="flex h-[72px] items-end justify-center text-center text-sm font-normal text-gray-
|
|
20
|
+
class="flex h-[72px] items-end justify-center text-center text-sm font-normal text-ink-gray-5"
|
|
16
21
|
:style="{ height: `${hourHeight}px` }"
|
|
17
22
|
/>
|
|
18
23
|
</div>
|
|
19
24
|
|
|
20
25
|
<!-- Calendar Grid / Right Column -->
|
|
21
26
|
<div class="grid h-full w-full grid-cols-1 pb-2">
|
|
22
|
-
<div
|
|
27
|
+
<div
|
|
28
|
+
class="calendar-column relative border-r-[1px] border-l-[1px] border-outline-gray-1"
|
|
29
|
+
>
|
|
23
30
|
<!-- Top Redundant Cell before time starts for giving the calendar some space -->
|
|
24
31
|
<div
|
|
25
|
-
class="flex h-[50px] w-full flex-wrap gap-2 overflow-y-scroll border-b-[1px] border-gray-
|
|
32
|
+
class="flex h-[50px] w-full flex-wrap gap-2 overflow-y-scroll border-b-[1px] border-outline-gray-1 transition-all"
|
|
26
33
|
:style="{ height: `${config.redundantCellHeight}px` }"
|
|
27
34
|
>
|
|
28
35
|
<CalendarEvent
|
|
@@ -37,15 +44,16 @@
|
|
|
37
44
|
</div>
|
|
38
45
|
<!-- Day Grid -->
|
|
39
46
|
<div
|
|
40
|
-
class="relative flex"
|
|
41
|
-
v-for="time in
|
|
47
|
+
class="relative flex text-ink-gray-8"
|
|
48
|
+
v-for="time in timeArray"
|
|
49
|
+
:key="time"
|
|
42
50
|
:data-time-attr="time"
|
|
43
51
|
@dblclick="
|
|
44
52
|
calendarActions.handleCellDblClick($event, currentDate, time)
|
|
45
53
|
"
|
|
46
54
|
>
|
|
47
55
|
<div
|
|
48
|
-
class="w-full border-b-[1px] border-gray-
|
|
56
|
+
class="w-full border-b-[1px] border-outline-gray-1"
|
|
49
57
|
:style="{ height: `${hourHeight}px` }"
|
|
50
58
|
/>
|
|
51
59
|
</div>
|
|
@@ -77,6 +85,7 @@ import CalendarTimeMarker from './CalendarTimeMarker.vue'
|
|
|
77
85
|
import {
|
|
78
86
|
parseDate,
|
|
79
87
|
parseDateWithDay,
|
|
88
|
+
twelveHoursFormat,
|
|
80
89
|
twentyFourHoursFormat,
|
|
81
90
|
} from './calendarUtils'
|
|
82
91
|
import useCalendarData from './composables/useCalendarData'
|
|
@@ -104,6 +113,9 @@ const gridRef = ref(null)
|
|
|
104
113
|
const hourHeight = props.config.hourHeight
|
|
105
114
|
const minuteHeight = hourHeight / 60
|
|
106
115
|
|
|
116
|
+
const timeArray =
|
|
117
|
+
props.config.timeFormat == '24h' ? twentyFourHoursFormat : twelveHoursFormat
|
|
118
|
+
|
|
107
119
|
onMounted(() => {
|
|
108
120
|
const currentHour = new Date().getHours()
|
|
109
121
|
gridRef.value.scrollBy(0, currentHour * 60 * minuteHeight)
|
|
@@ -26,24 +26,31 @@
|
|
|
26
26
|
]
|
|
27
27
|
"
|
|
28
28
|
>
|
|
29
|
-
<div v-if="config.showIcon">
|
|
29
|
+
<div v-if="config.showIcon && eventIcons[props.event.type]">
|
|
30
30
|
<component
|
|
31
31
|
v-if="eventIcons[props.event.type]"
|
|
32
32
|
:is="eventIcons[props.event.type]"
|
|
33
33
|
class="h-4 w-4 text-black"
|
|
34
34
|
/>
|
|
35
|
-
<FeatherIcon v-else name="circle" class="h-4 text-black" />
|
|
36
35
|
</div>
|
|
37
36
|
|
|
38
|
-
<div
|
|
39
|
-
|
|
37
|
+
<div
|
|
38
|
+
class="flex w-fit flex-col overflow-hidden whitespace-nowrap text-gray-800"
|
|
39
|
+
>
|
|
40
|
+
<p class="text-ellipsis text-sm font-medium truncate">
|
|
40
41
|
{{ props.event.title || 'New Event' }}
|
|
41
42
|
</p>
|
|
42
43
|
<p
|
|
43
|
-
class="text-ellipsis text-xs font-normal
|
|
44
|
+
class="text-ellipsis text-xs font-normal"
|
|
44
45
|
v-if="!props.event.isFullDay"
|
|
45
46
|
>
|
|
46
|
-
{{
|
|
47
|
+
{{
|
|
48
|
+
formattedDuration(
|
|
49
|
+
updatedEvent.from_time,
|
|
50
|
+
updatedEvent.to_time,
|
|
51
|
+
config.timeFormat,
|
|
52
|
+
)
|
|
53
|
+
}}
|
|
47
54
|
</p>
|
|
48
55
|
</div>
|
|
49
56
|
</div>
|
|
@@ -74,24 +81,28 @@
|
|
|
74
81
|
]
|
|
75
82
|
"
|
|
76
83
|
>
|
|
77
|
-
<div v-if="config.showIcon">
|
|
84
|
+
<div v-if="config.showIcon && eventIcons[props.event.type]">
|
|
78
85
|
<component
|
|
79
86
|
v-if="eventIcons[props.event.type]"
|
|
80
87
|
:is="eventIcons[props.event.type]"
|
|
81
88
|
class="h-4 w-4 text-black"
|
|
82
89
|
/>
|
|
83
|
-
<FeatherIcon v-else name="circle" class="h-4 text-black" />
|
|
84
90
|
</div>
|
|
85
91
|
|
|
86
|
-
<div
|
|
87
|
-
|
|
92
|
+
<div
|
|
93
|
+
class="flex w-fit flex-col text-start overflow-hidden whitespace-nowrap text-gray-800"
|
|
94
|
+
>
|
|
95
|
+
<p class="text-sm font-medium truncate">
|
|
88
96
|
{{ props.event.title || 'New Event' }}
|
|
89
97
|
</p>
|
|
90
|
-
<p
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
<p v-if="props.event.from_time" class="text-xs font-normal">
|
|
99
|
+
{{
|
|
100
|
+
formattedDuration(
|
|
101
|
+
updatedEvent.from_time,
|
|
102
|
+
updatedEvent.to_time,
|
|
103
|
+
config.timeFormat,
|
|
104
|
+
)
|
|
105
|
+
}}
|
|
95
106
|
</p>
|
|
96
107
|
</div>
|
|
97
108
|
</div>
|
|
@@ -138,6 +149,7 @@ import {
|
|
|
138
149
|
calculateDiff,
|
|
139
150
|
parseDate,
|
|
140
151
|
colorMap,
|
|
152
|
+
formattedDuration,
|
|
141
153
|
} from './calendarUtils'
|
|
142
154
|
|
|
143
155
|
const props = defineProps({
|
|
@@ -1,42 +1,61 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex flex-1 flex-col overflow-scroll">
|
|
3
3
|
<!-- Day List -->
|
|
4
|
-
<div class="grid w-full grid-cols-7
|
|
4
|
+
<div class="grid w-full grid-cols-7 py-2">
|
|
5
5
|
<span
|
|
6
6
|
v-for="day in daysList"
|
|
7
|
-
class="text-center text-
|
|
7
|
+
class="text-center text-base text-ink-gray-5"
|
|
8
8
|
>{{ day }}</span
|
|
9
9
|
>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
<!-- Date Grid -->
|
|
13
13
|
<div
|
|
14
|
-
class="grid w-full flex-1 grid-cols-7 border-
|
|
15
|
-
:class="
|
|
14
|
+
class="grid w-full flex-1 grid-cols-7 border-outline-gray-1"
|
|
15
|
+
:class="[
|
|
16
|
+
currentMonthDates.length > 35 ? 'grid-rows-6' : 'grid-rows-5',
|
|
17
|
+
config.noBorder ? 'border-t-[0.5px]' : 'border-[0.5px]',
|
|
18
|
+
]"
|
|
16
19
|
>
|
|
17
20
|
<div
|
|
18
21
|
v-for="date in currentMonthDates"
|
|
19
|
-
class="overflow-y-auto border"
|
|
22
|
+
class="overflow-y-auto border-[0.5px]"
|
|
20
23
|
@dragover.prevent
|
|
21
24
|
@drageneter.prevent
|
|
22
25
|
@drop="onDrop($event, date)"
|
|
23
26
|
@dblclick="calendarActions.handleCellDblClick($event, date)"
|
|
24
27
|
>
|
|
25
28
|
<div
|
|
26
|
-
class="
|
|
29
|
+
class="flex justify-center font-normal"
|
|
27
30
|
:class="currentMonthDate(date) ? 'text-gray-700' : 'text-gray-200'"
|
|
28
31
|
>
|
|
29
|
-
<div
|
|
32
|
+
<div
|
|
33
|
+
class="flex gap-1 w-full flex-col items-center text-xs text-right"
|
|
34
|
+
>
|
|
30
35
|
<span
|
|
31
36
|
v-if="currentMonthDate(date)"
|
|
32
|
-
class="z-10 w-full
|
|
33
|
-
:class="
|
|
34
|
-
date.toDateString() === new Date().toDateString()
|
|
35
|
-
|
|
37
|
+
class="z-10 w-full flex justify-between items-center"
|
|
38
|
+
:class="[
|
|
39
|
+
date.toDateString() === new Date().toDateString()
|
|
40
|
+
? 'py-0.5 px-1'
|
|
41
|
+
: 'py-1 px-2',
|
|
42
|
+
]"
|
|
36
43
|
>
|
|
37
|
-
|
|
44
|
+
<div></div>
|
|
45
|
+
<div
|
|
46
|
+
:class="[
|
|
47
|
+
date.toDateString() === new Date().toDateString()
|
|
48
|
+
? 'bg-surface-gray-7 text-ink-white rounded-sm p-[2px]'
|
|
49
|
+
: 'bg-surface-white text-ink-gray-6',
|
|
50
|
+
]"
|
|
51
|
+
>
|
|
52
|
+
{{ date.getDate() }}
|
|
53
|
+
</div>
|
|
38
54
|
</span>
|
|
39
|
-
<span
|
|
55
|
+
<span
|
|
56
|
+
v-else
|
|
57
|
+
class="z-10 w-full bg-surface-white py-1 px-2 text-ink-gray-4"
|
|
58
|
+
>
|
|
40
59
|
{{ parseDateEventPopupFormat(date, (showDay = false)) }}
|
|
41
60
|
</span>
|
|
42
61
|
|
|
@@ -59,7 +78,7 @@
|
|
|
59
78
|
<div v-else class="flex w-full flex-col justify-between">
|
|
60
79
|
<ShowMoreCalendarEvent
|
|
61
80
|
v-if="timedEvents[parseDate(date)]"
|
|
62
|
-
class="z-10
|
|
81
|
+
class="z-10 cursor-pointer"
|
|
63
82
|
:draggable="config.isEditMode"
|
|
64
83
|
@dragstart="
|
|
65
84
|
onDragStart($event, timedEvents[parseDate(date)][0].id)
|
|
@@ -7,19 +7,25 @@
|
|
|
7
7
|
<span
|
|
8
8
|
v-for="date in weeklyDates"
|
|
9
9
|
class="relative p-2 text-center text-sm text-gray-600"
|
|
10
|
-
:class="
|
|
10
|
+
:class="
|
|
11
|
+
isToday(date) ? 'font-semibold text-ink-gray-8' : 'font-normal'
|
|
12
|
+
"
|
|
11
13
|
>
|
|
12
14
|
<div
|
|
13
15
|
v-if="isToday(date)"
|
|
14
|
-
class="absolute left-[45%] top-0 h-[2px] w-5 bg-gray-
|
|
16
|
+
class="absolute left-[45%] top-0 h-[2px] w-5 bg-surface-gray-7"
|
|
15
17
|
/>
|
|
16
18
|
{{ parseDateWithDay(date) }}
|
|
17
19
|
</span>
|
|
18
20
|
</div>
|
|
19
21
|
</div>
|
|
20
22
|
|
|
21
|
-
<div
|
|
22
|
-
|
|
23
|
+
<div
|
|
24
|
+
class="relative flex h-full flex-col overflow-auto border-outline-gray-1"
|
|
25
|
+
:class="[config.noBorder ? '' : 'border-b-[1px] border-l-[1px]']"
|
|
26
|
+
ref="gridRef"
|
|
27
|
+
>
|
|
28
|
+
<div class="flex">
|
|
23
29
|
<!-- Time List form 0 - 24 -->
|
|
24
30
|
<div class="grid w-16 grid-cols-1">
|
|
25
31
|
<span
|
|
@@ -35,8 +41,13 @@
|
|
|
35
41
|
<div class="grid w-full grid-cols-7">
|
|
36
42
|
<div v-for="(date, idx) in weeklyDates">
|
|
37
43
|
<div
|
|
38
|
-
class="flex w-full flex-col gap-1 border-b-[1px] border-r-[1px] border-gray-
|
|
39
|
-
:class="[
|
|
44
|
+
class="flex w-full flex-col gap-1 border-b-[1px] border-r-[1px] border-outline-gray-1 transition-all"
|
|
45
|
+
:class="[
|
|
46
|
+
idx === 0 && 'relative border-l-[1px]',
|
|
47
|
+
config.noBorder &&
|
|
48
|
+
idx === weeklyDates.length - 1 &&
|
|
49
|
+
'border-r-0',
|
|
50
|
+
]"
|
|
40
51
|
ref="allDayCells"
|
|
41
52
|
:data-date-attr="date"
|
|
42
53
|
>
|
|
@@ -46,7 +57,6 @@
|
|
|
46
57
|
class="absolute -left-[42px] bottom-[4px] cursor-pointer font-bold"
|
|
47
58
|
:icon="isCollapsed ? 'chevron-down' : 'chevron-up'"
|
|
48
59
|
variant="ghost"
|
|
49
|
-
size="lg"
|
|
50
60
|
/>
|
|
51
61
|
<div class="w-full" v-if="!isCollapsed">
|
|
52
62
|
<CalendarEvent
|
|
@@ -77,21 +87,28 @@
|
|
|
77
87
|
<!-- 7 Columns -->
|
|
78
88
|
<div
|
|
79
89
|
v-for="(date, idx) in weeklyDates"
|
|
80
|
-
class="relative w-full border-r-[1px]"
|
|
81
|
-
:class="
|
|
90
|
+
class="relative w-full border-r-[1px] border-outline-gray-1"
|
|
91
|
+
:class="[
|
|
92
|
+
idx === 0 && 'calendar-column border-l-[1px]',
|
|
93
|
+
config.noBorder &&
|
|
94
|
+
idx == weeklyDates.length - 1 &&
|
|
95
|
+
'border-r-0',
|
|
96
|
+
]"
|
|
82
97
|
:data-date-attr="date"
|
|
83
98
|
>
|
|
84
99
|
<!-- Time Grid -->
|
|
85
100
|
<div
|
|
86
|
-
class="cell relative flex cursor-pointer"
|
|
87
|
-
v-for="time in
|
|
101
|
+
class="cell relative flex cursor-pointer text-ink-gray-8"
|
|
102
|
+
v-for="(time, i) in timeArray"
|
|
103
|
+
:key="time"
|
|
88
104
|
:data-time-attr="time"
|
|
89
105
|
@dblclick.prevent="
|
|
90
106
|
calendarActions.handleCellDblClick($event, date, time)
|
|
91
107
|
"
|
|
92
108
|
>
|
|
93
109
|
<div
|
|
94
|
-
class="border-gray-
|
|
110
|
+
class="border-outline-gray-1 w-full border-b-[1px]"
|
|
111
|
+
:class="i === timeArray.length - 1 && 'border-b-0'"
|
|
95
112
|
:style="{ height: `${hourHeight}px` }"
|
|
96
113
|
/>
|
|
97
114
|
</div>
|
|
@@ -119,6 +136,7 @@ import { ref, onMounted, watch, computed, inject } from 'vue'
|
|
|
119
136
|
import CalendarEvent from './CalendarEvent.vue'
|
|
120
137
|
import CalendarTimeMarker from './CalendarTimeMarker.vue'
|
|
121
138
|
import {
|
|
139
|
+
twelveHoursFormat,
|
|
122
140
|
twentyFourHoursFormat,
|
|
123
141
|
parseDateWithDay,
|
|
124
142
|
parseDate,
|
|
@@ -150,6 +168,9 @@ const isCollapsed = ref(false)
|
|
|
150
168
|
const hourHeight = props.config.hourHeight
|
|
151
169
|
const minuteHeight = hourHeight / 60
|
|
152
170
|
|
|
171
|
+
const timeArray =
|
|
172
|
+
props.config.timeFormat == '24h' ? twentyFourHoursFormat : twelveHoursFormat
|
|
173
|
+
|
|
153
174
|
const timedEvents = computed(
|
|
154
175
|
() => useCalendarData(props.events).timedEvents.value,
|
|
155
176
|
)
|
|
@@ -237,7 +258,6 @@ watch(
|
|
|
237
258
|
|
|
238
259
|
<style>
|
|
239
260
|
.calendar-column {
|
|
240
|
-
border-left: 1px solid #e5e5e5;
|
|
241
261
|
position: relative;
|
|
242
262
|
}
|
|
243
263
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="w-80 rounded bg-surface-
|
|
2
|
+
<div class="w-80 rounded bg-surface-modal text-ink-gray-8 p-4 shadow">
|
|
3
3
|
<div class="flex flex-row-reverse gap-2">
|
|
4
4
|
<span class="cursor-pointer" @click="$emit('close')">
|
|
5
5
|
<FeatherIcon name="x" class="h-4 w-4" />
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
/>
|
|
9
9
|
<span
|
|
10
10
|
v-if="totalEventsCount > 1"
|
|
11
|
-
class="w-fit
|
|
11
|
+
class="w-fit rounded-sm p-px px-1.5 mx-1 text-base font-medium text-ink-gray-6 hover:cursor-pointer hover:bg-surface-gray-1"
|
|
12
12
|
@click="emit('showMoreEvents')"
|
|
13
13
|
>
|
|
14
14
|
+{{ totalEventsCount - 1 }} more
|
|
@@ -202,6 +202,32 @@ export const daysListFull = [
|
|
|
202
202
|
'Friday',
|
|
203
203
|
'Saturday',
|
|
204
204
|
]
|
|
205
|
+
export const twelveHoursFormat = [
|
|
206
|
+
'12 am',
|
|
207
|
+
'1 am',
|
|
208
|
+
'2 am',
|
|
209
|
+
'3 am',
|
|
210
|
+
'4 am',
|
|
211
|
+
'5 am',
|
|
212
|
+
'6 am',
|
|
213
|
+
'7 am',
|
|
214
|
+
'8 am',
|
|
215
|
+
'9 am',
|
|
216
|
+
'10 am',
|
|
217
|
+
'11 am',
|
|
218
|
+
'12 pm',
|
|
219
|
+
'1 pm',
|
|
220
|
+
'2 pm',
|
|
221
|
+
'3 pm',
|
|
222
|
+
'4 pm',
|
|
223
|
+
'5 pm',
|
|
224
|
+
'6 pm',
|
|
225
|
+
'7 pm',
|
|
226
|
+
'8 pm',
|
|
227
|
+
'9 pm',
|
|
228
|
+
'10 pm',
|
|
229
|
+
'11 pm',
|
|
230
|
+
]
|
|
205
231
|
export const twentyFourHoursFormat = [
|
|
206
232
|
'00:00',
|
|
207
233
|
'01:00',
|
|
@@ -275,3 +301,26 @@ export const colorMap = {
|
|
|
275
301
|
border_color: 'border-amber-600',
|
|
276
302
|
},
|
|
277
303
|
}
|
|
304
|
+
|
|
305
|
+
export function formattedDuration(from_time, to_time, timeFormat) {
|
|
306
|
+
from_time = formatTime(from_time, timeFormat)
|
|
307
|
+
to_time = formatTime(to_time, timeFormat)
|
|
308
|
+
return from_time + ' - ' + to_time
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function formatTime(time, format) {
|
|
312
|
+
if (format === '12h') {
|
|
313
|
+
let [hours, minutes] = time.split(':')
|
|
314
|
+
hours = parseInt(hours)
|
|
315
|
+
const ampm = hours >= 12 ? 'pm' : 'am'
|
|
316
|
+
hours = hours % 12
|
|
317
|
+
hours = hours ? hours : 12 // the hour '0' should be '12'
|
|
318
|
+
|
|
319
|
+
// if minutes is 00, remove it
|
|
320
|
+
if (minutes === '00') {
|
|
321
|
+
return `${hours} ${ampm}`
|
|
322
|
+
}
|
|
323
|
+
time = `${hours}:${minutes} ${ampm}`
|
|
324
|
+
}
|
|
325
|
+
return time
|
|
326
|
+
}
|
|
@@ -130,10 +130,10 @@ e.g.
|
|
|
130
130
|
- Many functional props are also there which will be discussed in the below
|
|
131
131
|
sections.
|
|
132
132
|
|
|
133
|
-
## Custom API Integrations
|
|
133
|
+
## Custom API Integrations
|
|
134
134
|
|
|
135
135
|
To integrate the calendar with your API, you need to pass the following
|
|
136
|
-
functions as
|
|
136
|
+
functions as emits to the Calendar component:
|
|
137
137
|
|
|
138
138
|
- create: This function is called when a new event is created from the UI. The
|
|
139
139
|
first argument in the function is the new event created.
|
|
@@ -149,9 +149,9 @@ e.g.
|
|
|
149
149
|
<Calendar
|
|
150
150
|
:config="config"
|
|
151
151
|
:events="events"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
@create="(event) => console.log('createEvent', event)"
|
|
153
|
+
@update="(event) => console.log('updateEvent', event)"
|
|
154
|
+
@delete="(eventID) => console.log('deleteEvent', eventID)"
|
|
155
155
|
/>
|
|
156
156
|
|
|
157
157
|
In these functions, you can set up your API calls to create, update, and delete
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<Calendar
|
|
6
6
|
:config="config"
|
|
7
7
|
:events="events"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
@create="(event) => logEvent('createEvent', event)"
|
|
9
|
+
@update="(event) => logEvent('updateEvent', event)"
|
|
10
|
+
@delete="(eventID) => logEvent('deleteEvent', eventID)"
|
|
11
11
|
>
|
|
12
12
|
</Calendar>
|
|
13
13
|
</div>
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<Calendar
|
|
18
18
|
:config="config"
|
|
19
19
|
:events="events"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
@create="(event) => logEvent('createEvent', event)"
|
|
21
|
+
@update="(event) => logEvent('updateEvent', event)"
|
|
22
|
+
@delete="(eventID) => logEvent('deleteEvent', eventID)"
|
|
23
23
|
>
|
|
24
24
|
<template
|
|
25
25
|
#header="{
|
|
@@ -76,14 +76,24 @@ const config = {
|
|
|
76
76
|
enableShortcuts: false,
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function getCurrentMonthYear() {
|
|
80
|
+
const date = new Date()
|
|
81
|
+
const year = date.getFullYear()
|
|
82
|
+
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
83
|
+
|
|
84
|
+
return `${year}-${month}`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const currentMonthYear = getCurrentMonthYear()
|
|
88
|
+
|
|
79
89
|
const events = ref([
|
|
80
90
|
{
|
|
81
91
|
title: 'English by Ryan Mathew',
|
|
82
92
|
participant: 'Ryan Mathew',
|
|
83
93
|
id: 'EDU-CSH-2024-00091',
|
|
84
94
|
venue: 'CNF-ROOM-2024-00001',
|
|
85
|
-
fromDate: '
|
|
86
|
-
toDate: '
|
|
95
|
+
fromDate: currentMonthYear + '-02 16:30:00', //can be a date object
|
|
96
|
+
toDate: currentMonthYear + '-02 17:30:00',
|
|
87
97
|
color: 'violet',
|
|
88
98
|
},
|
|
89
99
|
{
|
|
@@ -91,8 +101,8 @@ const events = ref([
|
|
|
91
101
|
participant: 'Ryan Mathew',
|
|
92
102
|
id: 'EDU-CSH-2024-00092',
|
|
93
103
|
venue: 'CNF-ROOM-2024-00002',
|
|
94
|
-
fromDate: '
|
|
95
|
-
toDate: '
|
|
104
|
+
fromDate: currentMonthYear + '-04 13:30:00',
|
|
105
|
+
toDate: currentMonthYear + '-04 17:30:00',
|
|
96
106
|
color: 'green',
|
|
97
107
|
},
|
|
98
108
|
{
|
|
@@ -100,8 +110,8 @@ const events = ref([
|
|
|
100
110
|
participant: 'Sheldon',
|
|
101
111
|
id: 'EDU-CSH-2024-00093',
|
|
102
112
|
venue: 'CNF-ROOM-2024-00001',
|
|
103
|
-
fromDate: '
|
|
104
|
-
toDate: '
|
|
113
|
+
fromDate: currentMonthYear + '-16 10:30:00',
|
|
114
|
+
toDate: currentMonthYear + '-16 11:30:00',
|
|
105
115
|
color: 'blue',
|
|
106
116
|
},
|
|
107
117
|
{
|
|
@@ -109,8 +119,8 @@ const events = ref([
|
|
|
109
119
|
participant: 'Ryan Mathew',
|
|
110
120
|
id: 'EDU-CSH-2024-00094',
|
|
111
121
|
venue: 'CNF-ROOM-2024-00001',
|
|
112
|
-
fromDate: '
|
|
113
|
-
toDate: '
|
|
122
|
+
fromDate: currentMonthYear + '-21 16:30:00',
|
|
123
|
+
toDate: currentMonthYear + '-21 17:30:00',
|
|
114
124
|
color: 'red',
|
|
115
125
|
},
|
|
116
126
|
{
|
|
@@ -118,8 +128,8 @@ const events = ref([
|
|
|
118
128
|
participant: 'John',
|
|
119
129
|
id: '#htrht41',
|
|
120
130
|
venue: 'Google Meet',
|
|
121
|
-
fromDate: '
|
|
122
|
-
toDate: '
|
|
131
|
+
fromDate: currentMonthYear + '-11 00:00:00',
|
|
132
|
+
toDate: currentMonthYear + '-11 23:59:59',
|
|
123
133
|
color: 'amber',
|
|
124
134
|
isFullDay: true,
|
|
125
135
|
},
|
|
@@ -128,8 +138,8 @@ const events = ref([
|
|
|
128
138
|
participant: 'Sheldon',
|
|
129
139
|
id: '#htrht42',
|
|
130
140
|
venue: 'Google Meet',
|
|
131
|
-
fromDate: '
|
|
132
|
-
toDate: '
|
|
141
|
+
fromDate: currentMonthYear + '-07 00:00:00',
|
|
142
|
+
toDate: currentMonthYear + '-07 23:59:59',
|
|
133
143
|
color: 'amber',
|
|
134
144
|
isFullDay: true,
|
|
135
145
|
},
|
|
@@ -16,7 +16,7 @@ const value = ref(`
|
|
|
16
16
|
<li>Item 2</li>
|
|
17
17
|
</ul>
|
|
18
18
|
<p>
|
|
19
|
-
<a href="https://monakerp.com">Melon</a>
|
|
19
|
+
<a href="https://devel.monakerp.com">Melon</a>
|
|
20
20
|
</p>
|
|
21
21
|
<pre><code class="language-javascript">import { Button } from 'pimelon-ui'
|
|
22
22
|
const value = ref(true);</code>
|
|
@@ -25,6 +25,24 @@ export default Extension.create({
|
|
|
25
25
|
.filter((item) =>
|
|
26
26
|
item.name.toLowerCase().includes(query.toLowerCase()),
|
|
27
27
|
)
|
|
28
|
+
.sort((a, b) => {
|
|
29
|
+
const aName = a.name.toLowerCase()
|
|
30
|
+
const bName = b.name.toLowerCase()
|
|
31
|
+
const queryLower = query.toLowerCase()
|
|
32
|
+
|
|
33
|
+
// Exact matches first
|
|
34
|
+
if (aName === queryLower && bName !== queryLower) return -1
|
|
35
|
+
if (bName === queryLower && aName !== queryLower) return 1
|
|
36
|
+
|
|
37
|
+
// Then names starting with the query
|
|
38
|
+
if (aName.startsWith(queryLower) && !bName.startsWith(queryLower))
|
|
39
|
+
return -1
|
|
40
|
+
if (bName.startsWith(queryLower) && !aName.startsWith(queryLower))
|
|
41
|
+
return 1
|
|
42
|
+
|
|
43
|
+
// Then sort by name length (shorter first)
|
|
44
|
+
return aName.length - bName.length
|
|
45
|
+
})
|
|
28
46
|
.slice(0, 5)
|
|
29
47
|
},
|
|
30
48
|
render: () => {
|