@powerportalspro/react-fluent 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +603 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -4
- package/dist/index.d.ts +99 -4
- package/dist/index.js +606 -127
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -304,8 +304,12 @@ function isRecordWriteDenied(record) {
|
|
|
304
304
|
const perms = record.permissions ?? 0;
|
|
305
305
|
return (perms & core.TableSecurityPermission.Write) === 0;
|
|
306
306
|
}
|
|
307
|
-
function
|
|
308
|
-
if (
|
|
307
|
+
function isColumnWriteDenied(record, columnName) {
|
|
308
|
+
if (!record || !columnName) return false;
|
|
309
|
+
return !core.canWriteColumn(record, columnName);
|
|
310
|
+
}
|
|
311
|
+
function resolveEditState(metadata, record, explicitReadOnly, explicitDisabled, columnName) {
|
|
312
|
+
if (isRecordWriteDenied(record) || isColumnWriteDenied(record, columnName)) {
|
|
309
313
|
return { readOnly: true, disabled: true };
|
|
310
314
|
}
|
|
311
315
|
return {
|
|
@@ -733,7 +737,8 @@ function TextEdit({
|
|
|
733
737
|
metadata,
|
|
734
738
|
record,
|
|
735
739
|
readOnly,
|
|
736
|
-
disabled
|
|
740
|
+
disabled,
|
|
741
|
+
columnName
|
|
737
742
|
);
|
|
738
743
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
739
744
|
const validationErrors = useColumnValidation(
|
|
@@ -827,7 +832,8 @@ function MemoEdit({
|
|
|
827
832
|
metadata,
|
|
828
833
|
record,
|
|
829
834
|
readOnly,
|
|
830
|
-
disabled
|
|
835
|
+
disabled,
|
|
836
|
+
columnName
|
|
831
837
|
);
|
|
832
838
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
833
839
|
const validationErrors = useColumnValidation(
|
|
@@ -941,7 +947,8 @@ function BoolEdit({
|
|
|
941
947
|
metadata,
|
|
942
948
|
record,
|
|
943
949
|
readOnly,
|
|
944
|
-
disabled
|
|
950
|
+
disabled,
|
|
951
|
+
columnName
|
|
945
952
|
);
|
|
946
953
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
947
954
|
const isInactive = resolvedDisabled || resolvedReadOnly;
|
|
@@ -1117,7 +1124,8 @@ function NumberEdit({
|
|
|
1117
1124
|
metadata,
|
|
1118
1125
|
record,
|
|
1119
1126
|
readOnly,
|
|
1120
|
-
disabled
|
|
1127
|
+
disabled,
|
|
1128
|
+
columnName
|
|
1121
1129
|
);
|
|
1122
1130
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
1123
1131
|
const validationErrors = useColumnValidation(
|
|
@@ -1561,7 +1569,8 @@ function MoneyEdit({
|
|
|
1561
1569
|
metadata,
|
|
1562
1570
|
record,
|
|
1563
1571
|
readOnly,
|
|
1564
|
-
disabled
|
|
1572
|
+
disabled,
|
|
1573
|
+
columnName
|
|
1565
1574
|
);
|
|
1566
1575
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
1567
1576
|
const validationErrors = useColumnValidation(
|
|
@@ -1975,7 +1984,8 @@ function DateTimeEdit({
|
|
|
1975
1984
|
metadata,
|
|
1976
1985
|
record,
|
|
1977
1986
|
readOnly,
|
|
1978
|
-
disabled
|
|
1987
|
+
disabled,
|
|
1988
|
+
columnName
|
|
1979
1989
|
);
|
|
1980
1990
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
1981
1991
|
const resolvedEditorType = editorType ?? resolveEditorTypeFromMetadata(metadata);
|
|
@@ -5366,7 +5376,8 @@ function FileEdit({
|
|
|
5366
5376
|
metadata,
|
|
5367
5377
|
record,
|
|
5368
5378
|
readOnly,
|
|
5369
|
-
disabled
|
|
5379
|
+
disabled,
|
|
5380
|
+
columnName
|
|
5370
5381
|
);
|
|
5371
5382
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
5372
5383
|
const isInteractable = !resolvedReadOnly && !resolvedDisabled;
|
|
@@ -5683,7 +5694,8 @@ function ImageEdit({
|
|
|
5683
5694
|
metadata,
|
|
5684
5695
|
record,
|
|
5685
5696
|
readOnly,
|
|
5686
|
-
disabled
|
|
5697
|
+
disabled,
|
|
5698
|
+
columnName
|
|
5687
5699
|
);
|
|
5688
5700
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
5689
5701
|
const isInteractable = !resolvedReadOnly && !resolvedDisabled;
|
|
@@ -6163,7 +6175,8 @@ function ChoiceEdit({
|
|
|
6163
6175
|
metadata,
|
|
6164
6176
|
record,
|
|
6165
6177
|
readOnly,
|
|
6166
|
-
disabled
|
|
6178
|
+
disabled,
|
|
6179
|
+
columnName
|
|
6167
6180
|
);
|
|
6168
6181
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
6169
6182
|
const choiceMeta = getChoiceMetadata(metadata);
|
|
@@ -6383,7 +6396,8 @@ function MultiSelectChoiceEdit({
|
|
|
6383
6396
|
metadata,
|
|
6384
6397
|
record,
|
|
6385
6398
|
readOnly,
|
|
6386
|
-
disabled
|
|
6399
|
+
disabled,
|
|
6400
|
+
columnName
|
|
6387
6401
|
);
|
|
6388
6402
|
const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
|
|
6389
6403
|
const choiceMeta = getChoiceMetadata(metadata);
|
|
@@ -6505,30 +6519,30 @@ function foldString(s, caseSensitive) {
|
|
|
6505
6519
|
function tokenizeSearch(search, delimiters) {
|
|
6506
6520
|
if (!delimiters) return [search];
|
|
6507
6521
|
const delim = new Set(Array.from(delimiters));
|
|
6508
|
-
const
|
|
6522
|
+
const tokens44 = [];
|
|
6509
6523
|
let current = "";
|
|
6510
6524
|
for (const ch of search) {
|
|
6511
6525
|
if (delim.has(ch)) {
|
|
6512
6526
|
if (current.length > 0) {
|
|
6513
|
-
|
|
6527
|
+
tokens44.push(current);
|
|
6514
6528
|
current = "";
|
|
6515
6529
|
}
|
|
6516
6530
|
} else {
|
|
6517
6531
|
current += ch;
|
|
6518
6532
|
}
|
|
6519
6533
|
}
|
|
6520
|
-
if (current.length > 0)
|
|
6521
|
-
return
|
|
6534
|
+
if (current.length > 0) tokens44.push(current);
|
|
6535
|
+
return tokens44;
|
|
6522
6536
|
}
|
|
6523
6537
|
function splitHighlightSegments(text, search, options) {
|
|
6524
6538
|
if (!text || !search) return [];
|
|
6525
6539
|
const caseSensitive = options?.caseSensitive ?? false;
|
|
6526
6540
|
const delimiters = options?.delimiters ?? "";
|
|
6527
|
-
const
|
|
6528
|
-
if (
|
|
6541
|
+
const tokens44 = tokenizeSearch(search, delimiters);
|
|
6542
|
+
if (tokens44.length === 0) return [{ text, isMatch: false }];
|
|
6529
6543
|
const folded = foldString(text, caseSensitive);
|
|
6530
6544
|
const ranges = [];
|
|
6531
|
-
for (const token of
|
|
6545
|
+
for (const token of tokens44) {
|
|
6532
6546
|
const tokenFolded = foldString(token, caseSensitive).folded;
|
|
6533
6547
|
if (tokenFolded.length === 0) continue;
|
|
6534
6548
|
let cursor2 = 0;
|
|
@@ -7299,7 +7313,11 @@ function getRendererTypeForCode(typeCode) {
|
|
|
7299
7313
|
}
|
|
7300
7314
|
function ColumnEdit(props) {
|
|
7301
7315
|
const metadata = react$1.useColumnMetadata(props.columnName);
|
|
7316
|
+
const record = react$1.useRecordContextOptional()?.record;
|
|
7302
7317
|
const typeCode = metadata?.$type;
|
|
7318
|
+
if (record && !core.canReadColumn(record, props.columnName)) {
|
|
7319
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ColumnDisplayValue, { ...props });
|
|
7320
|
+
}
|
|
7303
7321
|
switch (typeCode) {
|
|
7304
7322
|
case 0:
|
|
7305
7323
|
return /* @__PURE__ */ jsxRuntime.jsx(BoolEdit, { ...props });
|
|
@@ -8319,6 +8337,36 @@ function MainGridVirtualizeFooter({
|
|
|
8319
8337
|
] });
|
|
8320
8338
|
}
|
|
8321
8339
|
|
|
8340
|
+
// src/grids/virtualize-paging.ts
|
|
8341
|
+
function hasMoreRowsToLoad(state) {
|
|
8342
|
+
if (state.moreRecords === false) return false;
|
|
8343
|
+
if (state.moreRecords === true) return true;
|
|
8344
|
+
if (state.lastPageEmpty) return false;
|
|
8345
|
+
if (state.totalCount === void 0) return true;
|
|
8346
|
+
return state.accumulatedCount < state.totalCount;
|
|
8347
|
+
}
|
|
8348
|
+
var VirtualizePagingCookies = class {
|
|
8349
|
+
key;
|
|
8350
|
+
byPage = /* @__PURE__ */ new Map();
|
|
8351
|
+
/** Store the cookie the response for `page` came back with (no-op for a null / empty cookie). */
|
|
8352
|
+
record(key, page, cookie) {
|
|
8353
|
+
if (this.key !== key) {
|
|
8354
|
+
this.key = key;
|
|
8355
|
+
this.byPage = /* @__PURE__ */ new Map();
|
|
8356
|
+
}
|
|
8357
|
+
if (cookie) this.byPage.set(page, cookie);
|
|
8358
|
+
}
|
|
8359
|
+
/**
|
|
8360
|
+
* The cookie to send when requesting `page` under `key`: the one page
|
|
8361
|
+
* `page - 1` returned, or `undefined` when it isn't known (page 1, a
|
|
8362
|
+
* non-sequential jump, or a key the store has no cookies for).
|
|
8363
|
+
*/
|
|
8364
|
+
cookieFor(key, page) {
|
|
8365
|
+
if (this.key !== key || page <= 1) return void 0;
|
|
8366
|
+
return this.byPage.get(page - 1);
|
|
8367
|
+
}
|
|
8368
|
+
};
|
|
8369
|
+
|
|
8322
8370
|
// src/grids/persisted-grid-state.ts
|
|
8323
8371
|
function readPersistedGridStateFromUrl(queryParameterName) {
|
|
8324
8372
|
if (!queryParameterName) return null;
|
|
@@ -8382,6 +8430,12 @@ var ViewSort = {
|
|
|
8382
8430
|
/** Localized display name, descending. */
|
|
8383
8431
|
NameDescending: "nameDescending"
|
|
8384
8432
|
};
|
|
8433
|
+
function resolveViewLabel(view, t) {
|
|
8434
|
+
const key = `tables.${view.tableName}.views.${view.id}.label`;
|
|
8435
|
+
const localized = t(key);
|
|
8436
|
+
if (localized !== key) return localized;
|
|
8437
|
+
return view.displayName?.trim() ? view.displayName : null;
|
|
8438
|
+
}
|
|
8385
8439
|
function customToViewMetadata(custom) {
|
|
8386
8440
|
return {
|
|
8387
8441
|
id: custom.id,
|
|
@@ -8850,8 +8904,22 @@ function MainGridImpl({
|
|
|
8850
8904
|
`[MainGrid] Declared <GridColumn> children override the column projection of the supplied fetchXml (columns: ${columnsKey.split(",").join(", ")}). Drop the children \u2014 or remove the conflicting <attribute> elements from the fetchXml \u2014 to silence this message.`
|
|
8851
8905
|
);
|
|
8852
8906
|
}, [columnsKey, useInlineFetchXml]);
|
|
8907
|
+
const accumulatorResetKey = react.useMemo(
|
|
8908
|
+
() => [
|
|
8909
|
+
resolvedViewId ?? "",
|
|
8910
|
+
useInlineFetchXml ? inlineFetchXml ?? "" : "",
|
|
8911
|
+
debouncedSearchText,
|
|
8912
|
+
sort.map((s) => `${s.columnName}:${s.descending ? "d" : "a"}`).join("|"),
|
|
8913
|
+
filtersKey,
|
|
8914
|
+
pageSize,
|
|
8915
|
+
tableName ?? ""
|
|
8916
|
+
].join("|"),
|
|
8917
|
+
[resolvedViewId, useInlineFetchXml, inlineFetchXml, debouncedSearchText, sort, filtersKey, pageSize, tableName]
|
|
8918
|
+
);
|
|
8919
|
+
const [virtualizeCookies] = react.useState(() => new VirtualizePagingCookies());
|
|
8853
8920
|
const searchRequest = react.useMemo(() => {
|
|
8854
8921
|
const trimmed = debouncedSearchText.trim();
|
|
8922
|
+
const pagingCookie = isVirtualize ? virtualizeCookies.cookieFor(accumulatorResetKey, page) : void 0;
|
|
8855
8923
|
return {
|
|
8856
8924
|
...useInlineFetchXml && inlineFetchXml ? { fetchXml: inlineFetchXml } : resolvedViewId !== void 0 && { viewId: resolvedViewId },
|
|
8857
8925
|
...trimmed && { searchText: trimmed },
|
|
@@ -8871,7 +8939,8 @@ function MainGridImpl({
|
|
|
8871
8939
|
},
|
|
8872
8940
|
...columnsOverride && { columns: [...columnsOverride] },
|
|
8873
8941
|
pageNumber: page,
|
|
8874
|
-
pageSize
|
|
8942
|
+
pageSize,
|
|
8943
|
+
...pagingCookie && { pagingCookie }
|
|
8875
8944
|
};
|
|
8876
8945
|
}, [
|
|
8877
8946
|
resolvedViewId,
|
|
@@ -8882,7 +8951,9 @@ function MainGridImpl({
|
|
|
8882
8951
|
page,
|
|
8883
8952
|
pageSize,
|
|
8884
8953
|
filtersKey,
|
|
8885
|
-
columnsKey
|
|
8954
|
+
columnsKey,
|
|
8955
|
+
isVirtualize,
|
|
8956
|
+
accumulatorResetKey
|
|
8886
8957
|
]);
|
|
8887
8958
|
const transformPending = !!transformViewAsync || isCustomActiveView;
|
|
8888
8959
|
const recordsResult = react$1.useGridData(searchRequest, {
|
|
@@ -9041,7 +9112,7 @@ function MainGridImpl({
|
|
|
9041
9112
|
const rowRecord = cellCtx.row.original;
|
|
9042
9113
|
const rowId = rowRecord.id;
|
|
9043
9114
|
const isPendingCreateRow = !!rowId && rowId.startsWith("__pending-create-");
|
|
9044
|
-
if (editable && !isPendingCreateRow && hasEditorForType) {
|
|
9115
|
+
if (editable && !isPendingCreateRow && hasEditorForType && core.canWriteColumn(rowRecord, columnName)) {
|
|
9045
9116
|
return (
|
|
9046
9117
|
// Sizing wrapper — overrides Fluent v9's default
|
|
9047
9118
|
// <Input> / <Combobox> / <Dropdown> min-width
|
|
@@ -9179,18 +9250,6 @@ function MainGridImpl({
|
|
|
9179
9250
|
]);
|
|
9180
9251
|
const pageRecords = dataSource ? dataSource.rows : recordsResult.data?.tableRecords ?? EMPTY_RECORDS;
|
|
9181
9252
|
const [accumulatedRows, setAccumulatedRows] = react.useState(null);
|
|
9182
|
-
const accumulatorResetKey = react.useMemo(
|
|
9183
|
-
() => [
|
|
9184
|
-
resolvedViewId ?? "",
|
|
9185
|
-
useInlineFetchXml ? inlineFetchXml ?? "" : "",
|
|
9186
|
-
debouncedSearchText,
|
|
9187
|
-
sort.map((s) => `${s.columnName}:${s.descending ? "d" : "a"}`).join("|"),
|
|
9188
|
-
filtersKey,
|
|
9189
|
-
pageSize,
|
|
9190
|
-
tableName ?? ""
|
|
9191
|
-
].join("|"),
|
|
9192
|
-
[resolvedViewId, useInlineFetchXml, inlineFetchXml, debouncedSearchText, sort, filtersKey, pageSize, tableName]
|
|
9193
|
-
);
|
|
9194
9253
|
const resetVirtualizedView = react.useCallback(() => {
|
|
9195
9254
|
setAccumulatedRows([]);
|
|
9196
9255
|
setPage(1);
|
|
@@ -9216,6 +9275,12 @@ function MainGridImpl({
|
|
|
9216
9275
|
return [...base, ...additions];
|
|
9217
9276
|
});
|
|
9218
9277
|
}, [isVirtualize, recordsResult.status, pageRecords]);
|
|
9278
|
+
react.useEffect(() => {
|
|
9279
|
+
if (!isVirtualize) return;
|
|
9280
|
+
const landed = recordsResult.data;
|
|
9281
|
+
if (!landed) return;
|
|
9282
|
+
virtualizeCookies.record(accumulatorResetKey, page, landed.pagingInfo?.pagingCookie);
|
|
9283
|
+
}, [isVirtualize, recordsResult.data]);
|
|
9219
9284
|
const data = isVirtualize ? accumulatedRows ?? EMPTY_RECORDS : pageRecords;
|
|
9220
9285
|
const [pendingRowUpdates, setPendingRowUpdates] = react.useState(() => /* @__PURE__ */ new Map());
|
|
9221
9286
|
const [pendingRowCreates, setPendingRowCreates] = react.useState(() => []);
|
|
@@ -9696,7 +9761,15 @@ function MainGridImpl({
|
|
|
9696
9761
|
if (raw == null) return void 0;
|
|
9697
9762
|
return typeof raw === "number" ? raw : Number(raw);
|
|
9698
9763
|
})();
|
|
9699
|
-
const
|
|
9764
|
+
const hasMoreRowsToLoad2 = isVirtualize && hasMoreRowsToLoad({
|
|
9765
|
+
accumulatedCount: accumulatedRows?.length ?? 0,
|
|
9766
|
+
totalCount,
|
|
9767
|
+
moreRecords: dataSource ? dataSource.moreRecords : recordsResult.data?.pagingInfo?.moreRecords,
|
|
9768
|
+
// "A page has landed and it carried no rows." In standalone mode
|
|
9769
|
+
// `data` is only ever defined for a landed response (errors wipe it);
|
|
9770
|
+
// the datasource has no data handle, so idle-after-success stands in.
|
|
9771
|
+
lastPageEmpty: dataSource ? dataSource.status === react$1.QueryStatus.Success && !dataSource.isFetching && dataSource.rows.length === 0 : recordsResult.data !== void 0 && pageRecords.length === 0
|
|
9772
|
+
});
|
|
9700
9773
|
react.useEffect(() => {
|
|
9701
9774
|
if (!isVirtualize) return;
|
|
9702
9775
|
const sentinel = loadMoreSentinelRef.current;
|
|
@@ -9707,7 +9780,7 @@ function MainGridImpl({
|
|
|
9707
9780
|
const entry = entries[0];
|
|
9708
9781
|
if (!entry?.isIntersecting) return;
|
|
9709
9782
|
if (recordsResult.isFetching) return;
|
|
9710
|
-
if (!
|
|
9783
|
+
if (!hasMoreRowsToLoad2) return;
|
|
9711
9784
|
setPage(page + 1);
|
|
9712
9785
|
},
|
|
9713
9786
|
{
|
|
@@ -9720,7 +9793,7 @@ function MainGridImpl({
|
|
|
9720
9793
|
);
|
|
9721
9794
|
observer.observe(sentinel);
|
|
9722
9795
|
return () => observer.disconnect();
|
|
9723
|
-
}, [isVirtualize,
|
|
9796
|
+
}, [isVirtualize, hasMoreRowsToLoad2, recordsResult.isFetching, page, setPage]);
|
|
9724
9797
|
const handlePageChange = (nextPage) => {
|
|
9725
9798
|
if (nextPage === page) return;
|
|
9726
9799
|
setPage(nextPage);
|
|
@@ -9729,8 +9802,8 @@ function MainGridImpl({
|
|
|
9729
9802
|
};
|
|
9730
9803
|
const selectedView = views?.find((v) => v.id === resolvedViewId);
|
|
9731
9804
|
const selectedViewLabelKey = selectedView ? `tables.${selectedView.tableName}.views.${selectedView.id}.label` : "";
|
|
9732
|
-
const selectedViewLabel = selectedView ? t
|
|
9733
|
-
const singleViewTitle = selectedView
|
|
9805
|
+
const selectedViewLabel = selectedView ? resolveViewLabel(selectedView, t) ?? selectedViewLabelKey : "";
|
|
9806
|
+
const singleViewTitle = selectedView ? resolveViewLabel(selectedView, t) ?? void 0 : void 0;
|
|
9734
9807
|
return /* @__PURE__ */ jsxRuntime.jsx(GridContextProvider, { value: gridContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9735
9808
|
"div",
|
|
9736
9809
|
{
|
|
@@ -9763,7 +9836,7 @@ function MainGridImpl({
|
|
|
9763
9836
|
if (data2.optionValue) handleViewChange(data2.optionValue);
|
|
9764
9837
|
},
|
|
9765
9838
|
children: views?.map((view) => {
|
|
9766
|
-
const label = t
|
|
9839
|
+
const label = resolveViewLabel(view, t) ?? `tables.${view.tableName}.views.${view.id}.label`;
|
|
9767
9840
|
return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: view.id, text: label, children: label }, view.id);
|
|
9768
9841
|
})
|
|
9769
9842
|
}
|
|
@@ -10340,7 +10413,7 @@ function MainGridImpl({
|
|
|
10340
10413
|
}
|
|
10341
10414
|
),
|
|
10342
10415
|
displayedData.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.emptyStateRow, children: t("app.components.grid.empty-content") }),
|
|
10343
|
-
isVirtualize &&
|
|
10416
|
+
isVirtualize && hasMoreRowsToLoad2 && // No spinner here: the full-body `refetchScrim` overlay below is the
|
|
10344
10417
|
// single load indicator. Rendering a `size="tiny"` spinner in the
|
|
10345
10418
|
// sentinel as well meant a deps-change refetch (refresh / sort /
|
|
10346
10419
|
// search / view switch) — which resets the accumulator to empty and
|
|
@@ -10859,7 +10932,8 @@ function LookupEdit({
|
|
|
10859
10932
|
metadata,
|
|
10860
10933
|
record,
|
|
10861
10934
|
readOnly,
|
|
10862
|
-
disabled
|
|
10935
|
+
disabled,
|
|
10936
|
+
columnName
|
|
10863
10937
|
);
|
|
10864
10938
|
const setLookupValue = useGuardedSetValue(
|
|
10865
10939
|
rawSetLookupValue,
|
|
@@ -12639,6 +12713,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12639
12713
|
defaultMode = ThemeMode.System,
|
|
12640
12714
|
defaultAccentColor = "default",
|
|
12641
12715
|
defaultTextDirection = TextDirection.Ltr,
|
|
12716
|
+
lockAccentColor = false,
|
|
12642
12717
|
customAccents,
|
|
12643
12718
|
storageKey = DEFAULT_MODE_STORAGE_KEY,
|
|
12644
12719
|
style,
|
|
@@ -12662,9 +12737,11 @@ function PowerPortalsProThemeProvider({
|
|
|
12662
12737
|
() => readStoredMode(storageKey) ?? defaultMode
|
|
12663
12738
|
);
|
|
12664
12739
|
const [accentColor, setAccentColorState] = react.useState(() => {
|
|
12740
|
+
const configured = defaultAccentColor in accentMap ? defaultAccentColor : "default";
|
|
12741
|
+
if (lockAccentColor) return configured;
|
|
12665
12742
|
const stored = readStoredString(accentStorageKey);
|
|
12666
12743
|
if (stored && stored in accentMap) return stored;
|
|
12667
|
-
return
|
|
12744
|
+
return configured;
|
|
12668
12745
|
});
|
|
12669
12746
|
const [textDirection, setTextDirectionState] = react.useState(
|
|
12670
12747
|
() => readStoredDirection(directionStorageKey) ?? defaultTextDirection
|
|
@@ -12694,6 +12771,12 @@ function PowerPortalsProThemeProvider({
|
|
|
12694
12771
|
document.body.classList.toggle("ppp-theme-dark", isDark);
|
|
12695
12772
|
document.body.classList.toggle("ppp-theme-light", !isDark);
|
|
12696
12773
|
}, [isDark]);
|
|
12774
|
+
react.useEffect(() => {
|
|
12775
|
+
if (!lockAccentColor) return;
|
|
12776
|
+
const configured = defaultAccentColor in accentMap ? defaultAccentColor : "default";
|
|
12777
|
+
setAccentColorState(configured);
|
|
12778
|
+
writeStored(accentStorageKey, configured);
|
|
12779
|
+
}, [lockAccentColor, defaultAccentColor, accentMap, accentStorageKey]);
|
|
12697
12780
|
const setMode = react.useCallback(
|
|
12698
12781
|
(next) => {
|
|
12699
12782
|
setModeState(next);
|
|
@@ -12703,11 +12786,12 @@ function PowerPortalsProThemeProvider({
|
|
|
12703
12786
|
);
|
|
12704
12787
|
const setAccentColor = react.useCallback(
|
|
12705
12788
|
(next) => {
|
|
12789
|
+
if (lockAccentColor) return;
|
|
12706
12790
|
if (!(next in accentMap)) return;
|
|
12707
12791
|
setAccentColorState(next);
|
|
12708
12792
|
writeStored(accentStorageKey, next);
|
|
12709
12793
|
},
|
|
12710
|
-
[accentMap, accentStorageKey]
|
|
12794
|
+
[lockAccentColor, accentMap, accentStorageKey]
|
|
12711
12795
|
);
|
|
12712
12796
|
const setTextDirection = react.useCallback(
|
|
12713
12797
|
(next) => {
|
|
@@ -12732,6 +12816,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12732
12816
|
setAccentColor,
|
|
12733
12817
|
availableAccents,
|
|
12734
12818
|
accentColors,
|
|
12819
|
+
accentColorLocked: lockAccentColor,
|
|
12735
12820
|
textDirection,
|
|
12736
12821
|
setTextDirection
|
|
12737
12822
|
}),
|
|
@@ -12743,6 +12828,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12743
12828
|
setAccentColor,
|
|
12744
12829
|
availableAccents,
|
|
12745
12830
|
accentColors,
|
|
12831
|
+
lockAccentColor,
|
|
12746
12832
|
textDirection,
|
|
12747
12833
|
setTextDirection
|
|
12748
12834
|
]
|
|
@@ -12823,7 +12909,7 @@ function ThemeColorSelector({
|
|
|
12823
12909
|
width,
|
|
12824
12910
|
accentLabels
|
|
12825
12911
|
}) {
|
|
12826
|
-
const { accentColor, setAccentColor, availableAccents, accentColors } = useTheme();
|
|
12912
|
+
const { accentColor, setAccentColor, availableAccents, accentColors, accentColorLocked } = useTheme();
|
|
12827
12913
|
const t = react$1.useT();
|
|
12828
12914
|
const labelId = react.useId();
|
|
12829
12915
|
const styles = useStyles6();
|
|
@@ -12837,6 +12923,7 @@ function ThemeColorSelector({
|
|
|
12837
12923
|
},
|
|
12838
12924
|
[t, accentLabels]
|
|
12839
12925
|
);
|
|
12926
|
+
if (accentColorLocked) return null;
|
|
12840
12927
|
const handleOptionSelect = (_event, data) => {
|
|
12841
12928
|
const next = data.optionValue;
|
|
12842
12929
|
if (next && availableAccents.includes(next)) {
|
|
@@ -13004,7 +13091,304 @@ function LanguageDropdown({
|
|
|
13004
13091
|
}
|
|
13005
13092
|
);
|
|
13006
13093
|
}
|
|
13094
|
+
var MINIMUM_QUERY_LENGTH = 3;
|
|
13095
|
+
var MAXIMUM_RESULTS = 50;
|
|
13096
|
+
var SEARCH_DEBOUNCE_MS4 = 300;
|
|
13097
|
+
var GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13007
13098
|
var useStyles7 = reactComponents.makeStyles({
|
|
13099
|
+
surface: {
|
|
13100
|
+
maxWidth: "480px",
|
|
13101
|
+
display: "flex",
|
|
13102
|
+
flexDirection: "column"
|
|
13103
|
+
},
|
|
13104
|
+
titleRow: {
|
|
13105
|
+
display: "flex",
|
|
13106
|
+
flexDirection: "row",
|
|
13107
|
+
alignItems: "flex-start",
|
|
13108
|
+
columnGap: reactComponents.tokens.spacingHorizontalS
|
|
13109
|
+
},
|
|
13110
|
+
titleText: {
|
|
13111
|
+
flexGrow: 1,
|
|
13112
|
+
minWidth: 0
|
|
13113
|
+
},
|
|
13114
|
+
body: {
|
|
13115
|
+
display: "flex",
|
|
13116
|
+
flexDirection: "column",
|
|
13117
|
+
rowGap: reactComponents.tokens.spacingVerticalM,
|
|
13118
|
+
paddingTop: reactComponents.tokens.spacingVerticalM
|
|
13119
|
+
},
|
|
13120
|
+
results: {
|
|
13121
|
+
display: "flex",
|
|
13122
|
+
flexDirection: "column",
|
|
13123
|
+
// Fixed height, not min/max: a list that resizes on every keystroke makes the confirm button
|
|
13124
|
+
// jump around under the pointer while the administrator is still reading.
|
|
13125
|
+
height: "280px",
|
|
13126
|
+
overflowY: "auto",
|
|
13127
|
+
// Full shorthand — griffel types `borderColor` and friends as `never`, so the longhand
|
|
13128
|
+
// properties aren't usable here.
|
|
13129
|
+
border: `1px solid ${reactComponents.tokens.colorNeutralStroke1}`,
|
|
13130
|
+
borderRadius: reactComponents.tokens.borderRadiusMedium,
|
|
13131
|
+
backgroundColor: reactComponents.tokens.colorNeutralBackground1
|
|
13132
|
+
},
|
|
13133
|
+
result: {
|
|
13134
|
+
display: "flex",
|
|
13135
|
+
flexDirection: "row",
|
|
13136
|
+
alignItems: "center",
|
|
13137
|
+
columnGap: reactComponents.tokens.spacingHorizontalS,
|
|
13138
|
+
padding: `${reactComponents.tokens.spacingVerticalS} ${reactComponents.tokens.spacingHorizontalM}`,
|
|
13139
|
+
cursor: "pointer",
|
|
13140
|
+
borderBottom: `1px solid ${reactComponents.tokens.colorNeutralStroke2}`,
|
|
13141
|
+
textAlign: "left",
|
|
13142
|
+
background: "none",
|
|
13143
|
+
// A native <button> does NOT inherit colour or font from its container — the user-agent
|
|
13144
|
+
// stylesheet gives it `color: buttontext` and its own font stack. Without these three the
|
|
13145
|
+
// rows rendered as near-black text on the dark surface in dark mode, which is how this was
|
|
13146
|
+
// first spotted. Set explicitly rather than relying on inheritance.
|
|
13147
|
+
color: reactComponents.tokens.colorNeutralForeground1,
|
|
13148
|
+
fontFamily: "inherit",
|
|
13149
|
+
fontSize: reactComponents.tokens.fontSizeBase300,
|
|
13150
|
+
// The UA also gives buttons a border; `background: none` alone leaves it drawing a box
|
|
13151
|
+
// around every row. Only three sides — borderBottom above is the row separator.
|
|
13152
|
+
borderTop: "none",
|
|
13153
|
+
borderRight: "none",
|
|
13154
|
+
borderLeft: "none",
|
|
13155
|
+
":hover": {
|
|
13156
|
+
backgroundColor: reactComponents.tokens.colorNeutralBackground1Hover,
|
|
13157
|
+
color: reactComponents.tokens.colorNeutralForeground1Hover
|
|
13158
|
+
},
|
|
13159
|
+
":focus-visible": {
|
|
13160
|
+
outline: `2px solid ${reactComponents.tokens.colorStrokeFocus2}`,
|
|
13161
|
+
outlineOffset: "-2px"
|
|
13162
|
+
}
|
|
13163
|
+
},
|
|
13164
|
+
resultSelected: {
|
|
13165
|
+
backgroundColor: reactComponents.tokens.colorBrandBackground,
|
|
13166
|
+
color: reactComponents.tokens.colorNeutralForegroundOnBrand,
|
|
13167
|
+
":hover": {
|
|
13168
|
+
backgroundColor: reactComponents.tokens.colorBrandBackgroundHover,
|
|
13169
|
+
// Repeated because the base row's :hover also sets colour, and whichever griffel emits
|
|
13170
|
+
// last would otherwise win — leaving selected-and-hovered text unreadable on the brand fill.
|
|
13171
|
+
color: reactComponents.tokens.colorNeutralForegroundOnBrand
|
|
13172
|
+
}
|
|
13173
|
+
},
|
|
13174
|
+
resultText: {
|
|
13175
|
+
display: "flex",
|
|
13176
|
+
flexDirection: "column",
|
|
13177
|
+
minWidth: 0
|
|
13178
|
+
},
|
|
13179
|
+
name: {
|
|
13180
|
+
fontSize: reactComponents.tokens.fontSizeBase300,
|
|
13181
|
+
overflow: "hidden",
|
|
13182
|
+
textOverflow: "ellipsis",
|
|
13183
|
+
whiteSpace: "nowrap"
|
|
13184
|
+
},
|
|
13185
|
+
email: {
|
|
13186
|
+
fontSize: reactComponents.tokens.fontSizeBase200,
|
|
13187
|
+
opacity: 0.8,
|
|
13188
|
+
overflow: "hidden",
|
|
13189
|
+
textOverflow: "ellipsis",
|
|
13190
|
+
whiteSpace: "nowrap"
|
|
13191
|
+
},
|
|
13192
|
+
status: {
|
|
13193
|
+
display: "flex",
|
|
13194
|
+
flexDirection: "row",
|
|
13195
|
+
alignItems: "center",
|
|
13196
|
+
columnGap: reactComponents.tokens.spacingHorizontalS,
|
|
13197
|
+
padding: reactComponents.tokens.spacingVerticalXL,
|
|
13198
|
+
color: reactComponents.tokens.colorNeutralForeground3,
|
|
13199
|
+
fontSize: reactComponents.tokens.fontSizeBase200
|
|
13200
|
+
},
|
|
13201
|
+
warning: {
|
|
13202
|
+
display: "flex",
|
|
13203
|
+
flexDirection: "row",
|
|
13204
|
+
alignItems: "flex-start",
|
|
13205
|
+
columnGap: reactComponents.tokens.spacingHorizontalS,
|
|
13206
|
+
padding: `${reactComponents.tokens.spacingVerticalS} ${reactComponents.tokens.spacingHorizontalM}`,
|
|
13207
|
+
borderRadius: reactComponents.tokens.borderRadiusMedium,
|
|
13208
|
+
backgroundColor: reactComponents.tokens.colorNeutralBackground2,
|
|
13209
|
+
borderLeft: `3px solid ${reactComponents.tokens.colorPaletteRedBackground3}`,
|
|
13210
|
+
fontSize: reactComponents.tokens.fontSizeBase200
|
|
13211
|
+
},
|
|
13212
|
+
footer: {
|
|
13213
|
+
display: "flex",
|
|
13214
|
+
flexDirection: "row",
|
|
13215
|
+
justifyContent: "flex-end",
|
|
13216
|
+
columnGap: reactComponents.tokens.spacingHorizontalS,
|
|
13217
|
+
paddingTop: reactComponents.tokens.spacingVerticalM
|
|
13218
|
+
}
|
|
13219
|
+
});
|
|
13220
|
+
function ImpersonationPickerDialog({
|
|
13221
|
+
open,
|
|
13222
|
+
onClose,
|
|
13223
|
+
returnUrl = "/",
|
|
13224
|
+
className
|
|
13225
|
+
}) {
|
|
13226
|
+
const auth = react$1.useAuth();
|
|
13227
|
+
const styles = useStyles7();
|
|
13228
|
+
const t = react$1.useT();
|
|
13229
|
+
const { searchImpersonationTargets, impersonate } = auth;
|
|
13230
|
+
const [query, setQuery] = react.useState("");
|
|
13231
|
+
const [results, setResults] = react.useState([]);
|
|
13232
|
+
const [selected, setSelected] = react.useState(null);
|
|
13233
|
+
const [isSearching, setIsSearching] = react.useState(false);
|
|
13234
|
+
const [hasSearched, setHasSearched] = react.useState(false);
|
|
13235
|
+
const [hasMore, setHasMore] = react.useState(false);
|
|
13236
|
+
const [isStarting, setIsStarting] = react.useState(false);
|
|
13237
|
+
const requestIdRef = react.useRef(0);
|
|
13238
|
+
react.useEffect(() => {
|
|
13239
|
+
if (open) return;
|
|
13240
|
+
setQuery("");
|
|
13241
|
+
setResults([]);
|
|
13242
|
+
setSelected(null);
|
|
13243
|
+
setIsSearching(false);
|
|
13244
|
+
setHasSearched(false);
|
|
13245
|
+
setHasMore(false);
|
|
13246
|
+
setIsStarting(false);
|
|
13247
|
+
}, [open]);
|
|
13248
|
+
const trimmed = query.trim();
|
|
13249
|
+
const isTooShort = trimmed.length > 0 && trimmed.length < MINIMUM_QUERY_LENGTH;
|
|
13250
|
+
react.useEffect(() => {
|
|
13251
|
+
if (!open) return;
|
|
13252
|
+
const requestId = ++requestIdRef.current;
|
|
13253
|
+
if (trimmed.length < MINIMUM_QUERY_LENGTH && !GUID_PATTERN.test(trimmed)) {
|
|
13254
|
+
setResults([]);
|
|
13255
|
+
setHasSearched(false);
|
|
13256
|
+
setHasMore(false);
|
|
13257
|
+
setIsSearching(false);
|
|
13258
|
+
return;
|
|
13259
|
+
}
|
|
13260
|
+
let cancelled = false;
|
|
13261
|
+
const timer = setTimeout(async () => {
|
|
13262
|
+
setIsSearching(true);
|
|
13263
|
+
try {
|
|
13264
|
+
const response = await searchImpersonationTargets(trimmed);
|
|
13265
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
13266
|
+
setResults(response.results ?? []);
|
|
13267
|
+
setHasMore(response.hasMore ?? false);
|
|
13268
|
+
setHasSearched(true);
|
|
13269
|
+
} catch {
|
|
13270
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
13271
|
+
setResults([]);
|
|
13272
|
+
setHasMore(false);
|
|
13273
|
+
setHasSearched(true);
|
|
13274
|
+
} finally {
|
|
13275
|
+
if (!cancelled && requestId === requestIdRef.current) {
|
|
13276
|
+
setIsSearching(false);
|
|
13277
|
+
}
|
|
13278
|
+
}
|
|
13279
|
+
}, SEARCH_DEBOUNCE_MS4);
|
|
13280
|
+
return () => {
|
|
13281
|
+
cancelled = true;
|
|
13282
|
+
clearTimeout(timer);
|
|
13283
|
+
};
|
|
13284
|
+
}, [open, searchImpersonationTargets, trimmed]);
|
|
13285
|
+
const handleQueryChange = react.useCallback((value) => {
|
|
13286
|
+
setQuery(value);
|
|
13287
|
+
setSelected(null);
|
|
13288
|
+
}, []);
|
|
13289
|
+
const handleConfirm = react.useCallback(async () => {
|
|
13290
|
+
if (!selected?.contactId) return;
|
|
13291
|
+
setIsStarting(true);
|
|
13292
|
+
try {
|
|
13293
|
+
const response = await impersonate(selected.contactId);
|
|
13294
|
+
if (response.result === core.ImpersonateResult.Started) {
|
|
13295
|
+
window.location.assign(returnUrl);
|
|
13296
|
+
return;
|
|
13297
|
+
}
|
|
13298
|
+
setIsStarting(false);
|
|
13299
|
+
onClose();
|
|
13300
|
+
} catch {
|
|
13301
|
+
setIsStarting(false);
|
|
13302
|
+
}
|
|
13303
|
+
}, [impersonate, onClose, returnUrl, selected]);
|
|
13304
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Dialog, { open, onOpenChange: (_, data) => {
|
|
13305
|
+
if (!data.open) onClose();
|
|
13306
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13307
|
+
reactComponents.DialogSurface,
|
|
13308
|
+
{
|
|
13309
|
+
className: reactComponents.mergeClasses("ppp-impersonation-picker-dialog", styles.surface, className),
|
|
13310
|
+
children: [
|
|
13311
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.titleRow, children: [
|
|
13312
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.DialogTitle, { className: styles.titleText, children: t("app.components.impersonation-picker.title") }),
|
|
13313
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13314
|
+
reactComponents.Button,
|
|
13315
|
+
{
|
|
13316
|
+
appearance: "subtle",
|
|
13317
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.Dismiss24Regular, {}),
|
|
13318
|
+
"aria-label": t("app.components.impersonation-picker.cancel"),
|
|
13319
|
+
onClick: onClose
|
|
13320
|
+
}
|
|
13321
|
+
)
|
|
13322
|
+
] }),
|
|
13323
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.body, children: [
|
|
13324
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13325
|
+
reactComponents.SearchBox,
|
|
13326
|
+
{
|
|
13327
|
+
value: query,
|
|
13328
|
+
onChange: (_, data) => handleQueryChange(data.value),
|
|
13329
|
+
placeholder: t("app.components.impersonation-picker.search-placeholder"),
|
|
13330
|
+
appearance: "outline",
|
|
13331
|
+
style: { width: "100%" }
|
|
13332
|
+
}
|
|
13333
|
+
),
|
|
13334
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.results, role: "listbox", children: isSearching ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.status, children: [
|
|
13335
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Spinner, { size: "tiny" }),
|
|
13336
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("app.components.impersonation-picker.searching") })
|
|
13337
|
+
] }) : isTooShort ? (
|
|
13338
|
+
// Distinct from "no matches": the search hasn't run. Conflating the two reads as
|
|
13339
|
+
// "this person doesn't exist" when the user has simply typed too few characters.
|
|
13340
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.hint", [String(MINIMUM_QUERY_LENGTH)]) })
|
|
13341
|
+
) : hasSearched && results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.no-matches") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13342
|
+
results.map((candidate) => {
|
|
13343
|
+
const isSelected = selected?.contactId === candidate.contactId;
|
|
13344
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13345
|
+
"button",
|
|
13346
|
+
{
|
|
13347
|
+
type: "button",
|
|
13348
|
+
role: "option",
|
|
13349
|
+
"aria-selected": isSelected,
|
|
13350
|
+
className: reactComponents.mergeClasses(
|
|
13351
|
+
styles.result,
|
|
13352
|
+
isSelected && styles.resultSelected
|
|
13353
|
+
),
|
|
13354
|
+
onClick: () => setSelected(candidate),
|
|
13355
|
+
children: [
|
|
13356
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactIcons.Person20Regular, {}),
|
|
13357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles.resultText, children: [
|
|
13358
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.name, children: candidate.fullName ?? candidate.email ?? candidate.contactId }),
|
|
13359
|
+
candidate.email && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.email, children: candidate.email })
|
|
13360
|
+
] })
|
|
13361
|
+
]
|
|
13362
|
+
},
|
|
13363
|
+
candidate.contactId
|
|
13364
|
+
);
|
|
13365
|
+
}),
|
|
13366
|
+
hasMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.too-many", [String(MAXIMUM_RESULTS)]) })
|
|
13367
|
+
] }) }),
|
|
13368
|
+
selected && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.warning, children: [
|
|
13369
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactIcons.Warning20Regular, {}),
|
|
13370
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t("app.components.impersonation-picker.warning") })
|
|
13371
|
+
] })
|
|
13372
|
+
] }),
|
|
13373
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.footer, children: [
|
|
13374
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13375
|
+
reactComponents.Button,
|
|
13376
|
+
{
|
|
13377
|
+
appearance: "primary",
|
|
13378
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.EyeTracking20Regular, {}),
|
|
13379
|
+
disabled: !selected || isStarting,
|
|
13380
|
+
onClick: handleConfirm,
|
|
13381
|
+
children: t("app.components.impersonation-picker.confirm")
|
|
13382
|
+
}
|
|
13383
|
+
),
|
|
13384
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Button, { appearance: "secondary", onClick: onClose, disabled: isStarting, children: t("app.components.impersonation-picker.cancel") })
|
|
13385
|
+
] })
|
|
13386
|
+
]
|
|
13387
|
+
}
|
|
13388
|
+
) });
|
|
13389
|
+
}
|
|
13390
|
+
var IMPERSONATION_ROLE = "SystemAdmin";
|
|
13391
|
+
var useStyles8 = reactComponents.makeStyles({
|
|
13008
13392
|
trigger: {
|
|
13009
13393
|
backgroundColor: "transparent",
|
|
13010
13394
|
border: "none",
|
|
@@ -13064,8 +13448,9 @@ function ProfileMainMenu({
|
|
|
13064
13448
|
signInUrl = "/login"
|
|
13065
13449
|
}) {
|
|
13066
13450
|
const auth = react$1.useAuth();
|
|
13067
|
-
const styles =
|
|
13451
|
+
const styles = useStyles8();
|
|
13068
13452
|
const t = react$1.useT();
|
|
13453
|
+
const [isPickerOpen, setIsPickerOpen] = react.useState(false);
|
|
13069
13454
|
const handleLogout = react.useCallback(async () => {
|
|
13070
13455
|
try {
|
|
13071
13456
|
await auth.logout();
|
|
@@ -13082,7 +13467,14 @@ function ProfileMainMenu({
|
|
|
13082
13467
|
}
|
|
13083
13468
|
}, [auth]);
|
|
13084
13469
|
if (auth.status === react$1.AuthStatus.Loading) {
|
|
13085
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13470
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13471
|
+
reactComponents.Avatar,
|
|
13472
|
+
{
|
|
13473
|
+
className: "ppp-profile-main-menu",
|
|
13474
|
+
size: 28,
|
|
13475
|
+
"aria-label": t("app.messages.record-loading")
|
|
13476
|
+
}
|
|
13477
|
+
);
|
|
13086
13478
|
}
|
|
13087
13479
|
if (auth.status === react$1.AuthStatus.Anonymous) {
|
|
13088
13480
|
if (anonymousContent !== void 0) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: anonymousContent });
|
|
@@ -13103,61 +13495,126 @@ function ProfileMainMenu({
|
|
|
13103
13495
|
const resolvedAccountLabel = accountLabel ?? t("app.components.profile-main-menu.account");
|
|
13104
13496
|
const signedInAsLabel = t("app.components.profile-main-menu.signed-in-as");
|
|
13105
13497
|
const logoutLabel = t("app.buttons.logout.label");
|
|
13498
|
+
const canImpersonate = (auth.user.roles?.includes(IMPERSONATION_ROLE) ?? false) && auth.user.portalUserType === core.PortalUserType.SystemUser;
|
|
13106
13499
|
const altIdentityTableName = auth.user.altIdentityTableName;
|
|
13107
|
-
const switchIdentityLabel = altIdentityTableName ? t(
|
|
13108
|
-
"app.components.profile-main-menu.
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13500
|
+
const switchIdentityLabel = altIdentityTableName ? t("app.components.profile-main-menu.switch-to", [
|
|
13501
|
+
altIdentityTableName === "systemuser" ? t("app.components.profile-main-menu.alt-kind-systemuser") : t("app.components.profile-main-menu.alt-kind-contact")
|
|
13502
|
+
]) : null;
|
|
13503
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13504
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactComponents.Menu, { children: [
|
|
13505
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13506
|
+
"button",
|
|
13507
|
+
{
|
|
13508
|
+
type: "button",
|
|
13509
|
+
className: reactComponents.mergeClasses("ppp-profile-main-menu", styles.trigger),
|
|
13510
|
+
"aria-label": email || logoutLabel,
|
|
13511
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 28, initials, name: email })
|
|
13512
|
+
}
|
|
13513
|
+
) }),
|
|
13514
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuPopover, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.popoverBody, children: [
|
|
13515
|
+
headerSlot,
|
|
13516
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identity, children: [
|
|
13517
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 40, initials, name: email }),
|
|
13518
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identityText, children: [
|
|
13519
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityLabel, children: signedInAsLabel }),
|
|
13520
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityValue, title: email, children: email })
|
|
13521
|
+
] })
|
|
13522
|
+
] }),
|
|
13523
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13524
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuList, { className: styles.actions, children: [
|
|
13525
|
+
accountUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13526
|
+
reactComponents.MenuItem,
|
|
13527
|
+
{
|
|
13528
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.PersonEdit20Regular, {}),
|
|
13529
|
+
onClick: () => {
|
|
13530
|
+
window.location.href = accountUrl;
|
|
13531
|
+
},
|
|
13532
|
+
children: resolvedAccountLabel
|
|
13533
|
+
}
|
|
13534
|
+
),
|
|
13535
|
+
switchIdentityLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13536
|
+
reactComponents.MenuItem,
|
|
13537
|
+
{
|
|
13538
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ArrowSwap20Regular, {}),
|
|
13539
|
+
onClick: handleSwitchIdentity,
|
|
13540
|
+
children: switchIdentityLabel
|
|
13541
|
+
}
|
|
13542
|
+
),
|
|
13543
|
+
canImpersonate && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13544
|
+
reactComponents.MenuItem,
|
|
13545
|
+
{
|
|
13546
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.EyeTracking20Regular, {}),
|
|
13547
|
+
onClick: () => setIsPickerOpen(true),
|
|
13548
|
+
children: t("app.components.profile-main-menu.impersonate")
|
|
13549
|
+
}
|
|
13550
|
+
),
|
|
13551
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItem, { icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.SignOut20Regular, {}), onClick: handleLogout, children: logoutLabel })
|
|
13552
|
+
] }),
|
|
13553
|
+
footerSlot && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13554
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13555
|
+
footerSlot
|
|
13556
|
+
] })
|
|
13557
|
+
] }) })
|
|
13558
|
+
] }),
|
|
13559
|
+
canImpersonate && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13560
|
+
ImpersonationPickerDialog,
|
|
13116
13561
|
{
|
|
13117
|
-
|
|
13118
|
-
|
|
13119
|
-
"aria-label": email || logoutLabel,
|
|
13120
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 28, initials, name: email })
|
|
13562
|
+
open: isPickerOpen,
|
|
13563
|
+
onClose: () => setIsPickerOpen(false)
|
|
13121
13564
|
}
|
|
13122
|
-
)
|
|
13123
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuPopover, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.popoverBody, children: [
|
|
13124
|
-
headerSlot,
|
|
13125
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identity, children: [
|
|
13126
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 40, initials, name: email }),
|
|
13127
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identityText, children: [
|
|
13128
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityLabel, children: signedInAsLabel }),
|
|
13129
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityValue, title: email, children: email })
|
|
13130
|
-
] })
|
|
13131
|
-
] }),
|
|
13132
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13133
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuList, { className: styles.actions, children: [
|
|
13134
|
-
accountUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13135
|
-
reactComponents.MenuItem,
|
|
13136
|
-
{
|
|
13137
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.PersonEdit20Regular, {}),
|
|
13138
|
-
onClick: () => {
|
|
13139
|
-
window.location.href = accountUrl;
|
|
13140
|
-
},
|
|
13141
|
-
children: resolvedAccountLabel
|
|
13142
|
-
}
|
|
13143
|
-
),
|
|
13144
|
-
switchIdentityLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13145
|
-
reactComponents.MenuItem,
|
|
13146
|
-
{
|
|
13147
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ArrowSwap20Regular, {}),
|
|
13148
|
-
onClick: handleSwitchIdentity,
|
|
13149
|
-
children: switchIdentityLabel
|
|
13150
|
-
}
|
|
13151
|
-
),
|
|
13152
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItem, { icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.SignOut20Regular, {}), onClick: handleLogout, children: logoutLabel })
|
|
13153
|
-
] }),
|
|
13154
|
-
footerSlot && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13155
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13156
|
-
footerSlot
|
|
13157
|
-
] })
|
|
13158
|
-
] }) })
|
|
13565
|
+
)
|
|
13159
13566
|
] });
|
|
13160
13567
|
}
|
|
13568
|
+
function ImpersonationBanner({
|
|
13569
|
+
returnUrl = "/",
|
|
13570
|
+
className
|
|
13571
|
+
}) {
|
|
13572
|
+
const auth = react$1.useAuth();
|
|
13573
|
+
const t = react$1.useT();
|
|
13574
|
+
const [isStopping, setIsStopping] = react.useState(false);
|
|
13575
|
+
const { stopImpersonation } = auth;
|
|
13576
|
+
const handleStop = react.useCallback(async () => {
|
|
13577
|
+
setIsStopping(true);
|
|
13578
|
+
try {
|
|
13579
|
+
await stopImpersonation();
|
|
13580
|
+
window.location.assign(returnUrl);
|
|
13581
|
+
} catch {
|
|
13582
|
+
setIsStopping(false);
|
|
13583
|
+
}
|
|
13584
|
+
}, [returnUrl, stopImpersonation]);
|
|
13585
|
+
if (auth.status !== react$1.AuthStatus.Authenticated || !auth.user.isImpersonating) {
|
|
13586
|
+
return null;
|
|
13587
|
+
}
|
|
13588
|
+
const viewingAs = auth.user.email ?? auth.user.userName ?? "";
|
|
13589
|
+
const impersonator = auth.user.impersonatorName;
|
|
13590
|
+
return (
|
|
13591
|
+
// The class is placement only — PageLayout targets it to assign the grid row. It carries no
|
|
13592
|
+
// appearance of its own; Fluent owns all of that.
|
|
13593
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
13594
|
+
reactComponents.MessageBar,
|
|
13595
|
+
{
|
|
13596
|
+
className: reactComponents.mergeClasses("ppp-impersonation-banner", className),
|
|
13597
|
+
intent: "error",
|
|
13598
|
+
children: [
|
|
13599
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MessageBarBody, { children: [
|
|
13600
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBarTitle, { children: t("app.components.impersonation-banner.viewing-as", [viewingAs]) }),
|
|
13601
|
+
impersonator ? ` ${t("app.components.impersonation-banner.you-are", [impersonator])}` : null
|
|
13602
|
+
] }),
|
|
13603
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MessageBarActions, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13604
|
+
reactComponents.Button,
|
|
13605
|
+
{
|
|
13606
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ArrowExit20Regular, {}),
|
|
13607
|
+
iconPosition: "after",
|
|
13608
|
+
disabled: isStopping,
|
|
13609
|
+
onClick: handleStop,
|
|
13610
|
+
children: t("app.components.impersonation-banner.stop")
|
|
13611
|
+
}
|
|
13612
|
+
) })
|
|
13613
|
+
]
|
|
13614
|
+
}
|
|
13615
|
+
)
|
|
13616
|
+
);
|
|
13617
|
+
}
|
|
13161
13618
|
var usePanelStyles = reactComponents.makeStyles({
|
|
13162
13619
|
// Vertical stack of setting sections with consistent gap. Matches the
|
|
13163
13620
|
// panel body the SiteSettingsButton's drawer renders, but extracted
|
|
@@ -13206,7 +13663,7 @@ function SiteSettingsPanel({
|
|
|
13206
13663
|
}
|
|
13207
13664
|
);
|
|
13208
13665
|
}
|
|
13209
|
-
var
|
|
13666
|
+
var useStyles9 = reactComponents.makeStyles({
|
|
13210
13667
|
// Wraps the standalone panel inside the drawer body to add the small
|
|
13211
13668
|
// top inset that the popover's title visually expects. The panel
|
|
13212
13669
|
// itself stays inset-free so it doesn't carry extra padding when
|
|
@@ -13226,7 +13683,7 @@ function SiteSettingsButton({
|
|
|
13226
13683
|
accentLabels
|
|
13227
13684
|
}) {
|
|
13228
13685
|
const t = react$1.useT();
|
|
13229
|
-
const styles =
|
|
13686
|
+
const styles = useStyles9();
|
|
13230
13687
|
const [open, setOpen] = react.useState(false);
|
|
13231
13688
|
const close = react.useCallback(() => setOpen(false), []);
|
|
13232
13689
|
const label = t("app.site-settings-label");
|
|
@@ -13704,7 +14161,7 @@ function bestRouteMatch(routes, pathname) {
|
|
|
13704
14161
|
}
|
|
13705
14162
|
return best;
|
|
13706
14163
|
}
|
|
13707
|
-
var
|
|
14164
|
+
var useStyles10 = reactComponents.makeStyles({
|
|
13708
14165
|
// Shared scrim look. Both global (full-screen) and scoped variants reuse
|
|
13709
14166
|
// these so the visual feel is identical — only the positioning differs.
|
|
13710
14167
|
// Theme-aware translucent backdrop drawn via a `::before` pseudo-element
|
|
@@ -13766,7 +14223,7 @@ function FluentOverlayProvider({ children }) {
|
|
|
13766
14223
|
] });
|
|
13767
14224
|
}
|
|
13768
14225
|
function GlobalOverlayLayer() {
|
|
13769
|
-
const styles =
|
|
14226
|
+
const styles = useStyles10();
|
|
13770
14227
|
const overlay = react$1.useOverlayForTarget(void 0);
|
|
13771
14228
|
if (!overlay) return null;
|
|
13772
14229
|
return /* @__PURE__ */ jsxRuntime.jsx(ScrimContent, { overlay, className: reactComponents.mergeClasses(styles.scrim, styles.global) });
|
|
@@ -13777,7 +14234,7 @@ function FluentOverlayTarget({
|
|
|
13777
14234
|
className,
|
|
13778
14235
|
style
|
|
13779
14236
|
}) {
|
|
13780
|
-
const styles =
|
|
14237
|
+
const styles = useStyles10();
|
|
13781
14238
|
const overlay = react$1.useOverlayForTarget(targetId);
|
|
13782
14239
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reactComponents.mergeClasses("ppp-overlay-target", styles.scopedHost, className), style, children: [
|
|
13783
14240
|
children,
|
|
@@ -13785,14 +14242,14 @@ function FluentOverlayTarget({
|
|
|
13785
14242
|
] });
|
|
13786
14243
|
}
|
|
13787
14244
|
function ScrimContent({ overlay, className }) {
|
|
13788
|
-
const styles =
|
|
14245
|
+
const styles = useStyles10();
|
|
13789
14246
|
const showProgress = overlay.showProgress !== false;
|
|
13790
14247
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, role: "status", "aria-live": "polite", children: [
|
|
13791
14248
|
showProgress && /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Spinner, { size: "large" }),
|
|
13792
14249
|
overlay.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.description, children: overlay.description })
|
|
13793
14250
|
] });
|
|
13794
14251
|
}
|
|
13795
|
-
var
|
|
14252
|
+
var useStyles11 = reactComponents.makeStyles({
|
|
13796
14253
|
// Centered placeholder used while prefixes are being fetched. Same
|
|
13797
14254
|
// shape as the spinner blocks in `<NewRecordGridButton>` /
|
|
13798
14255
|
// `<OpenRecordGridButton>` dialogs (~160px min height + vertical
|
|
@@ -13812,7 +14269,7 @@ function LocalizationBoundary({
|
|
|
13812
14269
|
fallback,
|
|
13813
14270
|
children
|
|
13814
14271
|
}) {
|
|
13815
|
-
const styles =
|
|
14272
|
+
const styles = useStyles11();
|
|
13816
14273
|
const resolvedPrefixes = prefixes ?? EMPTY_PREFIXES;
|
|
13817
14274
|
const { locale } = react$1.useLocale();
|
|
13818
14275
|
react$1.useLocalization(resolvedPrefixes);
|
|
@@ -13834,7 +14291,7 @@ function LocalizationBoundary({
|
|
|
13834
14291
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reactComponents.mergeClasses("ppp-localization-boundary", styles.fallback), role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Spinner, { size: "medium" }) });
|
|
13835
14292
|
}
|
|
13836
14293
|
var EMPTY_PREFIXES = Object.freeze([]);
|
|
13837
|
-
var
|
|
14294
|
+
var useStyles12 = reactComponents.makeStyles({
|
|
13838
14295
|
root: {
|
|
13839
14296
|
display: "flex",
|
|
13840
14297
|
flexDirection: "column",
|
|
@@ -13892,7 +14349,7 @@ function isSameLanguage(a, b) {
|
|
|
13892
14349
|
return primaryA.length > 0 && primaryA === primary(b);
|
|
13893
14350
|
}
|
|
13894
14351
|
function LocalizationTranslator({ className }) {
|
|
13895
|
-
const styles =
|
|
14352
|
+
const styles = useStyles12();
|
|
13896
14353
|
const t = react$1.useT();
|
|
13897
14354
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
13898
14355
|
const [availability, setAvailability] = react.useState(null);
|
|
@@ -14380,7 +14837,7 @@ function SectionColumnRenderer({
|
|
|
14380
14837
|
}
|
|
14381
14838
|
);
|
|
14382
14839
|
}
|
|
14383
|
-
var
|
|
14840
|
+
var useStyles13 = reactComponents.makeStyles({
|
|
14384
14841
|
root: {
|
|
14385
14842
|
display: "flex",
|
|
14386
14843
|
flexDirection: "column",
|
|
@@ -14460,6 +14917,18 @@ var useStyles12 = reactComponents.makeStyles({
|
|
|
14460
14917
|
},
|
|
14461
14918
|
// Scroll viewport for the merged grid so a tall result set scrolls inside the dialog.
|
|
14462
14919
|
dialogGrid: { maxHeight: "60vh", overflowY: "auto", overflowX: "auto" },
|
|
14920
|
+
// Merged-snapshot cells. Keys and sources are long single tokens (dotted keys,
|
|
14921
|
+
// backslash-only absolute paths) with no natural break opportunity, so without
|
|
14922
|
+
// this the text paints straight over the neighbouring column. `overflow-wrap:
|
|
14923
|
+
// anywhere` (not just `break-word`) is what makes the flex cell honour its
|
|
14924
|
+
// column width — it counts the soft breaks toward min-content — so the token
|
|
14925
|
+
// wraps inside the column instead of stretching it. Same recipe as
|
|
14926
|
+
// `sourceCell` in the per-source loads table above.
|
|
14927
|
+
mergedCell: {
|
|
14928
|
+
whiteSpace: "normal",
|
|
14929
|
+
overflowWrap: "anywhere",
|
|
14930
|
+
wordBreak: "break-word"
|
|
14931
|
+
},
|
|
14463
14932
|
filterInput: { minWidth: "260px" },
|
|
14464
14933
|
pager: { display: "flex", alignItems: "center", gap: reactComponents.tokens.spacingHorizontalS },
|
|
14465
14934
|
grow: { flex: 1 },
|
|
@@ -14491,7 +14960,7 @@ function cultureLabel(culture) {
|
|
|
14491
14960
|
}
|
|
14492
14961
|
}
|
|
14493
14962
|
function LocalizationAdmin({ className }) {
|
|
14494
|
-
const styles =
|
|
14963
|
+
const styles = useStyles13();
|
|
14495
14964
|
const t = react$1.useT();
|
|
14496
14965
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
14497
14966
|
const [overview, setOverview] = react.useState(null);
|
|
@@ -14941,7 +15410,7 @@ function MergedSourcesDialog({
|
|
|
14941
15410
|
size: "small",
|
|
14942
15411
|
children: [
|
|
14943
15412
|
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridRow, { children: ({ renderHeaderCell }) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridHeaderCell, { children: renderHeaderCell() }) }) }),
|
|
14944
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridBody, { children: ({ item, rowId }) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridRow, { children: ({ renderCell }) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridCell, { children: renderCell(item) }) }, rowId) })
|
|
15413
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridBody, { children: ({ item, rowId }) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridRow, { children: ({ renderCell }) => /* @__PURE__ */ jsxRuntime.jsx(reactComponents.DataGridCell, { className: styles.mergedCell, children: renderCell(item) }) }, rowId) })
|
|
14945
15414
|
]
|
|
14946
15415
|
}
|
|
14947
15416
|
) })
|
|
@@ -14961,7 +15430,7 @@ function MergedSourcesDialog({
|
|
|
14961
15430
|
] })
|
|
14962
15431
|
] }) }) });
|
|
14963
15432
|
}
|
|
14964
|
-
var
|
|
15433
|
+
var useStyles14 = reactComponents.makeStyles({
|
|
14965
15434
|
root: {
|
|
14966
15435
|
display: "flex",
|
|
14967
15436
|
flexDirection: "column",
|
|
@@ -14984,7 +15453,7 @@ var useStyles13 = reactComponents.makeStyles({
|
|
|
14984
15453
|
});
|
|
14985
15454
|
var toMs = (value) => Number(value);
|
|
14986
15455
|
function CacheAdmin({ className }) {
|
|
14987
|
-
const styles =
|
|
15456
|
+
const styles = useStyles14();
|
|
14988
15457
|
const t = react$1.useT();
|
|
14989
15458
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
14990
15459
|
const [cacheNames, setCacheNames] = react.useState([]);
|
|
@@ -15119,7 +15588,7 @@ function CacheAdmin({ className }) {
|
|
|
15119
15588
|
] }) : null
|
|
15120
15589
|
] });
|
|
15121
15590
|
}
|
|
15122
|
-
var
|
|
15591
|
+
var useStyles15 = reactComponents.makeStyles({
|
|
15123
15592
|
list: {
|
|
15124
15593
|
margin: 0,
|
|
15125
15594
|
paddingLeft: reactComponents.tokens.spacingHorizontalXL,
|
|
@@ -15134,7 +15603,7 @@ function ValidationSummary({
|
|
|
15134
15603
|
className,
|
|
15135
15604
|
style
|
|
15136
15605
|
}) {
|
|
15137
|
-
const styles =
|
|
15606
|
+
const styles = useStyles15();
|
|
15138
15607
|
const t = react$1.useT();
|
|
15139
15608
|
const validation = react$1.useValidationContext();
|
|
15140
15609
|
const resolvedTitle = title === void 0 ? t("app.errors.form-validation-errors") : title;
|
|
@@ -16011,7 +16480,7 @@ function isWizardRecordPageElement(node) {
|
|
|
16011
16480
|
const type = node.type;
|
|
16012
16481
|
return !!type && type[PAGE_MARKER] === true;
|
|
16013
16482
|
}
|
|
16014
|
-
var
|
|
16483
|
+
var useStyles16 = reactComponents.makeStyles({
|
|
16015
16484
|
shell: {
|
|
16016
16485
|
display: "flex",
|
|
16017
16486
|
flexDirection: "column",
|
|
@@ -16153,7 +16622,7 @@ function WizardRecordFormInner({
|
|
|
16153
16622
|
stepperVisibility,
|
|
16154
16623
|
isCreate
|
|
16155
16624
|
}) {
|
|
16156
|
-
const styles =
|
|
16625
|
+
const styles = useStyles16();
|
|
16157
16626
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reactComponents.mergeClasses("ppp-wizard-record-form", styles.shell), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16158
16627
|
reactUseWizard.Wizard,
|
|
16159
16628
|
{
|
|
@@ -16187,7 +16656,7 @@ function WizardFooter({
|
|
|
16187
16656
|
onCancel,
|
|
16188
16657
|
cancelLabel
|
|
16189
16658
|
}) {
|
|
16190
|
-
const styles =
|
|
16659
|
+
const styles = useStyles16();
|
|
16191
16660
|
const t = react$1.useT();
|
|
16192
16661
|
const recordContext = react$1.useRecordContext();
|
|
16193
16662
|
const wizard = reactUseWizard.useWizard();
|
|
@@ -17669,13 +18138,17 @@ var usePageLayoutStyles = reactComponents.makeStyles({
|
|
|
17669
18138
|
position: "relative",
|
|
17670
18139
|
display: "grid",
|
|
17671
18140
|
gridTemplateColumns: "auto minmax(0, 1fr)",
|
|
17672
|
-
|
|
18141
|
+
// Four rows, not three: the first carries the impersonation banner. It's an `auto` track and
|
|
18142
|
+
// the banner renders nothing for an ordinary session, so the row collapses to zero height and
|
|
18143
|
+
// the layout is unchanged whenever nobody is impersonating.
|
|
18144
|
+
gridTemplateRows: "auto auto 1fr auto",
|
|
17673
18145
|
// Lets the PageLayout shrink when it's nested inside another
|
|
17674
18146
|
// PageLayout's `<main>` (the outer main is a grid item; without
|
|
17675
18147
|
// `min-width: 0` on this inner root the outer's `minmax(0, 1fr)`
|
|
17676
18148
|
// track wouldn't be honored once content tries to grow wider).
|
|
17677
18149
|
minWidth: 0,
|
|
17678
18150
|
gridTemplateAreas: `
|
|
18151
|
+
'impersonation impersonation'
|
|
17679
18152
|
'header header'
|
|
17680
18153
|
'navigation main'
|
|
17681
18154
|
'footer footer'
|
|
@@ -17785,6 +18258,11 @@ var usePageLayoutStyles = reactComponents.makeStyles({
|
|
|
17785
18258
|
childHeader: {
|
|
17786
18259
|
borderBottomWidth: "1px"
|
|
17787
18260
|
},
|
|
18261
|
+
impersonationBanner: {
|
|
18262
|
+
// Placement only. The banner is a Fluent MessageBar and Fluent owns everything about how it
|
|
18263
|
+
// looks; this grid assignment is the extent of PageLayout's involvement.
|
|
18264
|
+
gridArea: "impersonation"
|
|
18265
|
+
},
|
|
17788
18266
|
navigation: {
|
|
17789
18267
|
gridArea: "navigation",
|
|
17790
18268
|
overflowY: "auto",
|
|
@@ -17911,6 +18389,7 @@ function PageLayout({
|
|
|
17911
18389
|
...resizable ? { "--ppp-nav-width": `${navWidth}px` } : {}
|
|
17912
18390
|
};
|
|
17913
18391
|
return /* @__PURE__ */ jsxRuntime.jsx(PageLayoutNestingContext.Provider, { value: { isNested: true }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: rootClass, style: rootStyle, children: [
|
|
18392
|
+
!isNested && /* @__PURE__ */ jsxRuntime.jsx(ImpersonationBanner, { className: styles.impersonationBanner }),
|
|
17914
18393
|
header !== void 0 && headerVisible && /* @__PURE__ */ jsxRuntime.jsx(Header, { className: headerClass, ...headerStyle && { style: headerStyle }, children: header }),
|
|
17915
18394
|
navigation !== void 0 && navigationVisible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17916
18395
|
"nav",
|
|
@@ -18139,7 +18618,7 @@ var RADIAL_CHART_TYPES = /* @__PURE__ */ new Set([
|
|
|
18139
18618
|
reactCharts.ChartType.Radar,
|
|
18140
18619
|
reactCharts.ChartType.Funnel
|
|
18141
18620
|
]);
|
|
18142
|
-
var
|
|
18621
|
+
var useStyles17 = reactComponents.makeStyles({
|
|
18143
18622
|
// The wrapper uses Fluent design tokens directly so the rounded bordered
|
|
18144
18623
|
// look picks up theme changes. `colorNeutralBackground1` (white in light,
|
|
18145
18624
|
// grey[16] in dark) matches the Blazor FluentUIChart's
|
|
@@ -18217,7 +18696,7 @@ function FluentUIChartImpl(props, ref) {
|
|
|
18217
18696
|
showBorder = true,
|
|
18218
18697
|
onElementClick
|
|
18219
18698
|
} = props;
|
|
18220
|
-
const styles =
|
|
18699
|
+
const styles = useStyles17();
|
|
18221
18700
|
const containerRef = react.useRef(null);
|
|
18222
18701
|
const palette = useFluentChartPalette(containerRef);
|
|
18223
18702
|
const themedDatasets = react.useMemo(() => {
|
|
@@ -18298,7 +18777,7 @@ function assembleTheme(palette, yAxisPrefix, yAxisSuffix, xAxisPrefix, xAxisSuff
|
|
|
18298
18777
|
if (orientation !== void 0) theme.indexAxis = reactCharts.toIndexAxis(orientation);
|
|
18299
18778
|
return Object.keys(theme).length > 0 ? theme : void 0;
|
|
18300
18779
|
}
|
|
18301
|
-
var
|
|
18780
|
+
var useStyles18 = reactComponents.makeStyles({
|
|
18302
18781
|
// Outer container — flex column so the view selector, message bar, and
|
|
18303
18782
|
// chart fill share the caller's height budget.
|
|
18304
18783
|
root: {
|
|
@@ -18376,7 +18855,7 @@ var DataverseChart = react.forwardRef(
|
|
|
18376
18855
|
} = props;
|
|
18377
18856
|
const chartSource = dataSource instanceof reactCharts.DataverseChartDataSource ? dataSource : void 0;
|
|
18378
18857
|
const viewDs = dataSource instanceof reactCharts.DataverseChartDataSource ? void 0 : dataSource;
|
|
18379
|
-
const styles =
|
|
18858
|
+
const styles = useStyles18();
|
|
18380
18859
|
const t = react$1.useT();
|
|
18381
18860
|
const { ppp, viewMetadataCache } = react$1.usePowerPortalsPro();
|
|
18382
18861
|
const [data, setData] = react.useState({ labels: [], datasets: [] });
|
|
@@ -18646,6 +19125,8 @@ exports.Header = Header;
|
|
|
18646
19125
|
exports.Highlighter = Highlighter;
|
|
18647
19126
|
exports.ImageEdit = ImageEdit;
|
|
18648
19127
|
exports.ImageViewer = ImageViewer;
|
|
19128
|
+
exports.ImpersonationBanner = ImpersonationBanner;
|
|
19129
|
+
exports.ImpersonationPickerDialog = ImpersonationPickerDialog;
|
|
18649
19130
|
exports.LabelPosition = LabelPosition;
|
|
18650
19131
|
exports.LanguageDropdown = LanguageDropdown;
|
|
18651
19132
|
exports.LinkExistingRecordGridButton = LinkExistingRecordGridButton;
|