@sarunyu/system-one 4.9.6 → 4.9.8
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/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/llms.txt +71 -22
- package/package.json +1 -1
package/llms.txt
CHANGED
|
@@ -330,15 +330,13 @@ Single-select dropdown.
|
|
|
330
330
|
options={[
|
|
331
331
|
{ label: "Option A", value: "a" },
|
|
332
332
|
{ label: "Option B", value: "b" },
|
|
333
|
-
{ label: "Disabled", value: "c", disabled: true },
|
|
334
333
|
]}
|
|
335
334
|
value={selected}
|
|
336
335
|
onChange={setSelected}
|
|
337
336
|
/>
|
|
338
337
|
```
|
|
339
338
|
|
|
340
|
-
Props: `placeholder`, `options: { label, value
|
|
341
|
-
`disabled`, `className`.
|
|
339
|
+
Props: `placeholder`, `label`, `required`, `forceState` (`"default"` | `"focus"` | `"error"` | `"disabled"`), `errorMessage`, `helperText`, `options: { label, value }[]`, `value`, `onChange(value)`, `className`. To disable, pass `forceState="disabled"` — there is no top-level `disabled` prop.
|
|
342
340
|
|
|
343
341
|
### DropdownMultiple
|
|
344
342
|
|
|
@@ -348,24 +346,24 @@ Multi-select dropdown with checkboxes.
|
|
|
348
346
|
<DropdownMultiple
|
|
349
347
|
placeholder="Select tags"
|
|
350
348
|
options={tagOptions}
|
|
351
|
-
|
|
349
|
+
value={selected}
|
|
352
350
|
onChange={setSelected}
|
|
353
351
|
/>
|
|
354
352
|
```
|
|
355
353
|
|
|
356
|
-
Props: `placeholder`, `options`, `
|
|
354
|
+
Props: `placeholder`, `label`, `required`, `forceState`, `errorMessage`, `helperText`, `options`, `value: string[]`, `onChange(value: string[])`, `className`. The controlled prop is `value` (not `values`). To disable, use `forceState="disabled"`.
|
|
357
355
|
|
|
358
356
|
---
|
|
359
357
|
|
|
360
358
|
### Checkbox
|
|
361
359
|
|
|
362
360
|
```tsx
|
|
363
|
-
<Checkbox checked={agreed}
|
|
364
|
-
<Checkbox checked="indeterminate"
|
|
365
|
-
<Checkbox checked={sub}
|
|
361
|
+
<Checkbox checked={agreed} onChange={setAgreed} label="I agree to the terms" />
|
|
362
|
+
<Checkbox checked="indeterminate" onChange={setAll} label="Select all" />
|
|
363
|
+
<Checkbox checked={sub} onChange={setSub} label="Subscribe" disabled />
|
|
366
364
|
```
|
|
367
365
|
|
|
368
|
-
`checked` is `boolean | "indeterminate"`. Always provide `label` — don't wrap Checkbox in a `<label>`.
|
|
366
|
+
`checked` is `boolean | "indeterminate"`. `onChange` receives a boolean (`(next: boolean) => void`) — NOT an event, NOT `onCheckedChange`. Always provide `label` — don't wrap Checkbox in a `<label>`.
|
|
369
367
|
|
|
370
368
|
### Toggle
|
|
371
369
|
|
|
@@ -395,11 +393,13 @@ Render a group — use `name` to bind siblings.
|
|
|
395
393
|
|
|
396
394
|
```tsx
|
|
397
395
|
<div className="flex flex-col gap-2">
|
|
398
|
-
<Radio name="plan" value="free" checked={plan === "free"} onChange={setPlan} label="Free" />
|
|
399
|
-
<Radio name="plan" value="pro" checked={plan === "pro"} onChange={setPlan}
|
|
396
|
+
<Radio name="plan" value="free" checked={plan === "free"} onChange={() => setPlan("free")} label="Free" />
|
|
397
|
+
<Radio name="plan" value="pro" checked={plan === "pro"} onChange={() => setPlan("pro")} label="Pro" />
|
|
400
398
|
</div>
|
|
401
399
|
```
|
|
402
400
|
|
|
401
|
+
`onChange` receives a boolean (`(next: boolean) => void`) — fires with `true` when the radio becomes selected. To track which value was picked, ignore the arg and set the value directly: `onChange={() => setPlan("pro")}`.
|
|
402
|
+
|
|
403
403
|
---
|
|
404
404
|
|
|
405
405
|
### Tag
|
|
@@ -412,11 +412,13 @@ type TagVariant = "blue" | "green" | "yellow" | "red" | "gray" | "lime";
|
|
|
412
412
|
|
|
413
413
|
<Tag text="Active" variant="green" />
|
|
414
414
|
<Tag text="Draft" variant="yellow" />
|
|
415
|
-
<Tag text="Filter: Design" close
|
|
415
|
+
<Tag text="Filter: Design" close />
|
|
416
416
|
<Tag text="Small" variant="blue" size="small" />
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
Props: `text`, `variant`, `size` (`"large"` default | `"small"`), `
|
|
419
|
+
Props: `text`, `variant`, `size` (`"large"` default | `"small"`), `state` (`"default"` | `"hover"` | `"disabled"`), `icon` (boolean — leading dot), `close` (boolean — visual × button only), `className`.
|
|
420
|
+
|
|
421
|
+
**Note** — `close` is purely visual; the Tag does not handle dismiss. To make a removable tag, wrap it in a clickable parent (`<button onClick={remove}><Tag close text="…" /></button>`) or use `<DropdownMultiple>`'s built-in removable chips.
|
|
420
422
|
|
|
421
423
|
### StatusTag
|
|
422
424
|
|
|
@@ -674,15 +676,33 @@ Props:
|
|
|
674
676
|
|
|
675
677
|
### DateInput / TimeInput
|
|
676
678
|
|
|
679
|
+
Modes: `"single"` (default) | `"range"`. There is **no `"multiple"` mode**.
|
|
680
|
+
|
|
681
|
+
Each mode uses different value props — single mode uses `value` + `onChange`, range mode uses `dateRange` + `onRangeChange` (or `startTime`/`endTime` + `onStartChange`/`onEndChange` for TimeInput).
|
|
682
|
+
|
|
677
683
|
```tsx
|
|
684
|
+
// DateInput — single
|
|
678
685
|
<DateInput placeholder="Select date" mode="single" value={date} onChange={setDate} />
|
|
679
|
-
<DateInput mode="range" value={range} onChange={setRange} />
|
|
680
|
-
<DateInput mode="multiple" value={dates} onChange={setDates} />
|
|
681
686
|
|
|
687
|
+
// DateInput — range (different prop names!)
|
|
688
|
+
<DateInput mode="range" dateRange={range} onRangeChange={setRange} />
|
|
689
|
+
|
|
690
|
+
// TimeInput — single
|
|
682
691
|
<TimeInput placeholder="Start time" value={time} onChange={setTime} />
|
|
683
|
-
|
|
692
|
+
|
|
693
|
+
// TimeInput — range (different prop names!)
|
|
694
|
+
<TimeInput
|
|
695
|
+
mode="range"
|
|
696
|
+
startTime={start}
|
|
697
|
+
endTime={end}
|
|
698
|
+
onStartChange={setStart}
|
|
699
|
+
onEndChange={setEnd}
|
|
700
|
+
/>
|
|
684
701
|
```
|
|
685
702
|
|
|
703
|
+
DateInput types: `value: Date | undefined`, `dateRange: DateRange` (re-exported from react-day-picker — `{ from?: Date; to?: Date }`).
|
|
704
|
+
TimeInput types: `value: TimeValue` where `TimeValue = { hour: 0–23, minute: 0–59 }`.
|
|
705
|
+
|
|
686
706
|
Always format `"DD MMM YYYY"` for display (system format). Don't mix Thai month names with C.E. year.
|
|
687
707
|
|
|
688
708
|
### OptionList
|
|
@@ -825,13 +845,23 @@ Centered overlay surface for confirmations, richer content, or status alerts.
|
|
|
825
845
|
Caller owns open/close state and supplies the backdrop — `<Modal>` only renders
|
|
826
846
|
the dialog panel.
|
|
827
847
|
|
|
848
|
+
**CRITICAL — choose the correct variant before writing any code:**
|
|
849
|
+
|
|
850
|
+
| Variant | Use when | Has children? |
|
|
851
|
+
|---|---|---|
|
|
852
|
+
| `"dialog"` | Simple confirmation: title + description text + up to 2 buttons. NO custom UI. | No |
|
|
853
|
+
| `"content"` | Any modal with custom content: filters, forms, inputs, lists, date pickers, etc. | Yes — required |
|
|
854
|
+
| `"alert"` | Status moments only: success / warning / danger with icon. | No |
|
|
855
|
+
|
|
856
|
+
**Rule: if the modal contains ANYTHING other than a title + plain description text → use `variant="content"` and pass the body as `children`. Never use `variant="dialog"` with custom UI inside.**
|
|
857
|
+
|
|
828
858
|
```tsx
|
|
829
859
|
type ModalVariant = "dialog" | "content" | "alert";
|
|
830
860
|
type ModalActionLayout = "none" | "single" | "double";
|
|
831
861
|
type ModalResponsive = "mobile" | "desktop";
|
|
832
862
|
type ModalAlertStatus = "warning" | "success" | "danger";
|
|
833
863
|
|
|
834
|
-
// Dialog —
|
|
864
|
+
// Dialog — ONLY for title + plain text + buttons. No custom children.
|
|
835
865
|
<Modal
|
|
836
866
|
variant="dialog"
|
|
837
867
|
actionLayout="double"
|
|
@@ -844,7 +874,24 @@ type ModalAlertStatus = "warning" | "success" | "danger";
|
|
|
844
874
|
onClose={close}
|
|
845
875
|
/>
|
|
846
876
|
|
|
847
|
-
// Content — custom
|
|
877
|
+
// Content — filters, forms, inputs, any custom UI. Always pass children.
|
|
878
|
+
<Modal
|
|
879
|
+
variant="content"
|
|
880
|
+
actionLayout="double"
|
|
881
|
+
title="Filters"
|
|
882
|
+
primaryLabel="Apply"
|
|
883
|
+
secondaryLabel="Reset"
|
|
884
|
+
onPrimaryClick={apply}
|
|
885
|
+
onSecondaryClick={reset}
|
|
886
|
+
onClose={close}
|
|
887
|
+
>
|
|
888
|
+
<div className="flex flex-col gap-4">
|
|
889
|
+
<Dropdown placeholder="Category" ... />
|
|
890
|
+
<DateInput mode="range" ... />
|
|
891
|
+
</div>
|
|
892
|
+
</Modal>
|
|
893
|
+
|
|
894
|
+
// Content desktop — right-aligns action buttons
|
|
848
895
|
<Modal variant="content" actionLayout="single" responsive="desktop" title="Edit profile" onClose={close}>
|
|
849
896
|
<Input placeholder="Name" value={name} onChange={setName} />
|
|
850
897
|
</Modal>
|
|
@@ -866,7 +913,9 @@ type ModalAlertStatus = "warning" | "success" | "danger";
|
|
|
866
913
|
```tsx
|
|
867
914
|
{open ? (
|
|
868
915
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4">
|
|
869
|
-
<Modal variant="
|
|
916
|
+
<Modal variant="content" title="Filters" actionLayout="double" onClose={close}>
|
|
917
|
+
{/* your content */}
|
|
918
|
+
</Modal>
|
|
870
919
|
</div>
|
|
871
920
|
) : null}
|
|
872
921
|
```
|
|
@@ -987,7 +1036,7 @@ export function LoginSheet({
|
|
|
987
1036
|
<div className="flex flex-col gap-4">
|
|
988
1037
|
<Input placeholder="Email" value={email} onChange={setEmail} required />
|
|
989
1038
|
<Input placeholder="Password" value={pw} onChange={setPw} type="password" required />
|
|
990
|
-
<Checkbox checked={remember}
|
|
1039
|
+
<Checkbox checked={remember} onChange={setRmb} label="Remember me" />
|
|
991
1040
|
<Button variant="primary" size="xl" className="w-full" onClick={() => onOpenChange(false)}>
|
|
992
1041
|
Sign in
|
|
993
1042
|
</Button>
|
|
@@ -1109,7 +1158,7 @@ export function LoginPage() {
|
|
|
1109
1158
|
<div className="flex flex-col gap-4">
|
|
1110
1159
|
<Input placeholder="Email" type="email" value={email} onChange={setEmail} required />
|
|
1111
1160
|
<Input placeholder="Password" type="password" value={pw} onChange={setPw} required />
|
|
1112
|
-
<Checkbox checked={remember}
|
|
1161
|
+
<Checkbox checked={remember} onChange={setRmb} label="Remember me" />
|
|
1113
1162
|
</div>
|
|
1114
1163
|
<Button variant="primary" size="lg" className="w-full">Sign in</Button>
|
|
1115
1164
|
<p className="text-center text-sm text-muted-foreground">
|
|
@@ -1199,7 +1248,7 @@ export function SettingsPage() {
|
|
|
1199
1248
|
<DropdownMultiple
|
|
1200
1249
|
placeholder="Interests"
|
|
1201
1250
|
options={interests}
|
|
1202
|
-
|
|
1251
|
+
value={picked}
|
|
1203
1252
|
onChange={setPicked}
|
|
1204
1253
|
/>
|
|
1205
1254
|
<DateInput placeholder="Birthday" mode="single" value={dob} onChange={setDob} />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sarunyu/system-one",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.8",
|
|
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": [
|