Skip to content

Commit 95039f4

Browse files
committed
improvement(landing): extract FeatureToggleItem to deduplicate accessibility logic
1 parent 53ac265 commit 95039f4

1 file changed

Lines changed: 78 additions & 71 deletions

File tree

apps/sim/app/(home)/components/enterprise/components/access-control-panel.tsx

Lines changed: 78 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,59 @@ function ProviderPreviewIcon({ providerId }: { providerId?: string }) {
8181
)
8282
}
8383

84+
interface FeatureToggleItemProps {
85+
feature: PermissionFeature
86+
enabled: boolean
87+
color: string
88+
isInView: boolean
89+
delay: number
90+
textClassName: string
91+
transition: Record<string, unknown>
92+
onToggle: () => void
93+
}
94+
95+
function FeatureToggleItem({
96+
feature,
97+
enabled,
98+
color,
99+
isInView,
100+
delay,
101+
textClassName,
102+
transition,
103+
onToggle,
104+
}: FeatureToggleItemProps) {
105+
return (
106+
<motion.div
107+
key={feature.key}
108+
role='button'
109+
tabIndex={0}
110+
aria-label={`Toggle ${feature.name}`}
111+
aria-pressed={enabled}
112+
className='flex cursor-pointer items-center gap-2 rounded-[4px] py-0.5'
113+
initial={{ opacity: 0, x: -6 }}
114+
animate={isInView ? { opacity: 1, x: 0 } : {}}
115+
transition={{ delay, ...transition }}
116+
onClick={onToggle}
117+
onKeyDown={(e) => {
118+
if (e.key === 'Enter' || e.key === ' ') {
119+
e.preventDefault()
120+
onToggle()
121+
}
122+
}}
123+
whileTap={{ scale: 0.98 }}
124+
>
125+
<CheckboxIcon checked={enabled} color={color} />
126+
<ProviderPreviewIcon providerId={feature.providerId} />
127+
<span
128+
className={textClassName}
129+
style={{ color: enabled ? '#F6F6F6AA' : '#F6F6F640' }}
130+
>
131+
{feature.name}
132+
</span>
133+
</motion.div>
134+
)
135+
}
136+
84137
export function AccessControlPanel() {
85138
const ref = useRef(null)
86139
const isInView = useInView(ref, { once: true, margin: '-40px' })
@@ -101,45 +154,21 @@ export function AccessControlPanel() {
101154
{category.label}
102155
</span>
103156
<div className='mt-2 grid grid-cols-2 gap-x-4 gap-y-2'>
104-
{category.features.map((feature, featIdx) => {
105-
const enabled = accessState[feature.key]
106-
107-
return (
108-
<motion.div
109-
key={feature.key}
110-
role='button'
111-
tabIndex={0}
112-
aria-label={`Toggle ${feature.name}`}
113-
aria-pressed={enabled}
114-
className='flex cursor-pointer items-center gap-2 rounded-[4px] py-0.5'
115-
initial={{ opacity: 0, x: -6 }}
116-
animate={isInView ? { opacity: 1, x: 0 } : {}}
117-
transition={{
118-
delay: 0.05 + (offsetBefore + featIdx) * 0.04,
119-
duration: 0.3,
120-
}}
121-
onClick={() =>
122-
setAccessState((prev) => ({ ...prev, [feature.key]: !prev[feature.key] }))
123-
}
124-
onKeyDown={(e) => {
125-
if (e.key === 'Enter' || e.key === ' ') {
126-
e.preventDefault()
127-
setAccessState((prev) => ({ ...prev, [feature.key]: !prev[feature.key] }))
128-
}
129-
}}
130-
whileTap={{ scale: 0.98 }}
131-
>
132-
<CheckboxIcon checked={enabled} color={category.color} />
133-
<ProviderPreviewIcon providerId={feature.providerId} />
134-
<span
135-
className='truncate font-[430] font-season text-[13px] leading-none tracking-[0.02em]'
136-
style={{ color: enabled ? '#F6F6F6AA' : '#F6F6F640' }}
137-
>
138-
{feature.name}
139-
</span>
140-
</motion.div>
141-
)
142-
})}
157+
{category.features.map((feature, featIdx) => (
158+
<FeatureToggleItem
159+
key={feature.key}
160+
feature={feature}
161+
enabled={accessState[feature.key]}
162+
color={category.color}
163+
isInView={isInView}
164+
delay={0.05 + (offsetBefore + featIdx) * 0.04}
165+
textClassName='truncate font-[430] font-season text-[13px] leading-none tracking-[0.02em]'
166+
transition={{ duration: 0.3 }}
167+
onToggle={() =>
168+
setAccessState((prev) => ({ ...prev, [feature.key]: !prev[feature.key] }))
169+
}
170+
/>
171+
))}
143172
</div>
144173
</div>
145174
)
@@ -155,48 +184,26 @@ export function AccessControlPanel() {
155184
</span>
156185
<div className='mt-2 grid grid-cols-2 gap-x-4 gap-y-2'>
157186
{category.features.map((feature, featIdx) => {
158-
const enabled = accessState[feature.key]
159187
const currentIndex =
160188
PERMISSION_CATEGORIES.slice(0, catIdx).reduce(
161189
(sum, c) => sum + c.features.length,
162190
0
163191
) + featIdx
164192

165193
return (
166-
<motion.div
194+
<FeatureToggleItem
167195
key={feature.key}
168-
role='button'
169-
tabIndex={0}
170-
aria-label={`Toggle ${feature.name}`}
171-
aria-pressed={enabled}
172-
className='flex cursor-pointer items-center gap-2 rounded-[4px] py-0.5'
173-
initial={{ opacity: 0, x: -6 }}
174-
animate={isInView ? { opacity: 1, x: 0 } : {}}
175-
transition={{
176-
delay: 0.1 + currentIndex * 0.04,
177-
duration: 0.3,
178-
ease: [0.25, 0.46, 0.45, 0.94],
179-
}}
180-
onClick={() =>
196+
feature={feature}
197+
enabled={accessState[feature.key]}
198+
color={category.color}
199+
isInView={isInView}
200+
delay={0.1 + currentIndex * 0.04}
201+
textClassName='truncate font-[430] font-season text-[11px] leading-none tracking-[0.02em] transition-opacity duration-200'
202+
transition={{ duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] }}
203+
onToggle={() =>
181204
setAccessState((prev) => ({ ...prev, [feature.key]: !prev[feature.key] }))
182205
}
183-
onKeyDown={(e) => {
184-
if (e.key === 'Enter' || e.key === ' ') {
185-
e.preventDefault()
186-
setAccessState((prev) => ({ ...prev, [feature.key]: !prev[feature.key] }))
187-
}
188-
}}
189-
whileTap={{ scale: 0.98 }}
190-
>
191-
<CheckboxIcon checked={enabled} color={category.color} />
192-
<ProviderPreviewIcon providerId={feature.providerId} />
193-
<span
194-
className='truncate font-[430] font-season text-[11px] leading-none tracking-[0.02em] transition-opacity duration-200'
195-
style={{ color: enabled ? '#F6F6F6AA' : '#F6F6F640' }}
196-
>
197-
{feature.name}
198-
</span>
199-
</motion.div>
206+
/>
200207
)
201208
})}
202209
</div>

0 commit comments

Comments
 (0)