Source: 04-CTagColumn.jsx

/**
 * Tags column.
 *
 * This column for tags multiselect. Value will be comma-separated set of tags or empty string.
 *
 * @arg {List[]} this.props.options - Tag list.
 * @arg {string} this.props.options[].0 - Tag key.
 * @arg {string} this.props.options[].1 - Tag text.
 * @arg {string} this.props.endpoint - Endpoint for loading options.
 * @arg {Object} this.props.params - Additional parameters to select query
 */

class CTagColumn extends CTableColumn{
    constructor() {
        super();

        this.filterChanged = this.filterChanged.bind(this);
        this.addClicked = this.addClicked.bind(this);
        this.deleteClicked = this.deleteClicked.bind(this);
        this.showTagPanel = this.showTagPanel.bind(this);
        this.hideTagPanel = this.hideTagPanel.bind(this);


        this.state = {options: [], values: [], editor_panel_opened:false}
        this.ref = createRef();
    }

    componentDidMount() {
        this.setState({values: this.value() === null ? [] : this.value().split(","),
                       editor_panel_opened:false});

        if (typeof this.props.endpoint === 'undefined') {
            this.setState({options: this.props.options})
        } else {
            this.props.table.load_options(this.props.endpoint, this.props.params, this, this.ref);
        }
    }

    showTagPanel(e){
        if(e.target.classList.contains("delete")) return;
        this.setState({editor_panel_opened:true});
    }

    hideTagPanel(){
        this.setState({editor_panel_opened:false});
    }

    render_cell() {
        var self = this;
        var cvl = this.value() === null ? [] : this.value().split(",");
        var cvalues = this.state.options.filter(function(item){return cvl.indexOf(item[0]) != -1;});
        return <div class="tags">{cvalues.map(function(c,i){ return <span class="tag is-medium">{c[1]}</span>; })}</div>;
    }

    filterChanged(e) {
        if(e.target.value == ''){
            this.props.table.change_search_for_column(this.props.column, null);
        } else {
            this.props.table.change_search_for_column(this.props.column, e.target.value);
        }
    }

    render_search() {
        if (typeof this.props.searching === 'undefined') {
            // do nothing
        } else {
            return <div class="select">
                     <select onChange={this.filterChanged} value={this.props.searching}>
                       <option value=''>{this.props.table.props.lang.no_filter}</option>
                       {this.state.options.map(function(item){return <option value={item[0]}>{item[1]}</option>; })}
                     </select>
                   </div>;
        }
    }

    deleteClicked(e){
        var tag = e.target.dataset.tagname;
        var del_filtred = this.state.values.filter(function(item){return item != tag;});
        this.setState({values: del_filtred});
        this.props.table.notify_changes(this.props.row, this.props.column, del_filtred === [] ? null : del_filtred.join(','));
    }

    addClicked(e){
        e.preventDefault();
        if(!this.state.values.includes(e.target.dataset.tagname)){
            var add_filtred = this.state.values.concat(e.target.dataset.tagname);
            this.setState({values: add_filtred});
            this.props.table.notify_changes(this.props.row, this.props.column, add_filtred === [] ? null : add_filtred.join(','));
        }
    }

    render_editor() {
        var self = this;
        var alltag = <div class="tags" style="margin-left: 1em; margin-right: 1em;">{this.state.options.map(function(c,i){ return <span class="tag button" data-tagname={c[0]} onMouseDown={self.addClicked}>{c[1]}</span>; })}</div>;
        var cvalues = this.state.options.filter(function(item){return self.state.values.indexOf(item[0]) != -1;});
        var taglist = <div class="tags" style="margin-right: 2em;">{cvalues.map(function(c,i){ return <span class="tag">{c[1]}<button class="delete is-small" data-tagname={c[0]} onClick={self.deleteClicked}></button></span>; })}</div>;

        return <div class="field" ref={this.ref}>
                   <label class="label">{this.title()}</label>
                   <div class="control">
                       <div class={this.state.editor_panel_opened ? "dropdown is-active" : "dropdown"} style="width: 100%;">
                         <div class="dropdown-trigger" style="width: 100%;">
                           <button class="input select" onClick={this.showTagPanel} onBlur={this.hideTagPanel}>{taglist}</button>
                         </div>
                         <div class="dropdown-menu" id="dropdown-menu" role="menu">
                           <div class="dropdown-content">
                             {alltag}
                           </div>
                         </div>
                       </div>
                   </div>
                   {this.props.footnote ? <div class="help">{this.props.footnote}</div> : ''}
               </div>;
    }
}