@opencorestack/opengridx 2.0.4 → 3.0.0

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 (221) hide show
  1. package/CHANGELOG.md +408 -3
  2. package/README.md +93 -72
  3. package/dist/index.d.ts +611 -121
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +7992 -5650
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +17 -23
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +348 -161
  10. package/docs/README.md +6 -1
  11. package/docs/architecture/datagrid-orchestration.md +81 -0
  12. package/docs/architecture/grid-row-meta.md +102 -0
  13. package/docs/architecture/use-grid-columns.md +128 -0
  14. package/docs/architecture/use-grid-controlled-state.md +157 -0
  15. package/docs/architecture/use-grid-row-pipeline.md +120 -0
  16. package/docs/architecture/use-grid-scroll-sync.md +111 -0
  17. package/docs/architecture/use-grid-state-snapshot.md +90 -0
  18. package/docs/architecture/use-grid-virtualization.md +115 -0
  19. package/docs/architecture/use-grid-visible-rows.md +85 -0
  20. package/docs/assets/banner-alt.png +0 -0
  21. package/docs/assets/banner.png +0 -0
  22. package/docs/assets/hero.png +0 -0
  23. package/docs/assets/logo.png +0 -0
  24. package/docs/components/aggregation-footer.md +15 -10
  25. package/docs/components/cell.md +16 -6
  26. package/docs/components/column-group-header.md +55 -28
  27. package/docs/components/column-resize.md +17 -13
  28. package/docs/components/column-visibility.md +45 -22
  29. package/docs/components/datagrid.md +14 -12
  30. package/docs/components/empty-state.md +2 -1
  31. package/docs/components/error-overlay.md +12 -7
  32. package/docs/components/filter-panel.md +28 -8
  33. package/docs/components/header.md +28 -9
  34. package/docs/components/pagination.md +14 -8
  35. package/docs/components/row.md +21 -5
  36. package/docs/components/toolbar.md +27 -9
  37. package/docs/components/tooltip.md +16 -7
  38. package/docs/customization/slots-api.md +100 -36
  39. package/docs/customization/theming.md +66 -18
  40. package/docs/features/aggregation-pivot.md +75 -9
  41. package/docs/features/cell-spanning.md +80 -125
  42. package/docs/features/clipboard.md +41 -16
  43. package/docs/features/custom-pagination.md +38 -18
  44. package/docs/features/data-source.md +36 -13
  45. package/docs/features/editing-reordering.md +125 -16
  46. package/docs/features/export-guide.md +147 -41
  47. package/docs/features/filtering.md +44 -6
  48. package/docs/features/infinite-scroll.md +54 -46
  49. package/docs/features/keyboard-navigation.md +83 -0
  50. package/docs/features/list-view.md +58 -5
  51. package/docs/features/loading-states.md +18 -20
  52. package/docs/features/master-detail.md +18 -5
  53. package/docs/features/pdf-export.md +56 -19
  54. package/docs/features/pinning.md +23 -5
  55. package/docs/features/selection.md +50 -10
  56. package/docs/features/sorting-pagination.md +66 -14
  57. package/docs/features/state-persistence.md +78 -10
  58. package/docs/features/toolbar-customization.md +26 -17
  59. package/docs/features/tree-data-grouping.md +44 -9
  60. package/docs/features/virtualization.md +64 -15
  61. package/docs/migration/v1-to-v2.md +115 -0
  62. package/docs/migration/v2-to-v3.md +1222 -0
  63. package/docs/performance.md +124 -0
  64. package/docs/roadmap.md +16 -10
  65. package/lib/components/Cell/Cell.css +13 -1
  66. package/lib/components/Cell/Cell.tsx +130 -57
  67. package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
  68. package/lib/components/Cell/GridEditInputCell.tsx +114 -41
  69. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
  70. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +11 -0
  71. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
  72. package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
  73. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
  74. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
  75. package/lib/components/DataGrid/DataGrid.css +84 -12
  76. package/lib/components/DataGrid/DataGrid.tsx +606 -872
  77. package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
  78. package/lib/components/DataGrid/GridEmptyState.tsx +7 -1
  79. package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
  80. package/lib/components/DataGrid/GridListView.tsx +166 -23
  81. package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
  82. package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
  83. package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
  84. package/lib/components/DataGrid/GridPinnedRows.tsx +65 -11
  85. package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
  86. package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
  87. package/lib/components/DataGrid/GridVirtualRows.tsx +48 -25
  88. package/lib/components/DetailPanel/DetailPanel.css +0 -6
  89. package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
  90. package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
  91. package/lib/components/FilterPanel/FilterPanel.css +13 -2
  92. package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
  93. package/lib/components/Header/ColumnMenu.tsx +91 -39
  94. package/lib/components/Header/Header.css +3 -3
  95. package/lib/components/Header/Header.tsx +179 -160
  96. package/lib/components/ListView/ListView.css +25 -0
  97. package/lib/components/ListView/ListViewRow.tsx +79 -10
  98. package/lib/components/Pagination/Pagination.tsx +15 -7
  99. package/lib/components/Row/Row.tsx +185 -75
  100. package/lib/components/Toolbar/GlobalSearch.css +6 -3
  101. package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
  102. package/lib/components/Toolbar/GridToolbar.css +12 -11
  103. package/lib/components/Toolbar/GridToolbar.tsx +133 -89
  104. package/lib/components/Toolbar/PivotPanel.tsx +89 -49
  105. package/lib/components/Tooltip/Tooltip.css +20 -0
  106. package/lib/components/Tooltip/Tooltip.tsx +68 -35
  107. package/lib/components/ui/Button.tsx +3 -0
  108. package/lib/components/ui/Checkbox.tsx +2 -1
  109. package/lib/components/ui/ExpandIcon.css +0 -9
  110. package/lib/components/ui/ExpandIcon.tsx +4 -1
  111. package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
  112. package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
  113. package/lib/hooks/core/useDataGrid.ts +115 -173
  114. package/lib/hooks/core/useGridAggregationApi.ts +72 -0
  115. package/lib/hooks/core/useGridApiMethods.ts +115 -0
  116. package/lib/hooks/core/useGridApiRef.ts +15 -3
  117. package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
  118. package/lib/hooks/core/useGridAriaRows.ts +38 -0
  119. package/lib/hooks/core/useGridClipboardApi.ts +38 -0
  120. package/lib/hooks/core/useGridColumnLookup.ts +30 -0
  121. package/lib/hooks/core/useGridColumns.tsx +284 -76
  122. package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
  123. package/lib/hooks/core/useGridControlledState.ts +99 -6
  124. package/lib/hooks/core/useGridDetailPanel.ts +51 -0
  125. package/lib/hooks/core/useGridDevWarnings.ts +123 -0
  126. package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
  127. package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
  128. package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
  129. package/lib/hooks/core/useGridHierarchy.ts +81 -0
  130. package/lib/hooks/core/useGridKeyboardNavigation.ts +452 -250
  131. package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
  132. package/lib/hooks/core/useGridPageCorrection.ts +55 -0
  133. package/lib/hooks/core/useGridRowIdOf.ts +32 -0
  134. package/lib/hooks/core/useGridRowInteractions.ts +81 -0
  135. package/lib/hooks/core/useGridRowPipeline.ts +69 -39
  136. package/lib/hooks/core/useGridRowSelection.ts +91 -0
  137. package/lib/hooks/core/useGridScrollSync.ts +80 -32
  138. package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
  139. package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
  140. package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
  141. package/lib/hooks/core/useGridToolbarProps.ts +97 -0
  142. package/lib/hooks/core/useGridViewport.ts +38 -0
  143. package/lib/hooks/core/useGridViewportSize.ts +26 -0
  144. package/lib/hooks/core/useGridVisibleRows.ts +20 -7
  145. package/lib/hooks/core/useLayout.ts +215 -195
  146. package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
  147. package/lib/hooks/core/useStableColumns.ts +34 -0
  148. package/lib/hooks/features/useAggregation.ts +86 -90
  149. package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
  150. package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
  151. package/lib/hooks/features/useGridClipboard.ts +133 -73
  152. package/lib/hooks/features/useGridDataSource.ts +354 -128
  153. package/lib/hooks/features/useGridEditing.ts +211 -51
  154. package/lib/hooks/features/useGridPivot.ts +92 -0
  155. package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
  156. package/lib/hooks/features/useGridSpanning.ts +410 -306
  157. package/lib/hooks/features/usePivot.ts +0 -0
  158. package/lib/hooks/features/useServerTreeChildren.ts +50 -0
  159. package/lib/hooks/useColumnReorder.ts +38 -44
  160. package/lib/hooks/useRowGrouping.ts +335 -214
  161. package/lib/hooks/useRowReorder.ts +62 -31
  162. package/lib/hooks/useTreeData.ts +330 -262
  163. package/lib/index.ts +30 -2
  164. package/lib/state/index.ts +1 -0
  165. package/lib/state/types.ts +7 -1
  166. package/lib/state/useGridStateStorage.ts +88 -29
  167. package/lib/theme/DataGridThemeProvider.tsx +143 -38
  168. package/lib/theme/gridThemeContext.ts +5 -0
  169. package/lib/theme/index.ts +5 -0
  170. package/lib/theme/presets.ts +5 -4
  171. package/lib/theme/types.ts +40 -2
  172. package/lib/types/index.ts +311 -108
  173. package/lib/utils/aggregation/index.ts +157 -0
  174. package/lib/utils/aria/index.ts +40 -0
  175. package/lib/utils/columnGroups.ts +110 -0
  176. package/lib/utils/columnLookup.ts +33 -0
  177. package/lib/utils/columnWidth.ts +22 -0
  178. package/lib/utils/dataSource/index.ts +46 -0
  179. package/lib/utils/detailPanel.ts +66 -0
  180. package/lib/utils/dragData.ts +44 -0
  181. package/lib/utils/editing/index.ts +30 -0
  182. package/lib/utils/export/exportShared.ts +197 -0
  183. package/lib/utils/export/exportToExcelAdvanced.ts +344 -121
  184. package/lib/utils/export/exportToPdf.ts +143 -92
  185. package/lib/utils/export/groupLabel.ts +14 -0
  186. package/lib/utils/export/index.ts +278 -272
  187. package/lib/utils/export/pdfText.ts +50 -0
  188. package/lib/utils/filtering/index.ts +277 -148
  189. package/lib/utils/focus/index.ts +115 -0
  190. package/lib/utils/gridApiRows.ts +35 -0
  191. package/lib/utils/gridModes.ts +77 -0
  192. package/lib/utils/grouping/groupedExportRows.ts +91 -0
  193. package/lib/utils/pagination/index.ts +56 -0
  194. package/lib/utils/pinning/index.ts +77 -33
  195. package/lib/utils/pivot/index.ts +368 -0
  196. package/lib/utils/scroll/index.ts +78 -0
  197. package/lib/utils/scroll/scrollEnd.ts +18 -0
  198. package/lib/utils/sorting/index.ts +154 -30
  199. package/lib/utils/syntheticRows.ts +12 -0
  200. package/lib/utils/values.ts +206 -0
  201. package/lib/utils/viewport.ts +14 -0
  202. package/llms.txt +272 -166
  203. package/package.json +18 -6
  204. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
  205. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
  206. package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
  207. package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
  208. package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
  209. package/lib/components/Header/Header.test.tsx +0 -135
  210. package/lib/components/Pagination/Pagination.test.tsx +0 -64
  211. package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
  212. package/lib/hooks/core/useGridControlledState.test.ts +0 -86
  213. package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
  214. package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
  215. package/lib/hooks/features/useAggregation.test.ts +0 -175
  216. package/lib/hooks/features/usePivot.test.ts +0 -202
  217. package/lib/hooks/useRowGrouping.test.ts +0 -137
  218. package/lib/utils/export/exportToPdf.test.ts +0 -120
  219. package/lib/utils/export/groupedExport.test.ts +0 -144
  220. package/lib/utils/filtering/filtering.test.ts +0 -178
  221. package/lib/utils/sorting/sorting.test.ts +0 -88
@@ -1,39 +1,78 @@
1
1
  import React from 'react';
2
- import type { GridColDef, GridRowModel, GridRowId, GridListViewColDef, GridRenderCellParams } from '../../types';
2
+ import { formatValueByType, getCellValue, getFormattedValue } from '../../utils/values';
3
+ import { ExpandIcon } from '../ui/ExpandIcon';
4
+ import { CellErrorBoundary } from '../Cell/CellErrorBoundary';
5
+ import { isSyntheticRowId } from '../../utils/syntheticRows';
6
+ import type { GridColDef, GridRowModel, GridRowId, GridRowMeta, GridListViewColDef, GridRenderCellParams } from '../../types';
3
7
 
4
8
  export interface ListViewRowProps<R extends GridRowModel = GridRowModel> {
5
9
  row: R;
10
+ /** The row's id (from getRowId). Defaults to `row.id`. */
11
+ rowId?: GridRowId;
12
+ /** Index of the row among the rendered rows; passed to renderCell and the row callbacks. */
6
13
  rowIndex: number;
14
+ /** 1-based position of the row in the whole data set. Defaults to rowIndex + 1. */
15
+ ariaRowIndex?: number;
7
16
  listViewColumn: GridListViewColDef<R>;
17
+ /** The grid column with the list field, if any: its valueGetter / valueFormatter produce value and formattedValue. */
18
+ gridColumn?: GridColDef<R>;
19
+ /** Tree-data / row-grouping metadata for the row. */
20
+ rowMeta?: GridRowMeta;
8
21
  isSelected?: boolean;
9
22
  checkboxSelection?: boolean;
10
23
  rowHeight?: number;
11
24
  onRowClick?: (row: R) => void;
25
+ onRowDoubleClick?: (row: R) => void;
12
26
  onSelectionChange?: (rowId: GridRowId, isSelected: boolean) => void;
27
+ /** Roving tab stop: 0 for the one row the list's Tab stop lands on, -1 for the others. */
28
+ tabIndex?: number;
29
+ /** Called when the row element itself receives focus. */
30
+ onFocus?: (rowIndex: number) => void;
31
+ /** Expands / collapses a row with children. When set, hierarchy rows get an expand chevron. */
32
+ onToggleExpansion?: (rowId: GridRowId) => void;
13
33
  }
14
34
 
15
35
  export function ListViewRow<R extends GridRowModel = GridRowModel>({
16
36
  row,
37
+ rowId,
17
38
  rowIndex,
39
+ ariaRowIndex,
18
40
  listViewColumn,
41
+ gridColumn,
42
+ rowMeta,
19
43
  isSelected = false,
20
44
  checkboxSelection = false,
21
45
  rowHeight,
22
46
  onRowClick,
47
+ onRowDoubleClick,
23
48
  onSelectionChange,
49
+ tabIndex = -1,
50
+ onFocus,
51
+ onToggleExpansion,
24
52
  }: ListViewRowProps<R>) {
53
+ const id = rowId ?? row.id;
54
+ const field = listViewColumn.field;
55
+ // Same value and formatting as a grid cell for this field (Cell.tsx). A valueGetter or
56
+ // valueFormatter that throws reads as undefined / the unformatted value instead of crashing.
57
+ const value = getCellValue(row, field, gridColumn);
58
+ const formattedValue = getFormattedValue(row, field, value, gridColumn) ?? formatValueByType(value, gridColumn);
59
+ // Row-grouping headers, subtotals and auto-created tree parents are not data rows.
60
+ const isSyntheticRow = rowMeta?.isGroupRow === true || isSyntheticRowId(id);
61
+
25
62
  const params: GridRenderCellParams<R> = {
26
63
  row,
27
- value: undefined,
28
- field: listViewColumn.field,
29
- colDef: { field: listViewColumn.field } as unknown as GridColDef<R>,
64
+ value,
65
+ formattedValue,
66
+ field,
67
+ colDef: gridColumn ?? listViewColumn,
30
68
  rowIndex,
31
69
  colIndex: 0,
70
+ rowMeta,
32
71
  };
33
72
 
34
73
  const handleCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
35
74
  e.stopPropagation();
36
- onSelectionChange?.(row.id, e.target.checked);
75
+ onSelectionChange?.(id, e.target.checked);
37
76
  };
38
77
 
39
78
  return (
@@ -41,22 +80,52 @@ export function ListViewRow<R extends GridRowModel = GridRowModel>({
41
80
  className={`ogx-list-view__row${isSelected ? ' ogx-list-view__row--selected' : ''}`}
42
81
  style={rowHeight ? { minHeight: rowHeight } : undefined}
43
82
  onClick={() => onRowClick?.(row)}
83
+ onDoubleClick={onRowDoubleClick ? () => onRowDoubleClick(row) : undefined}
44
84
  role="row"
45
- aria-rowindex={rowIndex + 2}
85
+ aria-rowindex={ariaRowIndex ?? rowIndex + 1}
46
86
  aria-selected={isSelected}
87
+ aria-level={rowMeta?.treeDepth !== undefined ? rowMeta.treeDepth + 1 : undefined}
88
+ aria-expanded={rowMeta?.hasChildren ? rowMeta.isExpanded === true : undefined}
89
+ data-rowindex={rowIndex}
90
+ tabIndex={tabIndex}
91
+ onFocus={onFocus ? (e) => { if (e.target === e.currentTarget) onFocus(rowIndex); } : undefined}
47
92
  >
48
93
  {checkboxSelection && (
49
94
  <div className="ogx-list-view__checkbox" onClick={(e) => e.stopPropagation()}>
50
- <input
95
+ {!isSyntheticRow && <input
51
96
  type="checkbox"
52
97
  checked={isSelected}
53
98
  onChange={handleCheckboxChange}
54
- aria-label={`Select row ${row.id}`}
55
- />
99
+ aria-label={`Select row ${id}`}
100
+ // Not a Tab stop of its own: the list is one Tab stop, Shift+Space selects the focused row.
101
+ tabIndex={-1}
102
+ />}
103
+ </div>
104
+ )}
105
+ {rowMeta && onToggleExpansion && (
106
+ // Tree data / row grouping: indentation by depth, and a chevron on rows with children
107
+ // (not a Tab stop: Enter or Alt+Arrow toggle the focused row).
108
+ <div
109
+ className="ogx-list-view__expand"
110
+ style={{ paddingLeft: (rowMeta.treeDepth ?? 0) * 16 }}
111
+ onClick={(e) => e.stopPropagation()}
112
+ onDoubleClick={(e) => e.stopPropagation()}
113
+ >
114
+ {rowMeta.hasChildren && (
115
+ <ExpandIcon isExpanded={rowMeta.isExpanded === true} onClick={() => onToggleExpansion(id)} tabIndex={-1} />
116
+ )}
56
117
  </div>
57
118
  )}
58
119
  <div className="ogx-list-view__cell" role="gridcell">
59
- {listViewColumn.renderCell(params)}
120
+ <CellErrorBoundary
121
+ field={field}
122
+ resetKey={row}
123
+ renderFn={() => {
124
+ const content = listViewColumn.renderCell(params);
125
+ // A group row whose renderCell has nothing for it still shows its label.
126
+ return content === undefined && isSyntheticRow ? rowMeta?.groupLabel : content;
127
+ }}
128
+ />
60
129
  </div>
61
130
  </div>
62
131
  );
@@ -1,6 +1,7 @@
1
1
 
2
2
  import React, { useId } from 'react';
3
3
  import { Button } from '../ui/Button';
4
+ import { normalizePageSize, getPageCount, clampPage } from '../../utils/pagination';
4
5
  import type { GridLocaleText } from '../../types';
5
6
 
6
7
  export interface PaginationProps {
@@ -29,11 +30,19 @@ export function Pagination(props: PaginationProps) {
29
30
  localeText,
30
31
  } = props;
31
32
 
32
- const pageCount = Math.max(1, Math.ceil(rowCount / pageSize));
33
- const currentPage = Math.min(page, pageCount - 1);
33
+ const safePageSize = normalizePageSize(pageSize);
34
+ const pageCount = getPageCount(rowCount, safePageSize);
35
+ const currentPage = clampPage(page, pageCount);
34
36
 
35
- const firstRowIndex = currentPage * pageSize;
36
- const lastRowIndex = Math.min(firstRowIndex + pageSize, rowCount);
37
+ // The select must be able to show the size in use, even when it is not one of the options
38
+ // (e.g. the default 100 with pageSizeOptions={[10, 25, 50]}); otherwise it displays the first
39
+ // option and choosing that option fires no change event.
40
+ const selectOptions = pageSizeOptions.includes(safePageSize)
41
+ ? pageSizeOptions
42
+ : [...pageSizeOptions, safePageSize].sort((a, b) => a - b);
43
+
44
+ const firstRowIndex = currentPage * safePageSize;
45
+ const lastRowIndex = Math.min(firstRowIndex + safePageSize, rowCount);
37
46
  const displayedRows = rowCount === 0 ? 0 : `${firstRowIndex + 1}–${lastRowIndex}`;
38
47
 
39
48
  const rowsPerPageLabel = localeText?.paginationRowsPerPage ?? 'Rows per page:';
@@ -79,11 +88,10 @@ export function Pagination(props: PaginationProps) {
79
88
  <select
80
89
  id={pageSizeId}
81
90
  className="ogx-pagination__select"
82
- value={pageSize}
91
+ value={safePageSize}
83
92
  onChange={handlePageSizeChange}
84
- aria-label="Rows per page"
85
93
  >
86
- {pageSizeOptions.map(option => (
94
+ {selectOptions.map(option => (
87
95
  <option key={option} value={option}>
88
96
  {option}
89
97
  </option>