diff --git a/jey_theme/public/css/shared.css b/jey_theme/public/css/shared.css index cf78ee1..c0c80a9 100644 --- a/jey_theme/public/css/shared.css +++ b/jey_theme/public/css/shared.css @@ -249,6 +249,51 @@ input.list-header-checkbox:checked::after { background: rgba(36, 144, 239, 0.05); } +/* ========================================================================== + List View Column Resize + Reorder + ========================================================================== */ + +.list-row-head .list-row-col { + position: relative; +} + +/* Column dividers — hint that headers can be resized / reordered. */ +.list-row-head .list-row-col:not(:last-child) { + border-right: 1px solid var(--border-color, #e2e6e9); +} + +.list-row-head .jey-col-resize { + position: absolute; + top: 0; + right: -2px; + width: 5px; + height: 100%; + cursor: col-resize; + z-index: 3; + background: transparent; + transition: background 0.15s; +} + +.list-row-head .jey-col-resize:hover, +.list-row-head .jey-col-resize:active { + background: var(--primary, #2490ef); + opacity: 0.5; +} + +.list-row-head .list-row-col[data-jey-draggable]:not(.list-subject) { + cursor: grab; +} + +.list-row-head .list-row-col.jey-drop-target.jey-drop-left { + box-shadow: inset 3px 0 0 var(--primary, #2490ef); + background: rgba(36, 144, 239, 0.05); +} + +.list-row-head .list-row-col.jey-drop-target.jey-drop-right { + box-shadow: inset -3px 0 0 var(--primary, #2490ef); + background: rgba(36, 144, 239, 0.05); +} + /* ========================================================================== Sidebar — Full Width button ========================================================================== */ diff --git a/jey_theme/public/js/jey_theme.js b/jey_theme/public/js/jey_theme.js index c3ad45b..fa478f0 100644 --- a/jey_theme/public/js/jey_theme.js +++ b/jey_theme/public/js/jey_theme.js @@ -23,6 +23,7 @@ resizeStyle.id = "jey-col-resize-styles"; var rules = []; var earlyGridKeys = []; + var earlyListDoctypes = []; for (var i = 0; i < localStorage.length; i++) { var k = localStorage.key(i); if (k.indexOf("jey-col-widths:") === 0) { @@ -37,13 +38,27 @@ ); }); } catch (e) {} + } else if (k.indexOf("jey-list-col-widths:") === 0) { + var listDoctype = k.substring(20); + earlyListDoctypes.push(listDoctype); + try { + var lwidths = JSON.parse(localStorage.getItem(k)); + Object.keys(lwidths).forEach(function (fn) { + var esc = (window.CSS && CSS.escape) ? CSS.escape(fn) : fn.replace(/[^a-zA-Z0-9_-]/g, "\\$&"); + rules.push( + '.list-view[data-jey-list="' + listDoctype + '"] .list-row-col.' + esc + + " { flex: 0 0 " + lwidths[fn] + "px !important; width: " + lwidths[fn] + + "px !important; max-width: none !important; min-width: 0 !important; }" + ); + }); + } catch (e) {} } } resizeStyle.textContent = rules.join("\n"); document.head.appendChild(resizeStyle); // Set data-jey-grid on grids as soon as they appear so CSS rules apply immediately - if (earlyGridKeys.length) { + if (earlyGridKeys.length || earlyListDoctypes.length) { var earlyObs = new MutationObserver(function () { earlyGridKeys.forEach(function (key) { // key = "Sales Invoice:items" → fieldname = "items" @@ -58,6 +73,18 @@ } } }); + if (earlyListDoctypes.length) { + // Stamp data-jey-list on any .list-view whose route matches a saved doctype + var route = (window.frappe && frappe.get_route) ? frappe.get_route() : null; + var routeDoctype = (route && route[0] === "List") ? route[1] : null; + if (routeDoctype && earlyListDoctypes.indexOf(routeDoctype) !== -1) { + document.querySelectorAll(".list-view").forEach(function (lv) { + if (lv.dataset.jeyList !== routeDoctype) { + lv.dataset.jeyList = routeDoctype; + } + }); + } + } }); earlyObs.observe(document.body, { childList: true, subtree: true }); } @@ -1344,6 +1371,517 @@ dragObserver.observe(document.body, { childList: true, subtree: true }); })(); +/* ========================================================================== + List View Column Resize + Reorder + Drag the right edge of a list header to resize; drag the header body to + reorder. Widths + order persist in localStorage per doctype: + jey-list-col-widths: { fieldname: pxWidth, ... } + jey-list-col-order: [fieldname, fieldname, ...] + ========================================================================== */ + +(function () { + "use strict"; + + var MIN_COL_WIDTH = 60; + var WIDTH_PREFIX = "jey-list-col-widths:"; + var ORDER_PREFIX = "jey-list-col-order:"; + var DRAG_THRESHOLD = 5; + + // Classnames that appear on .list-row-col but are NOT fieldnames. + var NON_FIELD_CLASSES = { + "list-row-col": 1, "ellipsis": 1, "hidden-xs": 1, "text-right": 1, + "list-subject": 1, "level": 1, "level-item": 1, + "tag-col": 1, "hide": 1, "bold": 1, "seen": 1, "notseen": 1, + "inner-group-button": 1 + }; + + var styleEl = document.getElementById("jey-col-resize-styles"); + // { "Sales Invoice": { customer: 220, ... }, ... } + var allListWidths = {}; + // Tracks doctypes already initialised from localStorage during this session + var loaded = {}; + + function escapeClass(fn) { + return (window.CSS && CSS.escape) ? CSS.escape(fn) : fn.replace(/[^a-zA-Z0-9_-]/g, "\\$&"); + } + + // Rebuild only the list-scoped portion of the shared stylesheet. + // Grid rules live inside their own