@baseline-ui/mcp 1.2.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.cjs 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
  ) {
@@ -12882,27 +13098,45 @@ console.log(greet('Markdown Maverick'));
12882
13098
  2. Selection options: The component can be configured to allow single, multiple, or no selection.
12883
13099
  3. Disabled items support: Certain menu items can be disabled, preventing user interaction.
12884
13100
  4. Sections support: Related items can be grouped into sections for better organization and navigation.
12885
- 5. Keyboard navigation: Users can navigate through the menu using their keyboard, including arrow keys, home/end, and page up/down.
12886
- 6. Auto-scroll: The menu automatically scrolls as the user navigates through it with their keyboard.
12887
- 7. Keyboard menu opening: The menu can be opened using the keyboard, and automatically focuses on the first or last item.
12888
- 8. Typeahead: Users can quickly navigate to a menu item by typing the first few letters of the item's label.
12889
- 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.
12890
13111
 
12891
13112
  \`\`\`jsx
12892
- import { ActionButton } from "../../utils";
12893
- import { Menu } from "@storybook/addon-docs/blocks";
13113
+ import { Menu } from "@baseline-ui/core";
12894
13114
 
12895
13115
  const items = [
12896
- {
12897
- label: "Cut",
12898
- id: "cut",
12899
- keyboardShortcut: "\u2318X",
12900
- },
13116
+ { id: "cut", label: "Cut", keyboardShortcut: "\u2318X" },
13117
+ { id: "copy", label: "Copy", keyboardShortcut: "\u2318C" },
13118
+ { id: "paste", label: "Paste", keyboardShortcut: "\u2318V" },
12901
13119
  ];
12902
13120
 
12903
- <Menu items={items}>
12904
- <ActionButton label="Label" />
12905
- </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
+ ];
12906
13140
  \`\`\`
12907
13141
 
12908
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\`.
@@ -12930,9 +13164,93 @@ const items = [
12930
13164
  ];
12931
13165
  \`\`\`
12932
13166
 
12933
- 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.
12934
13226
 
12935
- 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 {
12936
13254
  /**
12937
13255
  * Whether the overlay is open by default (controlled).
12938
13256
  */
@@ -12964,39 +13282,18 @@ contentClassName?: string
12964
13282
  */
12965
13283
  itemClassName?: string
12966
13284
  /**
12967
- * A list of items to render in the menu. Items have the following shape:
12968
- *
12969
- * \`\`\`ts
12970
- * export type MenuOption = {
12971
- * id: string;
12972
- * label: string;
12973
- * keyboardShortcut?: string;
12974
- * icon?: React.FC<IconProps>;
12975
- * };
12976
- *
12977
- * export type MenuSection = {
12978
- * id: string;
12979
- * title?: string;
12980
- * type: "section";
12981
- * children: MenuOption[];
12982
- * };
13285
+ * A list of items to render in the menu. See \`MenuItem\`.
12983
13286
  *
12984
- * export type MenuItem = MenuOption | MenuSection;
12985
- * \`\`\`
13287
+ * A \`MenuSection\` may opt into independent selection by listing its id in the
13288
+ * object form of \`selectionMode\` (e.g. \`selectionMode={{ view: "single" }}\`).
12986
13289
  */
12987
13290
  items: MenuItem[]
12988
13291
  /**
12989
13292
  * A function that renders the trigger element of the component. The default
12990
13293
  * implementation renders an \`ActionButton\` component.
12991
- *
12992
- * \`\`\`tsx
12993
- * <Menu renderTrigger={({ buttonProps, ref }) => <ActionButton {...buttonProps} label="Label" ref={ref} />
12994
- * \`\`\`
12995
13294
  */
12996
13295
  renderTrigger?: (options: {
12997
- buttonProps: ActionButtonProps & {
12998
- isOpen: boolean;
12999
- };
13296
+ buttonProps: ActionButtonProps & { isOpen: boolean };
13000
13297
  ref: React.RefObject<HTMLButtonElement>;
13001
13298
  }) => React.ReactNode
13002
13299
  /**
@@ -13004,6 +13301,33 @@ renderTrigger?: (options: {
13004
13301
  * function that accepts a boolean indicating whether the menu is open.
13005
13302
  */
13006
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
13007
13331
  placement?: any
13008
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 = () => {
13009
13333
  const [selectedKey, setSelectedKey] = React.useState(
@@ -13037,13 +13361,17 @@ placement?: any
13037
13361
  };`},{id:"core-collections-menu--with-selected-keys-controlled",name:"With Selected Keys Controlled",snippet:`const WithSelectedKeysControlled = () => <Menu
13038
13362
  triggerLabel="Menu Trigger"
13039
13363
  items={items}
13040
- selectedKeys={["light"]}
13364
+ selectedKeys={new Set(["light"])}
13041
13365
  selectionMode="single" />;`},{id:"core-collections-menu--long-menu",name:"Long Menu",snippet:`const LongMenu = () => <Menu
13042
13366
  triggerLabel="Menu Trigger"
13043
13367
  items={longListItems}
13044
13368
  selectionMode="single"
13045
13369
  defaultOpen
13046
- 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
13047
13375
  triggerLabel="Menu Trigger"
13048
13376
  renderTrigger={({ buttonProps, ref }) => (
13049
13377
  <ActionButton
@@ -13052,12 +13380,32 @@ placement?: any
13052
13380
  size="sm"
13053
13381
  ref={ref}
13054
13382
  />
13055
- )} />;`}],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";
13056
13401
 
13402
+ import { I18nProvider } from "../../I18nProvider/I18nProvider";
13057
13403
  import { Menu } from "../Menu";
13058
- import { items } from "./data";
13404
+ import { items, itemsWithSections, itemsWithSubmenu } from "./data";
13059
13405
 
13060
13406
  import type { MenuProps } from "../Menu.types";
13407
+ import type { Key } from "react-aria";
13408
+ import type { Selection } from "react-stately";
13061
13409
 
13062
13410
  export const MenuExample: React.FC<
13063
13411
  MenuProps & {
@@ -13087,63 +13435,218 @@ export const MenuActionValueExample: React.FC<{ label: string }> = ({
13087
13435
  <div data-testid="menu-action-value">{received}</div>
13088
13436
  </>
13089
13437
  );
13090
- };`},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
+ };
13091
13439
 
13092
- \`\`\`jsx
13093
- 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
+ };
13094
13460
 
13095
- const messages = {
13096
- en: {
13097
- hello: "Hello {name}!",
13098
- },
13099
- fr: {
13100
- hello: "Bonjour {name}!",
13101
- },
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
+ );
13102
13477
  };
13103
13478
 
13104
- const App = () => (
13105
- <I18nProvider locale="en" messages={messages}>
13106
- <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} />
13107
13484
  </I18nProvider>
13108
13485
  );
13109
- \`\`\``,props:`interface MessageFormatProps {
13110
- /**
13111
- * By default \`<MessageFormat>\` will render the formatted string into a
13112
- * \`<React.Fragment>\`. If you need to customize rendering, you can either wrap
13113
- * it with another React element (recommended), specify a different tagName
13114
- * (e.g., 'div')
13115
- */
13116
- elementType?: React.ElementType | "div" | "span"
13117
- /**
13118
- * The id of the message to format.
13119
- */
13120
- id: string
13121
- /**
13122
- * The default message to use if the message id is not found.
13123
- */
13124
- defaultMessage?: string
13125
- }`,stories:{usage:[{id:"core-utilities-messageformat--basic",name:"Basic",snippet:'const Basic = () => <MessageFormat id="addSignature" elementType={Text} />;'}],implementation:""},similarTo:[],figmaUrl:null},Modal:{id:"core-overlays-modal",breadcrumb:"Core/Overlays/Modal",importStatement:'import { DialogExample, Modal } from "@baseline-ui/core";',description:"`Modal` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.",documentation:`\`Modal\` renders content in an overlay above the page, trapping focus and blocking interaction with the background. Use it when a task or message must be addressed before the user can return to the underlying view.
13126
13486
 
13127
- * The content outside the modal is hidden from screen readers.
13128
- * The modal can optionally be closed by clicking outside the modal or by pressing the <kbd>Esc</kbd> key.
13129
- * 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.
13130
- * Scrolling of the page is disabled when the modal is open.
13131
-
13132
- 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
+ );
13133
13493
 
13134
- 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
+ );
13135
13506
 
13136
- 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(",");
13137
13509
 
13138
- \`\`\`jsx
13139
- import {
13140
- Dialog,
13141
- Modal,
13142
- ModalTrigger,
13143
- ModalContent,
13144
- ModalClose,
13145
- DialogTitle,
13146
- } 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";
13147
13650
 
13148
13651
  <Modal>
13149
13652
  <ModalTrigger>
@@ -16207,25 +16710,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
16207
16710
  );
16208
16711
  };
16209
16712
 
16210
- export const PopoverContainedFocusExample = () => {
16211
- return (
16212
- <Popover type="dialog">
16213
- <PopoverTrigger>
16214
- <ActionButton label="Open" />
16215
- </PopoverTrigger>
16216
- <PopoverContent shouldContainFocus={true}>
16217
- <Dialog
16218
- size="content"
16219
- className={sprinkles({
16220
- padding: "md",
16221
- display: "flex",
16222
- gap: "xl",
16223
- flexDirection: "column",
16224
- })}
16225
- style={{
16226
- width: 200,
16227
- }}
16228
- >
16713
+ export const PopoverContainedFocusExample = ({
16714
+ shouldContainFocus = true,
16715
+ }: {
16716
+ shouldContainFocus?: boolean;
16717
+ }) => {
16718
+ const content = (
16719
+ <>
16229
16720
  <Text type="label">The focus is contained within the popover.</Text>
16230
16721
 
16231
16722
  <TextInput
@@ -16237,10 +16728,55 @@ export const PopoverContainedFocusExample = () => {
16237
16728
  label="Button"
16238
16729
  style={{ width: "100%", justifyContent: "center" }}
16239
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}
16240
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
+ )}
16241
16764
  </PopoverContent>
16242
16765
  </Popover>
16243
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
+ );
16244
16780
  };
16245
16781
 
16246
16782
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -18318,18 +18854,20 @@ function App() {
18318
18854
  }
18319
18855
  \`\`\`
18320
18856
 
18321
- 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.
18322
18860
 
18323
18861
  \`\`\`jsx
18324
18862
  const itemsWithSections = [
18325
18863
  {
18326
18864
  id: "solid",
18327
- type: "section",
18865
+ title: "Solid",
18328
18866
  children: items,
18329
18867
  },
18330
18868
  {
18331
18869
  id: "dashed",
18332
- type: "section",
18870
+ title: "Dashed",
18333
18871
  children: [
18334
18872
  {
18335
18873
  id: "ellipse-dashed",
@@ -18353,6 +18891,18 @@ const itemsWithSections = [
18353
18891
  <Select items={itemsWithSections} label="Label" />;
18354
18892
  \`\`\`
18355
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
+
18356
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.
18357
18907
 
18358
18908
  \`\`\`jsx
@@ -18556,14 +19106,16 @@ import { Virtualizer, ListLayout } from "@baseline-ui/core";
18556
19106
  \`\`\`
18557
19107
 
18558
19108
  | Key | Function |
18559
- | ----------- | ------------------------------------------------------------------------------------ |
19109
+ | -------------------- | ------------------------------------------------------------------------------------ |
19110
+ | \`Tab\` | Moves focus to and from the trigger. The popup itself is not a tab stop |
18560
19111
  | \`Space\` | Opens the listbox popup or toggles selection of the focused item in multiselect mode |
18561
19112
  | \`Enter\` | Opens the listbox popup or selects the focused item if the popup is open |
18562
19113
  | \`Escape\` | Closes the listbox popup |
18563
19114
  | \`ArrowDown\` | Opens the listbox popup and focuses the first item if no item is focused |
18564
19115
  | \`ArrowUp\` | Opens the listbox popup and focuses the last item if no item is focused |
18565
- | \`Home\` | focuses the first item |
18566
- | \`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 |
18567
19119
 
18568
19120
  **Note:** In multiselect mode (\`selectionMode="multiple"\`), the \`Space\` key toggles selection of the focused item, and the popover remains open after selection.
18569
19121
 
@@ -18593,6 +19145,8 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
18593
19145
  | \`.BaselineUI-Select-Label\` | Label element |
18594
19146
  | \`.BaselineUI-Select-Popover\` | Popover container |
18595
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 |
18596
19150
  | \`[data-disabled]\` | Applied when \`isDisabled\` is true |
18597
19151
  | \`[data-readonly]\` | Applied when \`isReadOnly\` is true |
18598
19152
  | \`[data-focused]\` | Applied when the trigger is focused |
@@ -18601,6 +19155,7 @@ The \`renderTrigger\` prop allows you to completely replace the default trigger
18601
19155
  | \`[data-pressed]\` | Applied when the trigger is pressed |
18602
19156
  | \`[data-open]\` | Applied when the popover is open |
18603
19157
 
19158
+ * **ComboBox** \u2014 Use this instead when users should be able to type directly into the trigger to filter or enter a value
18604
19159
  * **IconSelect** \u2014 Select variant with an icon trigger instead of a text button
18605
19160
  * **ButtonSelect** \u2014 Select variant styled as a button
18606
19161
  * **ListBox** \u2014 Standalone listbox without the trigger/popover wrapper
@@ -18743,125 +19298,125 @@ hideSelectAll?: boolean
18743
19298
  */
18744
19299
  hideClear?: boolean
18745
19300
  }`,stories:{usage:[{id:"core-forms-select-multiselect--basic",name:"Basic",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18746
- 29 | type Story = StoryObj<typeof meta>;
18747
- 30 |
18748
- > 31 | export {
19301
+ 32 | type Story = StoryObj<typeof meta>;
19302
+ 33 |
19303
+ > 34 | export {
18749
19304
  | ^
18750
- 32 | Basic,
18751
- 33 | WithLabel,
18752
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-label",name:"WithLabel",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18753
- 29 | type Story = StoryObj<typeof meta>;
18754
- 30 |
18755
- > 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 {
18756
19311
  | ^
18757
- 32 | Basic,
18758
- 33 | WithLabel,
18759
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-description",name:"WithDescription",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18760
- 29 | type Story = StoryObj<typeof meta>;
18761
- 30 |
18762
- > 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 {
18763
19318
  | ^
18764
- 32 | Basic,
18765
- 33 | WithLabel,
18766
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-error",name:"WithError",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18767
- 29 | type Story = StoryObj<typeof meta>;
18768
- 30 |
18769
- > 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 {
18770
19325
  | ^
18771
- 32 | Basic,
18772
- 33 | WithLabel,
18773
- 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
18774
- 29 | type Story = StoryObj<typeof meta>;
18775
- 30 |
18776
- > 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 {
18777
19332
  | ^
18778
- 32 | Basic,
18779
- 33 | WithLabel,
18780
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-warning",name:"WithWarning",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18781
- 29 | type Story = StoryObj<typeof meta>;
18782
- 30 |
18783
- > 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 {
18784
19339
  | ^
18785
- 32 | Basic,
18786
- 33 | WithLabel,
18787
- 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
18788
- 29 | type Story = StoryObj<typeof meta>;
18789
- 30 |
18790
- > 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 {
18791
19346
  | ^
18792
- 32 | Basic,
18793
- 33 | WithLabel,
18794
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--ghost",name:"Ghost",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18795
- 29 | type Story = StoryObj<typeof meta>;
18796
- 30 |
18797
- > 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 {
18798
19353
  | ^
18799
- 32 | Basic,
18800
- 33 | WithLabel,
18801
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-disabled",name:"WithDisabled",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18802
- 29 | type Story = StoryObj<typeof meta>;
18803
- 30 |
18804
- > 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 {
18805
19360
  | ^
18806
- 32 | Basic,
18807
- 33 | WithLabel,
18808
- 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
18809
- 29 | type Story = StoryObj<typeof meta>;
18810
- 30 |
18811
- > 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 {
18812
19367
  | ^
18813
- 32 | Basic,
18814
- 33 | WithLabel,
18815
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--with-sections",name:"WithSections",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18816
- 29 | type Story = StoryObj<typeof meta>;
18817
- 30 |
18818
- > 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 {
18819
19374
  | ^
18820
- 32 | Basic,
18821
- 33 | WithLabel,
18822
- 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
18823
- 29 | type Story = StoryObj<typeof meta>;
18824
- 30 |
18825
- > 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 {
18826
19381
  | ^
18827
- 32 | Basic,
18828
- 33 | WithLabel,
18829
- 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
18830
- 29 | type Story = StoryObj<typeof meta>;
18831
- 30 |
18832
- > 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 {
18833
19388
  | ^
18834
- 32 | Basic,
18835
- 33 | WithLabel,
18836
- 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
18837
- 29 | type Story = StoryObj<typeof meta>;
18838
- 30 |
18839
- > 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 {
18840
19395
  | ^
18841
- 32 | Basic,
18842
- 33 | WithLabel,
18843
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--scrollable",name:"Scrollable",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18844
- 29 | type Story = StoryObj<typeof meta>;
18845
- 30 |
18846
- > 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 {
18847
19402
  | ^
18848
- 32 | Basic,
18849
- 33 | WithLabel,
18850
- 34 | WithDescription,`}},{id:"core-forms-select-multiselect--long-list",name:"LongList",error:{name:"SyntaxError",message:`Expected story to be a function or variable declaration
18851
- 29 | type Story = StoryObj<typeof meta>;
18852
- 30 |
18853
- > 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 {
18854
19409
  | ^
18855
- 32 | Basic,
18856
- 33 | WithLabel,
18857
- 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
18858
- 29 | type Story = StoryObj<typeof meta>;
18859
- 30 |
18860
- > 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 {
18861
19416
  | ^
18862
- 32 | Basic,
18863
- 33 | WithLabel,
18864
- 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
18865
19420
  aria-label="Choose Stroke Style"
18866
19421
  items={items}
18867
19422
  placeholder="Choose Stroke Style"
@@ -18920,7 +19475,29 @@ hideClear?: boolean
18920
19475
  aria-label="Choose Stroke Style"
18921
19476
  items={items}
18922
19477
  placeholder="Choose Stroke Style"
18923
- 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";
18924
19501
  import React from "react";
18925
19502
  import { useFilter } from "react-aria";
18926
19503
  import { Autocomplete, Virtualizer } from "react-aria-components";
@@ -18929,11 +19506,16 @@ import { useListData } from "react-stately";
18929
19506
  import { ActionButton } from "../../ActionButton";
18930
19507
  import { items } from "../../ListBox/__tests__/testComponents";
18931
19508
  import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
18932
- 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";
18933
19514
  import { IconSelect } from "../IconSelect";
18934
19515
  import { Select } from "../Select";
18935
19516
 
18936
19517
  import type { IconSelectProps } from "../Select.types";
19518
+ import type { Key } from "@react-types/shared";
18937
19519
 
18938
19520
  export const SelectExample: React.FC<
18939
19521
  Omit<React.ComponentProps<typeof Select>, "items">
@@ -18941,13 +19523,28 @@ export const SelectExample: React.FC<
18941
19523
  return (
18942
19524
  <Select
18943
19525
  placeholder="Choose an item"
18944
- {...args}
18945
19526
  optionClassName={(item) => item.label}
19527
+ {...args}
18946
19528
  items={items}
18947
19529
  />
18948
19530
  );
18949
19531
  };
18950
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
+
18951
19548
  export const SelectGetTargetRectExample: React.FC = () => {
18952
19549
  const [called, setCalled] = React.useState(false);
18953
19550
 
@@ -19004,90 +19601,228 @@ export const SelectCustomTriggerExample: React.FC<
19004
19601
  };
19005
19602
 
19006
19603
  export const IconSelectExample: React.FC<
19007
- Omit<IconSelectProps, "items" | "icon" | "aria-label">
19604
+ Omit<IconSelectProps, "items" | "aria-label">
19008
19605
  > = (args) => {
19009
19606
  return (
19010
19607
  <IconSelect
19011
19608
  placeholder="Choose an item"
19609
+ icon={EllipseIcon}
19012
19610
  {...args}
19013
19611
  items={items}
19014
- icon={EllipseIcon}
19015
19612
  aria-label="Aria Label"
19016
19613
  />
19017
19614
  );
19018
19615
  };
19019
19616
 
19020
- export const SelectWithVirtualizeAutocompleteExample: React.FC<
19617
+ export const VirtualSelectWithSectionHeadersExample: React.FC<
19021
19618
  Omit<React.ComponentProps<typeof Select>, "items">
19022
19619
  > = (args) => {
19023
- const list = useListData({
19024
- initialItems: virtualizeAutocompleteItems,
19025
- });
19026
- const { contains } = useFilter({ sensitivity: "base" });
19027
- const filter = (textValue, inputValue) => contains(textValue, inputValue);
19028
19620
  return (
19029
- <Virtualizer {...VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS.LIST_BOX}>
19030
- <Autocomplete filter={filter}>
19621
+ <Virtualizer
19622
+ layout={ListLayout}
19623
+ layoutOptions={{
19624
+ rowHeight: 36,
19625
+ headingHeight: 40,
19626
+ }}
19627
+ >
19031
19628
  <Select
19629
+ aria-label="Choose an item"
19630
+ placeholder="Choose an item"
19631
+ showSectionHeader={true}
19032
19632
  {...args}
19033
- items={list.items}
19034
- placeholder="Select multiple options"
19035
- maxHeight={300}
19036
- style={{
19037
- width: 400,
19038
- }}
19633
+ items={itemsWithSectionTitles}
19634
+ defaultOpen={true}
19635
+ maxHeight={400}
19039
19636
  />
19040
- </Autocomplete>
19041
19637
  </Virtualizer>
19042
19638
  );
19043
- };`},similarTo:[],figmaUrl:null},Separator:{id:"core-content-separator",breadcrumb:"Core/Content/Separator",importStatement:'import { Separator } from "@baseline-ui/core";',description:"`Separator` is a thin horizontal or vertical rule that divides adjacent content. Use it to create a visual break between sections, groups of controls, or items in a list.",documentation:`\`Separator\` is a thin horizontal or vertical rule that divides adjacent content. Use it to create a visual break between sections, groups of controls, or items in a list.
19044
-
19045
- \`\`\`jsx
19046
- import { Separator } from "../../utils";
19047
-
19048
- <Separator />;
19049
- \`\`\`
19050
-
19051
- By default, the separator is horizontal.
19052
-
19053
- \`\`\`jsx
19054
- import { Separator } from "../../utils";
19055
-
19056
- <div>
19057
- Section 1
19058
- <Separator />
19059
- Section 2
19060
- </div>;
19061
- \`\`\`
19062
-
19063
- The \`orientation\` prop can be used to change the orientation of the separator.
19064
-
19065
- \`\`\`jsx
19066
- import { Separator } from "../../utils";
19639
+ };
19067
19640
 
19068
- <div>
19069
- Section 1
19070
- <Separator orientation="vertical" variant="secondary" />
19071
- Section 2
19072
- </div>;
19073
- \`\`\`
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
+ };
19074
19655
 
19075
- | Selector | Description |
19076
- | ------------------ | ----------------------------------------------------------------------- |
19077
- | \\[data-orientation] | The orientation of the separator. It can be \`horizontal\` or \`vertical\`. |`,props:`interface SeparatorProps {
19078
- /**
19079
- * The orientation of the separator.
19080
- *
19081
- * @default 'horizontal'
19082
- */
19083
- orientation?: "horizontal" | "vertical"
19084
19656
  /**
19085
- * 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.
19086
19659
  */
19087
- elementType?: string
19088
- /**
19089
- * @deprecated Do not use in new components. This is a legacy block
19090
- * 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
19091
19826
  * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
19092
19827
  * Retained on existing components for backward compatibility.
19093
19828
  *
@@ -23296,7 +24031,7 @@ export const TaggedPaginationExample: React.FC<
23296
24031
  {...props}
23297
24032
  />
23298
24033
  );
23299
- };`},similarTo:[],figmaUrl:null},Text:{id:"core-content-text",breadcrumb:"Core/Content/Text",importStatement:'import { Text, VariantViewer } from "@baseline-ui/core";',description:"`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.",documentation:'`Text` is a typography primitive that renders strings with consistent type, size, and weight from the design system. Use it whenever you need to display textual content so that headings, body copy, values, and helper text stay visually aligned.\n\n```jsx\nimport { Text } from "@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 {
23300
24035
  /**
23301
24036
  * @deprecated Do not use in new components. This is a legacy block
23302
24037
  * identifier; new components should exclude it via
@@ -23352,6 +24087,13 @@ children: React.ReactNode
23352
24087
  * @default "span"
23353
24088
  */
23354
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
23355
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) => (
23356
24098
  <VariantViewer<React.ComponentProps<typeof Text>>
23357
24099
  header={["Small", "Medium", "Large"]}
@@ -23386,8 +24128,24 @@ elementType?: React.ElementType
23386
24128
  }}
23387
24129
  defaultProps={args}
23388
24130
  />
23389
- );`}],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";
23390
24147
 
24148
+ import { UNSAFE_ListBox } from "../../UNSAFE_ListBox";
23391
24149
  import { Text } from "../Text";
23392
24150
 
23393
24151
  export function TextWithRef() {
@@ -23408,65 +24166,61 @@ export function TextWithRef() {
23408
24166
  <span data-testid="tag-name">{tag}</span>
23409
24167
  </>
23410
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
+ );
23411
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 {
23412
- /**
23413
- * @deprecated Do not use in new components. This is a legacy block
23414
- * identifier; new components should exclude it via
23415
- * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
23416
- * Retained on existing components for backward compatibility.
23417
- *
23418
- * The unique identifier for the block. This is used to identify the block in
23419
- * the DOM and in the block map. It is added as a data attribute
23420
- * \`data-block-id\` to the root element of the block if a DOM node is
23421
- * rendered.
23422
- */
23423
- data-block-id?: string
23424
- /**
23425
- * @deprecated Do not use in new components. This is a legacy block group
23426
- * marker; new components should exclude it via
23427
- * \`Omit<StylingProps, keyof BlockProps>\` (see \`StatusCard\` / \`Code\`).
23428
- * Retained on existing components for backward compatibility.
23429
- *
23430
- * Represents a data block group. This is similar to \`data-block-id\` but it
23431
- * doesn't have to be unique just like \`class\`. This is used to group blocks
23432
- * together in the DOM and in the block map. It is added as a data attribute
23433
- * \`data-block-class\` to the root element of the block if a DOM node is
23434
- * rendered.
23435
- */
23436
- data-block-class?: string
23437
- /**
23438
- * The className applied to the root element of the component.
23439
- */
23440
- className?: string
23441
- /**
23442
- * The style applied to the root element of the component.
23443
- */
23444
- style?: React.CSSProperties
23445
- /**
23446
- * The description to display below the input.
23447
- */
23448
- description?: string
23449
- /**
23450
- * The error message to display when the input is in an error state.
23451
- */
23452
- errorMessage?: string
23453
- /**
23454
- * The warning message to display when the input is in a warning state.
23455
- */
23456
- warningMessage?: string
23457
- /**
23458
- * The style object to apply to the input element
23459
- */
23460
- inputStyle?: React.CSSProperties
23461
- /**
23462
- * The class name to apply to the input element
23463
- */
23464
- inputClassName?: string
23465
- /**
23466
- * The number of visible text rows for the multi-line input. Only applies when
23467
- * \`isMultiLine\` is set.
23468
- */
23469
- rows?: number
23470
24224
  variant?: any
23471
24225
  labelPosition?: any
23472
24226
  isMultiLine?: any
@@ -26809,6 +27563,7 @@ export const VirtualizedTreeViewWithRenameExample: React.FC<
26809
27563
  VirtualizedScrollIntoViewExample,
26810
27564
  VirtualListBoxGridLayoutExample,
26811
27565
  VirtualListBoxListLayoutExample,
27566
+ VirtualListBoxWithSectionHeadersExample,
26812
27567
  VirtualListBoxWithSectionsExample,
26813
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.
26814
27569
 
@@ -26905,6 +27660,34 @@ const fonts = [
26905
27660
  />;
26906
27661
  \`\`\`
26907
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
+
26908
27691
  \`\`\`jsx
26909
27692
  <UNSAFE_ListBox
26910
27693
  items={items}
@@ -27002,6 +27785,10 @@ style?: React.CSSProperties
27002
27785
  /**
27003
27786
  * The custom render function for the listbox options.
27004
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
+ *
27005
27792
  * @param item ListOption
27006
27793
  * @param options ListBoxItemRenderProps
27007
27794
  */
@@ -27011,6 +27798,9 @@ renderOption?: (
27011
27798
  ) => React.ReactNode
27012
27799
  /**
27013
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.
27014
27804
  */
27015
27805
  optionClassName?: | string
27016
27806
  | ((
@@ -27019,6 +27809,9 @@ optionClassName?: | string
27019
27809
  ) => string | undefined)
27020
27810
  /**
27021
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.
27022
27815
  */
27023
27816
  optionStyle?: | React.CSSProperties
27024
27817
  | ((
@@ -27028,15 +27821,25 @@ optionStyle?: | React.CSSProperties
27028
27821
  /**
27029
27822
  * The custom render function for the listbox sections.
27030
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
+ *
27031
27828
  * @param section ListSection
27032
27829
  */
27033
27830
  renderSectionHeader?: (section: ListSection) => React.ReactNode
27034
27831
  /**
27035
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.
27036
27836
  */
27037
27837
  sectionClassName?: string | ((section: ListSection) => string | undefined)
27038
27838
  /**
27039
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.
27040
27843
  */
27041
27844
  sectionStyle?: | React.CSSProperties
27042
27845
  | ((section: ListSection) => React.CSSProperties | undefined)
@@ -27072,6 +27875,11 @@ withSectionHeaderPadding?: boolean
27072
27875
  *
27073
27876
  * type ListItem = ListOption | ListSection;
27074
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.
27075
27883
  */
27076
27884
  items?: ListItem[]
27077
27885
  /**
@@ -27158,7 +27966,7 @@ listBoxHandle?: React.RefObject<ListHandle>
27158
27966
  items={items}
27159
27967
  aria-label="List box"
27160
27968
  selectionMode="multiple"
27161
- defaultSelectedKeys={["item-0"]} />;`},{id:"core-collections-unsafe-listbox--virtualized-horizontal",name:"Virtualized Horizontal",snippet:'const VirtualizedHorizontal = () => <VirtualListBoxGridLayoutExample items={items} aria-label="List box" selectionMode="multiple" />;'},{id:"core-collections-unsafe-listbox--virtualized-with-sections",name:"Virtualized With Sections",snippet:'const VirtualizedWithSections = () => <VirtualListBoxWithSectionsExample items={items} aria-label="List box" selectionMode="single" />;'},{id:"core-collections-unsafe-listbox--virtualized-scroll-into-view",name:"Virtualized Scroll Into View",snippet:"const VirtualizedScrollIntoView = () => <VirtualizedScrollIntoViewExample />;"}],implementation:`import { XIcon } from "@baseline-ui/icons/16";
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";
27162
27970
  import {
27163
27971
  EllipseDashedIcon,
27164
27972
  EllipseIcon,
@@ -27188,7 +27996,10 @@ import {
27188
27996
  import { useDragAndDrop } from "../hooks/useDragAndDrop";
27189
27997
  import { UNSAFE_ListBox as ListBox } from "../ListBox";
27190
27998
 
27191
- import type { ListItem } from "../../shared/types/List";
27999
+ import type {
28000
+ ListItem,
28001
+ ListOption as ListOptionType,
28002
+ } from "../../shared/types/List";
27192
28003
  import type { ListHandle } from "../ListBox.types";
27193
28004
  import type { DroppableCollectionReorderEvent } from "@react-types/shared";
27194
28005
 
@@ -27765,16 +28576,85 @@ export const VirtualListBoxWithSectionsExample: React.FC<
27765
28576
  );
27766
28577
  };
27767
28578
 
27768
- export const VirtualizedScrollIntoViewExample: React.FC = () => {
27769
- const listBoxHandle = React.useRef<ListHandle>(null);
27770
- const items = Array.from({ length: 10_000 }).map((_, i) => ({
27771
- id: \`item-\${i}\`,
27772
- label: \`Item \${i}\`,
27773
- }));
27774
-
28579
+ export const VirtualListBoxWithSectionHeadersExample: React.FC<
28580
+ Omit<React.ComponentProps<typeof ListBox>, "items">
28581
+ > = (args) => {
27775
28582
  return (
27776
- <Box display="flex" flexDirection="column" gap="md" style={{ width: 300 }}>
27777
- <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">
27778
28658
  <Text type="body" size="sm">
27779
28659
  Scroll to items in a virtualized list (10,000 items). Items that
27780
28660
  aren&apos;t currently rendered will be scrolled into view.
@@ -27822,7 +28702,110 @@ export const VirtualizedScrollIntoViewExample: React.FC = () => {
27822
28702
  </Virtualizer>
27823
28703
  </Box>
27824
28704
  );
27825
- };`},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\`.
27826
28809
 
27827
28810
  * Efficient rendering for very large collections
27828
28811
  * Preset layout options for list boxes, tree views, and image galleries
@@ -30800,25 +31783,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
30800
31783
  );
30801
31784
  };
30802
31785
 
30803
- export const PopoverContainedFocusExample = () => {
30804
- return (
30805
- <Popover type="dialog">
30806
- <PopoverTrigger>
30807
- <ActionButton label="Open" />
30808
- </PopoverTrigger>
30809
- <PopoverContent shouldContainFocus={true}>
30810
- <Dialog
30811
- size="content"
30812
- className={sprinkles({
30813
- padding: "md",
30814
- display: "flex",
30815
- gap: "xl",
30816
- flexDirection: "column",
30817
- })}
30818
- style={{
30819
- width: 200,
30820
- }}
30821
- >
31786
+ export const PopoverContainedFocusExample = ({
31787
+ shouldContainFocus = true,
31788
+ }: {
31789
+ shouldContainFocus?: boolean;
31790
+ }) => {
31791
+ const content = (
31792
+ <>
30822
31793
  <Text type="label">The focus is contained within the popover.</Text>
30823
31794
 
30824
31795
  <TextInput
@@ -30830,10 +31801,55 @@ export const PopoverContainedFocusExample = () => {
30830
31801
  label="Button"
30831
31802
  style={{ width: "100%", justifyContent: "center" }}
30832
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}
30833
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
+ )}
30834
31837
  </PopoverContent>
30835
31838
  </Popover>
30836
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
+ );
30837
31853
  };
30838
31854
 
30839
31855
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -31960,25 +32976,13 @@ export const PopoverDefaultOpenWithArrowExample = () => {
31960
32976
  );
31961
32977
  };
31962
32978
 
31963
- export const PopoverContainedFocusExample = () => {
31964
- return (
31965
- <Popover type="dialog">
31966
- <PopoverTrigger>
31967
- <ActionButton label="Open" />
31968
- </PopoverTrigger>
31969
- <PopoverContent shouldContainFocus={true}>
31970
- <Dialog
31971
- size="content"
31972
- className={sprinkles({
31973
- padding: "md",
31974
- display: "flex",
31975
- gap: "xl",
31976
- flexDirection: "column",
31977
- })}
31978
- style={{
31979
- width: 200,
31980
- }}
31981
- >
32979
+ export const PopoverContainedFocusExample = ({
32980
+ shouldContainFocus = true,
32981
+ }: {
32982
+ shouldContainFocus?: boolean;
32983
+ }) => {
32984
+ const content = (
32985
+ <>
31982
32986
  <Text type="label">The focus is contained within the popover.</Text>
31983
32987
 
31984
32988
  <TextInput
@@ -31990,10 +32994,55 @@ export const PopoverContainedFocusExample = () => {
31990
32994
  label="Button"
31991
32995
  style={{ width: "100%", justifyContent: "center" }}
31992
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}
31993
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
+ )}
31994
33030
  </PopoverContent>
31995
33031
  </Popover>
31996
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
+ );
31997
33046
  };
31998
33047
 
31999
33048
  export const PopoverWithScrollableViewportExample: React.FC<{
@@ -32759,7 +33808,15 @@ iconTooltip?: ActionIconButtonProps["tooltip"]
32759
33808
  placeholder="Select"
32760
33809
  items={items}
32761
33810
  isReadOnly
32762
- 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";
32763
33820
  import React from "react";
32764
33821
  import { useFilter } from "react-aria";
32765
33822
  import { Autocomplete, Virtualizer } from "react-aria-components";
@@ -32768,11 +33825,16 @@ import { useListData } from "react-stately";
32768
33825
  import { ActionButton } from "../../ActionButton";
32769
33826
  import { items } from "../../ListBox/__tests__/testComponents";
32770
33827
  import { virtualizeAutocompleteItems } from "../../Menu/__tests__/data";
32771
- 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";
32772
33833
  import { IconSelect } from "../IconSelect";
32773
33834
  import { Select } from "../Select";
32774
33835
 
32775
33836
  import type { IconSelectProps } from "../Select.types";
33837
+ import type { Key } from "@react-types/shared";
32776
33838
 
32777
33839
  export const SelectExample: React.FC<
32778
33840
  Omit<React.ComponentProps<typeof Select>, "items">
@@ -32780,13 +33842,28 @@ export const SelectExample: React.FC<
32780
33842
  return (
32781
33843
  <Select
32782
33844
  placeholder="Choose an item"
32783
- {...args}
32784
33845
  optionClassName={(item) => item.label}
33846
+ {...args}
32785
33847
  items={items}
32786
33848
  />
32787
33849
  );
32788
33850
  };
32789
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
+
32790
33867
  export const SelectGetTargetRectExample: React.FC = () => {
32791
33868
  const [called, setCalled] = React.useState(false);
32792
33869
 
@@ -32843,19 +33920,136 @@ export const SelectCustomTriggerExample: React.FC<
32843
33920
  };
32844
33921
 
32845
33922
  export const IconSelectExample: React.FC<
32846
- Omit<IconSelectProps, "items" | "icon" | "aria-label">
33923
+ Omit<IconSelectProps, "items" | "aria-label">
32847
33924
  > = (args) => {
32848
33925
  return (
32849
33926
  <IconSelect
32850
33927
  placeholder="Choose an item"
33928
+ icon={EllipseIcon}
32851
33929
  {...args}
32852
33930
  items={items}
32853
- icon={EllipseIcon}
32854
33931
  aria-label="Aria Label"
32855
33932
  />
32856
33933
  );
32857
33934
  };
32858
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
+
32859
34053
  export const SelectWithVirtualizeAutocompleteExample: React.FC<
32860
34054
  Omit<React.ComponentProps<typeof Select>, "items">
32861
34055
  > = (args) => {
@@ -32879,6 +34073,27 @@ export const SelectWithVirtualizeAutocompleteExample: React.FC<
32879
34073
  </Autocomplete>
32880
34074
  </Virtualizer>
32881
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
+ );
32882
34097
  };`},similarTo:[],figmaUrl:null},IconSlider:{id:"core-forms-slider-iconslider",importStatement:'import { IconSlider } from "@baseline-ui/core";',description:"",documentation:null,props:`interface IconSliderProps {
32883
34098
  /**
32884
34099
  * @deprecated Do not use in new components. This is a legacy block
@@ -32989,6 +34204,21 @@ import { ActionButton, I18nProvider, ThemeProvider } from "@baseline-ui/core";
32989
34204
 
32990
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.
32991
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
+
32992
34222
  ### Styling
32993
34223
 
32994
34224
  Import the following files at the top of your stylesheet to use the Baseline UI styles:
@@ -33257,22 +34487,24 @@ function App() {
33257
34487
  }
33258
34488
  \`\`\``,internationalization:`# Internationalization
33259
34489
 
33260
- It\u2019s important the content of your website is accessible to as many people as possible. This includes people who speak different languages and people who use screen readers. Internationalization (i18n) is the process of making your website accessible to people who speak different languages. Baseline UI supports internationalization for many of the components out of the box. 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).
33261
34493
 
33262
34494
  ## Localization
33263
34495
 
33264
- Localization is the process of translating your website into different languages. Baseline UI supports localization of the built-in strings, dates and times, number/currency formatting, collating and sorting, text search, and more. It\u2019s built on top of [react-aria](https://react-spectrum.adobe.com/react-aria), 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.
33265
34497
 
33266
- Baseline UI automatically detects a user\u2019s preferred language from the browser, and it uses that language for all localization. It\u2019s possible to override the language used for localization by providing a \`locale\` prop to the \`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.
33267
34499
 
33268
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.
33269
34501
 
33270
34502
  ### Example
33271
34503
 
33272
- The rootmost element of your application should define the [\`lang\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [\`dir\`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes so that the browser knows which language and direction the user interface should be rendered in. This can be done with the \`useLocale\` hook 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:
33273
34505
 
33274
34506
  \`\`\`jsx
33275
- import { useLocale } from "../../utils";
34507
+ import { useLocale } from "@baseline-ui/core";
33276
34508
 
33277
34509
  function YourApp() {
33278
34510
  let { locale, direction } = useLocale();
@@ -33288,27 +34520,128 @@ function YourApp() {
33288
34520
  You can also override the language used for localization with the [\`I18nProvider\`](/?path=/docs/utilities-i18nprovider--docs) component:
33289
34521
 
33290
34522
  \`\`\`jsx
33291
- import { I18nProvider } from "../../utils";
34523
+ import { I18nProvider } from "@baseline-ui/core";
33292
34524
 
33293
34525
  <I18nProvider locale="fr-FR">
33294
34526
  <YourApp />
33295
34527
  </I18nProvider>;
33296
34528
  \`\`\`
33297
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
+
33298
34589
  ### Supported locales
33299
34590
 
33300
- 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.
33301
34607
 
33302
- ### Translations list
34608
+ ### Translating them yourself
33303
34609
 
33304
- 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.
33305
34627
 
33306
34628
  ## Overriding Component Translations
33307
34629
 
33308
34630
  You can override any built-in component string by passing its \`bui.*\`-namespaced key to
33309
34631
  \`I18nProvider.messages\`. Only the keys you provide are overridden \u2014 all other strings
33310
- fall back to each component's built-in defaults. You can also supply strings for locales
33311
- 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
+ \`\`\`
33312
34645
 
33313
34646
  Each component's documentation lists its overridable keys and shows an example. See:
33314
34647
 
@@ -33316,7 +34649,43 @@ Each component's documentation lists its overridable keys and shows an example.
33316
34649
  * [ColorInput](?path=/docs/components-colorinput--docs#translations)
33317
34650
  * [ImageDropZone](?path=/docs/components-imagedropzone--docs#translations)
33318
34651
  * [FreehandCanvas](?path=/docs/components-freehandcanvas--docs#translations)
33319
- * [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
33320
34689
 
33321
34690
  Baseline UI provides a powerful, type-safe styling system built on top of [vanilla-extract sprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/).
33322
34691
 
@@ -34226,7 +35595,7 @@ padding={[null, "lg", "xl"]}
34226
35595
 
34227
35596
  * [vanilla-extract sprinkles documentation](https://vanilla-extract.style/documentation/packages/sprinkles/) - Learn about the underlying sprinkles framework
34228
35597
  * [Box component documentation](/docs/core-utilities-box--docs) - Detailed information about the Box component
34229
- * [Theme documentation](/docs/theming--docs) - Learn about Baseline UI's theming system`};var c={"8":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","ChevronRightFilledIcon","ChevronRightIcon","EllipseIcon","MinusIcon","PlusIcon","XIcon"],"12":["CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","EditIcon","EllipseIcon","EnterKeyIcon","LockFilledIcon","LockIcon","MinusIcon","MoreVIcon","MoreIcon","PlaceholderIcon","PlusIcon","SearchIcon","SizeIcon","TrashIcon","XIcon","ZoomIcon"],"16":["AlignBottomIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDiagonalTopLeftBottomRightIcon","ArrowDownCircleFilledIcon","ArrowDownIcon","ArrowIcon","ArrowLeftRightIcon","ArrowRightIcon","ArrowUpArrowDownIcon","ArrowUpIcon","AtIcon","AttachmentsIcon","AvatarIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BulletListIcon","CalendarIcon","CaretLeftIcon","CaretRightIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","CircleFilledIcon","ClockIcon","CopyIcon","CustomizeIcon","DocumentEditIcon","DownloadIcon","DuplicateIcon","EditIcon","ElipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FilterAltIcon","FolderIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FullScreenIcon","HelpCircleIcon","HelpIcon","HereIcon","HideIcon","HighlightTextAltIcon","HighlightTextIcon","HorizontalScrollIcon","ImageIcon","InfoCircleFilledIcon","InsertIcon","ItalicIcon","LightBulbIcon","LineIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MeasureIcon","MinusIcon","MoreIcon","MoreVerticalIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","NumberedListIcon","OpenIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageVerticalScrollIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PlaceholderIcon","PlayIcon","PlusIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","ReadOnlyIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedoIcon","RemoveFormattingIcon","ReorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RulerIcon","SearchIcon","SettingsIcon","ShowIcon","SlashCommandsIcon","SoundRecordIcon","StampIcon","StarFilledIcon","StarIcon","StrikeoutTextAltIcon","TableCellIcon","TableColumnIcon","TableHeaderIcon","TableIcon","TableRowIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextDecreaseIndentIcon","TextIcon","TextIncreaseIndentIcon","TextMarkIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UndoIcon","UnlockIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WindowedIcon","WorkflowIcon","XCircleFilledIcon","XIcon"],"20":["AddPageIcon","AnonymousIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpCircleFilledIcon","AtIcon","AvatarFilledIcon","BoldIcon","CalloutIcon","CaretDownIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ClockIcon","CollapseIcon","CommentIcon","CopyIcon","CutIcon","DistanceIcon","DownloadIcon","DuplicateIcon","EditIcon","EllipseIcon","EmojiSmileIcon","ErrorAltCircleFilledIcon","ErrorAlternativeCircleIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","FormDateIcon","FormSignatureIcon","FormTextFieldIcon","HelpCircleIcon","HighlightTextIcon","HomeIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","ItalicIcon","LinkIcon","ListIcon","LockIcon","MagicIcon","MinusIcon","MoreIcon","MoreVerticalIcon","MoveIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCrossIcon","NoteHelpIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OpenIcon","PageMoveLeftIcon","PageMoveRightIcon","PagesInsertIcon","PasteIcon","PipetteIcon","PlusIcon","PrintIcon","RotateClockwiseIcon","SearchIcon","SettingsIcon","ShapeIcon","ShareIcon","SoundIcon","SoundRecordIcon","StarFilledIcon","StarIcon","StyleIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TrashIcon","TypeTextIcon","UnderlineIcon","UploadIcon","WarningFilledIcon","WarningIcon","XCircleFilledIcon","XCircleIcon","XIcon"],"24":["AddNoteCloudIcon","AddNoteIcon","AddTextSerifIcon","AiIcon","AirplaneIcon","AlignBottomIcon","AlignHorizontalCenterIcon","AlignMiddleIcon","AlignTopIcon","AnonymousIcon","ArrowDownIcon","ArrowIcon","ArrowLeftIcon","ArrowRightIcon","ArrowUpIcon","AtIcon","AttachmentIcon","AvatarFilledIcon","AvatarIcon","BlendModeIcon","BoldIcon","BookmarkFilledIcon","BookmarkIcon","BorderColorIcon","BottomBorderIcon","BulletListIcon","CalibrateIcon","CaptureAddIcon","CaretDownIcon","CaretIcon","CaretLeftIcon","CaretRightIcon","CaretUpIcon","CheckmarkCircleFilledIcon","CheckmarkCircleIcon","CheckmarkIcon","ChevronListIcon","ClockIcon","CloudyBorderIcon","CollapseIcon","ColorPaletteIcon","ColorSwatchIcon","CommentIcon","CommentInSidebarIcon","CommentOnPageIcon","CompareDocumentsIcon","CopyIcon","CopyPageIcon","CropIcon","CustomizeIcon","CutIcon","DateModifiedIcon","DatePlusIcon","DebugIcon","DocumentArrowDownCircleIcon","DocumentArrowDownIcon","DocumentArrowRightIcon","DocumentFilledIcon","DocumentLockIcon","DocumentPdfIcon","DownloadIcon","DragIndicatorIcon","DragIndicatorVerticalIcon","DuplicateIcon","EditAnnotationsIcon","EditContentIcon","EditDocumentIcon","EditIcon","EditThumbnailsIcon","EllipseAreaIcon","EllipseCloudyIcon","EllipseDashedIcon","EllipseIcon","EmbedIcon","EmojiSmileIcon","EndCapArrowFilledIcon","EndCapArrowIcon","EndCapChevronFilledIcon","EndCapChevronIcon","EndCapCircleIcon","EndCapDiamondIcon","EndCapNoneIcon","EndCapSlantedIcon","EndCapSquareIcon","EndCapStraightIcon","EraserIcon","ErrorAltCircleFilledIcon","ErrorAltIcon","ErrorCircleFilledIcon","ErrorCircleIcon","ExpandIcon","ExpandVerticalIcon","FillColorIcon","FilterIcon","FitToHeightIcon","FivePagesHorizontalFilledIcon","FivePagesVerticalFilledIcon","FolderAddIcon","FolderIcon","FontListIcon","FontSizeIcon","FormButtonIcon","FormChoiceIcon","FormComboboxIcon","FormDateIcon","FormListboxIcon","FormPageIcon","FormRadioButtonIcon","FormSignatureIcon","FormTextFieldIcon","FormTwoRadioButtonsIcon","FourPagesGridFilledIcon","FourPagesHorizontalFilledIcon","FourPagesStackedFilledIcon","FourPagesVerticalFilledIcon","GroupIcon","HamburgerMenuIcon","HandIcon","HeartIcon","HideIcon","HideRevealIcon","HighlightTextIcon","HomeIcon","HorizontalScollIcon","ImageIcon","InfoCircleFilledIcon","InfoCircleIcon","InitialsIcon","InnerHorizontalBorderIcon","InnerVerticalBorderIcon","InsertIcon","ItalicIcon","LayerBottomIcon","LayerDownIcon","LayerTopIcon","LayerUpIcon","LayersIcon","LeftBindingIcon","LeftBorderIcon","LineCapsIcon","LineIcon","LineSpacingIcon","LineStyleCloudyIcon","LineStyleDashedDoubleDashIcon","LineStyleDashedDoubleGapIcon","LineStyleDashedQuadrupleDashIcon","LineStyleDashedSingleGapIcon","LineStyleIcon","LineStyleSolidIcon","LineWidthIcon","LinkIcon","LockFilledIcon","LockIcon","MagicIcon","MagicPenIcon","MailIcon","MarkupIcon","MarqueeZoomIcon","MeasureIcon","MergeIcon","MessageCloudIcon","MinusIcon","MoonIcon","MoreCircleIcon","MoreIcon","MoreVerticalIcon","MoveAllDirectionsIcon","MoveLeftIcon","MoveLeftRightIcon","MoveRightIcon","MultiplePagesIcon","NonEditableIcon","NoteArrowRightIcon","NoteCheckIcon","NoteCircleIcon","NoteCloudIcon","NoteCrossIcon","NoteHelpIcon","NoteIcon","NoteInsetIcon","NoteKeyIcon","NoteNewParagraphAltIcon","NoteNewParagraphIcon","NoteNoteIcon","NotePointerRightIcon","NoteSpeechBubbleIcon","NoteStarIcon","OcrIcon","OpacityIcon","PageAddIcon","PageCurlIcon","PageDuplicateIcon","PageFittingFillIcon","PageFittingFitIcon","PageHorizontalScrollIcon","PageLandscapeIcon","PageLayoutDoubleIcon","PageLayoutSingleIcon","PageMoveLeftIcon","PageMoveRightIcon","PageNumberCircleIcon","PageNumberIcon","PagePortraitIcon","PageRemoveIcon","PageVerticalScrollIcon","PagesInsertAltIcon","PagesInsertIcon","PagesNewFromSelectionAltIcon","PagesNewFromSelectionIcon","PagesSelectAllIcon","PagesSelectNoneIcon","PasteBoardIcon","PastePageIcon","PauseIcon","PenHighlighterIcon","PenIcon","PerimeterIcon","PinDropFilledIcon","PinDropIcon","PipetteIcon","PlayIcon","PlusCircleFilledIcon","PlusCircleIcon","PlusIcon","PointerIcon","PolygonAreaIcon","PolygonCloudyIcon","PolygonDashedIcon","PolygonIcon","PolylineIcon","PrecisionIcon","PrintIcon","PrivateModeIcon","PushPinIcon","QuestionmarkCircleIcon","ReaderViewIcon","RectangleAreaIcon","RectangleCloudyIcon","RectangleDashedIcon","RectangleIcon","RedactIcon","RedactRectangleIcon","RedactTextHighlighterIcon","RedactionTextRepeatingIcon","RedactionTextSingleIcon","RedoAllIcon","RedoIcon","RegexIcon","ReplaceIcon","RightBindingIcon","RightBorderIcon","RotateClockwiseIcon","RotateCounterClockwiseIcon","RotateObjectClockwiseIcon","RotateObjectCounterClockwiseIcon","RulerIcon","ScaleIcon","SearchCircleIcon","SearchIcon","SearchSelectionIcon","SelectAllIcon","SelectionToolIcon","SettingsIcon","ShapesIcon","ShareAltIcon","ShareIcon","ShieldAddIcon","ShieldCheckmarkIcon","ShieldWarningIcon","ShieldXIcon","ShowIcon","SidebarIcon","SignOutIcon","SignatureDigitalIcon","SignatureIcon","SinglePageFilledIcon","SoundIcon","SquigglyTextIcon","StampAddIcon","StampIcon","StarFilledIcon","StarIcon","StartCapArrowFilledIcon","StartCapArrowIcon","StartCapChevronFilledIcon","StartCapChevronIcon","StartCapCircleIcon","StartCapDiamondIcon","StartCapNoneIcon","StartCapSlantedIcon","StartCapSquareIcon","StartCapStraightIcon","StrikeoutTextIcon","StyleFilledIcon","StyleIcon","StylusFilledIcon","StylusIcon","SunIcon","TableCellIcon","TextAlignCenterIcon","TextAlignJustifyIcon","TextAlignLeftIcon","TextAlignRightIcon","TextCalloutIcon","TextColorIcon","TextIcon","TextPropertiesHideIcon","TextPropertiesShowIcon","TextSerifIcon","TextSmallerIcon","ThreePagesHorizontalFilledIcon","ThreePagesStackedFilledIcon","ThreePagesVerticalFilledIcon","ThumbnailsIcon","ThumbsDownIcon","ThumbsUpIcon","TopBorderIcon","TrashIcon","TwoPagesHorizontalFilledIcon","TwoPagesVerticalFilledIcon","TypeTextIcon","UnderlineIcon","UnderlineTextIcon","UndoAllIcon","UndoIcon","UndoRedoIcon","UngroupIcon","UnlockIcon","UploadIcon","UserIcon","VerticalScrollIcon","VideoIcon","WarningFilledIcon","WarningIcon","WidgetIcon","WorkflowIcon","XCircleFilledIcon","XCircleIcon","XIcon","ZoomInIcon","ZoomOutIcon"],"36":["ArrowRight","Check","Circle","Cross","Help","Inset","Key","NewParagraphAlt","NewParagraph","Note","PointerRight","SpeechBubble","Star"]};var p={version:"1.2.0"};var u=`
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=`
34230
35599
  # Baseline UI MCP Server Guidelines
34231
35600
 
34232
35601
  This MCP server provides AI assistants with structured access to Baseline UI's comprehensive component documentation, icon library, theming resources, and design guidelines.