@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 +38 -60
- package/dist/components/ui/bottom-nav/bottom-nav.d.ts +20 -0
- package/dist/components/ui/bottom-nav/bottom-nav.js +36 -0
- package/dist/components/ui/bottom-nav/index.d.ts +1 -0
- package/dist/components/ui/bottom-nav/index.js +1 -0
- package/dist/components/ui/desktop-nav/desktop-nav.d.ts +18 -0
- package/dist/components/ui/desktop-nav/desktop-nav.js +37 -0
- package/dist/components/ui/desktop-nav/index.d.ts +1 -0
- package/dist/components/ui/desktop-nav/index.js +1 -0
- package/dist/components/ui/popover/popover.js +1 -1
- package/dist/components/ui/sheet/sheet.js +1 -1
- package/dist/components/ui/tooltip/tooltip.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +20 -1
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
|
|
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
|
-
>
|
|
5
|
+
> **v0.3.0** adds `BottomNav`, `DesktopNav` and `activeIcon` support for selected navigation items.
|
|
6
6
|
|
|
7
|
-
|
|
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
|
|
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 `
|
|
30
|
-
- Change accent at runtime:
|
|
43
|
+
- Dark mode: add the `dark` class to the same element.
|
|
44
|
+
- Change the accent at runtime:
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
```tsx
|
|
47
|
+
import { accentStyle } from "@nphuonha/ui-kit";
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
<div data-ui="kit" style={accentStyle("#0d9488", "light")}>
|
|
50
|
+
<App />
|
|
51
|
+
</div>
|
|
52
|
+
```
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
Or generate variables directly:
|
|
37
55
|
|
|
38
56
|
```ts
|
|
39
|
-
|
|
40
|
-
const nextConfig = {
|
|
41
|
-
transpilePackages: ["@nphuonha/ui-kit"],
|
|
42
|
-
};
|
|
57
|
+
import { generateAccentVars } from "@nphuonha/ui-kit";
|
|
43
58
|
|
|
44
|
-
|
|
59
|
+
const vars = generateAccentVars("#0d9488", "dark");
|
|
45
60
|
```
|
|
46
61
|
|
|
47
|
-
|
|
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**
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
`BottomNav` and `DesktopNav` items accept an optional `activeIcon` to swap to a filled or alternate icon when selected.
|
|
107
110
|
|
|
108
|
-
|
|
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
|
-
|
|
113
|
+
## License
|
|
136
114
|
|
|
137
|
-
|
|
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.
|
|
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",
|