sprintify-ui 0.0.164 → 0.0.165

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.
@@ -0,0 +1,303 @@
1
+ <template>
2
+ <BaseField
3
+ :label="$t('sui.address')"
4
+ :name="`${namePrefix}address_1`"
5
+ class="mb-2"
6
+ required
7
+ >
8
+ <BaseInput
9
+ ref="address1Ref"
10
+ :model-value="normalizedModelValue.address_1 ?? ''"
11
+ :placeholder="$t('sui.address_1_placeholder')"
12
+ class="w-full"
13
+ prevent-submit
14
+ :autocomplete="false"
15
+ :required="false"
16
+ @update:model-value="update('address_1', $event)"
17
+ />
18
+ </BaseField>
19
+
20
+ <BaseField :name="`${namePrefix}address_2`" class="mb-4">
21
+ <BaseInput
22
+ :model-value="normalizedModelValue.address_2 ?? ''"
23
+ :placeholder="$t('sui.address_2_description')"
24
+ class="w-full"
25
+ @update:model-value="update('address_2', $event)"
26
+ />
27
+ </BaseField>
28
+
29
+ <div class="sm:flex sm:space-x-3">
30
+ <BaseField
31
+ :label="$t('sui.city')"
32
+ required
33
+ :name="`${namePrefix}city`"
34
+ class="mb-4 flex-1"
35
+ >
36
+ <BaseInput
37
+ :model-value="normalizedModelValue.city ?? ''"
38
+ class="w-full"
39
+ @update:model-value="update('city', $event)"
40
+ />
41
+ </BaseField>
42
+ <BaseField
43
+ :label="$t('sui.postal_code_zip_code')"
44
+ required
45
+ :name="`${namePrefix}postal_code`"
46
+ class="mb-4 flex-1"
47
+ >
48
+ <BaseInput
49
+ :model-value="normalizedModelValue.postal_code ?? ''"
50
+ class="w-full"
51
+ @update:model-value="update('postal_code', $event)"
52
+ />
53
+ </BaseField>
54
+ </div>
55
+ <div class="sm:flex sm:space-x-3">
56
+ <BaseField
57
+ :label="$t('sui.country')"
58
+ :name="`${namePrefix}country`"
59
+ required
60
+ class="mb-4 flex-1"
61
+ >
62
+ <BaseSelect
63
+ :model-value="normalizedModelValue.country ?? ''"
64
+ class="w-full"
65
+ @update:model-value="update('country', $event)"
66
+ >
67
+ <option
68
+ v-for="country in countries"
69
+ :key="country.id"
70
+ :value="country.id"
71
+ >
72
+ {{ country.name }}
73
+ </option>
74
+ </BaseSelect>
75
+ </BaseField>
76
+ <BaseField
77
+ :label="$t('sui.region')"
78
+ :name="`${namePrefix}country`"
79
+ required
80
+ class="mb-4 flex-1"
81
+ >
82
+ <BaseSelect
83
+ :model-value="normalizedModelValue.region ?? ''"
84
+ :label="$t('sui.region')"
85
+ :name="`${namePrefix}region`"
86
+ class="w-full"
87
+ required
88
+ @update:model-value="update('region', $event)"
89
+ >
90
+ <option v-for="region in regions" :key="region.id" :value="region.id">
91
+ {{ region.name }}
92
+ </option>
93
+ </BaseSelect>
94
+ </BaseField>
95
+ </div>
96
+ </template>
97
+
98
+ <script lang="ts" setup>
99
+ import { ComputedRef } from 'vue';
100
+ import { cloneDeep, isArray } from 'lodash';
101
+ import { Country } from '@/types/Country';
102
+ import { Region } from '@/types/Region';
103
+ import BaseField from './BaseField.vue';
104
+ import BaseInput from './BaseInput.vue';
105
+ import BaseSelect from './BaseSelect.vue';
106
+ import { config } from '..';
107
+
108
+ const props = withDefaults(
109
+ defineProps<{
110
+ modelValue: Record<string, string | number | null | undefined>;
111
+ prefix: string | null;
112
+ countries?: Country[];
113
+ regions?: Region[];
114
+ }>(),
115
+ {
116
+ modelValue() {
117
+ return {};
118
+ },
119
+ prefix: null,
120
+ countries() {
121
+ return [];
122
+ },
123
+ regions() {
124
+ return [];
125
+ },
126
+ }
127
+ );
128
+
129
+ const emit = defineEmits(['update:model-value']);
130
+
131
+ const normalizedModelValue = computed(
132
+ (): Record<string, string | number | null | undefined> => {
133
+ const form = cloneDeep(props.modelValue ?? {});
134
+ form.address_1 = form.address_1 ?? '';
135
+ form.address_2 = form.address_2 ?? '';
136
+ form.city = form.city ?? '';
137
+ form.postal_code = form.postal_code ?? '';
138
+ form.country = form.country ?? '';
139
+ form.region = form.region ?? '';
140
+ return form;
141
+ }
142
+ );
143
+
144
+ const countries = computed((): Country[] => {
145
+ if (props.countries && isArray(props.countries) && props.countries.length) {
146
+ return props.countries;
147
+ }
148
+ return config.countries;
149
+ });
150
+
151
+ const allRegions = computed((): Region[] => {
152
+ if (props.regions && isArray(props.regions) && props.regions.length) {
153
+ return props.regions;
154
+ }
155
+ return config.regions;
156
+ });
157
+
158
+ const regions = computed((): Country[] => {
159
+ return allRegions.value.filter(
160
+ (r) => r.country_id == normalizedModelValue.value.country
161
+ );
162
+ });
163
+
164
+ const namePrefix = computed((): string => {
165
+ if (props.prefix) {
166
+ return props.prefix + '.';
167
+ }
168
+ return '';
169
+ });
170
+
171
+ function update(field: string, value: string) {
172
+ const newForm = cloneDeep(normalizedModelValue.value);
173
+ newForm[field] = value;
174
+ emit('update:model-value', newForm);
175
+ }
176
+
177
+ // Autocomplete
178
+
179
+ const address1Ref = ref<InstanceType<typeof BaseInput> | null>(null);
180
+ const address1Input = computed(
181
+ () => address1Ref.value?.$refs.input ?? null
182
+ ) as ComputedRef<HTMLInputElement | null>;
183
+
184
+ // eslint-disable-next-line no-undef
185
+ let autocomplete = null as google.maps.places.Autocomplete | null;
186
+
187
+ onMounted(() => {
188
+ if (!window.google) {
189
+ return;
190
+ }
191
+
192
+ if (!address1Input.value) {
193
+ return;
194
+ }
195
+
196
+ autocomplete = new window.google.maps.places.Autocomplete(
197
+ address1Input.value,
198
+ {
199
+ fields: ['address_components'],
200
+ types: ['address'],
201
+ componentRestrictions: { country: 'ca' },
202
+ }
203
+ );
204
+
205
+ autocomplete.addListener('place_changed', fillAddress);
206
+ });
207
+
208
+ function fillAddress() {
209
+ if (!autocomplete) {
210
+ return;
211
+ }
212
+
213
+ // Get the place details from the autocomplete object.
214
+ const place = autocomplete.getPlace();
215
+ let address1 = '';
216
+ let postcode = '';
217
+
218
+ const newForm = cloneDeep(props.modelValue);
219
+
220
+ if (!place.address_components) {
221
+ return;
222
+ }
223
+
224
+ // Get each component of the address from the place details,
225
+ // and then fill-in the corresponding field on the form.
226
+ // place.address_components are google.maps.GeocoderAddressComponent objects
227
+ // which are documented at http://goo.gle/3l5i5Mr
228
+ // eslint-disable-next-line no-undef
229
+ for (const component of place.address_components as google.maps.GeocoderAddressComponent[]) {
230
+ const componentType = component.types[0];
231
+ switch (componentType) {
232
+ case 'street_number': {
233
+ address1 = `${component.long_name} ${address1}`;
234
+ break;
235
+ }
236
+ case 'route': {
237
+ address1 += component.long_name;
238
+ break;
239
+ }
240
+ case 'postal_code': {
241
+ postcode = `${component.long_name}${postcode}`;
242
+ break;
243
+ }
244
+ case 'postal_code_suffix': {
245
+ postcode = `${postcode}-${component.long_name}`;
246
+ break;
247
+ }
248
+ case 'locality':
249
+ newForm.city = component.long_name;
250
+ break;
251
+ case 'administrative_area_level_1': {
252
+ newForm.region = component.short_name.toLowerCase();
253
+ break;
254
+ }
255
+ case 'country':
256
+ newForm.country = component.short_name.toLowerCase();
257
+ break;
258
+ }
259
+ }
260
+
261
+ nextTick(() => {
262
+ newForm.address_1 = address1;
263
+
264
+ // Force value change
265
+ if (address1Input.value) {
266
+ //address1Input.value.value = address1;
267
+ }
268
+
269
+ newForm.postal_code = postcode;
270
+
271
+ emit('update:model-value', newForm);
272
+ });
273
+ }
274
+ </script>
275
+
276
+ <style lang="postcss">
277
+ .pac-container {
278
+ @apply rounded-lg border border-gray-200 font-sans shadow-md;
279
+
280
+ & .pac-item {
281
+ @apply flex cursor-pointer items-center py-2 pl-2;
282
+ }
283
+
284
+ & .pac-icon {
285
+ @apply m-0 mr-2;
286
+ }
287
+
288
+ & .pac-item,
289
+ & .pac-item-query,
290
+ & .pac-matched {
291
+ @apply text-base;
292
+ }
293
+
294
+ & .pac-matched {
295
+ @apply font-semibold;
296
+ }
297
+
298
+ &:after {
299
+ background-image: none !important;
300
+ @apply h-0 p-0;
301
+ }
302
+ }
303
+ </style>
@@ -39,7 +39,7 @@
39
39
  @update:model-value="emitUpdate"
40
40
  />
41
41
  <input
42
- v-else
42
+ v-else-if="maskOptions"
43
43
  ref="input"
44
44
  v-maska:[maskOptions]
45
45
  v-bind="bindings"
@@ -53,6 +53,20 @@
53
53
  }"
54
54
  @input="update"
55
55
  />
56
+ <input
57
+ v-else
58
+ ref="input"
59
+ v-bind="bindings"
60
+ :value="modelValue"
61
+ :type="type"
62
+ :autocomplete="autocomplete ? 'on' : 'off'"
63
+ :class="{
64
+ 'rounded-l': emptyLeft,
65
+ 'rounded-r': emptyRight,
66
+ 'w-full border-none bg-white outline-none focus:z-[1] focus:ring-2 focus:ring-primary-600 focus:ring-offset-1 disabled:cursor-not-allowed disabled:text-slate-300': true,
67
+ }"
68
+ @input="update"
69
+ />
56
70
  <div
57
71
  v-if="suffix"
58
72
  class="flex shrink-0 items-center justify-center border-l px-4 transition-colors"
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ const messages = { en, fr };
15
15
 
16
16
  import './assets/main.css';
17
17
  import { Locales } from './types';
18
+ import { Country } from './types/Country';
19
+ import { Region } from './types/Region';
18
20
 
19
21
  export interface Options {
20
22
  // eslint-disable-next-line @typescript-eslint/ban-types
@@ -24,6 +26,8 @@ export interface Options {
24
26
  locales?: Locales;
25
27
  formatQueryString?: (params: Record<string, any>) => string;
26
28
  parseQueryString?: (params: string) => Record<string, any>;
29
+ countries?: Country[];
30
+ regions?: Region[];
27
31
  }
28
32
 
29
33
  const config = {
@@ -47,6 +51,8 @@ const config = {
47
51
  parseQueryString(params: string): Record<string, any> {
48
52
  return QueryString.parse(params);
49
53
  },
54
+ countries: [] as Country[],
55
+ regions: [] as Region[],
50
56
  };
51
57
 
52
58
  function install(app: App, options?: Options) {
@@ -78,6 +84,14 @@ function install(app: App, options?: Options) {
78
84
  if (options?.parseQueryString) {
79
85
  config.parseQueryString = options.parseQueryString;
80
86
  }
87
+
88
+ if (options?.countries) {
89
+ config.countries = options.countries;
90
+ }
91
+
92
+ if (options?.regions) {
93
+ config.regions = options.regions;
94
+ }
81
95
  }
82
96
 
83
97
  export default { install };
package/src/lang/en.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "sui": {
3
+ "address": "Address",
4
+ "address_1_placeholder": "Postal address",
5
+ "address_2_description": "Apartment, suite, unit, building",
3
6
  "and": "and",
4
7
  "apply_filters": "Apply filters",
5
8
  "autocomplete_placeholder": "Type to start your search",
6
9
  "cancel": "Cancel",
10
+ "city": "City",
7
11
  "clear": "Clear",
8
12
  "click_or_select_date": "Click or select date",
9
13
  "click_to_copy": "Click to copy",
10
14
  "columns": "Columns",
11
15
  "confirm": "Confirm",
12
16
  "copied": "Copied",
17
+ "country": "Country",
13
18
  "create_new": "Create new",
14
19
  "day": "Day",
15
20
  "delete_record": "Delete the record",
@@ -34,9 +39,11 @@
34
39
  "or": "or",
35
40
  "page": "Page",
36
41
  "pagination_detail": "{page} records of {total}",
42
+ "postal_code_zip_code": "Postal Code / Zip Code",
37
43
  "previous": "Previous",
38
44
  "previous_month": "Previous month",
39
45
  "read_more": "Read more",
46
+ "region": "State / Province",
40
47
  "remove": "Remove",
41
48
  "remove_file": "Remove file?",
42
49
  "remove_file_description": "Are you sure you want to remove the file? This action is irreversible.",
package/src/lang/fr.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "sui": {
3
+ "address": "Adresse",
4
+ "address_1_placeholder": "Adresse postale",
5
+ "address_2_description": "Appartement, suite, unité, immeuble",
3
6
  "and": "et",
4
7
  "apply_filters": "Appliquer les filtres",
5
8
  "autocomplete_placeholder": "Tapez pour lancer votre recherche",
6
9
  "cancel": "Annuler",
10
+ "city": "Ville",
7
11
  "clear": "Effacer",
8
12
  "click_or_select_date": "Cliquez ou sélectionnez la date",
9
13
  "click_to_copy": "Cliquez pour copier",
10
14
  "columns": "Colonnes",
11
15
  "confirm": "Confirmer",
12
16
  "copied": "Copié",
17
+ "country": "Pays",
13
18
  "create_new": "Créer un nouveau",
14
19
  "day": "Jour",
15
20
  "delete_record": "Supprimer l'item",
@@ -34,9 +39,11 @@
34
39
  "or": "ou",
35
40
  "page": "Page",
36
41
  "pagination_detail": "{page} items de {total}",
42
+ "postal_code_zip_code": "Code postal",
37
43
  "previous": "Précédent",
38
44
  "previous_month": "Mois précédent",
39
45
  "read_more": "Lire la suite",
46
+ "region": "État / Province",
40
47
  "remove": "Retirer",
41
48
  "remove_file": "Retirer le fichier?",
42
49
  "remove_file_description": "Voulez-vous vraiment supprimer le fichier ? \nCette action est irréversible.",
@@ -0,0 +1,4 @@
1
+ export interface Country {
2
+ id: string;
3
+ name: string;
4
+ }
@@ -0,0 +1,5 @@
1
+ export interface Region {
2
+ id: string;
3
+ name: string;
4
+ country_id: string;
5
+ }