sprintify-ui 0.1.14 → 0.1.16

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 (66) hide show
  1. package/README.md +1 -26
  2. package/dist/sprintify-ui.es.js +11391 -11329
  3. package/dist/style.css +1 -1
  4. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +1 -1
  5. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +2 -2
  6. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +1 -1
  7. package/dist/types/src/components/BaseCounter.vue.d.ts +4 -3
  8. package/dist/types/src/components/BaseDatePicker.vue.d.ts +1 -1
  9. package/dist/types/src/components/BaseFileUploader.vue.d.ts +1 -1
  10. package/dist/types/src/components/BaseHeader.vue.d.ts +6 -1
  11. package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +1 -1
  12. package/dist/types/src/components/BaseMediaListItem.vue.d.ts +1 -1
  13. package/dist/types/src/components/BaseMenuItem.vue.d.ts +4 -3
  14. package/dist/types/src/components/BaseNumber.vue.d.ts +3 -3
  15. package/dist/types/src/components/BaseSwitch.vue.d.ts +0 -27
  16. package/dist/types/src/components/BaseTableColumn.vue.d.ts +1 -1
  17. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +1 -1
  18. package/dist/types/src/composables/mediaQuery.d.ts +2 -2
  19. package/dist/types/src/i18n/index.d.ts +1 -0
  20. package/dist/types/src/index.d.ts +0 -178
  21. package/dist/types/src/stores/i18n.d.ts +5 -0
  22. package/dist/types/src/types/index.d.ts +3 -2
  23. package/package.json +7 -6
  24. package/src/components/BaseActionItemButton.vue +2 -0
  25. package/src/components/BaseAddressForm.vue +8 -7
  26. package/src/components/BaseAutocomplete.vue +3 -3
  27. package/src/components/BaseAutocompleteDrawer.vue +2 -1
  28. package/src/components/BaseAutocompleteFetch.vue +2 -1
  29. package/src/components/BaseCharacterCounter.vue +2 -1
  30. package/src/components/BaseClipboard.vue +4 -2
  31. package/src/components/BaseCounter.stories.js +1 -1
  32. package/src/components/BaseCounter.vue +5 -3
  33. package/src/components/BaseCropper.vue +2 -5
  34. package/src/components/BaseCropperModal.vue +3 -2
  35. package/src/components/BaseDataIterator.vue +6 -7
  36. package/src/components/BaseDataTable.vue +14 -14
  37. package/src/components/BaseDatePicker.vue +4 -4
  38. package/src/components/BaseDateSelect.vue +10 -10
  39. package/src/components/BaseDialog.vue +5 -6
  40. package/src/components/BaseDisplayRelativeTime.vue +6 -6
  41. package/src/components/BaseFilePicker.vue +7 -8
  42. package/src/components/BaseFileUploader.vue +3 -3
  43. package/src/components/BaseForm.vue +3 -3
  44. package/src/components/BaseHeader.stories.js +12 -5
  45. package/src/components/BaseHeader.vue +73 -74
  46. package/src/components/BaseIconPicker.vue +2 -1
  47. package/src/components/BaseLayoutNotificationDropdown.vue +3 -2
  48. package/src/components/BaseMediaLibrary.vue +8 -11
  49. package/src/components/BaseMediaListItem.vue +4 -3
  50. package/src/components/BaseMenuItem.vue +4 -3
  51. package/src/components/BaseNumber.vue +25 -5
  52. package/src/components/BasePagination.vue +4 -3
  53. package/src/components/BaseReadMore.vue +2 -1
  54. package/src/components/BaseSelect.vue +3 -2
  55. package/src/components/BaseSwitch.stories.js +5 -2
  56. package/src/components/BaseSwitch.vue +9 -36
  57. package/src/components/BaseTagAutocomplete.vue +4 -4
  58. package/src/components/BaseTagAutocompleteFetch.vue +3 -2
  59. package/src/composables/mediaQuery.ts +2 -2
  60. package/src/i18n/index.ts +60 -0
  61. package/src/index.ts +11 -11
  62. package/src/stores/dialogs.ts +3 -3
  63. package/src/stores/i18n.ts +14 -0
  64. package/src/types/index.ts +11 -2
  65. package/src/utils/fileSizeFormat.ts +6 -6
  66. package/src/utils/toHumanList.ts +2 -2
@@ -19,7 +19,7 @@
19
19
  <input
20
20
  v-model="keywords"
21
21
  type="text"
22
- :placeholder="$t('sui.search') + '...'"
22
+ :placeholder="t('sui.search') + '...'"
23
23
  class="min-w-[40px] grow rounded-r border-slate-300 py-0 px-2 focus:border-blue-500 focus:ring-2 focus:ring-blue-300 sm:text-sm"
24
24
  />
25
25
  </div>
@@ -48,6 +48,7 @@
48
48
 
49
49
  <script lang="ts" setup>
50
50
  import { Icon as BaseIcon } from '@iconify/vue';
51
+ import { t } from '@/i18n';
51
52
 
52
53
  const props = withDefaults(
53
54
  defineProps<{
@@ -41,7 +41,7 @@
41
41
  class="mx-auto mb-2 h-8 w-8 text-slate-400"
42
42
  ></BaseIcon>
43
43
  <p class="text-center text-sm text-slate-700">
44
- {{ $t('sui.notifications_empty') }}
44
+ {{ t('sui.notifications_empty') }}
45
45
  </p>
46
46
  </div>
47
47
  </div>
@@ -59,7 +59,7 @@
59
59
  {{
60
60
  notificationsConfig.footerLabel
61
61
  ? notificationsConfig.footerLabel
62
- : $t('sui.see_all_notifications')
62
+ : t('sui.see_all_notifications')
63
63
  }}
64
64
  </div>
65
65
  </RouterLink>
@@ -79,6 +79,7 @@ import { useBreakpoints } from '@/composables/breakpoints';
79
79
  import { NotificationsConfig } from '../types';
80
80
  import { MenuItem } from '@headlessui/vue';
81
81
  import { Notification } from '@/types/Notification';
82
+ import { t } from '@/i18n';
82
83
 
83
84
  const emit = defineEmits(['click', 'open']);
84
85
 
@@ -38,7 +38,7 @@
38
38
  />
39
39
  <div class="text-center">
40
40
  <p class="mb-0 text-sm font-medium leading-tight">
41
- {{ $t('sui.drop_or_click_to_upload') }}
41
+ {{ t('sui.drop_or_click_to_upload') }}
42
42
  </p>
43
43
 
44
44
  <div class="mt-1 text-xs leading-tight text-slate-500">
@@ -110,8 +110,7 @@ import { BaseIcon } from '.';
110
110
  import BaseMediaList from './BaseMediaList.vue';
111
111
  import BaseMediaPictures from './BaseMediaPictures.vue';
112
112
  import BaseMediaGallery from './BaseMediaGallery.vue';
113
-
114
- const i18n = useI18n();
113
+ import { t } from '@/i18n';
115
114
 
116
115
  const dialogs = useDialogsStore();
117
116
  const notifications = useNotificationsStore();
@@ -228,15 +227,13 @@ const normalizedMax = computed(() => {
228
227
  });
229
228
 
230
229
  const maxFileText = computed(() => {
231
- return i18n.t('sui.you_can_upload_up_to_n_files', {
230
+ return t('sui.you_can_upload_up_to_n_files', {
232
231
  count: normalizedMax.value,
233
232
  });
234
233
  });
235
234
 
236
235
  const maxFileSize = computed(() => {
237
- return capitalize(
238
- i18n.t('sui.up_to_x', { x: fileSizeFormat(props.maxSize) })
239
- );
236
+ return capitalize(t('sui.up_to_x', { x: fileSizeFormat(props.maxSize) }));
240
237
  });
241
238
 
242
239
  // Upload
@@ -252,8 +249,8 @@ function onUploadSuccess(file: UploadedFile) {
252
249
  normalizedMax.value > 1
253
250
  ) {
254
251
  notifications.push({
255
- title: i18n.t('sui.whoops'),
256
- text: i18n.t('sui.you_can_upload_up_to_n_files', {
252
+ title: t('sui.whoops'),
253
+ text: t('sui.you_can_upload_up_to_n_files', {
257
254
  count: normalizedMax.value,
258
255
  }),
259
256
  color: 'danger',
@@ -279,8 +276,8 @@ function onUploadSuccess(file: UploadedFile) {
279
276
 
280
277
  function promptRemove(index: number, length = 1) {
281
278
  dialogs.push({
282
- title: i18n.t('sui.remove_file'),
283
- message: i18n.t('sui.remove_file_description'),
279
+ title: t('sui.remove_file'),
280
+ message: t('sui.remove_file_description'),
284
281
  color: 'warning',
285
282
  onConfirm() {
286
283
  removeByIndex(index, length);
@@ -54,7 +54,7 @@
54
54
  class="h-10 shrink-0 pr-2 pl-3 text-sm text-blue-600"
55
55
  @click="saveName"
56
56
  >
57
- {{ $t('sui.save') }}
57
+ {{ t('sui.save') }}
58
58
  </button>
59
59
  </div>
60
60
  </div>
@@ -68,7 +68,7 @@
68
68
  @click="$emit('remove')"
69
69
  >
70
70
  <span class="hidden sm:inline">
71
- {{ $t('sui.delete') }}
71
+ {{ t('sui.delete') }}
72
72
  </span>
73
73
  <BaseIcon
74
74
  icon="heroicons-solid:x"
@@ -82,7 +82,7 @@
82
82
  target="_blank"
83
83
  >
84
84
  <span class="hidden sm:inline">
85
- {{ $t('sui.download') }}
85
+ {{ t('sui.download') }}
86
86
  </span>
87
87
  <BaseIcon
88
88
  icon="heroicons-solid:download"
@@ -100,6 +100,7 @@ import { UploadedFile } from '@/types/UploadedFile';
100
100
  import { PropType } from 'vue';
101
101
  import { Icon as BaseIcon } from '@iconify/vue';
102
102
  import { cloneDeep } from 'lodash';
103
+ import { t } from '@/i18n';
103
104
 
104
105
  const emit = defineEmits(['remove', 'update', 'save:name']);
105
106
 
@@ -13,6 +13,7 @@
13
13
  <script lang="ts" setup>
14
14
  import { PropType } from 'vue';
15
15
  import BaseCounter from './BaseCounter.vue';
16
+ import { ActionColors } from '@/types';
16
17
 
17
18
  const props = defineProps({
18
19
  label: {
@@ -33,9 +34,7 @@ const props = defineProps({
33
34
  },
34
35
  color: {
35
36
  default: 'dark',
36
- type: String as PropType<
37
- 'dark' | 'light' | 'danger' | 'success' | 'warning' | 'primary'
38
- >,
37
+ type: String as PropType<ActionColors>,
39
38
  },
40
39
  size: {
41
40
  default: 'md',
@@ -56,6 +55,8 @@ const textColor = computed((): string => {
56
55
  return 'text-yellow-600';
57
56
  } else if (props.color == 'primary') {
58
57
  return 'text-primary-600';
58
+ } else if (props.color == 'secondary') {
59
+ return 'text-slate-600';
59
60
  }
60
61
 
61
62
  return '';
@@ -14,8 +14,11 @@
14
14
  >
15
15
  <span v-if="tooBig"> Maximum {{ max }} </span>
16
16
  <span v-else-if="tooSmall"> Minimum {{ min }} </span>
17
+ <span v-else-if="tooPrecise">
18
+ {{ t('sui.maximum_x_decimal_places', { count: precision }) }}
19
+ </span>
17
20
  <span v-else>
18
- {{ $t('sui.maximum_x_decimal_places', { count: precision }) }}
21
+ {{ t('sui.invalid_value') }}
19
22
  </span>
20
23
  </div>
21
24
  </div>
@@ -74,6 +77,7 @@ import { useField } from '@/composables/field';
74
77
  import { isNumber, round } from 'lodash';
75
78
  import { PropType } from 'vue';
76
79
  import { BaseIcon } from '.';
80
+ import { t } from '@/i18n';
77
81
 
78
82
  const AUTO_CORRECT_TIMEOUT = 2000;
79
83
  const SHOW_INVALID_INPUT_TIMEOUT = 500;
@@ -183,22 +187,38 @@ const realValueInternal = computed<number | null>(() => {
183
187
  return convertToNumber(valueInternal.value);
184
188
  });
185
189
 
190
+ const realValueInternalAsString = computed<string>(() => {
191
+ if (realValueInternal.value === null) return '';
192
+ return realValueInternal.value.toLocaleString('fullwide', {
193
+ useGrouping: false,
194
+ });
195
+ });
196
+
186
197
  const invalidInput = computed(() => {
187
198
  if (realValueInternal.value == null && valueInternal.value == '') {
188
199
  return false;
189
200
  }
190
201
 
191
- return realValueInternal.value != valueInternal.value;
202
+ return realValueInternalAsString.value != (valueInternal.value ?? '');
192
203
  });
193
204
 
194
205
  const tooBig = computed(() => {
195
- if (valueInternal.value === null) return false;
196
- return hasMax.value && valueInternal.value > (props.max as number);
206
+ if (realValueInternal.value === null) return false;
207
+ return hasMax.value && realValueInternal.value > (props.max as number);
197
208
  });
198
209
 
199
210
  const tooSmall = computed(() => {
211
+ if (realValueInternal.value === null) return false;
212
+ return hasMin.value && realValueInternal.value < (props.min as number);
213
+ });
214
+
215
+ const tooPrecise = computed(() => {
200
216
  if (valueInternal.value === null) return false;
201
- return hasMin.value && valueInternal.value < (props.min as number);
217
+ const parts = (valueInternal.value + '').split('.');
218
+
219
+ if (parts.length === 1) return false;
220
+
221
+ return parts[1].length > precision.value;
202
222
  });
203
223
 
204
224
  valueInternal.value = convertToNumber(props.modelValue);
@@ -41,7 +41,7 @@
41
41
  class="mx-3 flex shrink-0 items-center justify-end"
42
42
  >
43
43
  <span class="mr-2 text-sm font-normal text-slate-500">
44
- {{ $t('sui.page') }}
44
+ {{ t('sui.page') }}
45
45
  </span>
46
46
  <input
47
47
  v-model="manualPageMobile"
@@ -55,7 +55,7 @@
55
55
  @blur="setPageFromManualPageMobile"
56
56
  />
57
57
  <span class="ml-2 text-sm font-normal text-slate-500">
58
- {{ $t('sui.of') }} {{ lastPage }}
58
+ {{ t('sui.of') }} {{ lastPage }}
59
59
  </span>
60
60
  </div>
61
61
  <div class="-mt-px flex flex-1 justify-end">
@@ -76,7 +76,7 @@
76
76
  class="mt-3 ml-4 flex shrink-0 items-center justify-end"
77
77
  >
78
78
  <span class="mr-3 text-sm font-medium text-slate-500">
79
- {{ $t('sui.go_to_page') }}
79
+ {{ t('sui.go_to_page') }}
80
80
  </span>
81
81
  <input
82
82
  v-model="manualPage"
@@ -96,6 +96,7 @@
96
96
  <script lang="ts" setup>
97
97
  import { useResizeObserver } from '@vueuse/core';
98
98
  import { debounce, isNumber, range } from 'lodash';
99
+ import { t } from '@/i18n';
99
100
 
100
101
  const props = defineProps({
101
102
  modelValue: {
@@ -13,7 +13,7 @@
13
13
  class="mt-1 inline appearance-none border-b border-dashed border-slate-400 bg-transparent px-0.5 py-0 text-slate-900 hover:text-slate-600"
14
14
  @click="showMore = true"
15
15
  >
16
- <span class="text-base font-medium">{{ $t('sui.read_more') }}</span>
16
+ <span class="text-base font-medium">{{ t('sui.read_more') }}</span>
17
17
  </button>
18
18
  </div>
19
19
  </template>
@@ -21,6 +21,7 @@
21
21
  <script lang="ts" setup>
22
22
  import { useMutationObserver } from '@vueuse/core';
23
23
  import { StyleValue } from 'vue';
24
+ import { t } from '@/i18n';
24
25
 
25
26
  const content = ref<null | HTMLParagraphElement>(null);
26
27
 
@@ -14,12 +14,12 @@
14
14
  >
15
15
  <template v-if="requiredInternal">
16
16
  <option disabled hidden :value="EMPTY_VALUE_INTERNAL">
17
- {{ placeholder ? placeholder : $t('sui.select_an_option') }}
17
+ {{ placeholder ? placeholder : t('sui.select_an_option') }}
18
18
  </option>
19
19
  </template>
20
20
  <template v-else>
21
21
  <option :value="EMPTY_VALUE_INTERNAL">
22
- {{ placeholder ? placeholder : $t('sui.select_an_option') }}
22
+ {{ placeholder ? placeholder : t('sui.select_an_option') }}
23
23
  </option>
24
24
  </template>
25
25
 
@@ -43,6 +43,7 @@ import { get, isArray, isEqual } from 'lodash';
43
43
  import { useMutationObserver } from '@vueuse/core';
44
44
  import { useField } from '@/composables/field';
45
45
  import { NormalizedOption, OptionValue, Option } from '@/types';
46
+ import { t } from '@/i18n';
46
47
 
47
48
  type SelectOption = string | number | null;
48
49
 
@@ -118,11 +118,14 @@ const Icons = (args) => ({
118
118
  return { args, value };
119
119
  },
120
120
  template: `
121
- <BaseSwitch v-model="value" :hasIcon="true" v-bind="args">
121
+ <BaseSwitch v-model="value" v-bind="args">
122
122
  <span class="text-slate-600">Label</span>
123
123
  </BaseSwitch>
124
124
  `,
125
125
  });
126
126
 
127
127
  export const WithIcons = Icons.bind({});
128
- WithIcons.args = {};
128
+ WithIcons.args = {
129
+ checkedIcon: 'heroicons-solid:check',
130
+ unCheckedIcon: 'heroicons-solid:x',
131
+ };
@@ -20,12 +20,11 @@
20
20
  height: sizePx + 'px',
21
21
  width: sizePx + 'px',
22
22
  transform: `translateX(${translateX}px)`,
23
- color: iconColor,
24
23
  fontSize: sizePx - 7 + 'px',
25
24
  }"
26
- class="flex items-center justify-center inline-block transition duration-200 ease-in-out bg-white rounded-full shadow pointer-events-none ring-0"
25
+ class="flex items-center justify-center transition duration-200 ease-in-out bg-white rounded-full shadow pointer-events-none text-slate-500 ring-0"
27
26
  >
28
- <BaseIcon v-if="hasIcon" :icon="icon" />
27
+ <BaseIcon v-if="icon" :icon="icon" />
29
28
  </span>
30
29
  </Switch>
31
30
  <SwitchLabel v-if="$slots.default" class="cursor-pointer">
@@ -70,24 +69,12 @@ const props = defineProps({
70
69
  default: false,
71
70
  type: Boolean,
72
71
  },
73
- hasIcon: {
74
- default: false,
75
- type: Boolean,
76
- },
77
72
  checkedIcon: {
78
- default: 'heroicons-solid:check',
73
+ default: '',
79
74
  type: String,
80
75
  },
81
76
  unCheckedIcon: {
82
- default: 'heroicons-solid:x',
83
- type: String,
84
- },
85
- checkedIconColor: {
86
- default: '#9ca3af',
87
- type: String,
88
- },
89
- unCheckedIconColor: {
90
- default: '#9ca3af',
77
+ default: '',
91
78
  type: String,
92
79
  },
93
80
  });
@@ -206,26 +193,12 @@ const translateX = computed(() => {
206
193
  return padding.value;
207
194
  });
208
195
 
209
- const iconColor = computed(() => {
210
- if (props.hasIcon) {
211
- if (props.modelValue) {
212
- return props.checkedIconColor;
213
- }
214
- if (!props.modelValue) {
215
- return props.unCheckedIconColor;
216
- }
217
- }
218
- return '';
219
- });
220
-
221
196
  const icon = computed(() => {
222
- if (props.hasIcon) {
223
- if (props.modelValue) {
224
- return props.checkedIcon;
225
- }
226
- if (!props.modelValue) {
227
- return props.unCheckedIcon;
228
- }
197
+ if (props.modelValue && props.checkedIcon) {
198
+ return props.checkedIcon;
199
+ }
200
+ if (!props.modelValue && props.unCheckedIcon) {
201
+ return props.unCheckedIcon;
229
202
  }
230
203
  return null;
231
204
  });
@@ -38,7 +38,7 @@
38
38
  ref="inputElement"
39
39
  :value="keywords"
40
40
  type="text"
41
- :placeholder="placeholder ? placeholder : $t('sui.select_an_item')"
41
+ :placeholder="placeholder ? placeholder : t('sui.select_an_item')"
42
42
  class="w-full min-w-[50px] border-none p-0 pl-1 shadow-none outline-none focus:border-none focus:shadow-none focus:outline-none focus:ring-0 disabled:cursor-not-allowed"
43
43
  :class="[inputClass]"
44
44
  autocomplete="off"
@@ -96,8 +96,8 @@ import { useField } from '@/composables/field';
96
96
  import { useClickOutside } from '@/composables/clickOutside';
97
97
  import { useNotificationsStore } from '@/stores/notifications';
98
98
  import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
99
+ import { t } from '@/i18n';
99
100
 
100
- const i18n = useI18n();
101
101
  const notifications = useNotificationsStore();
102
102
 
103
103
  const props = defineProps({
@@ -283,8 +283,8 @@ const onSelect = (option: NormalizedOption) => {
283
283
 
284
284
  if (props.max && normalizedModelValue.value.length >= props.max) {
285
285
  notifications.push({
286
- title: i18n.t('sui.whoops'),
287
- text: i18n.t('sui.you_cannot_select_more_than_x_items', {
286
+ title: t('sui.whoops'),
287
+ text: t('sui.you_cannot_select_more_than_x_items', {
288
288
  count: props.max,
289
289
  }),
290
290
  color: 'warning',
@@ -31,7 +31,7 @@
31
31
  v-if="firstSearch"
32
32
  class="flex h-[80px] items-center justify-center px-3 text-center text-base leading-tight text-slate-600"
33
33
  >
34
- {{ $t('sui.nothing_found') }}
34
+ {{ t('sui.nothing_found') }}
35
35
  </div>
36
36
  </slot>
37
37
  </template>
@@ -39,7 +39,7 @@
39
39
  </template>
40
40
 
41
41
  <script lang="ts" setup>
42
- import { debounce, isArray, throttle } from 'lodash';
42
+ import { debounce, throttle } from 'lodash';
43
43
  import { config } from '@/index';
44
44
  import { PropType } from 'vue';
45
45
  import {
@@ -50,6 +50,7 @@ import {
50
50
  } from '@/types';
51
51
  import BaseTagAutocomplete from './BaseTagAutocomplete.vue';
52
52
  import { useHasPaginatedData } from '@/composables/paginatedData';
53
+ import { t } from '@/i18n';
53
54
 
54
55
  const props = defineProps({
55
56
  modelValue: {
@@ -1,8 +1,8 @@
1
1
  import { ref, watchEffect } from 'vue';
2
- import type { MaybeComputedRef } from '@vueuse/shared';
2
+ import type { MaybeRefOrGetter } from '@vueuse/shared';
3
3
  import { resolveRef, tryOnScopeDispose } from '@vueuse/shared';
4
4
 
5
- export function useMediaQuery(query: MaybeComputedRef<string>) {
5
+ export function useMediaQuery(query: MaybeRefOrGetter<string>) {
6
6
  const isSupported = () =>
7
7
  window && 'matchMedia' in window && typeof window.matchMedia === 'function';
8
8
 
@@ -0,0 +1,60 @@
1
+ import { useI18nStore } from '@/stores/i18n';
2
+ import en from '@/lang/en.json';
3
+ import fr from '@/lang/fr.json';
4
+ import { get } from 'lodash';
5
+
6
+ export function t(key: string, args?: Record<string, any> | undefined): string {
7
+ const value = getTranslationValue(key);
8
+
9
+ return replaceArgs(value, args);
10
+ }
11
+
12
+ function getCurrentLocale() {
13
+ try {
14
+ const i18nStore = useI18nStore();
15
+ return i18nStore.locale;
16
+ } catch (error) {
17
+ console.error(error);
18
+ return 'en';
19
+ }
20
+ }
21
+
22
+ function getTranslationValue(key: string) {
23
+ const locale = getCurrentLocale();
24
+
25
+ if (locale === 'fr') {
26
+ const frValue = get(fr, key);
27
+ if (frValue) return frValue;
28
+ }
29
+
30
+ return get(en, key, key);
31
+ }
32
+
33
+ function replaceArgs(
34
+ value: string,
35
+ args: Record<string, any> | undefined
36
+ ): string {
37
+ let result = value;
38
+
39
+ if (!args) return result;
40
+
41
+ if (args.count !== undefined && value.includes('|')) {
42
+ const parts = value.split('|');
43
+
44
+ if (parts.length == 2) {
45
+ const [singular, plural] = value.split('|');
46
+ result = args.count > 1 ? plural : singular;
47
+ } else if (parts.length == 3) {
48
+ const [zero, singular, plural] = value.split('|');
49
+ result = args.count == 0 ? zero : args.count > 1 ? plural : singular;
50
+ }
51
+
52
+ result.trim();
53
+ }
54
+
55
+ Object.keys(args).forEach((key: any) => {
56
+ result = result.replace(`{${key}}`, args[key]);
57
+ });
58
+
59
+ return result;
60
+ }
package/src/index.ts CHANGED
@@ -2,7 +2,6 @@ import axios, { AxiosInstance } from 'axios';
2
2
  import { App } from 'vue';
3
3
  import * as components from './components';
4
4
  import QueryString from 'qs';
5
- import { createI18n, I18n } from 'vue-i18n';
6
5
  import en from '@/lang/en.json';
7
6
  import fr from '@/lang/fr.json';
8
7
  import { useDialogsStore } from './stores/dialogs';
@@ -17,10 +16,10 @@ import './assets/main.css';
17
16
  import { Locales } from './types';
18
17
  import { Country } from './types/Country';
19
18
  import { Region } from './types/Region';
19
+ import { useI18nStore } from './stores/i18n';
20
20
 
21
21
  export interface Options {
22
22
  // eslint-disable-next-line @typescript-eslint/ban-types
23
- i18n?: I18n<typeof messages, {}, {}, string, true>;
24
23
  http?: AxiosInstance;
25
24
  upload_url?: string;
26
25
  locales?: Locales;
@@ -31,11 +30,6 @@ export interface Options {
31
30
  }
32
31
 
33
32
  const config = {
34
- i18n: createI18n({
35
- locale: 'en',
36
- fallbackLocale: 'en',
37
- messages: { en, fr },
38
- }),
39
33
  http: axios.create(),
40
34
  locales: {
41
35
  en: 'English',
@@ -61,10 +55,6 @@ function install(app: App, options?: Options) {
61
55
  app.component(key, components[key]);
62
56
  }
63
57
 
64
- if (options?.i18n) {
65
- config.i18n = options.i18n;
66
- }
67
-
68
58
  if (options?.http) {
69
59
  config.http = options.http;
70
60
  }
@@ -92,6 +82,16 @@ function install(app: App, options?: Options) {
92
82
  if (options?.regions) {
93
83
  config.regions = options.regions;
94
84
  }
85
+
86
+ if (app.config.globalProperties.$i18n) {
87
+ watch(
88
+ () => app.config.globalProperties.$i18n.locale,
89
+ (locale) => {
90
+ useI18nStore().set(locale);
91
+ },
92
+ { immediate: true }
93
+ );
94
+ }
95
95
  }
96
96
 
97
97
  export default { install };
@@ -1,6 +1,6 @@
1
1
  import { defineStore } from 'pinia';
2
- import { config } from '@/index';
3
2
  import { Dialog, DialogOptions } from '../types';
3
+ import { t } from '@/i18n';
4
4
 
5
5
  export const useDialogsStore = defineStore('dialogs', {
6
6
  state: () => {
@@ -17,8 +17,8 @@ export const useDialogsStore = defineStore('dialogs', {
17
17
  color: options.color ?? 'info',
18
18
  title: options.title,
19
19
  message: options.message,
20
- confirmText: options.confirmText ?? config.i18n.global.t('sui.confirm'),
21
- cancelText: options.cancelText ?? config.i18n.global.t('sui.cancel'),
20
+ confirmText: options.confirmText ?? t('sui.confirm'),
21
+ cancelText: options.cancelText ?? t('sui.cancel'),
22
22
  closeOnOutsideClick: options.closeOnOutsideClick ?? true,
23
23
  onConfirm:
24
24
  options.onConfirm ??
@@ -0,0 +1,14 @@
1
+ import { defineStore } from 'pinia';
2
+
3
+ export const useI18nStore = defineStore('i18n', {
4
+ state: () => {
5
+ return {
6
+ locale: 'en',
7
+ };
8
+ },
9
+ actions: {
10
+ set(locale: string) {
11
+ this.locale = locale;
12
+ },
13
+ },
14
+ });
@@ -65,6 +65,15 @@ export interface PaginatedCollection extends PaginationMetadata {
65
65
  data: Collection;
66
66
  }
67
67
 
68
+ export type ActionColors =
69
+ | 'dark'
70
+ | 'light'
71
+ | 'danger'
72
+ | 'success'
73
+ | 'warning'
74
+ | 'primary'
75
+ | 'secondary';
76
+
68
77
  export interface MenuItemInterface {
69
78
  label?: string;
70
79
  href?: string;
@@ -72,7 +81,7 @@ export interface MenuItemInterface {
72
81
  action?: () => Promise<void> | void;
73
82
  icon?: string;
74
83
  count?: number;
75
- color?: 'dark' | 'light' | 'danger' | 'success' | 'warning' | 'primary';
84
+ color?: ActionColors;
76
85
  line?: boolean;
77
86
  }
78
87
 
@@ -83,7 +92,7 @@ export interface ActionItem {
83
92
  action?: () => Promise<void> | void;
84
93
  icon?: string;
85
94
  count?: number;
86
- color?: 'dark' | 'light' | 'danger' | 'success' | 'warning' | 'primary';
95
+ color?: ActionColors;
87
96
  disabled?: boolean;
88
97
  meta?: Record<string, any>;
89
98
  actions?: ActionItem[];
@@ -1,14 +1,14 @@
1
- import { config } from '@/index';
1
+ import { t } from '@/i18n';
2
2
 
3
3
  export default function (size: number): string {
4
4
  const i = Math.floor(Math.log(size) / Math.log(1024));
5
5
 
6
6
  const units = [
7
- config.i18n.global.t('sui.units.b'),
8
- config.i18n.global.t('sui.units.kb'),
9
- config.i18n.global.t('sui.units.mb'),
10
- config.i18n.global.t('sui.units.gb'),
11
- config.i18n.global.t('sui.units.tb'),
7
+ t('sui.units.b'),
8
+ t('sui.units.kb'),
9
+ t('sui.units.mb'),
10
+ t('sui.units.gb'),
11
+ t('sui.units.tb'),
12
12
  ];
13
13
 
14
14
  return +(size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + units[i];