1.0.1 • Published 4 years ago

nu-input v1.0.1

Weekly downloads
7
License
MIT
Repository
github
Last release
4 years ago

nu-input

Live Demo


Usage

import React from 'react';
import NuInput, { StyledSegment } from '../src';

export const rainbow: React.FC = () => {
  const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple'];

  function styler(v: string): StyledSegment<never>[] {
    return v.split('').map((ch, idx) => {
      return {
        offset: idx,
        length: 1,
        style: { color: colors[idx % colors.length] },
      };
    });
  }

  return (
    <div>
      <NuInput placeholder="rainbow~" styler={styler} />
    </div>
  );
};

More examples are available here. If you want to see what each individual examples in action, look them up in the Live Demo.

Structure

Below is the structure of NuInput to give you a better understanding what each keywords mean, and its inherent shortcomings.

The props are defined as

export interface NuInputProps<P> extends React.InputHTMLAttributes<HTMLInputElement> {
  styler?: SegmentStyler<P>;
  onSegmentClick?: SegmentEventHandler<P>;
  onSegmentMouseEnter?: SegmentEventHandler<P>;
  onSegmentMouseExit?: SegmentEventHandler<P>;
  wrapperClassName?: string;
  wrapperStyle?: React.CSSProperties;
  rendererClassName?: string;
  rendererStyle?: React.CSSProperties;
}

, and all props for the HTMLInputElement goes straight into the input.

Convenience Stylers

Currently there's only one convenience styler available - regexStyler

export interface MatchInfo {
  start: number;
  end: number;
  content: string;
}

export interface RegexStylerConfigs<P> {
  pattern: RegExp;
  style?: ((match: MatchInfo) => React.CSSProperties) | React.CSSProperties;
  className?: ((match: MatchInfo) => string) | string;
  payload?: ((match: MatchInfo) => P) | P;
}

function regexStyler<P>(configs: RegexStylerConfigs<P>): SegmentStyler<P>;

Many of the examples are built with regexStyler so check it out!

Caveats & Warnings

  1. Since the invisible input element is on top of the renderer element, any :hover pseudo-class applied to the spans on the renderer element will not work.

  2. To mitigate 1, the component accepts 3 mouse-related event handlers onSegmentClick, onSegmentMouseEnter, onSegmentMouseExit, so that it's possible to implement interactive elements like tooltips, links, and such.

  3. The input element and the renderer element MUST have the exact same (padding width + border width), font-size, font-family, and any other styles that might break the sync between the invisible input and the renderer. Examples

  4. I personally think it's a browser-level bug, but even if you keep 3, if you have too many segments within the input, the sync between the input element and the renderer starts to break. Long story short, <span>a</span> and a should theoretically have the same width, but if you go ahead and concatenate 500 <span>a</span>s and compare it with 500 as, the span version has slightly larger width :/ But as long as you have a reasonable amount of segments (the rainbow example is trivially the worst case), it should be fine.

Contributing

This component, although it works pretty well, definitely has a lot of room for improvement. If you liked it, leave a star, but if you didn't, definitely create an issue! Any PRs are welcome, and I'm very very very open to changes that I'm willing to reimplement everything from scratch if there's a better way. (contenteditable divs looked promising, but it was definitely harder to tame than this one though.)

License

MIT