Skip to content

Commit 8732c56

Browse files
committed
feat: Inline editing support for html column
Signed-off-by: Enjeck C. <patrathewhiz@gmail.com>
1 parent 038a943 commit 8732c56

1 file changed

Lines changed: 180 additions & 18 deletions

File tree

src/shared/components/ncTable/partials/TableCellHtml.vue

Lines changed: 180 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,42 @@
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>
1226
import { Editor, EditorContent } from '@tiptap/vue-2'
1327
import { 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
1532
export 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

Comments
 (0)