From 935ec546545c31b2f7fa6897f86c70aa33b26dd9 Mon Sep 17 00:00:00 2001 From: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Date: Fri, 3 Jan 2025 03:11:18 -0800 Subject: [PATCH] Reverts #50, other style changes (#52) --- lib/common/color.ts | 291 --------------------------------- lib/common/hotkeys.ts | 6 +- lib/components/Interactive.tsx | 155 ------------------ lib/components/Pointer.tsx | 46 ------ lib/components/index.ts | 2 - 5 files changed, 3 insertions(+), 497 deletions(-) delete mode 100644 lib/components/Interactive.tsx delete mode 100644 lib/components/Pointer.tsx diff --git a/lib/common/color.ts b/lib/common/color.ts index 9ce9017..36580d3 100644 --- a/lib/common/color.ts +++ b/lib/common/color.ts @@ -87,294 +87,3 @@ export class Color { return Color.lerp(colors[index], colors[index + 1], ratio); } } - -/* - * MIT License - * https://github.com/omgovich/react-colorful/ - * - * Copyright (c) 2020 Vlad Shilov - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -const round = (number: number, digits = 0, base = 10 ** digits): number => { - return Math.round(base * number) / base; -}; - -export interface RgbColor { - r: number; - g: number; - b: number; -} - -export interface RgbaColor extends RgbColor { - a: number; -} - -export interface HslColor { - h: number; - s: number; - l: number; -} - -export interface HslaColor extends HslColor { - a: number; -} - -export interface HsvColor { - h: number; - s: number; - v: number; -} - -export interface HsvaColor extends HsvColor { - a: number; -} - -export type ObjectColor = - | RgbColor - | HslColor - | HsvColor - | RgbaColor - | HslaColor - | HsvaColor; - -export type AnyColor = string | ObjectColor; - -/** - * Valid CSS units. - * https://developer.mozilla.org/en-US/docs/Web/CSS/angle - */ -const angleUnits: Record = { - grad: 360 / 400, - turn: 360, - rad: 360 / (Math.PI * 2), -}; - -export const hexToHsva = (hex: string): HsvaColor => rgbaToHsva(hexToRgba(hex)); - -export const hexToRgba = (hex: string): RgbaColor => { - let hexValue = hex; - if (hexValue[0] === '#') hexValue = hexValue.substring(1); - - if (hexValue.length < 6) { - return { - r: Number.parseInt(hexValue[0] + hexValue[0], 16), - g: Number.parseInt(hexValue[1] + hexValue[1], 16), - b: Number.parseInt(hexValue[2] + hexValue[2], 16), - a: - hexValue.length === 4 - ? round(Number.parseInt(hexValue[3] + hexValue[3], 16) / 255, 2) - : 1, - }; - } - - return { - r: Number.parseInt(hexValue.substring(0, 2), 16), - g: Number.parseInt(hexValue.substring(2, 4), 16), - b: Number.parseInt(hexValue.substring(4, 6), 16), - a: - hexValue.length === 8 - ? round(Number.parseInt(hexValue.substring(6, 8), 16) / 255, 2) - : 1, - }; -}; - -export const parseHue = (value: string, unit = 'deg'): number => { - return Number(value) * (angleUnits[unit] || 1); -}; - -export const hslaStringToHsva = (hslString: string): HsvaColor => { - const matcher = - /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; - const match = matcher.exec(hslString); - - if (!match) return { h: 0, s: 0, v: 0, a: 1 }; - - return hslaToHsva({ - h: parseHue(match[1], match[2]), - s: Number(match[3]), - l: Number(match[4]), - a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), - }); -}; - -export const hslStringToHsva = hslaStringToHsva; - -export const hslaToHsva = ({ h, s, l, a }: HslaColor): HsvaColor => { - s *= (l < 50 ? l : 100 - l) / 100; - - return { - h: h, - s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0, - v: l + s, - a, - }; -}; - -export const hsvaToHex = (hsva: HsvaColor): string => - rgbaToHex(hsvaToRgba(hsva)); - -export const hsvaToHsla = ({ h, s, v, a }: HsvaColor): HslaColor => { - const hh = ((200 - s) * v) / 100; - - return { - h: round(h), - s: round( - hh > 0 && hh < 200 - ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 - : 0, - ), - l: round(hh / 2), - a: round(a, 2), - }; -}; - -export const hsvaToHslString = (hsva: HsvaColor): string => { - const { h, s, l } = hsvaToHsla(hsva); - return `hsl(${h}, ${s}%, ${l}%)`; -}; - -export const hsvaToHsvString = (hsva: HsvaColor): string => { - const { h, s, v } = roundHsva(hsva); - return `hsv(${h}, ${s}%, ${v}%)`; -}; - -export const hsvaToHsvaString = (hsva: HsvaColor): string => { - const { h, s, v, a } = roundHsva(hsva); - return `hsva(${h}, ${s}%, ${v}%, ${a})`; -}; - -export const hsvaToHslaString = (hsva: HsvaColor): string => { - const { h, s, l, a } = hsvaToHsla(hsva); - return `hsla(${h}, ${s}%, ${l}%, ${a})`; -}; - -export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => { - h = (h / 360) * 6; - s = s / 100; - v = v / 100; - - const hh = Math.floor(h); - const b = v * (1 - s); - const c = v * (1 - (h - hh) * s); - const d = v * (1 - (1 - h + hh) * s); - const mod = hh % 6; - - return { - r: [v, c, b, b, d, v][mod] * 255, - g: [d, v, v, c, b, b][mod] * 255, - b: [b, b, d, v, v, c][mod] * 255, - a: round(a, 2), - }; -}; - -export const hsvaToRgbString = (hsva: HsvaColor): string => { - const { r, g, b } = hsvaToRgba(hsva); - return `rgb(${round(r)}, ${round(g)}, ${round(b)})`; -}; - -export const hsvaToRgbaString = (hsva: HsvaColor): string => { - const { r, g, b, a } = hsvaToRgba(hsva); - return `rgba(${round(r)}, ${round(g)}, ${round(b)}, ${round(a, 2)})`; -}; - -export const hsvaStringToHsva = (hsvString: string): HsvaColor => { - const matcher = - /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; - const match = matcher.exec(hsvString); - - if (!match) return { h: 0, s: 0, v: 0, a: 1 }; - - return roundHsva({ - h: parseHue(match[1], match[2]), - s: Number(match[3]), - v: Number(match[4]), - a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), - }); -}; - -export const hsvStringToHsva = hsvaStringToHsva; - -export const rgbaStringToHsva = (rgbaString: string): HsvaColor => { - const matcher = - /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; - const match = matcher.exec(rgbaString); - - if (!match) return { h: 0, s: 0, v: 0, a: 1 }; - - return rgbaToHsva({ - r: Number(match[1]) / (match[2] ? 100 / 255 : 1), - g: Number(match[3]) / (match[4] ? 100 / 255 : 1), - b: Number(match[5]) / (match[6] ? 100 / 255 : 1), - a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1), - }); -}; - -export const rgbStringToHsva = rgbaStringToHsva; - -const format = (number: number) => { - const hex = number.toString(16); - return hex.length < 2 ? `0${hex}` : hex; -}; - -export const rgbaToHex = ({ r, g, b, a }: RgbaColor): string => { - const alphaHex = a < 1 ? format(round(a * 255)) : ''; - return `${'#'}${format(round(r))}${format(round(g))}${format(round(b))}${alphaHex}`; -}; - -export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => { - const max = Math.max(r, g, b); - const delta = max - Math.min(r, g, b); - - // prettier-ignore - const hh = delta - ? max === r - ? (g - b) / delta - : max === g - ? 2 + (b - r) / delta - : 4 + (r - g) / delta - : 0; - - return { - h: 60 * (hh < 0 ? hh + 6 : hh), - s: max ? (delta / max) * 100 : 0, - v: (max / 255) * 100, - a, - }; -}; - -export const roundHsva = (hsva: HsvaColor): HsvaColor => ({ - h: round(hsva.h), - s: round(hsva.s), - v: round(hsva.v), - a: round(hsva.a, 2), -}); - -export const rgbaToRgb = ({ r, g, b }: RgbaColor): RgbColor => ({ r, g, b }); - -export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l }); - -export const hsvaToHsv = (hsva: HsvaColor): HsvColor => { - const { h, s, v } = roundHsva(hsva); - return { h, s, v }; -}; - -const hexMatcher = /^#?([0-9A-F]{3,8})$/i; - -export const validHex = (value: string, alpha?: boolean): boolean => { - const match = hexMatcher.exec(value); - const length = match ? match[1].length : 0; - - return ( - length === 3 || // '#rgb' format - length === 6 || // '#rrggbb' format - (!!alpha && length === 4) || // '#rgba' format - (!!alpha && length === 8) // '#rrggbbaa' format - ); -}; diff --git a/lib/common/hotkeys.ts b/lib/common/hotkeys.ts index 7b27be3..32cd735 100644 --- a/lib/common/hotkeys.ts +++ b/lib/common/hotkeys.ts @@ -123,7 +123,7 @@ export function releaseHotKey(keyCode: number) { } export function releaseHeldKeys() { - for (const byondKeyCode of Object.keys(keyState)) { + for (const byondKeyCode in keyState) { if (keyState[byondKeyCode]) { keyState[byondKeyCode] = false; Byond.command(`KeyUp "${byondKeyCode}"`); @@ -141,7 +141,7 @@ export function setupHotKeys() { Byond.winget('default.*').then((data: Record) => { // Group each macro by ref const groupedByRef: Record = {}; - for (const key of Object.keys(data)) { + for (const key in data) { const keyPath = key.split('.'); const ref = keyPath[1]; const prop = keyPath[2]; @@ -162,7 +162,7 @@ export function setupHotKeys() { return str.substring(1, str.length - 1).replace(escapedQuotRegex, '"'); } - for (const ref of Object.keys(groupedByRef)) { + for (const ref in groupedByRef) { const macro = groupedByRef[ref]; const byondKeyName = unEscape(macro.name); byondMacros[byondKeyName] = unEscape(macro.command); diff --git a/lib/components/Interactive.tsx b/lib/components/Interactive.tsx deleted file mode 100644 index 58e057f..0000000 --- a/lib/components/Interactive.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/** - * MIT License - * https://github.com/omgovich/react-colorful/ - * - * Copyright (c) 2020 Vlad Shilov - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import type React from 'react'; -import { - Component, - type KeyboardEvent, - type MouseEvent, - type RefObject, - createRef, -} from 'react'; -import { clamp } from '../common/math'; - -export interface Interaction { - left: number; - top: number; -} - -// Finds the proper window object to fix iframe embedding issues -const getParentWindow = (node?: HTMLDivElement | null): Window => { - return node?.ownerDocument?.defaultView || self; -}; - -// Returns a relative position of the pointer inside the node's bounding box -const getRelativePosition = ( - node: HTMLDivElement, - event: MouseEvent, -): Interaction => { - const rect = node.getBoundingClientRect(); - return { - left: clamp( - (event.pageX - (rect.left + getParentWindow(node).pageXOffset)) / - rect.width, - 0, - 1, - ), - top: clamp( - (event.pageY - (rect.top + getParentWindow(node).pageYOffset)) / - rect.height, - 0, - 1, - ), - }; -}; - -export interface InteractiveProps { - onMove: (interaction: Interaction) => void; - onKey: (offset: Interaction) => void; - children: React.ReactNode; - style?: React.CSSProperties; -} - -export class Interactive extends Component { - containerRef: RefObject; - - constructor(props: InteractiveProps) { - super(props); - this.containerRef = createRef(); - } - - handleMoveStart = (event: MouseEvent) => { - const el = this.containerRef.current; - if (!el) return; - - // Prevent text selection - event.preventDefault(); - el.focus(); - this.props.onMove(getRelativePosition(el, event)); - this.toggleDocumentEvents(true); - }; - - handleMove = (event: MouseEvent) => { - // Prevent text selection - event.preventDefault(); - - // If user moves the pointer outside of the window or iframe bounds and release it there, - // `mouseup`/`touchend` won't be fired. In order to stop the picker from following the cursor - // after the user has moved the mouse/finger back to the document, we check `event.buttons` - // and `event.touches`. It allows us to detect that the user is just moving his pointer - // without pressing it down - const isDown = event.buttons > 0; - - if (isDown && this.containerRef.current) { - this.props.onMove(getRelativePosition(this.containerRef.current, event)); - } else { - this.toggleDocumentEvents(false); - } - }; - - handleMoveEnd = () => { - this.toggleDocumentEvents(false); - }; - - handleKeyDown = (event: KeyboardEvent) => { - const keyCode = event.which || event.keyCode; - - // Ignore all keys except arrow ones - if (keyCode < 37 || keyCode > 40) return; - // Do not scroll page by arrow keys when document is focused on the element - event.preventDefault(); - // Send relative offset to the parent component. - // We use codes (37←, 38↑, 39→, 40↓) instead of keys ('ArrowRight', 'ArrowDown', etc) - // to reduce the size of the library - this.props.onKey({ - left: keyCode === 39 ? 0.05 : keyCode === 37 ? -0.05 : 0, - top: keyCode === 40 ? 0.05 : keyCode === 38 ? -0.05 : 0, - }); - }; - - toggleDocumentEvents = (state: boolean) => { - const el = this.containerRef.current; - const parentWindow = getParentWindow(el); - - const toggleEvent = state - ? parentWindow.addEventListener.bind(parentWindow) - : parentWindow.removeEventListener.bind(parentWindow); - - toggleEvent('mousemove', this.handleMove as unknown as EventListener); - toggleEvent('mouseup', this.handleMoveEnd as unknown as EventListener); - }; - - componentDidMount() { - this.toggleDocumentEvents(true); - } - - componentWillUnmount() { - this.toggleDocumentEvents(false); - } - - render() { - return ( -
- {this.props.children} -
- ); - } -} diff --git a/lib/components/Pointer.tsx b/lib/components/Pointer.tsx deleted file mode 100644 index d82363f..0000000 --- a/lib/components/Pointer.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * MIT License - * https://github.com/omgovich/react-colorful/ - * - * Copyright (c) 2020 Vlad Shilov - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import type { ReactNode } from 'react'; -import { classes } from '../common/react'; - -interface PointerProps { - className?: string; - top?: number; - left: number; - color: string; -} - -export const Pointer = ({ - className, - color, - left, - top = 0.5, -}: PointerProps): ReactNode => { - const nodeClassName = classes(['react-colorful__pointer', className]); - - const style = { - top: `${top * 100}%`, - left: `${left * 100}%`, - }; - - return ( -
-
-
- ); -}; diff --git a/lib/components/index.ts b/lib/components/index.ts index 2e74a96..a06d765 100644 --- a/lib/components/index.ts +++ b/lib/components/index.ts @@ -21,7 +21,6 @@ export { Image } from './Image'; export { ImageButton } from './ImageButton'; export { InfinitePlane } from './InfinitePlane'; export { Input } from './Input'; -export { Interactive, type Interaction } from './Interactive'; export { KeyListener } from './KeyListener'; export { Knob } from './Knob'; export { LabeledControls } from './LabeledControls'; @@ -30,7 +29,6 @@ export { MenuBar } from './MenuBar'; export { Modal } from './Modal'; export { NoticeBox } from './NoticeBox'; export { NumberInput } from './NumberInput'; -export { Pointer } from './Pointer'; export { Popper } from './Popper'; export { ProgressBar } from './ProgressBar'; export { RestrictedInput } from './RestrictedInput';