@usevyre/ai-context 1.3.0 → 1.4.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.
- package/dist/anti-patterns.json +288 -1
- package/dist/cheat-sheets/carousel.md +50 -0
- package/dist/cheat-sheets/carouselslide.md +22 -0
- package/dist/cheat-sheets/emptystate.md +48 -0
- package/dist/cheat-sheets/form.md +50 -0
- package/dist/cheat-sheets/formfield.md +22 -0
- package/dist/cheat-sheets/index.md +18 -0
- package/dist/cheat-sheets/numberinput.md +41 -0
- package/dist/cheat-sheets/otpinput.md +51 -0
- package/dist/cheat-sheets/stat.md +41 -0
- package/dist/cheat-sheets/statgroup.md +23 -0
- package/dist/cheat-sheets/step.md +8 -0
- package/dist/cheat-sheets/steppanel.md +8 -0
- package/dist/cheat-sheets/stepper.md +59 -0
- package/dist/cheat-sheets/steppernav.md +8 -0
- package/dist/cheat-sheets/timeline.md +40 -0
- package/dist/cheat-sheets/timelineitem.md +28 -0
- package/dist/cheat-sheets/togglegroup.md +55 -0
- package/dist/cheat-sheets/toggleitem.md +29 -0
- package/dist/cheat-sheets/tree.md +48 -0
- package/dist/claude-context.md +601 -1
- package/dist/copilot-instructions.md +601 -1
- package/dist/cursor-rules.md +186 -1
- package/dist/full-context.md +600 -0
- package/dist/index.js +10049 -6006
- package/dist/schema.json +974 -2
- package/dist/tokens.json +1 -1
- package/dist/tokens.md +1 -1
- package/dist/version-info.json +232 -51
- package/dist/windsurf-rules.md +601 -1
- package/package.json +1 -1
package/dist/schema.json
CHANGED
|
@@ -1,14 +1,69 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"generatedAt": "2026-05-18",
|
|
5
5
|
"package": "@usevyre/react",
|
|
6
|
-
"packageVersion": "1.
|
|
6
|
+
"packageVersion": "1.16.0",
|
|
7
7
|
"validFor": [
|
|
8
8
|
"@usevyre/react@1.1.0+",
|
|
9
9
|
"@usevyre/vue@1.1.0+"
|
|
10
10
|
],
|
|
11
11
|
"changelog": {
|
|
12
|
+
"1.16.0": {
|
|
13
|
+
"date": "2026-05-18",
|
|
14
|
+
"breaking": false,
|
|
15
|
+
"summary": "Added Carousel + CarouselSlide: accessible controlled content slider with snap scrolling, dot indicators, prev/next arrows, keyboard navigation, optional loop and autoPlay (pauses on hover/focus). Completes the post-1.3 backlog (10/10 components)."
|
|
16
|
+
},
|
|
17
|
+
"1.15.0": {
|
|
18
|
+
"date": "2026-05-18",
|
|
19
|
+
"breaking": false,
|
|
20
|
+
"summary": "Added OTPInput: segmented one-time-code input for 2FA/verification with controlled string value, paste-aware fill, auto-advance/backspace, arrow keys, onComplete, type (numeric/alphanumeric), mask and sizes."
|
|
21
|
+
},
|
|
22
|
+
"1.14.0": {
|
|
23
|
+
"date": "2026-05-18",
|
|
24
|
+
"breaking": false,
|
|
25
|
+
"summary": "Added Tree: data-driven, controlled hierarchical tree view (file explorer / nested nav) with recursive rendering, controlled expanded/selected state, and full keyboard navigation."
|
|
26
|
+
},
|
|
27
|
+
"1.13.0": {
|
|
28
|
+
"date": "2026-05-18",
|
|
29
|
+
"breaking": false,
|
|
30
|
+
"summary": "Added Timeline + TimelineItem: vertical activity feed for audit logs/history with status-colored markers, connector line, and items[] or composable children for rich content."
|
|
31
|
+
},
|
|
32
|
+
"1.12.1": {
|
|
33
|
+
"date": "2026-05-18",
|
|
34
|
+
"breaking": false,
|
|
35
|
+
"summary": "Fix Stat: the arrow direction now follows the sign of `delta` (the actual change) instead of `trend`. `trend` controls only the color. So a falling 'lower is better' metric correctly shows a green DOWN arrow."
|
|
36
|
+
},
|
|
37
|
+
"1.12.0": {
|
|
38
|
+
"date": "2026-05-18",
|
|
39
|
+
"breaking": false,
|
|
40
|
+
"summary": "Added Stat + StatGroup: presentational dashboard KPI with an explicit trend (up/down/neutral) that controls the delta color, sizes, optional icon, and an evenly-split StatGroup row."
|
|
41
|
+
},
|
|
42
|
+
"1.11.0": {
|
|
43
|
+
"date": "2026-05-18",
|
|
44
|
+
"breaking": false,
|
|
45
|
+
"summary": "Added EmptyState: presentational placeholder for empty lists/tables/search with preset variant icons (default/search/error), sizes, and a composed call-to-action."
|
|
46
|
+
},
|
|
47
|
+
"1.10.0": {
|
|
48
|
+
"date": "2026-05-18",
|
|
49
|
+
"breaking": false,
|
|
50
|
+
"summary": "Added Stepper + StepperNav + Step + StepPanel: controlled multi-step flow (0-based index) for wizards/onboarding/checkout, horizontal or vertical, with automatic completed/current/upcoming states and optional clickable back-navigation."
|
|
51
|
+
},
|
|
52
|
+
"1.9.0": {
|
|
53
|
+
"date": "2026-05-18",
|
|
54
|
+
"breaking": false,
|
|
55
|
+
"summary": "Added ToggleGroup + ToggleItem: controlled segmented control with single/multiple modes (value adapts: string|null vs string[]), options[] or composable children, keyboard navigation. Emits the value, not an event."
|
|
56
|
+
},
|
|
57
|
+
"1.8.0": {
|
|
58
|
+
"date": "2026-05-18",
|
|
59
|
+
"breaking": false,
|
|
60
|
+
"summary": "Added NumberInput: controlled numeric input with −/+ stepper, min/max/step/precision, clamp-on-blur and keyboard stepping. Emits number|null (not an event) and composes directly with Form/FormField."
|
|
61
|
+
},
|
|
62
|
+
"1.7.0": {
|
|
63
|
+
"date": "2026-05-18",
|
|
64
|
+
"breaking": false,
|
|
65
|
+
"summary": "Added Form + FormField: controlled, data-driven, zero-dependency form with built-in validation rules (required/minLength/maxLength/min/max/pattern/email/custom) that map errors into Field automatically."
|
|
66
|
+
},
|
|
12
67
|
"1.6.0": {
|
|
13
68
|
"date": "2026-05-18",
|
|
14
69
|
"breaking": false,
|
|
@@ -2915,6 +2970,923 @@
|
|
|
2915
2970
|
}
|
|
2916
2971
|
]
|
|
2917
2972
|
},
|
|
2973
|
+
"Form": {
|
|
2974
|
+
"description": "Controlled, data-driven form. Zero dependencies. Validation runs on submit and (after the first submit) on blur. Errors map into the wrapped Field automatically (state=error + hint=message). Compose with FormField, which wires name/value/onChange/onBlur into a single control child.",
|
|
2975
|
+
"import": "import { Form, FormField } from \"@usevyre/react\"",
|
|
2976
|
+
"subcomponents": [
|
|
2977
|
+
"FormField"
|
|
2978
|
+
],
|
|
2979
|
+
"props": {
|
|
2980
|
+
"values": {
|
|
2981
|
+
"type": "Record<string, any>",
|
|
2982
|
+
"description": "Controlled values map. Omit for uncontrolled (use defaultValues)."
|
|
2983
|
+
},
|
|
2984
|
+
"defaultValues": {
|
|
2985
|
+
"type": "Record<string, any>",
|
|
2986
|
+
"description": "Initial values when uncontrolled"
|
|
2987
|
+
},
|
|
2988
|
+
"onChange": {
|
|
2989
|
+
"type": "function",
|
|
2990
|
+
"description": "(values) => void — called whenever any field value changes"
|
|
2991
|
+
},
|
|
2992
|
+
"onSubmit": {
|
|
2993
|
+
"type": "function",
|
|
2994
|
+
"description": "(values) => void | Promise<void> — called ONLY when the form is valid"
|
|
2995
|
+
},
|
|
2996
|
+
"onInvalid": {
|
|
2997
|
+
"type": "function",
|
|
2998
|
+
"description": "(errors: Record<string,string>) => void — called when submitted but invalid"
|
|
2999
|
+
}
|
|
3000
|
+
},
|
|
3001
|
+
"antiPatterns": [
|
|
3002
|
+
{
|
|
3003
|
+
"pattern": "Manually tracking each field's error state with useState",
|
|
3004
|
+
"reason": "Form already validates and maps errors into Field automatically",
|
|
3005
|
+
"fix": "Wrap controls in <FormField name rules> and let Form manage errors"
|
|
3006
|
+
},
|
|
3007
|
+
{
|
|
3008
|
+
"pattern": "Adding a validation library (zod/yup) just for basic rules",
|
|
3009
|
+
"reason": "Form ships zero-dependency built-in rules",
|
|
3010
|
+
"fix": "Use rules={{ required, minLength, pattern, email, validate }}"
|
|
3011
|
+
},
|
|
3012
|
+
{
|
|
3013
|
+
"pattern": "<FormField> with multiple control children",
|
|
3014
|
+
"reason": "FormField clones a single child to inject name/value/onChange",
|
|
3015
|
+
"fix": "Use one control per FormField (Input/Textarea/Select/etc.)"
|
|
3016
|
+
},
|
|
3017
|
+
{
|
|
3018
|
+
"pattern": "<FormField> outside a <Form>",
|
|
3019
|
+
"reason": "FormField reads form context",
|
|
3020
|
+
"fix": "Always nest FormField inside <Form>"
|
|
3021
|
+
}
|
|
3022
|
+
],
|
|
3023
|
+
"examples": [
|
|
3024
|
+
{
|
|
3025
|
+
"description": "Controlled sign-in form with built-in rules",
|
|
3026
|
+
"code": "const [values, setValues] = useState({ email: \"\", password: \"\" });\n\n<Form values={values} onChange={setValues} onSubmit={(v) => signIn(v)}>\n <FormField name=\"email\" label=\"Email\" rules={{ required: true, email: true }}>\n <Input type=\"email\" />\n </FormField>\n <FormField name=\"password\" label=\"Password\" rules={{ required: true, minLength: 8 }}>\n <Input type=\"password\" />\n </FormField>\n <Button type=\"submit\" variant=\"primary\">Sign in</Button>\n</Form>"
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
"description": "Custom cross-field validation",
|
|
3030
|
+
"code": "<FormField\n name=\"confirm\"\n label=\"Confirm password\"\n rules={{\n required: true,\n validate: (v, all) => v === all.password ? null : \"Passwords do not match\",\n }}\n>\n <Input type=\"password\" />\n</FormField>"
|
|
3031
|
+
}
|
|
3032
|
+
]
|
|
3033
|
+
},
|
|
3034
|
+
"FormField": {
|
|
3035
|
+
"description": "A single labelled, validated field inside <Form>. Injects name/value/onChange/onBlur into its one control child and wraps it in <Field> (label + error state + hint).",
|
|
3036
|
+
"import": "import { FormField } from \"@usevyre/react\"",
|
|
3037
|
+
"props": {
|
|
3038
|
+
"name": {
|
|
3039
|
+
"type": "string",
|
|
3040
|
+
"description": "Key into the form's values map (required)"
|
|
3041
|
+
},
|
|
3042
|
+
"label": {
|
|
3043
|
+
"type": "string",
|
|
3044
|
+
"description": "Label rendered by the wrapping Field"
|
|
3045
|
+
},
|
|
3046
|
+
"hint": {
|
|
3047
|
+
"type": "string",
|
|
3048
|
+
"description": "Helper text shown when there is no error"
|
|
3049
|
+
},
|
|
3050
|
+
"rules": {
|
|
3051
|
+
"type": "object",
|
|
3052
|
+
"description": "{ required?: boolean|string, minLength?, maxLength?, min?, max?: number, pattern?: RegExp, email?: boolean, validate?: (value, allValues) => string|null }"
|
|
3053
|
+
}
|
|
3054
|
+
},
|
|
3055
|
+
"antiPatterns": [
|
|
3056
|
+
{
|
|
3057
|
+
"pattern": "Putting onChange/value manually on the control inside FormField",
|
|
3058
|
+
"reason": "FormField already injects them; manual props can desync from form state",
|
|
3059
|
+
"fix": "Let FormField wire the control; only pass static props (type, placeholder)"
|
|
3060
|
+
}
|
|
3061
|
+
],
|
|
3062
|
+
"examples": [
|
|
3063
|
+
{
|
|
3064
|
+
"description": "Field with a hint",
|
|
3065
|
+
"code": "<FormField name=\"bio\" label=\"Bio\" hint=\"Max 200 characters\" rules={{ maxLength: 200 }}>\n <Textarea />\n</FormField>"
|
|
3066
|
+
}
|
|
3067
|
+
]
|
|
3068
|
+
},
|
|
3069
|
+
"NumberInput": {
|
|
3070
|
+
"description": "Controlled numeric input with −/+ stepper buttons. onChange emits a NUMBER (or null when empty) — NOT an event. Drops straight into <FormField> (Form handles the non-event value). Clamps to min/max on blur; keyboard ArrowUp/Down ±step, Shift+Arrow ±step×10.",
|
|
3071
|
+
"import": "import { NumberInput } from \"@usevyre/react\"",
|
|
3072
|
+
"props": {
|
|
3073
|
+
"value": {
|
|
3074
|
+
"type": "number | null",
|
|
3075
|
+
"description": "Controlled value. null = empty. Omit for uncontrolled."
|
|
3076
|
+
},
|
|
3077
|
+
"defaultValue": {
|
|
3078
|
+
"type": "number | null",
|
|
3079
|
+
"default": "null",
|
|
3080
|
+
"description": "Initial value when uncontrolled"
|
|
3081
|
+
},
|
|
3082
|
+
"onChange": {
|
|
3083
|
+
"type": "function",
|
|
3084
|
+
"description": "(value: number | null) => void — emits the parsed number, or null when empty"
|
|
3085
|
+
},
|
|
3086
|
+
"min": {
|
|
3087
|
+
"type": "number",
|
|
3088
|
+
"description": "Minimum; value is clamped to this on blur"
|
|
3089
|
+
},
|
|
3090
|
+
"max": {
|
|
3091
|
+
"type": "number",
|
|
3092
|
+
"description": "Maximum; value is clamped to this on blur"
|
|
3093
|
+
},
|
|
3094
|
+
"step": {
|
|
3095
|
+
"type": "number",
|
|
3096
|
+
"default": 1,
|
|
3097
|
+
"description": "Increment/decrement amount"
|
|
3098
|
+
},
|
|
3099
|
+
"precision": {
|
|
3100
|
+
"type": "number",
|
|
3101
|
+
"description": "Decimal places to round to"
|
|
3102
|
+
},
|
|
3103
|
+
"size": {
|
|
3104
|
+
"type": "enum",
|
|
3105
|
+
"values": [
|
|
3106
|
+
"sm",
|
|
3107
|
+
"md",
|
|
3108
|
+
"lg"
|
|
3109
|
+
],
|
|
3110
|
+
"default": "md",
|
|
3111
|
+
"description": "Control size"
|
|
3112
|
+
},
|
|
3113
|
+
"disabled": {
|
|
3114
|
+
"type": "boolean",
|
|
3115
|
+
"default": false,
|
|
3116
|
+
"description": "Disable input and steppers"
|
|
3117
|
+
},
|
|
3118
|
+
"readOnly": {
|
|
3119
|
+
"type": "boolean",
|
|
3120
|
+
"default": false,
|
|
3121
|
+
"description": "Read-only (no stepping/typing)"
|
|
3122
|
+
}
|
|
3123
|
+
},
|
|
3124
|
+
"antiPatterns": [
|
|
3125
|
+
{
|
|
3126
|
+
"pattern": "onChange={(e) => set(e.target.value)}",
|
|
3127
|
+
"reason": "NumberInput emits a number, not an event — there is no e.target",
|
|
3128
|
+
"fix": "onChange={(value) => set(value)} — value is number | null"
|
|
3129
|
+
},
|
|
3130
|
+
{
|
|
3131
|
+
"pattern": "Using <Input type=\"number\"> for numeric fields",
|
|
3132
|
+
"reason": "Native number input has no token-styled stepper, no clamp, returns a string",
|
|
3133
|
+
"fix": "Use <NumberInput value onChange min max step />"
|
|
3134
|
+
},
|
|
3135
|
+
{
|
|
3136
|
+
"pattern": "Parsing the value with Number() in form state",
|
|
3137
|
+
"reason": "NumberInput already emits a real number (or null)",
|
|
3138
|
+
"fix": "Store the value directly; it is already number | null"
|
|
3139
|
+
}
|
|
3140
|
+
],
|
|
3141
|
+
"examples": [
|
|
3142
|
+
{
|
|
3143
|
+
"description": "Quantity selector",
|
|
3144
|
+
"code": "const [qty, setQty] = useState<number | null>(1);\n\n<NumberInput value={qty} onChange={setQty} min={1} max={99} />"
|
|
3145
|
+
},
|
|
3146
|
+
{
|
|
3147
|
+
"description": "Inside a Form",
|
|
3148
|
+
"code": "<FormField name=\"age\" label=\"Age\" rules={{ required: true, min: 18 }}>\n <NumberInput min={0} max={120} />\n</FormField>"
|
|
3149
|
+
}
|
|
3150
|
+
]
|
|
3151
|
+
},
|
|
3152
|
+
"ToggleGroup": {
|
|
3153
|
+
"description": "Segmented control. CONTROLLED — the group owns the value. onChange emits the VALUE (not an event). type=single → value:string|null; type=multiple → value:string[]. Provide options[] for simple lists or <ToggleItem value> children for custom content. Distinct from Switch (boolean), ButtonGroup (layout only), RadioGroup (form radios, single only).",
|
|
3154
|
+
"import": "import { ToggleGroup, ToggleItem } from \"@usevyre/react\"",
|
|
3155
|
+
"subcomponents": [
|
|
3156
|
+
"ToggleItem"
|
|
3157
|
+
],
|
|
3158
|
+
"props": {
|
|
3159
|
+
"type": {
|
|
3160
|
+
"type": "enum",
|
|
3161
|
+
"values": [
|
|
3162
|
+
"single",
|
|
3163
|
+
"multiple"
|
|
3164
|
+
],
|
|
3165
|
+
"default": "single",
|
|
3166
|
+
"description": "single → value string|null; multiple → value string[]"
|
|
3167
|
+
},
|
|
3168
|
+
"value": {
|
|
3169
|
+
"type": "string | null | string[]",
|
|
3170
|
+
"description": "Controlled selected value. string|null when single, string[] when multiple."
|
|
3171
|
+
},
|
|
3172
|
+
"onChange": {
|
|
3173
|
+
"type": "function",
|
|
3174
|
+
"description": "(value) => void — emits string|null (single) or string[] (multiple). NOT an event."
|
|
3175
|
+
},
|
|
3176
|
+
"options": {
|
|
3177
|
+
"type": "array",
|
|
3178
|
+
"description": "{ value: string; label?; icon?; disabled? }[] — alternative to ToggleItem children"
|
|
3179
|
+
},
|
|
3180
|
+
"size": {
|
|
3181
|
+
"type": "enum",
|
|
3182
|
+
"values": [
|
|
3183
|
+
"sm",
|
|
3184
|
+
"md",
|
|
3185
|
+
"lg"
|
|
3186
|
+
],
|
|
3187
|
+
"default": "md",
|
|
3188
|
+
"description": "Control size"
|
|
3189
|
+
},
|
|
3190
|
+
"orientation": {
|
|
3191
|
+
"type": "enum",
|
|
3192
|
+
"values": [
|
|
3193
|
+
"horizontal",
|
|
3194
|
+
"vertical"
|
|
3195
|
+
],
|
|
3196
|
+
"default": "horizontal",
|
|
3197
|
+
"description": "Layout direction"
|
|
3198
|
+
},
|
|
3199
|
+
"disabled": {
|
|
3200
|
+
"type": "boolean",
|
|
3201
|
+
"default": false,
|
|
3202
|
+
"description": "Disable the whole group"
|
|
3203
|
+
}
|
|
3204
|
+
},
|
|
3205
|
+
"antiPatterns": [
|
|
3206
|
+
{
|
|
3207
|
+
"pattern": "onChange={(e) => set(e.target.value)}",
|
|
3208
|
+
"reason": "ToggleGroup emits the value, not an event",
|
|
3209
|
+
"fix": "onChange={(value) => set(value)} — string|null (single) or string[] (multiple)"
|
|
3210
|
+
},
|
|
3211
|
+
{
|
|
3212
|
+
"pattern": "Using ToggleGroup for a single on/off setting",
|
|
3213
|
+
"reason": "That is a boolean toggle",
|
|
3214
|
+
"fix": "Use <Switch> for on/off; ToggleGroup is for choosing among options"
|
|
3215
|
+
},
|
|
3216
|
+
{
|
|
3217
|
+
"pattern": "type=\"multiple\" with a string value",
|
|
3218
|
+
"reason": "multiple mode requires an array value",
|
|
3219
|
+
"fix": "value={['a','b']} and onChange receives string[]"
|
|
3220
|
+
},
|
|
3221
|
+
{
|
|
3222
|
+
"pattern": "<ToggleItem> outside <ToggleGroup>",
|
|
3223
|
+
"reason": "ToggleItem reads group context",
|
|
3224
|
+
"fix": "Always nest ToggleItem inside ToggleGroup (or use options)"
|
|
3225
|
+
}
|
|
3226
|
+
],
|
|
3227
|
+
"examples": [
|
|
3228
|
+
{
|
|
3229
|
+
"description": "Single-select view switcher (options)",
|
|
3230
|
+
"code": "const [view, setView] = useState<string | null>(\"grid\");\n\n<ToggleGroup\n value={view}\n onChange={setView}\n options={[\n { value: \"grid\", label: \"Grid\" },\n { value: \"list\", label: \"List\" },\n ]}\n/>"
|
|
3231
|
+
},
|
|
3232
|
+
{
|
|
3233
|
+
"description": "Multiple-select formatting (composable)",
|
|
3234
|
+
"code": "const [fmt, setFmt] = useState<string[]>([\"bold\"]);\n\n<ToggleGroup type=\"multiple\" value={fmt} onChange={setFmt}>\n <ToggleItem value=\"bold\">B</ToggleItem>\n <ToggleItem value=\"italic\">I</ToggleItem>\n <ToggleItem value=\"underline\">U</ToggleItem>\n</ToggleGroup>"
|
|
3235
|
+
}
|
|
3236
|
+
]
|
|
3237
|
+
},
|
|
3238
|
+
"ToggleItem": {
|
|
3239
|
+
"description": "A single toggle button inside <ToggleGroup>. Reads selection state from the group via context.",
|
|
3240
|
+
"import": "import { ToggleItem } from \"@usevyre/react\"",
|
|
3241
|
+
"props": {
|
|
3242
|
+
"value": {
|
|
3243
|
+
"type": "string",
|
|
3244
|
+
"description": "This item's value (required)"
|
|
3245
|
+
},
|
|
3246
|
+
"icon": {
|
|
3247
|
+
"type": "ReactNode",
|
|
3248
|
+
"description": "Optional leading icon"
|
|
3249
|
+
},
|
|
3250
|
+
"disabled": {
|
|
3251
|
+
"type": "boolean",
|
|
3252
|
+
"default": false,
|
|
3253
|
+
"description": "Disable just this item"
|
|
3254
|
+
}
|
|
3255
|
+
},
|
|
3256
|
+
"antiPatterns": [
|
|
3257
|
+
{
|
|
3258
|
+
"pattern": "Tracking selected state on ToggleItem yourself",
|
|
3259
|
+
"reason": "Selection is owned by ToggleGroup",
|
|
3260
|
+
"fix": "Only set value; the group controls selected state"
|
|
3261
|
+
}
|
|
3262
|
+
],
|
|
3263
|
+
"examples": [
|
|
3264
|
+
{
|
|
3265
|
+
"description": "Composable items",
|
|
3266
|
+
"code": "<ToggleGroup value={v} onChange={setV}>\n <ToggleItem value=\"left\">Left</ToggleItem>\n <ToggleItem value=\"center\">Center</ToggleItem>\n</ToggleGroup>"
|
|
3267
|
+
}
|
|
3268
|
+
]
|
|
3269
|
+
},
|
|
3270
|
+
"Stepper": {
|
|
3271
|
+
"description": "Multi-step flow indicator + controller (onboarding/checkout/wizard). CONTROLLED by a 0-based index. Compose StepperNav (with Step indicators) and StepPanel (content shown when its index == active). Step/StepPanel take an explicit 0-based `index`. Not Tabs — Stepper is an ORDERED linear flow with completed/current/upcoming states.",
|
|
3272
|
+
"import": "import { Stepper, StepperNav, Step, StepPanel } from \"@usevyre/react\"",
|
|
3273
|
+
"subcomponents": [
|
|
3274
|
+
"StepperNav",
|
|
3275
|
+
"Step",
|
|
3276
|
+
"StepPanel"
|
|
3277
|
+
],
|
|
3278
|
+
"props": {
|
|
3279
|
+
"value": {
|
|
3280
|
+
"type": "number",
|
|
3281
|
+
"description": "Controlled active step (0-based). Omit for uncontrolled."
|
|
3282
|
+
},
|
|
3283
|
+
"defaultValue": {
|
|
3284
|
+
"type": "number",
|
|
3285
|
+
"default": 0,
|
|
3286
|
+
"description": "Initial active step when uncontrolled"
|
|
3287
|
+
},
|
|
3288
|
+
"onChange": {
|
|
3289
|
+
"type": "function",
|
|
3290
|
+
"description": "(index: number) => void — emits the new active index"
|
|
3291
|
+
},
|
|
3292
|
+
"orientation": {
|
|
3293
|
+
"type": "enum",
|
|
3294
|
+
"values": [
|
|
3295
|
+
"horizontal",
|
|
3296
|
+
"vertical"
|
|
3297
|
+
],
|
|
3298
|
+
"default": "horizontal",
|
|
3299
|
+
"description": "Layout direction"
|
|
3300
|
+
},
|
|
3301
|
+
"clickable": {
|
|
3302
|
+
"type": "boolean",
|
|
3303
|
+
"default": false,
|
|
3304
|
+
"description": "Allow clicking a completed Step to jump back to it"
|
|
3305
|
+
}
|
|
3306
|
+
},
|
|
3307
|
+
"antiPatterns": [
|
|
3308
|
+
{
|
|
3309
|
+
"pattern": "Using Tabs for a wizard / checkout flow",
|
|
3310
|
+
"reason": "Tabs are peer panels; a step flow is ordered with completed/current/upcoming state",
|
|
3311
|
+
"fix": "Use <Stepper> with StepperNav + Step + StepPanel"
|
|
3312
|
+
},
|
|
3313
|
+
{
|
|
3314
|
+
"pattern": "onChange={(e) => set(e.target.value)}",
|
|
3315
|
+
"reason": "Stepper emits the index number, not an event",
|
|
3316
|
+
"fix": "onChange={(index) => setStep(index)}"
|
|
3317
|
+
},
|
|
3318
|
+
{
|
|
3319
|
+
"pattern": "Manually toggling which panel is visible",
|
|
3320
|
+
"reason": "StepPanel renders itself only when its index === active",
|
|
3321
|
+
"fix": "Give each StepPanel an index; Stepper shows the active one"
|
|
3322
|
+
},
|
|
3323
|
+
{
|
|
3324
|
+
"pattern": "<Step> or <StepPanel> outside <Stepper>",
|
|
3325
|
+
"reason": "They read Stepper context",
|
|
3326
|
+
"fix": "Nest Step inside StepperNav, StepPanel inside Stepper"
|
|
3327
|
+
}
|
|
3328
|
+
],
|
|
3329
|
+
"examples": [
|
|
3330
|
+
{
|
|
3331
|
+
"description": "Three-step wizard with Back/Next",
|
|
3332
|
+
"code": "const [step, setStep] = useState(0);\n\n<Stepper value={step} onChange={setStep}>\n <StepperNav>\n <Step index={0} label=\"Account\" />\n <Step index={1} label=\"Profile\" />\n <Step index={2} label=\"Done\" />\n </StepperNav>\n <StepPanel index={0}><AccountForm /></StepPanel>\n <StepPanel index={1}><ProfileForm /></StepPanel>\n <StepPanel index={2}><Summary /></StepPanel>\n <Stack direction=\"row\" gap=\"sm\" justify=\"between\">\n <Button onClick={() => setStep((s) => s - 1)} disabled={step === 0}>Back</Button>\n <Button variant=\"primary\" onClick={() => setStep((s) => s + 1)}>Next</Button>\n </Stack>\n</Stepper>"
|
|
3333
|
+
},
|
|
3334
|
+
{
|
|
3335
|
+
"description": "Vertical with descriptions",
|
|
3336
|
+
"code": "<Stepper orientation=\"vertical\" defaultValue={1}>\n <StepperNav>\n <Step index={0} label=\"Cart\" description=\"2 items\" />\n <Step index={1} label=\"Shipping\" description=\"Enter address\" />\n <Step index={2} label=\"Payment\" />\n </StepperNav>\n</Stepper>"
|
|
3337
|
+
}
|
|
3338
|
+
]
|
|
3339
|
+
},
|
|
3340
|
+
"StepperNav": {
|
|
3341
|
+
"description": "Container for Step indicators inside <Stepper>. Lays them out per the Stepper's orientation.",
|
|
3342
|
+
"import": "import { Stepper, StepperNav, Step, StepPanel } from \"@usevyre/react\"",
|
|
3343
|
+
"props": {}
|
|
3344
|
+
},
|
|
3345
|
+
"Step": {
|
|
3346
|
+
"description": "One step indicator inside <StepperNav>. State (completed/current/upcoming) derives from the Stepper's active index automatically.",
|
|
3347
|
+
"import": "import { Stepper, StepperNav, Step, StepPanel } from \"@usevyre/react\"",
|
|
3348
|
+
"props": {
|
|
3349
|
+
"index": {
|
|
3350
|
+
"type": "number",
|
|
3351
|
+
"description": "0-based position (required)"
|
|
3352
|
+
},
|
|
3353
|
+
"label": {
|
|
3354
|
+
"type": "ReactNode",
|
|
3355
|
+
"description": "Step label"
|
|
3356
|
+
},
|
|
3357
|
+
"description": {
|
|
3358
|
+
"type": "ReactNode",
|
|
3359
|
+
"description": "Secondary text under the label"
|
|
3360
|
+
},
|
|
3361
|
+
"icon": {
|
|
3362
|
+
"type": "ReactNode",
|
|
3363
|
+
"description": "Custom indicator content (defaults to number, or ✓ when completed)"
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
},
|
|
3367
|
+
"StepPanel": {
|
|
3368
|
+
"description": "Content for one step. Renders its children only when its index equals the Stepper's active step.",
|
|
3369
|
+
"import": "import { Stepper, StepperNav, Step, StepPanel } from \"@usevyre/react\"",
|
|
3370
|
+
"props": {
|
|
3371
|
+
"index": {
|
|
3372
|
+
"type": "number",
|
|
3373
|
+
"description": "0-based position matching its Step (required)"
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
},
|
|
3377
|
+
"EmptyState": {
|
|
3378
|
+
"description": "Presentational placeholder for empty lists, tables, and search results. No state. title/description/variant/size are props; the optional call-to-action goes in children (React) or the default slot (Vue). variant picks a preset icon (default=box, search=magnifier, error=warning); pass `icon` (or #icon slot) to override.",
|
|
3379
|
+
"import": "import { EmptyState } from \"@usevyre/react\"",
|
|
3380
|
+
"props": {
|
|
3381
|
+
"title": {
|
|
3382
|
+
"type": "string",
|
|
3383
|
+
"description": "Headline (required)"
|
|
3384
|
+
},
|
|
3385
|
+
"description": {
|
|
3386
|
+
"type": "string",
|
|
3387
|
+
"description": "Supporting text under the title"
|
|
3388
|
+
},
|
|
3389
|
+
"variant": {
|
|
3390
|
+
"type": "enum",
|
|
3391
|
+
"values": [
|
|
3392
|
+
"default",
|
|
3393
|
+
"search",
|
|
3394
|
+
"error"
|
|
3395
|
+
],
|
|
3396
|
+
"default": "default",
|
|
3397
|
+
"description": "Preset icon: default=box, search=magnifier, error=warning"
|
|
3398
|
+
},
|
|
3399
|
+
"icon": {
|
|
3400
|
+
"type": "ReactNode",
|
|
3401
|
+
"description": "Custom icon, overrides the variant preset"
|
|
3402
|
+
},
|
|
3403
|
+
"size": {
|
|
3404
|
+
"type": "enum",
|
|
3405
|
+
"values": [
|
|
3406
|
+
"sm",
|
|
3407
|
+
"md",
|
|
3408
|
+
"lg"
|
|
3409
|
+
],
|
|
3410
|
+
"default": "md",
|
|
3411
|
+
"description": "sm for inline/in-Card, md default, lg for full-page"
|
|
3412
|
+
},
|
|
3413
|
+
"children": {
|
|
3414
|
+
"type": "ReactNode",
|
|
3415
|
+
"description": "The call-to-action (a Button, or buttons in a Stack)"
|
|
3416
|
+
}
|
|
3417
|
+
},
|
|
3418
|
+
"antiPatterns": [
|
|
3419
|
+
{
|
|
3420
|
+
"pattern": "Building an empty placeholder with a bare <div> + centered text",
|
|
3421
|
+
"reason": "Inconsistent spacing/typography; AI reinvents it each time",
|
|
3422
|
+
"fix": "Use <EmptyState title description variant>"
|
|
3423
|
+
},
|
|
3424
|
+
{
|
|
3425
|
+
"pattern": "action / cta prop",
|
|
3426
|
+
"reason": "There is no action prop; the CTA is composed",
|
|
3427
|
+
"fix": "Put the Button as children of EmptyState"
|
|
3428
|
+
},
|
|
3429
|
+
{
|
|
3430
|
+
"pattern": "Using EmptyState for a loading state",
|
|
3431
|
+
"reason": "EmptyState is for no-data, not in-progress",
|
|
3432
|
+
"fix": "Use <Skeleton> while loading; EmptyState when the result set is empty"
|
|
3433
|
+
}
|
|
3434
|
+
],
|
|
3435
|
+
"examples": [
|
|
3436
|
+
{
|
|
3437
|
+
"description": "Empty search results with a reset CTA",
|
|
3438
|
+
"code": "<EmptyState\n variant=\"search\"\n title=\"No results\"\n description=\"Try a different search term.\"\n>\n <Button variant=\"secondary\" onClick={reset}>Clear filters</Button>\n</EmptyState>"
|
|
3439
|
+
},
|
|
3440
|
+
{
|
|
3441
|
+
"description": "Full-page empty list",
|
|
3442
|
+
"code": "<EmptyState\n size=\"lg\"\n title=\"No projects yet\"\n description=\"Create your first project to get started.\"\n>\n <Button variant=\"primary\">New project</Button>\n</EmptyState>"
|
|
3443
|
+
}
|
|
3444
|
+
]
|
|
3445
|
+
},
|
|
3446
|
+
"Stat": {
|
|
3447
|
+
"description": "Presentational dashboard KPI. No state. The arrow DIRECTION follows the sign of `delta` (the actual change: -0.4% → down arrow). The arrow/delta COLOR is set explicitly by `trend` (up=success, down=danger, neutral=muted) — so 'churn -0.4%, trend=up' shows a green DOWN arrow. Wrap several in StatGroup for an evenly-split row with dividers.",
|
|
3448
|
+
"import": "import { Stat, StatGroup } from \"@usevyre/react\"",
|
|
3449
|
+
"subcomponents": [
|
|
3450
|
+
"StatGroup"
|
|
3451
|
+
],
|
|
3452
|
+
"props": {
|
|
3453
|
+
"label": {
|
|
3454
|
+
"type": "string",
|
|
3455
|
+
"description": "Metric name (required)"
|
|
3456
|
+
},
|
|
3457
|
+
"value": {
|
|
3458
|
+
"type": "string | number",
|
|
3459
|
+
"description": "The headline figure (required, rendered large)"
|
|
3460
|
+
},
|
|
3461
|
+
"delta": {
|
|
3462
|
+
"type": "string | number",
|
|
3463
|
+
"description": "The change amount, e.g. \"+12%\" or 24. Its sign sets the arrow direction (+ up, - down, 0/none flat)."
|
|
3464
|
+
},
|
|
3465
|
+
"trend": {
|
|
3466
|
+
"type": "enum",
|
|
3467
|
+
"values": [
|
|
3468
|
+
"up",
|
|
3469
|
+
"down",
|
|
3470
|
+
"neutral"
|
|
3471
|
+
],
|
|
3472
|
+
"default": "neutral",
|
|
3473
|
+
"description": "Sets only the COLOR — up=success, down=danger, neutral=muted. Does NOT set the arrow direction (that comes from delta's sign)."
|
|
3474
|
+
},
|
|
3475
|
+
"deltaLabel": {
|
|
3476
|
+
"type": "string",
|
|
3477
|
+
"description": "Context for the delta, e.g. \"vs last month\""
|
|
3478
|
+
},
|
|
3479
|
+
"icon": {
|
|
3480
|
+
"type": "ReactNode",
|
|
3481
|
+
"description": "Optional leading icon (Vue: #icon slot)"
|
|
3482
|
+
},
|
|
3483
|
+
"size": {
|
|
3484
|
+
"type": "enum",
|
|
3485
|
+
"values": [
|
|
3486
|
+
"sm",
|
|
3487
|
+
"md",
|
|
3488
|
+
"lg"
|
|
3489
|
+
],
|
|
3490
|
+
"default": "md",
|
|
3491
|
+
"description": "Value size"
|
|
3492
|
+
}
|
|
3493
|
+
},
|
|
3494
|
+
"antiPatterns": [
|
|
3495
|
+
{
|
|
3496
|
+
"pattern": "Assuming trend flips the arrow direction",
|
|
3497
|
+
"reason": "Arrow direction comes from the delta sign; trend only sets color",
|
|
3498
|
+
"fix": "delta=\"-0.4%\" always shows a down arrow; trend=\"up\" just colors it green"
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
"pattern": "Building a KPI card with Card + manual layout",
|
|
3502
|
+
"reason": "Inconsistent value/label/delta styling; AI reinvents it",
|
|
3503
|
+
"fix": "Use <Stat label value delta trend />"
|
|
3504
|
+
},
|
|
3505
|
+
{
|
|
3506
|
+
"pattern": "Laying out a KPI row with custom flex + dividers",
|
|
3507
|
+
"reason": "StatGroup already splits evenly with dividers",
|
|
3508
|
+
"fix": "Wrap the Stats in <StatGroup>"
|
|
3509
|
+
}
|
|
3510
|
+
],
|
|
3511
|
+
"examples": [
|
|
3512
|
+
{
|
|
3513
|
+
"description": "KPI row; note churn stays green while going down",
|
|
3514
|
+
"code": "<StatGroup>\n <Stat label=\"Revenue\" value=\"$48.2k\" delta=\"+12%\" trend=\"up\" deltaLabel=\"vs last month\" />\n <Stat label=\"Churn\" value=\"2.1%\" delta=\"-0.4%\" trend=\"up\" deltaLabel=\"lower is better\" />\n <Stat label=\"Orders\" value=\"1,204\" delta=\"0%\" trend=\"neutral\" />\n</StatGroup>"
|
|
3515
|
+
},
|
|
3516
|
+
{
|
|
3517
|
+
"description": "Single stat with icon",
|
|
3518
|
+
"code": "<Stat label=\"Active users\" value=\"12,840\" delta=\"+3.2%\" trend=\"up\"\n icon={<UsersIcon />} size=\"lg\" />"
|
|
3519
|
+
}
|
|
3520
|
+
]
|
|
3521
|
+
},
|
|
3522
|
+
"StatGroup": {
|
|
3523
|
+
"description": "Evenly-split row of <Stat> with subtle dividers between items. Each Stat flexes to equal width.",
|
|
3524
|
+
"import": "import { Stat, StatGroup } from \"@usevyre/react\"",
|
|
3525
|
+
"props": {},
|
|
3526
|
+
"antiPatterns": [
|
|
3527
|
+
{
|
|
3528
|
+
"pattern": "Putting non-Stat children in StatGroup",
|
|
3529
|
+
"reason": "Dividers/equal-split styling target .vyre-stat",
|
|
3530
|
+
"fix": "Only place <Stat> elements inside StatGroup"
|
|
3531
|
+
}
|
|
3532
|
+
],
|
|
3533
|
+
"examples": [
|
|
3534
|
+
{
|
|
3535
|
+
"description": "Three KPIs in a row",
|
|
3536
|
+
"code": "<StatGroup>\n <Stat label=\"MRR\" value=\"$9.6k\" delta=\"+5%\" trend=\"up\" />\n <Stat label=\"Refunds\" value=\"32\" delta=\"+8\" trend=\"down\" />\n</StatGroup>"
|
|
3537
|
+
}
|
|
3538
|
+
]
|
|
3539
|
+
},
|
|
3540
|
+
"Timeline": {
|
|
3541
|
+
"description": "Vertical activity feed for audit logs and history. Presentational — a status dot per item plus a connector line. Pass `items` for plain logs, or TimelineItem children for rich per-item content. Timeline does NOT reorder; pass items in the order you want shown.",
|
|
3542
|
+
"import": "import { Timeline, TimelineItem } from \"@usevyre/react\"",
|
|
3543
|
+
"subcomponents": [
|
|
3544
|
+
"TimelineItem"
|
|
3545
|
+
],
|
|
3546
|
+
"props": {
|
|
3547
|
+
"items": {
|
|
3548
|
+
"type": "array",
|
|
3549
|
+
"description": "{ title, time?, description?, status?, icon? }[] — alternative to TimelineItem children"
|
|
3550
|
+
},
|
|
3551
|
+
"children": {
|
|
3552
|
+
"type": "ReactNode",
|
|
3553
|
+
"description": "TimelineItem elements (use for rich content)"
|
|
3554
|
+
}
|
|
3555
|
+
},
|
|
3556
|
+
"antiPatterns": [
|
|
3557
|
+
{
|
|
3558
|
+
"pattern": "Building an activity log with a <ul> + manual dots/lines",
|
|
3559
|
+
"reason": "Inconsistent markers/spacing; AI reinvents it each time",
|
|
3560
|
+
"fix": "Use <Timeline items={[...]} /> or TimelineItem children"
|
|
3561
|
+
},
|
|
3562
|
+
{
|
|
3563
|
+
"pattern": "Using Stepper for a history/audit feed",
|
|
3564
|
+
"reason": "Stepper is an ordered flow with completed/current/upcoming; a feed is just past events",
|
|
3565
|
+
"fix": "Use <Timeline> for logs/history; Stepper for wizards"
|
|
3566
|
+
},
|
|
3567
|
+
{
|
|
3568
|
+
"pattern": "Expecting Timeline to sort by time",
|
|
3569
|
+
"reason": "Timeline renders items in given order",
|
|
3570
|
+
"fix": "Sort the array yourself (newest- or oldest-first)"
|
|
3571
|
+
}
|
|
3572
|
+
],
|
|
3573
|
+
"examples": [
|
|
3574
|
+
{
|
|
3575
|
+
"description": "Plain audit log via items",
|
|
3576
|
+
"code": "<Timeline\n items={[\n { title: \"Deployed v2.1\", time: \"2m ago\", status: \"success\" },\n { title: \"Build started\", time: \"5m ago\", status: \"info\" },\n { title: \"Push to main\", time: \"6m ago\" },\n ]}\n/>"
|
|
3577
|
+
},
|
|
3578
|
+
{
|
|
3579
|
+
"description": "Rich content via children",
|
|
3580
|
+
"code": "<Timeline>\n <TimelineItem title=\"Invoice paid\" time=\"Apr 2\" status=\"success\">\n <Text size=\"sm\">$1,200 — <a href=\"#\">view receipt</a></Text>\n </TimelineItem>\n <TimelineItem title=\"Invoice sent\" time=\"Mar 28\" status=\"info\" />\n</Timeline>"
|
|
3581
|
+
}
|
|
3582
|
+
]
|
|
3583
|
+
},
|
|
3584
|
+
"TimelineItem": {
|
|
3585
|
+
"description": "One entry in a <Timeline>. Renders a status-colored dot (or a custom icon), a title, an optional time, and optional rich content.",
|
|
3586
|
+
"import": "import { Timeline, TimelineItem } from \"@usevyre/react\"",
|
|
3587
|
+
"props": {
|
|
3588
|
+
"title": {
|
|
3589
|
+
"type": "ReactNode",
|
|
3590
|
+
"description": "Entry title (required)"
|
|
3591
|
+
},
|
|
3592
|
+
"time": {
|
|
3593
|
+
"type": "ReactNode",
|
|
3594
|
+
"description": "Timestamp, right-aligned"
|
|
3595
|
+
},
|
|
3596
|
+
"status": {
|
|
3597
|
+
"type": "enum",
|
|
3598
|
+
"values": [
|
|
3599
|
+
"default",
|
|
3600
|
+
"success",
|
|
3601
|
+
"warning",
|
|
3602
|
+
"danger",
|
|
3603
|
+
"info"
|
|
3604
|
+
],
|
|
3605
|
+
"default": "default",
|
|
3606
|
+
"description": "Colors the marker dot"
|
|
3607
|
+
},
|
|
3608
|
+
"icon": {
|
|
3609
|
+
"type": "ReactNode",
|
|
3610
|
+
"description": "Custom marker, overrides the status dot (Vue: #icon slot)"
|
|
3611
|
+
},
|
|
3612
|
+
"children": {
|
|
3613
|
+
"type": "ReactNode",
|
|
3614
|
+
"description": "Rich content under the title"
|
|
3615
|
+
}
|
|
3616
|
+
},
|
|
3617
|
+
"antiPatterns": [
|
|
3618
|
+
{
|
|
3619
|
+
"pattern": "<TimelineItem> outside <Timeline>",
|
|
3620
|
+
"reason": "Marker/connector layout assumes the Timeline list context",
|
|
3621
|
+
"fix": "Always nest TimelineItem inside Timeline"
|
|
3622
|
+
}
|
|
3623
|
+
],
|
|
3624
|
+
"examples": [
|
|
3625
|
+
{
|
|
3626
|
+
"description": "Item with rich content",
|
|
3627
|
+
"code": "<TimelineItem title=\"Comment added\" time=\"1h ago\" status=\"default\">\n <Text size=\"sm\">“Looks good to me 👍”</Text>\n</TimelineItem>"
|
|
3628
|
+
}
|
|
3629
|
+
]
|
|
3630
|
+
},
|
|
3631
|
+
"Tree": {
|
|
3632
|
+
"description": "Hierarchical tree view for file explorers and nested navigation. DATA-DRIVEN and CONTROLLED — pass a nested `data` array; the Tree renders recursively. Single selection. A node WITH children is a folder (click toggles expand); a leaf fires onSelect. Keyboard: ArrowUp/Down move, ArrowRight/Left expand/collapse, Enter/Space select.",
|
|
3633
|
+
"import": "import { Tree } from \"@usevyre/react\"",
|
|
3634
|
+
"props": {
|
|
3635
|
+
"data": {
|
|
3636
|
+
"type": "TreeNode[]",
|
|
3637
|
+
"description": "Nested: { id: string; label: ReactNode; icon?: ReactNode; disabled?: boolean; children?: TreeNode[] }"
|
|
3638
|
+
},
|
|
3639
|
+
"expandedIds": {
|
|
3640
|
+
"type": "string[]",
|
|
3641
|
+
"description": "Controlled set of expanded node ids. Omit for uncontrolled."
|
|
3642
|
+
},
|
|
3643
|
+
"defaultExpandedIds": {
|
|
3644
|
+
"type": "string[]",
|
|
3645
|
+
"default": "[]",
|
|
3646
|
+
"description": "Initially expanded ids when uncontrolled"
|
|
3647
|
+
},
|
|
3648
|
+
"onExpandedChange": {
|
|
3649
|
+
"type": "function",
|
|
3650
|
+
"description": "(ids: string[]) => void"
|
|
3651
|
+
},
|
|
3652
|
+
"selectedId": {
|
|
3653
|
+
"type": "string | null",
|
|
3654
|
+
"description": "Controlled selected node id. Omit for uncontrolled."
|
|
3655
|
+
},
|
|
3656
|
+
"defaultSelectedId": {
|
|
3657
|
+
"type": "string | null",
|
|
3658
|
+
"default": "null",
|
|
3659
|
+
"description": "Initial selection when uncontrolled"
|
|
3660
|
+
},
|
|
3661
|
+
"onSelect": {
|
|
3662
|
+
"type": "function",
|
|
3663
|
+
"description": "(id: string) => void — fired on click/Enter/Space"
|
|
3664
|
+
}
|
|
3665
|
+
},
|
|
3666
|
+
"antiPatterns": [
|
|
3667
|
+
{
|
|
3668
|
+
"pattern": "Rendering a nested <ul> tree by hand with manual expand state",
|
|
3669
|
+
"reason": "Reinvents recursion, keyboard nav, and a11y roles every time",
|
|
3670
|
+
"fix": "Pass a nested `data` array to <Tree> and control expandedIds/selectedId"
|
|
3671
|
+
},
|
|
3672
|
+
{
|
|
3673
|
+
"pattern": "onSelect={(e) => ...}",
|
|
3674
|
+
"reason": "Tree emits the node id, not an event",
|
|
3675
|
+
"fix": "onSelect={(id) => setSelected(id)}"
|
|
3676
|
+
},
|
|
3677
|
+
{
|
|
3678
|
+
"pattern": "Mutating the data array to expand/collapse",
|
|
3679
|
+
"reason": "Expansion is controlled via expandedIds, not the data",
|
|
3680
|
+
"fix": "Track expandedIds in state (or use defaultExpandedIds)"
|
|
3681
|
+
},
|
|
3682
|
+
{
|
|
3683
|
+
"pattern": "Using DropdownMenu submenus for a file tree",
|
|
3684
|
+
"reason": "Submenus are transient menus; a tree is persistent hierarchical content",
|
|
3685
|
+
"fix": "Use <Tree> for file explorers / nested nav"
|
|
3686
|
+
}
|
|
3687
|
+
],
|
|
3688
|
+
"examples": [
|
|
3689
|
+
{
|
|
3690
|
+
"description": "File explorer, controlled selection",
|
|
3691
|
+
"code": "const [sel, setSel] = useState<string | null>(\"src/a.ts\");\n\n<Tree\n data={[\n { id: \"src\", label: \"src\", children: [\n { id: \"src/a.ts\", label: \"a.ts\" },\n { id: \"src/b\", label: \"b\", children: [\n { id: \"src/b/c.ts\", label: \"c.ts\" },\n ]},\n ]},\n { id: \"README.md\", label: \"README.md\" },\n ]}\n selectedId={sel}\n onSelect={setSel}\n defaultExpandedIds={[\"src\"]}\n/>"
|
|
3692
|
+
},
|
|
3693
|
+
{
|
|
3694
|
+
"description": "Fully controlled expansion",
|
|
3695
|
+
"code": "const [open, setOpen] = useState<string[]>([\"root\"]);\n\n<Tree data={tree} expandedIds={open} onExpandedChange={setOpen} />"
|
|
3696
|
+
}
|
|
3697
|
+
]
|
|
3698
|
+
},
|
|
3699
|
+
"OTPInput": {
|
|
3700
|
+
"description": "Segmented one-time-code input for verification / 2FA. CONTROLLED. onChange emits the STRING value (not an event), and onComplete fires once when every slot is filled. Paste-aware (pasting a full code fills all slots), auto-advance on input, backspace moves to the previous slot, arrow keys navigate. Drops straight into <FormField>.",
|
|
3701
|
+
"import": "import { OTPInput } from \"@usevyre/react\"",
|
|
3702
|
+
"props": {
|
|
3703
|
+
"value": {
|
|
3704
|
+
"type": "string",
|
|
3705
|
+
"description": "Controlled value (length ≤ `length`). Omit for uncontrolled."
|
|
3706
|
+
},
|
|
3707
|
+
"defaultValue": {
|
|
3708
|
+
"type": "string",
|
|
3709
|
+
"default": "\"\"",
|
|
3710
|
+
"description": "Initial value when uncontrolled"
|
|
3711
|
+
},
|
|
3712
|
+
"onChange": {
|
|
3713
|
+
"type": "function",
|
|
3714
|
+
"description": "(value: string) => void — emits the combined code string, NOT an event"
|
|
3715
|
+
},
|
|
3716
|
+
"onComplete": {
|
|
3717
|
+
"type": "function",
|
|
3718
|
+
"description": "(value: string) => void — fired once when all slots are filled"
|
|
3719
|
+
},
|
|
3720
|
+
"length": {
|
|
3721
|
+
"type": "number",
|
|
3722
|
+
"default": 6,
|
|
3723
|
+
"description": "Number of slots"
|
|
3724
|
+
},
|
|
3725
|
+
"type": {
|
|
3726
|
+
"type": "enum",
|
|
3727
|
+
"values": [
|
|
3728
|
+
"numeric",
|
|
3729
|
+
"alphanumeric"
|
|
3730
|
+
],
|
|
3731
|
+
"default": "numeric",
|
|
3732
|
+
"description": "Accepted characters"
|
|
3733
|
+
},
|
|
3734
|
+
"mask": {
|
|
3735
|
+
"type": "boolean",
|
|
3736
|
+
"default": false,
|
|
3737
|
+
"description": "Render dots instead of characters (like a password)"
|
|
3738
|
+
},
|
|
3739
|
+
"size": {
|
|
3740
|
+
"type": "enum",
|
|
3741
|
+
"values": [
|
|
3742
|
+
"sm",
|
|
3743
|
+
"md",
|
|
3744
|
+
"lg"
|
|
3745
|
+
],
|
|
3746
|
+
"default": "md",
|
|
3747
|
+
"description": "Slot size"
|
|
3748
|
+
},
|
|
3749
|
+
"disabled": {
|
|
3750
|
+
"type": "boolean",
|
|
3751
|
+
"default": false,
|
|
3752
|
+
"description": "Disable all slots"
|
|
3753
|
+
},
|
|
3754
|
+
"autoFocus": {
|
|
3755
|
+
"type": "boolean",
|
|
3756
|
+
"default": false,
|
|
3757
|
+
"description": "Focus the first slot on mount"
|
|
3758
|
+
}
|
|
3759
|
+
},
|
|
3760
|
+
"antiPatterns": [
|
|
3761
|
+
{
|
|
3762
|
+
"pattern": "onChange={(e) => set(e.target.value)}",
|
|
3763
|
+
"reason": "OTPInput emits the code string, not an event — there is no e.target",
|
|
3764
|
+
"fix": "onChange={(value) => setCode(value)}"
|
|
3765
|
+
},
|
|
3766
|
+
{
|
|
3767
|
+
"pattern": "Six separate <Input> boxes wired by hand",
|
|
3768
|
+
"reason": "Reinvents paste, auto-advance, backspace and focus management",
|
|
3769
|
+
"fix": "Use <OTPInput length={6} value onChange />"
|
|
3770
|
+
},
|
|
3771
|
+
{
|
|
3772
|
+
"pattern": "Reading completion by comparing length yourself",
|
|
3773
|
+
"reason": "onComplete already fires when the code is full",
|
|
3774
|
+
"fix": "Use onComplete={(code) => verify(code)}"
|
|
3775
|
+
},
|
|
3776
|
+
{
|
|
3777
|
+
"pattern": "type=\"password\" to hide digits",
|
|
3778
|
+
"reason": "There is no type=password; masking is a dedicated prop",
|
|
3779
|
+
"fix": "Use mask (type stays numeric/alphanumeric)"
|
|
3780
|
+
}
|
|
3781
|
+
],
|
|
3782
|
+
"examples": [
|
|
3783
|
+
{
|
|
3784
|
+
"description": "2FA code with verify on complete",
|
|
3785
|
+
"code": "const [code, setCode] = useState(\"\");\n\n<OTPInput\n value={code}\n onChange={setCode}\n onComplete={(c) => verify(c)}\n autoFocus\n/>"
|
|
3786
|
+
},
|
|
3787
|
+
{
|
|
3788
|
+
"description": "Inside a Form",
|
|
3789
|
+
"code": "<FormField name=\"otp\" label=\"Verification code\"\n rules={{ required: true, minLength: 6 }}>\n <OTPInput length={6} />\n</FormField>"
|
|
3790
|
+
}
|
|
3791
|
+
]
|
|
3792
|
+
},
|
|
3793
|
+
"Carousel": {
|
|
3794
|
+
"description": "Accessible content slider for galleries, onboarding, and testimonials. CONTROLLED by a 0-based slide index. Compose CarouselSlide children (slide order = index). Snap scrolling, clickable dot indicators, prev/next arrows, ArrowLeft/Right keyboard, optional loop and autoPlay (autoplay pauses on hover/focus). onChange emits the index (not an event).",
|
|
3795
|
+
"import": "import { Carousel, CarouselSlide } from \"@usevyre/react\"",
|
|
3796
|
+
"subcomponents": [
|
|
3797
|
+
"CarouselSlide"
|
|
3798
|
+
],
|
|
3799
|
+
"props": {
|
|
3800
|
+
"value": {
|
|
3801
|
+
"type": "number",
|
|
3802
|
+
"description": "Controlled active slide (0-based). Omit for uncontrolled."
|
|
3803
|
+
},
|
|
3804
|
+
"defaultValue": {
|
|
3805
|
+
"type": "number",
|
|
3806
|
+
"default": 0,
|
|
3807
|
+
"description": "Initial slide when uncontrolled"
|
|
3808
|
+
},
|
|
3809
|
+
"onChange": {
|
|
3810
|
+
"type": "function",
|
|
3811
|
+
"description": "(index: number) => void — emits the new active slide index"
|
|
3812
|
+
},
|
|
3813
|
+
"loop": {
|
|
3814
|
+
"type": "boolean",
|
|
3815
|
+
"default": false,
|
|
3816
|
+
"description": "Wrap past the first/last slide"
|
|
3817
|
+
},
|
|
3818
|
+
"autoPlay": {
|
|
3819
|
+
"type": "boolean",
|
|
3820
|
+
"default": false,
|
|
3821
|
+
"description": "Advance automatically; pauses on hover/focus"
|
|
3822
|
+
},
|
|
3823
|
+
"interval": {
|
|
3824
|
+
"type": "number",
|
|
3825
|
+
"default": 5000,
|
|
3826
|
+
"description": "Autoplay interval in ms"
|
|
3827
|
+
},
|
|
3828
|
+
"showArrows": {
|
|
3829
|
+
"type": "boolean",
|
|
3830
|
+
"default": true,
|
|
3831
|
+
"description": "Show prev/next arrow buttons"
|
|
3832
|
+
},
|
|
3833
|
+
"showIndicators": {
|
|
3834
|
+
"type": "boolean",
|
|
3835
|
+
"default": true,
|
|
3836
|
+
"description": "Show clickable dot indicators"
|
|
3837
|
+
}
|
|
3838
|
+
},
|
|
3839
|
+
"antiPatterns": [
|
|
3840
|
+
{
|
|
3841
|
+
"pattern": "onChange={(e) => set(e.target.value)}",
|
|
3842
|
+
"reason": "Carousel emits the slide index number, not an event",
|
|
3843
|
+
"fix": "onChange={(index) => setIndex(index)}"
|
|
3844
|
+
},
|
|
3845
|
+
{
|
|
3846
|
+
"pattern": "Putting raw elements directly in Carousel",
|
|
3847
|
+
"reason": "Slides must be CarouselSlide so snap/indices/aria work",
|
|
3848
|
+
"fix": "Wrap each slide in <CarouselSlide>"
|
|
3849
|
+
},
|
|
3850
|
+
{
|
|
3851
|
+
"pattern": "Building a slider with manual scroll + dot state",
|
|
3852
|
+
"reason": "Reinvents snap, keyboard, autoplay-pause and a11y roles",
|
|
3853
|
+
"fix": "Use <Carousel> with CarouselSlide children"
|
|
3854
|
+
},
|
|
3855
|
+
{
|
|
3856
|
+
"pattern": "autoPlay without considering reduced motion / pausing",
|
|
3857
|
+
"reason": "Autoplay can be an accessibility problem if it never pauses",
|
|
3858
|
+
"fix": "Carousel already pauses on hover/focus; keep interval reasonable or omit autoPlay"
|
|
3859
|
+
}
|
|
3860
|
+
],
|
|
3861
|
+
"examples": [
|
|
3862
|
+
{
|
|
3863
|
+
"description": "Image gallery with loop",
|
|
3864
|
+
"code": "const [i, setI] = useState(0);\n\n<Carousel value={i} onChange={setI} loop>\n <CarouselSlide><img src=\"/a.jpg\" alt=\"A\" /></CarouselSlide>\n <CarouselSlide><img src=\"/b.jpg\" alt=\"B\" /></CarouselSlide>\n <CarouselSlide><img src=\"/c.jpg\" alt=\"C\" /></CarouselSlide>\n</Carousel>"
|
|
3865
|
+
},
|
|
3866
|
+
{
|
|
3867
|
+
"description": "Onboarding, autoplay, no arrows",
|
|
3868
|
+
"code": "<Carousel autoPlay interval={4000} showArrows={false}>\n <CarouselSlide><Welcome /></CarouselSlide>\n <CarouselSlide><Features /></CarouselSlide>\n <CarouselSlide><GetStarted /></CarouselSlide>\n</Carousel>"
|
|
3869
|
+
}
|
|
3870
|
+
]
|
|
3871
|
+
},
|
|
3872
|
+
"CarouselSlide": {
|
|
3873
|
+
"description": "One slide inside <Carousel>. Holds arbitrary content (image, Card, testimonial). Slide order determines its index.",
|
|
3874
|
+
"import": "import { Carousel, CarouselSlide } from \"@usevyre/react\"",
|
|
3875
|
+
"props": {},
|
|
3876
|
+
"antiPatterns": [
|
|
3877
|
+
{
|
|
3878
|
+
"pattern": "<CarouselSlide> outside <Carousel>",
|
|
3879
|
+
"reason": "Snap/measurement/aria depend on the Carousel context",
|
|
3880
|
+
"fix": "Always nest CarouselSlide inside Carousel"
|
|
3881
|
+
}
|
|
3882
|
+
],
|
|
3883
|
+
"examples": [
|
|
3884
|
+
{
|
|
3885
|
+
"description": "Card slide",
|
|
3886
|
+
"code": "<CarouselSlide>\n <Card><CardBody>“Best tool ever.” — Ada</CardBody></Card>\n</CarouselSlide>"
|
|
3887
|
+
}
|
|
3888
|
+
]
|
|
3889
|
+
},
|
|
2918
3890
|
"DateRangePicker": {
|
|
2919
3891
|
"description": "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.",
|
|
2920
3892
|
"import": "import { DateRangePicker } from \"@usevyre/react\"",
|