@alaarab/ogrid-react-fluent 2.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/dist/esm/ColumnChooser/ColumnChooser.js +152 -0
- package/dist/esm/ColumnChooser/ColumnChooser.module.css +15 -0
- package/dist/esm/ColumnHeaderFilter/ColumnHeaderFilter.js +49 -0
- package/dist/esm/ColumnHeaderFilter/ColumnHeaderFilter.module.css +374 -0
- package/dist/esm/ColumnHeaderFilter/MultiSelectFilterPopover.js +9 -0
- package/dist/esm/ColumnHeaderFilter/PeopleFilterPopover.js +9 -0
- package/dist/esm/ColumnHeaderFilter/TextFilterPopover.js +12 -0
- package/dist/esm/ColumnHeaderFilter/index.js +1 -0
- package/dist/esm/DataGridTable/DataGridTable.js +265 -0
- package/dist/esm/DataGridTable/DataGridTable.module.css +592 -0
- package/dist/esm/DataGridTable/GridContextMenu.js +35 -0
- package/dist/esm/DataGridTable/InlineCellEditor.js +6 -0
- package/dist/esm/DataGridTable/StatusBar.js +7 -0
- package/dist/esm/FluentDataTable/FluentDataTable.js +18 -0
- package/dist/esm/FluentDataTable/index.js +1 -0
- package/dist/esm/PaginationControls/PaginationControls.js +20 -0
- package/dist/esm/PaginationControls/PaginationControls.module.css +74 -0
- package/dist/esm/index.js +8 -0
- package/dist/types/ColumnChooser/ColumnChooser.d.ts +10 -0
- package/dist/types/ColumnHeaderFilter/ColumnHeaderFilter.d.ts +22 -0
- package/dist/types/ColumnHeaderFilter/MultiSelectFilterPopover.d.ts +19 -0
- package/dist/types/ColumnHeaderFilter/PeopleFilterPopover.d.ts +14 -0
- package/dist/types/ColumnHeaderFilter/TextFilterPopover.d.ts +13 -0
- package/dist/types/ColumnHeaderFilter/index.d.ts +1 -0
- package/dist/types/DataGridTable/DataGridTable.d.ts +5 -0
- package/dist/types/DataGridTable/GridContextMenu.d.ts +10 -0
- package/dist/types/DataGridTable/InlineCellEditor.d.ts +12 -0
- package/dist/types/DataGridTable/StatusBar.d.ts +16 -0
- package/dist/types/FluentDataTable/FluentDataTable.d.ts +7 -0
- package/dist/types/FluentDataTable/index.d.ts +1 -0
- package/dist/types/PaginationControls/PaginationControls.d.ts +12 -0
- package/dist/types/index.d.ts +6 -0
- package/package.json +64 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
.tableScrollContent {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
width: 100%;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
min-height: 100%;
|
|
8
|
+
/* Match wrapper so any gap to the right is not transparent (no purple stripe showing through) */
|
|
9
|
+
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.tableWidthAnchor {
|
|
13
|
+
position: relative;
|
|
14
|
+
/* When table fits (data-auto-fit): fill 100% so last column gets space. When overflow: size to content for scroll. */
|
|
15
|
+
width: max-content;
|
|
16
|
+
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* When table fits in container: fill full width so columns (including Tags) use the space, no dead zone on the right */
|
|
20
|
+
.tableWrapper[data-auto-fit=true] .tableWidthAnchor {
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Always use full container width (matches pagination); columns fill the space */
|
|
25
|
+
.tableWrapper {
|
|
26
|
+
position: relative;
|
|
27
|
+
flex: 1;
|
|
28
|
+
min-height: 0;
|
|
29
|
+
/* Default: no horizontal scroll unless we explicitly allow overflow (wide tables). */
|
|
30
|
+
overflow-x: hidden;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
width: 100%;
|
|
33
|
+
min-width: 0;
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
/* Border is applied to the grid itself so we don't draw an empty bordered area
|
|
37
|
+
when the grid content is narrower than the container. */
|
|
38
|
+
border: none;
|
|
39
|
+
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
40
|
+
-webkit-overflow-scrolling: touch;
|
|
41
|
+
will-change: scroll-position;
|
|
42
|
+
/* Wide tables: allow horizontal scroll */
|
|
43
|
+
}
|
|
44
|
+
.tableWrapper[data-overflow-x=true] {
|
|
45
|
+
overflow-x: auto;
|
|
46
|
+
}
|
|
47
|
+
.tableWrapper {
|
|
48
|
+
/* Empty state: no horizontal scroll – can't scroll right into white space */
|
|
49
|
+
}
|
|
50
|
+
.tableWrapper[data-empty=true] {
|
|
51
|
+
overflow-x: hidden;
|
|
52
|
+
}
|
|
53
|
+
.tableWrapper {
|
|
54
|
+
/* Scoped by data-column-count so our overrides apply; variable is set on this wrapper */
|
|
55
|
+
}
|
|
56
|
+
.tableWrapper[data-column-count] :global {
|
|
57
|
+
/* Let the grid/table size to its content.
|
|
58
|
+
This avoids "last column becomes a giant spacer" when the container is wider than the columns,
|
|
59
|
+
and keeps header actions (sort/filter) from drifting all the way to the far right.
|
|
60
|
+
The wrapper still stays width:100% so pagination aligns. */
|
|
61
|
+
}
|
|
62
|
+
.tableWrapper[data-column-count] :global .fui-DataGrid {
|
|
63
|
+
/* Width behavior is controlled by a CSS var from the React wrapper. */
|
|
64
|
+
width: var(--data-table-width, fit-content) !important;
|
|
65
|
+
max-width: 100% !important;
|
|
66
|
+
min-width: var(--data-table-min-width, max-content) !important;
|
|
67
|
+
box-sizing: border-box !important;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
}
|
|
70
|
+
.tableWrapper[data-column-count] :global {
|
|
71
|
+
/* Use auto layout so Fluent sizing/resize works naturally. */
|
|
72
|
+
}
|
|
73
|
+
.tableWrapper[data-column-count] :global .fui-DataGrid .fui-Table {
|
|
74
|
+
width: var(--data-table-width, fit-content) !important;
|
|
75
|
+
max-width: 100% !important;
|
|
76
|
+
min-width: var(--data-table-min-width, max-content) !important;
|
|
77
|
+
table-layout: auto !important;
|
|
78
|
+
box-sizing: border-box !important;
|
|
79
|
+
border: none !important;
|
|
80
|
+
outline: none !important;
|
|
81
|
+
}
|
|
82
|
+
.tableWrapper[data-column-count] :global {
|
|
83
|
+
/* Rows span full table width */
|
|
84
|
+
}
|
|
85
|
+
.tableWrapper[data-column-count] :global .fui-DataGrid .fui-TableRow,
|
|
86
|
+
.tableWrapper[data-column-count] :global .fui-DataGrid .fui-DataGridRow {
|
|
87
|
+
width: 100% !important;
|
|
88
|
+
min-width: 100% !important;
|
|
89
|
+
}
|
|
90
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeader {
|
|
91
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
92
|
+
}
|
|
93
|
+
.tableWrapper[data-column-count] :global {
|
|
94
|
+
/* Let Fluent's column sizing system own widths (resizableColumns + columnSizingOptions).
|
|
95
|
+
We only enforce a min-width + sane box sizing. */
|
|
96
|
+
}
|
|
97
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeaderCell,
|
|
98
|
+
.tableWrapper[data-column-count] :global .fui-DataGridCell {
|
|
99
|
+
/* Do NOT use !important here: Fluent sets inline width/min/max during resize. */
|
|
100
|
+
min-width: 80px;
|
|
101
|
+
box-sizing: border-box !important;
|
|
102
|
+
}
|
|
103
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeaderCell {
|
|
104
|
+
min-width: var(--data-table-cell-min-width, 80px);
|
|
105
|
+
white-space: nowrap !important;
|
|
106
|
+
position: relative;
|
|
107
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
108
|
+
font-size: 14px;
|
|
109
|
+
border-right: 1px solid var(--colorNeutralStroke1, #c4c4c4);
|
|
110
|
+
}
|
|
111
|
+
.tableWrapper[data-column-count] :global {
|
|
112
|
+
/* No extra right border on last column – wrapper provides right edge (row may have trailing <i> so use last-of-type) */
|
|
113
|
+
}
|
|
114
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeader .fui-DataGridRow .fui-DataGridHeaderCell:last-of-type,
|
|
115
|
+
.tableWrapper[data-column-count] :global .fui-DataGridBody .fui-DataGridRow .fui-DataGridCell:last-of-type {
|
|
116
|
+
border-right: none !important;
|
|
117
|
+
}
|
|
118
|
+
.tableWrapper[data-column-count] :global .fui-DataGridCell {
|
|
119
|
+
min-width: var(--data-table-cell-min-width, 80px);
|
|
120
|
+
overflow: hidden !important;
|
|
121
|
+
text-overflow: ellipsis !important;
|
|
122
|
+
white-space: nowrap !important;
|
|
123
|
+
font-size: 12px;
|
|
124
|
+
padding: 0 !important;
|
|
125
|
+
height: 1px;
|
|
126
|
+
border-right: 1px solid var(--colorNeutralStroke1, #c4c4c4);
|
|
127
|
+
/* Prevent long unbroken content from forcing intrinsic widths / overlap */
|
|
128
|
+
}
|
|
129
|
+
.tableWrapper[data-column-count] :global .fui-DataGridCell > * {
|
|
130
|
+
min-width: 0;
|
|
131
|
+
}
|
|
132
|
+
.tableWrapper[data-column-count] :global .fui-DataGridCell .fui-TableCellLayout {
|
|
133
|
+
font-size: 12px;
|
|
134
|
+
}
|
|
135
|
+
.tableWrapper[data-column-count] :global {
|
|
136
|
+
/* Resize handle: contained so focus ring doesn't create a purple stripe to the right */
|
|
137
|
+
}
|
|
138
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeaderCell__aside {
|
|
139
|
+
display: flex !important;
|
|
140
|
+
cursor: col-resize !important;
|
|
141
|
+
position: absolute !important;
|
|
142
|
+
right: 0 !important;
|
|
143
|
+
top: 0 !important;
|
|
144
|
+
bottom: 0 !important;
|
|
145
|
+
width: 16px !important;
|
|
146
|
+
z-index: 1 !important;
|
|
147
|
+
outline: none !important;
|
|
148
|
+
border-radius: 0 !important;
|
|
149
|
+
}
|
|
150
|
+
.tableWrapper[data-column-count] :global {
|
|
151
|
+
/* Fix: Fluent renders a resize handle on the last header cell.
|
|
152
|
+
The handle has negative horizontal margins (hit target), which bleeds past the table edge
|
|
153
|
+
and makes the last header look like it has extra space vs the body cells.
|
|
154
|
+
Hide the resize affordance on the last column header. */
|
|
155
|
+
}
|
|
156
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeader .fui-DataGridRow .fui-DataGridHeaderCell:last-of-type .fui-DataGridHeaderCell__aside,
|
|
157
|
+
.tableWrapper[data-column-count] :global .fui-DataGridHeader .fui-DataGridRow .fui-DataGridHeaderCell:last-of-type .fui-TableResizeHandle {
|
|
158
|
+
display: none !important;
|
|
159
|
+
}
|
|
160
|
+
.tableWrapper[data-column-count] :global {
|
|
161
|
+
/* No extra bottom border: last row (row + cells) – wrapper provides bottom edge so no double line */
|
|
162
|
+
}
|
|
163
|
+
.tableWrapper[data-column-count] :global .fui-DataGridBody .fui-DataGridRow:last-child {
|
|
164
|
+
border-bottom: none !important;
|
|
165
|
+
}
|
|
166
|
+
.tableWrapper[data-column-count] :global .fui-DataGridBody .fui-DataGridRow:last-child .fui-DataGridCell {
|
|
167
|
+
border-bottom: none !important;
|
|
168
|
+
}
|
|
169
|
+
.tableWrapper[data-column-count] :global .fui-Link {
|
|
170
|
+
color: var(--colorBrandForeground1, #0f6cbd) !important;
|
|
171
|
+
font-weight: 600 !important;
|
|
172
|
+
text-decoration: none !important;
|
|
173
|
+
}
|
|
174
|
+
.tableWrapper[data-column-count] :global .fui-Link:hover {
|
|
175
|
+
text-decoration: underline !important;
|
|
176
|
+
color: var(--colorBrandForeground1Hover, #115ea3) !important;
|
|
177
|
+
}
|
|
178
|
+
.tableWrapper[data-column-count] :global .fui-Link:active {
|
|
179
|
+
color: var(--colorBrandForeground1Pressed, #0c3b5e) !important;
|
|
180
|
+
}
|
|
181
|
+
.tableWrapper {
|
|
182
|
+
/* When table fits: fill 100% and distribute column width evenly so last column (e.g. Tags) isn't tiny */
|
|
183
|
+
}
|
|
184
|
+
.tableWrapper[data-auto-fit=true][data-column-count] :global .fui-DataGrid {
|
|
185
|
+
width: 100% !important;
|
|
186
|
+
}
|
|
187
|
+
.tableWrapper[data-auto-fit=true][data-column-count] :global .fui-DataGrid .fui-Table {
|
|
188
|
+
width: 100% !important;
|
|
189
|
+
}
|
|
190
|
+
.tableWrapper[data-auto-fit=true][data-column-count] :global {
|
|
191
|
+
/* Equal base width for all columns — override Fluent's inline width/min-width/max-width */
|
|
192
|
+
}
|
|
193
|
+
.tableWrapper[data-auto-fit=true][data-column-count] :global .fui-DataGridHeaderCell,
|
|
194
|
+
.tableWrapper[data-auto-fit=true][data-column-count] :global .fui-DataGridCell {
|
|
195
|
+
width: calc(100% / var(--data-table-column-count, 1)) !important;
|
|
196
|
+
min-width: 0 !important;
|
|
197
|
+
max-width: none !important;
|
|
198
|
+
}
|
|
199
|
+
.tableWrapper {
|
|
200
|
+
/* Selection column must stay 48px even in auto-fit mode (needs same specificity depth to win) */
|
|
201
|
+
}
|
|
202
|
+
.tableWrapper[data-auto-fit=true][data-column-count] .selectionHeaderCellWrapper, .tableWrapper[data-auto-fit=true][data-column-count] .selectionCellWrapper {
|
|
203
|
+
width: 48px !important;
|
|
204
|
+
min-width: 48px !important;
|
|
205
|
+
max-width: 48px !important;
|
|
206
|
+
}
|
|
207
|
+
.tableWrapper {
|
|
208
|
+
/* Hide resize handle on last column only when table fits (autoFitColumns); when overflow/scroll, last column can resize (Fluent docs) */
|
|
209
|
+
}
|
|
210
|
+
.tableWrapper[data-column-count][data-auto-fit=true] :global(.fui-DataGridHeader .fui-DataGridRow .fui-DataGridHeaderCell:last-of-type .fui-DataGridHeaderCell__aside) {
|
|
211
|
+
display: none !important;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.dataGrid {
|
|
215
|
+
min-width: 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.groupHeaderRow th {
|
|
219
|
+
background: var(--ogrid-header-bg, #f5f5f5);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.groupHeaderCell {
|
|
223
|
+
text-align: center;
|
|
224
|
+
font-weight: 600;
|
|
225
|
+
border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
|
|
226
|
+
padding: 6px 10px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.leafHeaderCellSpan {
|
|
230
|
+
font-weight: 600;
|
|
231
|
+
padding: 6px 10px;
|
|
232
|
+
background: var(--ogrid-header-bg, #f5f5f5);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.selectionHeaderCellWrapper {
|
|
236
|
+
width: 48px !important;
|
|
237
|
+
min-width: 48px !important;
|
|
238
|
+
max-width: 48px !important;
|
|
239
|
+
padding: 0 !important;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.selectionCellWrapper {
|
|
243
|
+
width: 48px !important;
|
|
244
|
+
min-width: 48px !important;
|
|
245
|
+
max-width: 48px !important;
|
|
246
|
+
padding: 0 !important;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.selectionHeaderCell {
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
width: 100%;
|
|
254
|
+
height: 100%;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.selectionCell {
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
width: 100%;
|
|
262
|
+
height: 100%;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.selectedRow :global .fui-DataGridCell {
|
|
266
|
+
background-color: var(--colorNeutralBackground1Selected, #e6f0fb) !important;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.selectableGrid :global .fui-DataGridBody .fui-DataGridRow:hover .fui-DataGridCell {
|
|
270
|
+
background-color: var(--colorSubtleBackgroundHover, #f5f5f5);
|
|
271
|
+
cursor: pointer;
|
|
272
|
+
}
|
|
273
|
+
.selectableGrid :global .fui-DataGridBody .fui-DataGridRow:hover.selectedRow .fui-DataGridCell {
|
|
274
|
+
background-color: var(--colorNeutralBackground1Hover, #dae8f8) !important;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.selectableGrid :global(.fui-DataGridBody .fui-DataGridRow) {
|
|
278
|
+
cursor: pointer;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.cellContent {
|
|
282
|
+
width: 100%;
|
|
283
|
+
height: 100%;
|
|
284
|
+
display: flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
padding: 4px 8px;
|
|
287
|
+
box-sizing: border-box;
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
text-overflow: ellipsis;
|
|
290
|
+
white-space: nowrap;
|
|
291
|
+
user-select: none;
|
|
292
|
+
outline: none;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.activeCellContent {
|
|
296
|
+
outline: 2px solid var(--ogrid-selection, #217346) !important;
|
|
297
|
+
outline-offset: -1px;
|
|
298
|
+
z-index: 2;
|
|
299
|
+
position: relative;
|
|
300
|
+
overflow: visible;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.cellInRange {
|
|
304
|
+
background-color: var(--ogrid-bg-range, rgba(33, 115, 70, 0.12)) !important;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
:global([data-drag-range]) {
|
|
308
|
+
background-color: var(--ogrid-bg-range, rgba(33, 115, 70, 0.12)) !important;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.cellCut {
|
|
312
|
+
background-color: var(--colorNeutralBackground1Hover, rgba(0, 0, 0, 0.04)) !important;
|
|
313
|
+
opacity: 0.7;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.fillHandle {
|
|
317
|
+
position: absolute;
|
|
318
|
+
right: -3px;
|
|
319
|
+
bottom: -3px;
|
|
320
|
+
width: 7px;
|
|
321
|
+
height: 7px;
|
|
322
|
+
background: var(--ogrid-selection, #217346);
|
|
323
|
+
border: 1px solid var(--ogrid-bg, #fff);
|
|
324
|
+
border-radius: 1px;
|
|
325
|
+
cursor: crosshair;
|
|
326
|
+
pointer-events: auto;
|
|
327
|
+
z-index: 3;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* Freeze panes: sticky header when freezeRows >= 1 */
|
|
331
|
+
.stickyHeader {
|
|
332
|
+
position: sticky;
|
|
333
|
+
top: 0;
|
|
334
|
+
z-index: 8;
|
|
335
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Freeze panes: sticky first column(s) when freezeCols >= 1; only first column gets left: 0 (multi-column needs width measurement) */
|
|
339
|
+
.freezeColFirst {
|
|
340
|
+
position: sticky;
|
|
341
|
+
left: 0;
|
|
342
|
+
z-index: 6;
|
|
343
|
+
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
344
|
+
will-change: transform;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
:global(.fui-DataGridHeader) .freezeColFirst {
|
|
348
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* Freeze/pinned header cells need top: 0 + highest z-index so they stick in BOTH
|
|
352
|
+
directions — left/right (from .freezeCol/.pinnedCell) AND top (from .stickyHeader).
|
|
353
|
+
Without explicit top, the child's own position: sticky overrides the parent's. */
|
|
354
|
+
.stickyHeader .freezeColFirst,
|
|
355
|
+
.stickyHeader .pinnedLeft,
|
|
356
|
+
.stickyHeader .pinnedRight {
|
|
357
|
+
top: 0;
|
|
358
|
+
z-index: 9;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.activeRow :global .fui-DataGridCell:not(:has(.activeCellContent)) {
|
|
362
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.pinnedCell {
|
|
366
|
+
position: sticky;
|
|
367
|
+
z-index: 6;
|
|
368
|
+
background-color: inherit;
|
|
369
|
+
will-change: transform;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.pinnedLeft {
|
|
373
|
+
left: 0;
|
|
374
|
+
}
|
|
375
|
+
.pinnedLeft::after {
|
|
376
|
+
content: "";
|
|
377
|
+
position: absolute;
|
|
378
|
+
top: 0;
|
|
379
|
+
right: -4px;
|
|
380
|
+
bottom: 0;
|
|
381
|
+
width: 4px;
|
|
382
|
+
background: linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent);
|
|
383
|
+
pointer-events: none;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.pinnedRight {
|
|
387
|
+
right: 0;
|
|
388
|
+
}
|
|
389
|
+
.pinnedRight::before {
|
|
390
|
+
content: "";
|
|
391
|
+
position: absolute;
|
|
392
|
+
top: 0;
|
|
393
|
+
left: -4px;
|
|
394
|
+
bottom: 0;
|
|
395
|
+
width: 4px;
|
|
396
|
+
background: linear-gradient(to left, rgba(0, 0, 0, 0.08), transparent);
|
|
397
|
+
pointer-events: none;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.statusBar {
|
|
401
|
+
display: flex;
|
|
402
|
+
align-items: center;
|
|
403
|
+
gap: 16px;
|
|
404
|
+
width: 100%;
|
|
405
|
+
min-width: 0;
|
|
406
|
+
box-sizing: border-box;
|
|
407
|
+
padding: 6px 12px;
|
|
408
|
+
font-size: 12px;
|
|
409
|
+
color: var(--colorNeutralForeground2, #616161);
|
|
410
|
+
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
411
|
+
border-top: 1px solid var(--colorNeutralStroke2, #e0e0e0);
|
|
412
|
+
min-height: 28px;
|
|
413
|
+
user-select: none;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.statusBarItem {
|
|
417
|
+
display: inline-flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
gap: 4px;
|
|
420
|
+
}
|
|
421
|
+
.statusBarItem:not(:last-child)::after {
|
|
422
|
+
content: "";
|
|
423
|
+
display: inline-block;
|
|
424
|
+
width: 1px;
|
|
425
|
+
height: 14px;
|
|
426
|
+
background-color: var(--colorNeutralStroke1, #c4c4c4);
|
|
427
|
+
margin-left: 12px;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.statusBarLabel {
|
|
431
|
+
color: var(--colorNeutralForeground3, #707070);
|
|
432
|
+
font-weight: 400;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.statusBarValue {
|
|
436
|
+
color: var(--colorNeutralForeground1, #242424);
|
|
437
|
+
font-weight: 600;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.contextMenu {
|
|
441
|
+
position: fixed;
|
|
442
|
+
z-index: 10000;
|
|
443
|
+
min-width: 160px;
|
|
444
|
+
padding: 4px 0;
|
|
445
|
+
background: var(--colorNeutralBackground1, #fff);
|
|
446
|
+
border: 1px solid var(--colorNeutralStroke1, #e0e0e0);
|
|
447
|
+
border-radius: var(--borderRadiusMedium, 4px);
|
|
448
|
+
box-shadow: var(--shadow16, 0 4px 16px rgba(0, 0, 0, 0.12));
|
|
449
|
+
outline: none;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.contextMenuItem {
|
|
453
|
+
display: flex;
|
|
454
|
+
align-items: center;
|
|
455
|
+
justify-content: space-between;
|
|
456
|
+
gap: 24px;
|
|
457
|
+
width: 100%;
|
|
458
|
+
padding: 6px 12px;
|
|
459
|
+
border: none;
|
|
460
|
+
background: none;
|
|
461
|
+
font-size: 13px;
|
|
462
|
+
text-align: left;
|
|
463
|
+
cursor: pointer;
|
|
464
|
+
color: var(--colorNeutralForeground1, #242424);
|
|
465
|
+
}
|
|
466
|
+
.contextMenuItem:hover:not(:disabled) {
|
|
467
|
+
background-color: var(--colorSubtleBackgroundHover, #f5f5f5);
|
|
468
|
+
}
|
|
469
|
+
.contextMenuItem:disabled {
|
|
470
|
+
opacity: 0.5;
|
|
471
|
+
cursor: not-allowed;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.contextMenuItemLabel {
|
|
475
|
+
flex: 1;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.contextMenuItemShortcut {
|
|
479
|
+
color: var(--colorNeutralForeground3, rgba(0, 0, 0, 0.4));
|
|
480
|
+
font-size: 0.85em;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.contextMenuDivider {
|
|
484
|
+
height: 1px;
|
|
485
|
+
margin: 4px 0;
|
|
486
|
+
background-color: var(--colorNeutralStroke2, #e0e0e0);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.loadingOverlay {
|
|
490
|
+
position: absolute;
|
|
491
|
+
inset: 0;
|
|
492
|
+
z-index: 2;
|
|
493
|
+
display: flex;
|
|
494
|
+
align-items: center;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
background: rgba(255, 255, 255, 0.7);
|
|
497
|
+
backdrop-filter: blur(1px);
|
|
498
|
+
pointer-events: all;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.loadingOverlayContent {
|
|
502
|
+
display: flex;
|
|
503
|
+
flex-direction: column;
|
|
504
|
+
align-items: center;
|
|
505
|
+
gap: 8px;
|
|
506
|
+
padding: 16px 24px;
|
|
507
|
+
background: var(--colorNeutralBackground1, #ffffff);
|
|
508
|
+
border: 1px solid var(--colorNeutralStroke1, #c4c4c4);
|
|
509
|
+
border-radius: var(--borderRadiusMedium, 4px);
|
|
510
|
+
box-shadow: var(--shadow4, 0 2px 4px rgba(0, 0, 0, 0.14));
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.loadingOverlayText {
|
|
514
|
+
font-size: 13px;
|
|
515
|
+
font-weight: 500;
|
|
516
|
+
color: var(--colorNeutralForeground2, #616161);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.loadingDimmed {
|
|
520
|
+
opacity: 0.6;
|
|
521
|
+
pointer-events: none;
|
|
522
|
+
transition: opacity 0.15s ease;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.emptyStateInGrid {
|
|
526
|
+
display: flex;
|
|
527
|
+
flex-direction: column;
|
|
528
|
+
align-items: center;
|
|
529
|
+
justify-content: center;
|
|
530
|
+
text-align: center;
|
|
531
|
+
padding: 20px 16px;
|
|
532
|
+
min-height: 88px;
|
|
533
|
+
min-width: 0;
|
|
534
|
+
width: 100%;
|
|
535
|
+
box-sizing: border-box;
|
|
536
|
+
border-top: 1px solid var(--colorNeutralStroke2, #e0e0e0);
|
|
537
|
+
background-color: var(--colorNeutralBackground2, #fafafa);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.emptyStateInGridMessageSticky {
|
|
541
|
+
position: sticky;
|
|
542
|
+
left: 50%;
|
|
543
|
+
transform: translateX(-50%);
|
|
544
|
+
display: inline-flex;
|
|
545
|
+
flex-direction: column;
|
|
546
|
+
align-items: center;
|
|
547
|
+
text-align: center;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.emptyStateInGridIcon {
|
|
551
|
+
font-size: 24px;
|
|
552
|
+
margin-bottom: 8px;
|
|
553
|
+
opacity: 0.6;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.emptyStateInGridTitle {
|
|
557
|
+
font-size: 14px;
|
|
558
|
+
font-weight: 600;
|
|
559
|
+
color: var(--colorNeutralForeground1, #242424);
|
|
560
|
+
margin-bottom: 4px;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.emptyStateInGridMessage {
|
|
564
|
+
font-size: 13px;
|
|
565
|
+
color: var(--colorNeutralForeground2, #616161);
|
|
566
|
+
max-width: 100%;
|
|
567
|
+
line-height: 1.5;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.emptyStateInGridLink {
|
|
571
|
+
background: none;
|
|
572
|
+
border: none;
|
|
573
|
+
color: var(--colorBrandForeground1, #0f6cbd);
|
|
574
|
+
text-decoration: underline;
|
|
575
|
+
cursor: pointer;
|
|
576
|
+
padding: 0;
|
|
577
|
+
font-size: inherit;
|
|
578
|
+
font-family: inherit;
|
|
579
|
+
}
|
|
580
|
+
.emptyStateInGridLink:hover {
|
|
581
|
+
color: var(--colorBrandForeground1Hover, #115ea3);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/* Empty state: hide body, keep header and empty message */
|
|
585
|
+
.tableWrapper[data-empty=true] :global(.fui-DataGrid) tbody {
|
|
586
|
+
display: none;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/* Empty state: no extra bottom border (header row is last row, remove its border-bottom) */
|
|
590
|
+
.tableWrapper[data-empty=true] :global(.fui-DataGridHeader .fui-DataGridRow) {
|
|
591
|
+
border-bottom: none !important;
|
|
592
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from '@alaarab/ogrid-react';
|
|
4
|
+
import styles from './DataGridTable.module.css';
|
|
5
|
+
export function GridContextMenu(props) {
|
|
6
|
+
const { x, y, hasSelection, canUndo, canRedo, onClose } = props;
|
|
7
|
+
const ref = React.useRef(null);
|
|
8
|
+
const handlers = React.useMemo(() => getContextMenuHandlers(props), [props]);
|
|
9
|
+
const isDisabled = React.useCallback((item) => {
|
|
10
|
+
if (item.disabledWhenNoSelection && !hasSelection)
|
|
11
|
+
return true;
|
|
12
|
+
if (item.id === 'undo' && !canUndo)
|
|
13
|
+
return true;
|
|
14
|
+
if (item.id === 'redo' && !canRedo)
|
|
15
|
+
return true;
|
|
16
|
+
return false;
|
|
17
|
+
}, [hasSelection, canUndo, canRedo]);
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
const handleClickOutside = (e) => {
|
|
20
|
+
if (ref.current && !ref.current.contains(e.target))
|
|
21
|
+
onClose();
|
|
22
|
+
};
|
|
23
|
+
const handleKeyDown = (e) => {
|
|
24
|
+
if (e.key === 'Escape')
|
|
25
|
+
onClose();
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener('mousedown', handleClickOutside, true);
|
|
28
|
+
document.addEventListener('keydown', handleKeyDown, true);
|
|
29
|
+
return () => {
|
|
30
|
+
document.removeEventListener('mousedown', handleClickOutside, true);
|
|
31
|
+
document.removeEventListener('keydown', handleKeyDown, true);
|
|
32
|
+
};
|
|
33
|
+
}, [onClose]);
|
|
34
|
+
return (_jsx("div", { ref: ref, className: styles.contextMenu, role: "menu", style: { left: x, top: y }, "aria-label": "Grid context menu", children: GRID_CONTEXT_MENU_ITEMS.map((item) => (_jsxs(React.Fragment, { children: [item.dividerBefore && _jsx("div", { className: styles.contextMenuDivider }), _jsxs("button", { type: "button", className: styles.contextMenuItem, onClick: handlers[item.id], disabled: isDisabled(item), children: [_jsx("span", { className: styles.contextMenuItemLabel, children: item.label }), item.shortcut && (_jsx("span", { className: styles.contextMenuItemShortcut, children: formatShortcut(item.shortcut) }))] })] }, item.id))) }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Select, Checkbox } from '@fluentui/react-components';
|
|
3
|
+
import { BaseInlineCellEditor, editorWrapperStyle } from '@alaarab/ogrid-react';
|
|
4
|
+
export function InlineCellEditor(props) {
|
|
5
|
+
return (_jsx(BaseInlineCellEditor, { ...props, renderCheckbox: (checked, onCommit, onCancel) => (_jsx(Checkbox, { checked: checked, onChange: (_, data) => onCommit(!!data.checked), onKeyDown: (e) => e.key === 'Escape' && (e.preventDefault(), onCancel()) })), renderSelect: (value, values, onCommit, onCancel) => (_jsx("div", { style: editorWrapperStyle, children: _jsx(Select, { value: value !== null && value !== undefined ? String(value) : '', onChange: (_, data) => onCommit(data.value), onKeyDown: (e) => e.key === 'Escape' && (e.preventDefault(), onCancel()), children: values.map((v) => (_jsx("option", { value: String(v), children: String(v) }, String(v)))) }) })) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { getStatusBarParts } from '@alaarab/ogrid-react';
|
|
3
|
+
import styles from './DataGridTable.module.css';
|
|
4
|
+
export function StatusBar(props) {
|
|
5
|
+
const parts = getStatusBarParts(props);
|
|
6
|
+
return (_jsx("div", { className: styles.statusBar, role: "status", "aria-live": "polite", children: parts.map((p) => (_jsxs("span", { className: styles.statusBarItem, children: [_jsx("span", { className: styles.statusBarLabel, children: p.label }), _jsx("span", { className: styles.statusBarValue, children: p.value.toLocaleString() })] }, p.key))) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { DataGridTable } from '../DataGridTable/DataGridTable';
|
|
5
|
+
import { ColumnChooser } from '../ColumnChooser/ColumnChooser';
|
|
6
|
+
import { PaginationControls } from '../PaginationControls/PaginationControls';
|
|
7
|
+
import { useOGrid, OGridLayout, } from '@alaarab/ogrid-react';
|
|
8
|
+
const OGridInner = forwardRef(function OGridInner(props, ref) {
|
|
9
|
+
const { dataGridProps, pagination, columnChooser, layout } = useOGrid(props, ref);
|
|
10
|
+
return (_jsx(OGridLayout, { className: layout.className, sideBar: layout.sideBarProps, toolbar: layout.toolbar, toolbarBelow: layout.toolbarBelow, toolbarEnd: columnChooser.placement === 'toolbar' ? (_jsx(ColumnChooser, { columns: columnChooser.columns, visibleColumns: columnChooser.visibleColumns, onVisibilityChange: columnChooser.onVisibilityChange })) : undefined, pagination: _jsx(PaginationControls, { currentPage: pagination.page, pageSize: pagination.pageSize, totalCount: pagination.displayTotalCount, onPageChange: pagination.setPage, onPageSizeChange: (size) => {
|
|
11
|
+
pagination.setPageSize(size);
|
|
12
|
+
pagination.setPage(1);
|
|
13
|
+
}, pageSizeOptions: pagination.pageSizeOptions, entityLabelPlural: pagination.entityLabelPlural }), children: _jsx(DataGridTable, { ...dataGridProps }) }));
|
|
14
|
+
});
|
|
15
|
+
OGridInner.displayName = 'OGrid';
|
|
16
|
+
export const OGrid = React.memo(OGridInner);
|
|
17
|
+
/** @deprecated Use `OGrid` instead. Backward-compat alias. */
|
|
18
|
+
export const FluentDataTable = OGrid;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OGrid, FluentDataTable } from './FluentDataTable';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useMemo, useCallback } from 'react';
|
|
4
|
+
import { Button, Select } from '@fluentui/react-components';
|
|
5
|
+
import { ChevronLeftRegular, ChevronRightRegular, ChevronDoubleLeftRegular, ChevronDoubleRightRegular, } from '@fluentui/react-icons';
|
|
6
|
+
import { getPaginationViewModel } from '@alaarab/ogrid-react';
|
|
7
|
+
import styles from './PaginationControls.module.css';
|
|
8
|
+
export const PaginationControls = React.memo((props) => {
|
|
9
|
+
const { currentPage, pageSize, totalCount, onPageChange, onPageSizeChange, pageSizeOptions, entityLabelPlural, className } = props;
|
|
10
|
+
const labelPlural = entityLabelPlural ?? 'items';
|
|
11
|
+
const vm = useMemo(() => getPaginationViewModel(currentPage, pageSize, totalCount, pageSizeOptions ? { pageSizeOptions } : undefined), [currentPage, pageSize, totalCount, pageSizeOptions]);
|
|
12
|
+
const handlePageSizeChange = useCallback((_e, data) => {
|
|
13
|
+
onPageSizeChange(Number(data.value));
|
|
14
|
+
}, [onPageSizeChange]);
|
|
15
|
+
if (!vm) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const { pageNumbers, showStartEllipsis, showEndEllipsis, totalPages, startItem, endItem } = vm;
|
|
19
|
+
return (_jsxs("div", { className: `${styles.pagination} ${className || ''}`, role: "navigation", "aria-label": "Pagination", children: [_jsxs("div", { className: styles.paginationInfo, children: ["Showing ", startItem, " to ", endItem, " of ", totalCount.toLocaleString(), " ", labelPlural] }), _jsxs("div", { className: styles.paginationControls, children: [_jsx(Button, { appearance: "outline", shape: "circular", size: "small", icon: _jsx(ChevronDoubleLeftRegular, {}), onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "First page", className: styles.navBtn }), _jsx(Button, { appearance: "outline", shape: "circular", size: "small", icon: _jsx(ChevronLeftRegular, {}), onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, "aria-label": "Previous page", className: styles.navBtn }), _jsxs("div", { className: styles.pageNumbers, children: [showStartEllipsis && (_jsxs(_Fragment, { children: [_jsx(Button, { appearance: "outline", size: "small", shape: "rounded", onClick: () => onPageChange(1), "aria-label": "Page 1", className: styles.pageBtn, children: "1" }), _jsx("span", { className: styles.ellipsis, "aria-hidden": true, children: "\u2026" })] })), pageNumbers.map((pageNum) => (_jsx(Button, { appearance: currentPage === pageNum ? 'primary' : 'outline', size: "small", shape: "rounded", onClick: () => onPageChange(pageNum), "aria-label": `Page ${pageNum}`, "aria-current": currentPage === pageNum ? 'page' : undefined, className: styles.pageBtn, children: pageNum }, pageNum))), showEndEllipsis && (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.ellipsis, "aria-hidden": true, children: "\u2026" }), _jsx(Button, { appearance: "outline", size: "small", shape: "rounded", onClick: () => onPageChange(totalPages), "aria-label": `Page ${totalPages}`, className: styles.pageBtn, children: totalPages })] }))] }), _jsx(Button, { appearance: "outline", shape: "circular", size: "small", icon: _jsx(ChevronRightRegular, {}), onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, "aria-label": "Next page", className: styles.navBtn }), _jsx(Button, { appearance: "outline", shape: "circular", size: "small", icon: _jsx(ChevronDoubleRightRegular, {}), onClick: () => onPageChange(totalPages), disabled: currentPage >= totalPages, "aria-label": "Last page", className: styles.navBtn })] }), _jsxs("div", { className: styles.pageSizeSelector, children: [_jsx("span", { className: styles.pageSizeLabel, children: "Rows" }), _jsx(Select, { value: String(pageSize), onChange: handlePageSizeChange, size: "small", appearance: "outline", "aria-label": "Rows per page", className: styles.pageSizeSelect, children: vm.pageSizeOptions.map((n) => (_jsx("option", { value: n, children: n }, n))) })] })] }));
|
|
20
|
+
});
|