Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
41797e9
feat(draggable): scaffold the draggable component dir
habibayman Jul 24, 2026
23655b5
feat(draggable): create the universe component and composable
habibayman Jul 24, 2026
e7a35c1
feat(draggable): create the region component and composable
habibayman Jul 24, 2026
c365292
feat(draggable): add draggable handle and item components
habibayman Jul 24, 2026
ce49b4a
feat(draggable): migrate qti order interaction to draggable
habibayman Jul 24, 2026
cbf0d90
feat(draggable): migrate questions accordion to draggable
habibayman Jul 24, 2026
b7cef7a
feat(draggable): migrate section order to draggable
habibayman Jul 24, 2026
8b64ce5
feat(draggable): migrate learner resource table to draggable
habibayman Jul 24, 2026
ff845d5
feat(draggable): migrate rearrange channels page to draggable
habibayman Jul 24, 2026
980253f
refactor: delete sortable component in favor of the new draggable
habibayman Jul 24, 2026
ed777c2
feat(connect-pairs): add useAssociateSlots composable for pair state
habibayman Jul 29, 2026
ecb6ae5
feat(connect-pairs): add AssociateInteraction static rendering and fi…
habibayman Jul 29, 2026
581f101
feat(connect-pairs): add click only placement wiring
habibayman Jul 29, 2026
19a237e
feat(connect-pairs): wire basic dragging functionality
habibayman Aug 5, 2026
f04fe85
fix(connect-pairs): disable & not remove matched items in the pool
habibayman Aug 5, 2026
011cf68
fix(connect-pairs): chips stretching oversize on drag
habibayman Aug 5, 2026
49c46e4
feat(connect-pairs): add keyboard navigation support using up/down ar…
habibayman Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 97 additions & 97 deletions kolibri/plugins/coach/frontend/views/common/QuestionsAccordion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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';
Expand All @@ -151,9 +147,9 @@
export default {
name: 'QuestionsAccordion',
components: {
Draggable,
DragHandle,
DragContainer,
DraggableItem,
DraggableHandle,
DraggableRegion,
DragSortWidget,
AccordionItem,
AccordionContainer,
Expand Down Expand Up @@ -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() {
// 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();
Expand Down
Loading
Loading