@bagelink/vue 1.9.49 → 1.9.54

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 (79) hide show
  1. package/dist/components/AddressSearch.vue.d.ts.map +1 -1
  2. package/dist/components/Btn.vue.d.ts +2 -2
  3. package/dist/components/Btn.vue.d.ts.map +1 -1
  4. package/dist/components/Filter.vue.d.ts +0 -1
  5. package/dist/components/Filter.vue.d.ts.map +1 -1
  6. package/dist/components/ImportData.vue.d.ts.map +1 -1
  7. package/dist/components/ModalConfirm.vue.d.ts.map +1 -1
  8. package/dist/components/Spreadsheet/Index.vue.d.ts.map +1 -1
  9. package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
  10. package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -1
  11. package/dist/components/form/inputs/DateInput.vue.d.ts +1 -1
  12. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  13. package/dist/components/form/inputs/DatePicker.vue.d.ts +1 -1
  14. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/EmailInput.vue.d.ts.map +1 -1
  16. package/dist/components/form/inputs/PasswordInput.vue.d.ts +1 -1
  17. package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
  18. package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
  19. package/dist/components/form/inputs/SelectInput.vue.d.ts +0 -1
  20. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  21. package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
  22. package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
  23. package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
  24. package/dist/dialog/DialogConfirm.vue.d.ts +0 -3
  25. package/dist/dialog/DialogConfirm.vue.d.ts.map +1 -1
  26. package/dist/dialog/DialogForm.vue.d.ts.map +1 -1
  27. package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
  28. package/dist/form-flow/MultiStepForm.vue.d.ts.map +1 -1
  29. package/dist/i18n/index.d.ts +996 -0
  30. package/dist/i18n/index.d.ts.map +1 -0
  31. package/dist/index.cjs +135 -130
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.mjs +15415 -11483
  35. package/dist/plugins/bagel.d.ts +5 -7
  36. package/dist/plugins/bagel.d.ts.map +1 -1
  37. package/dist/plugins/useToast.d.ts.map +1 -1
  38. package/dist/style.css +1 -1
  39. package/dist/utils/calendar/dateUtils.d.ts +4 -3
  40. package/dist/utils/calendar/dateUtils.d.ts.map +1 -1
  41. package/dist/utils/index.d.ts +0 -1
  42. package/dist/utils/index.d.ts.map +1 -1
  43. package/dist/utils/lang.d.ts +6 -7
  44. package/dist/utils/lang.d.ts.map +1 -1
  45. package/package.json +2 -1
  46. package/src/components/AddressSearch.vue +5 -2
  47. package/src/components/Btn.vue +8 -6
  48. package/src/components/Filter.vue +37 -76
  49. package/src/components/ImportData.vue +35 -29
  50. package/src/components/ModalConfirm.vue +9 -7
  51. package/src/components/Spreadsheet/Index.vue +35 -60
  52. package/src/components/form/FieldArray.vue +4 -2
  53. package/src/components/form/inputs/ColorInput.vue +18 -12
  54. package/src/components/form/inputs/DateInput.vue +7 -4
  55. package/src/components/form/inputs/EmailInput.vue +9 -7
  56. package/src/components/form/inputs/PasswordInput.vue +17 -15
  57. package/src/components/form/inputs/RadioPillsInput.vue +5 -3
  58. package/src/components/form/inputs/SelectInput.vue +13 -11
  59. package/src/components/form/inputs/TelInput.vue +11 -9
  60. package/src/components/form/inputs/Upload/UploadInput.vue +7 -5
  61. package/src/components/lightbox/Lightbox.vue +4 -1
  62. package/src/dialog/DialogConfirm.vue +11 -6
  63. package/src/dialog/DialogForm.vue +9 -9
  64. package/src/form-flow/FormFlow.vue +7 -15
  65. package/src/form-flow/MultiStepForm.vue +16 -12
  66. package/src/i18n/index.ts +130 -0
  67. package/src/i18n/locales/en.json +153 -0
  68. package/src/i18n/locales/es.json +153 -0
  69. package/src/i18n/locales/fr.json +153 -0
  70. package/src/i18n/locales/he.json +174 -0
  71. package/src/i18n/locales/it.json +153 -0
  72. package/src/i18n/locales/ru.json +153 -0
  73. package/src/index.ts +11 -5
  74. package/src/plugins/bagel.ts +14 -26
  75. package/src/plugins/useToast.ts +4 -2
  76. package/src/styles/text.css +2 -1
  77. package/src/utils/calendar/dateUtils.ts +91 -59
  78. package/src/utils/index.ts +0 -2
  79. package/src/utils/lang.ts +0 -46
@@ -1,8 +1,14 @@
1
1
  <script lang="ts" setup>
2
- import { Btn, CheckInput, TextInput, ToggleInput, localRef, ListItem, Dropdown } from '@bagelink/vue'
2
+ import { Btn, CheckInput, TextInput, ToggleInput, localRef, ListItem, Dropdown, useI18n } from '@bagelink/vue'
3
3
  import { computed, ref, watch, nextTick, onUnmounted } from 'vue'
4
4
  import SpreadsheetTable from './SpreadsheetTable.vue'
5
5
 
6
+ const { modelValue, columnConfig, label, allowAddRow = true } = defineProps<Props>()
7
+
8
+ const emit = defineEmits(['update:modelValue'])
9
+
10
+ const { $t } = useI18n()
11
+
6
12
  // Define column configuration types
7
13
  type ColumnFormat = 'text' | 'number' | 'currency' | 'date' | 'percentage' | 'image' | 'boolean'
8
14
 
@@ -26,9 +32,6 @@ interface Props {
26
32
  allowAddRow?: boolean
27
33
  }
28
34
 
29
- const { modelValue, columnConfig, label, allowAddRow = true } = defineProps<Props>()
30
- const emit = defineEmits(['update:modelValue'])
31
-
32
35
  // Helper function to flatten an object with dot notation
33
36
  function flattenObject(obj: { [key: string]: any }, prefix = ''): { [key: string]: any } {
34
37
  const flattened: { [key: string]: any } = {}
@@ -692,29 +695,31 @@ const columnOptions = computed(() => {
692
695
  <div class="flex gap-05 py-05 justify-content-end m_flex-wrap">
693
696
  <label v-if="label" class="label me-auto">{{ label }}</label>
694
697
  <div class="flex gap-075">
695
- <TextInput ref="searchInputRef" v-model="search" icon="search" placeholder="Search" class="m-0 max-w200px" />
698
+ <TextInput
699
+ ref="searchInputRef" v-model="search" icon="search" placeholder="Search"
700
+ class="m-0 max-w200px"
701
+ />
696
702
  <Dropdown flat thin icon="more_vert">
697
703
  <ListItem title="Paste" icon="paste" @click="pasteSelection" />
698
704
  <ListItem title="copy" icon="copy" @click="copySelection" />
699
705
  <ListItem title="Undo" icon="undo" :disabled="!canUndo" @click="undo" />
700
706
  <ListItem title="Redo" icon="redo" :disabled="!canRedo" @click="redo" />
701
- <ToggleInput v-model="wrapText" label="Wrap Text" />
707
+ <ToggleInput v-model="wrapText" :label="$t('spreadsheet.wrapText')" />
702
708
  <Dropdown placement="auto-end">
703
709
  <template #trigger="{ show }">
704
710
  <ListItem title="Column Visibility" icon="view_column" @click="show()" />
705
711
  </template>
706
712
  <div class="p-05">
707
713
  <div class="flex space-between">
708
- <Btn flat thin small value="Select All" @click="visibleColumns = columnOptions.map(col => col.key)" />
709
- <Btn flat thin small value="Clear All" @click="visibleColumns = []" />
714
+ <Btn
715
+ flat thin small value="$t:spreadsheet.selectAll"
716
+ @click="visibleColumns = columnOptions.map(col => col.key)"
717
+ />
718
+ <Btn flat thin small value="$t:spreadsheet.clearAll" @click="visibleColumns = []" />
710
719
  </div>
711
720
  <CheckInput
712
- v-for="col in columnOptions"
713
- :key="col.key"
714
- v-model="visibleColumns"
715
- :value="col.key"
716
- :label="col.label || col.key"
717
- @update:modelValue="val => undefined"
721
+ v-for="col in columnOptions" :key="col.key" v-model="visibleColumns"
722
+ :value="col.key" :label="col.label || col.key" @update:modelValue="val => undefined"
718
723
  />
719
724
  </div>
720
725
  </Dropdown>
@@ -725,57 +730,27 @@ const columnOptions = computed(() => {
725
730
  <div class="flex w-100p relative">
726
731
  <!-- Fixed Columns -->
727
732
  <SpreadsheetTable
728
- v-if="fixedColumns.length"
729
- :columns="fixedColumns"
730
- :rows="filteredRows"
731
- :is-fixed="true"
732
- :show-row-numbers="true"
733
- :column-widths="columnWidths"
734
- :sort-column="sortColumn"
735
- :sort-direction="sortDirection"
736
- :selection-start="selectionStart"
737
- :selection-end="selectionEnd"
738
- :editing-cell="editingCell"
739
- :base-column-index="0"
740
- :wrap-text="wrapText"
741
- class="sticky z-2 start-0 bg-white"
742
- @sortColumn="sortByColumn"
743
- @resizeStart="handleResizeStart"
744
- @selectRow="selectEntireRow"
745
- @cellMouseDown="handleMouseDown"
746
- @cellMouseOver="handleMouseOver"
747
- @cellEditStart="startEditing"
748
- @cellKeyDown="handleCellKeyDown"
749
- @updateCell="updateCell"
750
- @stopEditing="handleStopEditingAndBlur"
751
- @setInputRef="setInputRef"
733
+ v-if="fixedColumns.length" :columns="fixedColumns" :rows="filteredRows"
734
+ :is-fixed="true" :show-row-numbers="true" :column-widths="columnWidths" :sort-column="sortColumn"
735
+ :sort-direction="sortDirection" :selection-start="selectionStart" :selection-end="selectionEnd"
736
+ :editing-cell="editingCell" :base-column-index="0" :wrap-text="wrapText"
737
+ class="sticky z-2 start-0 bg-white" @sortColumn="sortByColumn" @resizeStart="handleResizeStart"
738
+ @selectRow="selectEntireRow" @cellMouseDown="handleMouseDown" @cellMouseOver="handleMouseOver"
739
+ @cellEditStart="startEditing" @cellKeyDown="handleCellKeyDown" @updateCell="updateCell"
740
+ @stopEditing="handleStopEditingAndBlur" @setInputRef="setInputRef"
752
741
  />
753
742
 
754
743
  <!-- Scrollable Columns -->
755
744
  <div class="flex-shrink flex-grow">
756
745
  <SpreadsheetTable
757
- :columns="scrollableColumns"
758
- :rows="filteredRows"
759
- :is-fixed="false"
760
- :show-row-numbers="!fixedColumns.length"
761
- :column-widths="columnWidths"
762
- :sort-column="sortColumn"
763
- :sort-direction="sortDirection"
764
- :selection-start="selectionStart"
765
- :selection-end="selectionEnd"
766
- :editing-cell="editingCell"
767
- :base-column-index="fixedColumns.length"
768
- :wrap-text="wrapText"
769
- @sortColumn="sortByColumn"
770
- @resizeStart="handleResizeStart"
771
- @selectRow="selectEntireRow"
772
- @cellMouseDown="handleMouseDown"
773
- @cellMouseOver="handleMouseOver"
774
- @cellEditStart="startEditing"
775
- @cellKeyDown="handleCellKeyDown"
776
- @updateCell="updateCell"
777
- @stopEditing="handleStopEditingAndBlur"
778
- @setInputRef="setInputRef"
746
+ :columns="scrollableColumns" :rows="filteredRows" :is-fixed="false"
747
+ :show-row-numbers="!fixedColumns.length" :column-widths="columnWidths" :sort-column="sortColumn"
748
+ :sort-direction="sortDirection" :selection-start="selectionStart" :selection-end="selectionEnd"
749
+ :editing-cell="editingCell" :base-column-index="fixedColumns.length" :wrap-text="wrapText"
750
+ @sortColumn="sortByColumn" @resizeStart="handleResizeStart" @selectRow="selectEntireRow"
751
+ @cellMouseDown="handleMouseDown" @cellMouseOver="handleMouseOver" @cellEditStart="startEditing"
752
+ @cellKeyDown="handleCellKeyDown" @updateCell="updateCell"
753
+ @stopEditing="handleStopEditingAndBlur" @setInputRef="setInputRef"
779
754
  />
780
755
  </div>
781
756
  </div>
@@ -10,7 +10,7 @@ import type {
10
10
  Path
11
11
  } from '@bagelink/vue'
12
12
  import type { MaybeRefOrGetter } from 'vue'
13
- import { BagelForm, Btn, Loading, Icon, Card } from '@bagelink/vue'
13
+ import { BagelForm, Btn, Loading, Icon, Card, useI18n } from '@bagelink/vue'
14
14
  import { ref, computed, watch, toValue, onMounted } from 'vue'
15
15
 
16
16
  export interface FieldArrayProps<T, P extends Path<T>> {
@@ -52,6 +52,8 @@ const props = withDefaults(
52
52
 
53
53
  const emit = defineEmits(['update:modelValue'])
54
54
 
55
+ const { $t } = useI18n()
56
+
55
57
  const BagelFormFA = BagelForm<T, P>
56
58
 
57
59
  // State
@@ -297,7 +299,7 @@ const showMinimizeButton = computed(() => {
297
299
  v-if="props.defaultValue && props.defaultValue.length > 0" thin color="primary"
298
300
  class="txt12 mb-05" @click="loadDefaults"
299
301
  >
300
- Load Default {{ label || 'Items' }}
302
+ {{ $t('fieldArray.loadDefault', { label: label || 'Items' }) }}
301
303
  </Btn>
302
304
  </Card>
303
305
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { Btn } from '@bagelink/vue'
2
+ import { Btn, useI18n } from '@bagelink/vue'
3
3
  import { ref, watch } from 'vue'
4
4
 
5
5
  const props = withDefaults(
@@ -17,7 +17,11 @@ const props = withDefaults(
17
17
  editMode: true,
18
18
  },
19
19
  )
20
+
20
21
  const emits = defineEmits(['update:modelValue'])
22
+
23
+ const { $t } = useI18n()
24
+
21
25
  const inputVal = ref<string>(props.modelValue)
22
26
 
23
27
  watch(
@@ -32,34 +36,36 @@ watch(
32
36
  {{ label }}
33
37
  <div class="flex rounded colorInputPickWrap" :class="{ 'px-025 bg-input': !small }">
34
38
  <input
35
- :id="id"
36
- v-model="inputVal" class="border" type="color" :placeholder="placeholder || label"
37
- :class="{ 'no-edit': !editMode, 'opacity-1': !inputVal }" :required="required" v-bind="nativeInputAttrs"
39
+ :id="id" v-model="inputVal" class="border" type="color" :placeholder="placeholder || label"
40
+ :class="{ 'no-edit': !editMode, 'opacity-1': !inputVal }" :required="required"
41
+ v-bind="nativeInputAttrs"
38
42
  >
39
43
  <input
40
- v-if="!small"
41
- v-model="inputVal"
42
- class="flex-grow-1 border colorInputPick"
43
- type="text"
44
- placeholder="Enter color"
44
+ v-if="!small" v-model="inputVal" class="flex-grow-1 border colorInputPick" type="text"
45
+ :placeholder="$t('color.placeholder')"
45
46
  >
46
- <Btn :class="{ 'not-allowed opacity-3': !inputVal }" round flat thin icon="clear" @click="inputVal = ''" />
47
+ <Btn
48
+ :class="{ 'not-allowed opacity-3': !inputVal }" round flat thin icon="clear"
49
+ @click="inputVal = ''"
50
+ />
47
51
  </div>
48
52
  </label>
49
53
  </div>
50
54
  </template>
51
55
 
52
56
  <style>
53
- .colorInputPick{
57
+ .colorInputPick {
54
58
  --input-font-size: 12px;
55
59
  background: transparent !important;
56
60
  height: var(--input-height) !important;
57
61
  min-width: 50px !important;
58
62
  }
59
- .colorInputPick:focus{
63
+
64
+ .colorInputPick:focus {
60
65
  outline: none;
61
66
  box-shadow: none !important;
62
67
  }
68
+
63
69
  .colorInputPickWrap:focus-within {
64
70
  box-shadow: inset 0 0 10px #00000021;
65
71
  outline-color: var(--input-bg);
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { ModeType } from '../../../utils/calendar/typings'
3
- import { TextInput, Dropdown, formatDate } from '@bagelink/vue'
3
+ import { TextInput, Dropdown, formatDate, useI18n } from '@bagelink/vue'
4
4
  import { onClickOutside } from '@vueuse/core'
5
5
  import { ref, onMounted, watch } from 'vue'
6
6
  import { WEEK_START_DAY } from '../../../utils/calendar/time'
@@ -37,6 +37,8 @@ const props = withDefaults(
37
37
  },
38
38
  )
39
39
 
40
+ const { $t } = useI18n()
41
+
40
42
  const selectedDate = defineModel<string | Date>()
41
43
  const inputValue = ref('')
42
44
  const isValid = ref(true)
@@ -181,9 +183,10 @@ onMounted(() => {
181
183
  <TextInput
182
184
  v-model="inputValue" iconStart="calendar"
183
185
  :placeholder="enableTime ? 'DD.MM.YY HH:mm' : 'DD.MM.YY'" :required="required"
184
- :disabled="!editMode" :error="!isValid ? 'Invalid date format' : error"
185
- :class="{ 'txt-center': center }" class="date-input m-0" @input="handleInput" @focus="handleFocus"
186
- @blur="handleBlur" @click.stop @keydown="handleKeydown" @iconClick="handleIconClick"
186
+ :disabled="!editMode" :error="!isValid ? $t('date.invalidFormat') : error"
187
+ :class="{ 'txt-center': center }" class="date-input m-0" @input="handleInput"
188
+ @focus="handleFocus" @blur="handleBlur" @click.stop @keydown="handleKeydown"
189
+ @iconClick="handleIconClick"
187
190
  />
188
191
  </div>
189
192
  </template>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconType, ValidateInputBaseT } from '@bagelink/vue'
3
- import { Icon, useDebounceFn } from '@bagelink/vue'
3
+ import { Icon, useDebounceFn, useI18n } from '@bagelink/vue'
4
4
  import { onMounted, ref, watch, nextTick } from 'vue'
5
5
  import { EMAIL_REGEX } from '../../../utils/constants'
6
6
 
@@ -17,6 +17,8 @@ const props = withDefaults(
17
17
 
18
18
  const emit = defineEmits(['update:modelValue', 'debounce'])
19
19
 
20
+ const { $t } = useI18n()
21
+
20
22
  export interface EmailInputProps extends ValidateInputBaseT {
21
23
  id?: string
22
24
  title?: string
@@ -108,7 +110,7 @@ function validateEmail(value: string) {
108
110
 
109
111
  // Basic format validation
110
112
  if (!EMAIL_REGEX.test(value)) {
111
- return 'Please enter a valid email address'
113
+ return $t('email.invalidEmail')
112
114
  }
113
115
 
114
116
  // Check for fake email providers if enabled
@@ -173,7 +175,7 @@ async function validateEmailWithServer(email: string) {
173
175
  validatedEmails.set(email, isValid)
174
176
 
175
177
  if (!isValid) {
176
- validationMessage.value = 'This email domain appears to be invalid'
178
+ validationMessage.value = $t('email.invalidDomain')
177
179
  input.value?.setCustomValidity(validationMessage.value)
178
180
  } else {
179
181
  input.value?.setCustomValidity('')
@@ -467,9 +469,9 @@ onMounted(() => {
467
469
  .error-message {
468
470
  color: var(--bgl-red, #dc3545);
469
471
  font-size: 10px;
470
- position: absolute;
471
- inset-inline-end: 0;
472
- bottom: -0.9rem;
473
- margin-top: 0.25rem;
472
+ position: absolute;
473
+ inset-inline-end: 0;
474
+ bottom: -0.9rem;
475
+ margin-top: 0.25rem;
474
476
  }
475
477
  </style>
@@ -1,9 +1,20 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconType } from '@bagelink/vue'
3
- import { Btn, TextInput } from '@bagelink/vue'
3
+ import { Btn, TextInput, useI18n } from '@bagelink/vue'
4
4
  import { zxcvbn } from '@zxcvbn-ts/core'
5
5
  import { computed } from 'vue'
6
6
 
7
+ const props = withDefaults(
8
+ defineProps<TextInputProps>(),
9
+ {
10
+ autocomplete: 'current-password',
11
+ label: '',
12
+ strengthMeter: false
13
+ }
14
+ )
15
+
16
+ const { $t } = useI18n()
17
+
7
18
  export interface TextInputProps {
8
19
  id?: string
9
20
  title?: string
@@ -31,15 +42,6 @@ export interface TextInputProps {
31
42
  strengthMeter?: boolean
32
43
  error?: string
33
44
  }
34
- const props = withDefaults(
35
- defineProps<TextInputProps>(),
36
- {
37
- autocomplete: 'current-password',
38
- label: '',
39
- strengthMeter: false
40
- }
41
- )
42
-
43
45
  const password = defineModel<string>('modelValue')
44
46
  const showPwd = defineModel<boolean>('showPwd', { default: false })
45
47
 
@@ -73,11 +75,11 @@ const strengthColor = computed(() => {
73
75
  const strengthLabel = computed(() => {
74
76
  const score = strengthScore.value
75
77
  if (score == null) return ''
76
- if (score === 0) return 'Very Weak'
77
- if (score === 1) return 'Weak'
78
- if (score === 2) return 'Fair'
79
- if (score === 3) return 'Good'
80
- return 'Strong'
78
+ if (score === 0) return $t('password.veryWeak')
79
+ if (score === 1) return $t('password.weak')
80
+ if (score === 2) return $t('password.fair')
81
+ if (score === 3) return $t('password.good')
82
+ return $t('password.strong')
81
83
  })
82
84
 
83
85
  const strengthPercentage = computed(() => {
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { Option } from '@bagelink/vue'
3
-
3
+ import { useI18n } from '@bagelink/vue'
4
4
  import { onMounted, ref, watch } from 'vue'
5
5
 
6
6
  const props = withDefaults(
@@ -19,17 +19,19 @@ const props = withDefaults(
19
19
 
20
20
  const emits = defineEmits(['update:modelValue'])
21
21
 
22
+ const { $t } = useI18n()
23
+
22
24
  function getLabel(option: Option) {
23
25
  if (typeof option === 'string') { return option }
24
26
  if (typeof option === 'number') { return `${option}` }
25
- if (typeof option === 'boolean') { return option ? 'Yes' : 'No' }
27
+ if (typeof option === 'boolean') { return option ? $t('select.yes') : $t('select.no') }
26
28
  return option.label
27
29
  }
28
30
 
29
31
  function getValue(option: Option) {
30
32
  if (typeof option === 'string') { return option }
31
33
  if (typeof option === 'number') { return option }
32
- if (typeof option === 'boolean') { return option ? 'Yes' : 'No' }
34
+ if (typeof option === 'boolean') { return option ? $t('select.yes') : $t('select.no') }
33
35
  return option.value
34
36
  }
35
37
 
@@ -1,18 +1,19 @@
1
1
  <script lang="ts" setup>
2
2
  import type { IconType, Option } from '@bagelink/vue'
3
3
  import type { AlignedPlacement } from '../../Dropdown.vue'
4
- import { Btn, Card, Skeleton, Dropdown, Icon, TextInput, useSearch } from '@bagelink/vue'
4
+ import { Btn, Card, Skeleton, Dropdown, Icon, TextInput, useSearch, useI18n } from '@bagelink/vue'
5
5
  import { computed, onMounted, ref, watch } from 'vue'
6
6
  import 'floating-vue/style.css'
7
7
 
8
- type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
9
8
  const props = withDefaults(defineProps<PropTypes>(), {
10
- placeholder: 'Select',
11
9
  placement: 'bottom-start',
12
10
  })
13
11
 
14
12
  const emit = defineEmits(['update:modelValue'])
15
13
 
14
+ const { $t } = useI18n()
15
+
16
+ type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
16
17
  const isAsyncSource = (src: OptionsSource): src is (q: string) => Promise<Option[]> => typeof src === 'function'
17
18
 
18
19
  type Primitive = string | number | boolean
@@ -47,9 +48,10 @@ const searchTerm = ref<string>('')
47
48
  const dropdown = ref<InstanceType<typeof Dropdown> | undefined>()
48
49
  const selected = ref(false)
49
50
  const open = ref(false)
51
+ const selectPlaceholder = computed(() => props.placeholder ?? $t('select.placeholder'))
50
52
 
51
53
  const selectedLabel = computed((): string => {
52
- if (selectedItemCount.value === 0) { return props.placeholder }
54
+ if (selectedItemCount.value === 0) { return selectPlaceholder.value }
53
55
  if (selectedItemCount.value > 4) {
54
56
  const str = selectedItems.value
55
57
  .slice(0, 4)
@@ -59,8 +61,8 @@ const selectedLabel = computed((): string => {
59
61
  }
60
62
  return selectedItems.value.map(item => getLabel(item)).join(', ')
61
63
  })
62
- const searchPlaceholder = computed(() => props.searchPlaceholder ?? selectedLabel.value ?? 'Search')
63
64
 
65
+ const searchPlaceholder = computed(() => props.searchPlaceholder ?? selectedLabel.value ?? $t('select.search'))
64
66
  const { results, isLoading } = useSearch<Option>({
65
67
  searchTerm: () => searchTerm.value,
66
68
  serverSearch: isAsyncSource(props.options) ? props.options : undefined,
@@ -113,8 +115,8 @@ function scrollToSelectedItem() {
113
115
  function getLabel(option: Option) {
114
116
  if (option == null) { return '' }
115
117
  if (typeof option === 'object') { return option.label ?? String((option as any).value ?? '') }
116
- if (option === true) { return 'Yes' }
117
- if (option === false) { return 'No' }
118
+ if (option === true) { return $t('select.yes') }
119
+ if (option === false) { return $t('select.no') }
118
120
  return String(option)
119
121
  }
120
122
 
@@ -433,9 +435,9 @@ onMounted(() => {
433
435
  .error-message {
434
436
  color: var(--bgl-red, #dc3545);
435
437
  font-size: 10px;
436
- position: absolute;
437
- inset-inline-end: 0;
438
- bottom: -0.9rem;
439
- margin-top: 0.25rem;
438
+ position: absolute;
439
+ inset-inline-end: 0;
440
+ bottom: -0.9rem;
441
+ margin-top: 0.25rem;
440
442
  }
441
443
  </style>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Country } from '@bagelink/vue'
3
3
  import type { CountryCode } from 'libphonenumber-js'
4
- import { Dropdown, Flag, Icon, TextInput, allCountries, ipapi } from '@bagelink/vue'
4
+ import { Dropdown, Flag, Icon, TextInput, allCountries, ipapi, useI18n } from '@bagelink/vue'
5
5
  import { parsePhoneNumberFromString } from 'libphonenumber-js'
6
6
  import { computed, onMounted, ref, watch } from 'vue'
7
7
 
@@ -19,6 +19,8 @@ const props = defineProps<{
19
19
 
20
20
  const emit = defineEmits(['update:modelValue', 'blur', 'focus', 'keydown', 'input', 'paste'])
21
21
 
22
+ const { $t } = useI18n()
23
+
22
24
  const phoneNumber = ref(props.modelValue ?? '')
23
25
  const search = ref('')
24
26
  const activeCountry = ref<Country>()
@@ -64,9 +66,9 @@ const activeCountryCode = computed(() => activeCountry.value?.iso2)
64
66
  const computedPlaceholder = computed(() => {
65
67
  if (props.placeholder) return props.placeholder
66
68
  if (activeCountry.value) {
67
- return `+${activeCountry.value.dialCode} ${props.label || 'Phone Number'}`
69
+ return `+${activeCountry.value.dialCode} ${props.label || $t('tel.phoneNumber')}`
68
70
  }
69
- return props.label || 'Phone Number'
71
+ return props.label || $t('tel.phoneNumber')
70
72
  })
71
73
 
72
74
  function selectCountry(country: Country) {
@@ -139,12 +141,12 @@ function validatePhoneNumber() {
139
141
  inputRef.value.setCustomValidity('')
140
142
  isValid.value = true
141
143
  } else {
142
- inputRef.value.setCustomValidity('Please enter a valid phone number')
144
+ inputRef.value.setCustomValidity($t('tel.invalidPhone'))
143
145
  isValid.value = false
144
146
  }
145
147
  } catch (error) {
146
148
  console.error('Error validating phone number:', error)
147
- inputRef.value.setCustomValidity('Please enter a valid phone number')
149
+ inputRef.value.setCustomValidity($t('tel.invalidPhone'))
148
150
  isValid.value = false
149
151
  }
150
152
  }
@@ -349,10 +351,10 @@ onMounted(initializeCountry)
349
351
  .error-message {
350
352
  color: var(--bgl-red, #dc3545);
351
353
  font-size: 10px;
352
- position: absolute;
353
- inset-inline-end: 0;
354
- bottom: -0.9rem;
355
- margin-top: 0.25rem;
354
+ position: absolute;
355
+ inset-inline-end: 0;
356
+ bottom: -0.9rem;
357
+ margin-top: 0.25rem;
356
358
  }
357
359
 
358
360
  @keyframes highlight-country {
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { UploadInputProps } from '@bagelink/vue'
3
- import { Btn, IMAGE_FORMATS_REGEXP, Icon, Card, Image, pathKeyToURL, Loading } from '@bagelink/vue'
3
+ import { Btn, IMAGE_FORMATS_REGEXP, Icon, Card, Image, pathKeyToURL, Loading, useI18n } from '@bagelink/vue'
4
4
  import { watch, ref } from 'vue'
5
5
 
6
6
  import { useFileUpload } from './useFileUpload'
@@ -15,6 +15,8 @@ const props = withDefaults(defineProps<UploadInputProps & { showIcon?: boolean,
15
15
 
16
16
  const emit = defineEmits(['update:modelValue', 'addFileStart'])
17
17
 
18
+ const { $t } = useI18n()
19
+
18
20
  const {
19
21
  fileQueue,
20
22
  pathKeys,
@@ -78,13 +80,13 @@ function fileName(pathKey: string) {
78
80
  >
79
81
  <Btn
80
82
  v-if="!pathKeys.length && !fileQueue.length" class="px-1-5" icon="upload" outline
81
- :value="btnPlaceholder || 'Upload'" @click="browse()"
83
+ :value="btnPlaceholder || $t('upload.upload')" @click="browse()"
82
84
  />
83
85
  <!-- Loading state during upload -->
84
86
  <div v-if="fileQueue.length > 0" class="flex align-items-center gap-1 p-05">
85
87
  <Loading size="20" />
86
88
  <span class="txt-gray txt-12">
87
- Uploading {{ fileQueue.length }} file{{ fileQueue.length > 1 ? 's' : '' }}...
89
+ {{ $t('upload.uploading', { count: fileQueue.length, plural: fileQueue.length > 1 ? 's' : '' }) }}
88
90
  </span>
89
91
  </div>
90
92
 
@@ -111,7 +113,7 @@ function fileName(pathKey: string) {
111
113
  </template>
112
114
 
113
115
  <span v-if="!pathKeys.length && !fileQueue.length" class="txt-gray txt-12">
114
- {{ noFilePlaceholder || 'No file selected' }}
116
+ {{ noFilePlaceholder || $t('upload.noFile') }}
115
117
  </span>
116
118
  </Card>
117
119
 
@@ -208,7 +210,7 @@ function fileName(pathKey: string) {
208
210
  <p class="p-1 flex column hover fileUploadPlaceHolder justify-content-center mb-05 ">
209
211
  <Icon v-if="showIcon" :name="icon" class="user-select-none" />
210
212
  <span class=" pretty balance user-select-none ">
211
- {{ dropPlaceholder || 'Drag and Drop files here or click to upload' }}
213
+ {{ dropPlaceholder || $t('upload.dropPlaceholder') }}
212
214
  </span>
213
215
  </p>
214
216
  </slot>
@@ -3,6 +3,9 @@ import type { LightboxItem } from './lightbox.types'
3
3
 
4
4
  import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Swiper, downloadFile } from '@bagelink/vue'
5
5
  import { computed, ref, watch } from 'vue'
6
+ import { useI18n } from '../../i18n'
7
+
8
+ const { $t } = useI18n()
6
9
 
7
10
  const isOpen = ref(false)
8
11
  const group = ref<LightboxItem[]>([])
@@ -79,7 +82,7 @@ defineExpose({ open, close })
79
82
  </div>
80
83
  <Btn
81
84
  v-if="currentItem?.openFile && currentItem?.src" class="color-white" round thin flat
82
- iconEnd="arrow_outward" value="Open File" :href="currentItem?.src" target="_blank"
85
+ iconEnd="arrow_outward" value="$t:lightbox.openFile" :href="currentItem?.src" target="_blank"
83
86
  />
84
87
  <Btn
85
88
  v-if="currentItem?.download && currentItem?.src" class="color-white" round thin flat
@@ -3,6 +3,8 @@
3
3
  * DialogConfirm - Simple confirmation dialog
4
4
  */
5
5
  import type { DialogWidth, DialogPosition } from './dialogTypes'
6
+ import { useI18n } from '@bagelink/vue'
7
+ import { computed } from 'vue'
6
8
  import Btn from '../components/Btn.vue'
7
9
  import Dialog from './Dialog.vue'
8
10
 
@@ -17,12 +19,9 @@ const props = withDefaults(defineProps<{
17
19
  cancelText?: string
18
20
  confirmColor?: 'primary' | 'red' | 'green' | 'blue'
19
21
  }>(), {
20
- title: 'Confirm',
21
22
  width: 's',
22
23
  position: 'center',
23
24
  dismissable: true,
24
- confirmText: 'Confirm',
25
- cancelText: 'Cancel',
26
25
  confirmColor: 'primary'
27
26
  })
28
27
 
@@ -31,6 +30,12 @@ const emit = defineEmits<{
31
30
  'resolve': [confirmed: boolean]
32
31
  }>()
33
32
 
33
+ const { $t } = useI18n()
34
+
35
+ const resolvedTitle = computed(() => props.title || $t('modalConfirm.title'))
36
+ const resolvedConfirmText = computed(() => props.confirmText || $t('modalConfirm.confirm'))
37
+ const resolvedCancelText = computed(() => props.cancelText || $t('modalConfirm.cancel'))
38
+
34
39
  function handleConfirm() {
35
40
  emit('resolve', true)
36
41
  emit('update:open', false)
@@ -48,7 +53,7 @@ function handleClose() {
48
53
 
49
54
  <template>
50
55
  <Dialog
51
- :open="open" :title="title" :width="width" :position="position" :dismissable="dismissable"
56
+ :open="open" :title="resolvedTitle" :width="width" :position="position" :dismissable="dismissable"
52
57
  @update:open="$emit('update:open', $event)" @close="handleClose"
53
58
  >
54
59
  <p>
@@ -57,10 +62,10 @@ function handleClose() {
57
62
 
58
63
  <template #footer>
59
64
  <Btn flat @click="handleCancel">
60
- {{ cancelText }}
65
+ {{ resolvedCancelText }}
61
66
  </Btn>
62
67
  <Btn :color="confirmColor" @click="handleConfirm">
63
- {{ confirmText }}
68
+ {{ resolvedConfirmText }}
64
69
  </Btn>
65
70
  </template>
66
71
  </Dialog>