sprintify-ui 0.0.32 → 0.0.34

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 (27) hide show
  1. package/dist/sprintify-ui.es.js +3616 -3402
  2. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +9 -9
  3. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +9 -9
  4. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +10 -10
  5. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +23 -23
  6. package/dist/types/src/components/BaseFilePicker.vue.d.ts +1 -1
  7. package/dist/types/src/components/BaseFileUploader.vue.d.ts +4 -4
  8. package/dist/types/src/components/BaseHasMany.vue.d.ts +0 -1
  9. package/dist/types/src/components/BaseRadioGroup.vue.d.ts +226 -0
  10. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +0 -1
  11. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +0 -1
  12. package/dist/types/src/components/index.d.ts +3 -1
  13. package/dist/types/src/composables/hasOptions.d.ts +7 -0
  14. package/dist/types/src/types/types.d.ts +0 -2
  15. package/package.json +1 -1
  16. package/src/components/BaseAutocomplete.vue +29 -40
  17. package/src/components/BaseAutocompleteFetch.vue +2 -2
  18. package/src/components/BaseBelongsTo.vue +3 -3
  19. package/src/components/BaseButtonGroup.stories.js +5 -8
  20. package/src/components/BaseButtonGroup.vue +24 -66
  21. package/src/components/BaseRadioGroup.stories.js +88 -0
  22. package/src/components/BaseRadioGroup.vue +84 -0
  23. package/src/components/BaseTagAutocomplete.stories.js +4 -6
  24. package/src/components/BaseTagAutocomplete.vue +35 -53
  25. package/src/components/index.ts +4 -0
  26. package/src/composables/hasOptions.ts +68 -0
  27. package/src/types/types.ts +0 -4
@@ -82,7 +82,6 @@
82
82
  <slot
83
83
  name="option"
84
84
  :option="option.option"
85
- :selected="optionValues.includes(option.value)"
86
85
  :active="optionActive && optionActive.value == option.value"
87
86
  >
88
87
  <div
@@ -127,10 +126,11 @@
127
126
  <script lang="ts" setup>
128
127
  import { cloneDeep, get } from 'lodash';
129
128
  import { PropType, Ref, ComputedRef } from 'vue';
130
- import { NormalizedOption, Option, OptionValue } from '@/types/types';
129
+ import { NormalizedOption, Option } from '@/types/types';
131
130
  import { useInfiniteScroll, useMutationObserver } from '@vueuse/core';
132
131
  import { useNotificationsStore } from '@/stores/notifications';
133
132
  import BaseSkeleton from './BaseSkeleton.vue';
133
+ import { useHasOptions } from '@/composables/hasOptions';
134
134
 
135
135
  const props = defineProps({
136
136
  modelValue: {
@@ -194,9 +194,23 @@ const keywords = ref('');
194
194
  const showDropdown = ref(false);
195
195
  const inputElement = ref(null) as Ref<HTMLInputElement | null>;
196
196
  const dropdown = ref(null) as Ref<HTMLElement | null>;
197
- const selectionIndex = ref(0);
197
+ const activeIndex = ref(0);
198
198
  const selectionToDelete = ref(null) as Ref<NormalizedOption | null>;
199
199
 
200
+ const hasOptions = useHasOptions(
201
+ computed(() => props.modelValue),
202
+ computed(() => props.options),
203
+ computed(() => props.labelKey),
204
+ computed(() => props.valueKey),
205
+ computed(() => true)
206
+ );
207
+
208
+ const isSelected = hasOptions.isSelected;
209
+ const normalizedOptions = hasOptions.normalizedOptions;
210
+ const normalizedModelValue = hasOptions.normalizedModelValue as ComputedRef<
211
+ NormalizedOption[]
212
+ >;
213
+
200
214
  onMounted(() => {
201
215
  useInfiniteScroll(
202
216
  dropdown.value,
@@ -210,34 +224,11 @@ onMounted(() => {
210
224
  const optionActive = computed(() => {
211
225
  return (
212
226
  filteredNormalizedOptions.value[
213
- Math.min(selectionIndex.value, filteredNormalizedOptions.value.length - 1)
227
+ Math.min(activeIndex.value, filteredNormalizedOptions.value.length - 1)
214
228
  ] ?? null
215
229
  );
216
230
  });
217
231
 
218
- const normalizedModelValue = computed(() => {
219
- if (!props.modelValue) {
220
- return [];
221
- }
222
- return props.modelValue.map((o) => {
223
- return {
224
- label: o[props.labelKey] as string,
225
- value: o[props.valueKey] as string | number,
226
- option: o,
227
- };
228
- });
229
- }) as ComputedRef<NormalizedOption[]>;
230
-
231
- const normalizedOptions = computed(() => {
232
- return props.options.map((option) => {
233
- return {
234
- label: option[props.labelKey] as string,
235
- value: option[props.valueKey] as string | number,
236
- option: option,
237
- } as NormalizedOption;
238
- });
239
- });
240
-
241
232
  const filteredNormalizedOptions = computed((): NormalizedOption[] => {
242
233
  return normalizedOptions.value
243
234
  .filter((option) => {
@@ -250,20 +241,10 @@ const filteredNormalizedOptions = computed((): NormalizedOption[] => {
250
241
  return option.label.toLowerCase().includes(keywords.value.toLowerCase());
251
242
  })
252
243
  .filter((option) => {
253
- return !hasSelectedOption(option.value);
244
+ return !isSelected(option);
254
245
  });
255
246
  });
256
247
 
257
- const optionValues = computed(() => {
258
- return normalizedModelValue.value.map((o) => {
259
- return o.value;
260
- });
261
- });
262
-
263
- const hasSelectedOption = (value: OptionValue): boolean => {
264
- return optionValues.value.includes(value);
265
- };
266
-
267
248
  function preventUnfocus(elements: HTMLElement[]) {
268
249
  elements.forEach((e) => {
269
250
  e.removeEventListener('mousedown', dontLooseFocus);
@@ -294,7 +275,7 @@ const onTextBlur = () => {
294
275
  };
295
276
 
296
277
  const onTextInput = (event: Event) => {
297
- selectionIndex.value = 0;
278
+ activeIndex.value = 0;
298
279
  selectionToDelete.value = null;
299
280
  setKeywords(get(event, 'target.value', '') + '');
300
281
  dropdown.value?.scrollTo({
@@ -315,19 +296,22 @@ const onTextKeydown = (event: KeyboardEvent) => {
315
296
  }
316
297
 
317
298
  if (key === 'ArrowDown') {
318
- if (selectionIndex.value < props.options.length - 1) {
319
- selectionIndex.value++;
299
+ if (activeIndex.value < filteredNormalizedOptions.value.length - 1) {
300
+ activeIndex.value++;
320
301
  } else {
321
- selectionIndex.value = 0;
302
+ activeIndex.value = 0;
322
303
  }
323
304
  return;
324
305
  }
325
306
 
326
307
  if (key === 'ArrowUp') {
327
- if (selectionIndex.value > 0) {
328
- selectionIndex.value--;
308
+ if (activeIndex.value > 0) {
309
+ activeIndex.value--;
329
310
  } else {
330
- selectionIndex.value = Math.max(0, props.options.length - 1);
311
+ activeIndex.value = Math.max(
312
+ 0,
313
+ filteredNormalizedOptions.value.length - 1
314
+ );
331
315
  }
332
316
  return;
333
317
  }
@@ -343,9 +327,7 @@ const onTextKeydown = (event: KeyboardEvent) => {
343
327
 
344
328
  const optionClass = (option: NormalizedOption) => {
345
329
  const active = optionActive.value && optionActive.value.value == option.value;
346
- const selected =
347
- normalizedModelValue.value &&
348
- normalizedModelValue.value.map((o) => o.value).includes(option.value);
330
+ const selected = isSelected(option);
349
331
 
350
332
  if (selected) {
351
333
  if (active) {
@@ -389,7 +371,7 @@ const addOption = (option: NormalizedOption) => {
389
371
  return;
390
372
  }
391
373
 
392
- if (hasSelectedOption(option.value)) {
374
+ if (isSelected(option)) {
393
375
  return;
394
376
  }
395
377
 
@@ -440,19 +422,19 @@ const beforeAddOption = () => {
440
422
  };
441
423
 
442
424
  const afterUpdate = () => {
443
- validateSelectionIndex();
425
+ validateActiveIndex();
444
426
  };
445
427
 
446
428
  const clearInput = () => {
447
429
  setKeywords('');
448
430
  };
449
431
 
450
- const validateSelectionIndex = () => {
432
+ const validateActiveIndex = () => {
451
433
  // Wait for filteredOptions to update
452
434
  nextTick(() => {
453
- selectionIndex.value = Math.max(
435
+ activeIndex.value = Math.max(
454
436
  0,
455
- Math.min(selectionIndex.value, normalizedOptions.value.length - 1)
437
+ Math.min(activeIndex.value, filteredNormalizedOptions.value.length - 1)
456
438
  );
457
439
  });
458
440
  };
@@ -11,6 +11,7 @@ import BaseBelongsTo from './BaseBelongsTo.vue';
11
11
  import BaseBoolean from './BaseBoolean.vue';
12
12
  import BaseBreadcrumbs from './BaseBreadcrumbs.vue';
13
13
  import BaseButton from './BaseButton.vue';
14
+ import BaseButtonGroup from './BaseButtonGroup.vue';
14
15
  import BaseCard from './BaseCard.vue';
15
16
  import BaseCardRow from './BaseCardRow.vue';
16
17
  import BaseCharacterCounter from './BaseCharacterCounter.vue';
@@ -45,6 +46,7 @@ import BasePagination from './BasePagination.vue';
45
46
  import BasePanel from './BasePanel.vue';
46
47
  import BasePassword from './BasePassword.vue';
47
48
  import BaseProgressCircle from './BaseProgressCircle.vue';
49
+ import BaseRadioGroup from './BaseRadioGroup.vue';
48
50
  import BaseReadMore from './BaseReadMore.vue';
49
51
  import BaseSelect from './BaseSelect.vue';
50
52
  import BaseSideNavigation from './BaseSideNavigation.vue';
@@ -80,6 +82,7 @@ export {
80
82
  BaseBoolean,
81
83
  BaseBreadcrumbs,
82
84
  BaseButton,
85
+ BaseButtonGroup,
83
86
  BaseCard,
84
87
  BaseCardRow,
85
88
  BaseCharacterCounter,
@@ -114,6 +117,7 @@ export {
114
117
  BasePanel,
115
118
  BasePassword,
116
119
  BaseProgressCircle,
120
+ BaseRadioGroup,
117
121
  BaseReadMore,
118
122
  BaseSelect,
119
123
  BaseSideNavigation,
@@ -0,0 +1,68 @@
1
+ import { Ref } from 'vue';
2
+ import { NormalizedOption, Option } from '@/types/types';
3
+ import { isArray, isObject } from 'lodash';
4
+
5
+ export function useHasOptions(
6
+ modelValue: Ref<Option[] | Option | null | undefined>,
7
+ options: Ref<Option[]>,
8
+ labelKey: Ref<string>,
9
+ valueKey: Ref<string>,
10
+ multiple: Ref<boolean> = ref(false)
11
+ ) {
12
+ const normalizedModelValue = computed(
13
+ (): NormalizedOption[] | NormalizedOption | null => {
14
+ if (multiple.value) {
15
+ if (!isArray(modelValue.value)) {
16
+ return [];
17
+ }
18
+ return modelValue.value.map((option) => {
19
+ return {
20
+ label: option[labelKey.value] as string,
21
+ value: option[valueKey.value] as string | number,
22
+ option: option,
23
+ } as NormalizedOption;
24
+ });
25
+ } else {
26
+ if (!isObject(modelValue.value)) {
27
+ return null;
28
+ }
29
+
30
+ return {
31
+ label: modelValue.value[labelKey.value as never] as string,
32
+ value: modelValue.value[valueKey.value as never] as string | number,
33
+ option: modelValue.value,
34
+ } as NormalizedOption;
35
+ }
36
+ }
37
+ );
38
+
39
+ const normalizedOptions = computed((): NormalizedOption[] => {
40
+ return options.value.map((option) => {
41
+ return {
42
+ label: option[labelKey.value] as string,
43
+ value: option[valueKey.value] as string | number,
44
+ option: option,
45
+ } as NormalizedOption;
46
+ });
47
+ });
48
+
49
+ function isSelected(option: NormalizedOption): boolean {
50
+ if (isArray(normalizedModelValue.value)) {
51
+ return normalizedModelValue.value.some((modelValue) => {
52
+ return modelValue.value === option.value;
53
+ });
54
+ }
55
+
56
+ if (isObject(normalizedModelValue.value)) {
57
+ return normalizedModelValue.value.value == option.value;
58
+ }
59
+
60
+ return false;
61
+ }
62
+
63
+ return {
64
+ normalizedOptions,
65
+ normalizedModelValue,
66
+ isSelected,
67
+ };
68
+ }
@@ -21,16 +21,12 @@ export type OptionValue = string | number;
21
21
 
22
22
  export type Option = Record<string, any>;
23
23
 
24
- export type Selection = Option | null | undefined;
25
-
26
24
  export type NormalizedOption = {
27
25
  option: Option;
28
26
  value: OptionValue;
29
27
  label: string;
30
28
  };
31
29
 
32
- export type NormalizedSelection = NormalizedOption | null | undefined;
33
-
34
30
  export type MediaLibraryPayload = {
35
31
  to_remove: string[];
36
32
  to_add: UploadedFile[];