7.4.0
mantinedev/mantine7.4.0Jan 3, 2024by rtivital
AI Summary
Major release featuring the new @mantine/charts package and support for OKLCH color model.
Key Highlights
- New @mantine/charts package with AreaChart, LineChart, BarChart, and Sparkline
- Support for OKLCH colors in theme.colors
- New theme.autoContrast property for automatic text color adjustment
New Features
- New @mantine/charts package
- OKLCH color support
- autoContrast theme property
Full Release Notes
[View changelog with demos on mantine.dev website](https://mantine.dev/changelog/7-4-0)
## @mantine/charts
New [@mantine/charts](https://mantine.dev/charts/getting-started/) package provides a set of components
to build charts and graphs. All components are based on [recharts](https://recharts.org/en-US/).
Currently, the package provides [AreaChart](https://mantine.dev/charts/area-chart), [BarChart](https://mantine.dev/charts/bar-chart),
[LineChart](https://mantine.dev/charts/line-chart) and [Sparkline](https://mantine.dev/charts/sparkline) components.
More components will be added in the next minor releases.
## AreaChart component
New [AreaChart](https://mantine.dev/charts/area-chart) component:
```tsx
import { AreaChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<AreaChart
h={300}
data={data}
dataKey="date"
type="stacked"
series={[
{ name: 'Apples', color: 'indigo.6' },
{ name: 'Oranges', color: 'blue.6' },
{ name: 'Tomatoes', color: 'teal.6' },
]}
/>
);
}
```
## LineChart component
New [LineChart](https://mantine.dev/charts/line-chart) component:
```tsx
import { LineChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<LineChart
h={300}
data={data}
dataKey="date"
withLegend
series={[
{ name: 'Apples', color: 'indigo.6' },
{ name: 'Oranges', color: 'blue.6' },
{ name: 'Tomatoes', color: 'teal.6' },
]}
/>
);
}
```
## BarChart component
New [BarChart](https://mantine.dev/charts/bar-chart) component:
```tsx
import { BarChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<BarChart
h={300}
data={data}
dataKey="month"
type="stacked"
orientation="vertical"
yAxisProps={{ width: 80 }}
series={[
{ name: 'Smartphones', color: 'violet.6' },
{ name: 'Laptops', color: 'blue.6' },
{ name: 'Tablets', color: 'teal.6' },
]}
/>
);
}
```
## Sparkline component
New [Sparkline](https://mantine.dev/charts/sparkline) component:
```tsx
import { Sparkline } from '@mantine/charts';
function Demo() {
return (
<Sparkline
w={200}
h={60}
data={[10, 20, 40, 20, 40, 10, 50]}
curveType="linear"
color="blue"
fillOpacity={0.6}
strokeWidth={2}
/>
);
}
```
## OKLCH colors support
You can now use [OKLCH](https://oklch.com/) colors in `theme.colors`.
OKLCH color model has [88.18% browser support](https://caniuse.com/mdn-css_types_color_oklch),
it is supported in all modern browsers. OKLCH model provides 30% more colors than HSL model and
has [several other advantages](https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl).
Example of adding OKLCH color to the theme:
```tsx
import { Button, createTheme, Group, MantineProvider } from '@mantine/core';
const theme = createTheme({
colors: {
'oklch-blue': [
'oklch(96.27% 0.0217 238.66)',
'oklch(92.66% 0.0429 240.01)',
'oklch(86.02% 0.0827 241.66)',
'oklch(78.2% 0.13 243.83)',
'oklch(71.8% 0.1686 246.06)',
'oklch(66.89% 0.1986 248.32)',
'oklch(62.59% 0.2247 250.29)',
'oklch(58.56% 0.2209 251.26)',
'oklch(54.26% 0.2067 251.67)',
'oklch(49.72% 0.1888 251.59)',
],
},
});
function Demo() {
return (
<MantineProvider theme={theme}>
<Group>
<Button color="oklch-blue">Filled</Button>
<Button color="oklch-blue" variant="outline">
Outline
</Button>
<Button color="oklch-blue" variant="light">
Light
</Button>
</Group>
</MantineProvider>
);
}
```
## autoContrast
New `theme.autoContrast` property controls whether text color should be changed based on the given `color` prop
in the following components:
- [ActionIcon](https://mantine.dev/core/action-icon) with `variant="filled"` only
- [Alert](https://mantine.dev/core/alert) with `variant="filled"` only
- [Avatar](https://mantine.dev/core/avatar) with `variant="filled"` only
- [Badge](https://mantine.dev/core/badge) with `variant="filled"` only
- [Button](https://mantine.dev/core/button) with `variant="filled"` only
- [Chip](https://mantine.dev/core/chip) with `variant="filled"` only
- [NavLink](https://mantine.dev/core/nav-link) with `variant="filled"` only
- [ThemeIcon](https://mantine.dev/core/theme-icon) with `variant="filled"` only
- [Checkbox](https://mantine.dev/core/checkbox) with `variant="filled"` only
- [Radio](https://mantine.dev/core/radio) with `variant="filled"` only
- [Tabs](https://mantine.dev/core/tabs) with `variant="pills"` only
- [SegmentedControl](https://mantine.dev/core/segmented-control)
- [Stepper](https://mantine.dev/core/stepper)
- [Pagination](https://mantine.dev/core/pagination)
- [Progress](https://mantine.dev/core/progress)
- [Indicator](https://mantine.dev/core/indicator)
- [Timeline](https://mantine.dev/core/timeline)
- [Spotlight](https://mantine.dev/x/spotlight)
- All [@mantine/dates](https://mantine.dev/dates/getting-started) components that are based on [Calendar](https://mantine.dev/dates/calendar) component
`autoContrast` can be set globally on the theme level or individually for each component via `autoContrast` prop,
except for [Spotlight](https://mantine.dev/x/spotlight) and [@mantine/dates](https://mantine.dev/dates/getting-started) components, which only support global theme setting.
```tsx
import { Button, Code, Group } from '@mantine/core';
function Demo() {
return (
<>
<Code>autoContrast: true</Code>
<Group mt="xs" mb="lg">
<Button color="lime.4" autoContrast>
Lime.4 button
</Button>
<Button color="blue.2" autoContrast>
Blue.2 button
</Button>
<Button color="orange.3" autoContrast>
Orange.3 button
</Button>
</Group>
<Code>autoContrast: false</Code>
<Group mt="xs">
<Button color="lime.4">Lime.4 button</Button>
<Button color="blue.2">Blue.2 button</Button>
<Button color="orange.3">Orange.3 button</Button>
</Group>
</>
);
}
```
`autoContrast` checks whether the given color luminosity is above or below the `luminanceThreshold` value
and changes text color to either `theme.white` or `theme.black` accordingly:
```tsx
import { Button, createTheme, MantineProvider, Stack } from '@mantine/core';
const theme = createTheme({
autoContrast: true,
luminanceThreshold: 0.3,
});
function Wrapper(props: any) {
const buttons = Array(10)
.fill(0)
.map((_, index) => (
<Button key={index} color={`blue.${index}`}>
Button
</Button>
));
return (
<MantineProvider theme={theme}>
<Stack>{buttons}</Stack>
</MantineProvider>
);
}
```
## Color functions improvements
`alpha`, `lighten` and `darken` functions now support CSS variables (with [color-mix](https://caniuse.com/mdn-css_types_color_color-mix)) and OKLCH colors.
All functions are available both in `@mantine/core` (`.ts`/`.js` files) and [postcss-preset-mantine](https://mantine.dev/styles/postcss-preset) (`.css` files, requires version 1.12.0 or higher).
In `.css` files:
```scss
.demo-alpha {
color: alpha(var(--mantine-color-red-4), 0.5);
border: 1px solid alpha(#ffc, 0.2);
}
.demo-lighten-darken {
color: lighten(var(--mantine-color-red-4), 0.5);
border: 1px solid darken(#ffc, 0.2);
}
```
Will be transformed to:
```scss
.demo-alpha {
color: color-mix(in srgb, var(--mantine-color-red-4), transparent 50%);
border: 1px solid color-mix(in srgb, #ffc, transparent 80%);
}
.demo-lighten-darken {
color: color-mix(in srgb, var(--mantine-color-red-4), white 50%);
border: 1px solid color-mix(in srgb, #ffc, black 20%);
}
```
In `.ts`/`.js` files:
```tsx
import { alpha, lighten } from '@mantine/core';
alpha('#4578FC', 0.45); // -> rgba(69, 120, 252, 0.45)
alpha('var(--mantine-color-gray-4)', 0.74);
// -> color-mix(in srgb, var(--mantine-color-gray-4), transparent 26%)
lighten('#4578FC', 0.45); // -> #a3c1ff
lighten('var(--mantine-color-gray-4)', 0.74);
// -> color-mix(in srgb, var(--mantine-color-gray-4), white 74%)
```
Note that `alpha` function is a replacement for `rgba`. It was renamed to
have a more clear meaning, as it can now be used with CSS variables and OKLCH colors.
`rgba` function is still available as an alias for `alpha` function.
## enhanceGetInputProps
`@mantine/form` now supports [enhanceGetInputProps](https://mantine.dev/form/get-input-props/#enhancegetinputprops). `enhanceGetInputProps` is a function that can be used to add additional props to the object returned by `form.getInputProps`.
You can define it in `useForm` hook options. Its argument is an object with the following properties:
- `inputProps` – object returned by `form.getInputProps` by default
- `field` – field path, first argument of `form.getInputProps`, for example `name`, `user.email`, `users.0.name`
- `options` – second argument of `form.getInputProps`, for example `{ type: 'checkbox' }`, can be used to pass additional
options to `enhanceGetInputProps` function
- `form` – form instance
Example of using `enhanceGetInputProps` to disable input based on field path:
```tsx
import { NumberInput, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
interface FormValues {
name: string;
age: number | string;
}
function Demo() {
const form = useForm<FormValues>({
initialValues: { name: '', age: '' },
enhanceGetInputProps: (payload) => ({
disabled: payload.field === 'name',
}),
});
return (
<>
<TextInput {...form.getInputProps('name')} label="Name" placeholder="Name" />
<NumberInput {...form.getInputProps('age')} label="Age" placeholder="Age" mt="md" />
</>
);
}
```
Example of using `enhanceGetInputProps` to add additional props to the input based on option passed to `form.getInputProps`:
```tsx
import { NumberInput, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
interface FormValues {
name: string;
age: number | string;
}
function Demo() {
const form = useForm<FormValues>({
initialValues: { name: '', age: '' },
enhanceGetInputProps: (payload) => {
if (payload.options.fieldType === 'name') {
return {
label: 'Your name',
placeholder: 'Your name',
withAsterisk: true,
description: 'Your personal information is stored securely. (Just kidding!)',
};
}
return {};
},
});
return (
<>
<TextInput {...form.getInputProps('name', { fieldType: 'name' })} />
<NumberInput {...form.getInputProps('age')} label="Age" placeholder="Age" mt="md" />
</>
);
}
```
## form.initialize
`@mantine/form` now supports `form.initialize` handler.
When called `form.initialize` handler sets `initialValues` and `values` to the same value
and marks form as initialized. It can be used only once, next `form.initialize` calls
are ignored.
`form.initialize` is useful when you want to sync form values with backend API response:
```tsx
import { Button, NumberInput, TextInput } from '@mantine/core';
import { isInRange, isNotEmpty, useForm } from '@mantine/form';
interface FormValues {
name: string;
age: number | string;
}
function apiRequest(): Promise<FormValues> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: 'John Doe', age: 25 });
}, 1000);
});
}
function Demo() {
const form = useForm<FormValues>({
initialValues: { name: '', age: 0 },
validate: {
name: isNotEmpty('Name is required'),
age: isInRange({ min: 18 }, 'You must be at least 18 to register'),
},
});
return (
<>
<TextInput {...form.getInputProps('name')} label="Name" placeholder="Name" />
<NumberInput {...form.getInputProps('age')} label="Age" placeholder="Age" mt="md" />
<Button onClick={() => apiRequest().then((values) => form.initialize(values))} mt="md">
Initialize form
</Button>
</>
);
}
```
Example with [TanStack Query](https://tanstack.com/query/latest) (react-query):
```tsx
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useForm } from '@mantine/form';
function Demo() {
const query = useQuery({
queryKey: ['current-user'],
queryFn: () => fetch('/api/users/me').then((res) => res.json()),
});
const form = useForm({
initialValues: {
name: '',
email: '',
},
});
useEffect(() => {
if (query.data) {
// Even if query.data changes, form will be initialized only once
form.initialize(query.data);
}
}, [query.data]);
}
```
Note that `form.initialize` will erase all values that were set before it was called.
It is usually a good idea to set `readOnly` or `disabled` on all form fields before
`form.initialize` is called to prevent data loss. You can implement this with
[enhanceGetInputProps](https://mantine.dev/form/get-input-props/#enhancegetinputprops):
```tsx
import { NumberInput, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
interface FormValues {
name: string;
age: number | string;
}
function Demo() {
const form = useForm<FormValues>({
initialValues: { name: '', age: '' },
enhanceGetInputProps: (payload) => {
if (!payload.form.initialized) {
return { disabled: true };
}
return {};
},
});
return (
<>
<TextInput {...form.getInputProps('name')} label="Your name" placeholder="Your name" />
<NumberInput {...form.getInputProps('age')} label="Age" placeholder="Age" mt="md" />
<Button onClick={() => form.initialize({ name: 'John', age: 20 })} mt="md">
Initialize form
</Button>
</>
);
}
```
## valibot form resolver
`@mantine/form` now supports [validbot schema resolver](https://www.npmjs.com/package/mantine-form-valibot-resolver):
```bash
yarn add valibot mantine-form-valibot-resolver
```
Basic fields validation:
```tsx
import { valibotResolver } from 'mantine-form-valibot-resolver';
import { email, minLength, minValue, number, object, string } from 'valibot';
import { useForm } from '@mantine/form';
const schema = object({
name: string([minLength(2, 'Name should have at least 2 letters')]),
email: string([email('Invalid email')]),
age: number([minValue(18, 'You must be at least 18 to create an account')]),
});
const form = useForm({
initialValues: {
name: '',
email: '',
age: 16,
},
validate: valibotResolver(schema),
});
form.validate();
form.errors;
// -> {
// name: 'Name should have at least 2 letters',
// email: 'Invalid email',
// age: 'You must be at least 18 to create an account'
// }
```
Nested fields validation
```tsx
import { valibotResolver } from 'mantine-form-valibot-resolver';
import { minLength, object, string } from 'valibot';
import { useForm } from '@mantine/form';
const nestedSchema = object({
nested: object({
field: string([minLength(2, 'Field should have at least 2 letters')]),
}),
});
const form = useForm({
initialValues: {
nested: {
field: '',
},
},
validate: valibotResolver(nestedSchema),
});
form.validate();
form.errors;
// -> {
// 'nested.field': 'Field should have at least 2 letters',
// }
```
List fields validation:
```tsx
import { valibotResolver } from 'mantine-form-valibot-resolver';
import { array, minLength, object, string } from 'valibot';
import { useForm } from '@mantine/form';
const listSchema = object({
list: array(
object({
name: string([minLength(2, 'Name should have at least 2 letters')]),
})
),
});
const form = useForm({
initialValues: {
list: [{ name: '' }],
},
validate: valibotResolver(listSchema),
});
form.validate();
form.errors;
// -> {
// 'list.0.name': 'Name should have at least 2 letters',
// }
```
## ScrollArea scrollbars prop
[ScrollArea](https://mantine.dev/core/scroll-area) now supports `scrollbars` prop, which allows controlling directions at which scrollbars should be rendered.
Supported values are `x`, `y` and `xy`. If `scrollbars="y"` is set, only the vertical scrollbar will be rendered, and it will not be possible to scroll horizontally:
```tsx
import { Box, ScrollArea } from '@mantine/core';
function Demo() {
return (
<ScrollArea w={300} h={200} scrollbars="y">
<Box w={600}>{/* ... content */}</Box>
</ScrollArea>
);
}
```
## Title lineClamp prop
[Title](https://mantine.dev/core/title) component now supports `lineClamp` prop, which allows truncating text after a specified number of lines:
```tsx
import { Box, Title } from '@mantine/core';
function Demo() {
return (
<Box maw={400}>
<Title order={2} lineClamp={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure doloremque quas dolorum. Quo
amet earum alias consequuntur quam accusamus a quae beatae, odio, quod provident consectetur
non repudiandae enim adipisci?
</Title>
</Box>
);
}
```
## Primary color CSS variables
CSS variables for primary color are now available, you can use the following variables in your styles:
```scss
--mantine-primary-color-0
--mantine-primary-color-1
--mantine-primary-color-2
--mantine-primary-color-3
--mantine-primary-color-4
--mantine-primary-color-5
--mantine-primary-color-6
--mantine-primary-color-7
--mantine-primary-color-8
--mantine-primary-color-9
--mantine-primary-color-contrast
--mantine-primary-color-filled
--mantine-primary-color-filled-hover
--mantine-primary-color-light
--mantine-primary-color-light-hover
--mantine-primary-color-light-color
```
## Help center
[Help center](https://help.mantine.dev/) is a new website with guides, tutorials and frequently
asked questions. Currently, it has 14 questions, more FAQs will be added in the next releases.
- [Is there DataGrid component that I can use with Mantine?](https://help.mantine.dev/q/data-grid-i-need)
- [MantineProvider was not found in component tree. What should I do?](https://help.mantine.dev/q/mantine-provider-missing)
- [Can I use Mantine components as server components?](https://help.mantine.dev/q/server-components)
- [Can I use Mantine with Create React App (CRA)?](https://help.mantine.dev/q/can-i-use-mantine-with-cra)
- [How can I lint CSS files?](https://help.mantine.dev/q/how-to-setup-stylelint)
- [How to update Mantine dependencies?](https://help.mantine.dev/q/how-to-update-dependencies)
- [How can I add hover styles to an element?](https://help.mantine.dev/q/how-to-add-hover-styles)
- [How can I get current color scheme value in JavaScript?](https://help.mantine.dev/q/how-to-get-color-scheme-value-in-js)
- [Can I use private CSS variables to style components?](https://help.mantine.dev/q/private-css-variables)
- [How can I disable all inputs/inputs group inside form?](https://help.mantine.dev/q/disable-all-inputs-in-form)
- [How to use Dropzone with @mantine/form?](https://help.mantine.dev/q/how-to-use-dropzone-with-form)
- [How to call a function when Modal/Drawer closes and animation completes?](https://help.mantine.dev/q/how-to-call-function-when-modal-closes)
- [How to prevent Modal from closing?](https://help.mantine.dev/q/how-to-prevent-modal-from-closing)
- [What is the difference between searchable Select and Autocomplete?](https://help.mantine.dev/q/select-autocomplete-difference)
## Documentation updates
- [form.getInputProps](https://mantine.dev/form/get-input-props) guide now has a separate page. It describes `form.getInputProps`, `enhanceGetInputProps` and how to integrate `form.getInputProps` with custom inputs.
- [assignRef](https://mantine.dev/hooks/use-merged-ref/#assignref-function) function documentation has been added.
- [clampUseMovePosition](https://mantine.dev/hooks/use-move/#clampusemoveposition) function documentation has been added.
- Additional documentation about hook arguments and types has been added to [use-hotkeys](https://mantine.dev/hooks/use-hotkeys).
- [UseListStateHandlers type](https://mantine.dev/hooks/use-list-state/#useliststatehandlers-type) documentation has been added.
- [Functions reference](https://mantine.dev/guides/functions-reference) page has been added. Currently, it contains all functions that are exported from `@mantine/hooks` package. It is planned to document functions from other packages in next releases.
- Examples on how to change the close icon have been added to [Drawer](https://mantine.dev/core/drawer/#change-close-icon) and [Modal](https://mantine.dev/core/modal/#change-close-icon) components.
- `variantColorsResolver` demos have been added to [ActionIcon](https://mantine.dev/core/action-icon), [ThemeIcon](https://mantine.dev/core/theme-icon) and [Badge](https://mantine.dev/core/badge) components.
## Other changes
- [RichTextEditor](https://mantine.dev/x/tiptap) no longer depends on `@tabler/icons` package. It is no longer required to install `@tabler/icons` package to use `RichTextEditor` component. Icons used in the editor are now a part of the `@mantine/tiptap` package. This change improves bundling performance in several cases (mostly when using `RichTextEditor` in Next.js apps).
- [Badge](https://mantine.dev/core/badge) component now supports `circle` prop which makes the badge round.
- You can now reference theme values in `ff` [style prop](https://mantine.dev/styles/style-props) with `mono`, `text` and `heading` values: `<Box ff="mono" />`.
- [RichTextEditor](https://mantine.dev/x/tiptap) now has `RichTextEditor.Undo` and `RichTextEditor.Redo` controls.
- A new `luminance` [color function](https://mantine.dev/styles/color-functions) was added. It returns color luminance as a number between 0 and 1.
- All components now support new `flex` [style prop](https://mantine.dev/styles/style-props) which allows setting `flex` CSS property on the root element.
- [Collapse](https://mantine.dev/core/collapse) markup was reduced to single element, it can now be used in contexts that were previously not supported, for example, table rows.
- `stepHoldDelay` and `stepHoldInterval` props have been added to [NumberInput](https://mantine.dev/core/number-input).
- [mantine-form-zod-resolver](https://github.com/mantinedev/mantine-form-zod-resolver) now supports `errorPriority` configuration which allows controlling the order of errors specified in the schema. This feature requires updating `mantine-form-zod-resolver` to version 1.1.0 or higher.
- [CloseButton](https://mantine.dev/core/close-button) now supports `icon` prop, which allows overriding default icon. It is useful when it is not possible to replace `CloseButton`, for example, in [Drawer](https://mantine.dev/core/drawer) component.
- [Select](https://mantine.dev/core/select/#onchange-handler) component now calls `onChange` with an additional argument – option object. It contains `label`, `value` and optional `disabled` properties.
- It is now possible to define CSS variables in `styles` prop of all components.
- New [use-in-viewport](https://mantine.dev/hooks/use-in-viewport/) hook
- All Vite templates have been updated to Vite 5.0 and Vitest 1.0