ionbase-ui 0.81.1 → 0.86.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/components/Checkbox.d.ts +39 -0
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +77 -4
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Fieldset.d.ts +62 -0
- package/dist/components/Fieldset.d.ts.map +1 -0
- package/dist/components/Fieldset.js +58 -0
- package/dist/components/Fieldset.js.map +1 -0
- package/dist/components/Menu.d.ts +87 -24
- package/dist/components/Menu.d.ts.map +1 -1
- package/dist/components/Menu.js +245 -28
- package/dist/components/Menu.js.map +1 -1
- package/dist/components/PageHeader.d.ts +59 -0
- package/dist/components/PageHeader.d.ts.map +1 -0
- package/dist/components/PageHeader.js +29 -0
- package/dist/components/PageHeader.js.map +1 -0
- package/dist/components/Radio.d.ts +14 -0
- package/dist/components/Radio.d.ts.map +1 -1
- package/dist/components/Radio.js +15 -4
- package/dist/components/Radio.js.map +1 -1
- package/dist/components/SearchField.d.ts +36 -0
- package/dist/components/SearchField.d.ts.map +1 -0
- package/dist/components/SearchField.js +76 -0
- package/dist/components/SearchField.js.map +1 -0
- package/dist/components/index.d.ts +10 -4
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -2
- package/dist/components/index.js.map +1 -1
- package/dist/figma-descriptions.json +96 -76
- package/dist/figma-map.json +272 -39
- package/dist/meta/Checkbox.json +8 -6
- package/dist/meta/CheckboxGroup.json +268 -0
- package/dist/meta/Divider.json +2 -2
- package/dist/meta/Fieldset.json +162 -0
- package/dist/meta/Menu.json +201 -27
- package/dist/meta/MenuItem.json +137 -17
- package/dist/meta/MenuSection.json +90 -0
- package/dist/meta/MenuTrigger.json +177 -0
- package/dist/meta/PageHeader.json +169 -0
- package/dist/meta/Radio.json +0 -1
- package/dist/meta/RadioGroup.json +59 -7
- package/dist/meta/SearchField.json +487 -0
- package/dist/meta/Stepper.json +2 -2
- package/dist/meta/Tabs.json +3 -3
- package/dist/meta/components.json +1917 -217
- package/dist/meta/contrast.json +594 -90
- package/dist/meta/index.json +89 -10
- package/dist/meta/patterns/DataTable.json +14 -6
- package/dist/meta/patterns/Form.json +27 -2
- package/dist/meta/patterns/PageShell.json +18 -5
- package/dist/meta/patterns/index.json +5 -2
- package/dist/styles/fieldset.css +65 -0
- package/dist/styles/index.css +3 -0
- package/dist/styles/menu.css +153 -32
- package/dist/styles/page-header.css +94 -0
- package/dist/styles/radio.css +0 -20
- package/dist/styles/search-field.css +70 -0
- package/llms.txt +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "ionbase-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0",
|
|
4
4
|
"generated": "by scripts/build-meta.mjs — do not edit; intent lives in meta/*.json",
|
|
5
5
|
"hooks": [
|
|
6
6
|
"useAgentRun",
|
|
@@ -3080,8 +3080,7 @@
|
|
|
3080
3080
|
"summary": "A native checkbox with the system's styling, supporting an indeterminate state.",
|
|
3081
3081
|
"useWhen": [
|
|
3082
3082
|
"a single on/off choice inside a form that is submitted",
|
|
3083
|
-
"selecting rows in a Table"
|
|
3084
|
-
"several independent options where more than one may be picked"
|
|
3083
|
+
"selecting rows in a Table"
|
|
3085
3084
|
],
|
|
3086
3085
|
"useInstead": [
|
|
3087
3086
|
{
|
|
@@ -3089,6 +3088,11 @@
|
|
|
3089
3088
|
"use": "Toggle",
|
|
3090
3089
|
"why": "a switch reads as a setting taking effect now; a checkbox reads as a value being collected"
|
|
3091
3090
|
},
|
|
3091
|
+
{
|
|
3092
|
+
"when": "several options answer one question",
|
|
3093
|
+
"use": "CheckboxGroup",
|
|
3094
|
+
"why": "it announces the question with each option, carries the group's error, and can require at least one"
|
|
3095
|
+
},
|
|
3092
3096
|
{
|
|
3093
3097
|
"when": "the options are mutually exclusive",
|
|
3094
3098
|
"use": "RadioGroup"
|
|
@@ -3220,8 +3224,7 @@
|
|
|
3220
3224
|
"sm",
|
|
3221
3225
|
"md",
|
|
3222
3226
|
"lg"
|
|
3223
|
-
]
|
|
3224
|
-
"default": "md"
|
|
3227
|
+
]
|
|
3225
3228
|
},
|
|
3226
3229
|
"intent": {
|
|
3227
3230
|
"type": "CheckboxIntent | undefined",
|
|
@@ -3232,8 +3235,7 @@
|
|
|
3232
3235
|
"neutral",
|
|
3233
3236
|
"danger",
|
|
3234
3237
|
"brand"
|
|
3235
|
-
]
|
|
3236
|
-
"default": "brand"
|
|
3238
|
+
]
|
|
3237
3239
|
},
|
|
3238
3240
|
"isIndeterminate": {
|
|
3239
3241
|
"type": "boolean | undefined",
|
|
@@ -3282,6 +3284,274 @@
|
|
|
3282
3284
|
"other": 0
|
|
3283
3285
|
}
|
|
3284
3286
|
},
|
|
3287
|
+
"CheckboxGroup": {
|
|
3288
|
+
"name": "CheckboxGroup",
|
|
3289
|
+
"source": "src/components/Checkbox.tsx",
|
|
3290
|
+
"propsType": "CheckboxGroupProps",
|
|
3291
|
+
"description": "A set of checkboxes that answers one question — \"notify me when…\", \"which\nregions\". Owns the selected values, the group's label, help text and error,\nand \"select at least one\".\n\nA row of loose Checkboxes can do none of that accessibly: the question is\nnot announced with the options, an error has nothing to attach to, and\n\"at least one\" has no native expression at all. See the `required` note in\nCheckbox for how that last one is done.",
|
|
3292
|
+
"import": "import { CheckboxGroup } from 'ionbase-ui';",
|
|
3293
|
+
"status": "stable",
|
|
3294
|
+
"summary": "A set of checkboxes answering one question. Owns the selected values, the group's label, help and error, and \"select at least one\".",
|
|
3295
|
+
"useWhen": [
|
|
3296
|
+
"several options may be picked and they answer one question — \"notify me when\", \"regions\", \"columns to show\"",
|
|
3297
|
+
"the group needs a rule of its own — at least one, or an error that belongs to the set rather than to an option"
|
|
3298
|
+
],
|
|
3299
|
+
"useInstead": [
|
|
3300
|
+
{
|
|
3301
|
+
"when": "only one option may be picked",
|
|
3302
|
+
"use": "RadioGroup"
|
|
3303
|
+
},
|
|
3304
|
+
{
|
|
3305
|
+
"when": "the options are many, or space is tight",
|
|
3306
|
+
"use": "Select",
|
|
3307
|
+
"why": "past about seven options a list of boxes is longer than the form around it"
|
|
3308
|
+
},
|
|
3309
|
+
{
|
|
3310
|
+
"when": "there is one on/off choice",
|
|
3311
|
+
"use": "Checkbox",
|
|
3312
|
+
"why": "a group of one announces a question and then its only answer"
|
|
3313
|
+
},
|
|
3314
|
+
{
|
|
3315
|
+
"when": "each option takes effect immediately",
|
|
3316
|
+
"use": "Toggle"
|
|
3317
|
+
}
|
|
3318
|
+
],
|
|
3319
|
+
"composition": {
|
|
3320
|
+
"order": [
|
|
3321
|
+
"CheckboxGroup",
|
|
3322
|
+
"Checkbox"
|
|
3323
|
+
],
|
|
3324
|
+
"note": "Give every Checkbox a `value`; the group's `onChange` receives the array of ticked values. Selection lives on the group — do not also set `isSelected` on the boxes.",
|
|
3325
|
+
"example": "<CheckboxGroup label=\"Notify the team when\" isRequired value={notifyOn} onChange={setNotifyOn} isInvalid={!!error} errorMessage={error}><Checkbox value=\"failed\">A run fails</Checkbox><Checkbox value=\"approval\">A run needs approval</Checkbox></CheckboxGroup>"
|
|
3326
|
+
},
|
|
3327
|
+
"variants": {
|
|
3328
|
+
"size": {
|
|
3329
|
+
"sm": {
|
|
3330
|
+
"use": "dense forms and filter panels"
|
|
3331
|
+
},
|
|
3332
|
+
"md": {
|
|
3333
|
+
"use": "the default"
|
|
3334
|
+
},
|
|
3335
|
+
"lg": {
|
|
3336
|
+
"use": "touch targets and prominent choices"
|
|
3337
|
+
}
|
|
3338
|
+
},
|
|
3339
|
+
"intent": {
|
|
3340
|
+
"brand": {
|
|
3341
|
+
"use": "the default"
|
|
3342
|
+
},
|
|
3343
|
+
"neutral": {
|
|
3344
|
+
"use": "facts rather than approvals — the columns shown, the filters applied"
|
|
3345
|
+
},
|
|
3346
|
+
"danger": {
|
|
3347
|
+
"use": "a set of destructive consents"
|
|
3348
|
+
}
|
|
3349
|
+
},
|
|
3350
|
+
"orientation": {
|
|
3351
|
+
"vertical": {
|
|
3352
|
+
"use": "the default, and always when a label runs past a few words"
|
|
3353
|
+
},
|
|
3354
|
+
"horizontal": {
|
|
3355
|
+
"use": "two to four one-word options — weekdays, regions. Wraps rather than overflows"
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
},
|
|
3359
|
+
"slots": {
|
|
3360
|
+
"label": {
|
|
3361
|
+
"accepts": "text",
|
|
3362
|
+
"note": "renders as the <legend> — the question the options answer"
|
|
3363
|
+
},
|
|
3364
|
+
"description": {
|
|
3365
|
+
"accepts": "text",
|
|
3366
|
+
"note": "help beneath the options; replaced by `errorMessage` while `isInvalid` is set"
|
|
3367
|
+
},
|
|
3368
|
+
"errorMessage": {
|
|
3369
|
+
"accepts": "text",
|
|
3370
|
+
"note": "shown only while `isInvalid` is set"
|
|
3371
|
+
},
|
|
3372
|
+
"children": {
|
|
3373
|
+
"accepts": "Checkbox"
|
|
3374
|
+
}
|
|
3375
|
+
},
|
|
3376
|
+
"a11y": {
|
|
3377
|
+
"role": "group, from <fieldset> and <legend>",
|
|
3378
|
+
"guarantees": [
|
|
3379
|
+
"the legend is announced as the group's name when focus enters any box",
|
|
3380
|
+
"the description or error is on every box's `aria-describedby` as well as the fieldset's, so it is read on the box that takes focus",
|
|
3381
|
+
"`isInvalid` sets `aria-invalid` on every box",
|
|
3382
|
+
"`isRequired` sets native `required` on every box while none is ticked and on none once one is: the browser blocks the submit in its own language, and each box announces \"required\" exactly while the rule is unmet",
|
|
3383
|
+
"`isDisabled` cascades to every Checkbox that has not set its own"
|
|
3384
|
+
],
|
|
3385
|
+
"requires": [
|
|
3386
|
+
"`label`, or `aria-label` when the question is already visible elsewhere",
|
|
3387
|
+
"a `value` on every Checkbox — the group tracks the selection by it"
|
|
3388
|
+
]
|
|
3389
|
+
},
|
|
3390
|
+
"antiPatterns": [
|
|
3391
|
+
{
|
|
3392
|
+
"dont": "loose Checkboxes under a text label",
|
|
3393
|
+
"why": "the question is not announced with the options, an error has nothing to attach to, and \"at least one\" cannot be expressed"
|
|
3394
|
+
},
|
|
3395
|
+
{
|
|
3396
|
+
"dont": "`isSelected` or `checked` on a Checkbox inside a group",
|
|
3397
|
+
"why": "the group owns the selection; the box's own value is ignored"
|
|
3398
|
+
},
|
|
3399
|
+
{
|
|
3400
|
+
"dont": "`isRequired` on every Checkbox in the group",
|
|
3401
|
+
"why": "that means every box must be ticked; `isRequired` on the group means at least one"
|
|
3402
|
+
}
|
|
3403
|
+
],
|
|
3404
|
+
"stylesheet": "src/styles/checkbox.css",
|
|
3405
|
+
"tokens": [
|
|
3406
|
+
"--border-disabled",
|
|
3407
|
+
"--border-error-strong",
|
|
3408
|
+
"--border-focus",
|
|
3409
|
+
"--border-inverse",
|
|
3410
|
+
"--border-primary-strong",
|
|
3411
|
+
"--border-stronger",
|
|
3412
|
+
"--border-width-default",
|
|
3413
|
+
"--border-width-thick",
|
|
3414
|
+
"--font-family-sans",
|
|
3415
|
+
"--icon-disabled",
|
|
3416
|
+
"--icon-on-color",
|
|
3417
|
+
"--ion-checkbox-border",
|
|
3418
|
+
"--ion-checkbox-fill",
|
|
3419
|
+
"--ion-checkbox-font-size",
|
|
3420
|
+
"--ion-checkbox-gap",
|
|
3421
|
+
"--ion-checkbox-line-height",
|
|
3422
|
+
"--ion-checkbox-mark",
|
|
3423
|
+
"--ion-checkbox-radius",
|
|
3424
|
+
"--ion-checkbox-raised",
|
|
3425
|
+
"--ion-checkbox-size",
|
|
3426
|
+
"--ion-duration-base",
|
|
3427
|
+
"--ion-ease-out",
|
|
3428
|
+
"--ion-shadow-raised-flush-lg",
|
|
3429
|
+
"--ion-shadow-raised-flush-sm",
|
|
3430
|
+
"--ion-shadow-raised-flush-xs",
|
|
3431
|
+
"--radius-sm",
|
|
3432
|
+
"--radius-xs",
|
|
3433
|
+
"--spacing-12",
|
|
3434
|
+
"--spacing-16",
|
|
3435
|
+
"--spacing-20",
|
|
3436
|
+
"--spacing-24",
|
|
3437
|
+
"--spacing-4",
|
|
3438
|
+
"--spacing-8",
|
|
3439
|
+
"--surface-default",
|
|
3440
|
+
"--surface-disabled",
|
|
3441
|
+
"--surface-error",
|
|
3442
|
+
"--surface-inverse",
|
|
3443
|
+
"--surface-primary",
|
|
3444
|
+
"--text-disabled",
|
|
3445
|
+
"--text-secondary",
|
|
3446
|
+
"--type-body",
|
|
3447
|
+
"--type-body-line-height",
|
|
3448
|
+
"--type-body-sm",
|
|
3449
|
+
"--type-body-sm-line-height"
|
|
3450
|
+
],
|
|
3451
|
+
"props": {
|
|
3452
|
+
"label": {
|
|
3453
|
+
"type": "React.ReactNode",
|
|
3454
|
+
"required": false,
|
|
3455
|
+
"origin": "own",
|
|
3456
|
+
"description": "The question the options answer. Renders as the `<legend>`."
|
|
3457
|
+
},
|
|
3458
|
+
"description": {
|
|
3459
|
+
"type": "React.ReactNode",
|
|
3460
|
+
"required": false,
|
|
3461
|
+
"origin": "own",
|
|
3462
|
+
"description": "Help text beneath the options. Replaced by `errorMessage` while invalid."
|
|
3463
|
+
},
|
|
3464
|
+
"errorMessage": {
|
|
3465
|
+
"type": "React.ReactNode",
|
|
3466
|
+
"required": false,
|
|
3467
|
+
"origin": "own",
|
|
3468
|
+
"description": "Shown in the description's place while `isInvalid` is set."
|
|
3469
|
+
},
|
|
3470
|
+
"isInvalid": {
|
|
3471
|
+
"type": "boolean | undefined",
|
|
3472
|
+
"required": false,
|
|
3473
|
+
"origin": "own",
|
|
3474
|
+
"description": "Marks every box invalid and shows `errorMessage`."
|
|
3475
|
+
},
|
|
3476
|
+
"isRequired": {
|
|
3477
|
+
"type": "boolean | undefined",
|
|
3478
|
+
"required": false,
|
|
3479
|
+
"origin": "own",
|
|
3480
|
+
"description": "At least one option must be selected — enforced by native validation."
|
|
3481
|
+
},
|
|
3482
|
+
"value": {
|
|
3483
|
+
"type": "readonly string[] | undefined",
|
|
3484
|
+
"required": false,
|
|
3485
|
+
"origin": "own",
|
|
3486
|
+
"description": "The selected values (controlled)."
|
|
3487
|
+
},
|
|
3488
|
+
"defaultValue": {
|
|
3489
|
+
"type": "readonly string[] | undefined",
|
|
3490
|
+
"required": false,
|
|
3491
|
+
"origin": "own",
|
|
3492
|
+
"description": "The initially selected values (uncontrolled)."
|
|
3493
|
+
},
|
|
3494
|
+
"onChange": {
|
|
3495
|
+
"type": "((value: string[]) => void) | undefined",
|
|
3496
|
+
"required": false,
|
|
3497
|
+
"origin": "own",
|
|
3498
|
+
"description": "Receives the whole new selection, in the order the options were ticked."
|
|
3499
|
+
},
|
|
3500
|
+
"name": {
|
|
3501
|
+
"type": "string | undefined",
|
|
3502
|
+
"required": false,
|
|
3503
|
+
"origin": "own",
|
|
3504
|
+
"description": "Shared input name, so a form submits every ticked value under it."
|
|
3505
|
+
},
|
|
3506
|
+
"size": {
|
|
3507
|
+
"type": "CheckboxSize | undefined",
|
|
3508
|
+
"required": false,
|
|
3509
|
+
"origin": "own",
|
|
3510
|
+
"values": [
|
|
3511
|
+
"sm",
|
|
3512
|
+
"md",
|
|
3513
|
+
"lg"
|
|
3514
|
+
]
|
|
3515
|
+
},
|
|
3516
|
+
"intent": {
|
|
3517
|
+
"type": "CheckboxIntent | undefined",
|
|
3518
|
+
"required": false,
|
|
3519
|
+
"origin": "own",
|
|
3520
|
+
"values": [
|
|
3521
|
+
"neutral",
|
|
3522
|
+
"danger",
|
|
3523
|
+
"brand"
|
|
3524
|
+
]
|
|
3525
|
+
},
|
|
3526
|
+
"isDisabled": {
|
|
3527
|
+
"type": "boolean | undefined",
|
|
3528
|
+
"required": false,
|
|
3529
|
+
"origin": "own",
|
|
3530
|
+
"description": "Whether every checkbox in the group is disabled."
|
|
3531
|
+
},
|
|
3532
|
+
"orientation": {
|
|
3533
|
+
"type": "FieldsetOrientation | undefined",
|
|
3534
|
+
"required": false,
|
|
3535
|
+
"origin": "own",
|
|
3536
|
+
"values": [
|
|
3537
|
+
"vertical",
|
|
3538
|
+
"horizontal"
|
|
3539
|
+
]
|
|
3540
|
+
},
|
|
3541
|
+
"children": {
|
|
3542
|
+
"type": "React.ReactNode",
|
|
3543
|
+
"required": false,
|
|
3544
|
+
"origin": "own",
|
|
3545
|
+
"description": "Checkboxes, each with a `value`."
|
|
3546
|
+
}
|
|
3547
|
+
},
|
|
3548
|
+
"propCounts": {
|
|
3549
|
+
"own": 14,
|
|
3550
|
+
"aria": 0,
|
|
3551
|
+
"dom": 276,
|
|
3552
|
+
"other": 0
|
|
3553
|
+
}
|
|
3554
|
+
},
|
|
3285
3555
|
"Citation": {
|
|
3286
3556
|
"name": "Citation",
|
|
3287
3557
|
"source": "src/components/Citation.tsx",
|
|
@@ -4736,8 +5006,8 @@
|
|
|
4736
5006
|
"origin": "own",
|
|
4737
5007
|
"description": "Matches Figma's `Style` variant.",
|
|
4738
5008
|
"values": [
|
|
4739
|
-
"
|
|
4740
|
-
"
|
|
5009
|
+
"vertical",
|
|
5010
|
+
"horizontal"
|
|
4741
5011
|
]
|
|
4742
5012
|
}
|
|
4743
5013
|
},
|
|
@@ -5219,44 +5489,206 @@
|
|
|
5219
5489
|
"other": 0
|
|
5220
5490
|
}
|
|
5221
5491
|
},
|
|
5222
|
-
"
|
|
5223
|
-
"name": "
|
|
5224
|
-
"source": "src/components/
|
|
5225
|
-
"propsType": "
|
|
5226
|
-
"description": "
|
|
5227
|
-
"import": "import {
|
|
5492
|
+
"Fieldset": {
|
|
5493
|
+
"name": "Fieldset",
|
|
5494
|
+
"source": "src/components/Fieldset.tsx",
|
|
5495
|
+
"propsType": "FieldsetProps",
|
|
5496
|
+
"description": "Groups related fields under one label, with one description and one error —\nan address, a date range typed as two fields, a set of limits.\n\nFor checkboxes use CheckboxGroup and for radios RadioGroup: both render this\nsame shell, and add the selection state it deliberately does not own.\n\nThere is no `isDisabled`. A native `disabled` fieldset does disable every\ncontrol inside, but Input, Select and the rest draw their disabled state from\ntheir own prop, so the fields would stop working while still looking live.\nDisable the fields themselves; the choice groups can cascade because their\ncheckboxes and radios style off `:disabled`.",
|
|
5497
|
+
"import": "import { Fieldset } from 'ionbase-ui';",
|
|
5228
5498
|
"status": "stable",
|
|
5229
|
-
"summary": "A
|
|
5499
|
+
"summary": "A <fieldset>/<legend> that groups related fields under one label, one description and one error — an address, a pair of limits, a date typed as two fields.",
|
|
5230
5500
|
"useWhen": [
|
|
5231
|
-
"
|
|
5232
|
-
"the
|
|
5501
|
+
"several fields answer one question and a reader must hear the question with each of them — street, city and postcode under \"Billing address\"",
|
|
5502
|
+
"one error belongs to the group rather than to a field — \"the minimum must be below the maximum\""
|
|
5233
5503
|
],
|
|
5234
5504
|
"useInstead": [
|
|
5235
5505
|
{
|
|
5236
|
-
"when": "
|
|
5237
|
-
"use": "
|
|
5238
|
-
"why": "
|
|
5506
|
+
"when": "the fields are checkboxes",
|
|
5507
|
+
"use": "CheckboxGroup",
|
|
5508
|
+
"why": "it renders this same shell and adds the selected values and \"select at least one\""
|
|
5239
5509
|
},
|
|
5240
5510
|
{
|
|
5241
|
-
"when": "
|
|
5242
|
-
"use": "
|
|
5243
|
-
"why": "
|
|
5511
|
+
"when": "the fields are radios",
|
|
5512
|
+
"use": "RadioGroup",
|
|
5513
|
+
"why": "it renders this same shell and adds the shared name and the selected value"
|
|
5514
|
+
},
|
|
5515
|
+
{
|
|
5516
|
+
"when": "a single field needs a label and help text",
|
|
5517
|
+
"use": "Input",
|
|
5518
|
+
"why": "every field already renders its own label, description and error; a fieldset around one field announces the label twice"
|
|
5519
|
+
},
|
|
5520
|
+
{
|
|
5521
|
+
"when": "the grouping is visual — a titled section of a settings page",
|
|
5522
|
+
"use": "Card",
|
|
5523
|
+
"why": "a fieldset is a form grouping that assistive technology announces on entry; a section of unrelated settings is not one question"
|
|
5244
5524
|
}
|
|
5245
5525
|
],
|
|
5526
|
+
"composition": {
|
|
5527
|
+
"order": [
|
|
5528
|
+
"Fieldset",
|
|
5529
|
+
"Input"
|
|
5530
|
+
],
|
|
5531
|
+
"note": "Each field keeps its own label — the legend names the group, not the fields. Put a group-level error in `errorMessage` with `isInvalid`, and keep field-level errors on the fields.",
|
|
5532
|
+
"example": "<Fieldset label=\"Run limits\" description=\"Runs stop at whichever comes first.\" orientation=\"horizontal\"><NumberInput label=\"Max steps\" /><NumberInput label=\"Max minutes\" /></Fieldset>"
|
|
5533
|
+
},
|
|
5246
5534
|
"variants": {
|
|
5247
|
-
"
|
|
5248
|
-
"
|
|
5249
|
-
"use": "
|
|
5535
|
+
"orientation": {
|
|
5536
|
+
"vertical": {
|
|
5537
|
+
"use": "the default — fields stacked, as a form reads"
|
|
5250
5538
|
},
|
|
5251
|
-
"
|
|
5252
|
-
"use": "
|
|
5539
|
+
"horizontal": {
|
|
5540
|
+
"use": "two or three short fields that read as one value — a range, a size, a city and postcode. Wraps rather than overflows"
|
|
5253
5541
|
}
|
|
5254
5542
|
}
|
|
5255
5543
|
},
|
|
5256
|
-
"
|
|
5257
|
-
"
|
|
5258
|
-
"
|
|
5259
|
-
"
|
|
5544
|
+
"slots": {
|
|
5545
|
+
"label": {
|
|
5546
|
+
"accepts": "text",
|
|
5547
|
+
"note": "renders as the <legend> — the question the fields answer"
|
|
5548
|
+
},
|
|
5549
|
+
"description": {
|
|
5550
|
+
"accepts": "text",
|
|
5551
|
+
"note": "help beneath the fields; replaced by `errorMessage` while `isInvalid` is set, as Input's is"
|
|
5552
|
+
},
|
|
5553
|
+
"errorMessage": {
|
|
5554
|
+
"accepts": "text",
|
|
5555
|
+
"note": "the group's error, shown only while `isInvalid` is set"
|
|
5556
|
+
},
|
|
5557
|
+
"children": {
|
|
5558
|
+
"accepts": "Input, Select, NumberInput, DatePicker and other fields"
|
|
5559
|
+
}
|
|
5560
|
+
},
|
|
5561
|
+
"a11y": {
|
|
5562
|
+
"role": "group, from <fieldset> and <legend>",
|
|
5563
|
+
"guarantees": [
|
|
5564
|
+
"the legend is announced as the group's name when focus enters any field inside",
|
|
5565
|
+
"`aria-describedby` on the fieldset points at whichever of the description or the error is showing"
|
|
5566
|
+
],
|
|
5567
|
+
"requires": [
|
|
5568
|
+
"`label`, or `aria-label` when the question is already visible as a heading"
|
|
5569
|
+
],
|
|
5570
|
+
"notes": [
|
|
5571
|
+
"There is no `isDisabled`. A native disabled fieldset disables every control inside, but Input, Select and the rest draw their disabled look from their own prop — the fields would stop working while still looking live. Disable the fields themselves."
|
|
5572
|
+
]
|
|
5573
|
+
},
|
|
5574
|
+
"antiPatterns": [
|
|
5575
|
+
{
|
|
5576
|
+
"dont": "a Fieldset around one field",
|
|
5577
|
+
"why": "the field's label and the legend both announce, saying the same thing twice"
|
|
5578
|
+
},
|
|
5579
|
+
{
|
|
5580
|
+
"dont": "a Fieldset of Checkboxes",
|
|
5581
|
+
"do": "<CheckboxGroup label=\"Notify me when\">…</CheckboxGroup>",
|
|
5582
|
+
"why": "it looks the same, but nothing owns the selected values or \"select at least one\""
|
|
5583
|
+
},
|
|
5584
|
+
{
|
|
5585
|
+
"dont": "<fieldset disabled> around Inputs",
|
|
5586
|
+
"why": "the fields stop accepting input while still drawn as enabled"
|
|
5587
|
+
}
|
|
5588
|
+
],
|
|
5589
|
+
"stylesheet": "src/styles/fieldset.css",
|
|
5590
|
+
"tokens": [
|
|
5591
|
+
"--font-family-sans",
|
|
5592
|
+
"--font-weight-medium",
|
|
5593
|
+
"--font-weight-regular",
|
|
5594
|
+
"--ion-fieldset-gap",
|
|
5595
|
+
"--ion-fieldset-inline-gap",
|
|
5596
|
+
"--spacing-16",
|
|
5597
|
+
"--spacing-6",
|
|
5598
|
+
"--spacing-8",
|
|
5599
|
+
"--text-error",
|
|
5600
|
+
"--text-secondary",
|
|
5601
|
+
"--text-tertiary",
|
|
5602
|
+
"--type-body-sm",
|
|
5603
|
+
"--type-body-sm-line-height"
|
|
5604
|
+
],
|
|
5605
|
+
"props": {
|
|
5606
|
+
"label": {
|
|
5607
|
+
"type": "React.ReactNode",
|
|
5608
|
+
"required": false,
|
|
5609
|
+
"origin": "own",
|
|
5610
|
+
"description": "The question the fields answer. Renders as the `<legend>`."
|
|
5611
|
+
},
|
|
5612
|
+
"description": {
|
|
5613
|
+
"type": "React.ReactNode",
|
|
5614
|
+
"required": false,
|
|
5615
|
+
"origin": "own",
|
|
5616
|
+
"description": "Help text beneath the fields. Replaced by `errorMessage` while invalid."
|
|
5617
|
+
},
|
|
5618
|
+
"errorMessage": {
|
|
5619
|
+
"type": "React.ReactNode",
|
|
5620
|
+
"required": false,
|
|
5621
|
+
"origin": "own",
|
|
5622
|
+
"description": "Shown in the description's place while `isInvalid` is set."
|
|
5623
|
+
},
|
|
5624
|
+
"isInvalid": {
|
|
5625
|
+
"type": "boolean | undefined",
|
|
5626
|
+
"required": false,
|
|
5627
|
+
"origin": "own",
|
|
5628
|
+
"description": "Whether the group as a whole fails validation."
|
|
5629
|
+
},
|
|
5630
|
+
"orientation": {
|
|
5631
|
+
"type": "FieldsetOrientation | undefined",
|
|
5632
|
+
"required": false,
|
|
5633
|
+
"origin": "own",
|
|
5634
|
+
"description": "How the fields flow. Horizontal wraps rather than overflowing.",
|
|
5635
|
+
"values": [
|
|
5636
|
+
"vertical",
|
|
5637
|
+
"horizontal"
|
|
5638
|
+
],
|
|
5639
|
+
"default": "vertical"
|
|
5640
|
+
},
|
|
5641
|
+
"children": {
|
|
5642
|
+
"type": "React.ReactNode",
|
|
5643
|
+
"required": false,
|
|
5644
|
+
"origin": "own"
|
|
5645
|
+
}
|
|
5646
|
+
},
|
|
5647
|
+
"propCounts": {
|
|
5648
|
+
"own": 6,
|
|
5649
|
+
"aria": 0,
|
|
5650
|
+
"dom": 279,
|
|
5651
|
+
"other": 0
|
|
5652
|
+
}
|
|
5653
|
+
},
|
|
5654
|
+
"FileUpload": {
|
|
5655
|
+
"name": "FileUpload",
|
|
5656
|
+
"source": "src/components/FileUpload.tsx",
|
|
5657
|
+
"propsType": "FileUploadProps",
|
|
5658
|
+
"description": "FileUpload — a drop target wrapped around a real `<input type=\"file\">`.\n\nDrawn in Figma as `File Upload` (1367:2333) — two sizes by five states. The\nmeasurements came from `Input` and `EmptyState` rather than being invented:\nthe border, radius and disabled treatment are Input's; the centred\nicon-over-text stack is EmptyState's. Figma draws the drop zone alone, the\nsame split `Input` makes against `Form Field`.\n\nTHE INPUT IS THE CONTROL. THE DROP ZONE IS DECORATION.\n\nThe usual build of this component is a `<div>` with drag handlers and a\nclick that calls `input.click()`. That version cannot be reached by keyboard,\nhas no accessible name, does not participate in a form, and does not work in\nany environment without a pointer — which includes switch access, voice\ncontrol and most screen-reader browse modes.\n\nHere the file input is a real, focusable, labelled control that is visually\nhidden but NOT `display: none` — it keeps its place in the tab order and its\nlabel. Drag-and-drop is layered on top as an enhancement, and every path it\noffers is also reachable without it. That ordering is the whole component.\n\nVALIDATION IS ADVISORY, NOT SECURITY. `accept` and `maxSize` are checked here\nso the user finds out immediately instead of after an upload. A server that\ntrusts either one is trusting a value the client chose.",
|
|
5659
|
+
"import": "import { FileUpload } from 'ionbase-ui';",
|
|
5660
|
+
"status": "stable",
|
|
5661
|
+
"summary": "A drop target wrapped around a real file input, with client-side accept and size checks.",
|
|
5662
|
+
"useWhen": [
|
|
5663
|
+
"the user attaches one or more files to a form or a message",
|
|
5664
|
+
"the constraints (types, size, count) should be stated before the upload is attempted rather than after"
|
|
5665
|
+
],
|
|
5666
|
+
"useInstead": [
|
|
5667
|
+
{
|
|
5668
|
+
"when": "a single file is picked and no drop target or file list is wanted",
|
|
5669
|
+
"use": "a plain <input type=\"file\">",
|
|
5670
|
+
"why": "this component adds a drop zone and a removable list; without either it is a heavier way to render the same control"
|
|
5671
|
+
},
|
|
5672
|
+
{
|
|
5673
|
+
"when": "upload progress needs to be shown",
|
|
5674
|
+
"use": "ProgressBar",
|
|
5675
|
+
"why": "FileUpload selects files; it does not transfer them, and deliberately owns no network state"
|
|
5676
|
+
}
|
|
5677
|
+
],
|
|
5678
|
+
"variants": {
|
|
5679
|
+
"size": {
|
|
5680
|
+
"sm": {
|
|
5681
|
+
"use": "inside a dense form, or beside other fields"
|
|
5682
|
+
},
|
|
5683
|
+
"md": {
|
|
5684
|
+
"use": "the default — a standalone attachment area"
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
},
|
|
5688
|
+
"a11y": {
|
|
5689
|
+
"guarantees": [
|
|
5690
|
+
"the control is a real <input type=\"file\">, focusable and in the tab order — the drop zone is an enhancement layered on top, never the only way in",
|
|
5691
|
+
"the zone is a <label> for that input, so clicking anywhere opens the picker without any script",
|
|
5260
5692
|
"the input is clipped to 1px rather than display:none, which would remove it from the tab order and the accessibility tree",
|
|
5261
5693
|
"the focus ring is drawn on the zone, where the user is looking, not on the clipped input",
|
|
5262
5694
|
"a polite live region announces the number of files held, because a drop changes nothing near the user's focus",
|
|
@@ -7046,65 +7478,108 @@
|
|
|
7046
7478
|
"name": "Menu",
|
|
7047
7479
|
"source": "src/components/Menu.tsx",
|
|
7048
7480
|
"propsType": "MenuProps",
|
|
7049
|
-
"description": "Menu
|
|
7481
|
+
"description": "Menu — Figma `Menu` (82:306), `Menu Item` and `Menu Section Title`.\n\nA real ARIA menu: `role=\"menu\"`, one tab stop, arrow keys, Home and End,\ntypeahead, and disabled rows listed but skipped. Figma's `Type` is\n`selectionMode` — Single draws one check, Multi draws several — and with\na selection mode the rows become `menuitemradio` / `menuitemcheckbox` and\nannounce their checked state.\n\nOn its own it is the surface only, rendered in flow. Inside a MenuTrigger it\nfloats, opens and closes, is named by its trigger, and can open submenus.",
|
|
7050
7482
|
"import": "import { Menu } from 'ionbase-ui';",
|
|
7051
7483
|
"status": "stable",
|
|
7052
|
-
"summary": "
|
|
7484
|
+
"summary": "An ARIA menu: one tab stop, arrow keys, typeahead, and optional single or multiple selection. Children are MenuItem and MenuSection. Inside a MenuTrigger it floats, opens and closes, and can open submenus; on its own it renders in flow.",
|
|
7053
7485
|
"useWhen": [
|
|
7054
|
-
"
|
|
7055
|
-
"the rows need icons, a selected check, or
|
|
7486
|
+
"a list of actions or choices for the thing the user is on — row actions, a workspace switcher, a sort order",
|
|
7487
|
+
"the rows need icons, a selected check, or grouping under headings — more than a native <select> can hold"
|
|
7056
7488
|
],
|
|
7057
7489
|
"useInstead": [
|
|
7058
7490
|
{
|
|
7059
|
-
"when": "
|
|
7060
|
-
"use": "
|
|
7061
|
-
"why": "
|
|
7491
|
+
"when": "a button opens it",
|
|
7492
|
+
"use": "MenuTrigger",
|
|
7493
|
+
"why": "it gives the trigger aria-haspopup and aria-expanded, opens on ArrowDown, and closes on an action and returns focus — a Popover round a Menu does none of that"
|
|
7062
7494
|
},
|
|
7063
7495
|
{
|
|
7064
|
-
"when": "one value is picked from a modest list of plain text options",
|
|
7496
|
+
"when": "one value is picked for a form field from a modest list of plain text options",
|
|
7065
7497
|
"use": "Select",
|
|
7066
|
-
"why": "the native picker brings
|
|
7498
|
+
"why": "a form value is a listbox, not a menu; the native picker brings type-ahead and the mobile platform picker for free"
|
|
7067
7499
|
},
|
|
7068
7500
|
{
|
|
7069
|
-
"when": "
|
|
7070
|
-
"use": "
|
|
7501
|
+
"when": "the user types to narrow a long list down to one value",
|
|
7502
|
+
"use": "Combobox"
|
|
7503
|
+
},
|
|
7504
|
+
{
|
|
7505
|
+
"when": "it is primary navigation rather than a list of actions",
|
|
7506
|
+
"use": "NavItem",
|
|
7507
|
+
"why": "a menu's rows are actions; links to pages belong in the document's navigation, where a screen reader's landmarks find them"
|
|
7508
|
+
},
|
|
7509
|
+
{
|
|
7510
|
+
"when": "any action or page should be reachable by searching",
|
|
7511
|
+
"use": "CommandPalette"
|
|
7071
7512
|
}
|
|
7072
7513
|
],
|
|
7073
7514
|
"composition": {
|
|
7074
7515
|
"order": [
|
|
7075
7516
|
"Menu",
|
|
7517
|
+
"MenuSection",
|
|
7076
7518
|
"MenuItem"
|
|
7077
7519
|
],
|
|
7078
|
-
"
|
|
7520
|
+
"note": "MenuItem and MenuSection are collection elements: Menu reads them from its DIRECT children to build the collection, and neither renders anything itself. Every MenuItem needs a `key`, and `onAction`, `selectedKeys` and `disabledKeys` all speak in those keys. To open it from a button, put the Button and the Menu inside a MenuTrigger.",
|
|
7521
|
+
"example": "<Menu aria-label=\"Row actions\" onAction={(key) => run(key)}><MenuSection title=\"Edit\"><MenuItem key=\"rename\" icon={<Icon as={Pencil} />}>Rename</MenuItem><MenuItem key=\"duplicate\">Duplicate</MenuItem></MenuSection><MenuSection aria-label=\"Danger\"><MenuItem key=\"archive\" isDisabled>Archive</MenuItem></MenuSection></Menu>"
|
|
7522
|
+
},
|
|
7523
|
+
"variants": {
|
|
7524
|
+
"selectionMode": {
|
|
7525
|
+
"none": {
|
|
7526
|
+
"use": "the default — a list of actions. Rows are `menuitem`, and there is no check slot"
|
|
7527
|
+
},
|
|
7528
|
+
"single": {
|
|
7529
|
+
"use": "Figma's Type=Single — one current choice, such as a sort order or the active workspace. Rows are `menuitemradio`"
|
|
7530
|
+
},
|
|
7531
|
+
"multiple": {
|
|
7532
|
+
"use": "Figma's Type=Multi — independent toggles, such as visible columns. Rows are `menuitemcheckbox`"
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7079
7535
|
},
|
|
7080
7536
|
"slots": {
|
|
7081
7537
|
"children": {
|
|
7082
|
-
"accepts":
|
|
7538
|
+
"accepts": [
|
|
7539
|
+
"MenuItem",
|
|
7540
|
+
"MenuSection"
|
|
7541
|
+
]
|
|
7083
7542
|
}
|
|
7084
7543
|
},
|
|
7085
7544
|
"a11y": {
|
|
7086
|
-
"role": "
|
|
7545
|
+
"role": "menu",
|
|
7087
7546
|
"guarantees": [
|
|
7088
|
-
"
|
|
7547
|
+
"one tab stop; arrow keys move between rows, Home and End jump, and typing a letter moves to the next row starting with it",
|
|
7548
|
+
"disabled rows are listed and announced as unavailable, and the arrow keys skip them",
|
|
7549
|
+
"with a selection mode, rows are `menuitemradio` or `menuitemcheckbox` and announce `aria-checked` — the check glyph is never the only signal",
|
|
7550
|
+
"the pointer and the keyboard share one highlighted row: hovering a row focuses it"
|
|
7551
|
+
],
|
|
7552
|
+
"requires": [
|
|
7553
|
+
"an accessible name — `aria-label` or `aria-labelledby` naming what the actions are for"
|
|
7089
7554
|
],
|
|
7090
7555
|
"notes": [
|
|
7091
|
-
"
|
|
7092
|
-
"
|
|
7556
|
+
"A menu is not a form control. It has no `name` and submits nothing; `selectedKeys` is state you hold.",
|
|
7557
|
+
"Titled sections are named groups. An untitled section needs `aria-label`, and is set off by a rule.",
|
|
7558
|
+
"Submenus need a MenuTrigger. In a standalone Menu a row with MenuItem children renders as a plain row: there is no root to open a submenu from."
|
|
7093
7559
|
]
|
|
7094
7560
|
},
|
|
7095
7561
|
"antiPatterns": [
|
|
7096
7562
|
{
|
|
7097
|
-
"dont": "
|
|
7098
|
-
"why": "
|
|
7563
|
+
"dont": "wrapping MenuItems in a <div>, a fragment or your own component inside Menu",
|
|
7564
|
+
"why": "Menu reads its collection from its direct children; a wrapped row is invisible to it and renders nothing"
|
|
7565
|
+
},
|
|
7566
|
+
{
|
|
7567
|
+
"dont": "an `onClick` on MenuItem",
|
|
7568
|
+
"why": "MenuItem renders nothing, so there is no element to click. Pass `onAction` to Menu; it receives the item's key, from the pointer, Enter and Space alike"
|
|
7099
7569
|
},
|
|
7100
7570
|
{
|
|
7101
|
-
"dont": "using Menu
|
|
7102
|
-
"why": "
|
|
7571
|
+
"dont": "using Menu for a form field's value",
|
|
7572
|
+
"why": "a screen reader announces a menu as actions, and a form cannot read it. Use Select or Combobox"
|
|
7573
|
+
},
|
|
7574
|
+
{
|
|
7575
|
+
"dont": "putting links to other pages in a Menu",
|
|
7576
|
+
"why": "they vanish from the page's link list and landmarks. Navigation belongs in NavItem or Sidebar"
|
|
7103
7577
|
}
|
|
7104
7578
|
],
|
|
7105
7579
|
"stylesheet": "src/styles/menu.css",
|
|
7106
7580
|
"tokens": [
|
|
7107
7581
|
"--border-focus",
|
|
7582
|
+
"--border-strong",
|
|
7108
7583
|
"--border-subtle",
|
|
7109
7584
|
"--border-width-default",
|
|
7110
7585
|
"--border-width-thick",
|
|
@@ -7116,10 +7591,13 @@
|
|
|
7116
7591
|
"--icon-size-sm",
|
|
7117
7592
|
"--ion-duration-base",
|
|
7118
7593
|
"--ion-ease-out",
|
|
7594
|
+
"--ion-menu-min-width",
|
|
7595
|
+
"--ion-shadow-shadow-lg",
|
|
7119
7596
|
"--radius-sm",
|
|
7120
7597
|
"--radius-xl",
|
|
7121
7598
|
"--spacing-12",
|
|
7122
7599
|
"--spacing-2",
|
|
7600
|
+
"--spacing-4",
|
|
7123
7601
|
"--spacing-40",
|
|
7124
7602
|
"--spacing-6",
|
|
7125
7603
|
"--spacing-8",
|
|
@@ -7129,20 +7607,148 @@
|
|
|
7129
7607
|
"--text-disabled",
|
|
7130
7608
|
"--text-primary",
|
|
7131
7609
|
"--text-secondary",
|
|
7610
|
+
"--text-tertiary",
|
|
7132
7611
|
"--type-body",
|
|
7133
|
-
"--type-body-line-height"
|
|
7612
|
+
"--type-body-line-height",
|
|
7613
|
+
"--type-caption",
|
|
7614
|
+
"--type-caption-line-height"
|
|
7134
7615
|
],
|
|
7135
7616
|
"props": {
|
|
7136
|
-
"
|
|
7137
|
-
"type": "
|
|
7617
|
+
"className": {
|
|
7618
|
+
"type": "string | undefined",
|
|
7619
|
+
"required": false,
|
|
7620
|
+
"origin": "own"
|
|
7621
|
+
},
|
|
7622
|
+
"style": {
|
|
7623
|
+
"type": "React.CSSProperties | undefined",
|
|
7138
7624
|
"required": false,
|
|
7139
7625
|
"origin": "own"
|
|
7626
|
+
},
|
|
7627
|
+
"escapeKeyBehavior": {
|
|
7628
|
+
"type": "\"none\" | \"clearSelection\" | undefined",
|
|
7629
|
+
"required": false,
|
|
7630
|
+
"origin": "aria",
|
|
7631
|
+
"description": "Whether pressing the escape key should clear selection in the menu or not.\n\nMost experiences should not modify this option as it eliminates a keyboard user's ability to\neasily clear selection. Only use if the escape key is being handled externally or should not\ntrigger selection clearing contextually.",
|
|
7632
|
+
"values": [
|
|
7633
|
+
"none",
|
|
7634
|
+
"clearSelection"
|
|
7635
|
+
],
|
|
7636
|
+
"tags": {
|
|
7637
|
+
"default": "'clearSelection'"
|
|
7638
|
+
}
|
|
7639
|
+
},
|
|
7640
|
+
"autoFocus": {
|
|
7641
|
+
"type": "boolean | FocusStrategy | undefined",
|
|
7642
|
+
"required": false,
|
|
7643
|
+
"origin": "aria",
|
|
7644
|
+
"description": "Where the focus should be set."
|
|
7645
|
+
},
|
|
7646
|
+
"shouldFocusWrap": {
|
|
7647
|
+
"type": "boolean | undefined",
|
|
7648
|
+
"required": false,
|
|
7649
|
+
"origin": "aria",
|
|
7650
|
+
"description": "Whether keyboard navigation is circular."
|
|
7651
|
+
},
|
|
7652
|
+
"onAction": {
|
|
7653
|
+
"type": "((key: Key, value: T) => void) | undefined",
|
|
7654
|
+
"required": false,
|
|
7655
|
+
"origin": "aria",
|
|
7656
|
+
"description": "Handler that is called when an item is selected."
|
|
7657
|
+
},
|
|
7658
|
+
"onClose": {
|
|
7659
|
+
"type": "(() => void) | undefined",
|
|
7660
|
+
"required": false,
|
|
7661
|
+
"origin": "aria",
|
|
7662
|
+
"description": "Handler that is called when the menu should close after selecting an item."
|
|
7663
|
+
},
|
|
7664
|
+
"children": {
|
|
7665
|
+
"type": "CollectionChildren<T>",
|
|
7666
|
+
"required": true,
|
|
7667
|
+
"origin": "aria",
|
|
7668
|
+
"description": "The contents of the collection."
|
|
7669
|
+
},
|
|
7670
|
+
"items": {
|
|
7671
|
+
"type": "Iterable<T> | undefined",
|
|
7672
|
+
"required": false,
|
|
7673
|
+
"origin": "aria",
|
|
7674
|
+
"description": "Item objects in the collection."
|
|
7675
|
+
},
|
|
7676
|
+
"disabledKeys": {
|
|
7677
|
+
"type": "Iterable<Key> | undefined",
|
|
7678
|
+
"required": false,
|
|
7679
|
+
"origin": "aria",
|
|
7680
|
+
"description": "The item keys that are disabled. These items cannot be selected, focused, or otherwise\ninteracted with."
|
|
7681
|
+
},
|
|
7682
|
+
"selectionMode": {
|
|
7683
|
+
"type": "SelectionMode | undefined",
|
|
7684
|
+
"required": false,
|
|
7685
|
+
"origin": "aria",
|
|
7686
|
+
"description": "The type of selection that is allowed in the collection.",
|
|
7687
|
+
"values": [
|
|
7688
|
+
"multiple",
|
|
7689
|
+
"none",
|
|
7690
|
+
"single"
|
|
7691
|
+
]
|
|
7692
|
+
},
|
|
7693
|
+
"disallowEmptySelection": {
|
|
7694
|
+
"type": "boolean | undefined",
|
|
7695
|
+
"required": false,
|
|
7696
|
+
"origin": "aria",
|
|
7697
|
+
"description": "Whether the collection allows empty selection."
|
|
7698
|
+
},
|
|
7699
|
+
"selectedKeys": {
|
|
7700
|
+
"type": "Iterable<Key> | \"all\" | undefined",
|
|
7701
|
+
"required": false,
|
|
7702
|
+
"origin": "aria",
|
|
7703
|
+
"description": "The currently selected keys in the collection (controlled)."
|
|
7704
|
+
},
|
|
7705
|
+
"defaultSelectedKeys": {
|
|
7706
|
+
"type": "Iterable<Key> | \"all\" | undefined",
|
|
7707
|
+
"required": false,
|
|
7708
|
+
"origin": "aria",
|
|
7709
|
+
"description": "The initial selected keys in the collection (uncontrolled)."
|
|
7710
|
+
},
|
|
7711
|
+
"onSelectionChange": {
|
|
7712
|
+
"type": "((keys: Selection) => void) | undefined",
|
|
7713
|
+
"required": false,
|
|
7714
|
+
"origin": "aria",
|
|
7715
|
+
"description": "Handler that is called when the selection changes."
|
|
7716
|
+
},
|
|
7717
|
+
"id": {
|
|
7718
|
+
"type": "string | undefined",
|
|
7719
|
+
"required": false,
|
|
7720
|
+
"origin": "aria",
|
|
7721
|
+
"description": "The element's unique identifier. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id)."
|
|
7722
|
+
},
|
|
7723
|
+
"aria-label": {
|
|
7724
|
+
"type": "string | undefined",
|
|
7725
|
+
"required": false,
|
|
7726
|
+
"origin": "aria",
|
|
7727
|
+
"description": "Defines a string value that labels the current element."
|
|
7728
|
+
},
|
|
7729
|
+
"aria-labelledby": {
|
|
7730
|
+
"type": "string | undefined",
|
|
7731
|
+
"required": false,
|
|
7732
|
+
"origin": "aria",
|
|
7733
|
+
"description": "Identifies the element (or elements) that labels the current element."
|
|
7734
|
+
},
|
|
7735
|
+
"aria-describedby": {
|
|
7736
|
+
"type": "string | undefined",
|
|
7737
|
+
"required": false,
|
|
7738
|
+
"origin": "aria",
|
|
7739
|
+
"description": "Identifies the element (or elements) that describes the object."
|
|
7740
|
+
},
|
|
7741
|
+
"aria-details": {
|
|
7742
|
+
"type": "string | undefined",
|
|
7743
|
+
"required": false,
|
|
7744
|
+
"origin": "aria",
|
|
7745
|
+
"description": "Identifies the element (or elements) that provide a detailed, extended description for the\nobject."
|
|
7140
7746
|
}
|
|
7141
7747
|
},
|
|
7142
7748
|
"propCounts": {
|
|
7143
|
-
"own":
|
|
7144
|
-
"aria":
|
|
7145
|
-
"dom":
|
|
7749
|
+
"own": 2,
|
|
7750
|
+
"aria": 18,
|
|
7751
|
+
"dom": 0,
|
|
7146
7752
|
"other": 0
|
|
7147
7753
|
}
|
|
7148
7754
|
},
|
|
@@ -7150,11 +7756,12 @@
|
|
|
7150
7756
|
"name": "MenuItem",
|
|
7151
7757
|
"source": "src/components/Menu.tsx",
|
|
7152
7758
|
"propsType": "MenuItemProps",
|
|
7759
|
+
"description": "One row of a Menu. A collection element, like TabItem: it renders nothing\nitself — Menu reads its props and draws the row — so it only means anything\nas a direct child of Menu or MenuSection. Give it a `key`; that key is what\n`onAction` and `selectedKeys` speak in.",
|
|
7153
7760
|
"import": "import { MenuItem } from 'ionbase-ui';",
|
|
7154
7761
|
"status": "stable",
|
|
7155
|
-
"summary": "One row
|
|
7762
|
+
"summary": "One row of a Menu. A collection element: Menu reads its props and draws the row as `menuitem`, `menuitemradio` or `menuitemcheckbox`.",
|
|
7156
7763
|
"useWhen": [
|
|
7157
|
-
"a row in a Menu
|
|
7764
|
+
"a row in a Menu or a MenuSection — it is not usable anywhere else"
|
|
7158
7765
|
],
|
|
7159
7766
|
"useInstead": [
|
|
7160
7767
|
{
|
|
@@ -7166,6 +7773,14 @@
|
|
|
7166
7773
|
"use": "Button"
|
|
7167
7774
|
}
|
|
7168
7775
|
],
|
|
7776
|
+
"composition": {
|
|
7777
|
+
"order": [
|
|
7778
|
+
"Menu",
|
|
7779
|
+
"MenuItem"
|
|
7780
|
+
],
|
|
7781
|
+
"note": "A collection element, like TabItem: it renders nothing itself and never appears in the DOM. `key` identifies it to Menu's `onAction`, `selectedKeys` and `disabledKeys`.",
|
|
7782
|
+
"example": "<MenuItem key=\"duplicate\" icon={<Icon as={Copy} />}>Duplicate</MenuItem>\n<MenuItem key=\"move\" title=\"Move to\"><MenuItem key=\"archive\">Archive</MenuItem></MenuItem>"
|
|
7783
|
+
},
|
|
7169
7784
|
"slots": {
|
|
7170
7785
|
"icon": {
|
|
7171
7786
|
"accepts": "Icon",
|
|
@@ -7174,37 +7789,50 @@
|
|
|
7174
7789
|
"children": {
|
|
7175
7790
|
"accepts": "text",
|
|
7176
7791
|
"note": "the row label"
|
|
7792
|
+
},
|
|
7793
|
+
"title": {
|
|
7794
|
+
"accepts": "text",
|
|
7795
|
+
"note": "only on a row that opens a submenu: the row's label, while its children are the submenu's MenuItems. Submenus open only inside a MenuTrigger"
|
|
7177
7796
|
}
|
|
7178
7797
|
},
|
|
7179
7798
|
"a11y": {
|
|
7180
|
-
"role": "button",
|
|
7181
7799
|
"guarantees": [
|
|
7182
|
-
"`
|
|
7183
|
-
"the check glyph
|
|
7800
|
+
"the role, `aria-checked` and `aria-disabled` are supplied by Menu from its selection mode and keys, not by this element",
|
|
7801
|
+
"the check glyph occupies its slot whenever the menu has a selection mode, so rows never reflow as the selection moves"
|
|
7184
7802
|
],
|
|
7185
7803
|
"requires": [
|
|
7186
|
-
"a
|
|
7804
|
+
"a stable `key`",
|
|
7805
|
+
"text in `children`, or `textValue` when the children are not plain text — typeahead matches against it"
|
|
7187
7806
|
]
|
|
7188
7807
|
},
|
|
7189
7808
|
"antiPatterns": [
|
|
7190
7809
|
{
|
|
7191
7810
|
"dont": "using MenuItem outside a Menu",
|
|
7192
|
-
"why": "it renders
|
|
7811
|
+
"why": "it renders nothing on its own"
|
|
7193
7812
|
},
|
|
7194
7813
|
{
|
|
7195
7814
|
"dont": "an icon-only MenuItem with no children",
|
|
7196
|
-
"why": "the row announces nothing"
|
|
7815
|
+
"why": "the row announces nothing and typeahead cannot reach it"
|
|
7816
|
+
},
|
|
7817
|
+
{
|
|
7818
|
+
"dont": "array indexes as keys in a list that can change",
|
|
7819
|
+
"why": "selection and actions are expressed in keys, so the selected row silently becomes a different row"
|
|
7197
7820
|
}
|
|
7198
7821
|
],
|
|
7199
7822
|
"deprecated": [
|
|
7200
7823
|
{
|
|
7201
7824
|
"prop": "disabled",
|
|
7202
7825
|
"replacement": "isDisabled"
|
|
7826
|
+
},
|
|
7827
|
+
{
|
|
7828
|
+
"prop": "isSelected",
|
|
7829
|
+
"note": "Put the item's key in Menu's `selectedKeys` and set its `selectionMode`."
|
|
7203
7830
|
}
|
|
7204
7831
|
],
|
|
7205
7832
|
"stylesheet": "src/styles/menu.css",
|
|
7206
7833
|
"tokens": [
|
|
7207
7834
|
"--border-focus",
|
|
7835
|
+
"--border-strong",
|
|
7208
7836
|
"--border-subtle",
|
|
7209
7837
|
"--border-width-default",
|
|
7210
7838
|
"--border-width-thick",
|
|
@@ -7216,10 +7844,13 @@
|
|
|
7216
7844
|
"--icon-size-sm",
|
|
7217
7845
|
"--ion-duration-base",
|
|
7218
7846
|
"--ion-ease-out",
|
|
7847
|
+
"--ion-menu-min-width",
|
|
7848
|
+
"--ion-shadow-shadow-lg",
|
|
7219
7849
|
"--radius-sm",
|
|
7220
7850
|
"--radius-xl",
|
|
7221
7851
|
"--spacing-12",
|
|
7222
7852
|
"--spacing-2",
|
|
7853
|
+
"--spacing-4",
|
|
7223
7854
|
"--spacing-40",
|
|
7224
7855
|
"--spacing-6",
|
|
7225
7856
|
"--spacing-8",
|
|
@@ -7229,15 +7860,18 @@
|
|
|
7229
7860
|
"--text-disabled",
|
|
7230
7861
|
"--text-primary",
|
|
7231
7862
|
"--text-secondary",
|
|
7863
|
+
"--text-tertiary",
|
|
7232
7864
|
"--type-body",
|
|
7233
|
-
"--type-body-line-height"
|
|
7865
|
+
"--type-body-line-height",
|
|
7866
|
+
"--type-caption",
|
|
7867
|
+
"--type-caption-line-height"
|
|
7234
7868
|
],
|
|
7235
7869
|
"props": {
|
|
7236
|
-
"
|
|
7237
|
-
"type": "
|
|
7870
|
+
"title": {
|
|
7871
|
+
"type": "React.ReactNode",
|
|
7238
7872
|
"required": false,
|
|
7239
7873
|
"origin": "own",
|
|
7240
|
-
"description": "
|
|
7874
|
+
"description": "Only on a row that opens a submenu: the row's label. Its children are then\nthe submenu's MenuItems rather than its label. Submenus open only inside a\nMenuTrigger — a standalone Menu has nothing to open them from."
|
|
7241
7875
|
},
|
|
7242
7876
|
"icon": {
|
|
7243
7877
|
"type": "React.ReactNode",
|
|
@@ -7249,7 +7883,7 @@
|
|
|
7249
7883
|
"type": "boolean | undefined",
|
|
7250
7884
|
"required": false,
|
|
7251
7885
|
"origin": "own",
|
|
7252
|
-
"description": "Whether the item is disabled."
|
|
7886
|
+
"description": "Whether the item is disabled. Listed, skipped by the arrow keys."
|
|
7253
7887
|
},
|
|
7254
7888
|
"disabled": {
|
|
7255
7889
|
"type": "boolean | undefined",
|
|
@@ -7259,113 +7893,472 @@
|
|
|
7259
7893
|
"deprecated": "Use `isDisabled`. Accepted as an alias for one minor version."
|
|
7260
7894
|
}
|
|
7261
7895
|
},
|
|
7896
|
+
"isSelected": {
|
|
7897
|
+
"type": "boolean | undefined",
|
|
7898
|
+
"required": false,
|
|
7899
|
+
"origin": "own",
|
|
7900
|
+
"tags": {
|
|
7901
|
+
"deprecated": "Put the item's key in Menu's `selectedKeys`. Accepted for one\nminor version: when no item's key is in `selectedKeys` and Menu is given\nneither `selectedKeys` nor `defaultSelectedKeys`, the items passing this\nbecome the selection."
|
|
7902
|
+
}
|
|
7903
|
+
},
|
|
7262
7904
|
"children": {
|
|
7263
|
-
"type": "
|
|
7905
|
+
"type": "ReactNode",
|
|
7906
|
+
"required": true,
|
|
7907
|
+
"origin": "aria",
|
|
7908
|
+
"description": "Rendered contents of the item or child items."
|
|
7909
|
+
},
|
|
7910
|
+
"textValue": {
|
|
7911
|
+
"type": "string | undefined",
|
|
7264
7912
|
"required": false,
|
|
7265
|
-
"origin": "
|
|
7913
|
+
"origin": "aria",
|
|
7914
|
+
"description": "A string representation of the item's contents, used for features like typeahead."
|
|
7915
|
+
},
|
|
7916
|
+
"aria-label": {
|
|
7917
|
+
"type": "string | undefined",
|
|
7918
|
+
"required": false,
|
|
7919
|
+
"origin": "aria",
|
|
7920
|
+
"description": "An accessibility label for this item."
|
|
7921
|
+
},
|
|
7922
|
+
"childItems": {
|
|
7923
|
+
"type": "Iterable<T> | undefined",
|
|
7924
|
+
"required": false,
|
|
7925
|
+
"origin": "aria",
|
|
7926
|
+
"description": "A list of child item objects. Used for dynamic collections."
|
|
7927
|
+
},
|
|
7928
|
+
"hasChildItems": {
|
|
7929
|
+
"type": "boolean | undefined",
|
|
7930
|
+
"required": false,
|
|
7931
|
+
"origin": "aria",
|
|
7932
|
+
"description": "Whether this item has children, even if not loaded yet."
|
|
7933
|
+
},
|
|
7934
|
+
"href": {
|
|
7935
|
+
"type": "string | undefined",
|
|
7936
|
+
"required": false,
|
|
7937
|
+
"origin": "aria",
|
|
7938
|
+
"description": "A URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href)."
|
|
7939
|
+
},
|
|
7940
|
+
"hrefLang": {
|
|
7941
|
+
"type": "string | undefined",
|
|
7942
|
+
"required": false,
|
|
7943
|
+
"origin": "aria",
|
|
7944
|
+
"description": "Hints at the human language of the linked URL.\nSee[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#hreflang)."
|
|
7945
|
+
},
|
|
7946
|
+
"target": {
|
|
7947
|
+
"type": "HTMLAttributeAnchorTarget | undefined",
|
|
7948
|
+
"required": false,
|
|
7949
|
+
"origin": "aria",
|
|
7950
|
+
"description": "The target window for the link. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target)."
|
|
7951
|
+
},
|
|
7952
|
+
"rel": {
|
|
7953
|
+
"type": "string | undefined",
|
|
7954
|
+
"required": false,
|
|
7955
|
+
"origin": "aria",
|
|
7956
|
+
"description": "The relationship between the linked resource and the current page. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)."
|
|
7957
|
+
},
|
|
7958
|
+
"download": {
|
|
7959
|
+
"type": "string | boolean | undefined",
|
|
7960
|
+
"required": false,
|
|
7961
|
+
"origin": "aria",
|
|
7962
|
+
"description": "Causes the browser to download the linked URL. A string may be provided to suggest a file name.\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download)."
|
|
7963
|
+
},
|
|
7964
|
+
"ping": {
|
|
7965
|
+
"type": "string | undefined",
|
|
7966
|
+
"required": false,
|
|
7967
|
+
"origin": "aria",
|
|
7968
|
+
"description": "A space-separated list of URLs to ping when the link is followed. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping)."
|
|
7969
|
+
},
|
|
7970
|
+
"referrerPolicy": {
|
|
7971
|
+
"type": "HTMLAttributeReferrerPolicy | undefined",
|
|
7972
|
+
"required": false,
|
|
7973
|
+
"origin": "aria",
|
|
7974
|
+
"description": "How much of the referrer to send when following the link. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy).",
|
|
7975
|
+
"values": [
|
|
7976
|
+
"",
|
|
7977
|
+
"origin",
|
|
7978
|
+
"no-referrer",
|
|
7979
|
+
"no-referrer-when-downgrade",
|
|
7980
|
+
"origin-when-cross-origin",
|
|
7981
|
+
"same-origin",
|
|
7982
|
+
"strict-origin",
|
|
7983
|
+
"strict-origin-when-cross-origin",
|
|
7984
|
+
"unsafe-url"
|
|
7985
|
+
]
|
|
7986
|
+
},
|
|
7987
|
+
"routerOptions": {
|
|
7988
|
+
"type": "undefined",
|
|
7989
|
+
"required": false,
|
|
7990
|
+
"origin": "aria",
|
|
7991
|
+
"description": "Options for the configured client side router."
|
|
7266
7992
|
}
|
|
7267
7993
|
},
|
|
7268
7994
|
"propCounts": {
|
|
7269
7995
|
"own": 5,
|
|
7996
|
+
"aria": 13,
|
|
7997
|
+
"dom": 0,
|
|
7998
|
+
"other": 0
|
|
7999
|
+
}
|
|
8000
|
+
},
|
|
8001
|
+
"MenuSection": {
|
|
8002
|
+
"name": "MenuSection",
|
|
8003
|
+
"source": "../../node_modules/.pnpm/react-stately@3.48.0_react@19.2.8/node_modules/react-stately/dist/types/src/collections/Section.d.ts",
|
|
8004
|
+
"propsType": null,
|
|
8005
|
+
"import": "import { MenuSection } from 'ionbase-ui';",
|
|
8006
|
+
"status": "stable",
|
|
8007
|
+
"summary": "A named group of MenuItems. Re-exported from react-stately's collection Section — `title` is Figma's Menu Section Title.",
|
|
8008
|
+
"useWhen": [
|
|
8009
|
+
"a Menu has more than one kind of action, such as editing actions and destructive ones",
|
|
8010
|
+
"the rows fall into groups the user scans by name"
|
|
8011
|
+
],
|
|
8012
|
+
"useInstead": [
|
|
8013
|
+
{
|
|
8014
|
+
"when": "the menu has one group",
|
|
8015
|
+
"use": "MenuItem",
|
|
8016
|
+
"why": "a single section adds a heading the user must hear before every menu, and says nothing the menu's own label does not"
|
|
8017
|
+
}
|
|
8018
|
+
],
|
|
8019
|
+
"composition": {
|
|
8020
|
+
"order": [
|
|
8021
|
+
"Menu",
|
|
8022
|
+
"MenuSection",
|
|
8023
|
+
"MenuItem"
|
|
8024
|
+
],
|
|
8025
|
+
"note": "A collection element: Menu reads it from its direct children. With `title`, the heading is drawn with a rule after it; without one, `aria-label` names the group and a rule is drawn above it instead.",
|
|
8026
|
+
"example": "<MenuSection title=\"Sort by\"><MenuItem key=\"name\">Name</MenuItem><MenuItem key=\"updated\">Last updated</MenuItem></MenuSection>"
|
|
8027
|
+
},
|
|
8028
|
+
"a11y": {
|
|
8029
|
+
"role": "group",
|
|
8030
|
+
"guarantees": [
|
|
8031
|
+
"the group is named by its title, so a screen reader announces it on entering the first row"
|
|
8032
|
+
],
|
|
8033
|
+
"requires": [
|
|
8034
|
+
"a `title`; an untitled section still needs a label for the group, or it is announced as an unnamed group"
|
|
8035
|
+
],
|
|
8036
|
+
"notes": [
|
|
8037
|
+
"The accessible-name lint rule is deliberately not driven from here: a titled section is named by its title and needs nothing more, so demanding a label on every MenuSection would be wrong for the common case."
|
|
8038
|
+
]
|
|
8039
|
+
},
|
|
8040
|
+
"antiPatterns": [
|
|
8041
|
+
{
|
|
8042
|
+
"dont": "nesting a MenuSection inside another",
|
|
8043
|
+
"why": "a menu has one level of grouping; a section inside a section renders nothing"
|
|
8044
|
+
}
|
|
8045
|
+
],
|
|
8046
|
+
"stylesheet": "src/styles/menu.css",
|
|
8047
|
+
"tokens": [
|
|
8048
|
+
"--border-focus",
|
|
8049
|
+
"--border-strong",
|
|
8050
|
+
"--border-subtle",
|
|
8051
|
+
"--border-width-default",
|
|
8052
|
+
"--border-width-thick",
|
|
8053
|
+
"--font-family-sans",
|
|
8054
|
+
"--font-weight-regular",
|
|
8055
|
+
"--icon-default",
|
|
8056
|
+
"--icon-disabled",
|
|
8057
|
+
"--icon-primary",
|
|
8058
|
+
"--icon-size-sm",
|
|
8059
|
+
"--ion-duration-base",
|
|
8060
|
+
"--ion-ease-out",
|
|
8061
|
+
"--ion-menu-min-width",
|
|
8062
|
+
"--ion-shadow-shadow-lg",
|
|
8063
|
+
"--radius-sm",
|
|
8064
|
+
"--radius-xl",
|
|
8065
|
+
"--spacing-12",
|
|
8066
|
+
"--spacing-2",
|
|
8067
|
+
"--spacing-4",
|
|
8068
|
+
"--spacing-40",
|
|
8069
|
+
"--spacing-6",
|
|
8070
|
+
"--spacing-8",
|
|
8071
|
+
"--surface-default",
|
|
8072
|
+
"--surface-hover",
|
|
8073
|
+
"--surface-primary-subtle",
|
|
8074
|
+
"--text-disabled",
|
|
8075
|
+
"--text-primary",
|
|
8076
|
+
"--text-secondary",
|
|
8077
|
+
"--text-tertiary",
|
|
8078
|
+
"--type-body",
|
|
8079
|
+
"--type-body-line-height",
|
|
8080
|
+
"--type-caption",
|
|
8081
|
+
"--type-caption-line-height"
|
|
8082
|
+
],
|
|
8083
|
+
"props": {},
|
|
8084
|
+
"propCounts": {
|
|
8085
|
+
"own": 0,
|
|
7270
8086
|
"aria": 0,
|
|
7271
|
-
"dom":
|
|
8087
|
+
"dom": 0,
|
|
7272
8088
|
"other": 0
|
|
7273
8089
|
}
|
|
7274
8090
|
},
|
|
7275
|
-
"
|
|
7276
|
-
"name": "
|
|
7277
|
-
"source": "src/components/
|
|
7278
|
-
"propsType": "
|
|
7279
|
-
"description": "
|
|
7280
|
-
"import": "import {
|
|
8091
|
+
"MenuTrigger": {
|
|
8092
|
+
"name": "MenuTrigger",
|
|
8093
|
+
"source": "src/components/Menu.tsx",
|
|
8094
|
+
"propsType": "MenuTriggerProps",
|
|
8095
|
+
"description": "MenuTrigger — a Button that opens a Menu.\n\nThe \"⋯\" overflow menu is this with an icon-only Button; there is no separate\ncomponent for it, because the only difference is the Button's content and\nits required aria-label.\n\n`useMenuTrigger` does what a Popover wrapped round a Menu could not: the\ntrigger announces `aria-haspopup=\"menu\"` and `aria-expanded`, the menu is\nnamed by the trigger, ArrowDown and ArrowUp open it with focus on the first\nor last row, and choosing an action closes it and returns focus to the\ntrigger.",
|
|
8096
|
+
"import": "import { MenuTrigger } from 'ionbase-ui';",
|
|
7281
8097
|
"status": "stable",
|
|
7282
|
-
"
|
|
7283
|
-
"summary": "A focus-trapping dialog. Renders nothing when closed and portals when open.",
|
|
8098
|
+
"summary": "A Button that opens a Menu. The \"⋯\" overflow menu is this with an icon-only Button. Owns open state, positioning, focus, and the ARIA link between the two.",
|
|
7284
8099
|
"useWhen": [
|
|
7285
|
-
"
|
|
7286
|
-
"
|
|
7287
|
-
"
|
|
8100
|
+
"a button opens a list of actions — row actions, an overflow \"⋯\", a \"New\" button with several kinds of new",
|
|
8101
|
+
"a button opens a list of choices applied immediately, such as a sort order",
|
|
8102
|
+
"the actions nest one level or more — submenus open only inside a MenuTrigger"
|
|
7288
8103
|
],
|
|
7289
8104
|
"useInstead": [
|
|
7290
8105
|
{
|
|
7291
|
-
"when": "the
|
|
8106
|
+
"when": "the button opens a form, a filter panel or anything that is not a list of rows",
|
|
7292
8107
|
"use": "Popover",
|
|
7293
|
-
"why": "a
|
|
8108
|
+
"why": "a menu promises arrow keys between rows and nothing else; a field inside it cannot be reached"
|
|
7294
8109
|
},
|
|
7295
8110
|
{
|
|
7296
|
-
"when": "
|
|
7297
|
-
"use": "
|
|
8111
|
+
"when": "the choice is a form field's value",
|
|
8112
|
+
"use": "Select",
|
|
8113
|
+
"why": "a form value is a listbox the form can read, not a menu of actions"
|
|
8114
|
+
},
|
|
8115
|
+
{
|
|
8116
|
+
"when": "there are only two or three actions and room to show them",
|
|
8117
|
+
"use": "Button",
|
|
8118
|
+
"why": "hiding actions behind a menu costs a click and makes them undiscoverable; show them when they fit"
|
|
7298
8119
|
}
|
|
7299
8120
|
],
|
|
8121
|
+
"composition": {
|
|
8122
|
+
"order": [
|
|
8123
|
+
"MenuTrigger",
|
|
8124
|
+
"Button",
|
|
8125
|
+
"Menu"
|
|
8126
|
+
],
|
|
8127
|
+
"note": "Exactly two children, in this order: the Button, then the Menu. The Menu is named by the Button unless it has its own aria-label. A MenuItem with a `title` and MenuItem children opens a submenu; `onAction` on the root Menu receives actions from every level.",
|
|
8128
|
+
"example": "<MenuTrigger><Button variant=\"tertiary\" aria-label=\"Actions for Payroll\" startIcon={<Icon as={Ellipsis} />} /><Menu onAction={(key) => run(key)}><MenuItem key=\"rename\">Rename</MenuItem><MenuItem key=\"move\" title=\"Move to\"><MenuItem key=\"archive\">Archive</MenuItem><MenuItem key=\"trash\">Trash</MenuItem></MenuItem></Menu></MenuTrigger>"
|
|
8129
|
+
},
|
|
7300
8130
|
"variants": {
|
|
7301
|
-
"
|
|
7302
|
-
"
|
|
7303
|
-
"use": "
|
|
7304
|
-
},
|
|
7305
|
-
"md": {
|
|
7306
|
-
"use": "the default; short forms"
|
|
8131
|
+
"placement": {
|
|
8132
|
+
"bottom start": {
|
|
8133
|
+
"use": "the default — below, aligned to the trigger's start edge"
|
|
7307
8134
|
},
|
|
7308
|
-
"
|
|
7309
|
-
"use": "
|
|
8135
|
+
"bottom end": {
|
|
8136
|
+
"use": "a trigger at the end of a row or toolbar, so the menu does not run off the edge"
|
|
7310
8137
|
},
|
|
7311
|
-
"
|
|
7312
|
-
"use": "
|
|
7313
|
-
}
|
|
7314
|
-
},
|
|
7315
|
-
"align": {
|
|
7316
|
-
"left": {
|
|
7317
|
-
"use": "the default; forms and anything the user reads"
|
|
8138
|
+
"top start": {
|
|
8139
|
+
"use": "a trigger near the bottom of the viewport, such as a footer"
|
|
7318
8140
|
},
|
|
7319
|
-
"
|
|
7320
|
-
"use": "
|
|
8141
|
+
"top end": {
|
|
8142
|
+
"use": "as top start, for a trigger at the end of its row"
|
|
7321
8143
|
}
|
|
7322
8144
|
}
|
|
7323
8145
|
},
|
|
7324
8146
|
"slots": {
|
|
7325
|
-
"
|
|
7326
|
-
"accepts":
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
"note": "
|
|
8147
|
+
"children": {
|
|
8148
|
+
"accepts": [
|
|
8149
|
+
"Button",
|
|
8150
|
+
"Menu"
|
|
8151
|
+
],
|
|
8152
|
+
"note": "the Button first, the Menu second"
|
|
7331
8153
|
}
|
|
7332
8154
|
},
|
|
7333
8155
|
"a11y": {
|
|
7334
|
-
"role": "
|
|
8156
|
+
"role": "none — the Button gets aria-haspopup=\"menu\" and aria-expanded; the Menu is the menu",
|
|
8157
|
+
"namesChild": true,
|
|
7335
8158
|
"guarantees": [
|
|
7336
|
-
"
|
|
7337
|
-
"
|
|
7338
|
-
"
|
|
7339
|
-
"
|
|
8159
|
+
"Enter, Space and ArrowDown open the menu with focus on its first row; ArrowUp opens it on the last",
|
|
8160
|
+
"choosing an action closes every open level and returns focus to the trigger; so do Escape and an outside click",
|
|
8161
|
+
"the menu is named by the trigger unless it has its own aria-label",
|
|
8162
|
+
"a submenu opens with the right arrow, Enter or Space and closes with the left arrow or Escape, which puts focus back on the row that opened it",
|
|
8163
|
+
"submenus are non-modal: the parent menu stays on screen and hoverable, so the pointer can move between levels"
|
|
7340
8164
|
],
|
|
7341
8165
|
"requires": [
|
|
7342
|
-
"
|
|
8166
|
+
"an icon-only trigger Button needs its own aria-label — it names the menu too"
|
|
7343
8167
|
]
|
|
7344
8168
|
},
|
|
7345
8169
|
"antiPatterns": [
|
|
7346
8170
|
{
|
|
7347
|
-
"dont": "
|
|
7348
|
-
"
|
|
7349
|
-
"why": "Modal already renders nothing when closed. Wrapping it in a conditional discards the open/close transition and the focus-return behaviour."
|
|
8171
|
+
"dont": "wrapping a Menu in a Popover to make a dropdown",
|
|
8172
|
+
"why": "the trigger then announces a dialog, not a menu, the arrow keys cannot open it, and choosing an action does not close it. That is exactly what MenuTrigger exists to replace"
|
|
7350
8173
|
},
|
|
7351
8174
|
{
|
|
7352
|
-
"dont": "
|
|
7353
|
-
"why": "
|
|
8175
|
+
"dont": "a plain <button> as the trigger",
|
|
8176
|
+
"why": "the trigger must accept react-aria press props and a ref; IonBase's Button does, a DOM button silently never opens"
|
|
7354
8177
|
},
|
|
7355
8178
|
{
|
|
7356
|
-
"dont": "
|
|
7357
|
-
"why": "a
|
|
8179
|
+
"dont": "more than two levels of submenu",
|
|
8180
|
+
"why": "each level is a hover target narrower than the last; past two, the pointer falls off the path and the menu closes. Flatten with sections instead"
|
|
7358
8181
|
}
|
|
7359
8182
|
],
|
|
7360
|
-
"stylesheet": "src/styles/
|
|
8183
|
+
"stylesheet": "src/styles/menu.css",
|
|
7361
8184
|
"tokens": [
|
|
8185
|
+
"--border-focus",
|
|
8186
|
+
"--border-strong",
|
|
7362
8187
|
"--border-subtle",
|
|
7363
8188
|
"--border-width-default",
|
|
7364
8189
|
"--border-width-thick",
|
|
7365
8190
|
"--font-family-sans",
|
|
7366
8191
|
"--font-weight-regular",
|
|
7367
|
-
"--
|
|
7368
|
-
"--icon-
|
|
8192
|
+
"--icon-default",
|
|
8193
|
+
"--icon-disabled",
|
|
8194
|
+
"--icon-primary",
|
|
8195
|
+
"--icon-size-sm",
|
|
8196
|
+
"--ion-duration-base",
|
|
8197
|
+
"--ion-ease-out",
|
|
8198
|
+
"--ion-menu-min-width",
|
|
8199
|
+
"--ion-shadow-shadow-lg",
|
|
8200
|
+
"--radius-sm",
|
|
8201
|
+
"--radius-xl",
|
|
8202
|
+
"--spacing-12",
|
|
8203
|
+
"--spacing-2",
|
|
8204
|
+
"--spacing-4",
|
|
8205
|
+
"--spacing-40",
|
|
8206
|
+
"--spacing-6",
|
|
8207
|
+
"--spacing-8",
|
|
8208
|
+
"--surface-default",
|
|
8209
|
+
"--surface-hover",
|
|
8210
|
+
"--surface-primary-subtle",
|
|
8211
|
+
"--text-disabled",
|
|
8212
|
+
"--text-primary",
|
|
8213
|
+
"--text-secondary",
|
|
8214
|
+
"--text-tertiary",
|
|
8215
|
+
"--type-body",
|
|
8216
|
+
"--type-body-line-height",
|
|
8217
|
+
"--type-caption",
|
|
8218
|
+
"--type-caption-line-height"
|
|
8219
|
+
],
|
|
8220
|
+
"props": {
|
|
8221
|
+
"children": {
|
|
8222
|
+
"type": "[React.ReactElement<unknown, string | React.JSXElementConstructor<any>>, React.ReactElement<unknown, string | React.JSXElementConstructor<any>>]",
|
|
8223
|
+
"required": true,
|
|
8224
|
+
"origin": "own",
|
|
8225
|
+
"description": "Exactly two children: the Button that opens the menu, then the Menu. The\nButton must accept a ref and react-aria press props — IonBase's Button\ndoes; a plain `<button>` does not."
|
|
8226
|
+
},
|
|
8227
|
+
"placement": {
|
|
8228
|
+
"type": "MenuTriggerPlacement | undefined",
|
|
8229
|
+
"required": false,
|
|
8230
|
+
"origin": "own",
|
|
8231
|
+
"description": "Where the menu opens, relative to the trigger. A preference, not a\nguarantee: it flips when there is no room.",
|
|
8232
|
+
"values": [
|
|
8233
|
+
"bottom start",
|
|
8234
|
+
"bottom end",
|
|
8235
|
+
"top start",
|
|
8236
|
+
"top end"
|
|
8237
|
+
]
|
|
8238
|
+
},
|
|
8239
|
+
"isDisabled": {
|
|
8240
|
+
"type": "boolean | undefined",
|
|
8241
|
+
"required": false,
|
|
8242
|
+
"origin": "own",
|
|
8243
|
+
"description": "Whether the trigger is disabled."
|
|
8244
|
+
},
|
|
8245
|
+
"isOpen": {
|
|
8246
|
+
"type": "boolean | undefined",
|
|
8247
|
+
"required": false,
|
|
8248
|
+
"origin": "own"
|
|
8249
|
+
},
|
|
8250
|
+
"defaultOpen": {
|
|
8251
|
+
"type": "boolean | undefined",
|
|
8252
|
+
"required": false,
|
|
8253
|
+
"origin": "own"
|
|
8254
|
+
},
|
|
8255
|
+
"onOpenChange": {
|
|
8256
|
+
"type": "((isOpen: boolean) => void) | undefined",
|
|
8257
|
+
"required": false,
|
|
8258
|
+
"origin": "own"
|
|
8259
|
+
}
|
|
8260
|
+
},
|
|
8261
|
+
"propCounts": {
|
|
8262
|
+
"own": 6,
|
|
8263
|
+
"aria": 0,
|
|
8264
|
+
"dom": 0,
|
|
8265
|
+
"other": 0
|
|
8266
|
+
}
|
|
8267
|
+
},
|
|
8268
|
+
"Modal": {
|
|
8269
|
+
"name": "Modal",
|
|
8270
|
+
"source": "src/components/Modal.tsx",
|
|
8271
|
+
"propsType": "ModalProps",
|
|
8272
|
+
"description": "Modal — Figma `Modal` (792:1537).\n\n`Size` x `Align`, with the sections as props rather than variants. The panel\nhugs its content at every size except `fullscreen`, which fills the viewport\nand pushes the actions to the bottom edge.\n\nRENDERS NOTHING WHEN CLOSED, and mounts into a portal when open. Both come\nfrom React Aria's `Overlay`, and both matter: a modal left in the tree while\nclosed is still focusable by keyboard, and one rendered inline inherits any\n`overflow: hidden` or stacking context from wherever it was written.\n\nThe scrim is part of this component in code, unlike in Figma — see the note\nin modal.css.",
|
|
8273
|
+
"import": "import { Modal } from 'ionbase-ui';",
|
|
8274
|
+
"status": "stable",
|
|
8275
|
+
"since": "0.11.0",
|
|
8276
|
+
"summary": "A focus-trapping dialog. Renders nothing when closed and portals when open.",
|
|
8277
|
+
"useWhen": [
|
|
8278
|
+
"the task must be completed or abandoned before anything else",
|
|
8279
|
+
"confirming a destructive action",
|
|
8280
|
+
"a short focused form that would otherwise need its own page"
|
|
8281
|
+
],
|
|
8282
|
+
"useInstead": [
|
|
8283
|
+
{
|
|
8284
|
+
"when": "the content is supplementary and the page stays usable",
|
|
8285
|
+
"use": "Popover",
|
|
8286
|
+
"why": "a Popover does not trap focus or block the page"
|
|
8287
|
+
},
|
|
8288
|
+
{
|
|
8289
|
+
"when": "it is a short label for a control",
|
|
8290
|
+
"use": "Tooltip"
|
|
8291
|
+
}
|
|
8292
|
+
],
|
|
8293
|
+
"variants": {
|
|
8294
|
+
"size": {
|
|
8295
|
+
"sm": {
|
|
8296
|
+
"use": "confirmations and single-question prompts"
|
|
8297
|
+
},
|
|
8298
|
+
"md": {
|
|
8299
|
+
"use": "the default; short forms"
|
|
8300
|
+
},
|
|
8301
|
+
"lg": {
|
|
8302
|
+
"use": "multi-section forms"
|
|
8303
|
+
},
|
|
8304
|
+
"fullscreen": {
|
|
8305
|
+
"use": "immersive tasks; fills the viewport and pins the actions to the bottom edge"
|
|
8306
|
+
}
|
|
8307
|
+
},
|
|
8308
|
+
"align": {
|
|
8309
|
+
"left": {
|
|
8310
|
+
"use": "the default; forms and anything the user reads"
|
|
8311
|
+
},
|
|
8312
|
+
"center": {
|
|
8313
|
+
"use": "single-message confirmations only"
|
|
8314
|
+
}
|
|
8315
|
+
}
|
|
8316
|
+
},
|
|
8317
|
+
"slots": {
|
|
8318
|
+
"footer": {
|
|
8319
|
+
"accepts": "Button",
|
|
8320
|
+
"note": "the primary action goes last, at the trailing edge"
|
|
8321
|
+
},
|
|
8322
|
+
"media": {
|
|
8323
|
+
"note": "rendered above the title"
|
|
8324
|
+
}
|
|
8325
|
+
},
|
|
8326
|
+
"a11y": {
|
|
8327
|
+
"role": "dialog",
|
|
8328
|
+
"guarantees": [
|
|
8329
|
+
"focus moves into the panel on open and returns to the trigger on close",
|
|
8330
|
+
"Escape and a scrim click both close it",
|
|
8331
|
+
"focus is trapped while open",
|
|
8332
|
+
"nothing is rendered at all while closed, so no closed-modal content is keyboard reachable"
|
|
8333
|
+
],
|
|
8334
|
+
"requires": [
|
|
8335
|
+
"`title`, which provides the dialog's accessible name"
|
|
8336
|
+
]
|
|
8337
|
+
},
|
|
8338
|
+
"antiPatterns": [
|
|
8339
|
+
{
|
|
8340
|
+
"dont": "rendering <Modal> only when a state flag is true",
|
|
8341
|
+
"do": "render it always and drive `isOpen`",
|
|
8342
|
+
"why": "Modal already renders nothing when closed. Wrapping it in a conditional discards the open/close transition and the focus-return behaviour."
|
|
8343
|
+
},
|
|
8344
|
+
{
|
|
8345
|
+
"dont": "opening a Modal from inside another Modal",
|
|
8346
|
+
"why": "two focus traps compete and Escape becomes ambiguous"
|
|
8347
|
+
},
|
|
8348
|
+
{
|
|
8349
|
+
"dont": "a destructive action as the only button, with no cancel",
|
|
8350
|
+
"why": "a confirmation with no way out is not a confirmation"
|
|
8351
|
+
}
|
|
8352
|
+
],
|
|
8353
|
+
"stylesheet": "src/styles/modal.css",
|
|
8354
|
+
"tokens": [
|
|
8355
|
+
"--border-subtle",
|
|
8356
|
+
"--border-width-default",
|
|
8357
|
+
"--border-width-thick",
|
|
8358
|
+
"--font-family-sans",
|
|
8359
|
+
"--font-weight-regular",
|
|
8360
|
+
"--font-weight-semibold",
|
|
8361
|
+
"--icon-primary",
|
|
7369
8362
|
"--icon-secondary",
|
|
7370
8363
|
"--icon-size-lg",
|
|
7371
8364
|
"--icon-size-md",
|
|
@@ -8063,112 +9056,281 @@
|
|
|
8063
9056
|
"other": 2
|
|
8064
9057
|
}
|
|
8065
9058
|
},
|
|
8066
|
-
"
|
|
8067
|
-
"name": "
|
|
8068
|
-
"source": "src/components/
|
|
8069
|
-
"propsType": "
|
|
8070
|
-
"description": "
|
|
8071
|
-
"import": "import {
|
|
9059
|
+
"PageHeader": {
|
|
9060
|
+
"name": "PageHeader",
|
|
9061
|
+
"source": "src/components/PageHeader.tsx",
|
|
9062
|
+
"propsType": "PageHeaderProps",
|
|
9063
|
+
"description": "PageHeader — the top of a page: where you are, what it is, and what you can\ndo to it.\n\nPromoted from the demo app, where five screens hand-wrote it in three\ndifferent shapes (`.demo-page__header`, `.demo-run-header`, and a bare\n`<div>`), each with its own gap and its own idea of where the actions\naligned. Every enterprise system ships one — Carbon's PageHeader,\nLightning's page headers — because every page has one.\n\nNOT A LANDMARK\n\nIt renders a `<div>`, not a `<header>`. A `<header>` that is a child of\n`<body>` is the page's banner, and the app shell's Header already is that.\nThe page's landmark is `<main>`, named by this title: pass `titleId` and\npoint `<main aria-labelledby>` at it.\n\nNo `'use client'`: `useId` resolves on the server, and nothing here is\ninteractive. Interactive parts arrive through the slots.",
|
|
9064
|
+
"import": "import { PageHeader } from 'ionbase-ui';",
|
|
8072
9065
|
"status": "stable",
|
|
8073
|
-
"summary": "
|
|
9066
|
+
"summary": "The top of a page: breadcrumb, the page's h1, a description, the record's status, and the page's actions. Not a landmark — main is, named by this title.",
|
|
8074
9067
|
"useWhen": [
|
|
8075
|
-
"
|
|
8076
|
-
"the
|
|
9068
|
+
"the top of every page inside the app shell — list pages, record pages, settings, wizards",
|
|
9069
|
+
"the page has a title and actions that act on the whole page, such as New agent or Pause"
|
|
8077
9070
|
],
|
|
8078
9071
|
"useInstead": [
|
|
8079
9072
|
{
|
|
8080
|
-
"when": "the
|
|
8081
|
-
"use": "
|
|
8082
|
-
"why": "
|
|
9073
|
+
"when": "it is the bar across the top of the whole app, with the logo and the account menu",
|
|
9074
|
+
"use": "Header",
|
|
9075
|
+
"why": "Header is the banner landmark, rendered once by the shell. PageHeader is inside main and changes with each page"
|
|
8083
9076
|
},
|
|
8084
9077
|
{
|
|
8085
|
-
"when": "
|
|
8086
|
-
"use": "
|
|
8087
|
-
"why": "
|
|
9078
|
+
"when": "it titles one section of a page rather than the page",
|
|
9079
|
+
"use": "Card",
|
|
9080
|
+
"why": "a Card's title is an h2 for a named region; a page has one PageHeader and one h1"
|
|
8088
9081
|
},
|
|
8089
9082
|
{
|
|
8090
|
-
"when": "
|
|
8091
|
-
"use": "
|
|
9083
|
+
"when": "the page is empty or failed to load",
|
|
9084
|
+
"use": "EmptyState",
|
|
9085
|
+
"why": "keep the PageHeader so the user still knows where they are, and put the EmptyState beneath it with headingLevel 2"
|
|
8092
9086
|
}
|
|
8093
9087
|
],
|
|
8094
|
-
"
|
|
8095
|
-
"
|
|
8096
|
-
"
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
"
|
|
8100
|
-
|
|
8101
|
-
|
|
9088
|
+
"composition": {
|
|
9089
|
+
"order": [
|
|
9090
|
+
"PageHeader",
|
|
9091
|
+
"Breadcrumb",
|
|
9092
|
+
"Badge",
|
|
9093
|
+
"Button",
|
|
9094
|
+
"MenuTrigger",
|
|
9095
|
+
"Tabs"
|
|
9096
|
+
],
|
|
9097
|
+
"note": "Slots, not children: `breadcrumb` above, `status` beside the title, `actions` at the end of the title row, and `children` as a row beneath for the page's Tabs or filters. Pass `titleId` and point `<main aria-labelledby>` at it.",
|
|
9098
|
+
"example": "<main aria-labelledby=\"page-title\"><PageHeader titleId=\"page-title\" title=\"Payroll reconciler\" description=\"Matches payroll exports against the ledger every night.\" breadcrumb={<Breadcrumb><BreadcrumbItem href=\"/agents\">Agents</BreadcrumbItem><BreadcrumbItem isCurrent>Payroll reconciler</BreadcrumbItem></Breadcrumb>} status={<Badge intent=\"warning\">Paused</Badge>} actions={<><MenuTrigger><Button variant=\"tertiary\" aria-label=\"More actions\" startIcon={<Icon as={Ellipsis} />} /><Menu onAction={run}><MenuItem key=\"delete\">Delete…</MenuItem></Menu></MenuTrigger><Button variant=\"primary-brand\">Resume</Button></>} /></main>"
|
|
9099
|
+
},
|
|
9100
|
+
"slots": {
|
|
9101
|
+
"breadcrumb": {
|
|
9102
|
+
"accepts": "Breadcrumb"
|
|
8102
9103
|
},
|
|
8103
|
-
"
|
|
8104
|
-
"
|
|
8105
|
-
"
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
9104
|
+
"status": {
|
|
9105
|
+
"accepts": [
|
|
9106
|
+
"Badge",
|
|
9107
|
+
"Tag"
|
|
9108
|
+
],
|
|
9109
|
+
"note": "the record's state. Never an action, and never colour alone — a Badge carries its label"
|
|
9110
|
+
},
|
|
9111
|
+
"actions": {
|
|
9112
|
+
"accepts": [
|
|
9113
|
+
"Button",
|
|
9114
|
+
"MenuTrigger",
|
|
9115
|
+
"Link"
|
|
9116
|
+
],
|
|
9117
|
+
"note": "most important last, so the primary action sits at the end of the row. One primary Button at most; beyond two or three actions, put the rest in a MenuTrigger"
|
|
9118
|
+
},
|
|
9119
|
+
"children": {
|
|
9120
|
+
"accepts": [
|
|
9121
|
+
"Tabs",
|
|
9122
|
+
"SegmentedControl"
|
|
9123
|
+
],
|
|
9124
|
+
"note": "a row that belongs to the header: the page's own tabs, or the filters its table answers to"
|
|
8113
9125
|
}
|
|
8114
9126
|
},
|
|
8115
9127
|
"a11y": {
|
|
8116
|
-
"role": "
|
|
9128
|
+
"role": "none — a <div>. The page's landmark is <main>, named by this title",
|
|
8117
9129
|
"guarantees": [
|
|
8118
|
-
"the
|
|
8119
|
-
"
|
|
8120
|
-
"every cell has an explicit label — \"Go to page 4\", \"Go to previous page\" — so a screen reader never announces a bare digit",
|
|
8121
|
-
"the ellipsis is `aria-hidden` and not focusable: it marks absent pages, and a tab stop that does nothing when activated is worse than no tab stop",
|
|
8122
|
-
"in `simple`, the \"Page N of M\" text is an `aria-live=\"polite\"` region, because the page can change without anything else on screen announcing it",
|
|
8123
|
-
"prev and next are genuinely `disabled` at the ends rather than styled to look it",
|
|
8124
|
-
"the focus ring shows for keyboard focus only, and hover does not latch on touch"
|
|
9130
|
+
"the title is a real heading, h1 by default, with a stable id for aria-labelledby",
|
|
9131
|
+
"actions keep their DOM order when they wrap beneath the title on a narrow screen, so reading order matches visual order"
|
|
8125
9132
|
],
|
|
8126
|
-
"
|
|
8127
|
-
"`
|
|
8128
|
-
"
|
|
9133
|
+
"notes": [
|
|
9134
|
+
"`headingLevel` is 1 by default. Use 2 only for a header on a pane that is not the page — the detail half of a list-detail layout, under the list's h1.",
|
|
9135
|
+
"It is deliberately not a <header>: a <header> directly inside <body> is the banner, and the app shell's Header already is that."
|
|
8129
9136
|
]
|
|
8130
9137
|
},
|
|
8131
9138
|
"antiPatterns": [
|
|
8132
9139
|
{
|
|
8133
|
-
"
|
|
8134
|
-
"
|
|
9140
|
+
"dont": "two PageHeaders on one page",
|
|
9141
|
+
"why": "two h1s, and two answers to where am I. Sections below the header are Cards with h2 titles"
|
|
8135
9142
|
},
|
|
8136
9143
|
{
|
|
8137
|
-
"
|
|
8138
|
-
"
|
|
9144
|
+
"dont": "dropping the PageHeader while the page loads or fails",
|
|
9145
|
+
"why": "the user loses where they are exactly when something went wrong. Keep the title; put Skeleton or EmptyState beneath it"
|
|
8139
9146
|
},
|
|
8140
9147
|
{
|
|
8141
|
-
"
|
|
8142
|
-
"
|
|
9148
|
+
"dont": "putting filters or a search field in `actions`",
|
|
9149
|
+
"why": "actions act on the page; filters narrow what it shows. Filters go in `children`, the row beneath"
|
|
9150
|
+
},
|
|
9151
|
+
{
|
|
9152
|
+
"dont": "several primary Buttons in `actions`",
|
|
9153
|
+
"why": "a page has one next step. The one-primary-action lint rule counts them"
|
|
8143
9154
|
}
|
|
8144
9155
|
],
|
|
8145
|
-
"stylesheet": "src/styles/
|
|
9156
|
+
"stylesheet": "src/styles/page-header.css",
|
|
8146
9157
|
"tokens": [
|
|
8147
|
-
"--border-focus",
|
|
8148
|
-
"--border-width-thick",
|
|
8149
9158
|
"--font-family-sans",
|
|
8150
|
-
"--font-weight-
|
|
8151
|
-
"--
|
|
8152
|
-
"--icon-size-lg",
|
|
8153
|
-
"--icon-size-md",
|
|
8154
|
-
"--icon-size-xs",
|
|
8155
|
-
"--ion-duration-base",
|
|
8156
|
-
"--ion-ease-out",
|
|
8157
|
-
"--ion-pagination-box",
|
|
8158
|
-
"--ion-pagination-font-size",
|
|
8159
|
-
"--ion-pagination-icon",
|
|
8160
|
-
"--ion-pagination-line-height",
|
|
8161
|
-
"--radius-md",
|
|
9159
|
+
"--font-weight-semibold",
|
|
9160
|
+
"--ion-page-header-heading-basis",
|
|
8162
9161
|
"--spacing-16",
|
|
8163
|
-
"--spacing-2",
|
|
8164
|
-
"--spacing-32",
|
|
8165
9162
|
"--spacing-4",
|
|
8166
|
-
"--spacing-40",
|
|
8167
|
-
"--spacing-48",
|
|
8168
|
-
"--spacing-6",
|
|
8169
9163
|
"--spacing-8",
|
|
8170
|
-
"--
|
|
8171
|
-
"--
|
|
9164
|
+
"--text-default",
|
|
9165
|
+
"--text-secondary",
|
|
9166
|
+
"--type-body",
|
|
9167
|
+
"--type-body-line-height",
|
|
9168
|
+
"--type-h4",
|
|
9169
|
+
"--type-h4-line-height"
|
|
9170
|
+
],
|
|
9171
|
+
"props": {
|
|
9172
|
+
"title": {
|
|
9173
|
+
"type": "React.ReactNode",
|
|
9174
|
+
"required": true,
|
|
9175
|
+
"origin": "own",
|
|
9176
|
+
"description": "The page's title. Rendered as the `h1`."
|
|
9177
|
+
},
|
|
9178
|
+
"titleId": {
|
|
9179
|
+
"type": "string | undefined",
|
|
9180
|
+
"required": false,
|
|
9181
|
+
"origin": "own",
|
|
9182
|
+
"description": "The title's id. Pass it when something else points at the heading —\n`<main aria-labelledby>` is the usual one. Generated when omitted."
|
|
9183
|
+
},
|
|
9184
|
+
"headingLevel": {
|
|
9185
|
+
"type": "PageHeaderHeadingLevel | undefined",
|
|
9186
|
+
"required": false,
|
|
9187
|
+
"origin": "own",
|
|
9188
|
+
"description": "Heading element for the title. Defaults to `1`."
|
|
9189
|
+
},
|
|
9190
|
+
"description": {
|
|
9191
|
+
"type": "React.ReactNode",
|
|
9192
|
+
"required": false,
|
|
9193
|
+
"origin": "own",
|
|
9194
|
+
"description": "One or two sentences under the title: what this page is for."
|
|
9195
|
+
},
|
|
9196
|
+
"breadcrumb": {
|
|
9197
|
+
"type": "React.ReactNode",
|
|
9198
|
+
"required": false,
|
|
9199
|
+
"origin": "own",
|
|
9200
|
+
"description": "A Breadcrumb, above the title."
|
|
9201
|
+
},
|
|
9202
|
+
"status": {
|
|
9203
|
+
"type": "React.ReactNode",
|
|
9204
|
+
"required": false,
|
|
9205
|
+
"origin": "own",
|
|
9206
|
+
"description": "Beside the title: a Badge or two for the record's state — \"Paused\",\n\"Draft\". State, not actions."
|
|
9207
|
+
},
|
|
9208
|
+
"actions": {
|
|
9209
|
+
"type": "React.ReactNode",
|
|
9210
|
+
"required": false,
|
|
9211
|
+
"origin": "own",
|
|
9212
|
+
"description": "At the end of the title row: the page's actions, most important last.\nOne primary Button at most; several more go in a MenuTrigger."
|
|
9213
|
+
},
|
|
9214
|
+
"children": {
|
|
9215
|
+
"type": "React.ReactNode",
|
|
9216
|
+
"required": false,
|
|
9217
|
+
"origin": "own",
|
|
9218
|
+
"description": "A row beneath, that belongs to the header rather than the page's\ncontent: the page's Tabs, or the filters its table answers to."
|
|
9219
|
+
}
|
|
9220
|
+
},
|
|
9221
|
+
"propCounts": {
|
|
9222
|
+
"own": 8,
|
|
9223
|
+
"aria": 0,
|
|
9224
|
+
"dom": 276,
|
|
9225
|
+
"other": 0
|
|
9226
|
+
}
|
|
9227
|
+
},
|
|
9228
|
+
"Pagination": {
|
|
9229
|
+
"name": "Pagination",
|
|
9230
|
+
"source": "src/components/Pagination.tsx",
|
|
9231
|
+
"propsType": "PaginationProps",
|
|
9232
|
+
"description": "Page navigation for a table or list.\n\nGeometry from the Figma `Pagination` (1291:503) and `Pagination Item`\n(1283:289) components. The Figma item set is not exported — a caller places a\nPagination, never a cell.",
|
|
9233
|
+
"import": "import { Pagination } from 'ionbase-ui';",
|
|
9234
|
+
"status": "stable",
|
|
9235
|
+
"summary": "Page navigation for a table or list. Renders prev/next arrows, a truncated run of page numbers, and an optional rows-per-page Select.",
|
|
9236
|
+
"useWhen": [
|
|
9237
|
+
"a result set is split into pages and the user needs to move between them",
|
|
9238
|
+
"the total number of pages is known — the numbered run needs a last page to anchor to"
|
|
9239
|
+
],
|
|
9240
|
+
"useInstead": [
|
|
9241
|
+
{
|
|
9242
|
+
"when": "the total is unknown or unbounded, as with a cursor-based or infinite feed",
|
|
9243
|
+
"use": "Button",
|
|
9244
|
+
"why": "a numbered pager has to draw a last page; with no total there is nothing to draw, and a bare Load more button is honest about that"
|
|
9245
|
+
},
|
|
9246
|
+
{
|
|
9247
|
+
"when": "the user is moving between peer views rather than through one long list",
|
|
9248
|
+
"use": "Tabs",
|
|
9249
|
+
"why": "pagination implies a sequence with a position; tabs are peers in any order"
|
|
9250
|
+
},
|
|
9251
|
+
{
|
|
9252
|
+
"when": "it is navigation between pages of the app rather than pages of data",
|
|
9253
|
+
"use": "NavItem"
|
|
9254
|
+
}
|
|
9255
|
+
],
|
|
9256
|
+
"variants": {
|
|
9257
|
+
"type": {
|
|
9258
|
+
"numbered": {
|
|
9259
|
+
"use": "the default — prev, the page numbers, next. Use wherever the numbers fit"
|
|
9260
|
+
},
|
|
9261
|
+
"simple": {
|
|
9262
|
+
"use": "prev, \"Page N of M\", next. For drawers, narrow columns and mobile, where a numbered run would wrap or truncate to uselessness"
|
|
9263
|
+
}
|
|
9264
|
+
},
|
|
9265
|
+
"size": {
|
|
9266
|
+
"sm": {
|
|
9267
|
+
"use": "dense tables, and toolbars"
|
|
9268
|
+
},
|
|
9269
|
+
"md": {
|
|
9270
|
+
"use": "the default"
|
|
9271
|
+
},
|
|
9272
|
+
"lg": {
|
|
9273
|
+
"use": "roomy layouts, and touch targets on tablet"
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
},
|
|
9277
|
+
"a11y": {
|
|
9278
|
+
"role": "navigation / list",
|
|
9279
|
+
"guarantees": [
|
|
9280
|
+
"the whole control is a landmark — a `nav` with an accessible name, defaulting to \"Pagination\"",
|
|
9281
|
+
"the current page carries `aria-current=\"page\"`, not `aria-pressed`: this is a position in a set, not a toggle",
|
|
9282
|
+
"every cell has an explicit label — \"Go to page 4\", \"Go to previous page\" — so a screen reader never announces a bare digit",
|
|
9283
|
+
"the ellipsis is `aria-hidden` and not focusable: it marks absent pages, and a tab stop that does nothing when activated is worse than no tab stop",
|
|
9284
|
+
"in `simple`, the \"Page N of M\" text is an `aria-live=\"polite\"` region, because the page can change without anything else on screen announcing it",
|
|
9285
|
+
"prev and next are genuinely `disabled` at the ends rather than styled to look it",
|
|
9286
|
+
"the focus ring shows for keyboard focus only, and hover does not latch on touch"
|
|
9287
|
+
],
|
|
9288
|
+
"requires": [
|
|
9289
|
+
"`aria-label` when more than one Pagination is on a page — two identically named landmarks are two indistinguishable landmarks",
|
|
9290
|
+
"`pageSize` when `showPageSize` is set, or the rows-per-page Select renders with nothing selected"
|
|
9291
|
+
]
|
|
9292
|
+
},
|
|
9293
|
+
"antiPatterns": [
|
|
9294
|
+
{
|
|
9295
|
+
"avoid": "passing `pageCount={0}` for an empty result set",
|
|
9296
|
+
"instead": "render EmptyState and no pager — the component clamps to a single page, which draws a lone disabled `1` that says a page exists when none does"
|
|
9297
|
+
},
|
|
9298
|
+
{
|
|
9299
|
+
"avoid": "raising `siblingCount` to avoid truncation",
|
|
9300
|
+
"instead": "switch to `type=\"simple\"`. Widening the run past the container is the problem truncation exists to solve, and it reappears one breakpoint down"
|
|
9301
|
+
},
|
|
9302
|
+
{
|
|
9303
|
+
"avoid": "treating `page` as 0-based",
|
|
9304
|
+
"instead": "it is 1-based, matching what the numbers say. An off-by-one here is silent — the pager looks right and fetches the wrong rows"
|
|
9305
|
+
}
|
|
9306
|
+
],
|
|
9307
|
+
"stylesheet": "src/styles/pagination.css",
|
|
9308
|
+
"tokens": [
|
|
9309
|
+
"--border-focus",
|
|
9310
|
+
"--border-width-thick",
|
|
9311
|
+
"--font-family-sans",
|
|
9312
|
+
"--font-weight-medium",
|
|
9313
|
+
"--font-weight-regular",
|
|
9314
|
+
"--icon-size-lg",
|
|
9315
|
+
"--icon-size-md",
|
|
9316
|
+
"--icon-size-xs",
|
|
9317
|
+
"--ion-duration-base",
|
|
9318
|
+
"--ion-ease-out",
|
|
9319
|
+
"--ion-pagination-box",
|
|
9320
|
+
"--ion-pagination-font-size",
|
|
9321
|
+
"--ion-pagination-icon",
|
|
9322
|
+
"--ion-pagination-line-height",
|
|
9323
|
+
"--radius-md",
|
|
9324
|
+
"--spacing-16",
|
|
9325
|
+
"--spacing-2",
|
|
9326
|
+
"--spacing-32",
|
|
9327
|
+
"--spacing-4",
|
|
9328
|
+
"--spacing-40",
|
|
9329
|
+
"--spacing-48",
|
|
9330
|
+
"--spacing-6",
|
|
9331
|
+
"--spacing-8",
|
|
9332
|
+
"--surface-hover",
|
|
9333
|
+
"--surface-selected",
|
|
8172
9334
|
"--surface-selected-hover",
|
|
8173
9335
|
"--text-default",
|
|
8174
9336
|
"--text-disabled",
|
|
@@ -9482,7 +10644,6 @@
|
|
|
9482
10644
|
"--border-width-default",
|
|
9483
10645
|
"--border-width-thick",
|
|
9484
10646
|
"--font-family-sans",
|
|
9485
|
-
"--font-weight-medium",
|
|
9486
10647
|
"--icon-disabled",
|
|
9487
10648
|
"--icon-on-color",
|
|
9488
10649
|
"--ion-duration-base",
|
|
@@ -9574,10 +10735,10 @@
|
|
|
9574
10735
|
"name": "RadioGroup",
|
|
9575
10736
|
"source": "src/components/Radio.tsx",
|
|
9576
10737
|
"propsType": "RadioGroupProps",
|
|
9577
|
-
"description": "Renders a `<fieldset>` with a `<legend>` rather than a div with\n`role=\"radiogroup\"`. Both are announced correctly, but a fieldset also groups\nthe inputs for form submission and native validation, which the ARIA version\ndoes not.",
|
|
10738
|
+
"description": "Renders a `<fieldset>` with a `<legend>` rather than a div with\n`role=\"radiogroup\"`. Both are announced correctly, but a fieldset also groups\nthe inputs for form submission and native validation, which the ARIA version\ndoes not.\n\nThe fieldset itself is Fieldset's shell, shared with CheckboxGroup, so the\ntwo choice groups take the same label, help, error and orientation props.",
|
|
9578
10739
|
"import": "import { RadioGroup } from 'ionbase-ui';",
|
|
9579
10740
|
"status": "stable",
|
|
9580
|
-
"summary": "A <fieldset>/<legend> group of mutually exclusive options. Owns the shared name and the selected value.",
|
|
10741
|
+
"summary": "A <fieldset>/<legend> group of mutually exclusive options. Owns the shared name and the selected value, and the group's label, help and error.",
|
|
9581
10742
|
"useWhen": [
|
|
9582
10743
|
"exactly one choice from a small set, all worth showing at once",
|
|
9583
10744
|
"the options need explaining — each one can carry its own label text"
|
|
@@ -9589,7 +10750,7 @@
|
|
|
9589
10750
|
},
|
|
9590
10751
|
{
|
|
9591
10752
|
"when": "more than one may be picked",
|
|
9592
|
-
"use": "
|
|
10753
|
+
"use": "CheckboxGroup"
|
|
9593
10754
|
},
|
|
9594
10755
|
{
|
|
9595
10756
|
"when": "there are exactly two states and the change is immediate",
|
|
@@ -9618,6 +10779,14 @@
|
|
|
9618
10779
|
"danger": {
|
|
9619
10780
|
"use": "a set where the options are destructive"
|
|
9620
10781
|
}
|
|
10782
|
+
},
|
|
10783
|
+
"orientation": {
|
|
10784
|
+
"vertical": {
|
|
10785
|
+
"use": "the default, and always when a label runs past a few words"
|
|
10786
|
+
},
|
|
10787
|
+
"horizontal": {
|
|
10788
|
+
"use": "two to four one-word options. Wraps rather than overflows"
|
|
10789
|
+
}
|
|
9621
10790
|
}
|
|
9622
10791
|
},
|
|
9623
10792
|
"slots": {
|
|
@@ -9625,6 +10794,14 @@
|
|
|
9625
10794
|
"accepts": "text",
|
|
9626
10795
|
"note": "renders as the <legend> — the question the options answer"
|
|
9627
10796
|
},
|
|
10797
|
+
"description": {
|
|
10798
|
+
"accepts": "text",
|
|
10799
|
+
"note": "help beneath the options; replaced by `errorMessage` while `isInvalid` is set"
|
|
10800
|
+
},
|
|
10801
|
+
"errorMessage": {
|
|
10802
|
+
"accepts": "text",
|
|
10803
|
+
"note": "shown only while `isInvalid` is set"
|
|
10804
|
+
},
|
|
9628
10805
|
"children": {
|
|
9629
10806
|
"accepts": "Radio"
|
|
9630
10807
|
}
|
|
@@ -9634,7 +10811,10 @@
|
|
|
9634
10811
|
"guarantees": [
|
|
9635
10812
|
"a real fieldset rather than `role=\"radiogroup\"`: both announce correctly, but the fieldset also groups the inputs for form submission and native validation",
|
|
9636
10813
|
"`name` is generated when omitted, so two groups on one page never collide",
|
|
9637
|
-
"`isDisabled` cascades to every Radio that has not set its own"
|
|
10814
|
+
"`isDisabled` cascades to every Radio that has not set its own",
|
|
10815
|
+
"the fieldset is Fieldset's shell, shared with CheckboxGroup — the same label, help, error and orientation props",
|
|
10816
|
+
"the description or error is on every radio's `aria-describedby` as well as the fieldset's, so it is read on the radio that takes focus",
|
|
10817
|
+
"`isRequired` is native `required` on the radios, which the platform already reads as \"one of this name\""
|
|
9638
10818
|
],
|
|
9639
10819
|
"requires": [
|
|
9640
10820
|
"`label`, or `aria-label` when the question is already visible elsewhere"
|
|
@@ -9671,7 +10851,6 @@
|
|
|
9671
10851
|
"--border-width-default",
|
|
9672
10852
|
"--border-width-thick",
|
|
9673
10853
|
"--font-family-sans",
|
|
9674
|
-
"--font-weight-medium",
|
|
9675
10854
|
"--icon-disabled",
|
|
9676
10855
|
"--icon-on-color",
|
|
9677
10856
|
"--ion-duration-base",
|
|
@@ -9731,7 +10910,41 @@
|
|
|
9731
10910
|
"label": {
|
|
9732
10911
|
"type": "React.ReactNode",
|
|
9733
10912
|
"required": false,
|
|
9734
|
-
"origin": "own"
|
|
10913
|
+
"origin": "own",
|
|
10914
|
+
"description": "The question the options answer. Renders as the `<legend>`."
|
|
10915
|
+
},
|
|
10916
|
+
"description": {
|
|
10917
|
+
"type": "React.ReactNode",
|
|
10918
|
+
"required": false,
|
|
10919
|
+
"origin": "own",
|
|
10920
|
+
"description": "Help text beneath the options. Replaced by `errorMessage` while invalid."
|
|
10921
|
+
},
|
|
10922
|
+
"errorMessage": {
|
|
10923
|
+
"type": "React.ReactNode",
|
|
10924
|
+
"required": false,
|
|
10925
|
+
"origin": "own",
|
|
10926
|
+
"description": "Shown in the description's place while `isInvalid` is set."
|
|
10927
|
+
},
|
|
10928
|
+
"isInvalid": {
|
|
10929
|
+
"type": "boolean | undefined",
|
|
10930
|
+
"required": false,
|
|
10931
|
+
"origin": "own",
|
|
10932
|
+
"description": "Shows `errorMessage` in the description's place."
|
|
10933
|
+
},
|
|
10934
|
+
"isRequired": {
|
|
10935
|
+
"type": "boolean | undefined",
|
|
10936
|
+
"required": false,
|
|
10937
|
+
"origin": "own",
|
|
10938
|
+
"description": "One option must be chosen before the form submits."
|
|
10939
|
+
},
|
|
10940
|
+
"orientation": {
|
|
10941
|
+
"type": "FieldsetOrientation | undefined",
|
|
10942
|
+
"required": false,
|
|
10943
|
+
"origin": "own",
|
|
10944
|
+
"values": [
|
|
10945
|
+
"vertical",
|
|
10946
|
+
"horizontal"
|
|
10947
|
+
]
|
|
9735
10948
|
},
|
|
9736
10949
|
"size": {
|
|
9737
10950
|
"type": "RadioSize | undefined",
|
|
@@ -9774,7 +10987,7 @@
|
|
|
9774
10987
|
}
|
|
9775
10988
|
},
|
|
9776
10989
|
"propCounts": {
|
|
9777
|
-
"own":
|
|
10990
|
+
"own": 15,
|
|
9778
10991
|
"aria": 0,
|
|
9779
10992
|
"dom": 276,
|
|
9780
10993
|
"other": 0
|
|
@@ -9919,6 +11132,493 @@
|
|
|
9919
11132
|
"other": 0
|
|
9920
11133
|
}
|
|
9921
11134
|
},
|
|
11135
|
+
"SearchField": {
|
|
11136
|
+
"name": "SearchField",
|
|
11137
|
+
"source": "src/components/SearchField.tsx",
|
|
11138
|
+
"propsType": "SearchFieldProps",
|
|
11139
|
+
"description": "SearchField — a text field for a search query.\n\nNOT `<Input type=\"search\">`. That gets the right input type and nothing\nelse. `useSearchField` adds what a search box owes its user:\n\n `role=\"searchbox\"`, so a screen reader announces a search field, not a\n text field. Enter calls `onSubmit` with the query. Escape clears it — a\n second Escape then reaches whatever the field sits in, so a search inside\n a dialog clears first and closes second. A clear button appears once there\n is something to clear, named in the user's language by React Aria.\n\nThe clear button is out of the tab order on purpose, as React Aria sets it:\nEscape is the keyboard's way to clear, and an extra tab stop in every search\nbox costs every keyboard user a keystroke. Pressing it puts focus back in the\nfield.\n\nThe box IS Input's — the component renders `.ion-input` and its size and\nstate classes, so a search field beside an Input in a toolbar matches it\nexactly and cannot drift. Same arrangement as NumberInput.",
|
|
11140
|
+
"import": "import { SearchField } from 'ionbase-ui';",
|
|
11141
|
+
"status": "stable",
|
|
11142
|
+
"summary": "A text field for a search query: role=\"searchbox\", Enter submits, Escape clears, and a clear button once there is something to clear. Input's box and sizes.",
|
|
11143
|
+
"useWhen": [
|
|
11144
|
+
"the user types to search or filter a list, a table or a page — the search above a DataTable, the filter at the top of a Sidebar",
|
|
11145
|
+
"the query is free text that narrows what is shown, not a value chosen from a list"
|
|
11146
|
+
],
|
|
11147
|
+
"useInstead": [
|
|
11148
|
+
{
|
|
11149
|
+
"when": "the typed text picks one value from a known list — an assignee, a country",
|
|
11150
|
+
"use": "Combobox",
|
|
11151
|
+
"why": "a combobox commits a choice and offers the options; a search field commits a query and offers nothing"
|
|
11152
|
+
},
|
|
11153
|
+
{
|
|
11154
|
+
"when": "the user is looking for a page or an action anywhere in the product",
|
|
11155
|
+
"use": "CommandPalette",
|
|
11156
|
+
"why": "that is global and keyboard-first; a SearchField searches the content in front of it"
|
|
11157
|
+
},
|
|
11158
|
+
{
|
|
11159
|
+
"when": "it is ordinary text entry — a name, an email — that happens to be used for lookup later",
|
|
11160
|
+
"use": "Input",
|
|
11161
|
+
"why": "announcing a searchbox promises search behaviour: Escape clears, Enter searches"
|
|
11162
|
+
}
|
|
11163
|
+
],
|
|
11164
|
+
"composition": {
|
|
11165
|
+
"order": [
|
|
11166
|
+
"SearchField"
|
|
11167
|
+
],
|
|
11168
|
+
"note": "Filter as the user types with `onChange`, or search on Enter with `onSubmit` — pick one per field and say which in the placeholder or label. In a table toolbar, give it `aria-label` naming what it searches.",
|
|
11169
|
+
"example": "<SearchField aria-label=\"Search agents\" placeholder=\"Search by name or purpose\" value={query} onChange={setQuery} />"
|
|
11170
|
+
},
|
|
11171
|
+
"variants": {
|
|
11172
|
+
"size": {
|
|
11173
|
+
"sm": {
|
|
11174
|
+
"use": "toolbars, table headers and Sidebar filters, beside small Buttons"
|
|
11175
|
+
},
|
|
11176
|
+
"md": {
|
|
11177
|
+
"use": "the default"
|
|
11178
|
+
},
|
|
11179
|
+
"lg": {
|
|
11180
|
+
"use": "a page whose main task is searching"
|
|
11181
|
+
}
|
|
11182
|
+
}
|
|
11183
|
+
},
|
|
11184
|
+
"a11y": {
|
|
11185
|
+
"role": "searchbox",
|
|
11186
|
+
"guarantees": [
|
|
11187
|
+
"the input is role=\"searchbox\" and type=\"search\", so it is announced as a search field",
|
|
11188
|
+
"Escape clears the query; a second Escape is left for whatever contains the field, so a search in a dialog clears before it closes",
|
|
11189
|
+
"Enter calls onSubmit with the query",
|
|
11190
|
+
"the clear button is named by React Aria in the user's language, appears only when there is something to clear, and returns focus to the field",
|
|
11191
|
+
"the clear button is out of the tab order on purpose: Escape is the keyboard's way to clear, and an extra tab stop in every search box costs every keyboard user a keystroke",
|
|
11192
|
+
"the browser's own cancel button is hidden, so there are never two"
|
|
11193
|
+
],
|
|
11194
|
+
"requires": [
|
|
11195
|
+
"`label`, or an `aria-label` naming what is searched when no visible label is rendered — \"Search\" alone does not say what"
|
|
11196
|
+
],
|
|
11197
|
+
"notes": [
|
|
11198
|
+
"When filtering as the user types, announce the result count with a polite live region near the results. The field cannot know how many results its query produced; the list does."
|
|
11199
|
+
]
|
|
11200
|
+
},
|
|
11201
|
+
"antiPatterns": [
|
|
11202
|
+
{
|
|
11203
|
+
"dont": "<Input type=\"search\"> with a magnifier as leadingIcon",
|
|
11204
|
+
"why": "it looks the same and has none of the behaviour: no searchbox role, no Escape to clear, no labelled clear button"
|
|
11205
|
+
},
|
|
11206
|
+
{
|
|
11207
|
+
"dont": "placeholder as the only name",
|
|
11208
|
+
"why": "a placeholder disappears once the user types, and is not a reliable accessible name. Pass `aria-label` or `label`"
|
|
11209
|
+
},
|
|
11210
|
+
{
|
|
11211
|
+
"dont": "a separate Search button beside a field that already filters as you type",
|
|
11212
|
+
"why": "it teaches the user that nothing happens until they press it, and it does nothing when they do"
|
|
11213
|
+
}
|
|
11214
|
+
],
|
|
11215
|
+
"stylesheet": "src/styles/search-field.css",
|
|
11216
|
+
"tokens": [
|
|
11217
|
+
"--border-focus",
|
|
11218
|
+
"--border-width-thick",
|
|
11219
|
+
"--icon-default",
|
|
11220
|
+
"--icon-secondary",
|
|
11221
|
+
"--ion-input-height",
|
|
11222
|
+
"--ion-input-icon-size",
|
|
11223
|
+
"--radius-sm",
|
|
11224
|
+
"--spacing-4",
|
|
11225
|
+
"--spacing-8",
|
|
11226
|
+
"--surface-default",
|
|
11227
|
+
"--surface-hover",
|
|
11228
|
+
"--surface-pressed"
|
|
11229
|
+
],
|
|
11230
|
+
"props": {
|
|
11231
|
+
"size": {
|
|
11232
|
+
"type": "SearchFieldSize | undefined",
|
|
11233
|
+
"required": false,
|
|
11234
|
+
"origin": "own",
|
|
11235
|
+
"description": "Input's sizes: Small, Medium, Large.",
|
|
11236
|
+
"values": [
|
|
11237
|
+
"sm",
|
|
11238
|
+
"md",
|
|
11239
|
+
"lg"
|
|
11240
|
+
],
|
|
11241
|
+
"default": "md"
|
|
11242
|
+
},
|
|
11243
|
+
"className": {
|
|
11244
|
+
"type": "string | undefined",
|
|
11245
|
+
"required": false,
|
|
11246
|
+
"origin": "own",
|
|
11247
|
+
"description": "Class names for the control box (`.ion-input`)."
|
|
11248
|
+
},
|
|
11249
|
+
"wrapperClassName": {
|
|
11250
|
+
"type": "string | undefined",
|
|
11251
|
+
"required": false,
|
|
11252
|
+
"origin": "own",
|
|
11253
|
+
"description": "Class names for the `.ion-field` wrapper when a label or helper is shown."
|
|
11254
|
+
},
|
|
11255
|
+
"enterKeyHint": {
|
|
11256
|
+
"type": "\"done\" | \"enter\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\" | undefined",
|
|
11257
|
+
"required": false,
|
|
11258
|
+
"origin": "aria",
|
|
11259
|
+
"description": "An enumerated attribute that defines what action label or icon to preset for the enter key on\nvirtual keyboards. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint).",
|
|
11260
|
+
"values": [
|
|
11261
|
+
"done",
|
|
11262
|
+
"enter",
|
|
11263
|
+
"go",
|
|
11264
|
+
"next",
|
|
11265
|
+
"previous",
|
|
11266
|
+
"search",
|
|
11267
|
+
"send"
|
|
11268
|
+
]
|
|
11269
|
+
},
|
|
11270
|
+
"type": {
|
|
11271
|
+
"type": "(string & {}) | \"search\" | \"text\" | \"url\" | \"tel\" | \"email\" | \"password\" | undefined",
|
|
11272
|
+
"required": false,
|
|
11273
|
+
"origin": "aria",
|
|
11274
|
+
"description": "The type of input to render. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdeftype).",
|
|
11275
|
+
"tags": {
|
|
11276
|
+
"default": "'search'"
|
|
11277
|
+
}
|
|
11278
|
+
},
|
|
11279
|
+
"onSubmit": {
|
|
11280
|
+
"type": "((value: string) => void) | undefined",
|
|
11281
|
+
"required": false,
|
|
11282
|
+
"origin": "other",
|
|
11283
|
+
"description": "Handler that is called when the SearchField is submitted."
|
|
11284
|
+
},
|
|
11285
|
+
"onClear": {
|
|
11286
|
+
"type": "(() => void) | undefined",
|
|
11287
|
+
"required": false,
|
|
11288
|
+
"origin": "other",
|
|
11289
|
+
"description": "Handler that is called when the clear button is pressed."
|
|
11290
|
+
},
|
|
11291
|
+
"isDisabled": {
|
|
11292
|
+
"type": "boolean | undefined",
|
|
11293
|
+
"required": false,
|
|
11294
|
+
"origin": "aria",
|
|
11295
|
+
"description": "Whether the input is disabled."
|
|
11296
|
+
},
|
|
11297
|
+
"isReadOnly": {
|
|
11298
|
+
"type": "boolean | undefined",
|
|
11299
|
+
"required": false,
|
|
11300
|
+
"origin": "aria",
|
|
11301
|
+
"description": "Whether the input can be selected but not changed by the user."
|
|
11302
|
+
},
|
|
11303
|
+
"isRequired": {
|
|
11304
|
+
"type": "boolean | undefined",
|
|
11305
|
+
"required": false,
|
|
11306
|
+
"origin": "aria",
|
|
11307
|
+
"description": "Whether user input is required on the input before form submission."
|
|
11308
|
+
},
|
|
11309
|
+
"isInvalid": {
|
|
11310
|
+
"type": "boolean | undefined",
|
|
11311
|
+
"required": false,
|
|
11312
|
+
"origin": "aria",
|
|
11313
|
+
"description": "Whether the input value is invalid."
|
|
11314
|
+
},
|
|
11315
|
+
"validationState": {
|
|
11316
|
+
"type": "ValidationState | undefined",
|
|
11317
|
+
"required": false,
|
|
11318
|
+
"origin": "aria",
|
|
11319
|
+
"values": [
|
|
11320
|
+
"valid",
|
|
11321
|
+
"invalid"
|
|
11322
|
+
],
|
|
11323
|
+
"tags": {
|
|
11324
|
+
"deprecated": "Use `isInvalid` instead."
|
|
11325
|
+
}
|
|
11326
|
+
},
|
|
11327
|
+
"validationBehavior": {
|
|
11328
|
+
"type": "\"aria\" | \"native\" | undefined",
|
|
11329
|
+
"required": false,
|
|
11330
|
+
"origin": "aria",
|
|
11331
|
+
"description": "Whether to use native HTML form validation to prevent form submission\nwhen the value is missing or invalid, or mark the field as required\nor invalid via ARIA.",
|
|
11332
|
+
"values": [
|
|
11333
|
+
"aria",
|
|
11334
|
+
"native"
|
|
11335
|
+
],
|
|
11336
|
+
"tags": {
|
|
11337
|
+
"default": "'aria'"
|
|
11338
|
+
}
|
|
11339
|
+
},
|
|
11340
|
+
"validate": {
|
|
11341
|
+
"type": "((value: string) => ValidationError | true | null | undefined) | undefined",
|
|
11342
|
+
"required": false,
|
|
11343
|
+
"origin": "aria",
|
|
11344
|
+
"description": "A function that returns an error message if a given value is invalid.\nValidation errors are displayed to the user when the form is submitted\nif `validationBehavior=\"native\"`. For realtime validation, use the `isInvalid`\nprop instead."
|
|
11345
|
+
},
|
|
11346
|
+
"description": {
|
|
11347
|
+
"type": "ReactNode",
|
|
11348
|
+
"required": false,
|
|
11349
|
+
"origin": "aria",
|
|
11350
|
+
"description": "A description for the field. Provides a hint such as specific requirements for what to choose."
|
|
11351
|
+
},
|
|
11352
|
+
"errorMessage": {
|
|
11353
|
+
"type": "ReactNode | ((v: ValidationResult) => ReactNode)",
|
|
11354
|
+
"required": false,
|
|
11355
|
+
"origin": "aria",
|
|
11356
|
+
"description": "An error message for the field."
|
|
11357
|
+
},
|
|
11358
|
+
"autoFocus": {
|
|
11359
|
+
"type": "boolean | undefined",
|
|
11360
|
+
"required": false,
|
|
11361
|
+
"origin": "aria",
|
|
11362
|
+
"description": "Whether the element should receive focus on render."
|
|
11363
|
+
},
|
|
11364
|
+
"onFocus": {
|
|
11365
|
+
"type": "((e: FocusEvent<HTMLInputElement, Element>) => void) | undefined",
|
|
11366
|
+
"required": false,
|
|
11367
|
+
"origin": "aria",
|
|
11368
|
+
"description": "Handler that is called when the element receives focus."
|
|
11369
|
+
},
|
|
11370
|
+
"onBlur": {
|
|
11371
|
+
"type": "((e: FocusEvent<HTMLInputElement, Element>) => void) | undefined",
|
|
11372
|
+
"required": false,
|
|
11373
|
+
"origin": "aria",
|
|
11374
|
+
"description": "Handler that is called when the element loses focus."
|
|
11375
|
+
},
|
|
11376
|
+
"onFocusChange": {
|
|
11377
|
+
"type": "((isFocused: boolean) => void) | undefined",
|
|
11378
|
+
"required": false,
|
|
11379
|
+
"origin": "aria",
|
|
11380
|
+
"description": "Handler that is called when the element's focus status changes."
|
|
11381
|
+
},
|
|
11382
|
+
"onKeyDown": {
|
|
11383
|
+
"type": "((e: KeyboardEvent) => void) | undefined",
|
|
11384
|
+
"required": false,
|
|
11385
|
+
"origin": "aria",
|
|
11386
|
+
"description": "Handler that is called when a key is pressed."
|
|
11387
|
+
},
|
|
11388
|
+
"onKeyUp": {
|
|
11389
|
+
"type": "((e: KeyboardEvent) => void) | undefined",
|
|
11390
|
+
"required": false,
|
|
11391
|
+
"origin": "aria",
|
|
11392
|
+
"description": "Handler that is called when a key is released."
|
|
11393
|
+
},
|
|
11394
|
+
"placeholder": {
|
|
11395
|
+
"type": "string | undefined",
|
|
11396
|
+
"required": false,
|
|
11397
|
+
"origin": "aria",
|
|
11398
|
+
"description": "Temporary text that occupies the text input when it is empty."
|
|
11399
|
+
},
|
|
11400
|
+
"value": {
|
|
11401
|
+
"type": "string | undefined",
|
|
11402
|
+
"required": false,
|
|
11403
|
+
"origin": "aria",
|
|
11404
|
+
"description": "The current value (controlled)."
|
|
11405
|
+
},
|
|
11406
|
+
"defaultValue": {
|
|
11407
|
+
"type": "string | undefined",
|
|
11408
|
+
"required": false,
|
|
11409
|
+
"origin": "aria",
|
|
11410
|
+
"description": "The default value (uncontrolled)."
|
|
11411
|
+
},
|
|
11412
|
+
"onChange": {
|
|
11413
|
+
"type": "((value: string) => void) | undefined",
|
|
11414
|
+
"required": false,
|
|
11415
|
+
"origin": "aria",
|
|
11416
|
+
"description": "Handler that is called when the value changes."
|
|
11417
|
+
},
|
|
11418
|
+
"label": {
|
|
11419
|
+
"type": "ReactNode",
|
|
11420
|
+
"required": false,
|
|
11421
|
+
"origin": "aria",
|
|
11422
|
+
"description": "The content to display as the label."
|
|
11423
|
+
},
|
|
11424
|
+
"id": {
|
|
11425
|
+
"type": "string | undefined",
|
|
11426
|
+
"required": false,
|
|
11427
|
+
"origin": "aria",
|
|
11428
|
+
"description": "The element's unique identifier. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id)."
|
|
11429
|
+
},
|
|
11430
|
+
"spellCheck": {
|
|
11431
|
+
"type": "string | undefined",
|
|
11432
|
+
"required": false,
|
|
11433
|
+
"origin": "aria",
|
|
11434
|
+
"description": "An enumerated attribute that defines whether the element may be checked for spelling errors.\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck)."
|
|
11435
|
+
},
|
|
11436
|
+
"autoCorrect": {
|
|
11437
|
+
"type": "string | undefined",
|
|
11438
|
+
"required": false,
|
|
11439
|
+
"origin": "aria",
|
|
11440
|
+
"description": "An attribute that takes as its value a space-separated string that describes what, if any, type\nof autocomplete functionality the input should provide. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autocomplete)."
|
|
11441
|
+
},
|
|
11442
|
+
"inputMode": {
|
|
11443
|
+
"type": "\"none\" | \"search\" | \"text\" | \"url\" | \"tel\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
|
|
11444
|
+
"required": false,
|
|
11445
|
+
"origin": "aria",
|
|
11446
|
+
"description": "Hints at the type of data that might be entered by the user while editing the element or its\ncontents. See\n[MDN](https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute).",
|
|
11447
|
+
"values": [
|
|
11448
|
+
"none",
|
|
11449
|
+
"search",
|
|
11450
|
+
"text",
|
|
11451
|
+
"url",
|
|
11452
|
+
"tel",
|
|
11453
|
+
"email",
|
|
11454
|
+
"numeric",
|
|
11455
|
+
"decimal"
|
|
11456
|
+
]
|
|
11457
|
+
},
|
|
11458
|
+
"aria-activedescendant": {
|
|
11459
|
+
"type": "string | undefined",
|
|
11460
|
+
"required": false,
|
|
11461
|
+
"origin": "aria",
|
|
11462
|
+
"description": "Identifies the currently active element when DOM focus is on a composite widget, textbox,\ngroup, or application."
|
|
11463
|
+
},
|
|
11464
|
+
"aria-autocomplete": {
|
|
11465
|
+
"type": "\"inline\" | \"list\" | \"none\" | \"both\" | undefined",
|
|
11466
|
+
"required": false,
|
|
11467
|
+
"origin": "aria",
|
|
11468
|
+
"description": "Indicates whether inputting text could trigger display of one or more predictions of the user's\nintended value for an input and specifies how predictions would be presented if they are made.",
|
|
11469
|
+
"values": [
|
|
11470
|
+
"inline",
|
|
11471
|
+
"list",
|
|
11472
|
+
"none",
|
|
11473
|
+
"both"
|
|
11474
|
+
]
|
|
11475
|
+
},
|
|
11476
|
+
"aria-controls": {
|
|
11477
|
+
"type": "string | undefined",
|
|
11478
|
+
"required": false,
|
|
11479
|
+
"origin": "aria",
|
|
11480
|
+
"description": "Identifies the element (or elements) whose contents or presence are controlled by the current\nelement."
|
|
11481
|
+
},
|
|
11482
|
+
"aria-describedby": {
|
|
11483
|
+
"type": "string | undefined",
|
|
11484
|
+
"required": false,
|
|
11485
|
+
"origin": "aria",
|
|
11486
|
+
"description": "Identifies the element (or elements) that describes the object."
|
|
11487
|
+
},
|
|
11488
|
+
"aria-details": {
|
|
11489
|
+
"type": "string | undefined",
|
|
11490
|
+
"required": false,
|
|
11491
|
+
"origin": "aria",
|
|
11492
|
+
"description": "Identifies the element (or elements) that provide a detailed, extended description for the\nobject."
|
|
11493
|
+
},
|
|
11494
|
+
"aria-errormessage": {
|
|
11495
|
+
"type": "string | undefined",
|
|
11496
|
+
"required": false,
|
|
11497
|
+
"origin": "aria",
|
|
11498
|
+
"description": "Identifies the element that provides an error message for the object."
|
|
11499
|
+
},
|
|
11500
|
+
"aria-haspopup": {
|
|
11501
|
+
"type": "boolean | \"true\" | \"false\" | \"menu\" | \"listbox\" | \"tree\" | \"grid\" | \"dialog\" | undefined",
|
|
11502
|
+
"required": false,
|
|
11503
|
+
"origin": "aria",
|
|
11504
|
+
"description": "Indicates the availability and type of interactive popup element, such as menu or dialog, that\ncan be triggered by an element."
|
|
11505
|
+
},
|
|
11506
|
+
"aria-label": {
|
|
11507
|
+
"type": "string | undefined",
|
|
11508
|
+
"required": false,
|
|
11509
|
+
"origin": "aria",
|
|
11510
|
+
"description": "Defines a string value that labels the current element."
|
|
11511
|
+
},
|
|
11512
|
+
"aria-labelledby": {
|
|
11513
|
+
"type": "string | undefined",
|
|
11514
|
+
"required": false,
|
|
11515
|
+
"origin": "aria",
|
|
11516
|
+
"description": "Identifies the element (or elements) that labels the current element."
|
|
11517
|
+
},
|
|
11518
|
+
"onCopy": {
|
|
11519
|
+
"type": "ClipboardEventHandler<HTMLInputElement> | undefined",
|
|
11520
|
+
"required": false,
|
|
11521
|
+
"origin": "aria",
|
|
11522
|
+
"description": "Handler that is called when the user copies text. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/oncopy)."
|
|
11523
|
+
},
|
|
11524
|
+
"onCut": {
|
|
11525
|
+
"type": "ClipboardEventHandler<HTMLInputElement> | undefined",
|
|
11526
|
+
"required": false,
|
|
11527
|
+
"origin": "aria",
|
|
11528
|
+
"description": "Handler that is called when the user cuts text. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/oncut)."
|
|
11529
|
+
},
|
|
11530
|
+
"onPaste": {
|
|
11531
|
+
"type": "ClipboardEventHandler<HTMLInputElement> | undefined",
|
|
11532
|
+
"required": false,
|
|
11533
|
+
"origin": "aria",
|
|
11534
|
+
"description": "Handler that is called when the user pastes text. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/onpaste)."
|
|
11535
|
+
},
|
|
11536
|
+
"onCompositionEnd": {
|
|
11537
|
+
"type": "CompositionEventHandler<HTMLInputElement> | undefined",
|
|
11538
|
+
"required": false,
|
|
11539
|
+
"origin": "aria",
|
|
11540
|
+
"description": "Handler that is called when a text composition system completes or cancels the current text\ncomposition session. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event)."
|
|
11541
|
+
},
|
|
11542
|
+
"onCompositionStart": {
|
|
11543
|
+
"type": "CompositionEventHandler<HTMLInputElement> | undefined",
|
|
11544
|
+
"required": false,
|
|
11545
|
+
"origin": "aria",
|
|
11546
|
+
"description": "Handler that is called when a text composition system starts a new text composition session.\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionstart_event)."
|
|
11547
|
+
},
|
|
11548
|
+
"onCompositionUpdate": {
|
|
11549
|
+
"type": "CompositionEventHandler<HTMLInputElement> | undefined",
|
|
11550
|
+
"required": false,
|
|
11551
|
+
"origin": "aria",
|
|
11552
|
+
"description": "Handler that is called when a new character is received in the current text composition\nsession. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionupdate_event)."
|
|
11553
|
+
},
|
|
11554
|
+
"onBeforeInput": {
|
|
11555
|
+
"type": "FormEventHandler<HTMLInputElement> | undefined",
|
|
11556
|
+
"required": false,
|
|
11557
|
+
"origin": "aria",
|
|
11558
|
+
"description": "Handler that is called when the input value is about to be modified. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event)."
|
|
11559
|
+
},
|
|
11560
|
+
"onInput": {
|
|
11561
|
+
"type": "FormEventHandler<HTMLInputElement> | undefined",
|
|
11562
|
+
"required": false,
|
|
11563
|
+
"origin": "aria",
|
|
11564
|
+
"description": "Handler that is called when the input value is modified. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)."
|
|
11565
|
+
},
|
|
11566
|
+
"onSelect": {
|
|
11567
|
+
"type": "ReactEventHandler<HTMLInputElement> | undefined",
|
|
11568
|
+
"required": false,
|
|
11569
|
+
"origin": "aria",
|
|
11570
|
+
"description": "Handler that is called when text in the input is selected. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/select_event)."
|
|
11571
|
+
},
|
|
11572
|
+
"form": {
|
|
11573
|
+
"type": "string | undefined",
|
|
11574
|
+
"required": false,
|
|
11575
|
+
"origin": "aria",
|
|
11576
|
+
"description": "The `<form>` element to associate the input with.\nThe value of this attribute must be the id of a `<form>` in the same document.\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#form)."
|
|
11577
|
+
},
|
|
11578
|
+
"name": {
|
|
11579
|
+
"type": "string | undefined",
|
|
11580
|
+
"required": false,
|
|
11581
|
+
"origin": "aria",
|
|
11582
|
+
"description": "The name of the input element, used when submitting an HTML form. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname)."
|
|
11583
|
+
},
|
|
11584
|
+
"autoComplete": {
|
|
11585
|
+
"type": "string | undefined",
|
|
11586
|
+
"required": false,
|
|
11587
|
+
"origin": "aria",
|
|
11588
|
+
"description": "Describes the type of autocomplete functionality the input should provide if any. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefautocomplete)."
|
|
11589
|
+
},
|
|
11590
|
+
"maxLength": {
|
|
11591
|
+
"type": "number | undefined",
|
|
11592
|
+
"required": false,
|
|
11593
|
+
"origin": "aria",
|
|
11594
|
+
"description": "The maximum number of characters supported by the input. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefmaxlength)."
|
|
11595
|
+
},
|
|
11596
|
+
"minLength": {
|
|
11597
|
+
"type": "number | undefined",
|
|
11598
|
+
"required": false,
|
|
11599
|
+
"origin": "aria",
|
|
11600
|
+
"description": "The minimum number of characters required by the input. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefminlength)."
|
|
11601
|
+
},
|
|
11602
|
+
"pattern": {
|
|
11603
|
+
"type": "string | undefined",
|
|
11604
|
+
"required": false,
|
|
11605
|
+
"origin": "aria",
|
|
11606
|
+
"description": "Regex pattern that the value of the input must match to be valid. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefpattern)."
|
|
11607
|
+
},
|
|
11608
|
+
"excludeFromTabOrder": {
|
|
11609
|
+
"type": "boolean | undefined",
|
|
11610
|
+
"required": false,
|
|
11611
|
+
"origin": "aria",
|
|
11612
|
+
"description": "Whether to exclude the element from the sequential tab order. If true,\nthe element will not be focusable via the keyboard by tabbing. This should\nbe avoided except in rare scenarios where an alternative means of accessing\nthe element or its functionality via the keyboard is available."
|
|
11613
|
+
}
|
|
11614
|
+
},
|
|
11615
|
+
"propCounts": {
|
|
11616
|
+
"own": 3,
|
|
11617
|
+
"aria": 51,
|
|
11618
|
+
"dom": 264,
|
|
11619
|
+
"other": 2
|
|
11620
|
+
}
|
|
11621
|
+
},
|
|
9922
11622
|
"SegmentedControl": {
|
|
9923
11623
|
"name": "SegmentedControl",
|
|
9924
11624
|
"source": "src/components/SegmentedControl.tsx",
|
|
@@ -11609,8 +13309,8 @@
|
|
|
11609
13309
|
"origin": "own",
|
|
11610
13310
|
"description": "`horizontal` for a page-width header above the form, `vertical` for a side\nrail. Below 40rem a horizontal stepper hides every label but the current\none, so it does not need a second variant for mobile.",
|
|
11611
13311
|
"values": [
|
|
11612
|
-
"
|
|
11613
|
-
"
|
|
13312
|
+
"vertical",
|
|
13313
|
+
"horizontal"
|
|
11614
13314
|
]
|
|
11615
13315
|
},
|
|
11616
13316
|
"children": {
|
|
@@ -12737,13 +14437,13 @@
|
|
|
12737
14437
|
"default": "md"
|
|
12738
14438
|
},
|
|
12739
14439
|
"orientation": {
|
|
12740
|
-
"type": "\"
|
|
14440
|
+
"type": "\"vertical\" | \"horizontal\" | undefined",
|
|
12741
14441
|
"required": false,
|
|
12742
14442
|
"origin": "own",
|
|
12743
14443
|
"description": "Drives both the arrow-key axis and the track layout.\n\n`vertical` is keyboard- and ARIA-complete — up/down move between tabs, and\nthe track stacks — but its *decoration* is not: the underline rule and the\npill track's padding are still written for the horizontal axis only. Those\nare Figma's to specify, not this file's to invent. See tabs.css.",
|
|
12744
14444
|
"values": [
|
|
12745
|
-
"
|
|
12746
|
-
"
|
|
14445
|
+
"vertical",
|
|
14446
|
+
"horizontal"
|
|
12747
14447
|
],
|
|
12748
14448
|
"tags": {
|
|
12749
14449
|
"default": "'horizontal'"
|