sprintify-ui 0.0.41 → 0.0.42

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 (89) hide show
  1. package/dist/sprintify-ui.es.js +6033 -5518
  2. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +32 -12
  3. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +28 -28
  4. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +35 -35
  5. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +46 -8
  6. package/dist/types/src/components/BaseDatePicker.vue.d.ts +18 -9
  7. package/dist/types/src/components/BaseDateSelect.vue.d.ts +14 -5
  8. package/dist/types/src/components/BaseField.vue.d.ts +151 -0
  9. package/dist/types/src/components/BaseFieldI18n.vue.d.ts +93 -0
  10. package/dist/types/src/components/BaseForm.vue.d.ts +267 -0
  11. package/dist/types/src/components/BaseFormField.d.ts +81 -0
  12. package/dist/types/src/components/BaseHasMany.vue.d.ts +31 -31
  13. package/dist/types/src/components/BaseInput.vue.d.ts +1 -1
  14. package/dist/types/src/components/BaseInputError.vue.d.ts +48 -0
  15. package/dist/types/src/components/BaseInputPercent.vue.d.ts +1 -1
  16. package/dist/types/src/components/BaseLocaleForm.vue.d.ts +420 -0
  17. package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +46 -24
  18. package/dist/types/src/components/BaseNumberForm.vue.d.ts +382 -0
  19. package/dist/types/src/components/BasePassword.vue.d.ts +10 -14
  20. package/dist/types/src/components/BasePasswordForm.vue.d.ts +365 -0
  21. package/dist/types/src/components/BaseRadioGroup.vue.d.ts +23 -4
  22. package/dist/types/src/components/BaseSelect.vue.d.ts +20 -1
  23. package/dist/types/src/components/BaseSwitch.vue.d.ts +155 -23
  24. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +31 -12
  25. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +20 -20
  26. package/dist/types/src/components/BaseTextarea.vue.d.ts +9 -0
  27. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +18 -0
  28. package/dist/types/src/components/BaseTextareaForm.vue.d.ts +394 -0
  29. package/dist/types/src/components/index.d.ts +4 -1
  30. package/dist/types/src/composables/field.d.ts +17 -0
  31. package/dist/types/src/index.d.ts +3 -0
  32. package/dist/types/src/types/index.d.ts +11 -0
  33. package/package.json +4 -1
  34. package/src/components/BaseAutocomplete.stories.js +56 -51
  35. package/src/components/BaseAutocomplete.vue +25 -8
  36. package/src/components/BaseAutocompleteFetch.stories.js +67 -65
  37. package/src/components/BaseAutocompleteFetch.vue +9 -29
  38. package/src/components/BaseBelongsTo.stories.js +72 -82
  39. package/src/components/BaseBelongsTo.vue +10 -11
  40. package/src/components/BaseButtonGroup.stories.js +11 -10
  41. package/src/components/BaseButtonGroup.vue +22 -9
  42. package/src/components/BaseCharacterCounter.stories.js +1 -1
  43. package/src/components/BaseDatePicker.stories.js +13 -9
  44. package/src/components/BaseDatePicker.vue +25 -8
  45. package/src/components/BaseDateSelect.stories.js +15 -9
  46. package/src/components/BaseDateSelect.vue +20 -8
  47. package/src/components/BaseField.vue +109 -0
  48. package/src/components/BaseFieldI18n.stories.js +38 -0
  49. package/src/components/BaseFieldI18n.vue +162 -0
  50. package/src/components/BaseFileUploader.stories.js +3 -3
  51. package/src/components/BaseFileUploader.vue +3 -3
  52. package/src/components/BaseForm.vue +298 -0
  53. package/src/components/BaseFormField.ts +117 -0
  54. package/src/components/BaseHasMany.stories.js +25 -10
  55. package/src/components/BaseHasMany.vue +9 -9
  56. package/src/components/BaseInput.stories.js +27 -14
  57. package/src/components/BaseInput.vue +17 -8
  58. package/src/components/BaseInputError.vue +7 -0
  59. package/src/components/BaseInputPercent.stories.js +10 -3
  60. package/src/components/BaseInputPercent.vue +2 -1
  61. package/src/components/BaseLocaleForm.vue +142 -0
  62. package/src/components/BaseMediaLibrary.stories.js +7 -6
  63. package/src/components/BaseMediaLibrary.vue +32 -31
  64. package/src/components/BaseMenu.vue +1 -1
  65. package/src/components/BaseNumberForm.vue +67 -0
  66. package/src/components/BasePassword.stories.js +9 -4
  67. package/src/components/BasePassword.vue +49 -44
  68. package/src/components/BasePasswordForm.vue +59 -0
  69. package/src/components/BaseRadioGroup.stories.js +9 -8
  70. package/src/components/BaseRadioGroup.vue +17 -3
  71. package/src/components/BaseSelect.stories.js +15 -2
  72. package/src/components/BaseSelect.vue +26 -10
  73. package/src/components/BaseSwitch.stories.js +7 -0
  74. package/src/components/BaseSwitch.vue +134 -124
  75. package/src/components/BaseTagAutocomplete.stories.js +21 -14
  76. package/src/components/BaseTagAutocomplete.vue +25 -14
  77. package/src/components/BaseTagAutocompleteFetch.stories.js +37 -21
  78. package/src/components/BaseTagAutocompleteFetch.vue +5 -5
  79. package/src/components/BaseTextarea.stories.js +11 -3
  80. package/src/components/BaseTextarea.vue +20 -6
  81. package/src/components/BaseTextareaAutoresize.stories.js +11 -2
  82. package/src/components/BaseTextareaAutoresize.vue +28 -4
  83. package/src/components/BaseTextareaForm.vue +101 -0
  84. package/src/components/BaseTimeline.vue +1 -1
  85. package/src/components/BaseTimelineItem.vue +4 -4
  86. package/src/components/index.ts +6 -0
  87. package/src/composables/field.ts +100 -0
  88. package/src/index.ts +11 -1
  89. package/src/types/index.ts +12 -0
@@ -32,12 +32,13 @@ import { PropType } from 'vue';
32
32
  import { NormalizedOption, Option } from '@/types';
33
33
  import { cloneDeep, isArray } from 'lodash';
34
34
  import { useHasOptions } from '@/composables/hasOptions';
35
+ import { useField } from '@/composables/field';
35
36
 
36
37
  const props = defineProps({
37
38
  modelValue: {
38
39
  default: undefined,
39
- type: [Array, String, Number, null] as PropType<
40
- Option[] | Option | undefined
40
+ type: [Object, Array, null, undefined] as PropType<
41
+ Option[] | Option | null | undefined
41
42
  >,
42
43
  },
43
44
  required: {
@@ -84,10 +85,25 @@ const props = defineProps({
84
85
  default: false,
85
86
  type: Boolean,
86
87
  },
88
+ name: {
89
+ default: undefined,
90
+ type: String,
91
+ },
92
+ hasError: {
93
+ default: false,
94
+ type: Boolean,
95
+ },
87
96
  });
88
97
 
89
98
  const emit = defineEmits(['update:modelValue']);
90
99
 
100
+ const { requiredInternal, emitUpdate } = useField({
101
+ name: computed(() => props.name),
102
+ required: computed(() => props.required),
103
+ hasError: computed(() => props.hasError),
104
+ emit: emit,
105
+ });
106
+
91
107
  const { normalizedOptions, normalizedModelValue, isSelected } = useHasOptions(
92
108
  computed(() => props.modelValue),
93
109
  computed(() => props.options),
@@ -112,23 +128,20 @@ function onSelect(option: NormalizedOption) {
112
128
  newModalValue.push(option);
113
129
  }
114
130
 
115
- emit(
116
- 'update:modelValue',
117
- newModalValue.map((o) => o.option)
118
- );
131
+ emitUpdate(newModalValue.map((o) => o.option));
119
132
  } else {
120
- if (!props.required) {
133
+ if (!requiredInternal.value) {
121
134
  if (
122
135
  !isArray(normalizedModelValue.value) &&
123
136
  option.value == normalizedModelValue.value?.value
124
137
  ) {
125
- emit('update:modelValue', null);
138
+ emitUpdate(null);
126
139
  return;
127
140
  }
128
141
  }
129
142
 
130
143
  const newOption = option.option;
131
- emit('update:modelValue', newOption);
144
+ emitUpdate(newOption);
132
145
  }
133
146
  }
134
147
  </script>
@@ -1,5 +1,5 @@
1
1
  import BaseCharacterCounter from './BaseCharacterCounter.vue';
2
- import BaseInput from './BaseInput.vue';
2
+ import BaseInput from '@/components/BaseInput.vue';
3
3
 
4
4
  export default {
5
5
  title: 'Components/BaseCharacterCounter',
@@ -1,5 +1,7 @@
1
1
  import BaseDatePicker from './BaseDatePicker.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
2
3
  import { DateTime } from 'luxon';
4
+ import { createFieldStory } from '../../.storybook/utils';
3
5
 
4
6
  export default {
5
7
  title: 'Form/BaseDatePicker',
@@ -8,36 +10,32 @@ export default {
8
10
  };
9
11
 
10
12
  const Template = (args) => ({
11
- components: { BaseDatePicker },
13
+ components: { BaseDatePicker, ShowValue },
12
14
  setup() {
13
- return { args };
15
+ const value = ref(null);
16
+ return { value, args };
14
17
  },
15
18
  template: `
16
- <BaseDatePicker v-bind="args">
19
+ <BaseDatePicker v-model="value" v-bind="args">
17
20
  </BaseDatePicker>
21
+ <ShowValue :value="value" />
18
22
  `,
19
23
  });
20
24
 
21
25
  export const Demo = Template.bind({});
22
- Demo.args = {
23
- modelValue: '2023-01-01',
24
- };
25
26
 
26
27
  export const YearRange = Template.bind({});
27
28
  YearRange.args = {
28
- modelValue: '1980-11-16',
29
29
  yearRange: [1920, 2020],
30
30
  };
31
31
 
32
32
  export const MinDate = Template.bind({});
33
33
  MinDate.args = {
34
- modelValue: '2022-11-16',
35
34
  minDate: DateTime.fromISO('2022-11-10').toJSDate(),
36
35
  };
37
36
 
38
37
  export const MaxDate = Template.bind({});
39
38
  MaxDate.args = {
40
- modelValue: '2022-11-16',
41
39
  maxDate: DateTime.fromISO('2022-11-20').toJSDate(),
42
40
  };
43
41
 
@@ -46,3 +44,9 @@ Disabled.args = {
46
44
  modelValue: '2022-11-16',
47
45
  disabled: true,
48
46
  };
47
+
48
+ export const Field = createFieldStory({
49
+ component: BaseDatePicker,
50
+ componentName: 'BaseDatePicker',
51
+ label: 'Date',
52
+ });
@@ -14,8 +14,8 @@
14
14
  type="text"
15
15
  readonly
16
16
  :disabled="disabled"
17
- class="w-full rounded pl-10 pr-16 disabled:cursor-not-allowed disabled:text-gray-300"
18
- :class="inputClass"
17
+ class="w-full rounded pl-10 pr-16 disabled:cursor-not-allowed disabled:text-slate-300"
18
+ :class="[hasErrorInternal ? 'border-red-500' : 'border-slate-300']"
19
19
  :placeholder="$t('sui.click_or_select_date')"
20
20
  />
21
21
  <div
@@ -38,6 +38,8 @@ import { PropType, Ref } from 'vue';
38
38
  import Pikaday from 'pikaday';
39
39
  import { capitalize, padStart } from 'lodash';
40
40
  import { DateTime, Info } from 'luxon';
41
+ import { BaseIcon } from '.';
42
+ import { useField } from '@/composables/field';
41
43
 
42
44
  const props = defineProps({
43
45
  modelValue: {
@@ -52,10 +54,6 @@ const props = defineProps({
52
54
  default: false,
53
55
  type: Boolean,
54
56
  },
55
- inputClass: {
56
- default: 'border-slate-300',
57
- type: String,
58
- },
59
57
  minDate: {
60
58
  default: undefined,
61
59
  type: Date,
@@ -68,10 +66,25 @@ const props = defineProps({
68
66
  default: undefined,
69
67
  type: [Number, Array] as PropType<number | [number, number]>,
70
68
  },
69
+ hasError: {
70
+ default: false,
71
+ type: Boolean,
72
+ },
73
+ name: {
74
+ default: undefined,
75
+ type: String,
76
+ },
71
77
  });
72
78
 
73
79
  const emit = defineEmits(['update:modelValue']);
74
80
 
81
+ const { hasErrorInternal, emitUpdate } = useField({
82
+ name: computed(() => props.name),
83
+ required: computed(() => props.required),
84
+ hasError: computed(() => props.hasError),
85
+ emit: emit,
86
+ });
87
+
75
88
  const i18n = useI18n();
76
89
 
77
90
  const datepicker = ref(null) as Ref<HTMLInputElement | null>;
@@ -118,6 +131,10 @@ onMounted(() => {
118
131
  return `${year}-${month}-${day}`;
119
132
  },
120
133
  parse(dateString) {
134
+ if (!dateString) {
135
+ return null;
136
+ }
137
+
121
138
  const datetime = DateTime.fromISO(dateString);
122
139
  const year = datetime.year;
123
140
  const month = datetime.month - 1;
@@ -140,7 +157,7 @@ onMounted(() => {
140
157
  return;
141
158
  }
142
159
 
143
- emit('update:modelValue', datetime.toISODate());
160
+ emitUpdate(datetime.toISODate());
144
161
  },
145
162
  });
146
163
  });
@@ -153,7 +170,7 @@ onBeforeUnmount(() => {
153
170
 
154
171
  function clear() {
155
172
  picker?.clear();
156
- emit('update:modelValue', null);
173
+ emitUpdate(null);
157
174
  }
158
175
 
159
176
  // https://stackoverflow.com/a/23368052
@@ -1,4 +1,6 @@
1
1
  import BaseDateSelect from './BaseDateSelect.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
+ import { createFieldStory } from '../../.storybook/utils';
2
4
 
3
5
  export default {
4
6
  title: 'Form/BaseDateSelect',
@@ -7,30 +9,28 @@ export default {
7
9
  };
8
10
 
9
11
  const Template = (args) => ({
10
- components: { BaseDateSelect },
12
+ components: { BaseDateSelect, ShowValue },
11
13
  setup() {
12
- return { args };
14
+ const value = ref(null);
15
+ return { value, args };
13
16
  },
14
17
  template: `
15
- <BaseDateSelect v-bind="args">
16
- </BaseDateSelect>
18
+ <BaseDateSelect v-model="value" v-bind="args">
19
+ </BaseDateSelect>
20
+ <ShowValue :value="value" />
17
21
  `,
18
22
  });
19
23
 
20
24
  export const Demo = Template.bind({});
21
- Demo.args = {
22
- modelValue: '2021-01-01',
23
- };
25
+ Demo.args = {};
24
26
 
25
27
  export const MinYear = Template.bind({});
26
28
  MinYear.args = {
27
- modelValue: '2022-11-16',
28
29
  minYear: 1980,
29
30
  };
30
31
 
31
32
  export const MaxYear = Template.bind({});
32
33
  MaxYear.args = {
33
- modelValue: '2002-11-16',
34
34
  maxYear: 2010,
35
35
  };
36
36
 
@@ -39,3 +39,9 @@ Disabled.args = {
39
39
  modelValue: '2022-11-16',
40
40
  disabled: true,
41
41
  };
42
+
43
+ export const Field = createFieldStory({
44
+ component: BaseDateSelect,
45
+ componentName: 'BaseDateSelect',
46
+ label: 'Date',
47
+ });
@@ -12,7 +12,7 @@
12
12
  {
13
13
  'cursor-not-allowed bg-slate-100 text-slate-500': disabled,
14
14
  },
15
- inputClass,
15
+ [hasErrorInternal ? 'border-red-500' : 'border-slate-300'],
16
16
  ]"
17
17
  :placeholder="$t('sui.year')"
18
18
  @change="update()"
@@ -37,7 +37,7 @@
37
37
  {
38
38
  'cursor-not-allowed bg-slate-100 text-slate-500': disabled,
39
39
  },
40
- inputClass,
40
+ [hasErrorInternal ? 'border-red-500' : 'border-slate-300'],
41
41
  ]"
42
42
  :placeholder="$t('sui.month')"
43
43
  @change="update()"
@@ -62,7 +62,7 @@
62
62
  {
63
63
  'cursor-not-allowed bg-slate-100 text-slate-500': dayDisabled,
64
64
  },
65
- inputClass,
65
+ [hasErrorInternal ? 'border-red-500' : 'border-slate-300'],
66
66
  ]"
67
67
  :placeholder="$t('sui.day')"
68
68
  @change="update()"
@@ -92,6 +92,7 @@
92
92
  import { PropType } from 'vue';
93
93
  import { range, padStart } from 'lodash';
94
94
  import { DateTime, Info } from 'luxon';
95
+ import { useField } from '@/composables/field';
95
96
 
96
97
  const props = defineProps({
97
98
  modelValue: {
@@ -114,14 +115,25 @@ const props = defineProps({
114
115
  default: DateTime.now().year,
115
116
  type: Number,
116
117
  },
117
- inputClass: {
118
- default: 'border-slate-300',
118
+ name: {
119
+ default: undefined,
119
120
  type: String,
120
121
  },
122
+ hasError: {
123
+ default: false,
124
+ type: Boolean,
125
+ },
121
126
  });
122
127
 
123
128
  const emit = defineEmits(['update:modelValue']);
124
129
 
130
+ const { hasErrorInternal, emitUpdate } = useField({
131
+ name: computed(() => props.name),
132
+ required: computed(() => props.required),
133
+ hasError: computed(() => props.hasError),
134
+ emit: emit,
135
+ });
136
+
125
137
  const i18n = useI18n();
126
138
 
127
139
  const years = range(props.maxYear, props.minYear) as number[];
@@ -162,9 +174,9 @@ function update() {
162
174
  const dateTime = getDateTime();
163
175
 
164
176
  if (dateTime) {
165
- emit('update:modelValue', dateTime.toISODate());
177
+ emitUpdate(dateTime.toISODate());
166
178
  } else {
167
- emit('update:modelValue', null);
179
+ emitUpdate(null);
168
180
  }
169
181
  }
170
182
 
@@ -173,7 +185,7 @@ function clear() {
173
185
  date.value.month = null;
174
186
  date.value.year = null;
175
187
 
176
- emit('update:modelValue', null);
188
+ emitUpdate(null);
177
189
  }
178
190
 
179
191
  function getDateTime(): DateTime | null {
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div :data-name="name">
3
+ <BaseInputLabel
4
+ v-if="labelNormalized"
5
+ :label="labelNormalized"
6
+ :required="required"
7
+ :class="labelClassInternal"
8
+ />
9
+ <slot></slot>
10
+ <template v-if="errorMessage">
11
+ <BaseInputError v-if="errorTypeInternal == 'default'" class="mt-1">
12
+ {{ errorMessage }}
13
+ </BaseInputError>
14
+ <BaseAlert
15
+ v-else-if="errorTypeInternal == 'alert'"
16
+ bordered
17
+ color="danger"
18
+ class="mt-4"
19
+ >
20
+ {{ errorMessage }}
21
+ </BaseAlert>
22
+ </template>
23
+ </div>
24
+ </template>
25
+
26
+ <script lang="ts" setup>
27
+ import { PropType } from 'vue';
28
+ import BaseAlert from './BaseAlert.vue';
29
+ import BaseInputError from './BaseInputError.vue';
30
+ import BaseInputLabel from './BaseInputLabel.vue';
31
+
32
+ const props = defineProps({
33
+ name: {
34
+ default: '',
35
+ type: String,
36
+ },
37
+ label: {
38
+ type: String,
39
+ default: '',
40
+ },
41
+ required: {
42
+ type: Boolean,
43
+ default: false,
44
+ },
45
+ errorType: {
46
+ type: String as PropType<'default' | 'alert'>,
47
+ default: 'default',
48
+ },
49
+ labelClass: {
50
+ default: '',
51
+ type: [String, Array, Object] as PropType<
52
+ string | string[] | Record<string, boolean>
53
+ >,
54
+ },
55
+ });
56
+
57
+ const errorTypeInternal = ref(props.errorType);
58
+
59
+ function setErrorType(errorType: 'default' | 'alert' | null) {
60
+ if (errorType != null) {
61
+ errorTypeInternal.value = errorType;
62
+ }
63
+ }
64
+
65
+ const labelClassInternal = ref(props.labelClass);
66
+
67
+ function setLabelClass(
68
+ labelClass: string | string[] | Record<string, boolean> | null
69
+ ) {
70
+ if (labelClass != null) {
71
+ labelClassInternal.value = labelClass;
72
+ }
73
+ }
74
+
75
+ const getErrorMessageByName = inject(
76
+ 'form:getErrorMessageByName',
77
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
78
+ (name: string) => {
79
+ return null;
80
+ }
81
+ ) as (name: string) => string | null | undefined;
82
+
83
+ const clearErrors = inject('form:clearErrors', () => {
84
+ return;
85
+ }) as () => void;
86
+
87
+ function fieldOnUpdate() {
88
+ clearErrors();
89
+ }
90
+
91
+ const labelNormalized = computed((): string | null => {
92
+ if (props.label) {
93
+ return props.label;
94
+ }
95
+
96
+ return null;
97
+ });
98
+
99
+ const errorMessage = computed((): string | null | undefined => {
100
+ return getErrorMessageByName(props.name);
101
+ });
102
+
103
+ provide('field:name', readonly(ref(props.name)));
104
+ provide('field:required', readonly(ref(props.required)));
105
+ provide('field:onUpdate', fieldOnUpdate);
106
+ provide('field:errorMessage', errorMessage);
107
+ provide('field:setErrorType', setErrorType);
108
+ provide('field:setLabelClass', setLabelClass);
109
+ </script>
@@ -0,0 +1,38 @@
1
+ import BaseFieldI18n from './BaseFieldI18n.vue';
2
+ import BaseForm from './BaseForm.vue';
3
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
4
+
5
+ export default {
6
+ title: 'Form/BaseFieldI18n',
7
+ component: BaseFieldI18n,
8
+ decorators: [
9
+ (story) => ({
10
+ components: { story, BaseForm },
11
+ template: `
12
+ <BaseForm method="post" url="https://api.com/todos/422" :data="{}">
13
+ <story/>
14
+ <button type="submit" class="btn btn-primary mt-5">Submit</button>
15
+ </BaseForm>`,
16
+ }),
17
+ ],
18
+ args: {
19
+ component: 'BaseInput',
20
+ required: true,
21
+ name: 'name',
22
+ label: 'Name',
23
+ },
24
+ };
25
+
26
+ const Template = (args) => ({
27
+ components: { BaseFieldI18n, ShowValue },
28
+ setup() {
29
+ const value = ref(null);
30
+ return { args, value };
31
+ },
32
+ template: `
33
+ <BaseFieldI18n v-model="value" v-bind="args" class="w-full"></BaseFieldI18n>
34
+ <ShowValue :value="value" />
35
+ `,
36
+ });
37
+
38
+ export const Demo = Template.bind({});
@@ -0,0 +1,162 @@
1
+ <template>
2
+ <div :data-name="nameInternal">
3
+ <div class="space-y-2">
4
+ <div v-for="(locale, key) in localesInternal" :key="key">
5
+ <BaseField
6
+ :name="`${nameInternal}.${key}`"
7
+ :required="requiredInternal"
8
+ :label="getLabel(locale)"
9
+ >
10
+ <BaseInput
11
+ v-if="component == 'BaseInput'"
12
+ :model-value="formattedValue[key] + ''"
13
+ class="w-full bg-white"
14
+ v-bind="componentProps"
15
+ @update:model-value="onInput($event, key + '')"
16
+ />
17
+ <BaseTextarea
18
+ v-else-if="component == 'BaseTextarea'"
19
+ :model-value="formattedValue[key] + ''"
20
+ :required="requiredInternal"
21
+ :name="`${nameInternal}.${key}`"
22
+ class="w-full bg-white"
23
+ v-bind="componentProps"
24
+ @update:model-value="onInput($event, key + '')"
25
+ />
26
+ </BaseField>
27
+ </div>
28
+ </div>
29
+ <BaseAlert v-if="globalErrorMessage" class="mt-3" bordered color="danger">
30
+ {{ globalErrorMessage }}
31
+ </BaseAlert>
32
+ </div>
33
+ </template>
34
+
35
+ <script lang="ts" setup>
36
+ import { Locales } from '@/types';
37
+ import { get, isPlainObject } from 'lodash';
38
+ import objectHash from 'object-hash';
39
+ import { PropType } from 'vue';
40
+ import { config } from '..';
41
+ import { useField } from '@/composables/field';
42
+ import BaseInput from './BaseInput.vue';
43
+ import BaseTextarea from './BaseTextarea.vue';
44
+ import BaseField from './BaseField.vue';
45
+ import BaseAlert from './BaseAlert.vue';
46
+
47
+ defineComponent({
48
+ components: {
49
+ BaseInput,
50
+ BaseTextarea,
51
+ },
52
+ });
53
+
54
+ type Value = { [locale: string]: string | number | boolean };
55
+
56
+ const props = defineProps({
57
+ modelValue: {
58
+ required: true,
59
+ type: [Object, null, undefined] as PropType<Value | null | undefined>,
60
+ },
61
+ locales: {
62
+ default: undefined,
63
+ type: Object as PropType<Locales>,
64
+ },
65
+ component: {
66
+ default: 'BaseInput',
67
+ type: String as PropType<'BaseInput' | 'BaseTextarea'>,
68
+ },
69
+ componentProps: {
70
+ default: undefined,
71
+ type: Object,
72
+ },
73
+ defaultValue: {
74
+ default: '',
75
+ type: [String, Boolean, Number] as PropType<string | boolean | number>,
76
+ },
77
+ name: {
78
+ default: undefined,
79
+ type: String,
80
+ },
81
+ required: {
82
+ default: false,
83
+ type: Boolean,
84
+ },
85
+ hasError: {
86
+ default: false,
87
+ type: Boolean,
88
+ },
89
+ label: {
90
+ default: undefined,
91
+ type: String,
92
+ },
93
+ });
94
+
95
+ const emit = defineEmits(['update:modelValue']);
96
+
97
+ const { nameInternal, requiredInternal } = useField({
98
+ name: computed(() => props.name),
99
+ required: computed(() => props.required),
100
+ hasError: computed(() => props.hasError),
101
+ emit: emit,
102
+ });
103
+
104
+ const getErrorMessageByName = inject(
105
+ 'form:getErrorMessageByName',
106
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
+ (name: string) => {
108
+ return null;
109
+ }
110
+ ) as (name: string) => string | null | undefined;
111
+
112
+ const colors = ref(['#10b981', '#06b6d4', '#a855f7']);
113
+
114
+ const localesInternal = computed((): Locales => {
115
+ if (props.locales) {
116
+ return props.locales;
117
+ }
118
+ return config.locales;
119
+ });
120
+
121
+ const formattedValue = computed((): Value => {
122
+ // Get current value
123
+ let value = {} as Value;
124
+ if (props.modelValue && isPlainObject(props.modelValue)) {
125
+ value = props.modelValue as Value;
126
+ }
127
+ // Fill missing locales
128
+ Object.keys(localesInternal.value).forEach((locale) => {
129
+ const currentValue = get(props.modelValue, locale);
130
+ if (!currentValue) {
131
+ value[locale] = props.defaultValue;
132
+ }
133
+ });
134
+ return value;
135
+ });
136
+
137
+ // If formatted value is different, send event to parent
138
+ if (
139
+ !props.modelValue ||
140
+ objectHash(formattedValue.value) !== objectHash(props.modelValue)
141
+ ) {
142
+ const newFormattedValue = formattedValue.value;
143
+ emit('update:modelValue', newFormattedValue);
144
+ }
145
+
146
+ function onInput(value: string, locale: string) {
147
+ const newFormattedValue = formattedValue.value;
148
+ newFormattedValue[locale] = value;
149
+ emit('update:modelValue', newFormattedValue);
150
+ }
151
+
152
+ function getLabel(locale: string): string | undefined {
153
+ if (!props.label) {
154
+ return undefined;
155
+ }
156
+ return `${props.label} (${locale})`;
157
+ }
158
+
159
+ const globalErrorMessage = computed(() => {
160
+ return getErrorMessageByName(nameInternal.value);
161
+ });
162
+ </script>
@@ -1,6 +1,6 @@
1
- import BaseFileUploader from './BaseFileUploader.vue';
2
- import BaseLoadingCover from './BaseLoadingCover.vue';
3
- import BaseAppNotifications from './BaseAppNotifications.vue';
1
+ import BaseFileUploader from '@/components/BaseFileUploader.vue';
2
+ import BaseLoadingCover from '@/components/BaseLoadingCover.vue';
3
+ import BaseAppNotifications from '@/components/BaseAppNotifications.vue';
4
4
  import { Icon as BaseIcon } from '@iconify/vue';
5
5
 
6
6
  export default {
@@ -36,9 +36,9 @@
36
36
  import { config } from '@/index';
37
37
  import { PropType } from 'vue';
38
38
  import { UploadedFile } from '@/types/UploadedFile';
39
- import { toHumanList, fileSizeFormat } from '../utils';
40
- import { useNotificationsStore } from '../stores/notifications';
41
- import BaseLoadingCover from './BaseLoadingCover.vue';
39
+ import { toHumanList, fileSizeFormat } from '@/utils';
40
+ import { useNotificationsStore } from '@/stores/notifications';
41
+ import BaseLoadingCover from '@/components/BaseLoadingCover.vue';
42
42
  import BaseFilePicker from './BaseFilePicker.vue';
43
43
 
44
44
  const http = config.http;