@saooti/octopus-sdk 41.1.15 → 41.2.1

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/index.ts +29 -2
  3. package/package.json +1 -1
  4. package/src/api/groupsApi.ts +214 -0
  5. package/src/api/podcastApi.ts +47 -9
  6. package/src/components/buttons/ActionButton.vue +99 -0
  7. package/src/components/buttons/index.ts +5 -0
  8. package/src/components/composable/route/types.ts +11 -3
  9. package/src/components/composable/route/useAdvancedParamInit.ts +40 -13
  10. package/src/components/composable/useErrorHandler.ts +3 -2
  11. package/src/components/composable/useNotifications.ts +50 -0
  12. package/src/components/display/emission/EmissionGroupChooser.vue +56 -0
  13. package/src/components/display/emission/EmissionList.vue +8 -2
  14. package/src/components/display/filter/AdvancedSearch.vue +83 -23
  15. package/src/components/display/list/ListPaginate.vue +4 -1
  16. package/src/components/display/podcasts/PodcastList.vue +12 -5
  17. package/src/components/display/podcasts/PodcastPlayButton.vue +2 -2
  18. package/src/components/display/podcasts/TagList.vue +4 -1
  19. package/src/components/form/ClassicMultiselect.vue +43 -37
  20. package/src/components/icons.ts +13 -0
  21. package/src/components/misc/ClassicAlert.vue +8 -1
  22. package/src/components/misc/ClassicBigChip.vue +84 -0
  23. package/src/components/misc/ClassicDataTable.vue +98 -0
  24. package/src/components/misc/ClassicDataTable_Internal.vue +132 -0
  25. package/src/components/misc/ClassicHelpButton.vue +3 -3
  26. package/src/components/misc/ClassicNotifications.vue +23 -0
  27. package/src/components/misc/ClassicPopover.vue +1 -0
  28. package/src/components/pages/EmissionPage.vue +2 -2
  29. package/src/components/pages/EmissionsPage.vue +10 -15
  30. package/src/components/pages/PodcastPage.vue +1 -1
  31. package/src/components/pages/PodcastsPage.vue +8 -20
  32. package/src/helper/fetchHelper.ts +1 -0
  33. package/src/locale/de.ts +2 -0
  34. package/src/locale/en.ts +2 -0
  35. package/src/locale/es.ts +2 -0
  36. package/src/locale/fr.ts +5 -3
  37. package/src/locale/it.ts +2 -0
  38. package/src/locale/sl.ts +2 -0
  39. package/src/router/router.ts +14 -288
  40. package/src/router/routes.ts +236 -0
  41. package/src/router/utils.ts +43 -2
  42. package/src/stores/class/general/emission.ts +8 -2
  43. package/src/style/_variables.scss +3 -0
  44. package/src/style/bootstrap.scss +5 -0
@@ -12,6 +12,7 @@ export const useErrorHandler = ()=>{
12
12
  if (undefined === authStore.authOrgaId) {
13
13
  window.location.href = window.location.origin + "/sso/login";
14
14
  } else {
15
+ console.error(error);
15
16
  router.push({
16
17
  path: "/main/pub/error",
17
18
  });
@@ -19,7 +20,7 @@ export const useErrorHandler = ()=>{
19
20
  }
20
21
  }
21
22
 
22
- return {
23
+ return {
23
24
  handle403
24
- }
25
+ }
25
26
  }
@@ -0,0 +1,50 @@
1
+ import { ref } from "vue";
2
+
3
+ type NotificationType = 'success'|'info'|'warning'|'error';
4
+
5
+ /**
6
+ * Type for notifications
7
+ */
8
+ interface Notification {
9
+ /** Optional title of the notification */
10
+ title?: string;
11
+ /** Message displayed by the notification */
12
+ message: string;
13
+ /** Type of notification, may affect display */
14
+ type: NotificationType;
15
+ /** When set to false, disallow manual closing of modal notification (default: true) */
16
+ closeable?: boolean;
17
+ }
18
+
19
+ /** The notification currently displayed */
20
+ const notification = ref<Notification|null>(null);
21
+
22
+ /**
23
+ * Composable used to manage notifications
24
+ */
25
+ export const useNotifications = () => {
26
+ /**
27
+ * Add & display a notification
28
+ * @param newNotif The data of the new notification
29
+ */
30
+ function addNotification(newNotif: Notification): void {
31
+ notification.value = { ...newNotif };
32
+ }
33
+
34
+ /**
35
+ * Remove all notifications
36
+ */
37
+ function clearNotifications(): void {
38
+ notification.value = null;
39
+ }
40
+
41
+ return {
42
+ /** The currently active notification */
43
+ notification,
44
+
45
+ /** Add a new notification to be displayed */
46
+ addNotification,
47
+ /** Remove all active notifications */
48
+ clearNotifications
49
+ }
50
+ };
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <ClassicMultiselect
3
+ id="group-chooser"
4
+ ref="selectGroup"
5
+ option-label="name"
6
+ :placeholder="$t('Search - Emission groups placeholder')"
7
+ :max-element="maxElement"
8
+ width="400px"
9
+ in-modal
10
+ :option-chosen="groups"
11
+ multiple
12
+ @on-search="onSearch"
13
+ @selected="emitSelected"
14
+ />
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { useTemplateRef } from "vue";
19
+
20
+ import ClassicMultiselect from "../../form/ClassicMultiselect.vue";
21
+ import { groupsApi, EmissionGroup } from "../../../api/groupsApi";
22
+
23
+ //Props
24
+ const props = defineProps<{
25
+ /** Filter by organisation */
26
+ organisationId?: string|Array<string>;
27
+ /** Currently selected groups */
28
+ groups: Array<EmissionGroup>;
29
+ }>();
30
+
31
+ //Emits
32
+ const emit = defineEmits(["update:groups"]);
33
+
34
+ //Data
35
+ const maxElement = 50;
36
+ const selectGroupRef = useTemplateRef('selectGroup');
37
+
38
+ //Methods
39
+ async function onSearch(query?: string): Promise<void> {
40
+ const response = await groupsApi.search({
41
+ first: 0,
42
+ size: maxElement,
43
+ search: query,
44
+ organisationIds: [props.organisationId].flat(),
45
+ });
46
+
47
+ selectGroupRef.value!.afterSearch(
48
+ response.result.filter(g => g.emissionIds?.length ?? 0 > 0),
49
+ response.count
50
+ );
51
+ }
52
+
53
+ function emitSelected(option: Array<EmissionGroup>) {
54
+ emit("update:groups", option);
55
+ }
56
+ </script>
@@ -71,6 +71,7 @@ import { Rubriquage } from "@/stores/class/rubrique/rubriquage";
71
71
  import { useFilterStore } from "../../../stores/FilterStore";
72
72
  import { ListClassicReturn } from "@/stores/class/general/listReturn";
73
73
  import { useI18n } from "vue-i18n";
74
+ import { EmissionGroup } from "../../../api/groupsApi";
74
75
  const EmissionItem = defineAsyncComponent(() => import("./EmissionItem.vue"));
75
76
  const EmissionPlayerItem = defineAsyncComponent(
76
77
  () => import("./EmissionPlayerItem.vue"),
@@ -94,7 +95,9 @@ const props = defineProps({
94
95
  noRubriquageId: { default: () => [], type: Array as () => Array<number> },
95
96
  nbPodcasts: { default: undefined, type: Number },
96
97
  /** The beneficiaries to filter on */
97
- beneficiaries: { default: null, type: Array as () => Array<string> }
98
+ beneficiaries: { default: null, type: Array as () => Array<string> },
99
+ /** The emission groups to filter on */
100
+ emissionGroups: { default: null, type: Array as () => Array<EmissionGroup> }
98
101
  })
99
102
 
100
103
  //Data
@@ -128,7 +131,8 @@ const changePaginate = computed(() => `${props.first}|${props.size}`);
128
131
  const changed = computed(() => {
129
132
  return `${props.organisationId}|${props.query}|${props.monetisable}|${props.includeHidden}|\
130
133
  ${props.iabId}|${props.rubriqueId}|${props.rubriquageId}|${props.before}|\
131
- ${props.after}|${props.sort}|${props.noRubriquageId}|${props.beneficiaries}`;
134
+ ${props.after}|${props.sort}|${props.noRubriquageId}|${props.beneficiaries}|\
135
+ ${props.emissionGroups}`;
132
136
  });
133
137
  const sortText = computed(() => {
134
138
  let textSort = "";
@@ -204,6 +208,8 @@ async function fetchContent(reset: boolean): Promise<void> {
204
208
  param.visible = 'VISIBLE';
205
209
  }
206
210
 
211
+ // TODO use emissionGroups
212
+
207
213
  try {
208
214
  const data = await classicApi.fetchData<ListClassicReturn<Emission>>({
209
215
  api: 0,
@@ -32,6 +32,24 @@
32
32
  @update:rubrique-filter="updateRubriquageFilter"
33
33
  />
34
34
 
35
+ <!-- Group filters -->
36
+ <div v-if="!isEmission && showEmissionGroups" class="mt-3 d-flex">
37
+ <ClassicCheckbox
38
+ v-model:text-init="emissionGroupCheckbox"
39
+ class="flex-shrink-0"
40
+ id-checkbox="search-emission-groups-checkbox"
41
+ :label="t('Filters - Emission groups')"
42
+ />
43
+
44
+ <EmissionGroupChooser
45
+ v-if="emissionGroupCheckbox"
46
+ class="ms-4 flex-grow-1"
47
+ :organisation-id="organisationId"
48
+ :groups="emissionGroups"
49
+ @update:groups="updateEmissionGroupFilter"
50
+ />
51
+ </div>
52
+
35
53
  <!-- Date -->
36
54
  <DateFilter
37
55
  :is-emission="isEmission"
@@ -117,7 +135,7 @@ import { useAuthStore } from "../../../stores/AuthStore";
117
135
  import { useFilterStore } from "../../../stores/FilterStore";
118
136
  import { useRubriquesFilterParam } from "../../composable/route/useRubriquesFilterParam";
119
137
  import { RubriquageFilter } from "@/stores/class/rubrique/rubriquageFilter";
120
- import { defineAsyncComponent, ref, computed, watch } from "vue";
138
+ import { defineAsyncComponent, ref, computed, watch, onMounted } from "vue";
121
139
  import { useGeneralStore } from "../../../stores/GeneralStore";
122
140
  import { useI18n } from "vue-i18n";
123
141
  const MonetizableFilter = defineAsyncComponent(
@@ -141,27 +159,34 @@ const ClassicCheckbox = defineAsyncComponent(
141
159
  const DateFilter = defineAsyncComponent(() => import("./DateFilter.vue"));
142
160
  const SearchOrder = defineAsyncComponent(() => import("./SearchOrder.vue"));
143
161
  import { ROUTE_PARAMS } from '../../composable/route/types';
162
+ import { EmissionGroup, groupsApi } from "../../../api/groupsApi";
163
+ import EmissionGroupChooser from "../emission/EmissionGroupChooser.vue";
144
164
 
145
165
  //Props
146
- const props = defineProps({
147
- organisationId: { default: undefined, type: String },
148
- isEmission: { default: false, type: Boolean },
149
- includeHidden: { default: false, type: Boolean },
150
- sort: { default: "DATE", type: String },
151
- onlyVideo: { default: false, type: Boolean },
152
- monetisable: { default: "UNDEFINED", type: String },
153
- iabId: { default: undefined, type: Number },
154
- searchPattern: { default: "", type: String },
155
- fromDate: { default: undefined, type: String },
156
- toDate: { default: undefined, type: String },
157
- validity: { default: 'true', type: String },
166
+ const props = withDefaults(defineProps<{
167
+ organisationId?: string;
168
+ /** Indicates that the filters apply to emissions */
169
+ isEmission?: boolean;
170
+ includeHidden?: boolean;
171
+ sort?: string;
172
+ onlyVideo?: boolean;
173
+ monetisable?: string;
174
+ iabId?: number;
175
+ searchPattern?: string;
176
+ fromDate?: string;
177
+ toDate?: string;
178
+ validity?: string;
158
179
  /** The filter on beneficiaries */
159
- beneficiaries: { default: null, type: Array as () => Array<string> },
160
- rubriqueFilter: {
161
- default: () => [],
162
- type: Array as () => Array<RubriquageFilter>,
163
- },
164
- })
180
+ beneficiaries?: Array<string>;
181
+ rubriqueFilter?: Array<RubriquageFilter>;
182
+ /** The filter on groups */
183
+ emissionGroups?: Array<EmissionGroup>;
184
+ }>(), {
185
+ sort: "DATE",
186
+ monetisable: "UNDEFINED",
187
+ searchPattern: "",
188
+ validity: "true"
189
+ });
165
190
 
166
191
  //Emits
167
192
  const emit = defineEmits([
@@ -174,13 +199,14 @@ const emit = defineEmits([
174
199
  "update:validity",
175
200
  "update:rubriqueFilter",
176
201
  "update:onlyVideo",
177
- "update:beneficiaries"
202
+ "update:beneficiaries",
203
+ "update:emission-groups"
178
204
  ]);
179
205
 
180
206
  //Data
181
207
  const showFilters = ref(false);
182
208
  const firstLoaded = ref(false);
183
-
209
+ const showEmissionGroups = ref(false);
184
210
 
185
211
  //Composables
186
212
  const { t } = useI18n();
@@ -190,6 +216,13 @@ const generalStore = useGeneralStore();
190
216
  const filterStore = useFilterStore();
191
217
  const authStore = useAuthStore();
192
218
 
219
+ onMounted(async() => {
220
+ // Only show emission groups if there are some
221
+ const nbGroups = await groupsApi.count({
222
+ organisationIds: [props.organisationId]
223
+ });
224
+ showEmissionGroups.value = nbGroups > 0;
225
+ });
193
226
 
194
227
  //Computed
195
228
  const organisationRight = computed(() => isEditRights(props.organisationId));
@@ -205,6 +238,7 @@ const isSelectValidity = computed(() => {
205
238
  props.includeHidden
206
239
  );
207
240
  });
241
+
208
242
  /** The beneficiaries filter is only displayed if beneficiaries are enabled */
209
243
  const beneficiariesEnabled = computed(() => {
210
244
  return authStore.authOrganisation.attributes['beneficiaries.enabled'] === 'true';
@@ -288,11 +322,37 @@ function updateRubriquageFilter(value: Array<RubriquageFilter>) {
288
322
  filterRubriques = { rubriquesId: undefined };
289
323
  }
290
324
  updateRouteParamAdvanced({
291
- ...{ r: valueString.length ? valueString : undefined },
325
+ r: valueString.length ? valueString : undefined,
292
326
  ...filterRubriques,
293
327
  });
294
328
  }
295
329
 
330
+ /** Manage the checkbox enabling filtering on groups */
331
+ const emissionGroupCheckbox = computed({
332
+ get(): boolean {
333
+ return props.emissionGroups !== undefined && props.emissionGroups !== null;
334
+ },
335
+ set(value: boolean): void {
336
+ if (value) {
337
+ updateEmissionGroupFilter([]);
338
+ } else {
339
+ updateEmissionGroupFilter(undefined);
340
+ }
341
+ }
342
+ });
343
+
344
+ /** Update selected groups */
345
+ function updateEmissionGroupFilter(groups: Array<EmissionGroup>|undefined): void {
346
+ if (groups !== undefined && groups.length === 0) {
347
+ emit('update:emission-groups', []);
348
+ } else {
349
+ updateRouteParamAdvanced({
350
+ [ROUTE_PARAMS.EmissionGroups]: groups?.map(g => g.groupId)
351
+ });
352
+ emit('update:emission-groups', groups);
353
+ }
354
+ }
355
+
296
356
  /** Update the beneficiaries filter */
297
357
  function updateBeneficiaries(value: string[]|undefined): void {
298
358
  emit('update:beneficiaries', value);
@@ -350,4 +410,4 @@ function clickShowFilters(): void {
350
410
  overflow: hidden;
351
411
  }
352
412
  }
353
- </style>
413
+ </style>
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <div :id="id" class="d-flex flex-column align-items-center">
3
- <ClassicLoading :loading-text="loadingText" :error-text="errorText" />
3
+ <ClassicLoading
4
+ v-if="loading || errorText"
5
+ :loading-text="loadingText" :error-text="errorText"
6
+ />
4
7
  <template v-if="!loading">
5
8
  <div
6
9
  v-if="!justSizeChosen"
@@ -56,17 +56,15 @@
56
56
  <script setup lang="ts">
57
57
  import ListPaginate from "../list/ListPaginate.vue";
58
58
  import {useErrorHandler} from "../../composable/useErrorHandler";
59
- import classicApi from "../../../api/classicApi";
60
59
  import PodcastItem from "./PodcastItem.vue";
61
60
  import ClassicLazy from "../../misc/ClassicLazy.vue";
62
61
  import { useFilterStore } from "../../../stores/FilterStore";
63
62
  import { Podcast, PodcastProcessingStatus, emptyPodcastData } from "../../../stores/class/general/podcast";
64
63
  import { computed, onBeforeMount, Ref, ref, watch } from "vue";
65
- import { FetchParam } from "@/stores/class/general/fetchParam";
66
64
  import { AxiosError } from "axios";
67
- import { ListClassicReturn } from "../../../stores/class/general/listReturn";
68
65
  import { useI18n } from "vue-i18n";
69
66
  import { podcastApi, PodcastMonetisation, PodcastSearchOptions, PodcastSort } from "../../../api/podcastApi";
67
+ import { EmissionGroup } from "@/api/groupsApi";
70
68
 
71
69
  //Props
72
70
  const props = withDefaults(defineProps<{
@@ -96,6 +94,8 @@ const props = withDefaults(defineProps<{
96
94
  forceUpdateParameters?: boolean;
97
95
  /** The beneficiaries to filter on */
98
96
  beneficiaries?: Array<string>;
97
+ /** The emission groups to filter on */
98
+ emissionGroups?: Array<EmissionGroup>;
99
99
  }>(), {
100
100
  first: 0,
101
101
  size: 30,
@@ -106,6 +106,7 @@ const props = withDefaults(defineProps<{
106
106
  displaySortText: true,
107
107
  validity: true,
108
108
  justSizeChosen: false,
109
+ withVideo: undefined,
109
110
  forceUpdateParameters: false
110
111
  });
111
112
 
@@ -140,7 +141,7 @@ const changed = computed(() => {
140
141
  return `${organisation.value}|${props.emissionId}|${props.sortCriteria}|${sort.value}
141
142
  ${props.iabId}|${props.participantId}|${props.query}|${props.monetisable}|${props.popularSort}|
142
143
  ${props.rubriqueId}|${props.rubriquageId}|${props.before}|${props.after}|${props.includeHidden}|${props.noRubriquageId}|${props.validity}|
143
- ${props.withVideo}|${props.includeTag}|${props.beneficiaries}`;
144
+ ${props.withVideo}|${props.includeTag}|${props.beneficiaries}|${props.emissionGroups}`;
144
145
  });
145
146
  const organisation = computed(() => {
146
147
  if (props.organisationId) {
@@ -200,6 +201,7 @@ async function fetchContent(reset: boolean): Promise<void> {
200
201
  pageSize: dsize.value,
201
202
  organisationId: organisation.value,
202
203
  emissionId: props.emissionId,
204
+ emissionGroups: props.emissionGroups,
203
205
  iabId: props.iabId,
204
206
  participantId: props.participantId,
205
207
  query: props.query,
@@ -223,11 +225,16 @@ async function fetchContent(reset: boolean): Promise<void> {
223
225
  tags: props.includeTag?.length ? props.includeTag : undefined,
224
226
  beneficiaries: props.beneficiaries ?? undefined
225
227
  };
228
+
226
229
  try {
227
230
  const data = await podcastApi.searchFull(param, true);
228
231
  afterFetching(reset, data);
229
232
  } catch (error) {
230
- handle403(error as AxiosError);
233
+ if (error instanceof AxiosError) {
234
+ handle403(error as AxiosError);
235
+ } else {
236
+ console.error('error', error);
237
+ }
231
238
  }
232
239
  }
233
240
  function afterFetching(
@@ -47,11 +47,11 @@
47
47
  @mouseenter="hoverType = 'video'"
48
48
  @mouseleave="hoverType = ''"
49
49
  >
50
+ <PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
50
51
  <PlayVideoIcon
51
- v-if="!playerStore.playerVideo"
52
+ v-else
52
53
  :size="'video' === hoverType ? 50 : 40"
53
54
  />
54
- <PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
55
55
  <time
56
56
  class="ms-2 me-2"
57
57
  :datetime="durationIso"
@@ -74,9 +74,12 @@ const filterStore = useFilterStore();
74
74
 
75
75
  //Computed
76
76
  const tagListFiltered = computed(() => {
77
- return props.tagList.filter((tag: string) => {
77
+ const tags = props.tagList.filter((tag: string) => {
78
78
  return !tag.match(/^\[\[.*\]\]$/);
79
79
  });
80
+
81
+ // Each tag is only displayed once
82
+ return tags.filter((tag, index) => tags.indexOf(tag) === index);
80
83
  });
81
84
  const organisationQuery = computed(() => {
82
85
  if(filterStore.filterOrgaId){
@@ -96,7 +96,7 @@
96
96
  </div>
97
97
  </template>
98
98
 
99
- <script setup lang="ts">
99
+ <script setup lang="ts" generic="T">
100
100
  import { computed, defineAsyncComponent, ref, Ref, watch } from "vue";
101
101
  import { useI18n } from "vue-i18n";
102
102
  import AsteriskIcon from "vue-material-design-icons/Asterisk.vue";
@@ -108,37 +108,43 @@ const ClassicPopover = defineAsyncComponent(
108
108
  );
109
109
 
110
110
  //Props
111
- const props = defineProps({
112
- id: { default: "", type: String },
113
- label: { default: "", type: String },
114
- placeholder: { default: "", type: String },
115
- optionLabel: { default: "", type: String },
116
- inModal: { default: false, type: Boolean },
117
- multiple: { default: false, type: Boolean },
118
- isDisabled: { default: false, type: Boolean },
119
- width: { default: "100%", type: String },
120
- height: { default: undefined, type: String },
121
- maxElement: { default: 50, type: Number },
122
- minSearchLength: { default: 3, type: Number },
123
- optionChosen: { default: undefined, type: Object as () => unknown },
124
- noDeselect: { default: true, type: Boolean },
125
- optionCustomTemplating: { default: "", type: String },
126
- optionSelectedCustomTemplating: { default: "", type: String },
127
- displayLabel: { default: false, type: Boolean },
128
- maxOptions: { default: null, type: Number },
129
- allowEmpty: { default: true, type: Boolean },
130
- textDanger :{ default: undefined, type: String },
131
- displayRequired: { default: false, type: Boolean },
132
- popover: { default: undefined, type: String },
133
- popoverRelativeClass: { default: undefined, type: String },
134
- })
111
+ const {
112
+ inModal = false, multiple = false, isDisabled = false, width = "100%",
113
+ maxElement = 50, minSearchLength = 3, noDeselect = true, displayLabel = false,
114
+ allowEmpty = true, optionChosen, maxOptions = null,
115
+ optionCustomTemplating = '', optionSelectedCustomTemplating = ''
116
+ } = defineProps<{
117
+ id?: string;
118
+ label?: string;
119
+ placeholder?: string;
120
+ optionLabel?: string;
121
+ inModal?: boolean;
122
+ multiple?: boolean;
123
+ isDisabled?: boolean;
124
+ width?: string;
125
+ height?: string;
126
+ maxElement?: number;
127
+ minSearchLength?: number;
128
+ /** Currently chosen option */
129
+ optionChosen?: T|Array<T>;
130
+ noDeselect?: boolean;
131
+ optionCustomTemplating?: string;
132
+ optionSelectedCustomTemplating?: string;
133
+ displayLabel?: boolean;
134
+ maxOptions?: number;
135
+ allowEmpty?: boolean;
136
+ textDanger ?:string;
137
+ displayRequired?: boolean;
138
+ popover?: string;
139
+ popoverRelativeClass?: string;
140
+ }>();
135
141
 
136
142
  //Emits
137
143
  const emit = defineEmits(["onSearch", "selected", "onClose"]);
138
144
 
139
145
  //Data
140
- const optionSelected : Ref<unknown>= ref(undefined);
141
- const options : Ref<Array<unknown>>= ref([]);
146
+ const optionSelected : Ref<T|T[]>= ref(undefined);
147
+ const options : Ref<Array<T>>= ref([]);
142
148
  const remainingElements = ref(0);
143
149
  const isLoading = ref(false);
144
150
  const searchInput = ref("");
@@ -149,20 +155,20 @@ const { t } = useI18n();
149
155
 
150
156
  //Computed
151
157
  const maxOptionsSelected = computed(() => {
152
- if (props.maxOptions !== null && props.multiple) {
158
+ if (maxOptions !== null && multiple) {
153
159
  return (
154
- (optionSelected.value as Array<unknown>).length >= props.maxOptions
160
+ (optionSelected.value as Array<T>).length >= maxOptions
155
161
  );
156
162
  }
157
163
  return false;
158
164
  });
159
165
 
160
166
  //Watch
161
- watch(()=>props.optionChosen, () => {
162
- optionSelected.value = props.optionChosen;
167
+ watch(()=>optionChosen, () => {
168
+ optionSelected.value = optionChosen;
163
169
  }, {deep: true, immediate: true});
164
170
  watch(optionSelected, () => {
165
- if (props.noDeselect || null !== optionSelected.value) {
171
+ if (noDeselect || null !== optionSelected.value) {
166
172
  return;
167
173
  }
168
174
  emit("selected", undefined);
@@ -173,7 +179,7 @@ function fakeSearch(): Array<unknown> {
173
179
  return options.value;
174
180
  }
175
181
  function onSearch(search?: string): void {
176
- if (search && search.length < props.minSearchLength) {
182
+ if (search && search.length < minSearchLength) {
177
183
  return;
178
184
  } else if (search) {
179
185
  searchInput.value = search;
@@ -185,20 +191,20 @@ function onClose() {
185
191
  emit("onClose", searchInput.value);
186
192
  searchInput.value = "";
187
193
  }
188
- function afterSearch(optionsFetched: Array<unknown>, count: number): void {
194
+ function afterSearch(optionsFetched: Array<T>, count: number): void {
189
195
  options.value = optionsFetched;
190
- remainingElements.value = Math.max(0, count - props.maxElement);
196
+ remainingElements.value = Math.max(0, count - maxElement);
191
197
  isLoading.value = false;
192
198
  }
193
199
  function onOptionSelected(optionSelected: unknown): void {
194
200
  emit("selected", optionSelected);
195
201
  }
196
202
  function onOptionDeselect(event: unknown): void {
197
- if (!props.multiple) {
203
+ if (!multiple) {
198
204
  return;
199
205
  }
200
206
  if (
201
- !props.allowEmpty &&
207
+ !allowEmpty &&
202
208
  0 === (optionSelected.value as Array<unknown>).length
203
209
  ) {
204
210
  (optionSelected.value as Array<unknown>).push(event);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Library of icons to use in the application
3
+ * Icons should be defined here, then used with their alias.
4
+ * This will allow for better consistency, and easily changing icons.
5
+ */
6
+
7
+ import SquareEditOutline from 'vue-material-design-icons/SquareEditOutline.vue';
8
+ import TrashCan from 'vue-material-design-icons/TrashCan.vue';
9
+
10
+ export default {
11
+ Delete: TrashCan,
12
+ Edit: SquareEditOutline
13
+ }
@@ -82,6 +82,13 @@ const iconComponent = computed(() => {
82
82
  display: flex;
83
83
  flex-direction: column;
84
84
  align-self: center;
85
+
86
+ &:deep(p) {
87
+ margin: 0 !important;
88
+ &:not(:first-child) {
89
+ margin-top: 8px !important;
90
+ }
91
+ }
85
92
  }
86
93
 
87
94
  $types: (
@@ -103,4 +110,4 @@ const iconComponent = computed(() => {
103
110
  }
104
111
  }
105
112
  }
106
- </style>
113
+ </style>