33 - SPDX-License-Identifier: AGPL-3.0-or-later
44-->
55<template >
6- <div >
7- {{ getValue }}
6+ <div class =" cell-datetime" >
7+ <div v-if =" !isEditing" class =" non-edit-mode" @click =" startEditing" >
8+ {{ getValue }}
9+ </div >
10+ <div v-else
11+ ref =" editingContainer"
12+ class =" inline-editing-container"
13+ tabindex =" 0"
14+ @keydown.enter =" saveChanges"
15+ @keydown.escape =" cancelEdit" >
16+ <div class =" datetime-picker-container" :class =" { 'is-loading': localLoading }" >
17+ <NcDateTimePickerNative
18+ v-model =" editDateTimeValue "
19+ :type =" getPickerType "
20+ :label =" getPlaceholder "
21+ :disabled =" localLoading || ! canEditCell ()" />
22+ <div v-if =" canBeCleared" class =" icon-close make-empty" @click =" emptyValue" />
23+ </div >
24+ <div v-if =" localLoading" class =" loading-indicator" >
25+ <div class =" icon-loading-small icon-loading-inline" />
26+ </div >
27+ </div >
828 </div >
929</template >
1030
1131<script >
32+ import { NcDateTimePickerNative } from ' @nextcloud/vue'
33+ import Moment from ' @nextcloud/moment'
34+ import cellEditMixin from ' ../mixins/cellEditMixin.js'
35+ import { translate as t } from ' @nextcloud/l10n'
1236
1337export default {
1438 name: ' TableCellDateTime' ,
39+
40+ components: {
41+ NcDateTimePickerNative,
42+ },
43+
44+ mixins: [cellEditMixin],
45+
1546 props: {
1647 column: {
1748 type: Object ,
@@ -26,6 +57,13 @@ export default {
2657 default: null ,
2758 },
2859 },
60+
61+ data () {
62+ return {
63+ editDateTimeValue: null ,
64+ }
65+ },
66+
2967 computed: {
3068 getValue () {
3169 // default value is for the form, if you want to present the date from today, you should use the calculating column
@@ -34,17 +72,197 @@ export default {
3472 }
3573 return this .column .formatValue (this .value )
3674 },
75+
3776 getDefaultValue () {
3877 return this .column .formatValue (this .column .datetimeDefault )
3978 },
79+
80+ getPlaceholder () {
81+ switch (this .column .type ) {
82+ case ' datetime-date' :
83+ return t (' tables' , ' Select a date' )
84+ case ' datetime-time' :
85+ return t (' tables' , ' Select a time' )
86+ default :
87+ return t (' tables' , ' Select a date and time' )
88+ }
89+ },
90+
91+ getPickerType () {
92+ switch (this .column .type ) {
93+ case ' datetime-date' :
94+ return ' date'
95+ case ' datetime-time' :
96+ return ' time'
97+ default :
98+ return ' datetime-local'
99+ }
100+ },
101+
102+ canBeCleared () {
103+ return ! this .column .mandatory
104+ },
105+ },
106+
107+ watch: {
108+ isEditing (newValue ) {
109+ if (newValue) {
110+ this .initEditValue ()
111+ // Use a small delay to prevent the same click event that triggered editing
112+ // from immediately triggering the click outside handler
113+ this .$nextTick (() => {
114+ setTimeout (() => {
115+ document .addEventListener (' click' , this .handleClickOutside )
116+ }, 10 )
117+ })
118+ } else {
119+ // Remove click outside listener
120+ document .removeEventListener (' click' , this .handleClickOutside )
121+ }
122+ },
123+ },
124+
125+ methods: {
126+ t,
127+
128+ initEditValue () {
129+ if (this .value !== null && this .value !== ' none' ) {
130+ const format = this .getDateFormat ()
131+
132+ if (this .column .type === ' datetime-time' ) {
133+ // For time-only values, use the same approach as DatetimeTimeForm
134+ const timeMoment = Moment (this .value , format)
135+ if (timeMoment .isValid ()) {
136+ this .editDateTimeValue = timeMoment .toDate ()
137+ } else {
138+ this .editDateTimeValue = new Date ()
139+ }
140+ } else {
141+ // For date and datetime values, parse normally
142+ const parsedMoment = Moment (this .value , format)
143+ this .editDateTimeValue = parsedMoment .isValid () ? parsedMoment .toDate () : null
144+ }
145+ } else if ((this .value === null || this .value === ' ' ) && this .column .datetimeDefault ) {
146+ // Handle default values
147+ if (this .column .datetimeDefault === ' now' || this .column .datetimeDefault === ' today' ) {
148+ this .editDateTimeValue = new Date ()
149+ } else {
150+ this .editDateTimeValue = this .column .type === ' datetime-time' ? new Date () : null
151+ }
152+ } else {
153+ // For time columns, always provide a default Date object to prevent errors
154+ this .editDateTimeValue = this .column .type === ' datetime-time' ? new Date () : null
155+ }
156+ },
157+
158+ getDateFormat () {
159+ switch (this .column .type ) {
160+ case ' datetime-date' :
161+ return ' YYYY-MM-DD'
162+ case ' datetime-time' :
163+ return ' HH:mm'
164+ default :
165+ return ' YYYY-MM-DD HH:mm'
166+ }
167+ },
168+
169+ emptyValue () {
170+ this .editDateTimeValue = null
171+ },
172+
173+ async saveChanges () {
174+ if (this .localLoading ) {
175+ return
176+ }
177+
178+ let newValue = null
179+
180+ if (this .editDateTimeValue === null ) {
181+ newValue = ' none'
182+ } else {
183+ const format = this .getDateFormat ()
184+ newValue = Moment (this .editDateTimeValue ).format (format)
185+ }
186+
187+ if (newValue === this .value ) {
188+ this .isEditing = false
189+ return
190+ }
191+
192+ const success = await this .updateCellValue (newValue)
193+
194+ if (! success) {
195+ this .cancelEdit ()
196+ }
197+
198+ this .localLoading = false
199+ this .isEditing = false
200+ },
201+
202+ handleClickOutside (event ) {
203+ if (this .$refs .editingContainer && ! this .$refs .editingContainer .contains (event .target )) {
204+ this .saveChanges ()
205+ }
206+ },
40207 },
41208}
42209 </script >
43210
44211<style lang="scss" scoped>
212+ .cell-datetime {
213+ width : 100% ;
214+
215+ .non-edit-mode {
216+ cursor : pointer ;
217+ min-height : 20px ;
218+ }
219+
220+ > div :first-child {
221+ cursor : pointer ;
222+ text-align : right ;
223+ }
224+ }
225+
226+ .inline-editing-container {
227+ .datetime-picker-container {
228+ display : flex ;
229+ align-items : center ;
230+ width : 100% ;
231+
232+ & .is-loading {
233+ opacity : 0.7 ;
234+ }
235+
236+ div {
237+ width : 100% ;
238+ }
239+ }
240+
241+ .datetime-picker-container input {
242+ width : 100% ;
243+ }
45244
46- div {
47- text-align : right ;
48245}
49246
247+ .editor-buttons {
248+ display : flex ;
249+ gap : 8px ;
250+ margin-top : 8px ;
251+ align-items : center ;
252+ }
253+
254+ .make-empty {
255+ padding-left : 15px ;
256+ cursor : pointer ;
257+ }
258+
259+ .icon-loading-inline {
260+ margin-left : 4px ;
261+ }
262+
263+ :deep(input [type = " datetime-local" ]),
264+ :deep(input [type = " date" ]),
265+ :deep(input [type = " time" ]) {
266+ width : 100% ;
267+ }
50268 </style >
0 commit comments