feat(ui): add fieldset component
This commit is contained in:
@@ -2,7 +2,7 @@ import meta, { Story } from "./TextField.stories";
|
|||||||
|
|
||||||
const checkboxMeta = {
|
const checkboxMeta = {
|
||||||
...meta,
|
...meta,
|
||||||
title: "Components/Form/Checkbox",
|
title: "Components/Form/Fields/Checkbox",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default checkboxMeta;
|
export default checkboxMeta;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { Tooltip as KTooltip } from "@kobalte/core/tooltip";
|
|||||||
import "./Field.css";
|
import "./Field.css";
|
||||||
|
|
||||||
type Size = "default" | "s";
|
type Size = "default" | "s";
|
||||||
type Orientation = "horizontal" | "vertical";
|
export type Orientation = "horizontal" | "vertical";
|
||||||
type FieldType = "text" | "textarea" | "checkbox";
|
type FieldType = "text" | "textarea" | "checkbox";
|
||||||
|
|
||||||
export interface TextFieldProps {
|
export interface TextFieldProps {
|
||||||
|
|||||||
22
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.css
Normal file
22
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
fieldset {
|
||||||
|
@apply flex flex-col w-full;
|
||||||
|
|
||||||
|
legend {
|
||||||
|
@apply mb-2.5 w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.fields {
|
||||||
|
@apply flex flex-col gap-4 w-full rounded-md;
|
||||||
|
@apply px-4 py-5 bg-def-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error {
|
||||||
|
@apply w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inverted {
|
||||||
|
div.fields {
|
||||||
|
@apply bg-inv-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
119
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.stories.tsx
Normal file
119
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.stories.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
|
||||||
|
import { Fieldset, FieldsetProps } from "@/src/components/v2/Form/Fieldset";
|
||||||
|
import cx from "classnames";
|
||||||
|
|
||||||
|
const FieldsetExamples = (props: FieldsetProps) => (
|
||||||
|
<div class="flex flex-col gap-8">
|
||||||
|
<Fieldset {...props} />
|
||||||
|
<Fieldset {...props} inverted={true} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: "Components/Form/Fieldset",
|
||||||
|
component: FieldsetExamples,
|
||||||
|
decorators: [
|
||||||
|
(Story: StoryObj, context: StoryContext<FieldsetProps>) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={cx({
|
||||||
|
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
|
||||||
|
"w-[1024px]": context.args.orientation == "horizontal",
|
||||||
|
"bg-inv-acc-3": context.args.inverted,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} satisfies Meta<FieldsetProps>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
export type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
legend: "Signup",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
label: "First Name",
|
||||||
|
required: true,
|
||||||
|
control: { placeholder: "Ron" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
label: "Last Name",
|
||||||
|
required: true,
|
||||||
|
control: { placeholder: "Burgundy" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "textarea",
|
||||||
|
label: "Bio",
|
||||||
|
control: { placeholder: "Tell us a bit about yourself", rows: 8 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "checkbox",
|
||||||
|
label: "Accept Terms & Conditions",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Horizontal: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
orientation: "horizontal",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Vertical: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
orientation: "vertical",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Error: Story = {
|
||||||
|
args: {
|
||||||
|
legend: "Signup",
|
||||||
|
error: "You must enter a First Name",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
label: "First Name",
|
||||||
|
required: true,
|
||||||
|
invalid: true,
|
||||||
|
control: { placeholder: "Ron" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
label: "Last Name",
|
||||||
|
required: true,
|
||||||
|
invalid: true,
|
||||||
|
control: { placeholder: "Burgundy" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "textarea",
|
||||||
|
label: "Bio",
|
||||||
|
control: { placeholder: "Tell us a bit about yourself", rows: 8 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "checkbox",
|
||||||
|
label: "Accept Terms & Conditions",
|
||||||
|
invalid: true,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
67
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.tsx
Normal file
67
pkgs/clan-app/ui/src/components/v2/Form/Fieldset.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import "./Fieldset.css";
|
||||||
|
import { Field, FieldProps, Orientation } from "./Field";
|
||||||
|
import { For } from "solid-js";
|
||||||
|
import cx from "classnames";
|
||||||
|
import { Typography } from "@/src/components/v2/Typography/Typography";
|
||||||
|
|
||||||
|
export interface FieldsetProps {
|
||||||
|
legend: string;
|
||||||
|
fields: FieldProps[];
|
||||||
|
inverted?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
orientation?: Orientation;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Fieldset = (props: FieldsetProps) => {
|
||||||
|
const orientation = () => props.orientation || "vertical";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<fieldset
|
||||||
|
role="group"
|
||||||
|
class={cx(orientation(), { inverted: props.inverted })}
|
||||||
|
disabled={props.disabled}
|
||||||
|
>
|
||||||
|
<legend>
|
||||||
|
<Typography
|
||||||
|
hierarchy="label"
|
||||||
|
family="mono"
|
||||||
|
size="default"
|
||||||
|
weight="normal"
|
||||||
|
color="tertiary"
|
||||||
|
transform="uppercase"
|
||||||
|
inverted={props.inverted}
|
||||||
|
>
|
||||||
|
{props.legend}
|
||||||
|
</Typography>
|
||||||
|
</legend>
|
||||||
|
<div class="fields">
|
||||||
|
<For each={props.fields}>
|
||||||
|
{(fieldProps) => {
|
||||||
|
return (
|
||||||
|
<Field
|
||||||
|
{...fieldProps}
|
||||||
|
orientation={orientation()}
|
||||||
|
disabled={props.disabled}
|
||||||
|
inverted={props.inverted}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
{props.error && (
|
||||||
|
<div class="error" role="alert">
|
||||||
|
<Typography
|
||||||
|
hierarchy="body"
|
||||||
|
size="xxs"
|
||||||
|
weight="medium"
|
||||||
|
color="error"
|
||||||
|
inverted={props.inverted}
|
||||||
|
>
|
||||||
|
{props.error}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import meta, { Icon, Story } from "./TextField.stories";
|
import meta, { Story } from "./TextField.stories";
|
||||||
|
|
||||||
const textAreaMeta = {
|
const textAreaMeta = {
|
||||||
...meta,
|
...meta,
|
||||||
title: "Components/Form/TextArea",
|
title: "Components/Form/Fields/TextArea",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default textAreaMeta;
|
export default textAreaMeta;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const FieldExamples = (props: FieldProps) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "Components/Form/TextField",
|
title: "Components/Form/Fields/TextField",
|
||||||
component: FieldExamples,
|
component: FieldExamples,
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story: StoryObj, context: StoryContext<FieldProps>) => {
|
(Story: StoryObj, context: StoryContext<FieldProps>) => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export type Tag = "span" | "p" | "h1" | "h2" | "h3" | "h4" | "div";
|
|||||||
export type Hierarchy = "body" | "title" | "headline" | "label" | "teaser";
|
export type Hierarchy = "body" | "title" | "headline" | "label" | "teaser";
|
||||||
export type Weight = "normal" | "medium" | "bold";
|
export type Weight = "normal" | "medium" | "bold";
|
||||||
export type Family = "regular" | "condensed" | "mono";
|
export type Family = "regular" | "condensed" | "mono";
|
||||||
|
export type Transform = "uppercase" | "lowercase" | "capitalize";
|
||||||
|
|
||||||
// type Size = "default" | "xs" | "s" | "m" | "l";
|
// type Size = "default" | "xs" | "s" | "m" | "l";
|
||||||
interface SizeForHierarchy {
|
interface SizeForHierarchy {
|
||||||
@@ -94,6 +95,7 @@ interface _TypographyProps<H extends Hierarchy> {
|
|||||||
inverted?: boolean;
|
inverted?: boolean;
|
||||||
tag?: Tag;
|
tag?: Tag;
|
||||||
class?: string;
|
class?: string;
|
||||||
|
transform?: Transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Typography = <H extends Hierarchy>(props: _TypographyProps<H>) => {
|
export const Typography = <H extends Hierarchy>(props: _TypographyProps<H>) => {
|
||||||
@@ -113,6 +115,7 @@ export const Typography = <H extends Hierarchy>(props: _TypographyProps<H>) => {
|
|||||||
weight(),
|
weight(),
|
||||||
size(),
|
size(),
|
||||||
color(),
|
color(),
|
||||||
|
props.transform,
|
||||||
props.class,
|
props.class,
|
||||||
)}
|
)}
|
||||||
component={props.tag || "span"}
|
component={props.tag || "span"}
|
||||||
|
|||||||
Reference in New Issue
Block a user