JSFiddle - React, Tailwind, and code Playground

by MeoMix

HTML

<script src="http://requirejs.org/docs/release/2.1.2/minified/require.js"></script>

<div class='clickable'>
    right-click me!
</div>

<div id="contextMenu">
</div>

<script type="text/template" id="contextMenuTemplate">
    
    <% groups.each(function(group){ %>
        <ul id="<%= group.cid %>">
            <% group.get('items').each(function(item){ %>
                <li>
                    <a id="<%= item.cid %>" href="#"><%= item.get('text') %></a>
                </li>
            <% }); %>
        </ul>
    <% }); %>
                        
</script>

CSS

body{
    height: 600px;
    width: 600px;
}

.clickable{
    position: absolute;
    padding: 20px;
    background: black;
    color: white;
    left: 20%;
    top: 20%;
}

#contextMenu {
    -webkit-box-shadow: 4px 7px 3px rgba(0, 0, 0, .3);
    background: #fff;
    position: absolute;
    z-index: 100;
    padding: 5px 0;
    border: 1px solid #BABABA;
    display: none;
}

#contextMenu ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

#contextMenu ul:not(:last-child) {
   border-bottom: 1px solid #EFEFEF;
}

#contextMenu ul li {
    overflow: hidden;
    max-width: 200px;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: #000;
    padding: 0 15px;
}

#contextMenu ul a {
    color: #000;
    font: 12px/2.2 sans-serif;
    text-decoration: none;
}

#contextMenu ul li:hover { background: #EFEFEF; }

#contextMenu ul li:active {
    background: rgb(36, 109, 149);
    color: #fff;
}

JavaScript

require.config({
    paths: {
      jquery: "http://code.jquery.com/jquery.min",
      underscore: "http://underscorejs.org/underscore-min",
      backbone: "http://backbonejs.org/backbone-min"
    },
  shim: {
    underscore: { exports: "_" },
    backbone: { deps: ["underscore", "jquery"], exports: "Backbone" }, 
    stickit: { deps: ["backbone"] }
  }
});

require(['jquery', 'underscore', 'backbone'], function($, _, Backbone){  
    require(['app'], function(App){});
});

define("app", ['contextMenuView'], function(ContextMenuView){
    
    $('.clickable').contextmenu(function(event){
        
        ContextMenuView.addGroup({
            position: 0,
            items: [{
                position: 0,
                text: 'And this is a much longer title that could go on for quite some time',
                onClick: function () {
                    
                }
            }]
        });

        ContextMenuView.show({
            top: event.pageY,
            left: event.pageX + 1
        });        

        return false;        
    });
    
});

define("contextMenuView", ['contextMenu', 'helpers'], function (ContextMenu, helpers) {
    'use strict';

    //  A singleton view which is either displayed somewhere in body with groups of items or empty and hidden.
    var ContextMenuView = Backbone.View.extend({

        el: $('#contextMenu'),
        
        template: _.template($('#contextMenuTemplate').html()),

        events: {
            'click li': 'onItemClick',
        },

        model: new ContextMenu,
        
        render: function () {
            this.$el.html(this.template(this.model.toJSON()));
            
            this.$el.find('a').each(function () {
                var parentUl = $(this).closest('ul');
                helpers.scrollElementInsideParent(this, parentUl);
            });

            //  TODO: Should this logic be part of 'show' or 'render' ?
            //  Prevent display outside viewport.
            var...