7.14.0

mantinedev/mantine7.14.0Nov 12, 2024by rtivital

AI Summary

A major feature release introducing new chart types, modal stacks, and angle slider components.

Key Highlights

  • Introduce AngleSlider component for circular sliders
  • Introduce RadialBarChart component
  • Introduce FunnelChart component
  • Introduce Modal.Stack and Drawer.Stack components with useModalsStack hook
  • Add Slider restrictToMarks prop

New Features

  • AngleSlider component
  • RadialBarChart component
  • FunnelChart component
  • Modal.Stack and Drawer.Stack components
  • useModalsStack hook
  • Slider restrictToMarks prop

Full Release Notes

[View changelog with demos on mantine.dev website](https://mantine.dev/changelog/7-14-0)

## AngleSlider component

New [AngleSlider](https://mantine.dev/core/angle-slider/) component:

```tsx
import { AngleSlider, Group } from '@mantine/core';

function Demo() {
  return (
    <Group p="lg" gap={50}>
      <AngleSlider
        aria-label="Angle slider"
        formatLabel={(value) => `${value}°`}
        size={100}
        restrictToMarks
        marks={[
          { value: 0 },
          { value: 45 },
          { value: 90 },
          { value: 135 },
          { value: 180 },
          { value: 225 },
          { value: 270 },
          { value: 315 },
        ]}
      />

      <AngleSlider
        aria-label="Angle slider"
        formatLabel={(value) => `${value}°`}
        size={100}
        marks={[
          { value: 0, label: '0°' },
          { value: 45, label: '45°' },
          { value: 90, label: '90°' },
          { value: 135, label: '135°' },
          { value: 180, label: '180°' },
          { value: 225, label: '225°' },
          { value: 270, label: '270°' },
          { value: 315, label: '315°' },
        ]}
      />
    </Group>
  );
}
```

## RadialBarChart component

New [RadialBarChart](https://mantine.dev/charts/radial-bar-chart/) component:

```tsx
import { RadialBarChart } from '@mantine/charts';

const data = [
  { name: '18-24', value: 31.47, color: 'blue.7' },
  { name: '25-29', value: 26.69, color: 'orange.6' },
  { name: '30-34', value: 15.69, color: 'yellow.7' },
  { name: '35-39', value: 8.22, color: 'cyan.6' },
  { name: '40-49', value: 8.63, color: 'green' },
  { name: '50+', value: 2.63, color: 'pink' },
  { name: 'unknown', value: 6.67, color: 'gray' },
];

function Demo() {
  return <RadialBarChart data={data} dataKey="value" h={280} withLabels />;
}
```

## FunnelChart component

New [FunnelChart](https://mantine.dev/charts/funnel-chart/) component:

```tsx
import { FunnelChart } from '@mantine/charts';

const data = [
  { name: 'USA', value: 400, color: 'indigo.6' },
  { name: 'India', value: 300, color: 'yellow.6' },
  { name: 'Japan', value: 100, color: 'teal.6' },
  { name: 'Other', value: 200, color: 'gray.6' },
];

function Demo() {
  return <FunnelChart data={data} />;
}
```

## Modal.Stack and Drawer.Stack components

New [Modal.Stack](https://mantine.dev/core/modal/) and [Drawer.Stack](https://mantine.dev/core/drawer) components simplify usage of multiple modals/drawers at the same time.

Use `Modal.Stack` component to render multiple modals at the same time.
`Modal.Stack` keeps track of opened modals, manages z-index values, focus trapping
and `closeOnEscape` behavior. `Modal.Stack` is designed to be used with `useModalsStack` hook.

Differences from using multiple `Modal` components:

- `Modal.Stack` manages z-index values – modals that are opened later will always have higher z-index value disregarding their order in the DOM
- `Modal.Stack` disables focus trap and `Escape` key handling for all modals except the one that is currently opened
- Modals that are not currently opened are present in the DOM but are hidden with `opacity: 0` and `pointer-events: none`
- Only one overlay is rendered at a time

```tsx
import { Button, Group, Modal, useModalsStack } from '@mantine/core';

function Demo() {
  const stack = useModalsStack(['delete-page', 'confirm-action', 'really-confirm-action']);

  return (
    <>
      <Modal.Stack>
        <Modal {...stack.register('delete-page')} title="Delete this page?">
          Are you sure you want to delete this page? This action cannot be undone.
          <Group mt="lg" justify="flex-end">
            <Button onClick={stack.closeAll} variant="default">
              Cancel
            </Button>
            <Button onClick={() => stack.open('confirm-action')} color="red">
              Delete
            </Button>
          </Group>
        </Modal>

        <Modal {...stack.register('confirm-action')} title="Confirm action">
          Are you sure you want to perform this action? This action cannot be undone. If you are
          sure, press confirm button below.
          <Group mt="lg" justify="flex-end">
            <Button onClick={stack.closeAll} variant="default">
              Cancel
            </Button>
            <Button onClick={() => stack.open('really-confirm-action')} color="red">
              Confirm
            </Button>
          </Group>
        </Modal>

        <Modal {...stack.register('really-confirm-action')} title="Really confirm action">
          Jokes aside. You have confirmed this action. This is your last chance to cancel it. After
          you press confirm button below, action will be performed and cannot be undone. For real
          this time. Are you sure you want to proceed?
          <Group mt="lg" justify="flex-end">
            <Button onClick={stack.closeAll} variant="default">
              Cancel
            </Button>
            <Button onClick={stack.closeAll} color="red">
              Confirm
            </Button>
          </Group>
        </Modal>
      </Modal.Stack>

      <Button onClick={() => stack.open('delete-page')}>Open modal</Button>
    </>
  );
}
```

## useModalsStack/useDrawersStack hooks

`useModalsStack` hook provides an easy way to control multiple modals at the same time.
It accepts an array of unique modals ids and returns an object with the following properties:

```tsx
interface ModalStackReturnType<T extends string> {
  // Current opened state of each modal
  state: Record<T, boolean>;

  // Opens modal with the given id
  open: (id: T) => void;

  // Closes modal with the given id
  close: (id: T) => void;

  // Toggles modal with the given id
  toggle: (id: T) => void;

  // Closes all modals within the stack
  closeAll: () => void;

  // Returns props for modal with the given id
  register: (id: T) => {
    opened: boolean;
    onClose: () => void;
    stackId: T;
  };
}
```

Example of using `useModalsStack` with `Modal` component:

```tsx
import { Modal, useModalsStack } from '@mantine/core';

function Demo() {
  const stack = useModalsStack(['first', 'second']);

  return (
    <>
      <Modal {...stack.register('first')}>First</Modal>
      <Modal {...stack.register('second')}>Second</Modal>
      <Button onClick={() => stack.open('first')}>Open first</Button>
    </>
  );
}
```

## Restrict Slider selection to marks

[Slider](https://mantine.dev/core/slider) component now supports `restrictToMarks` prop that restricts slider value to marks only.
Note that in this case `step` prop is ignored:

```tsx
import { Slider } from '@mantine/core';

function Demo() {
  return (
    <Slider
      restrictToMarks
      defaultValue={25}
      marks={Array.from({ length: 5 }).map((_, index) => ({ value: index * 25 }))}
    />
  );
}
```

## BarChart SVG pattern fill

[BarChart](https://mantine.dev/charts/bar-chart) now can be used with SVG pattern fill:

```tsx
import { BarChart } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <BarChart
      h={300}
      data={mixedStackData}
      dataKey="month"
      series={[
        { name: 'Smartphones', color: 'url(#crosshatch)', stackId: 'a' },
        { name: 'Laptops', color: 'blue.6', stackId: 'b' },
        { name: 'Tablets', color: 'url(#diagonalStripes)', stackId: 'b' },
      ]}
    >
      <defs>
        <pattern
          id="diagonalStripes"
          patternUnits="userSpaceOnUse"
          width={6}
          height={8}
          patternTransform="rotate(45)"
        >
          <rect
            width="2"
            height="8"
            transform="translate(0,0)"
            fill="color-mix(in lch, var(--mantine-color-teal-6) 70%, rgba(0,0,0,0))"
          />
        </pattern>

        <pattern id="crosshatch" patternUnits="userSpaceOnUse" width={8} height={8}>
          <path
            d="M 0 0 L 8 0 L 8 8 L 0 8 Z"
            fill="none"
            stroke="color-mix(in lch, var(--mantine-color-indigo-6) 70%, rgba(0,0,0,0))"
            strokeWidth="1"
          />
          <path
            d="M 0 0 L 8 8"
            stroke="color-mix(in lch, var(--mantine-color-indigo-6) 70%, rgba(0,0,0,0))"
            strokeWidth="1"
          />
          <path
            d="M 8 0 L 0 8"
            stroke="color-mix(in lch, var(--mantine-color-indigo-6) 70%, rgba(0,0,0,0))"
            strokeWidth="1"
          />
        </pattern>
      </defs>
    </BarChart>
  );
}
```

## Help center updates

- New [Can I use nested inline styles with Mantine components?](https://help.mantine.dev/q/nested-inline-styles) question
- New [Can I use PostCSS function in inline styles?](https://help.mantine.dev/q/postcss-fns-inline) question
- New [Why my Carousel slides are in vertical orientation?](https://help.mantine.dev/q/carousel-missing-styles) question
- New [My buttons are transparent and the background is visible only on hover, what is wrong?](https://help.mantine.dev/q/transparent-buttons) question
- New [Can I have different primary color for light and dark color schemes?](https://help.mantine.dev/q/primary-virtual-color) question
- New [How can I change body background color?](https://help.mantine.dev/q/body-background) question
- New [My Popover dropdown closes when I click on the dropdown of nested Popover](https://help.mantine.dev/q/nested-popover-closes) question

## Other changes

- [useTree](https://mantine.dev/core/tree/) hook now accepts `onNodeExpand` and `onNodeCollapse` callbacks
- [useTree](https://mantine.dev/core/tree/) hook now returns additional `checkAllNodes`, `uncheckAllNodes` and `setCheckedState` handlers
- [Tree](https://mantine.dev/core/tree) component now includes `getTreeExpandedState` to generate expanded state based on the tree data
- [use-form](https://mantine.dev/form/use-form) now supports `form.replaceListItem` handler to replace list item at given index