Skip to content

Commit

Permalink
RC #291 - Adds the FacetSlider component
Browse files Browse the repository at this point in the history
  • Loading branch information
dleadbetter committed Jul 30, 2024
1 parent cf205d5 commit 488d473
Show file tree
Hide file tree
Showing 5 changed files with 468 additions and 5 deletions.
10 changes: 6 additions & 4 deletions packages/core-data/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/core-data",
"version": "2.2.8",
"version": "2.2.7",
"description": "A package of components used with the Core Data platform.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand All @@ -24,6 +24,8 @@
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-slider": "^1.2.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@samvera/clover-iiif": "^2.3.2",
"@turf/turf": "^6.5.0",
"clsx": "^2.1.0",
Expand All @@ -37,8 +39,8 @@
"underscore": "^1.13.2"
},
"peerDependencies": {
"@performant-software/shared-components": "^2.2.8",
"@performant-software/geospatial": "^2.2.8",
"@performant-software/geospatial": "^2.2.7",
"@performant-software/shared-components": "^2.2.7",
"@peripleo/maplibre": "^0.5.2",
"@peripleo/peripleo": "^0.5.2",
"react": ">= 16.13.1 < 19.0.0",
Expand All @@ -57,4 +59,4 @@
"vite": "^5.1.4",
"vite-plugin-copy": "^0.1.6"
}
}
}
227 changes: 227 additions & 0 deletions packages/core-data/src/components/FacetSlider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// @flow

import { Timer } from '@performant-software/shared-components';
import * as Slider from '@radix-ui/react-slider';
import * as Tooltip from '@radix-ui/react-tooltip';
import { clsx } from 'clsx';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import React, { useCallback, useEffect, useState } from 'react';

type MarkerProps = {
className?: string,
value: number
};

const SliderMarker = (props: MarkerProps) => {
const [initialized, setInitialized] = useState(false);
const [open, setOpen] = useState(false);

/**
* If initialized, sets the "open" state to "true".
*/
useEffect(() => {
if (!initialized) {
return;
}

setOpen(true);

Timer.clearSearchTimer();
Timer.setSearchTimer(() => setOpen(false));
}, [props.value]);

/**
* Sets the initialized state.
*/
useEffect(() => {
if (!initialized) {
setInitialized(true);
}
}, [props.value]);

return (
<Tooltip.Provider>
<Tooltip.Root
open={open}
>
<Tooltip.Trigger
asChild
>
<Slider.Thumb
className={clsx(
'block h-5 w-5 rounded-full bg-gray-600',
'focus:outline-none focus-visible:ring focus-visible:ring-black focus-visible:ring-opacity-30',
props.className
)}
onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
/>
</Tooltip.Trigger>
<Tooltip.Content
sideOffset={5}
>
<div
className='bg-white p-2 text-black rounded-md shadow-md'
>
{ props.value }
</div>
<Tooltip.Arrow
className='fill-white'
/>
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
);
};

type Props = {
/**
* Custom Tailwind CSS class names.
*/
classNames: {
button: string,
range: string,
root: string,
thumb: string,
track: string
},

/**
* The maximum facet value.
*/
max: number,

/**
* The minimum facet value.
*/
min: number,

/**
* Callback fired when the range is changed.
*/
onChange?: ([number, number]) => void,

/**
* Number of steps to increment the slider.
*/
step?: number
};

const FacetSlider = (props: Props) => {
const [range, setRange] = useState([props.min, props.max]);

/**
* Callback fired when the left button is clicked. This function decrements the min range value by the "step" prop.
*
* @type {(function(): void)|*}
*/
const onLeft = useCallback(() => {
const [start, end] = range;

let newStart = start - props.step;
if (newStart < props.min) {
newStart = props.min;
}

setRange([newStart, end]);
}, [range, props.min, props.step]);

/**
* Callback fired when the right button is clicked. This function increments the max range value by the "step" prop.
*
* @type {(function(): void)|*}
*/
const onRight = useCallback(() => {
const [start, end] = range;

let newEnd = end + props.step;
if (newEnd > props.max) {
newEnd = props.max;
}

setRange([start, newEnd]);
}, [range, props.max, props.step]);

/**
* Calls the onChange prop when the range value changes.
*/
useEffect(() => {
if (props.onChange) {
props.onChange(range);
}
}, [range]);

return (
<>
<div
className='flex justify-between items-center pt-4'
>
<button
aria-label='Slider Left'
className={clsx('px-3 py-3', props.classNames.button)}
onClick={onLeft}
type='button'
>
<ChevronLeft />
</button>
<Slider.Root
className={clsx(
'relative flex flex-grow h-5 touch-none items-center w-full',
props.classNames.root
)}
max={props.max}
min={props.min}
minStepsBetweenThumbs={1}
onValueChange={setRange}
step={1}
value={range}
>
<Slider.Track
className={clsx(
'relative h-1 w-full grow bg-gray-100',
props.classNames.track
)}
>
<Slider.Range
className={clsx(
'absolute h-full bg-gray-600',
props.classNames.range
)}
/>
</Slider.Track>
<SliderMarker
className={props.classNames.thumb}
value={range[0]}
/>
<SliderMarker
className={props.classNames.thumb}
value={range[1]}
/>
</Slider.Root>
<button
aria-label='Slider Right'
className={clsx('px-3 py-3', props.classNames.button)}
onClick={onRight}
type='button'
>
<ChevronRight />
</button>
</div>
<div
className='flex justify-between w-full px-12'
>
<div>{ props.min }</div>
<div>{ props.max }</div>
</div>
</>
);
};

FacetSlider.defaultProps = {
classNames: {},
step: 1,
};

export default FacetSlider;
5 changes: 4 additions & 1 deletion packages/core-data/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export default {
extend: {
colors: {
'event-selected': '#3758F9',
muted: '#0005119e'
muted: '#0005119e',
gray: {
1000: '#505A6A'
}
},
fontFamily: {
sans: [
Expand Down
35 changes: 35 additions & 0 deletions packages/storybook/src/core-data/FacetSlider.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @flow

import React from 'react';
import FacetSlider from '../../../core-data/src/components/FacetSlider';

export default {
title: 'Components/Core Data/FacetSlider',
component: FacetSlider
};

export const Default = () => (
<FacetSlider
markerStep={100}
min={1500}
max={2010}
/>
);

export const CustomStyles = () => (
<div
className='bg-gray-1000 text-white fill-white py-7 rounded-md'
>
<FacetSlider
classNames={{
button: 'px-4',
range: 'bg-white',
thumb: 'bg-white',
track: 'bg-gray-400'
}}
markerStep={100}
min={1500}
max={2010}
/>
</div>
);
Loading

0 comments on commit 488d473

Please sign in to comment.