ep-lib-ts 1.0.36 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BgAudio-BDO4Ln5y.js +4 -0
- package/dist/DisplayBox-Diko7_uH.js +4 -0
- package/dist/Ep360Image-Dmwx7NrY.js +228 -0
- package/dist/Ep360Video-b_stvC2Z.js +235 -0
- package/dist/EpAlert-iWXSHupC.js +4 -0
- package/dist/EpAudio-Bnb1MIw6.js +4 -0
- package/dist/EpAvatar-DTfkDxUP.js +4 -0
- package/dist/{EpAvatar-Dmpg0PFj.js → EpAvatar.vue_vue_type_script_setup_true_lang-IeQ9W4tD.js} +11 -11
- package/dist/EpBadge-BcVkBKJK.js +4 -0
- package/dist/{EpBadge-DqmSNdbi.js → EpBadge-CWIq_C51.js} +2 -2
- package/dist/EpBarChart-D42B3a97.js +4 -0
- package/dist/EpBranchingScenario-dEapAg2r.js +151 -0
- package/dist/EpBtn-BtyunWne.js +4 -0
- package/dist/EpCard--mtof8hL.js +4 -0
- package/dist/{EpCheckbox-_Nt9Bvq3.js → EpCheckbox-D-AiG1k1.js} +10 -10
- package/dist/EpChip-DitreF_J.js +4 -0
- package/dist/EpCodeblock-nBOawE0H.js +4 -0
- package/dist/EpConclusion-CcOvoC8e.js +4 -0
- package/dist/EpContentSlider-DpIiJmPt.js +4 -0
- package/dist/{EpDarkmode-fMMNJbJs.js → EpDarkmode-DV-fRn1K.js} +5 -5
- package/dist/EpDescription-DeVW7LNL.js +4 -0
- package/dist/EpDivider-vEs9W9Km.js +4 -0
- package/dist/EpEdu-BC4BxjYR.js +4 -0
- package/dist/EpFlex-xdVOYg4a.js +4 -0
- package/dist/EpFunnelChart-CF1CM2id.js +4 -0
- package/dist/EpHeader-BMoXpQdW.js +4 -0
- package/dist/{EpHotsPot-DFz_Du9o.js → EpHotsPot-nT87sUMT.js} +6 -6
- package/dist/EpHover-CwOc7bvn.js +4 -0
- package/dist/EpHover.vue_vue_type_script_setup_true_lang-DDLIDTha.js +36 -0
- package/dist/EpHoverCard-BTWsiA2z.js +84 -0
- package/dist/EpIcon-Cektqqle.js +4 -0
- package/dist/EpIframe-CFLSXU7z.js +4 -0
- package/dist/EpImg-Ct-KfkRp.js +4 -0
- package/dist/EpInput-c9THhDCs.js +1095 -0
- package/dist/EpInstructions-BCFWkov8.js +4 -0
- package/dist/EpIntroduction-Cx1Tc920.js +4 -0
- package/dist/EpLineChart-BffNNoNj.js +4 -0
- package/dist/EpLink-Bx_E9Gjz.js +4 -0
- package/dist/EpLinkVersion-6CTPFhT5.js +4 -0
- package/dist/{EpList-B6Q3fXGj.js → EpList-CM3b-mLc.js} +3 -3
- package/dist/{EpListitem-DzQrc-k2.js → EpListitem-DXh4Kniu.js} +4 -4
- package/dist/EpLottieSvg-NNSQLDpl.js +1971 -0
- package/dist/EpModal-DLG_mddB.js +4 -0
- package/dist/EpNothing-DVY_GKL6.js +10 -0
- package/dist/EpObjective-DIWMrap_.js +4 -0
- package/dist/EpPieChart-CTDL07vZ.js +4 -0
- package/dist/EpQuestion-RzrZk_Op.js +4 -0
- package/dist/EpQuote-SaMcAmmL.js +4 -0
- package/dist/EpRadio-w5IkZmj5.js +4 -0
- package/dist/EpRadioSummative-BeZecHlR.js +4 -0
- package/dist/EpReading-CVgr9TGN.js +4 -0
- package/dist/EpResource-BEql6U6K.js +4 -0
- package/dist/EpScope-BAuTDV_B.js +4 -0
- package/dist/EpSection-CESMVDna.js +4 -0
- package/dist/EpSectionCols-CacYX-v3.js +4 -0
- package/dist/{EpSelect-FIb6SyJU.js → EpSelect-kjH_wTXb.js} +3 -3
- package/dist/EpSkeleton-CuvqT7PL.js +4 -0
- package/dist/EpSoftware-LK_nIUJY.js +4 -0
- package/dist/EpSpecificObjective-BTavyAY9.js +4 -0
- package/dist/EpSpinner-CZo0f9Nx.js +4 -0
- package/dist/EpStackedList-DWkLmrqI.js +75 -0
- package/dist/EpSummativeTable-Dlhhgq1A.js +4 -0
- package/dist/EpSvg-CG-uua_J.js +4 -0
- package/dist/{EpSwitch-hdXyzuR1.js → EpSwitch-DtQon_hm.js} +2 -2
- package/dist/EpTable-DsXZI8Hm.js +4 -0
- package/dist/EpTerm-DGTKAJqq.js +4 -0
- package/dist/EpText-kB0vb1Da.js +4 -0
- package/dist/{EpTextarea-D8UCQuga.js → EpTextarea-VJ4pdPV_.js} +2 -2
- package/dist/EpTimeLine-DK_bzCen.js +4 -0
- package/dist/{EpToggle-BDp54LpY.js → EpToggle-BTkTNVEz.js} +2 -2
- package/dist/{EpTooltip-B4s0_PvZ.js → EpTooltip-J6UMMP3d.js} +2 -2
- package/dist/EpVideo-CPN6M6cs.js +4 -0
- package/dist/EpVideoPanopto-UZckfqS1.js +4 -0
- package/dist/EpWordDef-dSnuxRtl.js +4 -0
- package/dist/components/basics/EpHover.vue.d.ts +12 -10
- package/dist/components/basics/EpHoverCard.vue.d.ts +86 -0
- package/dist/components/basics/EpStackedList.vue.d.ts +50 -0
- package/dist/components/educationals/EpBranchingScenario.vue.d.ts +45 -0
- package/dist/components/educationals/EpCodeblock.vue.d.ts +1 -1
- package/dist/components/interactions/Ep360Image.vue.d.ts +16 -0
- package/dist/components/interactions/Ep360Video.vue.d.ts +16 -0
- package/dist/components/medias/EpLottieSvg.vue.d.ts +58 -0
- package/dist/ep-lib-ts.js +23 -23
- package/dist/ep-lib-ts.umd.cjs +5158 -109
- package/dist/{index-D4ekzRdY.js → index-Bw4yzGuc.js} +3999 -3114
- package/dist/{index-7_RFK7FL.js → index-CPs4nmaJ.js} +4684 -4549
- package/dist/index-IdtPmXeP.js +3483 -0
- package/dist/{prism-CZvJL8HS.js → prism-DM8vlN3d.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/testImg.jpg +0 -0
- package/dist/testMaria.jpg +0 -0
- package/dist/three.module-laS36oD-.js +48903 -0
- package/dist/types/Aframe.d.ts +1 -0
- package/dist/types/Component.d.ts +10 -0
- package/dist/types/Hover.d.ts +3 -0
- package/dist/types/StackedList.d.ts +37 -0
- package/dist/types/Three.d.ts +1 -0
- package/dist/types/image360.d.ts +16 -0
- package/dist/types/video360.d.ts +16 -0
- package/package.json +10 -5
- package/src/components/basics/EpHover.vue +32 -28
- package/src/components/basics/EpHoverCard.vue +123 -0
- package/src/components/basics/EpImg.vue +1 -1
- package/src/components/basics/EpListitem.vue +41 -59
- package/src/components/basics/EpStackedList.vue +83 -0
- package/src/components/educationals/EpBranchingScenario.vue +239 -0
- package/src/components/educationals/EpCodeblock.vue +1 -1
- package/src/components/educationals/EpEdu.vue +4 -4
- package/src/components/interactions/Ep360Image.vue +344 -0
- package/src/components/interactions/Ep360Video.vue +339 -0
- package/src/components/medias/EpLottieSvg.vue +79 -0
- package/dist/BgAudio-BpeNw9L4.js +0 -4
- package/dist/DisplayBox-aSVYrDHI.js +0 -4
- package/dist/EpAlert-x0STjaqD.js +0 -4
- package/dist/EpAudio-D7LIG4mf.js +0 -4
- package/dist/EpBadge-Du6v1vQL.js +0 -4
- package/dist/EpBarChart-BoINXvhV.js +0 -4
- package/dist/EpBtn-DE6qTHlW.js +0 -4
- package/dist/EpCard-CNcn4RbZ.js +0 -4
- package/dist/EpChip-DHgdqDEX.js +0 -4
- package/dist/EpCodeblock-CR2LGKR-.js +0 -4
- package/dist/EpConclusion-B9DhTuTc.js +0 -4
- package/dist/EpContentSlider-BdS_KctY.js +0 -4
- package/dist/EpDescription-D1TbwNrX.js +0 -4
- package/dist/EpDivider-DjTTwzGb.js +0 -4
- package/dist/EpEdu-CuI1_N9M.js +0 -4
- package/dist/EpFlex-Dx5C4Gc8.js +0 -4
- package/dist/EpFunnelChart-C6_M1nag.js +0 -4
- package/dist/EpHeader-B5jyGMDA.js +0 -4
- package/dist/EpHover-3fnZrdD6.js +0 -31
- package/dist/EpIcon-DDm9gGfm.js +0 -4
- package/dist/EpIframe-BNz3cawg.js +0 -4
- package/dist/EpImg-CxjLfziE.js +0 -4
- package/dist/EpInput-HmLHpLYh.js +0 -1168
- package/dist/EpInstructions-C0reDzIV.js +0 -4
- package/dist/EpIntroduction-C92mD-hu.js +0 -4
- package/dist/EpLineChart-Cm4W9z14.js +0 -4
- package/dist/EpLink-CGVS8yjO.js +0 -4
- package/dist/EpLinkVersion-CsGN9YS9.js +0 -4
- package/dist/EpModal-DuIZeI6V.js +0 -4
- package/dist/EpNothing-BDqIuSB9.js +0 -10
- package/dist/EpObjective-R1tWBwTe.js +0 -4
- package/dist/EpPieChart-CoTBcaaf.js +0 -4
- package/dist/EpQuestion-DmEcATq1.js +0 -4
- package/dist/EpQuote-BPCJBVbM.js +0 -4
- package/dist/EpRadio-TsUBsVoj.js +0 -4
- package/dist/EpRadioSummative-Cl9dVMJD.js +0 -4
- package/dist/EpReading-vOjzuYW7.js +0 -4
- package/dist/EpResource-Dbq_41hb.js +0 -4
- package/dist/EpScope-DIc4HvYy.js +0 -4
- package/dist/EpSection-Cb18CHq0.js +0 -4
- package/dist/EpSectionCols-B1P8ujg-.js +0 -4
- package/dist/EpSkeleton-vw7gDYoi.js +0 -4
- package/dist/EpSoftware-BAKbJhZg.js +0 -4
- package/dist/EpSpecificObjective-B5L6-OCN.js +0 -4
- package/dist/EpSpinner-rQzI6_Pe.js +0 -4
- package/dist/EpSummativeTable-Djg4DVSn.js +0 -4
- package/dist/EpSvg-DggY9PtW.js +0 -4
- package/dist/EpTable-CvhrvBOq.js +0 -4
- package/dist/EpTerm-C6HSHM0J.js +0 -4
- package/dist/EpText-CftDXaM-.js +0 -4
- package/dist/EpTimeLine-D7ks1LH_.js +0 -4
- package/dist/EpVideo-3MO0ao4H.js +0 -4
- package/dist/EpVideoPanopto-C-hIsXtV.js +0 -4
- package/dist/EpWordDef-BkwqmvfJ.js +0 -4
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import { ref, computed } from "vue";
|
|
4
|
+
import { useRenderText } from '../../composables/useRenderText';
|
|
5
|
+
import EpIcon from '../../components/basics/EpIcon.vue';
|
|
6
|
+
import { mdiArrowLeft } from "@mdi/js";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// Defining the props
|
|
10
|
+
|
|
11
|
+
interface Option {
|
|
12
|
+
name: string;
|
|
13
|
+
question: string;
|
|
14
|
+
imgSrc?: string;
|
|
15
|
+
options?: Option[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
scenarioTitle: string;
|
|
20
|
+
initialInstruction?: string | null;
|
|
21
|
+
bgColor?: string;
|
|
22
|
+
decisionTree: {
|
|
23
|
+
rootQuestion: string;
|
|
24
|
+
imgSrc?: string;
|
|
25
|
+
options: Option[];
|
|
26
|
+
};
|
|
27
|
+
transitionAnimation?: "fade" | "scale" | "slideY";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
32
|
+
initialInstruction: null,
|
|
33
|
+
transitionAnimation: "fade"
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// The necessary references to keep track of the evolution
|
|
38
|
+
|
|
39
|
+
const itinerary = ref<number[]>([]);
|
|
40
|
+
|
|
41
|
+
const currentSlide = ref(0);
|
|
42
|
+
const currentOption = ref<null | any>(null);
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// Manage the introductory part
|
|
46
|
+
const isIntroductoryPart = ref(true);
|
|
47
|
+
|
|
48
|
+
// Get the option from the itinerary
|
|
49
|
+
const getOptionFromItinerary = (itinerary: number[]) => {
|
|
50
|
+
let option: any = props.decisionTree;
|
|
51
|
+
if (itinerary.length > 0) {
|
|
52
|
+
for (let i = 0; i < itinerary.length; i++) {
|
|
53
|
+
option = option.options[itinerary[i]];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
return (option !== props.decisionTree)? option : null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// Return function
|
|
64
|
+
|
|
65
|
+
const goToPrevious = () => {
|
|
66
|
+
if(notTheEnd.value){
|
|
67
|
+
if(itinerary.value.length === 0) {
|
|
68
|
+
isIntroductoryPart.value = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
itinerary.value.pop();
|
|
72
|
+
currentOption.value = getOptionFromItinerary(itinerary.value);
|
|
73
|
+
currentSlide.value = itinerary.value.length;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Verifying if we are at the end
|
|
78
|
+
const notTheEnd = computed(() => {
|
|
79
|
+
return currentSlide.value === 0 || (currentOption.value && currentOption.value.options && currentOption.value.options.length > 0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// Restart function
|
|
85
|
+
const restart = () => {
|
|
86
|
+
currentSlide.value = 0;
|
|
87
|
+
currentOption.value = null;
|
|
88
|
+
itinerary.value = [];
|
|
89
|
+
isIntroductoryPart.value = true;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// Function to get the choices made based on the itinerary
|
|
95
|
+
const getChoices = (itinerary: number[]) => {
|
|
96
|
+
let choices: any[] = [{question: props.decisionTree.rootQuestion, choice: props.decisionTree.options[itinerary[0]].name}];
|
|
97
|
+
let option: any = props.decisionTree.options[itinerary[0]];
|
|
98
|
+
if (itinerary.length > 1) {
|
|
99
|
+
for (let i = 1; i < itinerary.length; i++) {
|
|
100
|
+
let question: string = option.question;
|
|
101
|
+
option = option.options[itinerary[i]];
|
|
102
|
+
choices.push({question, choice: option.name});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return choices;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<template>
|
|
114
|
+
<div>
|
|
115
|
+
|
|
116
|
+
<!-- The introductory part -->
|
|
117
|
+
<div v-if="isIntroductoryPart" class="m-4 mx-auto w-2/3 bg-gray-200 dark:bg-slate-800 rounded-md flex flex-col items-center justify-around p-4 fade" :style="{ minHeight: '500px', backgroundColor: bgColor }">
|
|
118
|
+
<h1 class="font-bold text-3xl">{{ scenarioTitle }}</h1>
|
|
119
|
+
<div class="text-center min-w-max m-3 flex flex-col items-center gap-3" v-if="initialInstruction" v-html="useRenderText(initialInstruction)"></div>
|
|
120
|
+
<button class="rounded bg-blue-500 text-white py-2 px-4 min-w-28 my-3" @click="isIntroductoryPart = false">Commencer</button>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
<div class="m-4 mx-auto w-2/3 rounded-md flex flex-col p-4 bg-gray-200 dark:bg-slate-800" :style="{ minHeight: '500px', backgroundColor: bgColor }" v-else>
|
|
125
|
+
<!-- Return button -->
|
|
126
|
+
<div><button :class="`rounded bg-blue-500 text-white py-2 px-4 m-2 ${transitionAnimation}`" v-if="notTheEnd" @click="goToPrevious">
|
|
127
|
+
<EpIcon :icon-path="mdiArrowLeft" size="25"></EpIcon>
|
|
128
|
+
</button></div>
|
|
129
|
+
|
|
130
|
+
<!-- First question -->
|
|
131
|
+
<div :class="`flex-grow flex flex-col items-center justify-around ${transitionAnimation}`" v-if="currentSlide === 0">
|
|
132
|
+
<div class="text-center min-w-max m-3 flex flex-col items-center gap-3" v-html="useRenderText(decisionTree.rootQuestion)"></div>
|
|
133
|
+
<img v-if="decisionTree.imgSrc" :src="decisionTree.imgSrc" class="rounded" width="200" />
|
|
134
|
+
<div :class="`w-full justify-items-center mx-4 grid ${(decisionTree.options.length <= 2)? 'grid-cols-2' : 'grid-cols-3'}`">
|
|
135
|
+
<button :class="`rounded bg-blue-500 text-white py-2 px-4 min-w-28 max-w-40 my-3 ${(index === (decisionTree.options.length - 1) && (index % 3) === 0)? `col-span-3`: 'col-span-1'}`" v-for="(option, index) in decisionTree.options" :key="index" @click="itinerary.push(index); currentSlide++; currentOption = option">{{ option.name }}</button>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<!-- The rest of the scenario including the end-->
|
|
140
|
+
<div class="flex-grow flex" v-else>
|
|
141
|
+
|
|
142
|
+
<!-- The current slide -->
|
|
143
|
+
<div :key="currentOption.question" :class="`flex-grow flex flex-col items-center justify-around ${transitionAnimation}`" v-if="notTheEnd">
|
|
144
|
+
<div class="text-center min-w-max m-3 flex flex-col items-center gap-3" v-html="useRenderText(currentOption.question)"></div>
|
|
145
|
+
<img v-if="currentOption.imgSrc" :src="currentOption.imgSrc" class="rounded" width="200" />
|
|
146
|
+
<div :class="`w-full justify-items-center mx-4 grid ${(currentOption.options.length <= 2)? 'grid-cols-2' : 'grid-cols-3'}`">
|
|
147
|
+
<button :class="`rounded bg-blue-500 text-white py-2 px-4 min-w-28 max-w-40 my-3 ${(index === (currentOption.options.length - 1) && (index % 3) === 0)? `col-span-3`: 'col-span-1'}`" v-for="(option, index) in currentOption.options" :key="index" @click="itinerary.push(index); currentSlide++; currentOption = option">{{ option.name }}</button>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<!-- End of the scenario -->
|
|
152
|
+
<div :class="`flex-grow flex flex-col items-center ${transitionAnimation}`" v-else>
|
|
153
|
+
<div class="flex-grow flex flex-col items-center">
|
|
154
|
+
<h1 class="my-2 font-bold text-3xl">Vous avez terminé le scénario!</h1>
|
|
155
|
+
<p>Voici votre parcours</p>
|
|
156
|
+
<div class="flex flex-col relative w-full gap-4 px-5 py-5 items-start overflow-auto border-l-4 border-gray-700" style="max-width: 600px; height: 300px;">
|
|
157
|
+
<div v-for="(question, index) in getChoices(itinerary)" :key="index" class="path-item rounded bg-blue-500 text-white py-2 px-4 flex flex-col items-center">
|
|
158
|
+
<u>{{ question.question }}</u>
|
|
159
|
+
<p>{{ question.choice }}</p>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
<button class='rounded bg-blue-500 text-white py-2 px-4 min-w-28 my-3 col-span-1' @click="restart">Recommencer</button>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</template>
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
<style scoped>
|
|
174
|
+
@keyframes fade {
|
|
175
|
+
0% {
|
|
176
|
+
opacity: 0;
|
|
177
|
+
}
|
|
178
|
+
100% {
|
|
179
|
+
opacity: 1;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@keyframes scale {
|
|
185
|
+
0% {
|
|
186
|
+
transform: scale(0.5);
|
|
187
|
+
opacity: 0;
|
|
188
|
+
}
|
|
189
|
+
100% {
|
|
190
|
+
transform: scale(1);
|
|
191
|
+
opacity: 1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@keyframes slideY {
|
|
196
|
+
0% {
|
|
197
|
+
transform: translateY(-100%);
|
|
198
|
+
opacity: 0;
|
|
199
|
+
}
|
|
200
|
+
100% {
|
|
201
|
+
transform: translateY(0);
|
|
202
|
+
opacity: 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Transition animation */
|
|
207
|
+
.fade{
|
|
208
|
+
animation: fade 1s;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.scale{
|
|
212
|
+
animation: scale 1s;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.slideY{
|
|
216
|
+
animation: slideY 1s;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
/* Path item styling */
|
|
224
|
+
.path-item {
|
|
225
|
+
position: relative;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.path-item::after {
|
|
229
|
+
content: '';
|
|
230
|
+
position: absolute;
|
|
231
|
+
top: 50%;
|
|
232
|
+
right: 100%;
|
|
233
|
+
width: 60px;
|
|
234
|
+
height: 4px;
|
|
235
|
+
background-color: rgb(55, 65, 81);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
</style>
|
|
239
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { toRefs, computed, useSlots } from "vue";
|
|
2
|
+
import { toRefs, computed, useSlots, SetupContext } from "vue";
|
|
3
3
|
//types with capital letter, object styles in lowercase
|
|
4
4
|
import { type MediaVariants, mediaVariants } from "../../types/Medias";
|
|
5
5
|
import { type MandateLevel } from "../../types/MandateLevel";
|
|
@@ -26,11 +26,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
26
26
|
type: "base",
|
|
27
27
|
hideIcon: false,
|
|
28
28
|
flat: false,
|
|
29
|
-
labelIntentions:""
|
|
29
|
+
labelIntentions: "",
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
//get slots
|
|
33
|
-
const slots = useSlots();
|
|
33
|
+
const slots: SetupContext["slots"] = useSlots();
|
|
34
34
|
|
|
35
35
|
const hasIntentions = computed(() => {
|
|
36
36
|
return slots.intentions ? true : false;
|
|
@@ -93,7 +93,7 @@ const { title, hideIcon } = toRefs(props);
|
|
|
93
93
|
class="font-semibold mb-2 text-sm uppercase tracking-wide text-gray-500 dark:text-white"
|
|
94
94
|
>
|
|
95
95
|
{{ labelIntentions }}
|
|
96
|
-
</h4>
|
|
96
|
+
</h4>
|
|
97
97
|
<slot name="intentions"></slot>
|
|
98
98
|
<EpDivider />
|
|
99
99
|
</div>
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// DO NOT DELETE. IMPORTANT FOR VIDEO NAVIGATION : this.el.object3D.getWorldPosition(position);
|
|
3
|
+
import { ref, onMounted } from "vue";
|
|
4
|
+
import * as AFRAME from "aframe";
|
|
5
|
+
import { Object3D, Quaternion, Vector3 } from "three";
|
|
6
|
+
import type { Pin, Scene } from "../../types/image360";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
scenes:Scene[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = defineProps<Props>();
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const currentSceneIndex = ref(0);
|
|
17
|
+
const hoveredPin = ref<Pin | null>(null);
|
|
18
|
+
const clickedPin = ref<Pin | null>(null);
|
|
19
|
+
|
|
20
|
+
if (!AFRAME.components["rotation-reader"]) {
|
|
21
|
+
AFRAME.registerComponent("rotation-reader", {
|
|
22
|
+
schema: {},
|
|
23
|
+
tick: createTickFunction(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function handleMarkerClick(pin: Pin, event: Event): void {
|
|
28
|
+
if (!pin) return;
|
|
29
|
+
|
|
30
|
+
if (pin?.event === "onClick" || !pin.event) {
|
|
31
|
+
if (pin.type == "descriptive") {
|
|
32
|
+
clickedPin.value = pin;
|
|
33
|
+
const target = event.target as HTMLElement;
|
|
34
|
+
target.setAttribute("material", "opacity: 0; transparent: true");
|
|
35
|
+
console.log("Marker clicked:", pin);
|
|
36
|
+
} else if (pin.type == "action") {
|
|
37
|
+
const nextSceneIndex = props.scenes.findIndex(
|
|
38
|
+
(scene) => scene.id === pin.scene
|
|
39
|
+
);
|
|
40
|
+
if (nextSceneIndex !== -1) {
|
|
41
|
+
currentSceneIndex.value = nextSceneIndex;
|
|
42
|
+
}
|
|
43
|
+
console.log(
|
|
44
|
+
"Action Pin Clicked. Scene switched to:",
|
|
45
|
+
currentSceneIndex.value
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleMouseEnter(pin: Pin | null, event: Event): void {
|
|
52
|
+
if (!pin) return;
|
|
53
|
+
|
|
54
|
+
if (pin?.event === "onHover" || !pin?.event) {
|
|
55
|
+
hoveredPin.value = pin;
|
|
56
|
+
const target = event.target as HTMLElement;
|
|
57
|
+
target.setAttribute("material", "opacity: 0; transparent: true");
|
|
58
|
+
console.log("Marker hovered:", pin);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleMouseLeave(pin: Pin | null, event: Event): void {
|
|
63
|
+
if (!pin) return;
|
|
64
|
+
|
|
65
|
+
const relatedTarget = (event as MouseEvent).relatedTarget as HTMLElement;
|
|
66
|
+
|
|
67
|
+
if (pin?.event === "onHover" || !pin?.event) {
|
|
68
|
+
const target = event.target as HTMLElement;
|
|
69
|
+
if (clickedPin.value == pin) {
|
|
70
|
+
target.setAttribute("material", "opacity: 1; transparent: true");
|
|
71
|
+
}
|
|
72
|
+
if (
|
|
73
|
+
!relatedTarget?.classList.contains("interactive-area") &&
|
|
74
|
+
hoveredPin.value?.id === pin?.id
|
|
75
|
+
) {
|
|
76
|
+
hoveredPin.value = null;
|
|
77
|
+
console.log("Hover cleared:", pin);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function convertToPosition(x: number, y: number): string {
|
|
83
|
+
return `${x / 10} ${y / 10} -5`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
(async () => {
|
|
88
|
+
await checkAndDeleteComponent("rotation-reader", 2000);
|
|
89
|
+
})();
|
|
90
|
+
(async () => {
|
|
91
|
+
await registerRotationReaderComponent(2000);
|
|
92
|
+
})();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
async function checkAndDeleteComponent(
|
|
96
|
+
componentName: string,
|
|
97
|
+
delayMs: number = 1000
|
|
98
|
+
) {
|
|
99
|
+
if (AFRAME.components[componentName]) {
|
|
100
|
+
await delay(delayMs);
|
|
101
|
+
delete AFRAME.components[componentName];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function delay(ms: number): Promise<void> {
|
|
106
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function registerRotationReaderComponent(delayMs: number = 1000) {
|
|
110
|
+
await delay(delayMs);
|
|
111
|
+
if (AFRAME.components["rotation-reader"]) {
|
|
112
|
+
console.warn(
|
|
113
|
+
"Existing rotation-reader component found. Re-registering..."
|
|
114
|
+
);
|
|
115
|
+
delete AFRAME.components["rotation-reader"];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
AFRAME.registerComponent("rotation-reader", {
|
|
119
|
+
schema: {},
|
|
120
|
+
tick: createTickFunction(),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function createTickFunction() {
|
|
125
|
+
const quaternion = new Quaternion();
|
|
126
|
+
const cameraPosition = new Vector3();
|
|
127
|
+
const objectPosition = new Vector3();
|
|
128
|
+
|
|
129
|
+
const referenceDistance = 5;
|
|
130
|
+
const initialScale = 0.8;
|
|
131
|
+
|
|
132
|
+
return function (this: {
|
|
133
|
+
el: { object3D: InstanceType<typeof Object3D>; sceneEl: any };
|
|
134
|
+
}) {
|
|
135
|
+
updateCameraQuaternion(this.el.object3D, quaternion);
|
|
136
|
+
updatePinsRotation(quaternion);
|
|
137
|
+
|
|
138
|
+
this.el.object3D.getWorldPosition(cameraPosition);
|
|
139
|
+
|
|
140
|
+
document.querySelectorAll(".pin-marker").forEach((pinElement) => {
|
|
141
|
+
const pinObject = (pinElement as any).object3D;
|
|
142
|
+
if (pinObject) {
|
|
143
|
+
pinObject.getWorldPosition(objectPosition);
|
|
144
|
+
const distance = cameraPosition.distanceTo(objectPosition);
|
|
145
|
+
const scale = (distance * initialScale) / referenceDistance;
|
|
146
|
+
pinObject.scale.set(scale, scale, scale);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
document.querySelectorAll(".frame").forEach((frameElement) => {
|
|
151
|
+
const frameObject = (frameElement as any).object3D;
|
|
152
|
+
if (frameObject) {
|
|
153
|
+
frameObject.getWorldPosition(objectPosition);
|
|
154
|
+
const distance = cameraPosition.distanceTo(objectPosition);
|
|
155
|
+
const scale = (distance * initialScale) / referenceDistance;
|
|
156
|
+
frameObject.scale.set(scale, scale, scale);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getPinOpacity(pinId: number): number {
|
|
163
|
+
return (hoveredPin.value && hoveredPin.value.id === pinId) ||
|
|
164
|
+
(clickedPin.value && clickedPin.value.id === pinId)
|
|
165
|
+
? 0
|
|
166
|
+
: 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function updateCameraQuaternion(
|
|
170
|
+
object3D: InstanceType<typeof Object3D>,
|
|
171
|
+
quaternion: InstanceType<typeof Quaternion>
|
|
172
|
+
) {
|
|
173
|
+
object3D.getWorldQuaternion(quaternion);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function updatePinsRotation(quaternion: InstanceType<typeof Quaternion>) {
|
|
177
|
+
document.querySelectorAll(".pin-marker").forEach((pinElement) => {
|
|
178
|
+
const pinObject = (pinElement as any).object3D;
|
|
179
|
+
if (pinObject) {
|
|
180
|
+
pinObject.setRotationFromQuaternion(quaternion);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
document.querySelectorAll(".frame").forEach((pinElement) => {
|
|
184
|
+
const pinObject = (pinElement as any).object3D;
|
|
185
|
+
if (pinObject) {
|
|
186
|
+
pinObject.setRotationFromQuaternion(quaternion);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function removeFrame() {
|
|
192
|
+
clickedPin.value = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getPinColor(pin: Pin): string {
|
|
196
|
+
return pin.color || "indigo";
|
|
197
|
+
}
|
|
198
|
+
</script>
|
|
199
|
+
|
|
200
|
+
<template>
|
|
201
|
+
<div
|
|
202
|
+
style="
|
|
203
|
+
width: 800px;
|
|
204
|
+
height: 600px;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
position: relative;
|
|
207
|
+
margin: auto;
|
|
208
|
+
"
|
|
209
|
+
>
|
|
210
|
+
<a-scene embedded>
|
|
211
|
+
<a-assets>
|
|
212
|
+
<img
|
|
213
|
+
v-for="scene in props.scenes"
|
|
214
|
+
:key="scene.id"
|
|
215
|
+
:id="`image${scene.id}`"
|
|
216
|
+
:src="scene.skyImage"
|
|
217
|
+
crossorigin="anonymous"
|
|
218
|
+
/>
|
|
219
|
+
</a-assets>
|
|
220
|
+
<a-entity camera look-controls id="rig" rotation-reader>
|
|
221
|
+
<a-cursor
|
|
222
|
+
id="cursor"
|
|
223
|
+
cursor="rayOrigin: mouse"
|
|
224
|
+
raycaster="objects: none"
|
|
225
|
+
geometry="primitive: ring; radiusInner: 0; radiusOuter: 0"
|
|
226
|
+
material="opacity: 0; transparent: true"
|
|
227
|
+
></a-cursor>
|
|
228
|
+
</a-entity>
|
|
229
|
+
|
|
230
|
+
<a-sky
|
|
231
|
+
:src="`#image${props.scenes[currentSceneIndex].id}`"
|
|
232
|
+
radius="100"
|
|
233
|
+
></a-sky>
|
|
234
|
+
|
|
235
|
+
<a-entity
|
|
236
|
+
v-for="pin in props.scenes[currentSceneIndex].pins"
|
|
237
|
+
:key="pin.id"
|
|
238
|
+
:position="convertToPosition(pin.x, pin.y)"
|
|
239
|
+
cursor="rayOrigin: mouse"
|
|
240
|
+
>
|
|
241
|
+
<a-circle
|
|
242
|
+
class="pin-marker"
|
|
243
|
+
:color="getPinColor(pin)"
|
|
244
|
+
radius="0.25"
|
|
245
|
+
shader="flat"
|
|
246
|
+
:material="`opacity: ${getPinOpacity(
|
|
247
|
+
pin.id
|
|
248
|
+
)}; transparent: true`"
|
|
249
|
+
@click="(event: Event) => handleMarkerClick(pin, event)"
|
|
250
|
+
@mouseenter="(event: Event) => handleMouseEnter(pin, event)"
|
|
251
|
+
></a-circle>
|
|
252
|
+
</a-entity>
|
|
253
|
+
|
|
254
|
+
<a-entity
|
|
255
|
+
v-if="hoveredPin"
|
|
256
|
+
:position="convertToPosition(hoveredPin.x, hoveredPin.y)"
|
|
257
|
+
class="frame interactive-area"
|
|
258
|
+
cursor="rayOrigin: mouse"
|
|
259
|
+
@mouseleave="(event: Event) => handleMouseLeave(hoveredPin, event)"
|
|
260
|
+
>
|
|
261
|
+
<a-plane
|
|
262
|
+
color="white"
|
|
263
|
+
:opacity="1"
|
|
264
|
+
width="4"
|
|
265
|
+
height="1.5"
|
|
266
|
+
material="shader: standard; side: double;"
|
|
267
|
+
position="0 0 0"
|
|
268
|
+
geometry="primitive: plane; height: 1.5; width: 4;"
|
|
269
|
+
>
|
|
270
|
+
</a-plane>
|
|
271
|
+
|
|
272
|
+
<a-text
|
|
273
|
+
:value="hoveredPin.title"
|
|
274
|
+
color="#2c3e50"
|
|
275
|
+
font="dejavu"
|
|
276
|
+
align="center"
|
|
277
|
+
width="3.8"
|
|
278
|
+
position="0 0.3 0.01"
|
|
279
|
+
>
|
|
280
|
+
</a-text>
|
|
281
|
+
|
|
282
|
+
<a-text
|
|
283
|
+
:value="hoveredPin.description"
|
|
284
|
+
color="#34495e"
|
|
285
|
+
font="dejavu"
|
|
286
|
+
align="center"
|
|
287
|
+
width="3.5"
|
|
288
|
+
position="0 -0.3 0.01"
|
|
289
|
+
>
|
|
290
|
+
</a-text>
|
|
291
|
+
</a-entity>
|
|
292
|
+
|
|
293
|
+
<a-entity
|
|
294
|
+
v-if="clickedPin"
|
|
295
|
+
:position="convertToPosition(clickedPin.x, clickedPin.y)"
|
|
296
|
+
class="frame interactive-area"
|
|
297
|
+
cursor="rayOrigin: mouse"
|
|
298
|
+
>
|
|
299
|
+
<a-plane
|
|
300
|
+
color="white"
|
|
301
|
+
:opacity="1"
|
|
302
|
+
width="4"
|
|
303
|
+
height="1.5"
|
|
304
|
+
material="shader: standard; side: double;"
|
|
305
|
+
position="0 0 0"
|
|
306
|
+
geometry="primitive: plane; height: 1.5; width: 4;"
|
|
307
|
+
>
|
|
308
|
+
</a-plane>
|
|
309
|
+
|
|
310
|
+
<a-plane
|
|
311
|
+
class="close-frame"
|
|
312
|
+
color="red"
|
|
313
|
+
width="0.4"
|
|
314
|
+
height="0.4"
|
|
315
|
+
position="1.8 0.55 0.01"
|
|
316
|
+
material="shader: flat; side: double; transparent: true;"
|
|
317
|
+
geometry="primitive: plane; height: 0.4; width: 0.4;"
|
|
318
|
+
@click="removeFrame"
|
|
319
|
+
>
|
|
320
|
+
</a-plane>
|
|
321
|
+
|
|
322
|
+
<a-text
|
|
323
|
+
:value="clickedPin.title"
|
|
324
|
+
color="#2c3e50"
|
|
325
|
+
font="dejavu"
|
|
326
|
+
align="center"
|
|
327
|
+
width="3.8"
|
|
328
|
+
position="0 0.3 0.01"
|
|
329
|
+
>
|
|
330
|
+
</a-text>
|
|
331
|
+
|
|
332
|
+
<a-text
|
|
333
|
+
:value="clickedPin.description"
|
|
334
|
+
color="#34495e"
|
|
335
|
+
font="dejavu"
|
|
336
|
+
align="center"
|
|
337
|
+
width="3.5"
|
|
338
|
+
position="0 -0.3 0.01"
|
|
339
|
+
>
|
|
340
|
+
</a-text>
|
|
341
|
+
</a-entity>
|
|
342
|
+
</a-scene>
|
|
343
|
+
</div>
|
|
344
|
+
</template>
|