Skip to content

Commit

Permalink
refactor: rewrite collapsible and accordion templates with jsx (#4675)
Browse files Browse the repository at this point in the history
Css variables in templates are easier to understand now.
  • Loading branch information
TrySound authored Jan 2, 2025
1 parent 874db0e commit 8308284
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 240 deletions.
116 changes: 116 additions & 0 deletions packages/sdk-components-react-radix/src/accordion.template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
$,
css,
PlaceholderValue,
type TemplateMeta,
} from "@webstudio-is/template";
import { radix } from "./shared/proxy";
import {
borderWidth,
colors,
fontSize,
fontSizeLineHeight,
height,
spacing,
transition,
weights,
width,
} from "./shared/theme";
import { ChevronDownIcon } from "@webstudio-is/icons/svg";

const createAccordionItem = (triggerText: string, contentText: string) => {
return (
<radix.AccordionItem
// border-b
ws:style={css`
border-bottom: ${borderWidth.DEFAULT} solid ${colors.border};
`}
>
<radix.AccordionHeader
// flex
ws:style={css`
display: flex;
`}
>
<radix.AccordionTrigger
// flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180
ws:style={css`
display: flex;
flex: 1 1 0;
align-items: center;
justify-content: between;
padding: ${spacing[4]} 0;
font-weight: ${weights.medium};
--accordion-trigger-icon-transform: 0deg;
&:hover {
text-decoration-line: underline;
}
&[data-state="open"] {
--accordion-trigger-icon-transform: 180deg;
}
`}
>
{new PlaceholderValue(triggerText)}
</radix.AccordionTrigger>
<$.Box
ws:label="Icon Container"
// h-4 w-4 shrink-0 transition-transform duration-200
ws:style={css`
rotate: --accordion-trigger-icon-transform;
height: ${height[4]};
width: ${width[4]};
flex-shrink: 0;
transition: ${transition.all};
transition-duration: 200ms;
`}
>
<$.HtmlEmbed ws:label="Chevron Icon" code={ChevronDownIcon} />
</$.Box>
</radix.AccordionHeader>
<radix.AccordionContent
// overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down
// pb-4 pt-0
ws:style={css`
overflow: hidden;
font-size: ${fontSize.sm};
line-height: ${fontSizeLineHeight.sm};
transition: ${transition.all};
padding-bottom: ${spacing[4]};
`}
>
{new PlaceholderValue(contentText)}
</radix.AccordionContent>
</radix.AccordionItem>
);
};

/**
* Styles source without animations:
* https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/default/ui/accordion.tsx
*
* Attributions
* MIT License
* Copyright (c) 2023 shadcn
**/
export const meta: TemplateMeta = {
category: "radix",
description:
"A vertically stacked set of interactive headings that each reveal an associated section of content. Clicking on the heading will open the item and close other items.",
order: 3,
template: (
<radix.Accordion collapsible={true} defaultValue="0">
{createAccordionItem(
"Is it accessible?",
"Yes. It adheres to the WAI-ARIA design pattern."
)}
{createAccordionItem(
"Is it styled?",
"Yes. It comes with default styles that matches the other components' aesthetic."
)}
{createAccordionItem(
"Is it animated?",
"Yes. It's animated by default, but you can disable it if you prefer."
)}
</radix.Accordion>
),
};
189 changes: 0 additions & 189 deletions packages/sdk-components-react-radix/src/accordion.ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ import {
HeaderIcon,
TriggerIcon,
ContentIcon,
ChevronDownIcon,
} from "@webstudio-is/icons/svg";
import {
defaultStates,
type EmbedTemplateStyleDecl,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
type WsEmbedTemplate,
} from "@webstudio-is/react-sdk";
import { div, h3, button } from "@webstudio-is/sdk/normalize.css";
import * as tc from "./theme/tailwind-classes";
Expand All @@ -29,202 +26,19 @@ const presetStyle = {
div,
} satisfies PresetStyle<"div">;

/**
* Styles source without animations:
* https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/default/ui/accordion.tsx
*
* Attributions
* MIT License
* Copyright (c) 2023 shadcn
**/

// border-b
const accordionItemStyles: EmbedTemplateStyleDecl[] = [tc.borderB()].flat();

const createAccordionTrigger = ({
children,
}: {
children: WsEmbedTemplate;
}): WsEmbedTemplate[number] => ({
type: "instance",
component: "AccordionHeader",
// flex
styles: [tc.flex()].flat(),
children: [
{
type: "instance",
component: "AccordionTrigger",
// flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180
styles: [
tc.flex(),
tc.flex(1),
tc.items("center"),
tc.justify("between"),
tc.py(4),
tc.font("medium"),
tc.hover([tc.underline()].flat()),
tc.property("--accordion-trigger-icon-transform", "0deg"),
tc.state(
[tc.property("--accordion-trigger-icon-transform", "180deg")],
"[data-state=open]"
),
].flat(),
children: [
{
type: "instance",
component: "Text",
children,
},
{
type: "instance",
component: "Box",
label: "Icon Container",
// h-4 w-4 shrink-0 transition-transform duration-200
styles: [
tc.property("rotate", "--accordion-trigger-icon-transform"),
tc.h(4),
tc.w(4),
tc.shrink(0),
tc.transition("all"),
tc.duration(200),
].flat(),
children: [
{
type: "instance",
component: "HtmlEmbed",
label: "Chevron Icon",
props: [
{
type: "string",
name: "code",
value: ChevronDownIcon,
},
],
children: [],
},
],
},
],
},
],
});

// overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down
// pb-4 pt-0
const accordionContentStyles: EmbedTemplateStyleDecl[] = [
tc.overflow("hidden"),
tc.text("sm"),
// transition does not work with display: none
// tc.transition("all"),
tc.pb(4),
].flat();

export const metaAccordion: WsComponentMeta = {
category: "radix",
order: 3,
type: "container",
icon: AccordionIcon,
presetStyle,
description:
"A vertically stacked set of interactive headings that each reveal an associated section of content. Clicking on the heading will open the item and close other items.",
constraints: [
{
relation: "descendant",
component: { $eq: "AccordionItem" },
},
],
template: [
{
type: "instance",
component: "Accordion",
props: [
{ type: "boolean", name: "collapsible", value: true },
{ type: "string", name: "defaultValue", value: "0" },
],
children: [
{
type: "instance",
component: "AccordionItem",
styles: accordionItemStyles,
children: [
createAccordionTrigger({
children: [
{ type: "text", value: "Is it accessible?", placeholder: true },
],
}),
{
type: "instance",
component: "AccordionContent",
styles: accordionContentStyles,
children: [
{
type: "text",
value: "Yes. It adheres to the WAI-ARIA design pattern.",
placeholder: true,
},
],
},
],
},

{
type: "instance",
component: "AccordionItem",
styles: accordionItemStyles,
children: [
createAccordionTrigger({
children: [
{ type: "text", value: "Is it styled?", placeholder: true },
],
}),
{
type: "instance",
component: "AccordionContent",
styles: accordionContentStyles,
children: [
{
type: "text",
value:
"Yes. It comes with default styles that matches the other components' aesthetic.",
placeholder: true,
},
],
},
],
},

{
type: "instance",
component: "AccordionItem",
styles: accordionItemStyles,
children: [
createAccordionTrigger({
children: [
{ type: "text", value: "Is it animated?", placeholder: true },
],
}),
{
type: "instance",
component: "AccordionContent",
styles: accordionContentStyles,
children: [
{
type: "text",
value:
"Yes. It's animated by default, but you can disable it if you prefer.",
placeholder: true,
},
],
},
],
},
],
},
],
};

export const metaAccordionItem: WsComponentMeta = {
category: "hidden",
type: "container",
label: "Item",
icon: ItemIcon,
Expand All @@ -247,7 +61,6 @@ export const metaAccordionItem: WsComponentMeta = {
};

export const metaAccordionHeader: WsComponentMeta = {
category: "hidden",
type: "container",
label: "Item Header",
icon: HeaderIcon,
Expand All @@ -267,7 +80,6 @@ export const metaAccordionHeader: WsComponentMeta = {
};

export const metaAccordionTrigger: WsComponentMeta = {
category: "hidden",
type: "container",
label: "Item Trigger",
icon: TriggerIcon,
Expand All @@ -289,7 +101,6 @@ export const metaAccordionTrigger: WsComponentMeta = {
};

export const metaAccordionContent: WsComponentMeta = {
category: "hidden",
type: "container",
label: "Item Content",
icon: ContentIcon,
Expand Down
22 changes: 22 additions & 0 deletions packages/sdk-components-react-radix/src/collapsible.template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { $, PlaceholderValue, type TemplateMeta } from "@webstudio-is/template";
import { radix } from "./shared/proxy";
import { getButtonStyle } from "./shared/styles";

export const meta: TemplateMeta = {
category: "radix",
description:
"An interactive component which expands and collapses some content, triggered by a button.",
order: 5,
template: (
<radix.Collapsible>
<radix.CollapsibleTrigger>
<$.Button ws:style={getButtonStyle("outline")}>
{new PlaceholderValue("Click to toggle content")}
</$.Button>
</radix.CollapsibleTrigger>
<radix.CollapsibleContent>
<$.Text>{new PlaceholderValue("Collapsible Content")}</$.Text>
</radix.CollapsibleContent>
</radix.Collapsible>
),
};
Loading

0 comments on commit 8308284

Please sign in to comment.