Skip to content

Commit 7d464a1

Browse files
committed
feat: Add StatusToolbar to DevIndex Grid with progressive streaming updates (#9114)
1 parent 7311f11 commit 7d464a1

9 files changed

Lines changed: 254 additions & 37 deletions

File tree

apps/devindex/view/home/ControlsContainer.mjs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ class ControlsContainer extends Container {
9191
style : {marginTop: '1em'},
9292
valueLabel : 'Show Animations',
9393
width : 200
94-
}, {
95-
ntype : 'label',
96-
reference: 'count-rows-label',
97-
style : {marginTop: '1em'}
9894
}]
9995
}, {
10096
module : Profile,
@@ -166,15 +162,6 @@ class ControlsContainer extends Container {
166162

167163
onConstructed() {
168164
super.onConstructed();
169-
170-
let me = this,
171-
{store} = me.grid;
172-
173-
store.on({
174-
filter: me.updateRowsLabel,
175-
load : me.updateRowsLabel,
176-
scope : me
177-
})
178165
}
179166

180167
/**
@@ -210,17 +197,6 @@ class ControlsContainer extends Container {
210197
onShowAnimationsChange(data) {
211198
this.grid.animateVisuals = data.value
212199
}
213-
214-
/**
215-
*
216-
*/
217-
updateRowsLabel() {
218-
let {store} = this.grid;
219-
220-
if (!store.isLoading) {
221-
this.getItem('count-rows-label').text = 'Visible: ' + store.getCount()
222-
}
223-
}
224200
}
225201

226202
export default Neo.setupClass(ControlsContainer);

apps/devindex/view/home/GridContainer.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import BaseGridContainer from '../../../../src/grid/Container.mjs';
22
import Contributors from '../../store/Contributors.mjs';
3+
import StatusToolbar from './StatusToolbar.mjs';
34

45
/**
56
* @class DevIndex.view.home.GridContainer
@@ -36,6 +37,13 @@ class GridContainer extends BaseGridContainer {
3637
isScrollingChange: 'onGridIsScrollingChange'
3738
}
3839
},
40+
/**
41+
* @member {Object} footerToolbar
42+
*/
43+
footerToolbar: {
44+
module: StatusToolbar,
45+
flex : 'none'
46+
},
3947
/**
4048
* Default configs for each column
4149
* @member {Object} columnDefaults
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import Progress from '../../../../src/component/Progress.mjs';
2+
import Toolbar from '../../../../src/grid/footer/Toolbar.mjs';
3+
4+
/**
5+
* @class DevIndex.view.home.StatusToolbar
6+
* @extends Neo.grid.footer.Toolbar
7+
*/
8+
class StatusToolbar extends Toolbar {
9+
static config = {
10+
/**
11+
* @member {String} className='DevIndex.view.home.StatusToolbar'
12+
* @protected
13+
*/
14+
className: 'DevIndex.view.home.StatusToolbar',
15+
/**
16+
* @member {String} ntype='devindex-status-toolbar'
17+
* @protected
18+
*/
19+
ntype: 'devindex-status-toolbar',
20+
/**
21+
* @member {String[]} cls=['devindex-status-toolbar']
22+
*/
23+
cls: ['devindex-status-toolbar'],
24+
/**
25+
* @member {Neo.data.Store|null} store_=null
26+
* @reactive
27+
*/
28+
store_: null,
29+
/**
30+
* @member {Object[]} items
31+
*/
32+
items: [{
33+
module : Progress,
34+
flex : 1,
35+
max : 100,
36+
reference: 'progress',
37+
value : 0
38+
}, {
39+
ntype: 'component',
40+
flex : 1
41+
}, {
42+
ntype : 'label',
43+
reference: 'count-rows-label',
44+
style : {marginLeft: '10px'},
45+
text : 'Visible: 0'
46+
}]
47+
}
48+
49+
/**
50+
* Triggered before the store config gets changed.
51+
* @param {Neo.data.Store|Object|null} value
52+
* @param {Neo.data.Store|null} oldValue
53+
* @returns {Neo.data.Store}
54+
* @protected
55+
*/
56+
beforeSetStore(value, oldValue) {
57+
let me = this,
58+
listeners = {
59+
filter : me.updateRowsLabel,
60+
load : me.onStoreLoad,
61+
progress: me.onStoreProgress,
62+
scope : me
63+
};
64+
65+
oldValue?.un(listeners);
66+
67+
// Store might be passed as an instance or config
68+
if (value && value.on) {
69+
value.on(listeners);
70+
}
71+
72+
return value
73+
}
74+
75+
/**
76+
* @param {Object} data
77+
*/
78+
onStoreLoad(data) {
79+
this.updateRowsLabel();
80+
81+
let progress = this.getItem('progress');
82+
83+
if (progress) {
84+
progress.value = progress.max;
85+
86+
this.timeout(500).then(() => {
87+
progress.hidden = true
88+
})
89+
}
90+
}
91+
92+
/**
93+
* @param {Object} data {loaded, total}
94+
*/
95+
onStoreProgress(data) {
96+
let progress = this.getItem('progress');
97+
98+
if (progress) {
99+
progress.hidden = false;
100+
progress.max = data.total || 100;
101+
progress.value = data.loaded;
102+
103+
// Indeterminate state if total is unknown
104+
if (!data.total) {
105+
progress.value = null
106+
}
107+
}
108+
}
109+
110+
/**
111+
*
112+
*/
113+
updateRowsLabel() {
114+
let {store} = this;
115+
116+
if (store) {
117+
this.getItem('count-rows-label').text = 'Visible: ' + store.count
118+
}
119+
}
120+
}
121+
122+
export default Neo.setupClass(StatusToolbar);

resources/scss/src/grid/Container.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
border : 1px solid var(--grid-container-border-color);
2929
border-spacing : 0;
3030
color : var(--grid-container-color);
31+
display : flex;
32+
flex-direction : column;
3133
font-size : var(--grid-container-font-size);
3234
font-weight : 400;
3335
height : 100%;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.neo-grid-footer-toolbar {
2+
border-top: 1px solid var(--grid-container-border-color);
3+
left : 0;
4+
min-width : fit-content;
5+
padding : 0;
6+
position : sticky;
7+
width : 100%;
8+
z-index : 2;
9+
}

src/data/Store.mjs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,14 @@ class Store extends Collection {
769769
me.fire('load', {items: me.items, total: me.count});
770770
};
771771

772-
me.proxy.on('data', onData);
772+
const onProgress = (data) => {
773+
me.fire('progress', data)
774+
};
775+
776+
me.proxy.on({
777+
data : onData,
778+
progress: onProgress
779+
});
773780

774781
try {
775782
// params.url can override proxy url
@@ -779,7 +786,10 @@ class Store extends Collection {
779786

780787
const response = await me.proxy.read(params);
781788

782-
me.proxy.un('data', onData);
789+
me.proxy.un({
790+
data : onData,
791+
progress: onProgress
792+
});
783793

784794
if (response.success) {
785795
me.totalCount = response.totalCount || me.count;
@@ -792,7 +802,10 @@ class Store extends Collection {
792802
return null;
793803
}
794804
} catch (e) {
795-
me.proxy.un('data', onData);
805+
me.proxy.un({
806+
data : onData,
807+
progress: onProgress
808+
});
796809
me.isLoading = false;
797810
throw e;
798811
}

src/data/proxy/Stream.mjs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ class Stream extends Base {
6464
throw new Error('ReadableStream not supported in this environment.');
6565
}
6666

67-
const reader = response.body
68-
.pipeThrough(new TextDecoderStream())
69-
.getReader();
67+
const reader = response.body.getReader();
68+
const decoder = new TextDecoder();
7069

7170
let buffer = '';
71+
let loaded = 0;
72+
const total = parseInt(response.headers.get('content-length') || 0, 10);
7273

7374
while (true) {
7475
const { value, done } = await reader.read();
@@ -86,7 +87,10 @@ class Stream extends Base {
8687
break;
8788
}
8889

89-
buffer += value;
90+
loaded += value.byteLength;
91+
me.fire('progress', {loaded, total});
92+
93+
buffer += decoder.decode(value, {stream: true});
9094
const lines = buffer.split('\n');
9195
// Keep the last partial line in the buffer
9296
buffer = lines.pop();

0 commit comments

Comments
 (0)