@saooti/octopus-sdk 41.0.15 → 41.0.17
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/CHANGELOG.md +23 -0
- package/eslint.config.mjs +13 -1
- package/index.ts +6 -1
- package/package.json +1 -1
- package/src/components/composable/route/useRouteUpdateParams.ts +15 -3
- package/src/components/display/playlist/PodcastList.vue +12 -1
- package/src/components/display/podcasts/PodcastImage.vue +28 -7
- package/src/components/display/podcasts/PodcastItem.vue +4 -1
- package/src/components/display/podcasts/PodcastList.vue +9 -1
- package/src/components/display/podcasts/PodcastPlayButton.vue +92 -20
- package/src/components/form/ClassicSelect.vue +29 -29
- package/src/components/misc/ClassicAccordion.vue +2 -2
- package/src/components/misc/ClassicAlert.vue +98 -0
- package/src/components/misc/ClassicHelpButton.vue +53 -23
- package/src/components/misc/ClassicPopover.vue +10 -7
- package/src/components/pages/PodcastPage.vue +4 -0
- package/tsconfig.json +38 -42
- package/typings/index.d.ts +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## 41.0.17 (07/11/2025)
|
|
4
|
+
|
|
5
|
+
**Features**
|
|
6
|
+
|
|
7
|
+
- Ajout options de configuration sur `ClassicHelpButton`
|
|
8
|
+
|
|
9
|
+
**Fixes**
|
|
10
|
+
|
|
11
|
+
- Correction anomalie fermeture `ClassicPopover`
|
|
12
|
+
|
|
13
|
+
**Misc**
|
|
14
|
+
|
|
15
|
+
- Ajout du `CHANGELOG`
|
|
16
|
+
- Ajustements configuration typescript pour analyse de code
|
|
17
|
+
- Nouvelles règles eslint
|
|
18
|
+
|
|
19
|
+
## 41.0.16 (04/11/2025)
|
|
20
|
+
|
|
21
|
+
- Bannière 'en cours de traitement' ne s'affiche plus dans les listes
|
|
22
|
+
- Ajustements sur `ClassicSelect` & `ClassicHelpButton`
|
|
23
|
+
- Ajout de ClassicAlert, pour l'affichage de message
|
package/eslint.config.mjs
CHANGED
|
@@ -23,9 +23,21 @@ export default typescriptEslint.config(
|
|
|
23
23
|
rules: {
|
|
24
24
|
// your rules
|
|
25
25
|
"curly": ['error'],
|
|
26
|
+
|
|
27
|
+
// Please don't use console statements and duplicated imports, TODOs are marked
|
|
26
28
|
"no-console": ['warn', { allow: ['warn', 'error'] }],
|
|
27
29
|
"no-warning-comments": ['warn'],
|
|
28
|
-
"no-duplicate-imports": ['warn']
|
|
30
|
+
"no-duplicate-imports": ['warn'],
|
|
31
|
+
|
|
32
|
+
// Prevent errors when testing on refs (instead of value of ref)
|
|
33
|
+
"vue/no-ref-as-operand": ['error'],
|
|
34
|
+
|
|
35
|
+
// Indentation
|
|
36
|
+
"vue/html-indent": ['warn', 4],
|
|
37
|
+
"vue/script-indent": ['warn', 4],
|
|
38
|
+
|
|
39
|
+
// Number of attributes per line (increase because sometimes two is not a lot)
|
|
40
|
+
"vue/max-attributes-per-line": ['warn', { singleline: 2 } ]
|
|
29
41
|
},
|
|
30
42
|
}
|
|
31
43
|
);
|
package/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export const getClassicLazy = () => import("./src/components/misc/ClassicLazy.vu
|
|
|
39
39
|
export const getContractPreviewModal = () => import("./src/components/misc/modal/ContractPreviewModal.vue");
|
|
40
40
|
export const getClassicModalInBody = () => import("./src/components/misc/modal/ClassicModalInBody.vue");
|
|
41
41
|
export const getClassicHelpButton = () => import("./src/components/misc/ClassicHelpButton.vue");
|
|
42
|
+
export const getClassicAlert = () => import("./src/components/misc/ClassicAlert.vue");
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
//Display
|
|
@@ -152,6 +153,9 @@ export const getXIcon = () => import("./src/components/icons/XIcon.vue");
|
|
|
152
153
|
// Routing
|
|
153
154
|
import { setupRouter } from './src/router/utils';
|
|
154
155
|
|
|
156
|
+
// Types
|
|
157
|
+
import { type SelectOption } from "./src/components/form/ClassicSelect.vue";
|
|
158
|
+
|
|
155
159
|
export {
|
|
156
160
|
useResizePhone,
|
|
157
161
|
useTagOf,
|
|
@@ -183,5 +187,6 @@ export {
|
|
|
183
187
|
deepEqual,
|
|
184
188
|
downloadHelper,
|
|
185
189
|
displayHelper,
|
|
186
|
-
setupRouter
|
|
190
|
+
setupRouter,
|
|
191
|
+
SelectOption,
|
|
187
192
|
};
|
package/package.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useRoute, useRouter } from 'vue-router';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
interface RouteParams {
|
|
4
|
+
productor: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const useRouteUpdateParams = () => {
|
|
3
8
|
|
|
4
9
|
const router = useRouter();
|
|
5
10
|
const route = useRoute();
|
|
@@ -38,11 +43,18 @@ export const useRouteUpdateParams = ()=>{
|
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
45
|
|
|
46
|
+
/*
|
|
47
|
+
function updateRouteParams(params: Partial<RouteParams>) {
|
|
48
|
+
router.push({ query: {
|
|
49
|
+
...route.query,
|
|
50
|
+
...params
|
|
51
|
+
}});
|
|
52
|
+
}*/
|
|
41
53
|
|
|
42
|
-
|
|
54
|
+
return {
|
|
43
55
|
updatePaginateSize,
|
|
44
56
|
updateRouteParam,
|
|
45
57
|
updateRouteParamAdvanced,
|
|
46
58
|
updateFiltersParam
|
|
47
|
-
|
|
59
|
+
}
|
|
48
60
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying a list of podcasts IN A PLAYLIST
|
|
3
|
+
Do not confuse this with PodcastList from podcasts
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
6
|
<div>
|
|
3
7
|
<h3 class="mb-3 align-self-baseline">
|
|
4
8
|
{{ titleList }}
|
|
5
9
|
</h3>
|
|
10
|
+
|
|
6
11
|
<ClassicSearch
|
|
7
12
|
v-if="!loading && notEmptyPlaylist"
|
|
8
13
|
v-model:text-init="searchPattern"
|
|
@@ -10,6 +15,7 @@
|
|
|
10
15
|
id-search="podcast-list-search"
|
|
11
16
|
:label="t('Search')"
|
|
12
17
|
/>
|
|
18
|
+
|
|
13
19
|
<ListPaginate
|
|
14
20
|
id="podcastPlaylistListPaginate"
|
|
15
21
|
v-model:first="dfirst"
|
|
@@ -38,7 +44,12 @@
|
|
|
38
44
|
:key="p.podcastId"
|
|
39
45
|
:min-height="410"
|
|
40
46
|
>
|
|
41
|
-
<PodcastItem
|
|
47
|
+
<PodcastItem
|
|
48
|
+
v-if="0 !== p.podcastId"
|
|
49
|
+
:podcast="p"
|
|
50
|
+
:in-list="true"
|
|
51
|
+
/>
|
|
52
|
+
|
|
42
53
|
<template #preview>
|
|
43
54
|
<router-link
|
|
44
55
|
:to="{
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component to display the image of a podcast.
|
|
3
|
+
May also display additional information, for example when an error occured
|
|
4
|
+
when processing the file (using PodcastPlayButton).
|
|
5
|
+
-->
|
|
1
6
|
<template>
|
|
2
7
|
<div
|
|
3
8
|
v-if="podcast"
|
|
@@ -16,10 +21,9 @@
|
|
|
16
21
|
height="270"
|
|
17
22
|
aria-hidden="true"
|
|
18
23
|
alt=""
|
|
19
|
-
|
|
20
24
|
class="img-box img-box-podcast"
|
|
21
25
|
:title="t('Episode name image', { name: podcast.title })"
|
|
22
|
-
|
|
26
|
+
>
|
|
23
27
|
</router-link>
|
|
24
28
|
<div
|
|
25
29
|
v-if="state.generalParameters.podcastmaker"
|
|
@@ -43,6 +47,7 @@
|
|
|
43
47
|
:podcast="podcast"
|
|
44
48
|
:hide-play="hidePlay"
|
|
45
49
|
:fetch-conference="fetchConference"
|
|
50
|
+
:in-list="inList"
|
|
46
51
|
/>
|
|
47
52
|
<button
|
|
48
53
|
v-if="displayDescription && isMobile"
|
|
@@ -73,6 +78,8 @@ const props = defineProps({
|
|
|
73
78
|
arrowDirection: { default: "up", type: String },
|
|
74
79
|
isAnimatorLive: { default: false, type: Boolean },
|
|
75
80
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
81
|
+
/** Indicates that the podcast is displayed in a list */
|
|
82
|
+
inList: { default: false, type: Boolean }
|
|
76
83
|
})
|
|
77
84
|
|
|
78
85
|
//Emits
|
|
@@ -104,23 +111,37 @@ const isRecordedInLive = computed(() => {
|
|
|
104
111
|
);
|
|
105
112
|
});
|
|
106
113
|
const statusText = computed(() => {
|
|
107
|
-
if (!props.fetchConference)
|
|
114
|
+
if (!props.fetchConference) {
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
switch (props.fetchConference.status) {
|
|
109
119
|
case "PLANNED":
|
|
110
120
|
return t("live in few time");
|
|
121
|
+
|
|
111
122
|
case "PENDING":
|
|
112
|
-
if (props.isAnimatorLive)
|
|
113
|
-
|
|
123
|
+
if (props.isAnimatorLive) {
|
|
124
|
+
return t("Open studio");
|
|
125
|
+
} else {
|
|
126
|
+
return t("live upcoming");
|
|
127
|
+
}
|
|
128
|
+
|
|
114
129
|
case "RECORDING":
|
|
115
130
|
return t("In live");
|
|
131
|
+
|
|
116
132
|
case "DEBRIEFING":
|
|
117
|
-
if ("READY_TO_RECORD" === props.podcast.processingStatus)
|
|
133
|
+
if ("READY_TO_RECORD" === props.podcast.processingStatus) {
|
|
118
134
|
return t("Not recording");
|
|
119
|
-
|
|
135
|
+
} else {
|
|
136
|
+
return t("Debriefing");
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
case "ERROR":
|
|
121
140
|
return t("In error");
|
|
141
|
+
|
|
122
142
|
case "PUBLISHING":
|
|
123
143
|
return t("Publishing");
|
|
144
|
+
|
|
124
145
|
default:
|
|
125
146
|
return "";
|
|
126
147
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:display-description="0 !== description.length"
|
|
11
11
|
:arrow-direction="arrowDirection"
|
|
12
12
|
:fetch-conference="fetchConference"
|
|
13
|
+
:in-list="inList"
|
|
13
14
|
@hide-description="hideDescription"
|
|
14
15
|
@show-description="showDescription"
|
|
15
16
|
/>
|
|
@@ -50,7 +51,9 @@ import { Conference } from "@/stores/class/conference/conference";
|
|
|
50
51
|
const props = defineProps({
|
|
51
52
|
podcast: { default: () => ({}), type: Object as () => Podcast },
|
|
52
53
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
53
|
-
|
|
54
|
+
/** Indicates that the podcast is displayed in a list */
|
|
55
|
+
inList: { default: false, type: Boolean }
|
|
56
|
+
});
|
|
54
57
|
|
|
55
58
|
//Data
|
|
56
59
|
const isMobile = ref(false);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying a list of podcast
|
|
3
|
+
DO NOT confuse this with PodcastList from playlist
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
6
|
<ListPaginate
|
|
3
7
|
id="podcastListPaginate"
|
|
@@ -28,7 +32,11 @@
|
|
|
28
32
|
:key="p.podcastId"
|
|
29
33
|
:min-height="410"
|
|
30
34
|
>
|
|
31
|
-
<PodcastItem
|
|
35
|
+
<PodcastItem
|
|
36
|
+
v-if="0 !== p.podcastId"
|
|
37
|
+
:podcast="p"
|
|
38
|
+
in-list
|
|
39
|
+
/>
|
|
32
40
|
<template #preview>
|
|
33
41
|
<router-link
|
|
34
42
|
:to="{
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying the play button on a podcast.
|
|
3
|
+
If the podcast is not available, an overlay is displayed with a message.
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
6
|
+
<div
|
|
7
|
+
v-if="!hidePlay || recordingLive"
|
|
8
|
+
:class="{ 'img-blur-background': displayBanner, 'allow-play': classicPodcastPlay }"
|
|
9
|
+
>
|
|
10
|
+
<div
|
|
11
|
+
v-if="displayBanner"
|
|
12
|
+
class="live-image-status bg-dark"
|
|
13
|
+
>
|
|
4
14
|
{{ textVisible }}
|
|
5
15
|
</div>
|
|
6
|
-
|
|
16
|
+
|
|
17
|
+
<div
|
|
18
|
+
class="multi-buttons-play"
|
|
19
|
+
:class="justButtons ? 'play-button-relative' : ''"
|
|
20
|
+
>
|
|
7
21
|
<template v-if="!isLiveToBeRecorded">
|
|
8
22
|
<button
|
|
9
23
|
class="d-flex"
|
|
@@ -16,8 +30,14 @@
|
|
|
16
30
|
v-if="!playingPodcast || (playingPodcast && playerStore.playerVideo)"
|
|
17
31
|
:size="'audio' === hoverType ? 50 : 40"
|
|
18
32
|
/>
|
|
19
|
-
<PodcastIsPlaying v-if="playingPodcast && !playerStore.playerVideo"/>
|
|
20
|
-
<time
|
|
33
|
+
<PodcastIsPlaying v-if="playingPodcast && !playerStore.playerVideo" />
|
|
34
|
+
<time
|
|
35
|
+
v-if="!isVideoPodcast"
|
|
36
|
+
class="ms-1"
|
|
37
|
+
:datetime="durationIso"
|
|
38
|
+
>
|
|
39
|
+
{{ durationString }}
|
|
40
|
+
</time>
|
|
21
41
|
</button>
|
|
22
42
|
<button
|
|
23
43
|
v-if="isVideoPodcast"
|
|
@@ -27,15 +47,35 @@
|
|
|
27
47
|
@mouseenter="hoverType = 'video'"
|
|
28
48
|
@mouseleave="hoverType = ''"
|
|
29
49
|
>
|
|
30
|
-
<PlayVideoIcon
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
<PlayVideoIcon
|
|
51
|
+
v-if="!playerStore.playerVideo"
|
|
52
|
+
:size="'video' === hoverType ? 50 : 40"
|
|
53
|
+
/>
|
|
54
|
+
<PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
|
|
55
|
+
<time
|
|
56
|
+
class="ms-2"
|
|
57
|
+
:datetime="durationIso"
|
|
58
|
+
>
|
|
59
|
+
{{ durationString }}
|
|
60
|
+
</time>
|
|
33
61
|
</button>
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
v-if="displayBanner"
|
|
65
|
+
class="special-icon-play-button"
|
|
66
|
+
>
|
|
67
|
+
<component
|
|
68
|
+
:is="iconName"
|
|
69
|
+
:size="16"
|
|
70
|
+
/>
|
|
36
71
|
</div>
|
|
37
72
|
</template>
|
|
38
|
-
<component
|
|
73
|
+
<component
|
|
74
|
+
:is="iconName"
|
|
75
|
+
v-else
|
|
76
|
+
:size="50"
|
|
77
|
+
:title="textVisible"
|
|
78
|
+
/>
|
|
39
79
|
</div>
|
|
40
80
|
</div>
|
|
41
81
|
</template>
|
|
@@ -70,6 +110,8 @@ const props = defineProps({
|
|
|
70
110
|
hidePlay: { default: false, type: Boolean },
|
|
71
111
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
72
112
|
justButtons: { default: false, type: Boolean },
|
|
113
|
+
/** Indicates that the podcast is displayed in a list */
|
|
114
|
+
inList: { default: false, type: Boolean }
|
|
73
115
|
})
|
|
74
116
|
|
|
75
117
|
|
|
@@ -119,42 +161,65 @@ const isLiveValidAndVisible = computed(() => {
|
|
|
119
161
|
props.podcast.availability.visibility
|
|
120
162
|
);
|
|
121
163
|
});
|
|
164
|
+
|
|
165
|
+
/** Whether the podcast can be played */
|
|
122
166
|
const classicPodcastPlay = computed(() => {
|
|
123
167
|
return (
|
|
124
168
|
isLiveValidAndVisible.value &&
|
|
125
169
|
!isLiveToBeRecorded.value &&
|
|
126
170
|
("READY_TO_RECORD" === props.podcast.processingStatus ||
|
|
127
171
|
"READY" === props.podcast.processingStatus ||
|
|
128
|
-
|
|
129
|
-
undefined === authStore.authOrgaId))
|
|
172
|
+
"PROCESSING" === props.podcast.processingStatus)
|
|
130
173
|
);
|
|
131
174
|
});
|
|
175
|
+
|
|
176
|
+
const displayBanner = computed(() => {
|
|
177
|
+
return !classicPodcastPlay.value || ("PROCESSING" === props.podcast.processingStatus && !props.inList);
|
|
178
|
+
});
|
|
179
|
+
|
|
132
180
|
const iconName = computed(() => {
|
|
133
|
-
if (isLiveToBeRecorded.value)
|
|
181
|
+
if (isLiveToBeRecorded.value) {
|
|
182
|
+
return ClockOutlineIcon;
|
|
183
|
+
}
|
|
184
|
+
|
|
134
185
|
if ("READY" === props.podcast.processingStatus || props.fetchConference) {
|
|
135
|
-
if (!props.podcast.valid)
|
|
186
|
+
if (!props.podcast.valid) {
|
|
187
|
+
return CheckIcon;
|
|
188
|
+
}
|
|
189
|
+
|
|
136
190
|
if (
|
|
137
191
|
!props.podcast.availability.visibility &&
|
|
138
192
|
props.podcast.availability.date
|
|
139
|
-
)
|
|
193
|
+
) {
|
|
140
194
|
return ClockOutlineIcon;
|
|
195
|
+
}
|
|
196
|
+
|
|
141
197
|
return EyeOffOutlineIcon;
|
|
142
198
|
}
|
|
199
|
+
|
|
143
200
|
if (
|
|
144
201
|
"PLANNED" === props.podcast.processingStatus ||
|
|
145
202
|
"PROCESSING" === props.podcast.processingStatus
|
|
146
|
-
)
|
|
203
|
+
) {
|
|
147
204
|
return TimerSandEmptyIcon;
|
|
148
|
-
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if ("CANCELED" === props.podcast.processingStatus) {
|
|
208
|
+
return CancelIcon;
|
|
209
|
+
}
|
|
149
210
|
return AlertIcon;
|
|
150
211
|
});
|
|
212
|
+
|
|
213
|
+
/** The text to display when the podcast is not ready */
|
|
151
214
|
const textVisible = computed(() => {
|
|
152
215
|
if (isLiveToBeRecorded.value){
|
|
153
216
|
return t("Podcast linked to waiting live");
|
|
154
217
|
}
|
|
155
218
|
|
|
156
219
|
if ("READY" === props.podcast.processingStatus || props.fetchConference) {
|
|
157
|
-
if (!props.podcast.valid)
|
|
220
|
+
if (!props.podcast.valid) {
|
|
221
|
+
return t("Podcast to validate");
|
|
222
|
+
}
|
|
158
223
|
if (
|
|
159
224
|
!props.podcast.availability.visibility &&
|
|
160
225
|
props.podcast.availability.date
|
|
@@ -174,6 +239,7 @@ const textVisible = computed(() => {
|
|
|
174
239
|
}
|
|
175
240
|
return t("Podcast in error");
|
|
176
241
|
});
|
|
242
|
+
|
|
177
243
|
const recordingLive = computed(() => {
|
|
178
244
|
return (
|
|
179
245
|
undefined !== props.fetchConference &&
|
|
@@ -188,7 +254,9 @@ const durationString = computed(() => {
|
|
|
188
254
|
);
|
|
189
255
|
});
|
|
190
256
|
const durationIso = computed(() => {
|
|
191
|
-
if (!props.podcast || props.podcast.duration <= 1)
|
|
257
|
+
if (!props.podcast || props.podcast.duration <= 1) {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
192
260
|
return dayjs.duration({ milliseconds: props.podcast.duration }).toISOString();
|
|
193
261
|
});
|
|
194
262
|
|
|
@@ -230,6 +298,10 @@ function play(isVideo: boolean): void {
|
|
|
230
298
|
background-color:var(--octopus-background-transparent);
|
|
231
299
|
// Allow pointer events to go through (allow click on image beneath blur)
|
|
232
300
|
pointer-events: none;
|
|
301
|
+
|
|
302
|
+
&.allow-play {
|
|
303
|
+
pointer-events: all;
|
|
304
|
+
}
|
|
233
305
|
}
|
|
234
306
|
|
|
235
307
|
.live-image-status {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="classic-select" :class="{ 'form-margin': displayLabel }">
|
|
3
|
-
<label v-show="displayLabel" :for="idSelect" :class="classLabel">
|
|
4
|
-
label
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<label v-show="displayLabel" :for="idSelect" :class="classLabel">
|
|
4
|
+
{{ label }}
|
|
5
|
+
<AsteriskIcon v-if="displayRequired" :size="10" class="ms-1 mb-2" :title="t('Mandatory input')"/>
|
|
6
|
+
<slot name="after-label" />
|
|
7
|
+
</label>
|
|
8
|
+
|
|
7
9
|
<select
|
|
8
10
|
:id="idSelect"
|
|
9
11
|
:value="textInit"
|
|
@@ -28,37 +30,36 @@
|
|
|
28
30
|
</select>
|
|
29
31
|
</div>
|
|
30
32
|
</template>
|
|
33
|
+
|
|
31
34
|
<script setup lang="ts">
|
|
32
35
|
import AsteriskIcon from "vue-material-design-icons/Asterisk.vue";
|
|
33
36
|
import { computed } from "vue";
|
|
34
37
|
import { useI18n } from "vue-i18n";
|
|
35
38
|
|
|
39
|
+
export interface SelectOption<T = number|string|undefined> {
|
|
40
|
+
title: string;
|
|
41
|
+
value: T;
|
|
42
|
+
fontFamily?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
//Props
|
|
37
46
|
const props = defineProps({
|
|
38
47
|
idSelect: { default: "", type: String },
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
fontFamily?: string;
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
textInit: { default: undefined, type: [String, Number] },
|
|
58
|
-
classLabel: { default: "form-label", type: String },
|
|
59
|
-
orderOptions: { default: true, type: Boolean},
|
|
60
|
-
placeholder: { default: undefined, type: String},
|
|
61
|
-
displayRequired: { default: false, type: Boolean },
|
|
48
|
+
label: { default: "", type: String },
|
|
49
|
+
displayLabel: { default: true, type: Boolean },
|
|
50
|
+
transparent: { default: false, type: Boolean },
|
|
51
|
+
isDisabled: { default: false, type: Boolean },
|
|
52
|
+
options: {
|
|
53
|
+
default: () => [],
|
|
54
|
+
type: Array as () => Array<SelectOption>,
|
|
55
|
+
},
|
|
56
|
+
topOption: { default: undefined,type: Object as () => SelectOption,
|
|
57
|
+
},
|
|
58
|
+
textInit: { default: undefined, type: [String, Number] },
|
|
59
|
+
classLabel: { default: "form-label", type: String },
|
|
60
|
+
orderOptions: { default: true, type: Boolean},
|
|
61
|
+
placeholder: { default: undefined, type: String},
|
|
62
|
+
displayRequired: { default: false, type: Boolean },
|
|
62
63
|
})
|
|
63
64
|
|
|
64
65
|
|
|
@@ -100,9 +101,8 @@ function onChange(value:string){
|
|
|
100
101
|
emit('update:textInit', value)
|
|
101
102
|
}
|
|
102
103
|
</script>
|
|
103
|
-
<style lang="scss">
|
|
104
|
-
|
|
105
104
|
|
|
105
|
+
<style lang="scss">
|
|
106
106
|
.octopus-app {
|
|
107
107
|
select option:is(:checked, :hover){
|
|
108
108
|
box-shadow: 0 0 10px 100px var(--octopus-secondary) inset;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
:src="imageUrl"
|
|
22
22
|
aria-hidden="true"
|
|
23
23
|
alt=""
|
|
24
|
-
|
|
24
|
+
>
|
|
25
25
|
<span>{{ title }}</span>
|
|
26
26
|
<slot name="afterTitle"/>
|
|
27
27
|
<ChevronDownIcon class="ms-auto" :class="{ 'arrow-transform': isOpen }" />
|
|
@@ -58,7 +58,7 @@ const emit = defineEmits(["open"]);
|
|
|
58
58
|
const isOpen = ref(false);
|
|
59
59
|
|
|
60
60
|
//Watch
|
|
61
|
-
watch(isOpen, () => emit("open"));
|
|
61
|
+
watch(isOpen, () => emit("open", isOpen.value));
|
|
62
62
|
|
|
63
63
|
onMounted(()=>{
|
|
64
64
|
isOpen.value = props.initOpen;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Simple component to display a message to the user.
|
|
3
|
+
Usage:
|
|
4
|
+
<ClassicAlert type='info'>This is an information</ClassicAlert>
|
|
5
|
+
<ClassicAlert type='success'>Your changes have been saved</ClassicAlert>
|
|
6
|
+
<ClassicAlert type='warning'>Some data will be lost</ClassicAlert>
|
|
7
|
+
<ClassicAlert type='error'>An error occured while saving</ClassicAlert>
|
|
8
|
+
-->
|
|
9
|
+
<template>
|
|
10
|
+
<div
|
|
11
|
+
class="p-2 pe-4 my-2 rounded d-flex alert"
|
|
12
|
+
:class="cardClass"
|
|
13
|
+
>
|
|
14
|
+
<!-- The icon -->
|
|
15
|
+
<component
|
|
16
|
+
:is="iconComponent"
|
|
17
|
+
v-if="!noIcon"
|
|
18
|
+
class="icon"
|
|
19
|
+
:size="30"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<!-- Main content -->
|
|
23
|
+
<span class="ms-2">
|
|
24
|
+
<strong v-if="title">
|
|
25
|
+
{{ title }}
|
|
26
|
+
<br>
|
|
27
|
+
</strong>
|
|
28
|
+
|
|
29
|
+
<slot />
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { computed } from 'vue';
|
|
36
|
+
|
|
37
|
+
import Alert from 'vue-material-design-icons/Alert.vue';
|
|
38
|
+
import CheckCircle from 'vue-material-design-icons/CheckCircle.vue';
|
|
39
|
+
import CloseCircle from 'vue-material-design-icons/CloseCircle.vue';
|
|
40
|
+
import Information from 'vue-material-design-icons/Information.vue';
|
|
41
|
+
|
|
42
|
+
const { type } = defineProps<{
|
|
43
|
+
/** Disables the icon when true */
|
|
44
|
+
noIcon?: boolean;
|
|
45
|
+
/** An optional title for the alert */
|
|
46
|
+
title?: string;
|
|
47
|
+
/** The type of message */
|
|
48
|
+
type: 'info'|'success'|'warning'|'error';
|
|
49
|
+
}>();
|
|
50
|
+
|
|
51
|
+
/** The class applied to the alert */
|
|
52
|
+
const cardClass = computed(() => {
|
|
53
|
+
return 'alert-' + type;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const iconComponent = computed(() => {
|
|
57
|
+
if (type === 'info') {
|
|
58
|
+
return Information;
|
|
59
|
+
} else if (type === 'success') {
|
|
60
|
+
return CheckCircle;
|
|
61
|
+
} else if (type === 'warning') {
|
|
62
|
+
return Alert;
|
|
63
|
+
} else if (type === 'error') {
|
|
64
|
+
return CloseCircle;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style lang="scss" scoped>
|
|
70
|
+
.alert {
|
|
71
|
+
//color: white;
|
|
72
|
+
border: 1px solid;
|
|
73
|
+
border-left: 8px solid;
|
|
74
|
+
|
|
75
|
+
.icon {
|
|
76
|
+
align-items: start !important;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$types: (
|
|
80
|
+
'success': hsl(from var(--octopus-primary) h s 45),
|
|
81
|
+
'info': var(--octopus-primary),
|
|
82
|
+
'warning': var(--octopus-warning),
|
|
83
|
+
'error': var(--octopus-danger),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
&.alert {
|
|
87
|
+
@each $type, $color in $types {
|
|
88
|
+
&-#{$type} {
|
|
89
|
+
background-color: hsl(from #{$color} h s 95) !important;
|
|
90
|
+
border-color: #{$color} !important;
|
|
91
|
+
.icon {
|
|
92
|
+
color: #{$color};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
A simple component to display a help button that shows its message when
|
|
3
|
+
hovered.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Usage:
|
|
6
|
+
<ClassicHelpButton>This is my help message</ClassicHelpButton>
|
|
7
7
|
-->
|
|
8
8
|
<template>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
>
|
|
13
|
-
<!-- Button icon -->
|
|
14
|
-
<HelpCircleIcon :size="30" />
|
|
15
|
-
|
|
16
|
-
<!-- Tooltip -->
|
|
17
|
-
<ClassicPopover
|
|
18
|
-
:target="computedId"
|
|
19
|
-
popover-class="popover-z-index"
|
|
9
|
+
<button
|
|
10
|
+
:id="computedId"
|
|
11
|
+
class="btn-transparent"
|
|
20
12
|
>
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
<!-- Button icon -->
|
|
14
|
+
<HelpCircleIcon :fill-color="color" :size="iconSize" />
|
|
15
|
+
|
|
16
|
+
<!-- Tooltip -->
|
|
17
|
+
<ClassicPopover
|
|
18
|
+
:target="computedId"
|
|
19
|
+
popover-class="help-popover"
|
|
20
|
+
>
|
|
21
|
+
<div class="content">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</ClassicPopover>
|
|
25
|
+
</button>
|
|
26
26
|
</template>
|
|
27
27
|
|
|
28
28
|
<script setup lang="ts">
|
|
@@ -31,14 +31,44 @@ import { computed, getCurrentInstance } from 'vue';
|
|
|
31
31
|
import HelpCircleIcon from "vue-material-design-icons/HelpCircle.vue";
|
|
32
32
|
import ClassicPopover from './ClassicPopover.vue';
|
|
33
33
|
|
|
34
|
+
const { colored, small } = defineProps<{
|
|
35
|
+
colored?: boolean;
|
|
36
|
+
/** Make the icon smaller if true *(default: false)* */
|
|
37
|
+
small?: boolean;
|
|
38
|
+
}>();
|
|
39
|
+
|
|
40
|
+
/** The color to use for the icon */
|
|
41
|
+
const color = computed(() => {
|
|
42
|
+
if (colored) {
|
|
43
|
+
return "var(--octopus-primary-darker)";
|
|
44
|
+
} else {
|
|
45
|
+
return "black";
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
34
49
|
/** The internal ID for the elements */
|
|
35
50
|
const computedId = computed(() => {
|
|
36
|
-
|
|
51
|
+
return 'popover-' + getCurrentInstance()?.uid;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/** The size of the icon, according to the props */
|
|
55
|
+
const iconSize = computed(() => {
|
|
56
|
+
if (small) {
|
|
57
|
+
return 24;
|
|
58
|
+
} else {
|
|
59
|
+
return 30;
|
|
60
|
+
}
|
|
37
61
|
});
|
|
38
62
|
</script>
|
|
39
63
|
|
|
40
64
|
<style lang="scss" scoped>
|
|
41
|
-
.
|
|
42
|
-
|
|
65
|
+
.content {
|
|
66
|
+
font: revert;
|
|
67
|
+
font-size: 18px !important;
|
|
68
|
+
text-align: start;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.help-popover {
|
|
72
|
+
background-color: var(--octopus-secondary-lighter);
|
|
43
73
|
}
|
|
44
74
|
</style>
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
isFixed && isTopLayerPopover ? 'position-fixed':'position-absolute',
|
|
13
13
|
popoverClass]"
|
|
14
14
|
:style="positionInlineStyle"
|
|
15
|
-
@focusout="clearDataBlur"
|
|
16
15
|
@mouseenter="overPopover = true"
|
|
17
16
|
@mouseleave="
|
|
18
17
|
overPopover = false;
|
|
@@ -139,8 +138,8 @@ function removeListeners() {
|
|
|
139
138
|
targetElement.value.removeEventListener("focusout", clearDataBlur);
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
|
-
function handleClickEvent(){
|
|
143
|
-
if (show.value && isClick.value) {
|
|
141
|
+
function handleClickEvent(e: MouseEvent | PointerEvent){
|
|
142
|
+
if (show.value && isClick.value && e.target !== popoverRef.value && !popoverRef.value?.contains(e.target)) {
|
|
144
143
|
isClick.value = false;
|
|
145
144
|
clearData();
|
|
146
145
|
return -1;
|
|
@@ -180,7 +179,7 @@ function setPopoverData(e: MouseEvent | PointerEvent) {
|
|
|
180
179
|
if (props.disable || !e || !e.target) {
|
|
181
180
|
return;
|
|
182
181
|
}
|
|
183
|
-
if ("click" === e.type && -1 === handleClickEvent()) {
|
|
182
|
+
if ("click" === e.type && -1 === handleClickEvent(e)) {
|
|
184
183
|
return;
|
|
185
184
|
}
|
|
186
185
|
show.value = true;
|
|
@@ -235,6 +234,7 @@ function setPopoverData(e: MouseEvent | PointerEvent) {
|
|
|
235
234
|
maxHeight.value = '80dvh';
|
|
236
235
|
}
|
|
237
236
|
}
|
|
237
|
+
|
|
238
238
|
function clearDataBlur(e: FocusEvent) {
|
|
239
239
|
if (isTabAction.value) {
|
|
240
240
|
isTabAction.value = false;
|
|
@@ -245,14 +245,18 @@ function clearDataBlur(e: FocusEvent) {
|
|
|
245
245
|
if (-1!==result) {
|
|
246
246
|
return;
|
|
247
247
|
}
|
|
248
|
+
|
|
249
|
+
const parent = popoverRef?.value as HTMLElement;
|
|
248
250
|
if (!e.relatedTarget) {
|
|
251
|
+
if (parent !== null && parent.contains(e.target)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
249
254
|
return clearClick();
|
|
250
255
|
}
|
|
251
256
|
const myElement = e.relatedTarget as HTMLElement;
|
|
252
257
|
if (popoverId.value === myElement.id) {
|
|
253
258
|
return;
|
|
254
259
|
}
|
|
255
|
-
const parent =popoverRef?.value as HTMLElement;
|
|
256
260
|
if (null === parent || !parent.contains(myElement)) {
|
|
257
261
|
return clearClick();
|
|
258
262
|
}
|
|
@@ -301,9 +305,8 @@ defineExpose({
|
|
|
301
305
|
clearClick
|
|
302
306
|
});
|
|
303
307
|
</script>
|
|
304
|
-
<style lang="scss">
|
|
305
|
-
|
|
306
308
|
|
|
309
|
+
<style lang="scss">
|
|
307
310
|
.octopus-popover {
|
|
308
311
|
background: var(--octopus-background);
|
|
309
312
|
border-radius: var(--octopus-border-radius);
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
:organisation-id="authOrgaId"
|
|
28
28
|
/>
|
|
29
29
|
<CommentSection :podcast="podcast" />
|
|
30
|
+
|
|
31
|
+
<!-- Suggestions -->
|
|
30
32
|
<PodcastInlineList
|
|
31
33
|
:emission-id="podcast.emission.emissionId"
|
|
32
34
|
:href="'/main/pub/emission/' + podcast.emission.emissionId"
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
<PodcastInlineList
|
|
40
42
|
class="mt-4"
|
|
41
43
|
title-tag="h3"
|
|
44
|
+
:organisation-id="[podcast.organisation.id]"
|
|
42
45
|
:podcast-id="podcastId"
|
|
43
46
|
:title="t('Suggested listening')"
|
|
44
47
|
/>
|
|
@@ -51,6 +54,7 @@
|
|
|
51
54
|
:href="'/main/pub/category/' + c.id"
|
|
52
55
|
:title="t('More episodes of this category : ', { name: c.name })"
|
|
53
56
|
:button-text="t('All podcast button', { name: c.name })"
|
|
57
|
+
:organisation-id="[podcast.organisation.id]"
|
|
54
58
|
/>
|
|
55
59
|
</ClassicLazy>
|
|
56
60
|
</section>
|
package/tsconfig.json
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
// "strict": true,
|
|
6
|
+
"jsx": "preserve",
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"allowSyntheticDefaultImports": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"types": [
|
|
16
|
+
"webpack-env", "sockjs-client"
|
|
17
|
+
],
|
|
18
|
+
"paths": {
|
|
19
|
+
"@/*": [
|
|
20
|
+
"./src/*"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"lib": [
|
|
24
|
+
"esnext",
|
|
25
|
+
"dom",
|
|
26
|
+
"dom.iterable",
|
|
27
|
+
"scripthost"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"include": [
|
|
31
|
+
"src/**/*.ts",
|
|
32
|
+
"src/**/*.tsx",
|
|
33
|
+
"src/**/*.vue",
|
|
34
|
+
"tests/**/*.ts",
|
|
35
|
+
"tests/**/*.tsx"
|
|
17
36
|
],
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"./node_modules/@types/"
|
|
37
|
+
"exclude": [
|
|
38
|
+
"node_modules"
|
|
21
39
|
],
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"src/*"
|
|
25
|
-
]
|
|
26
|
-
},
|
|
27
|
-
"lib": [
|
|
28
|
-
"esnext",
|
|
29
|
-
"dom",
|
|
30
|
-
"dom.iterable",
|
|
31
|
-
"scripthost"
|
|
40
|
+
"files": [
|
|
41
|
+
"src/shims-vue.d.ts"
|
|
32
42
|
]
|
|
33
|
-
},
|
|
34
|
-
"include": [
|
|
35
|
-
"src/**/*.ts",
|
|
36
|
-
"src/**/*.tsx",
|
|
37
|
-
"src/**/*.vue",
|
|
38
|
-
"tests/**/*.ts",
|
|
39
|
-
"tests/**/*.tsx"
|
|
40
|
-
],
|
|
41
|
-
"exclude": [
|
|
42
|
-
"node_modules"
|
|
43
|
-
],
|
|
44
|
-
"files": [
|
|
45
|
-
"src/shims-vue.d.ts"
|
|
46
|
-
]
|
|
47
43
|
}
|
package/typings/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare module 'sockjs-client/dist/sockjs';
|