@powerportalspro/react-fluent 5.0.0 → 5.1.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
@@ -2,7 +2,7 @@ import { makeStyles, mergeClasses, Input, tokens, Toolbar, ToolbarButton, Toolba
2
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 } 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, ArrowDownloadRegular, Save24Regular, Delete24Regular, Add20Regular, Delete20Regular, LinkDismiss20Regular, ArrowUpload32Regular, ArrowUpload24Regular, ArrowSync24Regular, DrawerArrowDownload24Regular, Money20Regular, ErrorCircle12Filled, MoreHorizontal20Regular, ChevronDoubleLeft20Regular, ChevronLeft20Regular, ChevronDoubleRight20Regular, Info12Regular, Checkmark16Regular, Dismiss16Regular, DocumentText20Regular, FolderZip20Regular, MusicNote220Regular, Video20Regular, Image20Regular, SlideText20Regular, DocumentTable20Regular, DocumentPdf20Regular } from '@fluentui/react-icons';
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, ArrowDownloadRegular, 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
6
  import { RequiredLevel, DateTimeBehavior, getRecordReferenceId, RelationshipFilterMode, SwitchIdentityResult, TableSecurityPermission } from '@powerportalspro/core';
7
7
  import { DatePicker } from '@fluentui/react-datepicker-compat';
8
8
  import { TimePicker } from '@fluentui/react-timepicker-compat';
@@ -2468,6 +2468,7 @@ var ImageViewer = forwardRef(function ImageViewer2({
2468
2468
  const selectionRef = useRef(selection);
2469
2469
  selectionRef.current = selection;
2470
2470
  const prevTransformRef = useRef({ angle, zoom, pan, flipH, flipV });
2471
+ const resizingRef = useRef(false);
2471
2472
  const dragRef = useRef(null);
2472
2473
  const [isDragging, setIsDragging] = useState(false);
2473
2474
  const zoomRef = useRef(zoom);
@@ -2497,6 +2498,7 @@ var ImageViewer = forwardRef(function ImageViewer2({
2497
2498
  const cur = { angle, zoom, pan, flipH, flipV };
2498
2499
  const transformChanged = prev.angle !== cur.angle || prev.zoom !== cur.zoom || prev.pan.x !== cur.pan.x || prev.pan.y !== cur.pan.y || prev.flipH !== cur.flipH || prev.flipV !== cur.flipV;
2499
2500
  if (transformChanged) {
2501
+ resizingRef.current = false;
2500
2502
  const sel = selectionRef.current;
2501
2503
  const imgEl = imgRef.current;
2502
2504
  if (sel && imgEl) {
@@ -2527,6 +2529,51 @@ var ImageViewer = forwardRef(function ImageViewer2({
2527
2529
  }
2528
2530
  prevTransformRef.current = cur;
2529
2531
  }, [angle, zoom, pan, flipH, flipV]);
2532
+ useEffect(() => {
2533
+ if (!toolbar) return;
2534
+ const container = imageContainerRef.current;
2535
+ if (!container) return;
2536
+ let prevMetrics = imgRef.current ? readImageLayoutMetrics(imgRef.current) : null;
2537
+ const observer = new ResizeObserver(() => {
2538
+ const imgEl = imgRef.current;
2539
+ if (!imgEl) return;
2540
+ const newMetrics = readImageLayoutMetrics(imgEl);
2541
+ if (!newMetrics) return;
2542
+ const sel = selectionRef.current;
2543
+ const layoutChanged = !prevMetrics || prevMetrics.layoutW !== newMetrics.layoutW || prevMetrics.layoutH !== newMetrics.layoutH || prevMetrics.offsetLeft !== newMetrics.offsetLeft || prevMetrics.offsetTop !== newMetrics.offsetTop;
2544
+ if (sel && prevMetrics && layoutChanged) {
2545
+ const natural = containerRectToImageNatural(
2546
+ sel,
2547
+ imgEl,
2548
+ angleRef.current,
2549
+ zoomRef.current,
2550
+ panRef.current,
2551
+ flipHRef.current,
2552
+ flipVRef.current,
2553
+ prevMetrics
2554
+ );
2555
+ if (natural) {
2556
+ const reprojected = imageNaturalRectToContainer(
2557
+ natural,
2558
+ imgEl,
2559
+ angleRef.current,
2560
+ zoomRef.current,
2561
+ panRef.current,
2562
+ flipHRef.current,
2563
+ flipVRef.current,
2564
+ newMetrics
2565
+ );
2566
+ if (reprojected) {
2567
+ resizingRef.current = true;
2568
+ setSelection(reprojected);
2569
+ }
2570
+ }
2571
+ }
2572
+ prevMetrics = newMetrics;
2573
+ });
2574
+ observer.observe(container);
2575
+ return () => observer.disconnect();
2576
+ }, [toolbar]);
2530
2577
  const onDirtyChangedRef = useRef(onDirtyChanged);
2531
2578
  onDirtyChangedRef.current = onDirtyChanged;
2532
2579
  useEffect(() => {
@@ -3280,7 +3327,7 @@ var ImageViewer = forwardRef(function ImageViewer2({
3280
3327
  // pointermove (which is what changes the selection during
3281
3328
  // a drag) calls setSelection — that triggers a re-render
3282
3329
  // which reads the current dragRef value.
3283
- transition: dragRef.current ? "none" : "left 120ms ease-out, top 120ms ease-out, width 120ms ease-out, height 120ms ease-out"
3330
+ transition: dragRef.current || resizingRef.current ? "none" : "left 120ms ease-out, top 120ms ease-out, width 120ms ease-out, height 120ms ease-out"
3284
3331
  },
3285
3332
  children: [
3286
3333
  showThirds && /* @__PURE__ */ jsxs("div", { className: styles.thirdsGuide, "aria-hidden": "true", children: [
@@ -3441,6 +3488,21 @@ function useImageSrc(source) {
3441
3488
  return source.value ? `data:${resolveMimeType(source)};base64,${source.value}` : null;
3442
3489
  return bytesUrlRef.current;
3443
3490
  }
3491
+ function readImageLayoutMetrics(imageEl) {
3492
+ const layoutW = imageEl.offsetWidth;
3493
+ const layoutH = imageEl.offsetHeight;
3494
+ const naturalW = imageEl.naturalWidth;
3495
+ const naturalH = imageEl.naturalHeight;
3496
+ if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;
3497
+ return {
3498
+ layoutW,
3499
+ layoutH,
3500
+ offsetLeft: imageEl.offsetLeft,
3501
+ offsetTop: imageEl.offsetTop,
3502
+ naturalW,
3503
+ naturalH
3504
+ };
3505
+ }
3444
3506
  function getDisplayedImageBounds(imageEl, angle, zoom, pan, flipH, flipV) {
3445
3507
  const layoutW = imageEl.offsetWidth;
3446
3508
  const layoutH = imageEl.offsetHeight;
@@ -3483,15 +3545,13 @@ function getDisplayedImageBounds(imageEl, angle, zoom, pan, flipH, flipV) {
3483
3545
  }
3484
3546
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
3485
3547
  }
3486
- function containerRectToImageNatural(rect, imageEl, angle, zoom, pan, flipH, flipV) {
3487
- const layoutW = imageEl.offsetWidth;
3488
- const layoutH = imageEl.offsetHeight;
3489
- const naturalW = imageEl.naturalWidth;
3490
- const naturalH = imageEl.naturalHeight;
3491
- if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;
3548
+ function containerRectToImageNatural(rect, imageEl, angle, zoom, pan, flipH, flipV, metricsOverride) {
3549
+ const m = metricsOverride ?? readImageLayoutMetrics(imageEl);
3550
+ if (!m) return null;
3551
+ const { layoutW, layoutH, naturalW, naturalH } = m;
3492
3552
  const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);
3493
- const imgCenterX = imageEl.offsetLeft + layoutW / 2;
3494
- const imgCenterY = imageEl.offsetTop + layoutH / 2;
3553
+ const imgCenterX = m.offsetLeft + layoutW / 2;
3554
+ const imgCenterY = m.offsetTop + layoutH / 2;
3495
3555
  const sx = zoom * (flipH ? -1 : 1);
3496
3556
  const sy = zoom * (flipV ? -1 : 1);
3497
3557
  if (sx === 0 || sy === 0) return null;
@@ -3524,15 +3584,13 @@ function containerRectToImageNatural(rect, imageEl, angle, zoom, pan, flipH, fli
3524
3584
  }
3525
3585
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
3526
3586
  }
3527
- function imageNaturalRectToContainer(rect, imageEl, angle, zoom, pan, flipH, flipV) {
3528
- const layoutW = imageEl.offsetWidth;
3529
- const layoutH = imageEl.offsetHeight;
3530
- const naturalW = imageEl.naturalWidth;
3531
- const naturalH = imageEl.naturalHeight;
3532
- if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;
3587
+ function imageNaturalRectToContainer(rect, imageEl, angle, zoom, pan, flipH, flipV, metricsOverride) {
3588
+ const m = metricsOverride ?? readImageLayoutMetrics(imageEl);
3589
+ if (!m) return null;
3590
+ const { layoutW, layoutH, naturalW, naturalH } = m;
3533
3591
  const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);
3534
- const imgCenterX = imageEl.offsetLeft + layoutW / 2;
3535
- const imgCenterY = imageEl.offsetTop + layoutH / 2;
3592
+ const imgCenterX = m.offsetLeft + layoutW / 2;
3593
+ const imgCenterY = m.offsetTop + layoutH / 2;
3536
3594
  const sx = zoom * (flipH ? -1 : 1);
3537
3595
  const sy = zoom * (flipV ? -1 : 1);
3538
3596
  const rad = angle * Math.PI / 180;
@@ -4410,6 +4468,69 @@ function ZipViewer({
4410
4468
  }
4411
4469
  );
4412
4470
  }
4471
+ function ZipEntryIcon({ fileName, className }) {
4472
+ switch (getFileExtension(fileName)) {
4473
+ case "pdf":
4474
+ return /* @__PURE__ */ jsx(DocumentPdf20Regular, { className });
4475
+ case "xls":
4476
+ case "xlsx":
4477
+ case "csv":
4478
+ case "ods":
4479
+ return /* @__PURE__ */ jsx(DocumentTable20Regular, { className });
4480
+ case "ppt":
4481
+ case "pptx":
4482
+ case "odp":
4483
+ return /* @__PURE__ */ jsx(SlideText20Regular, { className });
4484
+ case "png":
4485
+ case "jpg":
4486
+ case "jpeg":
4487
+ case "gif":
4488
+ case "bmp":
4489
+ case "webp":
4490
+ case "svg":
4491
+ case "ico":
4492
+ case "tif":
4493
+ case "tiff":
4494
+ return /* @__PURE__ */ jsx(Image20Regular, { className });
4495
+ case "mp4":
4496
+ case "mov":
4497
+ case "avi":
4498
+ case "mkv":
4499
+ case "webm":
4500
+ case "m4v":
4501
+ return /* @__PURE__ */ jsx(Video20Regular, { className });
4502
+ case "mp3":
4503
+ case "wav":
4504
+ case "ogg":
4505
+ case "flac":
4506
+ case "m4a":
4507
+ case "aac":
4508
+ return /* @__PURE__ */ jsx(MusicNote220Regular, { className });
4509
+ case "zip":
4510
+ case "7z":
4511
+ case "rar":
4512
+ case "tar":
4513
+ case "gz":
4514
+ return /* @__PURE__ */ jsx(FolderZip20Regular, { className });
4515
+ case "txt":
4516
+ case "md":
4517
+ case "markdown":
4518
+ case "log":
4519
+ case "json":
4520
+ case "xml":
4521
+ case "yaml":
4522
+ case "yml":
4523
+ case "html":
4524
+ case "htm":
4525
+ case "js":
4526
+ case "ts":
4527
+ case "cs":
4528
+ case "css":
4529
+ return /* @__PURE__ */ jsx(DocumentText20Regular, { className });
4530
+ default:
4531
+ return /* @__PURE__ */ jsx(Document20Regular, { className });
4532
+ }
4533
+ }
4413
4534
  function ZipTreeNode({ node, depth, expanded, selected, onToggle, onSelect }) {
4414
4535
  const styles = useFileViewerStyles();
4415
4536
  const isExpanded = expanded.has(node.fullPath);
@@ -4468,7 +4589,7 @@ function ZipTreeNode({ node, depth, expanded, selected, onToggle, onSelect }) {
4468
4589
  style: indent,
4469
4590
  children: [
4470
4591
  /* @__PURE__ */ jsx("span", { className: styles.zipChevronSpacer }),
4471
- /* @__PURE__ */ jsx(Document20Regular, { className: styles.zipIcon }),
4592
+ /* @__PURE__ */ jsx(ZipEntryIcon, { fileName: node.name, className: styles.zipIcon }),
4472
4593
  /* @__PURE__ */ jsx("span", { className: styles.zipName, children: node.name })
4473
4594
  ]
4474
4595
  }
@@ -5113,11 +5234,13 @@ var useFileViewerStyles = makeStyles({
5113
5234
  outlineOffset: "-1px"
5114
5235
  }
5115
5236
  },
5237
+ // Selection style ported from the Blazor zip viewer: a subtle accent tint over
5238
+ // the row (text stays its normal color), intensifying slightly on hover so the
5239
+ // neutral row-hover doesn't wash out the selection.
5116
5240
  zipRowSelected: {
5117
- backgroundColor: tokens.colorBrandBackground2,
5118
- color: tokens.colorBrandForeground2,
5241
+ backgroundColor: `color-mix(in srgb, ${tokens.colorBrandBackground} 12%, transparent)`,
5119
5242
  ":hover": {
5120
- backgroundColor: tokens.colorBrandBackground2Hover
5243
+ backgroundColor: `color-mix(in srgb, ${tokens.colorBrandBackground} 18%, transparent)`
5121
5244
  }
5122
5245
  },
5123
5246
  zipChevron: {
@@ -11656,15 +11779,11 @@ function ManyToManyLookupEdit({
11656
11779
  return relatedPrimaryName;
11657
11780
  }, [resolvedColumns, relatedPrimaryName]);
11658
11781
  const lastSeededRecordIdRef = useRef(void 0);
11659
- const lastSeededViewIdRef = useRef(void 0);
11660
11782
  useEffect(() => {
11661
11783
  if (initialLoad.status !== QueryStatus.Success || !initialLoad.data) return;
11662
11784
  if (!primaryNameColumn || !parentRecord) return;
11663
- if (lastSeededRecordIdRef.current === parentRefId && lastSeededViewIdRef.current === selectedViewId) {
11664
- return;
11665
- }
11785
+ if (lastSeededRecordIdRef.current === parentRefId) return;
11666
11786
  lastSeededRecordIdRef.current = parentRefId;
11667
- lastSeededViewIdRef.current = selectedViewId;
11668
11787
  const seeded = initialLoad.data.tableRecords.map((r) => ({
11669
11788
  id: r.id,
11670
11789
  displayName: resolveDisplayName(r, primaryNameColumn) ?? "",
@@ -11673,7 +11792,7 @@ function ManyToManyLookupEdit({
11673
11792
  setInitialSelection(seeded);
11674
11793
  setPendingAdds([]);
11675
11794
  setPendingRemoveIds([]);
11676
- }, [initialLoad.status, initialLoad.data, parentRecord, primaryNameColumn, selectedViewId]);
11795
+ }, [initialLoad.status, initialLoad.data, parentRecord, primaryNameColumn, parentRefId]);
11677
11796
  const [query, setQuery] = useState("");
11678
11797
  const [debouncedQuery, setDebouncedQuery] = useState("");
11679
11798
  const [searchResults, setSearchResults] = useState([]);
@@ -11785,7 +11904,6 @@ function ManyToManyLookupEdit({
11785
11904
  setPendingAdds([]);
11786
11905
  setPendingRemoveIds([]);
11787
11906
  lastSeededRecordIdRef.current = void 0;
11788
- lastSeededViewIdRef.current = void 0;
11789
11907
  },
11790
11908
  // Cancel/Discard: just drop the pending adds/removes; the
11791
11909
  // seed-guard refs stay so the initial selection (already loaded