feat(list,grid): period filter for date columns + dialog row fix
- list-view: replace single-date "=" standard filter with a DateRange "Between" control when the doctype carries posting_date as Date/Datetime. If posting_date isn't in standard_filter, inject the period control after the ID filter so it's always reachable. - shared.css: size the period filter to fit "DD/MM/YYYY to DD/MM/YYYY" without being squashed by the surrounding flex layout. - shared.css: let rows in the grid "Configure Columns" dialog grow with content. Frappe hardcodes height:32px inline on each .fields_order row in grid_row.js, which clips long translated labels (eg. Azerbaijani "Əsas Tarif (Anbar Ölçü Vahidinə görə)") so the wrapped portion overflows into the next row and the help text. Switch to flex centering with min-height; reset inline padding-tops that were calibrated for fixed 32px rows. - shared.css: hide the ERPNext desktop icon by default (same treatment as Organization). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c145f584bd
commit
a1e3fe96ed
|
|
@ -13,7 +13,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide Organization desktop icon by default */
|
/* Hide Organization desktop icon by default */
|
||||||
.desktop-icon[data-id="Organization"] {
|
.desktop-icon[data-id="Organization"],
|
||||||
|
.desktop-icon[data-id="ERPNext"] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1375,3 +1376,34 @@ textarea.form-control:focus,
|
||||||
border-color: transparent !important;
|
border-color: transparent !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* List view — period (Between) filter sized to fit "DD/MM/YYYY to DD/MM/YYYY". */
|
||||||
|
.standard-filter-section .frappe-control.jey-period-filter {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
.standard-filter-section .frappe-control.jey-period-filter input {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid "Configure Columns" dialog — let rows grow when label wraps.
|
||||||
|
Frappe hardcodes height:32px inline on each .fields_order row in
|
||||||
|
grid_row.js, which clips long translated labels (eg. Azerbaijani
|
||||||
|
"Əsas Tarif (Anbar Ölçü Vahidinə görə)") so the wrapped portion
|
||||||
|
overflows into the next row and into the help text below. Switch
|
||||||
|
to flex centering with min-height so rows grow with content. */
|
||||||
|
.control-input.fields_order {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.control-input.fields_order > .row {
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.control-input.fields_order > .row > [class*="col-"] {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1921,6 +1921,215 @@
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
List View — Period filter (Between) for date fields like posting_date
|
||||||
|
Replaces the single-date "=" standard filter with a DateRange "Between"
|
||||||
|
control, or injects one after the ID filter if it's not in standard_filter.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* Trigger fields: if doctype has any of these as Date/Datetime, we upgrade.
|
||||||
|
Order matters — first match wins. Extend freely. */
|
||||||
|
var TRIGGER_FIELDS = ["posting_date"];
|
||||||
|
|
||||||
|
function findTriggerDf(listview) {
|
||||||
|
if (!listview || !listview.meta || !Array.isArray(listview.meta.fields)) return null;
|
||||||
|
var fields = listview.meta.fields;
|
||||||
|
for (var i = 0; i < TRIGGER_FIELDS.length; i++) {
|
||||||
|
var name = TRIGGER_FIELDS[i];
|
||||||
|
for (var j = 0; j < fields.length; j++) {
|
||||||
|
var f = fields[j];
|
||||||
|
if (f.fieldname === name && (f.fieldtype === "Date" || f.fieldtype === "Datetime")) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchFilterAreaProto(filter_area) {
|
||||||
|
var proto = Object.getPrototypeOf(filter_area);
|
||||||
|
if (!proto || proto.__jeyBetweenPatched) return;
|
||||||
|
proto.__jeyBetweenPatched = true;
|
||||||
|
|
||||||
|
var origSetStandard = proto.set_standard_filter;
|
||||||
|
proto.set_standard_filter = function (filters) {
|
||||||
|
if (!filters || filters.length === 0) return origSetStandard.call(this, filters);
|
||||||
|
|
||||||
|
var fields_dict = this.list_view.page.fields_dict || {};
|
||||||
|
var handled = [];
|
||||||
|
var passthrough = [];
|
||||||
|
|
||||||
|
filters.forEach(function (filter) {
|
||||||
|
var fieldname = filter[1];
|
||||||
|
var condition = filter[2];
|
||||||
|
var value = filter[3];
|
||||||
|
var ctrl = fields_dict[fieldname];
|
||||||
|
if (
|
||||||
|
condition === "Between" &&
|
||||||
|
ctrl &&
|
||||||
|
ctrl.df &&
|
||||||
|
ctrl.df.fieldtype === "DateRange"
|
||||||
|
) {
|
||||||
|
handled.push({ ctrl: ctrl, value: value });
|
||||||
|
} else {
|
||||||
|
passthrough.push(filter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = origSetStandard.call(this, passthrough);
|
||||||
|
|
||||||
|
if (handled.length) {
|
||||||
|
result.promise = (result.promise || Promise.resolve()).then(function () {
|
||||||
|
handled.forEach(function (h) {
|
||||||
|
h.ctrl.df.match_type = "Between";
|
||||||
|
h.ctrl.df.condition = "Between";
|
||||||
|
return h.ctrl.set_value(h.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDateRangeField(listview, df) {
|
||||||
|
var page = listview.page;
|
||||||
|
var $section = page.page_form.find(".standard-filter-section");
|
||||||
|
if (!$section.length) $section = page.page_form;
|
||||||
|
|
||||||
|
var existing = page.fields_dict[df.fieldname];
|
||||||
|
var $placeholder = null;
|
||||||
|
|
||||||
|
if (existing && existing.$wrapper) {
|
||||||
|
var $existingWrap = $(existing.$wrapper);
|
||||||
|
if ($existingWrap.parent().length) {
|
||||||
|
$placeholder = $('<span class="jey-period-anchor" style="display:none"></span>');
|
||||||
|
$existingWrap.before($placeholder);
|
||||||
|
}
|
||||||
|
$existingWrap.remove();
|
||||||
|
delete page.fields_dict[df.fieldname];
|
||||||
|
}
|
||||||
|
|
||||||
|
var newField = page.add_field(
|
||||||
|
{
|
||||||
|
fieldtype: "DateRange",
|
||||||
|
label: df.label || "Posting Date",
|
||||||
|
fieldname: df.fieldname,
|
||||||
|
condition: "Between",
|
||||||
|
is_filter: 1,
|
||||||
|
onchange: function () {
|
||||||
|
if (
|
||||||
|
listview &&
|
||||||
|
listview.filter_area &&
|
||||||
|
typeof listview.filter_area.refresh_list_view === "function"
|
||||||
|
) {
|
||||||
|
listview.filter_area.trigger_refresh = true;
|
||||||
|
listview.filter_area.refresh_list_view();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$section
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!newField) return null;
|
||||||
|
|
||||||
|
newField.df.match_type = "Between";
|
||||||
|
newField.df.condition = "Between";
|
||||||
|
|
||||||
|
var $newWrap = $(newField.$wrapper || newField.wrapper);
|
||||||
|
$newWrap.addClass("jey-period-filter");
|
||||||
|
|
||||||
|
if ($placeholder && $placeholder.length) {
|
||||||
|
$placeholder.replaceWith($newWrap);
|
||||||
|
} else {
|
||||||
|
var $idWrap = $section.find('[data-fieldname="name"]').closest(".frappe-control");
|
||||||
|
if ($idWrap.length) {
|
||||||
|
$idWrap.after($newWrap);
|
||||||
|
} else {
|
||||||
|
$section.prepend($newWrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newField;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reflectExistingFilter(listview, df, field) {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!listview.filter_area ||
|
||||||
|
!listview.filter_area.filter_list ||
|
||||||
|
typeof listview.filter_area.filter_list.get_filters !== "function"
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var filters = listview.filter_area.filter_list.get_filters() || [];
|
||||||
|
var match = null;
|
||||||
|
for (var i = 0; i < filters.length; i++) {
|
||||||
|
var f = filters[i];
|
||||||
|
if (f[1] === df.fieldname && f[2] === "Between") {
|
||||||
|
match = f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) return;
|
||||||
|
|
||||||
|
var sidebarFilter = listview.filter_area.filter_list.get_filter(df.fieldname);
|
||||||
|
if (sidebarFilter && typeof sidebarFilter.remove === "function") {
|
||||||
|
sidebarFilter.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
field.df.match_type = "Between";
|
||||||
|
field.df.condition = "Between";
|
||||||
|
field.set_value(match[3]);
|
||||||
|
} catch (e) {
|
||||||
|
/* noop */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup(listview) {
|
||||||
|
if (!listview || listview.view_name !== "List") return;
|
||||||
|
if (listview.__jeyPeriodSet) return;
|
||||||
|
if (!listview.page || !listview.page.page_form) return;
|
||||||
|
|
||||||
|
var df = findTriggerDf(listview);
|
||||||
|
if (!df) return;
|
||||||
|
|
||||||
|
if (listview.filter_area) patchFilterAreaProto(listview.filter_area);
|
||||||
|
|
||||||
|
var field = buildDateRangeField(listview, df);
|
||||||
|
if (!field) return;
|
||||||
|
|
||||||
|
reflectExistingFilter(listview, df, field);
|
||||||
|
|
||||||
|
listview.__jeyPeriodSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapRenderList() {
|
||||||
|
if (!window.frappe || !frappe.views || !frappe.views.ListView) return false;
|
||||||
|
var proto = frappe.views.ListView.prototype;
|
||||||
|
if (proto.__jeyPeriodWrapped) return true;
|
||||||
|
proto.__jeyPeriodWrapped = true;
|
||||||
|
|
||||||
|
var originalRender = proto.render_list;
|
||||||
|
proto.render_list = function () {
|
||||||
|
var result = originalRender.apply(this, arguments);
|
||||||
|
try { setup(this); } catch (e) { /* noop */ }
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wrapRenderList()) {
|
||||||
|
var attempts = 0;
|
||||||
|
var iv = setInterval(function () {
|
||||||
|
attempts++;
|
||||||
|
if (wrapRenderList() || attempts > 100) clearInterval(iv);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
List View — Override SVG sprite icons
|
List View — Override SVG sprite icons
|
||||||
Replaces <symbol> content in Frappe's SVG sprite sheets so every
|
Replaces <symbol> content in Frappe's SVG sprite sheets so every
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue