Releases: mantinedev/mantine
7.9.2
What's Changed
[@mantine/dates]
DateTimePicker: Fix some oftimeInputProps
not being respected (#6204)[@mantine/core]
NavLink: Add react-router support to display active route (#6180)[@mantine/core]
Fixnonce
attribute not being set on<style />
tag generated in color scheme switching script[@mantine/core]
Input: Fix incorrect margins when input wrapper order is explicitly set[@mantine/core]
Pagination: Fix types definition being incompatible with @tabler/icons-react 3.x[@mantine/charts]
Fix incorrect tooltip position in LineChart, AreaChart and BarChart with vertical orientation[@mantine/core]
Rating: FixreadOnly
prop now working on touch devices (#6202)[@mantine/core]
TagsInput: Fix existing search value being ignored inonPaste
even handler (#6073)[@mantine/core]
TagsInput: Improveclearable
prop logic related to dropdown (#6115)
New Contributors
- @corydeppen made their first contribution in #6180
- @rodda-kyusu made their first contribution in #6204
- @iguit0 made their first contribution in #6233
- @Shadowfita made their first contribution in #6238
Full Changelog: 7.9.1...7.9.2
7.9.1
What's Changed
[@mantine/core]
Fixtheme.scale
being ignored in Input, Paper and Table border styles[@mantine/core]
FixvirtualColor
function requringuse client
in Next.js[@mantine/core]
FloatingIndicator: Fix incorrect resize observer logic (#6129)[@mantine/core]
NumberInput: Fix incorrectallowNegative
handling with up/down arrows (#6170)[@mantine/core]
Fixerror={true}
prop set on Checkbox, Radio and Switch rendering unxpected error element with margin[@mantine/core]
SegmentedControl: Fixtheme.primaryColor
not being respected in the focus ring styles[@mantine/core]
CloseButton: Fix incorrect specificity of some selectors[@mantine/core]
Fix incorrectaria-label
handling in Select, Autocomplete, MultiSelect and TagsInputs components (#6123)[@mantine/core]
Modal: PreventonClose
from being called when modal is not opened (#6156)[@mantine/core]
PasswordInput: Fix duplicated password visibility icon in Edge browser (#6126)[@mantine/hooks]
use-hash: Fix hash value not being updated correctly (#6145)[@mantine/emotion]
Fix incorrect transform logic that was causing extra hooks to render (#6159)
New Contributors
- @lachtanek made their first contribution in #6145
- @hsskey made their first contribution in #6156
- @ataldev made their first contribution in #6198
- @OliverWales made their first contribution in #6188
- @AustinWildgrube made their first contribution in #6170
- @theca11 made their first contribution in #6178
Full Changelog: 7.9.0...7.9.1
7.9.0 β¨
View changelog with demos on mantine.dev website
@mantine/emotion package
New @mantine/emotion package is now available to simplify migration
from 6.x to 7.x. It includes createStyles
function and additional
functionality for sx
and styles
props for all components similar to what was available
in @mantine/core
package in v6.
If you still haven't migrated to 7.x because of the change in styling approach, you can now
have a smoother transition by using @mantine/emotion
package. To learn more about the package,
visit the documentation page and updated 6.x to 7.x migration guide.
import { rem } from '@mantine/core';
import { createStyles } from '@mantine/emotion';
const useStyles = createStyles((theme, _, u) => ({
wrapper: {
maxWidth: rem(400),
width: '100%',
height: rem(180),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 'auto',
marginRight: 'auto',
borderRadius: theme.radius.sm,
// Use light and dark selectors to change styles based on color scheme
[u.light]: {
backgroundColor: theme.colors.gray[1],
},
[u.dark]: {
backgroundColor: theme.colors.dark[5],
},
// Reference theme.breakpoints in smallerThan and largerThan functions
[u.smallerThan('sm')]: {
// Child reference in nested selectors via ref
[`& .${u.ref('child')}`]: {
fontSize: theme.fontSizes.xs,
},
},
},
child: {
// Assign selector to a ref to reference it in other styles
ref: u.ref('child'),
padding: theme.spacing.md,
borderRadius: theme.radius.sm,
boxShadow: theme.shadows.md,
[u.light]: {
backgroundColor: theme.white,
color: theme.black,
},
[u.dark]: {
backgroundColor: theme.colors.dark[8],
color: theme.white,
},
},
}));
function Demo() {
const { classes } = useStyles();
return (
<div className={classes.wrapper}>
<div className={classes.child}>createStyles demo</div>
</div>
);
}
React 18.3 support
All @mantine/*
components and hooks have been updated to support React 18.3. It is
recommended to update your application as well to prepare for the upcoming React 19 release.
use-field hook
New use-field hook is now available in @mantine/form
package.
It can be used as a simpler alternative to use-form hook to manage state of a single input without the need to create a form.
The hook supports most of use-form
hook features: validation with function, touched and
dirty state, error message, validation on change/blur and more.
import { TextInput } from '@mantine/core';
import { isEmail, useField } from '@mantine/form';
function Demo() {
const field = useField({
initialValue: '',
validateOnChange: true,
validate: isEmail('Invalid email'),
});
return <TextInput {...field.getInputProps()} label="Email" placeholder="Enter your email" />;
}
use-field
hook also supports async validation:
import { Button, Loader, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';
function validateAsync(value: string): Promise<string | null> {
return new Promise((resolve) => {
window.setTimeout(() => {
resolve(value === 'mantine' ? null : 'Value must be "mantine"');
}, 800);
});
}
function Demo() {
const field = useField({
initialValue: '',
validate: validateAsync,
});
return (
<>
<TextInput
{...field.getInputProps()}
label="Enter 'mantine'"
placeholder="Enter 'mantine'"
rightSection={field.isValidating ? <Loader size={18} /> : null}
mb="md"
/>
<Button onClick={field.validate}>Validate async</Button>
</>
);
}
Custom PostCSS mixins
You can now define custom mixins that are not included in mantine-postcss-preset by specifying them
in the mixins
option. To learn about mixins syntax, follow postcss-mixins documentation.
Note that this feature is available in postcss-preset-mantine
starting from version 1.15.0.
Example of adding clearfix
and circle
mixins:
module.exports = {
plugins: {
'postcss-preset-mantine': {
autoRem: true,
mixins: {
clearfix: {
'&::after': {
content: '""',
display: 'table',
clear: 'both',
},
},
circle: (_mixin, size) => ({
borderRadius: '50%',
width: size,
height: size,
}),
},
},
// ... Other plugins
},
};
Then you can use these mixins in your styles:
.demo {
@mixin clearfix;
@mixin circle 100px;
}
use-matches hook
New use-matches
hook exported from @mantine/core
is an alternative to use-media-query
if you need to match multiple media queries and values. It accepts an object with media queries as keys and
values at given breakpoint as values.
Note that use-matches
hook uses the same logic as use-media-query under the hood,
it is not recommended to be used as a primary source of responsive styles, especially if you have ssr in your application.
In the following example:
- Starting from
theme.breakpoints.lg
, color will bered.9
- Between
theme.breakpoints.sm
andtheme.breakpoints.lg
, color will beorange.9
- Below
theme.breakpoints.sm
, color will beblue.9
import { Box, useMatches } from '@mantine/core';
function Demo() {
const color = useMatches({
base: 'blue.9',
sm: 'orange.9',
lg: 'red.9',
});
return (
<Box bg={color} c="white" p="xl">
Box with color that changes based on screen size
</Box>
);
}
BarChart value label
BarChart now supports withBarValueLabel
prop that allows
displaying value label on top of each bar:
import { BarChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<BarChart
h={300}
data={data}
dataKey="month"
valueFormatter={(value) => new Intl.NumberFormat('en-US').format(value)}
withBarValueLabel
series={[
{ name: 'Smartphones', color: 'violet.6' },
{ name: 'Laptops', color: 'blue.6' },
{ name: 'Tablets', color: 'teal.6' },
]}
/>
);
}
Documentation updates
- New usage with emotion guide
- 6.x -> 7.x guide has been updated to include migration to @mantine/emotion package
- use-field hook documentation
- Uncontrolled form mode examples now include usage of
form.key()
function - Custom PostCSS mixins documentation
- use-matches hook documentation has been added to the responsive guide
Other changes
- Advanced templates now include GitHub workflows to run tests on CI
- AspectRatio component has been migrated to aspect-ratio CSS property
7.8.1
Notes
Note that if you've already started using uncontrolled form mode introduced in 7.8.0, you need to include form.key()
as described in the documentation.
What's Changed
[@mantine/form]
AdddefaultValue
toform.getInputProps
return type[@mantine/form]
Replacekey
spread withform.getInputProps
withform.key()
function[@mantine/dropzone]
Fix keyboard activation not working (#6095)[@mantine/dates]
DatePicker: Fix date range being stuck in incorrect state when controlled state changes to an empty value (#6092)[@mantine/core]
Radio: Allownull
to be passed to Radio.Group value to clear the value (#6102)[@mantine/core]
NumberInput: Fix incorrect cursor position when backspace is pressed (#6072)[@mantine/core]
Fix incorrect empty string handling in style props (#6078)
New Contributors
Full Changelog: 7.8.0...7.8.1
7.8.0
View changelog with demos on mantine.dev website
Auto convert px to rem in .css files
Start from version 1.14.4
postcss-preset-mantine
supports autoRem
option that can be used to automatically convert all px
values
to rem
units in .css
files.
module.exports = {
plugins: {
'postcss-preset-mantine': {
autoRem: true,
},
},
};
This option works similar to rem
function. The following code:
.demo {
font-size: 16px;
@media (min-width: 320px) {
font-size: 32px;
}
}
Will be transformed to:
.demo {
font-size: calc(1rem * var(--mantine-scale));
@media (min-width: 320px) {
font-size: calc(2rem * var(--mantine-scale));
}
}
Note that autoRem
converts only CSS properties, values in @media
queries are
not converted automatically β you still need to use em
function to convert them.
autoRem
option does not convert values in the following cases:
- Values in
calc()
,var()
,clamp()
andurl()
functions - Values in
content
property - Values that contain
rgb()
,rgba()
,hsl()
,hsla()
colors
If you want to convert above values to rem units, use rem
function manually.
Uncontrolled form mode
useForm hook now supports uncontrolled mode.
Uncontrolled mode provides a significant performance improvement by reducing
the number of re-renders and the amount of state updates almost to 0. Uncontrolled
mode is now the recommended way to use the useForm
hook for almost all use cases.
Example of uncontrolled form (form.values
are not updated):
import { useState } from 'react';
import { Button, Code, Text, TextInput } from '@mantine/core';
import { hasLength, isEmail, useForm } from '@mantine/form';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: '', email: '' },
validate: {
name: hasLength({ min: 3 }, 'Must be at least 3 characters'),
email: isEmail('Invalid email'),
},
});
const [submittedValues, setSubmittedValues] = useState<typeof form.values | null>(null);
return (
<form onSubmit={form.onSubmit(setSubmittedValues)}>
<TextInput {...form.getInputProps('name')} label="Name" placeholder="Name" />
<TextInput {...form.getInputProps('email')} mt="md" label="Email" placeholder="Email" />
<Button type="submit" mt="md">
Submit
</Button>
<Text mt="md">Form values:</Text>
<Code block>{JSON.stringify(form.values, null, 2)}</Code>
<Text mt="md">Submitted values:</Text>
<Code block>{submittedValues ? JSON.stringify(submittedValues, null, 2) : 'β'}</Code>
</form>
);
}
form.getValues
With uncontrolled mode, you can not access form.values
as a state variable,
instead, you can use form.getValues()
method to get current form values at any time:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: 'John Doe' },
});
form.getValues(); // { name: 'John Doe' }
form.setValues({ name: 'John Smith' });
form.getValues(); // { name: 'John Smith' }
form.getValues()
always returns the latest form values, it is safe to use it
after state updates:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: 'John Doe' },
});
const handleNameChange = () => {
form.setFieldValue('name', 'Test Name');
// β Do not use form.values to get the current form values
// form.values has stale name value until next rerender in controlled mode
// and is always outdated in uncontrolled mode
console.log(form.values); // { name: 'John Doe' }
// β
Use form.getValues to get the current form values
// form.getValues always returns the latest form values
console.log(form.getValues()); // { name: 'Test Name' }
};
form.watch
form.watch
is an effect function that allows subscribing to changes of a
specific form field. It accepts field path and a callback function that is
called with new value, previous value, touched and dirty field states:
import { TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: {
name: '',
email: '',
},
});
form.watch('name', ({ previousValue, value, touched, dirty }) => {
console.log({ previousValue, value, touched, dirty });
});
return (
<div>
<TextInput label="Name" placeholder="Name" {...form.getInputProps('name')} />
<TextInput mt="md" label="Email" placeholder="Email" {...form.getInputProps('email')} />
</div>
);
}
Customize Popover middlewares
You can now customize middlewares
options in Popover component and
in other components (Menu, Select, Combobox, etc.)
based on Popover.
To customize Floating UI middlewares options, pass them as
an object to the middlewares
prop. For example, to change shift
middleware padding to 20px
use the following configuration:
import { Popover } from '@mantine/core';
function Demo() {
return (
<Popover middlewares={{ shift: { padding: 20 } }} position="bottom">
{/* Popover content */}
</Popover>
);
}
use-fetch hook
New use-fetch hook:
import { Box, Button, Code, Group, LoadingOverlay, Text } from '@mantine/core';
import { useFetch } from '@mantine/hooks';
interface Item {
userId: number;
id: number;
title: string;
completed: boolean;
}
function Demo() {
const { data, loading, error, refetch, abort } = useFetch<Item[]>(
'https://jsonplaceholder.typicode.com/todos/'
);
return (
<div>
{error && <Text c="red">{error.message}</Text>}
<Group>
<Button onClick={refetch} color="blue">
Refetch
</Button>
<Button onClick={abort} color="red">
Abort
</Button>
</Group>
<Box pos="relative" mt="md">
<Code block>{data ? JSON.stringify(data.slice(0, 3), null, 2) : 'Fetching'}</Code>
<LoadingOverlay visible={loading} />
</Box>
</div>
);
}
use-map hook
New use-map hook:
import { IconPlus, IconTrash } from '@tabler/icons-react';
import { ActionIcon, Group, Table } from '@mantine/core';
import { useMap } from '@mantine/hooks';
function Demo() {
const map = useMap([
['/hooks/use-media-query', 4124],
['/hooks/use-clipboard', 8341],
['/hooks/use-fetch', 9001],
]);
const rows = Array.from(map.entries()).map(([key, value]) => (
<Table.Tr key={key}>
<Table.Td>{key}</Table.Td>
<Table.Td>{value}</Table.Td>
<Table.Td>
<Group>
<ActionIcon variant="default" onClick={() => map.set(key, value + 1)} fw={500}>
<IconPlus stroke={1.5} size={18} />
</ActionIcon>
<ActionIcon variant="default" onClick={() => map.delete(key)} c="red">
<IconTrash stroke={1.5} size={18} />
</ActionIcon>
</Group>
</Table.Td>
</Table.Tr>
));
return (
<Table layout="fixed">
<Table.Thead>
<Table.Tr>
<Table.Th>Page</Table.Th>
<Table.Th>Views last month</Table.Th>
<Table.Th />
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
);
}
use-set hook
New use-set hook:
import { useState } from 'react';
import { Code, Stack, TextInput } from '@mantine/core';
import { useSet } from '@mantine/hooks';
function Demo() {
const [input, setInput] = useState('');
const scopes = useSet<string>(['@mantine', '@mantine-tests', '@mantinex']);
const isDuplicate = scopes.has(input.trim().toLowerCase());
const items = Array.from(scopes).map((scope) => <Code key={scope}>{scope}</Code>);
return (
<>
<TextInput
label="Add new scope"
placeholder="Enter scope"
description="Duplicate scopes are not allowed"
value={input}
onChange={(event) => setInput(event.currentTarget.value)}
error={isDuplicate && 'Scope already exists'}
onKeyDown={(event) => {
if (event.nativeEvent.code === 'Enter' && !isDuplicate) {
scopes.add(input.trim().toLowerCase());
setInput('');
}
}}
/>
<Stack gap={5} align="flex-start" mt="md">
{items}
</Stack>
</>
);
}
use-debounced-callback hook
New use-debounced-callback hook:
import { useState } from 'react';
import { Loader, Text, TextInput } from '@mantine/core';
import { useDebouncedCallback } from '@mantine/hooks';
function getSearchResults(query: string): Promise<{ id: number; title: string }[]> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(
query.trim() === ''
? []
: Array(5)
.fill(0)
.map((_, index) => ({ id: index, title: `${query} ${index + 1}` }))
);
}, 1000);
});
}
function Demo() {
const [search, setSearch] = useState('');
const [searchResults, setSearchResults] = useState<{ id: numbe...
7.7.2
What's Changed
[@mantine/core]
CloseButton: Add missing disabled styles (#6044)[@mantine/core]
AppShell: Fix incorrect app safe area handling by AppShell.Footer (#6060)[@mantine/core]
NumberInput: Fix cursor position changing when the value is incremented/decremented (#6004)[@mantine/core]
TagsInput: Fix incorrect IME keyboard input handling forBackspace
key (#6011)[@mantine/charts]
Fix incorrect overflow styles of svg element (#6040)[@mantine/core]
PinInput: AddrootRef
prop support (#6032)[@mantine/core]
ScrollArea: FixviewportProps.onScroll
not working (#6055)[@mantine/core]
ScrollArea: Fix incorrect inset position of the horizontal scrollbar (#6059)[@mantine/hooks]
use-local-storage: Fix infinite rerendering with object values (#6022)
New Contributors
Full Changelog: 7.7.1...7.7.2
7.7.1
What's Changed
[@mantine/tiptap]
Improve toolbar items alignment for non-native elements (#5993)[@mantine/spotlight]
Fix incorrect down key handling when the spotlight is opened repeatedly (#5995)[@mantine/core]
Image: Fix ref not being assigned for fallback images (#5989)[@mantine/core]
PinInput: Fix incorrect focus logic (#5963)[@mantine/core]
Table: FixhighlightOnHoverColor
prop not working[@mantine/core]
AppShell: Adjust footer position to include env(safe-area-inset-bottom) (#5502)[@mantine/core]
PinInput: Fix placeholder not being visible on the element that had focus when the component becomes disabled (#5831)[@mantine/dates]
Calendar: Fix double timezone shift (#5916)[@mantine/hooks]
use-local-storage: Fix value not being updated when key is changed (#5910)[@mantine/charts]
Fix incorrect charts legends height for multiline values (#5923)[@mantine/core]
NumberInput: Fix incorrect increment/decrement functions logic whenstep
is a float value (#5926)[@mantine/core]
Combobox: Fix incorrect IME input handling (#5935)[@mantine/core]
Menu: Fix unexpected focus styles in the dropdown element in Firefox (#5957)[@mantine/core]
Fix incorrectdisabled
prop handling in TagsInput and MultiSelect (#5959)[@mantine/core]
FixrenderOption
not working for grouped items in Combobox-based components (#5952)[@mantine/core]
AppShell: Fix error when used inside Suspense (#5979)[@mantine/core]
Update CSS selectors hashing algorithm to prevent collisions with other libraries (#5968)[@mantine/carousel]
Fix specificity issues of some selectors (#5973)[@mantine/core]
AppShell: Fix missing Aside offset in Header and Footer forlayout=alt
(#5974)
New Contributors
- @naughton made their first contribution in #5952
- @wasamistake made their first contribution in #5971
- @elecdeer made their first contribution in #5935
- @israelins85 made their first contribution in #5910
- @1g0rrr made their first contribution in #5916
- @joshua-webdev made their first contribution in #5963
- @s-cork made their first contribution in #5989
- @xiaohanyu made their first contribution in #5993
Full Changelog: 7.7.0...7.7.1
7.7.0
View changelog with demos on mantine.dev website
Virtual colors
Virtual color is a special color which values should be different for light and dark color schemes.
To define a virtual color, use virtualColor
function which accepts an object with the following
properties as a single argument:
name
β color name, must be the same as the key intheme.colors
objectlight
β a key oftheme.colors
object for light color schemedark
β a key oftheme.colors
object for dark color scheme
To see the demo in action, switch between light and dark color schemes (Ctrl + J
):
import { createTheme, MantineProvider, virtualColor } from '@mantine/core';
const theme = createTheme({
colors: {
primary: virtualColor({
name: 'primary',
dark: 'pink',
light: 'cyan',
}),
},
});
function App() {
return <MantineProvider theme={theme}>{/* Your app here */}</MantineProvider>;
}
FloatingIndicator component
New FloatingIndicator component:
import { useState } from 'react';
import {
IconArrowDown,
IconArrowDownLeft,
IconArrowDownRight,
IconArrowLeft,
IconArrowRight,
IconArrowUp,
IconArrowUpLeft,
IconArrowUpRight,
IconCircle,
} from '@tabler/icons-react';
import { FloatingIndicator, UnstyledButton } from '@mantine/core';
import classes from './Demo.module.css';
function Demo() {
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const [active, setActive] = useState('center');
const setControlRef = (name: string) => (node: HTMLButtonElement) => {
controlsRefs[name] = node;
setControlsRefs(controlsRefs);
};
return (
<div className={classes.root} dir="ltr" ref={setRootRef}>
<FloatingIndicator
target={controlsRefs[active]}
parent={rootRef}
className={classes.indicator}
/>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up-left')}
ref={setControlRef('up-left')}
mod={{ active: active === 'up-left' }}
>
<IconArrowUpLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up')}
ref={setControlRef('up')}
mod={{ active: active === 'up' }}
>
<IconArrowUp size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up-right')}
ref={setControlRef('up-right')}
mod={{ active: active === 'up-right' }}
>
<IconArrowUpRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('left')}
ref={setControlRef('left')}
mod={{ active: active === 'left' }}
>
<IconArrowLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('center')}
ref={setControlRef('center')}
mod={{ active: active === 'center' }}
>
<IconCircle size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('right')}
ref={setControlRef('right')}
mod={{ active: active === 'right' }}
>
<IconArrowRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down-left')}
ref={setControlRef('down-left')}
mod={{ active: active === 'down-left' }}
>
<IconArrowDownLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down')}
ref={setControlRef('down')}
mod={{ active: active === 'down' }}
>
<IconArrowDown size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down-right')}
ref={setControlRef('down-right')}
mod={{ active: active === 'down-right' }}
>
<IconArrowDownRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
</div>
);
}
ScatterChart component
New ScatterChart component:
import { useState } from 'react';
import {
IconArrowDown,
IconArrowDownLeft,
IconArrowDownRight,
IconArrowLeft,
IconArrowRight,
IconArrowUp,
IconArrowUpLeft,
IconArrowUpRight,
IconCircle,
} from '@tabler/icons-react';
import { FloatingIndicator, UnstyledButton } from '@mantine/core';
import classes from './Demo.module.css';
function Demo() {
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const [active, setActive] = useState('center');
const setControlRef = (name: string) => (node: HTMLButtonElement) => {
controlsRefs[name] = node;
setControlsRefs(controlsRefs);
};
return (
<div className={classes.root} dir="ltr" ref={setRootRef}>
<FloatingIndicator
target={controlsRefs[active]}
parent={rootRef}
className={classes.indicator}
/>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up-left')}
ref={setControlRef('up-left')}
mod={{ active: active === 'up-left' }}
>
<IconArrowUpLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up')}
ref={setControlRef('up')}
mod={{ active: active === 'up' }}
>
<IconArrowUp size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('up-right')}
ref={setControlRef('up-right')}
mod={{ active: active === 'up-right' }}
>
<IconArrowUpRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('left')}
ref={setControlRef('left')}
mod={{ active: active === 'left' }}
>
<IconArrowLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('center')}
ref={setControlRef('center')}
mod={{ active: active === 'center' }}
>
<IconCircle size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('right')}
ref={setControlRef('right')}
mod={{ active: active === 'right' }}
>
<IconArrowRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
<div className={classes.controlsGroup}>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down-left')}
ref={setControlRef('down-left')}
mod={{ active: active === 'down-left' }}
>
<IconArrowDownLeft size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down')}
ref={setControlRef('down')}
mod={{ active: active === 'down' }}
>
<IconArrowDown size={26} stroke={1.5} />
</UnstyledButton>
<UnstyledButton
className={classes.control}
onClick={() => setActive('down-right')}
ref={setControlRef('down-right')}
mod={{ active: active === 'down-right' }}
>
<IconArrowDownRight size={26} stroke={1.5} />
</UnstyledButton>
</div>
</div>
);
}
colorsTuple function
New colorsTuple
function can be used to:
- Use single color as the same color for all shades
- Transform dynamic string arrays to Mantine color tuple (the array should still have 10 values)
import { colorsTuple, createTheme } from '@mantine/core';
const theme = createTheme({
colors: {
custom: colorsTuple('#FFC0CB'),
dynamic: colorsTuple(Array.from({ length: 10 }, (_, index) => '#FFC0CB')),
},
});
use-mutation-observer hook
New useMutationObserver hook:
import { useState } from 'react';
import { Kbd, Text } from '@mantine/core';
import { useMutationObserver } from '@mantine/hooks';
function Demo() {
const [lastMutation, setLastMutation] = useState('');
useMutationObserver(
(mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'dir') {
mutation.target instanceof HTMLElement &&
setLastMutation(mutation.target.getAttribute('dir') || '');
}
});
...
7.6.2
What's Changed
[@mantine/hooks]
use-resize-observer: Fix types (#5847)[@mantine/hooks]
use-local-storage: Fixundefined
being written to the local storage whendefaultValue
is not defined (#5848)[@mantine/core]
NumberInput: FixonValueChange
not being called in increment/decrement functions (#5856)[@mantine/core]
InputWrapper: FixclassName
specified inlabelProps
,descriptionProps
anderrorProps
not being passed to the corresponding element (#5862)[@mantine/core]
Fix some functions not working correctly with TypeScript 5.4 (#5891)[@mantine/form]
FixonValuesChange
not using updated function (#5901)[@mantine/core]
Popover: Fix incorrect dropdown selectors (#5903)[@mantine/core]
Indicator: Fix processing animation in Safari (#5908)[@mantine/hooks]
use-headroom: Fix incorrect pinning logic when scrolling up (#5793)[@mantine/dropzone]
Add heic images format to default mime types (#5867)[@mantine/core]
Transition: Fix transitions resolving instantly in some cases (#5873)[@mantine/dropzone]
AddinputProps
prop support to pass props down to the underlying hidden input element (#5880)[@mantine/core]
Timeline: FixautoContrast
being passed to the dom node as attribute (#5890)
New Contributors
- @AdarshJais made their first contribution in #5833
- @Zachary-Kaye made their first contribution in #5890
- @redachl made their first contribution in #5867
- @timkrins made their first contribution in #5860
- @Alimobasheri made their first contribution in #5793
Full Changelog: 7.6.1...7.6.2
7.6.1
What's Changed
[@mantine/core]
Fix incorrect focus ring styles in Button.Group and ActionIcon.Group components (#5736)[@mantine/core]
Progress: Fix incorrect border-radius with multiple sections[@mantine/dates]
DateTimePicker: FixminDate
andmaxDate
not being respected in time input (#5819)[@mantine/core]
Switch: Userole="switch"
for better accessibility (#5746)[@mantine/hooks]
use-resize-observer: Fix incorrect ref type (#5780)[@mantine/dates]
FixpopoverProps.onClose
overriding original component value in DatePickerInput and other similar components (#4105)[@mantine/core]
Fix incorrect Escape key handling in Modal and Drawer components in some cases (#2827)[@mantine/core]
Combobox: Fix incorrect Escape key handling in Modal, Drawer and Popover[@mantine/core]
Transition: Fix transition resolving instantly in some cases (#3126, #5193)[@mantine/core]
Remove loader from the DOM ifloading
prop is not set on ActionIcon and Button components (#5795)[@mantine/hooks]
use-local-storage: Fix inconsistent default value persistence ifgetInitialValueInEffect
is set (#5796)[@mantine/core]
Select: FixautoComplete
prop not working (#5813)[@mantine/core]
Tabs: Fix incorrect border styles in outline variant[@mantine/core]
Checkbox: Fix incorrectindeterminate
+disabled
styles for outline variant (#5806)[@mantine/core]
SegmentedControl: Fix indicator state not being updated correctly when controlled state changes to a value that is not present in the data array (#5689)[@mantine/core]
Fix incorrect label offset with left label position in Checkbox, Switch and Radio components (#5823)[@mantine/core]
PinInput: Fix updating controlled value to an empty string working incorrectly[@mantine/core]
Menu: Fix incorrect role of dropdown elements
New Contributors
- @gl3nn made their first contribution in #5689
- @ktunador made their first contribution in #5711
- @snturk made their first contribution in #5819
Full Changelog: 7.6.0...7.6.1