mirror of
https://github.com/jgthms/bulma.git
synced 2024-12-12 13:28:30 +00:00
Create Slider
This commit is contained in:
parent
7d92a2b872
commit
4a165737df
6
docs/_react/bulma-customizer/package-lock.json
generated
6
docs/_react/bulma-customizer/package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "bulma-customizer",
|
"name": "bulma-customizer",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"classnames": "^2.5.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
@ -1560,6 +1561,11 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"classnames": "^2.5.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
|
@ -3,8 +3,22 @@ import "../../../../css/bulma.css";
|
|||||||
import "./App.css";
|
import "./App.css";
|
||||||
import Slider from "./components/Slider";
|
import Slider from "./components/Slider";
|
||||||
|
|
||||||
const KEYS = ["scheme-h", "primary-h", "primary-s", "primary-l"];
|
// const COLORS = ["primary", "link", "info", "success", "warning", "danger"];
|
||||||
|
|
||||||
|
const KEYS = [
|
||||||
|
"scheme-h",
|
||||||
|
"primary-h",
|
||||||
|
"primary-s",
|
||||||
|
"primary-l",
|
||||||
|
"skeleton-lines-gap",
|
||||||
|
];
|
||||||
const UNITS = ["deg", "rem", "em", "%"];
|
const UNITS = ["deg", "rem", "em", "%"];
|
||||||
|
const SUFFIX_TO_KIND = {
|
||||||
|
"-h": "hue",
|
||||||
|
"-s": "saturation",
|
||||||
|
"-l": "lightness",
|
||||||
|
"-gap": "gap",
|
||||||
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [vars, setVars] = useState([]);
|
const [vars, setVars] = useState([]);
|
||||||
@ -14,11 +28,15 @@ function App() {
|
|||||||
|
|
||||||
const cssvars = KEYS.map((key) => {
|
const cssvars = KEYS.map((key) => {
|
||||||
const original = rootStyle.getPropertyValue(`--bulma-${key}`);
|
const original = rootStyle.getPropertyValue(`--bulma-${key}`);
|
||||||
|
const suffix = Object.keys(SUFFIX_TO_KIND).find((kind) =>
|
||||||
|
key.endsWith(kind),
|
||||||
|
);
|
||||||
const unit = UNITS.find((unit) => original.endsWith(unit)) || "";
|
const unit = UNITS.find((unit) => original.endsWith(unit)) || "";
|
||||||
const value = unit !== "" ? original.split(unit)[0] : original;
|
const value = unit !== "" ? original.split(unit)[0] : original;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: key,
|
id: key,
|
||||||
|
kind: SUFFIX_TO_KIND[suffix] || "any",
|
||||||
original,
|
original,
|
||||||
unit,
|
unit,
|
||||||
start: Number(value),
|
start: Number(value),
|
||||||
@ -35,11 +53,18 @@ function App() {
|
|||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="card-content">
|
<div className="card-content">
|
||||||
{vars.map((v) => {
|
{vars.map((v) => {
|
||||||
const { id, original, unit, start } = v;
|
const { id, kind, original, unit, start } = v;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={id} className="block">
|
<div key={id} className="block">
|
||||||
<Slider id={id} original={original} start={start} unit={unit} />
|
<code>{id}</code>
|
||||||
|
<Slider
|
||||||
|
id={id}
|
||||||
|
kind={kind}
|
||||||
|
original={original}
|
||||||
|
start={start}
|
||||||
|
unit={unit}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
80
docs/_react/bulma-customizer/src/components/Picker.jsx
Normal file
80
docs/_react/bulma-customizer/src/components/Picker.jsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import cn from "./Slider.module.css";
|
||||||
|
|
||||||
|
const RANGES = {
|
||||||
|
deg: [0, 360, 1],
|
||||||
|
"%": [0, 100, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
function Picker({ id, kind, start, unit }) {
|
||||||
|
const [value, setValue] = useState(start);
|
||||||
|
|
||||||
|
let min = 0;
|
||||||
|
let max = 360;
|
||||||
|
let step = 1;
|
||||||
|
|
||||||
|
if (unit in RANGES) {
|
||||||
|
[min, max, step] = RANGES[unit];
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (event) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setValue(start);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const computedValue = `${value}${unit}`;
|
||||||
|
|
||||||
|
if (value === start) {
|
||||||
|
document.documentElement.style.removeProperty(`--bulma-${id}`);
|
||||||
|
} else {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
`--bulma-${id}`,
|
||||||
|
computedValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [id, start, unit, value]);
|
||||||
|
|
||||||
|
const mainCN = classNames({
|
||||||
|
[cn.main]: true,
|
||||||
|
[cn[kind]]: kind,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={mainCN}>
|
||||||
|
<code>{id}</code>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<code>
|
||||||
|
{value}
|
||||||
|
{unit}
|
||||||
|
</code>
|
||||||
|
<button className="button is-small" onClick={handleReset}>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Picker.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
kind: PropTypes.string,
|
||||||
|
original: PropTypes.string,
|
||||||
|
start: PropTypes.number,
|
||||||
|
unit: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Picker;
|
@ -1,28 +1,40 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import cn from "./Slider.module.css";
|
||||||
|
|
||||||
const RANGES = {
|
const RANGES = {
|
||||||
deg: [0, 360, 1],
|
hue: [0, 360, 1],
|
||||||
"%": [0, 100, 1],
|
saturation: [0, 100, 1],
|
||||||
|
lightness: [0, 100, 1],
|
||||||
|
gap: [0, 100, 1],
|
||||||
|
any: [0, 100, 1],
|
||||||
};
|
};
|
||||||
|
|
||||||
function Slider({ id, start, unit }) {
|
function Slider({ id, kind, start, unit }) {
|
||||||
const [value, setValue] = useState(start);
|
const [value, setValue] = useState(start);
|
||||||
|
const [isMoving, setMoving] = useState(false);
|
||||||
|
const [x, setX] = useState(0);
|
||||||
|
const sliderRef = useRef(null);
|
||||||
|
const handleRef = useRef(null);
|
||||||
|
|
||||||
let min = 0;
|
const [min, max, step] = RANGES[kind];
|
||||||
let max = 360;
|
|
||||||
let step = 1;
|
|
||||||
|
|
||||||
if (unit in RANGES) {
|
const handleMouseDown = (event) => {
|
||||||
[min, max, step] = RANGES[unit];
|
setMoving(true);
|
||||||
}
|
const slider = sliderRef.current;
|
||||||
|
const sliderRect = slider.getBoundingClientRect();
|
||||||
const handleChange = (event) => {
|
const target = event.clientX - sliderRect.left;
|
||||||
setValue(event.target.value);
|
setX(target);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const docMouseLeave = () => {
|
||||||
setValue(start);
|
setMoving(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const docMouseUp = () => {
|
||||||
|
setMoving(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -38,30 +50,74 @@ function Slider({ id, start, unit }) {
|
|||||||
}
|
}
|
||||||
}, [id, start, unit, value]);
|
}, [id, start, unit, value]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const docMouseMove = (event) => {
|
||||||
|
if (!isMoving || !sliderRef.current || !handleRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slider = sliderRef.current;
|
||||||
|
const sliderRect = slider.getBoundingClientRect();
|
||||||
|
let target = event.clientX - sliderRect.left;
|
||||||
|
|
||||||
|
if (target < 0) {
|
||||||
|
target = 0;
|
||||||
|
} else if (target > sliderRect.width) {
|
||||||
|
target = sliderRect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
setX(target);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("mousemove", docMouseMove);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mousemove", docMouseMove);
|
||||||
|
};
|
||||||
|
}, [isMoving, min, max, x]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("mouseleave", docMouseLeave);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mouseleave", docMouseLeave);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("mouseup", docMouseUp);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mouseup", docMouseUp);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const mainCN = classNames({
|
||||||
|
[cn.main]: true,
|
||||||
|
[cn.moving]: isMoving,
|
||||||
|
});
|
||||||
|
|
||||||
|
const backgroundCN = classNames({
|
||||||
|
[cn.background]: true,
|
||||||
|
[cn[kind]]: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleStyle = {
|
||||||
|
transform: `translateX(${x}px)`,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={mainCN} ref={sliderRef} onMouseDown={handleMouseDown}>
|
||||||
<code>{id}</code>
|
<div className={backgroundCN}>
|
||||||
<input
|
<span ref={handleRef} className={cn.handle} style={handleStyle} />
|
||||||
type="range"
|
</div>
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
value={value}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<code>
|
|
||||||
{value}
|
|
||||||
{unit}
|
|
||||||
</code>
|
|
||||||
<button className="button is-small" onClick={handleReset}>
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Slider.propTypes = {
|
Slider.propTypes = {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
|
kind: PropTypes.string,
|
||||||
original: PropTypes.string,
|
original: PropTypes.string,
|
||||||
start: PropTypes.number,
|
start: PropTypes.number,
|
||||||
unit: PropTypes.string,
|
unit: PropTypes.string,
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
width: 15rem;
|
||||||
|
padding: 0.375rem 0;
|
||||||
|
box-shadow: 0 0 0 1px green;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moving {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
box-shadow:
|
||||||
|
rgba(0, 0, 0, 0.15) 0 0 0 1px,
|
||||||
|
rgba(0, 0, 0, 0.05) 0 10px 10px -5px;
|
||||||
|
height: 1.25rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0rem;
|
||||||
|
cursor: grab;
|
||||||
|
border: 0.25rem solid white;
|
||||||
|
left: -0.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moving .handle,
|
||||||
|
.handle:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
border-radius: 0.125rem;
|
||||||
|
background-color: white;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hue {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgb(255, 0, 0),
|
||||||
|
rgb(255, 255, 0),
|
||||||
|
rgb(0, 255, 0),
|
||||||
|
rgb(0, 255, 255),
|
||||||
|
rgb(0, 0, 255),
|
||||||
|
rgb(255, 0, 255),
|
||||||
|
rgb(255, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
@ -1,7 +1,16 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from "vite";
|
||||||
import react from '@vitejs/plugin-react'
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
outDir: "../../assets/javascript/bulma-customizer",
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
assetFileNames: "[name].css",
|
||||||
|
entryFileNames: "[name].js",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
})
|
});
|
||||||
|
1
docs/assets/javascript/bulma-customizer/index.css
Normal file
1
docs/assets/javascript/bulma-customizer/index.css
Normal file
File diff suppressed because one or more lines are too long
14
docs/assets/javascript/bulma-customizer/index.html
Normal file
14
docs/assets/javascript/bulma-customizer/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React</title>
|
||||||
|
<script type="module" crossorigin src="/index.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
40
docs/assets/javascript/bulma-customizer/index.js
Normal file
40
docs/assets/javascript/bulma-customizer/index.js
Normal file
File diff suppressed because one or more lines are too long
16
docs/customizer.html
Normal file
16
docs/customizer.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
layout: default
|
||||||
|
theme: customizer
|
||||||
|
route: customizer
|
||||||
|
---
|
||||||
|
{% include global/header.html %}
|
||||||
|
|
||||||
|
{%
|
||||||
|
include docs/hero.html
|
||||||
|
title="The Bulma Customizer"
|
||||||
|
subtitle="Gorgeous websites built with Bulma."
|
||||||
|
%}
|
||||||
|
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script src="{{ site.url }}/assets/javascript/bulma-customizer/index.js"></script>
|
@ -31,9 +31,9 @@ breadcrumb:
|
|||||||
</div>
|
</div>
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<p class="title is-5">Use the <strong>HTML5 doctype</strong></p>
|
<p class="title is-5">Use the <strong>HTML5 doctype</strong></p>
|
||||||
{% highlight html %}
|
{%- highlight html -%}
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
{% endhighlight %}
|
{%- endhighlight -%}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user