fluentui-extended 2026.9.6 → 2026.9.16-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +100 -0
- package/dist/index.d.mts +2026 -142
- package/dist/index.d.ts +2026 -142
- package/dist/index.js +7203 -3169
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7132 -3161
- package/dist/index.mjs.map +1 -1
- package/package.json +28 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
> Version format: `YYYY.M.DD` (e.g., `2026.8.30` = August 30, 2026)
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
- ✨ **[DataGrid](docs/DataGrid.md)** — a virtualised, editable grid on TanStack Table and TanStack
|
|
8
|
+
Virtual. Two production apps had each grown their own copy of the same machinery — row
|
|
9
|
+
virtualisation, column drag and resize, grouping with subtotals, a filter row, density, inline
|
|
10
|
+
editors and layout persistence — and the copies had begun to drift. This is that machinery,
|
|
11
|
+
extracted: fetching, saving and the toolbar stay with the consumer.
|
|
12
|
+
|
|
13
|
+
Notable behaviour: `getCellValue` layers unsaved edits over the stored record, and sorting,
|
|
14
|
+
filtering, grouping and the footer totals all read through it — so the grid orders by what is on
|
|
15
|
+
screen rather than by what the server last returned. Column layout restores during the first
|
|
16
|
+
render rather than in an effect, so the grid paints its saved layout instead of reflowing a frame
|
|
17
|
+
later, and the storage key includes the origin so dev, test and production orgs in one browser do
|
|
18
|
+
not share one layout. Sorting ascends first whatever the data type, unlike TanStack's default.
|
|
19
|
+
|
|
20
|
+
Also exports `useDataGrid`, the headless state machine, plus the chrome components and style hook,
|
|
21
|
+
so a bespoke render does not have to fork the component.
|
|
22
|
+
|
|
23
|
+
Hooks added so the grid can replace the two hand-built project grids (tracked in
|
|
24
|
+
`enhancements/DataGrid-replace-project-grids.md`):
|
|
25
|
+
- **States:** custom and restyled cell and row states (`cellStates`, `rowStates`), layered row
|
|
26
|
+
states from `getRowState`, and built-in `deleted` and `placeholder` states.
|
|
27
|
+
- **Cells:** `getCellTooltip`.
|
|
28
|
+
- **Groups:** banner hooks (`renderGroupHeader`, `groupContextMenu`, `onGroupDoubleClick`,
|
|
29
|
+
`groupSelection`), and row drops that report `toGroup`.
|
|
30
|
+
- **Keyboard:** spreadsheet keys — Tab, F2, type-over, and caret-aware arrows.
|
|
31
|
+
- **Totals:** function summaries, `includeInSummary` and `groupSubtotalsMinGroups`.
|
|
32
|
+
- **Visibility and views:** controlled visibility, `forceHiddenColumns`, `columnGroup`, and
|
|
33
|
+
`getLayout` / `applyLayout`.
|
|
34
|
+
- **Row chrome and toolbar:** `renderRowIndicator`, `showEditableHints`, `highlightRow`, and
|
|
35
|
+
`renderToolbarSearch`.
|
|
36
|
+
|
|
37
|
+
Fixes in the same pass:
|
|
38
|
+
- Changing `persist.scope` while mounted now loads that scope's layout. It used to overwrite it
|
|
39
|
+
with the current one.
|
|
40
|
+
- Nested group subtotals and counts now come from leaf rows rather than inner banners.
|
|
41
|
+
- Hiding a column while grouped no longer saves the grouped column as hidden.
|
|
42
|
+
- Keys pressed inside a cell's popup no longer move the active cell.
|
|
43
|
+
|
|
44
|
+
`cellStateClass` is replaced by `cellStateProps`.
|
|
45
|
+
|
|
46
|
+
- 📦 `@tanstack/react-table`, `@tanstack/react-virtual` and the three `@dnd-kit` packages are
|
|
47
|
+
**optional** peer dependencies. Nothing outside `DataGrid` imports them, so consumers using only
|
|
48
|
+
the field components ship none of it.
|
|
49
|
+
|
|
5
50
|
## 2026.9.6
|
|
6
51
|
|
|
7
52
|
- ✨ **`size` on [OptionSetField](README.md#optionsetfield)**, forwarded to the underlying Combobox.
|
package/README.md
CHANGED
|
@@ -103,6 +103,36 @@ sorting, and lookups rendered as names rather than GUIDs.
|
|
|
103
103
|
|
|
104
104
|

|
|
105
105
|
|
|
106
|
+
### DataGrid
|
|
107
|
+
|
|
108
|
+
A virtualised, editable grid on TanStack Table and TanStack Virtual, for the spreadsheet-style
|
|
109
|
+
screens a subgrid cannot carry: thousands of rows, inline editors, column drag and resize,
|
|
110
|
+
grouping with subtotals, and a column layout that survives a reload.
|
|
111
|
+
|
|
112
|
+
Bring your own rows — fetching and saving stay yours. The grid libraries are **optional** peer
|
|
113
|
+
dependencies, so nothing ships them unless you use this component.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install @tanstack/react-table @tanstack/react-virtual \
|
|
117
|
+
@dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<DataGrid
|
|
122
|
+
data={rows}
|
|
123
|
+
columns={columns}
|
|
124
|
+
getRowId={(row) => row.id}
|
|
125
|
+
getCellValue={(row, key, rowId) => pending[rowId]?.[key] ?? row[key]}
|
|
126
|
+
onCellChange={stageEdit}
|
|
127
|
+
getRowState={(_, rowId) => (pending[rowId] ? 'dirty' : null)}
|
|
128
|
+
selectable
|
|
129
|
+
groupBy="category"
|
|
130
|
+
persist={{ scope: 'quote-lines' }}
|
|
131
|
+
/>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**[Read more →](https://github.com/garethcheyne/npm-fluentui-extended/blob/main/docs/DataGrid.md)** - columns, unsaved-edit layering, persistence, and the headless `useDataGrid` hook.
|
|
135
|
+
|
|
106
136
|
### DateTimeField
|
|
107
137
|
|
|
108
138
|
A date/time field that respects the attribute's Dynamics `DateTimeBehavior`, so `DateOnly` values
|
|
@@ -143,6 +173,76 @@ selections render as the usual Lookup badges and multi-select comes for free.
|
|
|
143
173
|
|
|
144
174
|

|
|
145
175
|
|
|
176
|
+
### AddressLookup
|
|
177
|
+
|
|
178
|
+
A preconfigured `Lookup` for Google Places. Type an address, hover a suggestion to see it on a
|
|
179
|
+
map, and the selection arrives flattened into the columns an address is stored in.
|
|
180
|
+
|
|
181
|
+
Google bills per request, so the work is split the way the API prices it: typing buys
|
|
182
|
+
Autocomplete predictions only, and a place is resolved once - when its card opens, or when the
|
|
183
|
+
row is chosen, whichever happens first. The result is cached, so hovering then clicking the same
|
|
184
|
+
suggestion costs a single Place Details call.
|
|
185
|
+
|
|
186
|
+
Each suggestion is iconed by what it actually is - a street address, a building, a suburb, a
|
|
187
|
+
region, an airport, a shop - rather than every row carrying the same pin, so a list of mixed
|
|
188
|
+
results can be read at a glance.
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { AddressLookup } from 'fluentui-extended';
|
|
192
|
+
import type { PlaceAddress } from 'fluentui-extended';
|
|
193
|
+
|
|
194
|
+
const [address, setAddress] = useState<PlaceAddress | null>(null);
|
|
195
|
+
|
|
196
|
+
<AddressLookup
|
|
197
|
+
apiKey={googleMapsKey}
|
|
198
|
+
label="Address"
|
|
199
|
+
value={address}
|
|
200
|
+
onSelect={(picked) => setAddress(picked)}
|
|
201
|
+
countries={['nz', 'au']}
|
|
202
|
+
stateShortName
|
|
203
|
+
countryShortName
|
|
204
|
+
/>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`onSelect` receives the flattened address and the place it came from:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
{
|
|
211
|
+
placeId: 'ChIJ...',
|
|
212
|
+
formattedAddress: '1 Queen Street, Auckland CBD, Auckland 1010, New Zealand',
|
|
213
|
+
building: '', street: '1 Queen Street', suburb: 'Auckland CBD',
|
|
214
|
+
city: 'Auckland', state: 'AUK', postcode: '1010', country: 'NZ',
|
|
215
|
+
latitude: -36.8442, longitude: 174.7676,
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The key needs the **Maps JavaScript API** and the **Places API** enabled, and the host's origin
|
|
220
|
+
allowed under its HTTP referrer restrictions. The Maps script is loaded once per page and shared,
|
|
221
|
+
so several AddressLookups on one form cost one script tag between them.
|
|
222
|
+
|
|
223
|
+
#### AddressLookup Props
|
|
224
|
+
|
|
225
|
+
| Prop | Type | Default | Description |
|
|
226
|
+
|------|------|---------|-------------|
|
|
227
|
+
| `apiKey` | `string` | - | Google Maps API key (required) |
|
|
228
|
+
| `value` / `onSelect` | `PlaceAddress \| null` | - | Selection (controlled). `onSelect` also receives the full `PlaceDetail` |
|
|
229
|
+
| `countries` | `string \| string[]` | - | Restrict to ISO country codes, e.g. `['nz','au']`. Google allows five |
|
|
230
|
+
| `searchTypes` | `string[]` | `['address']` | Use `['establishment']` for businesses, `['geocode']` for anything mappable |
|
|
231
|
+
| `stateShortName` | `boolean` | `false` | Return `NSW` rather than `New South Wales` |
|
|
232
|
+
| `countryShortName` | `boolean` | `false` | Return `AU` rather than `Australia` |
|
|
233
|
+
| `showMapCard` | `boolean` | `true` | Map card on hover |
|
|
234
|
+
| `mapCardTarget` | `'list' \| 'rest' \| 'both'` | `'both'` | Where the card is offered |
|
|
235
|
+
| `mapCardDelayMs` | `number` | `400` | Pointer settle delay before the place is resolved |
|
|
236
|
+
| `mapZoom` | `number` | `15` | Map zoom on the card |
|
|
237
|
+
| `showCoordinates` / `showRatings` | `boolean` | `true` | Card detail toggles |
|
|
238
|
+
| `cardActions` | `ReactNode` | - | Content at the bottom of the card |
|
|
239
|
+
| `minSearchLength` | `number` | `3` | Short input never leaves the browser |
|
|
240
|
+
| `onError` | `(error) => void` | - | Search or place lookup failures |
|
|
241
|
+
|
|
242
|
+
The Places client is exported on its own for geocoding outside a form:
|
|
243
|
+
`loadGoogleMaps`, `searchPlacePredictions`, `getPlaceDetails`, `parsePlaceAddress`,
|
|
244
|
+
`placeMapUrl`, `isGoogleMapsReady`, `clearPlacesCache`.
|
|
245
|
+
|
|
146
246
|
### ParentPortal
|
|
147
247
|
|
|
148
248
|
Renders Fluent UI content in the **parent document**, escaping an iframe boundary with full styling. Designed for Dynamics 365 web resources where dialogs must float above the entire D365 page rather than being trapped inside the iframe.
|