-
-
Notifications
You must be signed in to change notification settings - Fork 957
feat(common): add a source/zone drag abstraction on sortablejs #15086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
41797e9
23655b5
e7a35c1
c365292
ce49b4a
cbf0d90
b7cef7a
8b64ce5
ff845d5
980253f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,102 +34,98 @@ | |
| </div> | ||
| </template> | ||
|
|
||
| <DragContainer | ||
| <DraggableRegion | ||
| key="drag-container" | ||
| :items="questions" | ||
| @sort="handleQuestionOrderChange" | ||
| @dragStart="handleDragStart" | ||
| @update:items="handleQuestionOrderChange" | ||
| @dragstart="handleDragStart" | ||
| @dragend="handleDragEnd" | ||
| > | ||
| <transition-group | ||
| tag="div" | ||
| name="list" | ||
| <DraggableItem | ||
| v-for="(question, index) in questions" | ||
| :key="`drag-${question.item}`" | ||
| tabindex="-1" | ||
| :style="{ | ||
| background: $themeTokens.surface, | ||
| }" | ||
| > | ||
| <Draggable | ||
| v-for="(question, index) in questions" | ||
| :key="`drag-${question.item}`" | ||
| tabindex="-1" | ||
| :style="{ | ||
| background: $themeTokens.surface, | ||
| <AccordionItem | ||
| :title="getDisplayQuestionTitle(question, getQuestionContent(question)?.title)" | ||
| :disabledTitle="questionItemsToReplace?.includes(question.item)" | ||
| :aria-selected="questionIsChecked(question)" | ||
| :headerAppearanceOverrides="{ | ||
| userSelect: dragActive ? 'none !important' : 'text', | ||
| }" | ||
| > | ||
| <AccordionItem | ||
| :title="getDisplayQuestionTitle(question, getQuestionContent(question)?.title)" | ||
| :disabledTitle="questionItemsToReplace?.includes(question.item)" | ||
| :aria-selected="questionIsChecked(question)" | ||
| :headerAppearanceOverrides="{ | ||
| userSelect: dragActive ? 'none !important' : 'text', | ||
| }" | ||
| > | ||
| <template #leading-actions> | ||
| <DragHandle v-if="isSortable"> | ||
| <div> | ||
| <DragSortWidget | ||
| :isFirst="index === 0" | ||
| :isLast="index === questions.length - 1" | ||
| :itemLabel=" | ||
| getDisplayQuestionTitle(question, getQuestionContent(question)?.title) | ||
| " | ||
| :position="index + 1" | ||
| :total="questions.length" | ||
| @moveUp="() => handleKeyboardDragUp(index)" | ||
| @moveDown="() => handleKeyboardDragDown(index)" | ||
| /> | ||
| </div> | ||
| </DragHandle> | ||
| <KCheckbox | ||
| v-if="isSelectable" | ||
| class="accordion-item-checkbox" | ||
| :checked="questionIsChecked(question)" | ||
| :disabled="questionCheckboxDisabled(question)" | ||
| @change=" | ||
| (value, $event) => handleQuestionCheckboxChange(question.item, value, $event) | ||
| " | ||
| <template #leading-actions> | ||
| <DraggableHandle v-if="isSortable"> | ||
| <div> | ||
| <DragSortWidget | ||
| :isFirst="index === 0" | ||
| :isLast="index === questions.length - 1" | ||
| :itemLabel=" | ||
| getDisplayQuestionTitle(question, getQuestionContent(question)?.title) | ||
| " | ||
| :position="index + 1" | ||
| :total="questions.length" | ||
| @moveUp="() => handleKeyboardDragUp(index)" | ||
| @moveDown="() => handleKeyboardDragDown(index)" | ||
| /> | ||
| </div> | ||
| </DraggableHandle> | ||
| <KCheckbox | ||
| v-if="isSelectable" | ||
| class="accordion-item-checkbox" | ||
| :checked="questionIsChecked(question)" | ||
| :disabled="questionCheckboxDisabled(question)" | ||
| @change=" | ||
| (value, $event) => handleQuestionCheckboxChange(question.item, value, $event) | ||
| " | ||
| /> | ||
| </template> | ||
| <template #trailing-actions> | ||
| <span v-if="questionItemsToReplace?.includes(question.item)"> | ||
| {{ replacingThisQuestionLabel$() }} | ||
| </span> | ||
| <slot | ||
| name="question-trailing-actions" | ||
| :question="question" | ||
| ></slot> | ||
| </template> | ||
| <template #content> | ||
| <div | ||
| :id="`question-panel-${question.item}`" | ||
| :style="{ userSelect: dragActive ? 'none !important' : 'text' }" | ||
| > | ||
| <ContentViewer | ||
| v-if="questionContentExists(question)" | ||
| :ref="`contentRenderer-${question.item}`" | ||
| :contentNode="getQuestionContent(question)" | ||
| :itemId="question.question_id" | ||
| :allowHints="false" | ||
| :interactive="false" | ||
| :showCorrectAnswer="true" | ||
| @interaction="() => null" | ||
| @updateProgress="() => null" | ||
| @updateContentState="() => null" | ||
| @error="err => $emit('error', err)" | ||
| /> | ||
| </template> | ||
| <template #trailing-actions> | ||
| <span v-if="questionItemsToReplace?.includes(question.item)"> | ||
| {{ replacingThisQuestionLabel$() }} | ||
| </span> | ||
| <div v-else> | ||
| <KIcon | ||
| icon="warning" | ||
| :style="{ fill: $themePalette.yellow.v_600 }" | ||
| /> | ||
| {{ coreString('resourceNotFoundOnDevice') }} | ||
| </div> | ||
| <slot | ||
| name="question-trailing-actions" | ||
| name="questionExtraContent" | ||
| :question="question" | ||
| ></slot> | ||
| </template> | ||
| <template #content> | ||
| <div | ||
| :id="`question-panel-${question.item}`" | ||
| :style="{ userSelect: dragActive ? 'none !important' : 'text' }" | ||
| > | ||
| <ContentViewer | ||
| v-if="questionContentExists(question)" | ||
| :ref="`contentRenderer-${question.item}`" | ||
| :contentNode="getQuestionContent(question)" | ||
| :itemId="question.question_id" | ||
| :allowHints="false" | ||
| :interactive="false" | ||
| :showCorrectAnswer="true" | ||
| @interaction="() => null" | ||
| @updateProgress="() => null" | ||
| @updateContentState="() => null" | ||
| @error="err => $emit('error', err)" | ||
| /> | ||
| <div v-else> | ||
| <KIcon | ||
| icon="warning" | ||
| :style="{ fill: $themePalette.yellow.v_600 }" | ||
| /> | ||
| {{ coreString('resourceNotFoundOnDevice') }} | ||
| </div> | ||
| <slot | ||
| name="questionExtraContent" | ||
| :question="question" | ||
| ></slot> | ||
| </div> | ||
| </template> | ||
| </AccordionItem> | ||
| </Draggable> | ||
| </transition-group> | ||
| </DragContainer> | ||
| </div> | ||
| </template> | ||
| </AccordionItem> | ||
| </DraggableItem> | ||
| </DraggableRegion> | ||
| </AccordionContainer> | ||
|
|
||
| </template> | ||
|
|
@@ -139,10 +135,10 @@ | |
|
|
||
| import { computed, ref } from 'vue'; | ||
| import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings'; | ||
| import Draggable from 'kolibri-common/components/sortable/Draggable'; | ||
| import DragHandle from 'kolibri-common/components/sortable/DragHandle'; | ||
| import DragContainer from 'kolibri-common/components/sortable/DragContainer'; | ||
| import DragSortWidget from 'kolibri-common/components/sortable/DragSortWidget'; | ||
| import DraggableItem from 'kolibri-common/components/draggable/DraggableItem'; | ||
| import DraggableHandle from 'kolibri-common/components/draggable/DraggableHandle'; | ||
| import DraggableRegion from 'kolibri-common/components/draggable/DraggableRegion'; | ||
| import DragSortWidget from 'kolibri-common/components/draggable/DragSortWidget'; | ||
| import AccordionItem from 'kolibri-common/components/accordion/AccordionItem'; | ||
| import commonCoreStrings from 'kolibri/uiText/commonCoreStrings'; | ||
| import AccordionContainer from 'kolibri-common/components/accordion/AccordionContainer'; | ||
|
|
@@ -151,9 +147,9 @@ | |
| export default { | ||
| name: 'QuestionsAccordion', | ||
| components: { | ||
| Draggable, | ||
| DragHandle, | ||
| DragContainer, | ||
| DraggableItem, | ||
| DraggableHandle, | ||
| DraggableRegion, | ||
| DragSortWidget, | ||
| AccordionItem, | ||
| AccordionContainer, | ||
|
|
@@ -319,17 +315,21 @@ | |
| // Used to mitigate the issue of text being selected while dragging | ||
| this.dragActive = true; | ||
| }, | ||
| handleQuestionOrderChange({ newArray }) { | ||
| this.$emit('sort', { newArray }); | ||
| handleDragEnd() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Resolved — addressed in the current code. praise: Splitting this out of the sort handler fixes a real bug — QA confirms |
||
| // Reset on drag end (not only on a reorder) so a drag that changes nothing | ||
| // still re-enables text selection. | ||
| this.dragActive = false; | ||
| }, | ||
| handleQuestionOrderChange(newArray) { | ||
| this.$emit('sort', { newArray }); | ||
| }, | ||
| handleKeyboardDragDown(oldIndex) { | ||
| const newArray = this.moveDownOne(oldIndex, this.questions); | ||
| this.handleQuestionOrderChange({ newArray }); | ||
| this.handleQuestionOrderChange(newArray); | ||
| }, | ||
| handleKeyboardDragUp(oldIndex) { | ||
| const newArray = this.moveUpOne(oldIndex, this.questions); | ||
| this.handleQuestionOrderChange({ newArray }); | ||
| this.handleQuestionOrderChange(newArray); | ||
| }, | ||
| handleQuestionCheckboxChange(questionItem, value, $event) { | ||
| $event.stopPropagation(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Stale name — this is a
DraggableRegionnow. It's a static key on a non-v-for, non-v-ifelement, so dropping it entirely works too.