namirasoft-site-react 1.5.9 → 1.5.11

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.
Files changed (117) hide show
  1. package/dist/components/NSListMenu.d.ts +2 -0
  2. package/dist/components/NSListMenu.js +26 -9
  3. package/dist/components/NSListMenu.js.map +1 -1
  4. package/dist/components/NSReorderDialog.d.ts +30 -0
  5. package/dist/components/NSReorderDialog.js +73 -0
  6. package/dist/components/NSReorderDialog.js.map +1 -0
  7. package/dist/components/NSReorderDialog.module.css +50 -0
  8. package/dist/components/NSSortDialog.d.ts +21 -0
  9. package/dist/components/NSSortDialog.js +67 -0
  10. package/dist/components/NSSortDialog.js.map +1 -0
  11. package/dist/components/NSTable.d.ts +17 -1
  12. package/dist/components/NSTable.js +117 -39
  13. package/dist/components/NSTable.js.map +1 -1
  14. package/dist/components/NSTable.module.css +1 -0
  15. package/dist/formatter/BaseColumnFormatter.d.ts +7 -0
  16. package/dist/formatter/BaseColumnFormatter.js +53 -0
  17. package/dist/formatter/BaseColumnFormatter.js.map +1 -1
  18. package/dist/formatter/BaseURLImageFormatter.d.ts +1 -0
  19. package/dist/formatter/BaseURLImageFormatter.js +3 -0
  20. package/dist/formatter/BaseURLImageFormatter.js.map +1 -1
  21. package/dist/formatter/BooleanFormatter.d.ts +1 -0
  22. package/dist/formatter/BooleanFormatter.js +3 -0
  23. package/dist/formatter/BooleanFormatter.js.map +1 -1
  24. package/dist/formatter/BytesFormatter.d.ts +1 -0
  25. package/dist/formatter/BytesFormatter.js +3 -0
  26. package/dist/formatter/BytesFormatter.js.map +1 -1
  27. package/dist/formatter/DateFormatter.d.ts +1 -0
  28. package/dist/formatter/DateFormatter.js +3 -0
  29. package/dist/formatter/DateFormatter.js.map +1 -1
  30. package/dist/formatter/DateTimeFormatter.d.ts +1 -0
  31. package/dist/formatter/DateTimeFormatter.js +3 -0
  32. package/dist/formatter/DateTimeFormatter.js.map +1 -1
  33. package/dist/formatter/DurationFormatter.d.ts +1 -0
  34. package/dist/formatter/DurationFormatter.js +3 -0
  35. package/dist/formatter/DurationFormatter.js.map +1 -1
  36. package/dist/formatter/EmailFormatter.d.ts +1 -0
  37. package/dist/formatter/EmailFormatter.js +3 -0
  38. package/dist/formatter/EmailFormatter.js.map +1 -1
  39. package/dist/formatter/EnumFormatter.d.ts +1 -0
  40. package/dist/formatter/EnumFormatter.js +3 -0
  41. package/dist/formatter/EnumFormatter.js.map +1 -1
  42. package/dist/formatter/FloatFormatter.d.ts +1 -0
  43. package/dist/formatter/FloatFormatter.js +3 -0
  44. package/dist/formatter/FloatFormatter.js.map +1 -1
  45. package/dist/formatter/IDFormatter.d.ts +1 -0
  46. package/dist/formatter/IDFormatter.js +3 -0
  47. package/dist/formatter/IDFormatter.js.map +1 -1
  48. package/dist/formatter/IPFormatter.d.ts +1 -0
  49. package/dist/formatter/IPFormatter.js +23 -0
  50. package/dist/formatter/IPFormatter.js.map +1 -1
  51. package/dist/formatter/IntegerFormatter.d.ts +1 -0
  52. package/dist/formatter/IntegerFormatter.js +3 -0
  53. package/dist/formatter/IntegerFormatter.js.map +1 -1
  54. package/dist/formatter/JsonFormatter.d.ts +1 -0
  55. package/dist/formatter/JsonFormatter.js +13 -0
  56. package/dist/formatter/JsonFormatter.js.map +1 -1
  57. package/dist/formatter/MoneyFormatter.d.ts +1 -0
  58. package/dist/formatter/MoneyFormatter.js +3 -0
  59. package/dist/formatter/MoneyFormatter.js.map +1 -1
  60. package/dist/formatter/PercentFormatter.d.ts +1 -0
  61. package/dist/formatter/PercentFormatter.js +3 -0
  62. package/dist/formatter/PercentFormatter.js.map +1 -1
  63. package/dist/formatter/PhoneFormatter.d.ts +1 -0
  64. package/dist/formatter/PhoneFormatter.js +3 -0
  65. package/dist/formatter/PhoneFormatter.js.map +1 -1
  66. package/dist/formatter/SensitiveFormatter.d.ts +1 -0
  67. package/dist/formatter/SensitiveFormatter.js +3 -0
  68. package/dist/formatter/SensitiveFormatter.js.map +1 -1
  69. package/dist/formatter/StringFormatter.d.ts +1 -0
  70. package/dist/formatter/StringFormatter.js +3 -0
  71. package/dist/formatter/StringFormatter.js.map +1 -1
  72. package/dist/formatter/TimeFormatter.d.ts +1 -0
  73. package/dist/formatter/TimeFormatter.js +3 -0
  74. package/dist/formatter/TimeFormatter.js.map +1 -1
  75. package/dist/formatter/URLFormatter.d.ts +1 -0
  76. package/dist/formatter/URLFormatter.js +3 -0
  77. package/dist/formatter/URLFormatter.js.map +1 -1
  78. package/dist/formatter/UnknowFormatter.d.ts +1 -0
  79. package/dist/formatter/UnknowFormatter.js +3 -0
  80. package/dist/formatter/UnknowFormatter.js.map +1 -1
  81. package/dist/main.d.ts +2 -1
  82. package/dist/main.js +2 -1
  83. package/dist/main.js.map +1 -1
  84. package/package.json +1 -1
  85. package/src/components/NSListMenu.tsx +43 -12
  86. package/src/components/NSReorderDialog.module.css +50 -0
  87. package/src/components/NSReorderDialog.tsx +155 -0
  88. package/src/components/NSSortDialog.tsx +125 -0
  89. package/src/components/NSTable.module.css +1 -0
  90. package/src/components/NSTable.tsx +207 -68
  91. package/src/formatter/BaseColumnFormatter.ts +68 -0
  92. package/src/formatter/BaseURLImageFormatter.tsx +4 -0
  93. package/src/formatter/BooleanFormatter.ts +4 -0
  94. package/src/formatter/BytesFormatter.tsx +4 -0
  95. package/src/formatter/DateFormatter.ts +4 -0
  96. package/src/formatter/DateTimeFormatter.ts +4 -0
  97. package/src/formatter/DurationFormatter.ts +4 -0
  98. package/src/formatter/EmailFormatter.tsx +4 -0
  99. package/src/formatter/EnumFormatter.ts +4 -0
  100. package/src/formatter/FloatFormatter.ts +4 -0
  101. package/src/formatter/IDFormatter.tsx +5 -1
  102. package/src/formatter/IPFormatter.ts +26 -0
  103. package/src/formatter/IntegerFormatter.ts +4 -0
  104. package/src/formatter/JsonFormatter.tsx +17 -0
  105. package/src/formatter/MoneyFormatter.ts +4 -0
  106. package/src/formatter/PercentFormatter.tsx +4 -0
  107. package/src/formatter/PhoneFormatter.tsx +4 -0
  108. package/src/formatter/SensitiveFormatter.tsx +4 -0
  109. package/src/formatter/StringFormatter.tsx +4 -0
  110. package/src/formatter/TimeFormatter.ts +4 -0
  111. package/src/formatter/URLFormatter.tsx +4 -0
  112. package/src/formatter/UnknowFormatter.ts +4 -0
  113. package/src/main.ts +2 -1
  114. package/dist/routing/NSRoutes.d.ts +0 -11
  115. package/dist/routing/NSRoutes.js +0 -14
  116. package/dist/routing/NSRoutes.js.map +0 -1
  117. package/src/routing/NSRoutes.tsx +0 -31
@@ -10,6 +10,8 @@ import { NSDialogPageSelection } from './NSDialogPageSelection';
10
10
  import { NSLoading } from './NSLoading';
11
11
  import { NSNoData } from './NSNoData';
12
12
  import { NSPagination } from './NSPagination';
13
+ import { NSReorderDialog } from './NSReorderDialog';
14
+ import { NSSortDialog } from './NSSortDialog';
13
15
  import { NSTableChart } from './NSTableChart';
14
16
  import Styles from './NSTable.module.css';
15
17
 
@@ -69,12 +71,22 @@ export interface NSTableProps<RowType> extends IBaseComponentProps
69
71
  onSelectedIDsChanged?: () => void;
70
72
  onSortChanged?: (items: SortItem[] | null) => void;
71
73
  onVisibleColumnsChanged?: (visible_columns: { table: string, column: string }[]) => void;
74
+ sort?: NSTableSortConfig;
72
75
  ui?: {
73
76
  pagination?: boolean;
74
- button_panel?: boolean;
77
+ button_panel?: boolean | NSTableButtonPanelConfig;
75
78
  };
76
79
  }
77
80
 
81
+ export interface NSTableSortConfig
82
+ {
83
+ enabled?: boolean;
84
+ onSort?: (items: SortItem[] | null) => void;
85
+ }
86
+
87
+ export type NSTableButtonKey = "reorder" | "sort" | "fullscreen" | "chart" | "export" | "print" | "refresh";
88
+ export type NSTableButtonPanelConfig = { [key in NSTableButtonKey]?: boolean };
89
+
78
90
  interface NSTableState<RowType>
79
91
  {
80
92
  columns: TableColumnInfo[];
@@ -94,6 +106,8 @@ interface NSTableState<RowType>
94
106
  columnWidths: { [key: string]: number };
95
107
  fullscreen: boolean;
96
108
  showChart: boolean;
109
+ showSortDialog: boolean;
110
+ showReorderDialog: boolean;
97
111
  }
98
112
 
99
113
  enum PageSelectionMode
@@ -129,6 +143,8 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
129
143
  columnWidths: this.loadColumnWidths(),
130
144
  fullscreen: false,
131
145
  showChart: false,
146
+ showSortDialog: false,
147
+ showReorderDialog: false,
132
148
  };
133
149
  this.getSortItemsKey = this.getSortItemsKey.bind(this);
134
150
  this.getSortItems = this.getSortItems.bind(this);
@@ -172,15 +188,58 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
172
188
  }
173
189
  return [];
174
190
  }
191
+ isSortEnabled(): boolean
192
+ {
193
+ return this.props.sort?.enabled ?? true;
194
+ }
195
+ private hasCustomSort(): boolean
196
+ {
197
+ return !!this.props.sort?.onSort;
198
+ }
175
199
  setSortItems(items: SortItem[] | null)
176
200
  {
201
+ if (!this.isSortEnabled())
202
+ return;
177
203
  let storage = new IStorageLocal();
178
204
  storage.set(this.getSortItemsKey(), SortItem.stringify(items));
179
205
  this.setState({ sortItems: items }, () =>
180
206
  {
181
207
  this.props.onSortChanged?.(items);
182
- this.reload(null, null);
208
+ this.props.sort?.onSort?.(items);
209
+ });
210
+ }
211
+ private compareRowsBySortItems(a: RowType, b: RowType, items: SortItem[]): number
212
+ {
213
+ for (let item of items)
214
+ {
215
+ let column = this.state.columns.find(c => c.table.name === item.table.name && c.name === item.column.name);
216
+ let va = (a as any)[item.column.name];
217
+ let vb = (b as any)[item.column.name];
218
+ let cmp = column ? column.formatter.compare(va, vb) : BaseColumnFormatter.smartCompare(va, vb);
219
+ if (cmp !== 0)
220
+ return item.ascending ? cmp : -cmp;
221
+ }
222
+ return 0;
223
+ }
224
+ private sortRowsClient(rows: RowType[]): RowType[]
225
+ {
226
+ let items = this.state.sortItems;
227
+ if (!items || items.length === 0)
228
+ return rows;
229
+ let decorated = rows.map((row, index) => ({ row, index }));
230
+ decorated.sort((x, y) =>
231
+ {
232
+ let cmp = this.compareRowsBySortItems(x.row, y.row, items!);
233
+ return cmp !== 0 ? cmp : x.index - y.index;
183
234
  });
235
+ return decorated.map(d => d.row);
236
+ }
237
+ private getDisplayRows(): RowType[] | null
238
+ {
239
+ let rows = this.state.rows;
240
+ if (!rows || !this.isSortEnabled() || this.hasCustomSort())
241
+ return rows;
242
+ return this.sortRowsClient(rows);
184
243
  }
185
244
  private getVisibleColumnsKey(): string
186
245
  {
@@ -383,13 +442,14 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
383
442
  }
384
443
  getRowsInRange(fromIndex: number, toIndex: number)
385
444
  {
386
- if (!this.state.rows)
445
+ let rows = this.getDisplayRows();
446
+ if (!rows)
387
447
  return [];
388
448
 
389
449
  let start = Math.min(fromIndex, toIndex);
390
450
  let end = Math.max(fromIndex, toIndex);
391
451
 
392
- return this.state.rows?.slice(start, end + 1);
452
+ return rows.slice(start, end + 1);
393
453
  }
394
454
  getSelectedIDs(): string[]
395
455
  {
@@ -420,13 +480,13 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
420
480
  return v === null || v === undefined ? "" : String(v);
421
481
  }).join("");
422
482
  }
423
- private buildChildrenMap(): Map<string, RowType[]> | null
483
+ private buildChildrenMap(rows: RowType[] | null): Map<string, RowType[]> | null
424
484
  {
425
485
  let h = this.props.hierarchy;
426
- if (!h || !this.state.rows)
486
+ if (!h || !rows)
427
487
  return null;
428
488
  let map = new Map<string, RowType[]>();
429
- for (let r of this.state.rows)
489
+ for (let r of rows)
430
490
  {
431
491
  let pid = (r as any)[h.parent];
432
492
  let pidKey = pid === null || pid === undefined ? "" : String(pid);
@@ -590,7 +650,14 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
590
650
  {
591
651
 
592
652
  let show_pagination = this.props.ui?.pagination ?? true;
593
- let show_button_panel = this.props.ui?.button_panel ?? true;
653
+ let button_panel_config = this.props.ui?.button_panel;
654
+ let show_button_panel = button_panel_config !== false;
655
+ let isButtonEnabled = (key: NSTableButtonKey): boolean =>
656
+ {
657
+ if (button_panel_config === undefined || typeof button_panel_config === "boolean")
658
+ return true;
659
+ return button_panel_config[key] ?? true;
660
+ };
594
661
  let getRowAttributes = (row_value: RowType, row_index: number) =>
595
662
  {
596
663
  if (this.props.getRowAttributes)
@@ -656,10 +723,11 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
656
723
  }
657
724
  else
658
725
  {
726
+ let displayRows = this.getDisplayRows() ?? [];
659
727
  let hierarchical = !!this.props.hierarchy;
660
- let childrenMap = this.buildChildrenMap();
728
+ let childrenMap = this.buildChildrenMap(displayRows);
661
729
  let indexOfRow = new Map<RowType, number>();
662
- this.state.rows.forEach((r, i) => indexOfRow.set(r, i));
730
+ displayRows.forEach((r, i) => indexOfRow.set(r, i));
663
731
  let INDENT = 16;
664
732
  let lineColor = "rgba(20, 27, 92, 0.35)";
665
733
  let renderRow = (rowValue: RowType, rowIndex: number, depth: number, parentKey: string, ancestorHasNext: boolean[], isLastChild: boolean): React.ReactNode[] =>
@@ -852,8 +920,8 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
852
920
  {
853
921
  let parentCol = this.props.hierarchy.parent;
854
922
  let ids = new Set<string>();
855
- this.state.rows.forEach(r => ids.add(this.getRowIdValue(r) + "" + this.getGroupKey(r)));
856
- topLevel = this.state.rows.filter(r =>
923
+ displayRows.forEach(r => ids.add(this.getRowIdValue(r) + "" + this.getGroupKey(r)));
924
+ topLevel = displayRows.filter(r =>
857
925
  {
858
926
  let pid = (r as any)[parentCol];
859
927
  let pidKey = pid === null || pid === undefined ? "" : String(pid);
@@ -864,7 +932,7 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
864
932
  }
865
933
  else
866
934
  {
867
- topLevel = this.state.rows;
935
+ topLevel = displayRows;
868
936
  }
869
937
  content = topLevel.flatMap((rowValue, i) => renderRow(rowValue, indexOfRow.get(rowValue) ?? 0, 0, "", [], i === topLevel.length - 1));
870
938
  }
@@ -924,6 +992,30 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
924
992
  onClose={() => this.setState({ pageSelectionMode: PageSelectionMode.Hidden })}
925
993
  />
926
994
  }
995
+ {
996
+ isButtonEnabled("reorder") &&
997
+ <NSButton
998
+ attributes={{ title: 'Reorder' }}
999
+ icon={{ src: "https://static.namirasoft.com/image/concept/grip-vertical/blue.svg", alt: "reorder" }}
1000
+ onClick={{
1001
+ action: () => this.setState({ showReorderDialog: true }),
1002
+ showLoading: false
1003
+ }}
1004
+ classList={[Styles.ns_table_button]}
1005
+ />
1006
+ }
1007
+ {
1008
+ isButtonEnabled("sort") && this.isSortEnabled() &&
1009
+ <NSButton
1010
+ attributes={{ title: 'Sort' }}
1011
+ icon={{ src: "https://static.namirasoft.com/image/concept/arrow-down-short-wide/blue.svg", alt: "sort" }}
1012
+ onClick={{
1013
+ action: () => this.setState({ showSortDialog: true }),
1014
+ showLoading: false
1015
+ }}
1016
+ classList={[Styles.ns_table_button]}
1017
+ />
1018
+ }
927
1019
  {
928
1020
  this.props.buttons?.map((button, index) => (
929
1021
  <NSButton
@@ -933,60 +1025,75 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
933
1025
  />
934
1026
  ))
935
1027
  }
936
- <NSButton
937
- icon={{ src: `https://static.namirasoft.com/image/concept/full-screen/blue.svg` }}
938
- classList={[Styles.ns_table_button, Styles.ns_fullscreen_button]}
939
- onClick={{
940
- action: this.toggleFullscreen,
941
- showLoading: false
942
- }}
943
- attributes={{ title: this.state.fullscreen ? 'Minimize' : 'Maximize' }}
944
- />
945
- <NSButton
946
- attributes={{ title: 'Chart' }}
947
- icon={{ src: "https://static.namirasoft.com/image/concept/chart/blue.svg" }}
948
- onClick={{
949
- action: () =>
950
- {
951
- if (!this.state.rows || this.state.rows.length === 0)
952
- this.showModal(" ", "There is no data to display as a chart.");
953
- else
954
- this.setState({ showChart: true });
955
- },
956
- showLoading: false
957
- }}
958
- classList={[Styles.ns_table_button]}
959
- />
960
- <NSButton
961
- icon={{ src: "https://static.namirasoft.com/image/concept/export/blue.svg" }}
962
- onClick={{
963
- action: () => { this.setState({ pageSelectionMode: PageSelectionMode.CSV }); },
964
- showLoading: false
965
- }}
966
- classList={[Styles.ns_table_button]}
967
- attributes={{ title: 'Export' }}
968
- />
969
- <NSButton
970
- attributes={{ title: 'Print' }}
971
- icon={{ src: "https://static.namirasoft.com/image/concept/print/blue.svg" }}
972
- onClick={{
973
- action: () => { this.setState({ pageSelectionMode: PageSelectionMode.Print }); },
974
- showLoading: false
975
- }}
976
- classList={[Styles.ns_table_button]}
977
- />
978
- <NSButton
979
- attributes={{ title: 'Refresh' }}
980
- icon={{ src: "https://static.namirasoft.com/image/concept/refresh/blue.svg" }}
981
- onClick={{
982
- action: async (onFinished) =>
983
- {
984
- this.reload(null, null).then(onFinished).catch(onFinished);
985
- }
986
- }}
987
- classList={[Styles.ns_table_button]}
988
- style={{ border: '1px solid rgb(3, 119, 255)' }}
989
- />
1028
+ {
1029
+ isButtonEnabled("fullscreen") &&
1030
+ <NSButton
1031
+ icon={{ src: `https://static.namirasoft.com/image/concept/full-screen/blue.svg` }}
1032
+ classList={[Styles.ns_table_button, Styles.ns_fullscreen_button]}
1033
+ onClick={{
1034
+ action: this.toggleFullscreen,
1035
+ showLoading: false
1036
+ }}
1037
+ attributes={{ title: this.state.fullscreen ? 'Minimize' : 'Maximize' }}
1038
+ />
1039
+ }
1040
+ {
1041
+ isButtonEnabled("chart") &&
1042
+ <NSButton
1043
+ attributes={{ title: 'Chart' }}
1044
+ icon={{ src: "https://static.namirasoft.com/image/concept/chart/blue.svg" }}
1045
+ onClick={{
1046
+ action: () =>
1047
+ {
1048
+ if (!this.state.rows || this.state.rows.length === 0)
1049
+ this.showModal(" ", "There is no data to display as a chart.");
1050
+ else
1051
+ this.setState({ showChart: true });
1052
+ },
1053
+ showLoading: false
1054
+ }}
1055
+ classList={[Styles.ns_table_button]}
1056
+ />
1057
+ }
1058
+ {
1059
+ isButtonEnabled("export") &&
1060
+ <NSButton
1061
+ icon={{ src: "https://static.namirasoft.com/image/concept/export/blue.svg" }}
1062
+ onClick={{
1063
+ action: () => { this.setState({ pageSelectionMode: PageSelectionMode.CSV }); },
1064
+ showLoading: false
1065
+ }}
1066
+ classList={[Styles.ns_table_button]}
1067
+ attributes={{ title: 'Export' }}
1068
+ />
1069
+ }
1070
+ {
1071
+ isButtonEnabled("print") &&
1072
+ <NSButton
1073
+ attributes={{ title: 'Print' }}
1074
+ icon={{ src: "https://static.namirasoft.com/image/concept/print/blue.svg" }}
1075
+ onClick={{
1076
+ action: () => { this.setState({ pageSelectionMode: PageSelectionMode.Print }); },
1077
+ showLoading: false
1078
+ }}
1079
+ classList={[Styles.ns_table_button]}
1080
+ />
1081
+ }
1082
+ {
1083
+ isButtonEnabled("refresh") &&
1084
+ <NSButton
1085
+ attributes={{ title: 'Refresh' }}
1086
+ icon={{ src: "https://static.namirasoft.com/image/concept/refresh/blue.svg" }}
1087
+ onClick={{
1088
+ action: async (onFinished) =>
1089
+ {
1090
+ this.reload(null, null).then(onFinished).catch(onFinished);
1091
+ }
1092
+ }}
1093
+ classList={[Styles.ns_table_button]}
1094
+ style={{ border: '1px solid rgb(3, 119, 255)' }}
1095
+ />
1096
+ }
990
1097
  </div>
991
1098
  );
992
1099
 
@@ -1005,7 +1112,7 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
1005
1112
  }
1006
1113
  {
1007
1114
  show_pagination &&
1008
- <NSPagination ref={this.NSPagination} totalItems={this.state.totalItems} onPageChange={this.onPageChange} style={{ marginRight: "auto" }} />
1115
+ <NSPagination ref={this.NSPagination} totalItems={this.state.totalItems} onPageChange={this.onPageChange} />
1009
1116
  }
1010
1117
  {buttonPanel}
1011
1118
  </div>
@@ -1048,6 +1155,8 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
1048
1155
  {
1049
1156
  if ((e.target as HTMLElement).closest('[data-resize-handle]'))
1050
1157
  return;
1158
+ if (!this.isSortEnabled())
1159
+ return;
1051
1160
  let t = new BaseMetaTable(null, column.table.name, column.table.text);
1052
1161
  let c = new BaseMetaColumn(t, column.name, column.text, "", false);
1053
1162
  let items = this.getSortItems();
@@ -1104,6 +1213,36 @@ export class NSTable<RowType> extends Component<NSTableProps<RowType>, NSTableSt
1104
1213
  onClose={() => this.setState({ showChart: false })}
1105
1214
  />
1106
1215
  }
1216
+ {
1217
+ this.state.showSortDialog && this.isSortEnabled() &&
1218
+ <NSSortDialog
1219
+ sorts={this.getSortItems()}
1220
+ getColumns={() => this.getColumns(null)}
1221
+ onCancel={() => this.setState({ showSortDialog: false })}
1222
+ onApply={sorts =>
1223
+ {
1224
+ this.setSortItems(sorts);
1225
+ this.setState({ showSortDialog: false });
1226
+ }}
1227
+ />
1228
+ }
1229
+ {
1230
+ this.state.showReorderDialog &&
1231
+ <NSReorderDialog
1232
+ visibleColumnKeys={this.getVisibleColumns().map(c => c.table + "." + c.column)}
1233
+ getColumns={() => this.getColumns(null)}
1234
+ onCancel={() => this.setState({ showReorderDialog: false })}
1235
+ onApply={orderedKeys =>
1236
+ {
1237
+ const all = this.getColumns(null);
1238
+ const cs = orderedKeys
1239
+ .map(k => all.find(c => c.table.name + "." + c.name === k))
1240
+ .filter((c): c is TableColumnInfo => !!c);
1241
+ this.setVisibleColumns(cs.map(c => ({ table: c.table.name, column: c.name })));
1242
+ this.setState({ showReorderDialog: false });
1243
+ }}
1244
+ />
1245
+ }
1107
1246
  </div>
1108
1247
  );
1109
1248
  }
@@ -6,6 +6,74 @@ export abstract class BaseColumnFormatter
6
6
  public width: string = "100px";
7
7
  public onclick_overrided: boolean = false;
8
8
  abstract format(value: any, column: TableColumnInfo, row: TableRowInfo<any>, printable: boolean): any;
9
+
10
+ compare(a: any, b: any): number
11
+ {
12
+ return BaseColumnFormatter.smartCompare(a, b);
13
+ }
14
+
15
+ static isEmpty(v: any): boolean
16
+ {
17
+ return v === null || v === undefined || v === "";
18
+ }
19
+
20
+ static compareEmpty(a: any, b: any): number | null
21
+ {
22
+ const ae = BaseColumnFormatter.isEmpty(a);
23
+ const be = BaseColumnFormatter.isEmpty(b);
24
+ if (ae && be)
25
+ return 0;
26
+ if (ae)
27
+ return 1;
28
+ if (be)
29
+ return -1;
30
+ return null;
31
+ }
32
+
33
+ static numberCompare(a: any, b: any): number
34
+ {
35
+ const e = BaseColumnFormatter.compareEmpty(a, b);
36
+ if (e !== null)
37
+ return e;
38
+ const na = Number(a);
39
+ const nb = Number(b);
40
+ if (isNaN(na) || isNaN(nb))
41
+ return BaseColumnFormatter.stringCompare(a, b);
42
+ return na - nb;
43
+ }
44
+
45
+ static dateCompare(a: any, b: any): number
46
+ {
47
+ const e = BaseColumnFormatter.compareEmpty(a, b);
48
+ if (e !== null)
49
+ return e;
50
+ const ta = new Date(a).getTime();
51
+ const tb = new Date(b).getTime();
52
+ if (isNaN(ta) || isNaN(tb))
53
+ return BaseColumnFormatter.stringCompare(a, b);
54
+ return ta - tb;
55
+ }
56
+
57
+ static stringCompare(a: any, b: any): number
58
+ {
59
+ const e = BaseColumnFormatter.compareEmpty(a, b);
60
+ if (e !== null)
61
+ return e;
62
+ return String(a).localeCompare(String(b));
63
+ }
64
+
65
+ static smartCompare(a: any, b: any): number
66
+ {
67
+ const e = BaseColumnFormatter.compareEmpty(a, b);
68
+ if (e !== null)
69
+ return e;
70
+ const na = Number(a);
71
+ const nb = Number(b);
72
+ if (!isNaN(na) && !isNaN(nb))
73
+ return na - nb;
74
+ return String(a).localeCompare(String(b));
75
+ }
76
+
9
77
  static getColumn(column: BaseMetaColumn, formatter: BaseColumnFormatter, group?: string, defaults?: { hidden?: boolean }): TableColumnInfo
10
78
  {
11
79
  return {
@@ -31,4 +31,8 @@ export class BaseURLImageFormatter extends BaseColumnFormatter
31
31
  }
32
32
  return value.text;
33
33
  }
34
+ override compare(a: any, b: any): number
35
+ {
36
+ return BaseColumnFormatter.stringCompare(a?.text, b?.text);
37
+ }
34
38
  }
@@ -20,4 +20,8 @@ export class BooleanFormatter extends BaseColumnFormatter
20
20
  return this.false_value;
21
21
  return value;
22
22
  }
23
+ override compare(a: any, b: any): number
24
+ {
25
+ return BaseColumnFormatter.numberCompare(a, b);
26
+ }
23
27
  }
@@ -13,4 +13,8 @@ export class BytesFormatter extends BaseColumnFormatter
13
13
  {
14
14
  return ByteOperation.format(bytes, ByteUnit.B);
15
15
  }
16
+ override compare(a: any, b: any): number
17
+ {
18
+ return BaseColumnFormatter.numberCompare(a, b);
19
+ }
16
20
  }
@@ -20,4 +20,8 @@ export class DateFormatter extends BaseColumnFormatter
20
20
  }
21
21
  return value;
22
22
  }
23
+ override compare(a: any, b: any): number
24
+ {
25
+ return BaseColumnFormatter.dateCompare(a, b);
26
+ }
23
27
  }
@@ -20,4 +20,8 @@ export class DateTimeFormatter extends BaseColumnFormatter
20
20
  }
21
21
  return value;
22
22
  }
23
+ override compare(a: any, b: any): number
24
+ {
25
+ return BaseColumnFormatter.dateCompare(a, b);
26
+ }
23
27
  }
@@ -11,4 +11,8 @@ export class DurationFormatter extends BaseColumnFormatter
11
11
  {
12
12
  return value;
13
13
  }
14
+ override compare(a: any, b: any): number
15
+ {
16
+ return BaseColumnFormatter.numberCompare(a, b);
17
+ }
14
18
  }
@@ -19,4 +19,8 @@ export class EmailFormatter extends BaseColumnFormatter
19
19
  image: { url: "https://static.namirasoft.com/image/concept/type/email.png", alt: "phone" }
20
20
  }, column, row, printable);
21
21
  }
22
+ override compare(a: any, b: any): number
23
+ {
24
+ return BaseColumnFormatter.stringCompare(a, b);
25
+ }
22
26
  }
@@ -11,4 +11,8 @@ export class EnumFormatter extends BaseColumnFormatter
11
11
  {
12
12
  return value;
13
13
  }
14
+ override compare(a: any, b: any): number
15
+ {
16
+ return BaseColumnFormatter.smartCompare(a, b);
17
+ }
14
18
  }
@@ -21,4 +21,8 @@ export class FloatFormatter extends BaseColumnFormatter
21
21
  }
22
22
  return value;
23
23
  }
24
+ override compare(a: any, b: any): number
25
+ {
26
+ return BaseColumnFormatter.numberCompare(a, b);
27
+ }
24
28
  }
@@ -46,9 +46,13 @@ export class IDFormatter extends BaseColumnFormatter
46
46
  />
47
47
  ));
48
48
  }
49
- }
49
+ }
50
50
  catch (error) { }
51
51
 
52
52
  return value;
53
53
  }
54
+ override compare(a: any, b: any): number
55
+ {
56
+ return BaseColumnFormatter.smartCompare(a, b);
57
+ }
54
58
  }
@@ -11,4 +11,30 @@ export class IPFormatter extends BaseColumnFormatter
11
11
  {
12
12
  return value;
13
13
  }
14
+ override compare(a: any, b: any): number
15
+ {
16
+ const e = BaseColumnFormatter.compareEmpty(a, b);
17
+ if (e !== null)
18
+ return e;
19
+ const parse = (v: any): number[] | null =>
20
+ {
21
+ const parts = String(v).split(".");
22
+ if (parts.length !== 4)
23
+ return null;
24
+ const nums = parts.map(p => Number(p));
25
+ if (nums.some(n => isNaN(n)))
26
+ return null;
27
+ return nums;
28
+ };
29
+ const na = parse(a);
30
+ const nb = parse(b);
31
+ if (na && nb)
32
+ {
33
+ for (let i = 0; i < 4; i++)
34
+ if (na[i] !== nb[i])
35
+ return na[i] - nb[i];
36
+ return 0;
37
+ }
38
+ return BaseColumnFormatter.stringCompare(a, b);
39
+ }
14
40
  }
@@ -21,4 +21,8 @@ export class IntegerFormatter extends BaseColumnFormatter
21
21
  }
22
22
  return value;
23
23
  }
24
+ override compare(a: any, b: any): number
25
+ {
26
+ return BaseColumnFormatter.numberCompare(a, b);
27
+ }
24
28
  }
@@ -56,4 +56,21 @@ export class JsonFormatter extends BaseColumnFormatter
56
56
  return value;
57
57
  }
58
58
  }
59
+ override compare(a: any, b: any): number
60
+ {
61
+ const toText = (v: any) => typeof v === "string" ?
62
+ v :
63
+ (() =>
64
+ {
65
+ try
66
+ {
67
+ return JSON.stringify(v);
68
+ }
69
+ catch
70
+ {
71
+ return String(v);
72
+ }
73
+ })();
74
+ return BaseColumnFormatter.stringCompare(toText(a), toText(b));
75
+ }
59
76
  }
@@ -33,4 +33,8 @@ export class MoneyFormatter extends BaseColumnFormatter
33
33
  }
34
34
  return value;
35
35
  }
36
+ override compare(a: any, b: any): number
37
+ {
38
+ return BaseColumnFormatter.numberCompare(a, b);
39
+ }
36
40
  }
@@ -37,4 +37,8 @@ export class PercentFormatter extends BaseColumnFormatter
37
37
 
38
38
  return value;
39
39
  }
40
+ override compare(a: any, b: any): number
41
+ {
42
+ return BaseColumnFormatter.numberCompare(a, b);
43
+ }
40
44
  }