@usevyre/react 1.1.0 → 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,10 +1,10 @@
1
1
  /**
2
- * @usevyre/react — Calendar & DatePicker
2
+ * @usevyre/react — Calendar
3
3
  *
4
4
  * AI CONTEXT:
5
5
  * ┌─────────────────────────────────────────────────────────────┐
6
- * │ Components: Calendar · DatePicker
7
- * │ Import: import { Calendar, DatePicker } from "@usevyre/react"
6
+ * │ Component: Calendar (inline grid; no input/popover)
7
+ * │ Import: import { Calendar } from "@usevyre/react"
8
8
  * │ │
9
9
  * │ Calendar props: │
10
10
  * │ mode = "single"(default) | "range" | "multiple" │
@@ -13,11 +13,10 @@
13
13
  * │ showTime = boolean (adds HH:MM time picker) │
14
14
  * │ minDate / maxDate = Date │
15
15
  * │ disabled = (date: Date) => boolean │
16
+ * │ defaultMonth = Date (initial month when value empty) │
16
17
  * │ │
17
- * │ DatePicker props:
18
- * │ = all Calendar props +
19
- * │ placeholder = string │
20
- * │ className = string │
18
+ * │ For an input + popover, use DatePicker.
19
+ * │ For start/end ranges with presets, use DateRangePicker.
21
20
  * └─────────────────────────────────────────────────────────────┘
22
21
  *
23
22
  * @example
@@ -26,9 +25,6 @@
26
25
  *
27
26
  * // Date range
28
27
  * <Calendar mode="range" value={[start, end]} onChange={setRange} />
29
- *
30
- * // DatePicker (input + popover)
31
- * <DatePicker mode="single" value={date} onChange={setDate} placeholder="Pick a date" />
32
28
  */
33
29
  import React from "react";
34
30
  export type CalendarMode = "single" | "range" | "multiple";
@@ -54,11 +50,16 @@ export type CalendarBaseProps = {
54
50
  disabled?: (date: Date) => boolean;
55
51
  className?: string;
56
52
  weekStartsOn?: 0 | 1;
53
+ /** Month to display initially when value is empty (uncontrolled view). */
54
+ defaultMonth?: Date;
57
55
  };
58
56
  export type CalendarProps = (CalendarSingleProps & CalendarBaseProps) | (CalendarRangeProps & CalendarBaseProps) | (CalendarMultipleProps & CalendarBaseProps);
59
57
  export type DatePickerProps = CalendarProps & {
60
58
  placeholder?: string;
61
59
  inputClassName?: string;
62
60
  };
61
+ /** @internal — shared with DatePicker. */
62
+ export declare function formatDate(d: Date | null | undefined, opts?: Intl.DateTimeFormatOptions): string;
63
+ /** @internal — shared with DatePicker. */
64
+ export declare function formatTime(d: Date): string;
63
65
  export declare const Calendar: React.ForwardRefExoticComponent<CalendarProps & React.RefAttributes<HTMLDivElement>>;
64
- export declare const DatePicker: React.ForwardRefExoticComponent<DatePickerProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @usevyre/react — DatePicker
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────┐
6
+ * │ Component: DatePicker (input trigger + popover Calendar) │
7
+ * │ Import: import { DatePicker } from "@usevyre/react" │
8
+ * │ │
9
+ * │ = all Calendar props + │
10
+ * │ placeholder = string (default "Pick a date") │
11
+ * │ inputClassName = string │
12
+ * │ │
13
+ * │ mode = "single"(default) | "range" | "multiple" │
14
+ * │ value = Date | [Date,Date] | Date[] (matches mode) │
15
+ * │ │
16
+ * │ Renders a button that opens a Calendar in a portal popover. │
17
+ * │ For an always-visible inline grid, use Calendar. │
18
+ * │ For start/end ranges with presets + dual month, use │
19
+ * │ DateRangePicker. │
20
+ * └─────────────────────────────────────────────────────────────┘
21
+ *
22
+ * @example
23
+ * // Single date
24
+ * <DatePicker mode="single" value={date} onChange={setDate} placeholder="Pick a date" />
25
+ *
26
+ * // Date + time
27
+ * <DatePicker value={date} onChange={setDate} showTime />
28
+ */
29
+ import React from "react";
30
+ import { type DatePickerProps } from "./Calendar";
31
+ export type { DatePickerProps };
32
+ export declare const DatePicker: React.ForwardRefExoticComponent<DatePickerProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @usevyre/react — Conversation
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: Conversation (chat / inbox message thread) │
7
+ * │ Import: import { Conversation } from "@usevyre/react" │
8
+ * │ │
9
+ * │ CONTROLLED & data-driven (like Kanban/DataGrid). No internal │
10
+ * │ message state — you own `value`, append in your own handler. │
11
+ * │ │
12
+ * │ Props: │
13
+ * │ value = ConversationMessage[] (required) │
14
+ * │ currentUserId = string (required — whose messages align right)│
15
+ * │ composer? = boolean (built-in input + Send; default false) │
16
+ * │ onSend? = (text: string, files: File[]) => void │
17
+ * │ placeholder? = string (composer placeholder) │
18
+ * │ typing? = boolean | string (show typing indicator) │
19
+ * │ allowAttachments? = boolean (📎 button + staged-file chips) │
20
+ * │ accept? = string (file input accept, e.g. "image/*") │
21
+ * │ renderMessage?= (msg, meta) => ReactNode (custom bubble body) │
22
+ * │ renderComposer? = (api) => ReactNode (replace composer) │
23
+ * │ api = { value, setValue, files, setFiles, send } │
24
+ * │ │
25
+ * │ ConversationMessage = { │
26
+ * │ id; authorId; text?; │
27
+ * │ authorName?; authorAvatar?; │
28
+ * │ timestamp?: Date | string | number; │
29
+ * │ status?: "sending"|"sent"|"delivered"|"read"; │
30
+ * │ attachments?: ConversationAttachment[] │
31
+ * │ } │
32
+ * │ ConversationAttachment = { │
33
+ * │ kind: "image"|"audio"|"video"|"file"; │
34
+ * │ url; name?; size? │
35
+ * │ } → rendered inside the bubble (image preview, audio/video │
36
+ * │ player, or a download chip for files) │
37
+ * │ │
38
+ * │ Consecutive messages from the same author are grouped (avatar + │
39
+ * │ name shown once). Day separators inserted on date change. │
40
+ * │ Outgoing = authorId === currentUserId (aligned right). │
41
+ * └─────────────────────────────────────────────────────────────────┘
42
+ *
43
+ * @example
44
+ * const [messages, setMessages] = useState<ConversationMessage[]>([
45
+ * { id: "1", authorId: "sam", authorName: "Sam", text: "Hey!" },
46
+ * { id: "2", authorId: "me", text: "Hi 👋", status: "read" },
47
+ * ]);
48
+ * <Conversation
49
+ * value={messages}
50
+ * currentUserId="me"
51
+ * composer
52
+ * onSend={(t) => setMessages((m) => [...m, { id: crypto.randomUUID(), authorId: "me", text: t }])}
53
+ * />
54
+ */
55
+ import React from "react";
56
+ import type { BaseProps } from "../../types";
57
+ export type ConversationStatus = "sending" | "sent" | "delivered" | "read";
58
+ export type ConversationAttachmentKind = "image" | "audio" | "video" | "file";
59
+ export interface ConversationAttachment {
60
+ kind: ConversationAttachmentKind;
61
+ /** Source/href for the media or download. */
62
+ url: string;
63
+ /** Display name (file attachments) / alt text (image). */
64
+ name?: string;
65
+ /** Optional human-readable size, e.g. "2.4 MB". */
66
+ size?: string;
67
+ }
68
+ export interface ConversationMessage {
69
+ id: string;
70
+ /** Identifies the sender. Matched against currentUserId for alignment. */
71
+ authorId: string;
72
+ text?: string;
73
+ authorName?: string;
74
+ authorAvatar?: string;
75
+ timestamp?: Date | string | number;
76
+ status?: ConversationStatus;
77
+ /** Image / audio / video / file attachments rendered inside the bubble. */
78
+ attachments?: ConversationAttachment[];
79
+ }
80
+ /** Per-message layout info passed to renderMessage. */
81
+ export interface ConversationMessageMeta {
82
+ outgoing: boolean;
83
+ /** First message of a same-author run (avatar/name shown). */
84
+ isGroupStart: boolean;
85
+ /** Last message of a same-author run (timestamp/status shown). */
86
+ isGroupEnd: boolean;
87
+ }
88
+ export interface ConversationComposerApi {
89
+ value: string;
90
+ setValue: (v: string) => void;
91
+ /** Files staged via the attach button (when allowAttachments). */
92
+ files: File[];
93
+ setFiles: (f: File[]) => void;
94
+ send: () => void;
95
+ }
96
+ export interface ConversationProps extends BaseProps {
97
+ value: ConversationMessage[];
98
+ currentUserId: string;
99
+ composer?: boolean;
100
+ /**
101
+ * Called when the composer submits. `files` holds anything staged via the
102
+ * attach button (empty array when allowAttachments is off). You own the
103
+ * upload + how staged files become message attachments.
104
+ */
105
+ onSend?: (text: string, files: File[]) => void;
106
+ placeholder?: string;
107
+ typing?: boolean | string;
108
+ /** Show an attach (📎) button + staged-file chips in the built-in composer. */
109
+ allowAttachments?: boolean;
110
+ /** `accept` attribute forwarded to the file input (e.g. "image/*"). */
111
+ accept?: string;
112
+ renderMessage?: (message: ConversationMessage, meta: ConversationMessageMeta) => React.ReactNode;
113
+ renderComposer?: (api: ConversationComposerApi) => React.ReactNode;
114
+ }
115
+ export declare const Conversation: React.ForwardRefExoticComponent<ConversationProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @usevyre/react — DateRangePicker
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: DateRangePicker │
7
+ * │ Import: import { DateRangePicker } from "@usevyre/react" │
8
+ * │ │
9
+ * │ Built on top of Calendar (mode="range"). Friendlier API: │
10
+ * │ uses a { from, to } object instead of a [Date,Date] tuple, │
11
+ * │ shows TWO months side-by-side, and a preset shortcut column. │
12
+ * │ │
13
+ * │ Props: │
14
+ * │ value = { from: Date | null; to: Date | null } | null │
15
+ * │ onChange = (range: DateRange) => void │
16
+ * │ placeholder = string (default "Pick a date range") │
17
+ * │ numberOfMonths = 1 | 2 (default 2) │
18
+ * │ presets = boolean | DateRangePreset[] │
19
+ * │ true → built-in presets (Today, Last 7 days, …) │
20
+ * │ minDate / maxDate = Date │
21
+ * │ disabled = (date: Date) => boolean │
22
+ * │ weekStartsOn = 0 (Sun) | 1 (Mon, default) │
23
+ * │ │
24
+ * │ DateRange = { from: Date | null; to: Date | null } │
25
+ * │ DateRangePreset = { label: string; range: () => DateRange } │
26
+ * │ │
27
+ * │ Use this for "select a start AND end date" (reports, filters). │
28
+ * │ For a single date use DatePicker. Do NOT pass a tuple — pass │
29
+ * │ an object: value={{ from, to }}. │
30
+ * └─────────────────────────────────────────────────────────────────┘
31
+ *
32
+ * @example
33
+ * const [range, setRange] = useState<DateRange>({ from: null, to: null });
34
+ * <DateRangePicker value={range} onChange={setRange} presets />
35
+ */
36
+ import React from "react";
37
+ export interface DateRange {
38
+ from: Date | null;
39
+ to: Date | null;
40
+ }
41
+ export interface DateRangePreset {
42
+ label: string;
43
+ range: () => DateRange;
44
+ }
45
+ export interface DateRangePickerProps {
46
+ value?: DateRange | null;
47
+ onChange?: (range: DateRange) => void;
48
+ placeholder?: string;
49
+ numberOfMonths?: 1 | 2;
50
+ presets?: boolean | DateRangePreset[];
51
+ minDate?: Date;
52
+ maxDate?: Date;
53
+ disabled?: (date: Date) => boolean;
54
+ weekStartsOn?: 0 | 1;
55
+ className?: string;
56
+ inputClassName?: string;
57
+ }
58
+ export declare const DateRangePicker: React.ForwardRefExoticComponent<DateRangePickerProps & React.RefAttributes<HTMLDivElement>>;
@@ -6,11 +6,14 @@
6
6
  * │ Components: Field + Input + Textarea │
7
7
  * │ Import: import { Field, Input, Textarea } from "@usevyre/react" │
8
8
  * │ │
9
- * │ Field props:
9
+ * │ Field props (props-based, simplest):
10
10
  * │ label = string │
11
11
  * │ hint = string (helper text below input) │
12
12
  * │ state = "idle"|"error"|"success"|"warning" │
13
13
  * │ required = boolean │
14
+ * │ Field composable parts (richer layouts): │
15
+ * │ FieldLabel · FieldDescription · FieldError · │
16
+ * │ FieldGroup · FieldSet (props-based API still works) │
14
17
  * │ │
15
18
  * │ Input props: │
16
19
  * │ size = "sm"|"md"(default)|"lg" │
@@ -40,6 +43,24 @@ export interface FieldProps extends React.HTMLAttributes<HTMLDivElement>, BasePr
40
43
  htmlFor?: string;
41
44
  }
42
45
  export declare const Field: React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>>;
46
+ export interface FieldLabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>, BaseProps {
47
+ required?: boolean;
48
+ }
49
+ export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLLabelElement>>;
50
+ export interface FieldDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement>, BaseProps {
51
+ }
52
+ export declare const FieldDescription: React.ForwardRefExoticComponent<FieldDescriptionProps & React.RefAttributes<HTMLParagraphElement>>;
53
+ export interface FieldErrorProps extends React.HTMLAttributes<HTMLParagraphElement>, BaseProps {
54
+ }
55
+ export declare const FieldError: React.ForwardRefExoticComponent<FieldErrorProps & React.RefAttributes<HTMLParagraphElement>>;
56
+ export interface FieldGroupProps extends React.HTMLAttributes<HTMLDivElement>, BaseProps {
57
+ orientation?: "vertical" | "horizontal";
58
+ }
59
+ export declare const FieldGroup: React.ForwardRefExoticComponent<FieldGroupProps & React.RefAttributes<HTMLDivElement>>;
60
+ export interface FieldSetProps extends React.FieldsetHTMLAttributes<HTMLFieldSetElement>, BaseProps {
61
+ legend?: string;
62
+ }
63
+ export declare const FieldSet: React.ForwardRefExoticComponent<FieldSetProps & React.RefAttributes<HTMLFieldSetElement>>;
43
64
  export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, BaseProps {
44
65
  size?: Exclude<Size, "icon">;
45
66
  leftElement?: React.ReactNode;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @usevyre/react — Item
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: Item + ItemGroup + ItemMedia + ItemContent + │
7
+ * │ ItemTitle + ItemDescription + ItemActions │
8
+ * │ Import: import { Item, ItemContent, ... } from "@usevyre/react" │
9
+ * │ │
10
+ * │ Item props: │
11
+ * │ variant = "default"(default)|"outlined"|"muted"|"plain" │
12
+ * │ plain → transparent, no border — for composing Item │
13
+ * │ inside another surface (Card, Kanban card, list) │
14
+ * │ size = "sm"|"md"(default)|"lg" │
15
+ * │ clickable = boolean (cursor pointer + hover + role=button) │
16
+ * │ asChild = false (always renders <div>; no polymorphism) │
17
+ * │ │
18
+ * │ ItemGroup props: │
19
+ * │ separated = boolean (adds dividers between items) │
20
+ * │ │
21
+ * │ Slot structure (left → right): │
22
+ * │ <Item> │
23
+ * │ <ItemMedia> ← optional: icon / avatar / thumbnail │
24
+ * │ <ItemContent> ← required: text column │
25
+ * │ <ItemTitle> │
26
+ * │ <ItemDescription> │
27
+ * │ <ItemActions> ← optional: buttons / menu, pinned right │
28
+ * │ </Item> │
29
+ * │ │
30
+ * │ Use Item for list rows, settings rows, notification rows. │
31
+ * │ Do NOT use Card for dense list rows — Item is the primitive. │
32
+ * └─────────────────────────────────────────────────────────────────┘
33
+ *
34
+ * @example
35
+ * // Settings row
36
+ * <Item>
37
+ * <ItemMedia><BellIcon /></ItemMedia>
38
+ * <ItemContent>
39
+ * <ItemTitle>Notifications</ItemTitle>
40
+ * <ItemDescription>Receive email when someone mentions you.</ItemDescription>
41
+ * </ItemContent>
42
+ * <ItemActions>
43
+ * <Switch defaultChecked />
44
+ * </ItemActions>
45
+ * </Item>
46
+ *
47
+ * // Grouped list with dividers
48
+ * <ItemGroup separated>
49
+ * <Item clickable><ItemContent><ItemTitle>Profile</ItemTitle></ItemContent></Item>
50
+ * <Item clickable><ItemContent><ItemTitle>Billing</ItemTitle></ItemContent></Item>
51
+ * </ItemGroup>
52
+ */
53
+ import React from "react";
54
+ import type { BaseProps } from "../../types";
55
+ type ItemVariant = "default" | "outlined" | "muted" | "plain";
56
+ type ItemSize = "sm" | "md" | "lg";
57
+ export interface ItemProps extends React.HTMLAttributes<HTMLDivElement>, BaseProps {
58
+ variant?: ItemVariant;
59
+ size?: ItemSize;
60
+ clickable?: boolean;
61
+ }
62
+ export declare const Item: React.ForwardRefExoticComponent<ItemProps & React.RefAttributes<HTMLDivElement>>;
63
+ export interface ItemGroupProps extends React.HTMLAttributes<HTMLDivElement>, BaseProps {
64
+ separated?: boolean;
65
+ }
66
+ export declare const ItemGroup: React.ForwardRefExoticComponent<ItemGroupProps & React.RefAttributes<HTMLDivElement>>;
67
+ export interface ItemSectionProps extends React.HTMLAttributes<HTMLDivElement>, BaseProps {
68
+ }
69
+ export declare const ItemMedia: React.ForwardRefExoticComponent<ItemSectionProps & React.RefAttributes<HTMLDivElement>>;
70
+ export declare const ItemContent: React.ForwardRefExoticComponent<ItemSectionProps & React.RefAttributes<HTMLDivElement>>;
71
+ export declare const ItemTitle: React.ForwardRefExoticComponent<ItemSectionProps & React.RefAttributes<HTMLDivElement>>;
72
+ export declare const ItemDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & BaseProps & React.RefAttributes<HTMLParagraphElement>>;
73
+ export declare const ItemActions: React.ForwardRefExoticComponent<ItemSectionProps & React.RefAttributes<HTMLDivElement>>;
74
+ export {};
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @usevyre/react — Kanban
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: Kanban (board with drag-and-drop between columns) │
7
+ * │ Import: import { Kanban } from "@usevyre/react" │
8
+ * │ │
9
+ * │ CONTROLLED & data-driven (like DataGrid). No internal data │
10
+ * │ state — you own `value`, update it in `onChange`. │
11
+ * │ │
12
+ * │ Props: │
13
+ * │ value = KanbanColumn[] (required, controlled) │
14
+ * │ onChange = (next: KanbanColumn[]) => void (required) │
15
+ * │ renderCard?= (card, column) => ReactNode (custom card body) │
16
+ * │ onCardClick? = (card, column) => void │
17
+ * │ className? = string │
18
+ * │ │
19
+ * │ KanbanColumn = { id; title; cards: KanbanCard[]; │
20
+ * │ color?: KanbanColor } │
21
+ * │ KanbanCard = { id; title; description?; │
22
+ * │ color?: KanbanColor } │
23
+ * │ KanbanColor = "default" | "accent" | "teal" | │
24
+ * │ "success" | "warning" | "danger" │
25
+ * │ → tints the column/card background (token-based). │
26
+ * │ │
27
+ * │ Drag a card to another column (or reorder within a column); │
28
+ * │ Kanban calls onChange with the next columns array. While │
29
+ * │ dragging, a placeholder shows the exact drop position. Card │
30
+ * │ ids must be unique across the whole board. │
31
+ * │ │
32
+ * │ Each card is wrapped in a <Card> (variant="outlined"); the │
33
+ * │ default body is title + description, but renderCard can return │
34
+ * │ ANY content — including complex components (avatars, badges, │
35
+ * │ progress) — placed inside the Card's body. │
36
+ * │ │
37
+ * │ Native HTML5 drag-and-drop — zero dependencies. │
38
+ * └─────────────────────────────────────────────────────────────────┘
39
+ *
40
+ * @example
41
+ * const [columns, setColumns] = useState<KanbanColumn[]>([
42
+ * { id: "todo", title: "To Do", cards: [{ id: "1", title: "Spec API" }] },
43
+ * { id: "doing", title: "In Progress", cards: [] },
44
+ * { id: "done", title: "Done", cards: [{ id: "2", title: "Kickoff" }] },
45
+ * ]);
46
+ * <Kanban value={columns} onChange={setColumns} />
47
+ */
48
+ import React from "react";
49
+ import type { BaseProps } from "../../types";
50
+ /** Semantic tint applied to a column or card background. */
51
+ export type KanbanColor = "default" | "accent" | "teal" | "success" | "warning" | "danger";
52
+ export interface KanbanCard {
53
+ id: string;
54
+ title: string;
55
+ description?: string;
56
+ /** Tints the card background (token-based). Default: "default". */
57
+ color?: KanbanColor;
58
+ }
59
+ export interface KanbanColumn {
60
+ id: string;
61
+ title: string;
62
+ cards: KanbanCard[];
63
+ /** Tints the column background (token-based). Default: "default". */
64
+ color?: KanbanColor;
65
+ }
66
+ export interface KanbanProps extends BaseProps {
67
+ /** Controlled board data. */
68
+ value: KanbanColumn[];
69
+ /** Called with the next columns array after any drag move. */
70
+ onChange: (next: KanbanColumn[]) => void;
71
+ /** Custom card body renderer. Defaults to title + optional description. */
72
+ renderCard?: (card: KanbanCard, column: KanbanColumn) => React.ReactNode;
73
+ /** Called when a card is clicked (not fired while dragging). */
74
+ onCardClick?: (card: KanbanCard, column: KanbanColumn) => void;
75
+ }
76
+ export declare const Kanban: React.ForwardRefExoticComponent<KanbanProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @usevyre/react — Radio
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: RadioGroup + Radio │
7
+ * │ Import: import { RadioGroup, Radio } from "@usevyre/react" │
8
+ * │ │
9
+ * │ CONTROLLED. RadioGroup owns the selected value. │
10
+ * │ │
11
+ * │ RadioGroup props: │
12
+ * │ value = string (controlled selected value) │
13
+ * │ defaultValue = string (uncontrolled) │
14
+ * │ onChange = (value: string) => void │
15
+ * │ name? = string (radio group name; auto if omitted) │
16
+ * │ disabled? = boolean (disables the whole group) │
17
+ * │ size? = "sm" | "md"(default) │
18
+ * │ orientation? = "vertical"(default) | "horizontal" │
19
+ * │ options? = { value; label; description?; disabled? }[] │
20
+ * │ → data-driven; OR pass <Radio> children for custom layout │
21
+ * │ │
22
+ * │ Radio props (inside RadioGroup): │
23
+ * │ value = string (required) │
24
+ * │ label? = string │
25
+ * │ description? = string │
26
+ * │ disabled? = boolean │
27
+ * │ │
28
+ * │ Use options for simple lists; use <Radio> children when you │
29
+ * │ need custom content. role="radiogroup" + arrow-key roving focus. │
30
+ * └─────────────────────────────────────────────────────────────────┘
31
+ *
32
+ * @example
33
+ * // Data-driven
34
+ * <RadioGroup
35
+ * value={plan}
36
+ * onChange={setPlan}
37
+ * options={[
38
+ * { value: "free", label: "Free", description: "For hobby projects" },
39
+ * { value: "pro", label: "Pro", description: "For teams" },
40
+ * ]}
41
+ * />
42
+ *
43
+ * // Composable
44
+ * <RadioGroup value={plan} onChange={setPlan}>
45
+ * <Radio value="free" label="Free" />
46
+ * <Radio value="pro" label="Pro" />
47
+ * </RadioGroup>
48
+ */
49
+ import React from "react";
50
+ import type { BaseProps } from "../../types";
51
+ export interface RadioOption {
52
+ value: string;
53
+ label?: string;
54
+ description?: string;
55
+ disabled?: boolean;
56
+ }
57
+ type RadioSize = "sm" | "md";
58
+ export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">, BaseProps {
59
+ value?: string;
60
+ defaultValue?: string;
61
+ onChange?: (value: string) => void;
62
+ name?: string;
63
+ disabled?: boolean;
64
+ size?: RadioSize;
65
+ orientation?: "vertical" | "horizontal";
66
+ /** Data-driven options. Omit to pass <Radio> children instead. */
67
+ options?: RadioOption[];
68
+ }
69
+ export declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
70
+ export interface RadioProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "size" | "value" | "type">, BaseProps {
71
+ value: string;
72
+ label?: string;
73
+ description?: string;
74
+ disabled?: boolean;
75
+ }
76
+ export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
77
+ export {};
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @usevyre/react — RichTextEditor
3
+ *
4
+ * AI CONTEXT:
5
+ * ┌─────────────────────────────────────────────────────────────────┐
6
+ * │ Component: RichTextEditor (WYSIWYG, contentEditable) │
7
+ * │ Import: import { RichTextEditor } from "@usevyre/react" │
8
+ * │ │
9
+ * │ CONTROLLED. value is an HTML string; onChange gives next HTML. │
10
+ * │ Zero dependencies — native contentEditable + execCommand. │
11
+ * │ │
12
+ * │ Props: │
13
+ * │ value = string (HTML, controlled) │
14
+ * │ onChange = (html: string) => void │
15
+ * │ placeholder?= string (shown when empty) │
16
+ * │ disabled? = boolean (not editable, dimmed) │
17
+ * │ readOnly? = boolean (not editable, no toolbar) │
18
+ * │ toolbar? = RichTextTool[] (which buttons; default = all) │
19
+ * │ minHeight? = string (CSS, default "10rem") │
20
+ * │ │
21
+ * │ RichTextTool = "bold"|"italic"|"underline"|"strike"| │
22
+ * │ "h1"|"h2"|"h3"|"ul"|"ol"|"quote"|"code"|"link"|"clear" │
23
+ * │ │
24
+ * │ Controlled: store value in state and set it in onChange. │
25
+ * │ Output is sanitised-friendly semantic HTML (no inline styles). │
26
+ * └─────────────────────────────────────────────────────────────────┘
27
+ *
28
+ * @example
29
+ * const [html, setHtml] = useState("<p>Hello <strong>world</strong></p>");
30
+ * <RichTextEditor value={html} onChange={setHtml} placeholder="Write…" />
31
+ */
32
+ import React from "react";
33
+ import type { BaseProps } from "../../types";
34
+ export type RichTextTool = "bold" | "italic" | "underline" | "strike" | "h1" | "h2" | "h3" | "ul" | "ol" | "quote" | "code" | "link" | "clear";
35
+ export interface RichTextEditorProps extends BaseProps {
36
+ value: string;
37
+ onChange: (html: string) => void;
38
+ placeholder?: string;
39
+ disabled?: boolean;
40
+ readOnly?: boolean;
41
+ toolbar?: RichTextTool[];
42
+ minHeight?: string;
43
+ }
44
+ export declare const RichTextEditor: React.ForwardRefExoticComponent<RichTextEditorProps & React.RefAttributes<HTMLDivElement>>;