@bagelink/vue 0.0.439 → 0.0.447
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.
- package/dist/components/AccordionItem.vue.d.ts.map +1 -1
- package/dist/components/DataPreview.vue.d.ts +6 -0
- package/dist/components/DataPreview.vue.d.ts.map +1 -1
- package/dist/components/Flag.vue.d.ts +16 -0
- package/dist/components/Flag.vue.d.ts.map +1 -0
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/TableSchema.vue.d.ts +6 -5
- package/dist/components/TableSchema.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/FileUpload.vue.d.ts +166 -2
- package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +165 -0
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/TextInput.vue.d.ts +2 -5
- package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +1 -0
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
- package/dist/index.cjs +6513 -1544
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +6513 -1544
- package/dist/style.css +1877 -93
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/allCountries.d.ts +11 -0
- package/dist/utils/allCountries.d.ts.map +1 -0
- package/package.json +3 -4
- package/src/components/AccordionItem.vue +8 -5
- package/src/components/DataPreview.vue +14 -4
- package/src/components/Flag.vue +1349 -0
- package/src/components/Modal.vue +26 -9
- package/src/components/ModalForm.vue +1 -1
- package/src/components/PageTitle.vue +8 -1
- package/src/components/TableSchema.vue +34 -2
- package/src/components/form/inputs/FileUpload.vue +74 -61
- package/src/components/form/inputs/SelectInput.vue +6 -0
- package/src/components/form/inputs/TelInput.vue +401 -0
- package/src/components/form/inputs/TextInput.vue +16 -53
- package/src/components/form/inputs/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/components/layout/Tabs.vue +1 -0
- package/src/index.ts +1 -0
- package/src/styles/appearance.css +610 -0
- package/src/styles/buttons.css +8 -0
- package/src/styles/layout.css +46 -1
- package/src/styles/mobilLayout.css +37 -0
- package/src/styles/modal.css +5 -2
- package/src/styles/text.css +39 -0
- package/src/utils/BagelFormUtils.ts +11 -1
- package/src/utils/allCountries.ts +337 -0
- package/src/components/whatsapp/form/MsgTemplate.vue +0 -224
- package/src/components/whatsapp/form/TextVariableExamples.vue +0 -79
- package/src/components/whatsapp/index.ts +0 -2
- package/src/components/whatsapp/interfaces.ts +0 -58
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="bagel-input" :class="{ disabled: disabled }">
|
|
3
|
+
<label>
|
|
4
|
+
{{ label }}
|
|
5
|
+
<div
|
|
6
|
+
dir="ltr"
|
|
7
|
+
class="flex gap-05 tel-input"
|
|
8
|
+
tabindex="-1"
|
|
9
|
+
aria-label="Country Code Selector"
|
|
10
|
+
aria-haspopup="listbox"
|
|
11
|
+
:aria-expanded="open"
|
|
12
|
+
@keydown.esc="reset"
|
|
13
|
+
@keydown.tab="reset"
|
|
14
|
+
>
|
|
15
|
+
<Dropdown :placement="'bottom'" @hide="open = false">
|
|
16
|
+
<span class="flex gap-05" @click="open = true">
|
|
17
|
+
<Icon :icon="open ? 'collapse_all' : 'expand_all'" />
|
|
18
|
+
<Flag
|
|
19
|
+
v-if="dropdownOptions.showFlags && activeCountryCode"
|
|
20
|
+
:country-code="activeCountryCode?.toLowerCase()"
|
|
21
|
+
/>
|
|
22
|
+
</span>
|
|
23
|
+
<template #popper="{ hide }">
|
|
24
|
+
<div class="p-075 tel-countryp-dropdown">
|
|
25
|
+
<TextInput
|
|
26
|
+
v-if="searchable"
|
|
27
|
+
aria-label="Search by country name or country code"
|
|
28
|
+
placeholder="Search"
|
|
29
|
+
icon="search"
|
|
30
|
+
v-model="searchQuery"
|
|
31
|
+
/>
|
|
32
|
+
<ul
|
|
33
|
+
ref="listEl"
|
|
34
|
+
class="overflow-y p-0"
|
|
35
|
+
:style="{ 'max-height': '400px' }"
|
|
36
|
+
:class="dropdownOpenDirection"
|
|
37
|
+
role="listbox"
|
|
38
|
+
>
|
|
39
|
+
<li
|
|
40
|
+
v-for="(pb, index) in sortedCountries"
|
|
41
|
+
role="option"
|
|
42
|
+
class="flex gap-075"
|
|
43
|
+
:key="pb.iso2 + isPreferred(pb)"
|
|
44
|
+
tabindex="-1"
|
|
45
|
+
@click="
|
|
46
|
+
() => {
|
|
47
|
+
chooseCountry(pb.iso2);
|
|
48
|
+
hide();
|
|
49
|
+
}
|
|
50
|
+
"
|
|
51
|
+
@mousemove="selectedIndex = index"
|
|
52
|
+
:aria-selected="activeCountryCode === pb.iso2 && !isPreferred(pb)"
|
|
53
|
+
>
|
|
54
|
+
<Flag v-if="dropdownOptions.showFlags" :country-code="pb.iso2.toLowerCase()" />
|
|
55
|
+
<p class="tel-country">{{ pb.name }}</p>
|
|
56
|
+
<span v-if="dropdownOptions.showDialCodeInList"> +{{ pb.dialCode }} </span>
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
</Dropdown>
|
|
62
|
+
<input
|
|
63
|
+
:required="required"
|
|
64
|
+
:placeholder="placeholder"
|
|
65
|
+
:disabled="disabled"
|
|
66
|
+
v-model="phone"
|
|
67
|
+
type="tel"
|
|
68
|
+
ref="refInput"
|
|
69
|
+
:autocomplete="autocomplete"
|
|
70
|
+
:class="['vti__input']"
|
|
71
|
+
:id="id"
|
|
72
|
+
:maxlength="inputOptions.maxlength"
|
|
73
|
+
:name="inputOptions.name"
|
|
74
|
+
:readonly="inputOptions.readonly"
|
|
75
|
+
:tabindex="inputOptions.tabindex"
|
|
76
|
+
:aria-describedby="inputOptions['aria-describedby']"
|
|
77
|
+
@blur="onBlur"
|
|
78
|
+
@focus="onFocus"
|
|
79
|
+
@keyup.enter="onEnter"
|
|
80
|
+
@keyup.space="onSpace"
|
|
81
|
+
>
|
|
82
|
+
</div>
|
|
83
|
+
</label>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<script lang="ts" setup>
|
|
88
|
+
import {
|
|
89
|
+
Icon, Dropdown, TextInput, Flag,
|
|
90
|
+
allCountries, type Country,
|
|
91
|
+
} from '@bagelink/vue';
|
|
92
|
+
import { onMounted, watch } from 'vue';
|
|
93
|
+
import { parsePhoneNumberFromString, type CountryCode, type PhoneNumber } from 'libphonenumber-js';
|
|
94
|
+
import axios from 'axios';
|
|
95
|
+
|
|
96
|
+
async function getIp() {
|
|
97
|
+
const apiData = sessionStorage?.getItem('ipapi');
|
|
98
|
+
if (apiData) return JSON.parse(apiData);
|
|
99
|
+
const { data } = await axios.get('https://ipapi.co/json/');
|
|
100
|
+
sessionStorage.setItem('ipapi', JSON.stringify(data));
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type DropdownOptions = {
|
|
105
|
+
disabled?: boolean;
|
|
106
|
+
showFlags?: boolean;
|
|
107
|
+
showDialCodeInSelection?: boolean;
|
|
108
|
+
showDialCodeInList?: boolean;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
type InputOptions = {
|
|
112
|
+
autofocus?: boolean;
|
|
113
|
+
'aria-describedby'?: string;
|
|
114
|
+
maxlength?: number;
|
|
115
|
+
name?: string;
|
|
116
|
+
placeholder?: string;
|
|
117
|
+
readonly?: boolean;
|
|
118
|
+
tabindex?: number;
|
|
119
|
+
showDialCode?: boolean;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type Props = {
|
|
123
|
+
label?: string;
|
|
124
|
+
id?: string;
|
|
125
|
+
autocomplete?: 'on' | 'off';
|
|
126
|
+
placeholder?: string;
|
|
127
|
+
required?: boolean;
|
|
128
|
+
modelValue: string;
|
|
129
|
+
allCountries?: Country[];
|
|
130
|
+
autoFormat?: boolean;
|
|
131
|
+
customValidate?: boolean | RegExp;
|
|
132
|
+
defaultCountry?: string | number;
|
|
133
|
+
disabled?: boolean;
|
|
134
|
+
searchable?: boolean;
|
|
135
|
+
autoDefaultCountry?: boolean;
|
|
136
|
+
dropdownOptions?: DropdownOptions;
|
|
137
|
+
excludeCountries?: string[];
|
|
138
|
+
inputOptions?: InputOptions;
|
|
139
|
+
invalidMsg?: string;
|
|
140
|
+
mode?: 'auto' | 'international' | 'national';
|
|
141
|
+
onlyCountries?: string[];
|
|
142
|
+
preferredCountries?: string[];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
146
|
+
modelValue: '',
|
|
147
|
+
autocomplete: 'on',
|
|
148
|
+
allCountries: () => allCountries,
|
|
149
|
+
autoFormat: true,
|
|
150
|
+
customValidate: false,
|
|
151
|
+
defaultCountry: '',
|
|
152
|
+
placeholder: 'Enter a phone number',
|
|
153
|
+
searchable: true,
|
|
154
|
+
dropdownOptions: () => ({
|
|
155
|
+
disabled: false,
|
|
156
|
+
showFlags: true,
|
|
157
|
+
showDialCodeInSelection: true,
|
|
158
|
+
showDialCodeInList: true,
|
|
159
|
+
searchable: true,
|
|
160
|
+
}),
|
|
161
|
+
disabled: false,
|
|
162
|
+
autoDefaultCountry: true,
|
|
163
|
+
excludeCountries: () => [],
|
|
164
|
+
inputOptions: () => ({
|
|
165
|
+
showDialCode: true,
|
|
166
|
+
autofocus: false,
|
|
167
|
+
'aria-describedby': '',
|
|
168
|
+
id: '',
|
|
169
|
+
maxlength: 25,
|
|
170
|
+
name: '',
|
|
171
|
+
readonly: false,
|
|
172
|
+
tabindex: 0,
|
|
173
|
+
}),
|
|
174
|
+
required: false,
|
|
175
|
+
invalidMsg: 'Invalid phone number',
|
|
176
|
+
mode: 'auto',
|
|
177
|
+
onlyCountries: () => [],
|
|
178
|
+
preferredCountries: () => [],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
let activeCountryCode = $ref<CountryCode>();
|
|
182
|
+
let open = $ref(false);
|
|
183
|
+
let selectedIndex = $ref<number>();
|
|
184
|
+
const dropdownOpenDirection = $ref('below');
|
|
185
|
+
const searchQuery = $ref('');
|
|
186
|
+
|
|
187
|
+
const refInput = $ref<HTMLInputElement | null>(null);
|
|
188
|
+
|
|
189
|
+
const emit = defineEmits([
|
|
190
|
+
'update:modelValue',
|
|
191
|
+
'change',
|
|
192
|
+
'input',
|
|
193
|
+
'blur',
|
|
194
|
+
'focus',
|
|
195
|
+
'country-changed',
|
|
196
|
+
'enter',
|
|
197
|
+
'space',
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
function formatPhone(val: string) {
|
|
201
|
+
let result: PhoneNumber | undefined;
|
|
202
|
+
if (val?.[0] === '+') result = parsePhoneNumberFromString(val);
|
|
203
|
+
else result = parsePhoneNumberFromString(val, activeCountryCode);
|
|
204
|
+
const valid = result?.isValid?.();
|
|
205
|
+
refInput?.setCustomValidity?.(valid ? '' : props.invalidMsg);
|
|
206
|
+
const formatted = valid ? result?.format?.(parsedMode.toUpperCase() as any) : val;
|
|
207
|
+
return formatted;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const cleanStr = (str?: string) => {
|
|
211
|
+
const phoneNumber = (str?.match(/[()\-+0-9\s]*/g) || []).join('');
|
|
212
|
+
return phoneNumber.trim();
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
let phone = $computed<string>({
|
|
216
|
+
get: () => props.modelValue,
|
|
217
|
+
set: (value) => {
|
|
218
|
+
const clean = cleanStr(value);
|
|
219
|
+
const formatted = formatPhone(clean) || '';
|
|
220
|
+
emit('update:modelValue', formatted);
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Computed: activeCountry
|
|
225
|
+
const activeCountry = $computed(() => props.allCountries.find((country) => country.iso2 === activeCountryCode?.toUpperCase()));
|
|
226
|
+
|
|
227
|
+
const isPreferred = (country?: Country) => props.preferredCountries.includes(country?.iso2 as string);
|
|
228
|
+
|
|
229
|
+
// Computed: parsedMode
|
|
230
|
+
const parsedMode = $computed(() => {
|
|
231
|
+
if (props.mode === 'auto') {
|
|
232
|
+
if (!phone || phone[0] !== '+') return 'national';
|
|
233
|
+
return 'international';
|
|
234
|
+
}
|
|
235
|
+
return props.mode || 'international';
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const filteredCountries = $computed(() => {
|
|
239
|
+
if (props.onlyCountries.length) {
|
|
240
|
+
return props.allCountries.filter(({ iso2 }) => props.onlyCountries.some((c) => c.toUpperCase() === iso2));
|
|
241
|
+
}
|
|
242
|
+
if (props.excludeCountries.length) {
|
|
243
|
+
return props.allCountries.filter(
|
|
244
|
+
({ iso2 }) => !props.excludeCountries.includes(iso2.toUpperCase()) &&
|
|
245
|
+
!props.excludeCountries.includes(iso2.toLowerCase()),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return props.allCountries;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const sortedCountries = $computed(() => {
|
|
252
|
+
const preferredCountries = getCountries(props.preferredCountries);
|
|
253
|
+
|
|
254
|
+
const countriesList = [...preferredCountries, ...filteredCountries];
|
|
255
|
+
const cleanInput = searchQuery.replace(/[~`!@#$%^&*()+={}\[\];:'"<>.,\/\\\?-_]/g, '');
|
|
256
|
+
return countriesList
|
|
257
|
+
.filter(
|
|
258
|
+
(c) => new RegExp(cleanInput, 'i').test(c?.name || '') ||
|
|
259
|
+
new RegExp(cleanInput, 'i').test(c?.iso2 || '') ||
|
|
260
|
+
new RegExp(cleanInput, 'i').test(c?.dialCode || ''),
|
|
261
|
+
)
|
|
262
|
+
.filter(Boolean);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Watchers
|
|
266
|
+
watch(
|
|
267
|
+
() => activeCountry,
|
|
268
|
+
(value, oldValue) => {
|
|
269
|
+
if (!value && oldValue?.iso2) {
|
|
270
|
+
activeCountryCode = oldValue.iso2;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (value?.iso2) emit('country-changed', value);
|
|
274
|
+
},
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const initializeCountry = async () => {
|
|
278
|
+
// if (phone?.[0] === '+') return;
|
|
279
|
+
// 2. Use default country if passed from parent
|
|
280
|
+
if (props.defaultCountry) {
|
|
281
|
+
if (typeof props.defaultCountry === 'string') {
|
|
282
|
+
chooseCountry(props.defaultCountry);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (typeof props.defaultCountry === 'number') {
|
|
286
|
+
const country = findCountryByDialCode(props.defaultCountry);
|
|
287
|
+
if (country) {
|
|
288
|
+
chooseCountry(country.iso2);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const fallbackCountry = sortedCountries[0];
|
|
295
|
+
|
|
296
|
+
if (props.autoDefaultCountry) {
|
|
297
|
+
try {
|
|
298
|
+
const res = (await getIp()).country;
|
|
299
|
+
chooseCountry(res || activeCountryCode);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.warn(error);
|
|
302
|
+
chooseCountry(fallbackCountry?.iso2);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
// 4. Use the first country from preferred list (if available) or all countries list
|
|
306
|
+
chooseCountry(fallbackCountry?.iso2);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
onMounted(initializeCountry);
|
|
311
|
+
|
|
312
|
+
const findCountry = (iso: string) => filteredCountries.find((country) => country.iso2 === iso?.toUpperCase());
|
|
313
|
+
|
|
314
|
+
const getCountries = (list: string[]): Country[] => {
|
|
315
|
+
const countryList: Country[] = [];
|
|
316
|
+
list.forEach((countryCode) => {
|
|
317
|
+
const country = findCountry(countryCode);
|
|
318
|
+
if (country) countryList.push(country);
|
|
319
|
+
});
|
|
320
|
+
return countryList;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const findCountryByDialCode = (dialCode: number) => filteredCountries.find((country) => Number(country.dialCode) === dialCode);
|
|
324
|
+
|
|
325
|
+
const chooseCountry = (country?: string) => {
|
|
326
|
+
if (!country) return;
|
|
327
|
+
const parsedCountry = findCountry(country);
|
|
328
|
+
if (!parsedCountry) return;
|
|
329
|
+
activeCountryCode = parsedCountry.iso2;
|
|
330
|
+
|
|
331
|
+
if (props.inputOptions?.showDialCode && parsedCountry) {
|
|
332
|
+
phone = `+${parsedCountry.dialCode}`;
|
|
333
|
+
activeCountryCode = parsedCountry.iso2 || '';
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
activeCountryCode = parsedCountry.iso2 || '';
|
|
338
|
+
// emitInput(phone);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const onBlur = () => emit('blur');
|
|
342
|
+
|
|
343
|
+
const onFocus = () => emit('focus');
|
|
344
|
+
|
|
345
|
+
const onEnter = () => emit('enter');
|
|
346
|
+
|
|
347
|
+
const onSpace = () => emit('space');
|
|
348
|
+
|
|
349
|
+
// const focus = () => refInput?.focus();
|
|
350
|
+
|
|
351
|
+
// Method: reset
|
|
352
|
+
const reset = () => {
|
|
353
|
+
selectedIndex = sortedCountries.findIndex((c) => c.iso2 === activeCountryCode);
|
|
354
|
+
open = false;
|
|
355
|
+
};
|
|
356
|
+
</script>
|
|
357
|
+
|
|
358
|
+
<style scoped>
|
|
359
|
+
.tel-input {
|
|
360
|
+
direction: ltr;
|
|
361
|
+
text-align: left;
|
|
362
|
+
background: var(--input-bg);
|
|
363
|
+
border: none;
|
|
364
|
+
padding-inline-start: 0.7rem;
|
|
365
|
+
border-radius: var(--input-border-radius);
|
|
366
|
+
color: var(--input-color);
|
|
367
|
+
min-width: calc(var(--input-height) * 3);
|
|
368
|
+
width: 100%;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.tel-input:focus-within {
|
|
372
|
+
outline: none;
|
|
373
|
+
box-shadow: inset 0 0 10px #00000012;
|
|
374
|
+
}
|
|
375
|
+
.tel-input input {
|
|
376
|
+
background: transparent;
|
|
377
|
+
}
|
|
378
|
+
.tel-input input::placeholder {
|
|
379
|
+
color: transparent;
|
|
380
|
+
}
|
|
381
|
+
.tel-input input:focus-visible {
|
|
382
|
+
box-shadow: none;
|
|
383
|
+
}
|
|
384
|
+
.input_country-code {
|
|
385
|
+
font-size: var(--input-font-size);
|
|
386
|
+
color: var(--input-color);
|
|
387
|
+
}
|
|
388
|
+
.tel-country {
|
|
389
|
+
font-size: var(--input-font-size);
|
|
390
|
+
max-width: 200px;
|
|
391
|
+
white-space: nowrap;
|
|
392
|
+
text-overflow: ellipsis;
|
|
393
|
+
overflow: hidden;
|
|
394
|
+
margin-top: 0;
|
|
395
|
+
margin-bottom: 0;
|
|
396
|
+
}
|
|
397
|
+
.tel-countryp-dropdown {
|
|
398
|
+
direction: ltr;
|
|
399
|
+
text-align: left;
|
|
400
|
+
}
|
|
401
|
+
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
class="bagel-input text-input"
|
|
4
4
|
:class="{
|
|
5
|
-
dense, small, shrink,
|
|
5
|
+
dense, small, shrink, code,
|
|
6
6
|
textInputIconWrap: icon,
|
|
7
7
|
txtInputIconStart: iconStart,
|
|
8
8
|
}"
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
:rows="1"
|
|
21
21
|
ref="input"
|
|
22
22
|
:placeholder="placeholder || label"
|
|
23
|
-
:disabled
|
|
23
|
+
:disabled
|
|
24
24
|
:required="required"
|
|
25
25
|
:pattern="pattern"
|
|
26
26
|
v-bind="nativeInputAttrs"
|
|
27
|
-
@
|
|
27
|
+
@input="updateInputVal"
|
|
28
28
|
>
|
|
29
29
|
<textarea
|
|
30
30
|
v-else
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
:rows="rows"
|
|
36
36
|
ref="input"
|
|
37
37
|
:placeholder="placeholder || label"
|
|
38
|
-
:disabled
|
|
38
|
+
:disabled
|
|
39
39
|
:required="required"
|
|
40
40
|
:pattern="pattern"
|
|
41
41
|
v-bind="nativeInputAttrs"
|
|
42
|
-
@
|
|
42
|
+
@input="updateInputVal"
|
|
43
43
|
/>
|
|
44
44
|
<p v-if="helptext">{{ helptext }}</p>
|
|
45
45
|
</label>
|
|
@@ -53,14 +53,6 @@
|
|
|
53
53
|
v-if="icon"
|
|
54
54
|
:icon="icon"
|
|
55
55
|
/>
|
|
56
|
-
<Btn
|
|
57
|
-
class="toggleEditBtn"
|
|
58
|
-
v-if="toggleEdit"
|
|
59
|
-
thin
|
|
60
|
-
@click="toggleEditAction"
|
|
61
|
-
:icon="editMode ? 'check' : 'edit'"
|
|
62
|
-
flat
|
|
63
|
-
/>
|
|
64
56
|
</div>
|
|
65
57
|
</template>
|
|
66
58
|
|
|
@@ -87,7 +79,7 @@ const props = withDefaults(
|
|
|
87
79
|
required?: boolean;
|
|
88
80
|
pattern?: string;
|
|
89
81
|
shrink?: boolean;
|
|
90
|
-
|
|
82
|
+
disabled?: boolean;
|
|
91
83
|
type?: string;
|
|
92
84
|
nativeInputAttrs?: Record<string, any>;
|
|
93
85
|
icon?: MaterialIcons;
|
|
@@ -102,27 +94,13 @@ const props = withDefaults(
|
|
|
102
94
|
}>(),
|
|
103
95
|
{
|
|
104
96
|
type: 'text',
|
|
105
|
-
toggleEdit: false,
|
|
106
97
|
modelValue: '',
|
|
107
98
|
},
|
|
108
99
|
);
|
|
109
100
|
let inputVal = $ref<string | number>();
|
|
110
|
-
let editMode = $ref<boolean>(!props.toggleEdit);
|
|
111
101
|
|
|
112
102
|
const input = $ref<HTMLInputElement>();
|
|
113
103
|
|
|
114
|
-
const toggleEditAction = () => {
|
|
115
|
-
if (!props.toggleEdit) return;
|
|
116
|
-
if (editMode) {
|
|
117
|
-
editMode = false;
|
|
118
|
-
emit('debounce', inputVal);
|
|
119
|
-
emit('update:modelValue', inputVal as string);
|
|
120
|
-
} else {
|
|
121
|
-
editMode = true;
|
|
122
|
-
setTimeout(() => input?.focus(), 10);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
104
|
const rows = $computed(() => {
|
|
127
105
|
if (props.lines) return props.lines;
|
|
128
106
|
if (props.autoheight) return `${inputVal}`?.split('\n').length || 1;
|
|
@@ -130,15 +108,11 @@ const rows = $computed(() => {
|
|
|
130
108
|
return 1;
|
|
131
109
|
});
|
|
132
110
|
|
|
133
|
-
|
|
134
|
-
()
|
|
135
|
-
(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
emit('update:modelValue', newVal as string);
|
|
139
|
-
debounce(() => emit('debounce', newVal));
|
|
140
|
-
},
|
|
141
|
-
);
|
|
111
|
+
function updateInputVal() {
|
|
112
|
+
if (props.disabled) return;
|
|
113
|
+
emit('update:modelValue', inputVal as string);
|
|
114
|
+
debounce(() => emit('debounce', inputVal), 300);
|
|
115
|
+
}
|
|
142
116
|
|
|
143
117
|
watch(
|
|
144
118
|
() => props.modelValue,
|
|
@@ -185,10 +159,6 @@ onMounted(() => {
|
|
|
185
159
|
opacity: 0.3;
|
|
186
160
|
}
|
|
187
161
|
|
|
188
|
-
.bagel-input.toggleEdit:hover {
|
|
189
|
-
background-color: var(--input-bg);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
162
|
.bagel-input.small {
|
|
193
163
|
margin-bottom: 0;
|
|
194
164
|
height: 30px;
|
|
@@ -200,15 +170,13 @@ onMounted(() => {
|
|
|
200
170
|
gap: 0.5rem;
|
|
201
171
|
}
|
|
202
172
|
|
|
203
|
-
.bagel-input
|
|
204
|
-
|
|
173
|
+
.bagel-input input:disabled {
|
|
174
|
+
background: #f5f5f5;
|
|
175
|
+
|
|
205
176
|
}
|
|
206
177
|
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
right: -24px;
|
|
210
|
-
top: 4;
|
|
211
|
-
opacity: 0;
|
|
178
|
+
.bagel-input label {
|
|
179
|
+
font-size: var(--label-font-size);
|
|
212
180
|
}
|
|
213
181
|
|
|
214
182
|
.textInputIconWrap {
|
|
@@ -235,11 +203,6 @@ onMounted(() => {
|
|
|
235
203
|
padding-inline-start: 2rem;
|
|
236
204
|
}
|
|
237
205
|
|
|
238
|
-
.bagel-input:hover .toggleEditBtn,
|
|
239
|
-
.bagel-input.editMode .toggleEditBtn {
|
|
240
|
-
opacity: 1;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
206
|
.bagel-input.small textarea {
|
|
244
207
|
height: 30px;
|
|
245
208
|
}
|
|
@@ -11,4 +11,5 @@ export { default as RadioPillsInput } from './RadioPillsInput.vue';
|
|
|
11
11
|
export { default as FileUpload } from './FileUpload.vue';
|
|
12
12
|
export { default as ToggleInput } from './ToggleInput.vue';
|
|
13
13
|
export { default as RichText } from './RichText.vue';
|
|
14
|
+
export { default as TelInput } from './TelInput.vue';
|
|
14
15
|
export { Dropdown } from 'floating-vue';
|
package/src/components/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { default as Badge } from './Badge.vue';
|
|
|
21
21
|
export { default as BglVideo } from './BglVideo.vue';
|
|
22
22
|
export { default as Carousel } from './Carousel.vue';
|
|
23
23
|
export { default as ModalConfirm } from './ModalConfirm.vue';
|
|
24
|
+
export { default as Flag } from './Flag.vue';
|
|
24
25
|
|
|
25
26
|
export * from './form';
|
|
26
27
|
export * from './dashboard';
|
|
@@ -39,6 +39,7 @@ const tabComponent = defineComponent({
|
|
|
39
39
|
const currentTabIndex = props.tabs.findIndex(
|
|
40
40
|
(tab) => tabValue(tab) === currentTab.value,
|
|
41
41
|
);
|
|
42
|
+
if (currentTabIndex === -1) return null;
|
|
42
43
|
const slotChildren = slots?.default?.()?.[1]?.children;
|
|
43
44
|
|
|
44
45
|
return h('div', (slotChildren as VNode[])[currentTabIndex]);
|