Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: rewrite collapsible and accordion templates with jsx #4675

Merged
merged 2 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading