Skip to content

Commit edfa253

Browse files
committed
Improve drop behaviour for empty stacks
Signed-off-by: Theo <36564257+theoholl@users.noreply.github.com>
1 parent fc05c33 commit edfa253

2 files changed

Lines changed: 36 additions & 66 deletions

File tree

src/components/board/Board.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,11 @@ export default {
317317
position: relative;
318318
319319
.smooth-dnd-container.vertical {
320-
// Stacks may adjust the spacing, e.g. to collapse an empty card
321-
// list, by setting --stack-cards-spacing (see Stack.vue)
322-
$spacing: var(--stack-cards-spacing, #{$stack-gap});
323320
display: flex;
324321
flex-direction: column;
325-
gap: $spacing;
326-
padding: $spacing;
327-
margin: 0 calc(#{$spacing} * -1);
322+
gap: $stack-gap;
323+
padding: $stack-gap;
324+
margin: 0 calc(#{$stack-gap} * -1);
328325
overflow-y: auto;
329326
scrollbar-gutter: stable;
330327
}

src/components/board/Stack.vue

Lines changed: 33 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
'stack--done-column': isDoneColumn,
1010
'stack--bottom-add-empty': isEmptyStackWithBottomAddCard,
1111
'stack--add-card-at-top': canAddCardAtTop,
12+
'stack--dragging-card': draggingCard,
13+
'stack--drag-over': draggingOverStack,
1214
}"
1315
:data-cy-stack="stack.title">
1416
<div v-click-outside="stopCardCreation"
@@ -142,7 +144,9 @@
142144
data-dragscroll-enabled
143145
@should-accept-drop="canEdit"
144146
@drag-start="draggingCard = true"
145-
@drag-end="draggingCard = false"
147+
@drag-end="onDragEnd"
148+
@drag-enter="draggingOverStack = true"
149+
@drag-leave="draggingOverStack = false"
146150
@drop="($event) => onDropCard(stack.id, $event)">
147151
<Draggable v-for="card in cardsByStack" :key="card.id">
148152
<transition :appear="animate && !card.animated && (card.animated=true)"
@@ -153,50 +157,7 @@
153157
</Draggable>
154158
</Container>
155159

156-
<Container v-if="canAddCardAtBottom && isEmptyStackWithBottomAddCard"
157-
:get-child-payload="payloadForCard(stack.id)"
158-
class="dnd-container stack__card-add stack__card-add--bottom stack__card-add--dropzone"
159-
group-name="stack"
160-
data-click-closes-sidebar="true"
161-
non-drag-area-selector=".dragDisabled"
162-
:drag-handle-selector="dragHandleSelector"
163-
@should-accept-drop="canEdit"
164-
@drag-start="draggingCard = true"
165-
@drag-end="draggingCard = false"
166-
@drop="($event) => onDropCard(stack.id, $event)">
167-
<NcButton v-if="!showAddCard"
168-
data-cy="action:add-card"
169-
class="stack__card-add-button"
170-
type="tertiary"
171-
:wide="true"
172-
@click.stop="showAddCard=true">
173-
<template #icon>
174-
<PlusIcon :size="20" />
175-
</template>
176-
{{ t('deck', 'Add card') }}
177-
</NcButton>
178-
<form v-else
179-
:class="{ 'icon-loading-small': stateCardCreating }"
180-
@submit.prevent.stop="clickAddCard()">
181-
<label for="new-stack-input-main" class="hidden-visually">{{ t('deck', 'Add a new card') }}</label>
182-
<input id="new-stack-input-main"
183-
ref="newCardInput"
184-
v-model="newCardTitle"
185-
type="text"
186-
class="no-close"
187-
:disabled="stateCardCreating"
188-
:placeholder="t('deck', 'Card name')"
189-
required
190-
pattern=".*\S+.*"
191-
@focus="onCreateCardFocus"
192-
@keydown.esc.stop="closeCardCreation">
193-
<input v-show="!stateCardCreating"
194-
class="icon-confirm"
195-
type="submit"
196-
value="">
197-
</form>
198-
</Container>
199-
<div v-else-if="canAddCardAtBottom" class="stack__card-add stack__card-add--bottom">
160+
<div v-if="canAddCardAtBottom" class="stack__card-add stack__card-add--bottom">
200161
<NcButton v-if="!showAddCard"
201162
data-cy="action:add-card"
202163
class="stack__card-add-button"
@@ -279,6 +240,7 @@ export default {
279240
return {
280241
editing: false,
281242
draggingCard: false,
243+
draggingOverStack: false,
282244
copiedStack: '',
283245
newCardTitle: '',
284246
showAddCard: false,
@@ -370,6 +332,11 @@ export default {
370332
this.showAddCard = false
371333
return false
372334
},
335+
onDragEnd() {
336+
this.draggingCard = false
337+
// drag-leave is not emitted when the card is dropped inside the stack
338+
this.draggingOverStack = false
339+
},
373340
async onDropCard(stackId, event) {
374341
const { addedIndex, removedIndex, payload } = event
375342
const card = Object.assign({}, payload)
@@ -517,30 +484,36 @@ export default {
517484
flex-grow: 1;
518485
}
519486
520-
&:not(.stack--add-card-at-top) {
487+
&:not(.stack--add-card-at-top):not(.stack--bottom-add-empty) {
521488
.dnd-container {
522489
flex: 0 1 auto;
523490
min-height: 0;
524491
}
525492
}
526493
527494
&.stack--bottom-add-empty {
528-
// Collapse the card list while it is empty so the add card control is
529-
// not pushed down. Once a card is dragged over, the drop placeholder
530-
// becomes a child and the regular spacing applies again.
531-
:deep(.stack__cards-list:empty) {
532-
--stack-cards-spacing: 0;
495+
// The empty card list keeps filling the column so that it stays an
496+
// easy drop target, while the add card control floats on top of it,
497+
// right where the first card would be.
498+
.stack__card-add--bottom {
499+
position: absolute;
500+
inset-inline: 0;
501+
top: calc(var(--default-clickable-area) + #{$stack-gap});
502+
transition: top var(--animation-quick);
533503
}
534504
535-
// Spacing comes from the shared vertical dnd container rule in
536-
// Board.vue, the min-height accounts for its vertical padding
537-
.stack__card-add--dropzone {
538-
flex: 0 0 auto;
539-
display: flex;
540-
align-items: center;
541-
min-height: calc(var(--stack-card-add-control-height) + (2 * #{$stack-gap}));
542-
box-sizing: border-box;
543-
position: relative;
505+
// Make way for the drop placeholder while a card is dragged over the
506+
// list. The list itself is left untouched, resizing it would move it
507+
// away from under the pointer.
508+
&.stack--drag-over .stack__card-add--bottom {
509+
top: calc(var(--default-clickable-area) + var(--stack-card-add-control-height) + 2 * #{$stack-gap});
510+
}
511+
512+
// smooth-dnd resolves the hovered container with elementFromPoint(), so
513+
// the floating control has to let the drag through to the list below it,
514+
// otherwise dragging over it would count as leaving the list.
515+
&.stack--dragging-card .stack__card-add--bottom {
516+
pointer-events: none;
544517
}
545518
}
546519

0 commit comments

Comments
 (0)