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

Update mantine monorepo to v7 (major) #343

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Sep 18, 2023

Mend Renovate

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
@mantine/core (source) ^5.2.5 -> ^7.0.0 age adoption passing confidence
@mantine/hooks (source) ^5.2.5 -> ^7.0.0 age adoption passing confidence

Release Notes

mantinedev/mantine (@​mantine/core)

v7.8.0

Compare Source

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() and url() 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 '@&#8203;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 '@&#8203;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 '@&#8203;mantine/core';
import { useForm } from '@&#8203;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 '@&#8203;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 '@&#8203;mantine/core';
import { useFetch } from '@&#8203;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 '@&#8203;tabler/icons-react';
import { ActionIcon, Group, Table } from '@&#8203;mantine/core';
import { useMap } from '@&#8203;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 '@&#8203;mantine/core';
import { useSet } from '@&#8203;mantine/hooks';

function Demo() {
  const [input, setInput] = useState('');
  const scopes = useSet<string>(['@&#8203;mantine', '@&#8203;mantine-tests', '@&#8203;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 '@&#8203;mantine/core';
import { useDebouncedCallback } from '@&#8203;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: number; title: string }[]>([]);
  const [loading, setLoading] = useState(false);

  const handleSearch = useDebouncedCallback(async (query: string) => {
    setLoading(true);
    setSearchResults(await getSearchResults(query));
    setLoading(false);
  }, 500);

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSearch(event.currentTarget.value);
    handleSearch(event.currentTarget.value);
  };

  return (
    <>
      <TextInput
        value={search}
        onChange={handleChange}
        placeholder="Search..."
        rightSection={loading && <Loader size={20} />}
      />
      {searchResults.map((result) => (
        <Text key={result.id} size="sm">
          {result.title}
        </Text>
      ))}
    </>
  );
}
use-throttled-state hook

New use-throttled-state hook:

import { Text, TextInput } from '@&#8203;mantine/core';
import { useThrottledState } from '@&#8203;mantine/hooks';

function Demo() {
  const [throttledValue, setThrottledValue] = useThrottledState('', 1000);

  return (
    <>
      <TextInput
        placeholder="Search"
        onChange={(event) => setThrottledValue(event.currentTarget.value)}
      />
      <Text>Throttled value: {throttledValue || '–'}</Text>
    </>
  );
}
use-throttled-value hook

New use-throttled-value hook:

import { Text, TextInput } from '@&#8203;mantine/core';
import { useThrottledValue } from '@&#8203;mantine/hooks';

function Demo() {
  const [value, setValue] = useState('');
  const throttledValue = useThrottledValue(value, 1000);

  return (
    <>
      <TextInput placeholder="Search" onChange={(event) => setValue(event.currentTarget.value)} />
      <Text>Throttled value: {throttledValue || '–'}</Text>
    </>
  );
}
use-throttled-callback hook

New use-throttled-callback hook:

import { Text, TextInput } from '@&#8203;mantine/core';
import { useThrottledCallback } from '@&#8203;mantine/hooks';

function Demo() {
  const [throttledValue, setValue] = useState('');
  const throttledSetValue = useThrottledCallback((value) => setValue(value), 1000);

  return (
    <>
      <TextInput
        placeholder="Search"
        onChange={(event) => throttledSetValue(event.currentTarget.value)}
      />
      <Text>Throttled value: {throttledValue || '–'}</Text>
    </>
  );
}
use-orientation hook

New use-orientation hook:

import { Code, Text } from '@&#8203;mantine/core';
import { useOrientation } from '@&#8203;mantine/hooks';

function Demo() {
  const { angle, type } = useOrientation();
  return (
    <>
      <Text>
        Angle: <Code>{angle}</Code>
      </Text>
      <Text>
        Type: <Code>{type}</Code>
      </Text>
    </>
  );
}
use-is-first-render hook

New use-is-first-render hook:

import { useState } from 'react';
import { Button, Text } from '@&#8203;mantine/core';
import { useIsFirstRender } from '@&#8203;mantine/hooks';

function Demo() {
  const [counter, setCounter] = useState(0);
  const firstRender = useIsFirstRender();
  return (
    <div>
      <Text>
        Is first render:{' '}
        <Text span c={firstRender ? 'teal' : 'red'}>
          {firstRender ? 'Yes' : 'No!'}
        </Text>
      </Text>
      <Button onClick={() => setCounter((c) => c + 1)} mt="sm">
        Rerendered {counter} times, click to rerender
      </Button>
    </div>
  );
}
Documentation updates
Other changes
  • NumberInput now supports withKeyboardEvents={false} to disable up/down arrow keys handling
  • Popover shift middleware now has default padding of 5px to offset dropdown near the edge of the viewport

v7.7.2

Compare Source

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 for Backspace key (#​6011)
  • [@mantine/charts] Fix incorrect overflow styles of svg element (#​6040)
  • [@mantine/core] PinInput: Add rootRef prop support (#​6032)
  • [@mantine/core] ScrollArea: Fix viewportProps.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: mantinedev/mantine@7.7.1...7.7.2

v7.7.1

Compare Source

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: Fix highlightOnHoverColor 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 when step 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 incorrect disabled prop handling in TagsInput and MultiSelect (#​5959)
  • [@mantine/core] Fix renderOption 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 for layout=alt (#​5974)
New Contributors

Full Changelog: mantinedev/mantine@7.7.0...7.7.1

v7.7.0

Compare Source

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 in theme.colors object
  • light – a key of theme.colors object for light color scheme
  • dark – a key of theme.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 '@&#8203;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 '@&#8203;tabler/icons-react';
import { FloatingIndicator, UnstyledButton } from '@&#8203;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 '@&#8203;tabler/icons-react';
import { FloatingIndicator, UnstyledButton } from '@&#8203;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 '@&#8203;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 '@&#8203;mantine/core';
import { useMutationObserver } from '@&#8203;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') || '');
        }
      });
    },
    {
      attributes: true,
      attributeFilter: ['dir'],
    },
    () => document.documentElement
  );

  return (
    <>
      <Text>
        Press <Kbd>Ctrl</Kbd> + <Kbd>Shift</Kbd> + <Kbd>L</Kbd> to change direction
      </Text>

      <Text mt={10}>Direction was changed to: {lastMutation || 'Not changed yet'}</Text>
    </>
  );
}
use-state-history hook

New useStateHistory hook:

import { Button, Code, Group, Text } from '@&#8203;mantine/core';
import { useStateHistory } from '@&#8203;mantine/hooks';

function Demo() {
  const [value, handlers, history] = useStateHistory(1);
  return (
    <>
      <Text>Current value: {value}</Text>
      <Group my="md">
        <Button onClick={() => handlers.set(Math.ceil(Math.random() * 100) + 1)}>Set value</Button>
        <Button onClick={() => handlers.back()}>Back</Button>
        <Button onClick={() => handlers.forward()}>Forward</Button>
      </Group>
      <Code block>{JSON.stringify(history, null, 2)}</Code>
    </>
  );
}
Axis labels

AreaChart, BarChart and LineChart
components now support xAxisLabel and yAxisLabel props:

import { AreaChart } from '@&#8203;mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <AreaChart
      h={300}
      data={data}
      dataKey="date"
      type="stacked"
      xAxisLabel="Date"
      yAxisLabel="Amount"
      series={[
        { name: 'Apples', color: 'indigo.6' },
        { name: 'Oranges', color: 'blue.6' },
        { name: 'Tomatoes', color: 'teal.6' },
      ]}
    />
  );
}
Documentation updates
  • New section has been added to the responsive guide on how to use mantine-hidden-from-{x} and mantine-visible-from-{x} classes.
  • Remix guide has been updated to use new Vite bundler
  • Jest and Vitest guides configuration has been updated to include mocks for window.HTMLElement.prototype.scrollIntoView
  • CSS variables documentation has been updated to include more information about typography and colors variables
Help center updates

New articles added to the help center:

Other changes
  • SegmentedControl indicator positioning logic has been migrated to FloatingIndicator. It is now more performant and works better when used inside elements with transform: scale().
  • New use-mounted hook
  • Sparkline now supports connectNulls and areaProps props
  • Remix template has been updated to use new Vite bundler
  • Select, MultiSelect, Autocomplete and TagsInput components now support scrollAreaProps prop to pass props down to the ScrollArea component in the dropdown
  • Transition component now supports 4 new transitions: fade-up, fade-down, fade-left, fade-right
  • Default Modal transition was changed to fade-down. This change resolves issues with SegmentedControl indicator positioning when used inside modals.
  • You can now reference headings font sizes and line heights in fz and lh style props with h1, h2, h3, h4, h5, h6 values

v7.6.2

Compare Source

What's Changed

  • [@mantine/hooks] use-resize-observer: Fix types (#​5847)
  • [@mantine/hooks] use-local-storage: Fix undefined being written to the local storage when defaultValue is not defined (#​5848)
  • [@mantine/core] NumberInput: Fix onValueChange not being called in increment/decrement functions (#​5856)
  • [@mantine/core] InputWrapper: Fix className specified in labelProps, descriptionProps and errorProps not being passed to the corresponding element (#​5862)
  • [@mantine/core] Fix some functions not working correctly with TypeScript 5.4 (#​5891)
  • [@mantine/form] Fix onValuesChange 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] Add inputProps prop support to pass props down to the underlying hidden input element (#​5880)
  • [@mantine/core] Timeline: Fix autoContrast being passed to the dom node as attribute (#​5890)

New Contributors

Full Changelog: mantinedev/mantine@7.6.1...7.6.2

v7.6.1

Compare Source

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: Fix minDate and maxDate not being respected in time input (#​5819)
  • [@mantine/core] Switch: Use role="switch" for better accessibility (#​5746)
  • [@mantine/hooks] use-resize-observer: Fix incorrect ref type (#​5780)
  • [@mantine/dates] Fix popoverProps.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 if loading prop is not set on ActionIcon and Button components (#​5795)
  • [@mantine/hooks] use-local-storage: Fix inconsistent default value persistence if getInitialValueInEffect is set (#​5796)
  • [@mantine/core] Select: Fix autoComplete prop not working (#​5813)
  • [@mantine/core] Tabs: Fix incorrect border styles in outline variant
  • [@mantine/core] Checkbox: Fix incorrect indeterminate + 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

Full Changelog: mantinedev/mantine@7.6.0...7.6.1

v7.6.0: 🌟

Compare Source

View changelog with demos on mantine.dev website

Container queries support

You can now use container queries
with Mantine components. rem and em functions from postcss-preset-mantine
are available in container queries staring from postcss-preset-mantine@1.13.0.

.root {
  min-width: rem(200px);
  max-width: 100%;
  min-height: rem(120px);
  container-type: inline-size;
  overflow: auto;
  resize: horizontal;
}

.child {
  background-color: var(--mantine-color-dimmed);
  color: var(--mantine-color-white);
  padding: var(--mantine-spacing-md);

  @&#8203;container (max-width: rem(500px)) {
    background-color: var(--mantine-color-blue-filled);
  }

  @&#8203;container (max-width: rem(300px)) {
    background-color: var(--mantine-color-red-filled);
  }
}
RadarChart component

New RadarChart component:

import { RadarChart } from '@&#8203;mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <RadarChart
      h={300}
      data={multiData}
      dataKey="product"
      withPolarRadiusAxis
      series={[
        { name: 'sales_january', color: 'lime.4', opacity: 0.1 },
        { name: 'sales_february', color: 'cyan.4', opacity: 0.1 },
      ]}
    />
  );
}
FocusTrap.InitialFocus component

FocusTrap.InitialFocus is a new component that adds a visually hidden
element which will receive the focus when the focus trap is activated.
Once FocusTrap.InitialFocus loses focus, it is removed from the tab order.

For example, it is useful if you do not want to focus any elements inside the Modal when it is opened:

import { Button, FocusTrap, Modal, TextInput } from '@&#8203;mantine/core';
import { useDisclosure } from '@&#8203;mantine/hooks';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Modal opened={opened} onClose={close} title="Focus demo">
        <FocusTrap.InitialFocus />
        <TextInput label="First input" placeholder="First input" />
        <TextInput
          data-autofocus
          label="Input with initial focus"
          placeholder="It has data-autofocus attribute"
          mt="md"
        />
      </Modal>

      <Button onClick={open}>Open modal</Button>
    </>
  );
}
New MantineProvider props

MantineProvider now includes more props to control how styles
are generated and injected. These props are useful if you use Mantine as a headless library
and in test environments.

deduplicateCssVariables

deduplicateCssVariables prop determines whether CSS variables should be deduplicated: if CSS variable has the same value as in default theme, it is not added in the runtime.
By default, it is set to true. If set to false, all Mantine CSS variables will be added in <style /> tag
even if they have the same value as in the default theme.

import { MantineProvider } from '@&#8203;mantine/core';

function Demo() {
  return (
    <MantineProvider deduplicateCssVariables={false}>
      <App />
    </MantineProvider>
  );
}
withStaticClasses

withStaticClasses determines whether components should have static classes, for example, mantine-Button-root.
By default, static classes are enabled, to disable them set withStaticClasses to false:

import { MantineProvider } from '@&#8203;mantine/core';

function Demo() {
  return (
    <MantineProvider withStaticClasses={false}>
      <App />
    </MantineProvider>
  );
}
withGlobalClasses

withGlobalClasses determines whether global classes should be added with <style /> tag.
Global classes are required for hiddenFrom/visibleFrom and lightHidden/darkHidden props to work.
By default, global classes are enabled, to disable them set withGlobalClasses to false. Note that
disabling global classes may break styles of some components.

import { MantineProvider } from '@&#8203;mantine/core';

function Demo() {
  return (
    <MantineProvider withGlobalClasses={false}>
      <App />
    </MantineProvider>
  );
}
HeadlessMantineProvider

HeadlessMantineProvider is an alternative to MantineProvider
that should be used when you want to use Mantine as a headless UI library. It removes all
features that are related to Mantine styles:

  • Mantine classes are not applied to components
  • Inline CSS variables are not added with style attribute
  • All color scheme related features are removed
  • Global styles are not generated

Limitations of HeadlessMantineProvider:

  • Color scheme switching will not work. If your application has a dark mode, you will need to implement it on your side.
  • Props that are related to styles in all components (color, radius, size, etc.) will have no effect.
  • Some components that rely on styles will become unusable (Grid, SimpleGrid, Container, etc.).
  • lightHidden/darkHidden, visibleFrom/hiddenFrom props will not work.
  • Style props will work only with explicit values, for example mt="xs" will not work, but mt={5} will.

To use HeadlessMantineProvider, follow getting started guide and replace MantineProvider with HeadlessMantineProvider.
Note that you do not need to use ColorSchemeScript in your application, it will not have any effect,
you can ignore this part of the guide.

import { HeadlessMantineProvider } from '@&#8203;mantine/core';

function App() {
  return <HeadlessMantineProvider>{/* Your application */}</HeadlessMantineProvider>;
}
Sparkline trendColors

Sparkline now supports trendColors prop to change chart color depending on the trend.
The prop accepts an object with positive, negative and neutral properties:

  • positive - color for positive trend (first value is less than the last value in data array)
  • negative - color for negative trend (first value is greater than the last value in data array)
  • neutral - color for neutral trend (first and last values are equal)

neutral is optional, if not provided, the color will be the same as positive.

import { Sparkline } from '@&#8203;mantine/charts';
import { Stack, Text } from '@&#8203;mantine/core';

const positiveTrend = [10, 20, 40, 20, 40, 10, 50];
const negativeTrend = [50, 40, 20, 40, 20, 40, 10];
const neutralTrend = [10, 20, 40, 20, 40, 10, 50, 5, 10];

function Demo() {
  return (
    <Stack gap="sm">
      <Text>Positive trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={positiveTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />

      <Text mt="md">Negative trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={negativeTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />

      <Text mt="md">Neutral trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={neutralTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />
    </Stack>
  );
}
RichTextEditor tasks extension

RichTextEditor now supports tasks tiptap extension:

import TaskItem from '@&#8203;tiptap/extension-task-item';
import { useEditor } from '@&#8203;tiptap/react';
import StarterKit from '@&#8203;tiptap/starter-kit';
import { getTaskListExtension, RichTextEditor } from '@&#8203;mantine/tiptap';

function Demo() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      getTaskListExtension(TipTapTaskList),
      TaskItem.configure({
        nested: true,
        HTMLAttributes: {
          class: 'test-item',
        },
      }),
    ],
    content: `
        <ul data-type="taskList">
          <li data-type="taskItem" data-checked="true">A list item</li>
          <li data-type="taskItem" data-checked="false">And another one</li>
        </ul>
      `,
  });

  return (
    <div style={{ padding: 40 }}>
      <RichTextEditor editor={editor}>
        <RichTextEditor.Toolbar>
          <RichTextEditor.ControlsGroup>
            <RichTextEditor.TaskList />
            <RichTextEditor.TaskListLift />
            <RichTextEditor.TaskListSink />
          </RichTextEditor.ControlsGroup>
        </RichTextEditor.Toolbar>

        <RichTextEditor.Content />
      </RichTextEditor>
    </div>
  );
}
renderOption prop

Select, MultiSelect, TagsInput and Autocomplete
components now support renderOption prop that allows to customize option rendering:

import {
  IconAlignCenter,
  IconAlignJustified,
  IconAlignLeft,
  IconAlignRight,
  IconCheck,
} from '@&#8203;tabler/icons-react';
import { Group, Select, SelectProps } from '@&#8203;mantine/core';

const iconProps = {
  stroke: 1.5,
  color: 'currentColor',
  opacity: 0.6,
  size: 18,
};

const icons: Record<string, React.ReactNode> = {
  left: <IconAlignLeft {...iconProps} />,
  center: <IconAlignCenter {...iconProps} />,
  right: <IconAlignRight {...iconProps} />,
  justify: <IconAlignJustified {...iconProps} />,
};

const renderSelectOption: SelectProps['renderOption'] = ({ option, checked }) => (
  <Group flex="1" gap="xs">
    {icons[option.value]}
    {option.label}
    {checked && <IconCheck style={{ marginInlineStart: 'auto' }} {...iconProps} />}
  </Group>
);

function Demo() {
  return (
    <Select
      label="Select with renderOption"
      placeholder="Select text align"
      data={[
        { value: 'left', label: 'Left' },
        { value: 'center', label: 'Center' },
        { value: 'right', label: 'Right' },
        { value: 'justify', label: 'Justify' },
      ]}
      renderOption={renderSelectOption}
    />
  );
}
Styles improvements

All Mantine components have been migrated to logical CSS properties
(as a replacement for rtl styles) and :where pseudo-class
(as a replacement for private CSS variables). These changes
should not impact the usage of Mantine components, but now Mantine CSS files have smaller size. For example,
@mantine/core/styles.css now has ~ 8% smaller size (192kb -> 177kb).

Pass props to inner recharts components

You can now pass props down to recharts Bar, Area and Line components
with barProps, areaProps and lineProps props on BarChart, AreaChart and LineChart components.

All props accepts either an object with props or a function that receives
series data as an argument and returns an object with props.

PieChart percent labels

PieChart now supports percent labels:

import { BarChart } from '@&#8203;mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <BarChart
      h={200}
      data={data}
      dataKey="month"
      orientation="vertical"
      yAxisProps={{ width: 80 }}
      barProps={{ radius: 10 }}
      series={[{ name: 'Smartphones', color: 'blue.6' }]}
    />
  );
}
Documentation updates
Help center updates

New articles added to the help center:

Other changes
  • use-list-state hook now supports swap handler
  • form.setFieldValue now supports callback function as an argument
  • px, py, mx and my style props now use logical CSS properties padding-inline, padding-block, margin-inline and margin-block instead of padding-left, padding-right, etc.
  • All components now support me, ms, ps, pe style props to set margin-inline-end, margin-inline-start, padding-inline-start and `paddin

Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate. View repository job log here.

@renovate renovate bot added the renovate label Sep 18, 2023
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 4f17a19 to d48b437 Compare October 8, 2023 13:42
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from d48b437 to 1fb3d01 Compare October 20, 2023 07:41
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from b88389c to 3a44282 Compare November 6, 2023 00:23
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from d5a92a0 to dc2c6b5 Compare November 25, 2023 18:49
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from dc2c6b5 to 795ec6c Compare December 5, 2023 00:38
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 795ec6c to 87ef5af Compare December 17, 2023 00:02
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 87ef5af to 027e13c Compare December 24, 2023 19:34
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from 32e55ca to 43d5250 Compare January 11, 2024 08:28
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 43d5250 to 8634003 Compare January 14, 2024 18:19
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 8634003 to 7e4e219 Compare February 4, 2024 22:40
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 7e4e219 to 7dbcf23 Compare February 19, 2024 22:11
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 7dbcf23 to 23f02fa Compare March 2, 2024 21:31
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from c0e7faf to 52c7709 Compare March 13, 2024 22:16
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 52c7709 to b8d3ec0 Compare March 23, 2024 13:40
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from a593330 to 54fdf2b Compare April 26, 2024 17:12
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from 54fdf2b to 5a9dfc6 Compare April 28, 2024 18:07
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch 2 times, most recently from 6569a51 to a17a144 Compare May 12, 2024 19:06
@renovate renovate bot force-pushed the renovate/major-7-mantine-monorepo branch from a17a144 to 60fd9e3 Compare May 26, 2024 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

0 participants