@powerportalspro/react-fluent 6.1.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 +523 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -3
- package/dist/index.d.ts +94 -3
- package/dist/index.js +526 -108
- 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 });
|
|
@@ -8412,6 +8430,12 @@ var ViewSort = {
|
|
|
8412
8430
|
/** Localized display name, descending. */
|
|
8413
8431
|
NameDescending: "nameDescending"
|
|
8414
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
|
+
}
|
|
8415
8439
|
function customToViewMetadata(custom) {
|
|
8416
8440
|
return {
|
|
8417
8441
|
id: custom.id,
|
|
@@ -9088,7 +9112,7 @@ function MainGridImpl({
|
|
|
9088
9112
|
const rowRecord = cellCtx.row.original;
|
|
9089
9113
|
const rowId = rowRecord.id;
|
|
9090
9114
|
const isPendingCreateRow = !!rowId && rowId.startsWith("__pending-create-");
|
|
9091
|
-
if (editable && !isPendingCreateRow && hasEditorForType) {
|
|
9115
|
+
if (editable && !isPendingCreateRow && hasEditorForType && core.canWriteColumn(rowRecord, columnName)) {
|
|
9092
9116
|
return (
|
|
9093
9117
|
// Sizing wrapper — overrides Fluent v9's default
|
|
9094
9118
|
// <Input> / <Combobox> / <Dropdown> min-width
|
|
@@ -9778,8 +9802,8 @@ function MainGridImpl({
|
|
|
9778
9802
|
};
|
|
9779
9803
|
const selectedView = views?.find((v) => v.id === resolvedViewId);
|
|
9780
9804
|
const selectedViewLabelKey = selectedView ? `tables.${selectedView.tableName}.views.${selectedView.id}.label` : "";
|
|
9781
|
-
const selectedViewLabel = selectedView ? t
|
|
9782
|
-
const singleViewTitle = selectedView
|
|
9805
|
+
const selectedViewLabel = selectedView ? resolveViewLabel(selectedView, t) ?? selectedViewLabelKey : "";
|
|
9806
|
+
const singleViewTitle = selectedView ? resolveViewLabel(selectedView, t) ?? void 0 : void 0;
|
|
9783
9807
|
return /* @__PURE__ */ jsxRuntime.jsx(GridContextProvider, { value: gridContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9784
9808
|
"div",
|
|
9785
9809
|
{
|
|
@@ -9812,7 +9836,7 @@ function MainGridImpl({
|
|
|
9812
9836
|
if (data2.optionValue) handleViewChange(data2.optionValue);
|
|
9813
9837
|
},
|
|
9814
9838
|
children: views?.map((view) => {
|
|
9815
|
-
const label = t
|
|
9839
|
+
const label = resolveViewLabel(view, t) ?? `tables.${view.tableName}.views.${view.id}.label`;
|
|
9816
9840
|
return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Option, { value: view.id, text: label, children: label }, view.id);
|
|
9817
9841
|
})
|
|
9818
9842
|
}
|
|
@@ -10908,7 +10932,8 @@ function LookupEdit({
|
|
|
10908
10932
|
metadata,
|
|
10909
10933
|
record,
|
|
10910
10934
|
readOnly,
|
|
10911
|
-
disabled
|
|
10935
|
+
disabled,
|
|
10936
|
+
columnName
|
|
10912
10937
|
);
|
|
10913
10938
|
const setLookupValue = useGuardedSetValue(
|
|
10914
10939
|
rawSetLookupValue,
|
|
@@ -12688,6 +12713,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12688
12713
|
defaultMode = ThemeMode.System,
|
|
12689
12714
|
defaultAccentColor = "default",
|
|
12690
12715
|
defaultTextDirection = TextDirection.Ltr,
|
|
12716
|
+
lockAccentColor = false,
|
|
12691
12717
|
customAccents,
|
|
12692
12718
|
storageKey = DEFAULT_MODE_STORAGE_KEY,
|
|
12693
12719
|
style,
|
|
@@ -12711,9 +12737,11 @@ function PowerPortalsProThemeProvider({
|
|
|
12711
12737
|
() => readStoredMode(storageKey) ?? defaultMode
|
|
12712
12738
|
);
|
|
12713
12739
|
const [accentColor, setAccentColorState] = react.useState(() => {
|
|
12740
|
+
const configured = defaultAccentColor in accentMap ? defaultAccentColor : "default";
|
|
12741
|
+
if (lockAccentColor) return configured;
|
|
12714
12742
|
const stored = readStoredString(accentStorageKey);
|
|
12715
12743
|
if (stored && stored in accentMap) return stored;
|
|
12716
|
-
return
|
|
12744
|
+
return configured;
|
|
12717
12745
|
});
|
|
12718
12746
|
const [textDirection, setTextDirectionState] = react.useState(
|
|
12719
12747
|
() => readStoredDirection(directionStorageKey) ?? defaultTextDirection
|
|
@@ -12743,6 +12771,12 @@ function PowerPortalsProThemeProvider({
|
|
|
12743
12771
|
document.body.classList.toggle("ppp-theme-dark", isDark);
|
|
12744
12772
|
document.body.classList.toggle("ppp-theme-light", !isDark);
|
|
12745
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]);
|
|
12746
12780
|
const setMode = react.useCallback(
|
|
12747
12781
|
(next) => {
|
|
12748
12782
|
setModeState(next);
|
|
@@ -12752,11 +12786,12 @@ function PowerPortalsProThemeProvider({
|
|
|
12752
12786
|
);
|
|
12753
12787
|
const setAccentColor = react.useCallback(
|
|
12754
12788
|
(next) => {
|
|
12789
|
+
if (lockAccentColor) return;
|
|
12755
12790
|
if (!(next in accentMap)) return;
|
|
12756
12791
|
setAccentColorState(next);
|
|
12757
12792
|
writeStored(accentStorageKey, next);
|
|
12758
12793
|
},
|
|
12759
|
-
[accentMap, accentStorageKey]
|
|
12794
|
+
[lockAccentColor, accentMap, accentStorageKey]
|
|
12760
12795
|
);
|
|
12761
12796
|
const setTextDirection = react.useCallback(
|
|
12762
12797
|
(next) => {
|
|
@@ -12781,6 +12816,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12781
12816
|
setAccentColor,
|
|
12782
12817
|
availableAccents,
|
|
12783
12818
|
accentColors,
|
|
12819
|
+
accentColorLocked: lockAccentColor,
|
|
12784
12820
|
textDirection,
|
|
12785
12821
|
setTextDirection
|
|
12786
12822
|
}),
|
|
@@ -12792,6 +12828,7 @@ function PowerPortalsProThemeProvider({
|
|
|
12792
12828
|
setAccentColor,
|
|
12793
12829
|
availableAccents,
|
|
12794
12830
|
accentColors,
|
|
12831
|
+
lockAccentColor,
|
|
12795
12832
|
textDirection,
|
|
12796
12833
|
setTextDirection
|
|
12797
12834
|
]
|
|
@@ -12872,7 +12909,7 @@ function ThemeColorSelector({
|
|
|
12872
12909
|
width,
|
|
12873
12910
|
accentLabels
|
|
12874
12911
|
}) {
|
|
12875
|
-
const { accentColor, setAccentColor, availableAccents, accentColors } = useTheme();
|
|
12912
|
+
const { accentColor, setAccentColor, availableAccents, accentColors, accentColorLocked } = useTheme();
|
|
12876
12913
|
const t = react$1.useT();
|
|
12877
12914
|
const labelId = react.useId();
|
|
12878
12915
|
const styles = useStyles6();
|
|
@@ -12886,6 +12923,7 @@ function ThemeColorSelector({
|
|
|
12886
12923
|
},
|
|
12887
12924
|
[t, accentLabels]
|
|
12888
12925
|
);
|
|
12926
|
+
if (accentColorLocked) return null;
|
|
12889
12927
|
const handleOptionSelect = (_event, data) => {
|
|
12890
12928
|
const next = data.optionValue;
|
|
12891
12929
|
if (next && availableAccents.includes(next)) {
|
|
@@ -13053,7 +13091,304 @@ function LanguageDropdown({
|
|
|
13053
13091
|
}
|
|
13054
13092
|
);
|
|
13055
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;
|
|
13056
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({
|
|
13057
13392
|
trigger: {
|
|
13058
13393
|
backgroundColor: "transparent",
|
|
13059
13394
|
border: "none",
|
|
@@ -13113,8 +13448,9 @@ function ProfileMainMenu({
|
|
|
13113
13448
|
signInUrl = "/login"
|
|
13114
13449
|
}) {
|
|
13115
13450
|
const auth = react$1.useAuth();
|
|
13116
|
-
const styles =
|
|
13451
|
+
const styles = useStyles8();
|
|
13117
13452
|
const t = react$1.useT();
|
|
13453
|
+
const [isPickerOpen, setIsPickerOpen] = react.useState(false);
|
|
13118
13454
|
const handleLogout = react.useCallback(async () => {
|
|
13119
13455
|
try {
|
|
13120
13456
|
await auth.logout();
|
|
@@ -13131,7 +13467,14 @@ function ProfileMainMenu({
|
|
|
13131
13467
|
}
|
|
13132
13468
|
}, [auth]);
|
|
13133
13469
|
if (auth.status === react$1.AuthStatus.Loading) {
|
|
13134
|
-
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
|
+
);
|
|
13135
13478
|
}
|
|
13136
13479
|
if (auth.status === react$1.AuthStatus.Anonymous) {
|
|
13137
13480
|
if (anonymousContent !== void 0) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: anonymousContent });
|
|
@@ -13152,61 +13495,126 @@ function ProfileMainMenu({
|
|
|
13152
13495
|
const resolvedAccountLabel = accountLabel ?? t("app.components.profile-main-menu.account");
|
|
13153
13496
|
const signedInAsLabel = t("app.components.profile-main-menu.signed-in-as");
|
|
13154
13497
|
const logoutLabel = t("app.buttons.logout.label");
|
|
13498
|
+
const canImpersonate = (auth.user.roles?.includes(IMPERSONATION_ROLE) ?? false) && auth.user.portalUserType === core.PortalUserType.SystemUser;
|
|
13155
13499
|
const altIdentityTableName = auth.user.altIdentityTableName;
|
|
13156
|
-
const switchIdentityLabel = altIdentityTableName ? t(
|
|
13157
|
-
"app.components.profile-main-menu.
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
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,
|
|
13165
13561
|
{
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
"aria-label": email || logoutLabel,
|
|
13169
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 28, initials, name: email })
|
|
13562
|
+
open: isPickerOpen,
|
|
13563
|
+
onClose: () => setIsPickerOpen(false)
|
|
13170
13564
|
}
|
|
13171
|
-
)
|
|
13172
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuPopover, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.popoverBody, children: [
|
|
13173
|
-
headerSlot,
|
|
13174
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identity, children: [
|
|
13175
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Avatar, { size: 40, initials, name: email }),
|
|
13176
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.identityText, children: [
|
|
13177
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityLabel, children: signedInAsLabel }),
|
|
13178
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.identityValue, title: email, children: email })
|
|
13179
|
-
] })
|
|
13180
|
-
] }),
|
|
13181
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13182
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactComponents.MenuList, { className: styles.actions, children: [
|
|
13183
|
-
accountUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13184
|
-
reactComponents.MenuItem,
|
|
13185
|
-
{
|
|
13186
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.PersonEdit20Regular, {}),
|
|
13187
|
-
onClick: () => {
|
|
13188
|
-
window.location.href = accountUrl;
|
|
13189
|
-
},
|
|
13190
|
-
children: resolvedAccountLabel
|
|
13191
|
-
}
|
|
13192
|
-
),
|
|
13193
|
-
switchIdentityLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13194
|
-
reactComponents.MenuItem,
|
|
13195
|
-
{
|
|
13196
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.ArrowSwap20Regular, {}),
|
|
13197
|
-
onClick: handleSwitchIdentity,
|
|
13198
|
-
children: switchIdentityLabel
|
|
13199
|
-
}
|
|
13200
|
-
),
|
|
13201
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.MenuItem, { icon: /* @__PURE__ */ jsxRuntime.jsx(reactIcons.SignOut20Regular, {}), onClick: handleLogout, children: logoutLabel })
|
|
13202
|
-
] }),
|
|
13203
|
-
footerSlot && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13204
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactComponents.Divider, {}),
|
|
13205
|
-
footerSlot
|
|
13206
|
-
] })
|
|
13207
|
-
] }) })
|
|
13565
|
+
)
|
|
13208
13566
|
] });
|
|
13209
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
|
+
}
|
|
13210
13618
|
var usePanelStyles = reactComponents.makeStyles({
|
|
13211
13619
|
// Vertical stack of setting sections with consistent gap. Matches the
|
|
13212
13620
|
// panel body the SiteSettingsButton's drawer renders, but extracted
|
|
@@ -13255,7 +13663,7 @@ function SiteSettingsPanel({
|
|
|
13255
13663
|
}
|
|
13256
13664
|
);
|
|
13257
13665
|
}
|
|
13258
|
-
var
|
|
13666
|
+
var useStyles9 = reactComponents.makeStyles({
|
|
13259
13667
|
// Wraps the standalone panel inside the drawer body to add the small
|
|
13260
13668
|
// top inset that the popover's title visually expects. The panel
|
|
13261
13669
|
// itself stays inset-free so it doesn't carry extra padding when
|
|
@@ -13275,7 +13683,7 @@ function SiteSettingsButton({
|
|
|
13275
13683
|
accentLabels
|
|
13276
13684
|
}) {
|
|
13277
13685
|
const t = react$1.useT();
|
|
13278
|
-
const styles =
|
|
13686
|
+
const styles = useStyles9();
|
|
13279
13687
|
const [open, setOpen] = react.useState(false);
|
|
13280
13688
|
const close = react.useCallback(() => setOpen(false), []);
|
|
13281
13689
|
const label = t("app.site-settings-label");
|
|
@@ -13753,7 +14161,7 @@ function bestRouteMatch(routes, pathname) {
|
|
|
13753
14161
|
}
|
|
13754
14162
|
return best;
|
|
13755
14163
|
}
|
|
13756
|
-
var
|
|
14164
|
+
var useStyles10 = reactComponents.makeStyles({
|
|
13757
14165
|
// Shared scrim look. Both global (full-screen) and scoped variants reuse
|
|
13758
14166
|
// these so the visual feel is identical — only the positioning differs.
|
|
13759
14167
|
// Theme-aware translucent backdrop drawn via a `::before` pseudo-element
|
|
@@ -13815,7 +14223,7 @@ function FluentOverlayProvider({ children }) {
|
|
|
13815
14223
|
] });
|
|
13816
14224
|
}
|
|
13817
14225
|
function GlobalOverlayLayer() {
|
|
13818
|
-
const styles =
|
|
14226
|
+
const styles = useStyles10();
|
|
13819
14227
|
const overlay = react$1.useOverlayForTarget(void 0);
|
|
13820
14228
|
if (!overlay) return null;
|
|
13821
14229
|
return /* @__PURE__ */ jsxRuntime.jsx(ScrimContent, { overlay, className: reactComponents.mergeClasses(styles.scrim, styles.global) });
|
|
@@ -13826,7 +14234,7 @@ function FluentOverlayTarget({
|
|
|
13826
14234
|
className,
|
|
13827
14235
|
style
|
|
13828
14236
|
}) {
|
|
13829
|
-
const styles =
|
|
14237
|
+
const styles = useStyles10();
|
|
13830
14238
|
const overlay = react$1.useOverlayForTarget(targetId);
|
|
13831
14239
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reactComponents.mergeClasses("ppp-overlay-target", styles.scopedHost, className), style, children: [
|
|
13832
14240
|
children,
|
|
@@ -13834,14 +14242,14 @@ function FluentOverlayTarget({
|
|
|
13834
14242
|
] });
|
|
13835
14243
|
}
|
|
13836
14244
|
function ScrimContent({ overlay, className }) {
|
|
13837
|
-
const styles =
|
|
14245
|
+
const styles = useStyles10();
|
|
13838
14246
|
const showProgress = overlay.showProgress !== false;
|
|
13839
14247
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, role: "status", "aria-live": "polite", children: [
|
|
13840
14248
|
showProgress && /* @__PURE__ */ jsxRuntime.jsx(reactComponents.Spinner, { size: "large" }),
|
|
13841
14249
|
overlay.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.description, children: overlay.description })
|
|
13842
14250
|
] });
|
|
13843
14251
|
}
|
|
13844
|
-
var
|
|
14252
|
+
var useStyles11 = reactComponents.makeStyles({
|
|
13845
14253
|
// Centered placeholder used while prefixes are being fetched. Same
|
|
13846
14254
|
// shape as the spinner blocks in `<NewRecordGridButton>` /
|
|
13847
14255
|
// `<OpenRecordGridButton>` dialogs (~160px min height + vertical
|
|
@@ -13861,7 +14269,7 @@ function LocalizationBoundary({
|
|
|
13861
14269
|
fallback,
|
|
13862
14270
|
children
|
|
13863
14271
|
}) {
|
|
13864
|
-
const styles =
|
|
14272
|
+
const styles = useStyles11();
|
|
13865
14273
|
const resolvedPrefixes = prefixes ?? EMPTY_PREFIXES;
|
|
13866
14274
|
const { locale } = react$1.useLocale();
|
|
13867
14275
|
react$1.useLocalization(resolvedPrefixes);
|
|
@@ -13883,7 +14291,7 @@ function LocalizationBoundary({
|
|
|
13883
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" }) });
|
|
13884
14292
|
}
|
|
13885
14293
|
var EMPTY_PREFIXES = Object.freeze([]);
|
|
13886
|
-
var
|
|
14294
|
+
var useStyles12 = reactComponents.makeStyles({
|
|
13887
14295
|
root: {
|
|
13888
14296
|
display: "flex",
|
|
13889
14297
|
flexDirection: "column",
|
|
@@ -13941,7 +14349,7 @@ function isSameLanguage(a, b) {
|
|
|
13941
14349
|
return primaryA.length > 0 && primaryA === primary(b);
|
|
13942
14350
|
}
|
|
13943
14351
|
function LocalizationTranslator({ className }) {
|
|
13944
|
-
const styles =
|
|
14352
|
+
const styles = useStyles12();
|
|
13945
14353
|
const t = react$1.useT();
|
|
13946
14354
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
13947
14355
|
const [availability, setAvailability] = react.useState(null);
|
|
@@ -14429,7 +14837,7 @@ function SectionColumnRenderer({
|
|
|
14429
14837
|
}
|
|
14430
14838
|
);
|
|
14431
14839
|
}
|
|
14432
|
-
var
|
|
14840
|
+
var useStyles13 = reactComponents.makeStyles({
|
|
14433
14841
|
root: {
|
|
14434
14842
|
display: "flex",
|
|
14435
14843
|
flexDirection: "column",
|
|
@@ -14552,7 +14960,7 @@ function cultureLabel(culture) {
|
|
|
14552
14960
|
}
|
|
14553
14961
|
}
|
|
14554
14962
|
function LocalizationAdmin({ className }) {
|
|
14555
|
-
const styles =
|
|
14963
|
+
const styles = useStyles13();
|
|
14556
14964
|
const t = react$1.useT();
|
|
14557
14965
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
14558
14966
|
const [overview, setOverview] = react.useState(null);
|
|
@@ -15022,7 +15430,7 @@ function MergedSourcesDialog({
|
|
|
15022
15430
|
] })
|
|
15023
15431
|
] }) }) });
|
|
15024
15432
|
}
|
|
15025
|
-
var
|
|
15433
|
+
var useStyles14 = reactComponents.makeStyles({
|
|
15026
15434
|
root: {
|
|
15027
15435
|
display: "flex",
|
|
15028
15436
|
flexDirection: "column",
|
|
@@ -15045,7 +15453,7 @@ var useStyles13 = reactComponents.makeStyles({
|
|
|
15045
15453
|
});
|
|
15046
15454
|
var toMs = (value) => Number(value);
|
|
15047
15455
|
function CacheAdmin({ className }) {
|
|
15048
|
-
const styles =
|
|
15456
|
+
const styles = useStyles14();
|
|
15049
15457
|
const t = react$1.useT();
|
|
15050
15458
|
const { ppp } = react$1.usePowerPortalsPro();
|
|
15051
15459
|
const [cacheNames, setCacheNames] = react.useState([]);
|
|
@@ -15180,7 +15588,7 @@ function CacheAdmin({ className }) {
|
|
|
15180
15588
|
] }) : null
|
|
15181
15589
|
] });
|
|
15182
15590
|
}
|
|
15183
|
-
var
|
|
15591
|
+
var useStyles15 = reactComponents.makeStyles({
|
|
15184
15592
|
list: {
|
|
15185
15593
|
margin: 0,
|
|
15186
15594
|
paddingLeft: reactComponents.tokens.spacingHorizontalXL,
|
|
@@ -15195,7 +15603,7 @@ function ValidationSummary({
|
|
|
15195
15603
|
className,
|
|
15196
15604
|
style
|
|
15197
15605
|
}) {
|
|
15198
|
-
const styles =
|
|
15606
|
+
const styles = useStyles15();
|
|
15199
15607
|
const t = react$1.useT();
|
|
15200
15608
|
const validation = react$1.useValidationContext();
|
|
15201
15609
|
const resolvedTitle = title === void 0 ? t("app.errors.form-validation-errors") : title;
|
|
@@ -16072,7 +16480,7 @@ function isWizardRecordPageElement(node) {
|
|
|
16072
16480
|
const type = node.type;
|
|
16073
16481
|
return !!type && type[PAGE_MARKER] === true;
|
|
16074
16482
|
}
|
|
16075
|
-
var
|
|
16483
|
+
var useStyles16 = reactComponents.makeStyles({
|
|
16076
16484
|
shell: {
|
|
16077
16485
|
display: "flex",
|
|
16078
16486
|
flexDirection: "column",
|
|
@@ -16214,7 +16622,7 @@ function WizardRecordFormInner({
|
|
|
16214
16622
|
stepperVisibility,
|
|
16215
16623
|
isCreate
|
|
16216
16624
|
}) {
|
|
16217
|
-
const styles =
|
|
16625
|
+
const styles = useStyles16();
|
|
16218
16626
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reactComponents.mergeClasses("ppp-wizard-record-form", styles.shell), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16219
16627
|
reactUseWizard.Wizard,
|
|
16220
16628
|
{
|
|
@@ -16248,7 +16656,7 @@ function WizardFooter({
|
|
|
16248
16656
|
onCancel,
|
|
16249
16657
|
cancelLabel
|
|
16250
16658
|
}) {
|
|
16251
|
-
const styles =
|
|
16659
|
+
const styles = useStyles16();
|
|
16252
16660
|
const t = react$1.useT();
|
|
16253
16661
|
const recordContext = react$1.useRecordContext();
|
|
16254
16662
|
const wizard = reactUseWizard.useWizard();
|
|
@@ -17730,13 +18138,17 @@ var usePageLayoutStyles = reactComponents.makeStyles({
|
|
|
17730
18138
|
position: "relative",
|
|
17731
18139
|
display: "grid",
|
|
17732
18140
|
gridTemplateColumns: "auto minmax(0, 1fr)",
|
|
17733
|
-
|
|
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",
|
|
17734
18145
|
// Lets the PageLayout shrink when it's nested inside another
|
|
17735
18146
|
// PageLayout's `<main>` (the outer main is a grid item; without
|
|
17736
18147
|
// `min-width: 0` on this inner root the outer's `minmax(0, 1fr)`
|
|
17737
18148
|
// track wouldn't be honored once content tries to grow wider).
|
|
17738
18149
|
minWidth: 0,
|
|
17739
18150
|
gridTemplateAreas: `
|
|
18151
|
+
'impersonation impersonation'
|
|
17740
18152
|
'header header'
|
|
17741
18153
|
'navigation main'
|
|
17742
18154
|
'footer footer'
|
|
@@ -17846,6 +18258,11 @@ var usePageLayoutStyles = reactComponents.makeStyles({
|
|
|
17846
18258
|
childHeader: {
|
|
17847
18259
|
borderBottomWidth: "1px"
|
|
17848
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
|
+
},
|
|
17849
18266
|
navigation: {
|
|
17850
18267
|
gridArea: "navigation",
|
|
17851
18268
|
overflowY: "auto",
|
|
@@ -17972,6 +18389,7 @@ function PageLayout({
|
|
|
17972
18389
|
...resizable ? { "--ppp-nav-width": `${navWidth}px` } : {}
|
|
17973
18390
|
};
|
|
17974
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 }),
|
|
17975
18393
|
header !== void 0 && headerVisible && /* @__PURE__ */ jsxRuntime.jsx(Header, { className: headerClass, ...headerStyle && { style: headerStyle }, children: header }),
|
|
17976
18394
|
navigation !== void 0 && navigationVisible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17977
18395
|
"nav",
|
|
@@ -18200,7 +18618,7 @@ var RADIAL_CHART_TYPES = /* @__PURE__ */ new Set([
|
|
|
18200
18618
|
reactCharts.ChartType.Radar,
|
|
18201
18619
|
reactCharts.ChartType.Funnel
|
|
18202
18620
|
]);
|
|
18203
|
-
var
|
|
18621
|
+
var useStyles17 = reactComponents.makeStyles({
|
|
18204
18622
|
// The wrapper uses Fluent design tokens directly so the rounded bordered
|
|
18205
18623
|
// look picks up theme changes. `colorNeutralBackground1` (white in light,
|
|
18206
18624
|
// grey[16] in dark) matches the Blazor FluentUIChart's
|
|
@@ -18278,7 +18696,7 @@ function FluentUIChartImpl(props, ref) {
|
|
|
18278
18696
|
showBorder = true,
|
|
18279
18697
|
onElementClick
|
|
18280
18698
|
} = props;
|
|
18281
|
-
const styles =
|
|
18699
|
+
const styles = useStyles17();
|
|
18282
18700
|
const containerRef = react.useRef(null);
|
|
18283
18701
|
const palette = useFluentChartPalette(containerRef);
|
|
18284
18702
|
const themedDatasets = react.useMemo(() => {
|
|
@@ -18359,7 +18777,7 @@ function assembleTheme(palette, yAxisPrefix, yAxisSuffix, xAxisPrefix, xAxisSuff
|
|
|
18359
18777
|
if (orientation !== void 0) theme.indexAxis = reactCharts.toIndexAxis(orientation);
|
|
18360
18778
|
return Object.keys(theme).length > 0 ? theme : void 0;
|
|
18361
18779
|
}
|
|
18362
|
-
var
|
|
18780
|
+
var useStyles18 = reactComponents.makeStyles({
|
|
18363
18781
|
// Outer container — flex column so the view selector, message bar, and
|
|
18364
18782
|
// chart fill share the caller's height budget.
|
|
18365
18783
|
root: {
|
|
@@ -18437,7 +18855,7 @@ var DataverseChart = react.forwardRef(
|
|
|
18437
18855
|
} = props;
|
|
18438
18856
|
const chartSource = dataSource instanceof reactCharts.DataverseChartDataSource ? dataSource : void 0;
|
|
18439
18857
|
const viewDs = dataSource instanceof reactCharts.DataverseChartDataSource ? void 0 : dataSource;
|
|
18440
|
-
const styles =
|
|
18858
|
+
const styles = useStyles18();
|
|
18441
18859
|
const t = react$1.useT();
|
|
18442
18860
|
const { ppp, viewMetadataCache } = react$1.usePowerPortalsPro();
|
|
18443
18861
|
const [data, setData] = react.useState({ labels: [], datasets: [] });
|
|
@@ -18707,6 +19125,8 @@ exports.Header = Header;
|
|
|
18707
19125
|
exports.Highlighter = Highlighter;
|
|
18708
19126
|
exports.ImageEdit = ImageEdit;
|
|
18709
19127
|
exports.ImageViewer = ImageViewer;
|
|
19128
|
+
exports.ImpersonationBanner = ImpersonationBanner;
|
|
19129
|
+
exports.ImpersonationPickerDialog = ImpersonationPickerDialog;
|
|
18710
19130
|
exports.LabelPosition = LabelPosition;
|
|
18711
19131
|
exports.LanguageDropdown = LanguageDropdown;
|
|
18712
19132
|
exports.LinkExistingRecordGridButton = LinkExistingRecordGridButton;
|