Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-marks

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-marks

React component for combine editable text with any component using annotated text

  • 2.5.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
2
Maintainers
1
Weekly downloads
 
Created
Source

Marked Input · npm version Storybook

image

A React component that lets you combine editable text with any component using annotated text.

Feature

  • Powerful annotation tool
  • TypeScript
  • Two ways to configure
  • Support any components
  • Props customization
  • Utils for annotate and denote text
  • Button handling (Left, Right, Delete, Backspace, Esc)
  • Overlay with default the suggestion component
  • Zero dependency

Installation

Install the package via npm:

npm install rc-marked-input

Examples

A lot of examples can be seen in the storybook. You can also try a template on CodeSandbox.

Here is some examples to get you started.

Static marks · sandbox

import {MarkedInput} from "rc-marked-input";

const Mark = (props) => <mark onClick={_ => alert(props.value)}>{props.label}</mark>

const Marked = () => {
    const [value, setValue] = useState("Hello, clickable marked @[world](Hello! Hello!)!")
    return <MarkedInput Mark={Mark} value={value} onChange={setValue}/>
}
Configured · sandbox

The library allows you to configure the MarkedInput component in two ways.

Let's declare markups and suggestions data:

const Data = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth"]
const AnotherData = ["Seventh", "Eight", "Ninth"]
const Primary = "@[__label__](primary:__value__)"
const Default = "@[__label__](default)"

Using the components

import {MarkedInput, Option} from "rc-marked-input";

export const App = () => {
    const [value, setValue] = useState(
        "Enter the '@' for creating @[Primary Mark](primary:Hello!) or '/' for @[Default mark](default)!"
    )

    return (
        <MarkedInput Mark={Button} value={value} onChange={setValue}>
            <Option
                markup={Primary}
                data={Data}
                initMark={({label, value}) => ({label, primary: true, onClick: () => alert(value)})}
            />
            <Option
                markup={Default}
                trigger="/"
                data={AnotherData}
            />
        </MarkedInput>
    )
}

Using the createMarkedInput:

import {createMarkedInput} from "rc-marked-input";

const ConfiguredMarkedInput = createMarkedInput(Button, [{
    markup: Primary,
    data: Data,
    initMark: ({label, value}) => ({label, primary: true, onClick: () => alert(value)})
}, {
    trigger: '/',
    markup: Default,
    data: AnotherData
}])

const App = () => {
    const [value, setValue] = useState(
        "Enter the '@' for creating @[Primary Mark](primary:Hello!) or '/' for @[Default mark](default)!"
    )
    return <ConfiguredMarkedInput value={value} onChange={setValue}/>
}

Dynamic mark · sandbox

Marks can be dynamic: editable, removable, etc. via the useMark hook helper.

Editable
import {MarkedInput, useMark} from "rc-marked-input";

const Mark = () => {
    const {label, onChange} = useMark()

    const handleInput = (e) =>
        onChange({label: e.currentTarget.textContent ?? "", value: " "}, {silent: true})

    return <mark contentEditable onInput={handleInput} children={label}/>
}

export const Dynamic = () => {
    const [value, setValue] = useState("Hello, dynamical mark @[world]( )!")
    return <MarkedInput Mark={Mark} value={value} onChange={setValue}/>
}

Note: The silent option used to prevent re-rendering itself.

Removable
const RemovableMark = () => {
    const {label, onRemove} = useMark()
    return <mark onClick={onRemove} children={label}/>
}

export const Removable = () => {
    const [value, setValue] = useState("I @[contain]( ) @[removable]( ) by click @[marks]( )!")
    return <MarkedInput Mark={RemovableMark} value={value} onChange={setValue}/>
}
Focusable

If passed the reg prop of the useMark hook in ref of a component then it component can be focused by key operations.

Overlay

A default overlay is the suggestion component, but it can be easily replaced for any other.

Suggestions
export const DefaultOverlay = () => {
    const [value, setValue] = useState("Hello, default - suggestion overlay by trigger @!")
    return <MarkedInput Mark={Mark} value={value} onChange={setValue}>
        <Option data={['First', 'Second', 'Third']}/>
    </MarkedInput>
}
Custom overlay · sandbox
const Overlay = () => <h1>I am the overlay</h1>
export const CustomOverlay = () => {
    const [value, setValue] = useState("Hello, custom overlay by trigger @!")
    return <MarkedInput Mark={Mark} Overlay={Overlay} value={value} onChange={setValue}/>
}
Custom trigger
export const CustomTrigger = () => {
    const [value, setValue] = useState("Hello, custom overlay by trigger /!")
    return <MarkedInput Mark={Mark} Overlay={Overlay} value={value} onChange={setValue}>
        <Option trigger='/'/>
    </MarkedInput>
}
Positioned

The OverlayProps has a left and right absolute coordinate of a current caret position in the style prop.

const Tooltip = (props: OverlayProps) => <div style={{position: 'absolute', ...props.style}}>I am the overlay</div>
export const PositionedOverlay = () => {
    const [value, setValue] = useState("Hello, positioned overlay by trigger @!")
    return <MarkedInput Mark={Mark} Overlay={Tooltip} value={value} onChange={setValue}/>
}
Selectable

The OverlayProps provide some methods like onSelect for creating a new annotation.

const List = (props: OverlayProps) => <ul>
    <li onClick={() => props.onSelect({label: 'First'})}>Clickable First</li>
    <li onClick={() => props.onSelect({label: 'Second'})}>Clickable Second</li>
</ul>

export const SelectableOverlay = () => {
    const [value, setValue] = useState("Hello, suggest overlay by trigger @!")
    return <MarkedInput Mark={Mark} Overlay={List} value={value} onChange={setValue}/>
}

Note: Recommend to use the React.forwardRef for an overlay component. It used to detect outside click.

Event handlers

The onContainer prop allows to forward any of div events to a container of text.

<ConfiguredMarkedInput
    value={value} 
    onChange={setValue}
    onContainer={{
        onClick: (e) => console.log('onCLick'),
        onInput: (e) => console.log('onInput'),
        onBlur: (e) => console.log('onBlur'),
        //...
        onFocus: (e) => console.log('onFocus'),
        onKeyDown: (e) => console.log('onKeyDown'),
    }}
/>

Overall view

<MarkedInput Mark={Mark} Overlay={Overlay} value={value} onChange={setValue}>
    <Option
        trigger='@'
        markup='@[__label__](__value__)'
        data={Data}
        initMark={getCustomMarkProps}
        initOverlay={getCustomOverlayProps}
    />
    <Option
        trigger='/'
        markup='@(__label__)[__value__]'
        data={AnotherData}
        initMark={getAnotherCustomMarkProps}
        initOverlay={getAnotherCustomOverlayProps}
    />
</MarkedInput>

Or

const MarkedInput = createMarkedInput(Mark, Overlay, [{
    trigger: '@',
    markup: '@[__label__](__value__)',
    data: Data,
    initMark: getCustomMarkProps,
    initOverlay: getCustomOverlayProps,
}, {
    trigger: '/',
    markup: '@(__label__)[__value__]',
    data: AnotherData,
    initMark: getAnotherCustomMarkProps,
    initOverlay: getAnotherCustomOverlayProps,
}])

const App = () => <MarkedInput value={value} onChange={setValue}/>

API

MarkedInput

NameTypeDefaultDescription
valuestringAnnotated text with markups for mark
onChange(value: string) => voidChange event
MarkComponentType<T = MarkProps>Component that used for render markups
OverlayComponentType<T = OverlayProps>SuggestionsComponent that is rendered by trigger
readOnlybooleanundefinedPrevents from changing the value
onContainerDivEventsundefinedForward any div events to a text container

Option

NameTypeDefaultDescription
markupstring@[__label__](__value__)Template string instead of which the mark is rendered
Must contain placeholders: __label__ and optional __value__ __value__
For example: @[__label__](__value__)
triggerstring"@"Sequence of symbols for calling the overlay.
datastring[][]Data for a overlay component. By default, it is suggestions.
initMark(props: MarkProps) => TundefinedFunction to initialize props for mark render. Gets arguments from found markup
initOverlay(props: OverlayProps) => T1undefinedFunction to initialize overlay props to your requirements.
If missing then passed overlay props directly.

Helpers

NameTypeDescription
createMarkedInput(Mark: ComponentType, options: OptionProps[]): ConfiguredMarkedInput
(Mark: ComponentType, Overlay: ComponentType, options: OptionProps<T, T1>[]): ConfiguredMarkedInput
Create the configured MarkedInput component.
annotate(markup: Markup, label: string, value?: string) => stringMake annotation from the markup
denote(value: string, callback: (mark: Mark) => string, ...markups: Markup[]) => stringTransform the annotated text
useMark() => DynamicMarkAllow to use dynamic mark

Types

interface MarkProps {
    label: string
    value?: string
}
interface OverlayProps {
    /**
     * Style with caret absolute position. Used for placing an overlay.
     */
    style: {
        left: number
        top: number
    }
    /**
     * Used for close overlay.
     */
    onClose: () => void
    /**
     * Used for insert an annotation instead a triggered value.
     */
    onSelect: (value: MarkProps) => void
    /**
     * Trigger details
     */
    trigger: Trigger
}
type Trigger = {
    /**
     * Found value via a trigger
     */
    value: string,
    /**
     * Triggered value
     */
    source: string,
    /**
     * Piece of text, in which was a trigger
     */
    span: string,
    /**
     * Html element, in which was a trigger
     */
    node: Node,
    /**
     * Start position of a trigger
     */
    index: number,
    /**
     * Trigger's option
     */
    option: OptionType
}

Contributing

If you want to contribute, you are welcome! Create an issue or start a discussion.

Keywords

FAQs

Package last updated on 23 Nov 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc