33 - SPDX-License-Identifier: AGPL-3.0-or-later
44-->
55<template >
6- <div >
7- <EditorContent :editor =" editor " />
6+ <div class =" cell-editor" >
7+ <div v-if =" !isEditing" @click =" handleStartEditing" >
8+ <EditorContent :editor =" editor " />
9+ </div >
10+ <div v-else
11+ ref =" editingContainer"
12+ class =" tiptap-edit-mode"
13+ @keydown.escape.prevent =" cancelEdit" >
14+ <TiptapMenuBar
15+ :value .sync =" localValue "
16+ :text-length-limit =" getTextLimit "
17+ @input =" updateText " />
18+ <div v-if =" localLoading" class =" loading-indicator" >
19+ <div class =" icon-loading-small icon-loading-inline" />
20+ </div >
21+ </div >
822 </div >
923</template >
1024
1125<script >
1226import { Editor , EditorContent } from ' @tiptap/vue-2'
1327import { StarterKit } from ' @tiptap/starter-kit'
28+ import TiptapMenuBar from ' ./TiptapMenuBar.vue'
29+ import cellEditMixin from ' ../mixins/cellEditMixin.js'
30+ import { translate as t } from ' @nextcloud/l10n'
1431
1532export default {
1633 name: ' TableCellHtml' ,
1734
1835 components: {
1936 EditorContent,
37+ TiptapMenuBar,
2038 },
2139
40+ mixins: [cellEditMixin],
41+
2242 props: {
2343 column: {
2444 type: Object ,
@@ -37,12 +57,26 @@ export default {
3757 data () {
3858 return {
3959 editor: null ,
60+ localValue: ' ' ,
61+ isInitialEditClick: false ,
4062 }
4163 },
4264
65+ computed: {
66+ getTextLimit () {
67+ if (this .column .textMaxLength === - 1 ) {
68+ return null
69+ } else {
70+ return this .column .textMaxLength
71+ }
72+ },
73+ },
74+
4375 watch: {
4476 value (value ) {
45- this .editor .commands .setContent (value, false )
77+ if (this .editor ) {
78+ this .editor .commands .setContent (value, false )
79+ }
4680 },
4781 },
4882
@@ -57,30 +91,158 @@ export default {
5791 },
5892
5993 beforeUnmount () {
60- this .editor .destroy ()
94+ if (this .editor ) {
95+ this .editor .destroy ()
96+ }
97+ },
98+
99+ methods: {
100+ t,
101+
102+ handleStartEditing (event ) {
103+ // Don't start editing if clicking on links
104+ if (event .target .closest (' a' )) {
105+ return
106+ }
107+ this .startEditing ()
108+ event .stopPropagation ()
109+ },
110+
111+ handleClickOutside (event ) {
112+ if (! this .isEditing ) return
113+
114+ if (this .isInitialEditClick ) {
115+ this .isInitialEditClick = false
116+ return
117+ }
118+
119+ // Check if the click is outside our editing container
120+ if (this .$refs .editingContainer && ! this .$refs .editingContainer .contains (event .target )) {
121+ const isEditorRelated = event .target .closest (' .tiptap-wrapper' )
122+ || event .target .closest (' .ProseMirror' )
123+ || event .target .closest (' [contenteditable]' )
124+ || event .target .closest (' .text-menubar' )
125+ || event .target .closest (' .text-editor' )
126+ || event .target .closest (' .editor-wrapper' )
127+ || event .target .closest (' [role="dialog"]' )
128+ || event .target .closest (' [role="menu"]' )
129+ || event .target .closest (' [role="listbox"]' )
130+
131+ if (! isEditorRelated) {
132+ this .saveChanges ()
133+ }
134+ }
135+ },
136+
137+ updateText (text ) {
138+ this .localValue = text
139+ },
140+
141+ async saveChanges () {
142+ if (this .localLoading ) return
143+
144+ if (this .localValue === this .value ) {
145+ this .stopEditing ()
146+ return
147+ }
148+
149+ const success = await this .updateCellValue (this .localValue || ' ' )
150+
151+ if (success) {
152+ this .stopEditing ()
153+ } else {
154+ this .cancelEdit ()
155+ }
156+ this .localLoading = false
157+ },
158+
159+ cancelEdit () {
160+ this .localValue = this .value
161+ this .stopEditing ()
162+ },
163+
164+ startEditing () {
165+ if (! this .canEditCell ()) return false
166+ this .localValue = this .value || ' '
167+ this .isEditing = true
168+ this .isInitialEditClick = true
169+
170+ document .addEventListener (' click' , this .handleClickOutside , true )
171+ },
172+
173+ stopEditing () {
174+ this .isEditing = false
175+ this .isInitialEditClick = false
176+ document .removeEventListener (' click' , this .handleClickOutside , true )
177+ },
61178 },
62179
63180}
64181 </script >
65182
66- <style scoped lang="scss">
183+ <style lang="scss" scoped>
184+ .cell-editor {
185+ width : 100% ;
186+ }
67187
68- :deep(.tiptap-reader-cell ) {
69- max-height : calc (var (--default-line-height ) * 6 );
70- overflow-y : scroll ;
71- min-width : 100px ;
72- white-space : pre-wrap ;
73- margin-top : calc (var (--default-grid-baseline ) * 2 );
74- margin-bottom : calc (var (--default-grid-baseline ) * 2 );
188+ .cell-editor > div {
189+ cursor : pointer ;
190+ min-height : 24px ;
191+ }
75192
76- li {
77- display : flex ;
78- align-items : center ;
79- }
193+ .tiptap-edit-mode {
194+ position : relative ;
195+ border : 1px solid var (--color-border-maxcontrast );
196+ border-radius : var (--border-radius );
197+ background : var (--color-main-background );
198+ cursor : default ;
199+ }
80200
81- li > div {
82- padding-left : calc (var (--default-grid-baseline ) * 2 );
201+ .loading-indicator {
202+ position : absolute ;
203+ top : 4px ;
204+ right : 4px ;
205+ }
206+
207+ :deep(.text-editor__wrapper div .ProseMirror ) {
208+ padding : 8px ;
209+ min-height : 24px ;
210+ }
211+
212+ :deep(div [contenteditable = ' false' ]) {
213+ background : transparent ;
214+ color : var (--color-main-text );
215+ width :100% ;
216+ opacity : 1 ;
217+
218+ & :hover {
219+ background : var (--color-background-hover );
83220 }
84221}
85222
223+ :deep(.tiptap-wrapper ) {
224+ .menuBar {
225+ padding : 8px ;
226+ border-bottom : 1px solid var (--color-border );
227+ background : var (--color-background-dark );
228+ width : 100% ;
229+ }
230+
231+ .ProseMirror {
232+ padding : 8px ;
233+ min-height : 60px ;
234+ outline : none ;
235+ border : none !important ;
236+ border-color : none !important ;
237+ box-shadow : none !important ;
238+ }
239+
240+ .character-count {
241+ padding : 4px 8px ;
242+ font-size : 12px ;
243+ color : var (--color-text-maxcontrast );
244+ border-top : 1px solid var (--color-border );
245+ background : var (--color-background-dark );
246+ }
247+ }
86248 </style >
0 commit comments