FloatingIndicator
Display a floating indicator over a group of elements
Import
Source
Docs
Package
Usage
FloatingIndicator
is designed to highlight active element in a group.
It can be used to create custom segmented controls, tabs and other similar components.
FloatingIndicator
renders an element over the target
element. To calculate the position it is
required to pass parent
element which has a relative position.
By default, FloatingIndicator
does not have any visible styles. You can use className
prop
or Styles API to apply styles.
import { useState } from 'react';
import { FloatingIndicator, UnstyledButton } from '@mantine/core';
import classes from './Demo.module.css';
const data = ['React', 'Vue', 'Angular', 'Svelte'];
function Demo() {
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const [active, setActive] = useState(0);
const setControlRef = (index: number) => (node: HTMLButtonElement) => {
controlsRefs[index] = node;
setControlsRefs(controlsRefs);
};
const controls = data.map((item, index) => (
<UnstyledButton
key={item}
className={classes.control}
ref={setControlRef(index)}
onClick={() => setActive(index)}
mod={{ active: active === index }}
>
<span className={classes.controlLabel}>{item}</span>
</UnstyledButton>
));
return (
<div className={classes.root} ref={setRootRef}>
{controls}
<FloatingIndicator
target={controlsRefs[active]}
parent={rootRef}
className={classes.indicator}
/>
</div>
);
}
Multiple rows
FloatingIndicator
can be used to highlight active element in a group with multiple rows:
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>
);
}
Example: Tabs
First tab content
Second tab content
Third tab content
import { useState } from 'react';
import { FloatingIndicator, Tabs } from '@mantine/core';
import classes from './Demo.module.css';
function Demo() {
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [value, setValue] = useState<string | null>('1');
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const setControlRef = (val: string) => (node: HTMLButtonElement) => {
controlsRefs[val] = node;
setControlsRefs(controlsRefs);
};
return (
<Tabs variant="none" value={value} onChange={setValue}>
<Tabs.List ref={setRootRef} className={classes.list}>
<Tabs.Tab value="1" ref={setControlRef('1')} className={classes.tab}>
First tab
</Tabs.Tab>
<Tabs.Tab value="2" ref={setControlRef('2')} className={classes.tab}>
Second tab
</Tabs.Tab>
<Tabs.Tab value="3" ref={setControlRef('3')} className={classes.tab}>
Third tab
</Tabs.Tab>
<FloatingIndicator
target={value ? controlsRefs[value] : null}
parent={rootRef}
className={classes.indicator}
/>
</Tabs.List>
<Tabs.Panel value="1">First tab content</Tabs.Panel>
<Tabs.Panel value="2">Second tab content</Tabs.Panel>
<Tabs.Panel value="3">Third tab content</Tabs.Panel>
</Tabs>
);
}
Welcome to Mantine, React components library that you always wished for
Build fully functional accessible web applications faster than ever