@academix-admin/selection-viewer 0.4.1 → 0.5.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.
package/dist/index.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var React = require('react');
7
7
  var modalSheet = require('@academix-admin/modal-sheet');
8
+ var overlayRoute = require('@academix-admin/overlay-route');
8
9
  var jsxRuntime = require('react/jsx-runtime');
9
10
 
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -273,6 +274,7 @@ var useModalKeys = (isOpen, onClose, containerId) => {
273
274
  var SelectionViewer = ({
274
275
  id: providedId,
275
276
  isOpen,
277
+ historyRoute,
276
278
  backDrop = true,
277
279
  onClose,
278
280
  titleProp,
@@ -295,6 +297,7 @@ var SelectionViewer = ({
295
297
  selectionState: selectionStateProp = "initial",
296
298
  closeThreshold = 0.2
297
299
  }) => {
300
+ overlayRoute.useOverlayRoute(historyRoute ?? "", Boolean(historyRoute) && isOpen, onClose);
298
301
  const [id] = React.useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);
299
302
  const [searchValue, setSearchValue] = React.useState("");
300
303
  const [isSearchFocused, setIsSearchFocused] = React.useState(false);
@@ -388,25 +391,41 @@ var SelectionViewer = ({
388
391
  searchInputRef.current.focus();
389
392
  }
390
393
  };
391
- const handleScroll = React.useCallback(
392
- async (e) => {
393
- if (isPaginating.current || !onPaginate) return;
394
- const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
395
- const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;
396
- if (isNearBottom) {
397
- isPaginating.current = true;
398
- const hasMore = await onPaginate();
399
- if (!hasMore) {
400
- isPaginating.current = false;
401
- } else {
402
- setTimeout(() => {
403
- isPaginating.current = false;
404
- }, 500);
405
- }
406
- }
407
- },
408
- [onPaginate]
409
- );
394
+ const onPaginateRef = React.useRef(onPaginate);
395
+ onPaginateRef.current = onPaginate;
396
+ const [contentEl, setContentEl] = React.useState(null);
397
+ const checkNearBottom = React.useCallback(async (el) => {
398
+ const paginate = onPaginateRef.current;
399
+ if (isPaginating.current || !paginate) return;
400
+ const { scrollTop, scrollHeight, clientHeight } = el;
401
+ const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;
402
+ if (!isNearBottom) return;
403
+ isPaginating.current = true;
404
+ const hasMore = await paginate();
405
+ if (!hasMore) {
406
+ isPaginating.current = false;
407
+ } else {
408
+ setTimeout(() => {
409
+ isPaginating.current = false;
410
+ }, 500);
411
+ }
412
+ }, []);
413
+ const hasPaginate = Boolean(onPaginate);
414
+ React.useEffect(() => {
415
+ if (!contentEl || !hasPaginate) return;
416
+ const scroller = contentEl.closest(".react-modal-sheet-content-scroller") ?? contentEl;
417
+ const onScroll = () => void checkNearBottom(scroller);
418
+ const onInnerScroll = () => void checkNearBottom(contentEl);
419
+ scroller.addEventListener("scroll", onScroll, { passive: true });
420
+ if (scroller !== contentEl) contentEl.addEventListener("scroll", onInnerScroll, { passive: true });
421
+ const resize = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => onScroll()) : null;
422
+ resize?.observe(contentEl);
423
+ return () => {
424
+ scroller.removeEventListener("scroll", onScroll);
425
+ contentEl.removeEventListener("scroll", onInnerScroll);
426
+ resize?.disconnect();
427
+ };
428
+ }, [contentEl, hasPaginate, checkNearBottom]);
410
429
  if (!isOpen && unmountOnClose) return null;
411
430
  return /* @__PURE__ */ jsxRuntime.jsx(SelectionViewerAggregateContext.Provider, { value: aggregateReporterValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
412
431
  modalSheet.Sheet,
@@ -595,8 +614,8 @@ var SelectionViewer = ({
595
614
  /* @__PURE__ */ jsxRuntime.jsx(modalSheet.Sheet.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(
596
615
  "div",
597
616
  {
617
+ ref: setContentEl,
598
618
  className: `selection-viewer-content ${childrenDirection}`,
599
- onScroll: onPaginate ? handleScroll : void 0,
600
619
  style: {
601
620
  paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : "0",
602
621
  paddingBottom: keyboardHeight > 0 ? `${keyboardHeight + 16}px` : "16px",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SelectionViewer.tsx","../src/Row.tsx","../src/Column.tsx"],"names":["React","useEffect","useState","useRef","useCallback","useMemo","jsx","jsxs","Sheet","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\r\nimport { Sheet } from \"@academix-admin/modal-sheet\";\r\n\r\n// ==================== Aggregation (Row/Column) ====================\r\n// Independent from @academix-admin/search-viewer's equivalent (Library Charter: packages stay\r\n// mutually independent) — same shape, duplicated intentionally rather than shared.\r\n\r\ntype AggregateReporter = {\r\n report: (id: string, state: SelectionState) => void;\r\n unreport: (id: string) => void;\r\n};\r\n\r\n/** A `Row` reports its own `state` into the nearest `Column` through this. */\r\nconst RowAggregateContext = React.createContext<AggregateReporter | null>(null);\r\n\r\n/** `Column` reports its own aggregated state up through this — `SelectionViewer` provides it and\r\n * merges whatever's reported into its own selectionState resolution, with zero extra wiring. */\r\nconst SelectionViewerAggregateContext = React.createContext<AggregateReporter | null>(null);\r\n\r\nfunction computeAggregateState(states: SelectionState[]): SelectionState {\r\n if (states.length === 0) return \"initial\";\r\n if (states.some((s) => s === \"data\")) return \"data\";\r\n if (states.some((s) => s === \"loading\")) return \"loading\";\r\n if (states.every((s) => s === \"empty\")) return \"empty\";\r\n if (states.some((s) => s === \"error\")) return \"error\";\r\n return \"initial\";\r\n}\r\n\r\n// ==================== Types ====================\r\ntype Padding = {\r\n l: string;\r\n r: string;\r\n t: string;\r\n b: string;\r\n};\r\n\r\ntype SnapPoint = number;\r\n\r\ntype SelectionState = \"loading\" | \"empty\" | \"error\" | \"data\" | \"initial\";\r\n\r\n\r\ntype TitleProps = {\r\n text: string;\r\n textColor: string;\r\n className?: string;\r\n containerClass?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype SearchProps = {\r\n text: string;\r\n textColor: string;\r\n className?: string;\r\n background?: string;\r\n prefixIcon?: React.ReactNode;\r\n suffixIcon?: React.ReactNode;\r\n clearIcon?: React.ReactNode;\r\n backIcon?: React.ReactNode;\r\n prefixGap?: string;\r\n suffixGap?: string;\r\n padding?: Padding;\r\n autoFocus?: boolean;\r\n inputStyle?: React.CSSProperties;\r\n containerStyle?: React.CSSProperties;\r\n onChange?: (value: string) => void;\r\n onFocus?: () => void;\r\n onBlur?: () => void;\r\n};\r\n\r\ntype LoadingProps = {\r\n view: React.ReactNode;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype NoResultProps = {\r\n view: React.ReactNode;\r\n text?: string;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype ErrorProps = {\r\n view: React.ReactNode;\r\n text?: string;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype CancelButtonProps = {\r\n text?: string;\r\n view: React.ReactNode;\r\n position?: \"left\" | \"right\";\r\n style?: React.CSSProperties;\r\n onClick?: () => void;\r\n /**\r\n * What a screen reader calls it. Defaults to \"Close\".\r\n *\r\n * A button whose only content is an icon has no accessible name at all, so it was announced as\r\n * just \"button\" — the one control that dismisses the sheet, and nothing said so.\r\n */\r\n ariaLabel?: string;\r\n};\r\n\r\ntype LayoutProps = {\r\n gapBetweenHandleAndTitle?: string;\r\n gapBetweenTitleAndSearch?: string;\r\n gapBetweenSearchAndContent?: string;\r\n /**\r\n * How far the rows are set in from the sides. Defaults to `16px`, matching the search box.\r\n *\r\n * The content had no inset while everything above it had 16px, so a list ran flush into the\r\n * sides of the screen under a search box that did not. Pass `0px` for rows that should reach\r\n * the edges.\r\n */\r\n contentPadding?: string;\r\n backgroundColor?: string;\r\n handleColor?: string;\r\n handleWidth?: string;\r\n fullScreenSearchBackground?: string;\r\n fullScreenSearchHeaderStyle?: React.CSSProperties;\r\n maxWidth?: string;\r\n};\r\n\r\ntype SelectionViewerProps = {\r\n id?: string;\r\n isOpen: boolean;\r\n backDrop?: boolean;\r\n onClose: () => void;\r\n titleProp: TitleProps;\r\n searchProp?: SearchProps;\r\n loadingProp?: LoadingProps;\r\n noResultProp?: NoResultProps;\r\n errorProp?: ErrorProps;\r\n cancelButton?: CancelButtonProps;\r\n layoutProp?: LayoutProps;\r\n childrenDirection?: \"vertical\" | \"horizontal\";\r\n children?: React.ReactNode;\r\n onPaginate?: () => boolean | Promise<boolean>;\r\n snapPoints?: SnapPoint[];\r\n initialSnap?: number;\r\n unmountOnClose?: boolean;\r\n zIndex?: number;\r\n /**\r\n * Accessible name for the sheet, announced when it opens.\r\n *\r\n * A modal with no name is announced simply as \"dialog\", which tells a screen-reader user that\r\n * something has taken over the screen and nothing at all about what.\r\n */\r\n ariaLabel?: string;\r\n maxHeight?: string;\r\n minHeight?: string;\r\n selectionState?: SelectionState;\r\n closeThreshold?: number;\r\n};\r\n\r\n// ==================== Styles ====================\r\nconst getStyles = (id: string) => `\r\n#${id} .selection-viewer-header {\r\n padding: 0px;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * The close button, with a target a thumb can find.\r\n *\r\n * It sat at right: 0, top: 8 with no padding at all, so the icon was flush against the sheet's\r\n * edge and the only thing that could be tapped was the glyph itself — a target well under half\r\n * what a finger needs, in the corner of the screen where a thumb is least accurate. Inset by 4px\r\n * and padded out to 44px square, so the icon still reads as sitting in the corner while the\r\n * hittable area reaches the edges.\r\n *\r\n * The cancelButton.style prop is still spread last at the call site, so a consumer that was\r\n * overriding any of this keeps overriding it. (No backticks in here: this whole block lives\r\n * inside a template literal, and one would end the string.)\r\n */\r\n#${id} .selection-viewer-cancel {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 44px;\r\n min-height: 44px;\r\n padding: 10px;\r\n border: none;\r\n background-color: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n}\r\n\r\n#${id} .selection-viewer-drag-handle {\r\n height: 5px;\r\n border-radius: 3px;\r\n margin-top: 16px;\r\n}\r\n\r\n#${id} .selection-viewer-container {\r\n display: flex;\r\n align-items: flex-start;\r\n width: 100%;\r\n}\r\n\r\n#${id} .selection-viewer-title {\r\n margin: 0px;\r\n font-size: 18px;\r\n font-weight: 600;\r\n padding: 0px 16px 0px 16px;\r\n word-break: break-word; /* break long words */\r\n white-space: normal; /* allow wrapping */\r\n}\r\n\r\n#${id} .selection-viewer-search {\r\n display: flex;\r\n align-items: center;\r\n margin: 16px 16px 4px 16px;\r\n border-radius: 12px;\r\n box-sizing: border-box;\r\n border: 1px solid rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n#${id} .selection-viewer-search-input {\r\n flex: 1;\r\n border: none;\r\n background: transparent;\r\n padding: 12px 8px;\r\n outline: none;\r\n font-size: 16px !important;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * The rows, set in from the edges like everything else in the sheet.\r\n *\r\n * The title and the search box have carried a 16px margin from the start; the content had none, so\r\n * a list of cards ran flush into the sides of the screen while the search box above it did not.\r\n * The layoutProp.contentPadding option overrides it for a consumer that wants its rows edge to\r\n * edge. (No backticks: this block is inside a template literal.)\r\n */\r\n#${id} .selection-viewer-content {\r\n overflow-y: auto;\r\n -webkit-overflow-scrolling: touch;\r\n flex: 1;\r\n min-height: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#${id} .selection-viewer-content.vertical {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n#${id} .selection-viewer-content.horizontal {\r\n display: flex;\r\n flex-direction: row;\r\n gap: 8px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n}\r\n\r\n#${id} .selection-viewer-no-results,\r\n#${id} .selection-viewer-error,\r\n#${id} .selection-viewer-loading {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n min-height: 120px;\r\n}\r\n\r\n#${id} .selection-viewer-default-no-results {\r\n color: #666;\r\n font-size: 14px;\r\n}\r\n\r\n#${id} .selection-viewer-default-error {\r\n color: red;\r\n font-size: 14px;\r\n}\r\n\r\n/* Full screen search mode */\r\n#${id} .selection-viewer-fullscreen-search {\r\n// position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n z-index: 10;\r\n// padding-top: env(safe-area-inset-top);\r\n width: 100%;\r\n}\r\n\r\n#${id} .selection-viewer-search-back-button {\r\n background: none;\r\n border: none;\r\n padding: 8px;\r\n margin-right: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n transition: opacity 0.2s;\r\n}\r\n\r\n#${id} .selection-viewer-search-back-button:hover {\r\n opacity: 0.7;\r\n}\r\n\r\n#${id} .selection-viewer-search-clear-button {\r\n background: none;\r\n border: none;\r\n padding: 8px;\r\n margin-left: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n transition: opacity 0.2s;\r\n}\r\n\r\n#${id} .selection-viewer-search-clear-button:hover {\r\n opacity: 0.7;\r\n}\r\n\r\n/* React Modal Sheet overrides */\r\n#${id} .react-modal-sheet-container {\r\n max-width: 500px;\r\n margin: 0 auto;\r\n border-top-left-radius: 16px;\r\n border-top-right-radius: 16px;\r\n}\r\n\r\n#${id} .react-modal-sheet-backdrop {\r\n background-color: rgba(0, 0, 0, 0.5) !important;\r\n pointer-events: auto !important;\r\n}\r\n\r\n#${id} .react-modal-sheet-content {\r\n padding: 0 0px 0px 0px;\r\n height: 100%;\r\n}\r\n\r\n\r\n/* Mobile full-width behavior */\r\n@media (max-width: 500px) {\r\n #${id} .react-modal-sheet-container {\r\n max-width: 100%;\r\n border-radius: 0;\r\n }\r\n}\r\n\r\n/* Prevent iOS zooming */\r\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\r\n #${id} .selection-viewer-search-input {\r\n font-size: 16px !important;\r\n }\r\n}\r\n`;\r\n\r\n// ==================== Hook to inject CSS per instance ====================\r\nconst useInjectStyles = (id: string) => {\r\n useEffect(() => {\r\n const styleId = `selection-viewer-styles-${id}`;\r\n let styleTag = document.getElementById(styleId) as HTMLStyleElement | null;\r\n\r\n if (!styleTag) {\r\n styleTag = document.createElement(\"style\");\r\n styleTag.id = styleId;\r\n styleTag.innerHTML = getStyles(id);\r\n document.head.appendChild(styleTag);\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (styleTag && document.head.contains(styleTag)) {\r\n document.head.removeChild(styleTag);\r\n }\r\n };\r\n }, [id]);\r\n};\r\n\r\n// ==================== Component ====================\r\n/**\r\n * Escape closes the viewer, and Tab stays inside it.\r\n *\r\n * The viewer covers the screen, so what is behind it is not available — but nothing said so to the\r\n * keyboard. Tab walked out of the option list into the page underneath, where a person was\r\n * operating controls they could not see, and Escape did nothing at all, which is the one key\r\n * everybody reaches for to get out of a picker.\r\n *\r\n * Deliberately a private copy rather than an import from a sibling package: every library here\r\n * stands alone, and a shared helper between two of them is a dependency the consumer did not ask\r\n * for. Takes the container's `id` rather than a ref, because the sheet element belongs to the\r\n * underlying modal-sheet component; looking it up when a key is pressed finds it however that\r\n * component chooses to mount it. Focusables are re-queried on every Tab, since the option list\r\n * changes as the consumer filters it and a list captured on open is a list of dead elements.\r\n */\r\nconst useModalKeys = (isOpen: boolean, onClose: () => void, containerId: string) => {\r\n useEffect(() => {\r\n if (!isOpen || typeof document === \"undefined\") return;\r\n\r\n const onKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n e.preventDefault();\r\n onClose();\r\n return;\r\n }\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = document.getElementById(containerId)?.querySelectorAll<HTMLElement>(\r\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]),' +\r\n ' textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (!focusable || focusable.length === 0) return;\r\n\r\n const first = focusable[0];\r\n const last = focusable[focusable.length - 1];\r\n\r\n if (e.shiftKey && document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n } else if (!e.shiftKey && document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", onKeyDown);\r\n return () => document.removeEventListener(\"keydown\", onKeyDown);\r\n }, [isOpen, onClose, containerId]);\r\n};\r\n\r\nconst SelectionViewer: React.FC<SelectionViewerProps> = ({\r\n id: providedId,\r\n isOpen,\r\n backDrop = true,\r\n onClose,\r\n titleProp,\r\n searchProp,\r\n loadingProp,\r\n noResultProp,\r\n errorProp,\r\n cancelButton,\r\n layoutProp,\r\n childrenDirection = \"vertical\",\r\n children,\r\n onPaginate,\r\n snapPoints = [0, 1],\r\n initialSnap = 1,\r\n unmountOnClose = true,\r\n zIndex = 1000,\r\n ariaLabel,\r\n maxHeight = \"90dvh\",\r\n minHeight = \"65dvh\",\r\n selectionState: selectionStateProp = \"initial\",\r\n closeThreshold = 0.2,\r\n}) => {\r\n const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);\r\n const [searchValue, setSearchValue] = useState(\"\");\r\n const [isSearchFocused, setIsSearchFocused] = useState(false);\r\n const [keyboardHeight, setKeyboardHeight] = useState(0);\r\n const [inputKey, setInputKey] = useState(0);\r\n const [shouldAutoFocus, setShouldAutoFocus] = useState(false);\r\n const isPaginating = useRef(false);\r\n const searchInputRef = useRef<HTMLInputElement>(null);\r\n const sheetRef = useRef<any>(null);\r\n const headerRef = useRef<HTMLDivElement>(null);\r\n\r\n useInjectStyles(id);\r\n // Escape closes the viewer and Tab stays inside it — see useModalKeys.\r\n useModalKeys(isOpen, onClose, id);\r\n\r\n // Descendant sections (a Column, or anything else) can report a cumulative state up through this —\r\n // starts empty, so a plain SelectionViewer with no Row/Column children behaves exactly as before.\r\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\r\n const reportAggregate = useCallback((reporterId: string, state: SelectionState) => {\r\n setReportedStates((prev) => {\r\n const next = new Map(prev);\r\n next.set(reporterId, state);\r\n return next;\r\n });\r\n }, []);\r\n const unreportAggregate = useCallback((reporterId: string) => {\r\n setReportedStates((prev) => {\r\n if (!prev.has(reporterId)) return prev;\r\n const next = new Map(prev);\r\n next.delete(reporterId);\r\n return next;\r\n });\r\n }, []);\r\n const aggregateReporterValue = useMemo(\r\n () => ({ report: reportAggregate, unreport: unreportAggregate }),\r\n [reportAggregate, unreportAggregate]\r\n );\r\n const reportedAggregate =\r\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : null;\r\n const isComposed = reportedAggregate !== null;\r\n\r\n // Precedence: an explicit `selectionState` prop always wins (matches today's exact behaviour for\r\n // any consumer not using Row/Column) -> then a reported Column aggregate, if one exists -> \"initial\".\r\n const selectionState =\r\n selectionStateProp !== \"initial\" ? selectionStateProp : reportedAggregate ?? \"initial\";\r\n\r\n // Track keyboard height\r\n useEffect(() => {\r\n if (typeof window === 'undefined' || !window.visualViewport) return;\r\n\r\n const handleResize = () => {\r\n const vp = window.visualViewport!;\r\n const kbHeight = window.innerHeight - vp.height;\r\n setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);\r\n };\r\n\r\n window.visualViewport.addEventListener('resize', handleResize);\r\n window.visualViewport.addEventListener('scroll', handleResize);\r\n\r\n return () => {\r\n window.visualViewport?.removeEventListener('resize', handleResize);\r\n window.visualViewport?.removeEventListener('scroll', handleResize);\r\n };\r\n }, []);\r\n\r\n // Auto-focus on search\r\n useEffect(() => {\r\n if (!isOpen) {\r\n searchInputRef.current?.blur();\r\n setSearchValue(\"\");\r\n searchProp?.onChange?.(\"\");\r\n setShouldAutoFocus(false);\r\n return;\r\n }\r\n }, [isOpen, searchProp?.autoFocus]);\r\n\r\n const handleOpenEnd = useCallback(() => {\r\n if (searchProp?.autoFocus && searchInputRef.current) {\r\n setInputKey(prev => prev + 1);\r\n setShouldAutoFocus(true);\r\n searchInputRef.current.focus();\r\n searchInputRef.current.click();\r\n }\r\n }, [searchProp?.autoFocus]);\r\n\r\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n const value = e.target.value;\r\n setSearchValue(value);\r\n searchProp?.onChange?.(value);\r\n };\r\n\r\n const handleSearchFocus = () => {\r\n setIsSearchFocused(true);\r\n searchProp?.onFocus?.();\r\n requestAnimationFrame(() => {\r\n searchInputRef.current?.focus();\r\n });\r\n };\r\n\r\n const handleSearchBlur = () => {\r\n searchProp?.onBlur?.();\r\n };\r\n\r\n const handleBackFromSearch = () => {\r\n setIsSearchFocused(false);\r\n if (searchInputRef.current) {\r\n searchInputRef.current.blur();\r\n }\r\n };\r\n\r\n const clearSearch = () => {\r\n setSearchValue(\"\");\r\n searchProp?.onChange?.(\"\");\r\n if (searchInputRef.current) {\r\n searchInputRef.current.focus();\r\n }\r\n };\r\n\r\n const handleScroll = useCallback(\r\n async (e: React.UIEvent<HTMLDivElement>) => {\r\n if (isPaginating.current || !onPaginate) return;\r\n\r\n const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;\r\n const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;\r\n\r\n if (isNearBottom) {\r\n isPaginating.current = true;\r\n const hasMore = await onPaginate();\r\n if (!hasMore) {\r\n isPaginating.current = false;\r\n } else {\r\n setTimeout(() => {\r\n isPaginating.current = false;\r\n }, 500);\r\n }\r\n }\r\n },\r\n [onPaginate]\r\n );\r\n\r\n if (!isOpen && unmountOnClose) return null;\r\n\r\n return (\r\n <SelectionViewerAggregateContext.Provider value={aggregateReporterValue}>\r\n <Sheet\r\n ref={sheetRef}\r\n isOpen={isOpen}\r\n onClose={onClose}\r\n detent=\"content\"\r\n style={{ zIndex }}\r\n maxHeight={maxHeight}\r\n onOpenEnd={handleOpenEnd}\r\n >\r\n <Sheet.Container\r\n id={id}\r\n // Announced as a modal dialog. Without this a screen reader reads the sheet as one more\r\n // region of the page and gives no indication that what is behind it is unavailable — and\r\n // an automated check for an open dialog finds nothing at all.\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={ariaLabel}\r\n style={{\r\n maxHeight: maxHeight,\r\n minHeight: isSearchFocused ? \"100dvh\" : minHeight,\r\n maxWidth: layoutProp?.maxWidth ?? '800px',\r\n margin: \"0 auto\",\r\n width: \"100%\",\r\n left: 0,\r\n right: 0,\r\n paddingBottom: \"calc(0px + env(safe-area-inset-bottom))\",\r\n background: isSearchFocused\r\n ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor\r\n : layoutProp?.backgroundColor,\r\n borderTopLeftRadius: isSearchFocused ? \"0px\" : \"16px\",\r\n borderTopRightRadius: isSearchFocused ? \"0px\" : \"16px\",\r\n }}\r\n >\r\n {isSearchFocused ? (\r\n <Sheet.Header>\r\n <div\r\n className=\"selection-viewer-fullscreen-search\"\r\n style={layoutProp?.fullScreenSearchHeaderStyle}\r\n >\r\n <div className=\"selection-viewer-search\" style={{\r\n ...searchProp?.containerStyle,\r\n background: searchProp?.background,\r\n padding: searchProp?.padding\r\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\r\n : \"16px\"\r\n }}>\r\n <button\r\n className=\"selection-viewer-search-back-button\"\r\n onClick={handleBackFromSearch}\r\n aria-label=\"Exit search mode\"\r\n >\r\n {searchProp?.backIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M15 18L9 12L15 6\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n\r\n <input\r\n key={inputKey}\r\n ref={searchInputRef}\r\n type=\"text\"\r\n placeholder={searchProp?.text}\r\n value={searchValue}\r\n onChange={handleSearchChange}\r\n className={searchProp?.className || \"selection-viewer-search-input\"}\r\n style={{\r\n color: searchProp?.textColor,\r\n ...searchProp?.inputStyle\r\n }}\r\n onFocus={handleSearchFocus}\r\n onBlur={handleSearchBlur}\r\n autoFocus={shouldAutoFocus}\r\n />\r\n\r\n {searchValue && (\r\n <button\r\n className=\"selection-viewer-search-clear-button\"\r\n onClick={clearSearch}\r\n aria-label=\"Clear search\"\r\n >\r\n {searchProp?.clearIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18 6L6 18M6 6L18 18\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </Sheet.Header>\r\n ) : (\r\n <Sheet.Header>\r\n <div className=\"selection-viewer-header\">\r\n {/* Cancel button left */}\r\n {cancelButton?.position === \"left\" && (\r\n <button\r\n aria-label={cancelButton.ariaLabel || \"Close\"}\r\n className=\"selection-viewer-cancel\"\r\n style={{\r\n position: \"absolute\",\r\n left: \"4px\",\r\n top: \"4px\",\r\n ...cancelButton.style\r\n }}\r\n onClick={cancelButton.onClick || onClose}\r\n >\r\n {cancelButton.view || cancelButton.text || \"Cancel\"}\r\n </button>\r\n )}\r\n\r\n {/* Drag handle */}\r\n {!isSearchFocused && (\r\n <div\r\n className=\"selection-viewer-drag-handle\"\r\n style={{\r\n background: layoutProp?.handleColor || \"#ccc\",\r\n width: layoutProp?.handleWidth || \"40px\",\r\n marginBottom: layoutProp?.gapBetweenHandleAndTitle || \"12px\",\r\n }}\r\n />\r\n )}\r\n\r\n {/* Cancel button right */}\r\n {cancelButton?.position === \"right\" && (\r\n <button\r\n aria-label={cancelButton.ariaLabel || \"Close\"}\r\n className=\"selection-viewer-cancel\"\r\n style={{\r\n position: \"absolute\",\r\n right: \"4px\",\r\n top: \"4px\",\r\n ...cancelButton.style\r\n }}\r\n onClick={cancelButton.onClick || onClose}\r\n >\r\n {cancelButton.view || cancelButton.text || \"Cancel\"}\r\n </button>\r\n )}\r\n\r\n {/* Title */}\r\n <div className={titleProp.containerClass || \"selection-viewer-container\"}>\r\n <h2\r\n id={`${id}-title`}\r\n className={titleProp.className || \"selection-viewer-title\"}\r\n style={{\r\n marginBottom: layoutProp?.gapBetweenTitleAndSearch || \"8px\",\r\n color: titleProp?.textColor || 'black',\r\n ...titleProp.style\r\n }}\r\n >\r\n {titleProp.text}\r\n </h2>\r\n </div>\r\n </div>\r\n\r\n {/* Search */}\r\n {searchProp && (\r\n <div\r\n className=\"selection-viewer-search\"\r\n style={{\r\n background: searchProp.background,\r\n padding: searchProp.padding\r\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\r\n : \"8px 16px\",\r\n marginBottom: layoutProp?.gapBetweenSearchAndContent,\r\n ...searchProp.containerStyle\r\n }}\r\n >\r\n {searchProp.prefixIcon && (\r\n <span style={{ marginRight: searchProp.prefixGap || \"8px\" }}>\r\n {searchProp.prefixIcon}\r\n </span>\r\n )}\r\n <input\r\n key={inputKey}\r\n ref={searchInputRef}\r\n type=\"text\"\r\n placeholder={searchProp.text}\r\n value={searchValue}\r\n onChange={handleSearchChange}\r\n className={searchProp.className || \"selection-viewer-search-input\"}\r\n style={{\r\n color: searchProp?.textColor,\r\n ...searchProp.inputStyle\r\n }}\r\n onFocus={handleSearchFocus}\r\n onBlur={handleSearchBlur}\r\n autoFocus={shouldAutoFocus}\r\n />\r\n {searchValue && (\r\n <button\r\n className=\"selection-viewer-search-clear-button\"\r\n onClick={clearSearch}\r\n aria-label=\"Clear search\"\r\n style={{ marginLeft: searchProp.suffixGap || \"8px\" }}\r\n >\r\n {searchProp.clearIcon || searchProp.suffixIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n )}\r\n {!searchValue && searchProp.suffixIcon && (\r\n <span style={{ marginLeft: searchProp.suffixGap || \"8px\" }}>\r\n {searchProp.suffixIcon}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </Sheet.Header>\r\n )}\r\n\r\n <Sheet.Content>\r\n <div\r\n className={`selection-viewer-content ${childrenDirection}`}\r\n onScroll={onPaginate ? handleScroll : undefined}\r\n style={{\r\n paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : '0',\r\n paddingBottom: keyboardHeight > 0\r\n ? `${keyboardHeight + 16}px`\r\n : '16px',\r\n /*\r\n * Set in from the edges, like everything else in the sheet.\r\n *\r\n * The title and the search box have carried 16px from the start and the content had\r\n * none, so a list of rows ran flush into the sides of the screen under a search box\r\n * that did not. Overridable for rows that genuinely want the full width.\r\n */\r\n paddingLeft: layoutProp?.contentPadding ?? '16px',\r\n paddingRight: layoutProp?.contentPadding ?? '16px',\r\n }}\r\n >\r\n {/* Show children first, then loading at bottom */}\r\n {React.Children.count(children) > 0 ? (\r\n <>\r\n {children}\r\n {selectionState === \"loading\" && (\r\n <div\r\n className=\"selection-viewer-loading\"\r\n style={{\r\n padding: loadingProp?.padding\r\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\r\n : \"16px\",\r\n ...loadingProp?.style\r\n }}\r\n >\r\n {loadingProp?.view}\r\n </div>\r\n )}\r\n {/*\r\n Shown ALONGSIDE the children, not instead of them.\r\n\r\n This was guarded on composed mode — a Column/Row descendant reporting its own\r\n state — on the reasoning that only a composed sheet can have children and be\r\n empty at the same time. That is not true of the commonest arrangement there is:\r\n a consumer that wraps its rows in a single container. `React.Children.count` is\r\n then 1 whether the container holds fifty rows or none, so the empty branch below\r\n never ran and the sheet showed a blank white screen — at the exact moment\r\n somebody had searched for something that is not there, which is when the empty\r\n state is the only useful thing on screen.\r\n\r\n Layered rather than substituted, for the reason the composed case already had:\r\n a child that reports its own state must stay mounted to go on reporting. A\r\n consumer whose `selectionState` says \"empty\" is asserting there is nothing to\r\n show, so drawing its own view beside visually-empty children is what it asked\r\n for.\r\n */}\r\n {selectionState === \"empty\" && (\r\n <div\r\n className=\"selection-viewer-no-results\"\r\n style={{\r\n padding: noResultProp?.padding\r\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\r\n : \"16px\",\r\n ...noResultProp?.style\r\n }}\r\n >\r\n {noResultProp?.view || (\r\n <div className=\"selection-viewer-default-no-results\">\r\n {noResultProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n {selectionState === \"error\" && (\r\n <div\r\n className=\"selection-viewer-error\"\r\n style={{\r\n padding: errorProp?.padding\r\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\r\n : \"16px\",\r\n ...errorProp?.style\r\n }}\r\n >\r\n {errorProp?.view || (\r\n <div className=\"selection-viewer-default-error\">\r\n {errorProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </>\r\n ) : (selectionState === \"empty\" || (React.Children.count(children) <= 0 && (selectionState != \"loading\" && selectionState != \"error\"))) ? (\r\n <div\r\n className=\"selection-viewer-no-results\"\r\n style={{\r\n padding: noResultProp?.padding\r\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\r\n : \"16px\",\r\n ...noResultProp?.style\r\n }}\r\n >\r\n {noResultProp?.view || (\r\n <div className=\"selection-viewer-default-no-results\">\r\n {noResultProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n ) : selectionState === \"loading\" ? (\r\n <div\r\n className=\"selection-viewer-loading\"\r\n style={{\r\n padding: loadingProp?.padding\r\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\r\n : \"16px\",\r\n ...loadingProp?.style\r\n }}\r\n >\r\n {loadingProp?.view}\r\n </div>\r\n ) : (selectionState === \"error\") ? (\r\n <div\r\n className=\"selection-viewer-error\"\r\n style={{\r\n padding: errorProp?.padding\r\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\r\n : \"16px\",\r\n ...errorProp?.style\r\n }}\r\n >\r\n {errorProp?.view || (\r\n <div className=\"selection-viewer-default-error\">\r\n {errorProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n ) : null}\r\n </div>\r\n </Sheet.Content>\r\n </Sheet.Container>\r\n <Sheet.Backdrop onTap={backDrop ? onClose : undefined} />\r\n </Sheet>\r\n </SelectionViewerAggregateContext.Provider>\r\n );\r\n};\r\n\r\n// ==================== Controller Hook ====================\r\ntype Operation = {\r\n open: () => void;\r\n close: () => void;\r\n toggle: () => void;\r\n setSelectionState: (val: SelectionState) => void;\r\n};\r\n\r\nconst useSelectionController = (initialSelectionState?: SelectionState): [\r\n string,\r\n Operation,\r\n boolean,\r\n SelectionState\r\n] => {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [selectionState, setSelectionState] = useState(initialSelectionState || 'initial');\r\n const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);\r\n\r\n const operations = useMemo<Operation>(() => ({\r\n open: () => setIsOpen(true),\r\n close: () => setIsOpen(false),\r\n toggle: () => setIsOpen((prev) => !prev),\r\n setSelectionState\r\n }), [setSelectionState]);\r\n\r\n return [selectionId, operations, isOpen, selectionState];\r\n};\r\n\r\nexport { SelectionViewer, useSelectionController, RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState };\r\nexport type {\r\n SelectionViewerProps,\r\n SelectionState,\r\n TitleProps,\r\n SearchProps,\r\n LoadingProps,\r\n NoResultProps,\r\n ErrorProps,\r\n CancelButtonProps,\r\n LayoutProps,\r\n Padding,\r\n SnapPoint,\r\n AggregateReporter,\r\n};\r\nexport default SelectionViewer;\r\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { RowAggregateContext } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type RowProps = {\n id?: string;\n /** Consumer-owned — SelectionViewer has never owned fetch/filter state, so unlike\n * search-viewer's Row this isn't internally computed. Mirrors how `selectionState`\n * is already a prop on SelectionViewer itself today. Defaults to \"data\": if you\n * render children without wiring this, we assume what you rendered IS the data. */\n state?: SelectionState;\n /** Called when this Row is scrolled horizontally near its end. Return (or resolve to)\n * `true` if more data may follow (keeps the pagination trigger armed), `false` once\n * there's nothing left to fetch — mirrors SelectionViewer's own `onPaginate` contract. */\n onPaginate?: () => boolean | Promise<boolean>;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A horizontally-scrolling, independently-paginating section — the Row/Column\n * equivalent for `SelectionViewer`. No shared code with `@academix-admin/search-viewer`'s\n * Row (Library Charter: packages stay mutually independent) — this one has no\n * query/debounce/cache engine of its own since `SelectionViewer` never had one either;\n * the consumer owns fetching/filtering and just tells this Row what `state` it's in. */\nfunction Row({ id: providedId, state = \"data\", onPaginate, className, style, children }: RowProps) {\n const aggregateReporter = React.useContext(RowAggregateContext);\n const [rowId] = useState(() => providedId || `row-${Math.random().toString(36).substring(2, 11)}`);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const isPaginating = useRef(false);\n\n const handleScroll = useCallback(async () => {\n const el = containerRef.current;\n if (!el || isPaginating.current || !onPaginate) return;\n\n // Same \"near the end\" threshold as SelectionViewer's own vertical pagination trigger, adapted\n // to horizontal scroll metrics since a Row scrolls sideways.\n const { scrollLeft, scrollWidth, clientWidth } = el;\n if (scrollWidth - scrollLeft > clientWidth * 1.2) return;\n\n isPaginating.current = true;\n const hasMore = await onPaginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, [onPaginate]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el || !onPaginate) return;\n el.addEventListener(\"scroll\", handleScroll);\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll, onPaginate]);\n\n useEffect(() => {\n aggregateReporter?.report(rowId, state);\n return () => aggregateReporter?.unreport(rowId);\n }, [aggregateReporter, rowId, state]);\n\n return (\n <div\n ref={containerRef}\n className={className ? `selection-viewer-row ${className}` : \"selection-viewer-row\"}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n overflowX: \"auto\",\n overflowY: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n\nRow.displayName = \"Row\";\n\nexport { Row };\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type ColumnProps = {\n id?: string;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A vertical arrangement of `Row`s (or other content) — the explicit, composable\n * equivalent of today's flat `children` list. Its default layout is deliberately\n * identical to the existing `.selection-viewer-content.vertical` rendering\n * (`flex-direction: column, gap: 8px`), so today's flat usage is, unchanged, \"an\n * implicit Column with no Rows.\"\n *\n * Provides the `RowAggregateContext` its `Row` children report into, computes ONE\n * cumulative state from everything reported, and is itself a reporter one level up:\n * nested inside another `Column` it reports through that Column's `RowAggregateContext`\n * (behaving like a Row); at the top level it reports through `SelectionViewerAggregateContext`,\n * which `SelectionViewer` provides — this drives the outer sheet's loading/empty/error\n * views automatically with zero manual wiring, and lets Columns nest arbitrarily deep. */\nfunction Column({ id: providedId, className, style, children }: ColumnProps) {\n const [columnId] = useState(\n () => providedId || `column-${Math.random().toString(36).substring(2, 11)}`\n );\n\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const report = useCallback((id: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(id, state);\n return next;\n });\n }, []);\n const unreport = useCallback((id: string) => {\n setReportedStates((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n const rowAggregateValue = useMemo(() => ({ report, unreport }), [report, unreport]);\n\n const aggregateState =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : \"initial\";\n\n const parentRowAggregate = React.useContext(RowAggregateContext);\n const selectionViewerAggregate = React.useContext(SelectionViewerAggregateContext);\n const upstreamReporter = parentRowAggregate ?? selectionViewerAggregate;\n\n useEffect(() => {\n if (!upstreamReporter) return;\n upstreamReporter.report(columnId, aggregateState);\n return () => upstreamReporter.unreport(columnId);\n }, [upstreamReporter, columnId, aggregateState]);\n\n return (\n <RowAggregateContext.Provider value={rowAggregateValue}>\n <div\n className={className ? `selection-viewer-column ${className}` : \"selection-viewer-column\"}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\", ...style }}\n >\n {children}\n </div>\n </RowAggregateContext.Provider>\n );\n}\n\nColumn.displayName = \"Column\";\n\nexport { Column };\n"]}
1
+ {"version":3,"sources":["../src/SelectionViewer.tsx","../src/Row.tsx","../src/Column.tsx"],"names":["React","useEffect","useOverlayRoute","useState","useRef","useCallback","useMemo","jsx","jsxs","Sheet","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport { Sheet } from \"@academix-admin/modal-sheet\";\nimport { useOverlayRoute } from '@academix-admin/overlay-route';\n\n// ==================== Aggregation (Row/Column) ====================\n// Independent from @academix-admin/search-viewer's equivalent (Library Charter: packages stay\n// mutually independent) — same shape, duplicated intentionally rather than shared.\n\ntype AggregateReporter = {\n report: (id: string, state: SelectionState) => void;\n unreport: (id: string) => void;\n};\n\n/** A `Row` reports its own `state` into the nearest `Column` through this. */\nconst RowAggregateContext = React.createContext<AggregateReporter | null>(null);\n\n/** `Column` reports its own aggregated state up through this — `SelectionViewer` provides it and\n * merges whatever's reported into its own selectionState resolution, with zero extra wiring. */\nconst SelectionViewerAggregateContext = React.createContext<AggregateReporter | null>(null);\n\nfunction computeAggregateState(states: SelectionState[]): SelectionState {\n if (states.length === 0) return \"initial\";\n if (states.some((s) => s === \"data\")) return \"data\";\n if (states.some((s) => s === \"loading\")) return \"loading\";\n if (states.every((s) => s === \"empty\")) return \"empty\";\n if (states.some((s) => s === \"error\")) return \"error\";\n return \"initial\";\n}\n\n// ==================== Types ====================\ntype Padding = {\n l: string;\n r: string;\n t: string;\n b: string;\n};\n\ntype SnapPoint = number;\n\ntype SelectionState = \"loading\" | \"empty\" | \"error\" | \"data\" | \"initial\";\n\n\ntype TitleProps = {\n text: string;\n textColor: string;\n className?: string;\n containerClass?: string;\n style?: React.CSSProperties;\n};\n\ntype SearchProps = {\n text: string;\n textColor: string;\n className?: string;\n background?: string;\n prefixIcon?: React.ReactNode;\n suffixIcon?: React.ReactNode;\n clearIcon?: React.ReactNode;\n backIcon?: React.ReactNode;\n prefixGap?: string;\n suffixGap?: string;\n padding?: Padding;\n autoFocus?: boolean;\n inputStyle?: React.CSSProperties;\n containerStyle?: React.CSSProperties;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n};\n\ntype LoadingProps = {\n view: React.ReactNode;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype NoResultProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype ErrorProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype CancelButtonProps = {\n text?: string;\n view: React.ReactNode;\n position?: \"left\" | \"right\";\n style?: React.CSSProperties;\n onClick?: () => void;\n /**\n * What a screen reader calls it. Defaults to \"Close\".\n *\n * A button whose only content is an icon has no accessible name at all, so it was announced as\n * just \"button\" — the one control that dismisses the sheet, and nothing said so.\n */\n ariaLabel?: string;\n};\n\ntype LayoutProps = {\n gapBetweenHandleAndTitle?: string;\n gapBetweenTitleAndSearch?: string;\n gapBetweenSearchAndContent?: string;\n /**\n * How far the rows are set in from the sides. Defaults to `16px`, matching the search box.\n *\n * The content had no inset while everything above it had 16px, so a list ran flush into the\n * sides of the screen under a search box that did not. Pass `0px` for rows that should reach\n * the edges.\n */\n contentPadding?: string;\n backgroundColor?: string;\n handleColor?: string;\n handleWidth?: string;\n fullScreenSearchBackground?: string;\n fullScreenSearchHeaderStyle?: React.CSSProperties;\n maxWidth?: string;\n};\n\ntype SelectionViewerProps = {\n id?: string;\n isOpen: boolean;\n backDrop?: boolean;\n onClose: () => void;\n /**\n * Give this overlay its own history entry under this name, so the platform's own Back (and the\n * iOS edge-swipe, and Android's gesture) closes the OVERLAY rather than the page under it.\n *\n * Off by default, because it changes what the back gesture does and no existing consumer asked\n * for that. Without it, Back with this open closes it AND leaves the page behind it: measured in\n * a real shop, the gesture walked straight out of a half-built sale.\n *\n * The name must be unique among overlays that can be open together, or they mistake each other's\n * entry. Make it STABLE across page loads and the overlay can also come back after a refresh —\n * see `useOverlayRoute` in `@academix-admin/overlay-route`, which this uses and which any\n * component can use directly.\n */\n historyRoute?: string;\n titleProp: TitleProps;\n searchProp?: SearchProps;\n loadingProp?: LoadingProps;\n noResultProp?: NoResultProps;\n errorProp?: ErrorProps;\n cancelButton?: CancelButtonProps;\n layoutProp?: LayoutProps;\n childrenDirection?: \"vertical\" | \"horizontal\";\n children?: React.ReactNode;\n onPaginate?: () => boolean | Promise<boolean>;\n snapPoints?: SnapPoint[];\n initialSnap?: number;\n unmountOnClose?: boolean;\n zIndex?: number;\n /**\n * Accessible name for the sheet, announced when it opens.\n *\n * A modal with no name is announced simply as \"dialog\", which tells a screen-reader user that\n * something has taken over the screen and nothing at all about what.\n */\n ariaLabel?: string;\n maxHeight?: string;\n minHeight?: string;\n selectionState?: SelectionState;\n closeThreshold?: number;\n};\n\n// ==================== Styles ====================\nconst getStyles = (id: string) => `\n#${id} .selection-viewer-header {\n padding: 0px;\n display: flex;\n flex-direction: column;\n align-items: center;\n position: relative;\n width: 100%;\n}\n\n/*\n * The close button, with a target a thumb can find.\n *\n * It sat at right: 0, top: 8 with no padding at all, so the icon was flush against the sheet's\n * edge and the only thing that could be tapped was the glyph itself — a target well under half\n * what a finger needs, in the corner of the screen where a thumb is least accurate. Inset by 4px\n * and padded out to 44px square, so the icon still reads as sitting in the corner while the\n * hittable area reaches the edges.\n *\n * The cancelButton.style prop is still spread last at the call site, so a consumer that was\n * overriding any of this keeps overriding it. (No backticks in here: this whole block lives\n * inside a template literal, and one would end the string.)\n */\n#${id} .selection-viewer-cancel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 44px;\n min-height: 44px;\n padding: 10px;\n border: none;\n background-color: transparent;\n color: inherit;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n#${id} .selection-viewer-drag-handle {\n height: 5px;\n border-radius: 3px;\n margin-top: 16px;\n}\n\n#${id} .selection-viewer-container {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n#${id} .selection-viewer-title {\n margin: 0px;\n font-size: 18px;\n font-weight: 600;\n padding: 0px 16px 0px 16px;\n word-break: break-word; /* break long words */\n white-space: normal; /* allow wrapping */\n}\n\n#${id} .selection-viewer-search {\n display: flex;\n align-items: center;\n margin: 16px 16px 4px 16px;\n border-radius: 12px;\n box-sizing: border-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n#${id} .selection-viewer-search-input {\n flex: 1;\n border: none;\n background: transparent;\n padding: 12px 8px;\n outline: none;\n font-size: 16px !important;\n width: 100%;\n}\n\n/*\n * The rows, set in from the edges like everything else in the sheet.\n *\n * The title and the search box have carried a 16px margin from the start; the content had none, so\n * a list of cards ran flush into the sides of the screen while the search box above it did not.\n * The layoutProp.contentPadding option overrides it for a consumer that wants its rows edge to\n * edge. (No backticks: this block is inside a template literal.)\n */\n#${id} .selection-viewer-content {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n min-height: 0;\n box-sizing: border-box;\n}\n\n#${id} .selection-viewer-content.vertical {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#${id} .selection-viewer-content.horizontal {\n display: flex;\n flex-direction: row;\n gap: 8px;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n#${id} .selection-viewer-no-results,\n#${id} .selection-viewer-error,\n#${id} .selection-viewer-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 120px;\n}\n\n#${id} .selection-viewer-default-no-results {\n color: #666;\n font-size: 14px;\n}\n\n#${id} .selection-viewer-default-error {\n color: red;\n font-size: 14px;\n}\n\n/* Full screen search mode */\n#${id} .selection-viewer-fullscreen-search {\n// position: fixed;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n// padding-top: env(safe-area-inset-top);\n width: 100%;\n}\n\n#${id} .selection-viewer-search-back-button {\n background: none;\n border: none;\n padding: 8px;\n margin-right: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-back-button:hover {\n opacity: 0.7;\n}\n\n#${id} .selection-viewer-search-clear-button {\n background: none;\n border: none;\n padding: 8px;\n margin-left: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-clear-button:hover {\n opacity: 0.7;\n}\n\n/* React Modal Sheet overrides */\n#${id} .react-modal-sheet-container {\n max-width: 500px;\n margin: 0 auto;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n}\n\n#${id} .react-modal-sheet-backdrop {\n background-color: rgba(0, 0, 0, 0.5) !important;\n pointer-events: auto !important;\n}\n\n#${id} .react-modal-sheet-content {\n padding: 0 0px 0px 0px;\n height: 100%;\n}\n\n\n/* Mobile full-width behavior */\n@media (max-width: 500px) {\n #${id} .react-modal-sheet-container {\n max-width: 100%;\n border-radius: 0;\n }\n}\n\n/* Prevent iOS zooming */\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n #${id} .selection-viewer-search-input {\n font-size: 16px !important;\n }\n}\n`;\n\n// ==================== Hook to inject CSS per instance ====================\nconst useInjectStyles = (id: string) => {\n useEffect(() => {\n const styleId = `selection-viewer-styles-${id}`;\n let styleTag = document.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleTag) {\n styleTag = document.createElement(\"style\");\n styleTag.id = styleId;\n styleTag.innerHTML = getStyles(id);\n document.head.appendChild(styleTag);\n }\n\n // Cleanup on unmount\n return () => {\n if (styleTag && document.head.contains(styleTag)) {\n document.head.removeChild(styleTag);\n }\n };\n }, [id]);\n};\n\n// ==================== Component ====================\n/**\n * Escape closes the viewer, and Tab stays inside it.\n *\n * The viewer covers the screen, so what is behind it is not available — but nothing said so to the\n * keyboard. Tab walked out of the option list into the page underneath, where a person was\n * operating controls they could not see, and Escape did nothing at all, which is the one key\n * everybody reaches for to get out of a picker.\n *\n * Deliberately a private copy rather than an import from a sibling package: every library here\n * stands alone, and a shared helper between two of them is a dependency the consumer did not ask\n * for. Takes the container's `id` rather than a ref, because the sheet element belongs to the\n * underlying modal-sheet component; looking it up when a key is pressed finds it however that\n * component chooses to mount it. Focusables are re-queried on every Tab, since the option list\n * changes as the consumer filters it and a list captured on open is a list of dead elements.\n */\nconst useModalKeys = (isOpen: boolean, onClose: () => void, containerId: string) => {\n useEffect(() => {\n if (!isOpen || typeof document === \"undefined\") return;\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n onClose();\n return;\n }\n if (e.key !== \"Tab\") return;\n\n const focusable = document.getElementById(containerId)?.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]),' +\n ' textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n );\n if (!focusable || focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [isOpen, onClose, containerId]);\n};\n\nconst SelectionViewer: React.FC<SelectionViewerProps> = ({\n id: providedId,\n isOpen,\n historyRoute,\n backDrop = true,\n onClose,\n titleProp,\n searchProp,\n loadingProp,\n noResultProp,\n errorProp,\n cancelButton,\n layoutProp,\n childrenDirection = \"vertical\",\n children,\n onPaginate,\n snapPoints = [0, 1],\n initialSnap = 1,\n unmountOnClose = true,\n zIndex = 1000,\n ariaLabel,\n maxHeight = \"90dvh\",\n minHeight = \"65dvh\",\n selectionState: selectionStateProp = \"initial\",\n closeThreshold = 0.2,\n}) => {\n\n /*\n * THE BACK GESTURE CLOSES THIS, NOT THE PAGE UNDER IT.\n *\n * Only when the consumer named a route: `useOverlayRoute` is a no-op with an empty name, and an\n * overlay that never asked for a history entry must not acquire one, or Back starts meaning\n * something different in an app that did not opt in.\n *\n * The capability lives in its own package rather than here, so that an app wanting a\n * back-closable sheet does not have to take a router with it — and so that a navigation library\n * can supply the history writing without either package importing the other.\n */\n useOverlayRoute(historyRoute ?? '', Boolean(historyRoute) && isOpen, onClose);\n const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);\n const [searchValue, setSearchValue] = useState(\"\");\n const [isSearchFocused, setIsSearchFocused] = useState(false);\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n const [inputKey, setInputKey] = useState(0);\n const [shouldAutoFocus, setShouldAutoFocus] = useState(false);\n const isPaginating = useRef(false);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const sheetRef = useRef<any>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n\n useInjectStyles(id);\n // Escape closes the viewer and Tab stays inside it — see useModalKeys.\n useModalKeys(isOpen, onClose, id);\n\n // Descendant sections (a Column, or anything else) can report a cumulative state up through this —\n // starts empty, so a plain SelectionViewer with no Row/Column children behaves exactly as before.\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const reportAggregate = useCallback((reporterId: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(reporterId, state);\n return next;\n });\n }, []);\n const unreportAggregate = useCallback((reporterId: string) => {\n setReportedStates((prev) => {\n if (!prev.has(reporterId)) return prev;\n const next = new Map(prev);\n next.delete(reporterId);\n return next;\n });\n }, []);\n const aggregateReporterValue = useMemo(\n () => ({ report: reportAggregate, unreport: unreportAggregate }),\n [reportAggregate, unreportAggregate]\n );\n const reportedAggregate =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : null;\n const isComposed = reportedAggregate !== null;\n\n // Precedence: an explicit `selectionState` prop always wins (matches today's exact behaviour for\n // any consumer not using Row/Column) -> then a reported Column aggregate, if one exists -> \"initial\".\n const selectionState =\n selectionStateProp !== \"initial\" ? selectionStateProp : reportedAggregate ?? \"initial\";\n\n // Track keyboard height\n useEffect(() => {\n if (typeof window === 'undefined' || !window.visualViewport) return;\n\n const handleResize = () => {\n const vp = window.visualViewport!;\n const kbHeight = window.innerHeight - vp.height;\n setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);\n };\n\n window.visualViewport.addEventListener('resize', handleResize);\n window.visualViewport.addEventListener('scroll', handleResize);\n\n return () => {\n window.visualViewport?.removeEventListener('resize', handleResize);\n window.visualViewport?.removeEventListener('scroll', handleResize);\n };\n }, []);\n\n // Auto-focus on search\n useEffect(() => {\n if (!isOpen) {\n searchInputRef.current?.blur();\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n setShouldAutoFocus(false);\n return;\n }\n }, [isOpen, searchProp?.autoFocus]);\n\n const handleOpenEnd = useCallback(() => {\n if (searchProp?.autoFocus && searchInputRef.current) {\n setInputKey(prev => prev + 1);\n setShouldAutoFocus(true);\n searchInputRef.current.focus();\n searchInputRef.current.click();\n }\n }, [searchProp?.autoFocus]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n setSearchValue(value);\n searchProp?.onChange?.(value);\n };\n\n const handleSearchFocus = () => {\n setIsSearchFocused(true);\n searchProp?.onFocus?.();\n requestAnimationFrame(() => {\n searchInputRef.current?.focus();\n });\n };\n\n const handleSearchBlur = () => {\n searchProp?.onBlur?.();\n };\n\n const handleBackFromSearch = () => {\n setIsSearchFocused(false);\n if (searchInputRef.current) {\n searchInputRef.current.blur();\n }\n };\n\n const clearSearch = () => {\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n if (searchInputRef.current) {\n searchInputRef.current.focus();\n }\n };\n\n /*\n * PAGINATION LISTENS TO WHATEVER ACTUALLY SCROLLS.\n *\n * It listened to `.selection-viewer-content` alone. Inside the sheet that element is not the\n * scroller: react-modal-sheet wraps content in its own `.react-modal-sheet-content-scroller`, the\n * content grows to its full height (4,601px for a hundred rows, measured) and the WRAPPER scrolls.\n * The content box never fired a scroll event, so `onPaginate` was never called — every consumer's\n * list stopped at its first page with nothing to say it had.\n *\n * So the nearest scrolling ancestor is found and listened to, and the content itself as well (for\n * a layout where it is the scroller). And the check also runs when the content changes size: a\n * first page too short to scroll can never produce a scroll event, but it is plainly \"at the\n * bottom\", so it asks for the next one straight away.\n */\n const onPaginateRef = useRef(onPaginate);\n onPaginateRef.current = onPaginate;\n const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null);\n\n const checkNearBottom = useCallback(async (el: HTMLElement) => {\n const paginate = onPaginateRef.current;\n if (isPaginating.current || !paginate) return;\n\n const { scrollTop, scrollHeight, clientHeight } = el;\n const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;\n if (!isNearBottom) return;\n\n isPaginating.current = true;\n const hasMore = await paginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, []);\n\n const hasPaginate = Boolean(onPaginate);\n useEffect(() => {\n if (!contentEl || !hasPaginate) return;\n const scroller =\n (contentEl.closest(\".react-modal-sheet-content-scroller\") as HTMLElement | null) ?? contentEl;\n const onScroll = () => void checkNearBottom(scroller);\n const onInnerScroll = () => void checkNearBottom(contentEl);\n\n scroller.addEventListener(\"scroll\", onScroll, { passive: true });\n if (scroller !== contentEl) contentEl.addEventListener(\"scroll\", onInnerScroll, { passive: true });\n\n const resize =\n typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(() => onScroll()) : null;\n resize?.observe(contentEl);\n\n return () => {\n scroller.removeEventListener(\"scroll\", onScroll);\n contentEl.removeEventListener(\"scroll\", onInnerScroll);\n resize?.disconnect();\n };\n }, [contentEl, hasPaginate, checkNearBottom]);\n\n if (!isOpen && unmountOnClose) return null;\n\n return (\n <SelectionViewerAggregateContext.Provider value={aggregateReporterValue}>\n <Sheet\n ref={sheetRef}\n isOpen={isOpen}\n onClose={onClose}\n detent=\"content\"\n style={{ zIndex }}\n maxHeight={maxHeight}\n onOpenEnd={handleOpenEnd}\n >\n <Sheet.Container\n id={id}\n // Announced as a modal dialog. Without this a screen reader reads the sheet as one more\n // region of the page and gives no indication that what is behind it is unavailable — and\n // an automated check for an open dialog finds nothing at all.\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={ariaLabel}\n style={{\n maxHeight: maxHeight,\n minHeight: isSearchFocused ? \"100dvh\" : minHeight,\n maxWidth: layoutProp?.maxWidth ?? '800px',\n margin: \"0 auto\",\n width: \"100%\",\n left: 0,\n right: 0,\n paddingBottom: \"calc(0px + env(safe-area-inset-bottom))\",\n background: isSearchFocused\n ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor\n : layoutProp?.backgroundColor,\n borderTopLeftRadius: isSearchFocused ? \"0px\" : \"16px\",\n borderTopRightRadius: isSearchFocused ? \"0px\" : \"16px\",\n }}\n >\n {isSearchFocused ? (\n <Sheet.Header>\n <div\n className=\"selection-viewer-fullscreen-search\"\n style={layoutProp?.fullScreenSearchHeaderStyle}\n >\n <div className=\"selection-viewer-search\" style={{\n ...searchProp?.containerStyle,\n background: searchProp?.background,\n padding: searchProp?.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"16px\"\n }}>\n <button\n className=\"selection-viewer-search-back-button\"\n onClick={handleBackFromSearch}\n aria-label=\"Exit search mode\"\n >\n {searchProp?.backIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M15 18L9 12L15 6\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp?.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp?.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp?.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n >\n {searchProp?.clearIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n </div>\n </div>\n </Sheet.Header>\n ) : (\n <Sheet.Header>\n <div className=\"selection-viewer-header\">\n {/* Cancel button left */}\n {cancelButton?.position === \"left\" && (\n <button\n aria-label={cancelButton.ariaLabel || \"Close\"}\n className=\"selection-viewer-cancel\"\n style={{\n position: \"absolute\",\n left: \"4px\",\n top: \"4px\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Drag handle */}\n {!isSearchFocused && (\n <div\n className=\"selection-viewer-drag-handle\"\n style={{\n background: layoutProp?.handleColor || \"#ccc\",\n width: layoutProp?.handleWidth || \"40px\",\n marginBottom: layoutProp?.gapBetweenHandleAndTitle || \"12px\",\n }}\n />\n )}\n\n {/* Cancel button right */}\n {cancelButton?.position === \"right\" && (\n <button\n aria-label={cancelButton.ariaLabel || \"Close\"}\n className=\"selection-viewer-cancel\"\n style={{\n position: \"absolute\",\n right: \"4px\",\n top: \"4px\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Title */}\n <div className={titleProp.containerClass || \"selection-viewer-container\"}>\n <h2\n id={`${id}-title`}\n className={titleProp.className || \"selection-viewer-title\"}\n style={{\n marginBottom: layoutProp?.gapBetweenTitleAndSearch || \"8px\",\n color: titleProp?.textColor || 'black',\n ...titleProp.style\n }}\n >\n {titleProp.text}\n </h2>\n </div>\n </div>\n\n {/* Search */}\n {searchProp && (\n <div\n className=\"selection-viewer-search\"\n style={{\n background: searchProp.background,\n padding: searchProp.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"8px 16px\",\n marginBottom: layoutProp?.gapBetweenSearchAndContent,\n ...searchProp.containerStyle\n }}\n >\n {searchProp.prefixIcon && (\n <span style={{ marginRight: searchProp.prefixGap || \"8px\" }}>\n {searchProp.prefixIcon}\n </span>\n )}\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n style={{ marginLeft: searchProp.suffixGap || \"8px\" }}\n >\n {searchProp.clearIcon || searchProp.suffixIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n {!searchValue && searchProp.suffixIcon && (\n <span style={{ marginLeft: searchProp.suffixGap || \"8px\" }}>\n {searchProp.suffixIcon}\n </span>\n )}\n </div>\n )}\n </Sheet.Header>\n )}\n\n <Sheet.Content>\n <div\n ref={setContentEl}\n className={`selection-viewer-content ${childrenDirection}`}\n style={{\n paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : '0',\n paddingBottom: keyboardHeight > 0\n ? `${keyboardHeight + 16}px`\n : '16px',\n /*\n * Set in from the edges, like everything else in the sheet.\n *\n * The title and the search box have carried 16px from the start and the content had\n * none, so a list of rows ran flush into the sides of the screen under a search box\n * that did not. Overridable for rows that genuinely want the full width.\n */\n paddingLeft: layoutProp?.contentPadding ?? '16px',\n paddingRight: layoutProp?.contentPadding ?? '16px',\n }}\n >\n {/* Show children first, then loading at bottom */}\n {React.Children.count(children) > 0 ? (\n <>\n {children}\n {selectionState === \"loading\" && (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n )}\n {/*\n Shown ALONGSIDE the children, not instead of them.\n\n This was guarded on composed mode — a Column/Row descendant reporting its own\n state — on the reasoning that only a composed sheet can have children and be\n empty at the same time. That is not true of the commonest arrangement there is:\n a consumer that wraps its rows in a single container. `React.Children.count` is\n then 1 whether the container holds fifty rows or none, so the empty branch below\n never ran and the sheet showed a blank white screen — at the exact moment\n somebody had searched for something that is not there, which is when the empty\n state is the only useful thing on screen.\n\n Layered rather than substituted, for the reason the composed case already had:\n a child that reports its own state must stay mounted to go on reporting. A\n consumer whose `selectionState` says \"empty\" is asserting there is nothing to\n show, so drawing its own view beside visually-empty children is what it asked\n for.\n */}\n {selectionState === \"empty\" && (\n <div\n className=\"selection-viewer-no-results\"\n style={{\n padding: noResultProp?.padding\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\n : \"16px\",\n ...noResultProp?.style\n }}\n >\n {noResultProp?.view || (\n <div className=\"selection-viewer-default-no-results\">\n {noResultProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n )}\n {selectionState === \"error\" && (\n <div\n className=\"selection-viewer-error\"\n style={{\n padding: errorProp?.padding\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\n : \"16px\",\n ...errorProp?.style\n }}\n >\n {errorProp?.view || (\n <div className=\"selection-viewer-default-error\">\n {errorProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n )}\n </>\n ) : (selectionState === \"empty\" || (React.Children.count(children) <= 0 && (selectionState != \"loading\" && selectionState != \"error\"))) ? (\n <div\n className=\"selection-viewer-no-results\"\n style={{\n padding: noResultProp?.padding\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\n : \"16px\",\n ...noResultProp?.style\n }}\n >\n {noResultProp?.view || (\n <div className=\"selection-viewer-default-no-results\">\n {noResultProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : selectionState === \"loading\" ? (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n ) : (selectionState === \"error\") ? (\n <div\n className=\"selection-viewer-error\"\n style={{\n padding: errorProp?.padding\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\n : \"16px\",\n ...errorProp?.style\n }}\n >\n {errorProp?.view || (\n <div className=\"selection-viewer-default-error\">\n {errorProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : null}\n </div>\n </Sheet.Content>\n </Sheet.Container>\n <Sheet.Backdrop onTap={backDrop ? onClose : undefined} />\n </Sheet>\n </SelectionViewerAggregateContext.Provider>\n );\n};\n\n// ==================== Controller Hook ====================\ntype Operation = {\n open: () => void;\n close: () => void;\n toggle: () => void;\n setSelectionState: (val: SelectionState) => void;\n};\n\nconst useSelectionController = (initialSelectionState?: SelectionState): [\n string,\n Operation,\n boolean,\n SelectionState\n] => {\n const [isOpen, setIsOpen] = useState(false);\n const [selectionState, setSelectionState] = useState(initialSelectionState || 'initial');\n const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);\n\n const operations = useMemo<Operation>(() => ({\n open: () => setIsOpen(true),\n close: () => setIsOpen(false),\n toggle: () => setIsOpen((prev) => !prev),\n setSelectionState\n }), [setSelectionState]);\n\n return [selectionId, operations, isOpen, selectionState];\n};\n\nexport { SelectionViewer, useSelectionController, RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState };\nexport type {\n SelectionViewerProps,\n SelectionState,\n TitleProps,\n SearchProps,\n LoadingProps,\n NoResultProps,\n ErrorProps,\n CancelButtonProps,\n LayoutProps,\n Padding,\n SnapPoint,\n AggregateReporter,\n};\nexport default SelectionViewer;\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { RowAggregateContext } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type RowProps = {\n id?: string;\n /** Consumer-owned — SelectionViewer has never owned fetch/filter state, so unlike\n * search-viewer's Row this isn't internally computed. Mirrors how `selectionState`\n * is already a prop on SelectionViewer itself today. Defaults to \"data\": if you\n * render children without wiring this, we assume what you rendered IS the data. */\n state?: SelectionState;\n /** Called when this Row is scrolled horizontally near its end. Return (or resolve to)\n * `true` if more data may follow (keeps the pagination trigger armed), `false` once\n * there's nothing left to fetch — mirrors SelectionViewer's own `onPaginate` contract. */\n onPaginate?: () => boolean | Promise<boolean>;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A horizontally-scrolling, independently-paginating section — the Row/Column\n * equivalent for `SelectionViewer`. No shared code with `@academix-admin/search-viewer`'s\n * Row (Library Charter: packages stay mutually independent) — this one has no\n * query/debounce/cache engine of its own since `SelectionViewer` never had one either;\n * the consumer owns fetching/filtering and just tells this Row what `state` it's in. */\nfunction Row({ id: providedId, state = \"data\", onPaginate, className, style, children }: RowProps) {\n const aggregateReporter = React.useContext(RowAggregateContext);\n const [rowId] = useState(() => providedId || `row-${Math.random().toString(36).substring(2, 11)}`);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const isPaginating = useRef(false);\n\n const handleScroll = useCallback(async () => {\n const el = containerRef.current;\n if (!el || isPaginating.current || !onPaginate) return;\n\n // Same \"near the end\" threshold as SelectionViewer's own vertical pagination trigger, adapted\n // to horizontal scroll metrics since a Row scrolls sideways.\n const { scrollLeft, scrollWidth, clientWidth } = el;\n if (scrollWidth - scrollLeft > clientWidth * 1.2) return;\n\n isPaginating.current = true;\n const hasMore = await onPaginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, [onPaginate]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el || !onPaginate) return;\n el.addEventListener(\"scroll\", handleScroll);\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll, onPaginate]);\n\n useEffect(() => {\n aggregateReporter?.report(rowId, state);\n return () => aggregateReporter?.unreport(rowId);\n }, [aggregateReporter, rowId, state]);\n\n return (\n <div\n ref={containerRef}\n className={className ? `selection-viewer-row ${className}` : \"selection-viewer-row\"}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n overflowX: \"auto\",\n overflowY: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n\nRow.displayName = \"Row\";\n\nexport { Row };\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type ColumnProps = {\n id?: string;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A vertical arrangement of `Row`s (or other content) — the explicit, composable\n * equivalent of today's flat `children` list. Its default layout is deliberately\n * identical to the existing `.selection-viewer-content.vertical` rendering\n * (`flex-direction: column, gap: 8px`), so today's flat usage is, unchanged, \"an\n * implicit Column with no Rows.\"\n *\n * Provides the `RowAggregateContext` its `Row` children report into, computes ONE\n * cumulative state from everything reported, and is itself a reporter one level up:\n * nested inside another `Column` it reports through that Column's `RowAggregateContext`\n * (behaving like a Row); at the top level it reports through `SelectionViewerAggregateContext`,\n * which `SelectionViewer` provides — this drives the outer sheet's loading/empty/error\n * views automatically with zero manual wiring, and lets Columns nest arbitrarily deep. */\nfunction Column({ id: providedId, className, style, children }: ColumnProps) {\n const [columnId] = useState(\n () => providedId || `column-${Math.random().toString(36).substring(2, 11)}`\n );\n\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const report = useCallback((id: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(id, state);\n return next;\n });\n }, []);\n const unreport = useCallback((id: string) => {\n setReportedStates((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n const rowAggregateValue = useMemo(() => ({ report, unreport }), [report, unreport]);\n\n const aggregateState =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : \"initial\";\n\n const parentRowAggregate = React.useContext(RowAggregateContext);\n const selectionViewerAggregate = React.useContext(SelectionViewerAggregateContext);\n const upstreamReporter = parentRowAggregate ?? selectionViewerAggregate;\n\n useEffect(() => {\n if (!upstreamReporter) return;\n upstreamReporter.report(columnId, aggregateState);\n return () => upstreamReporter.unreport(columnId);\n }, [upstreamReporter, columnId, aggregateState]);\n\n return (\n <RowAggregateContext.Provider value={rowAggregateValue}>\n <div\n className={className ? `selection-viewer-column ${className}` : \"selection-viewer-column\"}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\", ...style }}\n >\n {children}\n </div>\n </RowAggregateContext.Provider>\n );\n}\n\nColumn.displayName = \"Column\";\n\nexport { Column };\n"]}
package/dist/index.d.cts CHANGED
@@ -99,6 +99,20 @@ type SelectionViewerProps = {
99
99
  isOpen: boolean;
100
100
  backDrop?: boolean;
101
101
  onClose: () => void;
102
+ /**
103
+ * Give this overlay its own history entry under this name, so the platform's own Back (and the
104
+ * iOS edge-swipe, and Android's gesture) closes the OVERLAY rather than the page under it.
105
+ *
106
+ * Off by default, because it changes what the back gesture does and no existing consumer asked
107
+ * for that. Without it, Back with this open closes it AND leaves the page behind it: measured in
108
+ * a real shop, the gesture walked straight out of a half-built sale.
109
+ *
110
+ * The name must be unique among overlays that can be open together, or they mistake each other's
111
+ * entry. Make it STABLE across page loads and the overlay can also come back after a refresh —
112
+ * see `useOverlayRoute` in `@academix-admin/overlay-route`, which this uses and which any
113
+ * component can use directly.
114
+ */
115
+ historyRoute?: string;
102
116
  titleProp: TitleProps;
103
117
  searchProp?: SearchProps;
104
118
  loadingProp?: LoadingProps;
package/dist/index.d.ts CHANGED
@@ -99,6 +99,20 @@ type SelectionViewerProps = {
99
99
  isOpen: boolean;
100
100
  backDrop?: boolean;
101
101
  onClose: () => void;
102
+ /**
103
+ * Give this overlay its own history entry under this name, so the platform's own Back (and the
104
+ * iOS edge-swipe, and Android's gesture) closes the OVERLAY rather than the page under it.
105
+ *
106
+ * Off by default, because it changes what the back gesture does and no existing consumer asked
107
+ * for that. Without it, Back with this open closes it AND leaves the page behind it: measured in
108
+ * a real shop, the gesture walked straight out of a half-built sale.
109
+ *
110
+ * The name must be unique among overlays that can be open together, or they mistake each other's
111
+ * entry. Make it STABLE across page loads and the overlay can also come back after a refresh —
112
+ * see `useOverlayRoute` in `@academix-admin/overlay-route`, which this uses and which any
113
+ * component can use directly.
114
+ */
115
+ historyRoute?: string;
102
116
  titleProp: TitleProps;
103
117
  searchProp?: SearchProps;
104
118
  loadingProp?: LoadingProps;
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import React, { useState, useRef, useCallback, useMemo, useEffect } from 'react';
3
3
  import { Sheet } from '@academix-admin/modal-sheet';
4
+ import { useOverlayRoute } from '@academix-admin/overlay-route';
4
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
6
 
6
7
  // src/SelectionViewer.tsx
@@ -265,6 +266,7 @@ var useModalKeys = (isOpen, onClose, containerId) => {
265
266
  var SelectionViewer = ({
266
267
  id: providedId,
267
268
  isOpen,
269
+ historyRoute,
268
270
  backDrop = true,
269
271
  onClose,
270
272
  titleProp,
@@ -287,6 +289,7 @@ var SelectionViewer = ({
287
289
  selectionState: selectionStateProp = "initial",
288
290
  closeThreshold = 0.2
289
291
  }) => {
292
+ useOverlayRoute(historyRoute ?? "", Boolean(historyRoute) && isOpen, onClose);
290
293
  const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);
291
294
  const [searchValue, setSearchValue] = useState("");
292
295
  const [isSearchFocused, setIsSearchFocused] = useState(false);
@@ -380,25 +383,41 @@ var SelectionViewer = ({
380
383
  searchInputRef.current.focus();
381
384
  }
382
385
  };
383
- const handleScroll = useCallback(
384
- async (e) => {
385
- if (isPaginating.current || !onPaginate) return;
386
- const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
387
- const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;
388
- if (isNearBottom) {
389
- isPaginating.current = true;
390
- const hasMore = await onPaginate();
391
- if (!hasMore) {
392
- isPaginating.current = false;
393
- } else {
394
- setTimeout(() => {
395
- isPaginating.current = false;
396
- }, 500);
397
- }
398
- }
399
- },
400
- [onPaginate]
401
- );
386
+ const onPaginateRef = useRef(onPaginate);
387
+ onPaginateRef.current = onPaginate;
388
+ const [contentEl, setContentEl] = useState(null);
389
+ const checkNearBottom = useCallback(async (el) => {
390
+ const paginate = onPaginateRef.current;
391
+ if (isPaginating.current || !paginate) return;
392
+ const { scrollTop, scrollHeight, clientHeight } = el;
393
+ const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;
394
+ if (!isNearBottom) return;
395
+ isPaginating.current = true;
396
+ const hasMore = await paginate();
397
+ if (!hasMore) {
398
+ isPaginating.current = false;
399
+ } else {
400
+ setTimeout(() => {
401
+ isPaginating.current = false;
402
+ }, 500);
403
+ }
404
+ }, []);
405
+ const hasPaginate = Boolean(onPaginate);
406
+ useEffect(() => {
407
+ if (!contentEl || !hasPaginate) return;
408
+ const scroller = contentEl.closest(".react-modal-sheet-content-scroller") ?? contentEl;
409
+ const onScroll = () => void checkNearBottom(scroller);
410
+ const onInnerScroll = () => void checkNearBottom(contentEl);
411
+ scroller.addEventListener("scroll", onScroll, { passive: true });
412
+ if (scroller !== contentEl) contentEl.addEventListener("scroll", onInnerScroll, { passive: true });
413
+ const resize = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => onScroll()) : null;
414
+ resize?.observe(contentEl);
415
+ return () => {
416
+ scroller.removeEventListener("scroll", onScroll);
417
+ contentEl.removeEventListener("scroll", onInnerScroll);
418
+ resize?.disconnect();
419
+ };
420
+ }, [contentEl, hasPaginate, checkNearBottom]);
402
421
  if (!isOpen && unmountOnClose) return null;
403
422
  return /* @__PURE__ */ jsx(SelectionViewerAggregateContext.Provider, { value: aggregateReporterValue, children: /* @__PURE__ */ jsxs(
404
423
  Sheet,
@@ -587,8 +606,8 @@ var SelectionViewer = ({
587
606
  /* @__PURE__ */ jsx(Sheet.Content, { children: /* @__PURE__ */ jsx(
588
607
  "div",
589
608
  {
609
+ ref: setContentEl,
590
610
  className: `selection-viewer-content ${childrenDirection}`,
591
- onScroll: onPaginate ? handleScroll : void 0,
592
611
  style: {
593
612
  paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : "0",
594
613
  paddingBottom: keyboardHeight > 0 ? `${keyboardHeight + 16}px` : "16px",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SelectionViewer.tsx","../src/Row.tsx","../src/Column.tsx"],"names":["React","useState","useRef","useCallback","useEffect","jsx","useMemo"],"mappings":"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","file":"index.js","sourcesContent":["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\r\nimport { Sheet } from \"@academix-admin/modal-sheet\";\r\n\r\n// ==================== Aggregation (Row/Column) ====================\r\n// Independent from @academix-admin/search-viewer's equivalent (Library Charter: packages stay\r\n// mutually independent) — same shape, duplicated intentionally rather than shared.\r\n\r\ntype AggregateReporter = {\r\n report: (id: string, state: SelectionState) => void;\r\n unreport: (id: string) => void;\r\n};\r\n\r\n/** A `Row` reports its own `state` into the nearest `Column` through this. */\r\nconst RowAggregateContext = React.createContext<AggregateReporter | null>(null);\r\n\r\n/** `Column` reports its own aggregated state up through this — `SelectionViewer` provides it and\r\n * merges whatever's reported into its own selectionState resolution, with zero extra wiring. */\r\nconst SelectionViewerAggregateContext = React.createContext<AggregateReporter | null>(null);\r\n\r\nfunction computeAggregateState(states: SelectionState[]): SelectionState {\r\n if (states.length === 0) return \"initial\";\r\n if (states.some((s) => s === \"data\")) return \"data\";\r\n if (states.some((s) => s === \"loading\")) return \"loading\";\r\n if (states.every((s) => s === \"empty\")) return \"empty\";\r\n if (states.some((s) => s === \"error\")) return \"error\";\r\n return \"initial\";\r\n}\r\n\r\n// ==================== Types ====================\r\ntype Padding = {\r\n l: string;\r\n r: string;\r\n t: string;\r\n b: string;\r\n};\r\n\r\ntype SnapPoint = number;\r\n\r\ntype SelectionState = \"loading\" | \"empty\" | \"error\" | \"data\" | \"initial\";\r\n\r\n\r\ntype TitleProps = {\r\n text: string;\r\n textColor: string;\r\n className?: string;\r\n containerClass?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype SearchProps = {\r\n text: string;\r\n textColor: string;\r\n className?: string;\r\n background?: string;\r\n prefixIcon?: React.ReactNode;\r\n suffixIcon?: React.ReactNode;\r\n clearIcon?: React.ReactNode;\r\n backIcon?: React.ReactNode;\r\n prefixGap?: string;\r\n suffixGap?: string;\r\n padding?: Padding;\r\n autoFocus?: boolean;\r\n inputStyle?: React.CSSProperties;\r\n containerStyle?: React.CSSProperties;\r\n onChange?: (value: string) => void;\r\n onFocus?: () => void;\r\n onBlur?: () => void;\r\n};\r\n\r\ntype LoadingProps = {\r\n view: React.ReactNode;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype NoResultProps = {\r\n view: React.ReactNode;\r\n text?: string;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype ErrorProps = {\r\n view: React.ReactNode;\r\n text?: string;\r\n padding?: Padding;\r\n style?: React.CSSProperties;\r\n};\r\n\r\ntype CancelButtonProps = {\r\n text?: string;\r\n view: React.ReactNode;\r\n position?: \"left\" | \"right\";\r\n style?: React.CSSProperties;\r\n onClick?: () => void;\r\n /**\r\n * What a screen reader calls it. Defaults to \"Close\".\r\n *\r\n * A button whose only content is an icon has no accessible name at all, so it was announced as\r\n * just \"button\" — the one control that dismisses the sheet, and nothing said so.\r\n */\r\n ariaLabel?: string;\r\n};\r\n\r\ntype LayoutProps = {\r\n gapBetweenHandleAndTitle?: string;\r\n gapBetweenTitleAndSearch?: string;\r\n gapBetweenSearchAndContent?: string;\r\n /**\r\n * How far the rows are set in from the sides. Defaults to `16px`, matching the search box.\r\n *\r\n * The content had no inset while everything above it had 16px, so a list ran flush into the\r\n * sides of the screen under a search box that did not. Pass `0px` for rows that should reach\r\n * the edges.\r\n */\r\n contentPadding?: string;\r\n backgroundColor?: string;\r\n handleColor?: string;\r\n handleWidth?: string;\r\n fullScreenSearchBackground?: string;\r\n fullScreenSearchHeaderStyle?: React.CSSProperties;\r\n maxWidth?: string;\r\n};\r\n\r\ntype SelectionViewerProps = {\r\n id?: string;\r\n isOpen: boolean;\r\n backDrop?: boolean;\r\n onClose: () => void;\r\n titleProp: TitleProps;\r\n searchProp?: SearchProps;\r\n loadingProp?: LoadingProps;\r\n noResultProp?: NoResultProps;\r\n errorProp?: ErrorProps;\r\n cancelButton?: CancelButtonProps;\r\n layoutProp?: LayoutProps;\r\n childrenDirection?: \"vertical\" | \"horizontal\";\r\n children?: React.ReactNode;\r\n onPaginate?: () => boolean | Promise<boolean>;\r\n snapPoints?: SnapPoint[];\r\n initialSnap?: number;\r\n unmountOnClose?: boolean;\r\n zIndex?: number;\r\n /**\r\n * Accessible name for the sheet, announced when it opens.\r\n *\r\n * A modal with no name is announced simply as \"dialog\", which tells a screen-reader user that\r\n * something has taken over the screen and nothing at all about what.\r\n */\r\n ariaLabel?: string;\r\n maxHeight?: string;\r\n minHeight?: string;\r\n selectionState?: SelectionState;\r\n closeThreshold?: number;\r\n};\r\n\r\n// ==================== Styles ====================\r\nconst getStyles = (id: string) => `\r\n#${id} .selection-viewer-header {\r\n padding: 0px;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * The close button, with a target a thumb can find.\r\n *\r\n * It sat at right: 0, top: 8 with no padding at all, so the icon was flush against the sheet's\r\n * edge and the only thing that could be tapped was the glyph itself — a target well under half\r\n * what a finger needs, in the corner of the screen where a thumb is least accurate. Inset by 4px\r\n * and padded out to 44px square, so the icon still reads as sitting in the corner while the\r\n * hittable area reaches the edges.\r\n *\r\n * The cancelButton.style prop is still spread last at the call site, so a consumer that was\r\n * overriding any of this keeps overriding it. (No backticks in here: this whole block lives\r\n * inside a template literal, and one would end the string.)\r\n */\r\n#${id} .selection-viewer-cancel {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 44px;\r\n min-height: 44px;\r\n padding: 10px;\r\n border: none;\r\n background-color: transparent;\r\n color: inherit;\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n}\r\n\r\n#${id} .selection-viewer-drag-handle {\r\n height: 5px;\r\n border-radius: 3px;\r\n margin-top: 16px;\r\n}\r\n\r\n#${id} .selection-viewer-container {\r\n display: flex;\r\n align-items: flex-start;\r\n width: 100%;\r\n}\r\n\r\n#${id} .selection-viewer-title {\r\n margin: 0px;\r\n font-size: 18px;\r\n font-weight: 600;\r\n padding: 0px 16px 0px 16px;\r\n word-break: break-word; /* break long words */\r\n white-space: normal; /* allow wrapping */\r\n}\r\n\r\n#${id} .selection-viewer-search {\r\n display: flex;\r\n align-items: center;\r\n margin: 16px 16px 4px 16px;\r\n border-radius: 12px;\r\n box-sizing: border-box;\r\n border: 1px solid rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n#${id} .selection-viewer-search-input {\r\n flex: 1;\r\n border: none;\r\n background: transparent;\r\n padding: 12px 8px;\r\n outline: none;\r\n font-size: 16px !important;\r\n width: 100%;\r\n}\r\n\r\n/*\r\n * The rows, set in from the edges like everything else in the sheet.\r\n *\r\n * The title and the search box have carried a 16px margin from the start; the content had none, so\r\n * a list of cards ran flush into the sides of the screen while the search box above it did not.\r\n * The layoutProp.contentPadding option overrides it for a consumer that wants its rows edge to\r\n * edge. (No backticks: this block is inside a template literal.)\r\n */\r\n#${id} .selection-viewer-content {\r\n overflow-y: auto;\r\n -webkit-overflow-scrolling: touch;\r\n flex: 1;\r\n min-height: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#${id} .selection-viewer-content.vertical {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n#${id} .selection-viewer-content.horizontal {\r\n display: flex;\r\n flex-direction: row;\r\n gap: 8px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n}\r\n\r\n#${id} .selection-viewer-no-results,\r\n#${id} .selection-viewer-error,\r\n#${id} .selection-viewer-loading {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n min-height: 120px;\r\n}\r\n\r\n#${id} .selection-viewer-default-no-results {\r\n color: #666;\r\n font-size: 14px;\r\n}\r\n\r\n#${id} .selection-viewer-default-error {\r\n color: red;\r\n font-size: 14px;\r\n}\r\n\r\n/* Full screen search mode */\r\n#${id} .selection-viewer-fullscreen-search {\r\n// position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n z-index: 10;\r\n// padding-top: env(safe-area-inset-top);\r\n width: 100%;\r\n}\r\n\r\n#${id} .selection-viewer-search-back-button {\r\n background: none;\r\n border: none;\r\n padding: 8px;\r\n margin-right: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n transition: opacity 0.2s;\r\n}\r\n\r\n#${id} .selection-viewer-search-back-button:hover {\r\n opacity: 0.7;\r\n}\r\n\r\n#${id} .selection-viewer-search-clear-button {\r\n background: none;\r\n border: none;\r\n padding: 8px;\r\n margin-left: 4px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n transition: opacity 0.2s;\r\n}\r\n\r\n#${id} .selection-viewer-search-clear-button:hover {\r\n opacity: 0.7;\r\n}\r\n\r\n/* React Modal Sheet overrides */\r\n#${id} .react-modal-sheet-container {\r\n max-width: 500px;\r\n margin: 0 auto;\r\n border-top-left-radius: 16px;\r\n border-top-right-radius: 16px;\r\n}\r\n\r\n#${id} .react-modal-sheet-backdrop {\r\n background-color: rgba(0, 0, 0, 0.5) !important;\r\n pointer-events: auto !important;\r\n}\r\n\r\n#${id} .react-modal-sheet-content {\r\n padding: 0 0px 0px 0px;\r\n height: 100%;\r\n}\r\n\r\n\r\n/* Mobile full-width behavior */\r\n@media (max-width: 500px) {\r\n #${id} .react-modal-sheet-container {\r\n max-width: 100%;\r\n border-radius: 0;\r\n }\r\n}\r\n\r\n/* Prevent iOS zooming */\r\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\r\n #${id} .selection-viewer-search-input {\r\n font-size: 16px !important;\r\n }\r\n}\r\n`;\r\n\r\n// ==================== Hook to inject CSS per instance ====================\r\nconst useInjectStyles = (id: string) => {\r\n useEffect(() => {\r\n const styleId = `selection-viewer-styles-${id}`;\r\n let styleTag = document.getElementById(styleId) as HTMLStyleElement | null;\r\n\r\n if (!styleTag) {\r\n styleTag = document.createElement(\"style\");\r\n styleTag.id = styleId;\r\n styleTag.innerHTML = getStyles(id);\r\n document.head.appendChild(styleTag);\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (styleTag && document.head.contains(styleTag)) {\r\n document.head.removeChild(styleTag);\r\n }\r\n };\r\n }, [id]);\r\n};\r\n\r\n// ==================== Component ====================\r\n/**\r\n * Escape closes the viewer, and Tab stays inside it.\r\n *\r\n * The viewer covers the screen, so what is behind it is not available — but nothing said so to the\r\n * keyboard. Tab walked out of the option list into the page underneath, where a person was\r\n * operating controls they could not see, and Escape did nothing at all, which is the one key\r\n * everybody reaches for to get out of a picker.\r\n *\r\n * Deliberately a private copy rather than an import from a sibling package: every library here\r\n * stands alone, and a shared helper between two of them is a dependency the consumer did not ask\r\n * for. Takes the container's `id` rather than a ref, because the sheet element belongs to the\r\n * underlying modal-sheet component; looking it up when a key is pressed finds it however that\r\n * component chooses to mount it. Focusables are re-queried on every Tab, since the option list\r\n * changes as the consumer filters it and a list captured on open is a list of dead elements.\r\n */\r\nconst useModalKeys = (isOpen: boolean, onClose: () => void, containerId: string) => {\r\n useEffect(() => {\r\n if (!isOpen || typeof document === \"undefined\") return;\r\n\r\n const onKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n e.preventDefault();\r\n onClose();\r\n return;\r\n }\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = document.getElementById(containerId)?.querySelectorAll<HTMLElement>(\r\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]),' +\r\n ' textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (!focusable || focusable.length === 0) return;\r\n\r\n const first = focusable[0];\r\n const last = focusable[focusable.length - 1];\r\n\r\n if (e.shiftKey && document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n } else if (!e.shiftKey && document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", onKeyDown);\r\n return () => document.removeEventListener(\"keydown\", onKeyDown);\r\n }, [isOpen, onClose, containerId]);\r\n};\r\n\r\nconst SelectionViewer: React.FC<SelectionViewerProps> = ({\r\n id: providedId,\r\n isOpen,\r\n backDrop = true,\r\n onClose,\r\n titleProp,\r\n searchProp,\r\n loadingProp,\r\n noResultProp,\r\n errorProp,\r\n cancelButton,\r\n layoutProp,\r\n childrenDirection = \"vertical\",\r\n children,\r\n onPaginate,\r\n snapPoints = [0, 1],\r\n initialSnap = 1,\r\n unmountOnClose = true,\r\n zIndex = 1000,\r\n ariaLabel,\r\n maxHeight = \"90dvh\",\r\n minHeight = \"65dvh\",\r\n selectionState: selectionStateProp = \"initial\",\r\n closeThreshold = 0.2,\r\n}) => {\r\n const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);\r\n const [searchValue, setSearchValue] = useState(\"\");\r\n const [isSearchFocused, setIsSearchFocused] = useState(false);\r\n const [keyboardHeight, setKeyboardHeight] = useState(0);\r\n const [inputKey, setInputKey] = useState(0);\r\n const [shouldAutoFocus, setShouldAutoFocus] = useState(false);\r\n const isPaginating = useRef(false);\r\n const searchInputRef = useRef<HTMLInputElement>(null);\r\n const sheetRef = useRef<any>(null);\r\n const headerRef = useRef<HTMLDivElement>(null);\r\n\r\n useInjectStyles(id);\r\n // Escape closes the viewer and Tab stays inside it — see useModalKeys.\r\n useModalKeys(isOpen, onClose, id);\r\n\r\n // Descendant sections (a Column, or anything else) can report a cumulative state up through this —\r\n // starts empty, so a plain SelectionViewer with no Row/Column children behaves exactly as before.\r\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\r\n const reportAggregate = useCallback((reporterId: string, state: SelectionState) => {\r\n setReportedStates((prev) => {\r\n const next = new Map(prev);\r\n next.set(reporterId, state);\r\n return next;\r\n });\r\n }, []);\r\n const unreportAggregate = useCallback((reporterId: string) => {\r\n setReportedStates((prev) => {\r\n if (!prev.has(reporterId)) return prev;\r\n const next = new Map(prev);\r\n next.delete(reporterId);\r\n return next;\r\n });\r\n }, []);\r\n const aggregateReporterValue = useMemo(\r\n () => ({ report: reportAggregate, unreport: unreportAggregate }),\r\n [reportAggregate, unreportAggregate]\r\n );\r\n const reportedAggregate =\r\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : null;\r\n const isComposed = reportedAggregate !== null;\r\n\r\n // Precedence: an explicit `selectionState` prop always wins (matches today's exact behaviour for\r\n // any consumer not using Row/Column) -> then a reported Column aggregate, if one exists -> \"initial\".\r\n const selectionState =\r\n selectionStateProp !== \"initial\" ? selectionStateProp : reportedAggregate ?? \"initial\";\r\n\r\n // Track keyboard height\r\n useEffect(() => {\r\n if (typeof window === 'undefined' || !window.visualViewport) return;\r\n\r\n const handleResize = () => {\r\n const vp = window.visualViewport!;\r\n const kbHeight = window.innerHeight - vp.height;\r\n setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);\r\n };\r\n\r\n window.visualViewport.addEventListener('resize', handleResize);\r\n window.visualViewport.addEventListener('scroll', handleResize);\r\n\r\n return () => {\r\n window.visualViewport?.removeEventListener('resize', handleResize);\r\n window.visualViewport?.removeEventListener('scroll', handleResize);\r\n };\r\n }, []);\r\n\r\n // Auto-focus on search\r\n useEffect(() => {\r\n if (!isOpen) {\r\n searchInputRef.current?.blur();\r\n setSearchValue(\"\");\r\n searchProp?.onChange?.(\"\");\r\n setShouldAutoFocus(false);\r\n return;\r\n }\r\n }, [isOpen, searchProp?.autoFocus]);\r\n\r\n const handleOpenEnd = useCallback(() => {\r\n if (searchProp?.autoFocus && searchInputRef.current) {\r\n setInputKey(prev => prev + 1);\r\n setShouldAutoFocus(true);\r\n searchInputRef.current.focus();\r\n searchInputRef.current.click();\r\n }\r\n }, [searchProp?.autoFocus]);\r\n\r\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n const value = e.target.value;\r\n setSearchValue(value);\r\n searchProp?.onChange?.(value);\r\n };\r\n\r\n const handleSearchFocus = () => {\r\n setIsSearchFocused(true);\r\n searchProp?.onFocus?.();\r\n requestAnimationFrame(() => {\r\n searchInputRef.current?.focus();\r\n });\r\n };\r\n\r\n const handleSearchBlur = () => {\r\n searchProp?.onBlur?.();\r\n };\r\n\r\n const handleBackFromSearch = () => {\r\n setIsSearchFocused(false);\r\n if (searchInputRef.current) {\r\n searchInputRef.current.blur();\r\n }\r\n };\r\n\r\n const clearSearch = () => {\r\n setSearchValue(\"\");\r\n searchProp?.onChange?.(\"\");\r\n if (searchInputRef.current) {\r\n searchInputRef.current.focus();\r\n }\r\n };\r\n\r\n const handleScroll = useCallback(\r\n async (e: React.UIEvent<HTMLDivElement>) => {\r\n if (isPaginating.current || !onPaginate) return;\r\n\r\n const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;\r\n const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;\r\n\r\n if (isNearBottom) {\r\n isPaginating.current = true;\r\n const hasMore = await onPaginate();\r\n if (!hasMore) {\r\n isPaginating.current = false;\r\n } else {\r\n setTimeout(() => {\r\n isPaginating.current = false;\r\n }, 500);\r\n }\r\n }\r\n },\r\n [onPaginate]\r\n );\r\n\r\n if (!isOpen && unmountOnClose) return null;\r\n\r\n return (\r\n <SelectionViewerAggregateContext.Provider value={aggregateReporterValue}>\r\n <Sheet\r\n ref={sheetRef}\r\n isOpen={isOpen}\r\n onClose={onClose}\r\n detent=\"content\"\r\n style={{ zIndex }}\r\n maxHeight={maxHeight}\r\n onOpenEnd={handleOpenEnd}\r\n >\r\n <Sheet.Container\r\n id={id}\r\n // Announced as a modal dialog. Without this a screen reader reads the sheet as one more\r\n // region of the page and gives no indication that what is behind it is unavailable — and\r\n // an automated check for an open dialog finds nothing at all.\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={ariaLabel}\r\n style={{\r\n maxHeight: maxHeight,\r\n minHeight: isSearchFocused ? \"100dvh\" : minHeight,\r\n maxWidth: layoutProp?.maxWidth ?? '800px',\r\n margin: \"0 auto\",\r\n width: \"100%\",\r\n left: 0,\r\n right: 0,\r\n paddingBottom: \"calc(0px + env(safe-area-inset-bottom))\",\r\n background: isSearchFocused\r\n ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor\r\n : layoutProp?.backgroundColor,\r\n borderTopLeftRadius: isSearchFocused ? \"0px\" : \"16px\",\r\n borderTopRightRadius: isSearchFocused ? \"0px\" : \"16px\",\r\n }}\r\n >\r\n {isSearchFocused ? (\r\n <Sheet.Header>\r\n <div\r\n className=\"selection-viewer-fullscreen-search\"\r\n style={layoutProp?.fullScreenSearchHeaderStyle}\r\n >\r\n <div className=\"selection-viewer-search\" style={{\r\n ...searchProp?.containerStyle,\r\n background: searchProp?.background,\r\n padding: searchProp?.padding\r\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\r\n : \"16px\"\r\n }}>\r\n <button\r\n className=\"selection-viewer-search-back-button\"\r\n onClick={handleBackFromSearch}\r\n aria-label=\"Exit search mode\"\r\n >\r\n {searchProp?.backIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M15 18L9 12L15 6\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n\r\n <input\r\n key={inputKey}\r\n ref={searchInputRef}\r\n type=\"text\"\r\n placeholder={searchProp?.text}\r\n value={searchValue}\r\n onChange={handleSearchChange}\r\n className={searchProp?.className || \"selection-viewer-search-input\"}\r\n style={{\r\n color: searchProp?.textColor,\r\n ...searchProp?.inputStyle\r\n }}\r\n onFocus={handleSearchFocus}\r\n onBlur={handleSearchBlur}\r\n autoFocus={shouldAutoFocus}\r\n />\r\n\r\n {searchValue && (\r\n <button\r\n className=\"selection-viewer-search-clear-button\"\r\n onClick={clearSearch}\r\n aria-label=\"Clear search\"\r\n >\r\n {searchProp?.clearIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18 6L6 18M6 6L18 18\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </Sheet.Header>\r\n ) : (\r\n <Sheet.Header>\r\n <div className=\"selection-viewer-header\">\r\n {/* Cancel button left */}\r\n {cancelButton?.position === \"left\" && (\r\n <button\r\n aria-label={cancelButton.ariaLabel || \"Close\"}\r\n className=\"selection-viewer-cancel\"\r\n style={{\r\n position: \"absolute\",\r\n left: \"4px\",\r\n top: \"4px\",\r\n ...cancelButton.style\r\n }}\r\n onClick={cancelButton.onClick || onClose}\r\n >\r\n {cancelButton.view || cancelButton.text || \"Cancel\"}\r\n </button>\r\n )}\r\n\r\n {/* Drag handle */}\r\n {!isSearchFocused && (\r\n <div\r\n className=\"selection-viewer-drag-handle\"\r\n style={{\r\n background: layoutProp?.handleColor || \"#ccc\",\r\n width: layoutProp?.handleWidth || \"40px\",\r\n marginBottom: layoutProp?.gapBetweenHandleAndTitle || \"12px\",\r\n }}\r\n />\r\n )}\r\n\r\n {/* Cancel button right */}\r\n {cancelButton?.position === \"right\" && (\r\n <button\r\n aria-label={cancelButton.ariaLabel || \"Close\"}\r\n className=\"selection-viewer-cancel\"\r\n style={{\r\n position: \"absolute\",\r\n right: \"4px\",\r\n top: \"4px\",\r\n ...cancelButton.style\r\n }}\r\n onClick={cancelButton.onClick || onClose}\r\n >\r\n {cancelButton.view || cancelButton.text || \"Cancel\"}\r\n </button>\r\n )}\r\n\r\n {/* Title */}\r\n <div className={titleProp.containerClass || \"selection-viewer-container\"}>\r\n <h2\r\n id={`${id}-title`}\r\n className={titleProp.className || \"selection-viewer-title\"}\r\n style={{\r\n marginBottom: layoutProp?.gapBetweenTitleAndSearch || \"8px\",\r\n color: titleProp?.textColor || 'black',\r\n ...titleProp.style\r\n }}\r\n >\r\n {titleProp.text}\r\n </h2>\r\n </div>\r\n </div>\r\n\r\n {/* Search */}\r\n {searchProp && (\r\n <div\r\n className=\"selection-viewer-search\"\r\n style={{\r\n background: searchProp.background,\r\n padding: searchProp.padding\r\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\r\n : \"8px 16px\",\r\n marginBottom: layoutProp?.gapBetweenSearchAndContent,\r\n ...searchProp.containerStyle\r\n }}\r\n >\r\n {searchProp.prefixIcon && (\r\n <span style={{ marginRight: searchProp.prefixGap || \"8px\" }}>\r\n {searchProp.prefixIcon}\r\n </span>\r\n )}\r\n <input\r\n key={inputKey}\r\n ref={searchInputRef}\r\n type=\"text\"\r\n placeholder={searchProp.text}\r\n value={searchValue}\r\n onChange={handleSearchChange}\r\n className={searchProp.className || \"selection-viewer-search-input\"}\r\n style={{\r\n color: searchProp?.textColor,\r\n ...searchProp.inputStyle\r\n }}\r\n onFocus={handleSearchFocus}\r\n onBlur={handleSearchBlur}\r\n autoFocus={shouldAutoFocus}\r\n />\r\n {searchValue && (\r\n <button\r\n className=\"selection-viewer-search-clear-button\"\r\n onClick={clearSearch}\r\n aria-label=\"Clear search\"\r\n style={{ marginLeft: searchProp.suffixGap || \"8px\" }}\r\n >\r\n {searchProp.clearIcon || searchProp.suffixIcon || (\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n )}\r\n </button>\r\n )}\r\n {!searchValue && searchProp.suffixIcon && (\r\n <span style={{ marginLeft: searchProp.suffixGap || \"8px\" }}>\r\n {searchProp.suffixIcon}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </Sheet.Header>\r\n )}\r\n\r\n <Sheet.Content>\r\n <div\r\n className={`selection-viewer-content ${childrenDirection}`}\r\n onScroll={onPaginate ? handleScroll : undefined}\r\n style={{\r\n paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : '0',\r\n paddingBottom: keyboardHeight > 0\r\n ? `${keyboardHeight + 16}px`\r\n : '16px',\r\n /*\r\n * Set in from the edges, like everything else in the sheet.\r\n *\r\n * The title and the search box have carried 16px from the start and the content had\r\n * none, so a list of rows ran flush into the sides of the screen under a search box\r\n * that did not. Overridable for rows that genuinely want the full width.\r\n */\r\n paddingLeft: layoutProp?.contentPadding ?? '16px',\r\n paddingRight: layoutProp?.contentPadding ?? '16px',\r\n }}\r\n >\r\n {/* Show children first, then loading at bottom */}\r\n {React.Children.count(children) > 0 ? (\r\n <>\r\n {children}\r\n {selectionState === \"loading\" && (\r\n <div\r\n className=\"selection-viewer-loading\"\r\n style={{\r\n padding: loadingProp?.padding\r\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\r\n : \"16px\",\r\n ...loadingProp?.style\r\n }}\r\n >\r\n {loadingProp?.view}\r\n </div>\r\n )}\r\n {/*\r\n Shown ALONGSIDE the children, not instead of them.\r\n\r\n This was guarded on composed mode — a Column/Row descendant reporting its own\r\n state — on the reasoning that only a composed sheet can have children and be\r\n empty at the same time. That is not true of the commonest arrangement there is:\r\n a consumer that wraps its rows in a single container. `React.Children.count` is\r\n then 1 whether the container holds fifty rows or none, so the empty branch below\r\n never ran and the sheet showed a blank white screen — at the exact moment\r\n somebody had searched for something that is not there, which is when the empty\r\n state is the only useful thing on screen.\r\n\r\n Layered rather than substituted, for the reason the composed case already had:\r\n a child that reports its own state must stay mounted to go on reporting. A\r\n consumer whose `selectionState` says \"empty\" is asserting there is nothing to\r\n show, so drawing its own view beside visually-empty children is what it asked\r\n for.\r\n */}\r\n {selectionState === \"empty\" && (\r\n <div\r\n className=\"selection-viewer-no-results\"\r\n style={{\r\n padding: noResultProp?.padding\r\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\r\n : \"16px\",\r\n ...noResultProp?.style\r\n }}\r\n >\r\n {noResultProp?.view || (\r\n <div className=\"selection-viewer-default-no-results\">\r\n {noResultProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n {selectionState === \"error\" && (\r\n <div\r\n className=\"selection-viewer-error\"\r\n style={{\r\n padding: errorProp?.padding\r\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\r\n : \"16px\",\r\n ...errorProp?.style\r\n }}\r\n >\r\n {errorProp?.view || (\r\n <div className=\"selection-viewer-default-error\">\r\n {errorProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </>\r\n ) : (selectionState === \"empty\" || (React.Children.count(children) <= 0 && (selectionState != \"loading\" && selectionState != \"error\"))) ? (\r\n <div\r\n className=\"selection-viewer-no-results\"\r\n style={{\r\n padding: noResultProp?.padding\r\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\r\n : \"16px\",\r\n ...noResultProp?.style\r\n }}\r\n >\r\n {noResultProp?.view || (\r\n <div className=\"selection-viewer-default-no-results\">\r\n {noResultProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n ) : selectionState === \"loading\" ? (\r\n <div\r\n className=\"selection-viewer-loading\"\r\n style={{\r\n padding: loadingProp?.padding\r\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\r\n : \"16px\",\r\n ...loadingProp?.style\r\n }}\r\n >\r\n {loadingProp?.view}\r\n </div>\r\n ) : (selectionState === \"error\") ? (\r\n <div\r\n className=\"selection-viewer-error\"\r\n style={{\r\n padding: errorProp?.padding\r\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\r\n : \"16px\",\r\n ...errorProp?.style\r\n }}\r\n >\r\n {errorProp?.view || (\r\n <div className=\"selection-viewer-default-error\">\r\n {errorProp?.text || \"No results found\"}\r\n </div>\r\n )}\r\n </div>\r\n ) : null}\r\n </div>\r\n </Sheet.Content>\r\n </Sheet.Container>\r\n <Sheet.Backdrop onTap={backDrop ? onClose : undefined} />\r\n </Sheet>\r\n </SelectionViewerAggregateContext.Provider>\r\n );\r\n};\r\n\r\n// ==================== Controller Hook ====================\r\ntype Operation = {\r\n open: () => void;\r\n close: () => void;\r\n toggle: () => void;\r\n setSelectionState: (val: SelectionState) => void;\r\n};\r\n\r\nconst useSelectionController = (initialSelectionState?: SelectionState): [\r\n string,\r\n Operation,\r\n boolean,\r\n SelectionState\r\n] => {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [selectionState, setSelectionState] = useState(initialSelectionState || 'initial');\r\n const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);\r\n\r\n const operations = useMemo<Operation>(() => ({\r\n open: () => setIsOpen(true),\r\n close: () => setIsOpen(false),\r\n toggle: () => setIsOpen((prev) => !prev),\r\n setSelectionState\r\n }), [setSelectionState]);\r\n\r\n return [selectionId, operations, isOpen, selectionState];\r\n};\r\n\r\nexport { SelectionViewer, useSelectionController, RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState };\r\nexport type {\r\n SelectionViewerProps,\r\n SelectionState,\r\n TitleProps,\r\n SearchProps,\r\n LoadingProps,\r\n NoResultProps,\r\n ErrorProps,\r\n CancelButtonProps,\r\n LayoutProps,\r\n Padding,\r\n SnapPoint,\r\n AggregateReporter,\r\n};\r\nexport default SelectionViewer;\r\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { RowAggregateContext } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type RowProps = {\n id?: string;\n /** Consumer-owned — SelectionViewer has never owned fetch/filter state, so unlike\n * search-viewer's Row this isn't internally computed. Mirrors how `selectionState`\n * is already a prop on SelectionViewer itself today. Defaults to \"data\": if you\n * render children without wiring this, we assume what you rendered IS the data. */\n state?: SelectionState;\n /** Called when this Row is scrolled horizontally near its end. Return (or resolve to)\n * `true` if more data may follow (keeps the pagination trigger armed), `false` once\n * there's nothing left to fetch — mirrors SelectionViewer's own `onPaginate` contract. */\n onPaginate?: () => boolean | Promise<boolean>;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A horizontally-scrolling, independently-paginating section — the Row/Column\n * equivalent for `SelectionViewer`. No shared code with `@academix-admin/search-viewer`'s\n * Row (Library Charter: packages stay mutually independent) — this one has no\n * query/debounce/cache engine of its own since `SelectionViewer` never had one either;\n * the consumer owns fetching/filtering and just tells this Row what `state` it's in. */\nfunction Row({ id: providedId, state = \"data\", onPaginate, className, style, children }: RowProps) {\n const aggregateReporter = React.useContext(RowAggregateContext);\n const [rowId] = useState(() => providedId || `row-${Math.random().toString(36).substring(2, 11)}`);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const isPaginating = useRef(false);\n\n const handleScroll = useCallback(async () => {\n const el = containerRef.current;\n if (!el || isPaginating.current || !onPaginate) return;\n\n // Same \"near the end\" threshold as SelectionViewer's own vertical pagination trigger, adapted\n // to horizontal scroll metrics since a Row scrolls sideways.\n const { scrollLeft, scrollWidth, clientWidth } = el;\n if (scrollWidth - scrollLeft > clientWidth * 1.2) return;\n\n isPaginating.current = true;\n const hasMore = await onPaginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, [onPaginate]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el || !onPaginate) return;\n el.addEventListener(\"scroll\", handleScroll);\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll, onPaginate]);\n\n useEffect(() => {\n aggregateReporter?.report(rowId, state);\n return () => aggregateReporter?.unreport(rowId);\n }, [aggregateReporter, rowId, state]);\n\n return (\n <div\n ref={containerRef}\n className={className ? `selection-viewer-row ${className}` : \"selection-viewer-row\"}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n overflowX: \"auto\",\n overflowY: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n\nRow.displayName = \"Row\";\n\nexport { Row };\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type ColumnProps = {\n id?: string;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A vertical arrangement of `Row`s (or other content) — the explicit, composable\n * equivalent of today's flat `children` list. Its default layout is deliberately\n * identical to the existing `.selection-viewer-content.vertical` rendering\n * (`flex-direction: column, gap: 8px`), so today's flat usage is, unchanged, \"an\n * implicit Column with no Rows.\"\n *\n * Provides the `RowAggregateContext` its `Row` children report into, computes ONE\n * cumulative state from everything reported, and is itself a reporter one level up:\n * nested inside another `Column` it reports through that Column's `RowAggregateContext`\n * (behaving like a Row); at the top level it reports through `SelectionViewerAggregateContext`,\n * which `SelectionViewer` provides — this drives the outer sheet's loading/empty/error\n * views automatically with zero manual wiring, and lets Columns nest arbitrarily deep. */\nfunction Column({ id: providedId, className, style, children }: ColumnProps) {\n const [columnId] = useState(\n () => providedId || `column-${Math.random().toString(36).substring(2, 11)}`\n );\n\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const report = useCallback((id: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(id, state);\n return next;\n });\n }, []);\n const unreport = useCallback((id: string) => {\n setReportedStates((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n const rowAggregateValue = useMemo(() => ({ report, unreport }), [report, unreport]);\n\n const aggregateState =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : \"initial\";\n\n const parentRowAggregate = React.useContext(RowAggregateContext);\n const selectionViewerAggregate = React.useContext(SelectionViewerAggregateContext);\n const upstreamReporter = parentRowAggregate ?? selectionViewerAggregate;\n\n useEffect(() => {\n if (!upstreamReporter) return;\n upstreamReporter.report(columnId, aggregateState);\n return () => upstreamReporter.unreport(columnId);\n }, [upstreamReporter, columnId, aggregateState]);\n\n return (\n <RowAggregateContext.Provider value={rowAggregateValue}>\n <div\n className={className ? `selection-viewer-column ${className}` : \"selection-viewer-column\"}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\", ...style }}\n >\n {children}\n </div>\n </RowAggregateContext.Provider>\n );\n}\n\nColumn.displayName = \"Column\";\n\nexport { Column };\n"]}
1
+ {"version":3,"sources":["../src/SelectionViewer.tsx","../src/Row.tsx","../src/Column.tsx"],"names":["React","useState","useRef","useCallback","useEffect","jsx","useMemo"],"mappings":"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","file":"index.js","sourcesContent":["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport { Sheet } from \"@academix-admin/modal-sheet\";\nimport { useOverlayRoute } from '@academix-admin/overlay-route';\n\n// ==================== Aggregation (Row/Column) ====================\n// Independent from @academix-admin/search-viewer's equivalent (Library Charter: packages stay\n// mutually independent) — same shape, duplicated intentionally rather than shared.\n\ntype AggregateReporter = {\n report: (id: string, state: SelectionState) => void;\n unreport: (id: string) => void;\n};\n\n/** A `Row` reports its own `state` into the nearest `Column` through this. */\nconst RowAggregateContext = React.createContext<AggregateReporter | null>(null);\n\n/** `Column` reports its own aggregated state up through this — `SelectionViewer` provides it and\n * merges whatever's reported into its own selectionState resolution, with zero extra wiring. */\nconst SelectionViewerAggregateContext = React.createContext<AggregateReporter | null>(null);\n\nfunction computeAggregateState(states: SelectionState[]): SelectionState {\n if (states.length === 0) return \"initial\";\n if (states.some((s) => s === \"data\")) return \"data\";\n if (states.some((s) => s === \"loading\")) return \"loading\";\n if (states.every((s) => s === \"empty\")) return \"empty\";\n if (states.some((s) => s === \"error\")) return \"error\";\n return \"initial\";\n}\n\n// ==================== Types ====================\ntype Padding = {\n l: string;\n r: string;\n t: string;\n b: string;\n};\n\ntype SnapPoint = number;\n\ntype SelectionState = \"loading\" | \"empty\" | \"error\" | \"data\" | \"initial\";\n\n\ntype TitleProps = {\n text: string;\n textColor: string;\n className?: string;\n containerClass?: string;\n style?: React.CSSProperties;\n};\n\ntype SearchProps = {\n text: string;\n textColor: string;\n className?: string;\n background?: string;\n prefixIcon?: React.ReactNode;\n suffixIcon?: React.ReactNode;\n clearIcon?: React.ReactNode;\n backIcon?: React.ReactNode;\n prefixGap?: string;\n suffixGap?: string;\n padding?: Padding;\n autoFocus?: boolean;\n inputStyle?: React.CSSProperties;\n containerStyle?: React.CSSProperties;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n};\n\ntype LoadingProps = {\n view: React.ReactNode;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype NoResultProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype ErrorProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype CancelButtonProps = {\n text?: string;\n view: React.ReactNode;\n position?: \"left\" | \"right\";\n style?: React.CSSProperties;\n onClick?: () => void;\n /**\n * What a screen reader calls it. Defaults to \"Close\".\n *\n * A button whose only content is an icon has no accessible name at all, so it was announced as\n * just \"button\" — the one control that dismisses the sheet, and nothing said so.\n */\n ariaLabel?: string;\n};\n\ntype LayoutProps = {\n gapBetweenHandleAndTitle?: string;\n gapBetweenTitleAndSearch?: string;\n gapBetweenSearchAndContent?: string;\n /**\n * How far the rows are set in from the sides. Defaults to `16px`, matching the search box.\n *\n * The content had no inset while everything above it had 16px, so a list ran flush into the\n * sides of the screen under a search box that did not. Pass `0px` for rows that should reach\n * the edges.\n */\n contentPadding?: string;\n backgroundColor?: string;\n handleColor?: string;\n handleWidth?: string;\n fullScreenSearchBackground?: string;\n fullScreenSearchHeaderStyle?: React.CSSProperties;\n maxWidth?: string;\n};\n\ntype SelectionViewerProps = {\n id?: string;\n isOpen: boolean;\n backDrop?: boolean;\n onClose: () => void;\n /**\n * Give this overlay its own history entry under this name, so the platform's own Back (and the\n * iOS edge-swipe, and Android's gesture) closes the OVERLAY rather than the page under it.\n *\n * Off by default, because it changes what the back gesture does and no existing consumer asked\n * for that. Without it, Back with this open closes it AND leaves the page behind it: measured in\n * a real shop, the gesture walked straight out of a half-built sale.\n *\n * The name must be unique among overlays that can be open together, or they mistake each other's\n * entry. Make it STABLE across page loads and the overlay can also come back after a refresh —\n * see `useOverlayRoute` in `@academix-admin/overlay-route`, which this uses and which any\n * component can use directly.\n */\n historyRoute?: string;\n titleProp: TitleProps;\n searchProp?: SearchProps;\n loadingProp?: LoadingProps;\n noResultProp?: NoResultProps;\n errorProp?: ErrorProps;\n cancelButton?: CancelButtonProps;\n layoutProp?: LayoutProps;\n childrenDirection?: \"vertical\" | \"horizontal\";\n children?: React.ReactNode;\n onPaginate?: () => boolean | Promise<boolean>;\n snapPoints?: SnapPoint[];\n initialSnap?: number;\n unmountOnClose?: boolean;\n zIndex?: number;\n /**\n * Accessible name for the sheet, announced when it opens.\n *\n * A modal with no name is announced simply as \"dialog\", which tells a screen-reader user that\n * something has taken over the screen and nothing at all about what.\n */\n ariaLabel?: string;\n maxHeight?: string;\n minHeight?: string;\n selectionState?: SelectionState;\n closeThreshold?: number;\n};\n\n// ==================== Styles ====================\nconst getStyles = (id: string) => `\n#${id} .selection-viewer-header {\n padding: 0px;\n display: flex;\n flex-direction: column;\n align-items: center;\n position: relative;\n width: 100%;\n}\n\n/*\n * The close button, with a target a thumb can find.\n *\n * It sat at right: 0, top: 8 with no padding at all, so the icon was flush against the sheet's\n * edge and the only thing that could be tapped was the glyph itself — a target well under half\n * what a finger needs, in the corner of the screen where a thumb is least accurate. Inset by 4px\n * and padded out to 44px square, so the icon still reads as sitting in the corner while the\n * hittable area reaches the edges.\n *\n * The cancelButton.style prop is still spread last at the call site, so a consumer that was\n * overriding any of this keeps overriding it. (No backticks in here: this whole block lives\n * inside a template literal, and one would end the string.)\n */\n#${id} .selection-viewer-cancel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 44px;\n min-height: 44px;\n padding: 10px;\n border: none;\n background-color: transparent;\n color: inherit;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n#${id} .selection-viewer-drag-handle {\n height: 5px;\n border-radius: 3px;\n margin-top: 16px;\n}\n\n#${id} .selection-viewer-container {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n#${id} .selection-viewer-title {\n margin: 0px;\n font-size: 18px;\n font-weight: 600;\n padding: 0px 16px 0px 16px;\n word-break: break-word; /* break long words */\n white-space: normal; /* allow wrapping */\n}\n\n#${id} .selection-viewer-search {\n display: flex;\n align-items: center;\n margin: 16px 16px 4px 16px;\n border-radius: 12px;\n box-sizing: border-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n#${id} .selection-viewer-search-input {\n flex: 1;\n border: none;\n background: transparent;\n padding: 12px 8px;\n outline: none;\n font-size: 16px !important;\n width: 100%;\n}\n\n/*\n * The rows, set in from the edges like everything else in the sheet.\n *\n * The title and the search box have carried a 16px margin from the start; the content had none, so\n * a list of cards ran flush into the sides of the screen while the search box above it did not.\n * The layoutProp.contentPadding option overrides it for a consumer that wants its rows edge to\n * edge. (No backticks: this block is inside a template literal.)\n */\n#${id} .selection-viewer-content {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n min-height: 0;\n box-sizing: border-box;\n}\n\n#${id} .selection-viewer-content.vertical {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#${id} .selection-viewer-content.horizontal {\n display: flex;\n flex-direction: row;\n gap: 8px;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n#${id} .selection-viewer-no-results,\n#${id} .selection-viewer-error,\n#${id} .selection-viewer-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 120px;\n}\n\n#${id} .selection-viewer-default-no-results {\n color: #666;\n font-size: 14px;\n}\n\n#${id} .selection-viewer-default-error {\n color: red;\n font-size: 14px;\n}\n\n/* Full screen search mode */\n#${id} .selection-viewer-fullscreen-search {\n// position: fixed;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n// padding-top: env(safe-area-inset-top);\n width: 100%;\n}\n\n#${id} .selection-viewer-search-back-button {\n background: none;\n border: none;\n padding: 8px;\n margin-right: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-back-button:hover {\n opacity: 0.7;\n}\n\n#${id} .selection-viewer-search-clear-button {\n background: none;\n border: none;\n padding: 8px;\n margin-left: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-clear-button:hover {\n opacity: 0.7;\n}\n\n/* React Modal Sheet overrides */\n#${id} .react-modal-sheet-container {\n max-width: 500px;\n margin: 0 auto;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n}\n\n#${id} .react-modal-sheet-backdrop {\n background-color: rgba(0, 0, 0, 0.5) !important;\n pointer-events: auto !important;\n}\n\n#${id} .react-modal-sheet-content {\n padding: 0 0px 0px 0px;\n height: 100%;\n}\n\n\n/* Mobile full-width behavior */\n@media (max-width: 500px) {\n #${id} .react-modal-sheet-container {\n max-width: 100%;\n border-radius: 0;\n }\n}\n\n/* Prevent iOS zooming */\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n #${id} .selection-viewer-search-input {\n font-size: 16px !important;\n }\n}\n`;\n\n// ==================== Hook to inject CSS per instance ====================\nconst useInjectStyles = (id: string) => {\n useEffect(() => {\n const styleId = `selection-viewer-styles-${id}`;\n let styleTag = document.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleTag) {\n styleTag = document.createElement(\"style\");\n styleTag.id = styleId;\n styleTag.innerHTML = getStyles(id);\n document.head.appendChild(styleTag);\n }\n\n // Cleanup on unmount\n return () => {\n if (styleTag && document.head.contains(styleTag)) {\n document.head.removeChild(styleTag);\n }\n };\n }, [id]);\n};\n\n// ==================== Component ====================\n/**\n * Escape closes the viewer, and Tab stays inside it.\n *\n * The viewer covers the screen, so what is behind it is not available — but nothing said so to the\n * keyboard. Tab walked out of the option list into the page underneath, where a person was\n * operating controls they could not see, and Escape did nothing at all, which is the one key\n * everybody reaches for to get out of a picker.\n *\n * Deliberately a private copy rather than an import from a sibling package: every library here\n * stands alone, and a shared helper between two of them is a dependency the consumer did not ask\n * for. Takes the container's `id` rather than a ref, because the sheet element belongs to the\n * underlying modal-sheet component; looking it up when a key is pressed finds it however that\n * component chooses to mount it. Focusables are re-queried on every Tab, since the option list\n * changes as the consumer filters it and a list captured on open is a list of dead elements.\n */\nconst useModalKeys = (isOpen: boolean, onClose: () => void, containerId: string) => {\n useEffect(() => {\n if (!isOpen || typeof document === \"undefined\") return;\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n onClose();\n return;\n }\n if (e.key !== \"Tab\") return;\n\n const focusable = document.getElementById(containerId)?.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]),' +\n ' textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n );\n if (!focusable || focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [isOpen, onClose, containerId]);\n};\n\nconst SelectionViewer: React.FC<SelectionViewerProps> = ({\n id: providedId,\n isOpen,\n historyRoute,\n backDrop = true,\n onClose,\n titleProp,\n searchProp,\n loadingProp,\n noResultProp,\n errorProp,\n cancelButton,\n layoutProp,\n childrenDirection = \"vertical\",\n children,\n onPaginate,\n snapPoints = [0, 1],\n initialSnap = 1,\n unmountOnClose = true,\n zIndex = 1000,\n ariaLabel,\n maxHeight = \"90dvh\",\n minHeight = \"65dvh\",\n selectionState: selectionStateProp = \"initial\",\n closeThreshold = 0.2,\n}) => {\n\n /*\n * THE BACK GESTURE CLOSES THIS, NOT THE PAGE UNDER IT.\n *\n * Only when the consumer named a route: `useOverlayRoute` is a no-op with an empty name, and an\n * overlay that never asked for a history entry must not acquire one, or Back starts meaning\n * something different in an app that did not opt in.\n *\n * The capability lives in its own package rather than here, so that an app wanting a\n * back-closable sheet does not have to take a router with it — and so that a navigation library\n * can supply the history writing without either package importing the other.\n */\n useOverlayRoute(historyRoute ?? '', Boolean(historyRoute) && isOpen, onClose);\n const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);\n const [searchValue, setSearchValue] = useState(\"\");\n const [isSearchFocused, setIsSearchFocused] = useState(false);\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n const [inputKey, setInputKey] = useState(0);\n const [shouldAutoFocus, setShouldAutoFocus] = useState(false);\n const isPaginating = useRef(false);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const sheetRef = useRef<any>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n\n useInjectStyles(id);\n // Escape closes the viewer and Tab stays inside it — see useModalKeys.\n useModalKeys(isOpen, onClose, id);\n\n // Descendant sections (a Column, or anything else) can report a cumulative state up through this —\n // starts empty, so a plain SelectionViewer with no Row/Column children behaves exactly as before.\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const reportAggregate = useCallback((reporterId: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(reporterId, state);\n return next;\n });\n }, []);\n const unreportAggregate = useCallback((reporterId: string) => {\n setReportedStates((prev) => {\n if (!prev.has(reporterId)) return prev;\n const next = new Map(prev);\n next.delete(reporterId);\n return next;\n });\n }, []);\n const aggregateReporterValue = useMemo(\n () => ({ report: reportAggregate, unreport: unreportAggregate }),\n [reportAggregate, unreportAggregate]\n );\n const reportedAggregate =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : null;\n const isComposed = reportedAggregate !== null;\n\n // Precedence: an explicit `selectionState` prop always wins (matches today's exact behaviour for\n // any consumer not using Row/Column) -> then a reported Column aggregate, if one exists -> \"initial\".\n const selectionState =\n selectionStateProp !== \"initial\" ? selectionStateProp : reportedAggregate ?? \"initial\";\n\n // Track keyboard height\n useEffect(() => {\n if (typeof window === 'undefined' || !window.visualViewport) return;\n\n const handleResize = () => {\n const vp = window.visualViewport!;\n const kbHeight = window.innerHeight - vp.height;\n setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);\n };\n\n window.visualViewport.addEventListener('resize', handleResize);\n window.visualViewport.addEventListener('scroll', handleResize);\n\n return () => {\n window.visualViewport?.removeEventListener('resize', handleResize);\n window.visualViewport?.removeEventListener('scroll', handleResize);\n };\n }, []);\n\n // Auto-focus on search\n useEffect(() => {\n if (!isOpen) {\n searchInputRef.current?.blur();\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n setShouldAutoFocus(false);\n return;\n }\n }, [isOpen, searchProp?.autoFocus]);\n\n const handleOpenEnd = useCallback(() => {\n if (searchProp?.autoFocus && searchInputRef.current) {\n setInputKey(prev => prev + 1);\n setShouldAutoFocus(true);\n searchInputRef.current.focus();\n searchInputRef.current.click();\n }\n }, [searchProp?.autoFocus]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n setSearchValue(value);\n searchProp?.onChange?.(value);\n };\n\n const handleSearchFocus = () => {\n setIsSearchFocused(true);\n searchProp?.onFocus?.();\n requestAnimationFrame(() => {\n searchInputRef.current?.focus();\n });\n };\n\n const handleSearchBlur = () => {\n searchProp?.onBlur?.();\n };\n\n const handleBackFromSearch = () => {\n setIsSearchFocused(false);\n if (searchInputRef.current) {\n searchInputRef.current.blur();\n }\n };\n\n const clearSearch = () => {\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n if (searchInputRef.current) {\n searchInputRef.current.focus();\n }\n };\n\n /*\n * PAGINATION LISTENS TO WHATEVER ACTUALLY SCROLLS.\n *\n * It listened to `.selection-viewer-content` alone. Inside the sheet that element is not the\n * scroller: react-modal-sheet wraps content in its own `.react-modal-sheet-content-scroller`, the\n * content grows to its full height (4,601px for a hundred rows, measured) and the WRAPPER scrolls.\n * The content box never fired a scroll event, so `onPaginate` was never called — every consumer's\n * list stopped at its first page with nothing to say it had.\n *\n * So the nearest scrolling ancestor is found and listened to, and the content itself as well (for\n * a layout where it is the scroller). And the check also runs when the content changes size: a\n * first page too short to scroll can never produce a scroll event, but it is plainly \"at the\n * bottom\", so it asks for the next one straight away.\n */\n const onPaginateRef = useRef(onPaginate);\n onPaginateRef.current = onPaginate;\n const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null);\n\n const checkNearBottom = useCallback(async (el: HTMLElement) => {\n const paginate = onPaginateRef.current;\n if (isPaginating.current || !paginate) return;\n\n const { scrollTop, scrollHeight, clientHeight } = el;\n const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;\n if (!isNearBottom) return;\n\n isPaginating.current = true;\n const hasMore = await paginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, []);\n\n const hasPaginate = Boolean(onPaginate);\n useEffect(() => {\n if (!contentEl || !hasPaginate) return;\n const scroller =\n (contentEl.closest(\".react-modal-sheet-content-scroller\") as HTMLElement | null) ?? contentEl;\n const onScroll = () => void checkNearBottom(scroller);\n const onInnerScroll = () => void checkNearBottom(contentEl);\n\n scroller.addEventListener(\"scroll\", onScroll, { passive: true });\n if (scroller !== contentEl) contentEl.addEventListener(\"scroll\", onInnerScroll, { passive: true });\n\n const resize =\n typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(() => onScroll()) : null;\n resize?.observe(contentEl);\n\n return () => {\n scroller.removeEventListener(\"scroll\", onScroll);\n contentEl.removeEventListener(\"scroll\", onInnerScroll);\n resize?.disconnect();\n };\n }, [contentEl, hasPaginate, checkNearBottom]);\n\n if (!isOpen && unmountOnClose) return null;\n\n return (\n <SelectionViewerAggregateContext.Provider value={aggregateReporterValue}>\n <Sheet\n ref={sheetRef}\n isOpen={isOpen}\n onClose={onClose}\n detent=\"content\"\n style={{ zIndex }}\n maxHeight={maxHeight}\n onOpenEnd={handleOpenEnd}\n >\n <Sheet.Container\n id={id}\n // Announced as a modal dialog. Without this a screen reader reads the sheet as one more\n // region of the page and gives no indication that what is behind it is unavailable — and\n // an automated check for an open dialog finds nothing at all.\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={ariaLabel}\n style={{\n maxHeight: maxHeight,\n minHeight: isSearchFocused ? \"100dvh\" : minHeight,\n maxWidth: layoutProp?.maxWidth ?? '800px',\n margin: \"0 auto\",\n width: \"100%\",\n left: 0,\n right: 0,\n paddingBottom: \"calc(0px + env(safe-area-inset-bottom))\",\n background: isSearchFocused\n ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor\n : layoutProp?.backgroundColor,\n borderTopLeftRadius: isSearchFocused ? \"0px\" : \"16px\",\n borderTopRightRadius: isSearchFocused ? \"0px\" : \"16px\",\n }}\n >\n {isSearchFocused ? (\n <Sheet.Header>\n <div\n className=\"selection-viewer-fullscreen-search\"\n style={layoutProp?.fullScreenSearchHeaderStyle}\n >\n <div className=\"selection-viewer-search\" style={{\n ...searchProp?.containerStyle,\n background: searchProp?.background,\n padding: searchProp?.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"16px\"\n }}>\n <button\n className=\"selection-viewer-search-back-button\"\n onClick={handleBackFromSearch}\n aria-label=\"Exit search mode\"\n >\n {searchProp?.backIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M15 18L9 12L15 6\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp?.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp?.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp?.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n >\n {searchProp?.clearIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n </div>\n </div>\n </Sheet.Header>\n ) : (\n <Sheet.Header>\n <div className=\"selection-viewer-header\">\n {/* Cancel button left */}\n {cancelButton?.position === \"left\" && (\n <button\n aria-label={cancelButton.ariaLabel || \"Close\"}\n className=\"selection-viewer-cancel\"\n style={{\n position: \"absolute\",\n left: \"4px\",\n top: \"4px\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Drag handle */}\n {!isSearchFocused && (\n <div\n className=\"selection-viewer-drag-handle\"\n style={{\n background: layoutProp?.handleColor || \"#ccc\",\n width: layoutProp?.handleWidth || \"40px\",\n marginBottom: layoutProp?.gapBetweenHandleAndTitle || \"12px\",\n }}\n />\n )}\n\n {/* Cancel button right */}\n {cancelButton?.position === \"right\" && (\n <button\n aria-label={cancelButton.ariaLabel || \"Close\"}\n className=\"selection-viewer-cancel\"\n style={{\n position: \"absolute\",\n right: \"4px\",\n top: \"4px\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Title */}\n <div className={titleProp.containerClass || \"selection-viewer-container\"}>\n <h2\n id={`${id}-title`}\n className={titleProp.className || \"selection-viewer-title\"}\n style={{\n marginBottom: layoutProp?.gapBetweenTitleAndSearch || \"8px\",\n color: titleProp?.textColor || 'black',\n ...titleProp.style\n }}\n >\n {titleProp.text}\n </h2>\n </div>\n </div>\n\n {/* Search */}\n {searchProp && (\n <div\n className=\"selection-viewer-search\"\n style={{\n background: searchProp.background,\n padding: searchProp.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"8px 16px\",\n marginBottom: layoutProp?.gapBetweenSearchAndContent,\n ...searchProp.containerStyle\n }}\n >\n {searchProp.prefixIcon && (\n <span style={{ marginRight: searchProp.prefixGap || \"8px\" }}>\n {searchProp.prefixIcon}\n </span>\n )}\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n style={{ marginLeft: searchProp.suffixGap || \"8px\" }}\n >\n {searchProp.clearIcon || searchProp.suffixIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n {!searchValue && searchProp.suffixIcon && (\n <span style={{ marginLeft: searchProp.suffixGap || \"8px\" }}>\n {searchProp.suffixIcon}\n </span>\n )}\n </div>\n )}\n </Sheet.Header>\n )}\n\n <Sheet.Content>\n <div\n ref={setContentEl}\n className={`selection-viewer-content ${childrenDirection}`}\n style={{\n paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : '0',\n paddingBottom: keyboardHeight > 0\n ? `${keyboardHeight + 16}px`\n : '16px',\n /*\n * Set in from the edges, like everything else in the sheet.\n *\n * The title and the search box have carried 16px from the start and the content had\n * none, so a list of rows ran flush into the sides of the screen under a search box\n * that did not. Overridable for rows that genuinely want the full width.\n */\n paddingLeft: layoutProp?.contentPadding ?? '16px',\n paddingRight: layoutProp?.contentPadding ?? '16px',\n }}\n >\n {/* Show children first, then loading at bottom */}\n {React.Children.count(children) > 0 ? (\n <>\n {children}\n {selectionState === \"loading\" && (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n )}\n {/*\n Shown ALONGSIDE the children, not instead of them.\n\n This was guarded on composed mode — a Column/Row descendant reporting its own\n state — on the reasoning that only a composed sheet can have children and be\n empty at the same time. That is not true of the commonest arrangement there is:\n a consumer that wraps its rows in a single container. `React.Children.count` is\n then 1 whether the container holds fifty rows or none, so the empty branch below\n never ran and the sheet showed a blank white screen — at the exact moment\n somebody had searched for something that is not there, which is when the empty\n state is the only useful thing on screen.\n\n Layered rather than substituted, for the reason the composed case already had:\n a child that reports its own state must stay mounted to go on reporting. A\n consumer whose `selectionState` says \"empty\" is asserting there is nothing to\n show, so drawing its own view beside visually-empty children is what it asked\n for.\n */}\n {selectionState === \"empty\" && (\n <div\n className=\"selection-viewer-no-results\"\n style={{\n padding: noResultProp?.padding\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\n : \"16px\",\n ...noResultProp?.style\n }}\n >\n {noResultProp?.view || (\n <div className=\"selection-viewer-default-no-results\">\n {noResultProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n )}\n {selectionState === \"error\" && (\n <div\n className=\"selection-viewer-error\"\n style={{\n padding: errorProp?.padding\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\n : \"16px\",\n ...errorProp?.style\n }}\n >\n {errorProp?.view || (\n <div className=\"selection-viewer-default-error\">\n {errorProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n )}\n </>\n ) : (selectionState === \"empty\" || (React.Children.count(children) <= 0 && (selectionState != \"loading\" && selectionState != \"error\"))) ? (\n <div\n className=\"selection-viewer-no-results\"\n style={{\n padding: noResultProp?.padding\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\n : \"16px\",\n ...noResultProp?.style\n }}\n >\n {noResultProp?.view || (\n <div className=\"selection-viewer-default-no-results\">\n {noResultProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : selectionState === \"loading\" ? (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n ) : (selectionState === \"error\") ? (\n <div\n className=\"selection-viewer-error\"\n style={{\n padding: errorProp?.padding\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\n : \"16px\",\n ...errorProp?.style\n }}\n >\n {errorProp?.view || (\n <div className=\"selection-viewer-default-error\">\n {errorProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : null}\n </div>\n </Sheet.Content>\n </Sheet.Container>\n <Sheet.Backdrop onTap={backDrop ? onClose : undefined} />\n </Sheet>\n </SelectionViewerAggregateContext.Provider>\n );\n};\n\n// ==================== Controller Hook ====================\ntype Operation = {\n open: () => void;\n close: () => void;\n toggle: () => void;\n setSelectionState: (val: SelectionState) => void;\n};\n\nconst useSelectionController = (initialSelectionState?: SelectionState): [\n string,\n Operation,\n boolean,\n SelectionState\n] => {\n const [isOpen, setIsOpen] = useState(false);\n const [selectionState, setSelectionState] = useState(initialSelectionState || 'initial');\n const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);\n\n const operations = useMemo<Operation>(() => ({\n open: () => setIsOpen(true),\n close: () => setIsOpen(false),\n toggle: () => setIsOpen((prev) => !prev),\n setSelectionState\n }), [setSelectionState]);\n\n return [selectionId, operations, isOpen, selectionState];\n};\n\nexport { SelectionViewer, useSelectionController, RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState };\nexport type {\n SelectionViewerProps,\n SelectionState,\n TitleProps,\n SearchProps,\n LoadingProps,\n NoResultProps,\n ErrorProps,\n CancelButtonProps,\n LayoutProps,\n Padding,\n SnapPoint,\n AggregateReporter,\n};\nexport default SelectionViewer;\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { RowAggregateContext } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type RowProps = {\n id?: string;\n /** Consumer-owned — SelectionViewer has never owned fetch/filter state, so unlike\n * search-viewer's Row this isn't internally computed. Mirrors how `selectionState`\n * is already a prop on SelectionViewer itself today. Defaults to \"data\": if you\n * render children without wiring this, we assume what you rendered IS the data. */\n state?: SelectionState;\n /** Called when this Row is scrolled horizontally near its end. Return (or resolve to)\n * `true` if more data may follow (keeps the pagination trigger armed), `false` once\n * there's nothing left to fetch — mirrors SelectionViewer's own `onPaginate` contract. */\n onPaginate?: () => boolean | Promise<boolean>;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A horizontally-scrolling, independently-paginating section — the Row/Column\n * equivalent for `SelectionViewer`. No shared code with `@academix-admin/search-viewer`'s\n * Row (Library Charter: packages stay mutually independent) — this one has no\n * query/debounce/cache engine of its own since `SelectionViewer` never had one either;\n * the consumer owns fetching/filtering and just tells this Row what `state` it's in. */\nfunction Row({ id: providedId, state = \"data\", onPaginate, className, style, children }: RowProps) {\n const aggregateReporter = React.useContext(RowAggregateContext);\n const [rowId] = useState(() => providedId || `row-${Math.random().toString(36).substring(2, 11)}`);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const isPaginating = useRef(false);\n\n const handleScroll = useCallback(async () => {\n const el = containerRef.current;\n if (!el || isPaginating.current || !onPaginate) return;\n\n // Same \"near the end\" threshold as SelectionViewer's own vertical pagination trigger, adapted\n // to horizontal scroll metrics since a Row scrolls sideways.\n const { scrollLeft, scrollWidth, clientWidth } = el;\n if (scrollWidth - scrollLeft > clientWidth * 1.2) return;\n\n isPaginating.current = true;\n const hasMore = await onPaginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }, [onPaginate]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el || !onPaginate) return;\n el.addEventListener(\"scroll\", handleScroll);\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll, onPaginate]);\n\n useEffect(() => {\n aggregateReporter?.report(rowId, state);\n return () => aggregateReporter?.unreport(rowId);\n }, [aggregateReporter, rowId, state]);\n\n return (\n <div\n ref={containerRef}\n className={className ? `selection-viewer-row ${className}` : \"selection-viewer-row\"}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n overflowX: \"auto\",\n overflowY: \"hidden\",\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n\nRow.displayName = \"Row\";\n\nexport { Row };\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RowAggregateContext, SelectionViewerAggregateContext, computeAggregateState } from \"./SelectionViewer\";\nimport type { SelectionState } from \"./SelectionViewer\";\n\nexport type ColumnProps = {\n id?: string;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n};\n\n/** A vertical arrangement of `Row`s (or other content) — the explicit, composable\n * equivalent of today's flat `children` list. Its default layout is deliberately\n * identical to the existing `.selection-viewer-content.vertical` rendering\n * (`flex-direction: column, gap: 8px`), so today's flat usage is, unchanged, \"an\n * implicit Column with no Rows.\"\n *\n * Provides the `RowAggregateContext` its `Row` children report into, computes ONE\n * cumulative state from everything reported, and is itself a reporter one level up:\n * nested inside another `Column` it reports through that Column's `RowAggregateContext`\n * (behaving like a Row); at the top level it reports through `SelectionViewerAggregateContext`,\n * which `SelectionViewer` provides — this drives the outer sheet's loading/empty/error\n * views automatically with zero manual wiring, and lets Columns nest arbitrarily deep. */\nfunction Column({ id: providedId, className, style, children }: ColumnProps) {\n const [columnId] = useState(\n () => providedId || `column-${Math.random().toString(36).substring(2, 11)}`\n );\n\n const [reportedStates, setReportedStates] = useState<Map<string, SelectionState>>(new Map());\n const report = useCallback((id: string, state: SelectionState) => {\n setReportedStates((prev) => {\n const next = new Map(prev);\n next.set(id, state);\n return next;\n });\n }, []);\n const unreport = useCallback((id: string) => {\n setReportedStates((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n const rowAggregateValue = useMemo(() => ({ report, unreport }), [report, unreport]);\n\n const aggregateState =\n reportedStates.size > 0 ? computeAggregateState(Array.from(reportedStates.values())) : \"initial\";\n\n const parentRowAggregate = React.useContext(RowAggregateContext);\n const selectionViewerAggregate = React.useContext(SelectionViewerAggregateContext);\n const upstreamReporter = parentRowAggregate ?? selectionViewerAggregate;\n\n useEffect(() => {\n if (!upstreamReporter) return;\n upstreamReporter.report(columnId, aggregateState);\n return () => upstreamReporter.unreport(columnId);\n }, [upstreamReporter, columnId, aggregateState]);\n\n return (\n <RowAggregateContext.Provider value={rowAggregateValue}>\n <div\n className={className ? `selection-viewer-column ${className}` : \"selection-viewer-column\"}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\", ...style }}\n >\n {children}\n </div>\n </RowAggregateContext.Provider>\n );\n}\n\nColumn.displayName = \"Column\";\n\nexport { Column };\n"]}
package/package.json CHANGED
@@ -1,76 +1,77 @@
1
- {
2
- "name": "@academix-admin/selection-viewer",
3
- "version": "0.4.1",
4
- "description": "Imperative React selection sheet \u2014 search + pick from a list \u2014 with a controller hook, built on @academix-admin/modal-sheet.",
5
- "keywords": [
6
- "react",
7
- "selection",
8
- "picker",
9
- "sheet",
10
- "modal",
11
- "search",
12
- "imperative",
13
- "controller"
14
- ],
15
- "license": "MIT",
16
- "author": "Academix",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/academix-admin/academix-ui.git",
20
- "directory": "packages/selection-viewer"
21
- },
22
- "homepage": "https://github.com/academix-admin/academix-ui/tree/main/packages/selection-viewer#readme",
23
- "bugs": {
24
- "url": "https://github.com/academix-admin/academix-ui/issues"
25
- },
26
- "type": "module",
27
- "sideEffects": false,
28
- "files": [
29
- "dist",
30
- "README.md",
31
- "LICENSE"
32
- ],
33
- "main": "./dist/index.cjs",
34
- "module": "./dist/index.js",
35
- "types": "./dist/index.d.ts",
36
- "exports": {
37
- ".": {
38
- "import": {
39
- "types": "./dist/index.d.ts",
40
- "default": "./dist/index.js"
41
- },
42
- "require": {
43
- "types": "./dist/index.d.cts",
44
- "default": "./dist/index.cjs"
45
- }
46
- },
47
- "./package.json": "./package.json"
48
- },
49
- "scripts": {
50
- "build": "tsup",
51
- "clean": "rimraf dist",
52
- "dev": "tsup --watch",
53
- "test": "vitest run",
54
- "test:watch": "vitest",
55
- "typecheck": "tsc --noEmit"
56
- },
57
- "dependencies": {
58
- "@academix-admin/modal-sheet": "^0.1.0"
59
- },
60
- "peerDependencies": {
61
- "react": ">=18.0.0",
62
- "react-dom": ">=18.0.0"
63
- },
64
- "devDependencies": {
65
- "@types/react": "^19.0.0",
66
- "@types/react-dom": "^19.0.0",
67
- "react": "^18.3.1",
68
- "react-dom": "^18.3.1",
69
- "rimraf": "^6.0.1",
70
- "tsup": "^8.3.5",
71
- "typescript": "^5.9.3"
72
- },
73
- "publishConfig": {
74
- "access": "public"
75
- }
76
- }
1
+ {
2
+ "name": "@academix-admin/selection-viewer",
3
+ "version": "0.5.1",
4
+ "description": "Imperative React selection sheet — search + pick from a list — with a controller hook, built on @academix-admin/modal-sheet.",
5
+ "keywords": [
6
+ "react",
7
+ "selection",
8
+ "picker",
9
+ "sheet",
10
+ "modal",
11
+ "search",
12
+ "imperative",
13
+ "controller"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "Academix",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/academix-admin/academix-ui.git",
20
+ "directory": "packages/selection-viewer"
21
+ },
22
+ "homepage": "https://github.com/academix-admin/academix-ui/tree/main/packages/selection-viewer#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/academix-admin/academix-ui/issues"
25
+ },
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "main": "./dist/index.cjs",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "import": {
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/index.d.cts",
44
+ "default": "./dist/index.cjs"
45
+ }
46
+ },
47
+ "./package.json": "./package.json"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "clean": "rimraf dist",
52
+ "dev": "tsup --watch",
53
+ "test": "vitest run",
54
+ "test:watch": "vitest",
55
+ "typecheck": "tsc --noEmit"
56
+ },
57
+ "dependencies": {
58
+ "@academix-admin/modal-sheet": "^0.1.0",
59
+ "@academix-admin/overlay-route": "^0.1.0"
60
+ },
61
+ "peerDependencies": {
62
+ "react": ">=18.0.0",
63
+ "react-dom": ">=18.0.0"
64
+ },
65
+ "devDependencies": {
66
+ "@types/react": "^19.0.0",
67
+ "@types/react-dom": "^19.0.0",
68
+ "react": "^18.3.1",
69
+ "react-dom": "^18.3.1",
70
+ "rimraf": "^6.0.1",
71
+ "tsup": "^8.3.5",
72
+ "typescript": "^5.9.3"
73
+ },
74
+ "publishConfig": {
75
+ "access": "public"
76
+ }
77
+ }