@powerportalspro/react-fluent 5.0.0-beta.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edits/base/column-edit-types.ts","../src/edits/base/description-icon.tsx","../src/edits/base/metadata-helpers.ts","../src/edits/base/field-validation.tsx","../src/edits/base/use-column-validation.ts","../src/edits/base/use-column-edit.ts","../src/edits/text-edit.tsx","../src/edits/memo-edit.tsx","../src/edits/bool-edit.tsx","../src/edits/number-edit.tsx","../src/edits/base/format-value.ts","../src/edits/money-edit.tsx","../src/edits/base/column-display.ts","../src/edits/column-display-value.tsx","../src/edits/date-time-edit.tsx","../src/edits/base/file-helpers.ts","../src/edits/base/use-file-upload.ts","../src/viewers/image-viewer.tsx","../src/viewers/file-viewer.tsx","../src/edits/file-edit.tsx","../src/edits/image-edit.tsx","../src/edits/base/choice-helpers.ts","../src/edits/choice-edit.tsx","../src/edits/multi-select-choice-edit.tsx","../src/components/highlighter.tsx","../src/grids/cells/bool-cell.tsx","../src/grids/cells/choice-cell.tsx","../src/grids/cells/date-cell.tsx","../src/grids/cells/fallback-cell.tsx","../src/grids/cells/file-type-icon.tsx","../src/grids/grid-context.tsx","../src/grids/cells/file-cell.tsx","../src/grids/cells/lookup-cell.tsx","../src/grids/cells/numeric-search-match.ts","../src/grids/cells/money-cell.tsx","../src/grids/cells/multi-select-choice-cell.tsx","../src/grids/cells/number-cell.tsx","../src/grids/cells/text-cell.tsx","../src/grids/cells/cell-dispatcher.tsx","../src/edits/column-edit.tsx","../src/grids/grid-column.tsx","../src/grids/grid-toolbar-overflow.tsx","../src/grids/use-is-narrow-viewport.ts","../src/layout/footer.tsx","../src/grids/main-grid-styles.ts","../src/grids/main-grid-paging.tsx","../src/grids/persisted-grid-state.ts","../src/grids/view-options.ts","../src/grids/main-grid.tsx","../src/form/lookup-form.tsx","../src/form/lookup-dialog.tsx","../src/edits/lookup-edit.tsx","../src/edits/many-to-many-lookup-edit.helpers.ts","../src/edits/many-to-many-lookup-edit.tsx","../src/dialog/fluent-dialog-provider.tsx","../src/theme/theme-context.tsx","../src/theme/theme-provider.tsx","../src/theme/theme-selector.tsx","../src/theme/theme-color-selector.tsx","../src/theme/theme-text-direction.tsx","../src/editors/language-dropdown.tsx","../src/menu/profile-main-menu.tsx","../src/menu/site-settings-panel.tsx","../src/menu/site-settings-button.tsx","../src/menu/logout-button.tsx","../src/menu/nav-menu.tsx","../src/overlay/fluent-overlay-provider.tsx","../src/localization/localization-boundary.tsx","../src/validation/validation-summary.tsx","../src/buttons/save-context-button.tsx","../src/buttons/refresh-context-button.tsx","../src/buttons/delete-context-button.tsx","../src/buttons/grid-button-role.ts","../src/buttons/grid-button-behavior.ts","../src/buttons/grid-button.tsx","../src/buttons/navigate-record-grid-button.tsx","../src/buttons/refresh-grid-button.tsx","../src/buttons/navigate-new-record-grid-button.tsx","../src/buttons/navigate-open-record-grid-button.tsx","../src/buttons/open-record-grid-button.tsx","../src/form/wizard-record-page.tsx","../src/form/wizard-record-form.tsx","../src/buttons/new-record-grid-button.tsx","../src/buttons/delete-record-grid-button.tsx","../src/buttons/link-existing-record-grid-button.tsx","../src/buttons/unlink-existing-record-grid-button.tsx","../src/grids/sub-grid.tsx","../src/grids/file-grid-helpers.ts","../src/grids/file-grid-styles.ts","../src/grids/grid-buttons.tsx","../src/grids/file-grid.tsx","../src/layout/header.tsx","../src/layout/page-layout.tsx","../src/layout/body.tsx","../src/routing/router-unsaved-changes-guard.tsx","../src/layout/section.tsx","../src/charts/palette.ts","../src/charts/use-fluent-chart-palette.ts","../src/charts/fluent-ui-chart.tsx","../src/charts/dataverse-chart.tsx","../src/index.ts"],"names":["makeStyles","tokens","useState","useRef","useCallback","useEffect","jsx","jsxs","Popover","PopoverTrigger","Info12Regular","PopoverSurface","RequiredLevel","isNewRecord","TableSecurityPermission","ErrorCircle12Filled","useValidationContext","useColumnValidationErrors","useT","useRecordContext","useColumnMetadata","Fragment","Field","mergeClasses","Input","Textarea","useStyles","useId","Switch","Checkbox","Label","useLocale","useMemo","DatePicker","TimePicker","DateTimeBehavior","useDialogService","forwardRef","ImageViewer","rect","localX","localY","dx","dy","x","y","w","h","ib","minX","minY","maxX","maxY","useImperativeHandle","Toolbar","ToolbarButton","ArrowRotateCounterclockwise20Regular","ArrowRotateClockwise20Regular","FlipHorizontal20Regular","FlipVertical20Regular","ZoomIn20Regular","ZoomOut20Regular","ArrowReset20Regular","ToolbarDivider","Menu","MenuTrigger","ChevronDown16Regular","MenuPopover","MenuList","MenuItemRadio","Grid20Regular","Crop20Regular","FullScreenMinimize20Regular","FullScreenMaximize20Regular","ArrowDownload20Regular","Prohibited20Regular","Checkmark20Filled","Button","hljs","xml","json","yaml","javascript","typescript","csharp","css","markdown","TabList","Tab","Markdown","unzipSync","DocumentArrowLeft24Regular","ChevronDown20Regular","ChevronRight20Regular","Folder20Regular","Tooltip","Document20Regular","Spinner","ErrorCircle20Regular","useLayoutEffect","usePowerPortalsPro","Eye20Regular","Dismiss20Regular","Dialog","DialogSurface","DialogBody","DialogTitle","DialogContent","DialogActions","Edit20Regular","RadioGroup","Radio","Dropdown","Option","cursor","Checkmark16Regular","Dismiss16Regular","DocumentPdf20Regular","DocumentTable20Regular","SlideText20Regular","Image20Regular","Video20Regular","MusicNote220Regular","FolderZip20Regular","DocumentText20Regular","createContext","useContext","EMPTY_GUID","DialogTrigger","isValidElement","Children","useOverflowMenu","MoreHorizontal20Regular","useIsOverflowItemVisible","ChevronDoubleLeft20Regular","ChevronLeft20Regular","ChevronDoubleRight20Regular","createColumnHelper","MainContext","useTableMetadata","useWarnAboutUnsavedChanges","useMainContext","useTransition","useViewsForTable","useViewMetadata","QueryStatus","useGridData","useReactTable","getCoreRowModel","getRecordReferenceId","data","Text","Overflow","OverflowItem","SearchBox","Add16Regular","CounterBadge","Edit16Regular","LinkAdd16Regular","LinkDismiss16Regular","Delete16Regular","ArrowClockwise24Regular","Settings20Regular","Search20Regular","MenuItem","ArrowClockwise20Regular","ArrowSortDown16Regular","ArrowSortUp16Regular","RecordContext","Dismiss24Regular","LinkAdd20Regular","SEARCH_DEBOUNCE_MS","t","record","Combobox","tableLabel","VIEW_TYPE_PUBLIC","RelationshipFilterMode","id","TagPicker","TagPickerControl","TagPickerGroup","Tag","TagPickerInput","TagPickerList","TagPickerOption","DialogServiceProvider","CheckmarkCircle24Regular","Warning24Regular","DismissCircle24Regular","Info24Regular","QuestionCircle24Regular","webLightTheme","webDarkTheme","teamsLightTheme","teamsDarkTheme","makeStaticStyles","FluentProvider","resolveDisplayName","english","useSupportedLocales","useAuth","SwitchIdentityResult","AuthStatus","Avatar","ArrowExit20Regular","Divider","PersonEdit20Regular","ArrowSwap20Regular","SignOut20Regular","OverlayDrawer","DrawerHeader","DrawerHeaderTitle","DrawerBody","useLocation","useNavigate","Nav","NavCategory","NavCategoryItem","NavSubItemGroup","NavSubItem","useOverlayServiceState","OverlayServiceProvider","useOverlayForTarget","useLocalization","useLocalizationLoaded","MessageBar","MessageBarBody","MessageBarTitle","useRunWithOverlay","Save24Regular","Delete24Regular","Add20Regular","useWizard","Wizard","useDialogStyles","DialogInner","Delete20Regular","LinkDismiss20Regular","useRecordContextOptional","useEnvironmentFileSettings","bytesToBase64","ArrowUpload32Regular","ArrowUpload24Regular","ArrowSync24Regular","DrawerArrowDownload24Regular","useUnsavedChangesGuard","useBlocker","ChartType","color","dataset","Chart","toIndexAxis","DataverseChart","DataverseChartDataSource","ArrowDownload24Regular","AggregateDataverseChartDataSource","ViewDataverseChartDataSource"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ColumnValidator } from '@powerportalspro/react';\r\nimport type { CSSProperties } from 'react';\r\n\r\n/**\r\n * Where the description (hint text) is rendered, when one is present. React\r\n * version of Blazor's `DisplayTooltipWhenAvailable: bool` — same intent but\r\n * three-way:\r\n *\r\n * - `'tooltip'` (default) — renders a clickable Info icon next to the label\r\n * that opens a Fluent `Popover` containing the description on click.\r\n * Mirrors Blazor's `BaseFluentColumnEdit.GetLabelTemplate()` pattern.\r\n * Doesn't take vertical space and keeps long form layouts compact.\r\n * - `'belowControl'` — renders the description inline below the input via\r\n * Fluent `Field`'s hint slot. Always visible; takes up vertical space.\r\n * Matches Blazor's older field-hint behavior.\r\n * - `'none'` — suppresses the description entirely, even when one is\r\n * provided by metadata or props.\r\n */\r\nexport const DescriptionLocation = {\r\n None: 'none',\r\n Tooltip: 'tooltip',\r\n BelowControl: 'belowControl',\r\n} as const;\r\nexport type DescriptionLocation = (typeof DescriptionLocation)[keyof typeof DescriptionLocation];\r\n\r\n/**\r\n * Shared props for every column-bound edit component. Mirrors the parameter\r\n * set Blazor's `BaseEdit` / `BaseColumnEdit` expose, minus a few Blazor-specific\r\n * concepts that don't translate (see notes at bottom).\r\n *\r\n * Most metadata-driven defaults are wired up — `label`, `description`,\r\n * `maxLength`, `min`, `max`, `required`, `readOnly` all auto-resolve from the\r\n * parent RecordContext's table metadata when the consumer doesn't pass them.\r\n * Consumer-passed props always win.\r\n *\r\n * Per-component subtypes (`TextEditProps`, `BoolEditProps`, etc.) extend this\r\n * with their type-specific props (Rows, EditorType, Precision, …).\r\n *\r\n * **Currently no-op props (kept for parity, will activate downstream):**\r\n * - `displayValidationErrorMessage` — pending the form-validation layer.\r\n *\r\n * **Blazor props NOT mirrored** (Blazor-specific or non-applicable):\r\n * - `ChildContent` — Blazor's `RenderFragment` for slot composition. React\r\n * has `children` natively; declaring it on a shared base is misleading\r\n * when most edits don't render children. Per-component types add it where\r\n * it actually makes sense.\r\n * - `Rows` on TextEdit — Blazor exposes it but FluentTextField is single-line\r\n * so it has no effect. {@link MemoEdit} is the multi-line variant where\r\n * `rows` belongs.\r\n */\r\nexport interface BaseColumnEditProps {\r\n /** Logical name of the column on the parent {@link RecordContext}'s record (e.g. `\"firstname\"`). Required. */\r\n columnName: string;\r\n\r\n /** Display label. Falls back to the column metadata's `displayName`, then to `columnName`. */\r\n label?: string;\r\n\r\n /** Description / hint text shown under the label. Falls back to the column metadata's `description`. */\r\n description?: string;\r\n\r\n /**\r\n * Render the label when one is available. Defaults to `true`. Set to `false`\r\n * to suppress the label even when the metadata or consumer provides one.\r\n */\r\n displayLabelWhenAvailable?: boolean;\r\n\r\n /**\r\n * Where the description (hint text) is rendered when one is available.\r\n * Defaults to {@link DescriptionLocation.Tooltip}. Three-way enum rather\r\n * than a single boolean toggle — see {@link DescriptionLocation} for the\r\n * modes. The description source is the `description` prop, falling back to\r\n * the column metadata's `description`.\r\n */\r\n descriptionLocation?: DescriptionLocation;\r\n\r\n /**\r\n * Render an inline error message below the input when the field fails\r\n * validation. Defaults to `true`. **Currently a no-op** — the React\r\n * layer's form-validation surface isn't in place yet. Kept on the prop\r\n * interface so consumer code stays portable.\r\n */\r\n displayValidationErrorMessage?: boolean;\r\n\r\n /** Marks the field required for visual styling. Falls back to metadata's RequiredLevel. */\r\n required?: boolean;\r\n\r\n /** Whether the input is read-only. Falls back to metadata's `isValidForCreate`/`isValidForUpdate`. */\r\n readOnly?: boolean;\r\n\r\n /** Whether the input is disabled (typically muted + uneditable + non-focusable). */\r\n disabled?: boolean;\r\n\r\n /** When false, renders nothing. Defaults to true. */\r\n isVisible?: boolean;\r\n\r\n /** Optional CSS class on the rendered input element. */\r\n className?: string;\r\n\r\n /** Optional inline style on the rendered input element. */\r\n style?: CSSProperties;\r\n\r\n /** Optional id on the rendered input element. */\r\n id?: string;\r\n\r\n /**\r\n * Custom validator that runs alongside the built-in checks (required,\r\n * min-length / max-length for strings, min / max for numbers, etc.).\r\n * Return a list of error messages — empty list means valid. Sync or\r\n * async; async validators run sequentially during the form-level\r\n * `validateAll()` invoked by `<SaveContextButton>`.\r\n *\r\n * Errors from the built-in validators land first, followed by errors from\r\n * this custom validator. Both render via the per-field inline error\r\n * (Field's `validationMessage` slot) and the `<ValidationSummary>`.\r\n *\r\n * Use this for per-column business rules — email regex on a contact's\r\n * `emailaddress1`, prefix check on an account number, etc. The validator\r\n * captures whatever state it needs at call time; refactor closure-captured\r\n * state into refs if you need it to react to other values.\r\n */\r\n validate?: ColumnValidator;\r\n}\r\n","import {\r\n Popover,\r\n PopoverSurface,\r\n PopoverTrigger,\r\n makeStyles,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { Info12Regular } from '@fluentui/react-icons';\r\nimport { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\n/** Hover-to-open delay. Long enough to suppress accidental brush-bys,\r\n * short enough that a deliberate pause feels responsive. */\r\nconst HOVER_OPEN_DELAY_MS = 500;\r\n/** Grace period after the pointer leaves before the popover closes — lets\r\n * the user move between the trigger and the popover surface without\r\n * collapsing it mid-transit. Handled by Popover's built-in mouseLeaveDelay. */\r\nconst HOVER_CLOSE_DELAY_MS = 200;\r\n\r\nconst useStyles = makeStyles({\r\n // Plain <button> rather than Fluent's `Button` — Fluent's size variants\r\n // impose a 24px+ min-height frame that overwhelms a small inline icon.\r\n // A naked button gives us a tight bounding box around just the SVG.\r\n trigger: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '12px',\r\n height: '12px',\r\n padding: 0,\r\n marginLeft: tokens.spacingHorizontalXS,\r\n verticalAlign: 'middle',\r\n border: 'none',\r\n background: 'transparent',\r\n color: tokens.colorBrandForeground1,\r\n cursor: 'pointer',\r\n borderRadius: tokens.borderRadiusSmall,\r\n lineHeight: 0,\r\n fontSize: 0,\r\n ':hover': {\r\n color: tokens.colorBrandForeground2Hover,\r\n },\r\n ':focus-visible': {\r\n outlineStyle: 'solid',\r\n outlineWidth: '1px',\r\n outlineColor: tokens.colorStrokeFocus2,\r\n outlineOffset: '1px',\r\n },\r\n },\r\n // Pin font-size on the icon — Fluent's react-icons render at `1em`, so\r\n // without this they pick up the surrounding label's text size.\r\n icon: {\r\n fontSize: '12px',\r\n width: '12px',\r\n height: '12px',\r\n display: 'block',\r\n },\r\n surface: {\r\n maxWidth: '320px',\r\n whiteSpace: 'pre-wrap',\r\n },\r\n});\r\n\r\nexport interface DescriptionIconProps {\r\n /** Description text to render in the popover surface. */\r\n description: string;\r\n /** Label of the field this icon belongs to — used to build the icon's accessible name. */\r\n ariaLabel?: string | undefined;\r\n}\r\n\r\n/**\r\n * Info-icon button that opens a {@link Popover} containing the description on\r\n * hover (after a brief delay) AND on click. Mirrors Blazor's\r\n * `BaseFluentColumnEdit.GetLabelTemplate()` paired with `<FluentTooltip>`,\r\n * which the FAST tooltip primitive opens on hover with a small delay and\r\n * keeps open on click.\r\n *\r\n * `openOnHover` is set so Popover treats this as a hover surface — that\r\n * disables its \"click-mode\" focus-out dismiss logic, which would otherwise\r\n * fire `onOpenChange({open: false})` shortly after a hover-driven open\r\n * whenever no element inside the popover happens to have focus. (Without\r\n * `openOnHover`, the popover only stayed open when the surrounding input\r\n * already had focus, a confusing surface-doesn't-stick behavior.)\r\n *\r\n * Hover-open delay is implemented in `onOpenChange` because `openOnHover`\r\n * fires the open event immediately on mouse-enter; we intercept it,\r\n * deferring the actual `setOpen(true)` by {@link HOVER_OPEN_DELAY_MS}.\r\n * Click-triggered opens skip the delay via a ref that flips on `mousedown`\r\n * (clicks should feel instant; hovers shouldn't fire on accidental\r\n * brush-bys). Close is handled by Popover's built-in `mouseLeaveDelay` so\r\n * brief detours to the popover body don't dismiss it.\r\n *\r\n * `trapFocus` is intentionally NOT set — a hover-openable surface that\r\n * seizes focus would feel jumpy as the user passes the icon. Click users\r\n * navigate the popover with normal Tab order.\r\n *\r\n * Stops click + mousedown propagation so the icon can sit safely inside\r\n * label slots that toggle their parent input on click (e.g. Checkbox/Switch\r\n * inline labels).\r\n */\r\nexport function DescriptionIcon({ description, ariaLabel }: DescriptionIconProps) {\r\n const styles = useStyles();\r\n const [open, setOpen] = useState(false);\r\n const openTimerRef = useRef<number | undefined>(undefined);\r\n // Tracks the last DOM-level interaction so the open delay only applies to\r\n // hover-triggered opens. A click should feel instant; a hover shouldn't\r\n // pop open on every accidental brush-by.\r\n const lastInteractionRef = useRef<'hover' | 'click'>('hover');\r\n\r\n const clearOpenTimer = useCallback(() => {\r\n if (openTimerRef.current !== undefined) {\r\n window.clearTimeout(openTimerRef.current);\r\n openTimerRef.current = undefined;\r\n }\r\n }, []);\r\n\r\n useEffect(() => () => clearOpenTimer(), [clearOpenTimer]);\r\n\r\n return (\r\n <span\r\n onClick={(e) => e.stopPropagation()}\r\n onMouseDown={(e) => {\r\n lastInteractionRef.current = 'click';\r\n e.stopPropagation();\r\n }}\r\n onPointerEnter={() => {\r\n lastInteractionRef.current = 'hover';\r\n }}\r\n >\r\n <Popover\r\n withArrow\r\n openOnHover\r\n mouseLeaveDelay={HOVER_CLOSE_DELAY_MS}\r\n open={open}\r\n onOpenChange={(_, data) => {\r\n if (data.open === false) {\r\n clearOpenTimer();\r\n setOpen(false);\r\n return;\r\n }\r\n // Open requested by Popover.\r\n if (lastInteractionRef.current === 'click') {\r\n // Click-toggle — instantaneous.\r\n clearOpenTimer();\r\n setOpen(true);\r\n return;\r\n }\r\n // Hover-triggered — apply the enter delay.\r\n if (openTimerRef.current !== undefined) return;\r\n openTimerRef.current = window.setTimeout(() => {\r\n openTimerRef.current = undefined;\r\n setOpen(true);\r\n }, HOVER_OPEN_DELAY_MS);\r\n }}\r\n >\r\n <PopoverTrigger disableButtonEnhancement>\r\n <button\r\n type=\"button\"\r\n // `tabIndex={-1}` removes the icon from the keyboard tab order.\r\n // The popover is purely informational — the description is also\r\n // exposed as a tooltip on hover and read by screen readers via\r\n // the parent input's `aria-describedby` / metadata — so keyboard\r\n // users don't need to tab through every label's info button.\r\n // Matches Blazor's <FluentTooltip Anchor=…>, which has no\r\n // intrinsic tab stop.\r\n tabIndex={-1}\r\n className={styles.trigger}\r\n aria-label={ariaLabel ? `Show description for ${ariaLabel}` : 'Show description'}\r\n >\r\n <Info12Regular className={styles.icon} />\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverSurface className={styles.surface}>\r\n {description}\r\n </PopoverSurface>\r\n </Popover>\r\n </span>\r\n );\r\n}\r\n","import { RequiredLevel, TableSecurityPermission, type components } from '@powerportalspro/core';\nimport type { Localizer } from '@powerportalspro/react';\n\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\ntype TableRecord = components['schemas']['TableRecord'];\n\n/**\n * Helpers that derive UI props from the parent RecordContext's table metadata.\n * Mirror Blazor's `BaseColumnEdit.OnParametersSetAsync` defaults — required is\n * driven by `RequiredLevel`, readOnly is driven by `isValidForCreate` /\n * `isValidForUpdate` based on whether the record is new or existing, and\n * type-specific props (maxLength, min/max, precision) come from the matching\n * metadata subtype's fields. Edit components apply each only when the\n * consumer hasn't passed an explicit prop value.\n */\n\n/**\n * True when the column is `SystemRequired` or `ApplicationRequired`. Returns\n * false for `None` and `Recommended` levels, and for missing metadata.\n */\nexport function isMetadataRequired(metadata: ColumnMetadataBase | undefined): boolean {\n if (!metadata) return false;\n return (\n metadata.requiredLevel === RequiredLevel.SystemRequired ||\n metadata.requiredLevel === RequiredLevel.ApplicationRequired\n );\n}\n\n/**\n * Resolves whether the column should render read-only given the record's\n * create/update lifecycle:\n * - For a new record (Guid.Empty / null id): the inverse of `isValidForCreate`.\n * - For an existing record: the inverse of `isValidForUpdate`.\n *\n * Returns false when no metadata is loaded yet — best-effort permissive\n * default; the consumer can still pass an explicit `readOnly` prop.\n *\n * **File and Image columns are exempt** from the IsValidFor* check.\n * Dataverse marks `FileMetadata` / `ImageMetadata` columns as\n * <c>isValidForUpdate=false</c> because they aren't writable via the\n * standard Update path — file content rides through a separate\n * `SetFileField` round-trip. The framework's update endpoint handles\n * that translation server-side, so from the consumer's perspective these\n * columns ARE editable. Mirrors Blazor's\n * `BaseColumnEdit.OnParametersSetAsync` exclusion of FileMetadata /\n * ImageMetadata when computing `_readOnlyInternal`.\n */\nexport function isMetadataReadOnly(\n metadata: ColumnMetadataBase | undefined,\n isNewRecord: boolean,\n): boolean {\n if (!metadata) return false;\n // $type 41 = FileMetadata, $type 42 = ImageMetadata. The discriminator\n // is optional in the generated schema even though it's always present\n // at runtime; the structural narrow handles both shapes safely.\n const typeCode = (metadata as { $type?: number }).$type;\n if (typeCode === 41 || typeCode === 42) return false;\n return isNewRecord ? !metadata.isValidForCreate : !metadata.isValidForUpdate;\n}\n\n/**\n * Whether a record is un-persisted (client-side only, never seen by the\n * server). Treats three signals as \"new\":\n * - missing / null id;\n * - Guid.Empty (`00000000-0000-0000-0000-000000000000`) id (the wire's\n * legacy \"new record\" marker);\n * - non-empty id but no `permissions` stamped. RecordContext create-mode\n * pre-generates a GUID so descendants like `<ManyToManyLookupEdit>`\n * can target a stable parent id before save; that defeats the id-only\n * heuristic but the missing-`permissions` marker still works because\n * the server stamps permissions on every record it returns.\n */\nexport function isNewRecord(\n record: { id?: string | null; permissions?: number } | undefined,\n): boolean {\n if (!record) return true;\n if (!record.id || record.id === '00000000-0000-0000-0000-000000000000') return true;\n return record.permissions === undefined;\n}\n\n/**\n * True when the record exists, is already persisted (non-empty id), and the\n * server-reported per-record `permissions` bitmask doesn't include the\n * `Write` bit. Mirrors the final permission check in Blazor's\n * `BaseColumnEdit.OnParametersSetAsync` that forces controls to readOnly +\n * disabled when the current user can't write this specific record (the\n * server has already evaluated `ITableRecordPermissionHandler.CanUpdateAsync`\n * for the loaded record and stamped the result on `TableRecord.Permissions`).\n *\n * Returns `false` for new (unsaved) records — Create-permission gating\n * belongs at the table level via the user's permissions cache, which is\n * deferred on the React side until that cache surface exists.\n */\nexport function isRecordWriteDenied(\n record: TableRecord | undefined,\n): boolean {\n if (!record) return false;\n if (isNewRecord(record)) return false;\n const perms = record.permissions ?? 0;\n return (perms & TableSecurityPermission.Write) === 0;\n}\n\n/** Resolved readOnly / disabled state from {@link resolveEditState}. */\nexport interface ResolvedEditState {\n readOnly: boolean;\n disabled: boolean;\n}\n\n/**\n * Resolves an edit component's final readOnly / disabled state from three\n * input layers: consumer props, column metadata, and the per-record\n * permissions reported by the server. Mirrors Blazor's\n * `BaseColumnEdit.OnParametersSetAsync` ordering — metadata's\n * `isValidForCreate` / `isValidForUpdate` set the baseline, then a per-\n * record Write-permission denial forces BOTH flags to true regardless of\n * what the consumer passed. \"Permission denies always win\" — a consumer\n * `readOnly={false}` does NOT override the lock.\n *\n * File / Image columns are exempt from the metadata readOnly check (their\n * `isValidForUpdate=false` is a wire-protocol detail, not a UI signal) but\n * NOT from the permission check; a Write-denied record's file fields are\n * still locked.\n */\nexport function resolveEditState(\n metadata: ColumnMetadataBase | undefined,\n record: TableRecord | undefined,\n explicitReadOnly: boolean | undefined,\n explicitDisabled: boolean | undefined,\n): ResolvedEditState {\n if (isRecordWriteDenied(record)) {\n return { readOnly: true, disabled: true };\n }\n return {\n readOnly: explicitReadOnly ?? isMetadataReadOnly(metadata, isNewRecord(record)),\n disabled: explicitDisabled ?? false,\n };\n}\n\n/**\n * Resolves the display label for a column via the active localizer, using the\n * key `tables.{tableName}.columns.{columnName}.label`. Mirrors Blazor's\n * `BaseColumnEdit` which does the identical lookup via `IStringLocalizer`.\n *\n * The cached metadata's `displayName` is NOT the source of truth — Dataverse\n * resolves it server-side using the SDK user's locale (typically English for\n * the application user), so it doesn't change when the request culture\n * switches. The localizer cache (populated per-LCID from Dataverse\n * `LocalizedLabels` at startup) is the per-culture source. On a localizer\n * miss the key itself is returned, matching Blazor's behavior and the\n * \"show keys, not a fallback language\" rule consumers rely on to spot\n * un-translated strings during development.\n *\n * The `metadata?.displayName || columnName` fallback only applies when no\n * `tableName` is available (edge case during metadata load without a record\n * context attached).\n */\nexport function getDisplayLabel(\n metadata: ColumnMetadataBase | undefined,\n columnName: string,\n tableName: string | undefined,\n t: Localizer['t'],\n): string {\n if (tableName) {\n return t(`tables.${tableName}.columns.${columnName}.label`);\n }\n return metadata?.displayName || columnName;\n}\n\n/**\n * Resolves the description (hint text) for a column via the active localizer\n * using the key `tables.{tableName}.columns.{columnName}.description`.\n * Returns undefined on a localizer miss (no info-icon tooltip rendered)\n * rather than the key string — a tooltip showing\n * `tables.foo.columns.bar.description` is noise, not signal, and a missing\n * description is a legitimate state.\n *\n * Falls back to `metadata?.description` only when no `tableName` is available.\n */\nexport function getDisplayDescription(\n metadata: ColumnMetadataBase | undefined,\n columnName: string,\n tableName: string | undefined,\n t: Localizer['t'],\n): string | undefined {\n if (tableName) {\n const key = `tables.${tableName}.columns.${columnName}.description`;\n const resolved = t(key);\n return resolved !== key ? resolved : undefined;\n }\n return metadata?.description ?? undefined;\n}\n\n/**\n * Extracts `maxLength` from `StringMetadata` ($type 14). Returns undefined for\n * other metadata variants, when the field is null (unlimited), or when the\n * metadata isn't loaded yet.\n *\n * Uses structural property access (instead of `$type` narrowing) because\n * `$type` is marked optional in the generated schema even though it's always\n * present at runtime — the structural check is robust to that.\n */\nexport function getStringMaxLength(metadata: ColumnMetadataBase | undefined): number | undefined {\n if (!metadata) return undefined;\n const m = metadata as { maxLength?: number | string | null };\n if (m.maxLength === undefined || m.maxLength === null) return undefined;\n return Number(m.maxLength);\n}\n\n/**\n * Plain shape of a choice option, with the wire's `number | string` value\n * collapsed to `number`. `displayName` is the metadata's already-localized\n * label — used as the final fallback when no localizer override exists for\n * the column / choice / value triple.\n */\nexport interface ChoiceOption {\n value: number;\n displayName: string;\n}\n\n/**\n * Choice metadata view normalized for ChoiceEdit consumption: the choice's\n * logical name (used to build the `choices.{name}.values.{v}.label`\n * localization keys) plus the available options.\n */\nexport interface ChoiceMetadataView {\n choiceLogicalName: string;\n isGlobal: boolean;\n choices: ChoiceOption[];\n}\n\n/**\n * Extracts the choice options + logical-name from `ChoiceMetadata` ($type 11)\n * or `MultiSelectChoiceMetadata` ($type 40). Returns undefined for other\n * variants or when metadata isn't loaded.\n */\nexport function getChoiceMetadata(metadata: ColumnMetadataBase | undefined): ChoiceMetadataView | undefined {\n if (!metadata) return undefined;\n const m = metadata as {\n $type?: number;\n choiceLogicalName?: string;\n isGlobal?: boolean;\n choices?: { value: number | string; displayName: string }[];\n };\n if (m.$type !== 11 && m.$type !== 40) return undefined;\n if (m.choiceLogicalName === undefined || m.choices === undefined) return undefined;\n return {\n choiceLogicalName: m.choiceLogicalName,\n isGlobal: m.isGlobal ?? false,\n choices: m.choices.map((c) => ({\n value: Number(c.value),\n displayName: c.displayName,\n })),\n };\n}\n\n/**\n * Lookup metadata view normalized for LookupEdit consumption — the list of\n * target tables this lookup can reference. Polymorphic lookups (customer,\n * regarding, etc.) carry multiple entries; standard single-target lookups\n * carry one.\n */\nexport interface LookupMetadataView {\n targetTables: string[];\n}\n\n/**\n * Extracts the target-table list from `LookupMetadata` ($type 6). Returns\n * undefined for other variants or when metadata isn't loaded.\n */\nexport function getLookupMetadata(metadata: ColumnMetadataBase | undefined): LookupMetadataView | undefined {\n if (!metadata) return undefined;\n const m = metadata as { $type?: number; targetTables?: string[] };\n if (m.$type !== 6) return undefined;\n if (!m.targetTables || m.targetTables.length === 0) return undefined;\n return { targetTables: m.targetTables };\n}\n\n/** Numeric limits extracted from Int / BigInt / Decimal / Double / Money metadata. */\nexport interface NumberLimits {\n min?: number;\n max?: number;\n precision?: number;\n}\n\n/**\n * Extracts `min`, `max`, and `precision` from any metadata variant that has\n * them. Int (5) and BigInt (18) have just min/max; Decimal (3), Double (4),\n * and Money (8) also have precision. Other variants return all-undefined.\n */\nexport function getNumberLimits(metadata: ColumnMetadataBase | undefined): NumberLimits {\n if (!metadata) return {};\n const m = metadata as {\n min?: number | string;\n max?: number | string;\n precision?: number | string;\n };\n const result: NumberLimits = {};\n if (m.min !== undefined) result.min = Number(m.min);\n if (m.max !== undefined) result.max = Number(m.max);\n if (m.precision !== undefined) result.precision = Number(m.precision);\n return result;\n}\n\n/**\n * Numeric value-type discriminator NumberEdit binds to. Mirrors\n * `NumberValueType` in `use-column-edit.ts` — repeated here as a type\n * literal so this module stays decoupled from the column-edit hook\n * surface.\n */\nexport type ResolvedNumberValueType = 'int' | 'decimal' | 'double' | 'bigint';\n\n/**\n * Maps a column's metadata `$type` discriminator to the corresponding\n * {@link ResolvedNumberValueType}. Mirrors Blazor's `<NumberEdit>` which\n * walks `ColumnMetadata is BigIntMetadata`/`DecimalMetadata`/etc. and\n * picks the right `BaseNumberEdit<TNumber>` (NumberEdit.razor:7-82).\n *\n * Returns `undefined` for non-numeric variants or when metadata hasn't\n * loaded yet — callers fall back to their own default (typically\n * `'int'`) so the editor still mounts when metadata is in-flight.\n *\n * `$type` values come from `ColumnType.cs`:\n * - 3 → Decimal\n * - 4 → Double\n * - 5 → Int\n * - 18 → BigInt\n *\n * Money (8) and other types intentionally return `undefined` —\n * Money has a dedicated {@link MoneyEdit} component, and other types\n * (string/bool/date/etc.) shouldn't be bound to NumberEdit at all.\n */\nexport function resolveNumberValueType(\n metadata: ColumnMetadataBase | undefined,\n): ResolvedNumberValueType | undefined {\n const t = (metadata as { $type?: number } | undefined)?.$type;\n switch (t) {\n case 3:\n return 'decimal';\n case 4:\n return 'double';\n case 5:\n return 'int';\n case 18:\n return 'bigint';\n default:\n return undefined;\n }\n}\n","import { ErrorCircle12Filled } from '@fluentui/react-icons';\r\n\r\n/**\r\n * Builds the validation-related props (`validationState` + `validationMessage`\r\n * + `validationMessageIcon`) for a Fluent `<Field>` based on the column's\r\n * current error list. Returns an empty object when there's nothing to surface,\r\n * so the caller can spread the result unconditionally.\r\n *\r\n * Centralizes the choice of error icon — `ErrorCircle12Filled`. Sized to match\r\n * Field's 12px validation-message text rather than Fluent's default 16px icon,\r\n * which reads as oversized next to the small message text. The larger 20px\r\n * variant is used in `<ValidationSummary>` where the MessageBar's body text is\r\n * full-size.\r\n */\r\nexport function buildFieldValidationProps(\r\n errors: readonly string[] | undefined,\r\n displayValidationErrorMessage: boolean,\r\n) {\r\n const firstError =\r\n displayValidationErrorMessage && errors && errors.length > 0 ? errors[0] : undefined;\r\n if (!firstError) return {};\r\n return {\r\n validationState: 'error' as const,\r\n validationMessage: firstError,\r\n validationMessageIcon: <ErrorCircle12Filled />,\r\n };\r\n}\r\n","import {\r\n useColumnValidationErrors,\r\n useT,\r\n useValidationContext,\r\n type ColumnValidator,\r\n type Localizer,\r\n} from '@powerportalspro/react';\r\nimport { useEffect, useRef } from 'react';\r\n\r\n/**\r\n * Common shape for every column-type validation state — required flag and\r\n * the display label used in error messages. Type-specific subtypes\r\n * ({@link StringColumnValidationState}, {@link NumberColumnValidationState},\r\n * etc.) extend this with the bounds relevant to their column type.\r\n */\r\nexport interface BaseColumnValidationState {\r\n /** Effective `required` flag (consumer prop or metadata-resolved). */\r\n required: boolean;\r\n /** Effective display label (used in error messages). */\r\n label: string | undefined;\r\n}\r\n\r\n/** Validation state for string-typed columns (text, memo). */\r\nexport interface StringColumnValidationState extends BaseColumnValidationState {\r\n readonly kind: 'string';\r\n value: string | null | undefined;\r\n /** Minimum allowed character count. */\r\n minLength?: number;\r\n /** Maximum allowed character count. */\r\n maxLength?: number;\r\n}\r\n\r\n/** Validation state for numeric-typed columns (int, decimal, double, money, bigint). */\r\nexport interface NumberColumnValidationState extends BaseColumnValidationState {\r\n readonly kind: 'number';\r\n value: number | null | undefined;\r\n /** Minimum allowed value. */\r\n min?: number;\r\n /** Maximum allowed value. */\r\n max?: number;\r\n}\r\n\r\n/** Validation state for boolean-typed columns. Required is the only built-in check. */\r\nexport interface BoolColumnValidationState extends BaseColumnValidationState {\r\n readonly kind: 'bool';\r\n value: boolean | null | undefined;\r\n}\r\n\r\n/** Validation state for array-typed columns (multi-select choice, many-to-many). */\r\nexport interface ArrayColumnValidationState extends BaseColumnValidationState {\r\n readonly kind: 'array';\r\n value: readonly unknown[] | null | undefined;\r\n /** Minimum allowed array length. */\r\n minCount?: number;\r\n /** Maximum allowed array length. */\r\n maxCount?: number;\r\n}\r\n\r\n/** Validation state for date / datetime columns. */\r\nexport interface DateTimeColumnValidationState extends BaseColumnValidationState {\r\n readonly kind: 'datetime';\r\n value: Date | string | null | undefined;\r\n}\r\n\r\n/**\r\n * Discriminated union of every column-type validation state. The `kind`\r\n * discriminator lets the built-in validator narrow to the right type-specific\r\n * shape without casts. New column types add a member here when they need\r\n * type-specific bounds beyond required.\r\n */\r\nexport type ColumnValidationState =\r\n | StringColumnValidationState\r\n | NumberColumnValidationState\r\n | BoolColumnValidationState\r\n | ArrayColumnValidationState\r\n | DateTimeColumnValidationState;\r\n\r\n/** @deprecated Renamed to {@link ColumnValidationState}. */\r\nexport type RequiredValidatorState = ColumnValidationState;\r\n\r\n/**\r\n * Built-in column validators — required-level check + type-specific bounds\r\n * (string length, numeric range, array count). Mirrors Blazor's\r\n * `BaseColumnEdit.GetValidationErrors` chain. Returns a list of error\r\n * messages; empty list means valid.\r\n *\r\n * Required treats `null`, `undefined`, empty string, and empty array as\r\n * \"no value\". Other falsy values (`0`, `false`) are valid — those are real\r\n * values for number / bool columns. Mirrors Blazor's check that explicitly\r\n * excludes non-string falsy values.\r\n *\r\n * Bounds checks only fire when the corresponding bound is provided AND the\r\n * value is the appropriate type. A `min: 5` on a string column is a no-op\r\n * (the value is a string, not a number). Localization keys:\r\n *\r\n * - `app.messages.column-required` — required failure\r\n * - `app.messages.column-min-length` / `app.messages.column-max-length` — string bounds\r\n * - `app.messages.column-min-value` / `app.messages.column-max-value` — numeric bounds\r\n * - `app.errors.value-min-count` / `app.errors.value-max-count` — array bounds\r\n */\r\nfunction defaultBuiltInErrors(state: ColumnValidationState, localizer: Localizer): string[] {\r\n const errors: string[] = [];\r\n const labelText = state.label ?? '';\r\n\r\n // Required check is universal across every column type. Empty semantics\r\n // depend on the value type (empty string, empty array, null/undefined),\r\n // so we inspect the value rather than the discriminator.\r\n if (state.required) {\r\n const v = state.value;\r\n const isEmpty =\r\n v === null ||\r\n v === undefined ||\r\n (typeof v === 'string' && v.trim() === '') ||\r\n (Array.isArray(v) && v.length === 0);\r\n if (isEmpty) {\r\n errors.push(localizer.t('app.messages.column-required', [labelText]));\r\n // When a required field is empty, there's no value to bounds-check\r\n // further. Return now so the error list stays focused on the primary\r\n // issue rather than piling on length / range messages too.\r\n return errors;\r\n }\r\n }\r\n\r\n // Type-specific bounds — TS narrows `state` to the right subtype inside\r\n // each branch via the `kind` discriminator.\r\n switch (state.kind) {\r\n case 'string': {\r\n const v = state.value;\r\n if (typeof v !== 'string') break;\r\n if (state.maxLength !== undefined && v.length > state.maxLength) {\r\n errors.push(localizer.t('app.messages.column-max-length', [state.maxLength]));\r\n }\r\n if (state.minLength !== undefined && v.length < state.minLength) {\r\n errors.push(localizer.t('app.messages.column-min-length', [state.minLength]));\r\n }\r\n break;\r\n }\r\n case 'number': {\r\n const v = state.value;\r\n if (typeof v !== 'number' || !Number.isFinite(v)) break;\r\n if (state.max !== undefined && v > state.max) {\r\n errors.push(localizer.t('app.messages.column-max-value', [state.max]));\r\n }\r\n if (state.min !== undefined && v < state.min) {\r\n errors.push(localizer.t('app.messages.column-min-value', [state.min]));\r\n }\r\n break;\r\n }\r\n case 'array': {\r\n const v = state.value;\r\n if (!Array.isArray(v)) break;\r\n if (state.maxCount !== undefined && v.length > state.maxCount) {\r\n errors.push(localizer.t('app.errors.value-max-count', [state.maxCount]));\r\n }\r\n if (state.minCount !== undefined && v.length < state.minCount) {\r\n errors.push(localizer.t('app.errors.value-min-count', [state.minCount]));\r\n }\r\n break;\r\n }\r\n case 'bool':\r\n case 'datetime':\r\n // Required is the only built-in check for these (handled above).\r\n break;\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n/**\r\n * Wires an edit component into the surrounding {@link ValidationContext}:\r\n *\r\n * - Registers a column validator that reads the latest required/label/value\r\n * via refs (so the validator is stable across renders but always reads\r\n * current state when called from outside the render cycle, e.g. from\r\n * `validateAll()` during save).\r\n * - Re-validates on state transitions, but only after the column has been\r\n * \"touched\" — i.e. the value has actually changed from its initial state\r\n * at least once. Before that, prop / bound / metadata-resolution changes\r\n * (e.g. async metadata flipping `required` from false to true on a fresh\r\n * form, or a consumer toggling `required` from a UI control) are silent.\r\n * Mirrors Blazor's `EditContext` \"modified field\" semantics. `validateAll()`\r\n * (called from a form Save) still runs every registered validator\r\n * unconditionally, so submit-time validation surfaces errors on\r\n * never-touched fields the way the user expects.\r\n * - Returns the current errors for the column, sourced from the context.\r\n *\r\n * Returns `undefined` for `errors` when no `<ValidationProvider>` is mounted —\r\n * the consuming edit component should treat this as \"validation isn't\r\n * active in this scope\" and skip rendering errors.\r\n */\r\nexport function useColumnValidation(\r\n columnName: string,\r\n state: ColumnValidationState,\r\n customValidator?: ColumnValidator,\r\n): readonly string[] | undefined {\r\n const validation = useValidationContext();\r\n const errors = useColumnValidationErrors(columnName);\r\n const t = useT();\r\n\r\n // Latest state in refs so the registered validator (called from outside\r\n // this render) always sees the current value.\r\n const stateRef = useRef(state);\r\n stateRef.current = state;\r\n const customValidatorRef = useRef(customValidator);\r\n customValidatorRef.current = customValidator;\r\n // Localizer also via ref — its identity changes when the JSON loads, but\r\n // the registered validator should see the latest version regardless.\r\n const localizerRef = useRef<Localizer>({ t });\r\n localizerRef.current = { t };\r\n // Pin the validation context in a ref so the effects below don't depend on\r\n // its identity. Without this, the registration effect re-runs every time\r\n // ANY column's errors change (because the context value re-memoizes on\r\n // `errors`), and its cleanup `unregister` deletes the column's errors,\r\n // which mutates state, which re-changes the context identity — infinite\r\n // loop. ValidationProvider's `register`/`validateColumn`/etc. functions\r\n // are themselves stable (useCallback []), so reading via ref is safe even\r\n // if the wrapping object's identity changes.\r\n const validationRef = useRef(validation);\r\n validationRef.current = validation;\r\n\r\n // Stable validator wrapper — re-registration only happens on column\r\n // changes, not on every keystroke.\r\n useEffect(() => {\r\n const v = validationRef.current;\r\n if (!v) return;\r\n const validator: ColumnValidator = async () => {\r\n const current = stateRef.current;\r\n const errs = defaultBuiltInErrors(current, localizerRef.current);\r\n if (customValidatorRef.current) {\r\n const custom = await customValidatorRef.current();\r\n return [...errs, ...custom];\r\n }\r\n return errs;\r\n };\r\n return v.register(columnName, validator);\r\n }, [columnName]);\r\n\r\n // Touched-gated re-validation. `touchedRef` flips the first time the value\r\n // diverges from its initial state (JSON-compared so array/object value\r\n // shapes work without an identity gotcha). Once touched, every subsequent\r\n // state change re-validates — so adjusting bounds after the user has\r\n // started editing re-evaluates with the new rules. Before touched, the\r\n // effect is a no-op for inline display; `validateAll()` (from Save) still\r\n // runs the registered validator and populates errors directly.\r\n const touchedRef = useRef(false);\r\n const lastValueJsonRef = useRef<string>(JSON.stringify(state.value));\r\n useEffect(() => {\r\n const v = validationRef.current;\r\n if (!v) return;\r\n\r\n const currentValueJson = JSON.stringify(state.value);\r\n if (currentValueJson !== lastValueJsonRef.current) {\r\n lastValueJsonRef.current = currentValueJson;\r\n touchedRef.current = true;\r\n }\r\n if (!touchedRef.current) return;\r\n\r\n void v.validateColumn(columnName);\r\n // Dep on the full stringified state so bounds / required changes after\r\n // touch DO re-validate. The touched gate above keeps pre-touch noise\r\n // (metadata async-resolve, parent prop tweaks) from surfacing errors.\r\n }, [columnName, JSON.stringify(state)]);\r\n\r\n return errors;\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\nimport { useRecordContext } from '@powerportalspro/react';\r\nimport { useCallback } from 'react';\r\n\r\ntype Schemas = components['schemas'];\r\ntype ColumnValueBase = Schemas['ColumnValueBase'];\r\n\r\n/**\r\n * Wraps a per-column `setValue` with a defense-in-depth gate that drops\r\n * the write when the edit component is in readOnly or disabled state.\r\n *\r\n * The underlying Fluent input components honor the `readOnly` / `disabled`\r\n * HTML attributes for the steady-state UI, but those are pure UX hints —\r\n * a user with browser dev tools can strip the attributes, type a value,\r\n * and the input's `onChange` will happily fire, propagating through\r\n * `setValue` → `setColumnValue` → marked-dirty → the save flow ships the\r\n * change. The server's Write-permission check at the table / record level\r\n * may not catch this when the user legitimately has Write rights but the\r\n * consumer chose to lock a specific column for a different reason\r\n * (computed, audited, \"set once\" semantics, etc.).\r\n *\r\n * Routing every `setValue` callsite through this guard means the\r\n * record-level dirty state matches the visible UI state — no hidden\r\n * column mutations land in the record, no PATCHes for \"locked\" columns\r\n * hit the server. Mirrors the spirit of Blazor's `BaseColumnEdit` which\r\n * routes value writes through a single property setter that respects the\r\n * computed readOnly / disabled flags.\r\n */\r\nexport function useGuardedSetValue<TArg>(\r\n rawSetValue: (next: TArg) => void,\r\n readOnly: boolean,\r\n disabled: boolean,\r\n): (next: TArg) => void {\r\n return useCallback(\r\n (next: TArg) => {\r\n if (readOnly || disabled) return;\r\n rawSetValue(next);\r\n },\r\n [rawSetValue, readOnly, disabled],\r\n );\r\n}\r\n\r\n/**\r\n * Per-type column hooks. Each one knows its `$type` discriminator and converts\r\n * between the on-the-wire `ColumnValueBase{Subtype}` shape and a JS-friendly\r\n * primitive (`string`, `boolean`, `number`).\r\n *\r\n * The full set will grow as edit components land — this initial batch covers\r\n * what TextEdit / MemoEdit / BoolEdit / NumberEdit / MoneyEdit need.\r\n *\r\n * `setValue(undefined | null)` clears the column from `record.properties`,\r\n * which is the correct way to send \"this field has no value\" to Dataverse —\r\n * different from sending an empty string or `0`.\r\n *\r\n * Returns `value: undefined` when the record hasn't loaded yet OR when the\r\n * column isn't on the record. The edit components fall back to an empty\r\n * input in that case.\r\n */\r\n\r\n/** Reads/writes a single-line or multi-line string column (StringValue, $type 14). */\r\nexport function useStringColumn(columnName: string): {\r\n value: string | undefined;\r\n setValue: (value: string | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseStringValue'] | undefined;\r\n // Wire schema is `value?: null | string` (C# `string?`); collapse null → undefined for consumers.\r\n const value = raw?.value ?? undefined;\r\n\r\n const setValue = useCallback(\r\n (next: string | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, { $type: 14, value: next });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/** Reads/writes a boolean column (BoolValue, $type 0). */\r\nexport function useBoolColumn(columnName: string): {\r\n value: boolean | undefined;\r\n setValue: (value: boolean | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseBoolValue'] | undefined;\r\n const value = raw?.value ?? undefined;\r\n\r\n const setValue = useCallback(\r\n (next: boolean | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, { $type: 0, value: next });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/** Numeric value flavors that NumberEdit can bind to. Each maps to a different ColumnValueBase subtype. */\r\nexport type NumberValueType = 'int' | 'decimal' | 'double' | 'bigint';\r\n\r\n/**\r\n * Reads/writes a numeric column. The `valueType` discriminator picks which\r\n * `ColumnValueBase` subtype to write back so the server-side deserializer\r\n * binds to the right Dataverse field type:\r\n *\r\n * - `int` → IntValue, $type 5\r\n * - `decimal` → DecimalValue, $type 3\r\n * - `double` → DoubleValue, $type 4\r\n * - `bigint` → BigIntValue, $type 18\r\n *\r\n * For very large `bigint` values exceeding `Number.MAX_SAFE_INTEGER`, the\r\n * generated wire schema accepts `number | string`. This v0 always sends\r\n * numbers — values that exceed the safe integer range will lose precision.\r\n * If/when that becomes a problem the hook can switch to string-typed bigint.\r\n */\r\nexport function useNumberColumn(\r\n columnName: string,\r\n valueType: NumberValueType = 'int',\r\n): {\r\n value: number | undefined;\r\n setValue: (value: number | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as\r\n | Schemas['ColumnValueBaseIntValue']\r\n | Schemas['ColumnValueBaseDecimalValue']\r\n | Schemas['ColumnValueBaseDoubleValue']\r\n | Schemas['ColumnValueBaseBigIntValue']\r\n | undefined;\r\n // The wire encodes large numbers as string for safety, and nullable values come through\r\n // as `null`. Coerce both null and undefined to undefined; pass numbers/strings through Number().\r\n const rawValue = raw?.value;\r\n const value = rawValue === undefined || rawValue === null ? undefined : Number(rawValue);\r\n\r\n const setValue = useCallback(\r\n (next: number | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n return;\r\n }\r\n const wireValue: ColumnValueBase =\r\n valueType === 'int'\r\n ? { $type: 5, value: next }\r\n : valueType === 'decimal'\r\n ? { $type: 3, value: next }\r\n : valueType === 'double'\r\n ? { $type: 4, value: next }\r\n : { $type: 18, value: next };\r\n ctx.setColumnValue(columnName, wireValue);\r\n },\r\n [columnName, ctx, valueType],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/**\r\n * Reads/writes a single-select choice column (ChoiceValue, $type 11). The\r\n * wire format encodes the option as an integer; the hook surfaces it as a\r\n * `number` and writes back the same shape. `null`/`undefined` clear the\r\n * column.\r\n *\r\n * The wire schema types `value` as `number | string` (Int32, encoded as a\r\n * string for very large values to dodge JS-precision loss). Choice option\r\n * values are well within the safe integer range; coerce to `number` here so\r\n * consumers always work with a primitive.\r\n */\r\nexport function useChoiceColumn(columnName: string): {\r\n value: number | undefined;\r\n setValue: (value: number | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseChoiceValue'] | undefined;\r\n const rawValue = raw?.value;\r\n const value = rawValue === undefined || rawValue === null ? undefined : Number(rawValue);\r\n\r\n const setValue = useCallback(\r\n (next: number | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, { $type: 11, value: next });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/**\r\n * Reads/writes a multi-select choice column (MultiSelectChoiceValue, $type 40).\r\n * The wire value is an array of inner `ChoiceValue` records (no `$type`\r\n * discriminator on the elements — the array's element type is concrete, so\r\n * polymorphism isn't engaged). The hook surfaces a plain `number[]` (sorted\r\n * by selection order) and reconstitutes the wire shape on write.\r\n *\r\n * `setValue(undefined | null | [])` all clear the column. Sending an empty\r\n * array would write `MultiSelectChoiceValue { value: [] }`, which the\r\n * server's `Update` path treats the same as clearing the column anyway —\r\n * collapsing to `null` here keeps the wire payload smaller and matches\r\n * Blazor's `BaseMultiSelectChoiceEdit.Value` setter.\r\n */\r\nexport function useMultiSelectChoiceColumn(columnName: string): {\r\n value: number[];\r\n setValue: (value: readonly number[] | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as\r\n | Schemas['ColumnValueBaseMultiSelectChoiceValue']\r\n | undefined;\r\n const value: number[] = (raw?.value ?? []).map((c) => Number(c.value));\r\n\r\n const setValue = useCallback(\r\n (next: readonly number[] | null | undefined) => {\r\n if (next === null || next === undefined || next.length === 0) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, {\r\n $type: 40,\r\n value: next.map((v) => ({ value: v })),\r\n });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/**\r\n * Consumer-facing shape of a lookup column value — the picked record's\r\n * id + display name + the table it belongs to. Polymorphic lookups (one\r\n * column that can reference different tables) surface the picked table\r\n * via `tableName`; v0 of LookupEdit only supports the first metadata-\r\n * declared target table, but the wire format already carries it.\r\n */\r\nexport interface LookupColumnValue {\r\n /** The referenced record's Guid. */\r\n id: string;\r\n /** The referenced record's primary-name value, when available. */\r\n name: string | null;\r\n /** Logical name of the referenced table. */\r\n tableName: string;\r\n}\r\n\r\n/**\r\n * Reads/writes a lookup (entity reference) column (LookupValue, $type 6).\r\n * The wire shape is `{ value: <guid>, name?: <display>, tableName: <table> }`\r\n * — collapsed here to a JS-friendly `LookupColumnValue` with `id` renamed\r\n * from `value` for symmetry with the other column hooks.\r\n *\r\n * `setValue(null | undefined)` clears the column. The empty-guid (\r\n * `00000000-...`) is treated as \"no value\" on read — Dataverse occasionally\r\n * emits it for cleared lookups.\r\n */\r\nexport function useLookupColumn(columnName: string): {\r\n value: LookupColumnValue | undefined;\r\n setValue: (value: LookupColumnValue | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseLookupValue'] | undefined;\r\n\r\n let value: LookupColumnValue | undefined;\r\n if (raw?.value && raw.value !== '00000000-0000-0000-0000-000000000000') {\r\n value = {\r\n id: raw.value,\r\n name: raw.name ?? null,\r\n tableName: raw.tableName,\r\n };\r\n }\r\n\r\n const setValue = useCallback(\r\n (next: LookupColumnValue | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, {\r\n $type: 6,\r\n value: next.id,\r\n name: next.name,\r\n tableName: next.tableName,\r\n });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/** Reads/writes a currency (MoneyValue, $type 8). Decimal-typed under the hood. */\r\nexport function useMoneyColumn(columnName: string): {\r\n value: number | undefined;\r\n setValue: (value: number | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseMoneyValue'] | undefined;\r\n const rawValue = raw?.value;\r\n const value = rawValue === undefined || rawValue === null ? undefined : Number(rawValue);\r\n\r\n const setValue = useCallback(\r\n (next: number | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, { $type: 8, value: next });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/**\r\n * Reads/writes a date/time column (DateValue, $type 2). The wire format is\r\n * an ISO 8601 string (`2025-08-13T14:30:00.000Z`); this hook surfaces it\r\n * verbatim. Components convert between the ISO string and the local-time\r\n * shape the HTML `<input type=\"date|datetime-local|time\">` expects.\r\n *\r\n * Returns `value: undefined` when the record hasn't loaded or the column\r\n * isn't on the record.\r\n */\r\nexport function useDateColumn(columnName: string): {\r\n value: string | undefined;\r\n setValue: (value: string | null | undefined) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const raw = ctx.record?.properties?.[columnName] as Schemas['ColumnValueBaseDateValue'] | undefined;\r\n const value = raw?.value ?? undefined;\r\n\r\n const setValue = useCallback(\r\n (next: string | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n ctx.setColumnValue(columnName, { $type: 2, value: next });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue };\r\n}\r\n\r\n/**\r\n * Consumer-facing shape of a file/image column value. Derived from the\r\n * OpenAPI-generated <c>ColumnValueBaseFileValue</c> record (which mirrors\r\n * the C# <c>FileValue</c> model) with the <c>$type</c> discriminator\r\n * stripped — <see cref=\"useFileColumn\"/> sets that automatically when\r\n * writing back to the wire. Carries the original filename, the assigned\r\n * Dataverse file id, and (optionally) the base64-encoded binary content.\r\n */\r\nexport type FileColumnValue = Omit<Schemas['ColumnValueBaseFileValue'], '$type'>;\r\n\r\n/**\r\n * Reads/writes a file or image column. Both <c>FileEdit</c> and\r\n * <c>ImageEdit</c> bind through this hook; the rendering differs but the\r\n * underlying wire-format work is identical.\r\n *\r\n * Dataverse exposes file/image columns through TWO different shapes that\r\n * this hook normalizes into a single `FileColumnValue`:\r\n *\r\n * <list type=\"bullet\">\r\n * <item><b>File columns</b> (Dataverse `FileMetadata`): the data column\r\n * itself surfaces post-fetch as a <c>GuidValue</c> ($type 15) holding\r\n * the assigned file id, with a sibling <c>{columnName}_name</c>\r\n * <c>StringValue</c> ($type 14) holding the original filename. Example\r\n * from a contact record: <c>ppp_contract = { $type: 15, value: \"df94…\" }</c>\r\n * plus <c>ppp_contract_name = { $type: 14, value: \"archive.zip\" }</c>.</item>\r\n * <item><b>Image columns</b> (Dataverse `ImageMetadata`): the data\r\n * column itself doesn't surface post-fetch; a sibling <c>{columnName}id</c>\r\n * <c>GuidValue</c> holds the file id (no `_name` companion since\r\n * images don't carry a meaningful original filename). Example:\r\n * <c>ppp_profileimageid = { $type: 15, value: \"4fd5…\" }</c>.</item>\r\n * <item><b>Client-side after upload</b> (both kinds): the data column\r\n * is replaced by a <c>FileValue</c> ($type \"FileValue\") carrying the\r\n * filename, an empty GUID, and the in-memory base64 bytes — that's the\r\n * shape `setValue` writes during an upload.</item>\r\n * </list>\r\n *\r\n * The hook collapses all three onto a single <c>FileColumnValue</c>\r\n * (filename + id + optional binary) so consumers never have to branch.\r\n * Mirrors Blazor's `FileInputColumnEdit.OnParametersSetAsync` synthesis\r\n * of the post-fetch GuidValue + `_name` pair into a `FileValue`.\r\n *\r\n * <c>setValue(null)</c> clears the column. The server's\r\n * <c>UpdateRecordAsync</c> looks for null entries on `FileMetadata` /\r\n * `ImageMetadata` columns and routes them through `DeleteFileField`;\r\n * the sibling `{name}id` (image) or the data-column-as-GuidValue (file)\r\n * gets cleared by Dataverse as a side-effect of the file delete.\r\n */\r\nexport function useFileColumn(columnName: string): {\r\n value: FileColumnValue | undefined;\r\n setValue: (value: FileColumnValue | null | undefined) => void;\r\n /**\r\n * The Dataverse-assigned file id, when one is present. Sourced from\r\n * either pattern (`{column}` GuidValue for file columns, `{column}id`\r\n * GuidValue for image columns, or `FileColumnValue.id` for in-memory\r\n * client uploads). Returns <c>undefined</c> when no file is attached\r\n * or when the id is the empty GUID (file unset).\r\n */\r\n fileId: string | undefined;\r\n /**\r\n * Updates the loaded baseline for this column WITHOUT marking it\r\n * dirty. Used by the lazy-load path to merge fetched binary content\r\n * into the record after a refresh — the binary came from the server,\r\n * so it's part of the baseline, not a user edit.\r\n */\r\n hydrateValue: (value: FileColumnValue | null) => void;\r\n} {\r\n const ctx = useRecordContext();\r\n const properties = ctx.record?.properties;\r\n const dataColumn = properties?.[columnName];\r\n\r\n // Pattern detection. Read the data column's `$type` to decide which\r\n // shape we're looking at. The wire shape is one of:\r\n // - \"FileValue\" → client-side after upload (FileValue)\r\n // - 15 (GuidValue) → file column post-fetch (data col holds the id)\r\n // - undefined → image column post-fetch (data col absent; id is at the sibling)\r\n const dataColumnType = (dataColumn as { $type?: number | string } | undefined)?.$type;\r\n let value: FileColumnValue | undefined;\r\n let fileId: string | undefined;\r\n\r\n if (dataColumnType === 'FileValue') {\r\n // Just-uploaded client-side state. Pass through — consumers see\r\n // filename, id, and base64 bytes. Extra `$type` on the runtime\r\n // object is harmless (TS hides it via the `Omit` alias).\r\n const fv = dataColumn as Schemas['ColumnValueBaseFileValue'];\r\n value = fv;\r\n if (fv.id && fv.id !== '00000000-0000-0000-0000-000000000000') {\r\n fileId = fv.id;\r\n }\r\n } else if (dataColumnType === 15) {\r\n // File column post-fetch: data column is GuidValue, name in `_name`\r\n // companion. Synthesize a FileColumnValue so callers don't have to\r\n // know about the dual shape.\r\n const guidValue = dataColumn as Schemas['ColumnValueBaseGuidValue'];\r\n if (guidValue.value && guidValue.value !== '00000000-0000-0000-0000-000000000000') {\r\n const nameColumn = properties?.[`${columnName}_name`] as\r\n | Schemas['ColumnValueBaseStringValue']\r\n | undefined;\r\n value = {\r\n fileName: nameColumn?.value ?? '',\r\n id: guidValue.value,\r\n value: null,\r\n };\r\n fileId = guidValue.value;\r\n }\r\n } else {\r\n // Image column post-fetch: data column not present, GuidValue at the\r\n // `{column}id` sibling. No `_name` companion exists for images, so\r\n // `value` stays undefined here — consumers (ImageEdit) trigger a\r\n // lazy `getFileInfoAsync` call on `fileId` to populate the filename.\r\n const idColumn = properties?.[`${columnName}id`] as\r\n | Schemas['ColumnValueBaseGuidValue']\r\n | undefined;\r\n if (\r\n idColumn?.value\r\n && idColumn.value !== '00000000-0000-0000-0000-000000000000'\r\n ) {\r\n fileId = idColumn.value;\r\n }\r\n }\r\n\r\n const setValue = useCallback(\r\n (next: FileColumnValue | null | undefined) => {\r\n if (next === null || next === undefined) {\r\n // Just clear the data column — the server's\r\n // GetFileFieldsToDelete looks for null FileMetadata/ImageMetadata\r\n // entries and routes them through DeleteFileField, which also\r\n // clears the id companion (image) or the GuidValue at the data\r\n // column itself (file). No need for the client to clear anything\r\n // else.\r\n ctx.setColumnValue(columnName, null);\r\n } else {\r\n // Re-stamp `$type` on the way out — consumers don't need to know\r\n // about the discriminator, the hook owns it.\r\n ctx.setColumnValue(columnName, {\r\n $type: 'FileValue',\r\n fileName: next.fileName,\r\n id: next.id,\r\n value: next.value ?? null,\r\n });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n const hydrateValue = useCallback(\r\n (next: FileColumnValue | null) => {\r\n if (next === null) {\r\n ctx.hydrateColumnValue(columnName, null);\r\n } else {\r\n ctx.hydrateColumnValue(columnName, {\r\n $type: 'FileValue',\r\n fileName: next.fileName,\r\n id: next.id,\r\n value: next.value ?? null,\r\n });\r\n }\r\n },\r\n [columnName, ctx],\r\n );\r\n\r\n return { value, setValue, fileId, hydrateValue };\r\n}\r\n","import { Field, Input, mergeClasses } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n getStringMaxLength,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useGuardedSetValue, useStringColumn } from './base/use-column-edit.js';\r\n\r\nexport interface TextEditProps extends BaseColumnEditProps {\r\n /** HTML input type — `text` (default), `email`, `tel`, `url`, `search`, `password`. */\r\n type?: 'text' | 'email' | 'tel' | 'url' | 'search' | 'password';\r\n /** Minimum allowed character count. */\r\n minLength?: number;\r\n /** Maximum allowed character count. Auto-resolved from StringMetadata.maxLength when omitted. */\r\n maxLength?: number;\r\n /** Browser autocomplete on/off. Defaults to off (Dataverse fields are not browser-autofill candidates by default). */\r\n autoComplete?: boolean;\r\n /** Optional placeholder shown when the field is empty. */\r\n placeholder?: string;\r\n}\r\n\r\n/**\r\n * Single-line text input bound to a `StringValue` ($type 14) on the parent\r\n * {@link RecordContext}'s record.\r\n *\r\n * `maxLength`, `required`, and `readOnly` are auto-resolved from the parent\r\n * RecordContext's table metadata when consumer props are omitted. The\r\n * description renders as a clickable Info-icon popover next to the label by\r\n * default ({@link DescriptionLocation.Tooltip}).\r\n *\r\n * Use {@link MemoEdit} for multi-line text. Use {@link NumberEdit} /\r\n * {@link MoneyEdit} for numeric fields — text input with `type=\"number\"` is\r\n * deliberately not used here because Dataverse decimal/money fields have\r\n * different wire-format requirements than plain string columns.\r\n */\r\nexport function TextEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n type = 'text',\r\n minLength,\r\n maxLength,\r\n autoComplete,\r\n placeholder,\r\n validate,\r\n}: TextEditProps) {\r\n const { value, setValue: rawSetValue } = useStringColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const t = useT();\r\n\r\n // Hook order matters — `useColumnValidation` must be called every render\r\n // (not gated by `isVisible`), so resolve all the inputs it needs first.\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedMaxLength = maxLength ?? getStringMaxLength(metadata);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n // Defense-in-depth: gate every write through `setValue` on the\r\n // resolved readOnly / disabled state. The underlying input's HTML\r\n // `readOnly` / `disabled` attributes are advisory — a user with dev\r\n // tools can strip them and type a value, but the onChange callback\r\n // still fires. Without this guard, the record would go dirty and the\r\n // save flow would PATCH the column even though the UI says \"locked\".\r\n // See `useGuardedSetValue` for the full rationale.\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'string',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? '',\r\n ...(minLength !== undefined && { minLength }),\r\n ...(resolvedMaxLength !== undefined && { maxLength: resolvedMaxLength }),\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n // Surface validation errors via Field's built-in slot when the consumer\r\n // hasn't opted out via `displayValidationErrorMessage={false}`. Shows the\r\n // first error to keep the layout compact; consumers wanting the full list\r\n // can read via `useColumnValidationErrors(columnName)`.\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-text-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <Input\r\n id={id}\r\n className={className}\r\n style={style}\r\n type={type}\r\n value={value ?? ''}\r\n minLength={minLength}\r\n maxLength={resolvedMaxLength}\r\n readOnly={resolvedReadOnly}\r\n disabled={resolvedDisabled}\r\n placeholder={placeholder}\r\n autoComplete={autoComplete ? 'on' : 'off'}\r\n onChange={(_, data) => {\r\n setValue(data.value === '' ? null : data.value);\r\n }}\r\n />\r\n </Field>\r\n );\r\n}\r\n","import { Field, Textarea, mergeClasses } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n getStringMaxLength,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useGuardedSetValue, useStringColumn } from './base/use-column-edit.js';\r\n\r\nexport interface MemoEditProps extends BaseColumnEditProps {\r\n /** Minimum allowed character count. */\r\n minLength?: number;\r\n /** Maximum allowed character count. Auto-resolved from StringMetadata.maxLength when omitted. */\r\n maxLength?: number;\r\n /** Number of visible text rows. Defaults to 3. */\r\n rows?: number;\r\n /** Optional placeholder shown when the field is empty. */\r\n placeholder?: string;\r\n /** Auto-resize the textarea as the user types. */\r\n resize?: 'none' | 'vertical' | 'horizontal' | 'both';\r\n}\r\n\r\n/**\r\n * Multi-line text input bound to the same `StringValue` ($type 14) wire\r\n * shape as {@link TextEdit}; the difference is the rendered control (Fluent\r\n * `Textarea` instead of `Input`).\r\n *\r\n * `maxLength`, `required`, and `readOnly` are auto-resolved from the parent\r\n * RecordContext's table metadata when consumer props are omitted.\r\n */\r\nexport function MemoEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n minLength,\r\n maxLength,\r\n rows = 3,\r\n placeholder,\r\n resize = 'vertical',\r\n validate,\r\n}: MemoEditProps) {\r\n const { value, setValue: rawSetValue } = useStringColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const t = useT();\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedMaxLength = maxLength ?? getStringMaxLength(metadata);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'string',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? '',\r\n ...(minLength !== undefined && { minLength }),\r\n ...(resolvedMaxLength !== undefined && { maxLength: resolvedMaxLength }),\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-memo-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <Textarea\r\n id={id}\r\n className={className}\r\n style={style}\r\n value={value ?? ''}\r\n rows={rows}\r\n minLength={minLength}\r\n maxLength={resolvedMaxLength}\r\n disabled={resolvedDisabled || resolvedReadOnly}\r\n placeholder={placeholder}\r\n resize={resize}\r\n onChange={(_, data) => {\r\n setValue(data.value === '' ? null : data.value);\r\n }}\r\n />\r\n </Field>\r\n );\r\n}\r\n","import {\r\n Checkbox,\r\n Field,\r\n Label,\r\n Switch,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport { useId } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useBoolColumn, useGuardedSetValue } from './base/use-column-edit.js';\r\n\r\n/** Mirrors Blazor's `BoolEditorType` enum — picks the rendered control. */\r\nexport const BoolEditorType = {\r\n Checkbox: 'checkbox',\r\n Switch: 'switch',\r\n} as const;\r\nexport type BoolEditorType = (typeof BoolEditorType)[keyof typeof BoolEditorType];\r\n\r\n/**\r\n * Mirrors Blazor's `LabelPosition` enum on `BoolEdit`. Controls whether the\r\n * label appears beside the control (the default) or above it (wrapping the\r\n * control in a Field).\r\n */\r\nexport const LabelPosition = {\r\n /** Label rendered next to the control via Fluent's built-in `label` prop on Checkbox/Switch. */\r\n Inline: 'inline',\r\n /** Label rendered above the control via a wrapping {@link Field}. */\r\n Default: 'default',\r\n} as const;\r\nexport type LabelPosition = (typeof LabelPosition)[keyof typeof LabelPosition];\r\n\r\nconst useStyles = makeStyles({\r\n // Inline layout for `LabelPosition.Inline` mode: Checkbox/Switch indicator\r\n // and the external label sit side-by-side. Matches Blazor's\r\n // `ppp-bool-edit-inline` div pattern. Gap matches FluentUI's own internal\r\n // checkbox-to-label spacing so a swap to / from inline-label mode doesn't\r\n // visibly shift positions.\r\n inlineWrapper: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n },\r\n // Pointer cursor on the label — signals to the user that the label is\r\n // clickable (matching the native browser behavior of clicking a\r\n // `<label htmlFor=...>` to toggle its associated input). Applied to\r\n // both the inline external label AND the Field-mode label above the\r\n // control so the cursor hint is consistent across LabelPosition modes.\r\n clickableLabel: {\r\n cursor: 'pointer',\r\n },\r\n});\r\n\r\nexport interface BoolEditProps extends BaseColumnEditProps {\r\n /** Render as a checkbox (default) or a Fluent Switch. */\r\n editorType?: BoolEditorType;\r\n /**\r\n * Where the label sits relative to the control. Defaults to {@link LabelPosition.Inline}\r\n * (next to the control via Fluent's built-in label slot). {@link LabelPosition.Default}\r\n * wraps the control in a Field with the label above.\r\n */\r\n labelPosition?: LabelPosition;\r\n}\r\n\r\n/**\r\n * Boolean column input bound to a `BoolValue` ($type 0). Renders as a Fluent\r\n * `Checkbox` (default) or `Switch`.\r\n *\r\n * `required` and `readOnly` are auto-resolved from the parent RecordContext's\r\n * table metadata when consumer props are omitted. (BoolMetadata has no\r\n * type-specific fields beyond the common required/isValidForCreate/Update.)\r\n *\r\n * Description rendering follows {@link descriptionLocation} — `Tooltip` adds a\r\n * clickable Info icon next to the label that opens a popover; `BelowControl`\r\n * renders the description as a Field hint below the control; `None`\r\n * suppresses entirely. The icon appears in whichever label is showing\r\n * (Checkbox/Switch's inline slot for `LabelPosition.Inline`, the Field slot\r\n * for `LabelPosition.Default`).\r\n *\r\n * Three-state semantics: when the bound column value is `null` / `undefined`,\r\n * the Checkbox variant renders in the `'mixed'` (indeterminate) state and\r\n * clicks cycle through `null → false → true → null`, so the user can return\r\n * to the unset state without dropping to the data layer. The Switch variant\r\n * falls back to `false` for null values — switches don't have a mixed visual\r\n * state and clicks toggle as `false ↔ true`.\r\n */\r\nexport function BoolEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n editorType = BoolEditorType.Checkbox,\r\n labelPosition = LabelPosition.Inline,\r\n validate,\r\n}: BoolEditProps) {\r\n const { value, setValue: rawSetValue } = useBoolColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const t = useT();\r\n\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n const isInactive = resolvedDisabled || resolvedReadOnly;\r\n const styles = useStyles();\r\n // Stable id for the inner input. Used by the external label's `htmlFor`\r\n // (Inline mode) and by Field's auto-binding to provide consistent\r\n // label-input association in Default mode. React's `useId` returns a\r\n // value safe to use as an HTML id.\r\n const generatedId = useId();\r\n const inputId = id ?? generatedId;\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const showLabel = displayLabelWhenAvailable;\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'bool',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? null,\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelChildren = (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n );\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n const hasError = 'validationState' in fieldValidationProps;\r\n\r\n // Wrap in Field when label is positioned above, description renders below\r\n // the control, OR there's a validation error to surface (Field's\r\n // validationMessage slot is the only place to render errors). Inline-label +\r\n // tooltip/none + no-error renders via the inline wrapper instead.\r\n const needsFieldWrapper =\r\n labelPosition === LabelPosition.Default ||\r\n descriptionLocation === DescriptionLocation.BelowControl ||\r\n hasError;\r\n const showInlineLabel = showLabel && labelPosition === LabelPosition.Inline;\r\n\r\n // The `ppp-bool-edit` marker class lands on whichever element is the\r\n // OUTERMOST in the rendered tree, so consumers can target the same\r\n // selector regardless of which render branch fires.\r\n const controlClassName =\r\n needsFieldWrapper || showInlineLabel\r\n ? className\r\n : mergeClasses('ppp-bool-edit', className);\r\n\r\n // The bare control: Checkbox or Switch with no internal label slot.\r\n // The label, in Inline mode, is rendered as a sibling <Label> below.\r\n // (Rendering the label OUTSIDE the Checkbox/Switch — instead of via\r\n // FluentUI's internal `label` slot — guarantees standard browser\r\n // label-input click association: clicks anywhere on the label text\r\n // toggle the input, matching Blazor's separate\r\n // `<FluentInputLabel ForId=...>` pattern.)\r\n const control =\r\n editorType === BoolEditorType.Switch ? (\r\n <Switch\r\n id={inputId}\r\n className={controlClassName}\r\n style={style}\r\n // Switches have no indeterminate visual; collapse null/undefined to\r\n // false on display (matches Blazor's `Value=\"(Value ?? false)\"` on\r\n // <FluentSwitch>). Click toggles boolean.\r\n checked={value ?? false}\r\n disabled={isInactive}\r\n required={resolvedRequired}\r\n onChange={(_, data) => setValue(data.checked === true)}\r\n />\r\n ) : (\r\n <Checkbox\r\n id={inputId}\r\n className={controlClassName}\r\n style={style}\r\n // Tri-state: null/undefined → 'mixed' (indeterminate). FluentUI v9\r\n // Checkbox's `checked` prop accepts `'mixed' | boolean`.\r\n checked={value == null ? 'mixed' : value}\r\n disabled={isInactive}\r\n required={resolvedRequired}\r\n // Three-state click cycle: null → false → true → null. FluentUI's\r\n // own onChange computes data.checked off its internal two-state\r\n // model, so we drive the cycle from the CURRENT value instead —\r\n // matches Blazor's `<FluentCheckbox ThreeState=true>` behavior with\r\n // a `bool?` binding.\r\n onChange={() => {\r\n if (value === null || value === undefined) {\r\n setValue(false);\r\n } else if (value === false) {\r\n setValue(true);\r\n } else {\r\n setValue(null);\r\n }\r\n }}\r\n />\r\n );\r\n\r\n // The Inline-mode label, rendered as a sibling <Label htmlFor={inputId}>.\r\n // Reused by BOTH the Field-wrapper and the bare-inline branches below so the\r\n // label doesn't disappear when a validation error forces the Field wrapper\r\n // on (the original bug: the Field branch only rendered a Default-mode label\r\n // slot, so an Inline-mode label vanished the moment an error appeared).\r\n // `htmlFor` gives clicks the standard label→input toggle; FluentUI's `Label`\r\n // matches the Default-mode Field label styling.\r\n const inlineLabelEl = (\r\n <Label\r\n htmlFor={inputId}\r\n disabled={isInactive}\r\n required={resolvedRequired}\r\n className={styles.clickableLabel}\r\n >\r\n {labelChildren}\r\n </Label>\r\n );\r\n\r\n if (needsFieldWrapper) {\r\n // `htmlFor={inputId}` on the label so clicking it toggles the inner\r\n // input via native browser label-input association. Field's\r\n // auto-binding (`useFieldControlProps_unstable`) sets the child's\r\n // `id` to its own `generatedControlId` ONLY when the child's `id` is\r\n // undefined — but we always pass `id={inputId}`, so we must mirror\r\n // that id onto the label's `htmlFor` ourselves. Without this, Field's\r\n // label points at a non-existent id and clicks no-op (visible parity\r\n // gap with Blazor's `<FluentInputLabel ForId=\"...\">` pattern, which\r\n // explicitly wires the association).\r\n const fieldLabelSlot =\r\n showLabel && labelPosition === LabelPosition.Default\r\n ? { children: labelChildren, htmlFor: inputId, className: styles.clickableLabel }\r\n : null;\r\n // In Inline mode the label sits BESIDE the control inside the Field body —\r\n // the Field's own label slot is reserved for Default mode. This keeps the\r\n // inline label visible when the Field wrapper is forced on by a validation\r\n // error; Field still renders the error message / hint below the control.\r\n const fieldBody = showInlineLabel ? (\r\n <div className={styles.inlineWrapper}>\r\n {control}\r\n {inlineLabelEl}\r\n </div>\r\n ) : (\r\n control\r\n );\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-bool-edit')}\r\n label={fieldLabelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n {fieldBody}\r\n </Field>\r\n );\r\n }\r\n\r\n // Inline mode (no Field needed): bare control + sibling label.\r\n if (showInlineLabel) {\r\n return (\r\n <div className={mergeClasses('ppp-bool-edit', styles.inlineWrapper)}>\r\n {control}\r\n {inlineLabelEl}\r\n </div>\r\n );\r\n }\r\n\r\n // No label, no Field wrapper — just the control with the\r\n // `ppp-bool-edit` marker folded into its className (controlClassName).\r\n return control;\r\n}\r\n","import { Field, Input, makeStyles, mergeClasses } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n getNumberLimits,\r\n isMetadataRequired,\r\n resolveEditState,\r\n resolveNumberValueType,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport {\r\n useGuardedSetValue,\r\n useNumberColumn,\r\n type NumberValueType,\r\n} from './base/use-column-edit.js';\r\n\r\nexport interface NumberEditProps extends BaseColumnEditProps {\r\n /**\r\n * Numeric flavor that determines which `ColumnValueBase` subtype is\r\n * sent back to the server. **Auto-resolved from column metadata when\r\n * omitted** — `IntMetadata` → `int`, `DecimalMetadata` → `decimal`,\r\n * `DoubleMetadata` → `double`, `BigIntMetadata` → `bigint`. Falls back\r\n * to `int` while metadata is still loading or for non-numeric columns\r\n * the editor was bound to incorrectly. Pass an explicit value to\r\n * override (e.g. force `decimal` round-tripping on an int column).\r\n *\r\n * Mirrors Blazor's `<NumberEdit>` which picks the right\r\n * `BaseNumberEdit<TNumber>` from metadata in NumberEdit.razor:7-82.\r\n *\r\n * For currency columns, use {@link MoneyEdit} instead — it binds to\r\n * `MoneyValue` ($type 8) which goes through Dataverse's transaction-\r\n * currency / exchange-rate plumbing.\r\n */\r\n valueType?: NumberValueType;\r\n /** Minimum allowed value. Auto-resolved from column metadata when omitted. */\r\n min?: number;\r\n /** Maximum allowed value. Auto-resolved from column metadata when omitted. */\r\n max?: number;\r\n /**\r\n * Number of decimal places to round to before storing. When set, the value\r\n * is rounded with `toFixed(precision)` on each change, so no more decimals\r\n * than `precision` ever land on the record. Auto-resolved from\r\n * Decimal/Double/Money metadata's `precision` when omitted.\r\n */\r\n precision?: number;\r\n /**\r\n * Hide the browser's increment/decrement spinner buttons on the number\r\n * input. Implemented via a Griffel-generated CSS class targeting the\r\n * inner `<input>` (covers both Firefox via `-moz-appearance: textfield`\r\n * and WebKit via the `::-webkit-inner-spin-button` /\r\n * `-outer-spin-button` pseudo-elements).\r\n */\r\n hideStep?: boolean;\r\n /**\r\n * Format string for displaying the numeric value (e.g. `\"n2\"`).\r\n * **Currently a no-op** in the React layer — used to render a\r\n * culture-aware formatted string in the read-only display branch. The\r\n * React layer doesn't have a dedicated read-only display variant yet;\r\n * when one lands it will honor this prop. Kept on the prop interface so\r\n * consumer code stays portable.\r\n */\r\n format?: string;\r\n /** Step interval. Pass-through to the underlying input's `step`. */\r\n step?: number;\r\n /** Optional placeholder shown when the field is empty. */\r\n placeholder?: string;\r\n}\r\n\r\nconst useNumberEditStyles = makeStyles({\r\n // Hides browser spin buttons cross-browser. WebKit/Blink need pseudo-element\r\n // appearance overrides; Firefox needs `-moz-appearance: textfield`.\r\n hideStep: {\r\n 'MozAppearance': 'textfield',\r\n '::-webkit-outer-spin-button': {\r\n 'WebkitAppearance': 'none',\r\n margin: 0,\r\n },\r\n '::-webkit-inner-spin-button': {\r\n 'WebkitAppearance': 'none',\r\n margin: 0,\r\n },\r\n },\r\n});\r\n\r\n/**\r\n * Numeric column input — writes back as `IntValue` (5), `DecimalValue` (3),\r\n * `DoubleValue` (4), or `BigIntValue` (18) based on column metadata\r\n * (`valueType` overrides). For currency columns, use {@link MoneyEdit}.\r\n *\r\n * `valueType`, `min`, `max`, `precision`, `required`, and `readOnly` are\r\n * auto-resolved from the parent RecordContext's table metadata when\r\n * consumer props are omitted.\r\n */\r\nexport function NumberEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n valueType,\r\n min,\r\n max,\r\n precision,\r\n hideStep,\r\n format: _format,\r\n step,\r\n placeholder,\r\n validate,\r\n}: NumberEditProps) {\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n // Auto-detect from metadata when the consumer didn't pin a flavor.\r\n // Falls back to 'int' while metadata is in-flight or when bound to a\r\n // non-numeric column (the editor still mounts; once metadata lands,\r\n // the resolved type takes over via the re-render).\r\n const resolvedValueType: NumberValueType =\r\n valueType ?? resolveNumberValueType(metadata) ?? 'int';\r\n const { value, setValue: rawSetValue } = useNumberColumn(columnName, resolvedValueType);\r\n const styles = useNumberEditStyles();\r\n const t = useT();\r\n\r\n const limits = getNumberLimits(metadata);\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedMin = min ?? limits.min;\r\n const resolvedMax = max ?? limits.max;\r\n const resolvedPrecision = precision ?? limits.precision;\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'number',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? null,\r\n ...(resolvedMin !== undefined && { min: resolvedMin }),\r\n ...(resolvedMax !== undefined && { max: resolvedMax }),\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const displayValue = value === undefined || value === null ? '' : String(value);\r\n\r\n // Conditional spread on the `input` slot so we don't materialize an empty\r\n // override when hideStep is false (would otherwise compile-error under\r\n // exactOptionalPropertyTypes if we tried `input={undefined}`).\r\n const inputSlot = hideStep ? { input: { className: styles.hideStep } } : {};\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-number-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <Input\r\n id={id}\r\n className={className}\r\n style={style}\r\n {...inputSlot}\r\n type=\"number\"\r\n value={displayValue}\r\n min={resolvedMin}\r\n max={resolvedMax}\r\n step={step}\r\n readOnly={resolvedReadOnly}\r\n disabled={resolvedDisabled}\r\n placeholder={placeholder}\r\n onChange={(_, data) => {\r\n if (data.value === '' || data.value === '-') {\r\n // '-' is a valid intermediate state while typing a negative number;\r\n // treat as cleared until the user enters a digit.\r\n setValue(null);\r\n return;\r\n }\r\n // parseInt for integer types, parseFloat for everything else. The\r\n // wire schema accepts both — picking one keeps the in-memory value\r\n // type stable for downstream consumers reading from the record.\r\n const parsed =\r\n resolvedValueType === 'int' || resolvedValueType === 'bigint'\r\n ? Number.parseInt(data.value, 10)\r\n : Number.parseFloat(data.value);\r\n if (Number.isNaN(parsed)) return;\r\n // Round to the metadata-supplied or consumer-supplied precision so\r\n // we never store more decimals than the column accepts. toFixed\r\n // returns a string; Number() converts back to a number for the\r\n // wire format.\r\n const rounded =\r\n resolvedPrecision !== undefined\r\n ? Number(parsed.toFixed(resolvedPrecision))\r\n : parsed;\r\n setValue(rounded);\r\n }}\r\n />\r\n </Field>\r\n );\r\n}\r\n","/**\r\n * Applies a .NET-style format string to a raw value. Used by\r\n * `<ColumnDisplayValue format=\"...\" />` to mirror Blazor's\r\n * `ColumnDisplayValue.Format` parameter, which calls `value.ToString(format)`\r\n * on the underlying typed value.\r\n *\r\n * Standard numeric format codes are mapped to `Intl.NumberFormat`; standard\r\n * date format codes are mapped to `Intl.DateTimeFormat`. Custom format\r\n * strings have limited support — only a small handful of common patterns\r\n * are recognized. Unknown formats fall back to `String(value)` so the\r\n * component still renders SOMETHING readable instead of empty.\r\n *\r\n * Supported numeric standard format codes (case-insensitive; trailing\r\n * digits set decimal precision per .NET semantics):\r\n * - `N`, `N0`, `N2`, … — number with thousands separators\r\n * - `F`, `F2`, … — fixed-point without thousands separators\r\n * - `D`, `D5`, … — integer padded with leading zeros (D ignores precision otherwise)\r\n * - `C`, `C2`, … — currency (USD by default; pass `locale` for locale-driven currency)\r\n * - `P`, `P0`, `P2`, … — percent (multiplies value by 100 — matches .NET)\r\n * - `E`, `E2`, … — scientific notation\r\n * - `X`, `x` (with optional digits) — hexadecimal (D/D-style padding)\r\n *\r\n * Supported custom numeric format strings:\r\n * - `0.00` / `#,##0.00` — fixed decimals + optional grouping\r\n *\r\n * Supported date standard format codes:\r\n * - `d` — short date, `D` — long date\r\n * - `t` — short time, `T` — long time\r\n * - `g` — general short, `G` — general long\r\n * - `f` — full short, `F` — full long, `M`/`m` — month-day,\r\n * `Y`/`y` — year-month, `s` — sortable, `o` — ISO round-trip\r\n *\r\n * Custom date format strings (`yyyy-MM-dd` etc.) are not parsed; pass\r\n * `s` for ISO output and reformat downstream if you need a specific\r\n * pattern.\r\n */\r\nexport function formatValue(\r\n value: unknown,\r\n format: string,\r\n locale?: string,\r\n): string {\r\n if (value === null || value === undefined || value === '') return '';\r\n const trimmed = format.trim();\r\n if (trimmed === '') return String(value);\r\n\r\n // Numeric paths — accept a JS number directly, or a numeric string\r\n // (decimal/bigint wire values arrive as strings to dodge precision\r\n // loss). Length-gate the string path so accidental ID / GUID values\r\n // don't slip through the numeric arm and emit garbage formatted\r\n // output. 24 chars covers a signed 19-digit bigint plus decimal\r\n // separator and a few zeros of padding.\r\n if (typeof value === 'number' || (typeof value === 'string' && value.length <= 24)) {\r\n const n = typeof value === 'number' ? value : Number(value);\r\n if (Number.isFinite(n)) {\r\n const numeric = tryFormatNumber(n, trimmed, locale);\r\n if (numeric !== undefined) return numeric;\r\n }\r\n }\r\n\r\n // Date paths — value can be a Date, an ISO string, or any string\r\n // Date.parse can recognize. We DON'T try to parse arbitrary strings as\r\n // dates unless the format code is in the date set, to avoid surprising\r\n // numeric-looking strings.\r\n if (isStandardDateCode(trimmed)) {\r\n const date = value instanceof Date ? value : new Date(String(value));\r\n if (!Number.isNaN(date.getTime())) {\r\n return formatDate(date, trimmed, locale);\r\n }\r\n }\r\n\r\n return String(value);\r\n}\r\n\r\n// Standard numeric format codes — letter optionally followed by digits.\r\nconst NUMERIC_STD_RE = /^([NnFfDdCcPpEeXx])(\\d+)?$/;\r\n// Custom numeric format — limited support for `0.00` / `#,##0.00` style.\r\nconst NUMERIC_CUSTOM_RE = /^([#0,]+)(?:\\.([0#]+))?$/;\r\n\r\nfunction tryFormatNumber(n: number, format: string, locale?: string): string | undefined {\r\n const std = format.match(NUMERIC_STD_RE);\r\n if (std) {\r\n const letter = std[1]!;\r\n const code = letter.toUpperCase();\r\n const digits = std[2] !== undefined ? Number(std[2]) : undefined;\r\n switch (code) {\r\n case 'N': {\r\n const opts: Intl.NumberFormatOptions = { useGrouping: true };\r\n applyDigits(opts, digits ?? 2);\r\n return new Intl.NumberFormat(locale, opts).format(n);\r\n }\r\n case 'F': {\r\n const opts: Intl.NumberFormatOptions = { useGrouping: false };\r\n applyDigits(opts, digits ?? 2);\r\n return new Intl.NumberFormat(locale, opts).format(n);\r\n }\r\n case 'D': {\r\n const int = Math.trunc(n);\r\n const absStr = String(Math.abs(int));\r\n const padded = digits !== undefined ? absStr.padStart(digits, '0') : absStr;\r\n return int < 0 ? '-' + padded : padded;\r\n }\r\n case 'C': {\r\n const opts: Intl.NumberFormatOptions = {\r\n style: 'currency',\r\n currency: currencyForLocale(locale),\r\n };\r\n applyDigits(opts, digits ?? 2);\r\n return new Intl.NumberFormat(locale, opts).format(n);\r\n }\r\n case 'P': {\r\n const opts: Intl.NumberFormatOptions = { style: 'percent' };\r\n applyDigits(opts, digits ?? 2);\r\n return new Intl.NumberFormat(locale, opts).format(n);\r\n }\r\n case 'E': {\r\n // Intl supports scientific notation with the 'scientific' notation.\r\n const opts: Intl.NumberFormatOptions = { notation: 'scientific' };\r\n applyDigits(opts, digits ?? 6);\r\n return new Intl.NumberFormat(locale, opts).format(n);\r\n }\r\n case 'X': {\r\n let hex = Math.trunc(n).toString(16);\r\n if (letter === 'X') hex = hex.toUpperCase();\r\n if (digits !== undefined) hex = hex.padStart(digits, '0');\r\n return hex;\r\n }\r\n }\r\n }\r\n const custom = format.match(NUMERIC_CUSTOM_RE);\r\n if (custom) {\r\n const useGrouping = custom[1]!.includes(',');\r\n const decimals = custom[2] !== undefined ? custom[2].length : 0;\r\n return new Intl.NumberFormat(locale, {\r\n useGrouping,\r\n minimumFractionDigits: decimals,\r\n maximumFractionDigits: decimals,\r\n }).format(n);\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction applyDigits(opts: Intl.NumberFormatOptions, digits: number): void {\r\n opts.minimumFractionDigits = digits;\r\n opts.maximumFractionDigits = digits;\r\n}\r\n\r\n// Locale → ISO-4217 currency code best-effort. Without a Dataverse\r\n// transaction-currency lookup we can't be precise; this is just the\r\n// \"the consumer didn't specify, but the locale strongly implies X\"\r\n// fallback for `C` formatting. Locales without a clear mapping default\r\n// to USD.\r\nfunction currencyForLocale(locale?: string): string {\r\n if (!locale) return 'USD';\r\n const region = locale.split('-')[1]?.toUpperCase();\r\n switch (region) {\r\n case 'GB':\r\n return 'GBP';\r\n case 'EU':\r\n case 'DE':\r\n case 'FR':\r\n case 'IT':\r\n case 'ES':\r\n case 'NL':\r\n return 'EUR';\r\n case 'JP':\r\n return 'JPY';\r\n case 'CA':\r\n return 'CAD';\r\n case 'AU':\r\n return 'AUD';\r\n case 'IN':\r\n return 'INR';\r\n default:\r\n return 'USD';\r\n }\r\n}\r\n\r\nfunction isStandardDateCode(format: string): boolean {\r\n if (format.length !== 1) return false;\r\n return 'dDfFgGtTMmYysoOuR'.includes(format);\r\n}\r\n\r\nfunction formatDate(date: Date, code: string, locale?: string): string {\r\n switch (code) {\r\n case 'd':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'short' }).format(date);\r\n case 'D':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(date);\r\n case 'f':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'long', timeStyle: 'short' }).format(date);\r\n case 'F':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeStyle: 'long' }).format(date);\r\n case 'g':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeStyle: 'short' }).format(date);\r\n case 'G':\r\n return new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeStyle: 'medium' }).format(date);\r\n case 't':\r\n return new Intl.DateTimeFormat(locale, { timeStyle: 'short' }).format(date);\r\n case 'T':\r\n return new Intl.DateTimeFormat(locale, { timeStyle: 'medium' }).format(date);\r\n case 'M':\r\n case 'm':\r\n return new Intl.DateTimeFormat(locale, { month: 'long', day: 'numeric' }).format(date);\r\n case 'Y':\r\n case 'y':\r\n return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long' }).format(date);\r\n case 's':\r\n // ISO 8601 sortable — matches .NET's `s` exactly.\r\n return date.toISOString().slice(0, 19);\r\n case 'o':\r\n case 'O':\r\n return date.toISOString();\r\n case 'u':\r\n // UTC sortable — `2024-01-15 14:30:45Z`. .NET uses a space; ISO uses 'T'.\r\n return date.toISOString().slice(0, 19).replace('T', ' ') + 'Z';\r\n case 'R':\r\n // RFC1123 — `Mon, 15 Jan 2024 14:30:45 GMT`.\r\n return date.toUTCString();\r\n default:\r\n return date.toLocaleString(locale);\r\n }\r\n}\r\n","import { Field, Input, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useLocale, useRecordContext, useT } from '@powerportalspro/react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport { formatValue } from './base/format-value.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n getNumberLimits,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useGuardedSetValue, useMoneyColumn } from './base/use-column-edit.js';\r\n\r\nexport interface MoneyEditProps extends BaseColumnEditProps {\r\n /** Minimum allowed value. Auto-resolved from MoneyMetadata.min when omitted. */\r\n min?: number;\r\n /** Maximum allowed value. Auto-resolved from MoneyMetadata.max when omitted. */\r\n max?: number;\r\n /**\r\n * Number of decimal places to round to before storing. Auto-resolved from\r\n * MoneyMetadata.precision when omitted.\r\n */\r\n precision?: number;\r\n /**\r\n * Hide the browser's increment/decrement spinner buttons.\r\n */\r\n hideStep?: boolean;\r\n /**\r\n * .NET-style format string applied to the read-only display value\r\n * (e.g. `\"c2\"` for currency with 2 decimals, `\"n0\"` for thousands-\r\n * separated integer). Mirrors Blazor's `BaseNumberEdit.Format` — the\r\n * editable input branch ignores this; the resolved value only appears\r\n * when `readOnly` or `disabled` is in effect.\r\n *\r\n * Resolution order in read-only mode:\r\n * 1. `record.formattedValues[columnName]` — Dataverse's\r\n * server-side formatted string (currency symbol, locale-aware\r\n * separators, Money base-currency context). Wins when present.\r\n * 2. `formatValue(value, format, locale)` — client-side formatting\r\n * via `Intl.NumberFormat`. Used for unsaved records, columns the\r\n * server didn't emit a formatted value for, or when the locale\r\n * mismatches the server's.\r\n * 3. `String(value)` — raw fallback when no format is set.\r\n *\r\n * Standard numeric codes (`N`, `F`, `C`, `P`, `E`, `X` ± digits) and\r\n * simple custom patterns (`0.00`, `#,##0.00`) are supported. See\r\n * `formatValue` for the full grid.\r\n */\r\n format?: string;\r\n /** Step interval. Defaults to `0.01` (one cent). */\r\n step?: number;\r\n /** Optional placeholder shown when the field is empty. */\r\n placeholder?: string;\r\n}\r\n\r\nconst useMoneyEditStyles = makeStyles({\r\n hideStep: {\r\n 'MozAppearance': 'textfield',\r\n '::-webkit-outer-spin-button': {\r\n 'WebkitAppearance': 'none',\r\n margin: 0,\r\n },\r\n '::-webkit-inner-spin-button': {\r\n 'WebkitAppearance': 'none',\r\n margin: 0,\r\n },\r\n },\r\n // Read-only / disabled display. Renders the formatted value as plain\r\n // text instead of a locked <Input> so:\r\n // * the server's locale-aware `record.formattedValues[columnName]`\r\n // (currency symbol + grouping) can show through verbatim — a\r\n // number input can't display \"$1,234.56\", only \"1234.56\";\r\n // * the `format` prop has somewhere to land.\r\n // Sized to the same vertical rhythm as a Fluent `<Input>` so a\r\n // permission-flipped read-only MoneyEdit doesn't shift adjacent edit\r\n // fields on the form.\r\n readOnlyValue: {\r\n minHeight: tokens.lineHeightBase300,\r\n padding: `${tokens.spacingVerticalXS} 0`,\r\n color: tokens.colorNeutralForeground1,\r\n fontSize: tokens.fontSizeBase300,\r\n lineHeight: tokens.lineHeightBase300,\r\n whiteSpace: 'normal',\r\n wordBreak: 'break-word',\r\n },\r\n});\r\n\r\n/**\r\n * Currency column editor. Always binds to a `MoneyValue` ($type 8) —\r\n * distinct from {@link NumberEdit} because Dataverse currency columns\r\n * participate in the org's transactioncurrency / exchange-rate plumbing\r\n * and round-trip through a different wire shape than plain decimals.\r\n *\r\n * `min`, `max`, `precision`, `required`, and `readOnly` are auto-resolved\r\n * from MoneyMetadata. In the editable branch the input shows the raw\r\n * decimal value — same as Blazor's `<FluentNumberField>` — so consumers\r\n * can type freely. In the read-only / disabled branch the value renders\r\n * as plain text and resolves through `record.formattedValues` → `format`\r\n * fallback → raw `String(value)`, matching Blazor's\r\n * `BaseNumberEdit.GetReadOnlyDisplayValue()`.\r\n */\r\nexport function MoneyEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n min,\r\n max,\r\n precision,\r\n hideStep,\r\n format,\r\n step = 0.01,\r\n placeholder,\r\n validate,\r\n}: MoneyEditProps) {\r\n const { value, setValue: rawSetValue } = useMoneyColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const { locale } = useLocale();\r\n const styles = useMoneyEditStyles();\r\n const t = useT();\r\n\r\n const limits = getNumberLimits(metadata);\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedMin = min ?? limits.min;\r\n const resolvedMax = max ?? limits.max;\r\n const resolvedPrecision = precision ?? limits.precision;\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'number',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? null,\r\n ...(resolvedMin !== undefined && { min: resolvedMin }),\r\n ...(resolvedMax !== undefined && { max: resolvedMax }),\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const displayValue = value === undefined || value === null ? '' : String(value);\r\n const inputSlot = hideStep ? { input: { className: styles.hideStep } } : {};\r\n const isReadOnlyDisplay = resolvedReadOnly || resolvedDisabled;\r\n\r\n // Read-only / disabled display text. Mirrors Blazor's\r\n // `BaseNumberEdit.GetReadOnlyDisplayValue()`:\r\n // 1. `record.formattedValues[columnName]` when present — that's\r\n // the locale-aware currency string Dataverse already formatted.\r\n // 2. `formatValue(value, format, locale)` when consumer passed a\r\n // format string. Covers unsaved records / columns missing from\r\n // FormattedValues.\r\n // 3. `String(value)` as a final fallback so something visible\r\n // always shows up.\r\n // Empty string when value is null/undefined (no formatted value to\r\n // render either way).\r\n let readOnlyDisplayText = '';\r\n if (isReadOnlyDisplay) {\r\n const formattedFromServer = record?.formattedValues?.[columnName];\r\n if (formattedFromServer !== undefined && formattedFromServer !== null && formattedFromServer !== '') {\r\n readOnlyDisplayText = formattedFromServer;\r\n } else if (value !== undefined && value !== null) {\r\n readOnlyDisplayText = format ? formatValue(value, format, locale) : String(value);\r\n }\r\n }\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-money-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n {isReadOnlyDisplay ? (\r\n <div\r\n id={id}\r\n className={mergeClasses(styles.readOnlyValue, className)}\r\n style={style}\r\n >\r\n {readOnlyDisplayText}\r\n </div>\r\n ) : (\r\n <Input\r\n id={id}\r\n className={className}\r\n style={style}\r\n {...inputSlot}\r\n type=\"number\"\r\n value={displayValue}\r\n min={resolvedMin}\r\n max={resolvedMax}\r\n step={step}\r\n placeholder={placeholder}\r\n onChange={(_, data) => {\r\n if (data.value === '' || data.value === '-') {\r\n setValue(null);\r\n return;\r\n }\r\n const parsed = Number.parseFloat(data.value);\r\n if (Number.isNaN(parsed)) return;\r\n const rounded =\r\n resolvedPrecision !== undefined\r\n ? Number(parsed.toFixed(resolvedPrecision))\r\n : parsed;\r\n setValue(rounded);\r\n }}\r\n />\r\n )}\r\n </Field>\r\n );\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\n\r\ntype Schemas = components['schemas'];\r\ntype TableRecord = Schemas['TableRecord'];\r\ntype ColumnMetadataBase = Schemas['ColumnMetadataBase'];\r\n\r\n/**\r\n * Reads the innermost primitive value from a column-value wire wrapper —\r\n * unwrapping the `RelatedRecordValue` shape Dataverse returns for\r\n * aliased / aggregate-groupby attributes (where the actual column\r\n * value is nested one extra level under `.value`).\r\n *\r\n * Use this when matching a column value against a known primitive\r\n * (e.g. cross-filter highlight) where both sides — aggregate result\r\n * records AND row-page records — need to compare equal. Without this\r\n * helper, an aggregate's groupby column comes back wrapped twice (so\r\n * `.value` is a `ChoiceValue` object, not the integer the row carries)\r\n * and strict equality always fails.\r\n *\r\n * Returns `undefined` for missing / null properties so callers can\r\n * skip-or-default uniformly.\r\n */\r\nexport function readColumnRawValue(property: unknown): unknown {\r\n if (property == null) return undefined;\r\n if (typeof property !== 'object') return property;\r\n const wrapped = property as { $type?: number | string; value?: unknown };\r\n // RelatedRecordValue nests another ColumnValueBase under `.value`;\r\n // recurse so the eventual primitive matches what a row-page record\r\n // would return at the same `properties[column].value` path.\r\n if (wrapped.$type === 'RelatedRecordValue') {\r\n return readColumnRawValue(wrapped.value);\r\n }\r\n return wrapped.value;\r\n}\r\n\r\n/**\r\n * Resolves the display string for a column on a record — the value a\r\n * user would see in a grid cell or lookup option. Mirrors Blazor's\r\n * `GridViewColumnDisplayValueResolver.ResolveDisplayValue`.\r\n *\r\n * **Resolution order:**\r\n *\r\n * 1. `record.formattedValues[columnName]` — Dataverse's server-side\r\n * pre-formatted localized string. Covers choices (localized label),\r\n * dates (locale-formatted), numbers (thousands separators), money\r\n * (currency symbol + locale), bool (Yes/No), and is the most reliable\r\n * source when available.\r\n * 2. The raw `record.properties[columnName]` value, with type-aware\r\n * fallback rendering. String → as-is. Lookup → the lookup's `name`.\r\n * Numeric → `String(value)`. File/Image GUIDs / byte arrays →\r\n * `null` (no useful display in a row context).\r\n * 3. `null` if the column isn't present on the record.\r\n *\r\n * Exposed alongside the lookup edit so consumers writing their own\r\n * grid-cell renderers / option templates can reuse the same fallback\r\n * chain.\r\n */\r\nexport function resolveColumnDisplayValue(\r\n record: TableRecord,\r\n columnName: string,\r\n _metadata?: ColumnMetadataBase,\r\n): string | null {\r\n // Dataverse's formatted values cover the common cases (choices, dates,\r\n // money, bools). Prefer them when present — they're already locale-\r\n // aware and match what Dataverse itself shows in the native UI.\r\n const formatted = record.formattedValues?.[columnName];\r\n if (formatted !== undefined && formatted !== null) {\r\n return formatted;\r\n }\r\n\r\n let property = record.properties?.[columnName];\r\n if (!property) return null;\r\n\r\n // Unwrap link-entity columns. The server emits joined-column values as\r\n // `RelatedRecordValue` — a thin wrapper carrying the related table /\r\n // column metadata, with the actual typed value nested inside `.value`.\r\n // Substituting `property` with the inner value lets the type switch\r\n // below handle the value as if it had been on the base record directly.\r\n if ((property as { $type?: number | string }).$type === 'RelatedRecordValue') {\r\n const wrapper = property as Schemas['ColumnValueBaseRelatedRecordValue'];\r\n if (!wrapper.value) return null;\r\n // Server also commonly populates a nested formattedValue (e.g. the\r\n // related table's choice column resolved to a localized label) —\r\n // prefer it when present, matching the formattedValues-first path\r\n // used at the top of this function.\r\n const innerFormatted = (wrapper.value as { formattedValue?: string | null }).formattedValue;\r\n if (innerFormatted) return innerFormatted;\r\n property = wrapper.value;\r\n }\r\n\r\n // Discriminate by `$type` against the wire's ColumnValueBase variants.\r\n // Numeric `$type` codes match the C# ColumnType enum: 0 bool, 2 date,\r\n // 3 decimal, 4 double, 5 int, 6 lookup, 8 money, 11 choice, 14 string,\r\n // 15 guid, 18 bigint, 40 multi-select-choice. File/image (`'FileValue'`)\r\n // and related-record (`'RelatedRecordValue'`) carry string discriminators.\r\n const t = (property as { $type?: number | string }).$type;\r\n\r\n switch (t) {\r\n case 14: {\r\n // StringValue — value is `null | string`. Convert null → null\r\n // (caller decides empty display); pass strings through.\r\n const v = (property as Schemas['ColumnValueBaseStringValue']).value;\r\n return v ?? null;\r\n }\r\n case 6: {\r\n // LookupValue — the display name is on `.name`. Falls back to the\r\n // GUID when no name is set (rare, but the GUID is the only useful\r\n // identifier left).\r\n const lookup = property as Schemas['ColumnValueBaseLookupValue'];\r\n return lookup.name ?? lookup.value ?? null;\r\n }\r\n case 0:\r\n case 2:\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 8:\r\n case 11:\r\n case 18: {\r\n // Numeric / date / bool / choice. `formattedValues` should have\r\n // already covered these; this is a fallback for records where the\r\n // server didn't emit formatted values (unusual but possible). Just\r\n // String() the raw value — better than rendering `[object Object]`.\r\n const raw = (property as { value?: unknown }).value;\r\n return raw === undefined || raw === null ? null : String(raw);\r\n }\r\n case 15: {\r\n // GuidValue. For file / image columns post-fetch, the data column\r\n // is a GuidValue carrying the file id with a `{columnName}_name`\r\n // companion StringValue holding the actual filename — prefer that\r\n // companion so the cell displays \"report.pdf\" instead of a raw\r\n // GUID. (MainGrid doesn't currently thread `metadata` through to\r\n // cell renderers, so we can't gate on metadata.$type === 41/42 —\r\n // instead, just check for the companion's presence: Dataverse only\r\n // emits `{column}_name` for file/image columns, so a match is\r\n // unambiguous.) Falls back to the GUID otherwise — the related\r\n // lookup would surface as a LookupValue, so the rare bare-GuidValue\r\n // case is dev-tools-only.\r\n const companion = record.properties?.[`${columnName}_name`] as\r\n | Schemas['ColumnValueBaseStringValue']\r\n | undefined;\r\n if (companion?.value) return companion.value;\r\n const v = (property as Schemas['ColumnValueBaseGuidValue']).value;\r\n return v ?? null;\r\n }\r\n case 40: {\r\n // MultiSelectChoiceValue — formattedValues normally contains the\r\n // semicolon-joined localized labels. Without it, fall back to the\r\n // comma-joined raw integers (uninformative but at least visible).\r\n const list = (property as Schemas['ColumnValueBaseMultiSelectChoiceValue']).value;\r\n if (!list || list.length === 0) return null;\r\n return list.map((c) => String(c.value)).join(', ');\r\n }\r\n case 'FileValue': {\r\n // File / image column in the pending-upload state — the wire shape\r\n // here is a FileValue (filename + base64 bytes + EMPTY_GUID id), set\r\n // by FileGrid's upload path before the row has been persisted.\r\n // Returning the filename lets the file cell render the name\r\n // immediately on the pending row, matching what users expect after\r\n // dropping a file in. Post-fetch shape is GuidValue + sibling `_name`\r\n // — that path runs through case 15 + the companion read below.\r\n const fv = property as Schemas['ColumnValueBaseFileValue'];\r\n return fv.fileName ?? null;\r\n }\r\n default: {\r\n // Byte arrays, files, related-records, etc. — not meaningful as a\r\n // single string. Return null and let the caller render a fallback\r\n // (or skip the column).\r\n return null;\r\n }\r\n }\r\n}\r\n","import { Field, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useLocale, useRecordContext, useT } from '@powerportalspro/react';\r\nimport type { CSSProperties } from 'react';\r\nimport { readColumnRawValue, resolveColumnDisplayValue } from './base/column-display.js';\r\nimport type { DescriptionLocation as DescriptionLocationType } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport { formatValue } from './base/format-value.js';\r\nimport { getDisplayDescription, getDisplayLabel } from './base/metadata-helpers.js';\r\n\r\nexport interface ColumnDisplayValueProps {\r\n /** Logical name of the column on the parent {@link RecordContext}'s record. Required. */\r\n columnName: string;\r\n /** Display label. Falls back to the column metadata's `displayName`, then to `columnName`. */\r\n label?: string;\r\n /** Description / hint text. Falls back to the column metadata's `description`. */\r\n description?: string;\r\n /**\r\n * Render the label when one is available. Defaults to `true`. Mirrors the\r\n * matching prop on {@link BaseColumnEditProps} so a `<ColumnDisplayValue>`\r\n * lays out identically to an adjacent `<TextEdit>` / `<NumberEdit>`.\r\n */\r\n displayLabelWhenAvailable?: boolean;\r\n /**\r\n * Where the description renders when one is available. Defaults to\r\n * {@link DescriptionLocation.Tooltip}. Same three-way semantics as the\r\n * edit components.\r\n */\r\n descriptionLocation?: DescriptionLocationType;\r\n /** When false, renders nothing. Defaults to true. */\r\n isVisible?: boolean;\r\n /**\r\n * String shown when the column has no value on the record. Defaults to\r\n * an em-dash. Pass an empty string to suppress.\r\n */\r\n emptyPlaceholder?: string;\r\n /**\r\n * Optional .NET-style format string applied to the column's raw value.\r\n * When set, the server's pre-formatted value in `record.formattedValues`\r\n * is **bypassed** and the raw value is formatted client-side via\r\n * `Intl.NumberFormat` / `Intl.DateTimeFormat`. Useful when the server's\r\n * locale differs from the consumer's, or when you want a specific\r\n * representation\r\n * (`'N0'` for integer counts, `'C'` for currency, `'D'` for long\r\n * dates) regardless of the server's formatted-value string.\r\n *\r\n * Standard numeric codes (`N`, `F`, `D`, `C`, `P`, `E`, `X` ± digits)\r\n * and standard date codes (`d`, `D`, `g`, `G`, `t`, `T`, `s`, `o`,\r\n * `f`, `F`, `M`, `Y`, `u`, `R`) are supported. Limited custom format\r\n * support: `0.00`-style numeric patterns work, custom date patterns\r\n * (`yyyy-MM-dd` etc.) are not parsed. Unknown formats fall back to\r\n * `String(value)`. See `formatValue` for the full grid.\r\n */\r\n format?: string;\r\n /** Optional CSS class on the rendered value element. */\r\n className?: string;\r\n /** Optional inline style on the rendered value element. */\r\n style?: CSSProperties;\r\n /** Optional id on the rendered value element. */\r\n id?: string;\r\n}\r\n\r\nconst useColumnDisplayValueStyles = makeStyles({\r\n value: {\r\n // Match the vertical rhythm of a Fluent `<Input>` so a read-only\r\n // display sits on the same baseline as adjacent edit fields. Tokens\r\n // (not literal pixel values) so the layout follows theme density\r\n // changes the consumer might dial in.\r\n minHeight: tokens.lineHeightBase300,\r\n padding: `${tokens.spacingVerticalXS} 0`,\r\n color: tokens.colorNeutralForeground1,\r\n fontSize: tokens.fontSizeBase300,\r\n lineHeight: tokens.lineHeightBase300,\r\n // Long lookups / multi-select labels can exceed the field width;\r\n // wrap rather than overflow so the form layout stays predictable.\r\n whiteSpace: 'normal',\r\n wordBreak: 'break-word',\r\n },\r\n empty: {\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n});\r\n\r\n/**\r\n * Read-only display of a column's value, formatted the way Dataverse\r\n * formats it server-side. Renders the same {@link Field} chrome the edit\r\n * components use (label + tooltip / hint slot) so a\r\n * `<ColumnDisplayValue>` drops into a form layout alongside `<TextEdit>`\r\n * / `<NumberEdit>` / etc. without visual drift.\r\n *\r\n * Resolution order goes through {@link resolveColumnDisplayValue}, which\r\n * prefers `record.formattedValues[columnName]` (locale-aware currency,\r\n * dates, choice labels, etc.) and falls back to a type-aware stringify\r\n * of the raw `record.properties[columnName]` when the server didn't\r\n * stamp a formatted value.\r\n *\r\n * Use for fields that should NEVER be editable in the surrounding form\r\n * — system audit columns (`createdon`, `modifiedby`), computed\r\n * columns, or any read-only summary panel. The lookup-cell click-through\r\n * and grid cell renderers run through the same underlying helper, so a\r\n * `<ColumnDisplayValue columnName=\"ppp_total\" />` in a form looks\r\n * identical to the matching grid cell.\r\n *\r\n * Not the same as `<NumberEdit readOnly>`: a read-only edit component\r\n * still mounts the input and the per-column validator; this component\r\n * mounts neither. Pick the edit component when the field MIGHT become\r\n * editable (permission-driven readOnly); pick `<ColumnDisplayValue>`\r\n * when it's never editable.\r\n */\r\nexport function ColumnDisplayValue({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n isVisible = true,\r\n emptyPlaceholder = '—',\r\n format,\r\n className,\r\n style,\r\n id,\r\n}: ColumnDisplayValueProps) {\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const { locale } = useLocale();\r\n const styles = useColumnDisplayValueStyles();\r\n const t = useT();\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n\r\n if (!isVisible) return null;\r\n\r\n // No record loaded yet (RecordContext still fetching) — render the\r\n // empty placeholder rather than nothing so the form layout doesn't\r\n // jump when the record lands.\r\n //\r\n // When a `format` prop is supplied, Blazor's `ColumnDisplayValue`\r\n // bypasses `record.formattedValues` and applies the format to the raw\r\n // typed value (`IntValue.Value.ToString(Format)` etc.). Mirror that\r\n // here: pull the raw value via `readColumnRawValue`, run it through\r\n // `formatValue`. Multi-select-choice values come back as an array of\r\n // `{ value: number }`; join the per-entry formatted results with the\r\n // same separator Blazor uses (\"`, `\").\r\n let display: string | null;\r\n if (record && format) {\r\n const raw = readColumnRawValue(record.properties?.[columnName]);\r\n if (raw === undefined || raw === null) {\r\n display = null;\r\n } else if (Array.isArray(raw)) {\r\n display = raw\r\n .map((entry) => formatValue((entry as { value?: unknown }).value, format, locale))\r\n .filter((s) => s.length > 0)\r\n .join(', ');\r\n if (display.length === 0) display = null;\r\n } else {\r\n display = formatValue(raw, format, locale);\r\n }\r\n } else {\r\n display = record ? resolveColumnDisplayValue(record, columnName, metadata) : null;\r\n }\r\n const isEmpty = display === null || display === '';\r\n const displayText = isEmpty ? emptyPlaceholder : display;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n // Empty placeholder gets the muted-text color so it reads as\r\n // \"no value here\" rather than as an actual value.\r\n const valueClass = `${styles.value}${isEmpty ? ` ${styles.empty}` : ''}${className ? ` ${className}` : ''}`;\r\n\r\n return (\r\n <Field className={mergeClasses('ppp-column-display-value')} label={labelSlot} hint={fieldHint}>\r\n <div id={id} className={valueClass} style={style}>\r\n {displayText}\r\n </div>\r\n </Field>\r\n );\r\n}\r\n","import { Field, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { DatePicker } from '@fluentui/react-datepicker-compat';\r\nimport { TimePicker, type TimeSelectionEvents, type TimeSelectionData } from '@fluentui/react-timepicker-compat';\r\nimport { DateTimeBehavior, type components } from '@powerportalspro/core';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport { useMemo } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useDateColumn, useGuardedSetValue } from './base/use-column-edit.js';\r\n\r\n/**\r\n * Which input(s) the editor renders. Mirrors Blazor's `DateTimeEditorType`.\r\n *\r\n * - `'dateTime'` (default for `UserLocal` / `TimeZoneIndependent` metadata) —\r\n * side-by-side `DatePicker` + `TimePicker`.\r\n * - `'dateOnly'` (default for `DateOnly` metadata) — `DatePicker` only.\r\n * - `'timeOnly'` — `TimePicker` only. The date portion of an existing\r\n * stored value is preserved; a fresh value uses today's date.\r\n */\r\nexport const DateTimeEditorType = {\r\n DateTime: 'dateTime',\r\n DateOnly: 'dateOnly',\r\n TimeOnly: 'timeOnly',\r\n} as const;\r\nexport type DateTimeEditorType = (typeof DateTimeEditorType)[keyof typeof DateTimeEditorType];\r\n\r\nexport interface DateTimeEditProps extends BaseColumnEditProps {\r\n /**\r\n * Which input(s) to render. Auto-resolved from the column metadata's\r\n * <c>dateTimeBehavior</c> when omitted: `DateOnly` → `'dateOnly'`,\r\n * `UserLocal` / `TimeZoneIndependent` → `'dateTime'`. Pass explicitly to\r\n * force a specific shape (e.g. `'timeOnly'` for a column conceptually\r\n * holding a time-of-day even though Dataverse always stores date+time).\r\n */\r\n editorType?: DateTimeEditorType;\r\n /**\r\n * Optional placeholder shown when the field is empty. Applies to whichever\r\n * picker(s) the chosen `editorType` renders.\r\n */\r\n placeholder?: string;\r\n /**\r\n * Per-day disable predicate. Returns `true` for dates that should appear\r\n * disabled (un-pickable) in the calendar. Typical use cases: block\r\n * weekends, block past dates, block specific holidays.\r\n *\r\n * The predicate is evaluated across a fixed window of ±2 years around\r\n * today and the resulting dates are passed to the underlying calendar's\r\n * `restrictedDates` prop. Dates outside that window aren't gated — if\r\n * your predicate needs to disable dates far in the past or future,\r\n * widen the window with a CSS-only solution or rely on `min` / `max`\r\n * date constraints instead.\r\n *\r\n * Memoized on the predicate's identity — wrap inline arrow functions in\r\n * `useCallback` if you want to avoid the per-render re-walk (the\r\n * cost is ~1500 predicate calls, usually fine).\r\n */\r\n disabledDateFunc?: (date: Date) => boolean;\r\n}\r\n\r\nconst useDateTimeStyles = makeStyles({\r\n // Side-by-side date + time pickers when editorType is `dateTime`. Stack\r\n // vertically on narrow screens — each picker's input is ~120px wide so\r\n // they fit comfortably in the typical form column at desktop widths.\r\n row: {\r\n display: 'flex',\r\n flexWrap: 'wrap',\r\n gap: tokens.spacingHorizontalS,\r\n alignItems: 'center',\r\n },\r\n});\r\n\r\n/**\r\n * Wraps Fluent UI's compat `DatePicker` and `TimePicker` (both shipped as\r\n * separate packages from `@fluentui/react-components` because v9's main\r\n * bundle hasn't ported them yet — these compat packages are the official\r\n * transitional surface and use the same design tokens as the rest of Fluent\r\n * v9). The editor shape is driven by `editorType` (or auto-resolved from\r\n * `DateTimeBehavior` metadata):\r\n *\r\n * - `dateOnly` → just `DatePicker`. Stored as midnight UTC on the wire.\r\n * - `dateTime` → `DatePicker` + `TimePicker` side-by-side; changes from\r\n * either preserve the other half so editing one doesn't clobber the other.\r\n * - `timeOnly` → just `TimePicker`, anchored to the existing wire value's\r\n * date (or today when there isn't one).\r\n *\r\n * Wire format is ISO 8601 string in/out via `useDateColumn`; `Date` objects\r\n * live only inside this component for picker interop.\r\n */\r\nexport function DateTimeEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n editorType,\r\n placeholder,\r\n disabledDateFunc,\r\n validate,\r\n}: DateTimeEditProps) {\r\n const { value, setValue: rawSetValue } = useDateColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const styles = useDateTimeStyles();\r\n const t = useT();\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n const resolvedEditorType = editorType ?? resolveEditorTypeFromMetadata(metadata);\r\n\r\n // Walk a ±2-year window around today and collect dates the predicate\r\n // says are disabled. Passed to the calendar's `restrictedDates` slot,\r\n // which renders them as un-pickable. The compat DatePicker doesn't\r\n // accept a predicate directly, so this materializes the closed set the\r\n // picker needs. Memoized on the predicate identity so the walk only\r\n // re-runs when the consumer swaps the function.\r\n const restrictedDates = useMemo<Date[] | undefined>(() => {\r\n if (!disabledDateFunc) return undefined;\r\n const out: Date[] = [];\r\n const cursor = new Date();\r\n cursor.setFullYear(cursor.getFullYear() - 2);\r\n cursor.setHours(0, 0, 0, 0);\r\n const end = new Date();\r\n end.setFullYear(end.getFullYear() + 2);\r\n while (cursor <= end) {\r\n if (disabledDateFunc(new Date(cursor))) {\r\n out.push(new Date(cursor));\r\n }\r\n cursor.setDate(cursor.getDate() + 1);\r\n }\r\n return out.length > 0 ? out : undefined;\r\n }, [disabledDateFunc]);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'datetime',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? null,\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n // Both pickers want `Date | null`; the wire is `string | undefined`.\r\n // Parse once and feed the same Date into the date and time halves so\r\n // their state stays consistent. For DateOnly columns we project the\r\n // wire's UTC date components into a local-midnight Date — otherwise\r\n // a user in any negative-offset timezone (e.g. `2026-05-08T00:00:00Z`\r\n // in MDT) would see \"May 7\" because `new Date(...)` interprets the\r\n // string as UTC and the picker displays in local.\r\n const currentDate = value ? parseWireValueForPicker(value, resolvedEditorType) : null;\r\n\r\n // Date-half change: keep the time portion of the existing value if any,\r\n // so toggling the calendar doesn't reset the clock to midnight.\r\n const handleDateSelected = (selected: Date | null | undefined) => {\r\n if (!selected) {\r\n setValue(null);\r\n return;\r\n }\r\n if (resolvedEditorType === DateTimeEditorType.DateOnly) {\r\n // Treat date-only as midnight UTC — symmetric with how Blazor stores\r\n // DateOnly columns; avoids timezone drift across users.\r\n const utcMidnight = new Date(\r\n Date.UTC(selected.getFullYear(), selected.getMonth(), selected.getDate()),\r\n );\r\n setValue(utcMidnight.toISOString());\r\n return;\r\n }\r\n // DateTime mode — preserve the time half of the existing value.\r\n const merged = new Date(selected);\r\n if (currentDate) {\r\n merged.setHours(\r\n currentDate.getHours(),\r\n currentDate.getMinutes(),\r\n currentDate.getSeconds(),\r\n currentDate.getMilliseconds(),\r\n );\r\n } else {\r\n merged.setHours(0, 0, 0, 0);\r\n }\r\n setValue(merged.toISOString());\r\n };\r\n\r\n // Time-half change: preserve the date portion of the existing value (or\r\n // today's date if there isn't one yet).\r\n const handleTimeSelected = (\r\n _ev: TimeSelectionEvents,\r\n data: TimeSelectionData,\r\n ) => {\r\n if (!data.selectedTime) {\r\n // For timeOnly we leave the date alone and clear only when the user\r\n // actively cleared the input. The TimePicker fires onTimeChange with\r\n // null both for clears and for invalid text — taking that as \"set\r\n // null\" matches the BoolEdit / TextEdit empty-input semantics.\r\n setValue(null);\r\n return;\r\n }\r\n const baseDate = currentDate ?? new Date();\r\n const merged = new Date(baseDate);\r\n merged.setHours(\r\n data.selectedTime.getHours(),\r\n data.selectedTime.getMinutes(),\r\n data.selectedTime.getSeconds(),\r\n data.selectedTime.getMilliseconds(),\r\n );\r\n setValue(merged.toISOString());\r\n };\r\n\r\n // Anchor for TimePicker's option list. For timeOnly without a date,\r\n // use today; otherwise use the date half of the current value so the\r\n // generated time options sit on the right day.\r\n const dateAnchor = currentDate ?? new Date();\r\n\r\n const showDate =\r\n resolvedEditorType === DateTimeEditorType.DateOnly\r\n || resolvedEditorType === DateTimeEditorType.DateTime;\r\n const showTime =\r\n resolvedEditorType === DateTimeEditorType.TimeOnly\r\n || resolvedEditorType === DateTimeEditorType.DateTime;\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-date-time-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <div className={`${styles.row}${className ? ' ' + className : ''}`} style={style}>\r\n {showDate && (\r\n <DatePicker\r\n value={currentDate}\r\n onSelectDate={handleDateSelected}\r\n disabled={resolvedDisabled || resolvedReadOnly}\r\n allowTextInput\r\n {...(restrictedDates !== undefined && {\r\n calendar: { restrictedDates },\r\n })}\r\n {...(id !== undefined && { id })}\r\n {...(placeholder !== undefined && { placeholder })}\r\n />\r\n )}\r\n {showTime && (\r\n <TimePicker\r\n selectedTime={currentDate}\r\n onTimeChange={handleTimeSelected}\r\n disabled={resolvedDisabled || resolvedReadOnly}\r\n dateAnchor={dateAnchor}\r\n {...(!showDate && id !== undefined && { id })}\r\n {...(placeholder !== undefined && { placeholder })}\r\n />\r\n )}\r\n </div>\r\n </Field>\r\n );\r\n}\r\n\r\n/**\r\n * Picks the default editor shape based on the column's\r\n * <c>DateTimeBehavior</c>. <c>DateOnly</c> renders a date-only input; the\r\n * other two behaviors (<c>UserLocal</c>, <c>TimeZoneIndependent</c>) render\r\n * a combined date+time input. Falls back to <c>'dateTime'</c> when no\r\n * metadata is loaded yet OR the column isn't a DateTime variant.\r\n */\r\nfunction resolveEditorTypeFromMetadata(\r\n metadata: components['schemas']['ColumnMetadataBase'] | undefined,\r\n): DateTimeEditorType {\r\n const m = metadata as { dateTimeBehavior?: DateTimeBehavior } | undefined;\r\n if (m?.dateTimeBehavior === DateTimeBehavior.DateOnly) {\r\n return DateTimeEditorType.DateOnly;\r\n }\r\n return DateTimeEditorType.DateTime;\r\n}\r\n\r\n/**\r\n * Parses an ISO 8601 wire value into a Date suitable for the matching\r\n * picker. Returns `null` for empty input or unparseable strings (rather\r\n * than the `Invalid Date` sentinel that the `Date` ctor returns and\r\n * would propagate into picker UI).\r\n *\r\n * For <c>dateOnly</c> the wire value is midnight UTC and the picker\r\n * shows local time — so we translate the UTC date components into a\r\n * Date constructed at LOCAL midnight with the same year/month/day. That\r\n * keeps \"May 8 UTC midnight\" displaying as \"May 8\" in every timezone\r\n * instead of shifting to \"May 7\" west of UTC. For other modes the wire\r\n * IS a real instant in time and direct parsing (UTC → local rendering)\r\n * is what the user wants.\r\n */\r\nfunction parseWireValueForPicker(wireValue: string, editorType: DateTimeEditorType): Date | null {\r\n const utc = new Date(wireValue);\r\n if (Number.isNaN(utc.getTime())) return null;\r\n if (editorType === DateTimeEditorType.DateOnly) {\r\n return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());\r\n }\r\n return utc;\r\n}\r\n","import type { PowerPortalsProClient } from '@powerportalspro/core';\r\n\r\n/**\r\n * Shared file-IO helpers used by `FileEdit` and `ImageEdit`. Both edits\r\n * round-trip a Dataverse `FileValue` through the wire-format base64 string;\r\n * these helpers cover the conversions and the on-demand fetch path for\r\n * downloading content that wasn't loaded with the record.\r\n */\r\n\r\n/** Sentinel id for a freshly-uploaded file that hasn't been saved yet. */\r\nexport const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\r\n\r\n/**\r\n * Reads a `File` as base64 (no `data:` prefix). The `FileReader` API\r\n * gives us a Data URL; we strip the leading <c>data:&lt;mime&gt;;base64,</c>\r\n * portion because the wire format is just the byte content base64-encoded.\r\n */\r\nexport function readFileAsBase64(file: File): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const result = reader.result;\r\n if (typeof result !== 'string') {\r\n reject(new Error('FileReader returned non-string result.'));\r\n return;\r\n }\r\n const commaIndex = result.indexOf(',');\r\n resolve(commaIndex >= 0 ? result.substring(commaIndex + 1) : result);\r\n };\r\n reader.onerror = () => reject(reader.error ?? new Error('FileReader error'));\r\n reader.readAsDataURL(file);\r\n });\r\n}\r\n\r\n/** Decodes a base64 string into a `Uint8Array` for `Blob` construction. */\r\nexport function base64ToBytes(base64: string): Uint8Array {\r\n const binary = atob(base64);\r\n const bytes = new Uint8Array(binary.length);\r\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\r\n return bytes;\r\n}\r\n\r\n/**\r\n * Fetches a file column's binary content from the server when the on-record\r\n * value carries metadata only (no inline base64). Returns null when the\r\n * fetch can't run (record not yet saved, no table/id) or the response\r\n * doesn't include data.\r\n */\r\nexport async function fetchFileBytes(\r\n ppp: PowerPortalsProClient,\r\n tableName: string | undefined,\r\n recordId: string | undefined,\r\n columnName: string,\r\n): Promise<Uint8Array | null> {\r\n if (!tableName || !recordId) return null;\r\n const info = await ppp.getFileInfoAsync(tableName, recordId, columnName, true);\r\n if (!info.fileData) return null;\r\n return base64ToBytes(info.fileData);\r\n}\r\n\r\n/** Saves a byte array as a downloadable file with the given name. */\r\nexport function triggerBlobDownload(bytes: Uint8Array, fileName: string): void {\r\n // Cast through ArrayBuffer (the BlobPart contract) — Uint8Array is a view\r\n // over an ArrayBuffer and TypeScript's lib.dom narrows BlobPart to the\r\n // concrete ArrayBuffer type.\r\n const blob = new Blob([bytes.buffer as ArrayBuffer]);\r\n const url = URL.createObjectURL(blob);\r\n const anchor = document.createElement('a');\r\n anchor.href = url;\r\n anchor.download = fileName;\r\n document.body.appendChild(anchor);\r\n anchor.click();\r\n document.body.removeChild(anchor);\r\n URL.revokeObjectURL(url);\r\n}\r\n\r\n/**\r\n * Best-effort MIME type derivation from a file name's extension. Used by\r\n * `ImageEdit` to compose the `data:` URL for inline preview when only the\r\n * binary content (not the original Content-Type) is available client-side.\r\n * Returns <c>image/jpeg</c> as the catchall for image-shaped extensions —\r\n * the right MIME type for `&lt;img src&gt;` to render as long as the bytes\r\n * actually decode (browsers sniff and rebind anyway).\r\n */\r\nexport function guessImageMimeType(fileName: string): string {\r\n const ext = fileName.toLowerCase().split('.').pop() ?? '';\r\n switch (ext) {\r\n case 'png':\r\n return 'image/png';\r\n case 'gif':\r\n return 'image/gif';\r\n case 'webp':\r\n return 'image/webp';\r\n case 'svg':\r\n return 'image/svg+xml';\r\n case 'bmp':\r\n return 'image/bmp';\r\n case 'jpg':\r\n case 'jpeg':\r\n default:\r\n return 'image/jpeg';\r\n }\r\n}\r\n\r\n/** Decodes a UTF-8 byte array into a string. */\r\nexport function bytesToText(bytes: Uint8Array): string {\r\n return new TextDecoder('utf-8').decode(bytes);\r\n}\r\n\r\n/** Decodes a base64-encoded string and returns its UTF-8 text content. */\r\nexport function base64ToText(base64: string): string {\r\n return bytesToText(base64ToBytes(base64));\r\n}\r\n\r\n/** Lower-cased file extension, no dot. Empty string when none. */\r\nexport function getFileExtension(fileName: string | undefined | null): string {\r\n if (!fileName) return '';\r\n const lastDot = fileName.lastIndexOf('.');\r\n if (lastDot < 0 || lastDot === fileName.length - 1) return '';\r\n return fileName.slice(lastDot + 1).toLowerCase();\r\n}\r\n\r\n/**\r\n * Best-effort MIME type from a file name's extension across file kinds —\r\n * not just images. Returns <c>application/octet-stream</c> when the\r\n * extension is unknown, which the file-info renderer handles gracefully.\r\n */\r\nexport function getMimeFromFileName(fileName: string | undefined | null): string {\r\n const ext = getFileExtension(fileName);\r\n switch (ext) {\r\n case 'png':\r\n return 'image/png';\r\n case 'gif':\r\n return 'image/gif';\r\n case 'webp':\r\n return 'image/webp';\r\n case 'svg':\r\n return 'image/svg+xml';\r\n case 'bmp':\r\n return 'image/bmp';\r\n case 'jpg':\r\n case 'jpeg':\r\n return 'image/jpeg';\r\n case 'pdf':\r\n return 'application/pdf';\r\n case 'html':\r\n case 'htm':\r\n return 'text/html';\r\n case 'md':\r\n case 'markdown':\r\n return 'text/markdown';\r\n case 'json':\r\n return 'application/json';\r\n case 'xml':\r\n return 'application/xml';\r\n case 'csv':\r\n return 'text/csv';\r\n case 'txt':\r\n case 'log':\r\n return 'text/plain';\r\n case 'zip':\r\n return 'application/zip';\r\n default:\r\n return 'application/octet-stream';\r\n }\r\n}\r\n\r\n/**\r\n * Categorization for `FileViewer`'s renderer dispatch. Each value maps to\r\n * one branch of the viewer:\r\n *\r\n * - `image`: handed off to `ImageViewer`.\r\n * - `markdown`: tabbed Source/Preview view (highlighted source + rendered HTML).\r\n * - `text`: monospace, syntax-highlighted via Shiki when the language is known.\r\n * - `embedded`: rendered in an `<iframe>` — PDFs, HTML, plain text the browser\r\n * can display natively. Falls back to the download card if the renderer\r\n * can't produce a `src` (e.g. a `bytes` source over an extension we can't\r\n * confidently MIME-type).\r\n * - `zip`: tree view of archive contents on the left, nested `FileViewer`\r\n * on the right for whichever entry the user picks.\r\n * - `download`: file-info card with a download button.\r\n */\r\nexport type FileCategory = 'image' | 'markdown' | 'text' | 'embedded' | 'zip' | 'download';\r\n\r\n/**\r\n * Decides which `FileViewer` renderer handles a given file. Drives off\r\n * `mimeType` first (when supplied — most accurate), then falls back to the\r\n * file extension. Returns `'download'` for everything we can't confidently\r\n * render inline.\r\n */\r\nexport function categorizeFile({\r\n mimeType,\r\n fileName,\r\n}: {\r\n mimeType?: string | undefined;\r\n fileName?: string | undefined;\r\n}): FileCategory {\r\n const mime = (mimeType ?? '').toLowerCase();\r\n if (mime.startsWith('image/')) return 'image';\r\n if (mime === 'text/markdown' || mime === 'text/x-markdown') return 'markdown';\r\n if (mime === 'application/pdf') return 'embedded';\r\n if (mime === 'application/zip' || mime === 'application/x-zip-compressed') return 'zip';\r\n\r\n const ext = getFileExtension(fileName);\r\n if (IMAGE_EXTS.has(ext)) return 'image';\r\n if (MARKDOWN_EXTS.has(ext)) return 'markdown';\r\n if (ext === 'pdf') return 'embedded';\r\n if (ext === 'zip') return 'zip';\r\n if (TEXT_EXTS.has(ext)) return 'text';\r\n\r\n // Generic text MIME types when the extension didn't pin one.\r\n if (mime.startsWith('text/') || mime === 'application/json' || mime === 'application/xml') {\r\n return 'text';\r\n }\r\n\r\n return 'download';\r\n}\r\n\r\nconst IMAGE_EXTS = new Set(['png', 'gif', 'webp', 'svg', 'bmp', 'jpg', 'jpeg', 'ico', 'avif']);\r\nconst MARKDOWN_EXTS = new Set(['md', 'markdown']);\r\nconst TEXT_EXTS = new Set([\r\n 'txt', 'log', 'csv', 'tsv', 'json', 'xml', 'yaml', 'yml', 'toml', 'ini',\r\n 'js', 'jsx', 'mjs', 'cjs', 'ts', 'tsx', 'html', 'htm', 'css', 'scss', 'less',\r\n 'cs', 'vb', 'fs', 'razor', 'cshtml', 'vbhtml', 'csproj', 'vbproj', 'fsproj', 'sln',\r\n 'py', 'rb', 'go', 'rs', 'java', 'kt', 'swift', 'php', 'pl', 'lua',\r\n 'sql', 'sh', 'bash', 'ps1', 'cmd', 'bat',\r\n 'config', 'props', 'targets',\r\n]);\r\n\r\n/**\r\n * Maps a file extension to a highlight.js language id, narrowed to the\r\n * grammar set the FileViewer registers (xml / json / yaml / javascript /\r\n * typescript / csharp / css / markdown — the same languages the Blazor\r\n * `FileViewer.razor.ts` loads). Extensions outside that set collapse to\r\n * the nearest registered grammar (e.g. `scss`/`less` → `css`, `html`/\r\n * `razor`/`csproj` → `xml`, `jsx` → `javascript`) so they still get\r\n * sensible coloring, and anything unrecognized returns `'plaintext'`.\r\n *\r\n * `'plaintext'` is highlight.js's built-in no-op language; the FileViewer\r\n * treats it (and any id that isn't a registered grammar) as \"escape and\r\n * render without highlighting\", matching the Blazor viewer's fallback.\r\n */\r\nexport function getHighlightLanguage(fileName: string | undefined | null): string {\r\n const ext = getFileExtension(fileName);\r\n switch (ext) {\r\n case 'js':\r\n case 'mjs':\r\n case 'cjs':\r\n case 'jsx':\r\n return 'javascript';\r\n case 'ts':\r\n case 'tsx':\r\n return 'typescript';\r\n case 'json':\r\n return 'json';\r\n case 'html':\r\n case 'htm':\r\n case 'xml':\r\n case 'csproj':\r\n case 'vbproj':\r\n case 'fsproj':\r\n case 'config':\r\n case 'props':\r\n case 'targets':\r\n case 'razor':\r\n case 'cshtml':\r\n case 'vbhtml':\r\n return 'xml';\r\n case 'css':\r\n case 'scss':\r\n case 'less':\r\n return 'css';\r\n case 'yaml':\r\n case 'yml':\r\n return 'yaml';\r\n case 'cs':\r\n return 'csharp';\r\n case 'md':\r\n case 'markdown':\r\n return 'markdown';\r\n case 'csv':\r\n case 'tsv':\r\n case 'log':\r\n case 'txt':\r\n default:\r\n return 'plaintext';\r\n }\r\n}\r\n","import { useDialogService, useT } from '@powerportalspro/react';\r\nimport { useCallback, useMemo, useRef, useState } from 'react';\r\nimport type { FileColumnValue } from './use-column-edit.js';\r\nimport { EMPTY_GUID, readFileAsBase64 } from './file-helpers.js';\r\n\r\n/** Options consumed by {@link useFileUpload}. */\r\nexport interface UseFileUploadOptions {\r\n /** Setter that receives the uploaded file (typically `useFileColumn`'s `setValue`). */\r\n setValue: (value: FileColumnValue | null | undefined) => void;\r\n /**\r\n * Optional client-side max-file-size guard. Files exceeding the limit are\r\n * rejected with a console warning before any state updates. Server-side\r\n * <c>EnvironmentFileSettings.maxUploadSize</c> still applies on save\r\n * regardless.\r\n */\r\n maxFileSizeBytes?: number;\r\n /**\r\n * False when the surrounding edit is read-only or disabled — suppresses\r\n * click handling, drag-over highlighting, drop handling, and the input's\r\n * native enable state. Returns no-op handlers when off so the consumer\r\n * can spread them unconditionally.\r\n */\r\n enabled: boolean;\r\n /**\r\n * Component-name prefix used in the size-rejection console.warn. Helps\r\n * disambiguate \"FileEdit rejected …\" vs \"ImageEdit rejected …\" when both\r\n * are on a page.\r\n */\r\n warnPrefix: string;\r\n}\r\n\r\n/** Return shape of {@link useFileUpload}. */\r\nexport interface UseFileUploadResult {\r\n /** Ref to attach to the hidden `<input type=\"file\">`. */\r\n readonly inputRef: React.RefObject<HTMLInputElement | null>;\r\n /**\r\n * True while the user is dragging a file over the drop target. The\r\n * consumer typically uses this to swap a Griffel class for active-state\r\n * styling.\r\n */\r\n readonly isDragOver: boolean;\r\n /**\r\n * `onChange` handler for the hidden `<input type=\"file\">`. Reads the\r\n * picked file as base64 and calls {@link UseFileUploadOptions.setValue}.\r\n * Resets the input's value after each pick so re-selecting the same file\r\n * fires `onChange` again.\r\n */\r\n readonly onInputChange: (ev: React.ChangeEvent<HTMLInputElement>) => void;\r\n /**\r\n * Spreadable bag of handlers + ARIA attributes for the drop zone /\r\n * click target. Spread onto a `<div>` (or any focusable element) so it\r\n * becomes:\r\n * <list type=\"bullet\">\r\n * <item>Click-to-browse (clicks the hidden input).</item>\r\n * <item>Keyboard-activatable (Enter / Space trigger the input).</item>\r\n * <item>Drag-and-drop target — drops invoke the same upload path.</item>\r\n * </list>\r\n * No-op when `enabled` is false (read-only / disabled state).\r\n */\r\n readonly dropZoneHandlers: {\r\n readonly role: 'button';\r\n readonly tabIndex: number;\r\n readonly 'aria-disabled': boolean;\r\n readonly onClick: () => void;\r\n readonly onKeyDown: (ev: React.KeyboardEvent) => void;\r\n readonly onDragOver: (ev: React.DragEvent) => void;\r\n readonly onDragLeave: () => void;\r\n readonly onDrop: (ev: React.DragEvent) => void;\r\n };\r\n}\r\n\r\n/**\r\n * Shared file-upload behavior used by `FileEdit` and `ImageEdit`'s\r\n * preview branch — hidden `<input type=\"file\">`, click-to-browse,\r\n * keyboard activation, drag-and-drop. Both edits read the picked file\r\n * as base64 and write a fresh <c>FileColumnValue</c> with `EMPTY_GUID`\r\n * back through the column setter; the rest is component-specific\r\n * rendering on top.\r\n *\r\n * Hook-based (rather than render-prop) so consumers retain full control\r\n * of the JSX — `ImageEdit` wraps an `<img>` with these handlers, while\r\n * `FileEdit` wraps a styled drop zone.\r\n */\r\nexport function useFileUpload(options: UseFileUploadOptions): UseFileUploadResult {\r\n const { setValue, maxFileSizeBytes, enabled, warnPrefix } = options;\r\n const dialog = useDialogService();\r\n const t = useT();\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const [isDragOver, setIsDragOver] = useState(false);\r\n\r\n const handleFileSelected = useCallback(\r\n async (file: File) => {\r\n if (maxFileSizeBytes !== undefined && file.size > maxFileSizeBytes) {\r\n // User-facing error dialog using the same framework keys the\r\n // Blazor `FileInputColumnEdit` uses — keeps the message identical\r\n // across stacks and shares translations:\r\n // `app.messages.file-upload-max-file-size-error` body\r\n // `app.messages.file-upload-max-file-size-error-title` title\r\n // Console warning kept alongside as a developer breadcrumb.\r\n // eslint-disable-next-line no-console\r\n console.warn(\r\n `${warnPrefix}: rejected ${file.name} (${file.size} bytes) — exceeds maxFileSizeBytes (${maxFileSizeBytes}).`,\r\n );\r\n await dialog.showErrorAsync(\r\n t('app.messages.file-upload-max-file-size-error', [maxFileSizeBytes, file.size]),\r\n { title: t('app.messages.file-upload-max-file-size-error-title') },\r\n );\r\n return;\r\n }\r\n const base64 = await readFileAsBase64(file);\r\n setValue({\r\n fileName: file.name,\r\n id: EMPTY_GUID,\r\n value: base64,\r\n });\r\n },\r\n [dialog, maxFileSizeBytes, setValue, t, warnPrefix],\r\n );\r\n\r\n const onInputChange = useCallback(\r\n (ev: React.ChangeEvent<HTMLInputElement>) => {\r\n const file = ev.target.files?.[0];\r\n if (file) void handleFileSelected(file);\r\n // Reset so re-selecting the same file fires onChange again.\r\n ev.target.value = '';\r\n },\r\n [handleFileSelected],\r\n );\r\n\r\n const dropZoneHandlers = useMemo(\r\n () =>\r\n ({\r\n role: 'button',\r\n tabIndex: enabled ? 0 : -1,\r\n 'aria-disabled': !enabled,\r\n onClick: () => {\r\n if (enabled) inputRef.current?.click();\r\n },\r\n onKeyDown: (ev: React.KeyboardEvent) => {\r\n if (!enabled) return;\r\n if (ev.key === 'Enter' || ev.key === ' ') {\r\n ev.preventDefault();\r\n inputRef.current?.click();\r\n }\r\n },\r\n onDragOver: (ev: React.DragEvent) => {\r\n if (!enabled) return;\r\n ev.preventDefault();\r\n setIsDragOver(true);\r\n },\r\n onDragLeave: () => setIsDragOver(false),\r\n onDrop: (ev: React.DragEvent) => {\r\n if (!enabled) return;\r\n ev.preventDefault();\r\n setIsDragOver(false);\r\n const file = ev.dataTransfer.files?.[0];\r\n if (file) void handleFileSelected(file);\r\n },\r\n } as const),\r\n [enabled, handleFileSelected],\r\n );\r\n\r\n return { inputRef, isDragOver, onInputChange, dropZoneHandlers };\r\n}\r\n","import {\r\n Button,\r\n makeStyles,\r\n mergeClasses,\r\n Menu,\r\n MenuItemRadio,\r\n MenuList,\r\n MenuPopover,\r\n MenuTrigger,\r\n Toolbar,\r\n ToolbarButton,\r\n ToolbarDivider,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport {\r\n ArrowDownload20Regular,\r\n ArrowReset20Regular,\r\n ArrowRotateClockwise20Regular,\r\n ArrowRotateCounterclockwise20Regular,\r\n Checkmark20Filled,\r\n ChevronDown16Regular,\r\n Crop20Regular,\r\n Grid20Regular,\r\n FlipHorizontal20Regular,\r\n FlipVertical20Regular,\r\n FullScreenMaximize20Regular,\r\n FullScreenMinimize20Regular,\r\n Prohibited20Regular,\r\n ZoomIn20Regular,\r\n ZoomOut20Regular,\r\n} from '@fluentui/react-icons';\r\nimport {\r\n forwardRef,\r\n useCallback,\r\n useEffect,\r\n useImperativeHandle,\r\n useRef,\r\n useState,\r\n type CSSProperties,\r\n type KeyboardEvent as ReactKeyboardEvent,\r\n type MouseEvent,\r\n type PointerEvent,\r\n type ReactNode,\r\n} from 'react';\r\nimport { base64ToBytes, guessImageMimeType, triggerBlobDownload } from '../edits/base/file-helpers.js';\r\n\r\n/**\r\n * Discriminated source for an `ImageViewer`. Each kind owns the conversion\r\n * path that produces the `<img>` `src` attribute:\r\n *\r\n * - `url`: passed straight through.\r\n * - `base64`: rendered as a `data:<mime>;base64,<value>` URL — no allocation,\r\n * no cleanup, but bytes are duplicated through the parser.\r\n * - `bytes`: wrapped in a `Blob` and exposed via `URL.createObjectURL`. The\r\n * resulting `blob:` URL is revoked on cleanup so memory doesn't leak.\r\n */\r\nexport type ImageSource =\r\n | { kind: 'url'; value: string }\r\n | { kind: 'base64'; value: string; mimeType?: string; fileName?: string }\r\n | { kind: 'bytes'; value: Uint8Array; mimeType?: string; fileName?: string };\r\n\r\n/** Aspect-ratio constraint applied while the user draws a crop selection. */\r\nexport type CropAspect = 'free' | '1:1' | '4:3' | '16:9';\r\n\r\n/**\r\n * Identifies which edge or corner handle of the crop selection is being\r\n * dragged. All eight handles are rendered regardless of aspect ratio.\r\n *\r\n * Behavior under a locked aspect ratio:\r\n * - Corner handles ('ne', 'nw', 'se', 'sw') anchor at the diagonal-opposite\r\n * corner and run the dominant-axis trim (same as the initial selection drag).\r\n * - Edge handles ('n', 's', 'e', 'w') drive one dimension from the cursor and\r\n * scale the perpendicular dimension symmetrically from the rectangle's\r\n * center on that axis — so an N-handle drag changes height freely and width\r\n * grows out from the horizontal center.\r\n */\r\ntype ResizeHandleKind = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';\r\n\r\n/**\r\n * Result handed to <see cref=\"ImageViewerProps.onCropApplied\"/> when the user\r\n * commits a crop. The same payload covers two persistence styles:\r\n *\r\n * - `bytes` for direct binary handoff (e.g. uploading via fetch).\r\n * - `base64` (no `data:` prefix) for Dataverse `FileValue.value`-shaped writes.\r\n *\r\n * Output is always PNG in v1 — lossless but heavier than JPEG. Open to\r\n * making the format consumer-selectable if file size becomes an issue.\r\n */\r\nexport interface CropResult {\r\n bytes: Uint8Array;\r\n base64: string;\r\n mimeType: string;\r\n fileName: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface ImageViewerProps {\r\n /**\r\n * Image data to render. When null/undefined, `fallback` is rendered instead\r\n * (or nothing if no fallback is provided).\r\n */\r\n source: ImageSource | null | undefined;\r\n /** Accessible alt text. Defaults to the source's `fileName` if available. */\r\n alt?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n id?: string;\r\n /**\r\n * When true, wraps the image in a Fluent-tokened bordered frame matching\r\n * the read-only preview style used elsewhere (e.g. `ImageEdit`'s preview).\r\n * Default: false (bare `<img>`).\r\n */\r\n framed?: boolean;\r\n /**\r\n * When true, renders a toolbar above the image with rotate / zoom / reset\r\n * / fullscreen controls. Implies a wrapper element regardless of `framed`.\r\n * Drag-to-pan on the image becomes available when zoomed in past 1×.\r\n */\r\n toolbar?: boolean;\r\n /**\r\n * When true, exposes a download UX:\r\n *\r\n * - With `toolbar`, adds a download button to the toolbar.\r\n * - With `framed` (no toolbar), renders a download button overlay in the\r\n * corner.\r\n * - Without either, makes the image itself click-to-download.\r\n *\r\n * Inline `base64`/`bytes` sources download the bytes directly. `url`\r\n * sources use a same-origin anchor with the `download` attribute — works\r\n * cross-origin only when the server permits it via CORS/`Content-Disposition`.\r\n */\r\n downloadable?: boolean;\r\n /**\r\n * File name used when downloading and as the default `alt` text. Falls\r\n * back to the source's own `fileName` (when set), otherwise `\"image\"`.\r\n */\r\n fileName?: string;\r\n /**\r\n * Click handler on the `<img>`. Suppresses the click-to-download behavior\r\n * that `downloadable` would otherwise enable in non-wrapped mode.\r\n */\r\n onClick?: (event: MouseEvent<HTMLImageElement>) => void;\r\n /** Rendered when `source` is null/undefined. */\r\n fallback?: ReactNode;\r\n /**\r\n * When true (and `toolbar` is also true), enables crop interactions:\r\n *\r\n * - Right-click drag on the image draws a dashed selection rectangle\r\n * (with the current aspect ratio applied if not `'free'`).\r\n * - The toolbar gains an aspect-ratio menu and a Crop button.\r\n * - The Crop button rasterizes the selection to a PNG and fires\r\n * `onCropApplied`. The viewer then displays the cropped result.\r\n * - Reset clears the local crop and reverts to the original `source`.\r\n */\r\n croppable?: boolean;\r\n /**\r\n * Fires when the user commits a crop. The viewer keeps an internal\r\n * snapshot of the cropped bytes and renders them as the new image; the\r\n * consumer is free to also persist the bytes elsewhere (e.g. into a\r\n * Dataverse `FileValue` column via `useFileColumn().setValue`).\r\n *\r\n * Resetting the viewer clears its internal snapshot; any external state\r\n * the consumer set inside this callback is the consumer's to manage.\r\n */\r\n onCropApplied?: (result: CropResult) => void;\r\n /**\r\n * Fires whenever {@link ImageViewerHandle.isDirty} flips. Use this to drive\r\n * a Save button's enabled state without polling. The callback receives the\r\n * new dirty value.\r\n *\r\n * \"Dirty\" in v1 means a crop has been applied (the viewer is showing\r\n * bytes that differ from the `source` prop). Pure view transforms\r\n * (rotation, flip, zoom, pan) don't toggle dirty on their own — they\r\n * don't generate new bytes unless they're folded into a crop.\r\n */\r\n onDirtyChange?: (isDirty: boolean) => void;\r\n /**\r\n * When set together with `toolbar`, renders a green checkmark button at\r\n * the right end of the toolbar. The button is disabled while the viewer\r\n * is clean — there's nothing to save. Click handler is fire-and-forget;\r\n * the consumer typically calls {@link ImageViewerHandle.commit} from\r\n * here to bake the displayed image to bytes for persistence.\r\n *\r\n * Designed for the {@link ImageEdit} edit-mode flow (commit edits back\r\n * into the bound column). Standalone consumers can use it the same way.\r\n */\r\n onSave?: () => void | Promise<void>;\r\n /**\r\n * When set together with `toolbar`, renders a red prohibited-circle\r\n * button at the right end of the toolbar. Always enabled. Consumers\r\n * typically use this to exit edit mode and discard in-progress\r\n * transforms — the viewer doesn't auto-revert anything on its own;\r\n * remount it (e.g. via a `key`) or update the `source` prop to wipe\r\n * its internal state.\r\n */\r\n onCancel?: () => void;\r\n /**\r\n * Output format for the rasterised crop result (and the committed image\r\n * bytes from `commit()`). Defaults to `'auto'`, which derives the format\r\n * from the source filename's extension — `.jpg` / `.jpeg` stay as JPEG,\r\n * everything else (including unknown / no extension) emits PNG. Pin to\r\n * `'png'` or `'jpeg'` to override that derivation.\r\n */\r\n outputFormat?: 'png' | 'jpeg' | 'auto';\r\n /**\r\n * JPEG quality in `[0, 1]`. Ignored when `outputFormat` is `'png'` (PNG\r\n * is lossless and has no quality knob). Defaults to `0.9` — visually\r\n * indistinguishable from the original for most photos while still\r\n * saving a lot vs PNG.\r\n */\r\n outputQuality?: number;\r\n}\r\n\r\n/**\r\n * Imperative handle exposed via `ref`. Lets a parent inspect dirty state,\r\n * commit the displayed image to bytes for save, mark the viewer clean\r\n * after a successful save, or revert just the crop overlay.\r\n */\r\nexport interface ImageViewerHandle {\r\n /**\r\n * True when the displayed image differs from the consumer's last\r\n * `source` prop or `resetDirty()` baseline. Toggled by rotation, flip,\r\n * or crop. Pure view changes (zoom, pan) don't affect it.\r\n */\r\n readonly isDirty: boolean;\r\n /**\r\n * Bakes the currently-displayed image — including any rotation and\r\n * flips — into a PNG. Returns null when the viewer is clean (nothing\r\n * to save). Any in-progress crop selection that hasn't been committed\r\n * via the Crop button is intentionally ignored; click Crop first if\r\n * you want it baked in.\r\n */\r\n commit(): Promise<CropResult | null>;\r\n /**\r\n * Marks the viewer clean *without* changing the displayed image — sets\r\n * the dirty baseline to the current state. Use after a successful save\r\n * so the displayed (rotated / cropped / flipped) image becomes the new\r\n * \"saved\" state and future changes are measured against it.\r\n */\r\n resetDirty(): void;\r\n /**\r\n * Drops the local crop overlay only — reverts to the original `source`\r\n * prop's content. Rotation and flips are left as the user has them, so\r\n * if those changed since the last save, `isDirty` may stay true. Use\r\n * the toolbar's Reset button (or updating the `source` prop) for a\r\n * full revert.\r\n */\r\n revertCrop(): void;\r\n}\r\n\r\n// Zoom bounds + multiplicative step. Geometric stepping (× / ÷ STEP) keeps\r\n// the visual change consistent across the range — a linear additive step\r\n// would feel huge near 1× and tiny near 8×.\r\nconst ZOOM_MIN = 0.25;\r\nconst ZOOM_MAX = 8;\r\nconst ZOOM_STEP = 1.25;\r\n\r\n/**\r\n * Standalone image viewer. Accepts a URL, a base64 string, or a `Uint8Array`,\r\n * resolves it to an `<img>` `src`, and renders. Defaults to a bare `<img>` —\r\n * the optional `framed` / `toolbar` / `downloadable` props add chrome on\r\n * demand.\r\n *\r\n * For component-bound editing (drop zone, upload, validation, record-context\r\n * binding), use `ImageEdit` instead.\r\n */\r\nexport const ImageViewer = forwardRef<ImageViewerHandle, ImageViewerProps>(function ImageViewer(\r\n {\r\n source,\r\n alt,\r\n className,\r\n style,\r\n id,\r\n framed = false,\r\n toolbar = false,\r\n downloadable = false,\r\n fileName,\r\n onClick,\r\n fallback,\r\n croppable = false,\r\n onCropApplied,\r\n onDirtyChange,\r\n onSave,\r\n onCancel,\r\n outputFormat = 'auto',\r\n outputQuality = 0.9,\r\n },\r\n ref,\r\n) {\r\n const styles = useImageViewerStyles();\r\n\r\n // Local crop overlay. Once applied, the viewer renders this snapshot\r\n // instead of `source` until Reset is pressed. The original `source` prop\r\n // stays untouched so Reset always has somewhere to revert to.\r\n const [croppedSource, setCroppedSource] = useState<ImageSource | null>(null);\r\n const displaySource = croppedSource ?? source;\r\n\r\n const resolvedFileName = fileName ?? extractFileName(displaySource) ?? 'image';\r\n const resolvedAlt = alt ?? resolvedFileName;\r\n\r\n const src = useImageSrc(displaySource);\r\n\r\n // Crop is only meaningful with the toolbar present (no UI to apply it\r\n // otherwise). Treat the prop as `false` if `toolbar` is off.\r\n const cropEnabled = toolbar && croppable;\r\n\r\n // Transform state for the toolbar's rotate/zoom/pan controls. Kept around\r\n // even when `toolbar` is off — the cost is three idle useState hooks; the\r\n // alternative (gating hooks on a prop) violates the rules of hooks if the\r\n // prop ever changes.\r\n const [angle, setAngle] = useState(0);\r\n const [zoom, setZoom] = useState(1);\r\n const [pan, setPan] = useState({ x: 0, y: 0 });\r\n // Mirror flags. Both compose with `zoom` in the scale step of the\r\n // transform pipeline — `scale(zoom * flipX, zoom * flipY)` where flipX/Y\r\n // is -1 when set. Applied AFTER rotate in the CSS transform string, so\r\n // the flip is in the image's own axes (matches Photoshop / typical\r\n // image-editor expectation: flipping a 90°-rotated image flips along\r\n // the original horizontal/vertical, not the screen's).\r\n const [flipH, setFlipH] = useState(false);\r\n const [flipV, setFlipV] = useState(false);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n // Rule-of-thirds guide overlay. Off by default; toggled on/off via a\r\n // toolbar button only when `cropEnabled` (the guide is meaningless\r\n // without a selection rectangle to subdivide).\r\n const [showThirds, setShowThirds] = useState(false);\r\n\r\n // Baseline of \"saved/clean\" state. The viewer is dirty when current\r\n // angle/flips/croppedSource differ from these. Initially everything is at\r\n // defaults (no transforms, no crop) — matching a freshly-loaded source.\r\n // After a save, the consumer calls `resetDirty()` which sets baseline to\r\n // current, marking the viewer clean without changing what's displayed.\r\n // zoom/pan are excluded — they're pure view state, not modifications.\r\n const [baseline, setBaseline] = useState<{\r\n angle: number;\r\n flipH: boolean;\r\n flipV: boolean;\r\n croppedSource: ImageSource | null;\r\n }>({ angle: 0, flipH: false, flipV: false, croppedSource: null });\r\n\r\n const isDirty =\r\n angle !== baseline.angle ||\r\n flipH !== baseline.flipH ||\r\n flipV !== baseline.flipV ||\r\n croppedSource !== baseline.croppedSource;\r\n const wrapperRef = useRef<HTMLDivElement>(null);\r\n const imageContainerRef = useRef<HTMLDivElement>(null);\r\n const imgRef = useRef<HTMLImageElement>(null);\r\n\r\n // Crop UI state. The selection is in image-container-local coords (origin\r\n // at the container's top-left, including its own padding).\r\n const [aspectRatio, setAspectRatio] = useState<CropAspect>('free');\r\n const [selection, setSelection] = useState<{\r\n x: number;\r\n y: number;\r\n w: number;\r\n h: number;\r\n } | null>(null);\r\n // Mirror `selection` into a ref so the pointerdown handler reads its\r\n // CURRENT value rather than a value captured at handler-creation time.\r\n // Without this, a fast second-press right after a move-drag could see\r\n // the pre-drag rectangle in the closure and fail the inside-hit check —\r\n // falling through to \"start a new select drag\" and silently replacing\r\n // the just-moved selection with a tiny one at the press point.\r\n // Pairing this ref with stable callback deps (no `selection` in the\r\n // deps) also keeps the pointerdown handler identity steady across the\r\n // dozens of selection updates that fire during a drag, which avoids\r\n // re-binding churn on the React event listener.\r\n const selectionRef = useRef(selection);\r\n selectionRef.current = selection;\r\n\r\n // Snapshot of the transform values from the previous render. The\r\n // transform-following effect below diffs this against the current\r\n // values to decide whether the selection needs to be re-projected\r\n // (selection follows the image when the user zooms / pans / rotates /\r\n // flips while a selection is up).\r\n const prevTransformRef = useRef({ angle, zoom, pan, flipH, flipV });\r\n\r\n // Tagged-union drag state: at any moment the user is either panning the\r\n // image (zoom > 1, no crop), drawing a brand-new crop selection (crop\r\n // mode, pointer-down outside any existing selection), or moving the\r\n // existing crop selection (crop mode, pointer-down inside it). One drag\r\n // at a time — a second buttondown mid-drag is ignored.\r\n const dragRef = useRef<\r\n | {\r\n kind: 'pan';\r\n pointerId: number;\r\n startX: number;\r\n startY: number;\r\n startPan: { x: number; y: number };\r\n }\r\n | {\r\n kind: 'select';\r\n pointerId: number;\r\n // Container-local origin captured at pointerdown.\r\n originX: number;\r\n originY: number;\r\n // Set true once the pointer has moved past the click-vs-drag\r\n // threshold. Used by the contextmenu handler to know whether to\r\n // suppress the menu — a plain right-click without drag should still\r\n // do nothing surprising; we only intercept after a real drag.\r\n moved: boolean;\r\n // Snapshot of the displayed image's bounds at drag-start (same\r\n // shape and purpose as the `move`-kind field above). The drawn\r\n // selection is clamped to the intersection of these bounds and\r\n // the container so the resulting rectangle never extends into\r\n // the letterbox area, even if the user starts the drag there.\r\n // Null when the image isn't measurable (mid-load) — clamp falls\r\n // back to the container.\r\n imageBounds: { x: number; y: number; w: number; h: number } | null;\r\n }\r\n | {\r\n kind: 'move';\r\n pointerId: number;\r\n // Container-local pointer position when the move started.\r\n originX: number;\r\n originY: number;\r\n // Selection rectangle as it was at drag start. Each pointermove\r\n // applies the cumulative pointer delta to *this* (rather than the\r\n // current selection), so we don't accumulate float drift across\r\n // dozens of move events.\r\n startSelection: { x: number; y: number; w: number; h: number };\r\n // Snapshot of the displayed image's bounds in container-local\r\n // coords at drag-start. The clamp window is the intersection of\r\n // this and the container — selection can't be dragged into the\r\n // letterbox area (outside the image) or the off-screen area\r\n // (outside the container, when the image is zoomed/panned past\r\n // the visible viewport). Snapshotting (vs reading on every\r\n // pointermove) keeps the constraint stable for the duration of\r\n // the drag even if some other transform changed mid-drag.\r\n // Null when the image isn't measurable (mid-load) — clamp falls\r\n // back to the container.\r\n imageBounds: { x: number; y: number; w: number; h: number } | null;\r\n }\r\n | {\r\n kind: 'resize';\r\n pointerId: number;\r\n handle: ResizeHandleKind;\r\n // Selection rectangle as it was at drag-start. The new rectangle\r\n // is computed from this + the live pointer position, so a slow\r\n // wobble doesn't drift the rectangle off its anchor edges.\r\n startSelection: { x: number; y: number; w: number; h: number };\r\n // Image bounds (intersected with container at clamp time). Same\r\n // shape and snapshotting rationale as the `move` kind above.\r\n imageBounds: { x: number; y: number; w: number; h: number } | null;\r\n // Aspect ratio in effect at drag-start. Snapshotted so a\r\n // mid-drag change can't yank the constraint sideways (the\r\n // aspect-ratio menu is in the same toolbar — the user can't\r\n // realistically be dragging and changing it at the same time,\r\n // but snapshotting is the safer default).\r\n aspectValue: number | null;\r\n }\r\n | null\r\n >(null);\r\n const [isDragging, setIsDragging] = useState(false);\r\n\r\n // Mirror the latest transform state into refs so native event listeners\r\n // (which are attached once and live across renders) can read current\r\n // values without capturing stale state. Inline assignment runs on every\r\n // render, which is fine — refs don't trigger re-renders.\r\n const zoomRef = useRef(zoom);\r\n const panRef = useRef(pan);\r\n const angleRef = useRef(angle);\r\n const flipHRef = useRef(flipH);\r\n const flipVRef = useRef(flipV);\r\n zoomRef.current = zoom;\r\n panRef.current = pan;\r\n angleRef.current = angle;\r\n flipHRef.current = flipH;\r\n flipVRef.current = flipV;\r\n\r\n // Reset transform state whenever the rendered src changes — the prior\r\n // image's rotation/zoom/pan/flips would be visually meaningless on a\r\n // new image. Baseline catches up too, so a swapped-in source starts\r\n // clean.\r\n useEffect(() => {\r\n setAngle(0);\r\n setZoom(1);\r\n setPan({ x: 0, y: 0 });\r\n setFlipH(false);\r\n setFlipV(false);\r\n // Drop any in-flight selection too — it was anchored to the previous\r\n // image content; carrying it through to the new image would let the\r\n // transform-following effect below drag a stale rectangle into the\r\n // new image's coordinate space.\r\n setSelection(null);\r\n setBaseline({ angle: 0, flipH: false, flipV: false, croppedSource: null });\r\n }, [src]);\r\n\r\n // Also clear the local crop overlay when the prop `source` changes — the\r\n // overlay was a derivative of a now-stale source. (The `[src]` effect\r\n // above also fires for this case since `src` is derived from\r\n // displaySource = croppedSource ?? source, but we want the clear to be\r\n // unconditional on prop change even if croppedSource hadn't yet flowed.)\r\n useEffect(() => {\r\n setCroppedSource(null);\r\n }, [source]);\r\n\r\n // Selection follows the image: when zoom / pan / rotate / flip changes\r\n // while a selection is up, re-project the selection so it stays\r\n // anchored to the same image content. Round-trip via natural-pixel\r\n // coords (via the OLD transform) and back (via the NEW transform).\r\n // Pure no-op when transforms didn't actually change in value (e.g.\r\n // a `setPan({x:0,y:0})` call producing a fresh object with the same\r\n // values), and when there's no selection or no measurable image yet.\r\n useEffect(() => {\r\n const prev = prevTransformRef.current;\r\n const cur = { angle, zoom, pan, flipH, flipV };\r\n const transformChanged =\r\n prev.angle !== cur.angle ||\r\n prev.zoom !== cur.zoom ||\r\n prev.pan.x !== cur.pan.x ||\r\n prev.pan.y !== cur.pan.y ||\r\n prev.flipH !== cur.flipH ||\r\n prev.flipV !== cur.flipV;\r\n\r\n if (transformChanged) {\r\n const sel = selectionRef.current;\r\n const imgEl = imgRef.current;\r\n if (sel && imgEl) {\r\n const natural = containerRectToImageNatural(\r\n sel,\r\n imgEl,\r\n prev.angle,\r\n prev.zoom,\r\n prev.pan,\r\n prev.flipH,\r\n prev.flipV,\r\n );\r\n if (natural) {\r\n const reprojected = imageNaturalRectToContainer(\r\n natural,\r\n imgEl,\r\n cur.angle,\r\n cur.zoom,\r\n cur.pan,\r\n cur.flipH,\r\n cur.flipV,\r\n );\r\n if (reprojected) {\r\n setSelection(reprojected);\r\n }\r\n }\r\n }\r\n }\r\n\r\n prevTransformRef.current = cur;\r\n }, [angle, zoom, pan, flipH, flipV]);\r\n\r\n // Notify consumer of dirty transitions. Ref-around-callback so an\r\n // unstable callback identity from the parent doesn't double-fire the\r\n // effect on every render.\r\n const onDirtyChangeRef = useRef(onDirtyChange);\r\n onDirtyChangeRef.current = onDirtyChange;\r\n useEffect(() => {\r\n onDirtyChangeRef.current?.(isDirty);\r\n }, [isDirty]);\r\n\r\n // Keep `isFullscreen` in sync with the document's fullscreen element. The\r\n // user can also exit fullscreen with Esc, which doesn't go through our\r\n // handler — the `fullscreenchange` event fires either way.\r\n useEffect(() => {\r\n if (!toolbar) return;\r\n const handler = () => {\r\n setIsFullscreen(document.fullscreenElement === wrapperRef.current);\r\n };\r\n document.addEventListener('fullscreenchange', handler);\r\n return () => document.removeEventListener('fullscreenchange', handler);\r\n }, [toolbar]);\r\n\r\n // Wheel-to-zoom on the image container. We attach a native listener with\r\n // `{ passive: false }` so we can `preventDefault()` to suppress page\r\n // scroll — React's synthetic `onWheel` is registered passive in modern\r\n // versions, which means a synthetic-event `preventDefault()` would warn\r\n // and no-op.\r\n //\r\n // Zoom anchors at the cursor: after a zoom step, the image-local point\r\n // under the cursor stays under the cursor. The pan adjustment falls out\r\n // of `pan_new = (1 - r) * cursorRelCenter + r * pan_prev` where\r\n // `r = zoom_new / zoom_prev` — works for any rotation because uniform\r\n // scale commutes with rotation.\r\n useEffect(() => {\r\n if (!toolbar) return;\r\n const node = imageContainerRef.current;\r\n if (!node) return;\r\n\r\n const handler = (e: WheelEvent) => {\r\n e.preventDefault();\r\n // Smooth multiplier per wheel delta. The constant is tuned so a\r\n // typical mouse-wheel notch (deltaY ~100) gives roughly the same\r\n // ×1.25 step the toolbar buttons apply.\r\n const factor = Math.exp(-e.deltaY * 0.00223);\r\n const prevZoom = zoomRef.current;\r\n const nextZoom = Math.min(Math.max(prevZoom * factor, ZOOM_MIN), ZOOM_MAX);\r\n if (nextZoom === prevZoom) return;\r\n\r\n setZoom(nextZoom);\r\n\r\n if (nextZoom <= 1) {\r\n // Pulled back to fit-or-smaller — recenter so the image isn't\r\n // stuck off-frame in a useless position.\r\n setPan({ x: 0, y: 0 });\r\n return;\r\n }\r\n\r\n const rect = node.getBoundingClientRect();\r\n const cursorRelCenterX = e.clientX - (rect.left + rect.width / 2);\r\n const cursorRelCenterY = e.clientY - (rect.top + rect.height / 2);\r\n const r = nextZoom / prevZoom;\r\n const prevPan = panRef.current;\r\n setPan({\r\n x: (1 - r) * cursorRelCenterX + r * prevPan.x,\r\n y: (1 - r) * cursorRelCenterY + r * prevPan.y,\r\n });\r\n };\r\n\r\n node.addEventListener('wheel', handler, { passive: false });\r\n return () => node.removeEventListener('wheel', handler);\r\n }, [toolbar]);\r\n\r\n // Auto-focus the wrapper on edit-mode entry AND on every fullscreen\r\n // transition so the keyboard shortcuts keep working. Two cases:\r\n // - Edit-mode entry: focus typically sits on whatever button\r\n // triggered edit mode (e.g. ImageEdit's pencil button), which\r\n // then unmounts — leaving focus on `<body>` and the wrapper's\r\n // keydown listeners unreachable.\r\n // - Fullscreen enter/exit: the browser routinely drops focus to\r\n // `<body>` during the transition (the fullscreen toolbar button\r\n // that triggered it doesn't always retain focus through the\r\n // change). Body-level keydowns don't pass through the wrapper,\r\n // so neither our capture-phase nor bubble-phase listener fires.\r\n // Refocusing the wrapper on `isFullscreen` change brings the\r\n // event path back to us.\r\n useEffect(() => {\r\n if (!toolbar) return;\r\n wrapperRef.current?.focus();\r\n }, [toolbar, isFullscreen]);\r\n\r\n // Capture-phase keydown listener for arrow keys. Fluent UI's Toolbar\r\n // uses a roving-tabindex pattern that consumes arrow keys to navigate\r\n // between buttons when a toolbar button has focus — that bubble-phase\r\n // handler would swallow the event before our wrapper-level\r\n // `onKeyDown` (also bubble-phase) ever fires. Listening in the capture\r\n // phase on the wrapper means we see the event on its way DOWN to the\r\n // target, before any descendant handler runs; preventDefault +\r\n // stopPropagation then keep the toolbar's nav from also firing.\r\n useEffect(() => {\r\n if (!toolbar || !cropEnabled) return;\r\n const wrapper = wrapperRef.current;\r\n if (!wrapper) return;\r\n const handler = (e: KeyboardEvent): void => {\r\n if (e.altKey || e.ctrlKey || e.metaKey) return;\r\n const key = e.key;\r\n if (\r\n key !== 'ArrowUp' &&\r\n key !== 'ArrowDown' &&\r\n key !== 'ArrowLeft' &&\r\n key !== 'ArrowRight'\r\n ) {\r\n return;\r\n }\r\n const sel = selectionRef.current;\r\n if (!sel) return;\r\n e.preventDefault();\r\n e.stopPropagation();\r\n const step = e.shiftKey ? 10 : 1;\r\n const dx = key === 'ArrowLeft' ? -step : key === 'ArrowRight' ? step : 0;\r\n const dy = key === 'ArrowUp' ? -step : key === 'ArrowDown' ? step : 0;\r\n // Use refs (not closure-captured state) so the listener — attached\r\n // once when toolbar/cropEnabled flip on — always sees current\r\n // transform values.\r\n const bounds = imgRef.current\r\n ? getDisplayedImageBounds(\r\n imgRef.current,\r\n angleRef.current,\r\n zoomRef.current,\r\n panRef.current,\r\n flipHRef.current,\r\n flipVRef.current,\r\n )\r\n : null;\r\n const rect = imageContainerRef.current?.getBoundingClientRect();\r\n const cw = rect?.width ?? Number.POSITIVE_INFINITY;\r\n const ch = rect?.height ?? Number.POSITIVE_INFINITY;\r\n const minX = Math.max(0, bounds ? bounds.x : 0);\r\n const minY = Math.max(0, bounds ? bounds.y : 0);\r\n const maxX = Math.min(cw, bounds ? bounds.x + bounds.w : cw);\r\n const maxY = Math.min(ch, bounds ? bounds.y + bounds.h : ch);\r\n setSelection({\r\n x: Math.max(minX, Math.min(sel.x + dx, maxX - sel.w)),\r\n y: Math.max(minY, Math.min(sel.y + dy, maxY - sel.h)),\r\n w: sel.w,\r\n h: sel.h,\r\n });\r\n };\r\n wrapper.addEventListener('keydown', handler, true);\r\n return () => wrapper.removeEventListener('keydown', handler, true);\r\n }, [toolbar, cropEnabled]);\r\n\r\n const handleDownload = useCallback(() => {\r\n // Download what the user is currently looking at — the cropped version\r\n // wins over the original when set, matching the visual state.\r\n if (!displaySource) return;\r\n void downloadSource(displaySource, resolvedFileName);\r\n }, [displaySource, resolvedFileName]);\r\n\r\n // Angle accumulates without modulo. CSS `rotate(...deg)` accepts arbitrary\r\n // values, and the `transition` always animates the short way between\r\n // current and next — wrapping at 360 would make a 270→0 step animate\r\n // backwards through 270°.\r\n const handleRotateCw = useCallback(() => setAngle((a) => a + 90), []);\r\n const handleRotateCcw = useCallback(() => setAngle((a) => a - 90), []);\r\n const handleZoomIn = useCallback(() => setZoom((z) => Math.min(z * ZOOM_STEP, ZOOM_MAX)), []);\r\n const handleZoomOut = useCallback(() => {\r\n // Zooming out below 1× would shrink the image inside the frame for no\r\n // real benefit, but we let it down to ZOOM_MIN to mirror standard viewer\r\n // behavior. Pan is force-zeroed at <= 1× so the image stays centered.\r\n setZoom((z) => {\r\n const next = Math.max(z / ZOOM_STEP, ZOOM_MIN);\r\n if (next <= 1) setPan({ x: 0, y: 0 });\r\n return next;\r\n });\r\n }, []);\r\n const handleReset = useCallback(() => {\r\n // Snap to the nearest 360 multiple instead of literal 0 — if the user\r\n // is at 270° clockwise, that's a +90° animation home, not -270°. Keeps\r\n // the reset visually shortest no matter how much they've accumulated.\r\n // Baseline catches up to the same snapped angle so isDirty flips to\r\n // false (current matches baseline matches \"no transforms\").\r\n const snappedAngle = Math.round(angle / 360) * 360;\r\n setAngle(snappedAngle);\r\n setZoom(1);\r\n setPan({ x: 0, y: 0 });\r\n setFlipH(false);\r\n setFlipV(false);\r\n setSelection(null);\r\n setCroppedSource(null);\r\n setBaseline({ angle: snappedAngle, flipH: false, flipV: false, croppedSource: null });\r\n }, [angle]);\r\n const handleFlipH = useCallback(() => setFlipH((f) => !f), []);\r\n const handleFlipV = useCallback(() => setFlipV((f) => !f), []);\r\n const handleToggleFullscreen = useCallback(() => {\r\n if (document.fullscreenElement) {\r\n void document.exitFullscreen();\r\n } else {\r\n void wrapperRef.current?.requestFullscreen();\r\n }\r\n }, []);\r\n\r\n // Numeric aspect ratio for the current `aspectRatio` state. `'free'` →\r\n // `null` (no constraint).\r\n const aspectValue = aspectRatioToNumber(aspectRatio);\r\n\r\n const onContainerPointerDown = useCallback(\r\n (e: PointerEvent<HTMLDivElement>) => {\r\n if (!toolbar) return;\r\n if (e.button !== 0) return;\r\n // Already dragging — don't let a second buttondown derail it.\r\n if (dragRef.current) return;\r\n\r\n if (cropEnabled) {\r\n // Crop mode: left-drag normally draws a new selection rectangle.\r\n // EXCEPT: if the press lands inside an existing selection, treat\r\n // it as a \"move the selection\" gesture instead. Pan-by-drag is\r\n // disabled in crop mode so the gesture isn't ambiguous; users\r\n // can still reposition a zoomed view via wheel-anchored zoom.\r\n //\r\n // preventDefault stops the browser from starting its own\r\n // text-selection drag on the surrounding container — that\r\n // gesture would race our pointer capture and intermittently\r\n // bail out mid-move (the browser pulls capture for its own\r\n // selection drag, our pointermove handlers stop seeing\r\n // updates, the rectangle freezes mid-drag).\r\n e.preventDefault();\r\n const rect = e.currentTarget.getBoundingClientRect();\r\n const localX = e.clientX - rect.left;\r\n const localY = e.clientY - rect.top;\r\n const sel = selectionRef.current;\r\n\r\n if (\r\n sel &&\r\n localX >= sel.x &&\r\n localX <= sel.x + sel.w &&\r\n localY >= sel.y &&\r\n localY <= sel.y + sel.h\r\n ) {\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n dragRef.current = {\r\n kind: 'move',\r\n pointerId: e.pointerId,\r\n originX: localX,\r\n originY: localY,\r\n startSelection: sel,\r\n imageBounds: imgRef.current\r\n ? getDisplayedImageBounds(imgRef.current, angle, zoom, pan, flipH, flipV)\r\n : null,\r\n };\r\n return;\r\n }\r\n\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n dragRef.current = {\r\n kind: 'select',\r\n pointerId: e.pointerId,\r\n originX: localX,\r\n originY: localY,\r\n moved: false,\r\n imageBounds: imgRef.current\r\n ? getDisplayedImageBounds(imgRef.current, angle, zoom, pan, flipH, flipV)\r\n : null,\r\n };\r\n setSelection({ x: localX, y: localY, w: 0, h: 0 });\r\n return;\r\n }\r\n\r\n if (zoom > 1) {\r\n // Non-crop mode + zoomed: left-drag pans the image. At zoom ≤ 1 a\r\n // pan would do nothing, so we let the click pass through.\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n dragRef.current = {\r\n kind: 'pan',\r\n pointerId: e.pointerId,\r\n startX: e.clientX,\r\n startY: e.clientY,\r\n startPan: pan,\r\n };\r\n setIsDragging(true);\r\n }\r\n },\r\n [toolbar, cropEnabled, zoom, pan, angle, flipH, flipV],\r\n );\r\n\r\n // Pointerdown handler attached to each resize handle. Reads which handle\r\n // it fired on from `data-handle`, captures the pointer to the IMAGE\r\n // CONTAINER (not the handle itself, so the existing container-level\r\n // pointermove/up handlers continue to drive the drag), and seeds a\r\n // `resize` drag in the same dragRef union the move/select drags use.\r\n // stopPropagation prevents the event from bubbling to the container's\r\n // own onPointerDown — without it, the container would (correctly) see\r\n // the press as \"inside the selection\" and start a `move` drag too,\r\n // racing the resize.\r\n const onHandlePointerDown = useCallback(\r\n (e: PointerEvent<HTMLDivElement>) => {\r\n if (e.button !== 0) return;\r\n if (dragRef.current) return;\r\n const handle = e.currentTarget.dataset.handle as ResizeHandleKind | undefined;\r\n if (!handle) return;\r\n\r\n const containerEl = imageContainerRef.current;\r\n const sel = selectionRef.current;\r\n if (!containerEl || !sel) return;\r\n\r\n e.stopPropagation();\r\n e.preventDefault();\r\n containerEl.setPointerCapture(e.pointerId);\r\n dragRef.current = {\r\n kind: 'resize',\r\n pointerId: e.pointerId,\r\n handle,\r\n startSelection: sel,\r\n imageBounds: imgRef.current\r\n ? getDisplayedImageBounds(imgRef.current, angle, zoom, pan, flipH, flipV)\r\n : null,\r\n aspectValue,\r\n };\r\n },\r\n [angle, zoom, pan, flipH, flipV, aspectValue],\r\n );\r\n\r\n const onContainerPointerMove = useCallback(\r\n (e: PointerEvent<HTMLDivElement>) => {\r\n const drag = dragRef.current;\r\n if (!drag || drag.pointerId !== e.pointerId) return;\r\n\r\n if (drag.kind === 'pan') {\r\n setPan({\r\n x: drag.startPan.x + (e.clientX - drag.startX),\r\n y: drag.startPan.y + (e.clientY - drag.startY),\r\n });\r\n return;\r\n }\r\n\r\n if (drag.kind === 'move') {\r\n // Translate the snapshot rectangle by the cumulative pointer\r\n // delta. Width/height stay fixed — we never resize during a move\r\n // drag. Clamp to the intersection of (image bounds, container\r\n // bounds): the selection can't be dragged into the letterbox\r\n // around the image, and (when zoomed past 1×) it also can't go\r\n // past the container's visible edges.\r\n const rect = e.currentTarget.getBoundingClientRect();\r\n const localX = e.clientX - rect.left;\r\n const localY = e.clientY - rect.top;\r\n const dx = localX - drag.originX;\r\n const dy = localY - drag.originY;\r\n const { x, y, w, h } = drag.startSelection;\r\n\r\n const ib = drag.imageBounds;\r\n const minX = Math.max(0, ib ? ib.x : 0);\r\n const minY = Math.max(0, ib ? ib.y : 0);\r\n const maxX = Math.min(rect.width, ib ? ib.x + ib.w : rect.width);\r\n const maxY = Math.min(rect.height, ib ? ib.y + ib.h : rect.height);\r\n\r\n // If the selection is wider/taller than the clamp window (e.g.\r\n // it was drawn before a zoom-out shrunk the image), the\r\n // upper-bound expression `maxX - w` can fall below `minX`.\r\n // `Math.max(minX, ...)` then anchors the selection to the left/\r\n // top edge of the window — no NaN, no off-screen jumps.\r\n const newX = Math.max(minX, Math.min(x + dx, maxX - w));\r\n const newY = Math.max(minY, Math.min(y + dy, maxY - h));\r\n setSelection({ x: newX, y: newY, w, h });\r\n return;\r\n }\r\n\r\n if (drag.kind === 'resize') {\r\n // Edge / corner resize. The non-moving edges of the start\r\n // rectangle stay anchored; the moving edge(s) follow the pointer.\r\n // Image-bound clamping is applied per-edge so the resulting\r\n // rectangle never extends into the letterbox or off-screen area.\r\n //\r\n // With a locked aspect ratio:\r\n // - Corner handles: dominant axis (whichever yields the larger box\r\n // at the locked ratio) wins, and the perpendicular dimension is\r\n // derived from it. Anchor at the diagonal-opposite corner.\r\n // - Edge handles: drive the parallel axis from the cursor; the\r\n // perpendicular axis scales symmetrically from the rectangle's\r\n // center on that axis.\r\n const rect = e.currentTarget.getBoundingClientRect();\r\n const localX = e.clientX - rect.left;\r\n const localY = e.clientY - rect.top;\r\n\r\n const ib = drag.imageBounds;\r\n const minX = Math.max(0, ib ? ib.x : 0);\r\n const minY = Math.max(0, ib ? ib.y : 0);\r\n const maxX = Math.min(rect.width, ib ? ib.x + ib.w : rect.width);\r\n const maxY = Math.min(rect.height, ib ? ib.y + ib.h : rect.height);\r\n\r\n const start = drag.startSelection;\r\n const startRight = start.x + start.w;\r\n const startBottom = start.y + start.h;\r\n const startCenterX = (start.x + startRight) / 2;\r\n const startCenterY = (start.y + startBottom) / 2;\r\n\r\n const movesLeft = drag.handle === 'w' || drag.handle === 'nw' || drag.handle === 'sw';\r\n const movesRight = drag.handle === 'e' || drag.handle === 'ne' || drag.handle === 'se';\r\n const movesTop = drag.handle === 'n' || drag.handle === 'nw' || drag.handle === 'ne';\r\n const movesBottom = drag.handle === 's' || drag.handle === 'sw' || drag.handle === 'se';\r\n\r\n // 1px floor so the rectangle can't collapse to / cross past zero\r\n // — that would flip the moving edge past the anchored one.\r\n let newLeft = movesLeft ? Math.max(minX, Math.min(localX, startRight - 1)) : start.x;\r\n let newRight = movesRight ? Math.min(maxX, Math.max(localX, start.x + 1)) : startRight;\r\n let newTop = movesTop ? Math.max(minY, Math.min(localY, startBottom - 1)) : start.y;\r\n let newBottom = movesBottom ? Math.min(maxY, Math.max(localY, start.y + 1)) : startBottom;\r\n\r\n let newW = newRight - newLeft;\r\n let newH = newBottom - newTop;\r\n\r\n const isCornerDrag = (movesLeft || movesRight) && (movesTop || movesBottom);\r\n const isEdgeDrag = !isCornerDrag && (movesLeft || movesRight || movesTop || movesBottom);\r\n\r\n if (drag.aspectValue !== null && isCornerDrag) {\r\n // Anchor = the corner diagonally opposite the dragged one.\r\n const anchorX = movesLeft ? startRight : start.x;\r\n const anchorY = movesTop ? startBottom : start.y;\r\n const aspect = drag.aspectValue;\r\n\r\n // Pick the dominant axis: whichever yields the larger box at\r\n // the locked ratio.\r\n if (newW / aspect > newH) {\r\n newH = newW / aspect;\r\n } else {\r\n newW = newH * aspect;\r\n }\r\n\r\n // Clamp the constrained pair to the image-bounds window\r\n // measured from the anchor — proportionally shrink both so the\r\n // ratio survives.\r\n const maxAllowedW = movesLeft ? anchorX - minX : maxX - anchorX;\r\n const maxAllowedH = movesTop ? anchorY - minY : maxY - anchorY;\r\n if (newW > maxAllowedW) {\r\n const scale = maxAllowedW / newW;\r\n newW *= scale;\r\n newH *= scale;\r\n }\r\n if (newH > maxAllowedH) {\r\n const scale = maxAllowedH / newH;\r\n newW *= scale;\r\n newH *= scale;\r\n }\r\n\r\n newLeft = movesLeft ? anchorX - newW : anchorX;\r\n newTop = movesTop ? anchorY - newH : anchorY;\r\n } else if (drag.aspectValue !== null && isEdgeDrag) {\r\n const aspect = drag.aspectValue;\r\n if (movesTop || movesBottom) {\r\n // N/S edge: height drives, width scales symmetrically from\r\n // the rectangle's horizontal center. Cap width by the\r\n // available space on either side of the center, then back-\r\n // derive height so the locked aspect still holds at the edge.\r\n const maxHalfW = Math.min(startCenterX - minX, maxX - startCenterX);\r\n let h = newH;\r\n let w = h * aspect;\r\n if (w / 2 > maxHalfW) {\r\n w = maxHalfW * 2;\r\n h = w / aspect;\r\n if (movesTop) newTop = newBottom - h;\r\n else newBottom = newTop + h;\r\n }\r\n newLeft = startCenterX - w / 2;\r\n newRight = startCenterX + w / 2;\r\n newW = w;\r\n newH = h;\r\n } else {\r\n // E/W edge: width drives, height scales symmetrically from\r\n // the rectangle's vertical center.\r\n const maxHalfH = Math.min(startCenterY - minY, maxY - startCenterY);\r\n let w = newW;\r\n let h = w / aspect;\r\n if (h / 2 > maxHalfH) {\r\n h = maxHalfH * 2;\r\n w = h * aspect;\r\n if (movesLeft) newLeft = newRight - w;\r\n else newRight = newLeft + w;\r\n }\r\n newTop = startCenterY - h / 2;\r\n newBottom = startCenterY + h / 2;\r\n newW = w;\r\n newH = h;\r\n }\r\n }\r\n\r\n setSelection({ x: newLeft, y: newTop, w: newW, h: newH });\r\n return;\r\n }\r\n\r\n // Selection drag.\r\n const rect = e.currentTarget.getBoundingClientRect();\r\n const localX = e.clientX - rect.left;\r\n const localY = e.clientY - rect.top;\r\n let dx = localX - drag.originX;\r\n let dy = localY - drag.originY;\r\n\r\n // Track whether we've moved past the drag-vs-click threshold so the\r\n // contextmenu suppression can decide whether to fire.\r\n if (!drag.moved && Math.abs(dx) + Math.abs(dy) > 3) {\r\n drag.moved = true;\r\n }\r\n\r\n // Aspect-ratio constraint: pick the dominant axis (whichever yields a\r\n // larger box at the locked ratio) and clamp the other to match. Signs\r\n // follow the user's drag direction so the rectangle keeps growing\r\n // toward the pointer.\r\n if (aspectValue !== null) {\r\n const absX = Math.abs(dx);\r\n const absY = Math.abs(dy);\r\n if (absX / aspectValue > absY) {\r\n const newAbsY = absX / aspectValue;\r\n dy = dy === 0 ? newAbsY : Math.sign(dy) * newAbsY;\r\n } else {\r\n const newAbsX = absY * aspectValue;\r\n dx = dx === 0 ? newAbsX : Math.sign(dx) * newAbsX;\r\n }\r\n }\r\n\r\n // Normalize negative dimensions to top-left + positive width/height.\r\n let x = drag.originX;\r\n let y = drag.originY;\r\n let w = dx;\r\n let h = dy;\r\n if (w < 0) {\r\n x += w;\r\n w = -w;\r\n }\r\n if (h < 0) {\r\n y += h;\r\n h = -h;\r\n }\r\n\r\n // Clamp to the intersection of (image bounds, container bounds) —\r\n // selection edges can't extend past the visible image. If the user\r\n // started the drag in the letterbox area, the parts of the resulting\r\n // rectangle that landed outside the image get cropped here, leaving\r\n // only the inside-image portion.\r\n const ib = drag.imageBounds;\r\n const minX = Math.max(0, ib ? ib.x : 0);\r\n const minY = Math.max(0, ib ? ib.y : 0);\r\n const maxX = Math.min(rect.width, ib ? ib.x + ib.w : rect.width);\r\n const maxY = Math.min(rect.height, ib ? ib.y + ib.h : rect.height);\r\n if (x < minX) {\r\n w -= minX - x;\r\n x = minX;\r\n }\r\n if (y < minY) {\r\n h -= minY - y;\r\n y = minY;\r\n }\r\n w = Math.min(w, maxX - x);\r\n h = Math.min(h, maxY - y);\r\n // Negative width/height from very off-axis drags collapse to zero —\r\n // pointerup's size-floor check (w/h < 5 → discard) cleans up.\r\n if (w < 0) w = 0;\r\n if (h < 0) h = 0;\r\n\r\n setSelection({ x, y, w, h });\r\n },\r\n [aspectValue],\r\n );\r\n\r\n const onContainerPointerUp = useCallback((e: PointerEvent<HTMLDivElement>) => {\r\n const drag = dragRef.current;\r\n if (!drag || drag.pointerId !== e.pointerId) return;\r\n e.currentTarget.releasePointerCapture(e.pointerId);\r\n\r\n if (drag.kind === 'pan') {\r\n setIsDragging(false);\r\n } else if (drag.kind === 'move') {\r\n // Move-drag finished — selection has already been translated via\r\n // pointermove. A click-without-drag inside the selection ends up\r\n // here too; intentionally a no-op (we don't clear or resize the\r\n // selection just because the user tapped on it).\r\n } else if (drag.kind === 'resize') {\r\n // Resize-drag finished — selection's edges are wherever the last\r\n // pointermove left them. No size-floor check: the pointermove math\r\n // already keeps both dimensions ≥ 1px.\r\n } else {\r\n // Selection drag finished. If it never crossed the click-vs-drag\r\n // threshold, treat as a stray right-click and clear any in-progress\r\n // tiny rectangle. Otherwise keep the rectangle for the Crop button.\r\n if (!drag.moved) {\r\n setSelection(null);\r\n } else {\r\n setSelection((prev) => {\r\n if (!prev || prev.w < 5 || prev.h < 5) return null;\r\n return prev;\r\n });\r\n }\r\n }\r\n dragRef.current = null;\r\n }, []);\r\n\r\n // Aspect-ratio change: clear any in-progress selection so the user\r\n // re-draws against the new constraint instead of keeping a now-mismatched\r\n // rectangle.\r\n const handleAspectChange = useCallback((next: CropAspect) => {\r\n setAspectRatio(next);\r\n setSelection(null);\r\n }, []);\r\n\r\n const handleCrop = useCallback(async () => {\r\n if (!selection || !imgRef.current || !displaySource) return;\r\n if (selection.w < 1 || selection.h < 1) return;\r\n const result = await renderCrop({\r\n imageEl: imgRef.current,\r\n selection,\r\n angle,\r\n zoom,\r\n pan,\r\n flipH,\r\n flipV,\r\n sourceFileName: extractFileName(displaySource) ?? 'image',\r\n outputFormat,\r\n outputQuality,\r\n });\r\n if (!result) return;\r\n setCroppedSource({\r\n kind: 'bytes',\r\n value: result.bytes,\r\n mimeType: result.mimeType,\r\n fileName: result.fileName,\r\n });\r\n setSelection(null);\r\n onCropApplied?.(result);\r\n }, [selection, angle, zoom, pan, flipH, flipV, displaySource, onCropApplied, outputFormat, outputQuality]);\r\n\r\n // Bubble-phase keyboard shortcuts for everything *except* arrow keys\r\n // (those are handled by the capture-phase listener above — see the\r\n // `useEffect` near the wheel listener for why). Esc/Enter cancel-or-\r\n // commit, +/-/0 zoom, Delete/Backspace clear the selection. All routes\r\n // preventDefault on the keys they actually handle so the browser's\r\n // defaults (e.g. Backspace = browser back) don't fire.\r\n const onWrapperKeyDown = useCallback(\r\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\r\n if (!toolbar) return;\r\n // Modifier-key combinations (Ctrl/Alt/Meta) belong to the browser /\r\n // OS / consumer app — don't shadow them here.\r\n if (e.altKey || e.ctrlKey || e.metaKey) return;\r\n\r\n const sel = selectionRef.current;\r\n\r\n switch (e.key) {\r\n case 'Escape':\r\n if (onCancel) {\r\n e.preventDefault();\r\n onCancel();\r\n }\r\n return;\r\n case 'Enter':\r\n // Enter prefers the most-edit-ey thing on screen: if there's a\r\n // committable selection, that's a Crop; otherwise it's a Save.\r\n if (sel && sel.w >= 5 && sel.h >= 5) {\r\n e.preventDefault();\r\n void handleCrop();\r\n } else if (onSave && isDirty) {\r\n e.preventDefault();\r\n void onSave();\r\n }\r\n return;\r\n case '+':\r\n case '=':\r\n e.preventDefault();\r\n handleZoomIn();\r\n return;\r\n case '-':\r\n case '_':\r\n e.preventDefault();\r\n handleZoomOut();\r\n return;\r\n case '0':\r\n e.preventDefault();\r\n setZoom(1);\r\n setPan({ x: 0, y: 0 });\r\n return;\r\n case 'Delete':\r\n case 'Backspace':\r\n if (sel) {\r\n e.preventDefault();\r\n setSelection(null);\r\n }\r\n return;\r\n }\r\n },\r\n [toolbar, onCancel, onSave, isDirty, handleCrop, handleZoomIn, handleZoomOut],\r\n );\r\n\r\n // Imperative handle for parent components: inspect dirty state, commit\r\n // the current displayed image to bytes, mark clean (post-save), or\r\n // revert just the crop overlay.\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n isDirty,\r\n resetDirty: () => {\r\n // Adopt the current state as the new clean baseline. Image stays\r\n // displayed as-is; isDirty flips to false.\r\n setBaseline({ angle, flipH, flipV, croppedSource });\r\n },\r\n commit: async () => {\r\n if (!isDirty) return null;\r\n if (!imgRef.current) return null;\r\n // Bake the displayed image's current transforms (rotation + flips)\r\n // to PNG bytes. The image element's `naturalWidth/Height` already\r\n // reflect the displayed source — when croppedSource is set, that's\r\n // the cropped image's natural size; when not, the original's.\r\n // Selection (if any) is intentionally ignored here — the user\r\n // would click Crop first if they wanted that selection in the\r\n // committed bytes.\r\n return await renderBakedImage({\r\n imageEl: imgRef.current,\r\n angle,\r\n flipH,\r\n flipV,\r\n sourceFileName: extractFileName(displaySource) ?? 'image',\r\n outputFormat,\r\n outputQuality,\r\n });\r\n },\r\n revertCrop: () => {\r\n // Drop the local crop overlay only. Rotation/flip stay where the\r\n // user has them; if they were also dirty, isDirty stays true.\r\n setCroppedSource(null);\r\n },\r\n }),\r\n [isDirty, angle, flipH, flipV, croppedSource, displaySource, outputFormat, outputQuality],\r\n );\r\n\r\n if (!source || !src) {\r\n return <>{fallback ?? null}</>;\r\n }\r\n\r\n const wrapped = framed || toolbar;\r\n const wantsClickDownload = downloadable && !wrapped && !onClick;\r\n const imgClickHandler = onClick ?? (wantsClickDownload ? () => handleDownload() : undefined);\r\n\r\n // Compose translate/rotate/scale in screen-space order: rotate the image\r\n // first (in its own frame), then scale it, then translate on screen.\r\n // Flip signs are folded into the scale factor so the mirroring lives in\r\n // the image's own axes (post-rotate in CSS terms).\r\n const scaleX = zoom * (flipH ? -1 : 1);\r\n const scaleY = zoom * (flipV ? -1 : 1);\r\n const transform = toolbar\r\n ? `translate(${pan.x}px, ${pan.y}px) rotate(${angle}deg) scale(${scaleX}, ${scaleY})`\r\n : undefined;\r\n\r\n const imgStyle: CSSProperties | undefined = (() => {\r\n if (!toolbar && !wrapped) return style;\r\n if (!toolbar) return undefined;\r\n return {\r\n transform,\r\n // Drop the easing while the user is mid-drag; otherwise pan would lag\r\n // a frame behind the pointer.\r\n transition: isDragging ? 'none' : 'transform 120ms ease-out',\r\n // Pointer events pass through the image to the container so the\r\n // container's pan/select handlers see the pointer. Cursor styling\r\n // lives on the container too.\r\n pointerEvents: 'none',\r\n };\r\n })();\r\n\r\n const img = (\r\n <img\r\n ref={imgRef}\r\n id={!wrapped ? id : undefined}\r\n src={src}\r\n alt={resolvedAlt}\r\n className={mergeClasses(\r\n wrapped ? styles.framedImage : styles.bareImage,\r\n isFullscreen && styles.framedImageFullscreen,\r\n wantsClickDownload && styles.clickable,\r\n !wrapped && className,\r\n )}\r\n style={imgStyle}\r\n onClick={imgClickHandler}\r\n // Pointer handlers live on the imageContainer (not the img) so left-\r\n // pan and right-select work whether the cursor is over the image or\r\n // its surrounding letterbox area.\r\n draggable={false}\r\n />\r\n );\r\n\r\n if (!wrapped) return img;\r\n\r\n // `angle % 360 !== 0` so the Reset button greys out once the rotation has\r\n // landed back on a multiple of 360 — we only ever add ±90, so the modulo\r\n // is exact and float drift isn't a concern.\r\n const isResettable =\r\n angle % 360 !== 0 ||\r\n zoom !== 1 ||\r\n pan.x !== 0 ||\r\n pan.y !== 0 ||\r\n flipH ||\r\n flipV ||\r\n croppedSource !== null ||\r\n selection !== null;\r\n\r\n // Live natural-pixel dimensions of the selection, shown in a small badge\r\n // anchored at the selection's top-left. Re-derived on every render that\r\n // has a selection so the readout tracks the rectangle through drags,\r\n // resizes, zooms, rotations, and flips. Cheap — four-corner inverse\r\n // transform via the helper we already have.\r\n const selectionNaturalDims = (() => {\r\n if (!selection || !imgRef.current) return null;\r\n const nat = containerRectToImageNatural(\r\n selection,\r\n imgRef.current,\r\n angle,\r\n zoom,\r\n pan,\r\n flipH,\r\n flipV,\r\n );\r\n if (!nat) return null;\r\n return { w: Math.max(0, Math.round(nat.w)), h: Math.max(0, Math.round(nat.h)) };\r\n })();\r\n\r\n return (\r\n <div\r\n ref={wrapperRef}\r\n id={id}\r\n className={mergeClasses(\r\n styles.wrapper,\r\n isFullscreen && styles.wrapperFullscreen,\r\n className,\r\n )}\r\n style={style}\r\n // Only focusable in edit mode — keyboard shortcuts have no meaning\r\n // in the read-only / framed-without-toolbar variants.\r\n tabIndex={toolbar ? 0 : undefined}\r\n onKeyDown={toolbar ? onWrapperKeyDown : undefined}\r\n >\r\n {toolbar && (\r\n <Toolbar size=\"small\" aria-label={`${resolvedFileName} toolbar`} className={styles.toolbar}>\r\n <ToolbarButton\r\n aria-label=\"Rotate 90° counter-clockwise\"\r\n icon={<ArrowRotateCounterclockwise20Regular />}\r\n onClick={handleRotateCcw}\r\n />\r\n <ToolbarButton\r\n aria-label=\"Rotate 90° clockwise\"\r\n icon={<ArrowRotateClockwise20Regular />}\r\n onClick={handleRotateCw}\r\n />\r\n <ToolbarButton\r\n aria-label={flipH ? 'Unflip horizontally' : 'Flip horizontally'}\r\n icon={<FlipHorizontal20Regular />}\r\n {...(flipH ? { appearance: 'primary' as const } : {})}\r\n onClick={handleFlipH}\r\n />\r\n <ToolbarButton\r\n aria-label={flipV ? 'Unflip vertically' : 'Flip vertically'}\r\n icon={<FlipVertical20Regular />}\r\n {...(flipV ? { appearance: 'primary' as const } : {})}\r\n onClick={handleFlipV}\r\n />\r\n <ToolbarButton\r\n aria-label=\"Zoom in\"\r\n icon={<ZoomIn20Regular />}\r\n disabled={zoom >= ZOOM_MAX}\r\n onClick={handleZoomIn}\r\n />\r\n <ToolbarButton\r\n aria-label=\"Zoom out\"\r\n icon={<ZoomOut20Regular />}\r\n disabled={zoom <= ZOOM_MIN}\r\n onClick={handleZoomOut}\r\n />\r\n <ToolbarButton\r\n aria-label=\"Reset view\"\r\n icon={<ArrowReset20Regular />}\r\n disabled={!isResettable}\r\n onClick={handleReset}\r\n />\r\n {cropEnabled && (\r\n <>\r\n <ToolbarDivider />\r\n <Menu\r\n checkedValues={{ aspect: [aspectRatio] }}\r\n onCheckedValueChange={(_e, data) => {\r\n const next = data.checkedItems[0] as CropAspect | undefined;\r\n if (next) handleAspectChange(next);\r\n }}\r\n // In fullscreen, the popover defaults to portaling into\r\n // `document.body`, which the browser hides while another\r\n // element holds fullscreen — the menu would open invisibly\r\n // off-screen. Mounting it inside the wrapper (which IS the\r\n // fullscreen element) keeps it inside the visible viewport.\r\n {...(isFullscreen && wrapperRef.current\r\n ? { mountNode: wrapperRef.current }\r\n : {})}\r\n >\r\n <MenuTrigger disableButtonEnhancement>\r\n <ToolbarButton\r\n aria-label={`Crop aspect ratio: ${aspectRatio}`}\r\n icon={<ChevronDown16Regular />}\r\n >\r\n {aspectRatioLabel(aspectRatio)}\r\n </ToolbarButton>\r\n </MenuTrigger>\r\n <MenuPopover>\r\n <MenuList>\r\n <MenuItemRadio name=\"aspect\" value=\"free\">\r\n Free\r\n </MenuItemRadio>\r\n <MenuItemRadio name=\"aspect\" value=\"1:1\">\r\n Square (1:1)\r\n </MenuItemRadio>\r\n <MenuItemRadio name=\"aspect\" value=\"4:3\">\r\n 4:3\r\n </MenuItemRadio>\r\n <MenuItemRadio name=\"aspect\" value=\"16:9\">\r\n 16:9\r\n </MenuItemRadio>\r\n </MenuList>\r\n </MenuPopover>\r\n </Menu>\r\n <ToolbarButton\r\n aria-label={showThirds ? 'Hide rule-of-thirds guide' : 'Show rule-of-thirds guide'}\r\n icon={<Grid20Regular />}\r\n {...(showThirds ? { appearance: 'primary' as const } : {})}\r\n onClick={() => setShowThirds((s) => !s)}\r\n />\r\n <ToolbarButton\r\n aria-label=\"Crop to selection\"\r\n icon={<Crop20Regular />}\r\n disabled={selection === null}\r\n onClick={() => void handleCrop()}\r\n />\r\n </>\r\n )}\r\n <ToolbarDivider />\r\n <ToolbarButton\r\n aria-label={isFullscreen ? 'Exit full screen' : 'Enter full screen'}\r\n icon={isFullscreen ? <FullScreenMinimize20Regular /> : <FullScreenMaximize20Regular />}\r\n onClick={handleToggleFullscreen}\r\n />\r\n {downloadable && (\r\n <ToolbarButton\r\n aria-label={`Download ${resolvedFileName}`}\r\n icon={<ArrowDownload20Regular />}\r\n onClick={handleDownload}\r\n />\r\n )}\r\n {(onSave || onCancel) && <ToolbarDivider />}\r\n {onCancel && (\r\n // Red prohibited-circle = \"discard / exit\". Color comes from a\r\n // palette token so it stays semantically red across light/dark\r\n // theme switches and consumer accent customizations.\r\n <ToolbarButton\r\n aria-label=\"Cancel\"\r\n icon={<Prohibited20Regular className={styles.cancelIcon} />}\r\n onClick={onCancel}\r\n />\r\n )}\r\n {onSave && (\r\n // Filled green checkmark = \"commit changes\". Disabled while the\r\n // viewer is clean so the consumer doesn't fire a no-op commit\r\n // (matches the `commit() returns null when !isDirty` contract).\r\n <ToolbarButton\r\n aria-label=\"Save\"\r\n icon={<Checkmark20Filled className={styles.saveIcon} />}\r\n disabled={!isDirty}\r\n onClick={() => void onSave()}\r\n />\r\n )}\r\n </Toolbar>\r\n )}\r\n <div\r\n ref={imageContainerRef}\r\n className={mergeClasses(\r\n styles.imageContainer,\r\n isFullscreen && styles.imageContainerFullscreen,\r\n )}\r\n onPointerDown={toolbar ? onContainerPointerDown : undefined}\r\n onPointerMove={toolbar ? onContainerPointerMove : undefined}\r\n onPointerUp={toolbar ? onContainerPointerUp : undefined}\r\n onPointerCancel={toolbar ? onContainerPointerUp : undefined}\r\n // Apply pan-cursor on the container so the cursor visibly tracks the\r\n // drag mode regardless of whether the pointer is over the image or\r\n // the surrounding letterbox. Crosshair signals crop mode; grab/\r\n // grabbing signals pan-by-drag in non-crop mode when zoomed.\r\n style={\r\n cropEnabled\r\n ? { cursor: 'crosshair' }\r\n : toolbar && zoom > 1\r\n ? { cursor: isDragging ? 'grabbing' : 'grab' }\r\n : undefined\r\n }\r\n >\r\n {img}\r\n {selection && (\r\n // Dashed selection rectangle over the image. The huge spread\r\n // box-shadow dims everything outside the rectangle inside the\r\n // container — clipped by the container's overflow:hidden.\r\n <div\r\n className={styles.selectionOverlay}\r\n style={{\r\n left: `${selection.x}px`,\r\n top: `${selection.y}px`,\r\n width: `${selection.w}px`,\r\n height: `${selection.h}px`,\r\n // Match the image's 120ms ease-out (see `imgStyle.transition`)\r\n // so a transform-driven re-projection animates in lockstep\r\n // with the image. Disabled while a drag is active so user\r\n // drags track the cursor 1:1 instead of trailing 120ms\r\n // behind. dragRef is a ref rather than state, but every\r\n // pointermove (which is what changes the selection during\r\n // a drag) calls setSelection — that triggers a re-render\r\n // which reads the current dragRef value.\r\n transition: dragRef.current\r\n ? 'none'\r\n : 'left 120ms ease-out, top 120ms ease-out, width 120ms ease-out, height 120ms ease-out',\r\n }}\r\n >\r\n {/* Rule-of-thirds guide. Two faint vertical + two faint\r\n horizontal lines at the 1/3 and 2/3 marks of the\r\n selection's W/H. Toggled by the toolbar grid button.\r\n `pointer-events: none` so the lines never intercept\r\n pointer events meant for the overlay/handles. */}\r\n {showThirds && (\r\n <div className={styles.thirdsGuide} aria-hidden=\"true\">\r\n <div className={styles.thirdsLineV1} />\r\n <div className={styles.thirdsLineV2} />\r\n <div className={styles.thirdsLineH1} />\r\n <div className={styles.thirdsLineH2} />\r\n </div>\r\n )}\r\n {/* Natural-pixel dimension readout. Anchored just above the\r\n selection's top-left so it doesn't crowd the resize\r\n handles. `pointer-events: none` keeps it out of the\r\n drag/move hit-testing entirely. */}\r\n {selectionNaturalDims && (\r\n <div className={styles.dimensionBadge} aria-hidden=\"true\">\r\n {selectionNaturalDims.w} × {selectionNaturalDims.h} px\r\n </div>\r\n )}\r\n {/* Resize handles. Corners always; edges only when the\r\n aspect ratio is `'free'` (an edge-only drag couldn't\r\n satisfy a locked ratio without effectively becoming a\r\n corner drag). Each handle is hit-tested individually\r\n via its `data-handle` attribute, and its onPointerDown\r\n stops propagation so the move-drag handler on the\r\n surrounding overlay doesn't also fire. */}\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleNW)}\r\n data-handle=\"nw\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: top-left\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleNE)}\r\n data-handle=\"ne\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: top-right\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleSE)}\r\n data-handle=\"se\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: bottom-right\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleSW)}\r\n data-handle=\"sw\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: bottom-left\"\r\n />\r\n {/* Edge handles rendered for all aspect modes. Under a locked\r\n aspect ratio, the resize math scales the perpendicular axis\r\n symmetrically from the rectangle's center (see the resize\r\n branch in onContainerPointerMove). */}\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleN)}\r\n data-handle=\"n\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: top edge\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleS)}\r\n data-handle=\"s\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: bottom edge\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleE)}\r\n data-handle=\"e\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: right edge\"\r\n />\r\n <div\r\n className={mergeClasses(styles.resizeHandle, styles.handleW)}\r\n data-handle=\"w\"\r\n onPointerDown={onHandlePointerDown}\r\n aria-label=\"Resize: left edge\"\r\n />\r\n </div>\r\n )}\r\n {downloadable && !toolbar && (\r\n <Button\r\n appearance=\"subtle\"\r\n size=\"small\"\r\n icon={<ArrowDownload20Regular />}\r\n aria-label={`Download ${resolvedFileName}`}\r\n className={styles.downloadButton}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n handleDownload();\r\n }}\r\n />\r\n )}\r\n </div>\r\n </div>\r\n );\r\n});\r\n\r\nfunction extractFileName(source: ImageSource | null | undefined): string | undefined {\r\n if (!source) return undefined;\r\n if (source.kind === 'url') {\r\n // Pull the trailing path segment off the URL, ignoring query/hash. We do\r\n // this lazily — only when no explicit `fileName` was supplied.\r\n try {\r\n const url = new URL(source.value, 'http://_resolve');\r\n const last = url.pathname.split('/').filter(Boolean).pop();\r\n return last ? decodeURIComponent(last) : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n }\r\n return source.fileName;\r\n}\r\n\r\nfunction resolveMimeType(source: ImageSource): string {\r\n if (source.kind === 'url') return 'image/*';\r\n if (source.mimeType) return source.mimeType;\r\n if (source.fileName) return guessImageMimeType(source.fileName);\r\n return 'image/jpeg';\r\n}\r\n\r\n/**\r\n * Resolves a source to an `<img>`-ready URL. For `bytes` sources, allocates\r\n * an object URL synchronously during render and revokes it on the next\r\n * source change or on unmount.\r\n *\r\n * The bytes-source allocation is done at render time (not in a useEffect)\r\n * specifically so the returned `src` is never transiently null between a\r\n * source change and the next effect commit. The previous effect-driven\r\n * implementation left `src` null for one render after a bytes-source swap\r\n * (e.g. just after `<Crop>` set `croppedSource`); the JSX's\r\n * `if (!source || !src)` early-return then unmounted the wrapper for that\r\n * one render, and the browser exited fullscreen because the fullscreen\r\n * element left the DOM. Synchronous allocation closes that gap.\r\n */\r\nfunction useImageSrc(source: ImageSource | null | undefined): string | null {\r\n // Track the source we last processed so we can detect changes during\r\n // render. Track the live blob URL so we can revoke it when source\r\n // changes again or the cleanup effect runs.\r\n const lastSourceRef = useRef<ImageSource | null | undefined>(undefined);\r\n const bytesUrlRef = useRef<string | null>(null);\r\n\r\n const isBytes = source != null && source.kind === 'bytes';\r\n\r\n // Two reasons to (re)allocate:\r\n // - Source identity changed — the normal path.\r\n // - Strict-mode dev simulates an unmount-remount and our cleanup\r\n // nulled the URL ref despite source not changing. Re-allocate so\r\n // the next ref read is live.\r\n if (source !== lastSourceRef.current || (isBytes && bytesUrlRef.current === null)) {\r\n if (bytesUrlRef.current !== null) {\r\n URL.revokeObjectURL(bytesUrlRef.current);\r\n bytesUrlRef.current = null;\r\n }\r\n if (isBytes && source) {\r\n const blob = new Blob([source.value.buffer as ArrayBuffer], {\r\n type: resolveMimeType(source),\r\n });\r\n bytesUrlRef.current = URL.createObjectURL(blob);\r\n }\r\n lastSourceRef.current = source;\r\n }\r\n\r\n // Final cleanup on unmount. Revoke the live URL and null the ref so\r\n // strict-mode's simulated remount hits the re-allocation branch above\r\n // on the next render.\r\n useEffect(() => {\r\n return () => {\r\n if (bytesUrlRef.current !== null) {\r\n URL.revokeObjectURL(bytesUrlRef.current);\r\n bytesUrlRef.current = null;\r\n }\r\n };\r\n }, []);\r\n\r\n // Final src derivation. Not memoized — base64's data-URL concat is the\r\n // only meaningful cost, and consumer-side memoization of `source` (e.g.\r\n // ImageEdit's useMemo) keeps re-renders cheap enough. A useMemo here\r\n // would introduce a stale-read hazard against `bytesUrlRef` (the\r\n // strict-mode branch above can rewrite it without a source change).\r\n if (!source) return null;\r\n if (source.kind === 'url') return source.value || null;\r\n if (source.kind === 'base64')\r\n return source.value ? `data:${resolveMimeType(source)};base64,${source.value}` : null;\r\n return bytesUrlRef.current;\r\n}\r\n\r\n/**\r\n * Axis-aligned bounding box of the *displayed image* inside its container,\r\n * in container-local coords. Accounts for `object-fit: contain`\r\n * letterboxing AND the current rotate/zoom/pan/flip transform applied via\r\n * CSS. Returns null when the image isn't measurable yet (mid-load —\r\n * `naturalWidth/Height` is 0).\r\n *\r\n * Used by the move-drag handler so the crop selection can't be dragged\r\n * into the letterbox bars surrounding the image.\r\n *\r\n * For non-axis-aligned rotations (e.g. 45°) the AABB is larger than the\r\n * actual rotated rectangle — selection can still extend into the empty\r\n * corners. Acceptable trade-off; a perfect rotated-polygon clamp would\r\n * be considerably more code for an edge case (the toolbar only emits\r\n * 90° increments today).\r\n */\r\nfunction getDisplayedImageBounds(\r\n imageEl: HTMLImageElement,\r\n angle: number,\r\n zoom: number,\r\n pan: { x: number; y: number },\r\n flipH: boolean,\r\n flipV: boolean,\r\n): { x: number; y: number; w: number; h: number } | null {\r\n const layoutW = imageEl.offsetWidth;\r\n const layoutH = imageEl.offsetHeight;\r\n const naturalW = imageEl.naturalWidth;\r\n const naturalH = imageEl.naturalHeight;\r\n if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;\r\n\r\n // object-fit:contain math — image fits inside the layout box without\r\n // distortion, leaving letterbox bars on whichever axis isn't tight.\r\n const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);\r\n const displayW = naturalW * containScale;\r\n const displayH = naturalH * containScale;\r\n\r\n // Image element's center in container-local coords (the container is\r\n // the offsetParent — it's `position: relative`).\r\n const imgCenterX = imageEl.offsetLeft + layoutW / 2;\r\n const imgCenterY = imageEl.offsetTop + layoutH / 2;\r\n\r\n // Replicate the on-screen CSS transform (scale → rotate → translate)\r\n // applied to each of the four image corners (image-center-local), then\r\n // take the AABB of the transformed corners.\r\n const halfW = displayW / 2;\r\n const halfH = displayH / 2;\r\n const sx = zoom * (flipH ? -1 : 1);\r\n const sy = zoom * (flipV ? -1 : 1);\r\n const rad = (angle * Math.PI) / 180;\r\n const cos = Math.cos(rad);\r\n const sin = Math.sin(rad);\r\n\r\n const corners: ReadonlyArray<readonly [number, number]> = [\r\n [-halfW, -halfH],\r\n [halfW, -halfH],\r\n [halfW, halfH],\r\n [-halfW, halfH],\r\n ];\r\n\r\n let minX = Infinity;\r\n let maxX = -Infinity;\r\n let minY = Infinity;\r\n let maxY = -Infinity;\r\n\r\n for (const [cx, cy] of corners) {\r\n const x1 = cx * sx;\r\n const y1 = cy * sy;\r\n const x2 = x1 * cos - y1 * sin;\r\n const y2 = x1 * sin + y1 * cos;\r\n const x3 = x2 + pan.x + imgCenterX;\r\n const y3 = y2 + pan.y + imgCenterY;\r\n if (x3 < minX) minX = x3;\r\n if (x3 > maxX) maxX = x3;\r\n if (y3 < minY) minY = y3;\r\n if (y3 > maxY) maxY = y3;\r\n }\r\n\r\n return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };\r\n}\r\n\r\n/**\r\n * Convert a rectangle in container-local coords to natural-image-pixel\r\n * coords, given the transform that produced the container view. Inverse\r\n * of the on-screen CSS transform pipeline:\r\n * container = (imgCenter + pan) + rotate(scale·flip · (display − displayCenter))\r\n * Solved for natural pixels.\r\n *\r\n * Used by the transform-following effect: a selection is converted to\r\n * natural coords using the OLD transform, then converted back via\r\n * `imageNaturalRectToContainer` using the NEW transform. The result is\r\n * a selection that anchors to the same image content across zoom, pan,\r\n * rotate, and flip changes. Returns null when the image isn't\r\n * measurable (mid-load).\r\n *\r\n * Both helpers convert all four corners independently and take the AABB.\r\n * For 90°-multiple rotations (the only kind the toolbar emits) the AABB\r\n * equals the rotated rectangle exactly. For non-axis-aligned rotations\r\n * (not currently exposed) the AABB would be conservative — selections\r\n * would grow into the rotated rectangle's empty corners.\r\n */\r\nfunction containerRectToImageNatural(\r\n rect: { x: number; y: number; w: number; h: number },\r\n imageEl: HTMLImageElement,\r\n angle: number,\r\n zoom: number,\r\n pan: { x: number; y: number },\r\n flipH: boolean,\r\n flipV: boolean,\r\n): { x: number; y: number; w: number; h: number } | null {\r\n const layoutW = imageEl.offsetWidth;\r\n const layoutH = imageEl.offsetHeight;\r\n const naturalW = imageEl.naturalWidth;\r\n const naturalH = imageEl.naturalHeight;\r\n if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;\r\n\r\n const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);\r\n const imgCenterX = imageEl.offsetLeft + layoutW / 2;\r\n const imgCenterY = imageEl.offsetTop + layoutH / 2;\r\n\r\n const sx = zoom * (flipH ? -1 : 1);\r\n const sy = zoom * (flipV ? -1 : 1);\r\n if (sx === 0 || sy === 0) return null;\r\n\r\n const rad = (angle * Math.PI) / 180;\r\n const cos = Math.cos(rad);\r\n const sin = Math.sin(rad);\r\n\r\n const corners: ReadonlyArray<readonly [number, number]> = [\r\n [rect.x, rect.y],\r\n [rect.x + rect.w, rect.y],\r\n [rect.x + rect.w, rect.y + rect.h],\r\n [rect.x, rect.y + rect.h],\r\n ];\r\n\r\n let minX = Infinity;\r\n let maxX = -Infinity;\r\n let minY = Infinity;\r\n let maxY = -Infinity;\r\n\r\n for (const [cx, cy] of corners) {\r\n // Inverse pipeline: undo container offset → undo pan → derotate →\r\n // unscale → centered display coords → natural-pixel top-left coords.\r\n const px = cx - imgCenterX - pan.x;\r\n const py = cy - imgCenterY - pan.y;\r\n const rx = px * cos + py * sin;\r\n const ry = -px * sin + py * cos;\r\n const tx = rx / sx;\r\n const ty = ry / sy;\r\n const nx = tx / containScale + naturalW / 2;\r\n const ny = ty / containScale + naturalH / 2;\r\n if (nx < minX) minX = nx;\r\n if (nx > maxX) maxX = nx;\r\n if (ny < minY) minY = ny;\r\n if (ny > maxY) maxY = ny;\r\n }\r\n\r\n return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };\r\n}\r\n\r\n/**\r\n * Forward of {@link containerRectToImageNatural}: rectangle in natural-\r\n * image-pixel coords → rectangle in container-local coords for the given\r\n * transform. See that helper's doc for the AABB caveat.\r\n */\r\nfunction imageNaturalRectToContainer(\r\n rect: { x: number; y: number; w: number; h: number },\r\n imageEl: HTMLImageElement,\r\n angle: number,\r\n zoom: number,\r\n pan: { x: number; y: number },\r\n flipH: boolean,\r\n flipV: boolean,\r\n): { x: number; y: number; w: number; h: number } | null {\r\n const layoutW = imageEl.offsetWidth;\r\n const layoutH = imageEl.offsetHeight;\r\n const naturalW = imageEl.naturalWidth;\r\n const naturalH = imageEl.naturalHeight;\r\n if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;\r\n\r\n const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);\r\n const imgCenterX = imageEl.offsetLeft + layoutW / 2;\r\n const imgCenterY = imageEl.offsetTop + layoutH / 2;\r\n\r\n const sx = zoom * (flipH ? -1 : 1);\r\n const sy = zoom * (flipV ? -1 : 1);\r\n\r\n const rad = (angle * Math.PI) / 180;\r\n const cos = Math.cos(rad);\r\n const sin = Math.sin(rad);\r\n\r\n const corners: ReadonlyArray<readonly [number, number]> = [\r\n [rect.x, rect.y],\r\n [rect.x + rect.w, rect.y],\r\n [rect.x + rect.w, rect.y + rect.h],\r\n [rect.x, rect.y + rect.h],\r\n ];\r\n\r\n let minX = Infinity;\r\n let maxX = -Infinity;\r\n let minY = Infinity;\r\n let maxY = -Infinity;\r\n\r\n for (const [nx, ny] of corners) {\r\n // Natural top-left → centered display coords → scale → rotate →\r\n // pan → container offset.\r\n const cx = (nx - naturalW / 2) * containScale;\r\n const cy = (ny - naturalH / 2) * containScale;\r\n const sxv = cx * sx;\r\n const syv = cy * sy;\r\n const rx = sxv * cos - syv * sin;\r\n const ry = sxv * sin + syv * cos;\r\n const tx = rx + pan.x + imgCenterX;\r\n const ty = ry + pan.y + imgCenterY;\r\n if (tx < minX) minX = tx;\r\n if (tx > maxX) maxX = tx;\r\n if (ty < minY) minY = ty;\r\n if (ty > maxY) maxY = ty;\r\n }\r\n\r\n return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };\r\n}\r\n\r\nasync function downloadSource(source: ImageSource, fileName: string): Promise<void> {\r\n if (source.kind === 'url') {\r\n // Same-origin / CORS-permitting URLs: let the anchor's `download`\r\n // attribute handle it. We don't blob-fetch ourselves to avoid surprising\r\n // the consumer with cross-origin failures we can't control.\r\n const anchor = document.createElement('a');\r\n anchor.href = source.value;\r\n anchor.download = fileName;\r\n document.body.appendChild(anchor);\r\n anchor.click();\r\n document.body.removeChild(anchor);\r\n return;\r\n }\r\n const bytes = source.kind === 'base64' ? base64ToBytes(source.value) : source.value;\r\n triggerBlobDownload(bytes, fileName);\r\n}\r\n\r\n/**\r\n * Resolves the `'auto'` output format to a concrete `'png'` or `'jpeg'`\r\n * based on the source filename's extension. `.jpg` / `.jpeg` / `.jpe` map\r\n * to JPEG; everything else (including unknown extensions and no extension\r\n * at all) falls back to PNG — lossless and broadest browser support,\r\n * matches the safer-default behavior for images we can't classify.\r\n */\r\nfunction resolveAutoOutputFormat(sourceFileName: string): 'png' | 'jpeg' {\r\n const dot = sourceFileName.lastIndexOf('.');\r\n if (dot < 0) return 'png';\r\n const ext = sourceFileName.slice(dot + 1).toLowerCase();\r\n return ext === 'jpg' || ext === 'jpeg' || ext === 'jpe' ? 'jpeg' : 'png';\r\n}\r\n\r\nfunction aspectRatioToNumber(aspect: CropAspect): number | null {\r\n switch (aspect) {\r\n case '1:1':\r\n return 1;\r\n case '4:3':\r\n return 4 / 3;\r\n case '16:9':\r\n return 16 / 9;\r\n case 'free':\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\nfunction aspectRatioLabel(aspect: CropAspect): string {\r\n return aspect === 'free' ? 'Free' : aspect;\r\n}\r\n\r\ninterface RenderCropArgs {\r\n imageEl: HTMLImageElement;\r\n selection: { x: number; y: number; w: number; h: number };\r\n angle: number;\r\n zoom: number;\r\n pan: { x: number; y: number };\r\n flipH: boolean;\r\n flipV: boolean;\r\n sourceFileName: string;\r\n outputFormat: 'png' | 'jpeg' | 'auto';\r\n outputQuality: number;\r\n}\r\n\r\n/**\r\n * Rasterizes the user's selection rectangle to a PNG at **natural image\r\n * resolution**.\r\n *\r\n * Approach: bake rotation + flip into a canvas sized to the natural image\r\n * (W/H swapped for 90°/270° rotations, since those are the only kind the\r\n * toolbar emits). The on-screen image is exactly that natural canvas\r\n * scaled uniformly by `containScale × zoom` and translated by\r\n * `imgCenter + pan`, with no further rotation, so a container-local point\r\n * P maps into the natural canvas via\r\n * P_canvas = canvasCenter + (P − imgCenter − pan) / (containScale × zoom)\r\n * The AABB of the four selection corners under that mapping is exactly the\r\n * rotated rectangle in natural pixels (for axis-aligned rotations). Crop\r\n * that from the natural canvas and you get source-resolution output with\r\n * no upscaling.\r\n *\r\n * Previously this rendered a `devicePixelRatio`-scaled container-sized\r\n * canvas, which threw away natural pixels when the image was zoomed out —\r\n * users got a \"blurry\" crop because the output was capped at the on-screen\r\n * size rather than the source-pixel size of the selected region.\r\n */\r\nasync function renderCrop(args: RenderCropArgs): Promise<CropResult | null> {\r\n const { imageEl, selection, angle, zoom, pan, flipH, flipV, sourceFileName, outputQuality } = args;\r\n // Resolve `'auto'` against the source filename so the crop preserves\r\n // the source's format by default (JPEG stays JPEG; everything else\r\n // emits PNG). Consumers can pin to a specific format via the prop.\r\n const outputFormat = args.outputFormat === 'auto'\r\n ? resolveAutoOutputFormat(sourceFileName)\r\n : args.outputFormat;\r\n\r\n // The image element's pre-transform layout box (offsetLeft/Top/Width/Height\r\n // are unaffected by CSS transforms — they reflect the static layout). The\r\n // image displayed inside this box is fitted via `object-fit: contain`.\r\n const layoutW = imageEl.offsetWidth;\r\n const layoutH = imageEl.offsetHeight;\r\n const naturalW = imageEl.naturalWidth;\r\n const naturalH = imageEl.naturalHeight;\r\n if (layoutW === 0 || layoutH === 0 || naturalW === 0 || naturalH === 0) return null;\r\n\r\n // object-fit: contain math — pick the larger downscale so the image fits\r\n // wholly inside the layout box, with letterbox bars on the smaller axis.\r\n const containScale = Math.min(layoutW / naturalW, layoutH / naturalH);\r\n\r\n // Image element's center in container-local coords. The container's\r\n // `position: relative` makes it the image's offsetParent, so the image's\r\n // offsetLeft/Top are measured against the container's padding edge.\r\n const imgCenterX = imageEl.offsetLeft + layoutW / 2;\r\n const imgCenterY = imageEl.offsetTop + layoutH / 2;\r\n\r\n // Normalize angle to [0, 360) and check whether the rotation swaps W/H.\r\n // We only ever emit ±90° clicks, so the angle is always a 90° multiple\r\n // and this 4-way classification is exhaustive.\r\n const angleNorm = ((angle % 360) + 360) % 360;\r\n const isQuarterTurn = angleNorm === 90 || angleNorm === 270;\r\n\r\n // Natural-canvas dimensions (the rotation/flip-baked image at full\r\n // source resolution). For 90° / 270° rotations W and H swap; 0° / 180°\r\n // leave them as-is.\r\n const rotatedW = isQuarterTurn ? naturalH : naturalW;\r\n const rotatedH = isQuarterTurn ? naturalW : naturalH;\r\n\r\n // Map each selection corner into natural-canvas coords. The natural\r\n // canvas's frame already includes the rotation/flip, so the on-screen\r\n // image is just the canvas scaled by `containScale × zoom` and\r\n // translated by `imgCenter + pan` — no further rotation between them.\r\n const scale = containScale * zoom;\r\n const corners: ReadonlyArray<readonly [number, number]> = [\r\n [selection.x, selection.y],\r\n [selection.x + selection.w, selection.y],\r\n [selection.x + selection.w, selection.y + selection.h],\r\n [selection.x, selection.y + selection.h],\r\n ];\r\n let minX = Infinity;\r\n let maxX = -Infinity;\r\n let minY = Infinity;\r\n let maxY = -Infinity;\r\n for (const [cx, cy] of corners) {\r\n const qx = rotatedW / 2 + (cx - imgCenterX - pan.x) / scale;\r\n const qy = rotatedH / 2 + (cy - imgCenterY - pan.y) / scale;\r\n if (qx < minX) minX = qx;\r\n if (qx > maxX) maxX = qx;\r\n if (qy < minY) minY = qy;\r\n if (qy > maxY) maxY = qy;\r\n }\r\n\r\n // Clamp the crop rectangle to the natural canvas's bounds. A selection\r\n // that extends past the image edge (e.g. after a transform-follow\r\n // effect re-projected it just outside) drops the out-of-bounds slice\r\n // rather than rendering transparent pixels into the output.\r\n const cropLeft = Math.max(0, minX);\r\n const cropTop = Math.max(0, minY);\r\n const cropRight = Math.min(rotatedW, maxX);\r\n const cropBottom = Math.min(rotatedH, maxY);\r\n const cropSrcW = cropRight - cropLeft;\r\n const cropSrcH = cropBottom - cropTop;\r\n if (cropSrcW < 1 || cropSrcH < 1) return null;\r\n\r\n // Bake rotation + flip into a canvas sized to the rotated natural\r\n // image. Zoom/pan are deliberately omitted — those are pure on-screen\r\n // view state, not part of the saved image. Composition order matches\r\n // the on-screen CSS transform: translate → rotate → scale(flip).\r\n const naturalCanvas = document.createElement('canvas');\r\n naturalCanvas.width = rotatedW;\r\n naturalCanvas.height = rotatedH;\r\n const nCtx = naturalCanvas.getContext('2d');\r\n if (!nCtx) return null;\r\n nCtx.translate(rotatedW / 2, rotatedH / 2);\r\n nCtx.rotate((angleNorm * Math.PI) / 180);\r\n nCtx.scale(flipH ? -1 : 1, flipV ? -1 : 1);\r\n nCtx.drawImage(imageEl, -naturalW / 2, -naturalH / 2, naturalW, naturalH);\r\n\r\n // Output canvas — selection cropped from the natural canvas. Output\r\n // dimensions follow the natural-pixel size of the crop region, which is\r\n // the whole point of this refactor.\r\n const out = document.createElement('canvas');\r\n out.width = Math.max(1, Math.round(cropSrcW));\r\n out.height = Math.max(1, Math.round(cropSrcH));\r\n const oCtx = out.getContext('2d');\r\n if (!oCtx) return null;\r\n oCtx.drawImage(\r\n naturalCanvas,\r\n cropLeft,\r\n cropTop,\r\n cropSrcW,\r\n cropSrcH,\r\n 0,\r\n 0,\r\n out.width,\r\n out.height,\r\n );\r\n\r\n // Consumer-selected output format. `toBlob`'s quality arg is ignored for\r\n // PNG (lossless, no quality knob); we pass it anyway because the spec\r\n // says implementations must ignore it for non-lossy formats.\r\n const mimeType = outputFormat === 'jpeg' ? 'image/jpeg' : 'image/png';\r\n const ext = outputFormat === 'jpeg' ? 'jpg' : 'png';\r\n const blob = await new Promise<Blob | null>((resolve) =>\r\n out.toBlob((b) => resolve(b), mimeType, outputQuality),\r\n );\r\n if (!blob) return null;\r\n const arrayBuffer = await blob.arrayBuffer();\r\n const bytes = new Uint8Array(arrayBuffer);\r\n const base64 = bytesToBase64(bytes);\r\n\r\n // Suggested file name: <orig stem>-cropped.<ext>. Stem-only fallback when\r\n // the source has no extension.\r\n const dot = sourceFileName.lastIndexOf('.');\r\n const stem = dot >= 0 ? sourceFileName.slice(0, dot) : sourceFileName;\r\n const fileName = `${stem}-cropped.${ext}`;\r\n\r\n return {\r\n bytes,\r\n base64,\r\n mimeType,\r\n fileName,\r\n width: out.width,\r\n height: out.height,\r\n };\r\n}\r\n\r\n/**\r\n * Bakes the displayed image's current rotation + flips into a fresh PNG at\r\n * source resolution. Used by the imperative `commit()` handle when the\r\n * consumer wants to save the current view to a backend.\r\n *\r\n * Output dimensions follow the rotated bounding box: 90°/270° swap width\r\n * and height; 0°/180° leave them as the natural size. Any cropping is\r\n * already baked into the displayed image (`croppedSource` becomes the new\r\n * `imageEl.naturalWidth/Height`), so this function doesn't need to know\r\n * about the crop history.\r\n */\r\nasync function renderBakedImage(args: {\r\n imageEl: HTMLImageElement;\r\n angle: number;\r\n flipH: boolean;\r\n flipV: boolean;\r\n sourceFileName: string;\r\n outputFormat: 'png' | 'jpeg' | 'auto';\r\n outputQuality: number;\r\n}): Promise<CropResult | null> {\r\n const { imageEl, angle, flipH, flipV, sourceFileName, outputQuality } = args;\r\n // Same `'auto'` resolution as renderCrop — preserve source format by\r\n // default (JPEG → JPEG, everything else → PNG).\r\n const outputFormat = args.outputFormat === 'auto'\r\n ? resolveAutoOutputFormat(sourceFileName)\r\n : args.outputFormat;\r\n const naturalW = imageEl.naturalWidth;\r\n const naturalH = imageEl.naturalHeight;\r\n if (naturalW === 0 || naturalH === 0) return null;\r\n\r\n // Normalize and decide whether the rotation swaps width and height. We\r\n // only emit ±90° clicks, so the angle is always a multiple of 90 and\r\n // this 4-way check is exhaustive.\r\n const angleNorm = ((angle % 360) + 360) % 360;\r\n const isQuarterTurn = angleNorm === 90 || angleNorm === 270;\r\n const outputW = isQuarterTurn ? naturalH : naturalW;\r\n const outputH = isQuarterTurn ? naturalW : naturalH;\r\n\r\n const canvas = document.createElement('canvas');\r\n canvas.width = outputW;\r\n canvas.height = outputH;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return null;\r\n\r\n // Center, rotate, flip, then draw the source centered around the\r\n // origin. Same composition order as the on-screen CSS transform, minus\r\n // zoom/pan (those are pure view state and not part of the saved image).\r\n ctx.translate(outputW / 2, outputH / 2);\r\n ctx.rotate((angleNorm * Math.PI) / 180);\r\n ctx.scale(flipH ? -1 : 1, flipV ? -1 : 1);\r\n ctx.drawImage(imageEl, -naturalW / 2, -naturalH / 2, naturalW, naturalH);\r\n\r\n const mimeType = outputFormat === 'jpeg' ? 'image/jpeg' : 'image/png';\r\n const ext = outputFormat === 'jpeg' ? 'jpg' : 'png';\r\n const blob = await new Promise<Blob | null>((resolve) =>\r\n canvas.toBlob((b) => resolve(b), mimeType, outputQuality),\r\n );\r\n if (!blob) return null;\r\n const arrayBuffer = await blob.arrayBuffer();\r\n const bytes = new Uint8Array(arrayBuffer);\r\n const base64 = bytesToBase64(bytes);\r\n\r\n // Suggested file name: `<orig stem>-edited.<ext>`. Sibling of\r\n // `renderCrop`'s `-cropped.<ext>` so the consumer can tell which path\r\n // produced the file by its name alone.\r\n const dot = sourceFileName.lastIndexOf('.');\r\n const stem = dot >= 0 ? sourceFileName.slice(0, dot) : sourceFileName;\r\n const fileName = `${stem}-edited.${ext}`;\r\n\r\n return {\r\n bytes,\r\n base64,\r\n mimeType,\r\n fileName,\r\n width: outputW,\r\n height: outputH,\r\n };\r\n}\r\n\r\n/**\r\n * Encodes a `Uint8Array` to base64 (no `data:` prefix). Chunked through a\r\n * binary-string buffer to avoid the call-stack overflow that\r\n * `String.fromCharCode(...bytes)` hits on large arrays.\r\n */\r\nfunction bytesToBase64(bytes: Uint8Array): string {\r\n const chunkSize = 0x8000; // 32 KB; comfortably under apply()'s arg-list limit\r\n let binary = '';\r\n for (let i = 0; i < bytes.length; i += chunkSize) {\r\n binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));\r\n }\r\n return btoa(binary);\r\n}\r\n\r\nconst useImageViewerStyles = makeStyles({\r\n bareImage: {\r\n maxWidth: '100%',\r\n height: 'auto',\r\n display: 'block',\r\n },\r\n clickable: {\r\n cursor: 'pointer',\r\n },\r\n wrapper: {\r\n display: 'inline-flex',\r\n flexDirection: 'column',\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n overflow: 'hidden',\r\n },\r\n wrapperFullscreen: {\r\n width: '100vw',\r\n height: '100vh',\r\n border: 'none',\r\n borderRadius: 0,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n toolbar: {\r\n flex: '0 0 auto',\r\n borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n imageContainer: {\r\n position: 'relative',\r\n flex: '1 1 auto',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: tokens.spacingHorizontalM,\r\n minHeight: '120px',\r\n overflow: 'hidden',\r\n },\r\n imageContainerFullscreen: {\r\n padding: 0,\r\n },\r\n framedImage: {\r\n maxWidth: '100%',\r\n maxHeight: '320px',\r\n objectFit: 'contain',\r\n display: 'block',\r\n userSelect: 'none',\r\n WebkitUserSelect: 'none',\r\n },\r\n framedImageFullscreen: {\r\n maxHeight: '100%',\r\n maxWidth: '100%',\r\n },\r\n selectionOverlay: {\r\n position: 'absolute',\r\n border: `${tokens.strokeWidthThicker} dashed ${tokens.colorNeutralStrokeOnBrand}`,\r\n // Spread shadow dims everything outside the rectangle. The container's\r\n // `overflow: hidden` clips the shadow to the visible viewer area.\r\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.45)',\r\n // `auto` (not `none`) so the overlay can show a `move` cursor while\r\n // the pointer is inside the selection — the pointerdown still bubbles\r\n // up to the container's handler (currentTarget is the container), so\r\n // hit-testing and pointer capture continue to work the same way.\r\n pointerEvents: 'auto',\r\n cursor: 'move',\r\n boxSizing: 'border-box',\r\n // Drag-handle hardening: stop the browser from interpreting the\r\n // press-and-drag as a text-selection or touch gesture. Without\r\n // these, the browser intermittently steals pointer capture mid-\r\n // move (the move drag freezes; the user has to click to \"reset\"\r\n // before they can drag again).\r\n userSelect: 'none',\r\n WebkitUserSelect: 'none',\r\n touchAction: 'none',\r\n },\r\n // Rule-of-thirds guide. A pointer-transparent container that fills the\r\n // selection rectangle and holds four thin lines (two vertical, two\r\n // horizontal) at the 1/3 and 2/3 marks. Lines are translucent white so\r\n // they read against any image content without overpowering it.\r\n thirdsGuide: {\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n right: 0,\r\n bottom: 0,\r\n pointerEvents: 'none',\r\n },\r\n thirdsLineV1: {\r\n position: 'absolute',\r\n top: 0,\r\n bottom: 0,\r\n left: '33.3333%',\r\n width: '1px',\r\n backgroundColor: 'rgba(255, 255, 255, 0.55)',\r\n },\r\n thirdsLineV2: {\r\n position: 'absolute',\r\n top: 0,\r\n bottom: 0,\r\n left: '66.6667%',\r\n width: '1px',\r\n backgroundColor: 'rgba(255, 255, 255, 0.55)',\r\n },\r\n thirdsLineH1: {\r\n position: 'absolute',\r\n left: 0,\r\n right: 0,\r\n top: '33.3333%',\r\n height: '1px',\r\n backgroundColor: 'rgba(255, 255, 255, 0.55)',\r\n },\r\n thirdsLineH2: {\r\n position: 'absolute',\r\n left: 0,\r\n right: 0,\r\n top: '66.6667%',\r\n height: '1px',\r\n backgroundColor: 'rgba(255, 255, 255, 0.55)',\r\n },\r\n // Live dimension readout. Anchored to the top-left of the selection,\r\n // sitting just outside the dashed border. Opaque background so it\r\n // shows clearly over the dimmed surrounding area. `pointer-events:\r\n // none` so the badge can't accidentally start a move-drag (the\r\n // overlay-level `cursor: move` and pointer handlers would otherwise\r\n // see it as a hit target).\r\n dimensionBadge: {\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n transform: 'translateY(calc(-100% - 4px))',\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n color: tokens.colorNeutralForeground1,\r\n paddingTop: '2px',\r\n paddingBottom: '2px',\r\n paddingLeft: tokens.spacingHorizontalXS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n borderTopLeftRadius: tokens.borderRadiusSmall,\r\n borderTopRightRadius: tokens.borderRadiusSmall,\r\n borderBottomLeftRadius: tokens.borderRadiusSmall,\r\n borderBottomRightRadius: tokens.borderRadiusSmall,\r\n fontSize: tokens.fontSizeBase200,\r\n fontFamily: tokens.fontFamilyMonospace,\r\n lineHeight: 1.2,\r\n whiteSpace: 'nowrap',\r\n pointerEvents: 'none',\r\n userSelect: 'none',\r\n WebkitUserSelect: 'none',\r\n boxShadow: tokens.shadow4,\r\n zIndex: 2,\r\n },\r\n // Resize handles. Small filled squares on each corner (always) and edge\r\n // midpoint (free aspect only). Positioned on the overlay's border with\r\n // a half-size translate so the visible square straddles the edge — gives\r\n // a wide hit target without the handle \"moving\" the visual edge of the\r\n // selection.\r\n resizeHandle: {\r\n position: 'absolute',\r\n width: '12px',\r\n height: '12px',\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n borderTopWidth: tokens.strokeWidthThin,\r\n borderRightWidth: tokens.strokeWidthThin,\r\n borderBottomWidth: tokens.strokeWidthThin,\r\n borderLeftWidth: tokens.strokeWidthThin,\r\n borderTopStyle: 'solid',\r\n borderRightStyle: 'solid',\r\n borderBottomStyle: 'solid',\r\n borderLeftStyle: 'solid',\r\n borderTopColor: tokens.colorNeutralStrokeAccessible,\r\n borderRightColor: tokens.colorNeutralStrokeAccessible,\r\n borderBottomColor: tokens.colorNeutralStrokeAccessible,\r\n borderLeftColor: tokens.colorNeutralStrokeAccessible,\r\n borderTopLeftRadius: tokens.borderRadiusSmall,\r\n borderTopRightRadius: tokens.borderRadiusSmall,\r\n borderBottomLeftRadius: tokens.borderRadiusSmall,\r\n borderBottomRightRadius: tokens.borderRadiusSmall,\r\n pointerEvents: 'auto',\r\n userSelect: 'none',\r\n WebkitUserSelect: 'none',\r\n touchAction: 'none',\r\n // Sit above the dashed border so the user can grab them.\r\n zIndex: 1,\r\n boxSizing: 'border-box',\r\n },\r\n handleNW: {\r\n top: 0,\r\n left: 0,\r\n transform: 'translate(-50%, -50%)',\r\n cursor: 'nwse-resize',\r\n },\r\n handleNE: {\r\n top: 0,\r\n right: 0,\r\n transform: 'translate(50%, -50%)',\r\n cursor: 'nesw-resize',\r\n },\r\n handleSE: {\r\n bottom: 0,\r\n right: 0,\r\n transform: 'translate(50%, 50%)',\r\n cursor: 'nwse-resize',\r\n },\r\n handleSW: {\r\n bottom: 0,\r\n left: 0,\r\n transform: 'translate(-50%, 50%)',\r\n cursor: 'nesw-resize',\r\n },\r\n handleN: {\r\n top: 0,\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n cursor: 'ns-resize',\r\n },\r\n handleS: {\r\n bottom: 0,\r\n left: '50%',\r\n transform: 'translate(-50%, 50%)',\r\n cursor: 'ns-resize',\r\n },\r\n handleE: {\r\n top: '50%',\r\n right: 0,\r\n transform: 'translate(50%, -50%)',\r\n cursor: 'ew-resize',\r\n },\r\n handleW: {\r\n top: '50%',\r\n left: 0,\r\n transform: 'translate(-50%, -50%)',\r\n cursor: 'ew-resize',\r\n },\r\n downloadButton: {\r\n position: 'absolute',\r\n top: tokens.spacingVerticalXS,\r\n right: tokens.spacingHorizontalXS,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n // Palette tokens (not bare hex) so the colors track the theme — Fluent's\r\n // light/dark switch picks the right contrast, and the disabled state\r\n // overlay still applies cleanly on top.\r\n saveIcon: {\r\n color: tokens.colorPaletteGreenForeground1,\r\n },\r\n cancelIcon: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n});\r\n","import {\r\n Button,\r\n makeStyles,\r\n mergeClasses,\r\n Spinner,\r\n Tab,\r\n TabList,\r\n tokens,\r\n Tooltip,\r\n} from '@fluentui/react-components';\r\nimport {\r\n ArrowDownload20Regular,\r\n ChevronDown20Regular,\r\n ChevronRight20Regular,\r\n Document20Regular,\r\n DocumentArrowLeft24Regular,\r\n ErrorCircle20Regular,\r\n Folder20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { unzipSync } from 'fflate';\r\nimport {\r\n useEffect,\r\n useLayoutEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type CSSProperties,\r\n type ReactNode,\r\n} from 'react';\r\nimport Markdown from 'react-markdown';\r\nimport hljs from 'highlight.js/lib/core';\r\nimport csharp from 'highlight.js/lib/languages/csharp';\r\nimport css from 'highlight.js/lib/languages/css';\r\nimport javascript from 'highlight.js/lib/languages/javascript';\r\nimport json from 'highlight.js/lib/languages/json';\r\nimport markdown from 'highlight.js/lib/languages/markdown';\r\nimport typescript from 'highlight.js/lib/languages/typescript';\r\nimport xml from 'highlight.js/lib/languages/xml';\r\nimport yaml from 'highlight.js/lib/languages/yaml';\r\nimport {\r\n base64ToBytes,\r\n base64ToText,\r\n bytesToText,\r\n categorizeFile,\r\n type FileCategory,\r\n getMimeFromFileName,\r\n getHighlightLanguage,\r\n triggerBlobDownload,\r\n} from '../edits/base/file-helpers.js';\r\nimport { ImageViewer, type ImageSource } from './image-viewer.js';\r\n\r\n// highlight.js core + the same grammar set the Blazor `FileViewer.razor.ts`\r\n// registers. Registered once at module load — `hljs` is a singleton.\r\n// Matching the Blazor set keeps the two FileViewers visually + behaviorally\r\n// aligned, and keeps the bundle small (core + 8 synchronous grammars, ~tens\r\n// of KB) versus Shiki's ~145 KB-gzipped WASM engine.\r\nhljs.registerLanguage('xml', xml);\r\nhljs.registerLanguage('json', json);\r\nhljs.registerLanguage('yaml', yaml);\r\nhljs.registerLanguage('javascript', javascript);\r\nhljs.registerLanguage('typescript', typescript);\r\nhljs.registerLanguage('csharp', csharp);\r\nhljs.registerLanguage('css', css);\r\nhljs.registerLanguage('markdown', markdown);\r\n\r\n// Renders code to highlight.js token markup (the inner `<span class=\"hljs-…\">`\r\n// HTML — NOT a full `<pre>`). When the language isn't a registered grammar\r\n// (`plaintext` or anything unmapped) the text is HTML-escaped and returned\r\n// unhighlighted. Mirrors the `viewers.highlight` helper in the Blazor\r\n// `FileViewer.razor.ts`.\r\nfunction highlightToHtml(code: string, language: string): string {\r\n if (language && language !== 'plaintext' && hljs.getLanguage(language)) {\r\n return hljs.highlight(code, { language, ignoreIllegals: true }).value;\r\n }\r\n return escapeHtml(code);\r\n}\r\n\r\nfunction escapeHtml(value: string): string {\r\n return value\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#39;');\r\n}\r\n\r\n/**\r\n * Discriminated source for a `FileViewer`. Mirrors `ImageSource` and adds\r\n * optional `mimeType` / `fileName` to the `url` kind so the renderer\r\n * dispatcher can categorize a remote file without a network round-trip.\r\n *\r\n * - `url`: the value is passed straight to renderers that accept URLs\r\n * (`<img>`, `<iframe>`); for text/markdown we `fetch` it and read text.\r\n * - `base64`: decoded once per resolution and either text-decoded (for\r\n * text/markdown) or wrapped in an object URL (for embedded iframes).\r\n * - `bytes`: same as `base64` but skips the decode step.\r\n */\r\nexport type FileSource =\r\n | { kind: 'url'; value: string; mimeType?: string; fileName?: string }\r\n | { kind: 'base64'; value: string; mimeType?: string; fileName?: string }\r\n | { kind: 'bytes'; value: Uint8Array; mimeType?: string; fileName?: string };\r\n\r\nexport interface FileViewerProps {\r\n /**\r\n * File data to render. When null/undefined, `fallback` is rendered\r\n * instead (or nothing if no fallback is provided).\r\n */\r\n source: FileSource | null | undefined;\r\n /**\r\n * File name used for category detection (extension), the download\r\n * action, and the download card label. Falls back to the source's own\r\n * `fileName`, otherwise to the URL's last path segment.\r\n */\r\n fileName?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n id?: string;\r\n /** Rendered when `source` is null/undefined. */\r\n fallback?: ReactNode;\r\n /**\r\n * Maximum height for inline renderers (text, markdown preview, embedded\r\n * iframe). Sets the scrollable viewport. Default: <c>60vh</c>.\r\n */\r\n maxHeight?: string | number;\r\n}\r\n\r\n/**\r\n * Standalone file viewer that auto-detects how to render its source.\r\n * Categories: image (delegates to {@link ImageViewer}), markdown (tabbed\r\n * Source/Preview), text (Shiki-highlighted), embedded (PDF and other\r\n * browser-renderable types via `<iframe>`), download (file-info card).\r\n *\r\n * Detection is driven by the source's MIME type when present, otherwise\r\n * by the file extension. Unknown types fall through to the download\r\n * card so consumers always get a usable result.\r\n */\r\nexport function FileViewer({\r\n source,\r\n fileName,\r\n className,\r\n style,\r\n id,\r\n fallback,\r\n maxHeight = '60vh',\r\n}: FileViewerProps) {\r\n const styles = useFileViewerStyles();\r\n\r\n if (!source) return <>{fallback ?? null}</>;\r\n\r\n const resolvedFileName =\r\n fileName ??\r\n (source.kind === 'url'\r\n ? source.fileName ?? extractFileNameFromUrl(source.value)\r\n : source.fileName) ??\r\n 'file';\r\n const resolvedMime = source.mimeType ?? getMimeFromFileName(resolvedFileName);\r\n const category: FileCategory = categorizeFile({\r\n mimeType: resolvedMime,\r\n fileName: resolvedFileName,\r\n });\r\n\r\n const wrapperClass = mergeClasses(styles.wrapper, className);\r\n const wrapperStyle: CSSProperties = { ...style };\r\n\r\n switch (category) {\r\n case 'image':\r\n // Wrap in a centering box so the framed `ImageViewer` (which is\r\n // shrink-to-content `inline-flex`) sits centered on both axes within\r\n // its parent — matters in the zip preview pane where the parent is\r\n // sized and we want the image visually centered, not pinned top-left.\r\n // Outside that context the wrapper's `height: 100%` collapses to auto\r\n // (no parent height), so non-zip uses look the same.\r\n return (\r\n <div className={styles.imageCenter}>\r\n <ImageViewer\r\n source={toImageSource(source)}\r\n fileName={resolvedFileName}\r\n framed\r\n downloadable\r\n {...(className !== undefined && { className })}\r\n {...(style !== undefined && { style })}\r\n {...(id !== undefined && { id })}\r\n />\r\n </div>\r\n );\r\n case 'markdown':\r\n return (\r\n <MarkdownRenderer\r\n source={source}\r\n fileName={resolvedFileName}\r\n className={wrapperClass}\r\n style={wrapperStyle}\r\n id={id}\r\n maxHeight={maxHeight}\r\n />\r\n );\r\n case 'text':\r\n return (\r\n <TextRenderer\r\n source={source}\r\n fileName={resolvedFileName}\r\n className={wrapperClass}\r\n style={wrapperStyle}\r\n id={id}\r\n maxHeight={maxHeight}\r\n />\r\n );\r\n case 'embedded':\r\n return (\r\n <EmbeddedRenderer\r\n source={source}\r\n fileName={resolvedFileName}\r\n mimeType={resolvedMime}\r\n className={wrapperClass}\r\n style={wrapperStyle}\r\n id={id}\r\n maxHeight={maxHeight}\r\n />\r\n );\r\n case 'zip':\r\n return (\r\n <ZipViewer\r\n source={source}\r\n fileName={resolvedFileName}\r\n className={wrapperClass}\r\n style={wrapperStyle}\r\n id={id}\r\n maxHeight={maxHeight}\r\n />\r\n );\r\n case 'download':\r\n default:\r\n return (\r\n <DownloadCard\r\n source={source}\r\n fileName={resolvedFileName}\r\n mimeType={resolvedMime}\r\n className={wrapperClass}\r\n style={wrapperStyle}\r\n id={id}\r\n />\r\n );\r\n }\r\n}\r\n\r\nfunction toImageSource(source: FileSource): ImageSource {\r\n if (source.kind === 'url') return { kind: 'url', value: source.value };\r\n const base = { kind: source.kind, value: source.value } as\r\n | { kind: 'base64'; value: string }\r\n | { kind: 'bytes'; value: Uint8Array };\r\n return {\r\n ...base,\r\n ...(source.mimeType !== undefined && { mimeType: source.mimeType }),\r\n ...(source.fileName !== undefined && { fileName: source.fileName }),\r\n } as ImageSource;\r\n}\r\n\r\nfunction extractFileNameFromUrl(url: string): string | undefined {\r\n try {\r\n const parsed = new URL(url, 'http://_resolve');\r\n const last = parsed.pathname.split('/').filter(Boolean).pop();\r\n return last ? decodeURIComponent(last) : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\ninterface RendererProps {\r\n source: FileSource;\r\n fileName: string;\r\n className?: string | undefined;\r\n style?: CSSProperties | undefined;\r\n id?: string | undefined;\r\n maxHeight: string | number;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Text renderer — highlight.js-colored code/text. Falls back to a plain\r\n// `<pre>` when the language isn't a registered grammar (`highlightToHtml`\r\n// HTML-escapes and returns the text unhighlighted). Light / dark Fluent\r\n// themes are handled by the host class swapping the `.hljs-*` token palette\r\n// (see `codeHostLight` / `codeHostDark`).\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction TextRenderer({ source, fileName, className, style, id, maxHeight }: RendererProps) {\r\n const styles = useFileViewerStyles();\r\n const { text, error, isLoading } = useFileText(source);\r\n const language = getHighlightLanguage(fileName);\r\n const html = useHighlightedHtml(text, language);\r\n\r\n if (isLoading) return <LoadingShell className={className} style={style} id={id} />;\r\n if (error) return <ErrorShell className={className} style={style} id={id} message={error} />;\r\n if (text === null) return null;\r\n\r\n return (\r\n <div\r\n id={id}\r\n className={mergeClasses(styles.codeFrame, className)}\r\n // `height: 100%` lets the frame fill a sized parent (e.g. the zip\r\n // preview pane). When the parent has no defined height it collapses\r\n // to auto and the frame sizes to content as before. `maxHeight` still\r\n // caps the upward growth in scrollable contexts. Consumer-supplied\r\n // `style` overrides come last so explicit consumer values win.\r\n style={{ height: '100%', maxHeight, ...style }}\r\n >\r\n {html !== null ? (\r\n <HighlightedCodeBlock html={html} />\r\n ) : (\r\n <pre className={styles.fallbackPre}>{text}</pre>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Markdown renderer — tabbed Preview / Source. Preview uses react-markdown\r\n// (returns React elements, no dangerouslySetInnerHTML for the rendered Markdown\r\n// itself). Source tab reuses the same highlight.js path as TextRenderer with\r\n// `language=markdown`.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction MarkdownRenderer({ source, fileName, className, style, id, maxHeight }: RendererProps) {\r\n const styles = useFileViewerStyles();\r\n const { text, error, isLoading } = useFileText(source);\r\n const [tab, setTab] = useState<'preview' | 'source'>('preview');\r\n const sourceHtml = useHighlightedHtml(tab === 'source' ? text : null, 'markdown');\r\n\r\n if (isLoading) return <LoadingShell className={className} style={style} id={id} />;\r\n if (error) return <ErrorShell className={className} style={style} id={id} message={error} />;\r\n if (text === null) return null;\r\n\r\n void fileName;\r\n\r\n return (\r\n <div id={id} className={mergeClasses(styles.markdownFrame, className)} style={style}>\r\n <TabList\r\n selectedValue={tab}\r\n onTabSelect={(_, data) => setTab(data.value as 'preview' | 'source')}\r\n size=\"small\"\r\n >\r\n <Tab value=\"preview\">Preview</Tab>\r\n <Tab value=\"source\">Source</Tab>\r\n </TabList>\r\n <div className={styles.markdownBody} style={{ maxHeight }}>\r\n {tab === 'preview' ? (\r\n <div className={styles.markdownPreview}>\r\n <Markdown>{text}</Markdown>\r\n </div>\r\n ) : sourceHtml !== null ? (\r\n <HighlightedCodeBlock html={sourceHtml} />\r\n ) : (\r\n <pre className={styles.fallbackPre}>{text}</pre>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Embedded renderer — `<iframe>` for PDFs and other browser-renderable\r\n// formats. Bytes/base64 sources get an object URL allocated and revoked on\r\n// cleanup. URL sources pass through.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction EmbeddedRenderer({\r\n source,\r\n fileName,\r\n mimeType,\r\n className,\r\n style,\r\n id,\r\n maxHeight,\r\n}: RendererProps & { mimeType: string }) {\r\n const styles = useFileViewerStyles();\r\n const objectUrl = useObjectUrl(source, mimeType);\r\n\r\n if (source.kind === 'url') {\r\n return (\r\n <iframe\r\n id={id}\r\n title={fileName}\r\n src={source.value}\r\n className={mergeClasses(styles.iframe, className)}\r\n style={{ ...style, height: maxHeight }}\r\n />\r\n );\r\n }\r\n\r\n if (!objectUrl) return <LoadingShell className={className} style={style} id={id} />;\r\n\r\n return (\r\n <iframe\r\n id={id}\r\n title={fileName}\r\n src={objectUrl}\r\n className={mergeClasses(styles.iframe, className)}\r\n style={{ ...style, height: maxHeight }}\r\n />\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Zip viewer — left pane lists the archive contents as a folder tree, right\r\n// pane renders the selected file via a nested `FileViewer`. Bytes are\r\n// extracted up-front via `fflate.unzipSync`; a folder tree is then derived\r\n// from the entry paths so we can render expand/collapse + sorted children.\r\n//\r\n// Mirrors the Blazor `FilePreviewContent` zip case in shape, with one\r\n// simplification: the splitter between panes is a CSS `resize: horizontal`\r\n// handle on the left pane (browser-native) rather than a draggable JS\r\n// splitter, since FluentUI React 9 doesn't ship one.\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface ZipNode {\r\n name: string;\r\n fullPath: string;\r\n isFolder: boolean;\r\n size: number;\r\n children: ZipNode[];\r\n}\r\n\r\n// Width clamps for the tree pane. The keyboard / drag handlers and the\r\n// initial render all read these — kept at module scope so the values stay\r\n// in sync.\r\nconst ZIP_TREE_MIN_WIDTH = 180;\r\nconst ZIP_TREE_MIN_PREVIEW = 280;\r\nconst ZIP_TREE_INITIAL_WIDTH = 280;\r\nconst ZIP_SPLITTER_KEY_STEP = 16;\r\n// Persisted across reloads so the user's last-chosen width sticks. Scoped\r\n// to the framework's namespace to avoid colliding with any consumer keys.\r\nconst ZIP_TREE_WIDTH_STORAGE_KEY = 'powerportalspro:zipViewer:treeWidth';\r\n// Drag emits at the pointer's frame rate; writing every move to\r\n// localStorage is wasteful even though writes are cheap. 250ms after the\r\n// last change is short enough that closing the tab right after a drag\r\n// still captures the final width on most browsers.\r\nconst ZIP_TREE_WIDTH_PERSIST_DEBOUNCE_MS = 250;\r\n\r\nfunction ZipViewer({ source, fileName, className, style, id, maxHeight }: RendererProps) {\r\n const styles = useFileViewerStyles();\r\n const { bytes, error, isLoading } = useFileBytes(source);\r\n const [entries, treeError] = useMemo<[Record<string, Uint8Array> | null, string | null]>(() => {\r\n if (!bytes) return [null, null];\r\n try {\r\n return [unzipSync(bytes), null];\r\n } catch (err) {\r\n return [null, err instanceof Error ? err.message : 'Failed to read archive.'];\r\n }\r\n }, [bytes]);\r\n const tree = useMemo(() => (entries ? buildZipTree(entries) : []), [entries]);\r\n\r\n // Tree state. Selection is the full path (matching the keys of `entries`)\r\n // of whichever file the user has clicked; null means \"show empty state\".\r\n // Expanded paths are tracked as a Set so parents reset cleanly when a\r\n // new archive is loaded (the dependency on `entries` resets both).\r\n const [selected, setSelected] = useState<string | null>(null);\r\n const [expanded, setExpanded] = useState<Set<string>>(() => new Set());\r\n useEffect(() => {\r\n setSelected(null);\r\n setExpanded(new Set());\r\n }, [entries]);\r\n\r\n // Splitter state. The frame ref measures the available width so we can\r\n // clamp the tree pane against it without ever overflowing the preview.\r\n // dragStateRef holds drag-start values so each pointermove computes a\r\n // fresh delta against the original anchor (rather than accumulating\r\n // floating-point drift across moves). Initial width restores from\r\n // localStorage when present; the lazy initializer guards against SSR\r\n // (no `window`) and against environments where storage is disabled\r\n // (private browsing, sandboxed iframes — `getItem` throws).\r\n const frameRef = useRef<HTMLDivElement>(null);\r\n const [treeWidth, setTreeWidth] = useState<number>(() => {\r\n if (typeof window === 'undefined') return ZIP_TREE_INITIAL_WIDTH;\r\n try {\r\n const stored = window.localStorage.getItem(ZIP_TREE_WIDTH_STORAGE_KEY);\r\n if (stored !== null) {\r\n const parsed = Number.parseInt(stored, 10);\r\n if (Number.isFinite(parsed) && parsed >= ZIP_TREE_MIN_WIDTH) return parsed;\r\n }\r\n } catch {\r\n // Storage unavailable; fall through to the default.\r\n }\r\n return ZIP_TREE_INITIAL_WIDTH;\r\n });\r\n const dragStateRef = useRef<{ startX: number; startWidth: number; maxWidth: number } | null>(\r\n null,\r\n );\r\n\r\n // Debounced persistence — drag emits one render per pointer-move frame,\r\n // so writing on every change would batter localStorage. The 250ms timer\r\n // resets on each new value; cleanup cancels the pending write so we\r\n // never persist an intermediate value that's already been superseded.\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n const handle = window.setTimeout(() => {\r\n try {\r\n window.localStorage.setItem(\r\n ZIP_TREE_WIDTH_STORAGE_KEY,\r\n String(Math.round(treeWidth)),\r\n );\r\n } catch {\r\n // Quota exceeded / storage disabled — ignore.\r\n }\r\n }, ZIP_TREE_WIDTH_PERSIST_DEBOUNCE_MS);\r\n return () => window.clearTimeout(handle);\r\n }, [treeWidth]);\r\n\r\n const computeMaxTreeWidth = () => {\r\n const totalWidth = frameRef.current?.getBoundingClientRect().width ?? 0;\r\n return Math.max(ZIP_TREE_MIN_WIDTH, totalWidth - ZIP_TREE_MIN_PREVIEW);\r\n };\r\n\r\n const onSplitterPointerDown = (ev: React.PointerEvent<HTMLDivElement>) => {\r\n // preventDefault here suppresses the browser's text-selection start —\r\n // without it, dragging the splitter selects the file names in the tree.\r\n ev.preventDefault();\r\n ev.currentTarget.setPointerCapture(ev.pointerId);\r\n dragStateRef.current = {\r\n startX: ev.clientX,\r\n startWidth: treeWidth,\r\n maxWidth: computeMaxTreeWidth(),\r\n };\r\n };\r\n\r\n const onSplitterPointerMove = (ev: React.PointerEvent<HTMLDivElement>) => {\r\n const drag = dragStateRef.current;\r\n if (!drag) return;\r\n if (!ev.currentTarget.hasPointerCapture(ev.pointerId)) return;\r\n const delta = ev.clientX - drag.startX;\r\n const next = Math.min(Math.max(drag.startWidth + delta, ZIP_TREE_MIN_WIDTH), drag.maxWidth);\r\n setTreeWidth(next);\r\n };\r\n\r\n const onSplitterPointerUp = (ev: React.PointerEvent<HTMLDivElement>) => {\r\n if (ev.currentTarget.hasPointerCapture(ev.pointerId)) {\r\n ev.currentTarget.releasePointerCapture(ev.pointerId);\r\n }\r\n dragStateRef.current = null;\r\n };\r\n\r\n const onSplitterKeyDown = (ev: React.KeyboardEvent<HTMLDivElement>) => {\r\n if (ev.key !== 'ArrowLeft' && ev.key !== 'ArrowRight') return;\r\n ev.preventDefault();\r\n const max = computeMaxTreeWidth();\r\n setTreeWidth((current) => {\r\n const step = ev.key === 'ArrowLeft' ? -ZIP_SPLITTER_KEY_STEP : ZIP_SPLITTER_KEY_STEP;\r\n return Math.min(Math.max(current + step, ZIP_TREE_MIN_WIDTH), max);\r\n });\r\n };\r\n\r\n // Standard split-pane convention: double-clicking the divider snaps it\r\n // back to the default. The persistence effect picks this value up like\r\n // any other change, so the reset survives reloads.\r\n const onSplitterDoubleClick = () => {\r\n setTreeWidth(ZIP_TREE_INITIAL_WIDTH);\r\n };\r\n\r\n if (isLoading) return <LoadingShell className={className} style={style} id={id} />;\r\n if (error) return <ErrorShell className={className} style={style} id={id} message={error} />;\r\n if (treeError)\r\n return <ErrorShell className={className} style={style} id={id} message={treeError} />;\r\n if (!entries) return null;\r\n\r\n const toggleFolder = (path: string) => {\r\n setExpanded((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(path)) next.delete(path);\r\n else next.add(path);\r\n return next;\r\n });\r\n };\r\n\r\n const selectedBytes = selected !== null ? entries[selected] ?? null : null;\r\n const selectedLeafName = selected !== null ? leafName(selected) : '';\r\n void fileName;\r\n\r\n return (\r\n <div\r\n ref={frameRef}\r\n id={id}\r\n className={mergeClasses(styles.zipFrame, className)}\r\n style={{ ...style, height: maxHeight }}\r\n >\r\n <div className={styles.zipTree} style={{ width: `${treeWidth}px` }}>\r\n {tree.length === 0 ? (\r\n <div className={styles.zipEmpty}>\r\n <span>Archive is empty.</span>\r\n </div>\r\n ) : (\r\n tree.map((node) => (\r\n <ZipTreeNode\r\n key={node.fullPath}\r\n node={node}\r\n depth={0}\r\n expanded={expanded}\r\n selected={selected}\r\n onToggle={toggleFolder}\r\n onSelect={setSelected}\r\n />\r\n ))\r\n )}\r\n </div>\r\n <div\r\n className={styles.zipSplitter}\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n aria-label=\"Resize tree pane (double-click to reset)\"\r\n aria-valuenow={Math.round(treeWidth)}\r\n aria-valuemin={ZIP_TREE_MIN_WIDTH}\r\n tabIndex={0}\r\n onPointerDown={onSplitterPointerDown}\r\n onPointerMove={onSplitterPointerMove}\r\n onPointerUp={onSplitterPointerUp}\r\n onPointerCancel={onSplitterPointerUp}\r\n onKeyDown={onSplitterKeyDown}\r\n onDoubleClick={onSplitterDoubleClick}\r\n >\r\n <span className={styles.zipSplitterGrip} aria-hidden />\r\n </div>\r\n <div className={styles.zipPreview}>\r\n {selectedBytes ? (\r\n <FileViewer\r\n // `key` forces a fresh viewer per selection so internal state\r\n // (markdown tab, scroll position, fetched text) resets cleanly.\r\n key={selected}\r\n source={{ kind: 'bytes', value: selectedBytes, fileName: selectedLeafName }}\r\n fileName={selectedLeafName}\r\n maxHeight=\"100%\"\r\n />\r\n ) : (\r\n <div className={styles.zipEmpty}>\r\n <DocumentArrowLeft24Regular />\r\n <span>Select a file to preview.</span>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\ninterface ZipTreeNodeProps {\r\n node: ZipNode;\r\n depth: number;\r\n expanded: Set<string>;\r\n selected: string | null;\r\n onToggle: (path: string) => void;\r\n onSelect: (path: string) => void;\r\n}\r\n\r\nfunction ZipTreeNode({ node, depth, expanded, selected, onToggle, onSelect }: ZipTreeNodeProps) {\r\n const styles = useFileViewerStyles();\r\n const isExpanded = expanded.has(node.fullPath);\r\n const isSelected = !node.isFolder && node.fullPath === selected;\r\n\r\n const indent: CSSProperties = { paddingLeft: `${depth * 20}px` };\r\n\r\n if (node.isFolder) {\r\n return (\r\n <>\r\n <div\r\n className={styles.zipRow}\r\n role=\"button\"\r\n tabIndex={0}\r\n onClick={() => onToggle(node.fullPath)}\r\n onKeyDown={(ev) => {\r\n if (ev.key === 'Enter' || ev.key === ' ') {\r\n ev.preventDefault();\r\n onToggle(node.fullPath);\r\n }\r\n }}\r\n style={indent}\r\n >\r\n <span className={styles.zipChevron}>\r\n {isExpanded ? <ChevronDown20Regular /> : <ChevronRight20Regular />}\r\n </span>\r\n <Folder20Regular className={styles.zipIconAccent} />\r\n <span className={styles.zipName}>{node.name}</span>\r\n </div>\r\n {isExpanded &&\r\n node.children.map((child) => (\r\n <ZipTreeNode\r\n key={child.fullPath}\r\n node={child}\r\n depth={depth + 1}\r\n expanded={expanded}\r\n selected={selected}\r\n onToggle={onToggle}\r\n onSelect={onSelect}\r\n />\r\n ))}\r\n </>\r\n );\r\n }\r\n\r\n return (\r\n <Tooltip relationship=\"description\" content={`Size: ${formatSize(node.size)}`}>\r\n <div\r\n className={mergeClasses(styles.zipRow, isSelected && styles.zipRowSelected)}\r\n role=\"button\"\r\n tabIndex={0}\r\n onClick={() => onSelect(node.fullPath)}\r\n onKeyDown={(ev) => {\r\n if (ev.key === 'Enter' || ev.key === ' ') {\r\n ev.preventDefault();\r\n onSelect(node.fullPath);\r\n }\r\n }}\r\n style={indent}\r\n >\r\n <span className={styles.zipChevronSpacer} />\r\n <Document20Regular className={styles.zipIcon} />\r\n <span className={styles.zipName}>{node.name}</span>\r\n </div>\r\n </Tooltip>\r\n );\r\n}\r\n\r\n/**\r\n * Builds a sorted folder tree from fflate's flat entry dict. Folder entries\r\n * (paths ending in `/`) become empty nodes at their own level; nested file\r\n * paths auto-create the folders above them. Folders sort first within each\r\n * level, then alphabetical (case-insensitive). Mirrors `BuildZipTree` on\r\n * the Blazor side.\r\n */\r\nfunction buildZipTree(entries: Record<string, Uint8Array>): ZipNode[] {\r\n const root: ZipNode[] = [];\r\n\r\n for (const [path, bytes] of Object.entries(entries)) {\r\n const normalized = path.replace(/\\\\/g, '/');\r\n const isExplicitFolder = normalized.endsWith('/');\r\n const segments = normalized.split('/').filter(Boolean);\r\n if (segments.length === 0) continue;\r\n\r\n let current = root;\r\n let runningPath = '';\r\n for (let i = 0; i < segments.length - 1; i++) {\r\n const folderName = segments[i]!;\r\n runningPath = runningPath.length === 0 ? folderName : `${runningPath}/${folderName}`;\r\n let folder = current.find((n) => n.isFolder && n.name === folderName);\r\n if (!folder) {\r\n folder = { name: folderName, fullPath: runningPath, isFolder: true, size: 0, children: [] };\r\n current.push(folder);\r\n }\r\n current = folder.children;\r\n }\r\n\r\n const leaf = segments[segments.length - 1]!;\r\n if (isExplicitFolder) {\r\n if (!current.some((n) => n.isFolder && n.name === leaf)) {\r\n const folderPath = segments.join('/');\r\n current.push({ name: leaf, fullPath: folderPath, isFolder: true, size: 0, children: [] });\r\n }\r\n } else {\r\n // `path` (the original key) is what we look up later in `entries`,\r\n // not the normalized form — fflate uses raw entry names as keys.\r\n current.push({\r\n name: leaf,\r\n fullPath: path,\r\n isFolder: false,\r\n size: bytes.length,\r\n children: [],\r\n });\r\n }\r\n }\r\n\r\n sortZipNodes(root);\r\n return root;\r\n}\r\n\r\nfunction sortZipNodes(nodes: ZipNode[]): void {\r\n nodes.sort((a, b) => {\r\n if (a.isFolder !== b.isFolder) return a.isFolder ? -1 : 1;\r\n return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });\r\n });\r\n for (const node of nodes) {\r\n if (node.isFolder) sortZipNodes(node.children);\r\n }\r\n}\r\n\r\nfunction leafName(fullPath: string): string {\r\n const trimmed = fullPath.endsWith('/') ? fullPath.slice(0, -1) : fullPath;\r\n const idx = trimmed.lastIndexOf('/');\r\n return idx < 0 ? trimmed : trimmed.slice(idx + 1);\r\n}\r\n\r\nfunction formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes} B`;\r\n let value = bytes;\r\n for (const unit of ['KB', 'MB', 'GB', 'TB']) {\r\n value /= 1024;\r\n if (value < 1024 || unit === 'TB') return `${value.toFixed(1)} ${unit}`;\r\n }\r\n return `${bytes} B`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Download card — fallback for unknown / non-renderable types.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction DownloadCard({\r\n source,\r\n fileName,\r\n mimeType,\r\n className,\r\n style,\r\n id,\r\n}: {\r\n source: FileSource;\r\n fileName: string;\r\n mimeType: string;\r\n className?: string | undefined;\r\n style?: CSSProperties | undefined;\r\n id?: string | undefined;\r\n}) {\r\n const styles = useFileViewerStyles();\r\n\r\n const handleDownload = () => {\r\n void downloadFileSource(source, fileName);\r\n };\r\n\r\n return (\r\n <div id={id} className={mergeClasses(styles.card, className)} style={style}>\r\n <Document20Regular className={styles.cardIcon} />\r\n <div className={styles.cardBody}>\r\n <div className={styles.cardName}>{fileName}</div>\r\n <div className={styles.cardMime}>{mimeType}</div>\r\n </div>\r\n <Button\r\n appearance=\"primary\"\r\n icon={<ArrowDownload20Regular />}\r\n onClick={handleDownload}\r\n >\r\n Download\r\n </Button>\r\n </div>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Loading / error shells, used while text or object URLs are resolving.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction LoadingShell({\r\n className,\r\n style,\r\n id,\r\n}: {\r\n className?: string | undefined;\r\n style?: CSSProperties | undefined;\r\n id?: string | undefined;\r\n}) {\r\n const styles = useFileViewerStyles();\r\n return (\r\n <div id={id} className={mergeClasses(styles.statusShell, className)} style={style}>\r\n <Spinner size=\"small\" />\r\n </div>\r\n );\r\n}\r\n\r\nfunction ErrorShell({\r\n className,\r\n style,\r\n id,\r\n message,\r\n}: {\r\n className?: string | undefined;\r\n style?: CSSProperties | undefined;\r\n id?: string | undefined;\r\n message: string;\r\n}) {\r\n const styles = useFileViewerStyles();\r\n return (\r\n <div id={id} className={mergeClasses(styles.statusShell, styles.errorShell, className)} style={style}>\r\n <ErrorCircle20Regular />\r\n <span>{message}</span>\r\n </div>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Highlighted-code host — wraps highlight.js token markup in a `<pre><code>`\r\n// and switches the `.hljs-*` token palette (light vs dark) based on the\r\n// active FluentUI theme. Detection is via a hidden probe element painted with\r\n// `tokens.colorNeutralBackground1`; we read its computed luminance to decide\r\n// which palette to apply. This avoids depending on FluentUI's\r\n// `useFluent_unstable` hook and works with any FluentProvider theme\r\n// (including consumer-customized ones).\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction HighlightedCodeBlock({ html }: { html: string }) {\r\n const styles = useFileViewerStyles();\r\n const { probeRef, isDark } = useFluentColorScheme();\r\n return (\r\n <div\r\n className={mergeClasses(styles.codeHost, isDark ? styles.codeHostDark : styles.codeHostLight)}\r\n >\r\n <span ref={probeRef} className={styles.themeProbe} aria-hidden />\r\n {/* `html` is highlight.js token markup — the inner spans only, so we own\r\n the <pre><code> wrapper. `code.hljs` is the class the token palette\r\n rules target. */}\r\n <pre>\r\n <code className=\"hljs\" dangerouslySetInnerHTML={{ __html: html }} />\r\n </pre>\r\n </div>\r\n );\r\n}\r\n\r\nfunction useFluentColorScheme(): {\r\n probeRef: React.RefObject<HTMLSpanElement | null>;\r\n isDark: boolean;\r\n} {\r\n const probeRef = useRef<HTMLSpanElement | null>(null);\r\n const [isDark, setIsDark] = useState(false);\r\n\r\n useLayoutEffect(() => {\r\n const probe = probeRef.current;\r\n if (!probe) return;\r\n\r\n const measure = () => {\r\n const bg = window.getComputedStyle(probe).backgroundColor;\r\n const match = /^rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/.exec(bg);\r\n if (!match) return;\r\n const r = Number(match[1]);\r\n const g = Number(match[2]);\r\n const b = Number(match[3]);\r\n // Standard luminance check; the FluentUI dark themes' neutral background\r\n // sits well below 0.5 and the light themes well above, so this is a\r\n // robust split point even for consumer-tuned themes.\r\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\r\n setIsDark(luminance < 0.5);\r\n };\r\n\r\n measure();\r\n\r\n // FluentProvider toggles theme by swapping its applied class. Observe the\r\n // document body for any class change in the subtree so we re-probe when\r\n // the consumer flips themes at runtime.\r\n const observer = new MutationObserver(measure);\r\n observer.observe(document.body, {\r\n attributes: true,\r\n attributeFilter: ['class'],\r\n subtree: true,\r\n });\r\n return () => observer.disconnect();\r\n }, []);\r\n\r\n return { probeRef, isDark };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Hooks\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface FileTextResult {\r\n text: string | null;\r\n error: string | null;\r\n isLoading: boolean;\r\n}\r\n\r\n/**\r\n * Resolves a `FileSource` to its UTF-8 text content. URL sources are fetched\r\n * lazily; base64/bytes sources are decoded synchronously via `useMemo`.\r\n * Errors land in `error` rather than throwing so the caller can render a\r\n * status shell. Effect / memo dependencies are narrowed to the discriminator\r\n * + `value` so a parent re-render that hands us a freshly-allocated source\r\n * object with the same content doesn't re-fetch or re-decode.\r\n */\r\nfunction useFileText(source: FileSource): FileTextResult {\r\n const fetchUrl = source.kind === 'url' ? source.value : null;\r\n const [urlState, setUrlState] = useState<FileTextResult>({\r\n text: null,\r\n error: null,\r\n isLoading: fetchUrl !== null,\r\n });\r\n\r\n useEffect(() => {\r\n if (fetchUrl === null) {\r\n setUrlState({ text: null, error: null, isLoading: false });\r\n return;\r\n }\r\n let cancelled = false;\r\n setUrlState({ text: null, error: null, isLoading: true });\r\n (async () => {\r\n try {\r\n const response = await fetch(fetchUrl);\r\n if (!response.ok) {\r\n throw new Error(`HTTP ${response.status}`);\r\n }\r\n const text = await response.text();\r\n if (cancelled) return;\r\n setUrlState({ text, error: null, isLoading: false });\r\n } catch (err) {\r\n if (cancelled) return;\r\n setUrlState({\r\n text: null,\r\n error: err instanceof Error ? err.message : 'Failed to load file.',\r\n isLoading: false,\r\n });\r\n }\r\n })();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [fetchUrl]);\r\n\r\n const inlineValue = source.kind === 'url' ? null : source.value;\r\n const inlineKind = source.kind;\r\n\r\n return useMemo<FileTextResult>(() => {\r\n if (inlineKind === 'url') return urlState;\r\n try {\r\n const text =\r\n inlineKind === 'base64'\r\n ? base64ToText(inlineValue as string)\r\n : bytesToText(inlineValue as Uint8Array);\r\n return { text, error: null, isLoading: false };\r\n } catch (err) {\r\n return {\r\n text: null,\r\n error: err instanceof Error ? err.message : 'Failed to decode file.',\r\n isLoading: false,\r\n };\r\n }\r\n }, [inlineKind, inlineValue, urlState]);\r\n}\r\n\r\ninterface FileBytesResult {\r\n bytes: Uint8Array | null;\r\n error: string | null;\r\n isLoading: boolean;\r\n}\r\n\r\n/**\r\n * Resolves a `FileSource` to its raw bytes. URL sources are fetched lazily\r\n * as `ArrayBuffer`; base64 is decoded synchronously; bytes pass through.\r\n * Same dependency-narrowing trick as `useFileText` so a parent re-render\r\n * with a fresh source literal of equal content doesn't refetch.\r\n */\r\nfunction useFileBytes(source: FileSource): FileBytesResult {\r\n const fetchUrl = source.kind === 'url' ? source.value : null;\r\n const [urlState, setUrlState] = useState<FileBytesResult>({\r\n bytes: null,\r\n error: null,\r\n isLoading: fetchUrl !== null,\r\n });\r\n\r\n useEffect(() => {\r\n if (fetchUrl === null) {\r\n setUrlState({ bytes: null, error: null, isLoading: false });\r\n return;\r\n }\r\n let cancelled = false;\r\n setUrlState({ bytes: null, error: null, isLoading: true });\r\n (async () => {\r\n try {\r\n const response = await fetch(fetchUrl);\r\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\r\n const buffer = await response.arrayBuffer();\r\n if (cancelled) return;\r\n setUrlState({ bytes: new Uint8Array(buffer), error: null, isLoading: false });\r\n } catch (err) {\r\n if (cancelled) return;\r\n setUrlState({\r\n bytes: null,\r\n error: err instanceof Error ? err.message : 'Failed to load file.',\r\n isLoading: false,\r\n });\r\n }\r\n })();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [fetchUrl]);\r\n\r\n const inlineValue = source.kind === 'url' ? null : source.value;\r\n const inlineKind = source.kind;\r\n\r\n return useMemo<FileBytesResult>(() => {\r\n if (inlineKind === 'url') return urlState;\r\n try {\r\n const bytes =\r\n inlineKind === 'base64'\r\n ? base64ToBytes(inlineValue as string)\r\n : (inlineValue as Uint8Array);\r\n return { bytes, error: null, isLoading: false };\r\n } catch (err) {\r\n return {\r\n bytes: null,\r\n error: err instanceof Error ? err.message : 'Failed to decode file.',\r\n isLoading: false,\r\n };\r\n }\r\n }, [inlineKind, inlineValue, urlState]);\r\n}\r\n\r\n/**\r\n * Allocates and revokes an object URL for a `bytes`/`base64` source so the\r\n * `<iframe>` has something to load. Returns null for `url` sources — those\r\n * pass straight through and don't need this hook. Dependencies are narrowed\r\n * to the discriminator + value reference so re-renders with an identical\r\n * payload don't churn the blob.\r\n */\r\nfunction useObjectUrl(source: FileSource, mimeType: string): string | null {\r\n const inlineValue = source.kind === 'url' ? null : source.value;\r\n const inlineKind = source.kind;\r\n const [url, setUrl] = useState<string | null>(null);\r\n\r\n useEffect(() => {\r\n if (inlineKind === 'url' || inlineValue === null) {\r\n setUrl(null);\r\n return;\r\n }\r\n const bytes =\r\n inlineKind === 'base64'\r\n ? base64ToBytes(inlineValue as string)\r\n : (inlineValue as Uint8Array);\r\n const blob = new Blob([bytes.buffer as ArrayBuffer], { type: mimeType });\r\n const next = URL.createObjectURL(blob);\r\n setUrl(next);\r\n return () => {\r\n URL.revokeObjectURL(next);\r\n };\r\n }, [inlineKind, inlineValue, mimeType]);\r\n\r\n return url;\r\n}\r\n\r\n/**\r\n * Renders text to highlight.js token markup. Returns `null` only when `text`\r\n * is `null` (caller renders nothing / a status shell); for non-null text it\r\n * always returns a string — highlighted when the language is a registered\r\n * grammar, HTML-escaped-but-unhighlighted otherwise (see `highlightToHtml`).\r\n *\r\n * highlight.js is synchronous, so this is a plain `useMemo` — no loading\r\n * state, no async race, no effect cleanup (unlike the Shiki path it replaced,\r\n * which had to await a WASM-backed highlighter). Theme switching is handled\r\n * downstream by the host class swapping the `.hljs-*` token palette.\r\n */\r\nfunction useHighlightedHtml(text: string | null, language: string): string | null {\r\n return useMemo(\r\n () => (text === null ? null : highlightToHtml(text, language)),\r\n [text, language],\r\n );\r\n}\r\n\r\nasync function downloadFileSource(source: FileSource, fileName: string): Promise<void> {\r\n if (source.kind === 'url') {\r\n const anchor = document.createElement('a');\r\n anchor.href = source.value;\r\n anchor.download = fileName;\r\n document.body.appendChild(anchor);\r\n anchor.click();\r\n document.body.removeChild(anchor);\r\n return;\r\n }\r\n const bytes = source.kind === 'base64' ? base64ToBytes(source.value) : source.value;\r\n triggerBlobDownload(bytes, fileName);\r\n}\r\n\r\nconst useFileViewerStyles = makeStyles({\r\n wrapper: {\r\n width: '100%',\r\n },\r\n imageCenter: {\r\n // Fills its parent on both axes when the parent is sized (zip preview\r\n // pane), collapses to content height when the parent is unsized\r\n // (everywhere else). The framed `ImageViewer` inside is `inline-flex`\r\n // and shrink-to-content, so the flex centering puts it in the middle\r\n // both horizontally and vertically.\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '100%',\r\n height: '100%',\r\n },\r\n codeFrame: {\r\n overflow: 'auto',\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n codeHost: {\r\n // Position relative so the absolutely-placed theme probe sits inside our\r\n // box. Column-flex chain so the inner <pre> (the actual scrollable content\r\n // surface) can stretch to fill the host. `height: 100%` resolves to\r\n // `auto` when the parent has no defined height — keeps non-zip usage\r\n // sized to content. minHeight: 0 lets `overflow:auto` on the pre take\r\n // effect instead of pushing the host taller than the parent.\r\n position: 'relative',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n height: '100%',\r\n minHeight: 0,\r\n '& pre': {\r\n margin: 0,\r\n padding: tokens.spacingHorizontalM,\r\n fontFamily: tokens.fontFamilyMonospace,\r\n fontSize: tokens.fontSizeBase200,\r\n lineHeight: tokens.lineHeightBase200,\r\n // Claim the remaining height in the column-flex chain and own the\r\n // scroll. Without `flex: 1` the pre would shrink to its content height\r\n // and leave the host's background visible below it.\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n overflow: 'auto',\r\n // The code surface sits on the neutral-1 layer; the surrounding\r\n // `codeFrame` uses neutral-2 so the two read as nested panes. Token\r\n // colors come from the palette below; the base text color is the\r\n // theme's foreground.\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n color: tokens.colorNeutralForeground1,\r\n },\r\n '& code.hljs': {\r\n background: 'transparent',\r\n padding: 0,\r\n },\r\n },\r\n // github.css-inspired light palette — ported verbatim from the Blazor\r\n // `hljs-theme.css` so the two FileViewers color identically. Syntax tokens\r\n // are a semantic palette (not expressible as neutral design tokens), so\r\n // hex literals are appropriate here, the same exception the Blazor side and\r\n // the FluentMessageBar intent colors take.\r\n codeHostLight: {\r\n '& .hljs-comment, & .hljs-quote': { color: '#6a737d', fontStyle: 'italic' },\r\n '& .hljs-keyword, & .hljs-selector-tag, & .hljs-literal, & .hljs-section, & .hljs-doctag': {\r\n color: '#d73a49',\r\n },\r\n '& .hljs-string, & .hljs-attr, & .hljs-attribute, & .hljs-addition, & .hljs-template-tag': {\r\n color: '#032f62',\r\n },\r\n '& .hljs-number, & .hljs-symbol, & .hljs-bullet, & .hljs-meta, & .hljs-link': {\r\n color: '#005cc5',\r\n },\r\n '& .hljs-title, & .hljs-type, & .hljs-built_in': { color: '#6f42c1' },\r\n '& .hljs-tag .hljs-name, & .hljs-variable, & .hljs-template-variable, & .hljs-params': {\r\n color: '#e36209',\r\n },\r\n '& .hljs-regexp, & .hljs-deletion': { color: '#b31d28' },\r\n '& .hljs-strong': { fontWeight: 'bold' },\r\n '& .hljs-emphasis': { fontStyle: 'italic' },\r\n },\r\n // github-dark.css-inspired dark palette — also ported from the Blazor theme.\r\n codeHostDark: {\r\n '& .hljs-comment, & .hljs-quote': { color: '#8b949e', fontStyle: 'italic' },\r\n '& .hljs-keyword, & .hljs-selector-tag, & .hljs-literal, & .hljs-section, & .hljs-doctag': {\r\n color: '#ff7b72',\r\n },\r\n '& .hljs-string, & .hljs-attr, & .hljs-attribute, & .hljs-addition, & .hljs-template-tag': {\r\n color: '#a5d6ff',\r\n },\r\n '& .hljs-number, & .hljs-symbol, & .hljs-bullet, & .hljs-meta, & .hljs-link': {\r\n color: '#79c0ff',\r\n },\r\n '& .hljs-title, & .hljs-type, & .hljs-built_in': { color: '#d2a8ff' },\r\n '& .hljs-tag .hljs-name, & .hljs-variable, & .hljs-template-variable, & .hljs-params': {\r\n color: '#ffa657',\r\n },\r\n '& .hljs-regexp, & .hljs-deletion': { color: '#ffa198' },\r\n '& .hljs-strong': { fontWeight: 'bold' },\r\n '& .hljs-emphasis': { fontStyle: 'italic' },\r\n },\r\n themeProbe: {\r\n // Hidden 1×1 probe carrying `tokens.colorNeutralBackground1` so the\r\n // luminance check in `useFluentColorScheme` resolves the active theme.\r\n // Visibility-hidden (not display-none) so getComputedStyle returns the\r\n // resolved color in every browser.\r\n position: 'absolute',\r\n visibility: 'hidden',\r\n pointerEvents: 'none',\r\n width: '1px',\r\n height: '1px',\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n fallbackPre: {\r\n margin: 0,\r\n padding: tokens.spacingHorizontalM,\r\n fontFamily: tokens.fontFamilyMonospace,\r\n fontSize: tokens.fontSizeBase200,\r\n lineHeight: tokens.lineHeightBase200,\r\n whiteSpace: 'pre',\r\n overflow: 'auto',\r\n },\r\n markdownFrame: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n overflow: 'hidden',\r\n // Claim full parent height when there is one. Resolves to `auto` (i.e.\r\n // shrink-to-content) when the parent's height is undefined, so default\r\n // non-zip usage still sizes to the markdown content.\r\n height: '100%',\r\n },\r\n markdownBody: {\r\n overflow: 'auto',\r\n borderTop: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n // Fills the remaining frame height inside the column-flex parent —\r\n // pairs with `markdownFrame: height 100%` so the Source tab's pre\r\n // (and the Preview tab's content) get a real container height to\r\n // anchor against. minHeight: 0 lets overflow:auto kick in instead of\r\n // being suppressed by the flex child's content-min-size default.\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n },\r\n markdownPreview: {\r\n padding: tokens.spacingHorizontalL,\r\n fontFamily: tokens.fontFamilyBase,\r\n color: tokens.colorNeutralForeground1,\r\n '& h1, & h2, & h3, & h4, & h5, & h6': {\r\n marginTop: tokens.spacingVerticalL,\r\n marginBottom: tokens.spacingVerticalS,\r\n },\r\n '& p': {\r\n marginTop: 0,\r\n marginBottom: tokens.spacingVerticalM,\r\n lineHeight: tokens.lineHeightBase300,\r\n },\r\n '& code': {\r\n fontFamily: tokens.fontFamilyMonospace,\r\n fontSize: '0.9em',\r\n padding: `0 ${tokens.spacingHorizontalXS}`,\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderRadius: tokens.borderRadiusSmall,\r\n },\r\n '& pre': {\r\n padding: tokens.spacingHorizontalM,\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderRadius: tokens.borderRadiusMedium,\r\n overflow: 'auto',\r\n },\r\n '& pre code': {\r\n padding: 0,\r\n backgroundColor: 'transparent',\r\n },\r\n '& a': {\r\n color: tokens.colorBrandForegroundLink,\r\n },\r\n '& blockquote': {\r\n margin: 0,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n borderLeft: `${tokens.strokeWidthThicker} solid ${tokens.colorNeutralStroke2}`,\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n '& table': {\r\n borderCollapse: 'collapse',\r\n },\r\n '& th, & td': {\r\n padding: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalS}`,\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n },\r\n },\r\n iframe: {\r\n width: '100%',\r\n border: 'none',\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n borderRadius: tokens.borderRadiusMedium,\r\n },\r\n card: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalM,\r\n padding: tokens.spacingHorizontalM,\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n cardIcon: {\r\n flexShrink: 0,\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n cardBody: {\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: tokens.spacingVerticalXXS,\r\n },\r\n cardName: {\r\n fontFamily: tokens.fontFamilyBase,\r\n fontSize: tokens.fontSizeBase300,\r\n fontWeight: tokens.fontWeightSemibold,\r\n color: tokens.colorNeutralForeground1,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n },\r\n cardMime: {\r\n fontFamily: tokens.fontFamilyMonospace,\r\n fontSize: tokens.fontSizeBase200,\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n statusShell: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: tokens.spacingHorizontalS,\r\n padding: tokens.spacingHorizontalL,\r\n minHeight: '120px',\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n errorShell: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n zipFrame: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n minHeight: '0',\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n overflow: 'hidden',\r\n },\r\n zipTree: {\r\n flexShrink: 0,\r\n overflow: 'auto',\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n // Width is set inline by ZipViewer (driven by the splitter); we just\r\n // own the scroll + background here.\r\n },\r\n zipSplitter: {\r\n flexShrink: 0,\r\n width: '6px',\r\n cursor: 'col-resize',\r\n position: 'relative',\r\n backgroundColor: tokens.colorNeutralStroke2,\r\n transitionProperty: 'background-color',\r\n transitionDuration: tokens.durationFast,\r\n ':hover': {\r\n backgroundColor: tokens.colorBrandStroke2,\r\n },\r\n ':focus-visible': {\r\n outline: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`,\r\n outlineOffset: '0',\r\n },\r\n // Wider invisible hit-target so the divider grabs cleanly even on\r\n // trackpads where 6px is unforgiving. The visible bar stays 6px;\r\n // ::before extends the hover/grab area ±4px on each side.\r\n '::before': {\r\n content: '\"\"',\r\n position: 'absolute',\r\n top: 0,\r\n bottom: 0,\r\n left: '-4px',\r\n right: '-4px',\r\n },\r\n },\r\n zipSplitterGrip: {\r\n // Subtle three-dot grip in the middle of the divider; visual only,\r\n // pointer-events pass through to the splitter container.\r\n position: 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n width: '2px',\r\n height: '24px',\r\n borderRadius: tokens.borderRadiusCircular,\r\n backgroundColor: tokens.colorNeutralForeground3,\r\n pointerEvents: 'none',\r\n opacity: 0.5,\r\n },\r\n zipPreview: {\r\n flex: '1 1 auto',\r\n minWidth: '0',\r\n overflow: 'auto',\r\n padding: tokens.spacingHorizontalM,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n zipRow: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n padding: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalS}`,\r\n cursor: 'pointer',\r\n userSelect: 'none',\r\n fontSize: tokens.fontSizeBase300,\r\n color: tokens.colorNeutralForeground1,\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground2Hover,\r\n },\r\n ':focus-visible': {\r\n outline: `${tokens.strokeWidthThin} solid ${tokens.colorStrokeFocus2}`,\r\n outlineOffset: '-1px',\r\n },\r\n },\r\n zipRowSelected: {\r\n backgroundColor: tokens.colorBrandBackground2,\r\n color: tokens.colorBrandForeground2,\r\n ':hover': {\r\n backgroundColor: tokens.colorBrandBackground2Hover,\r\n },\r\n },\r\n zipChevron: {\r\n flexShrink: 0,\r\n display: 'inline-flex',\r\n width: '20px',\r\n height: '20px',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n zipChevronSpacer: {\r\n flexShrink: 0,\r\n width: '20px',\r\n height: '20px',\r\n },\r\n zipIcon: {\r\n flexShrink: 0,\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n zipIconAccent: {\r\n flexShrink: 0,\r\n color: tokens.colorBrandForeground1,\r\n },\r\n zipName: {\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n },\r\n zipEmpty: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: tokens.spacingVerticalS,\r\n padding: tokens.spacingHorizontalL,\r\n minHeight: '160px',\r\n color: tokens.colorNeutralForeground3,\r\n textAlign: 'center',\r\n },\r\n});\r\n","import {\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n Field,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { ArrowDownload20Regular, Dismiss20Regular, Eye20Regular } from '@fluentui/react-icons';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport { usePowerPortalsPro } from '@powerportalspro/react';\r\nimport { useCallback, useEffect, useId, useMemo, useState } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useFileColumn, useGuardedSetValue } from './base/use-column-edit.js';\r\nimport {\r\n EMPTY_GUID,\r\n base64ToBytes,\r\n categorizeFile,\r\n fetchFileBytes,\r\n getMimeFromFileName,\r\n triggerBlobDownload,\r\n} from './base/file-helpers.js';\r\nimport { useFileUpload } from './base/use-file-upload.js';\r\nimport { FileViewer, type FileSource } from '../viewers/file-viewer.js';\r\n\r\nexport interface FileEditProps extends BaseColumnEditProps {\r\n /**\r\n * MIME types or file extensions the picker accepts (e.g. `'application/pdf'`,\r\n * `'.docx,.xlsx'`). Pass-through to the underlying `<input type=\"file\">`'s\r\n * `accept` attribute. Defaults to no restriction.\r\n */\r\n accept?: string;\r\n /**\r\n * Maximum file size in bytes the consumer is willing to accept. When\r\n * omitted, no client-side check runs — server-side\r\n * <c>EnvironmentFileSettings.maxUploadSize</c> still applies on save.\r\n * Files exceeding the limit are rejected with a console warning before\r\n * any state updates.\r\n */\r\n maxFileSizeBytes?: number;\r\n /**\r\n * Custom text shown inside the empty drop zone. Defaults to\r\n * <c>\"Drop a file here, or click to browse\"</c>. Localize at the consumer\r\n * boundary by reading from your localizer and passing the result.\r\n */\r\n uploadHintText?: string;\r\n}\r\n\r\n/**\r\n * Renders a drop zone for uploading a file into a Dataverse `FileValue`\r\n * column; once a file is present, shows the file name with download /\r\n * remove icons. The actual binary content is round-tripped via the column's\r\n * wire-format <c>value</c> field as base64.\r\n *\r\n * Upload flow:\r\n * 1. User drops a file or clicks the drop zone (which proxies a hidden\r\n * `<input type=\"file\">`).\r\n * 2. The file is read as a Data URL via `FileReader`, base64-decoded, and\r\n * stored on the record via `setValue({ fileName, id: emptyGuid, value })`.\r\n * 3. When the consumer's parent context saves the record, the framework's\r\n * file-upload pipeline picks up the new content and writes it to\r\n * Dataverse, replacing the empty `id` with the assigned file id.\r\n *\r\n * Download flow:\r\n * - When `value.value` is non-null (the upload happened in this session),\r\n * the in-memory bytes are turned into a blob and saved with the file name.\r\n * - Otherwise <c>getFileInfoAsync(table, id, column, includeData=true)</c>\r\n * fetches the binary on-demand.\r\n */\r\nexport function FileEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n accept,\r\n maxFileSizeBytes,\r\n uploadHintText,\r\n validate,\r\n}: FileEditProps) {\r\n const { value, setValue: rawSetValue, fileId, hydrateValue } = useFileColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const { ppp } = usePowerPortalsPro();\r\n const styles = useFileEditStyles();\r\n const t = useT();\r\n const reactId = useId();\r\n const inputId = id ?? reactId;\r\n const [isDownloading, setIsDownloading] = useState(false);\r\n const [isLoadingMetadata, setIsLoadingMetadata] = useState(false);\r\n const [isPreviewLoading, setIsPreviewLoading] = useState(false);\r\n const [previewSource, setPreviewSource] = useState<FileSource | null>(null);\r\n\r\n // Lazy-load the file metadata when the record arrives with only the\r\n // sibling `{columnName}id` GuidValue populated. The default record\r\n // fetch doesn't return file metadata either — after a refresh the\r\n // data column is empty and only the id column survives. Hits the\r\n // file-info endpoint with `includeData=false` since FileEdit shows\r\n // just the file name (no preview); the binary is fetched on demand\r\n // when the user clicks the download icon.\r\n useEffect(() => {\r\n // Already have file metadata loaded — nothing to do. For File\r\n // columns the data column itself surfaces post-fetch as a GuidValue\r\n // and the `useFileColumn` hook synthesizes a `FileColumnValue` from\r\n // the in-record `{name}_name` companion, so this skips for them.\r\n // Image columns have no `_name` companion, so `value` stays\r\n // undefined and we drop into the lazy fetch below.\r\n if (value) return;\r\n if (!fileId) return;\r\n if (!record?.tableName || !record.id) return;\r\n if (record.id === EMPTY_GUID) return;\r\n\r\n let cancelled = false;\r\n setIsLoadingMetadata(true);\r\n (async () => {\r\n try {\r\n const info = await ppp.getFileInfoAsync(record.tableName, record.id!, columnName, false);\r\n if (cancelled) return;\r\n if (info.fileName) {\r\n hydrateValue({\r\n fileName: info.fileName,\r\n id: fileId,\r\n value: null,\r\n });\r\n }\r\n } catch (err) {\r\n if (cancelled) return;\r\n // eslint-disable-next-line no-console\r\n console.warn(`PowerPortalsPro FileEdit: metadata fetch failed for ${columnName}`, err);\r\n } finally {\r\n if (!cancelled) setIsLoadingMetadata(false);\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [columnName, fileId, hydrateValue, ppp, record?.id, record?.tableName, value]);\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n const isInteractable = !resolvedReadOnly && !resolvedDisabled;\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'string',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n // The required check just needs \"is there a value\" semantics; the file\r\n // name is the natural sentinel — empty string means no file, present\r\n // means there is one. Using the string-kind validator avoids defining\r\n // a dedicated `file` kind that has the same required-only behavior.\r\n value: value?.fileName ?? '',\r\n },\r\n validate,\r\n );\r\n\r\n const upload = useFileUpload({\r\n setValue,\r\n enabled: isInteractable,\r\n warnPrefix: 'PowerPortalsPro FileEdit',\r\n ...(maxFileSizeBytes !== undefined && { maxFileSizeBytes }),\r\n });\r\n\r\n const handleDownload = useCallback(async () => {\r\n if (!value) return;\r\n setIsDownloading(true);\r\n try {\r\n const bytes = value.value\r\n ? base64ToBytes(value.value)\r\n : await fetchFileBytes(ppp, record?.tableName, record?.id, columnName);\r\n if (bytes === null) return;\r\n triggerBlobDownload(bytes, value.fileName);\r\n } finally {\r\n setIsDownloading(false);\r\n }\r\n }, [columnName, ppp, record, value]);\r\n\r\n // Whether the bound file can be rendered inline by `<FileViewer>`.\r\n // Drives whether the Preview button is shown — for `categorizeFile()`\r\n // returning `'download'` (binaries we can't render in-browser) the\r\n // button would just open a card with another download button, so we\r\n // hide it instead.\r\n const isPreviewable = useMemo(() => {\r\n if (!value?.fileName) return false;\r\n const mime = getMimeFromFileName(value.fileName);\r\n return categorizeFile({ mimeType: mime, fileName: value.fileName }) !== 'download';\r\n }, [value?.fileName]);\r\n\r\n // Opens the preview dialog. Resolves the binary first (uses the in-memory\r\n // bytes if `value.value` is loaded; otherwise fetches via\r\n // `getFileInfoAsync`) and hands it to `<FileViewer>` as a `base64`\r\n // source. Hydrates the column value with the fetched binary so a\r\n // subsequent download / preview hits the cached bytes.\r\n const handlePreview = useCallback(async () => {\r\n if (!value) return;\r\n setIsPreviewLoading(true);\r\n try {\r\n let base64 = value.value ?? null;\r\n let resolvedFileName = value.fileName;\r\n if (!base64) {\r\n if (!record?.tableName || !record.id) return;\r\n const info = await ppp.getFileInfoAsync(record.tableName, record.id, columnName, true);\r\n if (!info.fileData) return;\r\n base64 = info.fileData;\r\n // Prefer the server's fileName when available — it's the\r\n // authoritative one. Falls back to the cached value when the\r\n // server didn't return a name (which would itself be a bug worth\r\n // logging).\r\n resolvedFileName = info.fileName || value.fileName;\r\n hydrateValue({\r\n fileName: resolvedFileName,\r\n id: value.id,\r\n value: base64,\r\n });\r\n }\r\n const mime = getMimeFromFileName(resolvedFileName);\r\n setPreviewSource({\r\n kind: 'base64',\r\n value: base64,\r\n mimeType: mime,\r\n fileName: resolvedFileName,\r\n });\r\n } finally {\r\n setIsPreviewLoading(false);\r\n }\r\n }, [columnName, hydrateValue, ppp, record, value]);\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n const dropZoneClass = mergeClasses(\r\n styles.dropZone,\r\n upload.isDragOver && styles.dropZoneActive,\r\n !isInteractable && styles.dropZoneDisabled,\r\n className,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-file-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <div className={styles.wrapper} style={style}>\r\n <input\r\n ref={upload.inputRef}\r\n id={inputId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={!isInteractable}\r\n className={styles.hiddenInput}\r\n onChange={upload.onInputChange}\r\n />\r\n <div className={dropZoneClass} {...upload.dropZoneHandlers}>\r\n <span className={styles.dropZoneText}>\r\n {value\r\n ? value.fileName\r\n : fileId\r\n ? (isLoadingMetadata ? 'Loading…' : '')\r\n : (uploadHintText ?? 'Drop a file here, or click to browse')}\r\n </span>\r\n {value && (\r\n // Action overlay — top-right of the drop zone, sitting on top\r\n // of the filename text. Translucent backdrop + blur match\r\n // ImageEdit's overlay so the two edits look like siblings.\r\n // Each button stops click propagation so triggering an action\r\n // doesn't also fire the drop zone's \"open file picker\"\r\n // handler.\r\n <div className={styles.actions}>\r\n {isPreviewable && (\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Eye20Regular />}\r\n aria-label={`Preview ${value.fileName}`}\r\n disabled={isPreviewLoading}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n void handlePreview();\r\n }}\r\n />\r\n )}\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ArrowDownload20Regular />}\r\n aria-label={`Download ${value.fileName}`}\r\n disabled={isDownloading}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n void handleDownload();\r\n }}\r\n />\r\n {isInteractable && (\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Dismiss20Regular />}\r\n aria-label={`Remove ${value.fileName}`}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n setValue(null);\r\n }}\r\n />\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n {/* Preview dialog. Mounted via the `open` prop so opening / closing\r\n doesn't unmount the FileViewer between toggles for the same file\r\n — but we DO drop the source on close (via the surface-render gate)\r\n so reopening a different file doesn't show stale content briefly. */}\r\n <Dialog\r\n open={previewSource !== null}\r\n onOpenChange={(_, data) => {\r\n if (!data.open) setPreviewSource(null);\r\n }}\r\n modalType=\"modal\"\r\n >\r\n <DialogSurface className={styles.previewDialogSurface}>\r\n {previewSource && value && (\r\n <DialogBody>\r\n <DialogTitle>{value.fileName}</DialogTitle>\r\n <DialogContent className={styles.previewDialogContent}>\r\n <FileViewer source={previewSource} fileName={value.fileName} />\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"secondary\" onClick={() => setPreviewSource(null)}>\r\n Close\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n )}\r\n </DialogSurface>\r\n </Dialog>\r\n </Field>\r\n );\r\n}\r\n\r\nconst useFileEditStyles = makeStyles({\r\n wrapper: {\r\n width: '100%',\r\n },\r\n hiddenInput: {\r\n position: 'absolute',\r\n width: '1px',\r\n height: '1px',\r\n overflow: 'hidden',\r\n clip: 'rect(0, 0, 0, 0)',\r\n border: 'none',\r\n },\r\n dropZone: {\r\n // Position-relative so the action overlay can sit inside the drop\r\n // zone in the top-right corner. Mirrors ImageEdit's `previewFrame`\r\n // pattern so the two edits feel like siblings.\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n minHeight: '48px',\r\n paddingInline: tokens.spacingHorizontalM,\r\n paddingBlock: tokens.spacingVerticalM,\r\n cursor: 'pointer',\r\n border: `${tokens.strokeWidthThin} dashed ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n color: tokens.colorNeutralForeground1,\r\n transition: 'background-color 0.15s ease, box-shadow 0.15s ease',\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground2Hover,\r\n },\r\n ':focus-visible': {\r\n outline: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`,\r\n outlineOffset: '2px',\r\n },\r\n },\r\n // Active (drag-over) state. Inset box-shadow rather than overriding\r\n // borderColor avoids Griffel's strict-typing lockdown that bans mixing\r\n // `border` shorthand and `borderColor` longhand across rules in the\r\n // same `makeStyles` call.\r\n dropZoneActive: {\r\n backgroundColor: tokens.colorBrandBackground2,\r\n boxShadow: `inset 0 0 0 ${tokens.strokeWidthThick} ${tokens.colorBrandStroke1}`,\r\n },\r\n dropZoneDisabled: {\r\n cursor: 'not-allowed',\r\n opacity: 0.6,\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n },\r\n dropZoneText: {\r\n fontFamily: tokens.fontFamilyBase,\r\n fontSize: tokens.fontSizeBase300,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n // Reserve space on the right for the action overlay so a long\r\n // filename ellipsizes before colliding with the buttons. ~120px\r\n // accommodates three buttons (Preview / Download / X) plus their\r\n // gaps + the overlay's own padding.\r\n paddingRight: '120px',\r\n },\r\n // Overlay corner toolbar — top-right of the drop zone, sitting on top\r\n // of the filename text. Translucent backdrop keeps the icons readable\r\n // against any background; rounded corners + soft shadow separate it\r\n // visually from the frame edge. Same look as ImageEdit's overlay.\r\n actions: {\r\n position: 'absolute',\r\n top: tokens.spacingVerticalS,\r\n right: tokens.spacingHorizontalS,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n paddingLeft: tokens.spacingHorizontalXS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n paddingTop: '2px',\r\n paddingBottom: '2px',\r\n backgroundColor: tokens.colorNeutralBackgroundAlpha,\r\n backdropFilter: 'blur(4px)',\r\n borderRadius: tokens.borderRadiusMedium,\r\n boxShadow: tokens.shadow4,\r\n // Sit above the filename text inside the frame's stacking context.\r\n zIndex: 1,\r\n },\r\n // Preview dialog grows wider than Fluent's default surface so PDF /\r\n // markdown / image renderers have room to breathe. Capped at 90vw/90vh\r\n // so it stays inside the viewport on smaller screens.\r\n previewDialogSurface: {\r\n maxWidth: 'min(1200px, 90vw)',\r\n width: '90vw',\r\n },\r\n previewDialogContent: {\r\n // Let the FileViewer's own inner scroll-area handle overflow; we just\r\n // need the content slot to not constrain it.\r\n overflow: 'hidden',\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n },\r\n});\r\n\r\n","import { Button, Field, makeStyles, mergeClasses, Spinner, tokens } from '@fluentui/react-components';\r\nimport {\r\n ArrowDownload20Regular,\r\n Dismiss20Regular,\r\n Edit20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { useColumnMetadata, useRecordContext, usePowerPortalsPro, useT } from '@powerportalspro/react';\r\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { useFileColumn, useGuardedSetValue } from './base/use-column-edit.js';\r\nimport {\r\n EMPTY_GUID,\r\n base64ToBytes,\r\n fetchFileBytes,\r\n triggerBlobDownload,\r\n} from './base/file-helpers.js';\r\nimport { useFileUpload } from './base/use-file-upload.js';\r\nimport { FileEdit, type FileEditProps } from './file-edit.js';\r\nimport { ImageViewer, type ImageSource, type ImageViewerHandle } from '../viewers/image-viewer.js';\r\n\r\nexport interface ImageEditProps extends BaseColumnEditProps {\r\n /**\r\n * MIME types or file extensions the picker accepts. Defaults to\r\n * <c>\"image/*\"</c>. Override to narrow further (e.g. <c>\".png,.jpg\"</c>)\r\n * or pass an empty string to accept anything.\r\n */\r\n accept?: string;\r\n /**\r\n * When true (default), renders the bound image as an inline preview\r\n * once the binary content is available. When false, the component\r\n * behaves identically to {@link FileEdit} (drop zone + file name +\r\n * download/clear icons) — no image preview, no preview lazy-load.\r\n */\r\n displayImage?: boolean;\r\n /**\r\n * Maximum file size in bytes the consumer is willing to accept. When\r\n * omitted, no client-side check runs — server-side\r\n * <c>EnvironmentFileSettings.maxUploadSize</c> still applies on save.\r\n */\r\n maxFileSizeBytes?: number;\r\n /**\r\n * Custom text shown inside the empty drop zone. Defaults to\r\n * <c>\"Drop an image here, or click to browse\"</c>.\r\n */\r\n uploadHintText?: string;\r\n /**\r\n * Output format used when committing edits back to the bound column.\r\n * Defaults to `'auto'` — JPEG inputs stay JPEG, everything else emits\r\n * PNG. Pin to `'png'` for lossless writes regardless of source format,\r\n * or to `'jpeg'` for size-sensitive photo columns. Forwarded to\r\n * {@link ImageViewerProps.outputFormat}.\r\n */\r\n outputFormat?: 'png' | 'jpeg' | 'auto';\r\n /**\r\n * JPEG quality in `[0, 1]`. Ignored when `outputFormat` is `'png'`.\r\n * Defaults to `0.9`. Forwarded to\r\n * {@link ImageViewerProps.outputQuality}.\r\n */\r\n outputQuality?: number;\r\n}\r\n\r\n/**\r\n * Image-aware wrapper around {@link FileEdit} — when there's no image to\r\n * display (the column is empty, or the binary content hasn't loaded yet,\r\n * or `displayImage` is off), this component renders a `<FileEdit>` and\r\n * delegates everything: drop zone, upload, file-name display,\r\n * download/clear icons, validation, metadata-driven label/required/readOnly\r\n * resolution.\r\n *\r\n * When `displayImage` is on AND the binary content is available, the\r\n * inline preview takes over from the drop zone. Read-only mode in this\r\n * state shows just the image with click-to-download.\r\n *\r\n * Image-specific work this component owns (and `FileEdit` doesn't):\r\n * <list type=\"bullet\">\r\n * <item>The binary lazy-load — `FileEdit` only fetches metadata after\r\n * refresh; ImageEdit also fetches the bytes so the preview can render.</item>\r\n * <item>The `<img>` rendering, MIME-type guess, and read-only\r\n * click-to-download UX.</item>\r\n * </list>\r\n */\r\nexport function ImageEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n accept = 'image/*',\r\n displayImage = true,\r\n maxFileSizeBytes,\r\n uploadHintText,\r\n outputFormat = 'auto',\r\n outputQuality = 0.9,\r\n validate,\r\n}: ImageEditProps) {\r\n const { value, setValue: rawSetValue, fileId, hydrateValue } = useFileColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const { ppp } = usePowerPortalsPro();\r\n const styles = useImageEditStyles();\r\n const t = useT();\r\n const reactId = useId();\r\n const inputId = id ?? reactId;\r\n const [isDownloading, setIsDownloading] = useState(false);\r\n // Edit mode toggles ImageViewer's toolbar/croppable on. The viewer's\r\n // toolbar surfaces its own Save (green check) and Cancel (red prohibited\r\n // circle) buttons via the `onSave` / `onCancel` props below — Save's\r\n // enabled state is driven by the viewer's internal `isDirty`, so we\r\n // don't need to mirror dirty state into ImageEdit anymore.\r\n const [isEditing, setIsEditing] = useState(false);\r\n const viewerRef = useRef<ImageViewerHandle>(null);\r\n\r\n // Resolve readOnly/disabled here (rather than after the delegation\r\n // branch) so they can feed `useFileUpload` — hook calls have to run on\r\n // every render in the same order regardless of which branch we render.\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n const isInteractable = !resolvedReadOnly && !resolvedDisabled;\r\n\r\n // Upload behavior shared with `FileEdit` — clicking the preview\r\n // image (or dragging a file onto it) replaces the current image with\r\n // the new one. The hook returns a hidden-input ref + a bag of\r\n // drop-zone handlers that get spread on whichever element the consumer\r\n // wants to act as the drop target. Hook is called unconditionally so\r\n // its handlers exist even when we're delegating to `<FileEdit>` —\r\n // they're just never bound to the DOM in that branch.\r\n const upload = useFileUpload({\r\n setValue,\r\n enabled: isInteractable,\r\n warnPrefix: 'PowerPortalsPro ImageEdit',\r\n ...(maxFileSizeBytes !== undefined && { maxFileSizeBytes }),\r\n });\r\n\r\n // Lazy-load the binary content for preview when `displayImage` is on.\r\n // `FileEdit`'s built-in lazy-load fetches metadata only (the file name);\r\n // image preview needs the bytes too. Both effects fire in parallel when\r\n // delegating to FileEdit — the binary one supersedes since it lands a\r\n // strictly-richer FileValue (filename + bytes) via the same hydrate\r\n // path. The cost is one extra HTTP round-trip with `includeData=false`,\r\n // accepted to keep FileEdit's lazy-load self-contained.\r\n useEffect(() => {\r\n if (!displayImage) return;\r\n if (value?.value) return; // already have bytes\r\n if (!fileId) return;\r\n if (!record?.tableName || !record.id) return;\r\n if (record.id === EMPTY_GUID) return;\r\n\r\n let cancelled = false;\r\n (async () => {\r\n try {\r\n const info = await ppp.getFileInfoAsync(record.tableName, record.id!, columnName, true);\r\n if (cancelled) return;\r\n if (info.fileData) {\r\n hydrateValue({\r\n fileName: info.fileName ?? value?.fileName ?? '',\r\n id: fileId,\r\n value: info.fileData,\r\n });\r\n }\r\n } catch (err) {\r\n if (cancelled) return;\r\n // eslint-disable-next-line no-console\r\n console.warn(`PowerPortalsPro ImageEdit: preview fetch failed for ${columnName}`, err);\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [columnName, displayImage, fileId, hydrateValue, ppp, record?.id, record?.tableName, value?.fileName, value?.value]);\r\n\r\n const handleDownload = useCallback(async () => {\r\n if (!value) return;\r\n setIsDownloading(true);\r\n try {\r\n const bytes = value.value\r\n ? base64ToBytes(value.value)\r\n : await fetchFileBytes(ppp, record?.tableName, record?.id, columnName);\r\n if (bytes === null) return;\r\n triggerBlobDownload(bytes, value.fileName);\r\n } finally {\r\n setIsDownloading(false);\r\n }\r\n }, [columnName, ppp, record, value]);\r\n\r\n const handleEnterEdit = useCallback(() => {\r\n setIsEditing(true);\r\n }, []);\r\n\r\n const handleCancelEdit = useCallback(() => {\r\n // ImageViewer's internal angle/flip/crop state is wiped by the\r\n // `key={isEditing ? 'edit' : 'view'}` remount below — no extra\r\n // imperative cleanup needed here.\r\n setIsEditing(false);\r\n }, []);\r\n\r\n const handleSaveEdit = useCallback(async () => {\r\n const handle = viewerRef.current;\r\n if (!handle) {\r\n setIsEditing(false);\r\n return;\r\n }\r\n const result = await handle.commit();\r\n if (!result) {\r\n // Nothing dirty to commit — exit edit mode silently.\r\n setIsEditing(false);\r\n return;\r\n }\r\n // Preserve the original stem; pick the extension from the committed\r\n // mime type so it matches whatever format ImageViewer was configured\r\n // to produce (PNG by default, JPEG when `outputFormat=\"jpeg\"` is set).\r\n const origName = value?.fileName ?? 'image';\r\n const dot = origName.lastIndexOf('.');\r\n const stem = dot >= 0 ? origName.slice(0, dot) : origName;\r\n const ext = result.mimeType === 'image/jpeg' ? 'jpg' : 'png';\r\n setValue({\r\n fileName: `${stem}.${ext}`,\r\n id: EMPTY_GUID,\r\n value: result.base64,\r\n });\r\n setIsEditing(false);\r\n }, [setValue, value?.fileName]);\r\n\r\n const hasImagePreview = displayImage && value?.value != null;\r\n\r\n // \"Loading\" = `displayImage` is on, the record has a stored file (a non-\r\n // empty fileId), but the bytes haven't arrived yet. The lazy-load effect\r\n // above kicks off the fetch; this flag drives a placeholder that\r\n // overlays a Spinner-with-\"Loading\" label until the bytes hydrate. For\r\n // an empty column (no fileId) or a brand-new record (record.id ==\r\n // EMPTY_GUID) we fall through to the FileEdit delegation — there's\r\n // nothing to load.\r\n const isLoadingImagePreview =\r\n displayImage &&\r\n !value?.value &&\r\n !!fileId &&\r\n !!record?.id &&\r\n record.id !== EMPTY_GUID;\r\n\r\n // Memoize the ImageViewer source so its reference identity is stable\r\n // across renders that don't change the underlying bytes/filename.\r\n // Important: ImageViewer has `useEffect(() => setCroppedSource(null),\r\n // [source])` to discard a stale local crop when the consumer swaps in\r\n // a new source. If we built a fresh object literal every render, that\r\n // effect would fire every render and wipe out a just-applied crop the\r\n // moment React re-rendered ImageEdit for any unrelated reason — exactly\r\n // the \"select area, hit Crop, selection clears but image doesn't crop\"\r\n // symptom. Returning null when the column has no bytes lets the read-\r\n // only / preview branches still gate on `hasImagePreview` while keeping\r\n // the hook call unconditional.\r\n const imageSource = useMemo<ImageSource | null>(() => {\r\n if (value?.value == null) return null;\r\n return {\r\n kind: 'base64',\r\n value: value.value,\r\n fileName: value.fileName,\r\n };\r\n }, [value?.value, value?.fileName]);\r\n\r\n // Delegation branch. When there's nothing image-specific to render —\r\n // because `displayImage` is off, or the column is empty (no fileId AND\r\n // no in-memory bytes) — defer entirely to `FileEdit`. Pass-through\r\n // covers every shared prop; `accept` + `uploadHintText` get image-\r\n // flavored defaults. (We do NOT delegate while the bytes are loading;\r\n // that case gets its own placeholder branch below so the user sees a\r\n // \"Loading…\" spinner instead of FileEdit's filename-only render.)\r\n if (!hasImagePreview && !isLoadingImagePreview) {\r\n const fileEditProps: FileEditProps = {\r\n columnName,\r\n accept,\r\n displayLabelWhenAvailable,\r\n descriptionLocation,\r\n displayValidationErrorMessage,\r\n isVisible,\r\n uploadHintText: uploadHintText ?? 'Drop an image here, or click to browse',\r\n ...(label !== undefined && { label }),\r\n ...(description !== undefined && { description }),\r\n ...(required !== undefined && { required }),\r\n ...(readOnly !== undefined && { readOnly }),\r\n ...(disabled !== undefined && { disabled }),\r\n // Fold `ppp-image-edit` into the className forwarded to FileEdit so\r\n // the marker survives the delegation. FileEdit lands `className` on\r\n // its inner drop-zone div (not its Field), so the marker won't sit\r\n // on the outermost element in the delegate branch — but it's still\r\n // present in the rendered DOM for consumer CSS to target.\r\n className: mergeClasses('ppp-image-edit', className),\r\n ...(style !== undefined && { style }),\r\n ...(id !== undefined && { id }),\r\n ...(maxFileSizeBytes !== undefined && { maxFileSizeBytes }),\r\n ...(validate !== undefined && { validate }),\r\n };\r\n return <FileEdit {...fileEditProps} />;\r\n }\r\n\r\n // Preview branch.\r\n if (!isVisible) return null;\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n // `resolvedReadOnly` / `isInteractable` are computed at the top of the\r\n // component so the upload hook can read them.\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n // Lazy-load in flight — show a placeholder frame with a Spinner and\r\n // \"Loading…\" label. Same Field chrome as the preview branch so the\r\n // layout doesn't jump when the bytes arrive and we swap to the real\r\n // image. Bare `<img>` rendering would alternatively let the browser\r\n // show a broken-image icon for the brief loading window; the explicit\r\n // placeholder reads much more clearly.\r\n if (isLoadingImagePreview) {\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-image-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n >\r\n <div\r\n className={mergeClasses(styles.loadingFrame, className)}\r\n {...(style !== undefined && { style })}\r\n >\r\n <Spinner size=\"small\" label=\"Loading…\" />\r\n </div>\r\n </Field>\r\n );\r\n }\r\n\r\n // `hasImagePreview` already gated on `value?.value != null`, so the\r\n // memoized source is non-null on these paths.\r\n const source = imageSource!;\r\n\r\n // Read-only with binary present: framed image with a download-button\r\n // overlay (ImageViewer's `downloadable` mode does this for us). Mirrors\r\n // Blazor's `if (this._readOnlyInternal && Value != null)` compact form —\r\n // no drop-zone chrome, no edit affordance.\r\n if (resolvedReadOnly) {\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-image-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n >\r\n <ImageViewer\r\n source={source}\r\n framed\r\n downloadable\r\n className={mergeClasses(styles.viewer, className)}\r\n {...(style !== undefined && { style })}\r\n />\r\n </Field>\r\n );\r\n }\r\n\r\n // Outer wrapper is the drop target in view mode (clicking opens the\r\n // file picker, dragging a file replaces the current image). In edit\r\n // mode we unbind those handlers so the viewer's own pan/crop pointer\r\n // gestures aren't fighting with the upload click.\r\n const viewerWrapperClass = mergeClasses(\r\n styles.viewerWrapper,\r\n isInteractable && !isEditing && styles.viewerWrapperInteractable,\r\n upload.isDragOver && !isEditing && styles.viewerWrapperActive,\r\n );\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-image-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n >\r\n <div className={mergeClasses(styles.wrapper, className)} style={style}>\r\n <input\r\n ref={upload.inputRef}\r\n id={inputId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={!isInteractable}\r\n className={styles.hiddenInput}\r\n onChange={upload.onInputChange}\r\n />\r\n <div\r\n className={viewerWrapperClass}\r\n {...(isInteractable && !isEditing ? upload.dropZoneHandlers : {})}\r\n >\r\n {/* `key` flips on every view↔edit transition so ImageViewer's\r\n internal angle/zoom/pan/flip/crop state always starts fresh.\r\n Cancelling out of edit mode then re-entering shouldn't\r\n resurrect the previous in-progress edits. In edit mode the\r\n viewer's toolbar surfaces its own Save/Cancel buttons via\r\n `onSave` / `onCancel` — we don't pass them in view mode so\r\n the toolbar isn't shown at all then. */}\r\n <ImageViewer\r\n key={isEditing ? 'edit' : 'view'}\r\n ref={viewerRef}\r\n source={source}\r\n framed\r\n toolbar={isEditing}\r\n croppable={isEditing}\r\n outputFormat={outputFormat}\r\n outputQuality={outputQuality}\r\n className={styles.viewer}\r\n {...(isEditing && {\r\n onSave: handleSaveEdit,\r\n onCancel: handleCancelEdit,\r\n })}\r\n />\r\n {isInteractable && !isEditing && (\r\n // View-mode overlay: Edit (enters edit mode), Download, Clear.\r\n // Translucent backdrop keeps icons readable against arbitrary\r\n // image content; `stopPropagation` on each button stops the\r\n // click from also firing the wrapper's open-file-picker\r\n // handler.\r\n <div className={styles.actions}>\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Edit20Regular />}\r\n aria-label={`Edit ${value!.fileName}`}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n handleEnterEdit();\r\n }}\r\n />\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ArrowDownload20Regular />}\r\n aria-label={`Download ${value!.fileName}`}\r\n disabled={isDownloading}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n void handleDownload();\r\n }}\r\n />\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Dismiss20Regular />}\r\n aria-label={`Remove ${value!.fileName}`}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n setValue(null);\r\n }}\r\n />\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </Field>\r\n );\r\n}\r\n\r\nconst useImageEditStyles = makeStyles({\r\n wrapper: {\r\n width: '100%',\r\n },\r\n // Placeholder frame shown while the lazy-load is in flight. Same\r\n // chrome (border, background, rounded corners) as ImageViewer's\r\n // framed mode so the layout doesn't jump when the bytes arrive and\r\n // we swap to the real viewer. Min-height matches a reasonable image\r\n // size so the spinner sits in the middle of a visible area rather\r\n // than against a 0-height box.\r\n loadingFrame: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '100%',\r\n minHeight: '180px',\r\n borderTopWidth: tokens.strokeWidthThin,\r\n borderRightWidth: tokens.strokeWidthThin,\r\n borderBottomWidth: tokens.strokeWidthThin,\r\n borderLeftWidth: tokens.strokeWidthThin,\r\n borderTopStyle: 'solid',\r\n borderRightStyle: 'solid',\r\n borderBottomStyle: 'solid',\r\n borderLeftStyle: 'solid',\r\n borderTopColor: tokens.colorNeutralStroke1,\r\n borderRightColor: tokens.colorNeutralStroke1,\r\n borderBottomColor: tokens.colorNeutralStroke1,\r\n borderLeftColor: tokens.colorNeutralStroke1,\r\n borderTopLeftRadius: tokens.borderRadiusMedium,\r\n borderTopRightRadius: tokens.borderRadiusMedium,\r\n borderBottomLeftRadius: tokens.borderRadiusMedium,\r\n borderBottomRightRadius: tokens.borderRadiusMedium,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n hiddenInput: {\r\n position: 'absolute',\r\n width: '1px',\r\n height: '1px',\r\n overflow: 'hidden',\r\n clip: 'rect(0, 0, 0, 0)',\r\n border: 'none',\r\n },\r\n // Positioning context for the absolute-positioned action overlay. Sits\r\n // outside ImageViewer's own framed wrapper so the overlay can anchor\r\n // to the field-width edge, not to ImageViewer's intrinsic content\r\n // width.\r\n viewerWrapper: {\r\n position: 'relative',\r\n display: 'block',\r\n },\r\n // View-mode hover affordance: cursor hints that clicking opens the\r\n // file picker. Edit mode skips this since the viewer's pan/crop\r\n // gestures own the cursor in that state.\r\n viewerWrapperInteractable: {\r\n cursor: 'pointer',\r\n },\r\n // Drag-over highlight. Inset box-shadow (instead of overriding\r\n // borderColor on a `border` shorthand) sidesteps Griffel's strict\r\n // mixing rules — same trick used in `FileEdit` and the original\r\n // `previewFrameActive`.\r\n viewerWrapperActive: {\r\n boxShadow: `inset 0 0 0 ${tokens.strokeWidthThick} ${tokens.colorBrandStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n },\r\n // ImageViewer defaults to `display: inline-flex` (sized to content). For\r\n // a form field we want it to fill the field width so the action overlay\r\n // anchors to the visible image's right edge.\r\n viewer: {\r\n display: 'flex',\r\n width: '100%',\r\n maxWidth: '100%',\r\n },\r\n // Overlay corner toolbar — top-right of the viewer, sitting on top of\r\n // the image. Translucent backdrop keeps the icons readable against any\r\n // image content (a near-white photo would wash out a transparent\r\n // button); rounded corners + a soft shadow separate it visually from\r\n // the viewer's frame.\r\n actions: {\r\n position: 'absolute',\r\n top: tokens.spacingVerticalS,\r\n right: tokens.spacingHorizontalS,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n paddingLeft: tokens.spacingHorizontalXS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n paddingTop: '2px',\r\n paddingBottom: '2px',\r\n backgroundColor: tokens.colorNeutralBackgroundAlpha,\r\n backdropFilter: 'blur(4px)',\r\n borderRadius: tokens.borderRadiusMedium,\r\n boxShadow: tokens.shadow4,\r\n // Sit above the image inside the viewer's stacking context.\r\n zIndex: 1,\r\n },\r\n});\r\n","import type { Localizer } from '@powerportalspro/react';\r\nimport type { ChoiceMetadataView, ChoiceOption } from './metadata-helpers.js';\r\n\r\n/** Sort order for the rendered choice options. Mirrors Blazor's `ChoiceValueSort`. */\r\nexport const ChoiceValueSort = {\r\n /** Server order — whatever Dataverse returned. */\r\n Default: 'default',\r\n /** Alphabetical by localized display label. */\r\n DisplayName: 'displayName',\r\n /** Alphabetical descending by localized display label. */\r\n DisplayNameDescending: 'displayNameDescending',\r\n /** Ascending by integer option value. */\r\n Value: 'value',\r\n /** Descending by integer option value. */\r\n ValueDescending: 'valueDescending',\r\n} as const;\r\nexport type ChoiceValueSort = (typeof ChoiceValueSort)[keyof typeof ChoiceValueSort];\r\n\r\n/**\r\n * Behavior when a choice value is excluded by `validChoiceValues` /\r\n * `invalidChoiceValues`. Mirrors Blazor's `ChoiceInvalidValueBehavior`.\r\n */\r\nexport const ChoiceInvalidValueBehavior = {\r\n /** Hide the option entirely. */\r\n Remove: 'remove',\r\n /** Keep the option visible but disabled (un-selectable). */\r\n Disable: 'disable',\r\n} as const;\r\nexport type ChoiceInvalidValueBehavior =\r\n (typeof ChoiceInvalidValueBehavior)[keyof typeof ChoiceInvalidValueBehavior];\r\n\r\n/**\r\n * Layout direction for the radio (single-select) or checkbox (multi-select)\r\n * variants. Mirrors Blazor's `ComponentOrientation`. Shared because the\r\n * underlying axis decision is identical between the two editors.\r\n */\r\nexport const ChoiceOrientation = {\r\n Vertical: 'vertical',\r\n Horizontal: 'horizontal',\r\n} as const;\r\nexport type ChoiceOrientation = (typeof ChoiceOrientation)[keyof typeof ChoiceOrientation];\r\n\r\n/**\r\n * Walks the localizer fallback chain Blazor's `BaseChoiceEditCommon` uses\r\n * for individual option labels. Returns the first key that resolves; falls\r\n * back to the metadata `displayName` when no override is registered. The\r\n * column-name-scoped key is the most specific (per-column), then the\r\n * choice-name-scoped key under the same table, then the global\r\n * choice-name-scoped key.\r\n */\r\nexport function resolveChoiceLabel(\r\n t: Localizer['t'],\r\n tableName: string | undefined,\r\n columnName: string,\r\n choiceLogicalName: string,\r\n value: number,\r\n fallback: string,\r\n): string {\r\n const keys: string[] = [];\r\n if (tableName) {\r\n keys.push(`tables.${tableName}.columns.${columnName}.values.${value}.label`);\r\n keys.push(`tables.${tableName}.choices.${choiceLogicalName}.values.${value}.label`);\r\n }\r\n keys.push(`choices.${choiceLogicalName}.values.${value}.label`);\r\n for (const k of keys) {\r\n const resolved = t(k);\r\n if (resolved !== k) return resolved;\r\n }\r\n return fallback;\r\n}\r\n\r\n/** Option after labeling, filtering, and sorting are applied — ready to render. */\r\nexport interface RenderableChoice {\r\n value: number;\r\n text: string;\r\n disabled: boolean;\r\n}\r\n\r\n/** Options consumed by {@link buildRenderableChoices}. */\r\nexport interface BuildRenderableChoicesOptions {\r\n validChoiceValues: readonly number[] | undefined;\r\n invalidChoiceValues: readonly number[] | undefined;\r\n invalidValueBehavior: ChoiceInvalidValueBehavior;\r\n sort: ChoiceValueSort;\r\n t: Localizer['t'];\r\n}\r\n\r\n/**\r\n * Applies the localizer label cascade, the valid/invalid filter, and the\r\n * sort order to a choice column's metadata. Shared between ChoiceEdit and\r\n * MultiSelectChoiceEdit — both render the same option list, only the\r\n * selection model differs.\r\n */\r\nexport function buildRenderableChoices(\r\n meta: ChoiceMetadataView,\r\n tableName: string | undefined,\r\n columnName: string,\r\n options: BuildRenderableChoicesOptions,\r\n): RenderableChoice[] {\r\n const isInvalid = (value: number): boolean => {\r\n if (options.validChoiceValues !== undefined) {\r\n return !options.validChoiceValues.includes(value);\r\n }\r\n if (options.invalidChoiceValues !== undefined) {\r\n return options.invalidChoiceValues.includes(value);\r\n }\r\n return false;\r\n };\r\n\r\n let result: RenderableChoice[] = meta.choices.map((c: ChoiceOption) => ({\r\n value: c.value,\r\n text: resolveChoiceLabel(\r\n options.t,\r\n tableName,\r\n columnName,\r\n meta.choiceLogicalName,\r\n c.value,\r\n c.displayName,\r\n ),\r\n disabled: isInvalid(c.value),\r\n }));\r\n\r\n if (options.invalidValueBehavior === ChoiceInvalidValueBehavior.Remove) {\r\n result = result.filter((c) => !c.disabled);\r\n }\r\n\r\n switch (options.sort) {\r\n case ChoiceValueSort.DisplayName:\r\n result.sort((a, b) => a.text.localeCompare(b.text));\r\n break;\r\n case ChoiceValueSort.DisplayNameDescending:\r\n result.sort((a, b) => b.text.localeCompare(a.text));\r\n break;\r\n case ChoiceValueSort.Value:\r\n result.sort((a, b) => a.value - b.value);\r\n break;\r\n case ChoiceValueSort.ValueDescending:\r\n result.sort((a, b) => b.value - a.value);\r\n break;\r\n case ChoiceValueSort.Default:\r\n default:\r\n break;\r\n }\r\n\r\n return result;\r\n}\r\n","import { Dropdown, Field, Option, Radio, RadioGroup, mergeClasses } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport { useMemo } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n getChoiceMetadata,\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport {\r\n buildRenderableChoices,\r\n ChoiceInvalidValueBehavior,\r\n ChoiceOrientation,\r\n ChoiceValueSort,\r\n} from './base/choice-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useChoiceColumn, useGuardedSetValue } from './base/use-column-edit.js';\r\n\r\n/**\r\n * Renders the choice column as a single-select dropdown ({@link Dropdown}) or\r\n * a {@link RadioGroup}. Mirrors Blazor's `ChoiceEditType`.\r\n */\r\nexport const ChoiceEditorType = {\r\n Dropdown: 'dropdown',\r\n RadioButtons: 'radioButtons',\r\n} as const;\r\nexport type ChoiceEditorType = (typeof ChoiceEditorType)[keyof typeof ChoiceEditorType];\r\n\r\nexport interface ChoiceEditProps extends BaseColumnEditProps {\r\n /** Render as a dropdown (default) or as a radio-button group. */\r\n editorType?: ChoiceEditorType;\r\n /**\r\n * Allow-list of valid option values. Options outside the list are excluded\r\n * (or disabled, depending on {@link invalidValueBehavior}). When omitted,\r\n * every option in the column metadata is valid.\r\n */\r\n validChoiceValues?: readonly number[];\r\n /**\r\n * Deny-list of invalid option values. Options in the list are excluded (or\r\n * disabled, depending on {@link invalidValueBehavior}). Ignored for any\r\n * value also present in {@link validChoiceValues}.\r\n */\r\n invalidChoiceValues?: readonly number[];\r\n /**\r\n * Sort order applied to the rendered options. Defaults to\r\n * {@link ChoiceValueSort.Default} (server order).\r\n */\r\n sort?: ChoiceValueSort;\r\n /**\r\n * What to do with options excluded by {@link validChoiceValues} /\r\n * {@link invalidChoiceValues}. Defaults to {@link ChoiceInvalidValueBehavior.Remove}.\r\n */\r\n invalidValueBehavior?: ChoiceInvalidValueBehavior;\r\n /**\r\n * Layout direction for the radio-button variant. Ignored when\r\n * {@link editorType} is {@link ChoiceEditorType.Dropdown}.\r\n * Defaults to {@link ChoiceOrientation.Vertical}.\r\n */\r\n radioButtonOrientation?: ChoiceOrientation;\r\n /**\r\n * Whether to show a \"(none)\" choice that clears the column back to `null`.\r\n * Mirrors LookupEdit's `displayNullChoice`. When omitted, the default is\r\n * **required-aware**: a required column defaults to `false` (the user must\r\n * pick a real value), a non-required column defaults to `true`. Set\r\n * explicitly to override either default.\r\n */\r\n displayNullChoice?: boolean;\r\n}\r\n\r\n/**\r\n * Single-select choice (option set / picklist) editor bound to a\r\n * `ChoiceValue` ($type 11). Renders as a Fluent {@link Dropdown} (default)\r\n * or a {@link RadioGroup} with one {@link Radio} per option, controlled by\r\n * {@link editorType}.\r\n *\r\n * The option list comes from the parent {@link RecordContext}'s table\r\n * metadata — the column's `ChoiceMetadata.choices` collection. Labels follow\r\n * a fallback chain: the per-column localized override, the per-choice\r\n * (table-scoped) override, then the global choice override, and finally the\r\n * metadata's `displayName`.\r\n *\r\n * `required` and `readOnly` are auto-resolved from metadata when consumer\r\n * props are omitted, identical to the other edit components.\r\n */\r\nexport function ChoiceEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n editorType = ChoiceEditorType.Dropdown,\r\n validChoiceValues,\r\n invalidChoiceValues,\r\n sort = ChoiceValueSort.Default,\r\n invalidValueBehavior = ChoiceInvalidValueBehavior.Remove,\r\n radioButtonOrientation = ChoiceOrientation.Vertical,\r\n displayNullChoice,\r\n validate,\r\n}: ChoiceEditProps) {\r\n const { value, setValue: rawSetValue } = useChoiceColumn(columnName);\r\n const { record, tableMetadata } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const t = useT();\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n // Required-aware default: a required column hides the \"(none)\" choice (the\r\n // user must pick a real value); a non-required column shows it. An explicit\r\n // prop wins over the default.\r\n const resolvedDisplayNullChoice = displayNullChoice ?? !resolvedRequired;\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const choiceMeta = getChoiceMetadata(metadata);\r\n const tableName = tableMetadata?.tableName;\r\n\r\n // Build the rendered option list once per metadata / filter / sort change.\r\n // `validChoiceValues` / `invalidChoiceValues` are arrays — JSON-stringify\r\n // them for the dep so consumer-provided literals don't refresh the memo on\r\n // every render.\r\n const validKey = validChoiceValues ? JSON.stringify(validChoiceValues) : '';\r\n const invalidKey = invalidChoiceValues ? JSON.stringify(invalidChoiceValues) : '';\r\n const choices = useMemo(\r\n () =>\r\n choiceMeta\r\n ? buildRenderableChoices(choiceMeta, tableName, columnName, {\r\n validChoiceValues,\r\n invalidChoiceValues,\r\n invalidValueBehavior,\r\n sort,\r\n t,\r\n })\r\n : [],\r\n // `t` is stable across renders within a single localizer cache state, so\r\n // including it in the dep correctly re-computes labels when localization\r\n // strings finish loading.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [choiceMeta, tableName, columnName, validKey, invalidKey, invalidValueBehavior, sort, t],\r\n );\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'number',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: value ?? null,\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n // Resolve the currently-selected option's display text — Dropdown's `value`\r\n // slot is the visible label, separate from `selectedOptions`. When the\r\n // record has a value not present in the rendered list (e.g. excluded by\r\n // `validChoiceValues`), show the raw integer so the user sees that the\r\n // column is set to something out-of-band.\r\n // The \"(none)\" choice is modelled as an empty-string option value, which the\r\n // change handlers already map to `setValue(null)`. Its label is the shared\r\n // `app.null-choice-label` string. No fallback: if that key isn't supplied,\r\n // `t()` returns the raw key so the missing localization is visible.\r\n const nullChoiceLabel = t('app.null-choice-label');\r\n const selectedKey = value === undefined ? '' : String(value);\r\n const selectedOption = choices.find((c) => c.value === value);\r\n const dropdownDisplayText =\r\n value === undefined\r\n ? // Unset: show the \"(none)\" label only when the null choice is offered,\r\n // otherwise leave the dropdown blank so a required column reads as empty.\r\n resolvedDisplayNullChoice\r\n ? nullChoiceLabel\r\n : ''\r\n : selectedOption\r\n ? selectedOption.text\r\n : String(value);\r\n\r\n // Read-only/disabled gate. Match Blazor's union: the inner control is\r\n // disabled when EITHER the metadata says read-only OR the consumer passed\r\n // `disabled`. Fluent Dropdown has no `readOnly` slot (only `disabled`), so\r\n // we collapse them.\r\n const isInactive = resolvedDisabled || resolvedReadOnly;\r\n\r\n let control: React.ReactElement;\r\n if (editorType === ChoiceEditorType.RadioButtons) {\r\n control = (\r\n <RadioGroup\r\n id={id}\r\n className={className}\r\n style={style}\r\n value={selectedKey}\r\n layout={\r\n radioButtonOrientation === ChoiceOrientation.Horizontal ? 'horizontal' : 'vertical'\r\n }\r\n disabled={isInactive}\r\n required={resolvedRequired}\r\n onChange={(_, data) => {\r\n if (!data.value) {\r\n setValue(null);\r\n return;\r\n }\r\n setValue(Number(data.value));\r\n }}\r\n >\r\n {resolvedDisplayNullChoice && (\r\n <Radio key=\"__null__\" value=\"\" label={nullChoiceLabel} />\r\n )}\r\n {choices.map((c) => (\r\n <Radio key={c.value} value={String(c.value)} label={c.text} disabled={c.disabled} />\r\n ))}\r\n </RadioGroup>\r\n );\r\n } else {\r\n control = (\r\n <Dropdown\r\n id={id}\r\n className={className}\r\n style={style}\r\n selectedOptions={\r\n value === undefined ? (resolvedDisplayNullChoice ? [''] : []) : [selectedKey]\r\n }\r\n value={dropdownDisplayText}\r\n disabled={isInactive}\r\n onOptionSelect={(_, data) => {\r\n if (!data.optionValue) {\r\n setValue(null);\r\n return;\r\n }\r\n setValue(Number(data.optionValue));\r\n }}\r\n >\r\n {resolvedDisplayNullChoice && (\r\n <Option key=\"__null__\" value=\"\">\r\n {nullChoiceLabel}\r\n </Option>\r\n )}\r\n {choices.map((c) => (\r\n <Option key={c.value} value={String(c.value)} disabled={c.disabled}>\r\n {c.text}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n );\r\n }\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-choice-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n {control}\r\n </Field>\r\n );\r\n}\r\n","import { Checkbox, Field, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { useColumnMetadata, useRecordContext, useT } from '@powerportalspro/react';\r\nimport { useId, useMemo } from 'react';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n buildRenderableChoices,\r\n ChoiceInvalidValueBehavior,\r\n ChoiceOrientation,\r\n ChoiceValueSort,\r\n} from './base/choice-helpers.js';\r\nimport {\r\n getChoiceMetadata,\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport { useGuardedSetValue, useMultiSelectChoiceColumn } from './base/use-column-edit.js';\r\n\r\nexport interface MultiSelectChoiceEditProps extends BaseColumnEditProps {\r\n /**\r\n * Allow-list of valid option values. Options outside the list are excluded\r\n * (or disabled, depending on {@link invalidValueBehavior}). When omitted,\r\n * every option in the column metadata is valid.\r\n */\r\n validChoiceValues?: readonly number[];\r\n /**\r\n * Deny-list of invalid option values. Options in the list are excluded (or\r\n * disabled, depending on {@link invalidValueBehavior}). Ignored for any\r\n * value also present in {@link validChoiceValues}.\r\n */\r\n invalidChoiceValues?: readonly number[];\r\n /**\r\n * Sort order applied to the rendered options. Defaults to\r\n * {@link ChoiceValueSort.Default} (server order).\r\n */\r\n sort?: ChoiceValueSort;\r\n /**\r\n * What to do with options excluded by {@link validChoiceValues} /\r\n * {@link invalidChoiceValues}. Defaults to {@link ChoiceInvalidValueBehavior.Remove}.\r\n */\r\n invalidValueBehavior?: ChoiceInvalidValueBehavior;\r\n /**\r\n * Layout direction for the rendered checkboxes. Defaults to\r\n * {@link ChoiceOrientation.Vertical}.\r\n */\r\n checkboxOrientation?: ChoiceOrientation;\r\n /**\r\n * When `true`, checkboxes wrap into additional rows/columns instead of\r\n * scrolling once the container's bounds are reached:\r\n * - `checkboxOrientation: 'horizontal'` — wraps onto multiple rows.\r\n * - `checkboxOrientation: 'vertical'` — flows into additional columns when a\r\n * height cap is applied (e.g. `style={{ maxHeight }}`); otherwise it stays\r\n * a single growing column.\r\n *\r\n * When `false`, the layout scrolls instead (horizontal scrollbar for the row,\r\n * vertical scrollbar for the column). Defaults to `true`.\r\n */\r\n wrapCheckboxes?: boolean;\r\n /** Optional minimum number of selected values. */\r\n minCount?: number;\r\n /** Optional maximum number of selected values. */\r\n maxCount?: number;\r\n}\r\n\r\nconst useMultiSelectStyles = makeStyles({\r\n // Shared base for every orientation. `overflow: auto` (both axes, matching\r\n // Blazor's `.ppp-checkbox-container`) lets the box scroll naturally on\r\n // whichever axis genuinely overflows — no axis is force-`hidden`. The padding\r\n // is load-bearing, not cosmetic: Fluent v9's Checkbox draws its focus ring as\r\n // an absolutely-positioned `::after` inset by -2px (native outline disabled),\r\n // so a focused checkbox bleeds ~2px past its own box. The padding keeps that\r\n // ring inside the padding box so it never adds scrollable overflow (which\r\n // would otherwise flick a scrollbar / nudge content when you focus an option\r\n // at the edge).\r\n //\r\n // NOTE: a column flex-wrap needs a DEFINITE height to wrap against. Give the\r\n // editor a fixed `height` (not `max-height`) when you want vertical wrapping —\r\n // `max-height` lets the container collapse to its content, so it has no stable\r\n // height to wrap into columns against and the layout shifts as items change.\r\n container: {\r\n overflow: 'auto',\r\n padding: tokens.spacingHorizontalXS,\r\n },\r\n // Flex layout — column for vertical, row for horizontal. Gap matches Fluent's\r\n // standard small spacing so checkbox rows feel related without crowding.\r\n containerVertical: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: tokens.spacingVerticalXXS,\r\n },\r\n // Vertical + wrap: stack checkboxes in a column, then flow into additional\r\n // columns once the (definite) height is reached.\r\n containerVerticalWrap: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n flexWrap: 'wrap',\r\n rowGap: tokens.spacingVerticalXXS,\r\n columnGap: tokens.spacingHorizontalM,\r\n },\r\n containerHorizontal: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n flexWrap: 'nowrap',\r\n gap: tokens.spacingHorizontalM,\r\n // Keep each checkbox at its natural height. A row's default\r\n // `align-items: stretch` would stretch every checkbox to the container's\r\n // (fixed) height; the checkbox's indicator is `align-self: flex-start`\r\n // (top) while its label is `align-self: center`, so a stretched checkbox\r\n // splits them onto separate lines. `flex-start` keeps the row's items\r\n // their intrinsic height so indicator + label stay inline.\r\n alignItems: 'flex-start',\r\n },\r\n containerHorizontalWrap: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n flexWrap: 'wrap',\r\n rowGap: tokens.spacingVerticalXXS,\r\n columnGap: tokens.spacingHorizontalM,\r\n // Same as the horizontal row above: don't vertically stretch the\r\n // checkboxes, or the indicator (top) and label (center) separate.\r\n alignItems: 'flex-start',\r\n },\r\n // Keep each checkbox at its natural size. In the horizontal `nowrap` row,\r\n // Fluent's default `flex-shrink: 1` lets items compress below their content\r\n // width, which pushes each checkbox's absolutely-positioned hidden <input>\r\n // (it overlays the indicator at full row height) over the neighbouring\r\n // option — so a click lands on the wrong checkbox. Pinning shrink to 0 keeps\r\n // the hit targets aligned; the container's `overflow-x: auto` handles the row\r\n // running wider than its box.\r\n checkbox: {\r\n flexShrink: 0,\r\n },\r\n});\r\n\r\n/**\r\n * Multi-select choice (multi-select option set) editor bound to a\r\n * `MultiSelectChoiceValue` ($type 40). Renders one Fluent {@link Checkbox}\r\n * per option in a flex container — orientation and wrap behavior are\r\n * controlled by {@link checkboxOrientation} and {@link wrapCheckboxes}.\r\n *\r\n * The option list, label fallback chain, sort, and valid/invalid filtering\r\n * are identical to {@link ChoiceEdit}. The difference is purely the\r\n * selection model: this component reads/writes a `number[]` through\r\n * {@link useMultiSelectChoiceColumn} and toggles individual values on\r\n * checkbox change.\r\n *\r\n * Required validation: a multi-select column is \"empty\" when no boxes are\r\n * checked, so the built-in required check kicks in via `kind: 'array'` (any\r\n * empty array fails the required gate). {@link minCount} and\r\n * {@link maxCount} layer on top for \"at least N\" / \"at most N\" rules.\r\n */\r\nexport function MultiSelectChoiceEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n validChoiceValues,\r\n invalidChoiceValues,\r\n sort = ChoiceValueSort.Default,\r\n invalidValueBehavior = ChoiceInvalidValueBehavior.Remove,\r\n checkboxOrientation = ChoiceOrientation.Vertical,\r\n wrapCheckboxes = true,\r\n minCount,\r\n maxCount,\r\n validate,\r\n}: MultiSelectChoiceEditProps) {\r\n const { value, setValue: rawSetValue } = useMultiSelectChoiceColumn(columnName);\r\n const { record, tableMetadata } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const styles = useMultiSelectStyles();\r\n const t = useT();\r\n // Stable id base for the per-option checkboxes. Fluent's <Checkbox> consumes\r\n // the surrounding <Field> context, so WITHOUT an explicit id every checkbox\r\n // inherits the same field-generated control id — collapsing all the per-option\r\n // `<label htmlFor>` associations onto the first input (clicking any option's\r\n // label toggles only the first). Giving each checkbox a unique id avoids that.\r\n const generatedId = useId();\r\n const baseId = id ?? generatedId;\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setValue = useGuardedSetValue(rawSetValue, resolvedReadOnly, resolvedDisabled);\r\n\r\n const choiceMeta = getChoiceMetadata(metadata);\r\n const tableName = tableMetadata?.tableName;\r\n\r\n // Memoize the rendered option list against the dimensions that actually\r\n // affect it. Stringify the filter arrays so consumer-provided literals\r\n // don't refresh the memo on every render.\r\n const validKey = validChoiceValues ? JSON.stringify(validChoiceValues) : '';\r\n const invalidKey = invalidChoiceValues ? JSON.stringify(invalidChoiceValues) : '';\r\n const choices = useMemo(\r\n () =>\r\n choiceMeta\r\n ? buildRenderableChoices(choiceMeta, tableName, columnName, {\r\n validChoiceValues,\r\n invalidChoiceValues,\r\n invalidValueBehavior,\r\n sort,\r\n t,\r\n })\r\n : [],\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [choiceMeta, tableName, columnName, validKey, invalidKey, invalidValueBehavior, sort, t],\r\n );\r\n\r\n // Set membership lookup — O(1) instead of O(n) per render against `value`.\r\n const selectedSet = useMemo(() => new Set(value), [value]);\r\n\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'array',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value,\r\n ...(minCount !== undefined && { minCount }),\r\n ...(maxCount !== undefined && { maxCount }),\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n // Fluent Checkbox has no `readOnly` slot — collapse readOnly + disabled\r\n // into a single inactive flag, same convention as the other choice\r\n // controls.\r\n const isInactive = resolvedDisabled || resolvedReadOnly;\r\n\r\n const containerClass =\r\n checkboxOrientation === ChoiceOrientation.Horizontal\r\n ? wrapCheckboxes\r\n ? styles.containerHorizontalWrap\r\n : styles.containerHorizontal\r\n : wrapCheckboxes\r\n ? styles.containerVerticalWrap\r\n : styles.containerVertical;\r\n\r\n const toggle = (optionValue: number, checked: boolean): void => {\r\n if (checked && !selectedSet.has(optionValue)) {\r\n setValue([...value, optionValue]);\r\n } else if (!checked && selectedSet.has(optionValue)) {\r\n setValue(value.filter((v) => v !== optionValue));\r\n }\r\n };\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-multi-select-choice-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n <div id={id} className={mergeClasses(styles.container, containerClass, className)} style={style}>\r\n {choices.map((c) => (\r\n <Checkbox\r\n key={c.value}\r\n id={`${baseId}_${c.value}`}\r\n className={styles.checkbox}\r\n label={c.text}\r\n // Override the `required` the surrounding <Field> injects through\r\n // FieldContext (Checkbox is a field-aware control). Without this,\r\n // a required column draws a red asterisk on EVERY option's label;\r\n // the asterisk belongs on the Field's column label only — group\r\n // \"at least one\" validation is handled by useColumnValidation.\r\n required={false}\r\n checked={selectedSet.has(c.value)}\r\n disabled={isInactive || c.disabled}\r\n onChange={(_, data) => toggle(c.value, data.checked === true)}\r\n />\r\n ))}\r\n </div>\r\n </Field>\r\n );\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { useMemo } from 'react';\r\n\r\nconst useHighlighterStyles = makeStyles({\r\n // Inline marker around matched substrings. `colorPaletteYellowBackground3`\r\n // is the brightest of Fluent's three yellow-background tokens — the lower\r\n // tints (1, 2) read as \"barely tinted\" against typical light-theme\r\n // surfaces; tint 3 stays solidly yellow so matches pop without forcing\r\n // hardcoded hex. Bold weight reinforces the hit visually while keeping\r\n // the glyph anatomy unchanged (no shifts).\r\n //\r\n // Foreground uses `colorNeutralForeground1Static` rather than the\r\n // paired `colorPaletteYellowForeground2` because the latter's polarity\r\n // INVERTS between themes — in dark mode it resolves to a near-white\r\n // yellow tint that washes out against the bright yellow background.\r\n // The `Static` variant resolves to a dark grey in both light AND dark\r\n // themes, which gives consistent contrast against the always-bright\r\n // yellow background.\r\n mark: {\r\n backgroundColor: tokens.colorPaletteYellowBackground3,\r\n color: tokens.colorNeutralForeground1Static,\r\n fontWeight: tokens.fontWeightBold,\r\n padding: 0,\r\n borderRadius: tokens.borderRadiusSmall,\r\n },\r\n});\r\n\r\n/**\r\n * Folds a string for accent- and case-insensitive matching, while\r\n * tracking which original character(s) each folded character came from.\r\n *\r\n * The fold is NFD-decompose + drop combining marks + lowercase — the JS\r\n * equivalent of .NET's `CompareInfo` with `CompareOptions.IgnoreCase |\r\n * CompareOptions.IgnoreNonSpace`. After folding `'café'` and `'cafe'`\r\n * are equal; `'Strasse'` and `'STRASSE'` differ only in case.\r\n *\r\n * Deliberately does NOT apply linguistic equivalences like German\r\n * sharp-s → ss (NFKD would, but `IgnoreNonSpace` doesn't), so `'straße'`\r\n * and `'strasse'` are distinct. Matches Blazor's collation behavior.\r\n *\r\n * The `starts` / `ends` arrays preserve a positional mapping from each\r\n * folded index back into the original string. They're not always 1:1\r\n * because:\r\n * - One original char can fold to zero chars (pure combining marks\r\n * like `'́'`); we attach those to the preceding folded char's\r\n * range so highlights extend through trailing diacritics.\r\n * - In rare cases NFD decomposes a single codepoint into more than one\r\n * base character (compatibility characters in specialized blocks);\r\n * those distribute correctly through the per-fold-char loop.\r\n */\r\ninterface FoldedString {\r\n folded: string;\r\n starts: number[];\r\n ends: number[];\r\n}\r\n\r\nfunction foldString(s: string, caseSensitive: boolean): FoldedString {\r\n let folded = '';\r\n const starts: number[] = [];\r\n const ends: number[] = [];\r\n // Combining diacritical marks block: U+0300..U+036F. Stripping these\r\n // after NFD decompose leaves the bare base letter for matching.\r\n const combiningMarks = /[̀-ͯ]/g;\r\n for (let i = 0; i < s.length; i++) {\r\n const decomposed = s.charAt(i).normalize('NFD').replace(combiningMarks, '');\r\n const f = caseSensitive ? decomposed : decomposed.toLowerCase();\r\n if (f.length === 0) {\r\n if (ends.length > 0) ends[ends.length - 1] = i + 1;\r\n continue;\r\n }\r\n for (let j = 0; j < f.length; j++) {\r\n folded += f[j];\r\n starts.push(i);\r\n ends.push(i + 1);\r\n }\r\n }\r\n return { folded, starts, ends };\r\n}\r\n\r\n/**\r\n * Splits a search term on any character in `delimiters`. Empty delimiter\r\n * string returns the term as a single token (legacy behavior). Empty\r\n * tokens (consecutive delimiters) are dropped.\r\n */\r\nfunction tokenizeSearch(search: string, delimiters: string): string[] {\r\n if (!delimiters) return [search];\r\n const delim = new Set<string>(Array.from(delimiters));\r\n const tokens: string[] = [];\r\n let current = '';\r\n for (const ch of search) {\r\n if (delim.has(ch)) {\r\n if (current.length > 0) {\r\n tokens.push(current);\r\n current = '';\r\n }\r\n } else {\r\n current += ch;\r\n }\r\n }\r\n if (current.length > 0) tokens.push(current);\r\n return tokens;\r\n}\r\n\r\n/**\r\n * Options for {@link splitHighlightSegments}. Both fields mirror\r\n * Blazor's `AccentInsensitiveHighlighter` parameters.\r\n */\r\nexport interface SplitHighlightOptions {\r\n /**\r\n * Characters that split the search term into independent tokens. Each\r\n * token is matched and highlighted independently, with overlapping /\r\n * adjacent matches merged in the rendered output. Pass `' '` to\r\n * tokenize on whitespace, `',. '` for comma/period/space, etc. When\r\n * omitted (or empty string), the search term is matched verbatim.\r\n */\r\n delimiters?: string;\r\n /**\r\n * When `true`, matches require exact case. Accent-insensitivity still\r\n * applies — only the case-folding step is skipped. Defaults to `false`.\r\n */\r\n caseSensitive?: boolean;\r\n}\r\n\r\n/**\r\n * Splits `text` into alternating non-match / match segments based on\r\n * case-insensitive AND accent-insensitive occurrences of `search`.\r\n * Pure function — no React, no side effects — so it can be unit-tested\r\n * independently and reused for non-React rendering paths (e.g. ARIA\r\n * labels, text-only fallbacks).\r\n *\r\n * Mirrors Blazor's `AccentInsensitiveHighlighter`, which uses\r\n * `CompareInfo.IndexOf` with `IgnoreCase | IgnoreNonSpace`. So `'cafe'`\r\n * highlights inside `'café'`, `'CAFÉ'`, `'café'`, etc.\r\n */\r\nexport function splitHighlightSegments(\r\n text: string,\r\n search: string,\r\n options?: SplitHighlightOptions,\r\n): { text: string; isMatch: boolean }[] {\r\n if (!text || !search) return [];\r\n const caseSensitive = options?.caseSensitive ?? false;\r\n const delimiters = options?.delimiters ?? '';\r\n const tokens = tokenizeSearch(search, delimiters);\r\n if (tokens.length === 0) return [{ text, isMatch: false }];\r\n\r\n const folded = foldString(text, caseSensitive);\r\n\r\n // Collect every match range from every token in original-text\r\n // coordinates. With `delimiters` unset there's exactly one token so\r\n // this collapses to the single-pass behavior; with delimiters the\r\n // ranges may overlap or sit adjacent and need a merge pass below.\r\n const ranges: { start: number; end: number }[] = [];\r\n for (const token of tokens) {\r\n const tokenFolded = foldString(token, caseSensitive).folded;\r\n if (tokenFolded.length === 0) continue;\r\n let cursor = 0;\r\n while (cursor <= folded.folded.length - tokenFolded.length) {\r\n const foundAt = folded.folded.indexOf(tokenFolded, cursor);\r\n if (foundAt === -1) break;\r\n // `foundAt` is in range and `tokenFolded.length > 0`, so both\r\n // lookups are guaranteed to land within `folded.starts` / `ends`.\r\n ranges.push({\r\n start: folded.starts[foundAt]!,\r\n end: folded.ends[foundAt + tokenFolded.length - 1]!,\r\n });\r\n cursor = foundAt + tokenFolded.length;\r\n }\r\n }\r\n\r\n if (ranges.length === 0) return [{ text, isMatch: false }];\r\n\r\n // Sort by start then merge overlapping / adjacent ranges. Adjacent\r\n // ranges (end === next.start) merge so the rendered output doesn't\r\n // emit two `<mark>`s for what looks like a contiguous highlight.\r\n ranges.sort((a, b) => a.start - b.start);\r\n const merged: { start: number; end: number }[] = [];\r\n for (const r of ranges) {\r\n const last = merged[merged.length - 1];\r\n if (last && r.start <= last.end) {\r\n if (r.end > last.end) last.end = r.end;\r\n } else {\r\n merged.push({ start: r.start, end: r.end });\r\n }\r\n }\r\n\r\n const segments: { text: string; isMatch: boolean }[] = [];\r\n let cursor = 0;\r\n for (const r of merged) {\r\n if (r.start > cursor) {\r\n segments.push({ text: text.slice(cursor, r.start), isMatch: false });\r\n }\r\n segments.push({ text: text.slice(r.start, r.end), isMatch: true });\r\n cursor = r.end;\r\n }\r\n if (cursor < text.length) {\r\n segments.push({ text: text.slice(cursor), isMatch: false });\r\n }\r\n return segments;\r\n}\r\n\r\nexport interface HighlighterProps {\r\n /** The text to render. */\r\n text: string | null | undefined;\r\n /**\r\n * Substring to highlight. Matched case- AND accent-insensitively, so\r\n * `'cafe'` highlights inside `'café'`. Any leading or embedded `*`\r\n * characters are stripped before matching — the framework uses `*` as\r\n * a wildcard marker for grid/lookup search (`'*foo'` means\r\n * \"contains foo\"), but the rendered highlight should be the literal\r\n * substring without the wildcard.\r\n */\r\n searchText?: string | null;\r\n /**\r\n * Characters that split the search term into independent tokens —\r\n * pass `' '` to highlight every space-separated word, `',. '` for\r\n * comma/period/space-tokenized, etc. When omitted (or empty), the\r\n * search term is matched verbatim.\r\n */\r\n delimiters?: string;\r\n /**\r\n * When `true`, matches require exact case. Accent-insensitivity still\r\n * applies. Defaults to `false`.\r\n */\r\n caseSensitive?: boolean;\r\n /** Class applied to the wrapping `<span>`. */\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Renders `text` with matching substrings wrapped in a `<mark>` so the user\r\n * can see why each result was returned by a search. Mirrors Blazor's\r\n * `AccentInsensitiveHighlighter`: matches are case- AND accent-insensitive\r\n * (NFD-decompose + strip combining marks + lowercase, the JS analog of\r\n * `CompareInfo` with `IgnoreCase | IgnoreNonSpace`).\r\n *\r\n * Designed for reuse outside `LookupEdit` — the same component renders\r\n * grid-cell highlights in `MainGrid` and any other \"show what matched\"\r\n * surface, hence the headless `splitHighlightSegments` helper exported\r\n * alongside.\r\n */\r\nexport function Highlighter({\r\n text,\r\n searchText,\r\n delimiters,\r\n caseSensitive,\r\n className,\r\n}: HighlighterProps): React.ReactElement {\r\n const styles = useHighlighterStyles();\r\n // `*` is stripped from the search before matching — see prop docs.\r\n // Trimmed too so a stray trailing space doesn't kill the match,\r\n // EXCEPT when ` ` is in `delimiters` — in that case leading/trailing\r\n // whitespace is a separator and tokenization handles it.\r\n const cleanSearch = (() => {\r\n const stripped = (searchText ?? '').replace(/\\*/g, '');\r\n return delimiters?.includes(' ') ? stripped : stripped.trim();\r\n })();\r\n const segments = useMemo(\r\n () =>\r\n splitHighlightSegments(text ?? '', cleanSearch, {\r\n ...(delimiters !== undefined && { delimiters }),\r\n ...(caseSensitive !== undefined && { caseSensitive }),\r\n }),\r\n [text, cleanSearch, delimiters, caseSensitive],\r\n );\r\n\r\n if (segments.length === 0) {\r\n return <span className={mergeClasses('ppp-highlighter', className)}>{text ?? ''}</span>;\r\n }\r\n\r\n return (\r\n <span className={mergeClasses('ppp-highlighter', className)}>\r\n {segments.map((seg, idx) =>\r\n seg.isMatch ? (\r\n <mark key={idx} className={styles.mark}>\r\n {seg.text}\r\n </mark>\r\n ) : (\r\n <span key={idx}>{seg.text}</span>\r\n ),\r\n )}\r\n </span>\r\n );\r\n}\r\n\r\n/**\r\n * Wraps the entire string in the same `<mark>` style {@link Highlighter}\r\n * uses for substring matches. Used by grid cells whose match model is\r\n * \"whole-cell-on-predicate\" rather than \"substring-in-text\" — chiefly\r\n * the numeric / money / date cells, whose search syntax is an operator\r\n * + value (`<50000`) and where highlighting a literal substring of the\r\n * formatted display would never line up with the actual match logic.\r\n */\r\nexport function HighlightFull({\r\n text,\r\n className,\r\n}: {\r\n text: string;\r\n className?: string;\r\n}): React.ReactElement {\r\n const styles = useHighlighterStyles();\r\n return (\r\n <mark className={`${styles.mark}${className ? ` ${className}` : ''}`}>{text}</mark>\r\n );\r\n}\r\n","import { Checkmark16Regular, Dismiss16Regular } from '@fluentui/react-icons';\r\nimport type { components } from '@powerportalspro/core';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\ntype BoolValue = components['schemas']['ColumnValueBaseBoolValue'];\r\n\r\n/**\r\n * Renders Bool ($type 0) columns as a single accessible glyph — checkmark\r\n * for true, dismiss-X for false, nothing for null. Compact in dense grids,\r\n * and the `aria-label` keeps the meaning available to screen readers since\r\n * the icons alone don't translate.\r\n *\r\n * If the column's metadata defines custom true/false labels (Dataverse\r\n * YesNo columns can), Dataverse populates `record.formattedValues` with\r\n * the localized text — we prefer that over the glyph when present.\r\n */\r\nexport function BoolCell({ value, record, columnName }: GridColumnCellRendererProps) {\r\n const v = value as BoolValue | null | undefined;\r\n if (!v) return null;\r\n const formatted = record.formattedValues?.[columnName];\r\n if (formatted) return <span className=\"ppp-grid-cell-bool\">{formatted}</span>;\r\n if (v.value === true) {\r\n return <Checkmark16Regular aria-label=\"True\" className=\"ppp-grid-cell-bool\" />;\r\n }\r\n if (v.value === false) {\r\n return <Dismiss16Regular aria-label=\"False\" className=\"ppp-grid-cell-bool\" />;\r\n }\r\n return null;\r\n}\r\n","import { Highlighter } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Renders Choice ($type 11) columns — single-select option-set values.\r\n * The server resolves the integer to its localized label and ships it\r\n * via `formattedValues[columnName]`; choice search matches against the\r\n * same label, so `searchText` is piped to the highlighter.\r\n */\r\nexport function ChoiceCell({ value: _value, record, columnName, metadata, searchText }: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <Highlighter text={display} searchText={searchText ?? null} className=\"ppp-grid-cell-choice\" />;\r\n}\r\n","import { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Renders DateTime ($type 2) columns. Server's\r\n * `formattedValues[columnName]` already reflects the user's locale + the\r\n * column's `dateTimeBehavior` (date-only vs date+time, timezone\r\n * treatment), so we lean on it via the shared\r\n * {@link resolveColumnDisplayValue} fallback. No highlighter — date\r\n * search isn't substring-based.\r\n */\r\nexport function DateCell({ value: _value, record, columnName, metadata }: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <span className=\"ppp-grid-cell-date\">{display}</span>;\r\n}\r\n","import { Highlighter } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Fallback used for column types the dispatcher doesn't have a dedicated\r\n * renderer for (yet) — File / Image / Guid / Owner / Status / State are\r\n * the current set. Delegates display resolution to the shared\r\n * {@link resolveColumnDisplayValue}, and runs the result through the\r\n * highlighter so any string content shows search matches even for\r\n * \"fallback\" column types.\r\n */\r\nexport function FallbackCell({\r\n value: _value,\r\n record,\r\n columnName,\r\n metadata,\r\n searchText,\r\n}: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <Highlighter text={display} searchText={searchText ?? null} className=\"ppp-grid-cell-fallback\" />;\r\n}\r\n","import { makeStyles } from '@fluentui/react-components';\r\nimport {\r\n Document20Regular,\r\n DocumentPdf20Regular,\r\n DocumentTable20Regular,\r\n DocumentText20Regular,\r\n FolderZip20Regular,\r\n Image20Regular,\r\n MusicNote220Regular,\r\n SlideText20Regular,\r\n Video20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { useState, type ReactElement } from 'react';\r\nimport { getFileExtension } from '../../edits/base/file-helpers.js';\r\n\r\n/**\r\n * Base path for Microsoft's hosted branded file-type icons. Same CDN\r\n * SharePoint and OneDrive serve from; the icon at\r\n * <c>{base}/{size}/{name}.svg</c> is a small SVG of the official Office /\r\n * Adobe / etc. glyph. Pinned to a known-good Fabric-CDN revision so the\r\n * path is stable; new icons land in newer revisions, but this revision\r\n * already covers every name the {@link CDN_ICON_NAMES} map references.\r\n */\r\nconst CDN_BASE = 'https://res.cdn.office.net/files/fabric-cdn-prod_20240129.001/assets/item-types';\r\nconst CDN_SIZE = 20;\r\n\r\n/**\r\n * Maps a normalized file extension (lowercase, no dot) to the icon name\r\n * Microsoft's Fabric CDN serves at {@link CDN_BASE}. Entries only exist\r\n * for extensions that have an actual branded glyph on the CDN — anything\r\n * not listed here falls back to the Fluent outline icon picked by\r\n * {@link pickFallbackIcon}, so we never render a broken image.\r\n *\r\n * The names map one-to-one with the catalog Microsoft documents for\r\n * <c>@fluentui/react-file-type-icons</c>: docx/xlsx/pptx cover their\r\n * legacy `.doc`/`.xls`/`.ppt` variants, \"photo\" / \"video\" / \"audio\" /\r\n * \"zip\" are the bucket icons for the common media extensions, and\r\n * \"code\" handles js/ts/cs/etc. (CDN has no per-language icon).\r\n */\r\nconst CDN_ICON_NAMES: Record<string, string> = {\r\n // Adobe / PDF.\r\n pdf: 'pdf',\r\n\r\n // Microsoft 365 — Word.\r\n doc: 'docx',\r\n docx: 'docx',\r\n docm: 'docx',\r\n dot: 'dotx',\r\n dotx: 'dotx',\r\n rtf: 'rtf',\r\n\r\n // Microsoft 365 — Excel.\r\n xls: 'xlsx',\r\n xlsx: 'xlsx',\r\n xlsm: 'xlsx',\r\n xlsb: 'xlsx',\r\n csv: 'csv',\r\n\r\n // Microsoft 365 — PowerPoint.\r\n ppt: 'pptx',\r\n pptx: 'pptx',\r\n pptm: 'pptx',\r\n ppsx: 'ppsx',\r\n potx: 'potx',\r\n\r\n // Microsoft 365 — OneNote / Visio / Project / Publisher / Access.\r\n one: 'one',\r\n onetoc: 'one',\r\n onetoc2: 'one',\r\n vsdx: 'vsdx',\r\n vssx: 'vssx',\r\n vstx: 'vstx',\r\n mpp: 'mpp',\r\n mpt: 'mpt',\r\n pub: 'pub',\r\n accdb: 'accdb',\r\n mdb: 'mdb',\r\n\r\n // Outlook / email.\r\n msg: 'email',\r\n eml: 'email',\r\n\r\n // Archives.\r\n zip: 'zip',\r\n '7z': 'zip',\r\n rar: 'zip',\r\n tar: 'zip',\r\n gz: 'zip',\r\n\r\n // Images.\r\n png: 'photo',\r\n jpg: 'photo',\r\n jpeg: 'photo',\r\n gif: 'photo',\r\n bmp: 'photo',\r\n webp: 'photo',\r\n tif: 'photo',\r\n tiff: 'photo',\r\n ico: 'photo',\r\n svg: 'photo',\r\n heic: 'photo',\r\n heif: 'photo',\r\n\r\n // Video.\r\n mp4: 'video',\r\n mov: 'video',\r\n avi: 'video',\r\n mkv: 'video',\r\n webm: 'video',\r\n m4v: 'video',\r\n wmv: 'video',\r\n\r\n // Audio.\r\n mp3: 'audio',\r\n wav: 'audio',\r\n ogg: 'audio',\r\n flac: 'audio',\r\n m4a: 'audio',\r\n aac: 'audio',\r\n wma: 'audio',\r\n\r\n // Text / web / data.\r\n txt: 'txt',\r\n log: 'txt',\r\n md: 'txt',\r\n markdown: 'txt',\r\n html: 'html',\r\n htm: 'html',\r\n xml: 'xml',\r\n yaml: 'yaml',\r\n yml: 'yaml',\r\n css: 'css',\r\n\r\n // Source code — CDN has a single 'code' glyph rather than per-language.\r\n js: 'code',\r\n jsx: 'code',\r\n ts: 'code',\r\n tsx: 'code',\r\n mjs: 'code',\r\n cjs: 'code',\r\n cs: 'code',\r\n vb: 'code',\r\n fs: 'code',\r\n py: 'code',\r\n rb: 'code',\r\n go: 'code',\r\n rs: 'code',\r\n java: 'code',\r\n kt: 'code',\r\n swift: 'code',\r\n php: 'code',\r\n sh: 'code',\r\n bash: 'code',\r\n ps1: 'code',\r\n json: 'code',\r\n sql: 'code',\r\n\r\n // Executables / installers.\r\n exe: 'exe',\r\n msi: 'exe',\r\n};\r\n\r\nconst useStyles = makeStyles({\r\n // Sized via inline width/height attrs on the <img>; height-100% in the\r\n // class keeps the icon vertically centered inside the cell's flex row.\r\n cdnIcon: {\r\n flexShrink: 0,\r\n display: 'block',\r\n objectFit: 'contain',\r\n },\r\n});\r\n\r\n/**\r\n * Picks an icon component matching the extension of `fileName`. Prefers\r\n * the Microsoft-hosted branded SVG for known Office / PDF / media / etc.\r\n * extensions and falls back to a Fluent outline icon for anything else\r\n * (or for failed CDN loads — the `onError` handler swaps in the outline\r\n * icon so a network blip never leaves the cell with a broken image).\r\n *\r\n * Mirrors Blazor's `FileTypeIcons.GetIcon` shape but the actual artwork\r\n * comes from Microsoft's Fabric CDN — the same icons SharePoint and\r\n * OneDrive render, so they're recognizable to consumers of those apps.\r\n */\r\nexport function renderFileTypeIcon(\r\n fileName: string | undefined | null,\r\n className?: string,\r\n): ReactElement {\r\n const ext = getFileExtension(fileName);\r\n const brandedName = CDN_ICON_NAMES[ext];\r\n if (brandedName) {\r\n return (\r\n <CdnFileTypeIcon\r\n name={brandedName}\r\n alt={ext}\r\n {...(className !== undefined && { className })}\r\n />\r\n );\r\n }\r\n const Icon = pickFallbackIcon(ext);\r\n return <Icon {...(className !== undefined && { className })} aria-hidden />;\r\n}\r\n\r\n/**\r\n * Renders a Microsoft-CDN-hosted branded icon as an `<img>`. On a load\r\n * failure (network unreachable, the CDN moved the asset under a new\r\n * revision, etc.) the fallback Fluent outline icon takes its place so\r\n * the cell never displays a broken image. The fallback path is also\r\n * cheap — it's just toggling a state flag and re-rendering with the\r\n * same className.\r\n */\r\nfunction CdnFileTypeIcon({\r\n name,\r\n alt,\r\n className,\r\n}: {\r\n name: string;\r\n alt: string;\r\n className?: string;\r\n}) {\r\n const styles = useStyles();\r\n const [failed, setFailed] = useState(false);\r\n\r\n if (failed) {\r\n const Icon = pickFallbackIcon(alt);\r\n return <Icon {...(className !== undefined && { className })} aria-hidden />;\r\n }\r\n\r\n return (\r\n <img\r\n src={`${CDN_BASE}/${CDN_SIZE}/${name}.svg`}\r\n alt={alt}\r\n width={CDN_SIZE}\r\n height={CDN_SIZE}\r\n className={className ? `${styles.cdnIcon} ${className}` : styles.cdnIcon}\r\n onError={() => setFailed(true)}\r\n // `decoding=\"async\"` keeps a row of icons from blocking initial\r\n // paint on first render; SVG decoding is cheap, but free is free.\r\n decoding=\"async\"\r\n />\r\n );\r\n}\r\n\r\n/**\r\n * Fluent outline icon used as the fallback when the extension has no\r\n * branded mapping or the CDN fetch fails. Mirrors the original (pre-CDN)\r\n * icon picker so behavior degrades cleanly in disconnected scenarios.\r\n */\r\nfunction pickFallbackIcon(ext: string) {\r\n switch (ext) {\r\n case 'pdf':\r\n return DocumentPdf20Regular;\r\n case 'doc':\r\n case 'docx':\r\n case 'rtf':\r\n case 'odt':\r\n return Document20Regular;\r\n case 'xls':\r\n case 'xlsx':\r\n case 'csv':\r\n case 'ods':\r\n return DocumentTable20Regular;\r\n case 'ppt':\r\n case 'pptx':\r\n case 'odp':\r\n return SlideText20Regular;\r\n case 'png':\r\n case 'jpg':\r\n case 'jpeg':\r\n case 'gif':\r\n case 'bmp':\r\n case 'webp':\r\n case 'svg':\r\n case 'ico':\r\n case 'tif':\r\n case 'tiff':\r\n return Image20Regular;\r\n case 'mp4':\r\n case 'mov':\r\n case 'avi':\r\n case 'mkv':\r\n case 'webm':\r\n case 'm4v':\r\n return Video20Regular;\r\n case 'mp3':\r\n case 'wav':\r\n case 'ogg':\r\n case 'flac':\r\n case 'm4a':\r\n case 'aac':\r\n return MusicNote220Regular;\r\n case 'zip':\r\n case '7z':\r\n case 'rar':\r\n case 'tar':\r\n case 'gz':\r\n return FolderZip20Regular;\r\n case 'txt':\r\n case 'md':\r\n case 'markdown':\r\n case 'log':\r\n case 'json':\r\n case 'xml':\r\n case 'yaml':\r\n case 'yml':\r\n case 'html':\r\n case 'htm':\r\n case 'js':\r\n case 'ts':\r\n case 'cs':\r\n case 'css':\r\n return DocumentText20Regular;\r\n default:\r\n return Document20Regular;\r\n }\r\n}\r\n","import type { components, ResolvedColumn, TableSecurityPermission } from '@powerportalspro/core';\nimport { createContext, useContext } from 'react';\n\ntype TableRecord = components['schemas']['TableRecord'];\ntype TableRecordReference = components['schemas']['TableRecordReference'];\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\n\n/**\n * A row-level edit queued by an in-grid `<OpenRecordGridButton>` when its\n * `behavior=\"withGridContext\"` mode is active. The grid holds these in\n * memory until the surrounding `MainContext` saves — at which point its\n * `getRequests` cascade returns one `UpdateRequest` per queued row,\n * included in the parent's transactional `executeMultiple` batch alongside\n * the cascading record's own update.\n *\n * `null` values are explicit-clear signals (matches the wire's column-\n * clearing convention) so the server's update handler can interpret them as\n * \"set this column to no value\" rather than dropping them from the payload.\n */\nexport interface PendingRowUpdate {\n /** Logical name of the row's table — needed for the resulting UpdateRequest. */\n readonly tableName: string;\n /** Primary id of the row being updated. */\n readonly recordId: string;\n /** Dirty columns from the dialog session, keyed by column logical name. */\n readonly dirty: ReadonlyMap<string, ColumnValueBase | null>;\n}\n\n/**\n * A row-level create queued by an in-grid `<NewRecordGridButton>` when its\n * `behavior=\"withGridContext\"` mode is active. Same registry semantic as\n * {@link PendingRowUpdate} — the grid surfaces these via its descendant\n * `getRequests`, emitting `CreateRequest`s the surrounding `MainContext`\n * batches into its transactional save.\n *\n * Unlike updates, creates have no record id yet — they're full record\n * payloads ready to ship. The server assigns the id on commit (returned in\n * the matching `CreateResponse`, which the React port currently doesn't\n * propagate back to the queue — a follow-up when the post-save id-distribution\n * step lands).\n */\nexport interface PendingRowCreate {\n /** Full record to create — must carry `tableName` and `properties`. */\n readonly record: TableRecord;\n}\n\n/**\n * Cross-cutting state exposed by {@link MainGrid} to its descendant button\n * components via React context. Buttons read selection / table info /\n * refresh through {@link useGridContext} so consumers can drop\n * `<NewRecordGridButton>` / `<DeleteRecordGridButton>` / etc. inside\n * `<MainGrid>` without prop drilling.\n *\n * Exposes selected records, table name, view, and a refresh callback.\n * Extensions land here when new buttons need new fields.\n */\nexport interface GridContextValue {\n /**\n * Records currently selected in the grid. Empty when nothing is\n * selected. Per-page only in v1 — paging clears the selection.\n */\n selectedRecords: readonly TableRecord[];\n /**\n * Logical table name the grid was configured with. `undefined` when the\n * grid was given an explicit `viewId` and no `tableName` — buttons that\n * absolutely need a table name (e.g. New / Link) should guard for this.\n */\n tableName: string | undefined;\n /**\n * Active view id, once resolved. `undefined` until the views-for-table\n * response lands (or when the view picker is in transition between\n * selections).\n */\n viewId: string | undefined;\n /**\n * Server-resolved column descriptors for the current view, identical to\n * the array {@link MainGrid} uses for header rendering. Buttons that\n * need column metadata (e.g. a future \"Export columns to CSV\" button)\n * can read it without a separate metadata fetch.\n */\n columns: readonly ResolvedColumn[];\n /**\n * Table-level permission bitmask sampled from any record returned by the\n * grid's most recent fetch. Create / table-scoped flags (Create, etc.)\n * resolve identically on every row of a homogeneous result set since\n * Dataverse evaluates them at the table level, so reading off the first\n * row is sufficient. Read / Write / Delete are also reported per row\n * here — they happen to match the per-record bitmask of that first row,\n * which works for toolbar buttons that intend to operate on whatever's\n * currently selected.\n *\n * `undefined` when no records are visible yet (still loading, or an\n * empty grid). Consumers should fall back to enabled in that case so the\n * action isn't permanently locked on a legitimately empty grid — the\n * server will still reject the operation if the user truly lacks the\n * permission. No extra round trip needed; the server already stamped\n * this data onto each row of the search response.\n */\n tablePermissions: TableSecurityPermission | undefined;\n /**\n * Re-fetches the grid's current page / sort / search. Awaitable so a\n * button can sequence \"delete then refresh\" / \"save then refresh\" flows\n * deterministically. Internally calls the search hook's `refetch()`.\n *\n * Does **not** prompt about pending queue entries — callers that need\n * the \"discard your unsaved changes?\" gate should check {@link isDirty}\n * first and prompt via `useWarnAboutUnsavedChanges`. The built-in\n * refresh icon button on `<MainGrid>` and `<RefreshGridButton>` already\n * do this; raw `refresh()` is for the \"I know what I'm doing\" path.\n *\n * Also invokes the grid's `onRefresh` callback prop (if set) so\n * consumers can observe the reload — \"force reload\" semantics, fires\n * even when filter inputs haven't changed.\n */\n refresh: () => Promise<void>;\n /**\n * Clears every selected row in the grid. Useful for \"save then deselect\"\n * flows where the consumer's button-bar reacts to selection state and\n * the post-save UI should start from a clean slate.\n *\n * Returns synchronously — the React grid's selection is owned by\n * controlled state, so the effect lands on the next render. The\n * promise return shape is kept for API parity but resolves immediately.\n */\n clearSelection: () => Promise<void>;\n /**\n * Runs grid-level validation across every editable row. Returns `true`\n * when every per-row `<RecordContext>` validates cleanly, `false` when\n * any row fails.\n *\n * Today this returns `true` unconditionally — per-row validation runs\n * inside the dialog session before a queued edit lands in\n * `pendingRowUpdates`, so the queue holds only validation-clean\n * payloads. The grid-level call is reserved for inline-edit\n * structural rules that don't have an equivalent per-row check yet\n * (e.g. \"at least one row must exist\"). Surface kept stable so the\n * inline-editable phase can plug in real validation without changing\n * the contract.\n */\n validate: () => boolean;\n /**\n * `true` when the grid has any pending row changes (queued creates or\n * updates from dialog-based New/Edit, future queued deletes). Mirrors\n * the same `gridIsDirty` value the grid reports to its parent\n * MainContext via the descendant registry — surfaced here so toolbar\n * buttons (refresh, anything else that might discard local state) can\n * check whether to prompt the user before proceeding.\n */\n isDirty: boolean;\n /**\n * Called by `<OpenRecordGridButton>` / `<NavigateOpenRecordGridButton>`\n * on mount to publish their click action to the grid. The handler\n * receives a server-persisted row's record and is responsible for\n * opening the edit form (dialog, panel, or full-page navigation).\n *\n * Only fires for rows with a real Dataverse id — pending-create\n * rows route to {@link registerCreateReopenHandler} instead so the\n * user keeps editing the in-progress record via the form/wizard it\n * was created in.\n *\n * Returns a cleanup function that unregisters the handler on unmount\n * or when the button's effect re-runs. Only one handler is active at a\n * time — a second registration replaces the first; cleanup that finds\n * a different active handler is a no-op (so unmounting an old button\n * after a new one mounted doesn't wipe the active one).\n *\n * Note: this function is stable across renders. The currently-registered\n * handler is held internally in a ref so re-registrations don't churn\n * the grid's context value — only a transition from \"no handler\" to\n * \"handler present\" (or vice versa) updates rendered state.\n */\n registerOpenHandler: (handler: (record: TableRecord) => void) => () => void;\n /**\n * Called by `<NewRecordGridButton>` on mount to publish its\n * \"re-open a pending create\" entry point. The handler receives\n * the pending row's current values plus its index in the\n * `pendingRowCreates` queue, so the button can open its dialog\n * (form or wizard) seeded with those values and update the queue\n * entry on save via {@link updatePendingRowCreate} instead of\n * appending a new one.\n *\n * Fires when the grid detects a row-level interaction on a\n * `__pending-create-` row and a create handler is registered.\n * When no create handler is registered, the grid falls back to\n * the open handler (degraded but functional — the user just gets\n * the edit form instead of the originating create form).\n *\n * Same single-handler semantics and stable-callback identity as\n * {@link registerOpenHandler}.\n */\n registerCreateReopenHandler: (\n handler: (record: TableRecord, queueIndex: number) => void,\n ) => () => void;\n /**\n * Routes a record-level interaction (double-click, primary-name\n * link click, single-record Open button click) to the right\n * registered handler based on the row's state:\n *\n * - Synthetic `__pending-create-` id → create button's re-open\n * handler (when registered).\n * - Real Dataverse id → open button's handler (when registered).\n *\n * Falls back to the other handler when only one of the two is\n * registered. No-op when neither is mounted (the grid keeps the\n * primary-name link as plain text in that case).\n *\n * Surface this on the context so consumer code that wants to\n * trigger \"open this row\" programmatically (e.g. from a row-level\n * action menu) goes through the same routing as the built-in\n * interactions.\n */\n dispatchOpen: (record: TableRecord) => void;\n /**\n * `true` when at least one of `registerOpenHandler` /\n * `registerCreateReopenHandler` has a live registration. Drives\n * the primary-name cell's render decision: link when a handler\n * exists, plain text otherwise. Reactive so the cell flips when\n * a button mounts / unmounts.\n */\n hasRowOpenAffordance: boolean;\n /**\n * Per-row pending edits queued by `<OpenRecordGridButton>` in its\n * `behavior=\"withGridContext\"` mode. The grid surfaces these via its\n * descendant `getRequests` registration so the surrounding context's\n * transactional save batches them with whatever else is dirty in the\n * tree. Cleared on a successful save (server roundtrip refreshes the\n * grid; queue starts fresh).\n */\n pendingRowUpdates: ReadonlyMap<string, PendingRowUpdate>;\n /**\n * Pending creates queued by `<NewRecordGridButton>` in its\n * `behavior=\"withGridContext\"` mode. Parallel to {@link pendingRowUpdates}\n * — also surfaced via the grid's descendant `getRequests` as\n * `CreateRequest`s. Cleared on a successful save (with the same refresh\n * round-trip that re-fetches the grid).\n */\n pendingRowCreates: readonly PendingRowCreate[];\n /**\n * Parent record this grid is scoped to, when hosted inside a `<SubGrid>`.\n * `undefined` for a stand-alone `<MainGrid>`. Toolbar buttons that need\n * to relate new records to the parent (the `<NewRecordGridButton>` FK\n * pre-fill, the future M2M link button) read this off the context.\n */\n parentRecord: TableRecord | undefined;\n /**\n * Schema name of the relationship this grid is scoped through, when\n * hosted inside a `<SubGrid>`. Pairs with {@link parentRecord} — together\n * they let the New button resolve `parentTableMetadata.OneToMany[name].ReferencingTableColumn`\n * for the FK pre-fill. `undefined` outside a SubGrid.\n */\n relationshipName: string | undefined;\n /**\n * Stage an update for one row in the grid's pending queue. Replaces any\n * prior pending update for the same record id — later wins. Called from\n * `<OpenRecordGridButton>`'s on-save handler when the consumer chose\n * `behavior=\"withGridContext\"`.\n *\n * `dirty` should contain ONLY columns the user actually modified in the\n * dialog (the RecordContext inside the dialog tracks this for free via\n * `dirtyColumns`). Unmodified columns are excluded so the eventual\n * `UpdateRequest` ships a minimum-viable payload.\n */\n queueRowUpdate: (\n update: PendingRowUpdate,\n ) => void;\n /**\n * Stage a create in the grid's pending queue. Multiple creates are\n * appended in order; the eventual `executeMultiple` batch ships them\n * back in the same order. Called from `<NewRecordGridButton>`'s on-save\n * handler when the consumer chose `behavior=\"withGridContext\"`.\n */\n queueRowCreate: (create: PendingRowCreate) => void;\n /**\n * Replaces the pending create at `index` with a new payload — used when\n * the user re-opens the Edit dialog on a row that's still queued (a\n * pending create row that hasn't been committed to Dataverse yet). The\n * dialog runs in create-mode again, lets the user tweak values, and on\n * save calls this to swap the queue entry in place so order is\n * preserved. No-op when `index` is out of range.\n */\n updatePendingRowCreate: (index: number, create: PendingRowCreate) => void;\n /**\n * Drops every pending row change (updates AND creates AND M2M\n * associate/disassociate) from the queue. Called after a successful\n * save cascade so a subsequent dirty check reflects clean state; also\n * handy in tests / consumer code that needs to discard pending edits\n * explicitly.\n */\n clearPendingRowUpdates: () => void;\n /**\n * Pending M2M associations queued by `<LinkExistingRecordGridButton>`\n * in its `behavior=\"withGridContext\"` mode. The grid surfaces these\n * via its descendant `getRequests` as `AssociateRequest`s sent through\n * the parent context's transactional save. Only meaningful inside a\n * `<SubGrid>` (the request needs a `parentRecord` + `relationshipName`\n * to target).\n *\n * Entries carry the full {@link TableRecord} (not just a reference)\n * so the grid can pin them as visible rows while they're queued —\n * matching the UX of pending row creates. The wire-side\n * `AssociateRequest` only carries `{tableName, id}`; the grid strips\n * down at request-build time.\n */\n pendingRowAssociates: readonly TableRecord[];\n /**\n * Pending M2M disassociations queued by `<UnlinkExistingRecordGridButton>`\n * in its `behavior=\"withGridContext\"` mode. Same surface as\n * {@link pendingRowAssociates} — emitted as `DisassociateRequest` from\n * the descendant `getRequests`.\n */\n pendingRowDisassociates: readonly TableRecordReference[];\n /**\n * Queue one or more {@link TableRecord}s for M2M association via the\n * SubGrid's `parentRecord` + `relationshipName`. Replacement\n * semantics: a record already queued (same `tableName` + `id`)\n * isn't added a second time. Records already pending disassociation\n * are removed from that queue (the net intent is \"keep linked\"), and\n * vice versa for {@link queueRowDisassociates}. The full record is\n * required (not just a reference) so the grid can render the queued\n * row in-place while save is pending; the wire request still ships\n * only the identity slice.\n */\n queueRowAssociates: (records: readonly TableRecord[]) => void;\n /**\n * Queue one or more `<TableRecordReference>`s for M2M disassociation.\n * Mirror of {@link queueRowAssociates} — see that doc for dedup +\n * mutual-exclusion semantics.\n */\n queueRowDisassociates: (records: readonly TableRecordReference[]) => void;\n /**\n * Pending row deletes queued by `<DeleteRecordGridButton>` in its\n * `behavior=\"withGridContext\"` mode. Emitted as `DeleteRequest`s from\n * the grid's descendant `getRequests` so the surrounding\n * `<MainContext>.saveAll` batches them transactionally with sibling\n * edits. Cleared after a successful save round-trip (or via the\n * surrounding context's `resetState`).\n */\n pendingRowDeletes: readonly TableRecordReference[];\n /**\n * Queue one or more rows for delete-on-next-save. Dedups by\n * `{tableName, id}` (same record queued twice is a no-op). Called\n * from `<DeleteRecordGridButton>` when the consumer chose\n * `behavior=\"withGridContext\"`. Records with no `id` are dropped\n * (a DeleteRequest needs both halves).\n */\n queueRowDeletes: (records: readonly TableRecordReference[]) => void;\n /**\n * Server-fetched records currently rendered in the grid. In paged\n * mode this is the current page; in virtualize mode it's the\n * accumulated rows the user has scrolled past. Excludes pending row\n * creates (those carry synthetic ids and live in\n * {@link pendingRowCreates}). Consumers building toolbar actions that\n * operate on \"everything visible\" (bulk download, bulk apply, etc.)\n * can read off the same source the grid renders from.\n */\n loadedRecords: readonly TableRecord[];\n /**\n * Whether File / Image column cells should render the per-row preview\n * icon. Sourced from `<MainGrid allowPreviewForFileColumns>` (default\n * `true`); read by `FileCell` / `ImageCell` so the gate doesn't have to\n * thread through every cell-renderer prop signature.\n */\n allowPreviewForFileColumns: boolean;\n /**\n * Whether File / Image column cells should render the per-row download\n * icon. Sourced from `<MainGrid allowDownloadForFileColumns>` (default\n * `true`). Same context-read pattern as\n * {@link allowPreviewForFileColumns}.\n */\n allowDownloadForFileColumns: boolean;\n}\n\n// `null` sentinel lets `useGridContext` distinguish \"outside any provider\"\n// from \"inside provider with unset value\" — the latter never happens since\n// MainGrid always provides a real value, so a `null` read means the\n// consumer mounted the button outside `<MainGrid>` and we want a loud\n// error rather than a silent undefined-deref.\nconst GridContext = createContext<GridContextValue | null>(null);\n\nexport const GridContextProvider = GridContext.Provider;\n\n/**\n * Read the {@link GridContextValue} for the enclosing {@link MainGrid}.\n * Throws when called outside `<MainGrid>` — that's a wiring bug, not a\n * runtime condition worth swallowing, so the error gives consumers\n * immediate feedback during development.\n */\nexport function useGridContext(): GridContextValue {\n const ctx = useContext(GridContext);\n if (!ctx) {\n throw new Error(\n 'useGridContext() was called outside of <MainGrid>. ' +\n 'Grid buttons (NewRecordGridButton, DeleteRecordGridButton, RefreshGridButton, etc.) ' +\n 'must be rendered as children of <MainGrid>.',\n );\n }\n return ctx;\n}\n","import {\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n DialogTrigger,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n Tooltip,\r\n} from '@fluentui/react-components';\r\nimport {\r\n ArrowDownload20Regular,\r\n Eye20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { renderFileTypeIcon } from './file-type-icon.js';\r\nimport { usePowerPortalsPro, useT } from '@powerportalspro/react';\r\nimport {\r\n useCallback,\r\n useEffect,\r\n useLayoutEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type CSSProperties,\r\n} from 'react';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport {\r\n base64ToBytes,\r\n getMimeFromFileName,\r\n triggerBlobDownload,\r\n} from '../../edits/base/file-helpers.js';\r\nimport { FileViewer, type FileSource } from '../../viewers/file-viewer.js';\r\nimport { useGridContext } from '../grid-context.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\nconst EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\r\n\r\n/**\r\n * Renders File ($type 41) and Image ($type 42) columns. Same template for\r\n * both — Dataverse exposes them identically in a grid (filename + preview /\r\n * download actions); the inline image preview lives in the dialog, not the\r\n * cell. Mirrors Blazor's `ppp-file-cell` template in `FluentGridBase.razor`.\r\n *\r\n * Layout: leading document icon, filename text, trailing preview / download\r\n * icon buttons. The two trailing icons are gated individually by the grid's\r\n * `allowPreviewForFileColumns` / `allowDownloadForFileColumns` props (both\r\n * default `true`) and by whether the row represents a persisted record —\r\n * pending-create rows have no server file to preview or download yet.\r\n *\r\n * Click handlers stop propagation so triggering an action doesn't also\r\n * change selection / fire the row's double-click open handler.\r\n */\r\nexport function FileCell({ record, columnName, metadata }: GridColumnCellRendererProps) {\r\n const styles = useFileCellStyles();\r\n const t = useT();\r\n const { ppp } = usePowerPortalsPro();\r\n const { allowPreviewForFileColumns, allowDownloadForFileColumns } = useGridContext();\r\n const [isBusy, setIsBusy] = useState(false);\r\n const [previewSource, setPreviewSource] = useState<FileSource | null>(null);\r\n // Drives enter / exit animations. `mounted` keeps the Dialog rendered\r\n // for the exit-animation duration after the user dismisses; `closing`\r\n // flips the surface into its reverse-keyframe state during that\r\n // window. See `useDialogPresence` below for the timing.\r\n const presence = useDialogPresence(previewSource !== null, PREVIEW_EXIT_MS);\r\n // Last non-null preview source. We render from this (not from\r\n // `previewSource`) during the exit-animation window so the dialog\r\n // doesn't flash empty between Close-button click and unmount — the\r\n // user sees the same content shrinking + fading out, not a sudden\r\n // white rectangle. The ref is updated on every render where the live\r\n // source is non-null.\r\n //\r\n // We do NOT gate this on `presence.mounted`: when the unmount timer\r\n // fires, React renders one more time with mounted=false but the\r\n // wrapper itself is still in the DOM (until Fluent finalizes the\r\n // surface removal a frame or two later). If we cleared the children\r\n // at that moment the wrapper would collapse to its empty-padding\r\n // height (~48px) — visible briefly as a thin bar if the CSS opacity\r\n // transition hasn't quite hit 0 yet — and look like a \"frame\r\n // shrink\". Keeping the cached children rendered until the whole\r\n // surface tree unmounts hides that artifact entirely.\r\n const lastSourceRef = useRef<FileSource | null>(null);\r\n if (previewSource) lastSourceRef.current = previewSource;\r\n const renderedSource = previewSource ?? lastSourceRef.current;\r\n\r\n // Animation style derived from the presence state. Inline styles\r\n // (rather than Griffel classes) so the transition can't be overridden\r\n // by any other CSS rule — earlier attempts via `makeStyles` + nested\r\n // `[data-state]` selectors had the from/to values applied but the\r\n // transition itself never ran. Inline `transitionProperty` /\r\n // `transitionDuration` is bulletproof.\r\n //\r\n // We use two style objects driven from the same presence state:\r\n //\r\n // - `surfaceStyle` (applied inline to `<DialogSurface>`): fades\r\n // the surface chrome (white background, shadow, border-radius)\r\n // in and out via `opacity`. NO transform — Fluent's surface uses\r\n // `inset: 0; margin: auto;` for centering, and an extra scale\r\n // transform interferes with that geometry. Fading the chrome\r\n // prevents the \"blank white rectangle\" flash at the end of the\r\n // exit, because the entire frame goes transparent at the same\r\n // moment its inner content does.\r\n // - `animationStyle` (applied inline to the inner wrapper): also\r\n // fades opacity AND scales transform. Layering the two means the\r\n // content has its own slight scale gesture inside the fading\r\n // frame — the apparent visual effect is the whole dialog\r\n // softly fading + shrinking in / out.\r\n const isVisible = presence.surfaceState === 'open';\r\n const isClosing = presence.surfaceState === 'closing';\r\n const transitionDuration = isClosing ? `${PREVIEW_EXIT_MS}ms` : `${PREVIEW_ENTER_MS}ms`;\r\n // Enter uses a decelerate curve (snappy onset, gentle settle into\r\n // the open state); exit uses an accelerate curve (gentle start,\r\n // quick depart) — Fluent's motion conventions for modal surfaces.\r\n // The earlier debug pass saw these curves read as a \"flash\" at\r\n // 1500ms, but at the 120/160ms production durations the curves are\r\n // subtle and feel natural.\r\n const transitionTimingFunction = isClosing\r\n ? 'cubic-bezier(0.7, 0, 1, 0.5)'\r\n : 'cubic-bezier(0.1, 0.9, 0.2, 1)';\r\n // Surface style strips Fluent's default chrome (background,\r\n // box-shadow, border) so the DialogSurface itself becomes a\r\n // transparent positioning shell. The animated wrapper inside\r\n // carries the chrome AND the opacity transition, so the entire\r\n // visible \"frame\" fades together — no bare-white-surface flash at\r\n // the end of the close, because there IS no chrome on the surface\r\n // to be exposed when the wrapper fades.\r\n const surfaceStyle = useMemo<CSSProperties>(() => ({\r\n backgroundColor: 'transparent',\r\n boxShadow: 'none',\r\n border: 'none',\r\n padding: 0,\r\n }), []);\r\n const animationStyle = useMemo<CSSProperties>(() => ({\r\n width: '100%',\r\n transformOrigin: '50% 50%',\r\n transitionProperty: 'opacity, transform',\r\n transitionDuration,\r\n transitionTimingFunction,\r\n opacity: isVisible ? 1 : 0,\r\n transform: isVisible ? 'scale(1)' : 'scale(0.96)',\r\n // Chrome that used to live on DialogSurface — now on the\r\n // animating wrapper so it fades together with the content.\r\n backgroundColor: 'var(--colorNeutralBackground1)',\r\n color: 'var(--colorNeutralForeground1)',\r\n border: '1px solid var(--colorTransparentStroke)',\r\n borderRadius: 'var(--borderRadiusXLarge)',\r\n boxShadow: 'var(--shadow64)',\r\n padding: '24px',\r\n boxSizing: 'border-box',\r\n }), [isVisible, transitionDuration]);\r\n // When opening fresh, we want to seed `lastSourceRef` before the\r\n // first paint — otherwise the \"hidden\" first frame renders empty\r\n // and the user sees a flash of blank content between mount and the\r\n // promotion to \"open\". `useLayoutEffect`-driven sync write would be\r\n // overkill; the inline `if (previewSource)` above already covers it\r\n // because `previewSource` is what triggers mount in the first place.\r\n\r\n const fileName = resolveColumnDisplayValue(record, columnName, metadata);\r\n const tableName = record.tableName;\r\n const recordId = record.id;\r\n const isPersisted = !!recordId && recordId !== EMPTY_GUID && !recordId.startsWith('__pending-create-');\r\n\r\n const handleDownload = useCallback(async () => {\r\n if (!isPersisted || !tableName || !recordId || !fileName) return;\r\n setIsBusy(true);\r\n try {\r\n const info = await ppp.getFileInfoAsync(tableName, recordId, columnName, true);\r\n if (!info.fileData) return;\r\n const bytes = base64ToBytes(info.fileData);\r\n triggerBlobDownload(bytes, info.fileName || fileName);\r\n } finally {\r\n setIsBusy(false);\r\n }\r\n }, [columnName, fileName, isPersisted, ppp, recordId, tableName]);\r\n\r\n const handlePreview = useCallback(async () => {\r\n if (!isPersisted || !tableName || !recordId || !fileName) return;\r\n setIsBusy(true);\r\n try {\r\n const info = await ppp.getFileInfoAsync(tableName, recordId, columnName, true);\r\n if (!info.fileData) return;\r\n const resolvedName = info.fileName || fileName;\r\n setPreviewSource({\r\n kind: 'base64',\r\n value: info.fileData,\r\n mimeType: getMimeFromFileName(resolvedName),\r\n fileName: resolvedName,\r\n });\r\n } finally {\r\n setIsBusy(false);\r\n }\r\n }, [columnName, fileName, isPersisted, ppp, recordId, tableName]);\r\n\r\n if (!fileName) return null;\r\n\r\n const showPreview = allowPreviewForFileColumns && isPersisted;\r\n const showDownload = allowDownloadForFileColumns && isPersisted;\r\n const previewLabel = t('app.components.grid.file-cell-preview-tooltip');\r\n const downloadLabel = t('app.components.grid.file-cell-download-tooltip');\r\n\r\n return (\r\n <>\r\n <div className={mergeClasses('ppp-grid-cell-file', styles.cell)}>\r\n {/* Icon picked from the filename extension — pdf rows show the\r\n DocumentPdf glyph, images show Image, archives FolderZip, etc.\r\n Falls back to a generic Document icon. Mirrors Blazor's\r\n FileTypeIcons.GetIcon. */}\r\n {renderFileTypeIcon(fileName, styles.leadingIcon)}\r\n <span className={styles.name}>{fileName}</span>\r\n {showPreview && (\r\n <Tooltip content={previewLabel} relationship=\"label\" withArrow>\r\n <Button\r\n appearance=\"subtle\"\r\n size=\"small\"\r\n icon={<Eye20Regular />}\r\n aria-label={previewLabel}\r\n disabled={isBusy}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n void handlePreview();\r\n }}\r\n />\r\n </Tooltip>\r\n )}\r\n {showDownload && (\r\n <Tooltip content={downloadLabel} relationship=\"label\" withArrow>\r\n <Button\r\n appearance=\"subtle\"\r\n size=\"small\"\r\n icon={<ArrowDownload20Regular />}\r\n aria-label={downloadLabel}\r\n disabled={isBusy}\r\n onClick={(ev) => {\r\n ev.stopPropagation();\r\n void handleDownload();\r\n }}\r\n />\r\n </Tooltip>\r\n )}\r\n </div>\r\n <Dialog\r\n open={presence.mounted}\r\n onOpenChange={(_, data) => {\r\n if (!data.open) setPreviewSource(null);\r\n }}\r\n modalType=\"modal\"\r\n >\r\n <DialogSurface className={styles.previewSurface} style={surfaceStyle}>\r\n {/* Animation lives on an inner wrapper rather than the\r\n DialogSurface itself because Fluent's surface controls\r\n positioning / sizing (inset: 0; margin: auto; max-width)\r\n and overlaying our own transform on it interferes with\r\n that geometry. The wrapper transitions opacity + scale\r\n between the three presence states via inline `style`. */}\r\n <div style={animationStyle}>\r\n {renderedSource && (\r\n <DialogBody>\r\n <DialogTitle>{renderedSource.fileName}</DialogTitle>\r\n <DialogContent className={styles.previewContent}>\r\n <FileViewer source={renderedSource} fileName={renderedSource.fileName ?? fileName} />\r\n </DialogContent>\r\n {/* Mirrors the `PrimaryAction = \"OK\"` button Blazor's\r\n `FilePreviewDialog` ships with — gives keyboard / mouse\r\n users an explicit dismiss target alongside the\r\n click-outside / Escape dismissals.\r\n `DialogTrigger disableButtonEnhancement` wires the\r\n underlying Button to the dialog's close action without\r\n Fluent re-skinning it. */}\r\n <DialogActions>\r\n <DialogTrigger disableButtonEnhancement>\r\n <Button appearance=\"primary\">\r\n {t('app.buttons.close.label')}\r\n </Button>\r\n </DialogTrigger>\r\n </DialogActions>\r\n </DialogBody>\r\n )}\r\n </div>\r\n </DialogSurface>\r\n </Dialog>\r\n </>\r\n );\r\n}\r\n\r\n// Enter / exit transition durations, ms. The exit value also drives\r\n// how long the dialog stays mounted after the user dismisses so the\r\n// closing transition has time to play. Enter is slightly longer than\r\n// exit so the open feels gentle while the close feels snappy —\r\n// matches the asymmetry Fluent's design system uses for modal\r\n// surfaces.\r\nconst PREVIEW_ENTER_MS = 160;\r\nconst PREVIEW_EXIT_MS = 120;\r\n\r\n/**\r\n * Three-state presence machine for an enter/exit animation:\r\n *\r\n * - `mounted`: whether to render the dialog at all. Stays `true` for\r\n * `exitMs` after `open` flips to false so the closing transition\r\n * can play before unmount.\r\n * - `surfaceState`: drives the surface's `data-state` attribute:\r\n * - `'hidden'` — initial state on first mount; the surface paints\r\n * faded + scaled-down. We flip out of this on the next frame so\r\n * the browser sees a `from` value and runs the transition.\r\n * - `'open'` — fully visible, the target state of the enter\r\n * transition + the starting state of the exit transition.\r\n * - `'closing'` — exit target state (faded + scaled-down again).\r\n * Stays applied until the unmount timer fires.\r\n *\r\n * Why a separate state machine instead of just toggling on `open`:\r\n * the browser only runs a CSS transition when the from-value is in the\r\n * DOM for at least one paint frame BEFORE the to-value lands. Mounting\r\n * an element already in its final state skips the transition entirely.\r\n * Two-phase mount (paint hidden → next-frame promote to open) gives\r\n * the browser the paint it needs.\r\n */\r\nfunction useDialogPresence(open: boolean, exitMs: number): {\r\n mounted: boolean;\r\n surfaceState: 'hidden' | 'open' | 'closing';\r\n} {\r\n const [mounted, setMounted] = useState(open);\r\n const [surfaceState, setSurfaceState] = useState<'hidden' | 'open' | 'closing'>(\r\n open ? 'open' : 'hidden',\r\n );\r\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n // Mount / unmount sequencing. `useEffect` (not Layout) for the\r\n // exit-timer path so the closing render commits first; layout-effect\r\n // for the hidden→open promotion below to make sure the surface\r\n // paints once in its hidden state before promoting.\r\n useEffect(() => {\r\n if (open) {\r\n if (timerRef.current) {\r\n clearTimeout(timerRef.current);\r\n timerRef.current = null;\r\n }\r\n setMounted(true);\r\n return;\r\n }\r\n if (!mounted) return;\r\n setSurfaceState('closing');\r\n timerRef.current = setTimeout(() => {\r\n setMounted(false);\r\n setSurfaceState('hidden');\r\n timerRef.current = null;\r\n }, exitMs);\r\n return () => {\r\n if (timerRef.current) {\r\n clearTimeout(timerRef.current);\r\n timerRef.current = null;\r\n }\r\n };\r\n }, [open, mounted, exitMs]);\r\n\r\n // Two-phase enter promotion. When the surface JUST mounted with\r\n // `surfaceState='hidden'`, we want the browser to paint it once in\r\n // that state, then flip to `'open'` so the transition runs. Using\r\n // `requestAnimationFrame` from a layout effect guarantees the\r\n // hidden paint commits before we change the state. Without this,\r\n // React batches the mount + state flip into the same paint and the\r\n // browser sees the surface in its final state from the start.\r\n useLayoutEffect(() => {\r\n if (!mounted) return;\r\n if (surfaceState !== 'hidden') return;\r\n const id = requestAnimationFrame(() => setSurfaceState('open'));\r\n return () => cancelAnimationFrame(id);\r\n }, [mounted, surfaceState]);\r\n\r\n // When opening fresh, reset to 'hidden' first so the enter sequence\r\n // above kicks in. (After a previous close-then-reopen, surfaceState\r\n // would already be 'hidden' from the unmount-timer cleanup, but on\r\n // a first open right after mount, we still want the from-state.)\r\n useEffect(() => {\r\n if (open && mounted && surfaceState === 'closing') {\r\n // Re-opened mid-close: skip back to hidden, then promote.\r\n setSurfaceState('hidden');\r\n }\r\n }, [open, mounted, surfaceState]);\r\n\r\n return { mounted, surfaceState };\r\n}\r\n\r\nconst useFileCellStyles = makeStyles({\r\n cell: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n minWidth: 0,\r\n },\r\n leadingIcon: {\r\n flexShrink: 0,\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n name: {\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n },\r\n // Preview dialog mirrors FileEdit's surface so the two share a layout —\r\n // wider than Fluent's default so PDF / markdown / image renderers have\r\n // room to breathe, capped at the viewport for small screens.\r\n previewSurface: {\r\n maxWidth: 'min(1200px, 90vw)',\r\n width: '90vw',\r\n },\r\n // Animation wrapper styles live inline on the wrapper div (see\r\n // `animationStyle` in FileCell) — earlier Griffel-driven attempts\r\n // had the from/to values applied but the transition itself didn't\r\n // run, so inline `style` is the bulletproof path.\r\n previewContent: {\r\n overflow: 'hidden',\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n },\r\n});\r\n","import { Highlighter } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Renders Lookup ($type 6) columns — references to records in other\r\n * tables. Shows the referenced record's display name; lookup search\r\n * matches against the same name, so we pipe `searchText` through to\r\n * the highlighter. Click-through (open the record's edit page) is\r\n * deferred to Phase 3 with the navigation button infrastructure.\r\n */\r\nexport function LookupCell({ value: _value, record, columnName, metadata, searchText }: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <Highlighter text={display} searchText={searchText ?? null} className=\"ppp-grid-cell-lookup\" />;\r\n}\r\n","/**\r\n * Operator-prefixed numeric search support for grid cells.\r\n *\r\n * Mirrors the server-side `GetNumberSearchOptions` /\r\n * `TryParseNumberStringValue` pair in `FetchXmlQueryComposer` so a cell\r\n * is visually highlighted exactly when the server's filter would have\r\n * matched it. Without this, an operator search (`<50000`) shows the\r\n * filtered rows but gives no visual cue which *column* in each row\r\n * satisfied the predicate — confusing when the search is OR'd across\r\n * every searchable column on the row.\r\n */\r\n\r\nexport type NumericSearchOp = '=' | '<' | '>' | '<=' | '>=';\r\n\r\nexport interface ParsedNumericSearch {\r\n op: NumericSearchOp;\r\n value: number;\r\n}\r\n\r\n/**\r\n * Parses operator-prefixed search input (`<50000`, `>=100`, `=42`, or a\r\n * bare `50000` which is treated as equality) into an operator + numeric\r\n * value. Returns `null` when the residual after stripping the operator\r\n * isn't a finite number — e.g. plain text searches like `\"acme\"` don't\r\n * parse as numeric and the caller should fall through to no-highlight.\r\n *\r\n * Whitespace is stripped so `\">= 100\"` works (matches the server).\r\n */\r\nexport function parseNumericSearch(\r\n searchText: string | null | undefined,\r\n): ParsedNumericSearch | null {\r\n if (!searchText) return null;\r\n const trimmed = searchText.replace(/\\s+/g, '');\r\n if (!trimmed) return null;\r\n let op: NumericSearchOp = '=';\r\n let residual = trimmed;\r\n if (residual.startsWith('>=')) {\r\n op = '>=';\r\n residual = residual.slice(2);\r\n } else if (residual.startsWith('<=')) {\r\n op = '<=';\r\n residual = residual.slice(2);\r\n } else if (residual.startsWith('>')) {\r\n op = '>';\r\n residual = residual.slice(1);\r\n } else if (residual.startsWith('<')) {\r\n op = '<';\r\n residual = residual.slice(1);\r\n } else if (residual.startsWith('=')) {\r\n op = '=';\r\n residual = residual.slice(1);\r\n }\r\n if (!residual) return null;\r\n const n = Number(residual);\r\n if (!Number.isFinite(n)) return null;\r\n return { op, value: n };\r\n}\r\n\r\n/**\r\n * Evaluates a parsed numeric-search predicate against a cell's raw\r\n * value. Returns false for null / undefined / non-numeric inputs — the\r\n * server would already have filtered those out, but the grid can still\r\n * render placeholder rows during a paging transition.\r\n *\r\n * The numeric `ColumnValueBase` variants (Int / Decimal / Double /\r\n * BigInt / Money) all carry `value?: number | string`; the string form\r\n * occurs when an int64 overflows JS's safe-integer range. `Number()`\r\n * handles both — the precision loss on huge BigInts is consistent with\r\n * the rest of the React stack.\r\n */\r\nexport function matchesNumericSearch(\r\n rawCellValue: unknown,\r\n parsed: ParsedNumericSearch,\r\n): boolean {\r\n if (rawCellValue == null) return false;\r\n const n = typeof rawCellValue === 'number' ? rawCellValue : Number(rawCellValue);\r\n if (!Number.isFinite(n)) return false;\r\n switch (parsed.op) {\r\n case '=':\r\n return n === parsed.value;\r\n case '<':\r\n return n < parsed.value;\r\n case '>':\r\n return n > parsed.value;\r\n case '<=':\r\n return n <= parsed.value;\r\n case '>=':\r\n return n >= parsed.value;\r\n }\r\n}\r\n","import { HighlightFull } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\nimport { matchesNumericSearch, parseNumericSearch } from './numeric-search-match.js';\r\n\r\n/**\r\n * Renders Money ($type 8) columns. Server's\r\n * `formattedValues[columnName]` already applies the column's currency\r\n * + the user's locale (\"$1,234.56\" / \"1.234,56 €\"); the shared\r\n * {@link resolveColumnDisplayValue} fallback chain surfaces it.\r\n *\r\n * Same whole-cell-on-predicate highlight model as {@link NumberCell} —\r\n * operator-prefixed searches (`<50000`, `>=100`, `=42`) light up the\r\n * cell when the raw money value satisfies the comparison.\r\n */\r\nexport function MoneyCell({\r\n value,\r\n record,\r\n columnName,\r\n metadata,\r\n searchText,\r\n}: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n const parsed = parseNumericSearch(searchText);\r\n if (parsed) {\r\n const raw = (value as { value?: number | string } | null | undefined)?.value;\r\n if (matchesNumericSearch(raw, parsed)) {\r\n return <HighlightFull text={display} className=\"ppp-grid-cell-money\" />;\r\n }\r\n }\r\n return <span className=\"ppp-grid-cell-money\">{display}</span>;\r\n}\r\n","import { Highlighter } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Renders MultiSelectChoice ($type 40) columns. Server-side\r\n * `formattedValues[columnName]` already provides a pre-joined display\r\n * string (semicolon-separated labels); multi-select search matches\r\n * against those labels, so `searchText` flows through to the highlighter.\r\n */\r\nexport function MultiSelectChoiceCell({\r\n value: _value,\r\n record,\r\n columnName,\r\n metadata,\r\n searchText,\r\n}: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <Highlighter text={display} searchText={searchText ?? null} className=\"ppp-grid-cell-multi-select-choice\" />;\r\n}\r\n","import { HighlightFull } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\nimport { matchesNumericSearch, parseNumericSearch } from './numeric-search-match.js';\r\n\r\n/**\r\n * Renders numeric columns: Int / Decimal / Double / BigInt. Server's\r\n * `formattedValues[columnName]` carries the locale-aware + precision-\r\n * applied display string (\"1,234.56\"); we lean on the shared\r\n * {@link resolveColumnDisplayValue} fallback chain to surface it.\r\n *\r\n * Highlight model is whole-cell-on-predicate (see {@link HighlightFull}):\r\n * when the user's search parses as an operator-prefixed numeric term\r\n * (`<50000`, `>=100`, `=42`), each row's matching numeric / money cell\r\n * is highlighted so the user can see which column satisfied the\r\n * search. Substring highlighting against the formatted display\r\n * (\"1,234.56\" vs. `<50000`) would never line up with the actual server\r\n * predicate, so we evaluate the same comparison here.\r\n */\r\nexport function NumberCell({\r\n value,\r\n record,\r\n columnName,\r\n metadata,\r\n searchText,\r\n}: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n const parsed = parseNumericSearch(searchText);\r\n if (parsed) {\r\n const raw = (value as { value?: number | string } | null | undefined)?.value;\r\n if (matchesNumericSearch(raw, parsed)) {\r\n return <HighlightFull text={display} className=\"ppp-grid-cell-number\" />;\r\n }\r\n }\r\n return <span className=\"ppp-grid-cell-number\">{display}</span>;\r\n}\r\n","import { Highlighter } from '../../components/highlighter.js';\r\nimport { resolveColumnDisplayValue } from '../../edits/base/column-display.js';\r\nimport type { GridColumnCellRendererProps } from './cell-dispatcher.js';\r\n\r\n/**\r\n * Renders String ($type 14) and Memo ($type 7) columns. Both serialize as\r\n * `StringValue` over the wire — same `value` string field — so a single\r\n * cell handles them. Memo's potentially-long content is left to the parent\r\n * grid's CSS to truncate (white-space: nowrap + text-overflow: ellipsis on\r\n * the cell).\r\n *\r\n * Uses {@link resolveColumnDisplayValue} for the display fallback chain\r\n * (formattedValue → raw value → null), and {@link Highlighter} to mark\r\n * substring matches against the grid's current search text.\r\n */\r\nexport function TextCell({ value: _value, record, columnName, metadata, searchText }: GridColumnCellRendererProps) {\r\n const display = resolveColumnDisplayValue(record, columnName, metadata);\r\n if (display == null || display === '') return null;\r\n return <Highlighter text={display} searchText={searchText ?? null} className=\"ppp-grid-cell-text\" />;\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\nimport type { ReactNode } from 'react';\r\nimport { BoolCell } from './bool-cell.js';\r\nimport { ChoiceCell } from './choice-cell.js';\r\nimport { DateCell } from './date-cell.js';\r\nimport { FallbackCell } from './fallback-cell.js';\r\nimport { FileCell } from './file-cell.js';\r\nimport { LookupCell } from './lookup-cell.js';\r\nimport { MoneyCell } from './money-cell.js';\r\nimport { MultiSelectChoiceCell } from './multi-select-choice-cell.js';\r\nimport { NumberCell } from './number-cell.js';\r\nimport { TextCell } from './text-cell.js';\r\n\r\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\r\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\r\ntype TableRecord = components['schemas']['TableRecord'];\r\n\r\n/**\r\n * Common cell-context fields shared by every renderer flavor — the row\r\n * record, the optional SubGrid parent record, and the grid's current\r\n * search text. Renderer-specific shapes (`<GridColumn>` vs.\r\n * `<GridTemplateColumn>`) extend this base with the fields unique to\r\n * their pipeline.\r\n *\r\n * `parentRecord` is the surrounding `<RecordContext>`'s record when the\r\n * grid is mounted as a `<SubGrid>` (a 1:N or M:N child grid of a parent\r\n * form). Lets a renderer key conditional formatting off the parent —\r\n * e.g. \"highlight contacts whose `industrycode` doesn't match the parent\r\n * account's.\" `undefined` for top-level `<MainGrid>` usage where there's\r\n * no surrounding record context.\r\n */\r\nexport interface BaseCellRendererProps {\r\n record: TableRecord;\r\n parentRecord?: TableRecord;\r\n /**\r\n * Current search text from the grid's search box, with the framework's\r\n * `*` wildcard markers already stripped. Text-bearing cells (string,\r\n * lookup, choice, multi-select-choice) pass this through to\r\n * `<Highlighter>` so matched substrings render in a yellow `<mark>`.\r\n * Cells whose values aren't searched against (numeric, date, bool,\r\n * file/image) ignore it.\r\n */\r\n searchText?: string | null;\r\n}\r\n\r\n/**\r\n * Shape passed to a `<GridColumn>`'s `cellRenderer` (and to the built-in\r\n * cells the type-dispatcher returns). Carries the BOUND column's\r\n * pre-resolved value + metadata so the renderer doesn't have to dig\r\n * into `record.properties` itself.\r\n *\r\n * `value` is the column's typed value from\r\n * `TableRecord.properties[columnName]` — a discriminated\r\n * `ColumnValueBase` variant whose `$type` matches the column's\r\n * metadata `$type`. Cells cast to the variant they expect; values\r\n * whose `$type` doesn't match the dispatcher's pick fall back to the\r\n * type-display the cell does best (e.g. `NumberCell.formattedValue`\r\n * works whether the underlying value is Int / Decimal / Double /\r\n * BigInt because they all share the same shape).\r\n *\r\n * `record`, `columnName`, and `metadata` are passed for custom\r\n * renderers (see `MainGridProps.cellRenderers` / `typeRenderers`)\r\n * that need cross-column logic, e.g. \"render `firstname` as a link\r\n * using the row's primary id\" or \"use `lastname` when `firstname` is\r\n * empty\". The built-in cells ignore them.\r\n */\r\nexport interface GridColumnCellRendererProps extends BaseCellRendererProps {\r\n value: ColumnValueBase | null | undefined;\r\n metadata: ColumnMetadataBase | undefined;\r\n columnName: string;\r\n}\r\n\r\nexport type GridColumnCellRenderer = (\r\n props: GridColumnCellRendererProps,\r\n) => ReactNode;\r\n\r\n/**\r\n * Shape passed to a `<GridTemplateColumn>`'s `cellRenderer`. Doesn't\r\n * carry a single `value` or `metadata` (a template column has no\r\n * bound column to provide them for); instead it surfaces:\r\n *\r\n * - `id` — the template column's identifier (auto-generated by the\r\n * extract walker when the consumer omits it).\r\n * - `dependsOnMetadata` — pre-built lookup map keyed by the column\r\n * names declared in {@link GridTemplateColumnProps.dependsOn},\r\n * each entry the resolved `ColumnMetadataBase` from the primary\r\n * table's metadata. Entries that don't resolve (dot-notation\r\n * linked columns, or a non-existent column name) come back as\r\n * `undefined`.\r\n * - The shared `record` / `parentRecord` / `searchText` context from\r\n * {@link BaseCellRendererProps}.\r\n *\r\n * Read column values from `record.properties` / `record.formattedValues`\r\n * — or use `resolveColumnDisplayValue` for the formatted → raw\r\n * fallback chain the built-in cells follow.\r\n */\r\nexport interface GridTemplateColumnCellRendererProps extends BaseCellRendererProps {\r\n id: string;\r\n dependsOnMetadata: Record<string, ColumnMetadataBase | undefined>;\r\n}\r\n\r\nexport type GridTemplateColumnCellRenderer = (\r\n props: GridTemplateColumnCellRendererProps,\r\n) => ReactNode;\r\n\r\n/**\r\n * Shape passed to a `<GridTemplateColumn>`'s `editRenderer` when the\r\n * grid is in inline-edit mode. Same fields as\r\n * {@link GridTemplateColumnCellRendererProps} plus {@link setValue} —\r\n * the imperative escape hatch for the custom-widget editing path.\r\n *\r\n * Two authoring patterns are supported:\r\n *\r\n * 1. **Drop-in editors** — render `<ColumnEdit columnName=\"firstname\"/>`\r\n * (or any other type-dispatched editor) directly inside the\r\n * `editRenderer`'s returned JSX. The surrounding per-row\r\n * `<RecordContext>` provides their metadata + setValue hooks\r\n * automatically, so the edits dirty-track and validate exactly\r\n * like a bound `<GridColumn>`'s `cellRenderer` does. No need to\r\n * call {@link setValue} in this case.\r\n *\r\n * 2. **Custom widget** — render an arbitrary input (a \"First Last\"\r\n * string parser that emits two columns, a slider tied to multiple\r\n * percent fields, etc.) and call `setValue(columnName, value)` for\r\n * each affected column. **Validation is your responsibility on\r\n * this path** — the framework only validates editors that\r\n * registered with the row's edit-context.\r\n */\r\nexport interface GridTemplateColumnEditRendererProps\r\n extends GridTemplateColumnCellRendererProps {\r\n /**\r\n * Writes `value` to `columnName` on the row's record. Mirrors\r\n * `useRecordContext().setColumnValue` — sets the property on the\r\n * underlying record, marks the row dirty, and re-renders\r\n * descendants that read the column.\r\n *\r\n * Pass `null` or `undefined` to clear the column. For dot-notation\r\n * aliased link-entity columns the framework routes the write to\r\n * the matching related-record store.\r\n */\r\n setValue: (\r\n columnName: string,\r\n value: ColumnValueBase | null | undefined,\r\n ) => void;\r\n}\r\n\r\nexport type GridTemplateColumnEditRenderer = (\r\n props: GridTemplateColumnEditRendererProps,\r\n) => ReactNode;\r\n\r\n/**\r\n * Semantic categories used to drive `MainGridProps.typeRenderers` overrides.\r\n * Maps several Dataverse `$type` codes onto a single human-readable bucket\r\n * so consumers can override e.g. \"all number columns\" with one entry rather\r\n * than four (Int / Decimal / Double / BigInt).\r\n *\r\n * `fallback` covers every type without a dedicated built-in cell (Guid,\r\n * Owner, Status, State, etc.).\r\n */\r\nexport type CellRendererType =\r\n | 'string'\r\n | 'number'\r\n | 'money'\r\n | 'date'\r\n | 'bool'\r\n | 'choice'\r\n | 'multiSelectChoice'\r\n | 'lookup'\r\n | 'file'\r\n | 'image'\r\n | 'fallback';\r\n\r\n/**\r\n * Maps a column metadata's `$type` discriminator to the semantic category\r\n * a {@link CellRendererType}-keyed override targets. Same code paths as\r\n * {@link getCellRenderer} — kept in lockstep with the built-in dispatch\r\n * so overrides land on the same set of columns the default cells would.\r\n */\r\nexport function getRendererTypeForMetadata(\r\n metadata: ColumnMetadataBase | undefined,\r\n): CellRendererType {\r\n const typeCode = (metadata as { $type?: number } | undefined)?.$type;\r\n switch (typeCode) {\r\n case 0:\r\n return 'bool';\r\n case 2:\r\n return 'date';\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 18:\r\n return 'number';\r\n case 6:\r\n return 'lookup';\r\n case 7:\r\n case 14:\r\n return 'string';\r\n case 8:\r\n return 'money';\r\n case 11:\r\n return 'choice';\r\n case 40:\r\n return 'multiSelectChoice';\r\n case 41:\r\n return 'file';\r\n case 42:\r\n return 'image';\r\n default:\r\n return 'fallback';\r\n }\r\n}\r\n\r\n/**\r\n * Maps a column type code to the cell renderer that knows how to display\r\n * its values. Mirrors the per-type pattern we used for Edit components —\r\n * one file per type, dispatched here. New types are added by writing a\r\n * new cell file and adding a case.\r\n *\r\n * Type codes (matching the C# `ColumnType` enum / OpenAPI's\r\n * `ColumnMetadataBase*` `$type` discriminators):\r\n * 0 = Bool 2 = DateTime 3 = Decimal\r\n * 4 = Double 5 = Int 6 = Lookup\r\n * 7 = Memo 8 = Money 11 = Choice\r\n * 14 = String 15 = Guid 18 = BigInt\r\n * 40 = MultiSelectChoice 41 = File 42 = Image\r\n *\r\n * File / Image columns (41 / 42) share `FileCell` — same template (filename +\r\n * preview / download icon buttons) since Dataverse exposes them identically\r\n * in a grid. Mirrors Blazor's `isFileOrImage` branch in `FluentGridBase.razor`.\r\n *\r\n * MainGrid uses {@link getCellRendererByType} directly with the server-\r\n * provided `ResolvedColumn.type`; {@link getCellRenderer} (the metadata-\r\n * keyed wrapper) is kept for consumers writing their own grids who only\r\n * have `ColumnMetadataBase` in hand.\r\n */\r\nexport function getCellRendererByType(typeCode: number | undefined): GridColumnCellRenderer {\r\n switch (typeCode) {\r\n case 0:\r\n return BoolCell;\r\n case 2:\r\n return DateCell;\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 18:\r\n return NumberCell;\r\n case 6:\r\n return LookupCell;\r\n case 7:\r\n case 14:\r\n return TextCell;\r\n case 8:\r\n return MoneyCell;\r\n case 11:\r\n return ChoiceCell;\r\n case 40:\r\n return MultiSelectChoiceCell;\r\n case 41:\r\n case 42:\r\n return FileCell;\r\n default:\r\n return FallbackCell;\r\n }\r\n}\r\n\r\n/**\r\n * Metadata-keyed wrapper around {@link getCellRendererByType}. Useful for\r\n * consumer code that has `ColumnMetadataBase` rather than a raw type code\r\n * (e.g. custom renderers wrapping the built-in cells).\r\n */\r\nexport function getCellRenderer(metadata: ColumnMetadataBase | undefined): GridColumnCellRenderer {\r\n const typeCode = (metadata as { $type?: number } | undefined)?.$type;\r\n return getCellRendererByType(typeCode);\r\n}\r\n\r\n/**\r\n * Whether the column can be sorted server-side via a FetchXML `<order>`\r\n * clause. File (type 41) and Image (type 42) columns are excluded —\r\n * Dataverse stores them as GUIDs and sorting by GUID is meaningless to\r\n * a user (matches Blazor's behavior + the server-resolved\r\n * `ResolvedColumn.isSortable` flag).\r\n *\r\n * MainGrid prefers the server-provided `ResolvedColumn.isSortable` over\r\n * this client-side check; consumers without a `ResolvedColumn` (writing\r\n * their own grids) can use this helper directly against\r\n * `ColumnMetadataBase`.\r\n */\r\nexport function isColumnSortable(metadata: ColumnMetadataBase | undefined): boolean {\r\n const typeCode = (metadata as { $type?: number } | undefined)?.$type;\r\n return typeCode !== 41 && typeCode !== 42;\r\n}\r\n\r\n/**\r\n * Type-code-keyed counterpart to {@link getRendererTypeForMetadata}, for\r\n * dispatching from a `ResolvedColumn.type` directly.\r\n */\r\nexport function getRendererTypeForCode(typeCode: number | undefined): CellRendererType {\r\n switch (typeCode) {\r\n case 0:\r\n return 'bool';\r\n case 2:\r\n return 'date';\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 18:\r\n return 'number';\r\n case 6:\r\n return 'lookup';\r\n case 7:\r\n case 14:\r\n return 'string';\r\n case 8:\r\n return 'money';\r\n case 11:\r\n return 'choice';\r\n case 40:\r\n return 'multiSelectChoice';\r\n case 41:\r\n return 'file';\r\n case 42:\r\n return 'image';\r\n default:\r\n return 'fallback';\r\n }\r\n}\r\n","import { useColumnMetadata } from '@powerportalspro/react';\r\nimport type { components } from '@powerportalspro/core';\r\nimport { BoolEdit } from './bool-edit.js';\r\nimport { ChoiceEdit } from './choice-edit.js';\r\nimport { DateTimeEdit } from './date-time-edit.js';\r\nimport { FileEdit } from './file-edit.js';\r\nimport { ImageEdit } from './image-edit.js';\r\nimport { LookupEdit } from './lookup-edit.js';\r\nimport { MemoEdit } from './memo-edit.js';\r\nimport { MoneyEdit } from './money-edit.js';\r\nimport { MultiSelectChoiceEdit } from './multi-select-choice-edit.js';\r\nimport { NumberEdit } from './number-edit.js';\r\nimport { TextEdit } from './text-edit.js';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\n\r\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\r\n\r\n/**\r\n * Meta-dispatcher edit component — renders the per-type edit\r\n * (`<TextEdit>`, `<NumberEdit>`, `<DateTimeEdit>`, etc.) that matches the\r\n * column's metadata `$type` discriminator. Mirrors Blazor's\r\n * `ColumnEdit.razor`, which big-ifs over `ColumnMetadata is XxxMetadata`\r\n * and forwards the same prop set.\r\n *\r\n * Useful when the consumer doesn't know (or doesn't want to enumerate)\r\n * the column's type at authoring time — admin-style forms that drive off\r\n * a metadata-resolved column list, or grid cells in inline-edit mode\r\n * where every column type can appear in the same loop.\r\n *\r\n * Falls back to `null` (renders nothing) for column types without a\r\n * dedicated editor — Guid, Owner, State, Status, etc. The cell-renderer\r\n * dispatcher handles those for read-only display via\r\n * {@link FallbackCell}; there's no editing UI for them.\r\n *\r\n * Type-code mapping (matches the C# `ColumnType` enum / OpenAPI's\r\n * `ColumnMetadataBase*` `$type` discriminators):\r\n * 0 = Bool → BoolEdit\r\n * 2 = DateTime → DateTimeEdit\r\n * 3 = Decimal → NumberEdit\r\n * 4 = Double → NumberEdit\r\n * 5 = Int → NumberEdit\r\n * 6 = Lookup → LookupEdit\r\n * 7 = Memo → MemoEdit\r\n * 8 = Money → MoneyEdit\r\n * 11 = Choice → ChoiceEdit\r\n * 14 = String → TextEdit\r\n * 18 = BigInt → NumberEdit\r\n * 40 = MultiSelectChoice → MultiSelectChoiceEdit\r\n * 41 = File → FileEdit\r\n * 42 = Image → ImageEdit\r\n *\r\n * `<ManyToManyLookupEdit>` is NOT dispatched here — many-to-many isn't a\r\n * column type (no `ColumnMetadata` variant for it); it's a relationship\r\n * editor and gets declared explicitly by the consumer.\r\n */\r\nexport function ColumnEdit(props: BaseColumnEditProps) {\r\n const metadata = useColumnMetadata(props.columnName);\r\n const typeCode = (metadata as { $type?: number } | undefined)?.$type;\r\n\r\n switch (typeCode) {\r\n case 0:\r\n return <BoolEdit {...props} />;\r\n case 2:\r\n return <DateTimeEdit {...props} />;\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 18:\r\n return <NumberEdit {...props} />;\r\n case 6:\r\n return <LookupEdit {...props} />;\r\n case 7:\r\n return <MemoEdit {...props} />;\r\n case 8:\r\n return <MoneyEdit {...props} />;\r\n case 11:\r\n return <ChoiceEdit {...props} />;\r\n case 14:\r\n return <TextEdit {...props} />;\r\n case 40:\r\n return <MultiSelectChoiceEdit {...props} />;\r\n case 41:\r\n return <FileEdit {...props} />;\r\n case 42:\r\n return <ImageEdit {...props} />;\r\n default:\r\n // No dedicated editor for this type (Guid / Owner / State / Status /\r\n // unloaded metadata). Returning null keeps the component out of the\r\n // render tree entirely — Blazor's ColumnEdit does the same by\r\n // falling through the if-else chain.\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Picks the per-type edit component that {@link ColumnEdit} would render\r\n * for the given metadata, without rendering it. Useful for tests, for\r\n * consumer code that wants to inspect \"which editor would this column\r\n * get?\" before mounting, and for grid cells that need the component\r\n * reference to compose into a higher-order wrapper.\r\n *\r\n * Returns `null` for types without a dedicated editor (same fallback\r\n * behavior as `<ColumnEdit>`).\r\n */\r\nexport function getEditComponentForType(\r\n typeCode: number | undefined,\r\n): ((props: BaseColumnEditProps) => React.ReactNode) | null {\r\n switch (typeCode) {\r\n case 0:\r\n return BoolEdit;\r\n case 2:\r\n return DateTimeEdit;\r\n case 3:\r\n case 4:\r\n case 5:\r\n case 18:\r\n return NumberEdit;\r\n case 6:\r\n return LookupEdit;\r\n case 7:\r\n return MemoEdit;\r\n case 8:\r\n return MoneyEdit;\r\n case 11:\r\n return ChoiceEdit;\r\n case 14:\r\n return TextEdit;\r\n case 40:\r\n return MultiSelectChoiceEdit;\r\n case 41:\r\n return FileEdit;\r\n case 42:\r\n return ImageEdit;\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Metadata-keyed counterpart to {@link getEditComponentForType}. Lets\r\n * consumer code that has `ColumnMetadataBase` in hand pick the editor\r\n * directly instead of digging the `$type` discriminator out first.\r\n */\r\nexport function getEditComponentForMetadata(\r\n metadata: ColumnMetadataBase | undefined,\r\n): ((props: BaseColumnEditProps) => React.ReactNode) | null {\r\n const typeCode = (metadata as { $type?: number } | undefined)?.$type;\r\n return getEditComponentForType(typeCode);\r\n}\r\n","import { Children, isValidElement, type ReactElement, type ReactNode } from 'react';\r\nimport type {\r\n GridColumnCellRenderer,\r\n GridTemplateColumnCellRenderer,\r\n GridTemplateColumnEditRenderer,\r\n} from './cells/cell-dispatcher.js';\r\n\r\n/**\r\n * Horizontal alignment for a `<GridColumn>`'s body cells and header label.\r\n * Mirrors Blazor's `Align.Start | Center | End` enum. When omitted, the\r\n * cell uses the type-driven default: numeric columns end-align, boolean\r\n * columns center, everything else starts.\r\n */\r\nexport type GridColumnAlign = 'start' | 'center' | 'end';\r\n\r\nexport interface GridColumnProps {\r\n /**\r\n * Logical name of the column to render — must match a column the server\r\n * returned for the active view (otherwise the grid logs a warning and\r\n * skips the entry). Dot-notation aliased linked-entity columns\r\n * (`primarycontactid.emailaddress1`) are supported and matched verbatim\r\n * against the server's resolved column list.\r\n */\r\n columnName: string;\r\n /**\r\n * Display label for the header cell. When omitted, the grid uses the\r\n * server-resolved column's localized `displayName`. Use this to override\r\n * the label without touching the underlying view.\r\n */\r\n displayName?: string;\r\n /**\r\n * Column width — number is treated as pixels; string passes through as\r\n * a CSS length (`'200px'`, `'20%'`, `'auto'`). When omitted, the grid\r\n * uses the view's stored width (with the `columnWidthScale` applied) or\r\n * lets the table distribute remaining space.\r\n */\r\n width?: number | string;\r\n /**\r\n * Horizontal alignment override. See {@link GridColumnAlign}. When\r\n * omitted, falls back to the type-driven default.\r\n */\r\n align?: GridColumnAlign;\r\n /**\r\n * Cell renderer override for this column. Same shape as\r\n * `MainGridProps.cellRenderers[columnName]`; takes precedence over both\r\n * `cellRenderers` (column-name override map) and the built-in dispatcher.\r\n * Receives a {@link GridColumnCellRendererProps} (value + bound-column\r\n * metadata + columnName, plus the shared row / parentRecord / search\r\n * context).\r\n */\r\n cellRenderer?: GridColumnCellRenderer;\r\n /**\r\n * When `false`, the column is suppressed from the rendered table even\r\n * if other declared columns reference it. Default `true`. Useful for\r\n * conditionally showing a column without unmounting the declaration.\r\n */\r\n visible?: boolean;\r\n /**\r\n * Override the view-driven sortability. `true` forces the header to be\r\n * sortable even when the resolved column reports `isSortable: false`\r\n * (rare — usually paired with {@link sortBy} for computed / aliased\r\n * display columns whose underlying physical column IS sortable).\r\n * `false` disables sorting on a column that would otherwise allow it.\r\n * When omitted, the resolved column's `isSortable` is used.\r\n */\r\n sortable?: boolean;\r\n /**\r\n * Sort against a different column name than the displayed one. Useful\r\n * when the visible column is a computed/aliased value that the server\r\n * can't sort directly — e.g. show `fullname` but sort by `lastname`,\r\n * or show a localized choice label but sort by the underlying integer.\r\n * The server receives `sortBy` as the column to ORDER BY when the user\r\n * clicks this column's header.\r\n */\r\n sortBy?: string;\r\n /**\r\n * When `true`, this column is the grid's initial sort column on mount\r\n * (provided no URL-persisted sort state takes precedence). The sort\r\n * direction defaults to {@link initialSortDirection} or ascending. At\r\n * most one declared column should set this; ties are resolved by the\r\n * declaration order (first wins).\r\n */\r\n isDefaultSortColumn?: boolean;\r\n /**\r\n * Direction for the {@link isDefaultSortColumn} default. Ignored when\r\n * `isDefaultSortColumn` isn't set. Defaults to `'asc'`.\r\n */\r\n initialSortDirection?: 'asc' | 'desc';\r\n /**\r\n * Override the column-header tooltip. When omitted, the server-resolved\r\n * `ResolvedColumn.tooltip` (if any) is used. Pass an empty string to\r\n * suppress a server-provided tooltip on this column.\r\n */\r\n tooltip?: string;\r\n}\r\n\r\n/**\r\n * Marker on every `<GridColumn>` element so `extractGridColumnOverrides`\r\n * can identify them after fragments / wrappers strip Component identity.\r\n * Reading `.type.<symbol>` from a `ReactElement` is the reliable approach\r\n * — `displayName` strings get rewritten by minifiers, and tag comparisons\r\n * fail across module boundaries.\r\n */\r\nconst GRID_COLUMN_MARKER = Symbol.for('@powerportalspro/grid-column');\r\nconst GRID_COLUMNS_MARKER = Symbol.for('@powerportalspro/grid-columns');\r\nconst GRID_TEMPLATE_COLUMN_MARKER = Symbol.for(\r\n '@powerportalspro/grid-template-column',\r\n);\r\n\r\n/**\r\n * Declarative template column for `<MainGrid>` / `<SubGrid>`. Unlike\r\n * `<GridColumn>`, a template column doesn't bind to a single Dataverse\r\n * column. Instead it declares 0–N column names via {@link dependsOn}\r\n * (which the framework merges into the request's `columns` wire override\r\n * so the data is fetched) and provides a mandatory {@link cellRenderer}\r\n * that renders whatever it wants from the row record.\r\n *\r\n * Use cases:\r\n * - Combined-name column: `dependsOn={['firstname', 'lastname']}`,\r\n * renderer joins them as a single bold cell.\r\n * - Computed status badge: `dependsOn={['statuscode', 'createdon']}`,\r\n * renderer returns a coloured badge keyed on both.\r\n * - Pure-decoration actions column: `dependsOn` omitted, renderer\r\n * returns inline buttons / icons reading nothing from the row.\r\n *\r\n * Mixed with `<GridColumn>` children in declaration order — the grid\r\n * renders them interleaved as declared. Sort / search: template columns\r\n * don't bind to a single column for sort by default; pass {@link sortBy}\r\n * to point the header click at a specific column name, otherwise the\r\n * header is not sortable.\r\n *\r\n * ```tsx\r\n * <GridColumns>\r\n * <GridColumn columnName=\"emailaddress1\" />\r\n * <GridTemplateColumn\r\n * displayName=\"Full Name\"\r\n * dependsOn={['firstname', 'lastname']}\r\n * sortBy=\"lastname\"\r\n * cellRenderer={({ record }) => {\r\n * const f = resolveColumnDisplayValue(record, 'firstname') ?? '';\r\n * const l = resolveColumnDisplayValue(record, 'lastname') ?? '';\r\n * return <strong>{`${f} ${l}`.trim()}</strong>;\r\n * }}\r\n * />\r\n * </GridColumns>\r\n * ```\r\n */\r\nexport interface GridTemplateColumnProps {\r\n /**\r\n * Optional stable identifier for the column — used as the TanStack\r\n * column id and as the sort-persistence key.\r\n *\r\n * When omitted, `extractGridColumnOverrides` auto-generates one as\r\n * `__template-${declarationIndex}`. Stable across renders for static\r\n * markup; if your declarations reorder dynamically pass an explicit\r\n * `id` to keep TanStack's column identity stable across the reorder.\r\n *\r\n * Two template columns in the same grid with the same explicit `id`\r\n * is a wiring bug — the column-defs memo treats them as one column.\r\n */\r\n id?: string;\r\n\r\n /** Header label. Required — no metadata to derive a default from. */\r\n displayName: string;\r\n\r\n /**\r\n * Header tooltip text. Optional. Falls through to a plain header\r\n * with no tooltip when omitted.\r\n */\r\n tooltip?: string;\r\n\r\n /**\r\n * Column width. Number is treated as pixels; string passes through\r\n * as a CSS length (`'200px'`, `'20%'`, `'auto'`). When omitted, the\r\n * table distributes remaining space — same default as `<GridColumn>`.\r\n */\r\n width?: number | string;\r\n\r\n /**\r\n * Horizontal alignment override. When omitted, the cell defaults to\r\n * start-aligned (no type-driven default since there's no underlying\r\n * column type to dispatch off).\r\n */\r\n align?: GridColumnAlign;\r\n\r\n /**\r\n * When `false`, the column is suppressed from the rendered table\r\n * AND its {@link dependsOn} columns are dropped from the server\r\n * projection. Default `true`.\r\n */\r\n visible?: boolean;\r\n\r\n /**\r\n * Logical names of the Dataverse columns the renderer needs. The\r\n * framework merges these into the request's `columns` wire override\r\n * so the data lands in `record.properties` / `record.formattedValues`\r\n * for the renderer to read. Empty / omitted = decorative column\r\n * (no data dependency — actions, icons, etc.).\r\n *\r\n * Duplicates across multiple declarations are deduped automatically;\r\n * a `dependsOn` column that's ALSO declared as a regular `<GridColumn>`\r\n * doesn't double-fetch.\r\n *\r\n * Dot-notation aliased linked-entity columns (e.g.\r\n * `primarycontactid.emailaddress1`) are supported on the projection\r\n * side, but their metadata DOES NOT appear in the renderer's\r\n * `dependsOnMetadata` map — the framework only looks columns up\r\n * against the primary table's metadata. Consumers needing the\r\n * linked-table column's metadata should call\r\n * `useTableMetadata(linkedTable)` in a wrapping component and pass\r\n * the result down via closure.\r\n */\r\n dependsOn?: readonly string[];\r\n\r\n /**\r\n * When set, clicking the header sorts the grid by this Dataverse\r\n * column name. When omitted, the header is not sortable (most\r\n * template columns derive from multiple sources and have no natural\r\n * sort target).\r\n */\r\n sortBy?: string;\r\n\r\n /**\r\n * Cell renderer — required. Receives\r\n * {@link GridTemplateColumnCellRendererProps} carrying the row +\r\n * `dependsOnMetadata` lookup map; no bound-column `value` /\r\n * `metadata`. Read column values from `record.properties` or\r\n * `record.formattedValues` (or use `resolveColumnDisplayValue` for\r\n * the same fallback chain the built-in cells follow).\r\n */\r\n cellRenderer: GridTemplateColumnCellRenderer;\r\n\r\n /**\r\n * Optional editor renderer — when supplied AND the grid is in\r\n * inline-edit mode (`editable={true}` or the user toggled it via the\r\n * settings panel) AND the row isn't a pending-create row, this is\r\n * called instead of {@link cellRenderer}. Receives\r\n * {@link GridTemplateColumnEditRendererProps} — same data as the\r\n * read context plus a `setValue(columnName, value)` helper.\r\n *\r\n * Two authoring patterns are supported (see\r\n * {@link GridTemplateColumnEditRendererProps}):\r\n *\r\n * 1. **Drop-in editors** — return `<ColumnEdit columnName=\"firstname\"/>`\r\n * (or any other type-dispatched editor). The surrounding per-row\r\n * `<RecordContext>` provides their hooks automatically.\r\n *\r\n * 2. **Custom widget** — return an arbitrary input and call\r\n * `setValue(columnName, value)` for each affected column.\r\n * Validation is your responsibility on this path.\r\n *\r\n * Omit to keep the column read-only even when the grid is editable\r\n * (e.g. an actions column that's always just a pair of buttons).\r\n */\r\n editRenderer?: GridTemplateColumnEditRenderer;\r\n}\r\n\r\n/**\r\n * Declarative template column. Renders nothing on its own — MainGrid\r\n * harvests its props at render time alongside `<GridColumn>` children.\r\n * See {@link GridTemplateColumnProps} for the full surface.\r\n */\r\nexport function GridTemplateColumn(_props: GridTemplateColumnProps): null {\r\n return null;\r\n}\r\n(GridTemplateColumn as unknown as Record<symbol, true>)[GRID_TEMPLATE_COLUMN_MARKER] = true;\r\n\r\n/**\r\n * Declarative column override for `<MainGrid>` / `<SubGrid>`. Mirrors\r\n * Blazor's `<GridColumn>` — when one or more are declared as children,\r\n * the grid switches to **replace mode** and renders only the declared\r\n * columns in the declared order. Without any declarations, the grid\r\n * uses the view's full column set as before.\r\n *\r\n * ```tsx\r\n * <MainGrid tableName=\"contact\">\r\n * <GridColumns>\r\n * <GridColumn columnName=\"firstname\" />\r\n * <GridColumn columnName=\"lastname\" />\r\n * <GridColumn columnName=\"emailaddress1\" width={250} />\r\n * </GridColumns>\r\n * </MainGrid>\r\n * ```\r\n *\r\n * The wrapping `<GridColumns>` is optional — bare `<GridColumn>` children\r\n * work too. The wrapper exists for parity with Blazor's authoring style\r\n * and to give consumers a single place to group column declarations\r\n * alongside `<GridButtons>` and other toolbar children.\r\n *\r\n * Each `<GridColumn>` renders nothing on its own — it's read at MainGrid\r\n * render time and turned into a TanStack column definition. Server-\r\n * resolved column metadata (display name, type, sortability) still\r\n * applies; the props on `<GridColumn>` override specific fields.\r\n */\r\nexport function GridColumn(_props: GridColumnProps): null {\r\n return null;\r\n}\r\n// Symbol marker for traversal. Assignment via Object.assign keeps the\r\n// function signature clean for callers + IDE intellisense.\r\n(GridColumn as unknown as Record<symbol, true>)[GRID_COLUMN_MARKER] = true;\r\n\r\nexport interface GridColumnsProps {\r\n children?: ReactNode;\r\n}\r\n\r\n/**\r\n * Optional grouping wrapper for one or more `<GridColumn>` declarations.\r\n * Mirrors Blazor's `<GridColumns>` child fragment. Renders `null` —\r\n * MainGrid traverses the React tree at render time to harvest column\r\n * declarations.\r\n */\r\nexport function GridColumns(_props: GridColumnsProps): null {\r\n return null;\r\n}\r\n(GridColumns as unknown as Record<symbol, true>)[GRID_COLUMNS_MARKER] = true;\r\n\r\nfunction isGridColumnElement(\r\n node: ReactNode,\r\n): node is ReactElement<GridColumnProps> {\r\n if (!isValidElement(node)) return false;\r\n const type = node.type as unknown as Record<symbol, true> | undefined;\r\n return !!type && type[GRID_COLUMN_MARKER] === true;\r\n}\r\n\r\nfunction isGridColumnsElement(\r\n node: ReactNode,\r\n): node is ReactElement<GridColumnsProps> {\r\n if (!isValidElement(node)) return false;\r\n const type = node.type as unknown as Record<symbol, true> | undefined;\r\n return !!type && type[GRID_COLUMNS_MARKER] === true;\r\n}\r\n\r\nfunction isGridTemplateColumnElement(\r\n node: ReactNode,\r\n): node is ReactElement<GridTemplateColumnProps> {\r\n if (!isValidElement(node)) return false;\r\n const type = node.type as unknown as Record<symbol, true> | undefined;\r\n return !!type && type[GRID_TEMPLATE_COLUMN_MARKER] === true;\r\n}\r\n\r\n/**\r\n * One declared column in markup order — either a regular\r\n * `<GridColumn>` (`kind === 'column'`) or a synthetic\r\n * `<GridTemplateColumn>` (`kind === 'template'`). MainGrid iterates\r\n * `orderedDeclarations` to build TanStack column defs that interleave\r\n * the two flavors in the exact order the consumer wrote them.\r\n *\r\n * Template entries always carry a non-empty `id` — when the consumer\r\n * omitted the prop, the extract walker fills in `__template-${index}`\r\n * before returning, so downstream code never has to handle the\r\n * missing-id case.\r\n */\r\nexport type GridDeclaredColumn =\r\n | { readonly kind: 'column'; readonly props: GridColumnProps }\r\n | {\r\n readonly kind: 'template';\r\n readonly props: GridTemplateColumnProps & { readonly id: string };\r\n };\r\n\r\n/**\r\n * Result of `extractGridColumnOverrides` — the harvested column\r\n * declarations plus the remaining children (passed through to the\r\n * toolbar). The split lets MainGrid render toolbar buttons (and any\r\n * other non-column-declaration children) without re-traversing.\r\n */\r\nexport interface ExtractedGridColumns {\r\n /**\r\n * Regular `<GridColumn>` declarations in declaration order. Empty\r\n * when the consumer didn't pass any.\r\n */\r\n readonly declaredColumns: readonly GridColumnProps[];\r\n /**\r\n * `<GridTemplateColumn>` declarations in declaration order. Each\r\n * entry's `id` is guaranteed non-empty — auto-filled by the walker\r\n * when the consumer omitted the prop.\r\n */\r\n readonly templateColumns: readonly (GridTemplateColumnProps & {\r\n readonly id: string;\r\n })[];\r\n /**\r\n * Both kinds of declarations interleaved in the order they appeared\r\n * in markup. MainGrid uses this to build TanStack column defs that\r\n * preserve declaration order across the regular / template split —\r\n * a consumer who writes `[col, template, col]` gets three columns in\r\n * exactly that order.\r\n */\r\n readonly orderedDeclarations: readonly GridDeclaredColumn[];\r\n /**\r\n * Children with `<GridColumn>` / `<GridColumns>` /\r\n * `<GridTemplateColumn>` elements removed. MainGrid passes this\r\n * through to its toolbar slot so `<GridButtons>`, bare buttons, and\r\n * any other consumer-supplied children render normally without the\r\n * column declarations leaking into the toolbar.\r\n */\r\n readonly toolbarChildren: ReactNode;\r\n}\r\n\r\n/**\r\n * Walks the React children tree, collecting every `<GridColumn>` and\r\n * `<GridTemplateColumn>` — recursing one level into `<GridColumns>`\r\n * wrappers — and returning the column props arrays alongside the\r\n * remaining children (toolbar buttons etc.). Synchronous so MainGrid\r\n * can call it during render and feed the results straight into the\r\n * TanStack column defs memo.\r\n *\r\n * Limitation: this is a single-level recurse into `<GridColumns>` only —\r\n * deeper nesting (`<GridColumns><Fragment><GridColumn /></Fragment></GridColumns>`)\r\n * isn't supported. Matches Blazor's flat authoring style; consumers\r\n * wanting conditional columns should use `visible` instead of dynamic\r\n * wrapping.\r\n */\r\nexport function extractGridColumnOverrides(children: ReactNode): ExtractedGridColumns {\r\n const declared: GridColumnProps[] = [];\r\n const templates: (GridTemplateColumnProps & { readonly id: string })[] = [];\r\n const ordered: GridDeclaredColumn[] = [];\r\n const toolbarParts: ReactNode[] = [];\r\n // Position counter used to mint auto-ids for template columns\r\n // whose `id` was omitted. Increments across BOTH the top-level pass\r\n // AND the `<GridColumns>` nested pass so a mixed-style markup\r\n // (`[template, <GridColumns><template /></GridColumns>]`) still\r\n // assigns unique ids per template.\r\n let templateIndex = 0;\r\n\r\n function pushTemplate(props: GridTemplateColumnProps): void {\r\n const withId: GridTemplateColumnProps & { id: string } = {\r\n ...props,\r\n id: props.id ?? `__template-${templateIndex}`,\r\n };\r\n templates.push(withId);\r\n ordered.push({ kind: 'template', props: withId });\r\n templateIndex += 1;\r\n }\r\n\r\n function pushColumn(props: GridColumnProps): void {\r\n declared.push(props);\r\n ordered.push({ kind: 'column', props });\r\n }\r\n\r\n Children.forEach(children, (child) => {\r\n if (isGridColumnElement(child)) {\r\n pushColumn(child.props);\r\n return;\r\n }\r\n if (isGridTemplateColumnElement(child)) {\r\n pushTemplate(child.props);\r\n return;\r\n }\r\n if (isGridColumnsElement(child)) {\r\n Children.forEach(child.props.children, (nested) => {\r\n if (isGridColumnElement(nested)) {\r\n pushColumn(nested.props);\r\n } else if (isGridTemplateColumnElement(nested)) {\r\n pushTemplate(nested.props);\r\n }\r\n });\r\n return;\r\n }\r\n toolbarParts.push(child);\r\n });\r\n\r\n return {\r\n declaredColumns: declared,\r\n templateColumns: templates,\r\n orderedDeclarations: ordered,\r\n toolbarChildren: toolbarParts.length === 0 ? null : toolbarParts,\r\n };\r\n}\r\n","import {\r\n Button,\r\n Menu,\r\n MenuList,\r\n MenuPopover,\r\n MenuTrigger,\r\n useIsOverflowItemVisible,\r\n useOverflowMenu,\r\n} from '@fluentui/react-components';\r\nimport { MoreHorizontal20Regular } from '@fluentui/react-icons';\r\nimport type { ReactNode } from 'react';\r\n\r\n/**\r\n * Stable ids the MainGrid toolbar uses to register its overflow items.\r\n * Centralized so the inline `<OverflowItem id={...}>` declarations and\r\n * the `<OverflowMenuItem itemId={...}>` \"is this hidden?\" checks can't\r\n * drift. New toolbar overflow items add their ids here.\r\n */\r\nexport const GridToolbarOverflowId = {\r\n Actions: 'grid-actions',\r\n Search: 'grid-search',\r\n Refresh: 'grid-refresh',\r\n Settings: 'grid-settings',\r\n} as const;\r\nexport type GridToolbarOverflowId =\r\n (typeof GridToolbarOverflowId)[keyof typeof GridToolbarOverflowId];\r\n\r\n/**\r\n * The \"…\" trigger that opens a menu listing the toolbar items currently\r\n * hidden by `<Overflow>`. Renders nothing when every item fits.\r\n *\r\n * Children are the per-item overflow renderings — typically wrapped in\r\n * `<OverflowMenuItem itemId={...}>` so each one renders ONLY when its\r\n * matching `<OverflowItem>` is in the hidden set. Render order in\r\n * `children` is render order in the menu.\r\n *\r\n * Mirrors Blazor's `FluentOverflow` \"more\" menu that surfaces collapsed\r\n * toolbar entries on narrow viewports. The React implementation is built\r\n * on `@fluentui/react-overflow`'s `useOverflowMenu` hook.\r\n */\r\nexport function GridToolbarOverflowMenu({\r\n children,\r\n ariaLabel,\r\n}: {\r\n children: ReactNode;\r\n ariaLabel: string;\r\n}) {\r\n const { ref, overflowCount, isOverflowing } =\r\n useOverflowMenu<HTMLButtonElement>();\r\n if (!isOverflowing) return null;\r\n return (\r\n <Menu hasIcons>\r\n <MenuTrigger disableButtonEnhancement>\r\n <Button\r\n // `useOverflowMenu` returns a typed MutableRefObject; Fluent's\r\n // polymorphic Button ref type expects `Ref<never>` in the slot\r\n // system, which is incompatible with the concrete element ref\r\n // the hook produces. The runtime contract is identical — Button\r\n // forwards the ref to the underlying <button> when the slot\r\n // stays default — so cast through `any` to satisfy the slot\r\n // type without altering behavior.\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n ref={ref as any}\r\n className=\"ppp-grid-toolbar-overflow\"\r\n appearance=\"subtle\"\r\n icon={<MoreHorizontal20Regular />}\r\n aria-label={`${ariaLabel} (${overflowCount})`}\r\n title={ariaLabel}\r\n />\r\n </MenuTrigger>\r\n <MenuPopover>\r\n <MenuList>{children}</MenuList>\r\n </MenuPopover>\r\n </Menu>\r\n );\r\n}\r\n\r\n/**\r\n * Wraps overflow-only content with a visibility gate keyed on the matching\r\n * `<OverflowItem>`'s id. Renders nothing when the toolbar item is still\r\n * visible inline — guarantees we never double-render (inline + menu).\r\n *\r\n * Use inside `<GridToolbarOverflowMenu>` for each per-item entry:\r\n *\r\n * ```tsx\r\n * <OverflowMenuItem itemId={GridToolbarOverflowId.Refresh}>\r\n * <MenuItem icon={<Refresh />}>Refresh</MenuItem>\r\n * </OverflowMenuItem>\r\n * ```\r\n */\r\nexport function OverflowMenuItem({\r\n itemId,\r\n children,\r\n}: {\r\n itemId: string;\r\n children: ReactNode;\r\n}) {\r\n const isVisible = useIsOverflowItemVisible(itemId);\r\n if (isVisible) return null;\r\n return <>{children}</>;\r\n}\r\n","import { useEffect, useState } from 'react';\r\n\r\n/**\r\n * Viewport breakpoint used across the grid's responsive surfaces — same\r\n * `767.98px` as Blazor's `.desktop-screen` / `.mobile-screen` media-query\r\n * pair in `FluentGridBase.razor.css`. Kept as a single named constant so\r\n * the CSS-side (when we add per-element media queries) and the JS-side\r\n * (this hook) can't drift.\r\n */\r\nexport const NARROW_VIEWPORT_BREAKPOINT_PX = 767.98;\r\n\r\n/**\r\n * Returns `true` when the viewport is at or below the narrow breakpoint.\r\n * Backed by `window.matchMedia` with a `change` listener so the value\r\n * tracks live resizes without polling.\r\n *\r\n * Used by `<MainGrid>` to decide where to host the page-size selector\r\n * (footer at wide widths, Settings popover at narrow), mirroring the\r\n * Blazor mobile-screen / desktop-screen toggle.\r\n *\r\n * SSR-safe — returns `false` on the server (no `window`); the first\r\n * client-side render then re-evaluates and triggers the correct layout.\r\n */\r\nexport function useIsNarrowViewport(): boolean {\r\n const [isNarrow, setIsNarrow] = useState<boolean>(() => {\r\n if (typeof window === 'undefined') return false;\r\n return window.matchMedia(`(max-width: ${NARROW_VIEWPORT_BREAKPOINT_PX}px)`).matches;\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n const mq = window.matchMedia(`(max-width: ${NARROW_VIEWPORT_BREAKPOINT_PX}px)`);\r\n const handler = (ev: MediaQueryListEvent) => setIsNarrow(ev.matches);\r\n // Re-sync once on mount in case SSR's `false` lazy-init was wrong on\r\n // the actual client viewport.\r\n setIsNarrow(mq.matches);\r\n mq.addEventListener('change', handler);\r\n return () => mq.removeEventListener('change', handler);\r\n }, []);\r\n\r\n return isNarrow;\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport type { CSSProperties, HTMLAttributes, ReactNode } from 'react';\r\n\r\n/**\r\n * Canonical CSS class name. The stable `ppp-footer` class appears on\r\n * every rendered Footer instance alongside Fluent's atomic classes.\r\n * Mirrors Blazor's `<div class=\"ppp-footer\">` pattern.\r\n */\r\nexport const PPP_FOOTER_CLASS = 'ppp-footer';\r\n\r\nconst useFooterStyles = makeStyles({\r\n // Single source of truth for the framework's footer-band visual\r\n // treatment. Mirrors Blazor's `Footer.razor.css` token-for-token —\r\n // any change here propagates to every consumer (Section footer\r\n // bands, future grid footers, standalone usage).\r\n //\r\n // Token mapping:\r\n // --neutral-foreground-rest → tokens.colorNeutralForeground1\r\n // --neutral-layer-2 → tokens.colorNeutralBackground2\r\n // --neutral-stroke-rest → tokens.colorNeutralStroke1\r\n // --control-corner-radius → tokens.borderRadiusMedium\r\n // 2*--design-unit (8px) → tokens.spacingVerticalS\r\n // 3*--design-unit (12px) → tokens.spacingHorizontalM\r\n root: {\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n paddingRight: tokens.spacingHorizontalM,\r\n color: tokens.colorNeutralForeground1,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n borderTop: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderBottomLeftRadius: tokens.borderRadiusMedium,\r\n borderBottomRightRadius: tokens.borderRadiusMedium,\r\n // Mirrors Blazor's `margin-top: 0px !important` — keeps the footer\r\n // flush against whatever sits above (typically a section body).\r\n marginTop: 0,\r\n },\r\n});\r\n\r\nexport interface FooterProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\r\n /** Footer content. */\r\n children?: ReactNode;\r\n /**\r\n * Class merged onto the default Fluent footer styling. Use for\r\n * CONTEXT-specific overrides — e.g. PageLayout's grid-area placement\r\n * + accent-color treatment.\r\n */\r\n className?: string;\r\n /** Inline style override. */\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Page-footer band — the single source of truth for the framework's\r\n * \"footer strip\" visual treatment. Renders a semantic `<footer>`\r\n * element with Fluent token-driven styling matching Blazor's\r\n * `Footer.razor` + `Footer.razor.css` exactly.\r\n *\r\n * Used internally by `<Section>` for section-footer bands and by\r\n * `<PageLayout>` for the page-level footer slot. Equally usable\r\n * standalone.\r\n *\r\n * Stable `ppp-footer` class is applied alongside Fluent's atomic\r\n * makeStyles classes for devtools visibility and consumer-stylable\r\n * targeting.\r\n */\r\nexport function Footer({ children, className, style, ...rest }: FooterProps) {\r\n const styles = useFooterStyles();\r\n return (\r\n <footer\r\n {...rest}\r\n className={mergeClasses(styles.root, PPP_FOOTER_CLASS, className)}\r\n style={style}\r\n >\r\n {children}\r\n </footer>\r\n );\r\n}\r\n","import { makeStyles, tokens } from '@fluentui/react-components';\r\n\r\n/**\r\n * Griffel styles for the headless-TanStack-based MainGrid. The visual goal\r\n * is parity with Fluent v9's built-in `DataGrid`: subtle row separators,\r\n * a tinted header band, hover highlight per row, and Fluent-token-driven\r\n * typography so the grid blends in with surrounding edits / labels.\r\n *\r\n * We bypass Fluent's `DataGrid` itself because TanStack owns the row /\r\n * column model — but the chrome below matches its look so consumers can\r\n * mix this grid with Fluent forms without a visual seam.\r\n */\r\nexport const useMainGridStyles = makeStyles({\r\n // Top-level grid layout:\r\n //\r\n // ┌───────────────────────────────────────┐\r\n // │ toolbar (flex-shrink: 0, fixed top) │\r\n // ├───────────────────────────────────────┤\r\n // │ bodyArea (flex: 1, hosts scroll + │\r\n // │ refetch scrim; centers status │\r\n // │ messages when no rows) │\r\n // ├───────────────────────────────────────┤\r\n // │ paging (flex-shrink: 0, fixed bot.) │\r\n // └───────────────────────────────────────┘\r\n //\r\n // Sizing is configured via {@link MainGridProps.minHeight} /\r\n // {@link MainGridProps.maxHeight} / {@link MainGridProps.fullSize},\r\n // mirroring Blazor's `MinHeight` / `MaxHeight` / `FullSize` parameters.\r\n // The base `wrapper` style has no flex sizing — the grid grows to fit\r\n // content between the configured min/max. The {@link wrapperFullSize}\r\n // variant adds `flex: 1` for the \"fill the parent's height\" case.\r\n wrapper: {\r\n width: '100%',\r\n // Mirrors `.ppp-section`'s defensive sizing: in a flex/grid parent the\r\n // default `min-width: auto` is content-based, which lets the inner\r\n // table's resized pixel width push the wrapper beyond its parent's\r\n // bounds. `min-width: 0` clamps it so the wrapper stays at 100% of\r\n // the parent and the inner `tableContainer`'s `overflow: auto`\r\n // engages — producing a horizontal scrollbar instead of escaping.\r\n minWidth: 0,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n fontFamily: tokens.fontFamilyBase,\r\n fontSize: tokens.fontSizeBase300,\r\n color: tokens.colorNeutralForeground1,\r\n // Mirrors Blazor's `.ppp-grid` shell: thin neutral outline +\r\n // rounded corners around the whole grid. `overflow: hidden` clips\r\n // the rounded corners against children (toolbar tint, body cells)\r\n // so the corner radius reads cleanly; the inner `tableContainer`\r\n // owns its own scroll behavior so the clip doesn't fight scrolling.\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n overflow: 'hidden',\r\n },\r\n // Applied when `fullSize: true`. `flex: 1 1 auto` makes the grid take\r\n // the parent's available height (must be inside a flex column or\r\n // fixed-height container), and `min-height: 0` is the critical bit\r\n // letting the bodyArea's `overflow: auto` actually clip — without it\r\n // flex children's intrinsic min content size prevents overflow from\r\n // ever engaging. Min/max-height inline styles are skipped in this\r\n // mode (matches Blazor's `GetGridContainerStyle` behavior).\r\n wrapperFullSize: {\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n },\r\n // Drops the outer border + rounded corners when `borderVisible: false`.\r\n // Useful when an embedding card / panel already supplies framing — a\r\n // double border looks heavier than intended. The inner table's column\r\n // separators and bottom rules stay; only the wrapper's outer rectangle\r\n // is suppressed.\r\n wrapperBorderless: {\r\n border: 'none',\r\n borderRadius: 0,\r\n },\r\n // Middle stripe — fills the space between toolbar and paging row.\r\n // `position: relative` anchors the absolutely-positioned refetch scrim\r\n // to the body area (scrim covers the scrollable region without\r\n // scrolling along with the content). When the grid is empty / loading /\r\n // errored, the bodyArea hosts a centered status message instead of the\r\n // scroll container.\r\n bodyArea: {\r\n position: 'relative',\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n // Same reasoning as wrapper.minWidth: 0 — without this, a flex item's\r\n // `min-width: auto` resolves to content min-content, and the inner\r\n // table's pixel width would expand bodyArea past the wrapper, blocking\r\n // the horizontal scroll trick.\r\n minWidth: 0,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n },\r\n // Top toolbar — view picker on the left, search box + button slot on\r\n // the right. Locked at the top of the grid (`flexShrink: 0`) so it\r\n // doesn't compress when the body area takes the available height.\r\n // Mirrors Blazor's `.ppp-header`: subtle neutral tint + bottom\r\n // divider stroke. Uses `colorNeutralBackground3` (≈ #f5f5f5 in\r\n // light theme) rather than `colorNeutralBackground2` — Blazor's\r\n // `--neutral-layer-2` from the FAST token system renders noticeably\r\n // darker than Fluent v9's `Background2` (~#fafafa), so we pick the\r\n // next step up for visual parity. `Background3` is the Fluent v9\r\n // equivalent shade.\r\n toolbar: {\r\n flexShrink: 0,\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalM,\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalS,\r\n paddingRight: tokens.spacingHorizontalS,\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n },\r\n viewPicker: {\r\n minWidth: '220px',\r\n },\r\n // Static title shown in the toolbar in place of the view-picker dropdown\r\n // when the grid is locked to a single view (or the consumer passed `title`\r\n // on a single-view table). Mirrors Blazor's `Title` rendering.\r\n title: {\r\n fontWeight: tokens.fontWeightSemibold,\r\n paddingLeft: tokens.spacingHorizontalS,\r\n },\r\n // Flex spacer that pushes the search box to the right end of the\r\n // toolbar (view picker on the left, search on the right, both\r\n // anchored even if the picker hides).\r\n // Header text inside the settings popover — semibold, bottom margin to\r\n // separate from the settings rows below. Matches the visual weight of\r\n // Blazor's `<Header>` slot on `FluentPopover`.\r\n settingsHeader: {\r\n fontWeight: tokens.fontWeightSemibold,\r\n marginBottom: tokens.spacingVerticalM,\r\n },\r\n // One row inside the settings popover. Horizontal flex layout so the\r\n // label sits left of the control (matching the page-size group's\r\n // footer layout). Bottom margin separates stacked rows.\r\n settingsRow: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n marginBottom: tokens.spacingVerticalS,\r\n },\r\n // Label text inside a settings row. Matches the footer's page-size\r\n // label styling so the same control feels at home in both surfaces.\r\n settingsRowLabel: {\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n // Page-size dropdown inside the settings popover — narrower than the\r\n // toolbar Settings popover's natural width to fit the compact row.\r\n settingsPageSizeDropdown: {\r\n minWidth: '70px',\r\n },\r\n // Flex container for the toolbar's overflow-aware right cluster. The\r\n // `<Overflow>` measures this against its own width; OverflowItems\r\n // inside register their widths via context and get hidden in priority\r\n // order when they don't all fit. `flex: 1` lets it expand to fill the\r\n // toolbar's available space (after the spacer); `min-width: 0` is the\r\n // critical bit that lets Overflow shrink the container — without it\r\n // flex children's intrinsic min-content prevents the measurement loop\r\n // from ever signaling overflow. `overflow: hidden` clips items mid-\r\n // hide-transition (the OverflowItem CSS toggles visibility rather\r\n // than display, so they occupy zero space but their bounding box can\r\n // briefly extend during the transition).\r\n toolbarOverflow: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n overflow: 'hidden',\r\n justifyContent: 'flex-end',\r\n },\r\n // Slot wrapper for the consumer's action-button cluster when it lives\r\n // inside an OverflowItem. Keeps the cluster's internal layout (gap +\r\n // alignment) intact even though the surrounding `<GridButtons>` div\r\n // is also a flex container — the OverflowItem treats this whole slot\r\n // as a single unit for overflow measurement.\r\n toolbarSlot: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n },\r\n // Plain `<div>` wrapper around each OverflowItem's content (refresh\r\n // button, settings popover, etc.). Necessary because OverflowItem's\r\n // ref-forwarding recursively unwraps FluentTrigger components\r\n // (Tooltip, Popover, MenuTrigger, etc.) until it lands on a non-\r\n // trigger element — and the `data-overflowing` attribute it toggles\r\n // for the hide-CSS rule lands on whatever the deepest unwrap is.\r\n // A plain div is NOT a trigger, so OverflowItem stops here, measures\r\n // this element's offsetWidth, and toggles `data-overflowing` here.\r\n // Fluent's `[overflowingItems] [data-overflowing] { display: none }`\r\n // then collapses the whole wrapper (including its complex Fluent\r\n // child) cleanly. Display: contents would also work conceptually but\r\n // breaks ResizeObserver measurement.\r\n toolbarItemWrapper: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n },\r\n // Same pattern as `toolbarItemWrapper` but specifically for the\r\n // SearchBox, so the OverflowItem measures the wrapper instead of\r\n // recursively unwrapping into the Tooltip → SearchBox tree. Inherits\r\n // flex display so the SearchBox's natural minWidth shapes the\r\n // wrapper width.\r\n searchBoxWrapper: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n },\r\n // Wrapper for a non-MenuItem control rendered inside the overflow\r\n // menu (search box, settings switch, action buttons cluster). Gives\r\n // the row consistent padding so it looks at home next to plain\r\n // MenuItems. `width: 100%` ensures the contained input / button bar\r\n // fills the menu's surface width.\r\n menuActionRow: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n paddingTop: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n paddingRight: tokens.spacingHorizontalM,\r\n width: '100%',\r\n },\r\n // SearchBox sized to fit comfortably inside the overflow menu's\r\n // surface — the inline toolbar SearchBox has its own `searchBox`\r\n // sizing that's tuned for the toolbar's narrow strip; the menu\r\n // version gets a wider min-width to feel like a proper search input\r\n // when surfaced from the \"...\" menu.\r\n menuSearchBox: {\r\n minWidth: '200px',\r\n width: '100%',\r\n },\r\n searchBox: {\r\n // Narrower than the original 240px so the SearchBox can shrink with\r\n // the toolbar before the Overflow algorithm hides it entirely. On\r\n // narrow viewports where 240px wouldn't fit, the SearchBox still\r\n // collapses into the overflow menu (where `menuSearchBox` gives it\r\n // the wider styling).\r\n minWidth: '160px',\r\n },\r\n // Horizontal flex container for the toolbar's action-button area.\r\n // Children sit at the right end of the toolbar after the search box,\r\n // mirroring Blazor's MenuBar slot. `flexShrink: 0` keeps the cluster\r\n // intact when the viewport narrows (the view picker / search box flex\r\n // first); the small gap matches Fluent's typical button-row spacing.\r\n buttonsBar: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n flexShrink: 0,\r\n columnGap: tokens.spacingHorizontalXS,\r\n },\r\n // Layout for the search-box tooltip. Two stacked sections (string-bucket\r\n // intro + columns, number-bucket intro + columns), each indented one\r\n // level under its header. Mirrors the Blazor tooltip's \"Apply begins\r\n // with… / Uses number comparisons…\" structure so the documentation feel\r\n // matches across both UIs.\r\n searchTooltipBody: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalM,\r\n maxWidth: '320px',\r\n },\r\n searchTooltipSection: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalXXS,\r\n },\r\n searchTooltipColumnList: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n paddingLeft: tokens.spacingHorizontalM,\r\n },\r\n // Table itself. `border-collapse: separate` + per-row borders gives\r\n // Fluent's \"cards stacked into a list\" feel without doubled lines at\r\n // every cell intersection.\r\n //\r\n // `tableLayout: 'fixed'` is required for the resize handles to feel\r\n // crisp: with `auto`, the browser fights user-set column widths\r\n // against content width and resize drags look laggy or unstable. With\r\n // `fixed`, each `<th>`'s `width` style is authoritative and body cells\r\n // simply ellipsis-overflow within it (already wired via\r\n // `bodyCell.maxWidth: 0`).\r\n //\r\n // No `width: 100%` — that combined with `table-layout: fixed` causes\r\n // the browser to redistribute leftover space (container width minus\r\n // sum of declared column widths) proportionally across all columns,\r\n // INCLUDING the 50px-locked select column. Even `<col width=\"50px\">`\r\n // and `max-width: 50px` on the cell aren't honored in that case\r\n // (long-standing Chromium quirk). Setting the table's width to the\r\n // exact total via inline style at render time eliminates the leftover\r\n // entirely, so the select column stays at exactly 50px. Tradeoff: a\r\n // grid narrower than its container leaves visible space on the right\r\n // instead of stretching.\r\n table: {\r\n borderCollapse: 'separate',\r\n borderSpacing: 0,\r\n tableLayout: 'fixed',\r\n },\r\n headerRow: {\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n },\r\n headerCell: {\r\n // `position: sticky` pins the header row to the top of the scroll\r\n // container (`tableContainer`) as the body scrolls beneath it.\r\n // Sticky must be on the `<th>` cells, not the `<thead>` / `<tr>` —\r\n // Chrome / Safari don't honor sticky on table-row-group elements.\r\n position: 'sticky',\r\n top: 0,\r\n // Above body cells (no z-index) but below the refetch scrim\r\n // (zIndex: 2) — so the scrim covers the header too while still\r\n // letting the resize handle inside the th catch pointer events.\r\n zIndex: 1,\r\n // Background MUST live on the th itself, not just the parent <tr>.\r\n // When a child is `position: sticky`, the parent row's background\r\n // paints at the row's natural scroll position — not at the sticky\r\n // offset — so the cell becomes transparent and body rows scroll\r\n // through visibly underneath. Painting the background on the th\r\n // travels with the cell to its sticky position and occludes the\r\n // rows beneath cleanly.\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n textAlign: 'left',\r\n fontWeight: tokens.fontWeightSemibold,\r\n color: tokens.colorNeutralForeground2,\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n paddingRight: tokens.spacingHorizontalM,\r\n // Use `box-shadow` instead of `border-bottom` for the divider: with\r\n // `position: sticky`, table cell borders don't render correctly\r\n // (they're collapsed away in some browsers, or attached to the\r\n // wrong row). An inset box-shadow at -1px draws an internal hairline\r\n // along the bottom edge that paints reliably across all browsers\r\n // regardless of the sticky positioning.\r\n boxShadow: `inset 0 -${tokens.strokeWidthThin} 0 ${tokens.colorNeutralStroke1}`,\r\n // Wrapping in the header keeps narrow columns from blowing out the\r\n // grid width when label text is long. Body cells handle their own\r\n // overflow per-renderer.\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n },\r\n // Sortable headers — pointer cursor + hover background to advertise\r\n // clickability. Non-sortable headers (File/Image columns) inherit the\r\n // base headerCell style only.\r\n headerCellSortable: {\r\n cursor: 'pointer',\r\n userSelect: 'none',\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground2Hover,\r\n },\r\n },\r\n // Inner flex layout for sortable headers: label on the left, sort\r\n // indicator on the right. Stays a single row even when the label\r\n // ellipsis-truncates.\r\n headerCellInner: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n maxWidth: '100%',\r\n },\r\n headerCellLabel: {\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n },\r\n sortIcon: {\r\n flex: '0 0 auto',\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n // Small numeric badge that shows the 1-based sort precedence when\r\n // multiple columns are active. Mirrors the position-indicator pattern\r\n // most data grids use (AG Grid, Material UI, etc.) — number alone is\r\n // enough, no need to ape Excel's \"↑1\" combined glyph.\r\n sortPosition: {\r\n flex: '0 0 auto',\r\n fontSize: tokens.fontSizeBase200,\r\n fontWeight: tokens.fontWeightSemibold,\r\n color: tokens.colorNeutralForeground3,\r\n minWidth: '12px',\r\n textAlign: 'center',\r\n },\r\n // Resize handle — a narrow vertical strip on the right edge of each\r\n // header cell. Invisible by default so it doesn't clutter the chrome;\r\n // a subtle accent line appears on hover or while actively dragging.\r\n // `touchAction: 'none'` is required to keep mobile-Safari from\r\n // scrolling the page while the user drags.\r\n resizeHandle: {\r\n position: 'absolute',\r\n right: 0,\r\n top: 0,\r\n bottom: 0,\r\n width: '5px',\r\n cursor: 'col-resize',\r\n userSelect: 'none',\r\n touchAction: 'none',\r\n backgroundColor: 'transparent',\r\n transitionProperty: 'background-color',\r\n transitionDuration: tokens.durationFaster,\r\n ':hover': {\r\n backgroundColor: tokens.colorBrandStroke1,\r\n },\r\n },\r\n // Drag-in-progress — keep the indicator visible even after the cursor\r\n // leaves the strict 5px hit-zone (the OS continues sending mousemove\r\n // events to the original target until mouseup).\r\n resizeHandleActive: {\r\n backgroundColor: tokens.colorBrandStroke1,\r\n },\r\n // Checkbox column — the column that holds row-selection toggles. Locked\r\n // to 50px with `width` + `min-width` + `max-width` all three together.\r\n // With `table-layout: fixed` and `width: 100%` on the table, declaring\r\n // only `width: 50px` isn't enough: when the sum of all column widths is\r\n // less than the table's rendered width, the browser distributes the\r\n // extra space across columns proportionally — including this one — and\r\n // the cell ends up rendering noticeably wider than 50px. `min-width` /\r\n // `max-width` clamp the cell so the proportional distribution can't\r\n // steal width into it; the slack lands on the data columns instead.\r\n selectCell: {\r\n width: '50px',\r\n minWidth: '50px',\r\n maxWidth: '50px',\r\n paddingTop: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n paddingLeft: tokens.spacingHorizontalS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n },\r\n // Pending-operation column. Same 50px clamp as `selectCell` — matches\r\n // the visual rhythm of the checkbox column to its left. Same\r\n // `table-layout: fixed` distribution caveat applies (the width triple\r\n // is what keeps the column from absorbing slack from the data\r\n // columns). Header is empty by design; the meaning lives in the per-\r\n // row icons.\r\n pendingCell: {\r\n width: '50px',\r\n minWidth: '50px',\r\n maxWidth: '50px',\r\n paddingTop: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n paddingLeft: tokens.spacingHorizontalXS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n textAlign: 'center',\r\n },\r\n // Operation icons. Brand-tinted to draw the eye but kept at icon-only\r\n // intensity — they're status indicators, not actions. `verticalAlign:\r\n // middle` aligns to the row's baseline rather than sitting on the\r\n // text descender line (which would push the icon below cell center).\r\n pendingIconCreate: {\r\n color: tokens.colorPaletteGreenForeground1,\r\n verticalAlign: 'middle',\r\n },\r\n pendingIconUpdate: {\r\n color: tokens.colorBrandForeground1,\r\n verticalAlign: 'middle',\r\n },\r\n // Toolbar pending-operation summary indicators (Plus/Pencil/Trash + a\r\n // count badge). Inline-flex so the icon and its badge stay tight; small\r\n // gap separates them without making the chip feel airy. Each indicator\r\n // is its own compact unit so multiple can sit side-by-side without\r\n // any custom spacing logic.\r\n pendingIndicator: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalXXS,\r\n paddingLeft: tokens.spacingHorizontalXS,\r\n paddingRight: tokens.spacingHorizontalXS,\r\n },\r\n // Row that's currently selected — accent-tinted background so it\r\n // stands out from hover. Same treatment Fluent's DataGrid uses.\r\n bodyRowSelected: {\r\n backgroundColor: tokens.colorNeutralBackground1Selected,\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground1Selected,\r\n },\r\n },\r\n bodyRow: {\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n ':hover': {\r\n backgroundColor: tokens.colorNeutralBackground1Hover,\r\n },\r\n },\r\n // Click-anywhere selection cursor feedback. Applied when\r\n // `selectFromEntireRow` is on so the user sees the row as a click target.\r\n // Interactive children (links, buttons) override this via their own\r\n // cursor in the cascade.\r\n bodyRowClickable: {\r\n cursor: 'pointer',\r\n },\r\n // Applied when a shared `ViewDataSource.highlight` is set and the\r\n // row's column value doesn't match — drives the \"highlight + dim\"\r\n // chart cross-filter UX. The row data, selection, and pending\r\n // operations are unaffected; only visual opacity changes so the\r\n // user can see the full context with the matching subset\r\n // emphasized. Cleared by `setHighlight(undefined)` on the\r\n // datasource.\r\n bodyRowMuted: {\r\n opacity: 0.4,\r\n },\r\n // Sentinel rendered after the last body row in virtualize mode.\r\n // IntersectionObserver watches this against the scroll container; when\r\n // it enters view, the next page loads. Height + padding give the\r\n // observer a reasonable target box; the centered spinner indicates\r\n // \"loading the next page\" without a layout shift on the rows above.\r\n loadMoreSentinel: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n paddingTop: tokens.spacingVerticalM,\r\n paddingBottom: tokens.spacingVerticalM,\r\n minHeight: '32px',\r\n },\r\n // Primary-name cell rendered as a clickable link — fires the grid's\r\n // registered edit handler. Mirrors Blazor's `.ppp-grid-primary-name-link`:\r\n // accent foreground, no underline at rest, underline on hover/focus.\r\n // Inherits the surrounding cell's color when there's nothing to\r\n // navigate to (the cell falls back to plain text in that case, so\r\n // there's no styled-disabled state to worry about).\r\n primaryNameLink: {\r\n color: tokens.colorBrandForegroundLink,\r\n textDecorationLine: 'none',\r\n cursor: 'pointer',\r\n ':hover': {\r\n color: tokens.colorBrandForegroundLinkHover,\r\n textDecorationLine: 'underline',\r\n },\r\n ':focus-visible': {\r\n color: tokens.colorBrandForegroundLinkHover,\r\n textDecorationLine: 'underline',\r\n outlineStyle: 'none',\r\n },\r\n },\r\n // Subtle striping. Off by default in this v1 — toggle by setting a\r\n // `data-striped=\"true\"` attribute on the wrapper in a future phase.\r\n bodyCell: {\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n paddingRight: tokens.spacingHorizontalM,\r\n borderBottomWidth: tokens.strokeWidthThin,\r\n borderBottomStyle: 'solid',\r\n borderBottomColor: tokens.colorNeutralStroke2,\r\n verticalAlign: 'middle',\r\n // Single-line cells with ellipsis. Wrapping rows would be a per-\r\n // column opt-in (e.g. Memo) handled by the cell renderer setting\r\n // its own `white-space: normal` on its content.\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n maxWidth: 0,\r\n },\r\n // Inline-edit cell wrapper. Constrains the embedded editor to the\r\n // column's actual width — without this, Fluent v9's <Input> /\r\n // <Combobox> / <Dropdown> outer slots carry a default min-width\r\n // (~200px on `.fui-Input`, similar on the others) that overflows\r\n // narrow columns and visually leaks into the neighbour cell.\r\n //\r\n // Fluent's atomic-CSS rules can land in either order vs ours, so\r\n // `!important` is the reliable way to win specificity against the\r\n // outer-slot rule. The descendant cascade (`& *`) covers Field,\r\n // Input wrapper, Combobox button, Dropdown trigger, etc. without\r\n // having to enumerate Fluent's per-component class names — the\r\n // worst that happens elsewhere is that an inner intentional\r\n // min-width inside a dropdown listbox flattens, which is harmless\r\n // because the listbox is portaled out of the cell anyway.\r\n inlineEditCell: {\r\n width: '100%',\r\n minWidth: 0,\r\n // Direct children get width: 100% too so the typed edit's\r\n // <Field> stretches to fill the cell instead of shrink-wrapping.\r\n '& > *': {\r\n width: '100%',\r\n },\r\n '& *': {\r\n minWidth: '0 !important',\r\n },\r\n },\r\n // Scroll container for the table. Fills the bodyArea (`flex: 1`) and\r\n // shows scrollbars on demand — `overflow: auto` triggers vertical\r\n // scrolling when rows overflow the height, horizontal scrolling when\r\n // user-resized columns push the table wider than the container. The\r\n // sticky `<thead>` cells stay locked at the top of THIS box (not the\r\n // viewport) while the body scrolls beneath them. `min-height: 0` is\r\n // again the critical bit for the flex-child overflow trick to work.\r\n tableContainer: {\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n // The element that actually owns the horizontal scrollbar — for that to\r\n // engage, the container must be ALLOWED to be narrower than the table\r\n // inside it. Without `min-width: 0`, `min-width: auto` resolves to the\r\n // table's pixel width as content min-content, the container grows to\r\n // match, and `overflow: auto` never triggers — the table just escapes\r\n // its ancestors instead.\r\n minWidth: 0,\r\n overflow: 'auto',\r\n },\r\n // Translucent backdrop drawn over the existing rows while a deps-change\r\n // refetch is in flight (new page / sort / search / view). Mirrors the\r\n // Blazor `OverlayWithProgress` look so the visual feel is consistent\r\n // across both UIs — a theme-aware neutral tint (`colorNeutralBackground2`,\r\n // equivalent to Blazor's `--neutral-layer-2`) at 0.7 opacity drawn via a\r\n // `::before` pseudo-element so the spinner above stays at full opacity.\r\n // Light + readable in light theme, dark + readable in dark theme; the\r\n // token swaps automatically with FluentDesignTheme without any\r\n // additional rules. `pointerEvents: 'auto'` blocks accidental clicks on\r\n // the dimmed rows; the toolbar above stays interactive.\r\n //\r\n // Mounted as a sibling of `tableContainer` (inside `bodyArea`, which is\r\n // `position: relative`), NOT inside the scroll container — so the\r\n // scrim stays anchored to the viewport of the body area as the table\r\n // scrolls under it. `zIndex: 2` keeps it above the sticky header\r\n // (which sits at zIndex 1 inside its own scroll context).\r\n refetchScrim: {\r\n position: 'absolute',\r\n inset: 0,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n pointerEvents: 'auto',\r\n zIndex: 2,\r\n '::before': {\r\n content: '\"\"',\r\n position: 'absolute',\r\n inset: 0,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n opacity: 0.7,\r\n zIndex: -1,\r\n },\r\n },\r\n // Centered status surface that fills `bodyArea` when the grid has no\r\n // table to show — cold-start loading (no columns resolved yet) or an\r\n // error response. Replaces `tableContainer` in those states rather\r\n // than rendering alongside it.\r\n statusFill: {\r\n flex: '1 1 auto',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: tokens.spacingVerticalL,\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n statusFillError: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n // \"No records found\" row when a successful response has zero rows.\r\n // Lives INSIDE `tableContainer` so the column headers above it stay\r\n // sticky / scroll horizontally with the empty grid. Padding gives it\r\n // breathing room from the header divider.\r\n emptyStateRow: {\r\n display: 'flex',\r\n justifyContent: 'center',\r\n padding: tokens.spacingVerticalL,\r\n color: tokens.colorNeutralForeground3,\r\n },\r\n // Paging strip at the bottom of the grid. Renders inside the\r\n // `<Footer>` layout component, which paints the background tint +\r\n // top-border divider (mirroring Blazor's grid where FluentGridBase\r\n // renders a `<Footer>` in its `grid-area: footer` slot). This style\r\n // only handles the inner flex layout: page-size group on the left,\r\n // a flex spacer, then record count + nav buttons on the right.\r\n // Locked at the bottom (`flexShrink: 0`) so it stays visible even\r\n // when the body area is tall enough to require vertical scrolling.\r\n // Padding is tightened from the `<Footer>` default (`spacingHorizontalM`)\r\n // to `spacingHorizontalS` for visual parity with the grid `toolbar`.\r\n pagingRow: {\r\n flexShrink: 0,\r\n display: 'flex',\r\n alignItems: 'center',\r\n flexWrap: 'wrap',\r\n gap: tokens.spacingHorizontalM,\r\n paddingLeft: tokens.spacingHorizontalS,\r\n paddingRight: tokens.spacingHorizontalS,\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n pagingPageSizeGroup: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n pagingPageSizeLabel: {\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n // Tight dropdown — page-size options are 2-3 digits so the field\r\n // doesn't need the default 240px+ minWidth Fluent's Dropdown picks.\r\n pagingPageSizeDropdown: {\r\n minWidth: '80px',\r\n },\r\n pagingSpacer: {\r\n flex: '1 1 auto',\r\n },\r\n pagingNav: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalXS,\r\n },\r\n pagingCount: {\r\n fontVariantNumeric: 'tabular-nums',\r\n },\r\n});\r\n","import { Button, Dropdown, mergeClasses, Option } from '@fluentui/react-components';\r\nimport {\r\n ChevronDoubleLeft20Regular,\r\n ChevronDoubleRight20Regular,\r\n ChevronLeft20Regular,\r\n ChevronRight20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { Footer } from '../layout/footer.js';\r\nimport { useMainGridStyles } from './main-grid-styles.js';\r\n\r\n/**\r\n * Footer paging strip for {@link MainGrid}, modeled after Blazor's\r\n * `FluentGridBase` footer: page-size selector on the left, record count +\r\n * First/Prev/Page-X-of-Y/Next/Last on the right. The page-size selector\r\n * hides at narrow viewports — `MainGrid` reroutes it into the toolbar's\r\n * Settings popover via the {@link showPageSize} prop so phones get a\r\n * clean bottom strip with just the nav controls.\r\n *\r\n * Stateless — the grid owns `page` / `pageSize`; this component only\r\n * renders the controls and reports user intent through the two callbacks.\r\n * Total record count comes from the server's `pagingInfo.totalRecordCount`\r\n * (always populated now — `FetchXmlQueryComposer` sets\r\n * `returntotalrecordcount` on every non-aggregate query); when it's\r\n * unknown we degrade to \"Page X\" + a heuristic Next-enabled flag from the\r\n * current page's row count.\r\n */\r\nexport interface MainGridPagingProps {\r\n page: number;\r\n pageSize: number;\r\n /**\r\n * Total records that match the current filter+search. `undefined` when\r\n * the server didn't include it (aggregate views, transient errors).\r\n */\r\n totalCount: number | undefined;\r\n /**\r\n * Row count on the currently-displayed page. Used to derive\r\n * \"is there probably a next page?\" when {@link totalCount} is missing —\r\n * a full page is the heuristic for \"more might exist.\"\r\n */\r\n dataLength: number;\r\n onPageChange: (page: number) => void;\r\n onPageSizeChange: (pageSize: number) => void;\r\n /**\r\n * Options for the page-size dropdown. Defaults to `[10, 20, 50, 100]`\r\n * upstream; the component itself doesn't fill in defaults so the grid\r\n * can keep one source of truth.\r\n */\r\n pageSizes: readonly number[];\r\n /**\r\n * Whether to render the page-size selector inline in the footer.\r\n * `false` hides it (typically because the parent grid is at a narrow\r\n * viewport and has reparented the same control into its Settings\r\n * popover instead). The rest of the paging row still renders.\r\n */\r\n showPageSize: boolean;\r\n}\r\n\r\nexport function MainGridPaging({\r\n page,\r\n pageSize,\r\n totalCount,\r\n dataLength,\r\n onPageChange,\r\n onPageSizeChange,\r\n pageSizes,\r\n showPageSize,\r\n}: MainGridPagingProps) {\r\n const styles = useMainGridStyles();\r\n const t = useT();\r\n const pageSizeLabel = t('app.components.grid.page-size');\r\n\r\n const totalPages =\r\n totalCount !== undefined && totalCount > 0\r\n ? Math.max(1, Math.ceil(totalCount / pageSize))\r\n : undefined;\r\n const canPrev = page > 1;\r\n // When totalCount is known, Next is enabled only while we're not on the\r\n // last page. When it isn't, a full page is the heuristic for \"probably\r\n // more exist\" — matches the old inline behavior.\r\n const canNext = totalPages !== undefined ? page < totalPages : dataLength === pageSize;\r\n const canFirst = canPrev;\r\n const canLast = totalPages !== undefined && page < totalPages;\r\n\r\n return (\r\n <Footer className={mergeClasses('ppp-grid-paging', styles.pagingRow)}>\r\n {showPageSize && (\r\n <div className={styles.pagingPageSizeGroup}>\r\n <span className={styles.pagingPageSizeLabel}>{pageSizeLabel}</span>\r\n <Dropdown\r\n className={styles.pagingPageSizeDropdown}\r\n aria-label={pageSizeLabel}\r\n value={String(pageSize)}\r\n selectedOptions={[String(pageSize)]}\r\n onOptionSelect={(_, data) => {\r\n if (data.optionValue) {\r\n const next = Number(data.optionValue);\r\n if (Number.isFinite(next) && next > 0) onPageSizeChange(next);\r\n }\r\n }}\r\n >\r\n {pageSizes.map((size) => (\r\n <Option key={size} value={String(size)} text={String(size)}>\r\n {String(size)}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </div>\r\n )}\r\n <div className={styles.pagingSpacer} />\r\n {totalCount !== undefined && (\r\n // Singular/plural collapsed into Blazor's \"{0} items\" form so\r\n // both UIs share one localization key. Slight grammatical\r\n // oddity at count=1 (\"1 items\") matches Blazor exactly.\r\n <span className={styles.pagingCount}>\r\n {t('app.components.grid.record-count', [totalCount])}\r\n </span>\r\n )}\r\n <div className={styles.pagingNav}>\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ChevronDoubleLeft20Regular />}\r\n aria-label={t('app.components.grid.first-page')}\r\n disabled={!canFirst}\r\n onClick={() => onPageChange(1)}\r\n />\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ChevronLeft20Regular />}\r\n aria-label={t('app.components.grid.previous-page')}\r\n disabled={!canPrev}\r\n onClick={() => onPageChange(Math.max(1, page - 1))}\r\n />\r\n <span className={styles.pagingCount}>\r\n {totalPages !== undefined\r\n ? t('app.components.grid.paging-text', [page, totalPages])\r\n : t('app.components.grid.paging-text-no-total', [page])}\r\n </span>\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ChevronRight20Regular />}\r\n aria-label={t('app.components.grid.next-page')}\r\n disabled={!canNext}\r\n onClick={() => onPageChange(page + 1)}\r\n />\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ChevronDoubleRight20Regular />}\r\n aria-label={t('app.components.grid.last-page')}\r\n disabled={!canLast}\r\n onClick={() => {\r\n if (totalPages !== undefined) onPageChange(totalPages);\r\n }}\r\n />\r\n </div>\r\n </Footer>\r\n );\r\n}\r\n","import type { ColumnSort } from '@powerportalspro/core';\r\n\r\n/**\r\n * Wire shape for `<MainGrid persistedStateQueryParameter>`. Stored as a\r\n * JSON-stringified object in a single URL query parameter; field names are\r\n * deliberately short (`v` / `p` / `ps` / `s` / `q`) so the encoded blob\r\n * stays compact when round-tripped through bookmarks and shared links.\r\n *\r\n * Kept in lockstep with the Blazor `PersistedGridState` shape so a URL\r\n * produced by one stack is consumable by the other — same param name, same\r\n * keys, same value semantics. Update both sides together.\r\n */\r\nexport interface PersistedGridState {\r\n /** View id (Dataverse GUID, hyphenated, no braces). */\r\n v?: string;\r\n /** 1-based page number. Omitted when at page 1. */\r\n p?: number;\r\n /** Page size (rows per page). Omitted when matching the grid's default. */\r\n ps?: number;\r\n /** Sort directives in precedence order. Omitted when no user-driven sort is applied. */\r\n s?: PersistedSort[];\r\n /**\r\n * Active search text. Only included when the grid was configured with\r\n * `includeSearchInPersistedState={true}` AND the box has a non-empty value.\r\n */\r\n q?: string;\r\n}\r\n\r\nexport interface PersistedSort {\r\n /** Column logical name (matches `ColumnSort.columnName`). */\r\n c: string;\r\n /** `true` for descending, omitted/false for ascending. */\r\n d?: boolean;\r\n}\r\n\r\n/**\r\n * Reads the named query parameter out of `window.location.search` and parses\r\n * it as a {@link PersistedGridState}. Returns `null` when the parameter is\r\n * missing, empty, or not valid JSON — the grid falls back to its built-in\r\n * defaults in those cases (same as if the prop wasn't set at all).\r\n *\r\n * Safe to call in SSR / non-browser environments — the `window` guard\r\n * short-circuits before touching the location.\r\n */\r\nexport function readPersistedGridStateFromUrl(\r\n queryParameterName: string | undefined,\r\n): PersistedGridState | null {\r\n if (!queryParameterName) return null;\r\n if (typeof window === 'undefined') return null;\r\n const raw = new URLSearchParams(window.location.search).get(queryParameterName);\r\n if (!raw) return null;\r\n try {\r\n const parsed = JSON.parse(raw) as unknown;\r\n if (parsed && typeof parsed === 'object') {\r\n return parsed as PersistedGridState;\r\n }\r\n return null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Builds the canonical encoded form of a {@link PersistedGridState}. Strips\r\n * fields that match their natural defaults (page 1, empty sort, empty\r\n * search) so the URL stays as short as possible — there's no point in\r\n * `{\"p\":1}` cluttering the bar.\r\n */\r\nexport function encodePersistedGridState(state: PersistedGridState): string {\r\n const compact: PersistedGridState = {};\r\n if (state.v) compact.v = state.v;\r\n if (state.p !== undefined && state.p > 1) compact.p = state.p;\r\n if (state.ps !== undefined) compact.ps = state.ps;\r\n if (state.s && state.s.length > 0) compact.s = state.s;\r\n if (state.q && state.q.length > 0) compact.q = state.q;\r\n return JSON.stringify(compact);\r\n}\r\n\r\n/**\r\n * Serializes a runtime {@link ColumnSort} list into the {@link PersistedSort}\r\n * shape used in the URL. Omits the `d` field for ascending entries (the\r\n * common case) so the encoded blob stays compact.\r\n */\r\nexport function serializeSorts(sorts: readonly ColumnSort[]): PersistedSort[] {\r\n return sorts.map((s) => (s.descending ? { c: s.columnName, d: true } : { c: s.columnName }));\r\n}\r\n\r\n/**\r\n * Inverse of {@link serializeSorts}. Trusts the URL shape minimally — an\r\n * entry without a `c` field is dropped rather than producing an\r\n * unsortable empty-name `ColumnSort`.\r\n */\r\nexport function deserializeSorts(sorts: readonly PersistedSort[] | undefined): ColumnSort[] {\r\n if (!sorts) return [];\r\n return sorts\r\n .filter((s) => typeof s?.c === 'string' && s.c.length > 0)\r\n .map((s) => (s.d ? { columnName: s.c, descending: true } : { columnName: s.c }));\r\n}\r\n\r\n/**\r\n * Writes the encoded state to the named query parameter via\r\n * `history.replaceState` so the back button doesn't accumulate one entry\r\n * per state change. No-op outside the browser, when the param name is\r\n * empty, or when the URL already carries the same value (avoids redundant\r\n * history writes).\r\n */\r\nexport function writePersistedGridStateToUrl(\r\n queryParameterName: string | undefined,\r\n state: PersistedGridState,\r\n): void {\r\n if (!queryParameterName) return;\r\n if (typeof window === 'undefined') return;\r\n const encoded = encodePersistedGridState(state);\r\n const params = new URLSearchParams(window.location.search);\r\n if (params.get(queryParameterName) === encoded) return;\r\n params.set(queryParameterName, encoded);\r\n const next = `${window.location.pathname}?${params.toString()}${window.location.hash}`;\r\n window.history.replaceState(window.history.state, '', next);\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\n\r\ntype ViewMetadata = components['schemas']['ViewMetadata'];\r\ntype ViewColumn = components['schemas']['ViewColumn'];\r\n\r\n/**\r\n * Paging strategy used by the grid. Mirrors Blazor's `GridPagingMode` —\r\n * kept in lockstep with that source of truth at\r\n * `src/PowerPortalsPro.Web.Blazor.FluentUI/Components/Grids/FluentGridBase.razor.cs`\r\n * (search for `enum GridPagingMode`). When the Blazor enum gains a new\r\n * entry, add the matching key here.\r\n */\r\nexport const PagingMode = {\r\n /** Classic pager — Prev / Next / page-size selector at the bottom. */\r\n Paged: 'paged',\r\n /**\r\n * Infinite scroll. The pager footer is hidden, rows accumulate across page\r\n * boundaries, and a new page is fetched automatically when the user\r\n * scrolls past the rendered bottom. Use for long lists where the user\r\n * scans rather than navigates by page.\r\n */\r\n Virtualize: 'virtualize',\r\n} as const;\r\nexport type PagingMode = (typeof PagingMode)[keyof typeof PagingMode];\r\n\r\n/**\r\n * Sort order applied to the view-picker dropdown options. Mirrors Blazor's\r\n * `ViewSort` enum — kept in lockstep with that source of truth at\r\n * `src/PowerPortalsPro.Web.Blazor/Components/Grids/GridBase.razor.cs`\r\n * (search for `enum ViewSort`). When the Blazor enum gains a new entry,\r\n * add the matching key here.\r\n */\r\nexport const ViewSort = {\r\n /** Server-returned order, plus custom views appended in declaration order. */\r\n Default: 'default',\r\n /** Localized display name, ascending (the framework default). */\r\n NameAscending: 'nameAscending',\r\n /** Localized display name, descending. */\r\n NameDescending: 'nameDescending',\r\n} as const;\r\nexport type ViewSort = (typeof ViewSort)[keyof typeof ViewSort];\r\n\r\n/**\r\n * Consumer-supplied in-memory view, mixed into the view-picker dropdown\r\n * alongside Dataverse-resolved views. Mirrors Blazor's `GridViewDefinition`\r\n * shape — the minimum a grid needs to render a view without a Dataverse\r\n * `savedquery`/`userquery` round-trip.\r\n *\r\n * Custom views are submitted to `POST /api/grids/data` as inline `fetchXml`\r\n * rather than `viewId`, so the server takes the consumer-supplied FetchXML\r\n * verbatim (still wraps it through `IFetchXmlQueryComposer` for search,\r\n * sort, filter, paging — same as the view-id path).\r\n *\r\n * Source of truth: kept in lockstep (by hand, not codegen) with Blazor's\r\n * `GridViewDefinition` at\r\n * `src/PowerPortalsPro.Common/Models/GridViewDefinition.cs`. When that\r\n * record gains / drops fields a consumer would set, mirror the change here.\r\n * The generated `ViewMetadata` TS type is intentionally NOT reused — it\r\n * includes Dataverse-internal fields (`isDefault`, `quickFindCompatible`,\r\n * `userDefined`, `type`) that don't belong on the consumer-facing surface.\r\n */\r\nexport interface CustomViewDefinition {\r\n /**\r\n * Unique id for this view. Use a random GUID; must NOT collide with any\r\n * Dataverse view id (the grid keys the dropdown selection by this).\r\n */\r\n id: string;\r\n /** FetchXML query the grid will execute when this view is active. */\r\n fetchXml: string;\r\n /** Logical name of the table this view queries. */\r\n tableName: string;\r\n /** Columns to render, in display order, with optional widths. */\r\n columns: readonly ViewColumn[];\r\n /**\r\n * Optional display name shown in the dropdown. Defaults to the view id\r\n * when unset — set this for human-readable picker labels.\r\n */\r\n displayName?: string;\r\n}\r\n\r\n/**\r\n * Promotes a {@link CustomViewDefinition} into a `ViewMetadata`-shaped record\r\n * so it can ride the same internal \"views list\" as server-returned views.\r\n * The framework-only fields (`isDefault`, `quickFindCompatible`, `userDefined`,\r\n * `type`, `name`) are filled with sensible inert defaults — they're not used\r\n * for custom views (no Dataverse roundtrip ever consumes them).\r\n */\r\nexport function customToViewMetadata(custom: CustomViewDefinition): ViewMetadata {\r\n return {\r\n id: custom.id,\r\n fetchXml: custom.fetchXml,\r\n tableName: custom.tableName,\r\n columns: [...custom.columns],\r\n displayName: custom.displayName ?? null,\r\n name: custom.displayName ?? custom.id,\r\n isDefault: false,\r\n quickFindCompatible: false,\r\n userDefined: true,\r\n };\r\n}\r\n\r\n/**\r\n * Cheap identity check — custom views are tagged in the `views` list by\r\n * presence in the `customViewDefinitions` set. The grid uses this to decide\r\n * whether to send `viewId` (server-resolved) or `fetchXml` (inline) when\r\n * fetching grid data.\r\n */\r\nexport function isCustomViewId(\r\n viewId: string | undefined,\r\n customViewDefinitions: readonly CustomViewDefinition[] | undefined,\r\n): boolean {\r\n if (!viewId || !customViewDefinitions) return false;\r\n for (const v of customViewDefinitions) {\r\n if (v.id === viewId) return true;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Applies the configured {@link ViewSort} to a views list. Pure — returns a\r\n * new array, leaves the input untouched. The \"default\" mode keeps the\r\n * server's returned order (Dataverse-side ordering), then appends the\r\n * consumer's custom views in declaration order — `customToViewMetadata`\r\n * is called before this so both kinds live in the same array.\r\n */\r\nexport function sortViews(views: readonly ViewMetadata[], mode: ViewSort): ViewMetadata[] {\r\n if (mode === ViewSort.Default) return [...views];\r\n const sorted = [...views];\r\n sorted.sort((a, b) => {\r\n const labelA = a.displayName ?? a.name ?? '';\r\n const labelB = b.displayName ?? b.name ?? '';\r\n return mode === ViewSort.NameAscending\r\n ? labelA.localeCompare(labelB)\r\n : labelB.localeCompare(labelA);\r\n });\r\n return sorted;\r\n}\r\n\r\n/**\r\n * Composes the final views-list the grid renders + selects from:\r\n *\r\n * 1. Concat Dataverse views with custom views (custom appended).\r\n * 2. Filter by {@link MainGridProps.viewIds} whitelist when present.\r\n * 3. Sort per {@link MainGridProps.viewSort}.\r\n *\r\n * Returned array is referentially-fresh on every input change — callers\r\n * memoize externally if they need stable identity for effect deps.\r\n */\r\nexport function composeViewsList(args: {\r\n dataverseViews: readonly ViewMetadata[];\r\n customViewDefinitions: readonly CustomViewDefinition[] | undefined;\r\n viewIds: readonly string[] | undefined;\r\n viewSort: ViewSort;\r\n}): ViewMetadata[] {\r\n const customs = (args.customViewDefinitions ?? []).map(customToViewMetadata);\r\n const all = [...args.dataverseViews, ...customs];\r\n const filtered = args.viewIds && args.viewIds.length > 0\r\n ? all.filter((v) => args.viewIds!.includes(v.id))\r\n : all;\r\n return sortViews(filtered, args.viewSort);\r\n}\r\n","import {\n Button,\n Checkbox,\n CounterBadge,\n Dropdown,\n MenuItem,\n mergeClasses,\n Option,\n Overflow,\n OverflowItem,\n Popover,\n PopoverSurface,\n PopoverTrigger,\n Radio,\n SearchBox,\n Spinner,\n Switch,\n Text,\n Tooltip,\n useId,\n} from '@fluentui/react-components';\nimport {\n Add16Regular,\n ArrowClockwise20Regular,\n ArrowClockwise24Regular,\n ArrowSortDown16Regular,\n ArrowSortUp16Regular,\n Delete16Regular,\n Edit16Regular,\n LinkAdd16Regular,\n LinkDismiss16Regular,\n Search20Regular,\n Settings20Regular,\n} from '@fluentui/react-icons';\nimport { getRecordReferenceId } from '@powerportalspro/core';\nimport type {\n AssociateRequest,\n ColumnSort,\n components,\n CreateRequest,\n DeleteRequest,\n DisassociateRequest,\n GridDataRequest,\n OrganizationRequest,\n ResolvedColumn,\n TableRecordReference,\n UpdateRequest,\n} from '@powerportalspro/core';\nimport {\n MainContext,\n QueryStatus,\n RecordContext,\n useGridData,\n useMainContext,\n useRecordContext,\n useT,\n useTableMetadata,\n useViewMetadata,\n useViewsForTable,\n useWarnAboutUnsavedChanges,\n type ViewDataSource,\n} from '@powerportalspro/react';\nimport {\n createColumnHelper,\n getCoreRowModel,\n useReactTable,\n type ColumnSizingState,\n type RowSelectionState,\n} from '@tanstack/react-table';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useTransition,\n type CSSProperties,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport {\n getCellRendererByType,\n getRendererTypeForCode,\n type CellRendererType,\n type GridColumnCellRenderer,\n type GridTemplateColumnCellRendererProps,\n type GridTemplateColumnEditRenderer,\n type GridTemplateColumnEditRendererProps,\n} from './cells/cell-dispatcher.js';\nimport { ColumnEdit, getEditComponentForType } from '../edits/column-edit.js';\nimport { DescriptionLocation } from '../edits/base/column-edit-types.js';\nimport { readColumnRawValue } from '../edits/base/column-display.js';\nimport {\n GridContextProvider,\n type GridContextValue,\n type PendingRowCreate,\n type PendingRowUpdate,\n} from './grid-context.js';\nimport {\n extractGridColumnOverrides,\n type GridColumnAlign,\n type GridColumnProps,\n type GridTemplateColumnProps,\n} from './grid-column.js';\nimport {\n GridToolbarOverflowId,\n GridToolbarOverflowMenu,\n OverflowMenuItem,\n} from './grid-toolbar-overflow.js';\nimport { useIsNarrowViewport } from './use-is-narrow-viewport.js';\nimport { MainGridPaging } from './main-grid-paging.js';\nimport { useMainGridStyles } from './main-grid-styles.js';\nimport {\n deserializeSorts,\n readPersistedGridStateFromUrl,\n serializeSorts,\n writePersistedGridStateToUrl,\n type PersistedGridState,\n} from './persisted-grid-state.js';\nimport {\n composeViewsList,\n customToViewMetadata,\n isCustomViewId,\n PagingMode,\n ViewSort,\n type CustomViewDefinition,\n} from './view-options.js';\n\n/**\n * Debounce window between the last keystroke in the search box and the\n * actual server request. Matches LookupEdit's 300 ms — long enough to\n * dedupe rapid typing, short enough that results feel live.\n */\nconst SEARCH_DEBOUNCE_MS = 300;\n\n/**\n * Parses a CSS pixel-length string like `\"240px\"` back to its integer\n * pixel value. Returns `undefined` for any other CSS unit (`%`, `em`,\n * `auto`, `vw`) or a malformed value — the caller falls back to letting\n * the header `<th style={{ width }}>` path apply the raw string via\n * `table-layout: fixed`.\n */\nfunction parsePixelWidth(value: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed.toLowerCase().endsWith('px')) return undefined;\n const num = Number(trimmed.slice(0, -2));\n return Number.isFinite(num) ? num : undefined;\n}\n\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\ntype TableRecord = components['schemas']['TableRecord'];\ntype ViewMetadata = components['schemas']['ViewMetadata'];\n\n/**\n * Phase-1 read-only MainGrid. Loads a view (by id or first public view of a\n * table), fetches records via FetchXML, and renders them in a Fluent-styled\n * table powered by `@tanstack/react-table`'s headless core. View switching,\n * sorting, search, selection, inline edit, and toolbar buttons all land in\n * later phases — the shape of this component is intentionally narrow so\n * future props can grow it without breaking consumers.\n *\n * Consumer surface (Phase 1):\n * - One of `viewId` or `tableName` (viewId wins when both are set).\n * - `pageSize` (default 25), `className`, `style`.\n *\n * Data flow:\n * 1. Resolve the active viewId — explicit prop, or first public view for\n * `tableName`.\n * 2. Fetch view metadata (FetchXML + view columns).\n * 3. Fetch table metadata in parallel (per-column metadata for renderers).\n * 4. Inject `page` + `count` into the view's FetchXML and execute.\n * 5. Hand the result to TanStack + the type-dispatched cell renderers.\n */\nexport interface MainGridProps {\n /**\n * Logical name of the table to load. When set without {@link viewId},\n * the first view returned for the table is used as the default view.\n * Phase 2 will surface a view picker; for now consumers who need a\n * specific view should pass {@link viewId} directly.\n */\n tableName?: string;\n /**\n * Explicit view id (a Dataverse view GUID). Takes precedence over\n * {@link tableName} when both are set.\n */\n viewId?: string;\n /**\n * Default view id used when the grid loads with multiple views to choose\n * from and no persisted URL state pins one. Ignored when {@link viewId}\n * is set (the grid is hard-pinned in that case).\n */\n defaultViewId?: string;\n /**\n * Whitelist of view ids the picker dropdown should expose. When set, only\n * matching views (Dataverse-resolved AND\n * {@link customViewDefinitions}-supplied) appear in the dropdown — the\n * rest are hidden from the user but stay resolvable. Empty / undefined\n * means \"show every view returned by `useViewsForTable` plus every\n * custom view\".\n */\n viewIds?: readonly string[];\n /**\n * Consumer-supplied in-memory views, appended to the Dataverse-resolved\n * list. Each entry carries its own FetchXML, table name, and columns so\n * the grid can render it without a `savedquery` lookup — handy for\n * \"ad-hoc\" views like \"My Open Cases\" that don't warrant a Dataverse\n * record.\n *\n * Custom views are submitted to the grid-data endpoint as inline\n * `fetchXml` rather than `viewId`, so the server takes the consumer's\n * FetchXML verbatim through the same query-composer pipeline.\n */\n customViewDefinitions?: readonly CustomViewDefinition[];\n /**\n * Sort order applied to the view picker's dropdown. Defaults to\n * `ViewSort.NameAscending`. Use `ViewSort.Default` to preserve the\n * server-returned order (with custom views appended in the order they\n * were declared).\n */\n viewSort?: ViewSort;\n /**\n * Optional async hook that runs immediately after a view is loaded and\n * before the grid uses it to build columns or queries. Return a modified\n * view to transform what the grid ultimately renders — e.g. to force a\n * column into every view regardless of its stored configuration, or to\n * inject filter conditions into the FetchXML at runtime.\n *\n * When set, the grid always submits the transformed view's FetchXML\n * inline (instead of `viewId`) so the server consumes the post-transform\n * shape. Return a new object — never mutate the input — since the\n * upstream view metadata is cached across grid instances.\n */\n transformViewAsync?: (view: ViewMetadata) => Promise<ViewMetadata>;\n /**\n * When `true` (default), the grid renders an outer border around the\n * table + toolbar + footer. Set `false` for embedded contexts where the\n * surrounding layout already provides framing (e.g. a card with its own\n * border).\n */\n borderVisible?: boolean;\n /**\n * When `true` (default), clicking anywhere on a row toggles its\n * selection — not just the checkbox column. Clicks on interactive cell\n * content (links, buttons, inputs) still get their own handlers; the\n * row-level toggle only fires when the click lands on a non-interactive\n * cell. Has no effect when {@link selectionMode} is `'none'`.\n */\n selectFromEntireRow?: boolean;\n /**\n * Initial records per page — seeds the page-size dropdown. Defaults to\n * `50`. After mount, the user can change it via the footer's page-size\n * selector unless {@link allowChangingPageSize} is `false`. Prev / Next\n * traversal uses traditional FetchXML page-number paging; the server's\n * `pagingInfo.totalRecordCount` drives the disabled state of Next on\n * the last page.\n */\n pageSize?: number;\n /**\n * Options offered by the footer's page-size selector. Defaults to\n * `[10, 20, 50, 100]`. The current {@link pageSize} doesn't have to\n * appear in this list (the dropdown still renders the chosen value),\n * but it's clearest when it does.\n */\n pageSizes?: readonly number[];\n /**\n * Paging strategy. Defaults to `'paged'` (classic Prev/Next pager).\n * Set to `'virtualize'` for infinite-scroll — the pager footer is\n * hidden, rows accumulate across page boundaries, and the next page\n * fetches automatically when the user scrolls past the rendered\n * bottom.\n */\n pagingMode?: PagingMode;\n /**\n * When `true`, the grid renders an \"Edit\" toggle in the toolbar that\n * flips the grid into inline-edit mode. Defaults to `false`. Has no\n * effect when {@link editable} is supplied (controlled mode) — pass\n * your own toggle UI in that case.\n */\n allowEdit?: boolean;\n /**\n * Whether the grid is currently in inline-edit mode. When `true`, body\n * cells render as `<ColumnEdit>` instead of read-only text — the user\n * can type / pick directly in the grid. Each row gets its own scoped\n * `<RecordContext>` so column edits track per-row dirty state and the\n * surrounding `<MainContext>`'s `saveAll` aggregates every dirty row\n * into a single transactional `executeMultiple`.\n *\n * Controlled when supplied (consumer owns the state + must respond to\n * {@link onEditableChanged}); uncontrolled when omitted (the grid\n * owns the state internally, toggled by the toolbar button rendered\n * via {@link allowEdit}).\n */\n editable?: boolean;\n /**\n * Fired when the inline-edit toggle flips the grid's editable state.\n * Required for the controlled flavor of {@link editable}; ignored for\n * the uncontrolled flavor (which manages state internally).\n */\n onEditableChanged?: (editable: boolean) => void;\n /**\n * When false, the footer's page-size selector is hidden and the user is\n * pinned to whatever value {@link pageSize} carries. Defaults to true.\n */\n allowChangingPageSize?: boolean;\n /**\n * How the grid lets the user select rows.\n *\n * - `'none'`: no selector column; rows are display-only.\n * - `'single'`: a radio-button column appears; selecting one row\n * deselects any previously-selected row.\n * - `'multiple'` (default): a checkbox column appears with a\n * header-level select-all toggle; users can select any combination\n * of rows.\n *\n * The current selection ships back to the consumer via\n * {@link onSelectedRecordsChange} or the controlled\n * {@link selectedRecords} prop.\n */\n selectionMode?: 'none' | 'single' | 'multiple';\n /**\n * Fires whenever the row selection changes. Receives the live array of\n * `TableRecord`s for the selected rows in the order they appear on the\n * current page. Always called with `[]` when {@link selectionMode} is\n * `'none'` — useful as a no-op default.\n *\n * Selection is per-page — paging forward resets the visible selection\n * because the underlying rows change. Persistent cross-page selection\n * is a Phase 3+ feature.\n */\n onSelectedRecordsChange?: (records: readonly TableRecord[]) => void;\n /**\n * Controlled selection. When provided, the grid renders the rows whose\n * `record.id` appears in this array as selected, and the consumer owns\n * the state — every selection change (user click, page change, view\n * change, sort change, search change) flows through\n * {@link onSelectedRecordsChange} and the prop must be updated by the\n * parent for the visual to change. Omit for uncontrolled mode (the\n * grid keeps its own internal selection state and just calls\n * `onSelectedRecordsChange` on changes).\n */\n selectedRecords?: readonly TableRecord[];\n /**\n * Fires whenever the grid refreshes — either via the built-in\n * refresh icon button, a `<RefreshGridButton>`, or any consumer code\n * that calls `gridContext.refresh()`. \"Force reload\" semantic: fires\n * even when filter inputs are unchanged so downstream consumers can\n * mirror the reload deterministically.\n *\n * Does NOT fire on view / sort / search / paging changes — those\n * route through their own state and naturally invalidate the records\n * query without a refresh call.\n */\n onRefresh?: () => void;\n /**\n * Per-column cell-renderer overrides, keyed by column logical name.\n * Takes precedence over both {@link typeRenderers} and the built-in\n * type dispatcher — use it when one specific column needs bespoke\n * rendering (e.g. `name` as a link to the record, `creditlimit` in\n * red when it exceeds a threshold).\n *\n * The renderer receives a {@link GridColumnCellRendererProps}\n * carrying value + bound-column metadata + columnName plus the\n * shared row / parentRecord / search context.\n */\n cellRenderers?: Record<string, GridColumnCellRenderer>;\n /**\n * Cell-renderer overrides keyed by semantic type category. Falls\n * back to the built-in dispatcher for any category not specified.\n * Useful when a consumer wants to override \"all money columns\" or\n * \"all dates\" across the grid without enumerating column names.\n *\n * Precedence: {@link cellRenderers} (column-name) wins, then\n * `typeRenderers` (type), then built-in.\n */\n typeRenderers?: Partial<Record<CellRendererType, GridColumnCellRenderer>>;\n /**\n * Additional server-side filters AND-merged into the view's base\n * query. Each entry is a `GridFilterBase`-derived shape (e.g. a\n * `RelationshipFilter` for `SubGrid`-style parent-record scoping).\n * The server's `IFetchXmlQueryComposer` dispatches by `$type`\n * discriminator, so adding new filter variants is a server-side\n * change picked up by the regenerated wire types — consumers just\n * pass them through here. `undefined` / empty array → no additional\n * filters (the default).\n */\n filters?: readonly components['schemas']['GridFilterBase'][];\n /**\n * When `true` (default), the view's primary-name column renders as a\n * clickable link that invokes whichever edit-record button is mounted\n * inside the grid (e.g. `<NavigateOpenRecordGridButton>`). When no\n * edit button is registered the flag has no effect — the cell stays\n * plain text. Set `false` to keep the primary-name cell text-only\n * even with an edit button in the toolbar.\n */\n allowNavigateOnPrimaryNameClick?: boolean;\n /**\n * When `true` (default), double-clicking a body row invokes the\n * registered edit handler for that row's record — same code path the\n * primary-name link uses. Has no effect when no edit-record button is\n * registered (the gate inside MainGrid is identical to the primary-name\n * link's). Set `false` to disable the double-click affordance — useful\n * when the row is hosting custom controls whose double-click handler\n * should win, or when you want to keep edits strictly behind explicit\n * buttons.\n */\n allowOpenOnRowDoubleClick?: boolean;\n /**\n * Action buttons rendered at the right end of the toolbar — typically\n * `<NewRecordGridButton>` / `<OpenRecordGridButton>` /\n * `<DeleteRecordGridButton>` and friends, usually wrapped in\n * `<GridButtons>` for layout. Children read selection / tableName /\n * viewId / refresh via {@link useGridContext}, so consumers don't have\n * to plumb that state by hand.\n *\n * The built-in refresh icon button renders separately to the right of\n * these children (see {@link showRefreshButton}) — consumers don't\n * need to add it here.\n */\n children?: ReactNode;\n /**\n * When `true` (default), the grid renders a refresh icon button at\n * the far right of the toolbar — always visible, no consumer wiring\n * required. Set to `false` to suppress it (e.g. when the consumer\n * wants a custom button bar with their own refresh placement).\n */\n showRefreshButton?: boolean;\n /**\n * Minimum height of the grid. CSS value string (`\"250px\"`, `\"30vh\"`,\n * `\"50%\"`) or a raw number (treated as pixels). Defaults to `\"250px\"`\n * to match Blazor's `GridBase.MinHeight`. The body area grows to fit\n * content above this floor, then engages vertical scrolling once\n * content + {@link maxHeight} caps the total height. Ignored when\n * {@link fullSize} is `true`.\n */\n minHeight?: string | number;\n /**\n * Maximum height of the grid. CSS value or pixel number. When set,\n * the grid stops growing past this point and the body area scrolls\n * vertically. Defaults to `undefined` — no cap; the grid grows to\n * fit its rows. Ignored when {@link fullSize} is `true`.\n */\n maxHeight?: string | number;\n /**\n * When `true`, the grid fills its parent's available height\n * (`flex: 1 1 auto` + `min-height: 0`) and ignores\n * {@link minHeight} / {@link maxHeight}. Use this inside a flex\n * column or fixed-height layout container to make the grid stretch\n * to the page area. Defaults to `false`.\n */\n fullSize?: boolean;\n /**\n * Parent record passed through from a `<SubGrid>` wrapper — surfaced via\n * `useGridContext().parentRecord` so toolbar buttons (the `New` button's\n * parent-FK pre-fill, future M2M link button) can read it. `undefined`\n * for a stand-alone `<MainGrid>`. Consumers shouldn't pass this directly\n * on `<MainGrid>` — set it via `<SubGrid record={...} relationshipName=\"...\" />`.\n */\n parentRecord?: TableRecord;\n /**\n * Optional shared {@link ViewDataSource}. When set, the grid reads\n * its search / sort / page / pageSize and row data from the\n * datasource instead of owning that state internally — the same\n * datasource can drive a sibling `<DataverseChart>` or a second\n * `<MainGrid>` so they all paginate / filter / search together off\n * one round-trip.\n *\n * Standalone usage (no `dataSource`) keeps the existing internal-\n * state machine — the grid fetches via `useGridData` itself.\n *\n * **What the datasource does NOT own:** per-grid UI state (column\n * sizing, row selection, pending row creates/updates/associates).\n * Those stay grid-local — two grids sharing a datasource can still\n * have independent selection and per-grid pending edits.\n *\n * **`dataSource.highlight`** — when set (e.g. via a chart slice\n * click in cross-filter \"highlight\" mode), rows whose value in the\n * highlighted column doesn't match are visually muted via a CSS\n * class; the row data + selection are unaffected (the soft cross-\n * filter is purely styling, not a re-fetch).\n */\n dataSource?: ViewDataSource;\n /**\n * Schema name of the relationship this grid is scoped through — pairs\n * with {@link parentRecord}. Same SubGrid-injected origin.\n */\n relationshipName?: string;\n /**\n * When `true` (default), the toolbar renders a search box for any\n * searchable string / lookup / choice / number columns the active\n * view exposes. Set `false` to hide the search box entirely — useful\n * for grids where the consumer provides its own filtering UI or the\n * column set is intentionally read-only.\n */\n allowSearch?: boolean;\n /**\n * When `true`, the paging footer (prev / next / page-size selector /\n * \"showing X of Y\" indicator) is hidden entirely. Useful for grids\n * whose page size is fixed by configuration and you don't want to\n * surface the navigation chrome. Defaults to `false`.\n */\n hidePaging?: boolean;\n /**\n * Title rendered in the toolbar when no view-picker dropdown is shown\n * (single-view tables, or grids pinned to a specific `viewId`). When a\n * view picker IS shown, this is ignored — the dropdown carries the\n * grid's identity instead.\n */\n title?: string;\n /**\n * Name of the URL query-string parameter to persist the grid's interactive\n * state to. When set, the initial render reads this parameter and seeds\n * the active view, page number, page size, and sort from it; subsequent\n * user actions (view pick, page change, header sort) write the new state\n * back via `history.replaceState`. Survives page refresh and bookmarks.\n *\n * Wire shape: a JSON-stringified `{v, p, ps, s, q}` object — see\n * {@link PersistedGridState}.\n *\n * Router-agnostic — uses the platform `URLSearchParams` API and\n * `history.replaceState` directly so the grid doesn't take a hard\n * react-router dependency. Consumers using a client-side router will\n * still see the parameter participate in their router's URL state.\n *\n * Search text is excluded by default; opt in via\n * {@link includeSearchInPersistedState}.\n */\n persistedStateQueryParameter?: string;\n /**\n * When `true`, the active search text is included in the persisted state\n * URL parameter (under the `q` field). Defaults to `false` — search\n * terms can be sensitive, accumulate in browser history, and leak into\n * HTTP referrers, so the grid keeps them out of the URL unless the\n * consumer explicitly opts in. Has no effect when\n * {@link persistedStateQueryParameter} is not set.\n */\n includeSearchInPersistedState?: boolean;\n /**\n * When `true` (default), File / Image column cells render a per-row\n * preview icon (\"eye\") at the trailing edge that opens an inline\n * `<FileViewer>` dialog. Set `false` on read-only grids where opening\n * the file's contents shouldn't be an entry point. Pending-create rows\n * never show the icon — there's no persisted file to preview yet.\n */\n allowPreviewForFileColumns?: boolean;\n /**\n * When `true` (default), File / Image column cells render a per-row\n * download icon at the trailing edge. Set `false` on audit grids where\n * file export isn't allowed. Pending-create rows never show the icon\n * — there's no persisted file to download yet.\n */\n allowDownloadForFileColumns?: boolean;\n /**\n * Multiplier applied to numeric column widths that come from a view\n * — both server-returned `ResolvedColumn.width` (Dataverse saved\n * queries) and inline `CustomViewDefinition.columns[].width`.\n *\n * Defaults to `1` (no scaling — widths pass through as-is, matching\n * the Blazor `<GridColumn Width=\"...\" />` 1:1 behavior at\n * `FluentGridBase.razor`). Set to a value above 1 to inflate every\n * numeric width — useful when adapting Dataverse \"pixel\" widths\n * calibrated for the denser model-driven-app rendering and they\n * read cramped in a less-dense React page. User-resized columns\n * (the drag-handle widths held in `columnSizing`) are NOT scaled —\n * those are explicit pixel choices the user made in this rendering,\n * and re-scaling them would feel like the column \"fights back\"\n * against a deliberate resize. Only applies to numeric widths;\n * string widths like `\"200px\"` or `\"auto\"` pass through unchanged.\n */\n columnWidthScale?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst columnHelper = createColumnHelper<TableRecord>();\n\n// Module-level empty-array constant so renders that fall back here don't\n// create a fresh `[]` reference each time. Critical for effects that list\n// `data` in their dep array — without a stable ref, every render would\n// look like a data change and re-fire those effects, which (when one of\n// them calls a parent setter via `onSelectedRecordsChange`) cascades into\n// an infinite render loop on view changes / fetch-in-flight states.\n// Typed as mutable `TableRecord[]` to satisfy TanStack's `data` prop;\n// nothing actually mutates it (no push / splice / index assignment).\nconst EMPTY_RECORDS: TableRecord[] = [];\n\n// Same stability concern for row selection: each\n// `setInternalRowSelection({})` otherwise creates a fresh `{}` and\n// triggers a needless render even when nothing was selected. Module-level\n// constant lets React's state-equality bailout (Object.is) skip the\n// no-op update.\nconst EMPTY_ROW_SELECTION: RowSelectionState = {};\n\n// Stable fallback for the server-resolved columns array while the first\n// search response is still in flight. Same `useMemo` / dep-array\n// reasoning as `EMPTY_RECORDS` — every render building a fresh `[]`\n// would invalidate the column-defs memo on every keystroke.\nconst EMPTY_RESOLVED_COLUMNS: readonly ResolvedColumn[] = [];\n\n// Stable fallback for the views-for-table result during the first fetch.\n// The composed-views memo lists it as a dependency; without a stable\n// reference, every render would re-build the views array and re-fire the\n// default-pin effect downstream.\nconst EMPTY_VIEW_LIST: readonly ViewMetadata[] = [];\n\n// View-switch reset for the column-sizing map (TanStack's per-column\n// user-dragged widths). Stable identity so `setColumnSizing(EMPTY_…)`\n// short-circuits when nothing was sized.\nconst EMPTY_COLUMN_SIZING: ColumnSizingState = {};\n\n// Default options for the page-size dropdown. Hoisted to module scope so\n// the default-prop value is referentially stable across renders (consumers\n// that pass nothing don't get a fresh array each mount → no spurious\n// MainGridPaging re-renders, and downstream effects watching it stay\n// quiet). Matches the Blazor MainGrid's default options.\nconst DEFAULT_PAGE_SIZES: readonly number[] = [10, 20, 50, 100];\n\n// Default starting page size. Aligned with Blazor's `DefaultItemsPerPage = 50`\n// so cross-stack consumers don't see different row counts depending on which\n// renderer they use. Consumers that want the old 25 just pass `pageSize={25}`.\nconst DEFAULT_PAGE_SIZE = 50;\n\n/**\n * Three-state sort cycle (asc → desc → unsorted) used by `handleSortToggle`.\n * Hoisted to module scope so the handler can call it BEFORE `setSort` (the\n * caller needs the post-toggle list to write into the persisted URL state)\n * without smuggling a functional updater out of `setSort`'s callback.\n *\n * - Plain click: collapse to single-column; cycle direction if already the\n * sole sort column.\n * - Shift+click: extend the existing sort, cycling the clicked column\n * independently.\n */\nfunction computeNextSort(\n prev: readonly ColumnSort[],\n columnName: string,\n shiftKey: boolean,\n): ColumnSort[] {\n const existingIndex = prev.findIndex((d) => d.columnName === columnName);\n const existing = existingIndex >= 0 ? prev[existingIndex] : undefined;\n if (shiftKey) {\n if (!existing) return [...prev, { columnName, descending: false }];\n if (!existing.descending) {\n const next = prev.slice();\n next[existingIndex] = { columnName, descending: true };\n return next;\n }\n return prev.filter((_, i) => i !== existingIndex);\n }\n if (prev.length === 1 && existing) {\n if (!existing.descending) return [{ columnName, descending: true }];\n return [];\n }\n return [{ columnName, descending: false }];\n}\n\n/**\n * Sub-component that mounts inside the per-row `<RecordContext>` so it\n * can pull `setColumnValue` out of the row's edit-context and thread it\n * into the consumer's `editRenderer` as `setValue`. Existing as a tiny\n * component (rather than inlining `useRecordContext()` in the cell\n * render function) is what makes the hook call legal — TanStack's cell\n * function isn't itself a React component, so the hook needs its own\n * boundary.\n */\nfunction TemplateColumnEditCell({\n rendererProps,\n editRenderer,\n inlineEditCellClass,\n}: {\n rendererProps: GridTemplateColumnCellRendererProps;\n editRenderer: GridTemplateColumnEditRenderer;\n inlineEditCellClass: string;\n}) {\n const { record, setColumnValue } = useRecordContext();\n const editProps: GridTemplateColumnEditRendererProps = {\n ...rendererProps,\n // Read from the live record-context record (loadedRecord + pendingChanges)\n // rather than the TanStack row snapshot in `rendererProps.record`. The\n // snapshot is frozen at fetch time, but `setColumnValue` writes to the\n // context's working copy — so a custom editRenderer reading\n // `record.properties[col]` off the snapshot never sees its own edits\n // (input appears stuck while the row dirties). Bound `<ColumnEdit>` editors\n // already read the context record via their column hooks; this gives the\n // imperative editRenderer the same live view.\n record: record ?? rendererProps.record,\n setValue: setColumnValue,\n };\n // Same sizing wrapper the bound-column inline-edit path uses. Without\n // it the Fluent v9 `<Input>` / `<Combobox>` / `<Dropdown>` default\n // min-width (~200px) bleeds past the column's actual width and\n // visually overlaps the neighbour cell, which can also disrupt\n // focus / pointer-event hit-testing inside the cell.\n return (\n <div className={inlineEditCellClass}>\n {editRenderer(editProps)}\n </div>\n );\n}\n\n/**\n * Public MainGrid wrapper that mounts an inner `<MainContext>` around\n * the grid's body. The wrapper exists so the grid's inline-edit\n * descendants (per-row `<RecordContext>`s, dialog-driven row queues)\n * report dirty state to a context whose scope is exactly this grid —\n * letting the built-in refresh button warn on unsaved inline edits and\n * letting consumer code aggregate \"is this one grid dirty?\" without\n * sweeping in unrelated dirty state from the surrounding page.\n *\n * The inner MainContext still auto-registers with whatever outer\n * MainContext / RecordContext encloses the grid (the typical case is a\n * page-level `<RecordContext>`), so transactional save / refresh\n * cascade through it the same way they did before this wrapper landed.\n * `isTopMost` returns `false` inside the wrapper, so the inner context\n * doesn't register a duplicate `beforeunload` handler or its own entry\n * in the page-wide unsaved-changes registry.\n */\nexport function MainGrid(props: MainGridProps) {\n return (\n <MainContext>\n <MainGridImpl {...props} />\n </MainContext>\n );\n}\n\nfunction MainGridImpl({\n tableName,\n viewId,\n pageSize: initialPageSize = DEFAULT_PAGE_SIZE,\n pageSizes = DEFAULT_PAGE_SIZES,\n pagingMode = PagingMode.Paged,\n allowChangingPageSize = true,\n selectionMode = 'multiple',\n onSelectedRecordsChange,\n selectedRecords: controlledSelectedRecords,\n onRefresh,\n cellRenderers,\n typeRenderers,\n filters,\n allowNavigateOnPrimaryNameClick = true,\n allowOpenOnRowDoubleClick = true,\n children,\n showRefreshButton = true,\n minHeight = '250px',\n maxHeight,\n fullSize = false,\n parentRecord,\n relationshipName,\n dataSource,\n allowSearch = true,\n hidePaging = false,\n title,\n persistedStateQueryParameter,\n includeSearchInPersistedState = false,\n allowPreviewForFileColumns = true,\n allowDownloadForFileColumns = true,\n columnWidthScale = 1,\n defaultViewId,\n viewIds,\n customViewDefinitions,\n viewSort = ViewSort.NameAscending,\n transformViewAsync,\n borderVisible = true,\n selectFromEntireRow = true,\n allowEdit = false,\n editable: editableProp,\n onEditableChanged,\n className,\n style,\n}: MainGridProps) {\n const styles = useMainGridStyles();\n const t = useT();\n // Parent table metadata — only fetched in SubGrid mode (parentRecord\n // + relationshipName set). Used to detect whether the SubGrid's\n // relationship is M:N so `buildPendingRowChangeRequests` can emit\n // AssociateRequests for any new records queued via\n // `<NewRecordGridButton>`. Without this, a new record created inside\n // an M:N SubGrid would commit but never get linked to the parent.\n // The hook always mounts (stable hook order); `enabled` gates the\n // actual fetch.\n const parentTableMetadataQuery = useTableMetadata(parentRecord?.tableName ?? '', {\n enabled: !!parentRecord?.tableName,\n });\n\n // The grid's primary table metadata — drives the per-column metadata\n // lookups passed to consumer cell renderers (`<GridColumn>`'s\n // `cellRenderer.metadata` and `<GridTemplateColumn>`'s\n // `dependsOnMetadata` map). Only loaded when a `tableName` prop is\n // supplied; in `viewId`-only mode the grid doesn't have the table\n // name available pre-fetch so the lookups come back undefined until\n // the consumer also passes tableName (most do).\n const gridTableMetadataQuery = useTableMetadata(tableName ?? '', {\n enabled: !!tableName,\n });\n const gridTableMetadata = gridTableMetadataQuery.data;\n const isManyToManySubGrid = useMemo(() => {\n if (!relationshipName || !parentTableMetadataQuery.data) return false;\n return (\n parentTableMetadataQuery.data.manyToMany?.some(\n (rel) => rel.relationshipName === relationshipName,\n ) ?? false\n );\n }, [relationshipName, parentTableMetadataQuery.data]);\n // Harvest `<GridColumn>` / `<GridColumns>` children: when one or more\n // are declared the grid switches to \"replace mode\" — only the declared\n // columns render, in declared order, mirroring Blazor's `<GridColumn>`\n // semantics. The remaining children (toolbar buttons, `<GridButtons>`,\n // arbitrary consumer JSX) pass through to the toolbar slot via\n // `toolbarChildren`. Done synchronously each render so the column-defs\n // memo can read declarations off the result without an effect hop.\n const { declaredColumns, templateColumns, orderedDeclarations, toolbarChildren } =\n extractGridColumnOverrides(children);\n // Index of declared column props by columnName, for O(1) per-column\n // lookup inside the column-defs memo. `declaredColumnsKey` captures\n // declaration content as a structural string so the memo only re-fires\n // when something actually changed (declarations are typically inline\n // JSX, so the array identity churns on every parent render).\n const declaredColumnsByName = useMemo(() => {\n const map = new Map<string, GridColumnProps>();\n for (const col of declaredColumns) map.set(col.columnName, col);\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [declaredColumns]);\n // Index of template column props by their stable id (auto-generated\n // `__template-{index}` when the consumer omitted `id`). Used by the\n // header `<th>` width-lookup so consumer-declared\n // `<GridTemplateColumn width=\"240px\">` actually lands on the column —\n // the TanStack column id for a template is its `id`, not a Dataverse\n // column name, so it can't share `declaredColumnsByName`'s indexing.\n const templateColumnsById = useMemo(() => {\n const map = new Map<string, GridTemplateColumnProps & { id: string }>();\n for (const tpl of templateColumns) map.set(tpl.id, tpl);\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [templateColumns]);\n const hasDeclaredColumns = declaredColumns.length > 0 || templateColumns.length > 0;\n // Derived constant — used in multiple places (footer gate, infinite-\n // scroll accumulator, settings page-size gate). Hoisted so the\n // settings gate can read it without forward-reference issues.\n const isVirtualize = pagingMode === PagingMode.Virtualize;\n // Confirmation prompt for the built-in refresh icon button when there\n // are pending row changes in the queue. Resolves true to proceed,\n // false to cancel. Bound to the same dialog service the rest of the\n // framework uses, so the consumer's dialog provider styles the prompt.\n const warnAboutUnsavedChanges = useWarnAboutUnsavedChanges();\n // Surrounding MainContext (when nested inside a page-level\n // `<RecordContext>` / `<MainContext>`). Used by the descendant-\n // registration effect further down AND by the editable-toggle\n // handler to detect pending edits before discarding them.\n const parentMainContext = useMainContext();\n // Per-instance radio-group name. HTML radio inputs only behave as a\n // single-selection group when they share a `name`; useId gives every\n // grid instance its own group so two grids on the same page don't\n // accidentally share selection state.\n const radioGroupName = useId('ppp-grid-selection');\n // Tracks the viewport at the `767.98px` mobile breakpoint (same as\n // Blazor's `.desktop-screen` / `.mobile-screen` CSS). Drives where\n // the page-size selector renders: footer at wide widths, Settings\n // popover at narrow.\n const isNarrowViewport = useIsNarrowViewport();\n // Internal editable state for the uncontrolled flavor — only consulted\n // when the `editable` prop is undefined. Controlled mode (prop set)\n // always wins so the consumer's source of truth doesn't drift.\n const [internalEditable, setInternalEditable] = useState(false);\n const isControlledEditable = editableProp !== undefined;\n const editable = isControlledEditable ? editableProp : internalEditable;\n // `useTransition` lets React render the editable-toggle's downstream\n // work (mounting one `<RecordContext>` per row + its\n // `<ColumnEdit>` cells) without blocking the toolbar's response to\n // the toggle. The OLD render stays on screen until the new tree is\n // fully built, then commits atomically — no visible row-by-row paint.\n // `isEditableTransitionPending` is true while React is working on the\n // new tree; we drive a scrim over the body to signal that something's\n // loading rather than leave the user wondering why nothing's happening.\n const [isEditableTransitionPending, startEditableTransition] = useTransition();\n const handleEditableToggle = useCallback(\n async (next: boolean) => {\n // Discard-pending-edits prompt. Toggling off while editable rows\n // hold pending column changes would silently drop them (the per-\n // row `<RecordContext>`s unmount and their `pendingChanges` state\n // goes with them). Prompt before discarding so the user can\n // change their mind. Mirrors the same `warnAboutUnsavedChanges`\n // dialog the built-in refresh button uses.\n //\n // `parentMainContext` here is the inner `<MainContext>` that the\n // public `MainGrid` wrapper mounts around this body — so its\n // aggregate `isDirty` reports just this grid's subtree (queued\n // row changes + per-row inline edits) without sweeping in unrelated\n // dirty state from the surrounding page.\n if (!next && parentMainContext?.isDirty) {\n const proceed = await warnAboutUnsavedChanges();\n if (!proceed) return;\n }\n if (isControlledEditable) {\n // Controlled mode: consumer owns the state. We can't wrap their\n // setState in our transition — they'd need their own useTransition\n // to get the same atomic-swap behavior. Just notify them.\n onEditableChanged?.(next);\n return;\n }\n startEditableTransition(() => {\n setInternalEditable(next);\n });\n // onEditableChanged fires immediately (not deferred) — consumers\n // expect the callback to land synchronously with the toggle click,\n // and the transition is purely a local-render optimization.\n onEditableChanged?.(next);\n },\n [\n isControlledEditable,\n onEditableChanged,\n parentMainContext,\n warnAboutUnsavedChanges,\n ],\n );\n // Computed visibility flags for the two rows the Settings popover\n // currently hosts. Hoisted out of the render block so the overflow\n // menu equivalent and the inline popover share one source of truth.\n // Editable row: same gate as before — `allowEdit` opt-in, only in\n // uncontrolled mode. Page-size row: narrow viewports only (footer\n // hosts it on wide widths). The Settings BUTTON itself only shows\n // when at least one of these rows would render, so wide viewports\n // without `allowEdit` see no gear.\n const showSettingsEditableRow = allowEdit && !isControlledEditable;\n // Page-size row: narrow viewports only (footer hosts it on wide widths),\n // AND only in `paged` mode. Virtualize / infinite-scroll has no\n // meaningful \"page size\" the user thinks in terms of — rows accumulate\n // continuously as they scroll, so a \"rows per page\" dropdown is\n // misleading. The setting still has internal effect (controls fetch\n // batch size) but we don't surface it.\n const showSettingsPageSizeRow =\n allowChangingPageSize && isNarrowViewport && !isVirtualize;\n // Inline editing depends on a parent `<MainContext>` / `<RecordContext>`:\n // each editable row mounts its own RecordContext that auto-registers\n // with the surrounding context for save coordination. Without one,\n // dirty edits have nowhere to bubble to and the framework's save\n // button can't pick them up. Disable the toggle with an explanatory\n // tooltip rather than silently letting edits land in a dead end.\n const inlineEditDisabled = parentMainContext === null;\n // Persisted-state seed. Read once from `?<persistedStateQueryParameter>`;\n // the lazy `useState` ensures we don't reparse the URL on every render\n // and that downstream lazy initializers can read the same snapshot.\n // Writes happen synchronously inside each user handler (handleViewChange,\n // handlePageChange, etc.) via `writePersistedStateToUrl` so the auto-pin\n // effect and other non-user-driven state cascades don't pollute the URL.\n const [initialPersistedState] = useState(() =>\n readPersistedGridStateFromUrl(persistedStateQueryParameter),\n );\n // Persisted page number is restored only for paged mode — virtualize\n // mode accumulates pages on scroll, so a \"start at page 3\" wouldn't\n // skip-forward sensibly (the user would see an empty top with rows\n // dropping in from page 3, missing 1 and 2). Always start at 1 in\n // virtualize.\n const [_internalPage, _setInternalPage] = useState<number>(() =>\n pagingMode === PagingMode.Virtualize ? 1 : initialPersistedState?.p ?? 1,\n );\n // `pageSize` is owned here as state seeded from the prop (or the persisted\n // URL state, when present) so the footer's page-size selector can update\n // it. The `pageSize` prop is intentionally only read at mount — Blazor's\n // grid behaves the same way (consumers configure the initial value, the\n // user drives changes after that).\n const [_internalPageSize, _setInternalPageSize] = useState<number>(\n () => initialPersistedState?.ps ?? initialPageSize,\n );\n // The user-selected view from the dropdown. Undefined until the first\n // views-for-table response lands and the auto-pin effect sets it to the\n // first view; the dropdown drives subsequent changes. Skipped entirely\n // when an explicit `viewId` prop pins the grid to one view.\n const [selectedViewId, setSelectedViewId] = useState<string | undefined>(\n () => initialPersistedState?.v ?? undefined,\n );\n // Current sort directives (column + direction, in precedence order).\n // Empty = let the server use the view's own stored `<order>` clauses\n // (the grid-data endpoint maps null → keep base sorts, empty → strip\n // sorts, non-empty → replace). Clicking a column header rewrites this\n // list; Shift+click extends it for multi-column sort. Switching views\n // empties it so each view starts with its own default sort.\n //\n // Uses the wire-shared {@link ColumnSort} shape so the array can be\n // passed straight into `GridDataRequest.sorts` without a mapping\n // step.\n const [_internalSort, _setInternalSort] = useState<readonly ColumnSort[]>(\n () => {\n // URL-persisted sort wins — the user explicitly chose this state by\n // interacting with the grid before the navigation.\n const persisted = deserializeSorts(initialPersistedState?.s);\n if (persisted.length > 0) return persisted;\n // Fall through to `<GridColumn isDefaultSortColumn>`. First declared\n // column with the flag wins (matches Blazor's \"first declared\n // default wins\" tie-break). `initialSortDirection` selects the\n // direction; defaults to ascending.\n for (const col of declaredColumns) {\n if (col.isDefaultSortColumn) {\n return [\n {\n columnName: col.columnName,\n descending: col.initialSortDirection === 'desc',\n },\n ];\n }\n }\n return [];\n },\n );\n // What the user has typed into the search box, debounced before it\n // reaches the network. `searchText` is the live (every-keystroke)\n // value so the input stays responsive; `debouncedSearchText` is what\n // we send to the server. Seeded from the persisted URL state only when\n // `includeSearchInPersistedState` is on; otherwise stays empty even if\n // the URL happens to carry a `q` from a previous opt-in session.\n const [_internalSearchText, _setInternalSearchText] = useState<string>(() =>\n includeSearchInPersistedState ? initialPersistedState?.q ?? '' : '',\n );\n const [_internalDebouncedSearchText, _setInternalDebouncedSearchText] = useState<string>(() =>\n includeSearchInPersistedState ? initialPersistedState?.q ?? '' : '',\n );\n\n // Effective state — when a `dataSource` is set, read/write through\n // it so the same datasource can drive multiple grids/charts in\n // sync. Otherwise fall back to the local useState shadows. Setters\n // are wrapped in `useCallback` so identity-stable dep arrays\n // downstream don't churn when the user types. State updates land\n // synchronously regardless of mode (the datasource debounces the\n // network fetch independently, not the local state).\n const page = dataSource ? dataSource.page : _internalPage;\n const pageSize = dataSource ? dataSource.pageSize : _internalPageSize;\n const sort = dataSource ? dataSource.sort : _internalSort;\n const searchText = dataSource ? dataSource.search : _internalSearchText;\n // In dataSource mode there's no separate \"debounced\" copy — the\n // datasource debounces the actual network fetch internally, so the\n // grid's `debouncedSearchText` reads through to the same live value\n // as `searchText`. The local `_internalDebouncedSearchText` shadow\n // is unused in this mode (still declared above to keep hook order\n // stable across mode switches).\n const debouncedSearchText = dataSource ? dataSource.search : _internalDebouncedSearchText;\n\n const setPage = useCallback(\n (next: number) => {\n if (dataSource) dataSource.setPage(next);\n else _setInternalPage(next);\n },\n [dataSource],\n );\n const setPageSize = useCallback(\n (next: number) => {\n if (dataSource) dataSource.setPageSize(next);\n else _setInternalPageSize(next);\n },\n [dataSource],\n );\n const setSort = useCallback(\n (next: readonly ColumnSort[]) => {\n if (dataSource) dataSource.setSort(next);\n else _setInternalSort(next);\n },\n [dataSource],\n );\n const setSearchText = useCallback(\n (next: string) => {\n // dataSource mode: writes go straight to the shared store. The\n // datasource's `setSearch` resets its own page to 1 (matches\n // the same invariant `_setInternalSearchText` + manual setPage\n // calls enforce here in standalone mode).\n if (dataSource) dataSource.setSearch(next);\n else _setInternalSearchText(next);\n },\n [dataSource],\n );\n // In dataSource mode this is effectively a no-op — the network fetch\n // doesn't go through this internal \"debounced\" copy. Kept callable so\n // call sites (view-change reset, the debounce useEffect below) don't\n // need to branch on the mode.\n const setDebouncedSearchText = useCallback(\n (next: string) => {\n if (!dataSource) _setInternalDebouncedSearchText(next);\n },\n [dataSource],\n );\n // TanStack's per-row selection map: `{ [rowId]: true }` for selected\n // rows. The grid is always controlled vs. TanStack so paging / view /\n // sort changes can wipe it deterministically — TanStack's uncontrolled\n // mode would persist stale row ids across re-renders.\n //\n // Two layers of \"controlled\":\n // - vs. TanStack: always controlled (we feed `state.rowSelection`).\n // - vs. the *consumer*: when `controlledSelectedRecords` (the prop) is\n // provided, the consumer owns selection; we derive the rowSelection\n // map from the prop and never touch `internalRowSelection`. When\n // the prop is omitted we fall back to `internalRowSelection`.\n const isSelectionControlled = controlledSelectedRecords !== undefined;\n const [internalRowSelection, setInternalRowSelection] =\n useState<RowSelectionState>({});\n const controlledRowSelection = useMemo<RowSelectionState>(() => {\n if (!isSelectionControlled) return EMPTY_ROW_SELECTION;\n const map: RowSelectionState = {};\n for (const r of controlledSelectedRecords!) {\n if (r.id) map[r.id] = true;\n }\n return map;\n }, [isSelectionControlled, controlledSelectedRecords]);\n const rowSelection = isSelectionControlled\n ? controlledRowSelection\n : internalRowSelection;\n // Refs feed the latest `isSelectionControlled` + `onSelectedRecordsChange`\n // into stable callbacks (`requestEmptySelection`, the TanStack\n // `onRowSelectionChange` handler) so timeouts and effects don't capture\n // stale values across renders.\n const isSelectionControlledRef = useRef(isSelectionControlled);\n isSelectionControlledRef.current = isSelectionControlled;\n const onSelectedRecordsChangeRef = useRef(onSelectedRecordsChange);\n onSelectedRecordsChangeRef.current = onSelectedRecordsChange;\n // Programmatic-clear helper. In controlled mode the consumer owns\n // selection, so a programmatic clear (page change, view change, sort\n // change, etc.) becomes an `onSelectedRecordsChange([])` notification\n // rather than a local state mutation. In uncontrolled mode we just\n // wipe the internal map.\n const requestEmptySelection = useCallback(() => {\n if (isSelectionControlledRef.current) {\n onSelectedRecordsChangeRef.current?.(EMPTY_RECORDS);\n } else {\n setInternalRowSelection(EMPTY_ROW_SELECTION);\n }\n }, []);\n // User-dragged column widths (TanStack's per-column-id size map). Empty\n // until the user resizes; controlled here so view switches can reset\n // back to the server-supplied defaults (`ResolvedColumn.width`) instead\n // of carrying widths from the previous view's columns over to ones\n // that happen to share a name.\n const [columnSizing, setColumnSizing] = useState<ColumnSizingState>({});\n // Set by the resize handle's `mousedown` and cleared on the next\n // macrotask. Bridges the gap between the drag start and the\n // synchronous synthetic `click` the browser dispatches on the parent\n // `<th>` when the user mouses down on the handle but releases on a\n // sibling within the same header (common — the 5px hit-zone is\n // narrow, and any drag carries the cursor out of it). Without this\n // bridge, the parent's sort `onClick` fires after every resize.\n const resizingRef = useRef(false);\n // Ref to the inner `tableContainer` (the `overflow: auto` scroll element).\n // Used as the IntersectionObserver `root` for the infinite-scroll\n // sentinel — the user scrolls WITHIN this container, not the page, so\n // the observer has to watch it explicitly.\n const tableContainerRef = useRef<HTMLDivElement | null>(null);\n // Ref to the sentinel `<div>` rendered after the last body row in\n // virtualize mode. Its visibility (intersecting the tableContainer\n // viewport) drives the \"load next page\" trigger.\n const loadMoreSentinelRef = useRef<HTMLDivElement | null>(null);\n\n // --- View resolution -----------------------------------------------------\n //\n // viewId wins. When only tableName is set, we list the table's views and\n // pick the first one as the default. (Blazor's MainGrid picks the first\n // PUBLIC view; the views-for-table endpoint already filters appropriately\n // server-side, so the first returned entry is a sane default.)\n const viewsResult = useViewsForTable(tableName ?? '', { enabled: !viewId && !!tableName });\n const dataverseViews = viewsResult.data ?? EMPTY_VIEW_LIST;\n\n // Compose the picker's view list: server-returned views + consumer-supplied\n // custom views, optionally filtered by `viewIds` and sorted by `viewSort`.\n // Memoized off structural identity so dropdown options + the default-pin\n // effect don't churn on parent re-renders.\n const customViewsKey = customViewDefinitions\n ? customViewDefinitions.map((v) => v.id).join(',')\n : '';\n const viewIdsKey = viewIds ? viewIds.join(',') : '';\n const views = useMemo(\n () =>\n composeViewsList({\n dataverseViews,\n customViewDefinitions,\n viewIds,\n viewSort,\n }),\n // Structural keys cover content; viewSort is a primitive.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [dataverseViews, customViewsKey, viewIdsKey, viewSort],\n );\n\n // Default-view pinning: once the views land, lock our internal state to\n // a sensible default — explicit `defaultViewId` first, then the first\n // available view from the composed list. Subsequent renders honor\n // whatever the dropdown / URL-state wrote in. The `selectedViewId` bail\n // covers the case where URL state already pre-seeded a selection during\n // lazy-init.\n useEffect(() => {\n if (selectedViewId) return;\n if (views.length === 0) return;\n const preferred = defaultViewId && views.some((v) => v.id === defaultViewId)\n ? defaultViewId\n : views[0]?.id;\n if (preferred) setSelectedViewId(preferred);\n }, [selectedViewId, views, defaultViewId]);\n\n const resolvedViewId = viewId ?? selectedViewId;\n\n // --- TransformViewAsync pipeline ----------------------------------------\n //\n // When the consumer supplied a transform OR the selected view is custom,\n // the grid submits inline `fetchXml` to /api/grids/data instead of a\n // `viewId`. We need the full ViewMetadata client-side for that:\n //\n // - Custom views already carry FetchXML in the consumer-supplied\n // {@link CustomViewDefinition}.\n // - Dataverse views are fetched via `useViewMetadata` when (and only\n // when) `transformViewAsync` is set — saves a round-trip on the\n // common case where the server-side view-id resolution suffices.\n //\n // The transformed result is held in `transformedView` state; the grid's\n // search request reads from it.\n const isCustomActiveView = isCustomViewId(resolvedViewId, customViewDefinitions);\n const needsFullViewMetadata = !!transformViewAsync && !isCustomActiveView;\n const dataverseViewMetadataResult = useViewMetadata(resolvedViewId ?? '', {\n enabled: needsFullViewMetadata && !!resolvedViewId,\n });\n\n const [transformedView, setTransformedView] = useState<ViewMetadata | null>(null);\n // Run the transform whenever the resolved view changes. Custom views are\n // turned into ViewMetadata-shaped objects inline; Dataverse views come\n // from `useViewMetadata`. The transform is awaited and the result stored;\n // subsequent fetches read FetchXML from `transformedView`.\n useEffect(() => {\n if (!resolvedViewId) {\n setTransformedView(null);\n return;\n }\n // Plain Dataverse view, no transform — server-side viewId path handles it.\n if (!transformViewAsync && !isCustomActiveView) {\n setTransformedView(null);\n return;\n }\n // Resolve the source ViewMetadata before transforming.\n let source: ViewMetadata | null = null;\n if (isCustomActiveView) {\n const customDef = customViewDefinitions?.find((v) => v.id === resolvedViewId);\n if (customDef) source = customToViewMetadata(customDef);\n } else if (dataverseViewMetadataResult.status === QueryStatus.Success && dataverseViewMetadataResult.data) {\n source = dataverseViewMetadataResult.data;\n }\n if (!source) {\n // Either the custom def vanished or the metadata fetch hasn't landed\n // yet — defer until it does. The hook re-runs when status changes.\n setTransformedView(null);\n return;\n }\n let cancelled = false;\n (async () => {\n try {\n const result = transformViewAsync ? await transformViewAsync(source!) : source!;\n if (!cancelled) setTransformedView(result);\n } catch (err) {\n if (!cancelled) {\n // eslint-disable-next-line no-console\n console.error('[MainGrid] transformViewAsync threw', err);\n setTransformedView(null);\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n // `customViewDefinitions` deliberately excluded — it changes identity\n // on parent re-renders even when content is stable; the structural\n // key in the views memo above already covers content changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n resolvedViewId,\n transformViewAsync,\n isCustomActiveView,\n dataverseViewMetadataResult.status,\n dataverseViewMetadataResult.data,\n ]);\n\n // Click-coalescing flag for the built-in refresh icon button. Tracked\n // here rather than inside the button so it survives re-renders driven\n // by `keepPreviousData: true` (the records hook stays in success state\n // through the refetch — the button alone can't tell from props that a\n // request is mid-flight).\n const [refreshing, setRefreshing] = useState(false);\n\n // Snapshot the grid's current state into the persisted-state wire\n // shape and write it to the configured URL parameter. Each handler\n // passes its just-computed new values as overrides so the URL reflects\n // the post-update state without having to wait for React to commit.\n // No-op when `persistedStateQueryParameter` isn't set.\n const writeStateToUrl = (overrides?: {\n viewId?: string;\n page?: number;\n pageSize?: number;\n sort?: readonly ColumnSort[];\n search?: string;\n }) => {\n if (!persistedStateQueryParameter) return;\n const state: PersistedGridState = {};\n const viewIdValue = overrides?.viewId ?? resolvedViewId;\n if (viewIdValue) state.v = viewIdValue;\n // Skip page persistence in virtualize mode — the user's scroll\n // position isn't a discrete page concept, and a `p=3` in the URL\n // would clobber the page-1 default the restore path uses.\n if (pagingMode === PagingMode.Paged) {\n const pageValue = overrides?.page ?? page;\n if (pageValue > 1) state.p = pageValue;\n }\n state.ps = overrides?.pageSize ?? pageSize;\n const sortValue = overrides?.sort ?? sort;\n if (sortValue.length > 0) state.s = serializeSorts(sortValue);\n if (includeSearchInPersistedState) {\n const searchValue = overrides?.search ?? debouncedSearchText;\n if (searchValue) state.q = searchValue;\n }\n writePersistedGridStateToUrl(persistedStateQueryParameter, state);\n };\n\n // Page-size change resets to page 1 and clears the per-page selection\n // — same invariants the view / sort / search handlers maintain (the\n // visible row set is about to change, anchoring to the top avoids a\n // mid-list jump and a phantom selection on rows that may not exist at\n // the new boundary).\n const handlePageSizeChange = (nextPageSize: number) => {\n if (nextPageSize === pageSize) return;\n setPageSize(nextPageSize);\n setPage(1);\n requestEmptySelection();\n writeStateToUrl({ pageSize: nextPageSize, page: 1 });\n };\n\n const handleViewChange = (newViewId: string) => {\n setSelectedViewId(newViewId);\n // Paging + sort + search + selection are per-view — switching views\n // resets all four so the user isn't dropped onto an out-of-range\n // page, doesn't carry over the previous view's sort, doesn't keep a\n // search filter that may not apply to the new view's columns, and\n // doesn't end up with phantom selection rows that no longer exist\n // in the new view.\n setPage(1);\n setSort([]);\n setSearchText('');\n setDebouncedSearchText('');\n requestEmptySelection();\n setColumnSizing(EMPTY_COLUMN_SIZING);\n writeStateToUrl({ viewId: newViewId, page: 1, sort: [], search: '' });\n };\n\n // Debounce the search box. We rebuild the timer on each keystroke;\n // the cleanup clears it on the next keystroke so only the final\n // value lands in `debouncedSearchText`. Setting the debounced value\n // is what triggers the network fetch (via the `useGridData`\n // dep below).\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearchText(searchText);\n // A new search invalidates the current page + selection — same\n // reasoning as sort/view changes (the new result set has\n // different rows, the user wants to land on page 1).\n setPage(1);\n requestEmptySelection();\n // URL persistence: writes the just-debounced search alongside the\n // page-1 reset. Gated on `includeSearchInPersistedState` inside the\n // helper so search-disabled grids don't write `q` even mid-debounce.\n writeStateToUrl({ search: searchText, page: 1 });\n }, SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(timer);\n // writeStateToUrl is intentionally NOT in the dep array — it captures\n // current state via closure and re-creating it each render would\n // restart the debounce on every keystroke (since the search-text\n // setter triggers a re-render). The closure inside setTimeout sees\n // the latest `searchText` because the timeout body reads it directly.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [searchText]);\n\n /**\n * Column-header click — three-state cycle (ascending → descending →\n * unsorted), Excel/AG-Grid style.\n *\n * - **Plain click** replaces the whole sort with this one column.\n * Re-clicks cycle direction (asc → desc → cleared).\n * - **Shift+click** extends the existing sort: append column as\n * ascending the first time; subsequent shift-clicks flip its\n * direction (asc → desc → remove from sort) while leaving the\n * other columns alone. Sort precedence is the click order.\n *\n * Resets to page 1 + clears selection on any change so the user lands\n * on the new top-of-results without phantom selections from before.\n * File / image columns are filtered out at the header-render layer\n * (see `isColumnSortable`), so we don't reach this for those.\n */\n const handleSortToggle = (columnName: string, shiftKey: boolean) => {\n const nextSort = computeNextSort(sort, columnName, shiftKey);\n setSort(nextSort);\n setPage(1);\n requestEmptySelection();\n writeStateToUrl({ sort: nextSort, page: 1 });\n };\n\n // Show the view-picker dropdown when there are multiple views to choose\n // from. Single-view cases (or the explicit `viewId` prop) hide it — no\n // value in a picker that can only pick one thing.\n const showViewPicker = !viewId && !!tableName && (views?.length ?? 0) > 1;\n\n // --- Records fetch (server-driven search / sort / paging) --------------\n //\n // The server's POST /api/grids/data composes the final FetchXML via\n // the shared IFetchXmlQueryComposer: it applies search across the view's\n // searchable columns, replaces sorts, AND-merges the search filter into\n // the view's existing filter, and dispatches per-column-type search\n // semantics — the same path the Blazor grid takes. Client-side\n // FetchXML mutation (the old `applyFetchXmlPaging` / `applyFetchXmlSort`\n // helpers) is no longer needed for the default view-id-based path.\n // Stringify filters once so the request memo's dep array is value-based\n // — consumers commonly construct the filter list inline on each render\n // (the SubGrid wrapper does exactly this with a fresh\n // RelationshipFilter object). Without the key, the memo would see a\n // new reference every render and force a refetch each time even when\n // nothing changed.\n const filtersKey = filters ? JSON.stringify(filters) : '';\n\n // Custom views + transformed views ship as inline `fetchXml` (mutually\n // exclusive with `viewId` server-side). For the default Dataverse path,\n // we send `viewId` and the server resolves the view itself.\n const useInlineFetchXml = !!transformedView;\n const inlineFetchXml = transformedView?.fetchXml;\n\n // When the consumer declared `<GridColumn>` children, forward their\n // logical names to the server as the request's `columns` override —\n // server-side, this REPLACES the view's projected `<attribute>` list\n // with the declared set, so a declared column not in the active view\n // still gets fetched (and renders with data rather than as an empty\n // cell). `visible: false` declarations stay out of the override.\n //\n // `<GridTemplateColumn>` declarations contribute their `dependsOn`\n // arrays to the same set — those columns aren't rendered as their\n // own table column, but they need to land in the fetched\n // `record.properties` so the consumer's `cellRenderer` can read\n // them. Visibility on a template column also gates its dependsOn\n // entries (a hidden template column doesn't fetch its data).\n // Duplicates across declarations are deduped via the Set below;\n // declaration order is preserved for the visible set so the server\n // can use it as the new <attribute> projection order.\n const columnsKey = useMemo(() => {\n if (!hasDeclaredColumns) return '';\n const seen = new Set<string>();\n const ordered: string[] = [];\n for (const decl of orderedDeclarations) {\n if (decl.kind === 'column') {\n if (decl.props.visible === false) continue;\n if (!seen.has(decl.props.columnName)) {\n seen.add(decl.props.columnName);\n ordered.push(decl.props.columnName);\n }\n } else {\n if (decl.props.visible === false) continue;\n if (!decl.props.dependsOn) continue;\n for (const dep of decl.props.dependsOn) {\n if (!seen.has(dep)) {\n seen.add(dep);\n ordered.push(dep);\n }\n }\n }\n }\n return ordered.join(',');\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasDeclaredColumns, orderedDeclarations]);\n const columnsOverride = useMemo<readonly string[] | undefined>(\n () => {\n // Declared `<GridColumn>` children win when present — they describe\n // the explicit consumer-driven projection.\n if (columnsKey) return columnsKey.split(',');\n // Otherwise, when a custom view is active, fall back to the view's\n // own column list so the server projects just those columns. Without\n // this, inline-fetchXml requests come back with every `<attribute>`\n // in the FetchXML (the server has no layoutxml to consult for a\n // custom view), which surfaces extra columns the consumer's\n // `CustomViewDefinition.columns` array was meant to suppress.\n // Mirrors Blazor's `GridViewDefinition.Columns` behavior of being\n // the authoritative display-column list for a custom view.\n if (transformedView && transformedView.columns.length > 0) {\n return transformedView.columns.map((c) => c.columnName);\n }\n return undefined;\n },\n [columnsKey, transformedView],\n );\n // Informational warning when fetchXml + declared columns coexist —\n // the override still wins (server applies it after parsing the\n // FetchXML) but consumers who pasted in a custom fetchXml may not\n // expect that. One-shot per unique configuration. Gated on\n // `columnsKey` (which only reflects declared `<GridColumn>` children)\n // so the custom-view-derived fallback in `columnsOverride` doesn't\n // trip a warning that's specifically about declared children.\n const warnedFetchXmlComboRef = useRef<string | null>(null);\n useEffect(() => {\n if (!columnsKey || !useInlineFetchXml) return;\n if (warnedFetchXmlComboRef.current === columnsKey) return;\n warnedFetchXmlComboRef.current = columnsKey;\n // eslint-disable-next-line no-console\n console.info(\n `[MainGrid] Declared <GridColumn> children override the column projection of the supplied fetchXml (columns: ${columnsKey.split(',').join(', ')}). Drop the children — or remove the conflicting <attribute> elements from the fetchXml — to silence this message.`,\n );\n }, [columnsKey, useInlineFetchXml]);\n\n const searchRequest = useMemo<GridDataRequest>(() => {\n // Conditional spreads so optional fields are *absent* rather than\n // `undefined`. With exactOptionalPropertyTypes, the generated\n // GridDataRequest declares each optional field as `string | null`\n // (or similar) — setting it to undefined explicitly is a type error.\n // Empty `searchText` is omitted so the server skips the search filter\n // entirely (avoids a no-op `like '%'` clause). Empty `sorts` is also\n // omitted so the server keeps the view's stored base sort rather\n // than stripping it (server semantics: omitted=keep, empty=strip).\n // `filters` follows the same pattern — omitted when empty so the\n // server skips its filter-dispatch loop entirely.\n const trimmed = debouncedSearchText.trim();\n return {\n ...(useInlineFetchXml && inlineFetchXml\n ? { fetchXml: inlineFetchXml }\n : resolvedViewId !== undefined && { viewId: resolvedViewId }),\n ...(trimmed && { searchText: trimmed }),\n // Translate displayed column names to their `sortBy` override (when\n // a declared `<GridColumn sortBy=\"…\">` is present) so the server's\n // ORDER BY targets the physical column. Sort UI state stays keyed\n // on the displayed name so header arrows render against the\n // clicked column.\n ...(sort.length > 0 && {\n sorts: sort.map((s) => {\n const declared = declaredColumnsByName.get(s.columnName);\n return declared?.sortBy\n ? { ...s, columnName: declared.sortBy }\n : s;\n }) as ColumnSort[],\n }),\n ...(filters && filters.length > 0 && {\n filters: filters as components['schemas']['GridFilterBase'][],\n }),\n ...(columnsOverride && { columns: [...columnsOverride] }),\n pageNumber: page,\n pageSize,\n };\n // `filtersKey` carries the structural identity of `filters` so the\n // memo invalidates on actual content changes; `filters` itself isn't\n // in the dep array to avoid re-firing on identity-only churn.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n resolvedViewId,\n useInlineFetchXml,\n inlineFetchXml,\n debouncedSearchText,\n sort,\n page,\n pageSize,\n filtersKey,\n columnsKey,\n ]);\n\n // When `transformViewAsync` is set, gate the fetch on the transform having\n // completed — otherwise the brief window before the async resolves would\n // fire an un-transformed view-id request that the transformed-fetchXml\n // request immediately supersedes. `keepPreviousData` masks the flash, but\n // the wasted round-trip + the server seeing the un-transformed shape is\n // worth avoiding. Same gate covers custom views (no viewId to fall back\n // on) and the no-transform path (no waiting needed).\n const transformPending = !!transformViewAsync || isCustomActiveView;\n const recordsResult = useGridData(searchRequest, {\n // When a shared dataSource is set, IT owns the network fetch —\n // the grid's local `useGridData` stays idle so we don't issue\n // redundant round-trips. The grid reads rows / columns / paging\n // from `dataSource` instead (see the effective-value resolutions\n // below the hook).\n enabled: !dataSource && (transformPending\n ? !!inlineFetchXml\n : !!resolvedViewId),\n // Hold the prior page / sort / search result on screen while the next\n // request is in flight, so the user sees a translucent scrim over the\n // existing rows instead of the grid collapsing to a \"Loading…\"\n // placeholder between every interaction. The first load still goes\n // through the \"loading\" state because there's nothing to keep.\n keepPreviousData: true,\n });\n\n // --- Server-resolved columns -------------------------------------------\n //\n // The search response carries the full column spec (display name, type,\n // sortability, width) — already localized and pre-projected with the\n // view's selected columns in order. Header rendering and cell dispatch\n // read straight from this; no separate table-metadata round-trip is\n // needed. Linked-entity columns (`account.contactid.emailaddress1`-\n // style alias paths) come back with their parent-table-qualified\n // localized display name (\"Email (Primary Contact)\") — no client-side\n // humanization fallback required.\n // In dataSource mode the columns ride along with the row page from\n // the shared store; otherwise read from the local useGridData\n // result. Empty fallback matches the standalone path so the column\n // resolution memo below sees a stable empty when no data has\n // landed.\n const rawResolvedColumns: readonly ResolvedColumn[] = dataSource\n ? (dataSource.columns.length > 0 ? dataSource.columns : EMPTY_RESOLVED_COLUMNS)\n : (recordsResult.data?.columns ?? EMPTY_RESOLVED_COLUMNS);\n\n // Custom views supply their own column widths inline (the\n // `CustomViewDefinition.columns[].width` pixel value the consumer set).\n // For server-resolved views the same widths come back inside\n // `ResolvedColumn.width` on the response. For inline-fetchXml requests\n // (i.e. custom views) the server doesn't get the widths over the wire,\n // so we merge them in client-side here.\n //\n // We also apply `columnWidthScale` (default 1 — widths pass through\n // 1:1, matching Blazor's `<GridColumn Width=\"…px\" />` behavior).\n // Consumers can opt into inflation by passing a value > 1 when the\n // Dataverse \"pixel\" widths read too cramped in their layout.\n // String widths (`\"200px\"`, `\"auto\"`, etc.) pass through untouched\n // since they're consumer-supplied with an explicit unit.\n const resolvedColumns = useMemo<readonly ResolvedColumn[]>(() => {\n if (rawResolvedColumns.length === 0) return rawResolvedColumns;\n const widthByName = new Map<string, number | string>();\n if (transformedView) {\n for (const c of transformedView.columns) {\n if (c.width !== undefined && c.width !== null) widthByName.set(c.columnName, c.width);\n }\n }\n const noOverrides = widthByName.size === 0;\n const noScale = columnWidthScale === 1;\n if (noOverrides && noScale) return rawResolvedColumns;\n // Cast: `exactOptionalPropertyTypes` rejects `{ width: undefined }`\n // from the spread when col.width is absent, but the spread shape is\n // structurally identical to ResolvedColumn at runtime. Easier to\n // assert than to conditionally build the spread.\n return rawResolvedColumns.map((col) => {\n const override = widthByName.get(col.columnName);\n const baseWidth = override !== undefined ? override : col.width;\n const nextWidth =\n typeof baseWidth === 'number' ? Math.round(baseWidth * columnWidthScale) : baseWidth;\n if (nextWidth === col.width) return col;\n return { ...col, width: nextWidth } as ResolvedColumn;\n });\n }, [rawResolvedColumns, transformedView, columnWidthScale]);\n\n // Map keyed by columnName so the header render can read sortability and\n // display name without scanning the array.\n const resolvedColumnsByName = useMemo(() => {\n const map = new Map<string, ResolvedColumn>();\n for (const col of resolvedColumns) map.set(col.columnName, col);\n return map;\n }, [resolvedColumns]);\n\n // Bucket the resolved columns by search semantics so the search box's\n // tooltip can advertise exactly what the user's keystrokes will hit.\n // Mirrors GridBase.razor.cs's _searchBoxStringColumnTitles /\n // _searchBoxNumberColumnTitles pair: string-like columns get\n // begins-with matching (FetchXML `like '<term>%'` or `like '%<term>%'`\n // with the `*` wildcard prefix), numeric columns accept the operator-\n // prefixed comparison terms (`<50000`, `>=100`, `=42`). Choice / multi-\n // select-choice match localized labels client-side which still surfaces\n // as a \"begins-with\" interaction from the user's POV, hence grouping\n // them with strings.\n const { searchableStringColumns, searchableNumberColumns } = useMemo(() => {\n const stringCols: ResolvedColumn[] = [];\n const numberCols: ResolvedColumn[] = [];\n for (const col of resolvedColumns) {\n const category = getRendererTypeForCode(col.type);\n if (\n category === 'string' ||\n category === 'lookup' ||\n category === 'choice' ||\n category === 'multiSelectChoice'\n ) {\n stringCols.push(col);\n } else if (category === 'number' || category === 'money') {\n numberCols.push(col);\n }\n }\n // Alphabetize for stable presentation — matches the Blazor tooltip's\n // OrderBy(x => x.Title) so the same view shows the same column list\n // order in both UIs.\n const byDisplayName = (a: ResolvedColumn, b: ResolvedColumn) =>\n a.displayName.localeCompare(b.displayName);\n stringCols.sort(byDisplayName);\n numberCols.sort(byDisplayName);\n return { searchableStringColumns: stringCols, searchableNumberColumns: numberCols };\n }, [resolvedColumns]);\n\n const hasSearchableColumns =\n searchableStringColumns.length > 0 || searchableNumberColumns.length > 0;\n\n // Edit-handler registry. Split into a mutable ref (the live handler,\n // invoked at click time) and a boolean state (drives the primary-name\n // cell's link-vs-text rendering decision). The split is critical for\n // breaking a render loop that would otherwise fire whenever the\n // registered button's handler identity churns:\n //\n // Edit-record buttons (e.g. `<NavigateOpenRecordGridButton>`) commonly\n // build their click handler via `useCallback` that closes over `urlFor`\n // / `onNavigate` props and the grid context value. With inline arrow-\n // function props (the typical demo pattern) and a context value that\n // changes whenever any unrelated grid state changes (selection,\n // refetch, etc.), the handler identity is a new reference every\n // render. If we stored the handler itself in React state and exposed\n // it back through the context, each registration would invalidate the\n // context, force the button to re-render, produce another new handler\n // identity, fire the effect again — an infinite loop.\n //\n // Holding the handler in a ref makes registration a non-rendering\n // write. `hasOpenHandler` flips only when the registry transitions\n // between empty and populated, so re-registering with a new handler\n // when one is already present is a no-op as far as React is concerned.\n // Cleanup only flips the flag off if it's still the active handler\n // (the identity-guarded check), so a stale unmount doesn't wipe a new\n // registration.\n const openHandlerRef = useRef<((r: TableRecord) => void) | null>(null);\n const createReopenHandlerRef = useRef<\n ((r: TableRecord, queueIndex: number) => void) | null\n >(null);\n const [hasOpenHandler, setHasOpenHandler] = useState(false);\n const [hasCreateReopenHandler, setHasCreateReopenHandler] = useState(false);\n const registerOpenHandler = useCallback(\n (handler: (record: TableRecord) => void) => {\n openHandlerRef.current = handler;\n // setState bails out when value === current (React's Object.is\n // check), so this is a no-op on re-registration after the first.\n setHasOpenHandler(true);\n return () => {\n // Only flip the flag off when THIS handler is still the active\n // one. After a re-registration with a new handler, the cleanup\n // for the previous handler sees `current !== handler` and\n // becomes a no-op — the flag stays true with the new handler\n // still pointed at by the ref.\n if (openHandlerRef.current === handler) {\n openHandlerRef.current = null;\n setHasOpenHandler(false);\n }\n };\n },\n [],\n );\n const registerCreateReopenHandler = useCallback(\n (handler: (record: TableRecord, queueIndex: number) => void) => {\n createReopenHandlerRef.current = handler;\n setHasCreateReopenHandler(true);\n return () => {\n if (createReopenHandlerRef.current === handler) {\n createReopenHandlerRef.current = null;\n setHasCreateReopenHandler(false);\n }\n };\n },\n [],\n );\n // Row-state-aware dispatch. Pending-create rows route to the\n // create button's re-open path so the user keeps editing the\n // in-progress new record through the same form/wizard it was\n // created in. Persisted rows route to the open button. When only\n // one of the two is registered, we fall back to it rather than\n // doing nothing — the user gets a usable affordance even when a\n // grid has only New (no Open) or only Open (no New).\n const dispatchOpen = useCallback((record: TableRecord) => {\n const id = record.id;\n const isPendingCreate = !!id && id.startsWith('__pending-create-');\n if (isPendingCreate) {\n const idx = Number.parseInt(id.slice('__pending-create-'.length), 10);\n const reopen = createReopenHandlerRef.current;\n if (reopen && Number.isFinite(idx)) {\n reopen(record, idx);\n return;\n }\n // Fall back to the open handler when no create reopen is\n // registered. Degraded UX (user gets the edit form for a\n // pending row, which may not match the create form) but\n // better than a no-op click.\n openHandlerRef.current?.(record);\n return;\n }\n openHandlerRef.current?.(record);\n }, []);\n // Drives the primary-name cell's link-vs-text decision. Either\n // handler being registered makes the cell clickable; the dispatch\n // above picks the right one at click time.\n const hasRowOpenAffordance = hasOpenHandler || hasCreateReopenHandler;\n\n // --- TanStack column definitions ----------------------------------------\n //\n // When the consumer declared `<GridColumn>` children, the grid switches\n // to \"replace mode\": render only the declared columns, in declared\n // order. Server-resolved metadata supplies type / sortability /\n // alignment defaults — we just project the server's column list down\n // to the declared subset, in declared order. `visible: false`\n // declarations stay out of the rendered list.\n //\n // Server now honors the request's `columns` override (forwarded via\n // `columnsOverride` above from declared `<GridColumn>` children), so\n // every declared column should appear in `resolvedColumns` regardless\n // of what the active view originally projected. A declared column\n // missing from `resolvedColumns` is therefore a real bug — the\n // server's metadata-resolution dropped it (e.g. the column name\n // doesn't exist on the table at all) — and we warn so consumers\n // catch the wiring mistake during development.\n //\n // Without any declarations, the grid uses the server-resolved column\n // list verbatim (existing behavior, preserves backward compatibility).\n const effectiveResolvedColumns = useMemo<readonly ResolvedColumn[]>(() => {\n if (!hasDeclaredColumns) return resolvedColumns;\n const out: ResolvedColumn[] = [];\n for (const declared of declaredColumns) {\n if (declared.visible === false) continue;\n const base = resolvedColumnsByName.get(declared.columnName);\n if (!base) {\n // eslint-disable-next-line no-console\n console.warn(\n `[MainGrid] <GridColumn columnName=\"${declared.columnName}\"> declared but the server didn't return metadata for it. The column name likely doesn't exist on the table — check spelling and the table's column metadata.`,\n );\n continue;\n }\n // Apply declared overrides on top of the server-resolved column.\n // Conditional spreads so optional fields stay absent under\n // exactOptionalPropertyTypes (declaring `width: undefined`\n // clobbers the server value).\n const overridden: ResolvedColumn = {\n ...base,\n ...(declared.displayName !== undefined && { displayName: declared.displayName }),\n ...(declared.width !== undefined && { width: declared.width }),\n ...(declared.sortable !== undefined && { isSortable: declared.sortable }),\n ...(declared.tooltip !== undefined && {\n tooltip: declared.tooltip === '' ? null : declared.tooltip,\n }),\n };\n out.push(overridden);\n }\n return out;\n }, [hasDeclaredColumns, declaredColumns, resolvedColumns, resolvedColumnsByName]);\n\n // Same shape as `resolvedColumnsByName` but built from\n // `effectiveResolvedColumns` so the header read-paths see declared\n // `<GridColumn sortable>` / `<GridColumn tooltip>` overrides.\n // `resolvedColumnsByName` stays keyed off the un-overridden server\n // resolution so the effective-columns build below can read the\n // canonical base when applying overrides.\n const effectiveColumnsByName = useMemo(() => {\n const map = new Map<string, ResolvedColumn>();\n for (const col of effectiveResolvedColumns) map.set(col.columnName, col);\n return map;\n }, [effectiveResolvedColumns]);\n\n const columns = useMemo(() => {\n // When in declared mode and ANY template column is present, iterate\n // `orderedDeclarations` directly so regular and template columns\n // render in the exact declaration order. The legacy iteration over\n // `effectiveResolvedColumns` (server-resolved column list) below\n // handles the no-templates + view-driven cases — same code path\n // produces the regular column defs, plus a separate branch for\n // template column defs (columnHelper.display) that wires the\n // consumer's cellRenderer + a dependsOn metadata lookup map.\n const buildRegularColumnDef = (resolvedColumn: ResolvedColumn) => {\n const declared = declaredColumnsByName.get(resolvedColumn.columnName);\n const columnName = resolvedColumn.columnName;\n // Resolve the renderer with the documented precedence:\n // 1. Per-`<GridColumn cellRenderer>` override (most specific —\n // authored alongside the column declaration).\n // 2. Column-name override via `MainGridProps.cellRenderers`.\n // 3. Type override keyed by semantic category.\n // 4. Built-in type dispatcher.\n const typeCode = resolvedColumn.type;\n const Renderer: GridColumnCellRenderer =\n declared?.cellRenderer ??\n cellRenderers?.[columnName] ??\n typeRenderers?.[getRendererTypeForCode(typeCode)] ??\n getCellRendererByType(typeCode);\n // ResolvedColumn.width is a Dataverse pixel width carried straight\n // from the view definition. With `exactOptionalPropertyTypes: true`,\n // TanStack's `size: number` rejects `undefined`, so we only set it\n // when the width is actually a number.\n const width = typeof resolvedColumn.width === 'number' ? resolvedColumn.width : undefined;\n // Is THIS column the one that should render as a link to the edit\n // action? Two signals must both hold: the server flagged it as the\n // primary-name column AND the consumer hasn't disabled the link\n // affordance. The third signal — \"is an edit button registered?\"\n // — is checked inside the cell render below because it depends on\n // live grid-context state (a button mounted/unmounted after the\n // column defs were built).\n const isPrimaryNameLinkTarget =\n resolvedColumn.isPrimaryName && allowNavigateOnPrimaryNameClick;\n // Is this column's type code one for which `<ColumnEdit>` will\n // dispatch a real editor? File / Image / Memo / etc. all have\n // editors; Guid / Owner / State / Status fall through to null\n // (no editor). When inline-edit mode is on, columns without an\n // editor still render their read-only display — gating per-cell\n // avoids leaving blank cells in the row.\n const hasEditorForType = getEditComponentForType(typeCode) !== null;\n return columnHelper.accessor(\n (row) => row.properties?.[columnName] as ColumnValueBase | null | undefined,\n {\n id: columnName,\n header: () => resolvedColumn.displayName,\n cell: (cellCtx) => {\n const rowRecord = cellCtx.row.original;\n const rowId = rowRecord.id;\n const isPendingCreateRow =\n !!rowId && rowId.startsWith('__pending-create-');\n // Inline-edit path: render `<ColumnEdit>` instead of the\n // read-only renderer. The surrounding per-row\n // `<RecordContext>` (mounted at the `<tr>` level when\n // `editable` is on) provides the column metadata + setValue\n // hooks the underlying edit components need. Skip-fetch mode\n // on RecordContext keeps each row from issuing a redundant\n // retrieve-record round-trip — the row data is already in\n // hand from the grid's `useGridData` fetch.\n //\n // Pending-create rows are excluded — they're not yet\n // persisted, so the edit-against-empty-record dialog flow\n // owns them. Columns whose type has no editor dispatch fall\n // back to the read-only cell so the row doesn't show blanks.\n if (editable && !isPendingCreateRow && hasEditorForType) {\n return (\n // Sizing wrapper — overrides Fluent v9's default\n // <Input> / <Combobox> / <Dropdown> min-width\n // (~200px) so the editor fits the column's actual\n // width instead of bleeding into the neighbour cell.\n // See `styles.inlineEditCell` for the cascade rules.\n <div className={styles.inlineEditCell}>\n <ColumnEdit\n columnName={columnName}\n displayLabelWhenAvailable={false}\n descriptionLocation={DescriptionLocation.None}\n />\n </div>\n );\n }\n const content = (\n <Renderer\n value={cellCtx.getValue() as ColumnValueBase | null | undefined}\n columnName={columnName}\n record={rowRecord}\n // The grid passes the bound-column metadata through to\n // consumer cell renderers. The built-in cells use a\n // `_metadata` underscored signature (they read via\n // `resolveColumnDisplayValue` instead), so undefined is\n // fine for them. The lookup falls back to the row's\n // own type-resolved column metadata via the cached\n // `useTableMetadata` query — when that hasn't landed\n // yet, the renderer just sees `undefined` and reads\n // raw / formatted values directly.\n metadata={gridTableMetadata?.columns.find((c) => c.columnName === columnName)}\n // Surface the surrounding SubGrid's parent record when\n // this grid is mounted as a child of a `<RecordContext>`\n // (the SubGrid case). Lets consumer `cellRenderer`\n // overrides do conditional formatting that keys off the\n // parent — e.g. highlight a contact's industry when it\n // differs from the parent account's. Undefined for\n // standalone `<MainGrid>` usage where there's no parent.\n {...(parentRecord && { parentRecord })}\n // Pull from the live (non-debounced) search box so the\n // highlight tracks the input — feels more responsive than\n // waiting for the debounce to fire and never highlighting\n // until results arrive.\n searchText={searchText || null}\n />\n );\n // Per-row link gate. `hasRowOpenAffordance` only tells us that\n // SOME button registered — but a grid with just the New button\n // (create-reopen only, no open handler) shouldn't surface a\n // link affordance on already-persisted rows that have no\n // dispatch target. Pick the per-row applicable handler:\n // - Pending-create rows route to create-reopen.\n // - Persisted rows route to the open handler.\n // If the applicable handler isn't registered, drop back to\n // plain text so the cell doesn't pretend to be clickable.\n const rowHasOpenAffordance = isPendingCreateRow\n ? hasCreateReopenHandler\n : hasOpenHandler;\n if (!isPrimaryNameLinkTarget || !rowHasOpenAffordance) return content;\n // Render as a Fluent-styled link. `preventDefault` keeps the\n // anchor from triggering a browser navigation (we route via\n // the registered edit handler, which may be a react-router\n // navigate or a future dialog open); `stopPropagation` keeps\n // the row's select-on-click (if any future wiring adds that)\n // from also firing. The handler is read from the ref at click\n // time — guards against the handler being torn down between\n // render and click (an unlikely interleaving, but cheap to\n // cover).\n return (\n <a\n href=\"#\"\n className={styles.primaryNameLink}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n dispatchOpen(cellCtx.row.original);\n }}\n >\n {content}\n </a>\n );\n },\n // Floor for user resize — keep columns from collapsing to a\n // point where neither the header label nor the resize handle\n // is hittable. 40px ≈ checkbox-column + handle width and\n // matches Fluent DataGrid's own minimum.\n minSize: 40,\n ...(width !== undefined && { size: width }),\n // Per-column alignment override declared on `<GridColumn>`.\n // Body cell + header cell read this off `columnDef.meta` to\n // apply `textAlign` / `justifyContent`. No type-driven default\n // for now — alignment is purely opt-in via declaration.\n ...(declared?.align && { meta: { align: declared.align } }),\n },\n );\n };\n\n // Build a TanStack `display` column for a `<GridTemplateColumn>`\n // declaration. No accessor (no single bound column to read from);\n // the renderer reads whatever it needs from `record.properties` /\n // `record.formattedValues`. The `dependsOnMetadata` lookup map is\n // computed lazily per cell so it doesn't allocate when the\n // consumer's renderer doesn't read it.\n const buildTemplateColumnDef = (\n props: GridTemplateColumnProps & { id: string },\n ) => {\n // TanStack's `size` is `number` — it drives the resize handle's\n // starting pixel width when the user grabs the column edge.\n // Number widths flow straight through; pixel-string widths\n // (\"240px\") get parsed back to a number for that starting value.\n // Non-pixel CSS lengths (\"20%\", \"auto\") leave `size` undefined and\n // are applied via the header `<th style={{width}}>` path below —\n // the table's `table-layout: fixed` propagates the header width\n // to all body cells.\n const width =\n typeof props.width === 'number'\n ? props.width\n : typeof props.width === 'string'\n ? parsePixelWidth(props.width)\n : undefined;\n return columnHelper.display({\n id: props.id,\n header: () => props.displayName,\n cell: (cellCtx) => {\n const rowRecord = cellCtx.row.original;\n // Build the dependsOnMetadata lookup against the grid's\n // primary table metadata. Entries that don't resolve\n // (dot-notation linked-table columns, mistyped names, or\n // metadata not yet loaded) come back as undefined — same\n // shape consumers see when querying `record.properties` for\n // a missing column.\n const dependsOnMetadata: Record<\n string,\n ColumnMetadataBase | undefined\n > = {};\n if (props.dependsOn) {\n for (const dep of props.dependsOn) {\n dependsOnMetadata[dep] = gridTableMetadata?.columns.find(\n (c) => c.columnName === dep,\n );\n }\n }\n const rendererProps: GridTemplateColumnCellRendererProps = {\n id: props.id,\n record: rowRecord,\n ...(parentRecord && { parentRecord }),\n searchText: searchText || null,\n dependsOnMetadata,\n };\n // Inline-edit branch: when the grid is editable and the\n // consumer supplied an `editRenderer`, invoke it instead.\n // The renderer runs inside the per-row <RecordContext> mounted\n // at the <tr> level (same scope the bound-column ColumnEdit\n // path uses), so dropped-in `<ColumnEdit>` children find their\n // record store automatically — and the wrapper component\n // below reads setColumnValue out of that same context to\n // pass into the renderer's props for the imperative path.\n const rowId = rowRecord.id;\n const isPendingCreateRow =\n !!rowId && rowId.startsWith('__pending-create-');\n if (editable && !isPendingCreateRow && props.editRenderer) {\n return (\n <TemplateColumnEditCell\n rendererProps={rendererProps}\n editRenderer={props.editRenderer}\n inlineEditCellClass={styles.inlineEditCell}\n />\n );\n }\n return props.cellRenderer(rendererProps);\n },\n minSize: 40,\n ...(width !== undefined && { size: width }),\n ...(props.align && { meta: { align: props.align } }),\n });\n };\n\n // Interleave regular + template column defs in declaration order.\n // The legacy view-driven path (no declared columns) falls through\n // to mapping over the server-resolved column list.\n if (!hasDeclaredColumns) {\n return effectiveResolvedColumns.map(buildRegularColumnDef);\n }\n const defs: ReturnType<typeof buildRegularColumnDef>[] = [];\n for (const decl of orderedDeclarations) {\n if (decl.kind === 'column') {\n if (decl.props.visible === false) continue;\n const resolved = effectiveColumnsByName.get(decl.props.columnName);\n if (!resolved) continue; // already warned in effectiveResolvedColumns\n defs.push(buildRegularColumnDef(resolved));\n } else {\n if (decl.props.visible === false) continue;\n defs.push(\n buildTemplateColumnDef(decl.props) as unknown as ReturnType<\n typeof buildRegularColumnDef\n >,\n );\n }\n }\n return defs;\n // searchText is in deps so column defs re-create when the search\n // box changes — without that, TanStack reuses the cached cell\n // function whose closure captured the old searchText, and the\n // <Highlighter> never updates.\n // hasOpenHandler / allowNavigateOnPrimaryNameClick / styles are in\n // deps so the primary-name cell flips between plain text and link\n // when an edit button mounts/unmounts (or the consumer toggles the\n // prop). The handler identity itself isn't a dep — it's read from\n // `openHandlerRef.current` at click time so handler churn doesn't\n // invalidate the column defs (see registerOpenHandler comments).\n }, [\n effectiveResolvedColumns,\n effectiveColumnsByName,\n declaredColumnsByName,\n hasDeclaredColumns,\n orderedDeclarations,\n cellRenderers,\n typeRenderers,\n searchText,\n hasRowOpenAffordance,\n hasOpenHandler,\n hasCreateReopenHandler,\n dispatchOpen,\n allowNavigateOnPrimaryNameClick,\n styles,\n editable,\n parentRecord,\n gridTableMetadata,\n ]);\n\n const pageRecords = dataSource\n ? (dataSource.rows as readonly TableRecord[])\n : (recordsResult.data?.tableRecords ?? EMPTY_RECORDS);\n\n // Accumulator for infinite-scroll. In paged mode this stays `null` and\n // the grid renders the current page's `pageRecords` directly. In\n // virtualize mode each freshly-landed page is appended; the user sees a\n // single growing list. Reset by the dep-watching effect below when any\n // identity-changing input (view / sort / search / filters / pageSize /\n // tableName) shifts — a different result set, can't keep stale rows.\n const [accumulatedRows, setAccumulatedRows] = useState<readonly TableRecord[] | null>(null);\n\n // Reset key: anything that would invalidate the accumulated row set.\n // Includes the structural filters key (same one the search-request memo\n // uses) so identity-only filter churn doesn't reset.\n const accumulatorResetKey = useMemo(\n () =>\n [\n resolvedViewId ?? '',\n useInlineFetchXml ? (inlineFetchXml ?? '') : '',\n debouncedSearchText,\n sort.map((s) => `${s.columnName}:${s.descending ? 'd' : 'a'}`).join('|'),\n filtersKey,\n pageSize,\n tableName ?? '',\n ].join('|'),\n [resolvedViewId, useInlineFetchXml, inlineFetchXml, debouncedSearchText, sort, filtersKey, pageSize, tableName],\n );\n\n // Snap the virtualized view back to a fresh page-1 state: empty the\n // accumulator, reset the page index, and scroll the table container to\n // the top. Called by both the reset effect (sort / view / search /\n // pageSize / filter change) and the refresh path so all flows feel the\n // same — \"back to top with fresh data\" — instead of having different\n // sub-flows leave the scroll position dangling past the new content's\n // end while the IntersectionObserver fires transient \"load page 2\"\n // requests before the dust settles.\n const resetVirtualizedView = useCallback(() => {\n setAccumulatedRows([]);\n setPage(1);\n tableContainerRef.current?.scrollTo({ top: 0 });\n }, []);\n\n // Reset the accumulator (and page) on any reset-key change in virtualize\n // mode. Also clears when switching INTO paged mode so a re-entry into\n // virtualize starts fresh.\n useEffect(() => {\n if (isVirtualize) {\n resetVirtualizedView();\n } else {\n setAccumulatedRows(null);\n }\n // `resetVirtualizedView` is stable; deps tracked via `isVirtualize` +\n // the structural reset key (same invariant the view/sort/search\n // handlers maintain).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isVirtualize, accumulatorResetKey]);\n\n // Append each freshly-fetched page to the accumulator. Gate on success\n // so an in-flight fetch doesn't append the previous page's `pageRecords`\n // a second time (keepPreviousData holds the data steady through the\n // refetch, which would otherwise duplicate rows on every page change).\n useEffect(() => {\n if (!isVirtualize) return;\n if (recordsResult.status !== QueryStatus.Success) return;\n if (pageRecords.length === 0) return;\n setAccumulatedRows((prev) => {\n const base = prev ?? [];\n // Defensive dedup by record id — if the same fetch resolved twice\n // (StrictMode double-invocations in dev, network retry, etc.) we\n // don't want to double-list the same rows. Server-side id is the\n // dedup key; pending-create synthetic ids never land here because\n // they live in `pendingRowCreates`, not `pageRecords`.\n const seen = new Set<string>();\n for (const r of base) if (r.id) seen.add(r.id);\n const additions = pageRecords.filter((r) => r.id && !seen.has(r.id));\n if (additions.length === 0) return base;\n return [...base, ...additions];\n });\n }, [isVirtualize, recordsResult.status, pageRecords]);\n\n // The \"current rows\" source: in virtualize mode it's the accumulator\n // (which already includes every page the user has scrolled past); in\n // paged mode it's the current page.\n const data = isVirtualize ? (accumulatedRows ?? EMPTY_RECORDS) : pageRecords;\n\n // Pending row-change queues (updates from OpenRecordGridButton, creates\n // from NewRecordGridButton). Declared here — earlier in the function\n // body than the callbacks below — because the `displayedData` /\n // `pendingCreateIds` memos that follow need to read from them.\n // The callbacks (`queueRowUpdate`, `queueRowCreate`, etc.) and the\n // ref mirrors land further down alongside the descendant-registration\n // wiring.\n const [pendingRowUpdates, setPendingRowUpdates] = useState<\n ReadonlyMap<string, PendingRowUpdate>\n >(() => new Map());\n const [pendingRowCreates, setPendingRowCreates] = useState<\n readonly PendingRowCreate[]\n >(() => []);\n // M2M association / disassociation queues. Populated by\n // <LinkExistingRecordGridButton> / <UnlinkExistingRecordGridButton> in\n // their `behavior=\"withGridContext\"` mode. Emitted as Associate /\n // Disassociate requests by `buildPendingRowChangeRequests` and sent\n // through the parent context's transactional save. The two queues are\n // mutually exclusive per record id — adding to one removes a matching\n // entry from the other (the net intent is \"keep linked\" vs \"unlink\").\n //\n // Associates carry the full `TableRecord` (not just a reference) so the\n // grid can pin them as visible rows while they're queued — same UX\n // shape as `pendingRowCreates`. The disassociate queue stays as\n // references because those rows are already on screen from the server\n // fetch; the queue only needs enough identity to compose the\n // DisassociateRequest at save time.\n const [pendingRowAssociates, setPendingRowAssociates] = useState<\n readonly TableRecord[]\n >(() => []);\n const [pendingRowDisassociates, setPendingRowDisassociates] = useState<\n readonly TableRecordReference[]\n >(() => []);\n // Pending row deletes — populated by `<DeleteRecordGridButton>` in\n // its `behavior=\"withGridContext\"` mode. The grid emits one\n // `DeleteRequest` per entry from `buildPendingRowChangeRequests` so the\n // surrounding `<MainContext>.saveAll` batches them with other dirty\n // siblings. Empty unless a queued delete fires; refreshed away after a\n // successful save round-trip via `clearPendingRowUpdates`.\n const [pendingRowDeletes, setPendingRowDeletes] = useState<\n readonly TableRecordReference[]\n >(() => []);\n\n // Compose the displayed rows: pending creates first, then server-fetched\n // records with any queued pending-update columns overlaid onto each\n // matching row. Mirrors Blazor's grid behavior of (a) showing queued\n // new rows pinned at the top so the user can see their work-in-progress\n // before hitting Save, and (b) reflecting queued column edits in the\n // grid cells immediately — a column edited via dialog Save shows the\n // new value in the grid right away, not the stale server value.\n //\n // Pending creates are assigned a synthetic temp id\n // (`__pending-create-<idx>`) so TanStack's row-keying (`getRowId`) has a\n // stable per-row identity to attach selection / row state to — the real\n // server-assigned id lands later via the post-save refresh round-trip,\n // at which point the row leaves the queue and shows up as a normal\n // server row.\n //\n // Pending updates merge per-column onto each existing row's properties:\n // - `null` queue value ⇒ delete the column (matches the wire's\n // explicit-clear convention).\n // - non-null ⇒ overwrite. The original `row` reference isn't\n // mutated; a fresh object is produced so TanStack sees the cell\n // value as changed and re-renders.\n const displayedData = useMemo<TableRecord[]>(() => {\n const overlaid: TableRecord[] =\n pendingRowUpdates.size === 0\n ? [...data]\n : data.map((row) => {\n const rowId = row.id;\n if (!rowId) return row;\n const pending = pendingRowUpdates.get(rowId);\n if (!pending) return row;\n const properties: Record<string, ColumnValueBase> = {\n ...(row.properties ?? {}),\n };\n for (const [col, val] of pending.dirty) {\n if (val === null) {\n delete properties[col];\n } else {\n properties[col] = val;\n }\n }\n return { ...row, properties };\n });\n if (pendingRowCreates.length === 0 && pendingRowAssociates.length === 0) {\n return overlaid;\n }\n const pendingCreateRows: TableRecord[] = pendingRowCreates.map((create, idx) => ({\n ...create.record,\n id: `__pending-create-${idx}`,\n }));\n // Pending-associate rows keep their real server-assigned id — the\n // record already exists in Dataverse, it just isn't linked yet. No\n // synthetic id needed (and using the real id means selection / the\n // Unlink button / future inline edits all key off the same value the\n // post-save refresh will hand back).\n return [...pendingCreateRows, ...pendingRowAssociates, ...overlaid];\n }, [pendingRowCreates, pendingRowAssociates, pendingRowUpdates, data]);\n\n // Quick-membership Set keyed by the synthetic ids the prepend just\n // produced — read inside the body-row render to decide which icon to\n // surface in the pending-operation column. Cheaper than `Array.find`\n // per row, and the prefix-string approach loses to typos; an explicit\n // Set is more robust.\n const pendingCreateIds = useMemo(() => {\n const set = new Set<string>();\n for (let i = 0; i < pendingRowCreates.length; i++) {\n set.add(`__pending-create-${i}`);\n }\n return set;\n }, [pendingRowCreates]);\n\n // Parallel set for pending-associate rows. Keyed by the real record\n // id (associates are existing server records, not synthetic), used by\n // the body-row render to surface the LinkAdd icon in the pending-\n // operation column.\n const pendingAssociateIds = useMemo(() => {\n const set = new Set<string>();\n for (const r of pendingRowAssociates) {\n if (r.id) set.add(r.id);\n }\n return set;\n }, [pendingRowAssociates]);\n\n // Same pattern for pending-disassociate rows — the row keeps its\n // server-fetched data on screen but the pending-operation column\n // renders the LinkDismiss icon so the user can see which rows will\n // be unlinked on the next save.\n const pendingDisassociateIds = useMemo(() => {\n const set = new Set<string>();\n for (const r of pendingRowDisassociates) {\n if (r.id) set.add(r.id);\n }\n return set;\n }, [pendingRowDisassociates]);\n\n // Same shape as `pendingDisassociateIds` for the pending-delete queue\n // — read in the body-row render loop to surface the Trash icon in the\n // pending-operation column. Rows stay visible in the grid (so the user\n // can review what's staged) until the surrounding `MainContext.saveAll`\n // commits the DeleteRequests.\n const pendingDeleteIds = useMemo(() => {\n const set = new Set<string>();\n for (const r of pendingRowDeletes) {\n if (r.id) set.add(r.id);\n }\n return set;\n }, [pendingRowDeletes]);\n\n const table = useReactTable({\n data: displayedData,\n columns,\n getCoreRowModel: getCoreRowModel(),\n // TanStack's row id defaults to the row index, which means\n // `rowSelection` keys would collide across pages. Key on the\n // record's primary id instead so the selection map is stable.\n getRowId: (row, index) => row.id ?? String(index),\n enableRowSelection: selectionMode !== 'none',\n enableMultiRowSelection: selectionMode === 'multiple',\n // Column resizing: drag the right edge of any header to resize.\n // `onChange` mode updates `columnSizing` on every mousemove so cells\n // track the drag live; cheap at our page size (25 rows × few\n // columns). `'onEnd'` would defer until release but requires a\n // floating-indicator div to give the user feedback during drag.\n enableColumnResizing: true,\n columnResizeMode: 'onChange',\n state: { rowSelection, columnSizing },\n onRowSelectionChange: (updater) => {\n const next =\n typeof updater === 'function' ? updater(rowSelection) : updater;\n if (isSelectionControlled) {\n // Consumer owns selection — derive the records they'd want and\n // call onSelectedRecordsChange. They flow the new value back via\n // the `selectedRecords` prop. Records come from the current page\n // (`displayedData`); rows off-page can't be toggled by clicks\n // anyway, so the new map is a subset of what's visible.\n if (!onSelectedRecordsChangeRef.current) return;\n const records = displayedData.filter((r) => r.id && next[r.id]);\n onSelectedRecordsChangeRef.current(records);\n } else {\n setInternalRowSelection(next);\n }\n },\n onColumnSizingChange: setColumnSizing,\n });\n\n // Derive the live array of selected `TableRecord`s. Recomputes whenever\n // the underlying `rowSelection` map or `data` (current page rows) changes\n // — both fed into the dep list. Always `EMPTY_RECORDS` when\n // `selectionMode === 'none'` so the grid context exposes a stable empty\n // reference (no checkbox column rendered, nothing selectable). Same\n // computation Blazor's `GridContext.SelectedRecords` does.\n const selectedRecords = useMemo<readonly TableRecord[]>(() => {\n if (selectionMode === 'none') return EMPTY_RECORDS;\n return table.getSelectedRowModel().rows.map((r) => r.original);\n // `table` is stable; `rowSelection` is the actual change driver.\n // `data` is included because TanStack rebuilds the selection model\n // from data + rowSelection — a fresh data array (new page) needs to\n // flush stale selections.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [rowSelection, selectionMode, data]);\n\n // Push the current selection up to the consumer when one is wired.\n // Skipped when:\n // - no callback is wired (avoid an empty-array push on every render\n // in the selection-disabled case), or\n // - the grid is controlled (`selectedRecords` prop set); the\n // TanStack onRowSelectionChange handler + `requestEmptySelection`\n // already call the callback directly, and echoing the derived\n // value here would just bounce the prop back to the consumer.\n useEffect(() => {\n if (!onSelectedRecordsChange) return;\n if (isSelectionControlled) return;\n onSelectedRecordsChange(selectedRecords);\n }, [selectedRecords, onSelectedRecordsChange, isSelectionControlled]);\n\n // --- Status flags --------------------------------------------------------\n //\n // Two distinct loading conditions, so the UI can pick the right surface:\n //\n // - `isLoading`: cold start — no data to show yet, so the body collapses\n // to a centered \"Loading…\" status row. True when we're still\n // resolving the view list or the first records request hasn't\n // returned yet (`status === 'loading'`).\n // - `isRefetching`: a follow-up request is in flight (new page, new\n // sort, search keystroke, etc.) but we already have prior data to\n // keep on screen. Drives a translucent scrim over the existing\n // rows so the user sees their context preserved during the swap.\n //\n // `useGridData` is opted in to `keepPreviousData: true` above, so\n // after the first success the hook stays in `status === 'success'` and\n // flips `isFetching` for deps-change refetches — that's the signal the\n // scrim watches.\n const isResolvingView = !!tableName && !viewId && viewsResult.status === QueryStatus.Loading;\n // In dataSource mode, query status comes from the shared store. The\n // view-resolution path (loading the view metadata locally) still\n // affects `isLoading` in standalone mode; dataSource consumers\n // typically pin the source to a known viewId so isResolvingView is\n // false anyway.\n const isLoading = dataSource\n ? dataSource.status === QueryStatus.Loading\n : isResolvingView || recordsResult.status === QueryStatus.Loading;\n const isRefetching = dataSource\n ? !isLoading && dataSource.isFetching\n : !isLoading && recordsResult.isFetching;\n const error = dataSource\n ? dataSource.error\n : (viewsResult.error ?? recordsResult.error);\n\n // --- Grid context for descendant buttons --------------------------------\n //\n // Everything `<NewRecordGridButton>` / `<DeleteRecordGridButton>` /\n // `<RefreshGridButton>` and friends need to read or trigger from the\n // grid. `refresh` is wired to the records hook's `refetch()` so a\n // button can await it for sequenced \"delete then refresh\" flows\n // (matches the Blazor `GridContext.RefreshAsync()` semantics).\n //\n // Virtualize mode: a plain refetch would only re-fire the latest page's\n // request, and the accumulator-dedupe by record id would mask any\n // server-side changes to rows already on screen (and miss inserts /\n // deletes on earlier pages entirely). Treat refresh as \"back to top with\n // fresh data\" — same shape as sort / view / search changes, which all\n // route through `resetVirtualizedView`. If the user was already on\n // page 1 (hasn't scrolled), `resetVirtualizedView`'s `setPage(1)` is a\n // no-op and won't trigger the request-dep change, so we also call\n // `refetch()` to guarantee a fresh fetch in that case. Mirrors how a\n // \"pull to refresh\" feels in typical infinite-scroll UIs.\n // `onRefresh` stashed in a ref so a parent passing it inline (a fresh\n // arrow function each render) doesn't churn the `refresh` callback's\n // identity. Effect-list dep arrays elsewhere depend on `refresh` being\n // stable across renders with no behavior change. Mirrors how\n // `onSelectedRecordsChange` is handled.\n const onRefreshRef = useRef(onRefresh);\n onRefreshRef.current = onRefresh;\n const refresh = useCallback(async () => {\n // dataSource mode: the shared store owns the fetch — call its\n // refresh and skip the local useGridData.refetch() (which is\n // idle in this mode anyway). Virtualize-mode reset still applies\n // locally because the accumulator + scroll position are grid-\n // owned per-instance state.\n if (dataSource) {\n if (pagingMode === PagingMode.Virtualize) {\n resetVirtualizedView();\n }\n await dataSource.refresh();\n onRefreshRef.current?.();\n return;\n }\n if (pagingMode === PagingMode.Virtualize) {\n const wasOnFirstPage = page === 1;\n resetVirtualizedView();\n if (wasOnFirstPage) {\n await recordsResult.refetch();\n }\n onRefreshRef.current?.();\n return;\n }\n await recordsResult.refetch();\n // Fire onRefresh AFTER the records reload so consumers observing\n // it see the new data on the refresh tick rather than the\n // previous tick's stale snapshot.\n onRefreshRef.current?.();\n }, [dataSource, pagingMode, page, recordsResult.refetch, resetVirtualizedView]);\n\n // --- Pending row updates (dialog-based Edit queue) ---------------------\n //\n // Map<recordId, PendingRowUpdate> of edits staged by an\n // <OpenRecordGridButton behavior=\"withGridContext\"> dialog session. The\n // grid's outward `getRequests` (registered with the parent MainContext\n // below) converts the queue into UpdateRequests at save time, so the\n // surrounding context's transactional save batches them alongside its\n // own record update. Mirrors Blazor's `GridBase._rowsToUpdate`.\n //\n // Stable refs alongside state so the descendant `getRequests` callback\n // (built once via useCallback for stability — see the registry effect\n // below) can read the latest queue without churning re-registrations.\n // State declarations themselves are hoisted earlier in the function\n // body — see the `displayedData` block — so the data composition can\n // read from them. Refs land here next to the callbacks that use them.\n const pendingRowUpdatesRef = useRef(pendingRowUpdates);\n pendingRowUpdatesRef.current = pendingRowUpdates;\n const pendingRowCreatesRef = useRef(pendingRowCreates);\n pendingRowCreatesRef.current = pendingRowCreates;\n const pendingRowAssociatesRef = useRef(pendingRowAssociates);\n pendingRowAssociatesRef.current = pendingRowAssociates;\n const pendingRowDeletesRef = useRef(pendingRowDeletes);\n pendingRowDeletesRef.current = pendingRowDeletes;\n const pendingRowDisassociatesRef = useRef(pendingRowDisassociates);\n pendingRowDisassociatesRef.current = pendingRowDisassociates;\n // Stash the M:N-detection result in a ref so the stable\n // `buildPendingRowChangeRequests` callback can read the latest value\n // without re-registering with the parent on every metadata update.\n const isManyToManySubGridRef = useRef(isManyToManySubGrid);\n isManyToManySubGridRef.current = isManyToManySubGrid;\n\n const queueRowUpdate = useCallback((update: PendingRowUpdate) => {\n setPendingRowUpdates((prev) => {\n const next = new Map(prev);\n // Empty dirty map ⇒ the dialog session reverted all changes\n // (back to the server baseline). Drop the queue entry entirely\n // so subsequent operations see this row as clean — matches the\n // user's \"no net change\" intent. Otherwise replace the existing\n // entry wholesale: the caller's dirty map IS the complete\n // current set of pending changes for this row (the dialog\n // tracks accumulated dirty state via `pendingChanges`, which\n // includes any prior overlay seeded via\n // `initialPendingChanges`), so merging would double-up.\n if (update.dirty.size === 0) {\n next.delete(update.recordId);\n } else {\n next.set(update.recordId, update);\n }\n return next;\n });\n }, []);\n\n const queueRowCreate = useCallback((create: PendingRowCreate) => {\n setPendingRowCreates((prev) => [...prev, create]);\n }, []);\n\n const updatePendingRowCreate = useCallback(\n (index: number, create: PendingRowCreate) => {\n setPendingRowCreates((prev) => {\n if (index < 0 || index >= prev.length) return prev;\n const next = prev.slice();\n next[index] = create;\n return next;\n });\n },\n [],\n );\n\n const clearPendingRowUpdates = useCallback(() => {\n setPendingRowUpdates(new Map());\n setPendingRowCreates([]);\n setPendingRowAssociates([]);\n setPendingRowDisassociates([]);\n setPendingRowDeletes([]);\n }, []);\n\n // M2M queue updaters. Both share the dedup/mutex helper below:\n // - Adding a record removes any matching entry from the opposite\n // queue (the net intent is \"keep / unlink\" — they cancel out).\n // - Within the target queue, entries are deduped by `tableName + id`\n // so re-clicking Link/Unlink on the same row is idempotent.\n //\n // Associates store the full `TableRecord` so the grid can pin them as\n // visible rows; disassociates only need a reference because those\n // rows are already on screen from the server fetch.\n const queueRowAssociates = useCallback(\n (records: readonly TableRecord[]) => {\n if (records.length === 0) return;\n const toAdd = records;\n setPendingRowDisassociates((prev) =>\n prev.filter(\n (r) => !toAdd.some((a) => a.tableName === r.tableName && a.id === r.id),\n ),\n );\n setPendingRowAssociates((prev) => {\n const seen = new Set(prev.map((r) => `${r.tableName}:${r.id}`));\n const next = prev.slice();\n for (const r of toAdd) {\n const key = `${r.tableName}:${r.id}`;\n if (!seen.has(key)) {\n seen.add(key);\n next.push(r);\n }\n }\n return next.length === prev.length ? prev : next;\n });\n },\n [],\n );\n\n const queueRowDisassociates = useCallback(\n (records: readonly TableRecordReference[]) => {\n if (records.length === 0) return;\n // Bucket the incoming records by their current grid state so\n // Unlink \"does the right thing\" for each kind of pending row:\n // - Pending-create rows (synthetic id `__pending-create-<idx>`):\n // drop the entry from the create queue. The record has no\n // server id yet, so a DisassociateRequest can't target it.\n // - Pending-associate rows (real id matching an entry in the\n // associate queue): drop from the associate queue. The link\n // was never committed, so there's nothing to disassociate\n // on the server.\n // - Everything else (real server-associated rows): queue a\n // DisassociateRequest as usual.\n const createSyntheticIdsToDrop = new Set<string>();\n const realIds: TableRecordReference[] = [];\n for (const r of records) {\n if (!r.id) continue;\n if (r.id.startsWith('__pending-create-')) {\n createSyntheticIdsToDrop.add(r.id);\n } else {\n realIds.push(r);\n }\n }\n if (createSyntheticIdsToDrop.size > 0) {\n setPendingRowCreates((prev) => {\n const next = prev.filter(\n (_, idx) => !createSyntheticIdsToDrop.has(`__pending-create-${idx}`),\n );\n return next.length === prev.length ? prev : next;\n });\n }\n // Split the real-id records based on the LIVE associate queue\n // (read via ref so the same render cycle's earlier setters are\n // visible). Records present in the associate queue: drop them\n // there — never reach the disassociate path. Records absent\n // from the associate queue: must be server-side associations,\n // so they belong on the disassociate path.\n const currentAssociates = pendingRowAssociatesRef.current;\n const associatesToDrop: TableRecordReference[] = [];\n const realDisassociates: TableRecordReference[] = [];\n for (const r of realIds) {\n const inAssociates = currentAssociates.some(\n (a) => a.tableName === r.tableName && a.id === r.id,\n );\n if (inAssociates) {\n associatesToDrop.push(r);\n } else {\n realDisassociates.push(r);\n }\n }\n if (associatesToDrop.length > 0) {\n setPendingRowAssociates((prev) =>\n prev.filter(\n (r) =>\n !associatesToDrop.some(\n (a) => a.tableName === r.tableName && a.id === r.id,\n ),\n ),\n );\n }\n if (realDisassociates.length > 0) {\n setPendingRowDisassociates((prev) => {\n const seen = new Set(prev.map((r) => `${r.tableName}:${r.id}`));\n const next = prev.slice();\n for (const r of realDisassociates) {\n const key = `${r.tableName}:${r.id}`;\n if (!seen.has(key)) {\n seen.add(key);\n next.push(r);\n }\n }\n return next.length === prev.length ? prev : next;\n });\n }\n },\n [],\n );\n\n // Queue rows for delete-on-next-save. Bucketing similar to\n // `queueRowDisassociates`:\n // - Pending-create rows (synthetic id): just drop from the create\n // queue; they were never persisted server-side so there's no\n // DeleteRequest to emit later.\n // - Real server-side ids: dedup against the existing delete queue\n // and append.\n const queueRowDeletes = useCallback(\n (records: readonly TableRecordReference[]) => {\n if (records.length === 0) return;\n const syntheticIdsToDrop = new Set<string>();\n const realIds: TableRecordReference[] = [];\n for (const r of records) {\n if (!r.id) continue;\n if (r.id.startsWith('__pending-create-')) {\n syntheticIdsToDrop.add(r.id);\n } else {\n realIds.push(r);\n }\n }\n if (syntheticIdsToDrop.size > 0) {\n setPendingRowCreates((prev) => {\n const next = prev.filter(\n (_, idx) => !syntheticIdsToDrop.has(`__pending-create-${idx}`),\n );\n return next.length === prev.length ? prev : next;\n });\n }\n if (realIds.length > 0) {\n setPendingRowDeletes((prev) => {\n const seen = new Set<string>();\n for (const r of prev) seen.add(`${r.tableName}:${r.id}`);\n const next = [...prev];\n for (const r of realIds) {\n const key = `${r.tableName}:${r.id}`;\n if (!seen.has(key)) {\n seen.add(key);\n next.push(r);\n }\n }\n return next.length === prev.length ? prev : next;\n });\n }\n },\n [],\n );\n\n // Imperative selection-clear, exposed on the GridContext. Mirrors\n // Blazor's `GridBase.ClearSelectionAsync()`. Awaitable for parity\n // (Blazor's version awaits a `StateHasChanged` invocation); React's\n // setter is synchronous, so the promise resolves immediately and the\n // effect lands on the next render. Delegates to\n // `requestEmptySelection`, which is controlled-aware (notifies the\n // consumer instead of mutating state when `selectedRecords` is bound).\n const clearSelection = useCallback(async () => {\n requestEmptySelection();\n }, [requestEmptySelection]);\n\n // Grid-level validation gate. Returns `true` today — per-row\n // validation is enforced inside the dialog before the edit lands in\n // `pendingRowUpdates`, so the queue is validation-clean by\n // construction. Surface kept stable so the inline-editable phase can\n // plug in real structural validation (e.g. \"at least one row required\")\n // without changing the GridContext contract.\n const validate = useCallback(() => true, []);\n\n // Builds the wire-shape request array from the current queues — both\n // pending updates and pending creates, in that order (matches Blazor's\n // `GridBase.GetRequests` which walks `_rowsToUpdate` first, then\n // `_rowsToCreate`). Reading through refs lets the parent's\n // descendant.getRequests call stay a stable callback without\n // re-registration churn as the queues change.\n const buildPendingRowChangeRequests = useCallback((): (\n | UpdateRequest\n | CreateRequest\n | AssociateRequest\n | DisassociateRequest\n | DeleteRequest\n )[] => {\n const updates = pendingRowUpdatesRef.current;\n const creates = pendingRowCreatesRef.current;\n const associates = pendingRowAssociatesRef.current;\n const disassociates = pendingRowDisassociatesRef.current;\n const deletes = pendingRowDeletesRef.current;\n if (\n updates.size === 0 &&\n creates.length === 0 &&\n associates.length === 0 &&\n disassociates.length === 0 &&\n deletes.length === 0\n ) {\n return [];\n }\n const requests: (\n | UpdateRequest\n | CreateRequest\n | AssociateRequest\n | DisassociateRequest\n | DeleteRequest\n )[] = [];\n for (const update of updates.values()) {\n const properties: Record<string, ColumnValueBase | null> = {};\n for (const [col, val] of update.dirty) {\n properties[col] = val;\n }\n requests.push({\n $type: 'UpdateRequest',\n record: {\n id: update.recordId,\n tableName: update.tableName,\n properties: properties as Record<string, ColumnValueBase>,\n },\n });\n }\n for (const create of creates) {\n requests.push({\n $type: 'CreateRequest',\n record: create.record,\n });\n }\n // M2M associate/disassociate. Skipped silently when the grid isn't\n // a SubGrid (no parentRecord / relationshipName / parent reference id)\n // — without those, the wire request can't be composed and shipping\n // it would 400 at the server. The link/unlink buttons gate themselves\n // on the same SubGrid invariant up front so this branch should\n // rarely actually fire with missing inputs.\n //\n // `getRecordReferenceId` covers both saved and create-mode parents:\n // returns the real `id` if present, falls back to `_idForCreate`\n // (the temp GUID `<RecordContext>` seeds in create mode). The\n // server's `ResolveAssociateRecordAsync` accepts either shape and\n // matches the parent reference against an in-batch CreateRequest's\n // `_idForCreate`, so a Create-parent + Associate-child pair within\n // one `ExecuteMultiple` resolves correctly.\n const parentRefId = parentRecord ? getRecordReferenceId(parentRecord) : undefined;\n if (parentRecord?.tableName && parentRefId && relationshipName) {\n const parentRef: TableRecordReference = {\n tableName: parentRecord.tableName,\n id: parentRefId,\n };\n // Pending-creates inside an M:N SubGrid additionally need an\n // AssociateRequest pairing each newly-created record's pre-gen id\n // with the parent. Without this, a \"New region\" inside the Linked\n // Regions SubGrid would create the region but never link it to\n // the account; on save+refresh it wouldn't appear in the grid\n // because the relationship-filtered fetch wouldn't see it.\n // `getRecordReferenceId` resolves the new record's `_idForCreate`\n // — the same temp GUID rides into the CreateRequest body, so the\n // Associate's parent + child references match the in-batch\n // creates and Dataverse processes them in order. 1:N SubGrids\n // don't need this — the parent-FK pre-fill already encodes the\n // link via a column on the new record.\n if (isManyToManySubGridRef.current && creates.length > 0) {\n const createRefs: TableRecordReference[] = [];\n for (const c of creates) {\n if (!c.record.tableName) continue;\n const createRefId = getRecordReferenceId(c.record);\n if (!createRefId) continue;\n createRefs.push({ tableName: c.record.tableName, id: createRefId });\n }\n if (createRefs.length > 0) {\n requests.push({\n $type: 'AssociateRequest',\n record: parentRef,\n relationshipName,\n relatedRecords: createRefs,\n });\n }\n }\n if (associates.length > 0) {\n // Strip the full record down to {tableName, id} for the wire —\n // the grid carries the whole TableRecord in-queue so it can\n // render the pending row, but AssociateRequest only needs an\n // identity reference. Records missing an id are dropped (they\n // could never round-trip a valid AssociateRequest anyway, and\n // the dedup helper already gates against them).\n const relatedRecords: TableRecordReference[] = [];\n for (const a of associates) {\n if (!a.tableName || !a.id) continue;\n relatedRecords.push({ tableName: a.tableName, id: a.id });\n }\n if (relatedRecords.length > 0) {\n requests.push({\n $type: 'AssociateRequest',\n record: parentRef,\n relationshipName,\n relatedRecords,\n });\n }\n }\n if (disassociates.length > 0) {\n requests.push({\n $type: 'DisassociateRequest',\n record: parentRef,\n relationshipName,\n relatedRecords: disassociates as TableRecordReference[],\n });\n }\n }\n // Pending deletes — emitted last so updates / creates / associations\n // land before the rows they target disappear. (Within an\n // ExecuteMultiple batch the server processes requests in order, so\n // an update against a row in the same batch's delete list would\n // 404 if the delete went first.) Matches Blazor's\n // `GridBase.GetRequests` ordering.\n for (const ref of deletes) {\n if (!ref.tableName || !ref.id) continue;\n requests.push({\n $type: 'DeleteRequest',\n record: { tableName: ref.tableName, id: ref.id },\n });\n }\n return requests;\n }, [parentRecord, relationshipName]);\n\n // --- Parent MainContext registration -----------------------------------\n //\n // Mirrors Blazor's `GridBase.OnInitializedAsync` →\n // `Context.RegisterGrid(this)` (see `MainContext.razor.cs:295`): every\n // grid hosted inside a `<MainContext>` / `<RecordContext>` opts into the\n // parent's descendant registry so the parent's transactional save /\n // refresh-all / aggregate `isDirty` / validate cascade include the grid.\n // Without this, the RefreshContextButton on a record-edit page reloads\n // the main record but leaves the SubGrid's rows stale; SaveContextButton\n // can't see grid-level dirty state; and an unsaved-changes prompt would\n // miss in-progress grid edits.\n //\n // - `getRequests` returns one UpdateRequest per row in the dialog-queue\n // (populated by `<OpenRecordGridButton behavior=\"withGridContext\">`).\n // The future editable-grid phase will add Create/Delete requests from\n // `_rowsToCreate` / `_rowsToDelete`-equivalent slots alongside this.\n // - `validate` returns `true` until grid-level structural validation\n // exists (e.g. invalid queued-row rules).\n // - `refresh` calls the records hook's refetch.\n // - `isDirty` is a ref-backed getter so the registration sticks\n // across queue transitions; `notifyDescendantDirtyChanged` below\n // pokes the parent when the aggregate flips. This used to re-fire\n // the whole effect (re-register the descendant) every time the\n // queue grew or shrank, which cascaded context re-renders through\n // the grid and dropped focus from the active inline editor on the\n // first keystroke.\n const gridIsDirty =\n pendingRowUpdates.size > 0 ||\n pendingRowCreates.length > 0 ||\n pendingRowAssociates.length > 0 ||\n pendingRowDisassociates.length > 0 ||\n pendingRowDeletes.length > 0;\n const gridIsDirtyRef = useRef(gridIsDirty);\n gridIsDirtyRef.current = gridIsDirty;\n useEffect(() => {\n if (!parentMainContext) return;\n return parentMainContext.registerDescendant({\n getRequests: () => buildPendingRowChangeRequests(),\n validate,\n refresh: async () => {\n // Clear the local queue on refresh — a successful save above us\n // commits everything, and the post-save refresh round-trip\n // re-fetches authoritative data. Holding onto stale queued edits\n // past the refresh would re-submit them on the NEXT save.\n clearPendingRowUpdates();\n await refresh();\n },\n // Cancel/Discard: drop the queued row updates / creates / associates\n // / disassociates without touching the server. Doesn't clear the\n // visible selection — that's a separate primitive (clearSelection).\n resetState: clearPendingRowUpdates,\n isDirty: () => gridIsDirtyRef.current,\n });\n // Key on `registerDescendant` identity rather than the parent value\n // object — see record-context.tsx for the infinite-loop rationale.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMainContext?.registerDescendant, refresh, buildPendingRowChangeRequests, clearPendingRowUpdates, validate]);\n // Tell the parent to re-read our aggregate when it flips. Mirrors the\n // RecordContext notify pattern — registration stays stable, only the\n // version counter inside the parent bumps. Read the parent reference\n // via a ref so the effect doesn't re-fire when the outer's value\n // identity churns (which it does every time its own isDirty flips —\n // i.e. exactly when WE'D be notifying it, creating an infinite loop\n // without the ref indirection).\n const parentMainContextRef = useRef(parentMainContext);\n parentMainContextRef.current = parentMainContext;\n useEffect(() => {\n parentMainContextRef.current?.notifyDescendantDirtyChanged();\n }, [gridIsDirty]);\n\n // Sample the table-level permission bitmask off the first record in the\n // current result set. The server stamps every record with the user's\n // Read/Create/Write/Delete/Append/AppendTo flags (see Blazor's\n // PowerPortalsProService.ValidateCanRetrieve). Read straight off the\n // GridDataResponse — the server resolves the current user's table-\n // level mask via the cached `ITablePermissionCache` and stamps it on\n // the response specifically so toolbar buttons can gate \"New\" /\n // \"Delete\" off one authoritative value instead of sampling\n // `data[0].permissions` as a Create/Delete-bit proxy (which gave\n // false negatives on rows whose row-level permission masks didn't\n // happen to include the flag the toolbar cared about, and false\n // positives in the empty-grid case). Mirrors the table-cache lookup\n // Blazor's `NewRecordGridButton` / `DeleteRecordGridButton` already\n // do via DI. `undefined` only when the grid hasn't fetched anything\n // yet; toolbar buttons fall back to optimistic-enabled in that case.\n // In dataSource mode the shared datasource owns the network fetch and\n // exposes its own `tablePermissions`; otherwise read from the local\n // useGridData result. `undefined` only until the first response lands.\n const tablePermissions = dataSource\n ? dataSource.tablePermissions\n : recordsResult.data?.tablePermissions;\n\n const gridContextValue = useMemo<GridContextValue>(\n () => ({\n selectedRecords,\n tableName,\n viewId: resolvedViewId,\n columns: resolvedColumns,\n refresh,\n isDirty: gridIsDirty,\n registerOpenHandler,\n registerCreateReopenHandler,\n dispatchOpen,\n hasRowOpenAffordance,\n tablePermissions,\n pendingRowUpdates,\n pendingRowCreates,\n pendingRowAssociates,\n pendingRowDisassociates,\n parentRecord,\n relationshipName,\n queueRowUpdate,\n queueRowCreate,\n updatePendingRowCreate,\n queueRowAssociates,\n queueRowDisassociates,\n pendingRowDeletes,\n queueRowDeletes,\n clearPendingRowUpdates,\n clearSelection,\n validate,\n loadedRecords: data,\n allowPreviewForFileColumns,\n allowDownloadForFileColumns,\n }),\n [\n selectedRecords,\n tableName,\n resolvedViewId,\n resolvedColumns,\n refresh,\n gridIsDirty,\n registerOpenHandler,\n registerCreateReopenHandler,\n dispatchOpen,\n hasRowOpenAffordance,\n tablePermissions,\n pendingRowUpdates,\n pendingRowCreates,\n pendingRowAssociates,\n pendingRowDisassociates,\n pendingRowDeletes,\n parentRecord,\n relationshipName,\n queueRowUpdate,\n queueRowCreate,\n updatePendingRowCreate,\n queueRowAssociates,\n queueRowDisassociates,\n queueRowDeletes,\n clearPendingRowUpdates,\n clearSelection,\n validate,\n data,\n allowPreviewForFileColumns,\n allowDownloadForFileColumns,\n ],\n );\n\n // --- Paging --------------------------------------------------------------\n //\n // `totalRecordCount` arrives as `number | string | null` — the wire\n // schema widens to string for int64 overflow edge cases (huge datasets\n // beyond JS's safe-integer range, where precision loss is acceptable\n // for display but the JSON serializer plays it safe). Coerce to number\n // here so the paging component can do arithmetic on it without\n // re-checking the union at every use site.\n const totalCount = (() => {\n const raw = dataSource\n ? dataSource.totalCount\n : recordsResult.data?.pagingInfo?.totalRecordCount;\n if (raw == null) return undefined;\n return typeof raw === 'number' ? raw : Number(raw);\n })();\n\n // Virtualize-mode \"more rows available?\" check. `totalCount` is known\n // after the first fetch lands (the server stamps it into every response),\n // and we compare against the accumulator size — not the live `data`\n // length, since `data` already equals the accumulator in virtualize\n // mode and we want the same answer regardless of read path. `undefined`\n // (first fetch in flight) defaults to `true` so the sentinel renders\n // and the initial-pageload trigger still works.\n const hasMoreRowsToLoad =\n isVirtualize &&\n (totalCount === undefined || (accumulatedRows?.length ?? 0) < totalCount);\n\n // Infinite-scroll trigger. When the sentinel enters the table container's\n // viewport AND we're idle (not already fetching) AND more rows remain,\n // bump `page` — the request memo invalidates, `useGridData` fetches the\n // next page, and the append-to-accumulator effect picks it up. Rebuilds\n // the observer when the sentinel ref changes (mount/unmount across the\n // hasMoreRowsToLoad gate above) so it's always pointed at the live node.\n useEffect(() => {\n if (!isVirtualize) return;\n const sentinel = loadMoreSentinelRef.current;\n const root = tableContainerRef.current;\n if (!sentinel || !root) return;\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (!entry?.isIntersecting) return;\n if (recordsResult.isFetching) return;\n if (!hasMoreRowsToLoad) return;\n // Read the live `page` from the closure (re-captured each\n // render via the effect's deps). Functional updaters would\n // bypass the dataSource-mode delegation in the wrapped\n // `setPage`, so we resolve the next value here.\n setPage(page + 1);\n },\n {\n root,\n // Pre-fetch a half-page before the user actually hits bottom so\n // the scroll doesn't visibly stall while the next page loads.\n rootMargin: '200px 0px 200px 0px',\n threshold: 0,\n },\n );\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [isVirtualize, hasMoreRowsToLoad, recordsResult.isFetching, page, setPage]);\n const handlePageChange = (nextPage: number) => {\n if (nextPage === page) return;\n setPage(nextPage);\n // Selection is per-page; advancing strands it on rows that aren't\n // visible anymore, so clear it on every page transition.\n requestEmptySelection();\n writeStateToUrl({ page: nextPage });\n };\n\n // Display name for the currently-selected view (drives the Dropdown's\n // visible value). The dropdown identifies options by view id, but\n // Fluent's Dropdown also wants a human-readable `value` for the\n // input — resolve the localized label via the localizer key\n // `tables.{tableName}.views.{id}.label`. Mirrors Blazor's `ViewSelector`,\n // which renders `_localizer[...]` directly: the cached `displayName` /\n // `name` come from Dataverse in the SDK user's locale (typically English)\n // and don't change with the request culture, so reading them straight\n // would show English regardless of the active locale. The per-LCID\n // `StringLocalizerCache` populated from Dataverse `LocalizedLabels` is\n // the per-culture source of truth.\n const selectedView = views?.find((v) => v.id === resolvedViewId);\n const selectedViewLabelKey = selectedView\n ? `tables.${selectedView.tableName}.views.${selectedView.id}.label`\n : '';\n const selectedViewLabel = selectedView ? t(selectedViewLabelKey) : '';\n\n // Single-view heading: when there's no picker (only one view) and no\n // explicit `title`, show the view's own name — mirrors Blazor's\n // FluentGridBase header (the `_viewDefinition != null` branch). Prefer the\n // localized `tables.{table}.views.{id}.label`; `t()` returns the key\n // unchanged on a miss (the React equivalent of Blazor's\n // `ResourceNotFound`), so fall back to the view's `displayName` when the\n // label resolves to the raw key. Undefined when neither is available →\n // nothing rendered, matching Blazor.\n const singleViewTitle =\n selectedView && selectedViewLabel !== selectedViewLabelKey\n ? selectedViewLabel\n : (selectedView?.displayName ?? undefined);\n\n return (\n <GridContextProvider value={gridContextValue}>\n <div\n className={mergeClasses(\n 'ppp-main-grid',\n styles.wrapper,\n fullSize && styles.wrapperFullSize,\n !borderVisible && styles.wrapperBorderless,\n className,\n )}\n // Min/max heights mirror Blazor's GetGridContainerStyle: skipped\n // entirely when fullSize is on (the flex `1 1 auto` from the\n // wrapperFullSize class takes over instead). Numbers are treated\n // as pixels; strings pass straight through (\"250px\", \"30vh\",\n // \"50%\" all work). Consumer-supplied `style` wins last so a\n // custom `style={{ minHeight: 'auto' }}` can override the default.\n style={{\n ...(!fullSize && {\n minHeight: typeof minHeight === 'number' ? `${minHeight}px` : minHeight,\n }),\n ...(!fullSize &&\n maxHeight !== undefined && {\n maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,\n }),\n ...style,\n }}\n >\n <div className={styles.toolbar}>\n {showViewPicker && (\n <Dropdown\n className={styles.viewPicker}\n aria-label={t('app.components.view-selector.view-name')}\n value={selectedViewLabel}\n selectedOptions={resolvedViewId ? [resolvedViewId] : []}\n onOptionSelect={(_, data) => {\n if (data.optionValue) handleViewChange(data.optionValue);\n }}\n >\n {views?.map((view) => {\n const label = t(`tables.${view.tableName}.views.${view.id}.label`);\n return (\n <Option key={view.id} value={view.id} text={label}>\n {label}\n </Option>\n );\n })}\n </Dropdown>\n )}\n {!showViewPicker && title && (\n <Text className={styles.title}>{title}</Text>\n )}\n {!showViewPicker && !title && singleViewTitle && (\n <Text className={styles.title}>{singleViewTitle}</Text>\n )}\n {/* Note: no separate flex spacer between view picker / title and\n the Overflow container — `toolbarOverflow` has `flex: 1 1 auto`\n and `justify-content: flex-end`, so it claims all remaining\n space and packs its items right. A separate `flex: 1` spacer\n would *split* the remaining space with the Overflow container,\n making the Overflow narrower than its natural content width\n without triggering its hide algorithm (the algorithm measures\n the Overflow container's width, not the items' rendered widths\n outside of it). The result was items extending past the\n Overflow's right edge and rendering BEHIND the view picker on\n narrow viewports. */}\n {/* Overflow container — collapses items into a \"…\" menu when the\n toolbar narrows past a viewport breakpoint (mobile, embedded\n grids in tight layouts, etc.). Mirrors Blazor's MenuBar +\n FluentOverflowItem pattern in `FluentGridBase.razor`. Items\n register themselves via `<OverflowItem>`; the trailing\n `<GridToolbarOverflowMenu>` reads `useOverflowMenu` to render\n the trigger + the per-item menu equivalents that surface the\n hidden controls. */}\n <Overflow padding={20}>\n <div className={styles.toolbarOverflow}>\n {/* Action-button slot. Renders BEFORE the search box so the\n cluster sits between the view picker and the search input —\n user feedback wanted Edit / New / etc. left of Search, with\n only the built-in Refresh button anchoring the far-right\n edge. Buttons read the grid's selection / refresh / table\n via useGridContext(), so we don't plumb that data via\n props. Typically wrapped in <GridButtons> for layout but\n bare buttons work too. The cluster is treated as a single\n OverflowItem — when it can't fit, the consumer's buttons\n drop into the overflow menu as a vertical list. */}\n {toolbarChildren !== null && (\n <OverflowItem id={GridToolbarOverflowId.Actions}>\n <div className={styles.toolbarSlot}>{toolbarChildren}</div>\n </OverflowItem>\n )}\n {/* Search box. Fluent's `SearchBox` ships with built-in clear\n (\"dismiss\") behavior. The value is the LIVE (undebounced)\n text so the input stays responsive; debounce lives in the\n effect above. The tooltip mirrors Blazor's GridBase\n _searchBoxStringColumnTitles / _searchBoxNumberColumnTitles\n pair — it surfaces which columns the search will hit,\n grouped by interaction model (begins-with vs. number-\n comparison). Built from the server-resolved column list\n returned with the row data, so it stays in lockstep with\n whatever the active view exposes. */}\n {allowSearch && (\n // Wrapping the SearchBox (and its optional Tooltip) in a plain\n // `<div>` instead of making it the OverflowItem's direct\n // child sidesteps a measurement / data-attribute drift: with\n // OverflowItem's `applyTriggerPropsToChildren` recursively\n // unwrapping FluentTrigger components (Tooltip is one), the\n // `data-overflowing` attribute could land on the SearchBox's\n // root while the visible wrapper kept the slot space. A\n // plain div is not a FluentTrigger, so OverflowItem registers\n // its ref directly on the div, measures the div's\n // offsetWidth, and toggles `data-overflowing` on the div —\n // which Fluent's `[overflowingItems] [data-overflowing]\n // { display: none }` then collapses to zero width.\n <OverflowItem id={GridToolbarOverflowId.Search}>\n <div className={styles.searchBoxWrapper}>\n {hasSearchableColumns ? (\n <Tooltip\n relationship=\"description\"\n withArrow\n positioning=\"below-end\"\n content={\n <div className={styles.searchTooltipBody}>\n {searchableStringColumns.length > 0 && (\n <div className={styles.searchTooltipSection}>\n <span>{t('app.components.grid.search-string-columns-intro')}</span>\n <div className={styles.searchTooltipColumnList}>\n {searchableStringColumns.map((col) => (\n <span key={col.columnName}>{col.displayName}</span>\n ))}\n </div>\n </div>\n )}\n {searchableNumberColumns.length > 0 && (\n <div className={styles.searchTooltipSection}>\n <span>{t('app.components.grid.search-number-columns-intro')}</span>\n <div className={styles.searchTooltipColumnList}>\n {searchableNumberColumns.map((col) => (\n <span key={col.columnName}>{col.displayName}</span>\n ))}\n </div>\n </div>\n )}\n </div>\n }\n >\n <SearchBox\n className={styles.searchBox}\n placeholder={t('app.components.grid.search-placeholder')}\n value={searchText}\n onChange={(_, data) => setSearchText(data.value)}\n />\n </Tooltip>\n ) : (\n // Active view has no searchable columns — render the\n // SearchBox without the tooltip so the input still\n // appears (consistent toolbar geometry) but doesn't\n // advertise hits it can't deliver.\n <SearchBox\n className={styles.searchBox}\n placeholder={t('app.components.grid.search-placeholder')}\n value={searchText}\n onChange={(_, data) => setSearchText(data.value)}\n />\n )}\n </div>\n </OverflowItem>\n )}\n {/* Built-in refresh button at the far right. Always present\n unless the consumer disables it via `showRefreshButton={false}`\n — mirrors Blazor's hardcoded refresh icon in FluentGridBase\n (icon-only, no label). Lives outside the children slot so it\n stays anchored to the toolbar edge regardless of how many\n consumer buttons land. Consumers who want a labeled refresh\n in the action-button cluster can still drop in\n `<RefreshGridButton>` as a child and set\n `showRefreshButton={false}` to suppress this built-in. */}\n {/* Pending-operation summary indicators. One per non-zero\n queue: Plus + count for pending creates, Pencil + count for\n pending updates, future Trash + count for pending deletes.\n Sits between the search box and the refresh button so the\n user sees the queue summary inline with the toolbar's other\n \"current grid state\" affordances. Each indicator hides\n automatically when its queue is empty — no layout shift in\n the steady state. Mirrors Blazor's grid which surfaces the\n same per-operation tallies in its toolbar. */}\n {pendingRowCreates.length > 0 && (\n <div\n className={styles.pendingIndicator}\n title={`${pendingRowCreates.length} pending create${pendingRowCreates.length === 1 ? '' : 's'}`}\n aria-label={`${pendingRowCreates.length} pending create${pendingRowCreates.length === 1 ? '' : 's'}`}\n >\n <Add16Regular className={styles.pendingIconCreate} />\n {/* CounterBadge's color prop is narrower than the\n semantic-color set we'd ideally want — only brand /\n danger / important / informative are accepted. The icon\n next to the badge already carries the meaning (Plus =\n create), so the badge stays informative for a neutral\n visual treatment that doesn't fight the icon's green. */}\n <CounterBadge\n count={pendingRowCreates.length}\n size=\"small\"\n color=\"informative\"\n appearance=\"filled\"\n />\n </div>\n )}\n {pendingRowUpdates.size > 0 && (\n <div\n className={styles.pendingIndicator}\n title={`${pendingRowUpdates.size} pending edit${pendingRowUpdates.size === 1 ? '' : 's'}`}\n aria-label={`${pendingRowUpdates.size} pending edit${pendingRowUpdates.size === 1 ? '' : 's'}`}\n >\n <Edit16Regular className={styles.pendingIconUpdate} />\n <CounterBadge\n count={pendingRowUpdates.size}\n size=\"small\"\n color=\"brand\"\n appearance=\"filled\"\n />\n </div>\n )}\n {pendingRowAssociates.length > 0 && (\n <div\n className={styles.pendingIndicator}\n title={`${pendingRowAssociates.length} pending link${pendingRowAssociates.length === 1 ? '' : 's'}`}\n aria-label={`${pendingRowAssociates.length} pending link${pendingRowAssociates.length === 1 ? '' : 's'}`}\n >\n <LinkAdd16Regular className={styles.pendingIconCreate} />\n <CounterBadge\n count={pendingRowAssociates.length}\n size=\"small\"\n color=\"informative\"\n appearance=\"filled\"\n />\n </div>\n )}\n {pendingRowDisassociates.length > 0 && (\n <div\n className={styles.pendingIndicator}\n title={`${pendingRowDisassociates.length} pending unlink${pendingRowDisassociates.length === 1 ? '' : 's'}`}\n aria-label={`${pendingRowDisassociates.length} pending unlink${pendingRowDisassociates.length === 1 ? '' : 's'}`}\n >\n <LinkDismiss16Regular className={styles.pendingIconUpdate} />\n <CounterBadge\n count={pendingRowDisassociates.length}\n size=\"small\"\n color=\"brand\"\n appearance=\"filled\"\n />\n </div>\n )}\n {pendingRowDeletes.length > 0 && (\n <div\n className={styles.pendingIndicator}\n title={`${pendingRowDeletes.length} pending delete${pendingRowDeletes.length === 1 ? '' : 's'}`}\n aria-label={`${pendingRowDeletes.length} pending delete${pendingRowDeletes.length === 1 ? '' : 's'}`}\n >\n <Delete16Regular className={styles.pendingIconUpdate} />\n <CounterBadge\n count={pendingRowDeletes.length}\n size=\"small\"\n color=\"danger\"\n appearance=\"filled\"\n />\n </div>\n )}\n {showRefreshButton && (() => {\n const handleRefreshClick = async () => {\n // When ANY edit inside this grid is dirty — the queued row\n // creates / updates / deletes from the dialog-based\n // New/Edit/Delete buttons, OR the per-row inline edits\n // tracked by each row's `<RecordContext>` — refreshing\n // would throw the work away without warning. The wrapping\n // `<MainContext>` mounted at the public `MainGrid`\n // boundary aggregates both flavors of dirty, so\n // `parentMainContext.isDirty` is the right gate here\n // (`gridIsDirty` alone would miss inline edits, since\n // those live on the per-row RecordContext's `pendingChanges`\n // rather than on the grid-level queue).\n //\n // On confirm, drop the dialog-driven queues so post-refresh\n // state is consistent with what just landed on the wire —\n // leaving them populated would re-attach the now-stale\n // changes to refreshed rows and try to re-submit them on\n // the next save. The per-row RC's own `pendingChanges`\n // clears automatically when its `loadedRecord` prop flips\n // identity from the new fetch.\n const subtreeDirty = parentMainContext?.isDirty ?? false;\n if (gridIsDirty || subtreeDirty) {\n const proceed = await warnAboutUnsavedChanges();\n if (!proceed) return;\n clearPendingRowUpdates();\n }\n setRefreshing(true);\n try {\n await refresh();\n } finally {\n setRefreshing(false);\n }\n };\n return (\n <OverflowItem id={GridToolbarOverflowId.Refresh}>\n <div className={styles.toolbarItemWrapper}>\n <Button\n appearance=\"subtle\"\n icon={<ArrowClockwise24Regular />}\n aria-label={t('app.buttons.refresh.label')}\n title={t('app.buttons.refresh.label')}\n disabled={refreshing}\n onClick={handleRefreshClick}\n />\n </div>\n </OverflowItem>\n );\n })()}\n {/* Settings popover — gear icon at the trailing edge of the\n toolbar. Mirrors Blazor's `<FluentPopover>`-with-Settings-button\n pattern in `FluentGridBase.razor`. Contents are conditional:\n page-size selector reparented from the footer at narrow\n viewports, plus the inline-edit toggle when `allowEdit` is\n on (uncontrolled mode only — when the consumer controls\n `editable` externally, they own the toggle UI). Button only\n renders when there's at least one row of content to show,\n so wide viewports without `allowEdit` see no gear at all. */}\n {(showSettingsEditableRow || showSettingsPageSizeRow) && (\n <OverflowItem id={GridToolbarOverflowId.Settings}>\n <div className={styles.toolbarItemWrapper}>\n <Popover positioning=\"below-end\" withArrow>\n <PopoverTrigger disableButtonEnhancement>\n <Button\n appearance=\"subtle\"\n icon={<Settings20Regular />}\n aria-label={t('app.buttons.settings.label')}\n title={t('app.buttons.settings.label')}\n />\n </PopoverTrigger>\n <PopoverSurface>\n <div className={styles.settingsHeader}>{t('app.buttons.settings.label')}</div>\n {showSettingsPageSizeRow && (\n <div className={styles.settingsRow}>\n <span className={styles.settingsRowLabel}>\n {t('app.components.grid.page-size')}\n </span>\n <Dropdown\n className={styles.settingsPageSizeDropdown}\n aria-label={t('app.components.grid.page-size')}\n value={String(pageSize)}\n selectedOptions={[String(pageSize)]}\n onOptionSelect={(_, data) => {\n if (data.optionValue) {\n const next = Number(data.optionValue);\n if (Number.isFinite(next) && next > 0) handlePageSizeChange(next);\n }\n }}\n >\n {pageSizes.map((size) => (\n <Option key={size} value={String(size)} text={String(size)}>\n {String(size)}\n </Option>\n ))}\n </Dropdown>\n </div>\n )}\n {showSettingsEditableRow && (\n inlineEditDisabled ? (\n <Tooltip\n relationship=\"description\"\n withArrow\n content={t('app.components.grid.editable-disabled-tooltip')}\n >\n <Switch\n label={t('app.components.grid.editable')}\n checked={editable}\n disabled\n />\n </Tooltip>\n ) : (\n <Switch\n label={t('app.components.grid.editable')}\n checked={editable}\n onChange={(_, data) => handleEditableToggle(data.checked)}\n />\n )\n )}\n </PopoverSurface>\n </Popover>\n </div>\n </OverflowItem>\n )}\n {/* Overflow menu — \"...\" button that appears when one or more\n toolbar items don't fit inline. Per-item menu equivalents\n below re-surface the hidden controls (search input rendered\n as a row in the menu, refresh as a MenuItem, settings switch\n as a row, etc.). Items still in the visible inline cluster\n don't appear in the menu (gated by `<OverflowMenuItem>`).\n The trigger button itself hides when nothing is overflowing,\n so the chrome stays clean on wide viewports. */}\n <GridToolbarOverflowMenu ariaLabel={t('app.components.grid.more-actions') ?? 'More'}>\n {toolbarChildren !== null && (\n <OverflowMenuItem itemId={GridToolbarOverflowId.Actions}>\n <div className={styles.menuActionRow}>{toolbarChildren}</div>\n </OverflowMenuItem>\n )}\n {allowSearch && (\n <OverflowMenuItem itemId={GridToolbarOverflowId.Search}>\n <div className={styles.menuActionRow}>\n <SearchBox\n className={styles.menuSearchBox}\n placeholder={t('app.components.grid.search-placeholder')}\n value={searchText}\n onChange={(_, data) => setSearchText(data.value)}\n contentBefore={<Search20Regular />}\n />\n </div>\n </OverflowMenuItem>\n )}\n {showRefreshButton && (\n <OverflowMenuItem itemId={GridToolbarOverflowId.Refresh}>\n <MenuItem\n icon={<ArrowClockwise20Regular />}\n disabled={refreshing}\n onClick={async () => {\n // Mirror the toolbar refresh button — see the same\n // comment block above for the rationale on consulting\n // the wrapping MainContext's aggregate isDirty.\n const subtreeDirty = parentMainContext?.isDirty ?? false;\n if (gridIsDirty || subtreeDirty) {\n const proceed = await warnAboutUnsavedChanges();\n if (!proceed) return;\n clearPendingRowUpdates();\n }\n setRefreshing(true);\n try {\n await refresh();\n } finally {\n setRefreshing(false);\n }\n }}\n >\n {t('app.buttons.refresh.label')}\n </MenuItem>\n </OverflowMenuItem>\n )}\n {(showSettingsEditableRow || showSettingsPageSizeRow) && (\n <OverflowMenuItem itemId={GridToolbarOverflowId.Settings}>\n {showSettingsPageSizeRow && (\n <div className={styles.menuActionRow}>\n <span className={styles.settingsRowLabel}>\n {t('app.components.grid.page-size')}\n </span>\n <Dropdown\n aria-label={t('app.components.grid.page-size')}\n value={String(pageSize)}\n selectedOptions={[String(pageSize)]}\n onOptionSelect={(_, data) => {\n if (data.optionValue) {\n const next = Number(data.optionValue);\n if (Number.isFinite(next) && next > 0) handlePageSizeChange(next);\n }\n }}\n >\n {pageSizes.map((size) => (\n <Option key={size} value={String(size)} text={String(size)}>\n {String(size)}\n </Option>\n ))}\n </Dropdown>\n </div>\n )}\n {showSettingsEditableRow && (\n <div className={styles.menuActionRow}>\n {inlineEditDisabled ? (\n <Tooltip\n relationship=\"description\"\n withArrow\n content={t('app.components.grid.editable-disabled-tooltip')}\n >\n <Switch\n label={t('app.components.grid.editable')}\n checked={editable}\n disabled\n />\n </Tooltip>\n ) : (\n <Switch\n label={t('app.components.grid.editable')}\n checked={editable}\n onChange={(_, data) => handleEditableToggle(data.checked)}\n />\n )}\n </div>\n )}\n </OverflowMenuItem>\n )}\n </GridToolbarOverflowMenu>\n </div>\n </Overflow>\n </div>\n <div className={styles.bodyArea}>\n {isLoading ? (\n <div className={styles.statusFill} role=\"status\" aria-live=\"polite\">\n {/* Fluent Spinner's built-in label renders next to the\n spinner glyph with the right gap automatically — no\n additional layout wiring needed. Matches Blazor's\n `OverlayWithProgress` spinner-plus-label cold-start\n treatment. Label localized from the same\n `app.components.grid.*` namespace the Blazor grid uses\n (defaults live in `ppp-server.defaults.en.json` so both\n UIs share one source of truth). */}\n <Spinner label={t('app.components.grid.loading')} />\n </div>\n ) : error ? (\n <div\n className={mergeClasses(styles.statusFill, styles.statusFillError)}\n role=\"alert\"\n >\n {error.message || t('app.components.grid.load-records-error')}\n </div>\n ) : (\n <div className={styles.tableContainer} ref={tableContainerRef}>\n {/* Set `width` to the exact column total (TanStack-managed data\n columns plus the 50px select column when enabled). Critical\n for two reasons:\n - Locks the select column at exactly 50px. With `width: 100%`\n + `table-layout: fixed`, the browser distributes leftover\n space proportionally across every column — including the\n select column, even with `<col width=\"50px\">` and cell-\n level max-width clamps. Sizing the table to the exact\n column sum eliminates the leftover entirely.\n - Triggers horizontal scrolling naturally when the user-\n resized column total exceeds the scroll container's width\n (the parent `tableContainer` is `overflow: auto`).\n Tradeoff: when the column total is narrower than the body\n area, there's visible empty space to the right of the table\n rather than stretching to fill. */}\n {/* The pending-operation column only renders when there's at\n least one pending change in the grid's queue (see the\n `gridIsDirty` gate on the colgroup + header + body cell below).\n Avoids paying for an always-visible empty column on read-only\n grids while still surfacing per-row operation icons as soon as\n the queue gains an entry. The column hides automatically after\n a successful save clears the queue. */}\n <table\n className={styles.table}\n style={{\n width: `${\n table.getTotalSize() +\n (selectionMode !== 'none' ? 50 : 0) +\n (gridIsDirty ? 50 : 0)\n }px`,\n }}\n >\n {/* `<colgroup>` locks fixed-width columns. With `table-layout:\n fixed` and the explicit table width above, putting width /\n min-width / max-width on the <th> alone isn't authoritative —\n browsers still distribute leftover space proportionally across\n all columns including these. A `<col>` width IS authoritative\n in fixed-layout tables, so the column locks at exactly the\n declared width and the data columns absorb the slack. The\n colgroup contains:\n - select column at 50px (when selectionMode !== 'none')\n - pending-operation column at 50px (when gridIsDirty)\n Both are independent — either, both, or neither may be\n present. */}\n {(selectionMode !== 'none' || gridIsDirty) && (\n <colgroup>\n {selectionMode !== 'none' && <col style={{ width: '50px' }} />}\n {gridIsDirty && <col style={{ width: '50px' }} />}\n </colgroup>\n )}\n <thead>\n {table.getHeaderGroups().map((headerGroup) => (\n <tr key={headerGroup.id} className={styles.headerRow}>\n {selectionMode !== 'none' && (\n <th className={mergeClasses(styles.headerCell, styles.selectCell)}>\n {selectionMode === 'multiple' && (\n <Checkbox\n aria-label={t('app.components.grid.select-all-rows')}\n checked={\n table.getIsAllRowsSelected()\n ? true\n : table.getIsSomeRowsSelected()\n ? 'mixed'\n : false\n }\n onChange={(_, data) => table.toggleAllRowsSelected(!!data.checked)}\n />\n )}\n </th>\n )}\n {/* Empty header for the pending-operation column. The per-\n row icons in the body cells carry the meaning; an icon\n in the header would be redundant. */}\n {gridIsDirty && (\n <th\n className={mergeClasses(styles.headerCell, styles.pendingCell)}\n aria-label={t('app.components.grid.pending-operation') ?? 'Pending operation'}\n />\n )}\n {headerGroup.headers.map((header) => {\n const columnName = header.column.id;\n // Apply a width style ONLY when the user has resized this\n // column or the view supplied an explicit width. Columns\n // without either fall into the table's natural\n // distribution (table-layout: fixed shares remaining\n // horizontal space equally between unsized columns).\n // Reading from `columnSizing` directly — rather than\n // `header.getSize()` — sidesteps TanStack's 150 default\n // and lets unsized columns stay unsized.\n const userSize = columnSizing[columnName];\n // Width lookup precedence: explicit user resize, then\n // `<GridColumn width>` declaration (string or number),\n // then the server-resolved view width. String widths\n // (\"200px\", \"20%\", \"auto\") flow straight into the CSS\n // `width` without a px suffix; only numbers get one.\n const declaredCol = declaredColumnsByName.get(columnName);\n // Template-column header ids are their auto-generated /\n // explicit `id` (not a Dataverse column name), so the\n // bound-column map misses. Consult the parallel template\n // map so `<GridTemplateColumn width=\"240px\">` actually\n // applies a width to the header (and via\n // `table-layout: fixed`, every body cell).\n const templateCol = templateColumnsById.get(columnName);\n const declaredWidth = declaredCol?.width ?? templateCol?.width;\n const viewWidth = resolvedColumnsByName.get(columnName)?.width;\n const widthValue =\n typeof userSize === 'number'\n ? `${userSize}px`\n : declaredWidth !== undefined\n ? typeof declaredWidth === 'number'\n ? `${declaredWidth}px`\n : declaredWidth\n : typeof viewWidth === 'number'\n ? `${viewWidth}px`\n : undefined;\n const declaredAlign = (\n header.column.columnDef.meta as { align?: GridColumnAlign } | undefined\n )?.align;\n const headerStyle: CSSProperties = {\n ...(widthValue !== undefined && { width: widthValue }),\n ...(declaredAlign && {\n textAlign:\n declaredAlign === 'end'\n ? 'right'\n : declaredAlign === 'center'\n ? 'center'\n : 'left',\n }),\n };\n const effectiveCol = effectiveColumnsByName.get(columnName);\n const sortable = effectiveCol?.isSortable ?? false;\n const headerTooltip = effectiveCol?.tooltip ?? undefined;\n const sortIndex = sort.findIndex((d) => d.columnName === columnName);\n const sortDirective = sortIndex >= 0 ? sort[sortIndex] : undefined;\n const SortIcon = sortDirective\n ? sortDirective.descending\n ? ArrowSortDown16Regular\n : ArrowSortUp16Regular\n : null;\n // Show the 1-based precedence number only when there are\n // multiple active sort columns — at a single sort the\n // arrow alone is enough and the number would be noise.\n const showSortPosition = sortDirective && sort.length > 1;\n return (\n <th\n key={header.id}\n className={mergeClasses(\n styles.headerCell,\n sortable && styles.headerCellSortable,\n )}\n style={headerStyle}\n aria-sort={\n sortDirective\n ? sortDirective.descending\n ? 'descending'\n : 'ascending'\n : sortable\n ? 'none'\n : undefined\n }\n title={\n headerTooltip ??\n (sortable ? t('app.components.grid.sort-hint') : undefined)\n }\n onClick={\n sortable\n ? (e) => {\n // Swallow the click the browser synthesizes\n // on the `<th>` when the user mousedowns on\n // the resize handle and mouses up on a\n // sibling (or anywhere else inside the th).\n // See `resizingRef` for the full mechanic.\n if (resizingRef.current) return;\n handleSortToggle(columnName, e.shiftKey);\n }\n : undefined\n }\n >\n <span className={styles.headerCellInner}>\n <span className={styles.headerCellLabel}>\n {/* Direct invocation — see the cell render\n below for why we bypass `flexRender`. */}\n {(() => {\n const headerRenderer = header.column.columnDef.header;\n return typeof headerRenderer === 'function'\n ? headerRenderer(header.getContext())\n : headerRenderer;\n })()}\n </span>\n {SortIcon && <SortIcon className={styles.sortIcon} aria-hidden=\"true\" />}\n {showSortPosition && (\n <span className={styles.sortPosition} aria-hidden=\"true\">\n {sortIndex + 1}\n </span>\n )}\n </span>\n {/* Resize handle — a narrow strip on the right edge.\n `getResizeHandler` wires up the mouse + touch\n drag math; `stopPropagation` keeps the\n mousedown from also triggering the parent\n header's sort click. `pointerEvents` defaults\n to `auto` so the handle catches the cursor\n even when the header is non-sortable. */}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={t('app.components.grid.resize-column')}\n className={mergeClasses(\n styles.resizeHandle,\n header.column.getIsResizing() && styles.resizeHandleActive,\n )}\n onMouseDown={(e) => {\n e.stopPropagation();\n // Latch the suppression flag and let the\n // *next macrotask* clear it. The synthetic\n // `click` the browser may dispatch on the\n // ancestor `<th>` fires synchronously after\n // mouseup (same task); a `setTimeout(_, 0)`\n // runs in the following task, so the flag\n // is still set when the th's `onClick` runs\n // and clear by the time any subsequent real\n // header click arrives.\n resizingRef.current = true;\n setTimeout(() => {\n resizingRef.current = false;\n }, 0);\n header.getResizeHandler()(e);\n }}\n onTouchStart={(e) => {\n e.stopPropagation();\n resizingRef.current = true;\n setTimeout(() => {\n resizingRef.current = false;\n }, 0);\n header.getResizeHandler()(e);\n }}\n onClick={(e) => e.stopPropagation()}\n />\n </th>\n );\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {!isLoading &&\n !error &&\n table.getRowModel().rows.map((row) => {\n const isSelected = row.getIsSelected();\n // Determine the row's pending state for the operation-icon\n // column. Pending-create rows are identified by the\n // synthetic `__pending-create-*` id; pending-update rows\n // by membership in `pendingRowUpdates`. Future pending-\n // delete rows would slot in here via a parallel\n // `pendingRowDeletes` lookup. Mirrors Blazor's grid which\n // walks `_rowsToCreate` / `_rowsToUpdate` / `_rowsToDelete`\n // for the same per-row icon decision.\n const rowId = row.original.id;\n const isPendingCreate =\n rowId !== undefined && pendingCreateIds.has(rowId);\n const isPendingAssociate =\n rowId !== undefined &&\n !isPendingCreate &&\n pendingAssociateIds.has(rowId);\n const isPendingDisassociate =\n rowId !== undefined &&\n !isPendingCreate &&\n !isPendingAssociate &&\n pendingDisassociateIds.has(rowId);\n const isPendingDelete =\n rowId !== undefined &&\n !isPendingCreate &&\n !isPendingAssociate &&\n !isPendingDisassociate &&\n pendingDeleteIds.has(rowId);\n const isPendingUpdate =\n rowId !== undefined &&\n !isPendingCreate &&\n !isPendingAssociate &&\n !isPendingDisassociate &&\n !isPendingDelete &&\n pendingRowUpdates.has(rowId);\n // Cross-filter highlight (soft filter, client-side only).\n // When a chart slice was clicked in highlight mode the\n // datasource carries `{ column, value }`; rows whose\n // value doesn't match get muted. Comparison goes through\n // `readColumnRawValue` which unwraps Dataverse's wire\n // shapes — including the `RelatedRecordValue` double-\n // nesting aggregate groupby attributes use — so both\n // sides reduce to the same primitive (choice integer,\n // lookup GUID, string, etc.) regardless of source path.\n // Combined date groupings or aggregations whose labels\n // are server-formatted strings (e.g. \"Jan 2024\") aren't\n // matchable from row-level data; chart consumers route\n // those through hard filter (drill-through) instead.\n const isHighlightMuted = (() => {\n if (!dataSource || !dataSource.highlight) return false;\n const { column, value } = dataSource.highlight;\n const property = row.original.properties?.[column];\n if (!property) return true;\n return readColumnRawValue(property) !== value;\n })();\n // Row-double-click handler — invokes the grid's registered\n // edit handler for the row's record. Same code path the\n // primary-name link uses. Gated per-row by the handler\n // that actually applies to that row's lifecycle:\n // - Pending-create rows: the create-reopen handler.\n // - Persisted rows: the open handler.\n // Without this split, a grid that only registers New (no\n // Open) would wire up double-click on every existing row\n // for a no-op dispatch, masking the browser's default\n // text-selection behavior for no benefit.\n const rowDoubleClickAffordance = isPendingCreate\n ? hasCreateReopenHandler\n : hasOpenHandler;\n const handleRowDoubleClick =\n allowOpenOnRowDoubleClick && rowDoubleClickAffordance\n ? () => dispatchOpen(row.original)\n : undefined;\n // Row-level click toggles selection when `selectFromEntireRow`\n // is on and the grid is in single/multi-select mode. Skips\n // clicks that landed on interactive cell content (links,\n // buttons, inputs, checkboxes, the row's own action icons)\n // so those keep their own behavior — selection only fires\n // for \"blank\" cell clicks. Mirrors Blazor's\n // `SelectFromEntireRow` parameter.\n // Row-level click toggles selection when `selectFromEntireRow`\n // is on and the grid is in single/multi-select mode. Skips\n // clicks that landed on interactive cell content (links,\n // buttons, inputs, checkboxes, the row's own action icons)\n // so those keep their own behavior — selection only fires\n // for \"blank\" cell clicks. The select-cell below ALSO stops\n // propagation as belt-and-braces so clicking the explicit\n // radio/checkbox can't accidentally re-trigger the row\n // handler. Mirrors Blazor's `SelectFromEntireRow` parameter.\n const handleRowClick =\n selectFromEntireRow && selectionMode !== 'none'\n ? (ev: ReactMouseEvent<HTMLTableRowElement>) => {\n const target = ev.target as HTMLElement | null;\n if (\n target?.closest(\n 'a, button, input, select, textarea, [role=\"button\"], [role=\"link\"]',\n )\n ) {\n return;\n }\n row.toggleSelected(!isSelected);\n }\n : undefined;\n // Inline-edit wrap. Each editable persistent row gets its\n // own <RecordContext> via skip-fetch mode (we already have\n // the record in hand from the grid's fetch — no need for\n // a per-row retrieveRecord round-trip). The context\n // auto-registers with the surrounding page MainContext, so\n // dirty edits flow into the outer transactional saveAll.\n // Pending-create rows are excluded — they live in the\n // grid's create-queue and the dialog-edit flow owns them.\n // Pending-associate rows are also excluded — the user's\n // intent at this point is to link the record, not to\n // start editing its underlying columns, and inline edits\n // here would silently piggyback an UpdateRequest onto the\n // parent's save batch.\n const isRowInlineEditable =\n editable &&\n !isPendingCreate &&\n !isPendingAssociate &&\n !!row.original.id &&\n !!row.original.tableName;\n const rowBody = (\n <>\n {selectionMode !== 'none' && (\n <td\n className={mergeClasses(styles.bodyCell, styles.selectCell)}\n onClick={(e) => e.stopPropagation()}\n >\n {selectionMode === 'single' ? (\n <Radio\n aria-label={t('app.components.grid.select-row')}\n name={radioGroupName}\n value={row.id}\n checked={isSelected}\n // Radios only fire onChange when transitioning\n // unchecked → checked, so deselecting via the\n // radio isn't possible — that matches HTML's\n // native radio behavior. Clearing the selection\n // is done by selecting a different row (or\n // changing the view / page).\n onChange={() => row.toggleSelected(true)}\n // Skip in tab order while inline-editing — Tab\n // should flow cell → cell across rows for data\n // entry, not detour through every row's\n // selector. Click still selects, so the\n // affordance is only de-emphasized for\n // keyboard, not removed.\n tabIndex={editable ? -1 : undefined}\n />\n ) : (\n <Checkbox\n aria-label={t('app.components.grid.select-row')}\n checked={isSelected}\n onChange={(_, data) => row.toggleSelected(!!data.checked)}\n // Same Tab-skip rationale as the Radio variant\n // above.\n tabIndex={editable ? -1 : undefined}\n />\n )}\n </td>\n )}\n {/* Pending-operation icon cell. Empty for unchanged\n rows; the column itself only renders at all when\n the grid has at least one pending change (see the\n `gridIsDirty` gate on the colgroup + header above).\n Icons mirror the Blazor grid:\n Plus = pending create\n Pencil = pending update\n Trash = pending delete (queued by\n DeleteRecordGridButton in its default\n `behavior=\"withGridContext\"` mode)\n */}\n {gridIsDirty && (\n <td className={mergeClasses(styles.bodyCell, styles.pendingCell)}>\n {isPendingCreate ? (\n <Add16Regular\n className={styles.pendingIconCreate}\n aria-label={t('app.components.grid.pending-operation-create') ?? 'Pending create'}\n />\n ) : isPendingAssociate ? (\n <LinkAdd16Regular\n className={styles.pendingIconCreate}\n aria-label={t('app.components.grid.pending-operation-link') ?? 'Pending link'}\n />\n ) : isPendingDisassociate ? (\n <LinkDismiss16Regular\n className={styles.pendingIconUpdate}\n aria-label={t('app.components.grid.pending-operation-unlink') ?? 'Pending unlink'}\n />\n ) : isPendingDelete ? (\n <Delete16Regular\n className={styles.pendingIconUpdate}\n aria-label={t('app.components.grid.pending-operation-delete') ?? 'Pending delete'}\n />\n ) : isPendingUpdate ? (\n <Edit16Regular\n className={styles.pendingIconUpdate}\n aria-label={t('app.components.grid.pending-operation-update') ?? 'Pending update'}\n />\n ) : null}\n </td>\n )}\n {row.getVisibleCells().map((cell) => {\n const cellAlign = (\n cell.column.columnDef.meta as { align?: GridColumnAlign } | undefined\n )?.align;\n const cellStyle: CSSProperties | undefined = cellAlign\n ? {\n textAlign:\n cellAlign === 'end'\n ? 'right'\n : cellAlign === 'center'\n ? 'center'\n : 'left',\n }\n : undefined;\n // Call the cell renderer DIRECTLY instead of going\n // through `flexRender`. Flex-render wraps function\n // cells in `React.createElement(cellFn, props)`,\n // which treats each cell function as a React\n // component type — and our columns memo creates\n // fresh cell function references on every\n // MainGridImpl render (because `orderedDeclarations`\n // from `extractGridColumnOverrides(children)` is a\n // fresh array literal every render). React then\n // sees those wrappers as a different component type\n // each render and unmounts + remounts the cell's\n // entire DOM subtree — which destroys any\n // focused inline-edit `<input>` and drops focus on\n // the user's first keystroke. Invoking the function\n // directly returns its JSX (whose component types\n // — `TemplateColumnEditCell`, the read-mode\n // renderer, `<div>` — are stable references defined\n // at module scope), so React reconciles the *output*\n // and keeps the underlying DOM in place across\n // renders. Non-function cell values (rare —\n // primitive ReactNodes) pass through unchanged.\n const cellRenderer = cell.column.columnDef.cell;\n const cellContent =\n typeof cellRenderer === 'function'\n ? cellRenderer(cell.getContext())\n : cellRenderer;\n return (\n <td\n key={cell.id}\n className={styles.bodyCell}\n {...(cellStyle && { style: cellStyle })}\n >\n {cellContent}\n </td>\n );\n })}\n </>\n );\n return (\n <tr\n key={row.id}\n className={mergeClasses(\n styles.bodyRow,\n isSelected && styles.bodyRowSelected,\n handleRowClick !== undefined && styles.bodyRowClickable,\n isHighlightMuted && styles.bodyRowMuted,\n )}\n {...(handleRowDoubleClick !== undefined && {\n onDoubleClick: handleRowDoubleClick,\n })}\n {...(handleRowClick !== undefined && {\n onClick: handleRowClick,\n })}\n >\n {isRowInlineEditable && row.original.id ? (\n <RecordContext\n table={row.original.tableName}\n id={row.original.id}\n loadedRecord={row.original}\n >\n {rowBody}\n </RecordContext>\n ) : (\n rowBody\n )}\n </tr>\n );\n })}\n </tbody>\n </table>\n {/* Empty-result row stays INSIDE the scroll container so column\n headers (sticky) remain visible above it and horizontal\n scrolling still works on the empty grid. `displayedData` (not\n `data`) so a grid showing only pending creates doesn't look\n empty — there's something the user has to act on. */}\n {displayedData.length === 0 && (\n <div className={styles.emptyStateRow}>{t('app.components.grid.empty-content')}</div>\n )}\n {/* Infinite-scroll sentinel. The IntersectionObserver effect below\n watches this div against `tableContainerRef` (the scroll viewport)\n and bumps `page` when it enters view. Rendered only in virtualize\n mode AND only when more pages are still available — when we've\n loaded all rows, the sentinel disappears so no further fetches\n fire and the observer can't keep triggering on idle scrolls. */}\n {isVirtualize && hasMoreRowsToLoad && (\n <div ref={loadMoreSentinelRef} className={styles.loadMoreSentinel}>\n {isRefetching && <Spinner size=\"tiny\" />}\n </div>\n )}\n </div>\n )}\n {/* Refetch scrim — translucent overlay drawn over the existing\n rows while a deps-change refetch is in flight (new page / sort\n / search / view). Sibling of `tableContainer` (NOT inside it)\n so it stays anchored to the viewport of the body area instead\n of scrolling away with the rows underneath. Only renders when\n `keepPreviousData` is holding prior rows on screen; the cold-\n start \"Loading…\" status above covers the no-data-yet case. */}\n {(isRefetching || isEditableTransitionPending) && (\n <div className={styles.refetchScrim} role=\"status\" aria-live=\"polite\">\n <Spinner size=\"large\" />\n </div>\n )}\n </div>\n\n {!hidePaging && !isVirtualize && !isLoading && !error && (data.length > 0 || page > 1) && (\n <MainGridPaging\n page={page}\n pageSize={pageSize}\n totalCount={totalCount}\n dataLength={data.length}\n onPageChange={handlePageChange}\n onPageSizeChange={handlePageSizeChange}\n pageSizes={pageSizes}\n // Page-size lives in the footer at wide widths, in the Settings\n // popover at narrow widths (where the footer gets a clean\n // record-count + nav-only strip). The popover row below mirrors\n // the same prop combination.\n showPageSize={allowChangingPageSize && !isNarrowViewport}\n />\n )}\n </div>\n </GridContextProvider>\n );\n}\n","import {\n Dropdown,\n Label,\n makeStyles,\n mergeClasses,\n Option,\n tokens,\n} from '@fluentui/react-components';\nimport type { components, TableRecord, ViewMetadata } from '@powerportalspro/core';\nimport {\n QueryStatus,\n useT,\n useViewsForTable,\n type Localizer,\n} from '@powerportalspro/react';\nimport { useMemo, useState, type CSSProperties } from 'react';\nimport { MainGrid } from '../grids/main-grid.js';\n\n// Dataverse savedquery.querytype value the server maps to `ViewType.Public`.\n// Mirrors `PowerPortalsPro.Common.Models.ViewType.Public = 0` — see\n// `ViewMetadata.cs`. Used to filter the per-table view list down to views\n// the user is allowed to pick from in the lookup UI.\nconst VIEW_TYPE_PUBLIC = 0;\n\nconst useLookupFormStyles = makeStyles({\n root: {\n display: 'flex',\n flexDirection: 'column',\n rowGap: tokens.spacingVerticalS,\n width: '100%',\n },\n // Picker row — label + dropdown laid out horizontally. Mirrors Blazor's\n // `<FluentStack Orientation=\"Horizontal\">` around the table-type select.\n pickerRow: {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n columnGap: tokens.spacingHorizontalS,\n flexWrap: 'wrap',\n },\n pickerLabel: {\n color: tokens.colorNeutralForeground2,\n },\n pickerDropdown: {\n minWidth: '200px',\n },\n});\n\nexport interface LookupFormProps {\n /**\n * Logical names of the tables the user can pick from. Single-table forms\n * pass one name; polymorphic pickers pass several. At least one entry is\n * required — the form throws otherwise.\n */\n tableNames: readonly string[];\n /**\n * Table that should be selected on first render. Falls back to the first\n * entry in `tableNames` when omitted or not present in the list. Re-renders\n * with a different `defaultTable` after mount don't reselect — the active\n * table is user-controlled once mounted.\n */\n defaultTable?: string;\n /**\n * Per-table allow-list of view ids the user can pick from. Tables missing\n * from this map (or empty arrays) fall back to \"all Public views for the\n * table\".\n */\n viewIds?: Record<string, readonly string[]>;\n /**\n * Selection mode for the embedded grid. `'single'` (default) lets the\n * user pick one record; `'multiple'` exposes a select-all toggle and\n * lets them pick any combination.\n */\n selectionMode?: 'single' | 'multiple';\n /**\n * Fires whenever the selected-records set changes. Receives the live\n * `TableRecord[]` from the grid. Selection is per-page (see\n * `MainGridProps.selectionMode` for the rationale) — paging forward\n * clears the visible selection.\n */\n onSelectedRecordsChange?: (records: readonly TableRecord[]) => void;\n /**\n * Controlled selection. When provided, the embedded grid renders only\n * rows whose `record.id` appears in this array as selected, and the\n * consumer must update the prop in response to\n * {@link onSelectedRecordsChange} for the visual to change. Omit for\n * uncontrolled.\n */\n selectedRecords?: readonly TableRecord[];\n /**\n * Initial records-per-page for the embedded grid. Defaults to 10.\n */\n pageSize?: number;\n /**\n * Additional server-side filters applied on top of the resolved view's\n * FetchXML. Forwarded to the embedded `<MainGrid filters>`. Use for the\n * \"exclude records already linked via this M2M relationship\" picker\n * scenario by passing a `RelationshipFilter` with\n * `RelationshipFilterMode.ExcludeExistingRecords`.\n */\n filters?: readonly components['schemas']['GridFilterBase'][];\n className?: string;\n style?: CSSProperties;\n /** Class on the embedded `<MainGrid>` outer wrapper. */\n gridClassName?: string;\n /**\n * Inline style on the embedded `<MainGrid>` outer wrapper. Standard use:\n * `gridStyle={{ minHeight: 300, maxHeight: 300 }}` to clamp the grid's\n * height when embedded in a dialog.\n */\n gridStyle?: CSSProperties;\n}\n\n/**\n * Renders an optional table picker (visible only when more than one entry\n * is in `tableNames`) and an embedded `<MainGrid>` whose built-in header\n * view selector is constrained to the allowed views. Used standalone for\n * inline lookup pickers and as the body of `<LookupDialog>`.\n *\n * View switching is delegated to MainGrid's built-in selector rather than a\n * separate dropdown above the grid: LookupForm resolves the allowed set\n * (Public views ∩ the optional per-table `viewIds` allow-list) and passes\n * their ids as MainGrid's `viewIds`, so the selector renders in the grid\n * header — and auto-hides when only one view qualifies. The table picker\n * stays in the form because MainGrid is single-table.\n */\nexport function LookupForm({\n tableNames,\n defaultTable,\n viewIds,\n selectionMode = 'single',\n onSelectedRecordsChange,\n selectedRecords,\n pageSize = 10,\n filters,\n className,\n style,\n gridClassName,\n gridStyle,\n}: LookupFormProps) {\n const styles = useLookupFormStyles();\n const t = useT();\n\n if (tableNames.length === 0) {\n throw new Error('LookupForm: `tableNames` must contain at least one table.');\n }\n\n // Active table — defaultTable wins on first mount when present in the\n // list; otherwise the first entry. After mount the user owns this via\n // the table dropdown.\n const initialTable = useMemo(() => {\n if (defaultTable && tableNames.includes(defaultTable)) return defaultTable;\n return tableNames[0]!;\n }, [defaultTable, tableNames]);\n const [selectedTable, setSelectedTable] = useState<string>(initialTable);\n\n const viewsResult = useViewsForTable(selectedTable);\n\n // Filter views down to: Public type + (if the consumer supplied a\n // per-table allow-list) intersection with `viewIds[selectedTable]`.\n // Depend on `status` and `data` separately rather than the whole\n // `viewsResult` object — `useAsyncResource` spreads `{ ...state,\n // refetch }` each render, so the outer object identity churns even\n // when nothing meaningful changed, which would force this memo to\n // recompute (and downstream effects to fire) on every render.\n const viewsStatus = viewsResult.status;\n const viewsData = viewsResult.data;\n const allowedViews = useMemo<readonly ViewMetadata[]>(() => {\n if (viewsStatus !== QueryStatus.Success || !viewsData) return [];\n const publicViews = viewsData.filter((v) => v.type === VIEW_TYPE_PUBLIC);\n const allow = viewIds?.[selectedTable];\n if (!allow || allow.length === 0) return publicViews;\n const allowSet = new Set(allow);\n return publicViews.filter((v) => allowSet.has(v.id));\n }, [viewsStatus, viewsData, viewIds, selectedTable]);\n\n // View switching is delegated to MainGrid's built-in header selector\n // (driven by the `viewIds` allow-list below) rather than a separate\n // dropdown above the grid. Resolve the allowed ids (Public ∩ per-table\n // allow-list, already computed in `allowedViews`) and the default view;\n // MainGrid hides its selector automatically when only one view qualifies.\n const allowedViewIds = useMemo(() => allowedViews.map((v) => v.id), [allowedViews]);\n const defaultViewId = useMemo(\n () => (allowedViews.find((v) => v.isDefault) ?? allowedViews[0])?.id,\n [allowedViews],\n );\n\n // Switching tables strands any visible selection on rows that no longer\n // exist in the new table's views — clear it so consumers don't see stale\n // records. The embedded grid is keyed by table (see below), so it remounts\n // onto the new table's default view with fresh paging / sort / selection.\n const handleTableChange = (next: string) => {\n if (next === selectedTable) return;\n setSelectedTable(next);\n onSelectedRecordsChange?.([]);\n };\n\n return (\n <div className={mergeClasses('ppp-lookup-form', styles.root, className)} style={style}>\n {tableNames.length > 1 && (\n <div className={styles.pickerRow}>\n <Label className={styles.pickerLabel} htmlFor=\"ppp-lookup-form-table\">\n {t('app.components.lookup-form.table-type')}\n </Label>\n <Dropdown\n id=\"ppp-lookup-form-table\"\n className={styles.pickerDropdown}\n value={tableLabel(t, selectedTable)}\n selectedOptions={[selectedTable]}\n onOptionSelect={(_, data) => {\n if (data.optionValue) handleTableChange(data.optionValue);\n }}\n >\n {tableNames.map((name) => {\n const label = tableLabel(t, name);\n return (\n <Option key={name} value={name} text={label}>\n {label}\n </Option>\n );\n })}\n </Dropdown>\n </div>\n )}\n\n {allowedViews.length > 0 && (\n <MainGrid\n // Remount on table switch so the grid resets to the new table's\n // default view (and clears paging / sort / selection) cleanly.\n key={selectedTable}\n tableName={selectedTable}\n // Constrain the grid's built-in header selector to the allowed\n // (Public ∩ allow-list) views. MainGrid renders the selector in\n // the grid header when more than one qualifies and hides it for a\n // single view — no separate dropdown above the grid.\n viewIds={allowedViewIds}\n {...(defaultViewId ? { defaultViewId } : {})}\n selectionMode={selectionMode}\n // Whole-row click is the expected picker UX — Blazor's LookupForm\n // passes `SelectFromEntireRow=true` to its embedded grid for the\n // same reason.\n selectFromEntireRow\n pageSize={pageSize}\n {...(filters !== undefined && filters.length > 0 ? { filters } : {})}\n {...(onSelectedRecordsChange ? { onSelectedRecordsChange } : {})}\n {...(selectedRecords !== undefined ? { selectedRecords } : {})}\n {...(gridClassName !== undefined ? { className: gridClassName } : {})}\n {...(gridStyle !== undefined ? { style: gridStyle } : {})}\n />\n )}\n </div>\n );\n}\n\n/**\n * Resolves the localized label for a table logical name. Mirrors the\n * `tableLabel` helper in `lookup-edit.tsx` — looks up `tables.{name}.label`\n * and falls back to the bare logical name when the localization JSON\n * hasn't loaded (or the key is missing). The localizer auto-fetches the\n * per-table bundle on first miss, so subsequent renders show the\n * localized name.\n */\nfunction tableLabel(t: Localizer['t'], tableName: string): string {\n const key = `tables.${tableName}.label`;\n const resolved = t(key);\n return resolved === key ? tableName : resolved;\n}\n","import {\r\n Button,\r\n Dialog,\r\n DialogSurface,\r\n DialogTitle,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { Dismiss24Regular, LinkAdd20Regular } from '@fluentui/react-icons';\r\nimport type { TableRecord } from '@powerportalspro/core';\r\nimport { useT, useTableMetadata } from '@powerportalspro/react';\r\nimport { useCallback, useMemo, useState, type CSSProperties } from 'react';\r\nimport { resolveColumnDisplayValue } from '../edits/base/column-display.js';\r\nimport { LookupForm, type LookupFormProps } from './lookup-form.js';\r\n\r\nconst useLookupDialogStyles = makeStyles({\r\n // Override Fluent's default dialog width — lookup dialogs need most of\r\n // the viewport so the embedded grid has room. Mirrors Blazor's\r\n // `style=\"--dialog-width: 95% !important\"`. The flex column + explicit\r\n // height-constraints chain (surface → scrollableBody) is the load-\r\n // bearing piece for keeping the footer pinned at the bottom and the\r\n // body scrollable when the grid is taller than the surface allows.\r\n surface: {\r\n maxWidth: '95vw',\r\n width: '95vw',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n },\r\n // Title row — DialogTitle keeps Fluent's heading typography (it\r\n // renders an `<h2>` with the right aria semantics), and the close\r\n // button sits on the right via flex. Wrapping in a row is necessary\r\n // because DialogTitle's intrinsic `action` slot only positions\r\n // correctly inside a `<DialogBody>` grid — without that wrapper, the\r\n // action element renders inline after the title text. Owning the row\r\n // ourselves keeps the X reliably in the top-right.\r\n titleRow: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'flex-start',\r\n columnGap: tokens.spacingHorizontalS,\r\n flex: '0 0 auto',\r\n },\r\n titleText: {\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n },\r\n titleClose: {\r\n flex: '0 0 auto',\r\n },\r\n // Scrollable body region — flex 1 so it expands to fill the space\r\n // between title and footer, `minHeight: 0` to let it shrink past its\r\n // content's intrinsic height (otherwise the grid pushes the surface\r\n // taller than the dialog's maxHeight and the footer scrolls off\r\n // screen). `overflow: auto` is what turns this into the actual scroll\r\n // container.\r\n scrollableBody: {\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n overflowY: 'auto',\r\n overflowX: 'hidden',\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n },\r\n // Footer region. `flex: 0 0 auto` keeps it pinned at the bottom of the\r\n // surface regardless of body size; the inner flex with\r\n // `justify-content: space-between` lays the selected-records readout\r\n // on the left and the action buttons on the right.\r\n footer: {\r\n flex: '0 0 auto',\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n width: '100%',\r\n columnGap: tokens.spacingHorizontalM,\r\n paddingTop: tokens.spacingVerticalM,\r\n marginTop: tokens.spacingVerticalS,\r\n borderTop: `1px solid ${tokens.colorNeutralStroke2}`,\r\n },\r\n selectedText: {\r\n color: tokens.colorNeutralForeground2,\r\n fontSize: tokens.fontSizeBase200,\r\n flex: '1 1 auto',\r\n minWidth: 0,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n textAlign: 'left',\r\n },\r\n footerButtons: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n flex: '0 0 auto',\r\n },\r\n});\r\n\r\nexport interface LookupDialogProps\r\n extends Omit<LookupFormProps, 'onSelectedRecordsChange' | 'className' | 'style'> {\r\n /**\r\n * Whether the dialog is hidden. Controlled by the consumer — pass\r\n * `false` to show, `true` to hide. Defaults to `true` (hidden).\r\n */\r\n hidden?: boolean;\r\n /**\r\n * Fires when the dialog wants to change its hidden state — typically the\r\n * close button, Esc key, or backdrop click. The consumer flips its\r\n * controlling state in response. Cancel and the action button also call\r\n * this with `true` after their own callbacks fire.\r\n */\r\n onHiddenChange?: (hidden: boolean) => void;\r\n /** Title shown in the dialog header. */\r\n title: string;\r\n /**\r\n * Label for the confirm action button. Defaults to `app.buttons.ok.label`\r\n * from the localizer (e.g. \"OK\").\r\n */\r\n actionLabel?: string;\r\n /**\r\n * Fires when the user clicks the action button (only enabled while at\r\n * least one record is selected). Receives the live selected set.\r\n * The dialog auto-hides afterward — pass `event.preventDefault()` style\r\n * cancellation is not supported (consumers wanting to keep the dialog\r\n * open after an action should manage `hidden` separately).\r\n */\r\n onActionClicked?: (records: readonly TableRecord[]) => void;\r\n /**\r\n * Fires whenever the selected-records set changes — same signature as\r\n * `LookupForm.onSelectedRecordsChange`. Useful for consumers that want\r\n * live selection state without waiting for the action click.\r\n */\r\n onSelectedRecordsChange?: (records: readonly TableRecord[]) => void;\r\n /**\r\n * Minimum height applied to the dialog surface. Keeps the dialog from\r\n * shrinking when a view returns fewer records, so switching between\r\n * views (one with 3 rows, another with 50) doesn't make the surface\r\n * jump in size. Default `'600px'`. Pass any CSS length.\r\n */\r\n minHeight?: string;\r\n /**\r\n * Maximum height applied to the dialog surface. Caps the dialog so it\r\n * never overflows the viewport on small screens — when the content\r\n * would push past this, the dialog body scrolls internally instead.\r\n * Default `'95vh'`.\r\n */\r\n maxHeight?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Wraps `<LookupForm>` in a controlled Fluent v9 modal with a title bar\r\n * (title + dismiss button), the LookupForm body, and a footer that shows\r\n * a comma-separated readout of the currently-selected records' primary\r\n * names alongside a cancel + action button pair.\r\n *\r\n * The dialog is fully controlled — pass `hidden` + `onHiddenChange`. Esc\r\n * / backdrop / cancel / action all invoke `onHiddenChange(true)` so the\r\n * consumer can update its state and re-render with the dialog hidden.\r\n */\r\nexport function LookupDialog({\r\n hidden = true,\r\n onHiddenChange,\r\n title,\r\n actionLabel,\r\n onActionClicked,\r\n onSelectedRecordsChange,\r\n minHeight = '600px',\r\n maxHeight = '95vh',\r\n className,\r\n style,\r\n ...lookupFormProps\r\n}: LookupDialogProps) {\r\n const styles = useLookupDialogStyles();\r\n const t = useT();\r\n const [selected, setSelected] = useState<readonly TableRecord[]>([]);\r\n\r\n // Stable callback identity (`useCallback`) + content-aware state update.\r\n // MainGrid's selection-push effect lists this callback in its dep array\r\n // and fires whenever its identity changes; a fresh-each-render arrow\r\n // function would feed back into LookupDialog → re-render → new callback\r\n // → MainGrid effect → setSelected with a new `[]` reference → another\r\n // re-render, locking the page in an empty-to-empty selection loop. The\r\n // functional updater preserves the previous reference when the incoming\r\n // records list is structurally equal, so React's state-equality bailout\r\n // ends the cycle.\r\n const handleSelectedRecordsChange = useCallback(\r\n (records: readonly TableRecord[]) => {\r\n setSelected((prev) => {\r\n if (\r\n prev.length === records.length &&\r\n prev.every((r, i) => r === records[i])\r\n ) {\r\n return prev;\r\n }\r\n return records;\r\n });\r\n onSelectedRecordsChange?.(records);\r\n },\r\n [onSelectedRecordsChange],\r\n );\r\n\r\n const close = () => onHiddenChange?.(true);\r\n\r\n const handleActionClicked = () => {\r\n onActionClicked?.(selected);\r\n close();\r\n };\r\n\r\n // Selected-records readout — comma-separated primary-name values, same\r\n // as Blazor's footer (\"Selected record/s: Acme, Contoso\"). All\r\n // selected records share a tableName (LookupForm clears selection on\r\n // table-switch), so one `useTableMetadata` call for the first record's\r\n // table covers the whole batch.\r\n const tableNameForSelection = selected[0]?.tableName;\r\n const tableMetaQuery = useTableMetadata(tableNameForSelection ?? '', {\r\n enabled: !!tableNameForSelection,\r\n });\r\n\r\n const selectedSummary = useMemo(() => {\r\n if (selected.length === 0) {\r\n return t('app.messages.no-records-selected');\r\n }\r\n const primaryNameColumn = tableMetaQuery.data?.primaryNameColumn;\r\n if (!primaryNameColumn) return t('app.messages.no-records-selected');\r\n const emptyName = t('app.messages.empty-primary-name');\r\n const names = selected.map((record) => {\r\n const value = resolveColumnDisplayValue(record, primaryNameColumn);\r\n return value || emptyName;\r\n });\r\n return names.join(', ');\r\n }, [selected, tableMetaQuery.data, t]);\r\n\r\n const action = actionLabel ?? t('app.buttons.ok.label');\r\n const cancel = t('app.buttons.cancel.label');\r\n const selectedRecordsLabel = t('app.components.lookup-form.selected-records');\r\n\r\n return (\r\n <Dialog\r\n open={!hidden}\r\n onOpenChange={(_, data) => {\r\n if (!data.open) close();\r\n }}\r\n modalType=\"modal\"\r\n >\r\n <DialogSurface\r\n className={mergeClasses('ppp-lookup-dialog', styles.surface, className)}\r\n style={{ minHeight, maxHeight, ...style }}\r\n >\r\n <div className={styles.titleRow}>\r\n <DialogTitle className={styles.titleText}>{title}</DialogTitle>\r\n <Button\r\n className={styles.titleClose}\r\n appearance=\"subtle\"\r\n icon={<Dismiss24Regular />}\r\n aria-label={cancel}\r\n onClick={close}\r\n />\r\n </div>\r\n <div className={styles.scrollableBody}>\r\n <LookupForm\r\n {...lookupFormProps}\r\n onSelectedRecordsChange={handleSelectedRecordsChange}\r\n />\r\n </div>\r\n <div className={styles.footer}>\r\n <div className={styles.selectedText}>\r\n {selectedRecordsLabel} {selectedSummary}\r\n </div>\r\n <div className={styles.footerButtons}>\r\n <Button\r\n appearance=\"primary\"\r\n icon={<LinkAdd20Regular />}\r\n disabled={selected.length === 0}\r\n onClick={handleActionClicked}\r\n >\r\n {action}\r\n </Button>\r\n <Button onClick={close} autoFocus>\r\n {cancel}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogSurface>\r\n </Dialog>\r\n );\r\n}\r\n","import {\r\n Button,\r\n Combobox,\r\n Dropdown,\r\n Field,\r\n Option,\r\n Radio,\r\n RadioGroup,\r\n Spinner,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { Search20Regular } from '@fluentui/react-icons';\r\nimport type {\r\n components,\r\n GridDataRequest,\r\n TableRecord,\r\n} from '@powerportalspro/core';\r\nimport {\r\n useColumnMetadata,\r\n usePowerPortalsPro,\r\n useRecordContext,\r\n useT,\r\n useTableMetadata,\r\n useViewsForTable,\r\n} from '@powerportalspro/react';\r\nimport type { Localizer } from '@powerportalspro/react';\r\nimport { useEffect, useMemo, useRef, useState } from 'react';\r\nimport { Highlighter } from '../components/highlighter.js';\r\nimport { LookupDialog } from '../form/lookup-dialog.js';\r\nimport { resolveColumnDisplayValue } from './base/column-display.js';\r\nimport type { BaseColumnEditProps } from './base/column-edit-types.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport { ChoiceOrientation } from './base/choice-helpers.js';\r\nimport {\r\n getDisplayDescription,\r\n getDisplayLabel,\r\n getLookupMetadata,\r\n isMetadataRequired,\r\n resolveEditState,\r\n} from './base/metadata-helpers.js';\r\nimport { buildFieldValidationProps } from './base/field-validation.js';\r\nimport { useColumnValidation } from './base/use-column-validation.js';\r\nimport {\r\n useGuardedSetValue,\r\n useLookupColumn,\r\n type LookupColumnValue,\r\n} from './base/use-column-edit.js';\r\n\r\ntype Schemas = components['schemas'];\r\n\r\n/**\r\n * Debounce window applied to the typed search query before firing the\r\n * server-side FetchXML. Long enough to dedupe rapid keystrokes but short\r\n * enough that the dropdown feels responsive.\r\n */\r\nconst SEARCH_DEBOUNCE_MS = 300;\r\n\r\n/**\r\n * Sentinel option value for the \"(none)\" radio in {@link ChoiceEditorType.RadioButtons}\r\n * mode when {@link LookupEditProps.displayNullChoice} is true. The empty-guid\r\n * was a natural choice but it collides with Dataverse's \"no value\" guid for\r\n * cleared lookups; a non-guid sentinel keeps the two concepts distinct.\r\n */\r\nconst NULL_CHOICE_VALUE = '__null__';\r\n\r\n/**\r\n * Which control shape the lookup renders. Mirrors Blazor's `LookupEditType`.\r\n */\r\nexport const LookupEditorType = {\r\n /**\r\n * Default. Debounced server-side typeahead via Fluent {@link Combobox}.\r\n * Use this whenever the target table has more records than would\r\n * comfortably fit in a flat list.\r\n */\r\n AutoComplete: 'autoComplete',\r\n /**\r\n * Pre-fetched top-N records rendered as a Fluent {@link Dropdown}. No\r\n * typing — the consumer just picks from the visible list. Best for\r\n * small / bounded target tables (configuration lookups, status owners,\r\n * etc.). Cap controlled by {@link LookupEditProps.maxRecordsReturned}.\r\n */\r\n Dropdown: 'dropdown',\r\n /**\r\n * Pre-fetched top-N records rendered as a Fluent {@link RadioGroup}. Same\r\n * data shape as `Dropdown`; a \"(none)\" radio can be added on the front via\r\n * {@link LookupEditProps.displayNullChoice}.\r\n */\r\n RadioButtons: 'radioButtons',\r\n} as const;\r\nexport type LookupEditorType = (typeof LookupEditorType)[keyof typeof LookupEditorType];\r\n\r\nexport interface LookupEditProps extends BaseColumnEditProps {\r\n /**\r\n * Which control shape to render. Defaults to {@link LookupEditorType.AutoComplete}.\r\n */\r\n editorType?: LookupEditorType;\r\n /**\r\n * Maximum number of records returned from the server per search. The\r\n * dropdown lists at most this many matches; in AutoComplete mode users\r\n * narrow further by typing, in Dropdown/RadioButtons modes this is the\r\n * fixed pool of pickable records. Default 20.\r\n */\r\n maxRecordsReturned?: number;\r\n /** Optional placeholder shown when the input is empty. */\r\n placeholder?: string;\r\n /**\r\n * Which target table to default to when the lookup is polymorphic. Ignored\r\n * when the metadata has only one target. Falls back to the existing value's\r\n * table, then to the first declared target.\r\n */\r\n defaultTable?: string;\r\n /**\r\n * Only applicable when {@link editorType} is {@link LookupEditorType.RadioButtons}.\r\n * Adds a \"(none)\" radio button on the front of the group whose selection\r\n * clears the lookup column. Defaults to `true`.\r\n */\r\n displayNullChoice?: boolean;\r\n /**\r\n * Only applicable when {@link editorType} is {@link LookupEditorType.RadioButtons}.\r\n * Layout direction for the radio buttons. Defaults to\r\n * {@link ChoiceOrientation.Vertical}.\r\n */\r\n radioButtonOrientation?: ChoiceOrientation;\r\n /**\r\n * Maximum height of the dropdown popup (AutoComplete + Dropdown modes).\r\n * Beyond this height the listbox scrolls. Accepts any CSS length —\r\n * pixels (`'400px'`), viewport-relative (`'50vh'`), etc. Default\r\n * `'250px'`.\r\n */\r\n popupMaxHeight?: string;\r\n /**\r\n * Minimum height of the dropdown popup (AutoComplete + Dropdown modes).\r\n * Default `'150px'`. The min-height matters even when there are no\r\n * results: in polymorphic AutoComplete mode the inner target-table\r\n * picker renders below the header, and an outer popup that's only as\r\n * tall as a \"No matches\" line clips the inner picker's options — the\r\n * default leaves enough room for ~3 inner options to display without\r\n * scroll.\r\n */\r\n popupMinHeight?: string;\r\n /**\r\n * Optional view id (GUID) pinning which Dataverse view the typeahead\r\n * uses to determine which columns to show in each result row. When\r\n * omitted, the target table's Lookup view (ViewType.Lookup, 64) is\r\n * auto-selected; when no Lookup view exists, the option template\r\n * falls back to showing just the primary name column.\r\n */\r\n defaultViewId?: string;\r\n /**\r\n * Per-table allow-list of view ids the Advanced Search dialog can\r\n * present. Keys are target table logical names; values are arrays of\r\n * view ids. Tables missing from this map (or with empty arrays) let\r\n * the dialog show every Public view for the table. Has no effect on\r\n * the typeahead's Lookup-view-driven option template — only on the\r\n * `<LookupDialog>` rendered from the dropdown's Advanced Search button.\r\n */\r\n viewIds?: Record<string, readonly string[]>;\r\n}\r\n\r\nconst useLookupStyles = makeStyles({\r\n // Listbox sizing — min/max-height are dynamic (driven by the\r\n // `popupMinHeight` / `popupMaxHeight` props) and applied via inline\r\n // style on the slot, so only the always-on bits live here. Inline\r\n // style is used for size because Fluent's own Listbox class can carry\r\n // sizing rules with similar specificity to our Griffel class — inline\r\n // style beats both. The explicit `padding-top: 0` eliminates the gap\r\n // above the sticky polymorphic header where scrolling options would\r\n // otherwise briefly bleed through before disappearing behind the\r\n // header. (Fluent's Listbox applies a small top inset by default;\r\n // zeroing it lets the sticky header touch the listbox's top edge\r\n // exactly.)\r\n listbox: {\r\n overflowY: 'auto',\r\n paddingTop: '0',\r\n },\r\n // Polymorphic target-table picker rendered inside the Combobox's listbox\r\n // (above the options), matching Blazor's `HeaderContent`. The inner\r\n // Dropdown uses `inlinePopup` so its option list renders as a DOM\r\n // descendant of the trigger rather than being portaled to body — that\r\n // keeps the inner popup INSIDE the outer Combobox's tree, so clicking an\r\n // inner option isn't treated as an \"outside click\" that would dismiss\r\n // the outer Combobox.\r\n //\r\n // The wrapper's `onMouseDown` preventDefault is the second half of the\r\n // trick: mousedown on the inner Dropdown trigger (or its options) would\r\n // otherwise pull focus out of the outer Combobox's input and the outer\r\n // popup would blur-close before the click registered. preventDefault on\r\n // mousedown blocks the focus shift without blocking the subsequent click\r\n // event, so the inner Dropdown still opens and selects as usual.\r\n //\r\n // `position: sticky; top: 0` pins the header at the top of the scrolling\r\n // listbox so it stays visible while the user scrolls through options.\r\n // The background fill is required because sticky elements still allow\r\n // content to scroll BEHIND them — without an opaque fill, options would\r\n // bleed through.\r\n targetPickerHeader: {\r\n padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,\r\n borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,\r\n marginBottom: tokens.spacingVerticalXXS,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalS,\r\n position: 'sticky',\r\n top: 0,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n zIndex: 1,\r\n },\r\n targetPickerLabel: {\r\n color: tokens.colorNeutralForeground3,\r\n fontSize: tokens.fontSizeBase200,\r\n whiteSpace: 'nowrap',\r\n flex: '0 0 auto',\r\n },\r\n targetPickerControl: {\r\n flex: '1 1 auto',\r\n minWidth: '0',\r\n },\r\n statusOption: {\r\n color: tokens.colorNeutralForeground3,\r\n fontStyle: 'italic',\r\n },\r\n loadingOption: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n // Multi-column option template — one row per result, view columns\r\n // stacked vertically. The first column is bold (the \"primary\" line);\r\n // subsequent columns are normal weight + dimmer. Mirrors Blazor's\r\n // option template (`FluentStack Orientation=\"Vertical\"` with\r\n // bold-first-column styling).\r\n optionStack: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n width: '100%',\r\n gap: '2px',\r\n paddingTop: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n },\r\n optionRowPrimary: {\r\n fontWeight: tokens.fontWeightSemibold,\r\n },\r\n optionRowSecondary: {\r\n color: tokens.colorNeutralForeground2,\r\n fontSize: tokens.fontSizeBase200,\r\n },\r\n optionNull: {\r\n color: tokens.colorNeutralForeground4,\r\n fontStyle: 'italic',\r\n },\r\n // Sticky footer hosting the Advanced Search button. Mirrors the\r\n // sticky-top behavior of `targetPickerHeader` but pinned at the bottom\r\n // of the listbox so the button stays visible while scrolling through\r\n // results. Same `onMouseDown` preventDefault trick applies in the JSX\r\n // (block focus shift that would dismiss the outer Combobox before the\r\n // click registers).\r\n advancedSearchFooter: {\r\n display: 'flex',\r\n justifyContent: 'flex-end',\r\n padding: tokens.spacingHorizontalS,\r\n borderTop: `1px solid ${tokens.colorNeutralStroke2}`,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n position: 'sticky',\r\n bottom: 0,\r\n zIndex: 1,\r\n },\r\n});\r\n\r\ninterface LookupSearchResult {\r\n id: string;\r\n name: string;\r\n tableName: string;\r\n /**\r\n * Full server record. Retained on each result so the multi-column\r\n * option template can resolve view-column display values via\r\n * `resolveColumnDisplayValue` without re-fetching. Stripped of\r\n * properties when not needed by the view template.\r\n */\r\n record: Schemas['TableRecord'];\r\n}\r\n\r\n/**\r\n * Single-record lookup editor bound to a `LookupValue` ($type 6). Three\r\n * editor shapes via {@link editorType}:\r\n *\r\n * - **AutoComplete** (default) — debounced typeahead via Fluent {@link Combobox}.\r\n * Polymorphic columns get a target-table picker rendered alongside the\r\n * combobox; consumers can pin the initial target via {@link defaultTable}.\r\n * - **Dropdown** — pre-fetched top-N records in a Fluent {@link Dropdown}.\r\n * No typing; the user picks from the visible list.\r\n * - **RadioButtons** — pre-fetched top-N records as a {@link RadioGroup}.\r\n * Honors {@link displayNullChoice} for an explicit \"(none)\" radio and\r\n * {@link radioButtonOrientation} for horizontal/vertical layout.\r\n *\r\n * **Search behavior:** When the target table has a Lookup view configured\r\n * (or a view is pinned via {@link defaultViewId}), the typed query is\r\n * OR-merged across every searchable column the view declares — string\r\n * columns via FetchXML `like`, lookup columns via LEFT OUTER joins to the\r\n * referenced tables' primary-name columns, choice/multi-select columns via\r\n * localized-label match → `in`/`contain-values`. The composition is\r\n * server-side (`GridService.LoadDataAsync` → `FetchXmlQueryComposer.ApplySearchAsync`).\r\n * When no Lookup view exists, the fallback FetchXML covers `primaryId +\r\n * primaryName` only and the server searches against `primaryName` alone.\r\n *\r\n * Option-template rendering follows the same shape: when the view declares\r\n * extra columns beyond the primary name, the dropdown renders each row as\r\n * a vertical stack with the primary column bolded and remaining columns\r\n * dimmer below; the typed query is passed through `<Highlighter>` so\r\n * matching substrings render highlighted in every rendered column.\r\n */\r\nexport function LookupEdit({\r\n columnName,\r\n label,\r\n description,\r\n displayLabelWhenAvailable = true,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n displayValidationErrorMessage = true,\r\n required,\r\n readOnly,\r\n disabled,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n editorType = LookupEditorType.AutoComplete,\r\n maxRecordsReturned = 20,\r\n placeholder,\r\n defaultTable,\r\n displayNullChoice = true,\r\n radioButtonOrientation = ChoiceOrientation.Vertical,\r\n popupMaxHeight = '250px',\r\n popupMinHeight = '150px',\r\n defaultViewId,\r\n viewIds,\r\n validate,\r\n}: LookupEditProps) {\r\n const { value: lookupValue, setValue: rawSetLookupValue } = useLookupColumn(columnName);\r\n const { record } = useRecordContext();\r\n const metadata = useColumnMetadata(columnName);\r\n const styles = useLookupStyles();\r\n const { ppp, tableMetadataCache } = usePowerPortalsPro();\r\n const t = useT();\r\n\r\n const resolvedLabel = label ?? getDisplayLabel(metadata, columnName, record?.tableName, t);\r\n const resolvedDescription = description ?? getDisplayDescription(metadata, columnName, record?.tableName, t);\r\n const resolvedRequired = required ?? isMetadataRequired(metadata);\r\n const { readOnly: resolvedReadOnly, disabled: resolvedDisabled } = resolveEditState(\r\n metadata,\r\n record,\r\n readOnly,\r\n disabled,\r\n );\r\n const setLookupValue = useGuardedSetValue(\r\n rawSetLookupValue,\r\n resolvedReadOnly,\r\n resolvedDisabled,\r\n );\r\n\r\n const lookupMeta = getLookupMetadata(metadata);\r\n const targetTables = lookupMeta?.targetTables ?? [];\r\n const isPolymorphic = targetTables.length > 1;\r\n\r\n // User-picked target table (via the polymorphic picker). Starts undefined\r\n // so the resolved-target fallback chain picks the right initial value;\r\n // once the user explicitly chooses a table, this wins until they switch\r\n // again. The fallback chain mirrors Blazor's:\r\n // selectedTargetTable → defaultTable → existing value's table → targetTables[0]\r\n const [selectedTargetTable, setSelectedTargetTable] = useState<string | undefined>(undefined);\r\n const effectiveTargetTable =\r\n selectedTargetTable\r\n ?? (defaultTable && targetTables.includes(defaultTable) ? defaultTable : undefined)\r\n ?? lookupValue?.tableName\r\n ?? targetTables[0];\r\n\r\n // Target-table metadata needed for primary id / primary name column\r\n // names — those drive both the FetchXML query and the response parsing.\r\n const targetMetaQuery = useTableMetadata(effectiveTargetTable ?? '', {\r\n enabled: Boolean(effectiveTargetTable),\r\n });\r\n const primaryIdColumn = targetMetaQuery.data?.primaryIdColumn;\r\n const primaryNameColumn = targetMetaQuery.data?.primaryNameColumn;\r\n\r\n // Views for the target table — drives the option template's multi-column\r\n // layout. The Lookup view (ViewType.Lookup = 64) defines which columns to\r\n // show in each option row when the user opens the dropdown. Falls back\r\n // to the primary name column when no Lookup view exists; consumer can\r\n // also pin a specific view via `defaultViewId`. Bitwise check on the\r\n // numeric ViewType — Dataverse declares `Lookup = 64` as a flag value so\r\n // multi-purpose views (e.g. Lookup | QuickFind) still match.\r\n const viewsQuery = useViewsForTable(effectiveTargetTable ?? '', {\r\n enabled: Boolean(effectiveTargetTable),\r\n });\r\n const lookupView = useMemo(() => {\r\n const views = viewsQuery.data;\r\n if (!views || views.length === 0) return undefined;\r\n if (defaultViewId) {\r\n return views.find((v) => v.id === defaultViewId);\r\n }\r\n return views.find((v) => {\r\n const t = v.type;\r\n return typeof t === 'number' && (t & 64) === 64;\r\n });\r\n }, [viewsQuery.data, defaultViewId]);\r\n\r\n // Column names the option template renders, in display order. First\r\n // entry is the \"primary\" line (bold). When no Lookup view is configured,\r\n // a single-column view of the primary name is the fallback.\r\n const viewColumnNames = useMemo<string[]>(() => {\r\n if (lookupView?.columns && lookupView.columns.length > 0) {\r\n return lookupView.columns.map((c) => c.columnName);\r\n }\r\n return primaryNameColumn ? [primaryNameColumn] : [];\r\n }, [lookupView, primaryNameColumn]);\r\n\r\n // Column metadata for the view's columns — used by the option template's\r\n // display-value resolver for type-aware fallback rendering.\r\n const viewColumnMetadata = useMemo<Map<string, Schemas['ColumnMetadataBase']>>(() => {\r\n const m = new Map<string, Schemas['ColumnMetadataBase']>();\r\n const cols = targetMetaQuery.data?.columns ?? [];\r\n for (const c of cols) m.set(c.columnName, c);\r\n return m;\r\n }, [targetMetaQuery.data]);\r\n\r\n\r\n // Search state. For AutoComplete mode, `query === null` means \"not actively\r\n // searching\" — the Combobox shows the committed value's name; the popup\r\n // opens with an empty query (top-N) and closes back to null. For\r\n // Dropdown/RadioButtons modes there's no open/close interaction and we\r\n // always want the pre-fetched results visible, so query starts at '' and\r\n // never goes back to null (only updated when the target table changes,\r\n // which the search effect picks up automatically).\r\n const [query, setQuery] = useState<string | null>(\r\n editorType === LookupEditorType.AutoComplete ? null : '',\r\n );\r\n const [results, setResults] = useState<LookupSearchResult[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n // Outer Combobox open state. Controlled (rather than uncontrolled) so we\r\n // can keep the popup visible across a target-table change in the inner\r\n // polymorphic picker — without control, the inner click bubbles up and\r\n // Combobox interprets it as an option selection, which auto-closes the\r\n // popup. AutoComplete mode is the only one that needs this; Dropdown\r\n // and RadioButtons handle their own open/close state internally.\r\n const [comboboxOpen, setComboboxOpen] = useState(false);\r\n\r\n // Switching editorType after mount is uncommon but should still leave the\r\n // initial-fetch behavior consistent. When the consumer flips into a\r\n // pre-fetched mode, kick off the search by promoting `null` to `''`.\r\n useEffect(() => {\r\n if (editorType !== LookupEditorType.AutoComplete && query === null) {\r\n setQuery('');\r\n }\r\n }, [editorType, query]);\r\n\r\n // Debounced search effect. Re-runs on query / target-table changes; aborts\r\n // in-flight requests on dep change or unmount. Doesn't fire when query is\r\n // null (AutoComplete, popup closed) or when the target table's metadata\r\n // hasn't loaded yet.\r\n const abortRef = useRef<AbortController | null>(null);\r\n useEffect(() => {\r\n if (query === null) return;\r\n if (!effectiveTargetTable || !primaryIdColumn || !primaryNameColumn) return;\r\n\r\n const timer = setTimeout(() => {\r\n abortRef.current?.abort();\r\n const controller = new AbortController();\r\n abortRef.current = controller;\r\n\r\n setLoading(true);\r\n setError(null);\r\n\r\n // The server's POST /api/grids/data composes the FetchXML on\r\n // our behalf — applying search across the view's searchable\r\n // columns, replacing sorts with primary-name-asc, AND-merging the\r\n // search filter into the view's existing filter. We just supply\r\n // the source (viewId when a Lookup view loaded; else a minimal\r\n // FetchXML covering primaryId + primaryName + the view's\r\n // additional columns) and the user's query.\r\n const searchRequest: GridDataRequest = lookupView\r\n ? {\r\n viewId: lookupView.id,\r\n searchText: query,\r\n sorts: [{ columnName: primaryNameColumn, descending: false }],\r\n pageNumber: 1,\r\n pageSize: maxRecordsReturned,\r\n }\r\n : {\r\n fetchXml: buildMinimalFetchXml(\r\n effectiveTargetTable,\r\n primaryIdColumn,\r\n primaryNameColumn,\r\n viewColumnNames,\r\n ),\r\n searchText: query,\r\n sorts: [{ columnName: primaryNameColumn, descending: false }],\r\n pageNumber: 1,\r\n pageSize: maxRecordsReturned,\r\n };\r\n\r\n ppp\r\n .loadGridAsync(searchRequest, controller.signal)\r\n .then((resp) => {\r\n if (controller.signal.aborted) return;\r\n const mapped: LookupSearchResult[] = resp.tableRecords.map((r) => {\r\n const nameCol = r.properties?.[primaryNameColumn] as\r\n | Schemas['ColumnValueBaseStringValue']\r\n | undefined;\r\n return {\r\n id: r.id ?? '',\r\n name: nameCol?.value ?? '',\r\n tableName: r.tableName,\r\n record: r,\r\n };\r\n });\r\n setResults(mapped);\r\n })\r\n .catch((e: unknown) => {\r\n if (controller.signal.aborted) return;\r\n setError(e instanceof Error ? e.message : 'Search failed');\r\n setResults([]);\r\n })\r\n .finally(() => {\r\n if (!controller.signal.aborted) setLoading(false);\r\n });\r\n }, SEARCH_DEBOUNCE_MS);\r\n\r\n return () => {\r\n clearTimeout(timer);\r\n };\r\n // `viewColumnNames` / `lookupView` are `useMemo` results keyed on\r\n // their upstream inputs, so their identities change only when the\r\n // underlying view/metadata resolves — safe to put in the dep array\r\n // without spurious re-fetches on every render.\r\n }, [\r\n query,\r\n effectiveTargetTable,\r\n primaryIdColumn,\r\n primaryNameColumn,\r\n maxRecordsReturned,\r\n ppp,\r\n viewColumnNames,\r\n lookupView,\r\n ]);\r\n\r\n // Abort any in-flight fetch on unmount.\r\n useEffect(() => () => abortRef.current?.abort(), []);\r\n\r\n // Validation — wire the required check through the existing column-validation\r\n // pipeline. Use `kind: 'string'` with the lookup id as the value: a null/\r\n // undefined/empty id correctly triggers \"required and not set\", and we don't\r\n // need length checks for a guid.\r\n const validationErrors = useColumnValidation(\r\n columnName,\r\n {\r\n kind: 'string',\r\n required: resolvedRequired,\r\n label: resolvedLabel,\r\n value: lookupValue?.id ?? null,\r\n },\r\n validate,\r\n );\r\n\r\n if (!isVisible) return null;\r\n\r\n const showInfoIcon =\r\n descriptionLocation === DescriptionLocation.Tooltip && resolvedDescription !== undefined;\r\n const fieldHint =\r\n descriptionLocation === DescriptionLocation.BelowControl\r\n ? resolvedDescription ?? null\r\n : null;\r\n\r\n const labelSlot = displayLabelWhenAvailable\r\n ? {\r\n children: (\r\n <>\r\n {resolvedLabel}\r\n {showInfoIcon && (\r\n <DescriptionIcon description={resolvedDescription!} ariaLabel={resolvedLabel} />\r\n )}\r\n </>\r\n ),\r\n }\r\n : null;\r\n\r\n const fieldValidationProps = buildFieldValidationProps(\r\n validationErrors,\r\n displayValidationErrorMessage,\r\n );\r\n\r\n // Fluent Combobox / Dropdown have no `readOnly` slot — collapse readOnly +\r\n // disabled into a single inactive flag, same convention as the choice\r\n // controls. (Combobox / Dropdown also have no `required` prop; the Field\r\n // wrapper surfaces the required indicator via the label slot.)\r\n const isInactive = resolvedDisabled || resolvedReadOnly;\r\n\r\n const onPickRecord = (pickedId: string | null) => {\r\n if (!pickedId) {\r\n setLookupValue(null);\r\n return;\r\n }\r\n const picked = results.find((r) => r.id === pickedId);\r\n if (picked) {\r\n setLookupValue({\r\n id: picked.id,\r\n name: picked.name,\r\n tableName: picked.tableName,\r\n });\r\n }\r\n };\r\n\r\n // Advanced Search dialog state — controlled by the LookupEdit. The\r\n // dialog mounts inside the AutoComplete-mode dropdown's footer via the\r\n // button below; opening it closes the Combobox popup first (matches\r\n // Blazor's `_ref.CloseDropdownAsync()`) so the dialog doesn't render\r\n // on top of a still-visible dropdown.\r\n const [dialogHidden, setDialogHidden] = useState(true);\r\n\r\n // Title for the Advanced Search dialog — matches Blazor's\r\n // `app.messages.select-lookup-value` interpolation with the column's\r\n // display label. Recomputed when the label changes (e.g. when the\r\n // localization bundle resolves after first render).\r\n const dialogTitle = useMemo(\r\n () => t('app.messages.select-lookup-value', [resolvedLabel]),\r\n [t, resolvedLabel],\r\n );\r\n\r\n const onAdvancedSearchClicked = () => {\r\n setComboboxOpen(false);\r\n setDialogHidden(false);\r\n };\r\n\r\n // Picked-from-dialog handler. The dialog returns full TableRecord\r\n // objects, so the picked record's primary-name value (the \"name\"\r\n // half of LookupColumnValue) needs to be resolved against the\r\n // record's table metadata. Common path: the dialog stayed on the\r\n // current target — `primaryNameColumn` is already loaded. Fallback\r\n // path (polymorphic + user switched tables in the dialog): one-off\r\n // fetch via the client.\r\n const onDialogActionClicked = async (records: readonly TableRecord[]) => {\r\n if (records.length !== 1) {\r\n setDialogHidden(true);\r\n return;\r\n }\r\n const record = records[0]!;\r\n if (!record.id) {\r\n setDialogHidden(true);\r\n return;\r\n }\r\n let nameColumn = record.tableName === effectiveTargetTable ? primaryNameColumn : undefined;\r\n if (!nameColumn) {\r\n try {\r\n // Routed through the table-metadata cache — polymorphic lookups\r\n // often pick from the same handful of tables across a session,\r\n // and the in-memory layer means the second pick of a \"contact\"\r\n // (or whichever) is free.\r\n const meta = await tableMetadataCache.getAsync(record.tableName);\r\n nameColumn = meta?.primaryNameColumn;\r\n } catch {\r\n // Fall through — `name` stays blank, the lookup row will show\r\n // the id as its label until the consumer re-saves.\r\n }\r\n }\r\n const name = nameColumn ? resolveColumnDisplayValue(record, nameColumn) : null;\r\n setLookupValue({\r\n id: record.id,\r\n name: name ?? '',\r\n tableName: record.tableName,\r\n });\r\n // Realign the polymorphic picker if the user picked from a\r\n // different table in the dialog — keeps the dropdown reflective of\r\n // the new value's table on next open.\r\n if (record.tableName !== effectiveTargetTable && targetTables.includes(record.tableName)) {\r\n setSelectedTargetTable(record.tableName);\r\n }\r\n setDialogHidden(true);\r\n };\r\n\r\n // Polymorphic target-table picker. Only rendered in AutoComplete mode and\r\n // only when the metadata declares >1 target. Switching the target clears\r\n // the working state so a stale query/results from the old table don't\r\n // confuse the new one.\r\n const onTargetTableChange = (next: string) => {\r\n if (next === effectiveTargetTable) return;\r\n setSelectedTargetTable(next);\r\n setResults([]);\r\n setError(null);\r\n // Promote query back to '' so the search effect re-fires against the\r\n // new table with an unfiltered top-N (matches the \"just opened\" state).\r\n setQuery('');\r\n // Inner picker's option click bubbles to the outer Combobox which would\r\n // otherwise interpret it as an option selection and auto-close. React\r\n // batches state updates inside the same event tick, so this trumps any\r\n // setComboboxOpen(false) the outer's onOpenChange already fired.\r\n setComboboxOpen(true);\r\n };\r\n\r\n let control: React.ReactElement;\r\n\r\n if (editorType === LookupEditorType.RadioButtons) {\r\n control = renderRadioGroup({\r\n id,\r\n className,\r\n style,\r\n lookupValue,\r\n results,\r\n loading,\r\n error,\r\n displayNullChoice,\r\n radioButtonOrientation,\r\n isInactive,\r\n resolvedRequired,\r\n onPick: onPickRecord,\r\n t,\r\n styles,\r\n });\r\n } else if (editorType === LookupEditorType.Dropdown) {\r\n control = renderDropdown({\r\n id,\r\n className,\r\n style,\r\n lookupValue,\r\n results,\r\n loading,\r\n error,\r\n isInactive,\r\n placeholder,\r\n popupMaxHeight,\r\n popupMinHeight,\r\n viewColumnNames,\r\n viewColumnMetadata,\r\n onPick: onPickRecord,\r\n styles,\r\n });\r\n } else {\r\n control = renderAutoComplete({\r\n id,\r\n className,\r\n style,\r\n lookupValue,\r\n results,\r\n loading,\r\n error,\r\n isInactive,\r\n placeholder,\r\n popupMaxHeight,\r\n popupMinHeight,\r\n query,\r\n setQuery,\r\n open: comboboxOpen,\r\n setOpen: setComboboxOpen,\r\n onPick: onPickRecord,\r\n // Target picker is part of the AutoComplete row — pass everything it\r\n // needs to render and route changes back.\r\n isPolymorphic,\r\n targetTables,\r\n effectiveTargetTable,\r\n onTargetTableChange,\r\n // Multi-column option template inputs — view-loaded column list +\r\n // type-metadata map for display-value resolution.\r\n viewColumnNames,\r\n viewColumnMetadata,\r\n onAdvancedSearchClicked,\r\n t,\r\n styles,\r\n });\r\n }\r\n\r\n return (\r\n <>\r\n <Field\r\n className={mergeClasses('ppp-lookup-edit')}\r\n label={labelSlot}\r\n hint={fieldHint}\r\n required={resolvedRequired}\r\n {...fieldValidationProps}\r\n >\r\n {control}\r\n </Field>\r\n {/* Advanced Search dialog. Only AutoComplete mode exposes the\r\n trigger button, so other editor types don't need the dialog\r\n mounted (saves the initial table-metadata fetch chain a\r\n hidden dialog would trigger). */}\r\n {editorType === LookupEditorType.AutoComplete && targetTables.length > 0 && (\r\n <LookupDialog\r\n hidden={dialogHidden}\r\n onHiddenChange={setDialogHidden}\r\n title={dialogTitle}\r\n tableNames={targetTables}\r\n {...(effectiveTargetTable !== undefined && { defaultTable: effectiveTargetTable })}\r\n selectionMode=\"single\"\r\n {...(viewIds !== undefined && { viewIds })}\r\n actionLabel={t('app.buttons.select.label')}\r\n onActionClicked={onDialogActionClicked}\r\n />\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-editorType renderers. Pulled out so the top-level component stays\r\n// focused on data + state and each render path can read top-to-bottom\r\n// without nesting.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction renderAutoComplete(input: {\r\n id: string | undefined;\r\n className: string | undefined;\r\n style: React.CSSProperties | undefined;\r\n lookupValue: LookupColumnValue | undefined;\r\n results: LookupSearchResult[];\r\n loading: boolean;\r\n error: string | null;\r\n isInactive: boolean;\r\n placeholder: string | undefined;\r\n popupMaxHeight: string;\r\n popupMinHeight: string;\r\n query: string | null;\r\n setQuery: (next: string | null) => void;\r\n open: boolean;\r\n setOpen: (next: boolean) => void;\r\n onPick: (pickedId: string | null) => void;\r\n isPolymorphic: boolean;\r\n targetTables: string[];\r\n effectiveTargetTable: string | undefined;\r\n onTargetTableChange: (next: string) => void;\r\n viewColumnNames: string[];\r\n viewColumnMetadata: Map<string, Schemas['ColumnMetadataBase']>;\r\n onAdvancedSearchClicked: () => void;\r\n t: Localizer['t'];\r\n styles: ReturnType<typeof useLookupStyles>;\r\n}): React.ReactElement {\r\n const {\r\n id, className, style,\r\n lookupValue, results, loading, error, isInactive, placeholder,\r\n popupMaxHeight, popupMinHeight,\r\n query, setQuery, open, setOpen, onPick,\r\n isPolymorphic, targetTables, effectiveTargetTable, onTargetTableChange,\r\n viewColumnNames, viewColumnMetadata,\r\n onAdvancedSearchClicked,\r\n t, styles,\r\n } = input;\r\n\r\n const displayValue = query !== null ? query : lookupValue?.name ?? '';\r\n const selectedOptions = lookupValue?.id ? [lookupValue.id] : [];\r\n\r\n return (\r\n <Combobox\r\n id={id}\r\n className={className}\r\n style={style}\r\n clearable\r\n {...(placeholder !== undefined && { placeholder })}\r\n value={displayValue}\r\n selectedOptions={selectedOptions}\r\n disabled={isInactive}\r\n open={open}\r\n // Spyglass instead of the default chevron — the AutoComplete mode\r\n // is conceptually a SEARCH, not a generic \"open dropdown to pick\r\n // from a list\", so the icon should communicate that affordance.\r\n // Same icon is reused on the Advanced Search footer button below\r\n // for visual consistency.\r\n expandIcon={<Search20Regular />}\r\n // Positioning:\r\n // - `position: 'below'` + `align: 'start'`. Flip middleware\r\n // stays enabled (no `pinned`) so the popup auto-positions\r\n // ABOVE the input when there isn't enough room below — far\r\n // better UX than clamping to a thin sliver above the taskbar.\r\n // - NO `autoSize`. We previously had `'height-always'` to clamp\r\n // the popup to the viewport, but combined with the listbox's\r\n // `overflow: auto` it caused scrollbar oscillation near the\r\n // bottom of the page: autoSize would clamp height → listbox\r\n // content overflowed → scrollbar appeared → width shrank →\r\n // reflow → autoSize recomputed → scrollbar disappeared →\r\n // cycle. (Same issue independently diagnosed + fixed on\r\n // ManyToManyLookupEdit — see its inline comment.) The\r\n // listbox's `maxHeight: popupMaxHeight` is the sole height\r\n // cap now; flip handles the no-room-below case.\r\n positioning={{\r\n position: 'below',\r\n align: 'start',\r\n }}\r\n listbox={{\r\n className: styles.listbox,\r\n style: { maxHeight: popupMaxHeight, minHeight: popupMinHeight },\r\n }}\r\n onChange={(e) => {\r\n setQuery((e.target as HTMLInputElement).value);\r\n }}\r\n onOpenChange={(_, data) => {\r\n setOpen(data.open);\r\n if (data.open && query === null) {\r\n // Only auto-fire a browse search (empty query → top-N) when\r\n // there's no committed value. With a committed value, leave\r\n // the input showing the existing name and let the user decide\r\n // whether to clear/edit it — clicking the field shouldn't\r\n // erase what's already selected or kick off a search the user\r\n // didn't ask for.\r\n if (lookupValue === undefined) {\r\n setQuery('');\r\n }\r\n } else if (!data.open) {\r\n setQuery(null);\r\n }\r\n }}\r\n onOptionSelect={(_, data) => {\r\n // Inner polymorphic picker's option values are target-table names.\r\n // The outer Combobox sees those clicks via event delegation when\r\n // `inlinePopup` keeps the inner popup inside the outer's tree —\r\n // filter them out so the outer doesn't try to \"select\" them as\r\n // records (which would null out the lookup and exit search mode).\r\n if (data.optionValue && targetTables.includes(data.optionValue)) {\r\n return;\r\n }\r\n onPick(data.optionValue ?? null);\r\n setQuery(null);\r\n }}\r\n >\r\n {/* Polymorphic target picker — a nested Dropdown rendered inside the\r\n outer Combobox's listbox, matching Blazor's `HeaderContent`. See\r\n `targetPickerHeader` style for the focus/click-handling tricks\r\n needed to make a nested popover-style control work inside another\r\n popover. */}\r\n {isPolymorphic && effectiveTargetTable && (\r\n <div\r\n className={styles.targetPickerHeader}\r\n onMouseDown={(e) => e.preventDefault()}\r\n >\r\n <span className={styles.targetPickerLabel}>\r\n {tTargetPickerLabel(t)}\r\n </span>\r\n <Dropdown\r\n size=\"small\"\r\n inlinePopup\r\n className={styles.targetPickerControl}\r\n selectedOptions={[effectiveTargetTable]}\r\n value={tableLabel(t, effectiveTargetTable)}\r\n onOptionSelect={(event, data) => {\r\n // Stop propagation so the click doesn't bubble up to the\r\n // outer Combobox's listbox click handler, which would\r\n // otherwise treat the inner option click as one of its own\r\n // selections (auto-closing the outer popup). The outer's\r\n // `onOptionSelect` already filters target-table values\r\n // defensively too — this is the cheaper / cleaner fix.\r\n event.stopPropagation();\r\n if (data.optionValue) onTargetTableChange(data.optionValue);\r\n }}\r\n // Same reason: the inner Dropdown's open/close events bubble\r\n // up to anything listening on the outer Combobox's tree.\r\n // Stopping propagation here keeps the outer's open state from\r\n // reacting to the inner picker's internal toggles.\r\n onOpenChange={(event) => {\r\n event.stopPropagation();\r\n }}\r\n >\r\n {targetTables.map((name) => (\r\n <Option key={name} value={name} text={tableLabel(t, name)}>\r\n {tableLabel(t, name)}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </div>\r\n )}\r\n {renderListboxContents({\r\n loading,\r\n error,\r\n results,\r\n lookupValue,\r\n viewColumnNames,\r\n viewColumnMetadata,\r\n // Highlight what the user typed. `query` is null when the popup\r\n // just opened with no query — the highlighter treats null as \"no\r\n // highlight\" so the initial top-N list renders plain.\r\n searchText: query,\r\n styles,\r\n })}\r\n {/* Sticky footer — Advanced Search button. `onMouseDown`\r\n preventDefault blocks the focus shift that would otherwise\r\n dismiss the outer Combobox before the click registers; same\r\n trick used on the polymorphic header above. */}\r\n <div\r\n className={styles.advancedSearchFooter}\r\n onMouseDown={(e) => e.preventDefault()}\r\n >\r\n <Button\r\n size=\"small\"\r\n icon={<Search20Regular />}\r\n onClick={onAdvancedSearchClicked}\r\n >\r\n {t('app.buttons.advanced-search.label')}\r\n </Button>\r\n </div>\r\n </Combobox>\r\n );\r\n}\r\n\r\nfunction renderDropdown(input: {\r\n id: string | undefined;\r\n className: string | undefined;\r\n style: React.CSSProperties | undefined;\r\n lookupValue: LookupColumnValue | undefined;\r\n results: LookupSearchResult[];\r\n loading: boolean;\r\n error: string | null;\r\n isInactive: boolean;\r\n placeholder: string | undefined;\r\n popupMaxHeight: string;\r\n popupMinHeight: string;\r\n viewColumnNames: string[];\r\n viewColumnMetadata: Map<string, Schemas['ColumnMetadataBase']>;\r\n onPick: (pickedId: string | null) => void;\r\n styles: ReturnType<typeof useLookupStyles>;\r\n}): React.ReactElement {\r\n const {\r\n id, className, style,\r\n lookupValue, results, loading, error, isInactive, placeholder,\r\n popupMaxHeight, popupMinHeight,\r\n viewColumnNames, viewColumnMetadata,\r\n onPick, styles,\r\n } = input;\r\n\r\n const selectedOptions = lookupValue?.id ? [lookupValue.id] : [];\r\n\r\n return (\r\n <Dropdown\r\n id={id}\r\n className={className}\r\n style={style}\r\n clearable\r\n {...(placeholder !== undefined && { placeholder })}\r\n value={lookupValue?.name ?? ''}\r\n selectedOptions={selectedOptions}\r\n disabled={isInactive}\r\n // Spyglass instead of the default chevron — communicates \"search /\r\n // pick a record\" rather than a generic dropdown. Even though the\r\n // Dropdown variant is pre-fetched (not server-typeahead like\r\n // AutoComplete), the user-mental-model is still \"find a record\".\r\n expandIcon={<Search20Regular />}\r\n // Same positioning rules as the AutoComplete renderer — see the\r\n // comment there. Flip handles no-room-below; the listbox's\r\n // `maxHeight` is the sole height cap (no `autoSize`, no `pinned`)\r\n // to avoid the scrollbar-width oscillation feedback loop.\r\n positioning={{\r\n position: 'below',\r\n align: 'start',\r\n }}\r\n listbox={{\r\n className: styles.listbox,\r\n style: { maxHeight: popupMaxHeight, minHeight: popupMinHeight },\r\n }}\r\n onOptionSelect={(_, data) => {\r\n onPick(data.optionValue ?? null);\r\n }}\r\n >\r\n {renderListboxContents({\r\n loading,\r\n error,\r\n results,\r\n lookupValue,\r\n viewColumnNames,\r\n viewColumnMetadata,\r\n // Dropdown mode has no typed query, so nothing to highlight.\r\n searchText: null,\r\n styles,\r\n })}\r\n </Dropdown>\r\n );\r\n}\r\n\r\nfunction renderRadioGroup(input: {\r\n id: string | undefined;\r\n className: string | undefined;\r\n style: React.CSSProperties | undefined;\r\n lookupValue: LookupColumnValue | undefined;\r\n results: LookupSearchResult[];\r\n loading: boolean;\r\n error: string | null;\r\n displayNullChoice: boolean;\r\n radioButtonOrientation: ChoiceOrientation;\r\n isInactive: boolean;\r\n resolvedRequired: boolean;\r\n onPick: (pickedId: string | null) => void;\r\n t: Localizer['t'];\r\n styles: ReturnType<typeof useLookupStyles>;\r\n}): React.ReactElement {\r\n const {\r\n id, className, style,\r\n lookupValue, results, loading, error,\r\n displayNullChoice, radioButtonOrientation,\r\n isInactive, resolvedRequired, onPick, t, styles,\r\n } = input;\r\n\r\n // RadioGroup `value` is the currently-checked radio's value attr. When\r\n // the column is null + displayNullChoice is on, the null-choice radio\r\n // should appear checked.\r\n const groupValue =\r\n lookupValue?.id ?? (displayNullChoice ? NULL_CHOICE_VALUE : '');\r\n\r\n // Loading / error feedback for radio mode renders inline above the\r\n // group rather than as a fake radio — radios with status text would be\r\n // confusing to keyboard / screen reader users.\r\n return (\r\n <div className={className} style={style}>\r\n {loading && (\r\n <div className={styles.loadingOption}>\r\n <Spinner size=\"extra-tiny\" />\r\n <span className={styles.statusOption}>Searching…</span>\r\n </div>\r\n )}\r\n {error && !loading && (\r\n <div className={styles.statusOption}>{error}</div>\r\n )}\r\n <RadioGroup\r\n id={id}\r\n value={groupValue}\r\n layout={\r\n radioButtonOrientation === ChoiceOrientation.Horizontal ? 'horizontal' : 'vertical'\r\n }\r\n disabled={isInactive}\r\n required={resolvedRequired}\r\n onChange={(_, data) => {\r\n if (data.value === NULL_CHOICE_VALUE) {\r\n onPick(null);\r\n return;\r\n }\r\n onPick(data.value);\r\n }}\r\n >\r\n {displayNullChoice && (\r\n <Radio\r\n key={NULL_CHOICE_VALUE}\r\n value={NULL_CHOICE_VALUE}\r\n label={tNullChoice(t)}\r\n />\r\n )}\r\n {results.map((r) => (\r\n <Radio key={r.id} value={r.id} label={r.name || '(no name)'} />\r\n ))}\r\n </RadioGroup>\r\n </div>\r\n );\r\n}\r\n\r\n/**\r\n * Renders the listbox children for the Combobox and Dropdown variants based\r\n * on the current search state. Status entries (loading / error / empty) are\r\n * rendered as disabled options so they sit inside the popup without\r\n * breaking the listbox's structure.\r\n *\r\n * Also injects a hidden \"committed value\" option when the currently-saved\r\n * lookup isn't in the visible results — without that, Combobox /\r\n * Dropdown's `selectedOptions={[id]}` matching can't find the option, the\r\n * clear (×) button doesn't appear, and the displayed text falls back to\r\n * the option-key fallback.\r\n */\r\nfunction renderListboxContents(input: {\r\n loading: boolean;\r\n error: string | null;\r\n results: LookupSearchResult[];\r\n lookupValue: LookupColumnValue | undefined;\r\n /**\r\n * View column names to render per option, in display order. The first\r\n * column is the \"primary\" line (bold). When the array contains just\r\n * the primary name column (or is empty), the option renders as a\r\n * single line — matching the v0 layout before view-based templates\r\n * landed.\r\n */\r\n viewColumnNames: string[];\r\n /** Column metadata indexed by columnName, for type-aware display fallback. */\r\n viewColumnMetadata: Map<string, Schemas['ColumnMetadataBase']>;\r\n /**\r\n * Current typed search — used to highlight matching substrings in the\r\n * rendered option values. `null` (popup just opened, no query yet)\r\n * means no highlighting.\r\n */\r\n searchText: string | null;\r\n styles: ReturnType<typeof useLookupStyles>;\r\n}) {\r\n const {\r\n loading, error, results, lookupValue,\r\n viewColumnNames, viewColumnMetadata, searchText, styles,\r\n } = input;\r\n\r\n const showCommittedAsFallback =\r\n lookupValue !== undefined && !results.some((r) => r.id === lookupValue.id);\r\n\r\n // Multi-column rendering only fires when the view actually contributes\r\n // additional columns beyond the primary name; for the single-column\r\n // fallback we render a plain highlighted line so the v0 layout doesn't\r\n // regress (extra DOM, extra height) for non-polymorphic / no-view tables.\r\n const isMultiColumn = viewColumnNames.length > 1;\r\n\r\n return (\r\n <>\r\n {loading && (\r\n <Option\r\n key=\"__loading__\"\r\n value=\"__loading__\"\r\n text=\"Searching…\"\r\n disabled\r\n className={styles.statusOption}\r\n >\r\n <div className={styles.loadingOption}>\r\n <Spinner size=\"extra-tiny\" />\r\n <span>Searching…</span>\r\n </div>\r\n </Option>\r\n )}\r\n {error && !loading && (\r\n <Option\r\n key=\"__error__\"\r\n value=\"__error__\"\r\n text={error}\r\n disabled\r\n className={styles.statusOption}\r\n >\r\n {error}\r\n </Option>\r\n )}\r\n {!loading && !error && results.length === 0 && (\r\n // Two empty states: \"Type to search…\" when the popup is open\r\n // but the user hasn't typed anything yet (no search has run);\r\n // \"No matches\" when a search ran and returned zero rows. The\r\n // searchText prop is `null` only in the AutoComplete case where\r\n // the popup just opened over a committed value — we deliberately\r\n // skip the auto-browse search there so the existing entry stays\r\n // visible. See `onOpenChange` in `renderAutoComplete`.\r\n <Option\r\n key=\"__empty__\"\r\n value=\"__empty__\"\r\n text={searchText === null ? 'Type to search…' : 'No matches'}\r\n disabled\r\n className={styles.statusOption}\r\n >\r\n {searchText === null ? 'Type to search…' : 'No matches'}\r\n </Option>\r\n )}\r\n {results.map((r) =>\r\n isMultiColumn ? (\r\n <Option key={r.id} value={r.id} text={r.name}>\r\n <div className={styles.optionStack}>\r\n {viewColumnNames.map((colName, i) => {\r\n const value = resolveColumnDisplayValue(\r\n r.record,\r\n colName,\r\n viewColumnMetadata.get(colName),\r\n );\r\n // Omit null/empty rows entirely. Blazor's version renders\r\n // a `[NULL]` placeholder for these; we treat that as a\r\n // bug rather than a feature — empty columns just clutter\r\n // the option list, especially for sparsely-populated\r\n // tables where most rows have only the primary name\r\n // filled in. The exception is the FIRST (primary) column:\r\n // if its value is somehow missing, drop in `(no name)`\r\n // so the option still has SOMETHING to identify it by\r\n // (an option with no visible content is unselectable in\r\n // any meaningful way).\r\n if (value === null || value === '') {\r\n if (i === 0) {\r\n return (\r\n <span\r\n key={colName}\r\n className={`${styles.optionRowPrimary} ${styles.optionNull}`}\r\n >\r\n (no name)\r\n </span>\r\n );\r\n }\r\n return null;\r\n }\r\n const rowClass = i === 0 ? styles.optionRowPrimary : styles.optionRowSecondary;\r\n return (\r\n <span key={colName} className={rowClass}>\r\n <Highlighter text={value} searchText={searchText} />\r\n </span>\r\n );\r\n })}\r\n </div>\r\n </Option>\r\n ) : (\r\n // Single-column path: keep the v0 layout (one line per option)\r\n // so the popup stays compact when there's no view to expand to.\r\n <Option key={r.id} value={r.id} text={r.name}>\r\n {r.name ? (\r\n <Highlighter text={r.name} searchText={searchText} />\r\n ) : (\r\n <span className={styles.statusOption}>(no name)</span>\r\n )}\r\n </Option>\r\n ),\r\n )}\r\n {showCommittedAsFallback && (\r\n <Option\r\n key={lookupValue!.id}\r\n value={lookupValue!.id}\r\n text={lookupValue!.name ?? ''}\r\n style={{ display: 'none' }}\r\n >\r\n {lookupValue!.name ?? ''}\r\n </Option>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n/**\r\n * Resolves the localized label for a table logical name. Walks the same\r\n * `tables.{name}.label` fallback Blazor uses and degrades to the bare\r\n * logical name when no override is registered — that way polymorphic\r\n * pickers stay readable even before localization JSON loads.\r\n */\r\nfunction tableLabel(t: Localizer['t'], tableName: string): string {\r\n const key = `tables.${tableName}.label`;\r\n const resolved = t(key);\r\n return resolved === key ? tableName : resolved;\r\n}\r\n\r\n/**\r\n * Resolves the \"(none)\" label for the null-choice radio from the framework's\r\n * `app.null-choice-label` key (Blazor uses the same one). No fallback — when\r\n * the key isn't supplied, `t()` returns the raw key so the missing\r\n * localization is visible rather than silently masked.\r\n */\r\nfunction tNullChoice(t: Localizer['t']): string {\r\n return t('app.null-choice-label');\r\n}\r\n\r\n/**\r\n * Resolves the \"Search:\" label preceding the polymorphic target picker.\r\n * Falls back to a short English literal when no localization key is\r\n * registered.\r\n */\r\nfunction tTargetPickerLabel(t: Localizer['t']): string {\r\n const key = 'app.messages.lookup-target-label';\r\n const resolved = t(key);\r\n return resolved === key ? 'Search:' : resolved;\r\n}\r\n\r\nconst XML_ESCAPES: Record<string, string> = {\r\n '<': '&lt;',\r\n '>': '&gt;',\r\n '&': '&amp;',\r\n \"'\": '&apos;',\r\n '\"': '&quot;',\r\n};\r\n\r\nfunction escapeXmlAttribute(value: string): string {\r\n return value.replace(/[<>&'\"]/g, (c) => XML_ESCAPES[c] ?? c);\r\n}\r\n\r\n/**\r\n * Builds a minimal FetchXML covering primaryId + primaryName + any\r\n * additional view columns. Used for the no-view-configured case so the\r\n * server's <c>POST /api/grids/data</c> still has a usable base\r\n * query to layer the search filter / sort / paging on top of. Dotted\r\n * (linked-entity) view-column names are dropped — emitting them would\r\n * need <c>&lt;link-entity&gt;</c> scaffolding the server's\r\n * <c>BuildSyntheticViewDefinitionAsync</c> doesn't expect here.\r\n */\r\nfunction buildMinimalFetchXml(\r\n targetTable: string,\r\n primaryIdColumn: string,\r\n primaryNameColumn: string,\r\n additionalColumns: readonly string[],\r\n): string {\r\n const extras = Array.from(\r\n new Set(\r\n additionalColumns.filter(\r\n (c) => c !== primaryIdColumn && c !== primaryNameColumn && !c.includes('.'),\r\n ),\r\n ),\r\n );\r\n const extraAttrs = extras.map((c) => `<attribute name=\"${escapeXmlAttribute(c)}\" />`).join('');\r\n return (\r\n `<fetch>` +\r\n `<entity name=\"${escapeXmlAttribute(targetTable)}\">` +\r\n `<attribute name=\"${escapeXmlAttribute(primaryIdColumn)}\" />` +\r\n `<attribute name=\"${escapeXmlAttribute(primaryNameColumn)}\" />` +\r\n extraAttrs +\r\n `</entity>` +\r\n `</fetch>`\r\n );\r\n}\r\n","/**\r\n * Pure helpers for {@link ManyToManyLookupEdit}. Extracted so the FetchXML\r\n * construction, request-shaping, and display-name resolution can be unit\r\n * tested without spinning up the full component graph (which would need\r\n * jsdom + Fluent theme bootstrap).\r\n */\r\n\r\nimport {\r\n RelationshipFilterMode,\r\n getRecordReferenceId,\r\n type AssociateRequest,\r\n type DisassociateRequest,\r\n type OrganizationRequest,\r\n type RelationshipFilter,\r\n type TableRecord,\r\n type TableRecordReference,\r\n} from '@powerportalspro/core';\r\n\r\n/**\r\n * Internal shape — a related record with its id + primary-name display.\r\n * Identity is the record id; equality across queries is `===` on `id`.\r\n */\r\nexport interface RelatedRecord {\r\n id: string;\r\n displayName: string;\r\n /**\r\n * Full record payload — used to feed pending-associate requests through\r\n * to the save batch. Only present for records that came from a query\r\n * (i.e. exist server-side); the field is identical to the wire\r\n * `TableRecord` shape.\r\n */\r\n source: TableRecord;\r\n}\r\n\r\nexport function buildFilter(\r\n parentRecord: TableRecord,\r\n relationshipName: string,\r\n mode: RelationshipFilterMode,\r\n): RelationshipFilter {\r\n // Use the helper so create-mode parents (no real `id` yet, but a\r\n // `_idForCreate` temp GUID) still produce a valid filter — the initial\r\n // include-load just returns zero rows, which is the right behavior.\r\n return {\r\n $type: 'RelationshipFilter',\r\n parentRecord: {\r\n tableName: parentRecord.tableName,\r\n id: getRecordReferenceId(parentRecord)!,\r\n },\r\n relationshipName,\r\n mode,\r\n };\r\n}\r\n\r\n/**\r\n * Builds the {@link OrganizationRequest} batch that represents the\r\n * editor's pending changes. Returns an empty array when nothing is dirty.\r\n *\r\n * `pendingAdds` becomes one AssociateRequest; `pendingRemoveIds` (resolved\r\n * against `initialSelection` for the full TableRecordReference shape)\r\n * becomes one DisassociateRequest. The combined batch is what the parent\r\n * MainContext ships in its executeMultiple round-trip on save.\r\n */\r\nexport function buildRequests(\r\n parentRecord: TableRecord,\r\n relationshipName: string,\r\n relatedTableName: string | undefined,\r\n pendingAdds: readonly RelatedRecord[],\r\n pendingRemoveIds: readonly string[],\r\n initialSelection: readonly RelatedRecord[],\r\n): OrganizationRequest[] {\r\n if (!relatedTableName) return [];\r\n if (pendingAdds.length === 0 && pendingRemoveIds.length === 0) return [];\r\n\r\n const requests: OrganizationRequest[] = [];\r\n // For not-yet-saved parents, this returns the `_idForCreate` temp GUID.\r\n // The server's `ResolveAssociateRecordAsync` matches it against the\r\n // in-batch CreateRequest's `_idForCreate`, so Create + Associate share\r\n // a single `ExecuteMultiple` round-trip without needing the parent's\r\n // real id ahead of time.\r\n const parentRef: TableRecordReference = {\r\n tableName: parentRecord.tableName,\r\n id: getRecordReferenceId(parentRecord)!,\r\n };\r\n\r\n if (pendingAdds.length > 0) {\r\n const associate: AssociateRequest = {\r\n $type: 'AssociateRequest',\r\n record: parentRef,\r\n relationshipName,\r\n relatedRecords: pendingAdds.map<TableRecordReference>((r) => ({\r\n tableName: r.source.tableName,\r\n id: r.id,\r\n })),\r\n };\r\n requests.push(associate);\r\n }\r\n\r\n if (pendingRemoveIds.length > 0) {\r\n const removedRecords = initialSelection\r\n .filter((r) => pendingRemoveIds.includes(r.id))\r\n .map<TableRecordReference>((r) => ({ tableName: r.source.tableName, id: r.id }));\r\n if (removedRecords.length > 0) {\r\n const disassociate: DisassociateRequest = {\r\n $type: 'DisassociateRequest',\r\n record: parentRef,\r\n relationshipName,\r\n relatedRecords: removedRecords,\r\n };\r\n requests.push(disassociate);\r\n }\r\n }\r\n\r\n return requests;\r\n}\r\n\r\nexport function resolveDisplayName(record: TableRecord, primaryNameColumn: string): string | undefined {\r\n const formatted = record.formattedValues?.[primaryNameColumn];\r\n if (formatted && formatted.length > 0) return formatted;\r\n const raw = record.properties?.[primaryNameColumn];\r\n if (raw && typeof raw === 'object' && 'value' in raw) {\r\n const v = (raw as { value?: unknown }).value;\r\n if (typeof v === 'string' && v.length > 0) return v;\r\n }\r\n return undefined;\r\n}\r\n\r\n","/**\r\n * Coordinates the many-to-many associations of a parent record: shows the\r\n * currently-linked records as removable tags / chips, lets the user search\r\n * for and add more, and registers with the surrounding `MainContext` so the\r\n * next save batch includes the resulting `AssociateRequest` /\r\n * `DisassociateRequest` pair.\r\n *\r\n * Two editor flavors share the underlying state, picked via `editorType`:\r\n *\r\n * - `TagPicker` (default) — Fluent v9 `TagPicker`. Selected records render\r\n * as removable tag chips above the input. Best for selections of more\r\n * than 2-3 records.\r\n * - `Combobox` — Fluent v9 `Combobox` in `multiselect` mode. Selected\r\n * records render comma-separated inside the input; each suggestion has a\r\n * checkbox. Better when consistency with the single-select `LookupEdit`\r\n * matters.\r\n *\r\n * Both flavors share:\r\n * - A view picker that appears in the dropdown header when more than one\r\n * view resolves for the related table.\r\n * - Multi-column highlighted option rows driven by the active view's\r\n * column metadata (returned in `GridDataResponse.columns`).\r\n * - An Advanced Search button — a footer entry in the suggestion\r\n * dropdown that opens a `<LookupDialog>` modal hosting a multi-select\r\n * grid. Selecting records in the dialog adds them to the pending-\r\n * associate set the same way the autocomplete does.\r\n *\r\n * Server-side: both the initial selection load and the autocomplete go\r\n * through `POST /api/grids/data` (a `viewId` source plus a typed\r\n * `RelationshipFilter` with `Mode = Include` or `Exclude`). The composer\r\n * resolves the relationship from the parent's metadata, handles the\r\n * multi-column search, and emits the intersect-table join.\r\n */\r\n\r\nimport {\r\n Button,\r\n Combobox,\r\n Dropdown,\r\n Field,\r\n makeStyles,\r\n mergeClasses,\r\n Option,\r\n Spinner,\r\n Tag,\r\n TagPicker,\r\n TagPickerControl,\r\n TagPickerGroup,\r\n TagPickerInput,\r\n TagPickerList,\r\n TagPickerOption,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { Search20Regular } from '@fluentui/react-icons';\r\nimport {\r\n RelationshipFilterMode,\r\n getRecordReferenceId,\r\n type GridDataRequest,\r\n type ResolvedColumn,\r\n type TableRecord,\r\n type ViewMetadata,\r\n} from '@powerportalspro/core';\r\nimport {\r\n QueryStatus,\r\n useGridData,\r\n useMainContext,\r\n usePowerPortalsPro,\r\n useRecordContext,\r\n useT,\r\n useTableMetadata,\r\n useViewsForTable,\r\n} from '@powerportalspro/react';\r\nimport { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';\r\nimport { Highlighter } from '../components/highlighter.js';\r\nimport { LookupDialog } from '../form/lookup-dialog.js';\r\nimport { resolveColumnDisplayValue } from './base/column-display.js';\r\nimport { DescriptionLocation } from './base/column-edit-types.js';\r\nimport { DescriptionIcon } from './base/description-icon.js';\r\nimport {\r\n buildFilter,\r\n buildRequests,\r\n resolveDisplayName,\r\n type RelatedRecord,\r\n} from './many-to-many-lookup-edit.helpers.js';\r\n\r\n/**\r\n * Selectable render mode. Mirrors Blazor's `EditorType` pattern on other\r\n * editors (`BoolEditorType`, `LookupEditorType`).\r\n */\r\nexport const ManyToManyLookupEditorType = {\r\n /** Fluent `TagPicker` — selected records render as removable chips above the input. */\r\n TagPicker: 'tagPicker',\r\n /** Fluent `Combobox` in `multiselect` mode — selected records render comma-separated. */\r\n Combobox: 'combobox',\r\n} as const;\r\nexport type ManyToManyLookupEditorType =\r\n (typeof ManyToManyLookupEditorType)[keyof typeof ManyToManyLookupEditorType];\r\n\r\n/** Dataverse savedquery.querytype for `Public` views (matches `ViewType.Public = 0`). */\r\nconst VIEW_TYPE_PUBLIC = 0;\r\n\r\n/** Default cap on records returned per autocomplete / advanced-search query. */\r\nconst DEFAULT_MAX_RECORDS_RETURNED = 20;\r\n\r\n/** Debounce window between the last keystroke and the autocomplete fetch. */\r\nconst SEARCH_DEBOUNCE_MS = 300;\r\n\r\nexport interface ManyToManyLookupEditProps {\r\n /**\r\n * Schema name of the many-to-many relationship to edit (e.g.\r\n * `\"account_contacts\"`). Required. Must appear in the parent record's\r\n * `TableMetadata.ManyToMany`.\r\n */\r\n relationshipName: string;\r\n\r\n /**\r\n * Which control to render. Defaults to {@link ManyToManyLookupEditorType.TagPicker}.\r\n * Both flavors share the same state, view picker, and Advanced Search footer.\r\n */\r\n editorType?: ManyToManyLookupEditorType;\r\n\r\n /**\r\n * Display label rendered above the control. Optional — typical consumer\r\n * pattern is to set this to the related table's collection label.\r\n */\r\n label?: string;\r\n\r\n /**\r\n * Optional hint text. Rendered next to or below the label based on\r\n * {@link descriptionLocation}.\r\n */\r\n description?: string;\r\n\r\n /**\r\n * Where the description appears. Defaults to {@link DescriptionLocation.Tooltip}.\r\n */\r\n descriptionLocation?: DescriptionLocation;\r\n\r\n /**\r\n * View id pre-selected in the picker on mount. Falls back to the related\r\n * table's default Public view when omitted. Must appear in {@link viewIds}\r\n * when that list is supplied; otherwise ignored.\r\n */\r\n defaultViewId?: string;\r\n\r\n /**\r\n * Allow-list of Dataverse view ids the picker can offer. When supplied\r\n * with multiple ids, a header dropdown lets the user switch between\r\n * them. When omitted, every Public view for the related table is\r\n * eligible (and the table's default view is auto-selected). Also\r\n * forwarded to the Advanced Search dialog so the dialog and the inline\r\n * picker stay in sync on what views are reachable.\r\n */\r\n viewIds?: readonly string[];\r\n\r\n /**\r\n * Maximum number of records returned per autocomplete fetch. Defaults\r\n * to 20. Larger values are accepted but make the dropdown sluggish.\r\n */\r\n maxRecordsReturned?: number;\r\n\r\n /**\r\n * Maximum height of the suggestion dropdown. Beyond this height the\r\n * listbox scrolls internally instead of growing past the viewport.\r\n * Accepts any CSS length (`'400px'`, `'50vh'`, etc.). Default `'320px'`.\r\n */\r\n popupMaxHeight?: string;\r\n\r\n /**\r\n * Minimum height of the suggestion dropdown. Useful when there are\r\n * few results and you want the popup to keep a reasonable visual\r\n * weight (sticky view-picker header + footer Advanced Search button\r\n * have room to breathe). **Defaults to unset** — applying a min-height\r\n * fights Floating UI's `autoSize: 'height-always'` clamp when the\r\n * dropdown opens near the bottom of the viewport: autoSize wants to\r\n * shrink to the available space, the min-height resists, and the\r\n * scrollbar oscillates as the layout solver bounces between the two.\r\n * Leaving it unset lets autoSize own the height fully.\r\n */\r\n popupMinHeight?: string;\r\n\r\n /**\r\n * Minimum number of related records that must be selected for the\r\n * editor to validate. When unset, no lower bound is enforced.\r\n */\r\n minimumNumberOfItems?: number;\r\n\r\n /**\r\n * Maximum number of related records the user may select. When unset,\r\n * no upper bound is enforced.\r\n */\r\n maximumNumberOfItems?: number;\r\n\r\n /**\r\n * Visually marks the field as required. When {@link minimumNumberOfItems}\r\n * is at least 1, this defaults to `true`. Set to `false` to suppress the\r\n * required indicator even with a non-zero min.\r\n */\r\n required?: boolean;\r\n\r\n /**\r\n * Disables every interaction — picker is inert, Advanced Search button\r\n * hidden. Useful when the parent record's permissions don't allow\r\n * association edits.\r\n */\r\n disabled?: boolean;\r\n\r\n /**\r\n * Read-only flavor. Same UX as {@link disabled} — picker inert,\r\n * Advanced Search hidden — but reserves the design-system \"this field\r\n * isn't currently editable\" semantic separately from \"this field is\r\n * permanently disabled\". Combined with `disabled` at use time so either\r\n * flag suppresses interaction.\r\n */\r\n readOnly?: boolean;\r\n\r\n /**\r\n * When false, the inline label is suppressed even if {@link label} is\r\n * supplied. Useful inside compact list views or when the surrounding\r\n * Section header already carries the field name. Defaults to true.\r\n */\r\n displayLabelWhenAvailable?: boolean;\r\n\r\n /**\r\n * When false, the per-field validation error message is hidden but the\r\n * field still tracks the error internally (so {@link MainContext.saveAll}\r\n * still gates on validity, and a `<ValidationSummary>` rendered\r\n * elsewhere can surface the consolidated errors). Useful when a\r\n * downstream summary is the only error surface. Defaults to true.\r\n */\r\n displayValidationErrorMessage?: boolean;\r\n\r\n /** When false, renders nothing. Defaults to true. */\r\n isVisible?: boolean;\r\n\r\n /** Optional class on the outer wrapper. */\r\n className?: string;\r\n\r\n /** Optional inline style on the outer wrapper. */\r\n style?: CSSProperties;\r\n\r\n /** Optional id forwarded to the inner control. */\r\n id?: string;\r\n}\r\n\r\nconst useStyles = makeStyles({\r\n wrapper: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalXS,\r\n width: '100%',\r\n },\r\n // Dropdown list — caps the internal height + enables scrolling so the\r\n // popup can't grow off the bottom of the viewport when there are many\r\n // suggestions. `paddingTop: 0` / `paddingBottom: 0` remove Fluent's\r\n // default vertical insets so the sticky view-picker header and the\r\n // sticky Advanced Search footer sit flush against the list edges\r\n // (otherwise scrolling rows are briefly visible inside the padding\r\n // strip before being clipped behind the sticky element). The actual\r\n // min-height / max-height values come from the component's\r\n // `popupMinHeight` / `popupMaxHeight` props, applied inline on the slot.\r\n dropdownList: {\r\n overflowY: 'auto',\r\n paddingTop: '0',\r\n paddingBottom: '0',\r\n },\r\n labelRow: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalXS,\r\n },\r\n control: {\r\n width: '100%',\r\n },\r\n // Sticky-header view picker. Mirrors Blazor's `HeaderContent` slot on the\r\n // autocomplete — only shown when there are multiple views to choose from.\r\n // `position: sticky` keeps it pinned while the user scrolls suggestions.\r\n // The background fill avoids see-through against scrolling rows.\r\n viewPickerHeader: {\r\n position: 'sticky',\r\n top: 0,\r\n zIndex: 1,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n padding: tokens.spacingHorizontalS,\r\n },\r\n // Sticky-footer Advanced Search button inside the picker dropdown.\r\n // Mirrors the LookupEdit footer + the Blazor `FooterContent` in\r\n // ManyToManyLookupEdit.razor.\r\n advancedSearchFooter: {\r\n position: 'sticky',\r\n bottom: 0,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n borderTop: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n display: 'flex',\r\n justifyContent: 'flex-end',\r\n padding: tokens.spacingHorizontalS,\r\n },\r\n inlineSpinner: {\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n },\r\n emptyOption: {\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n color: tokens.colorNeutralForeground3,\r\n fontStyle: 'italic',\r\n },\r\n // Multi-column option row — one vertical stack per row. The Fluent\r\n // option chrome (focus ring, hover state) wraps this so the rows still\r\n // behave like single options.\r\n optionStack: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: '2px',\r\n width: '100%',\r\n },\r\n optionFirstLine: {\r\n fontWeight: tokens.fontWeightSemibold,\r\n },\r\n optionSubLine: {\r\n fontSize: tokens.fontSizeBase200,\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n // Empty-value placeholder rendered when a column has no value for this\r\n // row. Muted styling so it doesn't compete with real values.\r\n nullValue: {\r\n color: tokens.colorNeutralForeground3,\r\n fontStyle: 'italic',\r\n },\r\n});\r\n\r\nexport function ManyToManyLookupEdit({\r\n relationshipName,\r\n editorType = ManyToManyLookupEditorType.TagPicker,\r\n label,\r\n description,\r\n descriptionLocation = DescriptionLocation.Tooltip,\r\n defaultViewId,\r\n viewIds,\r\n maxRecordsReturned = DEFAULT_MAX_RECORDS_RETURNED,\r\n popupMaxHeight = '320px',\r\n popupMinHeight,\r\n minimumNumberOfItems,\r\n maximumNumberOfItems,\r\n required,\r\n disabled = false,\r\n readOnly = false,\r\n displayLabelWhenAvailable = true,\r\n displayValidationErrorMessage = true,\r\n isVisible = true,\r\n className,\r\n style,\r\n id,\r\n}: ManyToManyLookupEditProps) {\r\n const styles = useStyles();\r\n const t = useT();\r\n const { ppp } = usePowerPortalsPro();\r\n const { record: parentRecord } = useRecordContext();\r\n const mainContext = useMainContext();\r\n\r\n // ---------- Relationship + related-table metadata ----------\r\n\r\n const parentMetadataQuery = useTableMetadata(parentRecord?.tableName ?? '', {\r\n enabled: !!parentRecord?.tableName,\r\n });\r\n const parentMetadata = parentMetadataQuery.data;\r\n\r\n // Find the M:N relationship on the parent's metadata and derive which\r\n // logical name we should be querying. Memoized so we don't redo the\r\n // .find on every render.\r\n const relatedTableName = useMemo(() => {\r\n if (!parentMetadata) return undefined;\r\n const mtm = parentMetadata.manyToMany?.find((x) => x.relationshipName === relationshipName);\r\n if (!mtm) return undefined;\r\n return parentMetadata.tableName === mtm.table1 ? mtm.table2 : mtm.table1;\r\n }, [parentMetadata, relationshipName]);\r\n\r\n const relatedMetadataQuery = useTableMetadata(relatedTableName ?? '', {\r\n enabled: !!relatedTableName,\r\n });\r\n const relatedPrimaryName = relatedMetadataQuery.data?.primaryNameColumn;\r\n\r\n // ---------- View resolution ----------\r\n\r\n const viewsForTable = useViewsForTable(relatedTableName ?? '', {\r\n enabled: !!relatedTableName,\r\n });\r\n\r\n // Build the resolved view list: filter all-public-views by the optional\r\n // `viewIds` allow-list. When no allow-list, use every Public view. Sort\r\n // by name for a stable picker order.\r\n const resolvedViews = useMemo<readonly ViewMetadata[]>(() => {\r\n if (!viewsForTable.data) return [];\r\n const allowList = viewIds && viewIds.length > 0 ? new Set(viewIds) : null;\r\n return viewsForTable.data\r\n .filter((v) => v.type === VIEW_TYPE_PUBLIC && v.fetchXml && v.fetchXml.length > 0)\r\n .filter((v) => (allowList ? allowList.has(v.id) : true))\r\n .slice() // sort needs a mutable array\r\n .sort((a, b) => a.name.localeCompare(b.name));\r\n }, [viewsForTable.data, viewIds]);\r\n\r\n // selectedViewId starts undefined; pinned to defaultViewId / first\r\n // resolved view once those are available. The user can change it via\r\n // the picker dropdown, after which the consumer-driven defaultViewId\r\n // shouldn't keep overriding their selection — so the seed effect runs\r\n // exactly once per resolved-views set.\r\n const [selectedViewId, setSelectedViewId] = useState<string | undefined>(undefined);\r\n const lastSeededViewListRef = useRef<readonly ViewMetadata[] | undefined>(undefined);\r\n useEffect(() => {\r\n if (resolvedViews.length === 0) return;\r\n if (lastSeededViewListRef.current === resolvedViews) return;\r\n lastSeededViewListRef.current = resolvedViews;\r\n\r\n // Preference order:\r\n // 1. consumer's defaultViewId, when it appears in the resolved set\r\n // 2. table's default view (isDefault), when in the resolved set\r\n // 3. first resolved view\r\n const defaultMatch = defaultViewId\r\n ? resolvedViews.find((v) => v.id === defaultViewId)\r\n : undefined;\r\n const tableDefault = resolvedViews.find((v) => v.isDefault);\r\n const chosen = defaultMatch ?? tableDefault ?? resolvedViews[0];\r\n if (chosen) setSelectedViewId(chosen.id);\r\n }, [resolvedViews, defaultViewId]);\r\n\r\n // ---------- Selection state ----------\r\n\r\n // Records currently associated server-side. Populated from the initial-\r\n // load query on mount. The component's \"saved baseline\" — pending adds /\r\n // removes are diffed against this.\r\n const [initialSelection, setInitialSelection] = useState<readonly RelatedRecord[]>([]);\r\n // Records the user has added since mount. Will turn into an\r\n // `AssociateRequest` at save time.\r\n const [pendingAdds, setPendingAdds] = useState<readonly RelatedRecord[]>([]);\r\n // Ids the user has removed from `initialSelection` since mount. Will\r\n // turn into a `DisassociateRequest` at save time.\r\n const [pendingRemoveIds, setPendingRemoveIds] = useState<readonly string[]>([]);\r\n\r\n // The full current selection — what the picker shows. Derived from the\r\n // initial baseline minus removes plus adds. Stable identity per render\r\n // is fine here because the picker just reads it.\r\n const currentSelection = useMemo(() => {\r\n const removed = new Set(pendingRemoveIds);\r\n const kept = initialSelection.filter((r) => !removed.has(r.id));\r\n return [...kept, ...pendingAdds];\r\n }, [initialSelection, pendingAdds, pendingRemoveIds]);\r\n\r\n const selectedIds = useMemo(\r\n () => new Set(currentSelection.map((r) => r.id)),\r\n [currentSelection],\r\n );\r\n\r\n // ---------- Initial-load via useGridData with Include filter ----------\r\n\r\n // Skip the query until we know which view to query and what the\r\n // related table's primary-name column is (needed for chip display).\r\n //\r\n // New parent records work too: `<RecordContext>` pre-generates a temp\r\n // GUID into `_idForCreate` on create-mode seeding, and\r\n // `getRecordReferenceId` returns it when `id` is unset, so the\r\n // initial-load query produces a valid `RelationshipFilter`. The query\r\n // returns empty (no associations exist yet server-side for this new\r\n // id), pending adds queue normally, and on save the M:N edit emits an\r\n // `AssociateRequest` against the same temp id. The server's\r\n // `ResolveAssociateRecordAsync` matches it back to the in-batch\r\n // CreateRequest's `_idForCreate`, so a Create + Associate pair within\r\n // one `ExecuteMultiple` resolves to the same record.\r\n const parentRefId = getRecordReferenceId(parentRecord);\r\n const canQuery =\r\n !!parentRecord &&\r\n !!parentRefId &&\r\n !!relatedTableName &&\r\n !!relatedPrimaryName &&\r\n !!selectedViewId;\r\n\r\n const initialLoadRequest = useMemo<GridDataRequest>(() => {\r\n if (!canQuery || !parentRecord) {\r\n return { fetchXml: '<fetch><entity name=\"placeholder\"/></fetch>' };\r\n }\r\n return {\r\n viewId: selectedViewId!,\r\n filters: [\r\n buildFilter(\r\n parentRecord,\r\n relationshipName,\r\n RelationshipFilterMode.IncludeExistingRecords,\r\n ),\r\n ],\r\n pageSize: 500, // initial-selection load — return up to 500 records associated to this parent.\r\n };\r\n }, [canQuery, parentRecord, selectedViewId, relationshipName]);\r\n\r\n const initialLoad = useGridData(initialLoadRequest, { enabled: canQuery });\r\n\r\n // Resolved column descriptors for option rendering. The two responses\r\n // (initial load + search) both come from the same viewId, so columns\r\n // are identical between them — use whichever is freshest.\r\n const resolvedColumns: readonly ResolvedColumn[] = useMemo(() => {\r\n return initialLoad.data?.columns ?? [];\r\n }, [initialLoad.data?.columns]);\r\n\r\n // Pick the primary-name column from the resolved set so chip / display-\r\n // name rendering uses the same column the server flagged.\r\n const primaryNameColumn = useMemo(() => {\r\n const flagged = resolvedColumns.find((c) => c.isPrimaryName);\r\n if (flagged) return flagged.columnName;\r\n return relatedPrimaryName;\r\n }, [resolvedColumns, relatedPrimaryName]);\r\n\r\n // Seed `initialSelection` once the load completes successfully. Re-seed\r\n // when the parent record swaps (id change) or the user switches views.\r\n const lastSeededRecordIdRef = useRef<string | undefined>(undefined);\r\n const lastSeededViewIdRef = useRef<string | undefined>(undefined);\r\n useEffect(() => {\r\n if (initialLoad.status !== QueryStatus.Success || !initialLoad.data) return;\r\n if (!primaryNameColumn || !parentRecord) return;\r\n if (\r\n lastSeededRecordIdRef.current === parentRefId &&\r\n lastSeededViewIdRef.current === selectedViewId\r\n ) {\r\n return;\r\n }\r\n lastSeededRecordIdRef.current = parentRefId;\r\n lastSeededViewIdRef.current = selectedViewId;\r\n\r\n const seeded: RelatedRecord[] = initialLoad.data.tableRecords.map((r) => ({\r\n id: r.id!,\r\n displayName: resolveDisplayName(r, primaryNameColumn) ?? '',\r\n source: r,\r\n }));\r\n setInitialSelection(seeded);\r\n setPendingAdds([]);\r\n setPendingRemoveIds([]);\r\n }, [initialLoad.status, initialLoad.data, parentRecord, primaryNameColumn, selectedViewId]);\r\n\r\n // ---------- Autocomplete state ----------\r\n\r\n const [query, setQuery] = useState('');\r\n const [debouncedQuery, setDebouncedQuery] = useState('');\r\n const [searchResults, setSearchResults] = useState<readonly RelatedRecord[]>([]);\r\n const [searchLoading, setSearchLoading] = useState(false);\r\n\r\n // Debounce the user's typing — only the trailing value drives a fetch.\r\n useEffect(() => {\r\n const timer = setTimeout(() => setDebouncedQuery(query), SEARCH_DEBOUNCE_MS);\r\n return () => clearTimeout(timer);\r\n }, [query]);\r\n\r\n // Autocomplete fetch: view-based GridDataRequest with the user's\r\n // search text + Exclude filter so the server hides records already\r\n // associated to this parent. The composer applies the multi-column\r\n // search across the view's searchable columns. Aborts on every dep\r\n // change so an in-flight request from the previous keystroke can't\r\n // clobber the latest results.\r\n useEffect(() => {\r\n if (!canQuery || !parentRecord || !selectedViewId) return;\r\n const controller = new AbortController();\r\n setSearchLoading(true);\r\n\r\n const trimmed = debouncedQuery.trim();\r\n const request: GridDataRequest = {\r\n viewId: selectedViewId,\r\n ...(trimmed.length > 0 && { searchText: trimmed }),\r\n filters: [\r\n buildFilter(\r\n parentRecord,\r\n relationshipName,\r\n RelationshipFilterMode.ExcludeExistingRecords,\r\n ),\r\n ],\r\n pageSize: maxRecordsReturned,\r\n };\r\n\r\n ppp\r\n .loadGridAsync(request, controller.signal)\r\n .then((resp) => {\r\n if (controller.signal.aborted) return;\r\n const nameColumn =\r\n resp.columns.find((c) => c.isPrimaryName)?.columnName ?? primaryNameColumn ?? '';\r\n setSearchResults(\r\n resp.tableRecords.map((r) => ({\r\n id: r.id!,\r\n displayName: resolveDisplayName(r, nameColumn) ?? '',\r\n source: r,\r\n })),\r\n );\r\n })\r\n .catch((err) => {\r\n if (controller.signal.aborted) return;\r\n // Network / server failure shouldn't surface as an unhandled promise\r\n // — log to console and leave the previous results in place.\r\n // eslint-disable-next-line no-console\r\n console.warn('[ManyToManyLookupEdit] autocomplete fetch failed', err);\r\n })\r\n .finally(() => {\r\n if (!controller.signal.aborted) setSearchLoading(false);\r\n });\r\n\r\n return () => controller.abort();\r\n }, [\r\n canQuery,\r\n parentRecord,\r\n selectedViewId,\r\n relationshipName,\r\n debouncedQuery,\r\n maxRecordsReturned,\r\n primaryNameColumn,\r\n ppp,\r\n ]);\r\n\r\n // ---------- Selection mutation ----------\r\n\r\n const addRecord = useCallback(\r\n (rec: RelatedRecord) => {\r\n if (selectedIds.has(rec.id)) return;\r\n // Was it in the original set? Then this is \"undo a remove\" rather\r\n // than a fresh add.\r\n const wasInitial = initialSelection.some((r) => r.id === rec.id);\r\n if (wasInitial) {\r\n setPendingRemoveIds((prev) => prev.filter((id) => id !== rec.id));\r\n } else {\r\n setPendingAdds((prev) => [...prev, rec]);\r\n }\r\n },\r\n [initialSelection, selectedIds],\r\n );\r\n\r\n const removeRecord = useCallback(\r\n (recordId: string) => {\r\n // Was it a fresh add? Drop it from pendingAdds entirely.\r\n const isPendingAdd = pendingAdds.some((r) => r.id === recordId);\r\n if (isPendingAdd) {\r\n setPendingAdds((prev) => prev.filter((r) => r.id !== recordId));\r\n return;\r\n }\r\n // Otherwise it came from the original set — mark for disassociate.\r\n setPendingRemoveIds((prev) => (prev.includes(recordId) ? prev : [...prev, recordId]));\r\n },\r\n [pendingAdds],\r\n );\r\n\r\n // ---------- Validation ----------\r\n\r\n const selectionCount = currentSelection.length;\r\n const minError =\r\n minimumNumberOfItems !== undefined && selectionCount < minimumNumberOfItems\r\n ? t('app.errors.value-min-count', [minimumNumberOfItems])\r\n : null;\r\n const maxError =\r\n maximumNumberOfItems !== undefined && selectionCount > maximumNumberOfItems\r\n ? t('app.errors.value-max-count', [maximumNumberOfItems])\r\n : null;\r\n const validationError = minError ?? maxError;\r\n\r\n // ---------- MainContext registration ----------\r\n\r\n const isDirty = pendingAdds.length > 0 || pendingRemoveIds.length > 0;\r\n\r\n // Keep refs to mutable bits the descendant callbacks close over — so the\r\n // callbacks themselves can be stable identity (no re-registration on\r\n // every selection change). Mirrors the pattern other context-bound\r\n // editors use.\r\n const pendingAddsRef = useRef(pendingAdds);\r\n pendingAddsRef.current = pendingAdds;\r\n const pendingRemoveIdsRef = useRef(pendingRemoveIds);\r\n pendingRemoveIdsRef.current = pendingRemoveIds;\r\n const initialSelectionRef = useRef(initialSelection);\r\n initialSelectionRef.current = initialSelection;\r\n const validationRef = useRef(validationError);\r\n validationRef.current = validationError;\r\n const isDirtyRef = useRef(isDirty);\r\n isDirtyRef.current = isDirty;\r\n\r\n useEffect(() => {\r\n // Key on `registerDescendant` identity (stable per MainContext\r\n // instance) rather than the whole `mainContext` value object — see\r\n // record-context.tsx for the infinite-loop rationale.\r\n if (!mainContext || !parentRecord) return;\r\n return mainContext.registerDescendant({\r\n // Ref-backed getter so dirty transitions don't tear the\r\n // registration down + re-create it (which would cascade context\r\n // re-renders and disrupt focus in any open inline editors). The\r\n // notify effect below pokes the parent on actual transitions.\r\n isDirty: () => isDirtyRef.current,\r\n getRequests: () => buildRequests(\r\n parentRecord,\r\n relationshipName,\r\n relatedTableName,\r\n pendingAddsRef.current,\r\n pendingRemoveIdsRef.current,\r\n initialSelectionRef.current,\r\n ),\r\n validate: () => validationRef.current === null,\r\n refresh: async () => {\r\n // Drop pending edits and reset the seed-guards so the initial\r\n // load re-seeds on the next successful response.\r\n setPendingAdds([]);\r\n setPendingRemoveIds([]);\r\n lastSeededRecordIdRef.current = undefined;\r\n lastSeededViewIdRef.current = undefined;\r\n },\r\n // Cancel/Discard: just drop the pending adds/removes; the\r\n // seed-guard refs stay so the initial selection (already loaded\r\n // from the server) is what the editor renders after reset.\r\n resetState: () => {\r\n setPendingAdds([]);\r\n setPendingRemoveIds([]);\r\n },\r\n });\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [mainContext?.registerDescendant, parentRecord, relationshipName, relatedTableName]);\r\n\r\n // Notify the parent MainContext when our dirty aggregate flips. Read\r\n // mainContext via a ref so the effect doesn't re-fire when the\r\n // parent's value identity churns (it does every time its own isDirty\r\n // flips, which would re-trigger this notify and infinite-loop the\r\n // two layers).\r\n const mainContextRef = useRef(mainContext);\r\n mainContextRef.current = mainContext;\r\n useEffect(() => {\r\n mainContextRef.current?.notifyDescendantDirtyChanged();\r\n }, [isDirty]);\r\n\r\n // ---------- Advanced Search dialog state ----------\r\n\r\n const [dialogHidden, setDialogHidden] = useState(true);\r\n const openAdvancedSearch = () => setDialogHidden(false);\r\n\r\n const onDialogActionClicked = useCallback(\r\n (records: readonly TableRecord[]) => {\r\n if (!primaryNameColumn) return;\r\n for (const r of records) {\r\n if (!r.id) continue;\r\n addRecord({\r\n id: r.id,\r\n displayName: resolveDisplayName(r, primaryNameColumn) ?? '',\r\n source: r,\r\n });\r\n }\r\n },\r\n [addRecord, primaryNameColumn],\r\n );\r\n\r\n // Per-table allow-list for the Advanced Search dialog. The related\r\n // table is always the single target, so the map has just that one entry.\r\n const advancedSearchAllowList = useMemo<Record<string, readonly string[]> | undefined>(() => {\r\n if (!relatedTableName) return undefined;\r\n if (!viewIds || viewIds.length === 0) return undefined;\r\n return { [relatedTableName]: viewIds };\r\n }, [relatedTableName, viewIds]);\r\n\r\n // ---------- Rendering ----------\r\n\r\n if (!isVisible) return null;\r\n if (!parentRecord) return null;\r\n\r\n // Either flag suppresses interaction — same UX in this control because\r\n // M:N has no \"read text but selectable\" middle state (the picker is the\r\n // only way to interact with the value).\r\n const interactionDisabled = disabled || readOnly;\r\n const resolvedRequired = required ?? (minimumNumberOfItems !== undefined && minimumNumberOfItems > 0);\r\n // Suggestions exclude anything already selected — server already filters,\r\n // but a freshly-added record won't appear in the next-page server response\r\n // either way, so the client list IS the deduped view.\r\n const suggestions = searchResults.filter((r) => !selectedIds.has(r.id));\r\n\r\n const renderInput: RenderInput = {\r\n id,\r\n disabled: interactionDisabled,\r\n required: resolvedRequired,\r\n currentSelection,\r\n suggestions,\r\n searchLoading,\r\n query,\r\n onQueryChange: setQuery,\r\n onPick: addRecord,\r\n onUnpick: removeRecord,\r\n onAdvancedSearchClicked: openAdvancedSearch,\r\n resolvedColumns,\r\n resolvedViews,\r\n selectedViewId,\r\n onViewChange: setSelectedViewId,\r\n popupMaxHeight,\r\n popupMinHeight,\r\n styles,\r\n t,\r\n };\r\n\r\n const editorTypeRendered =\r\n editorType === ManyToManyLookupEditorType.Combobox\r\n ? renderCombobox(renderInput)\r\n : renderTagPicker(renderInput);\r\n\r\n return (\r\n <div className={mergeClasses('ppp-many-to-many-lookup-edit', className)} style={style}>\r\n <Field\r\n {...(displayLabelWhenAvailable && label !== undefined && { label })}\r\n {...(descriptionLocation === DescriptionLocation.BelowControl && description !== undefined && {\r\n hint: description,\r\n })}\r\n {...(resolvedRequired && { required: true })}\r\n {...(validationError && {\r\n validationState: 'error' as const,\r\n ...(displayValidationErrorMessage && { validationMessage: validationError }),\r\n })}\r\n >\r\n <div className={styles.wrapper}>\r\n {description && descriptionLocation === DescriptionLocation.Tooltip && (\r\n <div className={styles.labelRow}>\r\n <DescriptionIcon description={description} />\r\n </div>\r\n )}\r\n {editorTypeRendered}\r\n </div>\r\n </Field>\r\n\r\n {/* Advanced Search dialog. Only mounted while open so the inner grid\r\n tears down on close (avoids stale state when re-opening to a\r\n different view). */}\r\n {relatedTableName && !dialogHidden && (\r\n <LookupDialog\r\n hidden={false}\r\n onHiddenChange={setDialogHidden}\r\n title={label ?? relatedTableName}\r\n tableNames={[relatedTableName]}\r\n {...(advancedSearchAllowList !== undefined && { viewIds: advancedSearchAllowList })}\r\n selectionMode=\"multiple\"\r\n actionLabel={t('app.buttons.link.label')}\r\n onActionClicked={onDialogActionClicked}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Sub-renders — TagPicker / Combobox\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface RenderInput {\r\n id: string | undefined;\r\n disabled: boolean;\r\n required: boolean;\r\n currentSelection: readonly RelatedRecord[];\r\n suggestions: readonly RelatedRecord[];\r\n searchLoading: boolean;\r\n query: string;\r\n onQueryChange: (next: string) => void;\r\n onPick: (record: RelatedRecord) => void;\r\n onUnpick: (id: string) => void;\r\n onAdvancedSearchClicked: () => void;\r\n /** Columns from the latest GridDataResponse — drives option rendering. */\r\n resolvedColumns: readonly ResolvedColumn[];\r\n /** Resolved view list — drives the view picker visibility + options. */\r\n resolvedViews: readonly ViewMetadata[];\r\n selectedViewId: string | undefined;\r\n onViewChange: (next: string) => void;\r\n popupMaxHeight: string;\r\n popupMinHeight: string | undefined;\r\n styles: ReturnType<typeof useStyles>;\r\n t: ReturnType<typeof useT>;\r\n}\r\n\r\n/**\r\n * Renders one option's content — a vertical stack of the view's columns,\r\n * each highlighted against the user's current query. First column gets a\r\n * slightly bolder font so the row has a clear primary line. NULL / empty\r\n * cells render as a muted \"[NULL]\" placeholder so the row never collapses\r\n * (which would make the click target too small).\r\n */\r\nfunction renderOptionContent({\r\n record,\r\n resolvedColumns,\r\n fallbackText,\r\n query,\r\n styles,\r\n}: {\r\n record: TableRecord;\r\n resolvedColumns: readonly ResolvedColumn[];\r\n fallbackText: string;\r\n query: string;\r\n styles: ReturnType<typeof useStyles>;\r\n}): React.ReactElement {\r\n // No view columns yet (initial load hasn't responded, or the response\r\n // didn't carry any) — fall back to the single primary-name line we\r\n // already cached on the RelatedRecord.\r\n if (resolvedColumns.length === 0) {\r\n return <Highlighter text={fallbackText} searchText={query} />;\r\n }\r\n return (\r\n <div className={styles.optionStack}>\r\n {resolvedColumns.map((col, idx) => {\r\n const value = resolveColumnDisplayValue(record, col.columnName);\r\n const isFirst = idx === 0;\r\n if (!value || value.trim().length === 0) {\r\n return (\r\n <span\r\n key={col.columnName}\r\n className={`${isFirst ? styles.optionFirstLine : styles.optionSubLine} ${styles.nullValue}`}\r\n >\r\n [NULL]\r\n </span>\r\n );\r\n }\r\n return (\r\n <Highlighter\r\n key={col.columnName}\r\n text={value}\r\n searchText={query}\r\n className={isFirst ? styles.optionFirstLine : styles.optionSubLine}\r\n />\r\n );\r\n })}\r\n </div>\r\n );\r\n}\r\n\r\n/**\r\n * View-picker header — visible only when more than one view is in the\r\n * resolved set. Single-view (or zero-view) configurations don't render\r\n * anything so the dropdown surfaces the option list directly.\r\n *\r\n * The nested Dropdown uses the same focus / event-isolation pattern as\r\n * LookupEdit's polymorphic target picker (see\r\n * `lookup-edit.tsx` → `targetPickerHeader`):\r\n *\r\n * 1. `inlinePopup` — keeps the inner popup INSIDE the outer\r\n * Combobox / TagPicker's DOM tree. Without this, the inner option\r\n * list is portaled to body, and clicking an option counts as an\r\n * \"outside click\" from the outer's perspective → outer popup\r\n * dismisses.\r\n * 2. `onMouseDown preventDefault` on the wrapper — mousedown on the\r\n * inner Dropdown trigger / options would otherwise pull focus out\r\n * of the outer picker's input, and the outer popup would\r\n * blur-close before the click registered. preventDefault on\r\n * mousedown blocks the focus shift without blocking the subsequent\r\n * click event.\r\n * 3. `event.stopPropagation()` on the inner Dropdown's `onOptionSelect`\r\n * and `onOpenChange` — keeps those events from bubbling up to the\r\n * outer Combobox / TagPicker's listeners, which would otherwise\r\n * see the view-id as a record-selection or react to the inner\r\n * Dropdown's own open/close toggles.\r\n *\r\n * The outer `onOptionSelect` handlers in `renderTagPicker` /\r\n * `renderCombobox` also filter values that match a view id, so a single\r\n * defense slipping doesn't accidentally treat a view switch as a record\r\n * pick.\r\n */\r\nfunction renderViewPicker(input: RenderInput): React.ReactElement | null {\r\n const { resolvedViews, selectedViewId, onViewChange, styles, t } = input;\r\n if (resolvedViews.length <= 1) return null;\r\n const selectedView = resolvedViews.find((v) => v.id === selectedViewId);\r\n return (\r\n <div\r\n className={styles.viewPickerHeader}\r\n onMouseDown={(e) => e.preventDefault()}\r\n >\r\n <Dropdown\r\n size=\"small\"\r\n inlinePopup\r\n aria-label={t('app.components.view-selector.view-name')}\r\n selectedOptions={selectedViewId ? [selectedViewId] : []}\r\n value={selectedView?.name ?? ''}\r\n onOptionSelect={(event, data) => {\r\n event.stopPropagation();\r\n if (data.optionValue) onViewChange(data.optionValue);\r\n }}\r\n onOpenChange={(event) => {\r\n event.stopPropagation();\r\n }}\r\n >\r\n {resolvedViews.map((v) => (\r\n <Option key={v.id} value={v.id} text={v.name}>\r\n {v.name}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </div>\r\n );\r\n}\r\n\r\nfunction renderTagPicker(input: RenderInput) {\r\n const {\r\n id, disabled, currentSelection, suggestions, searchLoading,\r\n query, onQueryChange, onPick, onUnpick, onAdvancedSearchClicked,\r\n resolvedColumns, resolvedViews, popupMaxHeight, popupMinHeight, styles, t,\r\n } = input;\r\n\r\n const selectedIds = currentSelection.map((r) => r.id);\r\n const viewIdSet = new Set(resolvedViews.map((v) => v.id));\r\n\r\n return (\r\n <TagPicker\r\n disabled={disabled}\r\n selectedOptions={selectedIds}\r\n // Position rules:\r\n // - `position: 'below'` is the preferred placement.\r\n // - `flip` (the default with `pinned` unset) flips the dropdown\r\n // ABOVE the input when there isn't enough room below — far\r\n // better UX than clamping to a 50px sliver above the taskbar.\r\n // - NO `autoSize`. We tried `'height-always'` to clamp the popup\r\n // to the viewport, but combined with the listbox's\r\n // `overflow: auto` it caused scrollbar oscillation near the\r\n // bottom of the page: autoSize would clamp height → listbox\r\n // content overflowed → scrollbar appeared → width shrank →\r\n // reflow → autoSize recomputed → scrollbar disappeared →\r\n // cycle. The listbox's `maxHeight: popupMaxHeight` is the\r\n // sole height cap now; flip handles the no-room-below case.\r\n positioning={{\r\n position: 'below',\r\n align: 'start',\r\n }}\r\n onOptionSelect={(_, data) => {\r\n // Defensive filter: if a view-id slipped past the inner\r\n // view-picker's stopPropagation (Fluent's nested-popup event\r\n // model changes occasionally), the outer TagPicker would see it\r\n // as a record-selection candidate. Strip view ids before\r\n // diffing so view switches can never accidentally produce a\r\n // pending-associate request.\r\n const next = new Set(\r\n data.selectedOptions.filter((id) => !viewIdSet.has(id)),\r\n );\r\n const previous = new Set(selectedIds);\r\n for (const newId of next) {\r\n if (previous.has(newId)) continue;\r\n const found = suggestions.find((r) => r.id === newId);\r\n if (found) onPick(found);\r\n }\r\n for (const oldId of previous) {\r\n if (!next.has(oldId)) onUnpick(oldId);\r\n }\r\n // Clear the input field after a pick so the dropdown shows the\r\n // baseline suggestion list rather than the just-picked record.\r\n onQueryChange('');\r\n }}\r\n >\r\n <TagPickerControl className={styles.control}>\r\n <TagPickerGroup>\r\n {currentSelection.map((r) => (\r\n <Tag\r\n key={r.id}\r\n value={r.id}\r\n shape=\"rounded\"\r\n dismissible\r\n dismissIcon={{ 'aria-label': t('app.buttons.remove.label') }}\r\n >\r\n {r.displayName}\r\n </Tag>\r\n ))}\r\n </TagPickerGroup>\r\n <TagPickerInput\r\n id={id}\r\n aria-label={t('app.components.lookup-edit.search-placeholder')}\r\n value={query}\r\n onChange={(e) => onQueryChange((e.target as HTMLInputElement).value)}\r\n />\r\n </TagPickerControl>\r\n <TagPickerList\r\n className={styles.dropdownList}\r\n style={{\r\n maxHeight: popupMaxHeight,\r\n ...(popupMinHeight !== undefined && { minHeight: popupMinHeight }),\r\n }}\r\n >\r\n {renderViewPicker(input)}\r\n {searchLoading && (\r\n <div className={styles.inlineSpinner}>\r\n <Spinner size=\"tiny\" />\r\n </div>\r\n )}\r\n {!searchLoading && suggestions.length === 0 && (\r\n <div className={styles.emptyOption}>\r\n {t('app.components.lookup-edit.no-matches')}\r\n </div>\r\n )}\r\n {suggestions.map((r) => (\r\n // `text` is what Fluent uses for accessibility (announced by\r\n // screen readers, falls back to the input when the option is\r\n // matched against typed text); pass the primary-name line so\r\n // the announcement matches the visual primary line.\r\n <TagPickerOption key={r.id} value={r.id} text={r.displayName}>\r\n {renderOptionContent({\r\n record: r.source,\r\n resolvedColumns,\r\n fallbackText: r.displayName,\r\n query,\r\n styles,\r\n })}\r\n </TagPickerOption>\r\n ))}\r\n <div className={styles.advancedSearchFooter} onMouseDown={(e) => e.preventDefault()}>\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Search20Regular />}\r\n onClick={onAdvancedSearchClicked}\r\n >\r\n {t('app.buttons.advanced-search.label')}\r\n </Button>\r\n </div>\r\n </TagPickerList>\r\n </TagPicker>\r\n );\r\n}\r\n\r\nfunction renderCombobox(input: RenderInput) {\r\n const {\r\n id, disabled, currentSelection, suggestions, searchLoading,\r\n query, onQueryChange, onPick, onUnpick, onAdvancedSearchClicked,\r\n resolvedColumns, resolvedViews, popupMaxHeight, popupMinHeight, styles, t,\r\n } = input;\r\n\r\n const selectedIds = currentSelection.map((r) => r.id);\r\n const viewIdSet = new Set(resolvedViews.map((v) => v.id));\r\n\r\n return (\r\n <Combobox\r\n id={id}\r\n multiselect\r\n disabled={disabled || undefined}\r\n value={query}\r\n selectedOptions={selectedIds}\r\n // Same positioning rules as the TagPicker — see comment there.\r\n // Flip-up when no room below; listbox's `maxHeight` caps height\r\n // (no `autoSize`, no `pinned`).\r\n positioning={{\r\n position: 'below',\r\n align: 'start',\r\n }}\r\n listbox={{\r\n className: styles.dropdownList,\r\n style: {\r\n maxHeight: popupMaxHeight,\r\n ...(popupMinHeight !== undefined && { minHeight: popupMinHeight }),\r\n },\r\n }}\r\n onChange={(e) => onQueryChange((e.target as HTMLInputElement).value)}\r\n onOptionSelect={(_, data) => {\r\n // Same view-id guard as renderTagPicker — see comment there.\r\n const previous = new Set(selectedIds);\r\n const next = new Set(\r\n data.selectedOptions.filter((id) => !viewIdSet.has(id)),\r\n );\r\n for (const newId of next) {\r\n if (previous.has(newId)) continue;\r\n const found = suggestions.find((r) => r.id === newId);\r\n if (found) onPick(found);\r\n }\r\n for (const oldId of previous) {\r\n if (!next.has(oldId)) onUnpick(oldId);\r\n }\r\n onQueryChange('');\r\n }}\r\n className={styles.control}\r\n >\r\n {renderViewPicker(input)}\r\n {/* Render the already-selected records as options too (with their\r\n ids as values) so Combobox's checked-state lookup can match them.\r\n Without these entries, selectedOptions ids that don't have a\r\n matching <Option> in the listbox get dropped on next select. */}\r\n {currentSelection.map((r) => (\r\n <Option key={r.id} value={r.id} text={r.displayName}>\r\n {r.displayName}\r\n </Option>\r\n ))}\r\n {searchLoading && (\r\n <div className={styles.inlineSpinner}>\r\n <Spinner size=\"tiny\" />\r\n </div>\r\n )}\r\n {!searchLoading && suggestions.length === 0 && (\r\n <div className={styles.emptyOption}>\r\n {t('app.components.lookup-edit.no-matches')}\r\n </div>\r\n )}\r\n {suggestions.map((r) => (\r\n <Option key={r.id} value={r.id} text={r.displayName}>\r\n {renderOptionContent({\r\n record: r.source,\r\n resolvedColumns,\r\n fallbackText: r.displayName,\r\n query,\r\n styles,\r\n })}\r\n </Option>\r\n ))}\r\n <div className={styles.advancedSearchFooter} onMouseDown={(e) => e.preventDefault()}>\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<Search20Regular />}\r\n onClick={onAdvancedSearchClicked}\r\n >\r\n {t('app.buttons.advanced-search.label')}\r\n </Button>\r\n </div>\r\n </Combobox>\r\n );\r\n}\r\n","import {\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n makeStyles,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport {\r\n CheckmarkCircle24Regular,\r\n DismissCircle24Regular,\r\n Info24Regular,\r\n QuestionCircle24Regular,\r\n Warning24Regular,\r\n} from '@fluentui/react-icons';\r\nimport {\r\n DialogServiceProvider,\r\n useT,\r\n type ConfirmationDialogOptions,\r\n type DialogOptions,\r\n type DialogResult,\r\n type DialogService,\r\n} from '@powerportalspro/react';\r\nimport { useCallback, useMemo, useState, type ReactNode } from 'react';\r\n\r\ntype DialogVariant = 'success' | 'warning' | 'error' | 'info' | 'confirmation';\r\n\r\ninterface DialogRequest {\r\n variant: DialogVariant;\r\n message: string;\r\n title?: string;\r\n primaryText?: string;\r\n secondaryText?: string;\r\n resolve: (result: DialogResult) => void;\r\n}\r\n\r\nconst useStyles = makeStyles({\r\n titleRow: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n iconSuccess: { color: tokens.colorStatusSuccessForeground1 },\r\n iconWarning: { color: tokens.colorStatusWarningForeground1 },\r\n iconError: { color: tokens.colorStatusDangerForeground1 },\r\n iconInfo: { color: tokens.colorBrandForeground1 },\r\n iconConfirmation: { color: tokens.colorBrandForeground1 },\r\n});\r\n\r\n/**\r\n * Maps each dialog variant to its localization key for the default title.\r\n * Mirrors Blazor's `app.components.dialog.*-title` keys in\r\n * `ppp-server.defaults.en.json`. Resolved at render time via `useT` so locale\r\n * switches reflect immediately.\r\n */\r\nconst DEFAULT_TITLE_KEYS: Record<DialogVariant, string> = {\r\n success: 'app.components.dialog.success-title',\r\n warning: 'app.components.dialog.warning-title',\r\n error: 'app.components.dialog.error-title',\r\n info: 'app.components.dialog.information-title',\r\n confirmation: 'app.components.dialog.confirmation-title',\r\n};\r\n\r\n/**\r\n * Fluent UI implementation of {@link DialogService}. Mount a single\r\n * `<FluentDialogProvider>` near the root of your app — descendants get the\r\n * `DialogService` via React context (`useDialogService()`) and the provider\r\n * renders one Fluent `Dialog` at a time, queuing additional requests so\r\n * concurrent calls don't lose each other.\r\n *\r\n * Drop-in replacement for Blazor's DI registration of `IDialogService` +\r\n * `IPowerPortalsProDialogService` in `PowerPortalsProWebBlazorFluentUI` — same\r\n * concrete behavior (success / warning / error / info / confirmation dialogs),\r\n * just plumbed through React context instead of constructor-injected services.\r\n *\r\n * Single-button dialogs (success/warning/error/info) resolve with\r\n * `{ cancelled: false }` regardless of whether the user clicked the button or\r\n * dismissed via Escape/backdrop — there's only one outcome semantically.\r\n * Confirmation dialogs resolve with `{ cancelled: true }` for Escape/backdrop\r\n * (treated as \"No\"), `{ cancelled: false }` only when the primary button is\r\n * clicked.\r\n */\r\nexport function FluentDialogProvider({ children }: { children: ReactNode }) {\r\n const styles = useStyles();\r\n const t = useT();\r\n const [queue, setQueue] = useState<readonly DialogRequest[]>([]);\r\n const current = queue[0];\r\n\r\n // The DialogService methods enqueue requests; the provider component renders\r\n // them one at a time. Stable identity (no deps) so consumers caching the\r\n // service reference don't see churn — `setQueue` is React-stable.\r\n const dialogService = useMemo<DialogService>(() => {\r\n function enqueue(\r\n variant: DialogVariant,\r\n message: string,\r\n options?: DialogOptions | ConfirmationDialogOptions,\r\n ): Promise<DialogResult> {\r\n return new Promise<DialogResult>((resolve) => {\r\n setQueue((prev) => [\r\n ...prev,\r\n {\r\n variant,\r\n message,\r\n ...(options?.title !== undefined && { title: options.title }),\r\n ...(options?.primaryText !== undefined && { primaryText: options.primaryText }),\r\n ...((options as ConfirmationDialogOptions | undefined)?.secondaryText !== undefined && {\r\n secondaryText: (options as ConfirmationDialogOptions).secondaryText,\r\n }),\r\n resolve,\r\n },\r\n ]);\r\n });\r\n }\r\n return {\r\n showSuccessAsync: (m, o) => enqueue('success', m, o),\r\n showWarningAsync: (m, o) => enqueue('warning', m, o),\r\n showErrorAsync: (m, o) => enqueue('error', m, o),\r\n showInfoAsync: (m, o) => enqueue('info', m, o),\r\n showConfirmationAsync: (m, o) => enqueue('confirmation', m, o),\r\n };\r\n }, []);\r\n\r\n const dismiss = useCallback(\r\n (cancelled: boolean) => {\r\n if (!current) return;\r\n current.resolve({ cancelled });\r\n setQueue((prev) => prev.slice(1));\r\n },\r\n [current],\r\n );\r\n\r\n return (\r\n <DialogServiceProvider value={dialogService}>\r\n {children}\r\n {current && (\r\n <Dialog\r\n open={true}\r\n onOpenChange={(_, data) => {\r\n if (!data.open) {\r\n // Escape/backdrop. Confirmations treat dismiss as cancel; alerts\r\n // treat dismiss as acknowledgment (only one outcome anyway).\r\n dismiss(current.variant === 'confirmation');\r\n }\r\n }}\r\n // `modal` keeps the page interactive only after dismiss; `alert` would\r\n // also block backdrop dismissal entirely. We allow backdrop dismissal\r\n // for parity with how typical UI dialogs behave on the web.\r\n modalType=\"modal\"\r\n >\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={styles.titleRow}>\r\n <VariantIcon variant={current.variant} styles={styles} />\r\n <span>{current.title ?? t(DEFAULT_TITLE_KEYS[current.variant])}</span>\r\n </div>\r\n </DialogTitle>\r\n {/* Render message as HTML markup to match Blazor's\r\n IDialogService contract (see `DialogService` interface in\r\n @powerportalspro/react). Framework localization strings —\r\n e.g. `app.messages.unsaved-changes` — embed `<br/>` and\r\n other tags expecting them to be interpreted, not escaped.\r\n The dangerouslySetInnerHTML lives on a child div because\r\n Fluent UI's DialogContent is a slot component that doesn't\r\n forward the prop to its inner element. */}\r\n <DialogContent>\r\n <div dangerouslySetInnerHTML={{ __html: current.message }} />\r\n </DialogContent>\r\n <DialogActions>\r\n {current.variant === 'confirmation' && (\r\n <Button appearance=\"secondary\" onClick={() => dismiss(true)}>\r\n {current.secondaryText ?? t('app.buttons.no.label')}\r\n </Button>\r\n )}\r\n <Button appearance=\"primary\" onClick={() => dismiss(false)}>\r\n {current.primaryText ??\r\n t(current.variant === 'confirmation' ? 'app.buttons.yes.label' : 'app.buttons.ok.label')}\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n )}\r\n </DialogServiceProvider>\r\n );\r\n}\r\n\r\nfunction VariantIcon({\r\n variant,\r\n styles,\r\n}: {\r\n variant: DialogVariant;\r\n styles: ReturnType<typeof useStyles>;\r\n}) {\r\n switch (variant) {\r\n case 'success':\r\n return <CheckmarkCircle24Regular className={styles.iconSuccess} />;\r\n case 'warning':\r\n return <Warning24Regular className={styles.iconWarning} />;\r\n case 'error':\r\n return <DismissCircle24Regular className={styles.iconError} />;\r\n case 'info':\r\n return <Info24Regular className={styles.iconInfo} />;\r\n case 'confirmation':\r\n return <QuestionCircle24Regular className={styles.iconConfirmation} />;\r\n }\r\n}\r\n","import { createContext, useContext } from 'react';\r\n\r\n/**\r\n * Theme mode the user has selected. Mirrors Blazor's\r\n * `DesignThemeModes` enum (Light = 0, Dark = 1, System = 2).\r\n * Hybrid const-object + literal-union pattern so consumers can compare\r\n * via either string literal or the constant.\r\n */\r\nexport const ThemeMode = {\r\n Light: 'light',\r\n Dark: 'dark',\r\n System: 'system',\r\n} as const;\r\nexport type ThemeMode = (typeof ThemeMode)[keyof typeof ThemeMode];\r\n\r\n/**\r\n * Accent color slot the theme provider resolves to a Fluent v9\r\n * theme. Mirrors Blazor's `OfficeColor` enum at the surface level\r\n * but with a narrower set of out-of-the-box options — consumers\r\n * register more via `<PowerPortalsProThemeProvider customAccents={...}>`.\r\n *\r\n * Built-in values:\r\n * - `'default'` — Fluent v9 Communication Blue (`webLightTheme` /\r\n * `webDarkTheme`).\r\n * - `'teams'` — Fluent v9 Teams Purple (`teamsLightTheme` /\r\n * `teamsDarkTheme`).\r\n *\r\n * Custom keys are any string the consumer registers; they take\r\n * priority over built-ins when names collide.\r\n */\r\nexport type AccentColor = 'default' | 'teams' | (string & {});\r\n\r\n/**\r\n * Text-direction preference. Mirrors Blazor's `ThemeTextDirection`\r\n * boolean toggle but as a literal-union for parity with the rest of\r\n * the theme enums.\r\n */\r\nexport const TextDirection = {\r\n Ltr: 'ltr',\r\n Rtl: 'rtl',\r\n} as const;\r\nexport type TextDirection = (typeof TextDirection)[keyof typeof TextDirection];\r\n\r\n/**\r\n * Surface exposed by {@link PowerPortalsProThemeProvider} to consumers\r\n * who need to read or change the active theme — `<ThemeSelector>`,\r\n * `<ThemeColorSelector>`, `<ThemeTextDirection>`, anything that\r\n * wants to know \"is the user in dark mode right now\".\r\n *\r\n * `mode` is the user's *preference* (which may be `'system'`); `isDark`\r\n * is the *resolved* boolean after honoring the OS preference. Read the\r\n * one that matches the question you're asking.\r\n */\r\nexport interface ThemeContextValue {\r\n /** User's selected mode — what's persisted to localStorage. */\r\n readonly mode: ThemeMode;\r\n /**\r\n * Resolved dark/light boolean. `mode === 'dark'` always returns\r\n * `true`; `mode === 'light'` always returns `false`; `mode === 'system'`\r\n * tracks `prefers-color-scheme: dark` via `matchMedia`.\r\n */\r\n readonly isDark: boolean;\r\n /**\r\n * Updates the user's mode preference. Persists to localStorage and\r\n * applies the new Fluent v9 theme on the next render. Reading\r\n * `mode` after this call reflects the new value synchronously.\r\n */\r\n readonly setMode: (mode: ThemeMode) => void;\r\n /** User's selected accent color — persisted to localStorage. */\r\n readonly accentColor: AccentColor;\r\n /**\r\n * Updates the user's accent color preference. Must resolve to a\r\n * known built-in or a consumer-registered custom accent on the\r\n * provider; unknown keys fall back to `'default'`.\r\n */\r\n readonly setAccentColor: (accent: AccentColor) => void;\r\n /**\r\n * Available accent options. Includes both built-ins and any\r\n * consumer-registered customs. Drives the `<ThemeColorSelector>`\r\n * option list.\r\n */\r\n readonly availableAccents: readonly AccentColor[];\r\n /**\r\n * Each accent's representative brand-fill color (hex). Drives the\r\n * color swatch rendered next to each option in\r\n * `<ThemeColorSelector>`, and any consumer UI that wants a \"this is\r\n * what the accent looks like\" preview without consulting the full\r\n * FluentUI theme. Computed from the registered accent's\r\n * `colorBrandBackground` token in its light-theme variant (the most\r\n * recognizable shade for the brand identity).\r\n */\r\n readonly accentColors: Readonly<Record<AccentColor, string>>;\r\n /** Current text direction — persisted to localStorage. */\r\n readonly textDirection: TextDirection;\r\n /**\r\n * Updates the user's text-direction preference. Flips\r\n * FluentProvider's `dir` prop on the next render, which cascades\r\n * to every Fluent v9 component below.\r\n */\r\n readonly setTextDirection: (direction: TextDirection) => void;\r\n}\r\n\r\n// `null` sentinel lets `useTheme` distinguish \"outside provider\" from\r\n// \"inside provider with unset value\" — useTheme throws when null so\r\n// missing-provider wiring bugs surface loudly.\r\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\r\n\r\nexport const ThemeContextProvider = ThemeContext.Provider;\r\n\r\n/**\r\n * Reads the theme state from the surrounding {@link PowerPortalsProThemeProvider}.\r\n * Throws when called outside one — a missing provider is a wiring bug.\r\n */\r\nexport function useTheme(): ThemeContextValue {\r\n const ctx = useContext(ThemeContext);\r\n if (!ctx) {\r\n throw new Error(\r\n 'useTheme() was called outside <PowerPortalsProThemeProvider>. ' +\r\n 'Wrap your app in the provider at the same level you mount <FluentProvider>.',\r\n );\r\n }\r\n return ctx;\r\n}\r\n","import {\r\n FluentProvider,\r\n makeStaticStyles,\r\n teamsDarkTheme,\r\n teamsLightTheme,\r\n webDarkTheme,\r\n webLightTheme,\r\n type FluentProviderProps,\r\n type Theme,\r\n} from '@fluentui/react-components';\r\nimport {\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n type CSSProperties,\r\n type ReactNode,\r\n} from 'react';\r\nimport {\r\n TextDirection,\r\n ThemeContextProvider,\r\n ThemeMode,\r\n type AccentColor,\r\n type ThemeContextValue,\r\n} from './theme-context.js';\r\n\r\n/**\r\n * Pair of Fluent v9 themes a single accent registers. Light and dark\r\n * are both required so the mode toggle works regardless of accent.\r\n */\r\nexport interface AccentThemePair {\r\n readonly light: Theme;\r\n readonly dark: Theme;\r\n}\r\n\r\nconst BUILT_IN_ACCENTS: Readonly<Record<string, AccentThemePair>> = {\r\n default: { light: webLightTheme, dark: webDarkTheme },\r\n teams: { light: teamsLightTheme, dark: teamsDarkTheme },\r\n};\r\n\r\n/**\r\n * Global body styles keyed by the theme-mode class this provider\r\n * toggles on `document.body`. Fluent v9's `FluentProvider` applies its\r\n * design tokens to its own wrapper `<div>`, so any CSS on `<body>`\r\n * that references `var(--colorNeutralBackground*)` falls back to its\r\n * hard-coded default (light-theme white) — even when the app is in\r\n * dark mode. The visible symptom is a white sliver / flash anywhere\r\n * the FluentProvider's wrapper doesn't fully cover the viewport\r\n * (initial load, scrollbar gutters, routes that don't fill height).\r\n *\r\n * Painting body with hard-coded hex values that match Fluent's own\r\n * `webDarkTheme` / `webLightTheme` page surface tokens fixes the flash\r\n * AND lets consumers stop hand-painting body in their app CSS. The\r\n * `ppp-theme-dark` class is toggled on `document.body` from a\r\n * `useEffect` below; the `ppp-theme-light` class is its negation.\r\n *\r\n * Consumers can override per-app by declaring a higher-specificity\r\n * rule (`body.my-app.ppp-theme-dark { ... }`) or by setting\r\n * `background-color` on a 100%-height descendant inside FluentProvider\r\n * (where the tokens DO resolve, so `var(--colorNeutralBackground1)`\r\n * works as expected).\r\n */\r\nconst useGlobalBodyStyles = makeStaticStyles({\r\n // Fluent's `webDarkTheme.colorNeutralBackground4` (#0a0a0a) — same\r\n // token the side nav rail uses, so the body and rail share one\r\n // continuous surface in dark mode.\r\n 'body.ppp-theme-dark': {\r\n backgroundColor: '#0a0a0a',\r\n color: '#ffffff',\r\n },\r\n // Fluent's `webLightTheme.colorNeutralBackground2` (#fafafa) — a\r\n // hair off pure white, mirroring what most Fluent v9 demo apps\r\n // use as their page background in light mode.\r\n 'body.ppp-theme-light': {\r\n backgroundColor: '#fafafa',\r\n color: '#242424',\r\n },\r\n});\r\n\r\n/**\r\n * Default localStorage keys the provider reads/writes the user's\r\n * preferences to. Distinct from Blazor's `\"theme\"` key by design —\r\n * the stored shapes are different (single strings vs. Fluent\r\n * Blazor's JSON blob), so sharing slots across stacks would cause\r\n * one to misread the other's value.\r\n */\r\nconst DEFAULT_MODE_STORAGE_KEY = 'ppp-theme-mode';\r\nconst DEFAULT_ACCENT_STORAGE_KEY = 'ppp-theme-accent';\r\nconst DEFAULT_DIRECTION_STORAGE_KEY = 'ppp-theme-direction';\r\n\r\nexport interface PowerPortalsProThemeProviderProps {\r\n children: ReactNode;\r\n /**\r\n * Initial mode applied when no localStorage preference is set.\r\n * Defaults to `'system'` — honors the OS preference until the user\r\n * explicitly picks light or dark.\r\n */\r\n defaultMode?: ThemeMode;\r\n /**\r\n * Initial accent color applied when no localStorage preference is\r\n * set. Defaults to `'default'` (Fluent v9 Communication Blue).\r\n */\r\n defaultAccentColor?: AccentColor;\r\n /**\r\n * Initial text direction applied when no localStorage preference\r\n * is set. Defaults to `'ltr'`.\r\n */\r\n defaultTextDirection?: TextDirection;\r\n /**\r\n * Consumer-supplied custom accents. Each key registers as an\r\n * available `<ThemeColorSelector>` option. Built-ins (`'default'`,\r\n * `'teams'`) can be overridden by registering a key with the same\r\n * name. Construct light / dark variants via `createLightTheme(...)`\r\n * / `createDarkTheme(...)` from `@fluentui/react-components`, fed\r\n * with the `BrandVariants` 16-stop ramp.\r\n *\r\n * ```tsx\r\n * import { createLightTheme, createDarkTheme } from '@fluentui/react-components';\r\n * const forest = { 10: '#001805', ..., 160: '#FFFFFF' };\r\n * <PowerPortalsProThemeProvider\r\n * customAccents={{\r\n * forest: {\r\n * light: createLightTheme(forest),\r\n * dark: createDarkTheme(forest),\r\n * },\r\n * }}\r\n * >\r\n * ```\r\n */\r\n customAccents?: Readonly<Record<string, AccentThemePair>>;\r\n /**\r\n * localStorage key for persisting the user's mode preference.\r\n * Defaults to `'ppp-theme-mode'`. Override when running multiple\r\n * PowerPortalsPro apps on the same origin so they don't share theme\r\n * state. The accent / direction keys derive from this same root\r\n * (`'<key>-accent'` / `'<key>-direction'`).\r\n */\r\n storageKey?: string;\r\n /**\r\n * Inline style applied to the underlying `<FluentProvider>`. Pass\r\n * `{ height: '100%' }` for full-height app shells (matches the\r\n * pre-provider hardcoded value the React demo was using).\r\n */\r\n style?: CSSProperties;\r\n /**\r\n * Forwarded onto the underlying `<FluentProvider>`. Most consumers\r\n * leave this alone — the provider sets `theme` from the resolved\r\n * mode internally. Use `themeOverride` to layer overrides on top\r\n * of the resolved theme.\r\n */\r\n fluentProviderProps?: Omit<FluentProviderProps, 'theme' | 'children' | 'style' | 'dir'>;\r\n}\r\n\r\n/**\r\n * Reads the persisted mode preference from localStorage. Wrapped in a\r\n * try/catch because storage access can throw in private-browsing modes\r\n * or when the site quota is exhausted; in those cases we fall back to\r\n * the default. Validates the parsed value against the {@link ThemeMode}\r\n * enum so a malformed entry can't lock the app into an unrenderable\r\n * state.\r\n */\r\nfunction readStoredMode(storageKey: string): ThemeMode | null {\r\n if (typeof window === 'undefined') return null;\r\n try {\r\n const raw = window.localStorage.getItem(storageKey);\r\n if (raw === ThemeMode.Light || raw === ThemeMode.Dark || raw === ThemeMode.System) {\r\n return raw;\r\n }\r\n return null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction readStoredString(storageKey: string): string | null {\r\n if (typeof window === 'undefined') return null;\r\n try {\r\n return window.localStorage.getItem(storageKey);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction readStoredDirection(storageKey: string): TextDirection | null {\r\n const raw = readStoredString(storageKey);\r\n return raw === TextDirection.Ltr || raw === TextDirection.Rtl ? raw : null;\r\n}\r\n\r\nfunction writeStored(storageKey: string, value: string): void {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n window.localStorage.setItem(storageKey, value);\r\n } catch {\r\n // Silent — private browsing / quota exhausted / etc. The in-\r\n // memory state still tracks the user's preference for this session.\r\n }\r\n}\r\n\r\n/**\r\n * Returns the current `prefers-color-scheme: dark` match. SSR-safe —\r\n * returns `false` when `window` is absent (the server has no OS\r\n * preference to query).\r\n */\r\nfunction getSystemPrefersDark(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\r\n}\r\n\r\n/**\r\n * Top-level theme provider for `@powerportalspro/react-fluent`. Wraps\r\n * the app in a `<FluentProvider>` whose `theme` swaps between\r\n * `webLightTheme` and `webDarkTheme` based on the user's mode\r\n * preference (with `'system'` honoring `prefers-color-scheme: dark`).\r\n *\r\n * Mounts ABOVE everything in the app — descendants read the resolved\r\n * theme via Fluent v9's design tokens, and the optional\r\n * `<ThemeSelector>` / `<SiteSettingsButton>` UIs read/write the\r\n * preference via `useTheme()`.\r\n *\r\n * Mirrors Blazor's `<ThemeProvider>` + `FluentDesignTheme`\r\n * combination, condensed into a single React component since the\r\n * React FluentProvider already owns the design-token cascade. Cross-\r\n * stack consumers picking different storage keys keep their state\r\n * separate — pick a custom `storageKey` if you're embedding a\r\n * PowerPortalsPro React app inside a host that also persists theme.\r\n *\r\n * ```tsx\r\n * <PowerPortalsProThemeProvider style={{ height: '100%' }}>\r\n * <App />\r\n * </PowerPortalsProThemeProvider>\r\n * ```\r\n */\r\nexport function PowerPortalsProThemeProvider({\r\n children,\r\n defaultMode = ThemeMode.System,\r\n defaultAccentColor = 'default',\r\n defaultTextDirection = TextDirection.Ltr,\r\n customAccents,\r\n storageKey = DEFAULT_MODE_STORAGE_KEY,\r\n style,\r\n fluentProviderProps,\r\n}: PowerPortalsProThemeProviderProps) {\r\n // Derive accent / direction storage keys from the (possibly\r\n // overridden) root mode key so a consumer's `storageKey=\"acme-theme\"`\r\n // also scopes their accent + direction state. Default keys stay\r\n // distinct (`-mode`, `-accent`, `-direction`) so the legacy\r\n // ppp-theme-mode key isn't disturbed by the new ones.\r\n const accentStorageKey =\r\n storageKey === DEFAULT_MODE_STORAGE_KEY\r\n ? DEFAULT_ACCENT_STORAGE_KEY\r\n : `${storageKey}-accent`;\r\n const directionStorageKey =\r\n storageKey === DEFAULT_MODE_STORAGE_KEY\r\n ? DEFAULT_DIRECTION_STORAGE_KEY\r\n : `${storageKey}-direction`;\r\n\r\n // Merged accent map: built-ins + consumer customs. Customs win on\r\n // name collision so consumers can replace the default if they want\r\n // their own brand at the `'default'` slot. Stable identity across\r\n // renders to keep `availableAccents` (which downstream `useMemo`s\r\n // list as a dependency) from churning.\r\n const accentMap = useMemo<Readonly<Record<string, AccentThemePair>>>(\r\n () => ({ ...BUILT_IN_ACCENTS, ...customAccents }),\r\n // Stringified keys = stable identity for the customAccents map\r\n // when its contents don't actually change (a consumer passing\r\n // a fresh inline object literal each render is common).\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [customAccents ? Object.keys(customAccents).join('|') : ''],\r\n );\r\n const availableAccents = useMemo<readonly AccentColor[]>(\r\n () => Object.keys(accentMap) as AccentColor[],\r\n [accentMap],\r\n );\r\n\r\n // Lazy init from storage so first paint reflects the persisted\r\n // preference (avoids the light → dark flash on every page load when\r\n // the user had previously picked dark).\r\n const [mode, setModeState] = useState<ThemeMode>(\r\n () => readStoredMode(storageKey) ?? defaultMode,\r\n );\r\n const [accentColor, setAccentColorState] = useState<AccentColor>(() => {\r\n const stored = readStoredString(accentStorageKey);\r\n // Validate against the resolved accent map — a stale stored value\r\n // (consumer removed a custom registration) falls back rather\r\n // than rendering an undefined theme.\r\n if (stored && stored in accentMap) return stored;\r\n return defaultAccentColor in accentMap ? defaultAccentColor : 'default';\r\n });\r\n const [textDirection, setTextDirectionState] = useState<TextDirection>(\r\n () => readStoredDirection(directionStorageKey) ?? defaultTextDirection,\r\n );\r\n\r\n // Mirror the OS preference. Read on mount + subscribe so a user who\r\n // flips their system theme while the page is open sees the update\r\n // when their mode is `'system'`. SSR-safe via the helper.\r\n const [systemPrefersDark, setSystemPrefersDark] = useState<boolean>(\r\n () => getSystemPrefersDark(),\r\n );\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return undefined;\r\n const media = window.matchMedia('(prefers-color-scheme: dark)');\r\n const handler = (event: MediaQueryListEvent) =>\r\n setSystemPrefersDark(event.matches);\r\n // Safari < 14 only supports the deprecated addListener / removeListener\r\n // pair; modern browsers also accept addEventListener. Use the modern\r\n // API when available, fall back to the legacy one for compatibility.\r\n if (media.addEventListener) {\r\n media.addEventListener('change', handler);\r\n return () => media.removeEventListener('change', handler);\r\n }\r\n media.addListener(handler);\r\n return () => media.removeListener(handler);\r\n }, []);\r\n\r\n const isDark = useMemo(() => {\r\n if (mode === ThemeMode.Dark) return true;\r\n if (mode === ThemeMode.Light) return false;\r\n return systemPrefersDark;\r\n }, [mode, systemPrefersDark]);\r\n\r\n // Register the global body styles (see `useGlobalBodyStyles` above\r\n // for the rationale) and keep `document.body`'s class list in sync\r\n // with the current theme. The class toggle is what actually applies\r\n // the static styles — registration just ensures the rules are in\r\n // the document. Guarded against SSR (`typeof document`) so the\r\n // provider can render server-side without exploding.\r\n useGlobalBodyStyles();\r\n useEffect(() => {\r\n if (typeof document === 'undefined') return;\r\n document.body.classList.toggle('ppp-theme-dark', isDark);\r\n document.body.classList.toggle('ppp-theme-light', !isDark);\r\n // Cleanup is intentionally omitted — the class reflects the\r\n // CURRENT theme of the topmost provider, and removing it on\r\n // unmount would leave body in an unstyled state until the next\r\n // mount re-applies. If multiple PowerPortalsProThemeProvider\r\n // instances ever coexist (rare but legal), the last one to render\r\n // wins, which matches the \"topmost provider drives global state\"\r\n // pattern already used for the unsaved-changes registry.\r\n }, [isDark]);\r\n\r\n const setMode = useCallback(\r\n (next: ThemeMode) => {\r\n setModeState(next);\r\n writeStored(storageKey, next);\r\n },\r\n [storageKey],\r\n );\r\n const setAccentColor = useCallback(\r\n (next: AccentColor) => {\r\n // Reject unknown accents (stale storage / programmatic typo)\r\n // by falling back to the current value — a no-op rather than\r\n // breaking the FluentProvider render. The map is stable across\r\n // renders so reading from it during a setter is fine.\r\n if (!(next in accentMap)) return;\r\n setAccentColorState(next);\r\n writeStored(accentStorageKey, next);\r\n },\r\n [accentMap, accentStorageKey],\r\n );\r\n const setTextDirection = useCallback(\r\n (next: TextDirection) => {\r\n setTextDirectionState(next);\r\n writeStored(directionStorageKey, next);\r\n },\r\n [directionStorageKey],\r\n );\r\n\r\n // Representative brand fill per accent, exposed via context so\r\n // `<ThemeColorSelector>` can render a swatch next to each option\r\n // without consumers having to thread a parallel \"color per accent\"\r\n // map. We pick the LIGHT theme's `colorBrandBackground` because\r\n // that's the recognizable brand identity color (stop 80 in\r\n // FluentUI's BrandVariants); the dark-theme equivalent is brighter\r\n // and reads less like \"the brand color\" in a small swatch.\r\n const accentColors = useMemo<Readonly<Record<string, string>>>(() => {\r\n const map: Record<string, string> = {};\r\n for (const [name, pair] of Object.entries(accentMap)) {\r\n map[name] = pair.light.colorBrandBackground;\r\n }\r\n return map;\r\n }, [accentMap]);\r\n\r\n const value = useMemo<ThemeContextValue>(\r\n () => ({\r\n mode,\r\n isDark,\r\n setMode,\r\n accentColor,\r\n setAccentColor,\r\n availableAccents,\r\n accentColors,\r\n textDirection,\r\n setTextDirection,\r\n }),\r\n [\r\n mode,\r\n isDark,\r\n setMode,\r\n accentColor,\r\n setAccentColor,\r\n availableAccents,\r\n accentColors,\r\n textDirection,\r\n setTextDirection,\r\n ],\r\n );\r\n\r\n // Resolve the active Fluent v9 theme from the accent + mode pair.\r\n // Unknown accent falls back to `'default'` (defense-in-depth — the\r\n // setter already validates, but a custom override removed at\r\n // runtime would otherwise render undefined).\r\n const accentPair = accentMap[accentColor] ?? accentMap.default ?? BUILT_IN_ACCENTS.default!;\r\n const resolvedTheme = isDark ? accentPair.dark : accentPair.light;\r\n\r\n return (\r\n <ThemeContextProvider value={value}>\r\n <FluentProvider\r\n theme={resolvedTheme}\r\n dir={textDirection}\r\n {...(style && { style })}\r\n {...fluentProviderProps}\r\n >\r\n {children}\r\n </FluentProvider>\r\n </ThemeContextProvider>\r\n );\r\n}\r\n","import {\r\n Dropdown,\r\n Field,\r\n Option,\r\n type DropdownProps,\r\n} from '@fluentui/react-components';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { useCallback, useId } from 'react';\r\nimport { ThemeMode, useTheme } from './theme-context.js';\r\n\r\nexport interface ThemeSelectorProps {\r\n /** Disable the control. Useful when displaying the user's read-only preference. */\r\n readOnly?: boolean;\r\n /**\r\n * Override the displayed label. Defaults to the localized\r\n * `app.components.theme-selector.theme-label` string.\r\n */\r\n label?: string;\r\n /**\r\n * Forwarded to Fluent's Dropdown. Pass `'100%'` for full-width\r\n * layouts. Defaults to undefined (Fluent's intrinsic width).\r\n */\r\n width?: string;\r\n}\r\n\r\nconst MODE_ORDER: readonly ThemeMode[] = [\r\n ThemeMode.Light,\r\n ThemeMode.Dark,\r\n ThemeMode.System,\r\n];\r\n\r\n/**\r\n * Dropdown that lets the user pick between Light, Dark, and System\r\n * (follow OS preference) themes. Mirrors Blazor's `<ThemeSelector>`.\r\n * Reads/writes the preference via {@link useTheme} — wrap in a\r\n * `<PowerPortalsProThemeProvider>` for it to function.\r\n */\r\nexport function ThemeSelector({ readOnly, label, width }: ThemeSelectorProps) {\r\n const { mode, setMode } = useTheme();\r\n const t = useT();\r\n const labelId = useId();\r\n\r\n const optionLabel = useCallback(\r\n (m: ThemeMode) => t(`app.components.theme-selector.mode.${m}`),\r\n [t],\r\n );\r\n\r\n const handleOptionSelect: DropdownProps['onOptionSelect'] = (_event, data) => {\r\n const next = data.optionValue as ThemeMode | undefined;\r\n if (\r\n next === ThemeMode.Light ||\r\n next === ThemeMode.Dark ||\r\n next === ThemeMode.System\r\n ) {\r\n setMode(next);\r\n }\r\n };\r\n\r\n return (\r\n <Field\r\n className=\"ppp-theme-selector\"\r\n label={label ?? t('app.components.theme-selector.theme-label')}\r\n id={labelId}\r\n >\r\n <Dropdown\r\n aria-labelledby={labelId}\r\n value={optionLabel(mode)}\r\n selectedOptions={[mode]}\r\n onOptionSelect={handleOptionSelect}\r\n disabled={readOnly}\r\n {...(width && { style: { width } })}\r\n >\r\n {MODE_ORDER.map((m) => (\r\n <Option key={m} value={m} text={optionLabel(m)}>\r\n {optionLabel(m)}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </Field>\r\n );\r\n}\r\n","import {\r\n Dropdown,\r\n Field,\r\n Option,\r\n makeStyles,\r\n tokens,\r\n type DropdownProps,\r\n} from '@fluentui/react-components';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { useCallback, useId } from 'react';\r\nimport { useTheme, type AccentColor } from './theme-context.js';\r\n\r\nconst useStyles = makeStyles({\r\n // Color swatch rendered as the first child of each `<Option>`. Sized\r\n // to align with single-line text (14px ≈ text height at the dropdown's\r\n // default font size). The neutral-stroke border keeps a very light\r\n // accent (e.g. PowerBI yellow) visible against the dropdown panel's\r\n // white background.\r\n swatch: {\r\n display: 'inline-block',\r\n width: '14px',\r\n height: '14px',\r\n borderRadius: tokens.borderRadiusSmall,\r\n marginRight: tokens.spacingHorizontalS,\r\n verticalAlign: 'middle',\r\n border: `1px solid ${tokens.colorNeutralStroke1}`,\r\n flexShrink: 0,\r\n },\r\n});\r\n\r\nexport interface ThemeColorSelectorProps {\r\n /** Disable the control. Useful for read-only preference views. */\r\n readOnly?: boolean;\r\n /**\r\n * Override the displayed label. Defaults to the localized\r\n * `app.components.theme-color-selector.color-label` string.\r\n */\r\n label?: string;\r\n /**\r\n * Forwarded to Fluent's Dropdown via inline `style`. Pass\r\n * `'100%'` for full-width layouts.\r\n */\r\n width?: string;\r\n /**\r\n * Optional explicit display labels per accent name. When provided\r\n * and contains an entry for an accent, that string is used directly\r\n * — no localization lookup. Useful for re-routing labels through a\r\n * different localization namespace without modifying the bundle.\r\n * Accents NOT in the map fall through to the default localization\r\n * lookup (`app.components.theme-color-selector.accent.<accent>`),\r\n * then to the bare accent name.\r\n */\r\n accentLabels?: Readonly<Record<string, string>>;\r\n}\r\n\r\n/**\r\n * Dropdown that lets the user pick the active accent color. Mirrors\r\n * Blazor's `<ThemeColorSelector>`. Reads / writes the preference via\r\n * {@link useTheme} — wrap in `<PowerPortalsProThemeProvider>` for it\r\n * to function.\r\n *\r\n * Built-ins are `'default'` (Fluent v9 Communication Blue) and\r\n * `'teams'` (Teams Purple). Consumer-registered customs via\r\n * `<PowerPortalsProThemeProvider customAccents={...}>` appear in the\r\n * dropdown automatically — no per-call configuration required.\r\n *\r\n * Option labels resolve via the localizer:\r\n * `app.components.theme-color-selector.accent.<key>`. Missing keys\r\n * fall back to the accent's raw string (so an unlocalized custom\r\n * accent like `'forest'` shows as `\"forest\"` rather than blank).\r\n */\r\nexport function ThemeColorSelector({\r\n readOnly,\r\n label,\r\n width,\r\n accentLabels,\r\n}: ThemeColorSelectorProps) {\r\n const { accentColor, setAccentColor, availableAccents, accentColors } = useTheme();\r\n const t = useT();\r\n const labelId = useId();\r\n const styles = useStyles();\r\n\r\n const optionLabel = useCallback(\r\n (accent: AccentColor) => {\r\n // Explicit consumer-supplied label wins. Consumers use this to\r\n // route labels through a non-default localization namespace\r\n // (e.g. an existing Blazor framework's per-OfficeColor strings).\r\n const override = accentLabels?.[accent];\r\n if (override !== undefined) return override;\r\n const key = `app.components.theme-color-selector.accent.${accent}`;\r\n const resolved = t(key);\r\n // The localizer returns the key string unchanged when no match\r\n // is found — detect that and surface the bare accent name so a\r\n // custom (unlocalized) accent isn't displayed as a dotted token\r\n // path. Matches how other selectors degrade gracefully.\r\n return resolved === key ? accent : resolved;\r\n },\r\n [t, accentLabels],\r\n );\r\n\r\n const handleOptionSelect: DropdownProps['onOptionSelect'] = (_event, data) => {\r\n const next = data.optionValue;\r\n if (next && availableAccents.includes(next)) {\r\n setAccentColor(next);\r\n }\r\n };\r\n\r\n return (\r\n <Field\r\n className=\"ppp-theme-color-selector\"\r\n label={label ?? t('app.components.theme-color-selector.color-label')}\r\n id={labelId}\r\n >\r\n <Dropdown\r\n aria-labelledby={labelId}\r\n value={optionLabel(accentColor)}\r\n selectedOptions={[accentColor]}\r\n onOptionSelect={handleOptionSelect}\r\n disabled={readOnly}\r\n {...(width && { style: { width } })}\r\n >\r\n {availableAccents.map((accent) => {\r\n const swatch = accentColors[accent];\r\n return (\r\n <Option key={accent} value={accent} text={optionLabel(accent)}>\r\n {swatch && (\r\n <span\r\n className={styles.swatch}\r\n style={{ backgroundColor: swatch }}\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n {optionLabel(accent)}\r\n </Option>\r\n );\r\n })}\r\n </Dropdown>\r\n </Field>\r\n );\r\n}\r\n","import {\r\n Dropdown,\r\n Field,\r\n Option,\r\n type DropdownProps,\r\n} from '@fluentui/react-components';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { useCallback, useId } from 'react';\r\nimport { TextDirection, useTheme } from './theme-context.js';\r\n\r\nexport interface ThemeTextDirectionProps {\r\n /** Disable the control. */\r\n readOnly?: boolean;\r\n /**\r\n * Override the displayed label. Defaults to the localized\r\n * `app.components.theme-text-direction.direction-label` string.\r\n */\r\n label?: string;\r\n /** Forwarded to Fluent's Dropdown via inline `style`. */\r\n width?: string;\r\n}\r\n\r\nconst DIRECTION_ORDER: readonly TextDirection[] = [\r\n TextDirection.Ltr,\r\n TextDirection.Rtl,\r\n];\r\n\r\n/**\r\n * Dropdown that lets the user pick the active text direction (LTR or\r\n * RTL). Mirrors Blazor's `<ThemeTextDirection>`. The choice flows\r\n * through `<PowerPortalsProThemeProvider>` into\r\n * `<FluentProvider dir>` which cascades to every Fluent v9\r\n * component below.\r\n *\r\n * Persisted to localStorage so the preference survives page reloads.\r\n */\r\nexport function ThemeTextDirection({\r\n readOnly,\r\n label,\r\n width,\r\n}: ThemeTextDirectionProps) {\r\n const { textDirection, setTextDirection } = useTheme();\r\n const t = useT();\r\n const labelId = useId();\r\n\r\n const optionLabel = useCallback(\r\n (dir: TextDirection) =>\r\n t(`app.components.theme-text-direction.direction.${dir}`),\r\n [t],\r\n );\r\n\r\n const handleOptionSelect: DropdownProps['onOptionSelect'] = (_event, data) => {\r\n const next = data.optionValue as TextDirection | undefined;\r\n if (next === TextDirection.Ltr || next === TextDirection.Rtl) {\r\n setTextDirection(next);\r\n }\r\n };\r\n\r\n return (\r\n <Field\r\n className=\"ppp-theme-text-direction\"\r\n label={label ?? t('app.components.theme-text-direction.direction-label')}\r\n id={labelId}\r\n >\r\n <Dropdown\r\n aria-labelledby={labelId}\r\n value={optionLabel(textDirection)}\r\n selectedOptions={[textDirection]}\r\n onOptionSelect={handleOptionSelect}\r\n disabled={readOnly}\r\n {...(width && { style: { width } })}\r\n >\r\n {DIRECTION_ORDER.map((dir) => (\r\n <Option key={dir} value={dir} text={optionLabel(dir)}>\r\n {optionLabel(dir)}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </Field>\r\n );\r\n}\r\n","import {\r\n Dropdown,\r\n Field,\r\n mergeClasses,\r\n Option,\r\n type DropdownProps,\r\n} from '@fluentui/react-components';\r\nimport {\r\n QueryStatus,\r\n setAspNetCultureCookie,\r\n useLocale,\r\n useSupportedLocales,\r\n useT,\r\n} from '@powerportalspro/react';\r\nimport { useCallback, useId, useMemo } from 'react';\r\n\r\nexport interface LanguageDropdownProps {\r\n /**\r\n * Locales available in the dropdown. Each entry is a primary-tag\r\n * BCP-47 code (e.g. `'en'`, `'es'`, `'fr'`). Optional — when\r\n * omitted, the dropdown auto-discovers the list via\r\n * {@link useSupportedLocales}, which calls the framework's\r\n * `/localizations/version` endpoint to read whatever the server\r\n * has configured in `RequestLocalizationOptions.SupportedUICultures`.\r\n * Pass an explicit array to override (e.g. to expose a subset of\r\n * the server-supported locales in a specific UI surface).\r\n */\r\n supportedLocales?: readonly string[];\r\n /**\r\n * Override the displayed label. Defaults to the localized\r\n * `app.language-label` string.\r\n */\r\n label?: string;\r\n /**\r\n * When `true` (default), each option label shows the locale's native\r\n * name plus its English name in parens, e.g. \"Español (Spanish)\".\r\n * Set `false` to show only the English name. Falls back to the raw\r\n * locale code when the runtime's `Intl.DisplayNames` can't resolve\r\n * one (older browsers, unrecognized locales).\r\n */\r\n displayNativeLanguage?: boolean;\r\n /**\r\n * Forwarded to Fluent's Dropdown via inline `style`. Pass `'100%'`\r\n * for full-width layouts.\r\n */\r\n width?: string;\r\n /** Disable the control. */\r\n readOnly?: boolean;\r\n /**\r\n * Persist the chosen locale to the ASP.NET culture cookie\r\n * (`.AspNetCore.Culture`) so subsequent server-rendered requests\r\n * pick it up too. Default `true` — writes the same cookie the\r\n * `/Culture/{culture}` endpoint sets.\r\n */\r\n persistToCookie?: boolean;\r\n}\r\n\r\n/**\r\n * Resolves the display name for a given locale, optionally including\r\n * the English name in parens. Uses `Intl.DisplayNames` which is\r\n * available in all modern browsers; falls back to the raw locale\r\n * string when the runtime can't resolve a display name.\r\n */\r\nfunction resolveDisplayName(\r\n locale: string,\r\n displayNative: boolean,\r\n): string {\r\n if (typeof Intl === 'undefined' || typeof Intl.DisplayNames === 'undefined') {\r\n return locale;\r\n }\r\n try {\r\n const native = new Intl.DisplayNames([locale], { type: 'language' }).of(locale);\r\n if (!displayNative) {\r\n const english = new Intl.DisplayNames(['en'], { type: 'language' }).of(locale);\r\n return english ?? locale;\r\n }\r\n const english = new Intl.DisplayNames(['en'], { type: 'language' }).of(locale);\r\n if (!native) return english ?? locale;\r\n if (!english || english === native) return native;\r\n return `${native} (${english})`;\r\n } catch {\r\n return locale;\r\n }\r\n}\r\n\r\n/**\r\n * Dropdown that lets the user pick the active UI locale. Mirrors\r\n * Blazor's `<LanguageDropdown>` — selecting an option calls\r\n * `setLocale()` from `useLocale()` and (by default) writes the ASP.NET\r\n * culture cookie so server-rendered requests pick up the same value.\r\n *\r\n * Pass `supportedLocales` to populate the option list. Without it, the\r\n * dropdown shows only the current locale and is effectively read-only.\r\n *\r\n * Unlike Blazor, this does NOT trigger a full-page navigation on\r\n * change — the React app updates in place via the React-side\r\n * `setLocale`. If you need a hard reload (e.g. the host page has\r\n * server-rendered shells that need to re-emit with the new culture),\r\n * subscribe to the cookie change separately or call\r\n * `window.location.reload()` from a wrapper.\r\n */\r\nexport function LanguageDropdown({\r\n supportedLocales,\r\n label,\r\n displayNativeLanguage = true,\r\n width,\r\n readOnly,\r\n persistToCookie = true,\r\n}: LanguageDropdownProps) {\r\n const { locale, setLocale } = useLocale();\r\n const t = useT();\r\n const labelId = useId();\r\n\r\n // Auto-discover supported locales from the framework's manifest when\r\n // the consumer didn't supply an explicit list. Skip the fetch\r\n // entirely when an override is provided — no need to round-trip the\r\n // server just to ignore the result.\r\n const discovered = useSupportedLocales({ enabled: supportedLocales === undefined });\r\n const effectiveLocales =\r\n supportedLocales ?? (discovered.status === QueryStatus.Success ? discovered.data : null);\r\n\r\n // Build the option list. Always include the active locale so the\r\n // dropdown has something to display even when no list is resolved\r\n // yet (initial render before the manifest lands, or the active\r\n // locale isn't in the consumer-supplied list — handle the unusual\r\n // rather than break the UI).\r\n const options = useMemo(() => {\r\n const list = effectiveLocales ? [...effectiveLocales] : [locale];\r\n if (!list.includes(locale)) list.push(locale);\r\n return list\r\n .map((value) => ({\r\n value,\r\n label: resolveDisplayName(value, displayNativeLanguage),\r\n }))\r\n .sort((a, b) => a.label.localeCompare(b.label));\r\n }, [effectiveLocales, locale, displayNativeLanguage]);\r\n\r\n const handleOptionSelect = useCallback<NonNullable<DropdownProps['onOptionSelect']>>(\r\n (_event, data) => {\r\n const next = data.optionValue;\r\n if (!next || next === locale) return;\r\n setLocale(next);\r\n if (persistToCookie) setAspNetCultureCookie(next);\r\n },\r\n [locale, setLocale, persistToCookie],\r\n );\r\n\r\n const currentLabel =\r\n options.find((o) => o.value === locale)?.label ??\r\n resolveDisplayName(locale, displayNativeLanguage);\r\n\r\n return (\r\n <Field\r\n className={mergeClasses('ppp-language-dropdown')}\r\n label={label ?? t('app.language-label')}\r\n id={labelId}\r\n >\r\n <Dropdown\r\n aria-labelledby={labelId}\r\n value={currentLabel}\r\n selectedOptions={[locale]}\r\n onOptionSelect={handleOptionSelect}\r\n disabled={readOnly || options.length <= 1}\r\n {...(width && { style: { width } })}\r\n >\r\n {options.map((option) => (\r\n <Option key={option.value} value={option.value} text={option.label}>\r\n {option.label}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </Field>\r\n );\r\n}\r\n","import {\r\n Avatar,\r\n Button,\r\n Divider,\r\n Menu,\r\n MenuItem,\r\n MenuList,\r\n MenuPopover,\r\n MenuTrigger,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport {\r\n ArrowExit20Regular,\r\n ArrowSwap20Regular,\r\n PersonEdit20Regular,\r\n SignOut20Regular,\r\n} from '@fluentui/react-icons';\r\nimport { SwitchIdentityResult } from '@powerportalspro/core';\r\nimport { AuthStatus, useAuth, useT } from '@powerportalspro/react';\r\nimport { useCallback, type ReactNode } from 'react';\r\n\r\nconst useStyles = makeStyles({\r\n trigger: {\r\n backgroundColor: 'transparent',\r\n border: 'none',\r\n padding: 0,\r\n cursor: 'pointer',\r\n borderRadius: '50%',\r\n // Keep the focus ring snug around the avatar without the\r\n // default Fluent button-style padding distorting the circular\r\n // hit-target.\r\n ':focus-visible': {\r\n outline: `2px solid ${tokens.colorStrokeFocus2}`,\r\n outlineOffset: '2px',\r\n },\r\n },\r\n popoverBody: {\r\n minWidth: '260px',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalS,\r\n padding: tokens.spacingVerticalM,\r\n },\r\n identity: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalM,\r\n },\r\n identityText: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n minWidth: 0,\r\n },\r\n identityLabel: {\r\n color: tokens.colorNeutralForeground3,\r\n fontSize: tokens.fontSizeBase200,\r\n },\r\n identityValue: {\r\n color: tokens.colorNeutralForeground1,\r\n fontSize: tokens.fontSizeBase300,\r\n fontWeight: tokens.fontWeightSemibold,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n },\r\n actions: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalXS,\r\n },\r\n});\r\n\r\nexport interface ProfileMainMenuProps {\r\n /**\r\n * Optional URL to surface as the \"Account\" / \"Manage\" link in the\r\n * popover. Rendered as a plain anchor — the consumer's router (or a\r\n * server-side handler) takes over from there. When omitted, the\r\n * link is hidden.\r\n */\r\n accountUrl?: string;\r\n /**\r\n * Optional label override for the Account link. Defaults to the\r\n * localized `app.components.profile-main-menu.account` string.\r\n */\r\n accountLabel?: string;\r\n /**\r\n * Optional extra content rendered at the top of the popover body,\r\n * above the identity row. Useful for tenant pickers, role badges,\r\n * or app-specific quick links.\r\n */\r\n headerSlot?: ReactNode;\r\n /**\r\n * Optional extra content rendered below the action buttons. Use\r\n * this to surface app-specific account links (e.g. \"Billing\",\r\n * \"API keys\") without forking the component.\r\n */\r\n footerSlot?: ReactNode;\r\n /**\r\n * Rendered when the user is anonymous. Receives nothing — wire to\r\n * your router for sign-in / register links. When omitted, the menu\r\n * renders a single \"Sign in\" button that calls `useAuth().login` —\r\n * useful for apps with a separate `/login` page rather than an\r\n * inline form.\r\n */\r\n anonymousContent?: ReactNode;\r\n /**\r\n * Sign-in URL used by the default `anonymousContent` (skipped when\r\n * `anonymousContent` is supplied). Defaults to `'/login'`.\r\n */\r\n signInUrl?: string;\r\n}\r\n\r\n/**\r\n * Avatar-triggered popover with the signed-in user's identity + a\r\n * logout action, plus an optional Account link and anonymous-state\r\n * UI. Mirrors Blazor's `<ProfileMainMenu>`.\r\n *\r\n * When the signed-in user has a sibling Dataverse identity registered\r\n * (a contact↔systemuser pair — the framework's `UserManager.GetClaimsAsync`\r\n * surfaces this via `PortalClaimTypes.AltIdentity*`, which the React\r\n * provider exposes as `auth.user.altIdentityTableName`), the popover renders\r\n * an additional \"Switch to …\" menu item that calls\r\n * `auth.switchIdentity()` and reloads the page.\r\n *\r\n * Reads auth state from `useAuth()`. Wrap the app in\r\n * `<PowerPortalsProProvider>` for it to function.\r\n *\r\n * Three render branches:\r\n * - `loading`: renders a placeholder avatar with no popover so the\r\n * layout doesn't reflow when the auth call lands.\r\n * - `authenticated`: avatar with the user's initials → popover with\r\n * identity + (optional) switch-identity + logout + optional Account link.\r\n * - `anonymous`: a \"Sign in\" button (or consumer-supplied\r\n * `anonymousContent`).\r\n */\r\nexport function ProfileMainMenu({\r\n accountUrl,\r\n accountLabel,\r\n headerSlot,\r\n footerSlot,\r\n anonymousContent,\r\n signInUrl = '/login',\r\n}: ProfileMainMenuProps) {\r\n const auth = useAuth();\r\n const styles = useStyles();\r\n const t = useT();\r\n\r\n const handleLogout = useCallback(async () => {\r\n try {\r\n await auth.logout();\r\n } catch {\r\n // Logout is fire-and-forget from the UI perspective — the auth\r\n // state transitions to `anonymous` on success, and any network\r\n // failure leaves the cookie in place (server-side, not our\r\n // problem to recover from here).\r\n }\r\n }, [auth]);\r\n\r\n const handleSwitchIdentity = useCallback(async () => {\r\n try {\r\n const response = await auth.switchIdentity();\r\n if (response.result === SwitchIdentityResult.Switched) {\r\n // A full page reload matches Blazor's `data-enhance-nav=\"false\"`\r\n // form-post behavior: the cookie swap happens server-side, and\r\n // role-based UI (hidden routes, conditional menu items) needs a\r\n // fresh principal on the next render. `refreshAuth` (already\r\n // called inside `switchIdentity`) updates `auth.user` but doesn't\r\n // re-render any consumer that captured the old principal at\r\n // mount time — a reload guarantees a clean slate.\r\n window.location.reload();\r\n }\r\n } catch {\r\n // Mirrors handleLogout — network failures leave the existing\r\n // cookie + state in place; the user can retry. Surface non-success\r\n // results (NoAltIdentity, AltIdentityNotFound) via the response\r\n // object if a caller wires a toast, but the default UX is silent.\r\n }\r\n }, [auth]);\r\n\r\n if (auth.status === AuthStatus.Loading) {\r\n // Render a placeholder so the layout doesn't shift when the\r\n // auth call resolves. Same dimensions as the real avatar so the\r\n // header stays stable.\r\n return <Avatar className=\"ppp-profile-main-menu\" size={28} aria-label={t('app.messages.record-loading')} />;\r\n }\r\n\r\n if (auth.status === AuthStatus.Anonymous) {\r\n if (anonymousContent !== undefined) return <>{anonymousContent}</>;\r\n return (\r\n <Button\r\n as=\"a\"\r\n href={signInUrl}\r\n appearance=\"subtle\"\r\n icon={<ArrowExit20Regular />}\r\n className=\"ppp-profile-main-menu\"\r\n >\r\n {t('app.buttons.login.label')}\r\n </Button>\r\n );\r\n }\r\n\r\n const email = auth.user.email ?? auth.user.userName ?? '';\r\n const initials = email.length > 0 ? email[0]!.toUpperCase() : '?';\r\n const resolvedAccountLabel =\r\n accountLabel ?? t('app.components.profile-main-menu.account');\r\n const signedInAsLabel = t('app.components.profile-main-menu.signed-in-as');\r\n const logoutLabel = t('app.buttons.logout.label');\r\n\r\n // Switch-identity menu item is gated on the alt-identity table-name\r\n // claim being present on the principal. The value drives the localized\r\n // label (\"Switch to <Internal user>\" vs \"Switch to <Contact>\") — same\r\n // key pair Blazor's ProfileMainMenu uses, so server-side localization\r\n // JSON covers both surfaces without duplication.\r\n const altIdentityTableName = auth.user.altIdentityTableName;\r\n const switchIdentityLabel = altIdentityTableName\r\n ? t(\r\n 'app.components.profile-main-menu.switch-to',\r\n [\r\n altIdentityTableName === 'systemuser'\r\n ? t('app.components.profile-main-menu.alt-kind-systemuser')\r\n : t('app.components.profile-main-menu.alt-kind-contact'),\r\n ],\r\n )\r\n : null;\r\n\r\n return (\r\n <Menu>\r\n <MenuTrigger disableButtonEnhancement>\r\n <button\r\n type=\"button\"\r\n className={mergeClasses('ppp-profile-main-menu', styles.trigger)}\r\n aria-label={email || logoutLabel}\r\n >\r\n <Avatar size={28} initials={initials} name={email} />\r\n </button>\r\n </MenuTrigger>\r\n <MenuPopover>\r\n <div className={styles.popoverBody}>\r\n {headerSlot}\r\n <div className={styles.identity}>\r\n <Avatar size={40} initials={initials} name={email} />\r\n <div className={styles.identityText}>\r\n <span className={styles.identityLabel}>{signedInAsLabel}</span>\r\n <span className={styles.identityValue} title={email}>\r\n {email}\r\n </span>\r\n </div>\r\n </div>\r\n <Divider />\r\n <MenuList className={styles.actions}>\r\n {accountUrl && (\r\n <MenuItem\r\n icon={<PersonEdit20Regular />}\r\n onClick={() => {\r\n // Plain navigation — consumers using a client-side\r\n // router can hook into route changes if they need\r\n // to; the menu doesn't take a router dependency.\r\n window.location.href = accountUrl;\r\n }}\r\n >\r\n {resolvedAccountLabel}\r\n </MenuItem>\r\n )}\r\n {switchIdentityLabel && (\r\n <MenuItem\r\n icon={<ArrowSwap20Regular />}\r\n onClick={handleSwitchIdentity}\r\n >\r\n {switchIdentityLabel}\r\n </MenuItem>\r\n )}\r\n <MenuItem icon={<SignOut20Regular />} onClick={handleLogout}>\r\n {logoutLabel}\r\n </MenuItem>\r\n </MenuList>\r\n {footerSlot && (\r\n <>\r\n <Divider />\r\n {footerSlot}\r\n </>\r\n )}\r\n </div>\r\n </MenuPopover>\r\n </Menu>\r\n );\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport type { CSSProperties } from 'react';\r\nimport { LanguageDropdown } from '../editors/language-dropdown.js';\r\nimport { ThemeColorSelector } from '../theme/theme-color-selector.js';\r\nimport { ThemeSelector } from '../theme/theme-selector.js';\r\nimport { ThemeTextDirection } from '../theme/theme-text-direction.js';\r\n\r\nconst usePanelStyles = makeStyles({\r\n // Vertical stack of setting sections with consistent gap. Matches the\r\n // panel body the SiteSettingsButton's drawer renders, but extracted\r\n // so consumers can mount it elsewhere (a dedicated /settings page, a\r\n // tab inside profile-management, an embedded preferences card).\r\n body: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalL,\r\n },\r\n});\r\n\r\nexport interface SiteSettingsPanelProps {\r\n /**\r\n * When `true` (default), the panel renders the language dropdown.\r\n * Set `false` to hide it — useful for single-locale deployments\r\n * where switching would just produce errors.\r\n */\r\n allowLanguageChange?: boolean;\r\n /** When `true` (default), the panel renders the theme selector. */\r\n allowThemeChange?: boolean;\r\n /**\r\n * When `true` (default), the panel renders the accent color selector.\r\n * Set `false` to hide it (e.g. when the app brand is locked and\r\n * accent customization isn't desired).\r\n */\r\n allowColorChange?: boolean;\r\n /**\r\n * When `true`, the panel renders the LTR/RTL direction selector.\r\n * Defaults to `false` — RTL isn't a common need and showing the\r\n * dropdown without RTL content can confuse users.\r\n */\r\n allowRightToLeftChange?: boolean;\r\n /**\r\n * Locales offered by the embedded `<LanguageDropdown>`. Forwarded as\r\n * that component's `supportedLocales` prop.\r\n */\r\n supportedLocales?: readonly string[];\r\n /**\r\n * Forwarded to the inner `<ThemeColorSelector>`. See its\r\n * `accentLabels` prop for semantics — supply explicit per-accent\r\n * display labels when you want to route them through a different\r\n * localization namespace than the default\r\n * `app.components.theme-color-selector.accent.<accent>` lookup.\r\n */\r\n accentLabels?: Readonly<Record<string, string>>;\r\n /** Optional class on the panel's outer container. */\r\n className?: string;\r\n /** Optional inline style on the panel's outer container. */\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Stand-alone panel that bundles the language / theme / accent-color /\r\n * text-direction site-preference controls. Mirrors Blazor's\r\n * `<SiteSettingsPanel>` — same configurable sections, just mounted\r\n * directly into your layout instead of inside the gear-icon drawer.\r\n *\r\n * `<SiteSettingsButton>` renders this same panel inside its\r\n * `<OverlayDrawer>` popover; the standalone `<SiteSettingsPanel>` is\r\n * for surfaces that want the controls visible at all times (a\r\n * dedicated /settings page, a preferences tab inside an account\r\n * dashboard, an embedded sidebar widget).\r\n *\r\n * Each section is gated by its `allow…` prop with the same defaults as\r\n * the button (language + theme + color on, RTL off). When all four\r\n * sections are off the panel renders an empty container — same\r\n * behavior as the button (which hides itself entirely in that case).\r\n *\r\n * ```tsx\r\n * // dedicated /settings page\r\n * <PageLayout\r\n * header={<Title2>Preferences</Title2>}\r\n * body={\r\n * <SiteSettingsPanel\r\n * allowRightToLeftChange\r\n * supportedLocales={['en', 'es', 'fr']}\r\n * />\r\n * }\r\n * />\r\n * ```\r\n */\r\nexport function SiteSettingsPanel({\r\n allowLanguageChange = true,\r\n allowThemeChange = true,\r\n allowColorChange = true,\r\n allowRightToLeftChange = false,\r\n supportedLocales,\r\n accentLabels,\r\n className,\r\n style,\r\n}: SiteSettingsPanelProps) {\r\n const styles = usePanelStyles();\r\n return (\r\n <div\r\n className={mergeClasses('ppp-site-settings-panel', styles.body, className)}\r\n style={style}\r\n >\r\n {allowLanguageChange && (\r\n <LanguageDropdown\r\n {...(supportedLocales && { supportedLocales })}\r\n width=\"100%\"\r\n />\r\n )}\r\n {allowThemeChange && <ThemeSelector width=\"100%\" />}\r\n {allowColorChange && (\r\n <ThemeColorSelector\r\n width=\"100%\"\r\n {...(accentLabels && { accentLabels })}\r\n />\r\n )}\r\n {allowRightToLeftChange && <ThemeTextDirection width=\"100%\" />}\r\n </div>\r\n );\r\n}\r\n","import {\r\n Button,\r\n DrawerBody,\r\n DrawerHeader,\r\n DrawerHeaderTitle,\r\n OverlayDrawer,\r\n Tooltip,\r\n makeStyles,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { Dismiss24Regular, Settings20Regular } from '@fluentui/react-icons';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { useCallback, useState } from 'react';\r\nimport { SiteSettingsPanel } from './site-settings-panel.js';\r\n\r\nconst useStyles = makeStyles({\r\n // Wraps the standalone panel inside the drawer body to add the small\r\n // top inset that the popover's title visually expects. The panel\r\n // itself stays inset-free so it doesn't carry extra padding when\r\n // mounted elsewhere (a dedicated /settings page, a tab in an account\r\n // dashboard).\r\n body: {\r\n paddingTop: tokens.spacingVerticalM,\r\n },\r\n});\r\n\r\nexport interface SiteSettingsButtonProps {\r\n /**\r\n * When `true` (default), the panel renders the language dropdown.\r\n * Set `false` to hide it — useful for single-locale deployments\r\n * where switching would just produce errors.\r\n */\r\n allowLanguageChange?: boolean;\r\n /**\r\n * When `true` (default), the panel renders the theme selector.\r\n */\r\n allowThemeChange?: boolean;\r\n /**\r\n * When `true` (default), the panel renders the accent color\r\n * selector. Set `false` to hide it (e.g. when the app brand is\r\n * locked and accent customization isn't desired).\r\n */\r\n allowColorChange?: boolean;\r\n /**\r\n * When `true`, the panel renders the LTR/RTL direction selector.\r\n * Defaults to `false` — RTL isn't a common need and showing the\r\n * dropdown without RTL content can confuse users.\r\n */\r\n allowRightToLeftChange?: boolean;\r\n /**\r\n * Locales offered by the embedded `<LanguageDropdown>`. Forwarded\r\n * as that component's `supportedLocales` prop.\r\n */\r\n supportedLocales?: readonly string[];\r\n /** Forwarded to the trigger button's appearance. Defaults to `\"subtle\"`. */\r\n buttonAppearance?: 'subtle' | 'transparent' | 'outline' | 'primary' | 'secondary';\r\n /**\r\n * Forwarded to the inner `<ThemeColorSelector>`. See its\r\n * `accentLabels` prop for semantics — supply explicit per-accent\r\n * display labels when you want to route them through a different\r\n * localization namespace than the default\r\n * `app.components.theme-color-selector.accent.<accent>` lookup.\r\n */\r\n accentLabels?: Readonly<Record<string, string>>;\r\n}\r\n\r\n/**\r\n * Gear-icon button that opens a side drawer with the user's site\r\n * preferences (language + theme). Mirrors Blazor's\r\n * `<SiteSettingsButton>` / `<SiteSettingsPanel>` pair, condensed into\r\n * a single React component since the drawer state is local.\r\n *\r\n * The drawer renders into the document via Fluent v9's\r\n * `<OverlayDrawer>` so it sits on top of every other content layer\r\n * without needing a portal wrapper.\r\n *\r\n * ```tsx\r\n * <header>\r\n * <Logo />\r\n * <SiteSettingsButton supportedLocales={['en', 'es', 'fr']} />\r\n * <ProfileMainMenu accountUrl=\"/account/manage\" />\r\n * </header>\r\n * ```\r\n */\r\nexport function SiteSettingsButton({\r\n allowLanguageChange = true,\r\n allowThemeChange = true,\r\n allowColorChange = true,\r\n allowRightToLeftChange = false,\r\n supportedLocales,\r\n buttonAppearance = 'subtle',\r\n accentLabels,\r\n}: SiteSettingsButtonProps) {\r\n const t = useT();\r\n const styles = useStyles();\r\n const [open, setOpen] = useState(false);\r\n\r\n const close = useCallback(() => setOpen(false), []);\r\n const label = t('app.site-settings-label');\r\n const panelTitle = t('app.components.site-settings-panel.label');\r\n\r\n return (\r\n <>\r\n <Tooltip content={label} relationship=\"label\">\r\n <Button\r\n type=\"button\"\r\n appearance={buttonAppearance}\r\n icon={<Settings20Regular />}\r\n aria-label={label}\r\n onClick={() => setOpen(true)}\r\n className=\"ppp-site-settings-button\"\r\n />\r\n </Tooltip>\r\n <OverlayDrawer\r\n position=\"end\"\r\n open={open}\r\n onOpenChange={(_, data) => setOpen(data.open)}\r\n >\r\n <DrawerHeader>\r\n <DrawerHeaderTitle\r\n action={\r\n <Button\r\n appearance=\"subtle\"\r\n aria-label={t('app.buttons.cancel.label')}\r\n icon={<Dismiss24Regular />}\r\n onClick={close}\r\n />\r\n }\r\n >\r\n {panelTitle}\r\n </DrawerHeaderTitle>\r\n </DrawerHeader>\r\n <DrawerBody>\r\n <SiteSettingsPanel\r\n className={styles.body}\r\n allowLanguageChange={allowLanguageChange}\r\n allowThemeChange={allowThemeChange}\r\n allowColorChange={allowColorChange}\r\n allowRightToLeftChange={allowRightToLeftChange}\r\n {...(supportedLocales && { supportedLocales })}\r\n {...(accentLabels && { accentLabels })}\r\n />\r\n </DrawerBody>\r\n </OverlayDrawer>\r\n </>\r\n );\r\n}\r\n","import { Button, mergeClasses, type ButtonProps } from '@fluentui/react-components';\r\nimport { SignOut20Regular } from '@fluentui/react-icons';\r\nimport { useAuth, useT } from '@powerportalspro/react';\r\nimport type { ReactElement } from 'react';\r\nimport { useCallback } from 'react';\r\n\r\nexport interface LogoutButtonProps {\r\n /** Custom label override. Defaults to the localized `app.buttons.logout.label`. */\r\n label?: string;\r\n /**\r\n * Optional leading icon. Defaults to Fluent's `SignOut20Regular`. Pass\r\n * `null` to suppress entirely.\r\n */\r\n icon?: ReactElement | null;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `'subtle'`. */\r\n appearance?: ButtonProps['appearance'];\r\n /**\r\n * Hard override that forces the button into a disabled state. Useful\r\n * when the consumer wants to gate logout on some in-flight operation\r\n * (a pending save, an active dialog, etc.).\r\n */\r\n disabled?: boolean;\r\n /**\r\n * Fires after a successful logout, just before / after the auth state\r\n * transitions to anonymous. Useful for telemetry or for navigating to\r\n * a specific post-logout URL (the default behavior just lets\r\n * `useAuth().logout()` transition the cookie + state).\r\n */\r\n onLoggedOut?: () => void | Promise<void>;\r\n /**\r\n * Fires when `useAuth().logout()` throws. When provided, takes over\r\n * error display. When omitted, errors are swallowed silently —\r\n * matches the `ProfileMainMenu` baseline (server-side cookie still in\r\n * place on failure; user can retry).\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n /** Optional class on the rendered `<Button>`. */\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Standalone \"Sign out\" button. Mirrors Blazor's `<LogoutButton>`. Click\r\n * fires `useAuth().logout()` which clears the session cookie server-side\r\n * and transitions the React auth state to `anonymous`. Default styling\r\n * matches the logout entry inside `<ProfileMainMenu>`; the two can\r\n * coexist on the same page.\r\n *\r\n * Use this when you want logout reachable outside the profile dropdown\r\n * — e.g. a sticky footer action, a kebab on a row, or a dedicated\r\n * \"Account\" page that exposes logout alongside profile management.\r\n */\r\nexport function LogoutButton({\r\n label,\r\n icon,\r\n appearance = 'subtle',\r\n disabled,\r\n onLoggedOut,\r\n onError,\r\n className,\r\n}: LogoutButtonProps) {\r\n const auth = useAuth();\r\n const t = useT();\r\n const resolvedLabel = label ?? t('app.buttons.logout.label');\r\n const resolvedIcon = icon === undefined ? <SignOut20Regular /> : icon;\r\n\r\n const handleClick = useCallback(async () => {\r\n try {\r\n await auth.logout();\r\n if (onLoggedOut) await onLoggedOut();\r\n } catch (err) {\r\n if (onError) {\r\n await onError(err);\r\n }\r\n // Silent by default — see `onError` doc.\r\n }\r\n }, [auth, onLoggedOut, onError]);\r\n\r\n return (\r\n <Button\r\n type=\"button\"\r\n appearance={appearance}\r\n disabled={disabled === true}\r\n onClick={handleClick}\r\n {...(resolvedIcon !== null && { icon: resolvedIcon })}\r\n className={mergeClasses('ppp-logout-button', className)}\r\n >\r\n {resolvedLabel}\r\n </Button>\r\n );\r\n}\r\n","import { makeStaticStyles, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport {\r\n Nav,\r\n NavCategory,\r\n NavCategoryItem,\r\n NavSubItem,\r\n NavSubItemGroup,\r\n type OnNavItemSelectData,\r\n} from '@fluentui/react-nav';\r\nimport {\r\n Children,\r\n isValidElement,\r\n useMemo,\r\n type CSSProperties,\r\n type JSX,\r\n type MouseEvent as ReactMouseEvent,\r\n type ReactElement,\r\n type ReactNode,\r\n} from 'react';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\n\r\n// =============================================================================\r\n// NavMenu — side-navigation container. React equivalent of Blazor's\r\n// `<FluentNavMenu>`. Wraps Fluent UI 9's `<Nav>` (from `@fluentui/react-nav`)\r\n// and wires up router-aware active-link highlighting + react-router navigation.\r\n// Uses `<Nav>` rather than `<NavDrawer>` because we want the always-visible\r\n// inline shape (no slide-out / open-close ceremony) — `<NavDrawer>` is the\r\n// drawer-style wrapper for collapsible / mobile scenarios.\r\n//\r\n// API mirrors Blazor's nav: groups of links, each link with an icon + label,\r\n// auth-aware gating via `<AuthorizedView>` from `@powerportalspro/react`.\r\n//\r\n// @example\r\n// ```tsx\r\n// <NavMenu>\r\n// <NavGroup label=\"Main Menu\" icon={<HomeRegular />} defaultExpanded>\r\n// <NavItem to=\"/Dashboard\" icon={<DataBarVerticalRegular />}>Dashboard</NavItem>\r\n// <NavItem to=\"/Accounts\" icon={<BuildingRegular />}>Accounts</NavItem>\r\n// </NavGroup>\r\n// <AuthorizedView>\r\n// <NavGroup label=\"Account\" icon={<PersonAccountsRegular />}>\r\n// <NavItem to=\"/Account/Manage\" icon={<PersonRegular />}>Manage account</NavItem>\r\n// </NavGroup>\r\n// </AuthorizedView>\r\n// <AuthorizedView roles=\"SystemAdmin\">\r\n// <NavGroup label=\"Admin\" icon={<WrenchSettingsRegular />}>\r\n// <NavItem to=\"/SiteAdmin\" icon={<LauncherSettingsRegular />}>Site admin</NavItem>\r\n// </NavGroup>\r\n// </AuthorizedView>\r\n// </NavMenu>\r\n// ```\r\n// =============================================================================\r\n\r\nconst useNavMenuStyles = makeStyles({\r\n // Width + border so the side rail sits next to the body in PageLayout's\r\n // navigation slot. Width default matches Blazor's FluentNavMenu (250px).\r\n //\r\n // Background is intentionally NOT set here — `PageLayout`'s\r\n // `navigation` grid-area paints the rail's background so the colour\r\n // covers the entire vertical extent of the side band regardless of\r\n // how short the actual nav content is. Painting it on the `<Nav>`\r\n // element instead left a hard horizontal edge mid-page in dark mode\r\n // (the painted region ended where the last `<NavItem>` ended, with\r\n // a different lighter colour bleeding through below).\r\n nav: {\r\n minWidth: '250px',\r\n maxWidth: '320px',\r\n height: '100%',\r\n borderRight: `1px solid ${tokens.colorNeutralStroke2}`,\r\n // Paint the rail directly on `<Nav>` so the full vertical extent\r\n // of the side band is uniform. `colorNeutralBackground4` matches the\r\n // background tone Fluent's `<NavSubItem>` ships with — the rail's\r\n // empty area and its items now share one continuous surface\r\n // instead of showing a horizontal seam between Fluent's default\r\n // outer-Nav background and the inner-item background.\r\n backgroundColor: tokens.colorNeutralBackground4,\r\n },\r\n // Inline icon for sub-items. Fluent UI 9's <NavSubItem> doesn't have a\r\n // dedicated `icon` slot (sub-items are text-only in the Fluent design\r\n // language), so we render the icon as a leading inline-flex span inside\r\n // the link's content. Width is fixed so labels still align vertically\r\n // across rows whether their icon is present or not.\r\n subItemIcon: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: '20px',\r\n height: '20px',\r\n marginRight: tokens.spacingHorizontalS,\r\n verticalAlign: 'middle',\r\n color: tokens.colorNeutralForeground2,\r\n flexShrink: 0,\r\n },\r\n});\r\n\r\n// Dark-mode-only surface fix for the side nav. Fluent UI 9's `@fluentui/react-nav`\r\n// hardcodes its item buttons (and the rail, which we deliberately match) to the\r\n// `colorNeutralBackground4` family — which in the DARK theme is #0a0a0a, the same\r\n// near-black the page body uses. The buttons then read as solid black against the\r\n// rail rather than as distinct controls.\r\n//\r\n// Remap the Background4 family to the Background2 family for the nav subtree so the\r\n// rail + item buttons (rest / hover / pressed in lockstep) render as a dark grey\r\n// that's visibly lifted off the near-black body. Scoped to `body.ppp-theme-dark`\r\n// (the class `PowerPortalsProThemeProvider` toggles) so light mode keeps Fluent's\r\n// Background4 (a light grey there). Uses Fluent's own token CSS vars — no hardcoded\r\n// hex — so it still tracks accent / theme customization.\r\nconst useNavMenuDarkSurfaceStyles = makeStaticStyles({\r\n 'body.ppp-theme-dark .ppp-nav-menu': {\r\n '--colorNeutralBackground4': 'var(--colorNeutralBackground2)',\r\n '--colorNeutralBackground4Hover': 'var(--colorNeutralBackground2Hover)',\r\n '--colorNeutralBackground4Pressed': 'var(--colorNeutralBackground2Pressed)',\r\n },\r\n});\r\n\r\nexport type NavMatchMode = 'exact' | 'prefix';\r\n\r\nexport interface NavMenuProps {\r\n /** `<NavGroup>` children, each typically wrapping `<NavItem>` links. May be wrapped in `<AuthorizedView>` for auth-gated groups. */\r\n children: ReactNode;\r\n /** Side-rail width override. Defaults to `min: 250px, max: 320px`. */\r\n width?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\nexport interface NavGroupProps {\r\n /** Group title displayed on the category header. */\r\n label: string;\r\n /**\r\n * Optional icon for the category header. Typed as `JSX.Element` to match\r\n * Fluent UI 9's icon-slot shape — consumers pass an icon component instance\r\n * like `<HomeRegular />` from `@fluentui/react-icons`.\r\n */\r\n icon?: JSX.Element;\r\n /** Stable identifier for the category. Defaults to {@link label}. Override if two groups share the same label. */\r\n value?: string;\r\n /** When `true`, the group starts expanded. The currently-active item's group auto-expands regardless. */\r\n defaultExpanded?: boolean;\r\n /** `<NavItem>` children rendered inside the group. */\r\n children: ReactNode;\r\n}\r\n\r\nexport interface NavItemProps {\r\n /** Route path the item navigates to. Used as the item's Fluent `value` and matched against the current location to drive the active state. */\r\n to: string;\r\n /**\r\n * Optional leading icon for the link. Fluent UI 9's `<NavSubItem>` doesn't\r\n * have a dedicated icon slot, so we render this as an inline-flex span\r\n * before the label inside the link content, with a fixed-width icon\r\n * column so labels line up. Pass a Fluent icon component instance like\r\n * `<BuildingRegular />`.\r\n */\r\n icon?: JSX.Element;\r\n /**\r\n * How the current route is matched against {@link to}:\r\n * - `'exact'` (default for `to=\"/\"`): only when pathname equals `to`.\r\n * - `'prefix'` (default everywhere else): also when pathname starts with `to + '/'`,\r\n * so `/Accounts/Account/123` highlights `/Accounts`.\r\n */\r\n match?: NavMatchMode;\r\n /** Link label. */\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Side navigation menu — wraps Fluent UI 9's `<Nav>` with router-aware\r\n * active-link highlighting and react-router navigation.\r\n *\r\n * Walks {@link NavMenuProps.children} (descending through `<AuthorizedView>` /\r\n * other wrappers) to collect all `<NavItem>` `to` routes. The most-specific\r\n * match against the current pathname (longest-prefix wins) becomes the\r\n * Fluent `selectedValue`, which both highlights the item and auto-expands\r\n * its parent `<NavGroup>`.\r\n *\r\n * Selecting an item dispatches via react-router's `useNavigate()` — no full\r\n * page reload. Use `<a href=\"...\">` directly if you need a hard navigation.\r\n */\r\nexport function NavMenu({ children, width, className, style }: NavMenuProps) {\r\n const styles = useNavMenuStyles();\r\n useNavMenuDarkSurfaceStyles();\r\n const location = useLocation();\r\n const navigate = useNavigate();\r\n\r\n // Walk the static child tree once per render to find all NavItem routes +\r\n // their parent NavGroup values. Used to (a) pick the best-matching item to\r\n // pass as selectedValue, and (b) populate defaultOpenCategories from\r\n // groups whose `defaultExpanded` is true.\r\n const { allRoutes, defaultOpen } = useMemo(\r\n () => collectNavTree(children),\r\n [children],\r\n );\r\n\r\n const selectedValue = useMemo(\r\n () => bestRouteMatch(allRoutes, location.pathname),\r\n [allRoutes, location.pathname],\r\n );\r\n\r\n const handleSelect = (_: unknown, data: OnNavItemSelectData) => {\r\n const value = data.value;\r\n if (typeof value === 'string' && value.startsWith('/')) {\r\n navigate(value);\r\n }\r\n };\r\n\r\n const navStyle: CSSProperties | undefined = width\r\n ? { ...style, minWidth: width, maxWidth: width }\r\n : style;\r\n\r\n return (\r\n <Nav\r\n selectedValue={selectedValue ?? ''}\r\n onNavItemSelect={handleSelect}\r\n defaultOpenCategories={defaultOpen}\r\n className={mergeClasses('ppp-nav-menu', styles.nav, className)}\r\n style={navStyle}\r\n >\r\n {children}\r\n </Nav>\r\n );\r\n}\r\n\r\n/**\r\n * Group of `<NavItem>` links under a shared category header. Always renders\r\n * as a collapsible category in the underlying `<Nav>` — even with a single\r\n * child — so the chrome stays consistent across groups.\r\n */\r\nexport function NavGroup({ label, icon, value, children }: NavGroupProps) {\r\n // Fluent's `value` is what selection / open-state APIs key on. Defaulting\r\n // to the label is fine for unique labels; consumers with two groups sharing\r\n // a label can override via the `value` prop.\r\n const groupValue = value ?? `group:${label}`;\r\n // Branched JSX for the `icon` slot — `exactOptionalPropertyTypes: true`\r\n // rejects both `icon={undefined}` and a conditional spread that produces\r\n // `{ icon: ... } | {}`, because the union's empty branch's missing-`icon`\r\n // narrows the prop bag in a way the optional-slot typing won't accept.\r\n // Splitting the JSX gives each branch a cleanly-typed prop bag.\r\n return (\r\n <NavCategory value={groupValue}>\r\n {icon !== undefined\r\n ? <NavCategoryItem icon={icon}>{label}</NavCategoryItem>\r\n : <NavCategoryItem>{label}</NavCategoryItem>}\r\n <NavSubItemGroup>{children}</NavSubItemGroup>\r\n </NavCategory>\r\n );\r\n}\r\n\r\n/**\r\n * Single navigable link inside a `<NavGroup>`. The `to` prop becomes the\r\n * item's Fluent value AND the route it navigates to — so the parent\r\n * `<NavMenu>` can match it against the current location to drive active\r\n * styling.\r\n */\r\nexport function NavItem({ to, icon, children }: NavItemProps) {\r\n const styles = useNavMenuStyles();\r\n const navigate = useNavigate();\r\n // Intercept the click so the browser doesn't follow the `<a href>`'s\r\n // native navigation (which triggers a full page reload, wiping app\r\n // state — the bug that surfaced as \"DefaultLocalizerProvider re-\r\n // fetches everything on every nav\"). The parent `NavMenu`'s\r\n // `onNavItemSelect` callback ALSO calls `navigate()`, but that\r\n // fires AFTER the browser has already initiated its own navigation\r\n // — we need to preventDefault on the actual click event before the\r\n // browser sees it.\r\n //\r\n // `href={to}` stays on the `<a>` so accessibility tools see it as\r\n // a real link AND so right-click / cmd-click / middle-click \"Open\r\n // in new tab\" still works the way users expect. The guard below\r\n // lets those native paths through unchanged.\r\n const handleClick = (e: ReactMouseEvent<HTMLAnchorElement>) => {\r\n // Already handled by another listener — don't override.\r\n if (e.defaultPrevented) return;\r\n // Modifier keys / middle-click / right-click all mean \"the user\r\n // wants a new tab / context menu / etc.\" — let the browser do\r\n // its native thing.\r\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;\r\n e.preventDefault();\r\n navigate(to);\r\n };\r\n return (\r\n <NavSubItem as=\"a\" value={to} href={to} onClick={handleClick} className=\"ppp-nav-item\">\r\n {icon !== undefined && <span className={styles.subItemIcon}>{icon}</span>}\r\n {children}\r\n </NavSubItem>\r\n );\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Child-tree walking — collects all NavItem routes and defaultExpanded\r\n// NavGroup values, recursing through arbitrary wrappers (Fragment,\r\n// AuthorizedView, etc.) so the consumer can structure the tree freely.\r\n// -----------------------------------------------------------------------------\r\n\r\ninterface NavTreeInfo {\r\n allRoutes: Array<{ to: string; match: NavMatchMode }>;\r\n defaultOpen: string[];\r\n}\r\n\r\nfunction collectNavTree(children: ReactNode): NavTreeInfo {\r\n const allRoutes: Array<{ to: string; match: NavMatchMode }> = [];\r\n const defaultOpen: string[] = [];\r\n\r\n const visit = (nodes: ReactNode) => {\r\n Children.forEach(nodes, (child) => {\r\n if (!isValidElement(child)) return;\r\n\r\n if (child.type === NavGroup) {\r\n const props = child.props as NavGroupProps;\r\n const value = props.value ?? `group:${props.label}`;\r\n if (props.defaultExpanded) defaultOpen.push(value);\r\n visit(props.children);\r\n return;\r\n }\r\n\r\n if (child.type === NavItem) {\r\n const props = child.props as NavItemProps;\r\n allRoutes.push({\r\n to: props.to,\r\n match: props.match ?? (props.to === '/' ? 'exact' : 'prefix'),\r\n });\r\n return;\r\n }\r\n\r\n // Generic wrapper (Fragment, AuthorizedView, custom shell): recurse\r\n // into its children if present. Lets consumers freely compose without\r\n // breaking the route collection.\r\n const wrapperChildren = (child as ReactElement<{ children?: ReactNode }>).props\r\n .children;\r\n if (wrapperChildren !== undefined) visit(wrapperChildren);\r\n });\r\n };\r\n\r\n visit(children);\r\n return { allRoutes, defaultOpen };\r\n}\r\n\r\nfunction bestRouteMatch(\r\n routes: Array<{ to: string; match: NavMatchMode }>,\r\n pathname: string,\r\n): string | undefined {\r\n const normalize = (p: string) => (p.length > 1 && p.endsWith('/') ? p.slice(0, -1) : p);\r\n const target = normalize(pathname);\r\n let best: string | undefined;\r\n let bestLen = -1;\r\n for (const { to, match } of routes) {\r\n const route = normalize(to);\r\n const matches =\r\n route === target ||\r\n (match === 'prefix' && (route === '/' || target.startsWith(route + '/')));\r\n if (matches && route.length > bestLen) {\r\n best = to;\r\n bestLen = route.length;\r\n }\r\n }\r\n return best;\r\n}\r\n","import { Spinner, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport {\r\n OverlayServiceProvider,\r\n useOverlayForTarget,\r\n useOverlayServiceState,\r\n type OverlayInstance,\r\n} from '@powerportalspro/react';\r\nimport type { CSSProperties, ReactNode } from 'react';\r\n\r\nconst useStyles = makeStyles({\r\n // Shared scrim look. Both global (full-screen) and scoped variants reuse\r\n // these so the visual feel is identical — only the positioning differs.\r\n //\r\n // Theme-aware translucent backdrop drawn via a `::before` pseudo-element\r\n // so the spinner + description above stay at full opacity (the same\r\n // pattern the grid's refetch scrim uses, and a direct port of Blazor's\r\n // `OverlayWithProgress.razor.css`). `colorNeutralBackground2` matches\r\n // Blazor's `--neutral-layer-2` token and swaps with FluentDesignTheme's\r\n // light/dark state — light gray in light theme, near-black in dark\r\n // theme — so the overlay reads as a soft neutral tint instead of a\r\n // hardcoded black film over whatever's behind. `pointerEvents: 'auto'`\r\n // blocks accidental clicks on the dimmed content underneath.\r\n scrim: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n rowGap: tokens.spacingVerticalM,\r\n pointerEvents: 'auto',\r\n '::before': {\r\n content: '\"\"',\r\n position: 'absolute',\r\n inset: 0,\r\n backgroundColor: tokens.colorNeutralBackground2,\r\n opacity: 0.7,\r\n zIndex: -1,\r\n },\r\n },\r\n global: {\r\n position: 'fixed',\r\n inset: 0,\r\n // Above Fluent's Dialog backdrop (which sits ~1000) so the\r\n // overlay covers any open dialog when both happen simultaneously\r\n // (rare, but a save triggered from a dialog would land here).\r\n zIndex: 2000,\r\n },\r\n scoped: {\r\n position: 'absolute',\r\n inset: 0,\r\n // Below the global overlay so a global save during a scoped load\r\n // visually layers on top.\r\n zIndex: 100,\r\n },\r\n description: {\r\n // Foreground reads against `colorNeutralBackground2`, NOT a brand-\r\n // colored backdrop — so use the standard neutral foreground token\r\n // (was `colorNeutralForegroundOnBrand`, which is the white-on-blue\r\n // pairing that mismatched the new neutral scrim).\r\n color: tokens.colorNeutralForeground1,\r\n fontWeight: tokens.fontWeightSemibold,\r\n },\r\n scopedHost: {\r\n position: 'relative',\r\n },\r\n});\r\n\r\n/**\r\n * Fluent UI implementation of the {@link OverlayService}. Mount one of these\r\n * near the root of your app — descendants get the service via\r\n * `useOverlayService()` and the provider renders the active GLOBAL overlay\r\n * (full-screen) using a Fluent `Spinner` over a dimmed backdrop.\r\n *\r\n * Scoped overlays (those with a `targetId`) are NOT rendered here — they're\r\n * routed to a matching `<FluentOverlayTarget>` mounted inside whichever\r\n * subtree should host them. Mirrors Blazor's split between the global\r\n * `<OverlayProvider>` (no `TargetId`) and per-component `<OverlayProvider TargetId=\"…\">`.\r\n */\r\nexport function FluentOverlayProvider({ children }: { children: ReactNode }) {\r\n const ctxValue = useOverlayServiceState();\r\n return (\r\n <OverlayServiceProvider value={ctxValue}>\r\n {children}\r\n <GlobalOverlayLayer />\r\n </OverlayServiceProvider>\r\n );\r\n}\r\n\r\nfunction GlobalOverlayLayer() {\r\n const styles = useStyles();\r\n const overlay = useOverlayForTarget(undefined);\r\n if (!overlay) return null;\r\n return <ScrimContent overlay={overlay} className={mergeClasses(styles.scrim, styles.global)} />;\r\n}\r\n\r\n/**\r\n * Scoped overlay container. Wraps `children` in a `position: relative` div\r\n * and renders the topmost overlay for `targetId` as an absolutely-positioned\r\n * scrim covering the wrapper. Mirrors Blazor's per-target `<OverlayProvider>`.\r\n *\r\n * Typical use is alongside `<RecordContext>` to scope its loading overlay to\r\n * the record's region of the page:\r\n *\r\n * ```tsx\r\n * <RecordContext table=\"contact\" id={id}>\r\n * {(ctx) => (\r\n * <FluentOverlayTarget targetId={ctx.overlayTargetId}>\r\n * <Form />\r\n * </FluentOverlayTarget>\r\n * )}\r\n * </RecordContext>\r\n * ```\r\n *\r\n * Or with the `useRecordContext()` hook inside a child component:\r\n *\r\n * ```tsx\r\n * function RecordPanel() {\r\n * const ctx = useRecordContext();\r\n * return (\r\n * <FluentOverlayTarget targetId={ctx.overlayTargetId}>\r\n * <Form />\r\n * </FluentOverlayTarget>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function FluentOverlayTarget({\r\n targetId,\r\n children,\r\n className,\r\n style,\r\n}: {\r\n targetId: string;\r\n children: ReactNode;\r\n className?: string;\r\n style?: CSSProperties;\r\n}) {\r\n const styles = useStyles();\r\n const overlay = useOverlayForTarget(targetId);\r\n return (\r\n <div className={mergeClasses('ppp-overlay-target', styles.scopedHost, className)} style={style}>\r\n {children}\r\n {overlay && (\r\n <ScrimContent overlay={overlay} className={mergeClasses(styles.scrim, styles.scoped)} />\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction ScrimContent({ overlay, className }: { overlay: OverlayInstance; className: string }) {\r\n const styles = useStyles();\r\n const showProgress = overlay.showProgress !== false;\r\n return (\r\n <div className={className} role=\"status\" aria-live=\"polite\">\r\n {/* Default `primary` appearance — the spinner picks brand-accent\r\n colors that read well over the neutral backdrop in both light\r\n and dark themes. `inverted` was paired with the previous dark\r\n scrim and would now collide with the neutral surface. */}\r\n {showProgress && <Spinner size=\"large\" />}\r\n {overlay.description && (\r\n <span className={styles.description}>{overlay.description}</span>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { Spinner, makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport {\r\n useLocale,\r\n useLocalization,\r\n useLocalizationLoaded,\r\n} from '@powerportalspro/react';\r\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\r\n\r\nconst useStyles = makeStyles({\r\n // Centered placeholder used while prefixes are being fetched. Same\r\n // shape as the spinner blocks in `<NewRecordGridButton>` /\r\n // `<OpenRecordGridButton>` dialogs (~160px min height + vertical\r\n // centering) so a boundary swap doesn't pop the surrounding layout\r\n // around. Consumers wanting a tighter / inline fallback can pass\r\n // their own `fallback` prop.\r\n fallback: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n minHeight: '160px',\r\n rowGap: tokens.spacingVerticalM,\r\n },\r\n});\r\n\r\nexport interface LocalizationBoundaryProps {\r\n /**\r\n * Localization prefixes the children depend on. Each prefix may be a\r\n * token (`'tables.account'`, `'views.<viewId>'`) or a full key\r\n * (`'tables.account.label'`) — the framework coerces both to the\r\n * appropriate bundle. Prefixes outside `tables.*` / `views.*` are\r\n * covered by the default bundle and treated as already loaded once\r\n * the default bundle itself has settled.\r\n *\r\n * Pass an empty array (or omit) for a \"wait for the framework\r\n * baseline\" boundary — it still holds off rendering until the\r\n * default bundle + any prefixes declared on\r\n * `<DefaultLocalizerProvider prefixes={…}>` (or\r\n * `<PowerPortalsProProvider localizationPrefixes={…}>`) have\r\n * settled. Use this at the app root as a single Tier 1 gate.\r\n */\r\n prefixes?: readonly string[];\r\n /**\r\n * Rendered while at least one expected prefix is still being fetched.\r\n * Defaults to a centered `<Spinner />` (~160px-tall placeholder so\r\n * the surrounding layout doesn't shift when children mount). Pass\r\n * `null` to render nothing during loading, or your own JSX for a\r\n * custom skeleton / loading message.\r\n *\r\n * After the first paint with children mounted, the boundary stays\r\n * sticky — subsequent transient loading (e.g., the consumer\r\n * tightens the `prefixes` prop to include something new) won't\r\n * unmount the existing tree. Locale change is the one event that\r\n * does reset the gate.\r\n */\r\n fallback?: ReactNode;\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Holds off rendering `children` until every expected localization\r\n * prefix has been settled — the framework's default bundle (Tier 1),\r\n * any `prefixes` declared on the provider, and any declared on this\r\n * boundary. Mirrors Blazor's `<LocalizationBoundary>` with the\r\n * equivalent fetch-then-render gate.\r\n *\r\n * **Once-loaded stickiness.** After the first time the boundary's\r\n * loaded check returns `true`, children stay mounted regardless of\r\n * subsequent transient regressions (e.g., the `prefixes` prop\r\n * tightening to include a new token that takes a few hundred ms to\r\n * fetch). The existing tree keeps rendering with the strings it has;\r\n * new strings flow in via the cache as fetches resolve. The one\r\n * regression that DOES reset the gate is a locale change — the\r\n * boundary swaps back to its fallback while the new-locale strings\r\n * load, then re-mounts children once they settle.\r\n *\r\n * **Caching.** Per-resource bundles ride on the framework's two-tier\r\n * cache: an in-memory `settledTokens` set within the session, plus\r\n * thumbprinted `Cache-Control: immutable` HTTP responses across\r\n * sessions. So a second navigation to the same boundary (same\r\n * locale) usually mounts children on first paint with no fallback\r\n * shown at all — the cache already has the strings.\r\n *\r\n * **Failure semantics.** A failed bundle fetch counts as settled\r\n * (don't strand the UI). The underlying flush logs each failure via\r\n * `console.warn`; the boundary mounts children, where the affected\r\n * keys return their raw token until a manual refresh / retry.\r\n *\r\n * @example\r\n * ```tsx\r\n * <LocalizationBoundary prefixes={['tables.account']}>\r\n * <EditAccountPage />\r\n * </LocalizationBoundary>\r\n * ```\r\n *\r\n * @example Root gate (wait for default bundle + provider baselines):\r\n * ```tsx\r\n * <PowerPortalsProProvider localizationPrefixes={['tables.contact']}>\r\n * <LocalizationBoundary>\r\n * <App />\r\n * </LocalizationBoundary>\r\n * </PowerPortalsProProvider>\r\n * ```\r\n */\r\nexport function LocalizationBoundary({\r\n prefixes,\r\n fallback,\r\n children,\r\n}: LocalizationBoundaryProps) {\r\n const styles = useStyles();\r\n const resolvedPrefixes = prefixes ?? EMPTY_PREFIXES;\r\n // Read the active locale so we can detect locale changes and reset\r\n // the sticky-ready flag. Using the dedicated `useLocale` hook is a\r\n // hard dependency on the framework's locale state, but\r\n // <LocalizationBoundary> already depends on the framework's\r\n // localizer infrastructure, so this is the right place to read it.\r\n const { locale } = useLocale();\r\n // Fire the prefetch on mount + on prefix changes. `useLocalization`\r\n // takes care of deduping against already-fetched / in-flight tokens.\r\n useLocalization(resolvedPrefixes);\r\n // Reactive — re-renders when the settled set updates OR when tier-1\r\n // settles OR when the boundary's resolved-prefixes-key changes.\r\n const isLoaded = useLocalizationLoaded(resolvedPrefixes);\r\n\r\n // Sticky-ready state. Flips true the first time isLoaded reports\r\n // true and stays true even if isLoaded later regresses (e.g., the\r\n // consumer expanded `prefixes` to include a new token mid-session)\r\n // — except on locale change, where we reset to wait for the new\r\n // locale's bundles.\r\n const [ready, setReady] = useState(false);\r\n const lastLocaleRef = useRef(locale);\r\n\r\n useEffect(() => {\r\n if (locale !== lastLocaleRef.current) {\r\n lastLocaleRef.current = locale;\r\n // Locale change resets readiness even if `isLoaded` is\r\n // currently true (the prefetcher cleared its settled set for\r\n // the new locale, but the boundary's local `ready` state\r\n // lives outside the prefetcher — handle it here).\r\n setReady(false);\r\n return;\r\n }\r\n if (isLoaded && !ready) {\r\n setReady(true);\r\n }\r\n }, [isLoaded, locale, ready]);\r\n\r\n if (ready) return <>{children}</>;\r\n\r\n // Default fallback only constructed when needed — keeps the no-op\r\n // path (cache-hot re-navigation, immediate sticky-ready) from\r\n // instantiating the Fluent Spinner DOM at all.\r\n if (fallback !== undefined) return <>{fallback}</>;\r\n return (\r\n <div className={mergeClasses('ppp-localization-boundary', styles.fallback)} role=\"status\" aria-live=\"polite\">\r\n <Spinner size=\"medium\" />\r\n </div>\r\n );\r\n}\r\n\r\n// Stable empty-array reference so `prefixes={undefined}` doesn't create\r\n// a fresh array each render that would churn `useLocalization`'s value-\r\n// stable key calculation downstream.\r\nconst EMPTY_PREFIXES: readonly string[] = Object.freeze([]);\r\n","import {\r\n MessageBar,\r\n MessageBarBody,\r\n MessageBarTitle,\r\n makeStyles,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport { ErrorCircle20Regular } from '@fluentui/react-icons';\r\nimport { useT, useValidationContext } from '@powerportalspro/react';\r\nimport type { CSSProperties } from 'react';\r\n\r\nexport interface ValidationSummaryProps {\r\n /**\r\n * Header text shown above the error list. Defaults to the localized\r\n * `app.errors.form-validation-errors` string (English: \"There are 1 or\r\n * more errors:\"). Pass `null` to suppress the header entirely.\r\n */\r\n title?: string | null;\r\n /**\r\n * When provided, the summary lists only errors for these column names.\r\n * Without this prop, every error in the surrounding {@link ValidationContext}\r\n * is included. Useful for sectioning a long form into multiple summaries.\r\n */\r\n columns?: readonly string[];\r\n /** Forwarded to the rendered `MessageBar` for layout. */\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\nconst useStyles = makeStyles({\r\n list: {\r\n margin: 0,\r\n paddingLeft: tokens.spacingHorizontalXL,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n rowGap: tokens.spacingVerticalXXS,\r\n },\r\n});\r\n\r\n/**\r\n * Lists every validation error in the surrounding {@link ValidationContext}\r\n * (or the subset matching {@link ValidationSummaryProps.columns}). Mirrors\r\n * Blazor's `<ValidationSummary>` — intended for the top of a form so the\r\n * user sees a consolidated rollup alongside the per-field inline messages.\r\n *\r\n * Renders nothing when there are no errors (no empty MessageBar) so the\r\n * component can sit unconditionally in the form layout without taking\r\n * vertical space until validation actually fires.\r\n *\r\n * Reads from the nearest {@link ValidationContext} — typically the one\r\n * `<RecordContext>` auto-mounts for its subtree. Outside any provider\r\n * (no RecordContext, no manual ValidationProvider), this component is a no-op.\r\n */\r\nexport function ValidationSummary({\r\n title,\r\n columns,\r\n className,\r\n style,\r\n}: ValidationSummaryProps) {\r\n const styles = useStyles();\r\n const t = useT();\r\n const validation = useValidationContext();\r\n // `title === undefined` ⇒ use the localized default; `null` ⇒ suppress; a\r\n // string passed by the consumer wins as-is.\r\n const resolvedTitle = title === undefined ? t('app.errors.form-validation-errors') : title;\r\n if (!validation) return null;\r\n\r\n // Iterate the errors map preserving registration order. Filter to the\r\n // consumer's column allow-list when provided.\r\n const entries = columns\r\n ? Array.from(validation.errors.entries()).filter(([col]) => columns.includes(col))\r\n : Array.from(validation.errors.entries());\r\n\r\n // Flatten to a stable-keyed list — same column can have multiple errors\r\n // (e.g. required + custom validator), so we suffix the index within its\r\n // column for the React key.\r\n const items = entries.flatMap(([col, errs]) =>\r\n errs.map((message, idx) => ({ key: `${col}:${idx}`, message })),\r\n );\r\n if (items.length === 0) return null;\r\n\r\n return (\r\n <MessageBar\r\n intent=\"error\"\r\n // Override Fluent's default `ErrorCircleFilled` with the outlined\r\n // `ErrorCircle20Regular` to match the framework's other icon usage\r\n // (info popover, dialog variants — all Regular weight).\r\n icon={<ErrorCircle20Regular />}\r\n className={className}\r\n style={style}\r\n >\r\n <MessageBarBody>\r\n {resolvedTitle && <MessageBarTitle>{resolvedTitle}</MessageBarTitle>}\r\n <ul className={styles.list}>\r\n {items.map((item) => (\r\n <li key={item.key}>{item.message}</li>\r\n ))}\r\n </ul>\r\n </MessageBarBody>\r\n </MessageBar>\r\n );\r\n}\r\n","import { Button, type ButtonProps } from '@fluentui/react-components';\r\nimport { Save24Regular } from '@fluentui/react-icons';\r\nimport {\r\n useDialogService,\r\n useMainContext,\r\n useRunWithOverlay,\r\n useT,\r\n useValidationContext,\r\n} from '@powerportalspro/react';\r\nimport { useCallback, useState } from 'react';\r\n\r\nexport interface SaveContextButtonProps {\r\n /**\r\n * Force the button into a disabled state regardless of the context's dirty\r\n * state. The button is always disabled when the surrounding `MainContext` /\r\n * `RecordContext` reports clean — this prop adds a consumer override on top.\r\n */\r\n disabled?: boolean;\r\n /**\r\n * Called before `saveAll()` runs. Throw (or return a rejected promise) to\r\n * cancel the save. Throws here are routed through the same error path\r\n * as `saveAll()` failures (`onError` or the dialog service).\r\n */\r\n onBeforeSave?: () => void | Promise<void>;\r\n /** Called after `saveAll()` resolves successfully. */\r\n onSaved?: () => void | Promise<void>;\r\n /**\r\n * Called when `saveAll()` (or `onBeforeSave`/`onSaved`) throws. When\r\n * provided, takes over error display entirely — the dialog service is not\r\n * called. Use to integrate with your own toast/error reporting system or to\r\n * silently swallow specific cancel-style errors.\r\n *\r\n * When omitted, the error is surfaced via `dialog.showErrorAsync` with the\r\n * error's message as the body and `\"Save failed\"` as the title.\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n /** Custom button label. Defaults to `\"Save\"`. */\r\n label?: string;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"` for the toolbar look. */\r\n appearance?: ButtonProps['appearance'];\r\n}\r\n\r\n/**\r\n * Reads `isDirty` and `saveAll` from the nearest `MainContext` /\r\n * `RecordContext` (`RecordContext` IS-A `MainContext`, so either works) and:\r\n *\r\n * - Disables the button when nothing is dirty in the subtree.\r\n * - Calls `onBeforeSave` (cancellable) on click.\r\n * - Awaits `saveAll()` — own record + nested descendants in one shot.\r\n * - Calls `onSaved` after a successful save.\r\n *\r\n * Tracks an internal `saving` flag so rapid double-clicks during an in-flight\r\n * PATCH don't fire a second `saveAll` (also visually disabled while saving).\r\n */\r\nexport function SaveContextButton({\r\n disabled,\r\n onBeforeSave,\r\n onSaved,\r\n onError,\r\n label,\r\n appearance = 'subtle',\r\n}: SaveContextButtonProps) {\r\n const ctx = useMainContext();\r\n const validation = useValidationContext();\r\n const dialog = useDialogService();\r\n const runWithOverlay = useRunWithOverlay();\r\n const t = useT();\r\n const [saving, setSaving] = useState(false);\r\n const resolvedLabel = label ?? t('app.buttons.save.label');\r\n\r\n const handleClick = useCallback(async () => {\r\n if (!ctx) return;\r\n setSaving(true);\r\n try {\r\n if (onBeforeSave) await onBeforeSave();\r\n // Run the validation pass before the save attempt — same pattern as\r\n // Blazor's `EditContext.Validate()` gating `MainContext.SaveAsync`.\r\n // Errors land in the per-column error state, which the edit components\r\n // surface inline; the button itself just aborts silently (no error\r\n // dialog — the inline messages are the user feedback). Skipped when\r\n // the surrounding context's\r\n // `forceSuccessfulValidationBeforeSave` is `false`.\r\n if (\r\n ctx.forceSuccessfulValidationBeforeSave &&\r\n validation &&\r\n !(await validation.validateAll())\r\n ) {\r\n return;\r\n }\r\n // Global (full-screen) overlay during the save itself — no targetId.\r\n // Mirrors Blazor's MainContext.SaveAsync wrapping the work in a global\r\n // overlay (no TargetId on the OverlayInstance).\r\n await runWithOverlay({ description: t('app.messages.record-save') }, () => ctx.saveAll());\r\n if (onSaved) await onSaved();\r\n } catch (err) {\r\n if (onError) {\r\n await onError(err);\r\n } else {\r\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\r\n title: t('app.errors.record-save'),\r\n });\r\n }\r\n } finally {\r\n setSaving(false);\r\n }\r\n }, [ctx, validation, dialog, runWithOverlay, t, onBeforeSave, onSaved, onError]);\r\n\r\n // No record-level Write-permission hide. The Save button is the\r\n // commit point for the WHOLE subtree — not just the main record —\r\n // and Write may be denied on the main record while still being\r\n // granted on related records exposed via nested SubGrids /\r\n // RecordContexts. (Example: a portal user who can't edit their parent\r\n // account but CAN add / edit / delete contacts under it.) Hiding the\r\n // button here would strand those legit grid edits with no way to\r\n // commit them.\r\n //\r\n // The edit components on the main record are independently locked\r\n // when Write is denied (see `resolveEditState` in `metadata-helpers.ts`)\r\n // — they read-only AND disabled, so the main record can't go dirty\r\n // in the first place. The aggregate `ctx.isDirty` below picks up\r\n // only the descendants whose changes are legitimately pending, and\r\n // the server enforces permissions on each operation in the batch.\r\n //\r\n // Render even without a context so positional consumers don't get a layout\r\n // jump as the context mounts; but disable so click is a no-op.\r\n const isDisabled = !ctx || !ctx.isDirty || saving || disabled === true;\r\n\r\n return (\r\n <Button\r\n type=\"button\"\r\n className=\"ppp-save-context-button\"\r\n appearance={appearance}\r\n icon={<Save24Regular />}\r\n disabled={isDisabled}\r\n onClick={handleClick}\r\n >\r\n {resolvedLabel}\r\n </Button>\r\n );\r\n}\r\n","import { Button, type ButtonProps } from '@fluentui/react-components';\r\nimport { ArrowClockwise24Regular } from '@fluentui/react-icons';\r\nimport {\r\n useDialogService,\r\n useMainContext,\r\n useRunWithOverlay,\r\n useT,\r\n useWarnAboutUnsavedChanges,\r\n} from '@powerportalspro/react';\r\nimport { useCallback, useState } from 'react';\r\n\r\nexport interface RefreshContextButtonProps {\r\n /** Force the button into a disabled state. */\r\n disabled?: boolean;\r\n /**\r\n * Called before `refreshAll()` runs (and before the unsaved-changes prompt).\r\n * Throw to cancel the refresh. Errors here route through the same path as\r\n * `refreshAll()` failures (`onError` or the dialog service).\r\n */\r\n onBeforeRefresh?: () => void | Promise<void>;\r\n /** Called after `refreshAll()` resolves successfully. */\r\n onRefreshed?: () => void | Promise<void>;\r\n /**\r\n * Called when `refreshAll()` (or any of the lifecycle hooks) throws. When\r\n * provided, takes over error display. When omitted, the error is surfaced\r\n * via `dialog.showErrorAsync` with `\"Refresh failed\"` as the title.\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n /** Custom button label. Defaults to `\"Refresh\"`. */\r\n label?: string;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\r\n appearance?: ButtonProps['appearance'];\r\n}\r\n\r\n/**\r\n * Reads `refreshAll` from the nearest `MainContext` / `RecordContext` and:\r\n *\r\n * - Calls `onBeforeRefresh` (cancellable) on click.\r\n * - If the context has unsaved changes, prompts via the Dialog service\r\n * (\"You have unsaved changes…\") and aborts on cancel.\r\n * - Awaits `refreshAll()` — own reload + nested descendants in one shot.\r\n * - Calls `onRefreshed` after success.\r\n *\r\n * Always enabled (refresh is meaningful even on a clean context). Tracks a\r\n * `refreshing` flag for click coalescing.\r\n */\r\nexport function RefreshContextButton({\r\n disabled,\r\n onBeforeRefresh,\r\n onRefreshed,\r\n onError,\r\n label,\r\n appearance = 'subtle',\r\n}: RefreshContextButtonProps) {\r\n const ctx = useMainContext();\r\n const dialog = useDialogService();\r\n const warn = useWarnAboutUnsavedChanges();\r\n const runWithOverlay = useRunWithOverlay();\r\n const t = useT();\r\n const [refreshing, setRefreshing] = useState(false);\r\n const resolvedLabel = label ?? t('app.buttons.refresh.label');\r\n\r\n const handleClick = useCallback(async () => {\r\n if (!ctx) return;\r\n setRefreshing(true);\r\n try {\r\n if (onBeforeRefresh) await onBeforeRefresh();\r\n if (ctx.isDirty) {\r\n const proceed = await warn();\r\n if (!proceed) return;\r\n }\r\n // Global overlay during the refresh — mirrors Blazor's\r\n // MainContext.RefreshAsync wrapping the work in a global overlay.\r\n await runWithOverlay({ description: t('app.messages.record-loading') }, () =>\r\n ctx.refreshAll(),\r\n );\r\n if (onRefreshed) await onRefreshed();\r\n } catch (err) {\r\n if (onError) {\r\n await onError(err);\r\n } else {\r\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\r\n title: t('app.errors.generic-error'),\r\n });\r\n }\r\n } finally {\r\n setRefreshing(false);\r\n }\r\n }, [ctx, dialog, warn, runWithOverlay, t, onBeforeRefresh, onRefreshed, onError]);\r\n\r\n const isDisabled = !ctx || refreshing || disabled === true;\r\n\r\n return (\r\n <Button\r\n type=\"button\"\r\n className=\"ppp-refresh-context-button\"\r\n appearance={appearance}\r\n icon={<ArrowClockwise24Regular />}\r\n disabled={isDisabled}\r\n onClick={handleClick}\r\n >\r\n {resolvedLabel}\r\n </Button>\r\n );\r\n}\r\n","import { Button, type ButtonProps } from '@fluentui/react-components';\nimport { Delete24Regular } from '@fluentui/react-icons';\nimport { TableSecurityPermission } from '@powerportalspro/core';\nimport {\n useDialogService,\n usePowerPortalsPro,\n useRecordContext,\n useRunWithOverlay,\n useT,\n} from '@powerportalspro/react';\nimport { useCallback, useState } from 'react';\n\nexport interface DeleteContextButtonProps {\n /** Force the button into a disabled state regardless of permission. */\n disabled?: boolean;\n /**\n * Called before the delete prompt runs. Throw to cancel. Errors here route\n * through the same path as `deleteRecordAsync` failures (`onError` or the\n * dialog service).\n */\n onBeforeDelete?: () => void | Promise<void>;\n /**\n * Called after `deleteRecordAsync` resolves successfully. Defaults to\n * `() => window.history.back()` — pops the browser back one entry,\n * landing the user on whatever page brought them to this record.\n *\n * Parity note: Blazor's `<DeleteContextButton>` uses\n * `NavigationHistory.PopToPrevious()` (an in-app URL stack populated\n * by `NavigationManager.LocationChanged`) and falls back to\n * `history.back` only when that stack is empty. For the user, both\n * land on the same page in the common in-app flow (the in-app history\n * stack and the browser history mirror each other). Blazor's primitive\n * exists to work around Blazor Server's server-side navigation state;\n * a SPA's `window.history` already tracks the same URLs natively, so\n * React doesn't need the extra stack.\n *\n * Override when the default isn't right — e.g. the record was opened\n * via deep link (no in-app history), or you want to land somewhere\n * specific:\n *\n * `onDeleted={() => navigate('/accounts')}` (react-router)\n * `onDeleted={() => navigate(-1)}` (react-router, equivalent to default)\n * `onDeleted={() => router.push('/accounts')}` (TanStack Router)\n */\n onDeleted?: () => void | Promise<void>;\n /**\n * Called when `deleteRecordAsync` (or any of the lifecycle hooks) throws.\n * When provided, takes over error display. When omitted, the error is\n * surfaced via `dialog.showErrorAsync` with `\"Delete failed\"` as the title.\n */\n onError?: (error: unknown) => void | Promise<void>;\n /** Custom button label. Defaults to `\"Delete\"`. */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n}\n\n/**\n * Reads the record + its `permissions` from the nearest `RecordContext` and:\n *\n * - Disables when the current user lacks `TableSecurityPermission.Delete` on the\n * record, when no record has loaded, or when the consumer forces `disabled`.\n * - Calls `onBeforeDelete` (cancellable) on click.\n * - Prompts confirmation via the Dialog service — bound to the framework's\n * default messaging; consumers can pre-empt by handling the click externally.\n * - Calls `ppp.deleteRecordAsync(table, id)`.\n * - Calls `onDeleted` after success (default: `window.history.back()`).\n */\nexport function DeleteContextButton({\n disabled,\n onBeforeDelete,\n onDeleted,\n onError,\n label,\n appearance = 'subtle',\n}: DeleteContextButtonProps) {\n const { ppp } = usePowerPortalsPro();\n const ctx = useRecordContext();\n const dialog = useDialogService();\n const runWithOverlay = useRunWithOverlay();\n const t = useT();\n const [deleting, setDeleting] = useState(false);\n const resolvedLabel = label ?? t('app.buttons.delete.label');\n\n const record = ctx.record;\n // Bitmask check against TableSecurityPermission.Delete — `permissions` is a\n // TableSecurityPermission flags value from the wire. `undefined` permissions\n // means we don't know yet (record still loading) — disable until known.\n const canDelete =\n record?.permissions !== undefined &&\n (record.permissions & TableSecurityPermission.Delete) === TableSecurityPermission.Delete;\n\n const handleClick = useCallback(async () => {\n if (!record || !canDelete || !record.id || !record.tableName) return;\n // Capture the narrowed identifiers so the runWithOverlay arrow closure\n // doesn't lose TypeScript's narrowing across the await boundary.\n const recordTableName = record.tableName;\n const recordId = record.id;\n setDeleting(true);\n try {\n // Context-level gate first (record-scoped policy via\n // `<RecordContext onBeforeDelete={...}>`), then the button's\n // own gate. Either may throw to cancel; the catch below routes\n // the error through `onError` / the dialog service.\n await ctx.runBeforeDelete();\n if (onBeforeDelete) await onBeforeDelete();\n\n const result = await dialog.showConfirmationAsync(\n t('app.messages.delete-record-confirmation'),\n {\n title: t('app.messages.delete-title'),\n primaryText: t('app.buttons.delete.label'),\n secondaryText: t('app.buttons.cancel.label'),\n },\n );\n if (result.cancelled) return;\n\n // Global (full-screen) overlay during the delete itself — mirrors\n // SaveContextButton / RefreshContextButton and the Blazor side's\n // `MainContext.SaveAsync` / `DeleteAsync` patterns where the user is\n // blocked from interacting with the page while the server call is in\n // flight. `onDeleted` runs outside the overlay so consumer-side\n // navigation (the default `window.history.back()`) doesn't fight an\n // overlay that's about to unmount with the page.\n await runWithOverlay({ description: t('app.messages.deleting-record') }, () =>\n ppp.deleteRecordAsync(recordTableName, recordId),\n );\n if (onDeleted) {\n await onDeleted();\n } else {\n window.history.back();\n }\n } catch (err) {\n if (onError) {\n await onError(err);\n } else {\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\n title: t('app.errors.record-delete'),\n });\n }\n } finally {\n setDeleting(false);\n }\n }, [ppp, ctx, record, canDelete, dialog, runWithOverlay, t, onBeforeDelete, onDeleted, onError]);\n\n const isDisabled = !record || !canDelete || deleting || disabled === true;\n\n return (\n <Button\n type=\"button\"\n className=\"ppp-delete-context-button\"\n appearance={appearance}\n icon={<Delete24Regular />}\n disabled={isDisabled}\n onClick={handleClick}\n >\n {resolvedLabel}\n </Button>\n );\n}\n","import type { ComponentType, ReactElement, ReactNode } from 'react';\r\nimport { Children, isValidElement } from 'react';\r\n\r\n/**\r\n * Categorizes a grid toolbar button by its semantic action on a\r\n * record. The grid uses this to dispatch row-level interactions\r\n * (double-click, primary-name link, single-record Open click) to\r\n * the right button depending on the row's state — a pending-create\r\n * row routes to the `'create'` button's re-open path, a persisted\r\n * row to the `'open'` button.\r\n *\r\n * Hybrid const-object + literal-union pattern so consumers can\r\n * compare via either string literal or constant. Matches the\r\n * project's enum convention.\r\n */\r\nexport const GridButtonRole = {\r\n /**\r\n * Creates a new record. Receives the grid's pending-create\r\n * re-open dispatch when a row in `pendingRowCreates` is clicked,\r\n * so the user's in-progress new record is edited via the same\r\n * form/wizard it was created in.\r\n */\r\n Create: 'create',\r\n /**\r\n * Opens an existing (server-persisted) record for read or edit.\r\n * Receives the grid's row-click dispatch for any row that carries\r\n * a real Dataverse id (not the `__pending-create-` prefix).\r\n */\r\n Open: 'open',\r\n /** Deletes a record. */\r\n Delete: 'delete',\r\n /**\r\n * Anything that doesn't fit the create/open/delete taxonomy —\r\n * refresh, link/unlink, custom export, etc. The grid doesn't\r\n * dispatch row-level interactions to `'other'` buttons; they're\r\n * triggered exclusively by their own click handlers.\r\n */\r\n Other: 'other',\r\n} as const;\r\nexport type GridButtonRole = (typeof GridButtonRole)[keyof typeof GridButtonRole];\r\n\r\n/**\r\n * Reading `.type.<symbol>` from a `ReactElement` is the reliable way\r\n * to identify a component across module boundaries — `displayName`\r\n * strings get rewritten by minifiers, and direct type comparison\r\n * fails when the same component is bundled twice in linked-package\r\n * setups. Same pattern used by `WizardRecordPage` / `GridColumn`.\r\n */\r\nconst GRID_BUTTON_ROLE_MARKER = Symbol.for('@powerportalspro/grid-button-role');\r\n\r\n/**\r\n * Stamps a component with its default {@link GridButtonRole}. Called\r\n * at module scope by each built-in grid button so the grid's child\r\n * scan can identify it without requiring a prop on every usage.\r\n * Custom buttons can either call this themselves or set\r\n * `gridButtonRole` on their props at render time.\r\n */\r\nexport function setGridButtonRole<P>(\r\n Component: ComponentType<P>,\r\n role: GridButtonRole,\r\n): void {\r\n (Component as unknown as Record<symbol, GridButtonRole>)[GRID_BUTTON_ROLE_MARKER] = role;\r\n}\r\n\r\n/**\r\n * Read the {@link GridButtonRole} a React element claims via either:\r\n * 1. A `gridButtonRole` prop on the element (per-instance override).\r\n * 2. The component's stamped default from {@link setGridButtonRole}.\r\n *\r\n * Returns `null` when neither is set — the grid skips the element\r\n * for role-based dispatch but still renders it in the toolbar.\r\n */\r\nexport function getGridButtonRoleFromElement(node: ReactNode): GridButtonRole | null {\r\n if (!isValidElement(node)) return null;\r\n const props = node.props as { gridButtonRole?: GridButtonRole } | null | undefined;\r\n const propRole = props?.gridButtonRole;\r\n if (\r\n propRole === GridButtonRole.Create ||\r\n propRole === GridButtonRole.Open ||\r\n propRole === GridButtonRole.Delete ||\r\n propRole === GridButtonRole.Other\r\n ) {\r\n return propRole;\r\n }\r\n const type = node.type as unknown as Record<symbol, GridButtonRole | undefined> | undefined;\r\n return type?.[GRID_BUTTON_ROLE_MARKER] ?? null;\r\n}\r\n\r\n/**\r\n * Walks the React children tree (recursing one level into\r\n * `<GridButtons>` wrappers) and returns the first element matching\r\n * the given role, or `null`. Used by the grid's toolbar scan to\r\n * locate the consumer's create / open / delete buttons.\r\n *\r\n * Limitation: the same single-level recurse as\r\n * `extractGridColumnOverrides` — `<GridButtons><Fragment>...</Fragment></GridButtons>`\r\n * is not searched. The flat authoring style consumers actually use\r\n * works fine.\r\n */\r\nexport function findFirstButtonOfRole(\r\n children: ReactNode,\r\n role: GridButtonRole,\r\n): ReactElement | null {\r\n let found: ReactElement | null = null;\r\n function visit(node: ReactNode): void {\r\n if (found) return;\r\n Children.forEach(node, (child) => {\r\n if (found) return;\r\n if (!isValidElement(child)) return;\r\n const childRole = getGridButtonRoleFromElement(child);\r\n if (childRole === role) {\r\n found = child;\r\n return;\r\n }\r\n // GridButtons wrapper — descend one level.\r\n const props = child.props as { children?: ReactNode } | null | undefined;\r\n if (props?.children !== undefined && childRole === null) {\r\n visit(props.children);\r\n }\r\n });\r\n }\r\n visit(children);\r\n return found;\r\n}\r\n\r\n/**\r\n * Common prop accepted by grid toolbar buttons. Setting this on an\r\n * instance overrides the component's stamped default (rare — useful\r\n * for a custom `<GridButton>` that should be treated as a create\r\n * trigger, or a built-in that should be skipped from role-based\r\n * dispatch by setting `'other'`).\r\n */\r\nexport interface GridButtonRoleProps {\r\n /**\r\n * Override the button's default role. The built-in buttons stamp\r\n * sensible defaults at module load time, so this is opt-in for\r\n * consumers building bespoke triggers on top of `<GridButton>`.\r\n */\r\n gridButtonRole?: GridButtonRole;\r\n}\r\n","/**\r\n * Selection-based visibility / enabled mode for grid toolbar buttons.\r\n * Mirrors Blazor's `GridButtonBehavior` enum used by\r\n * `<NavigateRecordGridButton>` / `<GridButton>` consumers.\r\n *\r\n * Hybrid const-object + literal-union — same pattern other enums in\r\n * the package follow.\r\n */\r\nexport const GridButtonBehavior = {\r\n /** Always shown / enabled regardless of selection. */\r\n Always: 'always',\r\n /** Only when zero rows are selected (e.g. New / Link). */\r\n NoneSelected: 'none-selected',\r\n /** Only when exactly one row is selected (e.g. single-record Edit / Navigate). */\r\n OnlyOneSelected: 'only-one-selected',\r\n /** Only when two or more rows are selected (e.g. multi-edit, bulk delete). */\r\n MoreThanOneSelected: 'more-than-one-selected',\r\n /** Whenever at least one row is selected (Delete / Unlink). */\r\n OneOrMoreSelected: 'one-or-more-selected',\r\n} as const;\r\nexport type GridButtonBehavior = (typeof GridButtonBehavior)[keyof typeof GridButtonBehavior];\r\n\r\n/**\r\n * Evaluates a {@link GridButtonBehavior} against the current selection\r\n * count and returns whether the corresponding flag (visible / enabled)\r\n * should be active.\r\n */\r\nexport function evaluateGridButtonBehavior(\r\n behavior: GridButtonBehavior,\r\n selectionCount: number,\r\n): boolean {\r\n switch (behavior) {\r\n case GridButtonBehavior.Always:\r\n return true;\r\n case GridButtonBehavior.NoneSelected:\r\n return selectionCount === 0;\r\n case GridButtonBehavior.OnlyOneSelected:\r\n return selectionCount === 1;\r\n case GridButtonBehavior.MoreThanOneSelected:\r\n return selectionCount > 1;\r\n case GridButtonBehavior.OneOrMoreSelected:\r\n return selectionCount >= 1;\r\n default:\r\n return false;\r\n }\r\n}\r\n","import { Button, Tooltip, type ButtonProps } from '@fluentui/react-components';\r\nimport { useCallback, useState, type ReactNode } from 'react';\r\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\r\nimport {\r\n evaluateGridButtonBehavior,\r\n GridButtonBehavior,\r\n} from './grid-button-behavior.js';\r\nimport { type GridButtonRoleProps } from './grid-button-role.js';\r\n\r\nexport interface GridButtonProps extends GridButtonRoleProps {\r\n /**\r\n * Async click handler. Receives the live {@link GridContextValue} so\r\n * the handler can read selected records, the parent record (in a\r\n * SubGrid), the active view, etc. without a separate `useGridContext`\r\n * call.\r\n */\r\n onClick: (gridContext: GridContextValue) => void | Promise<void>;\r\n\r\n /**\r\n * Button label. Required — the consumer building a custom toolbar\r\n * button always has a meaningful text to show; defaulting to a\r\n * localized fallback like the typed buttons do would mask the\r\n * authoring intent.\r\n */\r\n label: string;\r\n\r\n /**\r\n * Optional icon rendered alongside the label. Pass a Fluent icon\r\n * element (e.g. `<Add20Regular />`). When omitted, the button\r\n * renders text-only.\r\n */\r\n icon?: ReactNode;\r\n\r\n /**\r\n * Selection-based visibility gate. Evaluated against the grid's\r\n * current selected-row count via {@link evaluateGridButtonBehavior}.\r\n * Defaults to {@link GridButtonBehavior.Always}. Returning `false`\r\n * removes the button from the toolbar entirely (no DOM emitted).\r\n */\r\n visibilityBehavior?: GridButtonBehavior;\r\n\r\n /**\r\n * Selection-based enabled gate. Same evaluator as\r\n * {@link visibilityBehavior}. Defaults to\r\n * {@link GridButtonBehavior.Always}. Returning `false` disables\r\n * the click but leaves the button rendered (greyed out).\r\n */\r\n enabledBehavior?: GridButtonBehavior;\r\n\r\n /**\r\n * Hard disable override. Takes precedence over\r\n * {@link enabledBehavior} when `true` — useful for gating against\r\n * external state (a saving spinner, an unauthenticated user) that\r\n * doesn't fit the selection-based predicates.\r\n */\r\n disabled?: boolean;\r\n\r\n /**\r\n * Optional Fluent tooltip text wrapped around the button. When\r\n * omitted, no tooltip mounts.\r\n */\r\n tooltip?: string;\r\n\r\n /**\r\n * Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"` so the\r\n * button reads as transparent at rest and only paints a background on hover —\r\n * matching the grid's built-in Refresh / Settings buttons. Override per-button\r\n * when a custom action wants a different visual weight (e.g. `\"primary\"` for a\r\n * primary call-to-action). Mirrors the Blazor `<GridButton Appearance=\"...\">`\r\n * surface — same default (FAST `Stealth` there, Fluent v9 `\"subtle\"` here, both\r\n * resolve to transparent-at-rest with a hover fill).\r\n */\r\n appearance?: ButtonProps['appearance'];\r\n\r\n /**\r\n * Called when the click handler throws. When omitted, the error\r\n * is re-thrown so it bubbles to an upstream error boundary or\r\n * surfaces in the browser console — same convention the typed\r\n * grid buttons follow.\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * Build-your-own grid toolbar button. The typed buttons\r\n * (`<NewRecordGridButton>`, `<DeleteRecordGridButton>`, etc.) handle\r\n * the common create / open / delete patterns; reach for `<GridButton>`\r\n * when you need a custom action (a one-off export, a custom workflow\r\n * trigger, anything outside the typed-button taxonomy).\r\n *\r\n * Mirrors Blazor's `<GridButton>` surface — selection-based visibility\r\n * + enabled gates, role marker for row-click dispatch, label + icon +\r\n * tooltip + appearance. Plumbed into the same {@link useGridContext}\r\n * grid-toolbar registry the typed buttons use, so a custom button\r\n * inside `<GridButtons>` participates in toolbar-overflow layout,\r\n * row-click dispatch (when `gridButtonRole` is set), and selection\r\n * tracking the same way.\r\n *\r\n * **Internal click coalescing.** A click on the button flips an\r\n * internal `working` flag that disables further clicks until the\r\n * async handler resolves — rapid double-clicks on a slow action\r\n * dispatch the handler once, not twice. Matches the convention the\r\n * typed buttons follow.\r\n *\r\n * ```tsx\r\n * <MainGrid tableName=\"contact\">\r\n * <GridButtons>\r\n * <NewRecordGridButton>\r\n * <Section><SectionColumn><TextEdit columnName=\"firstname\" /></SectionColumn></Section>\r\n * </NewRecordGridButton>\r\n *\r\n * <GridButton\r\n * label=\"Export selected\"\r\n * icon={<ArrowDownload20Regular />}\r\n * enabledBehavior={GridButtonBehavior.OneOrMoreSelected}\r\n * onClick={async (grid) => {\r\n * const ids = grid.selectedRecords.map((r) => r.id!);\r\n * await exportToCsvAsync(ids);\r\n * }}\r\n * />\r\n * </GridButtons>\r\n * </MainGrid>\r\n * ```\r\n */\r\nexport function GridButton({\r\n onClick,\r\n label,\r\n icon,\r\n visibilityBehavior = GridButtonBehavior.Always,\r\n enabledBehavior = GridButtonBehavior.Always,\r\n disabled,\r\n tooltip,\r\n appearance = 'subtle',\r\n onError,\r\n}: GridButtonProps) {\r\n const grid = useGridContext();\r\n const selectionCount = grid.selectedRecords.length;\r\n const [working, setWorking] = useState(false);\r\n\r\n const handleClick = useCallback(async () => {\r\n setWorking(true);\r\n try {\r\n await onClick(grid);\r\n } catch (err) {\r\n if (onError) {\r\n await onError(err);\r\n } else {\r\n // Re-throw so the error surfaces in the console / parent boundary\r\n // — silent failures here would mask wiring bugs. Same convention\r\n // as `<RefreshGridButton>` / `<SaveContextButton>`.\r\n throw err;\r\n }\r\n } finally {\r\n setWorking(false);\r\n }\r\n }, [grid, onClick, onError]);\r\n\r\n // Visibility gate: short-circuit before rendering so the button\r\n // doesn't occupy a toolbar slot when its selection requirement\r\n // isn't met. Mirrors Blazor's `IsButtonVisible` predicate.\r\n if (!evaluateGridButtonBehavior(visibilityBehavior, selectionCount)) {\r\n return null;\r\n }\r\n\r\n const enabledBySelection = evaluateGridButtonBehavior(enabledBehavior, selectionCount);\r\n const isDisabled = disabled === true || working || !enabledBySelection;\r\n\r\n // Build the icon-prop bag separately so we only pass `icon` when\r\n // the consumer supplied one — under `exactOptionalPropertyTypes`,\r\n // passing `icon={undefined}` directly fails Fluent's `icon?: Slot`\r\n // type, but omitting the key entirely is fine.\r\n const iconProps: Pick<ButtonProps, 'icon'> | object =\r\n icon !== undefined ? { icon } : {};\r\n\r\n const button = (\r\n <Button\r\n type=\"button\"\r\n className=\"ppp-grid-button\"\r\n appearance={appearance}\r\n {...iconProps}\r\n disabled={isDisabled}\r\n onClick={handleClick}\r\n >\r\n {label}\r\n </Button>\r\n );\r\n\r\n // Fluent tooltips wrap the trigger; only mount one when the consumer\r\n // supplied text so we don't emit an empty tooltip popper for the\r\n // common no-tooltip case.\r\n if (tooltip) {\r\n return (\r\n <Tooltip content={tooltip} relationship=\"label\">\r\n {button}\r\n </Tooltip>\r\n );\r\n }\r\n return button;\r\n}\r\n","import { type ButtonProps } from '@fluentui/react-components';\r\nimport { GridButton } from './grid-button.js';\r\nimport type { ReactElement } from 'react';\r\nimport { useCallback, useMemo } from 'react';\r\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\r\nimport {\r\n evaluateGridButtonBehavior,\r\n GridButtonBehavior,\r\n} from './grid-button-behavior.js';\r\nimport {\r\n GridButtonRole,\r\n setGridButtonRole,\r\n type GridButtonRoleProps,\r\n} from './grid-button-role.js';\r\n\r\n/**\r\n * Mutable context handed to a `<NavigateRecordGridButton>`'s\r\n * `onClick` callback. Mirrors Blazor's `NavigateGridButtonContext`.\r\n * The callback can rewrite `url` before navigation fires — useful\r\n * for view-aware routing (e.g. switch destination based on the\r\n * grid's active view's table).\r\n */\r\nexport interface NavigateRecordGridButtonContext {\r\n /** Grid context value at the moment of the click. */\r\n readonly gridContext: GridContextValue;\r\n /**\r\n * Resolved URL — `{0}` placeholder has already been replaced with\r\n * the comma-joined selected record ids. Mutable: assign to override\r\n * before navigation fires.\r\n */\r\n url: string;\r\n}\r\n\r\nexport interface NavigateRecordGridButtonProps extends GridButtonRoleProps {\r\n /**\r\n * Destination URL. Supports `{0}` as a placeholder — replaced with\r\n * the comma-joined ids of selected records before navigation fires.\r\n * When the selection is empty the placeholder collapses to an empty\r\n * string; pair with `buttonEnabledBehavior` if that's not the\r\n * desired outcome.\r\n */\r\n url: string;\r\n /** Required label shown on the button. */\r\n title: string;\r\n /**\r\n * Optional leading icon — pass an instance of a Fluent UI icon\r\n * (e.g. `<Eye20Regular />`). Defaults to no icon.\r\n */\r\n icon?: ReactElement;\r\n /**\r\n * Selection-based enabled mode. Defaults to {@link GridButtonBehavior.Always}.\r\n */\r\n buttonEnabledBehavior?: GridButtonBehavior;\r\n /**\r\n * Selection-based visibility mode. Defaults to {@link GridButtonBehavior.Always}.\r\n * When the resolved predicate is `false`, the button renders `null`\r\n * (no DOM, no disabled-style noise).\r\n */\r\n buttonVisibleBehavior?: GridButtonBehavior;\r\n /**\r\n * Hard override that forces the button enabled / disabled regardless\r\n * of the resolved {@link buttonEnabledBehavior}. `undefined` means\r\n * \"honor the behavior\".\r\n */\r\n enabled?: boolean;\r\n /**\r\n * Fires before navigation, with a mutable context the handler can\r\n * read or rewrite. Useful for view-aware routing (`/customer/{0}`\r\n * vs `/account/{0}` based on the active view's table). Async — the\r\n * navigation waits for the promise.\r\n */\r\n onClick?: (\r\n ctx: NavigateRecordGridButtonContext,\r\n ) => void | Promise<void>;\r\n /**\r\n * Override the navigation strategy. By default the button calls\r\n * `window.location.assign(url)`. Consumers using react-router\r\n * should pass `(url) => navigate(url)` to keep navigation in-app.\r\n */\r\n onNavigate?: (url: string) => void;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\r\n appearance?: ButtonProps['appearance'];\r\n}\r\n\r\n/**\r\n * Generic grid toolbar navigation button. Mirrors Blazor's\r\n * `<NavigateRecordGridButton>` — useful for navigation actions that\r\n * don't fit `<NavigateNewRecordGridButton>` / `<NavigateOpenRecordGridButton>`:\r\n * \"View Details\", \"Print\", \"Open Audit Log\", etc.\r\n *\r\n * Renders nothing when {@link buttonVisibleBehavior} doesn't match the\r\n * current selection — same semantic Blazor uses (clean DOM, no\r\n * disabled-style ghosting). Default role is `'other'` so this button\r\n * doesn't get drafted into the grid's row-click dispatch; pass\r\n * `gridButtonRole=\"open\"` to override (e.g. when the URL leads to an\r\n * edit page and the consumer wants the primary-name cell to route\r\n * here).\r\n *\r\n * ```tsx\r\n * <NavigateRecordGridButton\r\n * title=\"View details\"\r\n * url=\"/contacts/{0}/details\"\r\n * icon={<Eye20Regular />}\r\n * buttonEnabledBehavior={GridButtonBehavior.OnlyOneSelected}\r\n * />\r\n * ```\r\n */\r\nexport function NavigateRecordGridButton({\r\n url,\r\n title,\r\n icon,\r\n buttonEnabledBehavior = GridButtonBehavior.Always,\r\n buttonVisibleBehavior = GridButtonBehavior.Always,\r\n enabled,\r\n onClick,\r\n onNavigate,\r\n appearance,\r\n}: NavigateRecordGridButtonProps) {\r\n const grid = useGridContext();\r\n const selectionCount = grid.selectedRecords.length;\r\n\r\n const visible = useMemo(\r\n () => evaluateGridButtonBehavior(buttonVisibleBehavior, selectionCount),\r\n [buttonVisibleBehavior, selectionCount],\r\n );\r\n const behaviorEnabled = useMemo(\r\n () => evaluateGridButtonBehavior(buttonEnabledBehavior, selectionCount),\r\n [buttonEnabledBehavior, selectionCount],\r\n );\r\n const resolvedEnabled = enabled ?? behaviorEnabled;\r\n\r\n const handleClick = useCallback(async () => {\r\n // Substitute `{0}` with the comma-joined selected ids. Empty\r\n // selection collapses to an empty string — matches Blazor's\r\n // `string.Format(\"...{0}...\", string.Join(\",\", ids))` when `ids`\r\n // is empty. Strip braces (Dataverse sometimes round-trips\r\n // `{guid}` formatting) so URLs stay clean.\r\n const ids = grid.selectedRecords\r\n .map((r) => (r.id ?? '').replace(/[{}]/g, ''))\r\n .filter((id) => id.length > 0)\r\n .join(',');\r\n const seedUrl = url.replace(/\\{0\\}/g, ids);\r\n\r\n const context: NavigateRecordGridButtonContext = {\r\n gridContext: grid,\r\n url: seedUrl,\r\n };\r\n if (onClick) await onClick(context);\r\n\r\n if (!context.url) return;\r\n if (onNavigate) {\r\n onNavigate(context.url);\r\n } else {\r\n window.location.assign(context.url);\r\n }\r\n }, [grid, url, onClick, onNavigate]);\r\n\r\n if (!visible) return null;\r\n\r\n return (\r\n <GridButton\r\n label={title}\r\n onClick={handleClick}\r\n disabled={!resolvedEnabled}\r\n {...(icon !== undefined && { icon })}\r\n {...(appearance !== undefined && { appearance })}\r\n />\r\n );\r\n}\r\n\r\n// Default role is \"other\" — the catch-all button doesn't have a\r\n// pre-defined record-action semantic. Consumers wiring it as the\r\n// edit-style action can override at the call site:\r\n// `<NavigateRecordGridButton gridButtonRole=\"open\" .../>`.\r\nsetGridButtonRole(NavigateRecordGridButton, GridButtonRole.Other);\r\n","import { type ButtonProps } from '@fluentui/react-components';\r\nimport { ArrowClockwise24Regular } from '@fluentui/react-icons';\r\nimport { useT, useWarnAboutUnsavedChanges } from '@powerportalspro/react';\r\nimport { useCallback } from 'react';\r\nimport { type GridContextValue } from '../grids/grid-context.js';\r\nimport { GridButton } from './grid-button.js';\r\n\r\nexport interface RefreshGridButtonProps {\r\n /** Force the button into a disabled state. */\r\n disabled?: boolean;\r\n /**\r\n * Called before `refresh()` runs. Throw to cancel. Errors here are\r\n * surfaced through {@link onError} when wired, otherwise re-thrown.\r\n */\r\n onBeforeRefresh?: () => void | Promise<void>;\r\n /** Called after the refresh resolves successfully. */\r\n onRefreshed?: () => void | Promise<void>;\r\n /**\r\n * Called when refresh (or any lifecycle hook) throws. When omitted the\r\n * error is re-thrown so it can be caught by an upstream error boundary\r\n * or surface in the browser console.\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n /** Custom button label. Defaults to the localized `\"Refresh\"`. */\r\n label?: string;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\r\n appearance?: ButtonProps['appearance'];\r\n}\r\n\r\n/**\r\n * Reload the grid's current page / sort / search via the grid context.\r\n * Mirrors Blazor's built-in refresh toolbar button. Always enabled (a\r\n * refresh is meaningful even on an unchanged view); the\r\n * working-state coalescing that prevents rapid double-click round-trips\r\n * lives on the underlying {@link GridButton} primitive.\r\n */\r\nexport function RefreshGridButton({\r\n disabled,\r\n onBeforeRefresh,\r\n onRefreshed,\r\n onError,\r\n label,\r\n appearance,\r\n}: RefreshGridButtonProps) {\r\n const t = useT();\r\n const warnAboutUnsavedChanges = useWarnAboutUnsavedChanges();\r\n const resolvedLabel = label ?? t('app.buttons.refresh.label');\r\n\r\n const handleClick = useCallback(\r\n async (grid: GridContextValue) => {\r\n // Prompt before refresh when the grid has pending row changes\r\n // (queued creates / updates from dialog-based New / Edit) so we\r\n // don't silently discard the user's work. On confirm, drop the\r\n // queues — refreshing without clearing them would leave stale\r\n // pending changes attached to refreshed rows that may no longer\r\n // match. Mirrors the built-in refresh icon button on\r\n // `<MainGrid>` and `RefreshContextButton`'s page-level prompt.\r\n if (grid.isDirty) {\r\n const proceed = await warnAboutUnsavedChanges();\r\n if (!proceed) return;\r\n grid.clearPendingRowUpdates();\r\n }\r\n if (onBeforeRefresh) await onBeforeRefresh();\r\n await grid.refresh();\r\n if (onRefreshed) await onRefreshed();\r\n },\r\n [warnAboutUnsavedChanges, onBeforeRefresh, onRefreshed],\r\n );\r\n\r\n return (\r\n <GridButton\r\n label={resolvedLabel}\r\n icon={<ArrowClockwise24Regular />}\r\n onClick={handleClick}\r\n {...(appearance !== undefined && { appearance })}\r\n {...(disabled !== undefined && { disabled })}\r\n {...(onError !== undefined && { onError })}\r\n />\r\n );\r\n}\r\n","import { type ButtonProps } from '@fluentui/react-components';\nimport { GridButton } from './grid-button.js';\nimport { Add20Regular } from '@fluentui/react-icons';\nimport { TableSecurityPermission } from '@powerportalspro/core';\nimport { useT } from '@powerportalspro/react';\nimport { useCallback } from 'react';\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\nimport { GridButtonRole, setGridButtonRole } from './grid-button-role.js';\n\n/**\n * Mutable context handed to a `<NavigateNewRecordGridButton>`'s\n * `onClick` callback. Mirrors Blazor's `NavigateGridButtonContext`.\n * The callback can rewrite `url` before navigation fires.\n */\nexport interface NavigateNewRecordGridButtonContext {\n /** Grid context value at the moment of the click. */\n readonly gridContext: GridContextValue;\n /**\n * Resolved URL — the value of {@link NavigateNewRecordGridButtonProps.url}\n * (or the result of the function form). Mutable: assign to override\n * before navigation fires.\n */\n url: string;\n}\n\nexport interface NavigateNewRecordGridButtonProps {\n /**\n * Destination URL to navigate to. Either a static string (\"/contacts/new\")\n * or a function that receives the grid context and returns the URL\n * (useful when the URL depends on the active view, current tableName,\n * or other grid state).\n */\n url: string | ((ctx: GridContextValue) => string);\n /**\n * Fires before navigation, with a mutable context the handler can\n * read or rewrite. Async — navigation waits for the promise. Assign\n * `ctx.url = ''` to cancel navigation entirely.\n */\n onClick?: (\n ctx: NavigateNewRecordGridButtonContext,\n ) => void | Promise<void>;\n /**\n * Override the navigation strategy. By default the button calls\n * `window.location.assign(url)` — a hard navigation. Consumers using\n * a client-side router (react-router) should pass\n * `(url) => navigate(url)` to keep the SPA navigation in-app.\n */\n onNavigate?: (url: string) => void;\n /** Force the button into a disabled state regardless of selection. */\n disabled?: boolean;\n /** Custom button label. Defaults to the localized `\"New\"`. */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n}\n\n/**\n * Grid toolbar button that navigates to a URL for creating a new record.\n * Mirrors Blazor's `NavigateNewRecordGridButton` — disabled when one or\n * more records are selected (the \"new record\" intent doesn't combine with\n * an existing-record selection), AND when the current user lacks the\n * `Create` permission on the grid's table (sampled from\n * `useGridContext().tablePermissions`). When the grid is empty (no rows\n * to sample from), the button stays enabled — the server still enforces\n * the permission on the create attempt.\n */\nexport function NavigateNewRecordGridButton({\n url,\n onClick,\n onNavigate,\n disabled,\n label,\n appearance,\n}: NavigateNewRecordGridButtonProps) {\n const grid = useGridContext();\n const t = useT();\n const resolvedLabel = label ?? t('app.buttons.new.label');\n\n // Permission gate: only flag the lookup as denied when we have a\n // sampled permission bitmask to read from. `undefined` (empty grid /\n // pre-load) optimistically keeps the button enabled — the server\n // enforces on the create call. Mirrors Blazor's\n // `_isCreateAllowedForUser` default-true behavior while the lookup\n // resolves.\n const sampledPermissions = grid.tablePermissions;\n const createDeniedByPermissions =\n sampledPermissions !== undefined &&\n (sampledPermissions & TableSecurityPermission.Create) !== TableSecurityPermission.Create;\n\n // Disabled when records are selected — matches Blazor's NoneSelected\n // behavior on this button. The toolbar layout stays put (button is\n // visible but greyed) rather than jumping when selection changes.\n const isDisabled =\n disabled === true ||\n grid.selectedRecords.length > 0 ||\n createDeniedByPermissions;\n\n const handleClick = useCallback(async () => {\n const resolved = typeof url === 'function' ? url(grid) : url;\n const context: NavigateNewRecordGridButtonContext = {\n gridContext: grid,\n url: resolved,\n };\n if (onClick) await onClick(context);\n if (!context.url) return;\n if (onNavigate) {\n onNavigate(context.url);\n } else {\n window.location.assign(context.url);\n }\n }, [url, onClick, onNavigate, grid]);\n\n return (\n <GridButton\n label={resolvedLabel}\n icon={<Add20Regular />}\n onClick={handleClick}\n disabled={isDisabled}\n {...(appearance !== undefined && { appearance })}\n />\n );\n}\n\nsetGridButtonRole(NavigateNewRecordGridButton, GridButtonRole.Create);\n","import { type ButtonProps } from '@fluentui/react-components';\r\nimport { GridButton } from './grid-button.js';\r\nimport { Edit20Regular } from '@fluentui/react-icons';\r\nimport type { components } from '@powerportalspro/core';\r\nimport { useT } from '@powerportalspro/react';\r\nimport { useCallback, useEffect } from 'react';\r\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\r\nimport { GridButtonRole, setGridButtonRole } from './grid-button-role.js';\r\n\r\ntype TableRecord = components['schemas']['TableRecord'];\r\n\r\n/**\r\n * Mutable context handed to a `<NavigateOpenRecordGridButton>`'s\r\n * `onClick` callback. Mirrors Blazor's `NavigateGridButtonContext`.\r\n * The callback can rewrite `url` before navigation fires — useful for\r\n * view-aware routing (e.g. switch the destination based on the active\r\n * view's table).\r\n */\r\nexport interface NavigateOpenRecordGridButtonContext {\r\n /** Grid context value at the moment of the click. */\r\n readonly gridContext: GridContextValue;\r\n /** The record that's being opened. */\r\n readonly record: TableRecord;\r\n /**\r\n * Resolved URL — the result of {@link NavigateOpenRecordGridButtonProps.urlFor}.\r\n * Mutable: assign to override before navigation fires (assign `''` to\r\n * cancel navigation entirely).\r\n */\r\n url: string;\r\n}\r\n\r\nexport interface NavigateOpenRecordGridButtonProps {\r\n /**\r\n * Builds the destination URL from the selected record. Called with the\r\n * single selected row and the grid context; return the URL string the\r\n * browser should navigate to. Receives both so callers can compose\r\n * `/contacts/${record.id}/edit` style URLs or include the active view\r\n * id / tableName as query parameters.\r\n */\r\n urlFor: (record: TableRecord, ctx: GridContextValue) => string;\r\n /**\r\n * Fires before navigation, with a mutable context the handler can\r\n * read or rewrite. Async — navigation waits for the promise. Assign\r\n * `ctx.url = ''` to cancel navigation entirely. Fires for BOTH the\r\n * toolbar button click AND the grid's primary-name link click (the\r\n * Open intent published via `registerOpenHandler`), so view-aware\r\n * URL rewriting applies uniformly.\r\n */\r\n onClick?: (\r\n ctx: NavigateOpenRecordGridButtonContext,\r\n ) => void | Promise<void>;\r\n /**\r\n * Override the navigation strategy. By default the button calls\r\n * `window.location.assign(url)`. Consumers using react-router should\r\n * pass `(url) => navigate(url)` to keep navigation in-app.\r\n */\r\n onNavigate?: (url: string) => void;\r\n /** Force the button into a disabled state regardless of selection. */\r\n disabled?: boolean;\r\n /** Custom button label. Defaults to the localized `\"Edit\"`. */\r\n label?: string;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\r\n appearance?: ButtonProps['appearance'];\r\n}\r\n\r\n/**\r\n * Grid toolbar button that navigates to a URL for editing the currently-\r\n * selected record. Mirrors Blazor's `NavigateOpenRecordGridButton` — only\r\n * enabled when exactly ONE record is selected, since navigation targets a\r\n * single record's edit page (multi-record edit isn't a meaningful URL).\r\n */\r\nexport function NavigateOpenRecordGridButton({\r\n urlFor,\r\n onClick,\r\n onNavigate,\r\n disabled,\r\n label,\r\n appearance,\r\n}: NavigateOpenRecordGridButtonProps) {\r\n const grid = useGridContext();\r\n const t = useT();\r\n const resolvedLabel = label ?? t('app.buttons.open.label');\r\n\r\n // OnlyOneSelected behavior — disabled for 0 selections (no target) and\r\n // for 2+ (multi-record edit not supported by a single URL).\r\n const isDisabled = disabled === true || grid.selectedRecords.length !== 1;\r\n\r\n // Builds + executes the navigation for a given record. Extracted so the\r\n // toolbar click handler (uses `grid.selectedRecords[0]`) and the\r\n // grid's primary-name-cell link (uses the row's own record) both go\r\n // through the same logic.\r\n const navigateForRecord = useCallback(\r\n async (record: TableRecord) => {\r\n const resolved = urlFor(record, grid);\r\n const context: NavigateOpenRecordGridButtonContext = {\r\n gridContext: grid,\r\n record,\r\n url: resolved,\r\n };\r\n if (onClick) await onClick(context);\r\n if (!context.url) return;\r\n if (onNavigate) {\r\n onNavigate(context.url);\r\n } else {\r\n window.location.assign(context.url);\r\n }\r\n },\r\n [urlFor, onClick, onNavigate, grid],\r\n );\r\n\r\n // Publish the navigation as the grid's open handler. Always registers\r\n // now that `GridButtonRole` carries intent at the component-type level\r\n // — consumers wanting to suppress the registration should pick a\r\n // different component (or render a plain `<Button>`). Pending-create\r\n // rows don't have a real id, so they're routed to the create button's\r\n // re-open path by the grid's dispatch — this handler only ever sees\r\n // persisted rows. The registry in MainGrid holds the handler in a\r\n // ref so handler-identity churn (inline `urlFor` props, context-value\r\n // changes from selection / refetch) doesn't loop renders through the\r\n // context — see `registerOpenHandler` in `main-grid.tsx`.\r\n const { registerOpenHandler } = grid;\r\n useEffect(() => {\r\n return registerOpenHandler(navigateForRecord);\r\n }, [registerOpenHandler, navigateForRecord]);\r\n\r\n const handleClick = useCallback(() => {\r\n const record = grid.selectedRecords[0];\r\n if (!record) return;\r\n navigateForRecord(record);\r\n }, [grid, navigateForRecord]);\r\n\r\n return (\r\n <GridButton\r\n label={resolvedLabel}\r\n icon={<Edit20Regular />}\r\n onClick={handleClick}\r\n disabled={isDisabled}\r\n {...(appearance !== undefined && { appearance })}\r\n />\r\n );\r\n}\r\n\r\nsetGridButtonRole(NavigateOpenRecordGridButton, GridButtonRole.Open);\r\n","import {\n Button,\n Dialog,\n DialogActions,\n DialogBody,\n DialogContent,\n DialogSurface,\n DialogTitle,\n Spinner,\n makeStyles,\n tokens,\n type ButtonProps,\n type DialogOpenChangeData,\n type DialogOpenChangeEvent,\n} from '@fluentui/react-components';\nimport { Edit20Regular } from '@fluentui/react-icons';\nimport type { components } from '@powerportalspro/core';\nimport {\n QueryStatus,\n RecordContext,\n useDialogService,\n useMainContext,\n usePowerPortalsPro,\n useRecordContext,\n useRunWithOverlay,\n useT,\n useValidationContext,\n} from '@powerportalspro/react';\nimport { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';\nimport { useGridContext } from '../grids/grid-context.js';\nimport { GridButton } from './grid-button.js';\nimport { GridButtonBehavior } from './grid-button-behavior.js';\nimport { GridButtonRole, setGridButtonRole } from './grid-button-role.js';\n\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\ntype OrganizationRequest = components['schemas']['OrganizationRequest'];\ntype TableRecord = components['schemas']['TableRecord'];\n\nconst useDialogStyles = makeStyles({\n // Loading state replaces the form entirely while the record fetch is in\n // flight — NOT a scrim over the form, because the per-column edit\n // components mount and run their validators on first render. If they\n // mount against an undefined record they immediately report\n // \"required field empty\" errors that flicker visible for the duration\n // of the load. Gating on `status === Success` keeps the form out of\n // the tree until there's a real record to bind to.\n loadingArea: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '160px',\n rowGap: tokens.spacingVerticalM,\n },\n});\n\n/**\n * How an `<OpenRecordGridButton>` dialog commits its edits when the user\n * clicks Save:\n *\n * - **`withGridContext`** (default) — Queues the dirty columns into the\n * grid's pending-updates registry. The surrounding `<MainContext>` /\n * `<RecordContext>` picks them up in its next transactional save (one\n * `executeMultiple` round-trip covers the record-context's own update\n * plus every queued row in the grid). Use when the grid lives inside a\n * record-edit page and the user expects \"save the whole page\" semantics.\n * - **`immediately`** — Issues the update right away through the standard\n * RecordContext save flow, then refreshes the grid. Use for standalone\n * grid pages where each Edit is its own commit.\n *\n * Mirrors Blazor's `GridActionBehavior.WithGridContext` / `.Immediately`\n * enum (`OpenRecordGridButton<TForm>.Behavior` parameter).\n */\nexport type OpenRecordGridButtonBehavior = 'withGridContext' | 'immediately';\n\n/**\n * Where the dialog renders. `center` is a modal in the middle of the\n * viewport; `panel` slides in from the right edge. Mirrors Blazor's\n * `DialogLocation.Center` / `.Panel`.\n *\n * Implementation note: `panel` is approximated via Fluent's standard\n * `Dialog` with a `dialogSurface` style override that pins it to the\n * right edge — Fluent's React `Dialog` doesn't ship a first-class panel\n * variant the way Blazor's `FluentDialog` does.\n */\nexport type DialogLocation = 'center' | 'panel';\n\nexport interface OpenRecordGridButtonProps {\n /**\n * Form fields rendered inside the dialog. Receives no arguments — the\n * standard edit components (`<TextEdit>`, `<ChoiceEdit>`, etc.) read the\n * record + dirty state from the dialog's internal `<RecordContext>` via\n * `useRecordContext`, the same way they do on a record-edit page. So the\n * consumer just lists the columns they want editable.\n */\n children: ReactNode | (() => ReactNode);\n /** See {@link OpenRecordGridButtonBehavior}. Defaults to `withGridContext`. */\n behavior?: OpenRecordGridButtonBehavior;\n /** See {@link DialogLocation}. Defaults to `center`. */\n location?: DialogLocation;\n /** Force the button into a disabled state regardless of selection. */\n disabled?: boolean;\n /**\n * Custom button label. When omitted, the label tracks the current\n * selection: `\"Open\"` (the localized `app.buttons.open.label`) when one\n * record is selected, `\"Edit\"` (`app.buttons.edit.label`) when more\n * than one is selected — reflecting the dialog's two flavors (single-\n * record open vs. broadcast-to-many edit). Pass a string to lock the\n * label regardless of selection.\n */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n /**\n * Optional dialog title override. Defaults to the selected record's\n * primary-name column value when available, falling back to a generic\n * \"Open record\" label.\n */\n dialogTitle?: string;\n /**\n * Called after a successful commit (whether queued or immediate). For\n * `immediately`, fires after the server round-trip and grid refresh; for\n * `withGridContext`, fires after the queue is updated (no server hit yet).\n */\n onSaved?: () => void | Promise<void>;\n /**\n * Called when commit (or its in-flight save / validation step) throws.\n * When provided, takes over error display — the dialog service is not\n * called. When omitted, errors surface via `dialog.showErrorAsync`.\n */\n onError?: (error: unknown) => void | Promise<void>;\n /**\n * When `true` (default), the dialog's Save button runs the per-column\n * validation gate before committing — same pre-flight `SaveContextButton`\n * uses. Set `false` to bypass the gate and submit whatever values the\n * form holds; server-side validation still applies. Overrides the\n * surrounding `<MainContext>` / `<RecordContext>`'s\n * `forceSuccessfulValidationBeforeSave` cascade for the scope of this\n * button.\n *\n * In multi-record edit mode (more than one row selected), the dialog\n * is bound to an empty record, so validation against required-empty\n * columns would always fail — this prop is forced to `false`\n * internally for that mode regardless of consumer setting.\n */\n forceSuccessfulValidationBeforeSave?: boolean;\n}\n\n/**\n * Grid toolbar button that opens a dialog form for editing the currently\n * selected record(s). Mirrors Blazor's `<OpenRecordGridButton TForm=\"...\" />`\n * minus the `TForm` type parameter (React composes via children instead).\n *\n * Two flavors driven by the current selection size:\n *\n * - **Single record selected** — opens the form bound to a fresh fetch of\n * that record, with any already-queued dirty columns from a prior dialog\n * session pre-applied. Save commits just this record.\n * - **Multiple records selected** — opens the form bound to an EMPTY record\n * (only `tableName` populated). The user fills in whichever columns they\n * want to apply, and Save broadcasts those same dirty columns to every\n * selected record. Untouched columns leave each target row alone.\n * Mirrors Blazor's multi-edit flow in\n * `OpenRecordGridButton.razor.cs::OnSaveClicked`.\n *\n * The Open button gates on Read permission (not Write); per-column Write\n * enforcement happens inside the form via the bound record's `permissions`\n * mask. In multi-edit the bound record carries no permissions, so the\n * server-side write check is the authority — a user without Write to any\n * column on a target record gets a 403 on Save.\n *\n * Identifies itself as the grid's \"edit\" handler via the same registry\n * `<NavigateOpenRecordGridButton>` uses — so the primary-name cell renders\n * as a clickable link that opens this dialog (when\n * `<MainGrid allowNavigateOnPrimaryNameClick>` is on, the default).\n */\nexport function OpenRecordGridButton({\n children,\n behavior = 'withGridContext',\n location = 'center',\n disabled,\n label,\n appearance,\n dialogTitle,\n onSaved,\n onError,\n forceSuccessfulValidationBeforeSave,\n}: OpenRecordGridButtonProps) {\n const grid = useGridContext();\n const t = useT();\n // Label tracks selection size when the consumer hasn't pinned one:\n // single-row → \"Open\" (the dialog opens against that one record);\n // multi-row → \"Edit\" (the dialog broadcasts the form's dirty columns\n // to every selected record). Matches the action verb the dialog will\n // actually perform.\n const resolvedLabel =\n label ??\n (grid.selectedRecords.length > 1\n ? t('app.buttons.edit.label')\n : t('app.buttons.open.label'));\n\n // Active record(s) being edited. Two mutually-exclusive modes:\n // - Single edit: `recordToEdit` holds the row, `multiEditRecords` is null.\n // - Multi edit: `multiEditRecords` is the selected set, `recordToEdit` is null.\n // `null`/empty in both means no dialog is open. The dialog branches off\n // these in the render below.\n const [recordToEdit, setRecordToEdit] = useState<TableRecord | null>(null);\n const [multiEditRecords, setMultiEditRecords] = useState<readonly TableRecord[] | null>(null);\n\n const selected = grid.selectedRecords[0];\n // Pending-create rows are client-side only — no server permissions are\n // stamped on them, since they haven't been committed yet. Detected by\n // the synthetic id prefix MainGrid uses when prepending pending creates.\n // Routed to `<NewRecordGridButton>`'s re-open path via `dispatchOpen`\n // so the user keeps editing the in-progress new record through the\n // same form/wizard it was created in.\n const isSelectedPendingCreate =\n !!selected?.id && selected.id.startsWith('__pending-create-');\n // Pending in selection disables multi-edit — a mixed set has no\n // well-defined save semantics (the create flow and the bulk-update\n // flow target different request types). Single-pending is fine; it\n // just routes through `dispatchOpen` to the create button below.\n const anySelectedPendingCreate = grid.selectedRecords.some(\n (r) => !!r.id && r.id.startsWith('__pending-create-'),\n );\n // The Open button gates on **Read** permission, not Write. The button\n // just opens the form — once it's open, the form's per-column edit\n // components enforce readonly state from the record's Write bit (see\n // `resolveEditState` in `metadata-helpers.ts`). A user with Read but\n // not Write can still open the form to view fields they can't edit.\n //\n // For multi-edit (selectedRecords.length > 1), gate on every selected\n // record having Read so the user can't bulk-edit a set that contains\n // records they can't even see the form for. Pending-create rows can't\n // be part of a multi-selection (they don't get checked when the user\n // marks them — selection is server-record-only), so the all-records\n // check below is safe to apply unconditionally.\n //\n // TableSecurityPermission.Read = 1 in the wire enum. Inlined as a literal\n // to keep the import surface minimal.\n const hasReadOnAllSelected =\n grid.selectedRecords.length > 0 &&\n grid.selectedRecords.every(\n (r) =>\n !!r.id && !!r.tableName && !!r.permissions && (r.permissions & 1) === 1,\n );\n const canOpenSelected =\n grid.selectedRecords.length === 1\n ? isSelectedPendingCreate ||\n (!!selected?.id &&\n !!selected?.tableName &&\n !!selected?.permissions &&\n (selected.permissions & 1) === 1)\n : hasReadOnAllSelected && !anySelectedPendingCreate;\n const isDisabled =\n disabled === true || grid.selectedRecords.length === 0 || !canOpenSelected;\n\n // Toolbar click: open against the current selection. Three branches:\n // 1. N=1 + pending → defer to the grid's create-reopen dispatch so\n // the originating create form/wizard owns the edit.\n // 2. N=1 + persisted → open the standard single-record edit dialog.\n // 3. N>1 (all persisted; pending case is disabled above) → multi-\n // edit dialog whose dirty columns broadcast to every selected row.\n const openForSelected = useCallback(() => {\n if (grid.selectedRecords.length === 1 && selected) {\n if (isSelectedPendingCreate) {\n grid.dispatchOpen(selected);\n return;\n }\n setRecordToEdit(selected);\n } else if (grid.selectedRecords.length > 1) {\n setMultiEditRecords(grid.selectedRecords);\n }\n }, [grid, selected, isSelectedPendingCreate]);\n\n // Primary-name link / double-click dispatch. The grid's `dispatchOpen`\n // routes pending-create rows to the `<NewRecordGridButton>` re-open\n // path before reaching here, so this only ever sees persisted rows.\n // Skip anything without a real id defensively (a row stripped of\n // tableName / id shouldn't open at all).\n const openForRecord = useCallback((record: TableRecord) => {\n if (!record.id || !record.tableName) return;\n if (record.id.startsWith('__pending-create-')) return;\n setRecordToEdit(record);\n }, []);\n\n const closeDialog = useCallback(() => {\n setRecordToEdit(null);\n setMultiEditRecords(null);\n }, []);\n\n // Conditional-spread payload for the per-button validation override —\n // `exactOptionalPropertyTypes: true` rejects passing `undefined` into an\n // optional `T?` slot, so we either pass the prop or omit it entirely.\n // When omitted, DialogInner falls back to the cascaded value from the\n // surrounding context.\n const validationOverride: { forceSuccessfulValidationBeforeSave?: boolean } =\n forceSuccessfulValidationBeforeSave !== undefined\n ? { forceSuccessfulValidationBeforeSave }\n : {};\n\n // Stable empty-record reference for the multi-edit branch. RecordContext's\n // create-mode seeding effect (`record-context.tsx`'s\n // `[isCreateMode, initialRecord, table]` deps) wipes `pendingChanges`\n // whenever `initialRecord` changes identity — so a fresh `{ tableName: …\n // }` literal each render would clear the form on every keystroke. Memoed\n // off the active multi-edit selection's tableName so it only re-creates\n // when the consumer opens multi-edit against a different table (rare).\n const multiEditTableName = multiEditRecords?.[0]?.tableName;\n const multiEditInitialRecord = useMemo<TableRecord | undefined>(\n () => (multiEditTableName ? { tableName: multiEditTableName } : undefined),\n [multiEditTableName],\n );\n\n // Register as the grid's open handler — fires when `dispatchOpen` sees\n // a persisted row (real Dataverse id). Pending-create rows are routed\n // to `<NewRecordGridButton>`'s reopen path via the `'create'`-role\n // registration instead, so this handler never has to deal with them.\n const { registerOpenHandler } = grid;\n useEffect(() => {\n return registerOpenHandler(openForRecord);\n }, [registerOpenHandler, openForRecord]);\n\n // Controlled Dialog so we can close deterministically on Save / Cancel.\n // The uncontrolled flavor would dismiss on overlay-click — which we\n // want to suppress when there are pending edits in the form.\n const handleOpenChange = useCallback(\n (_evt: DialogOpenChangeEvent, data: DialogOpenChangeData) => {\n if (!data.open) {\n setRecordToEdit(null);\n setMultiEditRecords(null);\n }\n },\n [],\n );\n\n const open = recordToEdit !== null || multiEditRecords !== null;\n\n const dialogSurfaceStyle =\n location === 'panel'\n ? {\n margin: 0,\n marginLeft: 'auto',\n maxWidth: '480px',\n width: '100%',\n height: '100vh',\n maxHeight: '100vh',\n borderRadius: 0,\n }\n : undefined;\n\n return (\n <>\n <GridButton\n label={resolvedLabel}\n icon={<Edit20Regular />}\n onClick={openForSelected}\n visibilityBehavior={GridButtonBehavior.OneOrMoreSelected}\n disabled={isDisabled}\n {...(appearance !== undefined && { appearance })}\n />\n {open && recordToEdit?.id && recordToEdit.tableName && (\n // Standard edit branch — RecordContext fetches the record from\n // the server and DialogInner routes the save through the normal\n // update queue OR immediate save flow.\n //\n // Any already-queued dirty cols from a prior dialog session land\n // on RecordContext as `initialPendingChanges`. RecordContext seeds\n // `pendingChanges` with them after the server load — so\n // `loadedRecord` stays as the unmodified server fetch and\n // `pendingChanges` carries the work-in-progress. The crucial\n // property: when the user reverts a column back to the server\n // value in the dialog, `setColumnValue`'s baseline-match check\n // sees `loadedRecord[col]` and `newValue` collide and drops the\n // column from `pendingChanges`. Dialog Save then ships an empty\n // dirty set and the queue entry gets deleted — net-zero edits\n // resolve cleanly. Mirrors Blazor's\n // `OpenRecordGridButton.razor.cs` flow: load fresh, copy\n // `GetChangedProperties` over, `ResetState()` to mark clean.\n <Dialog open={open} onOpenChange={handleOpenChange} modalType=\"modal\">\n <DialogSurface style={dialogSurfaceStyle}>\n <RecordContext\n table={recordToEdit.tableName}\n id={recordToEdit.id}\n {...((): {\n initialPendingChanges?: ReadonlyMap<string, ColumnValueBase | null>;\n } => {\n // Conditional spread — `exactOptionalPropertyTypes: true`\n // rejects passing `T | undefined` into an optional `T?`\n // slot, so omit the prop entirely when there's no\n // queued update for this row instead of passing\n // `initialPendingChanges={undefined}`.\n const queued = grid.pendingRowUpdates.get(recordToEdit.id);\n return queued ? { initialPendingChanges: queued.dirty } : {};\n })()}\n >\n <DialogInner\n behavior={behavior}\n gridTableName={recordToEdit.tableName}\n gridRecordId={recordToEdit.id}\n dialogTitle={dialogTitle}\n onClose={closeDialog}\n onSaved={onSaved}\n onError={onError}\n queueRowUpdate={grid.queueRowUpdate}\n gridRefresh={grid.refresh}\n {...validationOverride}\n >\n {typeof children === 'function' ? children() : children}\n </DialogInner>\n </RecordContext>\n </DialogSurface>\n </Dialog>\n )}\n {open && multiEditRecords && multiEditRecords.length > 0 && multiEditRecords[0]?.tableName && (\n // Multi-edit branch — bound to an empty record (`tableName` only,\n // no id). RecordContext sees the missing id and enters create\n // mode, but DialogInner's `multiEditTargets` short-circuits the\n // standard save path: it extracts the dirty columns from the\n // form and broadcasts them to every target record via an\n // ExecuteMultiple batch (immediate mode) or N queueRowUpdate\n // calls (withGridContext mode). The empty `initialRecord` is\n // important — without it, RecordContext would synthesize one\n // and the form would start from server-supplied defaults that\n // we'd then accidentally write to every row. Mirrors Blazor's\n // `OpenRecordGridButton.razor.cs::EditButtonClicked` which\n // builds a bare `new TableRecord { TableName = ... }` for the\n // multi-select branch.\n <Dialog open={open} onOpenChange={handleOpenChange} modalType=\"modal\">\n <DialogSurface style={dialogSurfaceStyle}>\n <RecordContext\n table={multiEditRecords[0].tableName}\n {...(multiEditInitialRecord && { initialRecord: multiEditInitialRecord })}\n >\n <DialogInner\n behavior={behavior}\n gridTableName={multiEditRecords[0].tableName}\n gridRecordId=\"\"\n dialogTitle={dialogTitle}\n onClose={closeDialog}\n onSaved={onSaved}\n onError={onError}\n queueRowUpdate={grid.queueRowUpdate}\n gridRefresh={grid.refresh}\n {...validationOverride}\n multiEditTargets={multiEditRecords}\n >\n {typeof children === 'function' ? children() : children}\n </DialogInner>\n </RecordContext>\n </DialogSurface>\n </Dialog>\n )}\n </>\n );\n}\n\n// Stamp the component's default GridButtonRole at module scope so the\n// grid's child-scan can identify it via `getGridButtonRoleFromElement`\n// without consumers needing to set a prop on every usage.\nsetGridButtonRole(OpenRecordGridButton, GridButtonRole.Open);\n\n/**\n * Inner dialog body — split out so it can call `useRecordContext` (which\n * requires being inside `<RecordContext>`). Owns the Save/Cancel actions\n * and the commit dispatch.\n */\nfunction DialogInner({\n children,\n behavior,\n gridTableName,\n gridRecordId,\n dialogTitle,\n onClose,\n onSaved,\n onError,\n queueRowUpdate,\n gridRefresh,\n multiEditTargets,\n forceSuccessfulValidationBeforeSave,\n}: {\n children: ReactNode;\n behavior: OpenRecordGridButtonBehavior;\n gridTableName: string;\n gridRecordId: string;\n dialogTitle: string | undefined;\n onClose: () => void;\n onSaved: (() => void | Promise<void>) | undefined;\n onError: ((error: unknown) => void | Promise<void>) | undefined;\n queueRowUpdate: (update: {\n tableName: string;\n recordId: string;\n dirty: ReadonlyMap<string, ColumnValueBase | null>;\n }) => void;\n gridRefresh: () => Promise<void>;\n /**\n * Present only when the dialog is editing multiple records at once.\n * The Save handler skips the standard RecordContext save flow and\n * instead extracts the dirty columns from the form and applies them\n * to every target record — via an `executeMultiple` batch (immediate\n * mode) or N `queueRowUpdate` calls (withGridContext mode).\n * `undefined` for single-record edits.\n */\n multiEditTargets?: readonly TableRecord[];\n /**\n * Per-button override of the surrounding context's\n * `forceSuccessfulValidationBeforeSave`. `undefined` means \"inherit\n * from the cascade.\" Multi-edit forces `false` internally regardless\n * of this value.\n */\n forceSuccessfulValidationBeforeSave?: boolean;\n}) {\n const ctx = useRecordContext();\n // Dialog's own RecordContext mounts a nested MainContext — its\n // `forceSuccessfulValidationBeforeSave` is the resolved cascade from\n // the outer page-level context (or `true` at the root). Read from\n // there for the inherited value.\n const innerMainContext = useMainContext();\n const validation = useValidationContext();\n const dialogSvc = useDialogService();\n const runWithOverlay = useRunWithOverlay();\n const { ppp } = usePowerPortalsPro();\n const t = useT();\n const styles = useDialogStyles();\n const [saving, setSaving] = useState(false);\n const isMultiEdit = !!multiEditTargets && multiEditTargets.length > 0;\n // Resolution: multi-edit forces `false` (form is bound to an empty\n // record — required-empty would fail every untouched column);\n // otherwise per-button override > cascade from RecordContext's inner\n // MainContext > root default (true).\n const enforceValidation =\n isMultiEdit\n ? false\n : (forceSuccessfulValidationBeforeSave ??\n innerMainContext?.forceSuccessfulValidationBeforeSave ??\n true);\n\n // Don't render the form children until the record's first load lands.\n // Mounting the edit components against an undefined record fires their\n // per-column validators against an empty value and surfaces transient\n // \"required field\" errors that disappear the moment the data arrives —\n // a jarring flash. Holding the children behind the spinner skips that\n // whole cycle and matches the in-tree experience (`<RecordContext>` on\n // a record-edit page gates its form rendering on `status === Success`\n // the same way).\n const isRecordReady =\n ctx.status === QueryStatus.Success && ctx.record !== undefined;\n\n // Title resolution — explicit prop first, then either \"Edit multiple\"\n // (multi-edit) or the loaded record's primary-name column value, then a\n // generic fallback. Matches Blazor's `OpenRecordGridButton.razor.cs`:\n // title = _localizer[\"app.messages.edit-multiple\"].Value; // multi\n // title = _tableRecord.GetValueOrDefault<StringValue>(...); // single\n //\n // Read from `record.properties[primaryNameColumn]` (the raw StringValue)\n // rather than `formattedValues` — primary-name columns are always\n // strings, and the formatted-values dictionary doesn't always carry a\n // duplicate entry for them (it's primarily for lookups / choices /\n // dates whose display string differs from the wire value). The raw\n // property value is the canonical source either way.\n const primaryNameColumn = ctx.tableMetadata?.primaryNameColumn;\n const primaryNameValue =\n primaryNameColumn !== undefined && primaryNameColumn !== null\n ? (ctx.record?.properties?.[primaryNameColumn] as\n | { $type: number; value?: string | null }\n | undefined)?.value\n : undefined;\n const resolvedTitle =\n dialogTitle ??\n (isMultiEdit ? t('app.messages.edit-multiple') : undefined) ??\n primaryNameValue ??\n t('app.buttons.open.label');\n\n const handleSave = useCallback(async () => {\n setSaving(true);\n try {\n // Per-column validation gate — same pre-flight SaveContextButton\n // uses. Validation errors are surfaced inline by the edit\n // components; we just abort the commit silently here. Skipped when\n // `enforceValidation` resolves to `false` — either because the\n // surrounding context's `forceSuccessfulValidationBeforeSave` is\n // off, the consumer set the per-button override to `false`, or\n // this dialog is in multi-edit mode (which always skips because\n // the form is bound to an empty record and required-empty would\n // fail every untouched column).\n if (enforceValidation && validation && !(await validation.validateAll())) {\n return;\n }\n\n if (multiEditTargets && multiEditTargets.length > 0) {\n // Multi-edit branch — broadcast the form's dirty columns to every\n // selected record. Two sub-flows:\n //\n // - `immediately`: build one UpdateRequest per target and ship\n // via executeMultiple. Wrap in an overlay so the user can't\n // double-click while N round-trips (well, one batched\n // round-trip) are in flight. After success, refresh the grid\n // so the visible cell values pick up the new state.\n // - `withGridContext`: stage one queueRowUpdate per target with\n // the same dirty map. The outer MainContext's transactional\n // save will batch them with anything else dirty in the tree.\n //\n // Mirrors Blazor's `OpenRecordGridButton.razor.cs::OnSaveClicked`\n // multi-record branch (the `foreach (var record in SelectedRecords)`\n // building UpdateRequests or applying changes to grid records).\n const dirty = new Map<string, ColumnValueBase | null>();\n if (ctx.record) {\n for (const columnName of ctx.dirtyColumns) {\n const value = ctx.record.properties?.[columnName];\n dirty.set(columnName, value ?? null);\n }\n }\n // Nothing to apply — short-circuit cleanly. Closing the dialog\n // without firing requests matches \"user opened multi-edit, didn't\n // change anything, hit Save\" → no-op.\n if (dirty.size === 0) {\n if (onSaved) await onSaved();\n onClose();\n return;\n }\n\n if (behavior === 'immediately') {\n await runWithOverlay(\n {\n description:\n multiEditTargets.length === 1\n ? t('app.messages.record-save')\n : t('app.messages.updating-records'),\n },\n async () => {\n const properties: Record<string, ColumnValueBase> = {};\n for (const [col, val] of dirty) {\n if (val !== null) properties[col] = val;\n }\n const requests: OrganizationRequest[] = multiEditTargets.map(\n (target) => ({\n $type: 'UpdateRequest',\n record: {\n tableName: target.tableName,\n ...(target.id !== undefined && { id: target.id }),\n properties,\n },\n }),\n );\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\n },\n );\n await gridRefresh();\n } else {\n // Queue-mode broadcast. Each target gets its own row-update\n // entry so the outer MainContext's `executeMultiple` save\n // covers them alongside any other dirty descendants. The dirty\n // map is shared by reference — each entry reads from the same\n // ReadonlyMap, which is fine since none of the callers mutate\n // it after queueing.\n for (const target of multiEditTargets) {\n if (!target.id || !target.tableName) continue;\n queueRowUpdate({\n tableName: target.tableName,\n recordId: target.id,\n dirty,\n });\n }\n }\n } else if (behavior === 'immediately') {\n // Standard RecordContext save flow → executeMultiple → refetch.\n // Wrap in a global overlay so the user can't double-click while\n // the round-trip is in flight (matches SaveContextButton).\n await runWithOverlay(\n { description: t('app.messages.record-save') },\n () => ctx.save(),\n );\n // Refresh the grid to pick up the updated values in the row.\n await gridRefresh();\n } else {\n // Queue mode: extract the dirty columns + their current values\n // and push into the grid's pending-row-updates registry. The\n // surrounding MainContext's transactional save picks them up via\n // the grid's descendant getRequests. The dialog's RecordContext\n // is discarded here without invoking its save() — we DON'T want\n // a server round-trip yet.\n //\n // ALWAYS call queueRowUpdate, even with an empty dirty map —\n // the queue's own delete-on-empty rule resolves the net-zero\n // case (user reverted everything back to server values across\n // one-or-more sessions) by dropping the row's queue entry.\n // Don't skip the call here: skipping would leave a stale entry\n // from a prior session in the queue.\n const dirty = new Map<string, ColumnValueBase | null>();\n if (ctx.record) {\n for (const columnName of ctx.dirtyColumns) {\n // `record.properties[col]` is the merged view (loaded baseline\n // + pending edit), so reading from there gives the new value\n // the user wants persisted. `undefined` becomes null (explicit\n // clear) so the wire format matches RecordContext's own\n // GetChangedProperties shape.\n const value = ctx.record.properties?.[columnName];\n dirty.set(columnName, value ?? null);\n }\n }\n queueRowUpdate({\n tableName: gridTableName,\n recordId: gridRecordId,\n dirty,\n });\n }\n\n if (onSaved) await onSaved();\n onClose();\n } catch (err) {\n if (onError) {\n await onError(err);\n } else {\n await dialogSvc.showErrorAsync(\n err instanceof Error ? err.message : String(err),\n { title: t('app.errors.record-save') },\n );\n }\n } finally {\n setSaving(false);\n }\n }, [\n ctx,\n validation,\n behavior,\n runWithOverlay,\n t,\n dialogSvc,\n queueRowUpdate,\n gridRefresh,\n gridTableName,\n gridRecordId,\n onClose,\n onSaved,\n onError,\n multiEditTargets,\n isMultiEdit,\n enforceValidation,\n ppp,\n ]);\n\n return (\n <DialogBody>\n <DialogTitle>{resolvedTitle}</DialogTitle>\n <DialogContent>\n {isRecordReady ? (\n children\n ) : (\n <div className={styles.loadingArea} role=\"status\" aria-live=\"polite\">\n <Spinner size=\"large\" label={t('app.messages.record-loading')} />\n </div>\n )}\n </DialogContent>\n <DialogActions>\n <Button appearance=\"secondary\" onClick={onClose} disabled={saving}>\n {t('app.buttons.cancel.label')}\n </Button>\n <Button\n appearance=\"primary\"\n onClick={handleSave}\n // Disabled when there's nothing to persist — `ctx.isDirty`\n // tracks the size of the RecordContext's pending-changes map,\n // which is non-empty whenever the user has typed (or whenever\n // the dialog opened with `initialPendingChanges` from a prior\n // session, in which case it stays non-empty even after the\n // user reverts back to baseline values — those reverts are\n // pendingChanges entries themselves, so a Save with isDirty\n // still clears the queue entry correctly via queueRowUpdate's\n // delete-on-empty rule). The first-open-no-changes case (user\n // pops the dialog, never types, hits Cancel) is the one we're\n // gating against — Save staying enabled there is misleading.\n disabled={saving || !isRecordReady || !ctx.isDirty}\n >\n {t('app.buttons.save.label')}\n </Button>\n </DialogActions>\n </DialogBody>\n );\n}\n","import { useValidationContext } from '@powerportalspro/react';\r\nimport { isValidElement, type ReactElement, type ReactNode } from 'react';\r\nimport { useWizard } from 'react-use-wizard';\r\n\r\nexport interface WizardRecordPageProps {\r\n /**\r\n * Fields rendered inside this step. Edit components (`<TextEdit>`,\r\n * `<MemoEdit>`, etc.) register with the surrounding wizard's shared\r\n * `<RecordContext>` and participate in its dirty / validate cascade.\r\n */\r\n children?: ReactNode;\r\n /**\r\n * When `true` (default), advancing past this page calls\r\n * `recordContext.validate()` and cancels the forward navigation if\r\n * any descendant edit reports invalid. Set `false` to allow\r\n * proceeding with invalid values.\r\n */\r\n forceSuccessfulValidationBeforeSave?: boolean;\r\n /**\r\n * Optional short label rendered in the wizard's step indicator\r\n * (when `<WizardRecordForm stepperVisibility=\"visible\">`). When\r\n * omitted, the step shows its 1-based index.\r\n */\r\n label?: string;\r\n}\r\n\r\nconst PAGE_MARKER = Symbol.for('@powerportalspro/wizard-record-page');\r\n\r\n/**\r\n * A single step inside a `<WizardRecordForm>`. Renders its `children`\r\n * when active (react-use-wizard handles the mounting / unmounting of\r\n * the other steps), and gates forward navigation on the shared\r\n * `<RecordContext>`'s validate when\r\n * `forceSuccessfulValidationBeforeSave` is `true`.\r\n *\r\n * Mirrors Blazor's `<WizardRecordPage>`. Same gate semantics as the\r\n * Blazor `<FluentWizardStep>` + `RecordContext.Validate()` pair.\r\n */\r\nexport function WizardRecordPage({\r\n children,\r\n forceSuccessfulValidationBeforeSave = true,\r\n}: WizardRecordPageProps) {\r\n const validation = useValidationContext();\r\n const { handleStep } = useWizard();\r\n\r\n // Register the pre-transition validation gate. `handleStep` stores\r\n // exactly one handler in an internal ref — calling it again\r\n // replaces the previous one, and the ref is cleared after each\r\n // successful transition. Always register (with the gate inside the\r\n // handler body) so a runtime toggle of `forceSuccessfulValidationBeforeSave`\r\n // can't leave a stale handler in place from a previous render. The\r\n // handler is a no-op when the prop is false or the validation\r\n // context isn't mounted (e.g. a wizard hosted outside\r\n // `<RecordContext>` — unusual but supported).\r\n //\r\n // Throwing cancels the transition without surfacing the error to\r\n // the user — invalid fields already have inline error indicators\r\n // via the validation context, so a dialog/toast would be redundant.\r\n handleStep(async () => {\r\n if (!forceSuccessfulValidationBeforeSave || !validation) return;\r\n const isValid = await validation.validateAll();\r\n if (!isValid) {\r\n throw new Error('validation-failed');\r\n }\r\n });\r\n\r\n return <>{children}</>;\r\n}\r\n\r\n// Marker for `<WizardRecordForm>`'s child-traversal helper. Reading\r\n// `.type.<symbol>` from a React element is reliable across module\r\n// boundaries; comparing `child.type === WizardRecordPage` directly\r\n// would fail when bundlers duplicate the component (rare but possible\r\n// in monorepo / linked-package setups).\r\n(WizardRecordPage as unknown as Record<symbol, true>)[PAGE_MARKER] = true;\r\n\r\nexport function isWizardRecordPageElement(\r\n node: ReactNode,\r\n): node is ReactElement<WizardRecordPageProps> {\r\n if (!isValidElement(node)) return false;\r\n const type = node.type as unknown as Record<symbol, true> | undefined;\r\n return !!type && type[PAGE_MARKER] === true;\r\n}\r\n","import {\r\n Button,\r\n Spinner,\r\n makeStyles,\r\n mergeClasses,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport type { TableRecord } from '@powerportalspro/core';\r\nimport {\r\n RecordContext,\r\n useRecordContext,\r\n useT,\r\n type RecordContextProps,\r\n} from '@powerportalspro/react';\r\nimport { Children, useCallback, useMemo, type ReactNode } from 'react';\r\nimport { Wizard, useWizard } from 'react-use-wizard';\r\nimport { WizardRecordPage, isWizardRecordPageElement } from './wizard-record-page.js';\r\n\r\n/**\r\n * Visibility of the step indicator above the wizard body. Mirrors\r\n * Blazor's `StepperVisible` boolean — defaults to `false` (matches the\r\n * Blazor default; the Save flow is button-driven, not stepper-driven).\r\n */\r\nexport type StepperVisibility = 'visible' | 'hidden';\r\n\r\nexport interface WizardRecordFormProps\r\n extends Pick<RecordContextProps, 'table' | 'id' | 'initialRecord' | 'loadedRecord'> {\r\n /** Should be a sequence of `<WizardRecordPage>` children. */\r\n children: ReactNode;\r\n /**\r\n * Fires after the wizard's Finish button has run its commit path\r\n * (default `RecordContext.save()`, or a consumer-supplied\r\n * {@link skipSave} flow). Receives the in-memory record so consumers\r\n * can route it elsewhere — typically used by the\r\n * `<NewRecordGridButton>` wizard-mode integration to call the grid\r\n * context's `queueRowCreate` after the wizard is done.\r\n */\r\n onCompleted?: (record: TableRecord) => void | Promise<void>;\r\n /**\r\n * When `true`, the Finish button does NOT call\r\n * `RecordContext.save()` — `onCompleted` runs immediately with the\r\n * current in-memory record and the consumer is responsible for\r\n * persisting (or queueing) it.\r\n *\r\n * Used by `<NewRecordGridButton formType=\"wizard\">` in its\r\n * `behavior=\"withGridContext\"` mode: the new record goes into the\r\n * grid's pending-create queue, not directly to Dataverse. The\r\n * surrounding `<MainContext>` / `<RecordContext>`'s next save\r\n * commits everything in one `executeMultiple` batch.\r\n *\r\n * Default `false` — the wizard saves through its own RecordContext.\r\n */\r\n skipSave?: boolean;\r\n /**\r\n * Called when the save throws (server validation error, network\r\n * failure, etc.). When omitted, the error is logged to the console\r\n * and re-thrown so a parent error boundary can catch it.\r\n */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n /**\r\n * Label for the final-step action button. Defaults to the\r\n * localized \"Create\" string when the wizard is creating a new\r\n * record (no `id` prop), and \"Save\" when editing an existing one.\r\n */\r\n finishLabel?: string;\r\n /**\r\n * Optional Cancel button shown on every step. Fires this callback\r\n * — typically routes the user back / closes the hosting dialog.\r\n * Omit to hide the Cancel button entirely.\r\n */\r\n onCancel?: () => void;\r\n /** Override the Cancel button's label. Defaults to localized \"Cancel\". */\r\n cancelLabel?: string;\r\n /**\r\n * Controls the step-indicator (`<Stepper>`) visibility. Defaults to\r\n * `'hidden'` — most wizards are short enough that the Previous/Next\r\n * buttons are sufficient orientation. Set `'visible'` for longer\r\n * flows where users benefit from seeing the step count.\r\n */\r\n stepperVisibility?: StepperVisibility;\r\n}\r\n\r\nconst useStyles = makeStyles({\r\n shell: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n rowGap: tokens.spacingVerticalM,\r\n },\r\n stepper: {\r\n flex: '0 0 auto',\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalS,\r\n paddingBottom: tokens.spacingVerticalM,\r\n borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,\r\n },\r\n stepperItem: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalXS,\r\n color: tokens.colorNeutralForeground3,\r\n fontSize: tokens.fontSizeBase200,\r\n },\r\n stepperItemActive: {\r\n color: tokens.colorBrandForeground1,\r\n fontWeight: tokens.fontWeightSemibold,\r\n },\r\n stepperItemCompleted: {\r\n color: tokens.colorNeutralForeground2,\r\n },\r\n stepperDot: {\r\n width: '24px',\r\n height: '24px',\r\n borderRadius: '50%',\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n color: 'inherit',\r\n },\r\n stepperDotActive: {\r\n backgroundColor: tokens.colorBrandBackground,\r\n color: tokens.colorNeutralForegroundOnBrand,\r\n },\r\n stepperDotCompleted: {\r\n backgroundColor: tokens.colorPaletteGreenBackground2,\r\n color: tokens.colorPaletteGreenForeground2,\r\n },\r\n stepperSeparator: {\r\n flex: '1 1 auto',\r\n height: '1px',\r\n backgroundColor: tokens.colorNeutralStroke2,\r\n },\r\n body: {\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n display: 'flex',\r\n flexDirection: 'column',\r\n },\r\n footer: {\r\n flex: '0 0 auto',\r\n display: 'flex',\r\n flexDirection: 'row',\r\n justifyContent: 'space-between',\r\n alignItems: 'center',\r\n columnGap: tokens.spacingHorizontalM,\r\n paddingTop: tokens.spacingVerticalM,\r\n borderTop: `1px solid ${tokens.colorNeutralStroke2}`,\r\n },\r\n footerLeft: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n columnGap: tokens.spacingHorizontalS,\r\n },\r\n footerRight: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n columnGap: tokens.spacingHorizontalS,\r\n },\r\n});\r\n\r\n/**\r\n * Multi-step record-edit wizard backed by `react-use-wizard`. Mirrors\r\n * Blazor's `<WizardRecordForm>` / `<WizardRecordPage>` pair. Each child\r\n * `<WizardRecordPage>` is rendered as one step; only the active step\r\n * mounts at any time (react-use-wizard's default), so descendant edit\r\n * components register validators / dirty state only for the current\r\n * step's fields.\r\n *\r\n * The wizard is wrapped in a single `<RecordContext>` whose\r\n * `record` carries pending changes across step navigation. Hitting\r\n * Finish on the last step invokes the RecordContext's `save()`, then\r\n * `onCompleted` — same path as a single-page form's Save button.\r\n *\r\n * Per-step validation gates use {@link WizardRecordPageProps.forceSuccessfulValidationBeforeSave}\r\n * (defaults to `true`): the page registers a `handleStep` handler that\r\n * throws to cancel forward navigation when `recordContext.validate()`\r\n * reports invalid fields.\r\n *\r\n * ```tsx\r\n * <WizardRecordForm table=\"contact\" onCompleted={() => navigate('/contacts')}>\r\n * <WizardRecordPage>\r\n * <TextEdit columnName=\"firstname\" />\r\n * <TextEdit columnName=\"lastname\" />\r\n * </WizardRecordPage>\r\n * <WizardRecordPage forceSuccessfulValidationBeforeSave={false}>\r\n * <MemoEdit columnName=\"description\" />\r\n * </WizardRecordPage>\r\n * </WizardRecordForm>\r\n * ```\r\n */\r\nexport function WizardRecordForm({\r\n children,\r\n table,\r\n id,\r\n initialRecord,\r\n loadedRecord,\r\n onCompleted,\r\n skipSave,\r\n onError,\r\n finishLabel,\r\n onCancel,\r\n cancelLabel,\r\n stepperVisibility = 'hidden',\r\n}: WizardRecordFormProps) {\r\n // Build a stable map of step props for the stepper indicator. The\r\n // map is derived from the React children before they're handed to\r\n // react-use-wizard's `<Wizard>` — Wizard renders only the active\r\n // child, so we can't introspect rendered children for the stepper\r\n // labels.\r\n const pageProps = useMemo(() => {\r\n const props: { label?: string }[] = [];\r\n Children.forEach(children, (child) => {\r\n if (isWizardRecordPageElement(child)) {\r\n props.push({\r\n ...(child.props.label !== undefined && { label: child.props.label }),\r\n });\r\n }\r\n });\r\n return props;\r\n }, [children]);\r\n\r\n const recordContextProps: RecordContextProps = {\r\n table,\r\n ...(id !== undefined && { id }),\r\n ...(initialRecord !== undefined && { initialRecord }),\r\n ...(loadedRecord !== undefined && { loadedRecord }),\r\n children: null,\r\n };\r\n\r\n return (\r\n <RecordContext {...recordContextProps}>\r\n <WizardRecordFormInner\r\n pageProps={pageProps}\r\n {...(onCompleted !== undefined && { onCompleted })}\r\n {...(skipSave !== undefined && { skipSave })}\r\n {...(onError !== undefined && { onError })}\r\n {...(finishLabel !== undefined && { finishLabel })}\r\n {...(onCancel !== undefined && { onCancel })}\r\n {...(cancelLabel !== undefined && { cancelLabel })}\r\n stepperVisibility={stepperVisibility}\r\n isCreate={id === undefined || id === '' || id === '00000000-0000-0000-0000-000000000000'}\r\n >\r\n {children}\r\n </WizardRecordFormInner>\r\n </RecordContext>\r\n );\r\n}\r\n\r\ninterface WizardRecordFormInnerProps {\r\n children: ReactNode;\r\n pageProps: { label?: string }[];\r\n onCompleted?: (record: TableRecord) => void | Promise<void>;\r\n skipSave?: boolean;\r\n onError?: (error: unknown) => void | Promise<void>;\r\n finishLabel?: string;\r\n onCancel?: () => void;\r\n cancelLabel?: string;\r\n stepperVisibility: StepperVisibility;\r\n isCreate: boolean;\r\n}\r\n\r\nfunction WizardRecordFormInner({\r\n children,\r\n pageProps,\r\n onCompleted,\r\n skipSave,\r\n onError,\r\n finishLabel,\r\n onCancel,\r\n cancelLabel,\r\n stepperVisibility,\r\n isCreate,\r\n}: WizardRecordFormInnerProps) {\r\n const styles = useStyles();\r\n return (\r\n <div className={mergeClasses('ppp-wizard-record-form', styles.shell)}>\r\n <Wizard\r\n wrapper={<div className={styles.body} />}\r\n footer={\r\n <WizardFooter\r\n pageProps={pageProps}\r\n stepperVisibility={stepperVisibility}\r\n isCreate={isCreate}\r\n {...(onCompleted !== undefined && { onCompleted })}\r\n {...(skipSave !== undefined && { skipSave })}\r\n {...(onError !== undefined && { onError })}\r\n {...(finishLabel !== undefined && { finishLabel })}\r\n {...(onCancel !== undefined && { onCancel })}\r\n {...(cancelLabel !== undefined && { cancelLabel })}\r\n />\r\n }\r\n >\r\n {children}\r\n </Wizard>\r\n </div>\r\n );\r\n}\r\n\r\ninterface WizardFooterProps {\r\n pageProps: { label?: string }[];\r\n stepperVisibility: StepperVisibility;\r\n isCreate: boolean;\r\n onCompleted?: (record: TableRecord) => void | Promise<void>;\r\n skipSave?: boolean;\r\n onError?: (error: unknown) => void | Promise<void>;\r\n finishLabel?: string;\r\n onCancel?: () => void;\r\n cancelLabel?: string;\r\n}\r\n\r\nfunction WizardFooter({\r\n pageProps,\r\n stepperVisibility,\r\n isCreate,\r\n onCompleted,\r\n skipSave,\r\n onError,\r\n finishLabel,\r\n onCancel,\r\n cancelLabel,\r\n}: WizardFooterProps) {\r\n const styles = useStyles();\r\n const t = useT();\r\n const recordContext = useRecordContext();\r\n const wizard = useWizard();\r\n\r\n const handleFinish = useCallback(async () => {\r\n try {\r\n // Default commit goes through the wizard's RecordContext.save().\r\n // `skipSave` hands ownership to the consumer's `onCompleted`\r\n // (used by `<NewRecordGridButton formType=\"wizard\">` to queue\r\n // the new record into the grid's pending-create slot instead\r\n // of saving directly).\r\n if (!skipSave) {\r\n await recordContext.save();\r\n }\r\n if (onCompleted && recordContext.record) {\r\n await onCompleted(recordContext.record);\r\n }\r\n } catch (err) {\r\n if (onError) {\r\n await onError(err);\r\n } else {\r\n // eslint-disable-next-line no-console\r\n console.error('[WizardRecordForm] save failed', err);\r\n throw err;\r\n }\r\n }\r\n }, [recordContext, skipSave, onCompleted, onError]);\r\n\r\n const resolvedFinishLabel =\r\n finishLabel ??\r\n (isCreate ? t('app.buttons.create.label') : t('app.buttons.save.label'));\r\n const resolvedCancelLabel = cancelLabel ?? t('app.buttons.cancel.label');\r\n const previousLabel = t('app.buttons.previous.label');\r\n const nextLabel = t('app.buttons.next.label');\r\n\r\n return (\r\n <>\r\n {stepperVisibility === 'visible' && pageProps.length > 0 && (\r\n <div className={styles.stepper}>\r\n {pageProps.map((p, i) => {\r\n const isActive = i === wizard.activeStep;\r\n const isCompleted = i < wizard.activeStep;\r\n const itemClass = mergeClasses(\r\n styles.stepperItem,\r\n isActive && styles.stepperItemActive,\r\n isCompleted && styles.stepperItemCompleted,\r\n );\r\n const dotClass = mergeClasses(\r\n styles.stepperDot,\r\n isActive && styles.stepperDotActive,\r\n isCompleted && styles.stepperDotCompleted,\r\n );\r\n return (\r\n <>\r\n {i > 0 && <span key={`sep-${i}`} className={styles.stepperSeparator} aria-hidden=\"true\" />}\r\n <span key={i} className={itemClass}>\r\n <span className={dotClass}>{i + 1}</span>\r\n {p.label && <span>{p.label}</span>}\r\n </span>\r\n </>\r\n );\r\n })}\r\n </div>\r\n )}\r\n <div className={styles.footer}>\r\n <div className={styles.footerLeft}>\r\n {onCancel && (\r\n <Button onClick={onCancel} disabled={wizard.isLoading}>\r\n {resolvedCancelLabel}\r\n </Button>\r\n )}\r\n </div>\r\n <div className={styles.footerRight}>\r\n {!wizard.isFirstStep && (\r\n <Button\r\n onClick={() => {\r\n void wizard.previousStep();\r\n }}\r\n disabled={wizard.isLoading}\r\n >\r\n {previousLabel}\r\n </Button>\r\n )}\r\n {wizard.isLastStep ? (\r\n <Button\r\n appearance=\"primary\"\r\n onClick={() => {\r\n void handleFinish();\r\n }}\r\n disabled={wizard.isLoading}\r\n {...(wizard.isLoading && { icon: <Spinner size=\"tiny\" /> })}\r\n >\r\n {resolvedFinishLabel}\r\n </Button>\r\n ) : (\r\n <Button\r\n appearance=\"primary\"\r\n onClick={() => {\r\n void wizard.nextStep();\r\n }}\r\n disabled={wizard.isLoading}\r\n {...(wizard.isLoading && { icon: <Spinner size=\"tiny\" /> })}\r\n >\r\n {nextLabel}\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n\r\n// Re-export the page so consumers can `import { WizardRecordPage }` from\r\n// the same module they import the form. Keeps the public surface\r\n// alongside the wizard it composes with.\r\nexport { WizardRecordPage };\r\nexport type { WizardRecordPageProps } from './wizard-record-page.js';\r\n","import {\n Button,\n Dialog,\n DialogActions,\n DialogBody,\n DialogContent,\n DialogSurface,\n DialogTitle,\n Spinner,\n makeStyles,\n tokens,\n type ButtonProps,\n type DialogOpenChangeData,\n type DialogOpenChangeEvent,\n} from '@fluentui/react-components';\nimport { Add20Regular } from '@fluentui/react-icons';\nimport { getRecordReferenceId, TableSecurityPermission, type components } from '@powerportalspro/core';\nimport {\n QueryStatus,\n RecordContext,\n useDialogService,\n useMainContext,\n useRecordContext,\n useRunWithOverlay,\n useT,\n useTableMetadata,\n useValidationContext,\n} from '@powerportalspro/react';\nimport {\n Children,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { useGridContext } from '../grids/grid-context.js';\nimport { isWizardRecordPageElement } from '../form/wizard-record-page.js';\nimport { WizardRecordForm } from '../form/wizard-record-form.js';\nimport { GridButton } from './grid-button.js';\nimport { GridButtonRole, setGridButtonRole } from './grid-button-role.js';\n\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\ntype TableRecord = components['schemas']['TableRecord'];\ntype LookupValue = components['schemas']['ColumnValueBaseLookupValue'];\n\n/**\n * How a `<NewRecordGridButton>` dialog commits its create when Save is\n * clicked:\n *\n * - **`withGridContext`** (default) — Queues a `CreateRequest` into the\n * grid's pending-creates registry. The surrounding `<MainContext>` /\n * `<RecordContext>` picks it up in its next transactional save (one\n * `executeMultiple` round-trip covers the parent record's update plus\n * every queued new row in the grid). Use when the grid lives inside a\n * record-edit page and the user expects \"save the whole page\" semantics —\n * the new row's FK to the parent stays consistent with whatever id the\n * parent settles at.\n * - **`immediately`** — Issues the create right away through\n * `executeMultipleAsync`, then refreshes the grid. Use for standalone\n * grid pages where each new row is its own commit.\n *\n * Mirrors Blazor's `GridActionBehavior.WithGridContext` / `.Immediately`\n * on `NewRecordGridButton<TForm>.Behavior`.\n */\nexport type NewRecordGridButtonBehavior = 'withGridContext' | 'immediately';\n\n/** See {@link OpenRecordGridButton}'s `DialogLocation` — same semantic. */\nexport type NewRecordDialogLocation = 'center' | 'panel';\n\nexport interface NewRecordGridButtonProps {\n /** Form fields rendered inside the dialog. */\n children: ReactNode | (() => ReactNode);\n /** See {@link NewRecordGridButtonBehavior}. Defaults to `withGridContext`. */\n behavior?: NewRecordGridButtonBehavior;\n /** See {@link NewRecordDialogLocation}. Defaults to `center`. */\n location?: NewRecordDialogLocation;\n /** Force the button into a disabled state regardless of permission. */\n disabled?: boolean;\n /** Custom button label. Defaults to the localized `\"New\"`. */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n /**\n * Optional dialog title override. Defaults to the localized\n * `\"Create record\"` label. (Follow-up to expose a\n * `\"Create {table-label}\"` template when the localizer surfaces it.)\n */\n dialogTitle?: string;\n /**\n * Initial column values for the new record. Useful for seeding defaults\n * the consumer wants the user to start from. Note that when the button is\n * hosted inside a `<SubGrid>`, the parent foreign-key column is\n * **automatically** pre-filled from `gridContext.parentRecord` +\n * `relationshipName` — consumers don't need to set it here. This prop\n * stacks on top: the FK pre-fill applies first, then `initialValues`\n * overrides for any column.\n */\n initialValues?: ReadonlyMap<string, ColumnValueBase>;\n /** Called after a successful commit (whether queued or immediate). */\n onSaved?: () => void | Promise<void>;\n /** Called when commit (or its validation step) throws. */\n onError?: (error: unknown) => void | Promise<void>;\n /**\n * When `true` (default), the dialog's Save button runs the per-column\n * validation gate before committing. Set `false` to bypass and submit\n * whatever values the form holds; server-side validation still applies.\n * Overrides the surrounding `<MainContext>` / `<RecordContext>`'s\n * `forceSuccessfulValidationBeforeSave` cascade for this button.\n */\n forceSuccessfulValidationBeforeSave?: boolean;\n}\n\nconst useDialogStyles = makeStyles({\n loadingArea: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '160px',\n rowGap: tokens.spacingVerticalM,\n },\n});\n\n/**\n * Grid toolbar button that opens a dialog form for creating a new record.\n * Mirrors Blazor's `<NewRecordGridButton TForm=\"...\" />` minus the `TForm`\n * type parameter (React composes via children instead).\n *\n * When hosted inside a `<SubGrid>`, automatically pre-fills the parent\n * foreign-key column on the new record (resolved from the SubGrid's\n * `relationshipName` against the parent table's `OneToMany` metadata) so\n * the new row is properly related to the parent on save.\n *\n * Disabled when the user lacks `Create` on the grid's table (sampled from\n * `gridContext.tablePermissions`). Sampled-permission falls back to\n * enabled when the grid is empty (no row to read off), so a brand-new\n * empty SubGrid isn't locked out — the server enforces on commit.\n */\nexport function NewRecordGridButton({\n children,\n behavior = 'withGridContext',\n location = 'center',\n disabled,\n label,\n appearance,\n dialogTitle,\n initialValues,\n onSaved,\n onError,\n forceSuccessfulValidationBeforeSave,\n}: NewRecordGridButtonProps) {\n const grid = useGridContext();\n const t = useT();\n const resolvedLabel = label ?? t('app.buttons.new.label');\n\n // Permission gate — same logic as NavigateNewRecordGridButton. Stay\n // optimistic when the bitmask isn't known yet (empty grid), let the\n // server enforce.\n const sampledPermissions = grid.tablePermissions;\n const createDeniedByPermissions =\n sampledPermissions !== undefined &&\n (sampledPermissions & TableSecurityPermission.Create) !== TableSecurityPermission.Create;\n const isDisabled = disabled === true || createDeniedByPermissions;\n\n const [open, setOpen] = useState(false);\n // Re-open mode: the grid invoked our `createReopenHandler` for a row\n // already in the `pendingRowCreates` queue. Hold the queue index +\n // current record so the dialog body can seed itself with the pending\n // values and the save path can replace the queue entry in place via\n // `updatePendingRowCreate` instead of appending a new one.\n const [reopenState, setReopenState] = useState<\n { index: number; record: TableRecord } | null\n >(null);\n const openDialog = useCallback(() => {\n setReopenState(null);\n setOpen(true);\n }, []);\n const closeDialog = useCallback(() => {\n setOpen(false);\n setReopenState(null);\n }, []);\n const handleOpenChange = useCallback(\n (_evt: DialogOpenChangeEvent, data: DialogOpenChangeData) => {\n setOpen(data.open);\n if (!data.open) setReopenState(null);\n },\n [],\n );\n\n // Register as the grid's \"create re-open\" handler. The grid's\n // `dispatchOpen` invokes this when the user interacts with a row\n // sitting in `pendingRowCreates` (double-click, primary-name link,\n // single-row Open click) so the user keeps editing the in-progress\n // record through the same form/wizard it was created in.\n //\n // The `record` argument carries the grid-internal synthetic id\n // (`__pending-create-<idx>`) used as a TanStack row key — we\n // deliberately discard it and read the ORIGINAL queued record from\n // `pendingRowCreates[queueIndex]` instead. The queued record carries\n // the pre-gen GUID RecordContext seeded for the create batch, which\n // is what subsequent saves and re-opens want to keep stable.\n // `isNewRecord()` recognizes the queued record as new via the\n // missing-`permissions` marker — the server hasn't stamped it yet —\n // so the metadata gate evaluates against `isValidForCreate` as\n // intended.\n //\n // Read via a ref so the registered handler stays stable across\n // queue mutations — `dispatchOpen` calls it at click time with the\n // current queue index, and we look up the matching record from the\n // ref's `.current` view of `pendingRowCreates` (always fresh).\n const { registerCreateReopenHandler } = grid;\n const pendingRowCreatesRef = useRef(grid.pendingRowCreates);\n pendingRowCreatesRef.current = grid.pendingRowCreates;\n const handleReopen = useCallback(\n (_synthetic: TableRecord, queueIndex: number) => {\n const queued = pendingRowCreatesRef.current[queueIndex];\n if (!queued) return;\n setReopenState({ index: queueIndex, record: queued.record });\n setOpen(true);\n },\n [],\n );\n useEffect(() => {\n return registerCreateReopenHandler(handleReopen);\n }, [registerCreateReopenHandler, handleReopen]);\n\n // The grid's tableName is required to construct the initial record. A\n // grid mounted with an explicit `viewId` and no `tableName` would lack\n // this — the New button has no target table in that case and stays\n // disabled. Guard with `?? ''` to keep types happy; the open conditional\n // below skips the dialog entirely when the resolved tableName is empty.\n const tableName = grid.tableName ?? '';\n\n // Resolve the parent table's metadata (when this grid is hosted in a\n // SubGrid). Used to look up the OneToMany relationship's\n // `referencingTableColumn` — the FK column on the new record that\n // points back to the parent. Skip the fetch when there's no parent.\n const parentTableMetadataQuery = useTableMetadata(\n grid.parentRecord?.tableName ?? '',\n { enabled: !!grid.parentRecord?.tableName },\n );\n const parentTableMetadata = parentTableMetadataQuery.data;\n\n // Compose the initial record for the dialog's RecordContext: parent-FK\n // pre-fill first (when in a SubGrid + relationship resolvable), then\n // the consumer's `initialValues` overlay on top (so a consumer can\n // override the FK if they really want to).\n //\n // The FK pre-fill mirrors Blazor's\n // `NewRecordGridButton.OnNewButtonClick`:\n // var oneToMany = parentTableMetadata.OneToMany.FirstOrDefault(...)\n // _tableRecord[oneToMany.ReferencingTableColumn] = new LookupValue {...}\n //\n // We construct the lookup the same way Blazor does — TableName + Value\n // (the parent record id) + an optional display Name (the parent's\n // primary-name column value). Read with optional chaining so a not-yet-\n // resolved metadata returns a record without the FK; the user will see\n // it as a missing-required-field validation issue on save, which is the\n // right surface.\n const initialRecord = useMemo<TableRecord>(() => {\n const properties: Record<string, ColumnValueBase> = {};\n\n // SubGrid parent-FK pre-fill. `getRecordReferenceId` covers both\n // saved parents (returns `id`) and create-mode parents (returns\n // `_idForCreate`), so a \"New Contact\" inside a SubGrid on a brand\n // new account record correctly populates `parentcustomerid` with\n // the parent's temp GUID — the server's in-batch resolver matches\n // it back to the same-batch CreateRequest for the parent on save.\n const parent = grid.parentRecord;\n const relationshipName = grid.relationshipName;\n const parentRefId = parent ? getRecordReferenceId(parent) : undefined;\n if (parentRefId && parent?.tableName && relationshipName && parentTableMetadata) {\n const oneToMany = parentTableMetadata.oneToMany?.find(\n (rel) => rel.relationshipName === relationshipName,\n );\n if (oneToMany?.referencingTableColumn) {\n const parentPrimaryName = parentTableMetadata.primaryNameColumn;\n const parentName =\n parentPrimaryName && parent.properties\n ? (parent.properties[parentPrimaryName] as\n | { $type: number; value?: string | null }\n | undefined)?.value ?? null\n : null;\n // ColumnValueBase uses integer discriminators on the wire —\n // LookupValue's $type is 6 (see generated api-schema.ts). The\n // edit components use the same integer-discriminator shape\n // (e.g. StringValue is $type: 14).\n const fkValue: LookupValue = {\n $type: 6,\n tableName: parent.tableName,\n value: parentRefId,\n ...(parentName !== null && { name: parentName }),\n };\n properties[oneToMany.referencingTableColumn] = fkValue;\n }\n }\n\n // Consumer overrides — last-write-wins on FK collision.\n if (initialValues) {\n for (const [col, val] of initialValues) {\n properties[col] = val;\n }\n }\n\n return { tableName, properties };\n }, [tableName, grid.parentRecord, grid.relationshipName, parentTableMetadata, initialValues]);\n\n // Detect wizard-mode authoring: if any direct child is a\n // `<WizardRecordPage>`, switch the dialog body to render a\n // `<WizardRecordForm>` instead of a plain form + Save/Cancel.\n // Mirrors Blazor's `FormType.WizardForm` discriminator without\n // requiring an explicit prop — the consumer's choice of children\n // expresses intent. The wizard owns its own RecordContext (so the\n // parent-FK pre-fill flows through `initialRecord`), and its Finish\n // button routes through this button's `behavior` (immediately vs\n // withGridContext) via `onCompleted` + `skipSave`.\n const resolvedChildren = typeof children === 'function' ? children() : children;\n const isWizardMode = useMemo(() => {\n let found = false;\n Children.forEach(resolvedChildren, (child) => {\n if (isWizardRecordPageElement(child)) found = true;\n });\n return found;\n }, [resolvedChildren]);\n\n // Effective values for the active dialog session. In re-open mode\n // (the grid invoked our create-reopen handler for a pending row),\n // the dialog seeds from the pending record's current values and the\n // save path replaces the queue entry in place. Otherwise the\n // standard new-create flow applies (FK pre-fill + new queue entry\n // or immediate save).\n const effectiveInitialRecord = reopenState ? reopenState.record : initialRecord;\n // Force `withGridContext` semantics in re-open mode — the pending\n // record isn't persisted yet, so \"save immediately\" would have\n // nothing to write to (and the parent-FK might still be unresolved\n // for a new-account-with-subgrid flow). The queue update lands as\n // part of the parent context's next transactional save.\n const effectiveBehavior =\n reopenState !== null ? 'withGridContext' : behavior;\n\n const handleWizardCompleted = useCallback(\n async (record: TableRecord) => {\n try {\n if (reopenState !== null) {\n // Re-open mode — replace the existing queue entry in place\n // so submission order is preserved (and the row's index in\n // the grid stays stable across edits).\n grid.updatePendingRowCreate(reopenState.index, { record });\n } else if (behavior === 'withGridContext') {\n // Stage the new record in the grid's pending-create queue.\n // The surrounding `<MainContext>` / `<RecordContext>`'s\n // next save batches it alongside any other pending changes\n // — mirrors the Form-mode path through `queueRowCreate`.\n grid.queueRowCreate({ record });\n } else {\n // Immediate mode — the wizard already saved through its\n // own RecordContext (skipSave was not set). Just refresh\n // the grid so the new row appears.\n await grid.refresh();\n }\n closeDialog();\n if (onSaved) await onSaved();\n } catch (err) {\n if (onError) await onError(err);\n else throw err;\n }\n },\n [reopenState, behavior, grid, closeDialog, onSaved, onError],\n );\n\n // queueRowCreate target for the form-mode DialogInner. In re-open\n // mode, swap in a function that updates the queue entry in place\n // instead of appending a new one. The DialogInner doesn't need to\n // know about re-open as a concept — the queueRowCreate prop is the\n // single integration point.\n const dialogQueueRowCreate = useMemo(() => {\n if (reopenState === null) return grid.queueRowCreate;\n const idx = reopenState.index;\n return (create: { record: TableRecord }) =>\n grid.updatePendingRowCreate(idx, create);\n }, [reopenState, grid.queueRowCreate, grid.updatePendingRowCreate]);\n\n return (\n <>\n <GridButton\n label={resolvedLabel}\n icon={<Add20Regular />}\n onClick={openDialog}\n disabled={isDisabled}\n {...(appearance !== undefined && { appearance })}\n />\n {open && tableName && (\n <Dialog open={open} onOpenChange={handleOpenChange} modalType=\"modal\">\n <DialogSurface\n style={\n location === 'panel'\n ? {\n margin: 0,\n marginLeft: 'auto',\n maxWidth: '480px',\n width: '100%',\n height: '100vh',\n maxHeight: '100vh',\n borderRadius: 0,\n }\n : undefined\n }\n >\n {isWizardMode ? (\n <WizardRecordForm\n table={tableName}\n initialRecord={effectiveInitialRecord}\n onCancel={closeDialog}\n onCompleted={handleWizardCompleted}\n skipSave={effectiveBehavior === 'withGridContext'}\n >\n {resolvedChildren}\n </WizardRecordForm>\n ) : (\n <RecordContext table={tableName} initialRecord={effectiveInitialRecord}>\n <DialogInner\n behavior={effectiveBehavior}\n dialogTitle={dialogTitle}\n onClose={closeDialog}\n onSaved={onSaved}\n onError={onError}\n queueRowCreate={dialogQueueRowCreate}\n gridRefresh={grid.refresh}\n {...(forceSuccessfulValidationBeforeSave !== undefined && {\n forceSuccessfulValidationBeforeSave,\n })}\n >\n {resolvedChildren}\n </DialogInner>\n </RecordContext>\n )}\n </DialogSurface>\n </Dialog>\n )}\n </>\n );\n}\n\nsetGridButtonRole(NewRecordGridButton, GridButtonRole.Create);\n\nfunction DialogInner({\n children,\n behavior,\n dialogTitle,\n onClose,\n onSaved,\n onError,\n queueRowCreate,\n gridRefresh,\n forceSuccessfulValidationBeforeSave,\n}: {\n children: ReactNode;\n behavior: NewRecordGridButtonBehavior;\n dialogTitle: string | undefined;\n onClose: () => void;\n onSaved: (() => void | Promise<void>) | undefined;\n onError: ((error: unknown) => void | Promise<void>) | undefined;\n queueRowCreate: (create: { record: TableRecord }) => void;\n gridRefresh: () => Promise<void>;\n /**\n * Per-button override; `undefined` means \"inherit from the cascade.\"\n */\n forceSuccessfulValidationBeforeSave?: boolean;\n}) {\n const ctx = useRecordContext();\n const innerMainContext = useMainContext();\n const validation = useValidationContext();\n const dialogSvc = useDialogService();\n const runWithOverlay = useRunWithOverlay();\n const t = useT();\n const styles = useDialogStyles();\n const [saving, setSaving] = useState(false);\n // Resolution: per-button override > cascade from RecordContext's inner\n // MainContext > root default (true).\n const enforceValidation =\n forceSuccessfulValidationBeforeSave ??\n innerMainContext?.forceSuccessfulValidationBeforeSave ??\n true;\n\n // Status check — for create-mode RecordContext, the record is set\n // synchronously by the seed effect, so `status === Success` flips\n // within the first render cycle. Still gating here keeps the spinner\n // path identical to the Edit dialog's load gate (same visual UX).\n const isRecordReady =\n ctx.status === QueryStatus.Success && ctx.record !== undefined;\n\n // Title — explicit prop wins, else fall back to the localized\n // \"Create record\" label. Blazor uses `\"Create {table-label}\"`; the\n // table-label string requires the localizer to know the table name\n // pretty-name, which is a follow-up.\n const resolvedTitle = dialogTitle ?? t('app.buttons.new.label');\n\n const handleSave = useCallback(async () => {\n setSaving(true);\n try {\n if (enforceValidation && validation && !(await validation.validateAll())) return;\n\n if (behavior === 'immediately') {\n // Run the create through the standard RecordContext save flow.\n // RecordContext detects create-mode (no id) and emits a\n // CreateRequest from getRequests; saveAll wraps it in\n // executeMultiple as usual.\n await runWithOverlay(\n { description: t('app.messages.creating-record') },\n () => ctx.save(),\n );\n await gridRefresh();\n } else {\n // Queue mode: push the full record (defaults + user-fills) into\n // the grid's pending-creates registry. The outer MainContext's\n // transactional save picks it up via the grid's descendant\n // getRequests. Closing the dialog tears down the inner\n // RecordContext, so its own descendant registration (which\n // would otherwise duplicate the create through the cascade)\n // disappears at the same time.\n if (ctx.record) {\n queueRowCreate({ record: ctx.record });\n }\n }\n\n if (onSaved) await onSaved();\n onClose();\n } catch (err) {\n if (onError) {\n await onError(err);\n } else {\n await dialogSvc.showErrorAsync(\n err instanceof Error ? err.message : String(err),\n { title: t('app.errors.create-record') },\n );\n }\n } finally {\n setSaving(false);\n }\n }, [\n ctx,\n validation,\n behavior,\n runWithOverlay,\n t,\n dialogSvc,\n queueRowCreate,\n gridRefresh,\n onClose,\n onSaved,\n onError,\n enforceValidation,\n ]);\n\n return (\n <DialogBody>\n <DialogTitle>{resolvedTitle}</DialogTitle>\n <DialogContent>\n {isRecordReady ? (\n children\n ) : (\n <div className={styles.loadingArea} role=\"status\" aria-live=\"polite\">\n <Spinner size=\"large\" label={t('app.messages.record-loading')} />\n </div>\n )}\n </DialogContent>\n <DialogActions>\n <Button appearance=\"secondary\" onClick={onClose} disabled={saving}>\n {t('app.buttons.cancel.label')}\n </Button>\n <Button\n appearance=\"primary\"\n onClick={handleSave}\n disabled={saving || !isRecordReady || !ctx.isDirty}\n >\n {t('app.buttons.save.label')}\n </Button>\n </DialogActions>\n </DialogBody>\n );\n}\n","import { type ButtonProps } from '@fluentui/react-components';\nimport { Delete20Regular } from '@fluentui/react-icons';\nimport { TableSecurityPermission, type DeleteRequest } from '@powerportalspro/core';\nimport { useDialogService, usePowerPortalsPro, useT } from '@powerportalspro/react';\nimport { useCallback } from 'react';\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\nimport { GridButton } from './grid-button.js';\nimport { GridButtonBehavior } from './grid-button-behavior.js';\nimport { GridButtonRole, setGridButtonRole } from './grid-button-role.js';\n\n/**\n * How the immediate-delete path issues requests to the server. Mirrors\n * Blazor's `BulkOperationMode`.\n *\n * - `'individually'` (default): one `deleteRecordAsync` call per selected\n * record, awaited in sequence so a mid-batch failure short-circuits.\n * - `'bulk'`: one `executeMultipleAsync` round-trip carrying every\n * `DeleteRequest` in a single transactional batch.\n */\nexport type DeleteRecordGridButtonMode = 'individually' | 'bulk';\n\n/**\n * When the button actually contacts the server. Mirrors Blazor's\n * `GridActionBehavior` (and Blazor's default of `WithGridContext`).\n *\n * - `'withGridContext'` (default): stage the deletes via the grid's\n * pending-row queue. The actual `DeleteRequest`s are emitted from\n * the grid's descendant `getRequests` when the surrounding\n * `<MainContext>.saveAll` runs, so they land in one transactional\n * batch alongside any sibling edits. Requires a surrounding\n * `<MainContext>` or `<RecordContext>` to ever commit — without one\n * the staged deletes never ship.\n * - `'immediately'`: confirm → delete → refresh, all inside the\n * button's click handler. Use for grids that aren't hosted inside a\n * save-coordinating context (a stand-alone list page, an admin\n * table).\n */\nexport type DeleteRecordGridButtonBehavior = 'withGridContext' | 'immediately';\n\nexport interface DeleteRecordGridButtonProps {\n /** Force the button into a disabled state regardless of selection. */\n disabled?: boolean;\n /**\n * Called before the confirmation prompt runs. Throw to cancel. Errors\n * route through {@link onError} when wired, otherwise the dialog\n * service's `showErrorAsync`.\n */\n onBeforeDelete?: () => void | Promise<void>;\n /**\n * Called after every selected record has been deleted successfully and\n * the grid has refreshed. Receives the deleted records so consumers can\n * react (clear an external selection list, fire telemetry, etc.). For\n * `behavior=\"withGridContext\"`, fires immediately after the records are\n * queued — `onDeleted` doesn't await the surrounding save.\n */\n onDeleted?: (deletedRecords: readonly { tableName?: string; id?: string }[]) => void | Promise<void>;\n /**\n * Called when delete (or any lifecycle hook) throws. When provided,\n * takes over error display. When omitted, errors are surfaced via\n * `dialog.showErrorAsync` with the localized \"Delete failed\" title.\n */\n onError?: (error: unknown) => void | Promise<void>;\n /** Custom button label. Defaults to the localized `\"Delete\"`. */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n /**\n * Request-shape control for the immediate path. Defaults to\n * `'individually'`. Ignored when `behavior` is `'withGridContext'`\n * (the surrounding save batches everything regardless).\n */\n mode?: DeleteRecordGridButtonMode;\n /**\n * Stage-on-save vs. delete-now. Defaults to `'withGridContext'`: the\n * delete is queued via the grid's pending-row registry and committed\n * in the surrounding `<MainContext>.saveAll` batch. Set to\n * `'immediately'` for stand-alone grids that aren't hosted in a\n * save-coordinating context, or when the deletion should be its own\n * commit.\n */\n behavior?: DeleteRecordGridButtonBehavior;\n}\n\n/**\n * Bulk-delete every selected record in the grid. Mirrors Blazor's\n * `DeleteRecordGridButton` — disabled when nothing is selected. On click:\n *\n * 1. Calls `onBeforeDelete` (cancellable).\n * 2. Prompts a confirmation dialog (localized \"Delete N records?\" /\n * \"Delete record?\" depending on count).\n * 3. Issues a `deleteRecordAsync` per selected record, awaited in\n * sequence so the server processes them in a deterministic order\n * and a mid-batch failure short-circuits the rest.\n * 4. Triggers the grid's refresh so the deleted rows disappear from the\n * current page (and `totalRecordCount` decrements).\n * 5. Calls `onDeleted` with the records that were deleted.\n *\n * The button is also disabled when ANY selected record lacks the\n * `Delete` bit on its per-record `permissions` bitmask — the server\n * already stamps each row of the search response with the user's\n * resolved permission set (table-level + per-record-handler), so the\n * client can refuse to issue a guaranteed-failure bulk delete. Mixed\n * selections (some deletable, some not) disable the button entirely\n * rather than partially deleting; the user can de-select the\n * non-deletable rows to proceed.\n *\n * Composed on top of {@link GridButton} — the working-state\n * coalescing + `<Button>` shell live on the primitive; this component\n * holds the delete-specific selection / permission gates, the\n * confirmation prompt, and the request dispatch.\n */\nexport function DeleteRecordGridButton({\n disabled,\n onBeforeDelete,\n onDeleted,\n onError,\n label,\n appearance,\n mode = 'individually',\n behavior = 'withGridContext',\n}: DeleteRecordGridButtonProps) {\n const { ppp } = usePowerPortalsPro();\n const dialog = useDialogService();\n const t = useT();\n const resolvedLabel = label ?? t('app.buttons.delete.label');\n\n const handleClick = useCallback(\n async (grid: GridContextValue) => {\n const selectionCount = grid.selectedRecords.length;\n if (selectionCount === 0) return;\n\n if (onBeforeDelete) await onBeforeDelete();\n\n // Always include the count — matches Blazor's\n // `DeleteRecordGridButton.razor:15` which interpolates `_recordCount`\n // into the single `delete-records-confirmation` key regardless of\n // singular / plural (\"Are you sure you'd like to delete the {0}\n // record/s?\"). Consistent count display also makes it visible to\n // the user that the action operates on the SELECTED set, not just\n // the focused row.\n const message = t('app.messages.delete-records-confirmation', [\n selectionCount,\n ]);\n const result = await dialog.showConfirmationAsync(message, {\n title: t('app.messages.delete-title'),\n primaryText: t('app.buttons.delete.label'),\n secondaryText: t('app.buttons.cancel.label'),\n });\n if (result.cancelled) return;\n\n // Capture a copy of the selection up front: the grid's\n // `selectedRecords` reference changes as TanStack invalidates the\n // selection when rows disappear from the data, so iterating the\n // live array would skip entries.\n const targets = grid.selectedRecords.slice();\n const targetRefs = targets\n .filter((r) => r.id && r.tableName)\n .map((r) => ({ tableName: r.tableName!, id: r.id! }));\n\n if (behavior === 'withGridContext') {\n // Stage the deletes via the grid's pending queue. The actual\n // `DeleteRequest`s are emitted by the grid's descendant\n // `getRequests` when the surrounding `<MainContext>.saveAll`\n // runs, batching them with any sibling edits in one transactional\n // ExecuteMultiple. No refresh here — saveAll's post-save refresh\n // covers it.\n grid.queueRowDeletes(targetRefs);\n await grid.clearSelection();\n } else if (mode === 'bulk') {\n // Single ExecuteMultiple round-trip carrying one DeleteRequest\n // per selected row — mirrors Blazor's\n // `BulkOperationMode.Bulk`. The server processes the batch\n // transactionally; a mid-batch failure surfaces back with an\n // index pointing at the offending row.\n const requests: DeleteRequest[] = targetRefs.map((ref) => ({\n $type: 'DeleteRequest',\n record: ref,\n }));\n if (requests.length > 0) {\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\n }\n await grid.refresh();\n } else {\n // 'individually' — one `deleteRecordAsync` per selected record,\n // awaited in sequence so the server processes them in a\n // deterministic order and a mid-batch failure short-circuits the\n // rest.\n for (const ref of targetRefs) {\n await ppp.deleteRecordAsync(ref.tableName, ref.id);\n }\n await grid.refresh();\n }\n if (onDeleted) {\n await onDeleted(\n targets.map((r) => ({\n ...(r.tableName !== undefined && { tableName: r.tableName }),\n ...(r.id !== undefined && { id: r.id }),\n })),\n );\n }\n },\n [ppp, dialog, t, onBeforeDelete, onDeleted, mode, behavior],\n );\n\n // Per-record permission gate — disable the button when ANY selected\n // record lacks the Delete bit. Pending-create rows are always OK\n // (deleting them is a local queue-removal, no server call). The\n // GridButton's primary disabled gate covers selectionCount===0 via\n // `enabledBehavior=OneOrMoreSelected`; this `disabled` prop layers\n // the permission veto on top.\n //\n // Reading `grid.selectedRecords` here forces a re-render on selection\n // change, which is what we want — the permission gate flips with the\n // selection.\n const additionallyDisabled = usePermissionGate();\n\n // Error handling: route through onError when supplied, otherwise pop\n // a localized error dialog. This overrides GridButton's default\n // \"re-throw\" behavior because delete failures are typically\n // user-facing (network issue, permission denial mid-flight) and\n // worth surfacing inline.\n const effectiveOnError = useCallback(\n async (err: unknown) => {\n if (onError) {\n await onError(err);\n return;\n }\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\n title: t('app.errors.record-delete'),\n });\n },\n [dialog, t, onError],\n );\n\n return (\n <GridButton\n label={resolvedLabel}\n icon={<Delete20Regular />}\n onClick={handleClick}\n visibilityBehavior={GridButtonBehavior.OneOrMoreSelected}\n enabledBehavior={GridButtonBehavior.OneOrMoreSelected}\n disabled={disabled === true || additionallyDisabled}\n onError={effectiveOnError}\n {...(appearance !== undefined && { appearance })}\n />\n );\n}\n\n/**\n * Reads the current selection from the grid context and returns\n * `true` when ANY selected record lacks the `Delete` security\n * permission. Pending-create rows (synthetic\n * `__pending-create-<idx>` ids) bypass the check — deleting them is\n * a local queue-removal, no server permission applies. Records\n * without a `permissions` bitmask are treated as missing the bit\n * (defensive default — the server stamps permissions on every\n * search-response row, so a missing value means an explicit\n * default-deny).\n */\nfunction usePermissionGate(): boolean {\n // Reads the selection from the grid context. This hook only ever\n // runs inside a `<DeleteRecordGridButton>` which itself is only\n // inside `<GridButtons>` under a grid — the context is always\n // present.\n const grid = useGridContext();\n return !grid.selectedRecords.every((r) => {\n if (r.id?.startsWith('__pending-create-')) return true;\n return ((r.permissions ?? 0) & TableSecurityPermission.Delete) === TableSecurityPermission.Delete;\n });\n}\n\nsetGridButtonRole(DeleteRecordGridButton, GridButtonRole.Delete);\n","import { type ButtonProps } from '@fluentui/react-components';\nimport { LinkAdd20Regular } from '@fluentui/react-icons';\nimport { getRecordReferenceId, RelationshipFilterMode } from '@powerportalspro/core';\nimport type {\n AssociateRequest,\n components,\n OrganizationRequest,\n TableRecordReference,\n} from '@powerportalspro/core';\nimport {\n useDialogService,\n usePowerPortalsPro,\n useT,\n} from '@powerportalspro/react';\nimport { useCallback, useMemo, useState } from 'react';\nimport { useGridContext } from '../grids/grid-context.js';\nimport { buildFilter } from '../edits/many-to-many-lookup-edit.helpers.js';\nimport { LookupDialog } from '../form/lookup-dialog.js';\nimport { GridButton } from './grid-button.js';\n\ntype GridFilterBase = components['schemas']['GridFilterBase'];\n\ntype TableRecord = components['schemas']['TableRecord'];\n\n/**\n * How a `<LinkExistingRecordGridButton>` commits its associations:\n *\n * - **`withGridContext`** (default) — Queue the chosen records into the\n * grid's pending-associate registry. The surrounding `<MainContext>` /\n * `<RecordContext>` picks them up in its next transactional save so\n * the M2M link lands as part of the parent record's `executeMultiple`\n * batch. Required when the parent record doesn't have an id yet (new\n * record + subgrid flow): the AssociateRequest can't be composed until\n * after the parent's CreateRequest assigns the id, which the parent's\n * save flow handles automatically.\n * - **`immediately`** — Issue an AssociateRequest right away through\n * `executeMultipleAsync`, then refresh the grid. Use for grids where\n * each link is its own commit (the parent record already exists and\n * the user isn't editing it).\n *\n * Mirrors Blazor's `GridActionBehavior` on `LinkExistingRecordGridButton`.\n */\nexport type LinkExistingRecordBehavior = 'withGridContext' | 'immediately';\n\n/**\n * Whether the immediate-mode batch issues one Associate per selected\n * record (Individually) or a single Associate carrying every selected\n * record (Bulk). Only meaningful in `behavior=\"immediately\"` — the\n * `withGridContext` queue always emits a single Associate spanning the\n * accumulated queue. Mirrors Blazor's `BulkOperationMode`.\n */\nexport type LinkExistingBulkMode = 'individually' | 'bulk';\n\nexport interface LinkExistingRecordGridButtonProps {\n /** See {@link LinkExistingRecordBehavior}. Defaults to `withGridContext`. */\n behavior?: LinkExistingRecordBehavior;\n /** See {@link LinkExistingBulkMode}. Defaults to `individually`. */\n mode?: LinkExistingBulkMode;\n /** Force the button into a disabled state regardless of selection. */\n disabled?: boolean;\n /** Custom button label. Defaults to the localized `\"Link existing\"`. */\n label?: string;\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\n appearance?: ButtonProps['appearance'];\n /**\n * Optional dialog title override. Defaults to the localized\n * \"Look up {tableLabel}\" produced from\n * `app.components.lookup-form.title` with the grid's table label\n * filled in.\n */\n dialogTitle?: string;\n /**\n * Called after a successful commit (whether queued or immediate).\n * Receives the records that were linked.\n */\n onLinked?: (records: readonly TableRecord[]) => void | Promise<void>;\n /** Called when association (or any lifecycle hook) throws. */\n onError?: (error: unknown) => void | Promise<void>;\n}\n\n/**\n * Grid toolbar button that pops a multi-select lookup dialog for picking\n * existing records to associate with the SubGrid's parent record via the\n * SubGrid's M2M relationship. Mirrors Blazor's\n * `<LinkExistingRecordGridButton>`.\n *\n * Requires the grid to be a `<SubGrid>` — the button reads `parentRecord`\n * + `relationshipName` off `useGridContext()` and disables itself when\n * either is missing. Visible regardless of selection state (matches\n * Blazor's `GridButtonBehavior.NoneSelected` default — the action's\n * input is the lookup dialog's picker, not the grid's current row\n * selection).\n */\nexport function LinkExistingRecordGridButton({\n behavior = 'withGridContext',\n mode = 'individually',\n disabled,\n label,\n appearance,\n dialogTitle,\n onLinked,\n onError,\n}: LinkExistingRecordGridButtonProps) {\n const { ppp } = usePowerPortalsPro();\n const grid = useGridContext();\n const dialog = useDialogService();\n const t = useT();\n const resolvedLabel = label ?? t('app.buttons.link-existing.label');\n const [open, setOpen] = useState(false);\n const [working, setWorking] = useState(false);\n\n // SubGrid invariants: without parentRecord + relationshipName, the\n // AssociateRequest can't be composed. Disable the button rather than\n // letting consumers click into a guaranteed failure.\n const isSubGrid = !!grid.parentRecord && !!grid.relationshipName;\n const isDisabled = disabled === true || working || !isSubGrid;\n\n // Server-side picker dedup: when hosted inside a `<SubGrid>`, hide\n // already-linked records from the lookup dialog by composing a\n // `RelationshipFilter` with `ExcludeExistingRecords`. Same primitive\n // {@link ManyToManyLookupEdit} uses for its autocomplete pool, so the\n // server has been answering this question for a while — we just hand\n // it to the lookup picker too. Correct across ALL pages of the linked\n // set (the SubGrid's `loadedRecords` only knows the current page).\n // Memoized on the parent + relationship identity to keep MainGrid's\n // request memo stable across renders.\n const parentRecord = grid.parentRecord;\n const relationshipName = grid.relationshipName;\n const dialogFilters = useMemo<readonly GridFilterBase[] | undefined>(() => {\n if (!parentRecord || !relationshipName) return undefined;\n if (!getRecordReferenceId(parentRecord)) return undefined;\n return [\n buildFilter(parentRecord, relationshipName, RelationshipFilterMode.ExcludeExistingRecords),\n ];\n }, [parentRecord, relationshipName]);\n\n const openDialog = useCallback(() => setOpen(true), []);\n const closeDialog = useCallback(() => setOpen(false), []);\n\n const handleAction = useCallback(\n async (selectedRecords: readonly TableRecord[]) => {\n if (selectedRecords.length === 0) {\n closeDialog();\n return;\n }\n const parent = grid.parentRecord;\n const rel = grid.relationshipName;\n // Resolve via `getRecordReferenceId` so create-mode parents\n // (real `id` unset, `_idForCreate` populated) also produce a\n // valid `parentRef`. In `withGridContext` mode the grid's own\n // save loop emits the AssociateRequest with the same helper, so\n // an in-batch CreateRequest's `_idForCreate` resolves correctly.\n const parentRefId = parent ? getRecordReferenceId(parent) : undefined;\n if (!parentRefId || !parent?.tableName || !rel) {\n closeDialog();\n return;\n }\n const parentRef: TableRecordReference = {\n tableName: parent.tableName,\n id: parentRefId,\n };\n setWorking(true);\n try {\n // Belt-and-braces dedup against the grid's pending-associate queue\n // and currently-visible already-linked rows. The server-side\n // `ExcludeExistingRecords` filter on the picker already hides\n // already-linked records, so this primarily catches the\n // pending-queue case (records queued for associate in an earlier\n // dialog session aren't visible to the server yet, so they'd\n // otherwise be re-pickable). Skipping `id` from\n // `pendingRowDisassociates` deliberately: a record that's queued\n // for unlink is logically \"about to be unlinked\" — re-picking it\n // in the link dialog should cancel the unlink, which\n // `queueRowAssociates` already handles via its disassociate-queue\n // pruning. Filtering against records that lack a tableName + id\n // remains a defensive narrow on top.\n const existingIds = new Set<string>();\n for (const r of grid.pendingRowAssociates) {\n if (r.id) existingIds.add(r.id);\n }\n const pendingDisassociateIds = new Set<string>();\n for (const r of grid.pendingRowDisassociates) {\n if (r.id) pendingDisassociateIds.add(r.id);\n }\n for (const r of grid.loadedRecords) {\n if (r.id && !pendingDisassociateIds.has(r.id)) existingIds.add(r.id);\n }\n const linkable: TableRecord[] = [];\n for (const r of selectedRecords) {\n if (!r.id || !r.tableName) continue;\n if (existingIds.has(r.id)) continue;\n linkable.push(r);\n }\n\n // Nothing left after dedup — close the dialog without firing any\n // associate request. Avoids an empty `AssociateRequest` or a\n // pointless `onLinked` callback with an empty array.\n if (linkable.length === 0) {\n closeDialog();\n return;\n }\n\n if (behavior === 'withGridContext') {\n // Pass the full records so the grid can pin them as pending\n // rows visible to the user before the parent record's save\n // commits the AssociateRequest. The grid strips down to\n // {tableName, id} when composing the wire request.\n grid.queueRowAssociates(linkable);\n } else {\n // Immediate mode — ship the request(s) right now. The wire\n // surface only needs TableRecordReference, so strip down here.\n const refs: TableRecordReference[] = linkable.map((r) => ({\n tableName: r.tableName!,\n id: r.id!,\n }));\n const requests: OrganizationRequest[] = [];\n if (mode === 'bulk') {\n const req: AssociateRequest = {\n $type: 'AssociateRequest',\n record: parentRef,\n relationshipName: rel,\n relatedRecords: refs,\n };\n requests.push(req);\n } else {\n for (const ref of refs) {\n const req: AssociateRequest = {\n $type: 'AssociateRequest',\n record: parentRef,\n relationshipName: rel,\n relatedRecords: [ref],\n };\n requests.push(req);\n }\n }\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\n await grid.refresh();\n }\n closeDialog();\n // Report the records actually linked — `linkable` after the dedup\n // pass, not the raw `selectedRecords`. A consumer logging or\n // toasting from `onLinked` sees the truthful count.\n if (onLinked) await onLinked(linkable);\n } catch (err) {\n if (onError) {\n await onError(err);\n } else {\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\n title: t('app.errors.linking-records'),\n });\n }\n } finally {\n setWorking(false);\n }\n },\n [behavior, mode, grid, ppp, dialog, t, onLinked, onError, closeDialog],\n );\n\n const tableNameLabel = grid.tableName\n ? t(`tables.${grid.tableName}.label`)\n : '';\n const resolvedDialogTitle =\n dialogTitle ?? t('app.components.lookup-form.title', [tableNameLabel]);\n\n return (\n <>\n <GridButton\n label={resolvedLabel}\n icon={<LinkAdd20Regular />}\n onClick={openDialog}\n disabled={isDisabled}\n {...(appearance !== undefined && { appearance })}\n />\n {grid.tableName && (\n <LookupDialog\n hidden={!open}\n onHiddenChange={(hidden) => {\n if (hidden) closeDialog();\n }}\n title={resolvedDialogTitle}\n tableNames={[grid.tableName]}\n selectionMode=\"multiple\"\n actionLabel={t('app.buttons.link.label')}\n onActionClicked={handleAction}\n {...(dialogFilters !== undefined && { filters: dialogFilters })}\n />\n )}\n </>\n );\n}\n","import { type ButtonProps } from '@fluentui/react-components';\r\nimport { LinkDismiss20Regular } from '@fluentui/react-icons';\r\nimport { getRecordReferenceId } from '@powerportalspro/core';\r\nimport type {\r\n DisassociateRequest,\r\n OrganizationRequest,\r\n TableRecordReference,\r\n} from '@powerportalspro/core';\r\nimport {\r\n useDialogService,\r\n usePowerPortalsPro,\r\n useT,\r\n} from '@powerportalspro/react';\r\nimport { useCallback } from 'react';\r\nimport { useGridContext, type GridContextValue } from '../grids/grid-context.js';\r\nimport { GridButton } from './grid-button.js';\r\nimport { GridButtonBehavior } from './grid-button-behavior.js';\r\n\r\n/** Same behavior semantics as the Link button — see that doc. */\r\nexport type UnlinkExistingRecordBehavior = 'withGridContext' | 'immediately';\r\n\r\n/** Same bulk-mode semantics as the Link button — see that doc. */\r\nexport type UnlinkExistingBulkMode = 'individually' | 'bulk';\r\n\r\nexport interface UnlinkExistingRecordGridButtonProps {\r\n /** See {@link UnlinkExistingRecordBehavior}. Defaults to `withGridContext`. */\r\n behavior?: UnlinkExistingRecordBehavior;\r\n /** See {@link UnlinkExistingBulkMode}. Defaults to `individually`. */\r\n mode?: UnlinkExistingBulkMode;\r\n /** Force the button into a disabled state regardless of selection. */\r\n disabled?: boolean;\r\n /** Custom button label. Defaults to the localized `\"Unlink\"`. */\r\n label?: string;\r\n /** Forwarded onto Fluent's `Button.appearance`. Defaults to `\"subtle\"`. */\r\n appearance?: ButtonProps['appearance'];\r\n /**\r\n * Called after a successful commit (whether queued or immediate).\r\n * Receives the records that were unlinked.\r\n */\r\n onUnlinked?: (records: readonly TableRecordReference[]) => void | Promise<void>;\r\n /** Called when disassociation (or any lifecycle hook) throws. */\r\n onError?: (error: unknown) => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * Grid toolbar button that prompts the user to disassociate the currently\r\n * selected rows from the SubGrid's parent record via the M2M relationship.\r\n * Mirrors Blazor's `<UnlinkExistingRecordGridButton>`.\r\n *\r\n * Hidden when nothing is selected (`OneOrMoreSelected` visibility gate —\r\n * matches Blazor's `<UnlinkExistingRecordGridButton>` default). Requires\r\n * the grid to be a `<SubGrid>` — the click handler reads `parentRecord`\r\n * + `relationshipName` off the grid context and bails when either is\r\n * missing. Composed on top of {@link GridButton}.\r\n */\r\nexport function UnlinkExistingRecordGridButton({\r\n behavior = 'withGridContext',\r\n mode = 'individually',\r\n disabled,\r\n label,\r\n appearance,\r\n onUnlinked,\r\n onError,\r\n}: UnlinkExistingRecordGridButtonProps) {\r\n const { ppp } = usePowerPortalsPro();\r\n const dialog = useDialogService();\r\n const t = useT();\r\n const resolvedLabel = label ?? t('app.buttons.unlink.label');\r\n\r\n // The SubGrid-invariants gate (parent + relationship present) feeds the\r\n // `disabled` layer on top of the selection-based `enabledBehavior`.\r\n // Reads grid.selectedRecords here so the disabled state re-evaluates on\r\n // selection change.\r\n const gridForGate = useGridContext();\r\n const isSubGrid = !!gridForGate.parentRecord && !!gridForGate.relationshipName;\r\n\r\n const handleClick = useCallback(\r\n async (grid: GridContextValue) => {\r\n const selectionCount = grid.selectedRecords.length;\r\n if (selectionCount === 0) return;\r\n const parent = grid.parentRecord;\r\n const rel = grid.relationshipName;\r\n // `getRecordReferenceId` returns `_idForCreate` for create-mode\r\n // parents so `withGridContext`-mode queue updates still ride into\r\n // the parent's transactional save with a coherent reference.\r\n const parentRefId = parent ? getRecordReferenceId(parent) : undefined;\r\n if (!parentRefId || !parent?.tableName || !rel) return;\r\n const parentRef: TableRecordReference = {\r\n tableName: parent.tableName,\r\n id: parentRefId,\r\n };\r\n\r\n const message =\r\n selectionCount === 1\r\n ? t('app.messages.unlink-record-confirmation')\r\n : t('app.messages.unlink-records-confirmation', [selectionCount]);\r\n const result = await dialog.showConfirmationAsync(message, {\r\n title: t('app.messages.unlink-title'),\r\n primaryText: t('app.buttons.unlink.label'),\r\n secondaryText: t('app.buttons.cancel.label'),\r\n });\r\n if (result.cancelled) return;\r\n\r\n const refs: TableRecordReference[] = [];\r\n for (const r of grid.selectedRecords) {\r\n if (!r.id || !r.tableName) continue;\r\n refs.push({ tableName: r.tableName, id: r.id });\r\n }\r\n\r\n if (behavior === 'withGridContext') {\r\n grid.queueRowDisassociates(refs);\r\n } else {\r\n const requests: OrganizationRequest[] = [];\r\n if (mode === 'bulk') {\r\n const req: DisassociateRequest = {\r\n $type: 'DisassociateRequest',\r\n record: parentRef,\r\n relationshipName: rel,\r\n relatedRecords: refs,\r\n };\r\n requests.push(req);\r\n } else {\r\n for (const ref of refs) {\r\n const req: DisassociateRequest = {\r\n $type: 'DisassociateRequest',\r\n record: parentRef,\r\n relationshipName: rel,\r\n relatedRecords: [ref],\r\n };\r\n requests.push(req);\r\n }\r\n }\r\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\r\n await grid.refresh();\r\n }\r\n if (onUnlinked) await onUnlinked(refs);\r\n },\r\n [behavior, mode, ppp, dialog, t, onUnlinked],\r\n );\r\n\r\n // Same dialog-fallback error handling as DeleteRecordGridButton —\r\n // unlink failures are user-facing (a stale association attempted on a\r\n // record someone else already removed, etc.) so an inline message is\r\n // the right surface.\r\n const effectiveOnError = useCallback(\r\n async (err: unknown) => {\r\n if (onError) {\r\n await onError(err);\r\n return;\r\n }\r\n await dialog.showErrorAsync(err instanceof Error ? err.message : String(err), {\r\n title: t('app.errors.unlinking-records'),\r\n });\r\n },\r\n [dialog, t, onError],\r\n );\r\n\r\n return (\r\n <GridButton\r\n label={resolvedLabel}\r\n icon={<LinkDismiss20Regular />}\r\n onClick={handleClick}\r\n visibilityBehavior={GridButtonBehavior.OneOrMoreSelected}\r\n enabledBehavior={GridButtonBehavior.OneOrMoreSelected}\r\n disabled={disabled === true || !isSubGrid}\r\n onError={effectiveOnError}\r\n {...(appearance !== undefined && { appearance })}\r\n />\r\n );\r\n}\r\n","import { mergeClasses } from '@fluentui/react-components';\nimport { getRecordReferenceId, type components } from '@powerportalspro/core';\nimport { useRecordContextOptional } from '@powerportalspro/react';\nimport { useMemo } from 'react';\nimport { MainGrid, type MainGridProps } from './main-grid.js';\n\ntype TableRecord = components['schemas']['TableRecord'];\ntype GridFilterBase = components['schemas']['GridFilterBase'];\n\n/**\n * Grid that shows records related to a parent via a Dataverse relationship —\n * either one-to-many (\"contacts on this account\") or many-to-many (\"tags on\n * this article\"). Wraps {@link MainGrid} with a `RelationshipFilter` so the\n * server's `IFetchXmlQueryComposer` scopes the rows to \"children of this\n * parent.\" The server resolves whether the relationship is 1:N or N:N from\n * the parent's table metadata at filter-apply time — the client just\n * supplies parent + relationship name.\n *\n * **Shared `dataSource` mode.** When a {@link MainGridProps.dataSource}\n * (a `ViewDataSource` from `useViewDataSource()`) is passed through, the\n * SubGrid skips its own `RelationshipFilter` composition and relies on\n * the consumer to have already put the equivalent filter into the\n * datasource's `staticFilters` slot. Required because the datasource\n * owns the request shape; injecting a filter from inside the grid\n * after the consumer constructed it would silently double-apply or\n * conflict. Typical wiring:\n *\n * ```tsx\n * const staticFilters = useMemo(() => [\n * { $type: 'RelationshipFilter', parentRecord, relationshipName } as GridFilterBase,\n * ], [parentRecord, relationshipName]);\n * const ds = useViewDataSource({ viewId, staticFilters, aggregations });\n *\n * &lt;DataverseChart dataSource={ds} /&gt;\n * &lt;SubGrid dataSource={ds} relationshipName={relationshipName} record={parentRecord} /&gt;\n * ```\n *\n * The SubGrid still forwards `parentRecord` + `relationshipName` to\n * MainGrid so toolbar buttons (`<NewRecordGridButton>` parent-FK\n * pre-fill, link/unlink) keep working.\n *\n * Two ways to provide the parent record:\n *\n * 1. **Cascading from `<RecordContext>`** (most common):\n * ```tsx\n * <RecordContext table=\"account\" id={accountId}>\n * {() => <SubGrid relationshipName=\"contact_customer_accounts\" tableName=\"contact\" />}\n * </RecordContext>\n * ```\n *\n * 2. **Explicit `record` prop** when no context is in scope:\n * ```tsx\n * <SubGrid\n * relationshipName=\"contact_customer_accounts\"\n * tableName=\"contact\"\n * record={someAccountRecord}\n * />\n * ```\n *\n * Explicit prop wins over the cascading record so consumers can override\n * intentionally (e.g. a peek-at-related-records dialog whose parent isn't\n * the same as the surrounding record context).\n *\n * **Many-to-many semantics.** For M:N relationships the server defaults the\n * filter to \"records currently associated with the parent\" — the same\n * semantic a SubGrid expects. To edit the associations themselves, drop\n * `<LinkExistingRecordGridButton>` and `<UnlinkExistingRecordGridButton>`\n * into the toolbar; they read `parentRecord` + `relationshipName` off the\n * grid context and queue `AssociateRequest`/`DisassociateRequest`s that\n * the parent `<MainContext>` ships in its next transactional save.\n *\n * **Toolbar button choice cheat-sheet:**\n * - **One-to-many SubGrid:** `<NewRecordGridButton>` opens a dialog form\n * for the related record (parent FK pre-filled automatically from the\n * SubGrid's `relationshipName`); `<OpenRecordGridButton>` opens the\n * selected row in the same dialog form for editing. URL-navigation\n * variants (`<NavigateNewRecordGridButton>` / `<NavigateOpenRecordGridButton>`)\n * are alternatives when the consumer prefers a dedicated edit page over\n * an in-place dialog. `<DeleteRecordGridButton>` for bulk delete works\n * in either case. `allowEdit` is fine — inline edits update the child\n * record.\n * - **Many-to-many SubGrid:** `<LinkExistingRecordGridButton>` opens a\n * lookup dialog to pick existing records to associate;\n * `<UnlinkExistingRecordGridButton>` does the reverse on the current\n * selection. Don't pass `allowEdit={true}` — the inline-edit save path\n * issues `UpdateRequest`s against the related table, which alters the\n * related record itself rather than its association with the parent\n * (probably not what the user expects). Brand-new \"create + associate\"\n * isn't built (see the memory note `react-m2m-new-record-deferred`).\n *\n * The built-in refresh icon button is always present.\n */\nexport interface SubGridProps\n extends Omit<MainGridProps, 'filters' | 'parentRecord' | 'relationshipName'> {\n /**\n * Schema name of the relationship defined on the parent record's table.\n * Resolves server-side against the parent's `OneToMany` and `ManyToMany`\n * metadata; if neither contains the name, the request fails fast rather\n * than silently returning an unfiltered list.\n */\n relationshipName: string;\n /**\n * Parent record to scope by. Defaults to the nearest `<RecordContext>`\n * ancestor's record. When neither is available the SubGrid renders\n * nothing (it has no parent to filter by yet — typical during the\n * initial RecordContext load).\n */\n record?: TableRecord;\n}\n\nexport function SubGrid({ relationshipName, record: explicitRecord, className, ...mainGridProps }: SubGridProps) {\n // Read the optional RecordContext — falls through to `null` when no\n // ancestor `<RecordContext>` exists. Explicit prop wins so consumers\n // outside a context can still drive the SubGrid.\n const ctx = useRecordContextOptional();\n const parentRecord = explicitRecord ?? ctx?.record;\n\n // Shared-datasource mode skips SubGrid's own filter composition — the\n // consumer is expected to have built the equivalent\n // `RelationshipFilter` into `useViewDataSource({ staticFilters: [...] })`\n // at construction time. Re-injecting it from inside the grid would\n // silently double-apply (and we have no way to mutate a datasource\n // prop). See the JSDoc above for the typical wiring.\n const hasDataSource = mainGridProps.dataSource !== undefined;\n\n // Compose the filter ONCE per parent + relationship change so the\n // request memo in MainGrid sees a stable structural shape (its filters\n // key is JSON-stringified, but a stable reference here saves churn\n // upstream). Returns null when we don't have a usable parent yet —\n // MainGrid then doesn't render at all (the early-return below handles\n // that). Skipped entirely in shared-datasource mode.\n // `getRecordReferenceId` resolves the parent's effective wire id — the\n // real `id` for saved parents, `_idForCreate` for not-yet-saved ones\n // (a fresh `<RecordContext>` in create mode). For create-mode parents\n // the initial-load query returns zero rows (no associations exist\n // yet server-side), which is the right behavior. New child records\n // queued via this SubGrid pre-fill their parent-FK with the same temp\n // GUID; the server's in-batch resolver hooks them up on save.\n const parentRefId = parentRecord ? getRecordReferenceId(parentRecord) : undefined;\n\n const filters = useMemo<readonly GridFilterBase[] | null>(() => {\n if (hasDataSource) return null;\n if (!parentRecord?.tableName || !parentRefId) return null;\n return [\n {\n $type: 'RelationshipFilter',\n parentRecord: { tableName: parentRecord.tableName, id: parentRefId },\n relationshipName,\n // `mode` left at the server's default (IncludeExistingRecords)\n // — the SubGrid case wants \"records currently related to this\n // parent.\" `ExcludeExistingRecords` is for the\n // ManyToManyLookupEdit autocomplete pool, not this surface.\n } as GridFilterBase,\n ];\n }, [hasDataSource, parentRecord?.tableName, parentRefId, relationshipName]);\n\n // No parent yet — render nothing rather than firing an unfiltered query\n // that would dump every record from the related table. The\n // RecordContext load will trigger a re-render with the resolved record;\n // until then this SubGrid is invisible. Matches Blazor's\n // SubGrid.HaveLoadParametersChanged behavior which gates the load on a\n // non-null cascading Record.\n //\n // In shared-datasource mode the datasource may have already issued a\n // request before this SubGrid mounted (e.g. paired with a chart);\n // still gate on parentRecord so the toolbar buttons that need it\n // (NewRecordGridButton parent-FK pre-fill, etc.) get a real value.\n if (!parentRecord?.tableName || !parentRefId) return null;\n\n // Forward the parent record + relationship name to MainGrid so toolbar\n // buttons (specifically `<NewRecordGridButton>` for parent-FK pre-fill,\n // `<LinkExistingRecordGridButton>` / `<UnlinkExistingRecordGridButton>`\n // for the M:N associate/disassociate flow) can read them off the grid\n // context. The conditional `filters` spread keeps the prop absent in\n // shared-datasource mode so MainGrid doesn't see a stale local filter\n // (its dataSource branch ignores `filters` anyway, but explicit is\n // less surprising).\n return (\n <MainGrid\n {...mainGridProps}\n {...(filters && { filters })}\n parentRecord={parentRecord}\n relationshipName={relationshipName}\n className={mergeClasses('ppp-sub-grid', className)}\n />\n );\n}\n","import type { components } from '@powerportalspro/core';\r\nimport { getFileExtension } from '../edits/base/file-helpers.js';\r\n\r\ntype ViewMetadata = components['schemas']['ViewMetadata'];\r\n\r\n/**\r\n * Returns a clone of `view` that lists `fileColumn` in both its\r\n * `columns` array and its FetchXML `<entity>` block. Mirrors the\r\n * Blazor `EnsureFileColumnPresent` step so the file cell is visible\r\n * no matter which view the user picks.\r\n *\r\n * The view metadata cache is shared across grid instances, so we\r\n * clone rather than mutate.\r\n */\r\nexport function ensureFileColumnInView(view: ViewMetadata, fileColumn: string): ViewMetadata {\r\n const alreadyInColumns = view.columns.some((c) => c.columnName === fileColumn);\r\n const alreadyInFetchXml = fetchXmlHasAttribute(view.fetchXml, fileColumn);\r\n if (alreadyInColumns && alreadyInFetchXml) return view;\r\n\r\n const columns = alreadyInColumns\r\n ? view.columns\r\n : [...view.columns, { columnName: fileColumn, width: 250 }];\r\n const fetchXml = alreadyInFetchXml\r\n ? view.fetchXml\r\n : addFetchXmlAttribute(view.fetchXml, fileColumn);\r\n\r\n return { ...view, columns, fetchXml };\r\n}\r\n\r\n/**\r\n * True when the first `<entity>` element in `fetchXml` already declares\r\n * an `<attribute name=\"...\"/>` for `attributeName`. Lenient on\r\n * whitespace and quoting; returns false on parse failures so the\r\n * caller falls back to \"inject it\" rather than silently dropping the\r\n * column.\r\n */\r\nexport function fetchXmlHasAttribute(fetchXml: string, attributeName: string): boolean {\r\n const doc = parseFetchXml(fetchXml);\r\n if (!doc) return false;\r\n const entity = doc.querySelector('entity');\r\n if (!entity) return false;\r\n return Array.from(entity.children).some(\r\n (child) => child.tagName === 'attribute' && child.getAttribute('name') === attributeName,\r\n );\r\n}\r\n\r\n/**\r\n * Adds `<attribute name=\"<attributeName>\"/>` as the last child of the\r\n * first `<entity>` element in `fetchXml`, returning the serialized\r\n * result. No-op (returns input unchanged) when parsing fails — the\r\n * server-side composer will surface the malformed query if it ever\r\n * actually reaches it.\r\n */\r\nexport function addFetchXmlAttribute(fetchXml: string, attributeName: string): string {\r\n const doc = parseFetchXml(fetchXml);\r\n if (!doc) return fetchXml;\r\n const entity = doc.querySelector('entity');\r\n if (!entity) return fetchXml;\r\n\r\n const attr = doc.createElement('attribute');\r\n attr.setAttribute('name', attributeName);\r\n entity.appendChild(attr);\r\n\r\n return new XMLSerializer().serializeToString(doc);\r\n}\r\n\r\nfunction parseFetchXml(fetchXml: string): Document | null {\r\n try {\r\n const doc = new DOMParser().parseFromString(fetchXml, 'application/xml');\r\n // DOMParser returns a <parsererror> root on failure rather than\r\n // throwing — sniff for it and treat as a soft failure.\r\n if (doc.getElementsByTagName('parsererror').length > 0) return null;\r\n return doc;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * True when `fileName` ends in an extension matching the\r\n * (case-insensitive, dot-tolerant) allow list, or when the allow list\r\n * is empty / undefined. Mirrors Blazor's `IsExtensionAllowed`.\r\n */\r\nexport function isExtensionAllowed(\r\n fileName: string,\r\n allowedExtensions: readonly string[] | undefined,\r\n): boolean {\r\n if (!allowedExtensions || allowedExtensions.length === 0) return true;\r\n const ext = getFileExtension(fileName);\r\n if (!ext) return false;\r\n return allowedExtensions\r\n .map((x) => x.replace(/^\\./, '').toLowerCase())\r\n .includes(ext);\r\n}\r\n\r\n/**\r\n * True when `fileName`'s extension appears in `blockedExtensions`.\r\n * Blocked-list match always wins over the allow list — mirrors the\r\n * Blazor enforcement order.\r\n */\r\nexport function isExtensionBlocked(\r\n fileName: string,\r\n blockedExtensions: ReadonlySet<string>,\r\n): boolean {\r\n return blockedExtensions.has(getFileExtension(fileName));\r\n}\r\n\r\n","import { makeStyles, tokens } from '@fluentui/react-components';\r\n\r\n/**\r\n * Styles for the FileGrid wrapper — the drag-and-drop outer container,\r\n * the absolute-positioned dropzone overlay, and the busy-overlay\r\n * positioning helper. Mirrors the Blazor `FileGrid.razor.css`.\r\n *\r\n * Color values come from FluentUI design tokens so light/dark theme\r\n * switching and consumer-customized accents track automatically.\r\n */\r\nexport const useFileGridStyles = makeStyles({\r\n // Outer wrapper that receives the drag events. `position: relative`\r\n // anchors the dropzone overlay; full width matches the inner grid's\r\n // natural sizing.\r\n wrapper: {\r\n position: 'relative',\r\n width: '100%',\r\n minWidth: 0,\r\n },\r\n // Soft accent outline drawn while a drag is in progress. `outline`\r\n // (not `border`) is used so no extra layout space is taken, avoiding\r\n // a 2px jitter when the user crosses the grid boundary mid-drag.\r\n wrapperDragActive: {\r\n outlineWidth: '2px',\r\n outlineStyle: 'dashed',\r\n outlineColor: tokens.colorBrandStroke1,\r\n outlineOffset: '-2px',\r\n },\r\n // Full-bleed translucent overlay shown only while dragging. The\r\n // overlay itself is `pointer-events: none` so the underlying drop\r\n // handlers on the wrapper still receive the events — the overlay\r\n // is purely visual feedback.\r\n dropZone: {\r\n position: 'absolute',\r\n inset: 0,\r\n zIndex: 10,\r\n pointerEvents: 'none',\r\n backgroundColor: tokens.colorBrandBackground2,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: tokens.colorBrandForeground1,\r\n fontWeight: tokens.fontWeightSemibold,\r\n fontSize: tokens.fontSizeBase400,\r\n },\r\n // Centered card inside the overlay — icon stacked above the prompt\r\n // text. Sits on a solid panel so the prompt is readable regardless\r\n // of the rows underneath.\r\n dropZoneInner: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n rowGap: tokens.spacingVerticalS,\r\n paddingTop: tokens.spacingVerticalL,\r\n paddingBottom: tokens.spacingVerticalL,\r\n paddingLeft: tokens.spacingHorizontalXXL,\r\n paddingRight: tokens.spacingHorizontalXXL,\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n borderRadius: tokens.borderRadiusMedium,\r\n boxShadow: tokens.shadow8,\r\n pointerEvents: 'none',\r\n },\r\n // Hidden file inputs — kept in the DOM so the Upload / Replace\r\n // buttons can `.click()` them, but never visible. `display: none`\r\n // works fine here; Safari/Chrome both fire the OS picker correctly\r\n // from a programmatic click on a hidden input.\r\n hiddenInput: {\r\n display: 'none',\r\n },\r\n // Full-bleed busy scrim shown while uploads / downloads / replace\r\n // operations are in flight. `position: absolute` over the wrapper\r\n // (which is `position: relative`) so the spinner pins above the\r\n // grid without scrolling with it. Above the dropzone overlay\r\n // (zIndex 10) so a slow upload after a drop is visible.\r\n busyOverlay: {\r\n position: 'absolute',\r\n inset: 0,\r\n zIndex: 11,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: tokens.colorNeutralBackgroundAlpha,\r\n },\r\n});\r\n","import { mergeClasses } from '@fluentui/react-components';\r\nimport type { CSSProperties, ReactNode } from 'react';\r\nimport { useMainGridStyles } from './main-grid-styles.js';\r\n\r\n/**\r\n * Optional flex container for grouping grid action buttons inside any\r\n * `<MainGrid>` or `<SubGrid>`. Matches the Blazor `<Buttons>` render\r\n * fragment slot — consumers drop button components inside this wrapper\r\n * and they line up horizontally with the right spacing automatically.\r\n *\r\n * ```tsx\r\n * <MainGrid tableName=\"account\">\r\n * <GridButtons>\r\n * <NewRecordGridButton />\r\n * <OpenRecordGridButton />\r\n * <DeleteRecordGridButton />\r\n * <RefreshGridButton />\r\n * </GridButtons>\r\n * </MainGrid>\r\n * ```\r\n *\r\n * Bare buttons rendered directly under `<MainGrid>` / `<SubGrid>` also\r\n * work — this component is purely a layout convenience. Buttons read the\r\n * grid's selection / refresh / table info via {@link useGridContext}\r\n * regardless of whether they're wrapped here or not.\r\n */\r\nexport interface GridButtonsProps {\r\n children?: ReactNode;\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\nexport function GridButtons({ children, className, style }: GridButtonsProps) {\r\n const styles = useMainGridStyles();\r\n return (\r\n <div className={mergeClasses(styles.buttonsBar, className)} style={style}>\r\n {children}\r\n </div>\r\n );\r\n}\r\n","/**\n * Wraps {@link MainGrid} and adds:\n *\n * - Drag-and-drop upload (whole-grid drop target with overlay feedback).\n * - Toolbar Upload button (multi-file picker).\n * - Toolbar Replace button (single-file picker; replaces the selected row).\n * - Toolbar Download All / Download Selected (single file streams; many\n * files are zipped client-side via `fflate`).\n * - Toolbar Delete (delegated to `<DeleteRecordGridButton>`).\n * - Optional view transform that ensures the configured file column is\n * visible in every view the user picks.\n * - Client-side enforcement of per-column / environment max file size and\n * of allow- / block-listed extensions.\n *\n * Behavior when wrapped in a `<MainContext>`: uploads + replaces queue\n * into the grid's pending row updates / creates so they ship as part of\n * the next `executeMultiple` save batch. Outside a `<MainContext>` (the\n * free-standing case), they fire immediately through `createRecordAsync` /\n * `updateRecordAsync`.\n */\n\nimport {\n Button,\n Spinner,\n Tooltip,\n makeStyles,\n mergeClasses,\n} from '@fluentui/react-components';\nimport {\n ArrowSync24Regular,\n ArrowUpload24Regular,\n ArrowUpload32Regular,\n DrawerArrowDownload24Regular,\n} from '@fluentui/react-icons';\nimport type {\n components,\n TableSecurityPermission as SecurityPermissionType,\n ViewMetadata,\n} from '@powerportalspro/core';\nimport { TableSecurityPermission } from '@powerportalspro/core';\nimport {\n useDialogService,\n useEnvironmentFileSettings,\n useMainContext,\n usePowerPortalsPro,\n useT,\n useTableMetadata,\n} from '@powerportalspro/react';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { DeleteRecordGridButton } from '../buttons/delete-record-grid-button.js';\nimport {\n EMPTY_GUID,\n base64ToBytes,\n getFileExtension,\n readFileAsBase64,\n triggerBlobDownload,\n} from '../edits/base/file-helpers.js';\nimport {\n ensureFileColumnInView,\n isExtensionAllowed,\n isExtensionBlocked,\n} from './file-grid-helpers.js';\nimport { useFileGridStyles } from './file-grid-styles.js';\nimport { GridButtons } from './grid-buttons.js';\nimport { useGridContext, type GridContextValue } from './grid-context.js';\nimport { MainGrid, type MainGridProps } from './main-grid.js';\n\ntype TableRecord = components['schemas']['TableRecord'];\ntype FileColumnValueWire = components['schemas']['ColumnValueBaseFileValue'];\n\n/** Default cap, matches Blazor (`128 * 1024 * 1024`). */\nconst DEFAULT_MAX_FILE_SIZE_BYTES = 128 * 1024 * 1024;\n\n/** Hard ceiling on the multi-file picker. Matches Blazor's `maximumFileCount: 1000`. */\nconst MAX_FILES_PER_UPLOAD = 1000;\n\n/** Subset of {@link MainGridProps} forwarded to the inner grid. */\ntype ForwardedMainGridProps = Pick<\n MainGridProps,\n | 'tableName'\n | 'viewId'\n | 'defaultViewId'\n | 'viewIds'\n | 'customViewDefinitions'\n | 'viewSort'\n | 'pageSize'\n | 'pageSizes'\n | 'pagingMode'\n | 'allowChangingPageSize'\n | 'selectionMode'\n | 'selectFromEntireRow'\n | 'onSelectedRecordsChange'\n | 'selectedRecords'\n | 'minHeight'\n | 'maxHeight'\n | 'fullSize'\n | 'allowSearch'\n | 'hidePaging'\n | 'title'\n | 'persistedStateQueryParameter'\n | 'includeSearchInPersistedState'\n | 'allowPreviewForFileColumns'\n | 'allowDownloadForFileColumns'\n | 'columnWidthScale'\n | 'borderVisible'\n | 'showRefreshButton'\n | 'cellRenderers'\n | 'typeRenderers'\n | 'allowNavigateOnPrimaryNameClick'\n | 'allowOpenOnRowDoubleClick'\n>;\n\nexport interface FileGridProps extends ForwardedMainGridProps {\n /** Required logical name of the table whose file records this grid manages. */\n tableName: string;\n /**\n * Logical name of the file column on {@link tableName} this grid\n * manages. Required because a record may expose several file columns\n * and the grid needs to know which one the upload / download /\n * replace actions target.\n */\n fileColumn: string;\n /**\n * Whether the grid auto-adds {@link fileColumn} to the active view\n * when the view doesn't already include it. Defaults to `true` so\n * the file cell is visible no matter which view the user picks.\n */\n autoAddFileColumnIfMissingFromView?: boolean;\n /**\n * Whether the toolbar's \"Download All\" button is rendered. The\n * per-row download icon (when enabled) still works. Defaults to `true`.\n */\n allowDownloadAll?: boolean;\n /**\n * Upper bound on a single upload, in bytes. The effective cap is the\n * minimum of this value, the file column's metadata `maxSizeInKB`,\n * and the environment's `maxUploadFileSizeInBytes`. Defaults to\n * 128MB — Dataverse's documented ceiling for file columns.\n */\n maxFileSizeBytes?: number;\n /**\n * Optional allow-list of extensions (with or without leading dot,\n * case-insensitive). When set, files whose extension isn't in the\n * list are rejected before upload. `undefined` accepts any\n * extension.\n */\n allowedExtensions?: readonly string[];\n /**\n * Customises the record created for each uploaded file. Receives the\n * file name and bytes; returns the `TableRecord` to create (which\n * must have the file column populated). When `undefined`, the grid\n * creates a minimal record with only the file column populated.\n */\n onUploadRecordAsync?: (fileName: string, data: Uint8Array) => Promise<TableRecord>;\n /**\n * Optional hook to transform the active view. Runs <em>after</em> the\n * built-in file-column injection, so callers can layer additional\n * view tweaks without losing the file-column guarantee.\n */\n transformViewAsync?: (view: ViewMetadata) => Promise<ViewMetadata>;\n /** Caller-supplied grid buttons rendered alongside the built-in file buttons. */\n children?: ReactNode;\n /** Forwarded onto the outer wrapper. */\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * Bridge component rendered inside {@link MainGrid}'s children so the\n * outer FileGrid can read the grid context — needed for the drop\n * handler (queueRowCreate / refresh) and the toolbar Replace flow\n * (queueRowUpdate / pendingRowCreates).\n *\n * Context lives behind a ref because the drop handler is captured by\n * the DOM event system and shouldn't churn on every grid-context\n * update.\n */\nfunction GridContextBridge({\n contextRef,\n}: {\n contextRef: React.MutableRefObject<GridContextValue | null>;\n}) {\n const ctx = useGridContext();\n contextRef.current = ctx;\n return null;\n}\n\nexport function FileGrid(props: FileGridProps) {\n const {\n tableName,\n fileColumn,\n autoAddFileColumnIfMissingFromView = true,\n allowDownloadAll = true,\n maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES,\n allowedExtensions,\n onUploadRecordAsync,\n transformViewAsync,\n children,\n className,\n style,\n // Forwarded MainGrid props with FileGrid-friendly defaults.\n selectionMode = 'multiple',\n selectFromEntireRow = true,\n pageSize = 50,\n ...forwardedMainGridProps\n } = props;\n\n const styles = useFileGridStyles();\n const t = useT();\n const dialog = useDialogService();\n const { ppp } = usePowerPortalsPro();\n const mainContext = useMainContext();\n\n const tableMetadata = useTableMetadata(tableName);\n const environmentFileSettings = useEnvironmentFileSettings();\n\n // ---------------------------------------------------------------------------\n // Derived caps and blocklists (recomputed when metadata / settings land).\n // ---------------------------------------------------------------------------\n\n // Effective per-upload cap: the smallest of consumer-supplied cap, the\n // column's metadata `maxSizeInKB`, and the environment's\n // `maxUploadFileSizeInBytes`. We compute it on every render rather than in\n // an effect to avoid a one-render window where the cap is wrong.\n const effectiveMaxFileSizeBytes = useMemo(() => {\n let cap = maxFileSizeBytes;\n // `columns` is an array of typed-union entries; the file column carries\n // `maxSizeInKB`. Other column kinds either omit the property or carry\n // an unrelated value (Decimal `maxValue`, etc.) — locating by\n // `columnName` AND duck-typing on `maxSizeInKB` keeps the cap safe even\n // if the consumer misnames the column.\n const columnMeta = tableMetadata.data?.columns?.find(\n (c) => (c as { columnName?: string }).columnName === fileColumn,\n ) as { maxSizeInKB?: number | string } | undefined;\n const columnMaxKb = columnMeta?.maxSizeInKB;\n if (columnMaxKb !== undefined && columnMaxKb !== null) {\n const kb = typeof columnMaxKb === 'string' ? Number(columnMaxKb) : columnMaxKb;\n if (Number.isFinite(kb) && kb > 0) {\n cap = Math.min(cap, kb * 1024);\n }\n }\n const envMaxRaw = environmentFileSettings.data?.maxUploadFileSizeInBytes;\n const envMax = typeof envMaxRaw === 'string' ? Number(envMaxRaw) : envMaxRaw;\n if (typeof envMax === 'number' && Number.isFinite(envMax) && envMax > 0) {\n cap = Math.min(cap, envMax);\n }\n return cap;\n }, [\n maxFileSizeBytes,\n tableMetadata.data,\n fileColumn,\n environmentFileSettings.data,\n ]);\n\n const blockedExtensions = useMemo(() => {\n return new Set<string>(environmentFileSettings.data?.blockedExtensions ?? []);\n }, [environmentFileSettings.data]);\n\n // ---------------------------------------------------------------------------\n // Refs + local state.\n // ---------------------------------------------------------------------------\n\n const uploadInputRef = useRef<HTMLInputElement | null>(null);\n const replaceInputRef = useRef<HTMLInputElement | null>(null);\n const gridContextRef = useRef<GridContextValue | null>(null);\n\n // Counter of nested drag-enter / drag-leave events so the overlay\n // doesn't flicker as the cursor crosses inner children. Matches the\n // Blazor JS dropzone's `dragDepth` accounting.\n const dragDepthRef = useRef(0);\n\n const [dragActive, setDragActive] = useState(false);\n const [busy, setBusy] = useState(false);\n // Replace target captured when the toolbar Replace button is clicked,\n // consumed when the hidden file input change fires (success or\n // cancel). Kept in a ref because the file-picker round-trip is async.\n const replaceTargetRef = useRef<TableRecord | null>(null);\n\n const accept = useMemo(() => {\n if (!allowedExtensions || allowedExtensions.length === 0) return undefined;\n return allowedExtensions.map((e) => (e.startsWith('.') ? e : `.${e}`)).join(',');\n }, [allowedExtensions]);\n\n // ---------------------------------------------------------------------------\n // View transform composition — file-column injection + caller transform.\n // ---------------------------------------------------------------------------\n\n const composedTransformViewAsync = useMemo(() => {\n if (!autoAddFileColumnIfMissingFromView && !transformViewAsync) return undefined;\n return async (view: ViewMetadata): Promise<ViewMetadata> => {\n const withFile = autoAddFileColumnIfMissingFromView\n ? ensureFileColumnInView(view, fileColumn)\n : view;\n return transformViewAsync ? await transformViewAsync(withFile) : withFile;\n };\n }, [autoAddFileColumnIfMissingFromView, fileColumn, transformViewAsync]);\n\n // ---------------------------------------------------------------------------\n // Common helpers.\n // ---------------------------------------------------------------------------\n\n const showError = useCallback(\n (message: string, title?: string) => {\n void dialog.showErrorAsync(message, title ? { title } : undefined);\n },\n [dialog],\n );\n\n const filterAndReportExtensions = useCallback(\n (\n files: readonly { name: string; size: number; bytes: Uint8Array }[],\n ): { name: string; size: number; bytes: Uint8Array }[] => {\n const ok: { name: string; size: number; bytes: Uint8Array }[] = [];\n for (const file of files) {\n if (file.size > effectiveMaxFileSizeBytes) {\n showError(\n t('app.components.file-grid.file-too-large', [\n file.name,\n file.size,\n effectiveMaxFileSizeBytes,\n ]),\n );\n continue;\n }\n if (isExtensionBlocked(file.name, blockedExtensions)) {\n showError(\n t('app.components.file-grid.extension-blocked', [\n file.name,\n getFileExtension(file.name),\n ]),\n );\n continue;\n }\n if (!isExtensionAllowed(file.name, allowedExtensions)) {\n showError(\n t('app.components.file-grid.extension-not-allowed', [\n file.name,\n (allowedExtensions ?? []).join(', '),\n ]),\n );\n continue;\n }\n ok.push(file);\n }\n return ok;\n },\n [\n allowedExtensions,\n blockedExtensions,\n effectiveMaxFileSizeBytes,\n showError,\n t,\n ],\n );\n\n const buildDefaultUploadRecord = useCallback(\n (fileName: string, base64: string): TableRecord => {\n const fileValue: FileColumnValueWire = {\n $type: 'FileValue',\n fileName,\n id: EMPTY_GUID,\n value: base64,\n };\n return {\n tableName,\n properties: { [fileColumn]: fileValue },\n } as TableRecord;\n },\n [fileColumn, tableName],\n );\n\n // ---------------------------------------------------------------------------\n // Upload — shared path between drop and file-picker change.\n // ---------------------------------------------------------------------------\n\n const uploadFiles = useCallback(\n async (files: readonly { name: string; size: number; bytes: Uint8Array }[]) => {\n const grid = gridContextRef.current;\n const accepted = filterAndReportExtensions(files);\n if (accepted.length === 0) return;\n\n setBusy(true);\n try {\n const records: TableRecord[] = [];\n for (const { name, bytes } of accepted) {\n const base64 = bytesToBase64(bytes);\n const record = onUploadRecordAsync\n ? await onUploadRecordAsync(name, bytes)\n : buildDefaultUploadRecord(name, base64);\n records.push(record);\n }\n if (records.length === 0) return;\n\n if (mainContext) {\n // Queue creates into the grid's pending list; the surrounding\n // MainContext's save batches them into one `executeMultiple`.\n for (const record of records) {\n grid?.queueRowCreate({ record });\n }\n await grid?.refresh();\n } else {\n // Free-standing: each file uploads immediately. The server-side\n // create handler routes FileValue through `SetFileField` after\n // the (minimal) Create call.\n for (const record of records) {\n await ppp.createRecordAsync(record);\n }\n await grid?.refresh();\n }\n } catch (err) {\n showError(\n err instanceof Error ? err.message : String(err),\n t('app.components.file-grid.upload-failed-title'),\n );\n } finally {\n setBusy(false);\n }\n },\n [\n buildDefaultUploadRecord,\n filterAndReportExtensions,\n mainContext,\n onUploadRecordAsync,\n ppp,\n showError,\n t,\n ],\n );\n\n // ---------------------------------------------------------------------------\n // File input change handlers.\n // ---------------------------------------------------------------------------\n\n const onUploadInputChange = useCallback(\n async (ev: React.ChangeEvent<HTMLInputElement>) => {\n const fileList = ev.target.files;\n if (!fileList) return;\n const picked = Array.from(fileList).slice(0, MAX_FILES_PER_UPLOAD);\n // Reset so re-picking the same file fires onChange again.\n ev.target.value = '';\n\n const payloads: { name: string; size: number; bytes: Uint8Array }[] = [];\n for (const f of picked) {\n const buf = await f.arrayBuffer();\n payloads.push({ name: f.name, size: f.size, bytes: new Uint8Array(buf) });\n }\n await uploadFiles(payloads);\n },\n [uploadFiles],\n );\n\n const onReplaceInputChange = useCallback(\n async (ev: React.ChangeEvent<HTMLInputElement>) => {\n const target = replaceTargetRef.current;\n replaceTargetRef.current = null;\n\n const file = ev.target.files?.[0];\n ev.target.value = '';\n if (!target || !file) return;\n\n if (file.size > effectiveMaxFileSizeBytes) {\n showError(\n t('app.components.file-grid.file-too-large', [\n file.name,\n file.size,\n effectiveMaxFileSizeBytes,\n ]),\n );\n return;\n }\n if (isExtensionBlocked(file.name, blockedExtensions)) {\n showError(\n t('app.components.file-grid.extension-blocked', [\n file.name,\n getFileExtension(file.name),\n ]),\n );\n return;\n }\n if (!isExtensionAllowed(file.name, allowedExtensions)) {\n showError(\n t('app.components.file-grid.extension-not-allowed', [\n file.name,\n (allowedExtensions ?? []).join(', '),\n ]),\n );\n return;\n }\n\n const grid = gridContextRef.current;\n setBusy(true);\n try {\n const base64 = await readFileAsBase64(file);\n const fileValue: FileColumnValueWire = {\n $type: 'FileValue',\n fileName: file.name,\n id: target.id ?? EMPTY_GUID,\n value: base64,\n };\n\n if (mainContext && target.id) {\n grid?.queueRowUpdate({\n tableName,\n recordId: target.id,\n dirty: new Map([[fileColumn, fileValue]]),\n });\n await grid?.refresh();\n } else if (target.id) {\n const update: TableRecord = {\n tableName,\n id: target.id,\n properties: { [fileColumn]: fileValue },\n } as TableRecord;\n await ppp.updateRecordAsync(update);\n await grid?.refresh();\n }\n } catch (err) {\n showError(\n err instanceof Error ? err.message : String(err),\n t('app.components.file-grid.replace-failed-title'),\n );\n } finally {\n setBusy(false);\n }\n },\n [\n allowedExtensions,\n blockedExtensions,\n effectiveMaxFileSizeBytes,\n fileColumn,\n mainContext,\n ppp,\n showError,\n t,\n tableName,\n ],\n );\n\n // ---------------------------------------------------------------------------\n // Download (single = stream, multi = client-side zip).\n // ---------------------------------------------------------------------------\n\n const downloadRecords = useCallback(\n async (records: readonly TableRecord[]) => {\n const ids = records.map((r) => r.id).filter((id): id is string => !!id);\n if (ids.length === 0) {\n showError(\n t('app.components.file-grid.no-records-selected'),\n t('app.components.file-grid.nothing-to-download-title'),\n );\n return;\n }\n\n setBusy(true);\n try {\n if (ids.length === 1) {\n // Single-file download still goes through GetFileInfo rather than the\n // archive endpoint — the user expects the raw file (preserved name and\n // mime type), not a zip of one. Skipping the server-side archive for\n // n=1 keeps that contract.\n const onlyId = ids[0]!;\n const info = await ppp.getFileInfoAsync(tableName, onlyId, fileColumn, true);\n if (!info.fileName || !info.fileData) {\n showError(t('app.components.file-grid.no-file-attached'));\n return;\n }\n triggerBlobDownload(base64ToBytes(info.fileData), info.fileName);\n return;\n }\n\n // Multi-file: server builds the archive in one round-trip. The endpoint\n // returns binary bytes with a `Content-Disposition: attachment;\n // filename=...` header by default — no base64 detour, no client-side\n // zip work. Empty archives come back as an empty `data` buffer; we\n // surface the \"nothing attached\" message rather than streaming an\n // empty zip to the browser.\n const archive = await ppp.createFileArchiveAsync({\n tableName,\n columnName: fileColumn,\n recordIds: ids,\n // Format/responseFormat default server-side (Zip / BinaryStream).\n } as Parameters<typeof ppp.createFileArchiveAsync>[0]);\n if (archive.data.length === 0) {\n showError(t('app.components.file-grid.no-files-attached'));\n return;\n }\n triggerBlobDownload(archive.data, archive.fileName);\n } catch (err) {\n showError(\n err instanceof Error ? err.message : String(err),\n t('app.components.file-grid.download-failed-title'),\n );\n } finally {\n setBusy(false);\n }\n },\n [fileColumn, ppp, showError, t, tableName],\n );\n\n // ---------------------------------------------------------------------------\n // Drag-and-drop wiring on the outer wrapper.\n // ---------------------------------------------------------------------------\n\n // Re-entry into the wrapper from a nested child fires another\n // `dragenter`; bumping a counter and only flipping `dragActive` at\n // depth 1/0 keeps the overlay from flickering as the pointer crosses\n // grid rows and toolbar buttons.\n const onDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n dragDepthRef.current += 1;\n if (dragDepthRef.current === 1) setDragActive(true);\n }, []);\n\n const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n // Browsers ignore drops without an explicit preventDefault on dragover.\n e.preventDefault();\n }, []);\n\n const onDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);\n if (dragDepthRef.current === 0) setDragActive(false);\n }, []);\n\n const onDrop = useCallback(\n async (e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n dragDepthRef.current = 0;\n setDragActive(false);\n\n const fileList = e.dataTransfer?.files;\n if (!fileList || fileList.length === 0) return;\n\n const grid = gridContextRef.current;\n const canCreate = hasPermission(grid?.tablePermissions, TableSecurityPermission.Create);\n // When tablePermissions is undefined (empty grid, nothing fetched\n // yet), we accept the drop and let server-side enforce — matches\n // the documented fallback behavior.\n if (grid?.tablePermissions !== undefined && !canCreate) return;\n\n const picked = Array.from(fileList).slice(0, MAX_FILES_PER_UPLOAD);\n const payloads: { name: string; size: number; bytes: Uint8Array }[] = [];\n for (const f of picked) {\n const buf = await f.arrayBuffer();\n payloads.push({ name: f.name, size: f.size, bytes: new Uint8Array(buf) });\n }\n await uploadFiles(payloads);\n },\n [uploadFiles],\n );\n\n // Clear drag state when the document loses the drag (Esc, drop outside).\n // Without this, the overlay can stick if the user drops outside the wrapper.\n useEffect(() => {\n const onDocDragEnd = () => {\n dragDepthRef.current = 0;\n setDragActive(false);\n };\n document.addEventListener('dragend', onDocDragEnd);\n return () => document.removeEventListener('dragend', onDocDragEnd);\n }, []);\n\n // ---------------------------------------------------------------------------\n // Render — wrap MainGrid + bridge + toolbar buttons.\n // ---------------------------------------------------------------------------\n\n return (\n <div\n className={mergeClasses(\n 'ppp-file-grid',\n styles.wrapper,\n dragActive && styles.wrapperDragActive,\n className,\n )}\n style={style}\n onDragEnter={onDragEnter}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n onDrop={onDrop}\n >\n <input\n ref={uploadInputRef}\n type=\"file\"\n multiple\n className={styles.hiddenInput}\n accept={accept}\n onChange={onUploadInputChange}\n />\n <input\n ref={replaceInputRef}\n type=\"file\"\n className={styles.hiddenInput}\n accept={accept}\n onChange={onReplaceInputChange}\n />\n\n <MainGrid\n tableName={tableName}\n selectionMode={selectionMode}\n selectFromEntireRow={selectFromEntireRow}\n pageSize={pageSize}\n {...(composedTransformViewAsync !== undefined && {\n transformViewAsync: composedTransformViewAsync,\n })}\n {...forwardedMainGridProps}\n >\n <GridContextBridge contextRef={gridContextRef} />\n <GridButtons>\n <UploadGridButton\n onClick={() => uploadInputRef.current?.click()}\n />\n <ReplaceGridButton\n onClick={(record) => {\n replaceTargetRef.current = record;\n replaceInputRef.current?.click();\n }}\n />\n {allowDownloadAll && (\n <DownloadGridButton onDownload={downloadRecords} />\n )}\n <DeleteRecordGridButton />\n {children}\n </GridButtons>\n </MainGrid>\n\n {dragActive && (\n <div className={styles.dropZone}>\n <div className={styles.dropZoneInner}>\n <ArrowUpload32Regular />\n <span>{t('app.components.file-grid.drop-files-prompt')}</span>\n </div>\n </div>\n )}\n\n {busy && (\n <div className={styles.busyOverlay}>\n <Spinner size=\"medium\" />\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Internal toolbar buttons.\n//\n// These read selection / table permissions via `useGridContext()`. They\n// don't take labels / icons as props — FileGrid owns the labels through\n// localization keys, matching how Blazor's `<GridButton>` rendering in\n// the FileGrid template lays them out.\n// ---------------------------------------------------------------------------\n\nconst useButtonStyles = makeStyles({\n // Inline-flex so the icon + text stay tight and the tooltip wraps\n // the entire button (Fluent's <Tooltip> wraps its child outright).\n button: { minWidth: 'unset' },\n});\n\nfunction UploadGridButton({ onClick }: { onClick: () => void }) {\n const t = useT();\n const buttonStyles = useButtonStyles();\n const grid = useGridContext();\n const selectionCount = grid.selectedRecords.length;\n // Hide once a row is selected — Blazor's `IsButtonVisible=\"rows => _canCreate && !rows.Any()\"`.\n if (selectionCount > 0) return null;\n // Empty-grid fallback: tablePermissions is undefined → keep the\n // button visible (matches Blazor's \"permission check resolved =\n // false\" default of hiding, but on the React side we don't have the\n // separate `_tablePermissionCache` lookup yet; falling back to\n // visible defers to the server's authoritative check on click).\n if (\n grid.tablePermissions !== undefined &&\n !hasPermission(grid.tablePermissions, TableSecurityPermission.Create)\n ) {\n return null;\n }\n return (\n <Tooltip\n relationship=\"label\"\n content={t('app.components.file-grid.upload-tooltip')}\n >\n <Button\n appearance=\"subtle\"\n icon={<ArrowUpload24Regular />}\n onClick={onClick}\n className={buttonStyles.button}\n >\n {t('app.components.file-grid.upload-label')}\n </Button>\n </Tooltip>\n );\n}\n\nfunction ReplaceGridButton({\n onClick,\n}: {\n onClick: (record: TableRecord) => void;\n}) {\n const t = useT();\n const buttonStyles = useButtonStyles();\n const grid = useGridContext();\n // Replace targets exactly one saved row at a time — multi-replace\n // isn't a sensible UX.\n if (grid.selectedRecords.length !== 1) return null;\n const target = grid.selectedRecords[0];\n if (!target || !target.id) return null;\n if (\n grid.tablePermissions !== undefined &&\n !hasPermission(grid.tablePermissions, TableSecurityPermission.Write)\n ) {\n return null;\n }\n return (\n <Tooltip\n relationship=\"label\"\n content={t('app.components.file-grid.replace-tooltip')}\n >\n <Button\n appearance=\"subtle\"\n icon={<ArrowSync24Regular />}\n onClick={() => onClick(target)}\n className={buttonStyles.button}\n >\n {t('app.components.file-grid.replace-label')}\n </Button>\n </Tooltip>\n );\n}\n\nfunction DownloadGridButton({\n onDownload,\n}: {\n onDownload: (records: readonly TableRecord[]) => Promise<void>;\n}) {\n const t = useT();\n const buttonStyles = useButtonStyles();\n const grid = useGridContext();\n const selectionCount = grid.selectedRecords.length;\n\n // Selection toggles which set of rows we download: zero selected =\n // \"Download All (visible)\", any selection = \"Download Selected\".\n // Single label that swaps copy keeps the toolbar quiet.\n const label =\n selectionCount > 0\n ? t('app.components.file-grid.download-selected-label')\n : t('app.components.file-grid.download-all-label');\n const tooltip =\n selectionCount > 0\n ? t('app.components.file-grid.download-selected-tooltip')\n : t('app.components.file-grid.download-all-tooltip');\n\n const onClick = useCallback(() => {\n // \"All\" downloads every record the grid has loaded on the current\n // page; \"Selected\" downloads only the checked rows. Pending-create\n // rows are intentionally excluded from \"All\" — they have no\n // persisted id to fetch bytes for. Mirrors Blazor's\n // `_grid.LoadedRecords` semantics on Download All.\n const target =\n selectionCount > 0 ? grid.selectedRecords : grid.loadedRecords;\n void onDownload(target);\n }, [grid.loadedRecords, grid.selectedRecords, onDownload, selectionCount]);\n\n return (\n <Tooltip relationship=\"label\" content={tooltip}>\n <Button\n appearance=\"subtle\"\n icon={<DrawerArrowDownload24Regular />}\n onClick={onClick}\n className={buttonStyles.button}\n >\n {label}\n </Button>\n </Tooltip>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Tiny utilities kept module-local — not worth exporting.\n// ---------------------------------------------------------------------------\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // chunk the conversion to avoid `Maximum call stack` blowing up on\n // large files; 32k characters per chunk keeps every browser happy.\n const CHUNK = 0x8000;\n let binary = '';\n for (let i = 0; i < bytes.length; i += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(binary);\n}\n\nfunction hasPermission(\n bitmask: SecurityPermissionType | undefined,\n required: SecurityPermissionType,\n): boolean {\n if (bitmask === undefined) return false;\n return (bitmask & required) === required;\n}\n\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport type { CSSProperties, HTMLAttributes, ReactNode } from 'react';\r\n\r\n/**\r\n * Canonical CSS class name. The stable `ppp-header` class appears on\r\n * every rendered Header instance alongside the Fluent atomic classes,\r\n * so devtools inspection and consumer-supplied CSS overrides both have\r\n * a deterministic target. Mirrors Blazor's `<div class=\"ppp-header\">`\r\n * pattern from `Header.razor`.\r\n */\r\nexport const PPP_HEADER_CLASS = 'ppp-header';\r\n\r\nconst useHeaderStyles = makeStyles({\r\n // Single source of truth for the framework's header-band visual\r\n // treatment. Mirrors Blazor's `Header.razor.css` token-for-token —\r\n // any change here propagates automatically to every consumer\r\n // (`<Section>` header bands, future `<MainGrid>` toolbars, future\r\n // `<MenuBar>` headers, standalone consumer usage). The mapping from\r\n // Blazor's web-components tokens to FluentUI v9 React tokens was\r\n // calibrated for visual parity:\r\n //\r\n // --neutral-layer-4 → tokens.colorNeutralBackground5\r\n // --neutral-stroke-rest → tokens.colorNeutralStroke1\r\n // --control-corner-radius → tokens.borderRadiusMedium\r\n // 2*--design-unit (8px) → tokens.spacingVerticalS\r\n // 3*--design-unit (12px) → tokens.spacingHorizontalM\r\n //\r\n // Corners are rounded unconditionally to match Blazor's CSS — the\r\n // rounding is invisible without a parent border to align against, so\r\n // unbordered consumers (e.g. an unbordered `<Section>`) pay nothing\r\n // visually. Bordered consumers (e.g. a bordered Section) get the\r\n // exact flush-against-border-radius look that Blazor produces.\r\n root: {\r\n width: '100%',\r\n display: 'flex',\r\n alignItems: 'center',\r\n boxSizing: 'border-box',\r\n paddingTop: tokens.spacingVerticalS,\r\n paddingBottom: tokens.spacingVerticalS,\r\n paddingLeft: tokens.spacingHorizontalM,\r\n paddingRight: tokens.spacingHorizontalM,\r\n backgroundColor: tokens.colorNeutralBackground5,\r\n borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderTopLeftRadius: tokens.borderRadiusMedium,\r\n borderTopRightRadius: tokens.borderRadiusMedium,\r\n // h-tag margin reset — Blazor's `.ppp-header ::deep h1..h6 { margin: 0 }`.\r\n // Lets consumers write `<Header><h2>Title</h2></Header>` without\r\n // browser-default heading margins pushing the title off-center.\r\n '& h1, & h2, & h3, & h4, & h5, & h6': { margin: 0 },\r\n },\r\n});\r\n\r\nexport interface HeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\r\n /** Header content. */\r\n children?: ReactNode;\r\n /**\r\n * Class merged onto the default Fluent header styling. Use this to\r\n * apply CONTEXT-specific overrides — e.g. PageLayout adds its accent-\r\n * colored border by passing a className that overrides\r\n * `borderBottom`. Mirrors Blazor's per-consumer CSS specificity\r\n * pattern (`.page-layout header { ... }` overriding `.ppp-header`).\r\n */\r\n className?: string;\r\n /** Inline style override. */\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Page-header band — the single source of truth for the framework's\r\n * \"header strip\" visual treatment. Renders a semantic `<header>`\r\n * element with Fluent token-driven styling that mirrors Blazor's\r\n * `Header.razor` + `Header.razor.css` exactly.\r\n *\r\n * Used internally by `<Section>` for section-header bands and by\r\n * `<PageLayout>` for the page-level header slot — both compose with\r\n * this component so a single token tweak propagates to every consumer.\r\n * Equally usable standalone for custom dialog headings, embedded panel\r\n * titles, or any other \"styled header strip\" need.\r\n *\r\n * The stable `ppp-header` class is applied alongside Fluent's atomic\r\n * makeStyles classes — consumers can target it from custom stylesheets\r\n * (e.g. `.ppp-header h2 { color: red }`) without depending on the\r\n * generated atomic names.\r\n */\r\nexport function Header({ children, className, style, ...rest }: HeaderProps) {\r\n const styles = useHeaderStyles();\r\n return (\r\n <header\r\n {...rest}\r\n className={mergeClasses(styles.root, PPP_HEADER_CLASS, className)}\r\n style={style}\r\n >\r\n {children}\r\n </header>\r\n );\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport {\r\n createContext,\r\n useContext,\r\n useEffect,\r\n useRef,\r\n type CSSProperties,\r\n type ReactNode,\r\n} from 'react';\r\nimport { Footer } from './footer.js';\r\nimport { Header } from './header.js';\r\n\r\n/**\r\n * Detects whether a `<PageLayout>` is nested inside another `<PageLayout>`.\r\n * Nested layouts get the `child-layout` styling (thinner header border, no\r\n * outer viewport-height constraint) so the inner layout slots cleanly into\r\n * the outer layout's body area. Mirrors Blazor's `[CascadingParameter]\r\n * PageLayout? _parentLayout`.\r\n */\r\nconst PageLayoutNestingContext = createContext<{ isNested: boolean }>({ isNested: false });\r\n\r\nconst usePageLayoutStyles = makeStyles({\r\n // Top-level layout — fills the viewport. The `:has()` carve-out in\r\n // `mainContent` below handles the \"nested layout inside this body\"\r\n // case by suppressing the outer body's padding when an inner layout\r\n // is detected (so nested layouts don't get doubled padding).\r\n root: {\r\n position: 'relative',\r\n display: 'grid',\r\n gridTemplateColumns: 'auto minmax(0, 1fr)',\r\n gridTemplateRows: 'auto 1fr auto',\r\n // Lets the PageLayout shrink when it's nested inside another\r\n // PageLayout's `<main>` (the outer main is a grid item; without\r\n // `min-width: 0` on this inner root the outer's `minmax(0, 1fr)`\r\n // track wouldn't be honored once content tries to grow wider).\r\n minWidth: 0,\r\n gridTemplateAreas: `\r\n 'header header'\r\n 'navigation main'\r\n 'footer footer'\r\n `,\r\n height: '100vh',\r\n minHeight: '100vh',\r\n // Mobile uses dynamic viewport units so iOS Safari's collapsing URL\r\n // bar doesn't clip the footer behind it.\r\n '@media (max-width: 767.97px)': {\r\n height: '100dvh',\r\n minHeight: '100dvh',\r\n },\r\n },\r\n // Child layout — fills its parent's body area, not the viewport.\r\n childRoot: {\r\n height: '100%',\r\n minHeight: '100%',\r\n },\r\n // PageLayout-specific CONTEXT overrides on top of the canonical\r\n // `<Header>` styling (`layout/header.tsx`). These override the\r\n // background + border + corner radius + padding to produce the\r\n // page-level header look that mirrors Blazor's\r\n // `.page-layout header { ... }` rules — taller (min-height 50),\r\n // accent-colored 2px bottom border (vs Section's neutral 1px),\r\n // wider horizontal padding. Grid-area placement is the structural\r\n // concern PageLayout owns regardless of which component renders\r\n // inside.\r\n header: {\r\n gridArea: 'header',\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderBottom: `2px solid ${tokens.colorBrandStroke1}`,\r\n // PageLayout's header sits at the top of the viewport — no rounding\r\n // because there's nothing above it to wrap around.\r\n borderTopLeftRadius: 0,\r\n borderTopRightRadius: 0,\r\n minHeight: '50px',\r\n paddingLeft: tokens.spacingHorizontalL,\r\n paddingRight: tokens.spacingHorizontalL,\r\n },\r\n // Child layouts get a thinner border so the visual weight doesn't\r\n // stack when one PageLayout's body contains another.\r\n childHeader: {\r\n borderBottomWidth: '1px',\r\n },\r\n navigation: {\r\n gridArea: 'navigation',\r\n overflowY: 'auto',\r\n maxWidth: '35vw',\r\n },\r\n main: {\r\n gridArea: 'main',\r\n overflowY: 'auto',\r\n // CSS Grid items default to `min-width: auto`, which makes them as\r\n // wide as their content's intrinsic min size — overriding the\r\n // outer grid's `minmax(0, 1fr)` track when content is wider than\r\n // the viewport. Without `min-width: 0` here, a wide descendant\r\n // (e.g., a MainGrid full of data columns whose summed widths\r\n // exceed the viewport) drags the whole main column past the right\r\n // edge and every sibling inside the body inherits that wider\r\n // width — multi-line MessageBars, headers, etc. then wrap (or\r\n // don't) at viewport-relative positions that look wrong. With\r\n // `min-width: 0`, the main column respects the track's 1fr cap,\r\n // and the descendant's own scroll container (the grid's\r\n // `tableContainer` has `overflow: auto`) handles its internal\r\n // horizontal overflow without involving the page.\r\n minWidth: 0,\r\n // Default padding for the body. Suppressed via the `:has()` rule\r\n // below when a nested PageLayout lives inside; that prevents the\r\n // outer body's padding from double-wrapping the inner layout's\r\n // header/nav/footer.\r\n padding: `1.3rem 1.25rem 1.25rem 1.25rem`,\r\n '@media (max-width: 767.97px)': {\r\n padding: '0.75rem',\r\n },\r\n // When a descendant is itself a PageLayout (detected by the root\r\n // class below), drop the outer body's padding so the inner\r\n // layout's bands sit flush against the outer layout's main edges.\r\n '&:has(.ppp-page-layout-root)': {\r\n padding: 0,\r\n },\r\n },\r\n // PageLayout-specific CONTEXT overrides for the bottom-of-viewport\r\n // footer band. Same approach as `header` above — overrides the\r\n // canonical `<Footer>` styling (`layout/footer.tsx`) only where\r\n // PageLayout needs different visual treatment. Mirrors Blazor's\r\n // `.page-layout footer { ... }` rules: Background3 instead of\r\n // Background2, no top-border (Blazor relies on the body/footer\r\n // visual separation), no rounded corners (sitting flush at the\r\n // viewport bottom).\r\n footer: {\r\n gridArea: 'footer',\r\n flexDirection: 'row',\r\n backgroundColor: tokens.colorNeutralBackground3,\r\n borderTop: 'none',\r\n borderBottomLeftRadius: 0,\r\n borderBottomRightRadius: 0,\r\n paddingLeft: tokens.spacingHorizontalS,\r\n paddingRight: tokens.spacingHorizontalS,\r\n },\r\n});\r\n\r\nexport interface PageLayoutProps {\r\n /**\r\n * Content rendered in the header band at the top. Spans the full width;\r\n * stays fixed while the body scrolls. Typical contents: app title /\r\n * breadcrumbs / a toolbar with save/refresh buttons.\r\n */\r\n header?: ReactNode;\r\n /**\r\n * Content rendered in the navigation band to the left of the body.\r\n * Scrolls independently from the body. Sized to content, capped at\r\n * `max-width: 35vw` so a long menu doesn't crowd the main area.\r\n */\r\n navigation?: ReactNode;\r\n /**\r\n * Main content area. Scrolls independently. Default padding is applied\r\n * unless the body contains a nested `<PageLayout>` — then padding is\r\n * suppressed automatically so the nested layout's bands sit flush.\r\n */\r\n body?: ReactNode;\r\n /**\r\n * Content rendered in the footer band at the bottom. Spans the full\r\n * width; stays fixed while the body scrolls.\r\n */\r\n footer?: ReactNode;\r\n\r\n /** When `false`, the header band is removed from the grid. Default `true`. */\r\n headerVisible?: boolean;\r\n /** When `false`, the navigation band is removed from the grid. Default `true`. */\r\n navigationVisible?: boolean;\r\n /** When `false`, the footer band is removed from the grid. Default `true`. */\r\n footerVisible?: boolean;\r\n\r\n className?: string;\r\n style?: CSSProperties;\r\n\r\n /** Class applied to the `<header>` slot. */\r\n headerClassName?: string;\r\n /** Inline style applied to the `<header>` slot. */\r\n headerStyle?: CSSProperties;\r\n /** Class applied to the navigation slot. */\r\n navigationClassName?: string;\r\n /** Inline style applied to the navigation slot. */\r\n navigationStyle?: CSSProperties;\r\n /** Class applied to the `<main>` slot. */\r\n bodyClassName?: string;\r\n /** Inline style applied to the `<main>` slot. */\r\n bodyStyle?: CSSProperties;\r\n /** Class applied to the `<footer>` slot. */\r\n footerClassName?: string;\r\n /** Inline style applied to the `<footer>` slot. */\r\n footerStyle?: CSSProperties;\r\n\r\n /**\r\n * Optional reset key. When it changes, the body scrolls back to the top.\r\n * Standard use: `<PageLayout scrollResetKey={location.pathname} />` with\r\n * react-router. PowerPortalsPro doesn't take a hard router dependency, so\r\n * consumers wire their own routing-aware value here.\r\n */\r\n scrollResetKey?: string;\r\n}\r\n\r\n/**\r\n * CSS Grid layout with four named areas (header / navigation / body /\r\n * footer). Body and navigation scroll independently; header and footer span\r\n * the full width and stay fixed while the body scrolls. Supports nesting —\r\n * an inner `<PageLayout>` inside the body picks up a `child-layout` style\r\n * variant automatically (thinner header border, fills the parent's body\r\n * rather than the viewport).\r\n *\r\n * Slots are passed via props (`header`, `navigation`, `body`, `footer`)\r\n * rather than nested children, giving TypeScript a place to surface\r\n * required-slot constraints if we add them later.\r\n */\r\nexport function PageLayout({\r\n header,\r\n navigation,\r\n body,\r\n footer,\r\n headerVisible = true,\r\n navigationVisible = true,\r\n footerVisible = true,\r\n className,\r\n style,\r\n headerClassName,\r\n headerStyle,\r\n navigationClassName,\r\n navigationStyle,\r\n bodyClassName,\r\n bodyStyle,\r\n footerClassName,\r\n footerStyle,\r\n scrollResetKey,\r\n}: PageLayoutProps) {\r\n const styles = usePageLayoutStyles();\r\n const { isNested } = useContext(PageLayoutNestingContext);\r\n const mainRef = useRef<HTMLElement | null>(null);\r\n\r\n // Scroll the body to the top when the consumer-supplied reset key\r\n // changes. Standard wiring is `scrollResetKey={location.pathname}` —\r\n // every route change scrolls the new page's body to its top, matching\r\n // Blazor's `NavigationManager.LocationChanged → main.scrollTo(0, 0)`.\r\n useEffect(() => {\r\n if (scrollResetKey === undefined) return;\r\n mainRef.current?.scrollTo(0, 0);\r\n }, [scrollResetKey]);\r\n\r\n const rootClass = mergeClasses(\r\n 'ppp-page-layout-root',\r\n styles.root,\r\n isNested && styles.childRoot,\r\n className,\r\n );\r\n const headerClass = mergeClasses(\r\n styles.header,\r\n isNested && styles.childHeader,\r\n headerClassName,\r\n );\r\n\r\n return (\r\n <PageLayoutNestingContext.Provider value={{ isNested: true }}>\r\n <div className={rootClass} style={style}>\r\n {header !== undefined && headerVisible && (\r\n <Header className={headerClass} {...(headerStyle && { style: headerStyle })}>\r\n {header}\r\n </Header>\r\n )}\r\n {navigation !== undefined && navigationVisible && (\r\n <nav\r\n className={mergeClasses(styles.navigation, navigationClassName)}\r\n style={navigationStyle}\r\n >\r\n {navigation}\r\n </nav>\r\n )}\r\n {body !== undefined && (\r\n <main\r\n ref={mainRef}\r\n className={mergeClasses(styles.main, bodyClassName)}\r\n style={bodyStyle}\r\n >\r\n {body}\r\n </main>\r\n )}\r\n {footer !== undefined && footerVisible && (\r\n <Footer\r\n className={mergeClasses(styles.footer, footerClassName)}\r\n {...(footerStyle && { style: footerStyle })}\r\n >\r\n {footer}\r\n </Footer>\r\n )}\r\n </div>\r\n </PageLayoutNestingContext.Provider>\r\n );\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport type { CSSProperties, HTMLAttributes, ReactNode } from 'react';\r\n\r\n/**\r\n * Canonical CSS class name. The stable `ppp-body` class appears on\r\n * every rendered Body instance alongside Fluent's atomic classes.\r\n * Mirrors Blazor's `<div class=\"ppp-body\">` pattern from `Body.razor`.\r\n */\r\nexport const PPP_BODY_CLASS = 'ppp-body';\r\n\r\nconst useBodyStyles = makeStyles({\r\n // Single source of truth for the framework's body-area visual\r\n // treatment. Mirrors Blazor's `Body.razor.css` — `.ppp-body` defines\r\n // only `padding: calc(--design-unit * 3px)` (12px on all sides) and\r\n // nothing else. The component is intentionally lightweight: no\r\n // background, no border, no flex — just inset padding so content\r\n // doesn't butt against parent edges. Consumers and parent layouts\r\n // own the surrounding visual treatment (Section's border / radius,\r\n // PageLayout's grid placement, etc.).\r\n //\r\n // Token mapping:\r\n // 3*--design-unit (12px) → tokens.spacingHorizontalM (12px)\r\n root: {\r\n padding: tokens.spacingHorizontalM,\r\n },\r\n});\r\n\r\nexport interface BodyProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\r\n /** Body content. */\r\n children?: ReactNode;\r\n /** Class merged onto the default body styling. */\r\n className?: string;\r\n /** Inline style override. */\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Body content area — the single source of truth for the framework's\r\n * \"inset content region\" padding. Renders a semantic `<main>` element\r\n * matching Blazor's `Body.razor`. Used internally by the framework's\r\n * grid (toolbar + data area) and any other \"content needs inset\r\n * padding\" slot; equally usable standalone.\r\n *\r\n * Deliberately minimal: only padding is opinionated. Background,\r\n * border, and layout role are left to the parent or the consumer's\r\n * `className` override.\r\n *\r\n * The stable `ppp-body` class is applied alongside Fluent's atomic\r\n * makeStyles classes for devtools visibility and consumer-stylable\r\n * targeting.\r\n */\r\nexport function Body({ children, className, style, ...rest }: BodyProps) {\r\n const styles = useBodyStyles();\r\n return (\r\n <main\r\n {...rest}\r\n className={mergeClasses(styles.root, PPP_BODY_CLASS, className)}\r\n style={style}\r\n >\r\n {children}\r\n </main>\r\n );\r\n}\r\n","import { useUnsavedChangesGuard } from '@powerportalspro/react';\r\nimport { useEffect } from 'react';\r\nimport { useBlocker } from 'react-router-dom';\r\n\r\n/**\r\n * Drop-in component that blocks `react-router-dom` navigations whenever\r\n * any page-mounted {@link MainContext} or {@link RecordContext} is dirty,\r\n * and prompts the user via the framework dialog service to either\r\n * proceed (discarding pending edits) or stay on the page.\r\n *\r\n * Mount once per data router, anywhere inside it — typically just inside\r\n * `<RouterProvider>` so the guard inherits the router's blocker\r\n * machinery for the entire route tree:\r\n *\r\n * ```tsx\r\n * const router = createBrowserRouter([{ path: '/', element: (\r\n * <PowerPortalsProProvider>\r\n * <RouterUnsavedChangesGuard />\r\n * <App />\r\n * </PowerPortalsProProvider>\r\n * )}]);\r\n * ```\r\n *\r\n * Aggregation is handled by the page-wide\r\n * {@link UnsavedChangesRegistry} (auto-mounted inside\r\n * {@link PowerPortalsProProvider}): every topmost `MainContext` /\r\n * `RecordContext` reports its own aggregate `isDirty` to the registry\r\n * on mount, and this component reads `isAnyDirty` reactively. The\r\n * cases it handles:\r\n *\r\n * - Single top-level context: prompt when it dirties.\r\n * - Sibling top-level contexts on the same page: ONE prompt if any are\r\n * dirty (the registry OR-merges; the guard only opens one\r\n * `useBlocker`).\r\n * - Nested contexts (inner `<MainContext>` inside outer\r\n * `<RecordContext>`, etc.): only the outermost reports. Inner\r\n * dirty state already bubbles up through the existing\r\n * `registerDescendant` chain.\r\n * - Editable grid with N row `<RecordContext>`s: rows aren't topmost\r\n * (they're descendants of the page's MainContext), so they don't\r\n * register here. Their edits flow up via `registerDescendant` and\r\n * show in the page-level reporter's `isDirty`.\r\n *\r\n * Same-pathname navigations (search-param tweaks, `replace: true` to\r\n * the current URL) are NOT blocked — the user isn't actually leaving\r\n * the page, so prompting would be noise.\r\n *\r\n * **Requires a `react-router-dom` data router** (`createBrowserRouter`\r\n * + `<RouterProvider>`). The legacy `<BrowserRouter>` doesn't expose\r\n * `useBlocker` and will throw at mount. The package declares\r\n * `react-router-dom` as an optional peer dep so consumers using a\r\n * different router (or none) aren't forced to install it; this\r\n * component is the one piece that actually imports it.\r\n *\r\n * **Using a different router?** This component is a thin shell around\r\n * the headless {@link useUnsavedChangesGuard} hook from\r\n * `@powerportalspro/react`. See that hook's JSDoc for canonical\r\n * blocker patterns for TanStack Router, Wouter, Next.js App Router,\r\n * and other routers — same controller, different router glue.\r\n */\r\nexport function RouterUnsavedChangesGuard() {\r\n const guard = useUnsavedChangesGuard();\r\n\r\n // Same-pathname check matches the legacy per-page helper consumers\r\n // have been hand-rolling — query-string edits and replace-self\r\n // navigations stay through silently.\r\n const blocker = useBlocker(\r\n ({ currentLocation, nextLocation }) =>\r\n guard.isAnyDirty && currentLocation.pathname !== nextLocation.pathname,\r\n );\r\n\r\n useEffect(() => {\r\n if (blocker.state !== 'blocked') return;\r\n // The blocker latches; we must resolve it (proceed or reset) on\r\n // every transition that landed in `blocked`. Skipping either call\r\n // would freeze the router in `blocked` state and silently ignore\r\n // every subsequent navigation attempt.\r\n let cancelled = false;\r\n (async () => {\r\n const proceed = await guard.warn();\r\n if (cancelled) return;\r\n if (proceed) blocker.proceed();\r\n else blocker.reset();\r\n })();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [blocker, guard]);\r\n\r\n return null;\r\n}\r\n","import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport { Children, isValidElement, type CSSProperties, type ReactNode } from 'react';\r\nimport { Footer } from './footer.js';\r\nimport { Header } from './header.js';\r\n\r\n/**\r\n * Orientation hint for the column's stacked children. Vertical (default)\r\n * lays children out top-to-bottom; horizontal lays them in a row. Mirrors\r\n * Blazor's `ComponentOrientation` on `SectionColumn`.\r\n */\r\nexport const SectionColumnOrientation = {\r\n Vertical: 'vertical',\r\n Horizontal: 'horizontal',\r\n} as const;\r\nexport type SectionColumnOrientation =\r\n (typeof SectionColumnOrientation)[keyof typeof SectionColumnOrientation];\r\n\r\nconst useSectionStyles = makeStyles({\r\n section: {\r\n width: '100%',\r\n minWidth: 0,\r\n maxWidth: '100%',\r\n boxSizing: 'border-box',\r\n },\r\n // Optional outer border, applied to <Section> when `borderVisible` is on.\r\n sectionBorder: {\r\n border: `1px solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n width: '100%',\r\n },\r\n // (Header / Footer band chrome moved to `layout/header.tsx` and\r\n // `layout/footer.tsx` as the framework-wide source of truth. Section\r\n // composes with `<Header>` / `<Footer>` so a CSS change there\r\n // propagates here without per-component edits.)\r\n // The flex row that wraps the columns. Wraps to a new line when there\r\n // isn't enough horizontal space for the columns' min-widths. Gap is\r\n // 15px in Blazor — using a literal here for exact parity (no Fluent\r\n // token equals 15px).\r\n body: {\r\n display: 'flex',\r\n flexDirection: 'row',\r\n columnGap: '15px',\r\n rowGap: '15px',\r\n justifyContent: 'space-between',\r\n alignItems: 'flex-start',\r\n width: '100%',\r\n flexWrap: 'wrap',\r\n },\r\n bodyBorderedSection: {\r\n padding: '0 1rem',\r\n },\r\n // A single column. Default flex weight 3.4 mirrors Blazor's stylesheet\r\n // so single-Section columns size identically across the two stacks.\r\n column: {\r\n flex: '3.4 1 0%',\r\n display: 'block',\r\n boxSizing: 'border-box',\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n },\r\n columnBorder: {\r\n border: `1px solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n width: '100%',\r\n },\r\n // Combined-state margins between the column and the section's own\r\n // header/border/footer. Match Blazor's `.parent-header`/`.parent-border`/\r\n // `.parent-footer` selectors on `.ppp-section-column`.\r\n columnMarginTop: { marginTop: '1rem' },\r\n columnMarginBottom: { marginBottom: '1rem' },\r\n columnContent: {\r\n width: '100%',\r\n padding: '1rem',\r\n },\r\n // Inner flex container for the column's children. Vertical (column)\r\n // stacking is the default for forms; horizontal is the alternate for\r\n // toolbars or filter rows.\r\n columnContentStack: {\r\n display: 'flex',\r\n width: '100%',\r\n flexWrap: 'wrap',\r\n },\r\n});\r\n\r\n// ─── SectionColumn ───────────────────────────────────────────────────\r\n\r\nexport interface SectionColumnProps {\r\n /** Stacked children. Editors / readouts / nested Sections all fit. */\r\n children?: ReactNode;\r\n /**\r\n * Optional column-level header rendered above the stacked children.\r\n * Use this for toolbars or column-scoped controls (e.g., a switch\r\n * that toggles the column's contents).\r\n */\r\n header?: ReactNode;\r\n /** Optional column-level footer rendered below the stacked children. */\r\n footer?: ReactNode;\r\n /**\r\n * Convenience text-only header. Rendered as `<h5>{headerText}</h5>`\r\n * inside the styled header band. Ignored when `header` is supplied.\r\n */\r\n headerText?: string;\r\n /**\r\n * Convenience text-only footer. Ignored when `footer` is supplied.\r\n */\r\n footerText?: string;\r\n /**\r\n * Whether the column draws a border + corner radius around itself.\r\n * Default `true`.\r\n */\r\n borderVisible?: boolean;\r\n /**\r\n * Stacking orientation for the column's children. `Vertical` (default)\r\n * stacks top-to-bottom; `Horizontal` lays them in a row (with wrap).\r\n */\r\n columnContentOrientation?: SectionColumnOrientation;\r\n /** Horizontal gap (px) between children when laid out horizontally. */\r\n horizontalGap?: number;\r\n /** Vertical gap (px) between children. */\r\n verticalGap?: number;\r\n /**\r\n * Relative width weight. When set on multiple columns, the section\r\n * lays them out proportionally — three columns with `width={3}`,\r\n * `width={4}`, `width={3}` render as ~30/40/30. Unweighted columns\r\n * use the section's equal-distribution default.\r\n */\r\n width?: number;\r\n /** Class on the column's outer wrapper. */\r\n className?: string;\r\n /** Inline style on the column's outer wrapper. */\r\n style?: CSSProperties;\r\n}\r\n\r\n/**\r\n * Renders nothing on its own — exists as a configuration carrier that\r\n * `<Section>` reads via `React.Children.forEach`.\r\n *\r\n * Must be a direct child of `<Section>` to be detected.\r\n */\r\nexport function SectionColumn(_props: SectionColumnProps): React.ReactElement | null {\r\n // Marker component — Section reads props directly from the React\r\n // element rather than rendering this. Returning null keeps the React\r\n // reconciler happy if a SectionColumn is rendered standalone (which\r\n // it never should be).\r\n return null;\r\n}\r\n\r\n// ─── Section ─────────────────────────────────────────────────────────\r\n\r\nexport interface SectionProps {\r\n /**\r\n * `<SectionColumn>` children that lay out the section's content.\r\n * Anything else is ignored — the section only honors SectionColumn-\r\n * typed direct children.\r\n */\r\n children?: ReactNode;\r\n /** Section-level header above the column row. */\r\n header?: ReactNode;\r\n /** Section-level footer below the column row. */\r\n footer?: ReactNode;\r\n /**\r\n * Convenience text-only header. Rendered as `<h5>{headerText}</h5>`\r\n * inside the styled header band. Ignored when `header` is supplied.\r\n */\r\n headerText?: string;\r\n /**\r\n * Convenience text-only footer. Ignored when `footer` is supplied.\r\n */\r\n footerText?: string;\r\n /** When `true`, the section draws a border + corner radius. Default `false`. */\r\n borderVisible?: boolean;\r\n /**\r\n * Optional fixed column count. When omitted, the section infers count\r\n * from the number of `<SectionColumn>` children — extra positions are\r\n * rendered as empty placeholders. Must be 1..4 when supplied.\r\n */\r\n columnCount?: number;\r\n /**\r\n * Minimum width of each column. Below this, columns wrap onto new\r\n * lines. Default `'340px'`.\r\n */\r\n columnMinWidth?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n /** Class on the inner container that holds the column row. */\r\n childContentClassName?: string;\r\n /** Inline style on the inner container that holds the column row. */\r\n childContentStyle?: CSSProperties;\r\n /** Class on the header slot. */\r\n headerClassName?: string;\r\n /** Inline style on the header slot. */\r\n headerStyle?: CSSProperties;\r\n /** Class on the footer slot. */\r\n footerClassName?: string;\r\n /** Inline style on the footer slot. */\r\n footerStyle?: CSSProperties;\r\n id?: string;\r\n}\r\n\r\n/**\r\n * Renders a row of `<SectionColumn>` children with optional section-level\r\n * header / footer, supporting weighted column widths, configurable min-width\r\n * per column, and an optional outer border. Columns wrap to multiple rows\r\n * when the container is too narrow for `columnMinWidth × count`.\r\n *\r\n * Children must be direct `<SectionColumn>` elements to be detected\r\n * (the section uses `React.Children.forEach` + element-type comparison,\r\n * which doesn't cross wrapper boundaries). Other children are ignored.\r\n */\r\nexport function Section({\r\n children,\r\n header,\r\n footer,\r\n headerText,\r\n footerText,\r\n borderVisible = false,\r\n columnCount,\r\n columnMinWidth = '340px',\r\n className,\r\n style,\r\n childContentClassName,\r\n childContentStyle,\r\n headerClassName,\r\n headerStyle,\r\n footerClassName,\r\n footerStyle,\r\n id,\r\n}: SectionProps) {\r\n const styles = useSectionStyles();\r\n\r\n if (columnCount !== undefined && (columnCount < 1 || columnCount > 4)) {\r\n throw new Error(\r\n `Section: 'columnCount' must be between 1 and 4 inclusive (got ${columnCount}).`,\r\n );\r\n }\r\n\r\n // Collect SectionColumn children. Anything that's not a SectionColumn\r\n // element is ignored — it's the consumer's signal that they passed\r\n // the wrong shape. We don't warn, matching Blazor's silent ignore.\r\n const columns: SectionColumnProps[] = [];\r\n Children.forEach(children, (child) => {\r\n if (isValidElement(child) && child.type === SectionColumn) {\r\n columns.push(child.props as SectionColumnProps);\r\n }\r\n });\r\n\r\n const effectiveCount = columnCount ?? columns.length;\r\n const headerNode = resolveBandSlot(header, headerText);\r\n const footerNode = resolveBandSlot(footer, footerText);\r\n const hasHeader = headerNode !== undefined;\r\n const hasFooter = footerNode !== undefined;\r\n\r\n return (\r\n <div\r\n id={id}\r\n className={mergeClasses(\r\n 'ppp-section',\r\n styles.section,\r\n borderVisible && styles.sectionBorder,\r\n className,\r\n )}\r\n style={style}\r\n >\r\n {hasHeader && (\r\n <Header\r\n {...(headerClassName !== undefined && { className: headerClassName })}\r\n {...(headerStyle !== undefined && { style: headerStyle })}\r\n >\r\n {headerNode}\r\n </Header>\r\n )}\r\n <div\r\n className={mergeClasses(\r\n 'ppp-section-body',\r\n styles.body,\r\n borderVisible && styles.bodyBorderedSection,\r\n childContentClassName,\r\n )}\r\n style={childContentStyle}\r\n >\r\n {Array.from({ length: effectiveCount }).map((_, i) => {\r\n const col = columns[i];\r\n if (!col) {\r\n return (\r\n <div\r\n key={`__empty_${i}`}\r\n className={mergeClasses('ppp-section-column', styles.column)}\r\n style={{ minWidth: columnMinWidth }}\r\n />\r\n );\r\n }\r\n return (\r\n <SectionColumnRenderer\r\n key={i}\r\n column={col}\r\n columnMinWidth={columnMinWidth}\r\n sectionHasHeader={hasHeader}\r\n sectionHasFooter={hasFooter}\r\n sectionHasBorder={borderVisible}\r\n />\r\n );\r\n })}\r\n </div>\r\n {hasFooter && (\r\n <Footer\r\n {...(footerClassName !== undefined && { className: footerClassName })}\r\n {...(footerStyle !== undefined && { style: footerStyle })}\r\n >\r\n {footerNode}\r\n </Footer>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n// Resolves Blazor-style \"Header takes precedence, else HeaderText wrapped\r\n// in <h5>\". Returns undefined when neither is set / when text is blank\r\n// (matches Blazor's `IsNullOrWhiteSpace` short-circuit).\r\nfunction resolveBandSlot(node: ReactNode, text: string | undefined): ReactNode {\r\n if (node !== undefined) return node;\r\n if (text !== undefined && text.trim() !== '') return <h5>{text}</h5>;\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Renders a single column inside `<Section>`. Pulled out so the\r\n * column-style computation (flex weight, min-width, gap orientation)\r\n * stays readable; not exported because consumers should compose via\r\n * `<SectionColumn>` not this.\r\n */\r\nfunction SectionColumnRenderer({\r\n column,\r\n columnMinWidth,\r\n sectionHasHeader,\r\n sectionHasFooter,\r\n sectionHasBorder,\r\n}: {\r\n column: SectionColumnProps;\r\n columnMinWidth: string;\r\n sectionHasHeader: boolean;\r\n sectionHasFooter: boolean;\r\n sectionHasBorder: boolean;\r\n}) {\r\n const styles = useSectionStyles();\r\n const {\r\n children,\r\n header,\r\n footer,\r\n headerText,\r\n footerText,\r\n borderVisible = true,\r\n columnContentOrientation = SectionColumnOrientation.Vertical,\r\n horizontalGap = 10,\r\n verticalGap = 10,\r\n width,\r\n className,\r\n style,\r\n } = column;\r\n\r\n const headerNode = resolveBandSlot(header, headerText);\r\n const footerNode = resolveBandSlot(footer, footerText);\r\n const hasColumnHeader = headerNode !== undefined;\r\n\r\n // Margin rules ported from Blazor's `.ppp-section-column.parent-*.{border,column-header}`\r\n // selectors — give the column breathing room from the section's own bands.\r\n const wantsMarginTop =\r\n (sectionHasHeader && hasColumnHeader) ||\r\n (sectionHasHeader && borderVisible) ||\r\n (sectionHasBorder && borderVisible);\r\n const wantsMarginBottom =\r\n (sectionHasFooter && borderVisible) || (sectionHasBorder && borderVisible);\r\n\r\n const flexStyle: CSSProperties = {\r\n minWidth: columnMinWidth,\r\n ...(width !== undefined ? { flex: `${width} 1 0%` } : {}),\r\n ...style,\r\n };\r\n\r\n const stackStyle: CSSProperties = {\r\n columnGap: `${verticalGap}px`,\r\n rowGap: `${horizontalGap}px`,\r\n flexDirection:\r\n columnContentOrientation === SectionColumnOrientation.Horizontal ? 'row' : 'column',\r\n };\r\n\r\n return (\r\n <div\r\n className={mergeClasses(\r\n 'ppp-section-column',\r\n styles.column,\r\n borderVisible && styles.columnBorder,\r\n wantsMarginTop && styles.columnMarginTop,\r\n wantsMarginBottom && styles.columnMarginBottom,\r\n className,\r\n )}\r\n style={flexStyle}\r\n >\r\n {hasColumnHeader && <Header>{headerNode}</Header>}\r\n <div className={mergeClasses('ppp-section-column-content', styles.columnContent)}>\r\n <div\r\n className={mergeClasses(\r\n 'ppp-section-column-content-stack',\r\n styles.columnContentStack,\r\n )}\r\n style={stackStyle}\r\n >\r\n {children}\r\n </div>\r\n </div>\r\n {footerNode !== undefined && <Footer>{footerNode}</Footer>}\r\n </div>\r\n );\r\n}\r\n","/**\r\n * Reads a {@link FluentChartPalette} from the active FluentUI design tokens.\r\n * Mirrors `FluentUIChart.razor.ts` — `getComputedStyle` against\r\n * `document.documentElement` (where FAST sets global tokens) with\r\n * `document.body` as a fallback for token names some providers apply at\r\n * the body scope.\r\n */\r\n\r\n/** Colors read off the FluentUI design tokens for charting purposes. */\r\nexport interface FluentChartPalette {\r\n /** Color for legend labels, axis ticks, and title text. */\r\n textColor: string;\r\n /** Color for axis gridlines and borders. */\r\n gridColor: string;\r\n /** Active accent fill — also the first entry in the series palette. */\r\n accentColor: string;\r\n /** Palette used to auto-fill datasets that don't supply their own colors. */\r\n seriesColors: string[];\r\n}\r\n\r\nfunction readVariable(name: string, scope: Element): string {\r\n // Read from the caller-supplied scope first — Fluent v9's\r\n // `FluentProvider` writes design tokens to a wrapping element's\r\n // inline style, NOT to `document.documentElement`. Reading from\r\n // html/body returns empty strings for `--colorNeutralForeground1`\r\n // and the chart falls back to a dim default that's unreadable in\r\n // dark mode. Resolving against an element INSIDE the\r\n // FluentProvider picks up the scoped variable via CSS inheritance.\r\n //\r\n // FAST `DesignSystemProvider` (used by Fluent UI Blazor) does set\r\n // tokens on `document.documentElement`, so html/body are kept as\r\n // fallbacks for cross-stack hosting setups.\r\n const fromScope = window.getComputedStyle(scope).getPropertyValue(name).trim();\r\n if (fromScope.length > 0) return fromScope;\r\n const fromHtml = window\r\n .getComputedStyle(document.documentElement)\r\n .getPropertyValue(name)\r\n .trim();\r\n if (fromHtml.length > 0) return fromHtml;\r\n return window.getComputedStyle(document.body).getPropertyValue(name).trim();\r\n}\r\n\r\nfunction pickFirst(scope: Element, ...names: string[]): string {\r\n for (const name of names) {\r\n const value = readVariable(name, scope);\r\n if (value && value.length > 0) {\r\n return value;\r\n }\r\n }\r\n return '';\r\n}\r\n\r\n/**\r\n * Reads the current chart palette from the CSS vars in scope at the\r\n * supplied DOM element. Call from a `useEffect` with a ref to an\r\n * element that sits INSIDE the FluentProvider (typically the chart's\r\n * container) so Fluent v9's scoped design tokens resolve correctly —\r\n * reading from html/body would miss them and the chart text would\r\n * fall back to a dim gray that's unreadable in dark mode.\r\n *\r\n * Returns the palette synchronously — re-call on theme changes\r\n * (driven by a MutationObserver on the document root).\r\n */\r\nexport function getChartPalette(scope: Element): FluentChartPalette {\r\n // Default text color: Fluent neutral foreground rest. Token name\r\n // variants are checked in priority order — `--colorNeutralForeground1`\r\n // is Fluent v9 (camelCase var name, scoped to FluentProvider),\r\n // `--neutral-foreground-rest` is FAST/Fluent Blazor.\r\n const textColor =\r\n pickFirst(\r\n scope,\r\n '--colorNeutralForeground1',\r\n '--neutral-foreground-rest',\r\n '--neutral-foreground-1-rest',\r\n ) || '#666666';\r\n\r\n // Gridlines / dividers — subtle stroke that should sit JUST above the\r\n // card background, never compete with the data. `colorNeutralStroke3`\r\n // (grey[24] in dark, grey[88] in light) hits that \"barely there\" look\r\n // matching FAST's `--neutral-stroke-divider-rest`. Earlier we tried\r\n // Stroke2 (grey[32] in dark) — that read as visibly bright against\r\n // the grey[20] card bg.\r\n const gridColor =\r\n pickFirst(\r\n scope,\r\n '--colorNeutralStroke3',\r\n '--neutral-stroke-divider-rest',\r\n '--neutral-stroke-rest',\r\n ) || 'rgba(128, 128, 128, 0.2)';\r\n\r\n // Accent fill — used as the first series color when no caller-supplied\r\n // color overrides it.\r\n //\r\n // FAST's `--accent-fill-rest` comes FIRST so the React chart inherits\r\n // the EXACT same accent value as the Blazor chart when both are\r\n // rendered side-by-side on the docs site. The docs page has\r\n // FluentDesignTheme at the root setting `--accent-fill-rest` (e.g.\r\n // to the OfficeColor the user picked); CSS variables inherit into\r\n // the FluentProvider subtree, so reading it from inside React's\r\n // tree picks up the host's value. Falls back to v9's\r\n // `--colorBrandBackground` when the host is React-only (no FAST).\r\n const accentColor =\r\n pickFirst(\r\n scope,\r\n '--accent-fill-rest',\r\n '--colorBrandBackground',\r\n '--accent-foreground-rest',\r\n ) || '#0078d4';\r\n\r\n // Canonical PowerPortalsPro complementary palette. 16 entries (the\r\n // accent + 15 hand-picked hues) so charts with up to 16 series get\r\n // distinct colors before the palette cycles via\r\n // `seriesColors[i % seriesColors.length]`. Order maximizes hue\r\n // spacing between adjacent slots so a small dataset (3–4 series)\r\n // picks visually distinct colors, not three blues in a row.\r\n //\r\n // All values are FluentUI persona/status `.primary` hexes — same\r\n // canonical hues Blazor's `FluentUIChart` palette falls back to when\r\n // its `--palette-*` FAST tokens aren't resolvable. Keeping the\r\n // hardcoded list in lockstep across both stacks is the simplest\r\n // way to guarantee a side-by-side render on the docs site produces\r\n // IDENTICAL bar colors.\r\n //\r\n // Earlier iterations chained through Fluent v9 palette tokens\r\n // (`--colorPaletteRedBackground3` etc.) but those resolve to RGB\r\n // values produced by a different design-token pipeline than FAST,\r\n // so the resulting bars still didn't match Blazor exactly. Skipping\r\n // the CSS-var indirection eliminates the drift.\r\n //\r\n // The accent color stays theme-driven via the brand token chain\r\n // above — the docs site's accent picker switches the brand on BOTH\r\n // stacks to the same logical accent name, so consumers selecting\r\n // (say) \"Excel green\" see the same first series color in both.\r\n const seriesColors = [\r\n accentColor,\r\n '#d13438', // red\r\n '#107c10', // green\r\n '#ffb900', // yellow\r\n '#5c2d91', // purple\r\n '#008272', // teal\r\n '#d83b01', // orange\r\n '#e3008c', // pink / magenta\r\n '#4f6bed', // cornflower\r\n '#498205', // forest\r\n '#00cc6a', // seafoam\r\n '#7160e8', // lavender\r\n '#8e562e', // brown\r\n '#a4262c', // burgundy\r\n '#3a96dd', // lightBlue\r\n '#0099bc', // cyan\r\n ];\r\n\r\n return { textColor, gridColor, accentColor, seriesColors };\r\n}\r\n\r\n/**\r\n * Renders a color string with `alpha` opacity. Handles the THREE input\r\n * shapes the palette can produce after CSS variables are resolved:\r\n *\r\n * - `#rrggbb` (6-digit hex) — raw hex defaults baked into the palette\r\n * when no CSS variable resolved.\r\n * - `rgb(r, g, b)` / `rgb(r g b)` — what `getComputedStyle()` returns\r\n * when a CSS variable holds a hex value (browsers normalize colors\r\n * to `rgb()` on read, even for `--var` values that LOOK like hex in\r\n * the source CSS). This is what most FluentUI v9 tokens come back\r\n * as in practice.\r\n * - `rgba(r, g, b, a)` — already-translucent values that we let through\r\n * unchanged (overwriting the alpha would be presumptuous when the\r\n * caller deliberately set one).\r\n *\r\n * Anything else returns unchanged. Previously this only handled `#hex`\r\n * and let `rgb()` pass through opaque, so cartesian bar fills and\r\n * borders ended up the same color in dark mode and the \"translucent\r\n * fill, full border\" look from Blazor was lost.\r\n */\r\nexport function toTranslucent(color: string, alpha: number): string {\r\n if (!color || color.trim().length === 0) return color;\r\n const trimmed = color.trim();\r\n\r\n if (trimmed.startsWith('#') && trimmed.length === 7) {\r\n const r = parseInt(trimmed.slice(1, 3), 16);\r\n const g = parseInt(trimmed.slice(3, 5), 16);\r\n const b = parseInt(trimmed.slice(5, 7), 16);\r\n if (Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b)) {\r\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\r\n }\r\n }\r\n\r\n // `rgb(R, G, B)` and `rgb(R G B)` — modern browsers accept both\r\n // comma- and space-separated forms. Extract the three numeric channels\r\n // and rewrap with the requested alpha.\r\n const rgbMatch = trimmed.match(/^rgb\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)\\s*\\)$/i);\r\n if (rgbMatch) {\r\n return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${alpha})`;\r\n }\r\n\r\n return trimmed;\r\n}\r\n","/**\r\n * Reactive hook that reads the active FluentUI chart palette and re-reads\r\n * whenever the page's design tokens change. Mirrors how Blazor's\r\n * `FluentUIChart` subscribes to `ThemeService.ThemeChanged` — the React\r\n * surface area is a `MutationObserver` on the document root, since theme\r\n * switches in FluentProvider land as class / style changes on a wrapping\r\n * element.\r\n */\r\n\r\nimport { useEffect, useState, type RefObject } from 'react';\r\nimport { getChartPalette, type FluentChartPalette } from './palette.js';\r\n\r\n/**\r\n * Returns the current FluentUI chart palette, or `null` until the\r\n * caller's `scopeRef` element has mounted and the FluentProvider's\r\n * design-token stylesheet has been applied to the document.\r\n *\r\n * **The `scopeRef` element must live INSIDE the `FluentProvider` tree**\r\n * — Fluent v9 scopes its design tokens to the provider's wrapper\r\n * (via inline `style` on the wrapper element), so reading\r\n * `--colorNeutralForeground1` from `document.documentElement` returns\r\n * an empty string and the chart falls back to a dim gray that's\r\n * unreadable in dark mode. Resolving via an in-tree element picks up\r\n * the right values via CSS inheritance.\r\n *\r\n * The palette identity changes whenever the underlying tokens change,\r\n * so consumers can include it in effect deps and re-theme.\r\n */\r\nexport function useFluentChartPalette(\r\n scopeRef: RefObject<HTMLElement | null>,\r\n): FluentChartPalette | null {\r\n const [palette, setPalette] = useState<FluentChartPalette | null>(null);\r\n\r\n useEffect(() => {\r\n // Initial read happens AFTER mount so the FluentProvider's design-\r\n // token stylesheet has been applied to the document. Reading\r\n // synchronously during render would catch the pre-Fluent baseline.\r\n let active = true;\r\n const readPalette = () => {\r\n if (!active) return;\r\n const scope = scopeRef.current;\r\n if (!scope) return;\r\n // Re-evaluating only when the serialized result differs avoids a\r\n // tear-down of the underlying Chart.js instance on every cosmetic\r\n // class flip that doesn't actually change colors.\r\n setPalette((prev) => {\r\n const next = getChartPalette(scope);\r\n if (prev && samePalette(prev, next)) return prev;\r\n return next;\r\n });\r\n };\r\n\r\n readPalette();\r\n\r\n // FluentProvider applies theme classes / inline styles either on\r\n // `<body>` or on its own wrapper element. Observing the document\r\n // root + body covers both cases without binding to FluentProvider\r\n // internals.\r\n const observer = new MutationObserver(readPalette);\r\n observer.observe(document.documentElement, {\r\n attributes: true,\r\n attributeFilter: ['class', 'style'],\r\n subtree: true,\r\n });\r\n observer.observe(document.body, {\r\n attributes: true,\r\n attributeFilter: ['class', 'style'],\r\n });\r\n\r\n return () => {\r\n active = false;\r\n observer.disconnect();\r\n };\r\n }, [scopeRef]);\r\n\r\n return palette;\r\n}\r\n\r\nfunction samePalette(a: FluentChartPalette, b: FluentChartPalette): boolean {\r\n if (a.textColor !== b.textColor) return false;\r\n if (a.gridColor !== b.gridColor) return false;\r\n if (a.accentColor !== b.accentColor) return false;\r\n if (a.seriesColors.length !== b.seriesColors.length) return false;\r\n for (let i = 0; i < a.seriesColors.length; i++) {\r\n if (a.seriesColors[i] !== b.seriesColors[i]) return false;\r\n }\r\n return true;\r\n}\r\n","/**\r\n * Theme-aware wrapper around `<Chart>`. Reads colors from the active\r\n * FluentUI design tokens and auto-fills missing dataset colors from a\r\n * palette derived from the active accent. Mirrors\r\n * `PowerPortalsPro.Web.Blazor.FluentUI.Components.FluentUIChart`.\r\n *\r\n * Cartesian charts get one palette color per dataset; radial charts\r\n * (pie, doughnut, polarArea, radar, funnel) get one color per data point\r\n * so each slice/segment is distinct.\r\n */\r\n\r\nimport { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';\r\nimport {\r\n Chart,\r\n ChartType,\r\n type ChartClickEventArgs,\r\n type ChartDataset,\r\n type ChartHandle,\r\n type ChartOrientation,\r\n type ChartLegendPosition,\r\n type ChartProps,\r\n type ChartTheme,\r\n type DataPoint,\r\n} from '@powerportalspro/react-charts';\r\nimport { toIndexAxis } from '@powerportalspro/react-charts';\r\nimport {\r\n forwardRef,\r\n useImperativeHandle,\r\n useMemo,\r\n useRef,\r\n type ForwardedRef,\r\n type ReactElement,\r\n type RefAttributes,\r\n} from 'react';\r\nimport { toTranslucent, type FluentChartPalette } from './palette.js';\r\nimport { useFluentChartPalette } from './use-fluent-chart-palette.js';\r\n\r\nconst RADIAL_CHART_TYPES = new Set<ChartType>([\r\n ChartType.Pie,\r\n ChartType.Doughnut,\r\n ChartType.PolarArea,\r\n ChartType.Radar,\r\n ChartType.Funnel,\r\n]);\r\n\r\nconst useStyles = makeStyles({\r\n // The wrapper uses Fluent design tokens directly so the rounded\r\n // bordered look picks up theme changes — same as\r\n // `FluentUIChart.razor.css` does in Blazor. The Blazor side uses\r\n // `var(--neutral-fill-layer-rest)` (FAST's \"card lifted off the\r\n // page\" surface), which goes SLIGHTLY DARKER than the page in light\r\n // mode and SLIGHTLY LIGHTER than the page in dark mode — the v9\r\n // `colorNeutralBackground2` ramp goes the opposite direction in dark\r\n // mode (recedes via grey[12] when the page is grey[16]), making the\r\n // chart card vanish into the page in dark theme.\r\n //\r\n // `colorNeutralCardBackground` is the dedicated card-elevation token\r\n // that follows FAST's behavior in both modes: grey[98] (faint grey)\r\n // in light against a white page, grey[20] (lighter grey) in dark\r\n // against a grey[16] page — visibly raised in both.\r\n border: {\r\n backgroundColor: tokens.colorNeutralCardBackground,\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n borderRadius: tokens.borderRadiusLarge,\r\n padding: tokens.spacingHorizontalM,\r\n boxSizing: 'border-box',\r\n overflow: 'hidden',\r\n },\r\n});\r\n\r\n/**\r\n * Props for {@link FluentUIChart}. Generic over `TPoint` so a consumer-\r\n * defined data-point subtype flows through datasets to the click handler\r\n * (see {@link ChartDataset} for the extension pattern). Defaults to\r\n * {@link DataPoint}, so the common case never has to mention the generic.\r\n */\r\nexport interface FluentUIChartProps<TPoint = DataPoint> {\r\n /** Chart type. Defaults to `ChartType.Bar`. */\r\n type?: ChartType;\r\n /** Category labels rendered along the primary axis. */\r\n labels: string[];\r\n /**\r\n * Datasets to plot. Datasets that omit `backgroundColor`/`borderColor`\r\n * are auto-themed from the active Fluent palette.\r\n */\r\n datasets: ChartDataset<TPoint>[];\r\n /** Optional CSS width applied to the outer container. */\r\n width?: string;\r\n /** Optional CSS height applied to the outer container. Defaults to `'400px'`. */\r\n height?: string;\r\n /** Y-axis tick / tooltip prefix (e.g. `\"$\"`). */\r\n yAxisPrefix?: string;\r\n /** Y-axis tick / tooltip suffix (e.g. `\"%\"`). */\r\n yAxisSuffix?: string;\r\n /** X-axis tick prefix. */\r\n xAxisPrefix?: string;\r\n /** X-axis tick suffix. */\r\n xAxisSuffix?: string;\r\n /** Title rendered above the chart. */\r\n title?: string;\r\n /** Stack datasets instead of placing them side-by-side. */\r\n stacked?: boolean;\r\n /** Legend position, or `Hidden`. Falls back to Chart.js's default. */\r\n legendPosition?: ChartLegendPosition;\r\n /** Enable / disable chart animations. Defaults to `true`. */\r\n enableAnimation?: boolean;\r\n /** Orientation override. Funnel charts default to horizontal. */\r\n orientation?: ChartOrientation;\r\n /** Wrap the chart in a themed bordered container. Defaults to `true`. */\r\n showBorder?: boolean;\r\n /** Click handler fired when the user clicks a rendered element. */\r\n onElementClick?: (args: ChartClickEventArgs<TPoint>) => void;\r\n}\r\n\r\n/**\r\n * Applies the palette to the caller's datasets — cartesian charts get one\r\n * color per dataset, radial charts get one color per data point. Original\r\n * dataset / data-point references are preserved when the caller already\r\n * supplied a color, so consumer-defined `DataverseDataPoint.record`\r\n * survives the transform.\r\n */\r\nfunction applyPaletteToDatasets<TPoint>(\r\n source: ChartDataset<TPoint>[],\r\n palette: FluentChartPalette,\r\n chartType: ChartType,\r\n): ChartDataset<TPoint>[] {\r\n const seriesColors =\r\n palette.seriesColors.length > 0 ? palette.seriesColors : [palette.accentColor];\r\n const isRadial = RADIAL_CHART_TYPES.has(chartType);\r\n\r\n return source.map((original, i) => {\r\n // `data` / `label` are optional in the generated schema (mirrors C#\r\n // properties with default values). Default to safe values so the\r\n // strict-mode `exactOptionalPropertyTypes` checks pass.\r\n const points: TPoint[] = original.data ?? [];\r\n const label = original.label ?? '';\r\n\r\n if (isRadial) {\r\n // Radial charts need one color PER data point so each slice gets\r\n // a distinct color. Clone each data point, preserving any\r\n // caller-set colors and any consumer-attached fields (records,\r\n // custom DataPoint subtype context) via `...point` spread.\r\n //\r\n // Translucent fill (alpha 0.5) + full-opacity border, matching\r\n // Blazor's `ToTranslucent(color, 0.5)` + full-opacity\r\n // BorderColor pair. With the same hex source colors (canonical\r\n // `#d13438` red, etc.) and the same alpha, Chart.js produces\r\n // visually identical pixels across both stacks.\r\n const themedData: TPoint[] = points.map((point, j) => {\r\n const color = seriesColors[j % seriesColors.length] ?? palette.accentColor;\r\n const p = point as DataPoint;\r\n return {\r\n ...point,\r\n backgroundColor: p.backgroundColor ?? toTranslucent(color, 0.5),\r\n borderColor: p.borderColor ?? color,\r\n } as TPoint;\r\n });\r\n\r\n const dataset: ChartDataset<TPoint> = {\r\n label,\r\n data: themedData,\r\n // Default to 1px border to match Blazor's ChartDataset, whose\r\n // C# default is `BorderWidth = 1`. Chart.js treats undefined\r\n // as 0 — no border drawn — which made the React version look\r\n // borderless next to Blazor's outlined bars/segments.\r\n borderWidth: original.borderWidth ?? 1,\r\n };\r\n return dataset;\r\n }\r\n\r\n // Cartesian charts (bar, line, scatter, bubble) use one color per\r\n // dataset. We DO NOT clone the data array — that preserves consumer-\r\n // attached metadata (like the original DataverseDataPoint instances)\r\n // for click-handler discrimination.\r\n //\r\n // Translucent fill (alpha 0.5) + full-opacity border — exact\r\n // match with Blazor's `ToTranslucent(color, 0.5)` pair. See the\r\n // radial branch above for the cross-stack alignment rationale.\r\n const color = seriesColors[i % seriesColors.length] ?? palette.accentColor;\r\n const dataset: ChartDataset<TPoint> = {\r\n label,\r\n data: points,\r\n backgroundColor: original.backgroundColor ?? toTranslucent(color, 0.5),\r\n borderColor: original.borderColor ?? color,\r\n // 1px default to match Blazor's `BorderWidth = 1` — see the radial\r\n // branch above for rationale.\r\n borderWidth: original.borderWidth ?? 1,\r\n };\r\n return dataset;\r\n });\r\n}\r\n\r\nfunction FluentUIChartImpl<TPoint>(\r\n props: FluentUIChartProps<TPoint>,\r\n ref: ForwardedRef<ChartHandle>,\r\n): ReactElement | null {\r\n const {\r\n type = ChartType.Bar,\r\n labels,\r\n datasets,\r\n width,\r\n height = '400px',\r\n yAxisPrefix,\r\n yAxisSuffix,\r\n xAxisPrefix,\r\n xAxisSuffix,\r\n title,\r\n stacked,\r\n legendPosition,\r\n enableAnimation,\r\n orientation,\r\n showBorder = true,\r\n onElementClick,\r\n } = props;\r\n\r\n const styles = useStyles();\r\n // Ref to the chart's outer wrapper — the palette hook reads\r\n // CSS-var values resolved AT THIS ELEMENT so it picks up\r\n // FluentProvider's scoped design tokens (Fluent v9 sets these on\r\n // the provider wrapper higher in the tree, NOT on\r\n // `document.documentElement`). Reading from html/body returns\r\n // empty strings for `--colorNeutralForeground1` and the chart\r\n // would fall back to a dim gray that's unreadable in dark mode.\r\n const containerRef = useRef<HTMLDivElement | null>(null);\r\n const palette = useFluentChartPalette(containerRef);\r\n\r\n // Themed datasets. `useMemo` keyed on the source datasets + palette so\r\n // the underlying `<Chart>` only re-renders when something it cares\r\n // about actually changed — matches the Blazor wrapper's\r\n // `RebuildThemedDatasetsIfNeeded`.\r\n const themedDatasets = useMemo(() => {\r\n if (!palette) return datasets;\r\n return applyPaletteToDatasets(datasets, palette, type);\r\n }, [datasets, palette, type]);\r\n\r\n // Build the theme object. Same: stable identity unless something\r\n // changed so the `<Chart>` ref-equality check passes.\r\n const theme = useMemo<ChartTheme | undefined>(() => {\r\n if (!palette) {\r\n // No palette yet — caller's display-only theme bits still flow\r\n // through so e.g. the title shows on the first paint.\r\n return assembleTheme(\r\n undefined,\r\n yAxisPrefix,\r\n yAxisSuffix,\r\n xAxisPrefix,\r\n xAxisSuffix,\r\n title,\r\n stacked,\r\n legendPosition,\r\n enableAnimation,\r\n orientation,\r\n );\r\n }\r\n return assembleTheme(\r\n palette,\r\n yAxisPrefix,\r\n yAxisSuffix,\r\n xAxisPrefix,\r\n xAxisSuffix,\r\n title,\r\n stacked,\r\n legendPosition,\r\n enableAnimation,\r\n orientation,\r\n );\r\n }, [\r\n palette,\r\n yAxisPrefix,\r\n yAxisSuffix,\r\n xAxisPrefix,\r\n xAxisSuffix,\r\n title,\r\n stacked,\r\n legendPosition,\r\n enableAnimation,\r\n orientation,\r\n ]);\r\n\r\n // Forward the ChartHandle so consumers can call `exportAsImage()`.\r\n const innerRef = useRef<ChartHandle>(null);\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n exportAsImage: () => innerRef.current?.exportAsImage() ?? null,\r\n }),\r\n [],\r\n );\r\n\r\n const chartProps: ChartProps<TPoint> = {\r\n type,\r\n labels,\r\n datasets: themedDatasets,\r\n height,\r\n };\r\n if (width !== undefined) chartProps.width = width;\r\n if (theme !== undefined) chartProps.theme = theme;\r\n if (onElementClick !== undefined) chartProps.onElementClick = onElementClick;\r\n\r\n // `<Chart<TPoint>>` explicit type argument — without it TS infers\r\n // TPoint = DataPoint at this call site and the generic FluentUIChart\r\n // can't pass its TPoint through to the underlying Chart.\r\n return (\r\n <div ref={containerRef} className={mergeClasses('ppp-fluent-ui-chart', showBorder ? styles.border : undefined)}>\r\n <Chart<TPoint> ref={innerRef} {...chartProps} />\r\n </div>\r\n );\r\n}\r\n\r\n// forwardRef erases generics on its inferred type — the cast preserves\r\n// the public generic signature so callers can write\r\n// `<FluentUIChart<MyPoint> ... />`. Runtime value is unchanged.\r\nexport const FluentUIChart = forwardRef(FluentUIChartImpl) as <TPoint = DataPoint>(\r\n props: FluentUIChartProps<TPoint> & RefAttributes<ChartHandle>,\r\n) => ReactElement | null;\r\n\r\nfunction assembleTheme(\r\n palette: FluentChartPalette | undefined,\r\n yAxisPrefix: string | undefined,\r\n yAxisSuffix: string | undefined,\r\n xAxisPrefix: string | undefined,\r\n xAxisSuffix: string | undefined,\r\n title: string | undefined,\r\n stacked: boolean | undefined,\r\n legendPosition: ChartLegendPosition | undefined,\r\n enableAnimation: boolean | undefined,\r\n orientation: ChartOrientation | undefined,\r\n): ChartTheme | undefined {\r\n const theme: ChartTheme = {};\r\n if (palette?.textColor) theme.textColor = palette.textColor;\r\n if (palette?.gridColor) theme.gridColor = palette.gridColor;\r\n if (yAxisPrefix !== undefined) theme.yAxisPrefix = yAxisPrefix;\r\n if (yAxisSuffix !== undefined) theme.yAxisSuffix = yAxisSuffix;\r\n if (xAxisPrefix !== undefined) theme.xAxisPrefix = xAxisPrefix;\r\n if (xAxisSuffix !== undefined) theme.xAxisSuffix = xAxisSuffix;\r\n if (title !== undefined) theme.title = title;\r\n if (stacked !== undefined) theme.stacked = stacked;\r\n if (legendPosition !== undefined) theme.legendPosition = legendPosition;\r\n if (enableAnimation !== undefined) theme.enableAnimation = enableAnimation;\r\n if (orientation !== undefined) theme.indexAxis = toIndexAxis(orientation);\r\n\r\n return Object.keys(theme).length > 0 ? theme : undefined;\r\n}\r\n","/**\r\n * Convenience component that loads a {@link DataverseChartDataSource}\r\n * (stand-alone class-based source) or reads from a {@link ViewDataSource}\r\n * (shared store with a sibling `<MainGrid>`), renders the result via\r\n * {@link FluentUIChart}, and surfaces a refresh + export toolbar plus\r\n * optional view-picker dropdown. Mirrors\r\n * `PowerPortalsPro.Web.Blazor.FluentUI.Components.DataverseChart`.\r\n */\r\n\r\nimport {\r\n Button,\r\n Dropdown,\r\n makeStyles,\r\n mergeClasses,\r\n MessageBar,\r\n MessageBarBody,\r\n Option,\r\n Spinner,\r\n tokens,\r\n} from '@fluentui/react-components';\r\nimport {\r\n ArrowClockwise24Regular,\r\n ArrowDownload24Regular,\r\n} from '@fluentui/react-icons';\r\nimport { usePowerPortalsPro, type ViewDataSource } from '@powerportalspro/react';\r\nimport type { ChartDataset, DataPoint, DataverseDataPoint } from '@powerportalspro/core';\r\nimport {\r\n AggregateDataverseChartDataSource,\r\n DataverseChartDataSource,\r\n ViewDataverseChartDataSource,\r\n type ChartClickEventArgs,\r\n type ChartData,\r\n type ChartLegendPosition,\r\n type ChartOrientation,\r\n type ChartType,\r\n} from '@powerportalspro/react-charts';\r\nimport { readColumnRawValue } from '../edits/base/column-display.js';\r\nimport {\r\n forwardRef,\r\n useCallback,\r\n useEffect,\r\n useImperativeHandle,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type CSSProperties,\r\n} from 'react';\r\nimport { FluentUIChart, type FluentUIChartProps } from './fluent-ui-chart.js';\r\nimport type { ChartHandle } from '@powerportalspro/react-charts';\r\n\r\nconst useStyles = makeStyles({\r\n // Outer container — flex column so the view selector, message bar, and\r\n // chart fill share the caller's height budget the way Blazor's\r\n // DataverseChart does.\r\n root: {\r\n position: 'relative',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n },\r\n toolbar: {\r\n position: 'absolute',\r\n top: '4px',\r\n right: '4px',\r\n zIndex: 1,\r\n display: 'flex',\r\n gap: '2px',\r\n },\r\n toolbarButton: {\r\n minWidth: 'auto',\r\n },\r\n viewSelector: {\r\n marginBottom: tokens.spacingVerticalS,\r\n maxWidth: '300px',\r\n flex: '0 0 auto',\r\n },\r\n // `min-height: 0` + `grid-template-rows: 1fr` resolves the inner chart's\r\n // `height: 100%` against a definite size — same trick the Blazor CSS\r\n // uses for the `ppp-chart-fill` slot.\r\n chartFill: {\r\n flex: '1 1 auto',\r\n minHeight: 0,\r\n display: 'grid',\r\n gridTemplateRows: '1fr',\r\n },\r\n empty: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: tokens.colorNeutralForeground3,\r\n fontStyle: 'italic',\r\n },\r\n loadingOverlay: {\r\n position: 'absolute',\r\n inset: 0,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: 'rgba(0, 0, 0, 0.15)',\r\n zIndex: 2,\r\n pointerEvents: 'all',\r\n },\r\n});\r\n\r\nexport interface DataverseChartProps {\r\n /**\r\n * The data source. Two shapes accepted; the chart discriminates at\r\n * runtime via `instanceof DataverseChartDataSource`:\r\n *\r\n * - **`DataverseChartDataSource`** (and its subclasses\r\n * `AggregateDataverseChartDataSource` /\r\n * `ViewDataverseChartDataSource`) — stand-alone class instance.\r\n * The chart owns the load/refresh lifecycle; the source just\r\n * supplies the FetchXML and the post-load shaping. Use this\r\n * when a chart owns its own fetch.\r\n *\r\n * - **`ViewDataSource`** (from `useViewDataSource()`) — shared\r\n * reactive store. The chart reads its aggregation result from\r\n * {@link aggregationIndex} of `aggregationResults` and dispatches\r\n * cross-filter state on slice clicks per {@link crossFilterMode}.\r\n * Use this when one fetch should drive multiple grids/charts\r\n * atomically (one round-trip, consistent snapshot).\r\n */\r\n dataSource: DataverseChartDataSource | ViewDataSource;\r\n /**\r\n * Which aggregation in {@link ViewDataSource.aggregationResults} to\r\n * render. Defaults to `0`. Use when the datasource is configured\r\n * with multiple aggregations and the chart should pick a specific\r\n * one (e.g. one chart per metric). Ignored when {@link dataSource}\r\n * is a `DataverseChartDataSource`.\r\n */\r\n aggregationIndex?: number;\r\n /**\r\n * What happens when a chart slice is clicked while\r\n * {@link dataSource} is a `ViewDataSource`:\r\n * - `'highlight'` (default) — soft cross-filter via\r\n * `dataSource.setHighlight({ column, value })`. The matching\r\n * slice stays opaque, other slices stay rendered but a sibling\r\n * grid's non-matching rows dim. Click the same slice again to\r\n * clear. NO server re-fetch — the cross-filter is purely styling.\r\n * - `'none'` — no datasource interaction. Use the existing\r\n * {@link onElementClick} callback for fully custom slice-click\r\n * behavior (drill-through navigation, custom filter, etc.).\r\n *\r\n * Ignored when {@link dataSource} is a `DataverseChartDataSource`.\r\n * Consumer-supplied {@link onElementClick} STILL fires alongside\r\n * the cross-filter action, in case the consumer wants to layer\r\n * telemetry / custom handling on top.\r\n */\r\n crossFilterMode?: 'highlight' | 'none';\r\n /** Chart type. */\r\n type?: ChartType;\r\n /** CSS width applied to the outer container. */\r\n width?: string;\r\n /** CSS height. Defaults to `'400px'`. */\r\n height?: string;\r\n /** Y-axis prefix / suffix forwarded to the underlying chart. */\r\n yAxisPrefix?: string;\r\n yAxisSuffix?: string;\r\n /** X-axis prefix / suffix forwarded to the underlying chart. */\r\n xAxisPrefix?: string;\r\n xAxisSuffix?: string;\r\n /** Chart title. */\r\n title?: string;\r\n /** Whether bar / line datasets stack. */\r\n stacked?: boolean;\r\n /** Legend position or `Hidden`. */\r\n legendPosition?: ChartLegendPosition;\r\n /** Enable / disable Chart.js animations. Defaults to `true`. */\r\n enableAnimation?: boolean;\r\n /** Chart orientation. */\r\n orientation?: ChartOrientation;\r\n /** Click handler forwarded to the inner chart. */\r\n onElementClick?: (args: ChartClickEventArgs) => void;\r\n /** Show the refresh button. Defaults to `true`. */\r\n showRefreshButton?: boolean;\r\n /** Show the download button. Defaults to `true`. */\r\n showExportButton?: boolean;\r\n /** Wrap the chart in a themed border. Defaults to `true`. */\r\n showBorder?: boolean;\r\n /**\r\n * Optional list of Dataverse view IDs to expose as a dropdown above the\r\n * chart. Selecting a view applies its filter conditions to the data\r\n * source and reloads.\r\n */\r\n viewIds?: string[];\r\n /** Initial selection from `viewIds` (defaults to the first one). */\r\n defaultViewId?: string;\r\n}\r\n\r\ninterface ViewOption {\r\n id: string;\r\n displayName: string;\r\n}\r\n\r\n/**\r\n * Imperative handle exposed to callers — currently just `exportAsImage()`.\r\n * Matches the {@link ChartHandle} shape so consumers wrapping\r\n * `<DataverseChart>` can pass it through unchanged.\r\n */\r\nexport type DataverseChartHandle = ChartHandle;\r\n\r\nexport const DataverseChart = forwardRef<DataverseChartHandle, DataverseChartProps>(\r\n function DataverseChart(props, ref) {\r\n const {\r\n dataSource,\r\n aggregationIndex = 0,\r\n crossFilterMode = 'highlight',\r\n type,\r\n width,\r\n height = '400px',\r\n yAxisPrefix,\r\n yAxisSuffix,\r\n xAxisPrefix,\r\n xAxisSuffix,\r\n title,\r\n stacked,\r\n legendPosition,\r\n enableAnimation,\r\n orientation,\r\n onElementClick,\r\n showRefreshButton = true,\r\n showExportButton = true,\r\n showBorder = true,\r\n viewIds,\r\n defaultViewId,\r\n } = props;\r\n\r\n // Single discrimination point — every downstream branch reads\r\n // from `viewDs` (the shared store path) OR `chartSource` (the\r\n // class-based stand-alone path). `instanceof` works because\r\n // `DataverseChartDataSource` is a real class imported as a value\r\n // (not just a type), and `ViewDataSource` is a plain object that\r\n // can never match the class check.\r\n const chartSource =\r\n dataSource instanceof DataverseChartDataSource ? dataSource : undefined;\r\n const viewDs =\r\n dataSource instanceof DataverseChartDataSource ? undefined : (dataSource as ViewDataSource | undefined);\r\n\r\n const styles = useStyles();\r\n const { ppp, viewMetadataCache } = usePowerPortalsPro();\r\n\r\n const [data, setData] = useState<ChartData>({ labels: [], datasets: [] });\r\n const [error, setError] = useState<string | null>(null);\r\n const [initialLoading, setInitialLoading] = useState(true);\r\n const [refreshing, setRefreshing] = useState(false);\r\n const [selectedViewId, setSelectedViewId] = useState<string | undefined>(\r\n defaultViewId ?? viewIds?.[0],\r\n );\r\n const [viewOptions, setViewOptions] = useState<ViewOption[]>([]);\r\n\r\n const chartRef = useRef<ChartHandle>(null);\r\n\r\n // Monotonic counter used to discard results from superseded loads —\r\n // race protection when refresh + view switch fire close together.\r\n const loadVersionRef = useRef(0);\r\n\r\n // One-shot default-view application. Mutates `source.viewId` so the\r\n // subsequent load fires with the right filter set. Declared BEFORE\r\n // the load effect — React runs commit-phase effects in declaration\r\n // order, so this commits its mutation before `loadAsync` reads\r\n // `source.viewId` in the next effect. Only re-runs on `source`\r\n // identity change (matches Blazor's OnInitialized once-per-mount\r\n // semantics); runtime changes to `defaultViewId` / `viewIds` props\r\n // are driven through the view-picker dropdown instead, which fires\r\n // `handleViewChange` and reloads explicitly.\r\n useEffect(() => {\r\n if (!chartSource) return;\r\n const viewToApply = defaultViewId ?? viewIds?.[0];\r\n if (viewToApply) {\r\n applyViewIdToSource(chartSource, viewToApply);\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [chartSource]);\r\n\r\n const loadAsync = useCallback(\r\n async (signal?: AbortSignal) => {\r\n // View-datasource mode owns its own fetch — the chart reads\r\n // the aggregation result reactively from\r\n // `viewDs.aggregationResults`, so the source-load path is a\r\n // no-op here.\r\n if (!chartSource) return;\r\n const version = ++loadVersionRef.current;\r\n try {\r\n const result = await chartSource.loadAsync(ppp, signal);\r\n if (version === loadVersionRef.current) {\r\n setData(result);\r\n setError(null);\r\n }\r\n } catch (e) {\r\n if (version === loadVersionRef.current) {\r\n setError(e instanceof Error ? e.message : String(e));\r\n }\r\n }\r\n },\r\n [chartSource, ppp],\r\n );\r\n\r\n // Initial load + abort on unmount.\r\n useEffect(() => {\r\n const controller = new AbortController();\r\n let cancelled = false;\r\n (async () => {\r\n await loadAsync(controller.signal);\r\n if (!cancelled) {\r\n setInitialLoading(false);\r\n }\r\n })();\r\n return () => {\r\n cancelled = true;\r\n controller.abort();\r\n };\r\n }, [loadAsync]);\r\n\r\n // Resolve view names once for the dropdown.\r\n useEffect(() => {\r\n if (!viewIds || viewIds.length === 0) {\r\n setViewOptions([]);\r\n return;\r\n }\r\n let cancelled = false;\r\n (async () => {\r\n const resolved = await Promise.all(\r\n viewIds.map(async (id) => {\r\n try {\r\n // Routed through the metadata cache so two consumers\r\n // looking at the same view id share a single fetch (and\r\n // the chart's reused view lookups across page navigations\r\n // hit the in-memory layer instead of re-fetching).\r\n const view = await viewMetadataCache.getAsync(id);\r\n return { id, displayName: view?.displayName ?? id } satisfies ViewOption;\r\n } catch {\r\n return { id, displayName: id } satisfies ViewOption;\r\n }\r\n }),\r\n );\r\n if (!cancelled) setViewOptions(resolved);\r\n })();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [viewIds, viewMetadataCache]);\r\n\r\n const handleRefresh = useCallback(async () => {\r\n // View-datasource mode: the shared store owns the fetch; just\r\n // kick it and let the reactive `aggregationResults` flow back.\r\n // We don't toggle local `refreshing` state because the\r\n // datasource's `isFetching` already drives the loading overlay\r\n // below.\r\n if (viewDs) {\r\n await viewDs.refresh();\r\n return;\r\n }\r\n setRefreshing(true);\r\n try {\r\n await loadAsync();\r\n } finally {\r\n setRefreshing(false);\r\n }\r\n }, [viewDs, loadAsync]);\r\n\r\n const handleExport = useCallback(() => {\r\n const dataUri = chartRef.current?.exportAsImage();\r\n if (!dataUri) return;\r\n // Synthesize a download via a temporary anchor. Same approach the\r\n // Blazor convenience component uses — no extra dependency.\r\n const link = document.createElement('a');\r\n link.href = dataUri;\r\n link.download = 'chart.png';\r\n document.body.appendChild(link);\r\n link.click();\r\n document.body.removeChild(link);\r\n }, []);\r\n\r\n const handleViewChange = useCallback(\r\n async (viewId: string) => {\r\n // View picker isn't wired in view-datasource mode — the\r\n // datasource owns the source (viewId/fetchXml). Defensive\r\n // guard so a consumer who passes both a `ViewDataSource` AND\r\n // `viewIds` doesn't crash on `chartSource` being undefined.\r\n if (!chartSource) return;\r\n setSelectedViewId(viewId);\r\n applyViewIdToSource(chartSource, viewId);\r\n setRefreshing(true);\r\n try {\r\n await loadAsync();\r\n } finally {\r\n setRefreshing(false);\r\n }\r\n },\r\n [chartSource, loadAsync],\r\n );\r\n\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n exportAsImage: () => chartRef.current?.exportAsImage() ?? null,\r\n }),\r\n [],\r\n );\r\n\r\n // Effective ChartData — from the shared store's\r\n // `aggregationResults` slot in view-datasource mode, or the local\r\n // source-load state otherwise. Selecting a specific aggregation\r\n // index lets one datasource power multiple charts (one per\r\n // aggregation).\r\n const effectiveData: ChartData = viewDs\r\n ? viewDs.aggregationResults[aggregationIndex] ?? { labels: [], datasets: [] }\r\n : data;\r\n // The generated `ChartData` types every field as optional (`labels?:`,\r\n // `datasets?:`). The server always populates both, but TS doesn't know\r\n // that — default to empty so downstream typing is straightforward.\r\n const labels = effectiveData.labels ?? [];\r\n const rawDatasets = effectiveData.datasets ?? [];\r\n\r\n // Visual feedback for the soft cross-filter: when a slice is\r\n // highlighted, fade every NON-matching data point so the matched\r\n // slice visually pops. Per-point `backgroundColor` overrides the\r\n // dataset's default — matching points stay unset (palette-supplied\r\n // color preserved), non-matching get a faded neutral. Same\r\n // unwrap-via-`readColumnRawValue` the click handler uses so\r\n // aggregate `RelatedRecordValue` wrappers compare equal to the\r\n // highlight value. Skipped entirely when no highlight is set.\r\n const datasets = useMemo<ChartDataset[]>(() => {\r\n const highlight = viewDs?.highlight;\r\n const groupByColumn = viewDs?.aggregations[aggregationIndex]?.groupBy?.column;\r\n if (!highlight || !groupByColumn) return rawDatasets;\r\n return rawDatasets.map((dataset) => ({\r\n ...dataset,\r\n data: (dataset.data ?? []).map((point) => {\r\n const dvpt = point as DataverseDataPoint;\r\n const pointValue = readColumnRawValue(dvpt.record?.properties?.[groupByColumn]);\r\n if (pointValue === highlight.value) return point;\r\n // Non-match: copy the point with a faded color override.\r\n // rgba lets the fade show through any dataset border, and the\r\n // neutral gray avoids fighting the matched slice's\r\n // palette-derived color.\r\n return { ...point, backgroundColor: 'rgba(160, 160, 160, 0.3)' } as DataPoint;\r\n }),\r\n }));\r\n }, [rawDatasets, viewDs?.highlight, viewDs?.aggregations, aggregationIndex]);\r\n\r\n const hasData =\r\n datasets.length > 0 && datasets.some((d: ChartDataset) => (d.data?.length ?? 0) > 0);\r\n\r\n // Slice-click handler. In view-datasource mode, drives the\r\n // cross-filter state per `crossFilterMode`. Always falls through\r\n // to the consumer-supplied `onElementClick` so consumers can\r\n // layer telemetry / custom handling on top of the default\r\n // behavior.\r\n const handleElementClick = useCallback(\r\n (args: ChartClickEventArgs) => {\r\n if (viewDs && crossFilterMode === 'highlight') {\r\n // Resolve the groupBy column for the current aggregation —\r\n // matches the datasource entry that produced\r\n // `effectiveData`. Without it we can't tell the highlight\r\n // which column to compare against. Defensive nullable\r\n // unwrap because openapi-typescript marks `groupBy` /\r\n // `column` as optional on the wire (the server always\r\n // populates both, but TS doesn't know that).\r\n const aggregation = viewDs.aggregations[aggregationIndex];\r\n const column = aggregation?.groupBy?.column;\r\n if (column) {\r\n // DataverseDataPoint carries the original record. Read the\r\n // primitive column value via `readColumnRawValue`, which\r\n // unwraps Dataverse's aliased-attribute `RelatedRecordValue`\r\n // wrapper (aggregate groupby attributes come back nested\r\n // one extra level) so the highlight value matches what\r\n // grid rows carry at `properties[column].value`. Combined\r\n // date groupings or formatted-string labels aren't\r\n // matchable here — those need drill-through (which the\r\n // consumer wires via `onElementClick` directly).\r\n const point = args.dataPoint as DataverseDataPoint | undefined;\r\n const rawValue = readColumnRawValue(point?.record?.properties?.[column]);\r\n if (rawValue !== undefined) {\r\n // Toggle: clicking the same slice clears the highlight.\r\n const current = viewDs.highlight;\r\n if (current && current.column === column && current.value === rawValue) {\r\n viewDs.setHighlight(undefined);\r\n } else {\r\n viewDs.setHighlight({ column, value: rawValue });\r\n }\r\n }\r\n }\r\n }\r\n onElementClick?.(args);\r\n },\r\n [viewDs, crossFilterMode, aggregationIndex, onElementClick],\r\n );\r\n\r\n // Loading / error effective values — view-datasource mode reads\r\n // through to the shared store's status; class-source mode uses\r\n // the initialLoading/refreshing/error useState shadows.\r\n //\r\n // The \"refreshing\" spinner reads `isFetchingAggregations` (not\r\n // the generic `isFetching`) so a sibling grid's sort-only\r\n // refetch — which the datasource sends WITHOUT the aggregations\r\n // spec, and the server then skips — doesn't flash a spinner over\r\n // unchanged chart bars. Initial-loading covers the empty-data\r\n // window before the first response lands.\r\n const effectiveError = viewDs\r\n ? (viewDs.error ? String(viewDs.error.message ?? viewDs.error) : null)\r\n : error;\r\n const effectiveInitialLoading = viewDs\r\n ? viewDs.status === 'loading' && viewDs.aggregationResults.length === 0\r\n : initialLoading;\r\n const effectiveRefreshing = viewDs ? viewDs.isFetchingAggregations : refreshing;\r\n\r\n const rootStyle: CSSProperties = {};\r\n if (width !== undefined) rootStyle.width = width;\r\n if (height !== undefined) rootStyle.height = height;\r\n\r\n const innerChartProps: FluentUIChartProps = {\r\n labels,\r\n datasets,\r\n width: '100%',\r\n height: '100%',\r\n showBorder,\r\n };\r\n if (type !== undefined) innerChartProps.type = type;\r\n if (yAxisPrefix !== undefined) innerChartProps.yAxisPrefix = yAxisPrefix;\r\n if (yAxisSuffix !== undefined) innerChartProps.yAxisSuffix = yAxisSuffix;\r\n if (xAxisPrefix !== undefined) innerChartProps.xAxisPrefix = xAxisPrefix;\r\n if (xAxisSuffix !== undefined) innerChartProps.xAxisSuffix = xAxisSuffix;\r\n if (title !== undefined) innerChartProps.title = title;\r\n if (stacked !== undefined) innerChartProps.stacked = stacked;\r\n if (legendPosition !== undefined) innerChartProps.legendPosition = legendPosition;\r\n if (enableAnimation !== undefined) innerChartProps.enableAnimation = enableAnimation;\r\n if (orientation !== undefined) innerChartProps.orientation = orientation;\r\n // Always plug our wrapper so cross-filter dispatch fires in\r\n // view-datasource mode. The wrapper itself falls through to\r\n // `onElementClick` if provided.\r\n innerChartProps.onElementClick = handleElementClick;\r\n\r\n return (\r\n <div className={mergeClasses('ppp-dataverse-chart', styles.root)} style={rootStyle}>\r\n {effectiveError && !effectiveInitialLoading && (\r\n <MessageBar intent=\"error\">\r\n <MessageBarBody>{effectiveError}</MessageBarBody>\r\n </MessageBar>\r\n )}\r\n\r\n {(showExportButton || showRefreshButton) && (\r\n <div className={styles.toolbar}>\r\n {showExportButton && (\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ArrowDownload24Regular />}\r\n title=\"Download chart as image\"\r\n onClick={handleExport}\r\n className={styles.toolbarButton}\r\n />\r\n )}\r\n {showRefreshButton && (\r\n <Button\r\n appearance=\"subtle\"\r\n icon={<ArrowClockwise24Regular />}\r\n title=\"Refresh chart data\"\r\n disabled={effectiveRefreshing}\r\n onClick={() => void handleRefresh()}\r\n className={styles.toolbarButton}\r\n />\r\n )}\r\n </div>\r\n )}\r\n\r\n {viewOptions.length > 0 && (\r\n <div className={styles.viewSelector}>\r\n <Dropdown\r\n value={\r\n viewOptions.find((v) => v.id === selectedViewId)?.displayName ?? ''\r\n }\r\n selectedOptions={selectedViewId ? [selectedViewId] : []}\r\n onOptionSelect={(_e, option) => {\r\n if (option.optionValue) void handleViewChange(option.optionValue);\r\n }}\r\n >\r\n {viewOptions.map((v) => (\r\n <Option key={v.id} value={v.id}>\r\n {v.displayName}\r\n </Option>\r\n ))}\r\n </Dropdown>\r\n </div>\r\n )}\r\n\r\n <div className={styles.chartFill}>\r\n {hasData ? (\r\n <FluentUIChart ref={chartRef} {...innerChartProps} />\r\n ) : !effectiveInitialLoading && effectiveError === null ? (\r\n <div className={styles.empty}>No data to display</div>\r\n ) : null}\r\n </div>\r\n\r\n {(effectiveInitialLoading || effectiveRefreshing) && (\r\n <div className={styles.loadingOverlay}>\r\n <Spinner />\r\n </div>\r\n )}\r\n </div>\r\n );\r\n },\r\n);\r\n\r\n/**\r\n * Applies a view id to whichever subclass the data source actually is.\r\n * Mirrors Blazor's `ApplyViewIdToSource` — silent no-op for the bare\r\n * {@link DataverseChartDataSource} base class (no `viewId` field).\r\n */\r\nfunction applyViewIdToSource(source: DataverseChartDataSource, viewId: string): void {\r\n if (source instanceof AggregateDataverseChartDataSource) {\r\n source.viewId = viewId;\r\n } else if (source instanceof ViewDataverseChartDataSource) {\r\n source.viewId = viewId;\r\n }\r\n}\r\n","export const VERSION = '0.1.0';\r\n\r\n// Edit components — Blazor's `Editors/` folder. Bound to the parent\r\n// <RecordContext>'s record via per-type column hooks.\r\nexport { TextEdit, type TextEditProps } from './edits/text-edit.js';\r\nexport { MemoEdit, type MemoEditProps } from './edits/memo-edit.js';\r\nexport { BoolEdit, BoolEditorType, LabelPosition, type BoolEditProps } from './edits/bool-edit.js';\r\nexport { NumberEdit, type NumberEditProps } from './edits/number-edit.js';\r\nexport { MoneyEdit, type MoneyEditProps } from './edits/money-edit.js';\r\nexport {\r\n ColumnDisplayValue,\r\n type ColumnDisplayValueProps,\r\n} from './edits/column-display-value.js';\r\nexport {\r\n DateTimeEdit,\r\n DateTimeEditorType,\r\n type DateTimeEditProps,\r\n} from './edits/date-time-edit.js';\r\nexport { FileEdit, type FileEditProps } from './edits/file-edit.js';\r\nexport { ImageEdit, type ImageEditProps } from './edits/image-edit.js';\r\nexport {\r\n ChoiceEdit,\r\n ChoiceEditorType,\r\n type ChoiceEditProps,\r\n} from './edits/choice-edit.js';\r\nexport {\r\n MultiSelectChoiceEdit,\r\n type MultiSelectChoiceEditProps,\r\n} from './edits/multi-select-choice-edit.js';\r\nexport {\r\n LookupEdit,\r\n LookupEditorType,\r\n type LookupEditProps,\r\n} from './edits/lookup-edit.js';\r\nexport {\r\n ManyToManyLookupEdit,\r\n ManyToManyLookupEditorType,\r\n type ManyToManyLookupEditProps,\r\n} from './edits/many-to-many-lookup-edit.js';\r\n// Meta-dispatcher — picks the right per-type edit component based on the\r\n// column's metadata `$type` discriminator. Mirrors Blazor's `ColumnEdit.razor`.\r\n// Used by the inline-editable grid + dynamic forms that don't enumerate\r\n// column types at authoring time.\r\nexport {\r\n ColumnEdit,\r\n getEditComponentForType,\r\n getEditComponentForMetadata,\r\n} from './edits/column-edit.js';\r\n\r\n// Reusable text-highlighter component + headless splitter. Used by\r\n// LookupEdit's option template; also intended for MainGrid cell rendering\r\n// and any other \"show what matched\" surface that needs case-insensitive\r\n// substring highlighting.\r\nexport {\r\n Highlighter,\r\n splitHighlightSegments,\r\n type HighlighterProps,\r\n} from './components/highlighter.js';\r\n\r\n// Display-value resolver — collapses a `TableRecord` + column-logical-name\r\n// pair to its user-visible string. Prefers Dataverse's pre-formatted\r\n// localized values (`formattedValues`) and falls back to type-aware\r\n// rendering of the raw `properties` payload. Shared with MainGrid for\r\n// consistent grid-cell display semantics.\r\nexport { resolveColumnDisplayValue } from './edits/base/column-display.js';\r\n\r\n// Shared choice config — sort order, valid/invalid-behavior, orientation —\r\n// reused by ChoiceEdit's `radioButtonOrientation` and MultiSelectChoiceEdit's\r\n// `checkboxOrientation`. The const-objects + literal-union types ship from a\r\n// single source of truth so both editors stay in lockstep.\r\nexport {\r\n ChoiceValueSort,\r\n ChoiceInvalidValueBehavior,\r\n ChoiceOrientation,\r\n} from './edits/base/choice-helpers.js';\r\n\r\n// Viewers — read-only renderers not bound to a record column.\r\nexport {\r\n ImageViewer,\r\n type ImageViewerProps,\r\n type ImageSource,\r\n type ImageViewerHandle,\r\n type CropAspect,\r\n type CropResult,\r\n} from './viewers/image-viewer.js';\r\nexport {\r\n FileViewer,\r\n type FileViewerProps,\r\n type FileSource,\r\n} from './viewers/file-viewer.js';\r\n\r\n// Per-type column hooks — the layer edit components are built on. Exported so\r\n// consumers writing custom editors (or non-FluentUI variants) can reuse them.\r\nexport {\r\n useStringColumn,\r\n useBoolColumn,\r\n useNumberColumn,\r\n useMoneyColumn,\r\n useDateColumn,\r\n useFileColumn,\r\n useChoiceColumn,\r\n useMultiSelectChoiceColumn,\r\n useLookupColumn,\r\n type NumberValueType,\r\n type FileColumnValue,\r\n type LookupColumnValue,\r\n} from './edits/base/use-column-edit.js';\r\n\r\nexport {\r\n DescriptionLocation,\r\n type BaseColumnEditProps,\r\n} from './edits/base/column-edit-types.js';\r\n\r\nexport { FluentDialogProvider } from './dialog/fluent-dialog-provider.js';\r\n\r\n// Theme — light / dark / system theme switching backed by Fluent v9's\r\n// FluentProvider. The provider persists the user's mode preference to\r\n// localStorage and honors `prefers-color-scheme` for the System mode.\r\n// `<ThemeSelector>` is the Dropdown UI; descendants read the resolved\r\n// state via `useTheme()`. Mirrors Blazor's `<ThemeProvider>` +\r\n// `<ThemeSelector>` pair. Accent-color selection (ThemeColorSelector)\r\n// and RTL/LTR direction (ThemeTextDirection) are deferred — the\r\n// Communication Blue accent + LTR ship as the default.\r\nexport {\r\n PowerPortalsProThemeProvider,\r\n type PowerPortalsProThemeProviderProps,\r\n} from './theme/theme-provider.js';\r\nexport {\r\n useTheme,\r\n ThemeMode,\r\n TextDirection,\r\n type ThemeContextValue,\r\n type AccentColor,\r\n} from './theme/theme-context.js';\r\nexport {\r\n type AccentThemePair,\r\n} from './theme/theme-provider.js';\r\nexport {\r\n ThemeSelector,\r\n type ThemeSelectorProps,\r\n} from './theme/theme-selector.js';\r\nexport {\r\n ThemeColorSelector,\r\n type ThemeColorSelectorProps,\r\n} from './theme/theme-color-selector.js';\r\nexport {\r\n ThemeTextDirection,\r\n type ThemeTextDirectionProps,\r\n} from './theme/theme-text-direction.js';\r\n\r\n// Language dropdown — UI-locale picker that drives `useLocale().setLocale`\r\n// and (by default) persists to the ASP.NET culture cookie so server-\r\n// rendered requests pick up the same locale. Mirrors Blazor's\r\n// `<LanguageDropdown>`.\r\nexport {\r\n LanguageDropdown,\r\n type LanguageDropdownProps,\r\n} from './editors/language-dropdown.js';\r\n\r\n// Menu surface — `<ProfileMainMenu>` (avatar + identity popover with\r\n// logout + optional Account link) and `<SiteSettingsButton>` (gear\r\n// icon → side drawer with language + theme sections). Mirror Blazor's\r\n// `<ProfileMainMenu>` / `<SiteSettingsButton>` / `<SiteSettingsPanel>`.\r\nexport {\r\n ProfileMainMenu,\r\n type ProfileMainMenuProps,\r\n} from './menu/profile-main-menu.js';\r\nexport {\r\n SiteSettingsButton,\r\n type SiteSettingsButtonProps,\r\n} from './menu/site-settings-button.js';\r\n// Standalone settings panel — same controls the gear-icon drawer\r\n// renders, but mountable directly anywhere (a dedicated /settings page,\r\n// a tab inside profile management, an embedded preferences card).\r\n// Mirrors Blazor's `<SiteSettingsPanel>`.\r\nexport {\r\n SiteSettingsPanel,\r\n type SiteSettingsPanelProps,\r\n} from './menu/site-settings-panel.js';\r\n// Standalone logout button — mountable outside ProfileMainMenu for\r\n// surfaces that want a dedicated sign-out action (sticky footer, kebab\r\n// menu, dedicated account page). Mirrors Blazor's `<LogoutButton>`.\r\nexport {\r\n LogoutButton,\r\n type LogoutButtonProps,\r\n} from './menu/logout-button.js';\r\n\r\n// Side-navigation menu — `<NavMenu>` + `<NavGroup>` + `<NavItem>`. Router-aware\r\n// (uses react-router's useLocation/useNavigate), with auto-expand of the\r\n// matching category and longest-prefix active-link highlighting. Mirror of\r\n// Blazor's `<FluentNavMenu>` / `<FluentNavGroup>` / `<FluentNavLink>`. Auth-gate\r\n// groups with `<AuthorizedView>` from `@powerportalspro/react`.\r\nexport {\r\n NavMenu,\r\n NavGroup,\r\n NavItem,\r\n type NavMenuProps,\r\n type NavGroupProps,\r\n type NavItemProps,\r\n type NavMatchMode,\r\n} from './menu/nav-menu.js';\r\n\r\nexport {\r\n FluentOverlayProvider,\r\n FluentOverlayTarget,\r\n} from './overlay/fluent-overlay-provider.js';\r\n\r\n// LocalizationBoundary — holds off rendering its children until the\r\n// listed `tables.*` / `views.*` localization bundles have been settled.\r\n// Default fallback is a centered Fluent Spinner; consumers can override\r\n// via the `fallback` prop. Headless equivalents (`useLocalization`,\r\n// `useLocalizationLoaded`) live in `@powerportalspro/react` for\r\n// custom-UI consumers.\r\nexport {\r\n LocalizationBoundary,\r\n type LocalizationBoundaryProps,\r\n} from './localization/localization-boundary.js';\r\n\r\nexport {\r\n ValidationSummary,\r\n type ValidationSummaryProps,\r\n} from './validation/validation-summary.js';\r\n\r\n// Context-aware buttons — mirror Blazor's SaveContextButton /\r\n// RefreshContextButton / DeleteContextButton. Each reads from the nearest\r\n// MainContext / RecordContext to know what to save/refresh/delete; the dirty\r\n// + permission state drives enabled/disabled automatically.\r\nexport {\r\n SaveContextButton,\r\n type SaveContextButtonProps,\r\n} from './buttons/save-context-button.js';\r\nexport {\r\n RefreshContextButton,\r\n type RefreshContextButtonProps,\r\n} from './buttons/refresh-context-button.js';\r\nexport {\r\n DeleteContextButton,\r\n type DeleteContextButtonProps,\r\n} from './buttons/delete-context-button.js';\r\n// Grid toolbar buttons — drop these inside `<MainGrid>` / `<SubGrid>`\r\n// (typically wrapped in `<GridButtons>`) and they auto-wire to the\r\n// grid's selection, refresh, and table info via `useGridContext()`.\r\n// Mirrors the Blazor `<NewRecordGridButton>` / `<OpenRecordGridButton>` /\r\n// ... pattern.\r\n// Grid button role taxonomy — auto-detected via component-type\r\n// markers so the grid can route row-level interactions (pending-\r\n// create rows → create button's re-open path; persisted rows → open\r\n// button). Custom buttons can opt in via `gridButtonRole` prop or\r\n// the `setGridButtonRole(Component, role)` module-scope helper.\r\nexport {\r\n GridButtonRole,\r\n setGridButtonRole,\r\n getGridButtonRoleFromElement,\r\n findFirstButtonOfRole,\r\n type GridButtonRoleProps,\r\n} from './buttons/grid-button-role.js';\r\nexport {\r\n GridButtonBehavior,\r\n evaluateGridButtonBehavior,\r\n} from './buttons/grid-button-behavior.js';\r\n// `<GridButton>` — public primitive for custom toolbar buttons.\r\n// Composes onto useGridContext + the grid-toolbar registry the typed\r\n// buttons use, so a custom button participates in overflow layout,\r\n// selection-based visibility / enabled gates, and (when\r\n// `gridButtonRole` is set) row-click dispatch.\r\nexport { GridButton, type GridButtonProps } from './buttons/grid-button.js';\r\nexport {\r\n NavigateRecordGridButton,\r\n type NavigateRecordGridButtonProps,\r\n type NavigateRecordGridButtonContext,\r\n} from './buttons/navigate-record-grid-button.js';\r\nexport {\r\n RefreshGridButton,\r\n type RefreshGridButtonProps,\r\n} from './buttons/refresh-grid-button.js';\r\nexport {\r\n NavigateNewRecordGridButton,\r\n type NavigateNewRecordGridButtonProps,\r\n} from './buttons/navigate-new-record-grid-button.js';\r\nexport {\r\n NavigateOpenRecordGridButton,\r\n type NavigateOpenRecordGridButtonProps,\r\n} from './buttons/navigate-open-record-grid-button.js';\r\nexport {\r\n OpenRecordGridButton,\r\n type OpenRecordGridButtonProps,\r\n type OpenRecordGridButtonBehavior,\r\n type DialogLocation,\r\n} from './buttons/open-record-grid-button.js';\r\nexport {\r\n NewRecordGridButton,\r\n type NewRecordGridButtonProps,\r\n type NewRecordGridButtonBehavior,\r\n type NewRecordDialogLocation,\r\n} from './buttons/new-record-grid-button.js';\r\nexport {\r\n DeleteRecordGridButton,\r\n type DeleteRecordGridButtonProps,\r\n} from './buttons/delete-record-grid-button.js';\r\n// M2M toolbar buttons. Drop these inside a `<SubGrid>` (which sets\r\n// parentRecord + relationshipName on the grid context) — they pop a\r\n// multi-select lookup dialog for Link and a confirmation for Unlink,\r\n// emitting Associate/Disassociate via the parent context's\r\n// transactional save (default `withGridContext`) or directly\r\n// (`behavior=\"immediately\"`). Mirrors Blazor's\r\n// `<LinkExistingRecordGridButton>` / `<UnlinkExistingRecordGridButton>`.\r\nexport {\r\n LinkExistingRecordGridButton,\r\n type LinkExistingRecordGridButtonProps,\r\n type LinkExistingRecordBehavior,\r\n type LinkExistingBulkMode,\r\n} from './buttons/link-existing-record-grid-button.js';\r\nexport {\r\n UnlinkExistingRecordGridButton,\r\n type UnlinkExistingRecordGridButtonProps,\r\n type UnlinkExistingRecordBehavior,\r\n type UnlinkExistingBulkMode,\r\n} from './buttons/unlink-existing-record-grid-button.js';\r\n\r\n// Grids — Phase 1 read-only MainGrid. Loads a view by id (or first view for\r\n// a table), renders records via TanStack's headless table powered by per-\r\n// type cell renderers. View switching, sorting, search, selection, edit,\r\n// and toolbar buttons land in later phases.\r\nexport { MainGrid, type MainGridProps } from './grids/main-grid.js';\r\n// Wire shape for `<MainGrid persistedStateQueryParameter>`. Exported so\r\n// consumer code that reads/writes the same URL parameter (e.g. a\r\n// \"Reset view\" button) can do so against the canonical shape.\r\nexport {\r\n type PersistedGridState,\r\n type PersistedSort,\r\n} from './grids/persisted-grid-state.js';\r\n// View-picker configuration types — sort order + the in-memory view shape\r\n// consumers use to mix custom views into the dropdown alongside Dataverse-\r\n// resolved ones. Mirrors Blazor's `ViewSort` enum + `GridViewDefinition`.\r\n// `PagingMode` selects between classic Prev/Next paging and infinite-scroll\r\n// — mirrors Blazor's `GridPagingMode`.\r\nexport {\r\n PagingMode,\r\n ViewSort,\r\n type CustomViewDefinition,\r\n} from './grids/view-options.js';\r\n// SubGrid — MainGrid scoped to records related to a parent via a\r\n// one-to-many Dataverse relationship. Picks up the cascading record from\r\n// the nearest `<RecordContext>` by default; accepts an explicit `record`\r\n// prop for the outside-of-context case.\r\nexport { SubGrid, type SubGridProps } from './grids/sub-grid.js';\r\n// FileGrid — MainGrid wrapper specialised for file-attachment tables.\r\n// Adds drag-and-drop upload, toolbar Upload / Replace / Download buttons,\r\n// auto-injection of the file column into the active view, and\r\n// MainContext-aware staging of uploads + replaces alongside the rest of\r\n// the form's pending changes.\r\nexport { FileGrid, type FileGridProps } from './grids/file-grid.js';\r\nexport { GridButtons, type GridButtonsProps } from './grids/grid-buttons.js';\r\n// Declarative column overrides — drop `<GridColumn columnName=\"...\">`\r\n// children inside `<MainGrid>` / `<SubGrid>` to switch the grid into\r\n// \"replace mode\": only the declared columns render, in the order\r\n// declared. Per-column overrides for width, displayName, cellRenderer,\r\n// alignment, and visibility. Mirrors Blazor's `<GridColumn>` /\r\n// `<GridColumns>`.\r\n//\r\n// `<GridTemplateColumn>` is the unbound counterpart — no `columnName`,\r\n// just a `cellRenderer` plus an optional `dependsOn` list of columns\r\n// the renderer needs (which the framework merges into the server's\r\n// projection). Use for combined-name cells, computed badges, pure\r\n// action columns, etc.\r\nexport {\r\n GridColumn,\r\n GridColumns,\r\n GridTemplateColumn,\r\n type GridColumnProps,\r\n type GridColumnsProps,\r\n type GridColumnAlign,\r\n type GridTemplateColumnProps,\r\n type GridDeclaredColumn,\r\n} from './grids/grid-column.js';\r\n// Grid context surface — exposes selectedRecords / tableName / viewId /\r\n// columns / refresh() to descendants. Public so consumers can build\r\n// custom grid buttons that read the same context the built-in ones use.\r\nexport {\r\n useGridContext,\r\n type GridContextValue,\r\n type PendingRowUpdate,\r\n type PendingRowCreate,\r\n} from './grids/grid-context.js';\r\n// Cell-renderer types exposed for grid consumers writing column-name or\r\n// type-category overrides via `MainGridProps.cellRenderers` /\r\n// `typeRenderers`. `getCellRenderer` is the built-in dispatcher consumers\r\n// can fall back to from inside a custom renderer (e.g. \"wrap the default\r\n// rendering with a tooltip\").\r\nexport {\r\n getCellRenderer,\r\n getRendererTypeForMetadata,\r\n isColumnSortable,\r\n type BaseCellRendererProps,\r\n type CellRendererType,\r\n type GridColumnCellRenderer,\r\n type GridColumnCellRendererProps,\r\n type GridTemplateColumnCellRenderer,\r\n type GridTemplateColumnCellRendererProps,\r\n type GridTemplateColumnEditRenderer,\r\n type GridTemplateColumnEditRendererProps,\r\n} from './grids/cells/cell-dispatcher.js';\r\n\r\n// Form layer — record-picker form + dialog. LookupForm composes a\r\n// table-type picker (when multiple tables are allowed), a Public-view\r\n// picker (filtered to the consumer-supplied viewIds when present), and\r\n// an embedded MainGrid wired for selection. LookupDialog is the modal\r\n// wrapper for the same surface, with title + footer + action buttons.\r\nexport { LookupForm, type LookupFormProps } from './form/lookup-form.js';\r\nexport { LookupDialog, type LookupDialogProps } from './form/lookup-dialog.js';\r\n// Wizard form — multi-step record editor backed by `react-use-wizard`.\r\n// Wraps the steps in a shared `<RecordContext>` so dirty/validate state\r\n// flows across pages; the final Finish button invokes the context's\r\n// save. Mirrors Blazor's `<WizardRecordForm>` / `<WizardRecordPage>`.\r\nexport {\r\n WizardRecordForm,\r\n type WizardRecordFormProps,\r\n type StepperVisibility,\r\n} from './form/wizard-record-form.js';\r\nexport {\r\n WizardRecordPage,\r\n type WizardRecordPageProps,\r\n} from './form/wizard-record-page.js';\r\n\r\n// Layout — page-level CSS Grid scaffold + multi-column section primitive.\r\n// PageLayout mirrors Blazor's header/navigation/body/footer grid; Section\r\n// + SectionColumn mirror Blazor's column-row container with optional\r\n// per-section / per-column headers and weighted widths. Wraps the\r\n// React-side equivalent of Blazor's layout slots so consumer apps don't\r\n// need to hand-roll grid scaffolding.\r\nexport { PageLayout, type PageLayoutProps } from './layout/page-layout.js';\r\n\r\n// Standalone layout slot components — semantic `<header>` / `<main>` /\r\n// `<footer>` wrappers with thin Fluent v9 default styling. Drop them\r\n// either into `<PageLayout>`'s slot props or use anywhere a styled\r\n// strip / content area is wanted. Mirrors Blazor's `<Header>` /\r\n// `<Body>` / `<Footer>` components.\r\nexport { Header, type HeaderProps } from './layout/header.js';\r\nexport { Body, type BodyProps } from './layout/body.js';\r\nexport { Footer, type FooterProps } from './layout/footer.js';\r\n\r\n// Optional react-router-dom integration. The package's react-router-dom\r\n// peer dep is marked optional — consumers using a different router\r\n// simply don't import this export and never pull in the dependency.\r\nexport { RouterUnsavedChangesGuard } from './routing/router-unsaved-changes-guard.js';\r\nexport {\r\n Section,\r\n SectionColumn,\r\n SectionColumnOrientation,\r\n type SectionProps,\r\n type SectionColumnProps,\r\n} from './layout/section.js';\r\n\r\n// Charts — Fluent-themed wrappers around the base `<Chart>` in\r\n// `@powerportalspro/react-charts`. `<FluentUIChart>` reads colors from\r\n// the active FluentUI design tokens and auto-fills missing dataset\r\n// colors from a palette derived from the active accent. `<DataverseChart>`\r\n// loads a `DataverseChartDataSource` and adds a refresh/export toolbar\r\n// plus an optional view-selector dropdown — mirrors the Blazor\r\n// `DataverseChart` convenience component.\r\nexport { FluentUIChart, type FluentUIChartProps } from './charts/fluent-ui-chart.js';\r\nexport {\r\n DataverseChart,\r\n type DataverseChartProps,\r\n type DataverseChartHandle,\r\n} from './charts/dataverse-chart.js';\r\nexport {\r\n getChartPalette,\r\n toTranslucent,\r\n type FluentChartPalette,\r\n} from './charts/palette.js';\r\nexport { useFluentChartPalette } from './charts/use-fluent-chart-palette.js';\r\n\r\n// Metadata-helper utilities — extract type-specific fields (maxLength, min/max,\r\n// precision) from ColumnMetadataBase and resolve required/readOnly defaults\r\n// according to the same rules Blazor's BaseColumnEdit applies. Edit components\r\n// use these internally; exported so consumers writing custom editors can too.\r\nexport {\r\n isMetadataRequired,\r\n isMetadataReadOnly,\r\n isNewRecord,\r\n getDisplayLabel,\r\n getDisplayDescription,\r\n getStringMaxLength,\r\n getNumberLimits,\r\n getChoiceMetadata,\r\n getLookupMetadata,\r\n type NumberLimits,\r\n type ChoiceOption,\r\n type ChoiceMetadataView,\r\n type LookupMetadataView,\r\n} from './edits/base/metadata-helpers.js';\r\n"]}