@nphuonha/ui-kit 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -1,14 +1,28 @@
1
1
  # @nphuonha/ui-kit
2
2
 
3
- Solid, clean, flat, modern React UI kit. **Base UI** for behavior/a11y + owned tokens/styles + a consistent prop-driven API. Accent is vibrant by default and **customizable by color generation** (M3 Expressive-inspired, not M3 Expressive).
3
+ Solid, clean, flat, modern React UI kit. **Base UI** for behavior/a11y + owned tokens/styles + a consistent prop-driven API. Accent is vibrant by default and customizable by color generation.
4
4
 
5
- > Status: MVP rewrite (single suite). Breaking changes from previous `warm-editorial`/`celebration` suites.
5
+ > **v0.3.0** adds `BottomNav`, `DesktopNav` and `activeIcon` support for selected navigation items.
6
6
 
7
- ## Install / use (workspace or package)
7
+ - **Docs:** https://ui.nphuonha.id.vn
8
+ - **Agent guide:** https://ui.nphuonha.id.vn/docs/agents
9
+ - **llms.txt:** https://ui.nphuonha.id.vn/llms.txt
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @nphuonha/ui-kit
15
+ ```
16
+
17
+ Peer dependencies: React 19, React DOM 19, Tailwind CSS 4.
18
+
19
+ The published package ships compiled ESM + `.d.ts`, so it works with Next.js, Vite, Remix and other bundlers. No `transpilePackages` step is required.
20
+
21
+ ## Usage
8
22
 
9
23
  ```tsx
10
24
  import "@nphuonha/ui-kit/styles.css";
11
- import { Button, Card, CardContent, Input, Toaster, accentStyle } from "@nphuonha/ui-kit";
25
+ import { Button, Card, CardContent, Input, Toaster } from "@nphuonha/ui-kit";
12
26
 
13
27
  export function App() {
14
28
  return (
@@ -26,25 +40,26 @@ export function App() {
26
40
  ```
27
41
 
28
42
  - Scope tokens/overlays with `data-ui="kit"` (add to `<html>` for portalled overlays).
29
- - Dark mode: add `class="dark"` (and/or `data-ui="kit"` dark variant as supported by your setup).
30
- - Change accent at runtime: `style={accentStyle("#0d9488", "light")}` or `generateAccentVars(color, mode)` to override `--primary*`/`--ring`.
43
+ - Dark mode: add the `dark` class to the same element.
44
+ - Change the accent at runtime:
31
45
 
32
- ### Framework setup
46
+ ```tsx
47
+ import { accentStyle } from "@nphuonha/ui-kit";
33
48
 
34
- The published package ships compiled ESM + `.d.ts`, so it works with Next.js, Vite, Remix and other bundlers. **No `transpilePackages` is required for the published package.**
49
+ <div data-ui="kit" style={accentStyle("#0d9488", "light")}>
50
+ <App />
51
+ </div>
52
+ ```
35
53
 
36
- If you are consuming the TypeScript source from this monorepo instead, add:
54
+ Or generate variables directly:
37
55
 
38
56
  ```ts
39
- // next.config.ts
40
- const nextConfig = {
41
- transpilePackages: ["@nphuonha/ui-kit"],
42
- };
57
+ import { generateAccentVars } from "@nphuonha/ui-kit";
43
58
 
44
- export default nextConfig;
59
+ const vars = generateAccentVars("#0d9488", "dark");
45
60
  ```
46
61
 
47
- The published stylesheet scans its own components with `@source "./components"` inside `dist`. If your Tailwind setup ignores `@source` directives from dependencies, add this to your app CSS as a fallback:
62
+ Tailwind v4: the package stylesheet imports Tailwind. If your setup ignores `@source` from dependencies, add:
48
63
 
49
64
  ```css
50
65
  @source "../node_modules/@nphuonha/ui-kit/dist/components";
@@ -62,19 +77,9 @@ import { Input } from "@nphuonha/ui-kit/input";
62
77
 
63
78
  Variants live in server-safe `*-variants.ts` files and can be used from server components.
64
79
 
65
- ## Structure
66
-
67
- ```
68
- src/components/ui/button/
69
- button.tsx # "use client" behavior/render
70
- button-variants.ts # cva, server-safe
71
- button.test.tsx # colocated test
72
- index.ts # explicit exports
73
- ```
74
-
75
80
  ## Icons
76
81
 
77
- The kit uses **Phosphor** as its default internal icon set (check, minus, chevron, close, spinner). User-facing icons stay icon-agnostic — pass any icon component that accepts `size` and `className`:
82
+ The kit uses **Phosphor** for its internal affordances (check, minus, chevron, close, spinner). User-facing icons are pack-agnostic — pass any component that accepts `size` and `className`:
78
83
 
79
84
  ```tsx
80
85
  <Button startIcon={Plus}>New item</Button>
@@ -82,7 +87,7 @@ The kit uses **Phosphor** as its default internal icon set (check, minus, chevro
82
87
  <Alert icon={Info} title="Heads up">...</Alert>
83
88
  ```
84
89
 
85
- Swap the internal set at any time with `IconProvider`:
90
+ Swap the internal set with `IconProvider`:
86
91
 
87
92
  ```tsx
88
93
  import { IconProvider } from "@nphuonha/ui-kit";
@@ -97,41 +102,14 @@ import { Check, ChevronUpDown, Loader, Minus, X } from "your-icon-pack";
97
102
 
98
103
  `Toaster` also accepts an `icons` prop for `success`, `info`, `warning`, `error`, `loading` and `close`.
99
104
 
100
- The docs site uses Phosphor too. Swapping to another pack only affects the kit's internal affordances; icons you pass to `Button`, `IconButton` and `Alert` are always yours.
101
-
102
- ## MVP components
105
+ ## Components
103
106
 
104
- `Button`, `IconButton`, `Input`, `Textarea`, `Field`/`Label`, `Checkbox`, `RadioGroup`, `Switch`, `Select`, `Dialog`, `Sheet`, `Popover`, `Tooltip`, `Tabs`, `Card` (+Header/Title/Description/Content/Footer), `Badge`, `Alert`, `Separator`, `Spinner`, `Toaster`/`toast`.
107
+ `Button`, `IconButton`, `Input`, `Textarea`, `Field`/`Label`, `Checkbox`, `RadioGroup`, `Switch`, `Select`, `Dialog`, `Sheet`, `Popover`, `Tooltip`, `Tabs`, `BottomNav`, `DesktopNav`, `Card` (+Header/Title/Description/Content/Footer), `Badge`, `Alert`, `Separator`, `Spinner`, `Toaster`/`toast`.
105
108
 
106
- ## Scripts
109
+ `BottomNav` and `DesktopNav` items accept an optional `activeIcon` to swap to a filled or alternate icon when selected.
107
110
 
108
- ```bash
109
- npm run build -w @nphuonha/ui-kit # emit dist (ESM + .d.ts + CSS)
110
- npm run typecheck -w @nphuonha/ui-kit
111
- npm run test -w @nphuonha/ui-kit
112
- ```
113
-
114
- ## Publishing
115
-
116
- The package builds to `dist` as ESM + `.d.ts` with `npm run build`. `prepack` builds automatically before `npm pack`/`npm publish`; `prepublishOnly` runs typecheck + tests.
117
-
118
- ```bash
119
- npm login
120
- npm publish -w @nphuonha/ui-kit
121
- ```
122
-
123
- Or from the repo root:
124
-
125
- ```bash
126
- npm run publish:kit
127
- ```
128
-
129
- Bump the version with:
130
-
131
- ```bash
132
- npm version patch -w @nphuonha/ui-kit
133
- ```
111
+ Live previews, code and API reference: https://ui.nphuonha.id.vn
134
112
 
135
- The published package is compiled and works with Vite, Remix, Next.js and plain bundler-based JS apps. The repository's docs app consumes the TypeScript source via a local path alias for fast iteration.
113
+ ## License
136
114
 
137
- See `CONVENTIONS.md` for design/API rules.
115
+ MIT
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import { type IconComponent } from "../../../lib/utils.js";
3
+ export interface BottomNavItem {
4
+ value: string;
5
+ label: React.ReactNode;
6
+ icon: IconComponent;
7
+ activeIcon?: IconComponent;
8
+ href?: string;
9
+ badge?: React.ReactNode;
10
+ disabled?: boolean;
11
+ }
12
+ export interface BottomNavProps extends Omit<React.ComponentPropsWithoutRef<"nav">, "onChange"> {
13
+ items: BottomNavItem[];
14
+ value?: string;
15
+ defaultValue?: string;
16
+ onValueChange?: (value: string) => void;
17
+ /** Pin the bar to the bottom of the viewport. */
18
+ fixed?: boolean;
19
+ }
20
+ export declare const BottomNav: React.ForwardRefExoticComponent<BottomNavProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../../lib/utils.js";
5
+ export const BottomNav = React.forwardRef(({ items, value, defaultValue, onValueChange, fixed = false, className, "aria-label": ariaLabel = "Bottom navigation", ...props }, ref) => {
6
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
7
+ const activeValue = value ?? uncontrolledValue;
8
+ const select = (next) => {
9
+ if (value === undefined)
10
+ setUncontrolledValue(next);
11
+ onValueChange?.(next);
12
+ };
13
+ return (_jsx("nav", { ref: ref, "aria-label": ariaLabel, className: cn("surface-curved relative rounded-[24px] bg-card p-1.5 shadow-[var(--shadow-raised)]", fixed
14
+ ? "fixed inset-x-3 bottom-[calc(env(safe-area-inset-bottom)+0.75rem)] z-40 mx-auto max-w-md"
15
+ : "w-full", className), ...props, children: _jsx("div", { className: "flex items-stretch gap-1", children: items.map((item) => {
16
+ const active = item.value === activeValue;
17
+ const Icon = (active && item.activeIcon) || item.icon;
18
+ const content = (_jsxs(_Fragment, { children: [_jsxs("span", { className: cn("relative flex h-7 min-w-12 items-center justify-center rounded-pill px-2.5 transition-[background-color,color,box-shadow] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)]", active
19
+ ? "bg-primary text-primary-foreground shadow-[var(--shadow-raised)]"
20
+ : "text-muted-foreground group-hover:bg-surface group-hover:text-foreground"), children: [_jsx(Icon, { size: 18 }), item.badge != null ? (_jsx("span", { className: cn("absolute -right-1 -top-1 inline-flex min-w-4 items-center justify-center rounded-pill px-1 text-[9px] font-semibold leading-4", active
21
+ ? "bg-card text-foreground shadow-sm"
22
+ : "bg-primary text-primary-foreground"), children: item.badge })) : null] }), _jsx("span", { className: cn("max-w-full truncate text-[10px] font-medium leading-none tracking-[-0.01em]", active ? "font-semibold text-foreground" : "text-muted-foreground"), children: item.label })] }));
23
+ const itemClassName = cn("group relative flex min-w-0 flex-1 flex-col items-center justify-center gap-1 rounded-[18px] px-1 py-1.5 outline-none transition-colors duration-[var(--duration-ui)]", "focus-visible:ring-2 focus-visible:ring-ring/40", item.disabled && "pointer-events-none opacity-45");
24
+ if (item.href) {
25
+ return (_jsx("a", { href: item.href, "aria-current": active ? "page" : undefined, "aria-disabled": item.disabled || undefined, tabIndex: item.disabled ? -1 : undefined, className: itemClassName, onClick: (event) => {
26
+ if (item.disabled) {
27
+ event.preventDefault();
28
+ return;
29
+ }
30
+ select(item.value);
31
+ }, children: content }, item.value));
32
+ }
33
+ return (_jsx("button", { type: "button", disabled: item.disabled, "aria-current": active ? "page" : undefined, className: itemClassName, onClick: () => select(item.value), children: content }, item.value));
34
+ }) }) }));
35
+ });
36
+ BottomNav.displayName = "BottomNav";
@@ -0,0 +1 @@
1
+ export { BottomNav, type BottomNavItem, type BottomNavProps } from "./bottom-nav.js";
@@ -0,0 +1 @@
1
+ export { BottomNav } from "./bottom-nav.js";
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ import { type IconComponent } from "../../../lib/utils.js";
3
+ export interface DesktopNavItem {
4
+ value: string;
5
+ label: React.ReactNode;
6
+ icon?: IconComponent;
7
+ activeIcon?: IconComponent;
8
+ href?: string;
9
+ badge?: React.ReactNode;
10
+ disabled?: boolean;
11
+ }
12
+ export interface DesktopNavProps extends Omit<React.ComponentPropsWithoutRef<"nav">, "onChange"> {
13
+ items: DesktopNavItem[];
14
+ value?: string;
15
+ defaultValue?: string;
16
+ onValueChange?: (value: string) => void;
17
+ }
18
+ export declare const DesktopNav: React.ForwardRefExoticComponent<DesktopNavProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../../lib/utils.js";
5
+ export const DesktopNav = React.forwardRef(({ items, value, defaultValue, onValueChange, className, "aria-label": ariaLabel = "Desktop navigation", ...props }, ref) => {
6
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
7
+ const activeValue = value ?? uncontrolledValue;
8
+ const select = (next) => {
9
+ if (value === undefined)
10
+ setUncontrolledValue(next);
11
+ onValueChange?.(next);
12
+ };
13
+ return (_jsx("nav", { ref: ref, "aria-label": ariaLabel, className: cn(
14
+ // Keep `relative`: surface-curved-soft paints an absolute ::before rim,
15
+ // which otherwise anchors to the nearest positioned ancestor.
16
+ "surface-curved-soft relative scrollbar-none inline-flex max-w-full items-center gap-0.5 overflow-x-auto rounded-pill bg-surface p-1", className), ...props, children: items.map((item) => {
17
+ const active = item.value === activeValue;
18
+ const Icon = (active && item.activeIcon) || item.icon;
19
+ const content = (_jsxs(_Fragment, { children: [Icon ? _jsx(Icon, { size: 15, className: "shrink-0" }) : null, _jsx("span", { className: "whitespace-nowrap", children: item.label }), item.badge != null ? (_jsx("span", { className: cn("ml-0.5 inline-flex min-w-4 items-center justify-center rounded-pill px-1 text-[10px] font-semibold leading-4", active
20
+ ? "bg-primary-soft text-primary-soft-foreground"
21
+ : "bg-surface-hover text-muted-foreground"), children: item.badge })) : null] }));
22
+ const itemClassName = cn("relative inline-flex h-9 shrink-0 items-center justify-center gap-2 rounded-pill px-3.5 text-sm font-medium outline-none transition-[background-color,color,box-shadow] duration-[var(--duration-ui)]", "focus-visible:ring-2 focus-visible:ring-ring/40", active
23
+ ? "surface-curved bg-card text-foreground shadow-sm"
24
+ : "text-muted-foreground hover:bg-card/60 hover:text-foreground", item.disabled && "pointer-events-none opacity-45");
25
+ if (item.href) {
26
+ return (_jsx("a", { href: item.href, "aria-current": active ? "page" : undefined, "aria-disabled": item.disabled || undefined, tabIndex: item.disabled ? -1 : undefined, className: itemClassName, onClick: (event) => {
27
+ if (item.disabled) {
28
+ event.preventDefault();
29
+ return;
30
+ }
31
+ select(item.value);
32
+ }, children: content }, item.value));
33
+ }
34
+ return (_jsx("button", { type: "button", disabled: item.disabled, "aria-current": active ? "page" : undefined, className: itemClassName, onClick: () => select(item.value), children: content }, item.value));
35
+ }) }));
36
+ });
37
+ DesktopNav.displayName = "DesktopNav";
@@ -0,0 +1 @@
1
+ export { DesktopNav, type DesktopNavItem, type DesktopNavProps } from "./desktop-nav.js";
@@ -0,0 +1 @@
1
+ export { DesktopNav } from "./desktop-nav.js";
@@ -3,5 +3,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
4
4
  import { cn } from "../../../lib/utils.js";
5
5
  export function Popover({ trigger, children, title, description, side = "bottom", align = "center", open, defaultOpen, onOpenChange, className, }) {
6
- return (_jsxs(PopoverPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [_jsx(PopoverPrimitive.Trigger, { render: trigger }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Positioner, { side: side, align: align, sideOffset: 8, children: _jsxs(PopoverPrimitive.Popup, { className: cn("surface-curved relative z-50 w-72 rounded-overlay bg-popover p-4 text-popover-foreground shadow-[var(--shadow-overlay)] outline-none", "origin-[var(--transform-origin)] transition-[opacity,transform] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)]", "data-[starting-style]:opacity-0 data-[starting-style]:scale-[0.97] data-[ending-style]:opacity-0 data-[ending-style]:scale-[0.97]", className), children: [title ? _jsx(PopoverPrimitive.Title, { className: "text-sm font-semibold leading-none", children: title }) : null, description ? _jsx(PopoverPrimitive.Description, { className: "mt-1 text-sm text-muted-foreground", children: description }) : null, _jsx("div", { className: cn((title || description) && "mt-3"), children: children })] }) }) })] }));
6
+ return (_jsxs(PopoverPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [_jsx(PopoverPrimitive.Trigger, { render: trigger }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Positioner, { side: side, align: align, sideOffset: 8, className: "z-50", children: _jsxs(PopoverPrimitive.Popup, { className: cn("surface-curved relative z-50 w-72 rounded-overlay bg-popover p-4 text-popover-foreground shadow-[var(--shadow-overlay)] outline-none", "origin-[var(--transform-origin)] transition-[opacity,transform] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)]", "data-[starting-style]:opacity-0 data-[starting-style]:scale-[0.97] data-[ending-style]:opacity-0 data-[ending-style]:scale-[0.97]", className), children: [title ? _jsx(PopoverPrimitive.Title, { className: "text-sm font-semibold leading-none", children: title }) : null, description ? _jsx(PopoverPrimitive.Description, { className: "mt-1 text-sm text-muted-foreground", children: description }) : null, _jsx("div", { className: cn((title || description) && "mt-3"), children: children })] }) }) })] }));
7
7
  }
@@ -11,5 +11,5 @@ const sideStyles = {
11
11
  };
12
12
  export function Sheet({ open, defaultOpen, onOpenChange, trigger, title, description, children, footer, side = "right", className, showClose = true, }) {
13
13
  const CloseIcon = useKitIcon("close");
14
- return (_jsxs(DialogPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger ? _jsx(DialogPrimitive.Trigger, { render: trigger }) : null, _jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-overlay backdrop-blur-[2px] transition-opacity duration-[var(--duration-ui-slow)] data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(DialogPrimitive.Popup, { className: cn("surface-curved fixed z-50 flex flex-col gap-5 bg-popover p-6 text-popover-foreground shadow-[var(--shadow-overlay)] outline-none", "transition-transform duration-[var(--duration-ui-slow)] ease-[var(--ease-out-ui)]", sideStyles[side], className), children: [(title || description || showClose) && (_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "grid gap-1", children: [title ? _jsx(DialogPrimitive.Title, { className: "text-lg font-semibold tracking-[-0.02em] leading-tight", children: title }) : null, description ? _jsx(DialogPrimitive.Description, { className: "text-sm leading-relaxed text-muted-foreground", children: description }) : null] }), showClose ? (_jsx(DialogPrimitive.Close, { render: _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "icon-sm", className: "-mr-2 -mt-2 shrink-0", children: _jsx(CloseIcon, { size: 16 }) }) })) : null] })), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: children }), footer ? _jsx("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: footer }) : null] })] })] }));
14
+ return (_jsxs(DialogPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger ? _jsx(DialogPrimitive.Trigger, { render: trigger }) : null, _jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-overlay backdrop-blur-[2px] transition-opacity duration-[var(--duration-ui-slow)] data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(DialogPrimitive.Popup, { className: cn("surface-curved fixed z-50 flex flex-col gap-5 bg-popover p-6 text-popover-foreground shadow-[var(--shadow-overlay)] outline-none", "transition-transform duration-[var(--duration-ui-slow)] ease-[var(--ease-out-ui)]", sideStyles[side], className), children: [(title || description || showClose) && (_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "grid gap-1", children: [title ? _jsx(DialogPrimitive.Title, { className: "text-lg font-semibold tracking-[-0.02em] leading-tight", children: title }) : null, description ? _jsx(DialogPrimitive.Description, { className: "text-sm leading-relaxed text-muted-foreground", children: description }) : null] }), showClose ? (_jsx(DialogPrimitive.Close, { render: _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "icon-sm", className: "-mr-2 -mt-2 shrink-0", children: _jsx(CloseIcon, { size: 16 }) }) })) : null] })), _jsx("div", { className: "-m-1 min-h-0 flex-1 overflow-auto p-1", children: children }), footer ? _jsx("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: footer }) : null] })] })] }));
15
15
  }
@@ -5,5 +5,5 @@ import { cn } from "../../../lib/utils.js";
5
5
  export function Tooltip({ content, children, side = "top", align = "center", delay = 300, open, defaultOpen, onOpenChange, }) {
6
6
  if (content == null || content === "")
7
7
  return children;
8
- return (_jsx(TooltipPrimitive.Provider, { delay: delay, children: _jsxs(TooltipPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [_jsx(TooltipPrimitive.Trigger, { render: children }), _jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Positioner, { side: side, align: align, sideOffset: 6, children: _jsx(TooltipPrimitive.Popup, { role: "tooltip", className: cn("z-50 max-w-xs rounded-[10px] bg-foreground px-2.5 py-1.5 text-xs font-medium text-background shadow-[var(--shadow-overlay)]", "origin-[var(--transform-origin)] transition-[opacity,transform] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)]", "data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95"), children: content }) }) })] }) }));
8
+ return (_jsx(TooltipPrimitive.Provider, { delay: delay, children: _jsxs(TooltipPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [_jsx(TooltipPrimitive.Trigger, { render: children }), _jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Positioner, { side: side, align: align, sideOffset: 6, className: "z-50", children: _jsx(TooltipPrimitive.Popup, { role: "tooltip", className: cn("z-50 max-w-xs rounded-[10px] bg-foreground px-2.5 py-1.5 text-xs font-medium text-background shadow-[var(--shadow-overlay)]", "origin-[var(--transform-origin)] transition-[opacity,transform] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)]", "data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95"), children: content }) }) })] }) }));
9
9
  }
package/dist/index.d.ts CHANGED
@@ -6,9 +6,11 @@ export { IconProvider, useKitIcon } from "./components/ui/_shared/icon-provider.
6
6
  export type { IconProviderProps, KitIcon, KitIconName, } from "./components/ui/_shared/icon-provider.js";
7
7
  export * from "./components/ui/alert/index.js";
8
8
  export * from "./components/ui/badge/index.js";
9
+ export * from "./components/ui/bottom-nav/index.js";
9
10
  export * from "./components/ui/button/index.js";
10
11
  export * from "./components/ui/card/index.js";
11
12
  export * from "./components/ui/checkbox/index.js";
13
+ export * from "./components/ui/desktop-nav/index.js";
12
14
  export * from "./components/ui/dialog/index.js";
13
15
  export * from "./components/ui/field/index.js";
14
16
  export * from "./components/ui/icon-button/index.js";
package/dist/index.js CHANGED
@@ -8,9 +8,11 @@ export { generateAccentVars, accentStyle } from "./lib/color.js";
8
8
  export { IconProvider, useKitIcon } from "./components/ui/_shared/icon-provider.js";
9
9
  export * from "./components/ui/alert/index.js";
10
10
  export * from "./components/ui/badge/index.js";
11
+ export * from "./components/ui/bottom-nav/index.js";
11
12
  export * from "./components/ui/button/index.js";
12
13
  export * from "./components/ui/card/index.js";
13
14
  export * from "./components/ui/checkbox/index.js";
15
+ export * from "./components/ui/desktop-nav/index.js";
14
16
  export * from "./components/ui/dialog/index.js";
15
17
  export * from "./components/ui/field/index.js";
16
18
  export * from "./components/ui/icon-button/index.js";
package/package.json CHANGED
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "name": "@nphuonha/ui-kit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Solid, clean, flat, modern React UI kit (Base UI + Tailwind v4).",
5
5
  "license": "MIT",
6
6
  "author": "nphuonha",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/nphuonha2101/ui-kit.git",
10
+ "directory": "packages/ui-kit"
11
+ },
12
+ "homepage": "https://ui.nphuonha.id.vn",
13
+ "bugs": {
14
+ "url": "https://github.com/nphuonha2101/ui-kit/issues"
15
+ },
7
16
  "type": "module",
8
17
  "keywords": [
9
18
  "react",
@@ -38,6 +47,11 @@
38
47
  "import": "./dist/components/ui/badge/index.js",
39
48
  "default": "./dist/components/ui/badge/index.js"
40
49
  },
50
+ "./bottom-nav": {
51
+ "types": "./dist/components/ui/bottom-nav/index.d.ts",
52
+ "import": "./dist/components/ui/bottom-nav/index.js",
53
+ "default": "./dist/components/ui/bottom-nav/index.js"
54
+ },
41
55
  "./button": {
42
56
  "types": "./dist/components/ui/button/index.d.ts",
43
57
  "import": "./dist/components/ui/button/index.js",
@@ -53,6 +67,11 @@
53
67
  "import": "./dist/components/ui/checkbox/index.js",
54
68
  "default": "./dist/components/ui/checkbox/index.js"
55
69
  },
70
+ "./desktop-nav": {
71
+ "types": "./dist/components/ui/desktop-nav/index.d.ts",
72
+ "import": "./dist/components/ui/desktop-nav/index.js",
73
+ "default": "./dist/components/ui/desktop-nav/index.js"
74
+ },
56
75
  "./dialog": {
57
76
  "types": "./dist/components/ui/dialog/index.d.ts",
58
77
  "import": "./dist/components/ui/dialog/index.js",