Zum Inhalt

src/ui/ColorRow.tsx

import { type Component, Show, createMemo } from 'solid-js';
import type { RGB } from '~/engine/types';
import { Tooltip } from './Tooltip';
import { PARAM_HELP } from './paramHelp';

interface Props {
  label: string;
  color: RGB;
  name?: string;
  onChange: (color: RGB) => void;
  info?: string;
}

export const ColorRow: Component<Props> = (props) => {
  const hex = createMemo(() => rgbToHex(props.color));
  const helpText = createMemo(() => props.info ?? PARAM_HELP[props.label]);

  const onPick = (e: Event) => {
    const target = e.currentTarget as HTMLInputElement;
    props.onChange(hexToRgb(target.value));
  };

  return (
    <div style={{ display: 'flex', 'align-items': 'center', gap: '12px' }}>
      <label
        style={{
          position: 'relative',
          width: '32px',
          height: '32px',
          'border-radius': 'var(--radius-md)',
          background: hex(),
          border: '1px solid var(--border-strong)',
          'box-shadow': `0 0 0 1px var(--surface-raised) inset, 0 0 12px -4px ${hex()}`,
          'flex-shrink': '0',
          cursor: 'pointer',
        }}
      >
        <input
          type="color"
          value={hex()}
          onInput={onPick}
          style={{
            position: 'absolute',
            inset: '0',
            width: '100%',
            height: '100%',
            opacity: '0',
            cursor: 'pointer',
          }}
        />
      </label>
      <div style={{ flex: '1', display: 'flex', 'flex-direction': 'column', gap: '2px' }}>
        <span
          style={{ 'font-family': 'var(--font-body)', 'font-size': '12.5px', 'font-weight': '500' }}
        >
          {props.label}
        </span>
        {props.name && (
          <span
            class="phys-mono"
            style={{
              'font-size': '10px',
              color: 'var(--fg-tertiary)',
              'letter-spacing': '0.06em',
              'text-transform': 'uppercase',
            }}
          >
            {props.name}
          </span>
        )}
      </div>
      <span class="phys-mono" style={{ 'font-size': '11px', color: 'var(--fg-secondary)' }}>
        {hex()}
      </span>
      <Show when={helpText()}>
        <Tooltip text={helpText() ?? ''} />
      </Show>
    </div>
  );
};

function rgbToHex(rgb: RGB): string {
  return `#${rgb
    .map((c) =>
      Math.round(c * 255)
        .toString(16)
        .padStart(2, '0'),
    )
    .join('')}`;
}

function hexToRgb(hex: string): RGB {
  const n = Number.parseInt(hex.replace('#', ''), 16);
  return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
}