Skip to content

Commit 9a1eab7

Browse files
Merge pull request frappe#29142 from iamejaaz/child-table-ui-29106
2 parents 430c0c7 + 98be3f4 commit 9a1eab7

File tree

3 files changed

+140
-44
lines changed

3 files changed

+140
-44
lines changed

frappe/public/js/frappe/form/grid.js

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ export default class Grid {
7474
<div class="grid-empty text-center text-extra-muted">
7575
${__("No rows")}
7676
</div>
77-
<div class="grid-scroll-bar">
78-
<div class="grid-scroll-bar-rows"></div>
79-
</div>
8077
</div>
8178
</div>
79+
<div class="grid-scroll-bar">
80+
<div class="grid-scroll-bar-rows"></div>
81+
</div>
8282
</div>
8383
<div class="small form-clickable-section grid-footer">
8484
<div class="flex justify-between">
@@ -123,6 +123,56 @@ export default class Grid {
123123

124124
this.form_grid = this.wrapper.find(".form-grid");
125125

126+
if (this.form_grid) {
127+
this.form_grid.on("wheel", (e) => {
128+
const isTrackpad = Math.abs(e.originalEvent.wheelDeltaY) < 50;
129+
const delta = e.originalEvent.deltaX;
130+
const scroll_bar = this.wrapper.find(".grid-scroll-bar");
131+
132+
if (isTrackpad) {
133+
scroll_bar.scrollLeft(scroll_bar.scrollLeft() + delta);
134+
} else {
135+
scroll_bar.scrollLeft(scroll_bar.scrollLeft() + delta * 4);
136+
}
137+
138+
// prevent default behaviour when it is scrolled horizontally
139+
if (e.originalEvent.deltaX != 0) {
140+
e.preventDefault();
141+
}
142+
});
143+
let touchStartX = 0;
144+
let touchMoveX = 0;
145+
let isTouchScrolling = false;
146+
147+
// Handle touch start
148+
this.form_grid.on("touchstart", (e) => {
149+
const touch = e.originalEvent.touches[0];
150+
touchStartX = touch.pageX;
151+
isTouchScrolling = true;
152+
});
153+
154+
// Handle touch move
155+
this.form_grid.on("touchmove", (e) => {
156+
if (!isTouchScrolling) return;
157+
158+
const touch = e.originalEvent.touches[0];
159+
touchMoveX = touch.pageX;
160+
161+
const scrollBar = this.wrapper.find(".grid-scroll-bar");
162+
const deltaX = touchStartX - touchMoveX;
163+
164+
scrollBar.scrollLeft(scrollBar.scrollLeft() + deltaX);
165+
166+
touchStartX = touchMoveX;
167+
168+
e.preventDefault();
169+
});
170+
171+
// Handle touch end
172+
this.form_grid.on("touchend", () => {
173+
isTouchScrolling = false;
174+
});
175+
}
126176
this.setup_add_row();
127177

128178
this.setup_grid_pagination();

frappe/public/js/frappe/form/grid_row.js

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default class GridRow {
1010
this.columns_list = [];
1111
this.row_check_html = '<input type="checkbox" class="grid-row-check">';
1212
this.make();
13+
this.setup_tab_listeners();
1314
}
1415
make() {
1516
let me = this;
@@ -269,11 +270,15 @@ export default class GridRow {
269270
}
270271
});
271272
} else if (this.show_search) {
272-
this.row_check = $(`<div class="row-check col search"></div>`).appendTo(this.row);
273+
this.row_check = $(`
274+
<div class="row-check col search">
275+
<input type="text" class="form-control input-xs text-center invisible">
276+
</div>`).appendTo(this.row);
273277

274278
this.row_index = $(
275279
`<div class="row-index col search">
276280
<input type="text" class="form-control input-xs text-center" >
281+
<span style="width: 33px;" class="d-block"></span>
277282
</div>`
278283
).appendTo(this.row);
279284

@@ -982,43 +987,65 @@ export default class GridRow {
982987
$col.field_area = $('<div class="field-area"></div>').appendTo($col).toggle(false);
983988
$col.static_area = $('<div class="static-area ellipsis"></div>').appendTo($col).html(txt);
984989

990+
this.handle_scroll_bar();
991+
992+
// set title attribute to see full label for columns in the heading row
993+
if (!this.doc) {
994+
$col.attr("title", txt);
995+
}
996+
df.fieldname && $col.static_area.toggleClass("reqd", Boolean(df.reqd));
997+
998+
$col.df = df;
999+
$col.column_index = ci;
1000+
1001+
this.columns[df.fieldname] = $col;
1002+
this.columns_list.push($col);
1003+
1004+
return $col;
1005+
}
1006+
1007+
handle_scroll_bar() {
9851008
$(document).ready(function () {
9861009
let $scrollBar = $(".grid-scroll-bar");
9871010
let form_grid = $(".form-grid");
988-
let grid_container = $(".form-grid-container");
9891011
let grid_scroll_bar_rows = $(".grid-scroll-bar-rows");
1012+
let parent_field_name = "";
1013+
let grid_width = 0;
1014+
let grid_body_width = 0;
1015+
form_grid.each((grid_index, grid) => {
1016+
parent_field_name = $(grid)
1017+
.closest("[data-fieldname][data-fieldtype]")
1018+
.attr("data-fieldname");
1019+
grid_width = $($(grid)[0]).width();
1020+
grid_body_width = $(
1021+
'div[data-fieldname="' + parent_field_name + '"] .grid-body'
1022+
).width();
1023+
1024+
$('div[data-fieldname="' + parent_field_name + '"] .grid-scroll-bar').width(
1025+
grid_width
1026+
);
1027+
$('div[data-fieldname="' + parent_field_name + '"] .grid-scroll-bar-rows').width(
1028+
grid_body_width
1029+
);
1030+
});
1031+
9901032
// Make sure the grid container is scrollable
9911033
$scrollBar.on("scroll", function (event) {
992-
grid_container = $(event.currentTarget).closest(".form-grid-container");
993-
form_grid = $(event.currentTarget).closest(".form-grid");
9941034
grid_scroll_bar_rows = $(event.currentTarget).closest(".grid-scroll-bar-rows");
9951035

9961036
var scroll_left = $(this).scrollLeft();
9971037

9981038
// Sync the form grid's left position with the scroll bar
9991039
form_grid.css("position", "relative");
10001040
form_grid.css("left", -scroll_left + "px");
1001-
$(this).css("margin-left", scroll_left + "px");
10021041
});
1003-
1004-
$scrollBar.css("width", grid_container.width());
1005-
1006-
grid_scroll_bar_rows.css("width", form_grid[0].scrollWidth);
10071042
});
1043+
}
10081044

1009-
// set title attribute to see full label for columns in the heading row
1010-
if (!this.doc) {
1011-
$col.attr("title", txt);
1012-
}
1013-
df.fieldname && $col.static_area.toggleClass("reqd", Boolean(df.reqd));
1014-
1015-
$col.df = df;
1016-
$col.column_index = ci;
1017-
1018-
this.columns[df.fieldname] = $col;
1019-
this.columns_list.push($col);
1020-
1021-
return $col;
1045+
setup_tab_listeners() {
1046+
$(".nav-link").on("shown.bs.tab", () => {
1047+
this.handle_scroll_bar();
1048+
});
10221049
}
10231050

10241051
activate() {

frappe/public/scss/common/grid.scss

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
border: 1px solid var(--table-border-color);
33
border-radius: var(--border-radius-md);
44
color: var(--text-color);
5+
min-height: 150px;
56
background-color: var(--subtle-accent);
67
}
78

@@ -25,15 +26,14 @@
2526
}
2627

2728
&.with-filter {
28-
height: 64px;
29+
height: 66px;
2930
}
3031
.filter-row {
3132
background-color: var(--bg-color);
3233

3334
.search {
3435
// TODO: Align with table grid without overwriting padding if possible
3536
padding: 4px 7px 4px 7px !important;
36-
border-bottom: 1px solid var(--table-border-color);
3737
}
3838
}
3939

@@ -190,6 +190,7 @@
190190

191191
.row-index {
192192
text-align: center;
193+
flex-grow: 0;
193194
}
194195

195196
.grid-row > .row {
@@ -230,7 +231,9 @@
230231
--input-disabled-bg: var(--gray-50);
231232
.grid-static-col {
232233
padding: 0px !important;
233-
height: 32px;
234+
}
235+
.grid-static-col.row-index {
236+
padding: var(--grid-padding) !important;
234237
}
235238

236239
.frappe-control[data-fieldtype="Select"].form-group .select-icon {
@@ -328,6 +331,9 @@
328331
line-height: 1.3 !important;
329332
}
330333
}
334+
.data-row div[data-fieldname="options"] {
335+
height: 40px;
336+
}
331337
}
332338

333339
@media (max-width: 767px) {
@@ -600,44 +606,58 @@
600606
background-color: var(--subtle-accent);
601607
border-top-left-radius: var(--border-radius-md);
602608
border-top-right-radius: var(--border-radius-md);
609+
border: 1px solid var(--table-border-color);
603610
.form-grid {
604611
display: grid;
605612
grid-auto-rows: min-content;
613+
border: unset;
606614
.grid-static-col.col-xs-1 {
607615
flex: 1 0 60px;
616+
width: 60px;
608617
}
609618
.grid-static-col.col-xs-2 {
610-
flex: 1 0 90px;
619+
flex: 1 0 100px;
620+
width: 100px;
611621
}
612622
.grid-static-col.col-xs-3 {
613-
flex: 1 0 120px;
623+
flex: 1 0 140px;
624+
width: 140px;
614625
}
615626
.grid-static-col.col-xs-4 {
616-
flex: 1 0 150px;
627+
flex: 1 0 200px;
628+
width: 200;
617629
}
618630
.grid-static-col.col-xs-5 {
619-
flex: 1 0 200px;
631+
flex: 1 0 250px;
632+
width: 250px;
620633
}
621634
.grid-static-col.col-xs-6 {
622-
flex: 1 0 250px;
635+
flex: 1 0 300px;
636+
width: 300px;
623637
}
624638
.grid-static-col.col-xs-7 {
625-
flex: 1 0 300px;
639+
flex: 1 0 350px;
640+
width: 350px;
626641
}
627642
.grid-static-col.col-xs-8 {
628-
flex: 1 0 350px;
643+
flex: 1 0 400px;
644+
width: 400px;
629645
}
630646
.grid-static-col.col-xs-9 {
631-
flex: 1 0 400px;
647+
flex: 1 0 450px;
648+
width: 450px;
632649
}
633650
.grid-static-col.col-xs-10 {
634-
flex: 1 0 450px;
651+
flex: 1 0 500px;
652+
width: 500px;
635653
}
636654
.grid-static-col.col-xs-11 {
637-
flex: 1 0 500px;
655+
flex: 1 0 550px;
656+
width: 550px;
638657
}
639658
.grid-static-col.col-xs-12 {
640-
flex: 1 0 550px;
659+
flex: 1 0 600px;
660+
width: 600px;
641661
}
642662
.grid-row > .row .col:last-child,
643663
.grid-row > .dialog-assignment-row .col:first-child,
@@ -650,10 +670,13 @@
650670
max-width: 40px;
651671
min-width: 40px;
652672
}
673+
.grid-heading-row .grid-row .data-row.row,
674+
.grid-body .rows .grid-row .data-row.row {
675+
justify-content: space-between;
676+
}
653677
}
654678
}
655679
.grid-scroll-bar {
656-
display: none;
657680
overflow-x: auto;
658681
height: 12px;
659682
position: relative;
@@ -670,10 +693,6 @@
670693
}
671694

672695
@media (max-width: map-get($grid-breakpoints, "md")) {
673-
.grid-scroll-bar {
674-
display: block;
675-
}
676-
677696
.form-column.col-sm-6 .form-grid {
678697
.row-index {
679698
display: block;

0 commit comments

Comments
 (0)