ui icon indicating copy to clipboard operation
ui copied to clipboard

How to change the default chevron icon in Accordion to +/- ?

Open Huydnph076901606 opened this issue 2 years ago • 10 comments

How to change the default chevron icon in Accordion to +/- ?

Huydnph076901606 avatar Aug 07 '23 17:08 Huydnph076901606

ChevronDownIcon is hardcoded in the @/component/ui/accordion but you can easily replace it with PlusIcon & MinusIcon.

P.S. you need to change the animation (accordion-down) in the AccordionContent because it rotate the chevron

ralicata avatar Aug 07 '23 20:08 ralicata

Hi @Huydnph076901606 you can do like this:

'use client';

import * as React from 'react';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { ChevronDown, Plus, Minus } from 'lucide-react';

import { cn } from '@/lib/utils';

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
  <AccordionPrimitive.Item ref={ref} className={cn('border-b', className)} {...props} />
));
AccordionItem.displayName = 'AccordionItem';

const AccordionTrigger = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <AccordionPrimitive.Header className="flex">
    <AccordionPrimitive.Trigger
      ref={ref}
      className={cn(
        'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline group',
        className
      )}
      {...props}
    >
      {children}
      <Plus className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:hidden" />
      <Minus className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=closed]:hidden" />
    </AccordionPrimitive.Trigger>
  </AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <AccordionPrimitive.Content
    ref={ref}
    className={cn(
      'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
      className
    )}
    {...props}
  >
    <div className="pb-4 pt-0">{children}</div>
  </AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

The Plus and Minus icon will be hidden based on the current state of the accordion.

dan5py avatar Aug 08 '23 20:08 dan5py

@dan5py Thanks a lot, this is exactly what i wanted

Huydnph076901606 avatar Aug 09 '23 07:08 Huydnph076901606

@dan5py thanks a lot, didn't realise group was the solution, i was trying to rotate the vertical line of a + icon to make it -.

for ref:

<div className="relative">
  <div className="absolute h-[1px] w-[9px] bg-white" />
  <div className="absolute h-[1px] w-[9px] bg-white transition-transform duration-200 group-data-[state=closed]:rotate-90 group-data-[state=open]:rotate-0" />
</div>

Omkar-omi avatar Oct 11 '23 16:10 Omkar-omi

how to remove the ChevronDown icon in @/component/ui/accordion

Ansar0088 avatar Jan 29 '24 08:01 Ansar0088

As answered in #777 you can change the <ChevronDown /> component in the accordion.tsx file and insert whatever icon you like or just remove it.

const AccordionTrigger = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <AccordionPrimitive.Header className="flex">
    <AccordionPrimitive.Trigger
     ref={ref}
     className={cn(
       "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
       className
     )}
     {...props}
   >
     {children}
- <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" /> // <-- Old Icon
+ // Add any icon here 
    </AccordionPrimitive.Trigger>
  </AccordionPrimitive.Header>
))

Omkar-omi avatar Jan 29 '24 09:01 Omkar-omi

Hi @Huydnph076901606 you can do like this:

'use client';

import * as React from 'react';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { ChevronDown, Plus, Minus } from 'lucide-react';

import { cn } from '@/lib/utils';

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
  <AccordionPrimitive.Item ref={ref} className={cn('border-b', className)} {...props} />
));
AccordionItem.displayName = 'AccordionItem';

const AccordionTrigger = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <AccordionPrimitive.Header className="flex">
    <AccordionPrimitive.Trigger
      ref={ref}
      className={cn(
        'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline group',
        className
      )}
      {...props}
    >
      {children}
      <Plus className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:hidden" />
      <Minus className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=closed]:hidden" />
    </AccordionPrimitive.Trigger>
  </AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <AccordionPrimitive.Content
    ref={ref}
    className={cn(
      'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
      className
    )}
    {...props}
  >
    <div className="pb-4 pt-0">{children}</div>
  </AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

The Plus and Minus icon will be hidden based on the current state of the accordion.

Thanks brother!

matiassemelman avatar Apr 08 '24 15:04 matiassemelman

You can use only CSS instead of icons, so you can still use rotate animation instead of hidden.

"use client"
   
 import * as React from "react"
 import * as AccordionPrimitive from "@radix-ui/react-accordion"
 // import { ChevronDown } from "lucide-react"
 
 import { cn } from "@/lib/utils"
 
 const Accordion = AccordionPrimitive.Root
 
 const AccordionItem = React.forwardRef<
   React.ElementRef<typeof AccordionPrimitive.Item>,
   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
 >(({ className, ...props }, ref) => (
   <AccordionPrimitive.Item
     ref={ref}
     className={cn("border-b", className)}
     {...props}
   />
 ))
 AccordionItem.displayName = "AccordionItem"
 
 const AccordionTrigger = React.forwardRef<
   React.ElementRef<typeof AccordionPrimitive.Trigger>,
   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
 >(({ className, children, ...props }, ref) => (
   <AccordionPrimitive.Header className="flex">
     <AccordionPrimitive.Trigger
       ref={ref}
       className={cn(
         "flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>span]:rotate-180 group",
         className
       )}
       {...props}
     >
       {children}
       <span className="relative h-3 w-3 shrink-0 transition-transform duration-200 custom-plus-minus group-data-[state=open]:after:opacity-0"></span>
       {/* <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" /> */}
     </AccordionPrimitive.Trigger>
   </AccordionPrimitive.Header>
 ))
 AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
 
 const AccordionContent = React.forwardRef<
   React.ElementRef<typeof AccordionPrimitive.Content>,
   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
 >(({ className, children, ...props }, ref) => (
   <AccordionPrimitive.Content
     ref={ref}
     className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
     {...props}
   >
     <div className={cn("pb-4 pt-0", className)}>{children}</div>
   </AccordionPrimitive.Content>
 ))
 
 AccordionContent.displayName = AccordionPrimitive.Content.displayName
 
 export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

CSS

.custom-plus-minus:before,
.custom-plus-minus:after {
  content: "";
  display: block;
  position: absolute;
  top: 50%;
  left: 0;
  background-color: hsl(var(--foreground));
  width: 100%;
  height: 1px;
}

.custom-plus-minus:before {
  transform: translatey(-50%);
}

.custom-plus-minus:after {
  transform: translatey(-50%) rotate(90deg);
}

jeanzuck avatar Jul 04 '24 15:07 jeanzuck

Thank you very much @dan5py ❤️

Uhasith avatar Jul 09 '24 13:07 Uhasith

thanks man @jeanzuck @dan5py

abdullahmiraz avatar Jul 25 '24 04:07 abdullahmiraz