Skip to content

Commit

Permalink
feat(tag): ✨ finish tag component
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy committed Jun 16, 2022
1 parent 64342e3 commit a05b70d
Show file tree
Hide file tree
Showing 5 changed files with 1,263 additions and 166 deletions.
76 changes: 48 additions & 28 deletions src/tag/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,61 @@
import { ButtonOptions, useButton } from "ariakit";
import {
createComponent,
createElement,
createHook,
} from "ariakit-utils/system";
import { As, Props } from "ariakit-utils/types";

import { BoxOptions, useBox } from "../box";
import { CloseIcon } from "../icons";
import { useTheme } from "../theme";
import { cx, RenderProp, tcm, withIconA11y } from "../utils";
import { cx, passPropsNew, RenderProp, tcm } from "../utils";

export const useTag = createHook<TagOptions>(
({
size = "md",
themeColor = "base",
variant = "solid",
closable = false,
prefix,
suffix = closable ? <CloseIcon /> : null,
disabled,
prefix,
...props
}) => {
const theme = useTheme("tag");
const className = cx(
const className = tcm(
theme.base,
theme.size.default[size],
theme.variant.default[variant],
disabled
? theme.variant.disabled[variant]
: tcm(
theme.variant.hover[variant],
theme.variant.active[variant],
theme.variant.focus[variant],
),
theme.size[size]?.default,
theme.themeColor[themeColor]?.[variant].default,
theme.themeColor[themeColor]?.[variant].hover,
theme.themeColor[themeColor]?.[variant].active,
theme.themeColor[themeColor]?.[variant].focus,
theme.themeColor[themeColor]?.[variant].disabled,
props.className,
);
const prefixStyles = cx(theme.size.prefix[size]);
const suffixStyles = cx(theme.size.suffix[size]);

const prefixStyles = cx(theme.size[size]?.prefix);
const suffixStyles = cx(theme.size[size]?.suffix);

const state = {
size,
themeColor,
variant,
suffix,
};

const children = (
<>
{prefix ? (
<>{withIconA11y(prefix, { className: prefixStyles })}</>
<>{passPropsNew(prefix, { className: prefixStyles }, state)}</>
) : null}
<span>{props.children as React.ReactNode}</span>
{closable && suffix ? (
<>{withIconA11y(suffix, { className: suffixStyles })}</>
<>{passPropsNew(suffix, { className: suffixStyles }, state)}</>
) : null}
</>
);

props = { ...props, className, children };
props = useBox(props);
props = useButton(props);

return props;
},
Expand All @@ -62,35 +67,50 @@ export const Tag = createComponent<TagOptions>(props => {
return createElement("button", htmlProps);
});

export type TagOptions<T extends As = "button"> = BoxOptions<T> & {
export type TagState = {
/**
* How large should the tag be?
*
* @default md
*/
size?: keyof AdaptUI.GetThemeValue<"tag", "size", "default">;
size?: keyof AdaptUI.GetThemeValue<"tag", "size">;

/**
* How the tag should be themed?
*
* @default base
*/
themeColor: keyof AdaptUI.GetThemeValue<"tag", "themeColor">;

/**
* How the tag should look?
*
* @default solid
*/
variant?: keyof AdaptUI.GetThemeValue<"tag", "variant", "default">;
variant?: keyof AdaptUI.GetThemeValue<"tag", "themeColor", "base">;

/**
* If added, the tag will show an icon before the tag's text.
* If added, the tag will show an icon after the tag's text.
*/
prefix?: RenderProp;
prefix?: RenderProp<TagRenderProps & TagState>;

/**
* If added, the tag will allow to show an icon before the tag's text.
* If added, the tag will show an icon after the tag's text.
*/
closable?: boolean;
suffix?: RenderProp<TagRenderProps & TagState>;

/**
* If added, the tag will show an icon after the tag's text.
* If added, the tag will allow to show an icon before the tag's text.
*/
suffix?: RenderProp;
closable?: boolean;
};

export type TagRenderProps = Pick<TagProps, "className">;

export type TagOptions<T extends As = "button"> = Omit<
ButtonOptions<T>,
"size"
> &
Partial<TagState>;

export type TagProps<T extends As = "button"> = Props<TagOptions<T>>;
63 changes: 45 additions & 18 deletions src/tag/stories/TagBasic.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentMeta, ComponentStoryObj } from "@storybook/react";

import { createControls, createPreviewTabs } from "../../../.storybook/utils";
import { SlotIcon } from "../../icons";
import { SlotIcon } from "../../index";

import js from "./templates/TagBasicJsx";
import ts from "./templates/TagBasicTsx";
Expand All @@ -13,21 +13,31 @@ type Story = ComponentStoryObj<typeof TagBasic>;
export default {
title: "Primitives/Tag/Basic",
component: TagBasic,
argTypes: createControls("tag", {
unions: ["themeColor"],
ignore: [
"wrapElement",
"as",
"ref",
"autoFocus",
"focusable",
"accessibleWhenDisabled",
"onFocusVisible",
"clickOnEnter",
"clickOnSpace",
"prefix",
"suffix",
],
}),
parameters: {
layout: "centered",
options: { showPanel: true },
preview: createPreviewTabs({ js, ts }),
},
argTypes: createControls("badge", {
ignore: ["unstable_system", "wrapElement", "as"],
}),
} as Meta;

export const Default: Story = {
args: {
size: "md",
variant: "solid",
},
args: { size: "md", variant: "solid", themeColor: "base" },
};

export const Small: Story = {
Expand All @@ -44,6 +54,15 @@ export const ExtraLarge: Story = {
args: { ...Default.args, size: "xl" },
};

export const Base: Story = {
...Default,
args: { ...Default.args, themeColor: "base" },
};
export const Primary: Story = {
...Default,
args: { ...Default.args, themeColor: "primary" },
};

export const Solid: Story = { ...Default };
export const Subtle: Story = {
...Default,
Expand All @@ -53,28 +72,36 @@ export const Outline: Story = {
...Default,
args: { ...Default.args, variant: "outline" },
};
export const Ghost: Story = {
...Default,
args: { ...Default.args, variant: "ghost" },
};

export const Prefix: Story = {
args: {
size: "md",
variant: "solid",
prefix: <SlotIcon />,
},
...Default,
args: { ...Default.args, prefix: <SlotIcon /> },
};

export const Closable: Story = {
...Default,
args: {
size: "md",
variant: "solid",
...Default.args,
closable: true,
},
};

export const Suffix: Story = {
...Default,
args: { ...Default.args, closable: true, suffix: <SlotIcon /> },
};

export const Disabled: Story = {
...Default,
args: {
size: "md",
variant: "solid",
closable: true,
...Default.args,
prefix: <SlotIcon />,
suffix: <SlotIcon />,
disabled: true,
closable: true,
},
};
Loading

0 comments on commit a05b70d

Please sign in to comment.