@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.js CHANGED
@@ -1,9 +1,9 @@
1
- import { makeStyles, mergeClasses, Input, tokens, Toolbar, ToolbarButton, ToolbarDivider, Menu, MenuTrigger, MenuPopover, MenuList, MenuItemRadio, Button, createLightTheme, createDarkTheme, makeStaticStyles, MessageBar, MessageBarBody, Dropdown, Option, Spinner, Field, Textarea, Switch, Checkbox, Label, TabList, Tab, Tooltip, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, RadioGroup, Radio, useId as useId$1, Text, Overflow, OverflowItem, SearchBox, CounterBadge, Popover, PopoverTrigger, PopoverSurface, MenuItem, Combobox, TagPicker, TagPickerControl, TagPickerGroup, Tag, TagPickerInput, TagPickerList, TagPickerOption, webDarkTheme, webLightTheme, teamsDarkTheme, teamsLightTheme, FluentProvider, Avatar, Divider, OverlayDrawer, DrawerHeader, DrawerHeaderTitle, DrawerBody, useOverflowMenu, useIsOverflowItemVisible, Title3, Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, createTableColumn, DataGrid, DataGridHeader, DataGridRow, DataGridHeaderCell, DataGridBody, DataGridCell, MessageBarTitle, DialogTrigger } from '@fluentui/react-components';
2
- import { MaskMode, useMaskedTextField, useT, usePowerPortalsPro, useRecordContext, useColumnMetadata, useLocale, MainContext, useTableMetadata, useWarnAboutUnsavedChanges, useMainContext, useViewsForTable, useViewMetadata, QueryStatus, useGridData, RecordContext, DialogServiceProvider, useUrlCultureStrategy, useDefaultLocale, useSupportedLocales, setAspNetCultureCookie, buildLocalePath, useAuth, AuthStatus, useOverlayServiceState, OverlayServiceProvider, useOverlayForTarget, useLocalization, useLocalizationLoaded, useValidationContext, useDialogService, useRunWithOverlay, useRecordContextOptional, useOrganizationSettings, useUnsavedChangesGuard, useColumnValidationErrors, isKeyModeEnabled } from '@powerportalspro/react';
1
+ import { makeStyles, mergeClasses, Input, tokens, Toolbar, ToolbarButton, ToolbarDivider, Menu, MenuTrigger, MenuPopover, MenuList, MenuItemRadio, Button, createLightTheme, createDarkTheme, makeStaticStyles, MessageBar, MessageBarBody, Dropdown, Option, Spinner, Field, Textarea, Switch, Checkbox, Label, TabList, Tab, Tooltip, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, RadioGroup, Radio, useId as useId$1, Text, Overflow, OverflowItem, SearchBox, CounterBadge, Popover, PopoverTrigger, PopoverSurface, MenuItem, Combobox, TagPicker, TagPickerControl, TagPickerGroup, Tag, TagPickerInput, TagPickerList, TagPickerOption, webDarkTheme, webLightTheme, teamsDarkTheme, teamsLightTheme, FluentProvider, Avatar, Divider, MessageBarTitle, MessageBarActions, OverlayDrawer, DrawerHeader, DrawerHeaderTitle, DrawerBody, useOverflowMenu, useIsOverflowItemVisible, Title3, Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, createTableColumn, DataGrid, DataGridHeader, DataGridRow, DataGridHeaderCell, DataGridBody, DataGridCell, DialogTrigger } from '@fluentui/react-components';
2
+ import { MaskMode, useMaskedTextField, useT, usePowerPortalsPro, useRecordContext, useColumnMetadata, useLocale, useRecordContextOptional, MainContext, useTableMetadata, useWarnAboutUnsavedChanges, useMainContext, useViewsForTable, useViewMetadata, QueryStatus, useGridData, RecordContext, DialogServiceProvider, useUrlCultureStrategy, useDefaultLocale, useSupportedLocales, setAspNetCultureCookie, buildLocalePath, useAuth, AuthStatus, useOverlayServiceState, OverlayServiceProvider, useOverlayForTarget, useLocalization, useLocalizationLoaded, useValidationContext, useDialogService, useRunWithOverlay, useOrganizationSettings, useUnsavedChangesGuard, useColumnValidationErrors, isKeyModeEnabled } from '@powerportalspro/react';
3
3
  import { forwardRef, useRef, useImperativeHandle, useState, useEffect, useCallback, createContext, useMemo, useId, useLayoutEffect, useContext, useTransition, Children, isValidElement } from 'react';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
- import { ArrowRotateCounterclockwise20Regular, ArrowRotateClockwise20Regular, FlipHorizontal20Regular, FlipVertical20Regular, ZoomIn20Regular, ZoomOut20Regular, ArrowReset20Regular, ChevronDown16Regular, Grid20Regular, Crop20Regular, FullScreenMinimize20Regular, FullScreenMaximize20Regular, ArrowDownload20Regular, Prohibited20Regular, Checkmark20Filled, ArrowDownload24Regular, ArrowClockwise24Regular, ChevronUp12Regular, ChevronDown12Regular, DocumentArrowLeft24Regular, ChevronDown20Regular, ChevronRight20Regular, Folder20Regular, Document20Regular, ErrorCircle20Regular, Eye20Regular, Dismiss20Regular, Edit20Regular, Add16Regular, Edit16Regular, LinkAdd16Regular, LinkDismiss16Regular, Delete16Regular, Settings20Regular, Search20Regular, ArrowClockwise20Regular, ArrowSortDown16Regular, ArrowSortUp16Regular, Dismiss24Regular, LinkAdd20Regular, QuestionCircle24Regular, Info24Regular, DismissCircle24Regular, Warning24Regular, CheckmarkCircle24Regular, ArrowExit20Regular, PersonEdit20Regular, ArrowSwap20Regular, SignOut20Regular, MoreVerticalRegular, ArrowSyncRegular, EyeRegular, ArrowDownloadRegular, SearchRegular, DismissRegular, Save24Regular, Delete24Regular, Add20Regular, Delete20Regular, LinkDismiss20Regular, ArrowUpload32Regular, ArrowUpload24Regular, ArrowSync24Regular, DrawerArrowDownload24Regular, Money20Regular, ErrorCircle12Filled, DocumentText20Regular, FolderZip20Regular, MusicNote220Regular, Video20Regular, Image20Regular, SlideText20Regular, DocumentTable20Regular, DocumentPdf20Regular, MoreHorizontal20Regular, ChevronDoubleLeft20Regular, ChevronLeft20Regular, ChevronDoubleRight20Regular, Info12Regular, Checkmark16Regular, Dismiss16Regular } from '@fluentui/react-icons';
6
- import { RequiredLevel, DateTimeBehavior, getRecordReferenceId, RelationshipFilterMode, SwitchIdentityResult, TableSecurityPermission } from '@powerportalspro/core';
5
+ import { ArrowRotateCounterclockwise20Regular, ArrowRotateClockwise20Regular, FlipHorizontal20Regular, FlipVertical20Regular, ZoomIn20Regular, ZoomOut20Regular, ArrowReset20Regular, ChevronDown16Regular, Grid20Regular, Crop20Regular, FullScreenMinimize20Regular, FullScreenMaximize20Regular, ArrowDownload20Regular, Prohibited20Regular, Checkmark20Filled, ArrowDownload24Regular, ArrowClockwise24Regular, ChevronUp12Regular, ChevronDown12Regular, DocumentArrowLeft24Regular, ChevronDown20Regular, ChevronRight20Regular, Folder20Regular, Document20Regular, ErrorCircle20Regular, Eye20Regular, Dismiss20Regular, Edit20Regular, Add16Regular, Edit16Regular, LinkAdd16Regular, LinkDismiss16Regular, Delete16Regular, Settings20Regular, Search20Regular, ArrowClockwise20Regular, ArrowSortDown16Regular, ArrowSortUp16Regular, Dismiss24Regular, LinkAdd20Regular, QuestionCircle24Regular, Info24Regular, DismissCircle24Regular, Warning24Regular, CheckmarkCircle24Regular, Person20Regular, Warning20Regular, EyeTracking20Regular, ArrowExit20Regular, PersonEdit20Regular, ArrowSwap20Regular, SignOut20Regular, MoreVerticalRegular, ArrowSyncRegular, EyeRegular, ArrowDownloadRegular, SearchRegular, DismissRegular, Save24Regular, Delete24Regular, Add20Regular, Delete20Regular, LinkDismiss20Regular, ArrowUpload32Regular, ArrowUpload24Regular, ArrowSync24Regular, DrawerArrowDownload24Regular, Money20Regular, ErrorCircle12Filled, DocumentText20Regular, FolderZip20Regular, MusicNote220Regular, Video20Regular, Image20Regular, SlideText20Regular, DocumentTable20Regular, DocumentPdf20Regular, MoreHorizontal20Regular, ChevronDoubleLeft20Regular, ChevronLeft20Regular, ChevronDoubleRight20Regular, Info12Regular, Checkmark16Regular, Dismiss16Regular } from '@fluentui/react-icons';
6
+ import { RequiredLevel, DateTimeBehavior, canReadColumn, getRecordReferenceId, RelationshipFilterMode, ImpersonateResult, SwitchIdentityResult, PortalUserType, TableSecurityPermission, canWriteColumn } from '@powerportalspro/core';
7
7
  import { DatePicker } from '@fluentui/react-datepicker-compat';
8
8
  import { TimePicker } from '@fluentui/react-timepicker-compat';
9
9
  import { unzipSync } from 'fflate';
@@ -289,8 +289,12 @@ function isRecordWriteDenied(record) {
289
289
  const perms = record.permissions ?? 0;
290
290
  return (perms & TableSecurityPermission.Write) === 0;
291
291
  }
292
- function resolveEditState(metadata, record, explicitReadOnly, explicitDisabled) {
293
- if (isRecordWriteDenied(record)) {
292
+ function isColumnWriteDenied(record, columnName) {
293
+ if (!record || !columnName) return false;
294
+ return !canWriteColumn(record, columnName);
295
+ }
296
+ function resolveEditState(metadata, record, explicitReadOnly, explicitDisabled, columnName) {
297
+ if (isRecordWriteDenied(record) || isColumnWriteDenied(record, columnName)) {
294
298
  return { readOnly: true, disabled: true };
295
299
  }
296
300
  return {
@@ -718,7 +722,8 @@ function TextEdit({
718
722
  metadata,
719
723
  record,
720
724
  readOnly,
721
- disabled
725
+ disabled,
726
+ columnName
722
727
  );
723
728
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
724
729
  const validationErrors = useColumnValidation(
@@ -812,7 +817,8 @@ function MemoEdit({
812
817
  metadata,
813
818
  record,
814
819
  readOnly,
815
- disabled
820
+ disabled,
821
+ columnName
816
822
  );
817
823
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
818
824
  const validationErrors = useColumnValidation(
@@ -926,7 +932,8 @@ function BoolEdit({
926
932
  metadata,
927
933
  record,
928
934
  readOnly,
929
- disabled
935
+ disabled,
936
+ columnName
930
937
  );
931
938
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
932
939
  const isInactive = resolvedDisabled || resolvedReadOnly;
@@ -1102,7 +1109,8 @@ function NumberEdit({
1102
1109
  metadata,
1103
1110
  record,
1104
1111
  readOnly,
1105
- disabled
1112
+ disabled,
1113
+ columnName
1106
1114
  );
1107
1115
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
1108
1116
  const validationErrors = useColumnValidation(
@@ -1546,7 +1554,8 @@ function MoneyEdit({
1546
1554
  metadata,
1547
1555
  record,
1548
1556
  readOnly,
1549
- disabled
1557
+ disabled,
1558
+ columnName
1550
1559
  );
1551
1560
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
1552
1561
  const validationErrors = useColumnValidation(
@@ -1960,7 +1969,8 @@ function DateTimeEdit({
1960
1969
  metadata,
1961
1970
  record,
1962
1971
  readOnly,
1963
- disabled
1972
+ disabled,
1973
+ columnName
1964
1974
  );
1965
1975
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
1966
1976
  const resolvedEditorType = editorType ?? resolveEditorTypeFromMetadata(metadata);
@@ -5351,7 +5361,8 @@ function FileEdit({
5351
5361
  metadata,
5352
5362
  record,
5353
5363
  readOnly,
5354
- disabled
5364
+ disabled,
5365
+ columnName
5355
5366
  );
5356
5367
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
5357
5368
  const isInteractable = !resolvedReadOnly && !resolvedDisabled;
@@ -5668,7 +5679,8 @@ function ImageEdit({
5668
5679
  metadata,
5669
5680
  record,
5670
5681
  readOnly,
5671
- disabled
5682
+ disabled,
5683
+ columnName
5672
5684
  );
5673
5685
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
5674
5686
  const isInteractable = !resolvedReadOnly && !resolvedDisabled;
@@ -6148,7 +6160,8 @@ function ChoiceEdit({
6148
6160
  metadata,
6149
6161
  record,
6150
6162
  readOnly,
6151
- disabled
6163
+ disabled,
6164
+ columnName
6152
6165
  );
6153
6166
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
6154
6167
  const choiceMeta = getChoiceMetadata(metadata);
@@ -6368,7 +6381,8 @@ function MultiSelectChoiceEdit({
6368
6381
  metadata,
6369
6382
  record,
6370
6383
  readOnly,
6371
- disabled
6384
+ disabled,
6385
+ columnName
6372
6386
  );
6373
6387
  const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);
6374
6388
  const choiceMeta = getChoiceMetadata(metadata);
@@ -6490,30 +6504,30 @@ function foldString(s, caseSensitive) {
6490
6504
  function tokenizeSearch(search, delimiters) {
6491
6505
  if (!delimiters) return [search];
6492
6506
  const delim = new Set(Array.from(delimiters));
6493
- const tokens43 = [];
6507
+ const tokens44 = [];
6494
6508
  let current = "";
6495
6509
  for (const ch of search) {
6496
6510
  if (delim.has(ch)) {
6497
6511
  if (current.length > 0) {
6498
- tokens43.push(current);
6512
+ tokens44.push(current);
6499
6513
  current = "";
6500
6514
  }
6501
6515
  } else {
6502
6516
  current += ch;
6503
6517
  }
6504
6518
  }
6505
- if (current.length > 0) tokens43.push(current);
6506
- return tokens43;
6519
+ if (current.length > 0) tokens44.push(current);
6520
+ return tokens44;
6507
6521
  }
6508
6522
  function splitHighlightSegments(text, search, options) {
6509
6523
  if (!text || !search) return [];
6510
6524
  const caseSensitive = options?.caseSensitive ?? false;
6511
6525
  const delimiters = options?.delimiters ?? "";
6512
- const tokens43 = tokenizeSearch(search, delimiters);
6513
- if (tokens43.length === 0) return [{ text, isMatch: false }];
6526
+ const tokens44 = tokenizeSearch(search, delimiters);
6527
+ if (tokens44.length === 0) return [{ text, isMatch: false }];
6514
6528
  const folded = foldString(text, caseSensitive);
6515
6529
  const ranges = [];
6516
- for (const token of tokens43) {
6530
+ for (const token of tokens44) {
6517
6531
  const tokenFolded = foldString(token, caseSensitive).folded;
6518
6532
  if (tokenFolded.length === 0) continue;
6519
6533
  let cursor2 = 0;
@@ -7284,7 +7298,11 @@ function getRendererTypeForCode(typeCode) {
7284
7298
  }
7285
7299
  function ColumnEdit(props) {
7286
7300
  const metadata = useColumnMetadata(props.columnName);
7301
+ const record = useRecordContextOptional()?.record;
7287
7302
  const typeCode = metadata?.$type;
7303
+ if (record && !canReadColumn(record, props.columnName)) {
7304
+ return /* @__PURE__ */ jsx(ColumnDisplayValue, { ...props });
7305
+ }
7288
7306
  switch (typeCode) {
7289
7307
  case 0:
7290
7308
  return /* @__PURE__ */ jsx(BoolEdit, { ...props });
@@ -8304,6 +8322,36 @@ function MainGridVirtualizeFooter({
8304
8322
  ] });
8305
8323
  }
8306
8324
 
8325
+ // src/grids/virtualize-paging.ts
8326
+ function hasMoreRowsToLoad(state) {
8327
+ if (state.moreRecords === false) return false;
8328
+ if (state.moreRecords === true) return true;
8329
+ if (state.lastPageEmpty) return false;
8330
+ if (state.totalCount === void 0) return true;
8331
+ return state.accumulatedCount < state.totalCount;
8332
+ }
8333
+ var VirtualizePagingCookies = class {
8334
+ key;
8335
+ byPage = /* @__PURE__ */ new Map();
8336
+ /** Store the cookie the response for `page` came back with (no-op for a null / empty cookie). */
8337
+ record(key, page, cookie) {
8338
+ if (this.key !== key) {
8339
+ this.key = key;
8340
+ this.byPage = /* @__PURE__ */ new Map();
8341
+ }
8342
+ if (cookie) this.byPage.set(page, cookie);
8343
+ }
8344
+ /**
8345
+ * The cookie to send when requesting `page` under `key`: the one page
8346
+ * `page - 1` returned, or `undefined` when it isn't known (page 1, a
8347
+ * non-sequential jump, or a key the store has no cookies for).
8348
+ */
8349
+ cookieFor(key, page) {
8350
+ if (this.key !== key || page <= 1) return void 0;
8351
+ return this.byPage.get(page - 1);
8352
+ }
8353
+ };
8354
+
8307
8355
  // src/grids/persisted-grid-state.ts
8308
8356
  function readPersistedGridStateFromUrl(queryParameterName) {
8309
8357
  if (!queryParameterName) return null;
@@ -8367,6 +8415,12 @@ var ViewSort = {
8367
8415
  /** Localized display name, descending. */
8368
8416
  NameDescending: "nameDescending"
8369
8417
  };
8418
+ function resolveViewLabel(view, t) {
8419
+ const key = `tables.${view.tableName}.views.${view.id}.label`;
8420
+ const localized = t(key);
8421
+ if (localized !== key) return localized;
8422
+ return view.displayName?.trim() ? view.displayName : null;
8423
+ }
8370
8424
  function customToViewMetadata(custom) {
8371
8425
  return {
8372
8426
  id: custom.id,
@@ -8835,8 +8889,22 @@ function MainGridImpl({
8835
8889
  `[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.`
8836
8890
  );
8837
8891
  }, [columnsKey, useInlineFetchXml]);
8892
+ const accumulatorResetKey = useMemo(
8893
+ () => [
8894
+ resolvedViewId ?? "",
8895
+ useInlineFetchXml ? inlineFetchXml ?? "" : "",
8896
+ debouncedSearchText,
8897
+ sort.map((s) => `${s.columnName}:${s.descending ? "d" : "a"}`).join("|"),
8898
+ filtersKey,
8899
+ pageSize,
8900
+ tableName ?? ""
8901
+ ].join("|"),
8902
+ [resolvedViewId, useInlineFetchXml, inlineFetchXml, debouncedSearchText, sort, filtersKey, pageSize, tableName]
8903
+ );
8904
+ const [virtualizeCookies] = useState(() => new VirtualizePagingCookies());
8838
8905
  const searchRequest = useMemo(() => {
8839
8906
  const trimmed = debouncedSearchText.trim();
8907
+ const pagingCookie = isVirtualize ? virtualizeCookies.cookieFor(accumulatorResetKey, page) : void 0;
8840
8908
  return {
8841
8909
  ...useInlineFetchXml && inlineFetchXml ? { fetchXml: inlineFetchXml } : resolvedViewId !== void 0 && { viewId: resolvedViewId },
8842
8910
  ...trimmed && { searchText: trimmed },
@@ -8856,7 +8924,8 @@ function MainGridImpl({
8856
8924
  },
8857
8925
  ...columnsOverride && { columns: [...columnsOverride] },
8858
8926
  pageNumber: page,
8859
- pageSize
8927
+ pageSize,
8928
+ ...pagingCookie && { pagingCookie }
8860
8929
  };
8861
8930
  }, [
8862
8931
  resolvedViewId,
@@ -8867,7 +8936,9 @@ function MainGridImpl({
8867
8936
  page,
8868
8937
  pageSize,
8869
8938
  filtersKey,
8870
- columnsKey
8939
+ columnsKey,
8940
+ isVirtualize,
8941
+ accumulatorResetKey
8871
8942
  ]);
8872
8943
  const transformPending = !!transformViewAsync || isCustomActiveView;
8873
8944
  const recordsResult = useGridData(searchRequest, {
@@ -9026,7 +9097,7 @@ function MainGridImpl({
9026
9097
  const rowRecord = cellCtx.row.original;
9027
9098
  const rowId = rowRecord.id;
9028
9099
  const isPendingCreateRow = !!rowId && rowId.startsWith("__pending-create-");
9029
- if (editable && !isPendingCreateRow && hasEditorForType) {
9100
+ if (editable && !isPendingCreateRow && hasEditorForType && canWriteColumn(rowRecord, columnName)) {
9030
9101
  return (
9031
9102
  // Sizing wrapper — overrides Fluent v9's default
9032
9103
  // <Input> / <Combobox> / <Dropdown> min-width
@@ -9164,18 +9235,6 @@ function MainGridImpl({
9164
9235
  ]);
9165
9236
  const pageRecords = dataSource ? dataSource.rows : recordsResult.data?.tableRecords ?? EMPTY_RECORDS;
9166
9237
  const [accumulatedRows, setAccumulatedRows] = useState(null);
9167
- const accumulatorResetKey = useMemo(
9168
- () => [
9169
- resolvedViewId ?? "",
9170
- useInlineFetchXml ? inlineFetchXml ?? "" : "",
9171
- debouncedSearchText,
9172
- sort.map((s) => `${s.columnName}:${s.descending ? "d" : "a"}`).join("|"),
9173
- filtersKey,
9174
- pageSize,
9175
- tableName ?? ""
9176
- ].join("|"),
9177
- [resolvedViewId, useInlineFetchXml, inlineFetchXml, debouncedSearchText, sort, filtersKey, pageSize, tableName]
9178
- );
9179
9238
  const resetVirtualizedView = useCallback(() => {
9180
9239
  setAccumulatedRows([]);
9181
9240
  setPage(1);
@@ -9201,6 +9260,12 @@ function MainGridImpl({
9201
9260
  return [...base, ...additions];
9202
9261
  });
9203
9262
  }, [isVirtualize, recordsResult.status, pageRecords]);
9263
+ useEffect(() => {
9264
+ if (!isVirtualize) return;
9265
+ const landed = recordsResult.data;
9266
+ if (!landed) return;
9267
+ virtualizeCookies.record(accumulatorResetKey, page, landed.pagingInfo?.pagingCookie);
9268
+ }, [isVirtualize, recordsResult.data]);
9204
9269
  const data = isVirtualize ? accumulatedRows ?? EMPTY_RECORDS : pageRecords;
9205
9270
  const [pendingRowUpdates, setPendingRowUpdates] = useState(() => /* @__PURE__ */ new Map());
9206
9271
  const [pendingRowCreates, setPendingRowCreates] = useState(() => []);
@@ -9681,7 +9746,15 @@ function MainGridImpl({
9681
9746
  if (raw == null) return void 0;
9682
9747
  return typeof raw === "number" ? raw : Number(raw);
9683
9748
  })();
9684
- const hasMoreRowsToLoad = isVirtualize && (totalCount === void 0 || (accumulatedRows?.length ?? 0) < totalCount);
9749
+ const hasMoreRowsToLoad2 = isVirtualize && hasMoreRowsToLoad({
9750
+ accumulatedCount: accumulatedRows?.length ?? 0,
9751
+ totalCount,
9752
+ moreRecords: dataSource ? dataSource.moreRecords : recordsResult.data?.pagingInfo?.moreRecords,
9753
+ // "A page has landed and it carried no rows." In standalone mode
9754
+ // `data` is only ever defined for a landed response (errors wipe it);
9755
+ // the datasource has no data handle, so idle-after-success stands in.
9756
+ lastPageEmpty: dataSource ? dataSource.status === QueryStatus.Success && !dataSource.isFetching && dataSource.rows.length === 0 : recordsResult.data !== void 0 && pageRecords.length === 0
9757
+ });
9685
9758
  useEffect(() => {
9686
9759
  if (!isVirtualize) return;
9687
9760
  const sentinel = loadMoreSentinelRef.current;
@@ -9692,7 +9765,7 @@ function MainGridImpl({
9692
9765
  const entry = entries[0];
9693
9766
  if (!entry?.isIntersecting) return;
9694
9767
  if (recordsResult.isFetching) return;
9695
- if (!hasMoreRowsToLoad) return;
9768
+ if (!hasMoreRowsToLoad2) return;
9696
9769
  setPage(page + 1);
9697
9770
  },
9698
9771
  {
@@ -9705,7 +9778,7 @@ function MainGridImpl({
9705
9778
  );
9706
9779
  observer.observe(sentinel);
9707
9780
  return () => observer.disconnect();
9708
- }, [isVirtualize, hasMoreRowsToLoad, recordsResult.isFetching, page, setPage]);
9781
+ }, [isVirtualize, hasMoreRowsToLoad2, recordsResult.isFetching, page, setPage]);
9709
9782
  const handlePageChange = (nextPage) => {
9710
9783
  if (nextPage === page) return;
9711
9784
  setPage(nextPage);
@@ -9714,8 +9787,8 @@ function MainGridImpl({
9714
9787
  };
9715
9788
  const selectedView = views?.find((v) => v.id === resolvedViewId);
9716
9789
  const selectedViewLabelKey = selectedView ? `tables.${selectedView.tableName}.views.${selectedView.id}.label` : "";
9717
- const selectedViewLabel = selectedView ? t(selectedViewLabelKey) : "";
9718
- const singleViewTitle = selectedView && selectedViewLabel !== selectedViewLabelKey ? selectedViewLabel : selectedView?.displayName ?? void 0;
9790
+ const selectedViewLabel = selectedView ? resolveViewLabel(selectedView, t) ?? selectedViewLabelKey : "";
9791
+ const singleViewTitle = selectedView ? resolveViewLabel(selectedView, t) ?? void 0 : void 0;
9719
9792
  return /* @__PURE__ */ jsx(GridContextProvider, { value: gridContextValue, children: /* @__PURE__ */ jsxs(
9720
9793
  "div",
9721
9794
  {
@@ -9748,7 +9821,7 @@ function MainGridImpl({
9748
9821
  if (data2.optionValue) handleViewChange(data2.optionValue);
9749
9822
  },
9750
9823
  children: views?.map((view) => {
9751
- const label = t(`tables.${view.tableName}.views.${view.id}.label`);
9824
+ const label = resolveViewLabel(view, t) ?? `tables.${view.tableName}.views.${view.id}.label`;
9752
9825
  return /* @__PURE__ */ jsx(Option, { value: view.id, text: label, children: label }, view.id);
9753
9826
  })
9754
9827
  }
@@ -10325,7 +10398,7 @@ function MainGridImpl({
10325
10398
  }
10326
10399
  ),
10327
10400
  displayedData.length === 0 && /* @__PURE__ */ jsx("div", { className: styles.emptyStateRow, children: t("app.components.grid.empty-content") }),
10328
- isVirtualize && hasMoreRowsToLoad && // No spinner here: the full-body `refetchScrim` overlay below is the
10401
+ isVirtualize && hasMoreRowsToLoad2 && // No spinner here: the full-body `refetchScrim` overlay below is the
10329
10402
  // single load indicator. Rendering a `size="tiny"` spinner in the
10330
10403
  // sentinel as well meant a deps-change refetch (refresh / sort /
10331
10404
  // search / view switch) — which resets the accumulator to empty and
@@ -10844,7 +10917,8 @@ function LookupEdit({
10844
10917
  metadata,
10845
10918
  record,
10846
10919
  readOnly,
10847
- disabled
10920
+ disabled,
10921
+ columnName
10848
10922
  );
10849
10923
  const setLookupValue = useGuardedSetValue(
10850
10924
  rawSetLookupValue,
@@ -12624,6 +12698,7 @@ function PowerPortalsProThemeProvider({
12624
12698
  defaultMode = ThemeMode.System,
12625
12699
  defaultAccentColor = "default",
12626
12700
  defaultTextDirection = TextDirection.Ltr,
12701
+ lockAccentColor = false,
12627
12702
  customAccents,
12628
12703
  storageKey = DEFAULT_MODE_STORAGE_KEY,
12629
12704
  style,
@@ -12647,9 +12722,11 @@ function PowerPortalsProThemeProvider({
12647
12722
  () => readStoredMode(storageKey) ?? defaultMode
12648
12723
  );
12649
12724
  const [accentColor, setAccentColorState] = useState(() => {
12725
+ const configured = defaultAccentColor in accentMap ? defaultAccentColor : "default";
12726
+ if (lockAccentColor) return configured;
12650
12727
  const stored = readStoredString(accentStorageKey);
12651
12728
  if (stored && stored in accentMap) return stored;
12652
- return defaultAccentColor in accentMap ? defaultAccentColor : "default";
12729
+ return configured;
12653
12730
  });
12654
12731
  const [textDirection, setTextDirectionState] = useState(
12655
12732
  () => readStoredDirection(directionStorageKey) ?? defaultTextDirection
@@ -12679,6 +12756,12 @@ function PowerPortalsProThemeProvider({
12679
12756
  document.body.classList.toggle("ppp-theme-dark", isDark);
12680
12757
  document.body.classList.toggle("ppp-theme-light", !isDark);
12681
12758
  }, [isDark]);
12759
+ useEffect(() => {
12760
+ if (!lockAccentColor) return;
12761
+ const configured = defaultAccentColor in accentMap ? defaultAccentColor : "default";
12762
+ setAccentColorState(configured);
12763
+ writeStored(accentStorageKey, configured);
12764
+ }, [lockAccentColor, defaultAccentColor, accentMap, accentStorageKey]);
12682
12765
  const setMode = useCallback(
12683
12766
  (next) => {
12684
12767
  setModeState(next);
@@ -12688,11 +12771,12 @@ function PowerPortalsProThemeProvider({
12688
12771
  );
12689
12772
  const setAccentColor = useCallback(
12690
12773
  (next) => {
12774
+ if (lockAccentColor) return;
12691
12775
  if (!(next in accentMap)) return;
12692
12776
  setAccentColorState(next);
12693
12777
  writeStored(accentStorageKey, next);
12694
12778
  },
12695
- [accentMap, accentStorageKey]
12779
+ [lockAccentColor, accentMap, accentStorageKey]
12696
12780
  );
12697
12781
  const setTextDirection = useCallback(
12698
12782
  (next) => {
@@ -12717,6 +12801,7 @@ function PowerPortalsProThemeProvider({
12717
12801
  setAccentColor,
12718
12802
  availableAccents,
12719
12803
  accentColors,
12804
+ accentColorLocked: lockAccentColor,
12720
12805
  textDirection,
12721
12806
  setTextDirection
12722
12807
  }),
@@ -12728,6 +12813,7 @@ function PowerPortalsProThemeProvider({
12728
12813
  setAccentColor,
12729
12814
  availableAccents,
12730
12815
  accentColors,
12816
+ lockAccentColor,
12731
12817
  textDirection,
12732
12818
  setTextDirection
12733
12819
  ]
@@ -12808,7 +12894,7 @@ function ThemeColorSelector({
12808
12894
  width,
12809
12895
  accentLabels
12810
12896
  }) {
12811
- const { accentColor, setAccentColor, availableAccents, accentColors } = useTheme();
12897
+ const { accentColor, setAccentColor, availableAccents, accentColors, accentColorLocked } = useTheme();
12812
12898
  const t = useT();
12813
12899
  const labelId = useId();
12814
12900
  const styles = useStyles6();
@@ -12822,6 +12908,7 @@ function ThemeColorSelector({
12822
12908
  },
12823
12909
  [t, accentLabels]
12824
12910
  );
12911
+ if (accentColorLocked) return null;
12825
12912
  const handleOptionSelect = (_event, data) => {
12826
12913
  const next = data.optionValue;
12827
12914
  if (next && availableAccents.includes(next)) {
@@ -12989,7 +13076,304 @@ function LanguageDropdown({
12989
13076
  }
12990
13077
  );
12991
13078
  }
13079
+ var MINIMUM_QUERY_LENGTH = 3;
13080
+ var MAXIMUM_RESULTS = 50;
13081
+ var SEARCH_DEBOUNCE_MS4 = 300;
13082
+ var GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12992
13083
  var useStyles7 = makeStyles({
13084
+ surface: {
13085
+ maxWidth: "480px",
13086
+ display: "flex",
13087
+ flexDirection: "column"
13088
+ },
13089
+ titleRow: {
13090
+ display: "flex",
13091
+ flexDirection: "row",
13092
+ alignItems: "flex-start",
13093
+ columnGap: tokens.spacingHorizontalS
13094
+ },
13095
+ titleText: {
13096
+ flexGrow: 1,
13097
+ minWidth: 0
13098
+ },
13099
+ body: {
13100
+ display: "flex",
13101
+ flexDirection: "column",
13102
+ rowGap: tokens.spacingVerticalM,
13103
+ paddingTop: tokens.spacingVerticalM
13104
+ },
13105
+ results: {
13106
+ display: "flex",
13107
+ flexDirection: "column",
13108
+ // Fixed height, not min/max: a list that resizes on every keystroke makes the confirm button
13109
+ // jump around under the pointer while the administrator is still reading.
13110
+ height: "280px",
13111
+ overflowY: "auto",
13112
+ // Full shorthand — griffel types `borderColor` and friends as `never`, so the longhand
13113
+ // properties aren't usable here.
13114
+ border: `1px solid ${tokens.colorNeutralStroke1}`,
13115
+ borderRadius: tokens.borderRadiusMedium,
13116
+ backgroundColor: tokens.colorNeutralBackground1
13117
+ },
13118
+ result: {
13119
+ display: "flex",
13120
+ flexDirection: "row",
13121
+ alignItems: "center",
13122
+ columnGap: tokens.spacingHorizontalS,
13123
+ padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,
13124
+ cursor: "pointer",
13125
+ borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,
13126
+ textAlign: "left",
13127
+ background: "none",
13128
+ // A native <button> does NOT inherit colour or font from its container — the user-agent
13129
+ // stylesheet gives it `color: buttontext` and its own font stack. Without these three the
13130
+ // rows rendered as near-black text on the dark surface in dark mode, which is how this was
13131
+ // first spotted. Set explicitly rather than relying on inheritance.
13132
+ color: tokens.colorNeutralForeground1,
13133
+ fontFamily: "inherit",
13134
+ fontSize: tokens.fontSizeBase300,
13135
+ // The UA also gives buttons a border; `background: none` alone leaves it drawing a box
13136
+ // around every row. Only three sides — borderBottom above is the row separator.
13137
+ borderTop: "none",
13138
+ borderRight: "none",
13139
+ borderLeft: "none",
13140
+ ":hover": {
13141
+ backgroundColor: tokens.colorNeutralBackground1Hover,
13142
+ color: tokens.colorNeutralForeground1Hover
13143
+ },
13144
+ ":focus-visible": {
13145
+ outline: `2px solid ${tokens.colorStrokeFocus2}`,
13146
+ outlineOffset: "-2px"
13147
+ }
13148
+ },
13149
+ resultSelected: {
13150
+ backgroundColor: tokens.colorBrandBackground,
13151
+ color: tokens.colorNeutralForegroundOnBrand,
13152
+ ":hover": {
13153
+ backgroundColor: tokens.colorBrandBackgroundHover,
13154
+ // Repeated because the base row's :hover also sets colour, and whichever griffel emits
13155
+ // last would otherwise win — leaving selected-and-hovered text unreadable on the brand fill.
13156
+ color: tokens.colorNeutralForegroundOnBrand
13157
+ }
13158
+ },
13159
+ resultText: {
13160
+ display: "flex",
13161
+ flexDirection: "column",
13162
+ minWidth: 0
13163
+ },
13164
+ name: {
13165
+ fontSize: tokens.fontSizeBase300,
13166
+ overflow: "hidden",
13167
+ textOverflow: "ellipsis",
13168
+ whiteSpace: "nowrap"
13169
+ },
13170
+ email: {
13171
+ fontSize: tokens.fontSizeBase200,
13172
+ opacity: 0.8,
13173
+ overflow: "hidden",
13174
+ textOverflow: "ellipsis",
13175
+ whiteSpace: "nowrap"
13176
+ },
13177
+ status: {
13178
+ display: "flex",
13179
+ flexDirection: "row",
13180
+ alignItems: "center",
13181
+ columnGap: tokens.spacingHorizontalS,
13182
+ padding: tokens.spacingVerticalXL,
13183
+ color: tokens.colorNeutralForeground3,
13184
+ fontSize: tokens.fontSizeBase200
13185
+ },
13186
+ warning: {
13187
+ display: "flex",
13188
+ flexDirection: "row",
13189
+ alignItems: "flex-start",
13190
+ columnGap: tokens.spacingHorizontalS,
13191
+ padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,
13192
+ borderRadius: tokens.borderRadiusMedium,
13193
+ backgroundColor: tokens.colorNeutralBackground2,
13194
+ borderLeft: `3px solid ${tokens.colorPaletteRedBackground3}`,
13195
+ fontSize: tokens.fontSizeBase200
13196
+ },
13197
+ footer: {
13198
+ display: "flex",
13199
+ flexDirection: "row",
13200
+ justifyContent: "flex-end",
13201
+ columnGap: tokens.spacingHorizontalS,
13202
+ paddingTop: tokens.spacingVerticalM
13203
+ }
13204
+ });
13205
+ function ImpersonationPickerDialog({
13206
+ open,
13207
+ onClose,
13208
+ returnUrl = "/",
13209
+ className
13210
+ }) {
13211
+ const auth = useAuth();
13212
+ const styles = useStyles7();
13213
+ const t = useT();
13214
+ const { searchImpersonationTargets, impersonate } = auth;
13215
+ const [query, setQuery] = useState("");
13216
+ const [results, setResults] = useState([]);
13217
+ const [selected, setSelected] = useState(null);
13218
+ const [isSearching, setIsSearching] = useState(false);
13219
+ const [hasSearched, setHasSearched] = useState(false);
13220
+ const [hasMore, setHasMore] = useState(false);
13221
+ const [isStarting, setIsStarting] = useState(false);
13222
+ const requestIdRef = useRef(0);
13223
+ useEffect(() => {
13224
+ if (open) return;
13225
+ setQuery("");
13226
+ setResults([]);
13227
+ setSelected(null);
13228
+ setIsSearching(false);
13229
+ setHasSearched(false);
13230
+ setHasMore(false);
13231
+ setIsStarting(false);
13232
+ }, [open]);
13233
+ const trimmed = query.trim();
13234
+ const isTooShort = trimmed.length > 0 && trimmed.length < MINIMUM_QUERY_LENGTH;
13235
+ useEffect(() => {
13236
+ if (!open) return;
13237
+ const requestId = ++requestIdRef.current;
13238
+ if (trimmed.length < MINIMUM_QUERY_LENGTH && !GUID_PATTERN.test(trimmed)) {
13239
+ setResults([]);
13240
+ setHasSearched(false);
13241
+ setHasMore(false);
13242
+ setIsSearching(false);
13243
+ return;
13244
+ }
13245
+ let cancelled = false;
13246
+ const timer = setTimeout(async () => {
13247
+ setIsSearching(true);
13248
+ try {
13249
+ const response = await searchImpersonationTargets(trimmed);
13250
+ if (cancelled || requestId !== requestIdRef.current) return;
13251
+ setResults(response.results ?? []);
13252
+ setHasMore(response.hasMore ?? false);
13253
+ setHasSearched(true);
13254
+ } catch {
13255
+ if (cancelled || requestId !== requestIdRef.current) return;
13256
+ setResults([]);
13257
+ setHasMore(false);
13258
+ setHasSearched(true);
13259
+ } finally {
13260
+ if (!cancelled && requestId === requestIdRef.current) {
13261
+ setIsSearching(false);
13262
+ }
13263
+ }
13264
+ }, SEARCH_DEBOUNCE_MS4);
13265
+ return () => {
13266
+ cancelled = true;
13267
+ clearTimeout(timer);
13268
+ };
13269
+ }, [open, searchImpersonationTargets, trimmed]);
13270
+ const handleQueryChange = useCallback((value) => {
13271
+ setQuery(value);
13272
+ setSelected(null);
13273
+ }, []);
13274
+ const handleConfirm = useCallback(async () => {
13275
+ if (!selected?.contactId) return;
13276
+ setIsStarting(true);
13277
+ try {
13278
+ const response = await impersonate(selected.contactId);
13279
+ if (response.result === ImpersonateResult.Started) {
13280
+ window.location.assign(returnUrl);
13281
+ return;
13282
+ }
13283
+ setIsStarting(false);
13284
+ onClose();
13285
+ } catch {
13286
+ setIsStarting(false);
13287
+ }
13288
+ }, [impersonate, onClose, returnUrl, selected]);
13289
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (_, data) => {
13290
+ if (!data.open) onClose();
13291
+ }, children: /* @__PURE__ */ jsxs(
13292
+ DialogSurface,
13293
+ {
13294
+ className: mergeClasses("ppp-impersonation-picker-dialog", styles.surface, className),
13295
+ children: [
13296
+ /* @__PURE__ */ jsxs("div", { className: styles.titleRow, children: [
13297
+ /* @__PURE__ */ jsx(DialogTitle, { className: styles.titleText, children: t("app.components.impersonation-picker.title") }),
13298
+ /* @__PURE__ */ jsx(
13299
+ Button,
13300
+ {
13301
+ appearance: "subtle",
13302
+ icon: /* @__PURE__ */ jsx(Dismiss24Regular, {}),
13303
+ "aria-label": t("app.components.impersonation-picker.cancel"),
13304
+ onClick: onClose
13305
+ }
13306
+ )
13307
+ ] }),
13308
+ /* @__PURE__ */ jsxs("div", { className: styles.body, children: [
13309
+ /* @__PURE__ */ jsx(
13310
+ SearchBox,
13311
+ {
13312
+ value: query,
13313
+ onChange: (_, data) => handleQueryChange(data.value),
13314
+ placeholder: t("app.components.impersonation-picker.search-placeholder"),
13315
+ appearance: "outline",
13316
+ style: { width: "100%" }
13317
+ }
13318
+ ),
13319
+ /* @__PURE__ */ jsx("div", { className: styles.results, role: "listbox", children: isSearching ? /* @__PURE__ */ jsxs("div", { className: styles.status, children: [
13320
+ /* @__PURE__ */ jsx(Spinner, { size: "tiny" }),
13321
+ /* @__PURE__ */ jsx("span", { children: t("app.components.impersonation-picker.searching") })
13322
+ ] }) : isTooShort ? (
13323
+ // Distinct from "no matches": the search hasn't run. Conflating the two reads as
13324
+ // "this person doesn't exist" when the user has simply typed too few characters.
13325
+ /* @__PURE__ */ jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.hint", [String(MINIMUM_QUERY_LENGTH)]) })
13326
+ ) : hasSearched && results.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.no-matches") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
13327
+ results.map((candidate) => {
13328
+ const isSelected = selected?.contactId === candidate.contactId;
13329
+ return /* @__PURE__ */ jsxs(
13330
+ "button",
13331
+ {
13332
+ type: "button",
13333
+ role: "option",
13334
+ "aria-selected": isSelected,
13335
+ className: mergeClasses(
13336
+ styles.result,
13337
+ isSelected && styles.resultSelected
13338
+ ),
13339
+ onClick: () => setSelected(candidate),
13340
+ children: [
13341
+ /* @__PURE__ */ jsx(Person20Regular, {}),
13342
+ /* @__PURE__ */ jsxs("span", { className: styles.resultText, children: [
13343
+ /* @__PURE__ */ jsx("span", { className: styles.name, children: candidate.fullName ?? candidate.email ?? candidate.contactId }),
13344
+ candidate.email && /* @__PURE__ */ jsx("span", { className: styles.email, children: candidate.email })
13345
+ ] })
13346
+ ]
13347
+ },
13348
+ candidate.contactId
13349
+ );
13350
+ }),
13351
+ hasMore && /* @__PURE__ */ jsx("div", { className: styles.status, children: t("app.components.impersonation-picker.too-many", [String(MAXIMUM_RESULTS)]) })
13352
+ ] }) }),
13353
+ selected && /* @__PURE__ */ jsxs("div", { className: styles.warning, children: [
13354
+ /* @__PURE__ */ jsx(Warning20Regular, {}),
13355
+ /* @__PURE__ */ jsx("span", { children: t("app.components.impersonation-picker.warning") })
13356
+ ] })
13357
+ ] }),
13358
+ /* @__PURE__ */ jsxs("div", { className: styles.footer, children: [
13359
+ /* @__PURE__ */ jsx(
13360
+ Button,
13361
+ {
13362
+ appearance: "primary",
13363
+ icon: /* @__PURE__ */ jsx(EyeTracking20Regular, {}),
13364
+ disabled: !selected || isStarting,
13365
+ onClick: handleConfirm,
13366
+ children: t("app.components.impersonation-picker.confirm")
13367
+ }
13368
+ ),
13369
+ /* @__PURE__ */ jsx(Button, { appearance: "secondary", onClick: onClose, disabled: isStarting, children: t("app.components.impersonation-picker.cancel") })
13370
+ ] })
13371
+ ]
13372
+ }
13373
+ ) });
13374
+ }
13375
+ var IMPERSONATION_ROLE = "SystemAdmin";
13376
+ var useStyles8 = makeStyles({
12993
13377
  trigger: {
12994
13378
  backgroundColor: "transparent",
12995
13379
  border: "none",
@@ -13049,8 +13433,9 @@ function ProfileMainMenu({
13049
13433
  signInUrl = "/login"
13050
13434
  }) {
13051
13435
  const auth = useAuth();
13052
- const styles = useStyles7();
13436
+ const styles = useStyles8();
13053
13437
  const t = useT();
13438
+ const [isPickerOpen, setIsPickerOpen] = useState(false);
13054
13439
  const handleLogout = useCallback(async () => {
13055
13440
  try {
13056
13441
  await auth.logout();
@@ -13067,7 +13452,14 @@ function ProfileMainMenu({
13067
13452
  }
13068
13453
  }, [auth]);
13069
13454
  if (auth.status === AuthStatus.Loading) {
13070
- return /* @__PURE__ */ jsx(Avatar, { className: "ppp-profile-main-menu", size: 28, "aria-label": t("app.messages.record-loading") });
13455
+ return /* @__PURE__ */ jsx(
13456
+ Avatar,
13457
+ {
13458
+ className: "ppp-profile-main-menu",
13459
+ size: 28,
13460
+ "aria-label": t("app.messages.record-loading")
13461
+ }
13462
+ );
13071
13463
  }
13072
13464
  if (auth.status === AuthStatus.Anonymous) {
13073
13465
  if (anonymousContent !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: anonymousContent });
@@ -13088,61 +13480,126 @@ function ProfileMainMenu({
13088
13480
  const resolvedAccountLabel = accountLabel ?? t("app.components.profile-main-menu.account");
13089
13481
  const signedInAsLabel = t("app.components.profile-main-menu.signed-in-as");
13090
13482
  const logoutLabel = t("app.buttons.logout.label");
13483
+ const canImpersonate = (auth.user.roles?.includes(IMPERSONATION_ROLE) ?? false) && auth.user.portalUserType === PortalUserType.SystemUser;
13091
13484
  const altIdentityTableName = auth.user.altIdentityTableName;
13092
- const switchIdentityLabel = altIdentityTableName ? t(
13093
- "app.components.profile-main-menu.switch-to",
13094
- [
13095
- altIdentityTableName === "systemuser" ? t("app.components.profile-main-menu.alt-kind-systemuser") : t("app.components.profile-main-menu.alt-kind-contact")
13096
- ]
13097
- ) : null;
13098
- return /* @__PURE__ */ jsxs(Menu, { children: [
13099
- /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
13100
- "button",
13485
+ const switchIdentityLabel = altIdentityTableName ? t("app.components.profile-main-menu.switch-to", [
13486
+ altIdentityTableName === "systemuser" ? t("app.components.profile-main-menu.alt-kind-systemuser") : t("app.components.profile-main-menu.alt-kind-contact")
13487
+ ]) : null;
13488
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
13489
+ /* @__PURE__ */ jsxs(Menu, { children: [
13490
+ /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
13491
+ "button",
13492
+ {
13493
+ type: "button",
13494
+ className: mergeClasses("ppp-profile-main-menu", styles.trigger),
13495
+ "aria-label": email || logoutLabel,
13496
+ children: /* @__PURE__ */ jsx(Avatar, { size: 28, initials, name: email })
13497
+ }
13498
+ ) }),
13499
+ /* @__PURE__ */ jsx(MenuPopover, { children: /* @__PURE__ */ jsxs("div", { className: styles.popoverBody, children: [
13500
+ headerSlot,
13501
+ /* @__PURE__ */ jsxs("div", { className: styles.identity, children: [
13502
+ /* @__PURE__ */ jsx(Avatar, { size: 40, initials, name: email }),
13503
+ /* @__PURE__ */ jsxs("div", { className: styles.identityText, children: [
13504
+ /* @__PURE__ */ jsx("span", { className: styles.identityLabel, children: signedInAsLabel }),
13505
+ /* @__PURE__ */ jsx("span", { className: styles.identityValue, title: email, children: email })
13506
+ ] })
13507
+ ] }),
13508
+ /* @__PURE__ */ jsx(Divider, {}),
13509
+ /* @__PURE__ */ jsxs(MenuList, { className: styles.actions, children: [
13510
+ accountUrl && /* @__PURE__ */ jsx(
13511
+ MenuItem,
13512
+ {
13513
+ icon: /* @__PURE__ */ jsx(PersonEdit20Regular, {}),
13514
+ onClick: () => {
13515
+ window.location.href = accountUrl;
13516
+ },
13517
+ children: resolvedAccountLabel
13518
+ }
13519
+ ),
13520
+ switchIdentityLabel && /* @__PURE__ */ jsx(
13521
+ MenuItem,
13522
+ {
13523
+ icon: /* @__PURE__ */ jsx(ArrowSwap20Regular, {}),
13524
+ onClick: handleSwitchIdentity,
13525
+ children: switchIdentityLabel
13526
+ }
13527
+ ),
13528
+ canImpersonate && /* @__PURE__ */ jsx(
13529
+ MenuItem,
13530
+ {
13531
+ icon: /* @__PURE__ */ jsx(EyeTracking20Regular, {}),
13532
+ onClick: () => setIsPickerOpen(true),
13533
+ children: t("app.components.profile-main-menu.impersonate")
13534
+ }
13535
+ ),
13536
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(SignOut20Regular, {}), onClick: handleLogout, children: logoutLabel })
13537
+ ] }),
13538
+ footerSlot && /* @__PURE__ */ jsxs(Fragment, { children: [
13539
+ /* @__PURE__ */ jsx(Divider, {}),
13540
+ footerSlot
13541
+ ] })
13542
+ ] }) })
13543
+ ] }),
13544
+ canImpersonate && /* @__PURE__ */ jsx(
13545
+ ImpersonationPickerDialog,
13101
13546
  {
13102
- type: "button",
13103
- className: mergeClasses("ppp-profile-main-menu", styles.trigger),
13104
- "aria-label": email || logoutLabel,
13105
- children: /* @__PURE__ */ jsx(Avatar, { size: 28, initials, name: email })
13547
+ open: isPickerOpen,
13548
+ onClose: () => setIsPickerOpen(false)
13106
13549
  }
13107
- ) }),
13108
- /* @__PURE__ */ jsx(MenuPopover, { children: /* @__PURE__ */ jsxs("div", { className: styles.popoverBody, children: [
13109
- headerSlot,
13110
- /* @__PURE__ */ jsxs("div", { className: styles.identity, children: [
13111
- /* @__PURE__ */ jsx(Avatar, { size: 40, initials, name: email }),
13112
- /* @__PURE__ */ jsxs("div", { className: styles.identityText, children: [
13113
- /* @__PURE__ */ jsx("span", { className: styles.identityLabel, children: signedInAsLabel }),
13114
- /* @__PURE__ */ jsx("span", { className: styles.identityValue, title: email, children: email })
13115
- ] })
13116
- ] }),
13117
- /* @__PURE__ */ jsx(Divider, {}),
13118
- /* @__PURE__ */ jsxs(MenuList, { className: styles.actions, children: [
13119
- accountUrl && /* @__PURE__ */ jsx(
13120
- MenuItem,
13121
- {
13122
- icon: /* @__PURE__ */ jsx(PersonEdit20Regular, {}),
13123
- onClick: () => {
13124
- window.location.href = accountUrl;
13125
- },
13126
- children: resolvedAccountLabel
13127
- }
13128
- ),
13129
- switchIdentityLabel && /* @__PURE__ */ jsx(
13130
- MenuItem,
13131
- {
13132
- icon: /* @__PURE__ */ jsx(ArrowSwap20Regular, {}),
13133
- onClick: handleSwitchIdentity,
13134
- children: switchIdentityLabel
13135
- }
13136
- ),
13137
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(SignOut20Regular, {}), onClick: handleLogout, children: logoutLabel })
13138
- ] }),
13139
- footerSlot && /* @__PURE__ */ jsxs(Fragment, { children: [
13140
- /* @__PURE__ */ jsx(Divider, {}),
13141
- footerSlot
13142
- ] })
13143
- ] }) })
13550
+ )
13144
13551
  ] });
13145
13552
  }
13553
+ function ImpersonationBanner({
13554
+ returnUrl = "/",
13555
+ className
13556
+ }) {
13557
+ const auth = useAuth();
13558
+ const t = useT();
13559
+ const [isStopping, setIsStopping] = useState(false);
13560
+ const { stopImpersonation } = auth;
13561
+ const handleStop = useCallback(async () => {
13562
+ setIsStopping(true);
13563
+ try {
13564
+ await stopImpersonation();
13565
+ window.location.assign(returnUrl);
13566
+ } catch {
13567
+ setIsStopping(false);
13568
+ }
13569
+ }, [returnUrl, stopImpersonation]);
13570
+ if (auth.status !== AuthStatus.Authenticated || !auth.user.isImpersonating) {
13571
+ return null;
13572
+ }
13573
+ const viewingAs = auth.user.email ?? auth.user.userName ?? "";
13574
+ const impersonator = auth.user.impersonatorName;
13575
+ return (
13576
+ // The class is placement only — PageLayout targets it to assign the grid row. It carries no
13577
+ // appearance of its own; Fluent owns all of that.
13578
+ /* @__PURE__ */ jsxs(
13579
+ MessageBar,
13580
+ {
13581
+ className: mergeClasses("ppp-impersonation-banner", className),
13582
+ intent: "error",
13583
+ children: [
13584
+ /* @__PURE__ */ jsxs(MessageBarBody, { children: [
13585
+ /* @__PURE__ */ jsx(MessageBarTitle, { children: t("app.components.impersonation-banner.viewing-as", [viewingAs]) }),
13586
+ impersonator ? ` ${t("app.components.impersonation-banner.you-are", [impersonator])}` : null
13587
+ ] }),
13588
+ /* @__PURE__ */ jsx(MessageBarActions, { children: /* @__PURE__ */ jsx(
13589
+ Button,
13590
+ {
13591
+ icon: /* @__PURE__ */ jsx(ArrowExit20Regular, {}),
13592
+ iconPosition: "after",
13593
+ disabled: isStopping,
13594
+ onClick: handleStop,
13595
+ children: t("app.components.impersonation-banner.stop")
13596
+ }
13597
+ ) })
13598
+ ]
13599
+ }
13600
+ )
13601
+ );
13602
+ }
13146
13603
  var usePanelStyles = makeStyles({
13147
13604
  // Vertical stack of setting sections with consistent gap. Matches the
13148
13605
  // panel body the SiteSettingsButton's drawer renders, but extracted
@@ -13191,7 +13648,7 @@ function SiteSettingsPanel({
13191
13648
  }
13192
13649
  );
13193
13650
  }
13194
- var useStyles8 = makeStyles({
13651
+ var useStyles9 = makeStyles({
13195
13652
  // Wraps the standalone panel inside the drawer body to add the small
13196
13653
  // top inset that the popover's title visually expects. The panel
13197
13654
  // itself stays inset-free so it doesn't carry extra padding when
@@ -13211,7 +13668,7 @@ function SiteSettingsButton({
13211
13668
  accentLabels
13212
13669
  }) {
13213
13670
  const t = useT();
13214
- const styles = useStyles8();
13671
+ const styles = useStyles9();
13215
13672
  const [open, setOpen] = useState(false);
13216
13673
  const close = useCallback(() => setOpen(false), []);
13217
13674
  const label = t("app.site-settings-label");
@@ -13689,7 +14146,7 @@ function bestRouteMatch(routes, pathname) {
13689
14146
  }
13690
14147
  return best;
13691
14148
  }
13692
- var useStyles9 = makeStyles({
14149
+ var useStyles10 = makeStyles({
13693
14150
  // Shared scrim look. Both global (full-screen) and scoped variants reuse
13694
14151
  // these so the visual feel is identical — only the positioning differs.
13695
14152
  // Theme-aware translucent backdrop drawn via a `::before` pseudo-element
@@ -13751,7 +14208,7 @@ function FluentOverlayProvider({ children }) {
13751
14208
  ] });
13752
14209
  }
13753
14210
  function GlobalOverlayLayer() {
13754
- const styles = useStyles9();
14211
+ const styles = useStyles10();
13755
14212
  const overlay = useOverlayForTarget(void 0);
13756
14213
  if (!overlay) return null;
13757
14214
  return /* @__PURE__ */ jsx(ScrimContent, { overlay, className: mergeClasses(styles.scrim, styles.global) });
@@ -13762,7 +14219,7 @@ function FluentOverlayTarget({
13762
14219
  className,
13763
14220
  style
13764
14221
  }) {
13765
- const styles = useStyles9();
14222
+ const styles = useStyles10();
13766
14223
  const overlay = useOverlayForTarget(targetId);
13767
14224
  return /* @__PURE__ */ jsxs("div", { className: mergeClasses("ppp-overlay-target", styles.scopedHost, className), style, children: [
13768
14225
  children,
@@ -13770,14 +14227,14 @@ function FluentOverlayTarget({
13770
14227
  ] });
13771
14228
  }
13772
14229
  function ScrimContent({ overlay, className }) {
13773
- const styles = useStyles9();
14230
+ const styles = useStyles10();
13774
14231
  const showProgress = overlay.showProgress !== false;
13775
14232
  return /* @__PURE__ */ jsxs("div", { className, role: "status", "aria-live": "polite", children: [
13776
14233
  showProgress && /* @__PURE__ */ jsx(Spinner, { size: "large" }),
13777
14234
  overlay.description && /* @__PURE__ */ jsx("span", { className: styles.description, children: overlay.description })
13778
14235
  ] });
13779
14236
  }
13780
- var useStyles10 = makeStyles({
14237
+ var useStyles11 = makeStyles({
13781
14238
  // Centered placeholder used while prefixes are being fetched. Same
13782
14239
  // shape as the spinner blocks in `<NewRecordGridButton>` /
13783
14240
  // `<OpenRecordGridButton>` dialogs (~160px min height + vertical
@@ -13797,7 +14254,7 @@ function LocalizationBoundary({
13797
14254
  fallback,
13798
14255
  children
13799
14256
  }) {
13800
- const styles = useStyles10();
14257
+ const styles = useStyles11();
13801
14258
  const resolvedPrefixes = prefixes ?? EMPTY_PREFIXES;
13802
14259
  const { locale } = useLocale();
13803
14260
  useLocalization(resolvedPrefixes);
@@ -13819,7 +14276,7 @@ function LocalizationBoundary({
13819
14276
  return /* @__PURE__ */ jsx("div", { className: mergeClasses("ppp-localization-boundary", styles.fallback), role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx(Spinner, { size: "medium" }) });
13820
14277
  }
13821
14278
  var EMPTY_PREFIXES = Object.freeze([]);
13822
- var useStyles11 = makeStyles({
14279
+ var useStyles12 = makeStyles({
13823
14280
  root: {
13824
14281
  display: "flex",
13825
14282
  flexDirection: "column",
@@ -13877,7 +14334,7 @@ function isSameLanguage(a, b) {
13877
14334
  return primaryA.length > 0 && primaryA === primary(b);
13878
14335
  }
13879
14336
  function LocalizationTranslator({ className }) {
13880
- const styles = useStyles11();
14337
+ const styles = useStyles12();
13881
14338
  const t = useT();
13882
14339
  const { ppp } = usePowerPortalsPro();
13883
14340
  const [availability, setAvailability] = useState(null);
@@ -14365,7 +14822,7 @@ function SectionColumnRenderer({
14365
14822
  }
14366
14823
  );
14367
14824
  }
14368
- var useStyles12 = makeStyles({
14825
+ var useStyles13 = makeStyles({
14369
14826
  root: {
14370
14827
  display: "flex",
14371
14828
  flexDirection: "column",
@@ -14445,6 +14902,18 @@ var useStyles12 = makeStyles({
14445
14902
  },
14446
14903
  // Scroll viewport for the merged grid so a tall result set scrolls inside the dialog.
14447
14904
  dialogGrid: { maxHeight: "60vh", overflowY: "auto", overflowX: "auto" },
14905
+ // Merged-snapshot cells. Keys and sources are long single tokens (dotted keys,
14906
+ // backslash-only absolute paths) with no natural break opportunity, so without
14907
+ // this the text paints straight over the neighbouring column. `overflow-wrap:
14908
+ // anywhere` (not just `break-word`) is what makes the flex cell honour its
14909
+ // column width — it counts the soft breaks toward min-content — so the token
14910
+ // wraps inside the column instead of stretching it. Same recipe as
14911
+ // `sourceCell` in the per-source loads table above.
14912
+ mergedCell: {
14913
+ whiteSpace: "normal",
14914
+ overflowWrap: "anywhere",
14915
+ wordBreak: "break-word"
14916
+ },
14448
14917
  filterInput: { minWidth: "260px" },
14449
14918
  pager: { display: "flex", alignItems: "center", gap: tokens.spacingHorizontalS },
14450
14919
  grow: { flex: 1 },
@@ -14476,7 +14945,7 @@ function cultureLabel(culture) {
14476
14945
  }
14477
14946
  }
14478
14947
  function LocalizationAdmin({ className }) {
14479
- const styles = useStyles12();
14948
+ const styles = useStyles13();
14480
14949
  const t = useT();
14481
14950
  const { ppp } = usePowerPortalsPro();
14482
14951
  const [overview, setOverview] = useState(null);
@@ -14926,7 +15395,7 @@ function MergedSourcesDialog({
14926
15395
  size: "small",
14927
15396
  children: [
14928
15397
  /* @__PURE__ */ jsx(DataGridHeader, { children: /* @__PURE__ */ jsx(DataGridRow, { children: ({ renderHeaderCell }) => /* @__PURE__ */ jsx(DataGridHeaderCell, { children: renderHeaderCell() }) }) }),
14929
- /* @__PURE__ */ jsx(DataGridBody, { children: ({ item, rowId }) => /* @__PURE__ */ jsx(DataGridRow, { children: ({ renderCell }) => /* @__PURE__ */ jsx(DataGridCell, { children: renderCell(item) }) }, rowId) })
15398
+ /* @__PURE__ */ jsx(DataGridBody, { children: ({ item, rowId }) => /* @__PURE__ */ jsx(DataGridRow, { children: ({ renderCell }) => /* @__PURE__ */ jsx(DataGridCell, { className: styles.mergedCell, children: renderCell(item) }) }, rowId) })
14930
15399
  ]
14931
15400
  }
14932
15401
  ) })
@@ -14946,7 +15415,7 @@ function MergedSourcesDialog({
14946
15415
  ] })
14947
15416
  ] }) }) });
14948
15417
  }
14949
- var useStyles13 = makeStyles({
15418
+ var useStyles14 = makeStyles({
14950
15419
  root: {
14951
15420
  display: "flex",
14952
15421
  flexDirection: "column",
@@ -14969,7 +15438,7 @@ var useStyles13 = makeStyles({
14969
15438
  });
14970
15439
  var toMs = (value) => Number(value);
14971
15440
  function CacheAdmin({ className }) {
14972
- const styles = useStyles13();
15441
+ const styles = useStyles14();
14973
15442
  const t = useT();
14974
15443
  const { ppp } = usePowerPortalsPro();
14975
15444
  const [cacheNames, setCacheNames] = useState([]);
@@ -15104,7 +15573,7 @@ function CacheAdmin({ className }) {
15104
15573
  ] }) : null
15105
15574
  ] });
15106
15575
  }
15107
- var useStyles14 = makeStyles({
15576
+ var useStyles15 = makeStyles({
15108
15577
  list: {
15109
15578
  margin: 0,
15110
15579
  paddingLeft: tokens.spacingHorizontalXL,
@@ -15119,7 +15588,7 @@ function ValidationSummary({
15119
15588
  className,
15120
15589
  style
15121
15590
  }) {
15122
- const styles = useStyles14();
15591
+ const styles = useStyles15();
15123
15592
  const t = useT();
15124
15593
  const validation = useValidationContext();
15125
15594
  const resolvedTitle = title === void 0 ? t("app.errors.form-validation-errors") : title;
@@ -15996,7 +16465,7 @@ function isWizardRecordPageElement(node) {
15996
16465
  const type = node.type;
15997
16466
  return !!type && type[PAGE_MARKER] === true;
15998
16467
  }
15999
- var useStyles15 = makeStyles({
16468
+ var useStyles16 = makeStyles({
16000
16469
  shell: {
16001
16470
  display: "flex",
16002
16471
  flexDirection: "column",
@@ -16138,7 +16607,7 @@ function WizardRecordFormInner({
16138
16607
  stepperVisibility,
16139
16608
  isCreate
16140
16609
  }) {
16141
- const styles = useStyles15();
16610
+ const styles = useStyles16();
16142
16611
  return /* @__PURE__ */ jsx("div", { className: mergeClasses("ppp-wizard-record-form", styles.shell), children: /* @__PURE__ */ jsx(
16143
16612
  Wizard,
16144
16613
  {
@@ -16172,7 +16641,7 @@ function WizardFooter({
16172
16641
  onCancel,
16173
16642
  cancelLabel
16174
16643
  }) {
16175
- const styles = useStyles15();
16644
+ const styles = useStyles16();
16176
16645
  const t = useT();
16177
16646
  const recordContext = useRecordContext();
16178
16647
  const wizard = useWizard();
@@ -17654,13 +18123,17 @@ var usePageLayoutStyles = makeStyles({
17654
18123
  position: "relative",
17655
18124
  display: "grid",
17656
18125
  gridTemplateColumns: "auto minmax(0, 1fr)",
17657
- gridTemplateRows: "auto 1fr auto",
18126
+ // Four rows, not three: the first carries the impersonation banner. It's an `auto` track and
18127
+ // the banner renders nothing for an ordinary session, so the row collapses to zero height and
18128
+ // the layout is unchanged whenever nobody is impersonating.
18129
+ gridTemplateRows: "auto auto 1fr auto",
17658
18130
  // Lets the PageLayout shrink when it's nested inside another
17659
18131
  // PageLayout's `<main>` (the outer main is a grid item; without
17660
18132
  // `min-width: 0` on this inner root the outer's `minmax(0, 1fr)`
17661
18133
  // track wouldn't be honored once content tries to grow wider).
17662
18134
  minWidth: 0,
17663
18135
  gridTemplateAreas: `
18136
+ 'impersonation impersonation'
17664
18137
  'header header'
17665
18138
  'navigation main'
17666
18139
  'footer footer'
@@ -17770,6 +18243,11 @@ var usePageLayoutStyles = makeStyles({
17770
18243
  childHeader: {
17771
18244
  borderBottomWidth: "1px"
17772
18245
  },
18246
+ impersonationBanner: {
18247
+ // Placement only. The banner is a Fluent MessageBar and Fluent owns everything about how it
18248
+ // looks; this grid assignment is the extent of PageLayout's involvement.
18249
+ gridArea: "impersonation"
18250
+ },
17773
18251
  navigation: {
17774
18252
  gridArea: "navigation",
17775
18253
  overflowY: "auto",
@@ -17896,6 +18374,7 @@ function PageLayout({
17896
18374
  ...resizable ? { "--ppp-nav-width": `${navWidth}px` } : {}
17897
18375
  };
17898
18376
  return /* @__PURE__ */ jsx(PageLayoutNestingContext.Provider, { value: { isNested: true }, children: /* @__PURE__ */ jsxs("div", { className: rootClass, style: rootStyle, children: [
18377
+ !isNested && /* @__PURE__ */ jsx(ImpersonationBanner, { className: styles.impersonationBanner }),
17899
18378
  header !== void 0 && headerVisible && /* @__PURE__ */ jsx(Header, { className: headerClass, ...headerStyle && { style: headerStyle }, children: header }),
17900
18379
  navigation !== void 0 && navigationVisible && /* @__PURE__ */ jsx(
17901
18380
  "nav",
@@ -18124,7 +18603,7 @@ var RADIAL_CHART_TYPES = /* @__PURE__ */ new Set([
18124
18603
  ChartType.Radar,
18125
18604
  ChartType.Funnel
18126
18605
  ]);
18127
- var useStyles16 = makeStyles({
18606
+ var useStyles17 = makeStyles({
18128
18607
  // The wrapper uses Fluent design tokens directly so the rounded bordered
18129
18608
  // look picks up theme changes. `colorNeutralBackground1` (white in light,
18130
18609
  // grey[16] in dark) matches the Blazor FluentUIChart's
@@ -18202,7 +18681,7 @@ function FluentUIChartImpl(props, ref) {
18202
18681
  showBorder = true,
18203
18682
  onElementClick
18204
18683
  } = props;
18205
- const styles = useStyles16();
18684
+ const styles = useStyles17();
18206
18685
  const containerRef = useRef(null);
18207
18686
  const palette = useFluentChartPalette(containerRef);
18208
18687
  const themedDatasets = useMemo(() => {
@@ -18283,7 +18762,7 @@ function assembleTheme(palette, yAxisPrefix, yAxisSuffix, xAxisPrefix, xAxisSuff
18283
18762
  if (orientation !== void 0) theme.indexAxis = toIndexAxis(orientation);
18284
18763
  return Object.keys(theme).length > 0 ? theme : void 0;
18285
18764
  }
18286
- var useStyles17 = makeStyles({
18765
+ var useStyles18 = makeStyles({
18287
18766
  // Outer container — flex column so the view selector, message bar, and
18288
18767
  // chart fill share the caller's height budget.
18289
18768
  root: {
@@ -18361,7 +18840,7 @@ var DataverseChart = forwardRef(
18361
18840
  } = props;
18362
18841
  const chartSource = dataSource instanceof DataverseChartDataSource ? dataSource : void 0;
18363
18842
  const viewDs = dataSource instanceof DataverseChartDataSource ? void 0 : dataSource;
18364
- const styles = useStyles17();
18843
+ const styles = useStyles18();
18365
18844
  const t = useT();
18366
18845
  const { ppp, viewMetadataCache } = usePowerPortalsPro();
18367
18846
  const [data, setData] = useState({ labels: [], datasets: [] });
@@ -18595,6 +19074,6 @@ function applyViewIdToSource(source, viewId) {
18595
19074
  // src/index.ts
18596
19075
  var VERSION = "0.1.0";
18597
19076
 
18598
- export { Body, BoolEdit, BoolEditorType, CacheAdmin, ChoiceEdit, ChoiceEditorType, ChoiceInvalidValueBehavior, ChoiceOrientation, ChoiceValueSort, ColumnDisplayValue, ColumnEdit, DataverseChart, DateTimeEdit, DateTimeEditorType, DeleteContextButton, DeleteRecordGridButton, DescriptionLocation, FileEdit, FileGrid, FileViewer, FluentDialogProvider, FluentOverlayProvider, FluentOverlayTarget, FluentUIChart, Footer, GridButton, GridButtonBehavior, GridButtonRole, GridButtons, GridColumn, GridColumns, GridTemplateColumn, Header, Highlighter, ImageEdit, ImageViewer, LabelPosition, LanguageDropdown, LinkExistingRecordGridButton, LocalizationAdmin, LocalizationBoundary, LocalizationTranslator, LogoutButton, LookupDialog, LookupEdit, LookupEditorType, LookupForm, MainGrid, ManyToManyLookupEdit, ManyToManyLookupEditorType, MaskedTextField, MemoEdit, MenuBar, MoneyEdit, MultiSelectChoiceEdit, NavGroup, NavItem, NavMenu, NavigateNewRecordGridButton, NavigateOpenRecordGridButton, NavigateRecordGridButton, NewRecordGridButton, NumberEdit, OFFICE_ACCENT_DEFINITIONS, OFFICE_ACCENT_HEX_TO_NAME, OFFICE_ACCENT_THEMES, OFFICE_COLOR_NAME_TO_ACCENT, OpenRecordGridButton, PageLayout, PagingMode, PowerPortalsProThemeProvider, ProfileMainMenu, RefreshContextButton, RefreshGridButton, RouterUnsavedChangesGuard, SaveContextButton, Section, SectionColumn, SectionColumnOrientation, SiteSettingsButton, SiteSettingsPanel, SubGrid, TextDirection, TextEdit, ThemeColorSelector, ThemeMode, ThemeSelector, ThemeTextDirection, UnlinkExistingRecordGridButton, VERSION, ValidationSummary, ViewSort, WizardRecordForm, WizardRecordPage, evaluateGridButtonBehavior, findFirstButtonOfRole, getCellRenderer, getChartPalette, getChoiceMetadata, getDisplayDescription, getDisplayLabel, getEditComponentForMetadata, getEditComponentForType, getGridButtonRoleFromElement, getLookupMetadata, getNumberLimits, getRendererTypeForMetadata, getStringMaxLength, isColumnSortable, isMetadataReadOnly, isMetadataRequired, isNewRecord, officeColorValueToAccentName, resolveColumnDisplayValue, setGridButtonRole, splitHighlightSegments, toTranslucent, useBoolColumn, useChoiceColumn, useDateColumn, useFileColumn, useFluentChartPalette, useGridContext, useLookupColumn, useMoneyColumn, useMultiSelectChoiceColumn, useNumberColumn, useStringColumn, useTheme };
19077
+ export { Body, BoolEdit, BoolEditorType, CacheAdmin, ChoiceEdit, ChoiceEditorType, ChoiceInvalidValueBehavior, ChoiceOrientation, ChoiceValueSort, ColumnDisplayValue, ColumnEdit, DataverseChart, DateTimeEdit, DateTimeEditorType, DeleteContextButton, DeleteRecordGridButton, DescriptionLocation, FileEdit, FileGrid, FileViewer, FluentDialogProvider, FluentOverlayProvider, FluentOverlayTarget, FluentUIChart, Footer, GridButton, GridButtonBehavior, GridButtonRole, GridButtons, GridColumn, GridColumns, GridTemplateColumn, Header, Highlighter, ImageEdit, ImageViewer, ImpersonationBanner, ImpersonationPickerDialog, LabelPosition, LanguageDropdown, LinkExistingRecordGridButton, LocalizationAdmin, LocalizationBoundary, LocalizationTranslator, LogoutButton, LookupDialog, LookupEdit, LookupEditorType, LookupForm, MainGrid, ManyToManyLookupEdit, ManyToManyLookupEditorType, MaskedTextField, MemoEdit, MenuBar, MoneyEdit, MultiSelectChoiceEdit, NavGroup, NavItem, NavMenu, NavigateNewRecordGridButton, NavigateOpenRecordGridButton, NavigateRecordGridButton, NewRecordGridButton, NumberEdit, OFFICE_ACCENT_DEFINITIONS, OFFICE_ACCENT_HEX_TO_NAME, OFFICE_ACCENT_THEMES, OFFICE_COLOR_NAME_TO_ACCENT, OpenRecordGridButton, PageLayout, PagingMode, PowerPortalsProThemeProvider, ProfileMainMenu, RefreshContextButton, RefreshGridButton, RouterUnsavedChangesGuard, SaveContextButton, Section, SectionColumn, SectionColumnOrientation, SiteSettingsButton, SiteSettingsPanel, SubGrid, TextDirection, TextEdit, ThemeColorSelector, ThemeMode, ThemeSelector, ThemeTextDirection, UnlinkExistingRecordGridButton, VERSION, ValidationSummary, ViewSort, WizardRecordForm, WizardRecordPage, evaluateGridButtonBehavior, findFirstButtonOfRole, getCellRenderer, getChartPalette, getChoiceMetadata, getDisplayDescription, getDisplayLabel, getEditComponentForMetadata, getEditComponentForType, getGridButtonRoleFromElement, getLookupMetadata, getNumberLimits, getRendererTypeForMetadata, getStringMaxLength, isColumnSortable, isMetadataReadOnly, isMetadataRequired, isNewRecord, officeColorValueToAccentName, resolveColumnDisplayValue, setGridButtonRole, splitHighlightSegments, toTranslucent, useBoolColumn, useChoiceColumn, useDateColumn, useFileColumn, useFluentChartPalette, useGridContext, useLookupColumn, useMoneyColumn, useMultiSelectChoiceColumn, useNumberColumn, useStringColumn, useTheme };
18599
19078
  //# sourceMappingURL=index.js.map
18600
19079
  //# sourceMappingURL=index.js.map