@stmjsc/ui
v1.0.6
Published
STM design system — shadcn/ui custom theo thiết kế STM + component dùng chung (HIS/EMR/RIS)
Downloads
1,061
Readme
@stmjsc/ui
Thư viện component React + hệ thống thiết kế (design system) dựng trên shadcn/ui + Radix UI + Tailwind CSS, tối ưu cho ứng dụng quản trị/nghiệp vụ tiếng Việt: viết tắt tên kiểu Việt Nam, DatePicker dd/MM/yyyy, tìm kiếm không dấu, font subset tiếng Việt, chủ đề xanh y tế với sáng/tối.
Gồm 3 lớp: primitives (shadcn đã custom style), composites (bảng dữ liệu, phân trang, thanh lọc, form, trạng thái…) và app shell (header, sidebar, chuyển module, menu người dùng). Import và dùng ngay — không cần chép code.
- ESM, tree-shakeable, giữ
"use client"per-module (component tĩnh dùng được trong RSC) - Token hoá bằng CSS variables → đổi màu theo thương hiệu mà không cần rebuild
Yêu cầu
| Gói | Yêu cầu | Ghi chú |
|---|---|---|
| React | >=19 | bắt buộc |
| Tailwind CSS | 3.4.x | dùng preset của package |
| Next.js | >=15 (tùy chọn) | chỉ cần cho @stmjsc/ui/shell (dùng next/link, next/navigation). Primitives và composites chạy trong bất kỳ app React 19 nào (Vite, Remix, Next…). |
Sàn
Next.js >=15là vì đây là bản Next đầu tiên hỗ trợ React 19 — không phải bắt buộc đúng bản 15. Các bản mới hơn (16+) dùng bình thường; peer dependency đã khainext: ">=15".
Cài đặt & cấu hình (Next.js ≥15 + Tailwind 3.4)
pnpm add @stmjsc/ui lucide-react// tailwind.config.ts — BẮT BUỘC 2 dòng: preset + content quét class trong package
const config: Config = {
presets: [require("@stmjsc/ui/preset")],
content: [
"./app/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./node_modules/@stmjsc/ui/dist/**/*.{js,mjs}"
]
};App vẫn giữ globals.css với 3 directive Tailwind như mặc định (bắt buộc — không có thì Tailwind không sinh utility cho component):
/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;// app/layout.tsx — import globals + CSS tokens của package 1 lần + tự khai báo font (package chỉ dùng biến)
import "./globals.css";
import "@stmjsc/ui/styles.css";
import { Inter, JetBrains_Mono } from "next/font/google";
const inter = Inter({ subsets: ["latin", "vietnamese"], variable: "--font-sans" });
const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-mono" });
// <html lang="vi" className={`${inter.variable} ${mono.variable}`}>Entry points
| Import | Nội dung |
|---|---|
| @stmjsc/ui | Primitives (27 component shadcn-custom) + composites (17) + cn/helpers + tokens TS |
| @stmjsc/ui/shell | App shell (AppShell, AppHeader, Sidebar, ModuleSwitcher, UserMenu, ThemeToggle/ThemeProvider, MobileMenu, ShellProvider/useShell) — cần next (optional peer) |
| @stmjsc/ui/preset | Tailwind 3 preset (tokens semantic + status + header/sidebar/brand, radius, font, keyframes) |
| @stmjsc/ui/styles.css | CSS variables light/dark + base layer (focus ring, scrollbar) + utilities |
| @stmjsc/ui/tokens | Token dạng TS object (dùng cho chart/canvas/email/PDF) |
Ví dụ
import { Button, DataTableContainer, SearchFilterBar, StatusPill } from "@stmjsc/ui";// App shell — dữ liệu điều hướng (zones/menus) do ứng dụng tự khai báo
"use client";
import { AppShell } from "@stmjsc/ui/shell";
<AppShell
product="APP"
zones={zones}
menus={sidebarMenus}
user={user}
roleLabels={{ admin: "Quản trị viên", staff: "Nhân viên" }}
onLogout={logout}>
{children}
</AppShell>Quy ước
- Không hardcode mã màu hex — dùng token:
bg-success/15 text-success,text-warning,bg-info/15,status-purple,status-teal,bg-header,sidebar-*, brand (cyan-mint,green-accent…). Token trạng thái tự đổi sắc theo dark mode. - KHÔNG dùng opacity modifier trên
border/input(border-border/50sẽ sai — dark mode nhúng alpha trong biến). - Trạng thái nghiệp vụ: khai báo
StatusConfig<T>[]trong ứng dụng — package cung cấp cơ chế +StatusPill/StatusFilterChips/getBadgeClasses/getRowStatusClasses. - Kích thước chuẩn: control
h-10, form compacth-9, control trong bảng/phân trangh-8; ngàydd/MM/yyyy, giờ 24h; icon dùng Lucide. useColumnVisibilityvớionLayoutChange→ ứng dụng tự lưu cấu hình cột theo người dùng (server); không truyền thì tự lưu localStorage theostorageKey.
DatePicker & DateRangePicker
// Ràng buộc quá khứ/tương lai (kết hợp được với minDate/maxDate — lấy khoảng hẹp hơn)
<DatePicker value={dob} onChange={setDob} disableFuture />
<DatePicker value={apptDate} onChange={setApptDate} disablePast />
<DateRangePicker value={range} onChange={setRange} /> // {from, to}DateRangePicker: click 1 = từ ngày, click 2 = đến ngày (click trước "từ" bắt đầu lại); gõ tay dd/MM/yyyy - dd/MM/yyyy; ngày trong khoảng highlight bg-primary/15. disableFuture/disablePast thu hẹp cả danh sách năm và vô hiệu nút "Hôm nay" khi hôm nay ngoài khoảng cho phép.
Phát triển
pnpm install
pnpm typecheck # tsc strict + noUncheckedIndexedAccess
pnpm lint # biome
pnpm build # tsup → dist/ (ESM per-file, giữ "use client" per-module, kèm .d.ts)Publish package: xem PUBLISHING.md.
License
MIT © STM JSC
