@neoptocom/neopto-ui 1.6.7 → 1.6.9

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.
@@ -68,3 +68,8 @@ page load. The interactive example demonstrates an in-app documents flow.
68
68
 
69
69
 
70
70
 
71
+
72
+
73
+
74
+
75
+
@@ -8,7 +8,7 @@ export interface BreadcrumbItem {
8
8
  /** Optional href for navigation */
9
9
  href?: string;
10
10
  /** Optional icon name (Material Symbols) */
11
- icon?: string;
11
+ icon?: React.ReactNode;
12
12
  /** Optional click handler */
13
13
  onClick?: () => void;
14
14
  }
@@ -66,8 +66,7 @@ export const Breadcrumb: React.FC<BreadcrumbProps> = ({
66
66
  }}
67
67
  className="group flex items-center gap-1 cursor-pointer text-[var(--muted-fg)]"
68
68
  >
69
- {isFirst && showHomeIcon && <Icon name="home" size="sm" />}
70
- {item.icon && !showHomeIcon && <Icon name={item.icon} size="sm" />}
69
+ {item.icon && item.icon}
71
70
  <Typo variant="label-md" bold="semibold" className="group-hover:underline">{item.label}</Typo>
72
71
  </a>
73
72
  ) : (
@@ -88,8 +87,7 @@ export const Breadcrumb: React.FC<BreadcrumbProps> = ({
88
87
  }
89
88
  aria-current={isLast ? "page" : undefined}
90
89
  >
91
- {isFirst && showHomeIcon && <Icon name="home" size="sm" />}
92
- {item.icon && !showHomeIcon && <Icon name={item.icon} size="sm" />}
90
+ {item.icon && item.icon}
93
91
  <Typo variant="label-md" bold={isLast ? "bold" : "semibold"} className={item.onClick && !isLast ? "group-hover:underline" : ""}>{item.label}</Typo>
94
92
  </span>
95
93
  )}
@@ -64,3 +64,8 @@ Use variants to communicate hierarchy:
64
64
 
65
65
 
66
66
 
67
+
68
+
69
+
70
+
71
+
@@ -16,7 +16,7 @@ const meta: Meta<typeof Button> = {
16
16
  argTypes: {
17
17
  variant: {
18
18
  control: "inline-radio",
19
- options: ["primary", "secondary", "ghost"]
19
+ options: ["primary", "secondary", "ghost", "inline"]
20
20
  },
21
21
  size: {
22
22
  control: "inline-radio",
@@ -54,6 +54,11 @@ export const Variants: Story = {
54
54
  Ghost
55
55
  </Typo>
56
56
  </Button>
57
+ <Button variant="inline">
58
+ <Typo variant="title-sm" bold="semibold">
59
+ Inline
60
+ </Typo>
61
+ </Button>
57
62
  </div>
58
63
  )
59
64
  };
@@ -95,6 +100,10 @@ export const WithIcons: Story = {
95
100
  <Icon name="settings" />
96
101
  <span>Settings</span>
97
102
  </Button>
103
+ <Button variant="inline">
104
+ <Icon name="settings" />
105
+ <span>Inline</span>
106
+ </Button>
98
107
  </div>
99
108
  )
100
109
  };
@@ -118,3 +127,8 @@ export const FullWidthCallToAction: Story = {
118
127
 
119
128
 
120
129
 
130
+
131
+
132
+
133
+
134
+
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
 
3
3
  export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
4
4
  /** Button visual variant */
5
- variant?: "primary" | "secondary" | "ghost";
5
+ variant?: "primary" | "secondary" | "ghost" | "inline";
6
6
  /** Button size */
7
7
  size?: "sm" | "md" | "lg";
8
8
  /** Make button full width */
@@ -25,7 +25,8 @@ function getButtonClasses(
25
25
  const variants = {
26
26
  primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600 disabled:bg-neutral-400",
27
27
  secondary: "border border-cyan-500 text-cyan-500 bg-transparent hover:bg-cyan-50 active:bg-cyan-100 disabled:border-neutral-400 disabled:text-neutral-400",
28
- ghost: "bg-transparent text-[var(--fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50"
28
+ ghost: "bg-transparent text-[var(--fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50",
29
+ inline: "text-cyan-500 bg-transparent active:bg-cyan-100 disabled:text-neutral-400"
29
30
  };
30
31
 
31
32
  const sizes = {
@@ -37,7 +38,7 @@ function getButtonClasses(
37
38
  return [
38
39
  base,
39
40
  variants[variant],
40
- sizes[size],
41
+ variant === "inline" ? "px-2" : sizes[size],
41
42
  fullWidth ? "w-full" : "",
42
43
  className
43
44
  ].filter(Boolean).join(" ");
@@ -64,3 +64,8 @@ attaching click handlers to the card root.
64
64
 
65
65
 
66
66
 
67
+
68
+
69
+
70
+
71
+
@@ -1,9 +1,10 @@
1
1
  import * as React from "react";
2
2
  import Icon from "./Icon";
3
+ import { X } from "lucide-react";
3
4
 
4
5
  export type ChipProps = React.HTMLAttributes<HTMLDivElement> & {
5
6
  variant?: "warning" | "success" | "error" | "light" | "dark";
6
- icon?: string;
7
+ icon?: React.ReactNode;
7
8
  label?: string;
8
9
  /** Custom text color (overrides variant) */
9
10
  textColor?: string;
@@ -129,7 +130,7 @@ export default function Chip({
129
130
  title={title}
130
131
  {...props}
131
132
  >
132
- {icon ? <Icon name={icon} size="sm" className="mr-0.5 flex-shrink-0" /> : null}
133
+ {icon ? icon : null}
133
134
  <span className="truncate">{label}</span>
134
135
  {onDelete ? (
135
136
  <button
@@ -138,7 +139,7 @@ export default function Chip({
138
139
  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 flex-shrink-0"
139
140
  aria-label="Remove"
140
141
  >
141
- <Icon name="close" size="sm" />
142
+ <X size={12} className="flex-shrink-0" />
142
143
  </button>
143
144
  ) : null}
144
145
  </div>
@@ -6,10 +6,10 @@ export type SkeletonProps = React.HTMLAttributes<HTMLDivElement> & {
6
6
 
7
7
  const roundMap: Record<NonNullable<SkeletonProps["rounded"]>, string> = {
8
8
  none: "rounded-none",
9
- sm: "rounded-[var(--radius-sm)]",
10
- md: "rounded-[var(--radius-md)]",
11
- lg: "rounded-[var(--radius-lg)]",
12
- xl: "rounded-[var(--radius-xl)]",
9
+ sm: "rounded",
10
+ md: "rounded-md",
11
+ lg: "rounded-lg",
12
+ xl: "rounded-xl",
13
13
  full: "rounded-full"
14
14
  };
15
15
 
@@ -47,7 +47,7 @@ function getTypoClasses(
47
47
  semibold: "font-semibold",
48
48
  bold: "font-bold"
49
49
  };
50
-
50
+
51
51
  return [
52
52
  weights[weight],
53
53
  muted ? "text-[var(--muted-fg)]" : "",
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { useState } from "react";
3
- import { Breadcrumb, type BreadcrumbItem } from "./Breadcrumb";
3
+ import { Breadcrumb, type BreadcrumbItem } from "../components/Breadcrumb";
4
+ import { LayoutDashboard, Files, Rocket } from "lucide-react";
4
5
 
5
6
  const meta: Meta<typeof Breadcrumb> = {
6
7
  title: "Components/Breadcrumb",
@@ -26,9 +27,9 @@ export const Playground: Story = {};
26
27
  export const WithIcons: Story = {
27
28
  args: {
28
29
  items: [
29
- { label: "Dashboard", href: "/", icon: "dashboard" },
30
- { label: "Projects", href: "/projects", icon: "folder" },
31
- { label: "Neptune Launch", icon: "rocket_launch" }
30
+ { label: "Dashboard", href: "/", icon: <LayoutDashboard size={12} /> },
31
+ { label: "Projects", href: "/projects", icon: <Files size={12} /> },
32
+ { label: "Neptune Launch", icon: <Rocket size={12} /> }
32
33
  ]
33
34
  }
34
35
  };
@@ -44,7 +45,6 @@ export const InteractiveNavigation: Story = {
44
45
  const sections: BreadcrumbItem[] = [
45
46
  { label: "Getting Started", href: "/docs/getting-started" },
46
47
  { label: "Guides", href: "/docs/guides" },
47
- { label: "Auth", icon: "lock" }
48
48
  ];
49
49
 
50
50
  const [activeSection, setActiveSection] = useState(sections[sections.length - 1]);
@@ -86,3 +86,8 @@ export const InteractiveNavigation: Story = {
86
86
 
87
87
 
88
88
 
89
+
90
+
91
+
92
+
93
+
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import Chip from "../components/Chip";
3
+ import { Check } from "lucide-react";
3
4
 
4
5
  const meta: Meta<typeof Chip> = {
5
6
  title: "Components/Chip",
@@ -18,7 +19,7 @@ type Story = StoryObj<typeof Chip>;
18
19
  export const Playground: Story = {};
19
20
 
20
21
  export const WithIcon: Story = {
21
- args: { icon: "check", label: "Completed", variant: "success" }
22
+ args: { icon: <Check size={12} className="flex-shrink-0" />, label: "Completed", variant: "success" }
22
23
  };
23
24
 
24
25
  export const Variants: Story = {
@@ -13,7 +13,7 @@ export const Blocks: Story = {
13
13
  <div className="space-y-3">
14
14
  <Skeleton className="h-4 w-48" />
15
15
  <Skeleton className="h-4 w-72" />
16
- <Skeleton className="h-24 w-full rounded-[--radius-lg]" />
16
+ <Skeleton className="h-24 w-full rounded-lg" />
17
17
  </div>
18
18
  )
19
19
  };
@@ -33,7 +33,7 @@
33
33
  -webkit-font-smoothing: antialiased;
34
34
  }
35
35
 
36
- .btn { @apply inline-flex items-center justify-center gap-2 rounded-[--radius-lg] px-4 py-2 text-sm font-medium transition; }
36
+ .btn { @apply inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition; }
37
37
  .btn-outline { @apply border border-[--color-brand] text-[--color-brand] bg-[--surface] hover:bg-[--color-brand]/10; }
38
38
  .btn-primary { @apply bg-[--color-brand] text-white hover:opacity-90 active:opacity-80; }
39
39
 
@@ -37,6 +37,6 @@
37
37
  }
38
38
 
39
39
  /* Optional shared utility classes used by some components */
40
- .btn { @apply inline-flex items-center justify-center gap-2 rounded-[--radius-lg] px-4 py-2 text-sm font-medium transition; }
40
+ .btn { @apply inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition; }
41
41
  .btn-primary { @apply bg-[--color-brand] text-white hover:opacity-90 active:opacity-80; }
42
42
  .btn-outline { @apply border border-[--color-brand] text-[--color-brand] bg-[--surface] hover:bg-[--color-brand]/10; }
@@ -5,10 +5,6 @@
5
5
  --font-body: 'Roboto', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
6
6
  --font-sans: var(--font-body, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', sans-serif);
7
7
  --color-brand: #22A9CB;
8
- --radius-sm: 0.25rem;
9
- --radius-md: 0.375rem;
10
- --radius-lg: 0.5rem;
11
- --radius-xl: 0.75rem;
12
8
  --bg: #F3F4F6;
13
9
  --surface: #FFFFFF;
14
10
  --fg: #242832;
@@ -26,10 +22,6 @@
26
22
  --font-body: 'Roboto', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
27
23
  --font-sans: var(--font-body, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', sans-serif);
28
24
  --color-brand: #22A9CB;
29
- --radius-sm: 0.25rem;
30
- --radius-md: 0.375rem;
31
- --radius-lg: 0.5rem;
32
- --radius-xl: 0.75rem;
33
25
  --bg: #F3F4F6;
34
26
  --surface: #FFFFFF;
35
27
  --fg: #242832;
@@ -1,170 +0,0 @@
1
- import * as React from "react";
2
- import type { Meta, StoryObj } from "@storybook/react";
3
- import { AgentButton } from "../components/Chat";
4
- import Typo from "../components/Typo";
5
- import agentLogoDark from "../assets/agent-neopto-dark.svg";
6
- import agentLogoLight from "../assets/agent-neopto.svg";
7
-
8
- const meta: Meta<typeof AgentButton> = {
9
- title: "Components/AgentButton",
10
- component: AgentButton,
11
- args: {
12
- hasNotification: false,
13
- notificationMessage: "Hello! How can I help you today?",
14
- logoSrc: agentLogoDark,
15
- logoAlt: "NeoPTO Agent",
16
- },
17
- argTypes: {
18
- hasNotification: { control: "boolean" },
19
- notificationMessage: { control: "text" },
20
- },
21
- decorators: [
22
- (Story) => (
23
- <div className="min-h-screen relative">
24
- <Story />
25
- </div>
26
- ),
27
- ],
28
- };
29
-
30
- export default meta;
31
- type Story = StoryObj<typeof AgentButton>;
32
-
33
- export const Playground: Story = {
34
- render: (args) => {
35
- const [chatOpen, setChatOpen] = React.useState(false);
36
-
37
- return (
38
- <>
39
- <AgentButton
40
- {...args}
41
- onOpenChat={() => {
42
- setChatOpen(true);
43
- console.log("Chat opened!");
44
- }}
45
- />
46
- {chatOpen && (
47
- <div className="fixed bottom-24 right-8 bg-[var(--surface)] border border-[var(--border)] rounded-lg p-4 shadow-lg z-50">
48
- <Typo variant="body-md" className="text-[var(--fg)]">Chat opened!</Typo>
49
- <button
50
- onClick={() => setChatOpen(false)}
51
- className="mt-2 underline"
52
- >
53
- <Typo variant="label-sm" className="text-[var(--muted-fg)]">Close</Typo>
54
- </button>
55
- </div>
56
- )}
57
- </>
58
- );
59
- },
60
- };
61
-
62
- export const WithNotification: Story = {
63
- args: {
64
- hasNotification: true,
65
- notificationMessage: "I have a new update for your project. Would you like to see it?",
66
- },
67
- };
68
-
69
- export const AutoToggleNotification: Story = {
70
- render: (args) => {
71
- const [hasNotification, setHasNotification] = React.useState(false);
72
-
73
- React.useEffect(() => {
74
- const interval = setInterval(() => {
75
- setHasNotification((prev) => !prev);
76
- }, 5000);
77
-
78
- return () => clearInterval(interval);
79
- }, []);
80
-
81
- return (
82
- <AgentButton
83
- {...args}
84
- hasNotification={hasNotification}
85
- notificationMessage="This notification toggles every 5 seconds!"
86
- onOpenChat={() => {
87
- setHasNotification(false);
88
- console.log("Chat opened!");
89
- }}
90
- />
91
- );
92
- },
93
- };
94
-
95
- export const Disabled: Story = {
96
- args: {
97
- disabled: true,
98
- hasNotification: true,
99
- notificationMessage: "This button is disabled (greyscale, no animations)",
100
- },
101
- };
102
-
103
- export const DisabledWhenChatOpen: Story = {
104
- render: (args) => {
105
- const [chatOpen, setChatOpen] = React.useState(false);
106
-
107
- return (
108
- <>
109
- <AgentButton
110
- {...args}
111
- disabled={chatOpen}
112
- hasNotification={!chatOpen}
113
- notificationMessage="Click to open chat (button will become disabled)"
114
- onOpenChat={() => {
115
- setChatOpen(true);
116
- console.log("Chat opened!");
117
- }}
118
- />
119
- {chatOpen && (
120
- <div className="fixed bottom-24 right-8 bg-[var(--surface)] border border-[var(--border)] rounded-lg p-4 shadow-lg z-50 max-w-md">
121
- <Typo variant="body-md" className="text-[var(--fg)]">
122
- Chat is open! Notice the button is now disabled (greyscale with no animations).
123
- </Typo>
124
- <button
125
- onClick={() => setChatOpen(false)}
126
- className="mt-2 px-3 py-1 bg-[var(--color-brand)] text-white rounded-full"
127
- >
128
- <Typo variant="label-sm">Close Chat</Typo>
129
- </button>
130
- </div>
131
- )}
132
- </>
133
- );
134
- },
135
- };
136
-
137
- export const DisabledTransition: Story = {
138
- render: (args) => {
139
- const [disabled, setDisabled] = React.useState(false);
140
-
141
- React.useEffect(() => {
142
- const interval = setInterval(() => {
143
- setDisabled((prev) => !prev);
144
- }, 4000);
145
-
146
- return () => clearInterval(interval);
147
- }, []);
148
-
149
- return (
150
- <>
151
- <AgentButton
152
- {...args}
153
- disabled={disabled}
154
- hasNotification={!disabled}
155
- notificationMessage="Watch the smooth 1-second transition!"
156
- onOpenChat={() => console.log("Chat opened!")}
157
- />
158
- <div className="fixed top-8 left-8 bg-[var(--surface)] border border-[var(--border)] rounded-lg p-4 shadow-lg">
159
- <Typo variant="body-md" className="text-[var(--fg)]">
160
- State: <strong>{disabled ? "Disabled" : "Enabled"}</strong>
161
- </Typo>
162
- <Typo variant="label-sm" className="text-[var(--muted-fg)] mt-1">
163
- Toggles every 4 seconds
164
- </Typo>
165
- </div>
166
- </>
167
- );
168
- },
169
- };
170
-