{"_id":"@dtmai/tvu-ui","name":"@dtmai/tvu-ui","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@dtmai/tvu-ui","version":"0.1.0","description":"Thư viện UI/hooks/utils dùng chung cho nhiều dự án Next.js","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup","dev":"tsup --watch","lint":"tsc --noEmit","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18","react-dom":">=18","tailwindcss":">=3"},"dependencies":{"class-variance-authority":"^0.7.0","clsx":"^2.1.1","tailwind-merge":"^2.5.0","@radix-ui/react-slot":"^1.1.0","tailwindcss-animate":"^1.0.7"},"devDependencies":{"@types/node":"^20.14.0","@types/react":"^18.3.0","@types/react-dom":"^18.3.0","react":"^18.3.0","react-dom":"^18.3.0","tailwindcss":"^3.4.0","tsup":"^8.0.0","typescript":"^5.5.0"},"keywords":["react","nextjs","components","hooks","utils"],"license":"MIT","_id":"@dtmai/tvu-ui@0.1.0","_nodeVersion":"24.16.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-VHXWOHpt11vgyywxmOFGg4vqpCNRr3ZLfuoWvyz+3XpEiI6vwdh9gqPNsFVLR/51YfMQYyDzCkxuprYVQvh23w==","shasum":"8d451a7b322ab5a0c516069a21b77fb0f2b0a485","tarball":"https://registry.npmjs.org/@dtmai/tvu-ui/-/tvu-ui-0.1.0.tgz","fileCount":9,"unpackedSize":39736,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHZlWQaIfO9pwIIn1gSXRillPHoD48Thkb3cu4xV1f26AiEA1SOAY3NTlSbd+RTKoNghpYU+N5XAMLchMZkHD+a5H50="}]},"_npmUser":{"name":"dtmai","email":"tanmai833@gmail.com"},"directories":{},"maintainers":[{"name":"dtmai","email":"tanmai833@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tvu-ui_0.1.0_1788019428650_0.2067078994397744"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-29T16:03:48.377Z","0.1.0":"2026-08-29T16:03:48.777Z","modified":"2026-08-29T16:03:48.961Z"},"maintainers":[{"name":"dtmai","email":"tanmai833@gmail.com"}],"description":"Thư viện UI/hooks/utils dùng chung cho nhiều dự án Next.js","keywords":["react","nextjs","components","hooks","utils"],"license":"MIT","readme":"# @your-scope/my-lib\n\nThư viện dùng chung (UI components, hooks, utils, types, enums) cho nhiều dự án Next.js.\n\n## 1. Cài đặt dependencies để phát triển\n\n```bash\nnpm install\n```\n\n## 2. Build\n\n```bash\nnpm run build\n```\n\nKết quả build nằm trong thư mục `dist/` gồm:\n- `index.js` (CommonJS)\n- `index.mjs` (ESM)\n- `index.d.ts` (type declarations)\n\nChạy `npm run dev` để build lại tự động mỗi khi sửa code (watch mode).\n\n## 3. Xem UI của thư viện khi đang code (Playground)\n\nCó sẵn 1 app xem thử (Vite + React + Tailwind) trong thư mục `playground/`, import trực tiếp component từ `src/` — **không cần build hay `npm link`**, sửa code là thấy thay đổi ngay.\n\n```bash\ncd playground\nnpm install\nnpm run dev\n```\n\nMở `http://localhost:5173` để xem. File `playground/src/App.tsx` là nơi liệt kê các component để preview — thêm component mới vào thư viện thì import và thêm 1 khối xem thử ở đó.\n\nPlayground dùng chung `tailwind-preset.js` và biến CSS giống hệt dự án thật, nên UI thấy trong playground = UI thật khi dùng ở dự án Next.js. Muốn thử theme khác, sửa trực tiếp `playground/src/globals.css`.\n\n> Lưu ý: lần đầu chạy playground cần `npm install` ở cả thư mục gốc (`my-lib/`) lẫn `playground/`, vì `tailwind-preset.js` cần resolve `tailwindcss-animate` từ `node_modules` gốc.\n\n## 4. Test cục bộ trên 1 dự án Next.js khác (trước khi publish)\n\nTrong thư mục `my-lib`:\n\n```bash\nnpm run build\nnpm link\n```\n\nTrong thư mục dự án Next.js muốn dùng thử:\n\n```bash\nnpm link @your-scope/my-lib\n```\n\nGiờ bạn có thể import bình thường:\n\n```tsx\nimport { Button, useDebounce, formatDate, Status } from \"@your-scope/my-lib\";\n```\n\nKhi sửa code trong `my-lib`, chạy lại `npm run build` (hoặc để `npm run dev` chạy nền) là dự án Next.js sẽ nhận code mới ngay (không cần link lại).\n\nĐể gỡ link (unlink) sau khi test xong:\n\n```bash\n# Trong thư mục dự án Next.js\nnpm unlink @your-scope/my-lib\n\n# Trong thư mục my-lib\nnpm unlink\n```\n\n## 5. Publish lên npm\n\n### Cách A — Public package (miễn phí)\n\n```bash\nnpm login\nnpm publish --access public\n```\n\n### Cách B — Private package qua GitHub Packages\n\n1. Đổi tên package trong `package.json` theo format `@github-username/my-lib`\n2. Thêm file `.npmrc` với nội dung:\n   ```\n   @github-username:registry=https://npm.pkg.github.com\n   ```\n3. Login: `npm login --registry=https://npm.pkg.github.com`\n4. Publish: `npm publish`\n\nSau khi publish, các dự án Next.js khác cài bằng:\n\n```bash\nnpm install @your-scope/my-lib\n```\n\n## 6. Đổi tên package trước khi publish\n\nNhớ sửa các chỗ sau trong `package.json`:\n- `\"name\"`: đổi `@your-scope/my-lib` thành tên thật của bạn (vd: `@congty/ui-kit` hoặc `my-shared-lib` nếu không dùng scope)\n- `\"version\"`: bắt đầu từ `0.1.0`, tăng theo semver mỗi lần publish\n\n## 7. Component kiểu shadcn/ui + Tailwind + theme tùy biến\n\nThư viện này build component theo đúng phong cách **shadcn/ui**: dùng `class-variance-authority` (cva) để định nghĩa variants, `@radix-ui/react-slot` cho prop `asChild`, và `clsx` + `tailwind-merge` (qua hàm `cn()`) để gộp class không xung đột.\n\nĐiểm khác biệt so với dùng shadcn CLI truyền thống (copy-paste code): ở đây component được đóng gói sẵn trong thư viện, dự án chỉ cần `npm install` và import, không cần chạy `npx shadcn add` từng component ở mỗi dự án.\n\n### 6.1. Vì sao dùng CSS variables cho màu sắc?\n\nComponent không hard-code màu (`bg-blue-500`...) mà dùng token như `bg-primary`, `text-primary-foreground`. Các token này trỏ tới CSS variables (`--primary`, `--primary-foreground`...). Nhờ vậy, **mỗi dự án dùng thư viện có thể đổi theme hoàn toàn** (màu sắc, bo góc...) chỉ bằng cách định nghĩa lại giá trị CSS variables — không cần sửa code trong `node_modules`.\n\n### 6.2. Setup ở dự án Next.js dùng thư viện\n\n**Bước 1 — Cài đặt:**\n```bash\nnpm install @your-scope/my-lib tailwindcss-animate\n```\n(`tailwindcss-animate` cần cài thêm vì tailwind preset của thư viện dùng plugin này)\n\n**Bước 2 — Dùng chung Tailwind preset của thư viện** (`tailwind.config.js`):\n```js\nconst myLibPreset = require(\"@your-scope/my-lib/tailwind-preset\");\n\nmodule.exports = {\n  presets: [myLibPreset],\n  darkMode: [\"class\"],\n  content: [\n    \"./app/**/*.{js,ts,jsx,tsx}\",\n    \"./components/**/*.{js,ts,jsx,tsx}\",\n    // Quan trọng: quét luôn code đã build của thư viện để Tailwind\n    // nhận diện được các class dùng trong component\n    \"./node_modules/@your-scope/my-lib/dist/**/*.{js,mjs}\",\n  ],\n};\n```\n\n**Bước 3 — Copy giá trị theme mặc định vào `globals.css`:**\n\nLấy nội dung từ `css-vars.example.css` (đi kèm thư viện) dán vào `globals.css` của dự án, sau dòng `@tailwind base;`. Đây là nơi mỗi dự án chỉnh lại màu (`--primary`, `--secondary`, `--radius`...) để có theme riêng mà vẫn dùng chung 1 bộ component.\n\n**Bước 4 — Dùng component:**\n```tsx\nimport { Button } from \"@your-scope/my-lib\";\n\n<Button variant=\"outline\" size=\"sm\">Click me</Button>\n<Button variant=\"destructive\" asChild>\n  <Link href=\"/delete\">Xoá</Link>\n</Button>\n```\n\n### 6.3. Nếu dự án KHÔNG dùng Tailwind preset chung\n\nVẫn dùng được, chỉ cần dự án tự định nghĩa các CSS variables tương ứng (`--primary`, `--background`...) và đảm bảo Tailwind của dự án có các token màu cùng tên (`bg-primary`, `text-foreground`...) — tự cấu hình trong `tailwind.config.js` của dự án thay vì dùng preset có sẵn.\n\n## 8. Cấu trúc thư mục\n\n```\nsrc/\n├── components/   # UI components (Button, ...)\n├── hooks/        # Custom hooks (useDebounce, useLocalStorage, ...)\n├── utils/        # Hàm tiện ích (formatDate, cn, ...)\n├── types/        # TypeScript types/interfaces dùng chung\n├── enums/        # Enums dùng chung\n└── index.ts      # Entry point, export tất cả ra ngoài\n```\n\nThêm code mới: tạo file trong thư mục tương ứng, rồi export ra file `index.ts` của thư mục đó (đã tự động export ra `src/index.ts` tổng nhờ `export *`).\n","readmeFilename":"README.md","_rev":"1-ced3f2cd6f7ef6f53eca824cb6e7e498"}