@neoptocom/neopto-ui 1.4.4 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +86 -22
- package/dist/index.d.cts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +86 -22
- package/package.json +3 -2
- package/src/components/Breadcrumb.docs.mdx +60 -0
- package/src/components/Breadcrumb.stories.tsx +78 -0
- package/src/components/Button.docs.mdx +56 -0
- package/src/{stories → components}/Button.stories.tsx +39 -32
- package/src/components/Card.docs.mdx +56 -0
- package/src/components/Card.stories.tsx +129 -0
- package/src/components/Chip.tsx +16 -3
- package/src/components/Input.tsx +110 -25
- package/src/stories/Chip.stories.tsx +15 -0
- package/src/stories/Input.stories.tsx +26 -0
- package/src/stories/Breadcrumb.stories.tsx +0 -270
- package/src/stories/Card.stories.tsx +0 -350
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import Icon from "./Icon";
|
|
3
|
+
import Typo from "./Typo";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Button> = {
|
|
7
7
|
title: "Components/Button",
|
|
8
8
|
component: Button,
|
|
9
|
+
tags: ["autodocs"],
|
|
9
10
|
args: {
|
|
10
|
-
children: "
|
|
11
|
+
children: "Primary action",
|
|
11
12
|
variant: "primary",
|
|
12
13
|
size: "md",
|
|
13
14
|
disabled: false
|
|
14
15
|
},
|
|
15
16
|
argTypes: {
|
|
16
17
|
variant: {
|
|
17
|
-
control: "radio",
|
|
18
|
+
control: "inline-radio",
|
|
18
19
|
options: ["primary", "secondary", "ghost"]
|
|
19
20
|
},
|
|
20
21
|
size: {
|
|
21
|
-
control: "radio",
|
|
22
|
+
control: "inline-radio",
|
|
22
23
|
options: ["sm", "md", "lg"]
|
|
23
24
|
},
|
|
24
25
|
fullWidth: {
|
|
@@ -39,13 +40,19 @@ export const Variants: Story = {
|
|
|
39
40
|
render: () => (
|
|
40
41
|
<div className="flex flex-wrap items-center gap-4">
|
|
41
42
|
<Button variant="primary">
|
|
42
|
-
<Typo variant="title-sm" bold="semibold">
|
|
43
|
+
<Typo variant="title-sm" bold="semibold">
|
|
44
|
+
Primary
|
|
45
|
+
</Typo>
|
|
43
46
|
</Button>
|
|
44
47
|
<Button variant="secondary">
|
|
45
|
-
<Typo variant="title-sm" bold="semibold">
|
|
48
|
+
<Typo variant="title-sm" bold="semibold">
|
|
49
|
+
Secondary
|
|
50
|
+
</Typo>
|
|
46
51
|
</Button>
|
|
47
52
|
<Button variant="ghost">
|
|
48
|
-
<Typo variant="title-sm" bold="semibold">
|
|
53
|
+
<Typo variant="title-sm" bold="semibold">
|
|
54
|
+
Ghost
|
|
55
|
+
</Typo>
|
|
49
56
|
</Button>
|
|
50
57
|
</div>
|
|
51
58
|
)
|
|
@@ -55,29 +62,19 @@ export const Sizes: Story = {
|
|
|
55
62
|
render: () => (
|
|
56
63
|
<div className="flex flex-wrap items-center gap-4">
|
|
57
64
|
<Button size="sm">
|
|
58
|
-
<Typo variant="title-sm" bold="semibold">
|
|
65
|
+
<Typo variant="title-sm" bold="semibold">
|
|
66
|
+
Small
|
|
67
|
+
</Typo>
|
|
59
68
|
</Button>
|
|
60
69
|
<Button size="md">
|
|
61
|
-
<Typo variant="title-sm" bold="semibold">
|
|
70
|
+
<Typo variant="title-sm" bold="semibold">
|
|
71
|
+
Medium
|
|
72
|
+
</Typo>
|
|
62
73
|
</Button>
|
|
63
74
|
<Button size="lg">
|
|
64
|
-
<Typo variant="title-sm" bold="semibold">
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const States: Story = {
|
|
71
|
-
render: () => (
|
|
72
|
-
<div className="flex flex-wrap items-center gap-4">
|
|
73
|
-
<Button>
|
|
74
|
-
<Typo variant="title-sm" bold="semibold">Default</Typo>
|
|
75
|
-
</Button>
|
|
76
|
-
<Button disabled>
|
|
77
|
-
<Typo variant="title-sm" bold="semibold">Disabled</Typo>
|
|
78
|
-
</Button>
|
|
79
|
-
<Button fullWidth>
|
|
80
|
-
<Typo variant="title-sm" bold="semibold">Full Width</Typo>
|
|
75
|
+
<Typo variant="title-sm" bold="semibold">
|
|
76
|
+
Large
|
|
77
|
+
</Typo>
|
|
81
78
|
</Button>
|
|
82
79
|
</div>
|
|
83
80
|
)
|
|
@@ -88,16 +85,26 @@ export const WithIcons: Story = {
|
|
|
88
85
|
<div className="flex flex-wrap items-center gap-4">
|
|
89
86
|
<Button>
|
|
90
87
|
<Icon name="add" />
|
|
91
|
-
<
|
|
88
|
+
<span>Add Item</span>
|
|
92
89
|
</Button>
|
|
93
90
|
<Button variant="secondary">
|
|
94
|
-
<Icon name="
|
|
95
|
-
<
|
|
91
|
+
<Icon name="download" />
|
|
92
|
+
<span>Download</span>
|
|
96
93
|
</Button>
|
|
97
94
|
<Button variant="ghost">
|
|
98
95
|
<Icon name="settings" />
|
|
99
|
-
<
|
|
96
|
+
<span>Settings</span>
|
|
100
97
|
</Button>
|
|
101
98
|
</div>
|
|
102
99
|
)
|
|
103
100
|
};
|
|
101
|
+
|
|
102
|
+
export const FullWidthCallToAction: Story = {
|
|
103
|
+
args: {
|
|
104
|
+
fullWidth: true,
|
|
105
|
+
size: "lg",
|
|
106
|
+
children: "Start free trial"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Meta, Canvas, Story, ArgsTable } from "@storybook/blocks";
|
|
2
|
+
import { Card } from "./Card";
|
|
3
|
+
import * as CardStories from "./Card.stories";
|
|
4
|
+
|
|
5
|
+
<Meta of={CardStories} />
|
|
6
|
+
|
|
7
|
+
# Card
|
|
8
|
+
|
|
9
|
+
Cards provide glassmorphic containers for grouping content. They offer decorative borders, elevated
|
|
10
|
+
shadows, and an app-background variant that mirrors hero surfaces.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Card } from "@neoptocom/neopto-ui";
|
|
16
|
+
|
|
17
|
+
<Card>
|
|
18
|
+
<h3>Weekly summary</h3>
|
|
19
|
+
<p>Use cards to separate content into digestible surfaces.</p>
|
|
20
|
+
</Card>;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
<Canvas>
|
|
24
|
+
<Story of={CardStories.Playground} />
|
|
25
|
+
</Canvas>
|
|
26
|
+
|
|
27
|
+
<ArgsTable of={CardStories.Playground} />
|
|
28
|
+
|
|
29
|
+
## Variants
|
|
30
|
+
|
|
31
|
+
- `showDecorations` adds gradient strokes suited for marketing hero cards.
|
|
32
|
+
- `variant="app-background"` injects the NeoPTO hero artwork and auto-switches with theme.
|
|
33
|
+
- `elevated` applies a high-emphasis drop shadow—combine with default or app backgrounds.
|
|
34
|
+
|
|
35
|
+
<Canvas>
|
|
36
|
+
<Story of={CardStories.AppBackground} />
|
|
37
|
+
</Canvas>
|
|
38
|
+
|
|
39
|
+
## Layout tips
|
|
40
|
+
|
|
41
|
+
- Allow cards to breathe: default padding is `p-6`, override via the `className` prop for custom
|
|
42
|
+
spacing.
|
|
43
|
+
- Stack cards in responsive grids to create dashboards or settings panels.
|
|
44
|
+
- Avoid nesting too many cards; use muted surfaces inside when grouping secondary information.
|
|
45
|
+
|
|
46
|
+
<Canvas>
|
|
47
|
+
<Story of={CardStories.DashboardLayout} />
|
|
48
|
+
</Canvas>
|
|
49
|
+
|
|
50
|
+
## Accessibility
|
|
51
|
+
|
|
52
|
+
Cards render as semantic `<div>` elements. Provide meaningful headings and maintain contrast ratios
|
|
53
|
+
when overlaying text on app backgrounds. For interactive cards, wrap focusable elements instead of
|
|
54
|
+
attaching click handlers to the card root.
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Button } from "./Button";
|
|
3
|
+
import Typo from "./Typo";
|
|
4
|
+
import { Card } from "./Card";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Card> = {
|
|
7
|
+
title: "Components/Card",
|
|
8
|
+
component: Card,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "padded"
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default meta;
|
|
16
|
+
type Story = StoryObj<typeof Card>;
|
|
17
|
+
|
|
18
|
+
export const Playground: Story = {
|
|
19
|
+
render: (args) => (
|
|
20
|
+
<div className="max-w-md">
|
|
21
|
+
<Card {...args}>
|
|
22
|
+
<Typo variant="headline-sm" bold="semibold">
|
|
23
|
+
Glassmorphic card
|
|
24
|
+
</Typo>
|
|
25
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--muted-fg)]">
|
|
26
|
+
Cards wrap related content with elevated styling and optional backgrounds.
|
|
27
|
+
</Typo>
|
|
28
|
+
</Card>
|
|
29
|
+
</div>
|
|
30
|
+
),
|
|
31
|
+
args: {
|
|
32
|
+
children: undefined
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const WithDecorations: Story = {
|
|
37
|
+
render: () => (
|
|
38
|
+
<div className="max-w-md">
|
|
39
|
+
<Card showDecorations>
|
|
40
|
+
<Typo variant="headline-sm" bold="semibold">
|
|
41
|
+
Decorative frame
|
|
42
|
+
</Typo>
|
|
43
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--muted-fg)]">
|
|
44
|
+
Enable gradient borders for hero cards or marketing content.
|
|
45
|
+
</Typo>
|
|
46
|
+
</Card>
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const AppBackground: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div className="max-w-lg">
|
|
54
|
+
<Card variant="app-background" className="p-8">
|
|
55
|
+
<Typo variant="headline-sm" bold="semibold">
|
|
56
|
+
App background variant
|
|
57
|
+
</Typo>
|
|
58
|
+
<Typo variant="body-sm" className="mt-3 text-[var(--muted-fg)]">
|
|
59
|
+
Uses the same artwork as the `AppBackground` component and adapts to theme changes.
|
|
60
|
+
</Typo>
|
|
61
|
+
<div className="mt-6 flex gap-3">
|
|
62
|
+
<Button variant="primary">Get started</Button>
|
|
63
|
+
<Button variant="secondary">Learn more</Button>
|
|
64
|
+
</div>
|
|
65
|
+
</Card>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const ElevatedComparison: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<div className="grid max-w-3xl grid-cols-1 gap-6 md:grid-cols-2">
|
|
73
|
+
<Card>
|
|
74
|
+
<Typo variant="title-md" bold="semibold">
|
|
75
|
+
Default
|
|
76
|
+
</Typo>
|
|
77
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--muted-fg)]">
|
|
78
|
+
Soft glassmorphism without drop shadow.
|
|
79
|
+
</Typo>
|
|
80
|
+
</Card>
|
|
81
|
+
<Card elevated>
|
|
82
|
+
<Typo variant="title-md" bold="semibold">
|
|
83
|
+
Elevated
|
|
84
|
+
</Typo>
|
|
85
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--muted-fg)]">
|
|
86
|
+
Adds `var(--shadow-elevated)` for emphasis and layering.
|
|
87
|
+
</Typo>
|
|
88
|
+
</Card>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const DashboardLayout: Story = {
|
|
94
|
+
render: () => (
|
|
95
|
+
<div className="grid max-w-4xl gap-6 md:grid-cols-3">
|
|
96
|
+
<Card>
|
|
97
|
+
<Typo variant="title-md" bold="semibold">
|
|
98
|
+
Daily active users
|
|
99
|
+
</Typo>
|
|
100
|
+
<Typo variant="display-sm" bold="bold" className="mt-4">
|
|
101
|
+
1,248
|
|
102
|
+
</Typo>
|
|
103
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--success)]">
|
|
104
|
+
+12% vs last week
|
|
105
|
+
</Typo>
|
|
106
|
+
</Card>
|
|
107
|
+
<Card elevated>
|
|
108
|
+
<Typo variant="title-md" bold="semibold">
|
|
109
|
+
New signups
|
|
110
|
+
</Typo>
|
|
111
|
+
<Typo variant="display-sm" bold="bold" className="mt-4">
|
|
112
|
+
327
|
|
113
|
+
</Typo>
|
|
114
|
+
<Typo variant="body-sm" className="mt-2 text-[var(--muted-fg)]">
|
|
115
|
+
Compared to rolling average
|
|
116
|
+
</Typo>
|
|
117
|
+
</Card>
|
|
118
|
+
<Card showDecorations>
|
|
119
|
+
<Typo variant="title-md" bold="semibold">
|
|
120
|
+
Retention cohort
|
|
121
|
+
</Typo>
|
|
122
|
+
<Typo variant="body-sm" className="mt-3 text-[var(--muted-fg)]">
|
|
123
|
+
Combine props to create data-heavy layouts with visual hierarchy.
|
|
124
|
+
</Typo>
|
|
125
|
+
</Card>
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
};
|
|
129
|
+
|
package/src/components/Chip.tsx
CHANGED
|
@@ -9,6 +9,8 @@ export type ChipProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
9
9
|
backgroundColor?: string;
|
|
10
10
|
/** Custom text color (overrides variant) */
|
|
11
11
|
textColor?: string;
|
|
12
|
+
/** Optional handler to render a delete affordance */
|
|
13
|
+
onDelete?: React.MouseEventHandler<HTMLButtonElement>;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export default function Chip({
|
|
@@ -19,6 +21,7 @@ export default function Chip({
|
|
|
19
21
|
backgroundColor,
|
|
20
22
|
textColor,
|
|
21
23
|
style,
|
|
24
|
+
onDelete,
|
|
22
25
|
...props
|
|
23
26
|
}: ChipProps) {
|
|
24
27
|
const base =
|
|
@@ -42,17 +45,27 @@ export default function Chip({
|
|
|
42
45
|
const mergedStyle: React.CSSProperties = {
|
|
43
46
|
...style,
|
|
44
47
|
...(backgroundColor && { backgroundColor }),
|
|
45
|
-
...(textColor && { color: textColor })
|
|
48
|
+
...(textColor && { color: textColor })
|
|
46
49
|
};
|
|
47
50
|
|
|
48
51
|
return (
|
|
49
|
-
<div
|
|
50
|
-
className={[base, colorClasses, className].join(" ")}
|
|
52
|
+
<div
|
|
53
|
+
className={[base, colorClasses, className].join(" ")}
|
|
51
54
|
style={mergedStyle}
|
|
52
55
|
{...props}
|
|
53
56
|
>
|
|
54
57
|
{icon ? <Icon name={icon} size="sm" className="mr-0.5" /> : null}
|
|
55
58
|
<span>{label}</span>
|
|
59
|
+
{onDelete ? (
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
onClick={onDelete}
|
|
63
|
+
className="ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30"
|
|
64
|
+
aria-label="Remove"
|
|
65
|
+
>
|
|
66
|
+
<Icon name="close" size="sm" />
|
|
67
|
+
</button>
|
|
68
|
+
) : null}
|
|
56
69
|
</div>
|
|
57
70
|
);
|
|
58
71
|
}
|
package/src/components/Input.tsx
CHANGED
|
@@ -1,36 +1,121 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>,
|
|
3
|
+
export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
|
|
4
4
|
/** Input visual variant */
|
|
5
5
|
variant?: "default" | "inline";
|
|
6
|
+
/** Optional floating label (renders a fieldset wrapper when provided) */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Additional props for the surrounding fieldset when label is set */
|
|
9
|
+
fieldsetProps?: React.FieldsetHTMLAttributes<HTMLFieldSetElement>;
|
|
10
|
+
/** Additional props for the legend when label is set */
|
|
11
|
+
legendProps?: React.HTMLAttributes<HTMLLegendElement>;
|
|
12
|
+
/** Flag to visually mark the input as errored */
|
|
13
|
+
error?: boolean;
|
|
6
14
|
};
|
|
7
15
|
|
|
8
16
|
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
9
|
-
(
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
(
|
|
18
|
+
{
|
|
19
|
+
className,
|
|
20
|
+
disabled,
|
|
21
|
+
variant = "default",
|
|
22
|
+
label,
|
|
23
|
+
fieldsetProps,
|
|
24
|
+
legendProps,
|
|
25
|
+
error = false,
|
|
26
|
+
...props
|
|
27
|
+
},
|
|
28
|
+
ref
|
|
29
|
+
) => {
|
|
30
|
+
const isInlineVariant = variant === "inline";
|
|
31
|
+
const shouldUseInlineStyles = isInlineVariant || Boolean(label);
|
|
32
|
+
const isError = error && !disabled;
|
|
33
|
+
|
|
34
|
+
const inputClasses: string[] = [
|
|
35
|
+
"w-full bg-transparent outline-none transition-colors",
|
|
36
|
+
shouldUseInlineStyles ? "h-9" : "h-12 px-4 rounded-full",
|
|
37
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]"
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
if (!shouldUseInlineStyles) {
|
|
41
|
+
inputClasses.push("border");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (disabled) {
|
|
45
|
+
inputClasses.push("text-[#3F424F]", "cursor-not-allowed");
|
|
46
|
+
if (!shouldUseInlineStyles) {
|
|
47
|
+
inputClasses.push("border-[#3F424F]");
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
inputClasses.push("text-[var(--muted-fg)]", "focus:text-[var(--fg)]");
|
|
51
|
+
if (!shouldUseInlineStyles) {
|
|
52
|
+
inputClasses.push(
|
|
53
|
+
isError ? "border-[var(--destructive)]" : "border-[var(--muted-fg)]",
|
|
54
|
+
isError ? "hover:border-[var(--destructive)]" : "hover:border-[var(--border)]",
|
|
55
|
+
isError ? "focus:border-[var(--destructive)]" : "focus:border-[var(--color-brand)]"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (className) {
|
|
61
|
+
inputClasses.push(className);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const inputClassName = inputClasses.join(" ");
|
|
65
|
+
|
|
66
|
+
const inputElement = (
|
|
67
|
+
<input ref={ref} disabled={disabled} className={inputClassName} {...props} />
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (!label) {
|
|
71
|
+
return inputElement;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { className: fieldsetClassNameProp = "", ...restFieldsetProps } = fieldsetProps ?? {};
|
|
75
|
+
|
|
76
|
+
const { className: legendClassNameProp = "", ...restLegendProps } = legendProps ?? {};
|
|
77
|
+
|
|
78
|
+
const fieldsetClassName = [
|
|
79
|
+
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-14",
|
|
80
|
+
isError ? "border-[var(--destructive)]" : "border-[var(--border)]",
|
|
81
|
+
isError
|
|
82
|
+
? "focus-within:border-[var(--destructive)]"
|
|
83
|
+
: "focus-within:border-[var(--color-brand)]",
|
|
84
|
+
disabled ? "opacity-60 cursor-not-allowed" : "",
|
|
85
|
+
fieldsetClassNameProp
|
|
86
|
+
]
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
.join(" ");
|
|
89
|
+
|
|
90
|
+
const legendColorClass = disabled
|
|
91
|
+
? "text-[var(--muted-fg)]"
|
|
92
|
+
: isError
|
|
93
|
+
? "text-[var(--destructive)]"
|
|
94
|
+
: "text-[var(--muted-fg)]";
|
|
95
|
+
|
|
96
|
+
const legendClassNameCombined = [
|
|
97
|
+
"ml-4 px-1 text-sm leading-none relative font-normal select-none",
|
|
98
|
+
legendColorClass,
|
|
99
|
+
legendClassNameProp
|
|
100
|
+
]
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.join(" ");
|
|
103
|
+
|
|
12
104
|
return (
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"focus:text-[var(--fg)]",
|
|
28
|
-
isInline ? "" : "focus:border-[var(--color-brand)]"
|
|
29
|
-
].join(" "),
|
|
30
|
-
className
|
|
31
|
-
].join(" ")}
|
|
32
|
-
{...props}
|
|
33
|
-
/>
|
|
105
|
+
<fieldset
|
|
106
|
+
{...restFieldsetProps}
|
|
107
|
+
className={fieldsetClassName}
|
|
108
|
+
>
|
|
109
|
+
<legend
|
|
110
|
+
{...restLegendProps}
|
|
111
|
+
className={legendClassNameCombined}
|
|
112
|
+
>
|
|
113
|
+
{label}
|
|
114
|
+
</legend>
|
|
115
|
+
<div className="relative flex pl-5 pr-3 pb-1 h-full">
|
|
116
|
+
<div className="flex w-full">{inputElement}</div>
|
|
117
|
+
</div>
|
|
118
|
+
</fieldset>
|
|
34
119
|
);
|
|
35
120
|
}
|
|
36
121
|
);
|
|
@@ -33,4 +33,19 @@ export const Variants: Story = {
|
|
|
33
33
|
)
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
export const Deletable: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
label: "Filter: Active",
|
|
39
|
+
variant: "light",
|
|
40
|
+
onDelete: () => console.log("delete")
|
|
41
|
+
},
|
|
42
|
+
render: (args) => (
|
|
43
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
44
|
+
<Chip {...args} onDelete={args.onDelete} />
|
|
45
|
+
<Chip label="Team: Core" onDelete={args.onDelete} variant="dark" />
|
|
46
|
+
<Chip label="Status: Pending" onDelete={args.onDelete} variant="warning" />
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
};
|
|
50
|
+
|
|
36
51
|
|
|
@@ -47,3 +47,29 @@ export const Inline: Story = {
|
|
|
47
47
|
</div>
|
|
48
48
|
)
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
export const WithLabel: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div className="flex flex-col gap-4 w-96">
|
|
54
|
+
<Input label="Project name" placeholder="Neo PTO" />
|
|
55
|
+
<Input label="Email" type="email" placeholder="you@example.com" />
|
|
56
|
+
<Input label="Password" type="password" placeholder="••••••••" />
|
|
57
|
+
<Input label="Disabled field" placeholder="Not editable" disabled />
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Error: Story = {
|
|
63
|
+
render: () => (
|
|
64
|
+
<div className="flex flex-col gap-4 w-96">
|
|
65
|
+
<Input error placeholder="Unlabeled error" />
|
|
66
|
+
<Input label="Email" type="email" placeholder="you@example.com" error />
|
|
67
|
+
<Input
|
|
68
|
+
label="Password"
|
|
69
|
+
type="password"
|
|
70
|
+
placeholder="This is required"
|
|
71
|
+
error
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
};
|