@baseline-ui/mcp 1.2.0 → 2.1.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/CHANGELOG.md +8 -0
- package/README.md +21 -4
- package/dist/chunk-GTOFKGAS.js +36107 -0
- package/dist/index.js +1 -34392
- package/dist/{index.cjs → server.cjs} +2076 -362
- package/dist/server.d.cts +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +1 -0
- package/package.json +15 -2
- package/sbom.json +65 -65
- package/dist/index.d.cts +0 -1
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
'use strict';var mcp_js=require('@modelcontextprotocol/sdk/server/mcp.js'),stdio_js=require('@modelcontextprotocol/sdk/server/stdio.js'),zod=require('zod');/**
|
|
3
|
-
* Copyright (c) 2023-2024 PSPDFKit GmbH. All rights reserved.
|
|
4
|
-
*
|
|
5
|
-
* THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
|
|
6
|
-
* AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
|
|
7
|
-
* UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
|
|
8
|
-
* This notice may not be removed from this file.
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
var o={BarChart:{id:"charts-barchart",breadcrumb:"Charts/BarChart",importStatement:`import { BarChart } from "@baseline-ui/charts";
|
|
1
|
+
'use strict';var mcp_js=require('@modelcontextprotocol/sdk/server/mcp.js'),zod=require('zod');var t={BarChart:{id:"charts-barchart",breadcrumb:"Charts/BarChart",importStatement:`import { BarChart } from "@baseline-ui/charts";
|
|
12
2
|
import { Box } from "@baseline-ui/core";`,description:"The `BarChart` component renders a responsive bar chart for categorical data. It is built on top of [Recharts](https://recharts.org/) and styled with Baseline UI design tokens so it picks up the active theme automatically.",documentation:`The \`BarChart\` component renders a responsive bar chart for categorical data. It is built on top of [Recharts](https://recharts.org/) and styled with Baseline UI design tokens so it picks up the active theme automatically.
|
|
13
3
|
|
|
14
4
|
* Renders one or more bar series from a single dataset, keyed by \`xAxisDataKey\` and the per-bar \`dataKey\`.
|
|
@@ -992,7 +982,7 @@ export const AccordionWithDisabledItemsExample: React.FC<
|
|
|
992
982
|
Omit<React.ComponentProps<typeof AccordionExample>, "disabledKeys">
|
|
993
983
|
> = (props) => {
|
|
994
984
|
return <AccordionExample {...props} disabledKeys={new Set(["item-1"])} />;
|
|
995
|
-
};`},similarTo:[],figmaUrl:null},ActionButton:{id:"core-buttons-actionbutton",breadcrumb:"Core/Buttons/ActionButton",importStatement:'import { ActionButton, Menu, VariantViewer } from "@baseline-ui/core";',description:"`ActionButton` is a labeled button that triggers an action when activated by mouse, touch, or keyboard. Use it for primary user actions such as submitting a form, confirming a choice, or invoking a command.",documentation:'`ActionButton` is a labeled button that triggers an action when activated by mouse, touch, or keyboard. Use it for primary user actions such as submitting a form, confirming a choice, or invoking a command.\n\n* Mouse and touch event handling, and press state management\n* Keyboard focus management and cross browser normalization\n* Keyboard event support for Space and Enter keys\n\nYou can import the ActionButton component like so:\n\n```jsx\nimport { ActionButton } from "@baseline-ui/core";\n\nexport default function App() {\n return <ActionButton label="Click Me" />;\n}\n```\n\nIf you want to use a button that can be toggled on and off, you can use the `ToggleButton` component.\n\
|
|
985
|
+
};`},similarTo:[],figmaUrl:null},ActionButton:{id:"core-buttons-actionbutton",breadcrumb:"Core/Buttons/ActionButton",importStatement:'import { ActionButton, Menu, VariantViewer } from "@baseline-ui/core";',description:"`ActionButton` is a labeled button that triggers an action when activated by mouse, touch, or keyboard. Use it for primary user actions such as submitting a form, confirming a choice, or invoking a command.",documentation:'`ActionButton` is a labeled button that triggers an action when activated by mouse, touch, or keyboard. Use it for primary user actions such as submitting a form, confirming a choice, or invoking a command.\n\n* Mouse and touch event handling, and press state management\n* Keyboard focus management and cross browser normalization\n* Keyboard event support for Space and Enter keys\n\nYou can import the ActionButton component like so:\n\n```jsx\nimport { ActionButton } from "@baseline-ui/core";\n\nexport default function App() {\n return <ActionButton label="Click Me" />;\n}\n```\n\nIf you want to use a button that can be toggled on and off, you can use the `ToggleButton` component.\n\nThe button has six neutral variants: `primary`, `secondary`, `tertiary`, `popover`, `toolbar` and `ghost`. The default variant is `primary`. You can change the variant by passing the `variant` prop.\n\n```jsx\n<ActionButton label="Primary" />\n<ActionButton label="Secondary" variant="secondary" />\n<ActionButton label="Tertiary" variant="tertiary" />\n<ActionButton label={"Toolbar"} variant={"toolbar"} />\n<ActionButton label="Ghost" variant="ghost" />\n<ActionButton label="Popover" variant="popover" />\n```\n\n`success`, `warning` and `error` carry a support colour for actions whose outcome matters \u2014 confirming, acknowledging a risk, or deleting. Each comes in the same three emphasis levels as the neutral variants, so a support-coloured button can sit at whatever weight the surrounding layout calls for:\n\n* `{name}` \u2014 filled, for the primary action in its context\n* `{name}-secondary` \u2014 tinted fill, for a lower-emphasis action\n* `{name}-tertiary` \u2014 outline that fills with the support colour on hover and on keyboard focus\n\nWhen disabled, all three levels drop the support colour and render in the neutral disabled treatment.\n\n```jsx\n<ActionButton label="Success" variant="success" />\n<ActionButton label="Success" variant="success-secondary" />\n<ActionButton label="Success" variant="success-tertiary" />\n<ActionButton label="Warning" variant="warning" />\n<ActionButton label="Warning" variant="warning-secondary" />\n<ActionButton label="Warning" variant="warning-tertiary" />\n<ActionButton label="Error" variant="error" />\n<ActionButton label="Error" variant="error-secondary" />\n<ActionButton label="Error" variant="error-tertiary" />\n```\n\nActionButton supports three sizes: `sm` (default), `md`, and `lg`. You can change the size by passing the `size` prop.\n\n```jsx\n<ActionButton label="Small (default)" size="sm" />\n<ActionButton label="Medium" size="md" />\n<ActionButton label="Large" size="lg" />\n```\n\nYou can disable a button by passing the `isDisabled` prop. This will disable all mouse, touch, and keyboard interactions. The `-secondary` and `-tertiary` support variants are omitted below because they render identically to disabled `secondary` and `tertiary`.\n\n```jsx\n<ActionButton label="Primary" isDisabled />\n<ActionButton label="Secondary" variant="secondary" isDisabled />\n<ActionButton label="Tertiary" variant="tertiary" isDisabled />\n<ActionButton label="Ghost" variant="ghost" isDisabled />\n<ActionButton label="Toolbar" variant="toolbar" isDisabled />\n<ActionButton label="Popover" variant="popover" isDisabled />\n<ActionButton label="Success" variant="success" isDisabled />\n<ActionButton label="Warning" variant="warning" isDisabled />\n<ActionButton label="Error" variant="error" isDisabled />\n```\n\nYou can place an icon before or after the label by passing the `iconStart` or `iconEnd` props. Choose the icon entrypoint that matches your button size: `@baseline-ui/icons/16` for `sm`, `@baseline-ui/icons/20` for `md`, and `@baseline-ui/icons/24` for `lg`.\n\n```jsx\nimport { EllipseIcon } from "@baseline-ui/icons/16";\n\n<ActionButton label="Label" iconStart={EllipseIcon} />\n<ActionButton label="Label" iconEnd={EllipseIcon} />\n```\n\nYou can listen for events by passing the `onPress` prop. The `onPress` prop will fire when the button is activated by mouse, touch, or keyboard interactions.\n\n```jsx\n<ActionButton label="Click Me" onPress={() => alert("Hello World")} />\n```\n\n| Selector | Description |\n| -------------------- | -------------------------------------------------------------- |\n| \\[data-disabled] | Whether the button is disabled. |\n| \\[data-focused] | Whether the button is focused, either via a mouse or keyboard. |\n| \\[data-hovered] | Whether the button is currently hovered with a mouse. |\n| \\[data-focus-visible] | Whether the button is keyboard focused. |\n| \\[data-pressed] | Whether the button is currently pressed. |\n\n| Key | Function |\n| ------- | --------------------- |\n| `Space` | Activates the button. |\n| `Enter` | Activates the button. |',props:`interface ActionButtonProps {
|
|
996
986
|
/**
|
|
997
987
|
* The button's class name.
|
|
998
988
|
*/
|
|
@@ -1010,6 +1000,13 @@ slot?: string | null
|
|
|
1010
1000
|
/**
|
|
1011
1001
|
* The button's variant.
|
|
1012
1002
|
*
|
|
1003
|
+
* The \`success\`, \`warning\` and \`error\` variants carry a support colour. Each
|
|
1004
|
+
* comes in three emphasis levels, mirroring \`primary\`/\`secondary\`/\`tertiary\`:
|
|
1005
|
+
* the bare name is the filled treatment, \`-secondary\` is a tinted fill and
|
|
1006
|
+
* \`-tertiary\` is an outline that fills with the support colour on hover and
|
|
1007
|
+
* on keyboard focus. When disabled, all three drop the support colour and
|
|
1008
|
+
* render in the neutral disabled treatment.
|
|
1009
|
+
*
|
|
1013
1010
|
* @default primary
|
|
1014
1011
|
*/
|
|
1015
1012
|
variant?: | "primary"
|
|
@@ -1019,8 +1016,14 @@ variant?: | "primary"
|
|
|
1019
1016
|
| "popover"
|
|
1020
1017
|
| "toolbar"
|
|
1021
1018
|
| "success"
|
|
1019
|
+
| "success-secondary"
|
|
1020
|
+
| "success-tertiary"
|
|
1022
1021
|
| "warning"
|
|
1022
|
+
| "warning-secondary"
|
|
1023
|
+
| "warning-tertiary"
|
|
1023
1024
|
| "error"
|
|
1025
|
+
| "error-secondary"
|
|
1026
|
+
| "error-tertiary"
|
|
1024
1027
|
/**
|
|
1025
1028
|
* The button's label.
|
|
1026
1029
|
*/
|
|
@@ -1207,6 +1210,114 @@ size?: "sm" | "md" | "lg"
|
|
|
1207
1210
|
{ variant: "error", size: "lg", iconStart: E24 },
|
|
1208
1211
|
{ variant: "error", size: "lg", iconEnd: E24 },
|
|
1209
1212
|
],
|
|
1213
|
+
"Success Secondary": [
|
|
1214
|
+
{ variant: "success-secondary" },
|
|
1215
|
+
{ variant: "success-secondary", isDisabled: true },
|
|
1216
|
+
{ variant: "success-secondary", iconStart: EllipseIcon },
|
|
1217
|
+
{ variant: "success-secondary", iconEnd: EllipseIcon },
|
|
1218
|
+
],
|
|
1219
|
+
"Success Secondary Medium": [
|
|
1220
|
+
{ variant: "success-secondary", size: "md" },
|
|
1221
|
+
{ variant: "success-secondary", size: "md", isDisabled: true },
|
|
1222
|
+
{ variant: "success-secondary", size: "md", iconStart: E20 },
|
|
1223
|
+
{ variant: "success-secondary", size: "md", iconEnd: E20 },
|
|
1224
|
+
],
|
|
1225
|
+
"Success Secondary Large": [
|
|
1226
|
+
{ variant: "success-secondary", size: "lg" },
|
|
1227
|
+
{ variant: "success-secondary", size: "lg", isDisabled: true },
|
|
1228
|
+
{ variant: "success-secondary", size: "lg", iconStart: E24 },
|
|
1229
|
+
{ variant: "success-secondary", size: "lg", iconEnd: E24 },
|
|
1230
|
+
],
|
|
1231
|
+
"Success Tertiary": [
|
|
1232
|
+
{ variant: "success-tertiary" },
|
|
1233
|
+
{ variant: "success-tertiary", isDisabled: true },
|
|
1234
|
+
{ variant: "success-tertiary", iconStart: EllipseIcon },
|
|
1235
|
+
{ variant: "success-tertiary", iconEnd: EllipseIcon },
|
|
1236
|
+
],
|
|
1237
|
+
"Success Tertiary Medium": [
|
|
1238
|
+
{ variant: "success-tertiary", size: "md" },
|
|
1239
|
+
{ variant: "success-tertiary", size: "md", isDisabled: true },
|
|
1240
|
+
{ variant: "success-tertiary", size: "md", iconStart: E20 },
|
|
1241
|
+
{ variant: "success-tertiary", size: "md", iconEnd: E20 },
|
|
1242
|
+
],
|
|
1243
|
+
"Success Tertiary Large": [
|
|
1244
|
+
{ variant: "success-tertiary", size: "lg" },
|
|
1245
|
+
{ variant: "success-tertiary", size: "lg", isDisabled: true },
|
|
1246
|
+
{ variant: "success-tertiary", size: "lg", iconStart: E24 },
|
|
1247
|
+
{ variant: "success-tertiary", size: "lg", iconEnd: E24 },
|
|
1248
|
+
],
|
|
1249
|
+
"Warning Secondary": [
|
|
1250
|
+
{ variant: "warning-secondary" },
|
|
1251
|
+
{ variant: "warning-secondary", isDisabled: true },
|
|
1252
|
+
{ variant: "warning-secondary", iconStart: EllipseIcon },
|
|
1253
|
+
{ variant: "warning-secondary", iconEnd: EllipseIcon },
|
|
1254
|
+
],
|
|
1255
|
+
"Warning Secondary Medium": [
|
|
1256
|
+
{ variant: "warning-secondary", size: "md" },
|
|
1257
|
+
{ variant: "warning-secondary", size: "md", isDisabled: true },
|
|
1258
|
+
{ variant: "warning-secondary", size: "md", iconStart: E20 },
|
|
1259
|
+
{ variant: "warning-secondary", size: "md", iconEnd: E20 },
|
|
1260
|
+
],
|
|
1261
|
+
"Warning Secondary Large": [
|
|
1262
|
+
{ variant: "warning-secondary", size: "lg" },
|
|
1263
|
+
{ variant: "warning-secondary", size: "lg", isDisabled: true },
|
|
1264
|
+
{ variant: "warning-secondary", size: "lg", iconStart: E24 },
|
|
1265
|
+
{ variant: "warning-secondary", size: "lg", iconEnd: E24 },
|
|
1266
|
+
],
|
|
1267
|
+
"Warning Tertiary": [
|
|
1268
|
+
{ variant: "warning-tertiary" },
|
|
1269
|
+
{ variant: "warning-tertiary", isDisabled: true },
|
|
1270
|
+
{ variant: "warning-tertiary", iconStart: EllipseIcon },
|
|
1271
|
+
{ variant: "warning-tertiary", iconEnd: EllipseIcon },
|
|
1272
|
+
],
|
|
1273
|
+
"Warning Tertiary Medium": [
|
|
1274
|
+
{ variant: "warning-tertiary", size: "md" },
|
|
1275
|
+
{ variant: "warning-tertiary", size: "md", isDisabled: true },
|
|
1276
|
+
{ variant: "warning-tertiary", size: "md", iconStart: E20 },
|
|
1277
|
+
{ variant: "warning-tertiary", size: "md", iconEnd: E20 },
|
|
1278
|
+
],
|
|
1279
|
+
"Warning Tertiary Large": [
|
|
1280
|
+
{ variant: "warning-tertiary", size: "lg" },
|
|
1281
|
+
{ variant: "warning-tertiary", size: "lg", isDisabled: true },
|
|
1282
|
+
{ variant: "warning-tertiary", size: "lg", iconStart: E24 },
|
|
1283
|
+
{ variant: "warning-tertiary", size: "lg", iconEnd: E24 },
|
|
1284
|
+
],
|
|
1285
|
+
"Error Secondary": [
|
|
1286
|
+
{ variant: "error-secondary" },
|
|
1287
|
+
{ variant: "error-secondary", isDisabled: true },
|
|
1288
|
+
{ variant: "error-secondary", iconStart: EllipseIcon },
|
|
1289
|
+
{ variant: "error-secondary", iconEnd: EllipseIcon },
|
|
1290
|
+
],
|
|
1291
|
+
"Error Secondary Medium": [
|
|
1292
|
+
{ variant: "error-secondary", size: "md" },
|
|
1293
|
+
{ variant: "error-secondary", size: "md", isDisabled: true },
|
|
1294
|
+
{ variant: "error-secondary", size: "md", iconStart: E20 },
|
|
1295
|
+
{ variant: "error-secondary", size: "md", iconEnd: E20 },
|
|
1296
|
+
],
|
|
1297
|
+
"Error Secondary Large": [
|
|
1298
|
+
{ variant: "error-secondary", size: "lg" },
|
|
1299
|
+
{ variant: "error-secondary", size: "lg", isDisabled: true },
|
|
1300
|
+
{ variant: "error-secondary", size: "lg", iconStart: E24 },
|
|
1301
|
+
{ variant: "error-secondary", size: "lg", iconEnd: E24 },
|
|
1302
|
+
],
|
|
1303
|
+
"Error Tertiary": [
|
|
1304
|
+
{ variant: "error-tertiary" },
|
|
1305
|
+
{ variant: "error-tertiary", isDisabled: true },
|
|
1306
|
+
{ variant: "error-tertiary", iconStart: EllipseIcon },
|
|
1307
|
+
{ variant: "error-tertiary", iconEnd: EllipseIcon },
|
|
1308
|
+
],
|
|
1309
|
+
"Error Tertiary Medium": [
|
|
1310
|
+
{ variant: "error-tertiary", size: "md" },
|
|
1311
|
+
{ variant: "error-tertiary", size: "md", isDisabled: true },
|
|
1312
|
+
{ variant: "error-tertiary", size: "md", iconStart: E20 },
|
|
1313
|
+
{ variant: "error-tertiary", size: "md", iconEnd: E20 },
|
|
1314
|
+
],
|
|
1315
|
+
"Error Tertiary Large": [
|
|
1316
|
+
{ variant: "error-tertiary", size: "lg" },
|
|
1317
|
+
{ variant: "error-tertiary", size: "lg", isDisabled: true },
|
|
1318
|
+
{ variant: "error-tertiary", size: "lg", iconStart: E24 },
|
|
1319
|
+
{ variant: "error-tertiary", size: "lg", iconEnd: E24 },
|
|
1320
|
+
],
|
|
1210
1321
|
}}
|
|
1211
1322
|
/>
|
|
1212
1323
|
);`},{id:"core-buttons-actionbutton--disabled",name:"Disabled",snippet:'const Disabled = () => <ActionButton onPress={fn()} label="Label" isDisabled />;'},{id:"core-buttons-actionbutton--with-icon",name:"With Icon",snippet:'const WithIcon = () => <ActionButton onPress={fn()} label="Label" iconStart={EllipseIcon} />;'},{id:"core-buttons-actionbutton--with-both-icons",name:"With Both Icons",snippet:`const WithBothIcons = () => <ActionButton
|
|
@@ -2219,7 +2330,7 @@ isPrimaryActionDisabled?: boolean
|
|
|
2219
2330
|
* The props that are passed to the text input. If this is provided, a text
|
|
2220
2331
|
* input will be displayed at the bottom of the dialog.
|
|
2221
2332
|
*/
|
|
2222
|
-
textInputProps?:
|
|
2333
|
+
textInputProps?: SingleLineTextInputProps | MultiLineTextInputProps
|
|
2223
2334
|
/**
|
|
2224
2335
|
* The icon that is displayed at the top of the dialog. This is typically used
|
|
2225
2336
|
* to display an icon that represents the type of alert that is being shown.
|
|
@@ -3827,7 +3938,7 @@ children: React.ReactNode
|
|
|
3827
3938
|
"test": "cross-env BABEL_ENV=test jest",
|
|
3828
3939
|
"test:e2e": "cross-env BABEL_ENV=test jest --testPathPattern=e2e --testPathIgnorePatterns='examples,/packages/components/,/packages/react/'"
|
|
3829
3940
|
}
|
|
3830
|
-
}\`}</Code>;`}],implementation:""},similarTo:[],figmaUrl:null},ColorInput:{id:"core-forms-colorinput",breadcrumb:"Core/Forms/ColorInput",importStatement:'import { ColorInput, CustomTriggerButton, IndeterminateExample } from "@baseline-ui/core";',description:"`ColorInput` is a form control that opens a popover with preset swatches and a custom picker so users can choose or define a color. Use it when a user needs to pick a fill, stroke, highlight, or any color value, with optional alpha and persisted custom colors.",documentation:'`ColorInput` is a form control that opens a popover with preset swatches and a custom picker so users can choose or define a color. Use it when a user needs to pick a fill, stroke, highlight, or any color value, with optional alpha and persisted custom colors.\n\n* Includes custom color picker with color area, hue slider, and optional alpha slider\n* Supports alpha channel\n* Exposed to screen readers using ARIA attributes\n* Supports keyboard, touch and mouse interaction\n* Supports disabled and indeterminate states\n* Supports HEX and RGB color modes\n* Persists custom colors in local storage\n* Supports lazy picker mode for adding custom colors without live updates\n\n```jsx\nimport { ColorInput } from "@baseline-ui/core";\n\nconst presets = [\n { label: "Red", color: "#ff0000" },\n { label: "Green", color: "#00ff00" },\n { label: "Blue", color: "#0000ff" },\n { label: "Yellow", color: "#ffff00" },\n { label: "Cyan", color: "#00ffff" },\n { label: "Magenta", color: "#ff00ff" },\n { label: "Black", color: "#000000" },\n { label: "White", color: "#ffffff" },\n { label: "Gray", color: "#808080" },\n { label: "Orange", color: "#ffa500" },\n { label: "Brown", color: "#a52a2a" },\n { label: "Purple", color: "#800080" },\n];\n\n<ColorInput presets={presets} label="Color" />;\n```\n\nBy default, the label is placed above the trigger. Use `labelPosition="start"` to place it inline.\n\n```jsx\n<ColorInput\n presets={presets}\n label="Label"\n labelPosition="start"\n defaultValue="#ff0000"\n/>\n```\n\nHide the visible color name text next to the swatch by setting `colorLabel={false}`.\n\n```jsx\n<ColorInput presets={presets} colorLabel={false} aria-label="Color" />\n```\n\nShow only the preset list without the custom color picker by setting `includePicker={false}`.\n\n```jsx\n<ColorInput presets={presets} includePicker={false} label="Color" />\n```\n\nShow only the custom color picker without any presets.\n\n```jsx\n<ColorInput presets={[]} label="Color" />\n```\n\nYou can enable the alpha channel in the color picker by setting the `allowAlpha` prop to `true`.\n\n```jsx\n<ColorInput presets={presets} allowAlpha />\n```\n\nDisable the alpha slider by setting `allowAlpha={false}`.\n\n```jsx\n<ColorInput presets={presets} allowAlpha={false} label="Color" />\n```\n\n```jsx\n<ColorInput presets={presets} isDisabled label="Color" />\n```\n\nThe `ColorInput` component supports an indeterminate state. This is useful when you want to show a loading state or an unknown state. This property is always controlled and only makes a visual difference. If set to true, the color input trigger button will show "Indeterminate" as the color name. Apart from this, all the other functionality will work as expected.\n\n```jsx\n<ColorInput isIndeterminate presets={presets} />\n```\n\nBy default, you can add colors picked from the picker to the list of custom color presets. These are persisted in local storage under the key specified by the `storePickedColorKey` prop (defaults to `"baselinePickedColor"`). To use a separate storage key per instance, pass a unique value:\n\n```jsx\n<ColorInput storePickedColorKey="my-custom-key" />\n```\n\nBy default, you cannot unset the color. You can enable the ability to unset the color by setting the `allowRemoval` prop to `true`.\n\n```jsx\n<ColorInput presets={presets} allowRemoval />\n```\n\nYou can set the default color by setting the `defaultValue` prop to a color value.\n\n```jsx\n<ColorInput presets={presets} defaultValue="#ff0000" />\n```\n\nYou can make the `ColorInput` component controlled by setting the `value` prop to a color value. You can use the `onChange` prop to update the value.\n\n```jsx\n<ColorInput presets={presets} value="#ff0000" onChange={console.log} />\n```\n\nYou can use the `renderTriggerButton` prop to render a custom trigger.\n\n```jsx\n<ColorInput\n label="label"\n renderTriggerButton={({ colorName, ref, triggerProps }) => (\n <ActionIconButton\n {...triggerProps}\n aria-label={typeof colorName === "string" ? colorName : "Color"}\n icon={EllipseIcon}\n ref={ref}\n aria-haspopup="true"\n />\n )}\n/>\n```\n\nYou can use the `pickerMode="lazy"` prop to render the color picker only to add a custom color to the list of custom color presets. This is useful when you want to prevent the `onChange` event from firing while the user is picking a color from the picker. In case of mobile the picker opens in a modal.\n\n```jsx\n<ColorInput presets={presets} pickerMode="lazy" aria-label="Color" />\n```\n\nThe following CSS class selectors and data attributes are available for styling:\n\n| Selector | Description |\n| -------------------------------------------- | ------------------------------------------------------------------------- |\n| `.BaselineUI-ColorInput-Trigger` | The outer wrapper containing the label and trigger button |\n| `.BaselineUI-ColorInputButton` | The trigger button |\n| `.BaselineUI-ColorInput-Popover` | The popover container |\n| `.BaselineUI-ColorInput-ColorArea` | The color area (saturation/lightness) |\n| `.BaselineUI-ColorInput-ColorAreaThumb` | The draggable thumb on the color area |\n| `.BaselineUI-ColorInput-ColorSlider` | The hue/alpha slider |\n| `.BaselineUI-ColorInput-ColorSliderThumb` | The draggable thumb on a slider |\n| `.BaselineUI-ColorInput-FieldInput` | The hex/RGB text input |\n| `.BaselineUI-ColorInput-Presets` | The preset color list |\n| `.BaselineUI-ColorInput-CustomColors` | The custom colors header |\n| `.BaselineUI-ColorInput-CustomColorsListBox` | The custom colors list |\n| `[data-disabled]` | Applied when the button is disabled |\n| `[data-hovered]` | Applied when the button is hovered |\n| `[data-pressed]` | Applied when the button is pressed |\n| `[data-focus-visible]` | Applied when the button has keyboard focus |\n| `[data-color-mode="hexa"]` | Applied to hex field input when alpha is enabled |\n| `[data-color-mode="hex"]` | Applied to hex field input when alpha is disabled |\n| `[data-color-mode="rgba"]` | Applied to RGB field inputs when alpha is enabled |\n| `[data-color-mode="rgb"]` | Applied to RGB field inputs when alpha is disabled |\n| `[data-channel]` | Applied to color sliders, value is the channel name (e.g. `hue`, `alpha`) |\n\n| Key | Description |\n| ---------- | -------------------------------------------------------------------- |\n| Enter | Opens the popover or selects the focused color if popover is open |\n| Space | Opens the popover or selects the focused color if popover is open |\n| Escape | Closes the popover if open |\n| ArrowRight | Moves focus to the next preset color in the list |\n| ArrowLeft | Moves focus to the previous preset color in the list |\n| Tab | Moves focus between the color area, sliders, fields, and preset list |\n\nThe following strings are used by the `ColorInput` component and can be overridden via `I18nProvider`:\n\n| Key | Default (en) |\n| ------------------------------ | ---------------- |\n| `bui.colorInput.addColor` | Add Color |\n| `bui.colorInput.removeColor` | Remove Color |\n| `bui.colorInput.customColors` | Custom Colors |\n| `bui.colorInput.noColor` | None |\n| `bui.colorInput.transparent` | Transparent |\n| `bui.colorInput.add` | Add |\n| `bui.colorInput.cancel` | Cancel |\n| `bui.colorInput.colorFormat` | Color Format |\n| `bui.colorInput.colorPresets` | Color Presets |\n| `bui.colorInput.newColor` | New Custom Color |\n| `bui.colorInput.indeterminate` | Indeterminate |\n\n
|
|
3941
|
+
}\`}</Code>;`}],implementation:""},similarTo:[],figmaUrl:null},ColorInput:{id:"core-forms-colorinput",breadcrumb:"Core/Forms/ColorInput",importStatement:'import { ColorInput, CustomTriggerButton, IndeterminateExample } from "@baseline-ui/core";',description:"`ColorInput` is a form control that opens a popover with preset swatches and a custom picker so users can choose or define a color. Use it when a user needs to pick a fill, stroke, highlight, or any color value, with optional alpha and persisted custom colors.",documentation:'`ColorInput` is a form control that opens a popover with preset swatches and a custom picker so users can choose or define a color. Use it when a user needs to pick a fill, stroke, highlight, or any color value, with optional alpha and persisted custom colors.\n\n* Includes custom color picker with color area, hue slider, and optional alpha slider\n* Supports alpha channel\n* Exposed to screen readers using ARIA attributes\n* Supports keyboard, touch and mouse interaction\n* Supports disabled and indeterminate states\n* Supports HEX and RGB color modes\n* Persists custom colors in local storage\n* Supports lazy picker mode for adding custom colors without live updates\n\n```jsx\nimport { ColorInput } from "@baseline-ui/core";\n\nconst presets = [\n { label: "Red", color: "#ff0000" },\n { label: "Green", color: "#00ff00" },\n { label: "Blue", color: "#0000ff" },\n { label: "Yellow", color: "#ffff00" },\n { label: "Cyan", color: "#00ffff" },\n { label: "Magenta", color: "#ff00ff" },\n { label: "Black", color: "#000000" },\n { label: "White", color: "#ffffff" },\n { label: "Gray", color: "#808080" },\n { label: "Orange", color: "#ffa500" },\n { label: "Brown", color: "#a52a2a" },\n { label: "Purple", color: "#800080" },\n];\n\n<ColorInput presets={presets} label="Color" />;\n```\n\nBy default, the label is placed above the trigger. Use `labelPosition="start"` to place it inline.\n\n```jsx\n<ColorInput\n presets={presets}\n label="Label"\n labelPosition="start"\n defaultValue="#ff0000"\n/>\n```\n\nHide the visible color name text next to the swatch by setting `colorLabel={false}`.\n\n```jsx\n<ColorInput presets={presets} colorLabel={false} aria-label="Color" />\n```\n\nShow only the preset list without the custom color picker by setting `includePicker={false}`.\n\n```jsx\n<ColorInput presets={presets} includePicker={false} label="Color" />\n```\n\nShow only the custom color picker without any presets.\n\n```jsx\n<ColorInput presets={[]} label="Color" />\n```\n\nYou can enable the alpha channel in the color picker by setting the `allowAlpha` prop to `true`.\n\n```jsx\n<ColorInput presets={presets} allowAlpha />\n```\n\nDisable the alpha slider by setting `allowAlpha={false}`.\n\n```jsx\n<ColorInput presets={presets} allowAlpha={false} label="Color" />\n```\n\n```jsx\n<ColorInput presets={presets} isDisabled label="Color" />\n```\n\nThe `ColorInput` component supports an indeterminate state. This is useful when you want to show a loading state or an unknown state. This property is always controlled and only makes a visual difference. If set to true, the color input trigger button will show "Indeterminate" as the color name. Apart from this, all the other functionality will work as expected.\n\n```jsx\n<ColorInput isIndeterminate presets={presets} />\n```\n\nBy default, you can add colors picked from the picker to the list of custom color presets. These are persisted in local storage under the key specified by the `storePickedColorKey` prop (defaults to `"baselinePickedColor"`). To use a separate storage key per instance, pass a unique value:\n\n```jsx\n<ColorInput storePickedColorKey="my-custom-key" />\n```\n\nBy default, you cannot unset the color. You can enable the ability to unset the color by setting the `allowRemoval` prop to `true`.\n\n```jsx\n<ColorInput presets={presets} allowRemoval />\n```\n\nYou can set the default color by setting the `defaultValue` prop to a color value.\n\n```jsx\n<ColorInput presets={presets} defaultValue="#ff0000" />\n```\n\nYou can make the `ColorInput` component controlled by setting the `value` prop to a color value. You can use the `onChange` prop to update the value.\n\n```jsx\n<ColorInput presets={presets} value="#ff0000" onChange={console.log} />\n```\n\nYou can use the `renderTriggerButton` prop to render a custom trigger.\n\n```jsx\n<ColorInput\n label="label"\n renderTriggerButton={({ colorName, ref, triggerProps }) => (\n <ActionIconButton\n {...triggerProps}\n aria-label={typeof colorName === "string" ? colorName : "Color"}\n icon={EllipseIcon}\n ref={ref}\n aria-haspopup="true"\n />\n )}\n/>\n```\n\nYou can use the `pickerMode="lazy"` prop to render the color picker only to add a custom color to the list of custom color presets. This is useful when you want to prevent the `onChange` event from firing while the user is picking a color from the picker. In case of mobile the picker opens in a modal.\n\n```jsx\n<ColorInput presets={presets} pickerMode="lazy" aria-label="Color" />\n```\n\nThe following CSS class selectors and data attributes are available for styling:\n\n| Selector | Description |\n| -------------------------------------------- | ------------------------------------------------------------------------- |\n| `.BaselineUI-ColorInput-Trigger` | The outer wrapper containing the label and trigger button |\n| `.BaselineUI-ColorInputButton` | The trigger button |\n| `.BaselineUI-ColorInput-Popover` | The popover container |\n| `.BaselineUI-ColorInput-ColorArea` | The color area (saturation/lightness) |\n| `.BaselineUI-ColorInput-ColorAreaThumb` | The draggable thumb on the color area |\n| `.BaselineUI-ColorInput-ColorSlider` | The hue/alpha slider |\n| `.BaselineUI-ColorInput-ColorSliderThumb` | The draggable thumb on a slider |\n| `.BaselineUI-ColorInput-FieldInput` | The hex/RGB text input |\n| `.BaselineUI-ColorInput-Presets` | The preset color list |\n| `.BaselineUI-ColorInput-CustomColors` | The custom colors header |\n| `.BaselineUI-ColorInput-CustomColorsListBox` | The custom colors list |\n| `[data-disabled]` | Applied when the button is disabled |\n| `[data-hovered]` | Applied when the button is hovered |\n| `[data-pressed]` | Applied when the button is pressed |\n| `[data-focus-visible]` | Applied when the button has keyboard focus |\n| `[data-color-mode="hexa"]` | Applied to hex field input when alpha is enabled |\n| `[data-color-mode="hex"]` | Applied to hex field input when alpha is disabled |\n| `[data-color-mode="rgba"]` | Applied to RGB field inputs when alpha is enabled |\n| `[data-color-mode="rgb"]` | Applied to RGB field inputs when alpha is disabled |\n| `[data-channel]` | Applied to color sliders, value is the channel name (e.g. `hue`, `alpha`) |\n\n| Key | Description |\n| ---------- | -------------------------------------------------------------------- |\n| Enter | Opens the popover or selects the focused color if popover is open |\n| Space | Opens the popover or selects the focused color if popover is open |\n| Escape | Closes the popover if open |\n| ArrowRight | Moves focus to the next preset color in the list |\n| ArrowLeft | Moves focus to the previous preset color in the list |\n| Tab | Moves focus between the color area, sliders, fields, and preset list |\n\nThe following strings are used by the `ColorInput` component and can be overridden via `I18nProvider`:\n\n| Key | Default (en) |\n| ------------------------------ | ---------------- |\n| `bui.colorInput.addColor` | Add Color |\n| `bui.colorInput.removeColor` | Remove Color |\n| `bui.colorInput.customColors` | Custom Colors |\n| `bui.colorInput.noColor` | None |\n| `bui.colorInput.transparent` | Transparent |\n| `bui.colorInput.add` | Add |\n| `bui.colorInput.cancel` | Cancel |\n| `bui.colorInput.colorFormat` | Color Format |\n| `bui.colorInput.colorPresets` | Color Presets |\n| `bui.colorInput.newColor` | New Custom Color |\n| `bui.colorInput.indeterminate` | Indeterminate |\n\n```jsx\nimport { I18nProvider, ColorInput } from "@baseline-ui/core";\n\n<I18nProvider\n locale="en"\n messages={{\n en: {\n "bui.colorInput.addColor": "Pick a Color",\n "bui.colorInput.cancel": "Dismiss",\n },\n }}\n>\n <ColorInput />\n</I18nProvider>;\n```\n\nThe `IconColorInput` component is a wrapper around the `ColorInput` component that allows you to render an icon next to the color input. This basically overrides the `renderTriggerButton` prop of the `ColorInput` component to\nprovide a predefined trigger button with an icon.\n\n```jsx\nimport { IconColorInput } from "@baseline-ui/core";\nimport { BorderColorIcon } from "@baseline-ui/icons/24";\n\n<IconColorInput icon={BorderColorIcon} aria-label="Color Picker" />;\n```\n\nYou can use the `variant` prop to change the appearance of the `IconColorInput` component. The `variant` prop accepts the following values: `standard` and `compact`.\n\n```jsx\n<IconColorInput icon={BorderColorIcon} aria-label="Color Picker" isDisabled />\n```\n\nThe `IconColorInput` component supports adding tooltip to the trigger button which is enabled by default. The tooltip will be the same as the `aria-label` of the trigger button. If you want to disable the tooltip, you can set the `tooltip` and `iconTooltip` props to `false`.\n\nThe `ColorSwatch` component is used to display a color swatch. The `ColorSwatch` component is used in the `ColorInput` component to display the selected color.\n\n```jsx\nimport { ColorSwatch } from "@baseline-ui/core";\n\n<ColorSwatch color="#ff0000" />;\n```',props:`interface ColorInputProps {
|
|
3831
3942
|
/**
|
|
3832
3943
|
* Whether the overlay is open by default (controlled).
|
|
3833
3944
|
*/
|
|
@@ -3948,19 +4059,19 @@ storePickedColorKey?: string
|
|
|
3948
4059
|
/**
|
|
3949
4060
|
* The label to show on the add color button.
|
|
3950
4061
|
*
|
|
3951
|
-
* @default Add
|
|
4062
|
+
* @default Add Color
|
|
3952
4063
|
*/
|
|
3953
4064
|
addColorButtonLabel?: string
|
|
3954
4065
|
/**
|
|
3955
4066
|
* The label to show on the remove color button.
|
|
3956
4067
|
*
|
|
3957
|
-
* @default Remove
|
|
4068
|
+
* @default Remove Color
|
|
3958
4069
|
*/
|
|
3959
4070
|
removeColorButtonLabel?: string
|
|
3960
4071
|
/**
|
|
3961
4072
|
* The label to show on the custom colors section.
|
|
3962
4073
|
*
|
|
3963
|
-
* @default Custom
|
|
4074
|
+
* @default Custom Colors
|
|
3964
4075
|
*/
|
|
3965
4076
|
customColorsLabel?: string
|
|
3966
4077
|
/**
|
|
@@ -4576,6 +4687,28 @@ By default the ComboBox current value cannot be set to values not included in it
|
|
|
4576
4687
|
/>
|
|
4577
4688
|
\`\`\`
|
|
4578
4689
|
|
|
4690
|
+
\`renderOption\` replaces the entire contents of each option \u2014 including the default selection checkmark, so render your own affordance from \`isSelected\` if you need one. Selection and keyboard behavior are unchanged. \`optionClassName\` and \`optionStyle\` accept either a value or a function of the item and its render state, and apply to the option element itself.
|
|
4691
|
+
|
|
4692
|
+
Keep all three referentially stable. The collection caches its elements per option and rebuilds every one of them whenever one of these changes identity, so an inline arrow rebuilds the list on each render.
|
|
4693
|
+
|
|
4694
|
+
\`\`\`jsx
|
|
4695
|
+
const renderOption = React.useCallback(
|
|
4696
|
+
(item) => (
|
|
4697
|
+
<span>
|
|
4698
|
+
{item.label}
|
|
4699
|
+
<span style={{ color: color.text.secondary }}> \u2014 custom</span>
|
|
4700
|
+
</span>
|
|
4701
|
+
),
|
|
4702
|
+
[],
|
|
4703
|
+
);
|
|
4704
|
+
|
|
4705
|
+
<ComboBox
|
|
4706
|
+
label="Custom option rendering"
|
|
4707
|
+
items={items}
|
|
4708
|
+
renderOption={renderOption}
|
|
4709
|
+
/>;
|
|
4710
|
+
\`\`\`
|
|
4711
|
+
|
|
4579
4712
|
The \`ComboBox\` input field filters visible items in the list to show only the ones that match the typed text. By default, only items that contain that text are shown. A custom filter function can be provided to modify this behavior.
|
|
4580
4713
|
|
|
4581
4714
|
This example provides a filter that will only show items that match exactly the typed text, case sensitive.
|
|
@@ -4695,7 +4828,10 @@ inputStyle?: React.CSSProperties
|
|
|
4695
4828
|
*/
|
|
4696
4829
|
inputClassName?: string
|
|
4697
4830
|
/**
|
|
4698
|
-
*
|
|
4831
|
+
* Accessible name for the toggle button. Not rendered visually.
|
|
4832
|
+
*
|
|
4833
|
+
* A blank string is treated as unset, keeping the built-in name. Setting this
|
|
4834
|
+
* overrides any \`aria-labelledby\` the toggle would otherwise inherit.
|
|
4699
4835
|
*/
|
|
4700
4836
|
toggleLabel?: string
|
|
4701
4837
|
filterItems?: boolean
|
|
@@ -4740,13 +4876,15 @@ filter?: ComboBoxStateOptions<ListItem>["defaultFilter"]
|
|
|
4740
4876
|
*/
|
|
4741
4877
|
inputType?: "text" | "number"
|
|
4742
4878
|
/**
|
|
4743
|
-
* The minimum value for the input field.
|
|
4744
|
-
*
|
|
4879
|
+
* The minimum value for the input field. Only applies when \`inputType="number"\`.
|
|
4880
|
+
*
|
|
4881
|
+
* Requires \`onValueChange\` or \`onInputSubmit\` to receive and apply the clamped value.
|
|
4745
4882
|
*/
|
|
4746
4883
|
minValue?: number
|
|
4747
4884
|
/**
|
|
4748
|
-
* The maximum value for the input field.
|
|
4749
|
-
*
|
|
4885
|
+
* The maximum value for the input field. Only applies when \`inputType="number"\`.
|
|
4886
|
+
*
|
|
4887
|
+
* Requires \`onValueChange\` or \`onInputSubmit\` to receive and apply the clamped value.
|
|
4750
4888
|
*/
|
|
4751
4889
|
maxValue?: number
|
|
4752
4890
|
/**
|
|
@@ -4754,7 +4892,17 @@ maxValue?: number
|
|
|
4754
4892
|
*/
|
|
4755
4893
|
onInputSubmit?: (value: string) => void
|
|
4756
4894
|
/**
|
|
4757
|
-
* Listener to call when the selected value changes
|
|
4895
|
+
* Listener to call when the selected value changes.
|
|
4896
|
+
*
|
|
4897
|
+
* Also called when the selected item's label changes after selection (e.g. the consumer
|
|
4898
|
+
* relabels the item, or an async collection resolves), so the reported \`value\` converges on
|
|
4899
|
+
* the item's current label once the field settles. While the user has unsubmitted text, a
|
|
4900
|
+
* label-only change is deferred; it is reported on the next selection, submit, or blur that
|
|
4901
|
+
* settles the input back onto the selection. With a controlled \`inputValue\` the consumer owns
|
|
4902
|
+
* that sync, so a deferred label is only reported once they settle the input themselves.
|
|
4903
|
+
*
|
|
4904
|
+
* Do not relabel the selected item from inside this callback \u2014 the new label re-triggers the
|
|
4905
|
+
* listener, which loops unless the relabel is idempotent.
|
|
4758
4906
|
*/
|
|
4759
4907
|
onValueChange?: (option: { value?: string; key?: string | null }) => void
|
|
4760
4908
|
}`,stories:{usage:[{id:"core-forms-combobox--basic",name:"Basic",snippet:`const Basic = () => <ComboBox
|
|
@@ -4965,7 +5113,33 @@ onValueChange?: (option: { value?: string; key?: string | null }) => void
|
|
|
4965
5113
|
defaultInputValue="42"
|
|
4966
5114
|
allowsCustomValue
|
|
4967
5115
|
inputType="number"
|
|
4968
|
-
minValue={0} />;`},{id:"core-forms-combobox--with-
|
|
5116
|
+
minValue={0} />;`},{id:"core-forms-combobox--with-min-max-value",name:"With Min Max Value",snippet:`const WithMinMaxValue = () => {
|
|
5117
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
5118
|
+
|
|
5119
|
+
return (
|
|
5120
|
+
<ComboBox
|
|
5121
|
+
items={[
|
|
5122
|
+
{ id: "10", label: "10" },
|
|
5123
|
+
{ id: "25", label: "25" },
|
|
5124
|
+
{ id: "50", label: "50" },
|
|
5125
|
+
]}
|
|
5126
|
+
aria-label="ComboBox"
|
|
5127
|
+
label="Type a number outside 10-50 range and blur"
|
|
5128
|
+
description="Values are clamped to minValue=10, maxValue=50 on blur"
|
|
5129
|
+
placeholder="Enter a number"
|
|
5130
|
+
allowsCustomValue
|
|
5131
|
+
inputType="number"
|
|
5132
|
+
minValue={10}
|
|
5133
|
+
maxValue={50}
|
|
5134
|
+
inputValue={inputValue}
|
|
5135
|
+
onInputChange={setInputValue}
|
|
5136
|
+
onValueChange={({ value }) => {
|
|
5137
|
+
if (value !== undefined) {
|
|
5138
|
+
setInputValue(value);
|
|
5139
|
+
}
|
|
5140
|
+
}} />
|
|
5141
|
+
);
|
|
5142
|
+
};`},{id:"core-forms-combobox--with-custom-filter",name:"With Custom Filter",snippet:`const WithCustomFilter = () => <ComboBox
|
|
4969
5143
|
items={[
|
|
4970
5144
|
{ id: "pear", label: "Pear" },
|
|
4971
5145
|
{ id: "apple", label: "Apple" },
|
|
@@ -4978,7 +5152,17 @@ onValueChange?: (option: { value?: string; key?: string | null }) => void
|
|
|
4978
5152
|
<AddItemDecorator>
|
|
4979
5153
|
{(items) => <ComboBox label="Select a fruit" {...args} items={items} />}
|
|
4980
5154
|
</AddItemDecorator>
|
|
4981
|
-
);`},{id:"core-forms-combobox--with-
|
|
5155
|
+
);`},{id:"core-forms-combobox--with-render-option",name:"With Render Option",snippet:`const WithRenderOption = () => <ComboBox
|
|
5156
|
+
items={items}
|
|
5157
|
+
aria-label="ComboBox"
|
|
5158
|
+
label="Custom option rendering"
|
|
5159
|
+
placeholder="Placeholder"
|
|
5160
|
+
renderOption={(item) => (
|
|
5161
|
+
<span style={{ ...typography.label.md.regular }}>
|
|
5162
|
+
{item.label}
|
|
5163
|
+
<span style={{ color: color.text.secondary }}> \u2014 custom</span>
|
|
5164
|
+
</span>
|
|
5165
|
+
)} />;`},{id:"core-forms-combobox--with-tooltip",name:"With Tooltip",snippet:`const WithTooltip = () => <ComboBox
|
|
4982
5166
|
items={items}
|
|
4983
5167
|
aria-label="This is aria-label"
|
|
4984
5168
|
tooltip
|
|
@@ -4996,6 +5180,8 @@ import { ComboBox } from "..";
|
|
|
4996
5180
|
import { Box } from "../../Box";
|
|
4997
5181
|
|
|
4998
5182
|
import type { ListItem, ListOption } from "../../shared/types/List";
|
|
5183
|
+
import type { ComboBoxProps } from "../ComboBox.types";
|
|
5184
|
+
import type { Key } from "react-aria";
|
|
4999
5185
|
|
|
5000
5186
|
export const items = [
|
|
5001
5187
|
{ id: "pears", label: "Pears" },
|
|
@@ -5047,11 +5233,36 @@ export const itemsWithSections = [
|
|
|
5047
5233
|
] as ListItem[];
|
|
5048
5234
|
|
|
5049
5235
|
export const ComboBoxExample: React.FC<
|
|
5050
|
-
Omit<React.ComponentProps<typeof ComboBox>, "items">
|
|
5051
|
-
|
|
5236
|
+
Omit<React.ComponentProps<typeof ComboBox>, "items"> & {
|
|
5237
|
+
items?: ListItem[];
|
|
5238
|
+
/** Must stay focusable \u2014 the outside-click test asserts focus lands on it. */
|
|
5239
|
+
withSiblingField?: boolean;
|
|
5240
|
+
onNonFocusingPress?: () => void;
|
|
5241
|
+
}
|
|
5242
|
+
> = ({
|
|
5243
|
+
items: comboBoxItems = items,
|
|
5244
|
+
withSiblingField,
|
|
5245
|
+
onNonFocusingPress,
|
|
5246
|
+
...args
|
|
5247
|
+
}) => {
|
|
5052
5248
|
return (
|
|
5053
5249
|
<Box style={{ width: "100%", height: 200 }}>
|
|
5054
|
-
|
|
5250
|
+
{/* Above the ComboBox: the popover opens downwards and would otherwise cover
|
|
5251
|
+
the spot a test means to click "outside" on. */}
|
|
5252
|
+
{withSiblingField ? <input aria-label="Sibling field" /> : null}
|
|
5253
|
+
{onNonFocusingPress ? (
|
|
5254
|
+
<button
|
|
5255
|
+
// Stands in for \`preventFocusOnPress\`: the press lands but the input
|
|
5256
|
+
// never blurs.
|
|
5257
|
+
onMouseDown={(e) => {
|
|
5258
|
+
e.preventDefault();
|
|
5259
|
+
}}
|
|
5260
|
+
onClick={onNonFocusingPress}
|
|
5261
|
+
>
|
|
5262
|
+
Toolbar action
|
|
5263
|
+
</button>
|
|
5264
|
+
) : null}
|
|
5265
|
+
<ComboBox items={comboBoxItems} aria-label="Combo box" {...args} />
|
|
5055
5266
|
</Box>
|
|
5056
5267
|
);
|
|
5057
5268
|
};
|
|
@@ -5076,6 +5287,155 @@ export const NumberComboBoxExample: React.FC<
|
|
|
5076
5287
|
);
|
|
5077
5288
|
};
|
|
5078
5289
|
|
|
5290
|
+
export const DynamicLabelComboBoxExample: React.FC<{
|
|
5291
|
+
onValueChange?: ComboBoxProps["onValueChange"];
|
|
5292
|
+
allowsCustomValue?: boolean;
|
|
5293
|
+
inputValue?: string;
|
|
5294
|
+
/** Relabels the selected item from "(selected)" to "(renamed)" when this exact text is typed. */
|
|
5295
|
+
relabelOnInput?: string;
|
|
5296
|
+
}> = ({ onValueChange, allowsCustomValue, inputValue, relabelOnInput }) => {
|
|
5297
|
+
const [value, setValue] = React.useState<Key | null>(null);
|
|
5298
|
+
// One-way: relabelling on every keystroke would feed the sync back into onInputChange and loop.
|
|
5299
|
+
const [isRelabelled, setIsRelabelled] = React.useState(false);
|
|
5300
|
+
|
|
5301
|
+
const suffix = isRelabelled ? "(renamed)" : "(selected)";
|
|
5302
|
+
const dynamicItems = items.map((item) =>
|
|
5303
|
+
item.id === value ? { ...item, label: \`\${item.label} \${suffix}\` } : item,
|
|
5304
|
+
);
|
|
5305
|
+
|
|
5306
|
+
return (
|
|
5307
|
+
<Box style={{ width: "100%", height: 200 }}>
|
|
5308
|
+
<ComboBox
|
|
5309
|
+
items={dynamicItems}
|
|
5310
|
+
aria-label="Combo box"
|
|
5311
|
+
value={value}
|
|
5312
|
+
onChange={setValue}
|
|
5313
|
+
onValueChange={onValueChange}
|
|
5314
|
+
allowsCustomValue={allowsCustomValue}
|
|
5315
|
+
inputValue={inputValue}
|
|
5316
|
+
onInputChange={(text) => {
|
|
5317
|
+
if (text === relabelOnInput) {
|
|
5318
|
+
setIsRelabelled(true);
|
|
5319
|
+
}
|
|
5320
|
+
}}
|
|
5321
|
+
/>
|
|
5322
|
+
<span data-testid="relabel-probe">
|
|
5323
|
+
{dynamicItems.find((item) => item.id === value)?.label ?? ""}
|
|
5324
|
+
</span>
|
|
5325
|
+
</Box>
|
|
5326
|
+
);
|
|
5327
|
+
};
|
|
5328
|
+
|
|
5329
|
+
export const AsyncItemsComboBoxExample: React.FC<{
|
|
5330
|
+
initiallyLoaded?: boolean;
|
|
5331
|
+
onValueChange?: ComboBoxProps["onValueChange"];
|
|
5332
|
+
allowsCustomValue?: boolean;
|
|
5333
|
+
/** Unloads and reloads the items when this exact text is typed, without moving focus. */
|
|
5334
|
+
refreshOnInput?: string;
|
|
5335
|
+
}> = ({
|
|
5336
|
+
initiallyLoaded = true,
|
|
5337
|
+
onValueChange,
|
|
5338
|
+
allowsCustomValue,
|
|
5339
|
+
refreshOnInput,
|
|
5340
|
+
}) => {
|
|
5341
|
+
const [loadState, setLoadState] = React.useState<
|
|
5342
|
+
"loaded" | "unloaded" | "refreshing"
|
|
5343
|
+
>(initiallyLoaded ? "loaded" : "unloaded");
|
|
5344
|
+
const [refreshCount, setRefreshCount] = React.useState(0);
|
|
5345
|
+
const [value, setValue] = React.useState<Key | null>(
|
|
5346
|
+
initiallyLoaded ? null : "pears",
|
|
5347
|
+
);
|
|
5348
|
+
const isLoaded = loadState === "loaded";
|
|
5349
|
+
|
|
5350
|
+
// The unloaded collection must reach the DOM for a render before the items come back,
|
|
5351
|
+
// otherwise there is no transient for the refresh to be mistaken for a new selection.
|
|
5352
|
+
React.useEffect(() => {
|
|
5353
|
+
if (loadState === "refreshing") {
|
|
5354
|
+
setLoadState("loaded");
|
|
5355
|
+
setRefreshCount((count) => count + 1);
|
|
5356
|
+
}
|
|
5357
|
+
}, [loadState]);
|
|
5358
|
+
|
|
5359
|
+
return (
|
|
5360
|
+
<Box style={{ width: "100%", height: 200 }}>
|
|
5361
|
+
<ComboBox
|
|
5362
|
+
items={isLoaded ? items : []}
|
|
5363
|
+
aria-label="Combo box"
|
|
5364
|
+
value={value}
|
|
5365
|
+
onChange={setValue}
|
|
5366
|
+
onValueChange={onValueChange}
|
|
5367
|
+
allowsCustomValue={allowsCustomValue}
|
|
5368
|
+
onInputChange={(text) => {
|
|
5369
|
+
if (text === refreshOnInput) {
|
|
5370
|
+
setLoadState("refreshing");
|
|
5371
|
+
}
|
|
5372
|
+
}}
|
|
5373
|
+
/>
|
|
5374
|
+
<button
|
|
5375
|
+
type="button"
|
|
5376
|
+
onClick={() => {
|
|
5377
|
+
setLoadState((state) => (state === "loaded" ? "unloaded" : "loaded"));
|
|
5378
|
+
}}
|
|
5379
|
+
>
|
|
5380
|
+
{isLoaded ? "Unload items" : "Load items"}
|
|
5381
|
+
</button>
|
|
5382
|
+
<span data-testid="refresh-probe">{refreshCount}</span>
|
|
5383
|
+
</Box>
|
|
5384
|
+
);
|
|
5385
|
+
};
|
|
5386
|
+
|
|
5387
|
+
export const LabelledByComboBoxExample: React.FC<
|
|
5388
|
+
Omit<React.ComponentProps<typeof ComboBox>, "items" | "aria-labelledby">
|
|
5389
|
+
> = (args) => {
|
|
5390
|
+
return (
|
|
5391
|
+
<Box style={{ width: "100%", height: 200 }}>
|
|
5392
|
+
<span id="external-label">External</span>
|
|
5393
|
+
<ComboBox items={items} {...args} aria-labelledby="external-label" />
|
|
5394
|
+
</Box>
|
|
5395
|
+
);
|
|
5396
|
+
};
|
|
5397
|
+
|
|
5398
|
+
export const CustomOptionComboBoxExample: React.FC = () => {
|
|
5399
|
+
return (
|
|
5400
|
+
<ComboBoxExample
|
|
5401
|
+
renderOption={(item) => <span>{item.label} \u2014 custom</span>}
|
|
5402
|
+
optionClassName={(item) => \`custom-option-\${item.id}\`}
|
|
5403
|
+
optionStyle={(_item, { isSelected }) => ({
|
|
5404
|
+
outline: isSelected
|
|
5405
|
+
? "1px solid rgb(0, 128, 0)"
|
|
5406
|
+
: "1px solid rgb(255, 0, 0)",
|
|
5407
|
+
})}
|
|
5408
|
+
/>
|
|
5409
|
+
);
|
|
5410
|
+
};
|
|
5411
|
+
|
|
5412
|
+
export const CountingOptionComboBoxExample: React.FC<{
|
|
5413
|
+
withSections?: boolean;
|
|
5414
|
+
}> = ({ withSections = false }) => {
|
|
5415
|
+
const [count, setCount] = React.useState(0);
|
|
5416
|
+
|
|
5417
|
+
return (
|
|
5418
|
+
<>
|
|
5419
|
+
<button
|
|
5420
|
+
onClick={() => {
|
|
5421
|
+
setCount(count + 1);
|
|
5422
|
+
}}
|
|
5423
|
+
>
|
|
5424
|
+
Increment
|
|
5425
|
+
</button>
|
|
5426
|
+
<ComboBoxExample
|
|
5427
|
+
items={withSections ? itemsWithSections : items}
|
|
5428
|
+
renderOption={(item) => (
|
|
5429
|
+
<span>
|
|
5430
|
+
{item.label} #{count}
|
|
5431
|
+
</span>
|
|
5432
|
+
)}
|
|
5433
|
+
optionClassName={() => \`count-\${count}\`}
|
|
5434
|
+
/>
|
|
5435
|
+
</>
|
|
5436
|
+
);
|
|
5437
|
+
};
|
|
5438
|
+
|
|
5079
5439
|
export const SectionComboBoxExample: React.FC = () => {
|
|
5080
5440
|
return (
|
|
5081
5441
|
<Box style={{ width: "100%", height: 300 }}>
|
|
@@ -7710,6 +8070,10 @@ children?: ReactNode
|
|
|
7710
8070
|
* Enables the selection of directories instead of individual files.
|
|
7711
8071
|
*/
|
|
7712
8072
|
acceptDirectory?: boolean
|
|
8073
|
+
/**
|
|
8074
|
+
* The id applied to the root element of the component.
|
|
8075
|
+
*/
|
|
8076
|
+
id?: string
|
|
7713
8077
|
/**
|
|
7714
8078
|
* The className applied to the root element of the component.
|
|
7715
8079
|
*/
|
|
@@ -8005,6 +8369,7 @@ disabledKeys?: Iterable<string> | "all"
|
|
|
8005
8369
|
);
|
|
8006
8370
|
};`}],implementation:`import React from "react";
|
|
8007
8371
|
|
|
8372
|
+
import { ActionButton } from "../../ActionButton";
|
|
8008
8373
|
import { FileList } from "../FileList";
|
|
8009
8374
|
|
|
8010
8375
|
import type { FileListItem } from "../FileList.types";
|
|
@@ -8059,6 +8424,32 @@ export const manyItems: FileListItem[] = Array.from({ length: 12 }, (_, i) => ({
|
|
|
8059
8424
|
name: \`attachment-\${i + 1}.pdf\`,
|
|
8060
8425
|
}));
|
|
8061
8426
|
|
|
8427
|
+
// Swaps in a new array whose items carry new content, which is the only way a caller can
|
|
8428
|
+
// legitimately invalidate the memoised options.
|
|
8429
|
+
export const UploadingFileList: React.FC = () => {
|
|
8430
|
+
const [items, setItems] = React.useState<FileListItem[]>(loadingItems);
|
|
8431
|
+
|
|
8432
|
+
return (
|
|
8433
|
+
<>
|
|
8434
|
+
<ActionButton
|
|
8435
|
+
label="Finish upload"
|
|
8436
|
+
onPress={() => {
|
|
8437
|
+
setItems((prev) =>
|
|
8438
|
+
prev.map((item) => ({ ...item, isLoading: false })),
|
|
8439
|
+
);
|
|
8440
|
+
}}
|
|
8441
|
+
/>
|
|
8442
|
+
<FileList
|
|
8443
|
+
aria-label="Uploaded files"
|
|
8444
|
+
items={items}
|
|
8445
|
+
onRemove={(id) => {
|
|
8446
|
+
setItems((prev) => prev.filter((item) => item.id !== id));
|
|
8447
|
+
}}
|
|
8448
|
+
/>
|
|
8449
|
+
</>
|
|
8450
|
+
);
|
|
8451
|
+
};
|
|
8452
|
+
|
|
8062
8453
|
interface BasicProps {
|
|
8063
8454
|
onRemove?: (id: string) => void;
|
|
8064
8455
|
disabledKeys?: Iterable<string> | "all";
|
|
@@ -9816,9 +10207,9 @@ children: React.ReactNode
|
|
|
9816
10207
|
This example shows how to use \`I18nProvider\` to override the default locale.
|
|
9817
10208
|
|
|
9818
10209
|
\`\`\`jsx
|
|
9819
|
-
import { I18nProvider } from "@
|
|
10210
|
+
import { I18nProvider } from "@baseline-ui/core";
|
|
9820
10211
|
|
|
9821
|
-
export const
|
|
10212
|
+
export const Root = () => (
|
|
9822
10213
|
<I18nProvider locale="en">
|
|
9823
10214
|
<App />
|
|
9824
10215
|
</I18nProvider>
|
|
@@ -9829,14 +10220,18 @@ The \`App\` component will receive the new locale through \`useLocale\`.
|
|
|
9829
10220
|
|
|
9830
10221
|
\`\`\`jsx
|
|
9831
10222
|
// App.js
|
|
9832
|
-
import { useLocale } from "@
|
|
10223
|
+
import { useLocale } from "@baseline-ui/core";
|
|
9833
10224
|
|
|
9834
10225
|
const App = () => {
|
|
9835
|
-
const locale = useLocale();
|
|
10226
|
+
const { locale } = useLocale();
|
|
9836
10227
|
|
|
9837
10228
|
return <div>Current locale: {locale}</div>;
|
|
9838
10229
|
};
|
|
9839
|
-
|
|
10230
|
+
\`\`\`
|
|
10231
|
+
|
|
10232
|
+
\`locale\` only switches date, number and collation formatting and text direction. To
|
|
10233
|
+
translate the strings Baseline UI renders, pass a catalog through \`messages\` \u2014 see
|
|
10234
|
+
[Internationalization](/?path=/docs/internationalization--docs#loading-a-translation-catalog).`,props:`interface I18nProviderProps {
|
|
9840
10235
|
/**
|
|
9841
10236
|
* Contents that should have the locale applied.
|
|
9842
10237
|
*/
|
|
@@ -9847,7 +10242,12 @@ children: React.ReactNode
|
|
|
9847
10242
|
locale?: string
|
|
9848
10243
|
messages?: LocalizedStrings
|
|
9849
10244
|
/**
|
|
9850
|
-
*
|
|
10245
|
+
* Whether to \`console.warn\` about message ids that resolve through the
|
|
10246
|
+
* deprecated bare-id fallback or fall all the way through to their
|
|
10247
|
+
* \`defaultMessage\`. These warnings are development guidance, so they are off
|
|
10248
|
+
* in production builds \u2014 pass \`true\` to opt back in.
|
|
10249
|
+
*
|
|
10250
|
+
* @default process.env.NODE_ENV !== "production"
|
|
9851
10251
|
*/
|
|
9852
10252
|
shouldLogMissingMessages?: boolean
|
|
9853
10253
|
}`,stories:{usage:[{id:"core-utilities-i18nprovider--string-format",name:"String Format",snippet:`const StringFormat = ({ children }) => {
|
|
@@ -9987,7 +10387,6 @@ The following strings are used by the \`ImageDropZone\` component and can be ove
|
|
|
9987
10387
|
| Key | Default (en) |
|
|
9988
10388
|
| ------------------------------- | ------------ |
|
|
9989
10389
|
| \`bui.imageDropZone.selectImage\` | Select Image |
|
|
9990
|
-
| \`bui.imageDropZone.delete\` | Delete |
|
|
9991
10390
|
|
|
9992
10391
|
\`\`\`jsx
|
|
9993
10392
|
import { I18nProvider, ImageDropZone } from "@baseline-ui/core";
|
|
@@ -9997,7 +10396,6 @@ import { I18nProvider, ImageDropZone } from "@baseline-ui/core";
|
|
|
9997
10396
|
messages={{
|
|
9998
10397
|
en: {
|
|
9999
10398
|
"bui.imageDropZone.selectImage": "Choose Image",
|
|
10000
|
-
"bui.imageDropZone.delete": "Remove",
|
|
10001
10399
|
},
|
|
10002
10400
|
}}
|
|
10003
10401
|
>
|
|
@@ -10420,6 +10818,8 @@ renderImage?: (
|
|
|
10420
10818
|
imageContainerStyle?: React.CSSProperties
|
|
10421
10819
|
/**
|
|
10422
10820
|
* The dimensions of the image.
|
|
10821
|
+
*
|
|
10822
|
+
* @default { width: imageWidth, aspectRatio }
|
|
10423
10823
|
*/
|
|
10424
10824
|
imageDimensions?: | Dimension
|
|
10425
10825
|
| ((
|
|
@@ -10603,6 +11003,10 @@ import { Box } from "../../Box";
|
|
|
10603
11003
|
import { I18nProvider } from "../../I18nProvider";
|
|
10604
11004
|
import { NumberInput } from "../../NumberInput";
|
|
10605
11005
|
import { Text } from "../../Text";
|
|
11006
|
+
import {
|
|
11007
|
+
VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS,
|
|
11008
|
+
Virtualizer,
|
|
11009
|
+
} from "../../Virtualizer";
|
|
10606
11010
|
import { ImageGallery, ImageSize } from "../ImageGallery";
|
|
10607
11011
|
import { items } from "./data";
|
|
10608
11012
|
|
|
@@ -10878,6 +11282,35 @@ export function ScrollIntoViewImageGalleryExample() {
|
|
|
10878
11282
|
);
|
|
10879
11283
|
}
|
|
10880
11284
|
|
|
11285
|
+
// Counts \`renderImage\` calls so a spec can prove that a re-render which changes nothing
|
|
11286
|
+
// the gallery reads does not throw away the list box's cached options. \`Report\` publishes
|
|
11287
|
+
// the tally taken *before* its own render, so two clicks bracket exactly one re-render.
|
|
11288
|
+
export function RenderCountingImageGalleryExample() {
|
|
11289
|
+
const calls = React.useRef(0);
|
|
11290
|
+
const [reported, setReported] = React.useState(0);
|
|
11291
|
+
|
|
11292
|
+
const renderImage = React.useCallback<
|
|
11293
|
+
Exclude<React.ComponentProps<typeof ImageGallery>["renderImage"], undefined>
|
|
11294
|
+
>((item) => {
|
|
11295
|
+
calls.current += 1;
|
|
11296
|
+
|
|
11297
|
+
return <img src={item.data?.src} alt={item.data?.alt} />;
|
|
11298
|
+
}, []);
|
|
11299
|
+
|
|
11300
|
+
return (
|
|
11301
|
+
<Box display="flex" flexDirection="column" gap="lg">
|
|
11302
|
+
<ActionButton
|
|
11303
|
+
label="Report"
|
|
11304
|
+
onPress={() => {
|
|
11305
|
+
setReported(calls.current);
|
|
11306
|
+
}}
|
|
11307
|
+
/>
|
|
11308
|
+
<Text data-testid="render-count">{reported.toString()}</Text>
|
|
11309
|
+
<ImageGallery defaultItems={items} renderImage={renderImage} />
|
|
11310
|
+
</Box>
|
|
11311
|
+
);
|
|
11312
|
+
}
|
|
11313
|
+
|
|
10881
11314
|
export function RTLImageGalleryExample(
|
|
10882
11315
|
props: Omit<React.ComponentProps<typeof ImageGallery>, "onDelete">,
|
|
10883
11316
|
) {
|
|
@@ -10886,6 +11319,43 @@ export function RTLImageGalleryExample(
|
|
|
10886
11319
|
<ImageGallery {...props} />
|
|
10887
11320
|
</I18nProvider>
|
|
10888
11321
|
);
|
|
11322
|
+
}
|
|
11323
|
+
|
|
11324
|
+
/**
|
|
11325
|
+
* Driven by a window event rather than a button, because clicking anything would move focus
|
|
11326
|
+
* and end the keyboard drag by itself.
|
|
11327
|
+
*/
|
|
11328
|
+
export function RerenderOnDemandImageGalleryExample({
|
|
11329
|
+
isVirtualized = false,
|
|
11330
|
+
...props
|
|
11331
|
+
}: Omit<React.ComponentProps<typeof ImageGallery>, "onDelete"> & {
|
|
11332
|
+
isVirtualized?: boolean;
|
|
11333
|
+
}) {
|
|
11334
|
+
const [, rerender] = React.useReducer((tick: number) => tick + 1, 0);
|
|
11335
|
+
|
|
11336
|
+
React.useEffect(() => {
|
|
11337
|
+
const onRerender = () => {
|
|
11338
|
+
rerender();
|
|
11339
|
+
};
|
|
11340
|
+
|
|
11341
|
+
window.addEventListener("repro:rerender", onRerender);
|
|
11342
|
+
|
|
11343
|
+
return () => {
|
|
11344
|
+
window.removeEventListener("repro:rerender", onRerender);
|
|
11345
|
+
};
|
|
11346
|
+
}, []);
|
|
11347
|
+
|
|
11348
|
+
const gallery = <ImageGallery defaultItems={items} {...props} />;
|
|
11349
|
+
|
|
11350
|
+
if (!isVirtualized) {
|
|
11351
|
+
return gallery;
|
|
11352
|
+
}
|
|
11353
|
+
|
|
11354
|
+
return (
|
|
11355
|
+
<Virtualizer {...VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS.IMAGE_GALLERY}>
|
|
11356
|
+
{gallery}
|
|
11357
|
+
</Virtualizer>
|
|
11358
|
+
);
|
|
10889
11359
|
}`},similarTo:[],figmaUrl:null},InlineAlert:{id:"core-status-inlinealert",breadcrumb:"Core/Status/InlineAlert",importStatement:'import { InlineAlert } from "@baseline-ui/core";',description:"`InlineAlert` is a status message anchored next to related content, with success, warning, error, or info styling and optional action and dismiss buttons. Use it for persistent, in-context feedback that should stay visible without interrupting the user's flow.",documentation:`\`InlineAlert\` is a status message anchored next to related content, with success, warning, error, or info styling and optional action and dismiss buttons. Use it for persistent, in-context feedback that should stay visible without interrupting the user's flow.
|
|
10890
11360
|
|
|
10891
11361
|
* Exposed to assistive technology with \`role="alert"\`.
|
|
@@ -12882,27 +13352,45 @@ console.log(greet('Markdown Maverick'));
|
|
|
12882
13352
|
2. Selection options: The component can be configured to allow single, multiple, or no selection.
|
|
12883
13353
|
3. Disabled items support: Certain menu items can be disabled, preventing user interaction.
|
|
12884
13354
|
4. Sections support: Related items can be grouped into sections for better organization and navigation.
|
|
12885
|
-
5.
|
|
12886
|
-
6.
|
|
12887
|
-
7.
|
|
12888
|
-
8.
|
|
12889
|
-
9.
|
|
13355
|
+
5. Submenus: Any option can nest an \`items\` array to open a cascading submenu of arbitrary depth.
|
|
13356
|
+
6. Keyboard navigation: Users can navigate through the menu using their keyboard, including arrow keys, home/end, and page up/down.
|
|
13357
|
+
7. Auto-scroll: The menu automatically scrolls as the user navigates through it with their keyboard.
|
|
13358
|
+
8. Keyboard menu opening: The menu can be opened using the keyboard, and automatically focuses on the first or last item.
|
|
13359
|
+
9. Typeahead: Users can quickly navigate to a menu item by typing the first few letters of the item's label.
|
|
13360
|
+
10. Virtualized scrolling: For long lists, performance is improved by only rendering the visible items.
|
|
13361
|
+
|
|
13362
|
+
Pass the list of options via \`items\` and a \`triggerLabel\` for the default
|
|
13363
|
+
\`ActionButton\` trigger. \`Menu\` renders its own trigger and popover \u2014 it does not
|
|
13364
|
+
take children.
|
|
12890
13365
|
|
|
12891
13366
|
\`\`\`jsx
|
|
12892
|
-
import {
|
|
12893
|
-
import { Menu } from "@storybook/addon-docs/blocks";
|
|
13367
|
+
import { Menu } from "@baseline-ui/core";
|
|
12894
13368
|
|
|
12895
13369
|
const items = [
|
|
12896
|
-
{
|
|
12897
|
-
label: "
|
|
12898
|
-
id: "
|
|
12899
|
-
keyboardShortcut: "\u2318X",
|
|
12900
|
-
},
|
|
13370
|
+
{ id: "cut", label: "Cut", keyboardShortcut: "\u2318X" },
|
|
13371
|
+
{ id: "copy", label: "Copy", keyboardShortcut: "\u2318C" },
|
|
13372
|
+
{ id: "paste", label: "Paste", keyboardShortcut: "\u2318V" },
|
|
12901
13373
|
];
|
|
12902
13374
|
|
|
12903
|
-
|
|
12904
|
-
<
|
|
12905
|
-
|
|
13375
|
+
export default function App() {
|
|
13376
|
+
return <Menu items={items} triggerLabel="Edit" onAction={(id) => {}} />;
|
|
13377
|
+
}
|
|
13378
|
+
\`\`\`
|
|
13379
|
+
|
|
13380
|
+
To render a custom trigger, pass \`renderTrigger\` instead of \`triggerLabel\`. If
|
|
13381
|
+
you need on/off state rather than a command list, use \`ToggleButton\`; to pick a
|
|
13382
|
+
value from a set with an always-visible field, use \`Select\`.
|
|
13383
|
+
|
|
13384
|
+
Each option accepts an optional leading \`icon\` (a \`@baseline-ui/icons\` component)
|
|
13385
|
+
and a \`keyboardShortcut\` string rendered at the trailing edge of the item.
|
|
13386
|
+
|
|
13387
|
+
\`\`\`jsx
|
|
13388
|
+
import { CopyIcon, TrashIcon } from "@baseline-ui/icons/16";
|
|
13389
|
+
|
|
13390
|
+
const items = [
|
|
13391
|
+
{ id: "copy", label: "Copy", keyboardShortcut: "\u2318C", icon: CopyIcon },
|
|
13392
|
+
{ id: "delete", label: "Delete", keyboardShortcut: "\u232B", icon: TrashIcon },
|
|
13393
|
+
];
|
|
12906
13394
|
\`\`\`
|
|
12907
13395
|
|
|
12908
13396
|
The \`Menu\` component can be configured to allow single, multiple, or no selection. The default is no selection. To allow single selection, set the \`selectionMode\` prop to \`single\`. To allow multiple selection, set the \`selectionMode\` prop to \`multiple\`.
|
|
@@ -12930,9 +13418,93 @@ const items = [
|
|
|
12930
13418
|
];
|
|
12931
13419
|
\`\`\`
|
|
12932
13420
|
|
|
12933
|
-
|
|
13421
|
+
Pass the object form of \`selectionMode\` to make individual sections independent
|
|
13422
|
+
selection groups. \`selectedKeys\` and \`defaultSelectedKeys\` are then keyed by
|
|
13423
|
+
section id, and \`onSelectionChange\` receives the changed section's selection
|
|
13424
|
+
plus its \`sectionId\`.
|
|
12934
13425
|
|
|
12935
|
-
|
|
13426
|
+
\`\`\`tsx
|
|
13427
|
+
<Menu
|
|
13428
|
+
items={itemsWithSections}
|
|
13429
|
+
selectionMode={{ edit: "single", theme: "multiple" }}
|
|
13430
|
+
selectedKeys={{ edit: editKeys, theme: themeKeys }}
|
|
13431
|
+
onSelectionChange={(keys, meta) => {
|
|
13432
|
+
if (meta) setSelection((prev) => ({ ...prev, [meta.sectionId]: keys }));
|
|
13433
|
+
}}
|
|
13434
|
+
/>
|
|
13435
|
+
\`\`\`
|
|
13436
|
+
|
|
13437
|
+
Menu-wide selection (a string \`selectionMode\`) and per-section selection are
|
|
13438
|
+
mutually exclusive within one menu. Enter activation closes the menu. Pointer
|
|
13439
|
+
activation closes single-select menus and keeps multi-select menus open, while
|
|
13440
|
+
Space activation keeps both selection modes open.
|
|
13441
|
+
|
|
13442
|
+
Give any option an \`items\` array to turn it into a submenu trigger. The option
|
|
13443
|
+
renders with a trailing chevron and opens a nested menu on hover or when
|
|
13444
|
+
activated with \`ArrowRight\` / \`Enter\`. Submenus nest arbitrarily and may
|
|
13445
|
+
themselves contain leaf options, sections, or deeper submenus.
|
|
13446
|
+
|
|
13447
|
+
\`\`\`jsx
|
|
13448
|
+
const items = [
|
|
13449
|
+
{ id: "new", label: "New" },
|
|
13450
|
+
{
|
|
13451
|
+
id: "share",
|
|
13452
|
+
label: "Share",
|
|
13453
|
+
items: [
|
|
13454
|
+
{ id: "email", label: "Email link" },
|
|
13455
|
+
{ id: "sms", label: "SMS" },
|
|
13456
|
+
{
|
|
13457
|
+
id: "more",
|
|
13458
|
+
label: "More services",
|
|
13459
|
+
items: [
|
|
13460
|
+
{ id: "slack", label: "Slack" },
|
|
13461
|
+
{ id: "teams", label: "Teams" },
|
|
13462
|
+
],
|
|
13463
|
+
},
|
|
13464
|
+
],
|
|
13465
|
+
},
|
|
13466
|
+
];
|
|
13467
|
+
\`\`\`
|
|
13468
|
+
|
|
13469
|
+
\`onAction\` fires for the activated leaf at any depth (with that leaf's \`id\`);
|
|
13470
|
+
activating a submenu trigger does not fire \`onAction\`. Selecting a leaf closes
|
|
13471
|
+
the whole cascade. A single flat \`disabledKeys\` array disables triggers and
|
|
13472
|
+
nested items at any level. In RTL locales the chevron and the submenu's opening
|
|
13473
|
+
direction flip automatically.
|
|
13474
|
+
|
|
13475
|
+
Selection can be controlled or uncontrolled. When uncontrolled, the component manages its own state internally via \`defaultSelectedKeys\`. To control it, pass \`selectedKeys\` and handle \`onSelectionChange\`. The keys must match the \`id\` of the items. See the [Single Selection](#single-selection) example above.
|
|
13476
|
+
|
|
13477
|
+
The open state can likewise be controlled or uncontrolled. When uncontrolled, use \`defaultOpen\`; to control it, pass \`isOpen\` and handle \`onOpenChange\`.
|
|
13478
|
+
|
|
13479
|
+
Each menu item exposes the standard React Aria interaction-state attributes, and Baseline UI adds class hooks on the trigger, popover, and item subparts.
|
|
13480
|
+
|
|
13481
|
+
| Selector | Description |
|
|
13482
|
+
| ---------------------------------- | ------------------------------------------------------- |
|
|
13483
|
+
| \\[data-focused] | Whether the item is focused. |
|
|
13484
|
+
| \\[data-focus-visible] | Whether the item is keyboard focused. |
|
|
13485
|
+
| \\[data-hovered] | Whether the item is currently hovered with a mouse. |
|
|
13486
|
+
| \\[data-pressed] | Whether the item is currently pressed. |
|
|
13487
|
+
| \\[data-selected] | Whether the item is selected (selection modes). |
|
|
13488
|
+
| \\[data-disabled] | Whether the item is disabled. |
|
|
13489
|
+
| \\[data-has-submenu] | Whether the item opens a submenu. |
|
|
13490
|
+
| \\[data-open] | Whether the item's submenu is open. |
|
|
13491
|
+
| .BaselineUI-Menu-Trigger | The trigger button. |
|
|
13492
|
+
| .BaselineUI-Menu-Popover | The root popover container. |
|
|
13493
|
+
| .BaselineUI-Menu-SubmenuPopover | A nested submenu popover container. |
|
|
13494
|
+
| .BaselineUI-Menu-SubmenuChevron | The trailing chevron on a submenu trigger. |
|
|
13495
|
+
| .BaselineUI-Menu-OptionDescription | The trailing text of an item (e.g. \`keyboardShortcut\`). |
|
|
13496
|
+
|
|
13497
|
+
| Key | Function |
|
|
13498
|
+
| --------------------- | ---------------------------------------------------------------------------------- |
|
|
13499
|
+
| \`Enter\` / \`Space\` | Opens the menu from the trigger; activates the focused item. |
|
|
13500
|
+
| \`ArrowDown\` | Opens the menu focusing the first item; moves focus down. |
|
|
13501
|
+
| \`ArrowUp\` | Opens the menu focusing the last item; moves focus up. |
|
|
13502
|
+
| \`Home\` / \`End\` | Moves focus to the first / last item. |
|
|
13503
|
+
| \`PageUp\` / \`PageDown\` | Moves focus up / down by a page. |
|
|
13504
|
+
| \`A\`\u2013\`Z\` | Typeahead \u2014 focuses the next item starting with the typed letters. |
|
|
13505
|
+
| \`ArrowRight\` | Opens the focused submenu (\`ArrowLeft\` in RTL). |
|
|
13506
|
+
| \`ArrowLeft\` | Closes the current submenu and returns focus to its trigger (\`ArrowRight\` in RTL). |
|
|
13507
|
+
| \`Escape\` | Closes the menu (or the current submenu level). |`,props:`interface MenuProps {
|
|
12936
13508
|
/**
|
|
12937
13509
|
* Whether the overlay is open by default (controlled).
|
|
12938
13510
|
*/
|
|
@@ -12964,39 +13536,18 @@ contentClassName?: string
|
|
|
12964
13536
|
*/
|
|
12965
13537
|
itemClassName?: string
|
|
12966
13538
|
/**
|
|
12967
|
-
* A list of items to render in the menu.
|
|
12968
|
-
*
|
|
12969
|
-
* \`\`\`ts
|
|
12970
|
-
* export type MenuOption = {
|
|
12971
|
-
* id: string;
|
|
12972
|
-
* label: string;
|
|
12973
|
-
* keyboardShortcut?: string;
|
|
12974
|
-
* icon?: React.FC<IconProps>;
|
|
12975
|
-
* };
|
|
12976
|
-
*
|
|
12977
|
-
* export type MenuSection = {
|
|
12978
|
-
* id: string;
|
|
12979
|
-
* title?: string;
|
|
12980
|
-
* type: "section";
|
|
12981
|
-
* children: MenuOption[];
|
|
12982
|
-
* };
|
|
13539
|
+
* A list of items to render in the menu. See \`MenuItem\`.
|
|
12983
13540
|
*
|
|
12984
|
-
*
|
|
12985
|
-
*
|
|
13541
|
+
* A \`MenuSection\` may opt into independent selection by listing its id in the
|
|
13542
|
+
* object form of \`selectionMode\` (e.g. \`selectionMode={{ view: "single" }}\`).
|
|
12986
13543
|
*/
|
|
12987
13544
|
items: MenuItem[]
|
|
12988
13545
|
/**
|
|
12989
13546
|
* A function that renders the trigger element of the component. The default
|
|
12990
13547
|
* implementation renders an \`ActionButton\` component.
|
|
12991
|
-
*
|
|
12992
|
-
* \`\`\`tsx
|
|
12993
|
-
* <Menu renderTrigger={({ buttonProps, ref }) => <ActionButton {...buttonProps} label="Label" ref={ref} />
|
|
12994
|
-
* \`\`\`
|
|
12995
13548
|
*/
|
|
12996
13549
|
renderTrigger?: (options: {
|
|
12997
|
-
buttonProps: ActionButtonProps & {
|
|
12998
|
-
isOpen: boolean;
|
|
12999
|
-
};
|
|
13550
|
+
buttonProps: ActionButtonProps & { isOpen: boolean };
|
|
13000
13551
|
ref: React.RefObject<HTMLButtonElement>;
|
|
13001
13552
|
}) => React.ReactNode
|
|
13002
13553
|
/**
|
|
@@ -13004,6 +13555,33 @@ renderTrigger?: (options: {
|
|
|
13004
13555
|
* function that accepts a boolean indicating whether the menu is open.
|
|
13005
13556
|
*/
|
|
13006
13557
|
triggerLabel?: React.ReactNode | ((isOpen: boolean) => React.ReactNode)
|
|
13558
|
+
/**
|
|
13559
|
+
* \`"single"\`/\`"multiple"\` select the whole menu. A record keyed by section id
|
|
13560
|
+
* makes each listed section an independent selection group.
|
|
13561
|
+
*/
|
|
13562
|
+
selectionMode?: | "none"
|
|
13563
|
+
| "single"
|
|
13564
|
+
| "multiple"
|
|
13565
|
+
| Record<string, "single" | "multiple">
|
|
13566
|
+
/**
|
|
13567
|
+
* A menu-wide selection (\`"all"\` or an iterable of keys) or a per-section
|
|
13568
|
+
* record of \`Selection\`. The iterable form preserves the pre-existing
|
|
13569
|
+
* \`AriaMenuProps.selectedKeys\` shape (arrays stay assignable).
|
|
13570
|
+
*/
|
|
13571
|
+
selectedKeys?: "all" | Iterable<Key> | Record<string, Selection>
|
|
13572
|
+
/**
|
|
13573
|
+
* Initial selection, in the same flat-or-per-section shape as \`selectedKeys\`.
|
|
13574
|
+
*/
|
|
13575
|
+
defaultSelectedKeys?: | "all"
|
|
13576
|
+
| Iterable<Key>
|
|
13577
|
+
| Record<string, "all" | Iterable<Key>>
|
|
13578
|
+
/**
|
|
13579
|
+
* Fires on user selection. In per-section mode, the second argument carries
|
|
13580
|
+
* the \`sectionId\` of the section whose selection changed; it is \`undefined\`
|
|
13581
|
+
* in menu-wide mode. Typed optional so existing one-arg handlers stay
|
|
13582
|
+
* assignable.
|
|
13583
|
+
*/
|
|
13584
|
+
onSelectionChange?: (keys: Selection, meta?: { sectionId: string }) => void
|
|
13007
13585
|
placement?: any
|
|
13008
13586
|
}`,stories:{usage:[{id:"core-collections-menu--basic",name:"Basic",snippet:'const Basic = () => <Menu triggerLabel="Menu Trigger" items={items} />;'},{id:"core-collections-menu--with-sections",name:"With Sections",snippet:'const WithSections = () => <Menu triggerLabel="Menu Trigger" items={itemsWithSections} />;'},{id:"core-collections-menu--controlled-open",name:"Controlled Open",snippet:'const ControlledOpen = () => <Menu triggerLabel="Menu Trigger" items={items} isOpen />;'},{id:"core-collections-menu--disabled",name:"Disabled",snippet:'const Disabled = () => <Menu triggerLabel="Menu Trigger" items={items} isDisabled />;'},{id:"core-collections-menu--with-disabled-keys",name:"With Disabled Keys",snippet:'const WithDisabledKeys = () => <Menu triggerLabel="Menu Trigger" items={items} disabledKeys={["paste"]} />;'},{id:"core-collections-menu--single-selection",name:"Single Selection",snippet:`const SingleSelection = () => {
|
|
13009
13587
|
const [selectedKey, setSelectedKey] = React.useState(
|
|
@@ -13037,13 +13615,17 @@ placement?: any
|
|
|
13037
13615
|
};`},{id:"core-collections-menu--with-selected-keys-controlled",name:"With Selected Keys Controlled",snippet:`const WithSelectedKeysControlled = () => <Menu
|
|
13038
13616
|
triggerLabel="Menu Trigger"
|
|
13039
13617
|
items={items}
|
|
13040
|
-
selectedKeys={["light"]}
|
|
13618
|
+
selectedKeys={new Set(["light"])}
|
|
13041
13619
|
selectionMode="single" />;`},{id:"core-collections-menu--long-menu",name:"Long Menu",snippet:`const LongMenu = () => <Menu
|
|
13042
13620
|
triggerLabel="Menu Trigger"
|
|
13043
13621
|
items={longListItems}
|
|
13044
13622
|
selectionMode="single"
|
|
13045
13623
|
defaultOpen
|
|
13046
|
-
autoFocus="first" />;`},{id:"core-collections-menu--with-
|
|
13624
|
+
autoFocus="first" />;`},{id:"core-collections-menu--with-submenu",name:"With Submenu",snippet:'const WithSubmenu = () => <Menu triggerLabel="Menu Trigger" items={itemsWithSubmenu} defaultOpen />;'},{id:"core-collections-menu--with-icons",name:"With Icons",snippet:'const WithIcons = () => <Menu triggerLabel="Menu Trigger" items={itemsWithIcons} defaultOpen />;'},{id:"core-collections-menu--with-disabled-submenu",name:"With Disabled Submenu",snippet:`const WithDisabledSubmenu = () => <Menu
|
|
13625
|
+
triggerLabel="Menu Trigger"
|
|
13626
|
+
items={itemsWithSubmenu}
|
|
13627
|
+
disabledKeys={["share"]}
|
|
13628
|
+
defaultOpen />;`},{id:"core-collections-menu--with-long-labels",name:"With Long Labels",snippet:'const WithLongLabels = () => <Menu triggerLabel="Menu Trigger" items={longLabelItems} defaultOpen />;'},{id:"core-collections-menu--with-custom-trigger",name:"With Custom Trigger",snippet:`const WithCustomTrigger = () => <Menu
|
|
13047
13629
|
triggerLabel="Menu Trigger"
|
|
13048
13630
|
renderTrigger={({ buttonProps, ref }) => (
|
|
13049
13631
|
<ActionButton
|
|
@@ -13052,12 +13634,32 @@ placement?: any
|
|
|
13052
13634
|
size="sm"
|
|
13053
13635
|
ref={ref}
|
|
13054
13636
|
/>
|
|
13055
|
-
)} />;`}
|
|
13637
|
+
)} />;`},{id:"core-collections-menu--section-level-selection",name:"Section Level Selection",snippet:`const SectionLevelSelection = () => {
|
|
13638
|
+
const [sel, setSel] = React.useState<Record<string, Selection>>({
|
|
13639
|
+
edit: new Set<Key>(["copy"]),
|
|
13640
|
+
theme: new Set<Key>(["light", "dark"]),
|
|
13641
|
+
});
|
|
13642
|
+
|
|
13643
|
+
return (
|
|
13644
|
+
<Menu
|
|
13645
|
+
triggerLabel="Menu Trigger"
|
|
13646
|
+
items={itemsWithSections}
|
|
13647
|
+
isOpen
|
|
13648
|
+
selectionMode={{ edit: "single", theme: "multiple" }}
|
|
13649
|
+
selectedKeys={{ edit: sel.edit, theme: sel.theme }}
|
|
13650
|
+
onSelectionChange={(keys, meta) => {
|
|
13651
|
+
if (meta) setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
|
|
13652
|
+
}} />
|
|
13653
|
+
);
|
|
13654
|
+
};`}],implementation:`import React from "react";
|
|
13056
13655
|
|
|
13656
|
+
import { I18nProvider } from "../../I18nProvider/I18nProvider";
|
|
13057
13657
|
import { Menu } from "../Menu";
|
|
13058
|
-
import { items } from "./data";
|
|
13658
|
+
import { items, itemsWithSections, itemsWithSubmenu } from "./data";
|
|
13059
13659
|
|
|
13060
13660
|
import type { MenuProps } from "../Menu.types";
|
|
13661
|
+
import type { Key } from "react-aria";
|
|
13662
|
+
import type { Selection } from "react-stately";
|
|
13061
13663
|
|
|
13062
13664
|
export const MenuExample: React.FC<
|
|
13063
13665
|
MenuProps & {
|
|
@@ -13087,26 +13689,181 @@ export const MenuActionValueExample: React.FC<{ label: string }> = ({
|
|
|
13087
13689
|
<div data-testid="menu-action-value">{received}</div>
|
|
13088
13690
|
</>
|
|
13089
13691
|
);
|
|
13090
|
-
}
|
|
13692
|
+
};
|
|
13091
13693
|
|
|
13092
|
-
|
|
13093
|
-
|
|
13694
|
+
export const MenuSelectionExample: React.FC<{
|
|
13695
|
+
label: string;
|
|
13696
|
+
selectionMode: "single" | "multiple";
|
|
13697
|
+
}> = ({ label, selectionMode }) => {
|
|
13698
|
+
const [selected, setSelected] = React.useState<Selection>(new Set());
|
|
13699
|
+
return (
|
|
13700
|
+
<>
|
|
13701
|
+
<Menu
|
|
13702
|
+
items={items}
|
|
13703
|
+
triggerLabel={label}
|
|
13704
|
+
selectionMode={selectionMode}
|
|
13705
|
+
selectedKeys={selected}
|
|
13706
|
+
onSelectionChange={setSelected}
|
|
13707
|
+
/>
|
|
13708
|
+
<div data-testid="menu-selection">
|
|
13709
|
+
{selected === "all" ? "all" : [...selected].map(String).join(",")}
|
|
13710
|
+
</div>
|
|
13711
|
+
</>
|
|
13712
|
+
);
|
|
13713
|
+
};
|
|
13094
13714
|
|
|
13095
|
-
const
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13715
|
+
export const MenuOpenChangeExample: React.FC<{ label: string }> = ({
|
|
13716
|
+
label,
|
|
13717
|
+
}) => {
|
|
13718
|
+
const [log, setLog] = React.useState<string[]>([]);
|
|
13719
|
+
return (
|
|
13720
|
+
<>
|
|
13721
|
+
<Menu
|
|
13722
|
+
items={items}
|
|
13723
|
+
triggerLabel={label}
|
|
13724
|
+
onOpenChange={(isOpen) => {
|
|
13725
|
+
setLog((prev) => [...prev, String(isOpen)]);
|
|
13726
|
+
}}
|
|
13727
|
+
/>
|
|
13728
|
+
<div data-testid="menu-open-log">{log.join(",")}</div>
|
|
13729
|
+
</>
|
|
13730
|
+
);
|
|
13102
13731
|
};
|
|
13103
13732
|
|
|
13104
|
-
const
|
|
13105
|
-
|
|
13106
|
-
|
|
13733
|
+
export const MenuRtlSubmenuExample: React.FC<{ label: string }> = ({
|
|
13734
|
+
label,
|
|
13735
|
+
}) => (
|
|
13736
|
+
<I18nProvider locale="ar">
|
|
13737
|
+
<Menu items={itemsWithSubmenu} triggerLabel={label} />
|
|
13107
13738
|
</I18nProvider>
|
|
13108
13739
|
);
|
|
13109
|
-
|
|
13740
|
+
|
|
13741
|
+
export const MenuFunctionLabelExample: React.FC = () => (
|
|
13742
|
+
<Menu
|
|
13743
|
+
items={items}
|
|
13744
|
+
triggerLabel={(isOpen) => (isOpen ? "Opened" : "Closed")}
|
|
13745
|
+
/>
|
|
13746
|
+
);
|
|
13747
|
+
|
|
13748
|
+
export const MenuPerSectionTypeCheck: React.FC = () => (
|
|
13749
|
+
<Menu
|
|
13750
|
+
items={items}
|
|
13751
|
+
triggerLabel="types"
|
|
13752
|
+
selectionMode={{ a: "single", b: "multiple" }}
|
|
13753
|
+
selectedKeys={{ a: new Set<Key>(["x"]), b: new Set<Key>() }}
|
|
13754
|
+
onSelectionChange={(keys, meta) => {
|
|
13755
|
+
void keys;
|
|
13756
|
+
void meta?.sectionId;
|
|
13757
|
+
}}
|
|
13758
|
+
/>
|
|
13759
|
+
);
|
|
13760
|
+
|
|
13761
|
+
const fmtSelection = (sel: Selection): string =>
|
|
13762
|
+
sel === "all" ? "all" : [...sel].map(String).join(",");
|
|
13763
|
+
|
|
13764
|
+
export const MenuSectionSelectionExample: React.FC<{
|
|
13765
|
+
label: string;
|
|
13766
|
+
disallowEmptySelection?: boolean;
|
|
13767
|
+
}> = ({ label, disallowEmptySelection }) => {
|
|
13768
|
+
const [sel, setSel] = React.useState<Record<string, Selection>>({
|
|
13769
|
+
edit: new Set<Key>(),
|
|
13770
|
+
theme: new Set<Key>(["light"]),
|
|
13771
|
+
});
|
|
13772
|
+
const [lastSection, setLastSection] = React.useState("");
|
|
13773
|
+
return (
|
|
13774
|
+
<>
|
|
13775
|
+
<Menu
|
|
13776
|
+
items={itemsWithSections}
|
|
13777
|
+
triggerLabel={label}
|
|
13778
|
+
selectionMode={{ edit: "single", theme: "multiple" }}
|
|
13779
|
+
selectedKeys={{ edit: sel.edit, theme: sel.theme }}
|
|
13780
|
+
disallowEmptySelection={disallowEmptySelection}
|
|
13781
|
+
onSelectionChange={(keys, meta) => {
|
|
13782
|
+
if (!meta) return;
|
|
13783
|
+
setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
|
|
13784
|
+
setLastSection(meta.sectionId);
|
|
13785
|
+
}}
|
|
13786
|
+
/>
|
|
13787
|
+
<div data-testid="menu-section-selection-edit">
|
|
13788
|
+
{fmtSelection(sel.edit)}
|
|
13789
|
+
</div>
|
|
13790
|
+
<div data-testid="menu-section-selection-theme">
|
|
13791
|
+
{fmtSelection(sel.theme)}
|
|
13792
|
+
</div>
|
|
13793
|
+
<div data-testid="menu-section-selection-lastsection">{lastSection}</div>
|
|
13794
|
+
</>
|
|
13795
|
+
);
|
|
13796
|
+
};
|
|
13797
|
+
|
|
13798
|
+
export const MenuDisabledSectionSelectionExample: React.FC = () => (
|
|
13799
|
+
<Menu
|
|
13800
|
+
items={itemsWithSections}
|
|
13801
|
+
triggerLabel="Menu"
|
|
13802
|
+
selectionMode={{ edit: "single" }}
|
|
13803
|
+
disabledKeys={["copy"]}
|
|
13804
|
+
/>
|
|
13805
|
+
);
|
|
13806
|
+
|
|
13807
|
+
export const MenuOneShotDisabledKeysSectionSelectionExample: React.FC = () => {
|
|
13808
|
+
const disabledKeys = ["copy"][Symbol.iterator]();
|
|
13809
|
+
|
|
13810
|
+
return (
|
|
13811
|
+
<Menu
|
|
13812
|
+
items={itemsWithSections}
|
|
13813
|
+
triggerLabel="Menu"
|
|
13814
|
+
selectionMode={{ edit: "single" }}
|
|
13815
|
+
disabledKeys={disabledKeys}
|
|
13816
|
+
/>
|
|
13817
|
+
);
|
|
13818
|
+
};
|
|
13819
|
+
|
|
13820
|
+
export function MenuUncontrolledSectionSelectionExample() {
|
|
13821
|
+
const [lastSection, setLastSection] = React.useState("");
|
|
13822
|
+
const [themeKeys, setThemeKeys] = React.useState("");
|
|
13823
|
+
return (
|
|
13824
|
+
<>
|
|
13825
|
+
<Menu
|
|
13826
|
+
triggerLabel="Options"
|
|
13827
|
+
items={itemsWithSections}
|
|
13828
|
+
selectionMode={{ theme: "multiple" }}
|
|
13829
|
+
defaultSelectedKeys={{ theme: new Set<Key>(["light"]) }}
|
|
13830
|
+
onSelectionChange={(keys, meta) => {
|
|
13831
|
+
if (!meta) return;
|
|
13832
|
+
setLastSection(meta.sectionId);
|
|
13833
|
+
setThemeKeys(fmtSelection(keys));
|
|
13834
|
+
}}
|
|
13835
|
+
/>
|
|
13836
|
+
<div data-testid="menu-uncontrolled-lastsection">{lastSection}</div>
|
|
13837
|
+
<div data-testid="menu-uncontrolled-keys">{themeKeys}</div>
|
|
13838
|
+
</>
|
|
13839
|
+
);
|
|
13840
|
+
}
|
|
13841
|
+
|
|
13842
|
+
export const MenuPartialSectionSelectionExample: React.FC<{
|
|
13843
|
+
label: string;
|
|
13844
|
+
}> = ({ label }) => {
|
|
13845
|
+
const [sel, setSel] = React.useState<Record<string, Selection>>({
|
|
13846
|
+
edit: new Set<Key>(),
|
|
13847
|
+
});
|
|
13848
|
+
const [action, setAction] = React.useState("");
|
|
13849
|
+
return (
|
|
13850
|
+
<>
|
|
13851
|
+
<Menu
|
|
13852
|
+
items={itemsWithSections}
|
|
13853
|
+
triggerLabel={label}
|
|
13854
|
+
selectionMode={{ edit: "single" }}
|
|
13855
|
+
selectedKeys={{ edit: sel.edit }}
|
|
13856
|
+
onSelectionChange={(keys, meta) => {
|
|
13857
|
+
if (meta) setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
|
|
13858
|
+
}}
|
|
13859
|
+
onAction={(key) => {
|
|
13860
|
+
setAction(String(key));
|
|
13861
|
+
}}
|
|
13862
|
+
/>
|
|
13863
|
+
<div data-testid="menu-partial-action">{action}</div>
|
|
13864
|
+
</>
|
|
13865
|
+
);
|
|
13866
|
+
};`},similarTo:[],figmaUrl:null},MessageFormat:{id:"core-utilities-messageformat",breadcrumb:"Core/Utilities/MessageFormat",importStatement:'import { MessageFormat } from "@baseline-ui/core";',description:"`MessageFormat` renders a single message from the catalog supplied to `I18nProvider`. When the catalog has no entry for `id`, it renders `defaultMessage`.",documentation:'`MessageFormat` renders a single message from the catalog supplied to `I18nProvider`. When the catalog has no entry for `id`, it renders `defaultMessage`.\n\n```jsx\nimport { I18nProvider, MessageFormat } from "@baseline-ui/core";\nimport en from "@baseline-ui/core/intl/en.json";\nimport fr from "@baseline-ui/core/intl/fr.json";\n\nconst App = () => (\n <I18nProvider locale="en" messages={{ en, fr }}>\n <MessageFormat id="bui.calendar.previousMonth" />\n <MessageFormat id="myApp.hello" defaultMessage="Hello!" />\n </I18nProvider>\n);\n```',props:`interface MessageFormatProps {
|
|
13110
13867
|
/**
|
|
13111
13868
|
* By default \`<MessageFormat>\` will render the formatted string into a
|
|
13112
13869
|
* \`<React.Fragment>\`. If you need to customize rendering, you can either wrap
|
|
@@ -13122,7 +13879,7 @@ id: string
|
|
|
13122
13879
|
* The default message to use if the message id is not found.
|
|
13123
13880
|
*/
|
|
13124
13881
|
defaultMessage?: string
|
|
13125
|
-
}`,stories:{usage:[{id:"core-utilities-messageformat--basic",name:"Basic",snippet:'const Basic = () => <MessageFormat id="addSignature" elementType={Text} />;'}],implementation:""},similarTo:[],figmaUrl:null},Modal:{id:"core-overlays-modal",breadcrumb:"Core/Overlays/Modal",importStatement:'import { DialogExample, Modal } from "@baseline-ui/core";',description:"`Modal` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.",documentation:`\`Modal\` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.
|
|
13882
|
+
}`,stories:{usage:[{id:"core-utilities-messageformat--basic",name:"Basic",snippet:'const Basic = () => <MessageFormat id="myApp.addSignature" defaultMessage="Add signature" elementType={Text} />;'}],implementation:""},similarTo:[],figmaUrl:null},Modal:{id:"core-overlays-modal",breadcrumb:"Core/Overlays/Modal",importStatement:'import { DialogExample, Modal } from "@baseline-ui/core";',description:"`Modal` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.",documentation:`\`Modal\` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.
|
|
13126
13883
|
|
|
13127
13884
|
* The content outside the modal is hidden from screen readers.
|
|
13128
13885
|
* The modal can optionally be closed by clicking outside the modal or by pressing the <kbd>Esc</kbd> key.
|
|
@@ -16207,25 +16964,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
|
|
|
16207
16964
|
);
|
|
16208
16965
|
};
|
|
16209
16966
|
|
|
16210
|
-
export const PopoverContainedFocusExample = (
|
|
16211
|
-
|
|
16212
|
-
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
<Dialog
|
|
16218
|
-
size="content"
|
|
16219
|
-
className={sprinkles({
|
|
16220
|
-
padding: "md",
|
|
16221
|
-
display: "flex",
|
|
16222
|
-
gap: "xl",
|
|
16223
|
-
flexDirection: "column",
|
|
16224
|
-
})}
|
|
16225
|
-
style={{
|
|
16226
|
-
width: 200,
|
|
16227
|
-
}}
|
|
16228
|
-
>
|
|
16967
|
+
export const PopoverContainedFocusExample = ({
|
|
16968
|
+
shouldContainFocus = true,
|
|
16969
|
+
}: {
|
|
16970
|
+
shouldContainFocus?: boolean;
|
|
16971
|
+
}) => {
|
|
16972
|
+
const content = (
|
|
16973
|
+
<>
|
|
16229
16974
|
<Text type="label">The focus is contained within the popover.</Text>
|
|
16230
16975
|
|
|
16231
16976
|
<TextInput
|
|
@@ -16237,10 +16982,55 @@ export const PopoverContainedFocusExample = () => {
|
|
|
16237
16982
|
label="Button"
|
|
16238
16983
|
style={{ width: "100%", justifyContent: "center" }}
|
|
16239
16984
|
/>
|
|
16985
|
+
</>
|
|
16986
|
+
);
|
|
16987
|
+
const contentClassName = sprinkles({
|
|
16988
|
+
padding: "md",
|
|
16989
|
+
display: "flex",
|
|
16990
|
+
gap: "xl",
|
|
16991
|
+
flexDirection: "column",
|
|
16992
|
+
});
|
|
16993
|
+
|
|
16994
|
+
const popover = (
|
|
16995
|
+
<Popover type="dialog">
|
|
16996
|
+
<PopoverTrigger>
|
|
16997
|
+
<ActionButton label="Open" />
|
|
16998
|
+
</PopoverTrigger>
|
|
16999
|
+
<PopoverContent
|
|
17000
|
+
shouldContainFocus={shouldContainFocus}
|
|
17001
|
+
isNonModal={!shouldContainFocus}
|
|
17002
|
+
>
|
|
17003
|
+
{shouldContainFocus ? (
|
|
17004
|
+
<Dialog
|
|
17005
|
+
size="content"
|
|
17006
|
+
className={contentClassName}
|
|
17007
|
+
style={{ width: 200 }}
|
|
17008
|
+
>
|
|
17009
|
+
{content}
|
|
16240
17010
|
</Dialog>
|
|
17011
|
+
) : (
|
|
17012
|
+
// No Dialog: useDialog opts the overlay into focus containment,
|
|
17013
|
+
// which disables the Tab-out restore path.
|
|
17014
|
+
<Box className={contentClassName} style={{ width: 200 }}>
|
|
17015
|
+
{content}
|
|
17016
|
+
</Box>
|
|
17017
|
+
)}
|
|
16241
17018
|
</PopoverContent>
|
|
16242
17019
|
</Popover>
|
|
16243
17020
|
);
|
|
17021
|
+
|
|
17022
|
+
// The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
|
|
17023
|
+
// here rebaselines its visual snapshot.
|
|
17024
|
+
if (shouldContainFocus) {
|
|
17025
|
+
return popover;
|
|
17026
|
+
}
|
|
17027
|
+
|
|
17028
|
+
return (
|
|
17029
|
+
<Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
|
|
17030
|
+
{popover}
|
|
17031
|
+
<ActionButton label="After" />
|
|
17032
|
+
</Box>
|
|
17033
|
+
);
|
|
16244
17034
|
};
|
|
16245
17035
|
|
|
16246
17036
|
export const PopoverWithScrollableViewportExample: React.FC<{
|
|
@@ -18318,18 +19108,20 @@ function App() {
|
|
|
18318
19108
|
}
|
|
18319
19109
|
\`\`\`
|
|
18320
19110
|
|
|
18321
|
-
We can group items into sections by passing the \`items\` prop an array of objects
|
|
19111
|
+
We can group items into sections by passing the \`items\` prop an array of section objects. A section is any item with both a \`title\` and a \`children\` array of options \u2014 items missing either one are treated as plain options. The \`id\` of each section must be unique.
|
|
19112
|
+
|
|
19113
|
+
Sections are visually separated by a divider above every section but the first.
|
|
18322
19114
|
|
|
18323
19115
|
\`\`\`jsx
|
|
18324
19116
|
const itemsWithSections = [
|
|
18325
19117
|
{
|
|
18326
19118
|
id: "solid",
|
|
18327
|
-
|
|
19119
|
+
title: "Solid",
|
|
18328
19120
|
children: items,
|
|
18329
19121
|
},
|
|
18330
19122
|
{
|
|
18331
19123
|
id: "dashed",
|
|
18332
|
-
|
|
19124
|
+
title: "Dashed",
|
|
18333
19125
|
children: [
|
|
18334
19126
|
{
|
|
18335
19127
|
id: "ellipse-dashed",
|
|
@@ -18353,6 +19145,18 @@ const itemsWithSections = [
|
|
|
18353
19145
|
<Select items={itemsWithSections} label="Label" />;
|
|
18354
19146
|
\`\`\`
|
|
18355
19147
|
|
|
19148
|
+
By default each section's \`title\` is used only as the group's accessible name. Set \`showSectionHeader\` to render it as a visible heading above the section's options. This works the same way when the list is virtualized with \`Virtualizer\`.
|
|
19149
|
+
|
|
19150
|
+
<a href="?path=/story/core-forms-select--with-section-headers">View story</a>
|
|
19151
|
+
|
|
19152
|
+
\`\`\`jsx
|
|
19153
|
+
<Select
|
|
19154
|
+
items={itemsWithSections}
|
|
19155
|
+
showSectionHeader={true}
|
|
19156
|
+
aria-label="Choose an item"
|
|
19157
|
+
/>
|
|
19158
|
+
\`\`\`
|
|
19159
|
+
|
|
18356
19160
|
The \`Select\` component supports selecting multiple options by setting the \`selectionMode\` prop to \`"multiple"\`. When in multiselect mode, selected items are displayed as removable tags in the button, the popover stays open after selection, and checkboxes are shown instead of checkmarks.
|
|
18357
19161
|
|
|
18358
19162
|
\`\`\`jsx
|
|
@@ -18556,14 +19360,16 @@ import { Virtualizer, ListLayout } from "@baseline-ui/core";
|
|
|
18556
19360
|
\`\`\`
|
|
18557
19361
|
|
|
18558
19362
|
| Key | Function |
|
|
18559
|
-
|
|
|
19363
|
+
| -------------------- | ------------------------------------------------------------------------------------ |
|
|
19364
|
+
| \`Tab\` | Moves focus to and from the trigger. The popup itself is not a tab stop |
|
|
18560
19365
|
| \`Space\` | Opens the listbox popup or toggles selection of the focused item in multiselect mode |
|
|
18561
19366
|
| \`Enter\` | Opens the listbox popup or selects the focused item if the popup is open |
|
|
18562
19367
|
| \`Escape\` | Closes the listbox popup |
|
|
18563
19368
|
| \`ArrowDown\` | Opens the listbox popup and focuses the first item if no item is focused |
|
|
18564
19369
|
| \`ArrowUp\` | Opens the listbox popup and focuses the last item if no item is focused |
|
|
18565
|
-
| \`Home\` |
|
|
18566
|
-
| \`End\` |
|
|
19370
|
+
| \`Home\` | Focuses the first item |
|
|
19371
|
+
| \`End\` | Focuses the last item |
|
|
19372
|
+
| Printable characters | Focuses the first item matching the typed characters |
|
|
18567
19373
|
|
|
18568
19374
|
**Note:** In multiselect mode (\`selectionMode="multiple"\`), the \`Space\` key toggles selection of the focused item, and the popover remains open after selection.
|
|
18569
19375
|
|
|
@@ -18593,6 +19399,8 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
|
|
|
18593
19399
|
| \`.BaselineUI-Select-Label\` | Label element |
|
|
18594
19400
|
| \`.BaselineUI-Select-Popover\` | Popover container |
|
|
18595
19401
|
| \`.BaselineUI-Select-SearchInput\` | Search input (when wrapped with \`Autocomplete\`) |
|
|
19402
|
+
| \`.BaselineUI-ListBox\` | Listbox inside the popover |
|
|
19403
|
+
| \`.BaselineUI-ListBox-Section\` | A section within the listbox |
|
|
18596
19404
|
| \`[data-disabled]\` | Applied when \`isDisabled\` is true |
|
|
18597
19405
|
| \`[data-readonly]\` | Applied when \`isReadOnly\` is true |
|
|
18598
19406
|
| \`[data-focused]\` | Applied when the trigger is focused |
|
|
@@ -18601,6 +19409,7 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
|
|
|
18601
19409
|
| \`[data-pressed]\` | Applied when the trigger is pressed |
|
|
18602
19410
|
| \`[data-open]\` | Applied when the popover is open |
|
|
18603
19411
|
|
|
19412
|
+
* **ComboBox** \u2014 Use this instead when users should be able to type directly into the trigger to filter or enter a value
|
|
18604
19413
|
* **IconSelect** \u2014 Select variant with an icon trigger instead of a text button
|
|
18605
19414
|
* **ButtonSelect** \u2014 Select variant styled as a button
|
|
18606
19415
|
* **ListBox** \u2014 Standalone listbox without the trigger/popover wrapper
|
|
@@ -18743,125 +19552,125 @@ hideSelectAll?: boolean
|
|
|
18743
19552
|
*/
|
|
18744
19553
|
hideClear?: boolean
|
|
18745
19554
|
}`,stories:{usage:[{id:"core-forms-select-multiselect--basic",name:"Basic",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
18746
|
-
|
|
18747
|
-
|
|
18748
|
-
>
|
|
19555
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19556
|
+
33 |
|
|
19557
|
+
> 34 | export {
|
|
18749
19558
|
| ^
|
|
18750
|
-
|
|
18751
|
-
|
|
18752
|
-
|
|
18753
|
-
|
|
18754
|
-
|
|
18755
|
-
>
|
|
19559
|
+
35 | Basic,
|
|
19560
|
+
36 | WithLabel,
|
|
19561
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-label",name:"WithLabel",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19562
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19563
|
+
33 |
|
|
19564
|
+
> 34 | export {
|
|
18756
19565
|
| ^
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
>
|
|
19566
|
+
35 | Basic,
|
|
19567
|
+
36 | WithLabel,
|
|
19568
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-description",name:"WithDescription",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19569
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19570
|
+
33 |
|
|
19571
|
+
> 34 | export {
|
|
18763
19572
|
| ^
|
|
18764
|
-
|
|
18765
|
-
|
|
18766
|
-
|
|
18767
|
-
|
|
18768
|
-
|
|
18769
|
-
>
|
|
19573
|
+
35 | Basic,
|
|
19574
|
+
36 | WithLabel,
|
|
19575
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-error",name:"WithError",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19576
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19577
|
+
33 |
|
|
19578
|
+
> 34 | export {
|
|
18770
19579
|
| ^
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
>
|
|
19580
|
+
35 | Basic,
|
|
19581
|
+
36 | WithLabel,
|
|
19582
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-error-and-error-message",name:"WithErrorAndErrorMessage",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19583
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19584
|
+
33 |
|
|
19585
|
+
> 34 | export {
|
|
18777
19586
|
| ^
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
>
|
|
19587
|
+
35 | Basic,
|
|
19588
|
+
36 | WithLabel,
|
|
19589
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-warning",name:"WithWarning",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19590
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19591
|
+
33 |
|
|
19592
|
+
> 34 | export {
|
|
18784
19593
|
| ^
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
|
|
18789
|
-
|
|
18790
|
-
>
|
|
19594
|
+
35 | Basic,
|
|
19595
|
+
36 | WithLabel,
|
|
19596
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-warning-and-warning-message",name:"WithWarningAndWarningMessage",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19597
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19598
|
+
33 |
|
|
19599
|
+
> 34 | export {
|
|
18791
19600
|
| ^
|
|
18792
|
-
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
>
|
|
19601
|
+
35 | Basic,
|
|
19602
|
+
36 | WithLabel,
|
|
19603
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--ghost",name:"Ghost",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19604
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19605
|
+
33 |
|
|
19606
|
+
> 34 | export {
|
|
18798
19607
|
| ^
|
|
18799
|
-
|
|
18800
|
-
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
>
|
|
19608
|
+
35 | Basic,
|
|
19609
|
+
36 | WithLabel,
|
|
19610
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled",name:"WithDisabled",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19611
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19612
|
+
33 |
|
|
19613
|
+
> 34 | export {
|
|
18805
19614
|
| ^
|
|
18806
|
-
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
|
|
18811
|
-
>
|
|
19615
|
+
35 | Basic,
|
|
19616
|
+
36 | WithLabel,
|
|
19617
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled-items",name:"WithDisabledItems",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19618
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19619
|
+
33 |
|
|
19620
|
+
> 34 | export {
|
|
18812
19621
|
| ^
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18818
|
-
>
|
|
19622
|
+
35 | Basic,
|
|
19623
|
+
36 | WithLabel,
|
|
19624
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-sections",name:"WithSections",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19625
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19626
|
+
33 |
|
|
19627
|
+
> 34 | export {
|
|
18819
19628
|
| ^
|
|
18820
|
-
|
|
18821
|
-
|
|
18822
|
-
|
|
18823
|
-
|
|
18824
|
-
|
|
18825
|
-
>
|
|
19629
|
+
35 | Basic,
|
|
19630
|
+
36 | WithLabel,
|
|
19631
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-default-open",name:"WithDefaultOpen",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19632
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19633
|
+
33 |
|
|
19634
|
+
> 34 | export {
|
|
18826
19635
|
| ^
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
|
|
18831
|
-
|
|
18832
|
-
>
|
|
19636
|
+
35 | Basic,
|
|
19637
|
+
36 | WithLabel,
|
|
19638
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-controlled-open",name:"WithControlledOpen",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19639
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19640
|
+
33 |
|
|
19641
|
+
> 34 | export {
|
|
18833
19642
|
| ^
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
18837
|
-
|
|
18838
|
-
|
|
18839
|
-
>
|
|
19643
|
+
35 | Basic,
|
|
19644
|
+
36 | WithLabel,
|
|
19645
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-label-at-start",name:"WithLabelAtStart",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19646
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19647
|
+
33 |
|
|
19648
|
+
> 34 | export {
|
|
18840
19649
|
| ^
|
|
18841
|
-
|
|
18842
|
-
|
|
18843
|
-
|
|
18844
|
-
|
|
18845
|
-
|
|
18846
|
-
>
|
|
19650
|
+
35 | Basic,
|
|
19651
|
+
36 | WithLabel,
|
|
19652
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--scrollable",name:"Scrollable",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19653
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19654
|
+
33 |
|
|
19655
|
+
> 34 | export {
|
|
18847
19656
|
| ^
|
|
18848
|
-
|
|
18849
|
-
|
|
18850
|
-
|
|
18851
|
-
|
|
18852
|
-
|
|
18853
|
-
>
|
|
19657
|
+
35 | Basic,
|
|
19658
|
+
36 | WithLabel,
|
|
19659
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--long-list",name:"LongList",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19660
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19661
|
+
33 |
|
|
19662
|
+
> 34 | export {
|
|
18854
19663
|
| ^
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
18860
|
-
>
|
|
19664
|
+
35 | Basic,
|
|
19665
|
+
36 | WithLabel,
|
|
19666
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-custom-trigger",name:"WithCustomTrigger",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
|
|
19667
|
+
32 | type Story = StoryObj<typeof meta>;
|
|
19668
|
+
33 |
|
|
19669
|
+
> 34 | export {
|
|
18861
19670
|
| ^
|
|
18862
|
-
|
|
18863
|
-
|
|
18864
|
-
|
|
19671
|
+
35 | Basic,
|
|
19672
|
+
36 | WithLabel,
|
|
19673
|
+
37 | WithDescription,`}},{id:"core-forms-select-multiselect--read-only",name:"Read Only",snippet:`const ReadOnly = () => <Select
|
|
18865
19674
|
aria-label="Choose Stroke Style"
|
|
18866
19675
|
items={items}
|
|
18867
19676
|
placeholder="Choose Stroke Style"
|
|
@@ -18920,7 +19729,29 @@ hideClear?: boolean
|
|
|
18920
19729
|
aria-label="Choose Stroke Style"
|
|
18921
19730
|
items={items}
|
|
18922
19731
|
placeholder="Choose Stroke Style"
|
|
18923
|
-
selectionMode="multiple" />;`}
|
|
19732
|
+
selectionMode="multiple" />;`},{id:"core-forms-select-multiselect--with-error-and-tags",name:"With Error And Tags",snippet:`const WithErrorAndTags = () => <Select
|
|
19733
|
+
aria-label="Choose Stroke Style"
|
|
19734
|
+
items={items}
|
|
19735
|
+
placeholder="Choose Stroke Style"
|
|
19736
|
+
selectionMode="multiple"
|
|
19737
|
+
label="Label"
|
|
19738
|
+
validationState="error"
|
|
19739
|
+
errorMessage="Pick fewer shapes"
|
|
19740
|
+
defaultValue={["ellipse", "square", "polygon"]}
|
|
19741
|
+
maxCount={3} />;`,description:"Tags competing with the status icon for trigger width."},{id:"core-forms-select-multiselect--long-labels-as-tags",name:"Long Labels As Tags",snippet:`const LongLabelsAsTags = () => <Select
|
|
19742
|
+
aria-label="Choose Stroke Style"
|
|
19743
|
+
items={items}
|
|
19744
|
+
placeholder="Choose Stroke Style"
|
|
19745
|
+
selectionMode="multiple"
|
|
19746
|
+
defaultValue={["long1", "long2"]} />;`},{id:"core-forms-select-multiselect--read-only-with-tags-open",name:"Read Only With Tags Open",snippet:`const ReadOnlyWithTagsOpen = () => <Select
|
|
19747
|
+
aria-label="Choose Stroke Style"
|
|
19748
|
+
items={items}
|
|
19749
|
+
placeholder="Choose Stroke Style"
|
|
19750
|
+
selectionMode="multiple"
|
|
19751
|
+
label="Label"
|
|
19752
|
+
isReadOnly
|
|
19753
|
+
value={["ellipse", "square"]}
|
|
19754
|
+
defaultOpen />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
|
|
18924
19755
|
import React from "react";
|
|
18925
19756
|
import { useFilter } from "react-aria";
|
|
18926
19757
|
import { Autocomplete, Virtualizer } from "react-aria-components";
|
|
@@ -18929,11 +19760,16 @@ import { useListData } from "react-stately";
|
|
|
18929
19760
|
import { ActionButton } from "../../ActionButton";
|
|
18930
19761
|
import { items } from "../../ListBox/__tests__/testComponents";
|
|
18931
19762
|
import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
|
|
18932
|
-
import {
|
|
19763
|
+
import { itemsWithSectionTitles } from "../../UNSAFE_ListBox/__tests__/testComponents";
|
|
19764
|
+
import {
|
|
19765
|
+
ListLayout,
|
|
19766
|
+
VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS,
|
|
19767
|
+
} from "../../Virtualizer";
|
|
18933
19768
|
import { IconSelect } from "../IconSelect";
|
|
18934
19769
|
import { Select } from "../Select";
|
|
18935
19770
|
|
|
18936
19771
|
import type { IconSelectProps } from "../Select.types";
|
|
19772
|
+
import type { Key } from "@react-types/shared";
|
|
18937
19773
|
|
|
18938
19774
|
export const SelectExample: React.FC<
|
|
18939
19775
|
Omit<React.ComponentProps<typeof Select>, "items">
|
|
@@ -18941,13 +19777,28 @@ export const SelectExample: React.FC<
|
|
|
18941
19777
|
return (
|
|
18942
19778
|
<Select
|
|
18943
19779
|
placeholder="Choose an item"
|
|
18944
|
-
{...args}
|
|
18945
19780
|
optionClassName={(item) => item.label}
|
|
19781
|
+
{...args}
|
|
18946
19782
|
items={items}
|
|
18947
19783
|
/>
|
|
18948
19784
|
);
|
|
18949
19785
|
};
|
|
18950
19786
|
|
|
19787
|
+
// "Apples" is a substring of "Pineapples" \u2014 exercises exact string matching
|
|
19788
|
+
// in the tester's option() locator.
|
|
19789
|
+
const substringItems = [
|
|
19790
|
+
{ id: "apples", label: "Apples" },
|
|
19791
|
+
{ id: "pineapples", label: "Pineapples" },
|
|
19792
|
+
];
|
|
19793
|
+
|
|
19794
|
+
export const SelectSubstringLabelsExample: React.FC<
|
|
19795
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19796
|
+
> = (args) => {
|
|
19797
|
+
return (
|
|
19798
|
+
<Select placeholder="Choose an item" {...args} items={substringItems} />
|
|
19799
|
+
);
|
|
19800
|
+
};
|
|
19801
|
+
|
|
18951
19802
|
export const SelectGetTargetRectExample: React.FC = () => {
|
|
18952
19803
|
const [called, setCalled] = React.useState(false);
|
|
18953
19804
|
|
|
@@ -19004,19 +19855,136 @@ export const SelectCustomTriggerExample: React.FC<
|
|
|
19004
19855
|
};
|
|
19005
19856
|
|
|
19006
19857
|
export const IconSelectExample: React.FC<
|
|
19007
|
-
Omit<IconSelectProps, "items" | "
|
|
19858
|
+
Omit<IconSelectProps, "items" | "aria-label">
|
|
19008
19859
|
> = (args) => {
|
|
19009
19860
|
return (
|
|
19010
19861
|
<IconSelect
|
|
19011
19862
|
placeholder="Choose an item"
|
|
19863
|
+
icon={EllipseIcon}
|
|
19012
19864
|
{...args}
|
|
19013
19865
|
items={items}
|
|
19014
|
-
icon={EllipseIcon}
|
|
19015
19866
|
aria-label="Aria Label"
|
|
19016
19867
|
/>
|
|
19017
19868
|
);
|
|
19018
19869
|
};
|
|
19019
19870
|
|
|
19871
|
+
export const VirtualSelectWithSectionHeadersExample: React.FC<
|
|
19872
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19873
|
+
> = (args) => {
|
|
19874
|
+
return (
|
|
19875
|
+
<Virtualizer
|
|
19876
|
+
layout={ListLayout}
|
|
19877
|
+
layoutOptions={{
|
|
19878
|
+
rowHeight: 36,
|
|
19879
|
+
headingHeight: 40,
|
|
19880
|
+
}}
|
|
19881
|
+
>
|
|
19882
|
+
<Select
|
|
19883
|
+
aria-label="Choose an item"
|
|
19884
|
+
placeholder="Choose an item"
|
|
19885
|
+
showSectionHeader={true}
|
|
19886
|
+
{...args}
|
|
19887
|
+
items={itemsWithSectionTitles}
|
|
19888
|
+
defaultOpen={true}
|
|
19889
|
+
maxHeight={400}
|
|
19890
|
+
/>
|
|
19891
|
+
</Virtualizer>
|
|
19892
|
+
);
|
|
19893
|
+
};
|
|
19894
|
+
|
|
19895
|
+
export const SelectWithSectionHeadersExample: React.FC<
|
|
19896
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19897
|
+
> = (args) => {
|
|
19898
|
+
return (
|
|
19899
|
+
<Select
|
|
19900
|
+
aria-label="Choose an item"
|
|
19901
|
+
placeholder="Choose an item"
|
|
19902
|
+
showSectionHeader={true}
|
|
19903
|
+
{...args}
|
|
19904
|
+
items={itemsWithSectionTitles}
|
|
19905
|
+
defaultOpen={true}
|
|
19906
|
+
/>
|
|
19907
|
+
);
|
|
19908
|
+
};
|
|
19909
|
+
|
|
19910
|
+
/**
|
|
19911
|
+
* Owns \`isOpen\` so the controlled-open contract can be driven from outside the
|
|
19912
|
+
* Select, with a sibling readout of the current state.
|
|
19913
|
+
*/
|
|
19914
|
+
export const ControlledOpenSelectExample: React.FC<
|
|
19915
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19916
|
+
> = (args) => {
|
|
19917
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
19918
|
+
|
|
19919
|
+
return (
|
|
19920
|
+
<>
|
|
19921
|
+
<Select
|
|
19922
|
+
aria-label="Choose an item"
|
|
19923
|
+
placeholder="Choose an item"
|
|
19924
|
+
{...args}
|
|
19925
|
+
items={items}
|
|
19926
|
+
isOpen={isOpen}
|
|
19927
|
+
onOpenChange={setIsOpen}
|
|
19928
|
+
/>
|
|
19929
|
+
<button
|
|
19930
|
+
data-testid="open-externally"
|
|
19931
|
+
onClick={() => {
|
|
19932
|
+
setIsOpen(true);
|
|
19933
|
+
}}
|
|
19934
|
+
>
|
|
19935
|
+
Open
|
|
19936
|
+
</button>
|
|
19937
|
+
<span data-testid="open-state">{String(isOpen)}</span>
|
|
19938
|
+
</>
|
|
19939
|
+
);
|
|
19940
|
+
};
|
|
19941
|
+
|
|
19942
|
+
/**
|
|
19943
|
+
* Custom trigger that renders the multi-select half of the \`renderTrigger\`
|
|
19944
|
+
* payload \u2014 \`selectionMode\`, \`maxCount\` and \`onRemove\` \u2014 which the default
|
|
19945
|
+
* \`SelectButton\` otherwise keeps to itself.
|
|
19946
|
+
*/
|
|
19947
|
+
export const MultiSelectCustomTriggerExample: React.FC<
|
|
19948
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19949
|
+
> = (args) => {
|
|
19950
|
+
return (
|
|
19951
|
+
<Select
|
|
19952
|
+
aria-label="Choose an item"
|
|
19953
|
+
selectionMode="multiple"
|
|
19954
|
+
{...args}
|
|
19955
|
+
items={items}
|
|
19956
|
+
renderTrigger={({
|
|
19957
|
+
buttonProps,
|
|
19958
|
+
ref,
|
|
19959
|
+
selectedValue,
|
|
19960
|
+
selectionMode,
|
|
19961
|
+
onRemove,
|
|
19962
|
+
maxCount,
|
|
19963
|
+
}) => (
|
|
19964
|
+
<div>
|
|
19965
|
+
<ActionButton
|
|
19966
|
+
{...buttonProps}
|
|
19967
|
+
ref={ref}
|
|
19968
|
+
label={\`\${selectionMode}|\${maxCount}|\${selectedValue?.length ?? 0}\`}
|
|
19969
|
+
variant="popover"
|
|
19970
|
+
/>
|
|
19971
|
+
<button
|
|
19972
|
+
data-testid="remove-first"
|
|
19973
|
+
onClick={() => {
|
|
19974
|
+
const first = selectedValue?.[0];
|
|
19975
|
+
if (first) {
|
|
19976
|
+
onRemove?.(new Set([first.id]));
|
|
19977
|
+
}
|
|
19978
|
+
}}
|
|
19979
|
+
>
|
|
19980
|
+
Remove first
|
|
19981
|
+
</button>
|
|
19982
|
+
</div>
|
|
19983
|
+
)}
|
|
19984
|
+
/>
|
|
19985
|
+
);
|
|
19986
|
+
};
|
|
19987
|
+
|
|
19020
19988
|
export const SelectWithVirtualizeAutocompleteExample: React.FC<
|
|
19021
19989
|
Omit<React.ComponentProps<typeof Select>, "items">
|
|
19022
19990
|
> = (args) => {
|
|
@@ -19040,6 +20008,27 @@ export const SelectWithVirtualizeAutocompleteExample: React.FC<
|
|
|
19040
20008
|
</Autocomplete>
|
|
19041
20009
|
</Virtualizer>
|
|
19042
20010
|
);
|
|
20011
|
+
};
|
|
20012
|
+
|
|
20013
|
+
/**
|
|
20014
|
+
* \`optionClassName\` reads component state, and \`items\` is module-level so React Aria's
|
|
20015
|
+
* per-item element cache applies. Pinned open so the state change comes from selecting an
|
|
20016
|
+
* option rather than from an outside press the popover would swallow.
|
|
20017
|
+
*/
|
|
20018
|
+
export const CountingSelectExample: React.FC = () => {
|
|
20019
|
+
const [selected, setSelected] = React.useState<Key | null>(null);
|
|
20020
|
+
|
|
20021
|
+
return (
|
|
20022
|
+
<Select
|
|
20023
|
+
aria-label="Counting select"
|
|
20024
|
+
items={items}
|
|
20025
|
+
isOpen={true}
|
|
20026
|
+
optionClassName={(item) =>
|
|
20027
|
+
item.id === selected ? "option-picked" : "option-plain"
|
|
20028
|
+
}
|
|
20029
|
+
onSelectionChange={setSelected}
|
|
20030
|
+
/>
|
|
20031
|
+
);
|
|
19043
20032
|
};`},similarTo:[],figmaUrl:null},Separator:{id:"core-content-separator",breadcrumb:"Core/Content/Separator",importStatement:'import { Separator } from "@baseline-ui/core";',description:"`Separator` is a thin horizontal or vertical rule that divides adjacent content. Use it to create a visual break between sections, groups of controls, or items in a list.",documentation:`\`Separator\` is a thin horizontal or vertical rule that divides adjacent content. Use it to create a visual break between sections, groups of controls, or items in a list.
|
|
19044
20033
|
|
|
19045
20034
|
\`\`\`jsx
|
|
@@ -23296,7 +24285,7 @@ export const TaggedPaginationExample: React.FC<
|
|
|
23296
24285
|
{...props}
|
|
23297
24286
|
/>
|
|
23298
24287
|
);
|
|
23299
|
-
};`},similarTo:[],figmaUrl:null},Text:{id:"core-content-text",breadcrumb:"Core/Content/Text",importStatement:'import { Text, VariantViewer } from "@baseline-ui/core";',description:"`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.",documentation:'`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.\n\n```jsx\nimport { Text } from "@
|
|
24288
|
+
};`},similarTo:[],figmaUrl:null},Text:{id:"core-content-text",breadcrumb:"Core/Content/Text",importStatement:'import { Input, Label, Text, TextField, VariantViewer } from "@baseline-ui/core";',description:"`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.",documentation:'`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.\n\n* Consistent type, size, and weight pulled from the design system\n* Renders as any HTML element via the `elementType` prop\n* Opt-in integration with a container\'s context through the `slot` prop for automatic `aria-describedby` / `aria-labelledby` wiring\n\n```jsx\nimport { Text } from "@baseline-ui/core";\n\n<Text type="subtitle" size="sm">\n Text\n</Text>;\n```\n\nThe `Text` component supports the following types: `title`, `subtitle`, `body`, `label`, `value`, and `helper`. It\nalso supports the following sizes: `sm`, `md`, and `lg`.\n\n`Text` can integrate with a container component\'s context. Pass a `slot` name\nand the text picks up the `id` the container provides for that slot, so it gets\nassociated via `aria-describedby` / `aria-labelledby` without manually wiring an\n`id`.\n\n```jsx\n<Text slot="description">\n This description is linked to its field automatically.\n</Text>\n```\n\nSlot integration is **opt-in**: a `Text` without a `slot` never inherits an\nambient context, so it renders identically whether or not a container is\npresent. Naming a `slot` the surrounding container does not define will throw \u2014\nonly pass slots you know the container exposes.\n\nNo Baseline container currently provides a slotted `TextContext`, so this is\ngroundwork today: the mechanism is exercisable against raw React Aria containers\n(`TextField`, `Select`, `TagGroup`, etc.) but no Baseline component wires it up yet.\n\n`Text` always emits a stable class name you can target for styling.\n\n| Selector | Description |\n| ------------------ | ------------------------------------- |\n| `.BaselineUI-Text` | Applied to the rendered text element. |',props:`interface TextProps {
|
|
23300
24289
|
/**
|
|
23301
24290
|
* @deprecated Do not use in new components. This is a legacy block
|
|
23302
24291
|
* identifier; new components should exclude it via
|
|
@@ -23352,6 +24341,13 @@ children: React.ReactNode
|
|
|
23352
24341
|
* @default "span"
|
|
23353
24342
|
*/
|
|
23354
24343
|
elementType?: React.ElementType
|
|
24344
|
+
/**
|
|
24345
|
+
* A slot name that wires this text into a parent container's context \u2014
|
|
24346
|
+
* e.g. \`slot="description"\` or \`slot="label"\` \u2014 so the container can
|
|
24347
|
+
* associate it via \`aria-describedby\` / \`aria-labelledby\` without manually
|
|
24348
|
+
* threading an \`id\`. Pass \`null\` to opt out of an ambient slot.
|
|
24349
|
+
*/
|
|
24350
|
+
slot?: string | null
|
|
23355
24351
|
}`,stories:{usage:[{id:"core-content-text--basic",name:"Basic",snippet:"const Basic = () => <Text>Sample Text</Text>;"},{id:"core-content-text--variants",name:"Variants",snippet:`const Variants = (args) => (
|
|
23356
24352
|
<VariantViewer<React.ComponentProps<typeof Text>>
|
|
23357
24353
|
header={["Small", "Medium", "Large"]}
|
|
@@ -23386,8 +24382,24 @@ elementType?: React.ElementType
|
|
|
23386
24382
|
}}
|
|
23387
24383
|
defaultProps={args}
|
|
23388
24384
|
/>
|
|
23389
|
-
);`}
|
|
24385
|
+
);`},{id:"core-content-text--slots",name:"Slots",snippet:`const Slots = () => (
|
|
24386
|
+
<TextField>
|
|
24387
|
+
{/* The raw React Aria Label carries no Baseline styling, so it inherits
|
|
24388
|
+
the storybook default (black) text and fails contrast against the dark
|
|
24389
|
+
theme background. Give it the themed foreground; the slot wiring below
|
|
24390
|
+
is the point of this story. */}
|
|
24391
|
+
<Label style={{ color: themeVars.color.text.primary }}>
|
|
24392
|
+
Display name
|
|
24393
|
+
</Label>
|
|
24394
|
+
<Input />
|
|
24395
|
+
<Text slot="description" type="helper" size="sm">
|
|
24396
|
+
This is shown to other members of your workspace.
|
|
24397
|
+
</Text>
|
|
24398
|
+
</TextField>
|
|
24399
|
+
);`,description:"`Text` opts into a container's context when given a `slot`. Here a raw React Aria `TextField` provides a `TextContext`, and `<Text slot=\"description\">` picks up the id the field exposes for that slot \u2014 wiring itself as the field's `aria-describedby` without a manually threaded `id`."}],implementation:`import React from "react";
|
|
24400
|
+
import { TextContext } from "react-aria-components";
|
|
23390
24401
|
|
|
24402
|
+
import { UNSAFE_ListBox } from "../../UNSAFE_ListBox";
|
|
23391
24403
|
import { Text } from "../Text";
|
|
23392
24404
|
|
|
23393
24405
|
export function TextWithRef() {
|
|
@@ -23408,65 +24420,61 @@ export function TextWithRef() {
|
|
|
23408
24420
|
<span data-testid="tag-name">{tag}</span>
|
|
23409
24421
|
</>
|
|
23410
24422
|
);
|
|
24423
|
+
}
|
|
24424
|
+
|
|
24425
|
+
const SLOTTED_TEXT_CONTEXT = {
|
|
24426
|
+
slots: {
|
|
24427
|
+
description: { id: "provided-description-id" },
|
|
24428
|
+
},
|
|
24429
|
+
};
|
|
24430
|
+
|
|
24431
|
+
export function TextInSlotContext() {
|
|
24432
|
+
return (
|
|
24433
|
+
<TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
|
|
24434
|
+
<Text slot="description" data-block-id="slotted">
|
|
24435
|
+
Description
|
|
24436
|
+
</Text>
|
|
24437
|
+
</TextContext.Provider>
|
|
24438
|
+
);
|
|
24439
|
+
}
|
|
24440
|
+
|
|
24441
|
+
export function TextBareInSlotContext() {
|
|
24442
|
+
// A slotless <Text> inside a slotted context that has no default slot must
|
|
24443
|
+
// render (opt out) rather than throw "A slot prop is required".
|
|
24444
|
+
return (
|
|
24445
|
+
<TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
|
|
24446
|
+
<Text id="bare-id" data-testid="bare">
|
|
24447
|
+
Description
|
|
24448
|
+
</Text>
|
|
24449
|
+
</TextContext.Provider>
|
|
24450
|
+
);
|
|
24451
|
+
}
|
|
24452
|
+
|
|
24453
|
+
export function TextInListBoxRenderOption() {
|
|
24454
|
+
// A bare <Text> in renderOption lands inside RAC's ListBoxItem, which provides
|
|
24455
|
+
// a TextContext whose default slot maps to the option's generated label id.
|
|
24456
|
+
// A slotless <Text> must opt out (no inherited id) rather than silently adopt
|
|
24457
|
+
// that label id \u2014 otherwise two Texts in one option collide on the same id.
|
|
24458
|
+
return (
|
|
24459
|
+
<UNSAFE_ListBox
|
|
24460
|
+
aria-label="Options"
|
|
24461
|
+
items={[{ id: "opt-1", label: "Option 1" }]}
|
|
24462
|
+
renderOption={(item) => (
|
|
24463
|
+
<Text data-testid="render-option">{item.label}</Text>
|
|
24464
|
+
)}
|
|
24465
|
+
/>
|
|
24466
|
+
);
|
|
24467
|
+
}
|
|
24468
|
+
|
|
24469
|
+
export function TextOptOutOfSlotContext() {
|
|
24470
|
+
return (
|
|
24471
|
+
<TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
|
|
24472
|
+
<Text slot={null} id="own-id" data-block-id="opt-out">
|
|
24473
|
+
Description
|
|
24474
|
+
</Text>
|
|
24475
|
+
</TextContext.Provider>
|
|
24476
|
+
);
|
|
23411
24477
|
}`},similarTo:[],figmaUrl:null},TextInput:{id:"core-forms-textinput",breadcrumb:"Core/Forms/TextInput",importStatement:'import { TextInput, VariantViewer } from "@baseline-ui/core";',description:"`TextInput` is a field for capturing free-form text \u2014 a single-line `<input>` by default, or a resizable multi-line `<textarea>` when `isMultiLine` is set. Use it for values such as names, emails, or longer notes.",documentation:'`TextInput` is a field for capturing free-form text \u2014 a single-line `<input>` by default, or a resizable multi-line `<textarea>` when `isMultiLine` is set. Use it for values such as names, emails, or longer notes.\n\n* This component is built on top of the `input` element (or `textarea` in multi-line mode).\n* It provides visual and ARIA labels to the input element to make it more accessible.\n* It supports events for change, clipboard, composition, focus, and keyboard.\n* It exposes invalid states to assistive technology via ARIA.\n* It supports description and other state messages which are linked to the input element via ARIA.\n\n```jsx\nimport { TextInput } from "@baseline-ui/core";\n\n<TextInput placeholder="Enter Text" />;\n```\n\nFor numeric entry use `NumberInput`, for search fields use `SearchInput`, and for color values use `ColorInput`.\n\n`TextInput` supports two variants: `primary` (default) and `ghost`. Set the variant with the `variant` prop.\n\n```jsx\n<TextInput placeholder="Placeholder" variant="primary" />\n<TextInput placeholder="Placeholder" variant="ghost" />\n```\n\nYou can add a label to the `TextInput` by passing a `label` prop.\n\n```jsx\n<TextInput label="Label" placeholder="Placeholder" />\n```\n\nBy default, the label is positioned above the `TextInput`. You can change the\nposition of the label by passing a `labelPosition` prop.\n\n```jsx\n<TextInput\n label="Label"\n labelPosition="start"\n description="Description"\n placeholder="Placeholder"\n/>\n```\n\nYou can add a description to the `TextInput` by passing a `description` prop. A\ndescription is used to provide additional information about the `TextInput`.\n\n```jsx\n<TextInput label="Label" description="Description" placeholder="Placeholder" />\n```\n\nYou can put the `TextInput` into an error state by setting `validationState` to\n`"error"`. You can also pass `errorMessage` to provide additional information\nabout the error; passing `errorMessage` on its own is enough to render the error\nstate. When `description` is also present, it takes precedence and the error\nmessage is not shown.\n\n```jsx\n<TextInput\n label="Label"\n validationState="error"\n errorMessage="Error message"\n placeholder="Placeholder"\n/>\n```\n\nYou can put the `TextInput` into a warning state by setting `validationState` to\n`"warning"`. You can also pass `warningMessage` to provide additional\ninformation about the warning.\n\n```jsx\n<TextInput\n label="Label"\n validationState="warning"\n warningMessage="Warning message"\n placeholder="Placeholder"\n/>\n```\n\nYou can make the `TextInput` read only by passing an `isReadOnly` prop.\n\n```jsx\n<TextInput label="Label" isReadOnly defaultValue="Read-only value" />\n```\n\nYou can disable the `TextInput` by passing an `isDisabled` prop.\n\n```jsx\n<TextInput\n label="Label"\n isDisabled\n defaultValue="Disabled value"\n description="Description"\n placeholder="Placeholder"\n/>\n```\n\nYou can control the `TextInput` by passing a `value` prop together with an\n`onChange` prop. `onChange` is called with the new string value (not a DOM\nevent).\n\n```jsx\nconst [value, setValue] = useState("Controlled value");\n\n<TextInput\n label="Label"\n value={value}\n onChange={(value) => setValue(value)}\n placeholder="Placeholder"\n/>;\n```\n\nSet `isMultiLine` to render the field as a resizable `<textarea>` instead of a\nsingle-line `<input>`. The field starts at a minimum height and can be dragged\nto resize in both directions, except when it is read only or disabled. All\nstates (error, warning, disabled, read only, ghost) are supported, just like the\nsingle-line input.\n\n```jsx\n<TextInput\n label="Label"\n isMultiLine\n defaultValue="Lorem ipsum"\n description="Helper text"\n/>\n```\n\nUse the `rows` prop to set the initial number of visible text rows.\n\n```jsx\n<TextInput label="Label" isMultiLine rows={5} placeholder="Placeholder" />\n```\n\nYou can use the `TextInput` in a HTML form by passing a `name` prop. In addition, attributes such as `type`, `pattern`, `inputMode`, and others are passed through to the underlying input element.\n\n```jsx\n<form>\n <TextInput\n name="text-input"\n type="text"\n placeholder="Placeholder"\n description="Description"\n />\n</form>\n```\n\nThese selectors are applied to the underlying `<input>` / `<textarea>` element. The root wrapper is also exposed via the `BaselineUI-TextInput` class and the field element via the `BaselineUI-TextInput-Input` class.\n\n| Selector | Description |\n| ------------------------- | ------------------------------------------------------------------- |\n| `[data-readonly]` | Whether the input is read only. |\n| `[data-disabled]` | Whether the input is disabled. |\n| `[data-validation-state]` | The validation state of the input (`error`, `warning`, or `valid`). |\n| `[data-focused]` | Whether the input is focused, either via a mouse or keyboard. |\n| `[data-focus-visible]` | Whether the input is keyboard focused. |\n\n| Key | Function |\n| ---------------- | ---------------------------------------------------------------------------- |\n| <kbd>Enter</kbd> | Submits the form in single-line mode; inserts a new line in multi-line mode. |',props:`interface TextInputProps {
|
|
23412
|
-
/**
|
|
23413
|
-
* @deprecated Do not use in new components. This is a legacy block
|
|
23414
|
-
* identifier; new components should exclude it via
|
|
23415
|
-
* \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
|
|
23416
|
-
* Retained on existing components for backward compatibility.
|
|
23417
|
-
*
|
|
23418
|
-
* The unique identifier for the block. This is used to identify the block in
|
|
23419
|
-
* the DOM and in the block map. It is added as a data attribute
|
|
23420
|
-
* \`data-block-id\` to the root element of the block if a DOM node is
|
|
23421
|
-
* rendered.
|
|
23422
|
-
*/
|
|
23423
|
-
data-block-id?: string
|
|
23424
|
-
/**
|
|
23425
|
-
* @deprecated Do not use in new components. This is a legacy block group
|
|
23426
|
-
* marker; new components should exclude it via
|
|
23427
|
-
* \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
|
|
23428
|
-
* Retained on existing components for backward compatibility.
|
|
23429
|
-
*
|
|
23430
|
-
* Represents a data block group. This is similar to \`data-block-id\` but it
|
|
23431
|
-
* doesn't have to be unique just like \`class\`. This is used to group blocks
|
|
23432
|
-
* together in the DOM and in the block map. It is added as a data attribute
|
|
23433
|
-
* \`data-block-class\` to the root element of the block if a DOM node is
|
|
23434
|
-
* rendered.
|
|
23435
|
-
*/
|
|
23436
|
-
data-block-class?: string
|
|
23437
|
-
/**
|
|
23438
|
-
* The className applied to the root element of the component.
|
|
23439
|
-
*/
|
|
23440
|
-
className?: string
|
|
23441
|
-
/**
|
|
23442
|
-
* The style applied to the root element of the component.
|
|
23443
|
-
*/
|
|
23444
|
-
style?: React.CSSProperties
|
|
23445
|
-
/**
|
|
23446
|
-
* The description to display below the input.
|
|
23447
|
-
*/
|
|
23448
|
-
description?: string
|
|
23449
|
-
/**
|
|
23450
|
-
* The error message to display when the input is in an error state.
|
|
23451
|
-
*/
|
|
23452
|
-
errorMessage?: string
|
|
23453
|
-
/**
|
|
23454
|
-
* The warning message to display when the input is in a warning state.
|
|
23455
|
-
*/
|
|
23456
|
-
warningMessage?: string
|
|
23457
|
-
/**
|
|
23458
|
-
* The style object to apply to the input element
|
|
23459
|
-
*/
|
|
23460
|
-
inputStyle?: React.CSSProperties
|
|
23461
|
-
/**
|
|
23462
|
-
* The class name to apply to the input element
|
|
23463
|
-
*/
|
|
23464
|
-
inputClassName?: string
|
|
23465
|
-
/**
|
|
23466
|
-
* The number of visible text rows for the multi-line input. Only applies when
|
|
23467
|
-
* \`isMultiLine\` is set.
|
|
23468
|
-
*/
|
|
23469
|
-
rows?: number
|
|
23470
24478
|
variant?: any
|
|
23471
24479
|
labelPosition?: any
|
|
23472
24480
|
isMultiLine?: any
|
|
@@ -26809,6 +27817,7 @@ export const VirtualizedTreeViewWithRenameExample: React.FC<
|
|
|
26809
27817
|
VirtualizedScrollIntoViewExample,
|
|
26810
27818
|
VirtualListBoxGridLayoutExample,
|
|
26811
27819
|
VirtualListBoxListLayoutExample,
|
|
27820
|
+
VirtualListBoxWithSectionHeadersExample,
|
|
26812
27821
|
VirtualListBoxWithSectionsExample,
|
|
26813
27822
|
} from "@baseline-ui/core";`,description:"`UNSAFE_ListBox` is a low-level selectable list with custom option rendering, grouped sections, drag-and-drop reordering, and virtualization. Use it to build bespoke list UIs \u2014 such as font pickers, layer panels, or large catalogs \u2014 where the standard list components are too constrained.",documentation:`\`UNSAFE_ListBox\` is a low-level selectable list with custom option rendering, grouped sections, drag-and-drop reordering, and virtualization. Use it to build bespoke list UIs \u2014 such as font pickers, layer panels, or large catalogs \u2014 where the standard list components are too constrained.
|
|
26814
27823
|
|
|
@@ -26905,6 +27914,34 @@ const fonts = [
|
|
|
26905
27914
|
/>;
|
|
26906
27915
|
\`\`\`
|
|
26907
27916
|
|
|
27917
|
+
Rendered options and sections are cached per item object. Reading component state from
|
|
27918
|
+
\`renderOption\`, \`optionClassName\`, \`optionStyle\`, \`renderSectionHeader\`, \`sectionClassName\`
|
|
27919
|
+
or \`sectionStyle\` works \u2014 the options re-render when the state changes \u2014 and toggling
|
|
27920
|
+
\`showSectionHeader\` or \`withSectionHeaderPadding\` does too.
|
|
27921
|
+
|
|
27922
|
+
The cost only applies when \`items\` is memoized, since an inline \`items={data.map(...)}\`
|
|
27923
|
+
produces new item objects every render and the cache never applies in the first place. With
|
|
27924
|
+
stable items, any of these props changing identity rebuilds every option and section, so
|
|
27925
|
+
memoize the ones that don't depend on state \u2014 including object literals passed to
|
|
27926
|
+
\`optionStyle\`, which are new on every render.
|
|
27927
|
+
|
|
27928
|
+
\`\`\`jsx
|
|
27929
|
+
const [highlighted, setHighlighted] = useState(null);
|
|
27930
|
+
|
|
27931
|
+
// Rebuilds only when the highlight moves; \`fonts\` is module-level, so the cache applies.
|
|
27932
|
+
const optionStyle = useCallback(
|
|
27933
|
+
(item) => ({ fontWeight: item.id === highlighted ? "bold" : "normal" }),
|
|
27934
|
+
[highlighted],
|
|
27935
|
+
);
|
|
27936
|
+
|
|
27937
|
+
<UNSAFE_ListBox
|
|
27938
|
+
items={fonts}
|
|
27939
|
+
aria-label="Fonts"
|
|
27940
|
+
optionStyle={optionStyle}
|
|
27941
|
+
onAction={setHighlighted}
|
|
27942
|
+
/>;
|
|
27943
|
+
\`\`\`
|
|
27944
|
+
|
|
26908
27945
|
\`\`\`jsx
|
|
26909
27946
|
<UNSAFE_ListBox
|
|
26910
27947
|
items={items}
|
|
@@ -27002,6 +28039,14 @@ style?: React.CSSProperties
|
|
|
27002
28039
|
/**
|
|
27003
28040
|
* The custom render function for the listbox options.
|
|
27004
28041
|
*
|
|
28042
|
+
* Replaces the option's entire contents, including the default selection
|
|
28043
|
+
* indicator \u2014 render your own affordance from \`options.isSelected\` if you
|
|
28044
|
+
* need one. Selection and keyboard behavior are unaffected.
|
|
28045
|
+
*
|
|
28046
|
+
* Reading component state here works, at the cost of rebuilding every cached option and
|
|
28047
|
+
* section whenever the function's identity changes \u2014 see {@link UNSAFE_ListBoxProps.items}
|
|
28048
|
+
* for when that cost applies.
|
|
28049
|
+
*
|
|
27005
28050
|
* @param item ListOption
|
|
27006
28051
|
* @param options ListBoxItemRenderProps
|
|
27007
28052
|
*/
|
|
@@ -27010,7 +28055,10 @@ renderOption?: (
|
|
|
27010
28055
|
options: ListBoxItemRenderProps,
|
|
27011
28056
|
) => React.ReactNode
|
|
27012
28057
|
/**
|
|
27013
|
-
*
|
|
28058
|
+
* A CSS class name added to the option's built-in classes.
|
|
28059
|
+
*
|
|
28060
|
+
* Compared by identity, so an inline function or an unmemoized value rebuilds every
|
|
28061
|
+
* cached option and section on each render. A plain string is compared by value.
|
|
27014
28062
|
*/
|
|
27015
28063
|
optionClassName?: | string
|
|
27016
28064
|
| ((
|
|
@@ -27019,6 +28067,9 @@ optionClassName?: | string
|
|
|
27019
28067
|
) => string | undefined)
|
|
27020
28068
|
/**
|
|
27021
28069
|
* The style of the option.
|
|
28070
|
+
*
|
|
28071
|
+
* Compared by identity, so an inline function or object literal rebuilds every cached
|
|
28072
|
+
* option and section on each render \u2014 memoize it to avoid that.
|
|
27022
28073
|
*/
|
|
27023
28074
|
optionStyle?: | React.CSSProperties
|
|
27024
28075
|
| ((
|
|
@@ -27028,15 +28079,25 @@ optionStyle?: | React.CSSProperties
|
|
|
27028
28079
|
/**
|
|
27029
28080
|
* The custom render function for the listbox sections.
|
|
27030
28081
|
*
|
|
28082
|
+
* Reading component state here works, at the cost of rebuilding every cached option and
|
|
28083
|
+
* section whenever the function's identity changes \u2014 see {@link UNSAFE_ListBoxProps.items}
|
|
28084
|
+
* for when that cost applies.
|
|
28085
|
+
*
|
|
27031
28086
|
* @param section ListSection
|
|
27032
28087
|
*/
|
|
27033
28088
|
renderSectionHeader?: (section: ListSection) => React.ReactNode
|
|
27034
28089
|
/**
|
|
27035
28090
|
* The CSS class name for the section.
|
|
28091
|
+
*
|
|
28092
|
+
* Compared by identity, so an inline function or an unmemoized value rebuilds every
|
|
28093
|
+
* cached option and section on each render. A plain string is compared by value.
|
|
27036
28094
|
*/
|
|
27037
28095
|
sectionClassName?: string | ((section: ListSection) => string | undefined)
|
|
27038
28096
|
/**
|
|
27039
28097
|
* The style of the section.
|
|
28098
|
+
*
|
|
28099
|
+
* Compared by identity, so an inline function or object literal rebuilds every cached
|
|
28100
|
+
* option and section on each render \u2014 memoize it to avoid that.
|
|
27040
28101
|
*/
|
|
27041
28102
|
sectionStyle?: | React.CSSProperties
|
|
27042
28103
|
| ((section: ListSection) => React.CSSProperties | undefined)
|
|
@@ -27072,6 +28133,11 @@ withSectionHeaderPadding?: boolean
|
|
|
27072
28133
|
*
|
|
27073
28134
|
* type ListItem = ListOption | ListSection;
|
|
27074
28135
|
* \`\`\`
|
|
28136
|
+
*
|
|
28137
|
+
* Rendered options and sections are cached per item object. Building this array inline
|
|
28138
|
+
* (\`items={data.map(...)}\`) produces new item objects every render, so nothing is ever
|
|
28139
|
+
* reused and the customisation props cost nothing; memoize it and the cache applies,
|
|
28140
|
+
* which is when their identity starts to matter.
|
|
27075
28141
|
*/
|
|
27076
28142
|
items?: ListItem[]
|
|
27077
28143
|
/**
|
|
@@ -27158,7 +28224,7 @@ listBoxHandle?: React.RefObject<ListHandle>
|
|
|
27158
28224
|
items={items}
|
|
27159
28225
|
aria-label="List box"
|
|
27160
28226
|
selectionMode="multiple"
|
|
27161
|
-
defaultSelectedKeys={["item-0"]} />;`},{id:"core-collections-unsafe-listbox--virtualized-horizontal",name:"Virtualized Horizontal",snippet:'const VirtualizedHorizontal = () => <VirtualListBoxGridLayoutExample items={items} aria-label="List box" selectionMode="multiple" />;'},{id:"core-collections-unsafe-listbox--virtualized-with-sections",name:"Virtualized With Sections",snippet:'const VirtualizedWithSections = () => <VirtualListBoxWithSectionsExample items={items} aria-label="List box" selectionMode="single" />;'},{id:"core-collections-unsafe-listbox--virtualized-scroll-into-view",name:"Virtualized Scroll Into View",snippet:"const VirtualizedScrollIntoView = () => <VirtualizedScrollIntoViewExample />;"}],implementation:`import { XIcon } from "@baseline-ui/icons/16";
|
|
28227
|
+
defaultSelectedKeys={["item-0"]} />;`},{id:"core-collections-unsafe-listbox--virtualized-horizontal",name:"Virtualized Horizontal",snippet:'const VirtualizedHorizontal = () => <VirtualListBoxGridLayoutExample items={items} aria-label="List box" selectionMode="multiple" />;'},{id:"core-collections-unsafe-listbox--virtualized-with-sections",name:"Virtualized With Sections",snippet:'const VirtualizedWithSections = () => <VirtualListBoxWithSectionsExample items={items} aria-label="List box" selectionMode="single" />;'},{id:"core-collections-unsafe-listbox--virtualized-with-section-separators",name:"Virtualized With Section Separators",snippet:'const VirtualizedWithSectionSeparators = () => <VirtualListBoxWithSectionHeadersExample items={items} aria-label="List box" selectionMode="single" />;'},{id:"core-collections-unsafe-listbox--virtualized-scroll-into-view",name:"Virtualized Scroll Into View",snippet:"const VirtualizedScrollIntoView = () => <VirtualizedScrollIntoViewExample />;"}],implementation:`import { XIcon } from "@baseline-ui/icons/16";
|
|
27162
28228
|
import {
|
|
27163
28229
|
EllipseDashedIcon,
|
|
27164
28230
|
EllipseIcon,
|
|
@@ -27188,7 +28254,10 @@ import {
|
|
|
27188
28254
|
import { useDragAndDrop } from "../hooks/useDragAndDrop";
|
|
27189
28255
|
import { UNSAFE_ListBox as ListBox } from "../ListBox";
|
|
27190
28256
|
|
|
27191
|
-
import type {
|
|
28257
|
+
import type {
|
|
28258
|
+
ListItem,
|
|
28259
|
+
ListOption as ListOptionType,
|
|
28260
|
+
} from "../../shared/types/List";
|
|
27192
28261
|
import type { ListHandle } from "../ListBox.types";
|
|
27193
28262
|
import type { DroppableCollectionReorderEvent } from "@react-types/shared";
|
|
27194
28263
|
|
|
@@ -27298,8 +28367,27 @@ export const ListBoxWithSectionsExample: React.FC<
|
|
|
27298
28367
|
export const DragAndDropListBoxExample: React.FC<
|
|
27299
28368
|
Omit<React.ComponentProps<typeof ListBox>, "items" | "onReorder"> & {
|
|
27300
28369
|
customPreview?: boolean;
|
|
28370
|
+
rerenderOnEvent?: boolean;
|
|
28371
|
+
}
|
|
28372
|
+
> = ({ orientation, layout, customPreview, rerenderOnEvent, ...rest }) => {
|
|
28373
|
+
const [, rerender] = React.useReducer((tick: number) => tick + 1, 0);
|
|
28374
|
+
|
|
28375
|
+
React.useEffect(() => {
|
|
28376
|
+
if (!rerenderOnEvent) {
|
|
28377
|
+
return;
|
|
27301
28378
|
}
|
|
27302
|
-
|
|
28379
|
+
|
|
28380
|
+
const onRerender = () => {
|
|
28381
|
+
rerender();
|
|
28382
|
+
};
|
|
28383
|
+
|
|
28384
|
+
window.addEventListener("repro:rerender", onRerender);
|
|
28385
|
+
|
|
28386
|
+
return () => {
|
|
28387
|
+
window.removeEventListener("repro:rerender", onRerender);
|
|
28388
|
+
};
|
|
28389
|
+
}, [rerenderOnEvent]);
|
|
28390
|
+
|
|
27303
28391
|
const list = useListData({
|
|
27304
28392
|
initialItems: Array.from({ length: 20 }).map((_, i) => ({
|
|
27305
28393
|
id: \`item-\${i}\`,
|
|
@@ -27765,6 +28853,75 @@ export const VirtualListBoxWithSectionsExample: React.FC<
|
|
|
27765
28853
|
);
|
|
27766
28854
|
};
|
|
27767
28855
|
|
|
28856
|
+
export const VirtualListBoxWithSectionHeadersExample: React.FC<
|
|
28857
|
+
Omit<React.ComponentProps<typeof ListBox>, "items">
|
|
28858
|
+
> = (args) => {
|
|
28859
|
+
return (
|
|
28860
|
+
<Virtualizer
|
|
28861
|
+
layout={ListLayout}
|
|
28862
|
+
layoutOptions={{
|
|
28863
|
+
rowHeight: 36,
|
|
28864
|
+
headingHeight: 40,
|
|
28865
|
+
}}
|
|
28866
|
+
>
|
|
28867
|
+
<ListBox
|
|
28868
|
+
{...args}
|
|
28869
|
+
items={itemsWithSectionTitles}
|
|
28870
|
+
showSectionHeader={true}
|
|
28871
|
+
style={{
|
|
28872
|
+
height: 400,
|
|
28873
|
+
width: 300,
|
|
28874
|
+
overflow: "auto",
|
|
28875
|
+
}}
|
|
28876
|
+
aria-label="Virtualized list box with section headers"
|
|
28877
|
+
/>
|
|
28878
|
+
</Virtualizer>
|
|
28879
|
+
);
|
|
28880
|
+
};
|
|
28881
|
+
|
|
28882
|
+
const leadingSection = {
|
|
28883
|
+
id: "leading",
|
|
28884
|
+
title: "Leading",
|
|
28885
|
+
children: [{ id: "leading-item", label: "Leading item" }],
|
|
28886
|
+
} as ListItem;
|
|
28887
|
+
|
|
28888
|
+
export const VirtualListBoxWithPrependableSectionExample: React.FC = () => {
|
|
28889
|
+
const [showLeadingSection, setShowLeadingSection] = React.useState(false);
|
|
28890
|
+
|
|
28891
|
+
return (
|
|
28892
|
+
<>
|
|
28893
|
+
<ActionButton
|
|
28894
|
+
onPress={() => {
|
|
28895
|
+
setShowLeadingSection(true);
|
|
28896
|
+
}}
|
|
28897
|
+
label="Prepend section"
|
|
28898
|
+
/>
|
|
28899
|
+
<Virtualizer
|
|
28900
|
+
layout={ListLayout}
|
|
28901
|
+
layoutOptions={{
|
|
28902
|
+
rowHeight: 36,
|
|
28903
|
+
headingHeight: 40,
|
|
28904
|
+
}}
|
|
28905
|
+
>
|
|
28906
|
+
<ListBox
|
|
28907
|
+
items={
|
|
28908
|
+
showLeadingSection
|
|
28909
|
+
? [leadingSection, ...itemsWithSectionTitles]
|
|
28910
|
+
: itemsWithSectionTitles
|
|
28911
|
+
}
|
|
28912
|
+
showSectionHeader={true}
|
|
28913
|
+
style={{
|
|
28914
|
+
height: 400,
|
|
28915
|
+
width: 300,
|
|
28916
|
+
overflow: "auto",
|
|
28917
|
+
}}
|
|
28918
|
+
aria-label="Virtualized list box with a prependable section"
|
|
28919
|
+
/>
|
|
28920
|
+
</Virtualizer>
|
|
28921
|
+
</>
|
|
28922
|
+
);
|
|
28923
|
+
};
|
|
28924
|
+
|
|
27768
28925
|
export const VirtualizedScrollIntoViewExample: React.FC = () => {
|
|
27769
28926
|
const listBoxHandle = React.useRef<ListHandle>(null);
|
|
27770
28927
|
const items = Array.from({ length: 10_000 }).map((_, i) => ({
|
|
@@ -27822,7 +28979,110 @@ export const VirtualizedScrollIntoViewExample: React.FC = () => {
|
|
|
27822
28979
|
</Virtualizer>
|
|
27823
28980
|
</Box>
|
|
27824
28981
|
);
|
|
27825
|
-
}
|
|
28982
|
+
};
|
|
28983
|
+
|
|
28984
|
+
// Each example varies exactly one prop off \`count\` and keeps the rest at stable
|
|
28985
|
+
// module-level identity; varying several at once would let one invalidate the cache for
|
|
28986
|
+
// the others, hiding a missing \`dependencies\` entry.
|
|
28987
|
+
const stableRenderOption = (item: ListOptionType) => <span>{item.label}</span>;
|
|
28988
|
+
const stableOptionClassName = () => "option-stable";
|
|
28989
|
+
const stableOptionStyle = () => ({ marginBlockStart: "0px" });
|
|
28990
|
+
const stableSectionClassName = () => "section-stable";
|
|
28991
|
+
const stableSectionStyle = () => ({ marginBlockStart: "0px" });
|
|
28992
|
+
|
|
28993
|
+
// Offset off the \`0px\` browser default so the pre-click assertion also proves the prop
|
|
28994
|
+
// reached the DOM, not just that it refreshed.
|
|
28995
|
+
const varyingMargin = (count: number) => ({
|
|
28996
|
+
marginBlockStart: \`\${count + 2}px\`,
|
|
28997
|
+
});
|
|
28998
|
+
|
|
28999
|
+
const CountingExample: React.FC<{
|
|
29000
|
+
children: (count: number) => React.ReactNode;
|
|
29001
|
+
}> = ({ children }) => {
|
|
29002
|
+
const [count, setCount] = React.useState(0);
|
|
29003
|
+
|
|
29004
|
+
return (
|
|
29005
|
+
<>
|
|
29006
|
+
<ActionButton
|
|
29007
|
+
label="Increment"
|
|
29008
|
+
onPress={() => {
|
|
29009
|
+
setCount(count + 1);
|
|
29010
|
+
}}
|
|
29011
|
+
/>
|
|
29012
|
+
{children(count)}
|
|
29013
|
+
</>
|
|
29014
|
+
);
|
|
29015
|
+
};
|
|
29016
|
+
|
|
29017
|
+
export const CountingOptionListBoxExample: React.FC<{
|
|
29018
|
+
vary: "renderOption" | "optionClassName" | "optionStyle";
|
|
29019
|
+
// Options inside a section render through a second, nested collection that inherits the
|
|
29020
|
+
// root \`dependencies\` only via React Aria's collection context.
|
|
29021
|
+
sectioned?: boolean;
|
|
29022
|
+
}> = ({ vary, sectioned = false }) => (
|
|
29023
|
+
<CountingExample>
|
|
29024
|
+
{(count) => (
|
|
29025
|
+
<ListBox
|
|
29026
|
+
items={sectioned ? itemsWithSectionTitles : items}
|
|
29027
|
+
aria-label="Counting option list box"
|
|
29028
|
+
renderOption={
|
|
29029
|
+
vary === "renderOption"
|
|
29030
|
+
? (item) => <span>{\`\${item.label} count-\${count}\`}</span>
|
|
29031
|
+
: stableRenderOption
|
|
29032
|
+
}
|
|
29033
|
+
optionClassName={
|
|
29034
|
+
vary === "optionClassName"
|
|
29035
|
+
? () => \`option-count-\${count}\`
|
|
29036
|
+
: stableOptionClassName
|
|
29037
|
+
}
|
|
29038
|
+
optionStyle={
|
|
29039
|
+
vary === "optionStyle"
|
|
29040
|
+
? () => varyingMargin(count)
|
|
29041
|
+
: stableOptionStyle
|
|
29042
|
+
}
|
|
29043
|
+
/>
|
|
29044
|
+
)}
|
|
29045
|
+
</CountingExample>
|
|
29046
|
+
);
|
|
29047
|
+
|
|
29048
|
+
export const CountingSectionListBoxExample: React.FC<{
|
|
29049
|
+
vary:
|
|
29050
|
+
| "renderSectionHeader"
|
|
29051
|
+
| "sectionClassName"
|
|
29052
|
+
| "sectionStyle"
|
|
29053
|
+
| "showSectionHeader"
|
|
29054
|
+
| "withSectionHeaderPadding";
|
|
29055
|
+
}> = ({ vary }) => (
|
|
29056
|
+
<CountingExample>
|
|
29057
|
+
{(count) => (
|
|
29058
|
+
<ListBox
|
|
29059
|
+
items={itemsWithSectionTitles}
|
|
29060
|
+
aria-label="Counting section list box"
|
|
29061
|
+
// \`withSectionHeaderPadding\` only reaches the DOM through the default header, so
|
|
29062
|
+
// that variant must not supply a custom one.
|
|
29063
|
+
showSectionHeader={vary === "showSectionHeader" ? count > 0 : true}
|
|
29064
|
+
withSectionHeaderPadding={
|
|
29065
|
+
vary === "withSectionHeaderPadding" ? count > 0 : false
|
|
29066
|
+
}
|
|
29067
|
+
renderSectionHeader={
|
|
29068
|
+
vary === "renderSectionHeader"
|
|
29069
|
+
? (section) => \`\${section.title} count-\${count}\`
|
|
29070
|
+
: undefined
|
|
29071
|
+
}
|
|
29072
|
+
sectionClassName={
|
|
29073
|
+
vary === "sectionClassName"
|
|
29074
|
+
? () => \`section-count-\${count}\`
|
|
29075
|
+
: stableSectionClassName
|
|
29076
|
+
}
|
|
29077
|
+
sectionStyle={
|
|
29078
|
+
vary === "sectionStyle"
|
|
29079
|
+
? () => varyingMargin(count)
|
|
29080
|
+
: stableSectionStyle
|
|
29081
|
+
}
|
|
29082
|
+
/>
|
|
29083
|
+
)}
|
|
29084
|
+
</CountingExample>
|
|
29085
|
+
);`},similarTo:[],figmaUrl:null},Virtualizer:{id:"core-collections-virtualizer",breadcrumb:"Core/Collections/Virtualizer",importStatement:'import { Virtualizer } from "react-aria-components";',description:"`Virtualizer` renders large collections efficiently by keeping only the visible portion of the collection in the DOM. In baseline-ui it is typically paired with collection components such as `UNSAFE_ListBox`, `TreeView`, and `ImageGallery`.",documentation:`\`Virtualizer\` renders large collections efficiently by keeping only the visible portion of the collection in the DOM. In baseline-ui it is typically paired with collection components such as \`UNSAFE_ListBox\`, \`TreeView\`, and \`ImageGallery\`.
|
|
27826
29086
|
|
|
27827
29087
|
* Efficient rendering for very large collections
|
|
27828
29088
|
* Preset layout options for list boxes, tree views, and image galleries
|
|
@@ -30800,25 +32060,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
|
|
|
30800
32060
|
);
|
|
30801
32061
|
};
|
|
30802
32062
|
|
|
30803
|
-
export const PopoverContainedFocusExample = (
|
|
30804
|
-
|
|
30805
|
-
|
|
30806
|
-
|
|
30807
|
-
|
|
30808
|
-
|
|
30809
|
-
|
|
30810
|
-
<Dialog
|
|
30811
|
-
size="content"
|
|
30812
|
-
className={sprinkles({
|
|
30813
|
-
padding: "md",
|
|
30814
|
-
display: "flex",
|
|
30815
|
-
gap: "xl",
|
|
30816
|
-
flexDirection: "column",
|
|
30817
|
-
})}
|
|
30818
|
-
style={{
|
|
30819
|
-
width: 200,
|
|
30820
|
-
}}
|
|
30821
|
-
>
|
|
32063
|
+
export const PopoverContainedFocusExample = ({
|
|
32064
|
+
shouldContainFocus = true,
|
|
32065
|
+
}: {
|
|
32066
|
+
shouldContainFocus?: boolean;
|
|
32067
|
+
}) => {
|
|
32068
|
+
const content = (
|
|
32069
|
+
<>
|
|
30822
32070
|
<Text type="label">The focus is contained within the popover.</Text>
|
|
30823
32071
|
|
|
30824
32072
|
<TextInput
|
|
@@ -30830,10 +32078,55 @@ export const PopoverContainedFocusExample = () => {
|
|
|
30830
32078
|
label="Button"
|
|
30831
32079
|
style={{ width: "100%", justifyContent: "center" }}
|
|
30832
32080
|
/>
|
|
32081
|
+
</>
|
|
32082
|
+
);
|
|
32083
|
+
const contentClassName = sprinkles({
|
|
32084
|
+
padding: "md",
|
|
32085
|
+
display: "flex",
|
|
32086
|
+
gap: "xl",
|
|
32087
|
+
flexDirection: "column",
|
|
32088
|
+
});
|
|
32089
|
+
|
|
32090
|
+
const popover = (
|
|
32091
|
+
<Popover type="dialog">
|
|
32092
|
+
<PopoverTrigger>
|
|
32093
|
+
<ActionButton label="Open" />
|
|
32094
|
+
</PopoverTrigger>
|
|
32095
|
+
<PopoverContent
|
|
32096
|
+
shouldContainFocus={shouldContainFocus}
|
|
32097
|
+
isNonModal={!shouldContainFocus}
|
|
32098
|
+
>
|
|
32099
|
+
{shouldContainFocus ? (
|
|
32100
|
+
<Dialog
|
|
32101
|
+
size="content"
|
|
32102
|
+
className={contentClassName}
|
|
32103
|
+
style={{ width: 200 }}
|
|
32104
|
+
>
|
|
32105
|
+
{content}
|
|
30833
32106
|
</Dialog>
|
|
32107
|
+
) : (
|
|
32108
|
+
// No Dialog: useDialog opts the overlay into focus containment,
|
|
32109
|
+
// which disables the Tab-out restore path.
|
|
32110
|
+
<Box className={contentClassName} style={{ width: 200 }}>
|
|
32111
|
+
{content}
|
|
32112
|
+
</Box>
|
|
32113
|
+
)}
|
|
30834
32114
|
</PopoverContent>
|
|
30835
32115
|
</Popover>
|
|
30836
32116
|
);
|
|
32117
|
+
|
|
32118
|
+
// The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
|
|
32119
|
+
// here rebaselines its visual snapshot.
|
|
32120
|
+
if (shouldContainFocus) {
|
|
32121
|
+
return popover;
|
|
32122
|
+
}
|
|
32123
|
+
|
|
32124
|
+
return (
|
|
32125
|
+
<Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
|
|
32126
|
+
{popover}
|
|
32127
|
+
<ActionButton label="After" />
|
|
32128
|
+
</Box>
|
|
32129
|
+
);
|
|
30837
32130
|
};
|
|
30838
32131
|
|
|
30839
32132
|
export const PopoverWithScrollableViewportExample: React.FC<{
|
|
@@ -31960,25 +33253,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
|
|
|
31960
33253
|
);
|
|
31961
33254
|
};
|
|
31962
33255
|
|
|
31963
|
-
export const PopoverContainedFocusExample = (
|
|
31964
|
-
|
|
31965
|
-
|
|
31966
|
-
|
|
31967
|
-
|
|
31968
|
-
|
|
31969
|
-
|
|
31970
|
-
<Dialog
|
|
31971
|
-
size="content"
|
|
31972
|
-
className={sprinkles({
|
|
31973
|
-
padding: "md",
|
|
31974
|
-
display: "flex",
|
|
31975
|
-
gap: "xl",
|
|
31976
|
-
flexDirection: "column",
|
|
31977
|
-
})}
|
|
31978
|
-
style={{
|
|
31979
|
-
width: 200,
|
|
31980
|
-
}}
|
|
31981
|
-
>
|
|
33256
|
+
export const PopoverContainedFocusExample = ({
|
|
33257
|
+
shouldContainFocus = true,
|
|
33258
|
+
}: {
|
|
33259
|
+
shouldContainFocus?: boolean;
|
|
33260
|
+
}) => {
|
|
33261
|
+
const content = (
|
|
33262
|
+
<>
|
|
31982
33263
|
<Text type="label">The focus is contained within the popover.</Text>
|
|
31983
33264
|
|
|
31984
33265
|
<TextInput
|
|
@@ -31990,10 +33271,55 @@ export const PopoverContainedFocusExample = () => {
|
|
|
31990
33271
|
label="Button"
|
|
31991
33272
|
style={{ width: "100%", justifyContent: "center" }}
|
|
31992
33273
|
/>
|
|
33274
|
+
</>
|
|
33275
|
+
);
|
|
33276
|
+
const contentClassName = sprinkles({
|
|
33277
|
+
padding: "md",
|
|
33278
|
+
display: "flex",
|
|
33279
|
+
gap: "xl",
|
|
33280
|
+
flexDirection: "column",
|
|
33281
|
+
});
|
|
33282
|
+
|
|
33283
|
+
const popover = (
|
|
33284
|
+
<Popover type="dialog">
|
|
33285
|
+
<PopoverTrigger>
|
|
33286
|
+
<ActionButton label="Open" />
|
|
33287
|
+
</PopoverTrigger>
|
|
33288
|
+
<PopoverContent
|
|
33289
|
+
shouldContainFocus={shouldContainFocus}
|
|
33290
|
+
isNonModal={!shouldContainFocus}
|
|
33291
|
+
>
|
|
33292
|
+
{shouldContainFocus ? (
|
|
33293
|
+
<Dialog
|
|
33294
|
+
size="content"
|
|
33295
|
+
className={contentClassName}
|
|
33296
|
+
style={{ width: 200 }}
|
|
33297
|
+
>
|
|
33298
|
+
{content}
|
|
31993
33299
|
</Dialog>
|
|
33300
|
+
) : (
|
|
33301
|
+
// No Dialog: useDialog opts the overlay into focus containment,
|
|
33302
|
+
// which disables the Tab-out restore path.
|
|
33303
|
+
<Box className={contentClassName} style={{ width: 200 }}>
|
|
33304
|
+
{content}
|
|
33305
|
+
</Box>
|
|
33306
|
+
)}
|
|
31994
33307
|
</PopoverContent>
|
|
31995
33308
|
</Popover>
|
|
31996
33309
|
);
|
|
33310
|
+
|
|
33311
|
+
// The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
|
|
33312
|
+
// here rebaselines its visual snapshot.
|
|
33313
|
+
if (shouldContainFocus) {
|
|
33314
|
+
return popover;
|
|
33315
|
+
}
|
|
33316
|
+
|
|
33317
|
+
return (
|
|
33318
|
+
<Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
|
|
33319
|
+
{popover}
|
|
33320
|
+
<ActionButton label="After" />
|
|
33321
|
+
</Box>
|
|
33322
|
+
);
|
|
31997
33323
|
};
|
|
31998
33324
|
|
|
31999
33325
|
export const PopoverWithScrollableViewportExample: React.FC<{
|
|
@@ -32759,7 +34085,15 @@ iconTooltip?: ActionIconButtonProps["tooltip"]
|
|
|
32759
34085
|
placeholder="Select"
|
|
32760
34086
|
items={items}
|
|
32761
34087
|
isReadOnly
|
|
32762
|
-
value="square" />;`}
|
|
34088
|
+
value="square" />;`},{id:"core-forms-select-iconselect--without-icon",name:"Without Icon",snippet:`const WithoutIcon = () => <IconSelect
|
|
34089
|
+
aria-label="Choose Stroke Style"
|
|
34090
|
+
placeholder="Select"
|
|
34091
|
+
items={items}
|
|
34092
|
+
icon={undefined} />;`},{id:"core-forms-select-iconselect--with-default-open",name:"With Default Open",snippet:`const WithDefaultOpen = () => <IconSelect
|
|
34093
|
+
aria-label="Choose Stroke Style"
|
|
34094
|
+
placeholder="Select"
|
|
34095
|
+
items={items}
|
|
34096
|
+
defaultOpen />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
|
|
32763
34097
|
import React from "react";
|
|
32764
34098
|
import { useFilter } from "react-aria";
|
|
32765
34099
|
import { Autocomplete, Virtualizer } from "react-aria-components";
|
|
@@ -32768,11 +34102,16 @@ import { useListData } from "react-stately";
|
|
|
32768
34102
|
import { ActionButton } from "../../ActionButton";
|
|
32769
34103
|
import { items } from "../../ListBox/__tests__/testComponents";
|
|
32770
34104
|
import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
|
|
32771
|
-
import {
|
|
34105
|
+
import { itemsWithSectionTitles } from "../../UNSAFE_ListBox/__tests__/testComponents";
|
|
34106
|
+
import {
|
|
34107
|
+
ListLayout,
|
|
34108
|
+
VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS,
|
|
34109
|
+
} from "../../Virtualizer";
|
|
32772
34110
|
import { IconSelect } from "../IconSelect";
|
|
32773
34111
|
import { Select } from "../Select";
|
|
32774
34112
|
|
|
32775
34113
|
import type { IconSelectProps } from "../Select.types";
|
|
34114
|
+
import type { Key } from "@react-types/shared";
|
|
32776
34115
|
|
|
32777
34116
|
export const SelectExample: React.FC<
|
|
32778
34117
|
Omit<React.ComponentProps<typeof Select>, "items">
|
|
@@ -32780,13 +34119,28 @@ export const SelectExample: React.FC<
|
|
|
32780
34119
|
return (
|
|
32781
34120
|
<Select
|
|
32782
34121
|
placeholder="Choose an item"
|
|
32783
|
-
{...args}
|
|
32784
34122
|
optionClassName={(item) => item.label}
|
|
34123
|
+
{...args}
|
|
32785
34124
|
items={items}
|
|
32786
34125
|
/>
|
|
32787
34126
|
);
|
|
32788
34127
|
};
|
|
32789
34128
|
|
|
34129
|
+
// "Apples" is a substring of "Pineapples" \u2014 exercises exact string matching
|
|
34130
|
+
// in the tester's option() locator.
|
|
34131
|
+
const substringItems = [
|
|
34132
|
+
{ id: "apples", label: "Apples" },
|
|
34133
|
+
{ id: "pineapples", label: "Pineapples" },
|
|
34134
|
+
];
|
|
34135
|
+
|
|
34136
|
+
export const SelectSubstringLabelsExample: React.FC<
|
|
34137
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
34138
|
+
> = (args) => {
|
|
34139
|
+
return (
|
|
34140
|
+
<Select placeholder="Choose an item" {...args} items={substringItems} />
|
|
34141
|
+
);
|
|
34142
|
+
};
|
|
34143
|
+
|
|
32790
34144
|
export const SelectGetTargetRectExample: React.FC = () => {
|
|
32791
34145
|
const [called, setCalled] = React.useState(false);
|
|
32792
34146
|
|
|
@@ -32843,19 +34197,136 @@ export const SelectCustomTriggerExample: React.FC<
|
|
|
32843
34197
|
};
|
|
32844
34198
|
|
|
32845
34199
|
export const IconSelectExample: React.FC<
|
|
32846
|
-
Omit<IconSelectProps, "items" | "
|
|
34200
|
+
Omit<IconSelectProps, "items" | "aria-label">
|
|
32847
34201
|
> = (args) => {
|
|
32848
34202
|
return (
|
|
32849
34203
|
<IconSelect
|
|
32850
34204
|
placeholder="Choose an item"
|
|
34205
|
+
icon={EllipseIcon}
|
|
32851
34206
|
{...args}
|
|
32852
34207
|
items={items}
|
|
32853
|
-
icon={EllipseIcon}
|
|
32854
34208
|
aria-label="Aria Label"
|
|
32855
34209
|
/>
|
|
32856
34210
|
);
|
|
32857
34211
|
};
|
|
32858
34212
|
|
|
34213
|
+
export const VirtualSelectWithSectionHeadersExample: React.FC<
|
|
34214
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
34215
|
+
> = (args) => {
|
|
34216
|
+
return (
|
|
34217
|
+
<Virtualizer
|
|
34218
|
+
layout={ListLayout}
|
|
34219
|
+
layoutOptions={{
|
|
34220
|
+
rowHeight: 36,
|
|
34221
|
+
headingHeight: 40,
|
|
34222
|
+
}}
|
|
34223
|
+
>
|
|
34224
|
+
<Select
|
|
34225
|
+
aria-label="Choose an item"
|
|
34226
|
+
placeholder="Choose an item"
|
|
34227
|
+
showSectionHeader={true}
|
|
34228
|
+
{...args}
|
|
34229
|
+
items={itemsWithSectionTitles}
|
|
34230
|
+
defaultOpen={true}
|
|
34231
|
+
maxHeight={400}
|
|
34232
|
+
/>
|
|
34233
|
+
</Virtualizer>
|
|
34234
|
+
);
|
|
34235
|
+
};
|
|
34236
|
+
|
|
34237
|
+
export const SelectWithSectionHeadersExample: React.FC<
|
|
34238
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
34239
|
+
> = (args) => {
|
|
34240
|
+
return (
|
|
34241
|
+
<Select
|
|
34242
|
+
aria-label="Choose an item"
|
|
34243
|
+
placeholder="Choose an item"
|
|
34244
|
+
showSectionHeader={true}
|
|
34245
|
+
{...args}
|
|
34246
|
+
items={itemsWithSectionTitles}
|
|
34247
|
+
defaultOpen={true}
|
|
34248
|
+
/>
|
|
34249
|
+
);
|
|
34250
|
+
};
|
|
34251
|
+
|
|
34252
|
+
/**
|
|
34253
|
+
* Owns \`isOpen\` so the controlled-open contract can be driven from outside the
|
|
34254
|
+
* Select, with a sibling readout of the current state.
|
|
34255
|
+
*/
|
|
34256
|
+
export const ControlledOpenSelectExample: React.FC<
|
|
34257
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
34258
|
+
> = (args) => {
|
|
34259
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
34260
|
+
|
|
34261
|
+
return (
|
|
34262
|
+
<>
|
|
34263
|
+
<Select
|
|
34264
|
+
aria-label="Choose an item"
|
|
34265
|
+
placeholder="Choose an item"
|
|
34266
|
+
{...args}
|
|
34267
|
+
items={items}
|
|
34268
|
+
isOpen={isOpen}
|
|
34269
|
+
onOpenChange={setIsOpen}
|
|
34270
|
+
/>
|
|
34271
|
+
<button
|
|
34272
|
+
data-testid="open-externally"
|
|
34273
|
+
onClick={() => {
|
|
34274
|
+
setIsOpen(true);
|
|
34275
|
+
}}
|
|
34276
|
+
>
|
|
34277
|
+
Open
|
|
34278
|
+
</button>
|
|
34279
|
+
<span data-testid="open-state">{String(isOpen)}</span>
|
|
34280
|
+
</>
|
|
34281
|
+
);
|
|
34282
|
+
};
|
|
34283
|
+
|
|
34284
|
+
/**
|
|
34285
|
+
* Custom trigger that renders the multi-select half of the \`renderTrigger\`
|
|
34286
|
+
* payload \u2014 \`selectionMode\`, \`maxCount\` and \`onRemove\` \u2014 which the default
|
|
34287
|
+
* \`SelectButton\` otherwise keeps to itself.
|
|
34288
|
+
*/
|
|
34289
|
+
export const MultiSelectCustomTriggerExample: React.FC<
|
|
34290
|
+
Omit<React.ComponentProps<typeof Select>, "items">
|
|
34291
|
+
> = (args) => {
|
|
34292
|
+
return (
|
|
34293
|
+
<Select
|
|
34294
|
+
aria-label="Choose an item"
|
|
34295
|
+
selectionMode="multiple"
|
|
34296
|
+
{...args}
|
|
34297
|
+
items={items}
|
|
34298
|
+
renderTrigger={({
|
|
34299
|
+
buttonProps,
|
|
34300
|
+
ref,
|
|
34301
|
+
selectedValue,
|
|
34302
|
+
selectionMode,
|
|
34303
|
+
onRemove,
|
|
34304
|
+
maxCount,
|
|
34305
|
+
}) => (
|
|
34306
|
+
<div>
|
|
34307
|
+
<ActionButton
|
|
34308
|
+
{...buttonProps}
|
|
34309
|
+
ref={ref}
|
|
34310
|
+
label={\`\${selectionMode}|\${maxCount}|\${selectedValue?.length ?? 0}\`}
|
|
34311
|
+
variant="popover"
|
|
34312
|
+
/>
|
|
34313
|
+
<button
|
|
34314
|
+
data-testid="remove-first"
|
|
34315
|
+
onClick={() => {
|
|
34316
|
+
const first = selectedValue?.[0];
|
|
34317
|
+
if (first) {
|
|
34318
|
+
onRemove?.(new Set([first.id]));
|
|
34319
|
+
}
|
|
34320
|
+
}}
|
|
34321
|
+
>
|
|
34322
|
+
Remove first
|
|
34323
|
+
</button>
|
|
34324
|
+
</div>
|
|
34325
|
+
)}
|
|
34326
|
+
/>
|
|
34327
|
+
);
|
|
34328
|
+
};
|
|
34329
|
+
|
|
32859
34330
|
export const SelectWithVirtualizeAutocompleteExample: React.FC<
|
|
32860
34331
|
Omit<React.ComponentProps<typeof Select>, "items">
|
|
32861
34332
|
> = (args) => {
|
|
@@ -32879,6 +34350,27 @@ export const SelectWithVirtualizeAutocompleteExample: React.FC<
|
|
|
32879
34350
|
</Autocomplete>
|
|
32880
34351
|
</Virtualizer>
|
|
32881
34352
|
);
|
|
34353
|
+
};
|
|
34354
|
+
|
|
34355
|
+
/**
|
|
34356
|
+
* \`optionClassName\` reads component state, and \`items\` is module-level so React Aria's
|
|
34357
|
+
* per-item element cache applies. Pinned open so the state change comes from selecting an
|
|
34358
|
+
* option rather than from an outside press the popover would swallow.
|
|
34359
|
+
*/
|
|
34360
|
+
export const CountingSelectExample: React.FC = () => {
|
|
34361
|
+
const [selected, setSelected] = React.useState<Key | null>(null);
|
|
34362
|
+
|
|
34363
|
+
return (
|
|
34364
|
+
<Select
|
|
34365
|
+
aria-label="Counting select"
|
|
34366
|
+
items={items}
|
|
34367
|
+
isOpen={true}
|
|
34368
|
+
optionClassName={(item) =>
|
|
34369
|
+
item.id === selected ? "option-picked" : "option-plain"
|
|
34370
|
+
}
|
|
34371
|
+
onSelectionChange={setSelected}
|
|
34372
|
+
/>
|
|
34373
|
+
);
|
|
32882
34374
|
};`},similarTo:[],figmaUrl:null},IconSlider:{id:"core-forms-slider-iconslider",importStatement:'import { IconSlider } from "@baseline-ui/core";',description:"",documentation:null,props:`interface IconSliderProps {
|
|
32883
34375
|
/**
|
|
32884
34376
|
* @deprecated Do not use in new components. This is a legacy block
|
|
@@ -32963,7 +34455,7 @@ popoverContentProps?: Omit<PopoverContentProps, "children">
|
|
|
32963
34455
|
formatValue={(value: number) => \`\${value} pt\`}
|
|
32964
34456
|
aria-label="Icon slider"
|
|
32965
34457
|
onTriggerPress={fn()}
|
|
32966
|
-
isDisabled />;`}],implementation:""},similarTo:[],figmaUrl:null}};var
|
|
34458
|
+
isDisabled />;`}],implementation:""},similarTo:[],figmaUrl:null}};var o={gettingStarted:`# Getting started
|
|
32967
34459
|
|
|
32968
34460
|
Install the packages required for using Baseline UI with the following command:
|
|
32969
34461
|
|
|
@@ -32989,6 +34481,21 @@ import { ActionButton, I18nProvider, ThemeProvider } from "@baseline-ui/core";
|
|
|
32989
34481
|
|
|
32990
34482
|
It\u2019s important to wrap your application with the \`ThemeProvider\` and \`I18nProvider\` components. The \`ThemeProvider\` component provides the theme to all the components in the tree, and the \`I18nProvider\` component provides the locale to all the components in the tree.
|
|
32991
34483
|
|
|
34484
|
+
### Translations
|
|
34485
|
+
|
|
34486
|
+
Components render English out of the box. Translations ship as one JSON file per locale so projects that only need English don\u2019t pay for the other 32 \u2014 import the ones you need and pass them to \`I18nProvider\`:
|
|
34487
|
+
|
|
34488
|
+
\`\`\`jsx
|
|
34489
|
+
import { I18nProvider } from "@baseline-ui/core";
|
|
34490
|
+
import de from "@baseline-ui/core/intl/de.json";
|
|
34491
|
+
|
|
34492
|
+
<I18nProvider locale="de-DE" messages={{ de }}>
|
|
34493
|
+
<YourApp />
|
|
34494
|
+
</I18nProvider>;
|
|
34495
|
+
\`\`\`
|
|
34496
|
+
|
|
34497
|
+
Any string a catalog omits falls back to English. See [Internationalization](?path=/docs/internationalization--docs) for the full list of locales and message IDs, lazy loading, and overriding individual strings.
|
|
34498
|
+
|
|
32992
34499
|
### Styling
|
|
32993
34500
|
|
|
32994
34501
|
Import the following files at the top of your stylesheet to use the Baseline UI styles:
|
|
@@ -33257,22 +34764,24 @@ function App() {
|
|
|
33257
34764
|
}
|
|
33258
34765
|
\`\`\``,internationalization:`# Internationalization
|
|
33259
34766
|
|
|
33260
|
-
It\u2019s important the content of your website is accessible to as many people as possible. This includes people who speak different languages and people who use screen readers. Internationalization (i18n) is the process of making your website accessible to people who speak different languages. Baseline UI supports internationalization for many of the components out of the box
|
|
34767
|
+
It\u2019s important the content of your website is accessible to as many people as possible. This includes people who speak different languages and people who use screen readers. Internationalization (i18n) is the process of making your website accessible to people who speak different languages. Baseline UI supports internationalization for many of the components out of the box: localization of dates and times, number formatting, currency formatting, collation, and text direction all follow the active locale with no setup.
|
|
34768
|
+
|
|
34769
|
+
Translated strings are the one part you opt into. Components render English until you import a catalog and pass it to \`I18nProvider\`, so an application that ships in English carries none of the other locales in its bundle. See [Loading a translation catalog](#loading-a-translation-catalog).
|
|
33261
34770
|
|
|
33262
34771
|
## Localization
|
|
33263
34772
|
|
|
33264
|
-
Localization is the process of translating your website into different languages. Baseline UI supports localization of the built-in strings, dates and times, number/currency formatting, collating and sorting, text search, and more. It\u2019s built on top of [react-aria](https://react-spectrum.adobe.com/react-aria),
|
|
34773
|
+
Localization is the process of translating your website into different languages. Baseline UI supports localization of the built-in strings, dates and times, number/currency formatting, collating and sorting, text search, and more. It\u2019s built on top of [react-aria](https://react-spectrum.adobe.com/react-aria), whose own strings \u2014 the announcements a date picker or a drag handle makes to a screen reader \u2014 are bundled and translated for you. Baseline UI\u2019s strings are the ones you load a catalog for, and you can add your own on top.
|
|
33265
34774
|
|
|
33266
|
-
Baseline UI automatically detects a user\u2019s preferred language from the browser, and it uses that language for all localization. It\u2019s possible to override the language used for localization by providing a \`locale\` prop to the \`
|
|
34775
|
+
Baseline UI automatically detects a user\u2019s preferred language from the browser, and it uses that language for all localization. It\u2019s possible to override the language used for localization by providing a \`locale\` prop to the \`I18nProvider\` component.
|
|
33267
34776
|
|
|
33268
|
-
See [\`useMessageFormatter\`](https://react-spectrum.adobe.com/react-aria/useMessageFormatter.html), [\`DateFormat\`](/?path=/docs/utilities-dateformat--docs), [\`NumberFormat\`](/?path=/docs/utilities-numberformat--docs), and [\`useCollator\`](https://react-spectrum.adobe.com/react-aria/useCollator.html) for more information about using our internationalization hooks.
|
|
34777
|
+
See [\`useMessageFormatter\`](https://react-spectrum.adobe.com/react-aria/useMessageFormatter.html), [\`DateFormat\`](/?path=/docs/core-utilities-dateformat--docs), [\`NumberFormat\`](/?path=/docs/core-utilities-numberformat--docs), and [\`useCollator\`](https://react-spectrum.adobe.com/react-aria/useCollator.html) for more information about using our internationalization hooks.
|
|
33269
34778
|
|
|
33270
34779
|
### Example
|
|
33271
34780
|
|
|
33272
|
-
The rootmost element of your application should define the [\`lang\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [\`dir\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes so that the browser knows which language and direction the user interface should be rendered in. This can be done with the \`useLocale\` hook
|
|
34781
|
+
The rootmost element of your application should define the [\`lang\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [\`dir\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes so that the browser knows which language and direction the user interface should be rendered in. This can be done with the \`useLocale\` hook:
|
|
33273
34782
|
|
|
33274
34783
|
\`\`\`jsx
|
|
33275
|
-
import { useLocale } from "
|
|
34784
|
+
import { useLocale } from "@baseline-ui/core";
|
|
33276
34785
|
|
|
33277
34786
|
function YourApp() {
|
|
33278
34787
|
let { locale, direction } = useLocale();
|
|
@@ -33285,38 +34794,242 @@ function YourApp() {
|
|
|
33285
34794
|
}
|
|
33286
34795
|
\`\`\`
|
|
33287
34796
|
|
|
33288
|
-
You can also override the language used for localization with the [\`I18nProvider\`](/?path=/docs/utilities-i18nprovider--docs) component:
|
|
34797
|
+
You can also override the language used for localization with the [\`I18nProvider\`](/?path=/docs/core-utilities-i18nprovider--docs) component:
|
|
33289
34798
|
|
|
33290
34799
|
\`\`\`jsx
|
|
33291
|
-
import { I18nProvider } from "
|
|
34800
|
+
import { I18nProvider } from "@baseline-ui/core";
|
|
33292
34801
|
|
|
33293
34802
|
<I18nProvider locale="fr-FR">
|
|
33294
34803
|
<YourApp />
|
|
33295
34804
|
</I18nProvider>;
|
|
33296
34805
|
\`\`\`
|
|
33297
34806
|
|
|
34807
|
+
Setting \`locale\` alone switches date, number and collation formatting, and flips text direction for RTL languages. It does not translate Baseline UI\u2019s own strings \u2014 for that, load a catalog.
|
|
34808
|
+
|
|
34809
|
+
### Loading a translation catalog
|
|
34810
|
+
|
|
34811
|
+
Import the catalogs for the languages you ship and hand them to \`I18nProvider\`:
|
|
34812
|
+
|
|
34813
|
+
\`\`\`jsx
|
|
34814
|
+
import { I18nProvider } from "@baseline-ui/core";
|
|
34815
|
+
import de from "@baseline-ui/core/intl/de.json";
|
|
34816
|
+
import fr from "@baseline-ui/core/intl/fr.json";
|
|
34817
|
+
|
|
34818
|
+
// Hoisted out of render: a fresh object each render rebuilds the message
|
|
34819
|
+
// formatter, so every string reformats on every pass.
|
|
34820
|
+
const messages = { de, fr };
|
|
34821
|
+
|
|
34822
|
+
<I18nProvider locale="de-DE" messages={messages}>
|
|
34823
|
+
<YourApp />
|
|
34824
|
+
</I18nProvider>;
|
|
34825
|
+
\`\`\`
|
|
34826
|
+
|
|
34827
|
+
Each file is a flat map of \`bui.*\` ID to translated string, so your bundler includes only
|
|
34828
|
+
the locales you actually import. The key you file a catalog under is matched against the
|
|
34829
|
+
active locale exactly first, then by language, then by any other region of that language \u2014
|
|
34830
|
+
so \`{ de }\` answers for \`de\`, \`de-DE\` and \`de-AT\` alike, and \`{ de, "de-AT" }\` under
|
|
34831
|
+
\`de-AT\` uses the Austrian entry.
|
|
34832
|
+
|
|
34833
|
+
TypeScript needs \`resolveJsonModule\` enabled to import the files, and Node ESM needs a
|
|
34834
|
+
\`with { type: "json" }\` import attribute.
|
|
34835
|
+
|
|
34836
|
+
Key each catalog with a BCP 47 locale ID. An underscored key (\`de_DE\`) is read as its
|
|
34837
|
+
hyphenated form, so both spellings resolve \u2014 but supplying the same locale under both is
|
|
34838
|
+
reported once, and the hyphenated entry wins.
|
|
34839
|
+
|
|
34840
|
+
To avoid shipping every language up front, put each catalog behind a dynamic \`import()\`
|
|
34841
|
+
and load one when the user picks a language. Write the specifiers out in full \u2014 a
|
|
34842
|
+
template literal over a bare package subpath is not statically analyzable, so bundlers
|
|
34843
|
+
cannot split on it:
|
|
34844
|
+
|
|
34845
|
+
\`\`\`jsx
|
|
34846
|
+
const catalogs = {
|
|
34847
|
+
de: () => import("@baseline-ui/core/intl/de.json"),
|
|
34848
|
+
fr: () => import("@baseline-ui/core/intl/fr.json"),
|
|
34849
|
+
};
|
|
34850
|
+
|
|
34851
|
+
const [messages, setMessages] = useState({});
|
|
34852
|
+
|
|
34853
|
+
async function selectLocale(locale) {
|
|
34854
|
+
const catalog = await catalogs[locale]();
|
|
34855
|
+
|
|
34856
|
+
setMessages({ [locale]: catalog.default });
|
|
34857
|
+
}
|
|
34858
|
+
\`\`\`
|
|
34859
|
+
|
|
34860
|
+
An ID your catalog omits falls back to that component's English \`defaultMessage\`, so a
|
|
34861
|
+
partial catalog is a valid one \u2014 you are never forced to translate a string before you
|
|
34862
|
+
ship it. Outside production, each such ID is reported once in the console.
|
|
34863
|
+
|
|
34864
|
+
If you supply catalogs but none of them matches the active locale \u2014 \`{ de }\` while the
|
|
34865
|
+
locale is \`ja-JP\`, say \u2014 every string falls back to English and that is reported once for
|
|
34866
|
+
the locale rather than once per ID. It means the catalog keys and the locale disagree,
|
|
34867
|
+
which no per-ID warning can tell you. It stays quiet when the active locale is itself
|
|
34868
|
+
English, since falling back to English is then the intended result. Supplying no catalogs
|
|
34869
|
+
at all is the supported English-only mode and stays silent.
|
|
34870
|
+
|
|
34871
|
+
Only \`bui.*\` IDs are reported. An app that routes its own strings through
|
|
34872
|
+
[\`useI18n\`](#your-own-strings) is missing no translation Baseline UI can tell it how to add.
|
|
34873
|
+
|
|
34874
|
+
### Controlling the warnings
|
|
34875
|
+
|
|
34876
|
+
The warnings above \u2014 the missing-ID, no-matching-catalog, duplicate-locale-key and
|
|
34877
|
+
[bare-ID deprecation](#migrating-from-bare-ids) ones \u2014 are development guidance, so they
|
|
34878
|
+
are off when \`process.env.NODE_ENV === "production"\`. That keeps a consumer who never
|
|
34879
|
+
touched \`messages\` from logging Baseline UI's internal IDs to their end users. Set
|
|
34880
|
+
\`shouldLogMissingMessages\` to force them either way:
|
|
34881
|
+
|
|
34882
|
+
\`\`\`jsx
|
|
34883
|
+
// Surface them in a production build \u2014 e.g. a staging deploy checking catalog coverage.
|
|
34884
|
+
<I18nProvider locale="de-DE" messages={messages} shouldLogMissingMessages>
|
|
34885
|
+
<YourApp />
|
|
34886
|
+
</I18nProvider>;
|
|
34887
|
+
|
|
34888
|
+
// Or silence them in development, while a catalog is still being filled in.
|
|
34889
|
+
<I18nProvider
|
|
34890
|
+
locale="de-DE"
|
|
34891
|
+
messages={messages}
|
|
34892
|
+
shouldLogMissingMessages={false}
|
|
34893
|
+
>
|
|
34894
|
+
<YourApp />
|
|
34895
|
+
</I18nProvider>;
|
|
34896
|
+
\`\`\`
|
|
34897
|
+
|
|
34898
|
+
Each distinct warning is logged once and reset when the locale changes, so switching
|
|
34899
|
+
languages re-reports what the new locale is missing.
|
|
34900
|
+
|
|
34901
|
+
### Your own strings
|
|
34902
|
+
|
|
34903
|
+
\`useI18n\` is the same hook the components use, so you can put your application's strings
|
|
34904
|
+
through it and get one formatter for both:
|
|
34905
|
+
|
|
34906
|
+
\`\`\`tsx
|
|
34907
|
+
import { defineMessages, useI18n } from "@baseline-ui/core";
|
|
34908
|
+
|
|
34909
|
+
const messages = defineMessages({
|
|
34910
|
+
greeting: {
|
|
34911
|
+
id: "myApp.greeting",
|
|
34912
|
+
defaultMessage: "Hello, {name}",
|
|
34913
|
+
description:
|
|
34914
|
+
"Greeting on the dashboard header. {name} is the user's first name.",
|
|
34915
|
+
},
|
|
34916
|
+
});
|
|
34917
|
+
|
|
34918
|
+
// Hoisted for the same reason as the catalogs above.
|
|
34919
|
+
const catalogs = { de: { "myApp.greeting": "Hallo, {name}" } };
|
|
34920
|
+
|
|
34921
|
+
function Header({ name }) {
|
|
34922
|
+
const { formatMessage } = useI18n(catalogs);
|
|
34923
|
+
|
|
34924
|
+
return <h1>{formatMessage(messages.greeting, { name })}</h1>;
|
|
34925
|
+
}
|
|
34926
|
+
\`\`\`
|
|
34927
|
+
|
|
34928
|
+
The catalogs you pass to \`useI18n\` are merged per locale with the ones on \`I18nProvider\`,
|
|
34929
|
+
and the provider wins on a shared key \u2014 so an application can hold its own strings locally
|
|
34930
|
+
while a host app still overrides any \`bui.*\` string from above.
|
|
34931
|
+
|
|
33298
34932
|
### Supported locales
|
|
33299
34933
|
|
|
33300
|
-
|
|
34934
|
+
A catalog ships for each of these locales, with an entry for every message ID:
|
|
34935
|
+
|
|
34936
|
+
### Message IDs
|
|
34937
|
+
|
|
34938
|
+
Every string Baseline UI can render is keyed by a \`bui.<component>.<key>\` ID. The same
|
|
34939
|
+
list is exported as a value, so you can derive translation coverage in a test instead of
|
|
34940
|
+
committing a copy that goes stale on the next upgrade:
|
|
34941
|
+
|
|
34942
|
+
\`\`\`ts
|
|
34943
|
+
import { messageIds } from "@baseline-ui/core";
|
|
34944
|
+
|
|
34945
|
+
const uncovered = messageIds.filter((id) => !(id in ourCatalog.en));
|
|
34946
|
+
\`\`\`
|
|
33301
34947
|
|
|
33302
|
-
|
|
34948
|
+
Below is every ID with the English string it renders. For the translator context that goes
|
|
34949
|
+
with each one, read \`@baseline-ui/core/messages.json\` \u2014 the extracted source messages keyed
|
|
34950
|
+
by ID.
|
|
33303
34951
|
|
|
33304
|
-
|
|
34952
|
+
### Translating them yourself
|
|
34953
|
+
|
|
34954
|
+
If you localize the \`bui.*\` strings in your own pipeline rather than shipping our catalogs,
|
|
34955
|
+
hand your translators \`@baseline-ui/core/messages.json\` \u2014 the extracted source messages,
|
|
34956
|
+
keyed by ID and shaped the way a TMS expects:
|
|
34957
|
+
|
|
34958
|
+
\`\`\`json
|
|
34959
|
+
{
|
|
34960
|
+
"bui.select.more": {
|
|
34961
|
+
"defaultMessage": "+{count} more",
|
|
34962
|
+
"description": "Tag shown on the select trigger standing in for the selected options that did not fit \u2014 renders as '+3 more'. ..."
|
|
34963
|
+
}
|
|
34964
|
+
}
|
|
34965
|
+
\`\`\`
|
|
34966
|
+
|
|
34967
|
+
The \`description\` is the context a translator needs and cannot infer from the string
|
|
34968
|
+
alone \u2014 part of speech, sense, and where it renders. Translating \`bui.editor.mention\`
|
|
34969
|
+
without it yields the verb "to mention" where the noun label belongs, and
|
|
34970
|
+
\`bui.editor.shortcut\` becomes a desktop shortcut file rather than a key combination.
|
|
33305
34971
|
|
|
33306
34972
|
## Overriding Component Translations
|
|
33307
34973
|
|
|
33308
34974
|
You can override any built-in component string by passing its \`bui.*\`-namespaced key to
|
|
33309
34975
|
\`I18nProvider.messages\`. Only the keys you provide are overridden \u2014 all other strings
|
|
33310
|
-
fall back to each component's
|
|
33311
|
-
|
|
34976
|
+
fall back to each component's English \`defaultMessage\`. Spread a shipped catalog and
|
|
34977
|
+
override on top of it to change a single string:
|
|
34978
|
+
|
|
34979
|
+
\`\`\`jsx
|
|
34980
|
+
import de from "@baseline-ui/core/intl/de.json";
|
|
34981
|
+
|
|
34982
|
+
<I18nProvider
|
|
34983
|
+
locale="de-DE"
|
|
34984
|
+
messages={{ de: { ...de, "bui.imageDropZone.selectImage": "Bild w\xE4hlen" } }}
|
|
34985
|
+
>
|
|
34986
|
+
<YourApp />
|
|
34987
|
+
</I18nProvider>;
|
|
34988
|
+
\`\`\`
|
|
33312
34989
|
|
|
33313
34990
|
Each component's documentation lists its overridable keys and shows an example. See:
|
|
33314
34991
|
|
|
33315
|
-
* [Pagination](?path=/docs/
|
|
33316
|
-
* [ColorInput](?path=/docs/
|
|
33317
|
-
* [ImageDropZone](?path=/docs/
|
|
33318
|
-
* [FreehandCanvas](?path=/docs/
|
|
33319
|
-
* [InlineAlert](?path=/docs/
|
|
34992
|
+
* [Pagination](?path=/docs/core-forms-pagination--docs#translations)
|
|
34993
|
+
* [ColorInput](?path=/docs/core-forms-colorinput--docs#translations)
|
|
34994
|
+
* [ImageDropZone](?path=/docs/core-forms-imagedropzone--docs#translations)
|
|
34995
|
+
* [FreehandCanvas](?path=/docs/core-miscellaneous-freehandcanvas--docs#translations)
|
|
34996
|
+
* [InlineAlert](?path=/docs/core-status-inlinealert--docs#translations)
|
|
34997
|
+
|
|
34998
|
+
### Migrating from bare IDs
|
|
34999
|
+
|
|
35000
|
+
Older catalogs key off the bare tail of an ID (\`close\` rather than \`bui.drawer.close\`).
|
|
35001
|
+
That still resolves, with a deprecation warning, but it will be removed in a future
|
|
35002
|
+
major \u2014 and it is ambiguous: several bare IDs are claimed by more than one component,
|
|
35003
|
+
so a single entry silently translates all of them. That happens to work in English, but
|
|
35004
|
+
it forecloses any language where the word inflects differently per context.
|
|
35005
|
+
|
|
35006
|
+
\`legacyMessageIdMap\` maps each bare ID to every namespaced ID that claims it, so a catalog
|
|
35007
|
+
can be re-keyed mechanically. Keep each original key alongside the IDs it maps to \u2014 a bare
|
|
35008
|
+
key may also be one your own app looks up, and Baseline UI resolves the namespaced entry
|
|
35009
|
+
either way:
|
|
35010
|
+
|
|
35011
|
+
\`\`\`ts
|
|
35012
|
+
import { legacyMessageIdMap } from "@baseline-ui/core";
|
|
35013
|
+
|
|
35014
|
+
const migrated = Object.fromEntries(
|
|
35015
|
+
Object.entries(oldCatalog).flatMap(([key, value]) => [
|
|
35016
|
+
[key, value],
|
|
35017
|
+
...(legacyMessageIdMap[key] ?? []).map((id) => [id, value]),
|
|
35018
|
+
]),
|
|
35019
|
+
);
|
|
35020
|
+
\`\`\`
|
|
35021
|
+
|
|
35022
|
+
Review the ambiguous ones by hand \u2014 each deserves its own translation:
|
|
35023
|
+
|
|
35024
|
+
| Bare ID | Also translates |
|
|
35025
|
+
| ----------- | ---------------------------------------------------------------------------------------- |
|
|
35026
|
+
| \`close\` | \`bui.alertDialog.close\`, \`bui.drawer.close\`, \`bui.editor.close\`, \`bui.inlineAlert.close\` |
|
|
35027
|
+
| \`more\` | \`bui.buttonSelect.more\`, \`bui.select.more\`, \`bui.toolbar.more\` |
|
|
35028
|
+
| \`cancel\` | \`bui.colorInput.cancel\`, \`bui.editor.cancel\`, \`bui.imageGallery.cancel\` |
|
|
35029
|
+
| \`redo\` | \`bui.editor.redo\`, \`bui.freehandCanvas.redo\` |
|
|
35030
|
+
| \`selectAll\` | \`bui.editor.selectAll\`, \`bui.select.selectAll\` |
|
|
35031
|
+
| \`undo\` | \`bui.editor.undo\`, \`bui.freehandCanvas.undo\` |
|
|
35032
|
+
| \`loading\` | \`bui.fileList.loading\`, \`bui.imageGallery.loading\`, \`bui.table.loading\` |`,styling:`# Styling with Sprinkles
|
|
33320
35033
|
|
|
33321
35034
|
Baseline UI provides a powerful, type-safe styling system built on top of [vanilla-extract sprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/).
|
|
33322
35035
|
|
|
@@ -34226,7 +35939,7 @@ padding={[null, "lg", "xl"]}
|
|
|
34226
35939
|
|
|
34227
35940
|
* [vanilla-extract sprinkles documentation](https://vanilla-extract.style/documentation/packages/sprinkles/) - Learn about the underlying sprinkles framework
|
|
34228
35941
|
* [Box component documentation](/docs/core-utilities-box--docs) - Detailed information about the Box component
|
|
34229
|
-
* [Theme documentation](/docs/theming--docs) - Learn about Baseline UI's theming system`};var c={"8":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","ChevronRightFilledIcon","ChevronRightIcon","EllipseIcon","MinusIcon","PlusIcon","XIcon"],"12":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","EditIcon","EllipseIcon","EnterKeyIcon","LockFilledIcon","LockIcon","MinusIcon","MoreVIcon","MoreIcon","PlaceholderIcon","PlusIcon","SearchIcon","SizeIcon","TrashIcon","XIcon","ZoomIcon"],"16":["AlignBottomIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDiagonalTopLeftBottomRightIcon","ArrowDownCircleFilledIcon","ArrowDownIcon","ArrowIcon","ArrowLeftRightIcon","ArrowRightIcon","ArrowUpArrowDownIcon","ArrowUpIcon","AtIcon","AttachmentsIcon","AvatarIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BulletListIcon","CalendarIcon","CaretLeftIcon","CaretRightIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","CircleFilledIcon","ClockIcon","CopyIcon","CustomizeIcon","DocumentEditIcon","DownloadIcon","DuplicateIcon","EditIcon","ElipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FilterAltIcon","FolderIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FullScreenIcon","HelpCircleIcon","HelpIcon","HereIcon","HideIcon","HighlightTextAltIcon","HighlightTextIcon","HorizontalScrollIcon","ImageIcon","InfoCircleFilledIcon","InsertIcon","ItalicIcon","LightBulbIcon","LineIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MeasureIcon","MinusIcon","MoreIcon","MoreVerticalIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","NumberedListIcon","OpenIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageVerticalScrollIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PlaceholderIcon","PlayIcon","PlusIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","ReadOnlyIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedoIcon","RemoveFormattingIcon","ReorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RulerIcon","SearchIcon","SettingsIcon","ShowIcon","SlashCommandsIcon","SoundRecordIcon","StampIcon","StarFilledIcon","StarIcon","StrikeoutTextAltIcon","TableCellIcon","TableColumnIcon","TableHeaderIcon","TableIcon","TableRowIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextDecreaseIndentIcon","TextIcon","TextIncreaseIndentIcon","TextMarkIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UndoIcon","UnlockIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WindowedIcon","WorkflowIcon","XCircleFilledIcon","XIcon"],"20":["AddPageIcon","AnonymousIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpCircleFilledIcon","AtIcon","AvatarFilledIcon","BoldIcon","CalloutIcon","CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ClockIcon","CollapseIcon","CommentIcon","CopyIcon","CutIcon","DistanceIcon","DownloadIcon","DuplicateIcon","EditIcon","EllipseIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorAlternativeCircleIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FormDateIcon","FormSignatureIcon","FormTextFieldIcon","HelpCircleIcon","HighlightTextIcon","HomeIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","ItalicIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MinusIcon","MoreIcon","MoreVerticalIcon","MoveIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OpenIcon","PageMoveLeftIcon","PageMoveRightIcon","PagesInsertIcon","PasteIcon","PipetteIcon","PlusIcon","PrintIcon","RotateClockwiseIcon","SearchIcon","SettingsIcon","ShapeIcon","ShareIcon","SoundIcon","SoundRecordIcon","StarFilledIcon","StarIcon","StyleIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UploadIcon","WarningFilledIcon","WarningIcon","XCircleFilledIcon","XCircleIcon","XIcon"],"24":["AddNoteCloudIcon","AddNoteIcon","AddTextSerifIcon","AiIcon","AirplaneIcon","AlignBottomIcon","AlignHorizontalCenterIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDownIcon","ArrowIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpIcon","AtIcon","AttachmentIcon","AvatarFilledIcon","AvatarIcon","BlendModeIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BorderColorIcon","BottomBorderIcon","BulletListIcon","CalibrateIcon","CaptureAddIcon","CaretDownIcon","CaretIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ChevronListIcon","ClockIcon","CloudyBorderIcon","CollapseIcon","ColorPaletteIcon","ColorSwatchIcon","CommentIcon","CommentInSidebarIcon","CommentOnPageIcon","CompareDocumentsIcon","CopyIcon","CopyPageIcon","CropIcon","CustomizeIcon","CutIcon","DateModifiedIcon","DatePlusIcon","DebugIcon","DocumentArrowDownCircleIcon","DocumentArrowDownIcon","DocumentArrowRightIcon","DocumentFilledIcon","DocumentLockIcon","DocumentPdfIcon","DownloadIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","DuplicateIcon","EditAnnotationsIcon","EditContentIcon","EditDocumentIcon","EditIcon","EditThumbnailsIcon","EllipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","EndCapArrowFilledIcon","EndCapArrowIcon","EndCapChevronFilledIcon","EndCapChevronIcon","EndCapCircleIcon","EndCapDiamondIcon","EndCapNoneIcon","EndCapSlantedIcon","EndCapSquareIcon","EndCapStraightIcon","EraserIcon","ErrorAltCircleFilledIcon","ErrorAltIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","ExpandVerticalIcon","FillColorIcon","FilterIcon","FitToHeightIcon","FivePagesHorizontalFilledIcon","FivePagesVerticalFilledIcon","FolderAddIcon","FolderIcon","FontListIcon","FontSizeIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormPageIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FormTwoRadioButtonsIcon","FourPagesGridFilledIcon","FourPagesHorizontalFilledIcon","FourPagesStackedFilledIcon","FourPagesVerticalFilledIcon","GroupIcon","HamburgerMenuIcon","HandIcon","HeartIcon","HideIcon","HideRevealIcon","HighlightTextIcon","HomeIcon","HorizontalScollIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","InitialsIcon","InnerHorizontalBorderIcon","InnerVerticalBorderIcon","InsertIcon","ItalicIcon","LayerBottomIcon","LayerDownIcon","LayerTopIcon","LayerUpIcon","LayersIcon","LeftBindingIcon","LeftBorderIcon","LineCapsIcon","LineIcon","LineSpacingIcon","LineStyleCloudyIcon","LineStyleDashedDoubleDashIcon","LineStyleDashedDoubleGapIcon","LineStyleDashedQuadrupleDashIcon","LineStyleDashedSingleGapIcon","LineStyleIcon","LineStyleSolidIcon","LineWidthIcon","LinkIcon","LockFilledIcon","LockIcon","MagicIcon","MagicPenIcon","MailIcon","MarkupIcon","MarqueeZoomIcon","MeasureIcon","MergeIcon","MessageCloudIcon","MinusIcon","MoonIcon","MoreCircleIcon","MoreIcon","MoreVerticalIcon","MoveAllDirectionsIcon","MoveLeftIcon","MoveLeftRightIcon","MoveRightIcon","MultiplePagesIcon","NonEditableIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OcrIcon","OpacityIcon","PageAddIcon","PageCurlIcon","PageDuplicateIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLandscapeIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageMoveLeftIcon","PageMoveRightIcon","PageNumberCircleIcon","PageNumberIcon","PagePortraitIcon","PageRemoveIcon","PageVerticalScrollIcon","PagesInsertAltIcon","PagesInsertIcon","PagesNewFromSelectionAltIcon","PagesNewFromSelectionIcon","PagesSelectAllIcon","PagesSelectNoneIcon","PasteBoardIcon","PastePageIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PinDropFilledIcon","PinDropIcon","PipetteIcon","PlayIcon","PlusCircleFilledIcon","PlusCircleIcon","PlusIcon","PointerIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","PrecisionIcon","PrintIcon","PrivateModeIcon","PushPinIcon","QuestionmarkCircleIcon","ReaderViewIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedactIcon","RedactRectangleIcon","RedactTextHighlighterIcon","RedactionTextRepeatingIcon","RedactionTextSingleIcon","RedoAllIcon","RedoIcon","RegexIcon","ReplaceIcon","RightBindingIcon","RightBorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RotateObjectClockwiseIcon","RotateObjectCounterClockwiseIcon","RulerIcon","ScaleIcon","SearchCircleIcon","SearchIcon","SearchSelectionIcon","SelectAllIcon","SelectionToolIcon","SettingsIcon","ShapesIcon","ShareAltIcon","ShareIcon","ShieldAddIcon","ShieldCheckmarkIcon","ShieldWarningIcon","ShieldXIcon","ShowIcon","SidebarIcon","SignOutIcon","SignatureDigitalIcon","SignatureIcon","SinglePageFilledIcon","SoundIcon","SquigglyTextIcon","StampAddIcon","StampIcon","StarFilledIcon","StarIcon","StartCapArrowFilledIcon","StartCapArrowIcon","StartCapChevronFilledIcon","StartCapChevronIcon","StartCapCircleIcon","StartCapDiamondIcon","StartCapNoneIcon","StartCapSlantedIcon","StartCapSquareIcon","StartCapStraightIcon","StrikeoutTextIcon","StyleFilledIcon","StyleIcon","StylusFilledIcon","StylusIcon","SunIcon","TableCellIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextColorIcon","TextIcon","TextPropertiesHideIcon","TextPropertiesShowIcon","TextSerifIcon","TextSmallerIcon","ThreePagesHorizontalFilledIcon","ThreePagesStackedFilledIcon","ThreePagesVerticalFilledIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TopBorderIcon","TrashIcon","TwoPagesHorizontalFilledIcon","TwoPagesVerticalFilledIcon","TypeTextIcon","UnderlineIcon","UnderlineTextIcon","UndoAllIcon","UndoIcon","UndoRedoIcon","UngroupIcon","UnlockIcon","UploadIcon","UserIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WidgetIcon","WorkflowIcon","XCircleFilledIcon","XCircleIcon","XIcon","ZoomInIcon","ZoomOutIcon"],"36":["ArrowRight","Check","Circle","Cross","Help","Inset","Key","NewParagraphAlt","NewParagraph","Note","PointerRight","SpeechBubble","Star"]};var p={version:"1.2.0"};var u=`
|
|
35942
|
+
* [Theme documentation](/docs/theming--docs) - Learn about Baseline UI's theming system`};var c={"8":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","ChevronRightFilledIcon","ChevronRightIcon","EllipseIcon","MinusIcon","PlusIcon","XIcon"],"12":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","EditIcon","EllipseIcon","EnterKeyIcon","LockFilledIcon","LockIcon","MinusIcon","MoreVIcon","MoreIcon","PlaceholderIcon","PlusIcon","SearchIcon","SizeIcon","TrashIcon","XIcon","ZoomIcon"],"16":["AlignBottomIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDiagonalTopLeftBottomRightIcon","ArrowDownCircleFilledIcon","ArrowDownIcon","ArrowIcon","ArrowLeftRightIcon","ArrowRightIcon","ArrowUpArrowDownIcon","ArrowUpIcon","AtIcon","AttachmentsIcon","AvatarIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BulletListIcon","CalendarIcon","CaretLeftIcon","CaretRightIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","CircleFilledIcon","ClockIcon","CopyIcon","CustomizeIcon","DocumentEditIcon","DownloadIcon","DuplicateIcon","EditIcon","ElipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FilterAltIcon","FolderIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FullScreenIcon","HelpCircleIcon","HelpIcon","HereIcon","HideIcon","HighlightTextAltIcon","HighlightTextIcon","HorizontalScrollIcon","ImageIcon","InfoCircleFilledIcon","InsertIcon","ItalicIcon","LightBulbIcon","LineIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MeasureIcon","MinusIcon","MoreIcon","MoreVerticalIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","NumberedListIcon","OpenIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageVerticalScrollIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PlaceholderIcon","PlayIcon","PlusIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","ReadOnlyIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedoIcon","RemoveFormattingIcon","ReorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RulerIcon","SearchIcon","SettingsIcon","ShowIcon","SlashCommandsIcon","SoundRecordIcon","StampIcon","StarFilledIcon","StarIcon","StrikeoutTextAltIcon","TableCellIcon","TableColumnIcon","TableHeaderIcon","TableIcon","TableRowIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextDecreaseIndentIcon","TextIcon","TextIncreaseIndentIcon","TextMarkIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UndoIcon","UnlockIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WindowedIcon","WorkflowIcon","XCircleFilledIcon","XIcon"],"20":["AddPageIcon","AnonymousIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpCircleFilledIcon","AtIcon","AvatarFilledIcon","BoldIcon","CalloutIcon","CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ClockIcon","CollapseIcon","CommentIcon","CopyIcon","CutIcon","DistanceIcon","DownloadIcon","DuplicateIcon","EditIcon","EllipseIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorAlternativeCircleIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FormDateIcon","FormSignatureIcon","FormTextFieldIcon","HelpCircleIcon","HighlightTextIcon","HomeIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","ItalicIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MinusIcon","MoreIcon","MoreVerticalIcon","MoveIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OpenIcon","PageMoveLeftIcon","PageMoveRightIcon","PagesInsertIcon","PasteIcon","PipetteIcon","PlusIcon","PrintIcon","RotateClockwiseIcon","SearchIcon","SettingsIcon","ShapeIcon","ShareIcon","SoundIcon","SoundRecordIcon","StarFilledIcon","StarIcon","StyleIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UploadIcon","WarningFilledIcon","WarningIcon","XCircleFilledIcon","XCircleIcon","XIcon"],"24":["AddNoteCloudIcon","AddNoteIcon","AddTextSerifIcon","AiIcon","AirplaneIcon","AlignBottomIcon","AlignHorizontalCenterIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDownIcon","ArrowIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpIcon","AtIcon","AttachmentIcon","AvatarFilledIcon","AvatarIcon","BlendModeIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BorderColorIcon","BottomBorderIcon","BulletListIcon","CalibrateIcon","CaptureAddIcon","CaretDownIcon","CaretIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ChevronListIcon","ClockIcon","CloudyBorderIcon","CollapseIcon","ColorPaletteIcon","ColorSwatchIcon","CommentIcon","CommentInSidebarIcon","CommentOnPageIcon","CompareDocumentsIcon","CopyIcon","CopyPageIcon","CropIcon","CustomizeIcon","CutIcon","DateModifiedIcon","DatePlusIcon","DebugIcon","DocumentArrowDownCircleIcon","DocumentArrowDownIcon","DocumentArrowRightIcon","DocumentFilledIcon","DocumentLockIcon","DocumentPdfIcon","DownloadIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","DuplicateIcon","EditAnnotationsIcon","EditContentIcon","EditDocumentIcon","EditIcon","EditThumbnailsIcon","EllipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","EndCapArrowFilledIcon","EndCapArrowIcon","EndCapChevronFilledIcon","EndCapChevronIcon","EndCapCircleIcon","EndCapDiamondIcon","EndCapNoneIcon","EndCapSlantedIcon","EndCapSquareIcon","EndCapStraightIcon","EraserIcon","ErrorAltCircleFilledIcon","ErrorAltIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","ExpandVerticalIcon","FillColorIcon","FilterIcon","FitToHeightIcon","FivePagesHorizontalFilledIcon","FivePagesVerticalFilledIcon","FolderAddIcon","FolderIcon","FontListIcon","FontSizeIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormPageIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FormTwoRadioButtonsIcon","FourPagesGridFilledIcon","FourPagesHorizontalFilledIcon","FourPagesStackedFilledIcon","FourPagesVerticalFilledIcon","GroupIcon","HamburgerMenuIcon","HandIcon","HeartIcon","HideIcon","HideRevealIcon","HighlightTextIcon","HomeIcon","HorizontalScollIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","InitialsIcon","InnerHorizontalBorderIcon","InnerVerticalBorderIcon","InsertIcon","ItalicIcon","LayerBottomIcon","LayerDownIcon","LayerTopIcon","LayerUpIcon","LayersIcon","LeftBindingIcon","LeftBorderIcon","LineCapsIcon","LineIcon","LineSpacingIcon","LineStyleCloudyIcon","LineStyleDashedDoubleDashIcon","LineStyleDashedDoubleGapIcon","LineStyleDashedQuadrupleDashIcon","LineStyleDashedSingleGapIcon","LineStyleIcon","LineStyleSolidIcon","LineWidthIcon","LinkIcon","LockFilledIcon","LockIcon","MagicIcon","MagicPenIcon","MailIcon","MarkupIcon","MarqueeZoomIcon","MeasureIcon","MergeIcon","MessageCloudIcon","MinusIcon","MoonIcon","MoreCircleIcon","MoreIcon","MoreVerticalIcon","MoveAllDirectionsIcon","MoveLeftIcon","MoveLeftRightIcon","MoveRightIcon","MultiplePagesIcon","NonEditableIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OcrIcon","OpacityIcon","PageAddIcon","PageCurlIcon","PageDuplicateIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLandscapeIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageMoveLeftIcon","PageMoveRightIcon","PageNumberCircleIcon","PageNumberIcon","PagePortraitIcon","PageRemoveIcon","PageVerticalScrollIcon","PagesInsertAltIcon","PagesInsertIcon","PagesNewFromSelectionAltIcon","PagesNewFromSelectionIcon","PagesSelectAllIcon","PagesSelectNoneIcon","PasteBoardIcon","PastePageIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PinDropFilledIcon","PinDropIcon","PipetteIcon","PlayIcon","PlusCircleFilledIcon","PlusCircleIcon","PlusIcon","PointerIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","PrecisionIcon","PrintIcon","PrivateModeIcon","PushPinIcon","QuestionmarkCircleIcon","ReaderViewIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedactIcon","RedactRectangleIcon","RedactTextHighlighterIcon","RedactionTextRepeatingIcon","RedactionTextSingleIcon","RedoAllIcon","RedoIcon","RegexIcon","ReplaceIcon","RightBindingIcon","RightBorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RotateObjectClockwiseIcon","RotateObjectCounterClockwiseIcon","RulerIcon","ScaleIcon","SearchCircleIcon","SearchIcon","SearchSelectionIcon","SelectAllIcon","SelectionToolIcon","SettingsIcon","ShapesIcon","ShareAltIcon","ShareIcon","ShieldAddIcon","ShieldCheckmarkIcon","ShieldWarningIcon","ShieldXIcon","ShowIcon","SidebarIcon","SignOutIcon","SignatureDigitalIcon","SignatureIcon","SinglePageFilledIcon","SoundIcon","SquigglyTextIcon","StampAddIcon","StampIcon","StarFilledIcon","StarIcon","StartCapArrowFilledIcon","StartCapArrowIcon","StartCapChevronFilledIcon","StartCapChevronIcon","StartCapCircleIcon","StartCapDiamondIcon","StartCapNoneIcon","StartCapSlantedIcon","StartCapSquareIcon","StartCapStraightIcon","StrikeoutTextIcon","StyleFilledIcon","StyleIcon","StylusFilledIcon","StylusIcon","SunIcon","TableCellIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextColorIcon","TextIcon","TextPropertiesHideIcon","TextPropertiesShowIcon","TextSerifIcon","TextSmallerIcon","ThreePagesHorizontalFilledIcon","ThreePagesStackedFilledIcon","ThreePagesVerticalFilledIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TopBorderIcon","TrashIcon","TwoPagesHorizontalFilledIcon","TwoPagesVerticalFilledIcon","TypeTextIcon","UnderlineIcon","UnderlineTextIcon","UndoAllIcon","UndoIcon","UndoRedoIcon","UngroupIcon","UnlockIcon","UploadIcon","UserIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WidgetIcon","WorkflowIcon","XCircleFilledIcon","XCircleIcon","XIcon","ZoomInIcon","ZoomOutIcon"],"36":["ArrowRight","Check","Circle","Cross","Help","Inset","Key","NewParagraphAlt","NewParagraph","Note","PointerRight","SpeechBubble","Star"]};var p={version:"2.1.0"};var u=`
|
|
34230
35943
|
# Baseline UI MCP Server Guidelines
|
|
34231
35944
|
|
|
34232
35945
|
This MCP server provides AI assistants with structured access to Baseline UI's comprehensive component documentation, icon library, theming resources, and design guidelines.
|
|
@@ -34381,13 +36094,14 @@ Use this tool to:
|
|
|
34381
36094
|
- Consult **list_components** to find the right component for your needs
|
|
34382
36095
|
- Reference **theming** for custom design systems
|
|
34383
36096
|
- Check **styling** for CSS and design token guidance
|
|
34384
|
-
`;var
|
|
36097
|
+
`;var C=`# List of available components in Baseline UI design system
|
|
34385
36098
|
| Name | Description | Similar To |
|
|
34386
36099
|
| --- | --- | --- |
|
|
34387
|
-
${Object.entries(
|
|
36100
|
+
${Object.entries(t).toSorted(([n],[e])=>n.localeCompare(e)).map(([n,{description:e,similarTo:i}])=>`|${n}|${e}|${i?.join(", ")||""}|`).join(`
|
|
34388
36101
|
`)}
|
|
34389
|
-
`,
|
|
36102
|
+
`,T=Object.entries(c).map(([n,e])=>`${n}
|
|
34390
36103
|
|
|
34391
|
-
${
|
|
36104
|
+
${e.map(i=>"- "+i).join(`
|
|
34392
36105
|
`)}`).join(`
|
|
34393
|
-
`);
|
|
36106
|
+
`);function z(){let n=new mcp_js.McpServer({name:"baseline-ui",version:p.version});return n.registerResource("list_components","resource://baseline-ui/list_components.md",{description:a,mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:C}]})),n.registerResource("list_icons","resource://baseline-ui/list_icons.md",{description:r,mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:T}]})),n.registerResource("getting_started","resource://baseline-ui/getting_started.md",{description:"Quick start guide for integrating Baseline UI into new projects",mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:o.gettingStarted}]})),n.registerResource("nutrient_web_viewer_theming","resource://baseline-ui/nutrient_web_viewer_theming.md",{description:"Specialized theming guide for customizing Baseline UI in Nutrient Web Viewer. This is not applicable if you are not theming the Nutrient Web Viewer SDK.",mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:o.nutrientWebViewerTheming}]})),n.registerResource("theming","resource://baseline-ui/theming.md",{description:"Comprehensive guide for implementing custom themes and color schemes in Baseline UI",mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:o.theming}]})),n.registerResource("internationalization","resource://baseline-ui/internationalization.md",{description:"Guide for implementing multi-language support and localization in Baseline UI",mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:o.internationalization}]})),n.registerResource("styling","resource://baseline-ui/styling.md",{description:l,mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:o.styling}]})),n.registerResource("guidelines","resource://baseline-ui/guidelines.md",{description:s,mimeType:"text/markdown"},e=>({contents:[{uri:e.href,mimeType:"text/markdown",text:u}]})),n.registerTool("get_component_info",{title:"Get component info",description:m,inputSchema:{componentName:zod.z.enum(Object.keys(t))}},({componentName:e})=>({content:[{type:"text",text:JSON.stringify(Object.fromEntries(Object.entries(t[e]).filter(([i])=>!["description","similarTo","figmaUrl"].includes(i))),null,2)}]})),n.registerTool("get_story_url",{title:"Get story demo URL",description:h,inputSchema:{storyId:zod.z.string()}},({storyId:e})=>({content:[{type:"text",text:`https://nutrient.io/baseline-ui/iframe.html?id=${e}`}]})),n.registerTool("get_figma_url",{title:"Get Figma URL",description:b,inputSchema:{componentName:zod.z.enum(Object.keys(t))}},({componentName:e})=>({content:[{type:"text",text:t[e].figmaUrl}]})),n.registerTool("list_available_resources",{title:"List Available Resources",description:g,inputSchema:{}},()=>({content:[{type:"text",text:JSON.stringify([{name:"list_components",uri:"resource://baseline-ui/list_components.md",description:a,mimeType:"text/markdown"},{name:"list_icons",uri:"resource://baseline-ui/list_icons.md",description:r,mimeType:"text/markdown"},{name:"getting_started",uri:"resource://baseline-ui/getting_started.md",description:"Quick start guide for integrating Baseline UI into new projects",mimeType:"text/markdown"},{name:"nutrient_web_viewer_theming",uri:"resource://baseline-ui/nutrient_web_viewer_theming.md",description:"Specialized theming guide for customizing Baseline UI in Nutrient Web Viewer. This is not applicable if you are not theming the Nutrient Web Viewer SDK.",mimeType:"text/markdown"},{name:"theming",uri:"resource://baseline-ui/theming.md",description:"Comprehensive guide for implementing custom themes and color schemes in Baseline UI",mimeType:"text/markdown"},{name:"internationalization",uri:"resource://baseline-ui/internationalization.md",description:"Guide for implementing multi-language support and localization in Baseline UI",mimeType:"text/markdown"},{name:"styling",uri:"resource://baseline-ui/styling.md",description:l,mimeType:"text/markdown"},{name:"guidelines",uri:"resource://baseline-ui/guidelines.md",description:s,mimeType:"text/markdown"}],null,2)}]})),n}
|
|
36107
|
+
exports.createServer=z;
|