UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

229 lines 12.9 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ColorPicker = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ require("eyedropper-polyfill"); const base_1 = require("@neo4j-ndl/base"); const react_color_1 = require("@uiw/react-color"); const classnames_1 = __importDefault(require("classnames")); const react_1 = require("react"); const clean_icon_button_1 = require("../clean-icon-button"); const icons_1 = require("../icons"); const select_1 = require("../select"); const text_input_1 = require("../text-input"); exports.ColorPicker = (0, react_1.forwardRef)(function ColorPicker({ color, onChange, swatches = Object.values(base_1.tokens.graph).filter(react_color_1.validHex), shouldShowEyeDropper = true, className, style, }, ref) { const [isEyeDropperActiveState, setIsEyeDropperActiveState] = (0, react_1.useState)(false); const [format, setFormat] = (0, react_1.useState)({ label: 'Hex', value: 'hex', }); const isEyeDropperActive = 'EyeDropper' in window && shouldShowEyeDropper; const hsva = typeof color === 'string' ? (0, react_color_1.hexToHsva)(color) : typeof color === 'object' && 'r' in color ? (0, react_color_1.rgbaToHsva)(color) : Object.assign(Object.assign({ h: 0, s: 0, v: 0 }, color), { a: 1 }); const handleChange = (hsva) => { const hex = (0, react_color_1.hsvaToHex)(hsva); const rgb = (0, react_color_1.hsvaToRgba)(hsva); onChange({ // used since typescript cannot infer the type of the color hex: `#${hex.split('#')[1]}`, hsva, rgb, }); }; return ((0, jsx_runtime_1.jsxs)("div", { ref: ref, className: (0, classnames_1.default)('ndl-color-picker', className), style: style, children: [(0, jsx_runtime_1.jsx)(react_color_1.Saturation, { hsva: hsva, className: "ndl-color-picker-saturation", onChange: (newColor) => { handleChange(Object.assign(Object.assign(Object.assign({}, hsva), newColor), { a: hsva.a })); }, pointer: (_a) => { var { left, top } = _a, props = __rest(_a, ["left", "top"]); return ((0, jsx_runtime_1.jsx)(Pointer, Object.assign({ left: left === null || left === void 0 ? void 0 : left.toString(), top: top === null || top === void 0 ? void 0 : top.toString() }, props, { hsva: hsva }))); }, radius: base_1.tokens.borderRadius['lg'] }), (0, jsx_runtime_1.jsx)(Swatch, { colors: swatches, hsva: hsva, onChange: (newColor) => { handleChange(newColor); } }), (0, jsx_runtime_1.jsxs)("div", { className: "ndl-color-picker-hue-container", children: [isEyeDropperActive && ((0, jsx_runtime_1.jsx)(clean_icon_button_1.CleanIconButton, { size: "small", isActive: isEyeDropperActiveState, onClick: () => { setIsEyeDropperActiveState(true); // TODO: Remove the any casting when polyfiller is not needed anymore. https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper // Type assertion to handle the unknown type // eslint-disable-next-line @typescript-eslint/no-explicit-any const eyeDropper = new window.EyeDropper(); eyeDropper .open() .then((result) => { handleChange((0, react_color_1.hexToHsva)(result.sRGBHex)); }) .catch((err) => { console.error(err); }) .finally(() => { setIsEyeDropperActiveState(false); }); }, description: "Pick color", children: (0, jsx_runtime_1.jsx)(icons_1.EyeDropperIconOutline, {}) })), (0, jsx_runtime_1.jsx)(react_color_1.Hue, { className: "ndl-color-picker-hue", hue: hsva.h, onChange: (newHue) => { handleChange(Object.assign(Object.assign({}, hsva), { h: newHue.h })); }, radius: base_1.tokens.borderRadius['lg'], pointer: (props) => ((0, jsx_runtime_1.jsx)(Pointer, Object.assign({}, props, { hsva: { a: 1, h: hsva.h, s: 100, v: 100, } }))) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "ndl-color-picker-inputs", children: [(0, jsx_runtime_1.jsx)(select_1.Select, { size: "small", type: "select", ariaLabel: "Color format", style: { flexShrink: 0, }, selectProps: { isSearchable: false, onChange: (newFormat) => { if (!newFormat) { return; } setFormat({ label: newFormat.label, value: newFormat.value, }); }, options: [ { label: 'Hex', value: 'hex', }, { label: 'RGB', value: 'rgb', }, ], value: { label: format.label, value: format.value }, } }), format.value === 'hex' && ((0, jsx_runtime_1.jsx)(HexInput, { hsva: hsva, onChange: (newColor) => { handleChange((0, react_color_1.hexToHsva)(newColor)); } })), format.value === 'rgb' && ((0, jsx_runtime_1.jsx)(RgbInput, { hsva: hsva, onChange: (newColor) => { handleChange((0, react_color_1.hexToHsva)(newColor)); } }))] })] })); }); const removeHashPrefix = (hex) => hex.replace('#', ''); const HexInput = ({ hsva, onChange, }) => { const [inputValue, setInputValue] = (0, react_1.useState)(() => removeHashPrefix((0, react_color_1.hsvaToHex)(hsva))); // Update input value when hsva changes from outside (0, react_1.useEffect)(() => { setInputValue(removeHashPrefix((0, react_color_1.hsvaToHex)(hsva))); }, [hsva]); const handleChange = (e) => { const value = removeHashPrefix(e.target.value); // Always update the input value for immediate feedback setInputValue(value); // Check if the input is a valid hex color (only 6 characters) const isValidHex = /^[0-9A-Fa-f]{6}$/.test(value); if (isValidHex) { onChange(`#${value}`); } }; const hexValue = (0, react_color_1.hsvaToHex)(hsva); return ((0, jsx_runtime_1.jsx)(text_input_1.TextInput, { size: "small", value: inputValue, leadingElement: (0, jsx_runtime_1.jsx)("div", { className: "ndl-color-picker-hex-input-prefix", children: "#" }), onChange: handleChange, isFluid: true, htmlAttributes: { 'aria-label': 'Hex color code', maxLength: 6, onCopy: (e) => { e.preventDefault(); navigator.clipboard.writeText(hexValue); }, } })); }; const RgbInput = ({ hsva, onChange, }) => { const rgba = (0, react_color_1.hsvaToRgba)(hsva); const [rgbValues, setRgbValues] = (0, react_1.useState)({ b: rgba.b, g: rgba.g, r: rgba.r, }); // Update input values when hsva changes from outside (0, react_1.useEffect)(() => { const newRgba = (0, react_color_1.hsvaToRgba)(hsva); setRgbValues({ b: newRgba.b, g: newRgba.g, r: newRgba.r, }); }, [hsva]); const handleChange = (channel, e) => { const value = parseInt(e.target.value, 10); // Validate the input is a number between 0-255 const validValue = isNaN(value) ? 0 : Math.max(0, Math.min(255, value)); const newRgbValues = Object.assign(Object.assign({}, rgbValues), { [channel]: validValue }); setRgbValues(newRgbValues); // Convert RGB to hex and call onChange const hexColor = `#${newRgbValues.r.toString(16).padStart(2, '0')}${newRgbValues.g.toString(16).padStart(2, '0')}${newRgbValues.b.toString(16).padStart(2, '0')}`; onChange(hexColor); }; return ((0, jsx_runtime_1.jsxs)("div", { className: "ndl-color-picker-rgb-inputs", children: [(0, jsx_runtime_1.jsx)(text_input_1.TextInput, { size: "small", className: "ndl-color-picker-rgb-input", value: rgbValues.r.toString(), onChange: (e) => handleChange('r', e), htmlAttributes: { 'aria-label': 'Red', max: '255', min: '0', type: 'number', } }), (0, jsx_runtime_1.jsx)(text_input_1.TextInput, { size: "small", className: "ndl-color-picker-rgb-input", value: rgbValues.g.toString(), onChange: (e) => handleChange('g', e), htmlAttributes: { 'aria-label': 'Green', max: '255', min: '0', type: 'number', } }), (0, jsx_runtime_1.jsx)(text_input_1.TextInput, { size: "small", className: "ndl-color-picker-rgb-input", value: rgbValues.b.toString(), onChange: (e) => handleChange('b', e), htmlAttributes: { 'aria-label': 'Blue', max: '255', min: '0', type: 'number', } })] })); }; const Swatch = ({ colors, onChange, hsva, }) => { return ((0, jsx_runtime_1.jsx)("div", { className: "ndl-color-picker-swatch", children: colors.map((color) => { const hexColor = typeof color === 'string' ? color : typeof color === 'object' && 'r' in color ? (0, react_color_1.rgbaToHex)(color) : (0, react_color_1.hsvaToHex)(color); const hsvaColor = typeof color === 'string' ? (0, react_color_1.hexToHsva)(color) : 'r' in color ? (0, react_color_1.rgbaToHsva)(color) : color; const isActive = hsva.h === hsvaColor.h && hsva.s === hsvaColor.s && hsva.v === hsvaColor.v; return ((0, jsx_runtime_1.jsx)("button", { "aria-label": hexColor, className: (0, classnames_1.default)('ndl-color-picker-swatch-color', { 'ndl-color-picker-swatch-color-active': isActive, }), style: { backgroundColor: (0, react_color_1.hsvaToHex)(hsvaColor) }, onClick: () => onChange(hsvaColor) }, hexColor)); }) })); }; const Pointer = ({ prefixCls, left, top, hsva, }) => { const rgba = (0, react_color_1.hsvaToRgba)(hsva); return ((0, jsx_runtime_1.jsx)("div", { style: { backgroundColor: `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`, left, top, }, className: (0, classnames_1.default)(prefixCls, 'ndl-color-picker-pointer') })); }; //# sourceMappingURL=ColorPicker.js.map