@saasmakers/ui 2.1.2 → 2.1.4

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,7 +46,18 @@ 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 } = useDevice()
49
+ const focused = ref(false)
50
+ const { isDesktopBrowser, isIos } = useDevice()
51
+
52
+ const iosUppercaseTyping = computed(() => {
53
+ const isProseField = props.type === 'text' || props.type === 'search'
54
+
55
+ return props.uppercase && isIos.value && isProseField && !props.lowercaseOnly && !props.slugOnly
56
+ })
57
+
58
+ const hideCssUppercase = computed(() => {
59
+ return iosUppercaseTyping.value && focused.value
60
+ })
50
61
 
51
62
  onMounted(() => {
52
63
  if (props.autofocus && input.value && isDesktopBrowser.value) {
@@ -67,6 +78,8 @@ function focus() {
67
78
  }
68
79
 
69
80
  function onFieldBlur(event: FocusEvent) {
81
+ focused.value = false
82
+
70
83
  syncModelValueFromTarget(event.target)
71
84
 
72
85
  if (props.trim) {
@@ -77,6 +90,16 @@ function onFieldBlur(event: FocusEvent) {
77
90
  }
78
91
 
79
92
  function onFieldFocus(event: FocusEvent) {
93
+ if (iosUppercaseTyping.value) {
94
+ const uppercased = modelValue.value.toString().toUpperCase()
95
+
96
+ if (uppercased !== modelValue.value) {
97
+ modelValue.value = uppercased
98
+ }
99
+ }
100
+
101
+ focused.value = true
102
+
80
103
  emit('focus', event)
81
104
  }
82
105
 
@@ -193,10 +216,12 @@ defineExpose({
193
216
  :id="id"
194
217
  ref="input"
195
218
  :autocomplete="autocomplete ? 'on' : 'off'"
219
+ :autocapitalize="iosUppercaseTyping ? 'characters' : undefined"
196
220
  v-bind="autofocus ? { 'data-autofocus': '' } : {}"
197
221
  class="h-full w-full appearance-none items-center rounded-lg font-medium tracking-tight outline-none placeholder-gray-600 dark:placeholder-gray-400"
198
222
  :class="{
199
- 'uppercase': uppercase,
223
+ 'uppercase': uppercase && !hideCssUppercase,
224
+ 'placeholder:uppercase': hideCssUppercase,
200
225
  'field-disabled': disabled,
201
226
  'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
202
227
  'text-gray-900 dark:text-gray-100': !lineThrough,
@@ -30,7 +30,16 @@ 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 } = useDevice()
33
+ const focused = ref(false)
34
+ const { isDesktopBrowser, isIos } = useDevice()
35
+
36
+ const iosUppercaseTyping = computed(() => {
37
+ return props.uppercase && isIos.value
38
+ })
39
+
40
+ const hideCssUppercase = computed(() => {
41
+ return iosUppercaseTyping.value && focused.value
42
+ })
34
43
 
35
44
  onMounted(() => {
36
45
  if (props.autofocus && textarea.value && isDesktopBrowser.value) {
@@ -39,6 +48,8 @@ onMounted(() => {
39
48
  })
40
49
 
41
50
  function onFieldBlur(event: FocusEvent) {
51
+ focused.value = false
52
+
42
53
  syncModelValueFromTarget(event.target)
43
54
  emit('blur', event, modelValue.value || '')
44
55
  }
@@ -47,6 +58,18 @@ function onFieldClick(event: MouseEvent) {
47
58
  emit('click', event, modelValue.value || '')
48
59
  }
49
60
 
61
+ function onFieldFocus() {
62
+ if (iosUppercaseTyping.value) {
63
+ const uppercased = modelValue.value.toString().toUpperCase()
64
+
65
+ if (uppercased !== modelValue.value) {
66
+ modelValue.value = uppercased
67
+ }
68
+ }
69
+
70
+ focused.value = true
71
+ }
72
+
50
73
  function syncModelValueFromTarget(target: EventTarget | null) {
51
74
  if (!(target instanceof HTMLTextAreaElement)) {
52
75
  return
@@ -77,9 +100,11 @@ function syncModelValueFromTarget(target: EventTarget | null) {
77
100
  ref="textarea"
78
101
  v-model="modelValue"
79
102
  v-bind="{ 'data-enable-grammarly': 'false' }"
103
+ :autocapitalize="iosUppercaseTyping ? 'characters' : undefined"
80
104
  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"
81
105
  :class="{
82
- 'uppercase': uppercase,
106
+ 'uppercase': uppercase && !hideCssUppercase,
107
+ 'placeholder:uppercase': hideCssUppercase,
83
108
  'field-disabled': disabled,
84
109
  'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
85
110
  'px-sm py-2.5': padding && size === 'sm',
@@ -102,6 +127,7 @@ function syncModelValueFromTarget(target: EventTarget | null) {
102
127
  spellcheck="false"
103
128
  @blur="onFieldBlur"
104
129
  @click="onFieldClick"
130
+ @focus="onFieldFocus"
105
131
  />
106
132
 
107
133
  <FieldMessage
@@ -1,5 +1,6 @@
1
1
  export default function useDevice() {
2
- const { isNative } = useCapacitor()
2
+ const { isIOS, isNative } = useCapacitor()
3
+ const isIos = ref(false)
3
4
 
4
5
  const isDesktopBrowser = computed(() => {
5
6
  if (typeof navigator === 'undefined') {
@@ -18,5 +19,29 @@ export default function useDevice() {
18
19
  return !/Android|iPad|iPhone|iPod|Mobile/i.test(navigator.userAgent)
19
20
  })
20
21
 
21
- return { isDesktopBrowser }
22
+ onMounted(() => {
23
+ isIos.value = detectIosDevice(isIOS.value)
24
+ })
25
+
26
+ return {
27
+ isDesktopBrowser,
28
+ isIos,
29
+ }
30
+ }
31
+
32
+ function detectIosDevice(isNativeIos: boolean) {
33
+ if (isNativeIos) {
34
+ return true
35
+ }
36
+
37
+ if (typeof navigator === 'undefined') {
38
+ return false
39
+ }
40
+
41
+ if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
42
+ return true
43
+ }
44
+
45
+ // iPadOS 13+ uses a Macintosh user agent. Several touch points separate it from a Mac.
46
+ return navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1
22
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",