1212 'stack--dragging-card': draggingCard,
1313 }"
1414 :data-cy-stack =" stack.title" >
15- <div v-click-outside =" stopCardCreation"
16- class =" stack__header"
17- :class =" {'stack__header--add': showAddCard, 'stack__header--done-column': isDoneColumn}"
15+ <div class =" stack__header"
16+ :class =" {'stack__header--done-column': isDoneColumn}"
1817 :aria-label =" stack.title" >
1918 <transition name =" fade" mode =" out-in" >
2019 <h3 v-if =" !canManage || isArchived" tabindex =" 0" >
10099 </div >
101100 </NcModal >
102101
102+ <StackCardAdd v-if =" canAddCard && stackAddCardAtTop "
103+ :stack =" stack "
104+ :add-at-top =" true "
105+ @creating =" animate = true "
106+ @created =" handleCardCreated " />
107+
103108 <Container :get-child-payload =" payloadForCard (stack .id )"
104109 class="dnd-container stack__cards-list"
105110 group-name="stack"
120125 </Draggable >
121126 </Container >
122127
123- <div v-if =" canAddCard" class =" stack__card-add" >
124- <NcButton v-if =" ! showAddCard "
125- data-cy="action:add-card "
126- class="stack__card-add-button"
127- type="tertiary"
128- :wide =" true "
129- @click .stop =" showAddCard = true " >
130- <template #icon >
131- <PlusIcon :size =" 20 " />
132- </template >
133- {{ t('deck', 'Add card') }}
134- </NcButton >
135- <form v-else
136- :class =" { 'icon-loading-small': stateCardCreating }"
137- @submit.prevent.stop =" clickAddCard()" >
138- <label for =" new-stack-input-main" class =" hidden-visually" >{{ t('deck', 'Add a new card') }}</label >
139- <input id =" new-stack-input-main"
140- ref =" newCardInput"
141- v-model =" newCardTitle"
142- type =" text"
143- class =" no-close"
144- :disabled =" stateCardCreating"
145- :placeholder =" t('deck', 'Card name')"
146- required
147- pattern =" .*\S+.*"
148- @focus =" onCreateCardFocus"
149- @keydown.esc.stop =" showAddCard = false" >
150- <input v-show =" !stateCardCreating"
151- class =" icon-confirm"
152- type =" submit"
153- value =" " >
154- </form >
155- </div >
128+ <StackCardAdd v-if =" canAddCard && ! stackAddCardAtTop "
129+ :stack =" stack "
130+ @creating =" animate = true "
131+ @created =" handleCardCreated " />
156132 </div >
157133</template >
158134
@@ -162,11 +138,11 @@ import { mapGetters, mapState } from 'vuex'
162138import { Container , Draggable } from ' vue-smooth-dnd'
163139import ArchiveIcon from ' vue-material-design-icons/ArchiveOutline.vue'
164140import CheckCircleOutline from ' vue-material-design-icons/CheckCircleOutline.vue'
165- import PlusIcon from ' vue-material-design-icons/Plus.vue'
166- import { NcActions , NcActionButton , NcButton , NcModal } from ' @nextcloud/vue'
167- import { showError , showUndo } from ' @nextcloud/dialogs'
141+ import { NcActions , NcActionButton , NcModal } from ' @nextcloud/vue'
142+ import { showUndo } from ' @nextcloud/dialogs'
168143
169144import CardItem from ' ../cards/CardItem.vue'
145+ import StackCardAdd from ' ./StackCardAdd.vue'
170146
171147import ' @nextcloud/dialogs/style.css'
172148import { mapActions } from ' pinia'
@@ -178,14 +154,13 @@ export default {
178154 components: {
179155 NcActions,
180156 NcActionButton,
181- NcButton,
182157 CardItem,
158+ StackCardAdd,
183159 Container,
184160 Draggable,
185161 NcModal,
186162 ArchiveIcon,
187163 CheckCircleOutline,
188- PlusIcon,
189164 },
190165 directives: {
191166 ClickOutside,
@@ -205,9 +180,6 @@ export default {
205180 editing: false ,
206181 draggingCard: false ,
207182 copiedStack: ' ' ,
208- newCardTitle: ' ' ,
209- showAddCard: false ,
210- stateCardCreating: false ,
211183 animate: false ,
212184 modalArchivAllCardsShow: false ,
213185 stackTransfer: {
@@ -239,50 +211,20 @@ export default {
239211 dragHandleSelector () {
240212 return this .canEdit && ! this .showArchived ? null : ' .no-drag'
241213 },
242- cardDetailsInModal: {
243- get () {
244- return this .$store .getters .config (' cardDetailsInModal' )
245- },
246- set (newValue ) {
247- this .$store .dispatch (' setConfig' , { cardDetailsInModal: newValue })
248- },
249- },
250214 stackAddCardAtTop () {
251215 return this .$store .getters .config (' stackAddCardAtTop' ) === true
252216 },
253217 canAddCard () {
254218 return this .canEdit && ! this .showArchived && ! this .isArchived
255219 },
256220 },
257- watch: {
258- showAddCard (newValue ) {
259- if (! newValue) {
260- this .$store .dispatch (' toggleShortcutLock' , false )
261- } else {
262- this .$nextTick (() => {
263- this .$refs .newCardInput .focus ()
264- })
265- }
266- },
267- },
268-
269221 mounted () {
270222 this .setupAutoscrollOnDrag ()
271223 },
272224
273225 methods: {
274226 ... mapActions (useTrashbinStore, [' stackUndoDelete' ]),
275227 ... mapActions (useStackStore, [' setDoneStack' , ' deleteStack' , ' updateStack' ]),
276- stopCardCreation (e ) {
277- // For some reason the submit event triggers a MouseEvent that is bubbling to the outside
278- // so we have to ignore it
279- e .stopPropagation ()
280- if (this .$refs .newCardInput && this .$refs .newCardInput .parentElement === e .target .parentElement ) {
281- return false
282- }
283- this .showAddCard = false
284- return false
285- },
286228 async onDropCard (stackId , event ) {
287229 const { addedIndex , removedIndex , payload } = event
288230 const card = Object .assign ({}, payload)
@@ -343,41 +285,12 @@ export default {
343285 cancelEdit () {
344286 this .editing = false
345287 },
346- async clickAddCard () {
347- this .stateCardCreating = true
348- try {
349- const addCardAtTop = this .stackAddCardAtTop
350- this .animate = true
351- const newCard = await this .$store .dispatch (' addCard' , {
352- title: this .newCardTitle ,
353- stackId: this .stack .id ,
354- boardId: this .stack .boardId ,
355- // Without an order the API appends the card to the end of the stack
356- ... (addCardAtTop ? { order: 0 } : {}),
357- })
358- if (addCardAtTop) {
359- // Creating a card does not move the existing cards down, so reorder
360- await this .$store .dispatch (' reorderCard' , { ... newCard, order: 0 })
361- }
362- this .newCardTitle = ' '
363- this .showAddCard = true
364- this .$nextTick (() => {
365- this .$refs .newCardInput .focus ()
366- this .animate = false
367- // Refs of a v-for are registered in creation order, not in list order
368- this .$refs .card ? .find ((card ) => card .id === newCard .id )? .scrollIntoView ()
369- })
370- if (! this .cardDetailsInModal ) {
371- this .$router .push ({ name: ' card' , params: { cardId: newCard .id } })
372- }
373- } catch (e) {
374- showError (' Could not create card: ' + e .response .data .message )
375- } finally {
376- this .stateCardCreating = false
377- }
378- },
379- onCreateCardFocus () {
380- this .$store .dispatch (' toggleShortcutLock' , true )
288+ handleCardCreated (newCard ) {
289+ this .$nextTick (() => {
290+ this .animate = false
291+ // Refs of a v-for are registered in creation order, not in list order
292+ this .$refs .card ? .find ((card ) => card .id === newCard .id )? .scrollIntoView ()
293+ })
381294 },
382295 setupAutoscrollOnDrag () {
383296 let timer
@@ -436,18 +349,6 @@ export default {
436349 }
437350
438351 & .stack -- add- card- at- top {
439- .stack__header {
440- order: 1 ;
441- }
442-
443- .stack__card - add {
444- order: 2 ;
445- }
446-
447- .stack__cards - list {
448- order: 3 ;
449- }
450-
451352 & : after {
452353 content: ' ' ;
453354 display: block;
@@ -573,84 +474,6 @@ export default {
573474 }
574475 }
575476
576- .stack__card - add {
577- flex- shrink: 0 ;
578- z- index: 100 ;
579- display: flex;
580- background- color: var (-- color- main- background);
581- position: relative;
582-
583- .stack -- add- card- at- top & {
584- padding- top: $stack- gap;
585-
586- & : after {
587- content: ' ' ;
588- display: block;
589- position: absolute;
590- width: 100 % ;
591- height: $stack- gap;
592- bottom: 0 ;
593- z- index: 99 ;
594- pointer- events: none;
595- background- image: linear- gradient (180deg , var (-- color- main- background) 0 % , transparent 100 % );
596- transform: translateY (100 % );
597- }
598- }
599-
600- .stack -- add- card- at- bottom & {
601- padding- bottom: $stack- gap;
602- }
603-
604- // Smooth fade out of the cards at the top
605- & : before {
606- content: ' ' ;
607- display: block;
608- position: absolute;
609- width: 100 % ;
610- height: $stack- gap;
611- z- index: 99 ;
612- transition: bottom var (-- animation- slow);
613- background- image: linear- gradient (0deg , var (-- color- main- background) 0 % , transparent 100 % );
614- transform: translateY (- 100 % );
615- }
616-
617- : deep (.stack__card - add - button .button - vue ) {
618- -- button- size: var (-- stack- card- add- control- height);
619- color: var (-- color- text- maxcontrast);
620-
621- & : hover: not (: disabled),
622- & : focus- visible {
623- color: var (-- color- main- text);
624- }
625- }
626-
627- form {
628- display: flex;
629- width: 100 % ;
630- height: var (-- stack- card- add- control- height);
631- box- sizing: border- box;
632- border: 2px solid var (-- color- border- maxcontrast);
633- border- radius: var (-- border- radius- large);
634- overflow: hidden;
635- padding: 2px ;
636- }
637-
638- & .icon - loading- small: after,
639- & .icon - loading- small- dark: after {
640- margin- inline- start: calc (50 % - 25px );
641- }
642-
643- input[type= text] {
644- flex- grow: 1 ;
645- padding- inline- end: 16px ;
646- }
647-
648- input {
649- border: none;
650- margin: 0 ;
651- }
652- }
653-
654477 .modal__content {
655478 width: 25vw ;
656479 min- width: 250px ;
0 commit comments