beautiful-grid 1.0.0-rc.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/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +977 -0
- package/TRADEMARK.md +17 -0
- package/cjs/BGrid.js +503 -0
- package/cjs/components/CellEditorIcon.js +209 -0
- package/cjs/components/CellNavigationDomSync.js +117 -0
- package/cjs/components/CellTextEditorGateway.js +393 -0
- package/cjs/components/ColResizer.js +144 -0
- package/cjs/components/EditorPortalRoot.js +96 -0
- package/cjs/components/GridOptionalSurfaces.js +47 -0
- package/cjs/components/GripVertical.js +48 -0
- package/cjs/components/Loading.js +76 -0
- package/cjs/components/Pagination.js +93 -0
- package/cjs/components/PluginCellEditor.js +258 -0
- package/cjs/components/RowSelector.js +74 -0
- package/cjs/components/Table.js +2572 -0
- package/cjs/components/TableBody.js +446 -0
- package/cjs/components/TableBodyCell.js +112 -0
- package/cjs/components/TableBodyFrozen.js +44 -0
- package/cjs/components/TableColGroup.js +48 -0
- package/cjs/components/TableColGroupFrozen.js +57 -0
- package/cjs/components/TableFooter.js +70 -0
- package/cjs/components/TableHead.js +195 -0
- package/cjs/components/TableHeadColumn.js +124 -0
- package/cjs/components/TableHeadFrozen.js +165 -0
- package/cjs/components/TableSummary.js +133 -0
- package/cjs/components/TableSummaryFronzen.js +105 -0
- package/cjs/components/columnSortableRuntime.js +46 -0
- package/cjs/components/context-menu/GridContextMenu.js +174 -0
- package/cjs/components/context-menu/index.js +17 -0
- package/cjs/components/scrollbar/CustomScrollbar.js +58 -0
- package/cjs/components/scrollbar/ScrollbarButton.js +69 -0
- package/cjs/components/scrollbar/ScrollbarTrack.js +106 -0
- package/cjs/components/scrollbar/index.js +20 -0
- package/cjs/components/scrollbar/useScrollbarMetrics.js +103 -0
- package/cjs/components/search/GridSearchController.js +170 -0
- package/cjs/components/search/GridSearchPopover.js +163 -0
- package/cjs/components/search/index.js +18 -0
- package/cjs/components/selection/CellSelectionOverlay.js +52 -0
- package/cjs/components/selection/CellSelectionOverlayLayer.js +65 -0
- package/cjs/components/selection/index.js +18 -0
- package/cjs/components/toolbox/TableHeadToolbox.js +308 -0
- package/cjs/components/toolbox/ToolboxCustomSection.js +55 -0
- package/cjs/components/toolbox/ToolboxNumberFilterSection.js +151 -0
- package/cjs/components/toolbox/ToolboxSortSection.js +68 -0
- package/cjs/components/toolbox/ToolboxTextFilterSection.js +114 -0
- package/cjs/components/toolbox/ToolboxValueFilterSection.js +250 -0
- package/cjs/components/toolbox/index.js +22 -0
- package/cjs/components/useColumnSortable.js +70 -0
- package/cjs/editors/createDateEditorPlugin.js +88 -0
- package/cjs/editors/createSelectEditorPlugin.js +98 -0
- package/cjs/editors/defineEditorPlugin.js +17 -0
- package/cjs/editors/index.js +19 -0
- package/cjs/index.js +19 -0
- package/cjs/store/createAppStore.js +1645 -0
- package/cjs/store/index.js +17 -0
- package/cjs/types.js +14 -0
- package/cjs/utils/buildHeaderMatrix.js +265 -0
- package/cjs/utils/cellEditState.js +71 -0
- package/cjs/utils/cellEditTransaction.js +138 -0
- package/cjs/utils/cellSelectionGeometry.js +179 -0
- package/cjs/utils/common.js +154 -0
- package/cjs/utils/coordinate.js +125 -0
- package/cjs/utils/createPivotData.js +271 -0
- package/cjs/utils/delay.js +7 -0
- package/cjs/utils/filterData.js +182 -0
- package/cjs/utils/getCellSelectionAxisState.js +39 -0
- package/cjs/utils/getCellValue.js +21 -0
- package/cjs/utils/getColumnId.js +60 -0
- package/cjs/utils/getFrozenColumnsWidth.js +21 -0
- package/cjs/utils/getLineNumber.js +7 -0
- package/cjs/utils/getVisibleScrollableRowRange.js +24 -0
- package/cjs/utils/gridSearch.js +135 -0
- package/cjs/utils/index.js +39 -0
- package/cjs/utils/mergedCells.js +76 -0
- package/cjs/utils/mouseEventSubscribe.js +119 -0
- package/cjs/utils/number/index.js +19 -0
- package/cjs/utils/number/toFixed.js +6 -0
- package/cjs/utils/number/toMoney.js +13 -0
- package/cjs/utils/number/toNumber.js +12 -0
- package/cjs/utils/processDataQuery.js +126 -0
- package/cjs/utils/rowReorder.js +131 -0
- package/cjs/utils/scrollbar.js +62 -0
- package/cjs/utils/updateDataQuery.js +147 -0
- package/cjs/utils/useBodyData.js +277 -0
- package/cjs/utils/useForceUpdate.js +57 -0
- package/cjs/utils/useRowReorderController.js +666 -0
- package/esm/BGrid.js +466 -0
- package/esm/components/CellEditorIcon.js +115 -0
- package/esm/components/CellNavigationDomSync.js +80 -0
- package/esm/components/CellTextEditorGateway.js +275 -0
- package/esm/components/ColResizer.js +60 -0
- package/esm/components/EditorPortalRoot.js +57 -0
- package/esm/components/GridOptionalSurfaces.js +10 -0
- package/esm/components/GripVertical.js +11 -0
- package/esm/components/Loading.js +24 -0
- package/esm/components/Pagination.js +44 -0
- package/esm/components/PluginCellEditor.js +110 -0
- package/esm/components/RowSelector.js +38 -0
- package/esm/components/Table.js +2347 -0
- package/esm/components/TableBody.js +315 -0
- package/esm/components/TableBodyCell.js +73 -0
- package/esm/components/TableBodyFrozen.js +6 -0
- package/esm/components/TableColGroup.js +10 -0
- package/esm/components/TableColGroupFrozen.js +19 -0
- package/esm/components/TableFooter.js +30 -0
- package/esm/components/TableHead.js +114 -0
- package/esm/components/TableHeadColumn.js +86 -0
- package/esm/components/TableHeadFrozen.js +102 -0
- package/esm/components/TableSummary.js +66 -0
- package/esm/components/TableSummaryFronzen.js +62 -0
- package/esm/components/columnSortableRuntime.js +38 -0
- package/esm/components/context-menu/GridContextMenu.js +115 -0
- package/esm/components/context-menu/index.js +1 -0
- package/esm/components/scrollbar/CustomScrollbar.js +21 -0
- package/esm/components/scrollbar/ScrollbarButton.js +32 -0
- package/esm/components/scrollbar/ScrollbarTrack.js +66 -0
- package/esm/components/scrollbar/index.js +4 -0
- package/esm/components/scrollbar/useScrollbarMetrics.js +74 -0
- package/esm/components/search/GridSearchController.js +98 -0
- package/esm/components/search/GridSearchPopover.js +109 -0
- package/esm/components/search/index.js +2 -0
- package/esm/components/selection/CellSelectionOverlay.js +5 -0
- package/esm/components/selection/CellSelectionOverlayLayer.js +28 -0
- package/esm/components/selection/index.js +2 -0
- package/esm/components/toolbox/TableHeadToolbox.js +223 -0
- package/esm/components/toolbox/ToolboxCustomSection.js +18 -0
- package/esm/components/toolbox/ToolboxNumberFilterSection.js +98 -0
- package/esm/components/toolbox/ToolboxSortSection.js +31 -0
- package/esm/components/toolbox/ToolboxTextFilterSection.js +61 -0
- package/esm/components/toolbox/ToolboxValueFilterSection.js +195 -0
- package/esm/components/toolbox/index.js +6 -0
- package/esm/components/useColumnSortable.js +33 -0
- package/esm/editors/createDateEditorPlugin.js +48 -0
- package/esm/editors/createSelectEditorPlugin.js +60 -0
- package/esm/editors/defineEditorPlugin.js +6 -0
- package/esm/editors/index.js +3 -0
- package/esm/index.js +3 -0
- package/esm/store/createAppStore.js +1483 -0
- package/esm/store/index.js +1 -0
- package/esm/types.js +11 -0
- package/esm/utils/buildHeaderMatrix.js +231 -0
- package/esm/utils/cellEditState.js +32 -0
- package/esm/utils/cellEditTransaction.js +96 -0
- package/esm/utils/cellSelectionGeometry.js +166 -0
- package/esm/utils/common.js +145 -0
- package/esm/utils/coordinate.js +112 -0
- package/esm/utils/createPivotData.js +226 -0
- package/esm/utils/delay.js +1 -0
- package/esm/utils/filterData.js +176 -0
- package/esm/utils/getCellSelectionAxisState.js +34 -0
- package/esm/utils/getCellValue.js +18 -0
- package/esm/utils/getColumnId.js +52 -0
- package/esm/utils/getFrozenColumnsWidth.js +16 -0
- package/esm/utils/getLineNumber.js +3 -0
- package/esm/utils/getVisibleScrollableRowRange.js +20 -0
- package/esm/utils/gridSearch.js +127 -0
- package/esm/utils/index.js +23 -0
- package/esm/utils/mergedCells.js +70 -0
- package/esm/utils/mouseEventSubscribe.js +113 -0
- package/esm/utils/number/index.js +3 -0
- package/esm/utils/number/toFixed.js +3 -0
- package/esm/utils/number/toMoney.js +10 -0
- package/esm/utils/number/toNumber.js +9 -0
- package/esm/utils/processDataQuery.js +119 -0
- package/esm/utils/rowReorder.js +85 -0
- package/esm/utils/scrollbar.js +51 -0
- package/esm/utils/updateDataQuery.js +130 -0
- package/esm/utils/useBodyData.js +153 -0
- package/esm/utils/useForceUpdate.js +5 -0
- package/esm/utils/useRowReorderController.js +603 -0
- package/package.json +54 -0
- package/style.css +2508 -0
- package/types/BGrid.d.ts +3 -0
- package/types/components/CellEditorIcon.d.ts +13 -0
- package/types/components/CellNavigationDomSync.d.ts +17 -0
- package/types/components/CellTextEditorGateway.d.ts +20 -0
- package/types/components/ColResizer.d.ts +12 -0
- package/types/components/EditorPortalRoot.d.ts +8 -0
- package/types/components/GridOptionalSurfaces.d.ts +10 -0
- package/types/components/GripVertical.d.ts +6 -0
- package/types/components/Loading.d.ts +7 -0
- package/types/components/Pagination.d.ts +5 -0
- package/types/components/PluginCellEditor.d.ts +13 -0
- package/types/components/RowSelector.d.ts +10 -0
- package/types/components/Table.d.ts +61 -0
- package/types/components/TableBody.d.ts +39 -0
- package/types/components/TableBodyCell.d.ts +20 -0
- package/types/components/TableBodyFrozen.d.ts +15 -0
- package/types/components/TableColGroup.d.ts +3 -0
- package/types/components/TableColGroupFrozen.d.ts +5 -0
- package/types/components/TableFooter.d.ts +9 -0
- package/types/components/TableHead.d.ts +21 -0
- package/types/components/TableHeadColumn.d.ts +8 -0
- package/types/components/TableHeadFrozen.d.ts +7 -0
- package/types/components/TableSummary.d.ts +13 -0
- package/types/components/TableSummaryFronzen.d.ts +7 -0
- package/types/components/columnSortableRuntime.d.ts +12 -0
- package/types/components/context-menu/GridContextMenu.d.ts +6 -0
- package/types/components/context-menu/index.d.ts +1 -0
- package/types/components/scrollbar/CustomScrollbar.d.ts +11 -0
- package/types/components/scrollbar/ScrollbarButton.d.ts +9 -0
- package/types/components/scrollbar/ScrollbarTrack.d.ts +11 -0
- package/types/components/scrollbar/index.d.ts +4 -0
- package/types/components/scrollbar/useScrollbarMetrics.d.ts +12 -0
- package/types/components/search/GridSearchController.d.ts +1 -0
- package/types/components/search/GridSearchPopover.d.ts +6 -0
- package/types/components/search/index.d.ts +2 -0
- package/types/components/selection/CellSelectionOverlay.d.ts +17 -0
- package/types/components/selection/CellSelectionOverlayLayer.d.ts +18 -0
- package/types/components/selection/index.d.ts +2 -0
- package/types/components/toolbox/TableHeadToolbox.d.ts +13 -0
- package/types/components/toolbox/ToolboxCustomSection.d.ts +11 -0
- package/types/components/toolbox/ToolboxNumberFilterSection.d.ts +8 -0
- package/types/components/toolbox/ToolboxSortSection.d.ts +8 -0
- package/types/components/toolbox/ToolboxTextFilterSection.d.ts +8 -0
- package/types/components/toolbox/ToolboxValueFilterSection.d.ts +8 -0
- package/types/components/toolbox/index.d.ts +6 -0
- package/types/components/useColumnSortable.d.ts +13 -0
- package/types/editors/createDateEditorPlugin.d.ts +8 -0
- package/types/editors/createSelectEditorPlugin.d.ts +15 -0
- package/types/editors/defineEditorPlugin.d.ts +2 -0
- package/types/editors/index.d.ts +3 -0
- package/types/index.d.ts +3 -0
- package/types/store/createAppStore.d.ts +13 -0
- package/types/store/index.d.ts +1 -0
- package/types/types.d.ts +864 -0
- package/types/utils/buildHeaderMatrix.d.ts +32 -0
- package/types/utils/cellEditState.d.ts +12 -0
- package/types/utils/cellEditTransaction.d.ts +14 -0
- package/types/utils/cellSelectionGeometry.d.ts +34 -0
- package/types/utils/common.d.ts +3 -0
- package/types/utils/coordinate.d.ts +29 -0
- package/types/utils/createPivotData.d.ts +12 -0
- package/types/utils/delay.d.ts +1 -0
- package/types/utils/filterData.d.ts +18 -0
- package/types/utils/getCellSelectionAxisState.d.ts +12 -0
- package/types/utils/getCellValue.d.ts +2 -0
- package/types/utils/getColumnId.d.ts +19 -0
- package/types/utils/getFrozenColumnsWidth.d.ts +12 -0
- package/types/utils/getLineNumber.d.ts +6 -0
- package/types/utils/getVisibleScrollableRowRange.d.ts +20 -0
- package/types/utils/gridSearch.d.ts +18 -0
- package/types/utils/index.d.ts +23 -0
- package/types/utils/mergedCells.d.ts +14 -0
- package/types/utils/mouseEventSubscribe.d.ts +18 -0
- package/types/utils/number/index.d.ts +3 -0
- package/types/utils/number/toFixed.d.ts +1 -0
- package/types/utils/number/toMoney.d.ts +1 -0
- package/types/utils/number/toNumber.d.ts +1 -0
- package/types/utils/processDataQuery.d.ts +19 -0
- package/types/utils/rowReorder.d.ts +24 -0
- package/types/utils/scrollbar.d.ts +13 -0
- package/types/utils/updateDataQuery.d.ts +28 -0
- package/types/utils/useBodyData.d.ts +9 -0
- package/types/utils/useForceUpdate.d.ts +1 -0
- package/types/utils/useRowReorderController.d.ts +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
# BeautifulGrid
|
|
2
|
+
|
|
3
|
+
**Beautiful. Powerful. Naturally React.**
|
|
4
|
+
|
|
5
|
+
[](https://npmjs.org/package/beautiful-grid)
|
|
6
|
+
[](https://npmjs.org/package/beautiful-grid)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
BeautifulGrid is a beautiful, powerful, open-source React Data Grid for data-heavy business applications. It combines polished defaults with editing, sorting, filtering, selection, merging, aggregation, pivoting, and virtual scrolling.
|
|
10
|
+
|
|
11
|
+
Explore the live examples and documentation at [bgrid.axisj.com](https://bgrid.axisj.com).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i beautiful-grid
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Development
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Open [http://localhost:5173](http://localhost:5173) with your browser to see the demo app.
|
|
27
|
+
|
|
28
|
+
- [BGrid component structure](docs/component-structure.md)
|
|
29
|
+
- [Cell selection and clipboard](docs/cell-selection.md)
|
|
30
|
+
|
|
31
|
+
## Testing
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Unit tests (Vitest)
|
|
35
|
+
npm test
|
|
36
|
+
|
|
37
|
+
# Unit tests in watch mode
|
|
38
|
+
npm run test:watch
|
|
39
|
+
|
|
40
|
+
# Consumer compatibility test (install + cjs/esm/types check)
|
|
41
|
+
npm run test:library:consumers
|
|
42
|
+
|
|
43
|
+
# Published ESM initial bundle budget and generated homepage metrics
|
|
44
|
+
npm run test:library:bundle
|
|
45
|
+
|
|
46
|
+
# E2E tests (Playwright)
|
|
47
|
+
npm run test:e2e
|
|
48
|
+
|
|
49
|
+
# E2E tests with UI runner
|
|
50
|
+
npm run test:e2e:ui
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Build
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Demo app bundle (Vite)
|
|
57
|
+
npm run build
|
|
58
|
+
|
|
59
|
+
# Publishable library bundle (CJS + ESM + types + style)
|
|
60
|
+
npm run build:library
|
|
61
|
+
|
|
62
|
+
# Recalculate the published-library bundle metrics used by the website
|
|
63
|
+
npm run update:library:bundle-metrics
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The published ESM build targets ES2020 and loads column reordering, the column toolbox, search, and context menus only when they are used. The CommonJS build remains downleveled to ES5. Bundle measurements model an external ESM consumer and exclude the `react` and `react-dom` peer dependencies.
|
|
67
|
+
|
|
68
|
+
## Publish
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Build dist/cjs, dist/esm, dist/types and dist/package.json
|
|
72
|
+
npm run build:library
|
|
73
|
+
|
|
74
|
+
# Dry-run package file list from dist
|
|
75
|
+
npm run pack:library
|
|
76
|
+
|
|
77
|
+
# Publish a stable package manually
|
|
78
|
+
npm run publish:library
|
|
79
|
+
|
|
80
|
+
# Publish a prerelease without moving the latest tag
|
|
81
|
+
npm publish ./dist --access public --tag next
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Release checks
|
|
85
|
+
|
|
86
|
+
- Pushes to `main` run the GitHub Actions test workflow.
|
|
87
|
+
- Build and publish `dist` manually after the pushed commit passes CI.
|
|
88
|
+
- Prerelease versions must use the npm `next` tag so `latest` remains on the stable release.
|
|
89
|
+
- The production website deploy runs manually from `.github/workflows/deploy-website.yml` on `main`.
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
BeautifulGrid is open-source software licensed under the [Apache License 2.0](LICENSE). See [NOTICE](NOTICE) for attribution and [TRADEMARK.md](TRADEMARK.md) for use of the BeautifulGrid name and branding.
|
|
94
|
+
|
|
95
|
+
## Styling
|
|
96
|
+
|
|
97
|
+
Import the library stylesheet once in your app:
|
|
98
|
+
|
|
99
|
+
```typescript jsx
|
|
100
|
+
import 'beautiful-grid/style.css';
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`beautiful-grid` ships with pure static CSS and requires no runtime CSS-in-JS engine. The grid uses CSS custom properties (`--bgrid-*`) scoped to `[role='grid']`, allowing you to easily customize the theme globally or for a specific wrapper element.
|
|
104
|
+
|
|
105
|
+
We recommend overriding the public `--bgrid-*` CSS variables rather than targeting internal `.bgrid-*` classes directly to maintain upgrade compatibility across releases.
|
|
106
|
+
|
|
107
|
+
Default variables:
|
|
108
|
+
|
|
109
|
+
```css
|
|
110
|
+
[role='grid'] {
|
|
111
|
+
--bgrid-primary-color: #3b82f6;
|
|
112
|
+
--bgrid-header-bg: #f3f4f5;
|
|
113
|
+
--bgrid-header-color: #222;
|
|
114
|
+
--bgrid-header-font-weight: 500;
|
|
115
|
+
--bgrid-header-hover-bg: #e2e5e5;
|
|
116
|
+
--bgrid-header-group-bg: #e9e9e9;
|
|
117
|
+
--bgrid-footer-bg: #f3f4f5;
|
|
118
|
+
--bgrid-summary-bg: #eaeef6;
|
|
119
|
+
--bgrid-border-color-base: #d2d5d9;
|
|
120
|
+
--bgrid-border-color-light: #d2d5d9;
|
|
121
|
+
--bgrid-border-color-subtle: #e8ebef;
|
|
122
|
+
--bgrid-header-separator-color: #dde2e8;
|
|
123
|
+
--bgrid-frozen-boundary-color: #b8bec5;
|
|
124
|
+
--bgrid-frozen-boundary-width: 2px;
|
|
125
|
+
--bgrid-border-radius: 4px;
|
|
126
|
+
--bgrid-row-selector-color: #ffffff;
|
|
127
|
+
--bgrid-body-bg: #ffffff;
|
|
128
|
+
--bgrid-body-odd-bg: #f8f8f8;
|
|
129
|
+
--bgrid-body-hover-bg: #f3f4f5;
|
|
130
|
+
--bgrid-body-hover-odd-bg: #eeeeee;
|
|
131
|
+
--bgrid-body-active-bg: #e6f6ff;
|
|
132
|
+
--bgrid-cell-selected-bg: var(--bgrid-body-active-bg);
|
|
133
|
+
--bgrid-cell-selected-overlay-opacity: 0.72;
|
|
134
|
+
--bgrid-cell-selected-border-color: rgba(59, 130, 246, 0.78);
|
|
135
|
+
--bgrid-active-cell-bg: var(--bgrid-body-bg);
|
|
136
|
+
--bgrid-active-cell-ring-color: var(--bgrid-primary-color);
|
|
137
|
+
--bgrid-active-cell-ring-width: 2px;
|
|
138
|
+
--bgrid-cell-selected-border-width: var(--bgrid-active-cell-ring-width);
|
|
139
|
+
--bgrid-selection-axis-bg: #dbeafe;
|
|
140
|
+
--bgrid-selection-axis-color: var(--bgrid-primary-color);
|
|
141
|
+
--bgrid-selection-axis-border-color: var(--bgrid-primary-color);
|
|
142
|
+
--bgrid-cell-edited-bg: #fff7ed;
|
|
143
|
+
--bgrid-cell-edited-color: #c2410c;
|
|
144
|
+
--bgrid-cell-edited-border-color: #fdba74;
|
|
145
|
+
--bgrid-cell-value-changed-bg: var(--bgrid-cell-edited-bg);
|
|
146
|
+
--bgrid-cell-value-changed-color: var(--bgrid-cell-edited-color);
|
|
147
|
+
--bgrid-cell-value-changed-border-color: var(--bgrid-cell-edited-border-color);
|
|
148
|
+
--bgrid-body-color: #444;
|
|
149
|
+
|
|
150
|
+
--bgrid-toolbox-bg: #ffffff;
|
|
151
|
+
--bgrid-toolbox-color: #444444;
|
|
152
|
+
--bgrid-toolbox-muted-color: #64748b;
|
|
153
|
+
--bgrid-toolbox-control-bg: #ffffff;
|
|
154
|
+
--bgrid-toolbox-control-color: #444444;
|
|
155
|
+
--bgrid-toolbox-control-border-color: #d2d5d9;
|
|
156
|
+
--bgrid-toolbox-control-placeholder-color: #94a3b8;
|
|
157
|
+
--bgrid-toolbox-hover-bg: #f3f4f5;
|
|
158
|
+
--bgrid-toolbox-active-bg: #e6f6ff;
|
|
159
|
+
--bgrid-toolbox-danger-color: #dc2626;
|
|
160
|
+
--bgrid-toolbox-danger-bg: #fef2f2;
|
|
161
|
+
--bgrid-toolbox-button-bg: #f8fafc;
|
|
162
|
+
--bgrid-toolbox-primary-hover-color: #2563eb;
|
|
163
|
+
--bgrid-toolbox-primary-contrast-color: #ffffff;
|
|
164
|
+
--bgrid-toolbox-notice-bg: #f8fafc;
|
|
165
|
+
--bgrid-toolbox-scroll-thumb-bg: #c7ccda;
|
|
166
|
+
--bgrid-toolbox-scroll-track-bg: #f6f6f6;
|
|
167
|
+
--bgrid-toolbox-focus-ring-color: #bfdbfe;
|
|
168
|
+
|
|
169
|
+
--bgrid-search-bg: #ffffff;
|
|
170
|
+
--bgrid-search-color: #334155;
|
|
171
|
+
--bgrid-search-border-color: #d4dce8;
|
|
172
|
+
--bgrid-search-control-bg: #f8fafc;
|
|
173
|
+
--bgrid-search-control-color: #1f2937;
|
|
174
|
+
--bgrid-search-control-border-color: #cbd5e1;
|
|
175
|
+
--bgrid-search-muted-color: #64748b;
|
|
176
|
+
--bgrid-search-focus-ring-color: #3b82f6;
|
|
177
|
+
--bgrid-search-button-hover-bg: #f1f5f9;
|
|
178
|
+
--bgrid-search-match-bg: rgba(250, 204, 21, 0.28);
|
|
179
|
+
--bgrid-search-match-border-color: #ca8a04;
|
|
180
|
+
--bgrid-search-current-bg: rgba(249, 115, 22, 0.3);
|
|
181
|
+
--bgrid-search-current-border-color: #f97316;
|
|
182
|
+
--bgrid-context-menu-bg: var(--bgrid-toolbox-bg);
|
|
183
|
+
--bgrid-context-menu-color: var(--bgrid-toolbox-color);
|
|
184
|
+
--bgrid-context-menu-border-color: var(--bgrid-toolbox-control-border-color);
|
|
185
|
+
--bgrid-context-menu-hover-bg: var(--bgrid-toolbox-hover-bg);
|
|
186
|
+
--bgrid-context-menu-muted-color: var(--bgrid-toolbox-muted-color);
|
|
187
|
+
--bgrid-floating-z-editor: 9999;
|
|
188
|
+
--bgrid-floating-z-toolbox: 20;
|
|
189
|
+
--bgrid-floating-z-context-menu: 30;
|
|
190
|
+
--bgrid-search-z-index: 31;
|
|
191
|
+
|
|
192
|
+
--bgrid-scroll-size: 11px;
|
|
193
|
+
--bgrid-scroll-bg: #ffffff;
|
|
194
|
+
--bgrid-scroll-track-bg: #f6f6f6;
|
|
195
|
+
--bgrid-scroll-thumb-radius: 100px;
|
|
196
|
+
--bgrid-scroll-thumb-bg: #c7ccda;
|
|
197
|
+
--bgrid-scroll-thumb-hover-bg: #a1a3a6;
|
|
198
|
+
--bgrid-scroll-corner-bg: #c7ccda;
|
|
199
|
+
--bgrid-scroll-corner-radius: 5px;
|
|
200
|
+
|
|
201
|
+
--bgrid-loading-bg: rgba(163, 163, 163, 0.1);
|
|
202
|
+
--bgrid-loading-color: rgba(0, 0, 0, 0.1);
|
|
203
|
+
--bgrid-loading-second-color: #767676;
|
|
204
|
+
|
|
205
|
+
--bgrid-page-number-active-border-radius: 4px;
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Common customization targets:
|
|
210
|
+
|
|
211
|
+
| Area | Variables |
|
|
212
|
+
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
+
| Primary accents | `--bgrid-primary-color` |
|
|
214
|
+
| Header | `--bgrid-header-bg`, `--bgrid-header-color`, `--bgrid-header-font-weight`, `--bgrid-header-hover-bg`, `--bgrid-header-group-bg`, `--bgrid-header-separator-color` |
|
|
215
|
+
| Borders | `--bgrid-border-color-base`, `--bgrid-border-color-light`, `--bgrid-border-color-subtle`, `--bgrid-frozen-boundary-color`, `--bgrid-frozen-boundary-width`, `--bgrid-border-radius` |
|
|
216
|
+
| Body rows | `--bgrid-body-bg`, `--bgrid-body-odd-bg`, `--bgrid-body-hover-bg`, `--bgrid-body-hover-odd-bg`, `--bgrid-body-active-bg`, `--bgrid-body-color` |
|
|
217
|
+
| Cell selection | `--bgrid-cell-selected-bg`, `--bgrid-cell-selected-overlay-opacity`, `--bgrid-cell-selected-border-color`, `--bgrid-cell-selected-border-width`, `--bgrid-active-cell-bg`, `--bgrid-active-cell-ring-color`, `--bgrid-active-cell-ring-width` |
|
|
218
|
+
| Selection axes | `--bgrid-selection-axis-bg`, `--bgrid-selection-axis-color`, `--bgrid-selection-axis-border-color` |
|
|
219
|
+
| Edited cells | `--bgrid-cell-edited-bg`, `--bgrid-cell-edited-color`, `--bgrid-cell-edited-border-color` |
|
|
220
|
+
| Changed values | `--bgrid-cell-value-changed-bg`, `--bgrid-cell-value-changed-color`, `--bgrid-cell-value-changed-border-color` |
|
|
221
|
+
| Summary/Footer | `--bgrid-summary-bg`, `--bgrid-footer-bg` |
|
|
222
|
+
| Filter toolbox | `--bgrid-toolbox-bg`, `--bgrid-toolbox-color`, `--bgrid-toolbox-muted-color`, `--bgrid-toolbox-control-*`, `--bgrid-toolbox-hover-bg`, `--bgrid-toolbox-active-bg`, `--bgrid-toolbox-danger-*`, `--bgrid-toolbox-button-bg`, `--bgrid-toolbox-primary-*`, `--bgrid-toolbox-notice-bg`, `--bgrid-toolbox-scroll-*`, `--bgrid-toolbox-focus-ring-color` |
|
|
223
|
+
| Grid search | `--bgrid-search-bg`, `--bgrid-search-color`, `--bgrid-search-border-color`, `--bgrid-search-control-*`, `--bgrid-search-muted-color`, `--bgrid-search-focus-ring-color`, `--bgrid-search-button-hover-bg`, `--bgrid-search-match-*`, `--bgrid-search-current-*`, `--bgrid-search-z-index` |
|
|
224
|
+
| Context menu | `--bgrid-context-menu-bg`, `--bgrid-context-menu-color`, `--bgrid-context-menu-border-color`, `--bgrid-context-menu-hover-bg`, `--bgrid-context-menu-muted-color`, `--bgrid-floating-z-*` |
|
|
225
|
+
| Scrollbars | `--bgrid-scroll-size`, `--bgrid-scroll-bg`, `--bgrid-scroll-track-bg`, `--bgrid-scroll-thumb-radius`, `--bgrid-scroll-thumb-bg`, `--bgrid-scroll-thumb-hover-bg`, `--bgrid-scroll-corner-bg`, `--bgrid-scroll-corner-radius` |
|
|
226
|
+
| Loading overlay | `--bgrid-loading-bg`, `--bgrid-loading-color`, `--bgrid-loading-second-color` |
|
|
227
|
+
| Pagination | `--bgrid-page-number-active-border-radius` |
|
|
228
|
+
|
|
229
|
+
`selectedRowKey` row highlighting uses `--bgrid-body-active-bg`. Cell selection is painted by pointer-transparent overlay rectangles rather than per-cell borders. The overlay uses `--bgrid-cell-selected-bg` for the fill, `--bgrid-cell-selected-overlay-opacity` for fill opacity, and `--bgrid-cell-selected-border-color` for the selection edge. By default, `--bgrid-cell-selected-bg` references `--bgrid-body-active-bg` so row selection and cell selection stay in the same color tone.
|
|
230
|
+
|
|
231
|
+
When a range crosses frozen rows or columns, the Grid splits the overlay into the corresponding panel fragments and draws only the outside perimeter. This keeps the selection rectangular across merged cells and synchronized with horizontal and vertical scrolling.
|
|
232
|
+
|
|
233
|
+
The focused cell uses `--bgrid-active-cell-bg`. A single-cell selection also uses the inset ring controlled by `--bgrid-active-cell-ring-color` and `--bgrid-active-cell-ring-width`. During multi-cell selection the focused cell has no individual ring; only the outer border of the complete selection range is rendered. Its width is controlled by `--bgrid-cell-selected-border-width`, which defaults to the single-cell ring width.
|
|
234
|
+
|
|
235
|
+
The column headers and line numbers covered by the active cell or a multi-cell range receive `bgrid-column-axis-active` and `bgrid-row-axis-active`. Their fill, text, and inset axis line use the three `--bgrid-selection-axis-*` variables.
|
|
236
|
+
|
|
237
|
+
Cells directly changed by a text editor, plugin editor, legacy inline editor, or clipboard paste receive `bgrid-cell-edited` and `data-bgrid-cell-edited="true"`. The row wrapper records those stable column ids in `editedColumnIds`. It separately records normalized data key tokens in `changedKeys`. Every column sharing a changed key receives `bgrid-cell-value-changed` and `data-bgrid-cell-value-changed="true"`, even when only one column instance was directly edited. Clear both arrays after a successful commit. The two states use the `--bgrid-cell-edited-*` and `--bgrid-cell-value-changed-*` palettes; the changed-value palette defaults to the edited-cell palette.
|
|
238
|
+
|
|
239
|
+
The filter toolbox, editor plugins, and cell context menu share a document-level floating portal per Grid instance. The Grid copies its public `--bgrid-*` variables to that portal, so instance-level themes continue to apply outside the Grid DOM subtree.
|
|
240
|
+
|
|
241
|
+
### Grid Search and Cell Context Menu
|
|
242
|
+
|
|
243
|
+
Pass `searchOptions` to enable search over all currently loaded display rows. The Grid searches Store data rather than rendered DOM, highlights every matching cell, and scrolls virtual or frozen regions when moving between results.
|
|
244
|
+
|
|
245
|
+
Dedicated context menu example: [`examples/ContextMenuExample.tsx`](./examples/ContextMenuExample.tsx)
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
<BGrid
|
|
249
|
+
columns={columns}
|
|
250
|
+
data={data}
|
|
251
|
+
rowKey='id'
|
|
252
|
+
searchOptions={{}}
|
|
253
|
+
contextMenuOptions={{
|
|
254
|
+
items: target => [
|
|
255
|
+
{
|
|
256
|
+
id: 'inspect-row',
|
|
257
|
+
label: 'Inspect row',
|
|
258
|
+
onSelect: () => console.log(target.sourceIndex, target.values),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
}}
|
|
262
|
+
/>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- `Ctrl+F` / `Cmd+F`: open search in the focused Grid.
|
|
266
|
+
- `Enter` / `Shift+Enter`: next / previous match.
|
|
267
|
+
- `Escape`: close search and clear highlights.
|
|
268
|
+
- Right-click: activate and select the target cell before opening the body-cell context menu.
|
|
269
|
+
- Selecting another cell closes the open context menu.
|
|
270
|
+
- `Shift+F10`: open the context menu for the active cell.
|
|
271
|
+
- Search covers only rows currently supplied to the Grid. Server-wide search and result filtering are separate concerns.
|
|
272
|
+
- Searchable text defaults to the value read from `item.values`. Use column `getSearchText` when `itemRender` displays a formatted value, or `searchable: false` to exclude a column.
|
|
273
|
+
|
|
274
|
+
Controlled `searchOptions.open` and `searchOptions.query` require their matching callbacks:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
searchOptions={{
|
|
278
|
+
open,
|
|
279
|
+
query,
|
|
280
|
+
onOpenChange: setOpen,
|
|
281
|
+
onQueryChange: setQuery,
|
|
282
|
+
}}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Example: scope a custom blue selection theme to one grid wrapper.
|
|
286
|
+
|
|
287
|
+
```css
|
|
288
|
+
.my-grid [role='grid'] {
|
|
289
|
+
--bgrid-primary-color: #2563eb;
|
|
290
|
+
--bgrid-body-active-bg: #e0f2fe;
|
|
291
|
+
--bgrid-cell-selected-bg: var(--bgrid-body-active-bg);
|
|
292
|
+
--bgrid-cell-selected-overlay-opacity: 0.72;
|
|
293
|
+
--bgrid-cell-selected-border-color: rgba(37, 99, 235, 0.72);
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Usage
|
|
298
|
+
|
|
299
|
+
- codesandbox DEMO : https://codesandbox.io/p/devbox/basic-example-5ch6kt?embed=1&file=%2Fsrc%2FApp.tsx
|
|
300
|
+
|
|
301
|
+
### Basic Example
|
|
302
|
+
|
|
303
|
+
```typescript jsx
|
|
304
|
+
import * as React from 'react';
|
|
305
|
+
import { BGrid, BGridColumn } from 'beautiful-grid';
|
|
306
|
+
|
|
307
|
+
interface IListItem {
|
|
308
|
+
id: string;
|
|
309
|
+
title: string;
|
|
310
|
+
writer: string;
|
|
311
|
+
createAt: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const list = Array.from({ length: 1000 }).map((_, i) => ({
|
|
315
|
+
values: {
|
|
316
|
+
id: `ID_${i}`,
|
|
317
|
+
title: `title_${i}`,
|
|
318
|
+
writer: `writer_${i}`,
|
|
319
|
+
createAt: `2022-09-08`,
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
|
|
323
|
+
function BasicExample() {
|
|
324
|
+
const [columns, setColumns] = React.useState<BGridColumn<IListItem>[]>([
|
|
325
|
+
{ key: 'id', label: 'ID', width: 120 },
|
|
326
|
+
{
|
|
327
|
+
key: 'title',
|
|
328
|
+
label: '제목',
|
|
329
|
+
width: 260,
|
|
330
|
+
itemRender: ({ values }) => (
|
|
331
|
+
<>
|
|
332
|
+
{values.writer} / {values.title}
|
|
333
|
+
</>
|
|
334
|
+
),
|
|
335
|
+
},
|
|
336
|
+
{ key: 'writer', label: '작성자', width: 120 },
|
|
337
|
+
{ key: 'createAt', label: '작성일', width: 140 },
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
const [checkedRowKeys, setCheckedRowKeys] = React.useState<React.Key[]>([]);
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
<div style={{ fontSize: 13 }}>
|
|
344
|
+
<BGrid<IListItem>
|
|
345
|
+
width={720}
|
|
346
|
+
height={420}
|
|
347
|
+
data={list}
|
|
348
|
+
columns={columns}
|
|
349
|
+
rowKey={'id'}
|
|
350
|
+
onChangeColumns={(columnIndex, { width, columns }) => {
|
|
351
|
+
console.log('onChangeColumns', columnIndex, width, columns);
|
|
352
|
+
setColumns(columns);
|
|
353
|
+
}}
|
|
354
|
+
rowChecked={{
|
|
355
|
+
checkedRowKeys,
|
|
356
|
+
onChange: (checkedIndexes, checkedRowKeys, checkedAll) => {
|
|
357
|
+
console.log('rowChecked changed', checkedIndexes, checkedRowKeys, checkedAll);
|
|
358
|
+
setCheckedRowKeys(checkedRowKeys);
|
|
359
|
+
},
|
|
360
|
+
}}
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export default BasicExample;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Important Data/Column Rules
|
|
370
|
+
|
|
371
|
+
- Row data type is `BGridDataItem<T>` and the real domain model is always inside `item.values`.
|
|
372
|
+
- `BGridColumn.key` supports both `string` and `string[]`.
|
|
373
|
+
- `key: 'writer'`
|
|
374
|
+
- `key: ['user', 'profile', 'name']` (nested value path)
|
|
375
|
+
- `itemRender` receives both:
|
|
376
|
+
- `value`: current cell value by `column.key`
|
|
377
|
+
- `values`: full row model (`T`)
|
|
378
|
+
- `columns[i].left` is internal layout value computed by `BGrid`; do not manage it manually.
|
|
379
|
+
|
|
380
|
+
### Header Toolbox: Sorting and Filtering
|
|
381
|
+
|
|
382
|
+
Add `toolbox` to a column and control the applied sort/filter state through `dataControl.query`. Query state is controlled: `onChange` receives a new query, and the consumer must pass that query back to the Grid.
|
|
383
|
+
|
|
384
|
+
```typescript jsx
|
|
385
|
+
import * as React from 'react';
|
|
386
|
+
import { BGrid, BGridColumn, BGridDataItem, BGridDataQuery } from 'beautiful-grid';
|
|
387
|
+
|
|
388
|
+
interface Product {
|
|
389
|
+
id: number;
|
|
390
|
+
name: string;
|
|
391
|
+
category: string;
|
|
392
|
+
price: number;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const columns: BGridColumn<Product>[] = [
|
|
396
|
+
{
|
|
397
|
+
id: 'name',
|
|
398
|
+
key: 'name',
|
|
399
|
+
label: 'Name',
|
|
400
|
+
width: 220,
|
|
401
|
+
toolbox: true,
|
|
402
|
+
filter: { type: 'text' },
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: 'category',
|
|
406
|
+
key: 'category',
|
|
407
|
+
label: 'Category',
|
|
408
|
+
width: 140,
|
|
409
|
+
toolbox: true,
|
|
410
|
+
filter: { type: 'values' },
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: 'price',
|
|
414
|
+
key: 'price',
|
|
415
|
+
label: 'Price',
|
|
416
|
+
width: 120,
|
|
417
|
+
toolbox: { sort: true, filter: true },
|
|
418
|
+
filter: { type: 'number' },
|
|
419
|
+
},
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
function ProductGrid({ data }: { data: BGridDataItem<Product>[] }) {
|
|
423
|
+
const [query, setQuery] = React.useState<BGridDataQuery>({
|
|
424
|
+
sortParams: [],
|
|
425
|
+
filterParams: [],
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
<BGrid<Product>
|
|
430
|
+
width={720}
|
|
431
|
+
height={420}
|
|
432
|
+
data={data}
|
|
433
|
+
columns={columns}
|
|
434
|
+
rowKey='id'
|
|
435
|
+
dataControl={{
|
|
436
|
+
mode: 'client',
|
|
437
|
+
multiSort: true,
|
|
438
|
+
query,
|
|
439
|
+
onChange: nextQuery => setQuery(nextQuery),
|
|
440
|
+
}}
|
|
441
|
+
/>
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Toolbox configuration:
|
|
447
|
+
|
|
448
|
+
- `toolbox: true` enables the built-in sort and filter sections.
|
|
449
|
+
- `toolbox: { sort, filter, extraItems, render, icons }` configures sections and custom content. Object form enables only sections explicitly set to `true`.
|
|
450
|
+
- `filter.type` supports `values`, `text`, and `number`. Use `filter: false` to suppress the filter section.
|
|
451
|
+
- Give every Toolbox column a stable, unique `id`, especially when multiple columns read the same `key`.
|
|
452
|
+
- Grid-level `icons` supplies default Toolbox icons. A column-level `toolbox.icons` overrides them for that column.
|
|
453
|
+
|
|
454
|
+
Data modes:
|
|
455
|
+
|
|
456
|
+
- `mode: 'client'` filters and sorts the complete `data` array inside the Grid. Do not combine it with externally paged data as if the current page were the complete data set.
|
|
457
|
+
- `mode: 'manual'` only emits the next query. Fetch, filter, sort, and paginate on the server, then pass the resulting `data` back. For a manual `values` filter, provide `filter.values` because the Grid must not infer the full server-side value domain from one page.
|
|
458
|
+
- `mode` defaults to `manual`. Set it explicitly when shared configuration should make the processing location obvious.
|
|
459
|
+
- `dataControl` takes precedence over the legacy `sort` prop. Existing grids can continue using `sort` when Toolbox filtering is not needed.
|
|
460
|
+
|
|
461
|
+
Manual/server-controlled example:
|
|
462
|
+
|
|
463
|
+
```typescript jsx
|
|
464
|
+
<BGrid
|
|
465
|
+
width={720}
|
|
466
|
+
height={420}
|
|
467
|
+
data={serverPage}
|
|
468
|
+
columns={columns}
|
|
469
|
+
page={page}
|
|
470
|
+
dataControl={{
|
|
471
|
+
mode: 'manual',
|
|
472
|
+
query,
|
|
473
|
+
onChange: (nextQuery, event) => {
|
|
474
|
+
setQuery(nextQuery);
|
|
475
|
+
requestPage({ page: 0, query: nextQuery, changedBy: event });
|
|
476
|
+
},
|
|
477
|
+
}}
|
|
478
|
+
/>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Pivot mode disables the default Toolbox. Row reordering is automatically disabled while a client-side filter or sort is active because displayed row order no longer represents the source array order.
|
|
482
|
+
|
|
483
|
+
### Row Reorder
|
|
484
|
+
|
|
485
|
+
Enable line numbers and `reorder` to move rows. The dedicated handle supports pointer drag and keyboard operation: focus it, press `Space` or `Enter` to pick up, use `ArrowUp`/`ArrowDown`, then press `Enter` to drop or `Escape` to cancel.
|
|
486
|
+
|
|
487
|
+
```typescript jsx
|
|
488
|
+
<BGrid
|
|
489
|
+
data={data}
|
|
490
|
+
columns={columns}
|
|
491
|
+
rowKey='id'
|
|
492
|
+
showLineNumber
|
|
493
|
+
reorder={{
|
|
494
|
+
enabled: true,
|
|
495
|
+
onReorder: nextData => {
|
|
496
|
+
setData(nextData);
|
|
497
|
+
return true;
|
|
498
|
+
},
|
|
499
|
+
}}
|
|
500
|
+
/>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
The Grid previews the source, shifted rows, and insertion target before changing data. It commits once after the settle motion. Returning `false` from the synchronous `onReorder` callback rolls the operation back; `true` or `void` keeps it. Checked rows and the active cell follow the moved item, while an open editor blocks reordering. Virtual scrolling supports edge auto-scroll and an off-screen source preview. Merged-cell grids use a safe preview fallback instead of transforming `rowspan` geometry. Reduced-motion preferences remove both the transition and its commit delay.
|
|
504
|
+
|
|
505
|
+
Row reordering is disabled for pivot output, frozen rows, and active client-side sort/filter queries. `onReorder` is not a Promise-based save lifecycle; applications that persist remotely should own their optimistic update and failure policy.
|
|
506
|
+
|
|
507
|
+
### Cell Selection and Clipboard
|
|
508
|
+
|
|
509
|
+
Cell selection is enabled by default. Cells can be selected by mouse drag, and `Ctrl+C` or `Cmd+C` copies selected cell text using tab (`\t`) between columns and carriage return (`\r`) between rows. In an editable grid, select a target cell and press `Ctrl+V` or `Cmd+V` to paste tab/newline-delimited data from that cell. Disable the feature when it is not needed:
|
|
510
|
+
|
|
511
|
+
```typescript jsx
|
|
512
|
+
<BGrid width={700} height={400} columns={columns} data={data} cellSelectionOptions={{ enabled: false }} />
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Cell selection is kept when the user clicks inside the grid, including empty body space or the grid scrollbar. By default, selection is cleared when `Escape` is pressed or when the user clicks outside the grid. Use `cellSelectionOptions` to change those clear rules:
|
|
516
|
+
|
|
517
|
+
```typescript jsx
|
|
518
|
+
<BGrid
|
|
519
|
+
width={700}
|
|
520
|
+
height={400}
|
|
521
|
+
columns={columns}
|
|
522
|
+
data={data}
|
|
523
|
+
cellSelectionOptions={{
|
|
524
|
+
enabled: true,
|
|
525
|
+
clearOnEscape: false,
|
|
526
|
+
clearOnOutsideClick: false,
|
|
527
|
+
maxClipboardCells: 100000,
|
|
528
|
+
maxClipboardTextLength: 8 * 1024 * 1024,
|
|
529
|
+
onCopyError: error => {
|
|
530
|
+
console.warn(error);
|
|
531
|
+
},
|
|
532
|
+
onPasteError: error => {
|
|
533
|
+
console.warn(error);
|
|
534
|
+
},
|
|
535
|
+
createRowOnPaste: () => ({
|
|
536
|
+
values: createEmptyRow(),
|
|
537
|
+
}),
|
|
538
|
+
}}
|
|
539
|
+
/>
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Clipboard copy is skipped before building clipboard text when the selected range is too large. The default limits are
|
|
543
|
+
`maxClipboardCells: 100000` and `maxClipboardTextLength: 8 * 1024 * 1024`. The same limits apply to paste. Use `onCopyError` or `onPasteError` to notify users when clipboard work is skipped, parsing fails, or the browser rejects the clipboard write.
|
|
544
|
+
|
|
545
|
+
Pasted data beyond the last row is clipped unless `createRowOnPaste` is provided. The callback creates each missing row, which is registered with `BGridDataItemStatus.new` and reported through `onChangeData(rowIndex, null, values, null)`.
|
|
546
|
+
|
|
547
|
+
By default, copied text is read from `column.key`:
|
|
548
|
+
|
|
549
|
+
```typescript jsx
|
|
550
|
+
{ key: 'title', label: 'Title', width: 240 }
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
If a column renders a custom component or needs a different clipboard value, define `getClipboardText` on the column. `getClipboardText` is used before falling back to the `column.key` value.
|
|
554
|
+
|
|
555
|
+
```typescript jsx
|
|
556
|
+
const columns: BGridColumn<IListItem>[] = [
|
|
557
|
+
{
|
|
558
|
+
key: 'title',
|
|
559
|
+
label: 'Title',
|
|
560
|
+
width: 260,
|
|
561
|
+
itemRender: ({ values }) => (
|
|
562
|
+
<>
|
|
563
|
+
{values.writer} / {values.title}
|
|
564
|
+
</>
|
|
565
|
+
),
|
|
566
|
+
getClipboardText: ({ values }) => `${values.writer}\t${values.title}`,
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
key: ['user', 'profile', 'name'],
|
|
570
|
+
label: 'User',
|
|
571
|
+
width: 160,
|
|
572
|
+
getClipboardText: ({ value, values }) => value ?? values.writer,
|
|
573
|
+
},
|
|
574
|
+
];
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Cell Focus and Keyboard Navigation
|
|
578
|
+
|
|
579
|
+
Click a cell to make it active, then use Arrow keys, Home/End, PageUp/PageDown, or Tab to move. `Shift` + Arrow extends the cell selection. `Ctrl`/`Cmd` + Arrow moves to an edge, and `Ctrl`/`Cmd` + Home/End moves to the first or last grid cell.
|
|
580
|
+
|
|
581
|
+
Use `cellNavigationOptions` when the active cell must be initialized or controlled by the application:
|
|
582
|
+
|
|
583
|
+
```typescript jsx
|
|
584
|
+
const [activeCell, setActiveCell] = React.useState({ rowIndex: 0, columnIndex: 1 });
|
|
585
|
+
|
|
586
|
+
<BGrid
|
|
587
|
+
width={800}
|
|
588
|
+
height={420}
|
|
589
|
+
columns={columns}
|
|
590
|
+
data={data}
|
|
591
|
+
cellNavigationOptions={{
|
|
592
|
+
activeCell,
|
|
593
|
+
onActiveCellChange: cell => {
|
|
594
|
+
if (cell) setActiveCell(cell);
|
|
595
|
+
},
|
|
596
|
+
wrap: false,
|
|
597
|
+
editOnEnter: true,
|
|
598
|
+
keyRepeat: { interval: 16 },
|
|
599
|
+
}}
|
|
600
|
+
/>;
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
`F2` starts editing the active editable cell. `Enter` also starts editing when `editable` and `editOnEnter` are enabled; otherwise `Enter` and `Space` invoke the active cell's `onClick` callback without moving focus. Holding an Arrow key switches from the operating system repeat event to a frame-synchronized repeat loop; use `keyRepeat.interval` to tune the interval in milliseconds or `keyRepeat.enabled: false` to retain native repeat timing. Interactive elements such as inputs and buttons keep their own keyboard behavior. See the runnable [cell navigation example](examples/CellNavigationExample.tsx) and the [keyboard guide](site/src/content/learn/cell-navigation.md).
|
|
604
|
+
|
|
605
|
+
### Pivot
|
|
606
|
+
|
|
607
|
+
Pass `pivot` to render the grid as a derived pivot table. The API follows the Excel-style field areas:
|
|
608
|
+
|
|
609
|
+
- `rows`: row fields
|
|
610
|
+
- `columns`: column fields
|
|
611
|
+
- `values`: value fields and aggregate rules
|
|
612
|
+
|
|
613
|
+
When `pivot` is active, row-based grid UI is disabled internally because the rendered rows are derived summary rows. This includes frozen columns, line numbers, row check controls, sorting, column sorting, editing, pagination, summary, cell merge, reorder, and row/cell change callbacks.
|
|
614
|
+
|
|
615
|
+
```typescript jsx
|
|
616
|
+
import * as React from 'react';
|
|
617
|
+
import { BGrid, BGridColumn, BGridProps } from 'beautiful-grid';
|
|
618
|
+
|
|
619
|
+
interface SalesItem {
|
|
620
|
+
region: string;
|
|
621
|
+
product: string;
|
|
622
|
+
quarter: string;
|
|
623
|
+
sales: number;
|
|
624
|
+
quantity: number;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const data = [
|
|
628
|
+
{ values: { region: 'North', product: 'Desk', quarter: 'Q1', sales: 1200, quantity: 12 } },
|
|
629
|
+
{ values: { region: 'North', product: 'Desk', quarter: 'Q2', sales: 1400, quantity: 10 } },
|
|
630
|
+
{ values: { region: 'South', product: 'Chair', quarter: 'Q1', sales: 900, quantity: 8 } },
|
|
631
|
+
];
|
|
632
|
+
|
|
633
|
+
const columns: BGridColumn<SalesItem>[] = [
|
|
634
|
+
{ key: 'region', label: 'Region', width: 120 },
|
|
635
|
+
{ key: 'product', label: 'Product', width: 140 },
|
|
636
|
+
{ key: 'quarter', label: 'Quarter', width: 100 },
|
|
637
|
+
{ key: 'sales', label: 'Sales', width: 120, align: 'right' },
|
|
638
|
+
{ key: 'quantity', label: 'Quantity', width: 120, align: 'right' },
|
|
639
|
+
];
|
|
640
|
+
|
|
641
|
+
const pivot: BGridProps<SalesItem>['pivot'] = {
|
|
642
|
+
rows: [{ key: 'product', label: 'Product', width: 140 }],
|
|
643
|
+
columns: [{ key: 'region', label: 'Region' }],
|
|
644
|
+
values: [
|
|
645
|
+
{
|
|
646
|
+
key: 'sales',
|
|
647
|
+
label: 'Sales',
|
|
648
|
+
aggregate: 'sum',
|
|
649
|
+
itemRender: ({ value, columnValues, sourceItems }) => {
|
|
650
|
+
const text = `$${Number(value).toLocaleString()}`;
|
|
651
|
+
if (columnValues[0] === 'South') {
|
|
652
|
+
return <strong title={`${sourceItems.length} source rows`}>{text}</strong>;
|
|
653
|
+
}
|
|
654
|
+
return <span title={`${sourceItems.length} source rows`}>{text}</span>;
|
|
655
|
+
},
|
|
656
|
+
getClipboardText: ({ value }) => Number(value).toLocaleString(),
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
key: 'quantity',
|
|
660
|
+
label: 'Quantity',
|
|
661
|
+
aggregate: 'sum',
|
|
662
|
+
itemRender: ({ value }) => <>{Number(value).toLocaleString()} ea</>,
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
emptyValue: 0,
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
function PivotExample() {
|
|
669
|
+
return (
|
|
670
|
+
<BGrid<SalesItem>
|
|
671
|
+
width={900}
|
|
672
|
+
height={420}
|
|
673
|
+
columns={columns}
|
|
674
|
+
data={data}
|
|
675
|
+
pivot={pivot}
|
|
676
|
+
variant={'vertical-bordered'}
|
|
677
|
+
/>
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
Built-in aggregate values:
|
|
683
|
+
|
|
684
|
+
| Aggregate | Description |
|
|
685
|
+
| --------- | --------------------------------- |
|
|
686
|
+
| `sum` | Numeric sum. This is the default. |
|
|
687
|
+
| `count` | Number of source values. |
|
|
688
|
+
| `avg` | Numeric average. |
|
|
689
|
+
| `min` | Numeric minimum. |
|
|
690
|
+
| `max` | Numeric maximum. |
|
|
691
|
+
| `first` | First source value. |
|
|
692
|
+
|
|
693
|
+
You can also pass a custom aggregate function:
|
|
694
|
+
|
|
695
|
+
```typescript jsx
|
|
696
|
+
const pivot: BGridProps<SalesItem>['pivot'] = {
|
|
697
|
+
rows: [{ key: 'product', label: 'Product' }],
|
|
698
|
+
columns: [{ key: 'quarter', label: 'Quarter' }],
|
|
699
|
+
values: [
|
|
700
|
+
{
|
|
701
|
+
key: 'sales',
|
|
702
|
+
label: 'Max online sales',
|
|
703
|
+
aggregate: ({ items }) => {
|
|
704
|
+
return Math.max(...items.filter(item => item.values.region === 'North').map(item => item.values.sales));
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
],
|
|
708
|
+
};
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
`pivot.values[].itemRender` receives the normal cell render props plus pivot context:
|
|
712
|
+
|
|
713
|
+
| Prop | Description |
|
|
714
|
+
| -------------- | ----------------------------------------------- |
|
|
715
|
+
| `value` | Aggregated cell value |
|
|
716
|
+
| `values` | Pivot result row values |
|
|
717
|
+
| `rowValues` | Values for the current pivot row fields |
|
|
718
|
+
| `columnValues` | Values for the current pivot column fields |
|
|
719
|
+
| `sourceItems` | Original `BGridDataItem<T>[]` used for this cell |
|
|
720
|
+
| `pivotValue` | The current value-field definition |
|
|
721
|
+
| `aggregate` | The current aggregate setting |
|
|
722
|
+
|
|
723
|
+
### Common Scenarios
|
|
724
|
+
|
|
725
|
+
#### 1) Frozen Columns
|
|
726
|
+
|
|
727
|
+
```typescript jsx
|
|
728
|
+
<BGrid width={900} height={500} frozenColumnIndex={2} columns={columns} data={data} />
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
`frozenColumnIndex` 이전 컬럼(0, 1)은 고정 영역으로 렌더링됩니다.
|
|
732
|
+
|
|
733
|
+
행과 컬럼을 함께 고정할 수 있습니다. 상단 Summary Row를 사용하면 고정 행은 Summary 바로 다음 줄부터 시작합니다.
|
|
734
|
+
|
|
735
|
+
```typescript jsx
|
|
736
|
+
<BGrid
|
|
737
|
+
width={900}
|
|
738
|
+
height={500}
|
|
739
|
+
frozenColumnIndex={2}
|
|
740
|
+
frozenRowCount={3}
|
|
741
|
+
summary={{ position: 'top', columns: summaryColumns }}
|
|
742
|
+
columns={columns}
|
|
743
|
+
data={data}
|
|
744
|
+
/>
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
#### 2) Editable Cell
|
|
748
|
+
|
|
749
|
+
```typescript jsx
|
|
750
|
+
const columns: BGridColumn<IListItem>[] = [
|
|
751
|
+
{
|
|
752
|
+
key: 'title',
|
|
753
|
+
label: '제목',
|
|
754
|
+
width: 240,
|
|
755
|
+
editable: true,
|
|
756
|
+
editor: {
|
|
757
|
+
type: 'text',
|
|
758
|
+
startOnInput: true,
|
|
759
|
+
inputProps: { maxLength: 100, autoComplete: 'off' },
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
];
|
|
763
|
+
|
|
764
|
+
<BGrid editable editTrigger={'dblclick'} columns={columns} data={data} width={700} height={400} />;
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
The built-in text editor supports direct typing, IME composition, Enter/F2 preserve mode, Escape cancel,
|
|
768
|
+
and focus return after editing. Prebuilt Select and Date plugins are available from the editor subpath:
|
|
769
|
+
|
|
770
|
+
```typescript jsx
|
|
771
|
+
import { createDateEditorPlugin, createSelectEditorPlugin } from 'beautiful-grid/editors';
|
|
772
|
+
|
|
773
|
+
const statusEditor = createSelectEditorPlugin<IListItem>({
|
|
774
|
+
id: 'status',
|
|
775
|
+
options: [
|
|
776
|
+
{ value: 'ready', label: 'Ready' },
|
|
777
|
+
{ value: 'done', label: 'Done' },
|
|
778
|
+
],
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
const dateEditor = createDateEditorPlugin<IListItem>({ id: 'delivery-date' });
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
Use `defineEditorPlugin()` from the same subpath to connect application-specific editors. Portal-based UI
|
|
785
|
+
components should mount their popup into the `getPortalContainer()` supplied to the plugin component.
|
|
786
|
+
|
|
787
|
+
#### 3) Sort + Page
|
|
788
|
+
|
|
789
|
+
```typescript jsx
|
|
790
|
+
<BGrid
|
|
791
|
+
width={900}
|
|
792
|
+
height={560}
|
|
793
|
+
columns={columns}
|
|
794
|
+
data={rows}
|
|
795
|
+
sort={{
|
|
796
|
+
sortParams,
|
|
797
|
+
onChange: next => setSortParams(next),
|
|
798
|
+
}}
|
|
799
|
+
page={{
|
|
800
|
+
currentPage,
|
|
801
|
+
pageSize,
|
|
802
|
+
totalPages,
|
|
803
|
+
totalElements,
|
|
804
|
+
onChange: (nextPage, nextSize) => {
|
|
805
|
+
setCurrentPage(nextPage);
|
|
806
|
+
if (nextSize) setPageSize(nextSize);
|
|
807
|
+
},
|
|
808
|
+
}}
|
|
809
|
+
/>
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### Props Reference (BGrid)
|
|
813
|
+
|
|
814
|
+
아래는 자주 사용하는 Props 중심 정리입니다. 타입의 최종 기준은 `beautiful-grid/types.ts`의 `BGridProps<T>`입니다.
|
|
815
|
+
|
|
816
|
+
#### Required
|
|
817
|
+
|
|
818
|
+
| Prop | Type | Description |
|
|
819
|
+
| --------- | ------------------------------------ | ---------------- |
|
|
820
|
+
| `width` | `number` | 그리드 전체 너비 |
|
|
821
|
+
| `height` | `number` | 그리드 전체 높이 |
|
|
822
|
+
| `columns` | `BGridColumn<T>[]` (`width` optional) | 컬럼 정의 |
|
|
823
|
+
|
|
824
|
+
#### Data / Selection / Row Focus
|
|
825
|
+
|
|
826
|
+
| Prop | Type | Description |
|
|
827
|
+
| ----------------- | ----------------------------------- | ------------------------------------------- |
|
|
828
|
+
| `data` | `BGridDataItem<T>[]` | 행 데이터 (`values` 래핑 필수) |
|
|
829
|
+
| `rowKey` | `React.Key \| React.Key[]` | 행 고유 키 필드 |
|
|
830
|
+
| `selectedRowKey` | `React.Key \| React.Key[]` | 포커스된 행 키 |
|
|
831
|
+
| `rowChecked` | `BGridRowChecked<T>` | 체크박스/라디오 선택 제어 (`onChange` 필수) |
|
|
832
|
+
| `getRowClassName` | `(ri, item) => string \| undefined` | 행 단위 className 지정 |
|
|
833
|
+
|
|
834
|
+
> `rowSelection` / `selectedIds`는 현재 API가 아닙니다. `rowChecked`를 사용해야 합니다.
|
|
835
|
+
|
|
836
|
+
#### Layout / Rendering
|
|
837
|
+
|
|
838
|
+
| Prop | Type | Description |
|
|
839
|
+
| ------------------- | ---------------------------------- | -------------------------------- |
|
|
840
|
+
| `headerHeight` | `number` | 헤더 높이 (기본 30) |
|
|
841
|
+
| `footerHeight` | `number` | 페이지네이션 푸터 높이 (기본 30) |
|
|
842
|
+
| `summaryHeight` | `number` | summary 높이 (기본 30) |
|
|
843
|
+
| `itemHeight` | `number` | 본문 행 컨텐츠 높이 (기본 15) |
|
|
844
|
+
| `itemPadding` | `number` | 행 내부 패딩 (기본 7) |
|
|
845
|
+
| `frozenColumnIndex` | `number` | 고정 컬럼 경계 인덱스 |
|
|
846
|
+
| `frozenRowCount` | `number` | 상단에 고정할 선행 데이터 행 수 |
|
|
847
|
+
| `showLineNumber` | `boolean` | 좌측 라인 번호 표시 |
|
|
848
|
+
| `variant` | `'default' \| 'vertical-bordered'` | 스킨 변형 |
|
|
849
|
+
| `loading` | `boolean` | 전체 오버레이 로딩 |
|
|
850
|
+
| `spinning` | `boolean` | 바디 스피너 로딩 |
|
|
851
|
+
|
|
852
|
+
#### Column / Editing / Events
|
|
853
|
+
|
|
854
|
+
| Prop | Type | Description |
|
|
855
|
+
| ----------------- | -------------------------------------------- | ---------------------------------- |
|
|
856
|
+
| `columnGroups` | `BGridColumnGroupNode[]` | 컬럼 ID 기반 임의 깊이 헤더 그룹 |
|
|
857
|
+
| `columnsGroup` | `BGridColumnGroup[]` | 레거시 2단 헤더 그룹 (deprecated) |
|
|
858
|
+
| `onChangeColumns` | `(columnIndex, info) => void` | 컬럼 폭/순서/그룹 변경 콜백 |
|
|
859
|
+
| `onChangeData` | `(index, columnIndex, item, column) => void` | 셀 편집 데이터 변경 콜백 |
|
|
860
|
+
| `editable` | `boolean` | 편집 모드 활성화 |
|
|
861
|
+
| `editTrigger` | `'click' \| 'dblclick'` | 편집 진입 트리거 (기본 `dblclick`) |
|
|
862
|
+
| `onClick` | `(params) => void` | 셀 클릭 이벤트 |
|
|
863
|
+
|
|
864
|
+
Each `BGridColumn<T>` can specify `editable` and an `editor`. Supported editor configs are the built-in
|
|
865
|
+
`{ type: 'text' }` config and plugin objects created by `defineEditorPlugin()` or the prebuilt editor factories.
|
|
866
|
+
|
|
867
|
+
Nested headers use stable column IDs and can be composed to any depth:
|
|
868
|
+
|
|
869
|
+
```tsx
|
|
870
|
+
<BGrid
|
|
871
|
+
columns={[
|
|
872
|
+
{ id: 'customer', key: 'customer', label: 'Customer', width: 160 },
|
|
873
|
+
{ id: 'region', key: 'region', label: 'Region', width: 120 },
|
|
874
|
+
]}
|
|
875
|
+
columnGroups={[
|
|
876
|
+
{
|
|
877
|
+
id: 'sales',
|
|
878
|
+
label: 'Sales',
|
|
879
|
+
children: [{ id: 'customer-info', label: 'Customer info', children: ['customer', 'region'] }],
|
|
880
|
+
},
|
|
881
|
+
]}
|
|
882
|
+
{...props}
|
|
883
|
+
/>
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
Leaf headers can be styled with `BGridColumn.headerClassName` or `BGridColumn.headerStyle`. Nested group
|
|
887
|
+
headers support `BGridColumnGroupNode.className` and `BGridColumnGroupNode.headerStyle`; the same styling is
|
|
888
|
+
preserved when a header is rendered in the frozen region.
|
|
889
|
+
|
|
890
|
+
#### Extra Features
|
|
891
|
+
|
|
892
|
+
| Prop | Type | Description |
|
|
893
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
|
|
894
|
+
| `sort` | `BGridSortInfo` | 정렬 상태/변경 콜백 |
|
|
895
|
+
| `columnSortable` | `boolean` | 컬럼 drag sort 제어 |
|
|
896
|
+
| `page` | `BGridPage` | 페이지네이션 상태/콜백 |
|
|
897
|
+
| `summary` | `{ columns, position }` | 상/하단 summary 행 |
|
|
898
|
+
| `cellMergeOptions` | `{ columnsMap }` | 셀 병합 옵션 |
|
|
899
|
+
| `cellSelectionOptions` | `{ enabled?: boolean; clearOnEscape?: boolean; clearOnOutsideClick?: boolean; maxClipboardCells?: number; maxClipboardTextLength?: number; onCopyError?: (error) => void; onPasteError?: (error) => void; createRowOnPaste?: (context) => BGridDataItem<T> }` | 셀 선택 활성화 및 클립보드 룰 (기본 활성화) |
|
|
900
|
+
| `cellNavigationOptions` | `BGridCellNavigationOptions` | 활성 셀과 키보드 이동·편집 진입 정책 |
|
|
901
|
+
| `reorder` | `BGridReorderInfo<T>` | 행 reorder 설정 |
|
|
902
|
+
| `pivot` | `BGridPivotOptions<T>` | 피벗 테이블 렌더링 설정 |
|
|
903
|
+
| `msg` | `{ emptyList?: string }` | 커스텀 메시지 |
|
|
904
|
+
| `searchOptions` | `BGridSearchOptions<T>` | 검색 UI, 단축키, 문자열 getter와 제어형 상태 |
|
|
905
|
+
| `contextMenuOptions` | `BGridContextMenuOptions<T>` | 본문 셀 우클릭/키보드 메뉴 항목 |
|
|
906
|
+
|
|
907
|
+
## Update Note
|
|
908
|
+
|
|
909
|
+
### v1.5
|
|
910
|
+
|
|
911
|
+
- rowChecked 속성 추가 (rowChecked > isRadio, rowChecked > disabled)
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
<BGrid<IListItem>
|
|
915
|
+
width={containerWidth}
|
|
916
|
+
height={containerHeight}
|
|
917
|
+
headerHeight={35}
|
|
918
|
+
data={sortedList}
|
|
919
|
+
columns={columns}
|
|
920
|
+
onChangeColumns={(columnIndex, { width, columns }) => {
|
|
921
|
+
console.log('onChangeColumnWidths', columnIndex, width, columns);
|
|
922
|
+
setColumns(columns);
|
|
923
|
+
}}
|
|
924
|
+
rowChecked={{
|
|
925
|
+
disabled: (ri, item) => ri === 0,
|
|
926
|
+
isRadio: true,
|
|
927
|
+
checkedRowKeys: checkedKeys,
|
|
928
|
+
onChange: (ids, keys, selectedAll) => {
|
|
929
|
+
console.log('onChange rowSelection', ids, keys, selectedAll);
|
|
930
|
+
setCheckedKeys(keys);
|
|
931
|
+
},
|
|
932
|
+
}}
|
|
933
|
+
sort={{
|
|
934
|
+
sortParams,
|
|
935
|
+
onChange: sortParams => {
|
|
936
|
+
console.log('onChange: sortParams', sortParams);
|
|
937
|
+
setSortParams(sortParams);
|
|
938
|
+
},
|
|
939
|
+
}}
|
|
940
|
+
showLineNumber
|
|
941
|
+
rowKey={'nation'}
|
|
942
|
+
/>
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
### V1.4
|
|
946
|
+
|
|
947
|
+
- columnsGroup 타입변경
|
|
948
|
+
기존 columnsIndex: []에서 start, end 지정 형태로 변경되었습니다.
|
|
949
|
+
|
|
950
|
+
```typescript jsx
|
|
951
|
+
[{ label: '묶음', groupStartIndex: 2, groupEndIndex: 4, align: 'center' }];
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
- onChangeColumns 속성 변경
|
|
955
|
+
|
|
956
|
+
```typescript jsx
|
|
957
|
+
// onChangeColumns Type
|
|
958
|
+
onChangeColumns?: (
|
|
959
|
+
columnIndex: number | null,
|
|
960
|
+
info: {
|
|
961
|
+
width?: number;
|
|
962
|
+
columns: BGridColumn<T>[];
|
|
963
|
+
columnsGroup?: BGridColumnGroup[];
|
|
964
|
+
columnGroups?: BGridColumnGroupNode[];
|
|
965
|
+
},
|
|
966
|
+
) => void;
|
|
967
|
+
|
|
968
|
+
// onChangeColumns에서 변경된 컬럼과 컬럼 그룹을 받을 수 있습니다
|
|
969
|
+
<BGrid
|
|
970
|
+
/*...*/
|
|
971
|
+
onChangeColumns={(columnIndex, { columns, columnsGroup }) => {
|
|
972
|
+
console.log('onChangeColumnWidths', columnIndex, columns, columnsGroup);
|
|
973
|
+
setColumns(columns);
|
|
974
|
+
setColumnsGroup(columnsGroup);
|
|
975
|
+
}}
|
|
976
|
+
/>
|
|
977
|
+
```
|