diff --git a/packages/epo-react-lib/package.json b/packages/epo-react-lib/package.json index 355341bd..9d2fb892 100644 --- a/packages/epo-react-lib/package.json +++ b/packages/epo-react-lib/package.json @@ -1,7 +1,7 @@ { "name": "@rubin-epo/epo-react-lib", "description": "Rubin Observatory Education & Public Outreach team React UI library.", - "version": "2.0.3", + "version": "2.0.4", "author": "Rubin EPO", "license": "MIT", "homepage": "https://lsst-epo.github.io/epo-react-lib", diff --git a/packages/epo-react-lib/src/form/HorizontalSlider/HorizontalSlider.tsx b/packages/epo-react-lib/src/form/HorizontalSlider/HorizontalSlider.tsx index 8edc8ab8..4c5f4ff0 100644 --- a/packages/epo-react-lib/src/form/HorizontalSlider/HorizontalSlider.tsx +++ b/packages/epo-react-lib/src/form/HorizontalSlider/HorizontalSlider.tsx @@ -1,13 +1,12 @@ -import { isColorTransparent } from "@/lib/utils"; +import { isColorTransparent, isStyleSupported } from "@/lib/utils"; import { useState, FunctionComponent } from "react"; +import { ReactSliderProps } from "react-slider"; import * as Styled from "./styles"; type SliderValue = number | readonly number[]; -type BaseProps = { - min?: number; - max?: number; - step?: number; +interface BaseProps + extends Pick { label: string; minLabel?: string; maxLabel?: string; @@ -15,16 +14,17 @@ type BaseProps = { color?: string; darkMode?: boolean; isDisabled?: boolean; - className?: string; -}; +} interface SingleSliderProps extends BaseProps { value: number; + defaultValue?: number; onChangeCallback: (value: number, label: string) => void; } interface RangedSliderProps extends BaseProps { value: number[]; + defaultValue?: number[]; onChangeCallback: (value: number[], label: string) => void; } @@ -35,6 +35,7 @@ const HorizontalSlider: FunctionComponent = ({ max = 100, step = 1, value, + defaultValue, onChangeCallback, label, minLabel, @@ -55,21 +56,25 @@ const HorizontalSlider: FunctionComponent = ({ const getValidColor = (color?: string) => { const validColor = - color && CSS.supports("color", color) && !isColorTransparent(color) + color && isStyleSupported("color", color) && !isColorTransparent(color) ? color : undefined; return isDisabled ? "var(--neutral60, #6a6e6e)" : validColor; }; - const Track = (props: any, state: { index: number; value: SliderValue }) => { + const Track = (props: any, state: { index: number }) => { const { index } = state; + const { key, ...other } = props; const hasColor = (hasDoubleHandles && index === 1) || (!hasDoubleHandles && index === 0); const trackColor = getValidColor(color); - return ( - + ); }; @@ -77,14 +82,15 @@ const HorizontalSlider: FunctionComponent = ({ props: any, state: { index: number; value: SliderValue; valueNow: number } ) => { + const { key, ...other } = props; const { valueNow } = state; const thumbColor = getValidColor(color); return ( - + {valueNow} @@ -96,19 +102,18 @@ const HorizontalSlider: FunctionComponent = ({ const maxLabelId = `${label}-max-label`; return ( - + {minLabel || maxLabel ? ( - - {minLabel} - - - {maxLabel} - + {minLabel} + {maxLabel} ) : null} { setShowThumbLabels(true); }} diff --git a/packages/epo-react-lib/src/form/HorizontalSlider/styles.ts b/packages/epo-react-lib/src/form/HorizontalSlider/styles.ts index c7597791..30db396e 100644 --- a/packages/epo-react-lib/src/form/HorizontalSlider/styles.ts +++ b/packages/epo-react-lib/src/form/HorizontalSlider/styles.ts @@ -8,6 +8,16 @@ const thumbBorder = 4; const thumbHeight = 14; export const HorizontalSliderContainer = styled.div` + --slider-color: inherit; + --slider-border-color: var(--blue20, #c7d4f4); + --slider-background-color: var(--neutral10, #f5f5f5); + + &[data-theme="dark"] { + --slider-color: var(--white, #fff); + --slider-border-color: var(--white, #fff); + --slider-background-color: var(--neutral80, #404040); + } + display: flex; flex-flow: column nowrap; padding-bottom: 16px; @@ -21,20 +31,19 @@ export const TrackLabels = styled.div` padding: 0 3px 6px; `; -export const Label = styled.div<{ $darkMode: boolean }>` +export const Label = styled.div` box-sizing: border-box; font-size: 14px; font-weight: bold; line-height: 1.6; - color: ${({ $darkMode }) => - $darkMode ? css`var(--white, #fff)` : css`inherit`}; + color: var(--slider-color); `; -export const ThumbLabel = styled.span<{ - $showThumbLabels: boolean; - $darkMode: boolean; -}>` +export const ThumbLabel = styled.span` + background-color: var(--slider-background-color); + border: 1px solid var(--slider-border-color); box-sizing: border-box; + color: var(--slider-color); position: absolute; top: calc(100% + ${thumbBorder}px); padding: 5px; @@ -43,22 +52,9 @@ export const ThumbLabel = styled.span<{ text-align: center; line-height: 1.35; border-radius: 5px; - opacity: ${({ $showThumbLabels }) => ($showThumbLabels ? 1 : 0)}; + opacity: var(--thumb-label-opacity, 0); transition: opacity 0.4s ease-in-out; user-select: none; - - ${({ $darkMode }) => - $darkMode - ? css` - border: 1px solid var(--white); - background-color: var(--neutral80); - color: var(--white, #fff); - ` - : css` - border: 1px solid var(--blue20); - background-color: var(--neutral10); - color: var(--black, #000); - `} `; export const ThumbContainer = styled.div` diff --git a/packages/epo-react-lib/src/lib/utils.ts b/packages/epo-react-lib/src/lib/utils.ts index 63f3318c..2cf30dae 100644 --- a/packages/epo-react-lib/src/lib/utils.ts +++ b/packages/epo-react-lib/src/lib/utils.ts @@ -68,3 +68,33 @@ export const isColorTransparent = (color: string) => { return false; }; + +export function isStyleSupported(prop: string, value: string): boolean { + // If no value is supplied, use "inherit" + value = arguments.length === 2 ? value : "inherit"; + // Try the native standard method first + if ("CSS" in window && "supports" in window.CSS) { + return window.CSS.supports(prop, value); + } + // Check Opera's native method + if ("supportsCSS" in window) { + return (window.supportsCSS as (property: string, value: string) => boolean)( + prop, + value + ); + } + // Convert to camel-case for DOM interactions + const camel = prop.replace(/-([a-z]|[0-9])/gi, function (all, letter) { + return (letter + "").toUpperCase(); + }); + // Create test element + const el = document.createElement("div"); + // Check if the property is supported + const support = camel in el.style; + // Assign the property and value to invoke + // the CSS interpreter + el.style.cssText = prop + ":" + value; + // Ensure both the property and value are + // supported and return + return support && el.style[camel as any] !== ""; +}