@neoptocom/neopto-ui 1.6.7 → 1.6.8

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
+
@@ -118,3 +118,8 @@ export const FullWidthCallToAction: Story = {
118
118
 
119
119
 
120
120
 
121
+
122
+
123
+
124
+
125
+
@@ -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
 
@@ -48,9 +48,15 @@ function getTypoClasses(
48
48
  bold: "font-bold"
49
49
  };
50
50
 
51
+ // Add default horizontal spacing (margin-right) for spacing between side-by-side Typo components
52
+ // Users can override with className="mr-0" if needed
53
+ const hasMarginRight = className?.includes("mr-");
54
+ const horizontalSpacing = hasMarginRight ? "" : "mr-2";
55
+
51
56
  return [
52
57
  weights[weight],
53
58
  muted ? "text-[var(--muted-fg)]" : "",
59
+ horizontalSpacing,
54
60
  className
55
61
  ].filter(Boolean).join(" ");
56
62
  }
@@ -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
-