Skip to content

Commit 626f251

Browse files
committed
Add ToolbarInput
1 parent 32f785c commit 626f251

File tree

13 files changed

+394
-23
lines changed

13 files changed

+394
-23
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "ToolbarInput",
3+
"description": "",
4+
"props": {
5+
"focusableWhenDisabled": {
6+
"type": "boolean",
7+
"default": "true",
8+
"description": "When `true` the item remains focuseable when disabled."
9+
},
10+
"disabled": {
11+
"type": "boolean",
12+
"default": "false",
13+
"description": "When `true` the item is disabled."
14+
},
15+
"className": {
16+
"type": "string | (state) => string",
17+
"description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state."
18+
},
19+
"render": {
20+
"type": "React.ReactElement | (props, state) => React.ReactElement",
21+
"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."
22+
}
23+
},
24+
"dataAttributes": {},
25+
"cssVariables": {}
26+
}

docs/src/app/(private)/experiments/toolbar/_icons.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,53 @@ export function MousePointerIcon(props: React.ComponentProps<'svg'>) {
200200
</svg>
201201
);
202202
}
203+
204+
export function CursorGrowIcon(props: React.ComponentProps<'svg'>) {
205+
return (
206+
<svg
207+
width="26"
208+
height="14"
209+
viewBox="0 0 24 14"
210+
fill="black"
211+
stroke="white"
212+
xmlns="http://www.w3.org/2000/svg"
213+
{...props}
214+
>
215+
<path d="M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z" />
216+
</svg>
217+
);
218+
}
219+
220+
export function PlusIcon(props: React.ComponentProps<'svg'>) {
221+
return (
222+
<svg
223+
width="10"
224+
height="10"
225+
viewBox="0 0 10 10"
226+
fill="none"
227+
stroke="currentcolor"
228+
strokeWidth="1.6"
229+
xmlns="http://www.w3.org/2000/svg"
230+
{...props}
231+
>
232+
<path d="M0 5H5M10 5H5M5 5V0M5 5V10" />
233+
</svg>
234+
);
235+
}
236+
237+
export function MinusIcon(props: React.ComponentProps<'svg'>) {
238+
return (
239+
<svg
240+
width="10"
241+
height="10"
242+
viewBox="0 0 10 10"
243+
fill="none"
244+
stroke="currentcolor"
245+
strokeWidth="1.6"
246+
xmlns="http://www.w3.org/2000/svg"
247+
{...props}
248+
>
249+
<path d="M0 5H10" />
250+
</svg>
251+
);
252+
}

docs/src/app/(private)/experiments/toolbar/basic.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
'use client';
22
import * as React from 'react';
33
import { Toolbar } from '@base-ui-components/react/toolbar';
4-
import s from './toolbar.module.css';
5-
import selectClasses from '../../../(public)/(content)/react/components/select/demos/hero/css-modules/index.module.css';
6-
import menuClasses from '../../../(public)/(content)/react/components/menu/demos/hero/css-modules/index.module.css';
4+
import toolbarClasses from './toolbar.module.css';
5+
import inputClasses from '../../../(public)/(content)/react/components/input/demos/hero/css-modules/index.module.css';
76
import '../../../../demo-theme.css';
87

98
const DISABLED = false;
109

1110
const styles = {
12-
toolbar: s,
13-
select: selectClasses,
14-
menu: menuClasses,
11+
toolbar: toolbarClasses,
12+
input: inputClasses,
1513
};
1614

1715
const TEXT = `Shows the basic anatomy:
1816
- Toolbar.Root
1917
- Toolbar.Button
2018
- Toolbar.Link
19+
- Toolbar.Input
2120
- Toolbar.Separator
2221
- Toolbar.Group
2322
`;
@@ -40,15 +39,15 @@ export default function App() {
4039
className={styles.toolbar.Button}
4140
onClick={() => console.log('clicked a regular toolbar button')}
4241
>
43-
Toolbar.Button
42+
Button 1
4443
</Toolbar.Button>
4544

4645
<Toolbar.Link
4746
className={styles.toolbar.Button}
4847
href="https://base-ui.com"
4948
target="_blank"
5049
>
51-
Visit base-ui.com
50+
Link
5251
</Toolbar.Link>
5352

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

6665
<Toolbar.Button
6766
disabled={DISABLED}
6867
className={styles.toolbar.Button}
6968
onClick={() => console.log('clicked button 2 inside a group')}
7069
>
71-
Toolbar.Button in a Group
70+
Grouped Button 2
7271
</Toolbar.Button>
7372
</Toolbar.Group>
73+
74+
<Toolbar.Input className={styles.input.Input} defaultValue="A textbox" />
7475
</Toolbar.Root>
7576
<textarea
7677
className={styles.toolbar.Textarea}

docs/src/app/(private)/experiments/toolbar/text-editor.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Tooltip } from '@base-ui-components/react/tooltip';
55
import { Toggle } from '@base-ui-components/react/toggle';
66
import { ToggleGroup } from '@base-ui-components/react/toggle-group';
77
import { Select } from '@base-ui-components/react/select';
8+
import { NumberField } from '@base-ui-components/react/number-field';
89
import { Menu } from '@base-ui-components/react/menu';
910
import {
1011
SettingsMetadata,
@@ -14,6 +15,7 @@ import toolbarClasses from './toolbar.module.css';
1415
import selectClasses from '../../../(public)/(content)/react/components/select/demos/hero/css-modules/index.module.css';
1516
import tooltipClasses from '../../../(public)/(content)/react/components/tooltip/demos/hero/css-modules/index.module.css';
1617
import menuClasses from '../../../(public)/(content)/react/components/menu/demos/submenu/css-modules/index.module.css';
18+
import numberFieldClasses from '../../../(public)/(content)/react/components/number-field/demos/hero/css-modules/index.module.css';
1719
import '../../../../demo-theme.css';
1820
import {
1921
BoldIcon,
@@ -27,6 +29,9 @@ import {
2729
CheckIcon,
2830
MoreHorizontalIcon,
2931
ChevronRightIcon,
32+
CursorGrowIcon,
33+
MinusIcon,
34+
PlusIcon,
3035
} from './_icons';
3136

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

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

6974
const styles = {
7075
toolbar: toolbarClasses,
7176
tooltip: tooltipClasses,
7277
select: selectClasses,
7378
menu: menuClasses,
79+
numField: numberFieldClasses,
7480
};
7581

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

204+
<Tooltip.Root>
205+
<Tooltip.Trigger
206+
render={
207+
<NumberField.Root
208+
defaultValue={16}
209+
max={256}
210+
min={1}
211+
className={styles.numField.Field}
212+
>
213+
<NumberField.ScrubArea className={styles.numField.ScrubArea}>
214+
<NumberField.ScrubAreaCursor
215+
className={styles.numField.ScrubAreaCursor}
216+
>
217+
<CursorGrowIcon />
218+
</NumberField.ScrubAreaCursor>
219+
</NumberField.ScrubArea>
220+
221+
<NumberField.Group
222+
className={classNames(
223+
styles.toolbar.NumberFieldGroup,
224+
styles.numField.Group,
225+
)}
226+
>
227+
<NumberField.Decrement className={styles.numField.Decrement}>
228+
<MinusIcon />
229+
</NumberField.Decrement>
230+
231+
<Toolbar.Input
232+
className={styles.toolbar.Input}
233+
render={<NumberField.Input />}
234+
aria-label="Font size"
235+
disabled={settings.toolbarDisabled}
236+
/>
237+
238+
<NumberField.Increment className={styles.numField.Increment}>
239+
<PlusIcon />
240+
</NumberField.Increment>
241+
</NumberField.Group>
242+
</NumberField.Root>
243+
}
244+
/>
245+
<Tooltip.Portal>
246+
<Tooltip.Positioner sideOffset={10}>
247+
<Tooltip.Popup className={styles.tooltip.Popup}>
248+
<Tooltip.Arrow
249+
className={classNames(
250+
styles.tooltip.Arrow,
251+
styles.toolbar.TooltipArrow,
252+
)}
253+
>
254+
<ArrowSvg className={styles.toolbar.ArrowSvg} />
255+
</Tooltip.Arrow>
256+
Font size
257+
</Tooltip.Popup>
258+
</Tooltip.Positioner>
259+
</Tooltip.Portal>
260+
</Tooltip.Root>
261+
198262
<Toolbar.Separator className={styles.toolbar.Separator} />
199263

200264
<ToggleGroup

docs/src/app/(private)/experiments/toolbar/toolbar.module.css

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
/* disabled buttons + focusableWhenDisabled */
1919
.Root [type='button'][data-disabled],
20+
.Root input[data-disabled],
2021
.Root button[data-disabled] {
2122
color: color-mix(in srgb, currentColor 30%, transparent) !important;
2223
cursor: not-allowed !important;
@@ -191,20 +192,74 @@
191192
}
192193
}
193194

195+
/* Select */
194196
/* ArrowOuterStroke */
195197
.ArrowSvg > path:nth-child(2) {
196198
@media (prefers-color-scheme: light) {
197199
fill: var(--color-gray-200);
198200
}
199201
}
200202

203+
/* Select */
201204
/* ArrowInnerStroke */
202205
.ArrowSvg > path:nth-child(3) {
203206
@media (prefers-color-scheme: dark) {
204207
fill: var(--color-gray-300);
205208
}
206209
}
207210

211+
/* NumberField */
212+
.NumberFieldGroup {
213+
--corner-radius: 0.375rem;
214+
--border-radius-right: 0 var(--corner-radius) var(--corner-radius) 0;
215+
--border-radius-left: var(--corner-radius) 0 0 var(--corner-radius);
216+
217+
& button:first-of-type {
218+
border-radius: var(--border-radius-left);
219+
/* border-inline-end-color: transparent;*/
220+
}
221+
222+
& button:last-of-type {
223+
border-radius: var(--border-radius-right);
224+
/* border-inline-start-color: transparent;*/
225+
}
226+
227+
& button:first-of-type:dir(rtl) {
228+
border-radius: var(--border-radius-right);
229+
}
230+
231+
& button:last-of-type:dir(rtl) {
232+
border-radius: var(--border-radius-left);
233+
}
234+
}
235+
236+
.Input {
237+
box-sizing: border-box;
238+
margin: 0;
239+
padding: 0;
240+
border-top: 1px solid var(--color-gray-200);
241+
border-bottom: 1px solid var(--color-gray-200);
242+
border-left: none;
243+
border-right: none;
244+
width: 3rem;
245+
height: 2.5rem;
246+
font-family: inherit;
247+
font-size: 1rem;
248+
font-weight: normal;
249+
background-color: transparent;
250+
color: var(--color-gray-900);
251+
252+
text-align: center;
253+
font-variant-numeric: tabular-nums;
254+
255+
&:focus {
256+
z-index: 1;
257+
outline: 2px solid var(--color-blue);
258+
outline-offset: -1px;
259+
}
260+
}
261+
262+
/* Misc stuff */
208263
.Textarea {
209264
box-sizing: border-box;
210265
padding: 0.875rem;
@@ -225,7 +280,7 @@
225280
}
226281

227282
.Wrapper {
228-
width: 50dvw;
283+
min-width: 50dvw;
229284
display: flex;
230285
flex-direction: column;
231286
gap: 1rem;

docs/src/app/(private)/experiments/toolbar/triggers.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
'use client';
22
import * as React from 'react';
33
import { Toolbar } from '@base-ui-components/react/toolbar';
4-
import { Toggle } from '@base-ui-components/react/toggle';
4+
// import { Toggle } from '@base-ui-components/react/toggle';
55
import { Switch } from '@base-ui-components/react/switch';
66
import { Dialog } from '@base-ui-components/react/dialog';
77
import toolbarClasses from './toolbar.module.css';
88
import triggerToolbarClasses from './triggers.module.css';
99
import switchClasses from '../../../(public)/(content)/react/components/switch/demos/hero/css-modules/index.module.css';
1010
import dialogClasses from '../../../(public)/(content)/react/components/alert-dialog/demos/hero/css-modules/index.module.css';
11-
import popoverClasses from '../../../(public)/(content)/react/components/popover/demos/hero/css-modules/index.module.css';
11+
// import popoverClasses from '../../../(public)/(content)/react/components/popover/demos/hero/css-modules/index.module.css';
1212
import { MessageCircleIcon } from './_icons';
1313
import {
1414
SettingsMetadata,

docs/src/app/(public)/(content)/react/components/toolbar/page.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Subtitle>A container for grouping a set of buttons and controls.</Subtitle>
44
<Meta
55
name="description"
6-
content="A high-quality, unstyled React toolbar component that grouping a set of buttons and controls."
6+
content="A high-quality, unstyled React toolbar component that groups a set of buttons and controls."
77
/>
88

99
## API reference
@@ -21,7 +21,8 @@ import { Toolbar } from '@base-ui-components/react/toolbar';
2121
<Toolbar.Button />
2222
<Toolbar.Button />
2323
<Toolbar.Group />
24+
<Toolbar.Input />
2425
</Toolbar.Root>;
2526
```
2627

27-
<Reference component="Toolbar" parts="Root, Button, Link, Group, Separator" />
28+
<Reference component="Toolbar" parts="Root, Button, Link, Input, Group, Separator" />

0 commit comments

Comments
 (0)