Add chevron icon to accordion (#2126)

Here added chevron to accordion trigger
by using css custom properties for cascade.

We still do not support css custom properties in style panel though here
used only for transform which does not have ui too.
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2023-08-16 21:06:19 +04:00
1 parent 50ef1fbd81
commit c1f10dbfa1
22 files changed
+206 -171

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/chevron down">
<path id="vector" d="M4.04008 6.2838C4.29431 6.03034 4.70587 6.03097 4.95932 6.2852L7.99985 9.33503L11.0404 6.2852C11.2938 6.03097 11.7054 6.03034 11.9596 6.2838C12.2139 6.53725 12.2145 6.94881 11.961 7.20303L8.46017 10.7146C8.33822 10.8369 8.17259 10.9057 7.99985 10.9057C7.82712 10.9057 7.66149 10.8369 7.53953 10.7146L4.03868 7.20303C3.78523 6.94881 3.78585 6.53725 4.04008 6.2838Z" fill="#11181C"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 538 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/chevron left">
<path id="vector" d="M9.25586 4.49902L5.7443 7.99988L9.25586 11.5007" stroke="#11181C" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 290 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/chevron right">
<path id="vector" d="M6.74414 11.501L10.2557 8.00012L6.74414 4.49927" stroke="#11181C" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 291 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/chevron up">
<path id="vector" d="M11.501 9.25586L8.00012 5.7443L4.49927 9.25586" stroke="#11181C" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 287 B

+1 -1
View File
@@ -9,7 +9,7 @@
"dev": "build-package --watch",
"build": "build-package",
"dts": "tsc --declarationDir lib/types",
"generate": "rm -fr src/__generated__ && NODE_OPTIONS='--loader=tsx' svgo-jsx svgo-jsx.config.ts && tsx svg-string.ts && prettier --write ./",
"generate": "rm -fr src/__generated__ && NODE_OPTIONS='--loader=tsx' svgo-jsx svgo-jsx.config.ts && tsx svg-string.ts && prettier --write src/__generated__",
"typecheck": "tsc",
"checks": "pnpm typecheck",
"storybook:dev": "storybook dev -p 6006",
+15
View File
@@ -0,0 +1,15 @@
// Generated from icons/chevron-down.svg
import { forwardRef } from "react";
import type { IconProps } from "../types";
// prettier-ignore
export const ChevronDownIcon = forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fill="#11181C" d="M4.04 6.284a.65.65 0 0 1 .92.001L8 9.335l3.04-3.05a.65.65 0 1 1 .921.918l-3.5 3.512a.65.65 0 0 1-.921 0L4.039 7.203a.65.65 0 0 1 .001-.92Z" /></svg>
);
}
);
ChevronDownIcon.displayName = "ChevronDownIcon";
+15
View File
@@ -0,0 +1,15 @@
// Generated from icons/chevron-left.svg
import { forwardRef } from "react";
import type { IconProps } from "../types";
// prettier-ignore
export const ChevronLeftIcon = forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path stroke="#11181C" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.3" d="m9.256 4.499-3.512 3.5 3.512 3.502" /></svg>
);
}
);
ChevronLeftIcon.displayName = "ChevronLeftIcon";
+15
View File
@@ -0,0 +1,15 @@
// Generated from icons/chevron-right.svg
import { forwardRef } from "react";
import type { IconProps } from "../types";
// prettier-ignore
export const ChevronRightIcon = forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path stroke="#11181C" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.3" d="m6.744 11.501 3.512-3.5-3.512-3.502" /></svg>
);
}
);
ChevronRightIcon.displayName = "ChevronRightIcon";
+15
View File
@@ -0,0 +1,15 @@
// Generated from icons/chevron-up.svg
import { forwardRef } from "react";
import type { IconProps } from "../types";
// prettier-ignore
export const ChevronUpIcon = forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path stroke="#11181C" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.3" d="m11.501 9.256-3.5-3.512-3.502 3.512" /></svg>
);
}
);
ChevronUpIcon.displayName = "ChevronUpIcon";
+4
View File
@@ -50,10 +50,14 @@ export * from "./chevron-big-down";
export * from "./chevron-big-left";
export * from "./chevron-big-right";
export * from "./chevron-big-up";
export * from "./chevron-down";
export * from "./chevron-filled-down";
export * from "./chevron-filled-left";
export * from "./chevron-filled-right";
export * from "./chevron-filled-up";
export * from "./chevron-left";
export * from "./chevron-right";
export * from "./chevron-up";
export * from "./code-text";
export * from "./collapsible";
export * from "./commit";
+1
View File
@@ -0,0 +1 @@
export const ChevronDownIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="100%" height="100%" style="display: block;"><path fill="#11181C" d="M4.04 6.284a.65.65 0 0 1 .92.001L8 9.335l3.04-3.05a.65.65 0 1 1 .921.918l-3.5 3.512a.65.65 0 0 1-.921 0L4.039 7.203a.65.65 0 0 1 .001-.92Z"/></svg>`;
+1
View File
@@ -0,0 +1 @@
export const ChevronLeftIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="100%" height="100%" style="display: block;"><path stroke="#11181C" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3" d="m9.256 4.499-3.512 3.5 3.512 3.502"/></svg>`;
+1
View File
@@ -0,0 +1 @@
export const ChevronRightIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="100%" height="100%" style="display: block;"><path stroke="#11181C" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3" d="m6.744 11.501 3.512-3.5-3.512-3.502"/></svg>`;
+1
View File
@@ -0,0 +1 @@
export const ChevronUpIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="100%" height="100%" style="display: block;"><path stroke="#11181C" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3" d="m11.501 9.256-3.5-3.512-3.502 3.512"/></svg>`;
+4
View File
@@ -50,10 +50,14 @@ export { ChevronBigDownIcon } from "./chevron-big-down";
export { ChevronBigLeftIcon } from "./chevron-big-left";
export { ChevronBigRightIcon } from "./chevron-big-right";
export { ChevronBigUpIcon } from "./chevron-big-up";
export { ChevronDownIcon } from "./chevron-down";
export { ChevronFilledDownIcon } from "./chevron-filled-down";
export { ChevronFilledLeftIcon } from "./chevron-filled-left";
export { ChevronFilledRightIcon } from "./chevron-filled-right";
export { ChevronFilledUpIcon } from "./chevron-filled-up";
export { ChevronLeftIcon } from "./chevron-left";
export { ChevronRightIcon } from "./chevron-right";
export { ChevronUpIcon } from "./chevron-up";
export { CodeTextIcon } from "./code-text";
export { CollapsibleIcon } from "./collapsible";
export { CommitIcon } from "./commit";
-25
View File
@@ -1,25 +0,0 @@
import * as React from "react";
import type { IconProps } from "./types";
export const ChevronDownIcon = React.forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", ...props }, forwardedRef) => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
ref={forwardedRef}
>
<path
d="M4.04008 6.28383C4.29431 6.03037 4.70587 6.031 4.95932 6.28523L7.99985 9.33506L11.0404 6.28523C11.2938 6.031 11.7054 6.03037 11.9596 6.28383C12.2139 6.53728 12.2145 6.94884 11.961 7.20306L8.46017 10.7146C8.33822 10.837 8.17259 10.9057 7.99985 10.9057C7.82712 10.9057 7.66149 10.837 7.53953 10.7146L4.03868 7.20306C3.78523 6.94884 3.78585 6.53728 4.04008 6.28383Z"
fill={color}
/>
</svg>
);
}
);
ChevronDownIcon.displayName = "ChevronDownIcon";
-27
View File
@@ -1,27 +0,0 @@
import * as React from "react";
import type { IconProps } from "./types";
export const ChevronLeftIcon = React.forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", ...props }, forwardedRef) => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
ref={forwardedRef}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.7162 4.04011C9.96966 4.29434 9.96903 4.7059 9.7148 4.95935L6.66497 7.99988L9.7148 11.0404C9.96903 11.2939 9.96966 11.7054 9.7162 11.9597C9.46275 12.2139 9.05119 12.2145 8.79697 11.9611L5.28541 8.4602C5.16308 8.33825 5.09432 8.17262 5.09432 7.99988C5.09432 7.82715 5.16308 7.66152 5.28541 7.53956L8.79697 4.03871C9.05119 3.78526 9.46275 3.78588 9.7162 4.04011Z"
fill={color}
/>
</svg>
);
}
);
ChevronLeftIcon.displayName = "ChevronLeftIcon";
-27
View File
@@ -1,27 +0,0 @@
import * as React from "react";
import type { IconProps } from "./types";
export const ChevronRightIcon = React.forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", ...props }, forwardedRef) => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
ref={forwardedRef}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.2838 11.9599C6.03034 11.7057 6.03097 11.2941 6.2852 11.0407L9.33503 8.00013L6.2852 4.95959C6.03097 4.70614 6.03034 4.29458 6.2838 4.04036C6.53725 3.78613 6.94881 3.7855 7.20303 4.03895L10.7146 7.5398C10.8369 7.66176 10.9057 7.82739 10.9057 8.00013C10.9057 8.17286 10.8369 8.33849 10.7146 8.46045L7.20303 11.9613C6.94881 12.2148 6.53725 12.2141 6.2838 11.9599Z"
fill={color}
/>
</svg>
);
}
);
ChevronRightIcon.displayName = "ChevronRightIcon";
-27
View File
@@ -1,27 +0,0 @@
import * as React from "react";
import type { IconProps } from "./types";
export const ChevronUpIcon = React.forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", ...props }, forwardedRef) => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
ref={forwardedRef}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.9599 9.71617C11.7057 9.96963 11.2941 9.969 11.0407 9.71477L8.00015 6.66494L4.95962 9.71477C4.70616 9.969 4.29461 9.96963 4.04038 9.71617C3.78615 9.46272 3.78552 9.05116 4.03897 8.79694L7.53983 5.28538C7.66178 5.16305 7.82741 5.09429 8.00015 5.09429C8.17288 5.09429 8.33851 5.16305 8.46047 5.28538L11.9613 8.79694C12.2148 9.05116 12.2141 9.46272 11.9599 9.71617Z"
fill={color}
/>
</svg>
);
}
);
ChevronUpIcon.displayName = "ChevronUpIcon";
-4
View File
@@ -21,10 +21,6 @@ export * from "./chevron-double-up";
export * from "./chevron-double-down";
export * from "./chevron-double-left";
export * from "./chevron-double-right";
export * from "./chevron-up";
export * from "./chevron-down";
export * from "./chevron-left";
export * from "./chevron-right";
export * from "./cross-circled-filled";
export * from "./cross";
export * from "./magnifying-glass";
@@ -4,12 +4,14 @@ import {
HeaderIcon,
TriggerIcon,
ContentIcon,
ChevronDownIcon,
} from "@webstudio-is/icons/svg";
import type {
EmbedTemplateStyleDecl,
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
WsEmbedTemplate,
} from "@webstudio-is/react-sdk";
import { div, h3, button } from "@webstudio-is/react-sdk/css-normalize";
import * as tc from "./theme/tailwind-classes";
@@ -37,20 +39,68 @@ const presetStyle = {
// border-b
const accordionItemStyles: EmbedTemplateStyleDecl[] = [tc.borderB()].flat();
// flex
const accordionHeaderStyles: EmbedTemplateStyleDecl[] = [tc.flex()].flat();
// flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180
const accordionTriggerStyles: EmbedTemplateStyleDecl[] = [
tc.flex(),
tc.flex(1),
tc.items("center"),
tc.justify("between"),
tc.py(4),
tc.font("medium"),
tc.transition("all"),
tc.hover([tc.underline()].flat()),
].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: [
...children,
{
type: "instance",
component: "Box",
// 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
@@ -95,19 +145,9 @@ export const metaAccordion: WsComponentMeta = {
component: "AccordionItem",
styles: accordionItemStyles,
children: [
{
type: "instance",
component: "AccordionHeader",
styles: accordionHeaderStyles,
children: [
{
type: "instance",
component: "AccordionTrigger",
styles: accordionTriggerStyles,
children: [{ type: "text", value: "Is it accessible?" }],
},
],
},
createAccordionTrigger({
children: [{ type: "text", value: "Is it accessible?" }],
}),
{
type: "instance",
component: "AccordionContent",
@@ -127,19 +167,9 @@ export const metaAccordion: WsComponentMeta = {
component: "AccordionItem",
styles: accordionItemStyles,
children: [
{
type: "instance",
component: "AccordionHeader",
styles: accordionHeaderStyles,
children: [
{
type: "instance",
component: "AccordionTrigger",
styles: accordionTriggerStyles,
children: [{ type: "text", value: "Is it styled?" }],
},
],
},
createAccordionTrigger({
children: [{ type: "text", value: "Is it styled?" }],
}),
{
type: "instance",
component: "AccordionContent",
@@ -160,19 +190,9 @@ export const metaAccordion: WsComponentMeta = {
component: "AccordionItem",
styles: accordionItemStyles,
children: [
{
type: "instance",
component: "AccordionHeader",
styles: accordionHeaderStyles,
children: [
{
type: "instance",
component: "AccordionTrigger",
styles: accordionTriggerStyles,
children: [{ type: "text", value: "Is it animated?" }],
},
],
},
createAccordionTrigger({
children: [{ type: "text", value: "Is it animated?" }],
}),
{
type: "instance",
component: "AccordionContent",
@@ -7,9 +7,26 @@ import {
parseCssValue,
parseBoxShadow,
StyleValue,
type StyleProperty,
} from "@webstudio-is/css-data";
import type { EvaluatedDefaultTheme } from "./radix-common-types";
export const property = (
property: StyleProperty,
value: string
): EmbedTemplateStyleDecl => {
if (value.startsWith("--")) {
return {
property,
value: { type: "var", value: value.slice(2), fallbacks: [] },
};
}
return {
property,
value: { type: "unparsed", value },
};
};
// https://github.com/tailwindlabs/tailwindcss/blob/master/src/css/preflight.css
const preflight = (): EmbedTemplateStyleDecl[] => {
const borderColorValue = parseCssValue("color", theme("colors")["border"]);
@@ -585,11 +602,16 @@ export const grow = (): EmbedTemplateStyleDecl[] => {
return [
{
property: "flexGrow",
value: {
type: "unit",
value: 1,
unit: "number",
},
value: { type: "unit", value: 1, unit: "number" },
},
];
};
export const shrink = (value: number): EmbedTemplateStyleDecl[] => {
return [
{
property: "flexGrow",
value: { type: "unit", value, unit: "number" },
},
];
};
@@ -818,7 +840,9 @@ export const pointerEvents = (
return [{ property: "pointerEvents", value: { type: "keyword", value } }];
};
export const transition = (value: "none" | "all"): EmbedTemplateStyleDecl[] => {
export const transition = (
value: "none" | "all" | "transform"
): EmbedTemplateStyleDecl[] => {
if (value === "none") {
return [
{
@@ -830,7 +854,7 @@ export const transition = (value: "none" | "all"): EmbedTemplateStyleDecl[] => {
return [
{
property: "transitionProperty",
value: { type: "keyword", value: "all" },
value: { type: "keyword", value },
},
{
property: "transitionTimingFunction",
@@ -843,6 +867,15 @@ export const transition = (value: "none" | "all"): EmbedTemplateStyleDecl[] => {
];
};
export const duration = (ms: number): EmbedTemplateStyleDecl[] => {
return [
{
property: "transitionDuration",
value: { type: "unit", value: ms, unit: "ms" },
},
];
};
export const hover = (
value: EmbedTemplateStyleDecl[]
): EmbedTemplateStyleDecl[] => {