jb-grid 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/index.js +1 -0
- package/package.json +32 -0
- package/react/README.md +195 -0
- package/react/dist/JBGrid.cjs.js +17989 -0
- package/react/dist/JBGrid.cjs.js.map +1 -0
- package/react/dist/JBGrid.js +17983 -0
- package/react/dist/JBGrid.js.map +1 -0
- package/react/dist/JBGrid.umd.js +17989 -0
- package/react/dist/JBGrid.umd.js.map +1 -0
- package/react/dist/common/hooks/use-event.d.ts +3 -0
- package/react/dist/common/hooks/useLazyRef.d.ts +4 -0
- package/react/dist/common/hooks/useMobx.d.ts +4 -0
- package/react/dist/common/scripts/device-detection.d.ts +1 -0
- package/react/dist/common/scripts/persian-helper.d.ts +3 -0
- package/react/dist/web-component/jb-grid/react/lib/Components/Cell.d.ts +10 -0
- package/react/dist/web-component/jb-grid/react/lib/Components/ExpandRow.d.ts +11 -0
- package/react/dist/web-component/jb-grid/react/lib/Components/JBLoading.d.ts +6 -0
- package/react/dist/web-component/jb-grid/react/lib/Components/Row.d.ts +5 -0
- package/react/dist/web-component/jb-grid/react/lib/Components/content-error/ContentError.d.ts +10 -0
- package/react/dist/web-component/jb-grid/react/lib/Content.d.ts +16 -0
- package/react/dist/web-component/jb-grid/react/lib/Footer.d.ts +9 -0
- package/react/dist/web-component/jb-grid/react/lib/Header.d.ts +22 -0
- package/react/dist/web-component/jb-grid/react/lib/JBGrid.d.ts +24 -0
- package/react/dist/web-component/jb-grid/react/lib/JBGridData.d.ts +16 -0
- package/react/dist/web-component/jb-grid/react/lib/JBGridViewModel.d.ts +71 -0
- package/react/dist/web-component/jb-grid/react/lib/Types.d.ts +124 -0
- package/react/dist/web-component/jb-grid/react/lib/i18n.d.ts +2 -0
- package/react/dist/web-component/jb-searchbar/types.d.ts +1 -0
- package/react/index.js +1 -0
- package/react/lib/Components/Cell.scss +63 -0
- package/react/lib/Components/Cell.tsx +19 -0
- package/react/lib/Components/ExpandRow.scss +37 -0
- package/react/lib/Components/ExpandRow.tsx +19 -0
- package/react/lib/Components/JBLoading.scss +140 -0
- package/react/lib/Components/JBLoading.tsx +30 -0
- package/react/lib/Components/Row.scss +25 -0
- package/react/lib/Components/Row.tsx +18 -0
- package/react/lib/Components/content-error/ContentError.tsx +20 -0
- package/react/lib/Components/content-error/content-error.scss +26 -0
- package/react/lib/Content.tsx +76 -0
- package/react/lib/Footer.tsx +95 -0
- package/react/lib/Header.tsx +70 -0
- package/react/lib/JBGrid.scss +350 -0
- package/react/lib/JBGrid.tsx +62 -0
- package/react/lib/JBGridBridgeExample.js +90 -0
- package/react/lib/JBGridData.ts +51 -0
- package/react/lib/JBGridViewModel.ts +476 -0
- package/react/lib/Types.ts +142 -0
- package/react/lib/_constants.scss +7 -0
- package/react/lib/i18n.ts +15 -0
- package/react/package.json +42 -0
- package/react/tsconfig.json +18 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import { observable, extendObservable, makeObservable, action, computed } from 'mobx';
|
|
3
|
+
import { ActionDispatchers, AnyObject, JBGridBridgeClassInterface, JBGridBridgeInterface, JBGridCallbacks, JBGridColumnDef, JBGridConfig, JBGridConfigInterface, JBGridFilter, JBGridI18nConfig, JBGridResponseData, JBGridRowData, JBGridRowDetail, JBGridStyles, SearchbarConfig } from './Types.js';
|
|
4
|
+
import { JBSearchbarWebComponent } from 'jb-searchbar';
|
|
5
|
+
import { defaultI18n } from './i18n.js';
|
|
6
|
+
import { JBSearchbarValue } from 'jb-searchbar/types.js';
|
|
7
|
+
import { assign } from 'lodash';
|
|
8
|
+
class JBGridViewModel<T extends AnyObject> {
|
|
9
|
+
//we write computed style of grid here
|
|
10
|
+
styles: JBGridStyles = {
|
|
11
|
+
table: {
|
|
12
|
+
generalCols: {
|
|
13
|
+
gridTemplateColumns: "auto",
|
|
14
|
+
|
|
15
|
+
},
|
|
16
|
+
fullWidthCol: {
|
|
17
|
+
gridColumn: '1 / end'
|
|
18
|
+
},
|
|
19
|
+
scrollIndent: {
|
|
20
|
+
width: 'calc(100% - 17px)'
|
|
21
|
+
},
|
|
22
|
+
mainRowStyle: {
|
|
23
|
+
gridTemplateColumns: "auto",
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
searchBar: {
|
|
27
|
+
},
|
|
28
|
+
contentWrapper: {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
elements = {
|
|
32
|
+
refreshIcon: React.createRef<SVGElement>(),
|
|
33
|
+
searchbar: React.createRef<JBSearchbarWebComponent>()
|
|
34
|
+
}
|
|
35
|
+
//the whole component DOM store(referenced) in this variable
|
|
36
|
+
JBGridComponentDom: HTMLDivElement | null = null;
|
|
37
|
+
//keep wrapper DOM element for some pupose like wrapper changing in full screen functionality
|
|
38
|
+
gridWrapperElement: HTMLElement | null = null;
|
|
39
|
+
//when we start fetch new data from server it get true until load data is finished
|
|
40
|
+
isLoading = false;
|
|
41
|
+
//define bridge to convert grid data to server compatible data and convert server data to grid understandable format
|
|
42
|
+
dataBridge: JBGridBridgeInterface;
|
|
43
|
+
//add debounce feature to grid gotoPage function
|
|
44
|
+
paginationDebounce;
|
|
45
|
+
//keep grid searchbar height so on height
|
|
46
|
+
isErrorOccurred = false;
|
|
47
|
+
filter: JBGridFilter = {
|
|
48
|
+
config: null,
|
|
49
|
+
value: []
|
|
50
|
+
}
|
|
51
|
+
callBacks: JBGridCallbacks = {
|
|
52
|
+
onFullscreenChange: () => { console.error('you must set onFullscreenChange callback to jb-grid component if you want it to work'); }
|
|
53
|
+
}
|
|
54
|
+
config: JBGridConfig<T>;
|
|
55
|
+
i18n: JBGridI18nConfig;
|
|
56
|
+
constructor(onFullscreenChange: (isFullScreen: boolean) => void, config: JBGridConfigInterface<T>, bridge: JBGridBridgeClassInterface) {
|
|
57
|
+
makeObservable(this, {
|
|
58
|
+
styles: observable,
|
|
59
|
+
isLoading: observable,
|
|
60
|
+
isErrorOccurred: observable,
|
|
61
|
+
filter: observable,
|
|
62
|
+
exitFullScreenGrid: action,
|
|
63
|
+
fullScreenGrid: action,
|
|
64
|
+
goToLastPage: action,
|
|
65
|
+
fetchGridData: action,
|
|
66
|
+
initFilter: action.bound,
|
|
67
|
+
InitSize: action.bound,
|
|
68
|
+
onFetchSuccess: action.bound,
|
|
69
|
+
mergeObject: action,
|
|
70
|
+
sendFirstRequest: action,
|
|
71
|
+
refreshBtnClick: action.bound,
|
|
72
|
+
setSortColumn: action.bound,
|
|
73
|
+
goToPage: action.bound,
|
|
74
|
+
openMainHeaderSection: action.bound,
|
|
75
|
+
openSearchHeaderSection: action.bound,
|
|
76
|
+
onPageSizeChange: action.bound,
|
|
77
|
+
InitGrid: action.bound,
|
|
78
|
+
refreshData: action.bound,
|
|
79
|
+
showErrorPanel: action.bound,
|
|
80
|
+
hideErrorPanel: action.bound,
|
|
81
|
+
paginationDisplayNumbers: computed
|
|
82
|
+
});
|
|
83
|
+
if (config == undefined || config == null) {
|
|
84
|
+
//when user dont pass config prop
|
|
85
|
+
console.error("JBGrid need you to pass config as a prop to it \n and currently its null or undefined");
|
|
86
|
+
}
|
|
87
|
+
const observableConfig = observable(config);
|
|
88
|
+
this.setI18n({});
|
|
89
|
+
this.paginationDebounce = this.debounce(this.refreshData, 300);
|
|
90
|
+
|
|
91
|
+
//TODO:add trigger function so user can call grid functions outside of grid js file
|
|
92
|
+
const actionDispatchers: ActionDispatchers = Object.freeze({
|
|
93
|
+
refreshData: () => this.refreshData(),
|
|
94
|
+
fullScreenGrid: () => this.fullScreenGrid(),
|
|
95
|
+
exitFullScreenGrid: () => this.exitFullScreenGrid()
|
|
96
|
+
});
|
|
97
|
+
this.config = observableConfig;
|
|
98
|
+
this.config.actionDispatchers = actionDispatchers;
|
|
99
|
+
if (typeof bridge != 'function') {
|
|
100
|
+
//no bridge provided
|
|
101
|
+
}
|
|
102
|
+
this.dataBridge = new bridge();
|
|
103
|
+
if (typeof onFullscreenChange == "function") {
|
|
104
|
+
this.callBacks.onFullscreenChange = onFullscreenChange;
|
|
105
|
+
}
|
|
106
|
+
this.InitGrid();
|
|
107
|
+
}
|
|
108
|
+
setI18n(newValue: JBGridI18nConfig) {
|
|
109
|
+
//loadash assign work from left to right so newValue has top priority
|
|
110
|
+
this.i18n = assign({}, defaultI18n, newValue);
|
|
111
|
+
}
|
|
112
|
+
InitGrid() {
|
|
113
|
+
//init grid config on load or change
|
|
114
|
+
this.InitSize();
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
onComponentDidMount(searchbarConfig: SearchbarConfig | null) {
|
|
118
|
+
this.sendFirstRequest();
|
|
119
|
+
this.initFilter(searchbarConfig);
|
|
120
|
+
}
|
|
121
|
+
mergeObject(inputConfig: JBGridConfig<any>, defaultConfig: JBGridConfig<any>) {
|
|
122
|
+
const addedProperty = {};
|
|
123
|
+
for (const prop in defaultConfig) {
|
|
124
|
+
if (inputConfig[prop] == undefined || inputConfig[prop] == null) {
|
|
125
|
+
addedProperty[prop] = defaultConfig[prop];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
extendObservable(inputConfig, addedProperty);
|
|
129
|
+
return inputConfig;
|
|
130
|
+
}
|
|
131
|
+
sendFirstRequest() {
|
|
132
|
+
this.isLoading = true;
|
|
133
|
+
this.fetchGridData().then(action(() => {
|
|
134
|
+
this.isLoading = false;
|
|
135
|
+
this.hideErrorPanel();
|
|
136
|
+
})).catch(action((e: any) => {
|
|
137
|
+
this.isLoading = false;
|
|
138
|
+
this.showErrorPanel();
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
initFilter(searchbarConfig: SearchbarConfig | null) {
|
|
142
|
+
if (searchbarConfig && this.elements.searchbar.current) {
|
|
143
|
+
this.elements.searchbar.current.columnList = searchbarConfig.columnList;
|
|
144
|
+
this.elements.searchbar.current.searchOnChange = searchbarConfig.searchOnChange === true ? searchbarConfig.searchOnChange : false;
|
|
145
|
+
this.elements.searchbar.current.addEventListener('search', (e) => {
|
|
146
|
+
this.elements.searchbar.current!.isLoading = true;
|
|
147
|
+
const target = e.target as JBSearchbarWebComponent;
|
|
148
|
+
this.onSearch(target.value).finally(() => {
|
|
149
|
+
this.elements.searchbar.current!.isLoading = false;
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
//this.elements.searchbar.current.addEventListener('');
|
|
153
|
+
this.filter.config = searchbarConfig;
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
InitSize() {
|
|
158
|
+
//init table width column
|
|
159
|
+
const scrollWidth = this.getScrollbarWidth();
|
|
160
|
+
this.styles.table.scrollIndent.width = 'calc(100% - ' + scrollWidth + 'px)';
|
|
161
|
+
//config css grid for table layout
|
|
162
|
+
let gridTemplateColumns = "";
|
|
163
|
+
this.config.table.columns.map((item) => {
|
|
164
|
+
if (item.width != null || item.width != undefined) {
|
|
165
|
+
if (typeof (item.width) == "number") {
|
|
166
|
+
gridTemplateColumns += ' ' + item.width + 'px';
|
|
167
|
+
} else {
|
|
168
|
+
gridTemplateColumns += ' ' + item.width;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
} else {
|
|
172
|
+
gridTemplateColumns += " 1fr";
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
this.styles.table.generalCols.gridTemplateColumns = gridTemplateColumns;
|
|
176
|
+
this.styles.table.fullWidthCol.gridColumn = "1 / " + (this.config.table.columns.length + 1);
|
|
177
|
+
}
|
|
178
|
+
getScrollbarWidth() {
|
|
179
|
+
const outer = document.createElement("div");
|
|
180
|
+
outer.style.visibility = "hidden";
|
|
181
|
+
outer.style.width = "100px";
|
|
182
|
+
document.body.appendChild(outer);
|
|
183
|
+
|
|
184
|
+
const widthNoScroll = outer.offsetWidth;
|
|
185
|
+
// force scrollbars
|
|
186
|
+
outer.style.overflow = "scroll";
|
|
187
|
+
|
|
188
|
+
// add inner div
|
|
189
|
+
const inner = document.createElement("div");
|
|
190
|
+
inner.style.width = "100%";
|
|
191
|
+
outer.appendChild(inner);
|
|
192
|
+
|
|
193
|
+
const widthWithScroll = inner.offsetWidth;
|
|
194
|
+
|
|
195
|
+
// remove div's
|
|
196
|
+
outer.parentNode?.removeChild(outer);
|
|
197
|
+
|
|
198
|
+
return widthNoScroll - widthWithScroll;
|
|
199
|
+
}
|
|
200
|
+
fetchGridData() {
|
|
201
|
+
const fetchGridDataPromise = new Promise((resolve, reject) => {
|
|
202
|
+
const requestBody = this.CreateRequestBody();
|
|
203
|
+
this.dataBridge.getData(this.config.data.requestParams, requestBody).then(action((data) => {
|
|
204
|
+
const bridgeData = this.dataBridge.mapServerResponseDataToGridData(data);
|
|
205
|
+
if (bridgeData.pageIndex == this.config.page.index) {
|
|
206
|
+
this.config.data.data = [];
|
|
207
|
+
//check user don't change page during loading time if he do we wait for latest response
|
|
208
|
+
this.standardData(bridgeData.content).then((content: JBGridRowData<T>[]) => {
|
|
209
|
+
const data = { ...bridgeData, content };
|
|
210
|
+
this.onFetchSuccess(data);
|
|
211
|
+
resolve(null);
|
|
212
|
+
});
|
|
213
|
+
} else {
|
|
214
|
+
console.error('jb-grid requested page index is different from response page index it maybe a bridge problem or server data problem');
|
|
215
|
+
}
|
|
216
|
+
})).catch((err) => {
|
|
217
|
+
reject(err);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
return fetchGridDataPromise;
|
|
221
|
+
}
|
|
222
|
+
onFetchSuccess(data: JBGridResponseData<T>) {
|
|
223
|
+
this.config.data.data = data.content;
|
|
224
|
+
this.config.data.metaData.startItemIndex = data.startItemIndex;
|
|
225
|
+
this.config.data.metaData.endItemIndex = data.endItemIndex;
|
|
226
|
+
this.config.data.metaData.totalItemsCount = data.totalItemsCount;
|
|
227
|
+
this.config.page.totalPages = data.totalPages;
|
|
228
|
+
}
|
|
229
|
+
standardData(data: AnyObject[]) {
|
|
230
|
+
return new Promise<JBGridRowData<T>[]>((resolve) => {
|
|
231
|
+
const items: JBGridRowData<AnyObject>[] = data.map((item) => {
|
|
232
|
+
const detail: JBGridRowDetail = {
|
|
233
|
+
jbGridDetail: {
|
|
234
|
+
isDeleting: false,
|
|
235
|
+
isDeleted: false,
|
|
236
|
+
isRecovering: false,
|
|
237
|
+
isExpanded: false
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const row: JBGridRowData<AnyObject> = Object.assign({}, item, detail);
|
|
241
|
+
return row;
|
|
242
|
+
});
|
|
243
|
+
//in case of user want to modify or add custom field to our observable array
|
|
244
|
+
if (typeof this.config.callbacks.onDataStandardizing == "function") {
|
|
245
|
+
const response = this.config.callbacks.onDataStandardizing<T>(items);
|
|
246
|
+
if (response instanceof Promise) {
|
|
247
|
+
response.then((content) => {
|
|
248
|
+
resolve(content);
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
resolve(response);
|
|
252
|
+
}
|
|
253
|
+
//end of callback block
|
|
254
|
+
} else {
|
|
255
|
+
resolve(items as JBGridRowData<T>[]);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
CreateRequestBody() {
|
|
264
|
+
const sortColumn = this.config.table.columns.find(x => x.sort) || null;
|
|
265
|
+
const requestBody = this.dataBridge.createRequestBody(this.config.page, this.filter.value, sortColumn, this.config.data);
|
|
266
|
+
return requestBody;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
goToNextPage() {
|
|
270
|
+
const currentPage = this.config.page.index;
|
|
271
|
+
if (currentPage < this.config.page.totalPages) {
|
|
272
|
+
this.goToPage(currentPage + 1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
goToPrevPage() {
|
|
277
|
+
const currentPage = this.config.page.index;
|
|
278
|
+
if (currentPage > 1) {
|
|
279
|
+
this.goToPage(currentPage - 1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
goToLastPage() {
|
|
284
|
+
const currentPage = this.config.page.index;
|
|
285
|
+
if (currentPage != this.config.page.totalPages) {
|
|
286
|
+
this.goToPage(this.config.page.totalPages);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
goToFirstPage() {
|
|
291
|
+
const currentPage = this.config.page.index;
|
|
292
|
+
if (currentPage != 1) {
|
|
293
|
+
this.goToPage(1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
refreshBtnClick() {
|
|
298
|
+
const anime = this.playRefreshBtnAnimation();
|
|
299
|
+
this.refreshData().then(() => {
|
|
300
|
+
this.stopRefreshBtnAnimation(anime);
|
|
301
|
+
}).catch((e) => {
|
|
302
|
+
console.error('Error while refreshing data', e);
|
|
303
|
+
this.stopRefreshBtnAnimation(anime);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
playRefreshBtnAnimation() {
|
|
307
|
+
const anime = this.elements.refreshIcon.current!.animate([{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }], { id: 'rotate', duration: 400, direction: "reverse", iterations: Infinity });
|
|
308
|
+
return anime;
|
|
309
|
+
}
|
|
310
|
+
stopRefreshBtnAnimation(anime: Animation) {
|
|
311
|
+
anime.cancel();
|
|
312
|
+
}
|
|
313
|
+
goToPage(destinationPageIndex: number) {
|
|
314
|
+
return new Promise((resolve, reject) => {
|
|
315
|
+
//for navigate in pages you must call this function and every other way is forbidden
|
|
316
|
+
this.config.page.index = destinationPageIndex;
|
|
317
|
+
this.paginationDebounce()
|
|
318
|
+
.then(() => {
|
|
319
|
+
resolve(null);
|
|
320
|
+
if (this.config.callbacks.onPageIndexChange) {
|
|
321
|
+
this.config.callbacks.onPageIndexChange(destinationPageIndex);
|
|
322
|
+
}
|
|
323
|
+
}).catch((e: any) => {
|
|
324
|
+
reject(e);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
debounce(func: (...args: any[]) => any, delay: number) {
|
|
329
|
+
//create a waiting time for serial function call and execute last function execute request
|
|
330
|
+
let inDebounce: ReturnType<typeof setTimeout>;
|
|
331
|
+
const debounceInstance = (...inputs: any[]) => {
|
|
332
|
+
return new Promise((resolve, reject) => {
|
|
333
|
+
const self: JBGridViewModel<T> = this;
|
|
334
|
+
const args = inputs;
|
|
335
|
+
clearTimeout(inDebounce);
|
|
336
|
+
inDebounce = setTimeout(
|
|
337
|
+
() => func.apply(self, args)
|
|
338
|
+
.then((args: any) => {
|
|
339
|
+
resolve(args);
|
|
340
|
+
}).catch((e: Error) => { reject(e); })
|
|
341
|
+
, delay);
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
return debounceInstance;
|
|
345
|
+
}
|
|
346
|
+
refreshData(): Promise<void> {
|
|
347
|
+
const refreshDataPromise = new Promise<void>((resolve, reject) => {
|
|
348
|
+
this.isLoading = true;
|
|
349
|
+
this.fetchGridData().then(action(() => {
|
|
350
|
+
this.isLoading = false;
|
|
351
|
+
this.hideErrorPanel();
|
|
352
|
+
resolve();
|
|
353
|
+
})).catch(action((e) => {
|
|
354
|
+
this.isLoading = false;
|
|
355
|
+
this.showErrorPanel();
|
|
356
|
+
reject(e);
|
|
357
|
+
}));
|
|
358
|
+
});
|
|
359
|
+
//every time we need to change showing data we must call this func
|
|
360
|
+
return refreshDataPromise;
|
|
361
|
+
}
|
|
362
|
+
onSearch(filterList: JBSearchbarValue) {
|
|
363
|
+
this.filter.value = filterList;
|
|
364
|
+
const onSearchPromise = new Promise((resolve, reject) => {
|
|
365
|
+
//reset pagination when filter change
|
|
366
|
+
this.goToPage(1).then(() => {
|
|
367
|
+
resolve(null);
|
|
368
|
+
}).catch((e) => {
|
|
369
|
+
reject(e);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
return onSearchPromise;
|
|
373
|
+
}
|
|
374
|
+
onPageSizeChange(e) {
|
|
375
|
+
this.config.page.size = parseInt(e.target.value);
|
|
376
|
+
this.goToPage(1);
|
|
377
|
+
}
|
|
378
|
+
onFullScreenBtnClicked(currentValue: boolean) {
|
|
379
|
+
const newValue = !currentValue;
|
|
380
|
+
this.callBacks.onFullscreenChange(newValue);
|
|
381
|
+
}
|
|
382
|
+
onFullscreenChanged(newValue: boolean) {
|
|
383
|
+
if (newValue == true) {
|
|
384
|
+
this.fullScreenGrid();
|
|
385
|
+
} else {
|
|
386
|
+
this.exitFullScreenGrid();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
fullScreenGrid() {
|
|
390
|
+
//TODO: handle if another grid is open before new request come up
|
|
391
|
+
const container = document.createElement('div');
|
|
392
|
+
container.classList.add('jb-grid-full-screen-container');
|
|
393
|
+
document.body.append(container);
|
|
394
|
+
this.JBGridComponentDom;
|
|
395
|
+
const child = document.createElement('div');
|
|
396
|
+
child.innerHTML = "";
|
|
397
|
+
this.gridWrapperElement = this.JBGridComponentDom!.parentElement!;
|
|
398
|
+
container.append(this.JBGridComponentDom as Node);
|
|
399
|
+
//TODO:call on full screen call back
|
|
400
|
+
}
|
|
401
|
+
exitFullScreenGrid() {
|
|
402
|
+
const container = document.querySelector('.jb-grid-full-screen-container') as HTMLDivElement;
|
|
403
|
+
if (this.gridWrapperElement) {
|
|
404
|
+
//put grid element back to their orginal place
|
|
405
|
+
this.gridWrapperElement.append(this.JBGridComponentDom as Node);
|
|
406
|
+
//remove added temp fullscreen container
|
|
407
|
+
}
|
|
408
|
+
container[0].remove();
|
|
409
|
+
}
|
|
410
|
+
setSortColumn(column: JBGridColumnDef) {
|
|
411
|
+
if (column.sortable) {
|
|
412
|
+
if (column.sort) {
|
|
413
|
+
//if we just change sort order
|
|
414
|
+
column.sort = column.sort.toUpperCase() == "ASC" ? "DESC" : "ASC";
|
|
415
|
+
} else {
|
|
416
|
+
//we user chnge sort column
|
|
417
|
+
const prevColumnSort = this.config.table.columns.find(x => x.sort);
|
|
418
|
+
if (prevColumnSort) {
|
|
419
|
+
prevColumnSort.sort = undefined;
|
|
420
|
+
}
|
|
421
|
+
column.sort = "ASC";
|
|
422
|
+
}
|
|
423
|
+
this.refreshData();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
changePageNumberToInput() {
|
|
427
|
+
//when user click on page number
|
|
428
|
+
//TODO: change page Input method to text input
|
|
429
|
+
const pageNumber: string | null = prompt(this.i18n.messages.EnterPageNumberMessage, this.config.page.totalPages.toString());
|
|
430
|
+
if (pageNumber && parseInt(pageNumber) > 0 && parseInt(pageNumber) < this.config.page.totalPages) {
|
|
431
|
+
this.goToPage(parseInt(pageNumber));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
openSearchHeaderSection() {
|
|
435
|
+
this.elements.searchbar.current?.focus();
|
|
436
|
+
this.config.states.headerSection = "SEARCH";
|
|
437
|
+
}
|
|
438
|
+
openMainHeaderSection() {
|
|
439
|
+
this.config.states.headerSection = "MAIN";
|
|
440
|
+
}
|
|
441
|
+
showErrorPanel() {
|
|
442
|
+
//when we couldn't connect to server or get error from server for our request we show error panel to user
|
|
443
|
+
this.isErrorOccurred = true;
|
|
444
|
+
}
|
|
445
|
+
hideErrorPanel() {
|
|
446
|
+
this.isErrorOccurred = false;
|
|
447
|
+
}
|
|
448
|
+
get paginationDisplayNumbers() {
|
|
449
|
+
return {
|
|
450
|
+
currentPage: this.toPersianNumber(this.config.page.index),
|
|
451
|
+
nextPage: this.config.page.index + 1 <= this.config.page.totalPages ? this.toPersianNumber(this.config.page.index + 1) : "",
|
|
452
|
+
next2Page: this.config.page.index + 2 <= this.config.page.totalPages ? this.toPersianNumber(this.config.page.index + 2) : "",
|
|
453
|
+
prevPage: this.config.page.index - 1 > 0 ? this.toPersianNumber(this.config.page.index - 1) : "",
|
|
454
|
+
prev2Page: this.config.page.index - 2 > 0 ? this.toPersianNumber(this.config.page.index - 2) : "",
|
|
455
|
+
totalItemsCount: this.toPersianNumber(this.config.data.metaData.totalItemsCount),
|
|
456
|
+
startItemIndex:this.toPersianNumber(this.config.data.metaData.startItemIndex),
|
|
457
|
+
endItemIndex:this.toPersianNumber(this.config.data.metaData.endItemIndex),
|
|
458
|
+
pageSizes:[this.toPersianNumber(20),this.toPersianNumber(30),this.toPersianNumber(50),this.toPersianNumber(100)]
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
toPersianNumber(input: string | number) {
|
|
462
|
+
if (this.i18n.showPersianNumber) {
|
|
463
|
+
const inputString = input.toString();
|
|
464
|
+
const correctedString = inputString.replace(/[0-9]/g, function (word) {
|
|
465
|
+
return String.fromCharCode(1776 + parseInt(word));
|
|
466
|
+
});
|
|
467
|
+
return correctedString;
|
|
468
|
+
}
|
|
469
|
+
return input;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export default JBGridViewModel;
|
|
475
|
+
export const JBGridContext = createContext<JBGridViewModel<AnyObject> | null>(null);
|
|
476
|
+
export const useJBGridVM = () => useContext(JBGridContext);
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { FilterColumn, JBSearchbarValue } from "jb-searchbar/types.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// export type JBGridRowData = {
|
|
5
|
+
// [key: keyof T]: any,
|
|
6
|
+
// jbGridDetail: {
|
|
7
|
+
// isDeleting: boolean,
|
|
8
|
+
// isDeleted: boolean,
|
|
9
|
+
// isRecovering: boolean,
|
|
10
|
+
// isExpanded: boolean
|
|
11
|
+
// }
|
|
12
|
+
// }
|
|
13
|
+
export type JBGridRowDataDetail = {
|
|
14
|
+
isDeleting: boolean,
|
|
15
|
+
isDeleted: boolean,
|
|
16
|
+
isRecovering: boolean,
|
|
17
|
+
isExpanded: boolean
|
|
18
|
+
}
|
|
19
|
+
export type JBGridRowDetail = { jbGridDetail: JBGridRowDataDetail };
|
|
20
|
+
export type AnyObject = {
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
export type JBGridStyles = {
|
|
24
|
+
table: {
|
|
25
|
+
generalCols: {
|
|
26
|
+
gridTemplateColumns: string,
|
|
27
|
+
|
|
28
|
+
},
|
|
29
|
+
fullWidthCol: {
|
|
30
|
+
gridColumn: string
|
|
31
|
+
},
|
|
32
|
+
scrollIndent: {
|
|
33
|
+
width: string
|
|
34
|
+
},
|
|
35
|
+
mainRowStyle: {
|
|
36
|
+
gridTemplateColumns: string,
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
searchBar: any,
|
|
40
|
+
contentWrapper: any
|
|
41
|
+
}
|
|
42
|
+
export type JBGridRowData<T extends AnyObject> = T & JBGridRowDetail;
|
|
43
|
+
|
|
44
|
+
export type JBGridResponseData<T extends AnyObject> = {
|
|
45
|
+
pageIndex: number,
|
|
46
|
+
startItemIndex: number,
|
|
47
|
+
endItemIndex: number,
|
|
48
|
+
totalItemsCount: number,
|
|
49
|
+
totalPages: number,
|
|
50
|
+
content: JBGridRowData<T>[],
|
|
51
|
+
}
|
|
52
|
+
export type JBGridFilter = {
|
|
53
|
+
config: SearchbarConfig | null,
|
|
54
|
+
value: JBSearchbarValue
|
|
55
|
+
}
|
|
56
|
+
export type SearchbarConfig = {
|
|
57
|
+
columnList: FilterColumn[],
|
|
58
|
+
searchOnChange: boolean
|
|
59
|
+
}
|
|
60
|
+
export interface JBGridBridgeInterface {
|
|
61
|
+
mapServerResponseDataToGridData: (data: any) => JBGridResponseData<any>,
|
|
62
|
+
getData: (data: JBGridConfigRequestParams, requestBody: any) => Promise<any>,
|
|
63
|
+
createRequestBody: (page: JBGridDataPage, filter?: JBSearchbarValue, sortColumn?: JBGridColumnDef | null, requestConfig?: JBGridConfigRequestParams) => any
|
|
64
|
+
}
|
|
65
|
+
// export interface JBGridBridgeClassInterface {
|
|
66
|
+
// new(): JBGridBridgeInterface
|
|
67
|
+
// }
|
|
68
|
+
type ClassBuilder<I, Args extends any[] = any[]> = new (...args: Args) => I;
|
|
69
|
+
|
|
70
|
+
export type JBGridBridgeClassInterface = ClassBuilder<JBGridBridgeInterface, []>;
|
|
71
|
+
//
|
|
72
|
+
export type JBGridDataPage = {
|
|
73
|
+
index: number,
|
|
74
|
+
size: number,
|
|
75
|
+
totalPages: number,
|
|
76
|
+
}
|
|
77
|
+
export type JBGridColumnDef = {
|
|
78
|
+
width?: string | number,
|
|
79
|
+
sort?: "ASC" | "DESC" | undefined,
|
|
80
|
+
sortable?: boolean,
|
|
81
|
+
id: number,
|
|
82
|
+
title: string,
|
|
83
|
+
name: string
|
|
84
|
+
}
|
|
85
|
+
export type JBGridConfigRequestParams = {
|
|
86
|
+
method?: "POST" | "GET",
|
|
87
|
+
url?: string,
|
|
88
|
+
//user can add any parameter needed to create request in his app
|
|
89
|
+
[key: string]: any
|
|
90
|
+
}
|
|
91
|
+
export type JBGridDataConfig<T extends AnyObject> = {
|
|
92
|
+
data: JBGridRowData<T>[],
|
|
93
|
+
//TODO: make it as a Generic type so user can standard it in whole app
|
|
94
|
+
requestParams: JBGridConfigRequestParams,
|
|
95
|
+
metaData: {
|
|
96
|
+
startItemIndex: number,
|
|
97
|
+
endItemIndex: number,
|
|
98
|
+
totalItemsCount: number
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
export type JBGridTableConfig = {
|
|
102
|
+
columns: JBGridColumnDef[]
|
|
103
|
+
}
|
|
104
|
+
export type JBGridConfigStates = {
|
|
105
|
+
headerSection: 'MAIN' | "SEARCH",
|
|
106
|
+
isFullScreen: boolean
|
|
107
|
+
}
|
|
108
|
+
export type JBGridCallbackConfig<T extends AnyObject> = {
|
|
109
|
+
onDataStandardizing: ((data: JBGridRowData<AnyObject>[]) => JBGridRowData<T>[]) | (<T extends AnyObject>(data: JBGridRowData<AnyObject>[]) => Promise<JBGridRowData<T>[]>) | null | undefined,
|
|
110
|
+
onPageIndexChange: ((newPageIndex: number) => unknown) | null | undefined
|
|
111
|
+
}
|
|
112
|
+
export type JBGridI18nMessage = {
|
|
113
|
+
serverErrorText?: string,
|
|
114
|
+
serverErrorTitle?: string,
|
|
115
|
+
serverErrorRefreshButtonTitle?: string,
|
|
116
|
+
EnterPageNumberMessage?: string,
|
|
117
|
+
currentAvailableItem?: string,
|
|
118
|
+
pageItemCount?:string,
|
|
119
|
+
from?:string
|
|
120
|
+
}
|
|
121
|
+
export type JBGridI18nConfig = {
|
|
122
|
+
messages?: JBGridI18nMessage,
|
|
123
|
+
showPersianNumber?: boolean
|
|
124
|
+
}
|
|
125
|
+
export type ActionDispatchers = Readonly<{
|
|
126
|
+
refreshData: () => Promise<void>,
|
|
127
|
+
fullScreenGrid: () => void,
|
|
128
|
+
exitFullScreenGrid: () => void
|
|
129
|
+
}>
|
|
130
|
+
export interface JBGridConfigInterface<T extends AnyObject> {
|
|
131
|
+
|
|
132
|
+
table: JBGridTableConfig
|
|
133
|
+
data: JBGridDataConfig<T>,
|
|
134
|
+
page: JBGridDataPage,
|
|
135
|
+
callbacks: JBGridCallbackConfig<T>,
|
|
136
|
+
actionDispatchers?: ActionDispatchers,
|
|
137
|
+
states: JBGridConfigStates,
|
|
138
|
+
}
|
|
139
|
+
export type JBGridConfig<T extends AnyObject> = JBGridConfigInterface<T>;
|
|
140
|
+
export type JBGridCallbacks = {
|
|
141
|
+
onFullscreenChange: (isFullscreen: boolean) => void
|
|
142
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { JBGridI18nConfig, JBGridI18nMessage } from "./Types.js";
|
|
2
|
+
|
|
3
|
+
const i18nMessages:JBGridI18nMessage = {
|
|
4
|
+
EnterPageNumberMessage:"شماره صفحه ای که میخواهید وارد آن شوید را وارد کنید",
|
|
5
|
+
serverErrorText:"متاسفانه در هنگام بارگذاری اطلاعات خطایی رخ داده است",
|
|
6
|
+
serverErrorTitle:"すみません",
|
|
7
|
+
serverErrorRefreshButtonTitle:"تلاش مجدد",
|
|
8
|
+
currentAvailableItem:"تعداد کل ایتم های موجود",
|
|
9
|
+
pageItemCount:"تعداد آیتم در هر صفحه",
|
|
10
|
+
from:"از",
|
|
11
|
+
};
|
|
12
|
+
export const defaultI18n:JBGridI18nConfig = {
|
|
13
|
+
messages: i18nMessages,
|
|
14
|
+
showPersianNumber:false
|
|
15
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jb-grid-react",
|
|
3
|
+
"description": "react mobx jb grid table",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "mohammad javad bathaei",
|
|
7
|
+
"email": "javadbat@gmail.com"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"jb",
|
|
11
|
+
"jb-grid",
|
|
12
|
+
"jb-grid react",
|
|
13
|
+
"grid",
|
|
14
|
+
"table",
|
|
15
|
+
"react table",
|
|
16
|
+
"react",
|
|
17
|
+
"mobx"
|
|
18
|
+
],
|
|
19
|
+
"version": "0.20.2",
|
|
20
|
+
"bugs": "https://github.com/javadbat/jb-grid/issues",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"files": [
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"README.md",
|
|
25
|
+
"lib/",
|
|
26
|
+
"dist/",
|
|
27
|
+
"index.js"
|
|
28
|
+
],
|
|
29
|
+
"main": "index.js",
|
|
30
|
+
"types": "./dist/web-component/jb-grid/react/lib/JBGrid.d.ts",
|
|
31
|
+
"jsnext:main":"index.js",
|
|
32
|
+
"module": "index.js",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git@github.com:javadbat/jb-grid.git"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"mobx":">=6.0.0",
|
|
39
|
+
"mobx-react":">=7.0.0",
|
|
40
|
+
"jb-searchbar":">=2.3.1"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
},
|
|
5
|
+
"include": [
|
|
6
|
+
"../../../common/scripts/**/*.ts",
|
|
7
|
+
"../../../common/hooks/**/*.ts",
|
|
8
|
+
"../../../common/hooks/**/*.js",
|
|
9
|
+
"lib/**/*.ts",
|
|
10
|
+
"lib/**/*.tsx"
|
|
11
|
+
],
|
|
12
|
+
"exclude": [
|
|
13
|
+
"node_modules",
|
|
14
|
+
"**/*.spec.ts",
|
|
15
|
+
"dist",
|
|
16
|
+
],
|
|
17
|
+
"extends":"../../tsconfig-react.json"
|
|
18
|
+
}
|