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
JavaScript
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