Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions js/react/lib/components/topic/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./topic.component";
export * from "./subtopic.component";
export * from "./topic.types";
19 changes: 19 additions & 0 deletions js/react/lib/components/topic/subtopic.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Badge } from "../badge";
import { Level } from "../level";
import { ButtonHTMLAttributes } from "react";
import { TopicElement } from "./topic.types";
import { withAs } from "@/utils/hoc";

type SubTopicProps = TopicElement & ButtonHTMLAttributes<HTMLButtonElement>;

export const SubTopic = withAs(
(Component, { level, state, title, ...rest }: SubTopicProps) => {
return (
<Component {...rest} tabIndex={0} className="rustlanges-subtopic">
<Level variant={level} />
<span className="rustlanges-subtopic__title">{title}</span>
<Badge type="default" variant={state} />
</Component>
);
}
);
20 changes: 20 additions & 0 deletions js/react/lib/components/topic/topic.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PropsWithChildren } from "react";
import { ArrowDown } from "@/icons";
import { Badge } from "../badge";
import { Level } from "../level";
import { TopicElement } from "./topic.types";

type TopicProps = PropsWithChildren & TopicElement;
export const Topic = ({ level, title, state, children }: TopicProps) => {
return (
<details className="rustlanges-topic">
<summary className="rustlanges-topic__summary">
<Level as="span" variant={level} />
<span className="text-h6 rustlanges-topic__title">{title}</span>
<Badge type="default" variant={state} />
<ArrowDown />
</summary>
{children}
</details>
);
};
8 changes: 8 additions & 0 deletions js/react/lib/components/topic/topic.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BadgeVariants } from "../badge/badge.const";
import { LevelVariants } from "../level/level.const";

export type TopicElement = {
level: LevelVariants;
title: string;
state: BadgeVariants;
};
1 change: 1 addition & 0 deletions js/react/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from "./components/radio";
export * from "./components/badge";
export * from "./components/dropdown";
export * from "./components/calendar";
export * from "./components/topic";
export * from "./icons";
36 changes: 36 additions & 0 deletions js/react/showcase/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
DropdownState,
Calendar,
CalendarRangeDate,
Topic,
SubTopic,
} from "@rustlanges/react";
import { ShowComponent } from "./ShowComponent";
import { useState } from "react";
Expand Down Expand Up @@ -279,6 +281,40 @@ export function App() {
<Calendar type="multiple" onChange={setMultiple} value={multiple} />
<Calendar type="range" onChange={setRange} value={range} />
</ShowComponent>
<ShowComponent
component={Topic}
title="Topic"
propsDef={{
level: {
type: "string",
default: "n1",
options: ["n1", "n2", "n3", "op"],
},
title: { type: "string", default: "Tópico" },
state: {
type: "string",
default: "completed",
options: ["completed", "pending", "reading", "unread"],
},
}}
/>
<ShowComponent
component={SubTopic}
title="Sub Topic"
propsDef={{
level: {
type: "string",
default: "n1",
options: ["n1", "n2", "n3", "op"],
},
title: { type: "string", default: "Tópico" },
state: {
type: "string",
default: "completed",
options: ["completed", "pending", "reading", "unread"],
},
}}
/>
</div>
);
}
1 change: 1 addition & 0 deletions styles/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
@import "./components/radio.css";
@import "./components/tag.css";
@import "./components/text.css";
@import "./components/topic.css";
32 changes: 32 additions & 0 deletions styles/components/topic.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@layer component {
.rustlanges-topic {
@apply w-full;
}
.rustlanges-topic[open] svg {
@apply -rotate-180;
}

.rustlanges-topic__summary::-webkit-details-marker {
display: none;
}

.rustlanges-topic__summary {
@apply grid w-full grid-cols-[auto_1fr_auto_auto] items-center gap-2 border-y border-black px-3 py-4;
@apply [&>svg]:duration-400 [&>svg]:size-6 [&>svg]:transition;
Copy link
Member

@Brayan-724 Brayan-724 Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a variant is used frequently I prefer to use the @variant at-rule, but in this case the css nesting is better like

& > svg {
  @apply ...;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that in the current components this variant is used a lot but we should fix it, as this is a new component I think is good to set the guidelines.

}

.rustlanges-topic__title {
@apply text-neutral-950 dark:text-neutral-50;
}

.rustlanges-subtopic {
@apply grid w-full grid-cols-[auto_1fr_auto] items-center gap-2 border-b px-6 py-2.5;
@apply border-b-gray dark:border-b-neutral-600;
}

.rustlanges-subtopic__title {
@apply text-neutral-950;
@apply desktop:text-sm text-xs;
@apply dark:text-neutral-50;
}
}