@bagelink/vue 0.0.1300 → 0.0.1303
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/AddressSearch.vue.d.ts +0 -3
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/DataPreview.vue.d.ts +2 -2
- package/dist/components/DataPreview.vue.d.ts.map +1 -1
- package/dist/components/DropDown.vue.d.ts +0 -1
- package/dist/components/DropDown.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ColorInput.vue.d.ts +21 -0
- package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts +0 -6
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +1 -618
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +1 -1
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/index.cjs +301 -221
- package/dist/index.mjs +302 -222
- package/dist/style.css +32 -32
- package/dist/utils/BagelFormUtils.d.ts +5 -0
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/timeAgo.d.ts +1 -0
- package/dist/utils/timeAgo.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/DataPreview.vue +2 -2
- package/src/components/Dropdown.vue +20 -3
- package/src/components/Spreadsheet/Index.vue +2 -2
- package/src/components/form/FieldArray.vue +1 -1
- package/src/components/form/inputs/{ColorPicker.vue → ColorInput.vue} +1 -1
- package/src/components/form/inputs/TelInput.vue +198 -172
- package/src/components/form/inputs/index.ts +1 -1
- package/src/composables/useSchemaField.ts +6 -1
- package/src/utils/BagelFormUtils.ts +17 -3
- package/src/utils/timeAgo.ts +36 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { Country } from '@bagelink/vue'
|
|
3
3
|
import type { CountryCode, NumberFormat } from 'libphonenumber-js'
|
|
4
|
-
import type { Raw, StyleValue } from 'vue'
|
|
4
|
+
import type { Raw, StyleValue, Ref } from 'vue'
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
6
|
Dropdown,
|
|
8
7
|
Flag,
|
|
9
8
|
Icon,
|
|
@@ -12,11 +11,8 @@ import {
|
|
|
12
11
|
useDebounceFn
|
|
13
12
|
} from '@bagelink/vue'
|
|
14
13
|
import axios from 'axios'
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
parsePhoneNumberFromString
|
|
18
|
-
} from 'libphonenumber-js'
|
|
19
|
-
import { onMounted, watch } from 'vue'
|
|
14
|
+
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
|
15
|
+
import { onMounted, watch, ref, computed } from 'vue'
|
|
20
16
|
|
|
21
17
|
export interface Props {
|
|
22
18
|
label?: string
|
|
@@ -40,7 +36,6 @@ export interface Props {
|
|
|
40
36
|
onlyCountries?: string[]
|
|
41
37
|
preferredCountries?: string[]
|
|
42
38
|
parseArg?: { extract?: boolean }
|
|
43
|
-
debounceDelay?: number
|
|
44
39
|
}
|
|
45
40
|
|
|
46
41
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -60,7 +55,6 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
60
55
|
onlyCountries: () => [],
|
|
61
56
|
preferredCountries: () => [],
|
|
62
57
|
showDropdown: true,
|
|
63
|
-
debounceDelay: 300,
|
|
64
58
|
})
|
|
65
59
|
|
|
66
60
|
const emit = defineEmits([
|
|
@@ -75,13 +69,8 @@ const emit = defineEmits([
|
|
|
75
69
|
'debounce',
|
|
76
70
|
])
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (apiData) return JSON.parse(apiData)
|
|
81
|
-
const { data } = await axios.get('https://ipapi.co/json/')
|
|
82
|
-
sessionStorage.setItem('ipapi', JSON.stringify(data))
|
|
83
|
-
return data
|
|
84
|
-
}
|
|
72
|
+
const open = ref(false)
|
|
73
|
+
const dropdownOpenDirection = ref('below')
|
|
85
74
|
|
|
86
75
|
const defaultDropdownOptions = {
|
|
87
76
|
hide: false,
|
|
@@ -117,192 +106,225 @@ const computedInputOptions = $computed(() => ({
|
|
|
117
106
|
...props.inputOptions,
|
|
118
107
|
}))
|
|
119
108
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
const searchQuery =
|
|
125
|
-
|
|
126
|
-
// const refInput = $ref<HTMLInputElement | undefined>()
|
|
109
|
+
// Composables
|
|
110
|
+
function useCountrySelection(props: Props, emit: any) {
|
|
111
|
+
const activeCountryCode = ref<CountryCode>()
|
|
112
|
+
const selectedIndex = ref<number>()
|
|
113
|
+
const searchQuery = ref('')
|
|
127
114
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
),
|
|
132
|
-
)
|
|
115
|
+
const activeCountry = computed(() => props.allCountries?.find(
|
|
116
|
+
country => country.iso2 === activeCountryCode.value?.toUpperCase(),
|
|
117
|
+
))
|
|
133
118
|
|
|
134
|
-
const
|
|
119
|
+
const filteredCountries = computed(() => {
|
|
120
|
+
const countries = props.allCountries || []
|
|
121
|
+
const onlyCountries = props.onlyCountries || []
|
|
122
|
+
const excludeCountries = props.excludeCountries || []
|
|
135
123
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
return props.allCountries
|
|
148
|
-
})
|
|
124
|
+
if (onlyCountries.length > 0) {
|
|
125
|
+
return countries.filter(({ iso2 }) => onlyCountries.some(c => c.toUpperCase() === iso2))
|
|
126
|
+
}
|
|
127
|
+
if (excludeCountries.length > 0) {
|
|
128
|
+
return countries.filter(
|
|
129
|
+
({ iso2 }) => !excludeCountries.includes(iso2.toUpperCase())
|
|
130
|
+
&& !excludeCountries.includes(iso2.toLowerCase()),
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return countries
|
|
134
|
+
})
|
|
149
135
|
|
|
150
|
-
const sortedCountries =
|
|
151
|
-
|
|
136
|
+
const sortedCountries = computed(() => {
|
|
137
|
+
const preferredCountries = getCountries(props.preferredCountries || [])
|
|
138
|
+
const countriesList = [...preferredCountries, ...filteredCountries.value]
|
|
139
|
+
const cleanInput = searchQuery.value.replaceAll(
|
|
140
|
+
/[~`!#$%&*()+={};:'"<>.,\\/@-]/g,
|
|
141
|
+
'',
|
|
142
|
+
).toLowerCase()
|
|
143
|
+
return countriesList
|
|
144
|
+
.filter(
|
|
145
|
+
c => new RegExp(cleanInput, 'i').test(c.name || '')
|
|
146
|
+
|| new RegExp(cleanInput, 'i').test(c.iso2 || '')
|
|
147
|
+
|| new RegExp(cleanInput, 'i').test(c.dialCode || ''),
|
|
148
|
+
)
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
})
|
|
152
151
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
// eslint-disable-next-line regexp/no-obscure-range
|
|
156
|
-
/[~`!#$%&*()+={};:'"<>.,/?-_]/g,
|
|
157
|
-
'',
|
|
152
|
+
const findCountry = (iso: string) => filteredCountries.value.find(
|
|
153
|
+
country => country.iso2 === iso.toUpperCase()
|
|
158
154
|
)
|
|
159
|
-
return countriesList
|
|
160
|
-
.filter(
|
|
161
|
-
c => new RegExp(cleanInput, 'i').test(c.name || '')
|
|
162
|
-
|| new RegExp(cleanInput, 'i').test(c.iso2 || '')
|
|
163
|
-
|| new RegExp(cleanInput, 'i').test(c.dialCode || ''),
|
|
164
|
-
)
|
|
165
|
-
.filter(Boolean)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
const parseArgs = $computed(() => ({
|
|
169
|
-
...props.parseArg,
|
|
170
|
-
defaultCountry: activeCountryCode,
|
|
171
|
-
}))
|
|
172
|
-
|
|
173
|
-
const debouncedEmit = useDebounceFn((maybeFormatted: string) => {
|
|
174
|
-
emit('debounce', maybeFormatted)
|
|
175
|
-
}, props.debounceDelay)
|
|
176
|
-
|
|
177
|
-
const phone = defineModel<string>('modelValue', {
|
|
178
|
-
default: '',
|
|
179
|
-
set: (value) => {
|
|
180
|
-
let maybeFormatted = value
|
|
181
|
-
if (value.length > 5) {
|
|
182
|
-
maybeFormatted = formatPhone(`${value}`)
|
|
183
|
-
|
|
184
|
-
if (!maybeFormatted) { emit('update:modelValue', ''); return '' }
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
emit('update:modelValue', maybeFormatted)
|
|
188
|
-
debouncedEmit(maybeFormatted)
|
|
189
|
-
return maybeFormatted
|
|
190
|
-
},
|
|
191
|
-
get: value => value,
|
|
192
|
-
})
|
|
193
155
|
|
|
194
|
-
|
|
195
|
-
const phoneNumber = parsePhoneNumberFromString(val, parseArgs)
|
|
156
|
+
const findCountryByDialCode = (dialCode: number) => filteredCountries.value.find((country: Country) => Number(country.dialCode) === dialCode)
|
|
196
157
|
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
158
|
+
function getCountries(list: string[]): Country[] {
|
|
159
|
+
const countryList: Country[] = []
|
|
160
|
+
list.forEach((countryCode) => {
|
|
161
|
+
const country = findCountry(countryCode)
|
|
162
|
+
if (country) countryList.push(country)
|
|
163
|
+
})
|
|
164
|
+
return countryList
|
|
202
165
|
}
|
|
203
|
-
return phoneNumber.format(props.mode).replaceAll(' ', '') || ''
|
|
204
|
-
}
|
|
205
166
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!value && oldValue?.iso2) {
|
|
211
|
-
activeCountryCode = oldValue.iso2
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
// eslint-disable-next-line vue/custom-event-name-casing
|
|
215
|
-
if (value?.iso2) emit('country-changed', value)
|
|
216
|
-
},
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
async function initializeCountry() {
|
|
220
|
-
// if (phone?.[0] === '+') return;
|
|
221
|
-
// 2. Use default country if passed from parent
|
|
222
|
-
if (props.defaultCountry) {
|
|
223
|
-
if (typeof props.defaultCountry === 'string') {
|
|
224
|
-
chooseCountry(props.defaultCountry)
|
|
225
|
-
return
|
|
226
|
-
}
|
|
227
|
-
if (typeof props.defaultCountry === 'number') {
|
|
228
|
-
const country = findCountryByDialCode(props.defaultCountry)
|
|
229
|
-
if (country) {
|
|
230
|
-
chooseCountry(country.iso2)
|
|
167
|
+
async function initializeCountry() {
|
|
168
|
+
if (props.defaultCountry) {
|
|
169
|
+
if (typeof props.defaultCountry === 'string') {
|
|
170
|
+
chooseCountry(props.defaultCountry)
|
|
231
171
|
return
|
|
232
172
|
}
|
|
173
|
+
if (typeof props.defaultCountry === 'number') {
|
|
174
|
+
const country = findCountryByDialCode(props.defaultCountry)
|
|
175
|
+
if (country) {
|
|
176
|
+
chooseCountry(country.iso2)
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
}
|
|
233
180
|
}
|
|
234
|
-
}
|
|
235
181
|
|
|
236
|
-
|
|
182
|
+
const fallbackCountry = sortedCountries.value[0]
|
|
237
183
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
184
|
+
if (props.autoDefaultCountry) {
|
|
185
|
+
try {
|
|
186
|
+
const res = (await getIp()).country
|
|
187
|
+
chooseCountry(res || activeCountryCode.value)
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.warn(error)
|
|
190
|
+
chooseCountry(fallbackCountry.iso2)
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
244
193
|
chooseCountry(fallbackCountry.iso2)
|
|
245
194
|
}
|
|
246
|
-
} else {
|
|
247
|
-
// 4. Use the first country from preferred list (if available) or all countries list
|
|
248
|
-
chooseCountry(fallbackCountry.iso2)
|
|
249
195
|
}
|
|
250
|
-
}
|
|
251
196
|
|
|
252
|
-
|
|
197
|
+
function chooseCountry(country?: string) {
|
|
198
|
+
if (!country) return
|
|
199
|
+
const parsedCountry = findCountry(country)
|
|
200
|
+
if (!parsedCountry) return
|
|
201
|
+
activeCountryCode.value = parsedCountry.iso2
|
|
202
|
+
emit('country-changed', parsedCountry)
|
|
203
|
+
open.value = false
|
|
204
|
+
}
|
|
253
205
|
|
|
254
|
-
|
|
206
|
+
watch(
|
|
207
|
+
() => activeCountry.value,
|
|
208
|
+
(value, oldValue) => {
|
|
209
|
+
if (!value && oldValue?.iso2) {
|
|
210
|
+
activeCountryCode.value = oldValue.iso2
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
if (value?.iso2) emit('country-changed', value)
|
|
214
|
+
},
|
|
215
|
+
)
|
|
255
216
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
217
|
+
return {
|
|
218
|
+
activeCountryCode,
|
|
219
|
+
activeCountry,
|
|
220
|
+
selectedIndex,
|
|
221
|
+
searchQuery,
|
|
222
|
+
sortedCountries,
|
|
223
|
+
chooseCountry,
|
|
224
|
+
initializeCountry,
|
|
225
|
+
findCountry,
|
|
226
|
+
findCountryByDialCode
|
|
227
|
+
}
|
|
263
228
|
}
|
|
264
229
|
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
230
|
+
function usePhoneNumberFormatting(props: Props, activeCountryCode: Ref<CountryCode | undefined>) {
|
|
231
|
+
const parseArgs = computed(() => ({
|
|
232
|
+
...props.parseArg,
|
|
233
|
+
defaultCountry: activeCountryCode.value,
|
|
234
|
+
}))
|
|
235
|
+
|
|
236
|
+
function formatPhone(val: string): string {
|
|
237
|
+
// First, try to parse the number as is
|
|
238
|
+
let phoneNumber = parsePhoneNumberFromString(val, parseArgs.value)
|
|
239
|
+
|
|
240
|
+
// If parsing failed and there's a + at the start, try removing any existing country code
|
|
241
|
+
if (!phoneNumber && val.startsWith('+')) {
|
|
242
|
+
const currentCountry = props.allCountries?.find(c => c.iso2 === activeCountryCode.value)
|
|
243
|
+
if (currentCountry) {
|
|
244
|
+
// Remove the current country code if it exists
|
|
245
|
+
const { dialCode } = currentCountry
|
|
246
|
+
const withoutDialCode = val.replace(new RegExp(`^\\+${dialCode}`), '')
|
|
247
|
+
// Try parsing again with the cleaned number
|
|
248
|
+
phoneNumber = parsePhoneNumberFromString(withoutDialCode, parseArgs.value)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!phoneNumber) {
|
|
253
|
+
const dialCode = props.allCountries?.find(
|
|
254
|
+
c => c.iso2 === activeCountryCode.value
|
|
255
|
+
)?.dialCode || ''
|
|
268
256
|
|
|
269
|
-
|
|
257
|
+
if (props.mode === 'INTERNATIONAL') return `+${dialCode}`
|
|
258
|
+
return dialCode
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Format the number according to the selected mode
|
|
262
|
+
const formattedNumber = phoneNumber.format(props.mode || 'INTERNATIONAL')
|
|
270
263
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
264
|
+
// For international format, ensure proper formatting with country code
|
|
265
|
+
if (props.mode === 'INTERNATIONAL') {
|
|
266
|
+
const countryCode = phoneNumber.countryCallingCode
|
|
267
|
+
const { nationalNumber } = phoneNumber
|
|
268
|
+
return `+${countryCode}${nationalNumber}`
|
|
269
|
+
}
|
|
276
270
|
|
|
277
|
-
|
|
278
|
-
phone.value = `+ ${parsedCountry.dialCode}`
|
|
279
|
-
activeCountryCode = parsedCountry.iso2 || ''
|
|
280
|
-
return
|
|
271
|
+
return formattedNumber.replaceAll(' ', '') || ''
|
|
281
272
|
}
|
|
282
273
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
274
|
+
return {
|
|
275
|
+
formatPhone,
|
|
276
|
+
parseArgs
|
|
277
|
+
}
|
|
286
278
|
}
|
|
287
279
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
function onFocus() { emit('focus') }
|
|
280
|
+
const debouncedEmit = useDebounceFn((maybeFormatted: string) => { emit('debounce', maybeFormatted) })
|
|
291
281
|
|
|
292
|
-
|
|
282
|
+
const {
|
|
283
|
+
activeCountryCode,
|
|
284
|
+
selectedIndex,
|
|
285
|
+
searchQuery,
|
|
286
|
+
sortedCountries,
|
|
287
|
+
chooseCountry,
|
|
288
|
+
initializeCountry
|
|
289
|
+
} = useCountrySelection(props, emit)
|
|
293
290
|
|
|
294
|
-
|
|
291
|
+
const { formatPhone } = usePhoneNumberFormatting(props, activeCountryCode)
|
|
295
292
|
|
|
296
|
-
|
|
293
|
+
const isPreferred = (country?: Country) => props.preferredCountries.includes(country?.iso2 as CountryCode) || false
|
|
297
294
|
|
|
298
295
|
// Method: reset
|
|
299
296
|
function reset() {
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
if (!sortedCountries.value || !activeCountryCode.value) return
|
|
298
|
+
selectedIndex.value = sortedCountries.value.findIndex(
|
|
299
|
+
(c: Country) => c.iso2 === activeCountryCode.value
|
|
302
300
|
)
|
|
303
|
-
open = false
|
|
301
|
+
open.value = false
|
|
304
302
|
}
|
|
305
303
|
|
|
304
|
+
const phone = defineModel<string>('modelValue', {
|
|
305
|
+
default: '',
|
|
306
|
+
set: (value) => {
|
|
307
|
+
let maybeFormatted = value
|
|
308
|
+
if (value.length > 5) {
|
|
309
|
+
maybeFormatted = formatPhone(`${value}`)
|
|
310
|
+
if (!maybeFormatted) {
|
|
311
|
+
emit('update:modelValue', '')
|
|
312
|
+
return ''
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
emit('update:modelValue', maybeFormatted)
|
|
317
|
+
debouncedEmit(maybeFormatted)
|
|
318
|
+
return maybeFormatted
|
|
319
|
+
},
|
|
320
|
+
get: value => value,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
function onBlur() { emit('blur') }
|
|
324
|
+
function onFocus() { emit('focus') }
|
|
325
|
+
function onEnter() { emit('enter') }
|
|
326
|
+
function onSpace() { emit('space') }
|
|
327
|
+
|
|
306
328
|
function handleInput(e: KeyboardEvent) {
|
|
307
329
|
const keyVal = (e.key as string | undefined) ?? ''
|
|
308
330
|
if (keyVal.length > 1 || e.metaKey) return
|
|
@@ -313,6 +335,16 @@ function handleInput(e: KeyboardEvent) {
|
|
|
313
335
|
|
|
314
336
|
e.preventDefault()
|
|
315
337
|
}
|
|
338
|
+
|
|
339
|
+
async function getIp() {
|
|
340
|
+
const apiData = sessionStorage.getItem('ipapi')
|
|
341
|
+
if (apiData) return JSON.parse(apiData)
|
|
342
|
+
const { data } = await axios.get('https://ipapi.co/json/')
|
|
343
|
+
sessionStorage.setItem('ipapi', JSON.stringify(data))
|
|
344
|
+
return data
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
onMounted(initializeCountry)
|
|
316
348
|
</script>
|
|
317
349
|
|
|
318
350
|
<template>
|
|
@@ -333,17 +365,12 @@ function handleInput(e: KeyboardEvent) {
|
|
|
333
365
|
@keydown.tab="reset"
|
|
334
366
|
>
|
|
335
367
|
<Dropdown
|
|
336
|
-
v-
|
|
337
|
-
ref="phoneDropdown"
|
|
368
|
+
v-model:shown="open"
|
|
338
369
|
placement="bottom-start"
|
|
339
370
|
:disabled="computedDropDownOptions.disabled"
|
|
340
|
-
@hide="open = false"
|
|
341
371
|
>
|
|
342
372
|
<template #trigger>
|
|
343
|
-
<span
|
|
344
|
-
class="flex gap-05"
|
|
345
|
-
@click="open = true"
|
|
346
|
-
>
|
|
373
|
+
<span class="flex gap-05">
|
|
347
374
|
<Icon :icon="open ? 'collapse_all' : 'expand_all'" />
|
|
348
375
|
<Flag
|
|
349
376
|
v-if="computedDropDownOptions.showFlags && activeCountryCode"
|
|
@@ -361,8 +388,7 @@ function handleInput(e: KeyboardEvent) {
|
|
|
361
388
|
/>
|
|
362
389
|
|
|
363
390
|
<ul
|
|
364
|
-
class="overflow-y p-0"
|
|
365
|
-
:style="{ 'max-height': '400px' }"
|
|
391
|
+
class="overflow-y p-0 max-h-300px"
|
|
366
392
|
:class="dropdownOpenDirection"
|
|
367
393
|
role="listbox"
|
|
368
394
|
>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { default as Checkbox } from './Checkbox.vue'
|
|
2
2
|
export { default as CheckInput } from './CheckInput.vue'
|
|
3
3
|
export { default as CodeEditor } from './CodeEditor/Index.vue'
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as ColorInput } from './ColorInput.vue'
|
|
5
5
|
export { default as DateInput } from './DateInput.vue'
|
|
6
6
|
export { default as DatePick } from './DatePick.vue'
|
|
7
7
|
export { default as DatePicker } from './DatePicker.vue'
|
|
@@ -14,8 +14,11 @@ import {
|
|
|
14
14
|
BglForm,
|
|
15
15
|
bindAttrs,
|
|
16
16
|
classify,
|
|
17
|
-
keyToLabel
|
|
17
|
+
keyToLabel,
|
|
18
|
+
TelInput,
|
|
19
|
+
ColorInput,
|
|
18
20
|
} from '@bagelink/vue'
|
|
21
|
+
|
|
19
22
|
import { h, isVNode } from 'vue'
|
|
20
23
|
|
|
21
24
|
const SLOT_VALUE_COMPONENTS = new Set(['div', 'span', 'p'])
|
|
@@ -44,6 +47,8 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
|
|
|
44
47
|
textarea: TextInput,
|
|
45
48
|
number: NumberInput,
|
|
46
49
|
array: FieldArray,
|
|
50
|
+
color: ColorInput,
|
|
51
|
+
tel: TelInput,
|
|
47
52
|
select: SelectInput,
|
|
48
53
|
toggle: ToggleInput,
|
|
49
54
|
check: CheckInput,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { BglFormSchemaT, Field, Option, DotNotation } from '@bagelink/vue'
|
|
2
2
|
import type { UploadInputProps } from '../components/form/inputs/Upload/upload.types'
|
|
3
|
-
import { TelInput } from '@bagelink/vue'
|
|
4
|
-
import { markRaw } from 'vue'
|
|
5
3
|
|
|
6
4
|
interface InputOptions {
|
|
7
5
|
required?: boolean
|
|
@@ -223,11 +221,27 @@ export function telField<T extends { [key: string]: any }>(
|
|
|
223
221
|
options?: { [key: string]: any }
|
|
224
222
|
): Field<T> {
|
|
225
223
|
return {
|
|
226
|
-
$el:
|
|
224
|
+
$el: 'tel',
|
|
227
225
|
id,
|
|
228
226
|
label,
|
|
229
227
|
vIf: options?.vIf,
|
|
230
228
|
attrs: options,
|
|
229
|
+
class: options?.class,
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function colorField<T extends { [key: string]: any }>(
|
|
234
|
+
id: DotNotation<T> | string,
|
|
235
|
+
label?: string,
|
|
236
|
+
options?: { [key: string]: any }
|
|
237
|
+
): Field<T> {
|
|
238
|
+
return {
|
|
239
|
+
$el: 'color',
|
|
240
|
+
id,
|
|
241
|
+
label,
|
|
242
|
+
vIf: options?.vIf,
|
|
243
|
+
attrs: options,
|
|
244
|
+
class: options?.class,
|
|
231
245
|
}
|
|
232
246
|
}
|
|
233
247
|
|
package/src/utils/timeAgo.ts
CHANGED
|
@@ -117,3 +117,39 @@ export function timeAgo(date: string | Date, lang: AvailableTimeLanguages = 'en'
|
|
|
117
117
|
|
|
118
118
|
return selectedLang.justNow as string
|
|
119
119
|
}
|
|
120
|
+
|
|
121
|
+
const formatMap = {
|
|
122
|
+
dd: { type: 'day', format: '2-digit' },
|
|
123
|
+
ddd: { type: 'weekday', format: 'short' },
|
|
124
|
+
dddd: { type: 'weekday', format: 'long' },
|
|
125
|
+
mm: { type: 'month', format: '2-digit' },
|
|
126
|
+
mmm: { type: 'month', format: 'short' },
|
|
127
|
+
mmmm: { type: 'month', format: 'long' },
|
|
128
|
+
yy: { type: 'year', format: '2-digit' },
|
|
129
|
+
yyyy: { type: 'year', format: 'numeric' },
|
|
130
|
+
HH: { type: 'hour', format: '2-digit' },
|
|
131
|
+
hh: { type: 'hour', format: '2-digit' },
|
|
132
|
+
MM: { type: 'minute', format: '2-digit' },
|
|
133
|
+
ss: { type: 'second', format: '2-digit' },
|
|
134
|
+
ampm: { type: 'dayPeriod', format: 'short' }
|
|
135
|
+
} as const
|
|
136
|
+
|
|
137
|
+
export function formatDate(date?: string | Date, format: string = 'dd.mm.yy') {
|
|
138
|
+
if (!date) return ''
|
|
139
|
+
try {
|
|
140
|
+
const formatParts = format.split(/[.\s:]/)
|
|
141
|
+
const formatObject: Record<string, string> = {}
|
|
142
|
+
|
|
143
|
+
for (const part of formatParts) {
|
|
144
|
+
const formatInfo = formatMap[part as keyof typeof formatMap]
|
|
145
|
+
if (!formatInfo) continue
|
|
146
|
+
formatObject[formatInfo.type] = formatInfo.format
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const d = typeof date === 'string' ? new Date(date) : date
|
|
150
|
+
|
|
151
|
+
return d.toLocaleDateString('he-IL', formatObject)
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return ''
|
|
154
|
+
}
|
|
155
|
+
}
|