From 0e06c35dafe1ea166bcc87e300a070c66ed469bd Mon Sep 17 00:00:00 2001 From: mimshins Date: Wed, 29 May 2024 01:27:05 +0330 Subject: [PATCH 1/2] Improve user experience --- lib/InputSlider/InputSlider.tsx | 32 ++++++++++++++-------------- lib/InputSlider/components/Range.tsx | 4 ++-- lib/InputSlider/components/Thumb.tsx | 24 ++++++++++----------- lib/InputSlider/index.ts | 1 + lib/InputSlider/utils.ts | 22 +++++++++++-------- 5 files changed, 44 insertions(+), 39 deletions(-) diff --git a/lib/InputSlider/InputSlider.tsx b/lib/InputSlider/InputSlider.tsx index 030c0b0..18eea0e 100644 --- a/lib/InputSlider/InputSlider.tsx +++ b/lib/InputSlider/InputSlider.tsx @@ -268,7 +268,7 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { return stops.reduce((result, currentStop, idx) => { const segment: StopSegment = { - length: inLerp(0, max, currentStop) * 100, + length: inLerp(min, max, currentStop) * 100, index: idx, }; @@ -276,7 +276,7 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { return result; }, [] as StopSegment[]); - }, [stops, max]); + }, [stops, max, min]); const isMounted = useIsMounted(); @@ -335,12 +335,12 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { const getPositions = (): Positions => { const thumbsInfo = getThumbsInfo(); - const infimum = inLerp(0, max, thumbsInfo.infimum.value) * 100; - const supremum = inLerp(max, 0, thumbsInfo.supremum.value) * 100; + const infimum = inLerp(min, max, thumbsInfo.infimum.value) * 100; + const supremum = inLerp(min, max, thumbsInfo.supremum.value) * 100; const range = { start: multiThumb ? infimum : 0, - end: multiThumb ? supremum : inLerp(max, 0, 100 - supremum) * 100, + end: 100 - supremum, }; return { infimum, supremum, range }; @@ -376,15 +376,8 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { >(event => { if (readOnly || disabled || !isMounted()) return; - const increase = [ - SystemKeys.RIGHT, - orientation === "horizontal" ? SystemKeys.UP : SystemKeys.DOWN, - ].includes(event.key); - - const decrease = [ - SystemKeys.LEFT, - orientation === "horizontal" ? SystemKeys.DOWN : SystemKeys.UP, - ].includes(event.key); + const increase = [SystemKeys.RIGHT, SystemKeys.UP].includes(event.key); + const decrease = [SystemKeys.LEFT, SystemKeys.DOWN].includes(event.key); const goStart = event.key === SystemKeys.HOME; const goEnd = event.key === SystemKeys.END; @@ -490,6 +483,8 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { setTimeout(() => setIsClickAllowed(true), 10); thumb.setState(s => ({ ...s, active: false })); + thumb.ref.current?.focus(); + activeThumbRef.current = null; }; @@ -531,7 +526,7 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { widthOrHeight, thumbInfo, stopSegments, - { max, step }, + { max, min, step, orientation }, ); if (multiThumb) { @@ -574,7 +569,7 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { widthOrHeight, thumb, stopSegments, - { max, step }, + { max, min, step, orientation }, ); if (multiThumb) { @@ -590,6 +585,8 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { } } else emitValueChange(relativeValue); + thumb.ref.current?.focus(); + onClick?.(event); }; @@ -711,6 +708,9 @@ const InputSliderBase = (props: Props, ref: React.Ref) => { onClick={handleTrackClick} aria-orientation={orientation} data-slot={Slots.Root} + data-orientation={orientation} + data-multi-thumb={multiThumb ? "" : undefined} + data-dragging={isDragStarted ? "" : undefined} data-disabled={disabled ? "" : undefined} data-readonly={readOnly ? "" : undefined} > diff --git a/lib/InputSlider/components/Range.tsx b/lib/InputSlider/components/Range.tsx index f3a7744..e685ca3 100644 --- a/lib/InputSlider/components/Range.tsx +++ b/lib/InputSlider/components/Range.tsx @@ -55,8 +55,8 @@ const RangeBase = (props: Props, ref: React.Ref) => { right: `${position.end}%`, }, vertical: { - top: `${position.start}%`, - bottom: `${position.end}%`, + top: `${position.end}%`, + bottom: `${position.start}%`, }, }[orientation], position: "absolute", diff --git a/lib/InputSlider/components/Thumb.tsx b/lib/InputSlider/components/Thumb.tsx index 5f418e7..a25df74 100644 --- a/lib/InputSlider/components/Thumb.tsx +++ b/lib/InputSlider/components/Thumb.tsx @@ -161,24 +161,24 @@ const ThumbBase = (props: Props, ref: React.Ref) => { const children = resolvePropWithRenderContext(childrenProp, renderProps); const className = resolvePropWithRenderContext(classNameProp, classNameProps); + let transformCSSProperty: React.CSSProperties["transform"]; + + if (orientation === "horizontal") { + transformCSSProperty = "translateX(-50%)"; + } else { + transformCSSProperty = "translateY(50%)"; + } + const style: React.CSSProperties = { ...(styleProp ?? {}), ...disableUserSelectCSSProperties, ...{ - horizontal: { - supremum: { right: `${position}%` }, - infimum: { left: `${position}%` }, - }, - vertical: { - supremum: { bottom: `${position}%` }, - infimum: { top: `${position}%` }, - }, - }[orientation][thumbName], + horizontal: { left: `${position}%` }, + vertical: { bottom: `${position}%` }, + }[orientation], zIndex, position: "absolute", - transform: `translate${orientation === "horizontal" ? "X" : "Y"}(${ - thumbName === "infimum" ? -50 : 50 - }%)`, + transform: transformCSSProperty, }; let tabIndex = disabled ? -1 : 0; diff --git a/lib/InputSlider/index.ts b/lib/InputSlider/index.ts index 3c99f31..bdb36aa 100644 --- a/lib/InputSlider/index.ts +++ b/lib/InputSlider/index.ts @@ -2,6 +2,7 @@ export { default as Root, type ClassNameProps as RootClassNameProps, type Props as RootProps, + type RenderProps as RootRenderProps, } from "./InputSlider"; export * from "./components"; export type { StopSegment } from "./types"; diff --git a/lib/InputSlider/utils.ts b/lib/InputSlider/utils.ts index 9df3130..bd7a67e 100644 --- a/lib/InputSlider/utils.ts +++ b/lib/InputSlider/utils.ts @@ -1,4 +1,4 @@ -import { clamp, remap } from "../utils"; +import { clamp, lerp, remap } from "../utils"; import type { Props } from "./InputSlider"; import type { StopSegment, ThumbInfo } from "./types"; @@ -34,22 +34,26 @@ export const getRelativeValue = ( parentWidthOrHeight: number, thumbInfo: ThumbInfo, segments: StopSegment[], - requiredProps: { - max: Props["max"]; - step: Props["step"]; - }, + requiredProps: Pick, ) => { - const { max, step } = requiredProps; + const { max, min, step, orientation } = requiredProps; - let newValue = remap(clientXOrY, 0, parentWidthOrHeight, 0, max); + let newValue: number; - if (typeof step === "number" && step) + if (orientation === "horizontal") { + newValue = remap(clientXOrY, 0, parentWidthOrHeight, min, max); + } else { + newValue = remap(clientXOrY, 0, parentWidthOrHeight, max, min); + } + + if (typeof step === "number" && step) { newValue = Math.floor(newValue / step) * step; + } if (step === "snap") { const stopNums = segments .sort() - .map(segment => (segment.length * max) / 100); + .map(segment => lerp(min, max, segment.length / 100)); newValue = findNearestValue(stopNums, newValue); } From 91ef7f215c7a39b94d8ee1b340cb2abb9a5d0806 Mon Sep 17 00:00:00 2001 From: mimshins Date: Wed, 29 May 2024 01:41:58 +0330 Subject: [PATCH 2/2] Expose popper utils --- lib/Popper/utils.ts | 31 ++++++++++++++++++++++++----- lib/Select/components/List/utils.ts | 12 +++++------ lib/utils/index.ts | 1 + 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/Popper/utils.ts b/lib/Popper/utils.ts index 2316cc5..228cd9f 100644 --- a/lib/Popper/utils.ts +++ b/lib/Popper/utils.ts @@ -34,9 +34,22 @@ import type { Strategy, } from "./types"; -export const sides = ["top", "right", "bottom", "left"] as const; -export const alignments = ["end", "start"] as const; -export const strategies = ["absolute", "fixed"] as const; +export const sides: Readonly = Object.freeze([ + "top", + "right", + "bottom", + "left", +]); + +export const alignments: Readonly = Object.freeze([ + "end", + "start", +]); + +export const strategies: Readonly = Object.freeze([ + "absolute", + "fixed", +]); const allPlacements: Readonly = sides.reduce( (result, side) => [...result, side, `${side}-start`, `${side}-end`], @@ -369,6 +382,9 @@ const getRectRelativeToOffsetParent = ( }; }; +/** + * Returns the anchor and popper rects based on the given strategy. + */ export const getElementRects = ( elements: Elements, strategy: Strategy, @@ -547,7 +563,7 @@ const calcCoordinatesFromPlacement = (args: { return { x, y }; }; -export const suppressViewportOverflow = ( +const suppressViewportOverflow = ( excludeSides: Side[], args: { placement: Placement; @@ -753,7 +769,12 @@ export const computePosition = ( return { x, y, placement }; }; -export const translate = ({ x, y }: Coordinates) => { +/** + * Returns a translate CSS value that is rounded by DPR. + */ +export const translate = (coordinates: Coordinates) => { + const { x, y } = coordinates; + const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1; // Rounding coordinates by DPR diff --git a/lib/Select/components/List/utils.ts b/lib/Select/components/List/utils.ts index e3fbfd4..bc1a4a2 100644 --- a/lib/Select/components/List/utils.ts +++ b/lib/Select/components/List/utils.ts @@ -1,14 +1,14 @@ -import type { Strategy } from "../../../Popper/types"; -import { detectBoundaryOverflow, getElementRects } from "../../../Popper/utils"; +import { PopperUtils } from "../../../utils"; const calcBoundaryOverflow = ( anchorElement: HTMLElement, element: HTMLElement, ) => { + PopperUtils; const elements = { anchorElement, popperElement: element }; - const strategy: Strategy = "fixed"; + const strategy: (typeof PopperUtils.strategies)[0] = "fixed"; - const rects = getElementRects(elements, strategy); + const rects = PopperUtils.getElementRects(elements, strategy); const topSideCoordinates = { x: 0, @@ -22,12 +22,12 @@ const calcBoundaryOverflow = ( const overflowArgs = { strategy, elements, elementRects: rects }; - const topSideOverflow = detectBoundaryOverflow({ + const topSideOverflow = PopperUtils.detectBoundaryOverflow({ ...overflowArgs, coordinates: topSideCoordinates, }); - const bottomSideOverflow = detectBoundaryOverflow({ + const bottomSideOverflow = PopperUtils.detectBoundaryOverflow({ ...overflowArgs, coordinates: bottomSideCoordinates, }); diff --git a/lib/utils/index.ts b/lib/utils/index.ts index eb7e37f..8bddefe 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -11,6 +11,7 @@ export { default as usePreviousValue } from "@utilityjs/use-previous-value"; export { default as useRegisterNodeRef } from "@utilityjs/use-register-node-ref"; export { default as useScrollGuard } from "@utilityjs/use-scroll-guard"; export { computeAccessibleName } from "dom-accessibility-api"; +export * as PopperUtils from "../Popper/utils"; export { default as componentWithForwardedRef } from "./component-with-forwarded-ref"; export { default as createCustomEvent } from "./create-custom-event"; export { default as createVirtualElement } from "./create-virtual-element";