JSFiddle - React, Tailwind, and code Playground
by stitot
HTML
<script src="https://cdn.jsdelivr.net/npm/@highcharts/grid-pro/grid-pro.js"></script>
<div class="app">
<aside class="panel">
<div class="tabs">
<button class="tab active" data-tab="data">Data</button>
<button class="tab" data-tab="config">Config</button>
<button class="tab" data-tab="theming">Theming</button>
</div>
<div class="panel-body">
<!-- DATA TAB -->
<div class="tabview active" id="tab-data">
<div class="section">
<div class="section-h">
<div>Data</div>
<span class="badge">dataTable.columns</span>
</div>
<div class="section-b">
<div class="row">
<div class="row-actions">
<button id="addCol" type="button" class="btn">Add column</button>
<button id="removeCol" type="button" class="btn btn-ghost">Remove last</button>
</div>
<div class="help">
Edit column keys + types. Click <b>Update data</b> to rebuild the grid.
</div>
</div>
<div class="row">
<div>
<label>Row count</label>
</div>
<input id="rowCount" type="number" min="1" step="1" />
<div class="help">Number of rows to generate when you click Update data.</div>
</div>
<div class="data-table-wrap">
<table class="data-table" id="dataColsTable">
<thead>
<tr>
<th style="width:60%">Key</th>
<th style="width:40%">Type</th>
</tr>
</thead>
<tbody id="dataColsBody"></tbody>
</table>
</div>
<div class="row">
<label class="check">
<input id="randomizeData" type="checkbox" />
<span>Randomize generated...
CSS
@import url("https://cdn.jsdelivr.net/npm/@highcharts/grid-pro/css/grid-pro.css");
:root {
--panel-w: 420px;
--bg: #f6f7fb;
--panel-bg: #ffffff;
--surface: #ffffff;
--surface-2: #fbfbfe;
--text: #111827; /* slate-900 */
--muted: #6b7280; /* gray-500 */
--border: #e5e7eb; /* gray-200 */
--border-2: #d1d5db; /* gray-300 */
--shadow: 0 10px 30px rgba(17, 24, 39, 0.08);
--shadow-sm: 0 4px 14px rgba(17, 24, 39, 0.08);
--accent: #2563eb; /* blue-600 */
--accent-soft: rgba(37, 99, 235, 0.10);
--danger: #dc2626; /* red-600 */
--radius: 14px;
--radius-sm: 10px;
--font: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
}
html, body {
height: 100%;
margin: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font);
}
.app {
height: 100vh;
display: flex;
gap: 12px;
padding: 12px;
box-sizing: border-box;
}
.panel {
width: var(--panel-w);
min-width: var(--panel-w);
background: var(--panel-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
display: flex;
flex-direction: column;
}
.tabs {
display: flex;
gap: 8px;
padding: 10px;
border-bottom: 1px solid var(--border);
background: var(--surface-2);
}
.tab {
appearance: none;
border: 1px solid var(--border);
background: #fff;
color: var(--muted);
padding: 8px 10px;
border-radius: 999px;
cursor: pointer;
font-weight: 700;
font-size: 13px;
transition: transform 0.05s ease, border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.tab:hover {
border-color: var(--border-2);
color: var(--text);
}
.tab:active {
transform: translateY(1px);
}
.tab.active {
background: var(--accent-soft);
border-color: rgba(37, 99, 235, 0.35);
color:...
JavaScript
/* Global Grid */
let grid;
/** Base data from your fiddle */
const baseOptions = {
dataTable: {
columns: {
product: ["Apples", "Pears", "Plums", "Bananas"],
weight: [100, 40, 0.5, 200],
price: [1.5, 2.53, 5, 4.5]
}
}
};
const LOREM_WORDS_20 = [
"lorem", "ipsum", "dolor", "sit", "amet",
"consectetur", "adipiscing", "elit", "sed", "do",
"eiusmod", "tempor", "incididunt", "ut", "labore",
"et", "dolore", "magna", "aliqua", "enim"
];
/**
* IMPORTANT CHANGE:
* virtualization is intentionally UNDEFINED here to allow auto-virtualization.
* (Tri-state UI controls it: auto => undefined, on => true, off => false)
*/
const UI_BASELINE_DEFAULTS = {
id: "",
caption: { text: "" },
description: { text: "" },
credits: { enabled: true, position: "bottom", text: "", href: "" },
rendering: {
theme: "hcg-theme-default",
header: { enabled: true },
rows: {
// virtualization: undefined (intentionally absent)
virtualizationThreshold: 50,
strictHeights: false,
bufferSize: 10,
minVisibleRows: 2
},
columns: {
resizing: { enabled: true, mode: "" }
}
},
time: { timezone: "UTC" },
columnDefaults: {
dataType: "",
width: "",
sorting: { sortable: true },
filtering: { enabled: false, inline: false },
header: { format: "{id}" },
cells: { format: "{value}" }
}
};
const state = {
uiOptions: clone(UI_BASELINE_DEFAULTS),
selectedColumnId: "product",
advancedPatchText: "{}",
advancedPatchError: "",
dataEditor: {
columns: [
{ key: "product", type: "string" },
{ key: "weight", type: "number" },
{ key: "price", type: "number" }
],
randomize: false,
rowCount: 12
}
};
/* ---------------------------
* Utilities
* --------------------------- */
function deepMerge(target, src) {
if (!src || typeof src !== "object") return...