@baseline-ui/mcp 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2219,7 +2219,7 @@ isPrimaryActionDisabled?: boolean
2219
2219
  * The props that are passed to the text input. If this is provided, a text
2220
2220
  * input will be displayed at the bottom of the dialog.
2221
2221
  */
2222
- textInputProps?: TextInputProps
2222
+ textInputProps?: SingleLineTextInputProps | MultiLineTextInputProps
2223
2223
  /**
2224
2224
  * The icon that is displayed at the top of the dialog. This is typically used
2225
2225
  * to display an icon that represents the type of alert that is being shown.
@@ -3827,7 +3827,7 @@ children: React.ReactNode
3827
3827
  "test": "cross-env BABEL_ENV=test jest",
3828
3828
  "test:e2e": "cross-env BABEL_ENV=test jest --testPathPattern=e2e --testPathIgnorePatterns='examples,/packages/components/,/packages/react/'"
3829
3829
  }
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> **Note:** `bui.colorInput.indeterminate` is not present in the bundled locale JSON files \u2014 it relies on its `defaultMessage` as fallback. Override it via `I18nProvider.messages` when you need a custom string for the indeterminate state.\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 {
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```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
3831
  /**
3832
3832
  * Whether the overlay is open by default (controlled).
3833
3833
  */
@@ -3948,19 +3948,19 @@ storePickedColorKey?: string
3948
3948
  /**
3949
3949
  * The label to show on the add color button.
3950
3950
  *
3951
- * @default Add color
3951
+ * @default Add Color
3952
3952
  */
3953
3953
  addColorButtonLabel?: string
3954
3954
  /**
3955
3955
  * The label to show on the remove color button.
3956
3956
  *
3957
- * @default Remove color
3957
+ * @default Remove Color
3958
3958
  */
3959
3959
  removeColorButtonLabel?: string
3960
3960
  /**
3961
3961
  * The label to show on the custom colors section.
3962
3962
  *
3963
- * @default Custom colors
3963
+ * @default Custom Colors
3964
3964
  */
3965
3965
  customColorsLabel?: string
3966
3966
  /**
@@ -4695,7 +4695,10 @@ inputStyle?: React.CSSProperties
4695
4695
  */
4696
4696
  inputClassName?: string
4697
4697
  /**
4698
- * Optional label for the toggle button
4698
+ * Accessible name for the toggle button. Not rendered visually.
4699
+ *
4700
+ * A blank string is treated as unset, keeping the built-in name. Setting this
4701
+ * overrides any \`aria-labelledby\` the toggle would otherwise inherit.
4699
4702
  */
4700
4703
  toggleLabel?: string
4701
4704
  filterItems?: boolean
@@ -4740,13 +4743,15 @@ filter?: ComboBoxStateOptions<ListItem>["defaultFilter"]
4740
4743
  */
4741
4744
  inputType?: "text" | "number"
4742
4745
  /**
4743
- * The minimum value for the input field. This is only valid when \`inputType\`
4744
- * is set to \`number\`.
4746
+ * The minimum value for the input field. Only applies when \`inputType="number"\`.
4747
+ *
4748
+ * Requires \`onValueChange\` or \`onInputSubmit\` to receive and apply the clamped value.
4745
4749
  */
4746
4750
  minValue?: number
4747
4751
  /**
4748
- * The maximum value for the input field. This is only valid when \`inputType\`
4749
- * is set to \`number\`.
4752
+ * The maximum value for the input field. Only applies when \`inputType="number"\`.
4753
+ *
4754
+ * Requires \`onValueChange\` or \`onInputSubmit\` to receive and apply the clamped value.
4750
4755
  */
4751
4756
  maxValue?: number
4752
4757
  /**
@@ -4754,7 +4759,17 @@ maxValue?: number
4754
4759
  */
4755
4760
  onInputSubmit?: (value: string) => void
4756
4761
  /**
4757
- * Listener to call when the selected value changes
4762
+ * Listener to call when the selected value changes.
4763
+ *
4764
+ * Also called when the selected item's label changes after selection (e.g. the consumer
4765
+ * relabels the item, or an async collection resolves), so the reported \`value\` converges on
4766
+ * the item's current label once the field settles. While the user has unsubmitted text, a
4767
+ * label-only change is deferred; it is reported on the next selection, submit, or blur that
4768
+ * settles the input back onto the selection. With a controlled \`inputValue\` the consumer owns
4769
+ * that sync, so a deferred label is only reported once they settle the input themselves.
4770
+ *
4771
+ * Do not relabel the selected item from inside this callback \u2014 the new label re-triggers the
4772
+ * listener, which loops unless the relabel is idempotent.
4758
4773
  */
4759
4774
  onValueChange?: (option: { value?: string; key?: string | null }) => void
4760
4775
  }`,stories:{usage:[{id:"core-forms-combobox--basic",name:"Basic",snippet:`const Basic = () => <ComboBox
@@ -4965,7 +4980,33 @@ onValueChange?: (option: { value?: string; key?: string | null }) => void
4965
4980
  defaultInputValue="42"
4966
4981
  allowsCustomValue
4967
4982
  inputType="number"
4968
- minValue={0} />;`},{id:"core-forms-combobox--with-custom-filter",name:"With Custom Filter",snippet:`const WithCustomFilter = () => <ComboBox
4983
+ minValue={0} />;`},{id:"core-forms-combobox--with-min-max-value",name:"With Min Max Value",snippet:`const WithMinMaxValue = () => {
4984
+ const [inputValue, setInputValue] = React.useState("");
4985
+
4986
+ return (
4987
+ <ComboBox
4988
+ items={[
4989
+ { id: "10", label: "10" },
4990
+ { id: "25", label: "25" },
4991
+ { id: "50", label: "50" },
4992
+ ]}
4993
+ aria-label="ComboBox"
4994
+ label="Type a number outside 10-50 range and blur"
4995
+ description="Values are clamped to minValue=10, maxValue=50 on blur"
4996
+ placeholder="Enter a number"
4997
+ allowsCustomValue
4998
+ inputType="number"
4999
+ minValue={10}
5000
+ maxValue={50}
5001
+ inputValue={inputValue}
5002
+ onInputChange={setInputValue}
5003
+ onValueChange={({ value }) => {
5004
+ if (value !== undefined) {
5005
+ setInputValue(value);
5006
+ }
5007
+ }} />
5008
+ );
5009
+ };`},{id:"core-forms-combobox--with-custom-filter",name:"With Custom Filter",snippet:`const WithCustomFilter = () => <ComboBox
4969
5010
  items={[
4970
5011
  { id: "pear", label: "Pear" },
4971
5012
  { id: "apple", label: "Apple" },
@@ -4996,6 +5037,8 @@ import { ComboBox } from "..";
4996
5037
  import { Box } from "../../Box";
4997
5038
 
4998
5039
  import type { ListItem, ListOption } from "../../shared/types/List";
5040
+ import type { ComboBoxProps } from "../ComboBox.types";
5041
+ import type { Key } from "react-aria";
4999
5042
 
5000
5043
  export const items = [
5001
5044
  { id: "pears", label: "Pears" },
@@ -5076,6 +5119,114 @@ export const NumberComboBoxExample: React.FC<
5076
5119
  );
5077
5120
  };
5078
5121
 
5122
+ export const DynamicLabelComboBoxExample: React.FC<{
5123
+ onValueChange?: ComboBoxProps["onValueChange"];
5124
+ allowsCustomValue?: boolean;
5125
+ inputValue?: string;
5126
+ /** Relabels the selected item from "(selected)" to "(renamed)" when this exact text is typed. */
5127
+ relabelOnInput?: string;
5128
+ }> = ({ onValueChange, allowsCustomValue, inputValue, relabelOnInput }) => {
5129
+ const [value, setValue] = React.useState<Key | null>(null);
5130
+ // One-way: relabelling on every keystroke would feed the sync back into onInputChange and loop.
5131
+ const [isRelabelled, setIsRelabelled] = React.useState(false);
5132
+
5133
+ const suffix = isRelabelled ? "(renamed)" : "(selected)";
5134
+ const dynamicItems = items.map((item) =>
5135
+ item.id === value ? { ...item, label: \`\${item.label} \${suffix}\` } : item,
5136
+ );
5137
+
5138
+ return (
5139
+ <Box style={{ width: "100%", height: 200 }}>
5140
+ <ComboBox
5141
+ items={dynamicItems}
5142
+ aria-label="Combo box"
5143
+ value={value}
5144
+ onChange={setValue}
5145
+ onValueChange={onValueChange}
5146
+ allowsCustomValue={allowsCustomValue}
5147
+ inputValue={inputValue}
5148
+ onInputChange={(text) => {
5149
+ if (text === relabelOnInput) {
5150
+ setIsRelabelled(true);
5151
+ }
5152
+ }}
5153
+ />
5154
+ <span data-testid="relabel-probe">
5155
+ {dynamicItems.find((item) => item.id === value)?.label ?? ""}
5156
+ </span>
5157
+ </Box>
5158
+ );
5159
+ };
5160
+
5161
+ export const AsyncItemsComboBoxExample: React.FC<{
5162
+ initiallyLoaded?: boolean;
5163
+ onValueChange?: ComboBoxProps["onValueChange"];
5164
+ allowsCustomValue?: boolean;
5165
+ /** Unloads and reloads the items when this exact text is typed, without moving focus. */
5166
+ refreshOnInput?: string;
5167
+ }> = ({
5168
+ initiallyLoaded = true,
5169
+ onValueChange,
5170
+ allowsCustomValue,
5171
+ refreshOnInput,
5172
+ }) => {
5173
+ const [loadState, setLoadState] = React.useState<
5174
+ "loaded" | "unloaded" | "refreshing"
5175
+ >(initiallyLoaded ? "loaded" : "unloaded");
5176
+ const [refreshCount, setRefreshCount] = React.useState(0);
5177
+ const [value, setValue] = React.useState<Key | null>(
5178
+ initiallyLoaded ? null : "pears",
5179
+ );
5180
+ const isLoaded = loadState === "loaded";
5181
+
5182
+ // The unloaded collection must reach the DOM for a render before the items come back,
5183
+ // otherwise there is no transient for the refresh to be mistaken for a new selection.
5184
+ React.useEffect(() => {
5185
+ if (loadState === "refreshing") {
5186
+ setLoadState("loaded");
5187
+ setRefreshCount((count) => count + 1);
5188
+ }
5189
+ }, [loadState]);
5190
+
5191
+ return (
5192
+ <Box style={{ width: "100%", height: 200 }}>
5193
+ <ComboBox
5194
+ items={isLoaded ? items : []}
5195
+ aria-label="Combo box"
5196
+ value={value}
5197
+ onChange={setValue}
5198
+ onValueChange={onValueChange}
5199
+ allowsCustomValue={allowsCustomValue}
5200
+ onInputChange={(text) => {
5201
+ if (text === refreshOnInput) {
5202
+ setLoadState("refreshing");
5203
+ }
5204
+ }}
5205
+ />
5206
+ <button
5207
+ type="button"
5208
+ onClick={() => {
5209
+ setLoadState((state) => (state === "loaded" ? "unloaded" : "loaded"));
5210
+ }}
5211
+ >
5212
+ {isLoaded ? "Unload items" : "Load items"}
5213
+ </button>
5214
+ <span data-testid="refresh-probe">{refreshCount}</span>
5215
+ </Box>
5216
+ );
5217
+ };
5218
+
5219
+ export const LabelledByComboBoxExample: React.FC<
5220
+ Omit<React.ComponentProps<typeof ComboBox>, "items" | "aria-labelledby">
5221
+ > = (args) => {
5222
+ return (
5223
+ <Box style={{ width: "100%", height: 200 }}>
5224
+ <span id="external-label">External</span>
5225
+ <ComboBox items={items} {...args} aria-labelledby="external-label" />
5226
+ </Box>
5227
+ );
5228
+ };
5229
+
5079
5230
  export const SectionComboBoxExample: React.FC = () => {
5080
5231
  return (
5081
5232
  <Box style={{ width: "100%", height: 300 }}>
@@ -7710,6 +7861,10 @@ children?: ReactNode
7710
7861
  * Enables the selection of directories instead of individual files.
7711
7862
  */
7712
7863
  acceptDirectory?: boolean
7864
+ /**
7865
+ * The id applied to the root element of the component.
7866
+ */
7867
+ id?: string
7713
7868
  /**
7714
7869
  * The className applied to the root element of the component.
7715
7870
  */
@@ -8005,6 +8160,7 @@ disabledKeys?: Iterable<string> | "all"
8005
8160
  );
8006
8161
  };`}],implementation:`import React from "react";
8007
8162
 
8163
+ import { ActionButton } from "../../ActionButton";
8008
8164
  import { FileList } from "../FileList";
8009
8165
 
8010
8166
  import type { FileListItem } from "../FileList.types";
@@ -8059,6 +8215,32 @@ export const manyItems: FileListItem[] = Array.from({ length: 12 }, (_, i) => ({
8059
8215
  name: \`attachment-\${i + 1}.pdf\`,
8060
8216
  }));
8061
8217
 
8218
+ // Swaps in a new array whose items carry new content, which is the only way a caller can
8219
+ // legitimately invalidate the memoised options.
8220
+ export const UploadingFileList: React.FC = () => {
8221
+ const [items, setItems] = React.useState<FileListItem[]>(loadingItems);
8222
+
8223
+ return (
8224
+ <>
8225
+ <ActionButton
8226
+ label="Finish upload"
8227
+ onPress={() => {
8228
+ setItems((prev) =>
8229
+ prev.map((item) => ({ ...item, isLoading: false })),
8230
+ );
8231
+ }}
8232
+ />
8233
+ <FileList
8234
+ aria-label="Uploaded files"
8235
+ items={items}
8236
+ onRemove={(id) => {
8237
+ setItems((prev) => prev.filter((item) => item.id !== id));
8238
+ }}
8239
+ />
8240
+ </>
8241
+ );
8242
+ };
8243
+
8062
8244
  interface BasicProps {
8063
8245
  onRemove?: (id: string) => void;
8064
8246
  disabledKeys?: Iterable<string> | "all";
@@ -9847,7 +10029,12 @@ children: React.ReactNode
9847
10029
  locale?: string
9848
10030
  messages?: LocalizedStrings
9849
10031
  /**
9850
- * @default true
10032
+ * Whether to \`console.warn\` about message ids that resolve through the
10033
+ * deprecated bare-id fallback or fall all the way through to their
10034
+ * \`defaultMessage\`. These warnings are development guidance, so they are off
10035
+ * in production builds \u2014 pass \`true\` to opt back in.
10036
+ *
10037
+ * @default process.env.NODE_ENV !== "production"
9851
10038
  */
9852
10039
  shouldLogMissingMessages?: boolean
9853
10040
  }`,stories:{usage:[{id:"core-utilities-i18nprovider--string-format",name:"String Format",snippet:`const StringFormat = ({ children }) => {
@@ -9987,7 +10174,6 @@ The following strings are used by the \`ImageDropZone\` component and can be ove
9987
10174
  | Key | Default (en) |
9988
10175
  | ------------------------------- | ------------ |
9989
10176
  | \`bui.imageDropZone.selectImage\` | Select Image |
9990
- | \`bui.imageDropZone.delete\` | Delete |
9991
10177
 
9992
10178
  \`\`\`jsx
9993
10179
  import { I18nProvider, ImageDropZone } from "@baseline-ui/core";
@@ -9997,7 +10183,6 @@ import { I18nProvider, ImageDropZone } from "@baseline-ui/core";
9997
10183
  messages={{
9998
10184
  en: {
9999
10185
  "bui.imageDropZone.selectImage": "Choose Image",
10000
- "bui.imageDropZone.delete": "Remove",
10001
10186
  },
10002
10187
  }}
10003
10188
  >
@@ -10420,6 +10605,8 @@ renderImage?: (
10420
10605
  imageContainerStyle?: React.CSSProperties
10421
10606
  /**
10422
10607
  * The dimensions of the image.
10608
+ *
10609
+ * @default { width: imageWidth, aspectRatio }
10423
10610
  */
10424
10611
  imageDimensions?: | Dimension
10425
10612
  | ((
@@ -10878,6 +11065,35 @@ export function ScrollIntoViewImageGalleryExample() {
10878
11065
  );
10879
11066
  }
10880
11067
 
11068
+ // Counts \`renderImage\` calls so a spec can prove that a re-render which changes nothing
11069
+ // the gallery reads does not throw away the list box's cached options. \`Report\` publishes
11070
+ // the tally taken *before* its own render, so two clicks bracket exactly one re-render.
11071
+ export function RenderCountingImageGalleryExample() {
11072
+ const calls = React.useRef(0);
11073
+ const [reported, setReported] = React.useState(0);
11074
+
11075
+ const renderImage = React.useCallback<
11076
+ Exclude<React.ComponentProps<typeof ImageGallery>["renderImage"], undefined>
11077
+ >((item) => {
11078
+ calls.current += 1;
11079
+
11080
+ return <img src={item.data?.src} alt={item.data?.alt} />;
11081
+ }, []);
11082
+
11083
+ return (
11084
+ <Box display="flex" flexDirection="column" gap="lg">
11085
+ <ActionButton
11086
+ label="Report"
11087
+ onPress={() => {
11088
+ setReported(calls.current);
11089
+ }}
11090
+ />
11091
+ <Text data-testid="render-count">{reported.toString()}</Text>
11092
+ <ImageGallery defaultItems={items} renderImage={renderImage} />
11093
+ </Box>
11094
+ );
11095
+ }
11096
+
10881
11097
  export function RTLImageGalleryExample(
10882
11098
  props: Omit<React.ComponentProps<typeof ImageGallery>, "onDelete">,
10883
11099
  ) {
@@ -12183,10 +12399,13 @@ renderDragPreview?: (items: DragItem[]) => React.JSX.Element
12183
12399
  *
12184
12400
  * @param section ListSection
12185
12401
  * @param ref React.RefObject<HTMLDivElement>
12402
+ * @param headingProps ARIA props for the heading element \u2014 spread these onto the header's
12403
+ * root (and attach \`ref\`) so the section group's \`aria-labelledby\` resolves to it.
12186
12404
  */
12187
12405
  renderSectionHeader?: (
12188
12406
  section: Node<ListItem>,
12189
12407
  ref: React.Ref<HTMLSpanElement>,
12408
+ headingProps?: React.HTMLAttributes<HTMLElement>,
12190
12409
  ) => React.ReactNode
12191
12410
  /**
12192
12411
  * Whether to show the selected checkmark icon.
@@ -12879,27 +13098,45 @@ console.log(greet('Markdown Maverick'));
12879
13098
  2. Selection options: The component can be configured to allow single, multiple, or no selection.
12880
13099
  3. Disabled items support: Certain menu items can be disabled, preventing user interaction.
12881
13100
  4. Sections support: Related items can be grouped into sections for better organization and navigation.
12882
- 5. Keyboard navigation: Users can navigate through the menu using their keyboard, including arrow keys, home/end, and page up/down.
12883
- 6. Auto-scroll: The menu automatically scrolls as the user navigates through it with their keyboard.
12884
- 7. Keyboard menu opening: The menu can be opened using the keyboard, and automatically focuses on the first or last item.
12885
- 8. Typeahead: Users can quickly navigate to a menu item by typing the first few letters of the item's label.
12886
- 9. Virtualized scrolling: For long lists, performance is improved by only rendering the visible items.
13101
+ 5. Submenus: Any option can nest an \`items\` array to open a cascading submenu of arbitrary depth.
13102
+ 6. Keyboard navigation: Users can navigate through the menu using their keyboard, including arrow keys, home/end, and page up/down.
13103
+ 7. Auto-scroll: The menu automatically scrolls as the user navigates through it with their keyboard.
13104
+ 8. Keyboard menu opening: The menu can be opened using the keyboard, and automatically focuses on the first or last item.
13105
+ 9. Typeahead: Users can quickly navigate to a menu item by typing the first few letters of the item's label.
13106
+ 10. Virtualized scrolling: For long lists, performance is improved by only rendering the visible items.
13107
+
13108
+ Pass the list of options via \`items\` and a \`triggerLabel\` for the default
13109
+ \`ActionButton\` trigger. \`Menu\` renders its own trigger and popover \u2014 it does not
13110
+ take children.
12887
13111
 
12888
13112
  \`\`\`jsx
12889
- import { ActionButton } from "../../utils";
12890
- import { Menu } from "@storybook/addon-docs/blocks";
13113
+ import { Menu } from "@baseline-ui/core";
12891
13114
 
12892
13115
  const items = [
12893
- {
12894
- label: "Cut",
12895
- id: "cut",
12896
- keyboardShortcut: "\u2318X",
12897
- },
13116
+ { id: "cut", label: "Cut", keyboardShortcut: "\u2318X" },
13117
+ { id: "copy", label: "Copy", keyboardShortcut: "\u2318C" },
13118
+ { id: "paste", label: "Paste", keyboardShortcut: "\u2318V" },
12898
13119
  ];
12899
13120
 
12900
- <Menu items={items}>
12901
- <ActionButton label="Label" />
12902
- </Menu>;
13121
+ export default function App() {
13122
+ return <Menu items={items} triggerLabel="Edit" onAction={(id) => {}} />;
13123
+ }
13124
+ \`\`\`
13125
+
13126
+ To render a custom trigger, pass \`renderTrigger\` instead of \`triggerLabel\`. If
13127
+ you need on/off state rather than a command list, use \`ToggleButton\`; to pick a
13128
+ value from a set with an always-visible field, use \`Select\`.
13129
+
13130
+ Each option accepts an optional leading \`icon\` (a \`@baseline-ui/icons\` component)
13131
+ and a \`keyboardShortcut\` string rendered at the trailing edge of the item.
13132
+
13133
+ \`\`\`jsx
13134
+ import { CopyIcon, TrashIcon } from "@baseline-ui/icons/16";
13135
+
13136
+ const items = [
13137
+ { id: "copy", label: "Copy", keyboardShortcut: "\u2318C", icon: CopyIcon },
13138
+ { id: "delete", label: "Delete", keyboardShortcut: "\u232B", icon: TrashIcon },
13139
+ ];
12903
13140
  \`\`\`
12904
13141
 
12905
13142
  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\`.
@@ -12927,9 +13164,93 @@ const items = [
12927
13164
  ];
12928
13165
  \`\`\`
12929
13166
 
12930
- The \`Menu\` component can be controlled or uncontrolled. When uncontrolled, the component manages its own state internally. When controlled, the state is managed by the parent component. To control the component, set the \`selectedKeys\` prop to an array of item IDs. The value of the selected keys must match the \`id\` prop of the items.
13167
+ Pass the object form of \`selectionMode\` to make individual sections independent
13168
+ selection groups. \`selectedKeys\` and \`defaultSelectedKeys\` are then keyed by
13169
+ section id, and \`onSelectionChange\` receives the changed section's selection
13170
+ plus its \`sectionId\`.
13171
+
13172
+ \`\`\`tsx
13173
+ <Menu
13174
+ items={itemsWithSections}
13175
+ selectionMode={{ edit: "single", theme: "multiple" }}
13176
+ selectedKeys={{ edit: editKeys, theme: themeKeys }}
13177
+ onSelectionChange={(keys, meta) => {
13178
+ if (meta) setSelection((prev) => ({ ...prev, [meta.sectionId]: keys }));
13179
+ }}
13180
+ />
13181
+ \`\`\`
13182
+
13183
+ Menu-wide selection (a string \`selectionMode\`) and per-section selection are
13184
+ mutually exclusive within one menu. Enter activation closes the menu. Pointer
13185
+ activation closes single-select menus and keeps multi-select menus open, while
13186
+ Space activation keeps both selection modes open.
13187
+
13188
+ Give any option an \`items\` array to turn it into a submenu trigger. The option
13189
+ renders with a trailing chevron and opens a nested menu on hover or when
13190
+ activated with \`ArrowRight\` / \`Enter\`. Submenus nest arbitrarily and may
13191
+ themselves contain leaf options, sections, or deeper submenus.
13192
+
13193
+ \`\`\`jsx
13194
+ const items = [
13195
+ { id: "new", label: "New" },
13196
+ {
13197
+ id: "share",
13198
+ label: "Share",
13199
+ items: [
13200
+ { id: "email", label: "Email link" },
13201
+ { id: "sms", label: "SMS" },
13202
+ {
13203
+ id: "more",
13204
+ label: "More services",
13205
+ items: [
13206
+ { id: "slack", label: "Slack" },
13207
+ { id: "teams", label: "Teams" },
13208
+ ],
13209
+ },
13210
+ ],
13211
+ },
13212
+ ];
13213
+ \`\`\`
13214
+
13215
+ \`onAction\` fires for the activated leaf at any depth (with that leaf's \`id\`);
13216
+ activating a submenu trigger does not fire \`onAction\`. Selecting a leaf closes
13217
+ the whole cascade. A single flat \`disabledKeys\` array disables triggers and
13218
+ nested items at any level. In RTL locales the chevron and the submenu's opening
13219
+ direction flip automatically.
13220
+
13221
+ 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.
13222
+
13223
+ The open state can likewise be controlled or uncontrolled. When uncontrolled, use \`defaultOpen\`; to control it, pass \`isOpen\` and handle \`onOpenChange\`.
13224
+
13225
+ Each menu item exposes the standard React Aria interaction-state attributes, and Baseline UI adds class hooks on the trigger, popover, and item subparts.
12931
13226
 
12932
- The \`Menu\` component can be controlled or uncontrolled. When uncontrolled, the component manages its own state internally. When controlled, the state is managed by the parent component. To control the component, set the \`isOpen\` prop to a boolean value.`,props:`interface MenuProps {
13227
+ | Selector | Description |
13228
+ | ---------------------------------- | ------------------------------------------------------- |
13229
+ | \\[data-focused] | Whether the item is focused. |
13230
+ | \\[data-focus-visible] | Whether the item is keyboard focused. |
13231
+ | \\[data-hovered] | Whether the item is currently hovered with a mouse. |
13232
+ | \\[data-pressed] | Whether the item is currently pressed. |
13233
+ | \\[data-selected] | Whether the item is selected (selection modes). |
13234
+ | \\[data-disabled] | Whether the item is disabled. |
13235
+ | \\[data-has-submenu] | Whether the item opens a submenu. |
13236
+ | \\[data-open] | Whether the item's submenu is open. |
13237
+ | .BaselineUI-Menu-Trigger | The trigger button. |
13238
+ | .BaselineUI-Menu-Popover | The root popover container. |
13239
+ | .BaselineUI-Menu-SubmenuPopover | A nested submenu popover container. |
13240
+ | .BaselineUI-Menu-SubmenuChevron | The trailing chevron on a submenu trigger. |
13241
+ | .BaselineUI-Menu-OptionDescription | The trailing text of an item (e.g. \`keyboardShortcut\`). |
13242
+
13243
+ | Key | Function |
13244
+ | --------------------- | ---------------------------------------------------------------------------------- |
13245
+ | \`Enter\` / \`Space\` | Opens the menu from the trigger; activates the focused item. |
13246
+ | \`ArrowDown\` | Opens the menu focusing the first item; moves focus down. |
13247
+ | \`ArrowUp\` | Opens the menu focusing the last item; moves focus up. |
13248
+ | \`Home\` / \`End\` | Moves focus to the first / last item. |
13249
+ | \`PageUp\` / \`PageDown\` | Moves focus up / down by a page. |
13250
+ | \`A\`\u2013\`Z\` | Typeahead \u2014 focuses the next item starting with the typed letters. |
13251
+ | \`ArrowRight\` | Opens the focused submenu (\`ArrowLeft\` in RTL). |
13252
+ | \`ArrowLeft\` | Closes the current submenu and returns focus to its trigger (\`ArrowRight\` in RTL). |
13253
+ | \`Escape\` | Closes the menu (or the current submenu level). |`,props:`interface MenuProps {
12933
13254
  /**
12934
13255
  * Whether the overlay is open by default (controlled).
12935
13256
  */
@@ -12961,39 +13282,18 @@ contentClassName?: string
12961
13282
  */
12962
13283
  itemClassName?: string
12963
13284
  /**
12964
- * A list of items to render in the menu. Items have the following shape:
12965
- *
12966
- * \`\`\`ts
12967
- * export type MenuOption = {
12968
- * id: string;
12969
- * label: string;
12970
- * keyboardShortcut?: string;
12971
- * icon?: React.FC<IconProps>;
12972
- * };
12973
- *
12974
- * export type MenuSection = {
12975
- * id: string;
12976
- * title?: string;
12977
- * type: "section";
12978
- * children: MenuOption[];
12979
- * };
13285
+ * A list of items to render in the menu. See \`MenuItem\`.
12980
13286
  *
12981
- * export type MenuItem = MenuOption | MenuSection;
12982
- * \`\`\`
13287
+ * A \`MenuSection\` may opt into independent selection by listing its id in the
13288
+ * object form of \`selectionMode\` (e.g. \`selectionMode={{ view: "single" }}\`).
12983
13289
  */
12984
13290
  items: MenuItem[]
12985
13291
  /**
12986
13292
  * A function that renders the trigger element of the component. The default
12987
13293
  * implementation renders an \`ActionButton\` component.
12988
- *
12989
- * \`\`\`tsx
12990
- * <Menu renderTrigger={({ buttonProps, ref }) => <ActionButton {...buttonProps} label="Label" ref={ref} />
12991
- * \`\`\`
12992
13294
  */
12993
13295
  renderTrigger?: (options: {
12994
- buttonProps: ActionButtonProps & {
12995
- isOpen: boolean;
12996
- };
13296
+ buttonProps: ActionButtonProps & { isOpen: boolean };
12997
13297
  ref: React.RefObject<HTMLButtonElement>;
12998
13298
  }) => React.ReactNode
12999
13299
  /**
@@ -13001,6 +13301,33 @@ renderTrigger?: (options: {
13001
13301
  * function that accepts a boolean indicating whether the menu is open.
13002
13302
  */
13003
13303
  triggerLabel?: React.ReactNode | ((isOpen: boolean) => React.ReactNode)
13304
+ /**
13305
+ * \`"single"\`/\`"multiple"\` select the whole menu. A record keyed by section id
13306
+ * makes each listed section an independent selection group.
13307
+ */
13308
+ selectionMode?: | "none"
13309
+ | "single"
13310
+ | "multiple"
13311
+ | Record<string, "single" | "multiple">
13312
+ /**
13313
+ * A menu-wide selection (\`"all"\` or an iterable of keys) or a per-section
13314
+ * record of \`Selection\`. The iterable form preserves the pre-existing
13315
+ * \`AriaMenuProps.selectedKeys\` shape (arrays stay assignable).
13316
+ */
13317
+ selectedKeys?: "all" | Iterable<Key> | Record<string, Selection>
13318
+ /**
13319
+ * Initial selection, in the same flat-or-per-section shape as \`selectedKeys\`.
13320
+ */
13321
+ defaultSelectedKeys?: | "all"
13322
+ | Iterable<Key>
13323
+ | Record<string, "all" | Iterable<Key>>
13324
+ /**
13325
+ * Fires on user selection. In per-section mode, the second argument carries
13326
+ * the \`sectionId\` of the section whose selection changed; it is \`undefined\`
13327
+ * in menu-wide mode. Typed optional so existing one-arg handlers stay
13328
+ * assignable.
13329
+ */
13330
+ onSelectionChange?: (keys: Selection, meta?: { sectionId: string }) => void
13004
13331
  placement?: any
13005
13332
  }`,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 = () => {
13006
13333
  const [selectedKey, setSelectedKey] = React.useState(
@@ -13034,13 +13361,17 @@ placement?: any
13034
13361
  };`},{id:"core-collections-menu--with-selected-keys-controlled",name:"With Selected Keys Controlled",snippet:`const WithSelectedKeysControlled = () => <Menu
13035
13362
  triggerLabel="Menu Trigger"
13036
13363
  items={items}
13037
- selectedKeys={["light"]}
13364
+ selectedKeys={new Set(["light"])}
13038
13365
  selectionMode="single" />;`},{id:"core-collections-menu--long-menu",name:"Long Menu",snippet:`const LongMenu = () => <Menu
13039
13366
  triggerLabel="Menu Trigger"
13040
13367
  items={longListItems}
13041
13368
  selectionMode="single"
13042
13369
  defaultOpen
13043
- autoFocus="first" />;`},{id:"core-collections-menu--with-custom-trigger",name:"With Custom Trigger",snippet:`const WithCustomTrigger = () => <Menu
13370
+ 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
13371
+ triggerLabel="Menu Trigger"
13372
+ items={itemsWithSubmenu}
13373
+ disabledKeys={["share"]}
13374
+ 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
13044
13375
  triggerLabel="Menu Trigger"
13045
13376
  renderTrigger={({ buttonProps, ref }) => (
13046
13377
  <ActionButton
@@ -13049,12 +13380,32 @@ placement?: any
13049
13380
  size="sm"
13050
13381
  ref={ref}
13051
13382
  />
13052
- )} />;`}],implementation:`import React from "react";
13383
+ )} />;`},{id:"core-collections-menu--section-level-selection",name:"Section Level Selection",snippet:`const SectionLevelSelection = () => {
13384
+ const [sel, setSel] = React.useState<Record<string, Selection>>({
13385
+ edit: new Set<Key>(["copy"]),
13386
+ theme: new Set<Key>(["light", "dark"]),
13387
+ });
13388
+
13389
+ return (
13390
+ <Menu
13391
+ triggerLabel="Menu Trigger"
13392
+ items={itemsWithSections}
13393
+ isOpen
13394
+ selectionMode={{ edit: "single", theme: "multiple" }}
13395
+ selectedKeys={{ edit: sel.edit, theme: sel.theme }}
13396
+ onSelectionChange={(keys, meta) => {
13397
+ if (meta) setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
13398
+ }} />
13399
+ );
13400
+ };`}],implementation:`import React from "react";
13053
13401
 
13402
+ import { I18nProvider } from "../../I18nProvider/I18nProvider";
13054
13403
  import { Menu } from "../Menu";
13055
- import { items } from "./data";
13404
+ import { items, itemsWithSections, itemsWithSubmenu } from "./data";
13056
13405
 
13057
13406
  import type { MenuProps } from "../Menu.types";
13407
+ import type { Key } from "react-aria";
13408
+ import type { Selection } from "react-stately";
13058
13409
 
13059
13410
  export const MenuExample: React.FC<
13060
13411
  MenuProps & {
@@ -13084,63 +13435,218 @@ export const MenuActionValueExample: React.FC<{ label: string }> = ({
13084
13435
  <div data-testid="menu-action-value">{received}</div>
13085
13436
  </>
13086
13437
  );
13087
- };`},similarTo:[],figmaUrl:null},MessageFormat:{id:"core-utilities-messageformat",breadcrumb:"Core/Utilities/MessageFormat",importStatement:'import { MessageFormat } from "@baseline-ui/core";',description:"`MessageFormat` is a component for formatting messages. This is built on top of [react-intl](https://formatjs.io/docs/react-intl/).",documentation:`\`MessageFormat\` is a component for formatting messages. This is built on top of [react-intl](https://formatjs.io/docs/react-intl/).
13438
+ };
13088
13439
 
13089
- \`\`\`jsx
13090
- import { MessageFormat, I18nProvider } from "../../utils";
13440
+ export const MenuSelectionExample: React.FC<{
13441
+ label: string;
13442
+ selectionMode: "single" | "multiple";
13443
+ }> = ({ label, selectionMode }) => {
13444
+ const [selected, setSelected] = React.useState<Selection>(new Set());
13445
+ return (
13446
+ <>
13447
+ <Menu
13448
+ items={items}
13449
+ triggerLabel={label}
13450
+ selectionMode={selectionMode}
13451
+ selectedKeys={selected}
13452
+ onSelectionChange={setSelected}
13453
+ />
13454
+ <div data-testid="menu-selection">
13455
+ {selected === "all" ? "all" : [...selected].map(String).join(",")}
13456
+ </div>
13457
+ </>
13458
+ );
13459
+ };
13091
13460
 
13092
- const messages = {
13093
- en: {
13094
- hello: "Hello {name}!",
13095
- },
13096
- fr: {
13097
- hello: "Bonjour {name}!",
13098
- },
13461
+ export const MenuOpenChangeExample: React.FC<{ label: string }> = ({
13462
+ label,
13463
+ }) => {
13464
+ const [log, setLog] = React.useState<string[]>([]);
13465
+ return (
13466
+ <>
13467
+ <Menu
13468
+ items={items}
13469
+ triggerLabel={label}
13470
+ onOpenChange={(isOpen) => {
13471
+ setLog((prev) => [...prev, String(isOpen)]);
13472
+ }}
13473
+ />
13474
+ <div data-testid="menu-open-log">{log.join(",")}</div>
13475
+ </>
13476
+ );
13099
13477
  };
13100
13478
 
13101
- const App = () => (
13102
- <I18nProvider locale="en" messages={messages}>
13103
- <MessageFormat id="hello" values={{ name: "World" }} />
13479
+ export const MenuRtlSubmenuExample: React.FC<{ label: string }> = ({
13480
+ label,
13481
+ }) => (
13482
+ <I18nProvider locale="ar">
13483
+ <Menu items={itemsWithSubmenu} triggerLabel={label} />
13104
13484
  </I18nProvider>
13105
13485
  );
13106
- \`\`\``,props:`interface MessageFormatProps {
13107
- /**
13108
- * By default \`<MessageFormat>\` will render the formatted string into a
13109
- * \`<React.Fragment>\`. If you need to customize rendering, you can either wrap
13110
- * it with another React element (recommended), specify a different tagName
13111
- * (e.g., 'div')
13112
- */
13113
- elementType?: React.ElementType | "div" | "span"
13114
- /**
13115
- * The id of the message to format.
13116
- */
13117
- id: string
13118
- /**
13119
- * The default message to use if the message id is not found.
13120
- */
13121
- defaultMessage?: string
13122
- }`,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.
13123
13486
 
13124
- * The content outside the modal is hidden from screen readers.
13125
- * The modal can optionally be closed by clicking outside the modal or by pressing the <kbd>Esc</kbd> key.
13126
- * Focus is trapped within the modal when it is open. It is automatically returned to the element that triggered the modal when it is closed.
13127
- * Scrolling of the page is disabled when the modal is open.
13128
-
13129
- When your SDK or component library is embedded inside a host application you don't control, the host page may have its own interactive UI (inputs, buttons, etc.) that users need to access even while a modal is open. By default, the modal traps focus and hides outside elements from assistive technologies, which would block interaction with the host app's UI.
13487
+ export const MenuFunctionLabelExample: React.FC = () => (
13488
+ <Menu
13489
+ items={items}
13490
+ triggerLabel={(isOpen) => (isOpen ? "Opened" : "Closed")}
13491
+ />
13492
+ );
13130
13493
 
13131
- Use the \`disableFocusManagement\` prop on \`ModalContent\` to prevent focus trapping and restoration, allowing users to interact with elements outside the modal's container.
13494
+ export const MenuPerSectionTypeCheck: React.FC = () => (
13495
+ <Menu
13496
+ items={items}
13497
+ triggerLabel="types"
13498
+ selectionMode={{ a: "single", b: "multiple" }}
13499
+ selectedKeys={{ a: new Set<Key>(["x"]), b: new Set<Key>() }}
13500
+ onSelectionChange={(keys, meta) => {
13501
+ void keys;
13502
+ void meta?.sectionId;
13503
+ }}
13504
+ />
13505
+ );
13132
13506
 
13133
- Combine this with \`UNSAFE_enableInert\` to ensure that background elements behind the modal (inside the \`FrameProvider\` container) remain truly non-interactive while the host page's UI stays accessible.
13507
+ const fmtSelection = (sel: Selection): string =>
13508
+ sel === "all" ? "all" : [...sel].map(String).join(",");
13134
13509
 
13135
- \`\`\`jsx
13136
- import {
13137
- Dialog,
13138
- Modal,
13139
- ModalTrigger,
13140
- ModalContent,
13141
- ModalClose,
13142
- DialogTitle,
13143
- } from "../../utils";
13510
+ export const MenuSectionSelectionExample: React.FC<{
13511
+ label: string;
13512
+ disallowEmptySelection?: boolean;
13513
+ }> = ({ label, disallowEmptySelection }) => {
13514
+ const [sel, setSel] = React.useState<Record<string, Selection>>({
13515
+ edit: new Set<Key>(),
13516
+ theme: new Set<Key>(["light"]),
13517
+ });
13518
+ const [lastSection, setLastSection] = React.useState("");
13519
+ return (
13520
+ <>
13521
+ <Menu
13522
+ items={itemsWithSections}
13523
+ triggerLabel={label}
13524
+ selectionMode={{ edit: "single", theme: "multiple" }}
13525
+ selectedKeys={{ edit: sel.edit, theme: sel.theme }}
13526
+ disallowEmptySelection={disallowEmptySelection}
13527
+ onSelectionChange={(keys, meta) => {
13528
+ if (!meta) return;
13529
+ setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
13530
+ setLastSection(meta.sectionId);
13531
+ }}
13532
+ />
13533
+ <div data-testid="menu-section-selection-edit">
13534
+ {fmtSelection(sel.edit)}
13535
+ </div>
13536
+ <div data-testid="menu-section-selection-theme">
13537
+ {fmtSelection(sel.theme)}
13538
+ </div>
13539
+ <div data-testid="menu-section-selection-lastsection">{lastSection}</div>
13540
+ </>
13541
+ );
13542
+ };
13543
+
13544
+ export const MenuDisabledSectionSelectionExample: React.FC = () => (
13545
+ <Menu
13546
+ items={itemsWithSections}
13547
+ triggerLabel="Menu"
13548
+ selectionMode={{ edit: "single" }}
13549
+ disabledKeys={["copy"]}
13550
+ />
13551
+ );
13552
+
13553
+ export const MenuOneShotDisabledKeysSectionSelectionExample: React.FC = () => {
13554
+ const disabledKeys = ["copy"][Symbol.iterator]();
13555
+
13556
+ return (
13557
+ <Menu
13558
+ items={itemsWithSections}
13559
+ triggerLabel="Menu"
13560
+ selectionMode={{ edit: "single" }}
13561
+ disabledKeys={disabledKeys}
13562
+ />
13563
+ );
13564
+ };
13565
+
13566
+ export function MenuUncontrolledSectionSelectionExample() {
13567
+ const [lastSection, setLastSection] = React.useState("");
13568
+ const [themeKeys, setThemeKeys] = React.useState("");
13569
+ return (
13570
+ <>
13571
+ <Menu
13572
+ triggerLabel="Options"
13573
+ items={itemsWithSections}
13574
+ selectionMode={{ theme: "multiple" }}
13575
+ defaultSelectedKeys={{ theme: new Set<Key>(["light"]) }}
13576
+ onSelectionChange={(keys, meta) => {
13577
+ if (!meta) return;
13578
+ setLastSection(meta.sectionId);
13579
+ setThemeKeys(fmtSelection(keys));
13580
+ }}
13581
+ />
13582
+ <div data-testid="menu-uncontrolled-lastsection">{lastSection}</div>
13583
+ <div data-testid="menu-uncontrolled-keys">{themeKeys}</div>
13584
+ </>
13585
+ );
13586
+ }
13587
+
13588
+ export const MenuPartialSectionSelectionExample: React.FC<{
13589
+ label: string;
13590
+ }> = ({ label }) => {
13591
+ const [sel, setSel] = React.useState<Record<string, Selection>>({
13592
+ edit: new Set<Key>(),
13593
+ });
13594
+ const [action, setAction] = React.useState("");
13595
+ return (
13596
+ <>
13597
+ <Menu
13598
+ items={itemsWithSections}
13599
+ triggerLabel={label}
13600
+ selectionMode={{ edit: "single" }}
13601
+ selectedKeys={{ edit: sel.edit }}
13602
+ onSelectionChange={(keys, meta) => {
13603
+ if (meta) setSel((prev) => ({ ...prev, [meta.sectionId]: keys }));
13604
+ }}
13605
+ onAction={(key) => {
13606
+ setAction(String(key));
13607
+ }}
13608
+ />
13609
+ <div data-testid="menu-partial-action">{action}</div>
13610
+ </>
13611
+ );
13612
+ };`},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 {
13613
+ /**
13614
+ * By default \`<MessageFormat>\` will render the formatted string into a
13615
+ * \`<React.Fragment>\`. If you need to customize rendering, you can either wrap
13616
+ * it with another React element (recommended), specify a different tagName
13617
+ * (e.g., 'div')
13618
+ */
13619
+ elementType?: React.ElementType | "div" | "span"
13620
+ /**
13621
+ * The id of the message to format.
13622
+ */
13623
+ id: string
13624
+ /**
13625
+ * The default message to use if the message id is not found.
13626
+ */
13627
+ defaultMessage?: string
13628
+ }`,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.
13629
+
13630
+ * The content outside the modal is hidden from screen readers.
13631
+ * The modal can optionally be closed by clicking outside the modal or by pressing the <kbd>Esc</kbd> key.
13632
+ * Focus is trapped within the modal when it is open. It is automatically returned to the element that triggered the modal when it is closed.
13633
+ * Scrolling of the page is disabled when the modal is open.
13634
+
13635
+ When your SDK or component library is embedded inside a host application you don't control, the host page may have its own interactive UI (inputs, buttons, etc.) that users need to access even while a modal is open. By default, the modal traps focus and hides outside elements from assistive technologies, which would block interaction with the host app's UI.
13636
+
13637
+ Use the \`disableFocusManagement\` prop on \`ModalContent\` to prevent focus trapping and restoration, allowing users to interact with elements outside the modal's container.
13638
+
13639
+ Combine this with \`UNSAFE_enableInert\` to ensure that background elements behind the modal (inside the \`FrameProvider\` container) remain truly non-interactive while the host page's UI stays accessible.
13640
+
13641
+ \`\`\`jsx
13642
+ import {
13643
+ Dialog,
13644
+ Modal,
13645
+ ModalTrigger,
13646
+ ModalContent,
13647
+ ModalClose,
13648
+ DialogTitle,
13649
+ } from "../../utils";
13144
13650
 
13145
13651
  <Modal>
13146
13652
  <ModalTrigger>
@@ -16204,25 +16710,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
16204
16710
  );
16205
16711
  };
16206
16712
 
16207
- export const PopoverContainedFocusExample = () => {
16208
- return (
16209
- <Popover type="dialog">
16210
- <PopoverTrigger>
16211
- <ActionButton label="Open" />
16212
- </PopoverTrigger>
16213
- <PopoverContent shouldContainFocus={true}>
16214
- <Dialog
16215
- size="content"
16216
- className={sprinkles({
16217
- padding: "md",
16218
- display: "flex",
16219
- gap: "xl",
16220
- flexDirection: "column",
16221
- })}
16222
- style={{
16223
- width: 200,
16224
- }}
16225
- >
16713
+ export const PopoverContainedFocusExample = ({
16714
+ shouldContainFocus = true,
16715
+ }: {
16716
+ shouldContainFocus?: boolean;
16717
+ }) => {
16718
+ const content = (
16719
+ <>
16226
16720
  <Text type="label">The focus is contained within the popover.</Text>
16227
16721
 
16228
16722
  <TextInput
@@ -16234,10 +16728,55 @@ export const PopoverContainedFocusExample = () => {
16234
16728
  label="Button"
16235
16729
  style={{ width: "100%", justifyContent: "center" }}
16236
16730
  />
16731
+ </>
16732
+ );
16733
+ const contentClassName = sprinkles({
16734
+ padding: "md",
16735
+ display: "flex",
16736
+ gap: "xl",
16737
+ flexDirection: "column",
16738
+ });
16739
+
16740
+ const popover = (
16741
+ <Popover type="dialog">
16742
+ <PopoverTrigger>
16743
+ <ActionButton label="Open" />
16744
+ </PopoverTrigger>
16745
+ <PopoverContent
16746
+ shouldContainFocus={shouldContainFocus}
16747
+ isNonModal={!shouldContainFocus}
16748
+ >
16749
+ {shouldContainFocus ? (
16750
+ <Dialog
16751
+ size="content"
16752
+ className={contentClassName}
16753
+ style={{ width: 200 }}
16754
+ >
16755
+ {content}
16237
16756
  </Dialog>
16757
+ ) : (
16758
+ // No Dialog: useDialog opts the overlay into focus containment,
16759
+ // which disables the Tab-out restore path.
16760
+ <Box className={contentClassName} style={{ width: 200 }}>
16761
+ {content}
16762
+ </Box>
16763
+ )}
16238
16764
  </PopoverContent>
16239
16765
  </Popover>
16240
16766
  );
16767
+
16768
+ // The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
16769
+ // here rebaselines its visual snapshot.
16770
+ if (shouldContainFocus) {
16771
+ return popover;
16772
+ }
16773
+
16774
+ return (
16775
+ <Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
16776
+ {popover}
16777
+ <ActionButton label="After" />
16778
+ </Box>
16779
+ );
16241
16780
  };
16242
16781
 
16243
16782
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -18315,18 +18854,20 @@ function App() {
18315
18854
  }
18316
18855
  \`\`\`
18317
18856
 
18318
- We can group items into sections by passing the \`items\` prop an array of objects that contain a \`type\` property with a value of \`section\`. The \`children\` property of the section object should be an array of options. The \`id\` property of the section object should be unique.
18857
+ 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.
18858
+
18859
+ Sections are visually separated by a divider above every section but the first.
18319
18860
 
18320
18861
  \`\`\`jsx
18321
18862
  const itemsWithSections = [
18322
18863
  {
18323
18864
  id: "solid",
18324
- type: "section",
18865
+ title: "Solid",
18325
18866
  children: items,
18326
18867
  },
18327
18868
  {
18328
18869
  id: "dashed",
18329
- type: "section",
18870
+ title: "Dashed",
18330
18871
  children: [
18331
18872
  {
18332
18873
  id: "ellipse-dashed",
@@ -18350,6 +18891,18 @@ const itemsWithSections = [
18350
18891
  <Select items={itemsWithSections} label="Label" />;
18351
18892
  \`\`\`
18352
18893
 
18894
+ 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\`.
18895
+
18896
+ <a href="?path=/story/core-forms-select--with-section-headers">View story</a>
18897
+
18898
+ \`\`\`jsx
18899
+ <Select
18900
+ items={itemsWithSections}
18901
+ showSectionHeader={true}
18902
+ aria-label="Choose an item"
18903
+ />
18904
+ \`\`\`
18905
+
18353
18906
  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.
18354
18907
 
18355
18908
  \`\`\`jsx
@@ -18553,14 +19106,16 @@ import { Virtualizer, ListLayout } from "@baseline-ui/core";
18553
19106
  \`\`\`
18554
19107
 
18555
19108
  | Key | Function |
18556
- | ----------- | ------------------------------------------------------------------------------------ |
19109
+ | -------------------- | ------------------------------------------------------------------------------------ |
19110
+ | \`Tab\` | Moves focus to and from the trigger. The popup itself is not a tab stop |
18557
19111
  | \`Space\` | Opens the listbox popup or toggles selection of the focused item in multiselect mode |
18558
19112
  | \`Enter\` | Opens the listbox popup or selects the focused item if the popup is open |
18559
19113
  | \`Escape\` | Closes the listbox popup |
18560
19114
  | \`ArrowDown\` | Opens the listbox popup and focuses the first item if no item is focused |
18561
19115
  | \`ArrowUp\` | Opens the listbox popup and focuses the last item if no item is focused |
18562
- | \`Home\` | focuses the first item |
18563
- | \`End\` | focuses the last item |
19116
+ | \`Home\` | Focuses the first item |
19117
+ | \`End\` | Focuses the last item |
19118
+ | Printable characters | Focuses the first item matching the typed characters |
18564
19119
 
18565
19120
  **Note:** In multiselect mode (\`selectionMode="multiple"\`), the \`Space\` key toggles selection of the focused item, and the popover remains open after selection.
18566
19121
 
@@ -18590,6 +19145,8 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
18590
19145
  | \`.BaselineUI-Select-Label\` | Label element |
18591
19146
  | \`.BaselineUI-Select-Popover\` | Popover container |
18592
19147
  | \`.BaselineUI-Select-SearchInput\` | Search input (when wrapped with \`Autocomplete\`) |
19148
+ | \`.BaselineUI-ListBox\` | Listbox inside the popover |
19149
+ | \`.BaselineUI-ListBox-Section\` | A section within the listbox |
18593
19150
  | \`[data-disabled]\` | Applied when \`isDisabled\` is true |
18594
19151
  | \`[data-readonly]\` | Applied when \`isReadOnly\` is true |
18595
19152
  | \`[data-focused]\` | Applied when the trigger is focused |
@@ -18598,6 +19155,7 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
18598
19155
  | \`[data-pressed]\` | Applied when the trigger is pressed |
18599
19156
  | \`[data-open]\` | Applied when the popover is open |
18600
19157
 
19158
+ * **ComboBox** \u2014 Use this instead when users should be able to type directly into the trigger to filter or enter a value
18601
19159
  * **IconSelect** \u2014 Select variant with an icon trigger instead of a text button
18602
19160
  * **ButtonSelect** \u2014 Select variant styled as a button
18603
19161
  * **ListBox** \u2014 Standalone listbox without the trigger/popover wrapper
@@ -18740,125 +19298,125 @@ hideSelectAll?: boolean
18740
19298
  */
18741
19299
  hideClear?: boolean
18742
19300
  }`,stories:{usage:[{id:"core-forms-select-multiselect--basic",name:"Basic",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18743
- 29 | type Story = StoryObj<typeof meta>;
18744
- 30 |
18745
- > 31 | export {
19301
+ 32 | type Story = StoryObj<typeof meta>;
19302
+ 33 |
19303
+ > 34 | export {
18746
19304
  | ^
18747
- 32 | Basic,
18748
- 33 | WithLabel,
18749
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-label",name:"WithLabel",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18750
- 29 | type Story = StoryObj<typeof meta>;
18751
- 30 |
18752
- > 31 | export {
19305
+ 35 | Basic,
19306
+ 36 | WithLabel,
19307
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-label",name:"WithLabel",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19308
+ 32 | type Story = StoryObj<typeof meta>;
19309
+ 33 |
19310
+ > 34 | export {
18753
19311
  | ^
18754
- 32 | Basic,
18755
- 33 | WithLabel,
18756
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-description",name:"WithDescription",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18757
- 29 | type Story = StoryObj<typeof meta>;
18758
- 30 |
18759
- > 31 | export {
19312
+ 35 | Basic,
19313
+ 36 | WithLabel,
19314
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-description",name:"WithDescription",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19315
+ 32 | type Story = StoryObj<typeof meta>;
19316
+ 33 |
19317
+ > 34 | export {
18760
19318
  | ^
18761
- 32 | Basic,
18762
- 33 | WithLabel,
18763
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-error",name:"WithError",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18764
- 29 | type Story = StoryObj<typeof meta>;
18765
- 30 |
18766
- > 31 | export {
19319
+ 35 | Basic,
19320
+ 36 | WithLabel,
19321
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-error",name:"WithError",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19322
+ 32 | type Story = StoryObj<typeof meta>;
19323
+ 33 |
19324
+ > 34 | export {
18767
19325
  | ^
18768
- 32 | Basic,
18769
- 33 | WithLabel,
18770
- 34 | 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
18771
- 29 | type Story = StoryObj<typeof meta>;
18772
- 30 |
18773
- > 31 | export {
19326
+ 35 | Basic,
19327
+ 36 | WithLabel,
19328
+ 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
19329
+ 32 | type Story = StoryObj<typeof meta>;
19330
+ 33 |
19331
+ > 34 | export {
18774
19332
  | ^
18775
- 32 | Basic,
18776
- 33 | WithLabel,
18777
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-warning",name:"WithWarning",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18778
- 29 | type Story = StoryObj<typeof meta>;
18779
- 30 |
18780
- > 31 | export {
19333
+ 35 | Basic,
19334
+ 36 | WithLabel,
19335
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-warning",name:"WithWarning",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19336
+ 32 | type Story = StoryObj<typeof meta>;
19337
+ 33 |
19338
+ > 34 | export {
18781
19339
  | ^
18782
- 32 | Basic,
18783
- 33 | WithLabel,
18784
- 34 | 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
18785
- 29 | type Story = StoryObj<typeof meta>;
18786
- 30 |
18787
- > 31 | export {
19340
+ 35 | Basic,
19341
+ 36 | WithLabel,
19342
+ 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
19343
+ 32 | type Story = StoryObj<typeof meta>;
19344
+ 33 |
19345
+ > 34 | export {
18788
19346
  | ^
18789
- 32 | Basic,
18790
- 33 | WithLabel,
18791
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--ghost",name:"Ghost",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18792
- 29 | type Story = StoryObj<typeof meta>;
18793
- 30 |
18794
- > 31 | export {
19347
+ 35 | Basic,
19348
+ 36 | WithLabel,
19349
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--ghost",name:"Ghost",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19350
+ 32 | type Story = StoryObj<typeof meta>;
19351
+ 33 |
19352
+ > 34 | export {
18795
19353
  | ^
18796
- 32 | Basic,
18797
- 33 | WithLabel,
18798
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled",name:"WithDisabled",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18799
- 29 | type Story = StoryObj<typeof meta>;
18800
- 30 |
18801
- > 31 | export {
19354
+ 35 | Basic,
19355
+ 36 | WithLabel,
19356
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled",name:"WithDisabled",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19357
+ 32 | type Story = StoryObj<typeof meta>;
19358
+ 33 |
19359
+ > 34 | export {
18802
19360
  | ^
18803
- 32 | Basic,
18804
- 33 | WithLabel,
18805
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled-items",name:"WithDisabledItems",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18806
- 29 | type Story = StoryObj<typeof meta>;
18807
- 30 |
18808
- > 31 | export {
19361
+ 35 | Basic,
19362
+ 36 | WithLabel,
19363
+ 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
19364
+ 32 | type Story = StoryObj<typeof meta>;
19365
+ 33 |
19366
+ > 34 | export {
18809
19367
  | ^
18810
- 32 | Basic,
18811
- 33 | WithLabel,
18812
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-sections",name:"WithSections",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18813
- 29 | type Story = StoryObj<typeof meta>;
18814
- 30 |
18815
- > 31 | export {
19368
+ 35 | Basic,
19369
+ 36 | WithLabel,
19370
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--with-sections",name:"WithSections",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19371
+ 32 | type Story = StoryObj<typeof meta>;
19372
+ 33 |
19373
+ > 34 | export {
18816
19374
  | ^
18817
- 32 | Basic,
18818
- 33 | WithLabel,
18819
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-default-open",name:"WithDefaultOpen",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18820
- 29 | type Story = StoryObj<typeof meta>;
18821
- 30 |
18822
- > 31 | export {
19375
+ 35 | Basic,
19376
+ 36 | WithLabel,
19377
+ 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
19378
+ 32 | type Story = StoryObj<typeof meta>;
19379
+ 33 |
19380
+ > 34 | export {
18823
19381
  | ^
18824
- 32 | Basic,
18825
- 33 | WithLabel,
18826
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-controlled-open",name:"WithControlledOpen",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18827
- 29 | type Story = StoryObj<typeof meta>;
18828
- 30 |
18829
- > 31 | export {
19382
+ 35 | Basic,
19383
+ 36 | WithLabel,
19384
+ 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
19385
+ 32 | type Story = StoryObj<typeof meta>;
19386
+ 33 |
19387
+ > 34 | export {
18830
19388
  | ^
18831
- 32 | Basic,
18832
- 33 | WithLabel,
18833
- 34 | 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
18834
- 29 | type Story = StoryObj<typeof meta>;
18835
- 30 |
18836
- > 31 | export {
19389
+ 35 | Basic,
19390
+ 36 | WithLabel,
19391
+ 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
19392
+ 32 | type Story = StoryObj<typeof meta>;
19393
+ 33 |
19394
+ > 34 | export {
18837
19395
  | ^
18838
- 32 | Basic,
18839
- 33 | WithLabel,
18840
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--scrollable",name:"Scrollable",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18841
- 29 | type Story = StoryObj<typeof meta>;
18842
- 30 |
18843
- > 31 | export {
19396
+ 35 | Basic,
19397
+ 36 | WithLabel,
19398
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--scrollable",name:"Scrollable",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19399
+ 32 | type Story = StoryObj<typeof meta>;
19400
+ 33 |
19401
+ > 34 | export {
18844
19402
  | ^
18845
- 32 | Basic,
18846
- 33 | WithLabel,
18847
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--long-list",name:"LongList",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18848
- 29 | type Story = StoryObj<typeof meta>;
18849
- 30 |
18850
- > 31 | export {
19403
+ 35 | Basic,
19404
+ 36 | WithLabel,
19405
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--long-list",name:"LongList",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
19406
+ 32 | type Story = StoryObj<typeof meta>;
19407
+ 33 |
19408
+ > 34 | export {
18851
19409
  | ^
18852
- 32 | Basic,
18853
- 33 | WithLabel,
18854
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-custom-trigger",name:"WithCustomTrigger",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18855
- 29 | type Story = StoryObj<typeof meta>;
18856
- 30 |
18857
- > 31 | export {
19410
+ 35 | Basic,
19411
+ 36 | WithLabel,
19412
+ 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
19413
+ 32 | type Story = StoryObj<typeof meta>;
19414
+ 33 |
19415
+ > 34 | export {
18858
19416
  | ^
18859
- 32 | Basic,
18860
- 33 | WithLabel,
18861
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--read-only",name:"Read Only",snippet:`const ReadOnly = () => <Select
19417
+ 35 | Basic,
19418
+ 36 | WithLabel,
19419
+ 37 | WithDescription,`}},{id:"core-forms-select-multiselect--read-only",name:"Read Only",snippet:`const ReadOnly = () => <Select
18862
19420
  aria-label="Choose Stroke Style"
18863
19421
  items={items}
18864
19422
  placeholder="Choose Stroke Style"
@@ -18917,7 +19475,29 @@ hideClear?: boolean
18917
19475
  aria-label="Choose Stroke Style"
18918
19476
  items={items}
18919
19477
  placeholder="Choose Stroke Style"
18920
- selectionMode="multiple" />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
19478
+ selectionMode="multiple" />;`},{id:"core-forms-select-multiselect--with-error-and-tags",name:"With Error And Tags",snippet:`const WithErrorAndTags = () => <Select
19479
+ aria-label="Choose Stroke Style"
19480
+ items={items}
19481
+ placeholder="Choose Stroke Style"
19482
+ selectionMode="multiple"
19483
+ label="Label"
19484
+ validationState="error"
19485
+ errorMessage="Pick fewer shapes"
19486
+ defaultValue={["ellipse", "square", "polygon"]}
19487
+ 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
19488
+ aria-label="Choose Stroke Style"
19489
+ items={items}
19490
+ placeholder="Choose Stroke Style"
19491
+ selectionMode="multiple"
19492
+ defaultValue={["long1", "long2"]} />;`},{id:"core-forms-select-multiselect--read-only-with-tags-open",name:"Read Only With Tags Open",snippet:`const ReadOnlyWithTagsOpen = () => <Select
19493
+ aria-label="Choose Stroke Style"
19494
+ items={items}
19495
+ placeholder="Choose Stroke Style"
19496
+ selectionMode="multiple"
19497
+ label="Label"
19498
+ isReadOnly
19499
+ value={["ellipse", "square"]}
19500
+ defaultOpen />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
18921
19501
  import React from "react";
18922
19502
  import { useFilter } from "react-aria";
18923
19503
  import { Autocomplete, Virtualizer } from "react-aria-components";
@@ -18926,11 +19506,16 @@ import { useListData } from "react-stately";
18926
19506
  import { ActionButton } from "../../ActionButton";
18927
19507
  import { items } from "../../ListBox/__tests__/testComponents";
18928
19508
  import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
18929
- import { VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS } from "../../Virtualizer";
19509
+ import { itemsWithSectionTitles } from "../../UNSAFE_ListBox/__tests__/testComponents";
19510
+ import {
19511
+ ListLayout,
19512
+ VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS,
19513
+ } from "../../Virtualizer";
18930
19514
  import { IconSelect } from "../IconSelect";
18931
19515
  import { Select } from "../Select";
18932
19516
 
18933
19517
  import type { IconSelectProps } from "../Select.types";
19518
+ import type { Key } from "@react-types/shared";
18934
19519
 
18935
19520
  export const SelectExample: React.FC<
18936
19521
  Omit<React.ComponentProps<typeof Select>, "items">
@@ -18938,13 +19523,28 @@ export const SelectExample: React.FC<
18938
19523
  return (
18939
19524
  <Select
18940
19525
  placeholder="Choose an item"
18941
- {...args}
18942
19526
  optionClassName={(item) => item.label}
19527
+ {...args}
18943
19528
  items={items}
18944
19529
  />
18945
19530
  );
18946
19531
  };
18947
19532
 
19533
+ // "Apples" is a substring of "Pineapples" \u2014 exercises exact string matching
19534
+ // in the tester's option() locator.
19535
+ const substringItems = [
19536
+ { id: "apples", label: "Apples" },
19537
+ { id: "pineapples", label: "Pineapples" },
19538
+ ];
19539
+
19540
+ export const SelectSubstringLabelsExample: React.FC<
19541
+ Omit<React.ComponentProps<typeof Select>, "items">
19542
+ > = (args) => {
19543
+ return (
19544
+ <Select placeholder="Choose an item" {...args} items={substringItems} />
19545
+ );
19546
+ };
19547
+
18948
19548
  export const SelectGetTargetRectExample: React.FC = () => {
18949
19549
  const [called, setCalled] = React.useState(false);
18950
19550
 
@@ -19001,90 +19601,228 @@ export const SelectCustomTriggerExample: React.FC<
19001
19601
  };
19002
19602
 
19003
19603
  export const IconSelectExample: React.FC<
19004
- Omit<IconSelectProps, "items" | "icon" | "aria-label">
19604
+ Omit<IconSelectProps, "items" | "aria-label">
19005
19605
  > = (args) => {
19006
19606
  return (
19007
19607
  <IconSelect
19008
19608
  placeholder="Choose an item"
19609
+ icon={EllipseIcon}
19009
19610
  {...args}
19010
19611
  items={items}
19011
- icon={EllipseIcon}
19012
19612
  aria-label="Aria Label"
19013
19613
  />
19014
19614
  );
19015
19615
  };
19016
19616
 
19017
- export const SelectWithVirtualizeAutocompleteExample: React.FC<
19617
+ export const VirtualSelectWithSectionHeadersExample: React.FC<
19018
19618
  Omit<React.ComponentProps<typeof Select>, "items">
19019
19619
  > = (args) => {
19020
- const list = useListData({
19021
- initialItems: virtualizeAutocompleteItems,
19022
- });
19023
- const { contains } = useFilter({ sensitivity: "base" });
19024
- const filter = (textValue, inputValue) => contains(textValue, inputValue);
19025
19620
  return (
19026
- <Virtualizer {...VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS.LIST_BOX}>
19027
- <Autocomplete filter={filter}>
19621
+ <Virtualizer
19622
+ layout={ListLayout}
19623
+ layoutOptions={{
19624
+ rowHeight: 36,
19625
+ headingHeight: 40,
19626
+ }}
19627
+ >
19028
19628
  <Select
19629
+ aria-label="Choose an item"
19630
+ placeholder="Choose an item"
19631
+ showSectionHeader={true}
19029
19632
  {...args}
19030
- items={list.items}
19031
- placeholder="Select multiple options"
19032
- maxHeight={300}
19033
- style={{
19034
- width: 400,
19035
- }}
19633
+ items={itemsWithSectionTitles}
19634
+ defaultOpen={true}
19635
+ maxHeight={400}
19036
19636
  />
19037
- </Autocomplete>
19038
19637
  </Virtualizer>
19039
19638
  );
19040
- };`},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.
19041
-
19042
- \`\`\`jsx
19043
- import { Separator } from "../../utils";
19044
-
19045
- <Separator />;
19046
- \`\`\`
19047
-
19048
- By default, the separator is horizontal.
19049
-
19050
- \`\`\`jsx
19051
- import { Separator } from "../../utils";
19052
-
19053
- <div>
19054
- Section 1
19055
- <Separator />
19056
- Section 2
19057
- </div>;
19058
- \`\`\`
19059
-
19060
- The \`orientation\` prop can be used to change the orientation of the separator.
19061
-
19062
- \`\`\`jsx
19063
- import { Separator } from "../../utils";
19639
+ };
19064
19640
 
19065
- <div>
19066
- Section 1
19067
- <Separator orientation="vertical" variant="secondary" />
19068
- Section 2
19069
- </div>;
19070
- \`\`\`
19641
+ export const SelectWithSectionHeadersExample: React.FC<
19642
+ Omit<React.ComponentProps<typeof Select>, "items">
19643
+ > = (args) => {
19644
+ return (
19645
+ <Select
19646
+ aria-label="Choose an item"
19647
+ placeholder="Choose an item"
19648
+ showSectionHeader={true}
19649
+ {...args}
19650
+ items={itemsWithSectionTitles}
19651
+ defaultOpen={true}
19652
+ />
19653
+ );
19654
+ };
19071
19655
 
19072
- | Selector | Description |
19073
- | ------------------ | ----------------------------------------------------------------------- |
19074
- | \\[data-orientation] | The orientation of the separator. It can be \`horizontal\` or \`vertical\`. |`,props:`interface SeparatorProps {
19075
- /**
19076
- * The orientation of the separator.
19077
- *
19078
- * @default 'horizontal'
19079
- */
19080
- orientation?: "horizontal" | "vertical"
19081
19656
  /**
19082
- * The HTML element type that will be used to render the separator.
19657
+ * Owns \`isOpen\` so the controlled-open contract can be driven from outside the
19658
+ * Select, with a sibling readout of the current state.
19083
19659
  */
19084
- elementType?: string
19085
- /**
19086
- * @deprecated Do not use in new components. This is a legacy block
19087
- * identifier; new components should exclude it via
19660
+ export const ControlledOpenSelectExample: React.FC<
19661
+ Omit<React.ComponentProps<typeof Select>, "items">
19662
+ > = (args) => {
19663
+ const [isOpen, setIsOpen] = React.useState(false);
19664
+
19665
+ return (
19666
+ <>
19667
+ <Select
19668
+ aria-label="Choose an item"
19669
+ placeholder="Choose an item"
19670
+ {...args}
19671
+ items={items}
19672
+ isOpen={isOpen}
19673
+ onOpenChange={setIsOpen}
19674
+ />
19675
+ <button
19676
+ data-testid="open-externally"
19677
+ onClick={() => {
19678
+ setIsOpen(true);
19679
+ }}
19680
+ >
19681
+ Open
19682
+ </button>
19683
+ <span data-testid="open-state">{String(isOpen)}</span>
19684
+ </>
19685
+ );
19686
+ };
19687
+
19688
+ /**
19689
+ * Custom trigger that renders the multi-select half of the \`renderTrigger\`
19690
+ * payload \u2014 \`selectionMode\`, \`maxCount\` and \`onRemove\` \u2014 which the default
19691
+ * \`SelectButton\` otherwise keeps to itself.
19692
+ */
19693
+ export const MultiSelectCustomTriggerExample: React.FC<
19694
+ Omit<React.ComponentProps<typeof Select>, "items">
19695
+ > = (args) => {
19696
+ return (
19697
+ <Select
19698
+ aria-label="Choose an item"
19699
+ selectionMode="multiple"
19700
+ {...args}
19701
+ items={items}
19702
+ renderTrigger={({
19703
+ buttonProps,
19704
+ ref,
19705
+ selectedValue,
19706
+ selectionMode,
19707
+ onRemove,
19708
+ maxCount,
19709
+ }) => (
19710
+ <div>
19711
+ <ActionButton
19712
+ {...buttonProps}
19713
+ ref={ref}
19714
+ label={\`\${selectionMode}|\${maxCount}|\${selectedValue?.length ?? 0}\`}
19715
+ variant="popover"
19716
+ />
19717
+ <button
19718
+ data-testid="remove-first"
19719
+ onClick={() => {
19720
+ const first = selectedValue?.[0];
19721
+ if (first) {
19722
+ onRemove?.(new Set([first.id]));
19723
+ }
19724
+ }}
19725
+ >
19726
+ Remove first
19727
+ </button>
19728
+ </div>
19729
+ )}
19730
+ />
19731
+ );
19732
+ };
19733
+
19734
+ export const SelectWithVirtualizeAutocompleteExample: React.FC<
19735
+ Omit<React.ComponentProps<typeof Select>, "items">
19736
+ > = (args) => {
19737
+ const list = useListData({
19738
+ initialItems: virtualizeAutocompleteItems,
19739
+ });
19740
+ const { contains } = useFilter({ sensitivity: "base" });
19741
+ const filter = (textValue, inputValue) => contains(textValue, inputValue);
19742
+ return (
19743
+ <Virtualizer {...VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS.LIST_BOX}>
19744
+ <Autocomplete filter={filter}>
19745
+ <Select
19746
+ {...args}
19747
+ items={list.items}
19748
+ placeholder="Select multiple options"
19749
+ maxHeight={300}
19750
+ style={{
19751
+ width: 400,
19752
+ }}
19753
+ />
19754
+ </Autocomplete>
19755
+ </Virtualizer>
19756
+ );
19757
+ };
19758
+
19759
+ /**
19760
+ * \`optionClassName\` reads component state, and \`items\` is module-level so React Aria's
19761
+ * per-item element cache applies. Pinned open so the state change comes from selecting an
19762
+ * option rather than from an outside press the popover would swallow.
19763
+ */
19764
+ export const CountingSelectExample: React.FC = () => {
19765
+ const [selected, setSelected] = React.useState<Key | null>(null);
19766
+
19767
+ return (
19768
+ <Select
19769
+ aria-label="Counting select"
19770
+ items={items}
19771
+ isOpen={true}
19772
+ optionClassName={(item) =>
19773
+ item.id === selected ? "option-picked" : "option-plain"
19774
+ }
19775
+ onSelectionChange={setSelected}
19776
+ />
19777
+ );
19778
+ };`},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.
19779
+
19780
+ \`\`\`jsx
19781
+ import { Separator } from "../../utils";
19782
+
19783
+ <Separator />;
19784
+ \`\`\`
19785
+
19786
+ By default, the separator is horizontal.
19787
+
19788
+ \`\`\`jsx
19789
+ import { Separator } from "../../utils";
19790
+
19791
+ <div>
19792
+ Section 1
19793
+ <Separator />
19794
+ Section 2
19795
+ </div>;
19796
+ \`\`\`
19797
+
19798
+ The \`orientation\` prop can be used to change the orientation of the separator.
19799
+
19800
+ \`\`\`jsx
19801
+ import { Separator } from "../../utils";
19802
+
19803
+ <div>
19804
+ Section 1
19805
+ <Separator orientation="vertical" variant="secondary" />
19806
+ Section 2
19807
+ </div>;
19808
+ \`\`\`
19809
+
19810
+ | Selector | Description |
19811
+ | ------------------ | ----------------------------------------------------------------------- |
19812
+ | \\[data-orientation] | The orientation of the separator. It can be \`horizontal\` or \`vertical\`. |`,props:`interface SeparatorProps {
19813
+ /**
19814
+ * The orientation of the separator.
19815
+ *
19816
+ * @default 'horizontal'
19817
+ */
19818
+ orientation?: "horizontal" | "vertical"
19819
+ /**
19820
+ * The HTML element type that will be used to render the separator.
19821
+ */
19822
+ elementType?: string
19823
+ /**
19824
+ * @deprecated Do not use in new components. This is a legacy block
19825
+ * identifier; new components should exclude it via
19088
19826
  * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
19089
19827
  * Retained on existing components for backward compatibility.
19090
19828
  *
@@ -23293,7 +24031,7 @@ export const TaggedPaginationExample: React.FC<
23293
24031
  {...props}
23294
24032
  />
23295
24033
  );
23296
- };`},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 "@storybook/addon-docs/blocks";\n\n<Text type="subtitle" size="sm">\n Text\n</Text>;\n```\n\nThe `Text` component supports the following types: `title`, `subtitle`, `body`, `value` and `helper`. It\nalso supports the following sizes: `sm`, `md`, and `lg`. You can see all the variants [here](/story/core-text--variants)',props:`interface TextProps {
24034
+ };`},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 {
23297
24035
  /**
23298
24036
  * @deprecated Do not use in new components. This is a legacy block
23299
24037
  * identifier; new components should exclude it via
@@ -23349,6 +24087,13 @@ children: React.ReactNode
23349
24087
  * @default "span"
23350
24088
  */
23351
24089
  elementType?: React.ElementType
24090
+ /**
24091
+ * A slot name that wires this text into a parent container's context \u2014
24092
+ * e.g. \`slot="description"\` or \`slot="label"\` \u2014 so the container can
24093
+ * associate it via \`aria-describedby\` / \`aria-labelledby\` without manually
24094
+ * threading an \`id\`. Pass \`null\` to opt out of an ambient slot.
24095
+ */
24096
+ slot?: string | null
23352
24097
  }`,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) => (
23353
24098
  <VariantViewer<React.ComponentProps<typeof Text>>
23354
24099
  header={["Small", "Medium", "Large"]}
@@ -23383,8 +24128,24 @@ elementType?: React.ElementType
23383
24128
  }}
23384
24129
  defaultProps={args}
23385
24130
  />
23386
- );`}],implementation:`import React from "react";
24131
+ );`},{id:"core-content-text--slots",name:"Slots",snippet:`const Slots = () => (
24132
+ <TextField>
24133
+ {/* The raw React Aria Label carries no Baseline styling, so it inherits
24134
+ the storybook default (black) text and fails contrast against the dark
24135
+ theme background. Give it the themed foreground; the slot wiring below
24136
+ is the point of this story. */}
24137
+ <Label style={{ color: themeVars.color.text.primary }}>
24138
+ Display name
24139
+ </Label>
24140
+ <Input />
24141
+ <Text slot="description" type="helper" size="sm">
24142
+ This is shown to other members of your workspace.
24143
+ </Text>
24144
+ </TextField>
24145
+ );`,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";
24146
+ import { TextContext } from "react-aria-components";
23387
24147
 
24148
+ import { UNSAFE_ListBox } from "../../UNSAFE_ListBox";
23388
24149
  import { Text } from "../Text";
23389
24150
 
23390
24151
  export function TextWithRef() {
@@ -23405,65 +24166,61 @@ export function TextWithRef() {
23405
24166
  <span data-testid="tag-name">{tag}</span>
23406
24167
  </>
23407
24168
  );
24169
+ }
24170
+
24171
+ const SLOTTED_TEXT_CONTEXT = {
24172
+ slots: {
24173
+ description: { id: "provided-description-id" },
24174
+ },
24175
+ };
24176
+
24177
+ export function TextInSlotContext() {
24178
+ return (
24179
+ <TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
24180
+ <Text slot="description" data-block-id="slotted">
24181
+ Description
24182
+ </Text>
24183
+ </TextContext.Provider>
24184
+ );
24185
+ }
24186
+
24187
+ export function TextBareInSlotContext() {
24188
+ // A slotless <Text> inside a slotted context that has no default slot must
24189
+ // render (opt out) rather than throw "A slot prop is required".
24190
+ return (
24191
+ <TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
24192
+ <Text id="bare-id" data-testid="bare">
24193
+ Description
24194
+ </Text>
24195
+ </TextContext.Provider>
24196
+ );
24197
+ }
24198
+
24199
+ export function TextInListBoxRenderOption() {
24200
+ // A bare <Text> in renderOption lands inside RAC's ListBoxItem, which provides
24201
+ // a TextContext whose default slot maps to the option's generated label id.
24202
+ // A slotless <Text> must opt out (no inherited id) rather than silently adopt
24203
+ // that label id \u2014 otherwise two Texts in one option collide on the same id.
24204
+ return (
24205
+ <UNSAFE_ListBox
24206
+ aria-label="Options"
24207
+ items={[{ id: "opt-1", label: "Option 1" }]}
24208
+ renderOption={(item) => (
24209
+ <Text data-testid="render-option">{item.label}</Text>
24210
+ )}
24211
+ />
24212
+ );
24213
+ }
24214
+
24215
+ export function TextOptOutOfSlotContext() {
24216
+ return (
24217
+ <TextContext.Provider value={SLOTTED_TEXT_CONTEXT}>
24218
+ <Text slot={null} id="own-id" data-block-id="opt-out">
24219
+ Description
24220
+ </Text>
24221
+ </TextContext.Provider>
24222
+ );
23408
24223
  }`},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 {
23409
- /**
23410
- * @deprecated Do not use in new components. This is a legacy block
23411
- * identifier; new components should exclude it via
23412
- * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
23413
- * Retained on existing components for backward compatibility.
23414
- *
23415
- * The unique identifier for the block. This is used to identify the block in
23416
- * the DOM and in the block map. It is added as a data attribute
23417
- * \`data-block-id\` to the root element of the block if a DOM node is
23418
- * rendered.
23419
- */
23420
- data-block-id?: string
23421
- /**
23422
- * @deprecated Do not use in new components. This is a legacy block group
23423
- * marker; new components should exclude it via
23424
- * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
23425
- * Retained on existing components for backward compatibility.
23426
- *
23427
- * Represents a data block group. This is similar to \`data-block-id\` but it
23428
- * doesn't have to be unique just like \`class\`. This is used to group blocks
23429
- * together in the DOM and in the block map. It is added as a data attribute
23430
- * \`data-block-class\` to the root element of the block if a DOM node is
23431
- * rendered.
23432
- */
23433
- data-block-class?: string
23434
- /**
23435
- * The className applied to the root element of the component.
23436
- */
23437
- className?: string
23438
- /**
23439
- * The style applied to the root element of the component.
23440
- */
23441
- style?: React.CSSProperties
23442
- /**
23443
- * The description to display below the input.
23444
- */
23445
- description?: string
23446
- /**
23447
- * The error message to display when the input is in an error state.
23448
- */
23449
- errorMessage?: string
23450
- /**
23451
- * The warning message to display when the input is in a warning state.
23452
- */
23453
- warningMessage?: string
23454
- /**
23455
- * The style object to apply to the input element
23456
- */
23457
- inputStyle?: React.CSSProperties
23458
- /**
23459
- * The class name to apply to the input element
23460
- */
23461
- inputClassName?: string
23462
- /**
23463
- * The number of visible text rows for the multi-line input. Only applies when
23464
- * \`isMultiLine\` is set.
23465
- */
23466
- rows?: number
23467
24224
  variant?: any
23468
24225
  labelPosition?: any
23469
24226
  isMultiLine?: any
@@ -26806,6 +27563,7 @@ export const VirtualizedTreeViewWithRenameExample: React.FC<
26806
27563
  VirtualizedScrollIntoViewExample,
26807
27564
  VirtualListBoxGridLayoutExample,
26808
27565
  VirtualListBoxListLayoutExample,
27566
+ VirtualListBoxWithSectionHeadersExample,
26809
27567
  VirtualListBoxWithSectionsExample,
26810
27568
  } 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.
26811
27569
 
@@ -26902,6 +27660,34 @@ const fonts = [
26902
27660
  />;
26903
27661
  \`\`\`
26904
27662
 
27663
+ Rendered options and sections are cached per item object. Reading component state from
27664
+ \`renderOption\`, \`optionClassName\`, \`optionStyle\`, \`renderSectionHeader\`, \`sectionClassName\`
27665
+ or \`sectionStyle\` works \u2014 the options re-render when the state changes \u2014 and toggling
27666
+ \`showSectionHeader\` or \`withSectionHeaderPadding\` does too.
27667
+
27668
+ The cost only applies when \`items\` is memoized, since an inline \`items={data.map(...)}\`
27669
+ produces new item objects every render and the cache never applies in the first place. With
27670
+ stable items, any of these props changing identity rebuilds every option and section, so
27671
+ memoize the ones that don't depend on state \u2014 including object literals passed to
27672
+ \`optionStyle\`, which are new on every render.
27673
+
27674
+ \`\`\`jsx
27675
+ const [highlighted, setHighlighted] = useState(null);
27676
+
27677
+ // Rebuilds only when the highlight moves; \`fonts\` is module-level, so the cache applies.
27678
+ const optionStyle = useCallback(
27679
+ (item) => ({ fontWeight: item.id === highlighted ? "bold" : "normal" }),
27680
+ [highlighted],
27681
+ );
27682
+
27683
+ <UNSAFE_ListBox
27684
+ items={fonts}
27685
+ aria-label="Fonts"
27686
+ optionStyle={optionStyle}
27687
+ onAction={setHighlighted}
27688
+ />;
27689
+ \`\`\`
27690
+
26905
27691
  \`\`\`jsx
26906
27692
  <UNSAFE_ListBox
26907
27693
  items={items}
@@ -26999,6 +27785,10 @@ style?: React.CSSProperties
26999
27785
  /**
27000
27786
  * The custom render function for the listbox options.
27001
27787
  *
27788
+ * Reading component state here works, at the cost of rebuilding every cached option and
27789
+ * section whenever the function's identity changes \u2014 see {@link UNSAFE_ListBoxProps.items}
27790
+ * for when that cost applies.
27791
+ *
27002
27792
  * @param item ListOption
27003
27793
  * @param options ListBoxItemRenderProps
27004
27794
  */
@@ -27008,6 +27798,9 @@ renderOption?: (
27008
27798
  ) => React.ReactNode
27009
27799
  /**
27010
27800
  * The CSS class name for the option.
27801
+ *
27802
+ * Compared by identity, so an inline function or an unmemoized value rebuilds every
27803
+ * cached option and section on each render. A plain string is compared by value.
27011
27804
  */
27012
27805
  optionClassName?: | string
27013
27806
  | ((
@@ -27016,6 +27809,9 @@ optionClassName?: | string
27016
27809
  ) => string | undefined)
27017
27810
  /**
27018
27811
  * The style of the option.
27812
+ *
27813
+ * Compared by identity, so an inline function or object literal rebuilds every cached
27814
+ * option and section on each render \u2014 memoize it to avoid that.
27019
27815
  */
27020
27816
  optionStyle?: | React.CSSProperties
27021
27817
  | ((
@@ -27025,15 +27821,25 @@ optionStyle?: | React.CSSProperties
27025
27821
  /**
27026
27822
  * The custom render function for the listbox sections.
27027
27823
  *
27824
+ * Reading component state here works, at the cost of rebuilding every cached option and
27825
+ * section whenever the function's identity changes \u2014 see {@link UNSAFE_ListBoxProps.items}
27826
+ * for when that cost applies.
27827
+ *
27028
27828
  * @param section ListSection
27029
27829
  */
27030
27830
  renderSectionHeader?: (section: ListSection) => React.ReactNode
27031
27831
  /**
27032
27832
  * The CSS class name for the section.
27833
+ *
27834
+ * Compared by identity, so an inline function or an unmemoized value rebuilds every
27835
+ * cached option and section on each render. A plain string is compared by value.
27033
27836
  */
27034
27837
  sectionClassName?: string | ((section: ListSection) => string | undefined)
27035
27838
  /**
27036
27839
  * The style of the section.
27840
+ *
27841
+ * Compared by identity, so an inline function or object literal rebuilds every cached
27842
+ * option and section on each render \u2014 memoize it to avoid that.
27037
27843
  */
27038
27844
  sectionStyle?: | React.CSSProperties
27039
27845
  | ((section: ListSection) => React.CSSProperties | undefined)
@@ -27069,6 +27875,11 @@ withSectionHeaderPadding?: boolean
27069
27875
  *
27070
27876
  * type ListItem = ListOption | ListSection;
27071
27877
  * \`\`\`
27878
+ *
27879
+ * Rendered options and sections are cached per item object. Building this array inline
27880
+ * (\`items={data.map(...)}\`) produces new item objects every render, so nothing is ever
27881
+ * reused and the customisation props cost nothing; memoize it and the cache applies,
27882
+ * which is when their identity starts to matter.
27072
27883
  */
27073
27884
  items?: ListItem[]
27074
27885
  /**
@@ -27155,7 +27966,7 @@ listBoxHandle?: React.RefObject<ListHandle>
27155
27966
  items={items}
27156
27967
  aria-label="List box"
27157
27968
  selectionMode="multiple"
27158
- 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";
27969
+ 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";
27159
27970
  import {
27160
27971
  EllipseDashedIcon,
27161
27972
  EllipseIcon,
@@ -27185,7 +27996,10 @@ import {
27185
27996
  import { useDragAndDrop } from "../hooks/useDragAndDrop";
27186
27997
  import { UNSAFE_ListBox as ListBox } from "../ListBox";
27187
27998
 
27188
- import type { ListItem } from "../../shared/types/List";
27999
+ import type {
28000
+ ListItem,
28001
+ ListOption as ListOptionType,
28002
+ } from "../../shared/types/List";
27189
28003
  import type { ListHandle } from "../ListBox.types";
27190
28004
  import type { DroppableCollectionReorderEvent } from "@react-types/shared";
27191
28005
 
@@ -27762,16 +28576,85 @@ export const VirtualListBoxWithSectionsExample: React.FC<
27762
28576
  );
27763
28577
  };
27764
28578
 
27765
- export const VirtualizedScrollIntoViewExample: React.FC = () => {
27766
- const listBoxHandle = React.useRef<ListHandle>(null);
27767
- const items = Array.from({ length: 10_000 }).map((_, i) => ({
27768
- id: \`item-\${i}\`,
27769
- label: \`Item \${i}\`,
27770
- }));
27771
-
28579
+ export const VirtualListBoxWithSectionHeadersExample: React.FC<
28580
+ Omit<React.ComponentProps<typeof ListBox>, "items">
28581
+ > = (args) => {
27772
28582
  return (
27773
- <Box display="flex" flexDirection="column" gap="md" style={{ width: 300 }}>
27774
- <Box display="flex" flexDirection="column" gap="sm">
28583
+ <Virtualizer
28584
+ layout={ListLayout}
28585
+ layoutOptions={{
28586
+ rowHeight: 36,
28587
+ headingHeight: 40,
28588
+ }}
28589
+ >
28590
+ <ListBox
28591
+ {...args}
28592
+ items={itemsWithSectionTitles}
28593
+ showSectionHeader={true}
28594
+ style={{
28595
+ height: 400,
28596
+ width: 300,
28597
+ overflow: "auto",
28598
+ }}
28599
+ aria-label="Virtualized list box with section headers"
28600
+ />
28601
+ </Virtualizer>
28602
+ );
28603
+ };
28604
+
28605
+ const leadingSection = {
28606
+ id: "leading",
28607
+ title: "Leading",
28608
+ children: [{ id: "leading-item", label: "Leading item" }],
28609
+ } as ListItem;
28610
+
28611
+ export const VirtualListBoxWithPrependableSectionExample: React.FC = () => {
28612
+ const [showLeadingSection, setShowLeadingSection] = React.useState(false);
28613
+
28614
+ return (
28615
+ <>
28616
+ <ActionButton
28617
+ onPress={() => {
28618
+ setShowLeadingSection(true);
28619
+ }}
28620
+ label="Prepend section"
28621
+ />
28622
+ <Virtualizer
28623
+ layout={ListLayout}
28624
+ layoutOptions={{
28625
+ rowHeight: 36,
28626
+ headingHeight: 40,
28627
+ }}
28628
+ >
28629
+ <ListBox
28630
+ items={
28631
+ showLeadingSection
28632
+ ? [leadingSection, ...itemsWithSectionTitles]
28633
+ : itemsWithSectionTitles
28634
+ }
28635
+ showSectionHeader={true}
28636
+ style={{
28637
+ height: 400,
28638
+ width: 300,
28639
+ overflow: "auto",
28640
+ }}
28641
+ aria-label="Virtualized list box with a prependable section"
28642
+ />
28643
+ </Virtualizer>
28644
+ </>
28645
+ );
28646
+ };
28647
+
28648
+ export const VirtualizedScrollIntoViewExample: React.FC = () => {
28649
+ const listBoxHandle = React.useRef<ListHandle>(null);
28650
+ const items = Array.from({ length: 10_000 }).map((_, i) => ({
28651
+ id: \`item-\${i}\`,
28652
+ label: \`Item \${i}\`,
28653
+ }));
28654
+
28655
+ return (
28656
+ <Box display="flex" flexDirection="column" gap="md" style={{ width: 300 }}>
28657
+ <Box display="flex" flexDirection="column" gap="sm">
27775
28658
  <Text type="body" size="sm">
27776
28659
  Scroll to items in a virtualized list (10,000 items). Items that
27777
28660
  aren&apos;t currently rendered will be scrolled into view.
@@ -27819,7 +28702,110 @@ export const VirtualizedScrollIntoViewExample: React.FC = () => {
27819
28702
  </Virtualizer>
27820
28703
  </Box>
27821
28704
  );
27822
- };`},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\`.
28705
+ };
28706
+
28707
+ // Each example varies exactly one prop off \`count\` and keeps the rest at stable
28708
+ // module-level identity; varying several at once would let one invalidate the cache for
28709
+ // the others, hiding a missing \`dependencies\` entry.
28710
+ const stableRenderOption = (item: ListOptionType) => <span>{item.label}</span>;
28711
+ const stableOptionClassName = () => "option-stable";
28712
+ const stableOptionStyle = () => ({ marginBlockStart: "0px" });
28713
+ const stableSectionClassName = () => "section-stable";
28714
+ const stableSectionStyle = () => ({ marginBlockStart: "0px" });
28715
+
28716
+ // Offset off the \`0px\` browser default so the pre-click assertion also proves the prop
28717
+ // reached the DOM, not just that it refreshed.
28718
+ const varyingMargin = (count: number) => ({
28719
+ marginBlockStart: \`\${count + 2}px\`,
28720
+ });
28721
+
28722
+ const CountingExample: React.FC<{
28723
+ children: (count: number) => React.ReactNode;
28724
+ }> = ({ children }) => {
28725
+ const [count, setCount] = React.useState(0);
28726
+
28727
+ return (
28728
+ <>
28729
+ <ActionButton
28730
+ label="Increment"
28731
+ onPress={() => {
28732
+ setCount(count + 1);
28733
+ }}
28734
+ />
28735
+ {children(count)}
28736
+ </>
28737
+ );
28738
+ };
28739
+
28740
+ export const CountingOptionListBoxExample: React.FC<{
28741
+ vary: "renderOption" | "optionClassName" | "optionStyle";
28742
+ // Options inside a section render through a second, nested collection that inherits the
28743
+ // root \`dependencies\` only via React Aria's collection context.
28744
+ sectioned?: boolean;
28745
+ }> = ({ vary, sectioned = false }) => (
28746
+ <CountingExample>
28747
+ {(count) => (
28748
+ <ListBox
28749
+ items={sectioned ? itemsWithSectionTitles : items}
28750
+ aria-label="Counting option list box"
28751
+ renderOption={
28752
+ vary === "renderOption"
28753
+ ? (item) => <span>{\`\${item.label} count-\${count}\`}</span>
28754
+ : stableRenderOption
28755
+ }
28756
+ optionClassName={
28757
+ vary === "optionClassName"
28758
+ ? () => \`option-count-\${count}\`
28759
+ : stableOptionClassName
28760
+ }
28761
+ optionStyle={
28762
+ vary === "optionStyle"
28763
+ ? () => varyingMargin(count)
28764
+ : stableOptionStyle
28765
+ }
28766
+ />
28767
+ )}
28768
+ </CountingExample>
28769
+ );
28770
+
28771
+ export const CountingSectionListBoxExample: React.FC<{
28772
+ vary:
28773
+ | "renderSectionHeader"
28774
+ | "sectionClassName"
28775
+ | "sectionStyle"
28776
+ | "showSectionHeader"
28777
+ | "withSectionHeaderPadding";
28778
+ }> = ({ vary }) => (
28779
+ <CountingExample>
28780
+ {(count) => (
28781
+ <ListBox
28782
+ items={itemsWithSectionTitles}
28783
+ aria-label="Counting section list box"
28784
+ // \`withSectionHeaderPadding\` only reaches the DOM through the default header, so
28785
+ // that variant must not supply a custom one.
28786
+ showSectionHeader={vary === "showSectionHeader" ? count > 0 : true}
28787
+ withSectionHeaderPadding={
28788
+ vary === "withSectionHeaderPadding" ? count > 0 : false
28789
+ }
28790
+ renderSectionHeader={
28791
+ vary === "renderSectionHeader"
28792
+ ? (section) => \`\${section.title} count-\${count}\`
28793
+ : undefined
28794
+ }
28795
+ sectionClassName={
28796
+ vary === "sectionClassName"
28797
+ ? () => \`section-count-\${count}\`
28798
+ : stableSectionClassName
28799
+ }
28800
+ sectionStyle={
28801
+ vary === "sectionStyle"
28802
+ ? () => varyingMargin(count)
28803
+ : stableSectionStyle
28804
+ }
28805
+ />
28806
+ )}
28807
+ </CountingExample>
28808
+ );`},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\`.
27823
28809
 
27824
28810
  * Efficient rendering for very large collections
27825
28811
  * Preset layout options for list boxes, tree views, and image galleries
@@ -30797,25 +31783,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
30797
31783
  );
30798
31784
  };
30799
31785
 
30800
- export const PopoverContainedFocusExample = () => {
30801
- return (
30802
- <Popover type="dialog">
30803
- <PopoverTrigger>
30804
- <ActionButton label="Open" />
30805
- </PopoverTrigger>
30806
- <PopoverContent shouldContainFocus={true}>
30807
- <Dialog
30808
- size="content"
30809
- className={sprinkles({
30810
- padding: "md",
30811
- display: "flex",
30812
- gap: "xl",
30813
- flexDirection: "column",
30814
- })}
30815
- style={{
30816
- width: 200,
30817
- }}
30818
- >
31786
+ export const PopoverContainedFocusExample = ({
31787
+ shouldContainFocus = true,
31788
+ }: {
31789
+ shouldContainFocus?: boolean;
31790
+ }) => {
31791
+ const content = (
31792
+ <>
30819
31793
  <Text type="label">The focus is contained within the popover.</Text>
30820
31794
 
30821
31795
  <TextInput
@@ -30827,10 +31801,55 @@ export const PopoverContainedFocusExample = () => {
30827
31801
  label="Button"
30828
31802
  style={{ width: "100%", justifyContent: "center" }}
30829
31803
  />
31804
+ </>
31805
+ );
31806
+ const contentClassName = sprinkles({
31807
+ padding: "md",
31808
+ display: "flex",
31809
+ gap: "xl",
31810
+ flexDirection: "column",
31811
+ });
31812
+
31813
+ const popover = (
31814
+ <Popover type="dialog">
31815
+ <PopoverTrigger>
31816
+ <ActionButton label="Open" />
31817
+ </PopoverTrigger>
31818
+ <PopoverContent
31819
+ shouldContainFocus={shouldContainFocus}
31820
+ isNonModal={!shouldContainFocus}
31821
+ >
31822
+ {shouldContainFocus ? (
31823
+ <Dialog
31824
+ size="content"
31825
+ className={contentClassName}
31826
+ style={{ width: 200 }}
31827
+ >
31828
+ {content}
30830
31829
  </Dialog>
31830
+ ) : (
31831
+ // No Dialog: useDialog opts the overlay into focus containment,
31832
+ // which disables the Tab-out restore path.
31833
+ <Box className={contentClassName} style={{ width: 200 }}>
31834
+ {content}
31835
+ </Box>
31836
+ )}
30831
31837
  </PopoverContent>
30832
31838
  </Popover>
30833
31839
  );
31840
+
31841
+ // The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
31842
+ // here rebaselines its visual snapshot.
31843
+ if (shouldContainFocus) {
31844
+ return popover;
31845
+ }
31846
+
31847
+ return (
31848
+ <Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
31849
+ {popover}
31850
+ <ActionButton label="After" />
31851
+ </Box>
31852
+ );
30834
31853
  };
30835
31854
 
30836
31855
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -31957,25 +32976,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
31957
32976
  );
31958
32977
  };
31959
32978
 
31960
- export const PopoverContainedFocusExample = () => {
31961
- return (
31962
- <Popover type="dialog">
31963
- <PopoverTrigger>
31964
- <ActionButton label="Open" />
31965
- </PopoverTrigger>
31966
- <PopoverContent shouldContainFocus={true}>
31967
- <Dialog
31968
- size="content"
31969
- className={sprinkles({
31970
- padding: "md",
31971
- display: "flex",
31972
- gap: "xl",
31973
- flexDirection: "column",
31974
- })}
31975
- style={{
31976
- width: 200,
31977
- }}
31978
- >
32979
+ export const PopoverContainedFocusExample = ({
32980
+ shouldContainFocus = true,
32981
+ }: {
32982
+ shouldContainFocus?: boolean;
32983
+ }) => {
32984
+ const content = (
32985
+ <>
31979
32986
  <Text type="label">The focus is contained within the popover.</Text>
31980
32987
 
31981
32988
  <TextInput
@@ -31987,10 +32994,55 @@ export const PopoverContainedFocusExample = () => {
31987
32994
  label="Button"
31988
32995
  style={{ width: "100%", justifyContent: "center" }}
31989
32996
  />
32997
+ </>
32998
+ );
32999
+ const contentClassName = sprinkles({
33000
+ padding: "md",
33001
+ display: "flex",
33002
+ gap: "xl",
33003
+ flexDirection: "column",
33004
+ });
33005
+
33006
+ const popover = (
33007
+ <Popover type="dialog">
33008
+ <PopoverTrigger>
33009
+ <ActionButton label="Open" />
33010
+ </PopoverTrigger>
33011
+ <PopoverContent
33012
+ shouldContainFocus={shouldContainFocus}
33013
+ isNonModal={!shouldContainFocus}
33014
+ >
33015
+ {shouldContainFocus ? (
33016
+ <Dialog
33017
+ size="content"
33018
+ className={contentClassName}
33019
+ style={{ width: 200 }}
33020
+ >
33021
+ {content}
31990
33022
  </Dialog>
33023
+ ) : (
33024
+ // No Dialog: useDialog opts the overlay into focus containment,
33025
+ // which disables the Tab-out restore path.
33026
+ <Box className={contentClassName} style={{ width: 200 }}>
33027
+ {content}
33028
+ </Box>
33029
+ )}
31991
33030
  </PopoverContent>
31992
33031
  </Popover>
31993
33032
  );
33033
+
33034
+ // The contained variant is the \`ContainedFocus\` story: a wrapper or sibling
33035
+ // here rebaselines its visual snapshot.
33036
+ if (shouldContainFocus) {
33037
+ return popover;
33038
+ }
33039
+
33040
+ return (
33041
+ <Box display="flex" flexDirection="column" gap="lg" alignItems="flex-start">
33042
+ {popover}
33043
+ <ActionButton label="After" />
33044
+ </Box>
33045
+ );
31994
33046
  };
31995
33047
 
31996
33048
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -32756,7 +33808,15 @@ iconTooltip?: ActionIconButtonProps["tooltip"]
32756
33808
  placeholder="Select"
32757
33809
  items={items}
32758
33810
  isReadOnly
32759
- value="square" />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
33811
+ value="square" />;`},{id:"core-forms-select-iconselect--without-icon",name:"Without Icon",snippet:`const WithoutIcon = () => <IconSelect
33812
+ aria-label="Choose Stroke Style"
33813
+ placeholder="Select"
33814
+ items={items}
33815
+ icon={undefined} />;`},{id:"core-forms-select-iconselect--with-default-open",name:"With Default Open",snippet:`const WithDefaultOpen = () => <IconSelect
33816
+ aria-label="Choose Stroke Style"
33817
+ placeholder="Select"
33818
+ items={items}
33819
+ defaultOpen />;`}],implementation:`import { EllipseIcon } from "@baseline-ui/icons/24";
32760
33820
  import React from "react";
32761
33821
  import { useFilter } from "react-aria";
32762
33822
  import { Autocomplete, Virtualizer } from "react-aria-components";
@@ -32765,11 +33825,16 @@ import { useListData } from "react-stately";
32765
33825
  import { ActionButton } from "../../ActionButton";
32766
33826
  import { items } from "../../ListBox/__tests__/testComponents";
32767
33827
  import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
32768
- import { VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS } from "../../Virtualizer";
33828
+ import { itemsWithSectionTitles } from "../../UNSAFE_ListBox/__tests__/testComponents";
33829
+ import {
33830
+ ListLayout,
33831
+ VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS,
33832
+ } from "../../Virtualizer";
32769
33833
  import { IconSelect } from "../IconSelect";
32770
33834
  import { Select } from "../Select";
32771
33835
 
32772
33836
  import type { IconSelectProps } from "../Select.types";
33837
+ import type { Key } from "@react-types/shared";
32773
33838
 
32774
33839
  export const SelectExample: React.FC<
32775
33840
  Omit<React.ComponentProps<typeof Select>, "items">
@@ -32777,13 +33842,28 @@ export const SelectExample: React.FC<
32777
33842
  return (
32778
33843
  <Select
32779
33844
  placeholder="Choose an item"
32780
- {...args}
32781
33845
  optionClassName={(item) => item.label}
33846
+ {...args}
32782
33847
  items={items}
32783
33848
  />
32784
33849
  );
32785
33850
  };
32786
33851
 
33852
+ // "Apples" is a substring of "Pineapples" \u2014 exercises exact string matching
33853
+ // in the tester's option() locator.
33854
+ const substringItems = [
33855
+ { id: "apples", label: "Apples" },
33856
+ { id: "pineapples", label: "Pineapples" },
33857
+ ];
33858
+
33859
+ export const SelectSubstringLabelsExample: React.FC<
33860
+ Omit<React.ComponentProps<typeof Select>, "items">
33861
+ > = (args) => {
33862
+ return (
33863
+ <Select placeholder="Choose an item" {...args} items={substringItems} />
33864
+ );
33865
+ };
33866
+
32787
33867
  export const SelectGetTargetRectExample: React.FC = () => {
32788
33868
  const [called, setCalled] = React.useState(false);
32789
33869
 
@@ -32840,19 +33920,136 @@ export const SelectCustomTriggerExample: React.FC<
32840
33920
  };
32841
33921
 
32842
33922
  export const IconSelectExample: React.FC<
32843
- Omit<IconSelectProps, "items" | "icon" | "aria-label">
33923
+ Omit<IconSelectProps, "items" | "aria-label">
32844
33924
  > = (args) => {
32845
33925
  return (
32846
33926
  <IconSelect
32847
33927
  placeholder="Choose an item"
33928
+ icon={EllipseIcon}
32848
33929
  {...args}
32849
33930
  items={items}
32850
- icon={EllipseIcon}
32851
33931
  aria-label="Aria Label"
32852
33932
  />
32853
33933
  );
32854
33934
  };
32855
33935
 
33936
+ export const VirtualSelectWithSectionHeadersExample: React.FC<
33937
+ Omit<React.ComponentProps<typeof Select>, "items">
33938
+ > = (args) => {
33939
+ return (
33940
+ <Virtualizer
33941
+ layout={ListLayout}
33942
+ layoutOptions={{
33943
+ rowHeight: 36,
33944
+ headingHeight: 40,
33945
+ }}
33946
+ >
33947
+ <Select
33948
+ aria-label="Choose an item"
33949
+ placeholder="Choose an item"
33950
+ showSectionHeader={true}
33951
+ {...args}
33952
+ items={itemsWithSectionTitles}
33953
+ defaultOpen={true}
33954
+ maxHeight={400}
33955
+ />
33956
+ </Virtualizer>
33957
+ );
33958
+ };
33959
+
33960
+ export const SelectWithSectionHeadersExample: React.FC<
33961
+ Omit<React.ComponentProps<typeof Select>, "items">
33962
+ > = (args) => {
33963
+ return (
33964
+ <Select
33965
+ aria-label="Choose an item"
33966
+ placeholder="Choose an item"
33967
+ showSectionHeader={true}
33968
+ {...args}
33969
+ items={itemsWithSectionTitles}
33970
+ defaultOpen={true}
33971
+ />
33972
+ );
33973
+ };
33974
+
33975
+ /**
33976
+ * Owns \`isOpen\` so the controlled-open contract can be driven from outside the
33977
+ * Select, with a sibling readout of the current state.
33978
+ */
33979
+ export const ControlledOpenSelectExample: React.FC<
33980
+ Omit<React.ComponentProps<typeof Select>, "items">
33981
+ > = (args) => {
33982
+ const [isOpen, setIsOpen] = React.useState(false);
33983
+
33984
+ return (
33985
+ <>
33986
+ <Select
33987
+ aria-label="Choose an item"
33988
+ placeholder="Choose an item"
33989
+ {...args}
33990
+ items={items}
33991
+ isOpen={isOpen}
33992
+ onOpenChange={setIsOpen}
33993
+ />
33994
+ <button
33995
+ data-testid="open-externally"
33996
+ onClick={() => {
33997
+ setIsOpen(true);
33998
+ }}
33999
+ >
34000
+ Open
34001
+ </button>
34002
+ <span data-testid="open-state">{String(isOpen)}</span>
34003
+ </>
34004
+ );
34005
+ };
34006
+
34007
+ /**
34008
+ * Custom trigger that renders the multi-select half of the \`renderTrigger\`
34009
+ * payload \u2014 \`selectionMode\`, \`maxCount\` and \`onRemove\` \u2014 which the default
34010
+ * \`SelectButton\` otherwise keeps to itself.
34011
+ */
34012
+ export const MultiSelectCustomTriggerExample: React.FC<
34013
+ Omit<React.ComponentProps<typeof Select>, "items">
34014
+ > = (args) => {
34015
+ return (
34016
+ <Select
34017
+ aria-label="Choose an item"
34018
+ selectionMode="multiple"
34019
+ {...args}
34020
+ items={items}
34021
+ renderTrigger={({
34022
+ buttonProps,
34023
+ ref,
34024
+ selectedValue,
34025
+ selectionMode,
34026
+ onRemove,
34027
+ maxCount,
34028
+ }) => (
34029
+ <div>
34030
+ <ActionButton
34031
+ {...buttonProps}
34032
+ ref={ref}
34033
+ label={\`\${selectionMode}|\${maxCount}|\${selectedValue?.length ?? 0}\`}
34034
+ variant="popover"
34035
+ />
34036
+ <button
34037
+ data-testid="remove-first"
34038
+ onClick={() => {
34039
+ const first = selectedValue?.[0];
34040
+ if (first) {
34041
+ onRemove?.(new Set([first.id]));
34042
+ }
34043
+ }}
34044
+ >
34045
+ Remove first
34046
+ </button>
34047
+ </div>
34048
+ )}
34049
+ />
34050
+ );
34051
+ };
34052
+
32856
34053
  export const SelectWithVirtualizeAutocompleteExample: React.FC<
32857
34054
  Omit<React.ComponentProps<typeof Select>, "items">
32858
34055
  > = (args) => {
@@ -32876,6 +34073,27 @@ export const SelectWithVirtualizeAutocompleteExample: React.FC<
32876
34073
  </Autocomplete>
32877
34074
  </Virtualizer>
32878
34075
  );
34076
+ };
34077
+
34078
+ /**
34079
+ * \`optionClassName\` reads component state, and \`items\` is module-level so React Aria's
34080
+ * per-item element cache applies. Pinned open so the state change comes from selecting an
34081
+ * option rather than from an outside press the popover would swallow.
34082
+ */
34083
+ export const CountingSelectExample: React.FC = () => {
34084
+ const [selected, setSelected] = React.useState<Key | null>(null);
34085
+
34086
+ return (
34087
+ <Select
34088
+ aria-label="Counting select"
34089
+ items={items}
34090
+ isOpen={true}
34091
+ optionClassName={(item) =>
34092
+ item.id === selected ? "option-picked" : "option-plain"
34093
+ }
34094
+ onSelectionChange={setSelected}
34095
+ />
34096
+ );
32879
34097
  };`},similarTo:[],figmaUrl:null},IconSlider:{id:"core-forms-slider-iconslider",importStatement:'import { IconSlider } from "@baseline-ui/core";',description:"",documentation:null,props:`interface IconSliderProps {
32880
34098
  /**
32881
34099
  * @deprecated Do not use in new components. This is a legacy block
@@ -32986,6 +34204,21 @@ import { ActionButton, I18nProvider, ThemeProvider } from "@baseline-ui/core";
32986
34204
 
32987
34205
  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.
32988
34206
 
34207
+ ### Translations
34208
+
34209
+ 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\`:
34210
+
34211
+ \`\`\`jsx
34212
+ import { I18nProvider } from "@baseline-ui/core";
34213
+ import de from "@baseline-ui/core/intl/de.json";
34214
+
34215
+ <I18nProvider locale="de-DE" messages={{ de }}>
34216
+ <YourApp />
34217
+ </I18nProvider>;
34218
+ \`\`\`
34219
+
34220
+ 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.
34221
+
32989
34222
  ### Styling
32990
34223
 
32991
34224
  Import the following files at the top of your stylesheet to use the Baseline UI styles:
@@ -33254,22 +34487,24 @@ function App() {
33254
34487
  }
33255
34488
  \`\`\``,internationalization:`# Internationalization
33256
34489
 
33257
- 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. This includes translation for the built-in strings, localization of dates and times, number formatting, and currency formatting.
34490
+ 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.
34491
+
34492
+ 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).
33258
34493
 
33259
34494
  ## Localization
33260
34495
 
33261
- 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), which provides translations of built-in strings for many languages. You can also provide your own translations for custom strings.
34496
+ 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.
33262
34497
 
33263
- 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 \`Provider\` component.
34498
+ 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.
33264
34499
 
33265
34500
  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.
33266
34501
 
33267
34502
  ### Example
33268
34503
 
33269
- 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 from \`../../utils\`:
34504
+ 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:
33270
34505
 
33271
34506
  \`\`\`jsx
33272
- import { useLocale } from "../../utils";
34507
+ import { useLocale } from "@baseline-ui/core";
33273
34508
 
33274
34509
  function YourApp() {
33275
34510
  let { locale, direction } = useLocale();
@@ -33285,27 +34520,128 @@ function YourApp() {
33285
34520
  You can also override the language used for localization with the [\`I18nProvider\`](/?path=/docs/utilities-i18nprovider--docs) component:
33286
34521
 
33287
34522
  \`\`\`jsx
33288
- import { I18nProvider } from "../../utils";
34523
+ import { I18nProvider } from "@baseline-ui/core";
33289
34524
 
33290
34525
  <I18nProvider locale="fr-FR">
33291
34526
  <YourApp />
33292
34527
  </I18nProvider>;
33293
34528
  \`\`\`
33294
34529
 
34530
+ 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.
34531
+
34532
+ ### Loading a translation catalog
34533
+
34534
+ Import the catalogs for the languages you ship and hand them to \`I18nProvider\`:
34535
+
34536
+ \`\`\`jsx
34537
+ import { I18nProvider } from "@baseline-ui/core";
34538
+ import de from "@baseline-ui/core/intl/de.json";
34539
+ import fr from "@baseline-ui/core/intl/fr.json";
34540
+
34541
+ // Hoisted out of render: a fresh object each render rebuilds the message
34542
+ // formatter, so every string reformats on every pass.
34543
+ const messages = { de, fr };
34544
+
34545
+ <I18nProvider locale="de-DE" messages={messages}>
34546
+ <YourApp />
34547
+ </I18nProvider>;
34548
+ \`\`\`
34549
+
34550
+ Each file is a flat map of \`bui.*\` ID to translated string, so your bundler includes only
34551
+ the locales you actually import. The key you file a catalog under is matched against the
34552
+ active locale exactly first, then by language, then by any other region of that language \u2014
34553
+ so \`{ de }\` answers for \`de\`, \`de-DE\` and \`de-AT\` alike, and \`{ de, "de-AT" }\` under
34554
+ \`de-AT\` uses the Austrian entry.
34555
+
34556
+ TypeScript needs \`resolveJsonModule\` enabled to import the files, and Node ESM needs a
34557
+ \`with { type: "json" }\` import attribute.
34558
+
34559
+ To avoid shipping every language up front, put each catalog behind a dynamic \`import()\`
34560
+ and load one when the user picks a language. Write the specifiers out in full \u2014 a
34561
+ template literal over a bare package subpath is not statically analyzable, so bundlers
34562
+ cannot split on it:
34563
+
34564
+ \`\`\`jsx
34565
+ const catalogs = {
34566
+ de: () => import("@baseline-ui/core/intl/de.json"),
34567
+ fr: () => import("@baseline-ui/core/intl/fr.json"),
34568
+ };
34569
+
34570
+ const [messages, setMessages] = useState({});
34571
+
34572
+ async function selectLocale(locale) {
34573
+ const catalog = await catalogs[locale]();
34574
+
34575
+ setMessages({ [locale]: catalog.default });
34576
+ }
34577
+ \`\`\`
34578
+
34579
+ An ID your catalog omits falls back to that component's English \`defaultMessage\`, so a
34580
+ partial catalog is a valid one \u2014 you are never forced to translate a string before you
34581
+ ship it. Outside production, each such ID is reported once in the console.
34582
+
34583
+ If you supply catalogs but none of them matches the active locale \u2014 \`{ de }\` while the
34584
+ locale is \`ja-JP\`, say \u2014 every string falls back to English and that is reported once for
34585
+ the locale rather than once per ID. It means the catalog keys and the locale disagree,
34586
+ which no per-ID warning can tell you. Supplying no catalogs at all is the supported
34587
+ English-only mode and stays silent.
34588
+
33295
34589
  ### Supported locales
33296
34590
 
33297
- The following locales are supported:
34591
+ A catalog ships for each of these locales, with an entry for every message ID:
34592
+
34593
+ ### Message IDs
34594
+
34595
+ Every string Baseline UI can render is keyed by a \`bui.<component>.<key>\` ID. The same
34596
+ list is exported as a value, so you can derive translation coverage in a test instead of
34597
+ committing a copy that goes stale on the next upgrade:
34598
+
34599
+ \`\`\`ts
34600
+ import { messageIds } from "@baseline-ui/core";
34601
+
34602
+ const uncovered = messageIds.filter((id) => !(id in ourCatalog.en));
34603
+ \`\`\`
34604
+
34605
+ For the English string behind each ID \u2014 and the translator context that goes with it \u2014
34606
+ read \`@baseline-ui/core/messages.json\`, the extracted source messages keyed by ID.
33298
34607
 
33299
- ### Translations list
34608
+ ### Translating them yourself
33300
34609
 
33301
- This list includes the IDs for all of the translations in the \`messages\` object for the \`en\` locale. This can be used to find the ID of a translation to use in the \`formatMessage\` function:
34610
+ If you localize the \`bui.*\` strings in your own pipeline rather than shipping our catalogs,
34611
+ hand your translators \`@baseline-ui/core/messages.json\` \u2014 the extracted source messages,
34612
+ keyed by ID and shaped the way a TMS expects:
34613
+
34614
+ \`\`\`json
34615
+ {
34616
+ "bui.select.more": {
34617
+ "defaultMessage": "+{count} more",
34618
+ "description": "Tag shown on the select trigger standing in for the selected options that did not fit \u2014 renders as '+3 more'. ..."
34619
+ }
34620
+ }
34621
+ \`\`\`
34622
+
34623
+ The \`description\` is the context a translator needs and cannot infer from the string
34624
+ alone \u2014 part of speech, sense, and where it renders. Translating \`bui.editor.mention\`
34625
+ without it yields the verb "to mention" where the noun label belongs, and
34626
+ \`bui.editor.shortcut\` becomes a desktop shortcut file rather than a key combination.
33302
34627
 
33303
34628
  ## Overriding Component Translations
33304
34629
 
33305
34630
  You can override any built-in component string by passing its \`bui.*\`-namespaced key to
33306
34631
  \`I18nProvider.messages\`. Only the keys you provide are overridden \u2014 all other strings
33307
- fall back to each component's built-in defaults. You can also supply strings for locales
33308
- that a component does not ship with natively.
34632
+ fall back to each component's English \`defaultMessage\`. Spread a shipped catalog and
34633
+ override on top of it to change a single string:
34634
+
34635
+ \`\`\`jsx
34636
+ import de from "@baseline-ui/core/intl/de.json";
34637
+
34638
+ <I18nProvider
34639
+ locale="de-DE"
34640
+ messages={{ de: { ...de, "bui.imageDropZone.selectImage": "Bild w\xE4hlen" } }}
34641
+ >
34642
+ <YourApp />
34643
+ </I18nProvider>;
34644
+ \`\`\`
33309
34645
 
33310
34646
  Each component's documentation lists its overridable keys and shows an example. See:
33311
34647
 
@@ -33313,7 +34649,43 @@ Each component's documentation lists its overridable keys and shows an example.
33313
34649
  * [ColorInput](?path=/docs/components-colorinput--docs#translations)
33314
34650
  * [ImageDropZone](?path=/docs/components-imagedropzone--docs#translations)
33315
34651
  * [FreehandCanvas](?path=/docs/components-freehandcanvas--docs#translations)
33316
- * [InlineAlert](?path=/docs/components-inlinealert--docs#translations)`,styling:`# Styling with Sprinkles
34652
+ * [InlineAlert](?path=/docs/components-inlinealert--docs#translations)
34653
+
34654
+ ### Migrating from bare IDs
34655
+
34656
+ Older catalogs key off the bare tail of an ID (\`close\` rather than \`bui.drawer.close\`).
34657
+ That still resolves, with a deprecation warning, but it will be removed in a future
34658
+ major \u2014 and it is ambiguous: several bare IDs are claimed by more than one component,
34659
+ so a single entry silently translates all of them. That happens to work in English, but
34660
+ it forecloses any language where the word inflects differently per context.
34661
+
34662
+ \`legacyMessageIdMap\` maps each bare ID to every namespaced ID that claims it, so a catalog
34663
+ can be re-keyed mechanically. Keep each original key alongside the IDs it maps to \u2014 a bare
34664
+ key may also be one your own app looks up, and Baseline UI resolves the namespaced entry
34665
+ either way:
34666
+
34667
+ \`\`\`ts
34668
+ import { legacyMessageIdMap } from "@baseline-ui/core";
34669
+
34670
+ const migrated = Object.fromEntries(
34671
+ Object.entries(oldCatalog).flatMap(([key, value]) => [
34672
+ [key, value],
34673
+ ...(legacyMessageIdMap[key] ?? []).map((id) => [id, value]),
34674
+ ]),
34675
+ );
34676
+ \`\`\`
34677
+
34678
+ Review the ambiguous ones by hand \u2014 each deserves its own translation:
34679
+
34680
+ | Bare ID | Also translates |
34681
+ | ----------- | ---------------------------------------------------------------------------------------- |
34682
+ | \`close\` | \`bui.alertDialog.close\`, \`bui.drawer.close\`, \`bui.editor.close\`, \`bui.inlineAlert.close\` |
34683
+ | \`more\` | \`bui.buttonSelect.more\`, \`bui.select.more\`, \`bui.toolbar.more\` |
34684
+ | \`cancel\` | \`bui.colorInput.cancel\`, \`bui.editor.cancel\`, \`bui.imageGallery.cancel\` |
34685
+ | \`redo\` | \`bui.editor.redo\`, \`bui.freehandCanvas.redo\` |
34686
+ | \`selectAll\` | \`bui.editor.selectAll\`, \`bui.select.selectAll\` |
34687
+ | \`undo\` | \`bui.editor.undo\`, \`bui.freehandCanvas.undo\` |
34688
+ | \`loading\` | \`bui.fileList.loading\`, \`bui.imageGallery.loading\`, \`bui.table.loading\` |`,styling:`# Styling with Sprinkles
33317
34689
 
33318
34690
  Baseline UI provides a powerful, type-safe styling system built on top of [vanilla-extract sprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/).
33319
34691
 
@@ -34223,7 +35595,7 @@ padding={[null, "lg", "xl"]}
34223
35595
 
34224
35596
  * [vanilla-extract sprinkles documentation](https://vanilla-extract.style/documentation/packages/sprinkles/) - Learn about the underlying sprinkles framework
34225
35597
  * [Box component documentation](/docs/core-utilities-box--docs) - Detailed information about the Box component
34226
- * [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.1.0"};var u=`
35598
+ * [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.0.0"};var u=`
34227
35599
  # Baseline UI MCP Server Guidelines
34228
35600
 
34229
35601
  This MCP server provides AI assistants with structured access to Baseline UI's comprehensive component documentation, icon library, theming resources, and design guidelines.