React Example: Custom Styles

tsx
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'
import styled, { createGlobalStyle } from 'styled-components'
import { useRanger, Ranger } from '@tanstack/react-ranger'
import { createRoot } from 'react-dom/client'

const GlobalStyles = createGlobalStyle`
  body {
   font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
   font-weight: 300;
  }
`

export const Track = styled('div')`
  height: 8px;
  width: 90%;
  position: relative;
  userselect: none;
  height: 4px;
  background: #ddd;
  boxshadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
  borderradius: 2px;
`

export const Tick = styled('div')`
  position: absolute;
  top: 5px;
  left: ${(props: { percentage: number }) => `${props.percentage}%`};
  transform: translateX(-50%);
  :before {
    content: '';
    position: absolute;
    left: 0;
    background: rgba(0, 0, 0, 0.2);
    height: 5px;
    width: 2px;
    transform: translate(-50%, 0.7rem);
  }
`

export const TickLabel = styled('div')`
  position: absolute;
  font-size: 0.6rem;
  color: rgba(0, 0, 0, 0.5);
  top: 100%;
  transform: translate(-50%, 1.2rem);
  white-space: nowrap;
`

export const Segment = styled('div')`
  position: absolute;
  background: ${(props: { index: number; left: number; width: number }) =>
    props.index === 0
      ? '#3e8aff'
      : props.index === 1
      ? '#00d5c0'
      : props.index === 2
      ? '#f5c200'
      : '#ff6050'};
  left: ${(props: { left: number }) => `${props.left}%`};
  height: 100%;
  width: ${(props: { width: number }) => `${props.width}%`};
`

export const Handle = styled('button')`
  position: absolute;
  left: ${(props: { left: number }) => `${props.left}%`};
  zIndex: isActive ? 1 : 0;
  appearance: none;
  border: none;
  outline: none;
  background: #ff1a6b;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 100%;
  font-size: 0.7rem;
  white-space: nowrap;
  color: white;
  font-weight: ${(props: { active: boolean }) =>
    props.active ? 'bold' : 'normal'};
  transform: ${(props: { active: boolean }) =>
    props.active
      ? 'translate(-50%, -100%) scale(1.3)'
      : 'translate(-50%, -50%) scale(0.9)'};
`

function App() {
  const rangerRef = React.useRef<HTMLDivElement>(null)
  const [values, setValues] = React.useState<ReadonlyArray<number>>([
    15, 50, 80,
  ])

  const rangerInstance = useRanger<HTMLDivElement>({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 1,
    onChange: (instance: Ranger<HTMLDivElement>) =>
      setValues(instance.sortedValues),
  })

  return (
    <div className="App" style={{ padding: 20 }}>
      <GlobalStyles />
      <h1>Custom Styles</h1>
      <br />
      <br />
      <Track ref={rangerRef}>
        {rangerInstance.getTicks().map(({ value, key, percentage }) => (
          <Tick key={key} percentage={percentage}>
            <TickLabel>{value}</TickLabel>
          </Tick>
        ))}
        {rangerInstance.getSteps().map(({ left, width }, i) => (
          <Segment key={i} index={i} left={left} width={width} />
        ))}
        {rangerInstance
          .handles()
          .map(
            (
              {
                value,
                onKeyDownHandler,
                onMouseDownHandler,
                onTouchStart,
                isActive,
              },
              i,
            ) => (
              <Handle
                key={i}
                onKeyDown={onKeyDownHandler}
                onMouseDown={onMouseDownHandler}
                onTouchStart={onTouchStart}
                role="slider"
                aria-valuemin={rangerInstance.options.min}
                aria-valuemax={rangerInstance.options.max}
                aria-valuenow={value}
                left={rangerInstance.getPercentageForValue(value)}
                active={isActive}
              >
                {value}
              </Handle>
            ),
          )}
      </Track>
      <br />
      <br />
      <br />
      <pre
        style={{
          display: 'inline-block',
          textAlign: 'left',
        }}
      >
        <code>
          {JSON.stringify({
            values,
          })}
        </code>
      </pre>
    </div>
  )
}

const root = createRoot(document.getElementById('root')!)

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react'
import styled, { createGlobalStyle } from 'styled-components'
import { useRanger, Ranger } from '@tanstack/react-ranger'
import { createRoot } from 'react-dom/client'

const GlobalStyles = createGlobalStyle`
  body {
   font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
   font-weight: 300;
  }
`

export const Track = styled('div')`
  height: 8px;
  width: 90%;
  position: relative;
  userselect: none;
  height: 4px;
  background: #ddd;
  boxshadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
  borderradius: 2px;
`

export const Tick = styled('div')`
  position: absolute;
  top: 5px;
  left: ${(props: { percentage: number }) => `${props.percentage}%`};
  transform: translateX(-50%);
  :before {
    content: '';
    position: absolute;
    left: 0;
    background: rgba(0, 0, 0, 0.2);
    height: 5px;
    width: 2px;
    transform: translate(-50%, 0.7rem);
  }
`

export const TickLabel = styled('div')`
  position: absolute;
  font-size: 0.6rem;
  color: rgba(0, 0, 0, 0.5);
  top: 100%;
  transform: translate(-50%, 1.2rem);
  white-space: nowrap;
`

export const Segment = styled('div')`
  position: absolute;
  background: ${(props: { index: number; left: number; width: number }) =>
    props.index === 0
      ? '#3e8aff'
      : props.index === 1
      ? '#00d5c0'
      : props.index === 2
      ? '#f5c200'
      : '#ff6050'};
  left: ${(props: { left: number }) => `${props.left}%`};
  height: 100%;
  width: ${(props: { width: number }) => `${props.width}%`};
`

export const Handle = styled('button')`
  position: absolute;
  left: ${(props: { left: number }) => `${props.left}%`};
  zIndex: isActive ? 1 : 0;
  appearance: none;
  border: none;
  outline: none;
  background: #ff1a6b;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 100%;
  font-size: 0.7rem;
  white-space: nowrap;
  color: white;
  font-weight: ${(props: { active: boolean }) =>
    props.active ? 'bold' : 'normal'};
  transform: ${(props: { active: boolean }) =>
    props.active
      ? 'translate(-50%, -100%) scale(1.3)'
      : 'translate(-50%, -50%) scale(0.9)'};
`

function App() {
  const rangerRef = React.useRef<HTMLDivElement>(null)
  const [values, setValues] = React.useState<ReadonlyArray<number>>([
    15, 50, 80,
  ])

  const rangerInstance = useRanger<HTMLDivElement>({
    getRangerElement: () => rangerRef.current,
    values,
    min: 0,
    max: 100,
    stepSize: 1,
    onChange: (instance: Ranger<HTMLDivElement>) =>
      setValues(instance.sortedValues),
  })

  return (
    <div className="App" style={{ padding: 20 }}>
      <GlobalStyles />
      <h1>Custom Styles</h1>
      <br />
      <br />
      <Track ref={rangerRef}>
        {rangerInstance.getTicks().map(({ value, key, percentage }) => (
          <Tick key={key} percentage={percentage}>
            <TickLabel>{value}</TickLabel>
          </Tick>
        ))}
        {rangerInstance.getSteps().map(({ left, width }, i) => (
          <Segment key={i} index={i} left={left} width={width} />
        ))}
        {rangerInstance
          .handles()
          .map(
            (
              {
                value,
                onKeyDownHandler,
                onMouseDownHandler,
                onTouchStart,
                isActive,
              },
              i,
            ) => (
              <Handle
                key={i}
                onKeyDown={onKeyDownHandler}
                onMouseDown={onMouseDownHandler}
                onTouchStart={onTouchStart}
                role="slider"
                aria-valuemin={rangerInstance.options.min}
                aria-valuemax={rangerInstance.options.max}
                aria-valuenow={value}
                left={rangerInstance.getPercentageForValue(value)}
                active={isActive}
              >
                {value}
              </Handle>
            ),
          )}
      </Track>
      <br />
      <br />
      <br />
      <pre
        style={{
          display: 'inline-block',
          textAlign: 'left',
        }}
      >
        <code>
          {JSON.stringify({
            values,
          })}
        </code>
      </pre>
    </div>
  )
}

const root = createRoot(document.getElementById('root')!)

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.

Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.