Skip to content

Commit d7341cc

Browse files
committed
Add support for internal and external input decorations
External decorations are separated using an extra line
1 parent f9fac71 commit d7341cc

2 files changed

Lines changed: 86 additions & 9 deletions

File tree

src/components/ui/input.tsx

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,23 @@ function Input({
2626
afterEndDecoration,
2727
...props
2828
}: React.ComponentProps<'input'> & OwnProps) {
29+
const beforeStartDecorationRef = useRef<HTMLSpanElement>(null)
30+
const [beforeStartDecorationWidth, setBeforeStartDecorationWidth] = useState<number>(0)
2931
const startDecorationRef = useRef<HTMLSpanElement>(null)
3032
const [startDecorationWidth, setStartDecorationWidth] = useState<number>(0)
3133
const endDecorationRef = useRef<HTMLSpanElement>(null)
3234
const [endDecorationWidth, setEndDecorationWidth] = useState<number>(0)
35+
const afterEndDecorationRef = useRef<HTMLSpanElement>(null)
36+
const [afterEndDecorationWidth, setAfterEndDecorationWidth] = useState<number>(0)
37+
38+
useEffect(() => {
39+
if (!!beforeStartDecoration && !!beforeStartDecorationRef.current) {
40+
const rect = beforeStartDecorationRef.current.getBoundingClientRect()
41+
setBeforeStartDecorationWidth(rect.width)
42+
} else {
43+
setBeforeStartDecorationWidth(0)
44+
}
45+
}, [beforeStartDecoration, beforeStartDecorationRef.current])
3346

3447
useEffect(() => {
3548
if (!!startDecoration && !!startDecorationRef.current) {
@@ -49,17 +62,47 @@ function Input({
4962
}
5063
}, [endDecoration, endDecorationRef.current]) // Re-run if content changes
5164

52-
const startStyle = !!startDecorationWidth ? { paddingLeft: `${16 + startDecorationWidth}px` } : {}
53-
const endStyle = !!endDecorationWidth ? { paddingRight: `${16 + endDecorationWidth}px` } : {}
65+
useEffect(() => {
66+
if (!!afterEndDecoration && !!afterEndDecorationRef.current) {
67+
const rect = afterEndDecorationRef.current.getBoundingClientRect()
68+
setAfterEndDecorationWidth(rect.width)
69+
} else {
70+
setAfterEndDecorationWidth(0)
71+
}
72+
}, [afterEndDecoration, afterEndDecorationRef.current]) // Re-run if content changes
73+
74+
const startDecoratorStyle = beforeStartDecorationWidth
75+
? { paddingLeft: `${8 + beforeStartDecorationWidth}px` }
76+
: {}
77+
78+
const startStyle =
79+
!!startDecorationWidth || beforeStartDecorationWidth
80+
? { paddingLeft: `${16 + beforeStartDecorationWidth + startDecorationWidth}px` }
81+
: {}
82+
83+
const endStyle =
84+
!!endDecorationWidth || !afterEndDecorationWidth
85+
? { paddingRight: `${16 + endDecorationWidth + afterEndDecorationWidth}px` }
86+
: {}
87+
88+
const endDecoratorStyle = !!afterEndDecorationWidth
89+
? { paddingRight: `${8 + afterEndDecorationWidth}px` }
90+
: {}
5491

55-
// const hasInsideDecorations = !!startDecoration || !!endDecoration
56-
// const hasOutsideDecorations = !!beforeStartDecoration || !!afterEndDecoration
5792
return (
5893
<>
59-
{beforeStartDecoration}
6094
<div className={'relative'}>
95+
{!!beforeStartDecoration && (
96+
<span
97+
className={'absolute left-2.5 top-2.5 pr-2.5'}
98+
style={{ borderRight: '2px solid black' }}
99+
ref={beforeStartDecorationRef}
100+
>
101+
{beforeStartDecoration}
102+
</span>
103+
)}
61104
{!!startDecoration && (
62-
<span className={'absolute left-2.5 top-2.5'} ref={startDecorationRef}>
105+
<span className={'absolute left-2.5 top-2.5'} style={startDecoratorStyle} ref={startDecorationRef}>
63106
{startDecoration}
64107
</span>
65108
)}
@@ -76,12 +119,20 @@ function Input({
76119
{...props}
77120
/>
78121
{!!endDecoration && (
79-
<span className={'absolute right-2.5 top-2.5'} ref={endDecorationRef}>
122+
<span className={'absolute right-2.5 top-2.5'} style={endDecoratorStyle} ref={endDecorationRef}>
80123
{endDecoration}
81124
</span>
82125
)}
126+
{!!afterEndDecoration && (
127+
<span
128+
className={'absolute right-2.5 top-2.5 pl-2.5'}
129+
style={{ borderLeft: '2px solid black' }}
130+
ref={afterEndDecorationRef}
131+
>
132+
{afterEndDecoration}
133+
</span>
134+
)}
83135
</div>
84-
{afterEndDecoration}
85136
</>
86137
)
87138
}

src/stories/Input/Input.stories.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const WithManualIcon: Story = {
5757
),
5858
}
5959

60-
export const WithStartAndEndDecoration: Story = {
60+
export const WithInternalStartAndEndDecoration: Story = {
6161
render: () => (
6262
<div className="w-[300px]">
6363
<Input
@@ -69,6 +69,32 @@ export const WithStartAndEndDecoration: Story = {
6969
),
7070
}
7171

72+
export const WithExternalStartAndEndDecoration: Story = {
73+
render: () => (
74+
<div className="w-[300px]">
75+
<Input
76+
placeholder="Search..."
77+
beforeStartDecoration={<SettingsIcon className="h-4 w-4 text-muted-foreground" />}
78+
afterEndDecoration={<LaunchIcon className="h-4 w-4 text-muted-foreground" />}
79+
/>
80+
</div>
81+
),
82+
}
83+
84+
export const WithDoubleStartAndEndDecoration: Story = {
85+
render: () => (
86+
<div className="w-[300px]">
87+
<Input
88+
placeholder="Search..."
89+
beforeStartDecoration={<SettingsIcon className="h-4 w-4 text-muted-foreground" />}
90+
startDecoration={<SettingsIcon className="h-4 w-4" />}
91+
endDecoration={<LaunchIcon className="h-4 w-4" />}
92+
afterEndDecoration={<LaunchIcon className="h-4 w-4 text-muted-foreground" />}
93+
/>
94+
</div>
95+
),
96+
}
97+
7298
export const Invalid: Story = {
7399
render: () => (
74100
<div className="grid w-full max-w-sm items-center gap-1.5">

0 commit comments

Comments
 (0)