Skip to content

Commit

Permalink
Add ToolbarInput
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Feb 13, 2025
1 parent 32f785c commit 626f251
Show file tree
Hide file tree
Showing 13 changed files with 394 additions and 23 deletions.
26 changes: 26 additions & 0 deletions docs/reference/generated/toolbar-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "ToolbarInput",
"description": "",
"props": {
"focusableWhenDisabled": {
"type": "boolean",
"default": "true",
"description": "When `true` the item remains focuseable when disabled."
},
"disabled": {
"type": "boolean",
"default": "false",
"description": "When `true` the item is disabled."
},
"className": {
"type": "string | (state) => string",
"description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state."
},
"render": {
"type": "React.ReactElement | (props, state) => React.ReactElement",
"description": "Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
}
},
"dataAttributes": {},
"cssVariables": {}
}
50 changes: 50 additions & 0 deletions docs/src/app/(private)/experiments/toolbar/_icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,53 @@ export function MousePointerIcon(props: React.ComponentProps<'svg'>) {
</svg>
);
}

export function CursorGrowIcon(props: React.ComponentProps<'svg'>) {
return (
<svg
width="26"
height="14"
viewBox="0 0 24 14"
fill="black"
stroke="white"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z" />
</svg>
);
}

export function PlusIcon(props: React.ComponentProps<'svg'>) {
return (
<svg
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
stroke="currentcolor"
strokeWidth="1.6"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M0 5H5M10 5H5M5 5V0M5 5V10" />
</svg>
);
}

export function MinusIcon(props: React.ComponentProps<'svg'>) {
return (
<svg
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
stroke="currentcolor"
strokeWidth="1.6"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M0 5H10" />
</svg>
);
}
21 changes: 11 additions & 10 deletions docs/src/app/(private)/experiments/toolbar/basic.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
'use client';
import * as React from 'react';
import { Toolbar } from '@base-ui-components/react/toolbar';
import s from './toolbar.module.css';
import selectClasses from '../../../(public)/(content)/react/components/select/demos/hero/css-modules/index.module.css';
import menuClasses from '../../../(public)/(content)/react/components/menu/demos/hero/css-modules/index.module.css';
import toolbarClasses from './toolbar.module.css';
import inputClasses from '../../../(public)/(content)/react/components/input/demos/hero/css-modules/index.module.css';
import '../../../../demo-theme.css';

const DISABLED = false;

const styles = {
toolbar: s,
select: selectClasses,
menu: menuClasses,
toolbar: toolbarClasses,
input: inputClasses,
};

const TEXT = `Shows the basic anatomy:
- Toolbar.Root
- Toolbar.Button
- Toolbar.Link
- Toolbar.Input
- Toolbar.Separator
- Toolbar.Group
`;
Expand All @@ -40,15 +39,15 @@ export default function App() {
className={styles.toolbar.Button}
onClick={() => console.log('clicked a regular toolbar button')}
>
Toolbar.Button
Button 1
</Toolbar.Button>

<Toolbar.Link
className={styles.toolbar.Button}
href="https://base-ui.com"
target="_blank"
>
Visit base-ui.com
Link
</Toolbar.Link>

<Toolbar.Separator className={styles.toolbar.Separator} />
Expand All @@ -60,17 +59,19 @@ export default function App() {
onClick={() => console.log('clicked button 1 inside a group')}
style={{ marginRight: '0.5rem' }}
>
Toolbar.Button in a Group
Grouped Button 1
</Toolbar.Button>

<Toolbar.Button
disabled={DISABLED}
className={styles.toolbar.Button}
onClick={() => console.log('clicked button 2 inside a group')}
>
Toolbar.Button in a Group
Grouped Button 2
</Toolbar.Button>
</Toolbar.Group>

<Toolbar.Input className={styles.input.Input} defaultValue="A textbox" />
</Toolbar.Root>
<textarea
className={styles.toolbar.Textarea}
Expand Down
68 changes: 66 additions & 2 deletions docs/src/app/(private)/experiments/toolbar/text-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Tooltip } from '@base-ui-components/react/tooltip';
import { Toggle } from '@base-ui-components/react/toggle';
import { ToggleGroup } from '@base-ui-components/react/toggle-group';
import { Select } from '@base-ui-components/react/select';
import { NumberField } from '@base-ui-components/react/number-field';
import { Menu } from '@base-ui-components/react/menu';
import {
SettingsMetadata,
Expand All @@ -14,6 +15,7 @@ import toolbarClasses from './toolbar.module.css';
import selectClasses from '../../../(public)/(content)/react/components/select/demos/hero/css-modules/index.module.css';
import tooltipClasses from '../../../(public)/(content)/react/components/tooltip/demos/hero/css-modules/index.module.css';
import menuClasses from '../../../(public)/(content)/react/components/menu/demos/submenu/css-modules/index.module.css';
import numberFieldClasses from '../../../(public)/(content)/react/components/number-field/demos/hero/css-modules/index.module.css';
import '../../../../demo-theme.css';
import {
BoldIcon,
Expand All @@ -27,6 +29,9 @@ import {
CheckIcon,
MoreHorizontalIcon,
ChevronRightIcon,
CursorGrowIcon,
MinusIcon,
PlusIcon,
} from './_icons';

interface Settings extends Record<string, boolean> {}
Expand Down Expand Up @@ -62,15 +67,16 @@ export const settingsMetadata: SettingsMetadata<Settings> = {

const TEXT = `This demo uses the render prop to render Toolbar parts as other things:
- Toolbar.Button renders Toggles, Select.Trigger, Menu.Trigger
- Toolbar.Group is used to render ToggleGroup
- The toggle buttons stack the render prop multiple times: Tooltip.Trigger > Toolbar.Button > Toggle
- Toolbar.Input renders NumberField.Input
- The render prop is stacked multiple times e.g. Tooltip.Trigger > Toolbar.Button > Toggle
`;

const styles = {
toolbar: toolbarClasses,
tooltip: tooltipClasses,
select: selectClasses,
menu: menuClasses,
numField: numberFieldClasses,
};

function classNames(...c: Array<string | undefined | null | false>) {
Expand Down Expand Up @@ -195,6 +201,64 @@ export default function App() {
</Select.Portal>
</Select.Root>

<Tooltip.Root>
<Tooltip.Trigger
render={
<NumberField.Root
defaultValue={16}
max={256}
min={1}
className={styles.numField.Field}
>
<NumberField.ScrubArea className={styles.numField.ScrubArea}>
<NumberField.ScrubAreaCursor
className={styles.numField.ScrubAreaCursor}
>
<CursorGrowIcon />
</NumberField.ScrubAreaCursor>
</NumberField.ScrubArea>

<NumberField.Group
className={classNames(
styles.toolbar.NumberFieldGroup,
styles.numField.Group,
)}
>
<NumberField.Decrement className={styles.numField.Decrement}>
<MinusIcon />
</NumberField.Decrement>

<Toolbar.Input
className={styles.toolbar.Input}
render={<NumberField.Input />}
aria-label="Font size"
disabled={settings.toolbarDisabled}
/>

<NumberField.Increment className={styles.numField.Increment}>
<PlusIcon />
</NumberField.Increment>
</NumberField.Group>
</NumberField.Root>
}
/>
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={10}>
<Tooltip.Popup className={styles.tooltip.Popup}>
<Tooltip.Arrow
className={classNames(
styles.tooltip.Arrow,
styles.toolbar.TooltipArrow,
)}
>
<ArrowSvg className={styles.toolbar.ArrowSvg} />
</Tooltip.Arrow>
Font size
</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</Tooltip.Root>

<Toolbar.Separator className={styles.toolbar.Separator} />

<ToggleGroup
Expand Down
57 changes: 56 additions & 1 deletion docs/src/app/(private)/experiments/toolbar/toolbar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

/* disabled buttons + focusableWhenDisabled */
.Root [type='button'][data-disabled],
.Root input[data-disabled],
.Root button[data-disabled] {
color: color-mix(in srgb, currentColor 30%, transparent) !important;
cursor: not-allowed !important;
Expand Down Expand Up @@ -191,20 +192,74 @@
}
}

/* Select */
/* ArrowOuterStroke */
.ArrowSvg > path:nth-child(2) {
@media (prefers-color-scheme: light) {
fill: var(--color-gray-200);
}
}

/* Select */
/* ArrowInnerStroke */
.ArrowSvg > path:nth-child(3) {
@media (prefers-color-scheme: dark) {
fill: var(--color-gray-300);
}
}

/* NumberField */
.NumberFieldGroup {
--corner-radius: 0.375rem;
--border-radius-right: 0 var(--corner-radius) var(--corner-radius) 0;
--border-radius-left: var(--corner-radius) 0 0 var(--corner-radius);

& button:first-of-type {
border-radius: var(--border-radius-left);
/* border-inline-end-color: transparent;*/
}

& button:last-of-type {
border-radius: var(--border-radius-right);
/* border-inline-start-color: transparent;*/
}

& button:first-of-type:dir(rtl) {
border-radius: var(--border-radius-right);
}

& button:last-of-type:dir(rtl) {
border-radius: var(--border-radius-left);
}
}

.Input {
box-sizing: border-box;
margin: 0;
padding: 0;
border-top: 1px solid var(--color-gray-200);
border-bottom: 1px solid var(--color-gray-200);
border-left: none;
border-right: none;
width: 3rem;
height: 2.5rem;
font-family: inherit;
font-size: 1rem;
font-weight: normal;
background-color: transparent;
color: var(--color-gray-900);

text-align: center;
font-variant-numeric: tabular-nums;

&:focus {
z-index: 1;
outline: 2px solid var(--color-blue);
outline-offset: -1px;
}
}

/* Misc stuff */
.Textarea {
box-sizing: border-box;
padding: 0.875rem;
Expand All @@ -225,7 +280,7 @@
}

.Wrapper {
width: 50dvw;
min-width: 50dvw;
display: flex;
flex-direction: column;
gap: 1rem;
Expand Down
4 changes: 2 additions & 2 deletions docs/src/app/(private)/experiments/toolbar/triggers.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';
import * as React from 'react';
import { Toolbar } from '@base-ui-components/react/toolbar';
import { Toggle } from '@base-ui-components/react/toggle';
// import { Toggle } from '@base-ui-components/react/toggle';
import { Switch } from '@base-ui-components/react/switch';
import { Dialog } from '@base-ui-components/react/dialog';
import toolbarClasses from './toolbar.module.css';
import triggerToolbarClasses from './triggers.module.css';
import switchClasses from '../../../(public)/(content)/react/components/switch/demos/hero/css-modules/index.module.css';
import dialogClasses from '../../../(public)/(content)/react/components/alert-dialog/demos/hero/css-modules/index.module.css';
import popoverClasses from '../../../(public)/(content)/react/components/popover/demos/hero/css-modules/index.module.css';
// import popoverClasses from '../../../(public)/(content)/react/components/popover/demos/hero/css-modules/index.module.css';
import { MessageCircleIcon } from './_icons';
import {
SettingsMetadata,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<Subtitle>A container for grouping a set of buttons and controls.</Subtitle>
<Meta
name="description"
content="A high-quality, unstyled React toolbar component that grouping a set of buttons and controls."
content="A high-quality, unstyled React toolbar component that groups a set of buttons and controls."
/>

## API reference
Expand All @@ -21,7 +21,8 @@ import { Toolbar } from '@base-ui-components/react/toolbar';
<Toolbar.Button />
<Toolbar.Button />
<Toolbar.Group />
<Toolbar.Input />
</Toolbar.Root>;
```

<Reference component="Toolbar" parts="Root, Button, Link, Group, Separator" />
<Reference component="Toolbar" parts="Root, Button, Link, Input, Group, Separator" />
Loading

0 comments on commit 626f251

Please sign in to comment.