Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[meter] Create Meter component #743

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/data/api/meter-indicator.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"props": {
"className": { "type": { "name": "union", "description": "func<br>&#124;&nbsp;string" } },
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } }
},
"name": "MeterIndicator",
"imports": [
"import { Meter } from '@base-ui-components/react/meter';\nconst MeterIndicator = Meter.Indicator;"
],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "MeterIndicator",
"forwardsRefTo": "HTMLSpanElement",
"filename": "/packages/react/src/meter/indicator/MeterIndicator.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/react-meter/\">Meter</a></li></ul>",
"cssComponent": false
}
36 changes: 36 additions & 0 deletions docs/data/api/meter-root.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"props": {
"value": { "type": { "name": "number" }, "required": true },
"aria-label": { "type": { "name": "string" } },
"aria-labelledby": { "type": { "name": "string" } },
"aria-valuetext": { "type": { "name": "string" } },
"className": { "type": { "name": "union", "description": "func<br>&#124;&nbsp;string" } },
"getAriaLabel": {
"type": { "name": "func" },
"signature": { "type": "function(value: number) => string", "describedArgs": ["value"] }
},
"getAriaValueText": {
"type": { "name": "func" },
"signature": { "type": "function(value: number) => string", "describedArgs": ["value"] }
},
"high": { "type": { "name": "number" }, "default": "100" },
"low": { "type": { "name": "number" }, "default": "0" },
"max": { "type": { "name": "number" }, "default": "100" },
"min": { "type": { "name": "number" }, "default": "0" },
"optimum": { "type": { "name": "number" }, "default": "50" },
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } }
},
"name": "MeterRoot",
"imports": [
"import { Meter } from '@base-ui-components/react/meter';\nconst MeterRoot = Meter.Root;"
],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "MeterRoot",
"forwardsRefTo": "HTMLDivElement",
"filename": "/packages/react/src/meter/root/MeterRoot.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/react-meter/\">Meter</a></li></ul>",
"cssComponent": false
}
19 changes: 19 additions & 0 deletions docs/data/api/meter-track.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"props": {
"className": { "type": { "name": "union", "description": "func<br>&#124;&nbsp;string" } },
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } }
},
"name": "MeterTrack",
"imports": [
"import { Meter } from '@base-ui-components/react/meter';\nconst MeterTrack = Meter.Track;"
],
"classes": [],
"spread": true,
"themeDefaultProps": true,
"muiName": "MeterTrack",
"forwardsRefTo": "HTMLSpanElement",
"filename": "/packages/react/src/meter/track/MeterTrack.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/react-meter/\">Meter</a></li></ul>",
"cssComponent": false
}
29 changes: 29 additions & 0 deletions docs/data/components/meter/MeterIntroduction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';
import * as React from 'react';
import { Meter } from '@base-ui-components/react/meter';
import classes from './styles.module.css';

export default function MeterIntroduction() {
return (
<div className={classes.demo}>
<Meter.Root className={classes.meter} value={60} aria-label="Battery Life">
<Meter.Track className={classes.track}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
className={classes.icon}
>
<path
d="M10.67 21c-.35 0-.62-.31-.57-.66L11 14H7.5c-.88 0-.33-.75-.31-.78 1.26-2.23 3.15-5.53 5.65-9.93.1-.18.3-.29.5-.29.35 0 .62.31.57.66l-.9 6.34h3.51c.4 0 .62.19.4.66-3.29 5.74-5.2 9.09-5.75 10.05-.1.18-.29.29-.5.29z"
fill="currentColor"
/>
</svg>
<Meter.Indicator className={classes.indicator} />
</Meter.Track>
</Meter.Root>
</div>
);
}
29 changes: 29 additions & 0 deletions docs/data/components/meter/MeterIntroduction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';
import * as React from 'react';
import { Meter } from '@base-ui-components/react/meter';
import classes from './styles.module.css';

export default function MeterIntroduction() {
return (
<div className={classes.demo}>
<Meter.Root className={classes.meter} value={60} aria-label="Battery Life">
<Meter.Track className={classes.track}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
className={classes.icon}
>
<path
d="M10.67 21c-.35 0-.62-.31-.57-.66L11 14H7.5c-.88 0-.33-.75-.31-.78 1.26-2.23 3.15-5.53 5.65-9.93.1-.18.3-.29.5-.29.35 0 .62.31.57.66l-.9 6.34h3.51c.4 0 .62.19.4.66-3.29 5.74-5.2 9.09-5.75 10.05-.1.18-.29.29-.5.29z"
fill="currentColor"
/>
</svg>
<Meter.Indicator className={classes.indicator} />
</Meter.Track>
</Meter.Root>
</div>
);
}
107 changes: 107 additions & 0 deletions docs/data/components/meter/meter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
productId: base-ui
title: React Meter components
description: The Meter component provides a graphical display of a numeric value within a defined range
components: MeterRoot, MeterTrack, MeterIndicator
hooks: useMeterRoot, useMeterIndicator
githubLabel: 'component: meter'
waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/meter/
packageName: '@base-ui-components/react'
---

# Meter

<Description />

<ComponentLinkHeader design={false} />

<Demo demo="MeterIntroduction" defaultCodeOpen="false" bg="gradient" />

## Installation

<InstallationInstructions componentName="Meter" />

## Anatomy

Meter

- `<Meter.Root />` is a top-level component that wraps the other components.
- `<Meter.Track />` renders the rail that represents the full range of possible values.
- `<Meter.Indicator />` renders the filled portion of the track.

```tsx
<Meter.Root>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>
```

## Value

The `value` prop represents the (percentage) value of the Meter component.

```tsx
<Meter.Root value={myValue}>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>
```

## Min/max and high/low

The `min` and `max` props can be used to establish the lower and upper bound of the range. The default minimum and maximum values are `0` and `100`.

```tsx
<Meter.Root min={0} max={10}>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>
```

The `high` and `low` props can be used together with `min` and `max` to divide the range into 3 segments: `'low'`, `'medium'`, and `'high'`.
A `[data-segment='low' | 'medium' | 'high']` attribute is set depending on which segment the value lands on.

```tsx
<Meter.Root min={0} max={100} low={20} high={80}>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>
```

## Optimum value

The optimum prop defines whether the low, medium, or high segment of the range is "preferable". For example, for "battery health" higher is better, but for "CPU temperature" lower may be better. A `[data-optimum]` attribute is set when the value is in the "preferable" segment.

```tsx
<Meter.Root min={0} max={100} low={20} high={75} optimum={80}>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>
```

## RTL

Place the component inside any HTML element or component with the HTML dir attribute to change the direction that the `Indicator` fills towards for right-to-left languages:

```jsx
<html dir="rtl">
<body>
<Meter.Root>{/* Subcomponents */}</Meter.Root>
</body>
</html>
```

## Overriding default components

Use the `render` prop to override the rendered element for all subcomponents:

```jsx
<Meter.Indicator render={<MyCustomIndicator />} />
// or
<Meter.Indicator render={(props) => <MyCustomIndicator {...props} />} />
```
49 changes: 49 additions & 0 deletions docs/data/components/meter/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.demo {
--icon-size: 46px;
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
padding: 1rem;
}

.meter {
display: flex;
flex-flow: column nowrap;
gap: 1rem;
color: var(--gray-text-2);
}

.track {
position: relative;
width: 48px;
height: 26px;
border-radius: 5px;
border: 3px solid currentColor;
padding: 2px;
display: flex;
}

.track:after {
content: '';
background-color: currentColor;
position: absolute;
z-index: 1;
top: 3px;
right: -6px;
width: 3px;
height: 14px;
border-radius: 0 6px 6px 0;
}

.icon {
position: absolute;
width: var(--icon-size);
height: var(--icon-size);
transform: translate(-3px, -14px);
}

.indicator {
background-color: rgb(40, 205, 65);
border-radius: 3px;
}
1 change: 1 addition & 0 deletions docs/data/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const pages: readonly RouteMetadata[] = [
{ pathname: '/components/react-fieldset', title: 'Fieldset' },
{ pathname: '/components/react-form', title: 'Form' },
{ pathname: '/components/react-menu', title: 'Menu' },
{ pathname: '/components/react-meter', title: 'Meter' },
{ pathname: '/components/react-number-field', title: 'Number Field' },
{ pathname: '/components/react-popover', title: 'Popover' },
{ pathname: '/components/react-preview-card', title: 'Preview Card' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"componentDescription": "",
"propDescriptions": {
"className": {
"description": "Class names applied to the element or a function that returns them based on the component&#39;s state."
},
"render": { "description": "A function to customize rendering of the component." }
},
"classDescriptions": {}
}
37 changes: 37 additions & 0 deletions docs/data/translations/api-docs/meter-root/meter-root.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"componentDescription": "",
"propDescriptions": {
"aria-label": { "description": "The label for the Indicator component." },
"aria-labelledby": {
"description": "An id or space-separated list of ids of elements that label the Indicator component."
},
"aria-valuetext": {
"description": "A string value that provides a human-readable text alternative for the current value of the meter indicator."
},
"className": {
"description": "Class names applied to the element or a function that returns them based on the component&#39;s state."
},
"getAriaLabel": {
"description": "Accepts a function which returns a string value that provides an accessible name for the Indicator component",
"typeDescriptions": { "value": "The component&#39;s value" }
},
"getAriaValueText": {
"description": "Accepts a function which returns a string value that provides a human-readable text alternative for the current value of the meter indicator.",
"typeDescriptions": { "value": "The component&#39;s value to format" }
},
"high": {
"description": "Sets the lower boundary of the high end of the numeric range represented by the component. If unspecified, or greater than <code>max</code>, it will fall back to <code>max</code>."
},
"low": {
"description": "Sets the upper boundary of the low end of the numeric range represented by the component. If unspecified, or less than <code>min</code>, it will fall back to <code>min</code>."
},
"max": { "description": "The maximum value" },
"min": { "description": "The minimum value" },
"optimum": {
"description": "Indicates the optimal point in the numeric range represented by the component. If unspecified, it will fall back to the midpoint between <code>min</code> and <code>max</code>."
},
"render": { "description": "A function to customize rendering of the component." },
"value": { "description": "The current value." }
},
"classDescriptions": {}
}
10 changes: 10 additions & 0 deletions docs/data/translations/api-docs/meter-track/meter-track.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"componentDescription": "",
"propDescriptions": {
"className": {
"description": "Class names applied to the element or a function that returns them based on the component&#39;s state."
},
"render": { "description": "A function to customize rendering of the component." }
},
"classDescriptions": {}
}
14 changes: 14 additions & 0 deletions docs/reference/generated/meter-indicator.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "MeterIndicator",
"description": "",
"props": {
"className": {
"type": "string | (state) => string",
"description": "Class names applied to the element or a function that returns them based on the component's state."
},
"render": {
"type": "React.ReactElement | (props, state) => React.ReactElement",
"description": "A function to customize rendering of the component."
}
}
}
Loading