Загрузка данных


import { ChangeEvent, useId } from 'react';

import styles from './index.module.scss';

interface RangeFieldProps {
  label?: string;
  value: number;
  min?: number;
  max?: number;
  step?: number;
  onChange: (event: ChangeEvent<HTMLInputElement>) => void;
}

export const RangeField = ({ label, value, min = 0, max = 100, step = 1, onChange }: RangeFieldProps) => {
  const inputId = useId();

  return (
    <div className={styles.range}>
      {label && (
        <label
          htmlFor={inputId}
          className={styles.range_label}
        >
          {label}
        </label>
      )}

      <div className={styles.range_wrapper}>
        <input
          id={inputId}
          type="range"
          min={min}
          max={max}
          step={step}
          value={value}
          className={styles.range_input}
          onChange={onChange}
        />

        <span className={styles.range_value}>{value}%</span>
      </div>
    </div>
  );
};



import { InputText } from 'exchange-elements/v2';
import { ChangeEvent, useEffect, useRef, useState } from 'react';

import styles from './index.module.scss';
import { RangeField } from './RangeField';

interface ColorFieldProps {
  label: string;
  value: string;
  onValueChange: (value: string) => void;
}

function normalizeHex(value: string): string {
  const next = value.trim().startsWith('#') ? value.trim().toLowerCase() : `#${value.trim().toLowerCase()}`;

  if (/^#[0-9a-f]{6}$/.test(next)) {
    return `${next}ff`;
  }

  if (/^#[0-9a-f]{8}$/.test(next)) {
    return next;
  }

  return '#000000ff';
}

function isValidHex(value: string): boolean {
  return /^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(value) || /^([0-9a-f]{6}|[0-9a-f]{8})$/i.test(value);
}

export const ColorField = ({ label, value, onValueChange }: ColorFieldProps) => {
  const colorInputRef = useRef<HTMLInputElement | null>(null);
  const [draft, setDraft] = useState(normalizeHex(value));

  useEffect(() => {
    setDraft(normalizeHex(value));
  }, [value]);

  const current = isValidHex(draft) ? normalizeHex(draft) : normalizeHex(value);
  const opacity = Math.round((parseInt(current.slice(7, 9), 16) / 255) * 100);

  const handleTextChange = (event: ChangeEvent<HTMLInputElement>) => {
    setDraft(event.target.value);
  };

  const handleTextBlur = () => {
    if (!isValidHex(draft)) {
      setDraft(normalizeHex(value));
      return;
    }

    const next = normalizeHex(draft);

    setDraft(next);
    onValueChange(next);
  };

  const handleColorChange = (event: ChangeEvent<HTMLInputElement>) => {
    const next = `${event.target.value}${current.slice(7, 9)}`;

    setDraft(next);
    onValueChange(next);
  };

  const handleOpacityChange = (event: ChangeEvent<HTMLInputElement>) => {
    const alpha = Math.round((Number(event.target.value) / 100) * 255)
      .toString(16)
      .padStart(2, '0');

    const next = `${current.slice(0, 7)}${alpha}`;

    setDraft(next);
    onValueChange(next);
  };

  return (
    <div className={styles.inputWrapper}>
      <InputText
        value={draft}
        label={label}
        labelPos="top"
        size="sm"
        onChange={handleTextChange}
        onBlur={handleTextBlur}
      />

      <button
        type="button"
        className={styles.button_color}
        style={{ backgroundColor: current }}
        onClick={() => colorInputRef.current?.click()}
      />

      <input
        ref={colorInputRef}
        type="color"
        className={styles.input_hidden}
        value={current.slice(0, 7)}
        onChange={handleColorChange}
        tabIndex={-1}
      />

      <RangeField
        value={opacity}
        onChange={handleOpacityChange}
      />
    </div>
  );
};



[data-theme='mxt'],
[data-theme='mb'][data-mode='light'] {
  --dd-item-text-selected: var(--neutral-2);
  --dd-item-bg-selected: var(--neutral-12);
}

[data-theme='tr'],
[data-theme='mb'][data-mode='dark'] {
  --dd-item-text-selected: var(--neutral-13);
  --dd-item-bg-selected: var(--neutral-5);
}

$button-color-top-margin: 29px;

.input {
  color: var(--neutral-12);

  input {
    color: var(--neutral-12);
  }
}

.dropdown {
  li {
    &[aria-selected='true'] {
      background-color: var(--dd-item-bg-selected);

      div[aria-label='Label'] {
        color: var(--dd-item-text-selected);
      }
    }

    svg {
      color: transparent;
    }
  }
}

.inputWrapper {
  position: relative;

  &:focus-within {
    background-color: transparent !important;
  }

  div {
    background-color: transparent;

    &:focus-within {
      background-color: transparent !important;
    }

    .input {
      background-color: transparent;
    }
  }
}

.button_color {
  position: absolute;
  top: $button-color-top-margin;
  right: var(--space-0500);
  width: var(--space-1375);
  height: var(--space-1375);
  border-radius: var(--space-0125);
}

.input_hidden {
  position: absolute;
  top: $button-color-top-margin;
  right: var(--space-0500);
  width: var(--space-1375);
  height: var(--space-1375);
  opacity: 0;
}

.range {
  display: flex;
  flex-direction: column;
  gap: var(--space-0250);

  &_label {
    font-size: var(--space-0875);
    color: var(--neutral-11);
  }

  &_wrapper {
    display: flex;
    align-items: center;
    gap: var(--space-0500);
  }

  &_value {
    min-width: var(--space-2000);
    color: var(--neutral-11);
    font-size: var(--space-0750);
    text-align: right;
  }

  &_input {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: var(--space-0250);
    background: transparent;
    cursor: pointer;
    outline: none;

    &::-webkit-slider-runnable-track {
      height: var(--space-0250);
      border: none;
      border-radius: 999px;
      background: var(--blue-1);
    }

    &::-webkit-slider-thumb {
      -webkit-appearance: none;
      appearance: none;
      width: var(--space-1000);
      height: var(--space-1000);
      margin-top: calc(var(--space-0375) * -1);
      border: none;
      border-radius: 50%;
      background: var(--blue-1);
    }

    &::-moz-range-track {
      height: var(--space-0250);
      border: none;
      border-radius: 999px;
      background: var(--blue-1);
    }

    &::-moz-range-thumb {
      width: var(--space-1000);
      height: var(--space-1000);
      border: none;
      border-radius: 50%;
      background: var(--blue-1);
    }
  }
}