-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(components): add proportion selector
- Loading branch information
1 parent
6586ab1
commit 59d785b
Showing
5 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
components/src/preact/components/min-max-percent-slider.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
16
components/src/preact/components/proportion-selector.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 />; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |