Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
GuiΩ
ActivitesMentales_Django
Commits
11e80a01
Commit
11e80a01
authored
Jun 26, 2022
by
GuiΩ
Browse files
feat(seance): copy/paste on context menu
parent
dc197372
Changes
9
Hide whitespace changes
Inline
Side-by-side
frontend/src/assets/_base.sass
0 → 100644
View file @
11e80a01
/*!
/ Copyright (c) 2022.
/ GuiΩ guillaume.claus@worldonline.fr
/
$color-selector
:
#d9ffe6
$color-feuille
:
#ffdffb
$color-border
:
lightGray
$color-disabled
:
#aaa
$borders
:
solid
2px
$color-border
\ No newline at end of file
frontend/src/components/ContextMenu.vue
View file @
11e80a01
...
...
@@ -4,7 +4,7 @@
-->
<
script
lang=
"ts"
setup
>
import
{
nextTick
,
reactive
,
ref
}
from
"
vue
"
;
import
{
nextTick
,
reactive
,
ref
,
watchEffect
}
from
"
vue
"
;
export
interface
IContextMenuOption
{
name
?:
string
,
...
...
frontend/src/components/FeuilleList.vue
View file @
11e80a01
...
...
@@ -21,11 +21,11 @@ Events:
import
{
useClassesStore
}
from
"
@/store/classes
"
;
import
{
computed
,
provide
,
reactive
}
from
"
vue
"
;
import
{
useUndoablesStore
}
from
"
@/store/undoables
"
;
import
type
{
ISeance
,
ISeanceR
,
SeanceID
}
from
"
@/types
"
;
import
type
{
IClipboardContent
,
IClipboardSeance
,
ISeance
,
ISeanceR
,
ISelection
,
SeanceID
}
from
"
@/types
"
;
interface
IFeuilleListState
{
selection
:
{
seances
:
SeanceID
[]}
,
clipboard
:
{
seances
:
Partial
<
ISeanceR
>
[]}
selection
:
ISelection
,
clipboard
:
IClipboardContent
}
const
feuilles
=
useFeuillesStore
()
...
...
@@ -45,24 +45,21 @@ Events:
const
currFeuilles
=
computed
(()
=>
feuilles
.
getCurrFeuilles
)
function
setSelection
(
newSelection
)
{
function
setSelection
(
newSelection
:
ISelection
)
{
console
.
debug
(
'
FL.setSelection
'
,
newSelection
)
state
.
selection
=
newSelection
}
function
onCopy
(
cont
ent
)
{
console
.
debug
(
'
FL.
setClipboard
'
,
cont
ent
)
const
newSeances
:
Partial
<
I
Seance
R
>
[]
=
[]
function
onCopy
(
event
:
MouseEv
ent
)
{
console
.
debug
(
'
FL.
copy
'
,
ev
ent
)
const
newSeances
:
IClipboard
Seance
[]
=
[]
for
(
const
seanceId
of
state
.
selection
.
seances
)
{
const
seance
=
feuilles
.
getSeance
(
seanceId
)
const
newSeance
=
{
feuille
:
seance
?.
feuille
!
,
questionsPosees
:
[]}
const
newSeance
:
IClipboardSeance
=
{
feuille
:
seance
?.
feuille
!
,
questionsPosees
:
[]}
for
(
const
questionPosee
of
seance
?.
questionsPosees
!
)
newSeance
.
questionsPosees
!
.
push
({
id
:
null
,
position
:
questionPosee
.
position
,
question
:
questionPosee
.
question
,
score
:
null
,
seance
:
null
})
newSeances
.
push
(
newSeance
)
}
...
...
frontend/src/components/FeuilleListFeuille.vue
View file @
11e80a01
...
...
@@ -71,16 +71,16 @@ Events:
.FeuilleHeader
span.FeuilleHeader_deleteFeuilleBtn(
:class="{disabled: ! isDeletable}"
:title="isDeletable ? 'Supprimer' : 'Impossible de supprimer une feuille évaluée'"
:title="isDeletable ? 'Supprimer
la feuille
' : 'Impossible de supprimer une feuille évaluée'"
@click="deleteFeuille") 🗙
|
| feuille
{{
feuille
.
position
}}
|
template(v-for="model of latex.getAllModels")
template(v-if="model.type === 'FE'")
a(:href="'api/feuille/' + feuille.id + '/get_pdf/?model_id=' + model.id")
{{
model
.
label
}}
.pdf
|
div
(v-if="feuille.seances && feuille.seances.length")
//
|
//
template(v-for="model of latex.getAllModels")
//
template(v-if="model.type === 'FE'")
//
a(:href="'api/feuille/' + feuille.id + '/get_pdf/?model_id=' + model.id")
{{
model
.
label
}}
.pdf
//
|
.FeuilleBody
(v-if="feuille.seances && feuille.seances.length")
template(
v-for="seanceId in feuille.seances"
:key="seanceId")
...
...
@@ -99,44 +99,56 @@ Events:
@seance-changed="$emit('seance-changed', $event)"
@show-question-tooltip="$emit('show-question-tooltip', $event)"
@show-context-menu="$emit('show-context-menu', $event)")/
button.Feuille_addSeanceBtn(
type="button"
@click="addSeance({feuille: props.feuilleId})"
) Ajouter une séance
.FeuilleFooter
button.Feuille_addSeanceBtn(
type="button"
@click="addSeance({feuille: props.feuilleId})"
) Ajouter une séance
br
| feuille
{{
feuille
.
position
}}
- Téléchagements :
|
template(v-for="model of latex.getAllModels")
template(v-if="model.type === 'FE'")
a(:href="'api/feuille/' + feuille.id + '/get_pdf/?model_id=' + model.id")
{{
model
.
label
}}
.pdf
|
br
br
</
template
>
<
style
>
.Feuille
{
display
:
flex
;
flex-direction
:
column
;
border-bottom
:
solid
3px
black
;
padding
:
0
;
margin
:
0
;
}
.FeuilleHeader
{
/*display: block;*/
align-self
:
center
;
flex
:
0
0
auto
;
font-size
:
24px
;
margin
:
10px
;
}
.FeuilleHeader_deleteFeuilleBtn
{
color
:
red
;
cursor
:
pointer
;
}
.FeuilleHeader_deleteFeuilleBtn.disabled
{
color
:
gray
;
cursor
:
default
;
}
.Feuille_addSeanceBtn
{
align-self
:
center
;
margin
:
20px
;
max-width
:
fit-content
;
}
<
style
lang=
"sass"
>
@use
'../assets/base'
.Feuille
display
:
flex
flex-direction
:
column
border-bottom
:
solid
3px
black
padding
:
0
margin
:
0
.FeuilleHeader
align-self
:
center
flex
:
0
0
auto
font-size
:
24px
margin
:
10px
.FeuilleHeader_deleteFeuilleBtn
color
:
red
cursor
:
pointer
.FeuilleHeader_deleteFeuilleBtn.disabled
color
:
base
.
$color-disabled
cursor
:
default
.FeuilleFooter
align-self
:
center
.Feuille_addSeanceBtn
align-self
:
center
margin
:
20px
min-width
:
fit-content
</
style
>
\ No newline at end of file
frontend/src/components/FeuilleListFeuilleSeance.vue
View file @
11e80a01
...
...
@@ -198,30 +198,47 @@ Events:
emit
(
'
set-selection
'
,
newSelection
)
}
function
onContextMenu
(
event
:
MouseEvent
)
{
async
function
onContextMenu
(
event
:
MouseEvent
)
{
console
.
debug
(
'
FLFS.onContextMenu
'
,
event
,
props
.
clipboard
)
if
(
!
props
.
selection
.
seances
.
includes
(
props
.
seanceId
))
{
if
(
!
props
.
selection
.
seances
.
includes
(
props
.
seanceId
))
{
onClick
(
event
)
await
nextTick
()
}
// emit('set-selection', {seances: [ props.seanceId ]})
const
multipleSel
=
props
.
selection
.
seances
.
length
>
1
const
multipleClip
=
props
.
clipboard
.
seances
.
length
>
1
emit
(
'
show-context-menu
'
,
{
event
:
event
,
options
:[
{
name
:
'
Copier
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
)
,
callback
:
()
=>
emit
(
'
copy
'
,
event
)
},
{
name
:
'
Couper
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
)
,
disabled
:
!
(
selectionIsDeletable
.
value
&&
isDeletable
.
value
),
callback
:
()
=>
{
emit
(
'
copy
'
,
event
)
;
nextTick
();
undoables
.
deleteSeances
(
props
.
selection
.
seances
)
}},
{
name
:
'
Supprimer
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
),
disabled
:
!
(
selectionIsDeletable
.
value
&&
isDeletable
.
value
),
callback
:
()
=>
undoables
.
deleteSeances
(
props
.
selection
.
seances
)},
{
name
:
'
Insérer
'
+
(
multipleClip
?
'
les seances
'
:
'
la seance
'
)
+
'
avant
'
,
emit
(
'
show-context-menu
'
,
{
event
:
event
,
options
:
[
{
name
:
'
Copier
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
),
callback
:
()
=>
emit
(
'
copy
'
,
event
)
},
{
name
:
'
Couper
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
),
disabled
:
!
(
selectionIsDeletable
.
value
&&
isDeletable
.
value
),
callback
:
()
=>
{
emit
(
'
copy
'
,
event
);
nextTick
();
undoables
.
deleteSeances
(
props
.
selection
.
seances
)
}
},
{
name
:
'
Supprimer
'
+
(
multipleSel
?
'
les seances
'
:
'
la seance
'
),
disabled
:
!
(
selectionIsDeletable
.
value
&&
isDeletable
.
value
),
callback
:
()
=>
undoables
.
deleteSeances
(
props
.
selection
.
seances
)
},
{
name
:
'
Insérer
'
+
(
multipleClip
?
'
les seances
'
:
'
la seance
'
)
+
'
avant
'
,
disabled
:
props
.
clipboard
.
seances
.
length
<
1
,
callback
:
()
=>
pasteSeance
(
seance
.
value
?.
position
!
)},
{
name
:
'
Insérer
'
+
(
multipleClip
?
'
les seances
'
:
'
la seance
'
)
+
'
après
'
,
callback
:
()
=>
pasteSeance
(
seance
.
value
?.
position
!
)
},
{
name
:
'
Insérer
'
+
(
multipleClip
?
'
les seances
'
:
'
la seance
'
)
+
'
après
'
,
disabled
:
props
.
clipboard
.
seances
.
length
<
1
,
callback
:
()
=>
pasteSeance
(
seance
.
value
?.
position
!
+
1
)},
{
name
:
'
Annuler
'
}]})
callback
:
()
=>
pasteSeance
(
seance
.
value
?.
position
!
+
1
)
},
{
name
:
'
Annuler
'
}]
})
}
...
...
@@ -317,6 +334,7 @@ Events:
</
template
>
<
style
lang=
"sass"
>
@use
'../assets/base'
.Seance
display
:
flex
...
...
@@ -339,7 +357,7 @@ Events:
color
:
red
cursor
:
pointer
&
.disabled
color
:
gray
color
:
base
.
$color-disabled
cursor
:
default
...
...
frontend/src/components/MainPage.vue
View file @
11e80a01
...
...
@@ -142,7 +142,7 @@
}
function
onQuestionEdit
({
questionId
:
questionId
,
action
:
action
}:
{
questionId
:
QuestionID
,
action
:
'
edit
'
|
'
duplicate
'
})
{
questionTooltip
.
value
.
close
()
questionTooltip
.
value
!
.
close
()
state
.
onLoaded
=
undefined
switch
(
action
)
{
case
"
edit
"
:
...
...
@@ -182,16 +182,15 @@ QuestionTooltip(
@selection-changed="themes.setSelectedTheme($event[0])")/
.Selector_question
button(data-testid="newQuestion" @click="$refs.questionEditor.edit()") Nouvelle question
.Selector_questionListContainer
QuestionList.Selector_questionList(
ref="questionList"
:themeId="themes.getSelectedId"
:classeId="classes.getSelectedId"
@show-question-tooltip="onShowQuestionTooltip"
@show-context-menu="onShowContextMenu"
@question-edit="onQuestionEdit"
@question-drop="onQuestionDrop")
//@question-duplicate="questionDuplicate($event.questionId)"")/
QuestionList.Selector_questionList(
ref="questionList"
:themeId="themes.getSelectedId"
:classeId="classes.getSelectedId"
@show-question-tooltip="onShowQuestionTooltip"
@show-context-menu="onShowContextMenu"
@question-edit="onQuestionEdit"
@question-drop="onQuestionDrop")
//@question-duplicate="questionDuplicate($event.questionId)"")/
FeuilleList.FeuilleList(
ref="feuilleList"
...
...
@@ -206,89 +205,80 @@ QuestionTooltip(
</
template
>
<
style
>
body
{
background-color
:
#eff0f1
;
margin
:
0
;
}
.MessageBox
{
z-index
:
10
;
}
.QuestionEditor
{
z-index
:
5
;
}
#main-page
{
position
:
absolute
;
top
:
0
;
bottom
:
0
;
right
:
0
;
left
:
0
;
display
:
flex
;
flex-direction
:
column
;
align-content
:
flex-start
;
}
.Toolbar
{
flex
:
0
0
auto
;
width
:
100%
;
border-bottom-style
:
inset
;
}
.Mainpanel
{
flex
:
1
1
auto
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
flex-start
;
border
:
solid
;
height
:
calc
(
100%
-
45px
);
}
.Selector
{
flex
:
1
1
0
;
display
:
flex
;
flex-direction
:
row
;
background-color
:
#d9ffe6
;
padding
:
10px
;
border
:
solid
2px
lightGray
;
height
:
calc
(
100%
-
24px
);
}
.Selector_theme
{
flex
:
1
1
auto
;
vertical-align
:
top
;
background-color
:
white
;
border
:
solid
2px
lightGray
;
overflow
:
auto
;
/*max-height: 600px;*/
/*overflow-y: scroll;*/
margin
:
0
;
}
.Selector_question
{
text-align
:
center
;
display
:
flex
;
flex-direction
:
column
;
border-bottom
:
solid
2px
lightGray
;
}
.Selector_questionListContainer
{
flex
:
1
1
auto
;
overflow-y
:
auto
;
min-width
:
230px
;
}
.Selector_questionList
{
/*max-height: 100%;*/
/*overflow-y: scroll;*/
/*scroll-behavior: smooth;*/
/*height: 500px;*/
border
:
solid
2px
lightGray
;
}
.FeuilleList
{
flex
:
10
10
auto
;
}
<
style
lang=
"sass"
>
@use
'../assets/base'
body
background-color
:
#eff0f1
margin
:
0
.MessageBox
z-index
:
10
.QuestionEditor
z-index
:
5
#main-page
position
:
absolute
top
:
0
bottom
:
0
right
:
0
left
:
0
display
:
flex
flex-direction
:
column
align-content
:
flex-start
.Toolbar
flex
:
0
0
auto
width
:
100%
border-bottom-style
:
inset
.Mainpanel
flex
:
1
1
auto
display
:
flex
flex-direction
:
row
align-items
:
flex-start
border
:
solid
height
:
calc
(
100%
-
45px
)
.Selector
flex
:
1
1
0
display
:
flex
flex-direction
:
row
background-color
:
base
.
$color-selector
padding
:
10px
border
:
base
.
$borders
height
:
calc
(
100%
-
24px
)
.Selector_theme
flex
:
1
1
auto
vertical-align
:
top
background-color
:
white
border
:
base
.
$borders
overflow
:
auto
/*max-height: 600px*/
/*overflow-y: scroll*/
margin
:
0
.Selector_question
text-align
:
center
display
:
flex
flex-direction
:
column
border-bottom
:
base
.
$borders
.Selector_questionList
border
:
base
.
$borders
.FeuilleList
flex
:
10
10
auto
</
style
>
\ No newline at end of file
frontend/src/components/QuestionList.vue
View file @
11e80a01
...
...
@@ -389,9 +389,10 @@ Events:
</
script
>
<
template
lang=
"pug"
>
#question-list(ref="root")
{{
questionsIds
.
length
}}
question
{{
questionsIds
.
length
>
1
?
'
s
'
:
''
}}
div
QuestionListNode(
.QuestionList(ref="root")
|
{{
questionsIds
.
length
}}
question
{{
questionsIds
.
length
>
1
?
'
s
'
:
''
}}
.QuestionList_list
QuestionListNode.QuestionList_node(
v-for="(questionId, index) in questionsIds"
:key="index"
:ref="el => questionNodes[index] = el"
...
...
@@ -410,10 +411,20 @@ Events:
@drop.prevent="onDrop($event, index, questionId)"
@show-context-menu="$emit('show-context-menu', $event)"
@show-question-tooltip="$emit('show-question-tooltip', $event)")/
//question-edit="$emit('question-edit', $event)"
//)/
</
template
>
<
style
>
<
style
lang=
"sass"
>
@use
'../assets/base'
.QuestionList
flex
:
1
1
auto
height
:
calc
(
100%
-
100px
)
min-width
:
212px
display
:
flex
flex-direction
:
column
.QuestionList_list
flex
:
1
1
auto
overflow-y
:
auto
border-top
:
base
.
$borders
</
style
>
frontend/src/components/QuestionListNode.vue
View file @
11e80a01
...
...
@@ -58,7 +58,7 @@
</
script
>
<
template
lang=
"pug"
>
.
q
uestio
n
(
.
Q
uestio
List_node
(
ref="root"
:id="'question_' + questionId"
:data-testid="'question_' + questionId"
...
...
@@ -67,7 +67,7 @@
v-for="(score, index) in scores"
:key="index"
:score="score")/
img(
img
.QuestionList_imageThumb
(
v-if="question"
:title="'question ' + questionId"
:alt="'question ' + questionId"
...
...
@@ -82,36 +82,35 @@
@dragover.prevent
@drop.prevent="resend('drop', $event)"
draggable="true")/
.
q
uestion
_vi
de(v-else) Question
{{
questionId
}}
.
Q
uestion
Liste_emptyNo
de(v-else) Question
{{
questionId
}}
</
template
>
<
style
scoped
>
.question
{
background-color
:
rgba
(
255
,
255
,
255
,
.8
);
padding
:
0
;
margin
:
0
;
}
.question.selected
{
background-color
:
rgba
(
255
,
150
,
150
,
.8
);
}
img
.editable
{
background
:
rgba
(
100
,
100
,
100
,
.8
);
}
img
.avalider
{
background
:
rgba
(
200
,
200
,
200
,
.8
);
}
img
.atrier
{
border
:
1px
solid
red
;
}
.question_vide
{
height
:
60px
;
margin
:
auto
;
/*padding-top: 20px;*/
background
:
rgba
(
255
,
255
,
255
,
.8
)
center
no-repeat
url("/static/loading.gif")
;
border
:
1px
solid
black
;
}
<
style
lang=
"sass"
>
@use
'../assets/base'
.QuestionList_node
background-color
:
rgba
(
255
,
255
,
255
,
.8
)
padding
:
0
margin
:
0
&
.selected
background-color
:
rgb
(
242
,
228
,
240
)
border
:
solid
1px
rgba
(
255
,
0
,
0
,
1
)
.QuestionList_imageThumb
&
.editable
background
:
rgba
(
100
,
100
,
100
,
.8
)
&
.avalider
background
:
rgba
(
200
,
200
,
200
,
.8
)
&
.atrier
border
:
1px
solid
red
.QuestionList_emptyNode
height
:
60px
margin
:
auto
/*padding-top: 20px*/
background
:
rgba
(
255
,
255
,
255
,
.8
)
center
no-repeat
url("/static/loading.gif")
border
:
1px
solid
black
</
style
>
\ No newline at end of file
frontend/src/types.ts
View file @
11e80a01
...
...
@@ -13,6 +13,20 @@ export interface IClasse {
feuilles
:
FeuilleID
[],
}
export
interface
IClipboardContent
{
seances
:
IClipboardSeance
[]
}
export
interface
IClipboardQuestionPosee
{
position
:
number
,
question
:
QuestionID
}
export
interface
IClipboardSeance
{
feuille
:
FeuilleID
,
questionsPosees
:
IClipboardQuestionPosee
[]
}
export
interface
IQuestionDropEvent
{
event
:
DragEvent
,
seanceSourceId
?:
SeanceID
,
...
...
@@ -121,6 +135,10 @@ export interface ISeanceR { // recursive