JSFiddle - React, Tailwind, and code Playground

by Chris Lyon

HTML

<div id="grid-size-picker"></div>
<div id="selection-result"></div>

CSS

/* Component style */

#grid-size-picker {
}

.grid-picker-area {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: rgb(176, 176, 176) solid 1px;
    font-family: 'Segoe UI', Verdana, Arial, Helvetica, sans-serif;
    font-size: .75em;

    /* Disable text selection */
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
}
    .grid-picker-area .grid-cell {
        display: inline-block;
        margin: 0;
        padding: 0;
        text-align: center;
        color: rgb(113, 113, 113);
        background-color: #eee;
        cursor: pointer;
    }
        .grid-picker-area .grid-cell:hover {
            color: #fff;
            background-color: rgb(189, 189, 216);
        }
        .grid-picker-area .grid-cell.selected {
            color: #eee;
            background-color: rgb(49, 49, 84);
        }

JavaScript

/**
 * jQuery Grid Picker plugin
 *
 * @version 1.0
 * @author Chris Lyon <[email protected]>
 */
(function($) {
    /**
     * @param [options.cols] number of columns
     * @param [options.rows] number of rows
     * @param [options.cellSize] size of each cell (in pixels)
     */
    $.fn.gridpicker = function(options) {
        var grid = this,
            component = this,
            options = $.extend({
                name: this.data('name'),
                rows: this.data('rows') || 5,
                cols: this.data('cols') || 5,
                defaultRow: this.data('default-row'),
                defaultCol: this.data('default-col'),
                cellSize: this.data('cell-size') || 40,
                valueTransformer: function(row, col) {
                    return row + ',' + col;
                }
            }, options),
            row, col, cell;
        
        var gridArea = grid.find('.grid-picker-area');
        if (gridArea.length === 0) {
            gridArea = $('<div class="grid-picker-area"/>');
            grid.append(gridArea);
        }

        var hiddenEl = $('<input type="hidden" value=""/>');
        if (options.name)
            hiddenEl.attr('name', options.name);
        grid.append(hiddenEl);
        
        grid.css({
            width: (options.cols * options.cellSize) + 'px',
            height: (options.rows * options.cellSize) + 'px'
        });
        
        grid._value = { row: null, col: null };
        grid.val = function() {
            return grid._value;
        };
        
        var refreshSelection = function(row, col) {
            // Select all relevant cells between 1x1 this
            gridArea.find('.grid-cell').each(function() {
                var otherCell = $(this),
                    isSelected = otherCell.hasClass('selected');
                if (otherCell.data('row') <= row && 
                    otherCell.data('col') <= col) {
                    if (!isSelected)
                       ...