@usevyre/ai-context 1.0.1 → 1.2.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.
@@ -1,5 +1,5 @@
1
1
  # useVyre Copilot Instructions
2
- # Version: 1.0.0
2
+ # Version: 1.2.0
3
3
 
4
4
  When generating UI code in this project, follow the useVyre design system rules below.
5
5
 
@@ -310,7 +310,7 @@ import { Button } from "@usevyre/react"
310
310
 
311
311
  ### Calendar
312
312
 
313
- Date picker calendar widget for selecting single dates or ranges.
313
+ Inline date-grid widget (always visible, no input). mode: single | range | multiple, optional time picker. For an input + popover use DatePicker; for start/end ranges with presets use DateRangePicker.
314
314
 
315
315
  ```tsx
316
316
  import { Calendar } from "@usevyre/react"
@@ -319,6 +319,7 @@ import { Calendar } from "@usevyre/react"
319
319
  // value = Date | null
320
320
  // onChange = function
321
321
  // disabled = boolean (default: false)
322
+ // defaultMonth = Date
322
323
 
323
324
  // Examples:
324
325
  const [date, setDate] = useState(null);
@@ -326,7 +327,39 @@ const [date, setDate] = useState(null);
326
327
  ```
327
328
 
328
329
  **Common mistakes:**
329
- - ❌ `Using Calendar for time selection` Combine with a separate time Input if time selection is needed
330
+ - ❌ `Calendar for an input field that opens a popover` Use <DatePicker /> (single date) or <DateRangePicker /> (range)
331
+ - ❌ `value as tuple for mode="single"` → Pass value matching mode; use mode="range" for [start,end]
332
+
333
+ ---
334
+
335
+ ### DatePicker
336
+
337
+ Input trigger that opens a Calendar in a popover. Same modes as Calendar (single | range | multiple) plus a placeholder. Use this for a compact date field; use Calendar for an always-visible grid, or DateRangePicker for start/end ranges with presets.
338
+
339
+ ```tsx
340
+ import { DatePicker } from "@usevyre/react"
341
+
342
+ // Props:
343
+ // value = Date | [Date, Date] | Date[] | null
344
+ // onChange = function
345
+ // mode = "single" | "range" | "multiple" (default: single)
346
+ // placeholder = string (default: Pick a date)
347
+ // showTime = boolean (default: false)
348
+ // minDate = Date
349
+ // maxDate = Date
350
+ // disabled = function
351
+ // weekStartsOn = "0" | "1" (default: 1)
352
+ // inputClassName = string
353
+
354
+ // Examples:
355
+ const [date, setDate] = useState(null);
356
+ <DatePicker value={date} onChange={setDate} placeholder="Pick a date" />
357
+ <DatePicker value={date} onChange={setDate} showTime />
358
+ ```
359
+
360
+ **Common mistakes:**
361
+ - ❌ `DatePicker mode="range" for { from, to } object` → Use <DateRangePicker /> for the { from, to } object API + presets + dual month
362
+ - ❌ `DatePicker without value/onChange` → Provide value and onChange (e.g. from useState)
330
363
 
331
364
  ---
332
365
 
@@ -386,6 +419,78 @@ import { Checkbox } from "@usevyre/react"
386
419
 
387
420
  ---
388
421
 
422
+ ### RadioGroup
423
+
424
+ Controlled single-choice group. RadioGroup owns the selected value; render it data-driven via the options array OR with composable <Radio> children for custom content. role=radiogroup with proper labelling. For multi-select use Checkbox; for a compact dropdown use Select.
425
+
426
+ ```tsx
427
+ import { RadioGroup, Radio } from "@usevyre/react"
428
+
429
+ // Props:
430
+ // value = string
431
+ // defaultValue = string
432
+ // onChange = function
433
+ // name = string
434
+ // disabled = boolean (default: false)
435
+ // size = "sm" | "md" (default: md)
436
+ // orientation = "vertical" | "horizontal" (default: vertical)
437
+ // options = { value: string; label?: string; description?: string; disabled?: boolean }[]
438
+
439
+ // Examples:
440
+ <RadioGroup
441
+ value={plan}
442
+ onChange={setPlan}
443
+ options={[
444
+ { value: "free", label: "Free", description: "For hobby projects" },
445
+ { value: "pro", label: "Pro", description: "For teams" },
446
+ ]}
447
+ />
448
+ <RadioGroup value={plan} onChange={setPlan} orientation="horizontal">
449
+ <Radio value="free" label="Free" />
450
+ <Radio value="pro" label="Pro" />
451
+ </RadioGroup>
452
+ ```
453
+
454
+ **Common mistakes:**
455
+ - ❌ `<Radio> used outside a <RadioGroup>` → Always wrap <Radio> in <RadioGroup>
456
+ - ❌ `RadioGroup without value/onChange (React) or v-model (Vue)` → Bind value + onChange (React) or v-model (Vue); or defaultValue for uncontrolled in React
457
+ - ❌ `Using Checkbox for mutually-exclusive choices` → Use RadioGroup + Radio (or options) for one-of-many
458
+
459
+ ---
460
+
461
+ ### RichTextEditor
462
+
463
+ Controlled WYSIWYG editor. value is an HTML string; you own it in state and set it in onChange (React) / v-model (Vue). Native contentEditable + execCommand — zero dependencies. Toolbar: bold, italic, underline, strike, h1-h3, ordered/unordered lists, quote, code block, link, clear formatting.
464
+
465
+ ```tsx
466
+ import { RichTextEditor } from "@usevyre/react"
467
+
468
+ // Props:
469
+ // value = string
470
+ // onChange = function
471
+ // placeholder = string (default: Write something…)
472
+ // disabled = boolean (default: false)
473
+ // readOnly = boolean (default: false)
474
+ // toolbar = RichTextTool[]
475
+ // minHeight = string (default: 10rem)
476
+
477
+ // Examples:
478
+ const [html, setHtml] = useState("<p>Hello <strong>world</strong></p>");
479
+ <RichTextEditor value={html} onChange={setHtml} placeholder="Write…" />
480
+ <RichTextEditor
481
+ value={html}
482
+ onChange={setHtml}
483
+ toolbar={["bold", "italic", "link"]}
484
+ />
485
+ ```
486
+
487
+ **Common mistakes:**
488
+ - ❌ `RichTextEditor without value/onChange (React) or v-model (Vue)` → Keep the HTML string in state and update it in onChange / v-model
489
+ - ❌ `Rendering value as text or with dangerouslySetInnerHTML elsewhere without sanitising` → Sanitise (e.g. DOMPurify) before re-rendering untrusted RTE output
490
+ - ❌ `toolbar="bold" (string)` → Pass an array, e.g. toolbar={["bold","italic","link"]}
491
+
492
+ ---
493
+
389
494
  ### Command
390
495
 
391
496
  Command palette / search dialog. Use for search-first navigation or quick actions.
@@ -437,7 +542,7 @@ import { DropdownMenu, DropdownItem, DropdownSeparator, DropdownCheckboxItem, Dr
437
542
 
438
543
  ### Field
439
544
 
440
- Form field wrapper providing label, hint text, and validation state for Input or Textarea.
545
+ Form field wrapper. Two ways to use it (both supported): (1) props-based — pass label/hint/state/required for the common case; (2) composable — use the parts FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet for richer layouts (multiple controls, custom error placement). The props-based API is unchanged and still works.
441
546
 
442
547
  ```tsx
443
548
  import { Field, Input, Textarea } from "@usevyre/react"
@@ -455,10 +560,23 @@ import { Field, Input, Textarea } from "@usevyre/react"
455
560
  <Field label="Search">
456
561
  <Input leftElement={<SearchIcon />} placeholder="Search..." />
457
562
  </Field>
563
+ <Field>
564
+ <FieldLabel required htmlFor="email">Email</FieldLabel>
565
+ <Input id="email" type="email" />
566
+ <FieldDescription>We\u2019ll never share it.</FieldDescription>
567
+ <FieldError>{errors.email}</FieldError>
568
+ </Field>
569
+
570
+ // Two controls side by side
571
+ <FieldGroup orientation="horizontal">
572
+ <Field label="First name"><Input /></Field>
573
+ <Field label="Last name"><Input /></Field>
574
+ </FieldGroup>
458
575
  ```
459
576
 
460
577
  **Common mistakes:**
461
578
  - ❌ `Applying state prop directly to Input` → Wrap Input in <Field state="error"> to apply validation styling
579
+ - ❌ `Mixing props label/hint AND FieldLabel/FieldError for the same field` → Pick one: either props-based (label/hint/state) OR composable parts
462
580
 
463
581
  ---
464
582
 
@@ -470,6 +588,7 @@ Text input field. Wrap in Field for labels and validation. Use leftElement/right
470
588
  import { Input } from "@usevyre/react"
471
589
 
472
590
  // Props:
591
+ // modelValue = string | number
473
592
  // size = "sm" | "md" | "lg" (default: md)
474
593
  // leftElement = ReactNode
475
594
  // rightElement = ReactNode
@@ -481,6 +600,7 @@ import { Input } from "@usevyre/react"
481
600
  **Common mistakes:**
482
601
  - ❌ `size="icon"` → Use size="sm" | "md" | "lg"
483
602
  - ❌ `type="search" for search UI` → Import Command from @usevyre/react for search palettes
603
+ - ❌ `Vue: binding Input/Textarea value without v-model` → Use v-model on <Input>/<Textarea> in Vue; in React use value + onChange
484
604
 
485
605
  ---
486
606
 
@@ -886,6 +1006,362 @@ import { Text, Heading, Lead, Code, Blockquote } from "@usevyre/react"
886
1006
 
887
1007
  ---
888
1008
 
1009
+ ### ButtonGroup
1010
+
1011
+ Groups multiple Button components into one visual unit (toolbar, segmented control). Pure layout — no internal state.
1012
+
1013
+ ```tsx
1014
+ import { ButtonGroup, Button } from "@usevyre/react"
1015
+
1016
+ // Props:
1017
+ // orientation = "horizontal" | "vertical" (default: horizontal)
1018
+ // attached = boolean (default: false)
1019
+ // size = "sm" | "md" | "lg" | "icon"
1020
+
1021
+ // Examples:
1022
+ <ButtonGroup attached>
1023
+ <Button variant="secondary">Day</Button>
1024
+ <Button variant="secondary">Week</Button>
1025
+ <Button variant="secondary">Month</Button>
1026
+ </ButtonGroup>
1027
+ <ButtonGroup orientation="vertical" attached>
1028
+ <Button variant="secondary">Top</Button>
1029
+ <Button variant="secondary">Bottom</Button>
1030
+ </ButtonGroup>
1031
+ ```
1032
+
1033
+ **Common mistakes:**
1034
+ - ❌ `ButtonGroup variant="..."` → Set variant on each <Button> inside the group
1035
+ - ❌ `ButtonGroup without Button children` → Place <Button> elements as direct children
1036
+
1037
+ ---
1038
+
1039
+ ### TagsInput
1040
+
1041
+ Multi-tag input. Type and press Enter or comma to add a tag, click x to remove, Backspace on empty input removes the last tag. Controlled.
1042
+
1043
+ ```tsx
1044
+ import { TagsInput } from "@usevyre/react"
1045
+
1046
+ // Props:
1047
+ // value = string[]
1048
+ // onChange = (tags: string[]) => void
1049
+ // placeholder = string
1050
+ // disabled = boolean (default: false)
1051
+ // max = number
1052
+ // size = "sm" | "md" | "lg" (default: md)
1053
+
1054
+ // Examples:
1055
+ const [tags, setTags] = useState<string[]>([]);
1056
+ <TagsInput value={tags} onChange={setTags} placeholder="Add a tag…" />
1057
+ <TagsInput value={tags} onChange={setTags} max={5} />
1058
+ ```
1059
+
1060
+ **Common mistakes:**
1061
+ - ❌ `TagsInput value={string}` → Pass an array: value={['react','vue']}
1062
+ - ❌ `TagsInput without onChange` → Provide value and onChange (React) or v-model (Vue)
1063
+
1064
+ ---
1065
+
1066
+ ### Combobox
1067
+
1068
+ Searchable single-select dropdown with typeahead filtering and keyboard navigation. Use when the list is long enough to need search. Differs from Select (no search) and Command (palette).
1069
+
1070
+ ```tsx
1071
+ import { Combobox } from "@usevyre/react"
1072
+
1073
+ // Props:
1074
+ // options = { value: string; label: string; disabled?: boolean }[]
1075
+ // value = string | null
1076
+ // onChange = (value: string | null) => void
1077
+ // placeholder = string (default: "Search…")
1078
+ // disabled = boolean (default: false)
1079
+ // size = "sm" | "md" | "lg" (default: md)
1080
+ // emptyText = string (default: "No results")
1081
+
1082
+ // Examples:
1083
+ const [lang, setLang] = useState<string | null>(null);
1084
+ <Combobox
1085
+ options={[{ value: "ts", label: "TypeScript" }, { value: "go", label: "Go" }]}
1086
+ value={lang}
1087
+ onChange={setLang}
1088
+ placeholder="Search language…"
1089
+ />
1090
+ ```
1091
+
1092
+ **Common mistakes:**
1093
+ - ❌ `Combobox value=""` → Use value={null} for no selection
1094
+ - ❌ `Combobox options={string[]}` → Use [{ value: 'ts', label: 'TypeScript' }]
1095
+ - ❌ `Using Combobox for command palette` → Use Command for command palettes
1096
+
1097
+ ---
1098
+
1099
+ ### DataGrid
1100
+
1101
+ Table with built-in column sorting, loading skeletons, and empty state. Filtering and pagination are out of scope — compose with the Pagination component.
1102
+
1103
+ ```tsx
1104
+ import { DataGrid } from "@usevyre/react"
1105
+
1106
+ // Props:
1107
+ // columns = { key: string; label: string; sortable?: boolean; width?: string }[]
1108
+ // rows = Record<string, unknown>[]
1109
+ // sortKey = string
1110
+ // sortDir = "asc" | "desc"
1111
+ // onSort = (key: string, dir: 'asc' | 'desc') => void
1112
+ // loading = boolean (default: false)
1113
+ // emptyText = string (default: "No data")
1114
+ // stickyHeader = boolean (default: false)
1115
+
1116
+ // Examples:
1117
+ const cols = [{ key: "name", label: "Name", sortable: true }];
1118
+ <DataGrid
1119
+ columns={cols}
1120
+ rows={people}
1121
+ sortKey={sortKey}
1122
+ sortDir={sortDir}
1123
+ onSort={(k, d) => { setSortKey(k); setSortDir(d); }}
1124
+ />
1125
+ <DataGrid columns={cols} rows={[]} loading />
1126
+ ```
1127
+
1128
+ **Common mistakes:**
1129
+ - ❌ `DataGrid expecting built-in pagination` → Slice rows yourself and use the Pagination component
1130
+ - ❌ `DataGrid expecting built-in filtering` → Filter the rows array before passing it in
1131
+ - ❌ `sortable without onSort` → Handle onSort and sort the rows array in your state
1132
+
1133
+ ---
1134
+
1135
+ ### Tag
1136
+
1137
+ Standalone display tag/chip for categories, labels, or filter chips. NOT an input — for tag input use TagsInput. Group multiple with TagGroup.
1138
+
1139
+ ```tsx
1140
+ import { Tag } from "@usevyre/react"
1141
+
1142
+ // Props:
1143
+ // variant = "default" | "accent" | "danger" (default: default)
1144
+ // size = "sm" | "md" | "lg" (default: md)
1145
+ // onRemove = () => void
1146
+ // onClick = () => void
1147
+ // disabled = boolean (default: false)
1148
+
1149
+ // Examples:
1150
+ <TagGroup>
1151
+ <Tag>Design</Tag>
1152
+ <Tag variant="accent">Featured</Tag>
1153
+ <Tag>Engineering</Tag>
1154
+ </TagGroup>
1155
+ <Tag onRemove={() => removeFilter("react")}>react</Tag>
1156
+ <Tag onClick={() => toggleFilter("vue")}>vue</Tag>
1157
+ ```
1158
+
1159
+ **Common mistakes:**
1160
+ - ❌ `Tag variant="success"` → Use Badge for success/warning/teal status colors; Tag is for categories/filters
1161
+ - ❌ `Using Tag for tag input` → Use TagsInput for adding/removing tags via keyboard
1162
+ - ❌ `Tag size="xl"` → Use size="lg"
1163
+
1164
+ ---
1165
+
1166
+ ### TagGroup
1167
+
1168
+ Read-only container that lays out multiple Tag elements with automatic wrapping and consistent spacing. For tag input use TagsInput.
1169
+
1170
+ ```tsx
1171
+ import { TagGroup, Tag } from "@usevyre/react"
1172
+
1173
+ // Props:
1174
+ // gap = "sm" | "md" | "lg" (default: md)
1175
+ // wrap = boolean (default: true)
1176
+
1177
+ // Examples:
1178
+ <TagGroup gap="sm">
1179
+ <Tag>React</Tag>
1180
+ <Tag>Vue</Tag>
1181
+ <Tag variant="accent">TypeScript</Tag>
1182
+ </TagGroup>
1183
+ ```
1184
+
1185
+ **Common mistakes:**
1186
+ - ❌ `TagGroup without Tag children` → Place <Tag> elements as direct children
1187
+ - ❌ `Using TagGroup for tag input` → Use TagsInput for an editable tag field
1188
+
1189
+ ---
1190
+
1191
+ ### Item
1192
+
1193
+ Layout primitive for list rows, settings rows, and notification rows. Denser than Card — use Item (not Card) for repeated list rows.
1194
+
1195
+ ```tsx
1196
+ import { Item, ItemMedia, ItemContent, ItemTitle, ItemDescription, ItemActions, ItemGroup } from "@usevyre/react"
1197
+
1198
+ // Props:
1199
+ // variant = "default" | "outlined" | "muted" | "plain" (default: default)
1200
+ // size = "sm" | "md" | "lg" (default: md)
1201
+ // clickable = boolean (default: false)
1202
+
1203
+ // Examples:
1204
+ <Item>
1205
+ <ItemMedia><BellIcon /></ItemMedia>
1206
+ <ItemContent>
1207
+ <ItemTitle>Notifications</ItemTitle>
1208
+ <ItemDescription>Receive an email when someone mentions you.</ItemDescription>
1209
+ </ItemContent>
1210
+ <ItemActions>
1211
+ <Switch defaultChecked />
1212
+ </ItemActions>
1213
+ </Item>
1214
+ <ItemGroup separated>
1215
+ <Item clickable>
1216
+ <ItemContent><ItemTitle>Profile</ItemTitle></ItemContent>
1217
+ </Item>
1218
+ <Item clickable>
1219
+ <ItemContent><ItemTitle>Billing</ItemTitle></ItemContent>
1220
+ </Item>
1221
+ </ItemGroup>
1222
+ ```
1223
+
1224
+ **Common mistakes:**
1225
+ - ❌ `Card used for repeated list rows` → Use <Item> (optionally inside <ItemGroup separated>) for list/settings rows
1226
+ - ❌ `Item variant="primary"` → Use variant="default" | "outlined" | "muted"
1227
+ - ❌ `raw text directly inside Item` → Wrap text in <ItemContent><ItemTitle>…</ItemTitle></ItemContent>
1228
+
1229
+ ---
1230
+
1231
+ ### Kanban
1232
+
1233
+ Drag-and-drop board: cards move between columns (or reorder within a column). CONTROLLED & data-driven like DataGrid. While dragging, a placeholder shows the exact drop position. Each card is wrapped in a Card (variant="outlined"); renderCard (React) / #card slot (Vue) can render ANY content incl. complex components (Avatar/Badge/Progress). Columns and cards accept an optional semantic color tint. Native HTML5 DnD, zero deps.
1234
+
1235
+ ```tsx
1236
+ import { Kanban } from "@usevyre/react"
1237
+
1238
+ // Props:
1239
+ // value = KanbanColumn[]
1240
+ // onChange = function
1241
+ // renderCard = function
1242
+ // onCardClick = function
1243
+
1244
+ // Examples:
1245
+ const [columns, setColumns] = useState([
1246
+ { id: "todo", title: "To Do", cards: [{ id: "1", title: "Spec API" }] },
1247
+ { id: "doing", title: "In Progress", cards: [] },
1248
+ { id: "done", title: "Done", cards: [{ id: "2", title: "Kickoff" }] },
1249
+ ]);
1250
+ <Kanban value={columns} onChange={setColumns} />
1251
+ <Kanban
1252
+ value={columns}
1253
+ onChange={setColumns}
1254
+ onCardClick={(card) => openDetail(card.id)}
1255
+ renderCard={(card) => (
1256
+ <><strong>{card.title}</strong><Badge>{card.id}</Badge></>
1257
+ )}
1258
+ />
1259
+ const [cols, setCols] = useState([
1260
+ { id: "doing", title: "In Progress", color: "teal", cards: [
1261
+ { id: "t1", title: "OAuth", assignee: "AK", progress: 60, color: "warning" },
1262
+ ]},
1263
+ ]);
1264
+ <Kanban
1265
+ value={cols}
1266
+ onChange={setCols}
1267
+ renderCard={(card) => (
1268
+ <><strong>{card.title}</strong><Progress value={card.progress} /></>
1269
+ )}
1270
+ />
1271
+ ```
1272
+
1273
+ **Common mistakes:**
1274
+ - ❌ `Kanban without onChange (or ignoring it)` → Store columns in state and setColumns in onChange (v-model in Vue)
1275
+ - ❌ `Duplicate card ids across columns` → Use globally-unique card ids across the entire board
1276
+ - ❌ `Mutating value in place then calling onChange` → Pass the new array Kanban gives you straight to setState / v-model
1277
+ - ❌ `color="blue" (or any non-semantic value)` → Use one of: "default" | "accent" | "teal" | "success" | "warning" | "danger"
1278
+
1279
+ ---
1280
+
1281
+ ### Conversation
1282
+
1283
+ Chat / inbox message thread. CONTROLLED & data-driven like Kanban — you own `value` (messages array) and append in your own send handler; Conversation holds no message state. Consecutive messages from the same author are grouped (avatar + name shown once), day separators are inserted on date change, and outgoing messages (authorId === currentUserId) align right.
1284
+
1285
+ ```tsx
1286
+ import { Conversation } from "@usevyre/react"
1287
+
1288
+ // Props:
1289
+ // value = ConversationMessage[]
1290
+ // currentUserId = string
1291
+ // composer = boolean (default: false)
1292
+ // onSend = function
1293
+ // placeholder = string (default: Write a message…)
1294
+ // typing = boolean | string (default: false)
1295
+ // allowAttachments = boolean (default: false)
1296
+ // accept = string
1297
+ // renderMessage = function
1298
+ // renderComposer = function
1299
+
1300
+ // Examples:
1301
+ const [messages, setMessages] = useState([
1302
+ { id: "1", authorId: "sam", authorName: "Sam", text: "Hey!" },
1303
+ { id: "2", authorId: "me", text: "Hi \ud83d\udc4b", status: "read" },
1304
+ ]);
1305
+ <Conversation
1306
+ value={messages}
1307
+ currentUserId="me"
1308
+ composer
1309
+ onSend={(t) => setMessages((m) => [...m, { id: crypto.randomUUID(), authorId: "me", text: t }])}
1310
+ />
1311
+ <Conversation
1312
+ value={messages}
1313
+ currentUserId="me"
1314
+ typing="Sam is typing"
1315
+ renderMessage={(m) => <strong>{m.text}</strong>}
1316
+ />
1317
+ const messages = [
1318
+ { id: "1", authorId: "sam", authorName: "Sam", text: "Moodboard \ud83d\udc47",
1319
+ attachments: [{ kind: "image", url: "/board.png", name: "board.png" }] },
1320
+ { id: "2", authorId: "me", text: "Specs:", status: "read",
1321
+ attachments: [{ kind: "file", url: "/spec.pdf", name: "spec.pdf", size: "2.4 MB" }] },
1322
+ ];
1323
+ <Conversation value={messages} currentUserId="me" />
1324
+ ```
1325
+
1326
+ **Common mistakes:**
1327
+ - ❌ `Conversation without currentUserId` → Always pass currentUserId matching one of the message authorId values
1328
+ - ❌ `Expecting Conversation to store/append messages` → Append to your own state in onSend (or @send) and pass it back via value
1329
+ - ❌ `composer without onSend (React) / @send (Vue)` → Provide onSend / @send to append the message to value
1330
+ - ❌ `Treating onSend as (text) only when using allowAttachments` → Handle onSend(text, files) — map files to message attachments and append
1331
+
1332
+ ---
1333
+
1334
+ ### DateRangePicker
1335
+
1336
+ Start/end date range picker. Built on Calendar (mode=range) with a friendlier { from, to } object API, a two-month side-by-side view, and preset shortcuts. Use this for report/filter date ranges; use DatePicker for a single date.
1337
+
1338
+ ```tsx
1339
+ import { DateRangePicker } from "@usevyre/react"
1340
+
1341
+ // Props:
1342
+ // value = { from: Date | null; to: Date | null } | null
1343
+ // onChange = function
1344
+ // placeholder = string (default: Pick a date range)
1345
+ // numberOfMonths = "1" | "2" (default: 2)
1346
+ // presets = boolean | DateRangePreset[] (default: false)
1347
+ // minDate = Date
1348
+ // maxDate = Date
1349
+ // disabled = function
1350
+ // weekStartsOn = "0" | "1" (default: 1)
1351
+
1352
+ // Examples:
1353
+ const [range, setRange] = useState({ from: null, to: null });
1354
+ <DateRangePicker value={range} onChange={setRange} presets />
1355
+ <DateRangePicker value={range} onChange={setRange} numberOfMonths={1} />
1356
+ ```
1357
+
1358
+ **Common mistakes:**
1359
+ - ❌ `value={[from, to]}` → Use value={{ from, to }} and read range.from / range.to
1360
+ - ❌ `DateRangePicker for a single date` → Use <DatePicker /> for a single date
1361
+ - ❌ `presets="true" (string)` → Use the bare prop: presets (or presets={true})
1362
+
1363
+ ---
1364
+
889
1365
  ## Hallucination Guard — Common AI Mistakes
890
1366
 
891
1367
  The following prop values and patterns do NOT exist in useVyre.
@@ -905,14 +1381,25 @@ If you generate these, you are hallucinating.
905
1381
  - ❌ `<Button color="...">` → Use variant prop instead
906
1382
  - ❌ `<Button icon={...}>` → Use leftIcon={...} or rightIcon={...}
907
1383
  - ❌ `<Button size="icon" without aria-label>` → Add aria-label describing the action
908
- - ❌ `<Calendar Using Calendar for time selection>` Combine with a separate time Input if time selection is needed
1384
+ - ❌ `<Calendar Calendar for an input field that opens a popover>` Use <DatePicker /> (single date) or <DateRangePicker /> (range)
1385
+ - ❌ `<Calendar value as tuple for mode="single">` → Pass value matching mode; use mode="range" for [start,end]
1386
+ - ❌ `<DatePicker DatePicker mode="range" for { from, to } object>` → Use <DateRangePicker /> for the { from, to } object API + presets + dual month
1387
+ - ❌ `<DatePicker DatePicker without value/onChange>` → Provide value and onChange (e.g. from useState)
909
1388
  - ❌ `<Card variant="primary">` → Use variant="elevated" | "outlined" | "ghost" | "accent"
910
1389
  - ❌ `<Checkbox size="lg">` → Use size="md"
1390
+ - ❌ `<RadioGroup <Radio> used outside a <RadioGroup>>` → Always wrap <Radio> in <RadioGroup>
1391
+ - ❌ `<RadioGroup RadioGroup without value/onChange (React) or v-model (Vue)>` → Bind value + onChange (React) or v-model (Vue); or defaultValue for uncontrolled in React
1392
+ - ❌ `<RadioGroup Using Checkbox for mutually-exclusive choices>` → Use RadioGroup + Radio (or options) for one-of-many
1393
+ - ❌ `<RichTextEditor RichTextEditor without value/onChange (React) or v-model (Vue)>` → Keep the HTML string in state and update it in onChange / v-model
1394
+ - ❌ `<RichTextEditor Rendering value as text or with dangerouslySetInnerHTML elsewhere without sanitising>` → Sanitise (e.g. DOMPurify) before re-rendering untrusted RTE output
1395
+ - ❌ `<RichTextEditor toolbar="bold" (string)>` → Pass an array, e.g. toolbar={["bold","italic","link"]}
911
1396
  - ❌ `<Command Using Input type="search" for search UI>` → Use Command + CommandInput + CommandList + CommandItem
912
1397
  - ❌ `<DropdownMenu DropdownItem variant="primary">` → Use variant="danger" for destructive items only
913
1398
  - ❌ `<Field Applying state prop directly to Input>` → Wrap Input in <Field state="error"> to apply validation styling
1399
+ - ❌ `<Field Mixing props label/hint AND FieldLabel/FieldError for the same field>` → Pick one: either props-based (label/hint/state) OR composable parts
914
1400
  - ❌ `<Input size="icon">` → Use size="sm" | "md" | "lg"
915
1401
  - ❌ `<Input type="search" for search UI>` → Import Command from @usevyre/react for search palettes
1402
+ - ❌ `<Input Vue: binding Input/Textarea value without v-model>` → Use v-model on <Input>/<Textarea> in Vue; in React use value + onChange
916
1403
  - ❌ `<Modal size="xl">` → Use size="lg" or size="full"
917
1404
  - ❌ `<Popover placement="top-center">` → Use placement="top" for centered placement
918
1405
  - ❌ `<Progress value > 100>` → Normalize your value to 0–100 range before passing
@@ -922,6 +1409,35 @@ If you generate these, you are hallucinating.
922
1409
  - ❌ `<Toast variant="info">` → Use variant="default"
923
1410
  - ❌ `<Tooltip Using Tooltip for rich content (forms, buttons, etc.)>` → Use Popover for rich interactive content
924
1411
  - ❌ `<Typography Using raw <h1>, <p> tags instead of Typography components>` → Use <Heading>, <Text>, <Lead> from @usevyre/react
1412
+ - ❌ `<ButtonGroup ButtonGroup variant="...">` → Set variant on each <Button> inside the group
1413
+ - ❌ `<ButtonGroup ButtonGroup without Button children>` → Place <Button> elements as direct children
1414
+ - ❌ `<TagsInput TagsInput value={string}>` → Pass an array: value={['react','vue']}
1415
+ - ❌ `<TagsInput TagsInput without onChange>` → Provide value and onChange (React) or v-model (Vue)
1416
+ - ❌ `<Combobox Combobox value="">` → Use value={null} for no selection
1417
+ - ❌ `<Combobox Combobox options={string[]}>` → Use [{ value: 'ts', label: 'TypeScript' }]
1418
+ - ❌ `<Combobox Using Combobox for command palette>` → Use Command for command palettes
1419
+ - ❌ `<DataGrid DataGrid expecting built-in pagination>` → Slice rows yourself and use the Pagination component
1420
+ - ❌ `<DataGrid DataGrid expecting built-in filtering>` → Filter the rows array before passing it in
1421
+ - ❌ `<DataGrid sortable without onSort>` → Handle onSort and sort the rows array in your state
1422
+ - ❌ `<Tag Tag variant="success">` → Use Badge for success/warning/teal status colors; Tag is for categories/filters
1423
+ - ❌ `<Tag Using Tag for tag input>` → Use TagsInput for adding/removing tags via keyboard
1424
+ - ❌ `<Tag Tag size="xl">` → Use size="lg"
1425
+ - ❌ `<TagGroup TagGroup without Tag children>` → Place <Tag> elements as direct children
1426
+ - ❌ `<TagGroup Using TagGroup for tag input>` → Use TagsInput for an editable tag field
1427
+ - ❌ `<Item Card used for repeated list rows>` → Use <Item> (optionally inside <ItemGroup separated>) for list/settings rows
1428
+ - ❌ `<Item Item variant="primary">` → Use variant="default" | "outlined" | "muted"
1429
+ - ❌ `<Item raw text directly inside Item>` → Wrap text in <ItemContent><ItemTitle>…</ItemTitle></ItemContent>
1430
+ - ❌ `<Kanban Kanban without onChange (or ignoring it)>` → Store columns in state and setColumns in onChange (v-model in Vue)
1431
+ - ❌ `<Kanban Duplicate card ids across columns>` → Use globally-unique card ids across the entire board
1432
+ - ❌ `<Kanban Mutating value in place then calling onChange>` → Pass the new array Kanban gives you straight to setState / v-model
1433
+ - ❌ `<Kanban color="blue" (or any non-semantic value)>` → Use one of: "default" | "accent" | "teal" | "success" | "warning" | "danger"
1434
+ - ❌ `<Conversation Conversation without currentUserId>` → Always pass currentUserId matching one of the message authorId values
1435
+ - ❌ `<Conversation Expecting Conversation to store/append messages>` → Append to your own state in onSend (or @send) and pass it back via value
1436
+ - ❌ `<Conversation composer without onSend (React) / @send (Vue)>` → Provide onSend / @send to append the message to value
1437
+ - ❌ `<Conversation Treating onSend as (text) only when using allowAttachments>` → Handle onSend(text, files) — map files to message attachments and append
1438
+ - ❌ `<DateRangePicker value={[from, to]}>` → Use value={{ from, to }} and read range.from / range.to
1439
+ - ❌ `<DateRangePicker DateRangePicker for a single date>` → Use <DatePicker /> for a single date
1440
+ - ❌ `<DateRangePicker presets="true" (string)>` → Use the bare prop: presets (or presets={true})
925
1441
 
926
1442
  ---
927
1443