@sarunyu/system-one 4.9.36 → 4.9.39

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/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sarunyu/system-one — AI usage guide
2
2
 
3
- React component library. Tailwind CSS v4 + CSS custom properties. 30 components.
3
+ React component library. Tailwind CSS v4 + CSS custom properties. 35 components.
4
4
  Built for AI-powered UI generation (v0, Lovable, Figma Make, Cursor).
5
5
 
6
6
  **This file is the contract.** Read it top-to-bottom before generating any screen
@@ -12,14 +12,16 @@ that uses this library. The rules are non-negotiable.
12
12
 
13
13
  1. **Use library components for every element it provides.** Never recreate
14
14
  Button, Input, Tag, Dropdown, Card, Tab, Checkbox, Toggle, Radio, DateInput, TimeInput,
15
- Table, SearchInput, TextArea, Chip, Modal, BottomSheet, Alert, Toast, Notification, Badge, Avatar, AvatarStack, Breadcrumb, Pagination, PaginationBanner, PaginationCarousel, Tooltip, Popover as raw HTML.
15
+ Table, SearchInput, TextArea, Chip, Modal, BottomSheet, Alert, Toast, Notification, Badge, Avatar, AvatarStack, Breadcrumb, Pagination, PaginationBanner, PaginationCarousel, Tooltip, Popover,
16
+ Slider, UploadArea, UploadItem, List, ListItem as raw HTML.
16
17
  2. **Never override a component's built-in styles.** Every library component manages its own colors, shadows, padding, radius, and typography internally. Only use `className` on library components for **layout** (`w-*`, `max-w-*`, `flex`, `grid`, `gap-*`, `m-*`, `col-span-*`). Never pass `bg-*`, `shadow-*`, `text-*`, `p-*`, `rounded-*`, or `border-*` in `className` on a library component.
17
18
  3. **Use design-token classes for color and typography.** Never `text-blue-600`,
18
19
  `bg-gray-100`, `text-[#3b82f6]`. The token table below is exhaustive — if a
19
20
  color you need is not in it, use `text-foreground` / `bg-card`.
20
21
  4. **Use `@phosphor-icons/react` for all icons.** Never import from `lucide-react`
21
22
  or any other icon library. Install with `npm install @phosphor-icons/react`.
22
- Import named icons: `import { House, MagnifyingGlass, CaretDown } from "@phosphor-icons/react"`.
23
+ Import named icons: `import { HouseIcon, MagnifyingGlassIcon, CaretDownIcon } from "@phosphor-icons/react"`.
24
+ All icon names use the `Icon` suffix (e.g. `StarIcon`, `TrashIcon`, `ArrowUpIcon`). The un-suffixed names are deprecated.
23
25
  Every icon accepts `size`, `weight` (`"regular"` | `"fill"` — no other weights), and `className` props.
24
26
  5. **No arbitrary bracket values for spacing / sizing / typography.**
25
27
  Use scale utilities, not pixel overrides. The library's shipped stylesheet
@@ -153,6 +155,8 @@ import {
153
155
  Dropdown, DropdownMultiple, OptionList,
154
156
  Checkbox, Toggle, Radio,
155
157
  DateInput, TimeInput,
158
+ Slider,
159
+ UploadArea, UploadItem,
156
160
  // Navigation
157
161
  Breadcrumb,
158
162
  Pagination, PaginationBanner, PaginationCarousel,
@@ -162,6 +166,7 @@ import {
162
166
  Tab, TabGroup,
163
167
  Card,
164
168
  Table, TableRow, TableHeaderCell, TableCell,
169
+ LinearProgress, CircleProgress,
165
170
  // Feedback
166
171
  Alert,
167
172
  Toast, Toaster,
@@ -169,6 +174,8 @@ import {
169
174
  Badge,
170
175
  // Overlay
171
176
  Modal, BottomSheet, Tooltip, Popover,
177
+ // List
178
+ List, ListItem,
172
179
  // Utility
173
180
  cn, useIsMobile,
174
181
  } from "@sarunyu/system-one";
@@ -1124,6 +1131,24 @@ type BottomSheetRightSide = "icon" | "action" | "none";
1124
1131
  - `rightSide="action"` — inline text button (e.g., "Save"), fires `onActionClick`.
1125
1132
  - `rightSide="none"` — nothing on the right.
1126
1133
 
1134
+ **CRITICAL — lazy-mount pattern (always required):** Vaul mounts its portal
1135
+ immediately when `<BottomSheet>` enters the React tree, even with `open={false}`.
1136
+ Always guard with an `everOpened` flag so it never mounts until first opened.
1137
+
1138
+ ```tsx
1139
+ // button-triggered
1140
+ const [everOpened, setEverOpened] = useState(false);
1141
+ const [open, setOpen] = useState(false);
1142
+ <Button onClick={() => { setEverOpened(true); setOpen(true); }}>Open</Button>
1143
+ {everOpened && <BottomSheet open={open} onOpenChange={setOpen}>…</BottomSheet>}
1144
+
1145
+ // prop-driven (open comes from parent)
1146
+ const [everOpened, setEverOpened] = useState(false);
1147
+ useEffect(() => { if (open) setEverOpened(true); }, [open]);
1148
+ if (!everOpened) return null;
1149
+ return <BottomSheet open={open} onOpenChange={…}>…</BottomSheet>;
1150
+ ```
1151
+
1127
1152
  `showHandle` (default `true`) shows the grab handle at the top. Hide it only
1128
1153
  when you want a fully formal surface. Do not render a `<BottomSheet>` on desktop
1129
1154
  layouts — use `<Modal>` instead.
@@ -1141,7 +1166,7 @@ Dark-bubble contextual hint shown on hover over any trigger element.
1141
1166
 
1142
1167
  ```tsx
1143
1168
  <Tooltip content="Delete this item">
1144
- <Button variant="outline" size="icon-md" aria-label="Delete"><Trash size={16} /></Button>
1169
+ <Button variant="outline" size="icon-md" aria-label="Delete"><TrashIcon size={16} /></Button>
1145
1170
  </Tooltip>
1146
1171
 
1147
1172
  <Tooltip content="This field is required" side="right" align="start">
@@ -1192,7 +1217,7 @@ Floating panel that opens on **click** over any trigger element. Pass the trigge
1192
1217
  </div>
1193
1218
  }
1194
1219
  >
1195
- <Button variant="outline" size="md" leftIcon={<User size={16} />}>Account</Button>
1220
+ <Button variant="outline" size="md" leftIcon={<UserIcon size={16} />}>Account</Button>
1196
1221
  </Popover>
1197
1222
  ```
1198
1223
 
@@ -1208,6 +1233,102 @@ Props:
1208
1233
 
1209
1234
  ---
1210
1235
 
1236
+ ### Slider
1237
+
1238
+ Range input with a draggable thumb. Three track heights, single-thumb or two-thumb range. Never use `<input type="range">` directly.
1239
+
1240
+ ```tsx
1241
+ // Single thumb (uncontrolled default = 50)
1242
+ const [value, setValue] = useState(50)
1243
+ <Slider value={value} onChange={setValue} />
1244
+ <Slider size="sm" value={value} onChange={setValue} />
1245
+ <Slider size="lg" showSteps value={value} onChange={setValue} />
1246
+ <Slider disabled value={value} onChange={setValue} />
1247
+
1248
+ // Two-thumb range
1249
+ const [range, setRange] = useState<[number, number]>([25, 75])
1250
+ <Slider type="range" rangeValue={range} onRangeChange={setRange} />
1251
+ <Slider type="range" size="lg" showSteps rangeValue={range} onRangeChange={setRange} />
1252
+ ```
1253
+
1254
+ Props: `size` (`"sm"` 4px | `"md"` 8px default | `"lg"` 12px), `type` (`"single"` default | `"range"`), `disabled`, `showSteps` (labels at 0 / 25 / 50 / 75 / 100), `min` (default 0), `max` (default 100), `step` (default 1), `value` (controlled single), `rangeValue` (controlled range `[start, end]`), `defaultValue` (50), `defaultRangeValue` ([25, 75]), `onChange(value)`, `onRangeChange([start, end])`, `className`.
1255
+
1256
+ ---
1257
+
1258
+ ### LinearProgress / CircleProgress
1259
+
1260
+ Deterministic progress indicators. `value` is 0–100 (clamped automatically). Never use `<progress>` HTML element.
1261
+
1262
+ ```tsx
1263
+ // Linear bar — typically full-width or constrained by parent
1264
+ <LinearProgress value={65} />
1265
+ <LinearProgress value={30} className="w-64" />
1266
+
1267
+ // Circle — three sizes
1268
+ <CircleProgress value={75} /> // lg (128 px) — default, shows % label
1269
+ <CircleProgress value={50} size="md" /> // md (48 px) — shows % label
1270
+ <CircleProgress value={30} size="sm" /> // sm (24 px) — arc only, no label
1271
+ ```
1272
+
1273
+ Props `LinearProgress`: `value`, `className`.
1274
+ Props `CircleProgress`: `value`, `size` (`"sm"` | `"md"` | `"lg"` default), `className`.
1275
+
1276
+ `lg` and `md` sizes display a centred percentage label; `sm` is arc-only. The arc uses a brand cyan-to-navy gradient. At `value={0}`, `lg`/`md` show a placeholder-coloured `0%` label.
1277
+
1278
+ ---
1279
+
1280
+ ### UploadArea / UploadItem
1281
+
1282
+ Upload dropzone and per-file status rows. Never hand-roll dashed upload areas or custom file progress rows.
1283
+
1284
+ ```tsx
1285
+ // Dropzone — wire onClick to open a file input
1286
+ const inputRef = useRef<HTMLInputElement>(null)
1287
+ <UploadArea onClick={() => inputRef.current?.click()} />
1288
+ <UploadArea disabled />
1289
+ <UploadArea label="Browse files" onClick={() => inputRef.current?.click()} />
1290
+
1291
+ // Text variant — compact row, no card border
1292
+ <UploadItem variant="text" status="loading" fileName="report.pdf" progress={42} />
1293
+ <UploadItem variant="text" status="success" fileName="report.pdf" onDelete={handleDelete} />
1294
+ <UploadItem variant="text" status="error" fileName="report.pdf" errorText="File too large" onDelete={handleDelete} />
1295
+
1296
+ // Card variant — bordered card with file-size column and separate delete button
1297
+ <UploadItem variant="card" status="loading" fileName="photo.png" fileSize="1.66KB" progress={42} />
1298
+ <UploadItem variant="card" status="success" fileName="photo.png" fileSize="1.66KB" onDelete={handleDelete} />
1299
+ <UploadItem variant="card" status="error" fileName="photo.png" fileSize="1.66KB" errorText="Upload failed" onDelete={handleDelete} />
1300
+ ```
1301
+
1302
+ Props `UploadArea`: `disabled`, `label` (default `"อัปโหลดไฟล์"`), plus all native `<div>` props (`onClick`, `className`, etc.).
1303
+ Props `UploadItem`: `variant` (`"text"` default | `"card"`), `status` (`"loading"` | `"success"` | `"error"`, default `"loading"`), `fileName`, `fileSize` (card variant — e.g. `"1.66KB"`), `errorText`, `progress` (0–100, shown while `status="loading"`), `onDelete`, `className`.
1304
+
1305
+ ---
1306
+
1307
+ ### List / ListItem
1308
+
1309
+ Vertical list of rows. Each row has a label, optional leading/trailing slot, and optional action link or click handler. Never hand-roll `<ul>` / `<li>` rows for this pattern.
1310
+
1311
+ ```tsx
1312
+ <List>
1313
+ <ListItem label="Settings" />
1314
+ <ListItem label="Profile" leading={<UserIcon size={24} />} />
1315
+ <ListItem label="Documents" trailing={<CaretRightIcon size={24} />} onClick={() => navigate("/docs")} />
1316
+ <ListItem label="Billing" action="Manage" onAction={openBilling} />
1317
+ <ListItem label="Disabled row" highlighted />
1318
+ </List>
1319
+ ```
1320
+
1321
+ **Slots:**
1322
+ - `leading` — left slot (icon, avatar, image). Rendered at 24×24 with `text-icon-default`.
1323
+ - `trailing` — right slot (icon, chevron, badge). Same sizing.
1324
+ - `action` — string label for an inline text-button on the right (e.g. "Edit", "View all"). Fires `onAction`.
1325
+ - `onClick` — makes the whole row tappable. Press state (gray background) is applied automatically.
1326
+
1327
+ Props `ListItem`: `label` (required), `leading?`, `trailing?`, `action?`, `onAction?(e)`, `highlighted?`, `onClick?`, `className`.
1328
+ Props `List`: `children`, `className`.
1329
+
1330
+ ---
1331
+
1211
1332
  ### Modal vs BottomSheet — responsive rule
1212
1333
 
1213
1334
  **On mobile (< 768px), content-heavy / action-heavy modals MUST render as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarunyu/system-one",
3
- "version": "4.9.36",
3
+ "version": "4.9.39",
4
4
  "type": "module",
5
5
  "description": "A production-ready React design system built for AI-powered web generation tools (Figma Make, Lovable, V0). Tailwind CSS v4 + CSS custom properties for full theming support.",
6
6
  "keywords": [