Skip to content

Commit

Permalink
feat(components): add proportion selector
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKellerer committed Apr 4, 2024
1 parent 6586ab1 commit 59d785b
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 0 deletions.
40 changes: 40 additions & 0 deletions components/src/preact/components/min-max-percent-slider.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
pointer-events: all;
width: 24px;
height: 24px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 0 1px #C6C6C6;
cursor: pointer;
}

input[type=range]::-moz-range-thumb {
-webkit-appearance: none;
pointer-events: all;
width: 24px;
height: 24px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 0 1px #C6C6C6;
cursor: pointer;
}

input[type=range]::-webkit-slider-thumb:hover {
background: #f7f7f7;
}

input[type=range]::-webkit-slider-thumb:active {
box-shadow: inset 0 0 3px #387bbe, 0 0 9px #387bbe;
-webkit-box-shadow: inset 0 0 3px #387bbe, 0 0 9px #387bbe;
}

input[type="range"] {
-webkit-appearance: none;
appearance: none;
height: 2px;
width: 100%;
position: absolute;
background-color: #C6C6C6;
pointer-events: none;
}
95 changes: 95 additions & 0 deletions components/src/preact/components/min-max-range-slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { FunctionComponent } from 'preact';
import { useState } from 'preact/hooks';
import { ChangeEvent } from 'react';
import './min-max-percent-slider.css';

export interface MinMaxPercentSliderProps {
min: number;
max: number;
setMin: (min: number) => void;
setMax: (max: number) => void;
rangeMin?: number;
rangeMax?: number;
step?: number;
}

export const MinMaxRangeSlider: FunctionComponent<MinMaxPercentSliderProps> = ({
min,
max,
setMin,
setMax,
rangeMin = 0,
rangeMax = 100,
step = 0.1,
}) => {
const sliderColor = '#C6C6C6';
const rangeColor = '#387bbe';

const [zIndexTo, setZIndexTo] = useState(0);

const onMinChange = (event: ChangeEvent<HTMLInputElement>) => {
const input = event.target as HTMLInputElement;
const minValue = Number(input.value);

if (minValue > max) {
setMax(minValue);
setMin(minValue);
} else {
setMin(minValue);
}
};

const onMaxChange = (event: ChangeEvent<HTMLInputElement>) => {
const input = event.target as HTMLInputElement;
const maxValue = Number(input.value);

if (maxValue <= 0) {
setZIndexTo(2);
} else {
setZIndexTo(0);
}

if (maxValue < min) {
setMin(maxValue);
setMax(maxValue);
} else {
setMax(maxValue);
}
};

const background = `
linear-gradient(
to right,
${sliderColor} 0%,
${sliderColor} ${min}%,
${rangeColor} ${min}%,
${rangeColor} ${max}%,
${sliderColor} ${max}%,
${sliderColor} 100%)
`;

return (
<div class='my-4 relative'>
<input
id='fromSlider'
type='range'
value={min}
onInput={onMinChange}
min={rangeMin}
max={rangeMax}
step={step}
style={{ background, zIndex: 1, height: 0 }}
/>
<input
id='toSlider'
type='range'
value={max}
min={rangeMin}
max={rangeMax}
step={step}
onInput={onMaxChange}
style={{ background, zIndex: zIndexTo }}
/>
</div>
);
};
46 changes: 46 additions & 0 deletions components/src/preact/components/percent-intput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { FunctionComponent } from 'preact';
import { useState } from 'preact/hooks';
import { ChangeEvent } from 'react';

export type PercentInputProps = {
percentage: number;
setPercentage: (percentage: number) => void;
};

const percentageInRange = (percentage: number) => {
return percentage <= 100 && percentage >= 0;
};

export const PercentInput: FunctionComponent<PercentInputProps> = ({ percentage, setPercentage }) => {
const [error, setError] = useState(!percentageInRange(percentage));

const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const input = event.target as HTMLInputElement;
const value = Number(input.value);

const inRange = percentageInRange(value);

if (!inRange) {
setError(true);
} else {
setError(false);
setPercentage(value);
}
};

return (
<label className={`input input-bordered flex items-center gap-2 w-32 ${error ? 'input-error' : ''}`}>
<input
type='number'
step={0.1}
min={0}
max={100}
value={percentage}
onInput={handleInputChange}
lang='en'
className={`grow w-16`}
/>
%
</label>
);
};
16 changes: 16 additions & 0 deletions components/src/preact/components/proportion-selector.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Meta, StoryObj } from '@storybook/preact';
import { ProportionSelector, ProportionSelectorProps } from './proportion-selector';

const meta: Meta<ProportionSelectorProps> = {
title: 'Component/Proportion selector',
component: ProportionSelector,
parameters: { fetchMock: {} },
};

export default meta;

export const ProportionSelectorStory: StoryObj<ProportionSelectorProps> = {
render: () => {
return <ProportionSelector />;
},
};
35 changes: 35 additions & 0 deletions components/src/preact/components/proportion-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FunctionComponent } from 'preact';
import { useState } from 'preact/hooks';
import { MinMaxRangeSlider } from './min-max-range-slider';
import { PercentInput } from './percent-intput';

export interface ProportionSelectorProps {}

export const ProportionSelector: FunctionComponent<ProportionSelectorProps> = () => {
const [minProportion, setMinProportion] = useState(0);
const [maxProportion, setMaxProportion] = useState(1);

return (
<div class='flex flex-col w-64'>
<div class='flex items-center '>
<PercentInput
percentage={minProportion * 100}
setPercentage={(percentage) => setMinProportion(percentage / 100)}
/>
<div class='m-2'>-</div>
<PercentInput
percentage={maxProportion * 100}
setPercentage={(percentage) => setMaxProportion(percentage / 100)}
/>
</div>
<div class='my-1'>
<MinMaxRangeSlider
min={minProportion * 100}
max={maxProportion * 100}
setMin={(percentage) => setMinProportion(percentage / 100)}
setMax={(percentage) => setMaxProportion(percentage / 100)}
/>
</div>
</div>
);
};

0 comments on commit 59d785b

Please sign in to comment.