{"version":3,"file":"virtualListHeader.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualListHeader.tsx"],"sourcesContent":["import React from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faGear, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './virtualList.module.pcss';\nimport Checkbox from '../checkbox';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { isColumnSortable } from './virtualList.utils';\nimport { useHeaderNavigation } from './useHeaderNavigation';\nimport type { UseColumnsResult } from './useColumns';\nimport type { UseColumnResizeResult } from './useColumnResize';\nimport type { SlotClassNames, SlotStyles } from '../types/slots.types';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListSlots, VirtualListSort } from './virtualList.types';\n\n/** Width change of one Alt+arrow press, in px. Alt+Shift steps by one instead. */\nconst KEYBOARD_RESIZE_STEP = 10;\n\n/** FontAwesome's own style type, which carries the `--fa-font-*` custom properties. */\ntype FaStyle = React.CSSProperties & Record<`--fa-font-${string}`, string>;\n\ninterface VirtualListHeaderProps<T> {\n  headerRef: React.RefObject<HTMLDivElement | null>;\n  /** Every column, including hidden ones: the toggle menu lists them all. */\n  columns: VirtualListColumn<T>[];\n  cols: UseColumnsResult<T>;\n  resize: UseColumnResizeResult<T>;\n  sort: VirtualListSort | null;\n  onSort: (key: string) => void;\n  multiSelect: boolean;\n  allSelected: boolean;\n  someSelected: boolean;\n  onSelectAll: () => void;\n  labels: Required<VirtualListLabels>;\n  classNames?: SlotClassNames<VirtualListSlots>;\n  styles?: SlotStyles<VirtualListSlots>;\n}\n\n/**\n * The table-mode header row: one cell per visible column, each optionally a sort\n * button and a resize handle, plus the column-visibility menu pinned to the\n * right. It is a grid with the same template as every row, which is what keeps\n * the columns aligned across the separate grids.\n *\n * The whole header is a single tab stop (see `useHeaderNavigation`). Focus lands\n * on a header cell rather than on the button inside it, so every key a column\n * understands is handled in one place: Enter or Space sorts, Alt plus the arrow\n * keys resizes, Alt+Home and Alt+End take the column to its minimum or to its\n * content width.\n */\nfunction VirtualListHeader<T>({\n  headerRef,\n  columns,\n  cols,\n  resize,\n  sort,\n  onSort,\n  multiSelect,\n  allSelected,\n  someSelected,\n  onSelectAll,\n  labels,\n  classNames,\n  styles: slotStyles,\n}: VirtualListHeaderProps<T>) {\n  // Station order is the reading order: the select-all box, the columns, the menu.\n  const columnOffset = multiSelect ? 1 : 0;\n  const stationCount = columnOffset + cols.visibleColumns.length + (cols.shouldShowToggle ? 1 : 0);\n  const nav = useHeaderNavigation(stationCount);\n\n  return (\n    <div\n      ref={headerRef}\n      className={cx(styles.virtualListHeader, classNames?.header)}\n      style={{\n        gridTemplateColumns: cols.columnTemplate,\n        ...(cols.shouldShowToggle ? { paddingRight: 32 } : {}),\n        ...slotStyles?.header,\n      }}\n      role='row'\n      aria-rowindex={1}\n    >\n      {multiSelect && (\n        <div\n          {...nav.getStationProps(0)}\n          className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n          style={slotStyles?.selectCell}\n          role='columnheader'\n          onKeyDown={(event) => {\n            if (nav.handleNavigationKey(event, 0)) return;\n            if (event.target !== event.currentTarget) return;\n            if (event.key !== 'Enter' && event.key !== ' ') return;\n            event.preventDefault();\n            onSelectAll();\n          }}\n        >\n          <Checkbox\n            selected={allSelected}\n            indeterminate={someSelected}\n            onToggle={onSelectAll}\n            aria-label={labels.selectAllRows}\n            tabIndex={-1}\n          />\n        </div>\n      )}\n\n      {cols.visibleColumns.map((col, columnIndex) => {\n        const sortable = isColumnSortable(col);\n        const direction = sort && sort.key === col.key ? sort.direction : null;\n        const handleProps = resize.getHandleProps(col);\n        const station = columnOffset + columnIndex;\n        return (\n          <div\n            key={col.key}\n            {...nav.getStationProps(station)}\n            onKeyDown={(event) => {\n              if (nav.handleNavigationKey(event, station)) return;\n              if (event.altKey && resize.isResizable(col)) {\n                const step = event.shiftKey ? 1 : KEYBOARD_RESIZE_STEP;\n                if (event.key === 'ArrowRight') resize.resizeBy(col.key, step);\n                else if (event.key === 'ArrowLeft') resize.resizeBy(col.key, -step);\n                else if (event.key === 'Home') resize.resizeToMin(col.key);\n                else if (event.key === 'End') resize.autoFit(col.key);\n                else return;\n                event.preventDefault();\n                return;\n              }\n              // Only when the cell itself holds the focus: after a mouse click\n              // the focus sits on the sort button, which activates on its own.\n              if (!sortable || event.target !== event.currentTarget) return;\n              if (event.key !== 'Enter' && event.key !== ' ') return;\n              event.preventDefault();\n              onSort(col.key);\n            }}\n            className={cx(styles.virtualListHeaderCell, classNames?.headerCell)}\n            style={slotStyles?.headerCell}\n            role='columnheader'\n            data-col-key={col.key}\n            aria-sort={\n              sortable ? (direction === 'asc' ? 'ascending' : direction === 'desc' ? 'descending' : 'none') : undefined\n            }\n            data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n          >\n            {sortable ? (\n              <button\n                type='button'\n                className={cx(styles.virtualListHeaderSort, classNames?.headerSort)}\n                style={slotStyles?.headerSort}\n                onClick={() => onSort(col.key)}\n                tabIndex={-1}\n              >\n                <span className={styles.virtualListHeaderSortLabel}>{col.label}</span>\n                {/* Rendered in every state, inactive included: the header cell is\n                    measured for `fit` columns and those pins only ever grow, so an\n                    icon that appears on click would widen the column for good. */}\n                <FontAwesomeIcon\n                  className={cx(\n                    styles.virtualListHeaderSortIcon,\n                    direction && styles.virtualListHeaderSortIconActive,\n                    classNames?.sortIcon,\n                  )}\n                  style={slotStyles?.sortIcon as FaStyle}\n                  icon={direction === 'asc' ? faSortUp : direction === 'desc' ? faSortDown : faSort}\n                />\n              </button>\n            ) : (\n              col.label\n            )}\n            {handleProps && (\n              <div\n                {...handleProps}\n                className={cx(styles.virtualListHeaderResize, classNames?.resizeHandle)}\n                style={slotStyles?.resizeHandle}\n              />\n            )}\n          </div>\n        );\n      })}\n\n      {cols.shouldShowToggle && (\n        <ColumnToggle\n          station={stationCount - 1}\n          nav={nav}\n          columns={columns}\n          cols={cols}\n          resize={resize}\n          labels={labels}\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      )}\n    </div>\n  );\n}\n\ntype ColumnToggleProps<T> = Pick<\n  VirtualListHeaderProps<T>,\n  'columns' | 'cols' | 'resize' | 'labels' | 'classNames' | 'styles'\n> & {\n  station: number;\n  nav: ReturnType<typeof useHeaderNavigation>;\n};\n\n/**\n * The gear at the header's trailing edge: one checkbox per column, the reset for\n * user-set widths, and a badge counting the columns the user has hidden.\n */\nfunction ColumnToggle<T>({\n  station,\n  nav,\n  columns,\n  cols,\n  resize,\n  labels,\n  classNames,\n  styles: slotStyles,\n}: ColumnToggleProps<T>) {\n  return (\n    <div className={styles.virtualListToggleWrapper}>\n      <Popover\n        isOpen={cols.toggleOpen}\n        onOpenChange={cols.setToggleOpen}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.End}\n        constrainWidth={false}\n        classNames={{ panel: styles.virtualListTogglePanel }}\n        content={\n          <div\n            className={cx(styles.virtualListTogglePopover, classNames?.togglePopover)}\n            style={slotStyles?.togglePopover}\n          >\n            {columns\n              .filter((col) => !col.hideFromToggle)\n              .map((col) => (\n                <Checkbox\n                  key={col.key}\n                  className={cx(styles.virtualListToggleItem, classNames?.toggleItem)}\n                  style={slotStyles?.toggleItem}\n                  selected={cols.effectiveVisible.has(col.key)}\n                  onToggle={() => cols.handleToggleColumn(col.key)}\n                  disabled={col.lockVisible}\n                  cursorDefault={false}\n                >\n                  {col.toggleLabel ?? col.label}\n                </Checkbox>\n              ))}\n            {resize.isFrozen && (\n              <button\n                type='button'\n                className={cx(styles.virtualListToggleReset, classNames?.toggleReset)}\n                style={slotStyles?.toggleReset}\n                onClick={() => {\n                  resize.reset();\n                  cols.setToggleOpen(false);\n                }}\n              >\n                {labels.resetColumnWidths}\n              </button>\n            )}\n          </div>\n        }\n      >\n        <button\n          {...nav.getStationProps(station)}\n          type='button'\n          className={cx(styles.virtualListHeaderToggle, classNames?.headerToggle)}\n          style={slotStyles?.headerToggle}\n          aria-label={labels.toggleColumns}\n          // Enter and Space are the button's own; only the arrows are ours.\n          onKeyDown={(event) => nav.handleNavigationKey(event, station)}\n        >\n          <FontAwesomeIcon icon={faGear} style={slotStyles?.headerToggle as FaStyle} />\n        </button>\n      </Popover>\n      {cols.manuallyHiddenCount > 0 && (\n        <span\n          className={cx(styles.virtualListColumnChip, classNames?.columnChip)}\n          style={slotStyles?.columnChip}\n          data-column-hidden-count={cols.manuallyHiddenCount}\n          aria-label={labels.columnsHidden(cols.manuallyHiddenCount)}\n        >\n          {cols.manuallyHiddenCount}\n        </span>\n      )}\n    </div>\n  );\n}\n\nexport default VirtualListHeader;\n"],"mappings":"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"}