@saasmakers/ui 2.1.3 → 2.1.5
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.
|
@@ -46,12 +46,15 @@ const id = useId()
|
|
|
46
46
|
const modelValue = defineModel<FieldInput['modelValue']>({ default: '' })
|
|
47
47
|
const input = ref<HTMLInputElement>()
|
|
48
48
|
const showPassword = ref(false)
|
|
49
|
-
const { isDesktopBrowser
|
|
49
|
+
const { isDesktopBrowser } = useDevice()
|
|
50
50
|
|
|
51
|
-
const
|
|
52
|
-
|
|
51
|
+
const { isTypingOnIos, onBlur: onIosUppercaseBlur, onFocus: onIosUppercaseFocus, usesIosUppercaseKeyboard } = useIosUppercase({
|
|
52
|
+
enabled: () => {
|
|
53
|
+
const isProseField = props.type === 'text' || props.type === 'search'
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
return props.uppercase && isProseField && !props.lowercaseOnly && !props.slugOnly
|
|
56
|
+
},
|
|
57
|
+
modelValue,
|
|
55
58
|
})
|
|
56
59
|
|
|
57
60
|
onMounted(() => {
|
|
@@ -73,6 +76,7 @@ function focus() {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
function onFieldBlur(event: FocusEvent) {
|
|
79
|
+
onIosUppercaseBlur()
|
|
76
80
|
syncModelValueFromTarget(event.target)
|
|
77
81
|
|
|
78
82
|
if (props.trim) {
|
|
@@ -83,14 +87,7 @@ function onFieldBlur(event: FocusEvent) {
|
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
function onFieldFocus(event: FocusEvent) {
|
|
86
|
-
|
|
87
|
-
const uppercased = modelValue.value.toString().toUpperCase()
|
|
88
|
-
|
|
89
|
-
if (uppercased !== modelValue.value) {
|
|
90
|
-
modelValue.value = uppercased
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
90
|
+
onIosUppercaseFocus()
|
|
94
91
|
emit('focus', event)
|
|
95
92
|
}
|
|
96
93
|
|
|
@@ -207,12 +204,12 @@ defineExpose({
|
|
|
207
204
|
:id="id"
|
|
208
205
|
ref="input"
|
|
209
206
|
:autocomplete="autocomplete ? 'on' : 'off'"
|
|
210
|
-
:autocapitalize="
|
|
207
|
+
:autocapitalize="usesIosUppercaseKeyboard ? 'characters' : undefined"
|
|
211
208
|
v-bind="autofocus ? { 'data-autofocus': '' } : {}"
|
|
212
209
|
class="h-full w-full appearance-none items-center rounded-lg font-medium tracking-tight outline-none placeholder-gray-600 dark:placeholder-gray-400"
|
|
213
210
|
:class="{
|
|
214
|
-
'uppercase': uppercase && !
|
|
215
|
-
'placeholder:uppercase':
|
|
211
|
+
'uppercase': uppercase && !isTypingOnIos,
|
|
212
|
+
'placeholder:uppercase': isTypingOnIos,
|
|
216
213
|
'field-disabled': disabled,
|
|
217
214
|
'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
|
|
218
215
|
'text-gray-900 dark:text-gray-100': !lineThrough,
|
|
@@ -30,10 +30,11 @@ const emit = defineEmits<{
|
|
|
30
30
|
const id = useId()
|
|
31
31
|
const modelValue = defineModel<FieldTextarea['modelValue']>({ default: '' })
|
|
32
32
|
const textarea = ref<HTMLTextAreaElement>()
|
|
33
|
-
const { isDesktopBrowser
|
|
33
|
+
const { isDesktopBrowser } = useDevice()
|
|
34
34
|
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const { isTypingOnIos, onBlur: onIosUppercaseBlur, onFocus: onIosUppercaseFocus, usesIosUppercaseKeyboard } = useIosUppercase({
|
|
36
|
+
enabled: () => props.uppercase,
|
|
37
|
+
modelValue,
|
|
37
38
|
})
|
|
38
39
|
|
|
39
40
|
onMounted(() => {
|
|
@@ -43,6 +44,7 @@ onMounted(() => {
|
|
|
43
44
|
})
|
|
44
45
|
|
|
45
46
|
function onFieldBlur(event: FocusEvent) {
|
|
47
|
+
onIosUppercaseBlur()
|
|
46
48
|
syncModelValueFromTarget(event.target)
|
|
47
49
|
emit('blur', event, modelValue.value || '')
|
|
48
50
|
}
|
|
@@ -51,18 +53,6 @@ function onFieldClick(event: MouseEvent) {
|
|
|
51
53
|
emit('click', event, modelValue.value || '')
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function onFieldFocus() {
|
|
55
|
-
if (!iosUppercaseTyping.value) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const uppercased = modelValue.value.toString().toUpperCase()
|
|
60
|
-
|
|
61
|
-
if (uppercased !== modelValue.value) {
|
|
62
|
-
modelValue.value = uppercased
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
56
|
function syncModelValueFromTarget(target: EventTarget | null) {
|
|
67
57
|
if (!(target instanceof HTMLTextAreaElement)) {
|
|
68
58
|
return
|
|
@@ -93,11 +83,11 @@ function syncModelValueFromTarget(target: EventTarget | null) {
|
|
|
93
83
|
ref="textarea"
|
|
94
84
|
v-model="modelValue"
|
|
95
85
|
v-bind="{ 'data-enable-grammarly': 'false' }"
|
|
96
|
-
:autocapitalize="
|
|
86
|
+
:autocapitalize="usesIosUppercaseKeyboard ? 'characters' : undefined"
|
|
97
87
|
class="w-full flex-1 appearance-none rounded-lg text-gray-900 font-medium tracking-tight outline-none dark:text-gray-100 placeholder-gray-600 dark:placeholder-gray-400"
|
|
98
88
|
:class="{
|
|
99
|
-
'uppercase': uppercase && !
|
|
100
|
-
'placeholder:uppercase':
|
|
89
|
+
'uppercase': uppercase && !isTypingOnIos,
|
|
90
|
+
'placeholder:uppercase': isTypingOnIos,
|
|
101
91
|
'field-disabled': disabled,
|
|
102
92
|
'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
|
|
103
93
|
'px-sm py-2.5': padding && size === 'sm',
|
|
@@ -120,7 +110,7 @@ function syncModelValueFromTarget(target: EventTarget | null) {
|
|
|
120
110
|
spellcheck="false"
|
|
121
111
|
@blur="onFieldBlur"
|
|
122
112
|
@click="onFieldClick"
|
|
123
|
-
@focus="
|
|
113
|
+
@focus="onIosUppercaseFocus"
|
|
124
114
|
/>
|
|
125
115
|
|
|
126
116
|
<FieldMessage
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export default function useDevice() {
|
|
2
2
|
const { isIOS, isNative } = useCapacitor()
|
|
3
|
-
|
|
4
|
-
const isIos = ref(false)
|
|
3
|
+
const isIosDevice = ref(false)
|
|
5
4
|
|
|
6
5
|
const isDesktopBrowser = computed(() => {
|
|
7
6
|
if (typeof navigator === 'undefined') {
|
|
@@ -21,24 +20,16 @@ export default function useDevice() {
|
|
|
21
20
|
})
|
|
22
21
|
|
|
23
22
|
onMounted(() => {
|
|
24
|
-
|
|
23
|
+
isIosDevice.value = isIOS.value || isIosBrowser()
|
|
25
24
|
})
|
|
26
25
|
|
|
27
26
|
return {
|
|
28
27
|
isDesktopBrowser,
|
|
29
|
-
|
|
28
|
+
isIosDevice,
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
function
|
|
34
|
-
if (isNativeIos) {
|
|
35
|
-
return true
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof navigator === 'undefined') {
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
32
|
+
function isIosBrowser() {
|
|
42
33
|
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
|
|
43
34
|
return true
|
|
44
35
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// iOS WebKit drops keyboard suggestions while `text-transform: uppercase` is active,
|
|
2
|
+
// so the transform is lifted during typing and the value itself is uppercased instead.
|
|
3
|
+
export default function useIosUppercase(options: {
|
|
4
|
+
enabled: MaybeRefOrGetter<boolean>
|
|
5
|
+
modelValue: Ref<string>
|
|
6
|
+
}) {
|
|
7
|
+
const { isIosDevice } = useDevice()
|
|
8
|
+
const focused = ref(false)
|
|
9
|
+
|
|
10
|
+
const usesIosUppercaseKeyboard = computed(() => {
|
|
11
|
+
return toValue(options.enabled) && isIosDevice.value
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const isTypingOnIos = computed(() => {
|
|
15
|
+
return usesIosUppercaseKeyboard.value && focused.value
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
function onFocus() {
|
|
19
|
+
if (usesIosUppercaseKeyboard.value) {
|
|
20
|
+
options.modelValue.value = options.modelValue.value.toUpperCase()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
focused.value = true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function onBlur() {
|
|
27
|
+
focused.value = false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
isTypingOnIos,
|
|
32
|
+
onBlur,
|
|
33
|
+
onFocus,
|
|
34
|
+
usesIosUppercaseKeyboard,
|
|
35
|
+
}
|
|
36
|
+
}
|