Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slider Label and Value feature #229

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
### Changelog

#### 5.6.0

##### Fixes

- Fixed an issue where alpha value was reset to 1 after sliding both alpha and
any of the r/g/b sliders (#227).
- Fixed an issue with inaccurate kelvin conversion (replaced with more accurate script).

##### Additions

- Added optional label and input elements. Picker color can now be set via input field.
- Added optional `showInput`, `showLabel` and `disabled` slider options for displaying an input field and/or label field next to a slider.
- Added optional `sliderLength` slider option to set each slider's dimension seperately.

##### Changes
- Utilize display:flex on the entire wrapper component.
- Wrap Slider components in a flex wrapper for easy positioning of elements.
- Add IroColor.raw_rgb getter for kelvin conversion issues (returns float numbers instead of int).

#### 5.5.2

##### Fixes
Expand Down
20 changes: 20 additions & 0 deletions dist/Input.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { h } from 'preact';
import { LayoutDirection } from '@irojs/iro-core';
import { IroColor, SliderType } from '@irojs/iro-core';
interface IroInputProps {
sliderType: SliderType;
sliderSize: number;
activeColor: IroColor;
layoutDirection: LayoutDirection;
handleRadius: number;
disabled: boolean;
minTemperature: number;
maxTemperature: number;
}
export declare function IroInput(props: IroInputProps): h.JSX.Element;
export declare namespace IroInput {
var defaultProps: {
disabled: boolean;
};
}
export {};
10 changes: 10 additions & 0 deletions dist/Label.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { h } from 'preact';
import { LayoutDirection } from '@irojs/iro-core';
import { SliderType } from '@irojs/iro-core';
interface IroLabelProps {
sliderType: SliderType;
layoutDirection: LayoutDirection;
handleRadius: number;
}
export declare function IroLabel(props: IroLabelProps): h.JSX.Element;
export {};
4 changes: 4 additions & 0 deletions dist/Slider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { IroComponentProps } from './ComponentTypes';
interface IroSliderProps extends IroComponentProps {
sliderType: SliderType;
sliderShape: SliderShape;
sliderSize: number;
minTemperature: number;
maxTemperature: number;
showInput: boolean;
showLabel: boolean;
disabled: boolean;
}
export declare function IroSlider(props: IroSliderProps): h.JSX.Element;
export declare namespace IroSlider {
Expand Down
419 changes: 335 additions & 84 deletions dist/iro.es.js

Large diffs are not rendered by default.

423 changes: 337 additions & 86 deletions dist/iro.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/iro.min.js

Large diffs are not rendered by default.

419 changes: 335 additions & 84 deletions docs/.vuepress/theme/js/iro.es.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"main": "dist/iro.js",
"types": "dist/index.d.ts",
"dependencies": {
"@irojs/iro-core": "^1.2.1",
"@irojs/iro-core": "file:../iro-core",
"preact": "^10.0.0"
},
"devDependencies": {
Expand Down
5 changes: 3 additions & 2 deletions src/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class IroColorPicker extends Component<ColorPickerProps, ColorPickerState
public static defaultProps: ColorPickerProps = {
...iroColorPickerOptionDefaults,
colors: [],
display: 'block',
display: 'flex',
id: null,
layout: 'default',
margin: null
Expand Down Expand Up @@ -310,7 +310,8 @@ export class IroColorPicker extends Component<ColorPickerProps, ColorPickerState
class="IroColorPicker"
id={ state.id }
style={{
display: state.display
display: state.display,
flexDirection: props.layoutDirection === 'horizontal' ? 'row' : 'column'
}}
>
{ layout.map(({component: UiComponent, options: options }, componentIndex: number) => (
Expand Down
2 changes: 1 addition & 1 deletion src/ComponentWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class IroComponentWrapper extends Component<Props, State> {

const rootStyles = {
overflow: 'visible',
display: isHorizontal ? 'inline-block' : 'block'
display: isHorizontal ? 'inline-flex' : 'flex'
};

// first component shouldn't have any margin
Expand Down
96 changes: 96 additions & 0 deletions src/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { h } from 'preact';
import { cssValue, LayoutDirection } from '@irojs/iro-core';
import { useCallback, useState } from 'preact/hooks';
import {
IroColor,
SliderType,
getSliderValueFromInputField,
getSliderValueFromClipboard,
getInputDimensions,
clampSliderValue
} from '@irojs/iro-core';

interface IroInputProps {
sliderType: SliderType;
sliderSize: number;
activeColor: IroColor;
layoutDirection: LayoutDirection;
handleRadius: number;
disabled: boolean;
minTemperature: number;
maxTemperature: number;
}

export function IroInput(props: IroInputProps) {
const disabled = props.disabled;
const type = props.sliderType;
const {inputWidth, fontSize} = getInputDimensions(props);
const activeColor = props.activeColor;
const [sliderValue, setSliderValue] = useState(activeColor[props.sliderType]);
const val = (type === 'alpha') ? activeColor[props.sliderType].toFixed(2) : Math.round(activeColor[props.sliderType]);
setSliderValue(val);

const onKeypress = useCallback((e: KeyboardEvent) => {
const value = getSliderValueFromInputField(e);

if (type === 'kelvin') {
let strlen = value.toString().length,
minlen = props.minTemperature.toString().length,
maxlen = props.maxTemperature.toString().length;

if (strlen > maxlen) {
e.preventDefault();
activeColor[props.sliderType] = props.maxTemperature;
} else if (strlen >= minlen) {
if (value < props.minTemperature) {
if (maxlen === minlen) {
e.preventDefault();
activeColor[props.sliderType] = props.minTemperature;
}
} else if (value > props.maxTemperature) {
e.preventDefault();
activeColor[props.sliderType] = props.maxTemperature;
} else {
e.preventDefault();
activeColor[props.sliderType] = value;
}
}
} else {
e.preventDefault();
activeColor[props.sliderType] = clampSliderValue(props, value);
}
return value;
}, [setSliderValue, props.sliderType]);

const onPaste = useCallback((e: ClipboardEvent) => {
e.preventDefault();
const value = getSliderValueFromClipboard(props, e);
activeColor[props.sliderType] = value;
return value;
}, [setSliderValue, props.sliderType]);

return (
<div className="IroSliderValue">
<input
onKeyPress={ onKeypress }
onPaste={ onPaste }
className="IroSliderInput"
style={{
display: 'inline-block',
width: type === 'kelvin' ? cssValue(40) : inputWidth,
height: cssValue(18),
fontSize: fontSize,
padding: cssValue(2)
}}
type="text"
disabled={ disabled }
value={ sliderValue }
>
</input>
</div>
);
}

IroInput.defaultProps = {
disabled: false
};
28 changes: 28 additions & 0 deletions src/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { h } from 'preact';
import { cssValue, LayoutDirection } from '@irojs/iro-core';
import { SliderType } from '@irojs/iro-core';

interface IroLabelProps {
sliderType: SliderType;
layoutDirection: LayoutDirection;
handleRadius: number;
}

export function IroLabel(props: IroLabelProps) {
const name = props.sliderType[0].toUpperCase();

return (
<div
className="IroSliderLabel"
style={{
display: 'inline-block',
width: cssValue(10),
height: cssValue(12),
lineHeight: cssValue(12),
fontSize: props.layoutDirection === 'horizontal' ? cssValue(12) : cssValue(14)
}}
>
{name}
</div>
);
}
107 changes: 73 additions & 34 deletions src/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { h } from 'preact';
import {
IroColor,
SliderShape,
SliderType,
sliderDefaultOptions,
getSliderDimensions,
getSliderValueFromInput,
getSliderValueFromInput,
getSliderHandlePosition,
getSliderGradient,
cssBorderStyles,
Expand All @@ -16,12 +15,18 @@ import {
import { IroComponentWrapper } from './ComponentWrapper';
import { IroComponentProps, IroInputType } from './ComponentTypes';
import { IroHandle } from './Handle';
import { IroInput } from './Input';
import { IroLabel } from './Label';

interface IroSliderProps extends IroComponentProps {
sliderType: SliderType;
sliderShape: SliderShape;
sliderSize: number;
minTemperature: number;
maxTemperature: number;
showInput: boolean; // show input fields for manual value input
showLabel: boolean; // show label for slider
disabled: boolean; // enable / disable manual value input
};

export function IroSlider(props: IroSliderProps) {
Expand All @@ -35,52 +40,86 @@ export function IroSlider(props: IroSliderProps) {
const value = getSliderValueFromInput(props, x, y);
props.parent.inputActive = true;
activeColor[props.sliderType] = value;
if (props.sliderType === 'kelvin') {
activeColor._kelvin = value;
}
props.onInput(type, props.id);
}

return (
<IroComponentWrapper {...props} onInput={ handleInput }>
{(uid, rootProps, rootStyles) => (
// add wrapper element
<div
{ ...rootProps }
className="IroSlider"
style={{
position: 'relative',
width: cssValue(width),
height: cssValue(height),
borderRadius: cssValue(radius),
// checkered bg to represent alpha
background: `conic-gradient(#ccc 25%, #fff 0 50%, #ccc 0 75%, #fff 0)`,
backgroundSize: '8px 8px',
className="IroSliderWrapper"
style={{
width: props.layoutDirection === 'vertical' ? cssValue(props.width) : 'unset',
height: props.layoutDirection === 'horizontal' ? cssValue(props.width) : 'unset',
flexDirection: props.layoutDirection === 'horizontal' ? 'column' : 'row',
alignItems: 'center',
justifyContent: 'space-between',
...rootStyles
}}
>
<div
className="IroSliderGradient"
{ ...rootProps }
className="IroSlider"
style={{
position: 'absolute',
top: 0,
left: 0,
width: `100%`,
height: `100%`,
position: 'relative',
display: 'block',
width: cssValue(width),
height: cssValue(height),
borderRadius: cssValue(radius),
background: cssGradient(
'linear',
props.layoutDirection === 'horizontal' ? 'to top' : 'to right',
gradient
),
...cssBorderStyles(props)
// checkered bg to represent alpha
background: `conic-gradient(#ccc 25%, #fff 0 50%, #ccc 0 75%, #fff 0)`,
backgroundSize: '8px 8px',
}}
/>
<IroHandle
isActive={ true }
index={ activeColor.index }
r={ props.handleRadius }
url={ props.handleSvg }
props={ props.handleProps }
x={ handlePos.x }
y={ handlePos.y } // todo: use percentage
/>
>
<div
className="IroSliderGradient"
style={{
position: 'absolute',
top: 0,
left: 0,
width: `100%`,
height: `100%`,
borderRadius: cssValue(radius),
background: cssGradient(
'linear',
props.layoutDirection === 'horizontal' ? 'to top' : 'to right',
gradient
),
...cssBorderStyles(props)
}}
/>
<IroHandle
isActive={ true }
index={ activeColor.index }
r={ props.handleRadius }
url={ props.handleSvg }
props={ props.handleProps }
x={ handlePos.x }
y={ handlePos.y } // todo: use percentage
/>
</div>
{props.showLabel && (<IroLabel
sliderType={props.sliderType}
layoutDirection={ props.layoutDirection }
handleRadius={ props.handleRadius }
/>
)}
{props.showInput && (
<IroInput
disabled={ props.disabled }
sliderType={ props.sliderType }
sliderSize={ props.sliderSize }
activeColor={ activeColor }
handleRadius={ props.handleRadius }
layoutDirection={ props.layoutDirection }
minTemperature={ props.minTemperature }
maxTemperature={ props.maxTemperature }
/>
)}
</div>
)}
</IroComponentWrapper>
Expand Down