sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
1,166 lines (1,152 loc) • 1.65 MB
JavaScript
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { createContext, useContext, useRef, useLayoutEffect, useState, useMemo, memo, useCallback, forwardRef, useEffect, createElement, useId, useImperativeHandle, cloneElement, Fragment as Fragment$1, useSyncExternalStore, Suspense, Component, createRef, startTransition, PureComponent, isValidElement, Children, useReducer } from "react";
import flatten$1 from "lodash/flatten.js";
import { useMemoObservable, useObservableCallback } from "react-rx";
import { of, concat, map as map$1, from, catchError as catchError$1, Subject, merge, fromEvent, defer, throwError, timer, NEVER, empty, Observable, EMPTY as EMPTY$3, combineLatest, bufferTime, mergeMap as mergeMap$1, switchMap as switchMap$1, finalize, share, firstValueFrom, filter as filter$1, lastValueFrom, isObservable, BehaviorSubject, asyncScheduler, partition as partition$1, iif } from "rxjs";
import { switchMap, map, scan, distinctUntilChanged, catchError, shareReplay, filter, take, startWith, tap, mergeMapTo, takeUntil, mergeMap, first, concatMap, mergeAll, toArray as toArray$1, delay, reduce, share as share$1, publishReplay, refCount, withLatestFrom, groupBy as groupBy$1, throttleTime, last, distinct, skip, debounceTime, auditTime, switchMapTo, flatMap, debounce as debounce$1 } from "rxjs/operators";
import isEqual$1 from "lodash/isEqual.js";
import { ResizeObserver as ResizeObserver$1 } from "@juggle/resize-observer";
import createPubSub from "nano-pubsub";
import "lodash/throttle.js";
import isString$1 from "lodash/isString.js";
import { fromUrl } from "@sanity/bifur-client";
import { createClient } from "@sanity/client";
import { useLayer, useForwardedRef, Grid, rem, Box, Stack, Card, Spinner, Text as Text$1, Layer, Heading, Inline, Flex, Breadcrumbs, useClickOutside, Code, useTheme, MenuDivider, Menu, AvatarStack, Avatar, AvatarCounter, Badge, useToast, useGlobalKeyDown, useElementRect, TextSkeleton, Autocomplete, Checkbox as Checkbox$1, Switch as Switch$1, LayerProvider, TextInput as TextInput$1, ElementQuery, TabList, Select, TextArea, useElementSize, Skeleton, rgba, studioTheme, Container as Container$2, MenuItem as MenuItem$1, useBoundaryElement, BoundaryElementProvider, usePortal, PortalProvider, Portal, Button as Button$1, isHTMLElement, Radio, useMediaIndex, usePrefersDark, ThemeProvider, ErrorBoundary, ToastProvider, _responsive, useArrayProp } from "@sanity/ui";
import startCase from "lodash/startCase.js";
import { isValidElementType } from "react-is";
import { EllipsisHorizontalIcon, ChevronRightIcon, DocumentIcon, ImageIcon, ChevronDownIcon, ErrorOutlineIcon, ArrowDownIcon, ArrowRightIcon, RevertIcon, WarningOutlineIcon, InfoOutlineIcon, CheckmarkIcon, ToggleArrowRightIcon, BulbOutlineIcon, UnknownIcon, EditIcon, TrashIcon, CloseIcon, DotIcon, HelpCircleIcon, AccessDeniedIcon, SyncIcon, LaunchIcon, AddIcon, DragHandleIcon, UploadIcon, InsertAboveIcon, InsertBelowIcon, CopyIcon, CalendarIcon, ResetIcon, DownloadIcon, ReadOnlyIcon, BinaryDocumentIcon, SearchIcon, CropIcon, LinkIcon, BoldIcon, ItalicIcon, StrikethroughIcon, UnderlineIcon, CodeIcon, OlistIcon, UlistIcon, BlockElementIcon, InlineElementIcon, CollapseIcon, ExpandIcon, EyeOpenIcon, ChevronUpIcon, DocumentsIcon, CheckmarkCircleIcon, StringIcon, NumberIcon, BlockContentIcon, UndoIcon, ChevronLeftIcon, DesktopIcon, MoonIcon, SunIcon, ControlsIcon, ClockIcon, SpinnerIcon, ArrowLeftIcon, SortIcon, DoubleChevronRightIcon, Icon, BoltIcon, CogIcon, UsersIcon, LeaveIcon, MenuIcon } from "@sanity/icons";
import uniqueId from "lodash/uniqueId.js";
import styled, { css, keyframes, createGlobalStyle } from "styled-components";
import { T as Tooltip, B as Button, L as LocaleContext, P as Popover, M as MenuGroup, c as MenuItem, d as MenuButton, b as TooltipDelayGroupProvider, D as Dialog, i as LazyTextInput, f as DatePicker$1, g as getCalendarLabels, j as isValidDate$1, e as Tab, h as getJsonStream, u as useCurrentLocale, C as ConditionalWrapper, a as useLocale } from "./getJsonStream.js";
import { createMemoryHistory, createBrowserHistory } from "history";
import { createInstance } from "i18next";
import { useTranslation as useTranslation$1, I18nextProvider, initReactI18next } from "react-i18next";
import isPlainObject from "lodash/isPlainObject.js";
import { Schema } from "@sanity/schema";
import { DEFAULT_MAX_FIELD_DEPTH, validateSchema, groupProblems } from "@sanity/schema/_internal";
import cloneDeep from "lodash/cloneDeep.js";
import get from "lodash/get.js";
import { isIndexSegment, isKeySegment, isIndexTuple, isKeyedObject, isTypedObject, isPortableTextSpan, isPortableTextTextBlock, isObjectSchemaType, isArraySchemaType, isNumberSchemaType, isBooleanSchemaType, isStringSchemaType, isPrimitiveSchemaType, isCrossDatasetReferenceSchemaType, isReferenceSchemaType, isArrayOfObjectsSchemaType, isArrayOfBlocksSchemaType, isArrayOfPrimitivesSchemaType, isReference as isReference$1, isSlug, isTitledListValue, isImage, isCrossDatasetReference, isDeprecatedSchemaType } from "@sanity/types";
import * as legacyDateFormat from "@sanity/util/legacyDateFormat";
import { format, parse, DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT } from "@sanity/util/legacyDateFormat";
import memoize$1 from "lodash/memoize.js";
import { createClientConcurrencyLimiter } from "@sanity/util/client";
import { ConcurrencyLimiter } from "@sanity/util/concurrency-limiter";
import uniqBy from "lodash/uniqBy.js";
import DataLoader from "dataloader";
import * as PathUtils from "@sanity/util/paths";
import { toString, FOCUS_TERMINATOR, startsWith, isEqual as isEqual$2, trimLeft, pathFor, get as get$1 } from "@sanity/util/paths";
import deepCompare from "react-fast-compare";
import debounce from "lodash/debounce.js";
import { getTheme_v2, rgba as rgba$1, buildTheme } from "@sanity/ui/theme";
import sortBy from "lodash/sortBy.js";
import { useVirtualizer, defaultRangeExtractor, elementScroll } from "@tanstack/react-virtual";
import throttle from "lodash/throttle";
import { observableCallback } from "observable-callback";
import { usePortableTextEditor, PortableTextEditor, usePortableTextEditorSelection, PortableTextEditable } from "@sanity/portable-text-editor";
import { COLOR_HUES, hues, yellow, purple } from "@sanity/color";
import { parseISO, getMinutes, setMinutes, isValid, isSameDay, isSameMonth, isAfter, isBefore, getWeek, startOfMonth, eachWeekOfInterval, lastDayOfMonth, addDays, set as set$1, addMonths, startOfDay, endOfDay, format as format$1, parse as parse$2, startOfToday, sub, startOfMinute, endOfMinute, differenceInMonths, differenceInYears, differenceInWeeks, differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds } from "date-fns";
import omit from "lodash/omit.js";
import { cleanupEfficiency, makeDiff, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL, applyPatches, parsePatch } from "@sanity/diff-match-patch";
import orderBy from "lodash/orderBy.js";
import { getImageDimensions, isDefaultCrop, isDefaultHotspot, isImageSource, isFileSource } from "@sanity/asset-utils";
import imageUrlBuilder from "@sanity/image-url";
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
import escapeRegExp$1 from "lodash/escapeRegExp.js";
import { motion, AnimatePresence } from "framer-motion";
import FocusLock from "react-focus-lock";
import uniq from "lodash/uniq.js";
import xor from "lodash/xor.js";
import { diffItem } from "sanity-diff-patch";
import shallowEquals from "shallow-equals";
import { IntentLink, useIntentLink, useRouter, useRouterState, RouteScope, useStateLink, StateLink, route, RouterProvider } from "sanity/router";
import { getDevicePixelRatio as getDevicePixelRatio$2 } from "use-device-pixel-ratio";
import classNames from "classnames";
import groupBy from "lodash/groupBy.js";
import scrollIntoView from "scroll-into-view-if-needed";
import compact from "lodash/compact.js";
import intersection from "lodash/intersection.js";
import keyBy from "lodash/keyBy.js";
import partition from "lodash/partition.js";
import toLower from "lodash/toLower.js";
import union from "lodash/union.js";
import words from "lodash/words.js";
import flow from "lodash/flow.js";
import trim from "lodash/trim.js";
import isFinite from "lodash/isFinite.js";
import { uuid } from "@sanity/uuid";
import { Mutation, BufferedDocument } from "@sanity/mutator";
import { reduce as reduce$1 } from "json-reduce";
import { exhaustMapWithTrailing } from "rxjs-exhaustmap-with-trailing";
import { resolveTypeName as resolveTypeName$1, randomKey as randomKey$1, isShallowEmptyObject, isDeepEmpty } from "@sanity/util/content";
import isEqual$3 from "lodash/isEqual";
import startCase$1 from "lodash/startCase";
import { renderToString } from "react-dom/server";
import intersection$1 from "lodash/intersection";
import isEmpty$1 from "lodash/isEmpty";
import debug$3 from "debug";
import getRandomValues from "get-random-values-esm";
import capitalize from "lodash/capitalize.js";
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from "@dnd-kit/core";
import { restrictToHorizontalAxis, restrictToVerticalAxis } from "@dnd-kit/modifiers";
import { useSortable, SortableContext, sortableKeyboardCoordinates, horizontalListSortingStrategy, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import speakingurl from "speakingurl";
import difference from "lodash/difference.js";
import { useTelemetry, TelemetryProvider } from "@sanity/telemetry/react";
import { defineEvent, createSessionId, createBatchedStore } from "@sanity/telemetry";
import { normalizeBlock } from "@sanity/block-tools";
import find from "lodash/find.js";
import "lodash/castArray.js";
import pick from "lodash/pick.js";
import identity from "lodash/identity.js";
import values from "lodash/values.js";
import assignWith from "lodash/assignWith.js";
import "polished";
import exif from "exif-component";
import { parse as parse$1, evaluate } from "groq-js";
import { refCountDelay } from "rxjs-etc/operators";
import { applyPatch as applyPatch$1, incremental } from "mendoza";
import { diffInput, wrap as wrap$1 } from "@sanity/diff";
import { nanoid } from "nanoid";
import sample from "lodash/sample.js";
import raf from "raf";
import { darken, lighten, hasBadContrast, readableColor } from "color2k";
import "react-dom/client";
import Refractor from "react-refractor";
import bash from "refractor/lang/bash";
import javascript from "refractor/lang/javascript";
import json from "refractor/lang/json";
import jsx2 from "refractor/lang/jsx";
import typescript from "refractor/lang/typescript";
import { PortableText as PortableText$1 } from "@portabletext/react";
import { SanityLogo as SanityLogo$1, SanityMonogram } from "@sanity/logos";
import { generateHelpUrl } from "@sanity/generate-help-url";
import { useHotModuleReload } from "use-hot-module-reload";
import arrify from "arrify";
import isHotkey from "is-hotkey";
import upperFirst from "lodash/upperFirst.js";
import isValidDate from "date-fns/isValid";
import range from "lodash/range.js";
import isObject from "lodash/isObject.js";
import findIndex$1 from "lodash/findIndex.js";
import clone from "lodash/clone.js";
const ConnectorContext = createContext({
isReviewChangesOpen: !1,
onOpenReviewChanges: () => {
},
onSetFocus: () => {
}
});
function createStore() {
const reportedValues = /* @__PURE__ */ new Map(), { publish: publish2, subscribe } = createPubSub(), debouncedPublish = debounce(publish2, 10, { trailing: !0 }), read2 = () => Array.from(reportedValues.entries());
function add(id2, value) {
reportedValues.has(id2), reportedValues.set(id2, value), debouncedPublish(read2());
}
function update(id2, value) {
reportedValues.has(id2), reportedValues.set(id2, value), debouncedPublish(read2());
}
function remove2(id2) {
reportedValues.has(id2), reportedValues.delete(id2), debouncedPublish(read2());
}
return {
add,
remove: remove2,
update,
read: read2,
subscribe
};
}
function isFunc(value) {
return typeof value == "function";
}
function read(value) {
return isFunc(value) ? value() : value;
}
const noop$4 = () => {
};
function createUseReporter(Context2) {
return function(id2, value, isEqual2 = Object.is) {
const { add, update, remove: remove2 } = useContext(Context2), previous = useRef();
useLayoutEffect(() => {
if (id2 === null)
return noop$4;
const current = read(value);
return add(id2, current), previous.current = current, () => {
remove2(id2);
};
}, [add, id2, remove2, value]), useLayoutEffect(() => {
const current = read(value);
typeof previous.current < "u" && !isEqual2(previous.current, current) && id2 !== null && update(id2, current), previous.current = current;
});
};
}
let didWarn = !1;
const useReporterGuard = (id2) => {
didWarn || console.warn(
new Error(
`No context provided for reporter. Make sure that the component calling "useReporter(${id2}, ...)", is wrapped in a <Tracker> element`
)
), didWarn = !0;
};
function useReportedValueGuard() {
return didWarn || console.warn(
new Error(
'No context provided for reporter. Make sure that the component calling "useReportedValues()", is wrapped inside a <Tracker> element'
)
), didWarn = !0, [];
}
const useSubscribeGuard = () => (didWarn || console.warn(
new Error(
'No context provided for reporter. Make sure that the component calling "useReportedValues()", is wrapped inside a <Tracker> element'
)
), didWarn = !0, () => {
}), DEFAULT_CONTEXT = {
add: useReporterGuard,
update: useReporterGuard,
remove: useReporterGuard,
subscribe: useSubscribeGuard,
read: useReportedValueGuard
};
let id$1 = 0;
const getNextId = () => ++id$1;
function createTrackerScope() {
const Context2 = createContext(DEFAULT_CONTEXT);
function useReportedValues2() {
const context = useContext(Context2), [values2, setValues] = useState(context.read());
return useLayoutEffect(() => (setValues(context.read()), context.subscribe(setValues)), [context]), values2;
}
function Tracker2(props2) {
const store = useMemo(() => createStore(), []);
return /* @__PURE__ */ jsx(Context2.Provider, { value: store, children: props2.children });
}
const useReporter2 = createUseReporter(Context2);
return {
Tracker: Tracker2,
useReportedValues: useReportedValues2,
useReporter: useReporter2,
useAutoIdReporter: (value, isEqual2 = Object.is) => useReporter2(`element-${useRef(getNextId()).current}`, value, isEqual2)
};
}
const CORNER_RADIUS = 4, INTERACTIVE_STROKE_WIDTH = 16, CONNECTOR_MARGIN = 8, ARROW_MARGIN_X = 8, ARROW_MARGIN_Y = 2, ARROW_SIZE = 4, ARROW_THRESHOLD = 12, STROKE_WIDTH$2 = 1, DEBUG$1 = !1, DEBUG_LAYER_BOUNDS = !1, trackerScope = createTrackerScope(), Tracker$1 = trackerScope.Tracker, useReportedValues$1 = trackerScope.useReportedValues, useReporter$1 = trackerScope.useReporter;
function useUnique(value) {
const valueRef = useRef(value);
return isEqual$1(valueRef.current, value) || (valueRef.current = value), valueRef.current;
}
function createHookFromObservableFactory(observableFactory, initialValue) {
const initialLoadingTuple = [initialValue, !0], initialResult = { type: "tuple", tuple: initialLoadingTuple };
return function(_arg) {
const memoArg = useUnique(_arg), result = useMemoObservable(
() => of(memoArg).pipe(
switchMap(
(arg) => concat(
of({ type: "loading" }),
observableFactory(arg).pipe(map((value) => ({ type: "value", value })))
)
),
scan(([prevValue], next) => next.type === "loading" ? [prevValue, !0] : [next.value, !1], initialLoadingTuple),
distinctUntilChanged(([prevValue, prevIsLoading], [nextValue, nextIsLoading]) => !(prevValue !== nextValue || prevIsLoading !== nextIsLoading)),
map((tuple) => ({ type: "tuple", tuple })),
catchError((error) => of({ type: "error", error }))
),
[memoArg],
initialResult
);
if (result.type === "error")
throw result.error;
return result.tuple;
};
}
function isNonNullable$2(value) {
return value != null;
}
const DRAFTS_FOLDER = "drafts", DRAFTS_PREFIX = `${DRAFTS_FOLDER}.`;
function documentIdEquals(documentId, equalsDocumentId) {
return getPublishedId(documentId) === getPublishedId(equalsDocumentId);
}
function isDraft(document2) {
return isDraftId(document2._id);
}
function isDraftId(id2) {
return id2.startsWith(DRAFTS_PREFIX);
}
function getIdPair(id2) {
return {
draftId: getDraftId(id2),
publishedId: getPublishedId(id2)
};
}
function isPublishedId(id2) {
return !isDraftId(id2);
}
function getDraftId(id2) {
return isDraftId(id2) ? id2 : DRAFTS_PREFIX + id2;
}
function getPublishedId(id2) {
return isDraftId(id2) ? id2.slice(DRAFTS_PREFIX.length) : id2;
}
function createDraftFrom(document2) {
return {
...document2,
_id: getDraftId(document2._id)
};
}
function newDraftFrom(document2) {
return {
...document2,
_id: DRAFTS_PREFIX
};
}
function createPublishedFrom(document2) {
return {
...document2,
_id: getPublishedId(document2._id)
};
}
function collate(documents) {
const byId = documents.reduce((res, doc) => {
const publishedId = getPublishedId(doc._id);
let entry = res.get(publishedId);
return entry || (entry = { id: publishedId, type: doc._type, published: void 0, draft: void 0 }, res.set(publishedId, entry)), entry[publishedId === doc._id ? "published" : "draft"] = doc, res;
}, /* @__PURE__ */ new Map());
return Array.from(byId.values());
}
function removeDupes(documents) {
return collate(documents).map((entry) => entry.draft || entry.published).filter(isNonNullable$2);
}
const EMPTY_OBJECT = Object.freeze({}), EMPTY_ARRAY$b = Object.freeze([]);
function getGlobalScope() {
if (typeof globalThis < "u")
return globalThis;
if (typeof window < "u")
return window;
if (typeof self < "u")
return self;
if (typeof global < "u")
return global;
throw new Error("@sanity/ui: could not locate global scope");
}
const globalScope = getGlobalScope();
function isRecord$4(value) {
return !!value && typeof value == "object" && !Array.isArray(value);
}
function isString(value) {
return typeof value == "string";
}
const ResizeObserver = typeof document == "object" && typeof window == "object" && window.ResizeObserver ? window.ResizeObserver : ResizeObserver$1, createSharedResizeObserver = () => {
const event = createPubSub(), resizeObserver2 = new ResizeObserver(
(entries) => event.publish(entries)
);
return {
observe: (element, observer, options) => {
const unsubscribe = event.subscribe((entries) => {
const entry = entries.find((e) => e.target === element);
entry && observer(entry);
});
return resizeObserver2.observe(element, options), () => {
unsubscribe(), resizeObserver2.unobserve(element);
};
},
unobserve: (element) => resizeObserver2.unobserve(element)
};
}, resizeObserver = createSharedResizeObserver(), GROQ_KEYWORDS = ["match", "in", "asc", "desc", "true", "false", "null"], VALID_FIELD = /^[a-zA-Z_][a-zA-Z0-9_]*$/, fieldNeedsEscape = (fieldName) => !VALID_FIELD.test(fieldName) || GROQ_KEYWORDS.includes(fieldName), escapeField = (fieldName) => `["${fieldName}"]`, escapeFirst = (fieldName) => `@${escapeField(fieldName)}`, isEmptyArray = (value) => Array.isArray(value) && value.length === 0, joinPath = (pathArray) => {
let path = "";
for (let i = 0; i < pathArray.length; i++) {
const pathSegment = pathArray[i];
if (isEmptyArray(pathSegment)) {
path += "[]";
continue;
}
if (typeof pathSegment == "number") {
path += `[${pathSegment}]`;
continue;
}
const isFirst = i === 0;
fieldNeedsEscape(pathSegment) ? path = isFirst ? escapeFirst(pathSegment) : `${path}${escapeField(pathSegment)}` : path = isFirst ? pathSegment : `${path}.${pathSegment}`;
}
return path;
}, supportsTouch = isTouchDevice$1();
function isTouchDevice$1() {
return typeof window > "u" ? !1 : "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
}
const segmenter = typeof Intl == "object" && "Segmenter" in Intl ? new Intl.Segmenter() : void 0;
function sliceString(str, start, end) {
if (end < start)
throw new Error(
"End must be greater than start, use `String.prototype.slice()` for negative values"
);
if (!segmenter)
return str.slice(start, end);
let i = 0, sliced = "";
for (const value of segmenter.segment(str)) {
if (i === end)
return sliced;
sliced += value.segment, i++;
}
return sliced;
}
function truncateString(str, maxLength) {
const truncated = sliceString(str, 0, maxLength);
return truncated === str ? truncated : `${truncated}\u2026`;
}
const LOADING_STATE = {
isLoading: !0,
value: void 0,
error: null
};
function useLoadable(value$, initialValue) {
return useMemoObservable(() => value$.pipe(asLoadable()), [value$], typeof initialValue > "u" ? LOADING_STATE : { isLoading: !1, value: initialValue, error: null });
}
function asLoadable() {
return (value$) => value$.pipe(
map((value) => ({ isLoading: !1, value, error: null })),
catchError(
(error) => of({ isLoading: !1, value: void 0, error })
)
);
}
function userHasRole(user, roleId) {
return user !== null && user.roles.some((role) => role.name === roleId);
}
var _a$1;
const DEBUG_MODE$2 = typeof process > "u" ? !1 : (_a$1 = process == null ? void 0 : process.env) == null ? void 0 : _a$1.SANITY_STUDIO_DEBUG_I18N, DEBUG_I18N = !!DEBUG_MODE$2, debugWrappers = {
reverse: (str) => `\u202E${str}`,
triangles: (str) => `\u25E4 ${str} \u25E2`
};
function maybeWrapT(t2) {
const wrapper = DEBUG_MODE$2 === "reverse" || DEBUG_MODE$2 === "triangles" ? debugWrappers[DEBUG_MODE$2] : null;
return wrapper ? (...args) => wrapper(t2(...args)) : t2;
}
const translationOptionOverrides = {
// We're manually forcing a re-render with the locale key in the LocaleProvider,
// so we don't need to bind to the i18n instance for language change events.
bindI18n: !1
};
function useTranslation(ns, options) {
const { t: t2 } = useTranslation$1(
ns,
options ? { keyPrefix: options.keyPrefix, lng: options.lng, ...translationOptionOverrides } : translationOptionOverrides
);
return { t: maybeWrapT(t2) };
}
const animationSpeed = 250, ChangeBarWrapper$1 = styled.div(
({ $changed, $disabled, $hasFocus, $isReviewChangeOpen }) => $disabled ? css`
${ChangeBarMarker}:after {
display: none;
}
` : css`
--change-bar-offset: 4px;
display: flex;
position: relative;
${ChangeBarMarker}:after {
opacity: 0.5;
}
@media (hover: hover) {
&:hover {
z-index: 10;
${ChangeBarMarker}:after {
opacity: 1;
}
}
}
/* hide when field is not changed */
${$hasFocus && css`
${ChangeBarMarker}:after {
opacity: 1;
}
`}
/* hide when field is not changed */
${!$changed && css`
${ChangeBarMarker}:after {
opacity: 0;
pointer-events: none;
}
`}
/* hide hover effect when review changes is open */
${$isReviewChangeOpen && css`
${ChangeBarButton} {
opacity: 0;
}
`}
`
), FieldWrapper = styled.div`
flex-grow: 1;
min-width: 0;
`, ChangeBar = styled.div`
position: relative;
opacity: 1;
transition: opacity 100ms;
z-index: ${({ $zIndex }) => $zIndex};
`, ChangeBarMarker = styled.div((props2) => {
const { media } = getTheme_v2(props2.theme);
return css`
position: absolute;
top: -1px;
left: var(--change-bar-offset);
width: 1px;
bottom: -1px;
background-color: var(--card-bg-color);
@media (min-width: ${media[0]}px) {
display: unset;
}
&:after {
content: '';
display: block;
position: absolute;
top: 1px;
left: 0;
width: 1px;
bottom: 1px;
background-color: var(--card-badge-caution-dot-color);
border-radius: 0.5px;
}
`;
}), ChangeBarButton = styled.button((props2) => {
const { $withHoverEffect } = props2;
return css`
appearance: none;
border: 0;
outline: 0;
display: block;
padding: 0;
background: transparent;
opacity: 0;
position: absolute;
height: 100%;
cursor: pointer;
pointer-events: all;
left: calc(-0.25rem + var(--change-bar-offset));
width: calc(1rem - 1px);
transition: opacity ${animationSpeed}ms;
&:focus {
border: 0;
outline: 0;
}
&:focus {
border: 0;
outline: 0;
}
${$withHoverEffect && css`
@media (hover: hover) {
&:hover {
opacity: 0.2;
}
}
`}
`;
});
function ElementWithChangeBar(props2) {
const { children, disabled, hasFocus, isChanged, withHoverEffect = !0 } = props2, { onOpenReviewChanges, isReviewChangesOpen } = useContext(ConnectorContext), { zIndex } = useLayer(), { t: t2 } = useTranslation(), changeBar = useMemo(
() => disabled || !isChanged ? null : /* @__PURE__ */ jsxs(ChangeBar, { "data-testid": "change-bar", $zIndex: zIndex, children: [
/* @__PURE__ */ jsx(ChangeBarMarker, { "data-testid": "change-bar__marker" }),
/* @__PURE__ */ jsx(Tooltip, { content: t2("changes.change-bar.aria-label"), portal: !0, children: /* @__PURE__ */ jsx(
ChangeBarButton,
{
"aria-label": t2("changes.change-bar.aria-label"),
"data-testid": "change-bar__button",
onClick: isReviewChangesOpen ? void 0 : onOpenReviewChanges,
tabIndex: -1,
type: "button",
$withHoverEffect: withHoverEffect
}
) })
] }),
[disabled, isChanged, isReviewChangesOpen, onOpenReviewChanges, t2, withHoverEffect, zIndex]
);
return /* @__PURE__ */ jsxs(
ChangeBarWrapper$1,
{
"data-testid": "change-bar-wrapper",
$changed: isChanged,
$disabled: disabled,
$hasFocus: hasFocus,
$isReviewChangeOpen: isReviewChangesOpen,
children: [
/* @__PURE__ */ jsx(FieldWrapper, { "data-testid": "change-bar__field-wrapper", children }),
changeBar
]
}
);
}
const ChangeBarWrapper = memo(function(props2) {
const {
children,
disabled,
hasFocus,
isChanged,
onMouseEnter: onMouseEnterProp,
onMouseLeave: onMouseLeaveProp,
path = EMPTY_ARRAY$b,
withHoverEffect,
...restProps
} = props2, layer = useLayer(), [hasHover, setHover] = useState(!1), onMouseEnter = useCallback(
(event) => {
onMouseEnterProp == null || onMouseEnterProp(event), setHover(!0);
},
[onMouseEnterProp]
), onMouseLeave = useCallback(
(event) => {
onMouseLeaveProp == null || onMouseLeaveProp(event), setHover(!1);
},
[onMouseLeaveProp]
), ref = useRef(null);
return useReporter$1(
disabled ? null : `field-${PathUtils.toString(path)}`,
() => ({
element: ref.current,
path,
isChanged,
hasFocus,
hasHover,
zIndex: layer.zIndex
}),
deepCompare
// note: deepCompare should be ok here since we're not comparing deep values
), /* @__PURE__ */ jsx("div", { ...restProps, ref, onMouseEnter, onMouseLeave, children: /* @__PURE__ */ jsx(
ElementWithChangeBar,
{
hasFocus,
isChanged,
disabled,
withHoverEffect,
children
}
) });
});
function ChangeIndicator(props2) {
const { children, hasFocus, isChanged, path, withHoverEffect, ...restProps } = props2;
return /* @__PURE__ */ jsx(
ChangeBarWrapper,
{
...restProps,
path,
hasFocus,
isChanged,
withHoverEffect,
children
}
);
}
const ChangeIndicatorContext = createContext({
path: [],
fullPath: [],
focusPath: [],
isChanged: !1
}), ScrollContext = createContext(null), noop$3 = () => {
}, ScrollContainer = forwardRef(function(props2, ref) {
const { as = "div", onScroll, ...rest } = props2, forwardedRef = useForwardedRef(ref), parentContext = useContext(ScrollContext), childContext = useMemo(() => createPubSub(), []);
return useEffect(() => onScroll ? childContext.subscribe(onScroll) : noop$3, [childContext, onScroll]), useEffect(() => parentContext ? childContext.subscribe(parentContext.publish) : noop$3, [parentContext, childContext]), useEffect(() => {
const handleScroll = (event) => {
childContext.publish(event);
}, el = forwardedRef.current;
if (el)
return el.addEventListener("scroll", handleScroll, {
passive: !0,
capture: !0
}), () => {
el.removeEventListener("scroll", handleScroll);
};
}, [childContext, forwardedRef]), /* @__PURE__ */ jsx(ScrollContext.Provider, { value: childContext, children: createElement(as, { ref: forwardedRef, "data-testid": "scroll-container", ...rest }) });
});
function ClampedRect(props2) {
const { bounds, ...rest } = props2, x = Math.max(bounds.left, props2.left), y = Math.max(props2.top, bounds.top), height = Math.max(0, props2.height - (y - props2.top)), width = Math.max(0, props2.width - (x - props2.left));
return /* @__PURE__ */ jsx("rect", { ...rest, x, y, height, width });
}
styled.rect`
stroke: #ccc;
fill: none;
pointer-events: none;
stroke-linecap: round;
`;
const ConnectorPath = styled.path`
fill: none;
pointer-events: none;
stroke-linejoin: round;
stroke: var(--card-badge-caution-dot-color);
`, InteractivePath = styled.path`
fill: none;
pointer-events: stroke;
stroke: transparent;
cursor: pointer;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0;
&:hover {
opacity: 0.2;
}
`, RightBarWrapper = styled(ClampedRect)`
stroke: none;
pointer-events: none;
fill: var(--card-badge-caution-dot-color);
`;
function arrowPath(x, y, dir) {
return [
`M ${x - ARROW_SIZE} ${y - ARROW_SIZE * dir} `,
`L ${x} ${y}`,
`L ${x + ARROW_SIZE} ${y - ARROW_SIZE * dir}`
].join("");
}
function moveTo(x, y) {
return `M${x} ${y}`;
}
function lineTo(x, y) {
return `L${x} ${y}`;
}
function join(strings, delim = "") {
return strings.join(delim);
}
function quadCurve(x1, y1, x, y) {
return `Q${x1} ${y1} ${x} ${y}`;
}
function generateConnectorPath(line) {
const { from: from2, to } = line, { left: fromX, top: fromY } = from2, { left: toX, top: toY } = to, cmds = [], r1 = Math.min(CORNER_RADIUS, Math.abs(fromY - toY) / 2);
return from2.isAbove ? cmds.push(
moveTo(fromX + ARROW_MARGIN_X, fromY - ARROW_THRESHOLD + ARROW_MARGIN_Y),
lineTo(fromX + ARROW_MARGIN_X, fromY - CORNER_RADIUS),
quadCurve(fromX + ARROW_MARGIN_X, fromY, fromX + ARROW_MARGIN_X + CORNER_RADIUS, fromY)
) : from2.isBelow ? cmds.push(
moveTo(fromX + ARROW_MARGIN_X, fromY + ARROW_THRESHOLD - ARROW_MARGIN_Y),
lineTo(fromX + ARROW_MARGIN_X, fromY + CORNER_RADIUS),
quadCurve(fromX + ARROW_MARGIN_X, fromY, fromX + ARROW_MARGIN_X + CORNER_RADIUS, fromY)
) : cmds.push(moveTo(fromX, fromY)), to.isAbove ? fromY < to.bounds.top ? cmds.push(
lineTo(to.bounds.left - 8 - r1, fromY),
quadCurve(to.bounds.left - 8, fromY, to.bounds.left - 8, fromY + r1),
lineTo(to.bounds.left - 8, toY - r1),
quadCurve(to.bounds.left - 8, toY, to.bounds.left - 8 + r1, toY),
lineTo(to.bounds.left + ARROW_MARGIN_X - CORNER_RADIUS, toY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
toY,
to.bounds.left + ARROW_MARGIN_X,
toY - CORNER_RADIUS
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY - ARROW_THRESHOLD + ARROW_MARGIN_Y)
) : cmds.push(
lineTo(to.bounds.left + ARROW_MARGIN_X - CORNER_RADIUS, fromY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
fromY,
to.bounds.left + ARROW_MARGIN_X,
fromY - CORNER_RADIUS
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY - ARROW_THRESHOLD + ARROW_MARGIN_Y)
) : to.isBelow ? fromY > to.bounds.top + to.bounds.height ? cmds.push(
lineTo(to.bounds.left - ARROW_MARGIN_X - r1, fromY),
quadCurve(
to.bounds.left - ARROW_MARGIN_X,
fromY,
to.bounds.left - ARROW_MARGIN_X,
fromY - r1
),
lineTo(to.bounds.left - ARROW_MARGIN_X, toY + r1),
quadCurve(to.bounds.left - ARROW_MARGIN_X, toY, to.bounds.left - ARROW_MARGIN_X + r1, toY),
lineTo(to.bounds.left + ARROW_MARGIN_X - CORNER_RADIUS, toY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
toY,
to.bounds.left + ARROW_MARGIN_X,
toY + CORNER_RADIUS
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY + ARROW_THRESHOLD - ARROW_MARGIN_Y)
) : cmds.push(
lineTo(to.bounds.left + ARROW_MARGIN_X - CORNER_RADIUS, fromY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
fromY,
to.bounds.left + ARROW_MARGIN_X,
fromY + CORNER_RADIUS
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY + ARROW_THRESHOLD - ARROW_MARGIN_Y)
) : fromY < toY ? cmds.push(
lineTo(to.bounds.left + ARROW_MARGIN_X - r1, fromY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
fromY,
to.bounds.left + ARROW_MARGIN_X,
fromY + r1
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY - r1),
quadCurve(to.bounds.left + ARROW_MARGIN_X, toY, to.bounds.left + ARROW_MARGIN_X + r1, toY),
lineTo(toX, toY)
) : cmds.push(
lineTo(to.bounds.left + ARROW_MARGIN_X - r1, fromY),
quadCurve(
to.bounds.left + ARROW_MARGIN_X,
fromY,
to.bounds.left + ARROW_MARGIN_X,
fromY - r1
),
lineTo(to.bounds.left + ARROW_MARGIN_X, toY + r1),
quadCurve(to.bounds.left + ARROW_MARGIN_X, toY, to.bounds.left + ARROW_MARGIN_X + r1, toY),
lineTo(toX, toY)
), join(cmds);
}
function getConnectorLinePoint(rect, bounds) {
const centerY = rect.top + rect.height / 2, isAbove = rect.top + rect.height < bounds.top + ARROW_MARGIN_Y, isBelow = rect.top > bounds.top + bounds.height - ARROW_MARGIN_Y;
return {
bounds,
left: rect.left,
top: centerY,
centerY,
startY: rect.top + CONNECTOR_MARGIN,
endY: rect.top + rect.height - CONNECTOR_MARGIN,
isAbove,
isBelow,
outOfBounds: isAbove || isBelow
};
}
function mapConnectorToLine(connector) {
const fromBounds = {
top: connector.from.bounds.top + ARROW_THRESHOLD,
bottom: connector.from.bounds.top + connector.from.bounds.height - ARROW_THRESHOLD,
left: connector.from.bounds.left,
right: connector.from.bounds.left + connector.from.bounds.width,
width: connector.from.bounds.width,
height: connector.from.bounds.height - ARROW_THRESHOLD * 2
}, from2 = getConnectorLinePoint(connector.from.rect, fromBounds);
from2.left = connector.from.rect.left + connector.from.rect.width + 1;
const toBounds = {
top: connector.to.bounds.top + ARROW_THRESHOLD,
bottom: connector.to.bounds.top + connector.to.bounds.height - ARROW_THRESHOLD,
left: connector.to.bounds.left,
right: connector.to.bounds.left + connector.to.bounds.width,
width: connector.to.bounds.width,
height: connector.to.bounds.height - ARROW_THRESHOLD * 2
}, to = getConnectorLinePoint(connector.to.rect, toBounds), maxStartY = Math.max(to.startY, from2.startY);
return from2.top = Math.min(maxStartY, from2.endY), from2.top < toBounds.top ? from2.top = Math.min(toBounds.top, from2.endY) : from2.top > toBounds.bottom && (from2.top = Math.max(toBounds.bottom, from2.startY)), to.top = Math.min(maxStartY, to.endY), to.top < fromBounds.top ? to.top = Math.min(fromBounds.top, to.endY) : to.top > fromBounds.bottom && (to.top = Math.max(fromBounds.bottom, to.startY)), from2.top = Math.min(Math.max(from2.top, fromBounds.top), fromBounds.bottom), to.top = Math.min(Math.max(to.top, toBounds.top), toBounds.bottom), { from: from2, to };
}
const Connector = memo(function({ from: from2, to }) {
const line = mapConnectorToLine({ from: from2, to });
if (line.from.outOfBounds && line.to.outOfBounds)
return null;
const linePathDescription = generateConnectorPath(line);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(InteractivePath, { d: linePathDescription, strokeWidth: INTERACTIVE_STROKE_WIDTH }),
/* @__PURE__ */ jsx(ConnectorPath, { d: linePathDescription, strokeWidth: STROKE_WIDTH$2 }),
/* @__PURE__ */ jsx(
RightBarWrapper,
{
top: to.rect.top,
left: to.rect.left - 0.5,
height: to.rect.height,
width: STROKE_WIDTH$2,
bounds: to.bounds
}
),
line.from.isAbove && /* @__PURE__ */ jsx(
ConnectorPath,
{
d: arrowPath(
line.from.left + ARROW_MARGIN_X,
line.from.bounds.top - ARROW_THRESHOLD + ARROW_MARGIN_Y,
-1
),
strokeWidth: STROKE_WIDTH$2
}
),
line.from.isBelow && /* @__PURE__ */ jsx(
ConnectorPath,
{
d: arrowPath(
line.from.left + ARROW_MARGIN_X,
line.from.bounds.top + line.from.bounds.height + ARROW_THRESHOLD - ARROW_MARGIN_Y,
1
),
strokeWidth: STROKE_WIDTH$2
}
),
line.to.isAbove && /* @__PURE__ */ jsx(
ConnectorPath,
{
d: arrowPath(
line.to.bounds.left + ARROW_MARGIN_X,
line.to.bounds.top - ARROW_THRESHOLD + ARROW_MARGIN_Y,
-1
),
strokeWidth: STROKE_WIDTH$2
}
),
line.to.isBelow && /* @__PURE__ */ jsx(
ConnectorPath,
{
d: arrowPath(
line.to.bounds.left + ARROW_MARGIN_X,
line.to.bounds.top + line.to.bounds.height + ARROW_THRESHOLD - ARROW_MARGIN_Y,
1
),
strokeWidth: STROKE_WIDTH$2
}
),
DEBUG$1
] });
}), SvgWrapper = styled.svg`
pointer-events: none;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
`;
function focusRingBorderStyle$2(border) {
return `inset 0 0 0 ${border.width}px ${border.color}`;
}
const AlignedBottomGrid$1 = styled(Grid)`
align-items: flex-end;
`;
function focusRingStyle$2(opts) {
const { base, border, focusRing } = opts, focusRingOutsetWidth = focusRing.offset + focusRing.width, focusRingInsetWidth = 0 - focusRing.offset, bgColor = base ? base.bg : "var(--card-bg-color)";
return [
focusRingInsetWidth > 0 && `inset 0 0 0 ${focusRingInsetWidth}px var(--card-focus-ring-color)`,
border && focusRingBorderStyle$2(border),
focusRingInsetWidth < 0 && `0 0 0 ${0 - focusRingInsetWidth}px ${bgColor}`,
focusRingOutsetWidth > 0 && `0 0 0 ${focusRingOutsetWidth}px var(--card-focus-ring-color)`
].filter(Boolean).join(",");
}
const LIST_ITEM_DATA_ATTR_ACTIVE = "data-active", LIST_ITEM_INTERACTIVE_SELECTOR = "a,button", FocusOverlayDiv = styled.div(({ theme, offset }) => css`
bottom: ${-offset}px;
border-radius: ${rem(theme.sanity.radius[1])};
left: ${-offset}px;
pointer-events: none;
position: absolute;
right: ${-offset}px;
top: ${-offset}px;
z-index: 2;
${VirtualListBox}:focus-visible & {
box-shadow: ${focusRingStyle$2({
base: theme.sanity.color.base,
focusRing: theme.sanity.focusRing
})};
}
`), PointerOverlayDiv = styled.div`
bottom: 0;
display: none;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1;
@media (hover: hover) {
&[data-enabled='true'] {
display: block;
}
}
`, VirtualListBox = styled(Box)`
height: 100%;
outline: none;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
width: 100%;
`, VirtualListChildBox = styled(Box).attrs(({ $height }) => ({
style: { height: `${$height}px` }
}))`
position: relative;
width: 100%;
`, CommandList = forwardRef(function({
activeItemDataAttr = LIST_ITEM_DATA_ATTR_ACTIVE,
ariaLabel,
ariaMultiselectable = !1,
autoFocus,
canReceiveFocus,
fixedHeight,
focusRingOffset = 0,
getItemDisabled,
getItemKey: getItemKey2,
getItemSelected,
initialIndex,
initialScrollAlign = "start",
inputElement,
itemHeight,
items,
onEndReached,
onEndReachedIndexOffset = 0,
onlyShowSelectionWhenActive,
overscan,
renderItem,
wrapAround = !0,
...responsivePaddingProps
}, ref) {
const isMountedRef = useRef(!1), commandListId = useRef(useId()), activeIndexRef = useRef(initialIndex != null ? initialIndex : 0), [childContainerElement, setChildContainerElement] = useState(null), [hovered, setHovered] = useState(!1), [pointerOverlayElement, setPointerOverlayElement] = useState(null), [virtualListElement, setVirtualListElement] = useState(null), handleChange = useCallback(
(v) => {
if (!onEndReached)
return;
const [lastItem] = [...v.getVirtualItems()].reverse();
lastItem && lastItem.index >= items.length - onEndReachedIndexOffset - 1 && isMountedRef.current && onEndReached();
},
[onEndReached, items.length, onEndReachedIndexOffset]
), virtualizer = useVirtualizer({
count: items.length,
getItemKey: getItemKey2,
getScrollElement: () => virtualListElement,
estimateSize: () => itemHeight,
onChange: handleChange,
overscan
}), itemIndices = useMemo(() => {
let i = -1;
return items.reduce((acc, _, index) => {
var _a2, _b;
const disabled = (_a2 = getItemDisabled == null ? void 0 : getItemDisabled(index)) != null ? _a2 : !1, selected = (_b = getItemSelected == null ? void 0 : getItemSelected(index)) != null ? _b : !1;
return disabled || (i += 1), acc[index] = {
activeIndex: disabled ? null : i,
disabled,
selected
}, acc;
}, []);
}, [getItemDisabled, getItemSelected, items]), activeItemCount = useMemo(
() => itemIndices.filter((v) => !v.disabled).length,
[itemIndices]
), enableChildContainerPointerEvents = useCallback(
(enabled) => pointerOverlayElement == null ? void 0 : pointerOverlayElement.setAttribute("data-enabled", (!enabled).toString()),
[pointerOverlayElement]
), getChildDescendantId = useCallback(
(index) => `${commandListId.current}-item-${index}`,
[]
), getCommandListChildrenId = useCallback(() => `${commandListId.current}-children`, []), showChildrenActiveState = useCallback(() => {
const hasFocus = [inputElement, virtualListElement].some((el) => document.activeElement === el);
if (onlyShowSelectionWhenActive && !hasFocus && !hovered)
return;
const childElements = Array.from((childContainerElement == null ? void 0 : childContainerElement.children) || []);
childElements == null || childElements.forEach((child) => {
var _a2, _b, _c;
const virtualIndex = Number((_a2 = child.dataset) == null ? void 0 : _a2.index), targetIndex = (_b = itemIndices[virtualIndex]) == null ? void 0 : _b.activeIndex;
(_c = child.querySelector(LIST_ITEM_INTERACTIVE_SELECTOR)) == null || _c.toggleAttribute(activeItemDataAttr, targetIndex === activeIndexRef.current);
});
}, [
activeItemDataAttr,
childContainerElement == null ? void 0 : childContainerElement.children,
hovered,
inputElement,
itemIndices,
onlyShowSelectionWhenActive,
virtualListElement
]), hideChildrenActiveState = useCallback(() => {
const childElements = Array.from((childContainerElement == null ? void 0 : childContainerElement.children) || []);
childElements == null || childElements.forEach((child) => {
var _a2;
(_a2 = child.querySelector(LIST_ITEM_INTERACTIVE_SELECTOR)) == null || _a2.toggleAttribute(activeItemDataAttr, !1);
});
}, [activeItemDataAttr, childContainerElement == null ? void 0 : childContainerElement.children]), refreshChildrenActiveStateThrottled = useMemo(() => throttle(showChildrenActiveState, 200), [showChildrenActiveState]), handleUpdateActiveDescendant = useCallback(() => {
const activeIndex = activeIndexRef == null ? void 0 : activeIndexRef.current;
items.length > 0 ? (inputElement == null || inputElement.setAttribute("aria-activedescendant", getChildDescendantId(activeIndex)), virtualListElement == null || virtualListElement.setAttribute("aria-activedescendant", getChildDescendantId(activeIndex))) : (inputElement == null || inputElement.removeAttribute("aria-activedescendant"), virtualListElement == null || virtualListElement.removeAttribute("aria-activedescendant"));
}, [getChildDescendantId, inputElement, items.length, virtualListElement]), handleGetTopIndex = useCallback(() => {
var _a2, _b;
const childContainerParentElement = childContainerElement == null ? void 0 : childContainerElement.parentElement;
if (childContainerElement && childContainerParentElement) {
const offset = childContainerParentElement.getBoundingClientRect().top - childContainerElement.getBoundingClientRect().top;
return (_b = (_a2 = virtualizer.getVirtualItemForOffset(offset)) == null ? void 0 : _a2.index) != null ? _b : -1;
}
return -1;
}, [childContainerElement, virtualizer]), setActiveIndex = useCallback(
({
index,
scrollAlign,
scrollIntoView: scrollIntoView2 = !0
}) => {
if (activeIndexRef.current = index, handleUpdateActiveDescendant(), showChildrenActiveState(), scrollIntoView2) {
const virtualListIndex = itemIndices.findIndex((i) => i.activeIndex === index);
virtualListIndex > -1 && virtualizer.scrollToIndex(virtualListIndex, scrollAlign ? { align: scrollAlign } : {});
}
},
[handleUpdateActiveDescendant, itemIndices, showChildrenActiveState, virtualizer]
), selectAdjacentItemIndex = useCallback(
(direction) => {
let nextIndex = -1;
const lastIndex = activeItemCount - 1;
if (direction === "next") {
const wrapAroundIndex = wrapAround ? 0 : lastIndex;
nextIndex = activeIndexRef.current < activeItemCount - 1 ? activeIndexRef.current + 1 : wrapAroundIndex;
}
if (direction === "previous") {
const wrapAroundIndex = wrapAround ? lastIndex : 0;
nextIndex = activeIndexRef.current > 0 ? activeIndexRef.current - 1 : wrapAroundIndex;
}
setActiveIndex({ index: nextIndex, scrollIntoView: !0 }), enableChildContainerPointerEvents(!1);
},
[activeItemCount, enableChildContainerPointerEvents, setActiveIndex, wrapAround]
), focusElement = useCallback(
(type) => {
switch (type) {
case "input":
inputElement == null || inputElement.focus();
break;
case "list":
virtualListElement == null || virtualListElement.focus();
break;
}
},
[inputElement, virtualListElement]
), focusInputElement = useCallback(() => {
inputElement == null || inputElement.focus();
}, [inputElement]), focusListElement = useCallback(() => {
virtualListElement == null || virtualListElement.focus();
}, [virtualListElement]), handleChildMouseEnter = useCallback(
(index) => () => {
setActiveIndex({ index, scrollIntoView: !1 });
},
[setActiveIndex]
), handleFocus = useCallback(() => {
showChildrenActiveState();
}, [showChildrenActiveState]), handleKeyDown = useCallback(
(type) => (event) => {
const childElements = Array.from((childContainerElement == null ? void 0 : childContainerElement.children) || []);
if (childElements.length && (event.key === "ArrowDown" && (event.preventDefault(), focusElement(type), selectAdjacentItemIndex("next")), event.key === "ArrowUp" && (event.preventDefault(), focusElement(type), selectAdjacentItemIndex("previous")), event.key === "Enter")) {
event.preventDefault(), focusElement(type);
const currentElement = childElements.find(
(el) => Number(el.dataset.index) === itemIndices.findIndex((i) => i.activeIndex === activeIndexRef.current)
);
if (currentElement) {
const clickableElement = currentElement == null ? void 0 : currentElement.querySelector(
LIST_ITEM_INTERACTIVE_SELECTOR
);
clickableElement == null || clickableElement.click();
}
}
},
[childContainerElement == null ? void 0 : childContainerElement.children, focusElement, itemIndices, selectAdjacentItemIndex]
), handleKeyDownInput = useCallback(
(event) => handleKeyDown("input")(event),
[handleKeyDown]
), handleKeyDownList = useCallback(
(event) => handleKeyDown("list")(event),
[handleKeyDown]
), handleVirtualListMouseEnter = useCallback(() => {
onlyShowSelectionWhenActive && (showChildrenActiveState(), setHovered(!0));
}, [onlyShowSelectionWhenActive, showChildrenActiveState]), handleVirtualListMouseLeave = useCallback(() => {
onlyShowSelectionWhenActive && (hideChildrenActiveState(), setHovered(!1));
}, [hideChildrenActiveState, onlyShowSelectionWhenActive]);
useImperativeHandle(
ref,
() => ({
focusInputElement() {
focusInputElement();
},
focusListElement() {
focusListElement();
},
getTopIndex() {
return handleGetTopIndex();
},
scrollToIndex(index) {
setActiveIndex({ index }), enableChildContainerPointerEvents(!0);
}
}),
[
enableChildContainerPointerEvents,
focusInputElement,
focusListElement,
handleGetTopIndex,
setActiveIndex
]
), useEffect(() => {
typeof initialIndex < "u" && !isMountedRef.current && setActiveIndex({
index: initialIndex,
scrollAlign: initialScrollAlign,
scrollIntoView: !0
}), isMountedRef.current = !0;
}, [initialIndex, initialScrollAlign, onlyShowSelectionWhenActive, setActiveIndex]), useEffect(() => {
function handleMouseEvent() {
enableChildContainerPointerEvents(!0);
}
return virtualListElement == null || virtualListElement.addEventListener("mousemove", handleMouseEvent), virtualListElement == n