JSFiddle - React, Tailwind, and code Playground

by whelkaholism

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js"></script>
<script src="https://cdn.cfront-cloud.co.uk/scripts/koa1.2/cf.DOM.js"></script>
<script src="https://cdn.cfront-cloud.co.uk/scripts/koa1.2/cf.linq.js"></script>
<input data-bind="lookup: { boundField: Value, source: Src, allowDropDown: true }"/>
<div data-bind="text: ko.toJSON(Value)"></div>

CSS

.section > div:not(.group) > div.koa-lookup,
.koa-lookup {
    display: inline-block;
    position: relative;
    box-sizing: border-box;
    width: fit-content;
}

.koa-lookup .koa-lookup-items {
    display: none;
    position: absolute;
    left: 0;
    z-index: 1;
}

.koa-lookup .koa-lookup-items.loading {
    display: block;
}

.koa-lookup .koa-lookup-items.show {
    display: block;
}

.koa-lookup .koa-lookup-items.loading li:not(.loading) {
    display: none;
}

.koa-lookup .koa-lookup-items.show li.loading {
    display: none;
}

.koa-lookup .koa-lookup-items.show {
    display: block;
    background: #ffffffff;
    padding: 0.5em;
    border: 1px solid #dddddd;
    border-top: none;
}

.koa-lookup .koa-lookup-items li {
    padding: 0.25em;
}

.koa-lookup .koa-lookup-items li:hover,
.koa-lookup .koa-lookup-items li.active {
    background: #cccccc;
}

.koa-lookup .koa-lookup-items l.current {
    background: #cccccc;
    border-left: 3px solid #404040;
}

.koa-lookup input {
    width: 100%;
}

.koa-lookup .koa-lookup-items {
    max-height: 40em;
    overflow-y: auto;
}

.koa-lookup { display: flex }
.koa-lookup-dropdown::before { content: 'V'}
.koa-lookup.is-shown .koa-lookup-dropdown::before { content: '^'}

JavaScript

(function()
{
    var _getConfig = function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
    {
        var o = ko.unwrap(f_valueaccessor());

        var res = {
            obs: o.boundField,
            idobs: o.boundIDField,
            idField: 'ID',
            valueField: 'Name',
            source: o.source,
            itemTemplate: o.itemTemplate || '<li>{{Name}}</li>',
            valueTemplate: o.itemTemplate || '{{Name}}',
            rateLimit: typeof (o.rateLimit) != 'undefined' ? o.rateLimit : 300,
            renderItem: o.renderItem,
            selected: o.selected,
            allowDropDown: !!o.allowDropDown,
            _input: el
        };

        return res;
    };

    var _populateTemplate = function(template, item, cfg)
    {
        var res = (template || '').replace(/\{\{([^\}]+)\}\}/, function(m, g1)
        {
            var repres = item && ko.unwrap(item[g1]);
            return repres || '';
        });

        return res || '';
    };

    var _callSource = function(term, cfg)
    {
        return new Promise(function(resolve)
        {
            var res = null;
            var a = null;

            if (ko.isObservableArray(cfg.source))
                a = cfg.source();
            else if (typeof (cfg.source) == 'function')
                cfg.source(term).then(resolve);
            else
                a = cfg.source;

            if (a != null)
            {
                res = a.where(cfg.predicate || function(x)
                {
                    return new RegExp(term, 'i').test(typeof (x) == 'string' ? x : (x && x[cfg.valueField]));
                });
            }

            if (res != null)
                resolve(res);
        });
    };

    var _renderItem = function(item, cfg)
    {
        if (cfg.renderItem)
            return cfg.renderItem(item, cfg);
        else
            return _populateTemplate(cfg.itemTemplate,...