nu-input v1.0.1
nu-input
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
Since the invisible input element is on top of the renderer element, any :hover pseudo-class applied to the
span
s on the renderer element will not work.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.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
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>
anda
should theoretically have the same width, but if you go ahead and concatenate 500<span>a</span>
s and compare it with 500a
s, thespan
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.)