From 4fb798073c71c361bbdf728e2720b30dae1bc76d Mon Sep 17 00:00:00 2001 From: Benoit Rospars <benoit.rospars@inria.fr> Date: Wed, 18 Jan 2023 12:09:45 +0100 Subject: [PATCH 01/13] Fix paths --- electron/components/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electron/components/main.js b/electron/components/main.js index 22cd34d2..4cf8962f 100644 --- a/electron/components/main.js +++ b/electron/components/main.js @@ -17,7 +17,7 @@ module.exports.createMainWindow = function () { webPreferences: { nodeIntegration: false, contextIsolation: true, - preload: path.join(__dirname, 'preload.js') + preload: path.join(__dirname, '../preload.js') } }); @@ -25,7 +25,7 @@ module.exports.createMainWindow = function () { mainWindow.loadURL( isDev ? 'http://localhost:8000' - : `file://${path.join(__dirname, '../dist/index.html')}` + : `file://${path.join(__dirname, '../../dist/index.html')}` ); mainWindow.center(); return mainWindow -- GitLab From b037a97ac8f01e6b168b600d47eafc000335e515 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:18:14 +0100 Subject: [PATCH 02/13] Issue #55 #75: Can click on a screen node to edit the screen --- src/_colors.scss | 2 ++ src/components/ContentButton.vue | 2 +- src/features/ePocFlow/ePocFlow.vue | 20 ++++++++---- src/features/ePocFlow/nodes/ChapterNode.vue | 4 +-- .../nodes/{ContentNode.vue => ScreenNode.vue} | 8 +++-- src/features/ePocFlow/nodes/ePocNode.vue | 4 +-- .../components/inputs/card/CardGroup.vue | 7 ++-- .../components/inputs/card/CardInput.vue | 16 ++++------ src/features/sideBar/components/ModelMenu.vue | 2 +- .../sideBar/components/ModelMenuV0.vue | 6 ++-- .../{ScreenNode.vue => ScreenTemplate.vue} | 0 src/global.scss | 6 ++++ src/shared/data/form.data.ts | 32 ++++++++++++++++++- src/shared/stores/editorStore.ts | 20 ++++++------ 14 files changed, 85 insertions(+), 44 deletions(-) rename src/features/ePocFlow/nodes/{ContentNode.vue => ScreenNode.vue} (89%) rename src/features/sideBar/components/{ScreenNode.vue => ScreenTemplate.vue} (100%) diff --git a/src/_colors.scss b/src/_colors.scss index 918a68c8..741f7dba 100644 --- a/src/_colors.scss +++ b/src/_colors.scss @@ -22,6 +22,7 @@ $editor-lightblue: #E8EDF3; $editor-shadow-outer: #ADBCD5; $editor-grayblue: #7789A6; $editor-blue: #00B3E9; +$node-active: #E1F4FA; [color-scheme="light"] { --background: #{$editor-light}; @@ -35,6 +36,7 @@ $editor-blue: #00B3E9; --list-border: #E6EAF4; --button-blue: #{$editor-lightblue}; --node: #{$inria-light}; + --node-active: #{$node-active}; } // Dark theme not implemented yet diff --git a/src/components/ContentButton.vue b/src/components/ContentButton.vue index 7618d846..a8ea82f3 100644 --- a/src/components/ContentButton.vue +++ b/src/components/ContentButton.vue @@ -22,7 +22,7 @@ function click(event) { class="btn btn-content" :class="[classList, { active: isActive }, { 'draggable': isDraggable }]" :draggable="isDraggable" - @click="click($event)" + @click.stop="click($event)" > <i :class="icon" /> <span v-if="subtitle" class="subtitle">{{ subtitle }}</span> diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue index b8a54fa9..a4caf150 100644 --- a/src/features/ePocFlow/ePocFlow.vue +++ b/src/features/ePocFlow/ePocFlow.vue @@ -1,9 +1,9 @@ <script setup lang="ts"> import { VueFlow, useVueFlow, Panel, PanelPosition } from '@vue-flow/core'; import { markRaw, nextTick, watch } from 'vue'; -import ContentNode from './nodes/ContentNode.vue'; +import ScreenNode from './nodes/ScreenNode.vue'; import CustomConnectContent from './edges/CustomConnectContent.vue'; -import { SideAction, NodeElement } from '../../shared/interfaces'; +import { SideAction, NodeElement, Form } from '../../shared/interfaces'; import { useEditorStore } from '../../shared/stores'; import ChapterNode from './nodes/ChapterNode.vue'; import ePocNode from './nodes/ePocNode.vue'; @@ -16,7 +16,7 @@ onConnect((params) => addEdges([{...params, updatable: true, style: { stroke: '# const editorStore = useEditorStore(); const nodeTypes = { - content: markRaw(ContentNode), + content: markRaw(ScreenNode), chapter: markRaw(ChapterNode), epoc: markRaw(ePocNode), add: markRaw(AddChapterNode), @@ -89,7 +89,8 @@ function addNode(position, actions: SideAction[]) { let elements: NodeElement[] = []; - const id = (nodes.value.length + 1).toString(); + const id = editorStore.generateId(); + const form = editorStore.getForm('screen'); actions.forEach((action) => { elements.push({ @@ -104,11 +105,11 @@ function addNode(position, actions: SideAction[]) { id: id, type: 'content', // Put animated: nodeIcons.length === 1 when implementing v2 - data: { elements: elements, readyToDrop: false, animated: false, title: 'Screen' }, + data: { elements: elements, readyToDrop: false, animated: false, title: 'Screen', form: form }, position, events: { click: () => { - console.log('node' + id + ' clicked'); + openForm(id, form); } } }; @@ -180,6 +181,11 @@ function addChapter() { findNode('2').position.y += 200; } +function openForm(id: string, form: Form) { + console.log('open screen Form'); + editorStore.openFormPanel(id, form); +} + </script> <template> @@ -199,7 +205,7 @@ function addChapter() { <button style="background-color: #ff0000; padding: 1rem; border-radius: 8px; border: none; cursor: pointer; font-size: 1.2rem;" @click="onDelete">Delete all</button> </Panel> <template #node-custom="{ id, data }"> - <ContentNode :id="id" :data="data" /> + <ScreenNode :id="id" :data="data" /> </template> <template #node-chapter="{ id, data }"> <ChapterNode :id="id" :data="data" /> diff --git a/src/features/ePocFlow/nodes/ChapterNode.vue b/src/features/ePocFlow/nodes/ChapterNode.vue index 8044da99..260b8d50 100644 --- a/src/features/ePocFlow/nodes/ChapterNode.vue +++ b/src/features/ePocFlow/nodes/ChapterNode.vue @@ -19,7 +19,7 @@ const props = defineProps<{ const element: NodeElement = { id: props.id, action: { icon: 'icon-chapitre', type: 'chapter'}, form: editorStore.getForm('chapter') }; function openForm(element: NodeElement) { - editorStore.openFormPanel(element); + editorStore.openFormPanel(element.id, element.form); } </script> @@ -28,7 +28,7 @@ function openForm(element: NodeElement) { <div> <ContentButton :icon="element.action.icon" - :is-active="editorStore.formPanel.openedElement ? editorStore.formPanel.openedElement.id === element.id : false" + :is-active="editorStore.openedNodeId ? editorStore.openedNodeId === element.id : false" :is-draggable="false" :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true, 'btn-content-large': true }" :subtitle="data.title" diff --git a/src/features/ePocFlow/nodes/ContentNode.vue b/src/features/ePocFlow/nodes/ScreenNode.vue similarity index 89% rename from src/features/ePocFlow/nodes/ContentNode.vue rename to src/features/ePocFlow/nodes/ScreenNode.vue index b05d932f..bdfa2786 100644 --- a/src/features/ePocFlow/nodes/ContentNode.vue +++ b/src/features/ePocFlow/nodes/ScreenNode.vue @@ -3,7 +3,7 @@ import { Handle, useVueFlow } from '@vue-flow/core'; import ContentButton from '../../../components/ContentButton.vue'; import { onMounted } from 'vue'; import { useEditorStore } from '../../../shared/stores'; -import { NodeElement } from '../../..//shared/interfaces'; +import { NodeElement } from '../../../shared/interfaces'; import { Position } from '@vue-flow/core'; const editorStore = useEditorStore(); @@ -62,7 +62,7 @@ function dragEnter(event) { const { nodes } = useVueFlow(); function openForm(element: NodeElement) { - editorStore.openFormPanel(element); + editorStore.openFormPanel(element.id, element.form); } </script> @@ -72,6 +72,8 @@ function openForm(element: NodeElement) { <Handle type="target" :position="Position.Left" /> <div :id="'node'+props.id" + :class=" { 'active': editorStore.openedNodeId ? editorStore.openedNodeId === props.id : false }" + class="node" @dragover="dragOver" @dragleave="dragLeave" @dragenter="dragEnter" @@ -80,7 +82,7 @@ function openForm(element: NodeElement) { v-for="element of props.data.elements" :key="element.action.icon" :icon="element.action.icon" - :is-active="editorStore.formPanel.openedElement ? editorStore.formPanel.openedElement.id === element.id : false" + :is-active="editorStore.openedNodeId ? editorStore.openedNodeId === element.id : false" :is-draggable="false" :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true }" @click="openForm(element)" diff --git a/src/features/ePocFlow/nodes/ePocNode.vue b/src/features/ePocFlow/nodes/ePocNode.vue index 9cc58976..8e0239ae 100644 --- a/src/features/ePocFlow/nodes/ePocNode.vue +++ b/src/features/ePocFlow/nodes/ePocNode.vue @@ -15,7 +15,7 @@ const props = defineProps<{ const element: NodeElement = { id: props.id, action: { icon: 'icon-epoc', type: 'epoc'}, form: editorStore.getForm('epoc') }; function openForm(element: NodeElement) { - editorStore.openFormPanel(element); + editorStore.openFormPanel(element.id, element.form); } </script> @@ -24,7 +24,7 @@ function openForm(element: NodeElement) { <div> <ContentButton :icon="element.action.icon" - :is-active="editorStore.formPanel.openedElement ? editorStore.formPanel.openedElement.id === element.id : false" + :is-active="editorStore.openedNodeId ? editorStore.openedNodeId === element.id : false" :is-draggable="false" :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true, 'btn-content-large': true }" subtitle="ePoc" diff --git a/src/features/forms/components/inputs/card/CardGroup.vue b/src/features/forms/components/inputs/card/CardGroup.vue index da7deed8..00c1ce50 100644 --- a/src/features/forms/components/inputs/card/CardGroup.vue +++ b/src/features/forms/components/inputs/card/CardGroup.vue @@ -35,6 +35,7 @@ const cardMap = new Map([ :model-value="inputs" item-key="index" handle=".card-header" + ghost-class="ghost" @change="emit('swapCard', $event)" > <template #item="{element, index}"> @@ -57,8 +58,4 @@ const cardMap = new Map([ class="add-card" @click="emit('addCard')" /> -</template> - -<style scoped lang="scss"> - -</style> \ No newline at end of file +</template> \ No newline at end of file diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index 8c2b983b..c6e4005e 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -20,18 +20,12 @@ const emit = defineEmits<{ (e: 'moveDownCard'): void; }>(); - -const dragging = ref(false); - </script> <template> <Transition> <div class="card draggable-card" - :class="{ 'dragging' : dragging }" - @dragstart="dragging = true" - @dragend="dragging = false" > <div class="card-header"> <h3>{{ title }} {{ pos }}</h3> @@ -64,7 +58,7 @@ const dragging = ref(false); width: 25rem; border-radius: 4px; margin-bottom: 1rem; - cursor: grab; + cursor: move; transition: all .2s linear; &-header { padding: 0 .7rem; @@ -100,8 +94,12 @@ const dragging = ref(false); &-content { padding: .7rem; } - &.dragging { - opacity: .3; + &.ghost { + background-color: var(--item-background); + border: 2px dashed var(--border); + * { + opacity: 0; + } } } </style> \ No newline at end of file diff --git a/src/features/sideBar/components/ModelMenu.vue b/src/features/sideBar/components/ModelMenu.vue index ee14f307..85d455c7 100644 --- a/src/features/sideBar/components/ModelMenu.vue +++ b/src/features/sideBar/components/ModelMenu.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> // The beta use the ModelMenuV0 component //@ts-nocheck -import ScreenNode from './ScreenNode.vue'; +import ScreenNode from './ScreenTemplate.vue'; import { useEpocStore } from '../../../shared/stores'; import { ref } from 'vue'; diff --git a/src/features/sideBar/components/ModelMenuV0.vue b/src/features/sideBar/components/ModelMenuV0.vue index 01abe82e..9ded0a4a 100644 --- a/src/features/sideBar/components/ModelMenuV0.vue +++ b/src/features/sideBar/components/ModelMenuV0.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import ScreenNode from './ScreenNode.vue'; +import ScreenTemplate from './ScreenTemplate.vue'; import { useEditorStore } from '../../../shared/stores'; import { Ref, ref } from 'vue'; import { Screen, SideAction } from '../../../shared/interfaces'; @@ -44,7 +44,7 @@ const displayScreen = ref(true); </div> <div v-if="displayScreen" class="screens"> <div class="col-left"> - <ScreenNode + <ScreenTemplate v-for="(screen, index) of leftCol" :key="index" :title="screen.title" @@ -53,7 +53,7 @@ const displayScreen = ref(true); /> </div> <div class="col-right"> - <ScreenNode + <ScreenTemplate v-for="(screen, index) of rightCol" :key="index" :title="screen.title" diff --git a/src/features/sideBar/components/ScreenNode.vue b/src/features/sideBar/components/ScreenTemplate.vue similarity index 100% rename from src/features/sideBar/components/ScreenNode.vue rename to src/features/sideBar/components/ScreenTemplate.vue diff --git a/src/global.scss b/src/global.scss index 82d534ce..1405c551 100644 --- a/src/global.scss +++ b/src/global.scss @@ -220,6 +220,12 @@ hr { margin-top: .5rem; } + &.active { + border: 1px solid var(--editor-blue); + box-shadow: 0 1px 8px 0 var(--editor-blue-shadow); + background-color: var(--node-active); + } + &-animate { padding: 1.5rem; transition: all .15s ease-in-out; diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index b09c370d..a6347d0a 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -269,4 +269,34 @@ const epocForm: Form = { ] }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm]; \ No newline at end of file +const screenForm: Form = { + type: 'screen', + name: 'Écran', + icon: 'icon-ecran', + fields: [ + { + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; + +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm]; \ No newline at end of file diff --git a/src/shared/stores/editorStore.ts b/src/shared/stores/editorStore.ts index 994289e2..5c830a3c 100644 --- a/src/shared/stores/editorStore.ts +++ b/src/shared/stores/editorStore.ts @@ -5,6 +5,8 @@ import { toRaw } from 'vue'; import { formsModel } from '../data/form.data'; +type uid = string; + interface EditorState { recentProjects: ePocProject[]; floatingMenu: boolean; @@ -12,9 +14,8 @@ interface EditorState { formPanel: { isOpen: boolean; form: Form; - openedElement: NodeElement; - openedScreen: Screen; }; + openedNodeId: uid | null; sideActions: SideAction[]; questions: SideAction[]; standardScreens: Screen[]; @@ -29,9 +30,8 @@ export const useEditorStore = defineStore('editor', { formPanel: { isOpen: false, form: null, - openedElement: null, - openedScreen: null }, + openedNodeId: null, sideActions: actionItems, questions: questions, standardScreens: standardScreen, @@ -63,18 +63,18 @@ export const useEditorStore = defineStore('editor', { this.floatingMenu = false; this.modelMenu = false; }, - openFormPanel(element: NodeElement): void { + openFormPanel(id: string, form: Form): void { this.formPanel.isOpen = true; - this.formPanel.form = element.form; - this.formPanel.openedElement = element; + this.formPanel.form = form; + this.openedNodeId = id; }, closeFormPanel(): void { this.formPanel.isOpen = false; this.formPanel.form = null; - this.formPanel.openedElement = null; + this.openedNodeId = null; }, //generate id of format 'aaaaaaaa'-'aaaa'-'aaaa'-'aaaa'-'aaaaaaaaaaaa' - generateId(): string { + generateId(): uid { const s4 = () => { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) @@ -94,7 +94,7 @@ export const useEditorStore = defineStore('editor', { // type: 'remove', // } // ]); - console.log(this.formPanel.openedElement.parentId); + console.log(this.openedNodeId); }, addInput(type: string, fieldIndex: number):void { const newInput: Input = { -- GitLab From b40bf0738bc2f5702fc29abe1c428dfdbb1ddedf Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:36:31 +0100 Subject: [PATCH 03/13] Remove title prop from screen node --- src/features/ePocFlow/ePocFlow.vue | 2 +- src/features/ePocFlow/nodes/ScreenNode.vue | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue index a4caf150..9f6e39a9 100644 --- a/src/features/ePocFlow/ePocFlow.vue +++ b/src/features/ePocFlow/ePocFlow.vue @@ -105,7 +105,7 @@ function addNode(position, actions: SideAction[]) { id: id, type: 'content', // Put animated: nodeIcons.length === 1 when implementing v2 - data: { elements: elements, readyToDrop: false, animated: false, title: 'Screen', form: form }, + data: { elements: elements, readyToDrop: false, animated: false, form: form }, position, events: { click: () => { diff --git a/src/features/ePocFlow/nodes/ScreenNode.vue b/src/features/ePocFlow/nodes/ScreenNode.vue index bdfa2786..411f3311 100644 --- a/src/features/ePocFlow/nodes/ScreenNode.vue +++ b/src/features/ePocFlow/nodes/ScreenNode.vue @@ -15,7 +15,6 @@ const props = defineProps<{ required: true; readyToDrop: boolean; animated: boolean; - title: string; elements: NodeElement[]; } }>(); @@ -68,7 +67,7 @@ function openForm(element: NodeElement) { </script> <template> - <p contenteditable="true" class="node-title">{{ data.title }}</p> + <p contenteditable="true" class="node-title">Screen</p> <Handle type="target" :position="Position.Left" /> <div :id="'node'+props.id" -- GitLab From 2f241236149c8588346a2c33cce194fbec141e35 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:18:14 +0100 Subject: [PATCH 04/13] Issue #55 #75: Can click on a screen node to edit the screen --- src/features/ePocFlow/nodes/ScreenNode.vue | 3 +- .../ScreenNode.vue~refs/remotes/origin/main | 113 ++++++++++++++++++ .../components/inputs/card/CardInput.vue | 1 - 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 src/features/ePocFlow/nodes/ScreenNode.vue~refs/remotes/origin/main diff --git a/src/features/ePocFlow/nodes/ScreenNode.vue b/src/features/ePocFlow/nodes/ScreenNode.vue index 411f3311..bdfa2786 100644 --- a/src/features/ePocFlow/nodes/ScreenNode.vue +++ b/src/features/ePocFlow/nodes/ScreenNode.vue @@ -15,6 +15,7 @@ const props = defineProps<{ required: true; readyToDrop: boolean; animated: boolean; + title: string; elements: NodeElement[]; } }>(); @@ -67,7 +68,7 @@ function openForm(element: NodeElement) { </script> <template> - <p contenteditable="true" class="node-title">Screen</p> + <p contenteditable="true" class="node-title">{{ data.title }}</p> <Handle type="target" :position="Position.Left" /> <div :id="'node'+props.id" diff --git a/src/features/ePocFlow/nodes/ScreenNode.vue~refs/remotes/origin/main b/src/features/ePocFlow/nodes/ScreenNode.vue~refs/remotes/origin/main new file mode 100644 index 00000000..411f3311 --- /dev/null +++ b/src/features/ePocFlow/nodes/ScreenNode.vue~refs/remotes/origin/main @@ -0,0 +1,113 @@ +<script setup lang="ts"> +import { Handle, useVueFlow } from '@vue-flow/core'; +import ContentButton from '../../../components/ContentButton.vue'; +import { onMounted } from 'vue'; +import { useEditorStore } from '../../../shared/stores'; +import { NodeElement } from '../../../shared/interfaces'; +import { Position } from '@vue-flow/core'; + +const editorStore = useEditorStore(); + +const props = defineProps<{ + id: string; + data: { + type: object; + required: true; + readyToDrop: boolean; + animated: boolean; + elements: NodeElement[]; + } +}>(); + +// This add an animation when the node is added to the flow +onMounted(() => { + const node = document.querySelector('#node' + props.id); + node.classList.add('node'); + if(props.data.animated) node.classList.add('node-creation-animation'); +}); + +//TODO: Think about refactoring this +let dragOverCount = 0; + +function dragOver(event) { + if(event.dataTransfer.types.length > 1) return; + dragOverCount ++; + if(dragOverCount > 25) { + dragOverCount = 0; + nodes.value.find(element => element.id === props.id).data.readyToDrop = true; + document.querySelector('#node'+props.id).classList.add('node-animate'); + + // To be sure the counter is set to 1 when ready to drop + counter = 1; + } +} + +// This counter is used to avoid triggering dragLeave when not necessary +let counter = 0; + +function dragLeave() { + counter --; + if (counter > 0) return; + nodes.value.find(element => element.id === props.id).data.readyToDrop = false; + document.querySelector('#node'+props.id).classList.remove('node-animate'); + dragOverCount = 0; +} + +function dragEnter(event) { + event.preventDefault(); + counter ++; +} + +const { nodes } = useVueFlow(); + +function openForm(element: NodeElement) { + editorStore.openFormPanel(element.id, element.form); +} + +</script> + +<template> + <p contenteditable="true" class="node-title">Screen</p> + <Handle type="target" :position="Position.Left" /> + <div + :id="'node'+props.id" + :class=" { 'active': editorStore.openedNodeId ? editorStore.openedNodeId === props.id : false }" + class="node" + @dragover="dragOver" + @dragleave="dragLeave" + @dragenter="dragEnter" + > + <ContentButton + v-for="element of props.data.elements" + :key="element.action.icon" + :icon="element.action.icon" + :is-active="editorStore.openedNodeId ? editorStore.openedNodeId === element.id : false" + :is-draggable="false" + :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true }" + @click="openForm(element)" + /> + </div> + <Handle type="source" :position="Position.Right" /> +</template> + +<style scoped lang="scss"> + +.vue-flow__handle { + width: 12px; + height: 12px; + &-left { + left: -6px; + } + &-right { + right: -6px; + } +} +.node-title { + margin: .2rem; + padding: .2rem; + &:focus-visible { + outline: 1px solid var(--editor-blue); + border-radius: 4px; + } +} +</style> \ No newline at end of file diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index c6e4005e..fdfd4d76 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -2,7 +2,6 @@ <script setup lang="ts"> import TextAreaInput from '../TextAreaInput.vue'; import CheckBoxInput from '../CheckBoxInput.vue'; -import { ref } from 'vue'; defineProps<{ inputValue: string; -- GitLab From 833fe608bafcfc5de8ccfbedb839b7f5688f4709 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:36:31 +0100 Subject: [PATCH 05/13] Remove title prop from screen node --- src/features/ePocFlow/nodes/ScreenNode.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/ePocFlow/nodes/ScreenNode.vue b/src/features/ePocFlow/nodes/ScreenNode.vue index bdfa2786..411f3311 100644 --- a/src/features/ePocFlow/nodes/ScreenNode.vue +++ b/src/features/ePocFlow/nodes/ScreenNode.vue @@ -15,7 +15,6 @@ const props = defineProps<{ required: true; readyToDrop: boolean; animated: boolean; - title: string; elements: NodeElement[]; } }>(); @@ -68,7 +67,7 @@ function openForm(element: NodeElement) { </script> <template> - <p contenteditable="true" class="node-title">{{ data.title }}</p> + <p contenteditable="true" class="node-title">Screen</p> <Handle type="target" :position="Position.Left" /> <div :id="'node'+props.id" -- GitLab From 670774f7935627e39f0a935156b4d93ad522cc41 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:02:56 +0100 Subject: [PATCH 06/13] Issue #12: Audio form & screen template --- package-lock.json | 1 + src/shared/data/form.data.ts | 61 +++++++++++++++++++++++++++++++- src/shared/stores/editorStore.ts | 13 +++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index a8abf29c..dac98426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "epoc-editor-v2", "version": "0.0.0", + "license": "CeCILL-B", "dependencies": { "@vue-flow/additional-components": "^1.3.3", "@vue-flow/core": "^1.6.0", diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index a6347d0a..84c207cf 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -299,4 +299,63 @@ const screenForm: Form = { ] }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm]; \ No newline at end of file +const audioForm: Form = { + type: 'audio', + name: 'Audio', + icon: 'icon-audio', + fields: [ + { + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'file', + label: 'Piste audio', + value: '', + placeholder: 'Ajouter une piste audio', + accept: 'audio/*' + }, + { + type: 'file', + label: 'Transcription', + value: '', + placeholder: 'Ajouter une transcription', + accept: 'text/*' + }, + { + type: 'file', + label: 'Vignette', + value: '', + placeholder: 'Ajouter une vignette', + accept: 'image/*' + }, + { + type: 'file', + label: 'Sous-titres', + value: '', + placeholder: 'Ajouter des sous-titres', + accept: '.vtt' + } + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; + + +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm]; \ No newline at end of file diff --git a/src/shared/stores/editorStore.ts b/src/shared/stores/editorStore.ts index 5c830a3c..9ef83449 100644 --- a/src/shared/stores/editorStore.ts +++ b/src/shared/stores/editorStore.ts @@ -131,6 +131,19 @@ const standardScreen: Screen[] = [ } ] }, + { + title: 'Ecran audio', + actions: [ + { + icon: 'icon-audio', + type: 'audio' + }, + { + icon: 'icon-texte', + type: 'text' + } + ] + } ]; -- GitLab From 9cca55e57c1a04647681a582f0d03e6f19753e53 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:05:10 +0100 Subject: [PATCH 07/13] merge solve --- src/shared/data/form.data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index 84c207cf..c73251a2 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -358,4 +358,4 @@ const audioForm: Form = { }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm]; \ No newline at end of file +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm]; -- GitLab From 439e931fee0ba1df5d6181e465f496bd1e89103f Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:56:15 +0100 Subject: [PATCH 08/13] Issue #13 #4: Drag & Drop Form --- .../forms/components/inputs/GenericInput.vue | 2 +- .../components/inputs/card/CardGroup.vue | 4 +- .../components/inputs/card/CardInput.vue | 4 +- .../components/inputs/card/SelectInput.vue | 46 +++++ .../{ => card/components}/CheckBoxInput.vue | 0 .../{ => card/components}/FileInput.vue | 0 src/shared/data/form.data.ts | 194 ++++++++++++------ 7 files changed, 189 insertions(+), 61 deletions(-) create mode 100644 src/features/forms/components/inputs/card/SelectInput.vue rename src/features/forms/components/inputs/{ => card/components}/CheckBoxInput.vue (100%) rename src/features/forms/components/inputs/{ => card/components}/FileInput.vue (100%) diff --git a/src/features/forms/components/inputs/GenericInput.vue b/src/features/forms/components/inputs/GenericInput.vue index 75b03360..3e519748 100644 --- a/src/features/forms/components/inputs/GenericInput.vue +++ b/src/features/forms/components/inputs/GenericInput.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import TextInput from './TextInput.vue'; import TextAreaInput from './TextAreaInput.vue'; -import FileInput from './FileInput.vue'; +import FileInput from './card/components/FileInput.vue'; defineProps<{ type: string; diff --git a/src/features/forms/components/inputs/card/CardGroup.vue b/src/features/forms/components/inputs/card/CardGroup.vue index 00c1ce50..6351db42 100644 --- a/src/features/forms/components/inputs/card/CardGroup.vue +++ b/src/features/forms/components/inputs/card/CardGroup.vue @@ -23,7 +23,9 @@ const emit = defineEmits<{ //? This way doesn't seem to be optimal const cardMap = new Map([ ['check', 'Réponse'], - ['objective', 'Objectif'] + ['objective', 'Objectif'], + ['category', 'Catégorie'], + ['dd', 'Réponse'], ]); </script> diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index fdfd4d76..bc471080 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -1,7 +1,8 @@ <script setup lang="ts"> import TextAreaInput from '../TextAreaInput.vue'; -import CheckBoxInput from '../CheckBoxInput.vue'; +import CheckBoxInput from './components/CheckBoxInput.vue'; +import SelectInput from './SelectInput.vue'; defineProps<{ inputValue: string; @@ -47,6 +48,7 @@ const emit = defineEmits<{ /> </div> <CheckBoxInput v-if="type === 'check'" /> + <SelectInput v-if="type === 'dd'" :label="'À quelle catégorie appartient cette réponse ' + pos" /> </div> </Transition> </template> diff --git a/src/features/forms/components/inputs/card/SelectInput.vue b/src/features/forms/components/inputs/card/SelectInput.vue new file mode 100644 index 00000000..7976e163 --- /dev/null +++ b/src/features/forms/components/inputs/card/SelectInput.vue @@ -0,0 +1,46 @@ +<script setup lang="ts"> + +defineProps<{ + label: string; +}>(); + +</script> + +<template> + <div class="select"> + <label for="select-box">{{ label }}</label> + <select id="select-box" class="select-box"> + <option value="">Sélectionnez</option> + <option value="1">Catégorie 1</option> + <option value="2">Catégorie 2</option> + <option value="3">Catégorie 3</option> + </select> + </div> +</template> + +<style scoped lang="scss"> +.select { + display: flex; + flex-direction: column; + margin-bottom: 1rem; + label { + margin-bottom: 0.5rem; + } + select { + appearance: none; + padding: .5rem; + border: 1px solid var(--border); + border-radius: 4px; + background-color: var(--item-background); + cursor: pointer; + font-size: 1rem; + color: var(--text); + + background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgd2lkdGg9IjExcHgiIGhlaWdodD0iN3B4IiB2aWV3Qm94PSIwIDAgMTEuMCA3LjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxjbGlwUGF0aCBpZD0iaTAiPjxwYXRoIGQ9Ik0yNDE4LDAgTDI0MTgsMjQyNiBMMCwyNDI2IEwwLDAgTDI0MTgsMCBaIj48L3BhdGg+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkxIj48cGF0aCBkPSJNOS4yMDE3MjIyNywwIEM5LjYxNjUwNDA2LDAgOS45OTA4OTcyMSwwLjI3MzU1MDk4NyAxMC4xNDk4ODU5LDAuNjk0MzM1OTM3IEMxMC4zMDg4NzQ2LDEuMTE1MTIwODkgMTAuMjQ5ODk0OSwxLjU5OTYwOTM3IDkuOTU0OTk2NjMsMS45MTk1MzE0NiBMNS44ODA5MDQ1NSw2LjQxOTUzMTQ2IEM1LjY1MzMxOTM0LDYuNjQxMDE1NDEgNS4zOTA0NzQ3Niw2Ljc1IDUuMTI3NjMwMTksNi43NSBDNC44NjQ3ODU2Miw2Ljc1IDQuNjAyNTgxNzgsNi42NDAxMzY3MiA0LjQwMjI0Mjg2LDYuNDIwNDEwMTYgTDAuMzI4MTUwMjkxLDEuOTIwNDEwMTYgQzAuMDA2MTQ2NTU1MTksMS41OTk2MDkzNyAtMC4wODE2OTQ5MjIzLDEuMTE0NDUzMDIgMC4wNzcxMDE1NTQ3LDAuNjk2MDkzODU3IEMwLjIzNTg5ODAzMiwwLjI3NzczNDY5NyAwLjYxMDIyNzEwNiwwIDEuMDI0Njg4NTMsMCBMOS4yMDE3MjIyNywwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTMwNC4wIC0xMDYyLjApIj48ZyBjbGlwLXBhdGg9InVybCgjaTApIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5ODAuMCA5MC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAuMCA3MjkuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCA1MS4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUuMCAxNTAuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCAyNC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjg5LjM3MjM2OTgwNjgwMSAxOC4wKSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2kxKSI+PHBvbHlnb24gcG9pbnRzPSItMS4xMTAyMjMwMmUtMTYsMCAxMC4yMzYwMTA0LDAgMTAuMjM2MDEwNCw2Ljc1IC0xLjExMDIyMzAyZS0xNiw2Ljc1IC0xLjExMDIyMzAyZS0xNiwwIiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzNTQyNTgiPjwvcG9seWdvbj48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4="); + background-repeat: no-repeat; + background-position: right 0.7rem top 50%; + background-size: .8rem auto; + } + margin: 0 1rem 1rem 1rem; +} +</style> \ No newline at end of file diff --git a/src/features/forms/components/inputs/CheckBoxInput.vue b/src/features/forms/components/inputs/card/components/CheckBoxInput.vue similarity index 100% rename from src/features/forms/components/inputs/CheckBoxInput.vue rename to src/features/forms/components/inputs/card/components/CheckBoxInput.vue diff --git a/src/features/forms/components/inputs/FileInput.vue b/src/features/forms/components/inputs/card/components/FileInput.vue similarity index 100% rename from src/features/forms/components/inputs/FileInput.vue rename to src/features/forms/components/inputs/card/components/FileInput.vue diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index c73251a2..6ea6e496 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -107,62 +107,6 @@ const videoForm: Form = { ] }; -const qcmForm: Form = { - type: 'qcm', - name: 'QCM', - icon: 'icon-qcm', - fields: [ - { - name: 'Configuration de l\'activité', - index: 1, - inputs: [ - { - type: 'text', - label: 'Titre', - value: '', - placeholder: 'Saisissez...' - }, - { - type: 'textarea', - label: 'Énoncé', - value: '', - placeholder: 'Saisissez' - } - ] - }, - { - name: 'Question', - index: 2, - inputs: [ - { - type: 'textarea', - label: '', - value: '', - placeholder: 'Saisissez l\'intitulé de la question...' - } - ] - }, - { - name: 'Réponses', - index: 3, - type: 'cardGroup', - inputType: 'check', - inputs: [] - }, - { - name: 'Explication', - index: 4, - inputs: [ - { - type: 'textarea', - label: '', - value: '', - placeholder: 'Saisissez une explication' - } - ] - } - ] -}; const chapterForm: Form = { type: 'chapter', @@ -244,7 +188,7 @@ const epocForm: Form = { type: 'text', label: 'ID de l\'ePoc', value: 'id234567890', - + }, { type: 'text', @@ -357,5 +301,139 @@ const audioForm: Form = { ] }; +// Question forms + +const qcmForm: Form = { + type: 'qcm', + name: 'QCM', + icon: 'icon-qcm', + fields: [ + { + name: 'Configuration de l\'activité', + index: 1, + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Énoncé', + value: '', + placeholder: 'Saisissez' + } + ] + }, + { + name: 'Question', + index: 2, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + } + ] + }, + { + name: 'Réponses', + index: 3, + type: 'cardGroup', + inputType: 'check', + inputs: [] + }, + { + name: 'Explication', + index: 4, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication' + } + ] + } + ] +}; + +const dragDropForm: Form = { + type: 'dragdrop', + name: 'Drag & Drop', + icon: 'icon-dragdrop', + fields: [ + { + name: 'Configuration de l\'activité', + index: 1, + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Énoncé', + value: '', + placeholder: 'Saisissez...' + } + ] + }, + { + name: 'Question', + index: 2, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + } + ] + }, + { + name: 'Catégories de réponses proposées', + index: 3, + type: 'cardGroup', + inputType: 'category', + inputs: [], + }, + { + name: 'Réponses proposées', + index: 4, + type: 'cardGroup', + inputType: 'dd', + inputs: [], + }, + { + name: 'Explication', + index: 5, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication' + } + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm]; +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm]; -- GitLab From d28b28556ffb04c0f882dde1c7870ee3f35a722a Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 12:07:17 +0100 Subject: [PATCH 09/13] =?UTF-8?q?Issue=C2=A0#5:=20Reorder=20Form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{card/components => }/FileInput.vue | 0 .../forms/components/inputs/GenericInput.vue | 2 +- .../components/inputs/card/CardGroup.vue | 1 + .../components/inputs/card/CardInput.vue | 3 +- .../card/{ => components}/SelectInput.vue | 6 +- src/shared/data/form.data.ts | 71 ++++++++++++++++++- 6 files changed, 77 insertions(+), 6 deletions(-) rename src/features/forms/components/inputs/{card/components => }/FileInput.vue (100%) rename src/features/forms/components/inputs/card/{ => components}/SelectInput.vue (94%) diff --git a/src/features/forms/components/inputs/card/components/FileInput.vue b/src/features/forms/components/inputs/FileInput.vue similarity index 100% rename from src/features/forms/components/inputs/card/components/FileInput.vue rename to src/features/forms/components/inputs/FileInput.vue diff --git a/src/features/forms/components/inputs/GenericInput.vue b/src/features/forms/components/inputs/GenericInput.vue index 3e519748..75b03360 100644 --- a/src/features/forms/components/inputs/GenericInput.vue +++ b/src/features/forms/components/inputs/GenericInput.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import TextInput from './TextInput.vue'; import TextAreaInput from './TextAreaInput.vue'; -import FileInput from './card/components/FileInput.vue'; +import FileInput from './FileInput.vue'; defineProps<{ type: string; diff --git a/src/features/forms/components/inputs/card/CardGroup.vue b/src/features/forms/components/inputs/card/CardGroup.vue index 6351db42..f3c4b36e 100644 --- a/src/features/forms/components/inputs/card/CardGroup.vue +++ b/src/features/forms/components/inputs/card/CardGroup.vue @@ -26,6 +26,7 @@ const cardMap = new Map([ ['objective', 'Objectif'], ['category', 'Catégorie'], ['dd', 'Réponse'], + ['reorder', 'Réponse position'] ]); </script> diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index bc471080..47705eec 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -2,7 +2,7 @@ <script setup lang="ts"> import TextAreaInput from '../TextAreaInput.vue'; import CheckBoxInput from './components/CheckBoxInput.vue'; -import SelectInput from './SelectInput.vue'; +import SelectInput from './components/SelectInput.vue'; defineProps<{ inputValue: string; @@ -49,6 +49,7 @@ const emit = defineEmits<{ </div> <CheckBoxInput v-if="type === 'check'" /> <SelectInput v-if="type === 'dd'" :label="'À quelle catégorie appartient cette réponse ' + pos" /> + <SelectInput v-if="type === 'reorder'" label="Position affiché à l'écran avant réorganisation" /> </div> </Transition> </template> diff --git a/src/features/forms/components/inputs/card/SelectInput.vue b/src/features/forms/components/inputs/card/components/SelectInput.vue similarity index 94% rename from src/features/forms/components/inputs/card/SelectInput.vue rename to src/features/forms/components/inputs/card/components/SelectInput.vue index 7976e163..ae3bd60a 100644 --- a/src/features/forms/components/inputs/card/SelectInput.vue +++ b/src/features/forms/components/inputs/card/components/SelectInput.vue @@ -11,9 +11,9 @@ defineProps<{ <label for="select-box">{{ label }}</label> <select id="select-box" class="select-box"> <option value="">Sélectionnez</option> - <option value="1">Catégorie 1</option> - <option value="2">Catégorie 2</option> - <option value="3">Catégorie 3</option> + <option value="1">Position 1</option> + <option value="2">Position 2</option> + <option value="3">Position 3</option> </select> </div> </template> diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index 6ea6e496..c93d578b 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -436,4 +436,73 @@ const dragDropForm: Form = { ] }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm]; +const reorderForm: Form = { + type: 'reorder', + name: 'Reorder', + icon: 'icon-reorder', + fields: [ + { + name: 'Configuration de l\'activité', + index: 1, + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Énoncé', + value: '', + placeholder: 'Saisissez...' + } + ] + }, + { + name: 'Question', + index: 2, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + } + ] + }, + { + name: 'Réponses', + index: 3, + type: 'cardGroup', + inputType: 'reorder', + inputs: [], + }, + { + name: 'Explication', + index: 4, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication...' + } + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; + +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm, reorderForm]; -- GitLab From aae7aea769aad7f08f84ac254d55ec36e36c0a1d Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 13:59:47 +0100 Subject: [PATCH 10/13] Issue #11: Score input --- .../forms/components/inputs/GenericInput.vue | 7 ++ .../forms/components/inputs/ScoreInput.vue | 82 +++++++++++++++++++ src/shared/data/form.data.ts | 15 ++++ 3 files changed, 104 insertions(+) create mode 100644 src/features/forms/components/inputs/ScoreInput.vue diff --git a/src/features/forms/components/inputs/GenericInput.vue b/src/features/forms/components/inputs/GenericInput.vue index 75b03360..7cfcc16e 100644 --- a/src/features/forms/components/inputs/GenericInput.vue +++ b/src/features/forms/components/inputs/GenericInput.vue @@ -2,6 +2,7 @@ import TextInput from './TextInput.vue'; import TextAreaInput from './TextAreaInput.vue'; import FileInput from './FileInput.vue'; +import ScoreInput from './ScoreInput.vue'; defineProps<{ type: string; @@ -49,4 +50,10 @@ const emit = defineEmits<{ :placeholder="placeholder" @input="emit('update:modelValue', $event)" /> + <ScoreInput + v-if="type === 'score'" + :label="label" + :input-value="inputValue" + @input="emit('input', $event)" + /> </template> \ No newline at end of file diff --git a/src/features/forms/components/inputs/ScoreInput.vue b/src/features/forms/components/inputs/ScoreInput.vue new file mode 100644 index 00000000..061a0a5f --- /dev/null +++ b/src/features/forms/components/inputs/ScoreInput.vue @@ -0,0 +1,82 @@ +<script setup lang="ts"> + +defineProps<{ + inputValue: string; +}>(); + +const emit = defineEmits<{ + (e: 'input', value: string): void; +}>(); + +function minus(inputValue: string) { + const value = parseInt(inputValue) - 1; + emit('input', value.toString()); +} + +function plus(inputValue: string) { + const value = parseInt(inputValue) + 1; + emit('input', value.toString()); +} + +</script> + +<template> + <label for="input-score">Score</label> + <div id="input-score" class="input-score"> + <button @click="minus(inputValue)"><i class="icon-minus-circle"></i></button> + <input + type="number" + :value="inputValue" + @input="emit('input', ($event.target as HTMLInputElement).value)" + > + <button @click="plus(inputValue)"><i class="icon-plus-circle"></i></button> + </div> +</template> + +<style scoped lang="scss"> +.input-score { + background-color: var(--item-background); + border: 1px solid var(--border); + border-radius: 4px; + width: fit-content; + margin-bottom: 1.5rem; + + input[type="number"] { + border: none; + margin: .5rem; + background-color: transparent; + font-size: 1.1rem; + outline: none; + color: var(--editor-grayblue); + width: 4rem; + text-align: center; + &::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + } + + button { + border: none; + background-color: transparent; + font-size: 1.4rem; + outline: none; + color: var(--editor-grayblue); + cursor: pointer; + height: 100%; + } + + i { + display: block; + } +} + +label { + margin-bottom: .5rem; + font-size: 1rem; +} +</style> \ No newline at end of file diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index c93d578b..43cb6d26 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -323,6 +323,11 @@ const qcmForm: Form = { label: 'Énoncé', value: '', placeholder: 'Saisissez' + }, + { + type: 'score', + label: 'Score', + value: '0', } ] }, @@ -380,6 +385,11 @@ const dragDropForm: Form = { label: 'Énoncé', value: '', placeholder: 'Saisissez...' + }, + { + type: 'score', + label: 'Score', + value: '0', } ] }, @@ -456,6 +466,11 @@ const reorderForm: Form = { label: 'Énoncé', value: '', placeholder: 'Saisissez...' + }, + { + type: 'score', + label: 'Score', + value: '0', } ] }, -- GitLab From 6b152c48ab4d42ea48e47c86188477874e115a89 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 14:37:10 +0100 Subject: [PATCH 11/13] Issue #6: Swipe Form --- .../components/inputs/card/CardGroup.vue | 3 +- .../components/inputs/card/CardInput.vue | 2 + .../inputs/card/components/RadioInput.vue | 82 ++++++++++++++++ src/shared/data/form.data.ts | 94 ++++++++++++++++++- 4 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 src/features/forms/components/inputs/card/components/RadioInput.vue diff --git a/src/features/forms/components/inputs/card/CardGroup.vue b/src/features/forms/components/inputs/card/CardGroup.vue index f3c4b36e..61cccff7 100644 --- a/src/features/forms/components/inputs/card/CardGroup.vue +++ b/src/features/forms/components/inputs/card/CardGroup.vue @@ -26,7 +26,8 @@ const cardMap = new Map([ ['objective', 'Objectif'], ['category', 'Catégorie'], ['dd', 'Réponse'], - ['reorder', 'Réponse position'] + ['reorder', 'Réponse position'], + ['swipe', 'Carte'], ]); </script> diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index 47705eec..7710e4a3 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -3,6 +3,7 @@ import TextAreaInput from '../TextAreaInput.vue'; import CheckBoxInput from './components/CheckBoxInput.vue'; import SelectInput from './components/SelectInput.vue'; +import RadioInput from './components/RadioInput.vue'; defineProps<{ inputValue: string; @@ -50,6 +51,7 @@ const emit = defineEmits<{ <CheckBoxInput v-if="type === 'check'" /> <SelectInput v-if="type === 'dd'" :label="'À quelle catégorie appartient cette réponse ' + pos" /> <SelectInput v-if="type === 'reorder'" label="Position affiché à l'écran avant réorganisation" /> + <RadioInput :index="pos" v-if="type === 'swipe'" /> </div> </Transition> </template> diff --git a/src/features/forms/components/inputs/card/components/RadioInput.vue b/src/features/forms/components/inputs/card/components/RadioInput.vue new file mode 100644 index 00000000..8db062d1 --- /dev/null +++ b/src/features/forms/components/inputs/card/components/RadioInput.vue @@ -0,0 +1,82 @@ +<script setup lang="ts"> + +defineProps<{ + index: number; +}>(); + +</script> + +<template> + <div class="radio"> + <label class="group-label" for="radio-group">Réponse</label> + <div id="radio-group" class="radio-group"> + <div class="radio-btn"> + <input + id="left" + :name="'pos' + index" + type="radio" + class="radio-input" + > + <label for="left">Choix gauche</label> + </div> + <div class="radio-btn"> + <input + id="right" + :name="'pos' + index" + type="radio" + class="radio-input" + > + <label for="right">Choix droite</label> + </div> + </div> + </div> +</template> + +<style scoped lang="scss"> +.radio { + margin: 0 1rem 1rem 1rem; + + .radio-group { + margin-top: .5rem; + display: flex; + justify-content: space-between; + + .radio-btn { + display: flex; + &:last-child { + margin-right: 0.5rem; + } + + label { + margin-left: .5rem; + } + input[type="radio"] { + appearance: none; + width: 20px; + height: 20px; + border: 2px solid var(--border); + border-radius: 50%; + margin: 0; + cursor: pointer; + transform: translateY(0.075rem); + display: grid; + place-content: center; + + &::before { + content: ''; + width: 12px; + height: 12px; + transform: scale(0); + transition: .1s transform ease-in-out; + border-radius: 50%; + background-color: var(--editor-blue); + } + + &:checked::before { + transform: scale(1); + } + } + } + } +} +</style> \ No newline at end of file diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index 43cb6d26..62a51719 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -520,4 +520,96 @@ const reorderForm: Form = { ] }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm, reorderForm]; +const swipeForm: Form = { + type: 'swipe', + name: 'Swipe', + icon: 'icon-swipe', + fields: [ + { + name: 'Configuration de l\'activité', + index: 1, + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Énoncé', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'score', + label: 'Score', + value: '0', + } + ] + }, + { + name: 'Question', + index: 2, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + } + ] + }, + { + name: 'Catégories de choix proposées', + index: 3, + inputs: [ + { + type: 'text', + label: 'Choix gauche', + value: '', + placeholder: 'Saisissez une réponse...' + }, + { + type: 'text', + label: 'Choix droite', + value: '', + placeholder: 'Saisissez une réponse...' + } + ] + }, + { + name: 'Cartes', + index: 4, + type: 'cardGroup', + inputType: 'swipe', + inputs: [], + }, + { + name: 'Explication', + index: 5, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication...' + } + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; + +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm, reorderForm, swipeForm]; -- GitLab From 1072af09373b2b59a42b5b3787c4434f2cce55c9 Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 14:48:55 +0100 Subject: [PATCH 12/13] Issue #7: Dropdown list --- .../components/inputs/card/CardGroup.vue | 2 + .../components/inputs/card/CardInput.vue | 3 +- src/shared/data/form.data.ts | 84 ++++++++++++++++++- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/features/forms/components/inputs/card/CardGroup.vue b/src/features/forms/components/inputs/card/CardGroup.vue index 61cccff7..1ca1dd82 100644 --- a/src/features/forms/components/inputs/card/CardGroup.vue +++ b/src/features/forms/components/inputs/card/CardGroup.vue @@ -28,6 +28,8 @@ const cardMap = new Map([ ['dd', 'Réponse'], ['reorder', 'Réponse position'], ['swipe', 'Carte'], + ['list-choice', 'Choix'], + ['list', 'Carte'] ]); </script> diff --git a/src/features/forms/components/inputs/card/CardInput.vue b/src/features/forms/components/inputs/card/CardInput.vue index 7710e4a3..11930517 100644 --- a/src/features/forms/components/inputs/card/CardInput.vue +++ b/src/features/forms/components/inputs/card/CardInput.vue @@ -51,7 +51,8 @@ const emit = defineEmits<{ <CheckBoxInput v-if="type === 'check'" /> <SelectInput v-if="type === 'dd'" :label="'À quelle catégorie appartient cette réponse ' + pos" /> <SelectInput v-if="type === 'reorder'" label="Position affiché à l'écran avant réorganisation" /> - <RadioInput :index="pos" v-if="type === 'swipe'" /> + <SelectInput v-if="type === 'list'" label="Réponse" /> + <RadioInput v-if="type === 'swipe'" :index="pos" /> </div> </Transition> </template> diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index 62a51719..9cda77d6 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -612,4 +612,86 @@ const swipeForm: Form = { ] }; -export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm, reorderForm, swipeForm]; +const listForm: Form = { + type: 'list', + name: 'Listes déroulantes', + icon: 'icon-liste', + fields: [ + { + name: 'Configuration de l\'activité', + index: 1, + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Énoncé', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'score', + label: 'Score', + value: '0', + } + ] + }, + { + name: 'Question', + index: 2, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + } + ] + }, + { + name: 'Catégories de choix proposées', + index: 3, + type: 'cardGroup', + inputType: 'list-choice', + inputs: [] + }, + { + name: 'Cartes', + index: 4, + type: 'cardGroup', + inputType: 'list', + inputs: [], + }, + { + name: 'Explication', + index: 5, + inputs: [ + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication...' + } + ] + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] +}; + + +export const formsModel: Form[] = [textForm, videoForm, qcmForm, chapterForm, epocForm, screenForm, audioForm, dragDropForm, reorderForm, swipeForm, listForm]; -- GitLab From 64e046d312b0f2b899bb722e85eeed06efcac6cc Mon Sep 17 00:00:00 2001 From: NathanViaud <79544144+NathanViaud@users.noreply.github.com> Date: Wed, 18 Jan 2023 17:25:35 +0100 Subject: [PATCH 13/13] Issue #1: Quill Editor --- package-lock.json | 274 +++++++++++++++--- package.json | 1 + .../forms/components/inputs/GenericInput.vue | 8 + .../forms/components/inputs/QuillEditor.vue | 97 +++++++ src/global.scss | 5 +- src/shared/data/form.data.ts | 20 +- 6 files changed, 349 insertions(+), 56 deletions(-) create mode 100644 src/features/forms/components/inputs/QuillEditor.vue diff --git a/package-lock.json b/package-lock.json index dac98426..0cbd01de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@vue-flow/additional-components": "^1.3.3", "@vue-flow/core": "^1.6.0", + "@vueup/vue-quill": "^1.0.1", "pinia": "^2.0.28", "sass": "^1.56.2", "vue": "^3.2.45", @@ -1789,6 +1790,33 @@ "vue": "^3.0.1" } }, + "node_modules/@vueup/vue-quill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.0.1.tgz", + "integrity": "sha512-/VJHwUxH2BAms7gwIlNhHb0KfCqn/58onOK1H0ZZX9juEha4nrb32yZbv2xLsqLxf+JKsNbJgQ8Ne6VF8ACNGw==", + "dependencies": { + "quill": "^1.3.7", + "quill-delta": "^4.2.2" + }, + "peerDependencies": { + "vue": "^3.2.41" + } + }, + "node_modules/@vueup/vue-quill/node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "node_modules/@vueup/vue-quill/node_modules/quill-delta": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.2.tgz", + "integrity": "sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==", + "dependencies": { + "fast-diff": "1.2.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + } + }, "node_modules/@vueuse/core": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz", @@ -3117,7 +3145,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -3899,7 +3926,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -4866,6 +4892,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "node_modules/execa": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", @@ -4934,6 +4965,11 @@ "webdriverio": "^8.0.0-alpha.505" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -5008,6 +5044,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -5276,14 +5317,12 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5322,7 +5361,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -5626,7 +5664,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -5677,7 +5714,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.1" }, @@ -5689,7 +5725,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5701,7 +5736,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -6041,7 +6075,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6138,7 +6171,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6261,7 +6293,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6831,8 +6862,7 @@ "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "node_modules/lodash.defaults": { "version": "4.2.0", @@ -6858,6 +6888,11 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", @@ -7610,7 +7645,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -7626,7 +7660,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -7818,6 +7851,11 @@ "node": ">=6" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8298,6 +8336,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/quill-delta/node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quill/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/quill/node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8460,7 +8564,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -12624,6 +12727,32 @@ "dev": true, "requires": {} }, + "@vueup/vue-quill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.0.1.tgz", + "integrity": "sha512-/VJHwUxH2BAms7gwIlNhHb0KfCqn/58onOK1H0ZZX9juEha4nrb32yZbv2xLsqLxf+JKsNbJgQ8Ne6VF8ACNGw==", + "requires": { + "quill": "^1.3.7", + "quill-delta": "^4.2.2" + }, + "dependencies": { + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "quill-delta": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.2.tgz", + "integrity": "sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==", + "requires": { + "fast-diff": "1.2.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + } + } + } + }, "@vueuse/core": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz", @@ -13650,7 +13779,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -14215,7 +14343,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -14963,6 +15090,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "execa": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", @@ -15013,6 +15145,11 @@ "webdriverio": "^8.0.0-alpha.505" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -15069,6 +15206,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -15272,14 +15414,12 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "gaze": { "version": "1.1.3", @@ -15306,7 +15446,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -15543,7 +15682,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -15581,7 +15719,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -15589,14 +15726,12 @@ "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -15830,7 +15965,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -15897,7 +16031,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -15969,7 +16102,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -16423,8 +16555,7 @@ "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "lodash.defaults": { "version": "4.2.0", @@ -16450,6 +16581,11 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", @@ -16999,7 +17135,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -17008,8 +17143,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.4", @@ -17137,6 +17271,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -17461,6 +17600,64 @@ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true }, + "quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "requires": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + } + } + }, + "quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "dependencies": { + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + } + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -17590,7 +17787,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", diff --git a/package.json b/package.json index 76d89b60..3a4cbac5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "dependencies": { "@vue-flow/additional-components": "^1.3.3", "@vue-flow/core": "^1.6.0", + "@vueup/vue-quill": "^1.0.1", "pinia": "^2.0.28", "sass": "^1.56.2", "vue": "^3.2.45", diff --git a/src/features/forms/components/inputs/GenericInput.vue b/src/features/forms/components/inputs/GenericInput.vue index 7cfcc16e..7b90cfcd 100644 --- a/src/features/forms/components/inputs/GenericInput.vue +++ b/src/features/forms/components/inputs/GenericInput.vue @@ -3,6 +3,7 @@ import TextInput from './TextInput.vue'; import TextAreaInput from './TextAreaInput.vue'; import FileInput from './FileInput.vue'; import ScoreInput from './ScoreInput.vue'; +import QuillEditor from './QuillEditor.vue'; defineProps<{ type: string; @@ -36,6 +37,13 @@ const emit = defineEmits<{ :input-value="inputValue" @input="emit('input', $event)" /> + <QuillEditor + v-if="type === 'ql-editor'" + :label="label" + :placeholder="placeholder" + :input-value="inputValue" + @input="emit('input', $event)" + /> <TextAreaInput v-if="type === 'textarea'" :label="label" diff --git a/src/features/forms/components/inputs/QuillEditor.vue b/src/features/forms/components/inputs/QuillEditor.vue new file mode 100644 index 00000000..8bf7bd5b --- /dev/null +++ b/src/features/forms/components/inputs/QuillEditor.vue @@ -0,0 +1,97 @@ +<script setup lang="ts"> +import { QuillEditor } from '@vueup/vue-quill'; +import '@vueup/vue-quill/dist/vue-quill.snow.css'; +import { onMounted } from 'vue'; + +const props = defineProps<{ + label: string; + placeholder?: string; + inputValue: string; +}>(); + +const emit = defineEmits<{ + (e: 'input', value: string): void; +}>(); + +const toolbar = [['bold', 'italic', 'underline'], [{ 'header': 1}, { 'header': 2 }], [{ 'list': 'ordered' }, { 'list': 'bullet' }]]; + +function textChange() { + emit('input', document.querySelector('.ql-editor').innerHTML); +} + +onMounted(() => { + document.querySelector('.ql-editor').innerHTML = props.inputValue; +}); + +</script> + +<template> + <QuillEditor + :toolbar="toolbar" + theme="snow" + :placeholder="placeholder" + @text-change="textChange" + /> +</template> + +<style lang="scss"> + +.ql { + &-toolbar { + border: none !important; + &:hover { + border-radius: 8px; + } + } + &-container { + border: 1px solid var(--border) !important; + border-radius: 4px; + background-color: var(--item-background); + margin-bottom: 1.5rem; + &:focus-within { + border: 1px solid var(--editor-blue) !important; + box-shadow: 0 1px 8px 0 var(--editor-blue-shadow); + } + } + &-editor { + min-height: 10rem; + padding: .5rem; + font-size: 1rem; + color: var(--text); + width: 24rem; + } + + &-active { + background-color: transparent !important; + &:hover { + background-color: #F3F4F6 !important; + } + } + + &-formats { + button:hover { + border-radius: 4px; + background-color: #F3F4F6 !important; + } + } +} + +.ql-active .ql-stroke { + stroke: var(--editor-blue) !important; +} + +.ql-active .ql-fill { + fill: var(--editor-blue) !important; +} + +.ql-active:hover { + background-color: var(--) !important; +} + +.ql-blank::before { + color: var(--editor-grayblue) !important; + font-style: normal !important; + left: 0.5rem !important; +} + +</style> \ No newline at end of file diff --git a/src/global.scss b/src/global.scss index 1405c551..38e3450f 100644 --- a/src/global.scss +++ b/src/global.scss @@ -270,10 +270,7 @@ hr { &-textarea { resize: none; - min-height: 10rem; - &.input-card { - min-height: 5rem; - } + min-height: 5rem; } &:focus { outline: 1px solid var(--editor-blue); diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts index 9cda77d6..d06b3e94 100644 --- a/src/shared/data/form.data.ts +++ b/src/shared/data/form.data.ts @@ -14,7 +14,7 @@ const textForm: Form = { placeholder: 'Saisissez...' }, { - type: 'textarea', + type: 'ql-editor', label: 'Résumé', value: '', placeholder: 'Saisissez un résumé...' @@ -63,12 +63,6 @@ const videoForm: Form = { value: '', accept: 'video/*' }, - { - type: 'textarea', - label: 'Résumé', - value: '', - placeholder: 'Saisissez un résumé...' - }, { type: 'file', label: 'Transcription', @@ -179,7 +173,7 @@ const epocForm: Form = { accept: 'video/*' }, { - type: 'textarea', + type: 'ql-editor', label: 'Présentation', value: '', placeholder: 'Saisissez une présentation de l\'ePoc...' @@ -355,7 +349,7 @@ const qcmForm: Form = { index: 4, inputs: [ { - type: 'textarea', + type: 'ql-editor', label: '', value: '', placeholder: 'Saisissez une explication' @@ -424,7 +418,7 @@ const dragDropForm: Form = { index: 5, inputs: [ { - type: 'textarea', + type: 'ql-editor', label: '', value: '', placeholder: 'Saisissez une explication' @@ -498,7 +492,7 @@ const reorderForm: Form = { index: 4, inputs: [ { - type: 'textarea', + type: 'ql-editor', label: '', value: '', placeholder: 'Saisissez une explication...' @@ -590,7 +584,7 @@ const swipeForm: Form = { index: 5, inputs: [ { - type: 'textarea', + type: 'ql-editor', label: '', value: '', placeholder: 'Saisissez une explication...' @@ -671,7 +665,7 @@ const listForm: Form = { index: 5, inputs: [ { - type: 'textarea', + type: 'ql-editor', label: '', value: '', placeholder: 'Saisissez une explication...' -- GitLab