UNPKG

strapi-plugin-preview-button

Version:

A plugin for Strapi CMS that adds a preview button and live view button to the content manager edit view.

1,231 lines (1,228 loc) 2.97 MB
import { jsxs, jsx, Fragment as Fragment$1 } from "react/jsx-runtime"; import * as React from "react"; import React__default, { createContext, useMemo, createElement, useContext, PureComponent, useCallback, useState, useRef, useReducer, useEffect, useLayoutEffect, Component as Component$1, memo as memo$1, forwardRef, Children as Children$1, isValidElement, cloneElement, Fragment } from "react"; import { _ as _inheritsLoose, I as _extends, a as createContext$1, i as useDoc, g as useQueryParams, h as buildValidParams, q as useForm, v as useField, C as COLLECTION_TYPES, J as ForwardRef$59, B as getTranslation, k as useNotification, K as useFocusInputField, p as DocumentStatus, N as ForwardRef$3N, z as ForwardRef$3$, O as generateNKeysBetween, Q as getDefaultExportFromCjs, T as commonjsGlobal, U as getAugmentedNamespace, V as ForwardRef$3B, W as ForwardRef$1b, c as contentManagerApi, X as CLONE_PATH, Y as useAPIErrorHandler, Z as ForwardRef$4v, n as ForwardRef$3, $ as ForwardRef$2f, s as useStrapiApp, a0 as ForwardRef$45, a1 as ForwardRef$4X, a2 as ForwardRef$2v, a3 as ForwardRef$f, a4 as ForwardRef$v, a5 as ForwardRef$4d, a6 as ForwardRef$j, a7 as ForwardRef$1R, a8 as ForwardRef$4R, a9 as ForwardRef$1L, aa as ForwardRef$2F, y as ForwardRef$2l, ab as ForwardRef$11, r as useDocumentRBAC, S as SINGLE_TYPES, t as useDocLayout, M as MemoizedInputRenderer$1, ac as DOCUMENT_META_FIELDS, ad as ForwardRef$4b, ae as ForwardRef$2T, af as ForwardRef$2N, ag as ForwardRef$2P, ah as ForwardRef$2V, ai as ForwardRef$2X, aj as ForwardRef$2R, ak as ForwardRef$1t, al as ForwardRef$1d, am as ForwardRef$3v } from "./index-DX9HaYlZ.mjs"; import { Flex, TextButton, Field, Combobox, ComboboxOption, Typography, VisuallyHidden, Box, useComposedRefs, IconButton, Tooltip, Link as Link$1, BaseLink, Button, Popover, Divider, Accordion, TextInput, IconButtonGroup, Portal, FocusTrap, SingleSelect, SingleSelectOption, Grid as Grid$1, Menu, MenuItem } from "@strapi/design-system"; import pipe$1 from "lodash/fp/pipe"; import { useIntl } from "react-intl"; import { NavLink, useMatch, useLocation } from "react-router-dom"; import { styled, css, keyframes } from "styled-components"; import { u as useDragAndDrop, g as getEmptyImage, I as ItemTypes, D as DROP_SENSITIVITY, a as DIRECTIONS, b as getIn } from "./objects-D6yBsdmx-CoUb3Ffa.mjs"; import { u as useGetRelationsQuery, g as getRelationLabel, a as useLazySearchRelationsQuery, b as useDebounce, p as prefixFileUrlWithBackendUrl, c as usePrev } from "./useDebounce-DmuSJIF3-De6sMYL4.mjs"; import { C as COMPONENT_ICONS, a as ComponentIcon } from "./ComponentIcon-u4bIXTFY-BPp5f12a.mjs"; import { createDraft, finishDraft, isDraft, produce } from "immer"; import debounce from "lodash/debounce"; import throttle from "lodash/throttle"; import ReactDOM from "react-dom"; import require$$1$1 from "path"; import require$$3 from "url"; import require$$0$2 from "fs"; import require$$8 from "punycode"; function $c512c27ab02ef895$export$50c7b4e9d9f19c1(scopeName, createContextScopeDeps = []) { let defaultContexts = []; function $c512c27ab02ef895$export$fd42f52fd3ae1109(rootComponentName, defaultContext) { const BaseContext = /* @__PURE__ */ createContext(defaultContext); const index = defaultContexts.length; defaultContexts = [ ...defaultContexts, defaultContext ]; function Provider(props) { const { scope, children, ...context } = props; const Context = (scope === null || scope === void 0 ? void 0 : scope[scopeName][index]) || BaseContext; const value = useMemo( () => context, Object.values(context) ); return /* @__PURE__ */ createElement(Context.Provider, { value }, children); } function useContext$1(consumerName, scope) { const Context = (scope === null || scope === void 0 ? void 0 : scope[scopeName][index]) || BaseContext; const context = useContext(Context); if (context) return context; if (defaultContext !== void 0) return defaultContext; throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``); } Provider.displayName = rootComponentName + "Provider"; return [ Provider, useContext$1 ]; } const createScope = () => { const scopeContexts = defaultContexts.map((defaultContext) => { return /* @__PURE__ */ createContext(defaultContext); }); return function useScope(scope) { const contexts = (scope === null || scope === void 0 ? void 0 : scope[scopeName]) || scopeContexts; return useMemo( () => ({ [`__scope${scopeName}`]: { ...scope, [scopeName]: contexts } }), [ scope, contexts ] ); }; }; createScope.scopeName = scopeName; return [ $c512c27ab02ef895$export$fd42f52fd3ae1109, $c512c27ab02ef895$var$composeContextScopes(createScope, ...createContextScopeDeps) ]; } function $c512c27ab02ef895$var$composeContextScopes(...scopes) { const baseScope = scopes[0]; if (scopes.length === 1) return baseScope; const createScope1 = () => { const scopeHooks = scopes.map( (createScope) => ({ useScope: createScope(), scopeName: createScope.scopeName }) ); return function useComposedScopes(overrideScopes) { const nextScopes1 = scopeHooks.reduce((nextScopes, { useScope, scopeName }) => { const scopeProps = useScope(overrideScopes); const currentScope = scopeProps[`__scope${scopeName}`]; return { ...nextScopes, ...currentScope }; }, {}); return useMemo( () => ({ [`__scope${baseScope.scopeName}`]: nextScopes1 }), [ nextScopes1 ] ); }; }; createScope1.scopeName = baseScope.scopeName; return createScope1; } function _assertThisInitialized(e2) { if (void 0 === e2) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e2; } var safeIsNaN = Number.isNaN || function ponyfill(value) { return typeof value === "number" && value !== value; }; function isEqual(first2, second) { if (first2 === second) { return true; } if (safeIsNaN(first2) && safeIsNaN(second)) { return true; } return false; } function areInputsEqual(newInputs, lastInputs) { if (newInputs.length !== lastInputs.length) { return false; } for (var i = 0; i < newInputs.length; i++) { if (!isEqual(newInputs[i], lastInputs[i])) { return false; } } return true; } function memoizeOne(resultFn, isEqual2) { if (isEqual2 === void 0) { isEqual2 = areInputsEqual; } var lastThis; var lastArgs = []; var lastResult; var calledOnce = false; function memoized() { var newArgs = []; for (var _i = 0; _i < arguments.length; _i++) { newArgs[_i] = arguments[_i]; } if (calledOnce && lastThis === this && isEqual2(newArgs, lastArgs)) { return lastResult; } lastResult = resultFn.apply(this, newArgs); calledOnce = true; lastThis = this; lastArgs = newArgs; return lastResult; } return memoized; } var hasNativePerformanceNow = typeof performance === "object" && typeof performance.now === "function"; var now = hasNativePerformanceNow ? function() { return performance.now(); } : function() { return Date.now(); }; function cancelTimeout(timeoutID) { cancelAnimationFrame(timeoutID.id); } function requestTimeout(callback, delay) { var start2 = now(); function tick() { if (now() - start2 >= delay) { callback.call(null); } else { timeoutID.id = requestAnimationFrame(tick); } } var timeoutID = { id: requestAnimationFrame(tick) }; return timeoutID; } var size$1 = -1; function getScrollbarSize(recalculate) { if (recalculate === void 0) { recalculate = false; } if (size$1 === -1 || recalculate) { var div2 = document.createElement("div"); var style = div2.style; style.width = "50px"; style.height = "50px"; style.overflow = "scroll"; document.body.appendChild(div2); size$1 = div2.offsetWidth - div2.clientWidth; document.body.removeChild(div2); } return size$1; } var cachedRTLResult = null; function getRTLOffsetType(recalculate) { if (recalculate === void 0) { recalculate = false; } if (cachedRTLResult === null || recalculate) { var outerDiv = document.createElement("div"); var outerStyle = outerDiv.style; outerStyle.width = "50px"; outerStyle.height = "50px"; outerStyle.overflow = "scroll"; outerStyle.direction = "rtl"; var innerDiv = document.createElement("div"); var innerStyle = innerDiv.style; innerStyle.width = "100px"; innerStyle.height = "100px"; outerDiv.appendChild(innerDiv); document.body.appendChild(outerDiv); if (outerDiv.scrollLeft > 0) { cachedRTLResult = "positive-descending"; } else { outerDiv.scrollLeft = 1; if (outerDiv.scrollLeft === 0) { cachedRTLResult = "negative"; } else { cachedRTLResult = "positive-ascending"; } } document.body.removeChild(outerDiv); return cachedRTLResult; } return cachedRTLResult; } if (process.env.NODE_ENV !== "production") ; var IS_SCROLLING_DEBOUNCE_INTERVAL$1 = 150; var defaultItemKey$1 = function defaultItemKey(index, data) { return index; }; var devWarningsDirection = null; var devWarningsTagName$1 = null; if (process.env.NODE_ENV !== "production") { if (typeof window !== "undefined" && typeof window.WeakSet !== "undefined") { devWarningsDirection = /* @__PURE__ */ new WeakSet(); devWarningsTagName$1 = /* @__PURE__ */ new WeakSet(); } } function createListComponent(_ref) { var _class2; var getItemOffset2 = _ref.getItemOffset, getEstimatedTotalSize2 = _ref.getEstimatedTotalSize, getItemSize2 = _ref.getItemSize, getOffsetForIndexAndAlignment2 = _ref.getOffsetForIndexAndAlignment, getStartIndexForOffset2 = _ref.getStartIndexForOffset, getStopIndexForStartIndex2 = _ref.getStopIndexForStartIndex, initInstanceProps2 = _ref.initInstanceProps, shouldResetStyleCacheOnItemSizeChange = _ref.shouldResetStyleCacheOnItemSizeChange, validateProps2 = _ref.validateProps; return _class2 = /* @__PURE__ */ function(_PureComponent) { _inheritsLoose(List2, _PureComponent); function List2(props) { var _this; _this = _PureComponent.call(this, props) || this; _this._instanceProps = initInstanceProps2(_this.props, _assertThisInitialized(_this)); _this._outerRef = void 0; _this._resetIsScrollingTimeoutId = null; _this.state = { instance: _assertThisInitialized(_this), isScrolling: false, scrollDirection: "forward", scrollOffset: typeof _this.props.initialScrollOffset === "number" ? _this.props.initialScrollOffset : 0, scrollUpdateWasRequested: false }; _this._callOnItemsRendered = void 0; _this._callOnItemsRendered = memoizeOne(function(overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex) { return _this.props.onItemsRendered({ overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex }); }); _this._callOnScroll = void 0; _this._callOnScroll = memoizeOne(function(scrollDirection, scrollOffset, scrollUpdateWasRequested) { return _this.props.onScroll({ scrollDirection, scrollOffset, scrollUpdateWasRequested }); }); _this._getItemStyle = void 0; _this._getItemStyle = function(index) { var _this$props = _this.props, direction2 = _this$props.direction, itemSize = _this$props.itemSize, layout = _this$props.layout; var itemStyleCache = _this._getItemStyleCache(shouldResetStyleCacheOnItemSizeChange && itemSize, shouldResetStyleCacheOnItemSizeChange && layout, shouldResetStyleCacheOnItemSizeChange && direction2); var style; if (itemStyleCache.hasOwnProperty(index)) { style = itemStyleCache[index]; } else { var _offset = getItemOffset2(_this.props, index, _this._instanceProps); var size2 = getItemSize2(_this.props, index, _this._instanceProps); var isHorizontal = direction2 === "horizontal" || layout === "horizontal"; var isRtl = direction2 === "rtl"; var offsetHorizontal = isHorizontal ? _offset : 0; itemStyleCache[index] = style = { position: "absolute", left: isRtl ? void 0 : offsetHorizontal, right: isRtl ? offsetHorizontal : void 0, top: !isHorizontal ? _offset : 0, height: !isHorizontal ? size2 : "100%", width: isHorizontal ? size2 : "100%" }; } return style; }; _this._getItemStyleCache = void 0; _this._getItemStyleCache = memoizeOne(function(_, __, ___) { return {}; }); _this._onScrollHorizontal = function(event) { var _event$currentTarget = event.currentTarget, clientWidth = _event$currentTarget.clientWidth, scrollLeft = _event$currentTarget.scrollLeft, scrollWidth = _event$currentTarget.scrollWidth; _this.setState(function(prevState) { if (prevState.scrollOffset === scrollLeft) { return null; } var direction2 = _this.props.direction; var scrollOffset = scrollLeft; if (direction2 === "rtl") { switch (getRTLOffsetType()) { case "negative": scrollOffset = -scrollLeft; break; case "positive-descending": scrollOffset = scrollWidth - clientWidth - scrollLeft; break; } } scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollOffset ? "forward" : "backward", scrollOffset, scrollUpdateWasRequested: false }; }, _this._resetIsScrollingDebounced); }; _this._onScrollVertical = function(event) { var _event$currentTarget2 = event.currentTarget, clientHeight = _event$currentTarget2.clientHeight, scrollHeight = _event$currentTarget2.scrollHeight, scrollTop = _event$currentTarget2.scrollTop; _this.setState(function(prevState) { if (prevState.scrollOffset === scrollTop) { return null; } var scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollOffset ? "forward" : "backward", scrollOffset, scrollUpdateWasRequested: false }; }, _this._resetIsScrollingDebounced); }; _this._outerRefSetter = function(ref) { var outerRef = _this.props.outerRef; _this._outerRef = ref; if (typeof outerRef === "function") { outerRef(ref); } else if (outerRef != null && typeof outerRef === "object" && outerRef.hasOwnProperty("current")) { outerRef.current = ref; } }; _this._resetIsScrollingDebounced = function() { if (_this._resetIsScrollingTimeoutId !== null) { cancelTimeout(_this._resetIsScrollingTimeoutId); } _this._resetIsScrollingTimeoutId = requestTimeout(_this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL$1); }; _this._resetIsScrolling = function() { _this._resetIsScrollingTimeoutId = null; _this.setState({ isScrolling: false }, function() { _this._getItemStyleCache(-1, null); }); }; return _this; } List2.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { validateSharedProps$1(nextProps, prevState); validateProps2(nextProps); return null; }; var _proto = List2.prototype; _proto.scrollTo = function scrollTo(scrollOffset) { scrollOffset = Math.max(0, scrollOffset); this.setState(function(prevState) { if (prevState.scrollOffset === scrollOffset) { return null; } return { scrollDirection: prevState.scrollOffset < scrollOffset ? "forward" : "backward", scrollOffset, scrollUpdateWasRequested: true }; }, this._resetIsScrollingDebounced); }; _proto.scrollToItem = function scrollToItem(index, align) { if (align === void 0) { align = "auto"; } var _this$props2 = this.props, itemCount = _this$props2.itemCount, layout = _this$props2.layout; var scrollOffset = this.state.scrollOffset; index = Math.max(0, Math.min(index, itemCount - 1)); var scrollbarSize = 0; if (this._outerRef) { var outerRef = this._outerRef; if (layout === "vertical") { scrollbarSize = outerRef.scrollWidth > outerRef.clientWidth ? getScrollbarSize() : 0; } else { scrollbarSize = outerRef.scrollHeight > outerRef.clientHeight ? getScrollbarSize() : 0; } } this.scrollTo(getOffsetForIndexAndAlignment2(this.props, index, align, scrollOffset, this._instanceProps, scrollbarSize)); }; _proto.componentDidMount = function componentDidMount() { var _this$props3 = this.props, direction2 = _this$props3.direction, initialScrollOffset = _this$props3.initialScrollOffset, layout = _this$props3.layout; if (typeof initialScrollOffset === "number" && this._outerRef != null) { var outerRef = this._outerRef; if (direction2 === "horizontal" || layout === "horizontal") { outerRef.scrollLeft = initialScrollOffset; } else { outerRef.scrollTop = initialScrollOffset; } } this._callPropsCallbacks(); }; _proto.componentDidUpdate = function componentDidUpdate() { var _this$props4 = this.props, direction2 = _this$props4.direction, layout = _this$props4.layout; var _this$state = this.state, scrollOffset = _this$state.scrollOffset, scrollUpdateWasRequested = _this$state.scrollUpdateWasRequested; if (scrollUpdateWasRequested && this._outerRef != null) { var outerRef = this._outerRef; if (direction2 === "horizontal" || layout === "horizontal") { if (direction2 === "rtl") { switch (getRTLOffsetType()) { case "negative": outerRef.scrollLeft = -scrollOffset; break; case "positive-ascending": outerRef.scrollLeft = scrollOffset; break; default: var clientWidth = outerRef.clientWidth, scrollWidth = outerRef.scrollWidth; outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; break; } } else { outerRef.scrollLeft = scrollOffset; } } else { outerRef.scrollTop = scrollOffset; } } this._callPropsCallbacks(); }; _proto.componentWillUnmount = function componentWillUnmount() { if (this._resetIsScrollingTimeoutId !== null) { cancelTimeout(this._resetIsScrollingTimeoutId); } }; _proto.render = function render2() { var _this$props5 = this.props, children = _this$props5.children, className = _this$props5.className, direction2 = _this$props5.direction, height = _this$props5.height, innerRef = _this$props5.innerRef, innerElementType = _this$props5.innerElementType, innerTagName = _this$props5.innerTagName, itemCount = _this$props5.itemCount, itemData = _this$props5.itemData, _this$props5$itemKey = _this$props5.itemKey, itemKey = _this$props5$itemKey === void 0 ? defaultItemKey$1 : _this$props5$itemKey, layout = _this$props5.layout, outerElementType = _this$props5.outerElementType, outerTagName = _this$props5.outerTagName, style = _this$props5.style, useIsScrolling = _this$props5.useIsScrolling, width = _this$props5.width; var isScrolling = this.state.isScrolling; var isHorizontal = direction2 === "horizontal" || layout === "horizontal"; var onScroll = isHorizontal ? this._onScrollHorizontal : this._onScrollVertical; var _this$_getRangeToRend = this._getRangeToRender(), startIndex = _this$_getRangeToRend[0], stopIndex = _this$_getRangeToRend[1]; var items = []; if (itemCount > 0) { for (var _index = startIndex; _index <= stopIndex; _index++) { items.push(createElement(children, { data: itemData, key: itemKey(_index, itemData), index: _index, isScrolling: useIsScrolling ? isScrolling : void 0, style: this._getItemStyle(_index) })); } } var estimatedTotalSize = getEstimatedTotalSize2(this.props, this._instanceProps); return createElement(outerElementType || outerTagName || "div", { className, onScroll, ref: this._outerRefSetter, style: _extends({ position: "relative", height, width, overflow: "auto", WebkitOverflowScrolling: "touch", willChange: "transform", direction: direction2 }, style) }, createElement(innerElementType || innerTagName || "div", { children: items, ref: innerRef, style: { height: isHorizontal ? "100%" : estimatedTotalSize, pointerEvents: isScrolling ? "none" : void 0, width: isHorizontal ? estimatedTotalSize : "100%" } })); }; _proto._callPropsCallbacks = function _callPropsCallbacks() { if (typeof this.props.onItemsRendered === "function") { var itemCount = this.props.itemCount; if (itemCount > 0) { var _this$_getRangeToRend2 = this._getRangeToRender(), _overscanStartIndex = _this$_getRangeToRend2[0], _overscanStopIndex = _this$_getRangeToRend2[1], _visibleStartIndex = _this$_getRangeToRend2[2], _visibleStopIndex = _this$_getRangeToRend2[3]; this._callOnItemsRendered(_overscanStartIndex, _overscanStopIndex, _visibleStartIndex, _visibleStopIndex); } } if (typeof this.props.onScroll === "function") { var _this$state2 = this.state, _scrollDirection = _this$state2.scrollDirection, _scrollOffset = _this$state2.scrollOffset, _scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested; this._callOnScroll(_scrollDirection, _scrollOffset, _scrollUpdateWasRequested); } }; _proto._getRangeToRender = function _getRangeToRender() { var _this$props6 = this.props, itemCount = _this$props6.itemCount, overscanCount = _this$props6.overscanCount; var _this$state3 = this.state, isScrolling = _this$state3.isScrolling, scrollDirection = _this$state3.scrollDirection, scrollOffset = _this$state3.scrollOffset; if (itemCount === 0) { return [0, 0, 0, 0]; } var startIndex = getStartIndexForOffset2(this.props, scrollOffset, this._instanceProps); var stopIndex = getStopIndexForStartIndex2(this.props, startIndex, scrollOffset, this._instanceProps); var overscanBackward = !isScrolling || scrollDirection === "backward" ? Math.max(1, overscanCount) : 1; var overscanForward = !isScrolling || scrollDirection === "forward" ? Math.max(1, overscanCount) : 1; return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)), startIndex, stopIndex]; }; return List2; }(PureComponent), _class2.defaultProps = { direction: "ltr", itemData: void 0, layout: "vertical", overscanCount: 2, useIsScrolling: false }, _class2; } var validateSharedProps$1 = function validateSharedProps(_ref2, _ref3) { var children = _ref2.children, direction2 = _ref2.direction, height = _ref2.height, layout = _ref2.layout, innerTagName = _ref2.innerTagName, outerTagName = _ref2.outerTagName, width = _ref2.width; var instance = _ref3.instance; if (process.env.NODE_ENV !== "production") { if (innerTagName != null || outerTagName != null) { if (devWarningsTagName$1 && !devWarningsTagName$1.has(instance)) { devWarningsTagName$1.add(instance); console.warn("The innerTagName and outerTagName props have been deprecated. Please use the innerElementType and outerElementType props instead."); } } var isHorizontal = direction2 === "horizontal" || layout === "horizontal"; switch (direction2) { case "horizontal": case "vertical": if (devWarningsDirection && !devWarningsDirection.has(instance)) { devWarningsDirection.add(instance); console.warn('The direction prop should be either "ltr" (default) or "rtl". Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.'); } break; case "ltr": case "rtl": break; default: throw Error('An invalid "direction" prop has been specified. Value should be either "ltr" or "rtl". ' + ('"' + direction2 + '" was specified.')); } switch (layout) { case "horizontal": case "vertical": break; default: throw Error('An invalid "layout" prop has been specified. Value should be either "horizontal" or "vertical". ' + ('"' + layout + '" was specified.')); } if (children == null) { throw Error('An invalid "children" prop has been specified. Value should be a React component. ' + ('"' + (children === null ? "null" : typeof children) + '" was specified.')); } if (isHorizontal && typeof width !== "number") { throw Error('An invalid "width" prop has been specified. Horizontal lists must specify a number for width. ' + ('"' + (width === null ? "null" : typeof width) + '" was specified.')); } else if (!isHorizontal && typeof height !== "number") { throw Error('An invalid "height" prop has been specified. Vertical lists must specify a number for height. ' + ('"' + (height === null ? "null" : typeof height) + '" was specified.')); } } }; var FixedSizeList = /* @__PURE__ */ createListComponent({ getItemOffset: function getItemOffset(_ref, index) { var itemSize = _ref.itemSize; return index * itemSize; }, getItemSize: function getItemSize(_ref2, index) { var itemSize = _ref2.itemSize; return itemSize; }, getEstimatedTotalSize: function getEstimatedTotalSize(_ref3) { var itemCount = _ref3.itemCount, itemSize = _ref3.itemSize; return itemSize * itemCount; }, getOffsetForIndexAndAlignment: function getOffsetForIndexAndAlignment(_ref4, index, align, scrollOffset, instanceProps, scrollbarSize) { var direction2 = _ref4.direction, height = _ref4.height, itemCount = _ref4.itemCount, itemSize = _ref4.itemSize, layout = _ref4.layout, width = _ref4.width; var isHorizontal = direction2 === "horizontal" || layout === "horizontal"; var size2 = isHorizontal ? width : height; var lastItemOffset = Math.max(0, itemCount * itemSize - size2); var maxOffset = Math.min(lastItemOffset, index * itemSize); var minOffset = Math.max(0, index * itemSize - size2 + itemSize + scrollbarSize); if (align === "smart") { if (scrollOffset >= minOffset - size2 && scrollOffset <= maxOffset + size2) { align = "auto"; } else { align = "center"; } } switch (align) { case "start": return maxOffset; case "end": return minOffset; case "center": { var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(size2 / 2)) { return 0; } else if (middleOffset > lastItemOffset + Math.floor(size2 / 2)) { return lastItemOffset; } else { return middleOffset; } } case "auto": default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } }, getStartIndexForOffset: function getStartIndexForOffset(_ref5, offset) { var itemCount = _ref5.itemCount, itemSize = _ref5.itemSize; return Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize))); }, getStopIndexForStartIndex: function getStopIndexForStartIndex(_ref6, startIndex, scrollOffset) { var direction2 = _ref6.direction, height = _ref6.height, itemCount = _ref6.itemCount, itemSize = _ref6.itemSize, layout = _ref6.layout, width = _ref6.width; var isHorizontal = direction2 === "horizontal" || layout === "horizontal"; var offset = startIndex * itemSize; var size2 = isHorizontal ? width : height; var numVisibleItems = Math.ceil((size2 + scrollOffset - offset) / itemSize); return Math.max(0, Math.min( itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive )); }, initInstanceProps: function initInstanceProps(props) { }, shouldResetStyleCacheOnItemSizeChange: true, validateProps: function validateProps(_ref7) { var itemSize = _ref7.itemSize; if (process.env.NODE_ENV !== "production") { if (typeof itemSize !== "number") { throw Error('An invalid "itemSize" prop has been specified. Value should be a number. ' + ('"' + (itemSize === null ? "null" : typeof itemSize) + '" was specified.')); } } } }); const [ComponentProvider, useComponent] = createContext$1("ComponentContext", { id: void 0, level: -1, uid: void 0, type: void 0 }); function useHandleDisconnect(fieldName, consumerName) { const field = useField(fieldName); const removeFieldRow = useForm(consumerName, (state) => state.removeFieldRow); const addFieldRow = useForm(consumerName, (state) => state.addFieldRow); const handleDisconnect = (relation) => { if (field.value && field.value.connect) { const indexOfRelationInConnectArray = field.value.connect.findIndex( (rel) => rel.id === relation.id ); if (indexOfRelationInConnectArray >= 0) { removeFieldRow(`${fieldName}.connect`, indexOfRelationInConnectArray); return; } } addFieldRow(`${fieldName}.disconnect`, { id: relation.id, apiData: { id: relation.id, documentId: relation.documentId, locale: relation.locale } }); }; return handleDisconnect; } const RELATIONS_TO_DISPLAY = 5; const ONE_WAY_RELATIONS = ["oneWay", "oneToOne", "manyToOne", "oneToManyMorph", "oneToOneMorph"]; const RelationsField = React.forwardRef( ({ disabled, label: label2, ...props }, ref) => { const [currentPage, setCurrentPage] = React.useState(1); const { document: document2, model: documentModel } = useDoc(); const documentId = document2?.documentId; const { formatMessage } = useIntl(); const [{ query }] = useQueryParams(); const params = buildValidParams(query); const isMorph = props.attribute.relation.toLowerCase().includes("morph"); const isDisabled = isMorph || disabled; const { componentId, componentUID } = useComponent("RelationsField", ({ uid, id: id22 }) => ({ componentId: id22, componentUID: uid })); const isSubmitting = useForm("RelationsList", (state) => state.isSubmitting); React.useEffect(() => { setCurrentPage(1); }, [isSubmitting]); const id2 = componentId ? componentId.toString() : documentId; const model = componentUID ?? documentModel; const [targetField] = props.name.split(".").slice(-1); const { data, isLoading, isFetching } = useGetRelationsQuery( { model, targetField, // below we don't run the query if there is no id. id: id2, params: { ...params, pageSize: RELATIONS_TO_DISPLAY, page: currentPage } }, { refetchOnMountOrArgChange: true, skip: !id2, selectFromResult: (result2) => { return { ...result2, data: { ...result2.data, results: result2.data?.results ? result2.data.results : [] } }; } } ); const handleLoadMore = () => { setCurrentPage((prev) => prev + 1); }; const field = useField(props.name); const isFetchingMoreRelations = isLoading || isFetching; const realServerRelationsCount = "pagination" in data && data.pagination ? data.pagination.total : 0; const relationsConnected = (field.value?.connect ?? []).filter( (rel) => data.results.findIndex((relation) => relation.id === rel.id) === -1 ).length ?? 0; const relationsDisconnected = field.value?.disconnect?.length ?? 0; const relationsCount = realServerRelationsCount + relationsConnected - relationsDisconnected; const relations = React.useMemo(() => { const ctx = { field: field.value, // @ts-expect-error – targetModel does exist on the attribute. But it's not typed. href: `../${COLLECTION_TYPES}/${props.attribute.targetModel}`, mainField: props.mainField }; const transformations = pipe$1( removeConnected(ctx), removeDisconnected(ctx), addLabelAndHref(ctx) ); const transformedRels = transformations([...data.results]); return [...transformedRels, ...field.value?.connect ?? []].sort((a2, b2) => { if (a2.__temp_key__ < b2.__temp_key__) return -1; if (a2.__temp_key__ > b2.__temp_key__) return 1; return 0; }); }, [ data.results, field.value, // @ts-expect-error – targetModel does exist on the attribute. But it's not typed. props.attribute.targetModel, props.mainField ]); const handleDisconnect = useHandleDisconnect(props.name, "RelationsField"); const handleConnect = (relation) => { const [lastItemInList] = relations.slice(-1); const item = { id: relation.id, apiData: { id: relation.id, documentId: relation.documentId, locale: relation.locale }, status: relation.status, /** * If there's a last item, that's the first key we use to generate out next one. */ __temp_key__: generateNKeysBetween(lastItemInList?.__temp_key__ ?? null, null, 1)[0], // Fallback to `id` if there is no `mainField` value, which will overwrite the above `id` property with the exact same data. [props.mainField?.name ?? "documentId"]: relation[props.mainField?.name ?? "documentId"], label: getRelationLabel(relation, props.mainField), // @ts-expect-error – targetModel does exist on the attribute, but it's not typed. href: `../${COLLECTION_TYPES}/${props.attribute.targetModel}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ""}` }; if (ONE_WAY_RELATIONS.includes(props.attribute.relation)) { field.value?.connect?.forEach(handleDisconnect); relations.forEach(handleDisconnect); field.onChange(`${props.name}.connect`, [item]); } else { field.onChange(`${props.name}.connect`, [...field.value?.connect ?? [], item]); } }; return /* @__PURE__ */ jsxs( Flex, { ref, direction: "column", gap: 3, justifyContent: "space-between", alignItems: "stretch", wrap: "wrap", children: [ /* @__PURE__ */ jsxs(StyledFlex, { direction: "column", alignItems: "start", gap: 2, width: "100%", children: [ /* @__PURE__ */ jsx( RelationsInput, { disabled: isDisabled, id: componentUID ? componentId ? `${componentId}` : "" : documentId, label: `${label2} ${relationsCount > 0 ? `(${relationsCount})` : ""}`, model, onChange: handleConnect, ...props } ), "pagination" in data && data.pagination && data.pagination.pageCount > data.pagination.page ? /* @__PURE__ */ jsx( TextButton, { disabled: isFetchingMoreRelations, onClick: handleLoadMore, loading: isFetchingMoreRelations, startIcon: /* @__PURE__ */ jsx(ForwardRef$59, {}), shrink: 0, children: formatMessage({ id: getTranslation("relation.loadMore"), defaultMessage: "Load More" }) } ) : null ] }), /* @__PURE__ */ jsx( RelationsList, { data: relations, serverData: data.results, disabled: isDisabled, name: props.name, isLoading: isFetchingMoreRelations, relationType: props.attribute.relation } ) ] } ); } ); const StyledFlex = styled(Flex)` & > div { width: 100%; } `; const removeConnected = ({ field }) => (relations) => { return relations.filter((relation) => { const connectedRelations = field?.connect ?? []; return connectedRelations.findIndex((rel) => rel.id === relation.id) === -1; }); }; const removeDisconnected = ({ field }) => (relations) => relations.filter((relation) => { const disconnectedRelations = field?.disconnect ?? []; return disconnectedRelations.findIndex((rel) => rel.id === relation.id) === -1; }); const addLabelAndHref = ({ mainField, href }) => (relations) => relations.map((relation) => { return { ...relation, // Fallback to `id` if there is no `mainField` value, which will overwrite the above `documentId` property with the exact same data. [mainField?.name ?? "documentId"]: relation[mainField?.name ?? "documentId"], label: getRelationLabel(relation, mainField), href: `${href}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ""}` }; }); const RelationsInput = ({ hint, id: id2, model, label: label2, labelAction, name: name2, mainField, placeholder, required, unique: _unique, "aria-label": _ariaLabel, onChange, ...props }) => { const [textValue, setTextValue] = React.useState(""); const [searchParams, setSearchParams] = React.useState({ _q: "", page: 1 }); const { toggleNotification } = useNotification(); const [{ query }] = useQueryParams(); const { formatMessage } = useIntl(); const fieldRef = useFocusInputField(name2); const field = useField(name2); const [searchForTrigger, { data, isLoading }] = useLazySearchRelationsQuery(); React.useEffect(() => { const [targetField] = name2.split(".").slice(-1); searchForTrigger({ model, targetField, params: { ...buildValidParams(query), id: id2 ?? "", pageSize: 10, idsToInclude: field.value?.disconnect?.map((rel) => rel.id.toString()) ?? [], idsToOmit: field.value?.connect?.map((rel) => rel.id.toString()) ?? [], ...searchParams } }); }, [ field.value?.connect, field.value?.disconnect, id2, model, name2, query, searchForTrigger, searchParams ]); const handleSearch = async (search) => { setSearchParams((s) => ({ ...s, _q: search, page: 1 })); }; const hasNextPage = data?.pagination ? data.pagination.page < data.pagination.pageCount : false; const options = data?.results ?? []; const handleChange = (relationId) => { if (!relationId) { return; } const relation = options.find((opt) => opt.id.toString() === relationId); if (!relation) { console.error( "You've tried to add a relation with an id that does not exist in the options you can see, this is likely a bug with Strapi. Please open an issue." ); toggleNotification({ message: formatMessage({ id: getTranslation("relation.error-adding-relation"), defaultMessage: "An error occurred while trying to add the relation." }), type: "danger" }); return; } onChange(relation); }; const handleLoadMore = () => { if (!data || !data.pagination) { return; } else if (data.pagination.page < data.pagination.pageCount) { setSearchParams((s) => ({ ...s, page: s.page + 1 })); } }; React.useLayoutEffect(() => { setTextValue(""); }, [field.value]); return /* @__PURE__ */ jsxs(Field.Root, { error: field.error, hint, name: name2, required, children: [ /* @__PURE__ */ jsx(Field.Label, { action: labelAction, children: label2 }), /* @__PURE__ */ jsx( Combobox, { ref: fieldRef, name: name2, autocomplete: "list", placeholder: placeholder || formatMessage({ id: getTranslation("relation.add"), defaultMessage: "Add relation" }), hasMoreItems: hasNextPage, loading: isLoading, onOpenChange: () => { handleSearch(textValue ?? ""); }, noOptionsMessage: () => formatMessage({ id: getTranslation("relation.notAvailable"), defaultMessage: "No relations available" }), loadingMessage: formatMessage({ id: getTranslation("relation.isLoading"), defaultMessage: "Relations are loading" }), onLoadMore: handleLoadMore, textValue, onChange: handleChange, onTextValueChange: (text3) => { setTextValue(text3); }, onInputChange: (event) => { handleSearch(event.currentTarget.value); }, ...props, children: options.map((opt) => { const textValue2 = getRelationLabel(opt, mainField); return /* @__PURE__ */ jsx(ComboboxOption, { value: opt.id.toString(), textValue: textValue2, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "space-between", children: [ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: textValue2 }), opt.status ? /* @__PURE__ */ jsx(DocumentStatus, { status: opt.status }) : null ] }) }, opt.id); }) } ), /* @__PURE__ */ jsx(Field.Error, {}), /* @__PURE__ */ jsx(Field.Hint, {}) ] }); }; const RELATION_ITEM_HEIGHT = 50; const RELATION_GUTTER = 4; const RelationsList = ({ data, serverData, disabled, name: name2, isLoading, relationType }) => { const ariaDescriptionId = React.useId(); const { formatMessage } = useIntl(); const listRef = React.useRef(null); const outerListRef = React.useRef(null); const [overflow, setOverflow] = React.useState(); const [liveText, setLiveText] = React.useState(""); const field = useField(name2); React.useEffect(() => { if (data.length <= RELATIONS_TO_DISPLAY) { return setOverflow(void 0); } const handleNativeScroll = (e2) => { const el2 = e2.target; const parentScrollContainerHeight = el2.parentNode.scrollHeight; const maxScrollBottom = el2.scrollHeight - el2.scrollTop; if (el2.scrollTop === 0) { return setOverflow("bottom"); } if (maxScrollBottom === parentScrollContainerHeight) { return setOverflow("top"); } return setOverflow("top-bottom"); }; const outerListRefCurrent = outerListRef?.current; if (!isLoading && data.length > 0 && outerListRefCurrent) { outerListRef.current.addEventListener("scroll", handleNativeScroll); } return () => { if (outerListRefCurrent) { outerListRefCurrent.removeEventListener("scroll", handleNativeScroll); } }; }, [isLoading, data.length]); const getItemPos = (index) => `${index + 1} of ${data.length}`; const handleMoveItem = (newIndex, oldIndex) => { const item = data[oldIndex]; setLiveText( formatMessage( { id: getTranslation("dnd.reorder"), defaultMessage: "{item}, moved. New position in list: {position}." }, { item: item.label ?? item.documentId, position: getItemPos(newIndex) } ) ); const newData = [...data]; const currentRow = data[oldIndex]; const startKey = oldIndex > newIndex ? newData[newIndex - 1]?.__temp_key__ : newData[newIndex]?.__temp_key__; const endKey = oldIndex > newIndex ? newData[newIndex]?.__temp_key__ : newData[newIndex + 1]?.__temp_key__; const [newKey] = generateNKeysBetween(startKey, endKey, 1); newData.splice(oldIndex, 1); newData.splice(newIndex, 0, { ...currentRow, __temp_key__: newKey }); const connectedRelations = newData.reduce((acc, relation, currentIndex, array) => { const relationOnServer = serverData.find((oldRelation) => oldRelation.id === relation.id); const relationInFront = array[currentIndex + 1]; if (!relationOnServer || relationOnServer.__temp_key__ !== relation.__temp_key__) { const position = relationInFront ? { before: relationInFront.documentId, locale: relationInFront.locale, status: "publishedAt" in relationInFront && relationInFront.publishedAt ? "published" : "draft" } : { end: true }; const relationWithPosition = { ...relation, ...{ apiData: { id: relation.id, documentId: relation.documentId, locale: relation.locale, position } } }; return [...acc, relationWithPosition]; } return acc; }, []).toReversed(); field.onChange(`${name2}.connect`, connectedRelations); }; const handleGrabItem = (index) => { const item = data[index]; setLiveText( formatMessage( { id: getTranslation("dnd.grab-item"), defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.` }, { item: item.label ?? item.documentId, position: getItemPos(index) } ) ); }; const handleDropItem = (index) => { const { href: _href, label: label2, ...item } = data[index]; setLiveText( formatMessage( { id: getTranslation("dnd.drop-item"), defaultMessage: `{item}, dropped. Final position in list: {position}.` }, { item: label2 ?? item.documentId, position: getItemPos(index) } ) ); }; const handleCancel = (index) => { const item = data[index]; setLiveText( formatMessage( { id: getTranslation("dnd.cancel-item"), defaultMessage: "{item}, dropped. Re-order cancelled." }, { item: item.label ?? item.documentId } ) ); }; const handleDisconnect = useHandleDisconnect(name2, "RelationsList"); const canReorder = !ONE_WAY_RELATIONS.includes(relationType); const dynamicListHeight = data.length > RELATIONS_TO_DISPLAY ? Math.min(data.length, RELATIONS_TO_DISPLAY) * (RELATION_ITEM_HEIGHT + RELATION_GUTTER) + RELATION_ITEM_HEIGHT / 2 : Math.min(data.length, RELATIONS_TO_DISPLAY) * (RELATION_ITEM_HEIGHT + RELATION_GUTTER); return /* @__PURE__ */ jsxs(ShadowBox, { $overflowDirection: overflow, children: [ /* @__PURE__ */ jsx(VisuallyHidden, { id: ariaDescriptionId, children: formatMessage({ id: getTranslation("dnd.instructions"), defaultMessage: `Press spacebar to grab and re-order` }) }), /* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "assertive", children: liveText }), /* @__PURE__ */ jsx( FixedSizeList, { height: dynamicListHeight, ref: listRef, outerRef: outerListRef, itemCount: data.length, itemSize: RELATION_ITEM_HEIGHT + RELATION_GUTTER, itemData: { ariaDescribedBy: ariaDescriptionId, canDrag: canReorder, disabled, handleCancel, handleDropItem, handleGrabItem, handleMoveItem, name: name2, handleDisconnect, relations: data }, itemKey: (index) => data[index].id, innerElementType: "ol", children: ListItem } ) ] }); }; const ShadowBox = styled(Box)` position: relative; overflow: hidden; flex: 1; &:before, &:after { position: absolute; width: 100%; height: 4px; z-index: 1; } &:before { /* TODO: as for DS Table component we would need this to be handled by the DS theme */ content: ''; background: linear-gradient(rgba(3, 3, 5, 0.2) 0%, rgba(0, 0, 0, 0) 100%); top: 0; opacity: ${({ $overflowDirection }) => $overflowDirection === "top-bottom" || $overflowDirection === "top" ? 1 : 0}; transition: opacity 0.2s ease-in-out; } &:after { /* TODO: as for DS Table component we would need this to be handled by the DS theme */ content: ''; background: linear-gradient(0deg, rgba(3, 3, 5, 0.2) 0%, rgba(0, 0, 0, 0) 100%); bottom: 0; opacity: ${({ $overflowDirection }) => $overflowDirection === "top-bottom" || $overflowDirection === "bottom" ? 1 : 0}; transition: opacity 0.2s ease-in-out; } `; const ListItem = ({ data, index, style }) => { const { ariaDescribedBy, canDrag = false, disabled = false, handleCancel, handleDisconnect, handleDropItem, handleGrabItem, handleMoveItem, name: name2, relations } = data; const { formatMessage } = useIntl(); const { href, id: id2, label: label2, status } = relations[i