frappe-ui 0.1.192 → 0.1.193
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/package.json +2 -1
- package/src/components/Calendar/Calendar.story.md +3 -5
- package/src/components/Calendar/Calendar.story.vue +25 -14
- package/src/components/Calendar/Calendar.vue +232 -88
- package/src/components/Calendar/CalendarDaily.vue +80 -30
- package/src/components/Calendar/CalendarEvent.vue +204 -99
- package/src/components/Calendar/CalendarMonthly.vue +56 -25
- package/src/components/Calendar/CalendarTimeMarker.vue +12 -13
- package/src/components/Calendar/CalendarWeekly.vue +104 -88
- package/src/components/Calendar/DateMonthYearPicker.vue +271 -0
- package/src/components/Calendar/EventModalContent.vue +17 -6
- package/src/components/Calendar/Icon/DayIcon.vue +14 -0
- package/src/components/Calendar/Icon/MonthIcon.vue +14 -0
- package/src/components/Calendar/Icon/WeekIcon.vue +14 -0
- package/src/components/Calendar/NewEventModal.vue +24 -18
- package/src/components/Calendar/ShowMoreCalendarEvent.vue +7 -6
- package/src/components/Calendar/calendarUtils.js +135 -50
- package/src/components/Calendar/composables/useCalendarData.js +8 -6
- package/src/components/Calendar/composables/useEventModal.js +20 -6
- package/src/components/Calendar/index.ts +4 -0
- package/src/components/DatePicker/DatePicker.vue +3 -0
- package/src/components/TabButtons/TabButtons.vue +2 -2
- package/src/components/TextEditor/Menu.vue +3 -3
- package/src/components/TextEditor/TextEditor.vue +6 -1
- package/src/components/TextEditor/TextEditorFixedMenu.vue +1 -0
- package/src/components/TextEditor/commands.js +7 -0
- package/src/components/TextEditor/extensions/iframe/IframeNodeView.vue +305 -0
- package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +199 -0
- package/src/components/TextEditor/extensions/iframe/iframe-extension.ts +352 -0
- package/src/components/TextEditor/extensions/iframe/index.ts +11 -0
- package/src/components/TextEditor/extensions/iframe/utils.ts +282 -0
- package/src/components/TextEditor/extensions/slash-commands/slash-commands-extension.ts +8 -0
- package/src/components/TextEditor/types.ts +1 -0
- package/src/components/TextInput/TextInput.vue +1 -0
- package/src/index.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frappe-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.193",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@headlessui/vue": "^1.7.14",
|
|
35
35
|
"@popperjs/core": "^2.11.2",
|
|
36
36
|
"@tailwindcss/forms": "^0.5.3",
|
|
37
|
+
"@tailwindcss/line-clamp": "^0.4.4",
|
|
37
38
|
"@tailwindcss/typography": "^0.5.16",
|
|
38
39
|
"@tiptap/core": "^2.26.1",
|
|
39
40
|
"@tiptap/extension-code-block": "^2.26.1",
|
|
@@ -59,7 +59,6 @@ The object for this kind of event looks like:
|
|
|
59
59
|
defaultMode: 'Month',
|
|
60
60
|
isEditMode: false,
|
|
61
61
|
eventIcons: {},
|
|
62
|
-
redundantCellHeight: 50,
|
|
63
62
|
hourHeight: 50,
|
|
64
63
|
enableShortcuts: true,
|
|
65
64
|
showIcon: true,
|
|
@@ -90,7 +89,6 @@ e.g.
|
|
|
90
89
|
'Meeting': <MeetingIcon />,
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
- `redundantCellHeight`: The height of the cell to display full day events. This value is in Pixel, by default the value is `50px`.
|
|
94
92
|
- `hourHeight`: The height of each cell below the full day events cell. This value is in pixel, by default the value is `50px`.
|
|
95
93
|
- `enableShortcuts`: Boolean value which determines whether shortcuts will be enabled or not. By default the value is true i.e. shortcuts will be enabled, can be disabled by setting it to false, currently the calendar supports shortcuts like
|
|
96
94
|
|
|
@@ -141,7 +139,7 @@ If you wish to handle clicks on your own, the Calendar provides 3 functions to h
|
|
|
141
139
|
:events="events"
|
|
142
140
|
:onClick="(event) => console.log('onClick', event)"
|
|
143
141
|
:onDblClick="(event) => console.log('onDblClick', event)"
|
|
144
|
-
:
|
|
142
|
+
:onCellClick="(data) => console.log('onCellClick', data)"
|
|
145
143
|
/>
|
|
146
144
|
|
|
147
145
|
`Note: while using custom click events, the create, update & delete prop functions will not be triggered.`
|
|
@@ -166,7 +164,7 @@ If you wish to handle clicks on your own, the Calendar provides 3 functions to h
|
|
|
166
164
|
- e: this key represent the MouseEvent.
|
|
167
165
|
- calendarEvent: This key is an object, the object of calendarEvent is displayed above
|
|
168
166
|
|
|
169
|
-
- `
|
|
167
|
+
- `onCellClick`: The function is triggered when a cell is clicked. In the callback function you receive an argument which is an object and it looks like this:
|
|
170
168
|
|
|
171
169
|
{
|
|
172
170
|
e:MouseEvent,
|
|
@@ -176,7 +174,7 @@ If you wish to handle clicks on your own, the Calendar provides 3 functions to h
|
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
- e: this key represent the MouseEvent.
|
|
179
|
-
- date: Date Object, which has the date of the cell which was
|
|
177
|
+
- date: Date Object, which has the date of the cell which was clicked.
|
|
180
178
|
- time: String, ranges from "00:00" to "23:00", where the cell was clicked in the grid that time value will be displayed over here. (Note, this will be empty in Month view)
|
|
181
179
|
- view: String, shows the view in which the event was triggered.
|
|
182
180
|
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
:events="events"
|
|
55
55
|
:onClick="(event) => logEvent('onClick', event)"
|
|
56
56
|
:onDblClick="(event) => logEvent('onDblClick', event)"
|
|
57
|
-
:
|
|
57
|
+
:onCellClick="(data) => logEvent('onCellClick', data)"
|
|
58
58
|
>
|
|
59
59
|
</Calendar>
|
|
60
60
|
</div>
|
|
@@ -72,7 +72,6 @@ const config = {
|
|
|
72
72
|
isEditMode: true,
|
|
73
73
|
eventIcons: {},
|
|
74
74
|
allowCustomClickEvents: true,
|
|
75
|
-
redundantCellHeight: 100,
|
|
76
75
|
enableShortcuts: false,
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -92,8 +91,10 @@ const events = ref([
|
|
|
92
91
|
participant: 'Ryan Mathew',
|
|
93
92
|
id: 'EDU-CSH-2024-00091',
|
|
94
93
|
venue: 'CNF-ROOM-2024-00001',
|
|
95
|
-
fromDate: currentMonthYear + '-02
|
|
96
|
-
toDate: currentMonthYear + '-02
|
|
94
|
+
fromDate: currentMonthYear + '-02', //can be a date object
|
|
95
|
+
toDate: currentMonthYear + '-02',
|
|
96
|
+
fromTime: '16:30',
|
|
97
|
+
toTime: '17:30',
|
|
97
98
|
color: 'violet',
|
|
98
99
|
},
|
|
99
100
|
{
|
|
@@ -101,8 +102,10 @@ const events = ref([
|
|
|
101
102
|
participant: 'Ryan Mathew',
|
|
102
103
|
id: 'EDU-CSH-2024-00092',
|
|
103
104
|
venue: 'CNF-ROOM-2024-00002',
|
|
104
|
-
fromDate: currentMonthYear + '-04
|
|
105
|
-
toDate: currentMonthYear + '-04
|
|
105
|
+
fromDate: currentMonthYear + '-04',
|
|
106
|
+
toDate: currentMonthYear + '-04',
|
|
107
|
+
fromTime: '13:30',
|
|
108
|
+
toTime: '17:30',
|
|
106
109
|
color: 'green',
|
|
107
110
|
},
|
|
108
111
|
{
|
|
@@ -110,8 +113,10 @@ const events = ref([
|
|
|
110
113
|
participant: 'Sheldon',
|
|
111
114
|
id: 'EDU-CSH-2024-00093',
|
|
112
115
|
venue: 'CNF-ROOM-2024-00001',
|
|
113
|
-
fromDate: currentMonthYear + '-16
|
|
114
|
-
toDate: currentMonthYear + '-16
|
|
116
|
+
fromDate: currentMonthYear + '-16',
|
|
117
|
+
toDate: currentMonthYear + '-16',
|
|
118
|
+
fromTime: '10:30',
|
|
119
|
+
toTime: '11:30',
|
|
115
120
|
color: 'blue',
|
|
116
121
|
},
|
|
117
122
|
{
|
|
@@ -119,8 +124,10 @@ const events = ref([
|
|
|
119
124
|
participant: 'Ryan Mathew',
|
|
120
125
|
id: 'EDU-CSH-2024-00094',
|
|
121
126
|
venue: 'CNF-ROOM-2024-00001',
|
|
122
|
-
fromDate: currentMonthYear + '-21
|
|
123
|
-
toDate: currentMonthYear + '-21
|
|
127
|
+
fromDate: currentMonthYear + '-21',
|
|
128
|
+
toDate: currentMonthYear + '-21',
|
|
129
|
+
fromTime: '16:30',
|
|
130
|
+
toTime: '17:30',
|
|
124
131
|
color: 'red',
|
|
125
132
|
},
|
|
126
133
|
{
|
|
@@ -128,8 +135,10 @@ const events = ref([
|
|
|
128
135
|
participant: 'John',
|
|
129
136
|
id: '#htrht41',
|
|
130
137
|
venue: 'Google Meet',
|
|
131
|
-
fromDate: currentMonthYear + '-11
|
|
132
|
-
toDate: currentMonthYear + '-11
|
|
138
|
+
fromDate: currentMonthYear + '-11',
|
|
139
|
+
toDate: currentMonthYear + '-11',
|
|
140
|
+
fromTime: '00:00',
|
|
141
|
+
toTime: '02:00',
|
|
133
142
|
color: 'amber',
|
|
134
143
|
isFullDay: true,
|
|
135
144
|
},
|
|
@@ -138,8 +147,10 @@ const events = ref([
|
|
|
138
147
|
participant: 'Sheldon',
|
|
139
148
|
id: '#htrht42',
|
|
140
149
|
venue: 'Google Meet',
|
|
141
|
-
fromDate: currentMonthYear + '-07
|
|
142
|
-
toDate: currentMonthYear + '-07
|
|
150
|
+
fromDate: currentMonthYear + '-07',
|
|
151
|
+
toDate: currentMonthYear + '-07',
|
|
152
|
+
fromTime: '00:00',
|
|
153
|
+
toTime: '02:00',
|
|
143
154
|
color: 'amber',
|
|
144
155
|
isFullDay: true,
|
|
145
156
|
},
|
|
@@ -4,36 +4,36 @@
|
|
|
4
4
|
name="header"
|
|
5
5
|
v-bind="{
|
|
6
6
|
currentMonthYear,
|
|
7
|
+
currentYear,
|
|
8
|
+
currentMonth,
|
|
7
9
|
enabledModes,
|
|
8
10
|
activeView,
|
|
9
11
|
decrement,
|
|
10
12
|
increment,
|
|
11
13
|
updateActiveView,
|
|
14
|
+
setCalendarDate,
|
|
15
|
+
onMonthYearChange,
|
|
16
|
+
selectedMonthDate,
|
|
12
17
|
}"
|
|
13
18
|
>
|
|
14
19
|
<div class="mb-2 flex justify-between">
|
|
15
20
|
<!-- left side -->
|
|
16
21
|
<!-- Year, Month -->
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
<div class="flex items-center">
|
|
23
|
+
<DateMonthYearPicker
|
|
24
|
+
:modelValue="selectedMonthDate"
|
|
25
|
+
:formatter="() => currentMonthYear"
|
|
26
|
+
@update:modelValue="(val) => onMonthYearChange(val)"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
20
29
|
<!-- right side -->
|
|
21
30
|
<!-- actions buttons for calendar -->
|
|
22
31
|
<div class="flex gap-x-1">
|
|
23
32
|
<!-- Increment and Decrement Button-->
|
|
24
33
|
|
|
25
|
-
<Button
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class="h-4 w-4"
|
|
29
|
-
icon="chevron-left"
|
|
30
|
-
/>
|
|
31
|
-
<Button
|
|
32
|
-
@click="increment()"
|
|
33
|
-
variant="ghost"
|
|
34
|
-
class="h-4 w-4"
|
|
35
|
-
icon="chevron-right"
|
|
36
|
-
/>
|
|
34
|
+
<Button @click="decrement" variant="ghost" icon="chevron-left" />
|
|
35
|
+
<Button label="Today" @click="setCalendarDate()" variant="ghost" />
|
|
36
|
+
<Button @click="increment" variant="ghost" icon="chevron-right" />
|
|
37
37
|
|
|
38
38
|
<!-- View change button, default is months or can be set via props! -->
|
|
39
39
|
<TabButtons
|
|
@@ -83,15 +83,29 @@
|
|
|
83
83
|
</div>
|
|
84
84
|
</template>
|
|
85
85
|
<script setup>
|
|
86
|
-
import {
|
|
86
|
+
import {
|
|
87
|
+
computed,
|
|
88
|
+
onMounted,
|
|
89
|
+
onUnmounted,
|
|
90
|
+
provide,
|
|
91
|
+
ref,
|
|
92
|
+
watch,
|
|
93
|
+
nextTick,
|
|
94
|
+
} from 'vue'
|
|
87
95
|
import { Button } from '../Button'
|
|
88
96
|
import { TabButtons } from '../TabButtons'
|
|
89
97
|
import {
|
|
90
98
|
getCalendarDates,
|
|
91
99
|
monthList,
|
|
92
|
-
|
|
100
|
+
handleSeconds,
|
|
101
|
+
formatMonthYear,
|
|
102
|
+
getWeekMonthParts,
|
|
93
103
|
} from './calendarUtils'
|
|
94
|
-
import { dayjs } from
|
|
104
|
+
import { dayjs } from '../../utils/dayjs'
|
|
105
|
+
import DayIcon from './Icon/DayIcon.vue'
|
|
106
|
+
import WeekIcon from './Icon/WeekIcon.vue'
|
|
107
|
+
import MonthIcon from './Icon/MonthIcon.vue'
|
|
108
|
+
import DateMonthYearPicker from './DateMonthYearPicker.vue'
|
|
95
109
|
import CalendarMonthly from './CalendarMonthly.vue'
|
|
96
110
|
import CalendarWeekly from './CalendarWeekly.vue'
|
|
97
111
|
import CalendarDaily from './CalendarDaily.vue'
|
|
@@ -115,7 +129,7 @@ const props = defineProps({
|
|
|
115
129
|
type: Function,
|
|
116
130
|
required: false,
|
|
117
131
|
},
|
|
118
|
-
|
|
132
|
+
onCellClick: {
|
|
119
133
|
type: Function,
|
|
120
134
|
required: false,
|
|
121
135
|
},
|
|
@@ -129,19 +143,50 @@ const defaultConfig = {
|
|
|
129
143
|
defaultMode: 'Month',
|
|
130
144
|
isEditMode: false,
|
|
131
145
|
eventIcons: {},
|
|
132
|
-
redundantCellHeight: 50,
|
|
133
146
|
hourHeight: 50,
|
|
134
147
|
enableShortcuts: true,
|
|
135
148
|
showIcon: true,
|
|
136
149
|
timeFormat: '12h',
|
|
150
|
+
weekends: ['sunday'],
|
|
137
151
|
}
|
|
138
152
|
|
|
139
153
|
const overrideConfig = { ...defaultConfig, ...props.config }
|
|
140
154
|
let activeView = ref(overrideConfig.defaultMode)
|
|
141
155
|
|
|
142
|
-
function updateActiveView(value) {
|
|
143
|
-
console.log(value)
|
|
156
|
+
function updateActiveView(value, d, isPreviousMonth, isNextMonth) {
|
|
144
157
|
activeView.value = value
|
|
158
|
+
if (value == 'Day' && d) {
|
|
159
|
+
date.value = findIndexOfDate(d)
|
|
160
|
+
isPreviousMonth && decrementMonth()
|
|
161
|
+
isNextMonth && incrementMonth()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const selectedMonthDate = ref(dayjs().format('YYYY-MM-DD'))
|
|
166
|
+
|
|
167
|
+
function onMonthYearChange(val = '') {
|
|
168
|
+
const d = dayjs(val)
|
|
169
|
+
selectedMonthDate.value = d.format('YYYY-MM-DD')
|
|
170
|
+
|
|
171
|
+
setCalendarDate(selectedMonthDate.value)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function syncSelectedMonth(year, month) {
|
|
175
|
+
// Keep same day if possible; otherwise clamp to last day
|
|
176
|
+
if (typeof year === 'number' && typeof month === 'number') {
|
|
177
|
+
const currentDay = dayjs(selectedMonthDate.value).date()
|
|
178
|
+
|
|
179
|
+
let tentative = dayjs(
|
|
180
|
+
`${year}-${String(month + 1).padStart(2, '0')}-01`,
|
|
181
|
+
).date(currentDay)
|
|
182
|
+
|
|
183
|
+
if (tentative.month() !== month) {
|
|
184
|
+
// overflowed into next month, use last day of target month
|
|
185
|
+
tentative = tentative.startOf('month').month(month).endOf('month')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
selectedMonthDate.value = tentative.format('YYYY-MM-DD')
|
|
189
|
+
}
|
|
145
190
|
}
|
|
146
191
|
|
|
147
192
|
// shortcuts for changing the active view and navigating through the calendar
|
|
@@ -176,46 +221,67 @@ provide('config', overrideConfig)
|
|
|
176
221
|
const parseEvents = computed(() => {
|
|
177
222
|
return (
|
|
178
223
|
props.events?.map((event) => {
|
|
179
|
-
const { fromDate, toDate, ...rest } = event
|
|
180
|
-
const date =
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
224
|
+
const { fromDate, toDate, fromTime, toTime, ...rest } = event
|
|
225
|
+
const date = fromDate
|
|
226
|
+
const fromDateTime = fromDate + ' ' + fromTime
|
|
227
|
+
const toDateTime = toDate + ' ' + toTime
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
...rest,
|
|
231
|
+
date,
|
|
232
|
+
fromDateTime,
|
|
233
|
+
toDateTime,
|
|
234
|
+
fromDate,
|
|
235
|
+
toDate,
|
|
236
|
+
fromTime,
|
|
237
|
+
toTime,
|
|
185
238
|
}
|
|
186
|
-
return { ...rest, date, from_time, to_time }
|
|
187
239
|
}) || []
|
|
188
240
|
)
|
|
189
241
|
})
|
|
190
242
|
const events = ref(parseEvents.value)
|
|
191
243
|
|
|
244
|
+
watch(
|
|
245
|
+
() => props.events,
|
|
246
|
+
() => reloadEvents(),
|
|
247
|
+
{ deep: true },
|
|
248
|
+
)
|
|
249
|
+
|
|
192
250
|
function reloadEvents() {
|
|
193
251
|
events.value = parseEvents.value
|
|
194
252
|
}
|
|
195
253
|
|
|
254
|
+
events.value.forEach((event) => {
|
|
255
|
+
if (!event.fromTime || !event.toTime) return
|
|
256
|
+
|
|
257
|
+
event.fromTime = handleSeconds(event.fromTime)
|
|
258
|
+
event.toTime = handleSeconds(event.toTime)
|
|
259
|
+
})
|
|
260
|
+
|
|
196
261
|
const { showEventModal, newEvent, openNewEventModal } = useEventModal()
|
|
197
262
|
|
|
198
263
|
provide('calendarActions', {
|
|
199
264
|
createNewEvent,
|
|
200
265
|
updateEventState,
|
|
201
266
|
deleteEvent,
|
|
202
|
-
|
|
267
|
+
handleCellClick,
|
|
268
|
+
updateActiveView,
|
|
203
269
|
props,
|
|
204
270
|
})
|
|
205
271
|
|
|
206
272
|
// CRUD actions on an event
|
|
207
273
|
function createNewEvent(event) {
|
|
208
274
|
events.value.push(event)
|
|
209
|
-
event.
|
|
210
|
-
event.
|
|
275
|
+
event.fromDateTime = event.fromDate + ' ' + event.fromTime
|
|
276
|
+
event.toDateTime = event.toDate + ' ' + event.toTime
|
|
211
277
|
emit('create', event)
|
|
212
278
|
}
|
|
213
279
|
|
|
214
280
|
function updateEventState(event) {
|
|
215
281
|
const eventID = event.id
|
|
216
282
|
let eventIndex = events.value.findIndex((e) => e.id === eventID)
|
|
217
|
-
event.
|
|
218
|
-
event.
|
|
283
|
+
event.fromDateTime = event.fromDate + ' ' + event.fromTime
|
|
284
|
+
event.toDateTime = event.toDate + ' ' + event.toTime
|
|
219
285
|
events.value[eventIndex] = event
|
|
220
286
|
emit('update', event)
|
|
221
287
|
}
|
|
@@ -228,21 +294,22 @@ function deleteEvent(eventID) {
|
|
|
228
294
|
}
|
|
229
295
|
|
|
230
296
|
function openModal(data) {
|
|
231
|
-
const { e, view, date, time } = data
|
|
297
|
+
const { e, view, date, time, isFullDay } = data
|
|
232
298
|
const config = overrideConfig.isEditMode
|
|
233
|
-
openNewEventModal(e, view, date, config, time)
|
|
299
|
+
openNewEventModal(e, view, date, config, time, isFullDay)
|
|
234
300
|
}
|
|
235
301
|
|
|
236
|
-
function
|
|
302
|
+
function handleCellClick(e, date, time = '', isFullDay = false) {
|
|
237
303
|
const data = {
|
|
238
304
|
e,
|
|
239
305
|
view: activeView.value,
|
|
240
306
|
date,
|
|
241
307
|
time,
|
|
308
|
+
isFullDay,
|
|
242
309
|
}
|
|
243
310
|
|
|
244
|
-
if (props.
|
|
245
|
-
props.
|
|
311
|
+
if (props.onCellClick) {
|
|
312
|
+
props.onCellClick(data)
|
|
246
313
|
return
|
|
247
314
|
}
|
|
248
315
|
openModal(data)
|
|
@@ -250,12 +317,12 @@ function handleCellDblClick(e, date, time = '') {
|
|
|
250
317
|
|
|
251
318
|
// Calendar View Options
|
|
252
319
|
const actionOptions = [
|
|
253
|
-
{ label: 'Day',
|
|
254
|
-
{ label: 'Week',
|
|
255
|
-
{ label: 'Month',
|
|
320
|
+
{ label: 'Day', value: 'Day', iconLeft: DayIcon },
|
|
321
|
+
{ label: 'Week', value: 'Week', iconLeft: WeekIcon },
|
|
322
|
+
{ label: 'Month', value: 'Month', iconLeft: MonthIcon },
|
|
256
323
|
]
|
|
257
324
|
let enabledModes = actionOptions.filter(
|
|
258
|
-
(mode) => !overrideConfig.disableModes.includes(mode.
|
|
325
|
+
(mode) => !overrideConfig.disableModes.includes(mode.value),
|
|
259
326
|
)
|
|
260
327
|
|
|
261
328
|
let currentYear = ref(new Date().getFullYear())
|
|
@@ -302,6 +369,16 @@ function updateCurrentDate(d) {
|
|
|
302
369
|
week.value = findCurrentWeek(d)
|
|
303
370
|
}
|
|
304
371
|
|
|
372
|
+
function increment() {
|
|
373
|
+
incrementClickEvents[activeView.value]()
|
|
374
|
+
syncSelectedMonth(currentYear.value, currentMonth.value)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function decrement() {
|
|
378
|
+
decrementClickEvents[activeView.value]()
|
|
379
|
+
syncSelectedMonth(currentYear.value, currentMonth.value)
|
|
380
|
+
}
|
|
381
|
+
|
|
305
382
|
const incrementClickEvents = {
|
|
306
383
|
Month: incrementMonth,
|
|
307
384
|
Week: incrementWeek,
|
|
@@ -316,71 +393,97 @@ const decrementClickEvents = {
|
|
|
316
393
|
|
|
317
394
|
function incrementMonth() {
|
|
318
395
|
currentMonth.value++
|
|
319
|
-
date.value = findFirstDateOfMonth(currentMonth.value, currentYear.value)
|
|
320
|
-
week.value = findCurrentWeek(currentMonthDates.value[date.value]) + 1
|
|
321
396
|
if (currentMonth.value > 11) {
|
|
322
397
|
currentMonth.value = 0
|
|
323
398
|
currentYear.value++
|
|
324
399
|
}
|
|
400
|
+
// After month changes, recompute month dates and reset to first in-month day
|
|
401
|
+
date.value = findFirstDateOfMonth(currentMonth.value, currentYear.value)
|
|
402
|
+
week.value = findCurrentWeek(currentMonthDates.value[date.value])
|
|
325
403
|
}
|
|
326
404
|
|
|
327
405
|
function decrementMonth() {
|
|
328
|
-
currentMonth.value
|
|
329
|
-
date.value = findLastDateOfMonth(currentMonth.value, currentYear.value)
|
|
330
|
-
week.value = findCurrentWeek(currentMonthDates.value[date.value])
|
|
331
|
-
if (currentMonth.value < 0) {
|
|
406
|
+
if (currentMonth.value === 0) {
|
|
332
407
|
currentMonth.value = 11
|
|
333
408
|
currentYear.value--
|
|
409
|
+
} else {
|
|
410
|
+
currentMonth.value--
|
|
334
411
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
incrementClickEvents[activeView.value]()
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function decrement() {
|
|
342
|
-
decrementClickEvents[activeView.value]()
|
|
412
|
+
// After adjusting month/year, pick last in-month date and its week
|
|
413
|
+
date.value = findLastDateOfMonth(currentMonth.value, currentYear.value)
|
|
414
|
+
week.value = findCurrentWeek(currentMonthDates.value[date.value])
|
|
343
415
|
}
|
|
344
416
|
|
|
345
417
|
function incrementWeek() {
|
|
346
|
-
week.value
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
418
|
+
const nextWeek = week.value + 1 // target next week index
|
|
419
|
+
|
|
420
|
+
// Case 1: still within current grid
|
|
421
|
+
if (nextWeek < datesInWeeks.value.length) {
|
|
422
|
+
week.value = nextWeek
|
|
423
|
+
const weekDates = datesInWeeks.value[week.value]
|
|
424
|
+
const spansNextMonth = weekDates.some(
|
|
425
|
+
(d) => d.getMonth() !== currentMonth.value,
|
|
426
|
+
) // overlap into next month
|
|
427
|
+
if (spansNextMonth) {
|
|
428
|
+
// cross boundary -> advance month
|
|
429
|
+
incrementMonth()
|
|
430
|
+
week.value = 0 // first week row of new month
|
|
431
|
+
const firstWeekDates = datesInWeeks.value[0]
|
|
432
|
+
const day = firstInMonth(firstWeekDates, currentMonth.value) // first in-month day
|
|
433
|
+
date.value = findIndexOfDate(day)
|
|
434
|
+
return
|
|
435
|
+
}
|
|
436
|
+
const day = firstInMonth(weekDates, currentMonth.value) // first in-month day in target week
|
|
437
|
+
date.value = findIndexOfDate(day)
|
|
438
|
+
return
|
|
357
439
|
}
|
|
440
|
+
|
|
441
|
+
// Case 2: overflow -> next month first week
|
|
442
|
+
incrementMonth()
|
|
443
|
+
week.value = 0
|
|
444
|
+
const firstWeekDates = datesInWeeks.value[0]
|
|
445
|
+
const day = firstInMonth(firstWeekDates, currentMonth.value) // first valid in-month day
|
|
446
|
+
date.value = findIndexOfDate(day)
|
|
358
447
|
}
|
|
359
448
|
|
|
360
449
|
function decrementWeek() {
|
|
361
|
-
week.value
|
|
362
|
-
|
|
363
|
-
|
|
450
|
+
const prevWeek = week.value - 1 // target previous week index
|
|
451
|
+
|
|
452
|
+
// Case 1: still within current grid
|
|
453
|
+
if (prevWeek >= 0) {
|
|
454
|
+
week.value = prevWeek
|
|
455
|
+
const weekDates = datesInWeeks.value[week.value]
|
|
456
|
+
const spansPrevMonth = weekDates.some(
|
|
457
|
+
(d) => d.getMonth() !== currentMonth.value,
|
|
458
|
+
) // overlap into previous month
|
|
459
|
+
if (spansPrevMonth) {
|
|
460
|
+
// cross boundary -> go to previous month
|
|
461
|
+
decrementMonth()
|
|
462
|
+
week.value = datesInWeeks.value.length - 1 // last week row of new month
|
|
463
|
+
const targetWeekDates = datesInWeeks.value[week.value]
|
|
464
|
+
const day = firstInMonth(targetWeekDates, currentMonth.value) // first day actually in that month
|
|
465
|
+
date.value = findIndexOfDate(day)
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
const day = firstInMonth(weekDates, currentMonth.value) // first in-month day in target week
|
|
469
|
+
date.value = findIndexOfDate(day)
|
|
364
470
|
return
|
|
365
471
|
}
|
|
366
472
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (previousMonthDates.length > 0) {
|
|
373
|
-
decrementMonth()
|
|
374
|
-
week.value = findCurrentWeek(previousMonthDates[0])
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function filterCurrentWeekDates() {
|
|
379
|
-
let currentWeekDates = datesInWeeks.value[week.value]
|
|
380
|
-
let differentMonthDates = currentWeekDates.filter(
|
|
473
|
+
// Case 2: underflow -> jump to previous month
|
|
474
|
+
decrementMonth()
|
|
475
|
+
let targetIndex = datesInWeeks.value.length - 1 // start at last row
|
|
476
|
+
const lastWeekDates = datesInWeeks.value[targetIndex]
|
|
477
|
+
const hasNextMonthDates = lastWeekDates.some(
|
|
381
478
|
(d) => d.getMonth() !== currentMonth.value,
|
|
382
|
-
)
|
|
383
|
-
|
|
479
|
+
) // overlap into next month
|
|
480
|
+
if (hasNextMonthDates && targetIndex > 0) {
|
|
481
|
+
targetIndex = targetIndex - 1 // skip overlap row
|
|
482
|
+
}
|
|
483
|
+
week.value = targetIndex
|
|
484
|
+
const targetWeekDates = datesInWeeks.value[week.value]
|
|
485
|
+
const day = firstInMonth(targetWeekDates, currentMonth.value) // first valid in-month day
|
|
486
|
+
date.value = findIndexOfDate(day)
|
|
384
487
|
}
|
|
385
488
|
|
|
386
489
|
function incrementDay() {
|
|
@@ -403,6 +506,10 @@ function decrementDay() {
|
|
|
403
506
|
}
|
|
404
507
|
}
|
|
405
508
|
|
|
509
|
+
function firstInMonth(weekDates, month) {
|
|
510
|
+
return weekDates.find((d) => d.getMonth() === month) || weekDates[0]
|
|
511
|
+
}
|
|
512
|
+
|
|
406
513
|
function findLastDateOfMonth(month, year) {
|
|
407
514
|
let inputDate = new Date(year, month + 1, 0)
|
|
408
515
|
let lastDateIndex = currentMonthDates.value.findIndex(
|
|
@@ -424,8 +531,26 @@ function findIndexOfDate(date) {
|
|
|
424
531
|
(d) => new Date(d).toDateString() === new Date(date).toDateString(),
|
|
425
532
|
)
|
|
426
533
|
}
|
|
534
|
+
|
|
427
535
|
const currentMonthYear = computed(() => {
|
|
428
|
-
|
|
536
|
+
// Non-week views or empty week fallback
|
|
537
|
+
if (activeView.value !== 'Week')
|
|
538
|
+
return formatMonthYear(currentMonth.value, currentYear.value)
|
|
539
|
+
|
|
540
|
+
const weekDates = datesInWeeks.value[week.value] || []
|
|
541
|
+
if (!weekDates.length)
|
|
542
|
+
return formatMonthYear(currentMonth.value, currentYear.value)
|
|
543
|
+
|
|
544
|
+
const parts = getWeekMonthParts(weekDates)
|
|
545
|
+
if (parts.length === 1) return formatMonthYear(parts[0].month, parts[0].year)
|
|
546
|
+
|
|
547
|
+
const short = monthList.map((m) => m.slice(0, 3))
|
|
548
|
+
const first = parts[0]
|
|
549
|
+
const last = parts[parts.length - 1]
|
|
550
|
+
|
|
551
|
+
return first.year === last.year
|
|
552
|
+
? `${short[first.month]} - ${short[last.month]} ${first.year}` // Same year span
|
|
553
|
+
: `${short[first.month]} ${first.year} - ${short[last.month]} ${last.year}` // Cross-year span
|
|
429
554
|
})
|
|
430
555
|
|
|
431
556
|
function isCurrentMonthDate(date) {
|
|
@@ -433,5 +558,24 @@ function isCurrentMonthDate(date) {
|
|
|
433
558
|
return date.getMonth() === currentMonth.value
|
|
434
559
|
}
|
|
435
560
|
|
|
561
|
+
function setCalendarDate(d) {
|
|
562
|
+
const dt = d ? new Date(d) : new Date()
|
|
563
|
+
if (dt.toString() === 'Invalid Date') return
|
|
564
|
+
currentYear.value = dt.getFullYear()
|
|
565
|
+
currentMonth.value = dt.getMonth()
|
|
566
|
+
currentDate.value = dt
|
|
567
|
+
// Wait for reactive recalculations of month dates
|
|
568
|
+
nextTick(() => {
|
|
569
|
+
week.value = findCurrentWeek(dt)
|
|
570
|
+
const idx = findIndexOfDate(dt)
|
|
571
|
+
if (idx >= 0) {
|
|
572
|
+
date.value = idx
|
|
573
|
+
} else {
|
|
574
|
+
// Fallback: first date of month
|
|
575
|
+
date.value = findFirstDateOfMonth(currentMonth.value, currentYear.value)
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
|
|
436
580
|
defineExpose({ reloadEvents })
|
|
437
581
|
</script>
|