Skip to content

Click Potential, Competitive Density, CPC

Basic usage

tsx
import { Flex } from '@semcore/ui/base-components';
import { FilterTrigger } from '@semcore/ui/base-trigger';
import Button from '@semcore/ui/button';
import Dropdown from '@semcore/ui/dropdown';
import InputNumber from '@semcore/ui/input-number';
import { Text } from '@semcore/ui/typography';
import React, { useState, useRef } from 'react';

interface ValueState {
  from: string;
  to: string;
}

interface InputRangeProps {
  value: ValueState;
  changeValue: (updatedValue: ValueState) => void;
  [key: string]: unknown;
}

const InputRange: React.FC<InputRangeProps> = ({ value: valueState, changeValue, ...other }) => {
  const minRange = 1;
  const maxRange = 8;

  const fromRef = useRef<HTMLInputElement | null>(null);
  const toRef = useRef<HTMLInputElement | null>(null);

  const handleChange = (key: keyof ValueState) => (value: string | null) => {
    valueState[key] = value ?? '';
    changeValue({ ...valueState });
  };

  const handleBlur = () => {
    setTimeout(() => {
      if (document.activeElement !== fromRef.current && document.activeElement !== toRef.current) {
        const { from, to } = valueState;
        if (from > to && to !== '') {
          changeValue({
            from: Math.max(Number(to), minRange).toString(),
            to: Math.min(Number(from), maxRange).toString(),
          });
        }
        if ((to === '' || from === '') && (to !== '' || from !== '')) {
          changeValue({
            from: from !== '' ? from : minRange.toString(),
            to: to !== '' ? to : maxRange.toString(),
          });
        }
      }
    }, 0);
  };

  const { from, to } = valueState;

  return (
    <Flex {...other}>
      <InputNumber neighborLocation='right'>
        <InputNumber.Value
          min={minRange}
          max={maxRange}
          aria-label='From'
          placeholder='From'
          value={from}
          onChange={handleChange('from')}
          onBlur={handleBlur}
          ref={fromRef}
          autoFocus
        />
        <InputNumber.Controls />
      </InputNumber>
      <InputNumber neighborLocation='left'>
        <InputNumber.Value
          min={minRange}
          max={maxRange}
          aria-label='To'
          placeholder='To'
          value={to}
          onChange={handleChange('to')}
          onBlur={handleBlur}
          ref={toRef}
        />
        <InputNumber.Controls />
      </InputNumber>
    </Flex>
  );
};

const setTriggerText = ({ from, to }: { from: string; to: string }): string | null => {
  if (from !== '' && to !== '') {
    return from === to ? `${from}` : `${from}-${to}`;
  }
  return null;
};

const Demo = () => {
  const [value, setValue] = useState<ValueState>({ from: '', to: '' });
  const [filters, setFilters] = useState(false);
  const [visible, setVisible] = useState(false);
  const [displayValue, setDisplayValue] = useState('');
  const clearAll = () => {
    setFilters(false);
    setValue({ from: '', to: '' });
    setVisible(false);
  };
  const applyFilters = () => {
    const { from, to } = value;
    setVisible(false);
    setFilters(!!(from || to));
    setDisplayValue(setTriggerText(value) ?? '');
  };

  const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
    if (e.key === 'Enter') {
      applyFilters();
    }
  };

  return (
    <Dropdown visible={visible} onVisibleChange={setVisible}>
      <Dropdown.Trigger
        placeholder='Competitive Density'
        aria-label='Competitive Density'
        empty={!filters}
        onClear={clearAll}
        tag={FilterTrigger}
      >
        <span aria-hidden>Com.:</span>
        {' '}
        {displayValue}
      </Dropdown.Trigger>
      <Dropdown.Popper w={240} p={2} pb={3} aria-label='Competitive Density' tabIndex={-1}>
        <Text id='title-CD' size={200} bold>
          Custom range
        </Text>
        <InputRange
          role='group'
          aria-labelledby='title-CD'
          value={value}
          changeValue={setValue}
          my={2}
          onKeyDown={handleKeyDown}
        />
        <Button use='primary' theme='info' w='100%' onClick={applyFilters}>
          Apply
        </Button>
      </Dropdown.Popper>
    </Dropdown>
  );
};

export default Demo;

Released under the MIT License.

Released under the MIT License.