@startupjs-ui/range-input 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/Label/index.cssx.styl +31 -0
- package/Label/index.tsx +41 -0
- package/README.mdx +164 -0
- package/index.cssx.styl +34 -0
- package/index.d.ts +52 -0
- package/index.tsx +117 -0
- package/package.json +22 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/range-input
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
|
|
20
|
+
* **docs:** support static web bundling (each route as a separate html page) ([5e4738c](https://github.com/startupjs/startupjs-ui/commit/5e4738c50157ae37e14d8dc36cb43d6a45a008ad))
|
|
21
|
+
* **range-input:** refactor RangeInput component ([e1c4a74](https://github.com/startupjs/startupjs-ui/commit/e1c4a742fcc0e416b7129364b40ba3f2aef59971))
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.root
|
|
2
|
+
position relative
|
|
3
|
+
|
|
4
|
+
.label
|
|
5
|
+
position absolute
|
|
6
|
+
top 2px
|
|
7
|
+
width 12u
|
|
8
|
+
align-items center
|
|
9
|
+
|
|
10
|
+
// todo: implement common tooltip style
|
|
11
|
+
.text
|
|
12
|
+
background-color var(--Range-labelBg)
|
|
13
|
+
padding 0.5u 1u
|
|
14
|
+
font-style normal
|
|
15
|
+
font(caption)
|
|
16
|
+
color var(--Range-labelText)
|
|
17
|
+
shadow(3)
|
|
18
|
+
radius()
|
|
19
|
+
|
|
20
|
+
.arrow
|
|
21
|
+
width 0
|
|
22
|
+
height 0
|
|
23
|
+
background-color transparent
|
|
24
|
+
border-style solid
|
|
25
|
+
border-left-width 4px
|
|
26
|
+
border-right-width 4px
|
|
27
|
+
border-bottom-width 4px
|
|
28
|
+
border-left-color transparent
|
|
29
|
+
border-right-color transparent
|
|
30
|
+
border-bottom-color var(--Range-labelBg)
|
|
31
|
+
transform rotate(180deg)
|
package/Label/index.tsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type ReactNode } from 'react'
|
|
2
|
+
import { type LabelProps as MultiSliderLabelProps } from '@ptomasroos/react-native-multi-slider'
|
|
3
|
+
import { pug } from 'startupjs'
|
|
4
|
+
import Div from '@startupjs-ui/div'
|
|
5
|
+
import Span from '@startupjs-ui/span'
|
|
6
|
+
import STYLES from './index.cssx.styl'
|
|
7
|
+
|
|
8
|
+
function Label ({
|
|
9
|
+
oneMarkerValue,
|
|
10
|
+
twoMarkerValue,
|
|
11
|
+
oneMarkerLeftPosition,
|
|
12
|
+
twoMarkerLeftPosition,
|
|
13
|
+
oneMarkerPressed,
|
|
14
|
+
twoMarkerPressed
|
|
15
|
+
}: MultiSliderLabelProps): ReactNode {
|
|
16
|
+
// Number.isFinite - This condition has been taken from original vendor component.
|
|
17
|
+
// Be aware when you change this.
|
|
18
|
+
const showOne = oneMarkerPressed && Number.isFinite(oneMarkerLeftPosition) &&
|
|
19
|
+
Number.isFinite(oneMarkerValue)
|
|
20
|
+
const showTwo = twoMarkerPressed && Number.isFinite(twoMarkerLeftPosition) &&
|
|
21
|
+
Number.isFinite(twoMarkerValue)
|
|
22
|
+
|
|
23
|
+
return pug`
|
|
24
|
+
Div.root
|
|
25
|
+
if showOne
|
|
26
|
+
= renderLabel(oneMarkerLeftPosition, oneMarkerValue)
|
|
27
|
+
if showTwo
|
|
28
|
+
= renderLabel(twoMarkerLeftPosition, twoMarkerValue)
|
|
29
|
+
`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function renderLabel (position: number, value: string | number): ReactNode {
|
|
33
|
+
return pug`
|
|
34
|
+
Div.label(style={ left: position - STYLES.label.width / 2 })
|
|
35
|
+
// todo: implement common tooltip style
|
|
36
|
+
Span.text= value
|
|
37
|
+
Span.arrow
|
|
38
|
+
`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default Label
|
package/README.mdx
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import RangeInput, { _PropsJsonSchema as RangeInputPropsJsonSchema } from './index'
|
|
3
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
4
|
+
|
|
5
|
+
# RangeInput
|
|
6
|
+
|
|
7
|
+
RangeInput lets user make selections from a range of values.
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { RangeInput } from 'startupjs-ui'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Simple example
|
|
14
|
+
|
|
15
|
+
```jsx example
|
|
16
|
+
const [value, setValue] = useState(50)
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<RangeInput
|
|
20
|
+
value={value}
|
|
21
|
+
min={0}
|
|
22
|
+
max={100}
|
|
23
|
+
onChange={(val) => setValue(val)}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Two markers
|
|
29
|
+
|
|
30
|
+
By default component has one marker. To enable two makers pass `range=true`.
|
|
31
|
+
|
|
32
|
+
```jsx example
|
|
33
|
+
const [value, setValue] = useState([20, 60])
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<RangeInput
|
|
37
|
+
min={0}
|
|
38
|
+
max={100}
|
|
39
|
+
range
|
|
40
|
+
value={value}
|
|
41
|
+
onChange={(val) => setValue(val)}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Hide marker label
|
|
47
|
+
|
|
48
|
+
Pass `showLabel=false` to hide marker label.
|
|
49
|
+
|
|
50
|
+
```jsx example
|
|
51
|
+
const [value, setValue] = useState(40)
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<RangeInput
|
|
55
|
+
value={value}
|
|
56
|
+
min={0}
|
|
57
|
+
max={100}
|
|
58
|
+
showLabel={false}
|
|
59
|
+
onChange={(val) => setValue(val)}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Step
|
|
65
|
+
|
|
66
|
+
Step with which the marker can step through values. It cannot be negative. We recommend `max - min` to be evenly divisible by the step.
|
|
67
|
+
|
|
68
|
+
```jsx example
|
|
69
|
+
const [value, setValue] = useState(30)
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<RangeInput
|
|
73
|
+
value={value}
|
|
74
|
+
min={0}
|
|
75
|
+
max={100}
|
|
76
|
+
step={10}
|
|
77
|
+
onChange={(val) => setValue(val)}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Show labels and markers for step
|
|
83
|
+
|
|
84
|
+
Pass `showSteps=true` to display labels and markers for step.
|
|
85
|
+
|
|
86
|
+
```jsx example
|
|
87
|
+
const [value, setValue] = useState(4)
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<RangeInput
|
|
91
|
+
value={value}
|
|
92
|
+
min={0}
|
|
93
|
+
max={5}
|
|
94
|
+
showSteps
|
|
95
|
+
onChange={(val) => setValue(val)}
|
|
96
|
+
/>
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
When you pass `showSteps=true` to hide the labels or markers you need to pass `showStepLabels=false` or `showStepMarkers=false`.
|
|
101
|
+
|
|
102
|
+
**Hide labels**
|
|
103
|
+
|
|
104
|
+
```jsx example
|
|
105
|
+
const [value, setValue] = useState(4)
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<RangeInput
|
|
109
|
+
value={value}
|
|
110
|
+
min={0}
|
|
111
|
+
max={5}
|
|
112
|
+
showSteps
|
|
113
|
+
showStepLabels={false}
|
|
114
|
+
onChange={(val) => setValue(val)}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Hide markers**
|
|
120
|
+
|
|
121
|
+
```jsx example
|
|
122
|
+
const [value, setValue] = useState(4)
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<RangeInput
|
|
126
|
+
value={value}
|
|
127
|
+
min={0}
|
|
128
|
+
max={5}
|
|
129
|
+
showLabel={false}
|
|
130
|
+
showSteps
|
|
131
|
+
showStepMarkers={false}
|
|
132
|
+
onChange={(val) => setValue(val)}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Width
|
|
138
|
+
|
|
139
|
+
Use `width` property to specify custom width of input.
|
|
140
|
+
|
|
141
|
+
```jsx example
|
|
142
|
+
const [value, setValue] = useState(30)
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<RangeInput
|
|
146
|
+
value={value}
|
|
147
|
+
min={0}
|
|
148
|
+
max={100}
|
|
149
|
+
width={400}
|
|
150
|
+
onChange={(val) => setValue(val)}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Sandbox
|
|
156
|
+
|
|
157
|
+
<Sandbox
|
|
158
|
+
Component={RangeInput}
|
|
159
|
+
propsJsonSchema={RangeInputPropsJsonSchema}
|
|
160
|
+
props={{
|
|
161
|
+
value: 50,
|
|
162
|
+
onChange: value => alert('New value is ' + JSON.stringify(value))
|
|
163
|
+
}}
|
|
164
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
$this = merge({
|
|
2
|
+
trackColor: var(--color-bg-main-subtle),
|
|
3
|
+
selectedColor: var(--color-bg-primary),
|
|
4
|
+
stepMarker: var(--color-text-on-color)
|
|
5
|
+
}, $UI.RangeInput, true)
|
|
6
|
+
|
|
7
|
+
.root
|
|
8
|
+
&:part(container)
|
|
9
|
+
height 10u
|
|
10
|
+
|
|
11
|
+
&:part(track)
|
|
12
|
+
height 1u
|
|
13
|
+
background-color $this.trackColor
|
|
14
|
+
border-radius 4px
|
|
15
|
+
|
|
16
|
+
&:part(selected)
|
|
17
|
+
background-color $this.selectedColor
|
|
18
|
+
|
|
19
|
+
&:part(marker)
|
|
20
|
+
top 4px
|
|
21
|
+
width 16px
|
|
22
|
+
height @width
|
|
23
|
+
border-radius 10px
|
|
24
|
+
shadow(1)
|
|
25
|
+
|
|
26
|
+
&:part(stepLabel)
|
|
27
|
+
font-style normal
|
|
28
|
+
font(caption)
|
|
29
|
+
color var(--color-text-main)
|
|
30
|
+
|
|
31
|
+
&:part(stepMarker)
|
|
32
|
+
border-radius 0
|
|
33
|
+
width 2px
|
|
34
|
+
background-color $this.stepMarker
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type MultiSliderProps } from '@ptomasroos/react-native-multi-slider';
|
|
5
|
+
import './index.cssx.styl';
|
|
6
|
+
declare const _default: import("react").ComponentType<RangeInputProps>;
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const _PropsJsonSchema: {};
|
|
9
|
+
export interface RangeInputProps {
|
|
10
|
+
/** Custom marker label component @default Label */
|
|
11
|
+
customLabel?: MultiSliderProps['customLabel'];
|
|
12
|
+
/** Show pressed marker label @default true */
|
|
13
|
+
showLabel?: boolean;
|
|
14
|
+
/** Minimum value @default 0 */
|
|
15
|
+
min?: number;
|
|
16
|
+
/** Maximum value @default 100 */
|
|
17
|
+
max?: number;
|
|
18
|
+
/** Enable two markers mode @default false */
|
|
19
|
+
range?: boolean;
|
|
20
|
+
/** Show steps on the track @default false */
|
|
21
|
+
showSteps?: boolean;
|
|
22
|
+
/** Show step labels when showSteps is enabled @default true */
|
|
23
|
+
showStepLabels?: boolean;
|
|
24
|
+
/** Show step markers when showSteps is enabled @default true */
|
|
25
|
+
showStepMarkers?: boolean;
|
|
26
|
+
/** Step size @default 1 */
|
|
27
|
+
step?: number;
|
|
28
|
+
/** Current value (number for single marker, array for two markers) */
|
|
29
|
+
value?: number | number[] | null;
|
|
30
|
+
/** Slider width in pixels @default 280 */
|
|
31
|
+
width?: number;
|
|
32
|
+
/** Style overrides for the container part */
|
|
33
|
+
containerStyle?: any;
|
|
34
|
+
/** Style overrides for the selected track part */
|
|
35
|
+
selectedStyle?: any;
|
|
36
|
+
/** Style overrides for the step label part */
|
|
37
|
+
stepLabelStyle?: any;
|
|
38
|
+
/** Style overrides for the step marker part */
|
|
39
|
+
stepMarkerStyle?: any;
|
|
40
|
+
/** Style overrides for the step part */
|
|
41
|
+
stepStyle?: any;
|
|
42
|
+
/** Style overrides for the track part */
|
|
43
|
+
trackStyle?: any;
|
|
44
|
+
/** Style overrides for the marker part */
|
|
45
|
+
markerStyle?: any;
|
|
46
|
+
/** Change handler */
|
|
47
|
+
onChange?: (value: number | number[]) => void | Promise<void>;
|
|
48
|
+
/** Handler triggered when sliding starts */
|
|
49
|
+
onChangeStart?: MultiSliderProps['onValuesChangeStart'];
|
|
50
|
+
/** Handler triggered when sliding ends */
|
|
51
|
+
onChangeFinish?: MultiSliderProps['onValuesChangeFinish'];
|
|
52
|
+
}
|
package/index.tsx
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useMemo, type ReactNode } from 'react'
|
|
2
|
+
import MultiSlider, { type MultiSliderProps } from '@ptomasroos/react-native-multi-slider'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Label from './Label'
|
|
6
|
+
import './index.cssx.styl'
|
|
7
|
+
|
|
8
|
+
export default observer(themed('RangeInput', RangeInput))
|
|
9
|
+
|
|
10
|
+
export const _PropsJsonSchema = {/* RangeInputProps */}
|
|
11
|
+
|
|
12
|
+
export interface RangeInputProps {
|
|
13
|
+
/** Custom marker label component @default Label */
|
|
14
|
+
customLabel?: MultiSliderProps['customLabel']
|
|
15
|
+
/** Show pressed marker label @default true */
|
|
16
|
+
showLabel?: boolean
|
|
17
|
+
/** Minimum value @default 0 */
|
|
18
|
+
min?: number
|
|
19
|
+
/** Maximum value @default 100 */
|
|
20
|
+
max?: number
|
|
21
|
+
/** Enable two markers mode @default false */
|
|
22
|
+
range?: boolean
|
|
23
|
+
/** Show steps on the track @default false */
|
|
24
|
+
showSteps?: boolean
|
|
25
|
+
/** Show step labels when showSteps is enabled @default true */
|
|
26
|
+
showStepLabels?: boolean
|
|
27
|
+
/** Show step markers when showSteps is enabled @default true */
|
|
28
|
+
showStepMarkers?: boolean
|
|
29
|
+
/** Step size @default 1 */
|
|
30
|
+
step?: number
|
|
31
|
+
/** Current value (number for single marker, array for two markers) */
|
|
32
|
+
value?: number | number[] | null
|
|
33
|
+
/** Slider width in pixels @default 280 */
|
|
34
|
+
width?: number
|
|
35
|
+
/** Style overrides for the container part */
|
|
36
|
+
containerStyle?: any
|
|
37
|
+
/** Style overrides for the selected track part */
|
|
38
|
+
selectedStyle?: any
|
|
39
|
+
/** Style overrides for the step label part */
|
|
40
|
+
stepLabelStyle?: any
|
|
41
|
+
/** Style overrides for the step marker part */
|
|
42
|
+
stepMarkerStyle?: any
|
|
43
|
+
/** Style overrides for the step part */
|
|
44
|
+
stepStyle?: any
|
|
45
|
+
/** Style overrides for the track part */
|
|
46
|
+
trackStyle?: any
|
|
47
|
+
/** Style overrides for the marker part */
|
|
48
|
+
markerStyle?: any
|
|
49
|
+
/** Change handler */
|
|
50
|
+
onChange?: (value: number | number[]) => void | Promise<void>
|
|
51
|
+
/** Handler triggered when sliding starts */
|
|
52
|
+
onChangeStart?: MultiSliderProps['onValuesChangeStart']
|
|
53
|
+
/** Handler triggered when sliding ends */
|
|
54
|
+
onChangeFinish?: MultiSliderProps['onValuesChangeFinish']
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function RangeInput ({
|
|
58
|
+
customLabel = Label,
|
|
59
|
+
showLabel = true,
|
|
60
|
+
min = 0,
|
|
61
|
+
max = 100,
|
|
62
|
+
range = false,
|
|
63
|
+
showSteps = false,
|
|
64
|
+
showStepLabels = true,
|
|
65
|
+
showStepMarkers = true,
|
|
66
|
+
step = 1,
|
|
67
|
+
value,
|
|
68
|
+
width = 280,
|
|
69
|
+
onChange,
|
|
70
|
+
onChangeFinish,
|
|
71
|
+
onChangeStart,
|
|
72
|
+
...props
|
|
73
|
+
}: RangeInputProps): ReactNode {
|
|
74
|
+
useMemo(() => {
|
|
75
|
+
if (typeof value === 'undefined' || value === null) {
|
|
76
|
+
// to initialize a model with default value if it is missing
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
|
78
|
+
throw new Promise<void>(resolve => {
|
|
79
|
+
void (async () => {
|
|
80
|
+
// TODO: maybe throw an Error instead of console.warn?
|
|
81
|
+
if (!onChange) console.warn('[@startupjs-ui/range-input] `onChange` is required when `value` is undefined')
|
|
82
|
+
await onChange?.(range ? [min, max] : min)
|
|
83
|
+
resolve()
|
|
84
|
+
})()
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
|
88
|
+
|
|
89
|
+
// vendor component requires an array in any case
|
|
90
|
+
const values = Array.isArray(value) ? value : [value as any]
|
|
91
|
+
|
|
92
|
+
function onValuesChange (nextValues: number[]) {
|
|
93
|
+
onChange && onChange(range ? nextValues : nextValues[0])
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return pug`
|
|
97
|
+
MultiSlider.root(
|
|
98
|
+
...props
|
|
99
|
+
part='root'
|
|
100
|
+
customLabel=customLabel
|
|
101
|
+
enableLabel=showLabel
|
|
102
|
+
enabledTwo=range
|
|
103
|
+
min=min
|
|
104
|
+
max=max
|
|
105
|
+
showSteps=showSteps
|
|
106
|
+
showStepLabels=showStepLabels
|
|
107
|
+
showStepMarkers=showStepMarkers
|
|
108
|
+
sliderLength=width
|
|
109
|
+
snapped
|
|
110
|
+
step=step
|
|
111
|
+
values=values
|
|
112
|
+
onValuesChange=onValuesChange
|
|
113
|
+
onValuesChangeFinish=onChangeFinish
|
|
114
|
+
onValuesChangeStart=onChangeStart
|
|
115
|
+
)
|
|
116
|
+
`
|
|
117
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/range-input",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"main": "index.tsx",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@ptomasroos/react-native-multi-slider": "github:ptomasroos/react-native-multi-slider#368a0e8a67a595a84f411ea394b471a43ad8e7a4",
|
|
12
|
+
"@startupjs-ui/core": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
14
|
+
"@startupjs-ui/span": "^0.1.3"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"startupjs": "*"
|
|
20
|
+
},
|
|
21
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
22
|
+
}
|