Skip to content

Commit 4a16573

Browse files
committed
Create Slider
1 parent 7d92a2b commit 4a16573

File tree

12 files changed

+338
-40
lines changed

12 files changed

+338
-40
lines changed

docs/_react/bulma-customizer/package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/_react/bulma-customizer/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"classnames": "^2.5.1",
1314
"react": "^18.3.1",
1415
"react-dom": "^18.3.1"
1516
},

docs/_react/bulma-customizer/src/App.jsx

+28-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,22 @@ import "../../../../css/bulma.css";
33
import "./App.css";
44
import Slider from "./components/Slider";
55

6-
const KEYS = ["scheme-h", "primary-h", "primary-s", "primary-l"];
6+
// const COLORS = ["primary", "link", "info", "success", "warning", "danger"];
7+
8+
const KEYS = [
9+
"scheme-h",
10+
"primary-h",
11+
"primary-s",
12+
"primary-l",
13+
"skeleton-lines-gap",
14+
];
715
const UNITS = ["deg", "rem", "em", "%"];
16+
const SUFFIX_TO_KIND = {
17+
"-h": "hue",
18+
"-s": "saturation",
19+
"-l": "lightness",
20+
"-gap": "gap",
21+
};
822

923
function App() {
1024
const [vars, setVars] = useState([]);
@@ -14,11 +28,15 @@ function App() {
1428

1529
const cssvars = KEYS.map((key) => {
1630
const original = rootStyle.getPropertyValue(`--bulma-${key}`);
31+
const suffix = Object.keys(SUFFIX_TO_KIND).find((kind) =>
32+
key.endsWith(kind),
33+
);
1734
const unit = UNITS.find((unit) => original.endsWith(unit)) || "";
1835
const value = unit !== "" ? original.split(unit)[0] : original;
1936

2037
return {
2138
id: key,
39+
kind: SUFFIX_TO_KIND[suffix] || "any",
2240
original,
2341
unit,
2442
start: Number(value),
@@ -35,11 +53,18 @@ function App() {
3553
<div className="card">
3654
<div className="card-content">
3755
{vars.map((v) => {
38-
const { id, original, unit, start } = v;
56+
const { id, kind, original, unit, start } = v;
3957

4058
return (
4159
<div key={id} className="block">
42-
<Slider id={id} original={original} start={start} unit={unit} />
60+
<code>{id}</code>
61+
<Slider
62+
id={id}
63+
kind={kind}
64+
original={original}
65+
start={start}
66+
unit={unit}
67+
/>
4368
</div>
4469
);
4570
})}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useEffect, useState } from "react";
2+
import PropTypes from "prop-types";
3+
import classNames from "classnames";
4+
5+
import cn from "./Slider.module.css";
6+
7+
const RANGES = {
8+
deg: [0, 360, 1],
9+
"%": [0, 100, 1],
10+
};
11+
12+
function Picker({ id, kind, start, unit }) {
13+
const [value, setValue] = useState(start);
14+
15+
let min = 0;
16+
let max = 360;
17+
let step = 1;
18+
19+
if (unit in RANGES) {
20+
[min, max, step] = RANGES[unit];
21+
}
22+
23+
const handleChange = (event) => {
24+
setValue(event.target.value);
25+
};
26+
27+
const handleReset = () => {
28+
setValue(start);
29+
};
30+
31+
useEffect(() => {
32+
const computedValue = `${value}${unit}`;
33+
34+
if (value === start) {
35+
document.documentElement.style.removeProperty(`--bulma-${id}`);
36+
} else {
37+
document.documentElement.style.setProperty(
38+
`--bulma-${id}`,
39+
computedValue,
40+
);
41+
}
42+
}, [id, start, unit, value]);
43+
44+
const mainCN = classNames({
45+
[cn.main]: true,
46+
[cn[kind]]: kind,
47+
});
48+
49+
return (
50+
<div className={mainCN}>
51+
<code>{id}</code>
52+
53+
<input
54+
type="range"
55+
min={min}
56+
max={max}
57+
step={step}
58+
value={value}
59+
onChange={handleChange}
60+
/>
61+
<code>
62+
{value}
63+
{unit}
64+
</code>
65+
<button className="button is-small" onClick={handleReset}>
66+
Reset
67+
</button>
68+
</div>
69+
);
70+
}
71+
72+
Picker.propTypes = {
73+
id: PropTypes.string,
74+
kind: PropTypes.string,
75+
original: PropTypes.string,
76+
start: PropTypes.number,
77+
unit: PropTypes.string,
78+
};
79+
80+
export default Picker;

docs/_react/bulma-customizer/src/components/Slider.jsx

+87-31
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
1-
import { useEffect, useState } from "react";
1+
import { useEffect, useRef, useState } from "react";
22
import PropTypes from "prop-types";
3+
import classNames from "classnames";
4+
5+
import cn from "./Slider.module.css";
36

47
const RANGES = {
5-
deg: [0, 360, 1],
6-
"%": [0, 100, 1],
8+
hue: [0, 360, 1],
9+
saturation: [0, 100, 1],
10+
lightness: [0, 100, 1],
11+
gap: [0, 100, 1],
12+
any: [0, 100, 1],
713
};
814

9-
function Slider({ id, start, unit }) {
15+
function Slider({ id, kind, start, unit }) {
1016
const [value, setValue] = useState(start);
17+
const [isMoving, setMoving] = useState(false);
18+
const [x, setX] = useState(0);
19+
const sliderRef = useRef(null);
20+
const handleRef = useRef(null);
1121

12-
let min = 0;
13-
let max = 360;
14-
let step = 1;
22+
const [min, max, step] = RANGES[kind];
1523

16-
if (unit in RANGES) {
17-
[min, max, step] = RANGES[unit];
18-
}
24+
const handleMouseDown = (event) => {
25+
setMoving(true);
26+
const slider = sliderRef.current;
27+
const sliderRect = slider.getBoundingClientRect();
28+
const target = event.clientX - sliderRect.left;
29+
setX(target);
30+
};
1931

20-
const handleChange = (event) => {
21-
setValue(event.target.value);
32+
const docMouseLeave = () => {
33+
setMoving(false);
2234
};
2335

24-
const handleReset = () => {
25-
setValue(start);
36+
const docMouseUp = () => {
37+
setMoving(false);
2638
};
2739

2840
useEffect(() => {
@@ -38,30 +50,74 @@ function Slider({ id, start, unit }) {
3850
}
3951
}, [id, start, unit, value]);
4052

53+
useEffect(() => {
54+
const docMouseMove = (event) => {
55+
if (!isMoving || !sliderRef.current || !handleRef.current) {
56+
return;
57+
}
58+
59+
const slider = sliderRef.current;
60+
const sliderRect = slider.getBoundingClientRect();
61+
let target = event.clientX - sliderRect.left;
62+
63+
if (target < 0) {
64+
target = 0;
65+
} else if (target > sliderRect.width) {
66+
target = sliderRect.width;
67+
}
68+
69+
setX(target);
70+
};
71+
72+
window.addEventListener("mousemove", docMouseMove);
73+
74+
return () => {
75+
window.removeEventListener("mousemove", docMouseMove);
76+
};
77+
}, [isMoving, min, max, x]);
78+
79+
useEffect(() => {
80+
window.addEventListener("mouseleave", docMouseLeave);
81+
82+
return () => {
83+
window.removeEventListener("mouseleave", docMouseLeave);
84+
};
85+
}, []);
86+
87+
useEffect(() => {
88+
window.addEventListener("mouseup", docMouseUp);
89+
90+
return () => {
91+
window.removeEventListener("mouseup", docMouseUp);
92+
};
93+
}, []);
94+
95+
const mainCN = classNames({
96+
[cn.main]: true,
97+
[cn.moving]: isMoving,
98+
});
99+
100+
const backgroundCN = classNames({
101+
[cn.background]: true,
102+
[cn[kind]]: true,
103+
});
104+
105+
const handleStyle = {
106+
transform: `translateX(${x}px)`,
107+
};
108+
41109
return (
42-
<div>
43-
<code>{id}</code>
44-
<input
45-
type="range"
46-
min={min}
47-
max={max}
48-
step={step}
49-
value={value}
50-
onChange={handleChange}
51-
/>
52-
<code>
53-
{value}
54-
{unit}
55-
</code>
56-
<button className="button is-small" onClick={handleReset}>
57-
Reset
58-
</button>
110+
<div className={mainCN} ref={sliderRef} onMouseDown={handleMouseDown}>
111+
<div className={backgroundCN}>
112+
<span ref={handleRef} className={cn.handle} style={handleStyle} />
113+
</div>
59114
</div>
60115
);
61116
}
62117

63118
Slider.propTypes = {
64119
id: PropTypes.string,
120+
kind: PropTypes.string,
65121
original: PropTypes.string,
66122
start: PropTypes.number,
67123
unit: PropTypes.string,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.main {
2+
position: relative;
3+
width: 15rem;
4+
padding: 0.375rem 0;
5+
box-shadow: 0 0 0 1px green;
6+
cursor: grab;
7+
}
8+
9+
.moving {
10+
cursor: grabbing;
11+
}
12+
13+
.handle {
14+
background-color: rgba(255, 255, 255, 0.05);
15+
box-shadow:
16+
rgba(0, 0, 0, 0.15) 0 0 0 1px,
17+
rgba(0, 0, 0, 0.05) 0 10px 10px -5px;
18+
height: 1.25rem;
19+
width: 1.25rem;
20+
border-radius: 9999px;
21+
position: absolute;
22+
top: 0rem;
23+
cursor: grab;
24+
border: 0.25rem solid white;
25+
left: -0.625rem;
26+
}
27+
28+
.moving .handle,
29+
.handle:active {
30+
cursor: grabbing;
31+
}
32+
33+
.background {
34+
border-radius: 0.125rem;
35+
background-color: white;
36+
height: 0.5rem;
37+
}
38+
39+
.hue {
40+
background-image: linear-gradient(
41+
to right,
42+
rgb(255, 0, 0),
43+
rgb(255, 255, 0),
44+
rgb(0, 255, 0),
45+
rgb(0, 255, 255),
46+
rgb(0, 0, 255),
47+
rgb(255, 0, 255),
48+
rgb(255, 0, 0)
49+
);
50+
}
+12-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1-
import { defineConfig } from 'vite'
2-
import react from '@vitejs/plugin-react'
1+
import { defineConfig } from "vite";
2+
import react from "@vitejs/plugin-react";
33

44
// https://vitejs.dev/config/
55
export default defineConfig({
6+
build: {
7+
outDir: "../../assets/javascript/bulma-customizer",
8+
rollupOptions: {
9+
output: {
10+
assetFileNames: "[name].css",
11+
entryFileNames: "[name].js",
12+
},
13+
},
14+
},
615
plugins: [react()],
7-
})
16+
});

docs/assets/javascript/bulma-customizer/index.css

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)