├── .github └── workflows │ └── main.yml ├── LICENSE ├── README.md ├── background.js ├── content.js ├── getStorage.js ├── icon.png ├── manifest.json ├── popup.css ├── popup.html ├── popup.js ├── tabulator.lic ├── tabulator.min.css └── tabulator.min.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push but only when manifest.json (is changed) 8 | push: 9 | branches: [ main ] 10 | paths: 11 | 'manifest.json' 12 | 13 | # Allows to run this workflow manually 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # use meta script 26 | - name: built and push addon 27 | env: 28 | ISSUER: ${{secrets.ISSUER}} 29 | SECRET: ${{secrets.SECRET}} 30 | run: curl 'https://raw.githubusercontent.com/igorlogius/meta-addon-builder/main/README' | sh 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 igorlogius 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://raw.githubusercontent.com/igorlogius/igorlogius/main/geFxAddon.png)](https://addons.mozilla.org/en-US/firefox/addon/localstorage-editor/) 2 | 3 | [Report a bug, make a suggestion or ask a question](https://github.com/igorlogius/igorlogius/issues/new/choose) 4 | 5 | https://github.com/igorlogius/localstorage-editor/assets/67047467/1b77950f-cbc2-4c2f-bbf1-ff0b5e6ae3db 6 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | /* global browser */ 2 | 3 | browser.runtime.onMessage.addListener((data, sender) => { 4 | let text = ""; 5 | let tabId = sender.tab.id; 6 | 7 | if (data.hasSessionStorageData) { 8 | text += "S"; 9 | } 10 | if (data.hasLocalStorageData) { 11 | text += "L"; 12 | } 13 | browser.browserAction.setBadgeText({ tabId, text }); 14 | browser.browserAction.enable(tabId); 15 | }); 16 | 17 | browser.browserAction.disable(); 18 | -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | /* global browser */ 2 | 3 | function detect() { 4 | const hasSessionStorageData = window.sessionStorage.length > 0; 5 | const hasLocalStorageData = window.localStorage.length > 0; 6 | // send Message to Background Script to update/show the pageAction icon 7 | browser.runtime.sendMessage({ hasSessionStorageData, hasLocalStorageData }); 8 | } 9 | 10 | browser.runtime.onMessage.addListener((data /*, sender*/) => { 11 | localStorage.clear(); 12 | sessionStorage.clear(); 13 | 14 | let el, store, key, value; 15 | let i = 0; 16 | for (i = 0; i < data.length; i++) { 17 | el = data[i]; 18 | store = el.store; 19 | key = el.key; 20 | value = el.value; 21 | switch (store) { 22 | case "Local": 23 | localStorage.setItem(key, value); 24 | break; 25 | case "Session": 26 | sessionStorage.setItem(key, value); 27 | break; 28 | } 29 | } 30 | }); 31 | 32 | setInterval(detect, 3000); 33 | 34 | detect(); 35 | -------------------------------------------------------------------------------- /getStorage.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let i = 0, 3 | key, 4 | value, 5 | store; 6 | let ret = []; 7 | for (i = 0; i < localStorage.length; i++) { 8 | store = "Local"; 9 | key = localStorage.key(i); 10 | try { 11 | value = JSON.parse(localStorage.getItem(key)); 12 | value = JSON.stringify(value, null, 1); 13 | } catch (e) { 14 | value = localStorage.getItem(key); 15 | } 16 | ret.push({ store, key, value }); 17 | } 18 | for (i = 0; i < sessionStorage.length; i++) { 19 | store = "Session"; 20 | key = sessionStorage.key(i); 21 | try { 22 | value = JSON.parse(sessionStorage.getItem(key)); 23 | value = JSON.stringify(value, null, 1); 24 | } catch (e) { 25 | value = sessionStorage.getItem(key); 26 | } 27 | ret.push({ store, key, value }); 28 | } 29 | return ret; 30 | })(); 31 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorlogius/localstorage-editor/722f4a498ab69401108c08166daeeed2fd26f22c/icon.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "igorlogius", 3 | "homepage_url": "https://github.com/igorlogius", 4 | "background": { 5 | "scripts": ["background.js"] 6 | }, 7 | "browser_action": { 8 | "default_area": "navbar", 9 | "default_popup": "popup.html" 10 | }, 11 | "content_scripts": [ 12 | { 13 | "js": ["content.js"], 14 | "matches": [""] 15 | } 16 | ], 17 | "icons": { 18 | "128": "icon.png" 19 | }, 20 | "description": "Add, edit, copy, remove, export and import local- and session- storage data", 21 | "manifest_version": 2, 22 | "name": "LocalStorage Editor", 23 | "permissions": ["storage", "clipboardRead", "clipboardWrite", ""], 24 | "version": "1.3.20" 25 | } 26 | -------------------------------------------------------------------------------- /popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: lightgray; 3 | width: 700px; 4 | padding: 3px; 5 | } 6 | 7 | button { 8 | min-width: 5vw; 9 | } 10 | 11 | #log { 12 | width: 100%; 13 | } 14 | 15 | #tip { 16 | font-size: 0.8em; 17 | } 18 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | (?) 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | /* global browser Tabulator */ 2 | 3 | async function getFromStorage(type, id, fallback) { 4 | let tmp = await browser.storage.local.get(id); 5 | return typeof tmp[id] === type ? tmp[id] : fallback; 6 | } 7 | 8 | let table = null; 9 | let tableData = null; 10 | 11 | // button refs 12 | const impbtn = document.getElementById("impbtn"); 13 | const savbtn = document.getElementById("savbtn"); 14 | const disbtn = document.getElementById("disbtn"); 15 | const expbtn = document.getElementById("expbtn"); 16 | const cpybtn = document.getElementById("cpybtn"); 17 | const delbtn = document.getElementById("delbtn"); 18 | const addbtn = document.getElementById("addbtn"); 19 | const log = document.getElementById("log"); 20 | const tip = document.getElementById("tip"); 21 | 22 | const tips = [ 23 | "Holding shift and click dragging over the row selector column to invert the selection state", 24 | "Use Copy & Import to quickly duplicate items", 25 | "Filtering doenst not change the item selection", 26 | "Copy & Download actions only take selected items into account", 27 | "Imported items get autoselected", 28 | "Cell validation erros get shown, when hovering over a cell", 29 | "Use the row handle to reorder rows for copy or downloading", 30 | "Use shift+enter in a value cell to submit", 31 | ]; 32 | 33 | let validateAndHighlightTimer; 34 | 35 | function delayedValidateAndHighlight(data) { 36 | clearTimeout(validateAndHighlightTimer); 37 | validateAndHighlightTimer = setTimeout(() => { 38 | table.validate(); 39 | if (dataDifferentFromInital(data)) { 40 | highlightChange(); 41 | } else { 42 | unhighlightChange(); 43 | } 44 | }, 1000); 45 | } 46 | 47 | let logTimerId; 48 | 49 | function showLogMessage(msg) { 50 | log.innerText = msg; 51 | clearTimeout(logTimerId); 52 | logTimerId = setTimeout(() => { 53 | log.innerText = "..."; 54 | }, 5000); 55 | } 56 | 57 | function dataDifferentFromInital(newTableData) { 58 | //const newTableData = table.getData(); 59 | 60 | if (tableData.length !== newTableData.length) { 61 | return true; 62 | } 63 | // now we need to compare alle elements 64 | let equalFound; 65 | for (const i of newTableData) { 66 | equalFound = false; 67 | for (const j of tableData) { 68 | if (i.store === j.store && i.key === j.key && i.value === j.value) { 69 | equalFound = true; 70 | } 71 | } 72 | if (!equalFound) { 73 | return true; 74 | } 75 | } 76 | return false; 77 | } 78 | 79 | function getTimeStampStr() { 80 | const d = new Date(); 81 | let ts = ""; 82 | [ 83 | d.getFullYear(), 84 | d.getMonth() + 1, 85 | d.getDate(), 86 | d.getHours(), 87 | d.getMinutes(), 88 | d.getSeconds(), 89 | ].forEach((t, i) => { 90 | ts = ts + (i !== 3 ? "-" : "_") + (t < 10 ? "0" : "") + t; 91 | }); 92 | return ts.substring(1); 93 | } 94 | 95 | async function getTblData() { 96 | let data = []; 97 | try { 98 | data = await browser.tabs.executeScript({ 99 | file: "getStorage.js", 100 | }); 101 | } catch (e) { 102 | data = []; 103 | } 104 | return data; 105 | } 106 | 107 | function highlightChange() { 108 | savbtn.style.borderColor = "green"; 109 | disbtn.style.borderColor = "red"; 110 | } 111 | 112 | function unhighlightChange() { 113 | savbtn.style.borderColor = ""; 114 | disbtn.style.borderColor = ""; 115 | } 116 | 117 | // add new items 118 | addbtn.addEventListener("click", async () => { 119 | table.deselectRow(); 120 | table.addRow( 121 | { 122 | store: "", 123 | key: "", 124 | value: "", 125 | }, 126 | true, 127 | ); // add at the top 128 | //highlightChange(); 129 | }); 130 | 131 | // delete selected items 132 | delbtn.addEventListener("click", () => { 133 | //let changed = false; 134 | /* 135 | table.getSelectedRows().forEach( (row) => { 136 | row.delete(); 137 | //changed = true; 138 | }); 139 | */ 140 | table.deleteRow(table.getSelectedRows()); 141 | /* 142 | if(changed){ 143 | highlightChange(); 144 | } 145 | */ 146 | }); 147 | 148 | // discard changes / reload 149 | disbtn.addEventListener("click", () => { 150 | window.location.reload(); 151 | }); 152 | 153 | // save/commit changes 154 | savbtn.addEventListener("click", async () => { 155 | if (table.validate() !== true) { 156 | table.alert("Validation failed. Submit cancelled!", "error"); 157 | setTimeout(function () { 158 | table.clearAlert(); 159 | }, 2500); 160 | return; 161 | } 162 | const data = table.getData(); 163 | const tabs = await browser.tabs.query({ 164 | currentWindow: true, 165 | active: true, 166 | }); 167 | browser.tabs.sendMessage(tabs[0].id, data); 168 | //unhighlightChange(); 169 | table.alert("Syncing Storage ... ", "msg"); 170 | setTimeout(function () { 171 | //table.clearAlert(); 172 | window.location.reload(); // keep it simple 173 | }, 1000); 174 | }); 175 | 176 | // export/download as file 177 | expbtn.addEventListener("click", async () => { 178 | let selectedRows = table.getSelectedRows(); 179 | // order the selected by position 180 | selectedRows.sort((a, b) => { 181 | return a.getPosition() - b.getPosition(); 182 | }); 183 | const expData = []; 184 | selectedRows.forEach((row) => { 185 | const rowData = row.getData(); 186 | expData.push(rowData); 187 | }); 188 | 189 | if (expData.length > 0) { 190 | const tabs = await browser.tabs.query({ 191 | currentWindow: true, 192 | active: true, 193 | }); 194 | const url = new URL(tabs[0].url); 195 | 196 | const content = JSON.stringify(expData, null, 4); 197 | let dl = document.createElement("a"); 198 | const href = 199 | "data:application/json;charset=utf-8," + encodeURIComponent(content); 200 | dl.setAttribute("href", href); 201 | dl.setAttribute( 202 | "download", 203 | "Storage Export " + 204 | getTimeStampStr() + 205 | " " + 206 | encodeURIComponent(url.hostname).replaceAll(".", "_") + 207 | ".json", 208 | ); 209 | dl.setAttribute("visibility", "hidden"); 210 | dl.setAttribute("display", "none"); 211 | document.body.appendChild(dl); 212 | dl.click(); 213 | document.body.removeChild(dl); 214 | showLogMessage("Downloaded " + expData.length + " items"); 215 | } else { 216 | showLogMessage("No items to download selected"); 217 | } 218 | }); 219 | 220 | // copy/export to clipboard 221 | cpybtn.addEventListener("click", () => { 222 | let selectedRows = table.getSelectedRows(); 223 | // order the selected by position 224 | selectedRows.sort((a, b) => { 225 | return b.getPosition() - a.getPosition(); 226 | }); 227 | const expData = []; 228 | selectedRows.forEach((row) => { 229 | const rowData = row.getData(); 230 | expData.push(rowData); 231 | }); 232 | if (expData.length > 0) { 233 | const content = JSON.stringify(expData, null, 4); 234 | navigator.clipboard.writeText(content); 235 | } 236 | if (expData.length > 0) { 237 | showLogMessage("Copied " + expData.length + " items to clipboard"); 238 | } else { 239 | showLogMessage("No items to copy selected"); 240 | } 241 | }); 242 | 243 | // import from clipboard 244 | impbtn.addEventListener("click", async () => { 245 | table.deselectRow(); 246 | try { 247 | const clipText = await navigator.clipboard.readText(); 248 | const config = JSON.parse(clipText); 249 | let import_count = 0; 250 | config.forEach((selector) => { 251 | if ( 252 | typeof selector.store === "string" && 253 | typeof selector.key === "string" && 254 | typeof selector.value === "string" 255 | ) { 256 | table.addRow( 257 | { 258 | store: selector.store, 259 | key: selector.key, 260 | value: selector.value, 261 | }, 262 | false, 263 | ); 264 | import_count++; 265 | } 266 | }); 267 | /* 268 | if(import_count > 0) { 269 | highlightChange(); 270 | } 271 | */ 272 | showLogMessage("Imported " + import_count + " items"); 273 | } catch (e) { 274 | showLogMessage("Import failed: \n" + e.toString()); 275 | } 276 | }); 277 | 278 | function storeHeaderFilter(headerValue, rowValue /*, rowData, filterParams*/) { 279 | return headerValue.length < 1 || headerValue.includes(rowValue); 280 | } 281 | 282 | var uniqueKey = function (cell, value, parameters) { 283 | //cell - the cell component for the edited cell 284 | //value - the new input value of the cell 285 | //parameters - the parameters passed in with the validator 286 | 287 | //return value % parameters.divisor; //don't allow values divisible by divisor ; 288 | 289 | //const val = cell.getValue(); 290 | let tmp; // undefined 291 | 292 | for (let row of table.getData()) { 293 | if (row.key === value) { 294 | if (tmp === row.store) { 295 | return false; // find the same thing again 296 | } else { 297 | tmp = row.store; // find itself or something else 298 | } 299 | } 300 | } 301 | return true; 302 | }; 303 | 304 | async function onDOMContentLoaded() { 305 | tip.innerText = "Tip: " + tips[Math.floor(Math.random() * tips.length)]; 306 | 307 | table = new Tabulator("#mainTable", { 308 | autoColumns: true, 309 | height: "460px", 310 | placeholder: "No items found", 311 | layout: "fitDataStretch", 312 | pagination: false, 313 | movableRows: true, 314 | validationMode: "highlight", 315 | initialSort: [ 316 | { column: "key", dir: "asc" }, 317 | { column: "store", dir: "asc" }, 318 | ], 319 | columns: [ 320 | { 321 | rowHandle: true, 322 | formatter: "handle", 323 | headerSort: false, 324 | frozen: true, 325 | width: 30, 326 | minWidth: 30, 327 | }, 328 | { 329 | formatter: "rowSelection", 330 | titleFormatter: "rowSelection", 331 | titleFormatterParams: { 332 | rowRange: "active", //only toggle the values of the active filtered rows 333 | }, 334 | hozAlign: "left", 335 | headerSort: false, 336 | cellClick: (e, cell) => { 337 | cell.getRow().toggleSelect(); 338 | }, 339 | width: 30, 340 | minWidth: 30, 341 | }, 342 | { 343 | title: "Storage", 344 | field: "store", 345 | cellMouseOver: function (e, cell) { 346 | //e - the event object 347 | //cell - cell component 348 | const valid = cell.validate(); 349 | if (valid !== true) { 350 | showLogMessage( 351 | "Cell Validation Errors: " + valid.map((el) => el.type).join(","), 352 | ); 353 | } 354 | }, 355 | hozAlign: "left", 356 | headerFilter: "list", 357 | editor: "list", 358 | editorParams: { values: ["Local", "Session"] }, 359 | headerFilterPlaceholder: "Select", 360 | headerFilterFunc: storeHeaderFilter, 361 | headerFilterParams: { 362 | values: ["Local", "Session"], 363 | verticalNavigation: "hybrid", 364 | multiselect: true, 365 | }, 366 | validator: ["in:Local|Session", "required"], 367 | }, 368 | { 369 | title: "Key", 370 | field: "key", 371 | validator: [ 372 | "required", 373 | { 374 | type: uniqueKey, 375 | parameters: { errmsg: "not unique" }, 376 | }, 377 | ], 378 | cellMouseOver: function (e, cell) { 379 | //e - the event object 380 | //cell - cell component 381 | const valid = cell.validate(); 382 | if (valid !== true) { 383 | //console.debug(JSON.stringify(valid,null,4)); 384 | showLogMessage( 385 | "Cell Validation Errors: " + 386 | valid 387 | .map((el) => { 388 | if (el.type === "function") { 389 | return el.parameters.errmsg; 390 | } 391 | return el.type; 392 | }) 393 | .join(","), 394 | ); 395 | } 396 | }, 397 | width: "25%", 398 | headerFilter: "input", 399 | headerFilterPlaceholder: "Filter", 400 | editor: "input", 401 | editorParams: { 402 | elementAttributes: { 403 | spellcheck: "false", 404 | }, 405 | }, 406 | }, 407 | { 408 | title: "Value", 409 | field: "value", 410 | headerFilter: "input", 411 | headerFilterPlaceholder: "Filter", 412 | editor: "textarea", 413 | editorParams: { 414 | elementAttributes: { 415 | spellcheck: "false", 416 | }, 417 | verticalNavigation: "editor", 418 | shiftEnterSubmit: true, 419 | }, 420 | formatter: "plaintext", 421 | }, 422 | ], 423 | }); 424 | 425 | /** 426 | * Register Table Events 427 | */ 428 | const checkState = await getFromStorage("boolean", "checkState", false); 429 | 430 | // after adding a row highlight/select it 431 | table.on("rowAdded", function (row) { 432 | row.select(); 433 | }); 434 | 435 | // load data 436 | const data = await getTblData(); 437 | data.forEach(async (e, index) => { 438 | table.addRow(e, true); 439 | 440 | // after processing the last element 441 | if (index === data.length - 1) { 442 | if (!checkState) { 443 | // when unchecked, deselect everything after inital load 444 | table.deselectRow(); 445 | } 446 | } 447 | }); 448 | 449 | tableData = table.getData(); 450 | 451 | // do this after the inital data load 452 | // hlchange any values changed 453 | // and call validate afterwards 454 | table.on("dataChanged", function (data) { 455 | //data - the updated table data 456 | delayedValidateAndHighlight(data); 457 | /* 458 | setTimeout(() => { table.validate() }, 1000); 459 | if(dataDifferentFromInital(data)){ 460 | highlightChange(); 461 | }else{ 462 | unhighlightChange(); 463 | } 464 | */ 465 | }); 466 | } 467 | 468 | function onChange(evt) { 469 | let id = evt.target.id; 470 | let el = document.getElementById(id); 471 | 472 | let value = el.type === "checkbox" ? el.checked : el.value; 473 | let obj = {}; 474 | 475 | //console.log(id,value, el.type,el.min); 476 | if (value === "") { 477 | return; 478 | } 479 | if (el.type === "number") { 480 | try { 481 | value = parseInt(value); 482 | if (isNaN(value)) { 483 | value = el.min; 484 | } 485 | if (value < el.min) { 486 | value = el.min; 487 | } 488 | } catch (e) { 489 | value = el.min; 490 | } 491 | } 492 | 493 | obj[id] = value; 494 | 495 | //console.log(id, value); 496 | browser.storage.local.set(obj).catch(console.error); 497 | } 498 | 499 | ["checkState"].map((id) => { 500 | browser.storage.local 501 | .get(id) 502 | .then((obj) => { 503 | let el = document.getElementById(id); 504 | let val = obj[id]; 505 | 506 | if (typeof val !== "undefined") { 507 | if (el.type === "checkbox") { 508 | el.checked = val; 509 | } else { 510 | el.value = val; 511 | } 512 | } else { 513 | el.value = 0; 514 | } 515 | }) 516 | .catch(console.error); 517 | 518 | let el = document.getElementById(id); 519 | el.addEventListener("input", onChange); 520 | }); 521 | 522 | // init 523 | document.addEventListener("DOMContentLoaded", onDOMContentLoaded); 524 | -------------------------------------------------------------------------------- /tabulator.lic: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2022 Oli Folkerd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tabulator.min.css: -------------------------------------------------------------------------------- 1 | .tabulator { 2 | position: relative; 3 | border: 1px solid #999; 4 | background-color: #888; 5 | font-size: 14px; 6 | text-align: left; 7 | overflow: hidden; 8 | -webkit-transform: translateZ(0); 9 | -moz-transform: translateZ(0); 10 | -ms-transform: translateZ(0); 11 | -o-transform: translateZ(0); 12 | transform: translateZ(0); 13 | } 14 | .tabulator[tabulator-layout="fitDataFill"] 15 | .tabulator-tableholder 16 | .tabulator-table { 17 | min-width: 100%; 18 | } 19 | .tabulator[tabulator-layout="fitDataTable"] { 20 | display: inline-block; 21 | } 22 | .tabulator.tabulator-block-select { 23 | user-select: none; 24 | } 25 | .tabulator .tabulator-header { 26 | position: relative; 27 | box-sizing: border-box; 28 | width: 100%; 29 | border-bottom: 1px solid #999; 30 | background-color: #e6e6e6; 31 | color: #555; 32 | font-weight: 700; 33 | white-space: nowrap; 34 | overflow: hidden; 35 | -moz-user-select: none; 36 | -khtml-user-select: none; 37 | -webkit-user-select: none; 38 | -o-user-select: none; 39 | } 40 | .tabulator .tabulator-header.tabulator-header-hidden { 41 | display: none; 42 | } 43 | .tabulator .tabulator-header .tabulator-header-contents { 44 | position: relative; 45 | overflow: hidden; 46 | } 47 | .tabulator .tabulator-header .tabulator-header-contents .tabulator-headers { 48 | display: inline-block; 49 | } 50 | .tabulator .tabulator-header .tabulator-col { 51 | display: inline-flex; 52 | position: relative; 53 | box-sizing: border-box; 54 | flex-direction: column; 55 | justify-content: flex-start; 56 | border-right: 1px solid #aaa; 57 | background: #e6e6e6; 58 | text-align: left; 59 | vertical-align: bottom; 60 | overflow: hidden; 61 | } 62 | .tabulator .tabulator-header .tabulator-col.tabulator-moving { 63 | position: absolute; 64 | border: 1px solid #999; 65 | background: #cdcdcd; 66 | pointer-events: none; 67 | } 68 | .tabulator .tabulator-header .tabulator-col .tabulator-col-content { 69 | box-sizing: border-box; 70 | position: relative; 71 | padding: 4px; 72 | } 73 | .tabulator 74 | .tabulator-header 75 | .tabulator-col 76 | .tabulator-col-content 77 | .tabulator-header-popup-button { 78 | padding: 0 8px; 79 | } 80 | .tabulator 81 | .tabulator-header 82 | .tabulator-col 83 | .tabulator-col-content 84 | .tabulator-header-popup-button:hover { 85 | cursor: pointer; 86 | opacity: 0.6; 87 | } 88 | .tabulator 89 | .tabulator-header 90 | .tabulator-col 91 | .tabulator-col-content 92 | .tabulator-col-title-holder { 93 | position: relative; 94 | } 95 | .tabulator 96 | .tabulator-header 97 | .tabulator-col 98 | .tabulator-col-content 99 | .tabulator-col-title { 100 | box-sizing: border-box; 101 | width: 100%; 102 | white-space: nowrap; 103 | overflow: hidden; 104 | text-overflow: ellipsis; 105 | vertical-align: bottom; 106 | } 107 | .tabulator 108 | .tabulator-header 109 | .tabulator-col 110 | .tabulator-col-content 111 | .tabulator-col-title.tabulator-col-title-wrap { 112 | white-space: normal; 113 | text-overflow: clip; 114 | } 115 | .tabulator 116 | .tabulator-header 117 | .tabulator-col 118 | .tabulator-col-content 119 | .tabulator-col-title 120 | .tabulator-title-editor { 121 | box-sizing: border-box; 122 | width: 100%; 123 | border: 1px solid #999; 124 | padding: 1px; 125 | background: #fff; 126 | } 127 | .tabulator 128 | .tabulator-header 129 | .tabulator-col 130 | .tabulator-col-content 131 | .tabulator-col-title 132 | .tabulator-header-popup-button 133 | + .tabulator-title-editor { 134 | width: calc(100% - 22px); 135 | } 136 | .tabulator 137 | .tabulator-header 138 | .tabulator-col 139 | .tabulator-col-content 140 | .tabulator-col-sorter { 141 | display: flex; 142 | align-items: center; 143 | position: absolute; 144 | top: 0; 145 | bottom: 0; 146 | right: 4px; 147 | } 148 | .tabulator 149 | .tabulator-header 150 | .tabulator-col 151 | .tabulator-col-content 152 | .tabulator-col-sorter 153 | .tabulator-arrow { 154 | width: 0; 155 | height: 0; 156 | border-left: 6px solid transparent; 157 | border-right: 6px solid transparent; 158 | border-bottom: 6px solid #bbb; 159 | } 160 | .tabulator 161 | .tabulator-header 162 | .tabulator-col.tabulator-col-group 163 | .tabulator-col-group-cols { 164 | position: relative; 165 | display: flex; 166 | border-top: 1px solid #aaa; 167 | overflow: hidden; 168 | margin-right: -1px; 169 | } 170 | .tabulator .tabulator-header .tabulator-col .tabulator-header-filter { 171 | position: relative; 172 | box-sizing: border-box; 173 | margin-top: 2px; 174 | width: 100%; 175 | text-align: center; 176 | } 177 | .tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea { 178 | height: auto !important; 179 | } 180 | .tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg { 181 | margin-top: 3px; 182 | } 183 | .tabulator 184 | .tabulator-header 185 | .tabulator-col 186 | .tabulator-header-filter 187 | input::-ms-clear { 188 | width: 0; 189 | height: 0; 190 | } 191 | .tabulator 192 | .tabulator-header 193 | .tabulator-col.tabulator-sortable 194 | .tabulator-col-title { 195 | padding-right: 25px; 196 | } 197 | .tabulator 198 | .tabulator-header 199 | .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover { 200 | cursor: pointer; 201 | background-color: #cdcdcd; 202 | } 203 | .tabulator 204 | .tabulator-header 205 | .tabulator-col.tabulator-sortable[aria-sort="none"] 206 | .tabulator-col-content 207 | .tabulator-col-sorter { 208 | color: #bbb; 209 | } 210 | .tabulator 211 | .tabulator-header 212 | .tabulator-col.tabulator-sortable[aria-sort="none"] 213 | .tabulator-col-content 214 | .tabulator-col-sorter.tabulator-col-sorter-element 215 | .tabulator-arrow:hover { 216 | cursor: pointer; 217 | border-bottom: 6px solid #555; 218 | } 219 | .tabulator 220 | .tabulator-header 221 | .tabulator-col.tabulator-sortable[aria-sort="none"] 222 | .tabulator-col-content 223 | .tabulator-col-sorter 224 | .tabulator-arrow { 225 | border-top: none; 226 | border-bottom: 6px solid #bbb; 227 | } 228 | .tabulator 229 | .tabulator-header 230 | .tabulator-col.tabulator-sortable[aria-sort="ascending"] 231 | .tabulator-col-content 232 | .tabulator-col-sorter { 233 | color: #666; 234 | } 235 | .tabulator 236 | .tabulator-header 237 | .tabulator-col.tabulator-sortable[aria-sort="ascending"] 238 | .tabulator-col-content 239 | .tabulator-col-sorter.tabulator-col-sorter-element 240 | .tabulator-arrow:hover { 241 | cursor: pointer; 242 | border-bottom: 6px solid #555; 243 | } 244 | .tabulator 245 | .tabulator-header 246 | .tabulator-col.tabulator-sortable[aria-sort="ascending"] 247 | .tabulator-col-content 248 | .tabulator-col-sorter 249 | .tabulator-arrow { 250 | border-top: none; 251 | border-bottom: 6px solid #666; 252 | } 253 | .tabulator 254 | .tabulator-header 255 | .tabulator-col.tabulator-sortable[aria-sort="descending"] 256 | .tabulator-col-content 257 | .tabulator-col-sorter { 258 | color: #666; 259 | } 260 | .tabulator 261 | .tabulator-header 262 | .tabulator-col.tabulator-sortable[aria-sort="descending"] 263 | .tabulator-col-content 264 | .tabulator-col-sorter.tabulator-col-sorter-element 265 | .tabulator-arrow:hover { 266 | cursor: pointer; 267 | border-top: 6px solid #555; 268 | } 269 | .tabulator 270 | .tabulator-header 271 | .tabulator-col.tabulator-sortable[aria-sort="descending"] 272 | .tabulator-col-content 273 | .tabulator-col-sorter 274 | .tabulator-arrow { 275 | border-bottom: none; 276 | border-top: 6px solid #666; 277 | color: #666; 278 | } 279 | .tabulator 280 | .tabulator-header 281 | .tabulator-col.tabulator-col-vertical 282 | .tabulator-col-content 283 | .tabulator-col-title { 284 | writing-mode: vertical-rl; 285 | text-orientation: mixed; 286 | display: flex; 287 | align-items: center; 288 | justify-content: center; 289 | } 290 | .tabulator 291 | .tabulator-header 292 | .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip 293 | .tabulator-col-title { 294 | transform: rotate(180deg); 295 | } 296 | .tabulator 297 | .tabulator-header 298 | .tabulator-col.tabulator-col-vertical.tabulator-sortable 299 | .tabulator-col-title { 300 | padding-right: 0; 301 | padding-top: 20px; 302 | } 303 | .tabulator 304 | .tabulator-header 305 | .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip 306 | .tabulator-col-title { 307 | padding-right: 0; 308 | padding-bottom: 20px; 309 | } 310 | .tabulator 311 | .tabulator-header 312 | .tabulator-col.tabulator-col-vertical.tabulator-sortable 313 | .tabulator-col-sorter { 314 | justify-content: center; 315 | left: 0; 316 | right: 0; 317 | top: 4px; 318 | bottom: auto; 319 | } 320 | .tabulator .tabulator-header .tabulator-frozen { 321 | position: sticky; 322 | left: 0; 323 | z-index: 10; 324 | } 325 | .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left { 326 | border-right: 2px solid #aaa; 327 | } 328 | .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right { 329 | border-left: 2px solid #aaa; 330 | } 331 | .tabulator .tabulator-header .tabulator-calcs-holder { 332 | box-sizing: border-box; 333 | min-width: 600%; 334 | background: #f3f3f3 !important; 335 | border-top: 1px solid #aaa; 336 | border-bottom: 1px solid #aaa; 337 | } 338 | .tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row { 339 | background: #f3f3f3 !important; 340 | } 341 | .tabulator 342 | .tabulator-header 343 | .tabulator-calcs-holder 344 | .tabulator-row 345 | .tabulator-col-resize-handle { 346 | display: none; 347 | } 348 | .tabulator .tabulator-header .tabulator-frozen-rows-holder { 349 | min-width: 600%; 350 | } 351 | .tabulator .tabulator-header .tabulator-frozen-rows-holder:empty { 352 | display: none; 353 | } 354 | .tabulator .tabulator-tableholder { 355 | position: relative; 356 | width: 100%; 357 | white-space: nowrap; 358 | overflow: auto; 359 | -webkit-overflow-scrolling: touch; 360 | } 361 | .tabulator .tabulator-tableholder:focus { 362 | outline: none; 363 | } 364 | .tabulator .tabulator-tableholder .tabulator-placeholder { 365 | box-sizing: border-box; 366 | display: flex; 367 | align-items: center; 368 | width: 100%; 369 | } 370 | .tabulator 371 | .tabulator-tableholder 372 | .tabulator-placeholder[tabulator-render-mode="virtual"] { 373 | min-height: 100%; 374 | min-width: 100%; 375 | } 376 | .tabulator 377 | .tabulator-tableholder 378 | .tabulator-placeholder 379 | .tabulator-placeholder-contents { 380 | display: inline-block; 381 | text-align: center; 382 | padding: 10px; 383 | color: #ccc; 384 | font-weight: 700; 385 | font-size: 20px; 386 | white-space: normal; 387 | } 388 | .tabulator .tabulator-tableholder .tabulator-table { 389 | position: relative; 390 | display: inline-block; 391 | background-color: #fff; 392 | white-space: nowrap; 393 | overflow: visible; 394 | color: #333; 395 | } 396 | .tabulator 397 | .tabulator-tableholder 398 | .tabulator-table 399 | .tabulator-row.tabulator-calcs { 400 | font-weight: 700; 401 | background: #e2e2e2 !important; 402 | } 403 | .tabulator 404 | .tabulator-tableholder 405 | .tabulator-table 406 | .tabulator-row.tabulator-calcs.tabulator-calcs-top { 407 | border-bottom: 2px solid #aaa; 408 | } 409 | .tabulator 410 | .tabulator-tableholder 411 | .tabulator-table 412 | .tabulator-row.tabulator-calcs.tabulator-calcs-bottom { 413 | border-top: 2px solid #aaa; 414 | } 415 | .tabulator .tabulator-footer { 416 | border-top: 1px solid #999; 417 | background-color: #e6e6e6; 418 | color: #555; 419 | font-weight: 700; 420 | white-space: nowrap; 421 | user-select: none; 422 | -moz-user-select: none; 423 | -khtml-user-select: none; 424 | -webkit-user-select: none; 425 | -o-user-select: none; 426 | } 427 | .tabulator .tabulator-footer .tabulator-footer-contents { 428 | display: flex; 429 | flex-direction: row; 430 | align-items: center; 431 | justify-content: space-between; 432 | padding: 5px 10px; 433 | } 434 | .tabulator .tabulator-footer .tabulator-footer-contents:empty { 435 | display: none; 436 | } 437 | .tabulator .tabulator-footer .tabulator-calcs-holder { 438 | box-sizing: border-box; 439 | width: 100%; 440 | text-align: left; 441 | background: #f3f3f3 !important; 442 | border-bottom: 1px solid #aaa; 443 | border-top: 1px solid #aaa; 444 | overflow: hidden; 445 | } 446 | .tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row { 447 | display: inline-block; 448 | background: #f3f3f3 !important; 449 | } 450 | .tabulator 451 | .tabulator-footer 452 | .tabulator-calcs-holder 453 | .tabulator-row 454 | .tabulator-col-resize-handle { 455 | display: none; 456 | } 457 | .tabulator .tabulator-footer .tabulator-calcs-holder:only-child { 458 | margin-bottom: -5px; 459 | border-bottom: none; 460 | } 461 | .tabulator .tabulator-footer > * + .tabulator-page-counter { 462 | margin-left: 10px; 463 | } 464 | .tabulator .tabulator-footer .tabulator-page-counter { 465 | font-weight: 400; 466 | } 467 | .tabulator .tabulator-footer .tabulator-paginator { 468 | flex: 1; 469 | text-align: right; 470 | color: #555; 471 | font-family: inherit; 472 | font-weight: inherit; 473 | font-size: inherit; 474 | } 475 | .tabulator .tabulator-footer .tabulator-page-size { 476 | display: inline-block; 477 | margin: 0 5px; 478 | padding: 2px 5px; 479 | border: 1px solid #aaa; 480 | border-radius: 3px; 481 | } 482 | .tabulator .tabulator-footer .tabulator-pages { 483 | margin: 0 7px; 484 | } 485 | .tabulator .tabulator-footer .tabulator-page { 486 | display: inline-block; 487 | margin: 0 2px; 488 | padding: 2px 5px; 489 | border: 1px solid #aaa; 490 | border-radius: 3px; 491 | background: hsla(0, 0%, 100%, 0.2); 492 | } 493 | .tabulator .tabulator-footer .tabulator-page.active { 494 | color: #d00; 495 | } 496 | .tabulator .tabulator-footer .tabulator-page:disabled { 497 | opacity: 0.5; 498 | } 499 | .tabulator .tabulator-footer .tabulator-page:not(.disabled):hover { 500 | cursor: pointer; 501 | background: rgba(0, 0, 0, 0.2); 502 | color: #fff; 503 | } 504 | .tabulator .tabulator-col-resize-handle { 505 | position: relative; 506 | display: inline-block; 507 | width: 6px; 508 | margin-left: -3px; 509 | margin-right: -3px; 510 | z-index: 10; 511 | vertical-align: middle; 512 | } 513 | .tabulator .tabulator-col-resize-handle:hover { 514 | cursor: ew-resize; 515 | } 516 | .tabulator .tabulator-col-resize-handle:last-of-type { 517 | width: 3px; 518 | margin-right: 0; 519 | } 520 | .tabulator .tabulator-alert { 521 | position: absolute; 522 | display: flex; 523 | align-items: center; 524 | top: 0; 525 | left: 0; 526 | z-index: 100; 527 | height: 100%; 528 | width: 100%; 529 | background: rgba(0, 0, 0, 0.4); 530 | text-align: center; 531 | } 532 | .tabulator .tabulator-alert .tabulator-alert-msg { 533 | display: inline-block; 534 | margin: 0 auto; 535 | padding: 10px 20px; 536 | border-radius: 10px; 537 | background: #fff; 538 | font-weight: 700; 539 | font-size: 16px; 540 | } 541 | .tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg { 542 | border: 4px solid #333; 543 | color: #000; 544 | } 545 | .tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error { 546 | border: 4px solid #d00; 547 | color: #590000; 548 | } 549 | .tabulator-row { 550 | position: relative; 551 | box-sizing: border-box; 552 | min-height: 22px; 553 | background-color: #fff; 554 | } 555 | .tabulator-row.tabulator-row-even { 556 | background-color: #efefef; 557 | } 558 | .tabulator-row.tabulator-selectable:hover { 559 | background-color: #bbb; 560 | cursor: pointer; 561 | } 562 | .tabulator-row.tabulator-selected { 563 | background-color: #9abcea; 564 | } 565 | .tabulator-row.tabulator-selected:hover { 566 | background-color: #769bcc; 567 | cursor: pointer; 568 | } 569 | .tabulator-row.tabulator-row-moving { 570 | border: 1px solid #000; 571 | background: #fff; 572 | } 573 | .tabulator-row.tabulator-moving { 574 | position: absolute; 575 | border-top: 1px solid #aaa; 576 | border-bottom: 1px solid #aaa; 577 | pointer-events: none; 578 | z-index: 15; 579 | } 580 | .tabulator-row .tabulator-row-resize-handle { 581 | position: absolute; 582 | right: 0; 583 | bottom: 0; 584 | left: 0; 585 | height: 5px; 586 | } 587 | .tabulator-row .tabulator-row-resize-handle.prev { 588 | top: 0; 589 | bottom: auto; 590 | } 591 | .tabulator-row .tabulator-row-resize-handle:hover { 592 | cursor: ns-resize; 593 | } 594 | .tabulator-row .tabulator-responsive-collapse { 595 | box-sizing: border-box; 596 | padding: 5px; 597 | border-top: 1px solid #aaa; 598 | border-bottom: 1px solid #aaa; 599 | } 600 | .tabulator-row .tabulator-responsive-collapse:empty { 601 | display: none; 602 | } 603 | .tabulator-row .tabulator-responsive-collapse table { 604 | font-size: 14px; 605 | } 606 | .tabulator-row .tabulator-responsive-collapse table tr td { 607 | position: relative; 608 | } 609 | .tabulator-row .tabulator-responsive-collapse table tr td:first-of-type { 610 | padding-right: 10px; 611 | } 612 | .tabulator-row .tabulator-cell { 613 | display: inline-block; 614 | position: relative; 615 | box-sizing: border-box; 616 | padding: 4px; 617 | border-right: 1px solid #aaa; 618 | vertical-align: middle; 619 | white-space: nowrap; 620 | overflow: hidden; 621 | text-overflow: ellipsis; 622 | } 623 | .tabulator-row .tabulator-cell.tabulator-frozen { 624 | display: inline-block; 625 | position: sticky; 626 | left: 0; 627 | background-color: inherit; 628 | z-index: 10; 629 | } 630 | .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left { 631 | border-right: 2px solid #aaa; 632 | } 633 | .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right { 634 | border-left: 2px solid #aaa; 635 | } 636 | .tabulator-row .tabulator-cell.tabulator-editing { 637 | border: 1px solid #1d68cd; 638 | outline: none; 639 | padding: 0; 640 | } 641 | .tabulator-row .tabulator-cell.tabulator-editing input, 642 | .tabulator-row .tabulator-cell.tabulator-editing select { 643 | border: 1px; 644 | background: transparent; 645 | outline: none; 646 | } 647 | .tabulator-row .tabulator-cell.tabulator-validation-fail { 648 | border: 1px solid #d00; 649 | } 650 | .tabulator-row .tabulator-cell.tabulator-validation-fail input, 651 | .tabulator-row .tabulator-cell.tabulator-validation-fail select { 652 | border: 1px; 653 | background: transparent; 654 | color: #d00; 655 | } 656 | .tabulator-row .tabulator-cell.tabulator-row-handle { 657 | display: inline-flex; 658 | align-items: center; 659 | justify-content: center; 660 | -moz-user-select: none; 661 | -khtml-user-select: none; 662 | -webkit-user-select: none; 663 | -o-user-select: none; 664 | } 665 | .tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box { 666 | width: 80%; 667 | } 668 | .tabulator-row 669 | .tabulator-cell.tabulator-row-handle 670 | .tabulator-row-handle-box 671 | .tabulator-row-handle-bar { 672 | width: 100%; 673 | height: 3px; 674 | margin-top: 2px; 675 | background: #666; 676 | } 677 | .tabulator-row .tabulator-cell .tabulator-data-tree-branch { 678 | display: inline-block; 679 | vertical-align: middle; 680 | height: 9px; 681 | width: 7px; 682 | margin-top: -9px; 683 | margin-right: 5px; 684 | border-bottom-left-radius: 1px; 685 | border-left: 2px solid #aaa; 686 | border-bottom: 2px solid #aaa; 687 | } 688 | .tabulator-row .tabulator-cell .tabulator-data-tree-control { 689 | display: inline-flex; 690 | justify-content: center; 691 | align-items: center; 692 | vertical-align: middle; 693 | height: 11px; 694 | width: 11px; 695 | margin-right: 5px; 696 | border: 1px solid #333; 697 | border-radius: 2px; 698 | background: rgba(0, 0, 0, 0.1); 699 | overflow: hidden; 700 | } 701 | .tabulator-row .tabulator-cell .tabulator-data-tree-control:hover { 702 | cursor: pointer; 703 | background: rgba(0, 0, 0, 0.2); 704 | } 705 | .tabulator-row 706 | .tabulator-cell 707 | .tabulator-data-tree-control 708 | .tabulator-data-tree-control-collapse { 709 | display: inline-block; 710 | position: relative; 711 | height: 7px; 712 | width: 1px; 713 | background: transparent; 714 | } 715 | .tabulator-row 716 | .tabulator-cell 717 | .tabulator-data-tree-control 718 | .tabulator-data-tree-control-collapse:after { 719 | position: absolute; 720 | content: ""; 721 | left: -3px; 722 | top: 3px; 723 | height: 1px; 724 | width: 7px; 725 | background: #333; 726 | } 727 | .tabulator-row 728 | .tabulator-cell 729 | .tabulator-data-tree-control 730 | .tabulator-data-tree-control-expand { 731 | display: inline-block; 732 | position: relative; 733 | height: 7px; 734 | width: 1px; 735 | background: #333; 736 | } 737 | .tabulator-row 738 | .tabulator-cell 739 | .tabulator-data-tree-control 740 | .tabulator-data-tree-control-expand:after { 741 | position: absolute; 742 | content: ""; 743 | left: -3px; 744 | top: 3px; 745 | height: 1px; 746 | width: 7px; 747 | background: #333; 748 | } 749 | .tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle { 750 | display: inline-flex; 751 | align-items: center; 752 | justify-content: center; 753 | -moz-user-select: none; 754 | -khtml-user-select: none; 755 | -webkit-user-select: none; 756 | -o-user-select: none; 757 | height: 15px; 758 | width: 15px; 759 | border-radius: 20px; 760 | background: #666; 761 | color: #fff; 762 | font-weight: 700; 763 | font-size: 1.1em; 764 | } 765 | .tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover { 766 | opacity: 0.7; 767 | cursor: pointer; 768 | } 769 | .tabulator-row 770 | .tabulator-cell 771 | .tabulator-responsive-collapse-toggle.open 772 | .tabulator-responsive-collapse-toggle-close { 773 | display: initial; 774 | } 775 | .tabulator-row 776 | .tabulator-cell 777 | .tabulator-responsive-collapse-toggle.open 778 | .tabulator-responsive-collapse-toggle-open { 779 | display: none; 780 | } 781 | .tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg { 782 | stroke: #fff; 783 | } 784 | .tabulator-row 785 | .tabulator-cell 786 | .tabulator-responsive-collapse-toggle 787 | .tabulator-responsive-collapse-toggle-close { 788 | display: none; 789 | } 790 | .tabulator-row .tabulator-cell .tabulator-traffic-light { 791 | display: inline-block; 792 | height: 14px; 793 | width: 14px; 794 | border-radius: 14px; 795 | } 796 | .tabulator-row.tabulator-group { 797 | box-sizing: border-box; 798 | border-bottom: 1px solid #999; 799 | border-right: 1px solid #aaa; 800 | border-top: 1px solid #999; 801 | padding: 5px 5px 5px 10px; 802 | background: #ccc; 803 | font-weight: 700; 804 | min-width: 100%; 805 | } 806 | .tabulator-row.tabulator-group:hover { 807 | cursor: pointer; 808 | background-color: rgba(0, 0, 0, 0.1); 809 | } 810 | .tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow { 811 | margin-right: 10px; 812 | border-left: 6px solid transparent; 813 | border-right: 6px solid transparent; 814 | border-top: 6px solid #666; 815 | border-bottom: 0; 816 | } 817 | .tabulator-row.tabulator-group.tabulator-group-level-1 { 818 | padding-left: 30px; 819 | } 820 | .tabulator-row.tabulator-group.tabulator-group-level-2 { 821 | padding-left: 50px; 822 | } 823 | .tabulator-row.tabulator-group.tabulator-group-level-3 { 824 | padding-left: 70px; 825 | } 826 | .tabulator-row.tabulator-group.tabulator-group-level-4 { 827 | padding-left: 90px; 828 | } 829 | .tabulator-row.tabulator-group.tabulator-group-level-5 { 830 | padding-left: 110px; 831 | } 832 | .tabulator-row.tabulator-group .tabulator-group-toggle { 833 | display: inline-block; 834 | } 835 | .tabulator-row.tabulator-group .tabulator-arrow { 836 | display: inline-block; 837 | width: 0; 838 | height: 0; 839 | margin-right: 16px; 840 | border-top: 6px solid transparent; 841 | border-bottom: 6px solid transparent; 842 | border-right: 0; 843 | border-left: 6px solid #666; 844 | vertical-align: middle; 845 | } 846 | .tabulator-row.tabulator-group span { 847 | margin-left: 10px; 848 | color: #d00; 849 | } 850 | .tabulator-popup-container { 851 | position: absolute; 852 | display: inline-block; 853 | box-sizing: border-box; 854 | background: #fff; 855 | border: 1px solid #aaa; 856 | box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2); 857 | font-size: 14px; 858 | overflow-y: auto; 859 | -webkit-overflow-scrolling: touch; 860 | z-index: 10000; 861 | } 862 | .tabulator-popup { 863 | padding: 5px; 864 | border-radius: 3px; 865 | } 866 | .tabulator-tooltip { 867 | max-width: Min(500px, 100%); 868 | padding: 3px 5px; 869 | border-radius: 2px; 870 | box-shadow: none; 871 | font-size: 12px; 872 | pointer-events: none; 873 | } 874 | .tabulator-menu .tabulator-menu-item { 875 | position: relative; 876 | box-sizing: border-box; 877 | padding: 5px 10px; 878 | user-select: none; 879 | } 880 | .tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled { 881 | opacity: 0.5; 882 | } 883 | .tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover { 884 | cursor: pointer; 885 | background: #efefef; 886 | } 887 | .tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu { 888 | padding-right: 25px; 889 | } 890 | .tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after { 891 | display: inline-block; 892 | position: absolute; 893 | top: calc(5px + 0.4em); 894 | right: 10px; 895 | height: 7px; 896 | width: 7px; 897 | content: ""; 898 | border-color: #aaa; 899 | border-style: solid; 900 | border-width: 1px 1px 0 0; 901 | vertical-align: top; 902 | transform: rotate(45deg); 903 | } 904 | .tabulator-menu .tabulator-menu-separator { 905 | border-top: 1px solid #aaa; 906 | } 907 | .tabulator-edit-list { 908 | max-height: 200px; 909 | font-size: 14px; 910 | overflow-y: auto; 911 | -webkit-overflow-scrolling: touch; 912 | } 913 | .tabulator-edit-list .tabulator-edit-list-item { 914 | padding: 4px; 915 | color: #333; 916 | outline: none; 917 | } 918 | .tabulator-edit-list .tabulator-edit-list-item.active { 919 | color: #fff; 920 | background: #1d68cd; 921 | } 922 | .tabulator-edit-list .tabulator-edit-list-item.active.focused { 923 | outline: 1px solid hsla(0, 0%, 100%, 0.5); 924 | } 925 | .tabulator-edit-list .tabulator-edit-list-item.focused { 926 | outline: 1px solid #1d68cd; 927 | } 928 | .tabulator-edit-list .tabulator-edit-list-item:hover { 929 | cursor: pointer; 930 | color: #fff; 931 | background: #1d68cd; 932 | } 933 | .tabulator-edit-list .tabulator-edit-list-placeholder { 934 | padding: 4px; 935 | color: #333; 936 | text-align: center; 937 | } 938 | .tabulator-edit-list .tabulator-edit-list-group { 939 | border-bottom: 1px solid #aaa; 940 | padding: 6px 4px 4px; 941 | color: #333; 942 | font-weight: 700; 943 | } 944 | .tabulator-edit-list 945 | .tabulator-edit-list-group.tabulator-edit-list-group-level-2, 946 | .tabulator-edit-list 947 | .tabulator-edit-list-item.tabulator-edit-list-group-level-2 { 948 | padding-left: 12px; 949 | } 950 | .tabulator-edit-list 951 | .tabulator-edit-list-group.tabulator-edit-list-group-level-3, 952 | .tabulator-edit-list 953 | .tabulator-edit-list-item.tabulator-edit-list-group-level-3 { 954 | padding-left: 20px; 955 | } 956 | .tabulator-edit-list 957 | .tabulator-edit-list-group.tabulator-edit-list-group-level-4, 958 | .tabulator-edit-list 959 | .tabulator-edit-list-item.tabulator-edit-list-group-level-4 { 960 | padding-left: 28px; 961 | } 962 | .tabulator-edit-list 963 | .tabulator-edit-list-group.tabulator-edit-list-group-level-5, 964 | .tabulator-edit-list 965 | .tabulator-edit-list-item.tabulator-edit-list-group-level-5 { 966 | padding-left: 36px; 967 | } 968 | .tabulator.tabulator-ltr { 969 | direction: ltr; 970 | } 971 | .tabulator.tabulator-rtl { 972 | text-align: initial; 973 | direction: rtl; 974 | } 975 | .tabulator.tabulator-rtl .tabulator-header .tabulator-col { 976 | text-align: initial; 977 | border-left: 1px solid #aaa; 978 | border-right: initial; 979 | } 980 | .tabulator.tabulator-rtl 981 | .tabulator-header 982 | .tabulator-col.tabulator-col-group 983 | .tabulator-col-group-cols { 984 | margin-right: 0; 985 | margin-left: -1px; 986 | } 987 | .tabulator.tabulator-rtl 988 | .tabulator-header 989 | .tabulator-col.tabulator-sortable 990 | .tabulator-col-title { 991 | padding-right: 0; 992 | padding-left: 25px; 993 | } 994 | .tabulator.tabulator-rtl 995 | .tabulator-header 996 | .tabulator-col 997 | .tabulator-col-content 998 | .tabulator-col-sorter { 999 | left: 8px; 1000 | right: auto; 1001 | } 1002 | .tabulator.tabulator-rtl .tabulator-row .tabulator-cell { 1003 | border-right: initial; 1004 | border-left: 1px solid #aaa; 1005 | } 1006 | .tabulator.tabulator-rtl 1007 | .tabulator-row 1008 | .tabulator-cell 1009 | .tabulator-data-tree-branch { 1010 | margin-right: 0; 1011 | margin-left: 5px; 1012 | border-bottom-left-radius: 0; 1013 | border-bottom-right-radius: 1px; 1014 | border-left: initial; 1015 | border-right: 2px solid #aaa; 1016 | } 1017 | .tabulator.tabulator-rtl 1018 | .tabulator-row 1019 | .tabulator-cell 1020 | .tabulator-data-tree-control { 1021 | margin-right: 0; 1022 | margin-left: 5px; 1023 | } 1024 | .tabulator.tabulator-rtl 1025 | .tabulator-row 1026 | .tabulator-cell.tabulator-frozen.tabulator-frozen-left { 1027 | border-left: 2px solid #aaa; 1028 | } 1029 | .tabulator.tabulator-rtl 1030 | .tabulator-row 1031 | .tabulator-cell.tabulator-frozen.tabulator-frozen-right { 1032 | border-right: 2px solid #aaa; 1033 | } 1034 | .tabulator.tabulator-rtl 1035 | .tabulator-row 1036 | .tabulator-col-resize-handle:last-of-type { 1037 | width: 3px; 1038 | margin-left: 0; 1039 | margin-right: -3px; 1040 | } 1041 | .tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder { 1042 | text-align: initial; 1043 | } 1044 | .tabulator-print-fullscreen { 1045 | position: absolute; 1046 | top: 0; 1047 | bottom: 0; 1048 | left: 0; 1049 | right: 0; 1050 | z-index: 10000; 1051 | } 1052 | body.tabulator-print-fullscreen-hide > :not(.tabulator-print-fullscreen) { 1053 | display: none !important; 1054 | } 1055 | .tabulator-print-table { 1056 | border-collapse: collapse; 1057 | } 1058 | .tabulator-print-table .tabulator-data-tree-branch { 1059 | display: inline-block; 1060 | vertical-align: middle; 1061 | height: 9px; 1062 | width: 7px; 1063 | margin-top: -9px; 1064 | margin-right: 5px; 1065 | border-bottom-left-radius: 1px; 1066 | border-left: 2px solid #aaa; 1067 | border-bottom: 2px solid #aaa; 1068 | } 1069 | .tabulator-print-table .tabulator-print-table-group { 1070 | box-sizing: border-box; 1071 | border-bottom: 1px solid #999; 1072 | border-right: 1px solid #aaa; 1073 | border-top: 1px solid #999; 1074 | padding: 5px 5px 5px 10px; 1075 | background: #ccc; 1076 | font-weight: 700; 1077 | min-width: 100%; 1078 | } 1079 | .tabulator-print-table .tabulator-print-table-group:hover { 1080 | cursor: pointer; 1081 | background-color: rgba(0, 0, 0, 0.1); 1082 | } 1083 | .tabulator-print-table 1084 | .tabulator-print-table-group.tabulator-group-visible 1085 | .tabulator-arrow { 1086 | margin-right: 10px; 1087 | border-left: 6px solid transparent; 1088 | border-right: 6px solid transparent; 1089 | border-top: 6px solid #666; 1090 | border-bottom: 0; 1091 | } 1092 | .tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td { 1093 | padding-left: 30px !important; 1094 | } 1095 | .tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td { 1096 | padding-left: 50px !important; 1097 | } 1098 | .tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td { 1099 | padding-left: 70px !important; 1100 | } 1101 | .tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td { 1102 | padding-left: 90px !important; 1103 | } 1104 | .tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td { 1105 | padding-left: 110px !important; 1106 | } 1107 | .tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle { 1108 | display: inline-block; 1109 | } 1110 | .tabulator-print-table .tabulator-print-table-group .tabulator-arrow { 1111 | display: inline-block; 1112 | width: 0; 1113 | height: 0; 1114 | margin-right: 16px; 1115 | border-top: 6px solid transparent; 1116 | border-bottom: 6px solid transparent; 1117 | border-right: 0; 1118 | border-left: 6px solid #666; 1119 | vertical-align: middle; 1120 | } 1121 | .tabulator-print-table .tabulator-print-table-group span { 1122 | margin-left: 10px; 1123 | color: #d00; 1124 | } 1125 | .tabulator-print-table .tabulator-data-tree-control { 1126 | display: inline-flex; 1127 | justify-content: center; 1128 | align-items: center; 1129 | vertical-align: middle; 1130 | height: 11px; 1131 | width: 11px; 1132 | margin-right: 5px; 1133 | border: 1px solid #333; 1134 | border-radius: 2px; 1135 | background: rgba(0, 0, 0, 0.1); 1136 | overflow: hidden; 1137 | } 1138 | .tabulator-print-table .tabulator-data-tree-control:hover { 1139 | cursor: pointer; 1140 | background: rgba(0, 0, 0, 0.2); 1141 | } 1142 | .tabulator-print-table 1143 | .tabulator-data-tree-control 1144 | .tabulator-data-tree-control-collapse { 1145 | display: inline-block; 1146 | position: relative; 1147 | height: 7px; 1148 | width: 1px; 1149 | background: transparent; 1150 | } 1151 | .tabulator-print-table 1152 | .tabulator-data-tree-control 1153 | .tabulator-data-tree-control-collapse:after { 1154 | position: absolute; 1155 | content: ""; 1156 | left: -3px; 1157 | top: 3px; 1158 | height: 1px; 1159 | width: 7px; 1160 | background: #333; 1161 | } 1162 | .tabulator-print-table 1163 | .tabulator-data-tree-control 1164 | .tabulator-data-tree-control-expand { 1165 | display: inline-block; 1166 | position: relative; 1167 | height: 7px; 1168 | width: 1px; 1169 | background: #333; 1170 | } 1171 | .tabulator-print-table 1172 | .tabulator-data-tree-control 1173 | .tabulator-data-tree-control-expand:after { 1174 | position: absolute; 1175 | content: ""; 1176 | left: -3px; 1177 | top: 3px; 1178 | height: 1px; 1179 | width: 7px; 1180 | background: #333; 1181 | } 1182 | /*# sourceMappingURL=tabulator.min.css.map */ 1183 | --------------------------------------------------------------------------------