Skip to content

Commit 6d39697

Browse files
authored
Improved input layout to support proper positioning for right-to-left languages (#322)
* feat: FwbInput: Improved input layout to support proper positioning for different lang directions * Some fixed in focus managements and sizes for prefix/suffix
1 parent 8810269 commit 6d39697

File tree

3 files changed

+119
-39
lines changed

3 files changed

+119
-39
lines changed

docs/components/input/examples/FwbInputExampleSuffix.vue

Lines changed: 95 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,100 @@
11
<template>
22
<div class="vp-raw">
3-
<fwb-input
4-
v-model="name"
5-
label="Search"
6-
placeholder="enter your search query"
7-
size="lg"
8-
>
9-
<template #prefix>
10-
<svg
11-
aria-hidden="true"
12-
class="w-5 h-5 text-gray-500 dark:text-gray-400"
13-
fill="none"
14-
stroke="currentColor"
15-
viewBox="0 0 24 24"
16-
xmlns="http://www.w3.org/2000/svg"
17-
>
18-
<path
19-
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
20-
stroke-linecap="round"
21-
stroke-linejoin="round"
22-
stroke-width="2"
23-
/>
24-
</svg>
25-
</template>
26-
<template #suffix>
27-
<fwb-button>Search</fwb-button>
28-
</template>
29-
</fwb-input>
3+
<div class="mb-4">
4+
<fwb-input
5+
v-model="name"
6+
label="Search"
7+
placeholder="enter your search query"
8+
size="lg"
9+
>
10+
<template #prefix>
11+
<svg
12+
aria-hidden="true"
13+
class="w-5 h-5 text-gray-500 dark:text-gray-400"
14+
fill="none"
15+
stroke="currentColor"
16+
viewBox="0 0 24 24"
17+
xmlns="http://www.w3.org/2000/svg"
18+
>
19+
<path
20+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
21+
stroke-linecap="round"
22+
stroke-linejoin="round"
23+
stroke-width="2"
24+
/>
25+
</svg>
26+
</template>
27+
<template #suffix>
28+
<fwb-button size="md">
29+
Search
30+
</fwb-button>
31+
</template>
32+
</fwb-input>
33+
</div>
34+
35+
<div class="mb-4">
36+
<fwb-input
37+
v-model="name"
38+
label="Search"
39+
placeholder="enter your search query"
40+
size="md"
41+
>
42+
<template #prefix>
43+
<svg
44+
aria-hidden="true"
45+
class="w-5 h-5 text-gray-500 dark:text-gray-400"
46+
fill="none"
47+
stroke="currentColor"
48+
viewBox="0 0 24 24"
49+
xmlns="http://www.w3.org/2000/svg"
50+
>
51+
<path
52+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
53+
stroke-linecap="round"
54+
stroke-linejoin="round"
55+
stroke-width="2"
56+
/>
57+
</svg>
58+
</template>
59+
<template #suffix>
60+
<fwb-button size="sm">
61+
Search
62+
</fwb-button>
63+
</template>
64+
</fwb-input>
65+
</div>
66+
67+
<div class="mb-4">
68+
<fwb-input
69+
v-model="name"
70+
label="Search"
71+
placeholder="enter your search query"
72+
size="sm"
73+
>
74+
<template #prefix>
75+
<svg
76+
aria-hidden="true"
77+
class="w-5 h-5 text-gray-500 dark:text-gray-400"
78+
fill="none"
79+
stroke="currentColor"
80+
viewBox="0 0 24 24"
81+
xmlns="http://www.w3.org/2000/svg"
82+
>
83+
<path
84+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
85+
stroke-linecap="round"
86+
stroke-linejoin="round"
87+
stroke-width="2"
88+
/>
89+
</svg>
90+
</template>
91+
<template #suffix>
92+
<fwb-button size="xs">
93+
Search
94+
</fwb-button>
95+
</template>
96+
</fwb-input>
97+
</div>
3098
</div>
3199
</template>
32100

src/components/FwbInput/FwbInput.vue

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
v-if="label"
55
:class="labelClasses"
66
>{{ label }}</label>
7-
<div class="flex relative">
7+
<div
8+
class="flex relative items-center"
9+
:class="[inputBlockClasses]"
10+
>
811
<div
912
v-if="$slots.prefix"
10-
class="w-10 flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none overflow-hidden"
13+
class="flex items-center ms-2 flex-shrink-0"
1114
>
1215
<slot name="prefix" />
1316
</div>
@@ -18,11 +21,11 @@
1821
:type="type"
1922
:required="required"
2023
:autocomplete="autocomplete"
21-
:class="[inputClasses, $slots.prefix ? 'pl-10' : '']"
24+
:class="[inputClasses]"
2225
>
2326
<div
2427
v-if="$slots.suffix"
25-
class="absolute right-2.5 bottom-2.5"
28+
class="flex items-center me-2 flex-shrink-0"
2629
>
2730
<slot name="suffix" />
2831
</div>
@@ -85,7 +88,7 @@ const props = withDefaults(defineProps<InputProps>(), {
8588
8689
const model = useVModel(props, 'modelValue')
8790
88-
const { inputClasses, labelClasses } = useInputClasses(toRefs(props))
91+
const { inputClasses, inputBlockClasses, labelClasses } = useInputClasses(toRefs(props))
8992
9093
const validationWrapperClasses = computed(() => twMerge(
9194
'mt-2 text-sm',

src/components/FwbInput/composables/useInputClasses.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ import {
1010
const baseLabelClasses = 'block mb-2 text-sm font-medium'
1111

1212
// INPUT
13-
const defaultInputClasses =
14-
'bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500'
13+
const defaultInputClasses = 'block flex-grow w-full p-0 bg-transparent text-inherit ring-offset-0 ring-0 border-0 focus:ring-offset-0 focus:ring-0 focus:border-0'
14+
15+
// BLOCK
16+
const defaultBlockClasses =
17+
'has-[input:focus]:ring-offset-0 has-[input:focus]:ring-1 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg has-[input:focus]:ring-blue-500 has-[input:focus]:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:has-[input:focus]:ring-blue-500 dark:has-[input:focus]:border-blue-500'
18+
1519
const disabledInputClasses = 'cursor-not-allowed bg-gray-100'
1620
const inputSizeClasses: Record<InputSize, string> = {
1721
lg: 'p-4',
1822
md: 'p-2.5 text-sm',
1923
sm: 'p-2 text-sm',
2024
}
2125

22-
const successInputClasses = 'bg-green-50 border-green-500 dark:border-green-500 text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 focus:ring-green-500 focus:border-green-500'
23-
const errorInputClasses = 'bg-red-50 border-red-500 text-red-900 placeholder-red-700 focus:ring-red-500 focus:border-red-500 dark:text-red-500 dark:placeholder-red-500 dark:border-red-500'
26+
const successInputClasses = 'bg-green-50 border-green-500 dark:border-green-500 text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 has-[input:focus]:ring-green-500 has-[input:focus]:border-green-500'
27+
const errorInputClasses = 'bg-red-50 border-red-500 text-red-900 placeholder-red-700 has-[input:focus]:ring-red-500 has-[input:focus]:border-red-500 dark:text-red-500 dark:placeholder-red-500 dark:border-red-500'
2428

2529
export type UseInputClassesProps = {
2630
size: Ref<InputSize>
@@ -29,10 +33,11 @@ export type UseInputClassesProps = {
2933
}
3034

3135
export function useInputClasses (props: UseInputClassesProps): {
36+
inputBlockClasses: Ref<string>
3237
inputClasses: Ref<string>
3338
labelClasses: Ref<string>
3439
} {
35-
const inputClasses = computed(() => {
40+
const inputBlockClasses = computed(() => {
3641
const vs = props.validationStatus.value
3742

3843
const classByStatus = vs === validationStatusMap.Success
@@ -42,13 +47,16 @@ export function useInputClasses (props: UseInputClassesProps): {
4247
: ''
4348

4449
return twMerge(
45-
defaultInputClasses,
50+
defaultBlockClasses,
4651
classByStatus,
47-
inputSizeClasses[props.size.value],
4852
props.disabled.value ? disabledInputClasses : '',
4953
)
5054
})
5155

56+
const inputClasses = computed(() => {
57+
return twMerge(defaultInputClasses, inputSizeClasses[props.size.value])
58+
})
59+
5260
const labelClasses = computed(() => {
5361
const vs = props.validationStatus.value
5462
const classByStatus = vs === validationStatusMap.Success
@@ -61,6 +69,7 @@ export function useInputClasses (props: UseInputClassesProps): {
6169
})
6270

6371
return {
72+
inputBlockClasses,
6473
inputClasses,
6574
labelClasses,
6675
}

0 commit comments

Comments
 (0)