Skip to content

Commit 8d43254

Browse files
committed
feat: Move to contenteditable for inline title editing
fix #2563 fix #4522 Signed-off-by: Julius Härtl <[email protected]>
1 parent 29d5913 commit 8d43254

File tree

1 file changed

+30
-52
lines changed

1 file changed

+30
-52
lines changed

src/components/cards/CardItem.vue

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
<template>
2828
<AttachmentDragAndDrop v-if="card" :card-id="card.id" class="drop-upload--card">
29-
<div :class="{'compact': compactMode, 'current-card': currentCard, 'has-labels': card.labels && card.labels.length > 0, 'is-editing': editing, 'card__editable': canEdit, 'card__archived': card.archived }"
29+
<div :class="{'compact': compactMode, 'current-card': currentCard, 'has-labels': card.labels && card.labels.length > 0, 'card__editable': canEdit, 'card__archived': card.archived }"
3030
tag="div"
3131
class="card"
3232
@click="openCard">
@@ -39,29 +39,18 @@
3939
<h3 v-if="inlineEditingBlocked" dir="auto">
4040
{{ card.title }}
4141
</h3>
42-
<h3 v-else-if="!editing"
42+
<h3 v-else
4343
dir="auto"
44-
tabindex="0"
4544
class="editable"
46-
:aria-label="t('deck', 'Edit card title')"
47-
@keydown.enter.stop.prevent="startEditing(card)">
48-
<span @click.stop="startEditing(card)">{{ card.title }}</span>
45+
:aria-label="t('deck', 'Edit card title')">
46+
<span ref="titleContentEditable"
47+
tabindex="0"
48+
contenteditable="true"
49+
role="textbox"
50+
@blur="blurTitle"
51+
@keyup.esc="cancelEdit"
52+
@keyup.stop>{{ card.title }}</span>
4953
</h3>
50-
<form v-else-if="editing"
51-
v-click-outside="cancelEdit"
52-
class="dragDisabled"
53-
@click.stop
54-
@keyup.esc="cancelEdit"
55-
@submit.prevent="finishedEdit(card)">
56-
<input v-model="copiedCard.title"
57-
v-focus
58-
dir="auto"
59-
type="text"
60-
autocomplete="off"
61-
required
62-
pattern=".*\S+.*">
63-
<input type="submit" value="" class="icon-confirm">
64-
</form>
6554

6655
<CardMenu v-if="showMenuAtTitle" :card="card" class="right card-menu" />
6756
</div>
@@ -126,12 +115,6 @@ export default {
126115
default: false,
127116
},
128117
},
129-
data() {
130-
return {
131-
editing: false,
132-
copiedCard: null,
133-
}
134-
},
135118
computed: {
136119
...mapState({
137120
compactMode: state => state.compactMode,
@@ -183,9 +166,6 @@ export default {
183166
return this.$store.getters.config('cardIdBadge')
184167
},
185168
showMenuAtTitle() {
186-
if (this.editing) {
187-
return false
188-
}
189169
return this.compactMode || (!this.compactMode && !this.hasBadges && !this.hasLabels)
190170
},
191171
showMenuAtLabels() {
@@ -207,6 +187,12 @@ export default {
207187
this.$nextTick(() => this.$el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' }))
208188
}
209189
},
190+
'card.title'(value) {
191+
if (document.activeElement === this.$refs.titleContentEditable || this.$refs.titleContentEditable.textContent === value) {
192+
return
193+
}
194+
this.$refs.titleContentEditable.textContent = value
195+
},
210196
},
211197
methods: {
212198
openCard() {
@@ -216,18 +202,17 @@ export default {
216202
const boardId = this.card && this.card.boardId ? this.card.boardId : this.$route.params.id
217203
this.$router.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
218204
},
219-
startEditing(card) {
220-
this.copiedCard = Object.assign({}, card)
221-
this.editing = true
222-
},
223-
finishedEdit(card) {
224-
if (this.copiedCard.title !== card.title) {
225-
this.$store.dispatch('updateCardTitle', this.copiedCard)
205+
blurTitle(e) {
206+
// TODO Handle empty title
207+
if (e.target.innerText !== this.card.title) {
208+
this.$store.dispatch('updateCardTitle', {
209+
...this.card,
210+
title: e.target.innerText,
211+
})
226212
}
227-
this.editing = false
228213
},
229214
cancelEdit() {
230-
this.editing = false
215+
this.$refs.titleContentEditable.textContent = this.card.title
231216
},
232217
applyLabelFilter(label) {
233218
if (this.dragging) {
@@ -279,15 +264,6 @@ export default {
279264
280265
.card-upper {
281266
display: flex;
282-
form {
283-
display: flex;
284-
padding: 3px 5px;
285-
width: 100%;
286-
input[type=text] {
287-
flex-grow: 1;
288-
}
289-
290-
}
291267
h3 {
292268
margin: 0;
293269
padding: 6px;
@@ -300,17 +276,19 @@ export default {
300276
&.editable {
301277
span {
302278
cursor: text;
279+
padding-right: 8px;
280+
281+
&:focus, &:focus-visible {
282+
outline: none;
283+
}
303284
}
304285
305-
&:focus {
286+
&:focus-within {
306287
outline: 2px solid var(--color-border-dark);
307288
border-radius: 3px;
308289
}
309290
}
310291
}
311-
input[type=text] {
312-
font-size: 100%;
313-
}
314292
.card-menu {
315293
height: 44px;
316294
align-self: end;

0 commit comments

Comments
 (0)